lib/sys/src/kvm/vm.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const abi = @import("abi.zig");
3 const types = @import("types.zig");
4 const vcpu = @import("vcpu.zig");
5
6 pub const Vm = struct {
7 pub const descriptor_capacity: usize = 1;
8
9 operations: types.Operations,
10 descriptor: types.Descriptor,
11 run_mapping_bytes: u32,
12 extensions: types.Extensions,
13 active: bool = true,
14
15 pub fn deinit(self: *@This()) void {
16 if (!self.active) return;
17 std.debug.assert(self.descriptor >= 0);
18 self.operations.close(self.descriptor);
19 self.active = false;
20 }
21
22 pub fn registerMemory(
23 self: *@This(),
24 region: types.MemoryRegion,
25 ) types.MemoryError!void {
26 if (!self.active) return error.Closed;
27 try validateMemory(region, self.extensions);
28 const raw: abi.UserMemoryRegion = .{
29 .slot = region.slot,
30 .flags = region.flags.bits(),
31 .guest_physical_address = region.guest_physical_address,
32 .memory_size = @intCast(region.memory.len),
33 .userspace_address = @intFromPtr(region.memory.ptr),
34 };
35 try memoryControl(self.operations, self.descriptor, &raw);
36 }
37
38 pub fn unregisterMemory(self: *@This(), slot: u32) types.MemoryError!void {
39 if (!self.active) return error.Closed;
40 if (self.extensions.memory_slots != 0 and slot >= self.extensions.memory_slots) {
41 return error.InvalidSlot;
42 }
43 const raw: abi.UserMemoryRegion = .{
44 .slot = slot,
45 .flags = 0,
46 .guest_physical_address = 0,
47 .memory_size = 0,
48 .userspace_address = 0,
49 };
50 try memoryControl(self.operations, self.descriptor, &raw);
51 }
52
53 pub fn createVcpu(self: *@This(), identifier: u32) types.CreateError!vcpu.Vcpu {
54 if (!self.active) return error.Closed;
55 if (self.extensions.maximum_vcpu_id != 0 and
56 identifier >= self.extensions.maximum_vcpu_id)
57 {
58 return error.UnsupportedOperation;
59 }
60 const result = types.controlRetry(
61 self.operations,
62 self.descriptor,
63 abi.request.create_vcpu,
64 identifier,
65 );
66 const descriptor = try types.createDescriptor(result);
67 return createVcpuMapping(self, descriptor);
68 }
69
70 pub fn getClock(self: *@This()) types.StateError!abi.Clock {
71 if (!self.active) return error.Closed;
72 var clock = std.mem.zeroes(abi.Clock);
73 try types.stateControl(
74 self.operations,
75 self.descriptor,
76 abi.request.get_clock,
77 @intFromPtr(&clock),
78 );
79 return clock;
80 }
81
82 pub fn setClock(self: *@This(), clock: *const abi.Clock) types.StateError!void {
83 if (!self.active) return error.Closed;
84 try types.stateControl(
85 self.operations,
86 self.descriptor,
87 abi.request.set_clock,
88 @intFromPtr(clock),
89 );
90 }
91
92 fn createVcpuMapping(
93 self: *@This(),
94 descriptor: types.Descriptor,
95 ) types.CreateError!vcpu.Vcpu {
96 std.debug.assert(self.run_mapping_bytes >= abi.minimum_run_bytes);
97 const mapping = switch (self.operations.mapRun(
98 descriptor,
99 self.run_mapping_bytes,
100 )) {
101 .mapping => |value| value,
102 .failure => |code| {
103 self.operations.close(descriptor);
104 return types.createError(code);
105 },
106 };
107 std.debug.assert(mapping.len >= self.run_mapping_bytes);
108 return .{
109 .operations = self.operations,
110 .descriptor = descriptor,
111 .mapping = mapping,
112 .extensions = self.extensions,
113 };
114 }
115 };
116
117 fn validateMemory(
118 region: types.MemoryRegion,
119 extensions: types.Extensions,
120 ) types.MemoryError!void {
121 if (extensions.memory_slots != 0 and region.slot >= extensions.memory_slots) {
122 return error.InvalidSlot;
123 }
124 if (region.flags.read_only and extensions.read_only_memory == 0) {
125 return error.UnsupportedFlags;
126 }
127 if (region.memory.len == 0) return error.InvalidAlignment;
128 if (region.memory.len % types.page_size != 0) return error.InvalidAlignment;
129 if (region.guest_physical_address % types.page_size != 0) {
130 return error.InvalidAlignment;
131 }
132 _ = std.math.add(
133 u64,
134 region.guest_physical_address,
135 region.memory.len,
136 ) catch return error.GuestRangeOverflow;
137 }
138
139 fn memoryControl(
140 operations: types.Operations,
141 descriptor: types.Descriptor,
142 region: *const abi.UserMemoryRegion,
143 ) types.MemoryError!void {
144 const result = types.controlRetry(
145 operations,
146 descriptor,
147 abi.request.set_user_memory_region,
148 @intFromPtr(region),
149 );
150 switch (result) {
151 .value => return,
152 .failure => |code| return switch (code) {
153 .ACCES, .PERM => error.AccessDenied,
154 else => error.MemoryRegionFailed,
155 },
156 }
157 }