aml/
lib.rs

1//! `aml` is a pure-Rust AML (ACPI Machine Language) parser, used for parsing the DSDT and
2//! SSDT tables from ACPI. This crate can be used by kernels to gather information about the
3//! hardware, and invoke control methods to query and change the state of devices in a
4//! hardware-independent way.
5//!
6//! ### Using the library
7//! To use the library, you should create an `AmlContext` using `AmlContext::new()`, and then pass it tables
8//! containing AML (probably from the `acpi` crate), which you've mapped into the virtual address space. This will
9//! parse the table, populating the namespace with objects encoded by the AML. After this, you may unmap the memory
10//! the table was mapped into - all the information needed will be extracted and allocated on the heap.
11//!
12//! You can then access specific objects by name like so: e.g.
13//! ```ignore
14//! let my_aml_value = aml_context.lookup(&AmlName::from_str("\\_SB.PCI0.S08._ADR").unwrap());
15//! ```
16//!
17//! And invoke control methods like this: e.g.
18//! ```ignore
19//! let result = aml_context.invoke_method(&AmlName::from_str("\\_SB.HPET._CRS").unwrap(), value::Args::EMPTY);
20//! ```
21//!
22//! ### About the parser
23//! The parser is written using a set of custom parser combinators - the code can be confusing on
24//! first reading, but provides an extensible and type-safe way to write parsers. For an easy
25//! introduction to parser combinators and the foundations used for this library, I suggest reading
26//! [Bodil's fantastic blog post](https://bodil.lol/parser-combinators/).
27//!
28//! The actual combinators can be found in `parser.rs`. Various tricks are used to provide a nice
29//! API and work around limitations in the type system, such as the concrete types like
30//! `MapWithContext`.
31//!
32//! The actual parsers are then grouped into categories based loosely on the AML grammar sections in
33//! the ACPI spec. Most are written in terms of combinators, but some have to be written in a more
34//! imperitive style, either because they're clearer, or because we haven't yet found good
35//! combinator patterns to express the parse.
36
37#![no_std]
38#![feature(decl_macro)]
39
40extern crate alloc;
41
42#[cfg(test)]
43extern crate std;
44
45#[cfg(test)]
46mod test_utils;
47
48pub(crate) mod expression;
49pub(crate) mod misc;
50pub(crate) mod name_object;
51pub(crate) mod namespace;
52pub(crate) mod opcode;
53pub(crate) mod parser;
54pub mod pci_routing;
55pub(crate) mod pkg_length;
56pub mod resource;
57pub(crate) mod statement;
58pub(crate) mod term_object;
59pub mod value;
60
61pub use crate::{namespace::*, value::AmlValue};
62
63use alloc::{boxed::Box, string::ToString};
64use core::mem;
65use log::{error, warn};
66use misc::{ArgNum, LocalNum};
67use name_object::Target;
68use parser::{Parser, Propagate};
69use pkg_length::PkgLength;
70use term_object::term_list;
71use value::{AmlType, Args};
72
73/// AML has a `RevisionOp` operator that returns the "AML interpreter revision". It's not clear
74/// what this is actually used for, but this is ours.
75pub const AML_INTERPRETER_REVISION: u64 = 0;
76
77/// Describes how much debug information the parser should emit. Set the "maximum" expected verbosity in
78/// the context's `debug_verbosity` - everything will be printed that is less or equal in 'verbosity'.
79#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
80pub enum DebugVerbosity {
81    /// Print no debug information
82    None,
83    /// Print heads and tails when entering and leaving scopes of major objects, but not more minor ones.
84    Scopes,
85    /// Print heads and tails when entering and leaving scopes of all objects.
86    AllScopes,
87    /// Print heads and tails of all objects, and extra debug information as it's parsed.
88    All,
89}
90
91#[derive(Debug)]
92struct MethodContext {
93    /// AML local variables. These are used when we invoke a control method. A `None` value represents a null AML
94    /// object.
95    locals: [Option<AmlValue>; 8],
96    /// If we're currently invoking a control method, this stores the arguments that were passed to
97    /// it. It's `None` if we aren't invoking a method.
98    args: Args,
99}
100
101impl MethodContext {
102    fn new(args: Args) -> MethodContext {
103        // XXX: this is required because `Option<AmlValue>` is not `Copy`, so it can't be used to initialize an
104        // array, but consts can :(
105        const NONE_BUT_CONST: Option<AmlValue> = None;
106
107        MethodContext { locals: [NONE_BUT_CONST; 8], args }
108    }
109}
110
111pub struct AmlContext {
112    /// The `Handler` passed from the library user. This is stored as a boxed trait object simply to avoid having
113    /// to add a lifetime and type parameter to `AmlContext`, as they would massively complicate the parser types.
114    handler: Box<dyn Handler>,
115
116    pub namespace: Namespace,
117    method_context: Option<MethodContext>,
118
119    /*
120     * These track the state of the context while it's parsing an AML table.
121     */
122    current_scope: AmlName,
123    scope_indent: usize,
124    debug_verbosity: DebugVerbosity,
125}
126
127impl AmlContext {
128    /// Creates a new `AmlContext` - the central type in managing the AML tables. Only one of these should be
129    /// created, and it should be passed the DSDT and all SSDTs defined by the hardware.
130    pub fn new(handler: Box<dyn Handler>, debug_verbosity: DebugVerbosity) -> AmlContext {
131        let mut context = AmlContext {
132            handler,
133            namespace: Namespace::new(),
134            method_context: None,
135
136            current_scope: AmlName::root(),
137            scope_indent: 0,
138            debug_verbosity,
139        };
140
141        context.add_predefined_objects();
142        context
143    }
144
145    pub fn parse_table(&mut self, stream: &[u8]) -> Result<(), AmlError> {
146        if stream.len() == 0 {
147            return Err(AmlError::UnexpectedEndOfStream);
148        }
149
150        let table_length = PkgLength::from_raw_length(stream, stream.len() as u32).unwrap();
151        match term_object::term_list(table_length).parse(stream, self) {
152            Ok(_) => Ok(()),
153            Err((_, _, Propagate::Err(err))) => {
154                error!("Failed to parse AML stream. Err = {:?}", err);
155                Err(err)
156            }
157            Err((_, _, other)) => {
158                error!("AML table evaluated to unexpected result: {:?}", other);
159                Err(AmlError::MalformedStream)
160            }
161        }
162    }
163
164    // TODO: docs
165    pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
166        use value::MethodCode;
167
168        match self.namespace.get_by_path(path)?.clone() {
169            AmlValue::Method { flags, code } => {
170                /*
171                 * First, set up the state we expect to enter the method with, but clearing local
172                 * variables to "null" and setting the arguments. Save the current method state and scope, so if we're
173                 * already executing another control method, we resume into it correctly.
174                 */
175                let old_context = mem::replace(&mut self.method_context, Some(MethodContext::new(args)));
176                let old_scope = mem::replace(&mut self.current_scope, path.clone());
177
178                /*
179                 * Create a namespace level to store local objects created by the invocation.
180                 */
181                self.namespace.add_level(path.clone(), LevelType::MethodLocals)?;
182
183                let return_value = match code {
184                    MethodCode::Aml(ref code) => {
185                        match term_list(PkgLength::from_raw_length(code, code.len() as u32).unwrap())
186                            .parse(code, self)
187                        {
188                            // If the method doesn't return a value, we implicitly return `0`
189                            Ok(_) => Ok(AmlValue::Integer(0)),
190                            Err((_, _, Propagate::Return(result))) => Ok(result),
191                            Err((_, _, Propagate::Break)) => Err(AmlError::BreakInInvalidPosition),
192                            Err((_, _, Propagate::Continue)) => Err(AmlError::ContinueInInvalidPosition),
193                            Err((_, _, Propagate::Err(err))) => {
194                                error!("Failed to execute control method: {:?}", err);
195                                Err(err)
196                            }
197                        }
198                    }
199
200                    MethodCode::Native(ref method) => match (method)(self) {
201                        Ok(result) => Ok(result),
202                        Err(err) => {
203                            error!("Failed to execute control method: {:?}", err);
204                            Err(err)
205                        }
206                    },
207                };
208
209                /*
210                 * Locally-created objects should be destroyed on method exit (see §5.5.2.3 of the ACPI spec). We do
211                 * this by simply removing the method's local object layer.
212                 */
213                // TODO: this should also remove objects created by the method outside the method's scope, if they
214                // weren't statically created. This is harder.
215                self.namespace.remove_level(path.clone())?;
216
217                /*
218                 * Restore the old state.
219                 */
220                self.method_context = old_context;
221                self.current_scope = old_scope;
222
223                return_value
224            }
225
226            /*
227             * AML can encode methods that don't require any computation simply as the value that would otherwise be
228             * returned (e.g. a `_STA` object simply being an `AmlValue::Integer`, instead of a method that just
229             * returns an integer).
230             */
231            value => Ok(value),
232        }
233    }
234
235    // TODO: docs
236    pub fn initialize_objects(&mut self) -> Result<(), AmlError> {
237        use name_object::NameSeg;
238        use value::StatusObject;
239
240        /*
241         * If `\_SB._INI` exists, we unconditionally execute it at the beginning of device initialization.
242         */
243        match self.invoke_method(&AmlName::from_str("\\_SB._INI").unwrap(), Args::default()) {
244            Ok(_) => (),
245            Err(AmlError::ValueDoesNotExist(_)) => (),
246            Err(err) => return Err(err),
247        }
248
249        /*
250         * Next, we traverse the namespace, looking for devices.
251         *
252         * XXX: we clone the namespace here, which obviously drives up heap burden quite a bit (not as much as you
253         * might first expect though - we're only duplicating the level data structure, not all the objects). The
254         * issue here is that we need to access the namespace during traversal (e.g. to invoke a method), which the
255         * borrow checker really doesn't like. A better solution could be a iterator-like traversal system that
256         * keeps track of the namespace without keeping it borrowed. This works for now.
257         */
258        self.namespace.clone().traverse(|path, level: &NamespaceLevel| match level.typ {
259            LevelType::Device => {
260                let status = if level.values.contains_key(&NameSeg::from_str("_STA").unwrap()) {
261                    self.invoke_method(&AmlName::from_str("_STA").unwrap().resolve(&path)?, Args::default())?
262                        .as_status()?
263                } else {
264                    StatusObject::default()
265                };
266
267                /*
268                 * If the device is present and has an `_INI` method, invoke it.
269                 */
270                if status.present && level.values.contains_key(&NameSeg::from_str("_INI").unwrap()) {
271                    log::info!("Invoking _INI at level: {}", path);
272                    self.invoke_method(&AmlName::from_str("_INI").unwrap().resolve(&path)?, Args::default())?;
273                }
274
275                /*
276                 * We traverse the children of this device if it's present, or isn't present but is functional.
277                 */
278                Ok(status.present || status.functional)
279            }
280
281            LevelType::Scope => Ok(true),
282
283            // TODO: can any of these contain devices?
284            LevelType::Processor => Ok(false),
285            LevelType::PowerResource => Ok(false),
286            LevelType::ThermalZone => Ok(false),
287            LevelType::MethodLocals => Ok(false),
288        })?;
289
290        Ok(())
291    }
292
293    pub(crate) fn read_target(&self, target: &Target) -> Result<&AmlValue, AmlError> {
294        match target {
295            Target::Null => todo!(),
296            Target::Name(name) => {
297                let (_, handle) = self.namespace.search(name, &self.current_scope)?;
298                self.namespace.get(handle)
299            }
300            Target::Debug => todo!(),
301            Target::Arg(arg) => self.current_arg(*arg),
302            Target::Local(local) => self.local(*local),
303        }
304    }
305
306    /// Get the value of an argument by its argument number. Can only be executed from inside a control method.
307    pub(crate) fn current_arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
308        self.method_context.as_ref().ok_or(AmlError::NotExecutingControlMethod)?.args.arg(arg)
309    }
310
311    /// Get the current value of a local by its local number. Can only be executed from inside a control method.
312    pub(crate) fn local(&self, local: LocalNum) -> Result<&AmlValue, AmlError> {
313        if let None = self.method_context {
314            return Err(AmlError::NotExecutingControlMethod);
315        }
316        if local > 7 {
317            return Err(AmlError::InvalidLocalAccess(local));
318        }
319
320        self.method_context.as_ref().unwrap().locals[local as usize]
321            .as_ref()
322            .ok_or(AmlError::InvalidLocalAccess(local))
323    }
324
325    /// Perform a store into a `Target`, according to the rules specified by §19.3.5.8. This returns a value read
326    /// out of the target, if neccessary, as values can be altered during a store in some circumstances.  When
327    /// required, this also performs required implicit conversions, otherwise stores are semantically equivalent to
328    /// a `CopyObject`.
329    pub(crate) fn store(&mut self, target: Target, value: AmlValue) -> Result<AmlValue, AmlError> {
330        match target {
331            Target::Name(ref path) => {
332                let (_, handle) = self.namespace.search(path, &self.current_scope)?;
333
334                match self.namespace.get(handle).unwrap().type_of() {
335                    AmlType::FieldUnit => {
336                        let mut field = self.namespace.get(handle).unwrap().clone();
337                        field.write_field(value, self)?;
338                        field.read_field(self)
339                    }
340                    AmlType::BufferField => {
341                        let mut buffer_field = self.namespace.get(handle).unwrap().clone();
342                        buffer_field.write_buffer_field(value.clone(), self)?;
343                        Ok(value)
344                    }
345                    typ => {
346                        *self.namespace.get_mut(handle)? = value.as_type(typ, self)?;
347                        Ok(self.namespace.get(handle)?.clone())
348                    }
349                }
350            }
351
352            Target::Debug => {
353                // TODO
354                unimplemented!()
355            }
356
357            Target::Arg(arg_num) => {
358                if let None = self.method_context {
359                    return Err(AmlError::NotExecutingControlMethod);
360                }
361
362                /*
363                 * Stores into `Arg` objects are simply copied with no conversion applied, unless the `Arg`
364                 * contains an Object Reference, in which case an automatic de-reference occurs and the object is
365                 * copied to the target of the Object Reference, instead of overwriting the `Arg.`
366                 */
367                // TODO: implement behaviour for object references
368                self.method_context.as_mut().unwrap().args.store_arg(arg_num, value.clone())?;
369                Ok(value)
370            }
371
372            Target::Local(local_num) => {
373                if let None = self.method_context {
374                    return Err(AmlError::NotExecutingControlMethod);
375                }
376
377                /*
378                 * Stores into `Local` objects are always simply copied into the destination with no conversion
379                 * applied, even if it contains an Object Reference.
380                 */
381                self.method_context.as_mut().unwrap().locals[local_num as usize] = Some(value.clone());
382                Ok(value)
383            }
384
385            Target::Null => Ok(value),
386        }
387    }
388
389    /// Read from an operation-region, performing only standard-sized reads (supported powers-of-2 only. If a field
390    /// is not one of these sizes, it may need to be masked, or multiple reads may need to be performed).
391    pub(crate) fn read_region(&self, region_handle: AmlHandle, offset: u64, length: u64) -> Result<u64, AmlError> {
392        use bit_field::BitField;
393        use core::convert::TryInto;
394        use value::RegionSpace;
395
396        let (region_space, region_base, region_length, parent_device) = {
397            if let AmlValue::OpRegion { region, offset, length, parent_device } =
398                self.namespace.get(region_handle)?
399            {
400                (region, offset, length, parent_device)
401            } else {
402                return Err(AmlError::FieldRegionIsNotOpRegion);
403            }
404        };
405
406        match region_space {
407            RegionSpace::SystemMemory => {
408                let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
409                match length {
410                    8 => Ok(self.handler.read_u8(address) as u64),
411                    16 => Ok(self.handler.read_u16(address) as u64),
412                    32 => Ok(self.handler.read_u32(address) as u64),
413                    64 => Ok(self.handler.read_u64(address)),
414                    _ => Err(AmlError::FieldInvalidAccessSize),
415                }
416            }
417
418            RegionSpace::SystemIo => {
419                let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
420                match length {
421                    8 => Ok(self.handler.read_io_u8(port) as u64),
422                    16 => Ok(self.handler.read_io_u16(port) as u64),
423                    32 => Ok(self.handler.read_io_u32(port) as u64),
424                    _ => Err(AmlError::FieldInvalidAccessSize),
425                }
426            }
427
428            RegionSpace::PciConfig => {
429                /*
430                 * First, we need to get some extra information out of objects in the parent object. Both
431                 * `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
432                 * (e.g. systems with a single segment group and a single root, respectively).
433                 */
434                let parent_device = parent_device.as_ref().unwrap();
435                let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
436                    Ok((_, handle)) => self
437                        .namespace
438                        .get(handle)?
439                        .as_integer(self)?
440                        .try_into()
441                        .map_err(|_| AmlError::FieldInvalidAddress)?,
442                    Err(AmlError::ValueDoesNotExist(_)) => 0,
443                    Err(err) => return Err(err),
444                };
445                let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
446                    Ok((_, handle)) => self
447                        .namespace
448                        .get(handle)?
449                        .as_integer(self)?
450                        .try_into()
451                        .map_err(|_| AmlError::FieldInvalidAddress)?,
452                    Err(AmlError::ValueDoesNotExist(_)) => 0,
453                    Err(err) => return Err(err),
454                };
455                let adr = {
456                    let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
457                    self.namespace.get(handle)?.as_integer(self)?
458                };
459
460                let device = adr.get_bits(16..24) as u8;
461                let function = adr.get_bits(0..8) as u8;
462                let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
463
464                match length {
465                    8 => Ok(self.handler.read_pci_u8(seg, bbn, device, function, offset) as u64),
466                    16 => Ok(self.handler.read_pci_u16(seg, bbn, device, function, offset) as u64),
467                    32 => Ok(self.handler.read_pci_u32(seg, bbn, device, function, offset) as u64),
468                    _ => Err(AmlError::FieldInvalidAccessSize),
469                }
470            }
471
472            // TODO
473            _ => unimplemented!(),
474        }
475    }
476
477    pub(crate) fn write_region(
478        &mut self,
479        region_handle: AmlHandle,
480        offset: u64,
481        length: u64,
482        value: u64,
483    ) -> Result<(), AmlError> {
484        use bit_field::BitField;
485        use core::convert::TryInto;
486        use value::RegionSpace;
487
488        let (region_space, region_base, region_length, parent_device) = {
489            if let AmlValue::OpRegion { region, offset, length, parent_device } =
490                self.namespace.get(region_handle)?
491            {
492                (region, offset, length, parent_device)
493            } else {
494                return Err(AmlError::FieldRegionIsNotOpRegion);
495            }
496        };
497
498        match region_space {
499            RegionSpace::SystemMemory => {
500                let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
501                match length {
502                    8 => Ok(self.handler.write_u8(address, value as u8)),
503                    16 => Ok(self.handler.write_u16(address, value as u16)),
504                    32 => Ok(self.handler.write_u32(address, value as u32)),
505                    64 => Ok(self.handler.write_u64(address, value)),
506                    _ => Err(AmlError::FieldInvalidAccessSize),
507                }
508            }
509
510            RegionSpace::SystemIo => {
511                let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
512                match length {
513                    8 => Ok(self.handler.write_io_u8(port, value as u8)),
514                    16 => Ok(self.handler.write_io_u16(port, value as u16)),
515                    32 => Ok(self.handler.write_io_u32(port, value as u32)),
516                    _ => Err(AmlError::FieldInvalidAccessSize),
517                }
518            }
519
520            RegionSpace::PciConfig => {
521                /*
522                 * First, we need to get some extra information out of objects in the parent object. Both
523                 * `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
524                 * (e.g. systems with a single segment group and a single root, respectively).
525                 */
526                let parent_device = parent_device.as_ref().unwrap();
527                let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
528                    Ok((_, handle)) => self
529                        .namespace
530                        .get(handle)?
531                        .as_integer(self)?
532                        .try_into()
533                        .map_err(|_| AmlError::FieldInvalidAddress)?,
534                    Err(AmlError::ValueDoesNotExist(_)) => 0,
535                    Err(err) => return Err(err),
536                };
537                let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
538                    Ok((_, handle)) => self
539                        .namespace
540                        .get(handle)?
541                        .as_integer(self)?
542                        .try_into()
543                        .map_err(|_| AmlError::FieldInvalidAddress)?,
544                    Err(AmlError::ValueDoesNotExist(_)) => 0,
545                    Err(err) => return Err(err),
546                };
547                let adr = {
548                    let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
549                    self.namespace.get(handle)?.as_integer(self)?
550                };
551
552                let device = adr.get_bits(16..24) as u8;
553                let function = adr.get_bits(0..8) as u8;
554                let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
555
556                match length {
557                    8 => Ok(self.handler.write_pci_u8(seg, bbn, device, function, offset, value as u8)),
558                    16 => Ok(self.handler.write_pci_u16(seg, bbn, device, function, offset, value as u16)),
559                    32 => Ok(self.handler.write_pci_u32(seg, bbn, device, function, offset, value as u32)),
560                    _ => Err(AmlError::FieldInvalidAccessSize),
561                }
562            }
563
564            // TODO
565            _ => unimplemented!(),
566        }
567    }
568
569    fn add_predefined_objects(&mut self) {
570        /*
571         * These are the scopes predefined by the spec. Some tables will try to access them without defining them
572         * themselves, and so we have to pre-create them.
573         */
574        self.namespace.add_level(AmlName::from_str("\\_GPE").unwrap(), LevelType::Scope).unwrap();
575        self.namespace.add_level(AmlName::from_str("\\_SB").unwrap(), LevelType::Scope).unwrap();
576        self.namespace.add_level(AmlName::from_str("\\_SI").unwrap(), LevelType::Scope).unwrap();
577        self.namespace.add_level(AmlName::from_str("\\_PR").unwrap(), LevelType::Scope).unwrap();
578        self.namespace.add_level(AmlName::from_str("\\_TZ").unwrap(), LevelType::Scope).unwrap();
579
580        /*
581         * In the dark ages of ACPI 1.0, before `\_OSI`, `\_OS` was used to communicate to the firmware which OS
582         * was running. This was predictably not very good, and so was replaced in ACPI 3.0 with `_OSI`, which
583         * allows support for individual capabilities to be queried. `_OS` should not be used by modern firmwares,
584         * but to avoid problems we follow Linux in returning `"Microsoft Windows NT"`.
585         *
586         * See https://www.kernel.org/doc/html/latest/firmware-guide/acpi/osi.html for more information.
587         */
588        self.namespace
589            .add_value(AmlName::from_str("\\_OS").unwrap(), AmlValue::String("Microsoft Windows NT".to_string()))
590            .unwrap();
591
592        /*
593         * `\_OSI` was introduced by ACPI 3.0 to improve the situation created by `\_OS`. Unfortunately, exactly
594         * the same problem was immediately repeated by introducing capabilities reflecting that an ACPI
595         * implementation is exactly the same as a particular version of Windows' (e.g. firmwares will call
596         * `\_OSI("Windows 2001")`).
597         *
598         * We basically follow suit with whatever Linux does, as this will hopefully minimise breakage:
599         *    - We always claim `Windows *` compatability
600         *    - We answer 'yes' to `_OSI("Darwin")
601         *    - We answer 'no' to `_OSI("Linux")`, and report that the tables are doing the wrong thing
602         */
603        self.namespace
604            .add_value(
605                AmlName::from_str("\\_OSI").unwrap(),
606                AmlValue::native_method(1, false, 0, |context| {
607                    Ok(match context.current_arg(0)?.as_string(context)?.as_str() {
608                        "Windows 2000" => true,       // 2000
609                        "Windows 2001" => true,       // XP
610                        "Windows 2001 SP1" => true,   // XP SP1
611                        "Windows 2001 SP2" => true,   // XP SP2
612                        "Windows 2001.1" => true,     // Server 2003
613                        "Windows 2001.1 SP1" => true, // Server 2003 SP1
614                        "Windows 2006" => true,       // Vista
615                        "Windows 2006 SP1" => true,   // Vista SP1
616                        "Windows 2006 SP2" => true,   // Vista SP2
617                        "Windows 2006.1" => true,     // Server 2008
618                        "Windows 2009" => true,       // 7 and Server 2008 R2
619                        "Windows 2012" => true,       // 8 and Server 2012
620                        "Windows 2013" => true,       // 8.1 and Server 2012 R2
621                        "Windows 2015" => true,       // 10
622                        "Windows 2016" => true,       // 10 version 1607
623                        "Windows 2017" => true,       // 10 version 1703
624                        "Windows 2017.2" => true,     // 10 version 1709
625                        "Windows 2018" => true,       // 10 version 1803
626                        "Windows 2018.2" => true,     // 10 version 1809
627                        "Windows 2019" => true,       // 10 version 1903
628
629                        "Darwin" => true,
630
631                        "Linux" => {
632                            // TODO: should we allow users to specify that this should be true? Linux has a
633                            // command line option for this.
634                            warn!("ACPI evaluated `_OSI(\"Linux\")`. This is a bug. Reporting no support.");
635                            false
636                        }
637
638                        "Extended Address Space Descriptor" => true,
639                        // TODO: support module devices
640                        "Module Device" => false,
641                        "3.0 Thermal Model" => true,
642                        "3.0 _SCP Extensions" => true,
643                        // TODO: support processor aggregator devices
644                        "Processor Aggregator Device" => false,
645
646                        _ => false,
647                    }
648                    .then_some(AmlValue::ones())
649                    .unwrap_or(AmlValue::zero()))
650                }),
651            )
652            .unwrap();
653
654        /*
655         * `\_REV` evaluates to the version of the ACPI specification supported by this interpreter. Linux did this
656         * correctly until 2015, but firmwares misused this to detect Linux (as even modern versions of Windows
657         * return `2`), and so they switched to just returning `2` (as we'll also do). `_REV` should be considered
658         * useless and deprecated (this is mirrored in newer specs, which claim `2` means "ACPI 2 or greater").
659         */
660        self.namespace.add_value(AmlName::from_str("\\_REV").unwrap(), AmlValue::Integer(2)).unwrap();
661    }
662}
663
664/// Trait type used by [`AmlContext`] to handle reading and writing to various types of memory in the system.
665pub trait Handler: Send + Sync {
666    fn read_u8(&self, address: usize) -> u8;
667    fn read_u16(&self, address: usize) -> u16;
668    fn read_u32(&self, address: usize) -> u32;
669    fn read_u64(&self, address: usize) -> u64;
670
671    fn write_u8(&mut self, address: usize, value: u8);
672    fn write_u16(&mut self, address: usize, value: u16);
673    fn write_u32(&mut self, address: usize, value: u32);
674    fn write_u64(&mut self, address: usize, value: u64);
675
676    fn read_io_u8(&self, port: u16) -> u8;
677    fn read_io_u16(&self, port: u16) -> u16;
678    fn read_io_u32(&self, port: u16) -> u32;
679
680    fn write_io_u8(&self, port: u16, value: u8);
681    fn write_io_u16(&self, port: u16, value: u16);
682    fn write_io_u32(&self, port: u16, value: u32);
683
684    fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u8;
685    fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u16;
686    fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u32;
687
688    fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u8);
689    fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u16);
690    fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u32);
691
692    fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
693        panic!("Fatal error while executing AML (encountered DefFatal op). fatal_type = {:?}, fatal_code = {:?}, fatal_arg = {:?}", fatal_type, fatal_code, fatal_arg);
694    }
695}
696
697/// Used when an [`AmlContext`] encounters an error.
698#[derive(Clone, PartialEq, Eq, Debug)]
699pub enum AmlError {
700    /*
701     * Errors produced parsing the AML stream.
702     */
703    UnexpectedEndOfStream,
704    UnexpectedByte(u8),
705    /// Produced when the stream evaluates to something other than nothing or an error.
706    MalformedStream,
707    InvalidNameSeg,
708    InvalidPkgLength,
709    InvalidFieldFlags,
710    UnterminatedStringConstant,
711    InvalidStringConstant,
712    InvalidRegionSpace(u8),
713    /// Produced when a `DefPackage` contains a different number of elements to the package's length.
714    MalformedPackage,
715    /// Produced when a `DefBuffer` contains more bytes that its size.
716    MalformedBuffer,
717    /// Emitted by a parser when it's clear that the stream doesn't encode the object parsed by
718    /// that parser (e.g. the wrong opcode starts the stream). This is handled specially by some
719    /// parsers such as `or` and `choice!`.
720    WrongParser,
721    /// Returned when a `DefFatal` op is encountered. This is separately reported using [`Handler::handle_fatal_error`].
722    FatalError,
723
724    /*
725     * Errors produced manipulating AML names.
726     */
727    EmptyNamesAreInvalid,
728    /// Produced when trying to normalize a path that does not point to a valid level of the
729    /// namespace. E.g. `\_SB.^^PCI0` goes above the root of the namespace. The contained value is the name that
730    /// normalization was attempted upon.
731    InvalidNormalizedName(AmlName),
732    RootHasNoParent,
733
734    /*
735     * Errors produced working with the namespace.
736     */
737    /// Produced when a sub-level or value is added to a level that has not yet been added to the namespace. The
738    /// `AmlName` is the name of the entire sub-level/value.
739    LevelDoesNotExist(AmlName),
740    ValueDoesNotExist(AmlName),
741    /// Produced when two values with the same name are added to the namespace.
742    NameCollision(AmlName),
743    TriedToRemoveRootNamespace,
744
745    /*
746     * Errors produced executing control methods.
747     */
748    /// Produced when AML tries to do something only possible in a control method (e.g. read from an argument)
749    /// when there's no control method executing.
750    NotExecutingControlMethod,
751    /// Produced when a method accesses an argument it does not have (e.g. a method that takes 2
752    /// arguments accesses `Arg4`). The inner value is the number of the argument accessed.
753    InvalidArgAccess(ArgNum),
754    /// Produced when a method accesses a local that it has not stored into.
755    InvalidLocalAccess(LocalNum),
756    /// Tried to invoke a method with too many arguments.
757    TooManyArgs,
758    /// A `DefBreak` operation was performed outside of a `DefWhile` or `DefSwitch`.
759    BreakInInvalidPosition,
760    /// A `DefContinue` operation was performed outside of a `DefWhile`.
761    ContinueInInvalidPosition,
762
763    /*
764     * Errors produced parsing the PCI routing tables (_PRT objects).
765     */
766    PrtInvalidAddress,
767    PrtInvalidPin,
768    PrtInvalidSource,
769    PrtInvalidGsi,
770    /// Produced when the PRT doesn't contain an entry for the requested address + pin
771    PrtNoEntry,
772
773    /*
774     * Errors produced parsing Resource Descriptors.
775     */
776    ReservedResourceType,
777    ResourceDescriptorTooShort,
778    ResourceDescriptorTooLong,
779    UnexpectedResourceType,
780
781    /*
782     * Errors produced working with AML values.
783     */
784    IncompatibleValueConversion {
785        current: AmlType,
786        target: AmlType,
787    },
788    InvalidStatusObject,
789    InvalidShiftLeft,
790    InvalidShiftRight,
791    FieldRegionIsNotOpRegion,
792    FieldInvalidAddress,
793    FieldInvalidAccessSize,
794    TypeCannotBeCompared(AmlType),
795    /// Produced when the `Mid` operator is applied to a value of a type other than `Buffer` or `String`.
796    TypeCannotBeSliced(AmlType),
797    TypeCannotBeWrittenToBufferField(AmlType),
798    BufferFieldIndexesOutOfBounds,
799
800    /// Unimplemented functionality - return error rather than abort
801    Unimplemented,
802}
803
804#[cfg(test)]
805mod tests {
806    use super::*;
807
808    #[test]
809    fn test_send_sync() {
810        // verify that AmlContext implements Send and Sync
811        fn test_send_sync<T: Send + Sync>() {}
812        test_send_sync::<AmlContext>();
813    }
814}