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 }