Skip to main content

rdrive/probe/
acpi.rs

1use alloc::{
2    collections::BTreeSet,
3    format,
4    rc::Rc,
5    string::{String, ToString},
6    vec::Vec,
7};
8use core::{ptr::NonNull, str::FromStr};
9
10use acpi::{
11    AcpiError, AcpiTables, Handler, PhysicalMapping,
12    aml::{
13        AmlError, Interpreter,
14        namespace::{AmlName, NamespaceLevelKind},
15        object::Object,
16        pci_routing::{IrqDescriptor, PciRoutingTable, Pin},
17        resource::{InterruptPolarity, InterruptTrigger},
18    },
19    platform::{
20        AcpiPlatform,
21        interrupt::{InterruptModel, Polarity, TriggerMode},
22        pci::PciConfigRegions,
23    },
24};
25pub use rdif_base::irq::{AcpiGsiRoute, AcpiIrqPolarity, AcpiIrqTrigger};
26use spin::{Mutex, Once};
27
28use crate::{
29    DeviceId, PlatformDevice,
30    error::DriverError,
31    probe::{OnProbeError, ProbeError, pci::PciAddress},
32    register::{DriverRegister, ProbeKind},
33};
34
35pub const PCI_INTX_VECTOR_BASE: usize = 0x30;
36const PCI_ROOT_FALLBACK_PATHS: &[&str] = &["\\_SB.PCI0", "\\_SB.PCI1", "\\_SB.PC00", "\\_SB.PC01"];
37
38static SYSTEM: Once<System> = Once::new();
39static NULL_LOCK: Mutex<()> = Mutex::new(());
40
41#[derive(Clone, Copy)]
42pub struct AcpiRoot {
43    pub rsdp: usize,
44    pub phys_to_virt: fn(usize) -> *mut u8,
45}
46
47impl core::fmt::Debug for AcpiRoot {
48    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
49        f.debug_struct("AcpiRoot")
50            .field("rsdp", &self.rsdp)
51            .finish_non_exhaustive()
52    }
53}
54
55impl AcpiRoot {
56    pub const fn new(rsdp: usize, phys_to_virt: fn(usize) -> *mut u8) -> Self {
57        Self { rsdp, phys_to_virt }
58    }
59
60    pub const fn identity(rsdp: usize) -> Self {
61        Self::new(rsdp, identity_phys_to_virt)
62    }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct AcpiId {
67    pub hid: &'static str,
68    pub cids: &'static [&'static str],
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub struct AcpiPciEcam {
73    pub segment_group: u16,
74    pub bus_start: u8,
75    pub bus_end: u8,
76    pub base_address: u64,
77}
78
79impl AcpiPciEcam {
80    pub fn size(&self) -> usize {
81        let buses = usize::from(self.bus_end.saturating_sub(self.bus_start)) + 1;
82        buses << 20
83    }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub struct AcpiIoApic {
88    pub id: u8,
89    pub address: u32,
90    pub gsi_base: u32,
91    pub redirection_entries: u8,
92}
93
94impl AcpiIoApic {
95    fn contains_gsi(self, gsi: u32) -> bool {
96        let start = self.gsi_base;
97        let end = start.saturating_add(u32::from(self.redirection_entries));
98        (start..end).contains(&gsi)
99    }
100}
101
102#[derive(Debug, Clone)]
103pub struct AcpiRouting {
104    io_apics: Vec<AcpiIoApic>,
105}
106
107impl AcpiRouting {
108    pub const fn new() -> Self {
109        Self {
110            io_apics: Vec::new(),
111        }
112    }
113
114    pub fn add_io_apic(&mut self, io_apic: AcpiIoApic) {
115        self.io_apics.push(io_apic);
116    }
117
118    pub fn io_apics(&self) -> &[AcpiIoApic] {
119        &self.io_apics
120    }
121
122    pub fn resolve_gsi(&self, gsi: u32) -> Option<AcpiGsiRoute> {
123        let io_apic = self
124            .io_apics
125            .iter()
126            .copied()
127            .find(|io_apic| io_apic.contains_gsi(gsi))?;
128        let input = u8::try_from(gsi.saturating_sub(io_apic.gsi_base)).ok()?;
129        Some(AcpiGsiRoute {
130            gsi,
131            vector: PCI_INTX_VECTOR_BASE + gsi as usize,
132            controller_id: io_apic.id,
133            controller_address: io_apic.address,
134            controller_input: input,
135            trigger: AcpiIrqTrigger::Level,
136            polarity: AcpiIrqPolarity::ActiveLow,
137        })
138    }
139
140    pub fn resolve_vector(&self, vector: usize) -> Option<AcpiGsiRoute> {
141        let gsi = vector.checked_sub(PCI_INTX_VECTOR_BASE)?;
142        self.resolve_gsi(gsi as u32)
143    }
144}
145
146impl Default for AcpiRouting {
147    fn default() -> Self {
148        Self::new()
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::{AcpiIoApic, AcpiIrqPolarity, AcpiIrqTrigger, AcpiRouting};
155
156    #[test]
157    fn ioapic_routes_map_gsi_to_stable_vector() {
158        let mut routing = AcpiRouting::new();
159        routing.add_io_apic(AcpiIoApic {
160            id: 0,
161            address: 0xfec0_0000,
162            gsi_base: 0,
163            redirection_entries: 24,
164        });
165
166        let irq = routing
167            .resolve_gsi(16)
168            .expect("gsi 16 should be handled by the IOAPIC");
169        assert_eq!(irq.gsi, 16);
170        assert_eq!(irq.controller_id, 0);
171        assert_eq!(irq.controller_address, 0xfec0_0000);
172        assert_eq!(irq.controller_input, 16);
173        assert_eq!(irq.vector, 0x40);
174        assert_eq!(irq.trigger, AcpiIrqTrigger::Level);
175        assert_eq!(irq.polarity, AcpiIrqPolarity::ActiveLow);
176        assert!(routing.resolve_gsi(24).is_none());
177    }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub struct AcpiPciIrqRoute {
182    pub address: PciAddress,
183    pub interrupt_pin: u8,
184    pub gsi: AcpiGsiRoute,
185}
186
187pub struct AcpiInfo<'a> {
188    pub root: &'a System,
189    pub path: &'a str,
190}
191
192pub type FnOnProbe = fn(AcpiInfo<'_>, PlatformDevice) -> Result<(), OnProbeError>;
193
194pub fn check_root(root: AcpiRoot) -> Result<(), DriverError> {
195    if root.rsdp == 0 {
196        return Err(acpi_error(AcpiError::NoValidRsdp));
197    }
198    root.tables().map(|_| ()).map_err(acpi_error)
199}
200
201pub fn init(root: AcpiRoot) -> Result<(), DriverError> {
202    let system = System::new(root)?;
203    info!(
204        "ACPI initialized: {} PCI ECAM region(s), {} IOAPIC(s)",
205        system.pci_ecam_regions().len(),
206        system.routing().io_apics().len()
207    );
208    SYSTEM.call_once(|| system);
209    Ok(())
210}
211
212pub(crate) fn try_probe_register(
213    register: &DriverRegister,
214) -> Option<Result<alloc::vec::Vec<Result<(), OnProbeError>>, ProbeError>> {
215    SYSTEM.get().map(|system| system.probe_register(register))
216}
217
218pub(crate) fn try_system() -> Option<&'static System> {
219    SYSTEM.get()
220}
221
222pub fn with_acpi<T>(f: impl FnOnce(&System) -> T) -> Option<T> {
223    try_system().map(f)
224}
225
226fn acpi_error(err: AcpiError) -> DriverError {
227    DriverError::Unknown(format!("{err:?}"))
228}
229
230fn on_probe_error(err: impl core::fmt::Debug) -> OnProbeError {
231    OnProbeError::other(format!("{err:?}"))
232}
233
234fn identity_phys_to_virt(paddr: usize) -> *mut u8 {
235    paddr as *mut u8
236}
237
238#[derive(Clone)]
239struct AcpiHandler {
240    root: AcpiRoot,
241    pci_ecam_regions: Rc<Vec<AcpiPciEcam>>,
242}
243
244impl AcpiHandler {
245    fn new(root: AcpiRoot, pci_ecam_regions: Vec<AcpiPciEcam>) -> Self {
246        Self {
247            root,
248            pci_ecam_regions: Rc::new(pci_ecam_regions),
249        }
250    }
251
252    fn virt_addr(&self, physical_address: usize) -> usize {
253        (self.root.phys_to_virt)(physical_address) as usize
254    }
255
256    fn pci_config_ptr(
257        &self,
258        address: acpi::PciAddress,
259        offset: u16,
260        width: usize,
261    ) -> Option<*mut u8> {
262        let offset = usize::from(offset);
263        if offset.checked_add(width)? > 4096 {
264            return None;
265        }
266
267        let bus = address.bus();
268        let region = self.pci_ecam_regions.iter().find(|region| {
269            address.segment() == region.segment_group
270                && bus >= region.bus_start
271                && bus <= region.bus_end
272        })?;
273        let bus_offset = usize::from(bus - region.bus_start) << 20;
274        let device_offset = usize::from(address.device()) << 15;
275        let function_offset = usize::from(address.function()) << 12;
276        let physical_address =
277            region.base_address as usize + bus_offset + device_offset + function_offset + offset;
278
279        Some((self.root.phys_to_virt)(physical_address))
280    }
281}
282
283impl Handler for AcpiHandler {
284    unsafe fn map_physical_region<T>(
285        &self,
286        physical_address: usize,
287        size: usize,
288    ) -> PhysicalMapping<Self, T> {
289        PhysicalMapping {
290            physical_start: physical_address,
291            virtual_start: NonNull::new(self.virt_addr(physical_address) as *mut T)
292                .expect("ACPI physical mapping must not be null"),
293            region_length: size,
294            mapped_length: size,
295            handler: self.clone(),
296        }
297    }
298
299    fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
300
301    fn read_u8(&self, address: usize) -> u8 {
302        unsafe { (self.virt_addr(address) as *const u8).read_volatile() }
303    }
304
305    fn read_u16(&self, address: usize) -> u16 {
306        unsafe { (self.virt_addr(address) as *const u16).read_volatile() }
307    }
308
309    fn read_u32(&self, address: usize) -> u32 {
310        unsafe { (self.virt_addr(address) as *const u32).read_volatile() }
311    }
312
313    fn read_u64(&self, address: usize) -> u64 {
314        unsafe { (self.virt_addr(address) as *const u64).read_volatile() }
315    }
316
317    fn write_u8(&self, address: usize, value: u8) {
318        unsafe { (self.virt_addr(address) as *mut u8).write_volatile(value) }
319    }
320
321    fn write_u16(&self, address: usize, value: u16) {
322        unsafe { (self.virt_addr(address) as *mut u16).write_volatile(value) }
323    }
324
325    fn write_u32(&self, address: usize, value: u32) {
326        unsafe { (self.virt_addr(address) as *mut u32).write_volatile(value) }
327    }
328
329    fn write_u64(&self, address: usize, value: u64) {
330        unsafe { (self.virt_addr(address) as *mut u64).write_volatile(value) }
331    }
332
333    fn read_io_u8(&self, port: u16) -> u8 {
334        read_io_u8(port)
335    }
336
337    fn read_io_u16(&self, port: u16) -> u16 {
338        read_io_u16(port)
339    }
340
341    fn read_io_u32(&self, port: u16) -> u32 {
342        read_io_u32(port)
343    }
344
345    fn write_io_u8(&self, port: u16, value: u8) {
346        write_io_u8(port, value);
347    }
348
349    fn write_io_u16(&self, port: u16, value: u16) {
350        write_io_u16(port, value);
351    }
352
353    fn write_io_u32(&self, port: u16, value: u32) {
354        write_io_u32(port, value);
355    }
356
357    fn read_pci_u8(&self, address: acpi::PciAddress, offset: u16) -> u8 {
358        if let Some(ptr) = self.pci_config_ptr(address, offset, 1) {
359            return unsafe { ptr.read_volatile() };
360        }
361        pci_legacy_read_u8(address, offset).unwrap_or(u8::MAX)
362    }
363
364    fn read_pci_u16(&self, address: acpi::PciAddress, offset: u16) -> u16 {
365        let lo = u16::from(self.read_pci_u8(address, offset));
366        let hi = u16::from(self.read_pci_u8(address, offset.saturating_add(1)));
367        lo | (hi << 8)
368    }
369
370    fn read_pci_u32(&self, address: acpi::PciAddress, offset: u16) -> u32 {
371        let b0 = u32::from(self.read_pci_u8(address, offset));
372        let b1 = u32::from(self.read_pci_u8(address, offset.saturating_add(1)));
373        let b2 = u32::from(self.read_pci_u8(address, offset.saturating_add(2)));
374        let b3 = u32::from(self.read_pci_u8(address, offset.saturating_add(3)));
375        b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
376    }
377
378    fn write_pci_u8(&self, address: acpi::PciAddress, offset: u16, value: u8) {
379        if let Some(ptr) = self.pci_config_ptr(address, offset, 1) {
380            unsafe { ptr.write_volatile(value) };
381            return;
382        }
383        pci_legacy_write_u8(address, offset, value);
384    }
385
386    fn write_pci_u16(&self, address: acpi::PciAddress, offset: u16, value: u16) {
387        self.write_pci_u8(address, offset, value as u8);
388        self.write_pci_u8(address, offset.saturating_add(1), (value >> 8) as u8);
389    }
390
391    fn write_pci_u32(&self, address: acpi::PciAddress, offset: u16, value: u32) {
392        self.write_pci_u8(address, offset, value as u8);
393        self.write_pci_u8(address, offset.saturating_add(1), (value >> 8) as u8);
394        self.write_pci_u8(address, offset.saturating_add(2), (value >> 16) as u8);
395        self.write_pci_u8(address, offset.saturating_add(3), (value >> 24) as u8);
396    }
397
398    fn nanos_since_boot(&self) -> u64 {
399        0
400    }
401
402    fn stall(&self, microseconds: u64) {
403        for _ in 0..microseconds.saturating_mul(100) {
404            core::hint::spin_loop();
405        }
406    }
407
408    fn sleep(&self, milliseconds: u64) {
409        self.stall(milliseconds.saturating_mul(1000));
410    }
411
412    fn create_mutex(&self) -> acpi::Handle {
413        acpi::Handle(0)
414    }
415
416    fn acquire(&self, _mutex: acpi::Handle, _timeout: u16) -> Result<(), acpi::aml::AmlError> {
417        let _guard = NULL_LOCK.lock();
418        Ok(())
419    }
420
421    fn release(&self, _mutex: acpi::Handle) {}
422}
423
424impl AcpiRoot {
425    fn handler(self) -> AcpiHandler {
426        AcpiHandler::new(self, Vec::new())
427    }
428
429    fn handler_with_pci_ecam(self, pci_ecam_regions: Vec<AcpiPciEcam>) -> AcpiHandler {
430        AcpiHandler::new(self, pci_ecam_regions)
431    }
432
433    fn tables(self) -> Result<AcpiTables<AcpiHandler>, AcpiError> {
434        unsafe { AcpiTables::from_rsdp(self.handler(), self.rsdp) }
435    }
436}
437
438pub struct System {
439    ecam_regions: Vec<AcpiPciEcam>,
440    routing: AcpiRouting,
441    pci: Option<AcpiPciNamespace>,
442    probed_names: Mutex<BTreeSet<&'static str>>,
443}
444
445unsafe impl Send for System {}
446unsafe impl Sync for System {}
447
448struct AcpiPciNamespace {
449    interpreter: Interpreter<AcpiHandler>,
450    roots: Vec<AcpiPciRoot>,
451}
452
453struct AcpiPciRoot {
454    segment: u16,
455    bus: u8,
456    path: String,
457    prt: Option<PciRoutingTable>,
458}
459
460impl System {
461    pub fn new(root: AcpiRoot) -> Result<Self, DriverError> {
462        let tables = root.tables().map_err(acpi_error)?;
463        let ecam_regions = read_pci_ecam_regions(&tables)?;
464        let routing = read_interrupt_routing(&tables)?;
465        let pci = match read_pci_namespace(root, ecam_regions.clone()) {
466            Ok(pci) => Some(pci),
467            Err(err) => {
468                warn!("failed to discover ACPI PCI namespace: {err:?}");
469                None
470            }
471        };
472
473        Ok(Self {
474            ecam_regions,
475            routing,
476            pci,
477            probed_names: Mutex::new(BTreeSet::new()),
478        })
479    }
480
481    pub fn pci_ecam_regions(&self) -> &[AcpiPciEcam] {
482        &self.ecam_regions
483    }
484
485    pub fn routing(&self) -> &AcpiRouting {
486        &self.routing
487    }
488
489    pub fn pci_irq_for_endpoint(
490        &self,
491        address: PciAddress,
492        interrupt_pin: u8,
493    ) -> Result<Option<AcpiPciIrqRoute>, OnProbeError> {
494        let Some(mut irq) = self.resolve_endpoint_gsi(address, interrupt_pin)? else {
495            return Ok(None);
496        };
497        let Some(gsi) = irq_descriptor_gsi(&irq) else {
498            return Err(OnProbeError::other(format!(
499                "ACPI PCI endpoint {} pin {} returned an invalid IRQ descriptor: {:?}",
500                address, interrupt_pin, irq
501            )));
502        };
503        irq.irq = gsi;
504
505        let Some(route) = self.routing.resolve_gsi(gsi) else {
506            return Err(OnProbeError::other(format!(
507                "ACPI GSI {} for PCI endpoint {} is not covered by an IOAPIC",
508                gsi, address
509            )));
510        };
511
512        Ok(Some(AcpiPciIrqRoute {
513            address,
514            interrupt_pin,
515            gsi: AcpiGsiRoute {
516                trigger: irq_trigger(irq.trigger),
517                polarity: irq_polarity(irq.polarity),
518                ..route
519            },
520        }))
521    }
522
523    fn resolve_endpoint_gsi(
524        &self,
525        address: PciAddress,
526        interrupt_pin: u8,
527    ) -> Result<Option<IrqDescriptor>, OnProbeError> {
528        let pin = acpi_pin(interrupt_pin)?;
529        let Some(pci) = &self.pci else {
530            return Ok(None);
531        };
532        let roots = self.pci_root_candidates(address, pci);
533        if roots.is_empty() {
534            return Ok(None);
535        }
536
537        for root in roots {
538            let Some(prt) = &root.prt else {
539                continue;
540            };
541
542            match prt.route(
543                u16::from(address.device()),
544                u16::from(address.function()),
545                pin,
546                &pci.interpreter,
547            ) {
548                Ok(route) => return Ok(Some(route)),
549                Err(AmlError::PrtNoEntry) => {}
550                Err(err) => return Err(on_probe_error(err)),
551            }
552        }
553
554        Ok(None)
555    }
556
557    fn pci_root_candidates<'a>(
558        &self,
559        address: PciAddress,
560        pci: &'a AcpiPciNamespace,
561    ) -> Vec<&'a AcpiPciRoot> {
562        let mut roots = Vec::new();
563        if let Some(root) = pci
564            .roots
565            .iter()
566            .find(|root| root.segment == address.segment() && root.bus == address.bus())
567            .or_else(|| {
568                pci.roots
569                    .iter()
570                    .find(|root| root.segment == address.segment() && root.bus == 0)
571            })
572        {
573            roots.push(root);
574        }
575        for path in PCI_ROOT_FALLBACK_PATHS {
576            if let Some(root) = pci.roots.iter().find(|root| root.path == *path)
577                && !roots.iter().any(|candidate| candidate.path == root.path)
578            {
579                roots.push(root);
580            }
581        }
582        roots
583    }
584
585    fn probe_register(
586        &self,
587        register: &DriverRegister,
588    ) -> Result<Vec<Result<(), OnProbeError>>, ProbeError> {
589        let mut out = Vec::new();
590        for probe in register.probe_kinds {
591            let ProbeKind::Acpi { ids, on_probe } = probe else {
592                continue;
593            };
594            if ids.is_empty() {
595                continue;
596            }
597            if self.probed_names.lock().contains(register.name) {
598                continue;
599            }
600
601            let desc = crate::Descriptor {
602                name: register.name,
603                device_id: DeviceId::new(),
604                irq_parent: None,
605            };
606            let info = AcpiInfo {
607                root: self,
608                path: "\\",
609            };
610            let res = on_probe(info, PlatformDevice::new(desc));
611            if res.is_ok() {
612                self.probed_names.lock().insert(register.name);
613            }
614            out.push(res);
615        }
616        Ok(out)
617    }
618}
619
620fn read_pci_ecam_regions(
621    tables: &AcpiTables<AcpiHandler>,
622) -> Result<Vec<AcpiPciEcam>, DriverError> {
623    let regions = PciConfigRegions::new(tables).map_err(acpi_error)?;
624    Ok(regions
625        .regions
626        .iter()
627        .map(|region| AcpiPciEcam {
628            segment_group: region.pci_segment_group,
629            bus_start: region.bus_number_start,
630            bus_end: region.bus_number_end,
631            base_address: region.base_address,
632        })
633        .collect())
634}
635
636fn read_interrupt_routing(tables: &AcpiTables<AcpiHandler>) -> Result<AcpiRouting, DriverError> {
637    let (model, _) = InterruptModel::new(tables).map_err(acpi_error)?;
638    let mut routing = AcpiRouting::new();
639    if let InterruptModel::Apic(apic) = model {
640        for io_apic in &apic.io_apics {
641            routing.add_io_apic(AcpiIoApic {
642                id: io_apic.id,
643                address: io_apic.address,
644                gsi_base: io_apic.global_system_interrupt_base,
645                redirection_entries: 24,
646            });
647        }
648    }
649    Ok(routing)
650}
651
652fn read_pci_namespace(
653    root: AcpiRoot,
654    ecam_regions: Vec<AcpiPciEcam>,
655) -> Result<AcpiPciNamespace, AcpiError> {
656    let handler = root.handler_with_pci_ecam(ecam_regions);
657    let tables = unsafe { AcpiTables::from_rsdp(handler.clone(), root.rsdp) }?;
658    let platform = AcpiPlatform::new(tables, handler)?;
659    let interpreter = Interpreter::new_from_platform(&platform)?;
660    interpreter.initialize_namespace();
661
662    let mut roots = Vec::new();
663    {
664        let mut namespace = interpreter.namespace.lock().clone();
665        namespace
666            .traverse(|path, level| {
667                if level.kind == NamespaceLevelKind::Device && is_pci_root(&interpreter, path) {
668                    let segment =
669                        eval_integer_child(&interpreter, path, "_SEG")?.unwrap_or(0) as u16;
670                    let bus = eval_integer_child(&interpreter, path, "_BBN")?.unwrap_or(0) as u8;
671                    roots.push(AcpiPciRoot {
672                        segment,
673                        bus,
674                        path: path.as_string(),
675                        prt: None,
676                    });
677                }
678                Ok(true)
679            })
680            .map_err(AcpiError::Aml)?;
681    }
682
683    for root in &mut roots {
684        root.prt = read_pci_routing_table(&interpreter, &root.path)?;
685    }
686    for path in PCI_ROOT_FALLBACK_PATHS {
687        if roots.iter().any(|root| root.path == *path) {
688            continue;
689        }
690        let Some(prt) = read_pci_routing_table(&interpreter, path)? else {
691            continue;
692        };
693        roots.push(AcpiPciRoot {
694            segment: 0,
695            bus: 0,
696            path: path.to_string(),
697            prt: Some(prt),
698        });
699    }
700
701    Ok(AcpiPciNamespace { interpreter, roots })
702}
703
704fn read_pci_routing_table(
705    interpreter: &Interpreter<AcpiHandler>,
706    root_path: &str,
707) -> Result<Option<PciRoutingTable>, AcpiError> {
708    let prt_path = AmlName::from_str(&format!("{root_path}._PRT")).map_err(AcpiError::Aml)?;
709    match PciRoutingTable::from_prt_path(prt_path, interpreter) {
710        Ok(prt) => Ok(Some(prt)),
711        Err(AmlError::ObjectDoesNotExist(_)) | Err(AmlError::LevelDoesNotExist(_)) => Ok(None),
712        Err(err) => Err(AcpiError::Aml(err)),
713    }
714}
715
716fn is_pci_root(interpreter: &Interpreter<AcpiHandler>, path: &AmlName) -> bool {
717    has_pci_root_id(interpreter, path, "_HID") || has_pci_root_id(interpreter, path, "_CID")
718}
719
720fn has_pci_root_id(interpreter: &Interpreter<AcpiHandler>, path: &AmlName, name: &str) -> bool {
721    let Ok(Some(value)) = eval_child(interpreter, path, name) else {
722        return false;
723    };
724    object_matches_pci_root_id(&value)
725}
726
727fn object_matches_pci_root_id(value: &Object) -> bool {
728    match value {
729        Object::String(id) => matches!(id.as_str(), "PNP0A03" | "PNP0A08"),
730        Object::Integer(id) => {
731            let id = decode_eisa_id(*id as u32);
732            matches!(id.as_deref(), Some("PNP0A03" | "PNP0A08"))
733        }
734        Object::Package(values) => values.iter().any(|value| object_matches_pci_root_id(value)),
735        _ => false,
736    }
737}
738
739fn eval_integer_child(
740    interpreter: &Interpreter<AcpiHandler>,
741    path: &AmlName,
742    name: &str,
743) -> Result<Option<u64>, AmlError> {
744    eval_child(interpreter, path, name)?
745        .map(|value| value.as_integer())
746        .transpose()
747}
748
749fn eval_child(
750    interpreter: &Interpreter<AcpiHandler>,
751    path: &AmlName,
752    name: &str,
753) -> Result<Option<Rc<Object>>, AmlError> {
754    let child = AmlName::from_str(name)?.resolve(path)?;
755    match interpreter.evaluate_if_present(child, Vec::new())? {
756        Some(value) => Ok(Some(Rc::new((*value).clone()))),
757        None => Ok(None),
758    }
759}
760
761fn decode_eisa_id(raw: u32) -> Option<String> {
762    if raw == 0 {
763        return None;
764    }
765    let chars = [
766        (((raw >> 26) & 0x1f) as u8).wrapping_add(b'@'),
767        (((raw >> 21) & 0x1f) as u8).wrapping_add(b'@'),
768        (((raw >> 16) & 0x1f) as u8).wrapping_add(b'@'),
769    ];
770    if !chars.iter().all(u8::is_ascii_uppercase) {
771        return None;
772    }
773    Some(format!(
774        "{}{}{}{:04X}",
775        chars[0] as char,
776        chars[1] as char,
777        chars[2] as char,
778        raw & 0xffff
779    ))
780}
781
782fn acpi_pin(interrupt_pin: u8) -> Result<Pin, OnProbeError> {
783    match interrupt_pin {
784        1 => Ok(Pin::IntA),
785        2 => Ok(Pin::IntB),
786        3 => Ok(Pin::IntC),
787        4 => Ok(Pin::IntD),
788        _ => Err(OnProbeError::other(format!(
789            "invalid PCI interrupt pin {interrupt_pin}"
790        ))),
791    }
792}
793
794fn irq_descriptor_gsi(descriptor: &IrqDescriptor) -> Option<u32> {
795    let irq = descriptor.irq;
796    if !descriptor.is_consumer && irq.count_ones() == 1 && irq <= u16::MAX as u32 {
797        Some(irq.trailing_zeros())
798    } else {
799        Some(irq)
800    }
801}
802
803fn irq_trigger(trigger: InterruptTrigger) -> AcpiIrqTrigger {
804    match trigger {
805        InterruptTrigger::Edge => AcpiIrqTrigger::Edge,
806        InterruptTrigger::Level => AcpiIrqTrigger::Level,
807    }
808}
809
810fn irq_polarity(polarity: InterruptPolarity) -> AcpiIrqPolarity {
811    match polarity {
812        InterruptPolarity::ActiveHigh => AcpiIrqPolarity::ActiveHigh,
813        InterruptPolarity::ActiveLow => AcpiIrqPolarity::ActiveLow,
814    }
815}
816
817#[cfg(target_arch = "x86_64")]
818fn pci_legacy_read_u8(address: acpi::PciAddress, offset: u16) -> Option<u8> {
819    let value = pci_legacy_read_aligned_u32(address, offset)?;
820    let shift = u32::from(offset & 0b11) * 8;
821    Some((value >> shift) as u8)
822}
823
824#[cfg(not(target_arch = "x86_64"))]
825fn pci_legacy_read_u8(_address: acpi::PciAddress, _offset: u16) -> Option<u8> {
826    None
827}
828
829#[cfg(target_arch = "x86_64")]
830fn pci_legacy_write_u8(address: acpi::PciAddress, offset: u16, value: u8) {
831    let Some(old) = pci_legacy_read_aligned_u32(address, offset) else {
832        return;
833    };
834    let shift = u32::from(offset & 0b11) * 8;
835    let mask = 0xff_u32 << shift;
836    let new = (old & !mask) | (u32::from(value) << shift);
837    pci_legacy_write_aligned_u32(address, offset, new);
838}
839
840#[cfg(not(target_arch = "x86_64"))]
841fn pci_legacy_write_u8(_address: acpi::PciAddress, _offset: u16, _value: u8) {}
842
843#[cfg(target_arch = "x86_64")]
844fn pci_legacy_config_address(address: acpi::PciAddress, offset: u16) -> Option<u32> {
845    if address.segment() != 0 || offset >= 256 {
846        return None;
847    }
848
849    Some(
850        0x8000_0000
851            | (u32::from(address.bus()) << 16)
852            | (u32::from(address.device()) << 11)
853            | (u32::from(address.function()) << 8)
854            | u32::from(offset & !0b11),
855    )
856}
857
858#[cfg(target_arch = "x86_64")]
859fn pci_legacy_read_aligned_u32(address: acpi::PciAddress, offset: u16) -> Option<u32> {
860    let config_address = pci_legacy_config_address(address, offset)?;
861    unsafe {
862        x86::io::outl(0xcf8, config_address);
863        Some(x86::io::inl(0xcfc))
864    }
865}
866
867#[cfg(target_arch = "x86_64")]
868fn pci_legacy_write_aligned_u32(address: acpi::PciAddress, offset: u16, value: u32) {
869    if let Some(config_address) = pci_legacy_config_address(address, offset) {
870        unsafe {
871            x86::io::outl(0xcf8, config_address);
872            x86::io::outl(0xcfc, value);
873        }
874    }
875}
876
877pub fn acpi_trigger(trigger: TriggerMode) -> AcpiIrqTrigger {
878    match trigger {
879        TriggerMode::Edge => AcpiIrqTrigger::Edge,
880        TriggerMode::Level => AcpiIrqTrigger::Level,
881        _ => AcpiIrqTrigger::Level,
882    }
883}
884
885pub fn acpi_polarity(polarity: Polarity) -> AcpiIrqPolarity {
886    match polarity {
887        Polarity::ActiveHigh => AcpiIrqPolarity::ActiveHigh,
888        Polarity::ActiveLow => AcpiIrqPolarity::ActiveLow,
889        _ => AcpiIrqPolarity::ActiveLow,
890    }
891}
892
893#[cfg(target_arch = "x86_64")]
894fn read_io_u8(port: u16) -> u8 {
895    unsafe { x86::io::inb(port) }
896}
897
898#[cfg(not(target_arch = "x86_64"))]
899fn read_io_u8(_port: u16) -> u8 {
900    0
901}
902
903#[cfg(target_arch = "x86_64")]
904fn read_io_u16(port: u16) -> u16 {
905    unsafe { x86::io::inw(port) }
906}
907
908#[cfg(not(target_arch = "x86_64"))]
909fn read_io_u16(_port: u16) -> u16 {
910    0
911}
912
913#[cfg(target_arch = "x86_64")]
914fn read_io_u32(port: u16) -> u32 {
915    unsafe { x86::io::inl(port) }
916}
917
918#[cfg(not(target_arch = "x86_64"))]
919fn read_io_u32(_port: u16) -> u32 {
920    0
921}
922
923#[cfg(target_arch = "x86_64")]
924fn write_io_u8(port: u16, value: u8) {
925    unsafe { x86::io::outb(port, value) }
926}
927
928#[cfg(not(target_arch = "x86_64"))]
929fn write_io_u8(_port: u16, _value: u8) {}
930
931#[cfg(target_arch = "x86_64")]
932fn write_io_u16(port: u16, value: u16) {
933    unsafe { x86::io::outw(port, value) }
934}
935
936#[cfg(not(target_arch = "x86_64"))]
937fn write_io_u16(_port: u16, _value: u16) {}
938
939#[cfg(target_arch = "x86_64")]
940fn write_io_u32(port: u16, value: u32) {
941    unsafe { x86::io::outl(port, value) }
942}
943
944#[cfg(not(target_arch = "x86_64"))]
945fn write_io_u32(_port: u16, _value: u32) {}