Skip to content

Commit 4bdd54b

Browse files
martin-hughesIsaacWoods
authored andcommitted
Support multiple tables per test in aml_tester
1 parent a7e015c commit 4bdd54b

File tree

5 files changed

+106
-7
lines changed

5 files changed

+106
-7
lines changed

src/sdt/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ impl<T: Copy, const MIN_REVISION: u8> ExtendedField<T, MIN_REVISION> {
105105
#[repr(C, packed)]
106106
pub struct SdtHeader {
107107
pub signature: Signature,
108+
// TODO: Make sure this and other fields are interpreted as little-endian on big-endian machine.
108109
pub length: u32,
109110
pub revision: u8,
110111
pub checksum: u8,

tests/multi_table.asl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* This file is a test for `aml_tester` rather than of the parser itself.
2+
* Can `aml_tester` cope with multiple tables?
3+
*/
4+
DefinitionBlock("", "DSDT", 1, "RSACPI", "BUFFLD", 1) {
5+
OperationRegion(MEM, SystemMemory, 0x40000, 0x1000)
6+
Field(MEM, ByteAcc, NoLock, Preserve) {
7+
A, 8
8+
}
9+
}
10+
DefinitionBlock("", "SSDT", 1, "RSACPI", "BUFFLD", 1) {
11+
OperationRegion(MEMB, SystemMemory, 0x50000, 0x1000)
12+
Field(MEMB, ByteAcc, NoLock, Preserve) {
13+
B, 8
14+
}
15+
}

tests/test_infra/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use acpi::Handler;
22
use aml_test_tools::{
33
RunTestResult,
4+
TestResult,
45
handlers::logging_handler::LoggingHandler,
56
new_interpreter,
67
run_test_for_string,
@@ -13,5 +14,6 @@ pub fn run_aml_test(asl: &'static str, handler: impl Handler) {
1314
let logged_handler = LoggingHandler::new(handler);
1415
let interpreter = new_interpreter(logged_handler);
1516

16-
assert!(matches!(run_test_for_string(asl, interpreter), RunTestResult::Pass(_)));
17+
let result = run_test_for_string(asl, interpreter);
18+
assert!(matches!(result, RunTestResult::Pass(_)), "Test failed with: {:?}", TestResult::from(&result));
1719
}

tools/aml_test_tools/src/lib.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
//! As always, feel free to offer PRs for improvements.
55
66
pub mod handlers;
7+
pub mod tables;
78

9+
use crate::tables::{TestAcpiTable, bytes_to_tables};
810
use acpi::{
911
Handler,
1012
PhysicalMapping,
1113
address::MappedGas,
1214
aml::{AmlError, Interpreter, namespace::AmlName, object::Object},
15+
sdt::Signature,
1316
};
1417
use log::{error, trace};
1518
use std::{
@@ -99,6 +102,8 @@ pub enum TestFailureReason {
99102
CompileFail,
100103
/// Some error occurred attempting to read or write the test file.
101104
FilesystemErr,
105+
/// There was a problem interpreting the basic structure of the tables in the AML file.
106+
TablesErr,
102107
/// Our interpreter failed to parse or execute the resulting AML.
103108
ParseFail(AmlError),
104109
}
@@ -278,10 +283,11 @@ where
278283
let mut contents = Vec::new();
279284
file.read_to_end(&mut contents).unwrap();
280285

281-
const AML_TABLE_HEADER_LENGTH: usize = 36;
282-
let stream = &contents[AML_TABLE_HEADER_LENGTH..];
286+
let Ok(tables) = bytes_to_tables(&contents) else {
287+
return RunTestResult::Failed(interpreter, TestFailureReason::TablesErr);
288+
};
283289

284-
run_test(stream, interpreter)
290+
run_test(tables, interpreter)
285291
}
286292

287293
/// Internal function to create a temporary script file from an ASL string, plus to calculate the
@@ -307,19 +313,32 @@ fn create_script_file(asl: &'static str) -> TempScriptFile {
307313
///
308314
/// Arguments:
309315
///
310-
/// * `stream`: A slice containing the AML bytecode to test.
316+
/// * `tables`: A Vec of tables to test. The DSDT will be loaded first, if found. Other tables will
317+
/// be loaded in the order they appear in the Vec.
311318
/// * `interpreter`: The interpreter to test with. The interpreter is consumed to maintain unwind
312319
/// safety - if the interpreter panics, the caller should not be able to see the interpreter in
313320
/// an inconsistent state.
314-
pub fn run_test<T>(stream: &[u8], interpreter: Interpreter<T>) -> RunTestResult<T>
321+
pub fn run_test<T>(tables: Vec<TestAcpiTable>, interpreter: Interpreter<T>) -> RunTestResult<T>
315322
where
316323
T: Handler,
317324
{
318325
// Without `AssertUnwindSafe`, the following code will not build as the Interpreter is not
319326
// unwind safe. To avoid the caller being able to see an inconsistent Interpreter, if a panic
320327
// occurs we drop the Interpreter, forcing the caller to create a new one.
321328
let result = catch_unwind(AssertUnwindSafe(|| -> Result<(), AmlError> {
322-
interpreter.load_table(stream)?;
329+
// Load the DSDT table first, if there is one.
330+
if let Some(dsdt) = tables.iter().find(|t| t.header().signature == Signature::DSDT) {
331+
trace!("Loading table: DSDT");
332+
interpreter.load_table(dsdt.content())?;
333+
}
334+
let others = tables.iter().filter(|t| t.header().signature != Signature::DSDT);
335+
336+
for t in others {
337+
trace!("Loading table: {:?}", t.header().signature);
338+
interpreter.load_table(t.content())?;
339+
}
340+
341+
trace!("All tables loaded");
323342

324343
if let Some(result) = interpreter.evaluate_if_present(AmlName::from_str("\\MAIN").unwrap(), vec![])? {
325344
match *result {

tools/aml_test_tools/src/tables.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! A basic interpreter to extract ACPI tables from byte-slices.
2+
3+
use acpi::sdt::SdtHeader;
4+
use std::mem::transmute;
5+
6+
const AML_TABLE_HEADER_LENGTH: usize = 36;
7+
8+
/// An ACPI table separated into header and content.
9+
///
10+
/// This is not provided in the main crate as it is unnecessary - but it is useful for testing.
11+
pub struct TestAcpiTable {
12+
header: SdtHeader,
13+
content: Vec<u8>,
14+
}
15+
16+
impl TryFrom<&[u8]> for TestAcpiTable {
17+
type Error = &'static str;
18+
19+
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
20+
if bytes.len() < AML_TABLE_HEADER_LENGTH {
21+
return Err("Buffer shorter than table header");
22+
}
23+
24+
let mut header_bytes: [u8; AML_TABLE_HEADER_LENGTH] = [0; AML_TABLE_HEADER_LENGTH];
25+
header_bytes.copy_from_slice(&bytes[..AML_TABLE_HEADER_LENGTH]);
26+
let header: SdtHeader = unsafe { transmute(header_bytes) };
27+
28+
if header.length < AML_TABLE_HEADER_LENGTH as u32 {
29+
return Err("AML table header reported length too short");
30+
}
31+
32+
let content = bytes[AML_TABLE_HEADER_LENGTH..header.length as usize].to_vec();
33+
34+
Ok(Self { header, content })
35+
}
36+
}
37+
38+
/// Provide accessor functions for the two fields - don't provide write access so that `content`
39+
/// and `header` can't get out of sync with each other.
40+
impl TestAcpiTable {
41+
pub fn content(&self) -> &[u8] {
42+
&self.content
43+
}
44+
45+
pub fn header(&self) -> &SdtHeader {
46+
&self.header
47+
}
48+
}
49+
50+
/// Construct a Vec of AML tables from a slice of bytes.
51+
pub fn bytes_to_tables(bytes: &[u8]) -> Result<Vec<TestAcpiTable>, &'static str> {
52+
let mut tables = Vec::new();
53+
let mut offset = 0;
54+
55+
while offset < bytes.len() {
56+
let table = TestAcpiTable::try_from(&bytes[offset..])?;
57+
offset += table.header.length as usize;
58+
tables.push(table);
59+
}
60+
61+
Ok(tables)
62+
}

0 commit comments

Comments
 (0)