Skip to content

Commit 97861f8

Browse files
committed
aml: support executing method objects
This also cleans up a few bugs, such as methods not being run in their correct scopes, and creating a scope for their locally-defined names.
1 parent 782e26b commit 97861f8

File tree

3 files changed

+101
-35
lines changed

3 files changed

+101
-35
lines changed

aml/src/lib.rs

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,29 @@ impl Interpreter {
3838

3939
pub fn load_table(&self, stream: &[u8]) -> Result<(), AmlError> {
4040
// TODO: probs needs to do more stuff
41-
self.execute_method(stream)
41+
let context = unsafe { MethodContext::new_from_table(stream) };
42+
self.do_execute_method(context)?;
43+
Ok(())
4244
}
4345

44-
pub fn execute_method(&self, stream: &[u8]) -> Result<(), AmlError> {
45-
let mut context = unsafe { MethodContext::new_from_table(stream) };
46+
/// Invoke a method by its name, with the given set of arguments. If the referenced object is
47+
/// not a method, the object will instead be returned - this is useful for objects that can
48+
/// either be defined directly, or through a method (e.g. a `_CRS` object).
49+
pub fn invoke_method(&self, path: AmlName, args: Vec<Arc<Object>>) -> Result<Arc<Object>, AmlError> {
50+
info!("Invoking AML method: {}", path);
51+
52+
let object = self.namespace.lock().get(path.clone())?.clone();
53+
match object.typ() {
54+
ObjectType::Method => {
55+
self.namespace.lock().add_level(path.clone(), NamespaceLevelKind::MethodLocals)?;
56+
let context = MethodContext::new_from_method(object, args, path)?;
57+
self.do_execute_method(context)
58+
}
59+
_ => Ok(object),
60+
}
61+
}
4662

63+
fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
4764
/*
4865
* TODO
4966
*
@@ -277,9 +294,12 @@ impl Interpreter {
277294
self.handler.stall(usec);
278295
}
279296
Opcode::InternalMethodCall => {
280-
let Argument::Object(method) = &op.arguments[0] else { panic!() };
297+
let [Argument::Object(method), Argument::Namestring(method_scope)] = &op.arguments[0..2]
298+
else {
299+
panic!()
300+
};
281301

282-
let args = op.arguments[1..]
302+
let args = op.arguments[2..]
283303
.iter()
284304
.map(|arg| {
285305
if let Argument::Object(arg) = arg {
@@ -290,18 +310,30 @@ impl Interpreter {
290310
})
291311
.collect();
292312

293-
let new_context = MethodContext::new_from_method(method.clone(), args)?;
313+
self.namespace.lock().add_level(method_scope.clone(), NamespaceLevelKind::MethodLocals)?;
314+
315+
let new_context =
316+
MethodContext::new_from_method(method.clone(), args, method_scope.clone())?;
294317
let old_context = mem::replace(&mut context, new_context);
295318
self.context_stack.lock().push(old_context);
296319
}
297320
Opcode::Return => {
298321
let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
299-
context = self.context_stack.lock().pop().unwrap();
300322

301-
if let Some(prev_op) = context.in_flight.last_mut() {
302-
if prev_op.arguments.len() < prev_op.expected_arguments {
303-
prev_op.arguments.push(Argument::Object(object.clone()));
323+
if let Some(last) = self.context_stack.lock().pop() {
324+
context = last;
325+
326+
if let Some(prev_op) = context.in_flight.last_mut() {
327+
if prev_op.arguments.len() < prev_op.expected_arguments {
328+
prev_op.arguments.push(Argument::Object(object.clone()));
329+
}
304330
}
331+
} else {
332+
/*
333+
* If this is the top-most context, this is a `Return` from the actual
334+
* method.
335+
*/
336+
return Ok(object.clone());
305337
}
306338
}
307339
Opcode::ObjectType => {
@@ -353,13 +385,21 @@ impl Interpreter {
353385
*/
354386
match context.current_block.kind {
355387
BlockKind::Table => {
356-
break Ok(());
388+
break Ok(Arc::new(Object::Uninitialized));
357389
}
358-
BlockKind::Method => {
359-
// TODO: not sure how to handle no explicit return. Result is undefined
360-
// but we might still need to handle sticking it in an in-flight op?
361-
context = self.context_stack.lock().pop().unwrap();
362-
continue;
390+
BlockKind::Method { method_scope } => {
391+
self.namespace.lock().remove_level(method_scope)?;
392+
393+
if let Some(prev_context) = self.context_stack.lock().pop() {
394+
context = prev_context;
395+
continue;
396+
} else {
397+
/*
398+
* If there is no explicit `Return` op, the result is undefined. We
399+
* just return an uninitialized object.
400+
*/
401+
return Ok(Arc::new(Object::Uninitialized));
402+
}
363403
}
364404
BlockKind::Scope { old_scope } => {
365405
assert!(context.block_stack.len() > 0);
@@ -691,11 +731,11 @@ impl Interpreter {
691731
if let Object::Method { flags, .. } = *object {
692732
context.start_in_flight_op(OpInFlight::new_with(
693733
Opcode::InternalMethodCall,
694-
vec![Argument::Object(object)],
695734
flags.arg_count(),
696735
))
697736
} else {
698737
context.last_op()?.arguments.push(Argument::Object(object));
738+
vec![Argument::Object(object), Argument::Namestring(resolved_name)],
699739
}
700740
}
701741

@@ -915,10 +955,9 @@ impl Interpreter {
915955
/// ### Safety
916956
/// `MethodContext` does not keep the lifetime of the underlying AML stream, which for tables is
917957
/// borrowed from the underlying physical mapping. This is because the interpreter needs to
918-
/// pre-empt method contexts that execute other methods, storing pre-empted contexts.
919-
///
920-
/// This is made safe in the case of methods by the context holding a reference to the method
921-
/// object, but must be handled manually for AML tables.
958+
/// preempt method contexts that execute other methods, and these contexts may have disparate
959+
/// lifetimes. This is made safe in the case of methods by the context holding a reference to the
960+
/// method object, but must be handled manually for AML tables.
922961
struct MethodContext {
923962
current_block: Block,
924963
block_stack: Vec<Block>,
@@ -963,7 +1002,9 @@ impl Block {
9631002
#[derive(PartialEq, Debug)]
9641003
pub enum BlockKind {
9651004
Table,
966-
Method,
1005+
Method {
1006+
method_scope: AmlName,
1007+
},
9671008
Scope {
9681009
old_scope: AmlName,
9691010
},
@@ -997,12 +1038,20 @@ impl MethodContext {
9971038
}
9981039
}
9991040

1000-
fn new_from_method(method: Arc<Object>, args: Vec<Arc<Object>>) -> Result<MethodContext, AmlError> {
1041+
fn new_from_method(
1042+
method: Arc<Object>,
1043+
args: Vec<Arc<Object>>,
1044+
scope: AmlName,
1045+
) -> Result<MethodContext, AmlError> {
10011046
if let Object::Method { code, flags } = &*method {
10021047
if args.len() != flags.arg_count() {
10031048
return Err(AmlError::MethodArgCountIncorrect);
10041049
}
1005-
let block = Block { stream: code as &[u8] as *const [u8], pc: 0, kind: BlockKind::Method };
1050+
let block = Block {
1051+
stream: code as &[u8] as *const [u8],
1052+
pc: 0,
1053+
kind: BlockKind::Method { method_scope: scope.clone() },
1054+
};
10061055
let args = core::array::from_fn(|i| {
10071056
if let Some(arg) = args.get(i) { arg.clone() } else { Arc::new(Object::Uninitialized) }
10081057
});
@@ -1012,7 +1061,7 @@ impl MethodContext {
10121061
in_flight: Vec::new(),
10131062
args,
10141063
locals: core::array::from_fn(|_| Arc::new(Object::Uninitialized)),
1015-
current_scope: AmlName::root(),
1064+
current_scope: scope,
10161065
_method: Some(method.clone()),
10171066
};
10181067
Ok(context)
@@ -1530,9 +1579,9 @@ mod tests {
15301579
fn add_op() {
15311580
let interpreter = Interpreter::new(TestHandler);
15321581
// AddOp 0x0e 0x06 => Local2
1533-
interpreter.execute_method(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
1582+
interpreter.load_table(&[0x72, 0x0b, 0x0e, 0x00, 0x0a, 0x06, 0x62]).unwrap();
15341583
// AddOp 0x0e (AddOp 0x01 0x03 => Local1) => Local1
1535-
interpreter.execute_method(&[0x72, 0x0a, 0x0e, 0x72, 0x0a, 0x01, 0x0a, 0x03, 0x61, 0x61]).unwrap();
1584+
interpreter.load_table(&[0x72, 0x0a, 0x0e, 0x72, 0x0a, 0x01, 0x0a, 0x03, 0x61, 0x61]).unwrap();
15361585
}
15371586

15381587
#[test]
@@ -1541,9 +1590,5 @@ mod tests {
15411590
unsafe { MethodContext::new_from_table(b"\\\x2eABC_DEF_\0") }.namestring(),
15421591
Ok(AmlName::from_str("\\ABC.DEF").unwrap())
15431592
);
1544-
assert_eq!(
1545-
unsafe { MethodContext::new_from_table(b"\x2eABC_DEF_^_GHI") }.namestring(),
1546-
Ok(AmlName::from_str("ABC.DEF.^_GHI").unwrap())
1547-
);
15481593
}
15491594
}

aml/src/namespace.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ impl Namespace {
4444
Ok(())
4545
}
4646

47+
pub fn remove_level(&mut self, path: AmlName) -> Result<(), AmlError> {
48+
assert!(path.is_absolute());
49+
let path = path.normalize()?;
50+
51+
// Don't try to remove the root scope
52+
// TODO: we probably shouldn't be able to remove the pre-defined scopes either?
53+
if path != AmlName::root() {
54+
let (level, last_seg) = self.get_level_for_path_mut(&path)?;
55+
level.children.remove(&last_seg);
56+
}
57+
58+
Ok(())
59+
}
60+
4761
pub fn insert(&mut self, path: AmlName, object: Arc<Object>) -> Result<(), AmlError> {
4862
assert!(path.is_absolute());
4963
let path = path.normalize()?;
@@ -221,7 +235,7 @@ pub enum NamespaceLevelKind {
221235
Processor,
222236
PowerResource,
223237
ThermalZone,
224-
//MethodLocals,
238+
MethodLocals,
225239
}
226240

227241
pub struct NamespaceLevel {

tests/method.asl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
DefinitionBlock("method.aml", "DSDT", 1, "RSACPI", "METHOD", 1) {
2+
Name(X, 5)
3+
24
Method(FOO, 0, NotSerialized) {
3-
Return (0xff)
5+
If (X > 1) {
6+
Noop
7+
} Else {
8+
Return (0x55)
9+
}
10+
Return (0x3f)
411
}
512

6-
Name(X, 0)
7-
X = FOO()
13+
Name(Y, 0)
14+
Y = FOO()
815
}

0 commit comments

Comments
 (0)