1use crate::BDF_BUS_SHIFT;
7use crate::BDF_DEVICE_FUNCTION_MASK;
8use crate::BDF_DEVICE_SHIFT;
9use crate::MAX_FUNCTIONS_PER_BUS;
10use crate::PAGE_OFFSET_MASK;
11use crate::PAGE_SHIFT;
12use crate::PAGE_SIZE64;
13use crate::ROOT_PORT_DEVICE_ID;
14use crate::VENDOR_ID;
15use crate::port::PcieDownstreamPort;
16use crate::port::PciePortSettings;
17use chipset_device::ChipsetDevice;
18use chipset_device::io::IoError;
19use chipset_device::io::IoResult;
20use chipset_device::mmio::ControlMmioIntercept;
21use chipset_device::mmio::MmioIntercept;
22use chipset_device::mmio::RegisterMmioIntercept;
23use cxl_spec::CxlComponentRegisters;
24use inspect::Inspect;
25use inspect::InspectMut;
26use memory_range::MemoryRange;
27use pci_bus::GenericPciBusDevice;
28use pci_core::bus_range::AssignedBusRange;
29use pci_core::msi::MsiTarget;
30use pci_core::spec::caps::pci_express::DevicePortType;
31use pci_core::spec::hwid::ClassCode;
32use pci_core::spec::hwid::HardwareIds;
33use pci_core::spec::hwid::ProgrammingInterface;
34use pci_core::spec::hwid::Subclass;
35use std::ops::RangeInclusive;
36use std::sync::Arc;
37use thiserror::Error;
38use vmcore::device_state::ChangeDeviceState;
39use zerocopy::IntoBytes;
40
41#[derive(Debug, Error)]
43#[error("requested {port_count} root ports, but only {max} are supported")]
44pub struct InvalidRootComplexError {
45 port_count: usize,
46 max: usize,
47}
48
49#[derive(InspectMut)]
51pub struct GenericPcieRootComplex {
52 start_bus: u8,
54 end_bus: u8,
56 ecam: Box<dyn ControlMmioIntercept>,
58 chbcr: Option<Box<dyn ControlMmioIntercept>>,
60 cxl_component_registers: Option<CxlComponentRegisters>,
62 #[inspect(with = "|x| inspect::iter_by_key(x.iter().map(|(k, v)| (k, v)))")]
65 devices: Vec<(u8, BusDevice)>,
66}
67
68enum BusDevice {
70 RootPort { name: Arc<str>, port: Box<RootPort> },
72 Rciep {
74 name: Arc<str>,
75 dev: Box<dyn GenericPciBusDevice>,
76 },
77}
78
79impl Inspect for BusDevice {
80 fn inspect(&self, req: inspect::Request<'_>) {
81 match self {
82 BusDevice::RootPort { port, .. } => {
83 port.as_ref().inspect(req);
84 }
85 BusDevice::Rciep { name, .. } => {
86 req.value(name.as_ref());
87 }
88 }
89 }
90}
91
92pub struct DownstreamPortInfo {
94 pub devfn: u8,
96 pub name: Arc<str>,
98 pub bus_range: AssignedBusRange,
101}
102
103pub struct GenericPcieRootPortDefinition {
105 pub name: Arc<str>,
107 pub hotplug: bool,
109 pub settings: PciePortSettings,
111}
112
113pub struct GenericSwitchDefinition {
115 pub name: Arc<str>,
117 pub num_downstream_ports: u8,
119 pub parent_port: Arc<str>,
121 pub hotplug: bool,
123 pub dsp_settings: PciePortSettings,
125}
126
127impl GenericSwitchDefinition {
128 pub fn new(
130 name: impl Into<Arc<str>>,
131 num_downstream_ports: u8,
132 parent_port: impl Into<Arc<str>>,
133 hotplug: bool,
134 dsp_settings: PciePortSettings,
135 ) -> Self {
136 Self {
137 name: name.into(),
138 num_downstream_ports,
139 parent_port: parent_port.into(),
140 hotplug,
141 dsp_settings,
142 }
143 }
144}
145
146enum DecodedEcamAccess<'a> {
147 UnexpectedIntercept,
148 Unroutable,
149 InternalBus(&'a mut RootPort, u16),
150 DownstreamPort(&'a mut RootPort, u8, u8, u16),
151 Rciep(&'a mut dyn GenericPciBusDevice, u8, u16),
154}
155
156pub struct GenericPcieRootComplexBuilder<'a> {
161 register_mmio: &'a mut dyn RegisterMmioIntercept,
162 bus_range: RangeInclusive<u8>,
163 ecam_range: MemoryRange,
164 root_ports: Option<(Vec<GenericPcieRootPortDefinition>, &'a MsiTarget)>,
165 first_port_device_number: u8,
166 chbcr_range: Option<MemoryRange>,
167}
168
169impl<'a> GenericPcieRootComplexBuilder<'a> {
170 pub fn root_ports(
176 mut self,
177 ports: Vec<GenericPcieRootPortDefinition>,
178 msi_target: &'a MsiTarget,
179 ) -> Self {
180 self.root_ports = Some((ports, msi_target));
181 self
182 }
183
184 pub fn first_port_device_number(mut self, device: u8) -> Self {
190 self.first_port_device_number = device;
191 self
192 }
193
194 pub fn chbcr_range(mut self, range: Option<MemoryRange>) -> Self {
199 self.chbcr_range = range;
200 self
201 }
202
203 pub fn build(self) -> Result<GenericPcieRootComplex, InvalidRootComplexError> {
208 let Self {
209 register_mmio,
210 bus_range,
211 ecam_range,
212 root_ports,
213 first_port_device_number,
214 chbcr_range,
215 } = self;
216
217 let start_bus = *bus_range.start();
218 let end_bus = *bus_range.end();
219
220 let mut ecam = register_mmio.new_io_region("ecam", ecam_range.len());
221 ecam.map(ecam_range.start());
222
223 let cxl_component_registers = chbcr_range.as_ref().map(|_| CxlComponentRegisters::new());
226
227 let chbcr = chbcr_range.map(|range| {
228 tracing::info!(
229 root_bus_start = start_bus,
230 root_bus_end = end_bus,
231 start = range.start(),
232 end = range.end(),
233 len = range.len(),
234 "pcie root complex CHBCR range"
235 );
236 let mut region = register_mmio.new_io_region("chbcr", range.len());
237 region.map(range.start());
238 region
239 });
240
241 let mut devices: Vec<(u8, BusDevice)> = Vec::new();
242
243 if let Some((ports, msi_target)) = root_ports {
244 let port_count = ports.len();
247 let max = 32usize.saturating_sub(first_port_device_number as usize) * 8;
248 if port_count > max {
249 return Err(InvalidRootComplexError { port_count, max });
250 }
251
252 let multi_function = port_count > 1;
253
254 for (i, definition) in ports.into_iter().enumerate() {
255 let device = (i / 8) + first_port_device_number as usize;
256 let function = i % 8;
257 let devfn = ((device as u8) << BDF_DEVICE_SHIFT) | function as u8;
258 let hotplug_slot_number = if definition.hotplug {
259 Some(i as u32 + 1)
260 } else {
261 None
262 };
263 let port_msi_target = msi_target.with_devfn(devfn);
264 let root_port = RootPort::new(
265 register_mmio,
266 definition.name.clone(),
267 multi_function,
268 hotplug_slot_number,
269 &port_msi_target,
270 definition.settings,
271 );
272 devices.push((
273 devfn,
274 BusDevice::RootPort {
275 name: definition.name,
276 port: Box::new(root_port),
277 },
278 ));
279 }
280 }
281
282 Ok(GenericPcieRootComplex {
283 start_bus,
284 end_bus,
285 ecam,
286 chbcr,
287 cxl_component_registers,
288 devices,
289 })
290 }
291}
292
293impl GenericPcieRootComplex {
294 pub fn builder<'a>(
296 register_mmio: &'a mut dyn RegisterMmioIntercept,
297 bus_range: RangeInclusive<u8>,
298 ecam_range: MemoryRange,
299 ) -> GenericPcieRootComplexBuilder<'a> {
300 assert_eq!(
301 ecam_size_from_bus_numbers(*bus_range.start(), *bus_range.end()),
302 ecam_range.len(),
303 "ECAM range size does not match bus range"
304 );
305
306 GenericPcieRootComplexBuilder {
307 register_mmio,
308 bus_range,
309 ecam_range,
310 root_ports: None,
311 first_port_device_number: 0,
312 chbcr_range: None,
313 }
314 }
315
316 fn read_chbcr_component_registers(&self, offset: u16, data: &mut [u8]) -> IoResult {
320 let Some(component_regs) = &self.cxl_component_registers else {
321 data.fill(0);
322 return IoResult::Ok;
323 };
324
325 match component_regs.read(offset, data) {
326 IoResult::Err(IoError::InvalidRegister) => {
327 data.fill(0);
329 IoResult::Ok
330 }
331 res => res,
332 }
333 }
334
335 fn write_chbcr_component_registers(&mut self, offset: u16, data: &[u8]) -> IoResult {
339 let Some(component_regs) = &mut self.cxl_component_registers else {
340 return IoResult::Ok;
341 };
342
343 match component_regs.write(offset, data) {
344 IoResult::Err(IoError::InvalidRegister) => IoResult::Ok,
346 res => res,
347 }
348 }
349
350 pub fn add_pcie_device(
353 &mut self,
354 port_devfn: u8,
355 name: impl AsRef<str>,
356 dev: Box<dyn GenericPciBusDevice>,
357 ) -> Result<(), Arc<str>> {
358 let root_port = match self.devices.iter_mut().find(|(d, _)| *d == port_devfn) {
359 Some((_, BusDevice::RootPort { port, .. })) => port,
360 Some((_, BusDevice::Rciep { name: existing, .. })) => return Err(existing.clone()),
361 None => return Err(format!("devfn {port_devfn} is not a root port").into()),
362 };
363
364 root_port.connect_device(name, dev)
365 }
366
367 pub fn downstream_ports(&self) -> Vec<DownstreamPortInfo> {
369 self.devices
370 .iter()
371 .filter_map(|(devfn, d)| match d {
372 BusDevice::RootPort { name, port } => Some(DownstreamPortInfo {
373 devfn: *devfn,
374 name: name.clone(),
375 bus_range: port.port.bus_range(),
376 }),
377 BusDevice::Rciep { .. } => None,
378 })
379 .collect()
380 }
381
382 pub fn hotplug_add_device(
384 &mut self,
385 port_name: &str,
386 device_name: &str,
387 device: Box<dyn GenericPciBusDevice>,
388 ) -> anyhow::Result<()> {
389 let root_port = self
390 .devices
391 .iter_mut()
392 .find_map(|(_, d)| match d {
393 BusDevice::RootPort { name, port } if name.as_ref() == port_name => Some(port),
394 BusDevice::RootPort { .. } | BusDevice::Rciep { .. } => None,
395 })
396 .ok_or_else(|| anyhow::anyhow!("port '{}' not found", port_name))?;
397 root_port.port.hotplug_add_device(device_name, device)
398 }
399
400 pub fn hotplug_remove_device(&mut self, port_name: &str) -> anyhow::Result<()> {
402 let root_port = self
403 .devices
404 .iter_mut()
405 .find_map(|(_, d)| match d {
406 BusDevice::RootPort { name, port } if name.as_ref() == port_name => Some(port),
407 BusDevice::RootPort { .. } | BusDevice::Rciep { .. } => None,
408 })
409 .ok_or_else(|| anyhow::anyhow!("port '{}' not found", port_name))?;
410 root_port.port.hotplug_remove_device()
411 }
412
413 pub fn add_rciep(
428 &mut self,
429 devfn: u8,
430 name: impl Into<Arc<str>>,
431 dev: Box<dyn GenericPciBusDevice>,
432 ) -> Result<(), Arc<str>> {
433 let name = name.into();
434 match self.devices.binary_search_by_key(&devfn, |(d, _)| *d) {
435 Ok(i) => {
436 let existing = match &self.devices[i].1 {
437 BusDevice::RootPort { name, .. } | BusDevice::Rciep { name, .. } => {
438 name.clone()
439 }
440 };
441 return Err(existing);
442 }
443 Err(i) => {
444 self.devices
445 .insert(i, (devfn, BusDevice::Rciep { name, dev }));
446 }
447 }
448 Ok(())
449 }
450
451 fn decode_ecam_access<'a>(&'a mut self, addr: u64) -> DecodedEcamAccess<'a> {
452 let ecam_offset = match self.ecam.offset_of(addr) {
453 Some(offset) => offset,
454 None => {
455 return DecodedEcamAccess::UnexpectedIntercept;
456 }
457 };
458
459 let ecam_based_bdf = (ecam_offset >> PAGE_SHIFT) as u16;
460 let bus_number = ((ecam_based_bdf >> BDF_BUS_SHIFT) as u8) + self.start_bus;
461 let device_function = (ecam_based_bdf & BDF_DEVICE_FUNCTION_MASK) as u8;
462 let cfg_offset_within_function = (ecam_offset & PAGE_OFFSET_MASK) as u16;
463
464 if bus_number == self.start_bus {
465 let function = device_function & 0x7;
466 let devfn_fn0 = device_function & !7;
471 let mut idx = None;
472 let mut exact = false;
473 for (i, (d, _)) in self.devices.iter().enumerate() {
474 if *d == device_function {
475 idx = Some(i);
476 exact = true;
477 break;
478 }
479 if *d == devfn_fn0 {
480 idx = Some(i);
481 }
482 if *d > device_function {
483 break;
484 }
485 }
486 match idx.map(|i| (exact, &mut self.devices[i].1)) {
487 Some((true, BusDevice::RootPort { port, .. })) => {
489 return DecodedEcamAccess::InternalBus(port, cfg_offset_within_function);
490 }
491 Some((false, BusDevice::RootPort { .. })) => {
494 return DecodedEcamAccess::Unroutable;
495 }
496 Some((_, BusDevice::Rciep { dev, .. })) => {
497 return DecodedEcamAccess::Rciep(
498 dev.as_mut(),
499 function,
500 cfg_offset_within_function,
501 );
502 }
503 _ => {
504 return DecodedEcamAccess::Unroutable;
505 }
506 }
507 } else if bus_number > self.start_bus && bus_number <= self.end_bus {
508 for (_, d) in self.devices.iter_mut() {
509 if let BusDevice::RootPort { port, .. } = d {
510 if port
511 .port
512 .cfg_space
513 .assigned_bus_range()
514 .contains(&bus_number)
515 {
516 return DecodedEcamAccess::DownstreamPort(
517 port,
518 bus_number,
519 device_function,
520 cfg_offset_within_function,
521 );
522 }
523 }
524 }
525 return DecodedEcamAccess::Unroutable;
526 }
527
528 DecodedEcamAccess::UnexpectedIntercept
529 }
530
531 fn mmio_read_non_ecam(&mut self, addr: u64, data: &mut [u8]) -> Option<IoResult> {
532 if let Some(chbcr) = &self.chbcr {
533 if let Some(offset) = chbcr.offset_of(addr) {
534 let Some(offset) = u16::try_from(offset).ok() else {
535 data.fill(0);
536 return Some(IoResult::Ok);
537 };
538 return Some(self.read_chbcr_component_registers(offset, data));
539 }
540 }
541
542 for (_, d) in self.devices.iter_mut() {
543 if let BusDevice::RootPort { port, .. } = d {
544 if let Some((bar, offset)) = port.port.find_bar(addr) {
545 if let Err(err) =
546 validate_aligned_access(addr, data.len(), &BAR_ALLOWED_ACCESS_SIZES)
547 {
548 return Some(IoResult::Err(err));
549 }
550 return Some(port.port.bar_mmio_read(bar, offset, data));
551 }
552 }
553 }
554
555 None
556 }
557
558 fn mmio_write_non_ecam(&mut self, addr: u64, data: &[u8]) -> Option<IoResult> {
559 if let Some(chbcr) = &self.chbcr {
560 if let Some(offset) = chbcr.offset_of(addr) {
561 let Some(offset) = u16::try_from(offset).ok() else {
562 return Some(IoResult::Err(IoError::InvalidRegister));
563 };
564 return Some(self.write_chbcr_component_registers(offset, data));
565 }
566 }
567
568 for (_, d) in self.devices.iter_mut() {
569 if let BusDevice::RootPort { port, .. } = d {
570 if let Some((bar, offset)) = port.port.find_bar(addr) {
571 if let Err(err) =
572 validate_aligned_access(addr, data.len(), &BAR_ALLOWED_ACCESS_SIZES)
573 {
574 return Some(IoResult::Err(err));
575 }
576 return Some(port.port.bar_mmio_write(bar, offset, data));
577 }
578 }
579 }
580
581 None
582 }
583}
584
585fn ecam_size_from_bus_numbers(start_bus: u8, end_bus: u8) -> u64 {
586 assert!(end_bus >= start_bus);
587 let bus_count = (end_bus as u16) - (start_bus as u16) + 1;
588 (bus_count as u64) * (MAX_FUNCTIONS_PER_BUS as u64) * PAGE_SIZE64
589}
590
591impl ChangeDeviceState for GenericPcieRootComplex {
592 fn start(&mut self) {}
593
594 async fn stop(&mut self) {}
595
596 async fn reset(&mut self) {
597 for (_, d) in self.devices.iter_mut() {
598 if let BusDevice::RootPort { port, .. } = d {
599 port.port.cfg_space.reset();
600 }
601 }
602 }
603}
604
605impl ChipsetDevice for GenericPcieRootComplex {
606 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
607 Some(self)
608 }
609}
610
611const ECAM_ALLOWED_ACCESS_SIZES: [usize; 3] = [1, 2, 4];
612const BAR_ALLOWED_ACCESS_SIZES: [usize; 4] = [1, 2, 4, 8];
613
614fn validate_aligned_access(
615 address: u64,
616 len: usize,
617 allowed_sizes: &[usize],
618) -> Result<(), IoError> {
619 if !allowed_sizes.contains(&len) {
620 return Err(IoError::InvalidAccessSize);
621 }
622
623 if !address.is_multiple_of(len as u64) {
624 return Err(IoError::UnalignedAccess);
625 }
626
627 Ok(())
628}
629
630macro_rules! check_result {
631 ($result:expr) => {
632 match $result {
633 IoResult::Ok => (),
634 res => {
635 return res;
636 }
637 }
638 };
639}
640
641impl MmioIntercept for GenericPcieRootComplex {
642 fn mmio_read(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
643 if let Some(result) = self.mmio_read_non_ecam(addr, data) {
644 return result;
645 }
646
647 if let Err(err) = validate_aligned_access(addr, data.len(), &ECAM_ALLOWED_ACCESS_SIZES) {
648 return IoResult::Err(err);
649 }
650
651 let dword_aligned_addr = addr & !3;
658 let mut dword_value = !0;
659 let start_bus = self.start_bus;
660 match self.decode_ecam_access(dword_aligned_addr) {
661 DecodedEcamAccess::UnexpectedIntercept => {
662 tracelimit::warn_ratelimited!(addr, "unexpected intercept at ECAM address");
663 }
664 DecodedEcamAccess::Unroutable => {
665 tracing::debug!(addr, "unroutable config space access");
666 }
667 DecodedEcamAccess::InternalBus(port, cfg_offset) => {
668 check_result!(port.port.cfg_space.read_u32(cfg_offset, &mut dword_value));
669 }
670 DecodedEcamAccess::Rciep(dev, function, cfg_offset) => {
671 let bus = start_bus;
672 if let Some(result) =
673 dev.pci_cfg_read_with_routing(bus, bus, function, cfg_offset, &mut dword_value)
674 {
675 check_result!(result);
676 }
677 }
678 DecodedEcamAccess::DownstreamPort(port, bus_number, function, cfg_offset) => {
679 check_result!(port.forward_cfg_read(
680 &bus_number,
681 &function,
682 cfg_offset & !3,
683 &mut dword_value,
684 ));
685 }
686 }
687
688 let byte_offset_within_dword = (addr & 3) as usize;
689 data.copy_from_slice(
690 &dword_value.as_bytes()
691 [byte_offset_within_dword..byte_offset_within_dword + data.len()],
692 );
693
694 IoResult::Ok
695 }
696
697 fn mmio_write(&mut self, addr: u64, data: &[u8]) -> IoResult {
698 if let Some(result) = self.mmio_write_non_ecam(addr, data) {
699 return result;
700 }
701
702 if let Err(err) = validate_aligned_access(addr, data.len(), &ECAM_ALLOWED_ACCESS_SIZES) {
703 return IoResult::Err(err);
704 }
705
706 let dword_aligned_addr = addr & !3;
714 let write_dword = match data.len() {
715 4 => {
716 let mut temp: u32 = 0;
717 temp.as_mut_bytes().copy_from_slice(data);
718 temp
719 }
720 _ => {
721 let mut temp_bytes: [u8; 4] = [0, 0, 0, 0];
722 check_result!(self.mmio_read(dword_aligned_addr, &mut temp_bytes));
723
724 let byte_offset_within_dword = (addr & 3) as usize;
725 temp_bytes[byte_offset_within_dword..byte_offset_within_dword + data.len()]
726 .copy_from_slice(data);
727
728 let mut temp: u32 = 0;
729 temp.as_mut_bytes().copy_from_slice(&temp_bytes);
730 temp
731 }
732 };
733
734 let start_bus = self.start_bus;
735 match self.decode_ecam_access(dword_aligned_addr) {
736 DecodedEcamAccess::UnexpectedIntercept => {
737 tracelimit::warn_ratelimited!(addr, "unexpected intercept at ECAM address");
738 }
739 DecodedEcamAccess::Unroutable => {
740 tracing::debug!(addr, "unroutable config space access");
741 }
742 DecodedEcamAccess::InternalBus(port, cfg_offset) => {
743 check_result!(port.port.cfg_space.write_u32(cfg_offset, write_dword));
744 }
745 DecodedEcamAccess::Rciep(dev, function, cfg_offset) => {
746 let bus = start_bus;
747 if let Some(result) =
748 dev.pci_cfg_write_with_routing(bus, bus, function, cfg_offset, write_dword)
749 {
750 check_result!(result);
751 }
752 }
753 DecodedEcamAccess::DownstreamPort(port, bus_number, function, cfg_offset) => {
754 check_result!(port.forward_cfg_write(
755 &bus_number,
756 &function,
757 cfg_offset,
758 write_dword,
759 ));
760 }
761 }
762
763 IoResult::Ok
764 }
765}
766
767#[derive(Inspect)]
768struct RootPort {
769 #[inspect(flatten)]
771 port: PcieDownstreamPort,
772}
773
774impl RootPort {
775 pub fn new(
783 register_mmio: &mut dyn RegisterMmioIntercept,
784 name: impl Into<Arc<str>>,
785 multi_function: bool,
786 hotplug_slot_number: Option<u32>,
787 msi_target: &MsiTarget,
788 settings: PciePortSettings,
789 ) -> Self {
790 let name_str = name.into();
791
792 let hardware_ids = HardwareIds {
793 vendor_id: VENDOR_ID,
794 device_id: ROOT_PORT_DEVICE_ID,
795 revision_id: 0,
796 prog_if: ProgrammingInterface::NONE,
797 sub_class: Subclass::BRIDGE_PCI_TO_PCI,
798 base_class: ClassCode::BRIDGE,
799 type0_sub_vendor_id: 0,
800 type0_sub_system_id: 0,
801 };
802
803 let port = PcieDownstreamPort::new(
804 name_str.to_string(),
805 hardware_ids,
806 DevicePortType::RootPort,
807 multi_function,
808 hotplug_slot_number,
809 msi_target,
810 settings,
811 Some(register_mmio),
812 None,
813 );
814
815 Self { port }
816 }
817
818 fn connect_device(
821 &mut self,
822 name: impl AsRef<str>,
823 dev: Box<dyn GenericPciBusDevice>,
824 ) -> Result<(), Arc<str>> {
825 let device_name = name.as_ref();
826 let port_name = self.port.name.clone();
827
828 match self.port.add_pcie_device(&port_name, device_name, dev) {
829 Ok(()) => Ok(()),
830 Err(_error) => {
831 if let Some((existing_name, _)) = &self.port.link {
834 tracing::warn!(
835 "RootPort: '{}' failed to connect device '{}', port already occupied by '{}'",
836 port_name,
837 device_name,
838 existing_name
839 );
840 Err(existing_name.clone())
841 } else {
842 tracing::error!(
844 "RootPort: '{}' connection failed for device '{}' but no existing device found",
845 port_name,
846 device_name
847 );
848 panic!("Port connection failed but no existing device found")
849 }
850 }
851 }
852 }
853
854 fn forward_cfg_read(
855 &mut self,
856 bus: &u8,
857 function: &u8,
858 cfg_offset: u16,
859 value: &mut u32,
860 ) -> IoResult {
861 self.port
862 .forward_cfg_read_with_routing(bus, function, cfg_offset, value)
863 }
864
865 fn forward_cfg_write(
866 &mut self,
867 bus: &u8,
868 function: &u8,
869 cfg_offset: u16,
870 value: u32,
871 ) -> IoResult {
872 self.port
873 .forward_cfg_write_with_routing(bus, function, cfg_offset, value)
874 }
875}
876
877mod save_restore {
878 use super::*;
879 use pci_core::cfg_space_emu::ConfigSpaceType1Emulator;
880 use std::collections::HashSet;
881 use vmcore::save_restore::RestoreError;
882 use vmcore::save_restore::SaveError;
883 use vmcore::save_restore::SaveRestore;
884
885 mod state {
886 use super::ConfigSpaceType1Emulator;
887 use super::CxlComponentRegisters;
888 use super::SaveRestore;
889 use mesh::payload::Protobuf;
890 use vmcore::save_restore::SavedStateRoot;
891
892 type RootPortCfgSpaceSavedState = <ConfigSpaceType1Emulator as SaveRestore>::SavedState;
893 type CxlComponentRegistersSavedState = <CxlComponentRegisters as SaveRestore>::SavedState;
894
895 #[derive(Protobuf)]
897 #[mesh(package = "pcie.root")]
898 pub struct PortSavedState {
899 #[mesh(1)]
901 pub devfn: u8,
902 #[mesh(2)]
904 pub cfg_space: RootPortCfgSpaceSavedState,
905 #[mesh(3)]
907 pub cxl_component_registers: Option<CxlComponentRegistersSavedState>,
908 }
909
910 #[derive(Protobuf, SavedStateRoot)]
912 #[mesh(package = "pcie.root")]
913 pub struct SavedState {
914 #[mesh(1)]
916 pub start_bus: u8,
917 #[mesh(2)]
919 pub end_bus: u8,
920 #[mesh(3)]
922 pub ports: Vec<PortSavedState>,
923 #[mesh(4)]
925 pub cxl_component_registers: Option<CxlComponentRegistersSavedState>,
926 }
927 }
928
929 impl SaveRestore for GenericPcieRootComplex {
930 type SavedState = state::SavedState;
931
932 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
933 let mut ports = Vec::new();
935 for (devfn, d) in self.devices.iter_mut() {
936 if let BusDevice::RootPort { port, .. } = d {
937 ports.push(state::PortSavedState {
938 devfn: *devfn,
939 cfg_space: port.port.cfg_space.save()?,
940 cxl_component_registers: port.port.save_cxl_component_registers_state()?,
941 });
942 }
943 }
944
945 Ok(state::SavedState {
946 start_bus: self.start_bus,
947 end_bus: self.end_bus,
948 ports,
949 cxl_component_registers: self
950 .cxl_component_registers
951 .as_mut()
952 .map(|regs| regs.save())
953 .transpose()?,
954 })
955 }
956
957 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
958 let state::SavedState {
959 start_bus,
960 end_bus,
961 ports,
962 cxl_component_registers,
963 } = state;
964
965 if start_bus != self.start_bus || end_bus != self.end_bus {
967 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
968 "bus number mismatch: saved ({}-{}), current ({}-{})",
969 start_bus,
970 end_bus,
971 self.start_bus,
972 self.end_bus
973 )));
974 }
975
976 let root_port_count = self
978 .devices
979 .iter()
980 .filter(|(_, d)| matches!(d, BusDevice::RootPort { .. }))
981 .count();
982 if ports.len() != root_port_count {
983 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
984 "root port count mismatch: saved {}, current {}",
985 ports.len(),
986 root_port_count
987 )));
988 }
989
990 let mut seen_ports = HashSet::with_capacity(ports.len());
991
992 for port_state in ports {
994 if !seen_ports.insert(port_state.devfn) {
995 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
996 "duplicate root port devfn {} in saved state",
997 port_state.devfn
998 )));
999 }
1000
1001 match self
1002 .devices
1003 .iter_mut()
1004 .find(|(d, _)| *d == port_state.devfn)
1005 {
1006 Some((_, BusDevice::RootPort { port, .. })) => {
1007 port.port.cfg_space.restore(port_state.cfg_space)?;
1008 port.port.restore_cxl_component_registers_state(
1009 port_state.cxl_component_registers,
1010 )?;
1011 }
1012 Some((_, BusDevice::Rciep { .. })) | None => {
1013 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
1014 "root port devfn {} not found",
1015 port_state.devfn
1016 )));
1017 }
1018 }
1019 }
1020
1021 match (&mut self.cxl_component_registers, cxl_component_registers) {
1022 (Some(current), Some(saved)) => current.restore(saved)?,
1023 (None, None) => {}
1024 (Some(_), None) => {
1025 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
1026 "CXL mode mismatch: current root complex has CHBCR registers but saved state does not"
1027 )));
1028 }
1029 (None, Some(_)) => {
1030 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
1031 "CXL mode mismatch: saved state has CHBCR registers but current root complex does not"
1032 )));
1033 }
1034 }
1035
1036 Ok(())
1037 }
1038 }
1039}
1040
1041#[cfg(test)]
1042mod tests {
1043 use super::*;
1044 use crate::test_helpers::*;
1045 use cxl_spec::CxlComponentRegisterType;
1046 use cxl_spec::component_registers::test_helper::TestCxlComponentRegisterBlock;
1047 use pal_async::async_test;
1048
1049 fn instantiate_root_complex(
1050 start_bus: u8,
1051 end_bus: u8,
1052 port_count: u16,
1053 ) -> GenericPcieRootComplex {
1054 let port_defs = (0..port_count)
1055 .map(|i| GenericPcieRootPortDefinition {
1056 name: format!("test-port-{}", i).into(),
1057 hotplug: false,
1058 settings: PciePortSettings::default(),
1059 })
1060 .collect();
1061
1062 let mut register_mmio = TestPcieMmioRegistration {};
1063 let ecam = MemoryRange::new(0..ecam_size_from_bus_numbers(start_bus, end_bus));
1064 let rc_bus_range = AssignedBusRange::new();
1065 rc_bus_range.set_bus_range(start_bus, end_bus);
1066 let msi_conn = pci_core::msi::MsiConnection::new(rc_bus_range, 0);
1067 GenericPcieRootComplex::builder(&mut register_mmio, start_bus..=end_bus, ecam)
1068 .root_ports(port_defs, msi_conn.target())
1069 .build()
1070 .unwrap()
1071 }
1072
1073 fn instantiate_root_complex_with_chbcr(
1074 start_bus: u8,
1075 end_bus: u8,
1076 port_count: u16,
1077 chbcr_start: u64,
1078 ) -> GenericPcieRootComplex {
1079 let port_defs = (0..port_count)
1080 .map(|i| GenericPcieRootPortDefinition {
1081 name: format!("test-port-{}", i).into(),
1082 hotplug: false,
1083 settings: PciePortSettings::default(),
1084 })
1085 .collect();
1086
1087 let mut register_mmio = TestPcieMmioRegistration {};
1088 let ecam = MemoryRange::new(0..ecam_size_from_bus_numbers(start_bus, end_bus));
1089 let chbcr = MemoryRange::new(
1090 chbcr_start..(chbcr_start + cxl_spec::spec::CXL_COMPONENT_REGISTERS_SIZE_BYTES),
1091 );
1092 let rc_bus_range = AssignedBusRange::new();
1093 rc_bus_range.set_bus_range(start_bus, end_bus);
1094 let msi_conn = pci_core::msi::MsiConnection::new(rc_bus_range, 0);
1095
1096 GenericPcieRootComplex::builder(&mut register_mmio, start_bus..=end_bus, ecam)
1097 .root_ports(port_defs, msi_conn.target())
1098 .chbcr_range(Some(chbcr))
1099 .build()
1100 .unwrap()
1101 }
1102
1103 #[test]
1104 fn test_create() {
1105 assert_eq!(
1106 instantiate_root_complex(0, 0, 1).downstream_ports().len(),
1107 1
1108 );
1109 assert_eq!(
1110 instantiate_root_complex(0, 1, 1).downstream_ports().len(),
1111 1
1112 );
1113 assert_eq!(
1114 instantiate_root_complex(1, 1, 1).downstream_ports().len(),
1115 1
1116 );
1117 assert_eq!(
1118 instantiate_root_complex(255, 255, 1)
1119 .downstream_ports()
1120 .len(),
1121 1
1122 );
1123
1124 assert_eq!(
1125 instantiate_root_complex(0, 0, 4).downstream_ports().len(),
1126 4
1127 );
1128
1129 assert_eq!(
1130 instantiate_root_complex(0, 255, 32)
1131 .downstream_ports()
1132 .len(),
1133 32
1134 );
1135 assert_eq!(
1136 instantiate_root_complex(32, 32, 32)
1137 .downstream_ports()
1138 .len(),
1139 32
1140 );
1141 assert_eq!(
1142 instantiate_root_complex(255, 255, 32)
1143 .downstream_ports()
1144 .len(),
1145 32
1146 );
1147
1148 assert_eq!(
1150 instantiate_root_complex(0, 255, 64)
1151 .downstream_ports()
1152 .len(),
1153 64
1154 );
1155 assert_eq!(
1156 instantiate_root_complex(0, 255, 256)
1157 .downstream_ports()
1158 .len(),
1159 256
1160 );
1161 }
1162
1163 #[test]
1164 fn test_ecam_size() {
1165 assert_eq!(ecam_size_from_bus_numbers(0, 0), 0x10_0000);
1167 assert_eq!(ecam_size_from_bus_numbers(32, 32), 0x10_0000);
1168 assert_eq!(ecam_size_from_bus_numbers(255, 255), 0x10_0000);
1169
1170 assert_eq!(ecam_size_from_bus_numbers(0, 1), 0x20_0000);
1172 assert_eq!(ecam_size_from_bus_numbers(32, 33), 0x20_0000);
1173 assert_eq!(ecam_size_from_bus_numbers(254, 255), 0x20_0000);
1174
1175 assert_eq!(ecam_size_from_bus_numbers(0, 255), 0x1000_0000);
1177 }
1178
1179 #[test]
1180 fn test_probe_ports_via_config_space() {
1181 let mut rc = instantiate_root_complex(0, 255, 4);
1182 for devfn in 0..4u64 {
1185 let mut vendor_device: u32 = 0;
1186 rc.mmio_read(devfn * 4096, vendor_device.as_mut_bytes())
1187 .unwrap();
1188 assert_eq!(vendor_device, 0xC030_1414);
1189
1190 let mut value_16: u16 = 0;
1191 rc.mmio_read(devfn * 4096, value_16.as_mut_bytes()).unwrap();
1192 assert_eq!(value_16, 0x1414);
1193
1194 rc.mmio_read(devfn * 4096 + 2, value_16.as_mut_bytes())
1195 .unwrap();
1196 assert_eq!(value_16, 0xC030);
1197 }
1198
1199 for devfn in 4..10u64 {
1200 let mut value_32: u32 = 0;
1201 rc.mmio_read(devfn * 4096, value_32.as_mut_bytes()).unwrap();
1202 assert_eq!(value_32, 0xFFFF_FFFF);
1203
1204 let mut value_16: u16 = 0;
1205 rc.mmio_read(devfn * 4096, value_16.as_mut_bytes()).unwrap();
1206 assert_eq!(value_16, 0xFFFF);
1207 rc.mmio_read(devfn * 4096 + 2, value_16.as_mut_bytes())
1208 .unwrap();
1209 assert_eq!(value_16, 0xFFFF);
1210 }
1211 }
1212
1213 #[test]
1214 fn test_add_downstream_device_to_port() {
1215 let mut rc = instantiate_root_complex(0, 0, 1);
1216
1217 let endpoint1 = TestPcieEndpoint::new(
1218 |offset, value| match offset {
1219 0x0 => {
1220 *value = 0xAAAA_AAAA;
1221 Some(IoResult::Ok)
1222 }
1223 _ => Some(IoResult::Err(IoError::InvalidRegister)),
1224 },
1225 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
1226 );
1227
1228 let endpoint2 = TestPcieEndpoint::new(
1229 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
1230 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
1231 );
1232
1233 rc.add_pcie_device(0, "ep1", Box::new(endpoint1)).unwrap();
1234
1235 rc.add_pcie_device(0, "ep2", Box::new(endpoint2))
1236 .expect_err("should fail: port already occupied");
1237 }
1238
1239 #[test]
1240 fn test_root_port_cfg_forwarding() {
1241 const SECONDARY_BUS_NUM_REG: u64 = 0x19;
1242 const SUBOORDINATE_BUS_NUM_REG: u64 = 0x1A;
1243
1244 let mut rc = instantiate_root_complex(0, 255, 1);
1245
1246 let mut value_32: u32 = 0;
1248 rc.mmio_read(256 * 4096, value_32.as_mut_bytes()).unwrap();
1249 assert_eq!(value_32, 0xFFFF_FFFF);
1250
1251 let mut bus_number: u8 = 0xFF;
1254 rc.mmio_read(SECONDARY_BUS_NUM_REG, bus_number.as_mut_bytes())
1255 .unwrap();
1256 assert_eq!(bus_number, 0);
1257 rc.mmio_read(SUBOORDINATE_BUS_NUM_REG, bus_number.as_mut_bytes())
1258 .unwrap();
1259 assert_eq!(bus_number, 0);
1260
1261 rc.mmio_write(SECONDARY_BUS_NUM_REG, &[1]).unwrap();
1262 rc.mmio_read(SECONDARY_BUS_NUM_REG, bus_number.as_mut_bytes())
1263 .unwrap();
1264 assert_eq!(bus_number, 1);
1265
1266 rc.mmio_write(SUBOORDINATE_BUS_NUM_REG, &[2]).unwrap();
1267 rc.mmio_read(SUBOORDINATE_BUS_NUM_REG, bus_number.as_mut_bytes())
1268 .unwrap();
1269 assert_eq!(bus_number, 2);
1270
1271 rc.mmio_read(256 * 4096, value_32.as_mut_bytes()).unwrap();
1273 assert_eq!(value_32, 0xFFFF_FFFF);
1274
1275 let endpoint = TestPcieEndpoint::new(
1276 |offset, value| match offset {
1277 0x0 => {
1278 *value = 0xDEAD_BEEF;
1279 Some(IoResult::Ok)
1280 }
1281 _ => Some(IoResult::Err(IoError::InvalidRegister)),
1282 },
1283 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
1284 );
1285
1286 rc.add_pcie_device(0, "test-ep", Box::new(endpoint))
1287 .unwrap();
1288
1289 rc.mmio_read(256 * 4096, value_32.as_mut_bytes()).unwrap();
1292 assert_eq!(value_32, 0xDEAD_BEEF);
1293
1294 rc.mmio_write(SECONDARY_BUS_NUM_REG, &[2]).unwrap();
1296 rc.mmio_read(SECONDARY_BUS_NUM_REG, bus_number.as_mut_bytes())
1297 .unwrap();
1298 assert_eq!(bus_number, 2);
1299
1300 rc.mmio_read(256 * 4096, value_32.as_mut_bytes()).unwrap();
1303 assert_eq!(value_32, 0xFFFF_FFFF);
1304 rc.mmio_read(2 * 256 * 4096, value_32.as_mut_bytes())
1305 .unwrap();
1306 assert_eq!(value_32, 0xDEAD_BEEF);
1307 }
1308
1309 #[test]
1310 fn test_chbcr_reads_cxl_component_header_in_cxl_mode() {
1311 let chbcr_start = 0x2000_0000;
1312 let mut rc = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1313
1314 let mut header: u32 = 0;
1316 rc.mmio_read(chbcr_start + 0x1000, header.as_mut_bytes())
1317 .unwrap();
1318 assert_eq!(header, 0x0011_0001);
1319 }
1320
1321 #[test]
1322 fn test_chbcr_read_write_redirects_to_component_registers() {
1323 let chbcr_start = 0x3000_0000;
1324 let mut rc = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1325
1326 assert!(
1328 rc.cxl_component_registers
1329 .as_mut()
1330 .expect("CXL mode must allocate component registers")
1331 .add_register(Box::new(TestCxlComponentRegisterBlock::new(
1332 CxlComponentRegisterType::CXL_CACHE_MEM_REGISTER,
1333 16,
1334 )))
1335 );
1336
1337 let value = 0x1122_3344u32;
1338 rc.mmio_write(chbcr_start + 0x1008, value.as_bytes())
1339 .unwrap();
1340
1341 let mut read_back: u32 = 0;
1342 rc.mmio_read(chbcr_start + 0x1008, read_back.as_mut_bytes())
1343 .unwrap();
1344 assert_eq!(read_back, value);
1345 }
1346
1347 #[test]
1348 fn test_chbcr_8byte_read_write_redirects_to_component_registers() {
1349 let chbcr_start = 0x3001_0000;
1350 let mut rc = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1351
1352 assert!(
1354 rc.cxl_component_registers
1355 .as_mut()
1356 .expect("CXL mode must allocate component registers")
1357 .add_register(Box::new(TestCxlComponentRegisterBlock::new(
1358 CxlComponentRegisterType::CXL_CACHE_MEM_REGISTER,
1359 16,
1360 )))
1361 );
1362
1363 let value = 0x1122_3344_5566_7788u64;
1364 rc.mmio_write(chbcr_start + 0x1008, value.as_bytes())
1365 .unwrap();
1366
1367 let mut read_back: u64 = 0;
1368 rc.mmio_read(chbcr_start + 0x1008, read_back.as_mut_bytes())
1369 .unwrap();
1370 assert_eq!(read_back, value);
1371 }
1372
1373 #[test]
1374 fn test_chbcr_unmapped_read_is_handled() {
1375 let chbcr_start = 0x3002_0000;
1376 let mut rc = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1377
1378 let mut value: u64 = 0;
1381 rc.mmio_read(chbcr_start + 0x800, value.as_mut_bytes())
1382 .unwrap();
1383 assert_eq!(value, 0);
1384 }
1385
1386 #[test]
1387 fn test_chbcr_unmapped_write_is_handled() {
1388 let chbcr_start = 0x3003_0000;
1389 let mut rc = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1390
1391 let value = 0x0123_4567_89ab_cdefu64;
1393 rc.mmio_write(chbcr_start + 0x800, value.as_bytes())
1394 .unwrap();
1395 }
1396
1397 #[async_test]
1398 async fn test_reset() {
1399 const COMMAND_REG: u64 = 0x4;
1400 const COMMAND_REG_VALUE: u16 = 0x0004;
1401 const PORT0_ECAM: u64 = 0;
1402 const PORT1_ECAM: u64 = 4096;
1403
1404 let mut rc = instantiate_root_complex(0, 255, 2);
1405 let mut value_16: u16 = 0;
1406
1407 rc.mmio_write(PORT0_ECAM + COMMAND_REG, COMMAND_REG_VALUE.as_bytes())
1409 .unwrap();
1410 rc.mmio_write(PORT1_ECAM + COMMAND_REG, COMMAND_REG_VALUE.as_bytes())
1411 .unwrap();
1412 rc.mmio_read(PORT0_ECAM + COMMAND_REG, value_16.as_mut_bytes())
1413 .unwrap();
1414 assert_eq!(value_16, COMMAND_REG_VALUE);
1415 rc.mmio_read(PORT1_ECAM + COMMAND_REG, value_16.as_mut_bytes())
1416 .unwrap();
1417 assert_eq!(value_16, COMMAND_REG_VALUE);
1418
1419 rc.reset().await;
1421 rc.mmio_read(PORT0_ECAM + COMMAND_REG, value_16.as_mut_bytes())
1422 .unwrap();
1423 assert_eq!(value_16, 0);
1424 rc.mmio_read(PORT1_ECAM + COMMAND_REG, value_16.as_mut_bytes())
1425 .unwrap();
1426 assert_eq!(value_16, 0);
1427
1428 rc.mmio_write(PORT0_ECAM + COMMAND_REG, COMMAND_REG_VALUE.as_bytes())
1430 .unwrap();
1431 rc.mmio_write(PORT1_ECAM + COMMAND_REG, COMMAND_REG_VALUE.as_bytes())
1432 .unwrap();
1433 rc.mmio_read(PORT0_ECAM + COMMAND_REG, value_16.as_mut_bytes())
1434 .unwrap();
1435 assert_eq!(value_16, COMMAND_REG_VALUE);
1436 rc.mmio_read(PORT1_ECAM + COMMAND_REG, value_16.as_mut_bytes())
1437 .unwrap();
1438 assert_eq!(value_16, COMMAND_REG_VALUE);
1439 }
1440
1441 #[test]
1442 fn test_root_port_hotplug_options() {
1443 let root_port_no_hotplug = {
1445 let mut register_mmio = TestPcieMmioRegistration {};
1446 let c = pci_core::msi::MsiConnection::new(AssignedBusRange::new(), 0);
1447 RootPort::new(
1448 &mut register_mmio,
1449 "test-port-no-hotplug",
1450 false,
1451 None,
1452 c.target(),
1453 PciePortSettings::default(),
1454 )
1455 };
1456 let mut vendor_device_id: u32 = 0;
1459 root_port_no_hotplug
1460 .port
1461 .cfg_space
1462 .read_u32(0x0, &mut vendor_device_id)
1463 .unwrap();
1464 let expected = (ROOT_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
1465 assert_eq!(vendor_device_id, expected);
1466
1467 let root_port_with_hotplug = {
1469 let mut register_mmio = TestPcieMmioRegistration {};
1470 let c = pci_core::msi::MsiConnection::new(AssignedBusRange::new(), 0);
1471 RootPort::new(
1472 &mut register_mmio,
1473 "test-port-hotplug",
1474 false,
1475 Some(5),
1476 c.target(),
1477 PciePortSettings::default(),
1478 )
1479 };
1480 let mut vendor_device_id_hotplug: u32 = 0;
1481 root_port_with_hotplug
1482 .port
1483 .cfg_space
1484 .read_u32(0x0, &mut vendor_device_id_hotplug)
1485 .unwrap();
1486 assert_eq!(vendor_device_id_hotplug, expected);
1487 }
1490
1491 #[test]
1492 fn test_root_port_invalid_bus_range_handling() {
1493 let mut root_port = {
1494 let mut register_mmio = TestPcieMmioRegistration {};
1495 let c = pci_core::msi::MsiConnection::new(AssignedBusRange::new(), 0);
1496 RootPort::new(
1497 &mut register_mmio,
1498 "test-port",
1499 false,
1500 None,
1501 c.target(),
1502 PciePortSettings::default(),
1503 )
1504 };
1505
1506 let bus_range = root_port.port.cfg_space.assigned_bus_range();
1508 assert_eq!(bus_range, 0..=0);
1509
1510 let mut value = 0u32;
1512 let result = root_port
1513 .port
1514 .forward_cfg_read_with_routing(&1, &0, 0x0, &mut value);
1515 assert!(matches!(result, IoResult::Ok));
1516
1517 let result = root_port
1518 .port
1519 .forward_cfg_write_with_routing(&1, &0, 0x0, value);
1520 assert!(matches!(result, IoResult::Ok));
1521 }
1522
1523 #[test]
1524 fn test_save_restore_basic() {
1525 use vmcore::save_restore::SaveRestore;
1526
1527 let mut rc = instantiate_root_complex(0, 255, 4);
1528
1529 let saved_state = rc.save().expect("save should succeed");
1531
1532 assert_eq!(saved_state.start_bus, 0);
1534 assert_eq!(saved_state.end_bus, 255);
1535 assert_eq!(saved_state.ports.len(), 4);
1536
1537 let mut rc2 = instantiate_root_complex(0, 255, 4);
1539 rc2.restore(saved_state).expect("restore should succeed");
1540 }
1541
1542 #[test]
1543 fn test_save_restore_with_bus_configuration() {
1544 use vmcore::save_restore::SaveRestore;
1545 use zerocopy::IntoBytes;
1546
1547 const SECONDARY_BUS_NUM_REG: u64 = 0x19;
1548 const SUBORDINATE_BUS_NUM_REG: u64 = 0x1A;
1549
1550 let mut rc = instantiate_root_complex(0, 255, 2);
1551
1552 rc.mmio_write(SECONDARY_BUS_NUM_REG, &[1]).unwrap();
1554 rc.mmio_write(SUBORDINATE_BUS_NUM_REG, &[10]).unwrap();
1555
1556 const PORT1_ECAM: u64 = 4096;
1558 rc.mmio_write(PORT1_ECAM + SECONDARY_BUS_NUM_REG, &[11])
1559 .unwrap();
1560 rc.mmio_write(PORT1_ECAM + SUBORDINATE_BUS_NUM_REG, &[20])
1561 .unwrap();
1562
1563 let mut bus_number: u8 = 0;
1565 rc.mmio_read(SECONDARY_BUS_NUM_REG, bus_number.as_mut_bytes())
1566 .unwrap();
1567 assert_eq!(bus_number, 1);
1568 rc.mmio_read(SUBORDINATE_BUS_NUM_REG, bus_number.as_mut_bytes())
1569 .unwrap();
1570 assert_eq!(bus_number, 10);
1571
1572 rc.mmio_read(
1573 PORT1_ECAM + SECONDARY_BUS_NUM_REG,
1574 bus_number.as_mut_bytes(),
1575 )
1576 .unwrap();
1577 assert_eq!(bus_number, 11);
1578 rc.mmio_read(
1579 PORT1_ECAM + SUBORDINATE_BUS_NUM_REG,
1580 bus_number.as_mut_bytes(),
1581 )
1582 .unwrap();
1583 assert_eq!(bus_number, 20);
1584
1585 let saved_state = rc.save().expect("save should succeed");
1587
1588 let mut rc2 = instantiate_root_complex(0, 255, 2);
1590
1591 let mut default_bus: u8 = 0xFF;
1593 rc2.mmio_read(SECONDARY_BUS_NUM_REG, default_bus.as_mut_bytes())
1594 .unwrap();
1595 assert_eq!(default_bus, 0);
1596
1597 rc2.restore(saved_state).expect("restore should succeed");
1599
1600 let mut restored_bus: u8 = 0;
1602 rc2.mmio_read(SECONDARY_BUS_NUM_REG, restored_bus.as_mut_bytes())
1603 .unwrap();
1604 assert_eq!(restored_bus, 1);
1605 rc2.mmio_read(SUBORDINATE_BUS_NUM_REG, restored_bus.as_mut_bytes())
1606 .unwrap();
1607 assert_eq!(restored_bus, 10);
1608
1609 rc2.mmio_read(
1610 PORT1_ECAM + SECONDARY_BUS_NUM_REG,
1611 restored_bus.as_mut_bytes(),
1612 )
1613 .unwrap();
1614 assert_eq!(restored_bus, 11);
1615 rc2.mmio_read(
1616 PORT1_ECAM + SUBORDINATE_BUS_NUM_REG,
1617 restored_bus.as_mut_bytes(),
1618 )
1619 .unwrap();
1620 assert_eq!(restored_bus, 20);
1621 }
1622
1623 #[test]
1624 fn test_save_restore_bus_number_mismatch_error() {
1625 use vmcore::save_restore::SaveRestore;
1626
1627 let mut rc = instantiate_root_complex(0, 255, 2);
1629
1630 let saved_state = rc.save().expect("save should succeed");
1632
1633 let mut rc2 = instantiate_root_complex(0, 127, 2);
1635
1636 let result = rc2.restore(saved_state);
1638 assert!(result.is_err());
1639 }
1640
1641 #[test]
1642 fn test_save_restore_port_count_mismatch_error() {
1643 use vmcore::save_restore::SaveRestore;
1644
1645 let mut rc = instantiate_root_complex(0, 255, 4);
1647
1648 let saved_state = rc.save().expect("save should succeed");
1650 assert_eq!(saved_state.ports.len(), 4);
1651
1652 let mut rc2 = instantiate_root_complex(0, 255, 2);
1654
1655 let result = rc2.restore(saved_state);
1657 assert!(result.is_err());
1658 }
1659
1660 #[test]
1661 fn test_save_restore_with_cxl_component_registers() {
1662 use vmcore::save_restore::SaveRestore;
1663
1664 let chbcr_start = 0x3004_0000;
1665 let mut rc = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1666
1667 assert!(
1668 rc.cxl_component_registers
1669 .as_mut()
1670 .expect("CXL mode must allocate component registers")
1671 .add_register(Box::new(TestCxlComponentRegisterBlock::new(
1672 CxlComponentRegisterType::CXL_CACHE_MEM_REGISTER,
1673 16,
1674 )))
1675 );
1676
1677 let programmed = 0x3344_5566u32;
1678 rc.mmio_write(chbcr_start + 0x1008, programmed.as_bytes())
1679 .unwrap();
1680
1681 let saved_state = rc.save().expect("save should succeed");
1682
1683 let mut rc2 = instantiate_root_complex_with_chbcr(0, 0, 1, chbcr_start);
1684 assert!(
1685 rc2.cxl_component_registers
1686 .as_mut()
1687 .expect("CXL mode must allocate component registers")
1688 .add_register(Box::new(TestCxlComponentRegisterBlock::new(
1689 CxlComponentRegisterType::CXL_CACHE_MEM_REGISTER,
1690 16,
1691 )))
1692 );
1693
1694 rc2.restore(saved_state).expect("restore should succeed");
1695
1696 let mut read_back: u32 = 0;
1697 rc2.mmio_read(chbcr_start + 0x1008, read_back.as_mut_bytes())
1698 .unwrap();
1699 assert_eq!(read_back, programmed);
1700 }
1701
1702 #[test]
1703 fn test_bus_range_updated_on_cfg_write() {
1704 const SECONDARY_BUS_NUM_REG: u64 = 0x19;
1705 const SUBORDINATE_BUS_NUM_REG: u64 = 0x1A;
1706
1707 let mut rc = instantiate_root_complex(0, 255, 1);
1708
1709 let endpoint = TestPcieEndpoint::new(
1710 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
1711 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
1712 );
1713
1714 let bus_range = rc.downstream_ports().into_iter().next().unwrap().bus_range;
1716 assert_eq!(bus_range.bus_range(), (0, 0));
1717
1718 rc.add_pcie_device(0, "ep", Box::new(endpoint)).unwrap();
1719
1720 rc.mmio_write(SECONDARY_BUS_NUM_REG, &[5]).unwrap();
1722 rc.mmio_write(SUBORDINATE_BUS_NUM_REG, &[10]).unwrap();
1723
1724 assert_eq!(bus_range.bus_range(), (5, 10));
1726
1727 rc.mmio_write(SECONDARY_BUS_NUM_REG, &[20]).unwrap();
1729 rc.mmio_write(SUBORDINATE_BUS_NUM_REG, &[30]).unwrap();
1730 assert_eq!(bus_range.bus_range(), (20, 30));
1731 }
1732
1733 #[test]
1734 fn test_rciep_ecam_read_write() {
1735 let mut register_mmio = TestPcieMmioRegistration {};
1738 let start_bus: u8 = 0;
1739 let end_bus: u8 = 0;
1740 let ecam = MemoryRange::new(0..ecam_size_from_bus_numbers(start_bus, end_bus));
1741 let rc_bus_range = AssignedBusRange::new();
1742 rc_bus_range.set_bus_range(start_bus, end_bus);
1743 let msi_conn = pci_core::msi::MsiConnection::new(rc_bus_range, 0);
1744 let port_defs = vec![GenericPcieRootPortDefinition {
1745 name: "port-0".into(),
1746 hotplug: false,
1747 settings: PciePortSettings::default(),
1748 }];
1749 let mut rc = GenericPcieRootComplex::builder(&mut register_mmio, start_bus..=end_bus, ecam)
1750 .root_ports(port_defs, msi_conn.target())
1751 .first_port_device_number(1)
1752 .build()
1753 .unwrap();
1754
1755 let rciep = TestPcieEndpoint::new(
1757 |offset, value| {
1758 if offset == 0 {
1759 *value = 0xDEAD_BEEF;
1760 }
1761 Some(IoResult::Ok)
1762 },
1763 |_, _| Some(IoResult::Ok),
1764 );
1765 rc.add_rciep(0, "rciep-0", Box::new(rciep)).unwrap();
1766
1767 let mut vendor_device: u32 = 0;
1769 rc.mmio_read(0, vendor_device.as_mut_bytes()).unwrap();
1770 assert_eq!(vendor_device, 0xDEAD_BEEF);
1771
1772 rc.mmio_write(0, &0x1234_5678u32.to_le_bytes()).unwrap();
1775
1776 let mut root_port_vendor: u32 = 0;
1778 rc.mmio_read(8 * 4096, root_port_vendor.as_mut_bytes())
1780 .unwrap();
1781 assert_eq!(root_port_vendor, 0xC030_1414);
1782
1783 let mut empty: u32 = 0;
1785 rc.mmio_read(16 * 4096, empty.as_mut_bytes()).unwrap();
1787 assert_eq!(empty, 0xFFFF_FFFF);
1788 }
1789
1790 #[test]
1791 fn test_rciep_function0_fallback() {
1792 let mut register_mmio = TestPcieMmioRegistration {};
1796 let start_bus: u8 = 0;
1797 let end_bus: u8 = 0;
1798 let ecam = MemoryRange::new(0..ecam_size_from_bus_numbers(start_bus, end_bus));
1799 let mut rc = GenericPcieRootComplex::builder(&mut register_mmio, start_bus..=end_bus, ecam)
1800 .build()
1801 .unwrap();
1802
1803 let rciep = TestPcieEndpoint::new(
1804 |offset, value| {
1805 if offset == 0 {
1806 *value = 0xCAFE_F00D;
1807 }
1808 Some(IoResult::Ok)
1809 },
1810 |_, _| Some(IoResult::Ok),
1811 );
1812 rc.add_rciep(0, "rciep-0", Box::new(rciep)).unwrap();
1813
1814 let mut val: u32 = 0;
1816 rc.mmio_read(0, val.as_mut_bytes()).unwrap();
1817 assert_eq!(val, 0xCAFE_F00D);
1818
1819 let mut val_fn1: u32 = 0;
1822 rc.mmio_read(4096, val_fn1.as_mut_bytes()).unwrap();
1824 assert_eq!(val_fn1, 0xFFFF_FFFF);
1825 }
1826
1827 #[test]
1828 fn test_rciep_collision_with_root_port() {
1829 let mut rc = instantiate_root_complex(0, 0, 1);
1832
1833 let rciep = TestPcieEndpoint::new(|_, _| Some(IoResult::Ok), |_, _| Some(IoResult::Ok));
1834 let err = rc
1836 .add_rciep(0, "rciep-collision", Box::new(rciep))
1837 .expect_err("should fail: devfn occupied by root port");
1838 assert_eq!(err.as_ref(), "test-port-0");
1839 }
1840
1841 #[test]
1842 fn test_rciep_collision_with_rciep() {
1843 let mut register_mmio = TestPcieMmioRegistration {};
1845 let ecam = MemoryRange::new(0..ecam_size_from_bus_numbers(0, 0));
1846 let mut rc = GenericPcieRootComplex::builder(&mut register_mmio, 0..=0u8, ecam)
1847 .build()
1848 .unwrap();
1849
1850 let rciep1 = TestPcieEndpoint::new(|_, _| Some(IoResult::Ok), |_, _| Some(IoResult::Ok));
1851 let rciep2 = TestPcieEndpoint::new(|_, _| Some(IoResult::Ok), |_, _| Some(IoResult::Ok));
1852 rc.add_rciep(0, "rciep-first", Box::new(rciep1)).unwrap();
1853 let err = rc
1854 .add_rciep(0, "rciep-second", Box::new(rciep2))
1855 .expect_err("should fail: devfn already has an RCiEP");
1856 assert_eq!(err.as_ref(), "rciep-first");
1857 }
1858
1859 #[test]
1860 fn test_multi_function_header_bit() {
1861 let mut rc = instantiate_root_complex(0, 255, 2);
1864 let mut header_type_reg: u32 = 0;
1865 rc.mmio_read(0x0C, header_type_reg.as_mut_bytes()).unwrap();
1867 assert_ne!(
1869 header_type_reg & (1 << 23),
1870 0,
1871 "multi-function bit must be set"
1872 );
1873
1874 let mut rc_single = instantiate_root_complex(0, 255, 1);
1876 let mut header_single: u32 = 0;
1877 rc_single
1878 .mmio_read(0x0C, header_single.as_mut_bytes())
1879 .unwrap();
1880 assert_eq!(
1881 header_single & (1 << 23),
1882 0,
1883 "single-function: multi-function bit must be clear"
1884 );
1885 }
1886
1887 #[test]
1888 fn test_too_many_ports_returns_error() {
1889 let port_defs: Vec<GenericPcieRootPortDefinition> = (0..257)
1891 .map(|i| GenericPcieRootPortDefinition {
1892 name: format!("port-{}", i).into(),
1893 hotplug: false,
1894 settings: PciePortSettings::default(),
1895 })
1896 .collect();
1897 let mut register_mmio = TestPcieMmioRegistration {};
1898 let ecam = MemoryRange::new(0..ecam_size_from_bus_numbers(0, 255));
1899 let rc_bus_range = AssignedBusRange::new();
1900 rc_bus_range.set_bus_range(0, 255);
1901 let msi_conn = pci_core::msi::MsiConnection::new(rc_bus_range, 0);
1902 let result = GenericPcieRootComplex::builder(&mut register_mmio, 0..=255u8, ecam)
1903 .root_ports(port_defs, msi_conn.target())
1904 .build();
1905 assert!(
1906 result.is_err(),
1907 "257 ports should exceed the 256-port limit"
1908 );
1909 }
1910}