Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/tests/irqchip/kvm/x86_64.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::KvmKernelIrqChip;
17
use devices::KvmSplitIrqChip;
18
use devices::IOAPIC_BASE_ADDRESS;
19
use hypervisor::kvm::Kvm;
20
use hypervisor::kvm::KvmVm;
21
use hypervisor::IoapicRedirectionTableEntry;
22
use hypervisor::IrqRoute;
23
use hypervisor::IrqSource;
24
use hypervisor::PicSelect;
25
use hypervisor::PitRWMode;
26
use hypervisor::TriggerMode;
27
use hypervisor::Vm;
28
use hypervisor::VmX86_64;
29
use resources::AddressRange;
30
use resources::SystemAllocator;
31
use resources::SystemAllocatorConfig;
32
use vm_control::DeviceId;
33
use vm_control::PlatformDeviceId;
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
47
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
63
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]
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]
91
fn kernel_irqchip_get_pic() {
92
test_get_pic(get_kernel_chip());
93
}
94
95
#[test]
96
fn kernel_irqchip_set_pic() {
97
test_set_pic(get_kernel_chip());
98
}
99
100
#[test]
101
fn kernel_irqchip_get_ioapic() {
102
test_get_ioapic(get_kernel_chip());
103
}
104
105
#[test]
106
fn kernel_irqchip_set_ioapic() {
107
test_set_ioapic(get_kernel_chip());
108
}
109
110
#[test]
111
fn kernel_irqchip_get_pit() {
112
test_get_pit(get_kernel_chip());
113
}
114
115
#[test]
116
fn kernel_irqchip_set_pit() {
117
test_set_pit(get_kernel_chip());
118
}
119
120
#[test]
121
fn kernel_irqchip_get_lapic() {
122
test_get_lapic(get_kernel_chip())
123
}
124
125
#[test]
126
fn kernel_irqchip_set_lapic() {
127
test_set_lapic(get_kernel_chip())
128
}
129
130
#[test]
131
fn kernel_irqchip_route_irq() {
132
test_route_irq(get_kernel_chip());
133
}
134
135
#[test]
136
fn split_irqchip_get_pic() {
137
test_get_pic(get_split_chip());
138
}
139
140
#[test]
141
fn split_irqchip_set_pic() {
142
test_set_pic(get_split_chip());
143
}
144
145
#[test]
146
fn split_irqchip_get_ioapic() {
147
test_get_ioapic(get_split_chip());
148
}
149
150
#[test]
151
fn split_irqchip_set_ioapic() {
152
test_set_ioapic(get_split_chip());
153
}
154
155
#[test]
156
fn split_irqchip_get_pit() {
157
test_get_pit(get_split_chip());
158
}
159
160
#[test]
161
fn split_irqchip_set_pit() {
162
test_set_pit(get_split_chip());
163
}
164
165
#[test]
166
fn split_irqchip_route_irq() {
167
test_route_irq(get_split_chip());
168
}
169
170
#[test]
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]
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]
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: PlatformDeviceId::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(PlatformDeviceId::Cmos)
229
);
230
assert_eq!(tokens[1].2, *evt.get_trigger());
231
}
232
233
#[test]
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: PlatformDeviceId::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]
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]
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: PlatformDeviceId::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
463