Skip to content
This repository has been archived by the owner on Sep 1, 2024. It is now read-only.

Commit

Permalink
Incomplete vmexit handling of HALT, INIT and SIPI Signals.
Browse files Browse the repository at this point in the history
  • Loading branch information
memN0ps committed Feb 24, 2024
1 parent f369649 commit 82c5c3d
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 4 deletions.
26 changes: 26 additions & 0 deletions hypervisor/src/intel/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,32 @@ pub fn cr2_write(val: u64) {
unsafe { x86::controlregs::cr2_write(val) };
}

/// Writes a value to the DR0 register.
pub fn dr0_write(val: u64) {
unsafe { x86::debugregs::dr0_write(val as _) };
}

/// Writes a value to the DR1 register.
pub fn dr1_write(val: u64) {
unsafe { x86::debugregs::dr1_write(val as _) };
}

/// Writes a value to the DR2 register.
pub fn dr2_write(val: u64) {
unsafe { x86::debugregs::dr2_write(val as _) };
}

/// Writes a value to the DR3 register.
pub fn dr3_write(val: u64) {
unsafe { x86::debugregs::dr3_write(val as _) };
}

/// Writes a value to the DR6 register.
pub fn dr6_write(val: u64) {
let dr6 = x86::debugregs::Dr6::from_bits_truncate(val as _);
unsafe { x86::debugregs::dr6_write(dr6) };
}

/// Disables maskable interrupts.
pub fn cli() {
unsafe { x86::irq::disable() };
Expand Down
113 changes: 109 additions & 4 deletions hypervisor/src/intel/vmexit/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use x86::bits64::rflags;
use x86::controlregs::Cr0;
use x86::cpuid::{cpuid, CpuId};
use x86::msr::{IA32_VMX_CR0_FIXED0, IA32_VMX_CR0_FIXED1, IA32_VMX_CR4_FIXED0, IA32_VMX_CR4_FIXED1};
use x86::segmentation::{CodeSegmentType, DataSegmentType};
use x86::segmentation::{CodeSegmentType, DataSegmentType, SystemDescriptorTypes64};
use x86::vmx::vmcs;
use x86::vmx::vmcs::control::SecondaryControls;
use x86::vmx::vmcs::control::{SecondaryControls, VPID};
use crate::intel::capture::GuestRegisters;
use crate::intel::support::{cr0, cr2_write, cr4, rdmsr, vmread, vmwrite};
use crate::intel::invvpid::{invvpid_single_context, VPID_TAG};
use crate::intel::support::{cr0, cr2_write, cr4, dr0_write, dr1_write, dr2_write, dr3_write, dr6_write, rdmsr, vmread, vmwrite};

pub fn handle_init_signal(guest_registers: &mut GuestRegisters) {
//
Expand Down Expand Up @@ -96,8 +97,112 @@ pub fn handle_init_signal(guest_registers: &mut GuestRegisters) {
vmwrite(vmcs::guest::RSP, 0u64);

//
// Handle GDTR and IDTR and LDTR
// Handle GDTR and IDTR
//
vmwrite(vmcs::guest::GDTR_BASE, 0u64);
vmwrite(vmcs::guest::GDTR_LIMIT, 0xffffu64);
vmwrite(vmcs::guest::IDTR_BASE, 0u64);
vmwrite(vmcs::guest::IDTR_LIMIT, 0xffffu64);

//
// Handle LDTR
//
vmwrite(vmcs::guest::LDTR_SELECTOR, 0u64);
vmwrite(vmcs::guest::LDTR_BASE, 0u64);
vmwrite(vmcs::guest::LDTR_LIMIT, 0xffffu64);
vmwrite(vmcs::guest::LDTR_ACCESS_RIGHTS, SystemDescriptorTypes64::LDT as u64);

//
// Handle TR
//

vmwrite(vmcs::guest::TR_SELECTOR, 0u64);
vmwrite(vmcs::guest::TR_BASE, 0u64);
vmwrite(vmcs::guest::TR_LIMIT, 0xffffu64);
vmwrite(vmcs::guest::TR_ACCESS_RIGHTS, SystemDescriptorTypes64::TssBusy as u64);

//
// DR0, DR1, DR2, DR3, DR6, DR7
//
dr0_write(0u64);
dr1_write(0u64);
dr2_write(0u64);
dr3_write(0u64);
dr6_write(0xffff0ff0u64);
vmwrite(vmcs::guest::DR7, 0x400u64);

//
// Set the guest registers r8-r15 to 0.
//
guest_registers.r8 = 0u64;
guest_registers.r9 = 0u64;
guest_registers.r10 = 0u64;
guest_registers.r11 = 0u64;
guest_registers.r12 = 0u64;
guest_registers.r13 = 0u64;
guest_registers.r14 = 0u64;
guest_registers.r15 = 0u64;

//
// Those registers are supposed to be cleared but that is not implemented here.
// - IA32_XSS
// - BNDCFGU
// - BND0-BND3
// - IA32_BNDCFGS
//

//
// Set Guest EFER, FS_BASE and GS_BASE to 0.
//

vmwrite(vmcs::guest::IA32_EFER_FULL, 0u64);
vmwrite(vmcs::guest::FS_BASE, 0u64);
vmwrite(vmcs::guest::GS_BASE, 0u64);

//
// Set IA32E_MODE_GUEST to 0.
//
vmwrite(vmcs::guest::IA32_EFER_HIGH, 0u64);

//
// Invalidate TLBs to be on the safe side. It is unclear whether TLBs are
// invalidated on INIT, as the Intel SDM contradicts itself. However, doing
// so is harmless, while failure to invalidate them when necessary can cause
// issues.
//
// "Asserting the INIT# pin on the processor invokes a similar response to a
// hardware reset. ... the TLBs and BTB are invalidated as with a hardware
// reset)."
//
// See: 9.1 INITIALIZATION OVERVIEW
//
// "
// | Register | Power up | Reset | INIT |
// +---------------------------+----------+---------+-----------+
// | Data and Code Cache, TLBs | Invalid | Invalid | Unchanged |
// ""
//
// See: Table 9-1. IA-32 and Intel 64 Processor States Following Power-up, Reset, or INIT
//
invvpid_single_context(VPID_TAG);

//
// "All the processors on the system bus (...) execute the multiple processor
// (MP) initialization protocol. ... The application (non-BSP) processors
// (APs) go into a Wait For Startup IPI (SIPI) state while the BSP is executing
// initialization code."
// See: 10.1 INITIALIZATION OVERVIEW
//
// "Upon receiving an INIT ..., the processor responds by beginning the
// initialization process of the processor core and the local APIC. The state
// of the local APIC following an INIT reset is the same as it is after a
// power-up or hardware reset ... . This state is also referred to at the
// "wait-for-SIPI" state."
//
// See: 10.4.7.3 Local APIC State After an INIT Reset ("Wait-for-SIPI" State)
//
let vmx_wait_for_sipi = 0x3u64;
vmwrite(vmcs::guest::ACTIVITY_STATE, vmx_wait_for_sipi);
}

/// Further adjusts CR0 considering the UnrestrictedGuest feature.
Expand Down

0 comments on commit 82c5c3d

Please sign in to comment.