Skip to content

Commit 6e01602

Browse files
martin-hughesIsaacWoods
authored andcommitted
Implement Index and Bank fields
1 parent 4bdd54b commit 6e01602

File tree

3 files changed

+450
-22
lines changed

3 files changed

+450
-22
lines changed

src/aml/mod.rs

Lines changed: 96 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,33 @@ where
13741374
let (_, data) = namespace.search(&data_name, &context.current_scope)?;
13751375
(index, data)
13761376
};
1377+
1378+
if let Object::FieldUnit(ref data_fu) = *data {
1379+
if data_fu.flags.access_type_bytes()? < FieldFlags(field_flags).access_type_bytes()? {
1380+
// On ACPICA this causes reads/writes to be truncated to the width of
1381+
// the data register.
1382+
//
1383+
// The issue comes from this part of the spec:
1384+
// "The value written to the IndexName register is defined to be a byte
1385+
// offset that is aligned on an AccessType boundary."
1386+
//
1387+
// Consider the case where the index field is WordAcc but the data
1388+
// register is ByteAcc. Only even numbers could be written to the index
1389+
// register - multiples of width of word (2 bytes). But to access the
1390+
// high byte of the word for the field, we'd need to write an odd number
1391+
// to the index register. Which is not compatible with the spec.
1392+
//
1393+
// The ASL writer shouldn't have allowed this. And it seems that most
1394+
// uses of IndexField are ByteAcc all around. So whatever strange
1395+
// behaviour we allow is probably OK.
1396+
//
1397+
// But warn the user, just in case.
1398+
warn!("Data field access width is smaller than normal field width at {:?}", start_pc);
1399+
}
1400+
} else {
1401+
warn!("Wrong data field type in IndexField: {:?}", data);
1402+
};
1403+
13771404
let kind = FieldUnitKind::Index { index, data };
13781405
self.parse_field_list(&mut context, kind, start_pc, pkg_length, field_flags)?;
13791406
}
@@ -2376,17 +2403,19 @@ where
23762403
Output::Integer(value) => value,
23772404
};
23782405

