1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #include "debug_support.h"
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <lib/cksum.h>
23 #include <uefi/boot_service.h>
24 #include <uefi/types.h>
25
26 #include "boot_service_provider.h"
27 #include "memory_protocols.h"
28 #include "uefi_platform.h"
29
30 struct EFI_DEVICE_PATH_FILE_PATH_PROTOCOL {
31 struct EFI_DEVICE_PATH_PROTOCOL dp;
32 uint16_t str[];
33 };
34
35 static constexpr size_t EFI_DEVICE_PATH_TYPE_END = 0x7f;
36 static constexpr size_t EFI_DEVICE_PATH_SUB_TYPE_END = 0xff;
37 static constexpr size_t EFI_DEVICE_PATH_TYPE_MEDIA_DEVICE = 0x4;
38 static constexpr size_t EFI_DEVICE_PATH_SUB_TYPE_FILE_PATH = 0x4;
39
40 namespace {
41 struct EfiSystemTablePointer *efi_systab_pointer = nullptr;
42 }
43
44 struct EfiDebugImageInfoTableHeader efi_m_debug_info_table_header = {
45 0,
46 0,
47 nullptr
48 };
49
efi_initialize_system_table_pointer(struct EfiSystemTable * system_table)50 EfiStatus efi_initialize_system_table_pointer(struct EfiSystemTable *system_table) {
51 uint32_t crc = 0;
52 constexpr auto EFI_SYSTEM_TABLE_SIGNATURE =
53 static_cast<u64>(0x5453595320494249ULL);
54
55 /* Allocate efi_system_table_pointer structure with 4MB alignment. */
56 efi_systab_pointer = reinterpret_cast<EfiSystemTablePointer *>(alloc_page(sizeof(struct EfiSystemTablePointer), 22));
57
58 if (!efi_systab_pointer) {
59 printf("Installing EFI system table pointer failed\n");
60 return OUT_OF_RESOURCES;
61 }
62
63 memset(efi_systab_pointer, 0, sizeof(struct EfiSystemTablePointer));
64
65 efi_systab_pointer->signature = EFI_SYSTEM_TABLE_SIGNATURE;
66 efi_systab_pointer->system_table_base = system_table;
67
68 crc = crc32(crc,
69 (uint8_t *)efi_systab_pointer,
70 sizeof(struct EfiSystemTablePointer));
71
72 efi_systab_pointer->crc32 = crc;
73
74 return SUCCESS;
75 }
76
77 static uint32_t efi_m_max_table_entries;
78
79 static constexpr size_t EFI_DEBUG_TABLE_ENTRY_SIZE = (sizeof(union EfiDebugImageInfo));
80
efi_core_new_debug_image_info_entry(uint32_t image_info_type,struct EFI_LOADED_IMAGE_PROTOCOL * loaded_image,EfiHandle image_handle)81 EfiStatus efi_core_new_debug_image_info_entry(uint32_t image_info_type,
82 struct EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
83 EfiHandle image_handle) {
84 /* Set the flag indicating that we're in the process of updating
85 * the table.
86 */
87 efi_m_debug_info_table_header.update_status |=
88 EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
89
90 union EfiDebugImageInfo *table;
91 table = efi_m_debug_info_table_header.efi_debug_image_info_table;
92
93 if (efi_m_debug_info_table_header.table_size >= efi_m_max_table_entries) {
94 /* table is full, re-allocate the buffer increasing the size
95 * by 4 KiB.
96 */
97 uint32_t table_size = efi_m_max_table_entries * EFI_DEBUG_TABLE_ENTRY_SIZE;
98 union EfiDebugImageInfo *new_table;
99 allocate_pool(BOOT_SERVICES_DATA,
100 table_size + PAGE_SIZE,
101 reinterpret_cast<void **>(&new_table));
102
103 if (!new_table) {
104 efi_m_debug_info_table_header.update_status &=
105 ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
106 return OUT_OF_RESOURCES;
107 }
108
109 memset(new_table, 0, table_size + PAGE_SIZE);
110
111 /* Copy the old table into the new one. */
112 memcpy(new_table, table, table_size);
113 /* Free the old table. */
114 free_pool(table);
115 /* Update the table header. */
116 table = new_table;
117 efi_m_debug_info_table_header.efi_debug_image_info_table =
118 new_table;
119
120 /* Enlarge the max table entries and set the first empty
121 * entry index to be the original max table entries.
122 */
123 efi_m_max_table_entries +=
124 PAGE_SIZE / EFI_DEBUG_TABLE_ENTRY_SIZE;
125 }
126
127 /* We always put the next entry at the end of the currently consumed
128 * table (i.e. first free entry)
129 */
130 uint32_t index = efi_m_debug_info_table_header.table_size;
131
132 /* Allocate data for new entry. */
133 allocate_pool(BOOT_SERVICES_DATA,
134 sizeof(union EfiDebugImageInfo),
135 reinterpret_cast<void **>(&table[index].normal_image));
136 if (table[index].normal_image) {
137 /* Update the entry. */
138 table[index].normal_image->image_info_type = image_info_type;
139 table[index].normal_image->loaded_image_protocol_instance =
140 loaded_image;
141 table[index].normal_image->image_handle = image_handle;
142
143 /* Increase the number of EFI_DEBUG_IMAGE_INFO elements and
144 * set the efi_m_debug_info_table_header in modified status.
145 */
146 efi_m_debug_info_table_header.table_size++;
147 efi_m_debug_info_table_header.update_status |=
148 EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED;
149 } else {
150 return OUT_OF_RESOURCES;
151 }
152
153 efi_m_debug_info_table_header.update_status &=
154 ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
155
156 return SUCCESS;
157 }
158
efi_core_remove_debug_image_info_entry(EfiHandle image_handle)159 void efi_core_remove_debug_image_info_entry(EfiHandle image_handle)
160 {
161 efi_m_debug_info_table_header.update_status |=
162 EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
163
164 union EfiDebugImageInfo *table;
165 table = efi_m_debug_info_table_header.efi_debug_image_info_table;
166
167 for (uint32_t index = 0; index < efi_m_max_table_entries; index++) {
168 if (table[index].normal_image &&
169 table[index].normal_image->image_handle == image_handle) {
170 /* Found a match. Free up the table entry.
171 * Move the tail of the table one slot to the front.
172 */
173 free_pool(table[index].normal_image);
174
175 memmove(&table[index],
176 &table[index + 1],
177 (efi_m_debug_info_table_header.table_size -
178 index - 1) * EFI_DEBUG_TABLE_ENTRY_SIZE);
179
180 /* Decrease the number of EFI_DEBUG_IMAGE_INFO
181 * elements and set the efi_m_debug_info_table_header
182 * in modified status.
183 */
184 efi_m_debug_info_table_header.table_size--;
185 table[efi_m_debug_info_table_header.table_size].normal_image =
186 nullptr;
187 efi_m_debug_info_table_header.update_status |=
188 EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED;
189 break;
190 }
191 }
192
193 efi_m_debug_info_table_header.update_status &=
194 ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
195 }
196
setup_debug_support(EfiSystemTable & table,char * image_base,size_t virtual_size,bdev_t * dev)197 EfiStatus setup_debug_support(EfiSystemTable &table,
198 char *image_base,
199 size_t virtual_size,
200 bdev_t *dev) {
201 struct EFI_LOADED_IMAGE_PROTOCOL *efiLoadedImageProtocol = nullptr;
202
203 allocate_pool(BOOT_SERVICES_DATA,
204 sizeof(struct EFI_LOADED_IMAGE_PROTOCOL),
205 reinterpret_cast<void **>(&efiLoadedImageProtocol));
206 memset(efiLoadedImageProtocol, 0, sizeof(struct EFI_LOADED_IMAGE_PROTOCOL));
207
208 efiLoadedImageProtocol->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
209 efiLoadedImageProtocol->SystemTable = &table;
210 efiLoadedImageProtocol->ImageBase = reinterpret_cast<void *>(image_base);
211 efiLoadedImageProtocol->ImageSize = virtual_size;
212 char *device_buf = nullptr;
213 size_t fpsize = sizeof(struct EFI_DEVICE_PATH_PROTOCOL) + 2 * (strlen(dev->name) + 2);
214 allocate_pool(BOOT_SERVICES_DATA,
215 fpsize + sizeof(struct EFI_DEVICE_PATH_PROTOCOL),
216 reinterpret_cast<void **>(&device_buf));
217 memset(device_buf, 0, fpsize + sizeof(struct EFI_DEVICE_PATH_PROTOCOL));
218 struct EFI_DEVICE_PATH_FILE_PATH_PROTOCOL *fp = reinterpret_cast<struct EFI_DEVICE_PATH_FILE_PATH_PROTOCOL *>(device_buf);
219 struct EFI_DEVICE_PATH_PROTOCOL *dp_end = reinterpret_cast<struct EFI_DEVICE_PATH_PROTOCOL *>(device_buf + fpsize);
220 fp->dp.Type = EFI_DEVICE_PATH_TYPE_MEDIA_DEVICE;
221 fp->dp.SubType = EFI_DEVICE_PATH_SUB_TYPE_FILE_PATH;
222 fp->dp.Length[0] = fpsize % 256;
223 fp->dp.Length[1] = fpsize / 256;
224 fp->str[0] = '\\';
225 for (size_t i = 0; i < strlen(dev->name); i++) {
226 fp->str[i+1] = dev->name[i];
227 }
228 dp_end->Type = EFI_DEVICE_PATH_TYPE_END;
229 dp_end->SubType = EFI_DEVICE_PATH_SUB_TYPE_END;
230 dp_end->Length[0] = sizeof(struct EFI_DEVICE_PATH_PROTOCOL) % 256;
231 dp_end->Length[1] = sizeof(struct EFI_DEVICE_PATH_PROTOCOL) / 256;
232 efiLoadedImageProtocol->FilePath = reinterpret_cast<struct EFI_DEVICE_PATH_PROTOCOL *>(device_buf);
233 return efi_core_new_debug_image_info_entry(EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL,
234 efiLoadedImageProtocol,
235 image_base);
236 }
237
teardown_debug_support(char * image_base)238 void teardown_debug_support(char *image_base) {
239 union EfiDebugImageInfo *table = efi_m_debug_info_table_header.efi_debug_image_info_table;
240
241 for (uint32_t index = 0; index < efi_m_debug_info_table_header.table_size; index++) {
242 if (table[index].normal_image &&
243 table[index].normal_image->image_handle == image_base) {
244 /* Found a match. Get device_buf and efiLoadedImageProtocol. */
245 struct EFI_LOADED_IMAGE_PROTOCOL *efiLoadedImageProtocol = table[index].normal_image->loaded_image_protocol_instance;
246 char *device_buf = reinterpret_cast<char *>(efiLoadedImageProtocol->FilePath);
247 /* Free resources */
248 efi_core_remove_debug_image_info_entry(image_base);
249 free_pool(device_buf);
250 free_pool(efiLoadedImageProtocol);
251
252 return;
253 }
254 }
255 }
256