123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- use crate::{
- name_object::{name_string, super_name, target},
- opcode::{self, opcode},
- parser::{
- choice,
- comment_scope,
- id,
- make_parser_concrete,
- take,
- take_to_end_of_pkglength,
- try_with_context,
- Parser,
- },
- pkg_length::pkg_length,
- term_object::{data_ref_object, term_arg},
- value::AmlValue,
- AmlError,
- DebugVerbosity,
- };
- use alloc::vec::Vec;
- use core::convert::TryInto;
- /// Type 2 opcodes return a value and so can be used in expressions.
- pub fn type2_opcode<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * Type2Opcode := DefAquire | DefAdd | DefAnd | DefBuffer | DefConcat | DefConcatRes |
- * DefCondRefOf | DefCopyObject | DefDecrement | DefDerefOf | DefDivide |
- * DefFindSetLeftBit | DefFindSetRightBit | DefFromBCD | DefIncrement | DefIndex |
- * DefLAnd | DefLEqual | DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual |
- * DefMid | DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod |
- * DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | DefPackage |
- * DefVarPackage | DefRefOf | DefShiftLeft | DefShitRight | DefSizeOf | DefStore |
- * DefSubtract | DefTimer | DefToBCD | DefToBuffer | DefToDecimalString |
- * DefToHexString | DefToInteger | DefToString | DefWait | DefXOr | MethodInvocation
- *
- * NOTE: MethodInvocation should always appear last in the choice.
- */
- make_parser_concrete!(comment_scope(
- DebugVerbosity::AllScopes,
- "Type2Opcode",
- choice!(
- def_buffer(),
- def_l_equal(),
- def_package(),
- def_shift_left(),
- def_shift_right(),
- def_store(),
- method_invocation()
- ),
- ))
- }
- pub fn def_buffer<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DefBuffer := 0x11 PkgLength BufferSize ByteList
- * BufferSize := TermArg => Integer
- *
- * XXX: The spec says that zero-length buffers (e.g. the PkgLength is 0) are illegal, but
- * we've encountered them in QEMU-generated tables, so we return an empty buffer in these
- * cases.
- */
- opcode(opcode::DEF_BUFFER_OP)
- .then(comment_scope(
- DebugVerbosity::AllScopes,
- "DefBuffer",
- pkg_length().then(term_arg()).feed(|(pkg_length, buffer_size)| {
- take_to_end_of_pkglength(pkg_length).map_with_context(move |bytes, context| {
- let length = try_with_context!(context, buffer_size.as_integer(context));
- (Ok((bytes.to_vec(), length)), context)
- })
- }),
- ))
- .map(|((), (bytes, buffer_size))| Ok(AmlValue::Buffer { bytes, size: buffer_size }))
- }
- fn def_l_equal<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DefLEqual := 0x93 Operand Operand
- * Operand := TermArg => Integer
- */
- opcode(opcode::DEF_L_EQUAL_OP)
- .then(comment_scope(
- DebugVerbosity::AllScopes,
- "DefLEqual",
- term_arg().then(term_arg()).map_with_context(|(left_arg, right_arg), context| {
- let left = try_with_context!(context, left_arg.as_integer(context));
- let right = try_with_context!(context, right_arg.as_integer(context));
- (Ok(AmlValue::Boolean(left == right)), context)
- }),
- ))
- .map(|((), result)| Ok(result))
- }
- pub fn def_package<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DefPackage := 0x12 PkgLength NumElements PackageElementList
- * NumElements := ByteData
- * PackageElementList := Nothing | <PackageElement PackageElementList>
- * PackageElement := DataRefObject | NameString
- */
- opcode(opcode::DEF_PACKAGE_OP)
- .then(comment_scope(
- DebugVerbosity::AllScopes,
- "DefPackage",
- pkg_length().then(take()).feed(|(pkg_length, num_elements)| {
- move |mut input, mut context| {
- let mut package_contents = Vec::new();
- while pkg_length.still_parsing(input) {
- let (new_input, new_context, value) = package_element().parse(input, context)?;
- input = new_input;
- context = new_context;
- package_contents.push(value);
- }
- assert_eq!(package_contents.len(), num_elements as usize);
- Ok((input, context, AmlValue::Package(package_contents)))
- }
- }),
- ))
- .map(|((), package)| Ok(package))
- }
- pub fn package_element<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- choice!(data_ref_object(), name_string().map(|string| Ok(AmlValue::String(string.as_string()))))
- }
- fn def_shift_left<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DefShiftLeft := 0x79 Operand ShiftCount Target
- * Operand := TermArg => Integer
- * ShiftCount := TermArg => Integer
- */
- opcode(opcode::DEF_SHIFT_LEFT)
- .then(comment_scope(DebugVerbosity::Scopes, "DefShiftLeft", term_arg().then(term_arg()).then(target())))
- .map_with_context(|((), ((operand, shift_count), target)), context| {
- let operand = try_with_context!(context, operand.as_integer(context));
- let shift_count = try_with_context!(context, shift_count.as_integer(context));
- let shift_count =
- try_with_context!(context, shift_count.try_into().map_err(|_| AmlError::InvalidShiftLeft));
- let result = AmlValue::Integer(try_with_context!(
- context,
- operand.checked_shl(shift_count).ok_or(AmlError::InvalidShiftLeft)
- ));
- try_with_context!(context, context.store(target, result.clone()));
- (Ok(result), context)
- })
- }
- fn def_shift_right<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DefShiftRight := 0x7a Operand ShiftCount Target
- * Operand := TermArg => Integer
- * ShiftCount := TermArg => Integer
- */
- opcode(opcode::DEF_SHIFT_RIGHT)
- .then(comment_scope(DebugVerbosity::Scopes, "DefShiftRight", term_arg().then(term_arg()).then(target())))
- .map_with_context(|((), ((operand, shift_count), target)), context| {
- let operand = try_with_context!(context, operand.as_integer(context));
- let shift_count = try_with_context!(context, shift_count.as_integer(context));
- let shift_count =
- try_with_context!(context, shift_count.try_into().map_err(|_| AmlError::InvalidShiftRight));
- let result = AmlValue::Integer(try_with_context!(
- context,
- operand.checked_shr(shift_count).ok_or(AmlError::InvalidShiftRight)
- ));
- try_with_context!(context, context.store(target, result.clone()));
- (Ok(result), context)
- })
- }
- fn def_store<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DefStore := 0x70 TermArg SuperName
- *
- * Implicit conversion is only applied when the destination target is a `Name` - not when we
- * are storing into a method local or argument (these stores are semantically identical to
- * CopyObject). We must also make sure to return a copy of the data that is in the destination
- * after the store (as opposed to the data we think we put into it), because some stores can
- * alter the data during the store.
- */
- opcode(opcode::DEF_STORE_OP)
- .then(comment_scope(DebugVerbosity::Scopes, "DefStore", term_arg().then(super_name())))
- .map_with_context(|((), (value, target)), context| {
- (Ok(try_with_context!(context, context.store(target, value))), context)
- })
- }
- fn method_invocation<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * MethodInvocation := NameString TermArgList
- *
- * MethodInvocation is the worst of the AML structures, because you're meant to figure out how much you're
- * meant to parse using the name of the method (by knowing from its definition how how many arguments it
- * takes). However, the definition of a method can in theory appear after an invocation of that method, and
- * so parsing them properly can be very difficult.
- * NOTE: We don't support the case of the definition appearing after the invocation.
- */
- comment_scope(
- DebugVerbosity::Scopes,
- "MethodInvocation",
- name_string()
- .map_with_context(move |name, context| {
- let (_, handle) =
- try_with_context!(context, context.namespace.search(&name, &context.current_scope)).clone();
- (Ok(handle), context)
- })
- .feed(|handle| {
- id().map_with_context(move |(), context| {
- let object = try_with_context!(context, context.namespace.get(handle));
- if let AmlValue::Method { ref code, .. } = object {
- // TODO: we need to allow a method to be invoked from inside another method before we can
- // implement this (basically a stack of contexts) then implement this
- unimplemented!()
- } else {
- // We appear to be seeing AML where a MethodInvocation actually doesn't point to a method
- // at all, which isn't mentioned in the spec afaict. However, if we treat it as an
- // "invocation" with 0 arguments and simply return the object, the AML seems to do sensible
- // things.
- (Ok(object.clone()), context)
- }
- })
- }),
- )
- }
|