Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Typos and Add Missing Code in VMM Part #12

Draft
wants to merge 37 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6881254
fix(src/vmm/vmx_root): make var vm a const
gierens Jan 12, 2025
6343cf4
fix(src/vmm/vmx_root): change Error to VmError in vmx checks
gierens Jan 12, 2025
a89868c
feat(src/vmm/vmx_root): add link to intel sdm 2a in cpuid section
gierens Jan 14, 2025
e7f90c3
refactor(src/vmm/vmx_root): rename Leaf.version_info to vers_and_feat…
gierens Jan 14, 2025
2b6e2bf
fix(src/vmm/vmx_root): add missing FeatureInfoEcx definition
gierens Jan 14, 2025
987d98d
fix(src/vmm/vmx_root): add missing Msr.feature_control and MsrFeature…
gierens Jan 14, 2025
793347a
fix(src/vmm/vmx_root): use correct cpuid leaf 1 for vmx feature check
gierens Jan 14, 2025
e2444c2
fix(src/vmm/vmx_root): add missing vcpu creation in Vm.new
gierens Jan 14, 2025
4bd81ae
fix(src/vmm/vmx_root): change is to was in vcpu creation message
gierens Jan 14, 2025
f5acae4
fix(src/vmm/vmx_root): add missing Error definition
gierens Jan 14, 2025
787f510
fix(src/vmm/vmx_root): add missing FlagsRegister definition
gierens Jan 16, 2025
eeeb634
fix(src/vmm/vmx_root): add missing read/loadCr4 and Cr4 definition
gierens Jan 16, 2025
044c63d
fix(src/vmm/vmx_root): add missing Vm.vmxon_region definition
gierens Jan 16, 2025
d45c349
fix(src/vmm/vmcs): make clear that setupVmcs is part of Vcpu
gierens Jan 16, 2025
49ee323
fix(src/vmm/vmcs): add the to doc comments in vmcs encoding stuff
gierens Jan 16, 2025
ea5ea47
fix(src/vmm/vmcs): correct type in InstructionError.load comment
gierens Jan 16, 2025
c4db18b
fix(src/vmm/vmcs): add missing Vcpu.vmcs_region definition
gierens Jan 16, 2025
7c70afb
fix(src/vmm/vmcs): add missing call of Vcpu.setupVmcs in Vm.init
gierens Jan 16, 2025
0aa1eab
fix(src/vmm/vmcs): clarify that vmlaunch example is part of setupVmcs
gierens Jan 16, 2025
f01e380
fix(src/vmm/vmcs): add missing ro enum definition
gierens Jan 16, 2025
74d2792
fix(src/vmm/vmcs): correct path for encode stuff, ro enum and link
gierens Jan 17, 2025
132d5b1
fix(src/vmm/vmlaunch): use Self for PinExecCtrl.load argument
gierens Jan 18, 2025
3a58b3e
fix(src/vmm/vmlaunch): add missing ctrl definition
gierens Jan 18, 2025
c6c9b3d
fix(src/vmm/vmlaunch): add missing values for pin and proc based ctrl…
gierens Jan 18, 2025
b392c00
fix(src/vmm/vmlaunch): correct ExitInfo module to vmcs
gierens Jan 18, 2025
d005b9e
fix(src/vmm/vmlaunch): correct vmread module and add host definition
gierens Jan 18, 2025
cd2ab54
fix(src/vmm/vmlaunch): add missing Segment definition
gierens Jan 18, 2025
e560187
fix(src/vmm/vmlaunch): add missing sidt and gidt definitions
gierens Jan 18, 2025
8ae1d4e
fix(src/vmm/vmlaunch): add missing efer, entry and exit ctrl constant…
gierens Jan 18, 2025
9df1511
fix(src/vmm/vmlaunch): correct ExitInfo module in log output
gierens Jan 18, 2025
5416298
fix(src/vmm/vmx_root): add missing FlagsRegister._reservedO and new f…
gierens Jan 18, 2025
bdc14d4
fix(src/vmm/vmlaunch): add missing initialization of rflags in setupG…
gierens Jan 18, 2025
c7e2353
fix(src/vmm/vmentry_vmexit): correct ExitInfo module to vmcs
gierens Jan 19, 2025
746df80
fix(src/vmm/vmentry_vmexit): correct asmVmEntry/Exit confusion typo
gierens Jan 22, 2025
356e087
fix(src/vmm/vmentry_vmexit): add missing stepNextInst definition
gierens Jan 22, 2025
4059033
fix(src/vmm/vmentry_vmexit): add missing setupHostState rip update
gierens Jan 22, 2025
b6b2a10
fix(src/vmm): correct Guet typo in ept,linux_boot,cpuid,msr chapters
gierens Jan 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vmm/cpuid.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ Ymir ではこの Leaf はホストの値をパススルーします:
[INFO ] vmx | Guest memory region: 0x0000000000000000 - 0x0000000006400000
[INFO ] vmx | Guest kernel code offset: 0x0000000000005000
[DEBUG] ept | EPT Level4 Table @ FFFF88800000A000
[INFO ] vmx | Guet memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] vmx | Guest memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] main | Setup guest memory.
[INFO ] main | Starting the virtual machine...
[ERROR] vcpu | Unhandled VM-exit: reason=arch.x86.vmx.common.ExitReason.rdmsr
Expand Down
4 changes: 2 additions & 2 deletions src/vmm/ept.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ pub const Vm = struct {
// Create simple EPT mapping.
const eptp = try impl.mapGuest(self.guest_mem, allocator);
try self.vcpu.setEptp(eptp, self.guest_mem.ptr);
log.info("Guet memory is mapped: HVA=0x{X:0>16} (size=0x{X})", .{ @intFromPtr(self.guest_mem.ptr), self.guest_mem.len });
log.info("Guest memory is mapped: HVA=0x{X:0>16} (size=0x{X})", .{ @intFromPtr(self.guest_mem.ptr), self.guest_mem.len });
}
};
```
Expand Down Expand Up @@ -522,7 +522,7 @@ ffffffff80115f23: eb fd jmp ffffffff80115f22 <blobGue
```txt
[INFO ] main | Entered VMX root operation.
[DEBUG] ept | EPT Level4 Table @ FFFF88800000A000
[INFO ] vmx | Guet memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] vmx | Guest memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] main | Setup guest memory.
[INFO ] main | Starting the virtual machine...
```
Expand Down
2 changes: 1 addition & 1 deletion src/vmm/linux_boot.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ fn setupGuestState(vcpu: *Vcpu) VmxError!void {
[INFO ] vmx | Guest memory region: 0x0000000000000000 - 0x0000000006400000
[INFO ] vmx | Guest kernel code offset: 0x0000000000005000
[DEBUG] ept | EPT Level4 Table @ FFFF88800000A000
[INFO ] vmx | Guet memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] vmx | Guest memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] main | Setup guest memory.
[INFO ] main | Starting the virtual machine...
[ERROR] vcpu | Unhandled VM-exit: reason=arch.x86.vmx.common.ExitReason.cpuid
Expand Down
2 changes: 1 addition & 1 deletion src/vmm/msr.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ RDMSR よりは対応する必要のある MSR が多いです。
[INFO ] vmx | Guest memory region: 0x0000000000000000 - 0x0000000006400000
[INFO ] vmx | Guest kernel code offset: 0x0000000000005000
[DEBUG] ept | EPT Level4 Table @ FFFF88800000E000
[INFO ] vmx | Guet memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] vmx | Guest memory is mapped: HVA=0xFFFF888000A00000 (size=0x6400000)
[INFO ] main | Setup guest memory.
[INFO ] main | Starting the virtual machine...
No EFI environment detected.
Expand Down
79 changes: 62 additions & 17 deletions src/vmm/vmcs.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,21 @@ Read-only であることからも分かるように、このカテゴリは特
VMCS を設定するための雛形を `Vcpu` に追加しておきます:

```ymir/arch/x86/vmx/vcpu.zig
pub fn setupVmcs(self: *Self, allocator: Allocator) VmxError!void {
pub const Vcpu = struct {
...
vmcs_region: *VmcsRegion = undefined,
...

// Initialize VMCS fields.
try setupExecCtrls(self, allocator);
try setupExitCtrls(self);
try setupEntryCtrls(self);
try setupHostState(self);
try setupGuestState(self);
pub fn setupVmcs(self: *Self, allocator: Allocator) VmxError!void {
...

// Initialize VMCS fields.
try setupExecCtrls(self, allocator);
try setupExitCtrls(self);
try setupEntryCtrls(self);
try setupHostState(self);
try setupGuestState(self);
}
}

fn setupExecCtrls(vcpu: *Vcpu, allocator: Allocator) VmxError!void {}
Expand Down Expand Up @@ -241,6 +247,15 @@ pub fn setupVmcs(self: *Self, allocator: Allocator) VmxError!void {
}
```

`Vm.init` から `setupVmcs` を呼び出すことを忘れないでください:

```ymir/vmx.zig
pub fn init(self: *Self, allocator: Allocator) VmxError!void {
...
try self.vcpu.setupVmcs(allocator);
}
```

## フィールドアクセス

### VMCS-field Encoding
Expand Down Expand Up @@ -276,7 +291,7 @@ VMCS のフィールドレイアウトは実装依存です。
各フィールドに対する Encoding の内容は *SDM Appendix B FIELD ENCODING IN VMCS* に記載されています。
そのリストをもとに各フィールドの encoding を計算するヘルパー関数を定義します[^encoding]:

```ymir/arch/x86/vmx/common.zig
```ymir/arch/x86/vmx/vmcs.zig
fn encode(
comptime field_type: FieldType,
comptime index: u9,
Expand All @@ -291,25 +306,25 @@ fn encode(
});
}

/// Encodes a VMCS field for guest state area.
/// Encodes a VMCS field for the guest state area.
fn eg(
comptime index: u9,
comptime access_type: AccessType,
comptime width: Width,
) u32 { return encode(.guest_state, index, access_type, width); }
/// Encodes a VMCS field for host state area.
/// Encodes a VMCS field for the host state area.
fn eh(
comptime index: u9,
comptime access_type: AccessType,
comptime width: Width,
) u32 { return encode(.host_state, index, access_type, width); }
/// Encodes a VMCS field for control area.
/// Encodes a VMCS field for the control area.
fn ec(
comptime index: u9,
comptime access_type: AccessType,
comptime width: Width,
) u32 { return encode(.control, index, access_type, width); }
/// Encodes a VMCS field for read-only area.
/// Encodes a VMCS field for the read-only area.
fn er(
comptime index: u9,
comptime access_type: AccessType,
Expand Down Expand Up @@ -347,12 +362,12 @@ const ComponentEncoding = packed struct(u32) {
しかし、フィールドの数はとても多いです。
200 個ほどあります。
そのため、ここで定義部分のスニペットを載せることはしないでおきます。
全フィールドの encoding 定義は [Ymir のリポジトリを参照](https://github.com/smallkirby/ymir/blob/master/ymir/arch/x86/vmx/common.zig#TODO) してください。
全フィールドの encoding 定義は [Ymir のリポジトリを参照](https://github.com/smallkirby/ymir/blob/master/ymir/arch/x86/vmx/vmcs.zig) してください。

<details>
<summary>(GitHub にアクセスできないという稀有な人のために Guest-State タイプの encoding 定義だけ抜粋しておきます)</summary>

```ymir/arch/x86/vmx/common.zig
```ymir/arch/x86/vmx/vmcs.zig
pub const guest = enum(u32) {
// Natural-width fields.
cr0 = eg(0, .full, .natural),
Expand Down Expand Up @@ -503,8 +518,10 @@ pub fn vmwrite(field: anytype, value: anytype) VmxError!void {

```ymir/arch/x86/vmx/vcpu.tmp.zig
...
try resetVmcs(self.vmcs_region);
asm volatile("vmlaunch");
pub fn setupVmcs(self: *Self, allocator: Allocator) VmxError!void {
...
try resetVmcs(self.vmcs_region);
asm volatile("vmlaunch");
```

このまま実行すると...。
Expand Down Expand Up @@ -566,7 +583,7 @@ pub const InstructionError = enum(u32) {
vmentry_events_blocked = 26,
invalid_invept = 28,

/// Get a instruction error number from VMCS.
/// Get an instruction error number from VMCS.
pub fn load() VmxError!InstructionError {
return @enumFromInt(@as(u32, @truncate(try vmread(vmcs.ro.vminstruction_error))));
}
Expand All @@ -576,6 +593,34 @@ pub const InstructionError = enum(u32) {
`load()` が VMCS からエラーコードを取得する関数です。
VMREAD の初めての出番ですね。かわいい。

<details>
<summary>(GitHub にアクセスできない人のために、`ro` 列挙型の定義を次に示します):</summary>

```ymir/arch/x86/vmx/vmcs.zig
pub const ro = enum(u32) {
// Natural-width fields.
exit_qual = er(0, .full, .natural),
io_rcx = er(1, .full, .natural),
io_rsi = er(2, .full, .natural),
io_rdi = er(3, .full, .natural),
io_rip = er(4, .full, .natural),
guest_linear_address = er(5, .full, .natural),
// 32-bit fields.
vminstruction_error = er(0, .full, .dword),
vmexit_reason = er(1, .full, .dword),
exit_intr_info = er(2, .full, .dword),
exit_intr_ec = er(3, .full, .dword),
idt_vectoring_info = er(4, .full, .dword),
idt_vectoring_ec = er(5, .full, .dword),
exit_inst_len = er(6, .full, .dword),
exit_inst_info = er(7, .full, .dword),
// 64-bit fields.
guest_physical_address = er(0, .full, .qword),
};
```

</details>

さて、VMLAUNCH 後にエラーコードを取得してみましょう:

```ymir/arch/x86/vmx/vcpu.tmp.zig
Expand Down
20 changes: 18 additions & 2 deletions src/vmm/vmentry_vmexit.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ RAX はスクラッチレジスタとして使います。
呼び出し側は、**あたかも `asmVmEntry()` を関数呼び出したかのように処理を続行することができます**:

```ymir/arch/x86/vmx/asm.zig
// Return to caller of asmVmEntry()
// Return to caller of asmVmExit()
asm volatile (
\\mov $0, %%rax
\\ret
Expand All @@ -540,7 +540,7 @@ RAX はスクラッチレジスタとして使います。
VM Exit のハンドラ関数を定義します:

```ymir/arch/x86/vmx/vcpu.zig
fn handleExit(self: *Self, exit_info: vmx.ExitInfo) VmxError!void {
fn handleExit(self: *Self, exit_info: vmcs.ExitInfo) VmxError!void {
switch (exit_info.basic_reason) {
.hlt => {
try self.stepNextInst();
Expand All @@ -552,6 +552,11 @@ fn handleExit(self: *Self, exit_info: vmx.ExitInfo) VmxError!void {
},
}
}

fn stepNextInst(_: *Self) VmxError!void {
const rip = try vmread(vmcs.guest.rip);
try vmwrite(vmcs.guest.rip, rip + try vmread(vmcs.ro.exit_inst_len));
}
```

引数には `ExitInfo` 構造体を受け取ります。
Expand All @@ -572,6 +577,17 @@ pub fn loop(self: *Self) VmxError!void {
Exit ハンドラを呼び出したあとは、`while` ループの先頭に戻り再び VM Entry をします。
ひたすらにこの繰り返しです。

また、`setupHostState` の `vmwrite` 呼び出しを更新することも忘れないでください:
```ymir/arch/x86/vmx/vcpu.zig
let vmam = @import("asm.zig");

fn setupHostState(_: *Vcpu) VmxError!void {
...
try vmwrite(vmcs.host.rip, &vmam.asmVmExit);
...
}
```

## まとめ

それでは、実装した VM Entry / VM Exit を用いてゲストを実行してみましょう。
Expand Down
Loading
Loading