#![no_std]
#![feature(decl_macro)]
extern crate alloc;
#[cfg(test)]
extern crate std;
#[cfg(test)]
mod test_utils;
pub(crate) mod expression;
pub(crate) mod misc;
pub(crate) mod name_object;
pub(crate) mod namespace;
pub(crate) mod opcode;
pub(crate) mod parser;
pub mod pci_routing;
pub(crate) mod pkg_length;
pub mod resource;
pub(crate) mod statement;
pub(crate) mod term_object;
pub mod value;
pub use crate::{namespace::*, value::AmlValue};
use alloc::{boxed::Box, string::ToString};
use core::mem;
use log::{error, warn};
use misc::{ArgNum, LocalNum};
use name_object::Target;
use parser::{Parser, Propagate};
use pkg_length::PkgLength;
use term_object::term_list;
use value::{AmlType, Args};
pub const AML_INTERPRETER_REVISION: u64 = 0;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum DebugVerbosity {
None,
Scopes,
AllScopes,
All,
}
#[derive(Debug)]
struct MethodContext {
locals: [Option<AmlValue>; 8],
args: Args,
}
impl MethodContext {
fn new(args: Args) -> MethodContext {
const NONE_BUT_CONST: Option<AmlValue> = None;
MethodContext { locals: [NONE_BUT_CONST; 8], args }
}
}
pub struct AmlContext {
handler: Box<dyn Handler>,
pub namespace: Namespace,
method_context: Option<MethodContext>,
current_scope: AmlName,
scope_indent: usize,
debug_verbosity: DebugVerbosity,
}
impl AmlContext {
pub fn new(handler: Box<dyn Handler>, debug_verbosity: DebugVerbosity) -> AmlContext {
let mut context = AmlContext {
handler,
namespace: Namespace::new(),
method_context: None,
current_scope: AmlName::root(),
scope_indent: 0,
debug_verbosity,
};
context.add_predefined_objects();
context
}
pub fn parse_table(&mut self, stream: &[u8]) -> Result<(), AmlError> {
if stream.len() == 0 {
return Err(AmlError::UnexpectedEndOfStream);
}
let table_length = PkgLength::from_raw_length(stream, stream.len() as u32).unwrap();
match term_object::term_list(table_length).parse(stream, self) {
Ok(_) => Ok(()),
Err((_, _, Propagate::Err(err))) => {
error!("Failed to parse AML stream. Err = {:?}", err);
Err(err)
}
Err((_, _, other)) => {
error!("AML table evaluated to unexpected result: {:?}", other);
Err(AmlError::MalformedStream)
}
}
}
pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
use value::MethodCode;
match self.namespace.get_by_path(path)?.clone() {
AmlValue::Method { flags, code } => {
let old_context = mem::replace(&mut self.method_context, Some(MethodContext::new(args)));
let old_scope = mem::replace(&mut self.current_scope, path.clone());
self.namespace.add_level(path.clone(), LevelType::MethodLocals)?;
let return_value = match code {
MethodCode::Aml(ref code) => {
match term_list(PkgLength::from_raw_length(code, code.len() as u32).unwrap())
.parse(code, self)
{
Ok(_) => Ok(AmlValue::Integer(0)),
Err((_, _, Propagate::Return(result))) => Ok(result),
Err((_, _, Propagate::Break)) => Err(AmlError::BreakInInvalidPosition),
Err((_, _, Propagate::Continue)) => Err(AmlError::ContinueInInvalidPosition),
Err((_, _, Propagate::Err(err))) => {
error!("Failed to execute control method: {:?}", err);
Err(err)
}
}
}
MethodCode::Native(ref method) => match (method)(self) {
Ok(result) => Ok(result),
Err(err) => {
error!("Failed to execute control method: {:?}", err);
Err(err)
}
},
};
self.namespace.remove_level(path.clone())?;
self.method_context = old_context;
self.current_scope = old_scope;
return_value
}
value => Ok(value),
}
}
pub fn initialize_objects(&mut self) -> Result<(), AmlError> {
use name_object::NameSeg;
use value::StatusObject;
match self.invoke_method(&AmlName::from_str("\\_SB._INI").unwrap(), Args::default()) {
Ok(_) => (),
Err(AmlError::ValueDoesNotExist(_)) => (),
Err(err) => return Err(err),
}
self.namespace.clone().traverse(|path, level: &NamespaceLevel| match level.typ {
LevelType::Device => {
let status = if level.values.contains_key(&NameSeg::from_str("_STA").unwrap()) {
self.invoke_method(&AmlName::from_str("_STA").unwrap().resolve(&path)?, Args::default())?
.as_status()?
} else {
StatusObject::default()
};
if status.present && level.values.contains_key(&NameSeg::from_str("_INI").unwrap()) {
log::info!("Invoking _INI at level: {}", path);
self.invoke_method(&AmlName::from_str("_INI").unwrap().resolve(&path)?, Args::default())?;
}
Ok(status.present || status.functional)
}
LevelType::Scope => Ok(true),
LevelType::Processor => Ok(false),
LevelType::PowerResource => Ok(false),
LevelType::ThermalZone => Ok(false),
LevelType::MethodLocals => Ok(false),
})?;
Ok(())
}
pub(crate) fn read_target(&self, target: &Target) -> Result<&AmlValue, AmlError> {
match target {
Target::Null => todo!(),
Target::Name(name) => {
let (_, handle) = self.namespace.search(name, &self.current_scope)?;
self.namespace.get(handle)
}
Target::Debug => todo!(),
Target::Arg(arg) => self.current_arg(*arg),
Target::Local(local) => self.local(*local),
}
}
pub(crate) fn current_arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
self.method_context.as_ref().ok_or(AmlError::NotExecutingControlMethod)?.args.arg(arg)
}
pub(crate) fn local(&self, local: LocalNum) -> Result<&AmlValue, AmlError> {
if let None = self.method_context {
return Err(AmlError::NotExecutingControlMethod);
}
if local > 7 {
return Err(AmlError::InvalidLocalAccess(local));
}
self.method_context.as_ref().unwrap().locals[local as usize]
.as_ref()
.ok_or(AmlError::InvalidLocalAccess(local))
}
pub(crate) fn store(&mut self, target: Target, value: AmlValue) -> Result<AmlValue, AmlError> {
match target {
Target::Name(ref path) => {
let (_, handle) = self.namespace.search(path, &self.current_scope)?;
match self.namespace.get(handle).unwrap().type_of() {
AmlType::FieldUnit => {
let mut field = self.namespace.get(handle).unwrap().clone();
field.write_field(value, self)?;
field.read_field(self)
}
AmlType::BufferField => {
let mut buffer_field = self.namespace.get(handle).unwrap().clone();
buffer_field.write_buffer_field(value.clone(), self)?;
Ok(value)
}
typ => {
*self.namespace.get_mut(handle)? = value.as_type(typ, self)?;
Ok(self.namespace.get(handle)?.clone())
}
}
}
Target::Debug => {
unimplemented!()
}
Target::Arg(arg_num) => {
if let None = self.method_context {
return Err(AmlError::NotExecutingControlMethod);
}
self.method_context.as_mut().unwrap().args.store_arg(arg_num, value.clone())?;
Ok(value)
}
Target::Local(local_num) => {
if let None = self.method_context {
return Err(AmlError::NotExecutingControlMethod);
}
self.method_context.as_mut().unwrap().locals[local_num as usize] = Some(value.clone());
Ok(value)
}
Target::Null => Ok(value),
}
}
pub(crate) fn read_region(&self, region_handle: AmlHandle, offset: u64, length: u64) -> Result<u64, AmlError> {
use bit_field::BitField;
use core::convert::TryInto;
use value::RegionSpace;
let (region_space, region_base, region_length, parent_device) = {
if let AmlValue::OpRegion { region, offset, length, parent_device } =
self.namespace.get(region_handle)?
{
(region, offset, length, parent_device)
} else {
return Err(AmlError::FieldRegionIsNotOpRegion);
}
};
match region_space {
RegionSpace::SystemMemory => {
let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
match length {
8 => Ok(self.handler.read_u8(address) as u64),
16 => Ok(self.handler.read_u16(address) as u64),
32 => Ok(self.handler.read_u32(address) as u64),
64 => Ok(self.handler.read_u64(address)),
_ => Err(AmlError::FieldInvalidAccessSize),
}
}
RegionSpace::SystemIo => {
let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
match length {
8 => Ok(self.handler.read_io_u8(port) as u64),
16 => Ok(self.handler.read_io_u16(port) as u64),
32 => Ok(self.handler.read_io_u32(port) as u64),
_ => Err(AmlError::FieldInvalidAccessSize),
}
}
RegionSpace::PciConfig => {
let parent_device = parent_device.as_ref().unwrap();
let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
Ok((_, handle)) => self
.namespace
.get(handle)?
.as_integer(self)?
.try_into()
.map_err(|_| AmlError::FieldInvalidAddress)?,
Err(AmlError::ValueDoesNotExist(_)) => 0,
Err(err) => return Err(err),
};
let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
Ok((_, handle)) => self
.namespace
.get(handle)?
.as_integer(self)?
.try_into()
.map_err(|_| AmlError::FieldInvalidAddress)?,
Err(AmlError::ValueDoesNotExist(_)) => 0,
Err(err) => return Err(err),
};
let adr = {
let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
self.namespace.get(handle)?.as_integer(self)?
};
let device = adr.get_bits(16..24) as u8;
let function = adr.get_bits(0..8) as u8;
let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
match length {
8 => Ok(self.handler.read_pci_u8(seg, bbn, device, function, offset) as u64),
16 => Ok(self.handler.read_pci_u16(seg, bbn, device, function, offset) as u64),
32 => Ok(self.handler.read_pci_u32(seg, bbn, device, function, offset) as u64),
_ => Err(AmlError::FieldInvalidAccessSize),
}
}
_ => unimplemented!(),
}
}
pub(crate) fn write_region(
&mut self,
region_handle: AmlHandle,
offset: u64,
length: u64,
value: u64,
) -> Result<(), AmlError> {
use bit_field::BitField;
use core::convert::TryInto;
use value::RegionSpace;
let (region_space, region_base, region_length, parent_device) = {
if let AmlValue::OpRegion { region, offset, length, parent_device } =
self.namespace.get(region_handle)?
{
(region, offset, length, parent_device)
} else {
return Err(AmlError::FieldRegionIsNotOpRegion);
}
};
match region_space {
RegionSpace::SystemMemory => {
let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
match length {
8 => Ok(self.handler.write_u8(address, value as u8)),
16 => Ok(self.handler.write_u16(address, value as u16)),
32 => Ok(self.handler.write_u32(address, value as u32)),
64 => Ok(self.handler.write_u64(address, value)),
_ => Err(AmlError::FieldInvalidAccessSize),
}
}
RegionSpace::SystemIo => {
let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
match length {
8 => Ok(self.handler.write_io_u8(port, value as u8)),
16 => Ok(self.handler.write_io_u16(port, value as u16)),
32 => Ok(self.handler.write_io_u32(port, value as u32)),
_ => Err(AmlError::FieldInvalidAccessSize),
}
}
RegionSpace::PciConfig => {
let parent_device = parent_device.as_ref().unwrap();
let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
Ok((_, handle)) => self
.namespace
.get(handle)?
.as_integer(self)?
.try_into()
.map_err(|_| AmlError::FieldInvalidAddress)?,
Err(AmlError::ValueDoesNotExist(_)) => 0,
Err(err) => return Err(err),
};
let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
Ok((_, handle)) => self
.namespace
.get(handle)?
.as_integer(self)?
.try_into()
.map_err(|_| AmlError::FieldInvalidAddress)?,
Err(AmlError::ValueDoesNotExist(_)) => 0,
Err(err) => return Err(err),
};
let adr = {
let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
self.namespace.get(handle)?.as_integer(self)?
};
let device = adr.get_bits(16..24) as u8;
let function = adr.get_bits(0..8) as u8;
let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
match length {
8 => Ok(self.handler.write_pci_u8(seg, bbn, device, function, offset, value as u8)),
16 => Ok(self.handler.write_pci_u16(seg, bbn, device, function, offset, value as u16)),
32 => Ok(self.handler.write_pci_u32(seg, bbn, device, function, offset, value as u32)),
_ => Err(AmlError::FieldInvalidAccessSize),
}
}
_ => unimplemented!(),
}
}
fn add_predefined_objects(&mut self) {
self.namespace.add_level(AmlName::from_str("\\_GPE").unwrap(), LevelType::Scope).unwrap();
self.namespace.add_level(AmlName::from_str("\\_SB").unwrap(), LevelType::Scope).unwrap();
self.namespace.add_level(AmlName::from_str("\\_SI").unwrap(), LevelType::Scope).unwrap();
self.namespace.add_level(AmlName::from_str("\\_PR").unwrap(), LevelType::Scope).unwrap();
self.namespace.add_level(AmlName::from_str("\\_TZ").unwrap(), LevelType::Scope).unwrap();
self.namespace
.add_value(AmlName::from_str("\\_OS").unwrap(), AmlValue::String("Microsoft Windows NT".to_string()))
.unwrap();
self.namespace
.add_value(
AmlName::from_str("\\_OSI").unwrap(),
AmlValue::native_method(1, false, 0, |context| {
Ok(match context.current_arg(0)?.as_string(context)?.as_str() {
"Windows 2000" => true, "Windows 2001" => true, "Windows 2001 SP1" => true, "Windows 2001 SP2" => true, "Windows 2001.1" => true, "Windows 2001.1 SP1" => true, "Windows 2006" => true, "Windows 2006 SP1" => true, "Windows 2006 SP2" => true, "Windows 2006.1" => true, "Windows 2009" => true, "Windows 2012" => true, "Windows 2013" => true, "Windows 2015" => true, "Windows 2016" => true, "Windows 2017" => true, "Windows 2017.2" => true, "Windows 2018" => true, "Windows 2018.2" => true, "Windows 2019" => true,
"Darwin" => true,
"Linux" => {
warn!("ACPI evaluated `_OSI(\"Linux\")`. This is a bug. Reporting no support.");
false
}
"Extended Address Space Descriptor" => true,
"Module Device" => false,
"3.0 Thermal Model" => true,
"3.0 _SCP Extensions" => true,
"Processor Aggregator Device" => false,
_ => false,
}
.then_some(AmlValue::ones())
.unwrap_or(AmlValue::zero()))
}),
)
.unwrap();
self.namespace.add_value(AmlName::from_str("\\_REV").unwrap(), AmlValue::Integer(2)).unwrap();
}
}
pub trait Handler: Send + Sync {
fn read_u8(&self, address: usize) -> u8;
fn read_u16(&self, address: usize) -> u16;
fn read_u32(&self, address: usize) -> u32;
fn read_u64(&self, address: usize) -> u64;
fn write_u8(&mut self, address: usize, value: u8);
fn write_u16(&mut self, address: usize, value: u16);
fn write_u32(&mut self, address: usize, value: u32);
fn write_u64(&mut self, address: usize, value: u64);
fn read_io_u8(&self, port: u16) -> u8;
fn read_io_u16(&self, port: u16) -> u16;
fn read_io_u32(&self, port: u16) -> u32;
fn write_io_u8(&self, port: u16, value: u8);
fn write_io_u16(&self, port: u16, value: u16);
fn write_io_u32(&self, port: u16, value: u32);
fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u8;
fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u16;
fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u32;
fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u8);
fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u16);
fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u32);
fn handle_fatal_error(&self, fatal_type: u8, fatal_code: u32, fatal_arg: u64) {
panic!("Fatal error while executing AML (encountered DefFatal op). fatal_type = {:?}, fatal_code = {:?}, fatal_arg = {:?}", fatal_type, fatal_code, fatal_arg);
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum AmlError {
UnexpectedEndOfStream,
UnexpectedByte(u8),
MalformedStream,
InvalidNameSeg,
InvalidPkgLength,
InvalidFieldFlags,
UnterminatedStringConstant,
InvalidStringConstant,
InvalidRegionSpace(u8),
MalformedPackage,
MalformedBuffer,
WrongParser,
FatalError,
EmptyNamesAreInvalid,
InvalidNormalizedName(AmlName),
RootHasNoParent,
LevelDoesNotExist(AmlName),
ValueDoesNotExist(AmlName),
NameCollision(AmlName),
TriedToRemoveRootNamespace,
NotExecutingControlMethod,
InvalidArgAccess(ArgNum),
InvalidLocalAccess(LocalNum),
TooManyArgs,
BreakInInvalidPosition,
ContinueInInvalidPosition,
PrtInvalidAddress,
PrtInvalidPin,
PrtInvalidSource,
PrtInvalidGsi,
PrtNoEntry,
ReservedResourceType,
ResourceDescriptorTooShort,
ResourceDescriptorTooLong,
UnexpectedResourceType,
IncompatibleValueConversion {
current: AmlType,
target: AmlType,
},
InvalidStatusObject,
InvalidShiftLeft,
InvalidShiftRight,
FieldRegionIsNotOpRegion,
FieldInvalidAddress,
FieldInvalidAccessSize,
TypeCannotBeCompared(AmlType),
TypeCannotBeSliced(AmlType),
TypeCannotBeWrittenToBufferField(AmlType),
BufferFieldIndexesOutOfBounds,
Unimplemented,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_send_sync() {
fn test_send_sync<T: Send + Sync>() {}
test_send_sync::<AmlContext>();
}
}