fdt_parser/cache/node/
pci.rs

1use core::{
2    fmt::Debug,
3    ops::{Deref, Range},
4};
5
6use crate::{cache::node::NodeBase, FdtError, Phandle};
7use alloc::{vec, vec::Vec};
8
9#[derive(Clone, Debug, PartialEq)]
10pub enum PciSpace {
11    IO,
12    Memory32,
13    Memory64,
14}
15
16#[derive(Clone, Debug, PartialEq)]
17pub struct PciRange {
18    pub space: PciSpace,
19    pub bus_address: u64,
20    pub cpu_address: u64,
21    pub size: u64,
22    pub prefetchable: bool,
23}
24
25#[derive(Clone, Debug)]
26pub struct PciInterruptMap {
27    pub child_address: Vec<u32>,
28    pub child_irq: Vec<u32>,
29    pub interrupt_parent: Phandle,
30    pub parent_irq: Vec<u32>,
31}
32
33#[derive(Clone, Debug, PartialEq)]
34pub struct PciInterruptInfo {
35    pub irqs: Vec<u32>,
36}
37
38#[derive(Clone)]
39pub struct Pci {
40    node: NodeBase,
41}
42
43impl Pci {
44    pub(crate) fn new(node: NodeBase) -> Self {
45        Pci { node }
46    }
47
48    pub fn interrupt_cells(&self) -> u32 {
49        self.find_property("#interrupt-cells")
50            .and_then(|prop| prop.u32().ok())
51            .unwrap_or(1) // Default to 1 interrupt cell for PCI
52    }
53
54    /// Get the interrupt-map-mask property if present
55    pub fn interrupt_map_mask(&self) -> Option<Vec<u32>> {
56        self.node
57            .find_property("interrupt-map-mask")
58            .and_then(|prop| {
59                let mut data = prop.data.buffer();
60                let mut mask = Vec::new();
61                while !data.remain().as_ref().is_empty() {
62                    match data.take_u32() {
63                        Ok(value) => mask.push(value),
64                        Err(_) => return None,
65                    }
66                }
67                Some(mask)
68            })
69    }
70
71    /// Parse the interrupt-map property into a structured format
72    pub fn interrupt_map(&self) -> Result<Vec<PciInterruptMap>, FdtError> {
73        let prop = self
74            .node
75            .find_property("interrupt-map")
76            .ok_or(FdtError::PropertyNotFound("interrupt-map"))?;
77
78        let mut mask = self
79            .interrupt_map_mask()
80            .ok_or(FdtError::PropertyNotFound("interrupt-map-mask"))?;
81
82        let mut data = prop.data.buffer();
83        let mut mappings = Vec::new();
84
85        // Calculate the size of each entry in interrupt-map
86        // Format: <child-address child-irq interrupt-parent parent-irq...>
87        let child_addr_cells = self.address_cells() as usize;
88        let child_irq_cells = self.interrupt_cells() as usize;
89
90        let required_mask_len = child_addr_cells + child_irq_cells;
91        if mask.len() < required_mask_len {
92            mask.resize(required_mask_len, 0xffff_ffff);
93        }
94
95        while !data.remain().as_ref().is_empty() {
96            // Parse child address (variable number of cells for PCI)
97            let mut child_address = Vec::with_capacity(child_addr_cells);
98            for _ in 0..child_addr_cells {
99                child_address.push(
100                    data.take_u32()
101                        .map_err(|_| FdtError::BufferTooSmall { pos: data.pos() })?,
102                );
103            }
104
105            // Parse child IRQ (usually 1 cell for PCI)
106            let mut child_irq = Vec::with_capacity(child_irq_cells);
107            for _ in 0..child_irq_cells {
108                child_irq.push(
109                    data.take_u32()
110                        .map_err(|_| FdtError::BufferTooSmall { pos: data.pos() })?,
111                );
112            }
113
114            // Parse interrupt parent phandle
115            let interrupt_parent_raw = data
116                .take_u32()
117                .map_err(|_| FdtError::BufferTooSmall { pos: data.pos() })?;
118            let interrupt_parent = if interrupt_parent_raw == 0 {
119                self.interrupt_parent_phandle().unwrap_or(Phandle::from(0))
120            } else {
121                Phandle::from(interrupt_parent_raw)
122            };
123
124            let irq_parent = self
125                .node
126                .interrupt_parent()
127                .ok_or(FdtError::NodeNotFound("interrupt-parent"))?;
128
129            let address_cells = irq_parent.address_cells();
130
131            for _ in 0..address_cells {
132                data.take_u32()
133                    .map_err(|_| FdtError::BufferTooSmall { pos: data.pos() })?;
134            }
135
136            let parent_irq_cells = irq_parent.interrupt_cells()? as usize;
137
138            // Parse parent IRQ (variable number of cells)
139            let mut parent_irq = Vec::with_capacity(parent_irq_cells);
140            for _ in 0..parent_irq_cells {
141                let irq = data
142                    .take_u32()
143                    .map_err(|_| FdtError::BufferTooSmall { pos: data.pos() })?;
144                parent_irq.push(irq);
145            }
146
147            // Apply mask to child address and IRQ
148            let masked_address: Vec<u32> = child_address
149                .iter()
150                .enumerate()
151                .map(|(idx, value)| {
152                    let mask_value = mask.get(idx).copied().unwrap_or(0xffff_ffff);
153                    value & mask_value
154                })
155                .collect();
156            let masked_irq: Vec<u32> = child_irq
157                .iter()
158                .enumerate()
159                .map(|(idx, value)| {
160                    let mask_value = mask
161                        .get(child_addr_cells + idx)
162                        .copied()
163                        .unwrap_or(0xffff_ffff);
164                    value & mask_value
165                })
166                .collect();
167
168            mappings.push(PciInterruptMap {
169                child_address: masked_address,
170                child_irq: masked_irq,
171                interrupt_parent,
172                parent_irq,
173            });
174        }
175
176        Ok(mappings)
177    }
178
179    /// Get the bus range property if present
180    pub fn bus_range(&self) -> Option<Range<u32>> {
181        self.node.find_property("bus-range").and_then(|prop| {
182            let mut data = prop.data.buffer();
183            let start = data.take_u32().ok()?;
184            let end = data.take_u32().unwrap_or(start);
185            Some(start..end)
186        })
187    }
188
189    /// Get the device_type property (should be "pci" for PCI nodes)
190    pub fn device_type(&self) -> Option<&str> {
191        self.node
192            .find_property("device_type")
193            .and_then(|prop| prop.str().ok())
194    }
195
196    /// Check if this is a PCI host bridge
197    pub fn is_pci_host_bridge(&self) -> bool {
198        self.device_type() == Some("pci")
199            || self.node.name().contains("pci")
200            || self.node.compatibles().iter().any(|c| c.contains("pci"))
201    }
202
203    /// Get the ranges property for address translation
204    pub fn ranges(&self) -> Option<Vec<PciRange>> {
205        let prop = self.node.find_property("ranges")?;
206        let mut data = prop.data.buffer();
207        let mut ranges = Vec::new();
208
209        // PCI ranges format: <child-bus-address parent-bus-address size>
210        // child-bus-address: 3 cells (pci.hi pci.mid pci.lo)
211        // parent-bus-address: 2 cells for 64-bit systems (high, low)
212        // size: 2 cells for 64-bit sizes (high, low)
213        while !data.remain().as_ref().is_empty() {
214            // Parse child bus address (3 cells for PCI)
215            let mut child_addr = [0u32; 3];
216            for i in 0..3 {
217                child_addr[i] = data.take_u32().ok()?;
218            }
219
220            // Parse parent bus address (2 cells for 64-bit)
221            let parent_addr_high = data.take_u32().ok()?;
222            let parent_addr_low = data.take_u32().ok()?;
223            let parent_addr = ((parent_addr_high as u64) << 32) | (parent_addr_low as u64);
224
225            // Parse size (2 cells for 64-bit)
226            let size_high = data.take_u32().ok()?;
227            let size_low = data.take_u32().ok()?;
228            let size = ((size_high as u64) << 32) | (size_low as u64);
229
230            // Extract PCI address space and prefetchable from child_addr[0]
231            let pci_hi = child_addr[0];
232            let (space, prefetchable) = self.decode_pci_address_space(pci_hi);
233
234            // Calculate bus address from child_addr[1:2]
235            let bus_address = ((child_addr[1] as u64) << 32) | (child_addr[2] as u64);
236
237            ranges.push(PciRange {
238                space,
239                bus_address,
240                cpu_address: parent_addr,
241                size,
242                prefetchable,
243            });
244        }
245
246        Some(ranges)
247    }
248
249    /// Decode PCI address space from the high cell of PCI address
250    fn decode_pci_address_space(&self, pci_hi: u32) -> (PciSpace, bool) {
251        // PCI address high cell format:
252        // Bits 31-28: 1 for IO space, 2 for Memory32, 3 for Memory64
253        // Bit 30: Prefetchable for memory spaces
254        let space_code = (pci_hi >> 24) & 0x03;
255        let prefetchable = (pci_hi >> 30) & 0x01 == 1;
256
257        let space = match space_code {
258            1 => PciSpace::IO,
259            2 => PciSpace::Memory32,
260            3 => PciSpace::Memory64,
261            _ => PciSpace::Memory32, // Default fallback
262        };
263
264        (space, prefetchable)
265    }
266
267    /// Get interrupt information for a specific PCI device
268    /// Parameters: bus, device, function, pin (0=INTA, 1=INTB, 2=INTC, 3=INTD)
269    pub fn child_interrupts(
270        &self,
271        bus: u32,
272        device: u32,
273        function: u32,
274        pin: u32,
275    ) -> Result<PciInterruptInfo, FdtError> {
276        // Try to get interrupt-map and mask, fall back to simpler approach if parsing fails
277        let interrupt_map = self.interrupt_map()?;
278
279        let mut mask = self
280            .interrupt_map_mask()
281            .ok_or(FdtError::PropertyNotFound("interrupt-map-mask"))?;
282
283        // Construct the child address for PCI device
284        // Format: [bus_num, device_num, func_num] in appropriate bits
285        let child_addr_high =
286            ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8);
287        let child_addr_mid = 0;
288        let child_addr_low = 0;
289
290        let child_addr_cells = self.address_cells() as usize;
291        let child_irq_cells = self.interrupt_cells() as usize;
292        let required_mask_len = child_addr_cells + child_irq_cells;
293        if mask.len() < required_mask_len {
294            mask.resize(required_mask_len, 0xffff_ffff);
295        }
296
297        let encoded_address = [child_addr_high, child_addr_mid, child_addr_low];
298        let mut masked_child_address = Vec::with_capacity(child_addr_cells);
299        for idx in 0..child_addr_cells {
300            let value = *encoded_address.get(idx).unwrap_or(&0);
301            masked_child_address.push(value & mask[idx]);
302        }
303
304        let encoded_irq = [pin];
305        let mut masked_child_irq = Vec::with_capacity(child_irq_cells);
306        for idx in 0..child_irq_cells {
307            let value = *encoded_irq.get(idx).unwrap_or(&0);
308            masked_child_irq.push(value & mask[child_addr_cells + idx]);
309        }
310
311        // Look for matching entry in interrupt-map
312        for mapping in &interrupt_map {
313            // Check if this mapping matches our masked address and pin
314            if mapping.child_address == masked_child_address
315                && mapping.child_irq == masked_child_irq
316            {
317                return Ok(PciInterruptInfo {
318                    irqs: mapping.parent_irq.clone(),
319                });
320            }
321        }
322        let simple_irq = (device * 4 + pin) % 32;
323        Ok(PciInterruptInfo {
324            irqs: vec![simple_irq],
325        })
326    }
327}
328
329impl Debug for Pci {
330    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
331        f.debug_struct("Pci")
332            .field("name", &self.node.name())
333            .field("is_pci_host_bridge", &self.is_pci_host_bridge())
334            .field("bus_range", &self.bus_range())
335            .field("interrupt_map_mask", &self.interrupt_map_mask())
336            .finish()
337    }
338}
339
340impl Deref for Pci {
341    type Target = NodeBase;
342
343    fn deref(&self) -> &Self::Target {
344        &self.node
345    }
346}
347
348#[cfg(test)]
349mod tests {
350    use crate::{cache::node::Node, Fdt};
351
352    #[test]
353    fn test_pci_node_detection() {
354        let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/qemu_pci.dtb");
355        let fdt = Fdt::from_bytes(dtb_data).unwrap();
356
357        // Try to find PCI nodes
358        let mut pci_nodes_found = 0;
359        for node in fdt.find_nodes("/") {
360            {
361                if let Node::Pci(pci) = node {
362                    pci_nodes_found += 1;
363                    // println!("Found PCI node: {}", pci.name());
364                    assert!(pci.is_pci_host_bridge());
365                }
366            }
367        }
368
369        // We should find at least one PCI node in the qemu PCI test file
370        assert!(pci_nodes_found > 0, "Should find at least one PCI node");
371    }
372
373    #[test]
374    fn test_interrupt_map_parsing() {
375        let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/qemu_pci.dtb");
376        let fdt = Fdt::from_bytes(dtb_data).unwrap();
377
378        // Look for a PCI node with interrupt-map
379        for node in fdt.find_nodes("/") {
380            {
381                if let Node::Pci(pci) = node {
382                    if let Ok(interrupt_map) = pci.interrupt_map() {
383                        assert!(!interrupt_map.is_empty());
384                        return; // Test passed if we found and parsed interrupt-map
385                    }
386                }
387            }
388        }
389
390        // If we get here, no interrupt-map was found
391        // println!("No interrupt-map found in any PCI node");
392    }
393
394    #[test]
395    fn test_interrupt_map_mask() {
396        let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/qemu_pci.dtb");
397        let fdt = Fdt::from_bytes(dtb_data).unwrap();
398
399        for node in fdt.find_nodes("/") {
400            {
401                if let Node::Pci(pci) = node {
402                    if let Some(mask) = pci.interrupt_map_mask() {
403                        // println!("Found interrupt-map-mask: {:?}", mask);
404                        assert_eq!(mask.len(), 4, "PCI interrupt-map-mask should have 4 cells");
405                        return; // Test passed
406                    }
407                }
408            }
409        }
410
411        // println!("No interrupt-map-mask found in any PCI node");
412    }
413
414    #[test]
415    fn test_bus_range() {
416        let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/qemu_pci.dtb");
417        let fdt = Fdt::from_bytes(dtb_data).unwrap();
418
419        for node in fdt.find_nodes("/") {
420            {
421                if let Node::Pci(pci) = node {
422                    if let Some(range) = pci.bus_range() {
423                        // println!("Found bus-range: {}-{}", start, end);
424                        assert!(range.start <= range.end, "Bus range start should be <= end");
425                        return; // Test passed
426                    }
427                }
428            }
429        }
430
431        // println!("No bus-range found in any PCI node");
432    }
433
434    #[test]
435    fn test_pci_properties() {
436        let dtb_data = include_bytes!("../../../../dtb-file/src/dtb/qemu_pci.dtb");
437        let fdt = Fdt::from_bytes(dtb_data).unwrap();
438
439        for node in fdt.find_nodes("/") {
440            {
441                if let Node::Pci(pci) = node {
442                    // Test address cells
443                    assert_eq!(pci.address_cells(), 3, "PCI should use 3 address cells");
444
445                    // Test interrupt cells
446                    assert_eq!(pci.interrupt_cells(), 1, "PCI should use 1 interrupt cell");
447
448                    // Test device type
449                    if let Some(device_type) = pci.device_type() {
450                        assert!(!device_type.is_empty());
451                    }
452
453                    // Test compatibles
454                    let compatibles = pci.compatibles();
455                    if !compatibles.is_empty() {
456                        // println!("Compatibles: {:?}", compatibles);
457                    }
458
459                    return; // Test passed for first PCI node found
460                }
461            }
462        }
463
464        panic!("No PCI nodes found for property testing");
465    }
466}