acpi/
dsdt.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4pub mod helpers;
5pub mod objects;
6pub mod ops;
7pub mod resources;
8
9pub use helpers::*;
10use memory_range::MemoryRange;
11pub use objects::*;
12pub use ops::*;
13pub use resources::*;
14use x86defs::apic::APIC_BASE_ADDRESS;
15use zerocopy::FromBytes;
16use zerocopy::Immutable;
17use zerocopy::IntoBytes;
18use zerocopy::KnownLayout;
19
20#[repr(C, packed)]
21#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
22pub struct DescriptionHeader {
23    pub signature: u32,
24    _length: u32,
25    pub revision: u8,
26    _checksum: u8,
27    pub oem_id: [u8; 6],
28    pub oem_table_id: u64,
29    pub oem_revision: u32,
30    pub creator_id: u32,
31    pub creator_rev: u32,
32}
33
34pub struct Method {
35    pub name: [u8; 4],
36    pub sync_level: u8,
37    pub is_serialized: bool,
38    pub arg_count: u8,
39    operations: Vec<u8>,
40}
41
42impl Method {
43    pub fn new(name: &[u8; 4]) -> Self {
44        let local_name: [u8; 4] = [name[0], name[1], name[2], name[3]];
45        Self {
46            name: local_name,
47            sync_level: 0,
48            is_serialized: false,
49            arg_count: 0,
50            operations: vec![],
51        }
52    }
53
54    pub fn set_arg_count(&mut self, arg_count: u8) {
55        self.arg_count = arg_count;
56    }
57
58    pub fn add_operation(&mut self, op: &impl OperationObject) {
59        op.append_to_vec(&mut self.operations);
60    }
61}
62
63impl DsdtObject for Method {
64    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
65        byte_stream.push(0x14);
66        byte_stream.extend_from_slice(&encode_package_len(5 + self.operations.len()));
67        byte_stream.extend_from_slice(&self.name);
68        byte_stream.push(
69            self.sync_level << 4 | if self.is_serialized { 1 << 3 } else { 0 } | self.arg_count,
70        );
71        byte_stream.extend_from_slice(&self.operations);
72    }
73}
74
75pub struct EisaId(pub [u8; 7]);
76
77impl DsdtObject for EisaId {
78    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
79        let mut id: [u8; 4] = [0; 4];
80        id[0] = (self.0[0] - b'@') << 2 | (self.0[1] - b'@') >> 3;
81        id[1] = (self.0[1] & 7) << 5 | (self.0[2] - b'@');
82        id[2] = char_to_hex(self.0[3]) << 4 | char_to_hex(self.0[4]);
83        id[3] = char_to_hex(self.0[5]) << 4 | char_to_hex(self.0[6]);
84        byte_stream.append(&mut encode_integer(u32::from_le_bytes(id) as u64));
85    }
86}
87
88pub struct Device {
89    name: Vec<u8>,
90    objects: Vec<u8>,
91}
92
93impl Device {
94    pub fn new(name: &[u8]) -> Self {
95        Self {
96            name: encode_name(name),
97            objects: vec![],
98        }
99    }
100
101    pub fn add_object(&mut self, obj: &impl DsdtObject) {
102        obj.append_to_vec(&mut self.objects);
103    }
104}
105
106impl DsdtObject for Device {
107    // A device object consists of the extended identifier (0x5b 0x82) followed by the length, the name and then the
108    // contained objects.
109    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
110        byte_stream.push(0x5b);
111        byte_stream.push(0x82);
112        let length = self.name.len() + self.objects.len();
113        byte_stream.extend_from_slice(&encode_package_len(length));
114        byte_stream.extend_from_slice(&self.name);
115        byte_stream.extend_from_slice(&self.objects);
116    }
117}
118
119pub struct PciRoutingTableEntry {
120    pub address: u32,
121    pub pin: u8,
122    pub source: Option<Vec<u8>>,
123    pub source_index: u32,
124}
125
126pub struct PciRoutingTable {
127    entries: Vec<PciRoutingTableEntry>,
128}
129
130impl PciRoutingTable {
131    pub fn new() -> Self {
132        Self {
133            entries: Vec::new(),
134        }
135    }
136
137    pub fn add_entry(&mut self, entry: PciRoutingTableEntry) {
138        self.entries.push(entry);
139    }
140}
141
142impl DsdtObject for PciRoutingTable {
143    fn append_to_vec(&self, byte_stream: &mut Vec<u8>) {
144        let mut table_data: Vec<u8> = Vec::with_capacity(self.entries.len() * 10);
145        for entry in self.entries.iter() {
146            let mut elem_data: Vec<u8> = Vec::with_capacity(
147                9 + match &entry.source {
148                    Some(name) => name.len(),
149                    None => 1,
150                },
151            );
152            elem_data.extend_from_slice(&encode_dword(entry.address));
153            elem_data.push(entry.pin);
154            match &entry.source {
155                Some(name) => elem_data.extend_from_slice(name),
156                None => elem_data.push(0),
157            }
158            elem_data.extend_from_slice(&encode_dword(entry.source_index));
159            StructuredPackage {
160                elem_count: 4,
161                elem_data,
162            }
163            .append_to_vec(&mut table_data);
164        }
165
166        NamedObject::new(
167            b"_PRT",
168            &StructuredPackage {
169                elem_count: self.entries.len() as u8,
170                elem_data: table_data,
171            },
172        )
173        .append_to_vec(byte_stream);
174    }
175}
176
177pub struct Dsdt {
178    description_header: DescriptionHeader,
179    objects: Vec<u8>,
180}
181
182impl Dsdt {
183    pub fn new() -> Self {
184        Self {
185            description_header: DescriptionHeader {
186                signature: u32::from_le_bytes(*b"DSDT"),
187                _length: 0,
188                revision: 2,
189                _checksum: 0,
190                oem_id: *b"MSFTVM",
191                oem_table_id: 0x313054445344, // b'DSDT01'
192                oem_revision: 1,
193                creator_id: u32::from_le_bytes(*b"MSFT"),
194                creator_rev: 0x5000000,
195            },
196            objects: vec![],
197        }
198    }
199
200    pub fn to_bytes(&self) -> Vec<u8> {
201        let mut byte_stream = Vec::new();
202        byte_stream.extend_from_slice(self.description_header.as_bytes());
203        byte_stream.extend_from_slice(&self.objects);
204
205        let length = byte_stream.len();
206        byte_stream[4..8].copy_from_slice(&u32::try_from(length).unwrap().to_le_bytes());
207        let mut checksum: u8 = 0;
208        for byte in &byte_stream {
209            checksum = checksum.wrapping_add(*byte);
210        }
211
212        byte_stream[9] = (!checksum).wrapping_add(1);
213        byte_stream
214    }
215
216    pub fn add_object(&mut self, obj: &impl DsdtObject) {
217        obj.append_to_vec(&mut self.objects);
218    }
219
220    /// Add an APIC device to the DSDT with the following ASL code:
221    /// ```text
222    /// Device(\_SB.APIC)
223    /// {
224    ///     Name(_HID, EISAID("PNP0003"))
225    ///     Name(_CRS,
226    ///         ResourceTemplate()
227    ///         {
228    ///             Memory32Fixed(ReadWrite, 0xfee00000, 0x1000)
229    ///             Memory32Fixed(ReadWrite, 0xfec00000, 0x1000)
230    ///         })
231    /// }
232    /// ```
233    pub fn add_apic(&mut self) {
234        let mut apic = Device::new(b"\\_SB.APIC");
235        apic.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0003")));
236        let mut apic_crs = CurrentResourceSettings::new();
237        apic_crs.add_resource(&Memory32Fixed::new(APIC_BASE_ADDRESS, 0x1000, true));
238        apic_crs.add_resource(&Memory32Fixed::new(0xfec00000, 0x1000, true));
239        apic.add_object(&apic_crs);
240        self.add_object(&apic);
241    }
242
243    /// Add a 16650A compatible UART to the DSDT with the following ASL code:
244    /// ```text
245    /// Device(<name>)
246    /// {
247    ///     Name(_HID, EISAID("PNP0501")) // 16550A-compatible COM port
248    ///     Name(_DDN, <ddn>)
249    ///     Name(_UID, <uid>)
250    ///     Name(_CRS, ResourceTemplate()
251    ///     {
252    ///         IO(Decode16, <io_base>, <io_base>, 1, 8)
253    ///         Interrupt(ResourceConsumer, Edge, ActiveHigh, Exclusive)
254    ///             {<irq>}
255    ///     })
256    /// }
257    /// ```
258    pub fn add_uart(&mut self, name: &[u8], ddn: &[u8], uid: u64, io_base: u16, irq: u32) {
259        let mut uart = Device::new(name);
260        uart.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0501")));
261        uart.add_object(&NamedString::new(b"_DDN", ddn));
262        uart.add_object(&NamedInteger::new(b"_UID", uid));
263        let mut uart_crs = CurrentResourceSettings::new();
264        uart_crs.add_resource(&IoPort::new(io_base, io_base, 8));
265        let mut intr = Interrupt::new(irq);
266        intr.is_edge_triggered = true;
267        uart_crs.add_resource(&intr);
268        uart.add_object(&uart_crs);
269        self.add_object(&uart);
270    }
271
272    /// Add an ACPI module device to describe the low and high MMIO regions.
273    /// This is used when PCI is not present so that VMBus can find MMIO space.
274    ///
275    /// ```text
276    /// Device(\_SB.VMOD)
277    /// {
278    ///     Name(_HID, "ACPI0004")
279    ///     Name(_UID, 0)
280    ///     Name(_CRS, ResourceTemplate()
281    ///     {
282    ///         // Low gap
283    ///         QWORDMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
284    ///         // Granularity Min          Max T       Translation Range (Length = Max-Min+1)
285    ///             0,         <low_min>,   <low_max>,  0,          <dynamic>,,,
286    ///             MEM6)   // Name declaration for this descriptor
287    ///
288    ///         // High gap
289    ///         QWORDMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
290    ///         // Granularity Min          Max T       Translation Range (Length = Max-Min+1)
291    ///             0,         <high_min>,  <high_max>, 0,          <dynamic>,,,
292    ///             MEM6)   // Name declaration for this descriptor
293    ///     })
294    /// }
295    /// ```
296    pub fn add_mmio_module(&mut self, low: MemoryRange, high: MemoryRange) {
297        let mut vmod = Device::new(b"\\_SB.VMOD");
298        vmod.add_object(&NamedString::new(b"_HID", b"ACPI0004"));
299        vmod.add_object(&NamedInteger::new(b"_UID", 0));
300        let mut vmod_crs = CurrentResourceSettings::new();
301        vmod_crs.add_resource(&QwordMemory::new(low.start(), low.end() - low.start()));
302        vmod_crs.add_resource(&QwordMemory::new(high.start(), high.end() - high.start()));
303        vmod.add_object(&vmod_crs);
304        self.add_object(&vmod);
305    }
306
307    /// Adds a PCI bus with the specified MMIO ranges.
308    ///
309    /// ```text
310    /// Device(\_SB.PCI0)
311    /// {
312    ///     Name(_HID, PNP0A03)
313    ///     Name(_CRS, ResourceTemplate()
314    ///     {
315    ///         WordBusNumber(...) // Bus translation info
316    ///         IO(Decode16, 0xcf8, 0xcf8) // IO port
317    ///         QWordMemory() // Low gap
318    ///         QWordMemory() // High gap
319    ///     })
320    ///     // PCI routing table
321    ///     Name(_PRT, Package{
322    ///         Package{<address>, <PCI pin>, 0, <interrupt>},
323    ///         ...
324    ///     })
325    /// }
326    /// ```
327    pub fn add_pci(
328        &mut self,
329        low: MemoryRange,
330        high: MemoryRange,
331        // array of ((device, function), line)
332        legacy_interrupts: &[((u8, Option<u8>), u32)],
333    ) {
334        let mut pci0 = Device::new(b"\\_SB.PCI0");
335        pci0.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0A03")));
336        // FUTURE: when implementing PCIe, switch _HID over to "PNP0A08", and a
337        // _CID for "PNP0A03"
338
339        // OS negotiation for control of the bus. See https://uefi.org/specs/ACPI/6.4/06_Device_Configuration/Device_Configuration.html#osc-operating-system-capabilities
340        // TODO: Lots of work needed for _OSC.
341        let mut empty_os_method = Method::new(b"_OSC");
342        empty_os_method.set_arg_count(4);
343        empty_os_method.add_operation(&ReturnOp {
344            result: Buffer(0x10u64.to_le_bytes()).to_bytes(),
345        });
346        pci0.add_object(&empty_os_method);
347        let mut prt = PciRoutingTable::new();
348        for &((device, function), line) in legacy_interrupts {
349            prt.add_entry(PciRoutingTableEntry {
350                address: ((device as u32) << 16) | function.map(|x| x as u32).unwrap_or(0xffff),
351                pin: 0,
352                source: None,
353                source_index: line, // Interrupt line
354            });
355        }
356        pci0.add_object(&prt);
357        let mut crs = CurrentResourceSettings::new();
358        crs.add_resource(&BusNumber::new(0, 1));
359        crs.add_resource(&IoPort::new(0xcf8, 0xcf8, 8));
360        crs.add_resource(&QwordMemory::new(low.start(), low.end() - low.start()));
361        crs.add_resource(&QwordMemory::new(high.start(), high.end() - high.start()));
362        pci0.add_object(&crs);
363        self.add_object(&pci0);
364    }
365
366    /// Add a VMBUS device to the DSDT.
367    ///
368    /// If `in_pci`, then enumerate the device under PCI0. Otherwise, enumerate
369    /// it under the VMOD module created by `add_mmio_module`.
370    ///
371    /// ```text
372    /// Device(\_SB.VMOD.VMBS)
373    /// {
374    ///     Name(STA, 0xF)
375    ///     Name(_ADR, 0x00)
376    ///     Name(_DDN, "VMBUS")
377    ///     Name(_HID, "VMBus")
378    ///     Name(_UID, 0)
379    ///     Method(_DIS, 0) { And(STA, 0xD, STA) }
380    ///     Method(_PS0, 0) { Or(STA, 0xF, STA) }
381    ///     Method(_STA, 0)
382    ///     {
383    ///         return(STA)
384    ///     }
385    ///
386    ///     Name(_PS3, 0)
387    /// }
388    /// ```
389    pub fn add_vmbus(&mut self, in_pci: bool) {
390        let name = if in_pci {
391            b"\\_SB.PCI0.VMBS"
392        } else {
393            b"\\_SB.VMOD.VMBS"
394        };
395        let mut vmbs = Device::new(name);
396        vmbs.add_object(&NamedInteger::new(b"STA", 0xf));
397        vmbs.add_object(&NamedInteger::new(b"_ADR", 0));
398        vmbs.add_object(&NamedString::new(b"_DDN", b"VMBUS"));
399        vmbs.add_object(&NamedString::new(b"_HID", b"VMBus"));
400        vmbs.add_object(&NamedInteger::new(b"_UID", 0));
401        let op = AndOp {
402            operand1: b"STA_".to_vec(),
403            operand2: encode_integer(13),
404            target_name: b"STA_".to_vec(),
405        };
406        let mut method = Method::new(b"_DIS");
407        method.add_operation(&op);
408        vmbs.add_object(&method);
409        let op = OrOp {
410            operand1: b"STA_".to_vec(),
411            operand2: encode_integer(15),
412            target_name: b"STA_".to_vec(),
413        };
414        let mut method = Method::new(b"_PS0");
415        method.add_operation(&op);
416        vmbs.add_object(&method);
417        let op = ReturnOp {
418            result: b"STA_".to_vec(),
419        };
420        let mut method = Method::new(b"_STA");
421        method.add_operation(&op);
422        vmbs.add_object(&method);
423        vmbs.add_object(&NamedInteger::new(b"_PS3", 0));
424        // On linux, the vmbus driver will fail if the _CRS section is not present.
425        vmbs.add_object(&CurrentResourceSettings::new());
426        self.add_object(&vmbs);
427    }
428
429    /// Add an RTC device with the following ASL code:
430    /// ```text
431    /// Device(\_SB.RTC0)
432    /// {
433    ///     Name(_HID, EISAID("PNP0B00")) // AT real-time clock
434    ///     Name(_UID, 0)
435    ///     Name(_CRS, ResourceTemplate()
436    ///     {
437    ///         IO(Decode16, 0x70, 0x70, 0, 0x2)
438    ///         Interrupt(ResourceConsumer, Edge, ActiveHigh, Exclusive) {8}
439    ///     })
440    /// }
441    /// ```
442    pub fn add_rtc(&mut self) {
443        let mut rtc = Device::new(b"\\_SB.RTC0");
444        rtc.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0B00")));
445        rtc.add_object(&NamedInteger::new(b"_UID", 0));
446        let mut rtc_crs = CurrentResourceSettings::new();
447        let mut ioport = IoPort::new(0x70, 0x70, 2);
448        ioport.alignment = 0;
449        rtc_crs.add_resource(&ioport);
450        let mut intr = Interrupt::new(8);
451        intr.is_edge_triggered = true;
452        rtc_crs.add_resource(&intr);
453        rtc.add_object(&rtc_crs);
454        self.add_object(&rtc);
455    }
456}
457
458#[cfg(test)]
459mod tests {
460    use super::*;
461
462    pub fn verify_header(bytes: &[u8]) {
463        assert!(bytes.len() >= 36);
464
465        // signature
466        assert_eq!(bytes[0], b'D');
467        assert_eq!(bytes[1], b'S');
468        assert_eq!(bytes[2], b'D');
469        assert_eq!(bytes[3], b'T');
470
471        // length
472        let dsdt_len = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
473        assert_eq!(dsdt_len as usize, bytes.len());
474
475        // revision
476        assert_eq!(bytes[8], 2);
477
478        // Validate checksum bytes[9] by verifying content adds to zero.
479        let mut checksum: u8 = 0;
480        for byte in bytes.iter() {
481            checksum = checksum.wrapping_add(*byte);
482        }
483        assert_eq!(checksum, 0);
484
485        // oem_id
486        assert_eq!(bytes[10], b'M');
487        assert_eq!(bytes[11], b'S');
488        assert_eq!(bytes[12], b'F');
489        assert_eq!(bytes[13], b'T');
490        assert_eq!(bytes[14], b'V');
491        assert_eq!(bytes[15], b'M');
492
493        // oem_table_id
494        assert_eq!(bytes[16], b'D');
495        assert_eq!(bytes[17], b'S');
496        assert_eq!(bytes[18], b'D');
497        assert_eq!(bytes[19], b'T');
498        assert_eq!(bytes[20], b'0');
499        assert_eq!(bytes[21], b'1');
500        assert_eq!(bytes[22], 0);
501        assert_eq!(bytes[23], 0);
502
503        // oem_revision
504        let oem_revision = u32::from_le_bytes(bytes[24..28].try_into().unwrap());
505        assert_eq!(oem_revision, 1);
506
507        // creator_id
508        assert_eq!(bytes[28], b'M');
509        assert_eq!(bytes[29], b'S');
510        assert_eq!(bytes[30], b'F');
511        assert_eq!(bytes[31], b'T');
512
513        // creator_rev
514        let creator_rev = u32::from_le_bytes(bytes[32..36].try_into().unwrap());
515        assert_eq!(creator_rev, 0x5000000);
516    }
517
518    pub fn verify_expected_bytes(actual: &[u8], expected: &[u8]) {
519        assert_eq!(
520            actual.len(),
521            expected.len(),
522            "Length of buffer does not match"
523        );
524        for i in 0..actual.len() {
525            assert_eq!(actual[i], expected[i], "Mismatch at index {}", i);
526        }
527    }
528
529    #[test]
530    fn verify_eisaid() {
531        let eisa_id = EisaId(*b"PNP0003");
532        let bytes = eisa_id.to_bytes();
533        verify_expected_bytes(&bytes, &[0xc, 0x41, 0xd0, 0, 0x3]);
534    }
535
536    #[test]
537    fn verify_method() {
538        let op = AndOp {
539            operand1: vec![b'S', b'T', b'A', b'_'],
540            operand2: encode_integer(13),
541            target_name: vec![b'S', b'T', b'A', b'_'],
542        };
543        let mut method = Method::new(b"_DIS");
544        method.add_operation(&op);
545        let bytes = method.to_bytes();
546        verify_expected_bytes(
547            &bytes,
548            &[
549                0x14, 0x11, 0x5F, 0x44, 0x49, 0x53, 0x00, 0x7b, b'S', b'T', b'A', b'_', 0x0a, 0x0d,
550                b'S', b'T', b'A', b'_',
551            ],
552        );
553    }
554
555    #[test]
556    fn verify_device_object() {
557        let package = Package(vec![0]);
558        let nobj = NamedObject::new(b"FOO", &package);
559        let mut device = Device::new(b"DEV");
560        device.add_object(&nobj);
561        let bytes = device.to_bytes();
562        verify_expected_bytes(
563            &bytes,
564            &[
565                0x5b, 0x82, 14, b'D', b'E', b'V', b'_', 8, b'F', b'O', b'O', b'_', 0x12, 3, 1, 0,
566            ],
567        );
568    }
569
570    #[test]
571    fn verify_simple_table() {
572        let mut dsdt = Dsdt::new();
573        let nobj = NamedObject::new(b"_S0", &Package(vec![0, 0]));
574        dsdt.add_object(&nobj);
575        let bytes = dsdt.to_bytes();
576        verify_header(&bytes);
577        verify_expected_bytes(&bytes[36..], &[8, b'_', b'S', b'0', b'_', 0x12, 4, 2, 0, 0]);
578    }
579
580    #[test]
581    fn verify_table() {
582        let mut dsdt = Dsdt::new();
583        dsdt.add_object(&NamedObject::new(b"\\_S0", &Package(vec![0, 0])));
584        dsdt.add_object(&NamedObject::new(b"\\_S5", &Package(vec![0, 0])));
585
586        let mut apic = Device::new(b"\\_SB.APIC");
587        apic.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0003")));
588        let mut apic_crs = CurrentResourceSettings::new();
589        apic_crs.add_resource(&Memory32Fixed::new(0xfee00000, 0x1000, true));
590        apic_crs.add_resource(&Memory32Fixed::new(0xfec00000, 0x1000, true));
591        apic.add_object(&apic_crs);
592        dsdt.add_object(&apic);
593
594        let mut uart = Device::new(b"\\_SB.UAR1");
595        uart.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0501")));
596        uart.add_object(&NamedString::new(b"_DDN", b"COM1"));
597        uart.add_object(&NamedInteger::new(b"_UID", 1));
598        let mut uart_crs = CurrentResourceSettings::new();
599        uart_crs.add_resource(&IoPort::new(0x3f8, 0x3f8, 8));
600        let mut intr = Interrupt::new(4);
601        intr.is_edge_triggered = true;
602        uart_crs.add_resource(&intr);
603        uart.add_object(&uart_crs);
604        dsdt.add_object(&uart);
605
606        let mut vmod = Device::new(b"\\_SB.VMOD");
607        vmod.add_object(&NamedString::new(b"_HID", b"ACPI0004"));
608        vmod.add_object(&NamedInteger::new(b"_UID", 0));
609        let mut vmod_crs = CurrentResourceSettings::new();
610        vmod_crs.add_resource(&QwordMemory::new(0x100000000, 0x100000000));
611        vmod.add_object(&vmod_crs);
612        dsdt.add_object(&vmod);
613
614        let mut vmbs = Device::new(b"\\_SB.VMOD.VMBS");
615        vmbs.add_object(&NamedInteger::new(b"STA", 0xf));
616        vmbs.add_object(&NamedInteger::new(b"_ADR", 0));
617        vmbs.add_object(&NamedString::new(b"_DDN", b"VMBUS"));
618        vmbs.add_object(&NamedString::new(b"_HID", b"VMBus"));
619        vmbs.add_object(&NamedInteger::new(b"_UID", 0));
620        let op = AndOp {
621            operand1: vec![b'S', b'T', b'A', b'_'],
622            operand2: encode_integer(13),
623            target_name: vec![b'S', b'T', b'A', b'_'],
624        };
625        let mut method = Method::new(b"_DIS");
626        method.add_operation(&op);
627        vmbs.add_object(&method);
628        let op = OrOp {
629            operand1: vec![b'S', b'T', b'A', b'_'],
630            operand2: encode_integer(15),
631            target_name: vec![b'S', b'T', b'A', b'_'],
632        };
633        let mut method = Method::new(b"_PS0");
634        method.add_operation(&op);
635        vmbs.add_object(&method);
636        let op = ReturnOp {
637            result: vec![b'S', b'T', b'A', b'_'],
638        };
639        let mut method = Method::new(b"_STA");
640        method.add_operation(&op);
641        vmbs.add_object(&method);
642        vmbs.add_object(&NamedInteger::new(b"_PS3", 0));
643        dsdt.add_object(&vmbs);
644
645        let mut rtc = Device::new(b"\\_SB.RTC0");
646        rtc.add_object(&NamedObject::new(b"_HID", &EisaId(*b"PNP0B00")));
647        rtc.add_object(&NamedInteger::new(b"_UID", 0));
648        let mut rtc_crs = CurrentResourceSettings::new();
649        let mut ioport = IoPort::new(0x70, 0x70, 2);
650        ioport.alignment = 0;
651        rtc_crs.add_resource(&ioport);
652        let mut intr = Interrupt::new(8);
653        intr.is_edge_triggered = true;
654        rtc_crs.add_resource(&intr);
655        rtc.add_object(&rtc_crs);
656        dsdt.add_object(&rtc);
657
658        for proc_index in 1..3 {
659            let mut proc = Device::new(format!("P{:03}", proc_index).as_bytes());
660            proc.add_object(&NamedString::new(b"_HID", b"ACPI0007"));
661            proc.add_object(&NamedInteger::new(b"_UID", proc_index as u64));
662            let mut method = Method::new(b"_STA");
663            method.add_operation(&ReturnOp {
664                result: encode_integer(0xf),
665            });
666            proc.add_object(&method);
667            dsdt.add_object(&proc);
668        }
669
670        let bytes = dsdt.to_bytes();
671        verify_header(&bytes);
672        verify_expected_bytes(
673            &bytes[36..],
674            &[
675                0x08, 0x5C, 0x5F, 0x53, 0x30, 0x5F, 0x12, 0x04, 0x02, 0x00, 0x00, 0x08, 0x5C, 0x5F,
676                0x53, 0x35, 0x5F, 0x12, 0x04, 0x02, 0x00, 0x00, 0x5B, 0x82, 0x38, 0x5C, 0x2E, 0x5F,
677                0x53, 0x42, 0x5F, 0x41, 0x50, 0x49, 0x43, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41,
678                0xD0, 0x00, 0x03, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x1D, 0x0A, 0x1A, 0x86, 0x09,
679                0x00, 0x01, 0x00, 0x00, 0xE0, 0xFE, 0x00, 0x10, 0x00, 0x00, 0x86, 0x09, 0x00, 0x01,
680                0x00, 0x00, 0xC0, 0xFE, 0x00, 0x10, 0x00, 0x00, 0x79, 0x00, 0x5B, 0x82, 0x43, 0x04,
681                0x5C, 0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x55, 0x41, 0x52, 0x31, 0x08, 0x5F, 0x48, 0x49,
682                0x44, 0x0C, 0x41, 0xD0, 0x05, 0x01, 0x08, 0x5F, 0x44, 0x44, 0x4E, 0x0D, 0x43, 0x4F,
683                0x4D, 0x31, 0x00, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x01, 0x08, 0x5F, 0x43, 0x52, 0x53,
684                0x11, 0x16, 0x0A, 0x13, 0x47, 0x01, 0xF8, 0x03, 0xF8, 0x03, 0x01, 0x08, 0x89, 0x06,
685                0x00, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x79, 0x00, 0x5B, 0x82, 0x4A, 0x05, 0x5C,
686                0x2E, 0x5F, 0x53, 0x42, 0x5F, 0x56, 0x4D, 0x4F, 0x44, 0x08, 0x5F, 0x48, 0x49, 0x44,
687                0x0D, 0x41, 0x43, 0x50, 0x49, 0x30, 0x30, 0x30, 0x34, 0x00, 0x08, 0x5F, 0x55, 0x49,
688                0x44, 0x00, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11, 0x33, 0x0A, 0x30, 0x8A, 0x2B, 0x00,
689                0x00, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
690                0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00,
691                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
692                0x00, 0x79, 0x00, 0x5B, 0x82, 0x42, 0x07, 0x5C, 0x2F, 0x03, 0x5F, 0x53, 0x42, 0x5F,
693                0x56, 0x4D, 0x4F, 0x44, 0x56, 0x4D, 0x42, 0x53, 0x08, 0x53, 0x54, 0x41, 0x5F, 0x0A,
694                0x0F, 0x08, 0x5F, 0x41, 0x44, 0x52, 0x00, 0x08, 0x5F, 0x44, 0x44, 0x4E, 0x0D, 0x56,
695                0x4D, 0x42, 0x55, 0x53, 0x00, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0D, 0x56, 0x4D, 0x42,
696                0x75, 0x73, 0x00, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x00, 0x14, 0x11, 0x5F, 0x44, 0x49,
697                0x53, 0x00, 0x7B, 0x53, 0x54, 0x41, 0x5F, 0x0A, 0x0D, 0x53, 0x54, 0x41, 0x5F, 0x14,
698                0x11, 0x5F, 0x50, 0x53, 0x30, 0x00, 0x7D, 0x53, 0x54, 0x41, 0x5F, 0x0A, 0x0F, 0x53,
699                0x54, 0x41, 0x5F, 0x14, 0x0B, 0x5F, 0x53, 0x54, 0x41, 0x00, 0xA4, 0x53, 0x54, 0x41,
700                0x5F, 0x08, 0x5F, 0x50, 0x53, 0x33, 0x00, 0x5B, 0x82, 0x37, 0x5C, 0x2E, 0x5F, 0x53,
701                0x42, 0x5F, 0x52, 0x54, 0x43, 0x30, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0C, 0x41, 0xD0,
702                0x0B, 0x00, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x00, 0x08, 0x5F, 0x43, 0x52, 0x53, 0x11,
703                0x16, 0x0A, 0x13, 0x47, 0x01, 0x70, 0x00, 0x70, 0x00, 0x00, 0x02, 0x89, 0x06, 0x00,
704                0x03, 0x01, 0x08, 0x00, 0x00, 0x00, 0x79, 0x00, 0x5B, 0x82, 0x24, 0x50, 0x30, 0x30,
705                0x31, 0x08, 0x5F, 0x48, 0x49, 0x44, 0x0D, 0x41, 0x43, 0x50, 0x49, 0x30, 0x30, 0x30,
706                0x37, 0x00, 0x08, 0x5F, 0x55, 0x49, 0x44, 0x01, 0x14, 0x09, 0x5F, 0x53, 0x54, 0x41,
707                0x00, 0xA4, 0x0A, 0x0F, 0x5B, 0x82, 0x25, 0x50, 0x30, 0x30, 0x32, 0x08, 0x5F, 0x48,
708                0x49, 0x44, 0x0D, 0x41, 0x43, 0x50, 0x49, 0x30, 0x30, 0x30, 0x37, 0x00, 0x08, 0x5F,
709                0x55, 0x49, 0x44, 0x0A, 0x02, 0x14, 0x09, 0x5F, 0x53, 0x54, 0x41, 0x00, 0xA4, 0x0A,
710                0x0F,
711            ],
712        );
713    }
714}