lib/sys/src/kvm/vcpu.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const abi = @import("abi.zig");
3 const run_exit = @import("exit.zig");
4 const state = @import("state.zig");
5 const types = @import("types.zig");
6
7 pub const RunControls = struct {
8 request_interrupt_window: bool = false,
9 cr8: u64 = 0,
10 apic_base: u64 = 0,
11 };
12
13 pub const RunStatus = struct {
14 ready_for_interrupt_injection: bool,
15 interrupts_enabled: bool,
16 flags: u16,
17 cr8: u64,
18 apic_base: u64,
19 };
20
21 pub const Vcpu = struct {
22 pub const descriptor_capacity: usize = 1;
23 pub const mapping_capacity: usize = 1;
24
25 operations: types.Operations,
26 descriptor: types.Descriptor,
27 mapping: []align(types.page_size) u8,
28 extensions: types.Extensions,
29 active: bool = true,
30
31 pub fn deinit(self: *@This()) void {
32 if (!self.active) return;
33 std.debug.assert(self.descriptor >= 0);
34 std.debug.assert(self.mapping.len >= abi.minimum_run_bytes);
35 self.operations.unmap(self.mapping);
36 self.operations.close(self.descriptor);
37 self.active = false;
38 }
39
40 pub fn run(self: *@This()) (types.RunError || run_exit.DecodeError)!run_exit.Exit {
41 if (!self.active) return error.Closed;
42 std.debug.assert(self.mapping.len >= abi.minimum_run_bytes);
43 switch (self.operations.control(self.descriptor, abi.request.run, 0)) {
44 .value => {},
45 .failure => |code| return runError(code),
46 }
47 return run_exit.decode(self.mapping);
48 }
49
50 pub fn setImmediateExit(self: *@This(), requested: bool) types.RunError!void {
51 if (!self.active) return error.Closed;
52 if (self.extensions.immediate_exit == 0) {
53 return error.UnsupportedOperation;
54 }
55 std.debug.assert(self.mapping.len >= @sizeOf(abi.RunHeader));
56 const header: *abi.RunHeader = @ptrCast(self.mapping.ptr);
57 @atomicStore(u8, &header.immediate_exit, @intFromBool(requested), .monotonic);
58 }
59
60 pub fn setRunControls(self: *@This(), controls: RunControls) types.RunError!void {
61 if (!self.active) return error.Closed;
62 const header = self.runHeader();
63 header.request_interrupt_window = @intFromBool(controls.request_interrupt_window);
64 header.cr8 = controls.cr8;
65 header.apic_base = controls.apic_base;
66 }
67
68 pub fn runStatus(self: *@This()) types.RunError!RunStatus {
69 if (!self.active) return error.Closed;
70 const header = self.runHeader();
71 return .{
72 .ready_for_interrupt_injection = header.ready_for_interrupt_injection != 0,
73 .interrupts_enabled = header.interrupts_enabled != 0,
74 .flags = header.flags,
75 .cr8 = header.cr8,
76 .apic_base = header.apic_base,
77 };
78 }
79
80 pub fn capture(self: *@This(), parts: state.Parts) types.StateError!state.Fixed {
81 if (!self.active) return error.Closed;
82 if (!fixedStateSupported(parts, self.extensions)) return error.UnsupportedState;
83 var result = std.mem.zeroes(state.Fixed);
84 result.valid = parts;
85 try self.captureBasic(parts, &result);
86 try self.captureExtended(parts, &result);
87 return result;
88 }
89
90 pub fn restore(self: *@This(), snapshot: *const state.Fixed) types.StateError!void {
91 if (!self.active) return error.Closed;
92 if (!fixedStateSupported(snapshot.valid, self.extensions)) {
93 return error.UnsupportedState;
94 }
95 try self.restoreBasic(snapshot);
96 try self.restoreExtended(snapshot);
97 }
98
99 pub fn readMsrs(self: *@This(), buffer: anytype) types.StateError!void {
100 if (!self.active) return error.Closed;
101 validateMsrBuffer(@TypeOf(buffer.*));
102 if (buffer.count > buffer.entries.len) return error.CapacityExceeded;
103 const result = types.controlRetry(
104 self.operations,
105 self.descriptor,
106 abi.request.get_msrs,
107 @intFromPtr(buffer),
108 );
109 try expectTransfer(result, buffer.count);
110 }
111
112 pub fn writeMsrs(self: *@This(), buffer: anytype) types.StateError!void {
113 if (!self.active) return error.Closed;
114 validateMsrBuffer(@TypeOf(buffer.*));
115 if (buffer.count > buffer.entries.len) return error.CapacityExceeded;
116 const result = types.controlRetry(
117 self.operations,
118 self.descriptor,
119 abi.request.set_msrs,
120 @intFromPtr(buffer),
121 );
122 try expectTransfer(result, buffer.count);
123 }
124
125 pub fn readCpuid(self: *@This(), buffer: anytype) types.StateError!void {
126 if (!self.active) return error.Closed;
127 if (self.extensions.extended_cpuid == 0) return error.UnsupportedState;
128 validateCpuidBuffer(@TypeOf(buffer.*));
129 if (buffer.count > buffer.entries.len) return error.CapacityExceeded;
130 try types.stateControl(
131 self.operations,
132 self.descriptor,
133 abi.request.get_cpuid,
134 @intFromPtr(buffer),
135 );
136 }
137
138 pub fn writeCpuid(self: *@This(), buffer: anytype) types.StateError!void {
139 if (!self.active) return error.Closed;
140 if (self.extensions.extended_cpuid == 0) return error.UnsupportedState;
141 validateCpuidBuffer(@TypeOf(buffer.*));
142 if (buffer.count > buffer.entries.len) return error.CapacityExceeded;
143 try types.stateControl(
144 self.operations,
145 self.descriptor,
146 abi.request.set_cpuid,
147 @intFromPtr(buffer),
148 );
149 }
150
151 fn captureBasic(
152 self: *@This(),
153 parts: state.Parts,
154 result: *state.Fixed,
155 ) types.StateError!void {
156 if (parts.registers) try self.get(abi.request.get_registers, &result.registers);
157 if (parts.special_registers) {
158 try self.get(abi.request.get_special_registers, &result.special_registers);
159 }
160 if (parts.fpu) try self.get(abi.request.get_fpu, &result.fpu);
161 }
162
163 fn captureExtended(
164 self: *@This(),
165 parts: state.Parts,
166 result: *state.Fixed,
167 ) types.StateError!void {
168 if (parts.events) try self.get(abi.request.get_vcpu_events, &result.events);
169 if (parts.debug_registers) {
170 try self.get(abi.request.get_debug_registers, &result.debug_registers);
171 }
172 if (parts.xsave) try self.get(abi.request.get_xsave, &result.xsave);
173 if (parts.xcrs) try self.get(abi.request.get_xcrs, &result.xcrs);
174 if (parts.multiprocessor) {
175 try self.get(abi.request.get_mp_state, &result.multiprocessor);
176 }
177 }
178
179 fn restoreBasic(self: *@This(), snapshot: *const state.Fixed) types.StateError!void {
180 if (snapshot.valid.special_registers) {
181 try self.set(abi.request.set_special_registers, &snapshot.special_registers);
182 }
183 if (snapshot.valid.registers) {
184 try self.set(abi.request.set_registers, &snapshot.registers);
185 }
186 if (snapshot.valid.fpu) try self.set(abi.request.set_fpu, &snapshot.fpu);
187 }
188
189 fn restoreExtended(
190 self: *@This(),
191 snapshot: *const state.Fixed,
192 ) types.StateError!void {
193 if (snapshot.valid.events) {
194 try self.set(abi.request.set_vcpu_events, &snapshot.events);
195 }
196 if (snapshot.valid.debug_registers) {
197 try self.set(abi.request.set_debug_registers, &snapshot.debug_registers);
198 }
199 if (snapshot.valid.xsave) try self.set(abi.request.set_xsave, &snapshot.xsave);
200 if (snapshot.valid.xcrs) try self.set(abi.request.set_xcrs, &snapshot.xcrs);
201 if (snapshot.valid.multiprocessor) {
202 try self.set(abi.request.set_mp_state, &snapshot.multiprocessor);
203 }
204 }
205
206 fn get(self: *@This(), request: u32, destination: anytype) types.StateError!void {
207 std.debug.assert(request != 0);
208 try types.stateControl(
209 self.operations,
210 self.descriptor,
211 request,
212 @intFromPtr(destination),
213 );
214 }
215
216 fn set(self: *@This(), request: u32, source: anytype) types.StateError!void {
217 std.debug.assert(request != 0);
218 try types.stateControl(
219 self.operations,
220 self.descriptor,
221 request,
222 @intFromPtr(source),
223 );
224 }
225
226 fn runHeader(self: *@This()) *abi.RunHeader {
227 std.debug.assert(self.mapping.len >= @sizeOf(abi.RunHeader));
228 return @ptrCast(self.mapping.ptr);
229 }
230 };
231
232 fn runError(code: types.Errno) types.RunError {
233 return switch (code) {
234 .INTR => error.Interrupted,
235 .AGAIN => error.WouldBlock,
236 .ACCES, .PERM => error.AccessDenied,
237 .BADF, .NODEV => error.Closed,
238 .FAULT, .INVAL => error.InvalidRunState,
239 .NOSYS, .NOTTY, .OPNOTSUPP => error.UnsupportedOperation,
240 else => error.RunFailed,
241 };
242 }
243
244 fn expectTransfer(result: types.RawResult, expected: u32) types.StateError!void {
245 switch (result) {
246 .value => |value| {
247 if (value != expected) return error.PartialTransfer;
248 },
249 .failure => |code| return switch (code) {
250 .ACCES, .PERM => error.AccessDenied,
251 .BADF, .NODEV => error.Closed,
252 .INVAL, .NOSYS, .NOTTY, .OPNOTSUPP => error.UnsupportedState,
253 else => error.StateFailed,
254 },
255 }
256 }
257
258 fn fixedStateSupported(parts: state.Parts, extensions: types.Extensions) bool {
259 if (parts.events and extensions.vcpu_events == 0) return false;
260 if (parts.debug_registers and extensions.debug_registers == 0) return false;
261 if (parts.xsave and extensions.xsave == 0) return false;
262 if (parts.xcrs and extensions.xcrs == 0) return false;
263 if (parts.multiprocessor and extensions.multiprocessor_state == 0) return false;
264 return true;
265 }
266
267 fn validateMsrBuffer(comptime Buffer: type) void {
268 comptime {
269 if (!@hasField(Buffer, "count") or !@hasField(Buffer, "entries")) {
270 @compileError("expected abi.MsrBuffer");
271 }
272 if (@FieldType(Buffer, "count") != u32) @compileError("invalid MSR count type");
273 if (@typeInfo(@FieldType(Buffer, "entries")).array.child != abi.MsrEntry) {
274 @compileError("invalid MSR entry type");
275 }
276 if (@offsetOf(Buffer, "entries") != @sizeOf(abi.MsrHeader)) {
277 @compileError("invalid MSR buffer layout");
278 }
279 }
280 }
281
282 fn validateCpuidBuffer(comptime Buffer: type) void {
283 comptime {
284 if (!@hasField(Buffer, "count") or !@hasField(Buffer, "entries")) {
285 @compileError("expected abi.CpuidBuffer");
286 }
287 if (@FieldType(Buffer, "count") != u32) @compileError("invalid CPUID count type");
288 if (@typeInfo(@FieldType(Buffer, "entries")).array.child != abi.CpuidEntry) {
289 @compileError("invalid CPUID entry type");
290 }
291 if (@offsetOf(Buffer, "entries") != @sizeOf(abi.CpuidHeader)) {
292 @compileError("invalid CPUID buffer layout");
293 }
294 }
295 }