Skip to content

Commit

Permalink
define segment registers
Browse files Browse the repository at this point in the history
Signed-off-by: smallkirby <ssmallkirby@gmail.com>
  • Loading branch information
smallkirby committed Aug 20, 2024
1 parent 8bb8095 commit 3abe3d5
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 84 deletions.
1 change: 0 additions & 1 deletion ymir/arch/x86/arch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,4 @@ pub fn getFeatureInformation() cpuid.CpuidInformation {
test {
const testing = @import("std").testing;
testing.refAllDeclsRecursive(@This());
testing.refAllDeclsRecursive(@import("vmcs.zig"));
}
2 changes: 1 addition & 1 deletion ymir/arch/x86/asm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const ymir = @import("ymir");
const mem = ymir.mem;
const vmx = @import("vmx.zig");
const VmxError = vmx.VmxError;
const vmxerr = vmx.err;
const vmxerr = vmx.vmxtry;

pub inline fn inb(port: u16) u8 {
return asm volatile (
Expand Down
99 changes: 23 additions & 76 deletions ymir/arch/x86/vmx.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@ const mem = ymir.mem;
const Phys = mem.Phys;

const am = @import("asm.zig");
const vmcs = @import("vmcs.zig");

pub const VmxError = error{
/// VMCS pointer is invalid. No status available.
FailureInvalidVmcsPointer,
/// VMCS pointer is valid but the operation failed.
/// If a current VMCS is active, error status is stored in VM-instruction error field.
FailureStatusAvailable,
/// Failed to allocate memory.
OutOfMemory,
};
const vmcs = @import("vmx/vmcs.zig");
const regs = @import("vmx/regs.zig");

pub const VmxError = @import("vmx/error.zig").VmxError;
pub const VmxInstructionError = @import("vmx/error.zig").VmxInstructionError;

pub fn err(rflags: u64) VmxError!void {
pub fn vmxtry(rflags: u64) VmxError!void {
const flags: am.FlagsRegister = @bitCast(rflags);
return if (flags.cf) VmxError.FailureInvalidVmcsPointer else if (flags.zf) VmxError.FailureStatusAvailable;
}
Expand All @@ -44,6 +38,7 @@ pub fn enableVmx() void {
am.loadCr4(cr4);
}

/// Adjust physical CPU's CR0 and CR4 registers.
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));
Expand All @@ -61,6 +56,7 @@ fn adjustControlRegisters() void {
am.loadCr4(@bitCast(cr4));
}

/// Read VMCS revision identifier.
fn getVmcsRevisionId() u31 {
const vmx_basic = am.readMsrVmxBasic();
return vmx_basic.vmcs_revision_id;
Expand Down Expand Up @@ -99,73 +95,17 @@ pub fn setupVmcs(page_allocator: Allocator) VmxError!void {
const vmcs_region = try VmcsRegion.new(page_allocator);
vmcs_region.vmcs_revision_id = getVmcsRevisionId();

// TODO

// Reset VMCS.
try resetVmcs(vmcs_region);
}

// Load guest state
try vmcs.vmwrite(vmcs.guest_cr0, @bitCast(am.readCr0()));
try vmcs.vmwrite(vmcs.guest_cr3, am.readCr3());
try vmcs.vmwrite(vmcs.guest_cr4, @bitCast(am.readCr4()));
try vmcs.vmwrite(vmcs.guest_dr7, am.readDebugRegister(.dr7));
try vmcs.vmwrite(vmcs.guest_rflags, @bitCast(am.readEflags()));
try vmcs.vmwrite(vmcs.guest_debugctl, am.readMsr(.debugctl));
try vmcs.vmwrite(vmcs.guest_sysenter_esp, 0xDEADBEEF00); // TODO
try vmcs.vmwrite(vmcs.guest_sysenter_eip, 0xCAFEBABE00); // TODO
try vmcs.vmwrite(vmcs.guest_fs_base, 0x0); // TODO
try vmcs.vmwrite(vmcs.guest_gs_base, 0x0); // TODO
try vmcs.vmwrite(vmcs.guest_vmcs_link_pointer, 0xFFFF_FFFF_FFFF_FFFF); // TODO
try vmcs.vmwrite(vmcs.host_rsp, 0xDEADBEEF00); // TODO
try vmcs.vmwrite(vmcs.host_rip, 0xCAFEBABE00); // TODO

// Set up VM-entry controls
var entry_controls = vmcs.entry_control.EntryControls.new();
entry_controls.ia32e_mode_guest = true;
var entry_controls_val: u32 = @bitCast(entry_controls);
const basic_msr = am.readMsrVmxBasic();
const entry_ctls_msr: u64 = if (basic_msr.true_control) am.readMsr(.vmx_true_entry_ctls) else am.readMsr(.vmx_entry_ctls);
entry_controls_val |= @as(u32, @truncate(entry_ctls_msr)); // Mandatory 1
entry_controls_val &= @as(u32, @truncate(entry_ctls_msr >> 32)); // Mandatory 0
try vmcs.vmwrite(vmcs.control_vmentry_controls, entry_controls_val);

// Set up VM-entry controls
// TODO: make it function to adjust control values
var primary_exit_controls = vmcs.exit_control.PrimaryExitControls.new();
primary_exit_controls.activate_secondary_controls = false;
primary_exit_controls.host_addr_space_size = true;
var primary_exit_controls_val: u32 = @bitCast(primary_exit_controls);
const exit_ctls_msr: u64 = if (basic_msr.true_control) am.readMsr(.vmx_true_exit_ctls) else am.readMsr(.vmx_exit_ctls);
primary_exit_controls_val |= @as(u32, @truncate(exit_ctls_msr)); // Mandatory 1
primary_exit_controls_val &= @as(u32, @truncate(exit_ctls_msr >> 32)); // Mandatory 0
try vmcs.vmwrite(vmcs.control_primary_vmexit_controls, primary_exit_controls_val);

// Set up pin-based controls
const pin_exec_controls = vmcs.exec_control.PinBasedExecutionControl.new();
var pin_exec_controls_val: u32 = @bitCast(pin_exec_controls);
const pin_exec_ctls_msr: u64 = if (basic_msr.true_control) am.readMsr(.vmx_true_pinbased_ctls) else am.readMsr(.vmx_pinbased_ctls);
pin_exec_controls_val |= @as(u32, @truncate(pin_exec_ctls_msr)); // Mandatory 1
pin_exec_controls_val &= @as(u32, @truncate(pin_exec_ctls_msr >> 32)); // Mandatory 0
try vmcs.vmwrite(vmcs.control_pinbased_vmexec_controls, pin_exec_controls_val);

// Set up primary processor-based controls
var primary_proc_exec_controls = vmcs.exec_control.PrimaryProcessorBasedExecutionControl.new();
primary_proc_exec_controls.use_msr_bitmap = false;
primary_proc_exec_controls.activate_secondary_controls = true;
var primary_proc_exec_controls_val: u32 = @bitCast(primary_proc_exec_controls);
const primary_proc_exec_ctls_msr: u64 = if (basic_msr.true_control) am.readMsr(.vmx_true_procbased_ctls) else am.readMsr(.vmx_procbased_ctls);
primary_proc_exec_controls_val |= @as(u32, @truncate(primary_proc_exec_ctls_msr)); // Mandatory 1
primary_proc_exec_controls_val &= @as(u32, @truncate(primary_proc_exec_ctls_msr >> 32)); // Mandatory 0
try vmcs.vmwrite(vmcs.control_procbased_vmexec_controls, primary_proc_exec_controls_val);

// Set up secondary processor-based controls
var secondary_proc_exec_controls = vmcs.exec_control.SecondaryProcessorBasedExecutionControl.new();
secondary_proc_exec_controls.rdtscp = true;
secondary_proc_exec_controls.enable_xsaves_xrstors = true;
secondary_proc_exec_controls.enable_invpcid = true;
var secondary_proc_exec_controls_val: u32 = @bitCast(secondary_proc_exec_controls);
const secondary_proc_exec_ctls_msr = am.readMsr(.vmx_procbased_ctls2);
secondary_proc_exec_controls_val |= @as(u32, @truncate(secondary_proc_exec_ctls_msr)); // Mandatory 1
secondary_proc_exec_controls_val &= @as(u32, @truncate(secondary_proc_exec_ctls_msr >> 32)); // Mandatory 0
try vmcs.vmwrite(vmcs.control_secondary_procbased_vmexec_controls, secondary_proc_exec_controls_val);
fn adjustRegisterMandatoryBits(control: anytype, mask: u64) u32 {
var ret: u32 = @bitCast(control);
ret |= @as(u32, @truncate(mask)); // Mandatory 1
ret &= @as(u32, @truncate(mask >> 32)); // Mandatory 0
return ret;
}

// TODO: template
Expand All @@ -174,6 +114,11 @@ pub fn launch() VmxError!void {
try am.vmlaunch();
}

/// Read error reason from the current logical processor's VMCS.
pub fn getErrorReason() VmxError!VmxInstructionError {
return @enumFromInt(try vmcs.vmread(vmcs.ro_vminstruction_error));
}

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

Expand Down Expand Up @@ -248,4 +193,6 @@ pub const VmcsRegion = packed struct {

test {
std.testing.refAllDeclsRecursive(@This());

std.testing.refAllDeclsRecursive(@import("vmx/vmcs.zig"));
}
10 changes: 10 additions & 0 deletions ymir/arch/x86/vmx/error.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,13 @@ pub const VmxInstructionError = enum(u8) {
vmentry_events_blocked = 26,
invalid_invept = 28,
};

pub const VmxError = error{
/// VMCS pointer is invalid. No status available.
FailureInvalidVmcsPointer,
/// VMCS pointer is valid but the operation failed.
/// If a current VMCS is active, error status is stored in VM-instruction error field.
FailureStatusAvailable,
/// Failed to allocate memory.
OutOfMemory,
};
106 changes: 106 additions & 0 deletions ymir/arch/x86/vmx/regs.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const am = @import("../asm.zig");
const vmx = @import("../vmx.zig");
const vmcs = @import("vmcs.zig");
const VmxError = vmx.VmxError;

pub const guest = struct {
/// x64 guest registers.
pub const Registers = struct {
rax: u64,
rbx: u64,
rcx: u64,
rdx: u64,
rsi: u64,
rdi: u64,
rbp: u64,
rsp: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
rip: u64,
rflags: u64,
};

pub const SegRegisters = struct {
cs: SegRegister,
ds: SegRegister,
es: SegRegister,
fs: SegRegister,
gs: SegRegister,
ss: SegRegister,
tr: SegRegister,
ldt: SegRegister,
};

pub const ControlRegisters = struct {
cr0: u64,
cr3: u64,
cr4: u64,

pub fn load(self: ControlRegisters) VmxError!void {
try vmcs.vmwrite(vmcs.guest_cr0, self.cr0);
try vmcs.vmwrite(vmcs.guest_cr3, self.cr3);
try vmcs.vmwrite(vmcs.guest_cr4, self.cr4);
}

pub fn get() VmxError!ControlRegisters {
return .{
.cr0 = try vmcs.vmread(vmcs.guest_cr0),
.cr3 = try vmcs.vmread(vmcs.guest_cr3),
.cr4 = try vmcs.vmread(vmcs.guest_cr4),
};
}
};

pub const SegRegister = struct {
/// Segment base.
base: u64,
/// Segment limit.
limit: u32,
/// Segment selector.
selector: u16,

/// P. Present bit.
present: bool,
/// DPL. Descriptor privilege level.
dpl: u2,
/// S. Descriptor type (System or Code/Data).
system: bool,
/// E. Executable bit.
executable: bool,
/// DC. Direction bit.
direction: Direction,
/// RW. Read/Write bit.
/// For code segments, false means execute-only. For data segments, false means read-only.
rw: bool,
/// A. Accessed bit.
accessed: bool,

/// G. Size granularity.
granularity: Granularity,
/// DB. Size flag.
db: SizeFlag,
/// L. Long-mode code flag.
long: bool,

const SizeFlag = enum(u1) {
protected16 = 0,
protected32 = 1,
};

const Direction = enum(u1) {
up = 0,
down = 1,
};

const Granularity = enum(u1) {
byte = 0,
page = 1,
};
};
};
6 changes: 3 additions & 3 deletions ymir/arch/x86/vmcs.zig → ymir/arch/x86/vmx/vmcs.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const std = @import("std");

const am = @import("asm.zig");
const vmx = @import("vmx.zig");
const am = @import("../asm.zig");
const vmx = @import("../vmx.zig");
const VmxError = vmx.VmxError;
const err = vmx.err;
const err = vmx.vmxtry;

pub fn vmread(field: ComponentEncoding) VmxError!u64 {
const field_u64: u32 = @bitCast(field);
Expand Down
12 changes: 9 additions & 3 deletions ymir/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,19 @@ fn kernelMain(boot_info: surtr.BootInfo) !void {
// Setup VMCS
try arch.vmx.setupVmcs(ymir.mem.page_allocator);

// Launch
arch.vmx.launch() catch |err| switch (err) {
error.FailureStatusAvailable => {
log.err("VMLAUNCH failed: error={?}", .{try arch.vmx.getErrorReason()});
return err;
},
else => return err,
};

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

// Launch
try arch.vmx.launch();

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

0 comments on commit 3abe3d5

Please sign in to comment.