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,
Propagate,
},
pkg_length::{pkg_length, PkgLength},
type1::type1_opcode,
type2::{def_buffer, def_package, type2_opcode},
value::{AmlValue, FieldFlags, MethodCode, MethodFlags, RegionSpace},
AmlContext,
AmlError,
AmlHandle,
DebugVerbosity,
};
use alloc::string::String;
use core::str;
pub fn term_list<'a, 'c>(list_length: PkgLength) -> impl Parser<'a, 'c, ()>
where
'c: 'a,
{
move |mut input: &'a [u8], mut context: &'c mut AmlContext| {
while list_length.still_parsing(input) {
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,
{
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,
{
choice!(def_name(), def_scope())
}
pub fn named_obj<'a, 'c>() -> impl Parser<'a, 'c, ()>
where
'c: 'a,
{
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,
{
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,
{
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,
{
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(Propagate::Err(AmlError::InvalidRegionSpace(byte))), context),
};
let offset = match offset.as_integer(context) {
Ok(offset) => offset,
Err(err) => return (Err(Propagate::Err(err)), context),
};
let length = match length.as_integer(context) {
Ok(length) => length,
Err(err) => return (Err(Propagate::Err(err)), context),
};
let parent_device = match region {
RegionSpace::PciConfig | RegionSpace::IPMI | RegionSpace::GenericSerialBus => {
let resolved_path = try_with_context!(context, name.resolve(&context.current_scope));
Some(try_with_context!(context, resolved_path.parent()))
}
_ => None,
};
try_with_context!(
context,
context.namespace.add_value_at_resolved_path(
name,
&context.current_scope,
AmlValue::OpRegion { region, offset, length, parent_device }
)
);
(Ok(()), context)
},
),
))
.discard_result()
}
pub fn def_field<'a, 'c>() -> impl Parser<'a, 'c, ()>
where
'c: 'a,
{
let opregion_as_handle = name_string().map_with_context(|region_name, context| {
let (_, handle) =
try_with_context!(context, context.namespace.search(®ion_name, &context.current_scope));
(Ok(handle), context)
});
ext_opcode(opcode::EXT_DEF_FIELD_OP)
.then(comment_scope(
DebugVerbosity::Scopes,
"DefField",
pkg_length().then(opregion_as_handle).then(take()).feed(|((list_length, region_handle), flags)| {
move |mut input: &'a [u8], mut context: &'c mut AmlContext| -> ParseResult<'a, 'c, ()> {
let mut current_offset = 0;
while list_length.still_parsing(input) {
let (new_input, new_context, field_length) =
field_element(region_handle, FieldFlags::new(flags), current_offset)
.parse(input, context)?;
input = new_input;
context = new_context;
current_offset += field_length;
}
Ok((input, context, ()))
}
}),
))
.discard_result()
}
pub fn field_element<'a, 'c>(
region_handle: AmlHandle,
flags: FieldFlags,
current_offset: u64,
) -> impl Parser<'a, 'c, u64>
where
'c: 'a,
{
let reserved_field =
opcode(opcode::RESERVED_FIELD).then(pkg_length()).map(|((), length)| Ok(length.raw_length as u64));
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_handle,
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,
{
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::from(flags),
code: MethodCode::Aml(code.to_vec())
},
)
);
(Ok(()), context)
}),
))
.discard_result()
}
pub fn def_device<'a, 'c>() -> impl Parser<'a, 'c, ()>
where
'c: 'a,
{
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.clone(), LevelType::Device)
);
let previous_scope = context.current_scope.clone();
context.current_scope = resolved_name;
(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,
{
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| {
let resolved_name = try_with_context!(context, name.resolve(&context.current_scope));
try_with_context!(
context,
context.namespace.add_level(resolved_name.clone(), LevelType::Processor)
);
try_with_context!(
context,
context.namespace.add_value(
resolved_name.clone(),
AmlValue::Processor { id: proc_id, pblk_address, pblk_len }
)
);
let previous_scope = context.current_scope.clone();
context.current_scope = resolved_name;
(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,
{
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,
{
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,
{
comment_scope(DebugVerbosity::AllScopes, "DataRefObject", choice!(data_object()))
}
pub fn data_object<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
where
'c: 'a,
{
comment_scope(DebugVerbosity::AllScopes, "DataObject", choice!(def_package(), computational_data()))
}
pub fn computational_data<'a, 'c>() -> impl Parser<'a, 'c, AmlValue>
where
'c: 'a,
{
let const_parser = |input: &'a [u8], context: &'c mut AmlContext| {
let string_parser = |input: &'a [u8], context| -> ParseResult<'a, 'c, AmlValue> {
let nul_position = match input.iter().position(|&c| c == b'\0') {
Some(position) => position,
None => return Err((input, context, Propagate::Err(AmlError::UnterminatedStringConstant))),
};
let string = String::from(match str::from_utf8(&input[0..nul_position]) {
Ok(string) => string,
Err(_) => return Err((input, context, Propagate::Err(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::zero())),
opcode::ONE_OP => Ok((new_input, context, AmlValue::one())),
opcode::ONES_OP => Ok((new_input, context, AmlValue::ones())),
_ => Err((input, context, Propagate::Err(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::*;
#[test]
fn test_computational_data() {
let mut context = make_test_context();
check_ok_value!(
computational_data().parse(&[0x00, 0x34, 0x12], &mut context),
AmlValue::Integer(0),
&[0x34, 0x12]
);
check_ok_value!(
computational_data().parse(&[0x01, 0x18, 0xf3], &mut context),
AmlValue::Integer(1),
&[0x18, 0xf3]
);
check_ok_value!(
computational_data().parse(&[0xff, 0x98, 0xc3], &mut context),
AmlValue::Integer(u64::max_value()),
&[0x98, 0xc3]
);
check_ok_value!(
computational_data().parse(&[0x5b, 0x30], &mut context),
AmlValue::Integer(crate::AML_INTERPRETER_REVISION),
&[]
);
check_ok_value!(
computational_data().parse(&[0x0a, 0xf3, 0x35], &mut context),
AmlValue::Integer(0xf3),
&[0x35]
);
check_ok_value!(
computational_data().parse(&[0x0b, 0xf3, 0x35], &mut context),
AmlValue::Integer(0x35f3),
&[]
);
check_ok_value!(
computational_data().parse(&[0x0c, 0xf3, 0x35, 0x12, 0x65, 0xff, 0x00], &mut context),
AmlValue::Integer(0x651235f3),
&[0xff, 0x00]
);
check_ok_value!(
computational_data()
.parse(&[0x0e, 0xf3, 0x35, 0x12, 0x65, 0xff, 0x00, 0x67, 0xde, 0x28], &mut context),
AmlValue::Integer(0xde6700ff651235f3),
&[0x28]
);
check_ok_value!(
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]
);
}
}