diff --git a/src/aml/mod.rs b/src/aml/mod.rs index 6d67f282..ea5e48fd 100644 --- a/src/aml/mod.rs +++ b/src/aml/mod.rs @@ -21,6 +21,7 @@ pub mod object; pub mod op_region; pub mod pci_routing; pub mod resource; +pub mod string; use crate::{ AcpiError, @@ -32,23 +33,17 @@ use crate::{ registers::{FixedRegisters, Pm1ControlBit}, sdt::{SdtHeader, facs::Facs, fadt::Fadt}, }; -use alloc::{ - boxed::Box, - collections::btree_map::BTreeMap, - string::{String, ToString}, - sync::Arc, - vec, - vec::Vec, -}; +use alloc::{alloc::Global, string::{String, ToString}, boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; use bit_field::BitField; use core::{ + alloc::Allocator, mem, slice, - str::FromStr, sync::atomic::{AtomicU64, Ordering}, }; use log::{error, info, trace, warn}; use namespace::{AmlName, Namespace, NamespaceLevelKind}; +use string::AmlString; use object::{ DeviceStatus, FieldFlags, @@ -66,47 +61,56 @@ use op_region::{OpRegion, RegionHandler, RegionSpace}; use pci_types::PciAddress; use spinning_top::Spinlock; -/// Helper macro to extract an expected set of [`Argument`]s from the given [`OpInFlight`]. Use -/// like: -/// ``` ignore,rust -/// extract_args!(op => [Argument::Object(source), Argument::Object(target)]); -/// extract_args!(op[0..2] => [Argument::Object(source), Argument::Namespace(name)]); -/// ``` macro_rules! extract_args { ($op:ident => $args:tt) => { let $args = &$op.arguments[..] else { - return Err(AmlError::InternalError(alloc::format!( - "Operation has invalid argument types: {}, in {}:{}", - stringify!($args), - file!(), - line!(), - ))); + return Err(AmlError::InternalError( + concat!("Operation has invalid argument types at ", file!(), ":", line!()).to_string(), + )); }; }; ($op:ident[$x:expr] => $args:tt) => { let $args = &$op.arguments[$x] else { - return Err(AmlError::InternalError(alloc::format!( - "Operation has invalid argument types: {}, in {}:{}", - stringify!($args), - file!(), - line!(), - ))); + return Err(AmlError::InternalError( + concat!("Operation has invalid argument types at ", file!(), ":", line!()).to_string(), + )); }; }; } +/// Allocator-aware `vec![]` replacement. Three forms mirror `vec!`: +/// `vec_in!(alloc)` - empty Vec +/// `vec_in!(alloc; elem; n)` - `n` copies of `elem` +/// `vec_in!(alloc; a, b, c)` - Vec with the given elements +macro_rules! vec_in { + ($alloc:expr) => {{ + Vec::new_in($alloc) + }}; + ($alloc:expr; $elem:expr; $n:expr) => {{ + let mut v = Vec::with_capacity_in($n, $alloc); + v.resize($n, $elem); + v + }}; + ($alloc:expr; $($x:expr),+ $(,)?) => {{ + let mut v = Vec::new_in($alloc); + $(v.push($x);)+ + v + }}; +} + /// `Interpreter` implements a virtual machine for the dynamic AML bytecode. It can be used by a /// host operating system to load tables containing AML bytecode (generally the DSDT and SSDTs) and /// will then manage the AML namespace and all objects created during the life of the system. -pub struct Interpreter +pub struct Interpreter where H: Handler, { handler: H, - pub namespace: Spinlock, + alloc: A, + pub namespace: Spinlock>, pub object_token: Spinlock, integer_size: IntegerSize, - region_handlers: Spinlock>>, + region_handlers: Spinlock, A>, A>>, global_lock_mutex: Handle, @@ -118,8 +122,8 @@ where facs: Option>, } -unsafe impl Send for Interpreter where H: Handler + Send {} -unsafe impl Sync for Interpreter where H: Handler + Send {} +unsafe impl Send for Interpreter where H: Handler + Send {} +unsafe impl Sync for Interpreter where H: Handler + Send {} /// The value returned by the `Revision` opcode. const INTERPRETER_REVISION: u64 = 1; @@ -133,35 +137,66 @@ impl Interpreter where H: Handler, { - /// Construct a new [`Interpreter`]. This does not load any tables - if you have an + /// Construct a new [`Interpreter`] using the global allocator. This does not load any tables - if you have an /// [`crate::AcpiTables`] already, construct an [`AcpiPlatform`] first and then use - /// [`Interpreter::new_from_platform`] + /// [`Interpreter::new_from_platform`]. pub fn new( handler: H, dsdt_revision: u8, registers: Arc>, facs: Option>, ) -> Interpreter { + Interpreter::new_in(handler, dsdt_revision, registers, facs, Global) + } + + /// Construct a new [`Interpreter`] with the given [`AcpiPlatform`], using the global allocator. + pub fn new_from_platform(platform: &AcpiPlatform) -> Result, AcpiError> { + Interpreter::new_from_platform_in(platform, Global) + } +} + +impl Interpreter +where + H: Handler, +{ + /// Construct a new [`Interpreter`] using the supplied allocator. This does not load any tables - if you have an + /// [`crate::AcpiTables`] already, construct an [`AcpiPlatform`] first and then use + /// [`Interpreter::new_from_platform`]. + pub fn new_in( + handler: H, + dsdt_revision: u8, + registers: Arc>, + facs: Option>, + alloc: A, + ) -> Interpreter { info!("Initializing AML interpreter v{}", env!("CARGO_PKG_VERSION")); let global_lock_mutex = handler.create_mutex(); Interpreter { handler, - namespace: Spinlock::new(Namespace::new(global_lock_mutex)), + namespace: Spinlock::new(Namespace::new_in(global_lock_mutex, alloc.clone())), object_token: Spinlock::new(unsafe { ObjectToken::create_interpreter_token() }), integer_size: IntegerSize::from_revision(dsdt_revision), - region_handlers: Spinlock::new(BTreeMap::new()), + region_handlers: Spinlock::new(BTreeMap::new_in(alloc.clone())), global_lock_mutex, global_lock_acquisition_count: AtomicU64::new(0), registers, facs, + alloc, } } - /// Construct a new [`Interpreter`] with the given [`AcpiPlatform`]. - pub fn new_from_platform(platform: &AcpiPlatform) -> Result, AcpiError> { - fn load_table(interpreter: &Interpreter, table: AmlTable) -> Result<(), AcpiError> { + /// Construct a new [`Interpreter`] with the given [`AcpiPlatform`], allocating AML storage + /// through `alloc`. + pub fn new_from_platform_in( + platform: &AcpiPlatform, + alloc: A, + ) -> Result, AcpiError> { + fn load_table( + interpreter: &Interpreter, + table: AmlTable, + ) -> Result<(), AmlError> { let mapping = unsafe { interpreter.handler.map_physical_region::(table.phys_address, table.length as usize) }; @@ -171,7 +206,7 @@ where table.length as usize - mem::size_of::(), ) }; - interpreter.load_table(stream).map_err(AcpiError::Aml)?; + interpreter.load_table(stream)?; Ok(()) } @@ -185,7 +220,7 @@ where }; let dsdt = platform.tables.dsdt()?; - let interpreter = Interpreter::new(platform.handler.clone(), dsdt.revision, registers, facs); + let interpreter = Interpreter::new_in(platform.handler.clone(), dsdt.revision, registers, facs, alloc); if let Err(err) = load_table(&interpreter, dsdt) { error!("Error while loading DSDT: {:?}. Continuing; this may cause downstream errors.", err); @@ -204,21 +239,25 @@ where /// not the header at the start of a table. If you've used [`Interpreter::new_from_platform`], /// you'll likely not need to load any tables manually. pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> { - let context = unsafe { MethodContext::new_from_table(stream) }; + let context = unsafe { MethodContext::new_from_table(stream, self.alloc.clone()) }; self.do_execute_method(context)?; Ok(()) } /// Evaluate an object at the given path in the namespace. If the object is a method, this /// invokes the method with the given set of arguments. - pub fn evaluate(&self, path: AmlName, args: Vec) -> Result { + pub fn evaluate( + &self, + path: AmlName, + args: Vec, A>, + ) -> Result, AmlError> { trace!("Invoking AML method: {}", path); let object = self.namespace.lock().get(path.clone())?.clone(); match &*object { Object::Method { .. } => { self.namespace.lock().add_level(path.clone(), NamespaceLevelKind::MethodLocals)?; - let context = MethodContext::new_from_method(object, args, path)?; + let context = MethodContext::new_from_method(object, args, path, self.alloc.clone())?; self.do_execute_method(context) } Object::NativeMethod { f, .. } => f(&args), @@ -228,9 +267,9 @@ where pub fn evaluate_if_present( &self, - path: AmlName, - args: Vec, - ) -> Result, AmlError> { + path: AmlName, + args: Vec, A>, + ) -> Result>, AmlError> { match self.evaluate(path.clone(), args) { Ok(result) => Ok(Some(result)), Err(AmlError::ObjectDoesNotExist(not_present)) => { @@ -246,11 +285,11 @@ where pub fn install_region_handler(&self, space: RegionSpace, handler: RH) where - RH: RegionHandler + 'static, + RH: RegionHandler + 'static, { let mut handlers = self.region_handlers.lock(); assert!(handlers.get(&space).is_none(), "Tried to install handler for same space twice!"); - handlers.insert(space, Box::new(handler)); + handlers.insert(space, Box::new_in(handler, self.alloc.clone())); } /// Initialize the namespace - this should be called after all tables have been loaded and @@ -260,10 +299,16 @@ where /* * This should match the initialization order of ACPICA and uACPI. */ - if let Err(err) = self.evaluate_if_present(AmlName::from_str("\\_INI").unwrap(), vec![]) { + if let Err(err) = self.evaluate_if_present( + AmlName::parse_in("\\_INI", self.alloc.clone()).unwrap(), + vec_in!(self.alloc.clone()), + ) { warn!("Invoking \\_INI failed: {:?}", err); } - if let Err(err) = self.evaluate_if_present(AmlName::from_str("\\_SB._INI").unwrap(), vec![]) { + if let Err(err) = self.evaluate_if_present( + AmlName::parse_in("\\_SB._INI", self.alloc.clone()).unwrap(), + vec_in!(self.alloc.clone()), + ) { warn!("Invoking \\_SB._INI failed: {:?}", err); } @@ -292,9 +337,10 @@ where | NamespaceLevelKind::Processor | NamespaceLevelKind::ThermalZone | NamespaceLevelKind::PowerResource => { - let should_initialize = match self - .evaluate_if_present(AmlName::from_str("_STA").unwrap().resolve(path)?, vec![]) - { + let should_initialize = match self.evaluate_if_present( + AmlName::parse_in("_STA", self.alloc.clone()).unwrap().resolve(path)?, + vec_in!(self.alloc.clone()), + ) { Ok(Some(result)) => { let Object::Integer(result) = *result else { panic!() }; let status = DeviceStatus(result); @@ -309,9 +355,10 @@ where if should_initialize { num_devices_initialized += 1; - if let Err(err) = - self.evaluate_if_present(AmlName::from_str("_INI").unwrap().resolve(path)?, vec![]) - { + if let Err(err) = self.evaluate_if_present( + AmlName::parse_in("_INI", self.alloc.clone()).unwrap().resolve(path)?, + vec_in!(self.alloc.clone()), + ) { warn!("Failed to evaluate _INI for device {}: {:?}", path, err); } Ok(true) @@ -435,7 +482,20 @@ where } } - fn do_execute_method(&self, mut context: MethodContext) -> Result { + fn new_op(&self, op: Opcode, behaviours: &'static [ResolveBehaviour]) -> OpInFlight { + OpInFlight::new(op, behaviours, self.alloc.clone()) + } + + fn new_op_dynamic( + &self, + op: Opcode, + expected_arguments: usize, + behaviours: &'static [ResolveBehaviour], + ) -> OpInFlight { + OpInFlight::new_dynamic(op, expected_arguments, behaviours, self.alloc.clone()) + } + + fn do_execute_method(&self, mut context: MethodContext) -> Result, AmlError> { /* * This is the main loop that executes operations. Every op is handled at the top-level of * the loop to prevent pathological stack growth from nested operations. @@ -458,7 +518,7 @@ where * traditional fast bytecode VM, but also provides enough flexibility to handle the * quirkier parts of the AML grammar, particularly the left-to-right encoding of operands. */ - let mut context_stack: Vec = Vec::new(); + let mut context_stack: Vec, A> = Vec::new_in(self.alloc.clone()); loop { /* @@ -500,7 +560,8 @@ where }; *operand = new_value; - context.contribute_arg(Argument::Object(Object::Integer(new_value).wrap())); + context + .contribute_arg(Argument::Object(Object::Integer(new_value).wrap_in(self.alloc.clone()))); context.retire_op(op); } Opcode::LAnd @@ -529,13 +590,13 @@ where let source1 = source1.as_buffer()?; let source2 = source2.as_buffer()?; let result = { - let mut buffer = Vec::from(source1); + let mut buffer = source1.to_vec_in(self.alloc.clone()); buffer.extend_from_slice(source2); // Add a new end-tag buffer.push(0x78); // Don't calculate the new real checksum - just use 0 buffer.push(0x00); - Object::Buffer(buffer).wrap() + Object::Buffer(buffer).wrap_in(self.alloc.clone()) }; // TODO: use potentially-updated result for return value here self.do_store(target.clone(), result.clone())?; @@ -612,7 +673,7 @@ where } context.contribute_arg(Argument::Object( - Object::Integer(if timed_out { u64::MAX } else { 0 }).wrap(), + Object::Integer(if timed_out { u64::MAX } else { 0 }).wrap_in(self.alloc.clone()), )); } else { return Err(AmlError::InvalidOperationOnObject { @@ -661,7 +722,9 @@ where length: region_length.as_integer()?, parent_device_path: context.current_scope.clone(), }); - self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?; + self.namespace + .lock() + .insert(name.resolve(&context.current_scope)?, region.wrap_in(self.alloc.clone()))?; context.retire_op(op); } Opcode::DataRegion => { @@ -686,7 +749,9 @@ where length: 0, parent_device_path: context.current_scope.clone(), }); - self.namespace.lock().insert(name.resolve(&context.current_scope)?, region.wrap())?; + self.namespace + .lock() + .insert(name.resolve(&context.current_scope)?, region.wrap_in(self.alloc.clone()))?; context.retire_op(op); } Opcode::Buffer => { @@ -699,7 +764,7 @@ where buffer_size.clone().unwrap_transparent_reference().as_integer()? as usize; let buffer_len = pkg_length - (context.current_block.pc - start_pc); - let mut buffer = vec![0; buffer_size]; + let mut buffer = vec_in!(self.alloc.clone(); 0; buffer_size); /* * Copy the supplied elements into the buffer, avoiding a pathological case @@ -712,16 +777,14 @@ where ); context.current_block.pc += buffer_len; - context.contribute_arg(Argument::Object(Object::Buffer(buffer).wrap())); + context.contribute_arg(Argument::Object(Object::Buffer(buffer).wrap_in(self.alloc.clone()))); context.retire_op(op); } Opcode::Package => { - let mut elements = Vec::with_capacity(op.expected_arguments); + let mut elements = Vec::with_capacity_in(op.expected_arguments, self.alloc.clone()); for arg in &op.arguments { let Argument::Object(object) = arg else { - return Err(AmlError::InternalError( - "Invalid argument type produced for package element".to_string(), - )); + return Err(AmlError::InternalError("Invalid argument type produced for package element".to_string())); }; elements.push(object.clone()); } @@ -737,10 +800,11 @@ where * To make these consistent, we always remove the block here, making sure * we've finished it as a sanity check. */ - assert_eq!(context.current_block.kind, BlockKind::Package); - assert_eq!(context.peek(), Err(AmlError::RunOutOfStream)); + assert_eq!(context.current_block.kind, BlockKind::::Package); + assert_eq!(context.peek(), Err::(AmlError::RunOutOfStream)); context.current_block = context.block_stack.pop().unwrap(); - context.contribute_arg(Argument::Object(Object::Package(elements).wrap())); + context + .contribute_arg(Argument::Object(Object::Package(elements).wrap_in(self.alloc.clone()))); context.retire_op(op); } Opcode::VarPackage => { @@ -748,12 +812,10 @@ where let total_elements = total_elements.clone().unwrap_transparent_reference().as_integer()? as usize; - let mut elements = Vec::with_capacity(total_elements); + let mut elements = Vec::with_capacity_in(total_elements, self.alloc.clone()); for arg in &op.arguments[1..] { let Argument::Object(object) = arg else { - return Err(AmlError::InternalError( - "Invalid argument type produced for package element".to_string(), - )); + return Err(AmlError::InternalError("Invalid argument type produced for package element".to_string())); }; elements.push(object.clone()); } @@ -762,10 +824,11 @@ where * As above, we always remove the block here after the in-flight op has * been retired. */ - assert_eq!(context.current_block.kind, BlockKind::VarPackage); - assert_eq!(context.peek(), Err(AmlError::RunOutOfStream)); + assert_eq!(context.current_block.kind, BlockKind::::VarPackage); + assert_eq!(context.peek(), Err::(AmlError::RunOutOfStream)); context.current_block = context.block_stack.pop().unwrap(); - context.contribute_arg(Argument::Object(Object::Package(elements).wrap())); + context + .contribute_arg(Argument::Object(Object::Package(elements).wrap_in(self.alloc.clone()))); context.retire_op(op); } Opcode::If => { @@ -818,7 +881,8 @@ where }; self.namespace.lock().insert( name.resolve(&context.current_scope)?, - Object::BufferField { buffer: buffer.clone(), offset: offset as usize, length }.wrap(), + Object::BufferField { buffer: buffer.clone(), offset: offset as usize, length } + .wrap_in(self.alloc.clone()), )?; context.retire_op(op); } @@ -835,7 +899,7 @@ where offset: bit_index as usize, length: num_bits as usize, } - .wrap(), + .wrap_in(self.alloc.clone()), )?; context.retire_op(op); } @@ -851,8 +915,8 @@ where } Opcode::RefOf => { extract_args!(op => [Argument::Object(object)]); - let reference = - Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap(); + let reference = Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() } + .wrap_in(self.alloc.clone()); context.contribute_arg(Argument::Object(reference)); context.retire_op(op); } @@ -862,11 +926,12 @@ where Object::Integer(0) } else { let reference = - Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() }.wrap(); + Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() } + .wrap_in(self.alloc.clone()); self.do_store(target.clone(), reference)?; Object::Integer(u64::MAX) }; - context.contribute_arg(Argument::Object(result.wrap())); + context.contribute_arg(Argument::Object(result.wrap_in(self.alloc.clone()))); context.retire_op(op); } Opcode::DerefOf => { @@ -921,7 +986,7 @@ where if mutex == self.global_lock_mutex { self.acquire_global_lock(timeout)?; } else { - self.handler.acquire(mutex, timeout)?; + self.handler.acquire(mutex, timeout).map_err(|_| AmlError::MutexAcquireTimeout)?; } context.retire_op(op); @@ -944,24 +1009,29 @@ where } Opcode::InternalMethodCall => { extract_args!(op[0..2] => [Argument::Object(method), Argument::Namestring(method_scope)]); - let args = op.arguments[2..] - .iter() - .map(|arg| { - if let Argument::Object(arg) = arg { - arg.clone() - } else { - panic!(); - } - }) - .collect(); + // `.collect()` into a `Vec<_, A>` requires + // an allocator-aware FromIterator, which doesn't exist + // on stable nightly yet. Manual `push` loop instead. + let mut args: Vec, A> = Vec::new_in(self.alloc.clone()); + for arg in &op.arguments[2..] { + if let Argument::Object(arg) = arg { + args.push(arg.clone()); + } else { + panic!(); + } + } if let Object::Method { .. } = **method { self.namespace .lock() .add_level(method_scope.clone(), NamespaceLevelKind::MethodLocals)?; - let new_context = - MethodContext::new_from_method(method.clone(), args, method_scope.clone())?; + let new_context = MethodContext::new_from_method( + method.clone(), + args, + method_scope.clone(), + self.alloc.clone(), + )?; let old_context = mem::replace(&mut context, new_context); context_stack.push(old_context); context.retire_op(op); @@ -991,7 +1061,7 @@ where Opcode::ObjectType => { extract_args!(op => [Argument::Object(object)]); let object_type = self.object_type(object.clone())?; - context.contribute_arg(Argument::Object(Object::Integer(object_type).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(object_type).wrap_in(self.alloc.clone()))); context.retire_op(op); } Opcode::SizeOf => self.do_size_of(&mut context, op)?, @@ -1049,7 +1119,7 @@ where */ match context.current_block.kind { BlockKind::Table => { - break Ok(Object::Uninitialized.wrap()); + break Ok(Object::Uninitialized.wrap_in(self.alloc.clone())); } BlockKind::Method { method_scope } => { self.namespace.lock().remove_level(method_scope)?; @@ -1062,7 +1132,7 @@ where * If there is no explicit `Return` op, the result is undefined. We * just return an uninitialized object. */ - return Ok(Object::Uninitialized.wrap()); + return Ok(Object::Uninitialized.wrap_in(self.alloc.clone())); } } BlockKind::Scope { old_scope } => { @@ -1089,7 +1159,9 @@ where { let num_elements_left = package_op.expected_arguments - package_op.arguments.len(); for _ in 0..num_elements_left { - package_op.arguments.push(Argument::Object(Object::Uninitialized.wrap())); + package_op + .arguments + .push(Argument::Object(Object::Uninitialized.wrap_in(self.alloc.clone()))); } } @@ -1117,7 +1189,9 @@ where }; for _ in 0..num_elements_left { - package_op.arguments.push(Argument::Object(Object::Uninitialized.wrap())); + package_op + .arguments + .push(Argument::Object(Object::Uninitialized.wrap_in(self.alloc.clone()))); } } @@ -1154,7 +1228,7 @@ where * predicate. */ context.current_block.pc = start_pc; - context.start(OpInFlight::new(Opcode::While, &[ResolveBehaviour::TermArg])); + context.start(self.new_op(Opcode::While, &[ResolveBehaviour::TermArg])); continue; } } @@ -1168,13 +1242,13 @@ where * most places, but could also encode a `NullName` if we are expecting a * `Target`. We handle the latter in logic for stores to targets. */ - context.contribute_arg(Argument::Object(Object::Integer(0).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(0).wrap_in(self.alloc.clone()))); } Opcode::One => { - context.contribute_arg(Argument::Object(Object::Integer(1).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(1).wrap_in(self.alloc.clone()))); } Opcode::Ones => { - context.contribute_arg(Argument::Object(Object::Integer(u64::MAX).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(u64::MAX).wrap_in(self.alloc.clone()))); } Opcode::Alias => { let source = context.namestring()?; @@ -1189,35 +1263,39 @@ where let name = context.namestring()?; context.start(OpInFlight::new_with( Opcode::Name, - vec![Argument::Namestring(name)], + vec_in!(self.alloc.clone(); Argument::Namestring(name)), &[ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg], )); } Opcode::BytePrefix => { let value = context.next()?; - context.contribute_arg(Argument::Object(Object::Integer(value as u64).wrap())); + context + .contribute_arg(Argument::Object(Object::Integer(value as u64).wrap_in(self.alloc.clone()))); } Opcode::WordPrefix => { let value = context.next_u16()?; - context.contribute_arg(Argument::Object(Object::Integer(value as u64).wrap())); + context + .contribute_arg(Argument::Object(Object::Integer(value as u64).wrap_in(self.alloc.clone()))); } Opcode::DWordPrefix => { let value = context.next_u32()?; - context.contribute_arg(Argument::Object(Object::Integer(value as u64).wrap())); + context + .contribute_arg(Argument::Object(Object::Integer(value as u64).wrap_in(self.alloc.clone()))); } Opcode::StringPrefix => { let str_start = context.current_block.pc; while context.next()? != b'\0' {} // TODO: handle err - let str = String::from( - str::from_utf8(&context.current_block.stream()[str_start..(context.current_block.pc - 1)]) - .unwrap(), - ); - context.contribute_arg(Argument::Object(Object::String(str).wrap())); + let s = core::str::from_utf8( + &context.current_block.stream()[str_start..(context.current_block.pc - 1)], + ) + .unwrap(); + let str = AmlString::from_str_in(s, self.alloc.clone()); + context.contribute_arg(Argument::Object(Object::String(str).wrap_in(self.alloc.clone()))); } Opcode::QWordPrefix => { let value = context.next_u64()?; - context.contribute_arg(Argument::Object(Object::Integer(value).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(value).wrap_in(self.alloc.clone()))); } Opcode::Scope => { let start_pc = context.current_block.pc; @@ -1237,7 +1315,7 @@ where let pkg_length = context.pkglength()?; context.start(OpInFlight::new_with( Opcode::Buffer, - vec![Argument::TrackedPc(start_pc), Argument::PkgLength(pkg_length)], + vec_in!(self.alloc.clone(); Argument::TrackedPc(start_pc), Argument::PkgLength(pkg_length)), &[ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg], )); } @@ -1255,7 +1333,7 @@ where * combination of a block to manage the pkglength, plus an in-flight op to * store interpreted arguments. */ - context.start(OpInFlight::new_dynamic( + context.start(self.new_op_dynamic( Opcode::Package, num_elements as usize, &[ResolveBehaviour::AsPackageElements], @@ -1273,7 +1351,7 @@ where * elements as remain in the block, and we'll sort out how many are supposed to * be in the package later. */ - context.start(OpInFlight::new_dynamic( + context.start(self.new_op_dynamic( Opcode::VarPackage, usize::MAX, &[ResolveBehaviour::TermArg, ResolveBehaviour::AsPackageElements], @@ -1289,11 +1367,11 @@ where let code_len = pkg_length - (context.current_block.pc - start_pc); let code = context.current_block.stream() [context.current_block.pc..(context.current_block.pc + code_len)] - .to_vec(); + .to_vec_in(self.alloc.clone()); context.current_block.pc += code_len; let name = name.resolve(&context.current_scope)?; - self.namespace.lock().insert(name, Object::Method { code, flags }.wrap())?; + self.namespace.lock().insert(name, Object::Method { code, flags }.wrap_in(self.alloc.clone()))?; } Opcode::External => { let _name = context.namestring()?; @@ -1306,62 +1384,71 @@ where let name = name.resolve(&context.current_scope)?; let mutex = self.handler.create_mutex(); - self.namespace.lock().insert(name, Object::Mutex { mutex, sync_level }.wrap())?; + self.namespace + .lock() + .insert(name, Object::Mutex { mutex, sync_level }.wrap_in(self.alloc.clone()))?; } Opcode::Event => { let name = context.namestring()?; let name = name.resolve(&context.current_scope)?; - self.namespace.lock().insert(name, Object::Event(Arc::new(AtomicU64::new(0))).wrap())?; + self.namespace.lock().insert( + name, + Object::Event(Arc::new_in(AtomicU64::new(0), self.alloc.clone())).wrap_in(self.alloc.clone()), + )?; } Opcode::LoadTable => { - context.start(OpInFlight::new(Opcode::LoadTable, &[ResolveBehaviour::TermArg; 6])); + context.start(self.new_op(Opcode::LoadTable, &[ResolveBehaviour::TermArg; 6])); } Opcode::Load => { let name = context.namestring()?; context.start(OpInFlight::new_with( Opcode::Load, - vec![Argument::Namestring(name)], + vec_in!(self.alloc.clone(); Argument::Namestring(name)), &[ResolveBehaviour::Target], )); } - Opcode::Stall => context.start(OpInFlight::new(Opcode::Stall, &[ResolveBehaviour::TermArg])), - Opcode::Sleep => context.start(OpInFlight::new(Opcode::Sleep, &[ResolveBehaviour::TermArg])), - Opcode::Acquire => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::Release => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::Signal => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::Wait => context - .start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName, ResolveBehaviour::TermArg])), - Opcode::Reset => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::Notify => context - .start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName, ResolveBehaviour::TermArg])), + Opcode::Stall => context.start(self.new_op(Opcode::Stall, &[ResolveBehaviour::TermArg])), + Opcode::Sleep => context.start(self.new_op(Opcode::Sleep, &[ResolveBehaviour::TermArg])), + Opcode::Acquire => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::Release => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::Signal => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::Wait => { + context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName, ResolveBehaviour::TermArg])) + } + Opcode::Reset => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::Notify => { + context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName, ResolveBehaviour::TermArg])) + } Opcode::FromBCD | Opcode::ToBCD => { - context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) + context.start(self.new_op(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) } Opcode::Revision => { - context.contribute_arg(Argument::Object(Object::Integer(INTERPRETER_REVISION).wrap())); + context.contribute_arg(Argument::Object( + Object::Integer(INTERPRETER_REVISION).wrap_in(self.alloc.clone()), + )); } - Opcode::Debug => context.contribute_arg(Argument::Object(Object::Debug.wrap())), + Opcode::Debug => context.contribute_arg(Argument::Object(Object::Debug.wrap_in(self.alloc.clone()))), Opcode::Fatal => { let typ = context.next()?; let code = context.next_u32()?; context.start(OpInFlight::new_with( Opcode::Fatal, - vec![Argument::ByteData(typ), Argument::DWordData(code)], + vec_in!(self.alloc.clone(); Argument::ByteData(typ), Argument::DWordData(code)), &[ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg], )); } Opcode::Timer => { // Time has to be monotonically-increasing, in 100ns units let time = self.handler.nanos_since_boot() / 100; - context.contribute_arg(Argument::Object(Object::Integer(time).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(time).wrap_in(self.alloc.clone()))); } Opcode::OpRegion => { let name = context.namestring()?; let region_space = context.next()?; context.start(OpInFlight::new_with( Opcode::OpRegion, - vec![Argument::Namestring(name), Argument::ByteData(region_space)], + vec_in!(self.alloc.clone(); Argument::Namestring(name), Argument::ByteData(region_space)), &[ ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, @@ -1374,7 +1461,7 @@ where let name = context.namestring()?; context.start(OpInFlight::new_with( Opcode::DataRegion, - vec![Argument::Namestring(name)], + vec_in!(self.alloc.clone(); Argument::Namestring(name)), &[ ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg, @@ -1401,12 +1488,12 @@ where context.start(OpInFlight::new_with( Opcode::BankField, - vec![ + vec_in!(self.alloc.clone(); Argument::TrackedPc(start_pc), Argument::PkgLength(pkg_length), Argument::Namestring(region_name), Argument::Namestring(bank_name), - ], + ), &[ ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, @@ -1474,7 +1561,7 @@ where }; let mut namespace = self.namespace.lock(); namespace.add_level(new_scope.clone(), kind)?; - namespace.insert(new_scope.clone(), object.wrap())?; + namespace.insert(new_scope.clone(), object.wrap_in(self.alloc.clone()))?; let old_scope = mem::replace(&mut context.current_scope, new_scope); context.start_new_block(BlockKind::Scope { old_scope }, remaining_length); @@ -1493,7 +1580,7 @@ where let object = Object::Processor { proc_id, pblk_address, pblk_length }; let mut namespace = self.namespace.lock(); namespace.add_level(new_scope.clone(), NamespaceLevelKind::Processor)?; - namespace.insert(new_scope.clone(), object.wrap())?; + namespace.insert(new_scope.clone(), object.wrap_in(self.alloc.clone()))?; let old_scope = mem::replace(&mut context.current_scope, new_scope); context.start_new_block(BlockKind::Scope { old_scope }, remaining_length); @@ -1511,7 +1598,7 @@ where let object = Object::PowerResource { system_level, resource_order }; let mut namespace = self.namespace.lock(); namespace.add_level(new_scope.clone(), NamespaceLevelKind::PowerResource)?; - namespace.insert(new_scope.clone(), object.wrap())?; + namespace.insert(new_scope.clone(), object.wrap_in(self.alloc.clone()))?; let old_scope = mem::replace(&mut context.current_scope, new_scope); context.start_new_block(BlockKind::Scope { old_scope }, remaining_length); @@ -1519,28 +1606,23 @@ where Opcode::Local(local) => { let local = context.locals[local as usize].clone(); context.contribute_arg(Argument::Object( - Object::Reference { kind: ReferenceKind::Local, inner: local }.wrap(), + Object::Reference { kind: ReferenceKind::Local, inner: local }.wrap_in(self.alloc.clone()), )); } Opcode::Arg(arg) => { let arg = context.args[arg as usize].clone(); context.contribute_arg(Argument::Object( - Object::Reference { kind: ReferenceKind::Arg, inner: arg }.wrap(), + Object::Reference { kind: ReferenceKind::Arg, inner: arg }.wrap_in(self.alloc.clone()), )); } - Opcode::Store => context.start(OpInFlight::new( - Opcode::Store, - &[ResolveBehaviour::TermArg, ResolveBehaviour::SuperName], - )), - Opcode::CopyObject => context.start(OpInFlight::new( - Opcode::CopyObject, - &[ResolveBehaviour::TermArg, ResolveBehaviour::SimpleName], - )), - Opcode::RefOf => context.start(OpInFlight::new(Opcode::RefOf, &[ResolveBehaviour::SuperName])), - Opcode::CondRefOf => context.start(OpInFlight::new( - opcode, - &[ResolveBehaviour::SuperNameIfExists, ResolveBehaviour::Target], - )), + Opcode::Store => context + .start(self.new_op(Opcode::Store, &[ResolveBehaviour::TermArg, ResolveBehaviour::SuperName])), + Opcode::CopyObject => context.start( + self.new_op(Opcode::CopyObject, &[ResolveBehaviour::TermArg, ResolveBehaviour::SimpleName]), + ), + Opcode::RefOf => context.start(self.new_op(Opcode::RefOf, &[ResolveBehaviour::SuperName])), + Opcode::CondRefOf => context + .start(self.new_op(opcode, &[ResolveBehaviour::SuperNameIfExists, ResolveBehaviour::Target])), Opcode::DualNamePrefix | Opcode::MultiNamePrefix @@ -1563,7 +1645,8 @@ where match object { Ok((_resolved_name, object)) => { context.contribute_arg(Argument::Object( - Object::Reference { kind: ReferenceKind::Named, inner: object }.wrap(), + Object::Reference { kind: ReferenceKind::Named, inner: object } + .wrap_in(self.alloc.clone()), )); } Err(err) => Err(err)?, @@ -1576,11 +1659,14 @@ where context.contribute_arg(Argument::Object(object)); } Err(AmlError::ObjectDoesNotExist(_)) => { + let mut name_str = AmlString::new_in(self.alloc.clone()); + use core::fmt::Write; + write!(name_str, "{}", name).unwrap(); let reference = Object::Reference { kind: ReferenceKind::Unresolved, - inner: Object::String(name.to_string()).wrap(), + inner: Object::String(name_str).wrap_in(self.alloc.clone()), }; - context.contribute_arg(Argument::Object(reference.wrap())); + context.contribute_arg(Argument::Object(reference.wrap_in(self.alloc.clone()))); } Err(err) => Err(err)?, } @@ -1594,7 +1680,7 @@ where { context.start(OpInFlight::new_with_dynamic( Opcode::InternalMethodCall, - vec![Argument::Object(object), Argument::Namestring(resolved_name)], + vec_in!(self.alloc.clone(); Argument::Object(object), Argument::Namestring(resolved_name)), flags.arg_count(), &[ ResolveBehaviour::Placeholder, @@ -1613,8 +1699,9 @@ where let value = self.do_field_read(field)?; context.contribute_arg(Argument::Object(value)); } else if let Object::BufferField { .. } = *object { - let value = object.read_buffer_field(self.integer_size)?; - context.contribute_arg(Argument::Object(value.wrap())); + let value = + object.read_buffer_field(self.integer_size, self.alloc.clone())?; + context.contribute_arg(Argument::Object(value.wrap_in(self.alloc.clone()))); } else { context.contribute_arg(Argument::Object(object)); } @@ -1631,7 +1718,7 @@ where * indexing the package creates a reference to the element when needed. */ context.contribute_arg(Argument::Object( - Object::NamePath { name, scope: context.current_scope.clone() }.wrap(), + Object::NamePath { name, scope: context.current_scope.clone() }.wrap_in(self.alloc.clone()), )); } ResolveBehaviour::Placeholder => { @@ -1652,13 +1739,13 @@ where | Opcode::Nor | Opcode::Xor | Opcode::Concat => { - context.start(OpInFlight::new( + context.start(self.new_op( opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target], )); } - Opcode::Divide => context.start(OpInFlight::new( + Opcode::Divide => context.start(self.new_op( Opcode::Divide, &[ ResolveBehaviour::TermArg, @@ -1668,22 +1755,23 @@ where ], )), Opcode::Increment | Opcode::Decrement => { - context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])) + context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])) + } + Opcode::Not => { + context.start(self.new_op(Opcode::Not, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) } - Opcode::Not => context - .start(OpInFlight::new(Opcode::Not, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])), Opcode::FindSetLeftBit | Opcode::FindSetRightBit => { - context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) + context.start(self.new_op(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) } // Resolve as a SuperName so BufferField and FieldUnit objects are not read eagerly // as TermArgs; DerefOf handles those reads itself when executing the opcode. - Opcode::DerefOf => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::ConcatRes => context.start(OpInFlight::new( + Opcode::DerefOf => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::ConcatRes => context.start(self.new_op( opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target], )), - Opcode::SizeOf => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::Index => context.start(OpInFlight::new( + Opcode::SizeOf => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::Index => context.start(self.new_op( opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target], )), @@ -1700,15 +1788,13 @@ where | Opcode::CreateByteField | Opcode::CreateWordField | Opcode::CreateDWordField - | Opcode::CreateQWordField => { - context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg; 2])) - } + | Opcode::CreateQWordField => context.start(self.new_op(opcode, &[ResolveBehaviour::TermArg; 2])), Opcode::CreateField => { - context.start(OpInFlight::new(Opcode::CreateField, &[ResolveBehaviour::TermArg; 3])) + context.start(self.new_op(Opcode::CreateField, &[ResolveBehaviour::TermArg; 3])) } Opcode::LNot => { - context.start(OpInFlight::new(Opcode::LNot, &[ResolveBehaviour::TermArg])); + context.start(self.new_op(Opcode::LNot, &[ResolveBehaviour::TermArg])); } Opcode::LAnd @@ -1719,19 +1805,19 @@ where | Opcode::LEqual | Opcode::LGreater | Opcode::LLess => { - context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg; 2])); + context.start(self.new_op(opcode, &[ResolveBehaviour::TermArg; 2])); } Opcode::ToBuffer | Opcode::ToDecimalString | Opcode::ToHexString | Opcode::ToInteger => { - context.start(OpInFlight::new(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) + context.start(self.new_op(opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::Target])) } - Opcode::ToString => context.start(OpInFlight::new( + Opcode::ToString => context.start(self.new_op( opcode, &[ResolveBehaviour::TermArg, ResolveBehaviour::TermArg, ResolveBehaviour::Target], )), - Opcode::ObjectType => context.start(OpInFlight::new(opcode, &[ResolveBehaviour::SuperName])), - Opcode::Mid => context.start(OpInFlight::new( + Opcode::ObjectType => context.start(self.new_op(opcode, &[ResolveBehaviour::SuperName])), + Opcode::Mid => context.start(self.new_op( Opcode::Mid, &[ ResolveBehaviour::TermArg, @@ -1745,7 +1831,7 @@ where let then_length = context.pkglength()?; context.start(OpInFlight::new_with( Opcode::If, - vec![Argument::TrackedPc(start_pc), Argument::PkgLength(then_length)], + vec_in!(self.alloc.clone(); Argument::TrackedPc(start_pc), Argument::PkgLength(then_length)), &[ResolveBehaviour::Placeholder, ResolveBehaviour::Placeholder, ResolveBehaviour::TermArg], )); } @@ -1758,7 +1844,7 @@ where BlockKind::While { start_pc: context.current_block.pc }, remaining_length, ); - context.start(OpInFlight::new(Opcode::While, &[ResolveBehaviour::TermArg])); + context.start(self.new_op(Opcode::While, &[ResolveBehaviour::TermArg])); } Opcode::Continue => { if let BlockKind::While { start_pc } = &context.current_block.kind { @@ -1774,7 +1860,7 @@ where } } } - context.start(OpInFlight::new(Opcode::While, &[ResolveBehaviour::TermArg])); + context.start(self.new_op(Opcode::While, &[ResolveBehaviour::TermArg])); } Opcode::Break => { if let BlockKind::While { .. } = &context.current_block.kind { @@ -1791,7 +1877,7 @@ where } } } - Opcode::Return => context.start(OpInFlight::new(Opcode::Return, &[ResolveBehaviour::TermArg])), + Opcode::Return => context.start(self.new_op(Opcode::Return, &[ResolveBehaviour::TermArg])), Opcode::Noop => {} Opcode::Breakpoint => { self.handler.breakpoint(); @@ -1804,8 +1890,8 @@ where fn parse_field_list( &self, - context: &mut MethodContext, - kind: FieldUnitKind, + context: &mut MethodContext, + kind: FieldUnitKind, start_pc: usize, pkg_length: usize, mut flags: u8, @@ -1857,7 +1943,9 @@ where bit_length: field_length, flags: FieldFlags(flags), }); - self.namespace.lock().insert(field_name.resolve(&context.current_scope)?, field.wrap())?; + self.namespace + .lock() + .insert(field_name.resolve(&context.current_scope)?, field.wrap_in(self.alloc.clone()))?; field_offset += field_length; } @@ -1867,12 +1955,14 @@ where Ok(()) } - fn do_binary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_binary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op[0..3] => [Argument::Object(left), Argument::Object(right), Argument::Object(target)]); let target2 = if op.op == Opcode::Divide { Some(&op.arguments[3]) } else { None }; - let left = left.clone().unwrap_transparent_reference().to_integer(self.integer_size)?; - let right = right.clone().unwrap_transparent_reference().to_integer(self.integer_size)?; + let left = + left.clone().unwrap_transparent_reference().to_integer(self.integer_size, self.alloc.clone())?; + let right = + right.clone().unwrap_transparent_reference().to_integer(self.integer_size, self.alloc.clone())?; let result = match op.op { Opcode::Add => left.wrapping_add(right), @@ -1880,7 +1970,10 @@ where Opcode::Multiply => left.wrapping_mul(right), Opcode::Divide => { if let Some(Argument::Object(remainder)) = target2 { - self.do_store(remainder.clone(), Object::Integer(left.wrapping_rem(right)).wrap())?; + self.do_store( + remainder.clone(), + Object::Integer(left.wrapping_rem(right)).wrap_in(self.alloc.clone()), + )?; } left.wrapping_div_euclid(right) } @@ -1895,14 +1988,14 @@ where _ => panic!(), }; - let result = Object::Integer(result).wrap(); + let result = Object::Integer(result).wrap_in(self.alloc.clone()); let result = self.do_store(target.clone(), result)?; context.contribute_arg(Argument::Object(result)); context.retire_op(op); Ok(()) } - fn do_unary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_unary_maths(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(operand)]); let operand = operand.clone().unwrap_transparent_reference().as_integer()?; @@ -1938,18 +2031,18 @@ where _ => panic!(), }; - context.contribute_arg(Argument::Object(Object::Integer(result).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(result).wrap_in(self.alloc.clone()))); context.retire_op(op); Ok(()) } - fn do_logical_op(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_logical_op(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { if op.op == Opcode::LNot { extract_args!(op => [Argument::Object(operand)]); let operand = operand.clone().unwrap_transparent_reference().as_integer()?; let result = if operand == 0 { u64::MAX } else { 0 }; - context.contribute_arg(Argument::Object(Object::Integer(result).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(result).wrap_in(self.alloc.clone()))); context.retire_op(op); return Ok(()); } @@ -1963,14 +2056,14 @@ where // Make sure both sides are the same type. let right = match *left { Object::Integer(_) => &Object::Integer(right.as_integer()?), - Object::String(_) => &Object::String(right.as_string()?.parse().unwrap()), + Object::String(_) => &Object::String(AmlString::from_str_in(right.as_string()?, self.alloc.clone())), Object::Buffer(_) => { // When doing && or ||, uACPI and NT only compare the first 4 bytes of a buffer. int_size = IntegerSize::FourBytes; if right.typ() == ObjectType::Buffer { &*right } else { - &Object::Buffer(right.to_buffer(self.integer_size)?) + &Object::Buffer(right.to_buffer(self.integer_size, self.alloc.clone())?) } } _ => Err(AmlError::InvalidOperationOnObject { op: Operation::LogicalOp, typ: left.typ() })?, @@ -1978,8 +2071,14 @@ where let ordering = left.aml_cmp(right); let result = match op.op { - Opcode::LAnd => (left.to_integer(int_size)? > 0) && (right.to_integer(int_size)? > 0), - Opcode::LOr => (left.to_integer(int_size)? > 0) || (right.to_integer(int_size)? > 0), + Opcode::LAnd => { + (left.to_integer(int_size, self.alloc.clone())? > 0) + && (right.to_integer(int_size, self.alloc.clone())? > 0) + } + Opcode::LOr => { + (left.to_integer(int_size, self.alloc.clone())? > 0) + || (right.to_integer(int_size, self.alloc.clone())? > 0) + } Opcode::LNotEqual => ordering?.is_ne(), Opcode::LLessEqual => ordering?.is_le(), Opcode::LGreaterEqual => ordering?.is_ge(), @@ -1990,12 +2089,12 @@ where }; let result = if result { Object::Integer(u64::MAX) } else { Object::Integer(0) }; - context.contribute_arg(Argument::Object(result.wrap())); + context.contribute_arg(Argument::Object(result.wrap_in(self.alloc.clone()))); context.retire_op(op); Ok(()) } - fn do_to_buffer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_to_buffer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(operand), Argument::Object(target)]); let operand = operand.clone().unwrap_transparent_reference(); @@ -2003,24 +2102,24 @@ where Object::Buffer(ref bytes) => Object::Buffer(bytes.clone()), Object::Integer(value) => { if self.integer_size == IntegerSize::EightBytes { - Object::Buffer(value.to_le_bytes().to_vec()) + Object::Buffer(value.to_le_bytes().to_vec_in(self.alloc.clone())) } else { - Object::Buffer((value as u32).to_le_bytes().to_vec()) + Object::Buffer((value as u32).to_le_bytes().to_vec_in(self.alloc.clone())) } } Object::String(ref value) => { // XXX: an empty string is converted to an empty buffer, *without* the null-terminator if value.is_empty() { - Object::Buffer(vec![]) + Object::Buffer(vec_in!(self.alloc.clone())) } else { - let mut bytes = value.as_bytes().to_vec(); + let mut bytes = value.as_bytes().to_vec_in(self.alloc.clone()); bytes.push(b'\0'); Object::Buffer(bytes) } } _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?, } - .wrap(); + .wrap_in(self.alloc.clone()); let result = self.do_store(target.clone(), result)?; context.contribute_arg(Argument::Object(result)); @@ -2028,37 +2127,38 @@ where Ok(()) } - fn do_to_integer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_to_integer(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(operand), Argument::Object(target)]); let operand = operand.clone().unwrap_transparent_reference(); - let result = Object::Integer(operand.to_integer(self.integer_size)?).wrap(); + let result = + Object::Integer(operand.to_integer(self.integer_size, self.alloc.clone())?).wrap_in(self.alloc.clone()); let result = self.do_store(target.clone(), result)?; context.contribute_arg(Argument::Object(result)); context.retire_op(op); Ok(()) } - fn do_to_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_to_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(source), Argument::Object(length), Argument::Object(target)]); let source = source.clone().unwrap_transparent_reference(); let source = source.as_buffer()?; let length = length.clone().unwrap_transparent_reference().as_integer()? as usize; let result = if source.is_empty() { - Object::String(String::new()) + Object::String(AmlString::new_in(self.alloc.clone())) } else { let mut buffer = source.split_inclusive(|b| *b == b'\0').next().unwrap(); if length < usize::MAX { buffer = &buffer[0..usize::min(length, buffer.len())]; } - let string = str::from_utf8(buffer).map_err(|_| AmlError::InvalidOperationOnObject { + let string = core::str::from_utf8(buffer).map_err(|_| AmlError::InvalidOperationOnObject { op: Operation::ToString, typ: ObjectType::Buffer, })?; - Object::String(string.to_string()) + Object::String(AmlString::from_str_in(string, self.alloc.clone())) } - .wrap(); + .wrap_in(self.alloc.clone()); let result = self.do_store(target.clone(), result)?; context.contribute_arg(Argument::Object(result)); @@ -2067,40 +2167,44 @@ where } /// Perform a `ToDecimalString` or `ToHexString` operation - fn do_to_dec_hex_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_to_dec_hex_string(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(operand), Argument::Object(target)]); let operand = operand.clone().unwrap_transparent_reference(); let result = match *operand { Object::String(ref value) => Object::String(value.clone()), - Object::Integer(value) => match op.op { - Opcode::ToDecimalString => Object::String(value.to_string()), - Opcode::ToHexString => Object::String(alloc::format!("{value:#X}")), - _ => panic!(), - }, + Object::Integer(value) => { + let mut s = AmlString::new_in(self.alloc.clone()); + use core::fmt::Write; + match op.op { + Opcode::ToDecimalString => write!(s, "{value}").unwrap(), + Opcode::ToHexString => write!(s, "{value:#X}").unwrap(), + _ => panic!(), + }; + Object::String(s) + } Object::Buffer(ref bytes) => { if bytes.is_empty() { - Object::String(String::new()) + Object::String(AmlString::new_in(self.alloc.clone())) } else { - let mut string = String::new(); - for byte in bytes { - let as_str = match op.op { - Opcode::ToDecimalString => alloc::format!("{byte},"), - Opcode::ToHexString => alloc::format!("{byte:#04X},"), + let mut string = AmlString::new_in(self.alloc.clone()); + use core::fmt::Write; + for (index, byte) in bytes.iter().enumerate() { + if index > 0 { + string.push(','); + } + match op.op { + Opcode::ToDecimalString => write!(string, "{byte}").unwrap(), + Opcode::ToHexString => write!(string, "{byte:#04X}").unwrap(), _ => panic!(), - }; - string.push_str(&as_str); - } - // Remove last comma, if present - if !string.is_empty() { - string.pop(); + } } Object::String(string) } } _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToDecOrHexString, typ: operand.typ() })?, } - .wrap(); + .wrap_in(self.alloc.clone()); let result = self.do_store(target.clone(), result)?; context.contribute_arg(Argument::Object(result)); @@ -2108,7 +2212,7 @@ where Ok(()) } - fn do_mid(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_mid(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(source), Argument::Object(index), Argument::Object(length), Argument::Object(target)]); let index = index.clone().unwrap_transparent_reference().as_integer()? as usize; let length = length.clone().unwrap_transparent_reference().as_integer()? as usize; @@ -2116,25 +2220,25 @@ where let result = match **source { Object::String(ref string) => { if index >= string.len() { - Object::String(String::new()) + Object::String(AmlString::new_in(self.alloc.clone())) } else { let upper = usize::min(index + length, index + string.len()); - let chars = &string[index..upper]; - Object::String(String::from(chars)) + let chars = &string.as_str()[index..upper]; + Object::String(AmlString::from_str_in(chars, self.alloc.clone())) } } Object::Buffer(ref buffer) => { if index >= buffer.len() { - Object::Buffer(vec![]) + Object::Buffer(vec_in!(self.alloc.clone())) } else { let upper = usize::min(index + length, index + buffer.len()); let bytes = &buffer[index..upper]; - Object::Buffer(bytes.to_vec()) + Object::Buffer(bytes.to_vec_in(self.alloc.clone())) } } _ => Err(AmlError::InvalidOperationOnObject { op: Operation::Mid, typ: source.typ() })?, } - .wrap(); + .wrap_in(self.alloc.clone()); self.do_store(target.clone(), result.clone())?; context.contribute_arg(Argument::Object(result)); @@ -2142,41 +2246,48 @@ where Ok(()) } - fn do_concat(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_concat(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(source1), Argument::Object(source2), Argument::Object(target)]); let source1 = source1.clone().unwrap_transparent_reference(); let source2 = source2.clone().unwrap_transparent_reference(); - fn resolve_as_string(obj: &Object) -> String { + fn resolve_as_string(obj: &Object, alloc: A) -> AmlString { + use core::fmt::Write; + let mut s = AmlString::new_in(alloc.clone()); match obj { - Object::Uninitialized => "[Uninitialized Object]".to_string(), - Object::Buffer(bytes) => String::from_utf8_lossy(bytes).into_owned(), - Object::BufferField { .. } => "[Buffer Field]".to_string(), - Object::Device => "[Device]".to_string(), - Object::Event(_) => "[Event]".to_string(), - Object::FieldUnit(_) => "[Field]".to_string(), - Object::Integer(value) => value.to_string(), - Object::Method { .. } | Object::NativeMethod { .. } => "[Control Method]".to_string(), - Object::Mutex { .. } => "[Mutex]".to_string(), - Object::Reference { inner, .. } => resolve_as_string(&(inner.clone().unwrap_reference())), + Object::Uninitialized => s.push_str("[Uninitialized Object]"), + Object::Buffer(bytes) => { + s.push_str(AmlString::from_utf8_lossy_in(bytes, alloc.clone()).as_str()) + } + Object::BufferField { .. } => s.push_str("[Buffer Field]"), + Object::Device => s.push_str("[Device]"), + Object::Event(_) => s.push_str("[Event]"), + Object::FieldUnit(_) => s.push_str("[Field]"), + Object::Integer(value) => write!(s, "{value}").unwrap(), + Object::Method { .. } | Object::NativeMethod { .. } => s.push_str("[Control Method]"), + Object::Mutex { .. } => s.push_str("[Mutex]"), + Object::Reference { inner, .. } => { + s.push_str(resolve_as_string(&inner.clone().unwrap_reference(), alloc.clone()).as_str()) + } // We can't resolve the name here, as we don't have access to the namespace - Object::NamePath { name, .. } => name.to_string(), - Object::OpRegion(_) => "[Operation Region]".to_string(), - Object::Package(_) => "[Package]".to_string(), - Object::PowerResource { .. } => "[Power Resource]".to_string(), - Object::Processor { .. } => "[Processor]".to_string(), - Object::RawDataBuffer => "[Raw Data Buffer]".to_string(), - Object::String(value) => value.clone(), - Object::ThermalZone => "[Thermal Zone]".to_string(), - Object::Debug => "[Debug Object]".to_string(), + Object::NamePath { name, .. } => write!(s, "{name}").unwrap(), + Object::OpRegion(_) => s.push_str("[Operation Region]"), + Object::Package(_) => s.push_str("[Package]"), + Object::PowerResource { .. } => s.push_str("[Power Resource]"), + Object::Processor { .. } => s.push_str("[Processor]"), + Object::RawDataBuffer => s.push_str("[Raw Data Buffer]"), + Object::String(value) => s.push_str(value.as_str()), + Object::ThermalZone => s.push_str("[Thermal Zone]"), + Object::Debug => s.push_str("[Debug Object]"), } + s } let result = match source1.typ() { ObjectType::Integer => { let source1 = source1.as_integer()?; - let source2 = source2.to_integer(self.integer_size)?; - let mut buffer = Vec::new(); + let source2 = source2.to_integer(self.integer_size, self.alloc.clone())?; + let mut buffer = Vec::new_in(self.alloc.clone()); if self.integer_size == IntegerSize::EightBytes { buffer.extend_from_slice(&source1.to_le_bytes()); buffer.extend_from_slice(&source2.to_le_bytes()); @@ -2184,17 +2295,20 @@ where buffer.extend_from_slice(&(source1 as u32).to_le_bytes()); buffer.extend_from_slice(&(source2 as u32).to_le_bytes()); } - Object::Buffer(buffer).wrap() + Object::Buffer(buffer).wrap_in(self.alloc.clone()) } ObjectType::Buffer => { - let mut buffer = source1.as_buffer()?.to_vec(); - buffer.extend(source2.to_buffer(self.integer_size)?); - Object::Buffer(buffer).wrap() + let mut buffer = source1.as_buffer()?.to_vec_in(self.alloc.clone()); + buffer.extend(source2.to_buffer(self.integer_size, self.alloc.clone())?); + Object::Buffer(buffer).wrap_in(self.alloc.clone()) } _ => { - let source1 = resolve_as_string(&source1); - let source2 = resolve_as_string(&source2); - Object::String(source1 + &source2).wrap() + let s1 = resolve_as_string(&source1, self.alloc.clone()); + let s2 = resolve_as_string(&source2, self.alloc.clone()); + let mut combined = AmlString::new_in(self.alloc.clone()); + combined.push_str(s1.as_str()); + combined.push_str(s2.as_str()); + Object::String(combined).wrap_in(self.alloc.clone()) } }; @@ -2204,7 +2318,7 @@ where Ok(()) } - fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_from_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(value)]); let mut value = value.clone().unwrap_transparent_reference().as_integer()?; @@ -2216,12 +2330,12 @@ where value >>= 4; } - context.contribute_arg(Argument::Object(Object::Integer(result).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(result).wrap_in(self.alloc.clone()))); context.retire_op(op); Ok(()) } - fn do_to_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_to_bcd(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(value)]); let mut value = value.clone().unwrap_transparent_reference().as_integer()?; @@ -2233,12 +2347,12 @@ where i += 1; } - context.contribute_arg(Argument::Object(Object::Integer(result).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(result).wrap_in(self.alloc.clone()))); context.retire_op(op); Ok(()) } - fn do_size_of(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_size_of(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(object)]); let object = self.resolve_name_path(object.clone())?; @@ -2249,12 +2363,12 @@ where _ => Err(AmlError::InvalidOperationOnObject { op: Operation::SizeOf, typ: object.typ() })?, }; - context.contribute_arg(Argument::Object(Object::Integer(result as u64).wrap())); + context.contribute_arg(Argument::Object(Object::Integer(result as u64).wrap_in(self.alloc.clone()))); context.retire_op(op); Ok(()) } - fn do_index(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { + fn do_index(&self, context: &mut MethodContext, op: OpInFlight) -> Result<(), AmlError> { extract_args!(op => [Argument::Object(object), Argument::Object(index_value), Argument::Object(target)]); let object = object.clone().unwrap_transparent_reference(); let index_value = index_value.clone().unwrap_transparent_reference().as_integer()?; @@ -2272,7 +2386,7 @@ where offset: index_value as usize * 8, length: 8, } - .wrap(), + .wrap_in(self.alloc.clone()), } } Object::String(ref string) => { @@ -2287,7 +2401,7 @@ where offset: index_value as usize * 8, length: 8, } - .wrap(), + .wrap_in(self.alloc.clone()), } } Object::Package(ref package) => { @@ -2296,7 +2410,7 @@ where } _ => Err(AmlError::IndexOutOfBounds)?, } - .wrap(); + .wrap_in(self.alloc.clone()); self.do_store(target.clone(), result.clone())?; context.contribute_arg(Argument::Object(result)); @@ -2307,7 +2421,7 @@ where /// Resolve an object to the value it refers to, looking up `NamePath`s (i.e. names that were /// used as package elements) in the namespace. Objects that aren't references are returned /// unchanged. - fn resolve_name_path(&self, object: WrappedObject) -> Result { + fn resolve_name_path(&self, object: WrappedObject) -> Result, AmlError> { let mut object = object.unwrap_reference(); /* @@ -2326,7 +2440,7 @@ where Err(AmlError::NameResolutionLoop) } - fn object_type(&self, object: WrappedObject) -> Result { + fn object_type(&self, object: WrappedObject) -> Result { let object = self.resolve_name_path(object)?; // TODO: this should technically support scopes as well - this is less easy @@ -2354,13 +2468,13 @@ where }) } - fn do_deref_of(&self, object: WrappedObject, current_scope: &AmlName) -> Result { + fn do_deref_of(&self, object: WrappedObject, current_scope: &AmlName) -> Result, AmlError> { let object = self.resolve_name_path(object)?; match &*object { - Object::BufferField { .. } => Ok(object.read_buffer_field(self.integer_size)?.wrap()), + Object::BufferField { .. } => Ok(object.read_buffer_field(self.integer_size, self.alloc.clone())?.wrap_in(self.alloc.clone())), Object::FieldUnit(field) => self.do_field_read(field), Object::String(path) => { - let path = AmlName::from_str(path)?; + let path = AmlName::parse_in(path.as_str(), self.alloc.clone())?; let (_, object) = self.namespace.lock().search(&path, current_scope)?; Ok(object.clone()) } @@ -2377,7 +2491,11 @@ where /// object is overwritten /// - Index references behave the same as locals /// - Named objects are stored into, with implicit casting - fn do_store(&self, target: WrappedObject, object: WrappedObject) -> Result { + fn do_store( + &self, + target: WrappedObject, + object: WrappedObject, + ) -> Result, AmlError> { let object = object.unwrap_transparent_reference(); let token = self.object_token.lock(); @@ -2444,7 +2562,9 @@ where } } } - Object::Debug => self.handler.handle_debug(&object), + Object::Debug => { + // TODO: Route Debug stores through Handler once Handler can accept allocator-aware objects. + } Object::Integer(0) => {} // Store to NullName _ => return Err(AmlError::InvalidOperationOnObject { op: Operation::Store, typ: target.typ() }), } @@ -2459,7 +2579,7 @@ where /// - Objects referenced by name are overwritten /// - Index references cause the object at the index to be overwritten /// - Other reference operations are not allowed - fn do_copy_object(&self, target: WrappedObject, object: WrappedObject) -> Result<(), AmlError> { + fn do_copy_object(&self, target: WrappedObject, object: WrappedObject) -> Result<(), AmlError> { let Object::Reference { kind, ref inner } = *target else { return Err(AmlError::InternalError("Target of CopyObject must be a reference".to_string())); }; @@ -2490,7 +2610,7 @@ where /// operation regions, and then shifting and masking the resulting value as appropriate. Will /// return either an `Integer` or `Buffer` as appropriate, guided by the size of the field /// and expected integer size (as per the DSDT revision). - fn do_field_read(&self, field: &FieldUnit) -> Result { + fn do_field_read(&self, field: &FieldUnit) -> Result, AmlError> { let needs_buffer = field.bit_length > (self.integer_size as usize * 8); let access_width_bits = field.flags.access_type_bytes()? * 8; @@ -2498,12 +2618,12 @@ where // TODO: if the field needs to be locked, acquire/release a global mutex? - enum Output { + enum Output { Integer([u8; 8]), - Buffer(Vec), + Buffer(Vec), } let mut output = if needs_buffer { - Output::Buffer(vec![0; field.bit_length.next_multiple_of(8)]) + Output::Buffer(vec_in!(self.alloc.clone(); 0; field.bit_length.next_multiple_of(8))) } else { Output::Integer([0; 8]) }; @@ -2517,7 +2637,7 @@ where FieldUnitKind::Bank { ref region, ref bank, bank_value } => { let Object::FieldUnit(ref bank) = **bank else { panic!() }; assert!(matches!(bank.kind, FieldUnitKind::Normal { .. })); - self.do_field_write(bank, Object::Integer(bank_value).wrap())?; + self.do_field_write(bank, Object::Integer(bank_value).wrap_in(self.alloc.clone()))?; (region, 0) } FieldUnitKind::Index { index: _, ref data } => { @@ -2556,7 +2676,8 @@ where let Object::FieldUnit(ref data) = **data else { panic!() }; self.do_field_write( index, - Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(), + Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64) + .wrap_in(self.alloc.clone()), )?; // The offset is always that of the data register, as we always read from the @@ -2579,12 +2700,12 @@ where } match output { - Output::Buffer(bytes) => Ok(Object::Buffer(bytes).wrap()), - Output::Integer(value) => Ok(Object::Integer(u64::from_le_bytes(value)).wrap()), + Output::Buffer(bytes) => Ok(Object::Buffer(bytes).wrap_in(self.alloc.clone())), + Output::Integer(value) => Ok(Object::Integer(u64::from_le_bytes(value)).wrap_in(self.alloc.clone())), } } - fn do_field_write(&self, field: &FieldUnit, value: WrappedObject) -> Result<(), AmlError> { + fn do_field_write(&self, field: &FieldUnit, value: WrappedObject) -> Result<(), AmlError> { trace!("AML field write. Field = {:?}. Value = {}", field, value); let value_bytes = match &*value { @@ -2603,7 +2724,7 @@ where FieldUnitKind::Bank { ref region, ref bank, bank_value } => { let Object::FieldUnit(ref bank) = **bank else { panic!() }; assert!(matches!(bank.kind, FieldUnitKind::Normal { .. })); - self.do_field_write(bank, Object::Integer(bank_value).wrap())?; + self.do_field_write(bank, Object::Integer(bank_value).wrap_in(self.alloc.clone()))?; (region, 0) } FieldUnitKind::Index { index: _, ref data } => { @@ -2637,7 +2758,8 @@ where let Object::FieldUnit(ref data) = **data else { panic!() }; self.do_field_write( index, - Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(), + Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64) + .wrap_in(self.alloc.clone()), )?; // The offset is always that of the data register, as we always read from the @@ -2687,7 +2809,12 @@ where /// Performs an actual read from an operation region. `offset` and `length` must respect the /// access requirements of the field being read, and are supplied in **bytes**. This may call /// AML methods if required, and may invoke user-supplied handlers. - fn do_native_region_read(&self, region: &OpRegion, offset: usize, length: usize) -> Result { + fn do_native_region_read( + &self, + region: &OpRegion, + offset: usize, + length: usize, + ) -> Result { trace!("Native field read. Region = {:?}, offset = {:#x}, length={:#x}", region, offset, length); match region.space { @@ -2745,7 +2872,7 @@ where /// AML methods if required, and may invoke user-supplied handlers. fn do_native_region_write( &self, - region: &OpRegion, + region: &OpRegion, offset: usize, length: usize, value: u64, @@ -2808,21 +2935,30 @@ where } } - fn pci_address_for_device(&self, path: &AmlName) -> Result { + fn pci_address_for_device(&self, path: &AmlName) -> Result { /* * TODO: it's not ideal to do these reads for every native access. See if we can * cache them somewhere? */ - let seg = match self.evaluate_if_present(AmlName::from_str("_SEG").unwrap().resolve(path)?, vec![])? { + let seg = match self.evaluate_if_present( + AmlName::parse_in("_SEG", self.alloc.clone()).unwrap().resolve(path)?, + vec_in!(self.alloc.clone()), + )? { Some(value) => value.as_integer()?, None => 0, }; - let bus = match self.evaluate_if_present(AmlName::from_str("_BBN").unwrap().resolve(path)?, vec![])? { + let bus = match self.evaluate_if_present( + AmlName::parse_in("_BBN", self.alloc.clone()).unwrap().resolve(path)?, + vec_in!(self.alloc.clone()), + )? { Some(value) => value.as_integer()?, None => 0, }; let (device, function) = { - let adr = self.evaluate_if_present(AmlName::from_str("_ADR").unwrap().resolve(path)?, vec![])?; + let adr = self.evaluate_if_present( + AmlName::parse_in("_ADR", self.alloc.clone()).unwrap().resolve(path)?, + vec_in!(self.alloc.clone()), + )?; let adr = match adr { Some(adr) => adr.as_integer()?, None => 0, @@ -2842,37 +2978,37 @@ where /// preempt method contexts that execute other methods, and these contexts may have disparate /// lifetimes. This is made safe in the case of methods by the context holding a reference to the /// method object, but must be handled manually for AML tables. -struct MethodContext { - current_block: Block, - block_stack: Vec, - in_flight: Vec, - args: [WrappedObject; 8], - locals: [WrappedObject; 8], - current_scope: AmlName, - - _method: Option, +struct MethodContext { + current_block: Block, + block_stack: Vec, A>, + in_flight: Vec, A>, + args: [WrappedObject; 8], + locals: [WrappedObject; 8], + current_scope: AmlName, + + _method: Option>, + alloc: A, } -struct Block { +struct Block { stream: *const [u8], pc: usize, - kind: BlockKind, + kind: BlockKind, } -impl Block { +impl Block { fn stream(&self) -> &[u8] { unsafe { &*self.stream } } } -#[derive(PartialEq, Debug)] -pub enum BlockKind { +pub enum BlockKind { Table, Method { - method_scope: AmlName, + method_scope: AmlName, }, Scope { - old_scope: AmlName, + old_scope: AmlName, }, Package, VarPackage, @@ -2884,6 +3020,37 @@ pub enum BlockKind { }, } +impl core::fmt::Debug for BlockKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use BlockKind::*; + match self { + Table => f.write_str("Table"), + Method { method_scope } => write!(f, "Method {{ method_scope: {:?} }}", method_scope), + Scope { old_scope } => write!(f, "Scope {{ old_scope: {:?} }}", old_scope), + Package => f.write_str("Package"), + VarPackage => f.write_str("VarPackage"), + IfThenBranch => f.write_str("IfThenBranch"), + While { start_pc } => write!(f, "While {{ start_pc: {} }}", start_pc), + } + } +} + +// PartialEq for BlockKind is impl'd manually to avoid +// deriving with `A: PartialEq` bound. The AmlName fields use the +// AmlName cross-A PartialEq impl from namespace.rs. +impl PartialEq> for BlockKind { + fn eq(&self, other: &BlockKind) -> bool { + use BlockKind::*; + match (self, other) { + (Table, Table) | (Package, Package) | (VarPackage, VarPackage) | (IfThenBranch, IfThenBranch) => true, + (Method { method_scope: a }, Method { method_scope: b }) => a == b, + (Scope { old_scope: a }, Scope { old_scope: b }) => a == b, + (While { start_pc: a }, While { start_pc: b }) => a == b, + _ => false, + } + } +} + /// A `ResolveBehaviour` describes how a name at the top-level should be resolved as part of an /// operation. #[derive(Clone, Copy, PartialEq, Debug)] @@ -2916,29 +3083,58 @@ enum ResolveBehaviour { Placeholder, } -#[derive(Debug)] -struct OpInFlight { +struct OpInFlight { op: Opcode, expected_arguments: usize, - arguments: Vec, + arguments: Vec, A>, resolve_behaviour: &'static [ResolveBehaviour], } -#[derive(Debug)] -enum Argument { - Object(WrappedObject), - Namestring(AmlName), +enum Argument { + Object(WrappedObject), + Namestring(AmlName), ByteData(u8), DWordData(u32), TrackedPc(usize), PkgLength(usize), } -impl OpInFlight { +// Manual Debug impl - derive auto-adds `A: Debug` which `&'static BumpArena` +// doesn't satisfy. +impl core::fmt::Debug for OpInFlight { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("OpInFlight") + .field("op", &self.op) + .field("expected_arguments", &self.expected_arguments) + .field("arguments", &self.arguments) + .finish_non_exhaustive() + } +} + +impl core::fmt::Debug for Argument { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use Argument::*; + match self { + Object(o) => write!(f, "Object({})", **o), + Namestring(n) => write!(f, "Namestring({:?})", n), + ByteData(b) => write!(f, "ByteData({})", b), + DWordData(d) => write!(f, "DWordData({})", d), + TrackedPc(pc) => write!(f, "TrackedPc({})", pc), + PkgLength(l) => write!(f, "PkgLength({})", l), + } + } +} + +impl OpInFlight { /// Creates a new `OpInFlight`. The number of expected arguments is inferred from the number of /// `ResolveBehaviour`s passed. - pub fn new(op: Opcode, resolve_behaviour: &'static [ResolveBehaviour]) -> OpInFlight { - OpInFlight { op, expected_arguments: resolve_behaviour.len(), arguments: Vec::new(), resolve_behaviour } + pub fn new(op: Opcode, resolve_behaviour: &'static [ResolveBehaviour], alloc: A) -> OpInFlight { + OpInFlight { + op, + expected_arguments: resolve_behaviour.len(), + arguments: Vec::new_in(alloc), + resolve_behaviour, + } } /// Creates a new `OpInFlight` with the given number of expected arguments. This should be used @@ -2948,18 +3144,19 @@ impl OpInFlight { op: Opcode, expected_arguments: usize, resolve_behaviour: &'static [ResolveBehaviour], - ) -> OpInFlight { - OpInFlight { op, expected_arguments, arguments: Vec::new(), resolve_behaviour } + alloc: A, + ) -> OpInFlight { + OpInFlight { op, expected_arguments, arguments: Vec::new_in(alloc), resolve_behaviour } } /// Creates a new `OpInFlight` with a number of arguments that have already been interpreted, /// and is expecting some `more` arguments. pub fn new_with_dynamic( op: Opcode, - arguments: Vec, + arguments: Vec, A>, more: usize, resolve_behaviour: &'static [ResolveBehaviour], - ) -> OpInFlight { + ) -> OpInFlight { OpInFlight { op, expected_arguments: arguments.len() + more, arguments, resolve_behaviour } } @@ -2969,9 +3166,9 @@ impl OpInFlight { /// `ResolveBehaviour::Placeholder`). pub fn new_with( op: Opcode, - arguments: Vec, + arguments: Vec, A>, resolve_behaviour: &'static [ResolveBehaviour], - ) -> OpInFlight { + ) -> OpInFlight { OpInFlight { op, expected_arguments: resolve_behaviour.len(), arguments, resolve_behaviour } } @@ -2987,25 +3184,28 @@ impl OpInFlight { } } -impl MethodContext { - unsafe fn new_from_table(stream: &[u8]) -> MethodContext { +impl MethodContext { + unsafe fn new_from_table(stream: &[u8], alloc: A) -> MethodContext { let block = Block { stream: stream as *const [u8], pc: 0, kind: BlockKind::Table }; + let local_alloc = alloc.clone(); MethodContext { current_block: block, - block_stack: Vec::new(), - in_flight: Vec::new(), - args: core::array::from_fn(|_| Object::Uninitialized.wrap()), - locals: core::array::from_fn(|_| Object::Uninitialized.wrap()), - current_scope: AmlName::root(), + block_stack: Vec::new_in(alloc.clone()), + in_flight: Vec::new_in(alloc.clone()), + args: core::array::from_fn(|_| Object::Uninitialized.wrap_in(local_alloc.clone())), + locals: core::array::from_fn(|_| Object::Uninitialized.wrap_in(local_alloc.clone())), + current_scope: AmlName::root_in(alloc.clone()), _method: None, + alloc, } } fn new_from_method( - method: WrappedObject, - args: Vec, - scope: AmlName, - ) -> Result { + method: WrappedObject, + args: Vec, A>, + scope: AmlName, + alloc: A, + ) -> Result, AmlError> { if let Object::Method { code, flags } = &*method { if args.len() != flags.arg_count() { return Err(AmlError::MethodArgCountIncorrect); @@ -3015,17 +3215,23 @@ impl MethodContext { pc: 0, kind: BlockKind::Method { method_scope: scope.clone() }, }; + let local_alloc = alloc.clone(); let args = core::array::from_fn(|i| { - if let Some(arg) = args.get(i) { arg.clone() } else { Object::Uninitialized.wrap() } + if let Some(arg) = args.get(i) { + arg.clone() + } else { + Object::Uninitialized.wrap_in(local_alloc.clone()) + } }); let context = MethodContext { current_block: block, - block_stack: Vec::new(), - in_flight: Vec::new(), + block_stack: Vec::new_in(alloc.clone()), + in_flight: Vec::new_in(alloc.clone()), args, - locals: core::array::from_fn(|_| Object::Uninitialized.wrap()), + locals: core::array::from_fn(|_| Object::Uninitialized.wrap_in(local_alloc.clone())), current_scope: scope, _method: Some(method.clone()), + alloc, }; Ok(context) } else { @@ -3033,7 +3239,7 @@ impl MethodContext { } } - fn contribute_arg(&mut self, arg: Argument) { + fn contribute_arg(&mut self, arg: Argument) { if let Some(in_flight) = self.in_flight.last_mut() && in_flight.arguments.len() < in_flight.expected_arguments { @@ -3042,7 +3248,7 @@ impl MethodContext { } /// Start a new `InFlightOp`. - fn start(&mut self, op: OpInFlight) { + fn start(&mut self, op: OpInFlight) { trace!( "START OP: {:?}, args: {:?}, with {} more needed ({:?})", op.op, @@ -3053,11 +3259,11 @@ impl MethodContext { self.in_flight.push(op); } - fn retire_op(&mut self, op: OpInFlight) { + fn retire_op(&mut self, op: OpInFlight) { trace!("RETIRE OP: {:?}, args: {:?}", op.op, op.arguments); } - fn start_new_block(&mut self, kind: BlockKind, length: usize) { + fn start_new_block(&mut self, kind: BlockKind, length: usize) { let block = Block { stream: &self.current_block.stream()[..(self.current_block.pc + length)] as *const [u8], pc: self.current_block.pc, @@ -3223,7 +3429,7 @@ impl MethodContext { } } - fn namestring(&mut self) -> Result { + fn namestring(&mut self) -> Result, AmlError> { use namespace::{NameComponent, NameSeg}; /* @@ -3239,7 +3445,7 @@ impl MethodContext { const DUAL_NAME_PREFIX: u8 = 0x2e; const MULTI_NAME_PREFIX: u8 = 0x2f; - let mut components = vec![]; + let mut components = vec_in!(self.alloc.clone()); match self.peek()? { b'\\' => { @@ -3536,7 +3742,7 @@ pub enum AmlError { /// The library has given a response the host does not understand, or the host is otherwise /// unable to continue operating the library correctly. The specific reason is given in the - /// contained String. + /// contained string. /// /// This variant is set by the host, not by the library, and can be used when it is convenient /// not to construct a more complex error type around [`AmlError`]. @@ -3547,6 +3753,8 @@ pub enum AmlError { InternalError(String), } + + #[derive(Debug, Clone, Copy, PartialEq)] pub enum IntegerSize { FourBytes = 4, diff --git a/src/aml/namespace.rs b/src/aml/namespace.rs index faeb1023..e9f4dcc8 100644 --- a/src/aml/namespace.rs +++ b/src/aml/namespace.rs @@ -2,40 +2,56 @@ use super::{ AmlError, Handle, object::{Object, ObjectType, WrappedObject}, + string::AmlString, }; -use alloc::{ - collections::btree_map::BTreeMap, - string::{String, ToString}, - vec, - vec::Vec, -}; +use alloc::{alloc::Global, collections::btree_map::BTreeMap, string::String, vec::Vec}; use bit_field::BitField; -use core::{ - fmt, - str::{self, FromStr}, -}; +use core::{alloc::Allocator, fmt, str, str::FromStr}; use log::{trace, warn}; #[derive(Clone)] -pub struct Namespace { - root: NamespaceLevel, +pub struct Namespace { + alloc: A, + root: NamespaceLevel, } -impl Namespace { +impl Namespace { + pub fn new(global_lock_mutex: Handle) -> Namespace { + Namespace::new_in(global_lock_mutex, Global) + } +} + +impl Namespace { /// Create a new AML namespace, with the expected pre-defined objects. - pub fn new(global_lock_mutex: Handle) -> Namespace { - let mut namespace = Namespace { root: NamespaceLevel::new(NamespaceLevelKind::Scope) }; + pub fn new_in(global_lock_mutex: Handle, alloc: A) -> Namespace + where + A: 'static, + { + let mut namespace = Namespace { + alloc: alloc.clone(), + root: NamespaceLevel::new_in(NamespaceLevelKind::Scope, alloc.clone()), + }; - namespace.add_level(AmlName::from_str("\\_GPE").unwrap(), NamespaceLevelKind::Scope).unwrap(); - namespace.add_level(AmlName::from_str("\\_SB").unwrap(), NamespaceLevelKind::Scope).unwrap(); - namespace.add_level(AmlName::from_str("\\_SI").unwrap(), NamespaceLevelKind::Scope).unwrap(); - namespace.add_level(AmlName::from_str("\\_PR").unwrap(), NamespaceLevelKind::Scope).unwrap(); - namespace.add_level(AmlName::from_str("\\_TZ").unwrap(), NamespaceLevelKind::Scope).unwrap(); + namespace + .add_level(AmlName::parse_in("\\_GPE", alloc.clone()).unwrap(), NamespaceLevelKind::Scope) + .unwrap(); + namespace + .add_level(AmlName::parse_in("\\_SB", alloc.clone()).unwrap(), NamespaceLevelKind::Scope) + .unwrap(); + namespace + .add_level(AmlName::parse_in("\\_SI", alloc.clone()).unwrap(), NamespaceLevelKind::Scope) + .unwrap(); + namespace + .add_level(AmlName::parse_in("\\_PR", alloc.clone()).unwrap(), NamespaceLevelKind::Scope) + .unwrap(); + namespace + .add_level(AmlName::parse_in("\\_TZ", alloc.clone()).unwrap(), NamespaceLevelKind::Scope) + .unwrap(); namespace .insert( - AmlName::from_str("\\_GL").unwrap(), - Object::Mutex { mutex: global_lock_mutex, sync_level: 0 }.wrap(), + AmlName::parse_in("\\_GL", alloc.clone()).unwrap(), + Object::Mutex { mutex: global_lock_mutex, sync_level: 0 }.wrap_in(alloc.clone()), ) .unwrap(); @@ -47,8 +63,12 @@ impl Namespace { * * See https://www.kernel.org/doc/html/latest/firmware-guide/acpi/osi.html for more information. */ + let os_name = AmlString::from_str_in("Microsoft Windows NT", alloc.clone()); namespace - .insert(AmlName::from_str("\\_OS").unwrap(), Object::String("Microsoft Windows NT".to_string()).wrap()) + .insert( + AmlName::parse_in("\\_OS", alloc.clone()).unwrap(), + Object::String(os_name).wrap_in(alloc.clone()), + ) .unwrap(); /* @@ -62,67 +82,69 @@ impl Namespace { * - We answer 'yes' to `_OSI("Darwin") * - We answer 'no' to `_OSI("Linux")`, and report that the tables are doing the wrong thing */ - namespace - .insert( - AmlName::from_str("\\_OSI").unwrap(), - Object::native_method(1, |args| { - if args.len() != 1 { - return Err(AmlError::MethodArgCountIncorrect); + let inner_alloc = alloc.clone(); + let osi_method = Object::native_method( + 1, + move |args| -> Result, AmlError> { + if args.len() != 1 { + return Err(AmlError::MethodArgCountIncorrect); + } + let Object::String(ref feature) = *args[0] else { + return Err(AmlError::ObjectNotOfExpectedType { + expected: ObjectType::String, + got: args[0].typ(), + }); + }; + + let is_supported = match feature.as_str() { + "Windows 2000" => true, // 2000 + "Windows 2001" => true, // XP + "Windows 2001 SP1" => true, // XP SP1 + "Windows 2001 SP2" => true, // XP SP2 + "Windows 2001.1" => true, // Server 2003 + "Windows 2001.1 SP1" => true, // Server 2003 SP1 + "Windows 2006" => true, // Vista + "Windows 2006 SP1" => true, // Vista SP1 + "Windows 2006 SP2" => true, // Vista SP2 + "Windows 2006.1" => true, // Server 2008 + "Windows 2009" => true, // 7 and Server 2008 R2 + "Windows 2012" => true, // 8 and Server 2012 + "Windows 2013" => true, // 8.1 and Server 2012 R2 + "Windows 2015" => true, // 10 + "Windows 2016" => true, // 10 version 1607 + "Windows 2017" => true, // 10 version 1703 + "Windows 2017.2" => true, // 10 version 1709 + "Windows 2018" => true, // 10 version 1803 + "Windows 2018.2" => true, // 10 version 1809 + "Windows 2019" => true, // 10 version 1903 + "Windows 2020" => true, // 10 version 20H1 + "Windows 2021" => true, // 11 + "Windows 2022" => true, // 11 version 22H2 + + // TODO: Linux answers yes to this, NT answers no. Maybe make configurable + "Darwin" => false, + + "Linux" => { + // TODO: should we allow users to specify that this should be true? Linux has a + // command line option for this. + warn!("ACPI evaluated `_OSI(\"Linux\")`. This is a bug. Reporting no support."); + false } - let Object::String(ref feature) = *args[0] else { - return Err(AmlError::ObjectNotOfExpectedType { - expected: ObjectType::String, - got: args[0].typ(), - }); - }; - - let is_supported = match feature.as_str() { - "Windows 2000" => true, // 2000 - "Windows 2001" => true, // XP - "Windows 2001 SP1" => true, // XP SP1 - "Windows 2001 SP2" => true, // XP SP2 - "Windows 2001.1" => true, // Server 2003 - "Windows 2001.1 SP1" => true, // Server 2003 SP1 - "Windows 2006" => true, // Vista - "Windows 2006 SP1" => true, // Vista SP1 - "Windows 2006 SP2" => true, // Vista SP2 - "Windows 2006.1" => true, // Server 2008 - "Windows 2009" => true, // 7 and Server 2008 R2 - "Windows 2012" => true, // 8 and Server 2012 - "Windows 2013" => true, // 8.1 and Server 2012 R2 - "Windows 2015" => true, // 10 - "Windows 2016" => true, // 10 version 1607 - "Windows 2017" => true, // 10 version 1703 - "Windows 2017.2" => true, // 10 version 1709 - "Windows 2018" => true, // 10 version 1803 - "Windows 2018.2" => true, // 10 version 1809 - "Windows 2019" => true, // 10 version 1903 - "Windows 2020" => true, // 10 version 20H1 - "Windows 2021" => true, // 11 - "Windows 2022" => true, // 11 version 22H2 - - // TODO: Linux answers yes to this, NT answers no. Maybe make configurable - "Darwin" => false, - - "Linux" => { - // TODO: should we allow users to specify that this should be true? Linux has a - // command line option for this. - warn!("ACPI evaluated `_OSI(\"Linux\")`. This is a bug. Reporting no support."); - false - } - "Extended Address Space Descriptor" => true, - "Module Device" => true, - "3.0 Thermal Model" => true, - "3.0 _SCP Extensions" => true, - "Processor Aggregator Device" => true, - _ => false, - }; - - Ok(Object::Integer(if is_supported { u64::MAX } else { 0 }).wrap()) - }) - .wrap(), - ) + "Extended Address Space Descriptor" => true, + "Module Device" => true, + "3.0 Thermal Model" => true, + "3.0 _SCP Extensions" => true, + "Processor Aggregator Device" => true, + _ => false, + }; + + Ok(Object::Integer(if is_supported { u64::MAX } else { 0 }).wrap_in(inner_alloc.clone())) + }, + alloc.clone(), + ); + namespace + .insert(AmlName::parse_in("\\_OSI", alloc.clone()).unwrap(), osi_method.wrap_in(alloc.clone())) .unwrap(); /* @@ -131,34 +153,41 @@ impl Namespace { * return `2`), and so they switched to just returning `2` (as we'll also do). `_REV` should be considered * useless and deprecated (this is mirrored in newer specs, which claim `2` means "ACPI 2 or greater"). */ - namespace.insert(AmlName::from_str("\\_REV").unwrap(), Object::Integer(2).wrap()).unwrap(); + namespace + .insert(AmlName::parse_in("\\_REV", alloc.clone()).unwrap(), Object::Integer(2).wrap_in(alloc.clone())) + .unwrap(); namespace } - pub fn add_level(&mut self, path: AmlName, kind: NamespaceLevelKind) -> Result<(), AmlError> { + pub fn add_level(&mut self, path: AmlName, kind: NamespaceLevelKind) -> Result<(), AmlError> { let path = path.normalize_absolute()?; // Don't try to recreate the root scope - if path != AmlName::root() { + if path != AmlName::root_in(self.alloc.clone()) { + // clone `self.alloc` *before* the mutable borrow + // below. Doing it after (as we did initially) trips E0502 - + // `level` is `&mut`-borrowed from `self`, so `self.alloc` can't + // be read until `level` is dropped. + let level_alloc = self.alloc.clone(); let (level, last_seg) = self.get_level_for_path_mut(&path)?; /* * If the level has already been added, we don't need to add it again. The parser can try to add it * multiple times if the ASL contains multiple blocks that add to the same scope/device. */ - level.children.entry(last_seg).or_insert_with(|| NamespaceLevel::new(kind)); + level.children.entry(last_seg).or_insert_with(move || NamespaceLevel::new_in(kind, level_alloc)); } Ok(()) } - pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> { + pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> { let path = path.normalize_absolute()?; // Don't try to remove the root scope // TODO: we probably shouldn't be able to remove the pre-defined scopes either? - if path != AmlName::root() { + if path != AmlName::root_in(self.alloc.clone()) { let (level, last_seg) = self.get_level_for_path_mut(&path)?; level.children.remove(&last_seg); } @@ -166,7 +195,7 @@ impl Namespace { Ok(()) } - pub fn insert(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> { + pub fn insert(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> { let path = path.normalize_absolute()?; let (level, last_seg) = self.get_level_for_path_mut(&path)?; @@ -183,30 +212,30 @@ impl Namespace { } } - pub fn create_alias(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> { + pub fn create_alias(&mut self, path: AmlName, object: WrappedObject) -> Result<(), AmlError> { let path = path.normalize_absolute()?; let (level, last_seg) = self.get_level_for_path_mut(&path)?; match level.values.insert(last_seg, (ObjectFlags::new(true), object)) { None => Ok(()), - Some(_) => Err(AmlError::NameCollision(path)), + Some(_) => Err(AmlError::NameCollision(path.to_global())), } } - pub fn get(&mut self, path: AmlName) -> Result { + pub fn get(&mut self, path: AmlName) -> Result, AmlError> { let path = path.normalize_absolute()?; let (level, last_seg) = self.get_level_for_path_mut(&path)?; match level.values.get(&last_seg) { Some((_, object)) => Ok(object.clone()), - None => Err(AmlError::ObjectDoesNotExist(path.clone())), + None => Err(AmlError::ObjectDoesNotExist(path.to_global())), } } /// Search for an object at the given path of the namespace, applying the search rules described in §5.3 of the /// ACPI specification, if they are applicable. Returns the resolved name, and the handle of the first valid /// object, if found. Errors if `starting_scope` is not absolute. - pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, WrappedObject), AmlError> { + pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<(AmlName, WrappedObject), AmlError> { starting_scope.require_absolute()?; if path.search_rules_apply() { @@ -232,7 +261,7 @@ impl Namespace { // If we don't find it, go up a level in the namespace and search for it there recursively match scope.parent() { Ok(parent) => scope = parent, - Err(AmlError::RootHasNoParent) => return Err(AmlError::ObjectDoesNotExist(path.clone())), + Err(AmlError::RootHasNoParent) => return Err(AmlError::ObjectDoesNotExist(path.to_global())), Err(err) => return Err(err), } } @@ -244,12 +273,12 @@ impl Namespace { if let Some((_, object)) = level.values.get(&last_seg) { Ok((name, object.clone())) } else { - Err(AmlError::ObjectDoesNotExist(path.clone())) + Err(AmlError::ObjectDoesNotExist(path.to_global())) } } } - pub fn search_for_level(&self, level_name: &AmlName, starting_scope: &AmlName) -> Result { + pub fn search_for_level(&self, level_name: &AmlName, starting_scope: &AmlName) -> Result, AmlError> { starting_scope.require_absolute()?; if level_name.search_rules_apply() { @@ -266,7 +295,7 @@ impl Namespace { // If we don't find it, move the scope up a level and search for it there recursively match scope.parent() { Ok(parent) => scope = parent, - Err(AmlError::RootHasNoParent) => return Err(AmlError::LevelDoesNotExist(level_name.clone())), + Err(AmlError::RootHasNoParent) => return Err(AmlError::LevelDoesNotExist(level_name.to_global())), Err(err) => return Err(err), } } @@ -277,8 +306,8 @@ impl Namespace { /// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a /// last segment to index into that level. This must not be called on `\\`. - fn get_level_for_path(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> { - assert_ne!(*path, AmlName::root()); + fn get_level_for_path(&self, path: &AmlName) -> Result<(&NamespaceLevel, NameSeg), AmlError> { + assert_ne!(*path, AmlName::root_in(self.alloc.clone())); let (last_seg, levels) = path.0[1..].split_last().unwrap(); let NameComponent::Segment(last_seg) = last_seg else { @@ -286,7 +315,7 @@ impl Namespace { }; // TODO: this helps with diagnostics, but requires a heap allocation just in case we need to error. - let mut traversed_path = AmlName::root(); + let mut traversed_path = AmlName::root_in(self.alloc.clone()); let mut current_level = &self.root; for level in levels { @@ -296,7 +325,7 @@ impl Namespace { panic!(); }; current_level = - current_level.children.get(segment).ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?; + current_level.children.get(segment).ok_or(AmlError::LevelDoesNotExist(traversed_path.to_global()))?; } Ok((current_level, *last_seg)) @@ -304,8 +333,11 @@ impl Namespace { /// Split an absolute path into a bunch of level segments (used to traverse the level data structure), and a /// last segment to index into that level. This must not be called on `\\`. - fn get_level_for_path_mut(&mut self, path: &AmlName) -> Result<(&mut NamespaceLevel, NameSeg), AmlError> { - assert_ne!(*path, AmlName::root()); + fn get_level_for_path_mut( + &mut self, + path: &AmlName, + ) -> Result<(&mut NamespaceLevel, NameSeg), AmlError> { + assert_ne!(*path, AmlName::root_in(self.alloc.clone())); let (last_seg, levels) = path.0[1..].split_last().unwrap(); let NameComponent::Segment(last_seg) = last_seg else { @@ -315,7 +347,7 @@ impl Namespace { // TODO: this helps with diagnostics, but requires a heap allocation just in case we need to error. We can // improve this by changing the `levels` interation into an `enumerate()`, and then using the index to // create the correct path on the error path - let mut traversed_path = AmlName::root(); + let mut traversed_path = AmlName::root_in(self.alloc.clone()); let mut current_level = &mut self.root; for level in levels { @@ -327,7 +359,7 @@ impl Namespace { current_level = current_level .children .get_mut(segment) - .ok_or(AmlError::LevelDoesNotExist(traversed_path.clone()))?; + .ok_or(AmlError::LevelDoesNotExist(traversed_path.to_global()))?; } Ok((current_level, *last_seg)) @@ -338,14 +370,18 @@ impl Namespace { /// children of the level should also be traversed. pub fn traverse(&mut self, mut f: F) -> Result<(), AmlError> where - F: FnMut(&AmlName, &NamespaceLevel) -> Result, + F: FnMut(&AmlName, &NamespaceLevel) -> Result, { - fn traverse_level(level: &NamespaceLevel, scope: &AmlName, f: &mut F) -> Result<(), AmlError> + fn traverse_level( + level: &NamespaceLevel, + scope: &AmlName, + f: &mut F, + ) -> Result<(), AmlError> where - F: FnMut(&AmlName, &NamespaceLevel) -> Result, + F: FnMut(&AmlName, &NamespaceLevel) -> Result, { for (name, child) in level.children.iter() { - let name = AmlName::from_name_seg(*name).resolve(scope)?; + let name = AmlName::from_name_seg_in(*name, scope.0.allocator().clone()).resolve(scope)?; if f(&name, child)? { traverse_level(child, &name, f)?; @@ -355,21 +391,26 @@ impl Namespace { Ok(()) } - if f(&AmlName::root(), &self.root)? { - traverse_level(&self.root, &AmlName::root(), &mut f)?; + let root = AmlName::root_in(self.alloc.clone()); + if f(&root, &self.root)? { + traverse_level(&self.root, &root, &mut f)?; } Ok(()) } } -impl fmt::Display for Namespace { +impl fmt::Display for Namespace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { const STEM: &str = "│ "; const BRANCH: &str = "├── "; const END: &str = "└── "; - fn print_level(f: &mut fmt::Formatter<'_>, level: &NamespaceLevel, indent_stack: String) -> fmt::Result { + fn print_level( + f: &mut fmt::Formatter<'_>, + level: &NamespaceLevel, + indent_stack: String, + ) -> fmt::Result { for (i, (name, (flags, object))) in level.values.iter().enumerate() { let end = (i == level.values.len() - 1) && level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).count() == 0; @@ -393,7 +434,7 @@ impl fmt::Display for Namespace { } } - let remaining_scopes: Vec<_> = + let remaining_scopes: alloc::vec::Vec<_> = level.children.iter().filter(|(_, l)| l.kind == NamespaceLevelKind::Scope).collect(); for (i, (name, sub_level)) in remaining_scopes.iter().enumerate() { let end = i == remaining_scopes.len() - 1; @@ -420,10 +461,10 @@ pub enum NamespaceLevelKind { } #[derive(Clone)] -pub struct NamespaceLevel { +pub struct NamespaceLevel { pub kind: NamespaceLevelKind, - pub values: BTreeMap, - pub children: BTreeMap, + pub values: BTreeMap), A>, + pub children: BTreeMap, A>, } #[derive(Clone, Copy, Debug)] @@ -441,14 +482,33 @@ impl ObjectFlags { } } -impl NamespaceLevel { - pub fn new(kind: NamespaceLevelKind) -> NamespaceLevel { - NamespaceLevel { kind, values: BTreeMap::new(), children: BTreeMap::new() } +impl NamespaceLevel { + pub fn new_in(kind: NamespaceLevelKind, alloc: A) -> NamespaceLevel { + NamespaceLevel { kind, values: BTreeMap::new_in(alloc.clone()), children: BTreeMap::new_in(alloc) } + } +} + +// only Clone is derived. PartialEq/Debug get manual impls +// below to avoid the derive macro's auto-added `A: PartialEq` / `A: Debug` +// bounds - `&'static BumpArena` satisfies neither, and the bounds aren't +// needed because Vec's own impls don't require A: PartialEq/Debug. +#[derive(Clone)] +pub struct AmlName(Vec); + +impl PartialEq> for AmlName { + fn eq(&self, other: &AmlName) -> bool { + self.0 == other.0 } } -#[derive(Clone, PartialEq, Debug)] -pub struct AmlName(Vec); +impl Eq for AmlName {} + +impl fmt::Debug for AmlName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Use Display representation - that's what an AmlName "looks like". + write!(f, "AmlName({})", self) + } +} #[derive(Clone, Copy, PartialEq, Debug)] pub enum NameComponent { @@ -457,29 +517,79 @@ pub enum NameComponent { Segment(NameSeg), } -impl AmlName { - pub fn root() -> AmlName { - AmlName(vec![NameComponent::Root]) +impl AmlName { + pub fn root() -> AmlName { + AmlName::root_in(Global) + } + + pub fn from_name_seg(seg: NameSeg) -> AmlName { + AmlName::from_name_seg_in(seg, Global) } +} + +impl FromStr for AmlName { + type Err = AmlError; - pub fn from_name_seg(seg: NameSeg) -> AmlName { - AmlName(vec![NameComponent::Segment(seg)]) + fn from_str(string: &str) -> Result { + AmlName::parse_in(string, Global) } +} - pub fn from_components(components: Vec) -> AmlName { +impl AmlName { + /// Copy this name into the global allocator. + /// + /// [`AmlError`](crate::aml::AmlError) carries names for diagnostics and is not parameterised + /// over an allocator, so a name being reported in an error is copied here first. Name + /// components are `Copy` and names are short, and this only happens on error paths. + pub fn to_global(&self) -> AmlName { + let mut components = Vec::with_capacity_in(self.0.len(), Global); + components.extend_from_slice(&self.0); AmlName(components) } - pub fn as_string(&self) -> String { - self.0 - .iter() - .fold(String::new(), |name, component| match component { - NameComponent::Root => name + "\\", - NameComponent::Prefix => name + "^", - NameComponent::Segment(seg) => name + seg.as_str() + ".", - }) - .trim_end_matches('.') - .to_string() + pub fn root_in(alloc: A) -> AmlName { + let mut v = Vec::with_capacity_in(1, alloc); + v.push(NameComponent::Root); + AmlName(v) + } + + pub fn from_name_seg_in(seg: NameSeg, alloc: A) -> AmlName { + let mut v = Vec::with_capacity_in(1, alloc); + v.push(NameComponent::Segment(seg)); + AmlName(v) + } + + pub fn from_components(components: Vec) -> AmlName { + AmlName(components) + } + + // Allocator-aware replacement for `FromStr::from_str`. + pub fn parse_in(mut string: &str, alloc: A) -> Result, AmlError> { + if string.is_empty() { + return Err(AmlError::EmptyNamesAreInvalid); + } + + let mut components = Vec::new_in(alloc); + + // If it starts with a \, make it an absolute name + if string.starts_with('\\') { + components.push(NameComponent::Root); + string = &string[1..]; + } + + if !string.is_empty() { + for mut part in string.split('.') { + // Handle prefix chars + while part.starts_with('^') { + components.push(NameComponent::Prefix); + part = &part[1..]; + } + + components.push(NameComponent::Segment(NameSeg::from_str_inner(part)?)); + } + } + + Ok(AmlName(components)) } /// An AML path is normal if it does not contain any prefix elements ("^" characters, when @@ -504,7 +614,7 @@ impl AmlName { /// Normalize an AML path, resolving prefix chars. Returns `AmlError::InvalidNormalizedName` if the path /// normalizes to an invalid path (e.g. `\^_FOO`) - pub fn normalize(self) -> Result { + pub fn normalize(self) -> Result, AmlError> { /* * If the path is already normal, just return it as-is. This avoids an unneccessary heap allocation and * free. @@ -513,7 +623,8 @@ impl AmlName { return Ok(self); } - Ok(AmlName(self.0.iter().try_fold(Vec::new(), |mut name, &component| match component { + let alloc = self.0.allocator().clone(); + Ok(AmlName(self.0.iter().try_fold(Vec::new_in(alloc), |mut name, &component| match component { seg @ NameComponent::Segment(_) => { name.push(seg); Ok(name) @@ -529,7 +640,7 @@ impl AmlName { name.pop().unwrap(); Ok(name) } else { - Err(AmlError::InvalidNormalizedName(self.clone())) + Err(AmlError::InvalidNormalizedName(self.to_global())) } } })?)) @@ -537,7 +648,7 @@ impl AmlName { /// Get the parent of this `AmlName`. For example, the parent of `\_SB.PCI0._PRT` is `\_SB.PCI0`. The root /// path has no parent, and so returns `None`. - pub fn parent(&self) -> Result { + pub fn parent(&self) -> Result, AmlError> { // Firstly, normalize the path so we don't have to deal with prefix chars let mut normalized_self = self.clone().normalize()?; @@ -555,19 +666,19 @@ impl AmlName { /// entry points that require absolute ones from firmware input, so this is a normal error /// rather than a panic. pub fn require_absolute(&self) -> Result<(), AmlError> { - if self.is_absolute() { Ok(()) } else { Err(AmlError::NameNotAbsolute(self.clone())) } + if self.is_absolute() { Ok(()) } else { Err(AmlError::NameNotAbsolute(self.to_global())) } } /// Normalize an AML path that is required to be absolute. Prefer this over `normalize` where /// an absolute path is required. - pub fn normalize_absolute(self) -> Result { + pub fn normalize_absolute(self) -> Result, AmlError> { self.require_absolute()?; self.normalize() } /// Resolve this path against a given scope, making it absolute. If the path is absolute, it is /// returned directly. The path is also normalized. Errors if `scope` is not absolute. - pub fn resolve(&self, scope: &AmlName) -> Result { + pub fn resolve(&self, scope: &AmlName) -> Result, AmlError> { scope.require_absolute()?; if self.is_absolute() { @@ -580,42 +691,23 @@ impl AmlName { } } -impl FromStr for AmlName { - type Err = AmlError; - - fn from_str(mut string: &str) -> Result { - if string.is_empty() { - return Err(AmlError::EmptyNamesAreInvalid); - } - - let mut components = Vec::new(); - - // If it starts with a \, make it an absolute name - if string.starts_with('\\') { - components.push(NameComponent::Root); - string = &string[1..]; - } - - if !string.is_empty() { - // Divide the rest of it into segments, and parse those - for mut part in string.split('.') { - // Handle prefix chars - while part.starts_with('^') { - components.push(NameComponent::Prefix); - part = &part[1..]; +impl fmt::Display for AmlName { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut iter = self.0.iter().peekable(); + while let Some(component) = iter.next() { + match component { + NameComponent::Root => f.write_str("\\")?, + NameComponent::Prefix => f.write_str("^")?, + NameComponent::Segment(seg) => { + f.write_str(seg.as_str())?; + // Add separator if the next component is also a segment. + if matches!(iter.peek(), Some(NameComponent::Segment(_))) { + f.write_str(".")?; + } } - - components.push(NameComponent::Segment(NameSeg::from_str(part)?)); } } - - Ok(Self(components)) - } -} - -impl fmt::Display for AmlName { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_string()) + Ok(()) } } @@ -643,12 +735,8 @@ impl NameSeg { // We should only construct valid ASCII name segments unsafe { str::from_utf8_unchecked(&self.0) } } -} - -impl FromStr for NameSeg { - type Err = AmlError; - fn from_str(s: &str) -> Result { + pub fn from_str_inner(s: &str) -> Result { // Each NameSeg can only have four chars, and must have at least one if s.is_empty() || s.len() > 4 { return Err(AmlError::InvalidNameSeg([0xff, 0xff, 0xff, 0xff])); diff --git a/src/aml/object.rs b/src/aml/object.rs index bfc03b41..0630ae17 100644 --- a/src/aml/object.rs +++ b/src/aml/object.rs @@ -1,51 +1,60 @@ -use crate::aml::{AmlError, Handle, IntegerSize, Operation, namespace::AmlName, op_region::OpRegion}; -use alloc::{ - borrow::Cow, - string::{String, ToString}, - sync::Arc, - vec::Vec, +use crate::aml::{ + AmlError, Handle, IntegerSize, Operation, namespace::AmlName, op_region::OpRegion, string::AmlString, }; +use alloc::{alloc::Global, sync::Arc, vec::Vec}; use bit_field::BitField; -use core::{cell::UnsafeCell, cmp::Ordering, fmt, ops, sync::atomic::AtomicU64}; +use core::{alloc::Allocator, cell::UnsafeCell, cmp::Ordering, fmt, ops, sync::atomic::AtomicU64}; -type NativeMethod = dyn Fn(&[WrappedObject]) -> Result; +type NativeMethod = dyn Fn(&[WrappedObject]) -> Result, AmlError>; #[derive(Clone)] -pub enum Object { +pub enum Object { Uninitialized, - Buffer(Vec), - BufferField { buffer: WrappedObject, offset: usize, length: usize }, + Buffer(Vec), + BufferField { buffer: WrappedObject, offset: usize, length: usize }, Device, - Event(Arc), - FieldUnit(FieldUnit), + // Event's Arc is also allocator-parameterized. We could + // keep this Arc as a "small exception" for shared + // synchronization primitives, but consistency wins - every allocation + // goes through the same arena. + Event(Arc), + FieldUnit(FieldUnit), Integer(u64), - Method { code: Vec, flags: MethodFlags }, - NativeMethod { f: Arc, flags: MethodFlags }, + Method { code: Vec, flags: MethodFlags }, + NativeMethod { f: Arc, A>, flags: MethodFlags }, Mutex { mutex: Handle, sync_level: u8 }, - Reference { kind: ReferenceKind, inner: WrappedObject }, - NamePath { name: AmlName, scope: AmlName }, - OpRegion(OpRegion), - Package(Vec), + Reference { kind: ReferenceKind, inner: WrappedObject }, + NamePath { name: AmlName, scope: AmlName }, + OpRegion(OpRegion), + Package(Vec, A>), PowerResource { system_level: u8, resource_order: u16 }, Processor { proc_id: u8, pblk_address: u32, pblk_length: u8 }, RawDataBuffer, - String(String), + String(AmlString), ThermalZone, Debug, } -impl Object { - pub fn native_method(num_args: u8, f: F) -> Object +impl Object { + pub fn wrap(self) -> WrappedObject { + self.wrap_in(Global) + } +} + +impl Object { + pub fn native_method(num_args: u8, f: F, alloc: A) -> Object where - F: Fn(&[WrappedObject]) -> Result + 'static, + A: 'static, + F: Fn(&[WrappedObject]) -> Result, AmlError> + 'static, { let mut flags = 0; flags.set_bits(0..3, num_args); - Object::NativeMethod { f: Arc::new(f), flags: MethodFlags(flags) } + // Arc coerces to Arc at the field assignment. + Object::NativeMethod { f: Arc::new_in(f, alloc), flags: MethodFlags(flags) } } } -impl fmt::Display for Object { +impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Object::Uninitialized => write!(f, "[Uninitialized]"), @@ -104,13 +113,20 @@ impl ObjectToken { } } -#[derive(Clone, Debug)] -pub struct WrappedObject(Arc>); +#[derive(Clone)] +pub struct WrappedObject(Arc>, A>); + +// Manual Debug impl - derive auto-bounds `A: Debug`. +impl fmt::Debug for WrappedObject { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("WrappedObject").finish_non_exhaustive() + } +} -impl WrappedObject { - pub fn new(object: Object) -> WrappedObject { +impl WrappedObject { + pub fn new(object: Object, alloc: A) -> WrappedObject { #[allow(clippy::arc_with_non_send_sync)] - WrappedObject(Arc::new(UnsafeCell::new(object))) + WrappedObject(Arc::new_in(UnsafeCell::new(object), alloc)) } /// Gain a mutable reference to an [`Object`] from this [`WrappedObject`]. @@ -121,7 +137,7 @@ impl WrappedObject { /// prevent the same object, referenced from multiple [`WrappedObject`]s, having multiple /// mutable (and therefore aliasing) references being made to it, and therefore care must be /// taken in the interpreter to prevent this. - pub unsafe fn gain_mut<'r, 'a, 't>(&'a self, _token: &'t ObjectToken) -> &'r mut Object + pub unsafe fn gain_mut<'r, 'a, 't>(&'a self, _token: &'t ObjectToken) -> &'r mut Object where 't: 'r, 'a: 'r, @@ -129,7 +145,7 @@ impl WrappedObject { unsafe { &mut *(self.0.get()) } } - pub fn unwrap_reference(self) -> WrappedObject { + pub fn unwrap_reference(self) -> WrappedObject { let mut object = self; loop { if let Object::Reference { ref inner, .. } = *object { @@ -142,7 +158,7 @@ impl WrappedObject { /// Unwraps 'transparent' references (e.g. locals, arguments, and internal usage of reference-type objects), but maintain 'real' /// references deliberately created by AML. - pub fn unwrap_transparent_reference(self) -> WrappedObject { + pub fn unwrap_transparent_reference(self) -> WrappedObject { let mut object = self; loop { if let Object::Reference { kind, ref inner } = *object @@ -156,8 +172,8 @@ impl WrappedObject { } } -impl ops::Deref for WrappedObject { - type Target = Object; +impl ops::Deref for WrappedObject { + type Target = Object; fn deref(&self) -> &Self::Target { /* @@ -170,15 +186,15 @@ impl ops::Deref for WrappedObject { } } -impl fmt::Display for WrappedObject { +impl fmt::Display for WrappedObject { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Wrapped({})", unsafe { &*self.0.get() }) } } -impl Object { - pub fn wrap(self) -> WrappedObject { - WrappedObject::new(self) +impl Object { + pub fn wrap_in(self, alloc: A) -> WrappedObject { + WrappedObject::new(self, alloc) } /// Unwraps an integer object. Errors if not already an integer. @@ -192,9 +208,10 @@ impl Object { } } - pub fn as_string(&self) -> Result, AmlError> { + /// Unwraps a string object as a borrowed string slice. + pub fn as_string(&self) -> Result<&str, AmlError> { if let Object::String(value) = self { - Ok(Cow::from(value)) + Ok(value.as_str()) } else { Err(AmlError::ObjectNotOfExpectedType { expected: ObjectType::String, got: self.typ() }) } @@ -211,7 +228,7 @@ impl Object { /// Converts the object to an integer. Used for both implicit and explicit conversions. /// /// To avoid the cast, use [`Object::as_integer`] instead. - pub fn to_integer(&self, integer_size: IntegerSize) -> Result { + pub fn to_integer(&self, integer_size: IntegerSize, alloc: A) -> Result { match self { Object::Integer(value) => Ok(*value), Object::Buffer(bytes) => { @@ -231,12 +248,13 @@ impl Object { * that won't fit in a `u64` etc. We probably need to write a more robust parser * 'real' parser to handle those cases. */ - let value = value.trim(); - let value = value.to_ascii_lowercase(); - let (value, radix): (&str, u32) = match value.strip_prefix("0x") { - Some(value) => (value.split(|c: char| !c.is_ascii_hexdigit()).next().unwrap_or(""), 16), - None => (value.split(|c: char| !c.is_ascii_digit()).next().unwrap_or(""), 10), - }; + let value = value.as_str().trim(); + let (value, radix): (&str, u32) = + if let Some(value) = value.strip_prefix("0x").or_else(|| value.strip_prefix("0X")) { + (value.split(|c: char| !c.is_ascii_hexdigit()).next().unwrap_or(""), 16) + } else { + (value.split(|c: char| !c.is_ascii_digit()).next().unwrap_or(""), 10) + }; match value.len() { 0 => Ok(0), _ => Ok(u64::from_str_radix(value, radix).map_err(|_| { @@ -244,24 +262,41 @@ impl Object { })?), } } - Object::BufferField { .. } => self.read_buffer_field(integer_size)?.to_integer(integer_size), + Object::BufferField { .. } => { + self.read_buffer_field(integer_size, alloc.clone())?.to_integer(integer_size, alloc) + } _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: self.typ() })?, } } - pub fn to_buffer(&self, integer_size: IntegerSize) -> Result, AmlError> { + pub fn to_buffer(&self, integer_size: IntegerSize, alloc: A) -> Result, AmlError> { match self { Object::Buffer(bytes) => Ok(bytes.clone()), Object::Integer(value) => match integer_size { - IntegerSize::FourBytes => Ok((*value as u32).to_le_bytes().to_vec()), - IntegerSize::EightBytes => Ok(value.to_le_bytes().to_vec()), + IntegerSize::FourBytes => { + let bytes = (*value as u32).to_le_bytes(); + let mut out = Vec::with_capacity_in(bytes.len(), alloc); + out.extend_from_slice(&bytes); + Ok(out) + } + IntegerSize::EightBytes => { + let bytes = value.to_le_bytes(); + let mut out = Vec::with_capacity_in(bytes.len(), alloc); + out.extend_from_slice(&bytes); + Ok(out) + } }, - Object::String(value) => Ok(value.as_bytes().to_vec()), + Object::String(value) => { + let src = value.as_bytes(); + let mut out = Vec::with_capacity_in(src.len(), alloc); + out.extend_from_slice(src); + Ok(out) + } _ => Err(AmlError::InvalidOperationOnObject { op: Operation::ConvertToBuffer, typ: self.typ() }), } } - pub fn read_buffer_field(&self, integer_size: IntegerSize) -> Result { + pub fn read_buffer_field(&self, integer_size: IntegerSize, alloc: A) -> Result, AmlError> { if let Self::BufferField { buffer, offset, length } = self { let buffer = buffer.clone().unwrap_transparent_reference(); let buffer = match &*buffer { @@ -279,7 +314,9 @@ impl Object { copy_bits(buffer, *offset, &mut dst, 0, *length); Ok(Object::Integer(u64::from_le_bytes(dst))) } else { - let mut dst = alloc::vec![0u8; length.div_ceil(8)]; + let size = length.div_ceil(8); + let mut dst = Vec::with_capacity_in(size, alloc); + dst.resize(size, 0u8); copy_bits(buffer, *offset, &mut dst, 0, *length); Ok(Object::Buffer(dst)) } @@ -314,31 +351,79 @@ impl Object { /// Replace this object's contents with that of a `new` object, applying implicit casting rules /// as needed. This follows the NT interpreter's creative interpretation of implicit casts, which is /// effectively a byte-wise transmutation. - pub fn replace_with_implicit_casting(&mut self, new: Object) -> Result<(), AmlError> { - let new_bytes = match new { - Object::Integer(value) => &value.to_le_bytes(), + pub fn replace_with_implicit_casting(&mut self, new: Object) -> Result<(), AmlError> { + // Extract a &[u8] view of `new` without taking ownership (so we can keep + // its allocator A live for the lifetime of the borrow). + let new_bytes: &[u8] = match new { + Object::Integer(value) => { + // Convert to a fixed-size byte buffer first; the borrow below + // must outlive the match arm, so we stash it in a local. + let bytes = value.to_le_bytes(); + return apply_cast_bytes_owned(self, &bytes); + } Object::String(ref value) => value.as_bytes(), - Object::Buffer(ref value) => &value.clone(), + Object::Buffer(ref value) => value.as_slice(), _ => return Err(AmlError::InvalidImplicitCast { from: self.typ(), to: new.typ() }), }; + apply_cast_bytes(self, new_bytes)?; + return Ok(()); + + fn apply_cast_bytes_owned( + target: &mut Object, + bytes: &[u8], + ) -> Result<(), AmlError> { + apply_cast_bytes(target, bytes) + } - match self { - Object::Integer(value) => { - let bytes_to_copy = core::cmp::min(new_bytes.len(), 8); - let mut bytes = [0u8; 8]; - bytes[0..bytes_to_copy].copy_from_slice(&new_bytes[0..bytes_to_copy]); - *value = u64::from_le_bytes(bytes); - } - Object::String(value) => { - *value = String::from_utf8_lossy(&new_bytes).split('\0').next().unwrap().to_string(); - } - Object::Buffer(value) => { - *value = new_bytes.to_vec(); + fn apply_cast_bytes( + target: &mut Object, + new_bytes: &[u8], + ) -> Result<(), AmlError> { + match target { + Object::Integer(value) => { + let bytes_to_copy = core::cmp::min(new_bytes.len(), 8); + let mut bytes = [0u8; 8]; + bytes[0..bytes_to_copy].copy_from_slice(&new_bytes[0..bytes_to_copy]); + *value = u64::from_le_bytes(bytes); + } + Object::String(value) => { + value.clear(); + push_utf8_lossy_until_nul(value, new_bytes); + } + Object::Buffer(value) => { + value.clear(); + value.extend_from_slice(new_bytes); + } + _ => return Err(AmlError::InvalidImplicitCast { from: target.typ(), to: ObjectType::Buffer }), } - _ => return Err(AmlError::InvalidImplicitCast { from: self.typ(), to: new.typ() }), + Ok(()) } - Ok(()) + fn push_utf8_lossy_until_nul(target: &mut AmlString, bytes: &[u8]) { + let mut remaining = bytes.split(|byte| *byte == b'\0').next().unwrap_or_default(); + + loop { + match core::str::from_utf8(remaining) { + Ok(valid) => { + target.push_str(valid); + return; + } + Err(error) => { + let valid_up_to = error.valid_up_to(); + if valid_up_to > 0 { + // SAFETY: `valid_up_to` is the UTF-8-valid prefix reported by `from_utf8`. + target.push_str(unsafe { core::str::from_utf8_unchecked(&remaining[..valid_up_to]) }); + } + + target.push(char::REPLACEMENT_CHARACTER); + let Some(error_len) = error.error_len() else { + return; + }; + remaining = &remaining[(valid_up_to + error_len)..]; + } + } + } + } } /// Returns the `ObjectType` of this object. Returns the type of the referenced object in the @@ -374,10 +459,10 @@ impl Object { /// /// This function is not intended to be used for `impl PartialOrd` because we don't want to tie /// the meaning of `object_a.cmp(object_b)` to those AML rules - we may want more flexibility. - pub fn aml_cmp(&self, other: &Object) -> Result { + pub fn aml_cmp(&self, other: &Object) -> Result { match (self, &other) { (Object::Integer(a), Object::Integer(b)) => Ok(a.cmp(b)), - (Object::String(a), Object::String(b)) => Ok(a.cmp(b)), + (Object::String(a), Object::String(b)) => Ok(a.as_str().cmp(b.as_str())), (Object::Buffer(a), Object::Buffer(b)) => { let size_cmp = a.len().cmp(&b.len()); if size_cmp != Ordering::Equal { @@ -390,19 +475,38 @@ impl Object { } } -#[derive(Clone, Debug)] -pub struct FieldUnit { - pub kind: FieldUnitKind, +#[derive(Clone)] +pub struct FieldUnit { + pub kind: FieldUnitKind, pub flags: FieldFlags, pub bit_index: usize, pub bit_length: usize, } -#[derive(Clone, Debug)] -pub enum FieldUnitKind { - Normal { region: WrappedObject }, - Bank { region: WrappedObject, bank: WrappedObject, bank_value: u64 }, - Index { index: WrappedObject, data: WrappedObject }, +impl fmt::Debug for FieldUnit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FieldUnit") + .field("flags", &self.flags) + .field("bit_length", &self.bit_length) + .finish_non_exhaustive() + } +} + +#[derive(Clone)] +pub enum FieldUnitKind { + Normal { region: WrappedObject }, + Bank { region: WrappedObject, bank: WrappedObject, bank_value: u64 }, + Index { index: WrappedObject, data: WrappedObject }, +} + +impl fmt::Debug for FieldUnitKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Normal { .. } => f.write_str("Normal { .. }"), + Self::Bank { .. } => f.write_str("Bank { .. }"), + Self::Index { .. } => f.write_str("Index { .. }"), + } + } } #[derive(Clone, Copy, Debug)] @@ -607,6 +711,7 @@ pub(crate) fn align_down(value: usize, align: usize) -> usize { #[cfg(test)] mod tests { use super::*; + use alloc::alloc::Global; #[test] fn test_copy_bits() { @@ -620,37 +725,37 @@ mod tests { #[test] fn buffer_to_integer() { let buffer = Object::Buffer(Vec::from([0xab, 0xcd, 0xef, 0x01, 0xff])); - assert_eq!(buffer.to_integer(IntegerSize::FourBytes).unwrap(), 0x01efcdab); + assert_eq!(buffer.to_integer(IntegerSize::FourBytes, Global).unwrap(), 0x01efcdab); } #[test] fn buffer_field_to_integer() { const BUFFER: [u8; 5] = [0xffu8; 5]; - let buffer = Object::Buffer(Vec::from(BUFFER)).wrap(); + let buffer = Object::Buffer(Vec::from(BUFFER)).wrap_in(Global); let buffer_field = Object::BufferField { buffer, offset: 5, length: 9 }; - assert_eq!(buffer_field.to_integer(IntegerSize::FourBytes).unwrap(), 0x1ff); + assert_eq!(buffer_field.to_integer(IntegerSize::FourBytes, Global).unwrap(), 0x1ff); } #[test] fn buffer_field_to_4_byte_integer() { // The ones in this buffer are strategically chosen to not make it to the final integer. const BUFFER: [u8; 5] = [0x0f, 0x00, 0x00, 0x00, 0xf0]; - let buffer = Object::Buffer(Vec::from(BUFFER)).wrap(); + let buffer = Object::Buffer(Vec::from(BUFFER)).wrap_in(Global); let buffer_field = Object::BufferField { buffer, offset: 4, length: 36, // This should be truncated to 32 bits in the conversion }; - assert_eq!(buffer_field.to_integer(IntegerSize::FourBytes).unwrap(), 0); + assert_eq!(buffer_field.to_integer(IntegerSize::FourBytes, Global).unwrap(), 0); } #[test] fn buffer_field_to_8_byte_integer() { const BUFFER: [u8; 6] = [0x0f, 0x00, 0x00, 0x00, 0xf0, 0xff]; - let buffer = Object::Buffer(Vec::from(BUFFER)).wrap(); + let buffer = Object::Buffer(Vec::from(BUFFER)).wrap_in(Global); let buffer_field = Object::BufferField { buffer, offset: 4, length: 36 }; - assert_eq!(buffer_field.to_integer(IntegerSize::EightBytes).unwrap(), 0x0000000f_00000000); + assert_eq!(buffer_field.to_integer(IntegerSize::EightBytes, Global).unwrap(), 0x0000000f_00000000); } } diff --git a/src/aml/op_region.rs b/src/aml/op_region.rs index e2e73d64..2a9b9307 100644 --- a/src/aml/op_region.rs +++ b/src/aml/op_region.rs @@ -1,23 +1,36 @@ use crate::aml::{AmlError, namespace::AmlName}; +use alloc::alloc::Global; +use core::alloc::Allocator; -#[derive(Clone, Debug)] -pub struct OpRegion { +#[derive(Clone)] +pub struct OpRegion { pub space: RegionSpace, pub base: u64, pub length: u64, - pub parent_device_path: AmlName, + pub parent_device_path: AmlName, } -pub trait RegionHandler { - fn read_u8(&self, region: &OpRegion) -> Result; - fn read_u16(&self, region: &OpRegion) -> Result; - fn read_u32(&self, region: &OpRegion) -> Result; - fn read_u64(&self, region: &OpRegion) -> Result; +impl core::fmt::Debug for OpRegion { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("OpRegion") + .field("space", &self.space) + .field("base", &self.base) + .field("length", &self.length) + .field("parent_device_path", &self.parent_device_path) + .finish() + } +} + +pub trait RegionHandler { + fn read_u8(&self, region: &OpRegion) -> Result; + fn read_u16(&self, region: &OpRegion) -> Result; + fn read_u32(&self, region: &OpRegion) -> Result; + fn read_u64(&self, region: &OpRegion) -> Result; - fn write_u8(&self, region: &OpRegion, value: u8) -> Result<(), AmlError>; - fn write_u16(&self, region: &OpRegion, value: u16) -> Result<(), AmlError>; - fn write_u32(&self, region: &OpRegion, value: u32) -> Result<(), AmlError>; - fn write_u64(&self, region: &OpRegion, value: u64) -> Result<(), AmlError>; + fn write_u8(&self, region: &OpRegion, value: u8) -> Result<(), AmlError>; + fn write_u16(&self, region: &OpRegion, value: u16) -> Result<(), AmlError>; + fn write_u32(&self, region: &OpRegion, value: u32) -> Result<(), AmlError>; + fn write_u64(&self, region: &OpRegion, value: u64) -> Result<(), AmlError>; } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] diff --git a/src/aml/pci_routing.rs b/src/aml/pci_routing.rs index 5b3d037e..2f7733d2 100644 --- a/src/aml/pci_routing.rs +++ b/src/aml/pci_routing.rs @@ -8,8 +8,8 @@ use crate::aml::{ resource::{self, InterruptPolarity, InterruptTrigger, Resource}, }; use alloc::{vec, vec::Vec}; +use core::alloc::Allocator; use bit_field::BitField; -use core::str::FromStr; pub use crate::aml::resource::IrqDescriptor; @@ -58,13 +58,13 @@ impl PciRoutingTable { /// `AmlError::InvalidOperationOnObject` if the value passed is not a package, or if any of the /// values within it are not packages. Returns the various `AmlError::Prt*` errors if the /// internal structure of the entries is invalid. - pub fn from_prt_path( - prt_path: AmlName, - interpreter: &Interpreter, + pub fn from_prt_path( + prt_path: AmlName, + interpreter: &Interpreter, ) -> Result { let mut entries = Vec::new(); - let prt = interpreter.evaluate(prt_path.clone(), vec![])?; + let prt = interpreter.evaluate(prt_path.clone(), Vec::new_in(interpreter.alloc.clone()))?; if let Object::Package(ref inner_values) = *prt { for value in inner_values { @@ -117,10 +117,14 @@ impl PciRoutingTable { * so search from the scope the name appeared in, rather than resolving it. */ Object::NamePath { ref name, ref scope } => { - Some(interpreter.namespace.lock().search_for_level(name, scope)?) + Some(interpreter.namespace.lock().search_for_level(name, scope)?.to_global()) } Object::String(ref name) => Some( - interpreter.namespace.lock().search_for_level(&AmlName::from_str(name)?, &prt_path)?, + interpreter + .namespace + .lock() + .search_for_level(&AmlName::parse_in(name.as_str(), interpreter.alloc.clone())?, &prt_path)? + .to_global(), ), _ => None, }; @@ -194,7 +198,7 @@ impl PciRoutingTable { irq: gsi, }), PciRouteType::LinkObject(ref name) => { - let path = AmlName::from_str("_CRS").unwrap().resolve(name)?; + let path = AmlName::parse_in("_CRS", interpreter.alloc.clone()).unwrap().resolve(name)?; let link_crs = interpreter.evaluate(path, vec![])?; let resources = resource::resource_descriptor_list(link_crs)?; diff --git a/src/aml/string.rs b/src/aml/string.rs new file mode 100644 index 00000000..52d1100b --- /dev/null +++ b/src/aml/string.rs @@ -0,0 +1,166 @@ +//! Allocator-aware string storage for AML objects. +//! +//! `alloc::string::String` is not parameterised over an allocator - there is no +//! `String` and no `String::new_in` - so an allocator-aware [`Object`] cannot +//! store its strings in one. [`AmlString`] is a thin newtype over `Vec` +//! that upholds a UTF-8 invariant and provides the subset of `String` the +//! interpreter actually uses. +//! +//! [`Object`]: super::object::Object + +use alloc::{alloc::Global, vec::Vec}; +use core::{alloc::Allocator, fmt}; + +/// An allocator-aware, UTF-8 string. +/// +/// Every construction path either starts from a `&str` (already valid UTF-8) or +/// appends through [`push_str`](Self::push_str) / [`push`](Self::push), so the +/// invariant holds by construction. [`as_bytes_mut`](Self::as_bytes_mut) is the +/// only way to break it, and is `unsafe` for that reason. +pub struct AmlString(Vec); + +impl AmlString { + pub fn new_in(alloc: A) -> Self { + Self(Vec::new_in(alloc)) + } + + pub fn from_str_in(s: &str, alloc: A) -> Self { + let mut bytes = Vec::with_capacity_in(s.len(), alloc); + bytes.extend_from_slice(s.as_bytes()); + Self(bytes) + } + + /// Build a string from bytes that are not necessarily valid UTF-8, replacing + /// each invalid sequence with `U+FFFD`. + /// + /// This is the allocator-aware counterpart of `String::from_utf8_lossy`, + /// which would otherwise allocate through `Global`. + pub fn from_utf8_lossy_in(bytes: &[u8], alloc: A) -> Self { + let mut string = Self(Vec::with_capacity_in(bytes.len(), alloc)); + let mut rest = bytes; + + while !rest.is_empty() { + match core::str::from_utf8(rest) { + Ok(valid) => { + string.push_str(valid); + break; + } + Err(error) => { + let (valid, after) = rest.split_at(error.valid_up_to()); + // SAFETY: `valid_up_to` is by definition the length of the + // longest valid UTF-8 prefix of `rest`. + string.push_str(unsafe { core::str::from_utf8_unchecked(valid) }); + string.push(char::REPLACEMENT_CHARACTER); + + match error.error_len() { + // An invalid sequence of `len` bytes: skip past it. + Some(len) => rest = &after[len..], + // An unexpected end of input: nothing valid remains. + None => break, + } + } + } + } + + string + } + + #[inline] + pub fn as_str(&self) -> &str { + // SAFETY: the UTF-8 invariant is maintained by every safe constructor + // and mutator on this type. + unsafe { core::str::from_utf8_unchecked(&self.0) } + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + /// # Safety + /// The caller must leave the returned slice as valid UTF-8. Breaking that + /// makes subsequent [`as_str`](Self::as_str) calls unsound. + #[inline] + pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() + } + + pub fn push_str(&mut self, s: &str) { + self.0.extend_from_slice(s.as_bytes()); + } + + pub fn push(&mut self, c: char) { + let mut buf = [0u8; 4]; + self.0.extend_from_slice(c.encode_utf8(&mut buf).as_bytes()); + } + + pub fn clear(&mut self) { + self.0.clear(); + } + + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn parse(&self) -> Result { + self.as_str().parse::() + } +} + +impl Clone for AmlString { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +/* + * These are written out rather than derived because `derive` would bound + * `A: PartialEq`, while `Vec`'s own comparison works across differing + * allocators. Comparing two `AmlString`s with different allocators is + * meaningful, so the impl is generic over both. + */ +impl PartialEq> for AmlString { + fn eq(&self, other: &AmlString) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl Eq for AmlString {} + +impl PartialEq for AmlString { + fn eq(&self, other: &str) -> bool { + self.as_str() == other + } +} + +impl PartialEq<&str> for AmlString { + fn eq(&self, other: &&str) -> bool { + self.as_str() == *other + } +} + +impl fmt::Display for AmlString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl fmt::Debug for AmlString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_str(), f) + } +} + +/// Lets `write!(string, "...")` append without routing through `Global`. +impl fmt::Write for AmlString { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.push_str(s); + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7e897f68..361f7ab3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ #![no_std] #![feature(allocator_api)] +#![feature(btreemap_alloc)] #[cfg_attr(test, macro_use)] #[cfg(test)] diff --git a/tools/aml_test_tools/src/handlers/logging_handler.rs b/tools/aml_test_tools/src/handlers/logging_handler.rs index 80049e45..97d7561f 100644 --- a/tools/aml_test_tools/src/handlers/logging_handler.rs +++ b/tools/aml_test_tools/src/handlers/logging_handler.rs @@ -197,7 +197,7 @@ where self.next_handler.release(mutex); } - fn handle_debug(&self, object: &Object) { + fn handle_debug(&self, object: &Object) { info!("Debug store: {}", object); self.next_handler.handle_debug(object); } diff --git a/tools/aml_test_tools/src/lib.rs b/tools/aml_test_tools/src/lib.rs index 0407a9cd..cb015a91 100644 --- a/tools/aml_test_tools/src/lib.rs +++ b/tools/aml_test_tools/src/lib.rs @@ -1,4 +1,5 @@ #![feature(sync_unsafe_cell)] +#![feature(allocator_api)] //! A collection of helper utilities for testing AML using the [`acpi`] crate. //! //! These utilities are very heavily based on the way the [`acpi`] crate has used them historically. @@ -22,6 +23,7 @@ use acpi::{ }; use log::{error, trace}; use std::{ + alloc::Global, cell::SyncUnsafeCell, ffi::OsStr, fmt::Debug, @@ -31,7 +33,6 @@ use std::{ path::PathBuf, process::Command, ptr::NonNull, - str::FromStr, sync::{Arc, atomic::AtomicU32}, }; use tempfile::{NamedTempFile, TempDir, tempdir}; @@ -43,10 +44,10 @@ where T: Handler, { /// The test passed, and the interpreter is still valid. - Pass(Interpreter), + Pass(Interpreter), /// The test failed, but the interpreter is still valid. A failure reason is also provided. - Failed(Interpreter, TestFailureReason), + Failed(Interpreter, TestFailureReason), /// The test failed, and the interpreter is no longer valid. Panicked, @@ -206,45 +207,48 @@ pub fn resolve_and_compile(path: &PathBuf, can_compile: bool) -> CompilationOutc /// /// This function uses a single, static, FACS for all tests. If tests are run in parallel, this /// means they will share a single global lock. -pub fn new_interpreter(handler: T) -> Interpreter +pub fn new_interpreter(handler: T) -> Interpreter where T: Handler + Clone, { - let fake_registers = Arc::new(acpi::registers::FixedRegisters { - pm1_event_registers: acpi::registers::Pm1EventRegisterBlock { - pm1_event_length: 8, - pm1a: unsafe { - MappedGas::map_gas( - acpi::address::GenericAddress { - address_space: acpi::address::AddressSpace::SystemIo, - bit_width: 32, - bit_offset: 0, - access_size: 1, - address: 0x400, - }, - &handler, - ) - .unwrap() + let fake_registers = Arc::new_in( + acpi::registers::FixedRegisters { + pm1_event_registers: acpi::registers::Pm1EventRegisterBlock { + pm1_event_length: 8, + pm1a: unsafe { + MappedGas::map_gas( + acpi::address::GenericAddress { + address_space: acpi::address::AddressSpace::SystemIo, + bit_width: 32, + bit_offset: 0, + access_size: 1, + address: 0x400, + }, + &handler, + ) + .unwrap() + }, + pm1b: None, }, - pm1b: None, - }, - pm1_control_registers: acpi::registers::Pm1ControlRegisterBlock { - pm1a: unsafe { - MappedGas::map_gas( - acpi::address::GenericAddress { - address_space: acpi::address::AddressSpace::SystemIo, - bit_width: 32, - bit_offset: 0, - access_size: 1, - address: 0x600, - }, - &handler, - ) - .unwrap() + pm1_control_registers: acpi::registers::Pm1ControlRegisterBlock { + pm1a: unsafe { + MappedGas::map_gas( + acpi::address::GenericAddress { + address_space: acpi::address::AddressSpace::SystemIo, + bit_width: 32, + bit_offset: 0, + access_size: 1, + address: 0x600, + }, + &handler, + ) + .unwrap() + }, + pm1b: None, }, - pm1b: None, }, - }); + Global, + ); // As noted in the doc-comment, this Facs is shared between all tests - so if tests are run in // parallel, they will share a single global lock. @@ -277,7 +281,7 @@ where mapped_length: 32, handler: handler.clone(), }; - Interpreter::new(handler, 2, fake_registers, Some(fake_facs_mapping)) + Interpreter::new_in(handler, 2, fake_registers, Some(fake_facs_mapping), Global) } /// Test an ASL script given as a string, using [`run_test`]. @@ -288,7 +292,7 @@ where /// * `interpreter`: The interpreter to use for testing. pub fn run_test_for_string( asl: &'static str, - interpreter: Interpreter, + interpreter: Interpreter, expected_result: &Option, ) -> RunTestResult where @@ -312,7 +316,7 @@ where /// * `interpreter`: The interpreter to use for testing. pub fn run_test_for_file( file: &PathBuf, - interpreter: Interpreter, + interpreter: Interpreter, expected_result: &Option, ) -> RunTestResult where @@ -343,7 +347,7 @@ where /// successful. pub fn run_test_for_opcodes( opcodes: &[u8], - interpreter: Interpreter, + interpreter: Interpreter, expected_result: &Option, ) -> RunTestResult where @@ -416,7 +420,7 @@ fn create_script_file(asl: &'static str) -> TempScriptFile { /// an inconsistent state. pub fn run_test( tables: Vec, - interpreter: Interpreter, + interpreter: Interpreter, expected_result: &Option, ) -> RunTestResult where @@ -446,7 +450,9 @@ where trace!("All tables loaded"); - if let Some(result) = interpreter.evaluate_if_present(AmlName::from_str("\\MAIN").unwrap(), vec![])? { + if let Some(result) = + interpreter.evaluate_if_present(AmlName::parse_in("\\MAIN", Global).unwrap(), Vec::new_in(Global))? + { let expected_result = expected_result.as_ref().unwrap_or(&ExpectedResult::Integer(0)); if result_matches(expected_result, &result) { Ok(()) diff --git a/tools/aml_test_tools/src/result.rs b/tools/aml_test_tools/src/result.rs index b615b944..0934be01 100644 --- a/tools/aml_test_tools/src/result.rs +++ b/tools/aml_test_tools/src/result.rs @@ -1,4 +1,5 @@ use acpi::aml::object::Object; +use core::alloc::Allocator; #[derive(Clone, Debug)] pub enum ExpectedResult { @@ -6,10 +7,11 @@ pub enum ExpectedResult { String(String), } -pub fn result_matches(expected: &ExpectedResult, actual: &Object) -> bool { +pub fn result_matches(expected: &ExpectedResult, actual: &Object) -> bool { match (expected, actual) { (ExpectedResult::Integer(expected), Object::Integer(actual)) => expected == actual, - (ExpectedResult::String(expected), Object::String(actual)) => expected == actual, + // Compare the std String against AmlString's str view. + (ExpectedResult::String(expected), Object::String(actual)) => expected.as_str() == actual.as_str(), _ => false, } }