Skip to content

Commit 7b921f6

Browse files
committed
Better distinction between GSI and interrupt vectors
1 parent 7f9048d commit 7b921f6

File tree

7 files changed

+113
-76
lines changed

7 files changed

+113
-76
lines changed

mythril/src/kmain.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,17 @@ fn build_vm(
6565

6666
let mut madt = acpi::madt::MADTBuilder::<[_; 8]>::new();
6767
madt.set_ica(vm::GUEST_LOCAL_APIC_ADDR.as_u64() as u32);
68-
madt.add_ics(acpi::madt::Ics::LocalApic {
69-
apic_id: 0,
70-
apic_uid: 0,
71-
flags: acpi::madt::LocalApicFlags::ENABLED,
72-
})
73-
.expect("Failed to add APIC to MADT");
74-
madt.add_ics(acpi::madt::Ics::LocalApic {
75-
apic_id: 1,
76-
apic_uid: 0,
77-
flags: acpi::madt::LocalApicFlags::ENABLED,
78-
})
79-
.expect("Failed to add APIC to MADT");
68+
69+
for core in cfg.cpus.iter() {
70+
madt.add_ics(acpi::madt::Ics::LocalApic {
71+
// TODO(alschwalm): we should assign an actual APIC id here,
72+
// instead of using the core id
73+
apic_id: core.raw as u8,
74+
apic_uid: 0,
75+
flags: acpi::madt::LocalApicFlags::ENABLED,
76+
})
77+
.expect("Failed to add APIC to MADT");
78+
}
8079
madt.add_ics(acpi::madt::Ics::IoApic {
8180
ioapic_id: 0,
8281
ioapic_addr: 0xfec00000 as *mut u8,

mythril/src/time.rs

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,27 @@ fn frequency() -> u64 {
5757
TIME_SRC.frequency()
5858
}
5959

60+
/// An interrupt to be delivered by a timer
61+
#[derive(Clone)]
62+
pub enum TimerInterruptType {
63+
/// An interrupt to be delivered to the core that is running the timer.
64+
/// For example, when virtualizing the guest local apic timer, the
65+
/// generated interrupt is _not_ a guest GSI, but a directly delivered
66+
/// interrupt.
67+
Direct {
68+
/// The interrupt vector to be delivered by the timer
69+
vector: u8,
70+
71+
/// The kind of interrupt to be delivered by the timer
72+
kind: vcpu::InjectedInterruptType,
73+
},
74+
75+
/// An interrupt to be delivered to the guest via a GSI.
76+
/// For example, any hardware timer external to the core will generate
77+
/// a GSI and be routed to a vector through the guest IO APIC
78+
GSI(u32),
79+
}
80+
6081
/// A point in time on the system in terms of the global system `TimeSource`
6182
///
6283
/// An `Instant` can be added/subtracted with a `Duration` to produce an
@@ -123,38 +144,33 @@ enum TimerMode {
123144
pub struct ReadyTimer {
124145
duration: Duration,
125146
mode: TimerMode,
126-
127-
// The interrupt vector to deliver to the guest when this timer
128-
// expires.
129-
// TODO: Not all timers represent interrupts to deliver to the guest,
130-
// so we will need to make this more abstract.
131-
vector: u8,
147+
kind: TimerInterruptType,
132148
}
133149

134150
/// A started one-shot or periodic timer
135151
pub struct RunningTimer {
136152
duration: Duration,
137153
mode: TimerMode,
138154
started: Instant,
139-
vector: u8,
155+
kind: TimerInterruptType,
140156
}
141157

142158
impl ReadyTimer {
143159
/// Create a new one-shot timer.
144-
pub fn one_shot(duration: Duration, vector: u8) -> Self {
160+
pub fn one_shot(duration: Duration, kind: TimerInterruptType) -> Self {
145161
Self {
146-
duration: duration,
162+
duration,
147163
mode: TimerMode::OneShot,
148-
vector: vector,
164+
kind,
149165
}
150166
}
151167

152168
/// Create a new periodic timer.
153-
pub fn periodic(period: Duration, vector: u8) -> Self {
169+
pub fn periodic(period: Duration, kind: TimerInterruptType) -> Self {
154170
Self {
155171
duration: period,
156172
mode: TimerMode::Periodic,
157-
vector: vector,
173+
kind,
158174
}
159175
}
160176

@@ -164,7 +180,7 @@ impl ReadyTimer {
164180
duration: self.duration,
165181
mode: self.mode,
166182
started: now(),
167-
vector: self.vector,
183+
kind: self.kind,
168184
}
169185
}
170186

@@ -183,7 +199,7 @@ impl RunningTimer {
183199
ReadyTimer {
184200
duration: self.duration,
185201
mode: self.mode,
186-
vector: self.vector,
202+
kind: self.kind,
187203
}
188204
}
189205

@@ -273,7 +289,7 @@ impl TimerWheel {
273289
/// expired and will reset any periodic timers.
274290
pub fn expire_elapsed_timers(
275291
&mut self,
276-
) -> Result<vec::Vec<(u8, vcpu::InjectedInterruptType)>> {
292+
) -> Result<vec::Vec<TimerInterruptType>> {
277293
let mut interrupts = vec![];
278294
let elapsed_oneshots = self
279295
.timers
@@ -288,10 +304,7 @@ impl TimerWheel {
288304
.collect::<vec::Vec<_>>();
289305

290306
for id in elapsed_oneshots {
291-
interrupts.push((
292-
self.timers[&id].vector,
293-
vcpu::InjectedInterruptType::ExternalInterrupt,
294-
));
307+
interrupts.push(self.timers[&id].kind.clone());
295308
self.timers.remove(&id);
296309
}
297310

@@ -300,10 +313,7 @@ impl TimerWheel {
300313
.iter_mut()
301314
.filter(|(_, timer)| timer.elapsed() && timer.is_periodic())
302315
{
303-
interrupts.push((
304-
timer.vector,
305-
vcpu::InjectedInterruptType::ExternalInterrupt,
306-
));
316+
interrupts.push(timer.kind.clone());
307317
timer.reset();
308318
}
309319

@@ -315,8 +325,8 @@ impl TimerWheel {
315325
let soonest = self
316326
.timers
317327
.values()
318-
.map(|timer| (timer.elapses_at(), timer.vector))
319-
.min();
328+
.map(|timer| (timer.elapses_at(), &timer.kind))
329+
.min_by(|(time1, _), (time2, _)| time1.cmp(time2));
320330

321331
// TODO: we should only actually reset this if the new time
322332
// is sooner than the last time we set
@@ -407,19 +417,19 @@ pub fn cancel_timer(id: &TimerId) -> Result<()> {
407417
/// Set a one shot timer on this core
408418
pub fn set_oneshot_timer(
409419
duration: core::time::Duration,
410-
vector: u8,
420+
kind: TimerInterruptType,
411421
) -> TimerId {
412422
let wheel = unsafe { get_timer_wheel_mut() };
413-
let timer = ReadyTimer::one_shot(duration, vector);
423+
let timer = ReadyTimer::one_shot(duration, kind);
414424
wheel.register_timer(timer)
415425
}
416426

417427
/// Set a periodic timer on this core
418428
pub fn set_periodic_timer(
419429
interval: core::time::Duration,
420-
vector: u8,
430+
kind: TimerInterruptType,
421431
) -> TimerId {
422432
let wheel = unsafe { get_timer_wheel_mut() };
423-
let timer = ReadyTimer::periodic(interval, vector);
433+
let timer = ReadyTimer::periodic(interval, kind);
424434
wheel.register_timer(timer)
425435
}

mythril/src/vcpu.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,21 @@ impl VCpu {
425425
Ok(())
426426
}
427427

428+
pub fn route_interrupt(&mut self, gsi: u32) -> Result<()> {
429+
let (destination, vector, kind) =
430+
self.vm.read().gsi_destination(gsi)?;
431+
if destination == percore::read_core_id() {
432+
self.inject_interrupt(vector, kind);
433+
Ok(())
434+
} else {
435+
vm::send_vm_msg_core(
436+
vm::VirtualMachineMsg::GuestInterrupt { vector, kind },
437+
destination,
438+
true,
439+
)
440+
}
441+
}
442+
428443
/// Handle an arbitrary guest VMEXIT.
429444
///
430445
/// This is the rust 'entry' point when a guest exists.
@@ -443,10 +458,19 @@ impl VCpu {
443458

444459
// Always check for expired timers
445460
unsafe {
446-
for (vec, kind) in
461+
for timer_event in
447462
time::get_timer_wheel_mut().expire_elapsed_timers()?
448463
{
449-
self.inject_interrupt(vec, kind);
464+
match timer_event {
465+
time::TimerInterruptType::Direct {
466+
vector, kind, ..
467+
} => {
468+
self.inject_interrupt(vector, kind);
469+
}
470+
time::TimerInterruptType::GSI(gsi) => {
471+
self.route_interrupt(gsi)?;
472+
}
473+
}
450474
}
451475
}
452476

@@ -528,8 +552,6 @@ impl VCpu {
528552
.map(|serial| (serial.read(), serial.base_port()));
529553
drop(vm);
530554

531-
// info!("Received UART keypress on core: {}", crate::percore::read_core_id());
532-
533555
let mut vm = self.vm.write();
534556
if let Some((key, port)) = serial_info {
535557
vm.dispatch_event(
@@ -647,12 +669,8 @@ impl VCpu {
647669

648670
for response in responses {
649671
match response {
650-
virtdev::DeviceEventResponse::Interrupt((vector, kind)) => {
651-
// FIXME(alschwalm): for now, only deliver the UART interrupts to
652-
// the BSP core.
653-
if vector != 52 || crate::percore::read_core_id().raw == 0 {
654-
self.inject_interrupt(vector, kind);
655-
}
672+
virtdev::DeviceEventResponse::GSI(gsi) => {
673+
self.route_interrupt(gsi)?;
656674
}
657675
virtdev::DeviceEventResponse::NextConsole => {
658676
info!("Received Ctrl-a three times. Switching console to next VM");

mythril/src/virtdev/com.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,7 @@ impl EmulatedDevice for Uart8250 {
6161
fn on_event(&mut self, event: Event) -> Result<()> {
6262
match event.kind {
6363
DeviceEvent::HostUartReceived(key) => {
64-
// info!("Processing UART keypress on core: {}", crate::percore::read_core_id());
65-
event.responses.push(
66-
// IRQ4
67-
DeviceEventResponse::Interrupt((
68-
52,
69-
vcpu::InjectedInterruptType::ExternalInterrupt,
70-
)),
71-
);
64+
event.responses.push(DeviceEventResponse::GSI(4));
7265
if key == 0x01 {
7366
// ctrl+a
7467
self.ctrl_a_count += 1;
@@ -133,18 +126,11 @@ impl EmulatedDevice for Uart8250 {
133126
DeviceEventResponse::GuestUartTransmitted(val),
134127
);
135128

136-
//FIXME(alschwalm): this should actually either directly
137-
// respond with an interrupt device event _or_ send the
138-
// GuestInterrupt VirtualMachineMsg to the appropriate
139-
// core (according to either the PIC or IOAPIC)
140129
if self
141130
.interrupt_enable_register
142131
.contains(IerFlags::THR_EMPTY_INTERRUPT)
143132
{
144-
event.responses.push(
145-
// IRQ4
146-
DeviceEventResponse::Interrupt((52, vcpu::InjectedInterruptType::ExternalInterrupt))
147-
);
133+
event.responses.push(DeviceEventResponse::GSI(4));
148134
}
149135
self.interrupt_identification_register = 0b10;
150136
}

mythril/src/virtdev/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub enum DeviceEvent<'a> {
4545
pub enum DeviceEventResponse {
4646
GuestUartTransmitted(u8),
4747
NextConsole,
48-
Interrupt((u8, vcpu::InjectedInterruptType)),
48+
GSI(u32),
4949
}
5050

5151
pub struct Event<'a> {

mythril/src/virtdev/pit.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,10 @@ impl Pit8254 {
225225

226226
// Only channel 0 produces timer interrupts
227227
if port == PIT_COUNTER_0 {
228-
//FIXME: this value should be determined by the virtual
229-
// PIC/APIC. Currently use the vector linux has for IRQ0
230-
*timer =
231-
Some(time::set_oneshot_timer(duration, 48));
228+
*timer = Some(time::set_oneshot_timer(
229+
duration,
230+
time::TimerInterruptType::GSI(0),
231+
));
232232
}
233233
}
234234

@@ -241,10 +241,10 @@ impl Pit8254 {
241241
*start_time = Some(time::now());
242242

243243
if port == PIT_COUNTER_0 {
244-
//FIXME: this value should be determined by the virtual
245-
// PIC/APIC. Currently use the vector linux has for IRQ0
246-
*timer =
247-
Some(time::set_periodic_timer(duration, 48));
244+
*timer = Some(time::set_periodic_timer(
245+
duration,
246+
time::TimerInterruptType::GSI(0),
247+
));
248248
}
249249
}
250250
};

mythril/src/vm.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,30 @@ impl VirtualMachine {
426426
dev.write().on_event(event)
427427
}
428428

429+
pub fn gsi_destination(
430+
&self,
431+
gsi: u32,
432+
) -> Result<(percore::CoreId, u8, vcpu::InjectedInterruptType)> {
433+
//TODO(alschwalm): For now just route the UART interrupts to the BSP,
434+
// but this should ulimately do actual interrupt routing based on the
435+
// guest IO APICs. For now just blindly translate GSI to vector based
436+
// on this basic formula.
437+
let vector = (gsi + 48) as u8;
438+
if gsi == 4 {
439+
Ok((
440+
self.config.bsp_id(),
441+
vector,
442+
vcpu::InjectedInterruptType::ExternalInterrupt,
443+
))
444+
} else {
445+
Ok((
446+
percore::read_core_id(),
447+
vector,
448+
vcpu::InjectedInterruptType::ExternalInterrupt,
449+
))
450+
}
451+
}
452+
429453
fn map_data(
430454
image: &[u8],
431455
addr: &GuestPhysAddr,

0 commit comments

Comments
 (0)