Skip to content

Commit 6266398

Browse files
committed
Add more complete multi-core guest support
1 parent 3761494 commit 6266398

File tree

11 files changed

+205
-49
lines changed

11 files changed

+205
-49
lines changed

mythril/src/apic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ const IA32_APIC_BASE_BSP: u64 = 1 << 8;
2424
pub enum DstShorthand {
2525
/// No shorthand used
2626
NoShorthand = 0x00,
27-
// TODO(dlrobertson): Is there any reason to include self? AFAIK
28-
// SELF_IPI negates the need for it.
27+
/// Send only to myself
28+
MySelf = 0x01,
2929
/// Broadcast including myself
3030
AllIncludingSelf = 0x02,
3131
/// Broadcast excluding myself

mythril/src/emulate/memio.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,8 @@ pub fn handle_apic_access(
526526
_ => return Err(Error::NotSupported),
527527
};
528528

529-
vcpu.local_apic.register_write(offset, value)
529+
vcpu.local_apic
530+
.register_write(vcpu.vm.clone(), offset, value)
530531
}
531532

532533
let addr = vm::GUEST_LOCAL_APIC_ADDR

mythril/src/kmain.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,9 @@ fn build_vm(
5353
}
5454
};
5555

56-
let mut config = vm::VirtualMachineConfig::new(
57-
cfg.cpus.clone(),
58-
cfg.memory,
59-
physical_config,
60-
);
56+
let mut config =
57+
vm::VirtualMachineConfig::new(&cfg.cpus, cfg.memory, physical_config)
58+
.expect("Failed to create VirtualMachineConfig");
6159

