• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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