Skip to content

Commit 14ea326

Browse files
committed
Allow BufferField -> Integer conversion
Both explicit and some implicit conversions are supported.
1 parent 6e01602 commit 14ea326

4 files changed

Lines changed: 85 additions & 59 deletions

File tree

src/aml/mod.rs

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -807,17 +807,19 @@ where
807807
}
808808
Opcode::DerefOf => {
809809
let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
810-
let result = if object.typ() == ObjectType::Reference {
811-
object.clone().unwrap_reference()
812-
} else if object.typ() == ObjectType::String {
813-
let path = AmlName::from_str(&object.as_string().unwrap())?;
814-
let (_, object) = self.namespace.lock().search(&path, &context.current_scope)?;
815-
object.clone()
816-
} else {
817-
return Err(AmlError::ObjectNotOfExpectedType {
818-
expected: ObjectType::Reference,
819-
got: object.typ(),
820-
});
810+
let result = match **object {
811+
Object::Reference { kind: _, inner: _ } => object.clone().unwrap_reference(),
812+
Object::String(_) => {
813+
let path = AmlName::from_str(&object.as_string().unwrap())?;
814+
let (_, object) = self.namespace.lock().search(&path, &context.current_scope)?;
815+
object.clone()
816+
}
817+
_ => {
818+
return Err(AmlError::ObjectNotOfExpectedType {
819+
expected: ObjectType::Reference,
820+
got: object.typ(),
821+
});
822+
}
821823
};
822824
context.contribute_arg(Argument::Object(result));
823825
context.retire_op(op);
@@ -1796,8 +1798,9 @@ where
17961798
let [Argument::Object(left), Argument::Object(right), target] = &op.arguments[0..3] else { panic!() };
17971799
let target2 = if op.op == Opcode::Divide { Some(&op.arguments[3]) } else { None };
17981800

1799-
let left = left.clone().unwrap_transparent_reference().as_integer()?;
1800-
let right = right.clone().unwrap_transparent_reference().as_integer()?;
1801+
let allowed_length = if self.dsdt_revision >= 2 { 8 } else { 4 };
1802+
let left = left.clone().unwrap_transparent_reference().to_integer(allowed_length)?;
1803+
let right = right.clone().unwrap_transparent_reference().to_integer(allowed_length)?;
18011804

18021805
let result = match op.op {
18031806
Opcode::Add => left.wrapping_add(right),
@@ -1981,43 +1984,7 @@ where
19811984
let [Argument::Object(operand), target] = &op.arguments[..] else { panic!() };
19821985
let operand = operand.clone().unwrap_transparent_reference();
19831986

1984-
let result = match *operand {
1985-
Object::Integer(value) => Object::Integer(value),
1986-
Object::Buffer(ref bytes) => {
1987-
/*
1988-
* The spec says this should respect the revision of the current definition block.
1989-
* Apparently, the NT interpreter always uses the first 8 bytes of the buffer.
1990-
*/
1991-
let mut to_interpret = [0u8; 8];
1992-
(to_interpret[0..usize::min(bytes.len(), 8)]).copy_from_slice(bytes);
1993-
Object::Integer(u64::from_le_bytes(to_interpret))
1994-
}
1995-
Object::String(ref value) => {
1996-
/*
1997-
* This is about the same level of effort as ACPICA puts in. The uACPI test suite
1998-
* has tests that this fails - namely because of support for octal, signs, strings
1999-
* that won't fit in a `u64` etc. We probably need to write a more robust parser
2000-
* 'real' parser to handle those cases.
2001-
*/
2002-
let value = value.trim();
2003-
let value = value.to_ascii_lowercase();
2004-
let (value, radix): (&str, u32) = match value.strip_prefix("0x") {
2005-
Some(value) => {
2006-
(value.split(|c: char| !c.is_ascii_hexdigit()).next().unwrap_or(""), 16)
2007-
}
2008-
None => (value.split(|c: char| !c.is_ascii_digit()).next().unwrap_or(""), 10),
2009-
};
2010-
match value.len() {
2011-
0 => Object::Integer(0),
2012-
_ => Object::Integer(u64::from_str_radix(value, radix).map_err(|_| {
2013-
AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: ObjectType::String }
2014-
})?),
2015-
}
2016-
}
2017-
_ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToBuffer, typ: operand.typ() })?,
2018-
}
2019-
.wrap();
2020-
1987+
let result = Object::Integer(operand.to_integer(if self.dsdt_revision >= 2 { 8 } else { 4 })?).wrap();
20211988
let result = self.do_store(target, result)?;
20221989
context.contribute_arg(Argument::Object(result));
20231990
context.retire_op(op);

