mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-22 02:53:23 +00:00
272 lines
8.8 KiB
Rust
272 lines
8.8 KiB
Rust
use super::vmcs::{VmcsFields, VmxExitReason};
|
|
use super::vmx_asm_wrapper::{vmx_vmread, vmx_vmwrite};
|
|
|
|
use crate::virt::kvm::vm;
|
|
use core::arch::asm;
|
|
use log::debug;
|
|
use system_error::SystemError;
|
|
use x86::vmx::vmcs::ro::GUEST_PHYSICAL_ADDR_FULL;
|
|
|
|
#[derive(FromPrimitive)]
|
|
#[allow(non_camel_case_types)]
|
|
pub enum APICExceptionVectors {
|
|
EXCEPTION_DIVIDE_ERROR,
|
|
EXCEPTION_DEBUG_BREAKPOINT,
|
|
EXCEPTION_NMI,
|
|
EXCEPTION_BREAKPOINT,
|
|
EXCEPTION_OVERFLOW,
|
|
EXCEPTION_BOUND_RANGE_EXCEEDED,
|
|
EXCEPTION_UNDEFINED_OPCODE,
|
|
EXCEPTION_NO_MATH_COPROCESSOR,
|
|
EXCEPTION_DOUBLE_FAULT,
|
|
EXCEPTION_RESERVED0,
|
|
EXCEPTION_INVALID_TASK_SEGMENT_SELECTOR,
|
|
EXCEPTION_SEGMENT_NOT_PRESENT,
|
|
EXCEPTION_STACK_SEGMENT_FAULT,
|
|
EXCEPTION_GENERAL_PROTECTION_FAULT,
|
|
EXCEPTION_PAGE_FAULT,
|
|
EXCEPTION_RESERVED1,
|
|
EXCEPTION_MATH_FAULT,
|
|
EXCEPTION_ALIGNMENT_CHECK,
|
|
EXCEPTION_MACHINE_CHECK,
|
|
EXCEPTION_SIMD_FLOATING_POINT_NUMERIC_ERROR,
|
|
EXCEPTION_VIRTUAL_EXCEPTION,
|
|
EXCEPTION_RESERVED2,
|
|
EXCEPTION_RESERVED3,
|
|
EXCEPTION_RESERVED4,
|
|
EXCEPTION_RESERVED5,
|
|
EXCEPTION_RESERVED6,
|
|
EXCEPTION_RESERVED7,
|
|
EXCEPTION_RESERVED8,
|
|
EXCEPTION_RESERVED9,
|
|
EXCEPTION_RESERVED10,
|
|
EXCEPTION_RESERVED11,
|
|
EXCEPTION_RESERVED12,
|
|
}
|
|
|
|
#[derive(FromPrimitive)]
|
|
#[allow(non_camel_case_types)]
|
|
pub enum InterruptType {
|
|
INTERRUPT_TYPE_EXTERNAL_INTERRUPT = 0,
|
|
INTERRUPT_TYPE_RESERVED = 1,
|
|
INTERRUPT_TYPE_NMI = 2,
|
|
INTERRUPT_TYPE_HARDWARE_EXCEPTION = 3,
|
|
INTERRUPT_TYPE_SOFTWARE_INTERRUPT = 4,
|
|
INTERRUPT_TYPE_PRIVILEGED_SOFTWARE_INTERRUPT = 5,
|
|
INTERRUPT_TYPE_SOFTWARE_EXCEPTION = 6,
|
|
INTERRUPT_TYPE_OTHER_EVENT = 7,
|
|
}
|
|
|
|
pub fn vmexit_vmx_instruction_executed() -> Result<(), SystemError> {
|
|
let valid: u32 = 1;
|
|
let vector: u32 = APICExceptionVectors::EXCEPTION_UNDEFINED_OPCODE as u32;
|
|
let interrupt_type = InterruptType::INTERRUPT_TYPE_HARDWARE_EXCEPTION as u32;
|
|
let deliver_code: u32 = 0;
|
|
let interrupt_info = valid << 31 | interrupt_type << 8 | deliver_code << 11 | vector;
|
|
vmx_vmwrite(
|
|
VmcsFields::CTRL_VM_ENTRY_INTR_INFO_FIELD as u32,
|
|
interrupt_info as u64,
|
|
)?;
|
|
vmx_vmwrite(VmcsFields::CTRL_VM_ENTRY_INSTR_LEN as u32, 0)?;
|
|
let rflags: u64 = vmx_vmread(VmcsFields::GUEST_RFLAGS as u32).unwrap() | 0x0001_0000; // set RF flags
|
|
vmx_vmwrite(VmcsFields::GUEST_RFLAGS as u32, rflags)?;
|
|
Ok(())
|
|
}
|
|
|
|
// pub fn vmexit_cpuid_handler(guest_cpu_context: &mut GuestCpuContext) -> Result<(), SystemError>{
|
|
// let rax = guest_cpu_context.rax;
|
|
// let rcx = guest_cpu_context.rcx;
|
|
// // let rdx = guest_cpu_context.rdx;
|
|
// // let rbx = guest_cpu_context.rbx;
|
|
// cpuid!(rax, rcx);
|
|
// unsafe{asm!("mov {}, rax", out(reg) guest_cpu_context.rax)};
|
|
// unsafe{asm!("mov {}, rcx", out(reg) guest_cpu_context.rcx)};
|
|
// unsafe{asm!("mov {}, rdx", out(reg) guest_cpu_context.rdx)};
|
|
// unsafe{asm!("mov {}, rbx", out(reg) guest_cpu_context.rbx)};
|
|
// Ok(())
|
|
// }
|
|
|
|
unsafe fn save_rpg() {
|
|
asm!(
|
|
"push rax",
|
|
"push rcx",
|
|
"push rdx",
|
|
"push rbx",
|
|
"push rbp",
|
|
"push rsi",
|
|
"push rdi",
|
|
"push r8",
|
|
"push r9",
|
|
"push r10",
|
|
"push r11",
|
|
"push r12",
|
|
"push r13",
|
|
"push r14",
|
|
"push r15",
|
|
);
|
|
}
|
|
|
|
unsafe fn restore_rpg() {
|
|
asm!(
|
|
"pop r15",
|
|
"pop r14",
|
|
"pop r13",
|
|
"pop r12",
|
|
"pop r11",
|
|
"pop r10",
|
|
"pop r9",
|
|
"pop r8",
|
|
"pop rdi",
|
|
"pop rsi",
|
|
"pop rbp",
|
|
"pop rbx",
|
|
"pop rdx",
|
|
"pop rcx",
|
|
"pop rax",
|
|
);
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[allow(dead_code)]
|
|
pub struct GuestCpuContext {
|
|
pub r15: u64,
|
|
pub r14: u64,
|
|
pub r13: u64,
|
|
pub r12: u64,
|
|
pub r11: u64,
|
|
pub r10: u64,
|
|
pub r9: u64,
|
|
pub r8: u64,
|
|
pub rdi: u64,
|
|
pub rsi: u64,
|
|
pub rbp: u64,
|
|
pub rbx: u64,
|
|
pub rdx: u64,
|
|
pub rcx: u64,
|
|
pub rax: u64,
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn vmx_return() {
|
|
debug!("vmx_return!");
|
|
unsafe { save_rpg() };
|
|
vmexit_handler();
|
|
// XMM registers are vector registers. They're renamed onto the FP/SIMD register file
|
|
// unsafe {asm!(
|
|
// "sub rsp, 60h",
|
|
// "movaps xmmword ptr [rsp + 0h], xmm0",
|
|
// "movaps xmmword ptr [rsp + 10h], xmm1",
|
|
// "movaps xmmword ptr [rsp + 20h], xmm2",
|
|
// "movaps xmmword ptr [rsp + 30h], xmm3",
|
|
// "movaps xmmword ptr [rsp + 40h], xmm4",
|
|
// "movaps xmmword ptr [rsp + 50h], xmm5",
|
|
|
|
// "mov rdi, rsp",
|
|
// "sub rsp, 20h",
|
|
// "call vmexit_handler",
|
|
// "add rsp, 20h",
|
|
|
|
// "movaps xmm0, xmmword ptr [rsp + 0h]",
|
|
// "movaps xmm1, xmmword ptr [rsp + 10h]",
|
|
// "movaps xmm2, xmmword ptr [rsp + 20h]",
|
|
// "movaps xmm3, xmmword ptr [rsp + 30h]",
|
|
// "movaps xmm4, xmmword ptr [rsp + 40h]",
|
|
// "movaps xmm5, xmmword ptr [rsp + 50h]",
|
|
// "add rsp, 60h",
|
|
// clobber_abi("C"),
|
|
// )};
|
|
unsafe { restore_rpg() };
|
|
unsafe { asm!("vmresume",) };
|
|
}
|
|
|
|
#[no_mangle]
|
|
extern "C" fn vmexit_handler() {
|
|
// let guest_cpu_context = unsafe { guest_cpu_context_ptr.as_mut().unwrap() };
|
|
// debug!("guest_cpu_context_ptr={:p}",guest_cpu_context_ptr);
|
|
debug!("vmexit handler!");
|
|
|
|
let exit_reason = vmx_vmread(VmcsFields::VMEXIT_EXIT_REASON as u32).unwrap() as u32;
|
|
let exit_basic_reason = exit_reason & 0x0000_ffff;
|
|
let guest_rip = vmx_vmread(VmcsFields::GUEST_RIP as u32).unwrap();
|
|
// let guest_rsp = vmx_vmread(VmcsFields::GUEST_RSP as u32).unwrap();
|
|
debug!("guest_rip={:x}", guest_rip);
|
|
let _guest_rflags = vmx_vmread(VmcsFields::GUEST_RFLAGS as u32).unwrap();
|
|
|
|
match VmxExitReason::from(exit_basic_reason as i32) {
|
|
VmxExitReason::VMCALL
|
|
| VmxExitReason::VMCLEAR
|
|
| VmxExitReason::VMLAUNCH
|
|
| VmxExitReason::VMPTRLD
|
|
| VmxExitReason::VMPTRST
|
|
| VmxExitReason::VMREAD
|
|
| VmxExitReason::VMRESUME
|
|
| VmxExitReason::VMWRITE
|
|
| VmxExitReason::VMXOFF
|
|
| VmxExitReason::VMXON
|
|
| VmxExitReason::VMFUNC
|
|
| VmxExitReason::INVEPT
|
|
| VmxExitReason::INVVPID => {
|
|
debug!("vmexit handler: vmx instruction!");
|
|
vmexit_vmx_instruction_executed().expect("previledge instruction handle error");
|
|
}
|
|
VmxExitReason::CPUID => {
|
|
debug!("vmexit handler: cpuid instruction!");
|
|
// vmexit_cpuid_handler(guest_cpu_context);
|
|
adjust_rip(guest_rip).unwrap();
|
|
}
|
|
VmxExitReason::RDMSR => {
|
|
debug!("vmexit handler: rdmsr instruction!");
|
|
adjust_rip(guest_rip).unwrap();
|
|
}
|
|
VmxExitReason::WRMSR => {
|
|
debug!("vmexit handler: wrmsr instruction!");
|
|
adjust_rip(guest_rip).unwrap();
|
|
}
|
|
VmxExitReason::TRIPLE_FAULT => {
|
|
debug!("vmexit handler: triple fault!");
|
|
adjust_rip(guest_rip).unwrap();
|
|
}
|
|
VmxExitReason::EPT_VIOLATION => {
|
|
debug!("vmexit handler: ept violation!");
|
|
let gpa = vmx_vmread(GUEST_PHYSICAL_ADDR_FULL).unwrap();
|
|
let exit_qualification = vmx_vmread(VmcsFields::VMEXIT_QUALIFICATION as u32).unwrap();
|
|
/* It is a write fault? */
|
|
let mut error_code = exit_qualification & (1 << 1);
|
|
/* It is a fetch fault? */
|
|
error_code |= (exit_qualification << 2) & (1 << 4);
|
|
/* ept page table is present? */
|
|
error_code |= (exit_qualification >> 3) & (1 << 0);
|
|
|
|
let kvm = vm(0).unwrap();
|
|
let vcpu = kvm.vcpu[0].clone();
|
|
// Use the data
|
|
let kvm_ept_page_fault = vcpu.lock().mmu.page_fault.unwrap();
|
|
kvm_ept_page_fault(&mut vcpu.lock(), gpa, error_code as u32, false)
|
|
.expect("ept page fault error");
|
|
}
|
|
_ => {
|
|
debug!(
|
|
"vmexit handler: unhandled vmexit reason: {}!",
|
|
exit_basic_reason
|
|
);
|
|
|
|
let info = vmx_vmread(VmcsFields::VMEXIT_INSTR_LEN as u32).unwrap() as u32;
|
|
debug!("vmexit handler: VMEXIT_INSTR_LEN: {}!", info);
|
|
let info = vmx_vmread(VmcsFields::VMEXIT_INSTR_INFO as u32).unwrap() as u32;
|
|
debug!("vmexit handler: VMEXIT_INSTR_INFO: {}!", info);
|
|
let info = vmx_vmread(VmcsFields::CTRL_EXPECTION_BITMAP as u32).unwrap() as u32;
|
|
debug!("vmexit handler: CTRL_EXPECTION_BITMAP: {}!", info);
|
|
|
|
adjust_rip(guest_rip).unwrap();
|
|
// panic!();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn adjust_rip(rip: u64) -> Result<(), SystemError> {
|
|
let instruction_length = vmx_vmread(VmcsFields::VMEXIT_INSTR_LEN as u32)?;
|
|
vmx_vmwrite(VmcsFields::GUEST_RIP as u32, rip + instruction_length)?;
|
|
Ok(())
|
|
}
|