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            &registration,
 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}