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 }