lib/sys/src/kvm/abi.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const api_version: u32 = 12;
4 pub const device_path = "/dev/kvm";
5 pub const minimum_run_bytes: usize = 2352;
6 pub const run_union_offset: usize = 32;
7 pub const maximum_exit_data: usize = 16;
8 pub const maximum_xcrs: usize = 16;
9
10 const ioctl_type: u32 = 0xae;
11 const direction_none: u32 = 0;
12 const direction_write: u32 = 1;
13 const direction_read: u32 = 2;
14
15 fn ioctl(direction: u32, number: u8, size: usize) u32 {
16 std.debug.assert(direction <= direction_read | direction_write);
17 std.debug.assert(size < 1 << 14);
18 return (direction << 30) |
19 (@as(u32, @intCast(size)) << 16) |
20 (ioctl_type << 8) |
21 number;
22 }
23
24 fn io(number: u8) u32 {
25 return ioctl(direction_none, number, 0);
26 }
27
28 fn ior(number: u8, comptime T: type) u32 {
29 return ioctl(direction_read, number, @sizeOf(T));
30 }
31
32 fn iow(number: u8, comptime T: type) u32 {
33 return ioctl(direction_write, number, @sizeOf(T));
34 }
35
36 fn iowr(number: u8, comptime T: type) u32 {
37 return ioctl(direction_read | direction_write, number, @sizeOf(T));
38 }
39
40 pub const UserMemoryRegion = extern struct {
41 slot: u32,
42 flags: u32,
43 guest_physical_address: u64,
44 memory_size: u64,
45 userspace_address: u64,
46 };
47
48 pub const RunHeader = extern struct {
49 request_interrupt_window: u8,
50 immediate_exit: u8,
51 padding: [6]u8,
52 exit_reason: u32,
53 ready_for_interrupt_injection: u8,
54 interrupts_enabled: u8,
55 flags: u16,
56 cr8: u64,
57 apic_base: u64,
58 };
59
60 pub const Registers = extern struct {
61 rax: u64,
62 rbx: u64,
63 rcx: u64,
64 rdx: u64,
65 rsi: u64,
66 rdi: u64,
67 rsp: u64,
68 rbp: u64,
69 r8: u64,
70 r9: u64,
71 r10: u64,
72 r11: u64,
73 r12: u64,
74 r13: u64,
75 r14: u64,
76 r15: u64,
77 rip: u64,
78 rflags: u64,
79 };
80
81 pub const Segment = extern struct {
82 base: u64,
83 limit: u32,
84 selector: u16,
85 kind: u8,
86 present: u8,
87 privilege: u8,
88 default_operand_size: u8,
89 system: u8,
90 long_mode: u8,
91 granularity: u8,
92 available: u8,
93 unusable: u8,
94 padding: u8,
95 };
96
97 pub const DescriptorTable = extern struct {
98 base: u64,
99 limit: u16,
100 padding: [3]u16,
101 };
102
103 pub const SpecialRegisters = extern struct {
104 cs: Segment,
105 ds: Segment,
106 es: Segment,
107 fs: Segment,
108 gs: Segment,
109 ss: Segment,
110 tr: Segment,
111 ldt: Segment,
112 gdt: DescriptorTable,
113 idt: DescriptorTable,
114 cr0: u64,
115 cr2: u64,
116 cr3: u64,
117 cr4: u64,
118 cr8: u64,
119 efer: u64,
120 apic_base: u64,
121 interrupt_bitmap: [4]u64,
122 };
123
124 pub const Fpu = extern struct {
125 floating_point_registers: [8][16]u8,
126 control_word: u16,
127 status_word: u16,
128 tag_word: u8,
129 padding_1: u8,
130 last_opcode: u16,
131 last_instruction_pointer: u64,
132 last_data_pointer: u64,
133 xmm: [16][16]u8,
134 mxcsr: u32,
135 padding_2: u32,
136 };
137
138 pub const MsrEntry = extern struct {
139 index: u32,
140 reserved: u32,
141 data: u64,
142 };
143
144 pub const MsrHeader = extern struct {
145 count: u32,
146 padding: u32,
147 };
148
149 pub fn MsrBuffer(comptime capacity: usize) type {
150 if (capacity > std.math.maxInt(u32)) @compileError("MSR capacity exceeds KVM ABI");
151 return extern struct {
152 pub const entry_capacity = capacity;
153
154 count: u32,
155 padding: u32,
156 entries: [capacity]MsrEntry,
157
158 pub fn init(indices: []const u32) error{TooManyEntries}!@This() {
159 if (indices.len > capacity) return error.TooManyEntries;
160 var result = std.mem.zeroes(@This());
161 result.count = @intCast(indices.len);
162 for (indices, 0..) |index, entry_index| {
163 result.entries[entry_index].index = index;
164 }
165 return result;
166 }
167
168 pub fn values(self: *@This()) []MsrEntry {
169 std.debug.assert(self.count <= capacity);
170 const len: usize = @intCast(self.count);
171 return self.entries[0..len];
172 }
173
174 pub fn valuesConst(self: *const @This()) []const MsrEntry {
175 std.debug.assert(self.count <= capacity);
176 const len: usize = @intCast(self.count);
177 return self.entries[0..len];
178 }
179 };
180 }
181
182 pub const CpuidEntry = extern struct {
183 function: u32,
184 index: u32,
185 flags: u32,
186 eax: u32,
187 ebx: u32,
188 ecx: u32,
189 edx: u32,
190 padding: [3]u32,
191 };
192
193 pub const CpuidHeader = extern struct {
194 count: u32,
195 padding: u32,
196 };
197
198 pub fn CpuidBuffer(comptime capacity: usize) type {
199 if (capacity > std.math.maxInt(u32)) @compileError("CPUID capacity exceeds KVM ABI");
200 return extern struct {
201 pub const entry_capacity = capacity;
202
203 count: u32,
204 padding: u32,
205 entries: [capacity]CpuidEntry,
206
207 pub fn init() @This() {
208 var result = std.mem.zeroes(@This());
209 result.count = @intCast(capacity);
210 return result;
211 }
212
213 pub fn values(self: *@This()) []CpuidEntry {
214 std.debug.assert(self.count <= capacity);
215 const len: usize = @intCast(self.count);
216 return self.entries[0..len];
217 }
218
219 pub fn valuesConst(self: *const @This()) []const CpuidEntry {
220 std.debug.assert(self.count <= capacity);
221 const len: usize = @intCast(self.count);
222 return self.entries[0..len];
223 }
224 };
225 }
226
227 pub const MpState = extern struct {
228 state: u32,
229 };
230
231 pub const ExceptionEvent = extern struct {
232 injected: u8,
233 number: u8,
234 has_error_code: u8,
235 pending: u8,
236 error_code: u32,
237 };
238
239 pub const InterruptEvent = extern struct {
240 injected: u8,
241 number: u8,
242 software: u8,
243 shadow: u8,
244 };
245
246 pub const NmiEvent = extern struct {
247 injected: u8,
248 pending: u8,
249 masked: u8,
250 padding: u8,
251 };
252
253 pub const SmiEvent = extern struct {
254 system_management_mode: u8,
255 pending: u8,
256 inside_nmi: u8,
257 latched_init: u8,
258 };
259
260 pub const TripleFaultEvent = extern struct {
261 pending: u8,
262 };
263
264 pub const VcpuEvents = extern struct {
265 exception: ExceptionEvent,
266 interrupt: InterruptEvent,
267 nmi: NmiEvent,
268 sipi_vector: u32,
269 flags: u32,
270 smi: SmiEvent,
271 triple_fault: TripleFaultEvent,
272 reserved: [26]u8,
273 exception_has_payload: u8,
274 exception_payload: u64,
275 };
276
277 pub const DebugRegisters = extern struct {
278 breakpoint_address: [4]u64,
279 status: u64,
280 control: u64,
281 flags: u64,
282 reserved: [9]u64,
283 };
284
285 pub const Xsave = extern struct {
286 region: [1024]u32,
287 };
288
289 pub const Xcr = extern struct {
290 index: u32,
291 reserved: u32,
292 value: u64,
293 };
294
295 pub const Xcrs = extern struct {
296 count: u32,
297 flags: u32,
298 entries: [maximum_xcrs]Xcr,
299 padding: [16]u64,
300 };
301
302 pub const Clock = extern struct {
303 clock: u64,
304 flags: u32,
305 padding_0: u32,
306 realtime: u64,
307 host_tsc: u64,
308 padding: [4]u32,
309 };
310
311 pub const request = struct {
312 pub const get_api_version = io(0x00);
313 pub const create_vm = io(0x01);
314 pub const check_extension = io(0x03);
315 pub const get_vcpu_mmap_size = io(0x04);
316 pub const get_supported_cpuid = iowr(0x05, CpuidHeader);
317 pub const create_vcpu = io(0x41);
318 pub const set_user_memory_region = iow(0x46, UserMemoryRegion);
319 pub const set_clock = iow(0x7b, Clock);
320 pub const get_clock = ior(0x7c, Clock);
321 pub const run = io(0x80);
322 pub const get_registers = ior(0x81, Registers);
323 pub const set_registers = iow(0x82, Registers);
324 pub const get_special_registers = ior(0x83, SpecialRegisters);
325 pub const set_special_registers = iow(0x84, SpecialRegisters);
326 pub const get_msrs = iowr(0x88, MsrHeader);
327 pub const set_msrs = iow(0x89, MsrHeader);
328 pub const get_fpu = ior(0x8c, Fpu);
329 pub const set_fpu = iow(0x8d, Fpu);
330 pub const set_cpuid = iow(0x90, CpuidHeader);
331 pub const get_cpuid = iowr(0x91, CpuidHeader);
332 pub const get_mp_state = ior(0x98, MpState);
333 pub const set_mp_state = iow(0x99, MpState);
334 pub const get_vcpu_events = ior(0x9f, VcpuEvents);
335 pub const set_vcpu_events = iow(0xa0, VcpuEvents);
336 pub const get_debug_registers = ior(0xa1, DebugRegisters);
337 pub const set_debug_registers = iow(0xa2, DebugRegisters);
338 pub const get_xsave = ior(0xa4, Xsave);
339 pub const set_xsave = iow(0xa5, Xsave);
340 pub const get_xcrs = ior(0xa6, Xcrs);
341 pub const set_xcrs = iow(0xa7, Xcrs);
342 };
343
344 pub const Extension = enum(u32) {
345 user_memory = 3,
346 extended_cpuid = 7,
347 recommended_vcpus = 9,
348 memory_slots = 10,
349 mp_state = 14,
350 vcpu_events = 41,
351 debug_registers = 50,
352 xsave = 55,
353 xcrs = 56,
354 maximum_vcpus = 66,
355 read_only_memory = 81,
356 maximum_vcpu_id = 128,
357 immediate_exit = 136,
358 special_registers_2 = 200,
359 xsave_2 = 208,
360 };
361
362 pub const MemoryFlags = struct {
363 log_dirty_pages: bool = false,
364 read_only: bool = false,
365
366 pub fn bits(self: MemoryFlags) u32 {
367 return @as(u32, @intFromBool(self.log_dirty_pages)) |
368 (@as(u32, @intFromBool(self.read_only)) << 1);
369 }
370 };
371
372 test "Linux x86 KVM ABI layouts match the UAPI" {
373 try std.testing.expectEqual(@as(usize, 32), @sizeOf(UserMemoryRegion));
374 try std.testing.expectEqual(@as(usize, 32), @sizeOf(RunHeader));
375 try std.testing.expectEqual(@as(usize, 144), @sizeOf(Registers));
376 try std.testing.expectEqual(@as(usize, 24), @sizeOf(Segment));
377 try std.testing.expectEqual(@as(usize, 16), @sizeOf(DescriptorTable));
378 try std.testing.expectEqual(@as(usize, 312), @sizeOf(SpecialRegisters));
379 try std.testing.expectEqual(@as(usize, 416), @sizeOf(Fpu));
380 try std.testing.expectEqual(@as(usize, 64), @sizeOf(VcpuEvents));
381 try std.testing.expectEqual(@as(usize, 128), @sizeOf(DebugRegisters));
382 try std.testing.expectEqual(@as(usize, 4096), @sizeOf(Xsave));
383 try std.testing.expectEqual(@as(usize, 392), @sizeOf(Xcrs));
384 try std.testing.expectEqual(@as(usize, 48), @sizeOf(Clock));
385 try std.testing.expectEqual(@as(usize, 16), @sizeOf(MsrEntry));
386 try std.testing.expectEqual(@as(usize, 40), @sizeOf(CpuidEntry));
387 try std.testing.expectEqual(@as(usize, 4), @sizeOf(MpState));
388 }
389
390 test "Linux x86 KVM ABI alignments and run offsets match the UAPI" {
391 try std.testing.expectEqual(@as(usize, 8), @alignOf(UserMemoryRegion));
392 try std.testing.expectEqual(@as(usize, 8), @alignOf(RunHeader));
393 try std.testing.expectEqual(@as(usize, 8), @alignOf(SpecialRegisters));
394 try std.testing.expectEqual(@as(usize, 8), @alignOf(VcpuEvents));
395 try std.testing.expectEqual(@as(usize, 8), @offsetOf(RunHeader, "exit_reason"));
396 try std.testing.expectEqual(@as(usize, 16), @offsetOf(RunHeader, "cr8"));
397 try std.testing.expectEqual(@as(usize, 24), @offsetOf(RunHeader, "apic_base"));
398 try std.testing.expectEqual(@as(usize, 32), run_union_offset);
399 }
400
401 test "Linux x86 KVM ioctl numbers match the UAPI" {
402 try std.testing.expectEqual(@as(u32, 0x0000ae00), request.get_api_version);
403 try std.testing.expectEqual(@as(u32, 0x0000ae01), request.create_vm);
404 try std.testing.expectEqual(@as(u32, 0x0000ae03), request.check_extension);
405 try std.testing.expectEqual(@as(u32, 0x0000ae04), request.get_vcpu_mmap_size);
406 try std.testing.expectEqual(@as(u32, 0xc008ae05), request.get_supported_cpuid);
407 try std.testing.expectEqual(@as(u32, 0x0000ae41), request.create_vcpu);
408 try std.testing.expectEqual(@as(u32, 0x4020ae46), request.set_user_memory_region);
409 try std.testing.expectEqual(@as(u32, 0x4030ae7b), request.set_clock);
410 try std.testing.expectEqual(@as(u32, 0x8030ae7c), request.get_clock);
411 try std.testing.expectEqual(@as(u32, 0x0000ae80), request.run);
412 try std.testing.expectEqual(@as(u32, 0x8090ae81), request.get_registers);
413 try std.testing.expectEqual(@as(u32, 0x4090ae82), request.set_registers);
414 try std.testing.expectEqual(@as(u32, 0x8138ae83), request.get_special_registers);
415 try std.testing.expectEqual(@as(u32, 0x4138ae84), request.set_special_registers);
416 try std.testing.expectEqual(@as(u32, 0xc008ae88), request.get_msrs);
417 try std.testing.expectEqual(@as(u32, 0x4008ae89), request.set_msrs);
418 try std.testing.expectEqual(@as(u32, 0x81a0ae8c), request.get_fpu);
419 try std.testing.expectEqual(@as(u32, 0x41a0ae8d), request.set_fpu);
420 try std.testing.expectEqual(@as(u32, 0x4008ae90), request.set_cpuid);
421 try std.testing.expectEqual(@as(u32, 0xc008ae91), request.get_cpuid);
422 try std.testing.expectEqual(@as(u32, 0x8004ae98), request.get_mp_state);
423 try std.testing.expectEqual(@as(u32, 0x4004ae99), request.set_mp_state);
424 try std.testing.expectEqual(@as(u32, 0x8040ae9f), request.get_vcpu_events);
425 try std.testing.expectEqual(@as(u32, 0x4040aea0), request.set_vcpu_events);
426 try std.testing.expectEqual(@as(u32, 0x8080aea1), request.get_debug_registers);
427 try std.testing.expectEqual(@as(u32, 0x4080aea2), request.set_debug_registers);
428 try std.testing.expectEqual(@as(u32, 0x9000aea4), request.get_xsave);
429 try std.testing.expectEqual(@as(u32, 0x5000aea5), request.set_xsave);
430 try std.testing.expectEqual(@as(u32, 0x8188aea6), request.get_xcrs);
431 try std.testing.expectEqual(@as(u32, 0x4188aea7), request.set_xcrs);
432 }
433
434 test "static variable-state buffers reject capacity plus one" {
435 const Buffer = MsrBuffer(2);
436 _ = try Buffer.init(&.{ 0x10, 0x1b });
437 try std.testing.expectError(error.TooManyEntries, Buffer.init(&.{ 1, 2, 3 }));
438
439 const Cpuids = CpuidBuffer(3);
440 const cpuids = Cpuids.init();
441 try std.testing.expectEqual(@as(u32, 3), cpuids.count);
442 }