1use core::fmt;
2
3use alloc::{
4 collections::{btree_map::Entry, BTreeMap},
5 format,
6 string::{String, ToString},
7 vec,
8 vec::Vec,
9};
10use tracing::warn;
11
12use crate::testing;
13
14use super::{
15 display::AmlDisplayer,
16 parser::{
17 AmlTerm, FieldDef, IndexFieldDef, MethodObj, PowerResource, ProcessorDeprecated, RegionObj,
18 ScopeType, UnresolvedDataObject,
19 },
20 AmlCode,
21};
22
23#[derive(Debug, Clone)]
24#[allow(dead_code)]
25pub enum StructuredAmlError {
26 QueryPathMustBeAbsolute,
27 CannotQueryRoot,
32 PartOfPathNotScope(String),
33 InvalidName(String),
34}
35
36#[derive(Debug, Clone, Default)]
37#[allow(dead_code)]
38pub struct StructuredAml {
39 root: Scope,
41}
42
43impl StructuredAml {
44 pub fn parse(code: &AmlCode) -> Self {
45 let mut root = Scope::default();
49 let root_terms = Scope::parse(&code.term_list, &mut root, "\\", ScopeType::Scope);
50
51 root.merge(root_terms);
52
53 Self { root }
54 }
55
56 pub fn find_object(&self, label: &str) -> Result<Option<&ElementType>, StructuredAmlError> {
57 if let Some(rest) = label.strip_prefix('\\') {
58 if rest.is_empty() {
59 return Err(StructuredAmlError::CannotQueryRoot);
61 }
62 self.root.find_object(rest)
63 } else {
64 Err(StructuredAmlError::QueryPathMustBeAbsolute)
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
70pub enum ElementType {
71 ScopeOrDevice(Scope),
72 Method(MethodObj),
73 Processor(ProcessorDeprecated),
74 PowerResource(PowerResource),
75 RegionFields(Option<RegionObj>, Vec<FieldDef>),
76 IndexField(IndexFieldDef),
77 Name(UnresolvedDataObject),
78 UnknownElements(Vec<AmlTerm>),
79}
80
81#[derive(Debug, Clone)]
82pub struct Scope {
83 ty: ScopeType,
84 children: BTreeMap<String, ElementType>,
85}
86
87impl Default for Scope {
88 fn default() -> Self {
89 Self {
90 ty: ScopeType::Scope,
91 children: Default::default(),
92 }
93 }
94}
95
96impl Scope {
97 pub fn parse(terms: &[AmlTerm], root: &mut Scope, current_path: &str, ty: ScopeType) -> Self {
98 let mut this = Scope {
99 ty,
100 children: Default::default(),
101 };
102
103 fn handle_add(
104 current_path: &str,
105 this: &mut Scope,
106 root: &mut Scope,
107 name: &str,
108 element: ElementType,
109 ) {
110 if let Some(rest) = name.strip_prefix('\\') {
111 if rest.is_empty() {
112 if let ElementType::ScopeOrDevice(scope) = element {
113 root.merge(scope);
114 } else {
115 panic!("Root is not a scope or device");
116 }
117 } else {
118 root.add_child(rest, element);
119 }
120 } else if let Some(rest) = name.strip_prefix('^') {
121 assert!(!rest.is_empty());
122 let (parent, _) = current_path.rsplit_once('.').expect("Must have parent");
124 assert!(
125 parent.starts_with('\\'),
126 "parent {parent:?} must start with \\"
127 );
128
129 if rest.starts_with('^') {
130 handle_add(parent, this, root, rest, element);
132 } else {
133 let full_path = format!("{parent}.{rest}");
134 root.add_child(full_path.trim_start_matches('\\'), element);
135 }
136 } else {
137 this.add_child(name, element);
138 }
139 }
140
141 for term in terms {
142 match term {
143 AmlTerm::Device(scope) | AmlTerm::Scope(scope) => {
144 let scope_path = if scope.name.starts_with('\\') {
145 scope.name.clone()
146 } else {
147 format!(
148 "{}{}{}",
149 current_path,
150 if current_path.ends_with('\\') {
151 ""
152 } else {
153 "."
154 },
155 scope.name
156 )
157 };
158 let element = ElementType::ScopeOrDevice(Scope::parse(
159 &scope.term_list,
160 root,
161 &scope_path,
162 match term {
163 AmlTerm::Device(_) => ScopeType::Device,
164 AmlTerm::Scope(_) => ScopeType::Scope,
165 _ => unreachable!(),
166 },
167 ));
168 handle_add(current_path, &mut this, root, &scope.name, element);
169 }
170 AmlTerm::Region(region) => {
171 handle_add(
172 current_path,
173 &mut this,
174 root,
175 ®ion.name,
176 ElementType::RegionFields(Some(region.clone()), Vec::new()),
177 );
178 }
179 AmlTerm::Field(field) => {
180 handle_add(
181 current_path,
182 &mut this,
183 root,
184 &field.name,
185 ElementType::RegionFields(None, vec![field.clone()]),
186 );
187 }
188 AmlTerm::IndexField(index_field) => {
189 handle_add(
190 current_path,
191 &mut this,
192 root,
193 &index_field.name,
194 ElementType::IndexField(index_field.clone()),
195 );
196 }
197 AmlTerm::Processor(processor) => {
198 handle_add(
199 current_path,
200 &mut this,
201 root,
202 &processor.name,
203 ElementType::Processor(processor.clone()),
204 );
205 }
206 AmlTerm::PowerResource(power_resource) => {
207 handle_add(
208 current_path,
209 &mut this,
210 root,
211 &power_resource.name,
212 ElementType::PowerResource(power_resource.clone()),
213 );
214 }
215 AmlTerm::Method(method) => {
216 handle_add(
217 current_path,
218 &mut this,
219 root,
220 &method.name,
221 ElementType::Method(method.clone()),
222 );
223 }
224 AmlTerm::NameObj(name, obj) => {
225 handle_add(
226 current_path,
227 &mut this,
228 root,
229 name,
230 ElementType::Name(obj.clone()),
231 );
232 }
233 _ => {
234 warn!("Execution statements found in scope {term:?}");
247 handle_add(
248 current_path,
249 &mut this,
250 root,
251 "\\UNKW",
252 ElementType::UnknownElements(vec![term.clone()]),
253 );
254 }
255 }
256 }
257
258 this
259 }
260
261 fn add_immediate_child(&mut self, name: &str, element: ElementType) {
263 assert!(!name.starts_with('\\'));
264 assert_eq!(name.len(), 4, "Invalid name: {name:?}");
265 match self.children.entry(name.to_string()) {
266 Entry::Vacant(entry) => {
267 entry.insert(element);
268 }
269 Entry::Occupied(mut entry) => match entry.get_mut() {
270 ElementType::ScopeOrDevice(scope) => {
271 let ElementType::ScopeOrDevice(mut element) = element else {
272 panic!("New element: {name:?} is not a scope or device");
273 };
274
275 if matches!(scope.ty, ScopeType::Device)
278 || matches!(element.ty, ScopeType::Device)
279 {
280 element.ty = ScopeType::Device;
281 }
282
283 scope.merge(element);
284 }
285 ElementType::RegionFields(region, fields) => {
286 let ElementType::RegionFields(new_region, new_fields) = element else {
287 panic!("New element: {name:?} is not a region");
288 };
289
290 assert!(
291 !(region.is_some() && new_region.is_some()),
292 "Both regions are available, conflict, {region:?} && {new_region:?}"
293 );
294 *region = region.clone().or(new_region);
295 fields.extend(new_fields);
296 }
297 ElementType::UnknownElements(elements) => {
298 let ElementType::UnknownElements(new_elements) = element else {
299 panic!("New element: {name:?} is not an unknown element");
300 };
301 elements.extend(new_elements);
302 }
303 _ => panic!("Child: {name:?} is not a scope or device"),
304 },
305 }
306 }
307
308 pub fn add_child(&mut self, name: &str, mut element: ElementType) {
309 assert!(!name.starts_with('\\'));
310 let split_result = name.split_once('.');
311
312 match element {
314 ElementType::Method(ref mut method) => {
315 method.name = name.to_string();
316 }
317 ElementType::Processor(ref mut processor) => {
318 processor.name = name.to_string();
319 }
320 ElementType::PowerResource(ref mut power_resource) => {
321 power_resource.name = name.to_string();
322 }
323 ElementType::RegionFields(ref mut region, ref mut fields) => {
324 if let Some(r) = region.as_mut() {
325 r.name = name.to_string();
326 }
327 for field in fields {
328 field.name = name.to_string();
329 }
330 }
331 ElementType::IndexField(ref mut index_field) => {
332 index_field.name = name.to_string();
333 }
334 _ => {}
335 }
336
337 match split_result {
338 Some((first_child, rest)) => {
339 let child = self
340 .children
341 .entry(first_child.to_string())
342 .or_insert(ElementType::ScopeOrDevice(Scope::default()));
343
344 if let ElementType::ScopeOrDevice(scope) = child {
345 scope.add_child(rest, element);
346 } else {
347 panic!(
348 "Child: {first_child:?} of {name:?} is not a scope or device {:?}",
349 child
350 );
351 }
352 }
353 None => {
354 self.add_immediate_child(name, element);
355 }
356 }
357 }
358
359 pub fn merge(&mut self, other: Scope) {
360 for (name, element) in other.children {
361 self.add_immediate_child(&name, element)
362 }
363 }
364
365 fn find_object(&self, name: &str) -> Result<Option<&ElementType>, StructuredAmlError> {
366 let split_result = name.split_once('.');
367
368 match split_result {
369 Some((first_child, rest)) => {
370 if first_child.len() != 4 {
371 return Err(StructuredAmlError::InvalidName(first_child.to_string()));
372 }
373 let Some(child) = self.children.get(first_child) else {
374 return Ok(None);
375 };
376
377 if let ElementType::ScopeOrDevice(scope) = child {
378 scope.find_object(rest)
379 } else {
380 Err(StructuredAmlError::PartOfPathNotScope(
381 first_child.to_string(),
382 ))
383 }
384 }
385 None => {
386 if name.len() != 4 {
387 return Err(StructuredAmlError::InvalidName(name.to_string()));
388 }
389 Ok(self.children.get(name))
390 }
391 }
392 }
393}
394
395impl fmt::Display for Scope {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 for (i, (name, element)) in self.children.iter().enumerate() {
398 match element {
399 ElementType::ScopeOrDevice(scope) => {
400 let ty = match self.ty {
401 ScopeType::Scope => "Scope",
402 ScopeType::Device => "Device",
403 };
404 let mut d = AmlDisplayer::start(f, ty);
405 d.paren_arg(|f| f.write_str(name)).finish_paren_arg();
406
407 d.body_field(|f| scope.fmt(f));
408
409 d.at_least_empty_body().finish()
410 }
411 ElementType::Method(method) => method.fmt(f),
412 ElementType::Processor(processor) => processor.fmt(f),
413 ElementType::PowerResource(power_resource) => power_resource.fmt(f),
414 ElementType::RegionFields(region, fields) => {
415 if let Some(region) = region {
416 region.fmt(f)?;
417 } else {
418 write!(f, "Region {name}, NOT FOUND!!!")?;
419 }
420
421 if f.alternate() {
422 writeln!(f)?;
423 } else {
424 write!(f, "; ")?;
425 }
426
427 for (i, field) in fields.iter().enumerate() {
428 field.fmt(f)?;
429
430 if i < fields.len() - 1 {
431 if f.alternate() {
432 writeln!(f)?;
433 } else {
434 write!(f, "; ")?;
435 }
436 }
437 }
438
439 Ok(())
440 }
441 ElementType::IndexField(index_field) => index_field.fmt(f),
442 ElementType::Name(data_obj) => AmlDisplayer::start(f, "Name")
443 .paren_arg(|f| f.write_str(name))
444 .paren_arg(|f| data_obj.fmt(f))
445 .finish(),
446 ElementType::UnknownElements(elements) => {
447 let mut d = AmlDisplayer::start(f, "UnknownElements");
448
449 d.paren_arg(|f| f.write_str(name)).finish_paren_arg();
450
451 for element in elements {
452 d.body_field(|f| element.fmt(f));
453 }
454
455 d.at_least_empty_body().finish()
456 }
457 }?;
458
459 if i < self.children.len() - 1 {
460 if f.alternate() {
461 writeln!(f)?;
462 } else {
463 write!(f, "; ")?;
464 }
465 }
466 }
467
468 Ok(())
469 }
470}
471
472impl fmt::Display for StructuredAml {
473 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 let mut d = AmlDisplayer::start(f, "Scope");
475 d.paren_arg(|f| f.write_str("\\")).finish_paren_arg();
476
477 d.body_field(|f| self.root.fmt(f));
478
479 d.finish()
480 }
481}
482
483#[macro_rules_attribute::apply(testing::test)]
484fn test_structure() {
485 use super::parser::{
486 AccessType, FieldElement, FieldUpdateRule, IntegerData, RegionSpace, ScopeObj, Target,
487 TermArg, UnresolvedDataObject,
488 };
489 use alloc::boxed::Box;
490
491 let code = AmlCode {
492 term_list: vec![
493 AmlTerm::Scope(ScopeObj {
494 ty: ScopeType::Scope,
495 name: "\\".to_string(),
496 term_list: vec![
497 AmlTerm::Region(RegionObj {
498 name: "DBG_".to_string(),
499 region_space: RegionSpace::SystemIO,
500 region_offset: TermArg::DataObject(UnresolvedDataObject::Integer(
501 IntegerData::WordConst(1026),
502 )),
503 region_length: TermArg::DataObject(UnresolvedDataObject::Integer(
504 IntegerData::ConstOne,
505 )),
506 }),
507 AmlTerm::Field(FieldDef {
508 name: "DBG_".to_string(),
509 access_type: AccessType::Byte,
510 need_lock: false,
511 update_rule: FieldUpdateRule::Preserve,
512 fields: vec![FieldElement::Named("DBGB".to_string(), 8)],
513 }),
514 AmlTerm::Method(MethodObj {
515 name: "DBUG".to_string(),
516 num_args: 1,
517 is_serialized: false,
518 sync_level: 0,
519 term_list: vec![
520 AmlTerm::ToHexString(TermArg::Arg(0), Box::new(Target::Local(0))),
521 AmlTerm::ToBuffer(TermArg::Local(0), Box::new(Target::Local(0))),
522 ],
523 }),
524 ],
525 }),
526 AmlTerm::Method(MethodObj {
527 name: "\\_GPE._E02".to_string(),
528 num_args: 0,
529 is_serialized: false,
530 sync_level: 0,
531 term_list: vec![AmlTerm::MethodCall("\\_SB_.CPUS.CSCN".to_string(), vec![])],
532 }),
533 ],
534 };
535
536 let structured = StructuredAml::parse(&code);
537
538 assert_eq!(
539 structured.root.children.keys().collect::<Vec<_>>(),
540 vec!["DBG_", "DBUG", "_GPE"]
541 );
542
543 match &structured.root.children["DBG_"] {
544 ElementType::RegionFields(region, fields) => {
545 assert!(region.is_some());
546 assert!(!fields.is_empty());
547 }
548 _ => panic!("DBG_ is not a region"),
549 }
550 match &structured.root.children["DBUG"] {
551 ElementType::Method(method) => {
552 assert_eq!(method.name, "DBUG");
553 assert_eq!(method.term_list.len(), 2);
554 }
555 _ => panic!("DBUG is not a method"),
556 }
557 match &structured.root.children["_GPE"] {
558 ElementType::ScopeOrDevice(scope) => {
559 assert_eq!(scope.children.keys().collect::<Vec<_>>(), vec!["_E02"]);
560
561 match &scope.children["_E02"] {
562 ElementType::Method(method) => {
563 assert_eq!(method.name, "_E02");
564 assert_eq!(method.term_list.len(), 1);
565 }
566 _ => panic!("_E02 is not a method"),
567 }
568 }
569 _ => panic!("_GPE is not a scope"),
570 }
571}