kernel/acpi/aml/
structured.rs

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    /// This is a very stupid error, its a bit annoying to return `\\` scope element, since its not
28    /// stored inside an `Element`, and we can't return a temporary value
29    /// We will never (normally) execute or try to find the label `\\`, so hopefully we can rely on that XD
30    /// FIXME: get a better fix
31    CannotQueryRoot,
32    PartOfPathNotScope(String),
33    InvalidName(String),
34}
35
36#[derive(Debug, Clone, Default)]
37#[allow(dead_code)]
38pub struct StructuredAml {
39    /// Denoted by `\`
40    root: Scope,
41}
42
43impl StructuredAml {
44    pub fn parse(code: &AmlCode) -> Self {
45        // We use `root` as a root reference, and `root_terms` that will be the scope
46        // of the root `code`, the reason we have two is that some statement use `\_SB` for example
47        // and some will just use `_SB` in the root, those are the same thing.
48        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                // sad
60                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                // this will add to the parent
123                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                    // recurse
131                    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                        &region.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                    // TODO: the current way to structure is not good
235                    //       since the root may contain execution elements, that we need to take care of
236                    //       the language works similar to python in some sense. for example
237                    //       ```
238                    //       If (( PWRS & 0x02 )) {
239                    //         Name(_S1_, Package (0x02) {
240                    //           One, One
241                    //         })
242                    //       }
243                    //       ```
244                    //       this will enable `\_S1` name based on `PWRS` flags, and this is in the root scope
245                    //       so better to rewrite this whole thing :(
246                    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    // specific version for fast addition than `add_child`
262    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                    // device always wins if there is any, its a more special version of `Scope`
276                    // it shouldn't conflict anyway, but just in case
277                    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        // change the name
313        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}