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}