src/aml/object.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ impl Object {
177177
WrappedObject::new(self)
178178
}
179179

180+
/// Unwraps an integer object. Errors if not already an integer.
181+
///
182+
/// For casting to integer, use [`Object::to_integer`] instead.
180183
pub fn as_integer(&self) -> Result<u64, AmlError> {
181184
if let Object::Integer(value) = self {
182185
Ok(*value)
@@ -201,18 +204,48 @@ impl Object {
201204
}
202205
}
203206

207+
/// Converts the object to an integer. Used for both implicit and explicit conversions.
208+
///
209+
/// To avoid the cast, use [`Object::as_integer`] instead.
204210
pub fn to_integer(&self, allowed_bytes: usize) -> Result<u64, AmlError> {
205211
match self {
206212
Object::Integer(value) => Ok(*value),
207-
Object::Buffer(value) => {
208-
let length = usize::min(value.len(), allowed_bytes);
209-
let mut bytes = [0u8; 8];
210-
bytes[0..length].copy_from_slice(&value[0..length]);
211-
Ok(u64::from_le_bytes(bytes))
213+
Object::Buffer(bytes) => {
214+
/*
215+
* The spec says this should respect the revision of the current definition block.
216+
* Apparently, the NT interpreter always uses the first 8 bytes of the buffer.
217+
*/
218+
let length = usize::min(bytes.len(), allowed_bytes);
219+
let mut to_interpret = [0u8; 8];
220+
to_interpret[0..length].copy_from_slice(bytes);
221+
Ok(u64::from_le_bytes(to_interpret))
222+
}
223+
Object::String(value) => {
224+
/*
225+
* This is about the same level of effort as ACPICA puts in. The uACPI test suite
226+
* has tests that this fails - namely because of support for octal, signs, strings
227+
* that won't fit in a `u64` etc. We probably need to write a more robust parser
228+
* 'real' parser to handle those cases.
229+
*/
230+
let value = value.trim();
231+
let value = value.to_ascii_lowercase();
232+
let (value, radix): (&str, u32) = match value.strip_prefix("0x") {
233+
Some(value) => (value.split(|c: char| !c.is_ascii_hexdigit()).next().unwrap_or(""), 16),
234+
None => (value.split(|c: char| !c.is_ascii_digit()).next().unwrap_or(""), 10),
235+
};
236+
match value.len() {
237+
0 => Ok(0),
238+
_ => Ok(u64::from_str_radix(value, radix).map_err(|_| {
239+
AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: ObjectType::String }
240+
})?),
241+
}
242+
}
243+
Object::BufferField { .. } => {
244+
let mut buffer = [0u8; 8];
245+
self.read_buffer_field(&mut buffer)?;
246+
Ok(u64::from_le_bytes(buffer))
212247
}
213-
// TODO: how should we handle invalid inputs? What does NT do here?
214-
Object::String(value) => Ok(value.parse::<u64>().unwrap_or(0)),
215-
_ => Ok(0),
248+
_ => Err(AmlError::InvalidOperationOnObject { op: Operation::ToInteger, typ: self.typ() })?,
216249
}
217250
}
218251

src/aml/resource.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,6 @@ fn extended_interrupt_descriptor(bytes: &[u8]) -> Result<Resource, AmlError> {
551551
#[cfg(test)]
552552
mod tests {
553553
use super::*;
554-
use alloc::sync::Arc;
555554

556555
#[test]
557556
fn test_parses_keyboard_crs() {

tests/de_ref_of.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use aml_test_tools::handlers::null_handler::NullHandler;
2+
3+
mod test_infra;
4+
5+
#[test]
6+
fn test_deref_of_buffer_field() {
7+
const AML: &str = r#"
8+
DefinitionBlock ("", "SSDT", 2, "RSACPI", "DerefOf", 0x00000002) {
9+
Scope (\_SB) {
10+
Name (ADAT, Buffer (0x0010) {
11+
/* 0000 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12+
/* 0008 */ 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13+
})
14+
}
15+
16+
Method(MAIN, 0, NotSerialized) {
17+
Local0 = (DerefOf(\_SB.ADAT[0x09]))
18+
// This relies on subtraction rather than equality as logical ops on BufferFields don't work
19+
// yet.
20+
return (Local0 - 0xaa)
21+
}
22+
}
23+
"#;
24+
25+
let handler = NullHandler {};
26+
test_infra::run_aml_test(AML, handler);
27+
}

0 commit comments

Comments
 (0)