2379-
let read_region = match field.kind {
2380-
FieldUnitKind::Normal { ref region } => region,
2381-
// FieldUnitKind::Bank { ref region, ref bank, bank_value } => {
2382-
FieldUnitKind::Bank { .. } => {
2383-
// TODO: put the bank_value in the bank
2384-
todo!();
2406+
let (read_region, index_field_idx) = match field.kind {
2407+
FieldUnitKind::Normal { ref region } => (region, 0),
2408+
FieldUnitKind::Bank { ref region, ref bank, bank_value } => {
2409+
let Object::FieldUnit(ref bank) = **bank else { panic!() };
2410+
assert!(matches!(bank.kind, FieldUnitKind::Normal { .. }));
2411+
self.do_field_write(bank, Object::Integer(bank_value).wrap())?;
2412+
(region, 0)
23852413
}
2386-
// FieldUnitKind::Index { ref index, ref data } => {
2387-
FieldUnitKind::Index { .. } => {
2388-
// TODO: configure the correct index
2389-
todo!();
2414+
FieldUnitKind::Index { index: _, ref data } => {
2415+
let Object::FieldUnit(ref data) = **data else { panic!() };
2416+
let FieldUnitKind::Normal { region } = &data.kind else { panic!() };
2417+
let reg_idx = field.bit_index / 8;
2418+
(region, reg_idx)
23902419
}
23912420
};
23922421
let Object::OpRegion(ref read_region) = **read_region else { panic!() };
@@ -2406,7 +2435,27 @@ where
24062435
/ access_width_bits;
24072436
let mut read_so_far = 0;
24082437
for i in 0..native_accesses_needed {
2409-
let aligned_offset = object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
2438+
// Advance the read pointer. For Index fields, this also means updating the Index
2439+
// register.
2440+
let aligned_offset = match field.kind {
2441+
FieldUnitKind::Normal { .. } | FieldUnitKind::Bank { .. } => {
2442+
object::align_down(field.bit_index + i * access_width_bits, access_width_bits)
2443+
}
2444+
FieldUnitKind::Index { ref index, ref data } => {
2445+
// Update index register
2446+
let Object::FieldUnit(ref index) = **index else { panic!() };
2447+
let Object::FieldUnit(ref data) = **data else { panic!() };
2448+
self.do_field_write(
2449+
index,
2450+
Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(),
2451+
)?;
2452+
2453+
// The offset is always that of the data register, as we always read from the
2454+
// base of the data register.
2455+
data.bit_index
2456+
}
2457+
};
2458+
24102459
let raw = self.do_native_region_read(read_region, aligned_offset / 8, access_width_bits / 8)?;
24112460
let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
24122461
let remaining_length = field.bit_length - read_so_far;
@@ -2436,17 +2485,23 @@ where
24362485
};
24372486
let access_width_bits = field.flags.access_type_bytes()? * 8;
24382487

2439-
let write_region = match field.kind {
2440-
FieldUnitKind::Normal { ref region } => region,
2441-
// FieldUnitKind::Bank { ref region, ref bank, bank_value } => {
2442-
FieldUnitKind::Bank { .. } => {
2443-
// TODO: put the bank_value in the bank
2444-
todo!();
2488+
// In this tuple:
2489+
// - write_region is the region that the data will be written to.
2490+
// - index_field_idx is the initial index to write into the Index register of an index
2491+
// field. For all other field types it is unused and set to zero.
2492+
let (write_region, index_field_idx) = match field.kind {
2493+
FieldUnitKind::Normal { ref region } => (region, 0),
2494+
FieldUnitKind::Bank { ref region, ref bank, bank_value } => {
2495+
let Object::FieldUnit(ref bank) = **bank else { panic!() };
2496+
assert!(matches!(bank.kind, FieldUnitKind::Normal { .. }));
2497+
self.do_field_write(bank, Object::Integer(bank_value).wrap())?;
2498+
(region, 0)
24452499
}
2446-
// FieldUnitKind::Index { ref index, ref data } => {
2447-
FieldUnitKind::Index { .. } => {
2448-
// TODO: configure the correct index
2449-
todo!();
2500+
FieldUnitKind::Index { index: _, ref data } => {
2501+
let Object::FieldUnit(ref data) = **data else { panic!() };
2502+
let FieldUnitKind::Normal { region: data_region } = &data.kind else { panic!() };
2503+
let reg_idx = field.bit_index / 8;
2504+
(data_region, reg_idx)
24502505
}
24512506
};
24522507
let Object::OpRegion(ref write_region) = **write_region else { panic!() };
@@ -2461,7 +2516,26 @@ where
24612516
let mut written_so_far = 0;
24622517

24632518
for i in 0..native_accesses_needed {
2464-
let aligned_offset = object::align_down(field.bit_index + i * access_width_bits, access_width_bits);
2519+
// Advance the write pointer... For normal and bank fields this is straightforward. For
2520+
// Index fields, this involves updating the index register.
2521+
let aligned_offset = match field.kind {
2522+
FieldUnitKind::Normal { .. } | FieldUnitKind::Bank { .. } => {
2523+
object::align_down(field.bit_index + i * access_width_bits, access_width_bits)
2524+
}
2525+
FieldUnitKind::Index { ref index, ref data } => {
2526+
// Update index register
2527+
let Object::FieldUnit(ref index) = **index else { panic!() };
2528+
let Object::FieldUnit(ref data) = **data else { panic!() };
2529+
self.do_field_write(
2530+
index,
2531+
Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(),
2532+
)?;
2533+
2534+
// The offset is always that of the data register, as we always read from the
2535+
// base of the data register.
2536+
data.bit_index
2537+
}
2538+
};
24652539
let dst_index = if i == 0 { field.bit_index % access_width_bits } else { 0 };
24662540

24672541
/*

tests/bank_fields.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Test operations on "Bank" fields
2+
3+
use aml_test_tools::handlers::std_test_handler::{
4+
Command,
5+
construct_std_handler,
6+
create_mutex,
7+
read_u16,
8+
write_u8,
9+
write_u16,
10+
};
11+
12+
mod test_infra;
13+
14+
#[test]
15+
fn test_basic_bank_store_and_load() {
16+
// This is a straightforward test of banked fields.
17+
// Internally: Apart from setting the bank index beforehand, the field read/write is identical
18+
// to normal fields. So this test is probably sufficient testing of banked fields.
19+
const AML: &str = r#"DefinitionBlock("", "DSDT", 1, "RSACPI", "BNKFLD", 1) {
20+
OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)
21+
Field(MEM, ByteAcc, NoLock, Preserve) {
22+
INDX, 8
23+
}
24+
25+
BankField (MEM, INDX, 0, WordAcc, NoLock, Preserve) {
26+
OFFSET (0x02), // Prevent aliasing INDX
27+
A, 16,
28+
B, 16,
29+
C, 16
30+
}
31+
32+
BankField (MEM, INDX, 1, WordAcc, NoLock, Preserve) {
33+
OFFSET (0x02),
34+
D, 16,
35+
E, 16,
36+
F, 16
37+
}
38+
39+
Method(MAIN, 0, NotSerialized) {
40+
C = 0xA55A
41+
D = C
42+
E = 0x5AA5
43+
A = E
44+
Return (0)
45+
}
46+
}
47+
"#;
48+
49+
const EXPECTED_COMMANDS: &[Command] = &[
50+
create_mutex(),
51+
// C = 0xA55A
52+
write_u8(0x40000, 0), // Select bank 0.
53+
write_u16(0x40006, 0xA55A), // Write the value to C.
54+
// D = C
55+
write_u8(0x40000, 0), // Select bank 0.
56+
read_u16(0x40006, 0xA55A), // Read the value from C.
57+
write_u8(0x40000, 1), // Select bank 1.
58+
write_u16(0x40002, 0xA55A), // Write the value to D.
59+
// E = 0x5AA5
60+
write_u8(0x40000, 1), // Select bank 1.
61+
write_u16(0x40004, 0x5AA5), // Write the value to E.
62+
// A = E
63+
write_u8(0x40000, 1), // Select bank 1.
64+
read_u16(0x40004, 0x5AA5), // Read from E
65+
write_u8(0x40000, 0), // Select bank 0.
66+
write_u16(0x40002, 0x5AA5), // Write the value to A.
67+
];
68+
69+
let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec());
70+
test_infra::run_aml_test(AML, handler);
71+
}

0 commit comments

Comments
 (0)