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