kernel/acpi/tables/
mod.rs

1pub mod facp;
2
3pub use facp::Facp;
4
5use core::{
6    any::Any,
7    fmt,
8    mem::{self, MaybeUninit},
9    slice,
10};
11
12use alloc::{boxed::Box, vec::Vec};
13use byteorder::{ByteOrder, LittleEndian};
14
15use crate::{
16    cmdline::{self, LogAml},
17    io::{ByteStr, HexArray},
18    memory_management::{memory_layout::physical2virtual, virtual_space::VirtualSpace},
19    multiboot2::MultiBoot2Info,
20    sync::once::OnceLock,
21};
22
23use super::aml::Aml;
24
25const BIOS_RO_MEM_START: u64 = 0x000E0000;
26const BIOS_RO_MEM_END: u64 = 0x000FFFFF;
27
28/// # Safety
29///
30/// Must ensure the `physical_addr` is valid and point to correct DescriptionHeader
31/// Must ensure that the `physical_address` is not used in virtual_space before calling this function
32/// We are using `VirtualSpace` on low kernel addresses (i.e. already mapped by the kernel).
33/// Accessing these addresses manually without `VirtualSpace` may lead to undefined behavior due to aliasing memory referenced by other code
34unsafe fn get_acpi_table_bytes(physical_addr: u64) -> (DescriptionHeader, VirtualSpace<[u8]>) {
35    let header = VirtualSpace::<DescriptionHeader>::new(physical_addr).expect("Failed to map");
36    let len = header.length as usize;
37    let header_copy = *header;
38    drop(header);
39
40    let data_start_phys = physical_addr + mem::size_of::<DescriptionHeader>() as u64;
41    let data_len = len - mem::size_of::<DescriptionHeader>();
42
43    let header_data =
44        VirtualSpace::<u8>::new_slice(data_start_phys, data_len).expect("Failed to get slice");
45
46    // check sum
47    let sum = header_copy
48        .sum()
49        .wrapping_add(header_data.iter().fold(0u8, |acc, &x| acc.wrapping_add(x)));
50    assert_eq!(sum, 0);
51
52    // after this point, the header is valid and can be used safely
53    (header_copy, header_data)
54}
55
56/// Will fill the table from the header data, and zero out remaining bytes if any are left
57///
58/// # Safety
59/// the pointer must be valid and point to a valid table
60/// Also, `<T>` must be valid when some parts of it is zero
61unsafe fn get_table_from_body<T>(body: &[u8]) -> T {
62    let mut our_data_value = MaybeUninit::zeroed();
63    let out_data_slice =
64        slice::from_raw_parts_mut(our_data_value.as_mut_ptr() as *mut u8, mem::size_of::<T>());
65    out_data_slice[..body.len()].copy_from_slice(body);
66
67    our_data_value.assume_init()
68}
69
70/// Will fill the table from the header data, and zero out remaining bytes if any are left
71///
72/// # Safety
73///
74/// type `<T>` must be valid when some parts of it is zero
75///
76/// TODO: should this be unsafe?
77fn get_struct_from_bytes<T>(data: &[u8]) -> T {
78    assert_eq!(data.len(), mem::size_of::<T>());
79
80    let mut our_data_value = MaybeUninit::zeroed();
81    // Safety: it is safe to create a slice of bytes for the struct, since we know the pointer is valid
82    let out_data_slice = unsafe {
83        slice::from_raw_parts_mut(our_data_value.as_mut_ptr() as *mut u8, mem::size_of::<T>())
84    };
85    out_data_slice.copy_from_slice(data);
86
87    // Safety: we are sure that the data is valid, since we assume that in the function doc,
88    // assured from the caller
89    unsafe { our_data_value.assume_init() }
90}
91
92// cache the tables
93static BIOS_TABLES: OnceLock<BiosTables> = OnceLock::new();
94
95// Note: this requires allocation, so it should be called after the heap is initialized
96pub fn init_acpi_tables(multiboot_info: &MultiBoot2Info) -> &'static BiosTables {
97    BIOS_TABLES.get_or_init(|| {
98        let rdsp = multiboot_info
99            .get_most_recent_rsdp()
100            .or_else(|| {
101                // look for RSDP PTR
102                // this is inside the kernel low virtual range, so we can just convert to virtual directly without allocating space
103                let mut rsdp_ptr = physical2virtual(BIOS_RO_MEM_START) as *const u8;
104                let end = physical2virtual(BIOS_RO_MEM_END) as *const u8;
105
106                while rsdp_ptr < end {
107                    // Safety: this is a valid mapped range, as we are sure that the kernel is
108                    // mapped since boot and we are inside the kernel lower range
109                    let str = unsafe { slice::from_raw_parts(rsdp_ptr, 8) };
110                    if str == b"RSD PTR " {
111                        // calculate checksum
112                        // Safety: same as above, this pointer is mapped
113                        let sum = unsafe {
114                            slice::from_raw_parts(rsdp_ptr, 20)
115                                .iter()
116                                .fold(0u8, |acc, &x| acc.wrapping_add(x))
117                        };
118                        if sum == 0 {
119                            // Safety: same as above, this pointer is mapped
120                            let rsdp_ref = unsafe { &*(rsdp_ptr as *const RsdpV2) };
121                            return if rsdp_ref.rsdp_v1.revision >= 2 {
122                                Some(Rsdp::from_v2(rsdp_ref))
123                            } else {
124                                Some(Rsdp::from_v1(&rsdp_ref.rsdp_v1))
125                            };
126                        }
127                    }
128                    // Safety: same as above, this pointer is mapped
129                    rsdp_ptr = unsafe { rsdp_ptr.add(1) };
130                }
131
132                None
133            })
134            .expect("No RSDP found");
135
136        // Safety: this is called only once and we are sure no other call is using the ACPI memory
137        unsafe { BiosTables::new(rdsp) }
138    })
139}
140
141pub fn get_acpi_tables() -> &'static BiosTables {
142    BIOS_TABLES.get()
143}
144
145#[repr(C, packed)]
146pub struct RsdpV1 {
147    signature: ByteStr<[u8; 8]>,
148    checksum: u8,
149    oem_id: ByteStr<[u8; 6]>,
150    revision: u8,
151    rsdt_address: u32,
152}
153
154// used to copy
155#[repr(C, packed)]
156pub struct RsdpV2 {
157    rsdp_v1: RsdpV1,
158    // these are only v2, but its here to make copying easier
159    length: u32,
160    xsdt_address: u64,
161    extended_checksum: u8,
162    reserved: [u8; 3],
163}
164
165/// Represent v2 and above
166#[repr(C, packed)]
167#[derive(Debug, Clone)]
168pub struct Rsdp {
169    pub signature: ByteStr<[u8; 8]>,
170    pub checksum: u8,
171    pub oem_id: ByteStr<[u8; 6]>,
172    pub revision: u8,
173    pub rsdt_address: u32,
174    pub length: u32,
175    pub xsdt_address: u64,
176    pub extended_checksum: u8,
177    pub reserved: [u8; 3],
178}
179
180impl Rsdp {
181    pub fn from_v1(v0: &RsdpV1) -> Self {
182        Self {
183            signature: v0.signature,
184            checksum: v0.checksum,
185            oem_id: v0.oem_id,
186            revision: v0.revision,
187            rsdt_address: v0.rsdt_address,
188            length: 0,
189            xsdt_address: 0,
190            extended_checksum: 0,
191            reserved: [0; 3],
192        }
193    }
194
195    pub fn from_v2(v2: &RsdpV2) -> Self {
196        Self {
197            signature: v2.rsdp_v1.signature,
198            checksum: v2.rsdp_v1.checksum,
199            oem_id: v2.rsdp_v1.oem_id,
200            revision: v2.rsdp_v1.revision,
201            rsdt_address: v2.rsdp_v1.rsdt_address,
202            length: v2.length,
203            xsdt_address: v2.xsdt_address,
204            extended_checksum: v2.extended_checksum,
205            reserved: v2.reserved,
206        }
207    }
208
209    /// allocates a new RDST
210    ///
211    /// # Safety
212    ///
213    /// This should only be called once and not overlapping with any operation done to the region containing ACPI tables
214    /// this uses virtual space for the regions that the `rsdt` is inside and all its other children structures
215    unsafe fn rdst(&self) -> Rsdt {
216        // Safety: here we are the first
217        let (header, body_bytes) = get_acpi_table_bytes(self.rsdt_address as _);
218
219        // we copy the addresses here, we can't sadly use iter to iterate over them since inside `from_physical_ptr` we need to be
220        // sure that we don't own any references to the ACPI memory regions in the `VirtualSpace`
221        let entries_ptrs = body_bytes
222            .chunks(4)
223            .map(|a| u32::from_le_bytes(a.try_into().unwrap()))
224            .filter(|&a| a != 0)
225            .collect::<Vec<_>>();
226
227        // deallocate the virtual space memory, so we can use it again below if regions overlap
228        drop(body_bytes);
229
230        let entries = entries_ptrs
231            .into_iter()
232            // Safety: `from_physical_ptr` require we don't overlap usage of ACPI memory, we are `deallocating` the memory above
233            //         before going into this function, and it will handle its own deallocation, so we are safe on that side
234            .map(|p| unsafe { DescriptorTable::from_physical_ptr(p) })
235            .collect();
236
237        let mut s = Rsdt { header, entries };
238        // add extra entries
239        if let Some(facp) = s.get_table::<Facp>() {
240            if facp.dsdt != 0 {
241                // Safety: same as above, we are sure that we are not overlapping with any ACPI memory
242                s.entries
243                    .push(DescriptorTable::from_physical_ptr(facp.dsdt));
244            }
245        }
246
247        s
248    }
249}
250
251#[derive(Debug, Clone)]
252pub struct Rsdt {
253    pub header: DescriptionHeader,
254    entries: Vec<DescriptorTable>,
255}
256
257impl Rsdt {
258    pub fn get_table<T: Any>(&self) -> Option<&T> {
259        self.entries
260            .iter()
261            .filter_map(|entry| match &entry.body {
262                DescriptorTableBody::Unknown(_) => None,
263                DescriptorTableBody::Apic(a) => Some(a.as_ref() as &dyn Any),
264                DescriptorTableBody::Facp(a) => Some(a.as_ref() as &dyn Any),
265                DescriptorTableBody::Hpet(a) => Some(a.as_ref() as &dyn Any),
266                DescriptorTableBody::Dsdt(a) => Some(a.as_ref() as &dyn Any),
267                DescriptorTableBody::Ssdt(a) => Some(a.as_ref() as &dyn Any),
268                DescriptorTableBody::Bgrt(a) => Some(a.as_ref() as &dyn Any),
269                DescriptorTableBody::Waet(a) => Some(a.as_ref() as &dyn Any),
270                DescriptorTableBody::Srat(a) => Some(a.as_ref() as &dyn Any),
271            })
272            .find_map(|obj| obj.downcast_ref::<T>())
273    }
274
275    pub fn iter_tables<T: Any>(&self) -> impl Iterator<Item = &T> {
276        self.entries
277            .iter()
278            .filter_map(|entry| match &entry.body {
279                DescriptorTableBody::Unknown(_) => None,
280                DescriptorTableBody::Apic(a) => Some(a.as_ref() as &dyn Any),
281                DescriptorTableBody::Facp(a) => Some(a.as_ref() as &dyn Any),
282                DescriptorTableBody::Hpet(a) => Some(a.as_ref() as &dyn Any),
283                DescriptorTableBody::Dsdt(a) => Some(a.as_ref() as &dyn Any),
284                DescriptorTableBody::Ssdt(a) => Some(a.as_ref() as &dyn Any),
285                DescriptorTableBody::Bgrt(a) => Some(a.as_ref() as &dyn Any),
286                DescriptorTableBody::Waet(a) => Some(a.as_ref() as &dyn Any),
287                DescriptorTableBody::Srat(a) => Some(a.as_ref() as &dyn Any),
288            })
289            .filter_map(|obj| obj.downcast_ref::<T>())
290    }
291}
292
293#[repr(C, packed)]
294#[derive(Debug, Clone, Copy)]
295pub struct DescriptionHeader {
296    pub signature: ByteStr<[u8; 4]>,
297    pub length: u32,
298    pub revision: u8,
299    pub checksum: u8,
300    pub oem_id: ByteStr<[u8; 6]>,
301    pub oem_table_id: ByteStr<[u8; 8]>,
302    pub oem_revision: u32,
303    pub creator_id: u32,
304    pub creator_revision: u32,
305}
306
307impl DescriptionHeader {
308    pub fn sum(&self) -> u8 {
309        let mut sum = 0u8;
310        let ptr = self as *const Self as *const u8;
311        for i in 0..mem::size_of::<Self>() {
312            sum = sum.wrapping_add(unsafe { ptr.add(i).read() });
313        }
314        sum
315    }
316}
317
318#[derive(Debug, Clone)]
319pub struct DescriptorTable {
320    pub header: DescriptionHeader,
321    pub body: DescriptorTableBody,
322}
323
324impl DescriptorTable {
325    /// # Safety
326    ///
327    /// This should not overlap any reference to the ACPI memory, it will own a reference to virtual space
328    /// that points to the physical address, and then yields the reference before it returns.
329    /// Thus it must never be called concurrently as well
330    pub unsafe fn from_physical_ptr(ptr: u32) -> Self {
331        // Safety: here we are relying on the caller to ensure that the `ptr` is valid and no one is using ACPI memory
332        let (header, body_bytes) = unsafe { get_acpi_table_bytes(ptr as _) };
333
334        let body = match &header.signature.0 {
335            b"APIC" => DescriptorTableBody::Apic(Box::new(Apic::from_body_bytes(&body_bytes))),
336            b"FACP" => DescriptorTableBody::Facp(Box::new(get_table_from_body(&body_bytes))),
337            b"HPET" => DescriptorTableBody::Hpet(Box::new(get_table_from_body(&body_bytes))),
338            b"DSDT" => DescriptorTableBody::Dsdt(Box::new(Xsdt::from_body_bytes(&body_bytes))),
339            b"SSDT" => DescriptorTableBody::Ssdt(Box::new(Xsdt::from_body_bytes(&body_bytes))),
340            b"BGRT" => DescriptorTableBody::Bgrt(Box::new(get_table_from_body(&body_bytes))),
341            b"WAET" => DescriptorTableBody::Waet(Box::new(get_table_from_body(&body_bytes))),
342            b"SRAT" => DescriptorTableBody::Srat(Box::new(Srat::from_body_bytes(&body_bytes))),
343            _ => DescriptorTableBody::Unknown(HexArray(body_bytes.to_vec())),
344        };
345
346        Self { header, body }
347    }
348}
349
350#[derive(Debug, Clone)]
351pub enum DescriptorTableBody {
352    Apic(Box<Apic>),
353    Facp(Box<Facp>),
354    Hpet(Box<Hpet>),
355    Dsdt(Box<Xsdt>),
356    Ssdt(Box<Xsdt>),
357    Bgrt(Box<Bgrt>),
358    Waet(Box<Waet>),
359    Srat(Box<Srat>),
360    #[allow(dead_code)]
361    Unknown(HexArray<Vec<u8>>),
362}
363
364#[derive(Debug, Clone)]
365#[allow(dead_code)]
366pub struct Apic {
367    pub local_apic_address: u32,
368    pub flags: u32,
369    pub interrupt_controller_structs: Vec<InterruptControllerStruct>,
370}
371
372impl Apic {
373    /// # Safety
374    /// the pointer must be valid and point to a valid table
375    fn from_body_bytes(body: &[u8]) -> Self {
376        let mut apic = Self {
377            local_apic_address: LittleEndian::read_u32(body),
378            flags: LittleEndian::read_u32(&body[4..]),
379            interrupt_controller_structs: Vec::new(),
380        };
381
382        let mut remaining_body = &body[8..];
383        let mut remaining = body.len() - 8;
384        while remaining > 0 {
385            let struct_type = remaining_body[0];
386            let struct_len = remaining_body[1];
387            let struct_bytes = &remaining_body[2..struct_len as usize];
388            apic.interrupt_controller_structs
389                .push(InterruptControllerStruct::from_type_and_bytes(
390                    struct_type,
391                    struct_bytes,
392                ));
393            remaining -= struct_len as usize;
394            remaining_body = &remaining_body[struct_len as usize..];
395        }
396        apic
397    }
398}
399
400#[repr(u8)]
401#[derive(Debug, Clone)]
402#[allow(dead_code)]
403pub enum InterruptControllerStruct {
404    ProcessorLocalApic(ProcessorLocalApic) = 0,
405    IoApic(IoApic) = 1,
406    InterruptSourceOverride(InterruptSourceOverride) = 2,
407    NonMaskableInterrupt(NonMaskableInterrupt) = 3,
408    LocalApicNmi(LocalApicNmi) = 4,
409    LocalApicAddressOverride(LocalApicAddressOverride) = 5,
410    Unknown {
411        struct_type: u8,
412        bytes: HexArray<Vec<u8>>,
413    } = 255,
414}
415
416impl InterruptControllerStruct {
417    fn from_type_and_bytes(struct_type: u8, bytes: &[u8]) -> Self {
418        match struct_type {
419            0 => Self::ProcessorLocalApic(get_struct_from_bytes(bytes)),
420            1 => Self::IoApic(get_struct_from_bytes(bytes)),
421            2 => Self::InterruptSourceOverride(get_struct_from_bytes(bytes)),
422            3 => Self::NonMaskableInterrupt(get_struct_from_bytes(bytes)),
423            4 => Self::LocalApicNmi(get_struct_from_bytes(bytes)),
424            5 => Self::LocalApicAddressOverride(get_struct_from_bytes(bytes)),
425            _ => Self::Unknown {
426                struct_type,
427                bytes: HexArray(bytes.to_vec()),
428            },
429        }
430    }
431}
432
433// extract enum into outside structs
434#[repr(C, packed)]
435#[derive(Debug, Clone)]
436pub struct ProcessorLocalApic {
437    pub acpi_processor_id: u8,
438    pub apic_id: u8,
439    pub flags: u32,
440}
441
442#[repr(C, packed)]
443#[derive(Debug, Clone)]
444pub struct IoApic {
445    pub io_apic_id: u8,
446    pub reserved: u8,
447    pub io_apic_address: u32,
448    pub global_system_interrupt_base: u32,
449}
450
451#[repr(C, packed)]
452#[derive(Debug, Clone)]
453pub struct InterruptSourceOverride {
454    pub bus: u8,
455    pub source: u8,
456    pub global_system_interrupt: u32,
457    pub flags: u16,
458}
459
460#[repr(C, packed)]
461#[derive(Debug, Clone)]
462pub struct NonMaskableInterrupt {
463    pub flags: u16,
464    pub global_system_interrupt: u32,
465}
466
467#[repr(C, packed)]
468#[derive(Debug, Clone)]
469pub struct LocalApicNmi {
470    pub acpi_processor_uid: u8,
471    pub flags: u16,
472    pub local_apic_lint: u8,
473}
474
475#[repr(C, packed)]
476#[derive(Debug, Clone)]
477pub struct LocalApicAddressOverride {
478    pub reserved: u16,
479    pub local_apic_address: u64,
480}
481
482#[derive(Debug, Clone, Copy)]
483#[repr(C, packed)]
484pub struct ApicGenericAddress {
485    pub address_space_id: u8,
486    pub register_bit_width: u8,
487    pub register_bit_offset: u8,
488    pub reserved: u8,
489    pub address: u64,
490}
491impl ApicGenericAddress {
492    fn is_zero(&self) -> bool {
493        self.address == 0
494            && self.address_space_id == 0
495            && self.register_bit_offset == 0
496            && self.register_bit_width == 0
497            && self.reserved == 0
498    }
499}
500
501#[repr(C, packed)]
502#[derive(Debug, Clone)]
503pub struct Hpet {
504    pub event_timer_block_id: u32,
505    pub base_address: ApicGenericAddress,
506    pub hpet_number: u8,
507    pub main_counter_minimum_clock_tick: u16,
508    pub page_protection: u8,
509}
510
511#[derive(Debug, Clone)]
512#[allow(dead_code)]
513/// This is inside DSDT and SSDT
514pub struct Xsdt {
515    pub aml: Aml,
516}
517
518impl Xsdt {
519    fn from_body_bytes(body: &[u8]) -> Self {
520        let aml_code = Aml::parse(body).unwrap();
521        Self { aml: aml_code }
522    }
523}
524
525#[derive(Debug, Clone)]
526#[repr(C, packed)]
527pub struct Bgrt {
528    version: u16,
529    status: u8,
530    image_type: u8,
531    pub image_address: u64,
532    pub image_offset_x: u32,
533    pub image_offset_y: u32,
534}
535
536#[derive(Debug, Clone)]
537#[allow(dead_code)]
538pub struct Waet {
539    emulated_device_flags: u32,
540}
541
542#[derive(Debug, Clone)]
543#[allow(dead_code)]
544pub struct Srat {
545    reserved1: u32,
546    reserved2: u64,
547    static_resource_allocation: Vec<StaticResourceAffinity>,
548}
549
550impl Srat {
551    fn from_body_bytes(body: &[u8]) -> Self {
552        let mut srat = Self {
553            reserved1: LittleEndian::read_u32(body),
554            reserved2: LittleEndian::read_u64(&body[4..]),
555            static_resource_allocation: Vec::new(),
556        };
557
558        let mut remaining_body = &body[12..];
559
560        let mut remaining = body.len() - 12;
561        while remaining > 0 {
562            let struct_type = remaining_body[0];
563            let struct_len = remaining_body[1];
564            let struct_bytes = &remaining_body[2..struct_len as usize];
565            srat.static_resource_allocation
566                .push(StaticResourceAffinity::from_type_and_bytes(
567                    struct_type,
568                    struct_bytes,
569                ));
570            remaining -= struct_len as usize;
571            remaining_body = &remaining_body[struct_len as usize..];
572        }
573        srat
574    }
575}
576
577#[repr(u8)]
578#[derive(Debug, Clone)]
579#[allow(dead_code)]
580pub enum StaticResourceAffinity {
581    ProcessorLocalAcpi(ProcessorLocalAcpiAffinity) = 0,
582    MemoryAffinity(MemoryAffinity) = 1,
583    ProcessorLocalX2Apic(ProcessorLocalX2ApicAffinity) = 2,
584    GiccAffinity(GiccAffinity) = 3,
585    GicInterruptTranslationService(GicInterruptTranslationServiceAffinity) = 4,
586    GenericInitiatorAffinity(GenericInitiatorAffinity) = 5,
587    Unknown {
588        struct_type: u8,
589        bytes: HexArray<Vec<u8>>,
590    } = 255,
591}
592
593impl StaticResourceAffinity {
594    fn from_type_and_bytes(struct_type: u8, bytes: &[u8]) -> Self {
595        match struct_type {
596            0 => Self::ProcessorLocalAcpi(get_struct_from_bytes(bytes)),
597            1 => Self::MemoryAffinity(get_struct_from_bytes(bytes)),
598            2 => Self::ProcessorLocalX2Apic(get_struct_from_bytes(bytes)),
599            3 => Self::GiccAffinity(get_struct_from_bytes(bytes)),
600            4 => Self::GicInterruptTranslationService(get_struct_from_bytes(bytes)),
601            5 => Self::GenericInitiatorAffinity(get_struct_from_bytes(bytes)),
602            _ => Self::Unknown {
603                struct_type,
604                bytes: HexArray(bytes.to_vec()),
605            },
606        }
607    }
608}
609
610#[derive(Debug, Clone)]
611#[repr(C, packed)]
612pub struct ProcessorLocalAcpiAffinity {
613    proximity_domain_low: u8,
614    apic_id: u8,
615    flags: u32,
616    local_sapic_eid: u8,
617    proximity_domain_high: [u8; 3],
618    clock_domain: u32,
619}
620
621#[derive(Debug, Clone)]
622#[repr(C, packed)]
623pub struct MemoryAffinity {
624    proximity_domain: u32,
625    reserved1: u16,
626    base_address_low: u32,
627    base_address_high: u32,
628    length_low: u32,
629    length_high: u32,
630    reserved2: u32,
631    flags: u32,
632    reserved3: u64,
633}
634
635#[derive(Debug, Clone)]
636#[repr(C, packed)]
637pub struct ProcessorLocalX2ApicAffinity {
638    reserved1: u16,
639    proximity_domain: u32,
640    x2apic_id: u32,
641    flags: u32,
642    clock_domain: u32,
643    reserved2: u32,
644}
645
646#[derive(Debug, Clone)]
647#[repr(C, packed)]
648pub struct GiccAffinity {
649    proximity_domain: u32,
650    acpi_processor_uid: u32,
651    flags: u32,
652    clock_domain: u32,
653}
654
655#[derive(Debug, Clone)]
656#[repr(C, packed)]
657pub struct GicInterruptTranslationServiceAffinity {
658    proximity_domain: u32,
659    reserved1: u16,
660    its_id: u32,
661}
662
663#[derive(Debug, Clone)]
664#[repr(C, packed)]
665pub struct GenericInitiatorAffinity {
666    reserved1: u8,
667    device_handle_type: u8,
668    proximity_domain: u32,
669    device_handle: [u8; 16],
670    flags: u32,
671    reserved2: u32,
672}
673
674#[derive(Debug, Clone)]
675pub struct BiosTables {
676    pub rsdp: Rsdp,
677    pub rsdt: Rsdt,
678}
679
680impl BiosTables {
681    /// # Safety
682    ///
683    /// This should only be called once and not overlapping with any operation done to the region containing ACPI tables
684    pub unsafe fn new(rsdp: Rsdp) -> Self {
685        Self {
686            rsdt: rsdp.rdst(),
687            rsdp,
688        }
689    }
690}
691
692impl fmt::Display for BiosTables {
693    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
694        writeln!(f, "RSDP: {:X?}", self.rsdp)?;
695        writeln!(f, "RSDT: {:X?}", self.rsdt.header)?;
696        for entry in &self.rsdt.entries {
697            match &entry.body {
698                DescriptorTableBody::Dsdt(data) | DescriptorTableBody::Ssdt(data) => {
699                    writeln!(f, "{:X?}", entry.header)?;
700
701                    match cmdline::cmdline().log_aml {
702                        LogAml::Normal => {
703                            writeln!(f, "AML: \n{:#}", data.aml.code())?;
704                        }
705                        LogAml::Structured => {
706                            writeln!(f, "AML: \n{:#}", data.aml.structured())?;
707                        }
708                        LogAml::Off => {}
709                    }
710                }
711                DescriptorTableBody::Unknown(_) => {
712                    writeln!(f, "  {:X?}", entry.header)?;
713                    writeln!(f, "  {:X?}", entry.body)?;
714                }
715                _ => {
716                    writeln!(f, "{:X?}", entry.body)?;
717                }
718            }
719        }
720        Ok(())
721    }
722}