1use crate::DOWNSTREAM_SWITCH_PORT_DEVICE_ID;
14use crate::UPSTREAM_SWITCH_PORT_DEVICE_ID;
15use crate::VENDOR_ID;
16use crate::port::PcieDownstreamPort;
17use anyhow::{Context, bail};
18use chipset_device::ChipsetDevice;
19use chipset_device::io::IoResult;
20use chipset_device::pci::PciConfigSpace;
21use inspect::Inspect;
22use inspect::InspectMut;
23use pci_bus::GenericPciBusDevice;
24use pci_core::capabilities::pci_express::PciExpressCapability;
25use pci_core::cfg_space_emu::ConfigSpaceType1Emulator;
26use pci_core::spec::caps::pci_express::DevicePortType;
27use pci_core::spec::hwid::ClassCode;
28use pci_core::spec::hwid::HardwareIds;
29use pci_core::spec::hwid::ProgrammingInterface;
30use pci_core::spec::hwid::Subclass;
31use std::collections::HashMap;
32use std::sync::Arc;
33use vmcore::device_state::ChangeDeviceState;
34
35#[derive(Inspect)]
40pub struct UpstreamSwitchPort {
41 cfg_space: ConfigSpaceType1Emulator,
42}
43
44impl UpstreamSwitchPort {
45 pub fn new() -> Self {
47 let cfg_space = ConfigSpaceType1Emulator::new(
48 HardwareIds {
49 vendor_id: VENDOR_ID,
50 device_id: UPSTREAM_SWITCH_PORT_DEVICE_ID,
51 revision_id: 0,
52 prog_if: ProgrammingInterface::NONE,
53 sub_class: Subclass::BRIDGE_PCI_TO_PCI,
54 base_class: ClassCode::BRIDGE,
55 type0_sub_vendor_id: 0,
56 type0_sub_system_id: 0,
57 },
58 vec![Box::new(PciExpressCapability::new(
59 DevicePortType::UpstreamSwitchPort,
60 None,
61 ))],
62 );
63 Self { cfg_space }
64 }
65
66 pub fn cfg_space(&self) -> &ConfigSpaceType1Emulator {
68 &self.cfg_space
69 }
70
71 pub fn cfg_space_mut(&mut self) -> &mut ConfigSpaceType1Emulator {
73 &mut self.cfg_space
74 }
75}
76
77#[derive(Inspect)]
82pub struct DownstreamSwitchPort {
83 #[inspect(flatten)]
85 port: PcieDownstreamPort,
86}
87
88impl DownstreamSwitchPort {
89 pub fn new(
96 name: impl Into<Arc<str>>,
97 multi_function: Option<bool>,
98 hotplug_slot_number: Option<u32>,
99 ) -> Self {
100 let multi_function = multi_function.unwrap_or(false);
101 let hardware_ids = HardwareIds {
102 vendor_id: VENDOR_ID,
103 device_id: DOWNSTREAM_SWITCH_PORT_DEVICE_ID,
104 revision_id: 0,
105 prog_if: ProgrammingInterface::NONE,
106 sub_class: Subclass::BRIDGE_PCI_TO_PCI,
107 base_class: ClassCode::BRIDGE,
108 type0_sub_vendor_id: 0,
109 type0_sub_system_id: 0,
110 };
111
112 let port = PcieDownstreamPort::new(
113 name.into().to_string(),
114 hardware_ids,
115 DevicePortType::DownstreamSwitchPort,
116 multi_function,
117 hotplug_slot_number,
118 );
119
120 Self { port }
121 }
122
123 pub fn cfg_space(&self) -> &ConfigSpaceType1Emulator {
125 &self.port.cfg_space
126 }
127
128 pub fn cfg_space_mut(&mut self) -> &mut ConfigSpaceType1Emulator {
130 &mut self.port.cfg_space
131 }
132}
133
134pub struct GenericPcieSwitchDefinition {
136 pub name: Arc<str>,
138 pub downstream_port_count: u8,
141 pub hotplug: bool,
143}
144
145#[derive(InspectMut)]
154pub struct GenericPcieSwitch {
155 name: Arc<str>,
157 upstream_port: UpstreamSwitchPort,
159 #[inspect(with = "|x| inspect::iter_by_key(x).map_value(|(_, v)| v)")]
161 downstream_ports: HashMap<u8, (Arc<str>, DownstreamSwitchPort)>,
162}
163
164impl GenericPcieSwitch {
165 pub fn new(definition: GenericPcieSwitchDefinition) -> Self {
167 let upstream_port = UpstreamSwitchPort::new();
168
169 let multi_function = definition.downstream_port_count > 1;
171
172 let downstream_ports = (0..definition.downstream_port_count)
173 .map(|i| {
174 let port_name = format!("{}-downstream-{}", definition.name, i);
175 let hotplug_slot_number = if definition.hotplug {
177 Some((i as u32) + 1)
178 } else {
179 None
180 };
181 let port = DownstreamSwitchPort::new(
182 port_name.clone(),
183 Some(multi_function),
184 hotplug_slot_number,
185 );
186 (i, (port_name.into(), port))
187 })
188 .collect();
189
190 Self {
191 name: definition.name,
192 upstream_port,
193 downstream_ports,
194 }
195 }
196
197 pub fn name(&self) -> &Arc<str> {
199 &self.name
200 }
201
202 pub fn upstream_port(&self) -> &UpstreamSwitchPort {
204 &self.upstream_port
205 }
206
207 pub fn downstream_ports(&self) -> Vec<(u8, Arc<str>)> {
209 self.downstream_ports
210 .iter()
211 .map(|(port, (name, _))| (*port, name.clone()))
212 .collect()
213 }
214
215 fn route_cfg_read(
217 &mut self,
218 bus: u8,
219 device_function: u8,
220 cfg_offset: u16,
221 value: &mut u32,
222 ) -> Option<IoResult> {
223 let upstream_bus_range = self.upstream_port.cfg_space().assigned_bus_range();
224
225 if upstream_bus_range == (0..=0) {
227 return None;
228 }
229
230 if !upstream_bus_range.contains(&bus) {
232 return None;
233 }
234
235 let secondary_bus = *upstream_bus_range.start();
236
237 if bus == secondary_bus {
239 return self.handle_downstream_port_read(device_function, cfg_offset, value);
240 }
241
242 self.route_read_to_downstream_ports(bus, device_function, cfg_offset, value)
244 }
245
246 fn route_cfg_write(
248 &mut self,
249 bus: u8,
250 device_function: u8,
251 cfg_offset: u16,
252 value: u32,
253 ) -> Option<IoResult> {
254 let upstream_bus_range = self.upstream_port.cfg_space().assigned_bus_range();
255
256 if upstream_bus_range == (0..=0) {
258 return None;
259 }
260
261 if !upstream_bus_range.contains(&bus) {
263 return None;
264 }
265
266 let secondary_bus = *upstream_bus_range.start();
267
268 if bus == secondary_bus {
270 return self.handle_downstream_port_write(device_function, cfg_offset, value);
271 }
272
273 self.route_write_to_downstream_ports(bus, device_function, cfg_offset, value)
275 }
276
277 fn handle_downstream_port_read(
279 &mut self,
280 device_function: u8,
281 cfg_offset: u16,
282 value: &mut u32,
283 ) -> Option<IoResult> {
284 if let Some((_, downstream_port)) = self.downstream_ports.get_mut(&device_function) {
285 Some(downstream_port.port.cfg_space.read_u32(cfg_offset, value))
286 } else {
287 None
289 }
290 }
291
292 fn handle_downstream_port_write(
294 &mut self,
295 device_function: u8,
296 cfg_offset: u16,
297 value: u32,
298 ) -> Option<IoResult> {
299 if let Some((_, downstream_port)) = self.downstream_ports.get_mut(&device_function) {
300 Some(downstream_port.port.cfg_space.write_u32(cfg_offset, value))
301 } else {
302 None
304 }
305 }
306
307 fn route_read_to_downstream_ports(
309 &mut self,
310 bus: u8,
311 device_function: u8,
312 cfg_offset: u16,
313 value: &mut u32,
314 ) -> Option<IoResult> {
315 for (_, downstream_port) in self.downstream_ports.values_mut() {
316 let downstream_bus_range = downstream_port.cfg_space().assigned_bus_range();
317
318 if downstream_bus_range == (0..=0) {
320 continue;
321 }
322
323 if downstream_bus_range.contains(&bus) {
324 return Some(downstream_port.port.forward_cfg_read_with_routing(
325 &bus,
326 &device_function,
327 cfg_offset,
328 value,
329 ));
330 }
331 }
332
333 None
335 }
336
337 fn route_write_to_downstream_ports(
339 &mut self,
340 bus: u8,
341 device_function: u8,
342 cfg_offset: u16,
343 value: u32,
344 ) -> Option<IoResult> {
345 for (_, downstream_port) in self.downstream_ports.values_mut() {
346 let downstream_bus_range = downstream_port.cfg_space().assigned_bus_range();
347
348 if downstream_bus_range == (0..=0) {
350 continue;
351 }
352
353 if downstream_bus_range.contains(&bus) {
354 return Some(downstream_port.port.forward_cfg_write_with_routing(
355 &bus,
356 &device_function,
357 cfg_offset,
358 value,
359 ));
360 }
361 }
362
363 None
365 }
366
367 pub fn add_pcie_device(
369 &mut self,
370 port: u8,
371 name: &str,
372 dev: Box<dyn GenericPciBusDevice>,
373 ) -> anyhow::Result<()> {
374 if let Some((port_name, downstream_port)) = self.downstream_ports.get_mut(&port) {
376 downstream_port
378 .port
379 .add_pcie_device(port_name.as_ref(), name, dev)
380 .context("failed to add PCIe device to downstream port")?;
381 Ok(())
382 } else {
383 bail!("port {} not found", port);
385 }
386 }
387}
388
389impl ChangeDeviceState for GenericPcieSwitch {
390 fn start(&mut self) {}
391
392 async fn stop(&mut self) {}
393
394 async fn reset(&mut self) {
395 self.upstream_port.cfg_space.reset();
397
398 for (_, downstream_port) in self.downstream_ports.values_mut() {
400 downstream_port.port.cfg_space.reset();
401 }
402 }
403}
404
405impl ChipsetDevice for GenericPcieSwitch {
406 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
407 Some(self)
408 }
409}
410
411impl PciConfigSpace for GenericPcieSwitch {
412 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
413 self.upstream_port.cfg_space.read_u32(offset, value)
415 }
416
417 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
418 self.upstream_port.cfg_space.write_u32(offset, value)
420 }
421
422 fn pci_cfg_read_forward(
423 &mut self,
424 bus: u8,
425 device_function: u8,
426 offset: u16,
427 value: &mut u32,
428 ) -> Option<IoResult> {
429 self.route_cfg_read(bus, device_function, offset, value)
430 }
431
432 fn pci_cfg_write_forward(
433 &mut self,
434 bus: u8,
435 device_function: u8,
436 offset: u16,
437 value: u32,
438 ) -> Option<IoResult> {
439 self.route_cfg_write(bus, device_function, offset, value)
440 }
441
442 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
443 None
445 }
446}
447
448mod save_restore {
449 use super::*;
450 use vmcore::save_restore::SaveError;
451 use vmcore::save_restore::SaveRestore;
452 use vmcore::save_restore::SavedStateNotSupported;
453
454 impl SaveRestore for GenericPcieSwitch {
455 type SavedState = SavedStateNotSupported;
456
457 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
458 Err(SaveError::NotSupported)
459 }
460
461 fn restore(
462 &mut self,
463 state: Self::SavedState,
464 ) -> Result<(), vmcore::save_restore::RestoreError> {
465 match state {}
466 }
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 #[test]
475 fn test_upstream_switch_port_creation() {
476 let port = UpstreamSwitchPort::new();
477
478 let mut vendor_device_id: u32 = 0;
480 port.cfg_space.read_u32(0x0, &mut vendor_device_id).unwrap();
481 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
482 assert_eq!(vendor_device_id, expected);
483 }
484
485 #[test]
486 fn test_downstream_switch_port_creation() {
487 let port = DownstreamSwitchPort::new("test-downstream-port", None, None);
488 assert!(port.port.link.is_none());
489
490 let mut vendor_device_id: u32 = 0;
492 port.port
493 .cfg_space
494 .read_u32(0x0, &mut vendor_device_id)
495 .unwrap();
496 let expected = (DOWNSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
497 assert_eq!(vendor_device_id, expected);
498 }
499
500 #[test]
501 fn test_downstream_switch_port_multi_function_options() {
502 let port_default = DownstreamSwitchPort::new("test-port-default", None, None);
504 let mut header_type_value: u32 = 0;
505 port_default
506 .cfg_space()
507 .read_u32(0x0C, &mut header_type_value)
508 .unwrap();
509 let header_type_field = (header_type_value >> 16) & 0xFF;
510 assert_eq!(
511 header_type_field & 0x80,
512 0x00,
513 "Multi-function bit should NOT be set with None parameter"
514 );
515
516 let port_false = DownstreamSwitchPort::new("test-port-false", Some(false), None);
518 let mut header_type_value_false: u32 = 0;
519 port_false
520 .cfg_space()
521 .read_u32(0x0C, &mut header_type_value_false)
522 .unwrap();
523 let header_type_field_false = (header_type_value_false >> 16) & 0xFF;
524 assert_eq!(
525 header_type_field_false & 0x80,
526 0x00,
527 "Multi-function bit should NOT be set with Some(false)"
528 );
529
530 let port_true = DownstreamSwitchPort::new("test-port-true", Some(true), None);
532 let mut header_type_value_true: u32 = 0;
533 port_true
534 .cfg_space()
535 .read_u32(0x0C, &mut header_type_value_true)
536 .unwrap();
537 let header_type_field_true = (header_type_value_true >> 16) & 0xFF;
538 assert_eq!(
539 header_type_field_true & 0x80,
540 0x80,
541 "Multi-function bit should be set with Some(true)"
542 );
543 }
544
545 #[test]
546 fn test_downstream_switch_port_hotplug_options() {
547 let port_no_hotplug = DownstreamSwitchPort::new("test-port-no-hotplug", None, None);
549 let mut vendor_device_id: u32 = 0;
552 port_no_hotplug
553 .cfg_space()
554 .read_u32(0x0, &mut vendor_device_id)
555 .unwrap();
556 let expected = (DOWNSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
557 assert_eq!(vendor_device_id, expected);
558
559 let port_with_hotplug = DownstreamSwitchPort::new("test-port-hotplug", None, Some(42));
561 let mut vendor_device_id_hotplug: u32 = 0;
562 port_with_hotplug
563 .cfg_space()
564 .read_u32(0x0, &mut vendor_device_id_hotplug)
565 .unwrap();
566 assert_eq!(vendor_device_id_hotplug, expected);
567 }
570
571 #[test]
572 fn test_switch_creation() {
573 let definition = GenericPcieSwitchDefinition {
574 name: "test-switch".into(),
575 downstream_port_count: 3,
576 hotplug: false,
577 };
578 let switch = GenericPcieSwitch::new(definition);
579
580 assert_eq!(switch.name().as_ref(), "test-switch");
581 assert_eq!(switch.downstream_ports().len(), 3);
582
583 let ports = switch.downstream_ports();
585 let port_names: std::collections::HashSet<_> =
586 ports.iter().map(|(_, name)| name.as_ref()).collect();
587 assert!(port_names.contains("test-switch-downstream-0"));
588 assert!(port_names.contains("test-switch-downstream-1"));
589 assert!(port_names.contains("test-switch-downstream-2"));
590
591 let port_numbers: std::collections::HashSet<_> =
593 ports.iter().map(|(num, _)| *num).collect();
594 assert!(port_numbers.contains(&0));
595 assert!(port_numbers.contains(&1));
596 assert!(port_numbers.contains(&2));
597 }
598
599 #[test]
600 fn test_switch_device_connections() {
601 use crate::test_helpers::TestPcieEndpoint;
602 use chipset_device::io::IoError;
603
604 let definition = GenericPcieSwitchDefinition {
605 name: "test-switch".into(),
606 downstream_port_count: 2,
607 hotplug: false,
608 };
609 let mut switch = GenericPcieSwitch::new(definition);
610
611 let downstream_device = TestPcieEndpoint::new(
612 |offset, value| match offset {
613 0x0 => {
614 *value = 0xABCD_EF01;
615 Some(IoResult::Ok)
616 }
617 _ => Some(IoResult::Err(IoError::InvalidRegister)),
618 },
619 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
620 );
621
622 assert!(
624 switch
625 .add_pcie_device(
626 0, "downstream-dev",
628 Box::new(downstream_device)
629 )
630 .is_ok()
631 );
632
633 let invalid_device = TestPcieEndpoint::new(
635 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
636 |_, _| Some(IoResult::Err(IoError::InvalidRegister)),
637 );
638 let result = switch.add_pcie_device(99, "invalid-dev", Box::new(invalid_device)); assert!(result.is_err());
640 assert!(result.is_err());
643 }
644
645 #[test]
646 fn test_switch_routing_functionality() {
647 use crate::test_helpers::TestPcieEndpoint;
648 use chipset_device::io::IoResult;
649
650 let definition = GenericPcieSwitchDefinition {
651 name: "test-switch".into(),
652 downstream_port_count: 2,
653 hotplug: false,
654 };
655 let mut switch = GenericPcieSwitch::new(definition);
656
657 let test_device =
660 TestPcieEndpoint::new(|_, _| Some(IoResult::Ok), |_, _| Some(IoResult::Ok));
661 let add_result = switch.add_pcie_device(0, "test-device", Box::new(test_device));
662 assert!(add_result.is_ok());
664
665 let mut value = 0u32;
667 let result = switch
668 .upstream_port
669 .cfg_space_mut()
670 .read_u32(0x0, &mut value);
671 assert!(matches!(result, IoResult::Ok));
672
673 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
675 assert_eq!(value, expected);
676 }
677
678 #[test]
679 fn test_switch_chipset_device() {
680 use chipset_device::ChipsetDevice;
681 use chipset_device::pci::PciConfigSpace;
682
683 let definition = GenericPcieSwitchDefinition {
684 name: "test-switch".into(),
685 downstream_port_count: 4,
686 hotplug: false,
687 };
688 let mut switch = GenericPcieSwitch::new(definition);
689
690 assert!(switch.supports_pci().is_some());
692 assert!(switch.supports_mmio().is_none());
693 assert!(switch.supports_pio().is_none());
694 assert!(switch.supports_poll_device().is_none());
695
696 let mut value = 0u32;
698 let result = PciConfigSpace::pci_cfg_read(&mut switch, 0x0, &mut value);
699 assert!(matches!(result, IoResult::Ok));
700
701 let expected = (UPSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
703 assert_eq!(value, expected);
704
705 let result = PciConfigSpace::pci_cfg_write(&mut switch, 0x4, 0x12345678);
707 assert!(matches!(result, IoResult::Ok));
708 }
709
710 #[test]
711 fn test_switch_default() {
712 let definition = GenericPcieSwitchDefinition {
713 name: "default-switch".into(),
714 downstream_port_count: 4,
715 hotplug: false,
716 };
717 let switch = GenericPcieSwitch::new(definition);
718 assert_eq!(switch.name().as_ref(), "default-switch");
719 assert_eq!(switch.downstream_ports().len(), 4);
720 }
721
722 #[test]
723 fn test_switch_large_downstream_port_count() {
724 let definition = GenericPcieSwitchDefinition {
725 name: "test-switch".into(),
726 downstream_port_count: 16,
727 hotplug: false,
728 };
729 let switch = GenericPcieSwitch::new(definition);
730 assert_eq!(switch.downstream_ports().len(), 16);
731 }
732
733 #[test]
734 fn test_switch_downstream_port_direct_access() {
735 let definition = GenericPcieSwitchDefinition {
736 name: "test-switch".into(),
737 downstream_port_count: 3,
738 hotplug: false,
739 };
740 let mut switch = GenericPcieSwitch::new(definition);
741
742 let secondary_bus = 1u8;
744 let bus_config = (10u32 << 24) | ((secondary_bus as u32) << 16); switch
747 .upstream_port
748 .cfg_space_mut()
749 .write_u32(0x18, bus_config)
750 .unwrap();
751
752 let bus_range = switch.upstream_port.cfg_space().assigned_bus_range();
753 let switch_internal_bus = *bus_range.start(); let mut value = 0u32;
757 let result = switch.route_cfg_read(switch_internal_bus, 0, 0x0, &mut value);
758 assert!(result.is_some());
759
760 let expected = (DOWNSTREAM_SWITCH_PORT_DEVICE_ID as u32) << 16 | (VENDOR_ID as u32);
762 assert_eq!(value, expected);
763
764 let mut value2 = 0u32;
766 let result2 = switch.route_cfg_read(switch_internal_bus, 2, 0x0, &mut value2);
767 assert!(result2.is_some());
768 assert_eq!(value2, expected);
769
770 let mut value3 = 0u32;
772 let result3 = switch.route_cfg_read(switch_internal_bus, 5, 0x0, &mut value3);
773 assert!(result3.is_none());
774 }
775
776 #[test]
777 fn test_switch_invalid_bus_range_handling() {
778 let definition = GenericPcieSwitchDefinition {
779 name: "test-switch".into(),
780 downstream_port_count: 2,
781 hotplug: false,
782 };
783 let mut switch = GenericPcieSwitch::new(definition);
784
785 let bus_range = switch.upstream_port.cfg_space().assigned_bus_range();
787 assert_eq!(bus_range, 0..=0);
788
789 let mut value = 0u32;
791 let result = switch.route_cfg_read(0, 0, 0x0, &mut value);
792 assert!(result.is_none());
793
794 let result2 = switch.route_cfg_read(1, 0, 0x0, &mut value);
795 assert!(result2.is_none());
796
797 let result3 = switch.route_cfg_write(0, 0, 0x0, value);
798 assert!(result3.is_none());
799 }
800
801 #[test]
802 fn test_switch_downstream_port_invalid_bus_range_skipping() {
803 let definition = GenericPcieSwitchDefinition {
804 name: "test-switch".into(),
805 downstream_port_count: 2,
806 hotplug: false,
807 };
808 let mut switch = GenericPcieSwitch::new(definition);
809
810 let secondary_bus = 1u8;
812 let subordinate_bus = 10u8;
813 let primary_bus = 0u8;
814 let bus_config =
815 ((subordinate_bus as u32) << 16) | ((secondary_bus as u32) << 8) | (primary_bus as u32); switch
817 .upstream_port
818 .cfg_space_mut()
819 .write_u32(0x18, bus_config)
820 .unwrap();
821
822 let mut value = 0u32;
825
826 let result = switch.route_cfg_read(2, 0, 0x0, &mut value);
828 assert!(result.is_none());
829
830 let result2 = switch.route_cfg_read(5, 0, 0x0, &mut value);
832 assert!(result2.is_none());
833
834 let result3 = switch.route_cfg_read(secondary_bus, 0, 0x0, &mut value);
836 assert!(result3.is_some());
837 }
838
839 #[test]
840 fn test_switch_multi_function_bit() {
841 let multi_port_definition = GenericPcieSwitchDefinition {
843 name: "multi-port-switch".into(),
844 downstream_port_count: 3,
845 hotplug: false,
846 };
847 let multi_port_switch = GenericPcieSwitch::new(multi_port_definition);
848
849 for (port_num, _) in multi_port_switch.downstream_ports() {
851 if let Some((_, downstream_port)) = multi_port_switch.downstream_ports.get(&port_num) {
852 let mut header_type_value: u32 = 0;
853 downstream_port
854 .cfg_space()
855 .read_u32(0x0C, &mut header_type_value)
856 .unwrap();
857
858 let header_type_field = (header_type_value >> 16) & 0xFF;
860
861 assert_eq!(
863 header_type_field & 0x80,
864 0x80,
865 "Multi-function bit should be set for downstream port {} in multi-port switch",
866 port_num
867 );
868
869 assert_eq!(
871 header_type_field & 0x7F,
872 0x01,
873 "Header type should be 01 (bridge) for downstream port {}",
874 port_num
875 );
876 }
877 }
878
879 let single_port_definition = GenericPcieSwitchDefinition {
881 name: "single-port-switch".into(),
882 downstream_port_count: 1,
883 hotplug: false,
884 };
885 let single_port_switch = GenericPcieSwitch::new(single_port_definition);
886
887 for (port_num, _) in single_port_switch.downstream_ports() {
889 if let Some((_, downstream_port)) = single_port_switch.downstream_ports.get(&port_num) {
890 let mut header_type_value: u32 = 0;
891 downstream_port
892 .cfg_space()
893 .read_u32(0x0C, &mut header_type_value)
894 .unwrap();
895
896 let header_type_field = (header_type_value >> 16) & 0xFF;
898
899 assert_eq!(
901 header_type_field & 0x80,
902 0x00,
903 "Multi-function bit should NOT be set for downstream port {} in single-port switch",
904 port_num
905 );
906
907 assert_eq!(
909 header_type_field & 0x7F,
910 0x01,
911 "Header type should be 01 (bridge) for downstream port {}",
912 port_num
913 );
914 }
915 }
916 }
917
918 #[test]
919 fn test_hotplug_support() {
920 let definition_no_hotplug = GenericPcieSwitchDefinition {
922 name: "test-switch-no-hotplug".into(),
923 downstream_port_count: 1,
924 hotplug: false,
925 };
926 let switch_no_hotplug = GenericPcieSwitch::new(definition_no_hotplug);
927 assert_eq!(switch_no_hotplug.name().as_ref(), "test-switch-no-hotplug");
928
929 let definition_with_hotplug = GenericPcieSwitchDefinition {
931 name: "test-switch-with-hotplug".into(),
932 downstream_port_count: 1,
933 hotplug: true,
934 };
935 let switch_with_hotplug = GenericPcieSwitch::new(definition_with_hotplug);
936 assert_eq!(
937 switch_with_hotplug.name().as_ref(),
938 "test-switch-with-hotplug"
939 );
940 }
941}