| 1 | const std = @import("std"); |
| 2 | const uefi = std.os.uefi; |
| 3 | const Guid = uefi.Guid; |
| 4 | const TableHeader = uefi.tables.TableHeader; |
| 5 | const Time = uefi.Time; |
| 6 | const TimeCapabilities = uefi.TimeCapabilities; |
| 7 | const Status = uefi.Status; |
| 8 | const MemoryDescriptor = uefi.tables.MemoryDescriptor; |
| 9 | const MemoryMapSlice = uefi.tables.MemoryMapSlice; |
| 10 | const ResetType = uefi.tables.ResetType; |
| 11 | const CapsuleHeader = uefi.tables.CapsuleHeader; |
| 12 | const PhysicalAddress = uefi.tables.PhysicalAddress; |
| 13 | const cc = uefi.cc; |
| 14 | const 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. |
| 24 | pub 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: @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 | }; |