Skip to content

Commit f5229f8

Browse files
committed
Make virtual machine and emulated device allocation static
This patch make some significant changes to the allocation layout of Mythril. Now, instead of heap allocation of devices and virtual machines, these are allocated in the following way: There is a static 'VirtualMachineSet' object that contains a list of virtual machines and associated message passing contexts. This structure is how inter-core and inter-vm communication occurs. Each 'VirtualMachine' now contains an ArrayVec of 'DynamicVirtualDevice'. A 'DynamicVirtualDevice' is a wrapper around various EmulatedDevice types that are _not_ part of the required guest architecture. For example, a given PCI device is 'dynamic' in the sense that we cannot state that it is part of the guest at compile time. Non-dynamic EmulatedDevices are now stored explicitly as part of the 'StaticVirtualDevices' field of the 'VirtualMachine'. This makes it significantly easier to reference a particular part of the guest architecture (rather than having to look up a device by Port, for example).
1 parent 9ed8d0a commit f5229f8

File tree

24 files changed

+337
-297
lines changed

24 files changed

+337
-297
lines changed

mythril/Cargo.lock

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

mythril/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ test = []
1212

1313
[dependencies]
1414
arraydeque = { version = "0.4.5", default-features = false }
15-
arrayvec = { version = "0.5.1", default-features = false }
1615
bitflags = "1.2.0"
1716
byteorder = { version = "1", default-features = false }
1817
num_enum = { version = "0.5.0", default-features = false }
@@ -29,6 +28,11 @@ spin = "0.5"
2928
ux = { version = "0.1.3", default-features = false }
3029
managed = { version = "0.8.0", features = ["map", "alloc"], default-features = false }
3130

31+
[dependencies.arrayvec]
32+
version = "0.5.2"
33+
default-features = false
34+
features = ["unstable-const-fn"]
35+
3236
[dependencies.iced-x86]
3337
version = "1.8.0"
3438
default-features = false

mythril/src/boot.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%include "paging.mac"
22

3-
%define BSP_STACK_SIZE (PAGE_SIZE*50)
3+
%define BSP_STACK_SIZE (PAGE_SIZE*100)
44
%define PAGE_HIERARCHY_SIZE (PAGE_SIZE*7)
55

66
global PAGE_HIERARCHY

mythril/src/emulate/cpuid.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub fn emulate_cpuid(
2323

2424
// Hide TSC deadline timer
2525
res.ecx &= !(1 << 24);
26+
} else if guest_cpu.rax as u32 == 0x0b {
27+
res.edx = crate::percore::read_core_id().raw as u32;
2628
}
2729

2830
guest_cpu.rax = res.eax as u64 | (guest_cpu.rax & 0xffffffff00000000);

mythril/src/emulate/memio.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,7 @@ pub fn handle_apic_access(
523523
_ => return Err(Error::NotSupported),
524524
};
525525

526-
vcpu.local_apic
527-
.register_write(vcpu.vm.clone(), offset, value)
526+
vcpu.local_apic.register_write(vcpu.vm, offset, value)
528527
}
529528

530529
let addr = vm::GUEST_LOCAL_APIC_ADDR

mythril/src/kmain.rs

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use crate::virtdev;
1818
use crate::vm;
1919

