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