The Pedigree Project 0.1
exit_boot_services.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_UEFI_EXIT_BOOT_SERVICES_H
3#define PEDIGREE_UEFI_EXIT_BOOT_SERVICES_H
4#include <stdint.h>
5
6typedef uintptr_t efi_status_t;
7typedef void* efi_handle_t;
8typedef efi_status_t (*efi_get_memory_map_t)(uintptr_t*, void*, uintptr_t*, uintptr_t*, uint32_t*);
9typedef efi_status_t (*efi_exit_boot_services_t)(efi_handle_t, uintptr_t);
10
11typedef struct efi_memory_descriptor {
12 uint32_t type;
13 uint32_t pad;
14 uint64_t physical_start;
15 uint64_t virtual_start;
16 uint64_t number_of_pages;
17 uint64_t attribute;
19
21 uint32_t size;
22 uint64_t address;
23 uint64_t length;
24 uint32_t type;
26
27_Static_assert(sizeof(bootstrap_memory_map_entry_t) == 32,
28 "bootstrap memory-map entries must match the kernel ABI");
29
30#define EFI_SUCCESS 0
31#define EFI_ERROR_BIT ((uintptr_t)1 << (sizeof(uintptr_t) * 8 - 1))
32#define EFI_LOAD_ERROR (EFI_ERROR_BIT | 1)
33#define EFI_INVALID_PARAMETER (EFI_ERROR_BIT | 2)
34#define EFI_BUFFER_TOO_SMALL (EFI_ERROR_BIT | 5)
35#define EFI_EXIT_BOOT_SERVICES_ATTEMPTS 4
36
37static uint32_t normalized_type(uint32_t type) {
38 if (type == 7)
39 return 1;
40 if (type == 9)
41 return 3;
42 if (type == 10)
43 return 4;
44 return 2;
45}
46
47static efi_status_t exit_boot_services_with_map(
48 efi_get_memory_map_t get_map, efi_exit_boot_services_t exit_services, efi_handle_t image,
49 void* raw_map, uintptr_t raw_capacity, bootstrap_memory_map_entry_t* normalized_map,
50 uintptr_t normalized_capacity, uint32_t* normalized_bytes, int* exit_attempted) {
51 *normalized_bytes = 0;
52 *exit_attempted = 0;
53 for (unsigned attempt = 0; attempt < EFI_EXIT_BOOT_SERVICES_ATTEMPTS; ++attempt) {
54 uintptr_t map_size = raw_capacity;
55 uintptr_t map_key = 0;
56 uintptr_t descriptor_size = 0;
57 uint32_t descriptor_version = 0;
58 efi_status_t status =
59 get_map(&map_size, raw_map, &map_key, &descriptor_size, &descriptor_version);
60 if (status != EFI_SUCCESS)
61 return status;
62 if (!map_size || map_size > raw_capacity || descriptor_version != 1 ||
63 descriptor_size < sizeof(efi_memory_descriptor_t) || descriptor_size % 8 ||
64 map_size % descriptor_size)
65 return EFI_LOAD_ERROR;
66 const uint64_t count = map_size / descriptor_size;
67 if (count > normalized_capacity / sizeof(*normalized_map) ||
68 count > UINT32_MAX / sizeof(*normalized_map))
69 return EFI_LOAD_ERROR;
70 for (uint64_t i = 0; i < count; ++i) {
71 const efi_memory_descriptor_t* source =
72 (const efi_memory_descriptor_t*)((const uint8_t*)raw_map + i * descriptor_size);
73 if ((source->physical_start & 4095U) || source->number_of_pages > UINT64_MAX / 4096U ||
74 source->number_of_pages * 4096U > UINT64_MAX - source->physical_start)
75 return EFI_LOAD_ERROR;
76 bootstrap_memory_map_entry_t* destination = &normalized_map[i];
77 destination->size = sizeof(*destination) - sizeof(destination->size);
78 destination->address = source->physical_start;
79 destination->length = source->number_of_pages * 4096U;
80 destination->type = normalized_type(source->type);
81 }
82 // Firmware may partially shut down even when the first exit returns an error.
83 // The only callbacks reachable hereafter refresh the existing map or retry exit.
84 *exit_attempted = 1;
85 status = exit_services(image, map_key);
86 if (status == EFI_SUCCESS) {
87 *normalized_bytes = (uint32_t)(count * sizeof(*normalized_map));
88 return EFI_SUCCESS;
89 }
90 if (status != EFI_INVALID_PARAMETER)
91 return status;
92 }
93 return EFI_INVALID_PARAMETER;
94}
95#endif