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