fix kernel panic

This commit is contained in:
Masato Imai
2025-06-13 14:47:02 +00:00
parent 6584d8dab6
commit dc98f35fef
9 changed files with 237 additions and 22 deletions

BIN
bzImage

Binary file not shown.

View File

@ -30,6 +30,11 @@ macro_rules! info {
($($arg:tt)*) => ($crate::print!("[info] {}\n", format_args!($($arg)*))); ($($arg:tt)*) => ($crate::print!("[info] {}\n", format_args!($($arg)*)));
} }
#[macro_export]
macro_rules! error {
($($arg:tt)*) => ($crate::print!("[error] {}\n", format_args!($($arg)*)));
}
#[doc(hidden)] #[doc(hidden)]
pub fn _print(args: fmt::Arguments) { pub fn _print(args: fmt::Arguments) {
use core::fmt::Write; use core::fmt::Write;

View File

@ -1,17 +1,48 @@
use crate::info;
use raw_cpuid::cpuid; use raw_cpuid::cpuid;
use super::{vcpu::VCpu, vmcs::VmxLeaf}; use super::{vcpu::VCpu, vmcs::VmxLeaf};
pub fn handle_cpuid_exit(vcpu: &mut VCpu) { pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
let regs = &mut vcpu.guest_registers; let regs = &mut vcpu.guest_registers;
//let vendor: &[u8; 12] = b"miHypervisor"; let vendor: &[u8; 12] = b"miHypervisor";
let vendor: &[u8; 12] = b"KVMKVMKVMKVM";
let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) }; let vendor = unsafe { core::mem::transmute::<&[u8; 12], &[u32; 3]>(vendor) };
match VmxLeaf::from(regs.rax) { match VmxLeaf::from(regs.rax) {
VmxLeaf::EXTENDED_ENUMERATION => {
info!("CPUID: {:#x}.{:#x}", regs.rax, regs.rcx);
match regs.rcx {
0 => {
// EAX: supported XSAVE features (x87=bit0, SSE=bit1)
regs.rax = 0b11; // Only x87 and SSE supported
regs.rbx = 576; // Size required for enabled features
regs.rcx = 576; // Size required for all features
regs.rdx = 0x00000000;
}
1 => {
// EAX: XSAVEOPT and extended features
regs.rax = 0x00000001; // XSAVEOPT supported
regs.rbx = 0; // Size of enabled features in compacted format
regs.rcx = 0; // Lower 32 bits of XCR0 | IA32_XSS
regs.rdx = 0; // Upper 32 bits of XCR0 | IA32_XSS
}
2 => {
// x87 state component
regs.rax = 512; // Size of x87 state
regs.rbx = 0; // Offset in save area
regs.rcx = 0; // Not used for legacy components
regs.rdx = 0; // Not used for legacy components
}
_ => {
invalid(vcpu);
}
}
}
VmxLeaf::EXTENDED_FEATURE => match regs.rcx { VmxLeaf::EXTENDED_FEATURE => match regs.rcx {
0 => { 0 => {
let mut ebx = ExtFeatureEbx0::default(); let mut ebx = ExtFeatureEbx0::default();
ebx.fsgsbase = false;
ebx.smep = true; ebx.smep = true;
ebx.invpcid = false; ebx.invpcid = false;
ebx.smap = true; ebx.smap = true;
@ -70,15 +101,15 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
_reserved_0: false, _reserved_0: false,
pcid: true, pcid: true,
dca: false, dca: false,
sse4_1: false, sse4_1: true,
sse4_2: false, sse4_2: true,
x2apic: false, x2apic: false,
movbe: false, movbe: false,
popcnt: false, popcnt: false,
tsc_deadline: false, tsc_deadline: false,
aesni: false, aesni: false,
xsave: false, xsave: true,
osxsave: false, osxsave: true,
avx: false, avx: false,
f16c: false, f16c: false,
rdrand: false, rdrand: false,
@ -94,7 +125,7 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
pae: true, pae: true,
mce: false, mce: false,
cx8: true, cx8: true,
apic: false, apic: true,
_reserved_0: false, _reserved_0: false,
sep: true, sep: true,
mtrr: false, mtrr: false,
@ -118,9 +149,14 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
_reserved_2: false, _reserved_2: false,
pbe: false, pbe: false,
}; };
let cpuid = cpuid!(0x1, 0); let mut version_and_feature_info = cpuid!(0x1, 0);
regs.rax = cpuid.eax as u64; let feature = cpuid!(0x0, 0);
regs.rbx = cpuid.ebx as u64; if !((feature.ecx & (1 << 17)) == 1) {
version_and_feature_info.ecx &= !(1 << 17);
}
regs.rax = version_and_feature_info.eax as u64;
regs.rbx = version_and_feature_info.ebx as u64;
regs.rcx = ecx.to_u32() as u64; regs.rcx = ecx.to_u32() as u64;
regs.rdx = edx.to_u32() as u64; regs.rdx = edx.to_u32() as u64;
} }
@ -132,6 +168,7 @@ pub fn handle_cpuid_exit(vcpu: &mut VCpu) {
fn invalid(vcpu: &mut VCpu) { fn invalid(vcpu: &mut VCpu) {
let regs = &mut vcpu.guest_registers; let regs = &mut vcpu.guest_registers;
//info!("Invalid CPUID: {:#x}.{:#x}", regs.rax, regs.rcx);
regs.rax = 0; regs.rax = 0;
regs.rbx = 0; regs.rbx = 0;

View File

@ -6,8 +6,6 @@ use x86::{
vmx::vmcs, vmx::vmcs,
}; };
use crate::info;
use super::{ use super::{
qual::{AccessType, QualCr, Register}, qual::{AccessType, QualCr, Register},
vcpu::VCpu, vcpu::VCpu,

75
src/vmm/fpu.rs Normal file
View File

@ -0,0 +1,75 @@
use bitfield::bitfield;
use crate::{error, info};
use super::vcpu::VCpu;
bitfield! {
pub struct XCR0(u64);
pub x87, set_x87: 0, 1;
pub sse, set_sse: 1, 1;
pub avx, set_avx: 2, 1;
pub bndreg, set_bndreg: 3, 1;
pub bndcsr, set_bndcsr: 4, 1;
pub opmask, set_opmask: 5, 1;
pub zmm_hi256, set_zmm_hi256: 6, 1;
pub hi16_zmm, set_hi16_zmm: 7, 1;
pub pt, set_pt: 8, 1;
pub pkru, set_pkru: 9, 1;
pub pasid, set_pasid: 10, 1;
pub cet_u, set_cet_u: 11, 1;
pub cet_s, set_cet_s: 12, 1;
pub hdc, set_hdc: 13, 1;
pub intr, set_intr: 14, 1;
pub lbr, set_lbr: 15, 1;
pub hwp, set_hwp: 16, 1;
pub xtilecfg, set_xtilecfg: 17, 1;
pub xtiledata, set_xtiledata: 18, 1;
pub apx, set_apx: 19, 1;
pub reserved, set_reserved: 20, 44;
}
pub fn set_xcr(vcpu: &mut VCpu, index: u32, xcr: u64) -> Result<(), ()> {
info!("Setting XCR0: index={}, xcr={:x}", index, xcr);
if index != 0 {
error!("Invalid XCR index: {}", index);
return Err(());
}
if !(xcr & 0b1 != 0) {
error!("X87 is not enabled");
return Err(());
}
if (xcr & 0b100 != 0) && !(xcr & 0b10 != 0) {
error!("SSE is not enabled");
return Err(());
}
if !(xcr & 0b1000) != (!(xcr & 0b10000)) {
error!("BNDREGS and BNDCSR are not both enabled");
return Err(());
}
if xcr & 0b11100000 != 0 {
if !(xcr & 0b100 != 0) {
error!("YMM bits are not enabled");
return Err(());
}
if (xcr & 0b11100000) != 0b11100000 {
error!("Invalid bits set in XCR0");
return Err(());
}
}
if (xcr & 0b1000000000000 != 0) && (xcr & 0b1000000000000 != 0b1000000000000) {
error!("xtile bits are not both enabled");
return Err(());
}
vcpu.xcr0 = XCR0(xcr);
Ok(())
}

View File

@ -3,6 +3,7 @@ pub mod cpuid;
pub mod cr; pub mod cr;
pub mod ept; pub mod ept;
pub mod error; pub mod error;
pub mod fpu;
pub mod linux; pub mod linux;
pub mod msr; pub mod msr;
pub mod qual; pub mod qual;

View File

@ -128,7 +128,7 @@ impl ShadowMsr {
} }
x86::msr::IA32_KERNEL_GSBASE => Self::shadow_read(vcpu, msr_kind), x86::msr::IA32_KERNEL_GSBASE => Self::shadow_read(vcpu, msr_kind),
_ => { _ => {
panic!("Unhandled RDMSR: {}", msr_kind); panic!("Unhandled RDMSR: {:#x}", msr_kind);
} }
} }
} }
@ -145,6 +145,7 @@ impl ShadowMsr {
x86::msr::IA32_TSC_AUX => Self::shadow_write(vcpu, msr_kind), x86::msr::IA32_TSC_AUX => Self::shadow_write(vcpu, msr_kind),
x86::msr::IA32_FMASK => Self::shadow_write(vcpu, msr_kind), x86::msr::IA32_FMASK => Self::shadow_write(vcpu, msr_kind),
x86::msr::IA32_KERNEL_GSBASE => Self::shadow_write(vcpu, msr_kind), x86::msr::IA32_KERNEL_GSBASE => Self::shadow_write(vcpu, msr_kind),
x86::msr::MSR_C5_PMON_BOX_CTRL => Self::shadow_write(vcpu, msr_kind),
x86::msr::SYSENTER_CS_MSR => unsafe { x86::msr::SYSENTER_CS_MSR => unsafe {
vmwrite(vmcs::guest::IA32_SYSENTER_CS, value).unwrap() vmwrite(vmcs::guest::IA32_SYSENTER_CS, value).unwrap()
}, },
@ -157,8 +158,9 @@ impl ShadowMsr {
x86::msr::IA32_EFER => unsafe { vmwrite(vmcs::guest::IA32_EFER_FULL, value).unwrap() }, x86::msr::IA32_EFER => unsafe { vmwrite(vmcs::guest::IA32_EFER_FULL, value).unwrap() },
x86::msr::IA32_FS_BASE => unsafe { vmwrite(vmcs::guest::FS_BASE, value).unwrap() }, x86::msr::IA32_FS_BASE => unsafe { vmwrite(vmcs::guest::FS_BASE, value).unwrap() },
x86::msr::IA32_GS_BASE => unsafe { vmwrite(vmcs::guest::GS_BASE, value).unwrap() }, x86::msr::IA32_GS_BASE => unsafe { vmwrite(vmcs::guest::GS_BASE, value).unwrap() },
_ => { _ => {
panic!("Unhandled WRMSR: {}", msr_kind); panic!("Unhandled WRMSR: {:#x}", msr_kind);
} }
} }
} }

