pci_core/
cfg_space_emu.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Helpers that implement standardized PCI configuration space functionality.
5//!
6//! To be clear: PCI devices are not required to use these helpers, and may
7//! choose to implement configuration space accesses manually.
8
9use crate::PciInterruptPin;
10use crate::bar_mapping::BarMappings;
11use crate::capabilities::PciCapability;
12use crate::spec::caps::CapabilityId;
13use crate::spec::cfg_space;
14use crate::spec::hwid::HardwareIds;
15use chipset_device::io::IoError;
16use chipset_device::io::IoResult;
17use chipset_device::mmio::ControlMmioIntercept;
18use guestmem::MappableGuestMemory;
19use inspect::Inspect;
20use std::ops::RangeInclusive;
21use std::sync::Arc;
22use std::sync::atomic::AtomicBool;
23use std::sync::atomic::Ordering;
24use vmcore::line_interrupt::LineInterrupt;
25
26/// PCI configuration space header type with corresponding BAR count
27///
28/// This enum provides a type-safe way to work with PCI configuration space header types
29/// and their corresponding BAR counts. It improves readability over raw constants.
30///
31/// # Examples
32///
33/// ```rust
34/// # use pci_core::cfg_space_emu::HeaderType;
35/// // Get BAR count for different header types
36/// assert_eq!(HeaderType::Type0.bar_count(), 6);
37/// assert_eq!(HeaderType::Type1.bar_count(), 2);
38///
39/// // Convert to usize for use in generic contexts
40/// let bar_count: usize = HeaderType::Type0.into();
41/// assert_eq!(bar_count, 6);
42/// ```
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum HeaderType {
45    /// Type 0 header with 6 BARs (endpoint devices)
46    Type0,
47    /// Type 1 header with 2 BARs (bridge devices)  
48    Type1,
49}
50
51impl HeaderType {
52    /// Get the number of BARs for this header type
53    pub const fn bar_count(self) -> usize {
54        match self {
55            HeaderType::Type0 => 6,
56            HeaderType::Type1 => 2,
57        }
58    }
59}
60
61impl From<HeaderType> for usize {
62    fn from(header_type: HeaderType) -> usize {
63        header_type.bar_count()
64    }
65}
66
67/// Constants for header type BAR counts
68pub mod header_type_consts {
69    use super::HeaderType;
70
71    /// Number of BARs for Type 0 headers
72    pub const TYPE0_BAR_COUNT: usize = HeaderType::Type0.bar_count();
73
74    /// Number of BARs for Type 1 headers  
75    pub const TYPE1_BAR_COUNT: usize = HeaderType::Type1.bar_count();
76}
77
78/// Result type for common header emulator operations
79#[derive(Debug)]
80pub enum CommonHeaderResult {
81    /// The access was handled by the common header emulator
82    Handled,
83    /// The access is not handled by common header, caller should handle it
84    Unhandled,
85    /// The access failed with an error
86    Failed(IoError),
87}
88
89impl PartialEq for CommonHeaderResult {
90    fn eq(&self, other: &Self) -> bool {
91        match (self, other) {
92            (Self::Handled, Self::Handled) => true,
93            (Self::Unhandled, Self::Unhandled) => true,
94            (Self::Failed(_), Self::Failed(_)) => true, // Consider all failures equal for testing
95            _ => false,
96        }
97    }
98}
99
100const SUPPORTED_COMMAND_BITS: u16 = cfg_space::Command::new()
101    .with_pio_enabled(true)
102    .with_mmio_enabled(true)
103    .with_bus_master(true)
104    .with_special_cycles(true)
105    .with_enable_memory_write_invalidate(true)
106    .with_vga_palette_snoop(true)
107    .with_parity_error_response(true)
108    .with_enable_serr(true)
109    .with_enable_fast_b2b(true)
110    .with_intx_disable(true)
111    .into_bits();
112
113/// A wrapper around a [`LineInterrupt`] that considers PCI configuration space
114/// interrupt control bits.
115#[derive(Debug, Inspect)]
116pub struct IntxInterrupt {
117    pin: PciInterruptPin,
118    line: LineInterrupt,
119    interrupt_disabled: AtomicBool,
120    interrupt_status: AtomicBool,
121}
122
123impl IntxInterrupt {
124    /// Sets the line level high or low.
125    ///
126    /// NOTE: whether or not this will actually trigger an interrupt will depend
127    /// the status of the Interrupt Disabled bit in the PCI configuration space.
128    pub fn set_level(&self, high: bool) {
129        tracing::debug!(
130            disabled = ?self.interrupt_disabled,
131            status = ?self.interrupt_status,
132            ?high,
133            %self.line,
134            "set_level"
135        );
136
137        // the actual config space bit is set unconditionally
138        self.interrupt_status.store(high, Ordering::SeqCst);
139
140        // ...but whether it also fires an interrupt is a different story
141        if self.interrupt_disabled.load(Ordering::SeqCst) {
142            self.line.set_level(false);
143        } else {
144            self.line.set_level(high);
145        }
146    }
147
148    fn set_disabled(&self, disabled: bool) {
149        tracing::debug!(
150            disabled = ?self.interrupt_disabled,
151            status = ?self.interrupt_status,
152            ?disabled,
153            %self.line,
154            "set_disabled"
155        );
156
157        self.interrupt_disabled.store(disabled, Ordering::SeqCst);
158        if disabled {
159            self.line.set_level(false)
160        } else {
161            if self.interrupt_status.load(Ordering::SeqCst) {
162                self.line.set_level(true)
163            }
164        }
165    }
166}
167
168#[derive(Debug, Inspect)]
169struct ConfigSpaceCommonHeaderEmulatorState<const N: usize> {
170    /// The command register
171    command: cfg_space::Command,
172    /// OS-configured BARs
173    #[inspect(with = "inspect_helpers::bars_generic")]
174    base_addresses: [u32; N],
175    /// The PCI device doesn't actually care about what value is stored here -
176    /// this register is just a bit of standardized "scratch space", ostensibly
177    /// for firmware to communicate IRQ assignments to the OS, but it can really
178    /// be used for just about anything.
179    interrupt_line: u8,
180}
181
182impl<const N: usize> ConfigSpaceCommonHeaderEmulatorState<N> {
183    fn new() -> Self {
184        Self {
185            command: cfg_space::Command::new(),
186            base_addresses: {
187                const ZERO: u32 = 0;
188                [ZERO; N]
189            },
190            interrupt_line: 0,
191        }
192    }
193}
194
195/// Common emulator for shared PCI configuration space functionality.
196/// Generic over the number of BARs (6 for Type 0, 2 for Type 1).
197#[derive(Inspect)]
198pub struct ConfigSpaceCommonHeaderEmulator<const N: usize> {
199    // Fixed configuration
200    #[inspect(with = "inspect_helpers::bars_generic")]
201    bar_masks: [u32; N],
202    hardware_ids: HardwareIds,
203    multi_function_bit: bool,
204
205    // Runtime glue
206    #[inspect(with = r#"|x| inspect::iter_by_index(x).prefix("bar")"#)]
207    mapped_memory: [Option<BarMemoryKind>; N],
208    #[inspect(with = "|x| inspect::iter_by_key(x.iter().map(|cap| (cap.label(), cap)))")]
209    capabilities: Vec<Box<dyn PciCapability>>,
210    intx_interrupt: Option<Arc<IntxInterrupt>>,
211
212    // Runtime book-keeping
213    active_bars: BarMappings,
214
215    // Volatile state
216    state: ConfigSpaceCommonHeaderEmulatorState<N>,
217}
218
219/// Type alias for Type 0 common header emulator (6 BARs)
220pub type ConfigSpaceCommonHeaderEmulatorType0 =
221    ConfigSpaceCommonHeaderEmulator<{ header_type_consts::TYPE0_BAR_COUNT }>;
222
223/// Type alias for Type 1 common header emulator (2 BARs)
224pub type ConfigSpaceCommonHeaderEmulatorType1 =
225    ConfigSpaceCommonHeaderEmulator<{ header_type_consts::TYPE1_BAR_COUNT }>;
226
227impl<const N: usize> ConfigSpaceCommonHeaderEmulator<N> {
228    /// Create a new common header emulator
229    pub fn new(
230        hardware_ids: HardwareIds,
231        capabilities: Vec<Box<dyn PciCapability>>,
232        bars: DeviceBars,
233    ) -> Self {
234        let mut bar_masks = {
235            const ZERO: u32 = 0;
236            [ZERO; N]
237        };
238        let mut mapped_memory = {
239            const NONE: Option<BarMemoryKind> = None;
240            [NONE; N]
241        };
242
243        // Only process BARs that fit within our supported range (N)
244        for (bar_index, bar) in bars.bars.into_iter().enumerate().take(N) {
245            let (len, mapped) = match bar {
246                Some(bar) => bar,
247                None => continue,
248            };
249            // use 64-bit aware BARs
250            assert!(bar_index < N.saturating_sub(1));
251            // Round up regions to a power of 2, as required by PCI (and
252            // inherently required by the BAR representation). Round up to at
253            // least one page to avoid various problems in guest OSes.
254            const MIN_BAR_SIZE: u64 = 4096;
255            let len = std::cmp::max(len.next_power_of_two(), MIN_BAR_SIZE);
256            let mask64 = !(len - 1);
257            bar_masks[bar_index] = cfg_space::BarEncodingBits::from_bits(mask64 as u32)
258                .with_type_64_bit(true)
259                .into_bits();
260            if bar_index + 1 < N {
261                bar_masks[bar_index + 1] = (mask64 >> 32) as u32;
262            }
263            mapped_memory[bar_index] = Some(mapped);
264        }
265
266        Self {
267            hardware_ids,
268            capabilities,
269            bar_masks,
270            mapped_memory,
271            multi_function_bit: false,
272            intx_interrupt: None,
273            active_bars: Default::default(),
274            state: ConfigSpaceCommonHeaderEmulatorState::new(),
275        }
276    }
277
278    /// Get the number of BARs supported by this emulator
279    pub const fn bar_count(&self) -> usize {
280        N
281    }
282
283    /// Validate that this emulator has the correct number of BARs for the given header type
284    pub fn validate_header_type(&self, expected: HeaderType) -> bool {
285        N == expected.bar_count()
286    }
287
288    /// If the device is multi-function, enable bit 7 in the Header register.
289    pub fn with_multi_function_bit(mut self, bit: bool) -> Self {
290        self.multi_function_bit = bit;
291        self
292    }
293
294    /// If using legacy INT#x interrupts: wire a LineInterrupt to one of the 4
295    /// INT#x pins, returning an object that manages configuration space bits
296    /// when the device sets the interrupt level.
297    pub fn set_interrupt_pin(
298        &mut self,
299        pin: PciInterruptPin,
300        line: LineInterrupt,
301    ) -> Arc<IntxInterrupt> {
302        let intx_interrupt = Arc::new(IntxInterrupt {
303            pin,
304            line,
305            interrupt_disabled: AtomicBool::new(false),
306            interrupt_status: AtomicBool::new(false),
307        });
308        self.intx_interrupt = Some(intx_interrupt.clone());
309        intx_interrupt
310    }
311
312    /// Reset the common header state
313    pub fn reset(&mut self) {
314        tracing::info!("ConfigSpaceCommonHeaderEmulator: resetting state");
315        self.state = ConfigSpaceCommonHeaderEmulatorState::new();
316
317        tracing::info!("ConfigSpaceCommonHeaderEmulator: syncing command register after reset");
318        self.sync_command_register(self.state.command);
319
320        tracing::info!(
321            "ConfigSpaceCommonHeaderEmulator: resetting {} capabilities",
322            self.capabilities.len()
323        );
324        for cap in &mut self.capabilities {
325            cap.reset();
326        }
327
328        if let Some(intx) = &mut self.intx_interrupt {
329            tracing::info!("ConfigSpaceCommonHeaderEmulator: resetting interrupt level");
330            intx.set_level(false);
331        }
332        tracing::info!("ConfigSpaceCommonHeaderEmulator: reset completed");
333    }
334
335    /// Get hardware IDs
336    pub fn hardware_ids(&self) -> &HardwareIds {
337        &self.hardware_ids
338    }
339
340    /// Get capabilities
341    pub fn capabilities(&self) -> &[Box<dyn PciCapability>] {
342        &self.capabilities
343    }
344
345    /// Get capabilities mutably
346    pub fn capabilities_mut(&mut self) -> &mut [Box<dyn PciCapability>] {
347        &mut self.capabilities
348    }
349
350    /// Get multi-function bit
351    pub fn multi_function_bit(&self) -> bool {
352        self.multi_function_bit
353    }
354
355    /// Get the header type for this emulator
356    pub const fn header_type(&self) -> HeaderType {
357        match N {
358            header_type_consts::TYPE0_BAR_COUNT => HeaderType::Type0,
359            header_type_consts::TYPE1_BAR_COUNT => HeaderType::Type1,
360            _ => panic!("Unsupported BAR count - must be 6 (Type0) or 2 (Type1)"),
361        }
362    }
363
364    /// Get current command register state
365    pub fn command(&self) -> cfg_space::Command {
366        self.state.command
367    }
368
369    /// Get current base addresses
370    pub fn base_addresses(&self) -> &[u32; N] {
371        &self.state.base_addresses
372    }
373
374    /// Get current interrupt line
375    pub fn interrupt_line(&self) -> u8 {
376        self.state.interrupt_line
377    }
378
379    /// Get current interrupt pin (returns the pin number + 1, or 0 if no pin configured)
380    pub fn interrupt_pin(&self) -> u8 {
381        if let Some(intx) = &self.intx_interrupt {
382            (intx.pin as u8) + 1 // PCI spec: 1=INTA, 2=INTB, 3=INTC, 4=INTD, 0=no interrupt
383        } else {
384            0 // No interrupt pin configured
385        }
386    }
387
388    /// Set interrupt line (for save/restore)
389    pub fn set_interrupt_line(&mut self, interrupt_line: u8) {
390        self.state.interrupt_line = interrupt_line;
391    }
392
393    /// Set base addresses (for save/restore)
394    pub fn set_base_addresses(&mut self, base_addresses: &[u32; N]) {
395        self.state.base_addresses = *base_addresses;
396    }
397
398    /// Set command register (for save/restore)
399    pub fn set_command(&mut self, command: cfg_space::Command) {
400        self.state.command = command;
401    }
402
403    /// Sync command register changes by updating both interrupt and MMIO state
404    pub fn sync_command_register(&mut self, command: cfg_space::Command) {
405        tracing::info!(
406            "ConfigSpaceCommonHeaderEmulator: syncing command register - intx_disable={}, mmio_enabled={}",
407            command.intx_disable(),
408            command.mmio_enabled()
409        );
410        self.update_intx_disable(command.intx_disable());
411        self.update_mmio_enabled(command.mmio_enabled());
412    }
413
414    /// Update interrupt disable setting
415    pub fn update_intx_disable(&mut self, disabled: bool) {
416        tracing::info!(
417            "ConfigSpaceCommonHeaderEmulator: updating intx_disable={}",
418            disabled
419        );
420        if let Some(intx_interrupt) = &self.intx_interrupt {
421            intx_interrupt.set_disabled(disabled)
422        }
423    }
424
425    /// Update MMIO enabled setting and handle BAR mapping
426    pub fn update_mmio_enabled(&mut self, enabled: bool) {
427        tracing::info!(
428            "ConfigSpaceCommonHeaderEmulator: updating mmio_enabled={}",
429            enabled
430        );
431        if enabled {
432            // Note that BarMappings expects 6 BARs. Pad with 0 for Type 1 (N=2)
433            // and use directly for Type 0 (N=6).
434            let mut full_base_addresses = [0u32; 6];
435            let mut full_bar_masks = [0u32; 6];
436
437            // Copy our data into the first N positions
438            full_base_addresses[..N].copy_from_slice(&self.state.base_addresses[..N]);
439            full_bar_masks[..N].copy_from_slice(&self.bar_masks[..N]);
440
441            self.active_bars = BarMappings::parse(&full_base_addresses, &full_bar_masks);
442            for (bar, mapping) in self.mapped_memory.iter_mut().enumerate() {
443                if let Some(mapping) = mapping {
444                    let base = self.active_bars.get(bar as u8).expect("bar exists");
445                    match mapping.map_to_guest(base) {
446                        Ok(_) => {}
447                        Err(err) => {
448                            tracelimit::error_ratelimited!(
449                                error = &err as &dyn std::error::Error,
450                                bar,
451                                base,
452                                "failed to map bar",
453                            )
454                        }
455                    }
456                }
457            }
458        } else {
459            self.active_bars = Default::default();
460            for mapping in self.mapped_memory.iter_mut().flatten() {
461                mapping.unmap_from_guest();
462            }
463        }
464    }
465
466    // ===== Configuration Space Read/Write Functions =====
467
468    /// Read from the config space. `offset` must be 32-bit aligned.
469    /// Returns CommonHeaderResult indicating if handled, unhandled, or failed.
470    pub fn read_u32(&self, offset: u16, value: &mut u32) -> CommonHeaderResult {
471        use cfg_space::CommonHeader;
472
473        tracing::trace!(
474            "ConfigSpaceCommonHeaderEmulator: read_u32 offset={:#x}",
475            offset
476        );
477
478        *value = match CommonHeader(offset) {
479            CommonHeader::DEVICE_VENDOR => {
480                (self.hardware_ids.device_id as u32) << 16 | self.hardware_ids.vendor_id as u32
481            }
482            CommonHeader::STATUS_COMMAND => {
483                let mut status =
484                    cfg_space::Status::new().with_capabilities_list(!self.capabilities.is_empty());
485
486                if let Some(intx_interrupt) = &self.intx_interrupt {
487                    if intx_interrupt.interrupt_status.load(Ordering::SeqCst) {
488                        status.set_interrupt_status(true);
489                    }
490                }
491
492                (status.into_bits() as u32) << 16 | self.state.command.into_bits() as u32
493            }
494            CommonHeader::CLASS_REVISION => {
495                (u8::from(self.hardware_ids.base_class) as u32) << 24
496                    | (u8::from(self.hardware_ids.sub_class) as u32) << 16
497                    | (u8::from(self.hardware_ids.prog_if) as u32) << 8
498                    | self.hardware_ids.revision_id as u32
499            }
500            CommonHeader::RESERVED_CAP_PTR => {
501                if self.capabilities.is_empty() {
502                    0
503                } else {
504                    0x40
505                }
506            }
507            // Capabilities space - handled by common emulator
508            _ if (0x40..0x100).contains(&offset) => {
509                return self.read_capabilities(offset, value);
510            }
511            // Extended capabilities space - handled by common emulator
512            _ if (0x100..0x1000).contains(&offset) => {
513                return self.read_extended_capabilities(offset, value);
514            }
515            // Check if this is a BAR read
516            _ if self.is_bar_offset(offset) => {
517                return self.read_bar(offset, value);
518            }
519            // Unhandled access - not part of common header, caller should handle
520            _ => {
521                return CommonHeaderResult::Unhandled;
522            }
523        };
524
525        tracing::trace!(
526            "ConfigSpaceCommonHeaderEmulator: read_u32 offset={:#x} -> value={:#x}",
527            offset,
528            *value
529        );
530        // Handled access
531        CommonHeaderResult::Handled
532    }
533
534    /// Write to the config space. `offset` must be 32-bit aligned.
535    /// Returns CommonHeaderResult indicating if handled, unhandled, or failed.
536    pub fn write_u32(&mut self, offset: u16, val: u32) -> CommonHeaderResult {
537        use cfg_space::CommonHeader;
538
539        tracing::trace!(
540            "ConfigSpaceCommonHeaderEmulator: write_u32 offset={:#x} val={:#x}",
541            offset,
542            val
543        );
544
545        match CommonHeader(offset) {
546            CommonHeader::STATUS_COMMAND => {
547                let mut command = cfg_space::Command::from_bits(val as u16);
548                if command.into_bits() & !SUPPORTED_COMMAND_BITS != 0 {
549                    tracelimit::warn_ratelimited!(offset, val, "setting invalid command bits");
550                    // still do our best
551                    command =
552                        cfg_space::Command::from_bits(command.into_bits() & SUPPORTED_COMMAND_BITS);
553                };
554
555                if self.state.command.intx_disable() != command.intx_disable() {
556                    self.update_intx_disable(command.intx_disable())
557                }
558
559                if self.state.command.mmio_enabled() != command.mmio_enabled() {
560                    self.update_mmio_enabled(command.mmio_enabled())
561                }
562
563                self.state.command = command;
564            }
565            // Capabilities space - handled by common emulator
566            _ if (0x40..0x100).contains(&offset) => {
567                return self.write_capabilities(offset, val);
568            }
569            // Extended capabilities space - handled by common emulator
570            _ if (0x100..0x1000).contains(&offset) => {
571                return self.write_extended_capabilities(offset, val);
572            }
573            // Check if this is a BAR write (Type 0: 0x10-0x27, Type 1: 0x10-0x17)
574            _ if self.is_bar_offset(offset) => {
575                return self.write_bar(offset, val);
576            }
577            // Unhandled access - not part of common header, caller should handle
578            _ => {
579                return CommonHeaderResult::Unhandled;
580            }
581        }
582
583        // Handled access
584        CommonHeaderResult::Handled
585    }
586
587    /// Helper for reading BAR registers
588    fn read_bar(&self, offset: u16, value: &mut u32) -> CommonHeaderResult {
589        if !self.is_bar_offset(offset) {
590            return CommonHeaderResult::Unhandled;
591        }
592
593        let bar_index = self.get_bar_index(offset);
594        if bar_index < N {
595            *value = self.state.base_addresses[bar_index];
596        } else {
597            *value = 0;
598        }
599        CommonHeaderResult::Handled
600    }
601
602    /// Helper for writing BAR registers
603    fn write_bar(&mut self, offset: u16, val: u32) -> CommonHeaderResult {
604        if !self.is_bar_offset(offset) {
605            return CommonHeaderResult::Unhandled;
606        }
607
608        // Handle BAR writes - only allow when MMIO is disabled
609        if !self.state.command.mmio_enabled() {
610            let bar_index = self.get_bar_index(offset);
611            if bar_index < N {
612                let mut bar_value = val & self.bar_masks[bar_index];
613
614                // For even-indexed BARs, set the 64-bit type bit if the BAR is configured
615                if bar_index & 1 == 0 && self.bar_masks[bar_index] != 0 {
616                    bar_value = cfg_space::BarEncodingBits::from_bits(bar_value)
617                        .with_type_64_bit(true)
618                        .into_bits();
619                }
620
621                self.state.base_addresses[bar_index] = bar_value;
622            }
623        }
624        CommonHeaderResult::Handled
625    }
626
627    /// Read from capabilities space. `offset` must be 32-bit aligned and >= 0x40.
628    fn read_capabilities(&self, offset: u16, value: &mut u32) -> CommonHeaderResult {
629        if (0x40..0x100).contains(&offset) {
630            if let Some((cap_index, cap_offset)) =
631                self.get_capability_index_and_offset(offset - 0x40)
632            {
633                *value = self.capabilities[cap_index].read_u32(cap_offset);
634                if cap_offset == 0 {
635                    let next = if cap_index < self.capabilities.len() - 1 {
636                        offset as u32 + self.capabilities[cap_index].len() as u32
637                    } else {
638                        0
639                    };
640                    assert!(*value & 0xff00 == 0);
641                    *value |= next << 8;
642                }
643                CommonHeaderResult::Handled
644            } else {
645                tracelimit::warn_ratelimited!(offset, "unhandled config space read");
646                CommonHeaderResult::Failed(IoError::InvalidRegister)
647            }
648        } else {
649            CommonHeaderResult::Failed(IoError::InvalidRegister)
650        }
651    }
652
653    /// Write to capabilities space. `offset` must be 32-bit aligned and >= 0x40.
654    fn write_capabilities(&mut self, offset: u16, val: u32) -> CommonHeaderResult {
655        if (0x40..0x100).contains(&offset) {
656            if let Some((cap_index, cap_offset)) =
657                self.get_capability_index_and_offset(offset - 0x40)
658            {
659                self.capabilities[cap_index].write_u32(cap_offset, val);
660                CommonHeaderResult::Handled
661            } else {
662                tracelimit::warn_ratelimited!(offset, value = val, "unhandled config space write");
663                CommonHeaderResult::Failed(IoError::InvalidRegister)
664            }
665        } else {
666            CommonHeaderResult::Failed(IoError::InvalidRegister)
667        }
668    }
669
670    /// Read from extended capabilities space (0x100-0x1000). `offset` must be 32-bit aligned.
671    fn read_extended_capabilities(&self, offset: u16, value: &mut u32) -> CommonHeaderResult {
672        if (0x100..0x1000).contains(&offset) {
673            if self.is_pcie_device() {
674                *value = 0xffffffff;
675                CommonHeaderResult::Handled
676            } else {
677                tracelimit::warn_ratelimited!(offset, "unhandled extended config space read");
678                CommonHeaderResult::Failed(IoError::InvalidRegister)
679            }
680        } else {
681            CommonHeaderResult::Failed(IoError::InvalidRegister)
682        }
683    }
684
685    /// Write to extended capabilities space (0x100-0x1000). `offset` must be 32-bit aligned.
686    fn write_extended_capabilities(&mut self, offset: u16, val: u32) -> CommonHeaderResult {
687        if (0x100..0x1000).contains(&offset) {
688            if self.is_pcie_device() {
689                // For now, just ignore writes to extended config space
690                CommonHeaderResult::Handled
691            } else {
692                tracelimit::warn_ratelimited!(
693                    offset,
694                    value = val,
695                    "unhandled extended config space write"
696                );
697                CommonHeaderResult::Failed(IoError::InvalidRegister)
698            }
699        } else {
700            CommonHeaderResult::Failed(IoError::InvalidRegister)
701        }
702    }
703
704    // ===== Utility and Query Functions =====
705
706    /// Finds a BAR + offset by address.
707    pub fn find_bar(&self, address: u64) -> Option<(u8, u16)> {
708        self.active_bars.find(address)
709    }
710
711    /// Check if this device is a PCIe device by looking for the PCI Express capability.
712    pub fn is_pcie_device(&self) -> bool {
713        self.capabilities
714            .iter()
715            .any(|cap| cap.capability_id() == CapabilityId::PCI_EXPRESS)
716    }
717
718    /// Get capability index and offset for a given offset
719    fn get_capability_index_and_offset(&self, offset: u16) -> Option<(usize, u16)> {
720        let mut cap_offset = 0;
721        for i in 0..self.capabilities.len() {
722            let cap_size = self.capabilities[i].len() as u16;
723            if offset < cap_offset + cap_size {
724                return Some((i, offset - cap_offset));
725            }
726            cap_offset += cap_size;
727        }
728        None
729    }
730
731    /// Check if an offset corresponds to a BAR register
732    fn is_bar_offset(&self, offset: u16) -> bool {
733        // Type 0: BAR0-BAR5 (0x10-0x27), Type 1: BAR0-BAR1 (0x10-0x17)
734        let bar_start = cfg_space::HeaderType00::BAR0.0;
735        let bar_end = bar_start + (N as u16) * 4;
736        (bar_start..bar_end).contains(&offset) && offset.is_multiple_of(4)
737    }
738
739    /// Get the BAR index for a given offset
740    fn get_bar_index(&self, offset: u16) -> usize {
741        ((offset - cfg_space::HeaderType00::BAR0.0) / 4) as usize
742    }
743
744    /// Get BAR masks (for testing only)
745    #[cfg(test)]
746    pub fn bar_masks(&self) -> &[u32; N] {
747        &self.bar_masks
748    }
749}
750
751#[derive(Debug, Inspect)]
752struct ConfigSpaceType0EmulatorState {
753    /// A read/write register that doesn't matter in virtualized contexts
754    latency_timer: u8,
755}
756
757impl ConfigSpaceType0EmulatorState {
758    fn new() -> Self {
759        Self { latency_timer: 0 }
760    }
761}
762
763/// Emulator for the standard Type 0 PCI configuration space header.
764#[derive(Inspect)]
765pub struct ConfigSpaceType0Emulator {
766    /// The common header emulator that handles shared functionality
767    #[inspect(flatten)]
768    common: ConfigSpaceCommonHeaderEmulatorType0,
769    /// Type 0 specific state
770    state: ConfigSpaceType0EmulatorState,
771}
772
773mod inspect_helpers {
774    use super::*;
775
776    pub(crate) fn bars_generic<const N: usize>(bars: &[u32; N]) -> impl Inspect + '_ {
777        inspect::AsHex(inspect::iter_by_index(bars).prefix("bar"))
778    }
779}
780
781/// Different kinds of memory that a BAR can be backed by
782#[derive(Inspect)]
783#[inspect(tag = "kind")]
784pub enum BarMemoryKind {
785    /// BAR memory is routed to the device's `MmioIntercept` handler
786    Intercept(#[inspect(rename = "handle")] Box<dyn ControlMmioIntercept>),
787    /// BAR memory is routed to a shared memory region
788    SharedMem(#[inspect(skip)] Box<dyn MappableGuestMemory>),
789    /// **TESTING ONLY** BAR memory isn't backed by anything!
790    Dummy,
791}
792
793impl std::fmt::Debug for BarMemoryKind {
794    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
795        match self {
796            Self::Intercept(control) => {
797                write!(f, "Intercept(region_name: {}, ..)", control.region_name())
798            }
799            Self::SharedMem(_) => write!(f, "Mmap(..)"),
800            Self::Dummy => write!(f, "Dummy"),
801        }
802    }
803}
804
805impl BarMemoryKind {
806    fn map_to_guest(&mut self, gpa: u64) -> std::io::Result<()> {
807        match self {
808            BarMemoryKind::Intercept(control) => {
809                control.map(gpa);
810                Ok(())
811            }
812            BarMemoryKind::SharedMem(control) => control.map_to_guest(gpa, true),
813            BarMemoryKind::Dummy => Ok(()),
814        }
815    }
816
817    fn unmap_from_guest(&mut self) {
818        match self {
819            BarMemoryKind::Intercept(control) => control.unmap(),
820            BarMemoryKind::SharedMem(control) => control.unmap_from_guest(),
821            BarMemoryKind::Dummy => {}
822        }
823    }
824}
825
826/// Container type that describes a device's available BARs
827// TODO: support more advanced BAR configurations
828// e.g: mixed 32-bit and 64-bit
829// e.g: IO space BARs
830#[derive(Debug)]
831pub struct DeviceBars {
832    bars: [Option<(u64, BarMemoryKind)>; 6],
833}
834
835impl DeviceBars {
836    /// Create a new instance of [`DeviceBars`]
837    pub fn new() -> DeviceBars {
838        DeviceBars {
839            bars: Default::default(),
840        }
841    }
842
843    /// Set BAR0
844    pub fn bar0(mut self, len: u64, memory: BarMemoryKind) -> Self {
845        self.bars[0] = Some((len, memory));
846        self
847    }
848
849    /// Set BAR2
850    pub fn bar2(mut self, len: u64, memory: BarMemoryKind) -> Self {
851        self.bars[2] = Some((len, memory));
852        self
853    }
854
855    /// Set BAR4
856    pub fn bar4(mut self, len: u64, memory: BarMemoryKind) -> Self {
857        self.bars[4] = Some((len, memory));
858        self
859    }
860}
861
862impl ConfigSpaceType0Emulator {
863    /// Create a new [`ConfigSpaceType0Emulator`]
864    pub fn new(
865        hardware_ids: HardwareIds,
866        capabilities: Vec<Box<dyn PciCapability>>,
867        bars: DeviceBars,
868    ) -> Self {
869        let common = ConfigSpaceCommonHeaderEmulator::new(hardware_ids, capabilities, bars);
870
871        Self {
872            common,
873            state: ConfigSpaceType0EmulatorState::new(),
874        }
875    }
876
877    /// If the device is multi-function, enable bit 7 in the Header register.
878    pub fn with_multi_function_bit(mut self, bit: bool) -> Self {
879        self.common = self.common.with_multi_function_bit(bit);
880        self
881    }
882
883    /// If using legacy INT#x interrupts: wire a LineInterrupt to one of the 4
884    /// INT#x pins, returning an object that manages configuration space bits
885    /// when the device sets the interrupt level.
886    pub fn set_interrupt_pin(
887        &mut self,
888        pin: PciInterruptPin,
889        line: LineInterrupt,
890    ) -> Arc<IntxInterrupt> {
891        self.common.set_interrupt_pin(pin, line)
892    }
893
894    /// Resets the configuration space state.
895    pub fn reset(&mut self) {
896        self.common.reset();
897        self.state = ConfigSpaceType0EmulatorState::new();
898    }
899
900    /// Read from the config space. `offset` must be 32-bit aligned.
901    pub fn read_u32(&self, offset: u16, value: &mut u32) -> IoResult {
902        use cfg_space::HeaderType00;
903
904        // First try to handle with common header emulator
905        match self.common.read_u32(offset, value) {
906            CommonHeaderResult::Handled => return IoResult::Ok,
907            CommonHeaderResult::Failed(err) => return IoResult::Err(err),
908            CommonHeaderResult::Unhandled => {
909                // Continue with Type 0 specific handling
910            }
911        }
912
913        // Handle Type 0 specific registers
914        *value = match HeaderType00(offset) {
915            HeaderType00::BIST_HEADER => {
916                let mut v = (self.state.latency_timer as u32) << 8;
917                if self.common.multi_function_bit() {
918                    // enable top-most bit of the header register
919                    v |= 0x80 << 16;
920                }
921                v
922            }
923            HeaderType00::CARDBUS_CIS_PTR => 0,
924            HeaderType00::SUBSYSTEM_ID => {
925                (self.common.hardware_ids().type0_sub_system_id as u32) << 16
926                    | self.common.hardware_ids().type0_sub_vendor_id as u32
927            }
928            HeaderType00::EXPANSION_ROM_BASE => 0,
929            HeaderType00::RESERVED => 0,
930            HeaderType00::LATENCY_INTERRUPT => {
931                // Bits 7-0: Interrupt Line, Bits 15-8: Interrupt Pin, Bits 31-16: Latency Timer
932                (self.state.latency_timer as u32) << 16
933                    | (self.common.interrupt_pin() as u32) << 8
934                    | self.common.interrupt_line() as u32
935            }
936            _ => {
937                tracelimit::warn_ratelimited!(offset, "unexpected config space read");
938                return IoResult::Err(IoError::InvalidRegister);
939            }
940        };
941
942        IoResult::Ok
943    }
944
945    /// Write to the config space. `offset` must be 32-bit aligned.
946    pub fn write_u32(&mut self, offset: u16, val: u32) -> IoResult {
947        use cfg_space::HeaderType00;
948
949        // First try to handle with common header emulator
950        match self.common.write_u32(offset, val) {
951            CommonHeaderResult::Handled => return IoResult::Ok,
952            CommonHeaderResult::Failed(err) => return IoResult::Err(err),
953            CommonHeaderResult::Unhandled => {
954                // Continue with Type 0 specific handling
955            }
956        }
957
958        // Handle Type 0 specific registers
959        match HeaderType00(offset) {
960            HeaderType00::BIST_HEADER => {
961                // BIST_HEADER - Type 0 specific handling
962                // For now, just ignore these writes (header type is read-only)
963            }
964            HeaderType00::LATENCY_INTERRUPT => {
965                // Bits 7-0: Interrupt Line (read/write)
966                // Bits 15-8: Interrupt Pin (read-only, ignore writes)
967                // Bits 31-16: Latency Timer (read/write)
968                self.common.set_interrupt_line((val & 0xff) as u8);
969                self.state.latency_timer = (val >> 16) as u8;
970            }
971            // all other base regs are noops
972            _ if offset < 0x40 && offset.is_multiple_of(4) => (),
973            _ => {
974                tracelimit::warn_ratelimited!(offset, value = val, "unexpected config space write");
975                return IoResult::Err(IoError::InvalidRegister);
976            }
977        }
978
979        IoResult::Ok
980    }
981
982    /// Finds a BAR + offset by address.
983    pub fn find_bar(&self, address: u64) -> Option<(u8, u16)> {
984        self.common.find_bar(address)
985    }
986
987    /// Checks if this device is a PCIe device by looking for the PCI Express capability.
988    pub fn is_pcie_device(&self) -> bool {
989        self.common.is_pcie_device()
990    }
991
992    /// Set the presence detect state for a hotplug-capable slot.
993    /// This method finds the PCIe Express capability and calls its set_presence_detect_state method.
994    /// If the PCIe Express capability is not found, the call is silently ignored.
995    ///
996    /// # Arguments
997    /// * `present` - true if a device is present in the slot, false if the slot is empty
998    pub fn set_presence_detect_state(&mut self, present: bool) {
999        for capability in self.common.capabilities_mut() {
1000            if let Some(pcie_cap) = capability.as_pci_express_mut() {
1001                pcie_cap.set_presence_detect_state(present);
1002                return;
1003            }
1004        }
1005
1006        // PCIe Express capability not found - silently ignore
1007    }
1008}
1009
1010#[derive(Debug, Inspect)]
1011struct ConfigSpaceType1EmulatorState {
1012    /// The subordinate bus number register. Software programs
1013    /// this register with the highest bus number below the bridge.
1014    subordinate_bus_number: u8,
1015    /// The secondary bus number register. Software programs
1016    /// this register with the bus number assigned to the secondary
1017    /// side of the bridge.
1018    secondary_bus_number: u8,
1019    /// The primary bus number register. This is unused for PCI Express but
1020    /// is supposed to be read/write for compability with legacy software.
1021    primary_bus_number: u8,
1022    /// The memory base register. Software programs the upper 12 bits of this
1023    /// register with the upper 12 bits of a 32-bit base address of MMIO assigned
1024    /// to the hierarchy under the bridge (the lower 20 bits are assumed to be 0s).
1025    memory_base: u16,
1026    /// The memory limit register. Software programs the upper 12 bits of this
1027    /// register with the upper 12 bits of a 32-bit limit address of MMIO assigned
1028    /// to the hierarchy under the bridge (the lower 20 bits are assumed to be 1s).
1029    memory_limit: u16,
1030    /// The prefetchable memory base register. Software programs the upper 12 bits of
1031    /// this register with bits 20:31 of the base address of the prefetchable MMIO
1032    /// window assigned to the hierarchy under the bridge. Bits 0:19 are assumed to
1033    /// be 0s.
1034    prefetch_base: u16,
1035    /// The prefetchable memory limit register. Software programs the upper 12 bits of
1036    /// this register with bits 20:31 of the limit address of the prefetchable MMIO
1037    /// window assigned to the hierarchy under the bridge. Bits 0:19 are assumed to
1038    /// be 1s.
1039    prefetch_limit: u16,
1040    /// The prefetchable memory base upper 32 bits register. When the bridge supports
1041    /// 64-bit addressing for prefetchable memory, software programs this register
1042    /// with the upper 32 bits of the base address of the prefetchable MMIO window
1043    /// assigned to the hierarchy under the bridge.
1044    prefetch_base_upper: u32,
1045    /// The prefetchable memory limit upper 32 bits register. When the bridge supports
1046    /// 64-bit addressing for prefetchable memory, software programs this register
1047    /// with the upper 32 bits of the base address of the prefetchable MMIO window
1048    /// assigned to the hierarchy under the bridge.
1049    prefetch_limit_upper: u32,
1050    /// The bridge control register. Contains various control bits for bridge behavior
1051    /// such as secondary bus reset, VGA enable, etc.
1052    bridge_control: u16,
1053}
1054
1055impl ConfigSpaceType1EmulatorState {
1056    fn new() -> Self {
1057        Self {
1058            subordinate_bus_number: 0,
1059            secondary_bus_number: 0,
1060            primary_bus_number: 0,
1061            memory_base: 0,
1062            memory_limit: 0,
1063            prefetch_base: 0,
1064            prefetch_limit: 0,
1065            prefetch_base_upper: 0,
1066            prefetch_limit_upper: 0,
1067            bridge_control: 0,
1068        }
1069    }
1070}
1071
1072/// Emulator for the standard Type 1 PCI configuration space header.
1073#[derive(Inspect)]
1074pub struct ConfigSpaceType1Emulator {
1075    /// The common header emulator that handles shared functionality
1076    #[inspect(flatten)]
1077    common: ConfigSpaceCommonHeaderEmulatorType1,
1078    /// Type 1 specific state
1079    state: ConfigSpaceType1EmulatorState,
1080}
1081
1082impl ConfigSpaceType1Emulator {
1083    /// Create a new [`ConfigSpaceType1Emulator`]
1084    pub fn new(hardware_ids: HardwareIds, capabilities: Vec<Box<dyn PciCapability>>) -> Self {
1085        let common =
1086            ConfigSpaceCommonHeaderEmulator::new(hardware_ids, capabilities, DeviceBars::new());
1087
1088        Self {
1089            common,
1090            state: ConfigSpaceType1EmulatorState::new(),
1091        }
1092    }
1093
1094    /// Resets the configuration space state.
1095    pub fn reset(&mut self) {
1096        self.common.reset();
1097        self.state = ConfigSpaceType1EmulatorState::new();
1098    }
1099
1100    /// Set the multi-function bit for this device.
1101    pub fn with_multi_function_bit(mut self, multi_function: bool) -> Self {
1102        self.common = self.common.with_multi_function_bit(multi_function);
1103        self
1104    }
1105
1106    /// Returns the range of bus numbers the bridge is programmed to decode.
1107    pub fn assigned_bus_range(&self) -> RangeInclusive<u8> {
1108        let secondary = self.state.secondary_bus_number;
1109        let subordinate = self.state.subordinate_bus_number;
1110        if secondary <= subordinate {
1111            secondary..=subordinate
1112        } else {
1113            0..=0
1114        }
1115    }
1116
1117    fn decode_memory_range(&self, base_register: u16, limit_register: u16) -> (u32, u32) {
1118        let base_addr = ((base_register & !0b1111) as u32) << 16;
1119        let limit_addr = ((limit_register & !0b1111) as u32) << 16 | 0xF_FFFF;
1120        (base_addr, limit_addr)
1121    }
1122
1123    /// If memory decoding is currently enabled, and the memory window assignment is valid,
1124    /// returns the 32-bit memory addresses the bridge is programmed to decode.
1125    pub fn assigned_memory_range(&self) -> Option<RangeInclusive<u32>> {
1126        let (base_addr, limit_addr) =
1127            self.decode_memory_range(self.state.memory_base, self.state.memory_limit);
1128        if self.common.command().mmio_enabled() && base_addr <= limit_addr {
1129            Some(base_addr..=limit_addr)
1130        } else {
1131            None
1132        }
1133    }
1134
1135    /// If memory decoding is currently enabled, and the prefetchable memory window assignment
1136    /// is valid, returns the 64-bit prefetchable memory addresses the bridge is programmed to decode.
1137    pub fn assigned_prefetch_range(&self) -> Option<RangeInclusive<u64>> {
1138        let (base_low, limit_low) =
1139            self.decode_memory_range(self.state.prefetch_base, self.state.prefetch_limit);
1140        let base_addr = (self.state.prefetch_base_upper as u64) << 32 | base_low as u64;
1141        let limit_addr = (self.state.prefetch_limit_upper as u64) << 32 | limit_low as u64;
1142        if self.common.command().mmio_enabled() && base_addr <= limit_addr {
1143            Some(base_addr..=limit_addr)
1144        } else {
1145            None
1146        }
1147    }
1148
1149    /// Read from the config space. `offset` must be 32-bit aligned.
1150    pub fn read_u32(&self, offset: u16, value: &mut u32) -> IoResult {
1151        use cfg_space::HeaderType01;
1152
1153        // First try to handle with common header emulator
1154        match self.common.read_u32(offset, value) {
1155            CommonHeaderResult::Handled => return IoResult::Ok,
1156            CommonHeaderResult::Failed(err) => return IoResult::Err(err),
1157            CommonHeaderResult::Unhandled => {
1158                // Continue with Type 1 specific handling
1159            }
1160        }
1161
1162        // Handle Type 1 specific registers
1163        *value = match HeaderType01(offset) {
1164            HeaderType01::BIST_HEADER => {
1165                // Header type 01 with optional multi-function bit
1166                if self.common.multi_function_bit() {
1167                    0x00810000 // Header type 01 with multi-function bit (bit 23)
1168                } else {
1169                    0x00010000 // Header type 01 without multi-function bit
1170                }
1171            }
1172            HeaderType01::LATENCY_BUS_NUMBERS => {
1173                (self.state.subordinate_bus_number as u32) << 16
1174                    | (self.state.secondary_bus_number as u32) << 8
1175                    | self.state.primary_bus_number as u32
1176            }
1177            HeaderType01::SEC_STATUS_IO_RANGE => 0,
1178            HeaderType01::MEMORY_RANGE => {
1179                (self.state.memory_limit as u32) << 16 | self.state.memory_base as u32
1180            }
1181            HeaderType01::PREFETCH_RANGE => {
1182                // Set the low bit in both the limit and base registers to indicate
1183                // support for 64-bit addressing.
1184                ((self.state.prefetch_limit | 0b0001) as u32) << 16
1185                    | (self.state.prefetch_base | 0b0001) as u32
1186            }
1187            HeaderType01::PREFETCH_BASE_UPPER => self.state.prefetch_base_upper,
1188            HeaderType01::PREFETCH_LIMIT_UPPER => self.state.prefetch_limit_upper,
1189            HeaderType01::IO_RANGE_UPPER => 0,
1190            HeaderType01::EXPANSION_ROM_BASE => 0,
1191            HeaderType01::BRDIGE_CTRL_INTERRUPT => {
1192                // Read interrupt line from common header and bridge control from state
1193                // Bits 7-0: Interrupt Line, Bits 15-8: Interrupt Pin (0), Bits 31-16: Bridge Control
1194                (self.state.bridge_control as u32) << 16 | self.common.interrupt_line() as u32
1195            }
1196            _ => {
1197                tracelimit::warn_ratelimited!(offset, "unexpected config space read");
1198                return IoResult::Err(IoError::InvalidRegister);
1199            }
1200        };
1201
1202        IoResult::Ok
1203    }
1204
1205    /// Write to the config space. `offset` must be 32-bit aligned.
1206    pub fn write_u32(&mut self, offset: u16, val: u32) -> IoResult {
1207        use cfg_space::HeaderType01;
1208
1209        // First try to handle with common header emulator
1210        match self.common.write_u32(offset, val) {
1211            CommonHeaderResult::Handled => return IoResult::Ok,
1212            CommonHeaderResult::Failed(err) => return IoResult::Err(err),
1213            CommonHeaderResult::Unhandled => {
1214                // Continue with Type 1 specific handling
1215            }
1216        }
1217
1218        // Handle Type 1 specific registers
1219        match HeaderType01(offset) {
1220            HeaderType01::BIST_HEADER => {
1221                // BIST_HEADER - Type 1 specific handling
1222                // For now, just ignore these writes (latency timer would go here if supported)
1223            }
1224            HeaderType01::LATENCY_BUS_NUMBERS => {
1225                self.state.subordinate_bus_number = (val >> 16) as u8;
1226                self.state.secondary_bus_number = (val >> 8) as u8;
1227                self.state.primary_bus_number = val as u8;
1228            }
1229            HeaderType01::MEMORY_RANGE => {
1230                self.state.memory_base = val as u16;
1231                self.state.memory_limit = (val >> 16) as u16;
1232            }
1233            HeaderType01::PREFETCH_RANGE => {
1234                self.state.prefetch_base = val as u16;
1235                self.state.prefetch_limit = (val >> 16) as u16;
1236            }
1237            HeaderType01::PREFETCH_BASE_UPPER => {
1238                self.state.prefetch_base_upper = val;
1239            }
1240            HeaderType01::PREFETCH_LIMIT_UPPER => {
1241                self.state.prefetch_limit_upper = val;
1242            }
1243            HeaderType01::BRDIGE_CTRL_INTERRUPT => {
1244                // Delegate interrupt line writes to common header and store bridge control
1245                // Bits 7-0: Interrupt Line, Bits 15-8: Interrupt Pin (ignored), Bits 31-16: Bridge Control
1246                self.common.set_interrupt_line((val & 0xff) as u8);
1247                self.state.bridge_control = (val >> 16) as u16;
1248            }
1249            // all other base regs are noops
1250            _ if offset < 0x40 && offset.is_multiple_of(4) => (),
1251            _ => {
1252                tracelimit::warn_ratelimited!(offset, value = val, "unexpected config space write");
1253                return IoResult::Err(IoError::InvalidRegister);
1254            }
1255        }
1256
1257        IoResult::Ok
1258    }
1259
1260    /// Checks if this device is a PCIe device by looking for the PCI Express capability.
1261    pub fn is_pcie_device(&self) -> bool {
1262        self.common.is_pcie_device()
1263    }
1264
1265    /// Set the presence detect state for the slot.
1266    /// This method finds the PCIe Express capability and calls its set_presence_detect_state method.
1267    /// If the PCIe Express capability is not found, the call is silently ignored.
1268    ///
1269    /// # Arguments
1270    /// * `present` - true if a device is present in the slot, false if the slot is empty
1271    pub fn set_presence_detect_state(&mut self, present: bool) {
1272        // Find the PCIe Express capability
1273        for cap in self.common.capabilities_mut() {
1274            if cap.capability_id() == CapabilityId::PCI_EXPRESS {
1275                // Downcast to PciExpressCapability and call set_presence_detect_state
1276                if let Some(pcie_cap) = cap.as_pci_express_mut() {
1277                    pcie_cap.set_presence_detect_state(present);
1278                    return;
1279                }
1280            }
1281        }
1282        // If no PCIe Express capability is found, silently ignore the call
1283    }
1284}
1285
1286mod save_restore {
1287    use super::*;
1288    use thiserror::Error;
1289    use vmcore::save_restore::RestoreError;
1290    use vmcore::save_restore::SaveError;
1291    use vmcore::save_restore::SaveRestore;
1292
1293    mod state {
1294        use mesh::payload::Protobuf;
1295        use vmcore::save_restore::SavedStateBlob;
1296        use vmcore::save_restore::SavedStateRoot;
1297
1298        #[derive(Protobuf, SavedStateRoot)]
1299        #[mesh(package = "pci.cfg_space_emu")]
1300        pub struct SavedState {
1301            #[mesh(1)]
1302            pub command: u16,
1303            #[mesh(2)]
1304            pub base_addresses: [u32; 6],
1305            #[mesh(3)]
1306            pub interrupt_line: u8,
1307            #[mesh(4)]
1308            pub latency_timer: u8,
1309            #[mesh(5)]
1310            pub capabilities: Vec<(String, SavedStateBlob)>,
1311        }
1312    }
1313
1314    #[derive(Debug, Error)]
1315    enum ConfigSpaceRestoreError {
1316        #[error("found invalid config bits in saved state")]
1317        InvalidConfigBits,
1318        #[error("found unexpected capability {0}")]
1319        InvalidCap(String),
1320    }
1321
1322    impl<const N: usize> SaveRestore for ConfigSpaceCommonHeaderEmulator<N> {
1323        type SavedState = state::SavedState;
1324
1325        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1326            tracing::info!(
1327                "ConfigSpaceCommonHeaderEmulator<{}>: starting save operation",
1328                N
1329            );
1330
1331            let ConfigSpaceCommonHeaderEmulatorState {
1332                command,
1333                base_addresses,
1334                interrupt_line,
1335            } = self.state;
1336
1337            // Convert to 6-element array, padding with zeros if needed
1338            let mut saved_base_addresses = [0u32; 6];
1339            for (i, &addr) in base_addresses.iter().enumerate() {
1340                if i < 6 {
1341                    saved_base_addresses[i] = addr;
1342                }
1343            }
1344
1345            tracing::info!(
1346                "ConfigSpaceCommonHeaderEmulator<{}>: saving state - command={:#x}, interrupt_line={}, base_addresses={:?}",
1347                N,
1348                command.into_bits(),
1349                interrupt_line,
1350                saved_base_addresses
1351            );
1352
1353            let saved_state = state::SavedState {
1354                command: command.into_bits(),
1355                base_addresses: saved_base_addresses,
1356                interrupt_line,
1357                latency_timer: 0, // Not used in common header, always 0
1358                capabilities: self
1359                    .capabilities
1360                    .iter_mut()
1361                    .map(|cap| {
1362                        let id = cap.label().to_owned();
1363                        Ok((id, cap.save()?))
1364                    })
1365                    .collect::<Result<_, _>>()?,
1366            };
1367
1368            tracing::info!(
1369                "ConfigSpaceCommonHeaderEmulator<{}>: save operation completed successfully",
1370                N
1371            );
1372            Ok(saved_state)
1373        }
1374
1375        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1376            tracing::info!(
1377                "ConfigSpaceCommonHeaderEmulator<{}>: starting restore operation",
1378                N
1379            );
1380
1381            let state::SavedState {
1382                command,
1383                base_addresses,
1384                interrupt_line,
1385                latency_timer: _, // Ignore latency_timer field
1386                capabilities,
1387            } = state;
1388
1389            tracing::info!(
1390                "ConfigSpaceCommonHeaderEmulator<{}>: restoring state - command={:#x}, interrupt_line={}, base_addresses={:?}",
1391                N,
1392                command,
1393                interrupt_line,
1394                base_addresses
1395            );
1396
1397            // Convert from 6-element array, taking only what we need
1398            let mut restored_base_addresses = {
1399                const ZERO: u32 = 0;
1400                [ZERO; N]
1401            };
1402            for (i, &addr) in base_addresses.iter().enumerate() {
1403                if i < N {
1404                    restored_base_addresses[i] = addr;
1405                }
1406            }
1407
1408            self.state = ConfigSpaceCommonHeaderEmulatorState {
1409                command: cfg_space::Command::from_bits(command),
1410                base_addresses: restored_base_addresses,
1411                interrupt_line,
1412            };
1413
1414            if command & !SUPPORTED_COMMAND_BITS != 0 {
1415                tracing::warn!(
1416                    "ConfigSpaceCommonHeaderEmulator<{}>: invalid command bits found: {:#x}",
1417                    N,
1418                    command
1419                );
1420                return Err(RestoreError::InvalidSavedState(
1421                    ConfigSpaceRestoreError::InvalidConfigBits.into(),
1422                ));
1423            }
1424
1425            tracing::info!(
1426                "ConfigSpaceCommonHeaderEmulator<{}>: syncing command register",
1427                N
1428            );
1429            self.sync_command_register(self.state.command);
1430
1431            tracing::info!(
1432                "ConfigSpaceCommonHeaderEmulator<{}>: restoring {} capabilities",
1433                N,
1434                capabilities.len()
1435            );
1436            for (id, entry) in capabilities {
1437                tracing::debug!(
1438                    save_id = id.as_str(),
1439                    "restoring pci common header capability"
1440                );
1441
1442                // yes, yes, this is O(n^2), but devices never have more than a
1443                // handful of caps, so it's totally fine.
1444                let mut restored = false;
1445                for cap in self.capabilities.iter_mut() {
1446                    if cap.label() == id {
1447                        cap.restore(entry)?;
1448                        restored = true;
1449                        break;
1450                    }
1451                }
1452
1453                if !restored {
1454                    tracing::error!(
1455                        "ConfigSpaceCommonHeaderEmulator<{}>: failed to find capability: {}",
1456                        N,
1457                        id
1458                    );
1459                    return Err(RestoreError::InvalidSavedState(
1460                        ConfigSpaceRestoreError::InvalidCap(id).into(),
1461                    ));
1462                }
1463            }
1464
1465            tracing::info!(
1466                "ConfigSpaceCommonHeaderEmulator<{}>: restore operation completed successfully",
1467                N
1468            );
1469            Ok(())
1470        }
1471    }
1472
1473    mod type0_state {
1474        use super::state;
1475        use mesh::payload::Protobuf;
1476        use vmcore::save_restore::SavedStateRoot;
1477
1478        #[derive(Protobuf, SavedStateRoot)]
1479        #[mesh(package = "pci.cfg_space_emu")]
1480        pub struct SavedType0State {
1481            #[mesh(1)]
1482            pub latency_timer: u8,
1483            #[mesh(2)]
1484            pub common_header: state::SavedState,
1485        }
1486    }
1487
1488    impl SaveRestore for ConfigSpaceType0Emulator {
1489        type SavedState = type0_state::SavedType0State;
1490
1491        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1492            tracing::info!("ConfigSpaceType0Emulator: starting save operation");
1493
1494            let ConfigSpaceType0EmulatorState { latency_timer } = self.state;
1495
1496            tracing::info!(
1497                "ConfigSpaceType0Emulator: saving latency_timer={}",
1498                latency_timer
1499            );
1500
1501            let saved_state = type0_state::SavedType0State {
1502                latency_timer,
1503                common_header: self.common.save()?,
1504            };
1505
1506            tracing::info!("ConfigSpaceType0Emulator: save operation completed successfully");
1507            Ok(saved_state)
1508        }
1509
1510        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1511            tracing::info!("ConfigSpaceType0Emulator: starting restore operation");
1512
1513            let type0_state::SavedType0State {
1514                latency_timer,
1515                common_header,
1516            } = state;
1517
1518            tracing::info!(
1519                "ConfigSpaceType0Emulator: restoring latency_timer={}",
1520                latency_timer
1521            );
1522
1523            self.state = ConfigSpaceType0EmulatorState { latency_timer };
1524
1525            tracing::info!("ConfigSpaceType0Emulator: delegating to common header restore");
1526            self.common.restore(common_header)?;
1527
1528            tracing::info!("ConfigSpaceType0Emulator: restore operation completed successfully");
1529            Ok(())
1530        }
1531    }
1532
1533    mod type1_state {
1534        use super::state;
1535        use mesh::payload::Protobuf;
1536        use vmcore::save_restore::SavedStateRoot;
1537
1538        #[derive(Protobuf, SavedStateRoot)]
1539        #[mesh(package = "pci.cfg_space_emu")]
1540        pub struct SavedType1State {
1541            #[mesh(1)]
1542            pub subordinate_bus_number: u8,
1543            #[mesh(2)]
1544            pub secondary_bus_number: u8,
1545            #[mesh(3)]
1546            pub primary_bus_number: u8,
1547            #[mesh(4)]
1548            pub memory_base: u16,
1549            #[mesh(5)]
1550            pub memory_limit: u16,
1551            #[mesh(6)]
1552            pub prefetch_base: u16,
1553            #[mesh(7)]
1554            pub prefetch_limit: u16,
1555            #[mesh(8)]
1556            pub prefetch_base_upper: u32,
1557            #[mesh(9)]
1558            pub prefetch_limit_upper: u32,
1559            #[mesh(10)]
1560            pub bridge_control: u16,
1561            #[mesh(11)]
1562            pub common_header: state::SavedState,
1563        }
1564    }
1565
1566    impl SaveRestore for ConfigSpaceType1Emulator {
1567        type SavedState = type1_state::SavedType1State;
1568
1569        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1570            let ConfigSpaceType1EmulatorState {
1571                subordinate_bus_number,
1572                secondary_bus_number,
1573                primary_bus_number,
1574                memory_base,
1575                memory_limit,
1576                prefetch_base,
1577                prefetch_limit,
1578                prefetch_base_upper,
1579                prefetch_limit_upper,
1580                bridge_control,
1581            } = self.state;
1582
1583            let saved_state = type1_state::SavedType1State {
1584                subordinate_bus_number,
1585                secondary_bus_number,
1586                primary_bus_number,
1587                memory_base,
1588                memory_limit,
1589                prefetch_base,
1590                prefetch_limit,
1591                prefetch_base_upper,
1592                prefetch_limit_upper,
1593                bridge_control,
1594                common_header: self.common.save()?,
1595            };
1596
1597            Ok(saved_state)
1598        }
1599
1600        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1601            let type1_state::SavedType1State {
1602                subordinate_bus_number,
1603                secondary_bus_number,
1604                primary_bus_number,
1605                memory_base,
1606                memory_limit,
1607                prefetch_base,
1608                prefetch_limit,
1609                prefetch_base_upper,
1610                prefetch_limit_upper,
1611                bridge_control,
1612                common_header,
1613            } = state;
1614
1615            self.state = ConfigSpaceType1EmulatorState {
1616                subordinate_bus_number,
1617                secondary_bus_number,
1618                primary_bus_number,
1619                memory_base,
1620                memory_limit,
1621                prefetch_base,
1622                prefetch_limit,
1623                prefetch_base_upper,
1624                prefetch_limit_upper,
1625                bridge_control,
1626            };
1627
1628            self.common.restore(common_header)?;
1629
1630            Ok(())
1631        }
1632    }
1633}
1634
1635#[cfg(test)]
1636mod tests {
1637    use super::*;
1638    use crate::capabilities::pci_express::PciExpressCapability;
1639    use crate::capabilities::read_only::ReadOnlyCapability;
1640    use crate::spec::caps::pci_express::DevicePortType;
1641    use crate::spec::hwid::ClassCode;
1642    use crate::spec::hwid::ProgrammingInterface;
1643    use crate::spec::hwid::Subclass;
1644
1645    fn create_type0_emulator(caps: Vec<Box<dyn PciCapability>>) -> ConfigSpaceType0Emulator {
1646        ConfigSpaceType0Emulator::new(
1647            HardwareIds {
1648                vendor_id: 0x1111,
1649                device_id: 0x2222,
1650                revision_id: 1,
1651                prog_if: ProgrammingInterface::NONE,
1652                sub_class: Subclass::NONE,
1653                base_class: ClassCode::UNCLASSIFIED,
1654                type0_sub_vendor_id: 0x3333,
1655                type0_sub_system_id: 0x4444,
1656            },
1657            caps,
1658            DeviceBars::new(),
1659        )
1660    }
1661
1662    fn create_type1_emulator(caps: Vec<Box<dyn PciCapability>>) -> ConfigSpaceType1Emulator {
1663        ConfigSpaceType1Emulator::new(
1664            HardwareIds {
1665                vendor_id: 0x1111,
1666                device_id: 0x2222,
1667                revision_id: 1,
1668                prog_if: ProgrammingInterface::NONE,
1669                sub_class: Subclass::BRIDGE_PCI_TO_PCI,
1670                base_class: ClassCode::BRIDGE,
1671                type0_sub_vendor_id: 0,
1672                type0_sub_system_id: 0,
1673            },
1674            caps,
1675        )
1676    }
1677
1678    fn read_cfg(emulator: &ConfigSpaceType1Emulator, offset: u16) -> u32 {
1679        let mut val = 0;
1680        emulator.read_u32(offset, &mut val).unwrap();
1681        val
1682    }
1683
1684    #[test]
1685    fn test_type1_probe() {
1686        let emu = create_type1_emulator(vec![]);
1687        assert_eq!(read_cfg(&emu, 0), 0x2222_1111);
1688        assert_eq!(read_cfg(&emu, 4) & 0x10_0000, 0); // Capabilities pointer
1689
1690        let emu = create_type1_emulator(vec![Box::new(ReadOnlyCapability::new("foo", 0))]);
1691        assert_eq!(read_cfg(&emu, 0), 0x2222_1111);
1692        assert_eq!(read_cfg(&emu, 4) & 0x10_0000, 0x10_0000); // Capabilities pointer
1693    }
1694
1695    #[test]
1696    fn test_type1_bus_number_assignment() {
1697        let mut emu = create_type1_emulator(vec![]);
1698
1699        // The bus number (and latency timer) registers are
1700        // all default 0.
1701        assert_eq!(read_cfg(&emu, 0x18), 0);
1702        assert_eq!(emu.assigned_bus_range(), 0..=0);
1703
1704        // The bus numbers can be programmed one by one,
1705        // and the range may not be valid during the middle
1706        // of allocation.
1707        emu.write_u32(0x18, 0x0000_1000).unwrap();
1708        assert_eq!(read_cfg(&emu, 0x18), 0x0000_1000);
1709        assert_eq!(emu.assigned_bus_range(), 0..=0);
1710        emu.write_u32(0x18, 0x0012_1000).unwrap();
1711        assert_eq!(read_cfg(&emu, 0x18), 0x0012_1000);
1712        assert_eq!(emu.assigned_bus_range(), 0x10..=0x12);
1713
1714        // The primary bus number register is read/write for compatability
1715        // but unused.
1716        emu.write_u32(0x18, 0x0012_1033).unwrap();
1717        assert_eq!(read_cfg(&emu, 0x18), 0x0012_1033);
1718        assert_eq!(emu.assigned_bus_range(), 0x10..=0x12);
1719
1720        // Software can also just write the entire 4byte value at once
1721        emu.write_u32(0x18, 0x0047_4411).unwrap();
1722        assert_eq!(read_cfg(&emu, 0x18), 0x0047_4411);
1723        assert_eq!(emu.assigned_bus_range(), 0x44..=0x47);
1724
1725        // The subordinate bus number can equal the secondary bus number...
1726        emu.write_u32(0x18, 0x0088_8800).unwrap();
1727        assert_eq!(emu.assigned_bus_range(), 0x88..=0x88);
1728
1729        // ... but it cannot be less, that's a confused guest OS.
1730        emu.write_u32(0x18, 0x0087_8800).unwrap();
1731        assert_eq!(emu.assigned_bus_range(), 0..=0);
1732    }
1733
1734    #[test]
1735    fn test_type1_memory_assignment() {
1736        const MMIO_ENABLED: u32 = 0x0000_0002;
1737        const MMIO_DISABLED: u32 = 0x0000_0000;
1738
1739        let mut emu = create_type1_emulator(vec![]);
1740        assert!(emu.assigned_memory_range().is_none());
1741
1742        // The guest can write whatever it wants while MMIO
1743        // is disabled.
1744        emu.write_u32(0x20, 0xDEAD_BEEF).unwrap();
1745        assert!(emu.assigned_memory_range().is_none());
1746
1747        // The guest can program a valid resource assignment...
1748        emu.write_u32(0x20, 0xFFF0_FF00).unwrap();
1749        assert!(emu.assigned_memory_range().is_none());
1750        // ... enable memory decoding...
1751        emu.write_u32(0x4, MMIO_ENABLED).unwrap();
1752        assert_eq!(emu.assigned_memory_range(), Some(0xFF00_0000..=0xFFFF_FFFF));
1753        // ... then disable memory decoding it.
1754        emu.write_u32(0x4, MMIO_DISABLED).unwrap();
1755        assert!(emu.assigned_memory_range().is_none());
1756
1757        // Setting memory base equal to memory limit is a valid 1MB range.
1758        emu.write_u32(0x20, 0xBBB0_BBB0).unwrap();
1759        emu.write_u32(0x4, MMIO_ENABLED).unwrap();
1760        assert_eq!(emu.assigned_memory_range(), Some(0xBBB0_0000..=0xBBBF_FFFF));
1761        emu.write_u32(0x4, MMIO_DISABLED).unwrap();
1762        assert!(emu.assigned_memory_range().is_none());
1763
1764        // The guest can try to program an invalid assignment (base > limit), we
1765        // just won't decode it.
1766        emu.write_u32(0x20, 0xAA00_BB00).unwrap();
1767        assert!(emu.assigned_memory_range().is_none());
1768        emu.write_u32(0x4, MMIO_ENABLED).unwrap();
1769        assert!(emu.assigned_memory_range().is_none());
1770        emu.write_u32(0x4, MMIO_DISABLED).unwrap();
1771        assert!(emu.assigned_memory_range().is_none());
1772    }
1773
1774    #[test]
1775    fn test_type1_prefetch_assignment() {
1776        const MMIO_ENABLED: u32 = 0x0000_0002;
1777        const MMIO_DISABLED: u32 = 0x0000_0000;
1778
1779        let mut emu = create_type1_emulator(vec![]);
1780        assert!(emu.assigned_prefetch_range().is_none());
1781
1782        // The guest can program a valid prefetch range...
1783        emu.write_u32(0x24, 0xFFF0_FF00).unwrap(); // limit + base
1784        emu.write_u32(0x28, 0x00AA_BBCC).unwrap(); // base upper
1785        emu.write_u32(0x2C, 0x00DD_EEFF).unwrap(); // limit upper
1786        assert!(emu.assigned_prefetch_range().is_none());
1787        // ... enable memory decoding...
1788        emu.write_u32(0x4, MMIO_ENABLED).unwrap();
1789        assert_eq!(
1790            emu.assigned_prefetch_range(),
1791            Some(0x00AA_BBCC_FF00_0000..=0x00DD_EEFF_FFFF_FFFF)
1792        );
1793        // ... then disable memory decoding it.
1794        emu.write_u32(0x4, MMIO_DISABLED).unwrap();
1795        assert!(emu.assigned_prefetch_range().is_none());
1796
1797        // The validity of the assignment is determined using the combined 64-bit
1798        // address, not the lower bits or the upper bits in isolation.
1799
1800        // Lower bits of the limit are greater than the lower bits of the
1801        // base, but the upper bits make that valid.
1802        emu.write_u32(0x24, 0xFF00_FFF0).unwrap(); // limit + base
1803        emu.write_u32(0x28, 0x00AA_BBCC).unwrap(); // base upper
1804        emu.write_u32(0x2C, 0x00DD_EEFF).unwrap(); // limit upper
1805        assert!(emu.assigned_prefetch_range().is_none());
1806        emu.write_u32(0x4, MMIO_ENABLED).unwrap();
1807        assert_eq!(
1808            emu.assigned_prefetch_range(),
1809            Some(0x00AA_BBCC_FFF0_0000..=0x00DD_EEFF_FF0F_FFFF)
1810        );
1811        emu.write_u32(0x4, MMIO_DISABLED).unwrap();
1812        assert!(emu.assigned_prefetch_range().is_none());
1813
1814        // The base can equal the limit, which is a valid 1MB range.
1815        emu.write_u32(0x24, 0xDD00_DD00).unwrap(); // limit + base
1816        emu.write_u32(0x28, 0x00AA_BBCC).unwrap(); // base upper
1817        emu.write_u32(0x2C, 0x00AA_BBCC).unwrap(); // limit upper
1818        assert!(emu.assigned_prefetch_range().is_none());
1819        emu.write_u32(0x4, MMIO_ENABLED).unwrap();
1820        assert_eq!(
1821            emu.assigned_prefetch_range(),
1822            Some(0x00AA_BBCC_DD00_0000..=0x00AA_BBCC_DD0F_FFFF)
1823        );
1824        emu.write_u32(0x4, MMIO_DISABLED).unwrap();
1825        assert!(emu.assigned_prefetch_range().is_none());
1826    }
1827
1828    #[test]
1829    fn test_type1_is_pcie_device() {
1830        // Test Type 1 device without PCIe capability
1831        let emu = create_type1_emulator(vec![Box::new(ReadOnlyCapability::new("foo", 0))]);
1832        assert!(!emu.is_pcie_device());
1833
1834        // Test Type 1 device with PCIe capability
1835        let emu = create_type1_emulator(vec![Box::new(PciExpressCapability::new(
1836            DevicePortType::RootPort,
1837            None,
1838        ))]);
1839        assert!(emu.is_pcie_device());
1840
1841        // Test Type 1 device with multiple capabilities including PCIe
1842        let emu = create_type1_emulator(vec![
1843            Box::new(ReadOnlyCapability::new("foo", 0)),
1844            Box::new(PciExpressCapability::new(DevicePortType::Endpoint, None)),
1845            Box::new(ReadOnlyCapability::new("bar", 0)),
1846        ]);
1847        assert!(emu.is_pcie_device());
1848    }
1849
1850    #[test]
1851    fn test_type0_is_pcie_device() {
1852        // Test Type 0 device without PCIe capability
1853        let emu = ConfigSpaceType0Emulator::new(
1854            HardwareIds {
1855                vendor_id: 0x1111,
1856                device_id: 0x2222,
1857                revision_id: 1,
1858                prog_if: ProgrammingInterface::NONE,
1859                sub_class: Subclass::NONE,
1860                base_class: ClassCode::UNCLASSIFIED,
1861                type0_sub_vendor_id: 0,
1862                type0_sub_system_id: 0,
1863            },
1864            vec![Box::new(ReadOnlyCapability::new("foo", 0))],
1865            DeviceBars::new(),
1866        );
1867        assert!(!emu.is_pcie_device());
1868
1869        // Test Type 0 device with PCIe capability
1870        let emu = ConfigSpaceType0Emulator::new(
1871            HardwareIds {
1872                vendor_id: 0x1111,
1873                device_id: 0x2222,
1874                revision_id: 1,
1875                prog_if: ProgrammingInterface::NONE,
1876                sub_class: Subclass::NONE,
1877                base_class: ClassCode::UNCLASSIFIED,
1878                type0_sub_vendor_id: 0,
1879                type0_sub_system_id: 0,
1880            },
1881            vec![Box::new(PciExpressCapability::new(
1882                DevicePortType::Endpoint,
1883                None,
1884            ))],
1885            DeviceBars::new(),
1886        );
1887        assert!(emu.is_pcie_device());
1888
1889        // Test Type 0 device with multiple capabilities including PCIe
1890        let emu = ConfigSpaceType0Emulator::new(
1891            HardwareIds {
1892                vendor_id: 0x1111,
1893                device_id: 0x2222,
1894                revision_id: 1,
1895                prog_if: ProgrammingInterface::NONE,
1896                sub_class: Subclass::NONE,
1897                base_class: ClassCode::UNCLASSIFIED,
1898                type0_sub_vendor_id: 0,
1899                type0_sub_system_id: 0,
1900            },
1901            vec![
1902                Box::new(ReadOnlyCapability::new("foo", 0)),
1903                Box::new(PciExpressCapability::new(DevicePortType::Endpoint, None)),
1904                Box::new(ReadOnlyCapability::new("bar", 0)),
1905            ],
1906            DeviceBars::new(),
1907        );
1908        assert!(emu.is_pcie_device());
1909
1910        // Test Type 0 device with no capabilities
1911        let emu = ConfigSpaceType0Emulator::new(
1912            HardwareIds {
1913                vendor_id: 0x1111,
1914                device_id: 0x2222,
1915                revision_id: 1,
1916                prog_if: ProgrammingInterface::NONE,
1917                sub_class: Subclass::NONE,
1918                base_class: ClassCode::UNCLASSIFIED,
1919                type0_sub_vendor_id: 0,
1920                type0_sub_system_id: 0,
1921            },
1922            vec![],
1923            DeviceBars::new(),
1924        );
1925        assert!(!emu.is_pcie_device());
1926    }
1927
1928    #[test]
1929    fn test_capability_ids() {
1930        // Test that capabilities return the correct capability IDs
1931        let pcie_cap = PciExpressCapability::new(DevicePortType::Endpoint, None);
1932        assert_eq!(pcie_cap.capability_id(), CapabilityId::PCI_EXPRESS);
1933
1934        let read_only_cap = ReadOnlyCapability::new("test", 0u32);
1935        assert_eq!(read_only_cap.capability_id(), CapabilityId::VENDOR_SPECIFIC);
1936    }
1937
1938    #[test]
1939    fn test_common_header_emulator_type0() {
1940        // Test the common header emulator with Type 0 configuration (6 BARs)
1941        let hardware_ids = HardwareIds {
1942            vendor_id: 0x1111,
1943            device_id: 0x2222,
1944            revision_id: 1,
1945            prog_if: ProgrammingInterface::NONE,
1946            sub_class: Subclass::NONE,
1947            base_class: ClassCode::UNCLASSIFIED,
1948            type0_sub_vendor_id: 0,
1949            type0_sub_system_id: 0,
1950        };
1951
1952        let bars = DeviceBars::new().bar0(4096, BarMemoryKind::Dummy);
1953
1954        let common_emu: ConfigSpaceCommonHeaderEmulatorType0 =
1955            ConfigSpaceCommonHeaderEmulator::new(hardware_ids, vec![], bars);
1956
1957        assert_eq!(common_emu.hardware_ids().vendor_id, 0x1111);
1958        assert_eq!(common_emu.hardware_ids().device_id, 0x2222);
1959        assert!(!common_emu.multi_function_bit());
1960        assert!(!common_emu.is_pcie_device());
1961        assert_ne!(common_emu.bar_masks()[0], 0); // Should have a mask for BAR0
1962    }
1963
1964    #[test]
1965    fn test_common_header_emulator_type1() {
1966        // Test the common header emulator with Type 1 configuration (2 BARs)
1967        let hardware_ids = HardwareIds {
1968            vendor_id: 0x3333,
1969            device_id: 0x4444,
1970            revision_id: 1,
1971            prog_if: ProgrammingInterface::NONE,
1972            sub_class: Subclass::BRIDGE_PCI_TO_PCI,
1973            base_class: ClassCode::BRIDGE,
1974            type0_sub_vendor_id: 0,
1975            type0_sub_system_id: 0,
1976        };
1977
1978        let bars = DeviceBars::new().bar0(4096, BarMemoryKind::Dummy);
1979
1980        let mut common_emu: ConfigSpaceCommonHeaderEmulatorType1 =
1981            ConfigSpaceCommonHeaderEmulator::new(
1982                hardware_ids,
1983                vec![Box::new(PciExpressCapability::new(
1984                    DevicePortType::RootPort,
1985                    None,
1986                ))],
1987                bars,
1988            )
1989            .with_multi_function_bit(true);
1990
1991        assert_eq!(common_emu.hardware_ids().vendor_id, 0x3333);
1992        assert_eq!(common_emu.hardware_ids().device_id, 0x4444);
1993        assert!(common_emu.multi_function_bit());
1994        assert!(common_emu.is_pcie_device());
1995        assert_ne!(common_emu.bar_masks()[0], 0); // Should have a mask for BAR0
1996        assert_eq!(common_emu.bar_masks().len(), 2);
1997
1998        // Test reset functionality
1999        common_emu.reset();
2000        assert_eq!(common_emu.capabilities().len(), 1); // capabilities should still be there
2001    }
2002
2003    #[test]
2004    fn test_common_header_emulator_no_bars() {
2005        // Test the common header emulator with no BARs configured
2006        let hardware_ids = HardwareIds {
2007            vendor_id: 0x5555,
2008            device_id: 0x6666,
2009            revision_id: 1,
2010            prog_if: ProgrammingInterface::NONE,
2011            sub_class: Subclass::NONE,
2012            base_class: ClassCode::UNCLASSIFIED,
2013            type0_sub_vendor_id: 0,
2014            type0_sub_system_id: 0,
2015        };
2016
2017        // Create bars with no BARs configured
2018        let bars = DeviceBars::new();
2019
2020        let common_emu: ConfigSpaceCommonHeaderEmulatorType0 =
2021            ConfigSpaceCommonHeaderEmulator::new(hardware_ids, vec![], bars);
2022
2023        assert_eq!(common_emu.hardware_ids().vendor_id, 0x5555);
2024        assert_eq!(common_emu.hardware_ids().device_id, 0x6666);
2025
2026        // All BAR masks should be 0 when no BARs are configured
2027        for &mask in common_emu.bar_masks() {
2028            assert_eq!(mask, 0);
2029        }
2030    }
2031
2032    #[test]
2033    fn test_common_header_emulator_type1_ignores_extra_bars() {
2034        // Test that Type 1 emulator ignores BARs beyond index 1 (only supports 2 BARs)
2035        let hardware_ids = HardwareIds {
2036            vendor_id: 0x7777,
2037            device_id: 0x8888,
2038            revision_id: 1,
2039            prog_if: ProgrammingInterface::NONE,
2040            sub_class: Subclass::BRIDGE_PCI_TO_PCI,
2041            base_class: ClassCode::BRIDGE,
2042            type0_sub_vendor_id: 0,
2043            type0_sub_system_id: 0,
2044        };
2045
2046        // Configure BARs 0, 2, and 4 - Type 1 should only use BAR0 (and BAR1 as upper 32 bits)
2047        let bars = DeviceBars::new()
2048            .bar0(4096, BarMemoryKind::Dummy)
2049            .bar2(8192, BarMemoryKind::Dummy)
2050            .bar4(16384, BarMemoryKind::Dummy);
2051
2052        let common_emu: ConfigSpaceCommonHeaderEmulatorType1 =
2053            ConfigSpaceCommonHeaderEmulator::new(hardware_ids, vec![], bars);
2054
2055        assert_eq!(common_emu.hardware_ids().vendor_id, 0x7777);
2056        assert_eq!(common_emu.hardware_ids().device_id, 0x8888);
2057
2058        // Should have a mask for BAR0, and BAR1 should be the upper 32 bits (64-bit BAR)
2059        assert_ne!(common_emu.bar_masks()[0], 0); // BAR0 should be configured
2060        assert_ne!(common_emu.bar_masks()[1], 0); // BAR1 should be upper 32 bits of BAR0
2061        assert_eq!(common_emu.bar_masks().len(), 2); // Type 1 only has 2 BARs
2062
2063        // BAR2 and higher should be ignored (not accessible in Type 1 with N=2)
2064        // This demonstrates that extra BARs in DeviceBars are properly ignored
2065    }
2066
2067    #[test]
2068    fn test_common_header_extended_capabilities() {
2069        // Test common header emulator extended capabilities
2070        let mut common_emu_no_pcie = ConfigSpaceCommonHeaderEmulatorType0::new(
2071            HardwareIds {
2072                vendor_id: 0x1111,
2073                device_id: 0x2222,
2074                revision_id: 1,
2075                prog_if: ProgrammingInterface::NONE,
2076                sub_class: Subclass::NONE,
2077                base_class: ClassCode::UNCLASSIFIED,
2078                type0_sub_vendor_id: 0,
2079                type0_sub_system_id: 0,
2080            },
2081            vec![Box::new(ReadOnlyCapability::new("foo", 0))],
2082            DeviceBars::new(),
2083        );
2084        assert!(!common_emu_no_pcie.is_pcie_device());
2085
2086        let mut common_emu_pcie = ConfigSpaceCommonHeaderEmulatorType0::new(
2087            HardwareIds {
2088                vendor_id: 0x1111,
2089                device_id: 0x2222,
2090                revision_id: 1,
2091                prog_if: ProgrammingInterface::NONE,
2092                sub_class: Subclass::NONE,
2093                base_class: ClassCode::UNCLASSIFIED,
2094                type0_sub_vendor_id: 0,
2095                type0_sub_system_id: 0,
2096            },
2097            vec![Box::new(PciExpressCapability::new(
2098                DevicePortType::Endpoint,
2099                None,
2100            ))],
2101            DeviceBars::new(),
2102        );
2103        assert!(common_emu_pcie.is_pcie_device());
2104
2105        // Test reading extended capabilities - non-PCIe device should return error
2106        let mut value = 0;
2107        assert!(matches!(
2108            common_emu_no_pcie.read_extended_capabilities(0x100, &mut value),
2109            CommonHeaderResult::Failed(IoError::InvalidRegister)
2110        ));
2111
2112        // Test reading extended capabilities - PCIe device should return 0xffffffff
2113        let mut value = 0;
2114        assert!(matches!(
2115            common_emu_pcie.read_extended_capabilities(0x100, &mut value),
2116            CommonHeaderResult::Handled
2117        ));
2118        assert_eq!(value, 0xffffffff);
2119
2120        // Test writing extended capabilities - non-PCIe device should return error
2121        assert!(matches!(
2122            common_emu_no_pcie.write_extended_capabilities(0x100, 0x1234),
2123            CommonHeaderResult::Failed(IoError::InvalidRegister)
2124        ));
2125
2126        // Test writing extended capabilities - PCIe device should accept writes
2127        assert!(matches!(
2128            common_emu_pcie.write_extended_capabilities(0x100, 0x1234),
2129            CommonHeaderResult::Handled
2130        ));
2131
2132        // Test invalid offset ranges
2133        let mut value = 0;
2134        assert!(matches!(
2135            common_emu_pcie.read_extended_capabilities(0x99, &mut value),
2136            CommonHeaderResult::Failed(IoError::InvalidRegister)
2137        ));
2138        assert!(matches!(
2139            common_emu_pcie.read_extended_capabilities(0x1000, &mut value),
2140            CommonHeaderResult::Failed(IoError::InvalidRegister)
2141        ));
2142    }
2143
2144    #[test]
2145    fn test_common_header_emulator_save_restore() {
2146        use vmcore::save_restore::SaveRestore;
2147
2148        // Test Type 0 common header emulator save/restore
2149        let hardware_ids = HardwareIds {
2150            vendor_id: 0x1111,
2151            device_id: 0x2222,
2152            revision_id: 1,
2153            prog_if: ProgrammingInterface::NONE,
2154            sub_class: Subclass::NONE,
2155            base_class: ClassCode::UNCLASSIFIED,
2156            type0_sub_vendor_id: 0,
2157            type0_sub_system_id: 0,
2158        };
2159
2160        let bars = DeviceBars::new().bar0(4096, BarMemoryKind::Dummy);
2161
2162        let mut common_emu: ConfigSpaceCommonHeaderEmulatorType0 =
2163            ConfigSpaceCommonHeaderEmulator::new(hardware_ids, vec![], bars);
2164
2165        // Modify some state
2166        let mut test_val = 0u32;
2167        let result = common_emu.write_u32(0x04, 0x0007); // Enable some command bits
2168        assert_eq!(result, CommonHeaderResult::Handled);
2169        let result = common_emu.read_u32(0x04, &mut test_val);
2170        assert_eq!(result, CommonHeaderResult::Handled);
2171        assert_eq!(test_val & 0x0007, 0x0007);
2172
2173        // Save the state
2174        let saved_state = common_emu.save().expect("save should succeed");
2175
2176        // Reset the emulator
2177        common_emu.reset();
2178        let result = common_emu.read_u32(0x04, &mut test_val);
2179        assert_eq!(result, CommonHeaderResult::Handled);
2180        assert_eq!(test_val & 0x0007, 0x0000); // Should be reset
2181
2182        // Restore the state
2183        common_emu
2184            .restore(saved_state)
2185            .expect("restore should succeed");
2186        let result = common_emu.read_u32(0x04, &mut test_val);
2187        assert_eq!(result, CommonHeaderResult::Handled);
2188        assert_eq!(test_val & 0x0007, 0x0007); // Should be restored
2189
2190        // Test Type 1 common header emulator save/restore
2191        let hardware_ids = HardwareIds {
2192            vendor_id: 0x3333,
2193            device_id: 0x4444,
2194            revision_id: 1,
2195            prog_if: ProgrammingInterface::NONE,
2196            sub_class: Subclass::BRIDGE_PCI_TO_PCI,
2197            base_class: ClassCode::BRIDGE,
2198            type0_sub_vendor_id: 0,
2199            type0_sub_system_id: 0,
2200        };
2201
2202        let bars = DeviceBars::new(); // No BARs for Type 1
2203
2204        let mut common_emu_type1: ConfigSpaceCommonHeaderEmulatorType1 =
2205            ConfigSpaceCommonHeaderEmulator::new(hardware_ids, vec![], bars);
2206
2207        // Modify some state
2208        let result = common_emu_type1.write_u32(0x04, 0x0003); // Enable some command bits
2209        assert_eq!(result, CommonHeaderResult::Handled);
2210        let result = common_emu_type1.read_u32(0x04, &mut test_val);
2211        assert_eq!(result, CommonHeaderResult::Handled);
2212        assert_eq!(test_val & 0x0003, 0x0003);
2213
2214        // Save the state
2215        let saved_state = common_emu_type1.save().expect("save should succeed");
2216
2217        // Reset the emulator
2218        common_emu_type1.reset();
2219        let result = common_emu_type1.read_u32(0x04, &mut test_val);
2220        assert_eq!(result, CommonHeaderResult::Handled);
2221        assert_eq!(test_val & 0x0003, 0x0000); // Should be reset
2222
2223        // Restore the state
2224        common_emu_type1
2225            .restore(saved_state)
2226            .expect("restore should succeed");
2227        let result = common_emu_type1.read_u32(0x04, &mut test_val);
2228        assert_eq!(result, CommonHeaderResult::Handled);
2229        assert_eq!(test_val & 0x0003, 0x0003); // Should be restored
2230    }
2231
2232    #[test]
2233    fn test_config_space_type1_set_presence_detect_state() {
2234        // Test that ConfigSpaceType1Emulator can set presence detect state
2235        // when it has a PCIe Express capability with hotplug support
2236
2237        // Create a PCIe Express capability with hotplug support
2238        let pcie_cap =
2239            PciExpressCapability::new(DevicePortType::RootPort, None).with_hotplug_support(1);
2240
2241        let mut emulator = create_type1_emulator(vec![Box::new(pcie_cap)]);
2242
2243        // Initially, presence detect state should be 0
2244        let mut slot_status_val = 0u32;
2245        let result = emulator.read_u32(0x58, &mut slot_status_val); // 0x40 (cap start) + 0x18 (slot control/status)
2246        assert!(matches!(result, IoResult::Ok));
2247        let initial_presence_detect = (slot_status_val >> 22) & 0x1; // presence_detect_state is bit 6 of slot status
2248        assert_eq!(
2249            initial_presence_detect, 0,
2250            "Initial presence detect state should be 0"
2251        );
2252
2253        // Set device as present
2254        emulator.set_presence_detect_state(true);
2255        let result = emulator.read_u32(0x58, &mut slot_status_val);
2256        assert!(matches!(result, IoResult::Ok));
2257        let present_presence_detect = (slot_status_val >> 22) & 0x1;
2258        assert_eq!(
2259            present_presence_detect, 1,
2260            "Presence detect state should be 1 when device is present"
2261        );
2262
2263        // Set device as not present
2264        emulator.set_presence_detect_state(false);
2265        let result = emulator.read_u32(0x58, &mut slot_status_val);
2266        assert!(matches!(result, IoResult::Ok));
2267        let absent_presence_detect = (slot_status_val >> 22) & 0x1;
2268        assert_eq!(
2269            absent_presence_detect, 0,
2270            "Presence detect state should be 0 when device is not present"
2271        );
2272    }
2273
2274    #[test]
2275    fn test_config_space_type1_set_presence_detect_state_without_pcie() {
2276        // Test that ConfigSpaceType1Emulator silently ignores set_presence_detect_state
2277        // when there is no PCIe Express capability
2278
2279        let mut emulator = create_type1_emulator(vec![]); // No capabilities
2280
2281        // Should not panic and should be silently ignored
2282        emulator.set_presence_detect_state(true);
2283        emulator.set_presence_detect_state(false);
2284    }
2285
2286    #[test]
2287    fn test_interrupt_pin_register() {
2288        use vmcore::line_interrupt::LineInterrupt;
2289
2290        // Test Type 0 device with interrupt pin configured
2291        let mut emu = ConfigSpaceType0Emulator::new(
2292            HardwareIds {
2293                vendor_id: 0x1111,
2294                device_id: 0x2222,
2295                revision_id: 1,
2296                prog_if: ProgrammingInterface::NONE,
2297                sub_class: Subclass::NONE,
2298                base_class: ClassCode::UNCLASSIFIED,
2299                type0_sub_vendor_id: 0,
2300                type0_sub_system_id: 0,
2301            },
2302            vec![],
2303            DeviceBars::new(),
2304        );
2305
2306        // Initially, no interrupt pin should be configured
2307        let mut val = 0u32;
2308        emu.read_u32(0x3C, &mut val).unwrap(); // LATENCY_INTERRUPT register
2309        assert_eq!(val & 0xFF00, 0); // Interrupt pin should be 0
2310
2311        // Configure interrupt pin A
2312        let line_interrupt = LineInterrupt::detached();
2313        emu.set_interrupt_pin(PciInterruptPin::IntA, line_interrupt);
2314
2315        // Read the register again
2316        emu.read_u32(0x3C, &mut val).unwrap();
2317        assert_eq!((val >> 8) & 0xFF, 1); // Interrupt pin should be 1 (INTA)
2318
2319        // Set interrupt line to 0x42 and verify both pin and line are correct
2320        emu.write_u32(0x3C, 0x00110042).unwrap(); // Latency=0x11, pin=ignored, line=0x42
2321        emu.read_u32(0x3C, &mut val).unwrap();
2322        assert_eq!(val & 0xFF, 0x42); // Interrupt line should be 0x42
2323        assert_eq!((val >> 8) & 0xFF, 1); // Interrupt pin should still be 1 (writes ignored)
2324        assert_eq!((val >> 16) & 0xFF, 0x11); // Latency timer should be 0x11
2325
2326        // Test with interrupt pin D
2327        let mut emu_d = ConfigSpaceType0Emulator::new(
2328            HardwareIds {
2329                vendor_id: 0x1111,
2330                device_id: 0x2222,
2331                revision_id: 1,
2332                prog_if: ProgrammingInterface::NONE,
2333                sub_class: Subclass::NONE,
2334                base_class: ClassCode::UNCLASSIFIED,
2335                type0_sub_vendor_id: 0,
2336                type0_sub_system_id: 0,
2337            },
2338            vec![],
2339            DeviceBars::new(),
2340        );
2341
2342        let line_interrupt_d = LineInterrupt::detached();
2343        emu_d.set_interrupt_pin(PciInterruptPin::IntD, line_interrupt_d);
2344
2345        emu_d.read_u32(0x3C, &mut val).unwrap();
2346        assert_eq!((val >> 8) & 0xFF, 4); // Interrupt pin should be 4 (INTD)
2347    }
2348
2349    #[test]
2350    fn test_header_type_functionality() {
2351        // Test HeaderType enum values
2352        assert_eq!(HeaderType::Type0.bar_count(), 6);
2353        assert_eq!(HeaderType::Type1.bar_count(), 2);
2354        assert_eq!(usize::from(HeaderType::Type0), 6);
2355        assert_eq!(usize::from(HeaderType::Type1), 2);
2356
2357        // Test constant values
2358        assert_eq!(header_type_consts::TYPE0_BAR_COUNT, 6);
2359        assert_eq!(header_type_consts::TYPE1_BAR_COUNT, 2);
2360
2361        // Test Type 0 emulator
2362        let emu_type0 = create_type0_emulator(vec![]);
2363        assert_eq!(emu_type0.common.bar_count(), 6);
2364        assert_eq!(emu_type0.common.header_type(), HeaderType::Type0);
2365        assert!(emu_type0.common.validate_header_type(HeaderType::Type0));
2366        assert!(!emu_type0.common.validate_header_type(HeaderType::Type1));
2367
2368        // Test Type 1 emulator
2369        let emu_type1 = create_type1_emulator(vec![]);
2370        assert_eq!(emu_type1.common.bar_count(), 2);
2371        assert_eq!(emu_type1.common.header_type(), HeaderType::Type1);
2372        assert!(emu_type1.common.validate_header_type(HeaderType::Type1));
2373        assert!(!emu_type1.common.validate_header_type(HeaderType::Type0));
2374    }
2375}