@@ -10,16 +10,19 @@ pub mod pci_routing;
1010pub mod resource;
1111
1212use alloc:: {
13+ boxed:: Box ,
14+ collections:: btree_map:: BTreeMap ,
1315 string:: { String , ToString } ,
1416 sync:: Arc ,
1517 vec,
1618 vec:: Vec ,
1719} ;
1820use bit_field:: BitField ;
1921use core:: mem;
22+ use log:: { info, trace} ;
2023use namespace:: { AmlName , Namespace , NamespaceLevelKind } ;
2124use object:: { FieldFlags , FieldUnit , FieldUnitKind , MethodFlags , Object , ObjectType , ReferenceKind } ;
22- use op_region:: { OpRegion , RegionSpace } ;
25+ use op_region:: { OpRegion , RegionHandler , RegionSpace } ;
2326use spinning_top:: Spinlock ;
2427
2528pub struct Interpreter < H >
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
3439unsafe impl < H > Send for Interpreter < H > where H : Handler + Send { }
@@ -41,11 +46,15 @@ impl<H> Interpreter<H>
4146where
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
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 {
19042051pub 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
0 commit comments