1#![allow(dead_code)]
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13#[cfg(feature = "alloc")]
14use alloc::{collections::VecDeque, vec, vec::Vec};
15
16use super::VmError;
17
18const MAX_HOTPLUG_CPUS: usize = 256;
24
25const MAX_DIMMS: usize = 64;
27
28const MAX_PCI_SLOTS: usize = 32;
30
31const MAX_GED_EVENTS: usize = 64;
33
34const BITS_PER_WORD: usize = 64;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum HotplugType {
44 Cpu,
46 Memory,
48 PciDevice,
50}
51
52#[derive(Debug, Clone)]
58pub struct HotplugEvent {
59 pub event_type: HotplugType,
61 pub device_info: u64,
63 pub timestamp: u64,
65 pub is_add: bool,
67}
68
69impl HotplugEvent {
70 pub fn new(event_type: HotplugType, device_info: u64, timestamp: u64, is_add: bool) -> Self {
72 Self {
73 event_type,
74 device_info,
75 timestamp,
76 is_add,
77 }
78 }
79
80 pub fn cpu_add(cpu_id: u32, timestamp: u64) -> Self {
82 Self::new(HotplugType::Cpu, cpu_id as u64, timestamp, true)
83 }
84
85 pub fn cpu_remove(cpu_id: u32, timestamp: u64) -> Self {
87 Self::new(HotplugType::Cpu, cpu_id as u64, timestamp, false)
88 }
89
90 pub fn memory_add(dimm_slot: u32, timestamp: u64) -> Self {
92 Self::new(HotplugType::Memory, dimm_slot as u64, timestamp, true)
93 }
94
95 pub fn memory_remove(dimm_slot: u32, timestamp: u64) -> Self {
97 Self::new(HotplugType::Memory, dimm_slot as u64, timestamp, false)
98 }
99
100 pub fn pci_add(slot_id: u32, timestamp: u64) -> Self {
102 Self::new(HotplugType::PciDevice, slot_id as u64, timestamp, true)
103 }
104
105 pub fn pci_remove(slot_id: u32, timestamp: u64) -> Self {
107 Self::new(HotplugType::PciDevice, slot_id as u64, timestamp, false)
108 }
109}
110
111#[cfg(feature = "alloc")]
117pub struct AcpiGed {
118 events: VecDeque<HotplugEvent>,
120 event_counter: u64,
122 enabled: bool,
124}
125
126#[cfg(feature = "alloc")]
127impl Default for AcpiGed {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133#[cfg(feature = "alloc")]
134impl AcpiGed {
135 pub fn new() -> Self {
137 Self {
138 events: VecDeque::new(),
139 event_counter: 0,
140 enabled: true,
141 }
142 }
143
144 pub(crate) fn inject_event(&mut self, mut event: HotplugEvent) -> Result<(), VmError> {
146 if !self.enabled {
147 return Err(VmError::DeviceError);
148 }
149 if self.events.len() >= MAX_GED_EVENTS {
150 return Err(VmError::DeviceError);
151 }
152 self.event_counter = self.event_counter.saturating_add(1);
153 event.timestamp = self.event_counter;
154 self.events.push_back(event);
155 Ok(())
156 }
157
158 pub(crate) fn poll_event(&mut self) -> Option<HotplugEvent> {
160 self.events.pop_front()
161 }
162
163 pub(crate) fn has_pending_events(&self) -> bool {
165 !self.events.is_empty()
166 }
167
168 pub(crate) fn pending_count(&self) -> usize {
170 self.events.len()
171 }
172
173 pub(crate) fn clear(&mut self) {
175 self.events.clear();
176 }
177
178 pub(crate) fn set_enabled(&mut self, enabled: bool) {
180 self.enabled = enabled;
181 }
182
183 pub(crate) fn is_enabled(&self) -> bool {
185 self.enabled
186 }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
195pub enum CpuLifecycleState {
196 #[default]
198 Empty,
199 Allocated,
201 Initializing,
203 Online,
205 Removing,
207 Removed,
209}
210
211#[cfg(feature = "alloc")]
217pub struct CpuHotplug {
218 pub max_cpus: u32,
220 online_cpus: Vec<u64>,
222 cpu_states: Vec<CpuLifecycleState>,
224 online_count: u32,
226 boot_cpu: u32,
228}
229
230#[cfg(feature = "alloc")]
231impl CpuHotplug {
232 pub fn new(max_cpus: u32, boot_cpu: u32) -> Self {
234 let max = max_cpus.min(MAX_HOTPLUG_CPUS as u32);
235 let bitmap_words = (max as usize).div_ceil(BITS_PER_WORD);
236 let mut cpu_states = vec![CpuLifecycleState::Empty; max as usize];
237
238 let mut online_cpus = vec![0u64; bitmap_words];
240 if (boot_cpu as usize) < cpu_states.len() {
241 cpu_states[boot_cpu as usize] = CpuLifecycleState::Online;
242 let word = boot_cpu as usize / BITS_PER_WORD;
243 let bit = boot_cpu as usize % BITS_PER_WORD;
244 if word < online_cpus.len() {
245 online_cpus[word] |= 1u64 << bit;
246 }
247 }
248
249 Self {
250 max_cpus: max,
251 online_cpus,
252 cpu_states,
253 online_count: 1,
254 boot_cpu,
255 }
256 }
257
258 pub(crate) fn add_cpu(&mut self, cpu_id: u32) -> Result<(), VmError> {
260 if cpu_id >= self.max_cpus {
261 return Err(VmError::InvalidVmState);
262 }
263 let idx = cpu_id as usize;
264 if self.cpu_states[idx] != CpuLifecycleState::Empty
265 && self.cpu_states[idx] != CpuLifecycleState::Removed
266 {
267 return Err(VmError::InvalidVmState);
268 }
269
270 self.cpu_states[idx] = CpuLifecycleState::Allocated;
272 self.cpu_states[idx] = CpuLifecycleState::Initializing;
273 self.cpu_states[idx] = CpuLifecycleState::Online;
275
276 let word = idx / BITS_PER_WORD;
277 let bit = idx % BITS_PER_WORD;
278 if word < self.online_cpus.len() {
279 self.online_cpus[word] |= 1u64 << bit;
280 }
281 self.online_count = self.online_count.saturating_add(1);
282 Ok(())
283 }
284
285 pub(crate) fn remove_cpu(&mut self, cpu_id: u32) -> Result<(), VmError> {
287 if cpu_id >= self.max_cpus {
288 return Err(VmError::InvalidVmState);
289 }
290 if cpu_id == self.boot_cpu {
291 return Err(VmError::InvalidVmState); }
293 let idx = cpu_id as usize;
294 if self.cpu_states[idx] != CpuLifecycleState::Online {
295 return Err(VmError::InvalidVmState);
296 }
297
298 self.cpu_states[idx] = CpuLifecycleState::Removing;
300 self.cpu_states[idx] = CpuLifecycleState::Removed;
302
303 let word = idx / BITS_PER_WORD;
304 let bit = idx % BITS_PER_WORD;
305 if word < self.online_cpus.len() {
306 self.online_cpus[word] &= !(1u64 << bit);
307 }
308 self.online_count = self.online_count.saturating_sub(1);
309 Ok(())
310 }
311
312 pub(crate) fn is_online(&self, cpu_id: u32) -> bool {
314 if cpu_id >= self.max_cpus {
315 return false;
316 }
317 let word = cpu_id as usize / BITS_PER_WORD;
318 let bit = cpu_id as usize % BITS_PER_WORD;
319 if word < self.online_cpus.len() {
320 self.online_cpus[word] & (1u64 << bit) != 0
321 } else {
322 false
323 }
324 }
325
326 pub(crate) fn cpu_state(&self, cpu_id: u32) -> CpuLifecycleState {
328 if (cpu_id as usize) < self.cpu_states.len() {
329 self.cpu_states[cpu_id as usize]
330 } else {
331 CpuLifecycleState::Empty
332 }
333 }
334
335 pub(crate) fn online_count(&self) -> u32 {
337 self.online_count
338 }
339
340 pub(crate) fn max_cpus(&self) -> u32 {
342 self.max_cpus
343 }
344
345 pub(crate) fn online_cpu_ids(&self) -> Vec<u32> {
347 let mut ids = Vec::new();
348 for (word_idx, &word) in self.online_cpus.iter().enumerate() {
349 if word == 0 {
350 continue;
351 }
352 for bit in 0..BITS_PER_WORD {
353 if word & (1u64 << bit) != 0 {
354 let cpu_id = (word_idx * BITS_PER_WORD + bit) as u32;
355 if cpu_id < self.max_cpus {
356 ids.push(cpu_id);
357 }
358 }
359 }
360 }
361 ids
362 }
363}
364
365#[derive(Debug, Clone, Copy)]
371pub struct MemoryDimm {
372 pub slot: u32,
374 pub size_mb: u32,
376 pub base_addr: u64,
378 pub online: bool,
380}
381
382impl MemoryDimm {
383 pub fn new(slot: u32, size_mb: u32, base_addr: u64) -> Self {
385 Self {
386 slot,
387 size_mb,
388 base_addr,
389 online: false,
390 }
391 }
392
393 pub(crate) fn size_bytes(&self) -> u64 {
395 self.size_mb as u64 * 1024 * 1024
396 }
397
398 pub(crate) fn end_addr(&self) -> u64 {
400 self.base_addr + self.size_bytes()
401 }
402}
403
404#[cfg(feature = "alloc")]
410pub struct MemoryHotplug {
411 pub max_dimms: u32,
413 pub dimms: Vec<MemoryDimm>,
415 next_base_addr: u64,
417 total_online_mb: u64,
419}
420
421#[cfg(feature = "alloc")]
422impl MemoryHotplug {
423 pub fn new(max_dimms: u32, initial_base: u64) -> Self {
427 Self {
428 max_dimms: max_dimms.min(MAX_DIMMS as u32),
429 dimms: Vec::new(),
430 next_base_addr: initial_base,
431 total_online_mb: 0,
432 }
433 }
434
435 pub(crate) fn add_dimm(&mut self, size_mb: u32) -> Result<u32, VmError> {
437 if self.dimms.len() >= self.max_dimms as usize {
438 return Err(VmError::GuestMemoryError);
439 }
440 if size_mb == 0 {
441 return Err(VmError::GuestMemoryError);
442 }
443
444 let slot = self.dimms.len() as u32;
445 let base_addr = self.next_base_addr;
446 let size_bytes = size_mb as u64 * 1024 * 1024;
447
448 let mut dimm = MemoryDimm::new(slot, size_mb, base_addr);
449 dimm.online = true;
450 self.dimms.push(dimm);
451
452 self.next_base_addr = self.next_base_addr.saturating_add(size_bytes);
453 self.total_online_mb = self.total_online_mb.saturating_add(size_mb as u64);
454
455 Ok(slot)
461 }
462
463 pub(crate) fn remove_dimm(&mut self, slot: u32) -> Result<(), VmError> {
465 let dimm = self
466 .dimms
467 .iter_mut()
468 .find(|d| d.slot == slot)
469 .ok_or(VmError::GuestMemoryError)?;
470
471 if !dimm.online {
472 return Err(VmError::GuestMemoryError);
473 }
474
475 self.total_online_mb = self.total_online_mb.saturating_sub(dimm.size_mb as u64);
481 dimm.online = false;
482 Ok(())
483 }
484
485 pub(crate) fn dimm(&self, slot: u32) -> Option<&MemoryDimm> {
487 self.dimms.iter().find(|d| d.slot == slot)
488 }
489
490 pub(crate) fn total_online_mb(&self) -> u64 {
492 self.total_online_mb
493 }
494
495 pub(crate) fn dimm_count(&self) -> usize {
497 self.dimms.len()
498 }
499
500 pub(crate) fn online_dimm_count(&self) -> usize {
502 self.dimms.iter().filter(|d| d.online).count()
503 }
504}
505
506#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
512pub enum PowerIndicator {
513 #[default]
515 Off,
516 On,
518 Blink,
520}
521
522#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
524pub enum AttentionIndicator {
525 #[default]
527 Off,
528 On,
530 Blink,
532}
533
534#[derive(Debug, Clone, Copy, Default)]
540pub struct PciHotplugSlot {
541 pub slot_id: u32,
543 pub occupied: bool,
545 pub device_addr: u32, pub power_indicator: PowerIndicator,
549 pub attention_indicator: AttentionIndicator,
551 pub powered: bool,
553 pub surprise_removal_supported: bool,
555}
556
557impl PciHotplugSlot {
558 pub fn new(slot_id: u32) -> Self {
560 Self {
561 slot_id,
562 surprise_removal_supported: false,
563 ..Default::default()
564 }
565 }
566
567 pub fn with_surprise_removal(slot_id: u32) -> Self {
569 Self {
570 slot_id,
571 surprise_removal_supported: true,
572 ..Default::default()
573 }
574 }
575}
576
577#[cfg(feature = "alloc")]
583pub struct ShpcController {
584 pub slots: Vec<PciHotplugSlot>,
586 pub base_addr: u64,
588 pub enabled: bool,
590}
591
592#[cfg(feature = "alloc")]
593impl ShpcController {
594 pub fn new(num_slots: u32, base_addr: u64) -> Self {
596 let count = num_slots.min(MAX_PCI_SLOTS as u32);
597 let mut slots = Vec::with_capacity(count as usize);
598 for i in 0..count {
599 slots.push(PciHotplugSlot::new(i));
600 }
601 Self {
602 slots,
603 base_addr,
604 enabled: true,
605 }
606 }
607
608 pub(crate) fn slot_power_on(&mut self, slot_id: u32, device_bdf: u32) -> Result<(), VmError> {
610 let slot = self
611 .slots
612 .iter_mut()
613 .find(|s| s.slot_id == slot_id)
614 .ok_or(VmError::DeviceError)?;
615
616 if slot.occupied {
617 return Err(VmError::DeviceError);
618 }
619
620 slot.occupied = true;
621 slot.device_addr = device_bdf;
622 slot.powered = true;
623 slot.power_indicator = PowerIndicator::On;
624 Ok(())
625 }
626
627 pub(crate) fn slot_power_off(&mut self, slot_id: u32) -> Result<(), VmError> {
629 let slot = self
630 .slots
631 .iter_mut()
632 .find(|s| s.slot_id == slot_id)
633 .ok_or(VmError::DeviceError)?;
634
635 if !slot.occupied {
636 return Err(VmError::DeviceError);
637 }
638
639 slot.powered = false;
640 slot.power_indicator = PowerIndicator::Off;
641 slot.occupied = false;
642 slot.device_addr = 0;
643 Ok(())
644 }
645
646 pub(crate) fn surprise_removal(&mut self, slot_id: u32) -> Result<HotplugEvent, VmError> {
648 let slot = self
649 .slots
650 .iter_mut()
651 .find(|s| s.slot_id == slot_id)
652 .ok_or(VmError::DeviceError)?;
653
654 if !slot.occupied {
655 return Err(VmError::DeviceError);
656 }
657 if !slot.surprise_removal_supported {
658 return Err(VmError::DeviceError);
659 }
660
661 let event = HotplugEvent::pci_remove(slot_id, 0);
662 slot.occupied = false;
663 slot.powered = false;
664 slot.power_indicator = PowerIndicator::Off;
665 slot.attention_indicator = AttentionIndicator::Blink;
666 slot.device_addr = 0;
667
668 Ok(event)
669 }
670
671 pub(crate) fn slot(&self, slot_id: u32) -> Option<&PciHotplugSlot> {
673 self.slots.iter().find(|s| s.slot_id == slot_id)
674 }
675
676 pub(crate) fn occupied_count(&self) -> usize {
678 self.slots.iter().filter(|s| s.occupied).count()
679 }
680
681 pub(crate) fn slot_count(&self) -> usize {
683 self.slots.len()
684 }
685}
686
687#[derive(Debug, Clone, Copy, PartialEq, Eq)]
693pub enum SlotEvent {
694 AttentionButton,
696 PowerFault,
698 PresenceChanged,
700 CommandCompleted,
702 DllStateChanged,
704}
705
706#[derive(Debug, Clone, Copy, Default)]
708pub struct PcieNativeHotplug {
709 pub slot_id: u32,
711 pub presence_detect: bool,
713 pub power_fault: bool,
715 pub attention_button: bool,
717 pub power_enabled: bool,
719 pub power_indicator: PowerIndicator,
721 pub attention_indicator: AttentionIndicator,
723 pub surprise_supported: bool,
725 pub hotplug_capable: bool,
727 pub dll_active: bool,
729}
730
731impl PcieNativeHotplug {
732 pub fn new(slot_id: u32) -> Self {
734 Self {
735 slot_id,
736 hotplug_capable: true,
737 surprise_supported: true,
738 ..Default::default()
739 }
740 }
741
742 pub(crate) fn handle_slot_event(&mut self, event: SlotEvent) -> Option<HotplugEvent> {
744 match event {
745 SlotEvent::AttentionButton => {
746 self.attention_button = true;
747 self.attention_indicator = AttentionIndicator::Blink;
748 if self.presence_detect {
750 Some(HotplugEvent::pci_remove(self.slot_id, 0))
752 } else {
753 None
754 }
755 }
756 SlotEvent::PowerFault => {
757 self.power_fault = true;
758 self.power_enabled = false;
759 self.power_indicator = PowerIndicator::Blink;
760 self.attention_indicator = AttentionIndicator::On;
761 None
762 }
763 SlotEvent::PresenceChanged => {
764 self.presence_detect = !self.presence_detect;
765 if self.presence_detect {
766 self.power_indicator = PowerIndicator::Blink;
768 Some(HotplugEvent::pci_add(self.slot_id, 0))
769 } else {
770 self.power_indicator = PowerIndicator::Off;
772 self.power_enabled = false;
773 Some(HotplugEvent::pci_remove(self.slot_id, 0))
774 }
775 }
776 SlotEvent::CommandCompleted => {
777 None
779 }
780 SlotEvent::DllStateChanged => {
781 self.dll_active = !self.dll_active;
782 None
783 }
784 }
785 }
786
787 pub(crate) fn power_on(&mut self) {
789 self.power_enabled = true;
790 self.power_indicator = PowerIndicator::On;
791 self.power_fault = false;
792 }
793
794 pub(crate) fn power_off(&mut self) {
796 self.power_enabled = false;
797 self.power_indicator = PowerIndicator::Off;
798 }
799
800 pub(crate) fn is_present(&self) -> bool {
802 self.presence_detect
803 }
804
805 pub(crate) fn is_powered(&self) -> bool {
807 self.power_enabled
808 }
809}
810
811#[cfg(test)]
816mod tests {
817 use super::*;
818
819 #[test]
820 fn test_hotplug_event_cpu() {
821 let event = HotplugEvent::cpu_add(4, 100);
822 assert_eq!(event.event_type, HotplugType::Cpu);
823 assert_eq!(event.device_info, 4);
824 assert!(event.is_add);
825 }
826
827 #[test]
828 fn test_hotplug_event_memory() {
829 let event = HotplugEvent::memory_remove(2, 200);
830 assert_eq!(event.event_type, HotplugType::Memory);
831 assert!(!event.is_add);
832 }
833
834 #[test]
835 fn test_hotplug_event_pci() {
836 let event = HotplugEvent::pci_add(1, 300);
837 assert_eq!(event.event_type, HotplugType::PciDevice);
838 assert!(event.is_add);
839 }
840
841 #[test]
842 fn test_acpi_ged_inject_poll() {
843 let mut ged = AcpiGed::new();
844 assert!(!ged.has_pending_events());
845
846 ged.inject_event(HotplugEvent::cpu_add(1, 0)).unwrap();
847 ged.inject_event(HotplugEvent::memory_add(0, 0)).unwrap();
848 assert_eq!(ged.pending_count(), 2);
849
850 let ev1 = ged.poll_event().unwrap();
851 assert_eq!(ev1.event_type, HotplugType::Cpu);
852
853 let ev2 = ged.poll_event().unwrap();
854 assert_eq!(ev2.event_type, HotplugType::Memory);
855
856 assert!(ged.poll_event().is_none());
857 }
858
859 #[test]
860 fn test_acpi_ged_disabled() {
861 let mut ged = AcpiGed::new();
862 ged.set_enabled(false);
863 assert!(ged.inject_event(HotplugEvent::cpu_add(0, 0)).is_err());
864 }
865
866 #[test]
867 fn test_acpi_ged_clear() {
868 let mut ged = AcpiGed::new();
869 ged.inject_event(HotplugEvent::cpu_add(0, 0)).unwrap();
870 ged.inject_event(HotplugEvent::cpu_add(1, 0)).unwrap();
871 ged.clear();
872 assert_eq!(ged.pending_count(), 0);
873 }
874
875 #[test]
876 fn test_cpu_hotplug_new() {
877 let hp = CpuHotplug::new(8, 0);
878 assert_eq!(hp.online_count(), 1); assert!(hp.is_online(0));
880 }
881
882 #[test]
883 fn test_cpu_hotplug_add() {
884 let mut hp = CpuHotplug::new(8, 0);
885 hp.add_cpu(1).unwrap();
886 hp.add_cpu(2).unwrap();
887 assert_eq!(hp.online_count(), 3);
888 assert!(hp.is_online(1));
889 assert!(hp.is_online(2));
890 assert_eq!(hp.cpu_state(1), CpuLifecycleState::Online);
891 }
892
893 #[test]
894 fn test_cpu_hotplug_remove() {
895 let mut hp = CpuHotplug::new(8, 0);
896 hp.add_cpu(1).unwrap();
897 hp.remove_cpu(1).unwrap();
898 assert_eq!(hp.online_count(), 1);
899 assert!(!hp.is_online(1));
900 assert_eq!(hp.cpu_state(1), CpuLifecycleState::Removed);
901 }
902
903 #[test]
904 fn test_cpu_hotplug_cannot_remove_boot() {
905 let mut hp = CpuHotplug::new(8, 0);
906 assert!(hp.remove_cpu(0).is_err());
907 }
908
909 #[test]
910 fn test_cpu_hotplug_re_add() {
911 let mut hp = CpuHotplug::new(8, 0);
912 hp.add_cpu(3).unwrap();
913 hp.remove_cpu(3).unwrap();
914 hp.add_cpu(3).unwrap(); assert!(hp.is_online(3));
916 }
917
918 #[test]
919 fn test_cpu_hotplug_online_ids() {
920 let mut hp = CpuHotplug::new(8, 0);
921 hp.add_cpu(2).unwrap();
922 hp.add_cpu(5).unwrap();
923 let ids = hp.online_cpu_ids();
924 assert!(ids.contains(&0));
925 assert!(ids.contains(&2));
926 assert!(ids.contains(&5));
927 assert_eq!(ids.len(), 3);
928 }
929
930 #[test]
931 fn test_memory_dimm() {
932 let dimm = MemoryDimm::new(0, 1024, 0x1_0000_0000);
933 assert_eq!(dimm.size_bytes(), 1024 * 1024 * 1024);
934 assert_eq!(dimm.end_addr(), 0x1_0000_0000 + 1024 * 1024 * 1024);
935 }
936
937 #[test]
938 fn test_memory_hotplug_add() {
939 let mut hp = MemoryHotplug::new(8, 0x1_0000_0000);
940 let slot = hp.add_dimm(512).unwrap();
941 assert_eq!(slot, 0);
942 assert_eq!(hp.total_online_mb(), 512);
943 assert_eq!(hp.dimm_count(), 1);
944 }
945
946 #[test]
947 fn test_memory_hotplug_remove() {
948 let mut hp = MemoryHotplug::new(8, 0x1_0000_0000);
949 hp.add_dimm(512).unwrap();
950 hp.remove_dimm(0).unwrap();
951 assert_eq!(hp.total_online_mb(), 0);
952 assert_eq!(hp.online_dimm_count(), 0);
953 }
954
955 #[test]
956 fn test_memory_hotplug_remove_offline() {
957 let mut hp = MemoryHotplug::new(8, 0x1_0000_0000);
958 hp.add_dimm(256).unwrap();
959 hp.remove_dimm(0).unwrap();
960 assert!(hp.remove_dimm(0).is_err()); }
962
963 #[test]
964 fn test_shpc_controller_power_on() {
965 let mut shpc = ShpcController::new(4, 0xFE00_0000);
966 assert_eq!(shpc.slot_count(), 4);
967 shpc.slot_power_on(0, 0x0018).unwrap();
968 assert_eq!(shpc.occupied_count(), 1);
969 let slot = shpc.slot(0).unwrap();
970 assert!(slot.occupied);
971 assert!(slot.powered);
972 }
973
974 #[test]
975 fn test_shpc_controller_power_off() {
976 let mut shpc = ShpcController::new(4, 0);
977 shpc.slot_power_on(0, 0x0018).unwrap();
978 shpc.slot_power_off(0).unwrap();
979 assert_eq!(shpc.occupied_count(), 0);
980 }
981
982 #[test]
983 fn test_shpc_surprise_removal() {
984 let mut shpc = ShpcController::new(4, 0);
985 shpc.slots[0].surprise_removal_supported = true;
986 shpc.slot_power_on(0, 0x0018).unwrap();
987 let event = shpc.surprise_removal(0).unwrap();
988 assert_eq!(event.event_type, HotplugType::PciDevice);
989 assert!(!event.is_add);
990 assert_eq!(shpc.occupied_count(), 0);
991 }
992
993 #[test]
994 fn test_shpc_surprise_not_supported() {
995 let mut shpc = ShpcController::new(4, 0);
996 shpc.slot_power_on(0, 0x0018).unwrap();
997 assert!(shpc.surprise_removal(0).is_err());
998 }
999
1000 #[test]
1001 fn test_pcie_native_presence_change_insert() {
1002 let mut hp = PcieNativeHotplug::new(0);
1003 assert!(!hp.is_present());
1004 let event = hp.handle_slot_event(SlotEvent::PresenceChanged).unwrap();
1005 assert_eq!(event.event_type, HotplugType::PciDevice);
1006 assert!(event.is_add);
1007 assert!(hp.is_present());
1008 }
1009
1010 #[test]
1011 fn test_pcie_native_presence_change_remove() {
1012 let mut hp = PcieNativeHotplug::new(0);
1013 hp.handle_slot_event(SlotEvent::PresenceChanged);
1015 hp.power_on();
1016 let event = hp.handle_slot_event(SlotEvent::PresenceChanged).unwrap();
1018 assert!(!event.is_add);
1019 assert!(!hp.is_present());
1020 assert!(!hp.is_powered());
1021 }
1022
1023 #[test]
1024 fn test_pcie_native_power_fault() {
1025 let mut hp = PcieNativeHotplug::new(0);
1026 hp.power_on();
1027 assert!(hp.is_powered());
1028 let event = hp.handle_slot_event(SlotEvent::PowerFault);
1029 assert!(event.is_none()); assert!(!hp.is_powered());
1031 assert!(hp.power_fault);
1032 }
1033
1034 #[test]
1035 fn test_pcie_native_attention_button_empty() {
1036 let mut hp = PcieNativeHotplug::new(0);
1037 let event = hp.handle_slot_event(SlotEvent::AttentionButton);
1038 assert!(event.is_none()); }
1040
1041 #[test]
1042 fn test_pcie_native_attention_button_occupied() {
1043 let mut hp = PcieNativeHotplug::new(0);
1044 hp.handle_slot_event(SlotEvent::PresenceChanged); let event = hp.handle_slot_event(SlotEvent::AttentionButton);
1046 assert!(event.is_some());
1047 assert!(!event.unwrap().is_add); }
1049}