Skip to content

Resolve #24, by emulating CPUID #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .mythril_githooks/pre-commit

This file was deleted.

7 changes: 7 additions & 0 deletions mythril/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mythril/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ serde_json = {version = "^1", default-features = false, features = ["alloc"] }
spin = "0.5"
ux = { version = "0.1.3", default-features = false }
managed = { version = "0.8.0", features = ["map", "alloc"], default-features = false }
bitfield = "0.13.2"

[dependencies.arrayvec]
version = "0.5.2"
Expand Down
216 changes: 196 additions & 20 deletions mythril/src/emulate/cpuid.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,207 @@
use crate::error::Result;
use crate::vcpu::VCpu;
use crate::{vcpu, vmexit};
use arrayvec::ArrayVec;
use bitfield::bitfield;
use core::convert::TryInto;
use raw_cpuid::CpuIdResult;

pub fn emulate_cpuid(
_vcpu: &mut vcpu::VCpu,
guest_cpu: &mut vmexit::GuestCpuState,
) -> Result<()> {
//FIXME: for now just use the actual cpuid
let mut res = raw_cpuid::native_cpuid::cpuid_count(
guest_cpu.rax as u32,
guest_cpu.rcx as u32,
);
const CPUID_NAME: u32 = 0;
const CPUID_MODEL_FAMILY_STEPPING: u32 = 1;
const CPUID_CACHE_TLB_INFO: u32 = 2;
const INTEL_CORE_CACHE_TOPOLOGY: u32 = 4;
const THERMAL_AND_POWER_MANAGEMENT: u32 = 6;
const STRUCTURED_EXTENDED_FEATURE_FLAGS: u32 = 7;
const ARCHITECTURAL_PERFORMANCE: u32 = 0xA;
const EXTENDED_TOPOLOGY_ENUMERATION: u32 = 0xB;
const PROCESSOR_EXTENDED_STATE_ENUMERATION: u32 = 0xD;
const V2_EXTENDED_TOPOLOGY_ENUMERATION: u32 = 0x1F;
const EXTENDED_FUNCTION_CPUID_INFORMATION: u32 = 0x80000000;
const CPUID_BRAND_STRING_1: u32 = 0x80000002;
const CPUID_BRAND_STRING_2: u32 = 0x80000003;
const CPUID_BRAND_STRING_3: u32 = 0x80000004;
const MAX_CPUID_INPUT: u32 = 0x80000008;
//todo //CPUID leaves above 2 and below 80000000H are visible only when
// // IA32_MISC_ENABLE[bit 22] has its default value of 0.

if guest_cpu.rax as u32 == 1 {
// Disable MTRR
res.edx &= !(1 << 12);
const NAME_CREATION_ERROR_MESSAGE: &'static str =
"Somehow bytes was not actually a 12 element array";

// Disable XSAVE
res.ecx &= !(1 << 26);
fn get_cpu_id_result(
vcpu: &vcpu::VCpu,
eax_in: u32,
ecx_in: u32,
) -> CpuIdResult {
let mut actual = raw_cpuid::native_cpuid::cpuid_count(eax_in, ecx_in);

// Hide hypervisor feature
res.ecx &= !(1 << 31);
match eax_in {
CPUID_NAME => cpuid_name(vcpu, actual),
CPUID_MODEL_FAMILY_STEPPING => cpuid_model_family_stepping(actual),
INTEL_CORE_CACHE_TOPOLOGY => intel_cache_topo(actual),
THERMAL_AND_POWER_MANAGEMENT => {
todo!("Portions of this output are per core, but presumably we don't support thermal and power management.\
Additionally there is stuff about APIC timers here, also unsure if supported.\
Need reasonable defaults probably though\
")
}
STRUCTURED_EXTENDED_FEATURE_FLAGS => {
// nothing here seems to suspicious so just return actual:
actual
}
ARCHITECTURAL_PERFORMANCE => {
// For now I assume performance counters are unsupported, but if one wanted
// to support performance counters this would need to be handled here, and other places
actual
}
EXTENDED_TOPOLOGY_ENUMERATION => {
todo!("This basically requires APIC stuff to be done.")
}
PROCESSOR_EXTENDED_STATE_ENUMERATION => actual,
// There are bunch more leaves after PROCESSOR_EXTENDED_STATE_ENUMERATION, however most of them seem unlikely to be used/ not relevant
V2_EXTENDED_TOPOLOGY_ENUMERATION => {
todo!("Requires APIC")
}
0x40000000..=0x4FFFFFFF => {
// these are software reserved.
actual
}
EXTENDED_FUNCTION_CPUID_INFORMATION => CpuIdResult {
eax: MAX_CPUID_INPUT,
ebx: 0,
ecx: 0,
edx: 0,
},
CPUID_BRAND_STRING_1..=CPUID_BRAND_STRING_3 => {
if vcpu.vm.override_cpu_name {
todo!("CPU Brand string not implemented yet")
}
actual
}
_ => {
unimplemented!("Unimplemented CPUID value: {:?}", actual);
}
}
}

bitfield! {
pub struct IntelCoreCacheTopologyEaxRes(u32);
impl Debug;
cache_type,set_cache_type:4,0;
cache_level,set_cache_level:7,5;
self_init_cache_level,set_self_init_cache_level:8;
fully_associative,set_fully_associative:9;
max_addressable_ids_logical,set_max_addressable_ids_logical:14,25;
max_addressable_ids_physical,set_max_addressable_ids_physical:26,31;
}

// Hide TSC deadline timer
res.ecx &= !(1 << 24);
} else if guest_cpu.rax as u32 == 0x0b {
res.edx = crate::percore::read_core_id().raw as u32;
fn intel_cache_topo(actual: CpuIdResult) -> CpuIdResult {
let mut cache_topo_eax = IntelCoreCacheTopologyEaxRes(actual.eax);
cache_topo_eax.set_max_addressable_ids_logical(todo!("waiting on apics"));
cache_topo_eax.set_max_addressable_ids_physical(todo!("waiting on apics"));
let eax = cache_topo_eax.0;
CpuIdResult {
eax,
//no changes should be required for these:
ebx: actual.ebx,
ecx: actual.ecx,
edx: actual.edx,
}
}

bitfield! {
pub struct IntelTypeFamilyModelSteppingIDEaxRes(u32);
impl Debug;
stepping_id, _: 3,0;
model,_:7,4;
family_id,_:11,8;
processor_type,_:13,12;
extended_model_id,_:19,16;
extended_family_id,_:27,20;
}

bitfield! {
pub struct BrandCFlushMaxIDsInitialAPIC(u32);
impl Debug;
brand_idx, _: 7,0;
cflush,_:15,8;
max_processor_ids,set_max_processor_ids:23,16;
apic_id, set_apic_id:31,24;
}

bitfield! {
pub struct FeatureInformationECX(u32);
impl Debug;
//there are a lot of features here, so only add the ones we care about for now.
xsave, set_xsave: 26;
hypervisor, set_hypervisor: 31;
}

bitfield! {
pub struct FeatureInformationEDX(u32);
impl Debug;
//there are a lot of features here, so only add the ones we care about for now.
mtrr, set_mtrr: 12;
}

fn cpuid_model_family_stepping(actual: CpuIdResult) -> CpuIdResult {
let family_model_stepping =
IntelTypeFamilyModelSteppingIDEaxRes(actual.eax);
//we can change family_model_stepping, but for now just use actual.
let eax = family_model_stepping.0;
let mut brand_cflush_max_initial = BrandCFlushMaxIDsInitialAPIC(actual.ebx);
brand_cflush_max_initial.set_apic_id(todo!("Waiting on virtual APICs"));
brand_cflush_max_initial
.set_max_processor_ids(todo!("Waiting on virtual APICs"));
let ebx = brand_cflush_max_initial.0;
let mut features_ecx = FeatureInformationECX(actual.ecx);
let mut features_edx = FeatureInformationEDX(actual.edx);
// I would have made type safe bindings for this but then I saw how many fields there where...

// Disable MTRR
features_edx.set_mtrr(false);

// Disable XSAVE
// ecx &= !(1 << 26);
features_ecx.set_xsave(false);

// Hide hypervisor feature
features_ecx.set_hypervisor(false);
let ecx = features_ecx.0;
let edx = features_edx.0;
CpuIdResult { eax, ebx, ecx, edx }
}

fn cpuid_name(vcpu: &VCpu, actual: CpuIdResult) -> CpuIdResult {
if vcpu.vm.override_cpu_name {
let cpu_name = "MythrilCPU__";
let bytes = cpu_name
.chars()
.map(|char| char as u8)
.collect::<ArrayVec<[u8; 12]>>();
let first_bytes: [u8; 4] =
bytes[0..4].try_into().expect(NAME_CREATION_ERROR_MESSAGE);
let second_bytes: [u8; 4] =
bytes[4..8].try_into().expect(NAME_CREATION_ERROR_MESSAGE);
let third_bytes: [u8; 4] =
bytes[8..12].try_into().expect(NAME_CREATION_ERROR_MESSAGE);
return CpuIdResult {
eax: MAX_CPUID_INPUT,
ebx: u32::from_le_bytes(first_bytes),
ecx: u32::from_le_bytes(second_bytes),
edx: u32::from_le_bytes(third_bytes),
};
}
actual
}

pub fn emulate_cpuid(vcpu: &mut VCpu, guest_cpu: &mut vmexit::GuestCpuState) -> Result<()> {
//FIXME: for now just use the actual cpuid
let mut res = raw_cpuid::native_cpuid::cpuid_count(
guest_cpu.rax as u32,
guest_cpu.rcx as u32,
);

let res = get_cpu_id_result(vcpu, guest_cpu.rax as u32, guest_cpu.rcx as u32);

guest_cpu.rax = res.eax as u64 | (guest_cpu.rax & 0xffffffff00000000);
guest_cpu.rbx = res.ebx as u64 | (guest_cpu.rbx & 0xffffffff00000000);
Expand Down
6 changes: 4 additions & 2 deletions mythril/src/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use core::mem;
use core::pin::Pin;
use x86::controlregs::{cr0, cr3, cr4};
use x86::msr;
use crate::virtdev::lapic::VirtualAPICID;

extern "C" {
pub fn vmlaunch_wrapper() -> u64;
Expand Down Expand Up @@ -51,7 +52,7 @@ pub fn mp_entry_point() -> ! {
vm
};

let mut vcpu = VCpu::new(vm).expect("Failed to create vcpu");
let mut vcpu = VCpu::new(vm,VirtualAPICID(0)).expect("Failed to create vcpu");

let vm_id = vm.id;
let is_vm_bsp = vm.bsp_id() == core_id;
Expand Down Expand Up @@ -114,14 +115,15 @@ impl VCpu {
/// VMEXIT.
pub fn new(
vm: Pin<&'static VirtualMachine>,
virtual_apic_id: VirtualAPICID
) -> Result<Pin<&'static mut Self>> {
let vmx = vmx::Vmx::enable()?;
let vmcs = vmcs::Vmcs::new()?.activate(vmx)?;

let vcpu = Self {
vm: vm,
vmcs: vmcs,
local_apic: virtdev::lapic::LocalApic::new(),
local_apic: virtdev::lapic::LocalApic::new(virtual_apic_id),
stack: get_per_core_mut!(HOST_STACK),
pending_interrupts: BTreeMap::new(),
};
Expand Down
29 changes: 22 additions & 7 deletions mythril/src/virtdev/lapic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,38 @@ impl TryFrom<u16> for ApicRegisterOffset {
return Err(Error::InvalidValue(format!(
"Invalid APIC register offset: 0x{:x}",
offset
)))
)));
}
};

Ok(res)
}
}

