use crate::{misc::ArgNum, AmlContext, AmlError, AmlHandle, AmlName};
use alloc::{string::String, vec::Vec};
use bit_field::BitField;
use core::cmp;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum RegionSpace {
SystemMemory,
SystemIo,
PciConfig,
EmbeddedControl,
SMBus,
SystemCmos,
PciBarTarget,
IPMI,
GeneralPurposeIo,
GenericSerialBus,
OemDefined(u8),
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FieldAccessType {
Any,
Byte,
Word,
DWord,
QWord,
Buffer,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FieldUpdateRule {
Preserve,
WriteAsOnes,
WriteAsZeros,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct FieldFlags(u8);
impl FieldFlags {
pub fn new(value: u8) -> FieldFlags {
FieldFlags(value)
}
pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {
match self.0.get_bits(0..4) {
0 => Ok(FieldAccessType::Any),
1 => Ok(FieldAccessType::Byte),
2 => Ok(FieldAccessType::Word),
3 => Ok(FieldAccessType::DWord),
4 => Ok(FieldAccessType::QWord),
5 => Ok(FieldAccessType::Buffer),
_ => Err(AmlError::InvalidFieldFlags),
}
}
pub fn lock_rule(&self) -> bool {
self.0.get_bit(4)
}
pub fn field_update_rule(&self) -> Result<FieldUpdateRule, AmlError> {
match self.0.get_bits(5..7) {
0 => Ok(FieldUpdateRule::Preserve),
1 => Ok(FieldUpdateRule::WriteAsOnes),
2 => Ok(FieldUpdateRule::WriteAsZeros),
_ => Err(AmlError::InvalidFieldFlags),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MethodFlags(u8);
impl MethodFlags {
pub fn new(value: u8) -> MethodFlags {
MethodFlags(value)
}
pub fn arg_count(&self) -> u8 {
self.0.get_bits(0..3)
}
pub fn serialize(&self) -> bool {
self.0.get_bit(3)
}
pub fn sync_level(&self) -> u8 {
self.0.get_bits(4..8)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct StatusObject {
pub present: bool,
pub enabled: bool,
pub show_in_ui: bool,
pub functional: bool,
pub battery_present: bool,
}
impl Default for StatusObject {
fn default() -> Self {
StatusObject { present: true, enabled: true, show_in_ui: true, functional: true, battery_present: true }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AmlType {
Uninitialized,
Buffer,
BufferField,
DdbHandle,
DebugObject,
Event,
FieldUnit,
Integer,
Method,
Mutex,
ObjReference,
OpRegion,
Package,
PowerResource,
Processor,
RawDataBuffer,
String,
ThermalZone,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum AmlValue {
Boolean(bool),
Integer(u64),
String(String),
OpRegion {
region: RegionSpace,
offset: u64,
length: u64,
parent_device: Option<AmlName>,
},
Field {
region: AmlHandle,
flags: FieldFlags,
offset: u64,
length: u64,
},
Method {
flags: MethodFlags,
code: Vec<u8>,
},
Buffer {
bytes: Vec<u8>,
size: u64,
},
Processor {
id: u8,
pblk_address: u32,
pblk_len: u8,
},
Mutex {
sync_level: u8,
},
Package(Vec<AmlValue>),
}
impl AmlValue {
pub fn type_of(&self) -> AmlType {
match self {
AmlValue::Boolean(_) => AmlType::Integer,
AmlValue::Integer(_) => AmlType::Integer,
AmlValue::String(_) => AmlType::String,
AmlValue::OpRegion { .. } => AmlType::OpRegion,
AmlValue::Field { .. } => AmlType::FieldUnit,
AmlValue::Method { .. } => AmlType::Method,
AmlValue::Buffer { .. } => AmlType::Buffer,
AmlValue::Processor { .. } => AmlType::Processor,
AmlValue::Mutex { .. } => AmlType::Mutex,
AmlValue::Package(_) => AmlType::Package,
}
}
pub fn as_bool(&self) -> Result<bool, AmlError> {
match self {
AmlValue::Boolean(value) => Ok(*value),
AmlValue::Integer(value) => Ok(*value != 0),
_ => Err(AmlError::IncompatibleValueConversion),
}
}
pub fn as_integer(&self, context: &AmlContext) -> Result<u64, AmlError> {
match self {
AmlValue::Integer(value) => Ok(*value),
AmlValue::Buffer { ref bytes, .. } => {
let bytes = if bytes.len() > 8 { &bytes[0..8] } else { bytes };
Ok(bytes.iter().rev().fold(0: u64, |mut i, &popped| {
i <<= 8;
i += popped as u64;
i
}))
}
AmlValue::Field { .. } => self.read_field(context)?.as_integer(context),
_ => Err(AmlError::IncompatibleValueConversion),
}
}
pub fn as_status(&self) -> Result<StatusObject, AmlError> {
match self {
AmlValue::Integer(value) => {
if value.get_bits(5..64) != 0 {
return Err(AmlError::InvalidStatusObject);
}
Ok(StatusObject {
present: value.get_bit(0),
enabled: value.get_bit(1),
show_in_ui: value.get_bit(2),
functional: value.get_bit(3),
battery_present: value.get_bit(4),
})
}
_ => Err(AmlError::InvalidStatusObject),
}
}
pub fn as_type(&self, desired_type: AmlType, context: &AmlContext) -> Result<AmlValue, AmlError> {
if self.type_of() == desired_type {
return Ok(self.clone());
}
match desired_type {
AmlType::Integer => self.as_integer(context).map(|value| AmlValue::Integer(value)),
AmlType::FieldUnit => panic!(
"Can't implicitly convert to FieldUnit. This must be special-cased by the caller for now :("
),
_ => Err(AmlError::IncompatibleValueConversion),
}
}
pub fn read_field(&self, context: &AmlContext) -> Result<AmlValue, AmlError> {
if let AmlValue::Field { region, flags, offset, length } = self {
let maximum_access_size = {
if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
match region {
RegionSpace::SystemMemory => 64,
RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
_ => unimplemented!(),
}
} else {
return Err(AmlError::FieldRegionIsNotOpRegion);
}
};
let minimum_access_size = match flags.access_type()? {
FieldAccessType::Any => 8,
FieldAccessType::Byte => 8,
FieldAccessType::Word => 16,
FieldAccessType::DWord => 32,
FieldAccessType::QWord => 64,
FieldAccessType::Buffer => 8,
};
let access_size = u64::max(minimum_access_size, length.next_power_of_two());
Ok(AmlValue::Integer(
context.read_region(*region, *offset, access_size)?.get_bits(0..(*length as usize)),
))
} else {
Err(AmlError::IncompatibleValueConversion)
}
}
pub fn write_field(&mut self, value: AmlValue, context: &mut AmlContext) -> Result<(), AmlError> {
let field_update_rule = if let AmlValue::Field { region, flags, offset, length } = self {
flags.field_update_rule()?
} else {
return Err(AmlError::IncompatibleValueConversion);
};
let mut field_value = match field_update_rule {
FieldUpdateRule::Preserve => self.read_field(context)?.as_integer(context)?,
FieldUpdateRule::WriteAsOnes => 0xffffffff_ffffffff,
FieldUpdateRule::WriteAsZeros => 0x0,
};
if let AmlValue::Field { region, flags, offset, length } = self {
let maximum_access_size = {
if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
match region {
RegionSpace::SystemMemory => 64,
RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
_ => unimplemented!(),
}
} else {
return Err(AmlError::FieldRegionIsNotOpRegion);
}
};
let minimum_access_size = match flags.access_type()? {
FieldAccessType::Any => 8,
FieldAccessType::Byte => 8,
FieldAccessType::Word => 16,
FieldAccessType::DWord => 32,
FieldAccessType::QWord => 64,
FieldAccessType::Buffer => 8,
};
let access_size = u64::max(minimum_access_size, length.next_power_of_two());
field_value.set_bits(0..(*length as usize), value.as_integer(context)?);
context.write_region(*region, *offset, access_size, field_value)
} else {
Err(AmlError::IncompatibleValueConversion)
}
}
pub fn cmp(&self, other: AmlValue, context: &mut AmlContext) -> Result<cmp::Ordering, AmlError> {
let self_inner =
if self.type_of() == AmlType::FieldUnit { self.read_field(context)? } else { self.clone() };
match self_inner.type_of() {
AmlType::Integer => Ok(self.as_integer(context)?.cmp(&other.as_integer(context)?)),
AmlType::Buffer => unimplemented!(),
AmlType::String => unimplemented!(),
typ => Err(AmlError::TypeCannotBeCompared(typ)),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Args {
pub arg_0: Option<AmlValue>,
pub arg_1: Option<AmlValue>,
pub arg_2: Option<AmlValue>,
pub arg_3: Option<AmlValue>,
pub arg_4: Option<AmlValue>,
pub arg_5: Option<AmlValue>,
pub arg_6: Option<AmlValue>,
}
impl Args {
pub fn from_list(mut list: Vec<AmlValue>) -> Args {
assert!(list.len() <= 7);
list.reverse();
Args {
arg_0: list.pop(),
arg_1: list.pop(),
arg_2: list.pop(),
arg_3: list.pop(),
arg_4: list.pop(),
arg_5: list.pop(),
arg_6: list.pop(),
}
}
pub fn arg(&self, num: ArgNum) -> Result<&AmlValue, AmlError> {
match num {
0 => self.arg_0.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
1 => self.arg_1.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
2 => self.arg_2.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
3 => self.arg_3.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
4 => self.arg_4.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
5 => self.arg_5.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
6 => self.arg_6.as_ref().ok_or(AmlError::InvalidArgAccess(num)),
_ => Err(AmlError::InvalidArgAccess(num)),
}
}
}