Skip to content

Commit b6b78ca

Browse files
committed
aml: lay groundwork for field reads + support system memory + IO reads
1 parent 55989b3 commit b6b78ca

File tree

6 files changed

+327
-56
lines changed

6 files changed

+327
-56
lines changed

aml/src/lib.rs

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ pub mod pci_routing;
1010
pub mod resource;
1111

1212
use alloc::{
13+
boxed::Box,
14+
collections::btree_map::BTreeMap,
1315
string::{String, ToString},
1416
sync::Arc,
1517
vec,
1618
vec::Vec,
1719
};
1820
use bit_field::BitField;
1921
use core::mem;
22+
use log::{info, trace};
2023
use namespace::{AmlName, Namespace, NamespaceLevelKind};
2124
use object::{FieldFlags, FieldUnit, FieldUnitKind, MethodFlags, Object, ObjectType, ReferenceKind};
22-
use op_region::{OpRegion, RegionSpace};
25+
use op_region::{OpRegion, RegionHandler, RegionSpace};
2326
use spinning_top::Spinlock;
2427

2528
pub struct Interpreter<H>
@@ -29,6 +32,8 @@ where
2932
handler: H,
3033
pub namespace: Spinlock<Namespace>,
3134
context_stack: Spinlock<Vec<MethodContext>>,
35+
dsdt_revision: u8,
36+
region_handlers: Spinlock<BTreeMap<RegionSpace, Box<dyn RegionHandler>>>,
3237
}
3338

3439
unsafe impl<H> Send for Interpreter<H> where H: Handler + Send {}
@@ -41,11 +46,15 @@ impl<H> Interpreter<H>
4146
where
4247
H: Handler,
4348
{
44-
pub fn new(handler: H) -> Interpreter<H> {
49+
pub fn new(handler: H, dsdt_revision: u8) -> Interpreter<H> {
50+
info!("Initializing AML interpreter v{}", env!("CARGO_PKG_VERSION"));
4551
Interpreter {
4652
handler,
4753
namespace: Spinlock::new(Namespace::new()),
4854
context_stack: Spinlock::new(Vec::new()),
55+
dsdt_revision,
56+
ops_interpreted: AtomicUsize::new(0),
57+
region_handlers: Spinlock::new(BTreeMap::new()),
4958
}
5059
}
5160

@@ -73,6 +82,15 @@ where
7382
}
7483
}
7584

