Skip to main content

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