123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- use crate::{
- misc::{arg_obj, local_obj},
- name_object::{name_seg, name_string},
- namespace::{AmlName, LevelType},
- opcode::{self, ext_opcode, opcode},
- parser::{
- choice,
- comment_scope,
- make_parser_concrete,
- take,
- take_to_end_of_pkglength,
- take_u16,
- take_u32,
- take_u64,
- try_with_context,
- ParseResult,
- Parser,
- },
- pkg_length::{pkg_length, PkgLength},
- type1::type1_opcode,
- type2::{def_buffer, def_package, type2_opcode},
- value::{AmlValue, FieldFlags, MethodFlags, RegionSpace},
- AmlContext,
- AmlError,
- DebugVerbosity,
- };
- use alloc::string::String;
- use core::str;
- /// `TermList`s are usually found within explicit-length objects (so they have a `PkgLength`
- /// elsewhere in the structure), so this takes a number of bytes to parse.
- pub fn term_list<'a, 'c>(list_length: PkgLength) -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * TermList := Nothing | <TermObj TermList>
- */
- move |mut input: &'a [u8], mut context: &'c mut AmlContext| {
- while list_length.still_parsing(input) {
- // TODO: currently, we ignore the value of the expression. We may need to propagate
- // this.
- let (new_input, new_context, _) = term_object().parse(input, context)?;
- input = new_input;
- context = new_context;
- }
- Ok((input, context, ()))
- }
- }
- pub fn term_object<'a, 'c>() -> impl Parser<'a, 'c, Option<AmlValue>>
- where
- 'c: 'a,
- {
- /*
- * TermObj := NamespaceModifierObj | NamedObj | Type1Opcode | Type2Opcode
- */
- comment_scope(
- DebugVerbosity::AllScopes,
- "TermObj",
- choice!(
- namespace_modifier().map(|()| Ok(None)),
- named_obj().map(|()| Ok(None)),
- type1_opcode().map(|()| Ok(None)),
- type2_opcode().map(|value| Ok(Some(value)))
- ),
- )
- }
- pub fn namespace_modifier<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * NamespaceModifierObj := DefAlias | DefName | DefScope
- */
- choice!(def_name(), def_scope())
- }
- pub fn named_obj<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * NamedObj := DefBankField | DefCreateBitField | DefCreateByteField | DefCreateDWordField |
- * DefCreateField | DefCreateQWordField | DefCreateWordField | DefDataRegion |
- * DefExternal | DefOpRegion | DefPowerRes | DefProcessor | DefThermalZone |
- * DefMethod | DefMutex
- *
- * XXX: DefMethod and DefMutex (at least) are not included in any rule in the AML grammar,
- * but are defined in the NamedObj section so we assume they're part of NamedObj
- */
- comment_scope(
- DebugVerbosity::AllScopes,
- "NamedObj",
- choice!(def_op_region(), def_field(), def_method(), def_device(), def_processor(), def_mutex()),
- )
- }
- pub fn def_name<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefName := 0x08 NameString DataRefObject
- */
- opcode(opcode::DEF_NAME_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefName",
- name_string().then(data_ref_object()).map_with_context(|(name, data_ref_object), context| {
- try_with_context!(
- context,
- context.namespace.add_value_at_resolved_path(name, &context.current_scope, data_ref_object,)
- );
- (Ok(()), context)
- }),
- ))
- .discard_result()
- }
- pub fn def_scope<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefScope := 0x10 PkgLength NameString TermList
- */
- opcode(opcode::DEF_SCOPE_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefScope",
- pkg_length()
- .then(name_string())
- .map_with_context(|(length, name), context| {
- let previous_scope = context.current_scope.clone();
- context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
- context.comment(
- DebugVerbosity::Scopes,
- &(String::from("Scope name: ") + &context.current_scope.as_string()),
- );
- try_with_context!(
- context,
- context.namespace.add_level(context.current_scope.clone(), LevelType::Scope)
- );
- (Ok((length, previous_scope)), context)
- })
- .feed(|(pkg_length, previous_scope)| {
- term_list(pkg_length).map(move |_| Ok(previous_scope.clone()))
- })
- .map_with_context(|previous_scope, context| {
- context.current_scope = previous_scope;
- (Ok(()), context)
- }),
- ))
- .discard_result()
- }
- pub fn def_op_region<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefOpRegion := ExtOpPrefix 0x80 NameString RegionSpace RegionOffset RegionLen
- * RegionSpace := ByteData (where 0x00 = SystemMemory
- * 0x01 = SystemIO
- * 0x02 = PciConfig
- * 0x03 = EmbeddedControl
- * 0x04 = SMBus
- * 0x05 = SystemCMOS
- * 0x06 = PciBarTarget
- * 0x07 = IPMI
- * 0x08 = GeneralPurposeIO
- * 0x09 = GenericSerialBus
- * 0x80-0xff = OEM Defined)
- * ByteData := 0x00 - 0xff
- * RegionOffset := TermArg => Integer
- * RegionLen := TermArg => Integer
- */
- ext_opcode(opcode::EXT_DEF_OP_REGION_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefOpRegion",
- name_string().then(take()).then(term_arg()).then(term_arg()).map_with_context(
- |(((name, space), offset), length), context| {
- let region = match space {
- 0x00 => RegionSpace::SystemMemory,
- 0x01 => RegionSpace::SystemIo,
- 0x02 => RegionSpace::PciConfig,
- 0x03 => RegionSpace::EmbeddedControl,
- 0x04 => RegionSpace::SMBus,
- 0x05 => RegionSpace::SystemCmos,
- 0x06 => RegionSpace::PciBarTarget,
- 0x07 => RegionSpace::IPMI,
- 0x08 => RegionSpace::GeneralPurposeIo,
- 0x09 => RegionSpace::GenericSerialBus,
- space @ 0x80..=0xff => RegionSpace::OemDefined(space),
- byte => return (Err(AmlError::InvalidRegionSpace(byte)), context),
- };
- let offset = match offset.as_integer() {
- Ok(offset) => offset,
- Err(err) => return (Err(err), context),
- };
- let length = match length.as_integer() {
- Ok(length) => length,
- Err(err) => return (Err(err), context),
- };
- try_with_context!(
- context,
- context.namespace.add_value_at_resolved_path(
- name,
- &context.current_scope,
- AmlValue::OpRegion { region, offset, length }
- )
- );
- (Ok(()), context)
- },
- ),
- ))
- .discard_result()
- }
- pub fn def_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefField = ExtOpPrefix 0x81 PkgLength NameString FieldFlags FieldList
- * FieldFlags := ByteData
- */
- ext_opcode(opcode::EXT_DEF_FIELD_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefField",
- pkg_length().then(name_string()).then(take()).feed(|((list_length, region_name), flags)| {
- move |mut input: &'a [u8], mut context: &'c mut AmlContext| -> ParseResult<'a, 'c, ()> {
- /*
- * FieldList := Nothing | <FieldElement FieldList>
- */
- // TODO: can this pattern be expressed as a combinator
- let mut current_offset = 0;
- while list_length.still_parsing(input) {
- let (new_input, new_context, field_length) =
- field_element(region_name.clone(), FieldFlags::new(flags), current_offset)
- .parse(input, context)?;
- input = new_input;
- context = new_context;
- current_offset += field_length;
- }
- Ok((input, context, ()))
- }
- }),
- ))
- .discard_result()
- }
- /// Parses a `FieldElement`. Takes the current offset within the field list, and returns the length
- /// of the field element parsed.
- pub fn field_element<'a, 'c>(
- region_name: AmlName,
- flags: FieldFlags,
- current_offset: u64,
- ) -> impl Parser<'a, 'c, u64>
- where
- 'c: 'a,
- {
- /*
- * FieldElement := NamedField | ReservedField | AccessField | ExtendedAccessField |
- * ConnectField
- * NamedField := NameSeg PkgLength
- * ReservedField := 0x00 PkgLength
- * AccessField := 0x01 AccessType AccessAttrib
- * ConnectField := <0x02 NameString> | <0x02 BufferData>
- * ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength
- *
- * AccessType := ByteData
- * AccessAttrib := ByteData
- *
- * XXX: The spec says a ConnectField can be <0x02 BufferData>, but BufferData isn't an AML
- * object (it seems to be defined in ASL). We treat BufferData as if it was encoded like
- * DefBuffer, and this seems to work so far.
- */
- // TODO: parse ConnectField and ExtendedAccessField
- /*
- * Reserved fields shouldn't actually be added to the namespace; they seem to show gaps in
- * the operation region that aren't used for anything.
- */
- let reserved_field =
- opcode(opcode::RESERVED_FIELD).then(pkg_length()).map(|((), length)| Ok(length.raw_length as u64));
- // TODO: work out what to do with an access field
- // let access_field = opcode(opcode::ACCESS_FIELD)
- // .then(take())
- // .then(take())
- // .map_with_context(|(((), access_type), access_attrib), context| (Ok( , context));
- let named_field = name_seg().then(pkg_length()).map_with_context(move |(name_seg, length), context| {
- try_with_context!(
- context,
- context.namespace.add_value_at_resolved_path(
- AmlName::from_name_seg(name_seg),
- &context.current_scope,
- AmlValue::Field {
- region: region_name.clone(),
- flags,
- offset: current_offset,
- length: length.raw_length as u64,
- },
- )
- );
- (Ok(length.raw_length as u64), context)
- });
- choice!(reserved_field, named_field)
- }
- pub fn def_method<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefMethod := 0x14 PkgLength NameString MethodFlags TermList
- * MethodFlags := ByteData (where bits 0-2: ArgCount (0 to 7)
- * bit 3: SerializeFlag (0 = Not Serialized, 1 = Serialized)
- * bits 4-7: SyncLevel (0x00 to 0x0f))
- */
- opcode(opcode::DEF_METHOD_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefMethod",
- pkg_length()
- .then(name_string())
- .then(take())
- .feed(|((length, name), flags)| {
- take_to_end_of_pkglength(length).map(move |code| Ok((name.clone(), flags, code)))
- })
- .map_with_context(|(name, flags, code), context| {
- try_with_context!(
- context,
- context.namespace.add_value_at_resolved_path(
- name,
- &context.current_scope,
- AmlValue::Method { flags: MethodFlags::new(flags), code: code.to_vec() },
- )
- );
- (Ok(()), context)
- }),
- ))
- .discard_result()
- }
- pub fn def_device<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefDevice := ExtOpPrefix 0x82 PkgLength NameString TermList
- */
- ext_opcode(opcode::EXT_DEF_DEVICE_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefDevice",
- pkg_length()
- .then(name_string())
- .map_with_context(|(length, name), context| {
- let resolved_name = try_with_context!(context, name.clone().resolve(&context.current_scope));
- try_with_context!(context, context.namespace.add_level(resolved_name, LevelType::Device));
- let previous_scope = context.current_scope.clone();
- context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
- (Ok((length, previous_scope)), context)
- })
- .feed(|(length, previous_scope)| term_list(length).map(move |_| Ok(previous_scope.clone())))
- .map_with_context(|previous_scope, context| {
- context.current_scope = previous_scope;
- (Ok(()), context)
- }),
- ))
- .discard_result()
- }
- pub fn def_processor<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefProcessor := ExtOpPrefix 0x83 PkgLength NameString ProcID PblkAddress PblkLen TermList
- * ProcID := ByteData
- * PblkAddress := DWordData
- * PblkLen := ByteData
- */
- ext_opcode(opcode::EXT_DEF_PROCESSOR_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefProcessor",
- pkg_length()
- .then(name_string())
- .then(take())
- .then(take_u32())
- .then(take())
- .map_with_context(|((((pkg_length, name), proc_id), pblk_address), pblk_len), context| {
- try_with_context!(
- context,
- context.namespace.add_value_at_resolved_path(
- name.clone(),
- &context.current_scope,
- AmlValue::Processor { id: proc_id, pblk_address, pblk_len }
- )
- );
- let previous_scope = context.current_scope.clone();
- context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
- (Ok((previous_scope, pkg_length)), context)
- })
- .feed(move |(previous_scope, pkg_length)| {
- term_list(pkg_length).map(move |_| Ok(previous_scope.clone()))
- })
- .map_with_context(|previous_scope, context| {
- context.current_scope = previous_scope;
- (Ok(()), context)
- }),
- ))
- .discard_result()
- }
- pub fn def_mutex<'a, 'c>() -> impl Parser<'a, 'c, ()>
- where
- 'c: 'a,
- {
- /*
- * DefMutex := ExtOpPrefix 0x01 NameString SyncFlags
- * SyncFlags := ByteData (where bits 0-3: SyncLevel
- * bits 4-7: Reserved)
- */
- ext_opcode(opcode::EXT_DEF_MUTEX_OP)
- .then(comment_scope(
- DebugVerbosity::Scopes,
- "DefMutex",
- name_string().then(take()).map_with_context(|(name, sync_level), context| {
- try_with_context!(
- context,
- context.namespace.add_value_at_resolved_path(
- name,
- &context.current_scope,
- AmlValue::Mutex { sync_level }
- )
- );
- (Ok(()), context)
- }),
- ))
- .discard_result()
- }
- pub fn term_arg<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * TermArg := Type2Opcode | DataObject | ArgObj | LocalObj
- */
- comment_scope(
- DebugVerbosity::AllScopes,
- "TermArg",
- choice!(
- data_object(),
- arg_obj().map_with_context(|arg_num, context| {
- (Ok(try_with_context!(context, context.current_arg(arg_num)).clone()), context)
- }),
- local_obj().map_with_context(|local_num, context| {
- (Ok(try_with_context!(context, context.local(local_num)).clone()), context)
- }),
- make_parser_concrete!(type2_opcode())
- ),
- )
- }
- pub fn data_ref_object<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DataRefObject := DataObject | ObjectReference | DDBHandle
- */
- comment_scope(DebugVerbosity::AllScopes, "DataRefObject", choice!(data_object()))
- }
- pub fn data_object<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * DataObject := DefPackage | DefVarPackage | ComputationalData
- *
- * The order of the parsers are important here, as DefPackage and DefVarPackage can be
- * accidently parsed as ComputationalDatas.
- */
- // TODO: this doesn't yet parse DefVarPackage
- comment_scope(DebugVerbosity::AllScopes, "DataObject", choice!(def_package(), computational_data()))
- }
- pub fn computational_data<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
- where
- 'c: 'a,
- {
- /*
- * ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String |
- * ConstObj | RevisionOp | DefBuffer
- * ByteConst := 0x0a ByteData
- * WordConst := 0x0b WordData
- * DWordConst := 0x0c DWordData
- * QWordConst := 0x0e QWordData
- * String := 0x0d AsciiCharList NullChar
- * ConstObj := ZeroOp(0x00) | OneOp(0x01) | OnesOp(0xff)
- * RevisionOp := ExtOpPrefix(0x5b) 0x30
- */
- let const_parser = |input: &'a [u8], context: &'c mut AmlContext| {
- let string_parser = |input: &'a [u8], context| -> ParseResult<'a, 'c, AmlValue> {
- /*
- * Using `position` isn't very efficient here, but is probably fine because the
- * strings are usually quite short.
- */
- let nul_position = match input.iter().position(|&c| c == b'\0') {
- Some(position) => position,
- None => return Err((input, context, AmlError::UnterminatedStringConstant)),
- };
- let string = String::from(match str::from_utf8(&input[0..nul_position]) {
- Ok(string) => string,
- Err(_) => return Err((input, context, AmlError::InvalidStringConstant)),
- });
- Ok((&input[(nul_position + 1)..], context, AmlValue::String(string)))
- };
- let (new_input, context, op) = take().parse(input, context)?;
- match op {
- opcode::BYTE_CONST => {
- take().map(|value| Ok(AmlValue::Integer(value as u64))).parse(new_input, context)
- }
- opcode::WORD_CONST => {
- take_u16().map(|value| Ok(AmlValue::Integer(value as u64))).parse(new_input, context)
- }
- opcode::DWORD_CONST => {
- take_u32().map(|value| Ok(AmlValue::Integer(value as u64))).parse(new_input, context)
- }
- opcode::QWORD_CONST => take_u64().map(|value| Ok(AmlValue::Integer(value))).parse(new_input, context),
- opcode::STRING_PREFIX => string_parser.parse(new_input, context),
- opcode::ZERO_OP => Ok((new_input, context, AmlValue::Integer(0))),
- opcode::ONE_OP => Ok((new_input, context, AmlValue::Integer(1))),
- opcode::ONES_OP => Ok((new_input, context, AmlValue::Integer(u64::max_value()))),
- _ => Err((input, context, AmlError::WrongParser)),
- }
- };
- comment_scope(
- DebugVerbosity::AllScopes,
- "ComputationalData",
- choice!(
- ext_opcode(opcode::EXT_REVISION_OP).map(|_| Ok(AmlValue::Integer(crate::AML_INTERPRETER_REVISION))),
- const_parser,
- make_parser_concrete!(def_buffer())
- ),
- )
- }
- #[cfg(test)]
- mod test {
- use super::*;
- use crate::{test_utils::*, AmlContext, DebugVerbosity};
- #[test]
- fn test_computational_data() {
- let mut context = AmlContext::new(DebugVerbosity::None);
- check_ok!(
- computational_data().parse(&[0x00, 0x34, 0x12], &mut context),
- AmlValue::Integer(0),
- &[0x34, 0x12]
- );
- check_ok!(
- computational_data().parse(&[0x01, 0x18, 0xf3], &mut context),
- AmlValue::Integer(1),
- &[0x18, 0xf3]
- );
- check_ok!(
- computational_data().parse(&[0xff, 0x98, 0xc3], &mut context),
- AmlValue::Integer(u64::max_value()),
- &[0x98, 0xc3]
- );
- check_ok!(
- computational_data().parse(&[0x5b, 0x30], &mut context),
- AmlValue::Integer(crate::AML_INTERPRETER_REVISION),
- &[]
- );
- check_ok!(computational_data().parse(&[0x0a, 0xf3, 0x35], &mut context), AmlValue::Integer(0xf3), &[0x35]);
- check_ok!(computational_data().parse(&[0x0b, 0xf3, 0x35], &mut context), AmlValue::Integer(0x35f3), &[]);
- check_ok!(
- computational_data().parse(&[0x0c, 0xf3, 0x35, 0x12, 0x65, 0xff, 0x00], &mut context),
- AmlValue::Integer(0x651235f3),
- &[0xff, 0x00]
- );
- check_ok!(
- computational_data()
- .parse(&[0x0e, 0xf3, 0x35, 0x12, 0x65, 0xff, 0x00, 0x67, 0xde, 0x28], &mut context),
- AmlValue::Integer(0xde6700ff651235f3),
- &[0x28]
- );
- check_ok!(
- computational_data().parse(&[0x0d, b'A', b'B', b'C', b'D', b'\0', 0xff, 0xf5], &mut context),
- AmlValue::String(String::from("ABCD")),
- &[0xff, 0xf5]
- );
- }
- }
|