Skip to main content

stable_aml/
value.rs

1use crate::{misc::ArgNum, AmlContext, AmlError, AmlHandle, AmlName};
2use alloc::{
3    string::{String, ToString},
4    sync::Arc,
5    vec::Vec,
6};
7use bit_field::BitField;
8use core::{cmp, fmt, fmt::Debug};
9use spinning_top::Spinlock;
10
11#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12pub enum RegionSpace {
13    SystemMemory,
14    SystemIo,
15    PciConfig,
16    EmbeddedControl,
17    SMBus,
18    SystemCmos,
19    PciBarTarget,
20    IPMI,
21    GeneralPurposeIo,
22    GenericSerialBus,
23    OemDefined(u8),
24}
25
26#[derive(Clone, Copy, PartialEq, Eq, Debug)]
27pub enum FieldAccessType {
28    Any,
29    Byte,
30    Word,
31    DWord,
32    QWord,
33    Buffer,
34}
35
36#[derive(Clone, Copy, PartialEq, Eq, Debug)]
37pub enum FieldUpdateRule {
38    Preserve,
39    WriteAsOnes,
40    WriteAsZeros,
41}
42
43// TODO: custom debug impl
44#[derive(Clone, Copy, PartialEq, Eq, Debug)]
45pub struct FieldFlags(u8);
46
47impl FieldFlags {
48    pub fn new(value: u8) -> FieldFlags {
49        FieldFlags(value)
50    }
51
52    pub fn access_type(&self) -> Result<FieldAccessType, AmlError> {
53        match self.0.get_bits(0..4) {
54            0 => Ok(FieldAccessType::Any),
55            1 => Ok(FieldAccessType::Byte),
56            2 => Ok(FieldAccessType::Word),
57            3 => Ok(FieldAccessType::DWord),
58            4 => Ok(FieldAccessType::QWord),
59            5 => Ok(FieldAccessType::Buffer),
60            _ => Err(AmlError::InvalidFieldFlags),
61        }
62    }
63
64    pub fn lock_rule(&self) -> bool {
65        self.0.get_bit(4)
66    }
67
68    pub fn field_update_rule(&self) -> Result<FieldUpdateRule, AmlError> {
69        match self.0.get_bits(5..7) {
70            0 => Ok(FieldUpdateRule::Preserve),
71            1 => Ok(FieldUpdateRule::WriteAsOnes),
72            2 => Ok(FieldUpdateRule::WriteAsZeros),
73            _ => Err(AmlError::InvalidFieldFlags),
74        }
75    }
76}
77
78#[derive(Clone, Copy, PartialEq, Eq, Debug)]
79pub struct MethodFlags(u8);
80
81impl MethodFlags {
82    pub fn new(arg_count: u8, serialize: bool, sync_level: u8) -> MethodFlags {
83        assert!(arg_count <= 7);
84        assert!(sync_level <= 15);
85
86        let mut value = 0;
87        value.set_bits(0..3, arg_count);
88        value.set_bit(3, serialize);
89        value.set_bits(4..8, sync_level);
90        MethodFlags(value)
91    }
92
93    pub fn from(value: u8) -> MethodFlags {
94        MethodFlags(value)
95    }
96
97    pub fn arg_count(&self) -> u8 {
98        self.0.get_bits(0..3)
99    }
100
101    pub fn serialize(&self) -> bool {
102        self.0.get_bit(3)
103    }
104
105    pub fn sync_level(&self) -> u8 {
106        self.0.get_bits(4..8)
107    }
108}
109
110/// Representation of the return value of a `_STA` method, which represents the status of an object. It must be
111/// evaluated, if present, before evaluating the `_INI` method for an device.
112///
113/// The `Default` implementation of this type is the correct value to use if a device doesn't have a `_STA` object
114/// to evaluate.
115#[derive(Clone, Copy, PartialEq, Eq, Debug)]
116pub struct StatusObject {
117    /// Whether the device is physically present. If this is `false`, `enabled` should also be `false` (i.e. a
118    /// device that is not present can't be enabled). However, this is not enforced here if the firmware is doing
119    /// something wrong.
120    pub present: bool,
121    /// Whether the device is enabled. Both `present` and `enabled` must be `true` for the device to decode its
122    /// hardware resources.
123    pub enabled: bool,
124    pub show_in_ui: bool,
125    pub functional: bool,
126    /// Only applicable for Control Method Battery Devices (`PNP0C0A`). For all other devices, ignore this value.
127    pub battery_present: bool,
128}
129
130impl Default for StatusObject {
131    fn default() -> Self {
132        StatusObject {
133            present: true,
134            enabled: true,
135            show_in_ui: true,
136            functional: true,
137            battery_present: true,
138        }
139    }
140}
141
142#[derive(Clone, Copy, PartialEq, Eq, Debug)]
143pub enum AmlType {
144    Uninitialized,
145    Buffer,
146    BufferField,
147    /// Handle to a definition block handle. Returned by the `Load` operator.
148    DdbHandle,
149    DebugObject,
150    Event,
151    FieldUnit,
152    Device,
153    Integer,
154    Method,
155    Mutex,
156    ObjReference,
157    OpRegion,
158    Package,
159    PowerResource,
160    Processor,
161    RawDataBuffer,
162    String,
163    ThermalZone,
164}
165
166#[derive(Clone)]
167pub enum MethodCode {
168    Aml(Vec<u8>),
169    Native(Arc<dyn Fn(&mut AmlContext) -> Result<AmlValue, AmlError> + Send + Sync>),
170}
171
172impl fmt::Debug for MethodCode {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            MethodCode::Aml(ref code) => write!(f, "AML({:x?})", code),
176            MethodCode::Native(_) => write!(f, "(native method)"),
177        }
178    }
179}
180
181#[derive(Clone, Debug)]
182pub enum AmlValue {
183    Boolean(bool),
184    Integer(u64),
185    String(String),
186    /// Describes an operation region. Some regions require other objects to be declared under their parent device
187    /// (e.g. an `_ADR` object for a `PciConfig` region), in which case an absolute path to the object is stored in
188    /// `parent_device`.
189    OpRegion {
190        region: RegionSpace,
191        offset: u64,
192        length: u64,
193        parent_device: Option<AmlName>,
194    },
195    /// Describes a field unit within an operation region.
196    Field {
197        region: AmlHandle,
198        flags: FieldFlags,
199        offset: u64,
200        length: u64,
201    },
202    Device,
203    Method {
204        flags: MethodFlags,
205        code: MethodCode,
206    },
207    Buffer(Arc<Spinlock<Vec<u8>>>),
208    BufferField {
209        buffer_data: Arc<Spinlock<Vec<u8>>>,
210        /// In bits.
211        offset: u64,
212        /// In bits.
213        length: u64,
214    },
215    Processor {
216        id: u8,
217        pblk_address: u32,
218        pblk_len: u8,
219    },
220    Mutex {
221        sync_level: u8,
222    },
223    // TODO: I think this will need to be `Arc`ed as well, as `Index` can be used on both Buffers and Packages
224    Package(Vec<AmlValue>),
225    PowerResource {
226        system_level: u8,
227        resource_order: u16,
228    },
229    ThermalZone,
230}
231
232impl AmlValue {
233    pub fn zero() -> AmlValue {
234        AmlValue::Integer(0)
235    }
236
237    pub fn one() -> AmlValue {
238        AmlValue::Integer(1)
239    }
240
241    pub fn ones() -> AmlValue {
242        AmlValue::Integer(u64::max_value())
243    }
244
245    pub fn native_method<F>(arg_count: u8, serialize: bool, sync_level: u8, f: F) -> AmlValue
246    where
247        F: (Fn(&mut AmlContext) -> Result<AmlValue, AmlError>) + 'static + Send + Sync,
248    {
249        let flags = MethodFlags::new(arg_count, serialize, sync_level);
250        AmlValue::Method {
251            flags,
252            code: MethodCode::Native(Arc::new(f)),
253        }
254    }
255
256    pub fn type_of(&self) -> AmlType {
257        match self {
258            AmlValue::Boolean(_) => AmlType::Integer,
259            AmlValue::Integer(_) => AmlType::Integer,
260            AmlValue::String(_) => AmlType::String,
261            AmlValue::OpRegion { .. } => AmlType::OpRegion,
262            AmlValue::Field { .. } => AmlType::FieldUnit,
263            AmlValue::Device => AmlType::Device,
264            AmlValue::Method { .. } => AmlType::Method,
265            AmlValue::Buffer(_) => AmlType::Buffer,
266            AmlValue::BufferField { .. } => AmlType::BufferField,
267            AmlValue::Processor { .. } => AmlType::Processor,
268            AmlValue::Mutex { .. } => AmlType::Mutex,
269            AmlValue::Package(_) => AmlType::Package,
270            AmlValue::PowerResource { .. } => AmlType::PowerResource,
271            AmlValue::ThermalZone => AmlType::ThermalZone,
272        }
273    }
274
275    pub fn as_bool(&self) -> Result<bool, AmlError> {
276        match self {
277            AmlValue::Boolean(value) => Ok(*value),
278            AmlValue::Integer(value) => Ok(*value != 0),
279            _ => Err(AmlError::IncompatibleValueConversion {
280                current: self.type_of(),
281                target: AmlType::Integer,
282            }),
283        }
284    }
285
286    pub fn as_integer(&self, context: &AmlContext) -> Result<u64, AmlError> {
287        match self {
288            AmlValue::Integer(value) => Ok(*value),
289            AmlValue::Boolean(value) => Ok(if *value { u64::max_value() } else { 0 }),
290            AmlValue::Buffer(ref bytes) => {
291                /*
292                 * "The first 8 bytes of the buffer are converted to an integer, taking the first
293                 * byte as the least significant byte of the integer. A zero-length buffer is
294                 * illegal." - §19.6.140
295                 *
296                 * XXX: Buffers with length `0` appear in real tables, so we return `0` for them.
297                 */
298                let bytes = bytes.lock();
299                let bytes = if bytes.len() > 8 {
300                    &bytes[0..8]
301                } else {
302                    &bytes[..]
303                };
304
305                Ok(bytes.iter().rev().fold(0u64, |mut i, &popped| {
306                    i <<= 8;
307                    i += popped as u64;
308                    i
309                }))
310            }
311            /*
312             * Read from a field or buffer field. These can return either a `Buffer` or an `Integer`, so we make sure to call
313             * `as_integer` on the result.
314             */
315            AmlValue::Field { .. } => self.read_field(context)?.as_integer(context),
316            AmlValue::BufferField { .. } => self.read_buffer_field(context)?.as_integer(context),
317
318            _ => Err(AmlError::IncompatibleValueConversion {
319                current: self.type_of(),
320                target: AmlType::Integer,
321            }),
322        }
323    }
324
325    pub fn as_buffer(&self, context: &AmlContext) -> Result<Arc<Spinlock<Vec<u8>>>, AmlError> {
326        match self {
327            AmlValue::Buffer(ref bytes) => Ok(bytes.clone()),
328            // TODO: implement conversion of String and Integer to Buffer
329            AmlValue::Field { .. } => self.read_field(context)?.as_buffer(context),
330            AmlValue::BufferField { .. } => self.read_buffer_field(context)?.as_buffer(context),
331            _ => Err(AmlError::IncompatibleValueConversion {
332                current: self.type_of(),
333                target: AmlType::Buffer,
334            }),
335        }
336    }
337
338    pub fn as_string(&self, context: &AmlContext) -> Result<String, AmlError> {
339        match self {
340            AmlValue::String(ref string) => Ok(string.clone()),
341            // TODO: implement conversion of Buffer to String
342            AmlValue::Field { .. } => self.read_field(context)?.as_string(context),
343            _ => Err(AmlError::IncompatibleValueConversion {
344                current: self.type_of(),
345                target: AmlType::String,
346            }),
347        }
348    }
349
350    /// Converts an `AmlValue` to the representation that should be used when concatenating it with other values,
351    /// primarily by the `DefConcat` opcode. This will always produce a `AmlValue::Integer`, `AmlValue::String`, or
352    /// `AmlValue::Buffer`, with other types being converted to strings containing the name of their type.
353    pub fn as_concat_type(&self) -> AmlValue {
354        match self.type_of() {
355            AmlType::Integer => self.clone(),
356            AmlType::String => self.clone(),
357            AmlType::Buffer => self.clone(),
358
359            AmlType::Uninitialized => AmlValue::String("[Uninitialized]".to_string()),
360            AmlType::BufferField => AmlValue::String("[Buffer Field]".to_string()),
361            AmlType::DdbHandle => AmlValue::String("[Ddb Handle]".to_string()),
362            AmlType::DebugObject => AmlValue::String("[Debug Object]".to_string()),
363            AmlType::Event => AmlValue::String("[Event]".to_string()),
364            AmlType::FieldUnit => AmlValue::String("[Field]".to_string()),
365            AmlType::Device => AmlValue::String("[Device]".to_string()),
366            AmlType::Method => AmlValue::String("[Control Method]".to_string()),
367            AmlType::Mutex => AmlValue::String("[Mutex]".to_string()),
368            AmlType::ObjReference => AmlValue::String("[Obj Reference]".to_string()),
369            AmlType::OpRegion => AmlValue::String("[Operation Region]".to_string()),
370            AmlType::Package => AmlValue::String("[Package]".to_string()),
371            AmlType::Processor => AmlValue::String("[Processor]".to_string()),
372            AmlType::PowerResource => AmlValue::String("[Power Resource]".to_string()),
373            AmlType::RawDataBuffer => AmlValue::String("[Raw Data Buffer]".to_string()),
374            AmlType::ThermalZone => AmlValue::String("[Thermal Zone]".to_string()),
375        }
376    }
377
378    /// Turns an `AmlValue` returned from a `_STA` method into a `StatusObject`. Should only be called for values
379    /// returned from `_STA`. If you need a `StatusObject`, but the device does not have a `_STA` method, use
380    /// `StatusObject::default()`.
381    pub fn as_status(&self) -> Result<StatusObject, AmlError> {
382        match self {
383            AmlValue::Integer(value) => {
384                /*
385                 * Bits 5+ are reserved and are expected to be cleared.
386                 */
387                if value.get_bits(5..64) != 0 {
388                    return Err(AmlError::InvalidStatusObject);
389                }
390
391                Ok(StatusObject {
392                    present: value.get_bit(0),
393                    enabled: value.get_bit(1),
394                    show_in_ui: value.get_bit(2),
395                    functional: value.get_bit(3),
396                    battery_present: value.get_bit(4),
397                })
398            }
399
400            _ => Err(AmlError::InvalidStatusObject),
401        }
402    }
403
404    /// Convert this value to a value of the same data, but with the given AML type, if possible,
405    /// by converting the implicit conversions described in §19.3.5 of the spec.
406    ///
407    /// The implicit conversions applied are:
408    ///     `Buffer` from: `Integer`, `String`, `Debug`
409    ///     `BufferField` from: `Integer`, `Buffer`, `String`, `Debug`
410    ///     `DdbHandle` from: `Integer`, `Debug`
411    ///     `FieldUnit` from: `Integer`,`Buffer`, `String`, `Debug`
412    ///     `Integer` from: `Buffer`, `BufferField`, `DdbHandle`, `FieldUnit`, `String`, `Debug`
413    ///     `Package` from: `Debug`
414    ///     `String` from: `Integer`, `Buffer`, `Debug`
415    pub fn as_type(
416        &self,
417        desired_type: AmlType,
418        context: &AmlContext,
419    ) -> Result<AmlValue, AmlError> {
420        // If the value is already of the correct type, just return it as is
421        if self.type_of() == desired_type {
422            return Ok(self.clone());
423        }
424
425        // TODO: implement all of the rules
426        match desired_type {
427            AmlType::Integer => self.as_integer(context).map(|value| AmlValue::Integer(value)),
428            AmlType::Buffer => self.as_buffer(context).map(|value| AmlValue::Buffer(value)),
429            AmlType::FieldUnit => panic!(
430                "Can't implicitly convert to FieldUnit. This must be special-cased by the caller for now :("
431            ),
432            _ => Err(AmlError::IncompatibleValueConversion { current: self.type_of(), target: desired_type }),
433        }
434    }
435
436    /// Reads from a field of an opregion, returning either a `AmlValue::Integer` or an `AmlValue::Buffer`,
437    /// depending on the size of the field.
438    pub fn read_field(&self, context: &AmlContext) -> Result<AmlValue, AmlError> {
439        if let AmlValue::Field {
440            region,
441            flags,
442            offset,
443            length,
444        } = self
445        {
446            let _maximum_access_size = {
447                if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
448                    match region {
449                        RegionSpace::SystemMemory => 64,
450                        RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
451                        _ => unimplemented!(),
452                    }
453                } else {
454                    return Err(AmlError::FieldRegionIsNotOpRegion);
455                }
456            };
457            let minimum_access_size = match flags.access_type()? {
458                FieldAccessType::Any => 8,
459                FieldAccessType::Byte => 8,
460                FieldAccessType::Word => 16,
461                FieldAccessType::DWord => 32,
462                FieldAccessType::QWord => 64,
463                FieldAccessType::Buffer => 8, // TODO
464            };
465
466            /*
467             * Find the access size, as either the minimum access size allowed by the region, or the field length
468             * rounded up to the next power-of-2, whichever is larger.
469             */
470            let access_size = u64::max(minimum_access_size, length.next_power_of_two());
471
472            /*
473             * TODO: we need to decide properly how to read from the region itself. Complications:
474             *    - if the region has a minimum access size greater than the desired length, we need to read the
475             *      minimum and mask it (reading a byte from a WordAcc region)
476             *    - if the desired length is larger than we can read, we need to do multiple reads
477             */
478            Ok(AmlValue::Integer(
479                context
480                    .read_region(*region, *offset, access_size)?
481                    .get_bits(0..(*length as usize)),
482            ))
483        } else {
484            Err(AmlError::IncompatibleValueConversion {
485                current: self.type_of(),
486                target: AmlType::FieldUnit,
487            })
488        }
489    }
490
491    pub fn write_field(
492        &mut self,
493        value: AmlValue,
494        context: &mut AmlContext,
495    ) -> Result<(), AmlError> {
496        /*
497         * If the field's update rule is `Preserve`, we need to read the initial value of the field, so we can
498         * overwrite the correct bits. We destructure the field to do the actual write, so we read from it if
499         * needed here, otherwise the borrow-checker doesn't understand.
500         */
501        let field_update_rule = if let AmlValue::Field { flags, .. } = self {
502            flags.field_update_rule()?
503        } else {
504            return Err(AmlError::IncompatibleValueConversion {
505                current: self.type_of(),
506                target: AmlType::FieldUnit,
507            });
508        };
509        let mut field_value = match field_update_rule {
510            FieldUpdateRule::Preserve => self.read_field(context)?.as_integer(context)?,
511            FieldUpdateRule::WriteAsOnes => 0xffffffff_ffffffff,
512            FieldUpdateRule::WriteAsZeros => 0x0,
513        };
514
515        if let AmlValue::Field {
516            region,
517            flags,
518            offset,
519            length,
520        } = self
521        {
522            let _maximum_access_size = {
523                if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
524                    match region {
525                        RegionSpace::SystemMemory => 64,
526                        RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
527                        _ => unimplemented!(),
528                    }
529                } else {
530                    return Err(AmlError::FieldRegionIsNotOpRegion);
531                }
532            };
533            let minimum_access_size = match flags.access_type()? {
534                FieldAccessType::Any => 8,
535                FieldAccessType::Byte => 8,
536                FieldAccessType::Word => 16,
537                FieldAccessType::DWord => 32,
538                FieldAccessType::QWord => 64,
539                FieldAccessType::Buffer => 8, // TODO
540            };
541
542            /*
543             * Find the access size, as either the minimum access size allowed by the region, or the field length
544             * rounded up to the next power-of-2, whichever is larger.
545             */
546            let access_size = u64::max(minimum_access_size, length.next_power_of_two());
547
548            field_value.set_bits(0..(*length as usize), value.as_integer(context)?);
549            context.write_region(*region, *offset, access_size, field_value)
550        } else {
551            Err(AmlError::IncompatibleValueConversion {
552                current: self.type_of(),
553                target: AmlType::FieldUnit,
554            })
555        }
556    }
557
558    pub fn read_buffer_field(&self, _context: &AmlContext) -> Result<AmlValue, AmlError> {
559        use bitvec::view::BitView;
560
561        if let AmlValue::BufferField {
562            buffer_data,
563            offset,
564            length,
565        } = self
566        {
567            let offset = *offset as usize;
568            let length = *length as usize;
569            let inner_data = buffer_data.lock();
570
571            if (offset + length) > (inner_data.len() * 8) {
572                return Err(AmlError::BufferFieldIndexesOutOfBounds);
573            }
574
575            let bitslice = inner_data.view_bits::<bitvec::order::Lsb0>();
576            let bits = &bitslice[offset..(offset + length)];
577
578            if length > 64 {
579                let mut bitvec = bits.to_bitvec();
580                bitvec.set_uninitialized(false);
581                Ok(AmlValue::Buffer(Arc::new(spinning_top::Spinlock::new(
582                    bitvec.into_vec(),
583                ))))
584            } else if length > 32 {
585                /*
586                 * TODO: this is a pretty gross hack to work around a weird limitation with the `bitvec` crate on
587                 * 32-bit platforms. For reasons beyond me right now, it can't operate on a `u64` on a 32-bit
588                 * platform, so we manually extract two `u32`s and stick them together. In the future, we should
589                 * definitely have a closer look at what `bitvec` is doing and see if we can fix this code, or
590                 * replace it with a different crate. This should hold everything vaguely together until we have
591                 * time to do that.
592                 */
593                let mut upper = 0u32;
594                let mut lower = 0u32;
595                lower.view_bits_mut::<bitvec::order::Lsb0>()[0..32].clone_from_bitslice(bits);
596                upper.view_bits_mut::<bitvec::order::Lsb0>()[0..(length - 32)]
597                    .clone_from_bitslice(&bits[32..]);
598                Ok(AmlValue::Integer((upper as u64) << 32 + (lower as u64)))
599            } else {
600                let mut value = 0u32;
601                value.view_bits_mut::<bitvec::order::Lsb0>()[0..length].clone_from_bitslice(bits);
602                Ok(AmlValue::Integer(value as u64))
603            }
604        } else {
605            Err(AmlError::IncompatibleValueConversion {
606                current: self.type_of(),
607                target: AmlType::BufferField,
608            })
609        }
610    }
611
612    pub fn write_buffer_field(
613        &mut self,
614        value: AmlValue,
615        _context: &mut AmlContext,
616    ) -> Result<(), AmlError> {
617        use bitvec::view::BitView;
618
619        if let AmlValue::BufferField {
620            buffer_data,
621            offset,
622            length,
623        } = self
624        {
625            let offset = *offset as usize;
626            let length = *length as usize;
627            // TODO: check these against the size of the buffer to be written into
628            let mut inner_data = buffer_data.lock();
629            let bitslice = inner_data.view_bits_mut::<bitvec::order::Lsb0>();
630
631            match value {
632                AmlValue::Integer(value) => {
633                    /*
634                     * When an `Integer` is written into a `BufferField`, the entire contents are overwritten. If
635                     * it's smaller than the length of the buffer field, it's zero-extended. If it's larger, the
636                     * upper bits are truncated.
637                     */
638                    let bits_to_copy = cmp::min(length, 64);
639                    bitslice[offset..(offset + bits_to_copy)].copy_from_bitslice(
640                        &value.to_le_bytes().view_bits()[..(bits_to_copy as usize)],
641                    );
642                    // Zero extend to the end of the buffer field
643                    bitslice[(offset + bits_to_copy)..(offset + length)].fill(false);
644                    Ok(())
645                }
646                AmlValue::Boolean(value) => {
647                    bitslice.set(offset, value);
648                    Ok(())
649                }
650                AmlValue::Buffer(value) => {
651                    /*
652                     * When a `Buffer` is written into a `BufferField`, the entire contents are copied into the
653                     * field. If the buffer is smaller than the size of the buffer field, it is zero extended. If
654                     * the buffer is larger, the upper bits are truncated.
655                     * XXX: this behaviour is only explicitly defined in ACPI 2.0+. While undefined in ACPI 1.0,
656                     * we produce the same behaviour there.
657                     */
658                    let value_data = value.lock();
659                    let bits_to_copy = cmp::min(length, value_data.len() * 8);
660                    bitslice[offset..(offset + bits_to_copy)]
661                        .copy_from_bitslice(&value_data.view_bits()[..(bits_to_copy as usize)]);
662                    // Zero extend to the end of the buffer field
663                    bitslice[(offset + bits_to_copy)..(offset + length)].fill(false);
664                    Ok(())
665                }
666                _ => Err(AmlError::TypeCannotBeWrittenToBufferField(value.type_of())),
667            }
668        } else {
669            Err(AmlError::IncompatibleValueConversion {
670                current: self.type_of(),
671                target: AmlType::BufferField,
672            })
673        }
674    }
675
676    /// Logically compare two `AmlValue`s, according to the rules that govern opcodes like `DefLEqual`, `DefLLess`,
677    /// etc. The type of `self` dictates the type that `other` will be converted to, and the method by which the
678    /// values will be compared:
679    ///    - `Integer`s are simply compared by numeric comparison
680    ///    - `String`s and `Buffer`s are compared lexicographically - `other` is compared byte-wise until a byte
681    ///      is discovered that is either less or greater than the corresponding byte of `self`. If the bytes are
682    ///      identical, the lengths are compared. Luckily, the Rust standard library implements lexicographic
683    ///      comparison of strings and `[u8]` for us already.
684    pub fn cmp(
685        &self,
686        other: AmlValue,
687        context: &mut AmlContext,
688    ) -> Result<cmp::Ordering, AmlError> {
689        let self_inner = if self.type_of() == AmlType::FieldUnit {
690            self.read_field(context)?
691        } else {
692            self.clone()
693        };
694
695        match self_inner.type_of() {
696            AmlType::Integer => Ok(self.as_integer(context)?.cmp(&other.as_integer(context)?)),
697            AmlType::Buffer => Ok(self
698                .as_buffer(context)?
699                .lock()
700                .cmp(&other.as_buffer(context)?.lock())),
701            AmlType::String => Ok(self.as_string(context)?.cmp(&other.as_string(context)?)),
702            typ => Err(AmlError::TypeCannotBeCompared(typ)),
703        }
704    }
705}
706
707/// A control method can take up to 7 arguments, each of which is an `AmlValue`.
708#[derive(Clone, Default, Debug)]
709pub struct Args(pub [Option<AmlValue>; 7]);
710
711impl Args {
712    pub const EMPTY: Self = Self([None, None, None, None, None, None, None]);
713
714    pub fn from_list(list: Vec<AmlValue>) -> Result<Args, AmlError> {
715        use core::convert::TryInto;
716
717        if list.len() > 7 {
718            return Err(AmlError::TooManyArgs);
719        }
720
721        let mut args: Vec<Option<AmlValue>> = list.into_iter().map(Option::Some).collect();
722        args.extend(core::iter::repeat(None).take(7 - args.len()));
723        Ok(Args(args.try_into().unwrap()))
724    }
725
726    pub fn arg(&self, arg: ArgNum) -> Result<&AmlValue, AmlError> {
727        if arg > 6 {
728            return Err(AmlError::InvalidArgAccess(arg));
729        }
730
731        self.0[arg as usize]
732            .as_ref()
733            .ok_or(AmlError::InvalidArgAccess(arg))
734    }
735
736    pub fn store_arg(&mut self, arg: ArgNum, value: AmlValue) -> Result<(), AmlError> {
737        if arg > 6 {
738            return Err(AmlError::InvalidArgAccess(arg));
739        }
740
741        self.0[arg as usize] = Some(value);
742        Ok(())
743    }
744}