vmm/arch/x86_64/
interrupts.rs1use kvm_bindings::kvm_lapic_state;
9use kvm_ioctls::VcpuFd;
10use zerocopy::IntoBytes;
11
12use crate::utils::byte_order;
13
14#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
16pub enum InterruptError {
17 GetLapic(kvm_ioctls::Error),
19 SetLapic(kvm_ioctls::Error),
21}
22
23const APIC_LVT0: usize = 0x350;
25const APIC_LVT1: usize = 0x360;
26const APIC_MODE_NMI: u32 = 0x4;
27const APIC_MODE_EXTINT: u32 = 0x7;
28
29fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 {
30 let range = reg_offset..reg_offset + 4;
31 let reg = klapic.regs.get(range).expect("get_klapic_reg range");
32 byte_order::read_le_u32(reg.as_bytes())
33}
34
35fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
36 let range = reg_offset..reg_offset + 4;
37 let reg = klapic.regs.get_mut(range).expect("set_klapic_reg range");
38 byte_order::write_le_u32(reg.as_mut_bytes(), value);
39}
40
41fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
42 ((reg) & !0x700) | ((mode) << 8)
43}
44
45pub fn set_lint(vcpu: &VcpuFd) -> Result<(), InterruptError> {
50 let mut klapic = vcpu.get_lapic().map_err(InterruptError::GetLapic)?;
51
52 let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0);
53 set_klapic_reg(
54 &mut klapic,
55 APIC_LVT0,
56 set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT),
57 );
58 let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1);
59 set_klapic_reg(
60 &mut klapic,
61 APIC_LVT1,
62 set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI),
63 );
64
65 vcpu.set_lapic(&klapic).map_err(InterruptError::SetLapic)
66}
67
68#[cfg(test)]
69mod tests {
70 use kvm_ioctls::Kvm;
71
72 use super::*;
73
74 const KVM_APIC_REG_SIZE: usize = 0x400;
75
76 #[test]
77 fn test_set_and_get_klapic_reg() {
78 let reg_offset = 0x340;
79 let mut klapic = kvm_lapic_state::default();
80 set_klapic_reg(&mut klapic, reg_offset, 3);
81 let value = get_klapic_reg(&klapic, reg_offset);
82 assert_eq!(value, 3);
83 }
84
85 #[test]
86 fn test_set_and_get_klapic_reg_overflow() {
87 let reg_offset = 0x340;
88 let mut klapic = kvm_lapic_state::default();
89 set_klapic_reg(
90 &mut klapic,
91 reg_offset,
92 u32::try_from(i32::MAX).unwrap() + 1u32,
93 );
94 let value = get_klapic_reg(&klapic, reg_offset);
95 assert_eq!(value, u32::try_from(i32::MAX).unwrap() + 1u32);
96 }
97
98 #[test]
99 #[should_panic]
100 fn test_set_and_get_klapic_out_of_bounds() {
101 let reg_offset = KVM_APIC_REG_SIZE + 10;
102 let mut klapic = kvm_lapic_state::default();
103 set_klapic_reg(&mut klapic, reg_offset, 3);
104 }
105
106 #[test]
107 fn test_apic_delivery_mode() {
108 let mut v: Vec<u32> = (0..20)
109 .map(|_| vmm_sys_util::rand::xor_pseudo_rng_u32())
110 .collect();
111
112 v.iter_mut()
113 .for_each(|x| *x = set_apic_delivery_mode(*x, 2));
114 let after: Vec<u32> = v.iter().map(|x| ((*x & !0x700) | ((2) << 8))).collect();
115 assert_eq!(v, after);
116 }
117
118 #[test]
119 fn test_setlint() {
120 let kvm = Kvm::new().unwrap();
121 assert!(kvm.check_extension(kvm_ioctls::Cap::Irqchip));
122 let vm = kvm.create_vm().unwrap();
123 vm.create_irq_chip().unwrap();
125 let vcpu = vm.create_vcpu(0).unwrap();
126 let klapic_before: kvm_lapic_state = vcpu.get_lapic().unwrap();
127
128 let lint0 = get_klapic_reg(&klapic_before, APIC_LVT0);
130 let lint1 = get_klapic_reg(&klapic_before, APIC_LVT1);
131 let lint0_mode_expected = set_apic_delivery_mode(lint0, APIC_MODE_EXTINT);
132 let lint1_mode_expected = set_apic_delivery_mode(lint1, APIC_MODE_NMI);
133
134 set_lint(&vcpu).unwrap();
135
136 let klapic_actual: kvm_lapic_state = vcpu.get_lapic().unwrap();
138 let lint0_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT0);
139 let lint1_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT1);
140 assert_eq!(lint0_mode_expected, lint0_mode_actual);
141 assert_eq!(lint1_mode_expected, lint1_mode_actual);
142 }
143
144 #[test]
145 fn test_setlint_fails() {
146 let kvm = Kvm::new().unwrap();
147 let vm = kvm.create_vm().unwrap();
148 let vcpu = vm.create_vcpu(0).unwrap();
149 set_lint(&vcpu).unwrap_err();
152 }
153}