lib/sys/src/kvm/types.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const abi = @import("abi.zig");
  3 
  4 pub const Descriptor = std.posix.fd_t;
  5 pub const Errno = std.posix.E;
  6 pub const page_size = std.heap.page_size_min;
  7 pub const maximum_interrupt_retries: usize = 128;
  8 
  9 pub const Availability = enum {
 10     available,
 11     unsupported,
 12     unavailable,
 13     denied,
 14     failed,
 15 };
 16 
 17 pub const CapabilityStage = enum {
 18     platform,
 19     device,
 20     api_version,
 21     run_mapping,
 22     extensions,
 23     complete,
 24 };
 25 
 26 pub const Extensions = struct {
 27     user_memory: u32 = 0,
 28     extended_cpuid: u32 = 0,
 29     recommended_vcpus: u32 = 0,
 30     memory_slots: u32 = 0,
 31     multiprocessor_state: u32 = 0,
 32     vcpu_events: u32 = 0,
 33     debug_registers: u32 = 0,
 34     xsave: u32 = 0,
 35     xcrs: u32 = 0,
 36     maximum_vcpus: u32 = 0,
 37     read_only_memory: u32 = 0,
 38     maximum_vcpu_id: u32 = 0,
 39     immediate_exit: u32 = 0,
 40     special_registers_2: u32 = 0,
 41     xsave_2: u32 = 0,
 42 };
 43 
 44 pub const CapabilityReceipt = struct {
 45     availability: Availability = .failed,
 46     stage: CapabilityStage = .platform,
 47     errno_code: u16 = 0,
 48     api_version: u32 = 0,
 49     run_mapping_bytes: u32 = 0,
 50     extensions: Extensions = .{},
 51 };
 52 
 53 pub const MemoryRegion = struct {
 54     slot: u32,
 55     guest_physical_address: u64,
 56     memory: []align(page_size) u8,
 57     flags: abi.MemoryFlags = .{},
 58 };
 59 
 60 pub const CreateError = error{
 61     AccessDenied,
 62     Closed,
 63     CreateFailed,
 64     ResourceLimit,
 65     UnsupportedOperation,
 66 };
 67 
 68 pub const MemoryError = error{
 69     AccessDenied,
 70     Closed,
 71     GuestRangeOverflow,
 72     InvalidAlignment,
 73     InvalidSlot,
 74     MemoryRegionFailed,
 75     UnsupportedFlags,
 76 };
 77 
 78 pub const RunError = error{
 79     AccessDenied,
 80     Closed,
 81     Interrupted,
 82     InvalidRunState,
 83     RunFailed,
 84     UnsupportedOperation,
 85     WouldBlock,
 86 };
 87 
 88 pub const StateError = error{
 89     AccessDenied,
 90     CapacityExceeded,
 91     Closed,
 92     PartialTransfer,
 93     StateFailed,
 94     UnsupportedState,
 95 };
 96 
 97 pub const RawResult = union(enum) {
 98     value: usize,
 99     failure: Errno,
100 };
101 
102 pub const MapResult = union(enum) {
103     mapping: []align(page_size) u8,
104     failure: Errno,
105 };
106 
107 pub const Operations = struct {
108     context: *anyopaque,
109     platform_supported: *const fn (*anyopaque) bool,
110     open_device: *const fn (*anyopaque) RawResult,
111     control_descriptor: *const fn (*anyopaque, Descriptor, u32, usize) RawResult,
112     map_run: *const fn (*anyopaque, Descriptor, usize) MapResult,
113     unmap_run: *const fn (*anyopaque, []align(page_size) u8) void,
114     close_descriptor: *const fn (*anyopaque, Descriptor) void,
115 
116     pub fn platformSupported(self: @This()) bool {
117         return self.platform_supported(self.context);
118     }
119 
120     pub fn openDevice(self: @This()) RawResult {
121         return self.open_device(self.context);
122     }
123 
124     pub fn control(
125         self: @This(),
126         descriptor: Descriptor,
127         request: u32,
128         argument: usize,
129     ) RawResult {
130         return self.control_descriptor(self.context, descriptor, request, argument);
131     }
132 
133     pub fn mapRun(self: @This(), descriptor: Descriptor, len: usize) MapResult {
134         return self.map_run(self.context, descriptor, len);
135     }
136 
137     pub fn unmap(self: @This(), mapping: []align(page_size) u8) void {
138         self.unmap_run(self.context, mapping);
139     }
140 
141     pub fn close(self: @This(), descriptor: Descriptor) void {
142         self.close_descriptor(self.context, descriptor);
143     }
144 };
145 
146 fn Adapter(comptime Implementation: type) type {
147     return struct {
148         fn target(context: *anyopaque) *Implementation {
149             return @ptrCast(@alignCast(context));
150         }
151 
152         fn platformSupported(context: *anyopaque) bool {
153             return target(context).platformSupported();
154         }
155 
156         fn openDevice(context: *anyopaque) RawResult {
157             return target(context).openDevice();
158         }
159 
160         fn control(
161             context: *anyopaque,
162             descriptor: Descriptor,
163             request: u32,
164             argument: usize,
165         ) RawResult {
166             return target(context).control(descriptor, request, argument);
167         }
168 
169         fn mapRun(context: *anyopaque, descriptor: Descriptor, len: usize) MapResult {
170             return target(context).mapRun(descriptor, len);
171         }
172 
173         fn unmap(context: *anyopaque, mapping: []align(page_size) u8) void {
174             target(context).unmap(mapping);
175         }
176 
177         fn close(context: *anyopaque, descriptor: Descriptor) void {
178             target(context).close(descriptor);
179         }
180     };
181 }
182 
183 pub fn operationsFor(implementation: anytype) Operations {
184     const pointer = @typeInfo(@TypeOf(implementation)).pointer;
185     const ImplementationAdapter = Adapter(pointer.child);
186     return .{
187         .context = implementation,
188         .platform_supported = ImplementationAdapter.platformSupported,
189         .open_device = ImplementationAdapter.openDevice,
190         .control_descriptor = ImplementationAdapter.control,
191         .map_run = ImplementationAdapter.mapRun,
192         .unmap_run = ImplementationAdapter.unmap,
193         .close_descriptor = ImplementationAdapter.close,
194     };
195 }
196 
197 pub fn controlRetry(
198     operations: Operations,
199     descriptor: Descriptor,
200     request: u32,
201     argument: usize,
202 ) RawResult {
203     for (0..maximum_interrupt_retries) |_| switch (operations.control(
204         descriptor,
205         request,
206         argument,
207     )) {
208         .value => |value| return .{ .value = value },
209         .failure => |code| {
210             if (code == .INTR) continue;
211             return .{ .failure = code };
212         },
213     };
214     return .{ .failure = .INTR };
215 }
216 
217 pub fn createDescriptor(result: RawResult) CreateError!Descriptor {
218     return switch (result) {
219         .value => |value| std.math.cast(Descriptor, value) orelse error.CreateFailed,
220         .failure => |code| createError(code),
221     };
222 }
223 
224 pub fn createError(code: Errno) CreateError {
225     return switch (code) {
226         .ACCES, .PERM => error.AccessDenied,
227         .MFILE, .NFILE, .NOMEM => error.ResourceLimit,
228         .INVAL, .NODEV, .NOSYS, .OPNOTSUPP => error.UnsupportedOperation,
229         else => error.CreateFailed,
230     };
231 }
232 
233 pub fn stateControl(
234     operations: Operations,
235     descriptor: Descriptor,
236     request: u32,
237     argument: usize,
238 ) StateError!void {
239     switch (controlRetry(operations, descriptor, request, argument)) {
240         .value => return,
241         .failure => |code| return switch (code) {
242             .ACCES, .PERM => error.AccessDenied,
243             .BADF, .NODEV => error.Closed,
244             .@"2BIG" => error.CapacityExceeded,
245             .INVAL, .NOSYS, .NOTTY, .OPNOTSUPP => error.UnsupportedState,
246             else => error.StateFailed,
247         },
248     }
249 }