View File

@ -125,3 +125,16 @@ impl QualCr {
self.set_register_raw(val as u8); self.set_register_raw(val as u8);
} }
} }
bitfield! {
#[derive(Clone, Copy)]
pub struct QualIo(u64);
impl Debug;
u8, size, set_size: 3, 0;
u8, direction, set_direction: 4, 4;
u8, string, set_string: 5, 5;
u8, rep, set_rep: 6, 6;
u8, operand_encoding, set_operand_encoding: 7, 7;
u16, port, set_port: 31, 16;
}

View File

@ -1,4 +1,7 @@
use core::u64; use core::{
arch::x86_64::{_xgetbv, _xsetbv},
u64,
};
use x86::{ use x86::{
bits64::vmx::{vmread, vmwrite}, bits64::vmx::{vmread, vmwrite},
@ -13,7 +16,7 @@ use crate::{
info, info,
memory::BootInfoFrameAllocator, memory::BootInfoFrameAllocator,
vmm::{ vmm::{
cpuid, cr, msr, cpuid, cr, fpu, msr,
qual::QualCr, qual::QualCr,
vmcs::{ vmcs::{
DescriptorType, EntryControls, Granularity, PrimaryExitControls, DescriptorType, EntryControls, Granularity, PrimaryExitControls,
@ -25,6 +28,7 @@ use crate::{
use super::{ use super::{
ept::{EPT, EPTP}, ept::{EPT, EPTP},
fpu::XCR0,
linux::{self, BootParams, E820Type}, linux::{self, BootParams, E820Type},
msr::ShadowMsr, msr::ShadowMsr,
register::GuestRegisters, register::GuestRegisters,
@ -44,6 +48,8 @@ pub struct VCpu {
pub host_msr: ShadowMsr, pub host_msr: ShadowMsr,
pub guest_msr: ShadowMsr, pub guest_msr: ShadowMsr,
pub ia32e_enabled: bool, pub ia32e_enabled: bool,
pub xcr0: XCR0,
pub host_xcr0: u64,
} }
const TEMP_STACK_SIZE: usize = 4096; const TEMP_STACK_SIZE: usize = 4096;
@ -68,6 +74,8 @@ impl VCpu {
host_msr: ShadowMsr::new(), host_msr: ShadowMsr::new(),
guest_msr: ShadowMsr::new(), guest_msr: ShadowMsr::new(),
ia32e_enabled: false, ia32e_enabled: false,
xcr0: XCR0(3),
host_xcr0: 0,
} }
} }
@ -200,6 +208,9 @@ impl VCpu {
rdmsr(x86::msr::IA32_KERNEL_GSBASE) as u64, rdmsr(x86::msr::IA32_KERNEL_GSBASE) as u64,
) )
.unwrap(); .unwrap();
self.host_msr
.set(x86::msr::MSR_C5_PMON_BOX_CTRL, 0)
.unwrap();
self.guest_msr.set(x86::msr::IA32_TSC_AUX, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_TSC_AUX, 0).unwrap();
self.guest_msr.set(x86::msr::IA32_STAR, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_STAR, 0).unwrap();
@ -207,6 +218,9 @@ impl VCpu {
self.guest_msr.set(x86::msr::IA32_CSTAR, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_CSTAR, 0).unwrap();
self.guest_msr.set(x86::msr::IA32_FMASK, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_FMASK, 0).unwrap();
self.guest_msr.set(x86::msr::IA32_KERNEL_GSBASE, 0).unwrap(); self.guest_msr.set(x86::msr::IA32_KERNEL_GSBASE, 0).unwrap();
self.guest_msr
.set(x86::msr::MSR_C5_PMON_BOX_CTRL, 0)
.unwrap();
vmwrite( vmwrite(
vmcs::control::VMEXIT_MSR_LOAD_ADDR_FULL, vmcs::control::VMEXIT_MSR_LOAD_ADDR_FULL,
@ -286,10 +300,11 @@ impl VCpu {
primary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32; primary_exec_ctrl.0 |= (reserved_bits & 0xFFFFFFFF) as u32;
primary_exec_ctrl.0 &= (reserved_bits >> 32) as u32; primary_exec_ctrl.0 &= (reserved_bits >> 32) as u32;
primary_exec_ctrl.set_hlt(true); primary_exec_ctrl.set_hlt(false);
primary_exec_ctrl.set_activate_secondary_controls(true); primary_exec_ctrl.set_activate_secondary_controls(true);
primary_exec_ctrl.set_use_tpr_shadow(true); primary_exec_ctrl.set_use_tpr_shadow(true);
primary_exec_ctrl.set_use_msr_bitmap(false); primary_exec_ctrl.set_use_msr_bitmap(false);
primary_exec_ctrl.set_unconditional_io(true);
primary_exec_ctrl.write(); primary_exec_ctrl.write();
@ -365,6 +380,14 @@ impl VCpu {
exit_ctrl.write(); exit_ctrl.write();
unsafe {
vmwrite(
vmcs::control::EXCEPTION_BITMAP,
0, /*(1u64 << irq::INVALID_OPCODE_VECTOR)*/
)
.unwrap();
};
Ok(()) Ok(())
} }
@ -373,7 +396,10 @@ impl VCpu {
unsafe { unsafe {
vmwrite(vmcs::host::CR0, cr0().bits() as u64)?; vmwrite(vmcs::host::CR0, cr0().bits() as u64)?;
vmwrite(vmcs::host::CR3, cr3())?; vmwrite(vmcs::host::CR3, cr3())?;
vmwrite(vmcs::host::CR4, cr4().bits() as u64)?; vmwrite(
vmcs::host::CR4,
cr4().bits() as u64 | Cr4Flags::OSXSAVE.bits(),
)?;
vmwrite(vmcs::host::RIP, crate::vmm::asm::asm_vmexit_handler as u64)?; vmwrite(vmcs::host::RIP, crate::vmm::asm::asm_vmexit_handler as u64)?;
vmwrite( vmwrite(
@ -433,10 +459,7 @@ impl VCpu {
vmwrite(vmcs::guest::CR3, cr3())?; vmwrite(vmcs::guest::CR3, cr3())?;
vmwrite( vmwrite(
vmcs::guest::CR4, vmcs::guest::CR4,
vmread(vmcs::guest::CR4)? vmread(vmcs::guest::CR4)? | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits(),
| 1 << 5
| 1 << 7
| Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits(),
)?; )?;
vmwrite(vmcs::guest::CS_BASE, 0)?; vmwrite(vmcs::guest::CS_BASE, 0)?;
@ -570,13 +593,57 @@ impl VCpu {
} }
} }
fn load_guest_xcr0(&mut self) -> Result<(), VmFail> {
let host_cr4 = unsafe { cr4() };
if (host_cr4.bits() & Cr4Flags::OSXSAVE.bits() as usize) == 0 {
return Ok(());
}
if self.host_xcr0 == 0 {
self.host_xcr0 = unsafe { _xgetbv(0) };
}
let guest_cr4 = unsafe { vmread(vmcs::guest::CR4)? };
if guest_cr4 & Cr4Flags::OSXSAVE.bits() != 0 && self.xcr0.0 != self.host_xcr0 {
unsafe {
_xsetbv(0, self.xcr0.0);
}
}
Ok(())
}
fn load_host_xcr0(&mut self) -> Result<(), VmFail> {
let host_cr4 = unsafe { cr4() };
if (host_cr4.bits() & Cr4Flags::OSXSAVE.bits() as usize) == 0 {
return Ok(());
}
let guest_cr4 = unsafe { vmread(vmcs::guest::CR4)? };
if guest_cr4 & Cr4Flags::OSXSAVE.bits() != 0 {
let current_xcr0 = unsafe { _xgetbv(0) };
if current_xcr0 != self.host_xcr0 {
self.xcr0 = XCR0(current_xcr0);
unsafe {
_xsetbv(0, self.host_xcr0);
}
}
}
Ok(())
}
fn vmentry(&mut self) -> Result<(), InstructionError> { fn vmentry(&mut self) -> Result<(), InstructionError> {
let success = { let success = {
let result: u16; let result: u16;
self.load_guest_xcr0().unwrap();
unsafe { unsafe {
result = crate::vmm::asm::asm_vm_entry(self as *mut _); result = crate::vmm::asm::asm_vm_entry(self as *mut _);
}; };
self.load_host_xcr0().unwrap();
result == 0 result == 0
}; };
@ -627,6 +694,7 @@ impl VCpu {
_ => {} _ => {}
} }
} else { } else {
info!("RIP: {:#x}", unsafe { vmread(vmcs::guest::RIP) }.unwrap());
match info.get_reason() { match info.get_reason() {
VmxExitReason::HLT => { VmxExitReason::HLT => {
info!("HLT instruction executed"); info!("HLT instruction executed");
@ -649,6 +717,22 @@ impl VCpu {
cr::handle_cr_access(self, &qual); cr::handle_cr_access(self, &qual);
self.step_next_inst().unwrap(); self.step_next_inst().unwrap();
} }
VmxExitReason::XSETBV => {
fpu::set_xcr(
self,
self.guest_registers.rcx as u32,
self.guest_registers.rax,
)
.unwrap();
self.step_next_inst().unwrap();
}
VmxExitReason::EXCEPTION => {
self.step_next_inst().unwrap();
}
VmxExitReason::IO_INSTRUCTION => {
info!("IO instruction executed");
self.step_next_inst().unwrap();
}
_ => { _ => {
panic!("VMExit reason: {:?}", info.get_reason()); panic!("VMExit reason: {:?}", info.get_reason());
} }