1const std = @import("std");
2const uefi = std.os.uefi;
3const Event = uefi.Event;
4const EventRegistration = uefi.EventRegistration;
5const Guid = uefi.Guid;
6const Handle = uefi.Handle;
7const Page = uefi.Page;
8const Pages = uefi.Pages;
9const Status = uefi.Status;
10const TableHeader = uefi.tables.TableHeader;
11const DevicePathProtocol = uefi.protocol.DevicePath;
12const AllocateLocation = uefi.tables.AllocateLocation;
13const AllocateType = uefi.tables.AllocateType;
14const MemoryType = uefi.tables.MemoryType;
15const MemoryDescriptor = uefi.tables.MemoryDescriptor;
16const MemoryMapKey = uefi.tables.MemoryMapKey;
17const MemoryMapInfo = uefi.tables.MemoryMapInfo;
18const MemoryMapSlice = uefi.tables.MemoryMapSlice;
19const TimerDelay = uefi.tables.TimerDelay;
20const InterfaceType = uefi.tables.InterfaceType;
21const LocateSearch = uefi.tables.LocateSearch;
22const LocateSearchType = uefi.tables.LocateSearchType;
23const OpenProtocolArgs = uefi.tables.OpenProtocolArgs;
24const OpenProtocolAttributes = uefi.tables.OpenProtocolAttributes;
25const ProtocolInformationEntry = uefi.tables.ProtocolInformationEntry;
26const EventNotify = uefi.tables.EventNotify;
27const cc = uefi.cc;
28const Error = Status.Error;
29
30/// Boot services are services provided by the system's firmware until the operating system takes
31/// over control over the hardware by calling exitBootServices.
32///
33/// Boot Services must not be used after exitBootServices has been called. The only exception is
34/// getMemoryMap, which may be used after the first unsuccessful call to exitBootServices.
35/// After successfully calling exitBootServices, system_table.console_in_handle, system_table.con_in,
36/// system_table.console_out_handle, system_table.con_out, system_table.standard_error_handle,
37/// system_table.std_err, and system_table.boot_services should be set to null. After setting these
38/// attributes to null, system_table.hdr.crc32 must be recomputed.
39///
40/// As the boot_services table may grow with new UEFI versions, it is important to check hdr.header_size.
41pub const BootServices = extern struct {
42 hdr: TableHeader,
43
44 /// Raises a task's priority level and returns its previous level.
45 raiseTpl: *const fn (new_tpl: TaskPriorityLevel) callconv(cc) TaskPriorityLevel,
46
47 /// Restores a task's priority level to its previous value.
48 restoreTpl: *const fn (old_tpl: TaskPriorityLevel) callconv(cc) void,
49
50 /// Allocates memory pages from the system.
51 _allocatePages: *const fn (alloc_type: AllocateType, mem_type: MemoryType, pages: usize, memory: *[*]align(4096) Page) callconv(cc) Status,
52
53 /// Frees memory pages.
54 _freePages: *const fn (memory: [*]align(4096) Page, pages: usize) callconv(cc) Status,
55
56 /// Returns the current memory map.
57 _getMemoryMap: *const fn (mmap_size: *usize, mmap: ?[*]align(@alignOf(MemoryDescriptor)) u8, map_key: *MemoryMapKey, descriptor_size: *usize, descriptor_version: *u32) callconv(cc) Status,
58
59 /// Allocates pool memory.
60 _allocatePool: *const fn (pool_type: MemoryType, size: usize, buffer: *[*]align(8) u8) callconv(cc) Status,
61
62 /// Returns pool memory to the system.
63 _freePool: *const fn (buffer: [*]align(8) u8) callconv(cc) Status,
64
65 /// Creates an event.
66 _createEvent: *const fn (type: u32, notify_tpl: TaskPriorityLevel, notify_func: ?*const fn (Event, ?*anyopaque) callconv(cc) void, notify_ctx: ?*anyopaque, event: *Event) callconv(cc) Status,
67
68 /// Sets the type of timer and the trigger time for a timer event.
69 _setTimer: *const fn (event: Event, type: TimerDelay, trigger_time: u64) callconv(cc) Status,
70
71 /// Stops execution until an event is signaled.
72 _waitForEvent: *const fn (event_len: usize, events: [*]const Event, index: *usize) callconv(cc) Status,
73
74 /// Signals an event.
75 _signalEvent: *const fn (event: Event) callconv(cc) Status,
76
77 /// Closes an event.
78 _closeEvent: *const fn (event: Event) callconv(cc) Status,
79
80 /// Checks whether an event is in the signaled state.
81 _checkEvent: *const fn (event: Event) callconv(cc) Status,
82
83 /// Installs a protocol interface on a device handle. If the handle does not exist, it is created
84 /// and added to the list of handles in the system. installMultipleProtocolInterfaces()
85 /// performs more error checking than installProtocolInterface(), so its use is recommended over this.
86 _installProtocolInterface: *const fn (handle: Handle, protocol: *const Guid, interface_type: InterfaceType, interface: *anyopaque) callconv(cc) Status,
87
88 /// Reinstalls a protocol interface on a device handle
89 _reinstallProtocolInterface: *const fn (handle: Handle, protocol: *const Guid, old_interface: *anyopaque, new_interface: *anyopaque) callconv(cc) Status,
90
91 /// Removes a protocol interface from a device handle. Usage of
92 /// uninstallMultipleProtocolInterfaces is recommended over this.
93 _uninstallProtocolInterface: *const fn (handle: Handle, protocol: *const Guid, interface: *anyopaque) callconv(cc) Status,
94
95 /// Queries a handle to determine if it supports a specified protocol.
96 _handleProtocol: *const fn (handle: Handle, protocol: *const Guid, interface: *?*anyopaque) callconv(cc) Status,
97
98 _reserved: *anyopaque,
99
100 /// Creates an event that is to be signaled whenever an interface is installed for a specified protocol.
101 _registerProtocolNotify: *const fn (protocol: *const Guid, event: Event, registration: *EventRegistration) callconv(cc) Status,
102
103 /// Returns an array of handles that support a specified protocol.
104 _locateHandle: *const fn (search_type: LocateSearchType, protocol: ?*const Guid, search_key: ?*const anyopaque, buffer_size: *usize, buffer: ?[*]Handle) callconv(cc) Status,
105
106 /// Locates the handle to a device on the device path that supports the specified protocol
107 _locateDevicePath: *const fn (protocols: *const Guid, device_path: **const DevicePathProtocol, device: *?Handle) callconv(cc) Status,
108
109 /// Adds, updates, or removes a configuration table entry from the EFI System Table.
110 _installConfigurationTable: *const fn (guid: *const Guid, table: ?*anyopaque) callconv(cc) Status,
111
112 /// Loads an EFI image into memory.
113 _loadImage: *const fn (boot_policy: bool, parent_image_handle: Handle, device_path: ?*const DevicePathProtocol, source_buffer: ?[*]const u8, source_size: usize, image_handle: *Handle) callconv(cc) Status,
114
115 /// Transfers control to a loaded image's entry point.
116 _startImage: *const fn (image_handle: Handle, exit_data_size: ?*usize, exit_data: ?*[*]u16) callconv(cc) Status,
117
118 /// Terminates a loaded EFI image and returns control to boot services.
119 _exit: *const fn (image_handle: Handle, exit_status: Status, exit_data_size: usize, exit_data: ?[*]align(2) const u8) callconv(cc) Status,
120
121 /// Unloads an image.
122 _unloadImage: *const fn (image_handle: Handle) callconv(cc) Status,
123
124 /// Terminates all boot services.
125 _exitBootServices: *const fn (image_handle: Handle, map_key: MemoryMapKey) callconv(cc) Status,
126
127 /// Returns a monotonically increasing count for the platform.
128 _getNextMonotonicCount: *const fn (count: *u64) callconv(cc) Status,
129
130 /// Induces a fine-grained stall.
131 _stall: *const fn (microseconds: usize) callconv(cc) Status,
132
133 /// Sets the system's watchdog timer.
134 _setWatchdogTimer: *const fn (timeout: usize, watchdog_code: u64, data_size: usize, watchdog_data: ?[*]const u16) callconv(cc) Status,
135
136 /// Connects one or more drives to a controller.
137 _connectController: *const fn (controller_handle: Handle, driver_image_handle: ?[*:null]?Handle, remaining_device_path: ?*const DevicePathProtocol, recursive: bool) callconv(cc) Status,
138
139 // Disconnects one or more drivers from a controller
140 _disconnectController: *const fn (controller_handle: Handle, driver_image_handle: ?Handle, child_handle: ?Handle) callconv(cc) Status,
141
142 /// Queries a handle to determine if it supports a specified protocol.
143 _openProtocol: *const fn (handle: Handle, protocol: *const Guid, interface: ?*?*anyopaque, agent_handle: ?Handle, controller_handle: ?Handle, attributes: OpenProtocolAttributes) callconv(cc) Status,
144
145 /// Closes a protocol on a handle that was opened using openProtocol().
146 _closeProtocol: *const fn (handle: Handle, protocol: *const Guid, agent_handle: Handle, controller_handle: ?Handle) callconv(cc) Status,
147
148 /// Retrieves the list of agents that currently have a protocol interface opened.
149 _openProtocolInformation: *const fn (handle: Handle, protocol: *const Guid, entry_buffer: *[*]ProtocolInformationEntry, entry_count: *usize) callconv(cc) Status,
150
151 /// Retrieves the list of protocol interface GUIDs that are installed on a handle in a buffer allocated from pool.
152 _protocolsPerHandle: *const fn (handle: Handle, protocol_buffer: *[*]*const Guid, protocol_buffer_count: *usize) callconv(cc) Status,
153
154 /// Returns an array of handles that support the requested protocol in a buffer allocated from pool.
155 _locateHandleBuffer: *const fn (search_type: LocateSearchType, protocol: ?*const Guid, search_key: ?*const anyopaque, num_handles: *usize, buffer: *[*]Handle) callconv(cc) Status,
156
157 /// Returns the first protocol instance that matches the given protocol.
158 _locateProtocol: *const fn (protocol: *const Guid, registration: ?EventRegistration, interface: *?*const anyopaque) callconv(cc) Status,
159
160 /// Installs one or more protocol interfaces into the boot services environment
161 _installMultipleProtocolInterfaces: *const fn (handle: *?Handle, ...) callconv(cc) Status,
162
163 /// Removes one or more protocol interfaces into the boot services environment
164 _uninstallMultipleProtocolInterfaces: *const fn (handle: Handle, ...) callconv(cc) Status,
165
166 /// Computes and returns a 32-bit CRC for a data buffer.
167 _calculateCrc32: *const fn (data: [*]const u8, data_size: usize, *u32) callconv(cc) Status,
168
169 /// Copies the contents of one buffer to another buffer
170 _copyMem: *const fn (dest: [*]u8, src: [*]const u8, len: usize) callconv(cc) void,
171
172 /// Fills a buffer with a specified value
173 _setMem: *const fn (buffer: [*]u8, size: usize, value: u8) callconv(cc) void,
174
175 /// Creates an event in a group.
176 _createEventEx: *const fn (type: u32, notify_tpl: usize, notify_func: EventNotify, notify_ctx: *const anyopaque, event_group: *const Guid, event: *Event) callconv(cc) Status,
177
178 pub const AllocatePagesError = uefi.UnexpectedError || error{
179 OutOfResources,
180 InvalidParameter,
181 NotFound,
182 };
183
184 pub const FreePagesError = uefi.UnexpectedError || error{
185 NotFound,
186 InvalidParameter,
187 };
188
189 pub const GetMemoryMapError = uefi.UnexpectedError || error{
190 InvalidParameter,
191 BufferTooSmall,
192 };
193
194 pub const AllocatePoolError = uefi.UnexpectedError || error{
195 OutOfResources,
196 InvalidParameter,
197 };
198
199 pub const FreePoolError = uefi.UnexpectedError || error{
200 InvalidParameter,
201 };
202
203 pub const CreateEventError = uefi.UnexpectedError || error{
204 InvalidParameter,
205 OutOfResources,
206 };
207
208 pub const SetTimerError = uefi.UnexpectedError || error{
209 InvalidParameter,
210 };
211
212 pub const WaitForEventError = uefi.UnexpectedError || error{
213 InvalidParameter,
214 Unsupported,
215 };
216
217 pub const CheckEventError = uefi.UnexpectedError || error{
218 InvalidParameter,
219 };
220
221 pub const ReinstallProtocolInterfaceError = uefi.UnexpectedError || error{
222 NotFound,
223 AccessDenied,
224 InvalidParameter,
225 };
226
227 pub const HandleProtocolError = uefi.UnexpectedError || error{
228 Unsupported,
229 };
230
231 pub const RegisterProtocolNotifyError = uefi.UnexpectedError || error{
232 OutOfResources,
233 InvalidParameter,
234 };
235
236 pub const NumHandlesError = uefi.UnexpectedError;
237
238 pub const LocateHandleError = uefi.UnexpectedError || error{
239 BufferTooSmall,
240 InvalidParameter,
241 };
242
243 pub const LocateDevicePathError = uefi.UnexpectedError || error{
244 NotFound,
245 InvalidParameter,
246 };
247
248 pub const InstallConfigurationTableError = uefi.UnexpectedError || error{
249 InvalidParameter,
250 OutOfResources,
251 };
252
253 pub const UninstallConfigurationTableError = InstallConfigurationTableError || error{
254 NotFound,
255 };
256
257 pub const LoadImageError = uefi.UnexpectedError || error{
258 NotFound,
259 InvalidParameter,
260 Unsupported,
261 OutOfResources,
262 LoadError,
263 DeviceError,
264 AccessDenied,
265 SecurityViolation,
266 };
267
268 pub const StartImageError = uefi.UnexpectedError || error{
269 InvalidParameter,
270 SecurityViolation,
271 };
272
273 pub const ExitError = uefi.UnexpectedError || error{
274 InvalidParameter,
275 };
276
277 pub const ExitBootServicesError = uefi.UnexpectedError || error{
278 InvalidParameter,
279 };
280
281 pub const GetNextMonotonicCountError = uefi.UnexpectedError || error{
282 DeviceError,
283 InvalidParameter,
284 };
285
286 pub const SetWatchdogTimerError = uefi.UnexpectedError || error{
287 InvalidParameter,
288 Unsupported,
289 DeviceError,
290 };
291
292 pub const ConnectControllerError = uefi.UnexpectedError || error{
293 InvalidParameter,
294 NotFound,
295 SecurityViolation,
296 };
297
298 pub const DisconnectControllerError = uefi.UnexpectedError || error{
299 InvalidParameter,
300 OutOfResources,
301 DeviceError,
302 };
303
304 pub const OpenProtocolError = uefi.UnexpectedError || error{
305 InvalidParameter,
306 Unsupported,
307 AccessDenied,
308 AlreadyStarted,
309 };
310
311 pub const CloseProtocolError = uefi.UnexpectedError || error{
312 InvalidParameter,
313 NotFound,
314 };
315
316 pub const OpenProtocolInformationError = uefi.UnexpectedError || error{
317 OutOfResources,
318 };
319
320 pub const ProtocolsPerHandleError = uefi.UnexpectedError || error{
321 InvalidParameter,
322 OutOfResources,
323 };
324
325 pub const LocateHandleBufferError = uefi.UnexpectedError || error{
326 InvalidParameter,
327 OutOfResources,
328 };
329
330 pub const LocateProtocolError = uefi.UnexpectedError || error{
331 InvalidParameter,
332 };
333
334 pub const InstallProtocolInterfacesError = uefi.UnexpectedError || error{
335 AlreadyStarted,
336 OutOfResources,
337 InvalidParameter,
338 };
339
340 pub const UninstallProtocolInterfacesError = uefi.UnexpectedError || error{
341 InvalidParameter,
342 };
343
344 pub const CalculateCrc32Error = uefi.UnexpectedError || error{
345 InvalidParameter,
346 };
347
348 /// Allocates pages of memory.
349 ///
350 /// This function scans the memory map to locate free pages. When it finds a
351 /// physically contiguous block of pages that is large enough and also satisfies
352 /// the allocation requirements of `alloc_type`, it changes the memory map to
353 /// indicate that the pages are now of type `mem_type`.
354 ///
355 /// In general, UEFI OS loaders and UEFI applications should allocate memory
356 /// (and pool) of type `.loader_data`. UEFI boot service drivers must allocate
357 /// memory (and pool) of type `.boot_services_data`. UREFI runtime drivers
358 /// should allocate memory (and pool) of type `.runtime_services_data`
359 /// (although such allocation can only be made during boot services time).
360 ///
361 /// Allocation requests of `.allocate_any_pages` allocate any available range
362 /// of pages that satisfies the request.
363 ///
364 /// Allocation requests of `.allocate_max_address` allocate any available range
365 /// of pages whose uppermost address is less than or equal to the address
366 /// pointed to by the input.
367 ///
368 /// Allocation requests of `.allocate_address` allocate pages at the address
369 /// pointed to by the input.
370 pub fn allocatePages(
371 self: *BootServices,
372 location: AllocateLocation,
373 mem_type: MemoryType,
374 pages: usize,
375 ) AllocatePagesError![]align(4096) Page {
376 var ptr: [*]align(4096) Page = switch (location) {
377 .any => undefined,
378 .address, .max_address => |ptr| ptr,
379 };
380
381 switch (self._allocatePages(
382 std.meta.activeTag(location),
383 mem_type,
384 pages,
385 &ptr,
386 )) {
387 .success => return ptr[0..pages],
388 .out_of_resources => return error.OutOfResources,
389 .invalid_parameter => return error.InvalidParameter,
390 .not_found => return error.NotFound,
391 else => |status| return uefi.unexpectedStatus(status),
392 }
393 }
394
395 pub fn freePages(self: *BootServices, pages: []align(4096) Page) FreePagesError!void {
396 switch (self._freePages(pages.ptr, pages.len)) {
397 .success => {},
398 .not_found => return error.NotFound,
399 .invalid_parameter => return error.InvalidParameter,
400 else => |status| return uefi.unexpectedStatus(status),
401 }
402 }
403
404 pub fn getMemoryMapInfo(self: *const BootServices) uefi.UnexpectedError!MemoryMapInfo {
405 var info: MemoryMapInfo = undefined;
406 info.len = 0;
407
408 switch (self._getMemoryMap(
409 &info.len,
410 null,
411 &info.key,
412 &info.descriptor_size,
413 &info.descriptor_version,
414 )) {
415 .success, .buffer_too_small => {
416 info.len = @divExact(info.len, info.descriptor_size);
417 return info;
418 },
419 else => |status| return uefi.unexpectedStatus(status),
420 }
421 }
422
423 pub fn getMemoryMap(
424 self: *const BootServices,
425 buffer: []align(@alignOf(MemoryDescriptor)) u8,
426 ) GetMemoryMapError!MemoryMapSlice {
427 var info: MemoryMapInfo = undefined;
428 info.len = buffer.len;
429
430 switch (self._getMemoryMap(
431 &info.len,
432 buffer.ptr,
433 &info.key,
434 &info.descriptor_size,
435 &info.descriptor_version,
436 )) {
437 .success => {
438 info.len = @divExact(info.len, info.descriptor_size);
439 return .{ .info = info, .ptr = buffer.ptr };
440 },
441 .buffer_too_small => return error.BufferTooSmall,
442 .invalid_parameter => return error.InvalidParameter,
443 else => |status| return uefi.unexpectedStatus(status),
444 }
445 }
446
447 /// Allocates a memory region of `size` bytes from memory of type `pool_type`
448 /// and returns the allocated memory. Allocates pages from `.conventional_memory`
449 /// as needed to grow the requested pool type.
450 pub fn allocatePool(
451 self: *BootServices,
452 pool_type: MemoryType,
453 size: usize,
454 ) AllocatePoolError![]align(8) u8 {
455 var ptr: [*]align(8) u8 = undefined;
456
457 switch (self._allocatePool(pool_type, size, &ptr)) {
458 .success => return ptr[0..size],
459 .out_of_resources => return error.OutOfResources,
460 .invalid_parameter => return error.InvalidParameter,
461 else => |status| return uefi.unexpectedStatus(status),
462 }
463 }
464
465 pub fn freePool(self: *BootServices, ptr: [*]align(8) u8) FreePoolError!void {
466 switch (self._freePool(ptr)) {
467 .success => {},
468 .invalid_parameter => return error.InvalidParameter,
469 else => |status| return uefi.unexpectedStatus(status),
470 }
471 }
472
473 pub fn createEvent(
474 self: *BootServices,
475 event_type: uefi.EventType,
476 notify_opts: NotifyOpts,
477 ) CreateEventError!Event {
478 var evt: Event = undefined;
479
480 switch (self._createEvent(
481 @bitCast(event_type),
482 notify_opts.tpl,
483 notify_opts.function,
484 notify_opts.context,
485 &evt,
486 )) {
487 .success => return evt,
488 .invalid_parameter => return error.InvalidParameter,
489 .out_of_resources => return error.OutOfResources,
490 else => |status| return uefi.unexpectedStatus(status),
491 }
492 }
493
494 /// Cancels any previous time trigger setting for the event, and sets a new
495 /// trigger timer for the event.
496 ///
497 /// Returns `error.InvalidParameter` if the event is not a timer event.
498 pub fn setTimer(
499 self: *BootServices,
500 event: Event,
501 @"type": TimerDelay,
502 trigger_time: u64,
503 ) SetTimerError!void {
504 switch (self._setTimer(event, @"type", trigger_time)) {
505 .success => {},
506 .invalid_parameter => return error.InvalidParameter,
507 else => |status| return uefi.unexpectedStatus(status),
508 }
509 }
510
511 /// Returns the event that was signaled, along with its index in the slice.
512 pub fn waitForEvent(
513 self: *BootServices,
514 events: []const Event,
515 ) WaitForEventError!struct { *const Event, usize } {
516 var idx: usize = undefined;
517 switch (self._waitForEvent(events.len, events.ptr, &idx)) {
518 .success => return .{ &events[idx], idx },
519 .invalid_parameter => return error.InvalidParameter,
520 .unsupported => return error.Unsupported,
521 else => |status| return uefi.unexpectedStatus(status),
522 }
523 }
524
525 /// If `event` is `EventType.signal`, then the event’s notification function
526 /// is scheduled to be invoked at the event’s notification task priority level.
527 /// This function may be invoked from any task priority level.
528 ///
529 /// If the supplied Event is a part of an event group, then all of the events
530 /// in the event group are also signaled and their notification functions are
531 /// scheduled.
532 ///
533 /// When signaling an event group, it is possible to create an event in the
534 /// group, signal it and then close the event to remove it from the group.
535 pub fn signalEvent(self: *BootServices, event: Event) uefi.UnexpectedError!void {
536 switch (self._signalEvent(event)) {
537 .success => {},
538 else => |status| return uefi.unexpectedStatus(status),
539 }
540 }
541
542 pub fn closeEvent(self: *BootServices, event: Event) uefi.UnexpectedError!void {
543 switch (self._closeEvent(event)) {
544 .success => {},
545 else => |status| return uefi.unexpectedStatus(status),
546 }
547 }
548
549 /// Checks to see whether an event is signaled.
550 ///
551 /// The underlying function is equivalent to this pseudo-code:
552 /// ```
553 /// if (event.type.signal)
554 /// return error.InvalidParameter;
555 ///
556 /// if (event.signaled) {
557 /// event.signaled = false;
558 /// return true;
559 /// }
560 ///
561 /// const notify = event.notification_function orelse return false;
562 /// notify();
563 ///
564 /// if (event.signaled) {
565 /// event.signaled = false;
566 /// return true;
567 /// }
568 ///
569 /// return false;
570 /// ```
571 pub fn checkEvent(self: *BootServices, event: Event) CheckEventError!bool {
572 switch (self._checkEvent(event)) {
573 .success => return true,
574 .not_ready => return false,
575 .invalid_parameter => return error.InvalidParameter,
576 else => |status| return uefi.unexpectedStatus(status),
577 }
578 }
579
580 /// See `installProtocolInterfaces`.
581 ///
582 /// Does not call `self._installProtocolInterface`, because
583 /// `self._installMultipleProtocolInterfaces` performs more error checks.
584 pub fn installProtocolInterface(
585 self: *BootServices,
586 handle: ?Handle,
587 interface: anytype,
588 ) InstallProtocolInterfacesError!Handle {
589 return self.installProtocolInterfaces(handle, .{
590 interface,
591 });
592 }
593
594 /// Reinstalls a protocol interface on a device handle.
595 ///
596 /// `new` may be the same as `old`. If it is, the registered protocol notifications
597 /// occur for the handle without replacing the interface on the handle.
598 ///
599 /// Any process that has registered to wait for the installation of the interface
600 /// is notified.
601 ///
602 /// The caller is responsible for ensuring that there are no references to `old`
603 /// if it is being removed.
604 pub fn reinstallProtocolInterface(
605 self: *BootServices,
606 handle: Handle,
607 Protocol: type,
608 old: ?*const Protocol,
609 new: ?*const Protocol,
610 ) ReinstallProtocolInterfaceError!void {
611 if (!@hasDecl(Protocol, "guid"))
612 @compileError("protocol is missing guid");
613
614 switch (self._reinstallProtocolInterface(
615 handle,
616 &Protocol.guid,
617 old,
618 new,
619 )) {
620 .success => {},
621 .not_found => return error.NotFound,
622 .access_denied => return error.AccessDenied,
623 .invalid_parameter => return error.InvalidParameter,
624 else => |status| return uefi.unexpectedStatus(status),
625 }
626 }
627
628 /// See `uninstallProtocolInterfaces`.
629 ///
630 /// Does not call `self._uninstallProtocolInterface`, because
631 /// `self._uninstallMultipleProtocolInterfaces` performs more error checks.
632 pub fn uninstallProtocolInterface(
633 self: *BootServices,
634 handle: Handle,
635 interface: anytype,
636 ) UninstallProtocolInterfacesError!void {
637 return self.uninstallProtocolInterfaces(handle, .{
638 interface,
639 });
640 }
641
642 /// Returns a pointer to the `Protocol` interface if it's supported by the
643 /// handle.
644 ///
645 /// Note that UEFI implementations are no longer required to implement this
646 /// function, so it's implemented using `openProtocol` instead.
647 pub fn handleProtocol(
648 self: *BootServices,
649 Protocol: type,
650 handle: Handle,
651 ) HandleProtocolError!?*Protocol {
652 // per https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#efi-boot-services-handleprotocol
653 // handleProtocol is basically `openProtocol` where:
654 // 1. agent_handle is `uefi.handle` (aka handle passed to `EfiMain`)
655 // 2. controller_handle is `null`
656 // 3. attributes is `EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL`
657
658 return self.openProtocol(
659 Protocol,
660 handle,
661 .{ .by_handle_protocol = .{ .agent = uefi.handle } },
662 ) catch |err| switch (err) {
663 error.AlreadyStarted => return uefi.unexpectedStatus(.already_started),
664 error.AccessDenied => return uefi.unexpectedStatus(.access_denied),
665 error.InvalidParameter => return uefi.unexpectedStatus(.invalid_parameter),
666 else => return @errorCast(err),
667 };
668 }
669
670 pub fn registerProtocolNotify(
671 self: *BootServices,
672 Protocol: type,
673 event: Event,
674 ) RegisterProtocolNotifyError!EventRegistration {
675 if (!@hasDecl(Protocol, "guid"))
676 @compileError("Protocol is missing guid");
677
678 var registration: EventRegistration = undefined;
679 switch (self._registerProtocolNotify(
680 &Protocol.guid,
681 event,
682 &registration,
683 )) {
684 .success => return registration,
685 .out_of_resources => return error.OutOfResources,
686 .invalid_parameter => return error.InvalidParameter,
687 else => |status| return uefi.unexpectedStatus(status),
688 }
689 }
690
691 /// Returns the number of handles that match the given search criteria.
692 pub fn locateHandleLen(self: *const BootServices, search: LocateSearch) NumHandlesError!usize {
693 var len: usize = 0;
694 switch (self._locateHandle(
695 std.meta.activeTag(search),
696 if (search == .by_protocol) search.by_protocol else null,
697 if (search == .by_register_notify) search.by_register_notify else null,
698 &len,
699 null,
700 )) {
701 // If len is zero, it should return not_found, otherwise buffer_too_small.
702 // This is because it can/should only return success when a valid buffer is
703 // passed with a non zero size, which is not the case.
704 // Thus this status is considered unreachable and will return error.Unexpected
705 // .success => unreachable,
706 .buffer_too_small => return @divExact(len, @sizeOf(uefi.Handle)),
707 .not_found => return 0,
708 // This function accounts for all possible causes of this error code
709 // as per the most recent UEFI spec 2.10A, therefore this branch is
710 // considered unreachable and will return error.Unexpected instead
711 // .invalid_parameter => unreachable
712 else => |status| return uefi.unexpectedStatus(status),
713 }
714 }
715
716 /// To determine the necessary size of `buffer`, call `locateHandleLen` first.
717 pub fn locateHandle(
718 self: *BootServices,
719 search: LocateSearch,
720 buffer: []Handle,
721 ) LocateHandleError![]Handle {
722 var len: usize = @sizeOf(Handle) * buffer.len;
723 switch (self._locateHandle(
724 std.meta.activeTag(search),
725 if (search == .by_protocol) search.by_protocol else null,
726 if (search == .by_register_notify) search.by_register_notify else null,
727 &len,
728 buffer.ptr,
729 )) {
730 .success => return buffer[0..@divExact(len, @sizeOf(Handle))],
731 .not_found => return buffer[0..0],
732 .buffer_too_small => return error.BufferTooSmall,
733 .invalid_parameter => return error.InvalidParameter,
734 else => |status| return uefi.unexpectedStatus(status),
735 }
736 }
737
738 /// Locates all devices on `device_path` that support `Protocol`. Once the closest
739 /// match to `device_path` is found, it returns the unmatched device path and handle.
740 pub fn locateDevicePath(
741 self: *const BootServices,
742 device_path: *const DevicePathProtocol,
743 Protocol: type,
744 ) LocateHandleError!?struct { *const DevicePathProtocol, Handle } {
745 if (!@hasDecl(Protocol, "guid"))
746 @compileError("Protocol is missing guid");
747
748 var dev_path = device_path;
749 var device: ?Handle = undefined;
750 switch (self._locateDevicePath(
751 &Protocol.guid,
752 &dev_path,
753 &device,
754 )) {
755 .success => return .{ dev_path, device.? },
756 .not_found => return null,
757 .invalid_parameter => return error.InvalidParameter,
758 else => |status| return uefi.unexpectedStatus(status),
759 }
760 }
761
762 pub fn installConfigurationTable(
763 self: *BootServices,
764 guid: *const Guid,
765 table: *anyopaque,
766 ) InstallConfigurationTableError!void {
767 switch (self._installConfigurationTable(
768 guid,
769 table,
770 )) {
771 .success => {},
772 .invalid_parameter => return error.InvalidParameter,
773 .out_of_resources => return error.OutOfResources,
774 else => |status| return uefi.unexpectedStatus(status),
775 }
776 }
777
778 pub fn uninstallConfigurationTable(
779 self: *BootServices,
780 guid: *const Guid,
781 ) UninstallConfigurationTableError!void {
782 switch (self._installConfigurationTable(
783 guid,
784 null,
785 )) {
786 .success => {},
787 .not_found => return error.NotFound,
788 .invalid_parameter => return error.InvalidParameter,
789 .out_of_resources => return error.OutOfResources,
790 else => |status| return uefi.unexpectedStatus(status),
791 }
792 }
793
794 pub const LoadImageSource = union(enum) {
795 buffer: []const u8,
796 device_path: *const DevicePathProtocol,
797 };
798
799 pub fn loadImage(
800 self: *BootServices,
801 boot_policy: bool,
802 parent_image: Handle,
803 source: LoadImageSource,
804 ) LoadImageError!Handle {
805 var handle: Handle = undefined;
806
807 switch (self._loadImage(
808 boot_policy,
809 parent_image,
810 if (source == .device_path) source.device_path else null,
811 if (source == .buffer) source.buffer.ptr else null,
812 if (source == .buffer) source.buffer.len else 0,
813 &handle,
814 )) {
815 .success => return handle,
816 .not_found => return error.NotFound,
817 .invalid_parameter => return error.InvalidParameter,
818 .unsupported => return error.Unsupported,
819 .out_of_resources => return error.OutOfResources,
820 .load_error => return error.LoadError,
821 .device_error => return error.DeviceError,
822 .access_denied => return error.AccessDenied,
823 .security_violation => return error.SecurityViolation,
824 else => |status| return uefi.unexpectedStatus(status),
825 }
826 }
827
828 pub fn startImage(self: *BootServices, image: Handle) StartImageError!ImageExitData {
829 var exit_data_size: usize = undefined;
830 var exit_data: [*]u16 = undefined;
831
832 const exit_code = switch (self._startImage(
833 image,
834 &exit_data_size,
835 &exit_data,
836 )) {
837 .invalid_parameter => return error.InvalidParameter,
838 .security_violation => return error.SecurityViolation,
839 else => |exit_code| exit_code,
840 };
841
842 if (exit_data_size == 0) return .{
843 .code = exit_code,
844 .description = null,
845 .data = null,
846 };
847
848 const description_ptr: [*:0]const u16 = @ptrCast(exit_data);
849 const description = std.mem.sliceTo(description_ptr, 0);
850
851 return ImageExitData{
852 .code = exit_code,
853 .description = description,
854 .data = exit_data[description.len + 1 .. exit_data_size],
855 };
856 }
857
858 /// `message` must be allocated using `allocatePool`.
859 pub fn exit(
860 self: *BootServices,
861 handle: Handle,
862 status: Status,
863 message: ?[:0]const u16,
864 ) ExitError!void {
865 switch (self._exit(
866 handle,
867 status,
868 if (message) |msg| (2 * msg.len) + 1 else 0,
869 if (message) |msg| @ptrCast(msg.ptr) else null,
870 )) {
871 .success => {},
872 .invalid_parameter => return error.InvalidParameter,
873 else => |exit_status| return uefi.unexpectedStatus(exit_status),
874 }
875 }
876
877 /// `message` should be a null-terminated u16 string followed by binary data
878 /// allocated using `allocatePool`.
879 pub fn exitWithData(
880 self: *BootServices,
881 handle: Handle,
882 status: Status,
883 data: []align(2) const u8,
884 ) ExitError!void {
885 switch (self._exit(handle, status, data.len, data.ptr)) {
886 .success => {},
887 .invalid_parameter => return error.InvalidParameter,
888 else => |exit_status| return uefi.unexpectedStatus(exit_status),
889 }
890 }
891
892 /// The result is the exit code of the unload handler. Any error codes are
893 /// `try/catch`-able, leaving only success and warning codes as the result.
894 pub fn unloadImage(
895 self: *BootServices,
896 image: Handle,
897 ) Status.Error!Status {
898 const status = self._unloadImage(image);
899 try status.err();
900 return status;
901 }
902
903 pub fn exitBootServices(
904 self: *BootServices,
905 image: Handle,
906 map_key: MemoryMapKey,
907 ) ExitBootServicesError!void {
908 switch (self._exitBootServices(image, map_key)) {
909 .success => {},
910 .invalid_parameter => return error.InvalidParameter,
911 else => |status| return uefi.unexpectedStatus(status),
912 }
913 }
914
915 pub fn getNextMonotonicCount(
916 self: *const BootServices,
917 count: *u64,
918 ) GetNextMonotonicCountError!void {
919 switch (self._getNextMonotonicCount(count)) {
920 .success => {},
921 .device_error => return error.DeviceError,
922 .invalid_parameter => return error.InvalidParameter,
923 else => |status| return uefi.unexpectedStatus(status),
924 }
925 }
926
927 pub fn stall(self: *const BootServices, microseconds: usize) uefi.UnexpectedError!void {
928 switch (self._stall(microseconds)) {
929 .success => {},
930 else => |status| return uefi.unexpectedStatus(status),
931 }
932 }
933
934 pub fn setWatchdogTimer(
935 self: *BootServices,
936 timeout: usize,
937 watchdog_code: u64,
938 data: ?[]const u16,
939 ) SetWatchdogTimerError!void {
940 switch (self._setWatchdogTimer(
941 timeout,
942 watchdog_code,
943 if (data) |d| d.len else 0,
944 if (data) |d| d.ptr else null,
945 )) {
946 .success => {},
947 .invalid_parameter => return error.InvalidParameter,
948 .unsupported => return error.Unsupported,
949 .device_error => return error.DeviceError,
950 else => |status| return uefi.unexpectedStatus(status),
951 }
952 }
953
954 /// `driver_image` should be a null-terminated ordered list of handles.
955 pub fn connectController(
956 self: *BootServices,
957 controller: Handle,
958 driver_image: ?[*:null]?Handle,
959 remaining_device_path: ?*const DevicePathProtocol,
960 recursive: bool,
961 ) ConnectControllerError!void {
962 switch (self._connectController(
963 controller,
964 driver_image,
965 remaining_device_path,
966 recursive,
967 )) {
968 .success => {},
969 .invalid_parameter => return error.InvalidParameter,
970 .not_found => return error.NotFound,
971 .security_violation => return error.SecurityViolation,
972 else => |status| return uefi.unexpectedStatus(status),
973 }
974 }
975
976 pub fn disconnectController(
977 self: *BootServices,
978 controller: Handle,
979 driver_image: ?Handle,
980 child: ?Handle,
981 ) DisconnectControllerError!void {
982 switch (self._disconnectController(
983 controller,
984 driver_image,
985 child,
986 )) {
987 .success => {},
988 .invalid_parameter => return error.InvalidParameter,
989 .out_of_resources => return error.OutOfResources,
990 .device_error => return error.DeviceError,
991 else => |status| return uefi.unexpectedStatus(status),
992 }
993 }
994
995 /// Opens a protocol with a structure as the loaded image for a UEFI application
996 ///
997 /// If `flag` is `.test_protocol`, then the only valid return value is `null`,
998 /// and `Status.unsupported` is returned. Otherwise, if `_openProtocol` returns
999 /// `Status.unsupported`, then `null` is returned.
1000 pub fn openProtocol(
1001 self: *BootServices,
1002 Protocol: type,
1003 handle: Handle,
1004 attributes: OpenProtocolArgs,
1005 ) OpenProtocolError!?*Protocol {
1006 if (!@hasDecl(Protocol, "guid"))
1007 @compileError("Protocol is missing guid: " ++ @typeName(Protocol));
1008
1009 const agent_handle: ?Handle, const controller_handle: ?Handle = switch (attributes) {
1010 inline else => |arg| .{ arg.agent, arg.controller },
1011 };
1012
1013 var ptr: ?*Protocol = undefined;
1014
1015 switch (self._openProtocol(
1016 handle,
1017 &Protocol.guid,
1018 @as(*?*anyopaque, @ptrCast(&ptr)),
1019 agent_handle,
1020 controller_handle,
1021 std.meta.activeTag(attributes),
1022 )) {
1023 .success => return if (attributes == .test_protocol) null else ptr,
1024 .unsupported => return if (attributes == .test_protocol) error.Unsupported else null,
1025 .access_denied => return error.AccessDenied,
1026 .already_started => return error.AlreadyStarted,
1027 else => |status| return uefi.unexpectedStatus(status),
1028 }
1029 }
1030
1031 pub fn closeProtocol(
1032 self: *BootServices,
1033 handle: Handle,
1034 Protocol: type,
1035 agent: Handle,
1036 controller: ?Handle,
1037 ) CloseProtocolError!void {
1038 if (!@hasDecl(Protocol, "guid"))
1039 @compileError("protocol is missing guid: " ++ @typeName(Protocol));
1040
1041 switch (self._closeProtocol(
1042 handle,
1043 &Protocol.guid,
1044 agent,
1045 controller,
1046 )) {
1047 .success => {},
1048 .invalid_parameter => return error.InvalidParameter,
1049 .not_found => return error.NotFound,
1050 else => |status| return uefi.unexpectedStatus(status),
1051 }
1052 }
1053
1054 pub fn openProtocolInformation(
1055 self: *const BootServices,
1056 handle: Handle,
1057 Protocol: type,
1058 ) OpenProtocolInformationError!?[]ProtocolInformationEntry {
1059 var entries: [*]ProtocolInformationEntry = undefined;
1060 var len: usize = undefined;
1061
1062 switch (self._openProtocolInformation(
1063 handle,
1064 &Protocol.guid,
1065 &entries,
1066 &len,
1067 )) {
1068 .success => return entries[0..len],
1069 .not_found => return null,
1070 .out_of_resources => return error.OutOfResources,
1071 else => |status| return uefi.unexpectedStatus(status),
1072 }
1073 }
1074
1075 pub fn protocolsPerHandle(
1076 self: *const BootServices,
1077 handle: Handle,
1078 ) ProtocolsPerHandleError![]*const Guid {
1079 var guids: [*]*const Guid = undefined;
1080 var len: usize = undefined;
1081
1082 switch (self._protocolsPerHandle(
1083 handle,
1084 &guids,
1085 &len,
1086 )) {
1087 .success => return guids[0..len],
1088 .invalid_parameter => return error.InvalidParameter,
1089 .out_of_resources => return error.OutOfResources,
1090 else => |status| return uefi.unexpectedStatus(status),
1091 }
1092 }
1093
1094 pub fn locateHandleBuffer(
1095 self: *const BootServices,
1096 search: LocateSearch,
1097 ) LocateHandleBufferError!?[]Handle {
1098 var handles: [*]Handle = undefined;
1099 var len: usize = undefined;
1100
1101 switch (self._locateHandleBuffer(
1102 std.meta.activeTag(search),
1103 if (search == .by_protocol) search.by_protocol else null,
1104 if (search == .by_register_notify) search.by_register_notify else null,
1105 &len,
1106 &handles,
1107 )) {
1108 .success => return handles[0..len],
1109 .invalid_parameter => return error.InvalidParameter,
1110 .not_found => return null,
1111 .out_of_resources => return error.OutOfResources,
1112 else => |status| return uefi.unexpectedStatus(status),
1113 }
1114 }
1115
1116 pub fn locateProtocol(
1117 self: *const BootServices,
1118 Protocol: type,
1119 registration: ?EventRegistration,
1120 ) LocateProtocolError!?*Protocol {
1121 var interface: *Protocol = undefined;
1122
1123 switch (self._locateProtocol(
1124 &Protocol.guid,
1125 registration,
1126 @ptrCast(&interface),
1127 )) {
1128 .success => return interface,
1129 .not_found => return null,
1130 .invalid_parameter => return error.InvalidParameter,
1131 else => |status| return uefi.unexpectedStatus(status),
1132 }
1133 }
1134
1135 /// Installs a set of protocol interfaces into the boot services environment.
1136 ///
1137 /// This function's final argument should be a tuple of pointers to protocol
1138 /// interfaces. For example:
1139 ///
1140 /// ```
1141 /// const handle = try boot_services.installProtocolInterfaces(null, .{
1142 /// &my_interface_1,
1143 /// &my_interface_2,
1144 /// });
1145 /// ```
1146 ///
1147 /// The underlying function accepts a vararg list of pairs of Guid pointers
1148 /// and opaque pointers to the interface. To provide a guid, the interface
1149 /// types should declare a `guid` constant like so:
1150 ///
1151 /// ```
1152 /// pub const guid: uefi.Guid = .{ ... };
1153 /// ```
1154 ///
1155 /// See `std.os.uefi.protocol` for examples of protocol type definitions.
1156 pub fn installProtocolInterfaces(
1157 self: *BootServices,
1158 handle: ?Handle,
1159 interfaces: anytype,
1160 ) InstallProtocolInterfacesError!Handle {
1161 var hdl: ?Handle = handle;
1162 const args_tuple = protocolInterfaces(&hdl, interfaces);
1163
1164 switch (@call(
1165 .auto,
1166 self._installMultipleProtocolInterfaces,
1167 args_tuple,
1168 )) {
1169 .success => return hdl.?,
1170 .already_started => return error.AlreadyStarted,
1171 .out_of_resources => return error.OutOfResources,
1172 .invalid_parameter => return error.InvalidParameter,
1173 else => |status| return uefi.unexpectedStatus(status),
1174 }
1175 }
1176
1177 pub fn uninstallProtocolInterfaces(
1178 self: *BootServices,
1179 handle: Handle,
1180 interfaces: anytype,
1181 ) UninstallProtocolInterfacesError!void {
1182 const args_tuple = protocolInterfaces(handle, interfaces);
1183
1184 switch (@call(
1185 .auto,
1186 self._uninstallMultipleProtocolInterfaces,
1187 args_tuple,
1188 )) {
1189 .success => {},
1190 .invalid_parameter => return error.InvalidParameter,
1191 else => |status| return uefi.unexpectedStatus(status),
1192 }
1193 }
1194
1195 pub fn calculateCrc32(
1196 self: *const BootServices,
1197 data: []const u8,
1198 ) CalculateCrc32Error!u32 {
1199 var value: u32 = undefined;
1200 switch (self._calculateCrc32(data.ptr, data.len, &value)) {
1201 .success => return value,
1202 .invalid_parameter => return error.InvalidParameter,
1203 else => |status| return uefi.unexpectedStatus(status),
1204 }
1205 }
1206
1207 pub const signature: u64 = 0x56524553544f4f42;
1208
1209 pub const NotifyOpts = struct {
1210 tpl: TaskPriorityLevel = .application,
1211 function: ?*const fn (Event, ?*anyopaque) callconv(cc) void = null,
1212 context: ?*anyopaque = null,
1213 };
1214
1215 pub const TaskPriorityLevel = enum(usize) {
1216 application = 4,
1217 callback = 8,
1218 notify = 16,
1219 high_level = 31,
1220 _,
1221 };
1222
1223 pub const ImageExitData = struct {
1224 code: Status,
1225 description: ?[:0]const u16,
1226 data: ?[]const u16,
1227 };
1228};
1229
1230fn protocolInterfaces(
1231 handle_arg: anytype,
1232 interfaces: anytype,
1233) ProtocolInterfaces(@TypeOf(handle_arg), @TypeOf(interfaces)) {
1234 var result: ProtocolInterfaces(
1235 @TypeOf(handle_arg),
1236 @TypeOf(interfaces),
1237 ) = undefined;
1238 result[0] = handle_arg;
1239 result[result.len - 1] = null;
1240
1241 comptime var idx: usize = 1;
1242 inline for (interfaces) |interface| {
1243 const InterfacePtr = @TypeOf(interface);
1244 const Interface = switch (@typeInfo(InterfacePtr)) {
1245 .pointer => |pointer| pointer.child,
1246 else => @compileError("expected tuple of '*const Protocol', got " ++ @typeName(InterfacePtr)),
1247 };
1248
1249 if (!@hasDecl(Interface, "guid"))
1250 @compileError("protocol interface '" ++ @typeName(Interface) ++
1251 "' does not declare a 'const guid: uefi.Guid'.");
1252
1253 switch (@typeInfo(Interface)) {
1254 .@"struct" => |struct_info| if (struct_info.layout != .@"extern")
1255 @compileLog("protocol interface '" ++ @typeName(Interface) ++
1256 "' is not extern - this is likely a mistake"),
1257 else => @compileError("protocol interface must be a struct, got " ++ @typeName(Interface)),
1258 }
1259
1260 result[idx] = &Interface.guid;
1261 result[idx + 1] = @ptrCast(interface);
1262 idx += 2;
1263 }
1264
1265 return result;
1266}
1267
1268fn ProtocolInterfaces(HandleType: type, Interfaces: type) type {
1269 const interfaces_type_info = @typeInfo(Interfaces);
1270 if (interfaces_type_info != .@"struct" or !interfaces_type_info.@"struct".is_tuple)
1271 @compileError("expected tuple of protocol interfaces, got " ++ @typeName(Interfaces));
1272 const interfaces_info = interfaces_type_info.@"struct";
1273
1274 var tuple_types: [interfaces_info.field_names.len * 2 + 2]type = undefined;
1275 tuple_types[0] = HandleType;
1276 tuple_types[tuple_types.len - 1] = ?*const Guid;
1277
1278 var idx = 1;
1279 while (idx < tuple_types.len - 1) : (idx += 2) {
1280 tuple_types[idx] = *const Guid;
1281 tuple_types[idx + 1] = *const anyopaque;
1282 }
1283
1284 return @Tuple(tuple_types[0..]);
1285}