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}