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