@@ -10,16 +10,19 @@ pub mod pci_routing;
10
10
pub mod resource;
11
11
12
12
use alloc:: {
13
+ boxed:: Box ,
14
+ collections:: btree_map:: BTreeMap ,
13
15
string:: { String , ToString } ,
14
16
sync:: Arc ,
15
17
vec,
16
18
vec:: Vec ,
17
19
} ;
18
20
use bit_field:: BitField ;
19
21
use core:: mem;
22
+ use log:: { info, trace} ;
20
23
use namespace:: { AmlName , Namespace , NamespaceLevelKind } ;
21
24
use object:: { FieldFlags , FieldUnit , FieldUnitKind , MethodFlags , Object , ObjectType , ReferenceKind } ;
22
- use op_region:: { OpRegion , RegionSpace } ;
25
+ use op_region:: { OpRegion , RegionHandler , RegionSpace } ;
23
26
use spinning_top:: Spinlock ;
24
27
25
28
pub struct Interpreter < H >
29
32
handler : H ,
30
33
pub namespace : Spinlock < Namespace > ,
31
34
context_stack : Spinlock < Vec < MethodContext > > ,
35
+ dsdt_revision : u8 ,
36
+ region_handlers : Spinlock < BTreeMap < RegionSpace , Box < dyn RegionHandler > > > ,
32
37
}
33
38
34
39
unsafe impl < H > Send for Interpreter < H > where H : Handler + Send { }
@@ -41,11 +46,15 @@ impl<H> Interpreter<H>
41
46
where
42
47
H : Handler ,
43
48
{
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" ) ) ;
45
51
Interpreter {
46
52
handler,
47
53
namespace : Spinlock :: new ( Namespace :: new ( ) ) ,
48
54
context_stack : Spinlock :: new ( Vec :: new ( ) ) ,
55
+ dsdt_revision,
56
+ ops_interpreted : AtomicUsize :: new ( 0 ) ,
57
+ region_handlers : Spinlock :: new ( BTreeMap :: new ( ) ) ,
49
58
}
50
59
}
51
60
73
82
}
74
83
}
75
84
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
+
76
94
fn do_execute_method ( & self , mut context : MethodContext ) -> Result < Arc < Object > , AmlError > {
77
95
/*
78
96
* TODO
@@ -1342,6 +1360,10 @@ where
1342
1360
let value = u64:: from_le_bytes ( buffer) ;
1343
1361
* target = value;
1344
1362
}
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
+ }
1345
1367
_ => panic ! ( "Store to integer from unsupported object: {:?}" , object) ,
1346
1368
} ,
1347
1369
Object :: BufferField { .. } => match object. gain_mut ( ) {
@@ -1366,6 +1388,131 @@ where
1366
1388
}
1367
1389
Ok ( ( ) )
1368
1390
}
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
+ }
1369
1516
}
1370
1517
1371
1518
/// A `MethodContext` represents a piece of running AML code - either a real method, or the
@@ -1904,6 +2051,7 @@ enum Opcode {
1904
2051
pub enum AmlError {
1905
2052
RunOutOfStream ,
1906
2053
IllegalOpcode ( u16 ) ,
2054
+ InvalidFieldFlags ,
1907
2055
1908
2056
InvalidName ( Option < AmlName > ) ,
1909
2057
@@ -2025,7 +2173,7 @@ mod tests {
2025
2173
2026
2174
#[ test]
2027
2175
fn add_op ( ) {
2028
- let interpreter = Interpreter :: new ( TestHandler ) ;
2176
+ let interpreter = Interpreter :: new ( TestHandler , 2 ) ;
2029
2177
// AddOp 0x0e 0x06 => Local2
2030
2178
interpreter. load_table ( & [ 0x72 , 0x0b , 0x0e , 0x00 , 0x0a , 0x06 , 0x62 ] ) . unwrap ( ) ;
2031
2179
// AddOp 0x0e (AddOp 0x01 0x03 => Local1) => Local1
0 commit comments