Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/tests/irqchip/whpx.rs
5394 views
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::IrqChip;
12
use devices::IrqChipX86_64;
13
use devices::IrqEdgeEvent;
14
use devices::IrqEventSource;
15
use devices::IrqLevelEvent;
16
use devices::PlatformDeviceId;
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_control::DeviceId;
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
45
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.
50
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]
72
fn set_pic() {
73
if !split_supported() {
74
return;
75
}
76
test_set_pic(get_chip(1));
77
}
78
79
#[test]
80
fn get_ioapic() {
81
if !split_supported() {
82
return;
83
}
84
test_get_ioapic(get_chip(1));
85
}
86
87
#[test]
88
fn set_ioapic() {
89
if !split_supported() {
90
return;
91
}
92
test_set_ioapic(get_chip(1));
93
}
94
95
#[test]
96
fn get_pit() {
97
if !split_supported() {
98
return;
99
}
100
test_get_pit(get_chip(1));
101
}
102
103
#[test]
104
fn set_pit() {
105
if !split_supported() {
106
return;
107
}
108
test_set_pit(get_chip(1));
109
}
110
111
#[test]
112
fn route_irq() {
113
if !split_supported() {
114
return;
115
}
116
test_route_irq(get_chip(1));
117
}
118
119
#[test]
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]
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]
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: PlatformDeviceId::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(PlatformDeviceId::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]
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: PlatformDeviceId::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]
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: PlatformDeviceId::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
410