#[derive(Default)]
/// CPUID only returns APIC ID values up to 8 bits.
/// This does raise the question of what AMD are
/// going to do when they add more cores to threadripper.
/// And also what Xeon Phi does.
/// Apparently it does this:
/// "The local APIC registers have expanded fields for
/// the APIC ID, Logical APIC ID, and APIC Destination ID. "
/// https://www.intel.com/content/dam/www/public/us/en/
/// documents/product-briefs/xeon-phi
/// -coprocessor-system-software-developers-guide.pdf
///
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct VirtualAPICID(pub u8);

pub struct LocalApic {
icr_destination: Option<u32>,
pub virtual_apic_id: VirtualAPICID,
}

impl LocalApic {
pub fn new() -> Self {
pub fn new(virtual_apic_id: VirtualAPICID) -> Self {
LocalApic {
icr_destination: None,
virtual_apic_id,
}
}

Expand Down Expand Up @@ -167,9 +182,9 @@ impl LocalApic {
if *core == percore::read_core_id() {
continue;
}
vm::virtual_machines().send_msg_core(vm::VirtualMachineMsg::GuestInterrupt{
vm::virtual_machines().send_msg_core(vm::VirtualMachineMsg::GuestInterrupt {
kind: crate::vcpu::InjectedInterruptType::ExternalInterrupt,
vector: vector as u8
vector: vector as u8,
}, *core, true)?
}
return Ok(());
Expand All @@ -190,9 +205,9 @@ impl LocalApic {
if *core == percore::read_core_id() {
continue;
}
vm::virtual_machines().send_msg_core(vm::VirtualMachineMsg::GuestInterrupt{
vm::virtual_machines().send_msg_core(vm::VirtualMachineMsg::GuestInterrupt {
kind: crate::vcpu::InjectedInterruptType::ExternalInterrupt,
vector: vector as u8
vector: vector as u8,
}, *core, true)?
}
}
Expand Down
Loading