85+
pub fn install_region_handler<RH>(&self, space: RegionSpace, handler: RH)
86+
where
87+
RH: RegionHandler + 'static,
88+
{
89+
let mut handlers = self.region_handlers.lock();
90+
assert!(handlers.get(&space).is_none(), "Tried to install handler for same space twice!");
91+
handlers.insert(space, Box::new(handler));
92+
}
93+
7694
fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
7795
/*
7896
* TODO
@@ -1342,6 +1360,10 @@ where
13421360
let value = u64::from_le_bytes(buffer);
13431361
*target = value;
13441362
}
1363+
Object::FieldUnit(field) => {
1364+
// TODO: not sure if we should convert buffers to integers if needed here?
1365+
*target = self.do_field_read(field)?.as_integer()?;
1366+
}
13451367
_ => panic!("Store to integer from unsupported object: {:?}", object),
13461368
},
13471369
Object::BufferField { .. } => match object.gain_mut() {
@@ -1366,6 +1388,131 @@ where
13661388
}
13671389
Ok(())
13681390
}
1391+
1392+
/// Do a read from a field by performing one or more well-formed accesses to the underlying
1393+
/// operation regions, and then shifting and masking the resulting value as appropriate. Will
1394+
/// return either an `Integer` or `Buffer` as appropriate, guided by the size of the field
1395+
/// and expected integer size (as per the DSDT revision).
1396+
fn do_field_read(&self, field: &FieldUnit) -> Result<Arc<Object>, AmlError> {
1397+
let needs_buffer = if self.dsdt_revision >= 2 { field.bit_length > 64 } else { field.bit_length > 32 };
1398+
let access_width_bits = field.flags.access_type_bytes()? * 8;
1399+
1400+
trace!("AML field read. Field = {:?}", field);
1401+
1402+
// TODO: if the field needs to be locked, acquire/release a global mutex?
1403+
1404+
enum Output {
1405+
Integer([u8; 8]),
1406+
Buffer(Vec<u8>),
1407+
}
1408+
let mut output = if needs_buffer {
1409+
Output::Buffer(vec![0; field.bit_length.next_multiple_of(8)])
1410+
} else {
1411+
Output::Integer([0; 8])
1412+
};
1413+
let output_bytes = match &mut output {
1414+
Output::Buffer(bytes) => bytes.as_mut_slice(),
1415+
Output::Integer(value) => value,
1416+
};
1417+
1418+
match field.kind {
1419+
FieldUnitKind::Normal { ref region } => {
1420+
let Object::OpRegion(ref region) = **region else { panic!() };
1421+
1422+
/*
1423+
* TODO: it might be worth having a fast path here for reads that don't do weird
1424+
* unaligned accesses, which I'm guessing might be relatively common on real
1425+
* hardware? Eg. single native read + mask
1426+
*/
1427+
1428+
/*
1429+
* Break the field read into native reads that respect the region's access width.
1430+
* Copy each potentially-unaligned part into the destination's bit range.
1431+
*/
1432+
let native_accesses_needed = (field.bit_length + (field.bit_index % access_width_bits))
1433+
.next_multiple_of(access_width_bits)
1434+
/ access_width_bits;
1435+
let mut read_so_far = 0;
1436+
for i in 0..native_accesses_needed {
1437+
let aligned_offset =
1438+
object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
1439+
let raw = self.do_native_region_read(region, aligned_offset / 8, access_width_bits / 8)?;
1440+
let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
1441+
let remaining_length = field.bit_length - read_so_far;
1442+
let length = if i == 0 {
1443+
usize::min(remaining_length, access_width_bits - (field.bit_index % access_width_bits))
1444+
} else {
1445+
usize::min(remaining_length, access_width_bits)
1446+
};
1447+
1448+
trace!(
1449+
"Extracting bits {}..{} from native read to bits {}..{}",
1450+
src_index,
1451+
src_index + length,
1452+
read_so_far,
1453+
read_so_far + length,
1454+
);
1455+
object::copy_bits(&raw.to_le_bytes(), src_index, output_bytes, read_so_far, length);
1456+
1457+
read_so_far += length;
1458+
}
1459+
1460+
match output {
1461+
Output::Buffer(bytes) => Ok(Arc::new(Object::Buffer(bytes))),
1462+
Output::Integer(value) => Ok(Arc::new(Object::Integer(u64::from_le_bytes(value)))),
1463+
}
1464+
}
1465+
FieldUnitKind::Bank { ref region, ref bank, bank_value } => todo!(),
1466+
FieldUnitKind::Index { ref index, ref data } => todo!(),
1467+
}
1468+
}
1469+
1470+
/// Performs an actual read from an operation region. `offset` and `length` must respect the
1471+
/// access requirements of the field being read, and are supplied in **bytes**. This may call
1472+
/// AML methods if required, and may invoke user-supplied handlers.
1473+
fn do_native_region_read(&self, region: &OpRegion, offset: usize, length: usize) -> Result<u64, AmlError> {
1474+
trace!("Native field read. Region = {:?}, offset = {:#x}, length={:#x}", region, offset, length);
1475+
1476+
match region.space {
1477+
RegionSpace::SystemMemory => Ok({
1478+
let address = region.base as usize + offset;
1479+
match length {
1480+
1 => self.handler.read_u8(address) as u64,
1481+
2 => self.handler.read_u16(address) as u64,
1482+
4 => self.handler.read_u32(address) as u64,
1483+
8 => self.handler.read_u64(address) as u64,
1484+
_ => panic!(),
1485+
}
1486+
}),
1487+
RegionSpace::SystemIO => Ok({
1488+
let address = region.base as u16 + offset as u16;
1489+
match length {
1490+
1 => self.handler.read_io_u8(address) as u64,
1491+
2 => self.handler.read_io_u16(address) as u64,
1492+
4 => self.handler.read_io_u32(address) as u64,
1493+
_ => panic!(),
1494+
}
1495+
}),
1496+
RegionSpace::PciConfig => todo!(),
1497+
1498+
RegionSpace::EmbeddedControl
1499+
| RegionSpace::SmBus
1500+
| RegionSpace::SystemCmos
1501+
| RegionSpace::PciBarTarget
1502+
| RegionSpace::Ipmi
1503+
| RegionSpace::GeneralPurposeIo
1504+
| RegionSpace::GenericSerialBus
1505+
| RegionSpace::Pcc
1506+
| RegionSpace::Oem(_) => {
1507+
if let Some(handler) = self.region_handlers.lock().get(&region.space) {
1508+
todo!("Utilise handler");
1509+
} else {
1510+
// TODO: panic or normal error here??
1511+
panic!("Unhandled region space that needs handler!");
1512+
}
1513+
}
1514+
}
1515+
}
13691516
}
13701517

