Skip to content

Commit

Permalink
enter VMX root operation
Browse files Browse the repository at this point in the history
Signed-off-by: smallkirby <ssmallkirby@gmail.com>
  • Loading branch information
smallkirby committed Aug 18, 2024
1 parent 70435ec commit d811046
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 18 deletions.
113 changes: 108 additions & 5 deletions ymir/arch/x86/asm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@ pub inline fn hlt() void {
asm volatile ("hlt");
}

pub inline fn loadCr0(cr0: u64) void {
asm volatile (
\\mov %[cr0], %%cr0
:
: [cr0] "r" (cr0),
);
}

pub inline fn readCr0() u64 {
var cr0: u64 = undefined;
asm volatile (
\\mov %%cr0, %[cr0]
: [cr0] "=r" (cr0),
);
return cr0;
}

pub inline fn readCr2() u64 {
var cr2: u64 = undefined;
asm volatile (
Expand All @@ -105,21 +122,21 @@ pub inline fn readCr3() u64 {
return cr3;
}

pub inline fn loadCr4(cr4: u64) void {
pub inline fn loadCr4(cr4: Cr4) void {
asm volatile (
\\mov %[cr4], %%cr4
:
: [cr4] "r" (cr4),
: [cr4] "r" (@as(u64, @bitCast(cr4))),
);
}

pub inline fn readCr4() u64 {
pub inline fn readCr4() Cr4 {
var cr4: u64 = undefined;
asm volatile (
\\mov %%cr4, %[cr4]
: [cr4] "=r" (cr4),
);
return cr4;
return @bitCast(cr4);
}

pub inline fn readEflags() FlagsRegister {
Expand Down Expand Up @@ -209,14 +226,38 @@ pub fn writeMsrFeatureControl(value: MsrFeatureControl) void {
writeMsr(Msr.feature_control, @bitCast(value));
}

pub fn readMsrVmxBasic() MsrVmxBasic {
const val = readMsr(Msr.vmx_basic);
return @bitCast(val);
}

pub fn writeMsrVmxBasic(value: MsrVmxBasic) void {
writeMsr(Msr.vmx_basic, @bitCast(value));
}

pub fn testA20Mode() bool {
const cr4 = readCr4();
return (cr4 & (1 << 1)) != 0;
}

/// Pause the CPU for a short period of time.
pub fn relax() void {
asm volatile ("rep; nop");
}

pub const Msr = enum(u32) {
/// IA32_FEATURE_CONTROL MSR.
feature_control = 0x3A,
feature_control = 0x003A,
/// IA32_VMX_BASIC MSR.
vmx_basic = 0x0480,
/// IA32_VMX_CR0_FIXED0 MSR.
vmx_cr0_fixed0 = 0x0486,
/// IA32_VMX_CR0_FIXED1 MSR.
vmx_cr0_fixed1 = 0x0487,
/// IA32_VMX_CR4_FIXED0 MSR.
vmx_cr4_fixed0 = 0x0488,
/// IA32_VMX_CR4_FIXED1 MSR.
vmx_cr4_fixed1 = 0x0489,
};

/// IA32_FEATURE_CONTROL MSR.
Expand Down Expand Up @@ -248,6 +289,13 @@ pub const MsrFeatureControl = packed struct(u64) {
_reserved4: u43,
};

pub const MsrVmxBasic = packed struct(u64) {
/// VMCS revision identifier.
vmcs_revision_id: u31,
/// Reserved.
_reserved: u33, // TODO: VMXON region size
};

pub const FlagsRegister = packed struct(u64) {
/// Carry flag.
cf: bool,
Expand Down Expand Up @@ -300,3 +348,58 @@ pub const FlagsRegister = packed struct(u64) {
/// Reserved.
_reserved: u32,
};

pub const Cr4 = packed struct(u64) {
/// Virtual-8086 mode extensions.
vme: bool,
/// Protected mode virtual interrupts.
pvi: bool,
/// Time stamp disable.
tsd: bool,
/// Debugging extensions.
de: bool,
/// Page size extension.
pse: bool,
/// Physical address extension.
pae: bool,
/// Machine check exception.
mce: bool,
/// Page global enable.
pge: bool,
/// Performance monitoring counter enable.
pce: bool,
/// Operating system support for FXSAVE and FXRSTOR instructions.
osfxsr: bool,
/// Operating system support for unmasked SIMD floating-point exceptions.
osxmmexcpt: bool,
/// Virtual machine extensions.
umip: bool,
/// Reserved.
_reserved: u1 = 0,
/// Virtual machine extensions enable.
vmxe: bool,
/// Safer mode extensions enable.
smxe: bool,
/// Reserved.
_reserved2: u1 = 0,
/// Enables the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE.
fsgsbase: bool,
/// PCID enable.
pcide: bool,
/// XSAVE and processor extended states enable.
osxsave: bool,
/// Reserved.
_reserved3: u1 = 0,
/// Supervisor mode execution protection enable.
smep: bool,
/// Supervisor mode access protection enable.
smap: bool,
/// Protection key enable.
pke: bool,
/// Control-flow Enforcement Technology enable.
cet: bool,
/// Protection keys for supervisor-mode pages enable.
pks: bool,
/// Reserved.
_reserved4: u39 = 0,
};
113 changes: 100 additions & 13 deletions ymir/arch/x86/vmx.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const log = std.log.scoped(.vmx);
const Allocator = std.mem.Allocator;

const ymir = @import("ymir");
Expand All @@ -8,10 +9,8 @@ const am = @import("asm.zig");

/// Enable VMX operations.
pub fn enableVmx() void {
// Set VMXE bit in CR4.
var cr4 = am.readCr4();
cr4 |= 1 << 13; // VMXE
am.loadCr4(cr4);
// Adjust control registers.
adjustControlRegisters();

// Check VMXON is allowed outside SMX.
var msr = am.readMsrFeatureControl();
Expand All @@ -21,24 +20,112 @@ pub fn enableVmx() void {
msr.vmx_outside_smx = true;
am.writeMsrFeatureControl(msr);
}

// Set VMXE bit in CR4.
var cr4 = am.readCr4();
cr4.vmxe = true;
am.loadCr4(cr4);
}

fn adjustControlRegisters() void {
const vmx_cr0_fixed0: u32 = @truncate(am.readMsr(.vmx_cr0_fixed0));
const vmx_cr0_fixed1: u32 = @truncate(am.readMsr(.vmx_cr0_fixed1));
const vmx_cr4_fixed0: u32 = @truncate(am.readMsr(.vmx_cr4_fixed0));
const vmx_cr4_fixed1: u32 = @truncate(am.readMsr(.vmx_cr4_fixed1));

var cr0: u64 = @bitCast(am.readCr0());
cr0 |= vmx_cr0_fixed0; // Mandatory 1
cr0 &= vmx_cr0_fixed1; // Mandatory 0
var cr4: u64 = @bitCast(am.readCr4());
cr4 |= vmx_cr4_fixed0; // Mandatory 1
cr4 &= vmx_cr4_fixed1; // Mandatory 0;

am.loadCr0(@bitCast(cr0));
am.loadCr4(@bitCast(cr4));
}

/// Puts the logical processor in VMX operation with no VMCS loaded.
pub fn vmxon(page_allocator: Allocator) !void {
const vmxon_region = page_allocator.alloc(u8, 4096) catch |err| return err;
@memset(vmxon_region, 0);
const vmcs_revision_id: *u32 = @alignCast(@ptrCast(vmxon_region.ptr));
vmcs_revision_id.* = 0xDEADBEEF; // TODO
const vmx_basic = am.readMsrVmxBasic();
const vmxon_region = try VmcsRegion.new(page_allocator);
vmxon_region.vmcs_revision_id = vmx_basic.vmcs_revision_id;
log.debug("VMCS revision ID: 0x{X:0>8}", .{vmxon_region.vmcs_revision_id});

const vmxon_phys = mem.virt2phys(@intFromPtr(vmxon_region));
log.debug("VMXON region physical address: 0x{X:0>16}", .{vmxon_phys});

debugPrintVmxonValidity();

var rflags: u64 = undefined;
asm volatile (
\\vmxon %[vmxon_region]
:
: [vmxon_region] "m" (@intFromPtr(vmxon_region.ptr)),
: "memory"
\\clc
\\vmxon (%[vmxon_phys])
\\pushf
\\popq %[rflags]
: [rflags] "=r" (rflags),
: [vmxon_phys] "r" (&vmxon_phys),
: "cc", "memory"
);

// Check if VMXON succeeded.
const flags = am.readEflags();
const flags: am.FlagsRegister = @bitCast(rflags);
if (flags.cf) @panic("VMXON: VMCS pointer is invalid");
if (flags.zf) @panic("VMXON: Error during VMXON");
}

pub fn vmxoff() void {
asm volatile (
\\vmxoff
::: "cc");
}

fn debugPrintVmxonValidity() void {
log.debug("VMX Validity", .{});

const cpuid_feature = ymir.arch.getFeatureInformation();
if (cpuid_feature.ecx.vmx) {
log.debug("\t\tVMX is supported.", .{});
} else @panic("\t\tVMX in CPUID not set");

const feature_control = am.readMsrFeatureControl();
if (feature_control.vmx_outside_smx) {
log.debug("\t\tVMX outside SMX is enabled.", .{});
} else @panic("\t\tVMX outside SMX is not enabled");
if (feature_control.lock) {
log.debug("\t\tIA32_FEATURE_CONTROL is locked.", .{});
} else @panic("\t\tIA32_FEATURE_CONTROL is not locked");

const vmx_basic = am.readMsrVmxBasic();
log.debug("\t\tVMXON region size: 0x{X}", .{(vmx_basic._reserved >> 1) & 0xFFFF});

const vmx_cr0_fixed0: u32 = @truncate(am.readMsr(.vmx_cr0_fixed0));
const vmx_cr0_fixed1: u32 = @truncate(am.readMsr(.vmx_cr0_fixed1));
const vmx_cr4_fixed0: u32 = @truncate(am.readMsr(.vmx_cr4_fixed0));
const vmx_cr4_fixed1: u32 = @truncate(am.readMsr(.vmx_cr4_fixed1));
const cr0 = am.readCr0();
const cr4 = am.readCr4();
log.debug("\t\tCR0 : {b:0>32}", .{@as(u64, @bitCast(cr0))});
log.debug("\t\tMask 0 : {b:0>32}", .{vmx_cr0_fixed1});
log.debug("\t\tMask 1 : {b:0>32}", .{vmx_cr0_fixed0});
log.debug("\t\tCR4 : {b:0>32}", .{@as(u64, @bitCast(cr4))});
log.debug("\t\tMask 0 : {b:0>32}", .{vmx_cr4_fixed1});
log.debug("\t\tMask 1 : {b:0>32}", .{vmx_cr4_fixed0});

if (cr4.vmxe) {
log.debug("\t\tVMXE bit is set.", .{});
} else @panic("\t\tVMXE bit is not set");
}

pub const VmcsRegion = packed struct {
vmcs_revision_id: u31,
zero: u1 = 0,

pub fn new(page_allocator: Allocator) !*align(4096) VmcsRegion {
const page = try page_allocator.alloc(u8, 4096);
if (page.len != 4096 or @intFromPtr(page.ptr) % 4096 != 0) {
return error.OutOfMemory;
}
@memset(page, 0);
return @alignCast(@ptrCast(page.ptr));
}
};
4 changes: 4 additions & 0 deletions ymir/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ fn kernelMain(boot_info: surtr.BootInfo) !void {
try arch.vmx.vmxon(ymir.mem.page_allocator);
log.info("Entered VMX root operation.", .{});

// Exit VMX root operation.
log.info("Exiting VMX root operation...", .{});
arch.vmx.vmxoff();

// EOL
log.info("Reached EOL.", .{});
while (true)
Expand Down

0 comments on commit d811046

Please sign in to comment.