6260
let mut acpi = acpi::rsdp::RSDPBuilder::<[_; 1024]>::new(
6361
ManagedMap::Owned(BTreeMap::new()),

mythril/src/lock/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
pub mod ro_after_init;
2+
3+
/// Provides a hint to the processor that it is in a spin loop
4+
#[inline(always)]
5+
pub fn relax_cpu() {
6+
unsafe {
7+
llvm_asm!("rep; nop" ::: "memory");
8+
}
9+
}

mythril/src/time.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,7 @@ impl TimerWheel {
392392
pub fn busy_wait(duration: core::time::Duration) {
393393
let start = now();
394394
while now() < start + duration {
395-
unsafe {
396-
// Relax the cpu
397-
llvm_asm!("rep; nop" ::: "memory");
398-
}
395+
crate::lock::relax_cpu();
399396
}
400397
}
401398

mythril/src/vcpu.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ pub fn mp_entry_point() -> ! {
4343
let vm_id = vm.read().id;
4444
let is_vm_bsp = vm.read().config.bsp_id() == core_id;
4545
let mut vcpu = VCpu::new(vm).expect("Failed to create vcpu");
46+
47+
// Increment the VM's count of ready cores
48+
vcpu.vm.read().notify_ready();
49+
50+
// Wait until all the cores are done with their early init
51+
while !vcpu.vm.read().all_cores_ready() {
52+
crate::lock::relax_cpu();
53+
}
54+
55+
// Cores other than this VM's BSP must wait for the INIT/SIPI to actually start
4656
if !is_vm_bsp {
4757
debug!("Waiting for init signal on core id '{}'", core_id);
4858
vcpu.wait_for_init()

mythril/src/virtdev/lapic.rs

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use crate::error::{Error, Result};
33
use crate::memory;
44
use crate::percore;
55
use crate::vm;
6+
use alloc::sync::Arc;
67
use core::convert::TryFrom;
8+
use core::sync::atomic::AtomicU32;
79
use num_enum::TryFromPrimitive;
810

911
#[derive(Debug)]
@@ -41,6 +43,26 @@ enum ApicRegisterSimpleOffset {
4143
TimerDivideConfig = 0x3e0,
4244
}
4345

46+
/// The portion of guest local APIC state related to logical addressing
47+
///
48+
/// This portion of state must be 'shared' with other cores, because the
49+
/// state of each local APIC's logical destination registers affects how
50+
/// logically addressed IPIs are transmitted. Therefore, this state is
51+
/// stored in the VirtualMachine instead of in each VCpu.
52+
pub struct LogicalApicState {
53+
pub logical_destination: AtomicU32,
54+
pub destination_format: AtomicU32,
55+
}
56+
57+
impl core::default::Default for LogicalApicState {
58+
fn default() -> Self {
59+
Self {
60+
logical_destination: AtomicU32::new(0),
61+
destination_format: AtomicU32::new(0),
62+
}
63+
}
64+
}
65+
4466
impl TryFrom<u16> for ApicRegisterOffset {
4567
type Error = Error;
4668

@@ -102,7 +124,7 @@ impl LocalApic {
102124

103125
// FIXME(alschwalm): The destination is actually a virtual local
104126
// apic id. We should convert that to a global core id for this.
105-
let core_id = percore::CoreId::from(dest);
127+
let core_id = percore::CoreId::from(dest >> 24);
106128

107129
debug!(
108130
"Sending startup message for address = {:?} to core {}",
@@ -118,38 +140,77 @@ impl LocalApic {
118140
Ok(())
119141
}
120142

121-
fn process_interrupt_command(&mut self, value: u32) -> Result<()> {
143+
fn process_interrupt_command(
144+
&mut self,
145+
vm: Arc<spin::RwLock<vm::VirtualMachine>>,
146+
value: u32,
147+
) -> Result<()> {
122148
let mode = DeliveryMode::try_from((value >> 8) as u8 & 0b111)?;
123149
match mode {
124-
DeliveryMode::StartUp => self.process_sipi_request(value)?,
150+
// TODO: for now, just ignore the INIT signal
151+
DeliveryMode::Init => return Ok(()),
152+
DeliveryMode::StartUp => return self.process_sipi_request(value),
125153
_ => (),
126154
}
127155

128156
let vector = value as u64 & 0xff;
129157
let dst_mode = DstMode::try_from((value >> 11 & 0b1) as u8)?;
158+
let shorthand = DstShorthand::try_from((value >> 18 & 0b11) as u8)?;
130159

160+
match shorthand {
161+
DstShorthand::AllIncludingSelf => {
162+
warn!("Unsupported local apic command register shorthand AllIncludingSelf");
163+
return Ok(());
164+
}
165+
DstShorthand::AllExcludingSelf => {
166+
for core in vm.read().config.cpus() {
167+
if *core == percore::read_core_id() {
168+
continue;
169+
}
170+
vm::send_vm_msg_core(vm::VirtualMachineMsg::GuestInterrupt{
171+
kind: crate::vcpu::InjectedInterruptType::ExternalInterrupt,
172+
vector: vector as u8
173+
}, *core, true)?
174+
}
175+
return Ok(());
176+
}
177+
DstShorthand::MySelf => {
178+
warn!("Unsupported local apic command register shorthand Self");
179+
return Ok(());
180+
}
181+
DstShorthand::NoShorthand => (),
182+
}
183+
184+
// No shorthand was used, so we should have an ICR destination of some sort
131185
if let Some(dest) = self.icr_destination {
132-
// info!("Value = 0x{:x}", value);
133-
// info!("Send interrupt vector 0x{:x} to dest = {} [mode={:?}]", vector, dest, dst_mode);
134-
135-
// FIXME: hack for time interrupt
136-
if vector == 0xec {
137-
vm::send_vm_msg_core(vm::VirtualMachineMsg::GuestInterrupt{
138-
kind: crate::vcpu::InjectedInterruptType::ExternalInterrupt,
139-
vector: vector as u8
140-
}, percore::CoreId::from(0x01), true)?
186+
match dst_mode {
187+
DstMode::Logical => {
188+
for core in vm.read().logical_apic_destination(dest)? {
189+
// FIXME(alschwalm): we need to support sending to ourselves (I think)
190+
if *core == percore::read_core_id() {
191+
continue;
192+
}
193+
vm::send_vm_msg_core(vm::VirtualMachineMsg::GuestInterrupt{
194+
kind: crate::vcpu::InjectedInterruptType::ExternalInterrupt,
195+
vector: vector as u8
196+
}, *core, true)?
197+
}
198+
}
199+
DstMode::Physical => {
200+
warn!("Unsupported Physical address for IPI vector=0x{:x} (dest=0x{:x}/short={:?}/mode={:?})",
201+
vector, dest, shorthand, mode);
202+
}
141203
}
204+
} else {
205+
warn!("IPI with no icr_destination vector=0x{:x} (dest={:?}/short={:?})",
206+
vector, dst_mode, shorthand);
142207
}
143208

144209
Ok(())
145210
}
146211

147212
pub fn register_read(&mut self, offset: u16) -> Result<u32> {
148213
let offset = ApicRegisterOffset::try_from(offset)?;
149-
// debug!(
150-
// "Read from virtual local apic: {:?}",
151-
// offset
152-
// );
153214
match offset {
154215
ApicRegisterOffset::Simple(ApicRegisterSimpleOffset::ApicId) => {
155216
// FIXME(alschwalm): we shouldn't really use the core id for this
@@ -159,11 +220,19 @@ impl LocalApic {
159220
}
160221
}
161222

162-
pub fn register_write(&mut self, offset: u16, value: u32) -> Result<()> {
223+
pub fn register_write(
224+
&mut self,
225+
vm: Arc<spin::RwLock<vm::VirtualMachine>>,
226+
offset: u16,
227+
value: u32,
228+
) -> Result<()> {
163229
let offset = ApicRegisterOffset::try_from(offset)?;
164230
match offset {
165231
ApicRegisterOffset::Simple(ref simple) => match simple {
166232
ApicRegisterSimpleOffset::EndOfInterrupt => (),
233+
ApicRegisterSimpleOffset::LogicalDestination => {
234+
vm.write().update_core_logical_destination(value);
235+
}
167236
_ => info!(
168237
"Write to virtual local apic: {:?}, value=0x{:x}",
169238
offset, value
@@ -172,13 +241,13 @@ impl LocalApic {
172241
ApicRegisterOffset::InterruptCommand(offset) => {
173242
match offset {
174243
0 => {
175-
self.process_interrupt_command(value)?;
244+
self.process_interrupt_command(vm, value)?;
176245

177246
// TODO(alschwalm): What is the expected behavior here?
178247
self.icr_destination = None;
179248
}
180249
1 => {
181-
self.icr_destination = Some(value >> 24);
250+
self.icr_destination = Some(value);
182251
}
183252
_ => unreachable!(),
184253
}

mythril/src/virtdev/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::error::{Error, Result};
22
use crate::memory::{GuestAddressSpaceViewMut, GuestPhysAddr};
3-
use crate::vcpu;
43
use alloc::collections::btree_map::BTreeMap;
54
use alloc::sync::Arc;
65
use alloc::vec::Vec;

0 commit comments

Comments
 (0)