diff --git a/Cargo.lock b/Cargo.lock index 6581dacbea..7f385b7175 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "redox_syscall", "ron", "rustc-hash", + "serde", "thiserror", ] diff --git a/acpid/Cargo.toml b/acpid/Cargo.toml index 2e71a1e811..8b162993a5 100644 --- a/acpid/Cargo.toml +++ b/acpid/Cargo.toml @@ -24,4 +24,5 @@ amlserde = { path = "../amlserde" } common = { path = "../common" } libredox = "0.1.3" redox-scheme = "0.6.2" -arrayvec = "0.7.6" \ No newline at end of file +arrayvec = "0.7.6" +serde = { version = "1.0.228", features = ["derive"] } diff --git a/acpid/src/acpi.rs b/acpid/src/acpi.rs index 464a8a1a2c..9ddfdb3ab1 100644 --- a/acpid/src/acpi.rs +++ b/acpid/src/acpi.rs @@ -1,3 +1,4 @@ +use acpi::aml::object::{Object, WrappedObject}; use rustc_hash::FxHashMap; use std::convert::{TryFrom, TryInto}; use std::ops::Deref; @@ -18,7 +19,7 @@ use acpi::{ AcpiTables, }; use amlserde::aml_serde_name::aml_to_symbol; -use amlserde::AmlSerde; +use amlserde::{AmlSerde, AmlSerdeValue}; #[cfg(target_arch = "x86_64")] pub mod dmar; @@ -328,6 +329,21 @@ impl AmlSymbols { } } +#[derive(Debug, Error)] +pub enum AmlEvalError { + #[error("AML error")] + AmlError(AmlError), + #[error("Failed to serialize argument")] + SerializationError, + #[error("Failed to deserialize")] + DeserializationError, +} +impl From for AmlEvalError { + fn from(value: AmlError) -> Self { + AmlEvalError::AmlError(value) + } +} + pub struct AcpiContext { tables: Vec, dsdt: Option, @@ -344,6 +360,38 @@ pub struct AcpiContext { } impl AcpiContext { + pub fn aml_eval( + &self, + symbol: AmlName, + args: Vec, + ) -> Result { + let interpreter = &mut self.aml_symbols.write().aml_context; + interpreter.acquire_global_lock(16)?; + + let args = args + .into_iter() + .map(|aml_serde_value| { + aml_serde_value + .to_aml_object() + .map(Object::wrap) + .ok_or(AmlEvalError::DeserializationError) + }) + .collect::, AmlEvalError>>()?; + + let result = interpreter.evaluate(symbol, args); + interpreter + .release_global_lock() + .expect("Failed to release GIL!"); //TODO: check if this should panic + + result + .map_err(AmlEvalError::from) + .map(|object| { + AmlSerdeValue::from_aml_value(object.deref()) + .ok_or(AmlEvalError::SerializationError) + }) + .flatten() + } + pub fn init(rxsdt_physaddrs: impl Iterator) -> Self { let tables = rxsdt_physaddrs .map(|physaddr| { diff --git a/acpid/src/aml_physmem.rs b/acpid/src/aml_physmem.rs index 4145a5fe00..8e0d47d7bc 100644 --- a/acpid/src/aml_physmem.rs +++ b/acpid/src/aml_physmem.rs @@ -358,7 +358,7 @@ impl acpi::Handler for AmlPhysMemHandler { } fn acquire(&self, mutex: Handle, timeout: u16) -> Result<(), AmlError> { - log::warn!("TODO: Handler::aquire"); + log::warn!("TODO: Handler::acquire"); Ok(()) } diff --git a/acpid/src/main.rs b/acpid/src/main.rs index aef18c2cb6..65e35513fb 100644 --- a/acpid/src/main.rs +++ b/acpid/src/main.rs @@ -10,6 +10,7 @@ use syscall::{EAGAIN, EWOULDBLOCK}; mod acpi; mod aml_physmem; + mod scheme; fn daemon(daemon: redox_daemon::Daemon) -> ! { diff --git a/acpid/src/scheme.rs b/acpid/src/scheme.rs index e744b5b257..13797f2924 100644 --- a/acpid/src/scheme.rs +++ b/acpid/src/scheme.rs @@ -1,9 +1,14 @@ +use acpi::aml::namespace::AmlName; +use amlserde::aml_serde_name::to_aml_format; +use amlserde::AmlSerdeValue; use core::str; use parking_lot::RwLockReadGuard; use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult}; +use ron::de::SpannedError; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; +use std::str::FromStr; use syscall::dirent::{DirEntry, DirentBuf, DirentKind}; use syscall::schemev2::NewFdFlags; @@ -12,7 +17,7 @@ use syscall::error::{Error, Result}; use syscall::error::{EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR}; use syscall::flag::{MODE_DIR, MODE_FILE}; use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDONLY, O_STAT, O_SYMLINK}; -use syscall::EOPNOTSUPP; +use syscall::{EOPNOTSUPP, EOVERFLOW, EPERM}; use crate::acpi::{AcpiContext, AmlSymbols, SdtSignature}; @@ -25,13 +30,14 @@ pub struct AcpiScheme<'acpi> { struct Handle<'a> { kind: HandleKind<'a>, stat: bool, + allowed_to_eval: bool, } enum HandleKind<'a> { TopLevel, Tables, Table(SdtSignature), Symbols(RwLockReadGuard<'a, AmlSymbols>), - Symbol(String), + Symbol { name: String, description: String }, } impl HandleKind<'_> { @@ -41,7 +47,7 @@ impl HandleKind<'_> { Self::Tables => true, Self::Table(_) => false, Self::Symbols(_) => true, - Self::Symbol(_) => false, + Self::Symbol { .. } => false, } } fn len(&self, acpi_ctx: &AcpiContext) -> Result { @@ -51,7 +57,7 @@ impl HandleKind<'_> { .sdt_from_signature(signature) .ok_or(Error::new(EBADFD))? .length(), - Self::Symbol(description) => description.len(), + Self::Symbol { description, .. } => description.len(), // Directories Self::TopLevel | Self::Symbols(_) | Self::Tables => 0, }) @@ -143,7 +149,7 @@ fn parse_table(table: &[u8]) -> Option { } impl SchemeSync for AcpiScheme<'_> { - fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { + fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { let path = path.trim_start_matches('/'); let flag_stat = flags & O_STAT == O_STAT; @@ -179,7 +185,10 @@ impl SchemeSync for AcpiScheme<'_> { ["symbols", symbol] => { if let Some(description) = self.ctx.aml_lookup(symbol) { - HandleKind::Symbol(description) + HandleKind::Symbol { + name: (*symbol).to_owned(), + description, + } } else { return Err(Error::new(ENOENT)); } @@ -194,9 +203,13 @@ impl SchemeSync for AcpiScheme<'_> { return Err(Error::new(ENOTDIR)); } - if flags & O_ACCMODE != O_RDONLY && !flag_stat { + let allowed_to_eval = if flags & O_ACCMODE == O_RDONLY || flag_stat { + false + } else if ctx.uid != 0 { + true + } else { return Err(Error::new(EINVAL)); - } + }; if flags & O_SYMLINK == O_SYMLINK && !flag_stat { return Err(Error::new(EINVAL)); @@ -210,6 +223,7 @@ impl SchemeSync for AcpiScheme<'_> { Handle { stat: flag_stat, kind, + allowed_to_eval, }, ); @@ -259,7 +273,7 @@ impl SchemeSync for AcpiScheme<'_> { .sdt_from_signature(signature) .ok_or(Error::new(EBADFD))? .as_slice(), - HandleKind::Symbol(description) => description.as_bytes(), + HandleKind::Symbol { description, .. } => description.as_bytes(), _ => return Err(Error::new(EINVAL)), }; @@ -358,6 +372,47 @@ impl SchemeSync for AcpiScheme<'_> { ) -> Result { Err(Error::new(EBADF)) } + + fn call(&mut self, id: usize, payload: &mut [u8], _metadata: &[u64]) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if !handle.allowed_to_eval { + return Err(Error::new(EPERM)); + } + + let Ok(args): Result, SpannedError> = ron::de::from_bytes(payload) + else { + return Err(Error::new(EINVAL)); + }; + + let HandleKind::Symbol { name, .. } = &handle.kind else { + return Err(Error::new(EBADF)); + }; + + let Ok(aml_name) = AmlName::from_str(&to_aml_format(name)) else { + log::error!("Failed to convert symbol name: \"{name}\" to aml name!"); + return Err(Error::new(EBADF)); + }; + + let Ok(result) = self.ctx.aml_eval(aml_name, args) else { + return Err(Error::new(EINVAL)); + }; + + let Ok(serialized_result) = ron::ser::to_string(&result) else { + log::error!("Failed to serialize aml result!"); + return Err(Error::new(EINVAL)); + }; + + let byte_result = serialized_result.as_bytes(); + let result_len = byte_result.len(); + + if result_len > payload.len() { + return Err(Error::new(EOVERFLOW)); + } + + payload[..result_len].copy_from_slice(byte_result); + + Ok(result_len) + } } impl AcpiScheme<'_> { diff --git a/amlserde/src/lib.rs b/amlserde/src/lib.rs index 7fa2f7c50e..1cf8e3f2c6 100644 --- a/amlserde/src/lib.rs +++ b/amlserde/src/lib.rs @@ -1,14 +1,24 @@ use acpi::{ aml::{ namespace::AmlName, - object::{FieldAccessType, FieldUnitKind, FieldUpdateRule, Object, ReferenceKind}, - op_region::RegionSpace, + object::{ + FieldAccessType, FieldFlags, FieldUnit, FieldUnitKind, FieldUpdateRule, MethodFlags, + Object, ReferenceKind, WrappedObject, + }, + op_region::{OpRegion, RegionSpace}, Interpreter, }, - Handler, + Handle, Handler, }; use serde::{Deserialize, Serialize}; -use std::{ops::Deref, sync::atomic::Ordering}; +use std::{ + ops::{Deref, Shl}, + str::FromStr, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, +}; #[derive(Debug, Serialize, Deserialize)] pub struct AmlSerde { @@ -25,7 +35,7 @@ pub enum AmlSerdeValue { region: AmlSerdeRegionSpace, offset: u64, length: u64, - parent_device: Option, + parent_device: String, }, Field { kind: AmlSerdeFieldKind, @@ -44,7 +54,7 @@ pub enum AmlSerdeValue { BufferField { offset: u64, length: u64, - data: Option>, + data: Box, }, Processor { id: u8, @@ -57,7 +67,7 @@ pub enum AmlSerdeValue { }, Reference { kind: AmlSerdeReferenceKind, - inner: Option>, + inner: Box, }, Package { contents: Vec, @@ -90,41 +100,53 @@ pub enum AmlSerdeRegionSpace { #[derive(Debug, Serialize, Deserialize)] pub enum AmlSerdeFieldKind { Normal { - region: Option>, + region: Box, }, Bank { - region: Option>, - bank: Option>, + region: Box, + bank: Box, bank_value: u64, }, Index { - index: Option>, - data: Option>, + index: Box, + data: Box, }, } #[derive(Debug, Serialize, Deserialize)] pub struct AmlSerdeFieldFlags { pub access_type: AmlSerdeFieldAccessType, - pub lock_rule: bool, + pub lock_rule: bool, // bit 4 pub update_rule: AmlSerdeFieldUpdateRule, } - -#[derive(Debug, Serialize, Deserialize)] -pub enum AmlSerdeFieldAccessType { - Any, - Byte, - Word, - DWord, - QWord, - Buffer, +impl Into for AmlSerdeFieldFlags { + fn into(self) -> u8 { + // bits 0..4 + (self.access_type as u8) + + // bit 4 + (self.lock_rule as u8).shl(4) + + // bits 5..7 + (self.update_rule as u8).shl(5) + } } #[derive(Debug, Serialize, Deserialize)] +#[repr(u8)] +pub enum AmlSerdeFieldAccessType { + Any = 0, + Byte = 1, + Word = 2, + DWord = 3, + QWord = 4, + Buffer = 5, +} + +#[derive(Debug, Serialize, Deserialize)] +#[repr(u8)] pub enum AmlSerdeFieldUpdateRule { - Preserve, - WriteAsOnes, - WriteAsZeros, + Preserve = 0, + WriteAsOnes = 1, + WriteAsZeros = 2, } #[derive(Debug, Serialize, Deserialize)] @@ -168,7 +190,7 @@ impl AmlSerdeValue { AmlSerdeValue::String("".to_owned()) } - fn from_aml_value(aml_value: &Object) -> Option { + pub fn from_aml_value(aml_value: &Object) -> Option { Some(match aml_value { Object::Uninitialized => AmlSerdeValue::Uninitialized, Object::Integer(n) => AmlSerdeValue::Integer(n.to_owned()), @@ -190,25 +212,25 @@ impl AmlSerdeValue { }, offset: region.base, length: region.length, - parent_device: Some(region.parent_device_path.to_string()), + parent_device: region.parent_device_path.to_string(), }, Object::FieldUnit(field) => AmlSerdeValue::Field { kind: match &field.kind { FieldUnitKind::Normal { region } => AmlSerdeFieldKind::Normal { - region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new), + region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new)?, }, FieldUnitKind::Bank { region, bank, bank_value, } => AmlSerdeFieldKind::Bank { - region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new), - bank: AmlSerdeValue::from_aml_value(bank.deref()).map(Box::new), + region: AmlSerdeValue::from_aml_value(region.deref()).map(Box::new)?, + bank: AmlSerdeValue::from_aml_value(bank.deref()).map(Box::new)?, bank_value: bank_value.to_owned(), }, FieldUnitKind::Index { index, data } => AmlSerdeFieldKind::Index { - index: AmlSerdeValue::from_aml_value(index.deref()).map(Box::new), - data: AmlSerdeValue::from_aml_value(data.deref()).map(Box::new), + index: AmlSerdeValue::from_aml_value(index.deref()).map(Box::new)?, + data: AmlSerdeValue::from_aml_value(data.deref()).map(Box::new)?, }, }, flags: AmlSerdeFieldFlags { @@ -252,7 +274,7 @@ impl AmlSerdeValue { } => AmlSerdeValue::BufferField { offset: offset.to_owned() as u64, length: length.to_owned() as u64, - data: AmlSerdeValue::from_aml_value(buffer.deref()).map(Box::new), + data: AmlSerdeValue::from_aml_value(buffer.deref()).map(Box::new)?, }, Object::Processor { proc_id, @@ -273,7 +295,7 @@ impl AmlSerdeValue { ReferenceKind::LocalOrArg => AmlSerdeReferenceKind::LocalOrArg, ReferenceKind::Unresolved => AmlSerdeReferenceKind::Unresolved, }, - inner: AmlSerdeValue::from_aml_value(inner.deref()).map(Box::new), + inner: AmlSerdeValue::from_aml_value(inner.deref()).map(Box::new)?, }, Object::Package(aml_contents) => AmlSerdeValue::Package { contents: aml_contents @@ -293,6 +315,129 @@ impl AmlSerdeValue { Object::Debug => AmlSerdeValue::Debug, }) } + pub fn to_aml_object(self) -> Option { + Some(match self { + AmlSerdeValue::Uninitialized => Object::Uninitialized, + AmlSerdeValue::Integer(n) => Object::Integer(n), + AmlSerdeValue::String(s) => Object::String(s), + AmlSerdeValue::OpRegion { + region, + offset, + length, + parent_device, + } => Object::OpRegion(OpRegion { + space: match region { + AmlSerdeRegionSpace::PciConfig => RegionSpace::PciConfig, + AmlSerdeRegionSpace::EmbeddedControl => RegionSpace::EmbeddedControl, + AmlSerdeRegionSpace::SMBus => RegionSpace::SmBus, + AmlSerdeRegionSpace::SystemCmos => RegionSpace::SystemCmos, + AmlSerdeRegionSpace::PciBarTarget => RegionSpace::PciBarTarget, + AmlSerdeRegionSpace::IPMI => RegionSpace::Ipmi, + AmlSerdeRegionSpace::GeneralPurposeIo => RegionSpace::GeneralPurposeIo, + AmlSerdeRegionSpace::GenericSerialBus => RegionSpace::GenericSerialBus, + AmlSerdeRegionSpace::SystemMemory => RegionSpace::SystemMemory, + AmlSerdeRegionSpace::SystemIo => RegionSpace::SystemIO, + AmlSerdeRegionSpace::Pcc => RegionSpace::Pcc, + AmlSerdeRegionSpace::OemDefined(n) => RegionSpace::Oem(n), + }, + base: offset, + length, + // + parent_device_path: AmlName::from_str(&parent_device).ok()?, // TODO: Error value hidden + }), + AmlSerdeValue::Field { + kind, + flags, + offset, + length, + } => Object::FieldUnit(FieldUnit { + kind: match kind { + AmlSerdeFieldKind::Normal { region } => FieldUnitKind::Normal { + region: region.to_aml_object()?.wrap(), + }, + AmlSerdeFieldKind::Bank { + region, + bank, + bank_value, + } => FieldUnitKind::Bank { + region: region.to_aml_object()?.wrap(), + bank: bank.to_aml_object()?.wrap(), + bank_value: bank_value.to_owned(), + }, + AmlSerdeFieldKind::Index { index, data } => FieldUnitKind::Index { + index: index.to_aml_object()?.wrap(), + data: data.to_aml_object()?.wrap(), + }, + }, + flags: FieldFlags(flags.into()), + bit_index: offset as usize, + bit_length: length as usize, + }), + AmlSerdeValue::Device => Object::Device, + AmlSerdeValue::Event(event) => Object::Event(Arc::new(AtomicU64::new(event))), + AmlSerdeValue::Method { + arg_count, + serialize, + sync_level, + } => Object::Method { + code: (return None), //TODO figure out what to do here + //TODO check specs to see if all bit patterns are allowed + flags: MethodFlags( + (arg_count as u8).clamp(0, 7) + + (serialize as u8).shl(3) + + sync_level.clamp(0, 15).shl(4), + ), + }, + //TODO: handle native method? + AmlSerdeValue::Buffer(buffer_data) => Object::Buffer(buffer_data), + AmlSerdeValue::BufferField { + data, + offset, + length, + } => Object::BufferField { + offset: offset as usize, + length: length as usize, + buffer: data.to_aml_object()?.wrap(), + }, + AmlSerdeValue::Processor { + id, + pblk_address, + pblk_len, + } => Object::Processor { + proc_id: id, + pblk_address, + pblk_length: pblk_len, + }, + AmlSerdeValue::Mutex { mutex, sync_level } => Object::Mutex { + mutex: Handle(mutex), + sync_level: sync_level, + }, + AmlSerdeValue::Reference { kind, inner } => Object::Reference { + kind: match kind { + AmlSerdeReferenceKind::RefOf => ReferenceKind::RefOf, + AmlSerdeReferenceKind::LocalOrArg => ReferenceKind::LocalOrArg, + AmlSerdeReferenceKind::Unresolved => ReferenceKind::Unresolved, + }, + inner: inner.to_aml_object()?.wrap(), + }, + AmlSerdeValue::Package { contents } => Object::Package( + contents + .into_iter() + .map(|item| item.to_aml_object().map(Object::wrap)) // TODO: see if errors should be ignored here + .collect::>>()?, + ), + AmlSerdeValue::PowerResource { + system_level, + resource_order, + } => Object::PowerResource { + system_level: system_level.to_owned(), + resource_order: resource_order.to_owned(), + }, + AmlSerdeValue::RawDataBuffer => Object::RawDataBuffer, + AmlSerdeValue::ThermalZone => Object::ThermalZone, + AmlSerdeValue::Debug => Object::Debug, + }) + } } pub mod aml_serde_name {