Skip to content

Commit 7f9048d

Browse files
committed
Hacky multi core guest support
1 parent 53fa186 commit 7f9048d

File tree

13 files changed

+374
-88
lines changed

13 files changed

+374
-88
lines changed

mythril/src/apic.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::error::{Error, Result};
44
use crate::time;
55
use crate::{declare_per_core, get_per_core, get_per_core_mut};
6+
use num_enum::TryFromPrimitive;
67
use raw_cpuid::CpuId;
78
use x86::msr;
89

@@ -17,7 +18,7 @@ const IA32_APIC_BASE_EXD: u64 = 1 << 10;
1718
/// BSP mask
1819
const IA32_APIC_BASE_BSP: u64 = 1 << 8;
1920

20-
#[derive(Debug)]
21+
#[derive(Debug, TryFromPrimitive)]
2122
#[repr(u8)]
2223
/// ICR destination shorthand values
2324
pub enum DstShorthand {
@@ -31,7 +32,7 @@ pub enum DstShorthand {
3132
AllExcludingSelf = 0x03,
3233
}
3334

34-
#[derive(Debug)]
35+
#[derive(Debug, TryFromPrimitive)]
3536
#[repr(u8)]
3637
/// INIT IPI Level
3738
pub enum Level {
@@ -41,7 +42,7 @@ pub enum Level {
4142
Assert = 0x01,
4243
}
4344

44-
#[derive(Debug)]
45+
#[derive(Debug, TryFromPrimitive)]
4546
#[repr(u8)]
4647
/// ICR trigger modes
4748
pub enum TriggerMode {
@@ -51,7 +52,7 @@ pub enum TriggerMode {
5152
Level = 0x01,
5253
}
5354

54-
#[derive(Debug)]
55+
#[derive(Debug, TryFromPrimitive)]
5556
#[repr(u8)]
5657
/// ICR mode of the Destination field
5758
pub enum DstMode {
@@ -61,7 +62,7 @@ pub enum DstMode {
6162
Logical = 0x01,
6263
}
6364

64-
#[derive(Debug)]
65+
#[derive(Debug, TryFromPrimitive)]
6566
#[repr(u8)]
6667
/// ICR delivery mode
6768
pub enum DeliveryMode {

mythril/src/emulate/memio.rs

Lines changed: 131 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,32 @@ use crate::memory;
33
use crate::virtdev::{
44
DeviceEvent, MemReadRequest, MemWriteRequest, ResponseEventArray,
55
};
6-
use crate::{vcpu, vmcs, vmexit};
6+
use crate::{vcpu, vm, vmcs, vmexit};
77
use arrayvec::ArrayVec;
88
use byteorder::ByteOrder;
99
use core::mem::size_of;
1010
use iced_x86;
11+
use x86::bits64::paging::BASE_PAGE_SIZE;
12+
13+
trait MemIoCallback:
14+
Fn(
15+
&mut vcpu::VCpu,
16+
memory::GuestPhysAddr,
17+
DeviceEvent,
18+
&mut ResponseEventArray,
19+
) -> Result<()>
20+
{
21+
}
22+
23+
impl<T> MemIoCallback for T where
24+
T: Fn(
25+
&mut vcpu::VCpu,
26+
memory::GuestPhysAddr,
27+
DeviceEvent,
28+
&mut ResponseEventArray,
29+
) -> Result<()>
30+
{
31+
}
1132

1233
macro_rules! read_register {
1334
($out:ident, $value:expr, $type:ty) => {{
@@ -138,6 +159,7 @@ fn do_mmio_write(
138159
guest_cpu: &mut vmexit::GuestCpuState,
139160
responses: &mut ResponseEventArray,
140161
instr: iced_x86::Instruction,
162+
on_write: impl MemIoCallback,
141163
) -> Result<()> {
142164
let mut res = ArrayVec::<[u8; 8]>::new();
143165
let data = match instr.op1_kind() {
@@ -169,11 +191,10 @@ fn do_mmio_write(
169191
};
170192
let request = MemWriteRequest::new(&data[..]);
171193

172-
let mut vm = vcpu.vm.write();
173-
vm.dispatch_event(
194+
(on_write)(
195+
vcpu,
174196
addr,
175197
crate::virtdev::DeviceEvent::MemWrite(addr, request),
176-
vcpu,
177198
responses,
178199
)
179200
}
@@ -184,6 +205,7 @@ fn do_mmio_read(
184205
guest_cpu: &mut vmexit::GuestCpuState,
185206
responses: &mut ResponseEventArray,
186207
instr: iced_x86::Instruction,
208+
on_read: impl MemIoCallback,
187209
) -> Result<()> {
188210
let (reg, size, offset) = match instr.op0_kind() {
189211
iced_x86::OpKind::Register => match instr.op_register(0) {
@@ -341,13 +363,7 @@ fn do_mmio_read(
341363

342364
let mut arr = [0u8; size_of::<u64>()];
343365
let request = MemReadRequest::new(&mut arr[..size]);
344-
let mut vm = vcpu.vm.write();
345-
vm.dispatch_event(
346-
addr,
347-
DeviceEvent::MemRead(addr, request),
348-
vcpu,
349-
responses,
350-
)?;
366+
(on_read)(vcpu, addr, DeviceEvent::MemRead(addr, request), responses)?;
351367

352368
let (value, mask) = match size {
353369
1 => (arr[0] as u64, ((u8::MAX as u64) << (offset * 8))),
@@ -372,11 +388,13 @@ fn do_mmio_read(
372388
Ok(())
373389
}
374390

375-
pub fn handle_ept_violation(
391+
fn process_memio_op(
392+
addr: memory::GuestPhysAddr,
376393
vcpu: &mut vcpu::VCpu,
377394
guest_cpu: &mut vmexit::GuestCpuState,
378-
_exit: vmexit::EptInformation,
379395
responses: &mut ResponseEventArray,
396+
on_read: impl MemIoCallback,
397+
on_write: impl MemIoCallback,
380398
) -> Result<()> {
381399
let instruction_len = vcpu
382400
.vmcs
@@ -406,21 +424,16 @@ pub fn handle_ept_violation(
406424
decoder.set_ip(ip);
407425
let instr = decoder.decode();
408426

409-
let addr = memory::GuestPhysAddr::new(
410-
vcpu.vmcs
411-
.read_field(vmcs::VmcsField::GuestPhysicalAddress)?,
412-
);
413-
414427
// For now, just assume everything is like MOV. This is obviously very
415428
// incomplete.
416429
if instr.op0_kind() == iced_x86::OpKind::Memory
417430
|| instr.op0_kind() == iced_x86::OpKind::Memory64
418431
{
419-
do_mmio_write(addr, vcpu, guest_cpu, responses, instr)?;
432+
do_mmio_write(addr, vcpu, guest_cpu, responses, instr, on_write)?;
420433
} else if instr.op1_kind() == iced_x86::OpKind::Memory
421434
|| instr.op1_kind() == iced_x86::OpKind::Memory64
422435
{
423-
do_mmio_read(addr, vcpu, guest_cpu, responses, instr)?;
436+
do_mmio_read(addr, vcpu, guest_cpu, responses, instr, on_read)?;
424437
} else {
425438
return Err(Error::InvalidValue(format!(
426439
"Unsupported mmio instruction: {:?} (rip=0x{:x}, bytes={:?})",
@@ -429,6 +442,103 @@ pub fn handle_ept_violation(
429442
bytes,
430443
)));
431444
}
432-
433445
Ok(())
434446
}
447+
448+
pub fn handle_ept_violation(
449+
vcpu: &mut vcpu::VCpu,
450+
guest_cpu: &mut vmexit::GuestCpuState,
451+
_exit: vmexit::EptInformation,
452+
responses: &mut ResponseEventArray,
453+
) -> Result<()> {
454+
fn on_ept_violation(
455+
vcpu: &mut vcpu::VCpu,
456+
addr: memory::GuestPhysAddr,
457+
event: DeviceEvent,
458+
responses: &mut ResponseEventArray,
459+
) -> Result<()> {
460+
let mut vm = vcpu.vm.write();
461+
vm.dispatch_event(addr, event, vcpu, responses)
462+
}
463+
464+
let addr = memory::GuestPhysAddr::new(
465+
vcpu.vmcs
466+
.read_field(vmcs::VmcsField::GuestPhysicalAddress)?,
467+
);
468+
469+
process_memio_op(
470+
addr,
471+
vcpu,
472+
guest_cpu,
473+
responses,
474+
on_ept_violation,
475+
on_ept_violation,
476+
)
477+
}
478+
479+
pub fn handle_apic_access(
480+
vcpu: &mut vcpu::VCpu,
481+
guest_cpu: &mut vmexit::GuestCpuState,
482+
exit: vmexit::ApicAccessInformation,
483+
responses: &mut ResponseEventArray,
484+
) -> Result<()> {
485+
fn address_to_apic_offset(addr: memory::GuestPhysAddr) -> u16 {
486+
let addr = addr.as_u64();
487+
let apic_base = vm::GUEST_LOCAL_APIC_ADDR.as_u64();
488+
assert!(
489+
addr >= apic_base && addr < (apic_base + BASE_PAGE_SIZE as u64)
490+
);
491+
((addr - apic_base) / size_of::<u32>() as u64) as u16
492+
}
493+
494+
fn on_apic_read(
495+
vcpu: &mut vcpu::VCpu,
496+
addr: memory::GuestPhysAddr,
497+
event: DeviceEvent,
498+
_responses: &mut ResponseEventArray,
499+
) -> Result<()> {
500+
let offset = address_to_apic_offset(addr);
501+
let res = vcpu.local_apic.register_read(offset)?;
502+
let mut bytes = res.to_be_bytes();
503+
504+
match event {
505+
DeviceEvent::MemRead(_, mut req) => {
506+
req.as_mut_slice().copy_from_slice(&mut bytes[..]);
507+
Ok(())
508+
}
509+
_ => return Err(Error::NotSupported),
510+
}
511+
}
512+
513+
fn on_apic_write(
514+
vcpu: &mut vcpu::VCpu,
515+
addr: memory::GuestPhysAddr,
516+
event: DeviceEvent,
517+
_responses: &mut ResponseEventArray,
518+
) -> Result<()> {
519+
let offset = address_to_apic_offset(addr);
520+
let mut bytes = [0u8; 4];
521+
let value = match event {
522+
DeviceEvent::MemWrite(_, req) => {
523+
bytes[..].copy_from_slice(req.as_slice());
524+
u32::from_be_bytes(bytes)
525+
}
526+
_ => return Err(Error::NotSupported),
527+
};
528+
529+
vcpu.local_apic.register_write(offset, value)
530+
}
531+
532+
let addr = vm::GUEST_LOCAL_APIC_ADDR
533+
+ (exit.offset.expect("Apic access with no offset") as usize
534+
* size_of::<u32>());
535+
536+
process_memio_op(
537+
addr,
538+
vcpu,
539+
guest_cpu,
540+
responses,
541+
on_apic_read,
542+
on_apic_write,
543+
)
544+
}

mythril/src/ioapic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub unsafe fn init_ioapics(madt: &MADT) -> Result<()> {
9898
},
9999
_ => None,
100100
}) {
101+
debug!("Registering IOAPIC for gsi_base = 0x{:x}", ioapic.gsi_base);
101102
ioapics.push(ioapic);
102103
}
103104
RoAfterInit::init(&IOAPICS, ioapics);

mythril/src/kmain.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ fn build_vm(
7171
flags: acpi::madt::LocalApicFlags::ENABLED,
7272
})
7373
.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");
7480
madt.add_ics(acpi::madt::Ics::IoApic {
7581
ioapic_id: 0,
7682
ioapic_addr: 0xfec00000 as *mut u8,
@@ -183,7 +189,7 @@ static LOGGER: logger::DirectLogger = logger::DirectLogger::new();
183189
pub unsafe extern "C" fn kmain_early(multiboot_info_addr: usize) -> ! {
184190
// Setup our (com0) logger
185191
log::set_logger(&LOGGER)
186-
.map(|()| log::set_max_level(log::LevelFilter::Info))
192+
.map(|()| log::set_max_level(log::LevelFilter::Debug))
187193
.expect("Failed to set logger");
188194

189195
let boot_info = if IS_MULTIBOOT_BOOT == 1 {

mythril/src/time.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ pub fn cancel_timer(id: &TimerId) -> Result<()> {
398398
vm::send_vm_msg_core(
399399
vm::VirtualMachineMsg::CancelTimer(id.clone()),
400400
id.core_id,
401+
true,
401402
)?;
402403
}
403404
Ok(())

0 commit comments

Comments
 (0)