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