1#[cfg(feature = "alloc")]
14extern crate alloc;
15
16use core::sync::atomic::{AtomicU64, Ordering};
17
18pub mod lapic;
19pub mod migration;
20pub mod nested;
21pub mod passthrough;
22pub mod smp;
23pub mod snapshot;
24
25pub use self::{lapic::*, migration::*, nested::*, passthrough::*, smp::*, snapshot::*};
27
28pub(crate) const MAX_VCPUS: usize = 16;
34
35pub(crate) const _MAX_VMS: usize = 64;
37
38pub(crate) const LAPIC_BASE_ADDR: u64 = 0xFEE0_0000;
40
41pub(crate) const LAPIC_REGION_SIZE: u64 = 0x1000;
43
44pub(crate) const PAGE_SIZE: u64 = 4096;
46
47pub(crate) const _VMCS_FIELD_GROUP_COUNT: usize = 7;
49
50pub(crate) const PRECOPY_BATCH_SIZE: u64 = 256;
52
53pub(crate) const BITS_PER_U64: u64 = 64;
55
56pub(crate) const SNAPSHOT_MAGIC: u32 = 0x564D_534E; pub(crate) const SNAPSHOT_VERSION: u32 = 1;
61
62pub(crate) const _MAX_PASSTHROUGH_DEVICES: usize = 32;
64
65pub(crate) const MAX_MSIX_VECTORS: usize = 64;
67
68#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
74#[repr(C)]
75pub struct GuestRegisters {
76 pub rax: u64,
77 pub rbx: u64,
78 pub rcx: u64,
79 pub rdx: u64,
80 pub rsi: u64,
81 pub rdi: u64,
82 pub rbp: u64,
83 pub rsp: u64,
84 pub r8: u64,
85 pub r9: u64,
86 pub r10: u64,
87 pub r11: u64,
88 pub r12: u64,
89 pub r13: u64,
90 pub r14: u64,
91 pub r15: u64,
92 pub rip: u64,
93 pub rflags: u64,
94}
95
96#[derive(Debug, Default)]
102pub struct HypervisorStats {
103 pub total_vm_entries: AtomicU64,
104 pub total_vm_exits: AtomicU64,
105 pub total_ipis_sent: AtomicU64,
106 pub total_lapic_timer_fires: AtomicU64,
107 pub total_ept_violations: AtomicU64,
108 pub total_snapshots_taken: AtomicU64,
109 pub total_migrations_started: AtomicU64,
110 pub total_migrations_completed: AtomicU64,
111}
112
113impl HypervisorStats {
114 pub const fn new() -> Self {
115 Self {
116 total_vm_entries: AtomicU64::new(0),
117 total_vm_exits: AtomicU64::new(0),
118 total_ipis_sent: AtomicU64::new(0),
119 total_lapic_timer_fires: AtomicU64::new(0),
120 total_ept_violations: AtomicU64::new(0),
121 total_snapshots_taken: AtomicU64::new(0),
122 total_migrations_started: AtomicU64::new(0),
123 total_migrations_completed: AtomicU64::new(0),
124 }
125 }
126
127 pub fn record_vm_entry(&self) {
128 self.total_vm_entries.fetch_add(1, Ordering::Relaxed);
129 }
130
131 pub fn record_vm_exit(&self) {
132 self.total_vm_exits.fetch_add(1, Ordering::Relaxed);
133 }
134
135 pub fn record_ipi(&self) {
136 self.total_ipis_sent.fetch_add(1, Ordering::Relaxed);
137 }
138
139 pub fn record_timer_fire(&self) {
140 self.total_lapic_timer_fires.fetch_add(1, Ordering::Relaxed);
141 }
142
143 pub fn record_ept_violation(&self) {
144 self.total_ept_violations.fetch_add(1, Ordering::Relaxed);
145 }
146
147 pub fn record_snapshot(&self) {
148 self.total_snapshots_taken.fetch_add(1, Ordering::Relaxed);
149 }
150
151 pub fn record_migration_start(&self) {
152 self.total_migrations_started
153 .fetch_add(1, Ordering::Relaxed);
154 }
155
156 pub fn record_migration_complete(&self) {
157 self.total_migrations_completed
158 .fetch_add(1, Ordering::Relaxed);
159 }
160}
161
162static HYPERVISOR_STATS: HypervisorStats = HypervisorStats::new();
163
164pub fn get_stats() -> &'static HypervisorStats {
166 &HYPERVISOR_STATS
167}
168
169#[cfg(test)]
174mod tests {
175 #[allow(unused_imports)]
176 use alloc::vec;
177
178 use super::*;
179
180 #[test]
183 fn test_shadow_vmcs_read_write() {
184 use crate::virt::vmx::VmcsFields;
185 let mut shadow = ShadowVmcs::new();
186 shadow.write_field(VmcsFields::GUEST_RIP, 0x1000);
187 assert_eq!(shadow.read_field(VmcsFields::GUEST_RIP), Some(0x1000));
188 assert_eq!(shadow.read_field(VmcsFields::GUEST_RSP), None);
189 assert_eq!(shadow.field_count(), 1);
190 }
191
192 #[test]
193 fn test_shadow_vmcs_activate_deactivate() {
194 let mut shadow = ShadowVmcs::new();
195 assert!(!shadow.is_active());
196 shadow.activate(0x2000);
197 assert!(shadow.is_active());
198 assert_eq!(shadow.link_pointer(), 0x2000);
199 shadow.deactivate();
200 assert!(!shadow.is_active());
201 assert_eq!(shadow.link_pointer(), 0xFFFF_FFFF_FFFF_FFFF);
202 }
203
204 #[test]
205 fn test_shadow_vmcs_clear() {
206 let mut shadow = ShadowVmcs::new();
207 shadow.write_field(0x100, 42);
208 shadow.write_field(0x200, 84);
209 shadow.activate(0x3000);
210 shadow.clear();
211 assert_eq!(shadow.field_count(), 0);
212 assert!(!shadow.is_active());
213 }
214
215 #[test]
216 fn test_nested_controller_l1_vmwrite_passthrough() {
217 use crate::virt::vmx::VmcsFields;
218 let mut ctrl = NestedVirtController::new();
219 assert!(ctrl
220 .handle_l1_vmwrite(VmcsFields::GUEST_CR0, 0x80000011)
221 .is_ok());
222 assert_eq!(ctrl.handle_l1_vmread(VmcsFields::GUEST_CR0), Ok(0x80000011));
223 }
224
225 #[test]
226 fn test_nested_controller_l1_vmwrite_hidden_field() {
227 use crate::virt::vmx::VmcsFields;
228 let ctrl = NestedVirtController::new();
229 assert_eq!(
230 ctrl.handle_l1_vmread(VmcsFields::HOST_RIP),
231 Err(crate::virt::VmError::VmcsFieldError)
232 );
233 }
234
235 #[test]
236 fn test_nested_controller_l1_vmwrite_readonly_field() {
237 use crate::virt::vmx::VmcsFields;
238 let mut ctrl = NestedVirtController::new();
239 assert_eq!(
240 ctrl.handle_l1_vmwrite(VmcsFields::VM_EXIT_REASON, 42),
241 Err(crate::virt::VmError::VmcsFieldError)
242 );
243 }
244
245 #[test]
246 fn test_nested_enter_exit_l2() {
247 use crate::virt::vmx::VmcsFields;
248 let mut ctrl = NestedVirtController::new();
249 ctrl.enable_nested_vmx();
250 ctrl.handle_l1_vmwrite(VmcsFields::GUEST_RIP, 0x5000)
251 .unwrap();
252
253 let l1_regs = GuestRegisters {
254 rax: 1,
255 rip: 0x4000,
256 ..Default::default()
257 };
258 assert!(ctrl.enter_l2(&l1_regs).is_ok());
259 assert_eq!(ctrl.nesting_level(), NestingLevel::L2);
260
261 let l1_restored = ctrl.exit_l2(NestedExitReason::Vmcall).unwrap();
262 assert_eq!(l1_restored.rax, 1);
263 assert_eq!(l1_restored.rip, 0x4000);
264 assert_eq!(ctrl.nesting_level(), NestingLevel::L1);
265 }
266
267 #[test]
268 fn test_nested_exit_l2_stores_reason() {
269 use crate::virt::vmx::VmcsFields;
270 let mut ctrl = NestedVirtController::new();
271 ctrl.enable_nested_vmx();
272 ctrl.handle_l1_vmwrite(VmcsFields::GUEST_RIP, 0x1000)
273 .unwrap();
274 ctrl.enter_l2(&GuestRegisters::default()).unwrap();
275 ctrl.exit_l2(NestedExitReason::EptViolation).unwrap();
276 assert_eq!(
278 ctrl.shadow_vmcs.read_field(VmcsFields::VM_EXIT_REASON),
279 Some(48)
280 );
281 }
282
283 #[test]
284 fn test_nested_should_forward() {
285 let ctrl = NestedVirtController::new();
286 assert!(ctrl.should_forward_to_l1(NestedExitReason::VmxInstruction));
287 assert!(ctrl.should_forward_to_l1(NestedExitReason::Vmcall));
288 assert!(!ctrl.should_forward_to_l1(NestedExitReason::ExternalInterrupt));
289 }
290
291 #[test]
294 fn test_passthrough_device_assign_unassign() {
295 let mut dev = PassthroughDevice::new(
296 PassthroughDeviceType::VirtioNet,
297 0x1AF4,
298 0x1041,
299 0x0000_0800,
300 );
301 assert!(!dev.is_assigned());
302 assert!(dev.assign_to_vm(1).is_ok());
303 assert!(dev.is_assigned());
304 assert_eq!(dev.owner_vm_id, 1);
305 assert_eq!(dev.assign_to_vm(2), Err(crate::virt::VmError::DeviceError));
307 dev.unassign();
308 assert!(!dev.is_assigned());
309 }
310
311 #[test]
312 fn test_passthrough_msix_remap() {
313 let mut dev = PassthroughDevice::new(
314 PassthroughDeviceType::VirtioBlk,
315 0x1AF4,
316 0x1042,
317 0x0000_1000,
318 );
319 dev.add_msix_remap(32, 64, 0);
320 dev.add_msix_remap(33, 65, 1);
321 assert_eq!(dev.msix_remap_count(), 2);
322 assert_eq!(dev.remap_interrupt(32), Some((64, 0)));
323 assert_eq!(dev.remap_interrupt(33), Some((65, 1)));
324 assert_eq!(dev.remap_interrupt(99), None);
325 }
326
327 #[test]
328 fn test_passthrough_mmio_region() {
329 let mut dev = PassthroughDevice::new(
330 PassthroughDeviceType::VirtioGpu,
331 0x1AF4,
332 0x1050,
333 0x0000_1800,
334 );
335 dev.add_mmio_region(0xFE00_0000, 0xC000_0000, 0x1000);
336 assert_eq!(dev.mmio_region_count(), 1);
337 assert!(dev.mmio_regions[0].mapped);
338 }
339
340 #[test]
341 fn test_pci_config_passthrough() {
342 let mut pci = PciConfigPassthrough::new(0x1AF4, 0x1041, 0);
343 assert_eq!(pci.read_config(0), 0xF4); assert_eq!(pci.read_config(1), 0x1A); assert_eq!(pci.read_config(2), 0x41); assert_eq!(pci.read_config(3), 0x10); pci.write_config(4, 0x07);
349 assert_eq!(pci.read_config(4), 0x07);
350 pci.write_config(0, 0xFF);
352 assert_eq!(pci.read_config(0), 0xF4); }
354
355 #[test]
358 fn test_dirty_page_bitmap() {
359 let mut bm = DirtyPageBitmap::new(256);
360 assert_eq!(bm.dirty_count(), 0);
361 assert_eq!(bm.total_pages(), 256);
362
363 bm.set_dirty(0);
364 bm.set_dirty(63);
365 bm.set_dirty(64);
366 bm.set_dirty(255);
367 assert_eq!(bm.dirty_count(), 4);
368 assert!(bm.is_dirty(0));
369 assert!(bm.is_dirty(63));
370 assert!(bm.is_dirty(64));
371 assert!(bm.is_dirty(255));
372 assert!(!bm.is_dirty(1));
373
374 bm.clear_dirty(63);
375 assert_eq!(bm.dirty_count(), 3);
376 assert!(!bm.is_dirty(63));
377 }
378
379 #[test]
380 fn test_dirty_page_bitmap_idempotent() {
381 let mut bm = DirtyPageBitmap::new(128);
382 bm.set_dirty(10);
383 bm.set_dirty(10); assert_eq!(bm.dirty_count(), 1);
385 bm.clear_dirty(10);
386 bm.clear_dirty(10); assert_eq!(bm.dirty_count(), 0);
388 }
389
390 #[test]
391 fn test_dirty_page_iterator() {
392 let mut bm = DirtyPageBitmap::new(200);
393 bm.set_dirty(5);
394 bm.set_dirty(100);
395 bm.set_dirty(199);
396 let pages: Vec<u64> = bm.dirty_pages().collect();
397 assert_eq!(pages, vec![5, 100, 199]);
398 }
399
400 #[test]
401 fn test_dirty_page_clear_all() {
402 let mut bm = DirtyPageBitmap::new(128);
403 bm.set_dirty(0);
404 bm.set_dirty(50);
405 bm.set_dirty(127);
406 let old_count = bm.clear_all();
407 assert_eq!(old_count, 3);
408 assert_eq!(bm.dirty_count(), 0);
409 }
410
411 #[test]
412 fn test_migration_progress_bandwidth() {
413 let mut progress = MigrationProgress::default();
414 progress.total_bytes = 1_000_000;
415 progress.update_bandwidth(500_000, 100); assert_eq!(progress.bandwidth_bytes_per_ms, 5000);
417 assert_eq!(progress.transferred_bytes, 500_000);
418 progress.estimate_remaining();
419 assert_eq!(progress.estimated_remaining_ms, 100); }
421
422 #[test]
423 fn test_migration_progress_completion() {
424 let mut progress = MigrationProgress::default();
425 progress.total_bytes = 1000;
426 progress.transferred_bytes = 750;
427 assert_eq!(progress.completion_percent(), 75);
428 }
429
430 #[test]
431 fn test_migration_convergence() {
432 let mut progress = MigrationProgress::default();
433 progress.previous_dirty_pages = 1000;
434 progress.current_dirty_pages = 700;
435 assert!(progress.has_converged(20));
437 progress.current_dirty_pages = 900;
438 assert!(!progress.has_converged(20));
440 }
441
442 #[test]
443 fn test_migration_state_machine() {
444 let mut ctrl = MigrationController::new(1);
445 assert_eq!(ctrl.state(), MigrationState::Idle);
446
447 ctrl.begin_setup(100).unwrap();
448 assert_eq!(ctrl.state(), MigrationState::Setup);
449
450 ctrl.begin_precopy().unwrap();
451 assert_eq!(ctrl.state(), MigrationState::PreCopy);
452
453 let pages = ctrl.precopy_iteration().unwrap();
454 assert!(!pages.is_empty());
455 }
456
457 #[test]
458 fn test_serialized_vmcs() {
459 use crate::virt::vmx::VmcsFields;
460 let mut vmcs = SerializedVmcs::new();
461 vmcs.add_field(VmcsFields::GUEST_RIP, 0x1000);
462 vmcs.add_field(VmcsFields::GUEST_RSP, 0x7FF0);
463 assert_eq!(vmcs.field_count(), 2);
464 assert_eq!(vmcs.find_field(VmcsFields::GUEST_RIP), Some(0x1000));
465 assert_eq!(vmcs.find_field(VmcsFields::GUEST_CR0), None);
466 }
467
468 #[test]
471 fn test_smp_vm_creation() {
472 let vm = SmpVm::new(1, 4).unwrap();
473 assert_eq!(vm.vcpu_count(), 4);
474 assert!(vm.vcpu(0).unwrap().is_bsp);
475 assert!(!vm.vcpu(1).unwrap().is_bsp);
476 assert_eq!(vm.vcpu(1).unwrap().state, VcpuState::WaitingForSipi);
477 }
478
479 #[test]
480 fn test_smp_vm_max_vcpu_limit() {
481 assert!(SmpVm::new(1, 0).is_err());
482 assert!(SmpVm::new(1, MAX_VCPUS + 1).is_err());
483 assert!(SmpVm::new(1, MAX_VCPUS).is_ok());
484 }
485
486 #[test]
487 fn test_vcpu_sipi_startup() {
488 let mut vm = SmpVm::new(1, 2).unwrap();
489 assert_eq!(vm.vcpu(1).unwrap().state, VcpuState::WaitingForSipi);
491
492 vm.startup_ap(1, 0x10).unwrap(); let ap = vm.vcpu(1).unwrap();
496 assert_eq!(ap.state, VcpuState::Running);
497 assert_eq!(ap.registers.rip, 0x10000);
498 assert_eq!(ap.sipi_vector, 0x10);
499 }
500
501 #[test]
502 fn test_vcpu_ipi_delivery() {
503 let mut vm = SmpVm::new(1, 4).unwrap();
504 for i in 1..4 {
506 vm.startup_ap(i, 0x20).unwrap();
507 }
508
509 vm.send_ipi(0, 2, IpiDeliveryMode::Fixed, 0x30).unwrap();
511 assert_eq!(vm.vcpu(2).unwrap().pending_ipi_count(), 1);
512 let ipi = vm.vcpu_mut(2).unwrap().pop_ipi().unwrap();
513 assert_eq!(ipi.vector, 0x30);
514 assert_eq!(ipi.source, 0);
515 }
516
517 #[test]
518 fn test_vcpu_broadcast_ipi() {
519 let mut vm = SmpVm::new(1, 4).unwrap();
520 for i in 1..4 {
521 vm.startup_ap(i, 0x20).unwrap();
522 }
523
524 vm.send_ipi(0, 0xFF, IpiDeliveryMode::Fixed, 0x40).unwrap();
526 assert_eq!(vm.vcpu(0).unwrap().pending_ipi_count(), 0);
528 assert_eq!(vm.vcpu(1).unwrap().pending_ipi_count(), 1);
529 assert_eq!(vm.vcpu(2).unwrap().pending_ipi_count(), 1);
530 assert_eq!(vm.vcpu(3).unwrap().pending_ipi_count(), 1);
531 }
532
533 #[test]
534 fn test_vcpu_halt_and_nmi_wake() {
535 let mut vcpu = VirtualCpu::new(0, true);
536 vcpu.state = VcpuState::Running;
537 vcpu.halt();
538 assert_eq!(vcpu.state, VcpuState::Halted);
539
540 vcpu.deliver_ipi(IpiMessage {
541 source: 1,
542 destination: 0,
543 delivery_mode: IpiDeliveryMode::Nmi,
544 vector: 0,
545 level: true,
546 trigger_level: false,
547 });
548 assert_eq!(vcpu.state, VcpuState::Running);
549 }
550
551 #[test]
552 fn test_vcpu_pause_resume() {
553 let mut vm = SmpVm::new(1, 2).unwrap();
554 vm.vcpu_mut(0).unwrap().state = VcpuState::Running;
555 vm.startup_ap(1, 0x10).unwrap();
556 assert_eq!(vm.running_vcpu_count(), 2);
557
558 vm.pause_all();
559 assert_eq!(vm.vcpu(0).unwrap().state, VcpuState::Paused);
560 assert_eq!(vm.vcpu(1).unwrap().state, VcpuState::Paused);
562
563 vm.resume_all();
564 assert_eq!(vm.running_vcpu_count(), 2);
565 }
566
567 #[test]
570 fn test_lapic_register_rw() {
571 let mut lapic = VirtualLapic::new(0);
572 lapic.write_register(LapicRegs::TPR, 0x20);
574 assert_eq!(lapic.read_register(LapicRegs::TPR), 0x20);
575 assert_eq!(lapic.read_register(LapicRegs::VERSION), 0x0005_0014);
577 assert_eq!(lapic.read_register(LapicRegs::ID), 0);
579 }
580
581 #[test]
582 fn test_lapic_enable_via_svr() {
583 let mut lapic = VirtualLapic::new(0);
584 assert!(!lapic.is_enabled());
585 lapic.write_register(LapicRegs::SVR, 0x1FF); assert!(lapic.is_enabled());
587 }
588
589 #[test]
590 fn test_lapic_accept_and_deliver_interrupt() {
591 let mut lapic = VirtualLapic::new(0);
592 lapic.write_register(LapicRegs::SVR, 0x1FF); lapic.accept_interrupt(0x30);
594 assert!(lapic.irr[1] & (1 << 16) != 0); let vec = lapic.deliver_pending_interrupt();
597 assert_eq!(vec, Some(0x30));
598 assert!(lapic.isr[1] & (1 << 16) != 0);
600 }
601
602 #[test]
603 fn test_lapic_eoi() {
604 let mut lapic = VirtualLapic::new(0);
605 lapic.write_register(LapicRegs::SVR, 0x1FF);
606 lapic.accept_interrupt(0x30);
607 lapic.deliver_pending_interrupt();
608 lapic.write_register(LapicRegs::EOI, 0);
610 assert_eq!(lapic.isr[1] & (1 << 16), 0);
612 }
613
614 #[test]
615 fn test_lapic_timer_oneshot() {
616 let mut lapic = VirtualLapic::new(0);
617 lapic.lvt_timer = LvtEntry { raw: 0x0000_0020 }; lapic.write_register(LapicRegs::TIMER_INITIAL_COUNT, 100);
619 assert!(!lapic.tick_timer(50));
620 assert_eq!(lapic.timer_current_count, 50);
621 assert!(lapic.tick_timer(60)); assert_eq!(lapic.timer_current_count, 0);
623 assert!(!lapic.tick_timer(10)); }
625
626 #[test]
627 fn test_lapic_timer_periodic() {
628 let mut lapic = VirtualLapic::new(0);
629 lapic.lvt_timer = LvtEntry { raw: 0x0002_0020 }; lapic.write_register(LapicRegs::TIMER_INITIAL_COUNT, 100);
632 assert!(lapic.tick_timer(110)); assert_eq!(lapic.timer_current_count, 100); }
635
636 #[test]
637 fn test_lapic_timer_divide_value() {
638 let mut lapic = VirtualLapic::new(0);
639 lapic.timer_divide_config = 0b0000; assert_eq!(lapic.timer_divide_value(), 2);
641 lapic.timer_divide_config = 0b0011; assert_eq!(lapic.timer_divide_value(), 16);
643 lapic.timer_divide_config = 0b1011; assert_eq!(lapic.timer_divide_value(), 1);
645 }
646
647 #[test]
648 fn test_lapic_extract_ipi() {
649 let mut lapic = VirtualLapic::new(0);
650 lapic.icr_low = 0x0000_4030; lapic.icr_low = 0x0000_0530; lapic.icr_high = 0x0200_0000; let ipi = lapic.extract_ipi();
655 assert_eq!(ipi.vector, 0x30);
656 assert_eq!(ipi.destination, 2);
657 assert_eq!(ipi.delivery_mode, IpiDeliveryMode::Init);
658 }
659
660 #[test]
661 fn test_lapic_priority() {
662 let mut lapic = VirtualLapic::new(0);
663 lapic.write_register(LapicRegs::SVR, 0x1FF);
664 lapic.write_register(LapicRegs::TPR, 0x40); lapic.accept_interrupt(0x30);
669 assert_eq!(lapic.deliver_pending_interrupt(), None);
670
671 lapic.accept_interrupt(0x50);
673 assert_eq!(lapic.deliver_pending_interrupt(), Some(0x50));
674 }
675
676 #[test]
679 fn test_snapshot_header_validation() {
680 let mut header = SnapshotHeader::default();
681 header.vm_id = 42;
682 header.vcpu_count = 4;
683 header.memory_pages = 1024;
684 header.checksum = header.compute_checksum();
685 assert!(header.is_valid());
686
687 header.magic = 0;
689 assert!(!header.is_valid());
690 }
691
692 #[test]
693 fn test_snapshot_creation_and_finalize() {
694 let mut snap = VmSnapshot::new(1, 2, 1024, 123456);
695 snap.add_register_state(
696 0,
697 GuestRegisters {
698 rip: 0x1000,
699 ..Default::default()
700 },
701 );
702 snap.add_register_state(
703 1,
704 GuestRegisters {
705 rip: 0x2000,
706 ..Default::default()
707 },
708 );
709 snap.add_memory_page(0);
710 snap.add_memory_page(100);
711 snap.finalize();
712
713 assert!(snap.validate());
714 assert_eq!(snap.vcpu_state_count(), 2);
715 assert_eq!(snap.memory_page_count(), 2);
716 assert!(snap.header.total_size > 0);
717 }
718
719 #[test]
720 fn test_snapshot_lapic_roundtrip() {
721 let mut lapic = VirtualLapic::new(3);
722 lapic.write_register(LapicRegs::SVR, 0x1FF);
723 lapic.write_register(LapicRegs::TPR, 0x50);
724 lapic.accept_interrupt(0x80);
725 lapic.timer_initial_count = 5000;
726 lapic.timer_current_count = 2500;
727
728 let snap = LapicSnapshot::from_lapic(&lapic);
729 let mut restored = VirtualLapic::new(0);
730 snap.restore_to_lapic(&mut restored);
731
732 assert_eq!(restored.id, 3);
733 assert_eq!(restored.tpr, 0x50);
734 assert!(restored.is_enabled());
735 assert_eq!(restored.timer_initial_count, 5000);
736 assert_eq!(restored.timer_current_count, 2500);
737 assert!(restored.irr[4] & 1 != 0); }
740
741 #[test]
742 fn test_snapshot_device_state() {
743 use alloc::string::String;
744 let mut snap = VmSnapshot::new(1, 1, 256, 0);
745 snap.add_device_state(String::from("uart0"), vec![0x60, 0x00, 0x00, 0x00]);
746 assert_eq!(snap.device_state_count(), 1);
747 assert_eq!(snap.device_states[0].name, "uart0");
748 assert_eq!(snap.device_states[0].data.len(), 4);
749 }
750
751 #[test]
754 fn test_hypervisor_stats() {
755 let stats = HypervisorStats::new();
756 stats.record_vm_entry();
757 stats.record_vm_entry();
758 stats.record_vm_exit();
759 stats.record_ipi();
760 assert_eq!(stats.total_vm_entries.load(Ordering::Relaxed), 2);
761 assert_eq!(stats.total_vm_exits.load(Ordering::Relaxed), 1);
762 assert_eq!(stats.total_ipis_sent.load(Ordering::Relaxed), 1);
763 }
764
765 #[test]
768 fn test_lvt_entry_fields() {
769 let entry = LvtEntry { raw: 0x0002_0030 }; assert_eq!(entry.vector(), 0x30);
771 assert!(!entry.is_masked());
772 assert_eq!(entry.timer_mode(), LapicTimerMode::Periodic);
773
774 let masked = LvtEntry { raw: 0x0001_0020 }; assert!(masked.is_masked());
776 }
777
778 #[test]
779 fn test_nesting_level_default() {
780 let level = NestingLevel::default();
781 assert_eq!(level, NestingLevel::L0);
782 }
783
784 #[test]
785 fn test_migration_state_default() {
786 let state = MigrationState::default();
787 assert_eq!(state, MigrationState::Idle);
788 }
789}