13711518
/// A `MethodContext` represents a piece of running AML code - either a real method, or the
@@ -1904,6 +2051,7 @@ enum Opcode {
19042051
pub enum AmlError {
19052052
RunOutOfStream,
19062053
IllegalOpcode(u16),
2054+
InvalidFieldFlags,
19072055

19082056
InvalidName(Option<AmlName>),
19092057

@@ -2025,7 +2173,7 @@ mod tests {
20252173

20262174
#[test]
20272175
fn add_op() {
2028-
let interpreter = Interpreter::new(TestHandler);
2176+
let interpreter = Interpreter::new(TestHandler, 2);
20292177
// AddOp 0x0e 0x06 => Local2
20302178
interpreter.load_table(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
20312179
// AddOp 0x0e (AddOp 0x01 0x03 => Local1) => Local1

aml/src/object.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,64 @@ pub enum FieldUnitKind {
157157
#[derive(Clone, Copy, Debug)]
158158
pub struct FieldFlags(pub u8);
159159

160+
#[derive(Clone, Copy, Debug)]
161+
pub enum FieldAccessType {
162+
Any,
163+
Byte,
164+
Word,
165+
DWord,
166+
QWord,
167+
Buffer,
168+
}
169+
170+
#[derive(Clone, Copy, Debug)]
171+
pub enum FieldUpdateRule {
172+
Preserve,
173+
WriteAsOnes,
174+
WriteAsZeros,
175+
}
176+
177+
impl FieldFlags {
178+
pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {
179+
match self.0.get_bits(0..4) {
180+
0 => Ok(FieldAccessType::Any),
181+
1 => Ok(FieldAccessType::Byte),
182+
2 => Ok(FieldAccessType::Word),
183+
3 => Ok(FieldAccessType::DWord),
184+
4 => Ok(FieldAccessType::QWord),
185+
5 => Ok(FieldAccessType::Buffer),
186+
_ => Err(AmlError::InvalidFieldFlags),
187+
}
188+
}
189+
190+
pub fn access_type_bytes(&self) -> Result<usize, AmlError> {
191+
match self.access_type()? {
192+
FieldAccessType::Any => {
193+
// TODO: given more info about the field, we might be able to make a more efficient
194+
// read, since all are valid in this case
195+
Ok(1)
196+
}
197+
FieldAccessType::Byte | FieldAccessType::Buffer => Ok(1),
198+
FieldAccessType::Word => Ok(2),
199+
FieldAccessType::DWord => Ok(4),
200+
FieldAccessType::QWord => Ok(8),
201+
}
202+
}
203+
204+
pub fn lock_rule(&self) -> bool {
205+
self.0.get_bit(4)
206+
}
207+
208+
pub fn update_rule(&self) -> FieldUpdateRule {
209+
match self.0.get_bits(5..7) {
210+
0 => FieldUpdateRule::Preserve,
211+
1 => FieldUpdateRule::WriteAsOnes,
212+
2 => FieldUpdateRule::WriteAsZeros,
213+
_ => unreachable!(),
214+
}
215+
}
216+
}
217+
160218
#[derive(Clone, Copy, Debug)]
161219
pub struct MethodFlags(pub u8);
162220

@@ -206,7 +264,13 @@ pub enum ObjectType {
206264
/// Copy an arbitrary bit range of `src` to an arbitrary bit range of `dst`. This is used for
207265
/// buffer fields. Data is zero-extended if `src` does not cover `length` bits, matching the
208266
/// expected behaviour for buffer fields.
209-
fn copy_bits(src: &[u8], mut src_index: usize, dst: &mut [u8], mut dst_index: usize, mut length: usize) {
267+
pub(crate) fn copy_bits(
268+
src: &[u8],
269+
mut src_index: usize,
270+
dst: &mut [u8],
271+
mut dst_index: usize,
272+
mut length: usize,
273+
) {
210274
while length > 0 {
211275
let src_shift = src_index & 7;
212276
let mut src_bits = src.get(src_index / 8).unwrap_or(&0x00) >> src_shift;
@@ -239,6 +303,26 @@ fn copy_bits(src: &[u8], mut src_index: usize, dst: &mut [u8], mut dst_index: us
239303
}
240304
}
241305

306+
#[inline]
307+
pub(crate) fn align_down(value: usize, align: usize) -> usize {
308+
assert!(align == 0 || align.is_power_of_two());
309+
310+
if align == 0 {
311+
value
312+
} else {
313+
/*
314+
* Alignment must be a power of two.
315+
*
316+
* E.g.
317+
* align = 0b00001000
318+
* align-1 = 0b00000111
319+
* !(align-1) = 0b11111000
320+
* ^^^ Masks the value to the one below it with the correct align
321+
*/
322+
value & !(align - 1)
323+
}
324+
}
325+
242326
#[cfg(test)]
243327
mod tests {
244328
use super::*;

aml/src/op_region.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::AmlError;
2+
13
#[derive(Debug)]
24
pub struct OpRegion {
35
pub space: RegionSpace,
@@ -6,7 +8,12 @@ pub struct OpRegion {
68
// parent_device
79
}
810

9-
#[derive(Clone, Copy, PartialEq, Debug)]
11+
pub trait RegionHandler {
12+
fn read(&self, region: &OpRegion) -> Result<(), AmlError>;
13+
fn write(&self, region: &OpRegion) -> Result<(), AmlError>;
14+
}
15+
16+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
1017
pub enum RegionSpace {
1118
SystemMemory,
1219
SystemIO,

tests/fields.asl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
DefinitionBlock("fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
2+
OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)
3+
Field(MEM, WordAcc, NoLock, Preserve) {
4+
, 7,
5+
A, 15,
6+
, 8,
7+
B, 1,
8+
, 1,
9+
C, 35
10+
}
11+
212
OperationRegion(GIO0, SystemIO, 0x125, 0x100)
313

414
Field(GIO0, ByteAcc, NoLock, Preserve) {
@@ -31,4 +41,13 @@ DefinitionBlock("fields.aml", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
3141
, 7,
3242
FET4, 1
3343
}
44+
45+
Name(RESA, 0)
46+
Name(RESB, 0)
47+
Name(RESC, 0)
48+
Method(MAIN, 0, NotSerialized) {
49+
RESA = A
50+
RESB = B
51+
RESC = C
52+
}
3453
}

tools/aml_tester/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ authors = ["Isaac Woods"]
55
edition = "2018"
66

77
[dependencies]
8+
aml = { path = "../../aml" }
89
clap = "4"
910
termion = "1"
1011
log = "0.4"
11-
aml = { path = "../../aml" }

0 commit comments

Comments
 (0)