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};