From 3abe3d5078c28ce7a5239221ab0508ebea883730 Mon Sep 17 00:00:00 2001 From: smallkirby Date: Tue, 20 Aug 2024 23:47:51 +0900 Subject: [PATCH] define segment registers Signed-off-by: smallkirby --- ymir/arch/x86/arch.zig | 1 - ymir/arch/x86/asm.zig | 2 +- ymir/arch/x86/vmx.zig | 99 +++++++---------------------- ymir/arch/x86/vmx/error.zig | 10 +++ ymir/arch/x86/vmx/regs.zig | 106 +++++++++++++++++++++++++++++++ ymir/arch/x86/{ => vmx}/vmcs.zig | 6 +- ymir/main.zig | 12 +++- 7 files changed, 152 insertions(+), 84 deletions(-) create mode 100644 ymir/arch/x86/vmx/regs.zig rename ymir/arch/x86/{ => vmx}/vmcs.zig (99%) diff --git a/ymir/arch/x86/arch.zig b/ymir/arch/x86/arch.zig index 482d478..4104b6f 100644 --- a/ymir/arch/x86/arch.zig +++ b/ymir/arch/x86/arch.zig @@ -96,5 +96,4 @@ pub fn getFeatureInformation() cpuid.CpuidInformation { test { const testing = @import("std").testing; testing.refAllDeclsRecursive(@This()); - testing.refAllDeclsRecursive(@import("vmcs.zig")); } diff --git a/ymir/arch/x86/asm.zig b/ymir/arch/x86/asm.zig index 0e8dcf5..75b2f5d 100644 --- a/ymir/arch/x86/asm.zig +++ b/ymir/arch/x86/asm.zig @@ -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 ( diff --git a/ymir/arch/x86/vmx.zig b/ymir/arch/x86/vmx.zig index be6f961..598d6d8 100644 --- a/ymir/arch/x86/vmx.zig +++ b/ymir/arch/x86/vmx.zig @@ -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; } @@ -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)); @@ -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; @@ -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 @@ -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", .{}); @@ -248,4 +193,6 @@ pub const VmcsRegion = packed struct { test { std.testing.refAllDeclsRecursive(@This()); + + std.testing.refAllDeclsRecursive(@import("vmx/vmcs.zig")); } diff --git a/ymir/arch/x86/vmx/error.zig b/ymir/arch/x86/vmx/error.zig index fb356be..dbe0e2a 100644 --- a/ymir/arch/x86/vmx/error.zig +++ b/ymir/arch/x86/vmx/error.zig @@ -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, +}; diff --git a/ymir/arch/x86/vmx/regs.zig b/ymir/arch/x86/vmx/regs.zig new file mode 100644 index 0000000..a0a4030 --- /dev/null +++ b/ymir/arch/x86/vmx/regs.zig @@ -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, + }; + }; +}; diff --git a/ymir/arch/x86/vmcs.zig b/ymir/arch/x86/vmx/vmcs.zig similarity index 99% rename from ymir/arch/x86/vmcs.zig rename to ymir/arch/x86/vmx/vmcs.zig index dacf752..09b2e23 100644 --- a/ymir/arch/x86/vmcs.zig +++ b/ymir/arch/x86/vmx/vmcs.zig @@ -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); diff --git a/ymir/main.zig b/ymir/main.zig index 1434040..5c3966a 100644 --- a/ymir/main.zig +++ b/ymir/main.zig @@ -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)