xref: /aosp_15_r20/external/crosvm/devices/tests/irqchip/whpx.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::WhpxSplitIrqChip;
19 use devices::IOAPIC_BASE_ADDRESS;
20 use hypervisor::whpx::Whpx;
21 use hypervisor::whpx::WhpxFeature;
22 use hypervisor::whpx::WhpxVm;
23 use hypervisor::CpuId;
24 use hypervisor::IoapicRedirectionTableEntry;
25 use hypervisor::IrqRoute;
26 use hypervisor::IrqSource;
27 use hypervisor::PicSelect;
28 use hypervisor::PitRWMode;
29 use hypervisor::TriggerMode;
30 use hypervisor::Vm;
31 use hypervisor::VmX86_64;
32 use resources::AddressRange;
33 use resources::SystemAllocator;
34 use resources::SystemAllocatorConfig;
35 use vm_memory::GuestAddress;
36 use vm_memory::GuestMemory;
37 
38 use crate::x86_64::test_get_ioapic;
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_pic;
43 use crate::x86_64::test_set_pit;
44 
split_supported() -> bool45 fn split_supported() -> bool {
46     Whpx::check_whpx_feature(WhpxFeature::LocalApicEmulation).expect("failed to get whpx features")
47 }
48 
49 /// Helper function for setting up a WhpxSplitIrqChip.
get_chip(num_vcpus: usize) -> WhpxSplitIrqChip50 fn get_chip(num_vcpus: usize) -> WhpxSplitIrqChip {
51     let whpx = Whpx::new().expect("failed to instantiate Whpx");
52     let mem = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap();
53     let vm = WhpxVm::new(&whpx, num_vcpus, mem, CpuId::new(0), true, None)
54         .expect("failed to instantiate vm");
55 
56     let (_, irq_tube) = Tube::pair().expect("failed to create irq tube");
57 
58     let mut chip =
59         WhpxSplitIrqChip::new(vm.try_clone().expect("failed to clone vm"), irq_tube, None)
60             .expect("failed to instantiate WhpxSplitIrqChip");
61 
62     for i in 0..num_vcpus {
63         let vcpu = vm.create_vcpu(i).expect("failed to instantiate vcpu");
64         chip.add_vcpu(i, vcpu.as_vcpu())
65             .expect("failed to add vcpu");
66     }
67 
68     chip
69 }
70 
71 #[test]
set_pic()72 fn set_pic() {
73     if !split_supported() {
74         return;
75     }
76     test_set_pic(get_chip(1));
77 }
78 
79 #[test]
get_ioapic()80 fn get_ioapic() {
81     if !split_supported() {
82         return;
83     }
84     test_get_ioapic(get_chip(1));
85 }
86 
87 #[test]
set_ioapic()88 fn set_ioapic() {
89     if !split_supported() {
90         return;
91     }
92     test_set_ioapic(get_chip(1));
93 }
94 
95 #[test]
get_pit()96 fn get_pit() {
97     if !split_supported() {
98         return;
99     }
100     test_get_pit(get_chip(1));
101 }
102 
103 #[test]
set_pit()104 fn set_pit() {
105     if !split_supported() {
106         return;
107     }
108     test_set_pit(get_chip(1));
109 }
110 
111 #[test]
route_irq()112 fn route_irq() {
113     if !split_supported() {
114         return;
115     }
116     test_route_irq(get_chip(1));
117 }
118 
119 #[test]
pit_uses_speaker_port()120 fn pit_uses_speaker_port() {
121     if !split_supported() {
122         return;
123     }
124     let chip = get_chip(1);
125     assert!(chip.pit_uses_speaker_port());
126 }
127 
128 #[test]
routes_conflict()129 fn routes_conflict() {
130     if !split_supported() {
131         return;
132     }
133     let mut chip = get_chip(1);
134     chip.route_irq(IrqRoute {
135         gsi: 32,
136         source: IrqSource::Msi {
137             address: 4276092928,
138             data: 0,
139         },
140     })
141     .expect("failed to set msi route");
142     // this second route should replace the first
143     chip.route_irq(IrqRoute {
144         gsi: 32,
145         source: IrqSource::Msi {
146             address: 4276092928,
147             data: 32801,
148         },
149     })
150     .expect("failed to set msi route");
151 }
152 
153 #[test]
irq_event_tokens()154 fn irq_event_tokens() {
155     if !split_supported() {
156         return;
157     }
158     let mut chip = get_chip(1);
159     let tokens = chip
160         .irq_event_tokens()
161         .expect("could not get irq_event_tokens");
162 
163     // there should be one token on a fresh split irqchip, for the pit
164     assert_eq!(tokens.len(), 1);
165     assert_eq!(tokens[0].1.device_name, "userspace PIT");
166 
167     // register another irq event
168     let evt = IrqEdgeEvent::new().expect("failed to create event");
169     let source = IrqEventSource {
170         device_id: CrosvmDeviceId::DebugConsole.into(),
171         queue_id: 0,
172         device_name: "test".to_owned(),
173     };
174     chip.register_edge_irq_event(6, &evt, source)
175         .expect("failed to register irq event");
176 
177     let tokens = chip
178         .irq_event_tokens()
179         .expect("could not get irq_event_tokens");
180 
181     // now there should be two tokens
182     assert_eq!(tokens.len(), 2);
183     assert_eq!(tokens[0].1.device_name, "userspace PIT");
184     assert_eq!(
185         tokens[1].1.device_id,
186         DeviceId::PlatformDeviceId(CrosvmDeviceId::DebugConsole)
187     );
188     assert_eq!(tokens[1].2, evt.get_trigger().try_clone().unwrap());
189 }
190 
191 // TODO(srichman): Factor out of UserspaceIrqChip and KvmSplitIrqChip and WhpxSplitIrqChip.
192 #[test]
finalize_devices()193 fn finalize_devices() {
194     if !split_supported() {
195         return;
196     }
197     let mut chip = get_chip(1);
198 
199     let mmio_bus = Bus::new(BusType::Mmio);
200     let io_bus = Bus::new(BusType::Io);
201     let mut resources = SystemAllocator::new(
202         SystemAllocatorConfig {
203             io: Some(AddressRange {
204                 start: 0xc000,
205                 end: 0xFFFF,
206             }),
207             low_mmio: AddressRange {
208                 start: 0,
209                 end: 2048,
210             },
211             high_mmio: AddressRange {
212                 start: 2048,
213                 end: 6143,
214             },
215             platform_mmio: None,
216             first_irq: 5,
217         },
218         None,
219         &[],
220     )
221     .expect("failed to create SystemAllocator");
222 
223     // setup an event for irq line 1
224     let evt = IrqLevelEvent::new().expect("failed to create event");
225 
226     let source = IrqEventSource {
227         device_id: CrosvmDeviceId::DebugConsole.into(),
228         device_name: "test".to_owned(),
229         queue_id: 0,
230     };
231 
232     let evt_index = chip
233         .register_level_irq_event(1, &evt, source)
234         .expect("failed to register_level_irq_event")
235         .expect("register_level_irq_event should not return None");
236 
237     // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
238     chip.finalize_devices(&mut resources, &io_bus, &mmio_bus)
239         .expect("failed to finalize devices");
240 
241     // Should not be able to allocate an irq < 24 now
242     assert!(resources.allocate_irq().expect("failed to allocate irq") >= 24);
243 
244     // set PIT counter 2 to "SquareWaveGen"(aka 3) mode and "Both" access mode
245     io_bus.write(0x43, &[0b10110110]);
246 
247     let state = chip.get_pit().expect("failed to get pit state");
248     assert_eq!(state.channels[2].mode, 3);
249     assert_eq!(state.channels[2].rw_mode, PitRWMode::Both);
250 
251     // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
252     // ICW2 0x08: Interrupt vector base address 0x08.
253     // ICW3 0xff: Value written does not matter.
254     // ICW4 0x13: Special fully nested mode, auto EOI.
255     io_bus.write(0x20, &[0x11]);
256     io_bus.write(0x21, &[0x08]);
257     io_bus.write(0x21, &[0xff]);
258     io_bus.write(0x21, &[0x13]);
259 
260     let state = chip
261         .get_pic_state(PicSelect::Primary)
262         .expect("failed to get pic state");
263 
264     // auto eoi and special fully nested mode should be turned on
265     assert!(state.auto_eoi);
266     assert!(state.special_fully_nested_mode);
267 
268     // Need to write to the irq event before servicing it
269     evt.trigger().expect("failed to write to event");
270 
271     // if we assert irq line one, and then get the resulting interrupt, an auto-eoi should
272     // occur and cause the resample_event to be written to
273     chip.service_irq_event(evt_index)
274         .expect("failed to service irq");
275 
276     assert!(chip.interrupt_requested(0));
277     assert_eq!(
278         chip.get_external_interrupt(0)
279             .expect("failed to get external interrupt"),
280         // Vector is 9 because the interrupt vector base address is 0x08 and this is irq
281         // line 1 and 8+1 = 9
282         Some(0x9)
283     );
284 
285     assert_eq!(
286         evt.get_resample()
287             .wait_timeout(std::time::Duration::from_secs(1))
288             .expect("failed to read_timeout"),
289         EventWaitResult::Signaled
290     );
291 
292     // setup a ioapic redirection table entry 14
293     let mut entry = IoapicRedirectionTableEntry::default();
294     entry.set_vector(44);
295 
296     let irq_14_offset = 0x10 + 14 * 2;
297     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_14_offset]);
298     mmio_bus.write(
299         IOAPIC_BASE_ADDRESS + 0x10,
300         &(entry.get(0, 32) as u32).to_ne_bytes(),
301     );
302     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_14_offset + 1]);
303     mmio_bus.write(
304         IOAPIC_BASE_ADDRESS + 0x10,
305         &(entry.get(32, 32) as u32).to_ne_bytes(),
306     );
307 
308     let state = chip.get_ioapic_state().expect("failed to get ioapic state");
309 
310     // redirection table entry 14 should have a vector of 44
311     assert_eq!(state.redirect_table[14].get_vector(), 44);
312 }
313 
314 // TODO(srichman): Factor out of UserspaceIrqChip and KvmSplitIrqChip and WhpxSplitIrqChip.
315 #[test]
broadcast_eoi()316 fn broadcast_eoi() {
317     if !split_supported() {
318         return;
319     }
320     let mut chip = get_chip(1);
321 
322     let mmio_bus = Bus::new(BusType::Mmio);
323     let io_bus = Bus::new(BusType::Io);
324     let mut resources = SystemAllocator::new(
325         SystemAllocatorConfig {
326             io: Some(AddressRange {
327                 start: 0xc000,
328                 end: 0xFFFF,
329             }),
330             low_mmio: AddressRange {
331                 start: 0,
332                 end: 2048,
333             },
334             high_mmio: AddressRange {
335                 start: 2048,
336                 end: 6143,
337             },
338             platform_mmio: None,
339             first_irq: 5,
340         },
341         None,
342         &[],
343     )
344     .expect("failed to create SystemAllocator");
345 
346     // setup an event for irq line 1
347     let evt = IrqLevelEvent::new().expect("failed to create event");
348 
349     let source = IrqEventSource {
350         device_id: CrosvmDeviceId::DebugConsole.into(),
351         device_name: "test".to_owned(),
352         queue_id: 0,
353     };
354 
355     chip.register_level_irq_event(1, &evt, source)
356         .expect("failed to register_level_irq_event");
357 
358     // Once we finalize devices, the pic/pit/ioapic should be attached to io and mmio busses
359     chip.finalize_devices(&mut resources, &io_bus, &mmio_bus)
360         .expect("failed to finalize devices");
361 
362     // setup a ioapic redirection table entry 1 with a vector of 123
363     let mut entry = IoapicRedirectionTableEntry::default();
364     entry.set_vector(123);
365     entry.set_trigger_mode(TriggerMode::Level);
366 
367     let irq_write_offset = 0x10 + 1 * 2;
368     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_write_offset]);
369     mmio_bus.write(
370         IOAPIC_BASE_ADDRESS + 0x10,
371         &(entry.get(0, 32) as u32).to_ne_bytes(),
372     );
373     mmio_bus.write(IOAPIC_BASE_ADDRESS, &[irq_write_offset + 1]);
374     mmio_bus.write(
375         IOAPIC_BASE_ADDRESS + 0x10,
376         &(entry.get(32, 32) as u32).to_ne_bytes(),
377     );
378 
379     // Assert line 1
380     chip.service_irq(1, true).expect("failed to service irq");
381 
382     // resample event should not be written to
383     assert_eq!(
384         evt.get_resample()
385             .wait_timeout(std::time::Duration::from_millis(10))
386             .expect("failed to read_timeout"),
387         EventWaitResult::TimedOut
388     );
389 
390     // irq line 1 should be asserted
391     let state = chip.get_ioapic_state().expect("failed to get ioapic state");
392     assert_eq!(state.current_interrupt_level_bitmap, 1 << 1);
393 
394     // Now broadcast an eoi for vector 123
395     chip.broadcast_eoi(123).expect("failed to broadcast eoi");
396 
397     // irq line 1 should be deasserted
398     let state = chip.get_ioapic_state().expect("failed to get ioapic state");
399     assert_eq!(state.current_interrupt_level_bitmap, 0);
400 
401     // resample event should be written to by ioapic
402     assert_eq!(
403         evt.get_resample()
404             .wait_timeout(std::time::Duration::from_millis(10))
405             .expect("failed to read_timeout"),
406         EventWaitResult::Signaled
407     );
408 }
409