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 }