master
1const std = @import("std");
2const uefi = std.os.uefi;
3const Guid = uefi.Guid;
4const TableHeader = uefi.tables.TableHeader;
5const Time = uefi.Time;
6const TimeCapabilities = uefi.TimeCapabilities;
7const Status = uefi.Status;
8const MemoryDescriptor = uefi.tables.MemoryDescriptor;
9const MemoryMapSlice = uefi.tables.MemoryMapSlice;
10const ResetType = uefi.tables.ResetType;
11const CapsuleHeader = uefi.tables.CapsuleHeader;
12const PhysicalAddress = uefi.tables.PhysicalAddress;
13const cc = uefi.cc;
14const Error = Status.Error;
15
16/// Runtime services are provided by the firmware before and after exitBootServices has been called.
17///
18/// As the runtime_services table may grow with new UEFI versions, it is important to check hdr.header_size.
19///
20/// Some functions may not be supported. Check the RuntimeServicesSupported variable using getVariable.
21/// getVariable is one of the functions that may not be supported.
22///
23/// Some functions may not be called while other functions are running.
24pub const RuntimeServices = extern struct {
25 hdr: TableHeader,
26
27 /// Returns the current time and date information, and the time-keeping capabilities of the hardware platform.
28 _getTime: *const fn (time: *Time, capabilities: ?*TimeCapabilities) callconv(cc) Status,
29
30 /// Sets the current local time and date information
31 _setTime: *const fn (time: *const Time) callconv(cc) Status,
32
33 /// Returns the current wakeup alarm clock setting
34 _getWakeupTime: *const fn (enabled: *bool, pending: *bool, time: *Time) callconv(cc) Status,
35
36 /// Sets the system wakeup alarm clock time
37 _setWakeupTime: *const fn (enable: bool, time: ?*const Time) callconv(cc) Status,
38
39 /// Changes the runtime addressing mode of EFI firmware from physical to virtual.
40 _setVirtualAddressMap: *const fn (mmap_size: usize, descriptor_size: usize, descriptor_version: u32, virtual_map: [*]align(@alignOf(MemoryDescriptor)) u8) callconv(cc) Status,
41
42 /// Determines the new virtual address that is to be used on subsequent memory accesses.
43 _convertPointer: *const fn (debug_disposition: DebugDisposition, address: *?*anyopaque) callconv(cc) Status,
44
45 /// Returns the value of a variable.
46 _getVariable: *const fn (var_name: [*:0]const u16, vendor_guid: *const Guid, attributes: ?*VariableAttributes, data_size: *usize, data: ?*anyopaque) callconv(cc) Status,
47
48 /// Enumerates the current variable names.
49 _getNextVariableName: *const fn (var_name_size: *usize, var_name: ?[*:0]const u16, vendor_guid: *Guid) callconv(cc) Status,
50
51 /// Sets the value of a variable.
52 _setVariable: *const fn (var_name: [*:0]const u16, vendor_guid: *const Guid, attributes: VariableAttributes, data_size: usize, data: [*]const u8) callconv(cc) Status,
53
54 /// Return the next high 32 bits of the platform's monotonic counter
55 _getNextHighMonotonicCount: *const fn (high_count: *u32) callconv(cc) Status,
56
57 /// Resets the entire platform.
58 _resetSystem: *const fn (reset_type: ResetType, reset_status: Status, data_size: usize, reset_data: ?[*]const u16) callconv(cc) noreturn,
59
60 /// Passes capsules to the firmware with both virtual and physical mapping.
61 /// Depending on the intended consumption, the firmware may process the capsule immediately.
62 /// If the payload should persist across a system reset, the reset value returned from
63 /// `queryCapsuleCapabilities` must be passed into resetSystem and will cause the capsule
64 /// to be processed by the firmware as part of the reset process.
65 _updateCapsule: *const fn (capsule_header_array: [*]*const CapsuleHeader, capsule_count: usize, scatter_gather_list: PhysicalAddress) callconv(cc) Status,
66
67 /// Returns if the capsule can be supported via `updateCapsule`
68 _queryCapsuleCapabilities: *const fn (capsule_header_array: [*]*const CapsuleHeader, capsule_count: usize, maximum_capsule_size: *usize, reset_type: *ResetType) callconv(cc) Status,
69
70 /// Returns information about the EFI variables
71 _queryVariableInfo: *const fn (attributes: VariableAttributes, maximum_variable_storage_size: *u64, remaining_variable_storage_size: *u64, maximum_variable_size: *u64) callconv(cc) Status,
72
73 pub const GetTimeError = uefi.UnexpectedError || error{
74 DeviceError,
75 Unsupported,
76 };
77
78 pub const SetTimeError = uefi.UnexpectedError || error{
79 DeviceError,
80 Unsupported,
81 };
82
83 pub const GetWakeupTimeError = uefi.UnexpectedError || error{
84 DeviceError,
85 Unsupported,
86 };
87
88 pub const SetWakeupTimeError = uefi.UnexpectedError || error{
89 InvalidParameter,
90 DeviceError,
91 Unsupported,
92 };
93
94 pub const SetVirtualAddressMapError = uefi.UnexpectedError || error{
95 Unsupported,
96 NoMapping,
97 NotFound,
98 };
99
100 pub const ConvertPointerError = uefi.UnexpectedError || error{
101 InvalidParameter,
102 Unsupported,
103 };
104
105 pub const GetVariableSizeError = uefi.UnexpectedError || error{
106 DeviceError,
107 Unsupported,
108 };
109
110 pub const GetVariableError = GetVariableSizeError || error{
111 BufferTooSmall,
112 };
113
114 pub const SetVariableError = uefi.UnexpectedError || error{
115 InvalidParameter,
116 OutOfResources,
117 DeviceError,
118 WriteProtected,
119 SecurityViolation,
120 NotFound,
121 Unsupported,
122 };
123
124 pub const GetNextHighMonotonicCountError = uefi.UnexpectedError || error{
125 DeviceError,
126 Unsupported,
127 };
128
129 pub const UpdateCapsuleError = uefi.UnexpectedError || error{
130 InvalidParameter,
131 DeviceError,
132 Unsupported,
133 OutOfResources,
134 };
135
136 pub const QueryCapsuleCapabilitiesError = uefi.UnexpectedError || error{
137 Unsupported,
138 OutOfResources,
139 };
140
141 pub const QueryVariableInfoError = uefi.UnexpectedError || error{
142 InvalidParameter,
143 Unsupported,
144 };
145
146 /// Returns the current time and the time capabilities of the platform.
147 pub fn getTime(
148 self: *const RuntimeServices,
149 ) GetTimeError!struct { Time, TimeCapabilities } {
150 var time: Time = undefined;
151 var capabilities: TimeCapabilities = undefined;
152
153 switch (self._getTime(&time, &capabilities)) {
154 .success => return .{ time, capabilities },
155 .device_error => return error.DeviceError,
156 .unsupported => return error.Unsupported,
157 else => |status| return uefi.unexpectedStatus(status),
158 }
159 }
160
161 pub fn setTime(self: *RuntimeServices, time: *const Time) SetTimeError!void {
162 switch (self._setTime(time)) {
163 .success => {},
164 .device_error => return error.DeviceError,
165 .unsupported => return error.Unsupported,
166 else => |status| return uefi.unexpectedStatus(status),
167 }
168 }
169
170 pub const GetWakeupTime = struct {
171 enabled: bool,
172 pending: bool,
173 time: Time,
174 };
175
176 pub fn getWakeupTime(
177 self: *const RuntimeServices,
178 ) GetWakeupTimeError!GetWakeupTime {
179 var result: GetWakeupTime = undefined;
180 switch (self._getWakeupTime(
181 &result.enabled,
182 &result.pending,
183 &result.time,
184 )) {
185 .success => return result,
186 .device_error => return error.DeviceError,
187 .unsupported => return error.Unsupported,
188 else => |status| return uefi.unexpectedStatus(status),
189 }
190 }
191
192 pub const SetWakeupTime = union(enum) {
193 enabled: *const Time,
194 disabled,
195 };
196
197 pub fn setWakeupTime(
198 self: *RuntimeServices,
199 set: SetWakeupTime,
200 ) SetWakeupTimeError!void {
201 switch (self._setWakeupTime(
202 set != .disabled,
203 if (set == .enabled) set.enabled else null,
204 )) {
205 .success => {},
206 .invalid_parameter => return error.InvalidParameter,
207 .device_error => return error.DeviceError,
208 .unsupported => return error.Unsupported,
209 else => |status| return uefi.unexpectedStatus(status),
210 }
211 }
212
213 pub fn setVirtualAddressMap(
214 self: *RuntimeServices,
215 map: MemoryMapSlice,
216 ) SetVirtualAddressMapError!void {
217 switch (self._setVirtualAddressMap(
218 map.info.len * map.info.descriptor_size,
219 map.info.descriptor_size,
220 map.info.descriptor_version,
221 @ptrCast(map.ptr),
222 )) {
223 .success => {},
224 .unsupported => return error.Unsupported,
225 .no_mapping => return error.NoMapping,
226 .not_found => return error.NotFound,
227 else => |status| return uefi.unexpectedStatus(status),
228 }
229 }
230
231 pub fn convertPointer(
232 self: *const RuntimeServices,
233 comptime disposition: DebugDisposition,
234 cvt: @FieldType(PointerConversion, @tagName(disposition)),
235 ) ConvertPointerError!?@FieldType(PointerConversion, @tagName(disposition)) {
236 var pointer = cvt;
237
238 switch (self._convertPointer(disposition, @ptrCast(&pointer))) {
239 .success => return pointer,
240 .not_found => return null,
241 .invalid_parameter => return error.InvalidParameter,
242 .unsupported => return error.Unsupported,
243 else => |status| return uefi.unexpectedStatus(status),
244 }
245 }
246
247 /// Returns the length of the variable's data and its attributes.
248 pub fn getVariableSize(
249 self: *const RuntimeServices,
250 name: [*:0]const u16,
251 guid: *const Guid,
252 ) GetVariableSizeError!?struct { usize, VariableAttributes } {
253 var size: usize = 0;
254 var attrs: VariableAttributes = undefined;
255
256 switch (self._getVariable(
257 name,
258 guid,
259 &attrs,
260 &size,
261 null,
262 )) {
263 .buffer_too_small => return .{ size, attrs },
264 .not_found => return null,
265 .device_error => return error.DeviceError,
266 .unsupported => return error.Unsupported,
267 else => |status| return uefi.unexpectedStatus(status),
268 }
269 }
270
271 /// To determine the minimum necessary buffer size for the variable, call
272 /// `getVariableSize` first.
273 pub fn getVariable(
274 self: *const RuntimeServices,
275 name: [*:0]const u16,
276 guid: *const Guid,
277 buffer: []u8,
278 ) GetVariableError!?struct { []u8, VariableAttributes } {
279 var attrs: VariableAttributes = undefined;
280 var len = buffer.len;
281
282 switch (self._getVariable(
283 name,
284 guid,
285 &attrs,
286 &len,
287 buffer.ptr,
288 )) {
289 .success => return .{ buffer[0..len], attrs },
290 .not_found => return null,
291 .buffer_too_small => return error.BufferTooSmall,
292 .device_error => return error.DeviceError,
293 .unsupported => return error.Unsupported,
294 else => |status| return uefi.unexpectedStatus(status),
295 }
296 }
297
298 pub fn variableNameIterator(
299 self: *const RuntimeServices,
300 buffer: []u16,
301 ) VariableNameIterator {
302 buffer[0] = 0;
303 return .{
304 .services = self,
305 .buffer = buffer,
306 .guid = undefined,
307 };
308 }
309
310 pub fn setVariable(
311 self: *RuntimeServices,
312 name: [*:0]const u16,
313 guid: *const Guid,
314 attributes: VariableAttributes,
315 data: []const u8,
316 ) SetVariableError!void {
317 switch (self._setVariable(
318 name,
319 guid,
320 attributes,
321 data.len,
322 data.ptr,
323 )) {
324 .success => {},
325 .invalid_parameter => return error.InvalidParameter,
326 .out_of_resources => return error.OutOfResources,
327 .device_error => return error.DeviceError,
328 .write_protected => return error.WriteProtected,
329 .security_violation => return error.SecurityViolation,
330 .not_found => return error.NotFound,
331 .unsupported => return error.Unsupported,
332 else => |status| return uefi.unexpectedStatus(status),
333 }
334 }
335
336 pub fn getNextHighMonotonicCount(self: *const RuntimeServices) GetNextHighMonotonicCountError!u32 {
337 var cnt: u32 = undefined;
338 switch (self._getNextHighMonotonicCount(&cnt)) {
339 .success => return cnt,
340 .device_error => return error.DeviceError,
341 .unsupported => return error.Unsupported,
342 else => |status| return uefi.unexpectedStatus(status),
343 }
344 }
345
346 pub fn resetSystem(
347 self: *RuntimeServices,
348 reset_type: ResetType,
349 reset_status: Status,
350 data: ?[]align(2) const u8,
351 ) noreturn {
352 self._resetSystem(
353 reset_type,
354 reset_status,
355 if (data) |d| d.len else 0,
356 if (data) |d| @ptrCast(@alignCast(d.ptr)) else null,
357 );
358 }
359
360 pub fn updateCapsule(
361 self: *RuntimeServices,
362 capsules: []*const CapsuleHeader,
363 scatter_gather_list: PhysicalAddress,
364 ) UpdateCapsuleError!void {
365 switch (self._updateCapsule(
366 capsules.ptr,
367 capsules.len,
368 scatter_gather_list,
369 )) {
370 .success => {},
371 .invalid_parameter => return error.InvalidParameter,
372 .device_error => return error.DeviceError,
373 .unsupported => return error.Unsupported,
374 .out_of_resources => return error.OutOfResources,
375 else => |status| return uefi.unexpectedStatus(status),
376 }
377 }
378
379 pub fn queryCapsuleCapabilities(
380 self: *const RuntimeServices,
381 capsules: []*const CapsuleHeader,
382 ) QueryCapsuleCapabilitiesError!struct { u64, ResetType } {
383 var max_capsule_size: u64 = undefined;
384 var reset_type: ResetType = undefined;
385
386 switch (self._queryCapsuleCapabilities(
387 capsules.ptr,
388 capsules.len,
389 &max_capsule_size,
390 &reset_type,
391 )) {
392 .success => return .{ max_capsule_size, reset_type },
393 .unsupported => return error.Unsupported,
394 .out_of_resources => return error.OutOfResources,
395 else => |status| return uefi.unexpectedStatus(status),
396 }
397 }
398
399 pub fn queryVariableInfo(
400 self: *const RuntimeServices,
401 // Note: .append_write is ignored
402 attributes: VariableAttributes,
403 ) QueryVariableInfoError!VariableInfo {
404 var res: VariableInfo = undefined;
405
406 switch (self._queryVariableInfo(
407 attributes,
408 &res.max_variable_storage_size,
409 &res.remaining_variable_storage_size,
410 &res.max_variable_size,
411 )) {
412 .success => return res,
413 .invalid_parameter => return error.InvalidParameter,
414 .unsupported => return error.Unsupported,
415 else => |status| return uefi.unexpectedStatus(status),
416 }
417 }
418
419 pub const DebugDisposition = enum(usize) {
420 const Bits = packed struct(usize) {
421 optional_ptr: bool = false,
422 _pad: std.meta.Int(.unsigned, @bitSizeOf(usize) - 1) = 0,
423 };
424
425 pointer = @bitCast(Bits{}),
426 optional = @bitCast(Bits{ .optional_ptr = true }),
427 _,
428 };
429
430 pub const PointerConversion = union(DebugDisposition) {
431 pointer: *anyopaque,
432 optional: ?*anyopaque,
433 };
434
435 pub const VariableAttributes = packed struct(u32) {
436 non_volatile: bool = false,
437 bootservice_access: bool = false,
438 runtime_access: bool = false,
439 hardware_error_record: bool = false,
440 /// Note: deprecated and should be considered reserved.
441 authenticated_write_access: bool = false,
442 time_based_authenticated_write_access: bool = false,
443 append_write: bool = false,
444 /// Indicates that the variable payload begins with a EFI_VARIABLE_AUTHENTICATION_3
445 /// structure, and potentially more structures as indicated by fields of
446 /// this structure.
447 enhanced_authenticated_access: bool = false,
448 _pad: u24 = 0,
449 };
450
451 pub const VariableAuthentication3 = extern struct {
452 version: u8 = 1,
453 type: Type,
454 metadata_size: u32,
455 flags: Flags,
456
457 pub fn payloadConst(self: *const VariableAuthentication3) []const u8 {
458 return @constCast(self).payload();
459 }
460
461 pub fn payload(self: *VariableAuthentication3) []u8 {
462 var ptr: [*]u8 = @ptrCast(self);
463 return ptr[@sizeOf(VariableAuthentication3)..self.metadata_size];
464 }
465
466 pub const Flags = packed struct(u32) {
467 update_cert: bool = false,
468 _pad: u31 = 0,
469 };
470
471 pub const Type = enum(u8) {
472 timestamp = 1,
473 nonce = 2,
474 _,
475 };
476 };
477
478 pub const VariableInfo = struct {
479 max_variable_storage_size: u64,
480 remaining_variable_storage_size: u64,
481 max_variable_size: u64,
482 };
483
484 pub const VariableNameIterator = struct {
485 pub const NextSizeError = uefi.UnexpectedError || error{
486 DeviceError,
487 Unsupported,
488 };
489
490 pub const IterateVariableNameError = NextSizeError || error{
491 BufferTooSmall,
492 };
493
494 services: *const RuntimeServices,
495 buffer: []u16,
496 guid: Guid,
497
498 pub fn nextSize(self: *VariableNameIterator) NextSizeError!?usize {
499 var len: usize = 0;
500 switch (self.services._getNextVariableName(
501 &len,
502 null,
503 &self.guid,
504 )) {
505 .buffer_too_small => return len,
506 .not_found => return null,
507 .device_error => return error.DeviceError,
508 .unsupported => return error.Unsupported,
509 else => |status| return uefi.unexpectedStatus(status),
510 }
511 }
512
513 /// Call `nextSize` to get the length of the next variable name and check
514 /// if `buffer` is large enough to hold the name.
515 pub fn next(
516 self: *VariableNameIterator,
517 ) IterateVariableNameError!?[:0]const u16 {
518 var len = self.buffer.len;
519 switch (self.services._getNextVariableName(
520 &len,
521 @ptrCast(self.buffer.ptr),
522 &self.guid,
523 )) {
524 .success => return self.buffer[0 .. len - 1 :0],
525 .not_found => return null,
526 .buffer_too_small => return error.BufferTooSmall,
527 .device_error => return error.DeviceError,
528 .unsupported => return error.Unsupported,
529 else => |status| return uefi.unexpectedStatus(status),
530 }
531 }
532 };
533
534 pub const signature: u64 = 0x56524553544e5552;
535};