Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AML: Correctly invoke _SEG,_BBN, and _ADR methods for PCI region accesses, plus assorted bits #208

Merged
merged 7 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion aml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description = "Library for parsing AML"
categories = ["hardware-support", "no-std"]
readme = "../README.md"
license = "MIT/Apache-2.0"
edition = "2018"
edition = "2021"

[dependencies]
log = "0.4"
Expand Down
6 changes: 3 additions & 3 deletions aml/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use alloc::{
vec,
vec::Vec,
};
use core::{cmp::Ordering, convert::TryInto, mem, ops::Deref};
use core::{cmp::Ordering, mem, ops::Deref};

pub fn expression_opcode<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
where
Expand Down Expand Up @@ -331,7 +331,7 @@ where
DebugVerbosity::AllScopes,
"DefIncrement",
super_name().map_with_context(|addend, context| {
let value = try_with_context!(context, context.read_target(&addend));
let value = try_with_context!(context, context.read_target(&addend)).clone();
let value = try_with_context!(context, value.as_integer(context));
let new_value = AmlValue::Integer(value + 1);
try_with_context!(context, context.store(addend, new_value.clone()));
Expand All @@ -353,7 +353,7 @@ where
DebugVerbosity::AllScopes,
"DefDecrement",
super_name().map_with_context(|minuend, context| {
let value = try_with_context!(context, context.read_target(&minuend));
let value = try_with_context!(context, context.read_target(&minuend)).clone();
let value = try_with_context!(context, value.as_integer(context));
let new_value = AmlValue::Integer(value - 1);
try_with_context!(context, context.store(minuend, new_value.clone()));
Expand Down
184 changes: 3 additions & 181 deletions aml/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub(crate) mod misc;
pub(crate) mod name_object;
pub(crate) mod namespace;
pub(crate) mod opcode;
pub mod opregion;
pub(crate) mod parser;
pub mod pci_routing;
pub(crate) mod pkg_length;
Expand Down Expand Up @@ -387,186 +388,6 @@ impl AmlContext {
}
}

/// Read from an operation-region, performing only standard-sized reads (supported powers-of-2 only. If a field
/// is not one of these sizes, it may need to be masked, or multiple reads may need to be performed).
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 => {
/*
* First, we need to get some extra information out of objects in the parent object. Both
* `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
* (e.g. systems with a single segment group and a single root, respectively).
*/
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),
}
}

// TODO
_ => 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 => {
/*
* First, we need to get some extra information out of objects in the parent object. Both
* `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
* (e.g. systems with a single segment group and a single root, respectively).
*/
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),
}
}

// TODO
_ => unimplemented!(),
}
}

fn add_predefined_objects(&mut self) {
/*
* These are the scopes predefined by the spec. Some tables will try to access them without defining them
Expand Down Expand Up @@ -605,7 +426,8 @@ impl AmlContext {
.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() {
let value = context.current_arg(0)?.clone();
Ok(match value.as_string(context)?.as_str() {
"Windows 2000" => true, // 2000
"Windows 2001" => true, // XP
"Windows 2001 SP1" => true, // XP SP1
Expand Down