2020
use alloc::collections::BTreeMap;
21-
use alloc::sync::Arc;
2221
use alloc::vec::Vec;
2322
use log::{debug, info};
2423
use managed::ManagedMap;
@@ -40,11 +39,11 @@ fn build_vm(
4039
cfg: &config::UserVmConfig,
4140
info: &BootInfo,
4241
add_uart: bool,
43-
) -> Arc<vm::VirtualMachine> {
42+
) -> vm::VirtualMachine {
4443
let physical_config = if add_uart == false {
45-
vm::PhysicalDeviceConfig::default()
44+
vm::HostPhysicalDevices::default()
4645
} else {
47-
vm::PhysicalDeviceConfig {
46+
vm::HostPhysicalDevices {
4847
serial: RwLock::new(Some(
4948
physdev::com::Uart8250::new(0x3f8)
5049
.expect("Failed to create UART"),
@@ -83,47 +82,18 @@ fn build_vm(
8382

8483
acpi.add_sdt(madt).unwrap();
8584

86-
let device_map = config.virtual_devices_mut();
85+
let virtual_devices = &mut config.virtual_devices;
8786

88-
device_map
89-
.register_device(virtdev::acpi::AcpiRuntime::new(0x600).unwrap())
90-
.unwrap();
91-
device_map
92-
.register_device(virtdev::debug::DebugPort::new(0x402))
93-
.unwrap();
94-
device_map
95-
.register_device(virtdev::com::Uart8250::new(0x3F8))
96-
.unwrap();
97-
device_map
98-
.register_device(virtdev::vga::VgaController::new())
99-
.unwrap();
100-
device_map
101-
.register_device(virtdev::dma::Dma8237::new())
102-
.unwrap();
103-
device_map
104-
.register_device(virtdev::ignore::IgnoredDevice::new())
105-
.unwrap();
106-
device_map
107-
.register_device(virtdev::pci::PciRootComplex::new())
108-
.unwrap();
109-
device_map
110-
.register_device(virtdev::pic::Pic8259::new())
111-
.unwrap();
112-
device_map
113-
.register_device(virtdev::keyboard::Keyboard8042::new())
114-
.unwrap();
115-
device_map
116-
.register_device(virtdev::pit::Pit8254::new())
117-
.unwrap();
118-
device_map
119-
.register_device(virtdev::pos::ProgrammableOptionSelect::new())
120-
.unwrap();
121-
device_map
122-
.register_device(virtdev::rtc::CmosRtc::new(cfg.memory))
123-
.unwrap();
124-
device_map
125-
.register_device(virtdev::ioapic::IoApic::new())
126-
.unwrap();
87+
virtual_devices.push(RwLock::new(
88+
virtdev::DynamicVirtualDevice::DebugPort(
89+
virtdev::debug::DebugPort::new(0x402)
90+
.expect("Failed to make DebugPort"),
91+
),
92+
));
93+
94+
virtual_devices.push(RwLock::new(virtdev::DynamicVirtualDevice::Uart(
95+
virtdev::com::Uart8250::new(0x3F8).expect("Failed to make Uart"),
96+
)));
12797

12898
let mut fw_cfg_builder = virtdev::qemu_fw_cfg::QemuFwCfgBuilder::new();
12999

@@ -155,7 +125,9 @@ fn build_vm(
155125
)
156126
.unwrap();
157127

158-
device_map.register_device(fw_cfg_builder.build()).unwrap();
128+
virtual_devices.push(RwLock::new(virtdev::DynamicVirtualDevice::Qemu(
129+
fw_cfg_builder.build(),
130+
)));
159131

160132
vm::VirtualMachine::new(vm_id, config, info).expect("Failed to create vm")
161133
}
@@ -251,8 +223,6 @@ unsafe fn kmain(mut boot_info: BootInfo) -> ! {
251223
ioapic::map_gsi_vector(interrupt::gsi::UART, interrupt::vector::UART, 0)
252224
.expect("Failed to map com0 gsi");
253225

254-
let mut builder = vm::VirtualMachineSetBuilder::new();
255-
256226
let raw_cfg = boot_info
257227
.find_module("mythril.cfg")
258228
.expect("Failed to find 'mythril.cfg' in boot information")
@@ -263,13 +233,15 @@ unsafe fn kmain(mut boot_info: BootInfo) -> ! {
263233

264234
debug!("mythril.cfg: {:?}", mythril_cfg);
265235

266-
for (num, vm) in mythril_cfg.vms.into_iter().enumerate() {
267-
builder
268-
.insert_machine(build_vm(num as u32, &vm, &boot_info, num == 0))
269-
.expect("Failed to insert new vm");
270-
}
271-
272-
vm::init_virtual_machines(builder.finalize());
236+
let vms = mythril_cfg
237+
.vms
238+
.into_iter()
239+
.enumerate()
240+
.map(|(num, vm_cfg)| {
241+
build_vm(num as u32, &vm_cfg, &boot_info, num == 0)
242+
});
243+
vm::init_virtual_machines(vms)
244+
.expect("Failed to initialize early virtual machine state");
273245

274246
debug!("AP_STARTUP address: 0x{:x}", AP_STARTUP_ADDR);
275247

@@ -332,5 +304,17 @@ unsafe fn kmain(mut boot_info: BootInfo) -> ! {
332304
core::ptr::write_volatile(&mut AP_READY as *mut u8, 0);
333305
}
334306

307+
// Also don't start the bsp core if there is no guest assigned to use it
308+
let bsp_core_id = percore::read_core_id();
309+
if !vm::virtual_machines().is_assigned_core_id(bsp_core_id) {
310+
debug!(
311+
"Not starting core ID '{}' because it is not assigned to a guest",
312+
bsp_core_id
313+
);
314+
loop {
315+
crate::lock::relax_cpu();
316+
}
317+
}
318+
335319
vcpu::mp_entry_point()
336320
}

mythril/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![feature(get_mut_unchecked)]
66
#![feature(fixed_size_array)]
77
#![feature(panic_info_message)]
8+
#![feature(array_value_iter)]
89
#![feature(alloc_error_handler)]
910
#![feature(lang_items)]
1011
#![feature(stmt_expr_attributes)]

mythril/src/percore.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,22 @@ pub unsafe fn init_sections(ncores: usize) -> Result<()> {
6161
Ok(())
6262
}
6363

64+
unsafe fn percore_section_start(core_idx: u64) -> *const u8 {
65+
if core_idx == 0 {
66+
&PER_CORE_START as *const u8
67+
} else {
68+
let section_len = per_core_section_len();
69+
(&AP_PER_CORE_SECTIONS[section_len * (core_idx - 1) as usize])
70+
as *const u8
71+
}
72+
}
73+
6474
/// Initialize this core's per-core data
6575
///
6676
/// This must be called by each AP (and the BSP) before
6777
/// the usage of any per-core variable
6878
pub unsafe fn init_segment_for_core(core_idx: u64) {
69-
let fs = if core_idx == 0 {
70-
&PER_CORE_START as *const u8 as u64
71-
} else {
72-
let section_len = per_core_section_len();
73-
(&AP_PER_CORE_SECTIONS[section_len * (core_idx - 1) as usize])
74-
as *const u8 as u64
75-
};
76-
77-
msr::wrmsr(msr::IA32_FS_BASE, fs);
79+
msr::wrmsr(msr::IA32_FS_BASE, percore_section_start(core_idx) as u64);
7880

7981
RoAfterInit::init(
8082
crate::get_per_core_mut!(CORE_ID),

mythril/src/time.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ impl TimerWheel {
289289
/// expired and will reset any periodic timers.
290290
pub fn expire_elapsed_timers(
291291
&mut self,
292-
) -> Result<vec::Vec<TimerInterruptType>> {
292+
) -> Result<impl Iterator<Item = TimerInterruptType>> {
293293
let mut interrupts = vec![];
294294
let elapsed_oneshots = self
295295
.timers
@@ -318,7 +318,7 @@ impl TimerWheel {
318318
}
319319

320320
self.update_interrupt_timer();
321-
Ok(interrupts)
321+
Ok(interrupts.into_iter())
322322
}
323323

324324
fn update_interrupt_timer(&mut self) {

mythril/src/vcpu.rs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ use crate::percore;
88
use crate::registers::{GdtrBase, IdtrBase};
99
use crate::time;
1010
use crate::vm::VirtualMachine;
11+
use crate::{declare_per_core, get_per_core_mut};
1112
use crate::{virtdev, vm, vmcs, vmexit, vmx};
1213
use alloc::boxed::Box;
1314
use alloc::collections::BTreeMap;
14-
use alloc::sync::Arc;
15-
use alloc::vec::Vec;
1615
use core::mem;
1716
use core::pin::Pin;
1817
use x86::controlregs::{cr0, cr3, cr4};
@@ -24,6 +23,13 @@ extern "C" {
2423
static GDT64_DATA: u64;
2524
}
2625

26+
const PER_CORE_HOST_STACK_SIZE: usize = 1024 * 1024;
27+
28+
declare_per_core! {
29+
static mut HOST_STACK: [u8; PER_CORE_HOST_STACK_SIZE]
30+
= [0u8; PER_CORE_HOST_STACK_SIZE];
31+
}
32+
2733
/// The post-startup point where a core begins executing its statically
2834
/// assigned VCPU. Past this point, there is no distinction between BSP
2935
/// and AP.
@@ -41,10 +47,10 @@ pub fn mp_entry_point() -> ! {
4147
vm
4248
};
4349

44-
let mut vcpu = VCpu::new(vm.clone()).expect("Failed to create vcpu");
50+
let mut vcpu = VCpu::new(vm).expect("Failed to create vcpu");
4551

4652
let vm_id = vm.id;
47-
let is_vm_bsp = vm.config.bsp_id() == core_id;
53+
let is_vm_bsp = vm.bsp_id() == core_id;
4854

4955
// Increment the VM's count of ready cores
5056
vm.notify_ready();
@@ -89,11 +95,11 @@ pub enum InjectedInterruptType {
8995
/// ultimate handling will occur within an emulated device in the `VirtualMachine`'s
9096
/// `DeviceMap`)
9197
pub struct VCpu {
92-
pub vm: Arc<VirtualMachine>,
98+
pub vm: &'static VirtualMachine,
9399
pub vmcs: vmcs::ActiveVmcs,
94100
pub local_apic: virtdev::lapic::LocalApic,
95101
pending_interrupts: BTreeMap<u8, InjectedInterruptType>,
96-
stack: Vec<u8>,
102+
stack: &'static mut [u8; PER_CORE_HOST_STACK_SIZE],
97103
}
98104

99105
impl VCpu {
@@ -102,18 +108,15 @@ impl VCpu {
102108
/// Note that the result must be `Pin`, as the `VCpu` pushes its own
103109
/// address on to the per-core host stack so it can be retrieved on
104110
/// VMEXIT.
105-
pub fn new(vm: Arc<VirtualMachine>) -> Result<Pin<Box<Self>>> {
111+
pub fn new(vm: &'static VirtualMachine) -> Result<Pin<Box<Self>>> {
106112
let vmx = vmx::Vmx::enable()?;
107113
let vmcs = vmcs::Vmcs::new()?.activate(vmx)?;
108114

109-
// Allocate 1MB for host stack space
110-
let stack = vec![0u8; 1024 * 1024];
111-
112115
let mut vcpu = Box::pin(Self {
113116
vm: vm,
114117
vmcs: vmcs,
115118
local_apic: virtdev::lapic::LocalApic::new(),
116-
stack: stack,
119+
stack: get_per_core_mut!(HOST_STACK),
117120
pending_interrupts: BTreeMap::new(),
118121
});
119122

@@ -554,8 +557,7 @@ impl VCpu {
554557
for msg in vm::virtual_machines().recv_all_msgs() {
555558
match msg {
556559
vm::VirtualMachineMsg::GrantConsole(serial) => {
557-
*self.vm.config.physical_devices().serial.write() =
558-
Some(serial);
560+
*self.vm.host_devices.serial.write() = Some(serial);
559561
}
560562
vm::VirtualMachineMsg::CancelTimer(timer_id) => {
561563
time::cancel_timer(&timer_id)?;
@@ -577,8 +579,7 @@ impl VCpu {
577579
) -> Result<()> {
578580
let serial_info = self
579581
.vm
580-
.config
581-
.physical_devices()
582+
.host_devices
582583
.serial
583584
.read()
584585
.as_ref()
@@ -686,8 +687,7 @@ impl VCpu {
686687

687688
let serial = self
688689
.vm
689-
.config
690-
.physical_devices()
690+
.host_devices
691691
.serial
692692
.write()
693693
.take()
@@ -719,8 +719,7 @@ impl VCpu {
719719
})?;
720720
}
721721
virtdev::DeviceEventResponse::GuestUartTransmitted(val) => {
722-
if self.vm.config.physical_devices().serial.read().is_some()
723-
{
722+
if self.vm.host_devices.serial.read().is_some() {
724723
//TODO: This should be a write to the physical serial device
725724
let buff = &[val];
726725
let s = alloc::string::String::from_utf8_lossy(buff);

0 commit comments

Comments
 (0)