diff --git a/.mythril_githooks/pre-commit b/.mythril_githooks/pre-commit deleted file mode 100755 index 63060eb..0000000 --- a/.mythril_githooks/pre-commit +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -diff=$(make check-fmt) -result=$? - -if [[ ${result} -ne 0 ]] ; then - cat <<\EOF -There are some code style issues, run `cargo fmt` first. -EOF - exit 1 -fi - -exit 0 diff --git a/mythril/Cargo.lock b/mythril/Cargo.lock index 1fc5619..2872b09 100644 --- a/mythril/Cargo.lock +++ b/mythril/Cargo.lock @@ -30,6 +30,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + [[package]] name = "bitflags" version = "1.2.1" @@ -156,6 +162,7 @@ version = "0.1.0" dependencies = [ "arraydeque", "arrayvec", + "bitfield", "bitflags", "byteorder", "iced-x86", diff --git a/mythril/Cargo.toml b/mythril/Cargo.toml index 6081519..81361a0 100644 --- a/mythril/Cargo.toml +++ b/mythril/Cargo.toml @@ -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" diff --git a/mythril/src/emulate/cpuid.rs b/mythril/src/emulate/cpuid.rs index 9c61c4e..ee63041 100644 --- a/mythril/src/emulate/cpuid.rs +++ b/mythril/src/emulate/cpuid.rs @@ -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::>(); + 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); diff --git a/mythril/src/vcpu.rs b/mythril/src/vcpu.rs index cc89816..9413302 100644 --- a/mythril/src/vcpu.rs +++ b/mythril/src/vcpu.rs @@ -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; @@ -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; @@ -114,6 +115,7 @@ impl VCpu { /// VMEXIT. pub fn new( vm: Pin<&'static VirtualMachine>, + virtual_apic_id: VirtualAPICID ) -> Result> { let vmx = vmx::Vmx::enable()?; let vmcs = vmcs::Vmcs::new()?.activate(vmx)?; @@ -121,7 +123,7 @@ impl VCpu { 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(), }; diff --git a/mythril/src/virtdev/lapic.rs b/mythril/src/virtdev/lapic.rs index d467d7f..8642fb8 100644 --- a/mythril/src/virtdev/lapic.rs +++ b/mythril/src/virtdev/lapic.rs @@ -95,7 +95,7 @@ impl TryFrom for ApicRegisterOffset { return Err(Error::InvalidValue(format!( "Invalid APIC register offset: 0x{:x}", offset - ))) + ))); } }; @@ -103,15 +103,30 @@ impl TryFrom for ApicRegisterOffset { } } -#[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, + 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, } } @@ -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(()); @@ -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)? } } diff --git a/mythril/src/vm.rs b/mythril/src/vm.rs index 1cec159..3515d4a 100644 --- a/mythril/src/vm.rs +++ b/mythril/src/vm.rs @@ -365,6 +365,7 @@ pub struct VirtualMachineConfig { /// The size of this machines physical address space in MiB pub memory: u64, + override_cpu_name: bool } impl VirtualMachineConfig { @@ -386,7 +387,8 @@ impl VirtualMachineConfig { images: ArrayVec::new(), virtual_devices: ArrayVec::new(), host_devices: physical_devices, - memory: memory, + memory, + override_cpu_name: true }) } @@ -445,6 +447,12 @@ pub struct VirtualMachine { /// The number of vcpus that are up and waiting to start cpus_ready: AtomicU32, + + + /// Whether to display the cpu name as "Mythril CPU" in cpuid + pub override_cpu_name: bool + + } impl VirtualMachine { @@ -462,7 +470,7 @@ impl VirtualMachine { // Prepare the portion of per-core local apic state that is stored at the // VM level (as needed for logical addressing) let mut logical_apic_states = BTreeMap::new(); - for core in config.cpus.iter() { + for core in config.cpus.as_slice() { logical_apic_states.insert( core.clone(), virtdev::lapic::LogicalApicState::default(), @@ -483,6 +491,7 @@ impl VirtualMachine { apic_access_page: Raw4kPage([0u8; 4096]), logical_apic_state: logical_apic_states, cpus_ready: AtomicU32::new(0), + override_cpu_name: config.override_cpu_name }) }