Skip to content

Commit 1480463

Browse files
committed
Add inter-vm communications
This also adds a basic 'transfer console' implementation
1 parent 8a46c85 commit 1480463

File tree

11 files changed

+298
-40
lines changed

11 files changed

+298
-40
lines changed

mythril/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mythril/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ description = "A intel-focused hypervisor using VT-x/EPT"
1111
test = []
1212

1313
[dependencies]
14+
arraydeque = { version = "0.4.5", default-features = false }
1415
arrayvec = { version = "0.5.1", default-features = false }
1516
bitflags = "1.2.0"
1617
byteorder = { version = "1", default-features = false }

mythril/src/apic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ impl LocalApic {
188188
}
189189

190190
/// The APIC ID
191-
pub fn id(&self) -> usize {
192-
unsafe { msr::rdmsr(msr::IA32_X2APIC_APICID) as usize }
191+
pub fn id(&self) -> u32 {
192+
unsafe { msr::rdmsr(msr::IA32_X2APIC_APICID) as u32 }
193193
}
194194

195195
/// The Logical APIC ID

mythril/src/boot.S

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,13 @@ _start:
168168
out 0x21, al
169169
out 0xa1, al
170170

171-
; Set IRQ masks
172-
mov al, 0xED ; Unmask IRQ1 (ps2) and IRQ4 (COM1)
171+
;; Mask everything so the I/O APIC will work
172+
mov al, 0xff
173173
out 0x21, al
174-
mov al, 0xFF
174+
mov al, 0xff
175175
out 0xA1, al
176176

177-
; ACK any pending interrupts
177+
;; ACK any pending interrupts
178178
mov al, 0x20
179179
out 0x20, al
180180
out 0xa0, al

mythril/src/interrupt/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
pub mod idt;
22

3+
pub const UART_VECTOR: u8 = 36;
4+
pub const TIMER_VECTOR: u8 = 48;
5+
pub const IPC_VECTOR: u8 = 49;
6+
37
pub unsafe fn enable_interrupts() {
48
llvm_asm!("sti" :::: "volatile");
59
}

mythril/src/kmain.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::ap;
33
use crate::apic;
44
use crate::boot_info::BootInfo;
55
use crate::interrupt;
6+
use crate::ioapic;
67
use crate::linux;
78
use crate::logger;
89
use crate::memory;
@@ -14,7 +15,6 @@ use crate::vcpu;
1415
use crate::virtdev;
1516
use crate::vm;
1617

17-
use alloc::collections::BTreeMap;
1818
use alloc::sync::Arc;
1919
use alloc::vec::Vec;
2020
use log::{debug, info};
@@ -29,7 +29,7 @@ extern "C" {
2929

3030
// Temporary helper function to create a vm for a single core
3131
fn default_vm(
32-
core: usize,
32+
core: u32,
3333
mem: u64,
3434
info: &BootInfo,
3535
add_uart: bool,
@@ -47,7 +47,7 @@ fn default_vm(
4747
};
4848

4949
let mut config =
50-
vm::VirtualMachineConfig::new(vec![core as u8], mem, physical_config);
50+
vm::VirtualMachineConfig::new(vec![core], mem, physical_config);
5151

5252
// FIXME: When `map_bios` may return an error, log the error.
5353
config.map_bios("seabios.bin".into()).unwrap_or(());
@@ -132,7 +132,7 @@ fn default_vm(
132132
.unwrap();
133133
device_map.register_device(fw_cfg_builder.build()).unwrap();
134134

135-
vm::VirtualMachine::new(config, info).expect("Failed to create vm")
135+
vm::VirtualMachine::new(core, config, info).expect("Failed to create vm")
136136
}
137137

138138
#[no_mangle]
@@ -207,18 +207,27 @@ unsafe fn kmain(mut boot_info: BootInfo) -> ! {
207207
})
208208
.collect::<Vec<_>>();
209209

210+
ioapic::init_ioapics(&madt).expect("Failed to initialize IOAPICs");
211+
ioapic::map_gsi_vector(4, interrupt::UART_VECTOR, 0)
212+
.expect("Failed to map com0 gsi");
213+
210214
percore::init_sections(apic_ids.len())
211215
.expect("Failed to initialize per-core sections");
212216

213-
let mut map = BTreeMap::new();
217+
let mut builder = vm::VirtualMachineBuilder::new();
218+
214219
for apic_id in apic_ids.iter() {
215-
map.insert(
216-
*apic_id as usize,
217-
default_vm(*apic_id as usize, 256, &boot_info, *apic_id == 0),
218-
);
220+
builder
221+
.insert_machine(default_vm(
222+
*apic_id,
223+
256,
224+
&boot_info,
225+
*apic_id == 0,
226+
))
227+
.expect("Failed to insert new vm");
219228
}
220229

221-
vm::VM_MAP = Some(map);
230+
vm::init_virtual_machines(builder.finalize());
222231

223232
debug!("AP_STARTUP address: 0x{:x}", AP_STARTUP_ADDR);
224233

mythril/src/time.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
use crate::apic;
1010
use crate::error::Result;
11+
use crate::interrupt;
1112
use crate::tsc;
1213
use crate::vcpu;
1314
use crate::{declare_per_core, get_per_core, get_per_core_mut};
@@ -16,8 +17,6 @@ use alloc::{collections::BTreeMap, vec};
1617
use core::ops::{Add, AddAssign, Sub, SubAssign};
1718
use core::time::Duration;
1819

19-
const TIMER_VECTOR: u8 = 48;
20-
2120
//TODO: should this just be stored as a VCPU member?
2221
declare_per_core! {
2322
static mut TIMER_WHEEL: Option<TimerWheel> = None;
@@ -331,7 +330,7 @@ impl TimerWheel {
331330
if let Some((when, _)) = soonest {
332331
unsafe {
333332
apic::get_local_apic_mut()
334-
.schedule_interrupt(when, TIMER_VECTOR);
333+
.schedule_interrupt(when, interrupt::TIMER_VECTOR);
335334
}
336335
}
337336
}

mythril/src/vcpu.rs

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::apic;
22
use crate::emulate;
33
use crate::error::{self, Error, Result};
4+
use crate::interrupt;
5+
use crate::ioapic;
46
use crate::memory::Raw4kPage;
57
use crate::percore;
68
use crate::registers::{GdtrBase, IdtrBase};
@@ -33,14 +35,11 @@ pub fn mp_entry_point() -> ! {
3335
}
3436

3537
let vm = unsafe {
36-
vm::VM_MAP
37-
.as_ref()
38-
.unwrap()
39-
.get(&apic::get_local_apic().id())
40-
.expect("Failed to find VM for core")
41-
.clone()
38+
let id = apic::get_local_apic().id();
39+
vm::get_vm_for_apic_id(id)
40+
.expect(&format!("Failed to find VM associated with 0x{:x}", id))
4241
};
43-
let vcpu = VCpu::new(vm.clone()).expect("Failed to create vcpu");
42+
let vcpu = VCpu::new(vm).expect("Failed to create vcpu");
4443
vcpu.launch().expect("Failed to launch vm")
4544
}
4645

@@ -507,17 +506,26 @@ impl VCpu {
507506
vmexit::ExitInformation::InterruptWindow => {}
508507
vmexit::ExitInformation::ExternalInterrupt(info) => unsafe {
509508
match info.vector {
510-
0x24 => self.handle_uart_keypress(&mut responses)?,
509+
interrupt::UART_VECTOR => {
510+
self.handle_uart_keypress(&mut responses)?
511+
}
512+
interrupt::IPC_VECTOR => {
513+
let msg =
514+
vm::recv_vm_msg().ok_or_else(|| Error::NotFound)?;
515+
match msg {
516+
vm::VirtualMachineMsg::GrantConsole(serial) => {
517+
let mut vm = self.vm.write();
518+
vm.config.physical_devices_mut().serial =
519+
Some(serial);
520+
}
521+
}
522+
}
511523
_ => (),
512524
}
513525

514-
// Ack the interrupt via PIC or APIC based on the vector
515-
if info.vector < 48 {
516-
//TODO: this should be a call to the physical PIC
517-
x86::io::outb(0x20, 0x20);
518-
} else {
519-
apic::get_local_apic_mut().eoi();
520-
}
526+
// We don't use the PIC, so any interrupt must be ACKed through
527+
// the local apic
528+
apic::get_local_apic_mut().eoi();
521529
},
522530
_ => {
523531
info!("{}", self.vmcs);
@@ -530,6 +538,38 @@ impl VCpu {
530538
virtdev::DeviceEventResponse::Interrupt((vector, kind)) => {
531539
self.inject_interrupt(vector, kind);
532540
}
541+
virtdev::DeviceEventResponse::NextConsole => {
542+
info!("Received Ctrl-a three times. Switching console to next VM");
543+
544+
let mut vm = self.vm.write();
545+
let serial = vm
546+
.config
547+
.physical_devices_mut()
548+
.serial
549+
.take()
550+
.ok_or_else(|| Error::NotFound)?;
551+
let vmid = vm.id;
552+
drop(vm);
553+
554+
let next_vmid = (vmid + 1) % vm::max_vm_id();
555+
556+
vm::send_vm_msg(
557+
vm::VirtualMachineMsg::GrantConsole(serial),
558+
next_vmid,
559+
)?;
560+
561+
//FIXME(alschwalm): this should use the vm's bsp apicid
562+
ioapic::map_gsi_vector(
563+
4,
564+
interrupt::UART_VECTOR,
565+
next_vmid as u8,
566+
)
567+
.map_err(|_| {
568+
Error::DeviceError(
569+
"Failed to update console GSI mapping".into(),
570+
)
571+
})?;
572+
}
533573
virtdev::DeviceEventResponse::GuestUartTransmitted(val) => {
534574
let vm = self.vm.read();
535575
if vm.config.physical_devices().serial.is_some() {

mythril/src/virtdev/com.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub struct Uart8250 {
2020
_line_status_register: LsrFlags,
2121
_modem_status_register: u8,
2222
_scratch_register: u8,
23+
24+
ctrl_a_count: u8,
2325
}
2426

2527
impl Uart8250 {
@@ -35,6 +37,7 @@ impl Uart8250 {
3537
_line_status_register: LsrFlags::empty(),
3638
_modem_status_register: 0,
3739
_scratch_register: 0,
40+
ctrl_a_count: 0,
3841
}))
3942
}
4043

@@ -65,6 +68,14 @@ impl EmulatedDevice for Uart8250 {
6568
vcpu::InjectedInterruptType::ExternalInterrupt,
6669
)),
6770
);
71+
if key == 0x01 {
72+
// ctrl+a
73+
self.ctrl_a_count += 1;
74+
}
75+
if self.ctrl_a_count == 3 {
76+
event.responses.push(DeviceEventResponse::NextConsole);
77+
self.ctrl_a_count = 0;
78+
}
6879
self.write(key)
6980
}
7081
DeviceEvent::PortRead(port, mut val) => {

mythril/src/virtdev/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub enum DeviceEvent<'a> {
4343
#[derive(Debug)]
4444
pub enum DeviceEventResponse {
4545
GuestUartTransmitted(u8),
46+
NextConsole,
4647
Interrupt((u8, vcpu::InjectedInterruptType)),
4748
}
4849

0 commit comments

Comments
 (0)