xref: /aosp_15_r20/external/crosvm/devices/tests/irqchip/kvm/x86_64.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #![cfg(target_arch = "x86_64")]
6 
7 use base::EventWaitResult;
8 use base::Tube;
9 use devices::Bus;
10 use devices::BusType;
11 use devices::CrosvmDeviceId;
12 use devices::DeviceId;
13 use devices::IrqChip;
14 use devices::IrqChipX86_64;
15 use devices::IrqEdgeEvent;
16 use devices::IrqEventSource;
17 use devices::IrqLevelEvent;
18 use devices::KvmKernelIrqChip;
19 use devices::KvmSplitIrqChip;
20 use devices::IOAPIC_BASE_ADDRESS;
21 use hypervisor::kvm::Kvm;
22 use hypervisor::kvm::KvmVm;
23 use hypervisor::IoapicRedirectionTableEntry;
24 use hypervisor::IrqRoute;
25 use hypervisor::IrqSource;
26 use hypervisor::PicSelect;
27 use hypervisor::PitRWMode;
28 use hypervisor::TriggerMode;
29 use hypervisor::Vm;
30 use hypervisor::VmX86_64;
31 use resources::AddressRange;
32 use resources::SystemAllocator;
33 use resources::SystemAllocatorConfig;
34 use vm_memory::GuestMemory;
35 
36 use crate::x86_64::test_get_ioapic;
37 use crate::x86_64::test_get_lapic;
38 use crate::x86_64::test_get_pic;
39 use crate::x86_64::test_get_pit;
40 use crate::x86_64::test_route_irq;
41 use crate::x86_64::test_set_ioapic;
42 use crate::x86_64::test_set_lapic;
43 use crate::x86_64::test_set_pic;
44 use crate::x86_64::test_set_pit;
45 
46 /// Helper function for setting up a KvmKernelIrqChip
get_kernel_chip() -> KvmKernelIrqChip47 fn get_kernel_chip() -> KvmKernelIrqChip {
48     let kvm = Kvm::new().expect("failed to instantiate Kvm");
49     let mem = GuestMemory::new(&[]).unwrap();
50     let vm = KvmVm::new(&kvm, mem, Default::default()).expect("failed tso instantiate vm");
51 
52     let mut chip = KvmKernelIrqChip::new(vm.try_clone().expect("failed to clone vm"), 1)
53         .expect("failed to instantiate KvmKernelIrqChip");
54 
55     let vcpu = vm.create_vcpu(0).expect("failed to instantiate vcpu");
56     chip.add_vcpu(0, vcpu.as_vcpu())
57         .expect("failed to add vcpu");
58 
59     chip
60 }
61 
62 /// Helper function for setting up a KvmSplitIrqChip
get_split_chip() -> KvmSplitIrqChip63 fn get_split_chip() -> KvmSplitIrqChip {
64     let kvm = Kvm::new().expect("failed to instantiate Kvm");
65     let mem = GuestMemory::new(&[]).unwrap();
66     let vm = KvmVm::new(&kvm, mem, Default::default()).expect("failed tso instantiate vm");
67 
68     let (_, device_tube) = Tube::pair().expect("failed to create irq tube");
69 
70     let mut chip = KvmSplitIrqChip::new(
71         vm.try_clone().expect("failed to clone vm"),
72         1,
73         device_tube,
74         None,
75     )
76     .expect("failed to instantiate KvmKernelIrqChip");
77 
78     let vcpu = vm.create_vcpu(0).expect("failed to instantiate vcpu");
79     chip.add_vcpu(0, vcpu.as_vcpu())
80         .expect("failed to add vcpu");
81     chip
82 }
83 
84 #[test]
kernel_irqchip_pit_uses_speaker_port()85 fn kernel_irqchip_pit_uses_speaker_port() {
86     let chip = get_kernel_chip();
87     assert!(!chip.pit_uses_speaker_port());
88 }
89 
90 #[test]
kernel_irqchip_get_pic()91 fn kernel_irqchip_get_pic() {
92     test_get_pic(get_kernel_chip());
93 }
94 
95 #[test]
kernel_irqchip_set_pic()96 fn kernel_irqchip_set_pic() {
97     test_set_pic(get_kernel_chip());
98 }
99 
100 #[test]
kernel_irqchip_get_ioapic()101 fn kernel_irqchip_get_ioapic() {
102     test_get_ioapic(get_kernel_chip());
103 }
104 
105 #[test]
kernel_irqchip_set_ioapic()106 fn kernel_irqchip_set_ioapic() {
107     test_set_ioapic(get_kernel_chip());
108 }
109 
110 #[test]
kernel_irqchip_get_pit()111 fn kernel_irqchip_get_pit() {
112     test_get_pit(get_kernel_chip());
113 }
114 
115 #[test]
kernel_irqchip_set_pit()116 fn kernel_irqchip_set_pit() {
117     test_set_pit(get_kernel_chip());
118 }
119 
120 #[test]
kernel_irqchip_get_lapic()121 fn kernel_irqchip_get_lapic() {
122     test_get_lapic(get_kernel_chip())
123 }
124 
125 #[test]
kernel_irqchip_set_lapic()126 fn kernel_irqchip_set_lapic() {
127     test_set_lapic(get_kernel_chip())
128 }
129 
130 #[test]
kernel_irqchip_route_irq()131 fn kernel_irqchip_route_irq() {
132     test_route_irq(get_kernel_chip());
133 }
134 
135 #[test]
split_irqchip_get_pic()136 fn split_irqchip_get_pic() {
137     test_get_pic(get_split_chip());
138 }
139 
140 #[test]
split_irqchip_set_pic()141 fn split_irqchip_set_pic() {
142     test_set_pic(get_split_chip());
143 }
144 
145 #[test]
split_irqchip_get_ioapic()146 fn split_irqchip_get_ioapic() {
147     test_get_ioapic(get_split_chip());
148 }
149 
150 #[test]
split_irqchip_set_ioapic()151 fn split_irqchip_set_ioapic() {
152     test_set_ioapic(get_split_chip());
153 }
154 
155 #[test]
split_irqchip_get_pit()156 fn split_irqchip_get_pit() {
157     test_get_pit(get_split_chip());
158 }
159 
160 #[test]
split_irqchip_set_pit()161 fn split_irqchip_set_pit() {
162     test_set_pit(get_split_chip());
163 }
164 
165 #[test]
split_irqchip_route_irq()166 fn split_irqchip_route_irq() {
167     test_route_irq(get_split_chip());
168 }
169 
170 #[test]
split_irqchip_pit_uses_speaker_port()171 fn split_irqchip_pit_uses_speaker_port() {
172     let chip = get_split_chip();
173     assert!(chip.pit_uses_speaker_port());
174 }
175 
176 #[test]
split_irqchip_routes_conflict()177 fn split_irqchip_routes_conflict() {
178     let mut chip = get_split_chip();
179     chip.route_irq(IrqRoute {
180         gsi: 5,
181         source: IrqSource::Msi {
182             address: 4276092928,
183             data: 0,
184         },
185     })
186     .expect("failed to set msi rout");
187     // this second route should replace the first
188     chip.route_irq(IrqRoute {
189         gsi: 5,
190         source: IrqSource::Msi {
191             address: 4276092928,
192             data: 32801,
193         },
194     })
195     .expect("failed to set msi rout");
196 }
197 
198 #[test]
irq_event_tokens()199 fn irq_event_tokens() {
200     let mut chip = get_split_chip();
201     let tokens = chip
202         .irq_event_tokens()
203         .expect("could not get irq_event_tokens");
204 
205     // there should be one token on a fresh split irqchip, for the pit
206     assert_eq!(tokens.len(), 1);
207     assert_eq!(tokens[0].1.device_name, "userspace PIT");
208 
209     // register another irq event
210     let evt = IrqEdgeEvent::new().expect("failed to create event");
211     let source = IrqEventSource {
212         device_id: CrosvmDeviceId::Cmos.into(),
213         queue_id: 0,
214         device_name: "test".into(),
215     };
216     chip.register_edge_irq_event(6, &evt, source)
217         .expect("failed to register irq event");
218 
219     let tokens = chip
220         .irq_event_tokens()
221         .expect("could not get irq_event_tokens");
222 
223     // now there should be two tokens
224     assert_eq!(tokens.len(), 2);
225     assert_eq!(tokens[0].1.device_name, "userspace PIT");
226     assert_eq!(
227         tokens[1].1.device_id,
228         DeviceId::PlatformDeviceId(CrosvmDeviceId::Cmos)
229     );
230     assert_eq!(tokens[1].2, *evt.get_trigger());
231 }
232 
233 #[test]
finalize_devices()234 fn finalize_devices() {
235     let mut chip = get_split_chip();
236 
237     let mmio_bus = Bus::new(BusType::Mmio);
238     let io_bus = Bus::new(BusType::Io);
239     let mut resources = SystemAllocator::new(
240         SystemAllocatorConfig {
241             io: Some(AddressRange {
242                 start: 0xc000,
243                 end: 0xffff,
244             }),
245             low_mmio: AddressRange {
246                 start: 0,
247                 end: 2047,
248             },
249             high_mmio: AddressRange {
250                 start: 0x1_0000_0000,
251                 end: 0x2_ffff_ffff,
252             },
253             platform_mmio: None,
254             first_irq: 5,
255         },
256         None,
257         &[],
258     )
259     .expect("failed to create SystemAllocator");
260 
261     // Set up a level-triggered interrupt line 1
262     let evt = IrqLevelEvent::new().expect("failed to create event");
263 
264     let source = IrqEventSource {
265         device_id: CrosvmDeviceId::Cmos.into(),
266         device_name: "test".into(),
267         queue_id: 0,
268     };
269     let evt_index = chip
270         .register_level_irq_event(1, &evt, source)
271         .expect("failed to register irq event")
272         .expect("register_irq_event should not return None");
273 
274     // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
275     chip.finalize_devices(&mut resources, &io_bus, &mmio_bus)
276         .expect("failed to finalize devices");
277 
278     // Should not be able to allocate an irq < 24 now
279     assert!(resources.allocate_irq().expect("failed to allocate irq") >= 24);
280 
281     // set PIT counter 2 to "SquareWaveGen"(aka 3) mode and "Both" access mode
282     io_bus.write(0x43, &[0b10110110]);
283 
284     let state = chip.get_pit().expect("failed to get pit state");
285     assert_eq!(state.channels[2].mode, 3);
286     assert_eq!(state.channels[2].rw_mode, PitRWMode::Both);
287 
288     // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
289     // ICW2 0x08: Interrupt vector base address 0x08.
290     // ICW3 0xff: Value written does not matter.
291     // ICW4 0x13: Special fully nested mode, auto EOI.
292     io_bus.write(0x20, &[0x11]);
293     io_bus.write(0x21, &[0x08]);
294     io_bus.write(0x21, &[0xff]);
295     io_bus.write(0x21, &[0x13]);
296 
297     let state = chip
298         .get_pic_state(PicSelect::Primary)
299         .expect("failed to get pic state");
300 
301     // auto eoi and special fully nested mode should be turned on
302     assert!(state.auto_eoi);
303     assert!(state.special_fully_nested_mode);
304 
305     // Need to write to the irq event before servicing it
306     evt.trigger().expect("failed to write to event");
307 
308     // if we assert irq line one, and then get the resulting interrupt, an auto-eoi should
309     // occur and cause the resample_event to be written to
310     chip.service_irq_event(evt_index)
311         .expect("failed to service irq");
312 
313     assert!(chip.interrupt_requested(0));
314     assert_eq!(
315         chip.get_external_interrupt(0)
316             .expect("failed to get external interrupt"),
317         // Vector is 9 because the interrupt vector base address is 0x08 and this is irq
318         // line 1 and 8+1 = 9
319         0x9
320     );
321 
322     // Clone resample event because read_timeout() needs a mutable reference.
323     let resample_evt = evt.get_resample().try_clone().unwrap();
324     assert_eq!(
325         resample_evt
326             .wait_timeout(std::time::Duration::from_secs(1))
327             .expect("failed to read_timeout"),
328         EventWaitResult::Signaled
329     );
330 
331     // setup a ioapic redirection table entry 14
332     let mut entry = IoapicRedirectionTableEntry::default();
333     entry.set_vector(44);
334 
335     let irq_14_offset = 0x10 + 14 * 2;
336     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_14_offset]);
337     mmio_bus.write(
338         IOAPIC_BASE_ADDRESS + 0x10,
339         &(entry.get(0, 32) as u32).to_ne_bytes(),
340     );
341     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_14_offset + 1]);
342     mmio_bus.write(
343         IOAPIC_BASE_ADDRESS + 0x10,
344         &(entry.get(32, 32) as u32).to_ne_bytes(),
345     );
346 
347     let state = chip.get_ioapic_state().expect("failed to get ioapic state");
348 
349     // redirection table entry 14 should have a vector of 44
350     assert_eq!(state.redirect_table[14].get_vector(), 44);
351 }
352 
353 #[test]
get_external_interrupt()354 fn get_external_interrupt() {
355     let mut chip = get_split_chip();
356     assert!(!chip.interrupt_requested(0));
357 
358     chip.service_irq(0, true).expect("failed to service irq");
359     assert!(chip.interrupt_requested(0));
360 
361     // Should return Some interrupt
362     assert_eq!(
363         chip.get_external_interrupt(0)
364             .expect("failed to get external interrupt"),
365         0,
366     );
367 
368     // interrupt is not requested twice
369     assert!(!chip.interrupt_requested(0));
370 }
371 
372 #[test]
broadcast_eoi()373 fn broadcast_eoi() {
374     let mut chip = get_split_chip();
375 
376     let mmio_bus = Bus::new(BusType::Mmio);
377     let io_bus = Bus::new(BusType::Io);
378     let mut resources = SystemAllocator::new(
379         SystemAllocatorConfig {
380             io: Some(AddressRange {
381                 start: 0xc000,
382                 end: 0xffff,
383             }),
384             low_mmio: AddressRange {
385                 start: 0,
386                 end: 2047,
387             },
388             high_mmio: AddressRange {
389                 start: 0x1_0000_0000,
390                 end: 0x2_ffff_ffff,
391             },
392             platform_mmio: None,
393             first_irq: 5,
394         },
395         None,
396         &[],
397     )
398     .expect("failed to create SystemAllocator");
399 
400     let source = IrqEventSource {
401         device_id: CrosvmDeviceId::Cmos.into(),
402         device_name: "test".into(),
403         queue_id: 0,
404     };
405     // setup an event and a resample event for irq line 1
406     let evt = IrqLevelEvent::new().expect("failed to create event");
407 
408     chip.register_level_irq_event(1, &evt, source)
409         .expect("failed to register_level_irq_event");
410 
411     // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
412     chip.finalize_devices(&mut resources, &io_bus, &mmio_bus)
413         .expect("failed to finalize devices");
414 
415     // setup a ioapic redirection table entry 1 with a vector of 123
416     let mut entry = IoapicRedirectionTableEntry::default();
417     entry.set_vector(123);
418     entry.set_trigger_mode(TriggerMode::Level);
419 
420     let irq_write_offset = 0x10 + 1 * 2;
421     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_write_offset]);
422     mmio_bus.write(
423         IOAPIC_BASE_ADDRESS + 0x10,
424         &(entry.get(0, 32) as u32).to_ne_bytes(),
425     );
426     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_write_offset + 1]);
427     mmio_bus.write(
428         IOAPIC_BASE_ADDRESS + 0x10,
429         &(entry.get(32, 32) as u32).to_ne_bytes(),
430     );
431 
432     // Assert line 1
433     chip.service_irq(1, true).expect("failed to service irq");
434 
435     // resample event should not be written to
436     assert_eq!(
437         evt.get_resample()
438             .wait_timeout(std::time::Duration::from_millis(10))
439             .expect("failed to read_timeout"),
440         EventWaitResult::TimedOut
441     );
442 
443     // irq line 1 should be asserted
444     let state = chip.get_ioapic_state().expect("failed to get ioapic state");
445     assert_eq!(state.current_interrupt_level_bitmap, 1 << 1);
446 
447     // Now broadcast an eoi for vector 123
448     chip.broadcast_eoi(123).expect("failed to broadcast eoi");
449 
450     // irq line 1 should be deasserted
451     let state = chip.get_ioapic_state().expect("failed to get ioapic state");
452     assert_eq!(state.current_interrupt_level_bitmap, 0);
453 
454     // resample event should be written to by ioapic
455     assert_eq!(
456         evt.get_resample()
457             .wait_timeout(std::time::Duration::from_millis(10))
458             .expect("failed to read_timeout"),
459         EventWaitResult::Signaled
460     );
461 }
462