1use crate::DOWNSTREAM_SWITCH_PORT_DEVICE_ID;
14use crate::UPSTREAM_SWITCH_PORT_DEVICE_ID;
15use crate::VENDOR_ID;
16use crate::port::PcieDownstreamPort;
17use crate::port::PciePortSettings;
18use anyhow::Context;
19use chipset_device::ChipsetDevice;
20use chipset_device::io::IoResult;
21use chipset_device::pci::PciConfigSpace;
22use inspect::Inspect;
23use inspect::InspectMut;
24use pci_bus::GenericPciBusDevice;
25use pci_core::capabilities::pci_express::PciExpressCapability;
26use pci_core::cfg_space_emu::ConfigSpaceType1Emulator;
27use pci_core::msi::MsiTarget;
28use pci_core::spec::caps::pci_express::DevicePortType;
29use pci_core::spec::hwid::ClassCode;
30use pci_core::spec::hwid::HardwareIds;
31use pci_core::spec::hwid::ProgrammingInterface;
32use pci_core::spec::hwid::Subclass;
33use std::sync::Arc;
34use vmcore::device_state::ChangeDeviceState;
35
36#[derive(Inspect)]
41pub struct UpstreamSwitchPort {
42 cfg_space: ConfigSpaceType1Emulator,
43}
44
45impl UpstreamSwitchPort {
46 pub fn new() -> Self {
48 let cfg_space = ConfigSpaceType1Emulator::new(
49 HardwareIds {
50 vendor_id: VENDOR_ID,
51 device_id: UPSTREAM_SWITCH_PORT_DEVICE_ID,
52 revision_id: 0,
53 prog_if: ProgrammingInterface::NONE,
54 sub_class: Subclass::BRIDGE_PCI_TO_PCI,
55 base_class: ClassCode::BRIDGE,
56 type0_sub_vendor_id: 0,
57 type0_sub_system_id: 0,
58 },
59 vec![Box::new(PciExpressCapability::new(
60 DevicePortType::UpstreamSwitchPort,
61 None,
62 ))],
63 vec![],
64 );
65 Self { cfg_space }
66 }
67
68 pub fn cfg_space(&self) -> &ConfigSpaceType1Emulator {
70 &self.cfg_space
71 }
72
73 pub fn cfg_space_mut(&mut self) -> &mut ConfigSpaceType1Emulator {
75 &mut self.cfg_space
76 }
77}
78
79#[derive(Inspect)]
84pub struct DownstreamSwitchPort {
85 #[inspect(flatten)]
87 port: PcieDownstreamPort,
88}
89
90impl DownstreamSwitchPort {
91 pub fn new(
100 name: impl Into<Arc<str>>,
101 multi_function: Option<bool>,
102 hotplug_slot_number: Option<u32>,
103 msi_target: &MsiTarget,
104 settings: PciePortSettings,
105 ) -> Self {
106 let multi_function = multi_function.unwrap_or(false);
107 let hardware_ids = HardwareIds {
108 vendor_id: VENDOR_ID,
109 device_id: DOWNSTREAM_SWITCH_PORT_DEVICE_ID,
110 revision_id: 0,
111 prog_if: ProgrammingInterface::NONE,
112 sub_class: Subclass::BRIDGE_PCI_TO_PCI,
113 base_class: ClassCode::BRIDGE,
114 type0_sub_vendor_id: 0,
115 type0_sub_system_id: 0,
116 };
117
118 let port = PcieDownstreamPort::new(
119 name.into().to_string(),
120 hardware_ids,
121 DevicePortType::DownstreamSwitchPort,
122 multi_function,
123 hotplug_slot_number,
124 msi_target,
125 settings,
126 None,
127 None,
128 );
129
130 Self { port }
131 }
132
133 pub fn cfg_space(&self) -> &ConfigSpaceType1Emulator {
135 &self.port.cfg_space
136 }
137
138 pub fn cfg_space_mut(&mut self) -> &mut ConfigSpaceType1Emulator {
140 &mut self.port.cfg_space
141 }
142}
143
144pub struct GenericPcieSwitchDefinition {
146 pub name: Arc<str>,
148 pub downstream_port_count: u8,
151 pub hotplug: bool,
153 pub msi_target: MsiTarget,
156 pub dsp_settings: PciePortSettings,
158}
159
160#[derive(InspectMut)]
169pub struct GenericPcieSwitch {
170 name: Arc<str>,
172 upstream_port: UpstreamSwitchPort,
174 #[inspect(with = "|x| inspect::iter_by_index(x).map_value(|(_, v)| v)")]
176 downstream_ports: Vec<(Arc<str>, DownstreamSwitchPort)>,
177}
178
179impl GenericPcieSwitch {
180 pub fn new(definition: GenericPcieSwitchDefinition) -> Self {
182 let upstream_port = UpstreamSwitchPort::new();
183
184 let multi_function = definition.downstream_port_count > 1;
186
187 let switch_msi_target = definition
192 .msi_target
193 .with_bus_range(upstream_port.cfg_space().bus_range(), 0);
194
195 let downstream_ports: Vec<(Arc<str>, DownstreamSwitchPort)> = (0..definition
196 .downstream_port_count)
197 .map(|i| {
198 let port_name: Arc<str> = format!("{}-downstream-{}", definition.name, i).into();
199 let hotplug_slot_number = if definition.hotplug {
201 Some((i as u32) + 1)
202 } else {
203 None
204 };
205 let port_msi_target = switch_msi_target.with_devfn(i);
206 let port = DownstreamSwitchPort::new(
207 port_name.clone(),
208 Some(multi_function),
209 hotplug_slot_number,
210 &port_msi_target,
211 definition.dsp_settings.clone(),
212 );
213 (port_name, port)
214 })
215 .collect();
216
217 Self {
218 name: definition.name,
219 upstream_port,
220 downstream_ports,
221 }
222 }
223
224 pub fn name(&self) -> &Arc<str> {
226 &self.name
227 }
228
229 pub fn upstream_port(&self) -> &UpstreamSwitchPort {
231 &self.upstream_port
232 }
233
234 pub fn downstream_ports(&self) -> Vec<crate::root::DownstreamPortInfo> {
236 self.downstream_ports
237 .iter()
238 .enumerate()
239 .map(|(i, (name, dsp))| crate::root::DownstreamPortInfo {
240 devfn: i as u8,
241 name: name.clone(),
242 bus_range: dsp.port.bus_range(),
243 })
244 .collect()
245 }
246
247 fn route_cfg_read(
249 &mut self,
250 bus: u8,
251 function: u8,
252 cfg_offset: u16,
253 value: &mut u32,
254 ) -> Option<IoResult> {
255 let upstream_bus_range = self.upstream_port.cfg_space().assigned_bus_range();
256
257 if upstream_bus_range == (0..=0) {
259 return None;
260 }
261
262 if !upstream_bus_range.contains(&bus) {
264 return None;
265 }
266
267 let secondary_bus = *upstream_bus_range.start();
268
269 if bus == secondary_bus {
271 return self.handle_downstream_port_read(function, cfg_offset, value);
272 }
273
274 self.route_read_to_downstream_ports(bus, function, cfg_offset, value)
276 }
277
278 fn route_cfg_write(
280 &mut self,
281 bus: u8,
282 function: u8,
283 cfg_offset: u16,
284 value: u32,
285 ) -> Option<IoResult> {
286 let upstream_bus_range = self.upstream_port.cfg_space().assigned_bus_range();
287
288 if upstream_bus_range == (0..=0) {
290 return None;
291 }
292
293 if !upstream_bus_range.contains(&bus) {
295 return None;
296 }
297
298 let secondary_bus = *upstream_bus_range.start();
299
300 if bus == secondary_bus {
302 return self.handle_downstream_port_write(function, cfg_offset, value);
303 }
304
305 self.route_write_to_downstream_ports(bus, function, cfg_offset, value)
307 }
308
309 fn handle_downstream_port_read(
311 &mut self,
312 function: u8,
313 cfg_offset: u16,
314 value: &mut u32,
315 ) -> Option<IoResult> {
316 let (_, downstream_port) = self.downstream_ports.get_mut(function as usize)?;
317 Some(downstream_port.port.cfg_space.read_u32(cfg_offset, value))
318 }
319
320 fn handle_downstream_port_write(
322 &mut self,
323 function: u8,
324 cfg_offset: u16,
325 value: u32,
326 ) -> Option<IoResult> {
327 let (_, downstream_port) = self.downstream_ports.get_mut(function as usize)?;
328 Some(downstream_port.port.cfg_space.write_u32(cfg_offset, value))
329 }
330
331 fn route_read_to_downstream_ports(
333 &mut self,
334 bus: u8,
335 function: u8,
336 cfg_offset: u16,
337 value: &mut u32,
338 ) -> Option<IoResult> {
339 for (_, downstream_port) in self.downstream_ports.iter_mut() {
340 let downstream_bus_range = downstream_port.cfg_space().assigned_bus_range();
341
342 if downstream_bus_range == (0..=0) {
344 continue;
345 }
346
347 if downstream_bus_range.contains(&bus) {
348 return Some(
349 downstream_port
350 .port
351 .forward_cfg_read_with_routing(&bus, &function, cfg_offset, value),
352 );
353 }
354 }
355
356 None
358 }
359
360 fn route_write_to_downstream_ports(
362 &mut self,
363 bus: u8,
364 function: u8,
365 cfg_offset: u16,
366 value: u32,
367 ) -> Option<IoResult> {
368 for (_, downstream_port) in self.downstream_ports.iter_mut() {
369 let downstream_bus_range = downstream_port.cfg_space().assigned_bus_range();
370
371 if downstream_bus_range == (0..=0) {
373 continue;
374 }
375
376 if downstream_bus_range.contains(&bus) {
377 return Some(
378 downstream_port
379 .port
380 .forward_cfg_write_with_routing(&bus, &function, cfg_offset, value),
381 );
382 }
383 }
384
385 None
387 }
388
389 pub fn add_pcie_device(
391 &mut self,
392 port_devfn: u8,
393 name: &str,
394 dev: Box<dyn GenericPciBusDevice>,
395 ) -> anyhow::Result<()> {
396 let (port_name, downstream_port) = self
397 .downstream_ports
398 .get_mut(port_devfn as usize)
399 .ok_or_else(|| anyhow::anyhow!("port devfn {} not found", port_devfn))?;
400 downstream_port
401 .port
402 .add_pcie_device(port_name.as_ref(), name, dev)
403 .context("failed to add PCIe device to downstream port")?;
404 Ok(())
405 }
406}
407
408impl ChangeDeviceState for GenericPcieSwitch {
409 fn start(&mut self) {}
411
412 async fn stop(&mut self) {}
414
415 async fn reset(&mut self) {
417 self.upstream_port.cfg_space.reset();
419
420 for (_, downstream_port) in self.downstream_ports.iter_mut() {
422 downstream_port.port.cfg_space.reset();
423 }
424 }
425}
426
427impl ChipsetDevice for GenericPcieSwitch {
428 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
430 Some(self)
431 }
432}
433
434impl PciConfigSpace for GenericPcieSwitch {
435 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
437 self.upstream_port.cfg_space.read_u32(offset, value)
439 }
440
441 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
443 self.upstream_port.cfg_space.write_u32(offset, value)
445 }
446
447 fn pci_cfg_read_with_routing(
448 &mut self,
449 secondary_bus: u8,
450 target_bus: u8,
451 function: u8,
452 offset: u16,
453 value: &mut u32,
454 ) -> IoResult {
455 if let Some(result) = self.route_cfg_read(target_bus, function, offset, value) {
457 return result;
458 }
459
460 if target_bus == secondary_bus {
464 if function == 0 {
465 return self.upstream_port.cfg_space.read_u32(offset, value);
466 }
467 }
468
469 *value = !0;
471 IoResult::Ok
472 }
473
474 fn pci_cfg_write_with_routing(
475 &mut self,
476 secondary_bus: u8,
477 target_bus: u8,
478 function: u8,
479 offset: u16,
480 value: u32,
481 ) -> IoResult {
482 if let Some(result) = self.route_cfg_write(target_bus, function, offset, value) {
484 return result;
485 }
486
487 if target_bus == secondary_bus {
491 if function == 0 {
492 return self.upstream_port.cfg_space.write_u32(offset, value);
493 }
494 }
495
496 IoResult::Ok
497 }
498
499 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
500 None
502 }
503}
504
505mod save_restore {
506 use super::*;
507 use std::collections::HashSet;
508 use vmcore::save_restore::RestoreError;
509 use vmcore::save_restore::SaveError;
510 use vmcore::save_restore::SaveRestore;
511
512 mod state {
513 use super::ConfigSpaceType1Emulator;
514 use super::SaveRestore;
515 use cxl_spec::CxlComponentRegisters;
516 use mesh::payload::Protobuf;
517 use vmcore::save_restore::SavedStateRoot;
518
519 type SwitchPortCfgSpaceSavedState = <ConfigSpaceType1Emulator as SaveRestore>::SavedState;
520 type CxlComponentRegistersSavedState = <CxlComponentRegisters as SaveRestore>::SavedState;
521
522 #[derive(Protobuf)]
524 #[mesh(package = "pcie.switch")]
525 pub struct DownstreamPortSavedState {
526 #[mesh(1)]
528 pub devfn: u8,
529 #[mesh(2)]
531 pub cfg_space: SwitchPortCfgSpaceSavedState,
532 #[mesh(3)]
534 pub cxl_component_registers: Option<CxlComponentRegistersSavedState>,
535 }
536
537 #[derive(Protobuf, SavedStateRoot)]
539 #[mesh(package = "pcie.switch")]
540 pub struct SavedState {
541 #[mesh(1)]
543 pub upstream_cfg_space: SwitchPortCfgSpaceSavedState,
544 #[mesh(2)]
549 pub downstream_ports: Vec<DownstreamPortSavedState>,
550 }
551 }
552
553 impl SaveRestore for GenericPcieSwitch {
554 type SavedState = state::SavedState;
555
556 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
557 let upstream_cfg_space = self.upstream_port.cfg_space.save()?;
559
560 let mut downstream_ports = Vec::with_capacity(self.downstream_ports.len());
562 for (i, (_, downstream_port)) in self.downstream_ports.iter_mut().enumerate() {
563 downstream_ports.push(state::DownstreamPortSavedState {
564 devfn: i as u8,
565 cfg_space: downstream_port.port.cfg_space.save()?,
566 cxl_component_registers: downstream_port
567 .port
568 .save_cxl_component_registers_state()?,
569 });
570 }
571
572 Ok(state::SavedState {
573 upstream_cfg_space,
574 downstream_ports,
575 })
576 }
577
578 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
579 let state::SavedState {
580 upstream_cfg_space,
581 downstream_ports,
582 } = state;
583
584 if downstream_ports.len() != self.downstream_ports.len() {
586 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
587 "downstream port count mismatch: saved {}, current {}",
588 downstream_ports.len(),
589 self.downstream_ports.len()
590 )));
591 }
592
593 self.upstream_port.cfg_space.restore(upstream_cfg_space)?;
595
596 let mut seen_ports = HashSet::with_capacity(downstream_ports.len());
597
598 for port_state in downstream_ports {
600 if !seen_ports.insert(port_state.devfn) {
602 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
603 "duplicate downstream port devfn {} in saved state",
604 port_state.devfn
605 )));
606 }
607
608 if let Some((_, downstream_port)) =
609 self.downstream_ports.get_mut(port_state.devfn as usize)
610 {
611 downstream_port
612 .port
613 .cfg_space
614 .restore(port_state.cfg_space)?;
615 downstream_port.port.restore_cxl_component_registers_state(
616 port_state.cxl_component_registers,
617 )?;
618 } else {
619 return Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
620 "downstream port devfn {} not found",
621 port_state.devfn
622 )));
623 }
624 }
625
626 Ok(())
627 }
628 }
629}
630
631#[cfg(test)]
632mod tests {
633 use super::*;
634 use pci_core::bus_range::AssignedBusRange;
635 use pci_core::msi::MsiConnection;
636
637 #[test]
638 fn test_upstream_switch_port_creation() {
639 let port = UpstreamSwitchPort::new();
640
641 let mut vendor_device_id: u32 = 0;
643 port.cfg_space.read_u32(0x0, &mut vendor_device_id).unwrap();
644 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
645 assert_eq!(vendor_device_id, expected);
646 }
647
648 #[test]
649 fn test_downstream_switch_port_creation() {
650 let port = DownstreamSwitchPort::new(
651 "test-downstream-port",
652 None,
653 None,
654 &MsiTarget::disconnected(),
655 PciePortSettings::default(),
656 );
657 assert!(port.port.link.is_none());
658
659 let mut vendor_device_id: u32 = 0;
661 port.port
662 .cfg_space
663 .read_u32(0x0, &mut vendor_device_id)
664 .unwrap();
665 let expected = (DOWNSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
666 assert_eq!(vendor_device_id, expected);
667 }
668
669 #[test]
670 fn test_downstream_switch_port_multi_function_options() {
671 let port_default = DownstreamSwitchPort::new(
673 "test-port-default",
674 None,
675 None,
676 &MsiTarget::disconnected(),
677 PciePortSettings::default(),
678 );
679 let mut header_type_value: u32 = 0;
680 port_default
681 .cfg_space()
682 .read_u32(0x0C, &mut header_type_value)
683 .unwrap();
684 let header_type_field = (header_type_value >> 16) & 0xFF;
685 assert_eq!(
686 header_type_field & 0x80,
687 0x00,
688 "Multi-function bit should NOT be set with None parameter"
689 );
690
691 let port_false = DownstreamSwitchPort::new(
693 "test-port-false",
694 Some(false),
695 None,
696 &MsiTarget::disconnected(),
697 PciePortSettings::default(),
698 );
699 let mut header_type_value_false: u32 = 0;
700 port_false
701 .cfg_space()
702 .read_u32(0x0C, &mut header_type_value_false)
703 .unwrap();
704 let header_type_field_false = (header_type_value_false >> 16) & 0xFF;
705 assert_eq!(
706 header_type_field_false & 0x80,
707 0x00,
708 "Multi-function bit should NOT be set with Some(false)"
709 );
710
711 let port_true = DownstreamSwitchPort::new(
713 "test-port-true",
714 Some(true),
715 None,
716 &MsiTarget::disconnected(),
717 PciePortSettings::default(),
718 );
719 let mut header_type_value_true: u32 = 0;
720 port_true
721 .cfg_space()
722 .read_u32(0x0C, &mut header_type_value_true)
723 .unwrap();
724 let header_type_field_true = (header_type_value_true >> 16) & 0xFF;
725 assert_eq!(
726 header_type_field_true & 0x80,
727 0x80,
728 "Multi-function bit should be set with Some(true)"
729 );
730 }
731
732 #[test]
733 fn test_downstream_switch_port_hotplug_options() {
734 let port_no_hotplug = DownstreamSwitchPort::new(
736 "test-port-no-hotplug",
737 None,
738 None,
739 &MsiTarget::disconnected(),
740 PciePortSettings::default(),
741 );
742 let mut vendor_device_id: u32 = 0;
745 port_no_hotplug
746 .cfg_space()
747 .read_u32(0x0, &mut vendor_device_id)
748 .unwrap();
749 let expected = (DOWNSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
750 assert_eq!(vendor_device_id, expected);
751
752 let port_with_hotplug = DownstreamSwitchPort::new(
754 "test-port-hotplug",
755 None,
756 Some(42),
757 &MsiTarget::disconnected(),
758 PciePortSettings::default(),
759 );
760 let mut vendor_device_id_hotplug: u32 = 0;
761 port_with_hotplug
762 .cfg_space()
763 .read_u32(0x0, &mut vendor_device_id_hotplug)
764 .unwrap();
765 assert_eq!(vendor_device_id_hotplug, expected);
766 }
769
770 #[test]
771 fn test_switch_creation() {
772 let definition = GenericPcieSwitchDefinition {
773 name: "test-switch".into(),
774 downstream_port_count: 3,
775 hotplug: false,
776 dsp_settings: PciePortSettings::default(),
777 msi_target: MsiTarget::disconnected(),
778 };
779 let switch = GenericPcieSwitch::new(definition);
780
781 assert_eq!(switch.name().as_ref(), "test-switch");
782 assert_eq!(switch.downstream_ports().len(), 3);
783
784 let ports = switch.downstream_ports();
786 let port_names: std::collections::HashSet<_> =
787 ports.iter().map(|p| p.name.as_ref()).collect();
788 assert!(port_names.contains("test-switch-downstream-0"));
789 assert!(port_names.contains("test-switch-downstream-1"));
790 assert!(port_names.contains("test-switch-downstream-2"));
791
792 let port_numbers: std::collections::HashSet<_> = ports.iter().map(|p| p.devfn).collect();
794 assert!(port_numbers.contains(&0));
795 assert!(port_numbers.contains(&1));
796 assert!(port_numbers.contains(&2));
797 }
798
799 #[test]
800 fn test_switch_device_connections() {
801 use crate::test_helpers::TestPcieEndpoint;
802 use chipset_device::io::IoError;
803
804 let definition = GenericPcieSwitchDefinition {
805 name: "test-switch".into(),
806 downstream_port_count: 2,
807 hotplug: false,
808 dsp_settings: PciePortSettings::default(),
809 msi_target: MsiTarget::disconnected(),
810 };
811 let mut switch = GenericPcieSwitch::new(definition);
812
813 let downstream_device = TestPcieEndpoint::new(
814 |offset, value| match offset {
815 0x0 => {
816 *value = 0xABCD_EF01;
817 Some(IoResult::Ok)
818 }
819 _ => Some(IoResult::Err(IoError::InvalidRegister)),
820 },
821 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
822 );
823
824 assert!(
826 switch
827 .add_pcie_device(
828 0, "downstream-dev",
830 Box::new(downstream_device),
831 )
832 .is_ok()
833 );
834
835 let invalid_device = TestPcieEndpoint::new(
837 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
838 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
839 );
840 let result = switch.add_pcie_device(99, "invalid-dev", Box::new(invalid_device)); assert!(result.is_err());
842 assert!(result.is_err());
845 }
846
847 #[test]
848 fn test_switch_routing_functionality() {
849 use crate::test_helpers::TestPcieEndpoint;
850 use chipset_device::io::IoResult;
851
852 let definition = GenericPcieSwitchDefinition {
853 name: "test-switch".into(),
854 downstream_port_count: 2,
855 hotplug: false,
856 dsp_settings: PciePortSettings::default(),
857 msi_target: MsiTarget::disconnected(),
858 };
859 let mut switch = GenericPcieSwitch::new(definition);
860
861 let test_device =
864 TestPcieEndpoint::new(|_, _| Some(IoResult::Ok), |_, _| Some(IoResult::Ok));
865 let add_result = switch.add_pcie_device(0, "test-device", Box::new(test_device));
866 assert!(add_result.is_ok());
868
869 let mut value = 0u32;
871 let result = switch
872 .upstream_port
873 .cfg_space_mut()
874 .read_u32(0x0, &mut value);
875 assert!(matches!(result, IoResult::Ok));
876
877 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
879 assert_eq!(value, expected);
880 }
881
882 #[test]
883 fn test_switch_chipset_device() {
884 use chipset_device::ChipsetDevice;
885 use chipset_device::pci::PciConfigSpace;
886
887 let definition = GenericPcieSwitchDefinition {
888 name: "test-switch".into(),
889 downstream_port_count: 4,
890 hotplug: false,
891 dsp_settings: PciePortSettings::default(),
892 msi_target: MsiTarget::disconnected(),
893 };
894 let mut switch = GenericPcieSwitch::new(definition);
895
896 assert!(switch.supports_pci().is_some());
898 assert!(switch.supports_mmio().is_none());
899 assert!(switch.supports_pio().is_none());
900 assert!(switch.supports_poll_device().is_none());
901
902 let mut value = 0u32;
904 let result = PciConfigSpace::pci_cfg_read(&mut switch, 0x0, &mut value);
905 assert!(matches!(result, IoResult::Ok));
906
907 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
909 assert_eq!(value, expected);
910
911 let result = PciConfigSpace::pci_cfg_write(&mut switch, 0x4, 0x12345678);
913 assert!(matches!(result, IoResult::Ok));
914 }
915
916 #[test]
917 fn test_switch_default() {
918 let definition = GenericPcieSwitchDefinition {
919 name: "default-switch".into(),
920 downstream_port_count: 4,
921 hotplug: false,
922 dsp_settings: PciePortSettings::default(),
923 msi_target: MsiTarget::disconnected(),
924 };
925 let switch = GenericPcieSwitch::new(definition);
926 assert_eq!(switch.name().as_ref(), "default-switch");
927 assert_eq!(switch.downstream_ports().len(), 4);
928 }
929
930 #[test]
931 fn test_switch_large_downstream_port_count() {
932 let definition = GenericPcieSwitchDefinition {
933 name: "test-switch".into(),
934 downstream_port_count: 16,
935 hotplug: false,
936 dsp_settings: PciePortSettings::default(),
937 msi_target: MsiTarget::disconnected(),
938 };
939 let switch = GenericPcieSwitch::new(definition);
940 assert_eq!(switch.downstream_ports().len(), 16);
941 }
942
943 #[test]
944 fn test_switch_downstream_port_direct_access() {
945 let definition = GenericPcieSwitchDefinition {
946 name: "test-switch".into(),
947 downstream_port_count: 3,
948 hotplug: false,
949 dsp_settings: PciePortSettings::default(),
950 msi_target: MsiTarget::disconnected(),
951 };
952 let mut switch = GenericPcieSwitch::new(definition);
953
954 let secondary_bus = 1u8;
956 let bus_config = (10u32 << 24) | ((secondary_bus as u32) << 16); switch
959 .upstream_port
960 .cfg_space_mut()
961 .write_u32(0x18, bus_config)
962 .unwrap();
963
964 let bus_range = switch.upstream_port.cfg_space().assigned_bus_range();
965 let switch_internal_bus = *bus_range.start(); let mut value = 0u32;
969 let result = switch.route_cfg_read(switch_internal_bus, 0, 0x0, &mut value);
970 assert!(result.is_some());
971
972 let expected = (DOWNSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
974 assert_eq!(value, expected);
975
976 let mut value2 = 0u32;
978 let result2 = switch.route_cfg_read(switch_internal_bus, 2, 0x0, &mut value2);
979 assert!(result2.is_some());
980 assert_eq!(value2, expected);
981
982 let mut value3 = 0u32;
984 let result3 = switch.route_cfg_read(switch_internal_bus, 5, 0x0, &mut value3);
985 assert!(result3.is_none());
986 }
987
988 #[test]
989 fn test_switch_invalid_bus_range_handling() {
990 let definition = GenericPcieSwitchDefinition {
991 name: "test-switch".into(),
992 downstream_port_count: 2,
993 hotplug: false,
994 dsp_settings: PciePortSettings::default(),
995 msi_target: MsiTarget::disconnected(),
996 };
997 let mut switch = GenericPcieSwitch::new(definition);
998
999 let bus_range = switch.upstream_port.cfg_space().assigned_bus_range();
1001 assert_eq!(bus_range, 0..=0);
1002
1003 let mut value = 0u32;
1005 let result = switch.route_cfg_read(0, 0, 0x0, &mut value);
1006 assert!(result.is_none());
1007
1008 let result2 = switch.route_cfg_read(1, 0, 0x0, &mut value);
1009 assert!(result2.is_none());
1010
1011 let result3 = switch.route_cfg_write(0, 0, 0x0, value);
1012 assert!(result3.is_none());
1013 }
1014
1015 #[test]
1016 fn test_switch_downstream_port_invalid_bus_range_skipping() {
1017 let definition = GenericPcieSwitchDefinition {
1018 name: "test-switch".into(),
1019 downstream_port_count: 2,
1020 hotplug: false,
1021 dsp_settings: PciePortSettings::default(),
1022 msi_target: MsiTarget::disconnected(),
1023 };
1024 let mut switch = GenericPcieSwitch::new(definition);
1025
1026 let secondary_bus = 1u8;
1028 let subordinate_bus = 10u8;
1029 let primary_bus = 0u8;
1030 let bus_config =
1031 ((subordinate_bus as u32) << 16) | ((secondary_bus as u32) << 8) | (primary_bus as u32); switch
1033 .upstream_port
1034 .cfg_space_mut()
1035 .write_u32(0x18, bus_config)
1036 .unwrap();
1037
1038 let mut value = 0u32;
1041
1042 let result = switch.route_cfg_read(2, 0, 0x0, &mut value);
1044 assert!(result.is_none());
1045
1046 let result2 = switch.route_cfg_read(5, 0, 0x0, &mut value);
1048 assert!(result2.is_none());
1049
1050 let result3 = switch.route_cfg_read(secondary_bus, 0, 0x0, &mut value);
1052 assert!(result3.is_some());
1053 }
1054
1055 #[test]
1056 fn test_switch_multi_function_bit() {
1057 let multi_port_definition = GenericPcieSwitchDefinition {
1059 name: "multi-port-switch".into(),
1060 downstream_port_count: 3,
1061 hotplug: false,
1062 dsp_settings: PciePortSettings::default(),
1063 msi_target: MsiTarget::disconnected(),
1064 };
1065 let multi_port_switch = GenericPcieSwitch::new(multi_port_definition);
1066
1067 for p in multi_port_switch.downstream_ports() {
1069 let port_num = p.devfn;
1070 if let Some((_, downstream_port)) =
1071 multi_port_switch.downstream_ports.get(port_num as usize)
1072 {
1073 let mut header_type_value: u32 = 0;
1074 downstream_port
1075 .cfg_space()
1076 .read_u32(0x0C, &mut header_type_value)
1077 .unwrap();
1078
1079 let header_type_field = (header_type_value >> 16) & 0xFF;
1081
1082 assert_eq!(
1084 header_type_field & 0x80,
1085 0x80,
1086 "Multi-function bit should be set for downstream port {} in multi-port switch",
1087 port_num
1088 );
1089
1090 assert_eq!(
1092 header_type_field & 0x7F,
1093 0x01,
1094 "Header type should be 01 (bridge) for downstream port {}",
1095 port_num
1096 );
1097 }
1098 }
1099
1100 let single_port_definition = GenericPcieSwitchDefinition {
1102 name: "single-port-switch".into(),
1103 downstream_port_count: 1,
1104 hotplug: false,
1105 dsp_settings: PciePortSettings::default(),
1106 msi_target: MsiTarget::disconnected(),
1107 };
1108 let single_port_switch = GenericPcieSwitch::new(single_port_definition);
1109
1110 for p in single_port_switch.downstream_ports() {
1112 let port_num = p.devfn;
1113 if let Some((_, downstream_port)) =
1114 single_port_switch.downstream_ports.get(port_num as usize)
1115 {
1116 let mut header_type_value: u32 = 0;
1117 downstream_port
1118 .cfg_space()
1119 .read_u32(0x0C, &mut header_type_value)
1120 .unwrap();
1121
1122 let header_type_field = (header_type_value >> 16) & 0xFF;
1124
1125 assert_eq!(
1127 header_type_field & 0x80,
1128 0x00,
1129 "Multi-function bit should NOT be set for downstream port {} in single-port switch",
1130 port_num
1131 );
1132
1133 assert_eq!(
1135 header_type_field & 0x7F,
1136 0x01,
1137 "Header type should be 01 (bridge) for downstream port {}",
1138 port_num
1139 );
1140 }
1141 }
1142 }
1143
1144 #[test]
1145 fn test_hotplug_support() {
1146 let definition_no_hotplug = GenericPcieSwitchDefinition {
1148 name: "test-switch-no-hotplug".into(),
1149 downstream_port_count: 1,
1150 hotplug: false,
1151 dsp_settings: PciePortSettings::default(),
1152 msi_target: MsiTarget::disconnected(),
1153 };
1154 let switch_no_hotplug = GenericPcieSwitch::new(definition_no_hotplug);
1155 assert_eq!(switch_no_hotplug.name().as_ref(), "test-switch-no-hotplug");
1156
1157 let definition_with_hotplug = GenericPcieSwitchDefinition {
1159 name: "test-switch-with-hotplug".into(),
1160 downstream_port_count: 1,
1161 hotplug: true,
1162 dsp_settings: PciePortSettings::default(),
1163 msi_target: MsiTarget::disconnected(),
1164 };
1165 let switch_with_hotplug = GenericPcieSwitch::new(definition_with_hotplug);
1166 assert_eq!(
1167 switch_with_hotplug.name().as_ref(),
1168 "test-switch-with-hotplug"
1169 );
1170 }
1171
1172 #[test]
1173 fn test_save_restore_port_mismatch_error() {
1174 use vmcore::save_restore::SaveRestore;
1175
1176 let definition = GenericPcieSwitchDefinition {
1178 name: "test-switch".into(),
1179 downstream_port_count: 3,
1180 hotplug: false,
1181 dsp_settings: PciePortSettings::default(),
1182 msi_target: MsiTarget::disconnected(),
1183 };
1184 let mut switch = GenericPcieSwitch::new(definition);
1185
1186 let saved_state = switch.save().expect("save should succeed");
1188 assert_eq!(saved_state.downstream_ports.len(), 3);
1189
1190 let definition2 = GenericPcieSwitchDefinition {
1192 name: "test-switch".into(),
1193 downstream_port_count: 2,
1194 hotplug: false,
1195 dsp_settings: PciePortSettings::default(),
1196 msi_target: MsiTarget::disconnected(),
1197 };
1198 let mut switch2 = GenericPcieSwitch::new(definition2);
1199
1200 let result = switch2.restore(saved_state);
1202 assert!(result.is_err());
1203 }
1204
1205 #[test]
1206 fn test_save_restore_basic() {
1207 use vmcore::save_restore::SaveRestore;
1208
1209 let definition = GenericPcieSwitchDefinition {
1210 name: "test-switch".into(),
1211 downstream_port_count: 2,
1212 hotplug: false,
1213 dsp_settings: PciePortSettings::default(),
1214 msi_target: MsiTarget::disconnected(),
1215 };
1216 let mut switch = GenericPcieSwitch::new(definition);
1217
1218 let saved_state = switch.save().expect("save should succeed");
1220
1221 assert_eq!(saved_state.downstream_ports.len(), 2);
1223
1224 let definition2 = GenericPcieSwitchDefinition {
1226 name: "test-switch".into(),
1227 downstream_port_count: 2,
1228 hotplug: false,
1229 dsp_settings: PciePortSettings::default(),
1230 msi_target: MsiTarget::disconnected(),
1231 };
1232 let mut switch2 = GenericPcieSwitch::new(definition2);
1233 switch2
1234 .restore(saved_state)
1235 .expect("restore should succeed");
1236 }
1237
1238 #[test]
1239 fn test_save_restore_with_bus_configuration() {
1240 use vmcore::save_restore::SaveRestore;
1241
1242 let definition = GenericPcieSwitchDefinition {
1243 name: "test-switch".into(),
1244 downstream_port_count: 3,
1245 hotplug: false,
1246 dsp_settings: PciePortSettings::default(),
1247 msi_target: MsiTarget::disconnected(),
1248 };
1249 let mut switch = GenericPcieSwitch::new(definition);
1250
1251 let secondary_bus = 5u8;
1253 let subordinate_bus = 15u8;
1254 let primary_bus = 0u8;
1255 let bus_config =
1256 ((subordinate_bus as u32) << 16) | ((secondary_bus as u32) << 8) | (primary_bus as u32);
1257 switch
1258 .upstream_port
1259 .cfg_space_mut()
1260 .write_u32(0x18, bus_config)
1261 .unwrap();
1262
1263 let bus_range = switch.upstream_port.cfg_space().assigned_bus_range();
1265 assert_eq!(*bus_range.start(), secondary_bus);
1266 assert_eq!(*bus_range.end(), subordinate_bus);
1267
1268 let saved_state = switch.save().expect("save should succeed");
1270
1271 let definition2 = GenericPcieSwitchDefinition {
1273 name: "test-switch".into(),
1274 downstream_port_count: 3,
1275 hotplug: false,
1276 dsp_settings: PciePortSettings::default(),
1277 msi_target: MsiTarget::disconnected(),
1278 };
1279 let mut switch2 = GenericPcieSwitch::new(definition2);
1280
1281 let default_bus_range = switch2.upstream_port.cfg_space().assigned_bus_range();
1283 assert_eq!(default_bus_range, 0..=0);
1284
1285 switch2
1287 .restore(saved_state)
1288 .expect("restore should succeed");
1289
1290 let restored_bus_range = switch2.upstream_port.cfg_space().assigned_bus_range();
1292 assert_eq!(*restored_bus_range.start(), secondary_bus);
1293 assert_eq!(*restored_bus_range.end(), subordinate_bus);
1294 }
1295
1296 #[test]
1297 fn test_save_restore_downstream_port_state() {
1298 use vmcore::save_restore::SaveRestore;
1299
1300 let definition = GenericPcieSwitchDefinition {
1301 name: "test-switch".into(),
1302 downstream_port_count: 2,
1303 hotplug: false,
1304 dsp_settings: PciePortSettings::default(),
1305 msi_target: MsiTarget::disconnected(),
1306 };
1307 let mut switch = GenericPcieSwitch::new(definition);
1308
1309 if let Some((_, downstream_port)) = switch.downstream_ports.get_mut(0) {
1312 let secondary_bus = 10u8;
1313 let subordinate_bus = 20u8;
1314 let primary_bus = 5u8;
1315 let bus_config = ((subordinate_bus as u32) << 16)
1316 | ((secondary_bus as u32) << 8)
1317 | (primary_bus as u32);
1318 downstream_port
1319 .port
1320 .cfg_space
1321 .write_u32(0x18, bus_config)
1322 .unwrap();
1323 }
1324
1325 if let Some((_, downstream_port)) = switch.downstream_ports.first() {
1327 let bus_range = downstream_port.cfg_space().assigned_bus_range();
1328 assert_eq!(*bus_range.start(), 10);
1329 assert_eq!(*bus_range.end(), 20);
1330 }
1331
1332 let saved_state = switch.save().expect("save should succeed");
1334
1335 let definition2 = GenericPcieSwitchDefinition {
1337 name: "test-switch".into(),
1338 downstream_port_count: 2,
1339 hotplug: false,
1340 dsp_settings: PciePortSettings::default(),
1341 msi_target: MsiTarget::disconnected(),
1342 };
1343 let mut switch2 = GenericPcieSwitch::new(definition2);
1344
1345 if let Some((_, downstream_port)) = switch2.downstream_ports.first() {
1347 let default_bus_range = downstream_port.cfg_space().assigned_bus_range();
1348 assert_eq!(default_bus_range, 0..=0);
1349 }
1350
1351 switch2
1353 .restore(saved_state)
1354 .expect("restore should succeed");
1355
1356 if let Some((_, downstream_port)) = switch2.downstream_ports.first() {
1358 let restored_bus_range = downstream_port.cfg_space().assigned_bus_range();
1359 assert_eq!(*restored_bus_range.start(), 10);
1360 assert_eq!(*restored_bus_range.end(), 20);
1361 }
1362 }
1363
1364 #[test]
1365 fn test_save_restore_preserves_upstream_and_downstream_cfg_space() {
1366 use vmcore::save_restore::SaveRestore;
1367
1368 let definition = GenericPcieSwitchDefinition {
1369 name: "test-switch".into(),
1370 downstream_port_count: 2,
1371 hotplug: false,
1372 dsp_settings: PciePortSettings::default(),
1373 msi_target: MsiTarget::disconnected(),
1374 };
1375 let mut switch = GenericPcieSwitch::new(definition);
1376
1377 switch
1379 .upstream_port
1380 .cfg_space_mut()
1381 .write_u32(0x18, 0x0014_1200)
1382 .unwrap();
1383
1384 if let Some((_, downstream_port)) = switch.downstream_ports.get_mut(1) {
1386 downstream_port
1387 .port
1388 .cfg_space
1389 .write_u32(0x18, 0x0020_1f12)
1390 .unwrap();
1391 }
1392
1393 let saved_state = switch.save().expect("save should succeed");
1394
1395 let definition2 = GenericPcieSwitchDefinition {
1396 name: "test-switch".into(),
1397 downstream_port_count: 2,
1398 hotplug: false,
1399 dsp_settings: PciePortSettings::default(),
1400 msi_target: MsiTarget::disconnected(),
1401 };
1402 let mut switch2 = GenericPcieSwitch::new(definition2);
1403
1404 switch2
1405 .restore(saved_state)
1406 .expect("restore should succeed");
1407
1408 let upstream_bus_range = switch2.upstream_port.cfg_space().assigned_bus_range();
1409 assert_eq!(*upstream_bus_range.start(), 0x12);
1410 assert_eq!(*upstream_bus_range.end(), 0x14);
1411
1412 if let Some((_, downstream_port)) = switch2.downstream_ports.get(1) {
1413 let downstream_bus_range = downstream_port.cfg_space().assigned_bus_range();
1414 assert_eq!(*downstream_bus_range.start(), 0x1f);
1415 assert_eq!(*downstream_bus_range.end(), 0x20);
1416 } else {
1417 panic!("missing downstream port 1");
1418 }
1419 }
1420
1421 struct SwitchAdapter(GenericPcieSwitch);
1424
1425 impl GenericPciBusDevice for SwitchAdapter {
1426 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> Option<IoResult> {
1427 Some(PciConfigSpace::pci_cfg_read(&mut self.0, offset, value))
1428 }
1429
1430 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> Option<IoResult> {
1431 Some(PciConfigSpace::pci_cfg_write(&mut self.0, offset, value))
1432 }
1433
1434 fn pci_cfg_read_with_routing(
1435 &mut self,
1436 secondary_bus: u8,
1437 target_bus: u8,
1438 function: u8,
1439 offset: u16,
1440 value: &mut u32,
1441 ) -> Option<IoResult> {
1442 Some(self.0.pci_cfg_read_with_routing(
1443 secondary_bus,
1444 target_bus,
1445 function,
1446 offset,
1447 value,
1448 ))
1449 }
1450
1451 fn pci_cfg_write_with_routing(
1452 &mut self,
1453 secondary_bus: u8,
1454 target_bus: u8,
1455 function: u8,
1456 offset: u16,
1457 value: u32,
1458 ) -> Option<IoResult> {
1459 Some(self.0.pci_cfg_write_with_routing(
1460 secondary_bus,
1461 target_bus,
1462 function,
1463 offset,
1464 value,
1465 ))
1466 }
1467 }
1468
1469 #[test]
1470 fn test_switch_enumeration_through_port() {
1471 use crate::port::PcieDownstreamPort;
1472 use pci_core::spec::hwid::{ClassCode, ProgrammingInterface, Subclass};
1473
1474 let hardware_ids = HardwareIds {
1475 vendor_id: 0x1234,
1476 device_id: 0x5678,
1477 revision_id: 0,
1478 prog_if: ProgrammingInterface::NONE,
1479 sub_class: Subclass::BRIDGE_PCI_TO_PCI,
1480 base_class: ClassCode::BRIDGE,
1481 type0_sub_vendor_id: 0,
1482 type0_sub_system_id: 0,
1483 };
1484
1485 let msi_conn = MsiConnection::new(AssignedBusRange::new(), 0);
1486 let mut port = PcieDownstreamPort::new(
1487 "root-port",
1488 hardware_ids,
1489 DevicePortType::RootPort,
1490 false,
1491 None,
1492 msi_conn.target(),
1493 PciePortSettings::default(),
1494 None,
1495 None,
1496 );
1497
1498 port.cfg_space
1500 .write_u32(0x18, (10u32 << 16) | (1u32 << 8))
1501 .unwrap();
1502
1503 let switch = GenericPcieSwitch::new(GenericPcieSwitchDefinition {
1505 name: "test-switch".into(),
1506 downstream_port_count: 2,
1507 hotplug: false,
1508 dsp_settings: PciePortSettings::default(),
1509 msi_target: MsiTarget::disconnected(),
1510 });
1511
1512 port.link = Some(("switch".into(), Box::new(SwitchAdapter(switch))));
1513
1514 let mut value = 0u32;
1518 let result = port.forward_cfg_read_with_routing(&1, &0, 0x0, &mut value);
1519 assert!(matches!(result, IoResult::Ok));
1520
1521 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
1522 assert_eq!(
1523 value, expected,
1524 "Type 0 access to bus 1 function 0 must read the switch's upstream port"
1525 );
1526
1527 let mut value2 = 0u32;
1530 let result2 = port.forward_cfg_read_with_routing(&1, &1, 0x0, &mut value2);
1531 assert!(matches!(result2, IoResult::Ok));
1532 assert_eq!(
1533 value2, !0,
1534 "Non-zero function should return all-1s (no device)"
1535 );
1536 }
1537
1538 #[test]
1539 fn test_switch_acs_only_applies_to_dsp() {
1540 use pci_core::spec::caps::ExtendedCapabilityId;
1541
1542 let switch = GenericPcieSwitch::new(GenericPcieSwitchDefinition {
1543 name: "acs-switch".into(),
1544 downstream_port_count: 1,
1545 hotplug: false,
1546 dsp_settings: PciePortSettings {
1547 acs_capabilities_supported: 0x0001,
1548 ..Default::default()
1549 },
1550 msi_target: MsiTarget::disconnected(),
1551 });
1552
1553 let mut upstream_header = 0u32;
1555 switch
1556 .upstream_port()
1557 .cfg_space()
1558 .read_u32(0x100, &mut upstream_header)
1559 .unwrap();
1560 assert_eq!(upstream_header, 0xffff_ffff);
1561
1562 let mut switch = switch;
1563 let (_, downstream_port) = switch
1564 .downstream_ports
1565 .iter_mut()
1566 .next()
1567 .expect("expected downstream port");
1568
1569 let mut downstream_header = 0u32;
1570 downstream_port
1571 .cfg_space_mut()
1572 .read_u32(0x100, &mut downstream_header)
1573 .unwrap();
1574 assert_eq!(
1575 downstream_header & 0xffff,
1576 ExtendedCapabilityId::ACS.0 as u32
1577 );
1578
1579 let mut caps_control = 0u32;
1580 downstream_port
1581 .cfg_space_mut()
1582 .read_u32(0x104, &mut caps_control)
1583 .unwrap();
1584 assert_eq!(caps_control as u16, 0x0001);
1585 }
1586}