PatchworkOS
Loading...
Searching...
No Matches
kernel.c
Go to the documentation of this file.
1#include "kernel.h"
2
3#include <boot/boot_info.h>
4#include <stdint.h>
5#include <sys/elf.h>
6#include <sys/math.h>
7#include <sys/proc.h>
8
9static BOOLEAN is_valid_phdr(const elf_phdr_t* phdr, uint64_t fileSize)
10{
11 if (phdr == NULL)
12 {
13 return FALSE;
14 }
15
16 if (phdr->offset > fileSize || phdr->fileSize > fileSize || phdr->offset + phdr->fileSize > fileSize)
17 {
18 return FALSE;
19 }
20
21 return TRUE;
22}
23
24static BOOLEAN is_valid_shdr(const elf_shdr_t* shdr, uint64_t fileSize)
25{
26 if (shdr == NULL)
27 {
28 return FALSE;
29 }
30
31 if (shdr->type != ELF_SHDR_TYPE_NOBITS)
32 {
33 if (shdr->offset > fileSize || shdr->size > fileSize || shdr->offset + shdr->size > fileSize)
34 {
35 return FALSE;
36 }
37 }
38
39 return TRUE;
40}
41
42static EFI_STATUS determine_kernel_bounds(const elf_phdr_t* phdrs, const elf_hdr_t* header, uint64_t phdrTableSize,
43 uintptr_t* virtStart, uintptr_t* virtEnd)
44{
45 if (phdrs == NULL || header == NULL || virtStart == NULL || virtEnd == NULL)
46 {
47 return EFI_INVALID_PARAMETER;
48 }
49
50 *virtStart = UINT64_MAX;
51 *virtEnd = 0;
52 BOOLEAN foundLoadable = FALSE;
53
54 for (uint32_t i = 0; i < header->phdrAmount; i++)
55 {
56 const elf_phdr_t* phdr = (const elf_phdr_t*)((uint64_t)phdrs + (i * header->phdrSize));
57
58 if ((uint64_t)phdr + sizeof(elf_phdr_t) > (uint64_t)phdrs + phdrTableSize)
59 {
60 return EFI_INVALID_PARAMETER;
61 }
62
63 if (phdr->type == ELF_PHDR_TYPE_LOAD)
64 {
65 foundLoadable = TRUE;
66 *virtStart = MIN(*virtStart, phdr->virtAddr);
67 *virtEnd = MAX(*virtEnd, phdr->virtAddr + phdr->memorySize);
68 }
69 }
70
71 if (!foundLoadable)
72 {
73 return EFI_NOT_FOUND;
74 }
75
76 return EFI_SUCCESS;
77}
78
79static EFI_STATUS load_section_headers(EFI_FILE* file, boot_kernel_t* kernel, uint64_t fileSize)
80{
81 if (file == NULL || kernel == NULL)
82 {
83 return EFI_INVALID_PARAMETER;
84 }
85
86 if (kernel->header.shdrAmount == 0 || kernel->header.shdrOffset == 0)
87 {
88 kernel->shdrs = NULL;
89 kernel->shdrCount = 0;
90 return EFI_SUCCESS;
91 }
92
93 uint64_t shdrTableSize = (uint64_t)kernel->header.shdrAmount * kernel->header.shdrSize;
94 if (shdrTableSize > fileSize || kernel->header.shdrOffset > fileSize ||
95 kernel->header.shdrOffset + shdrTableSize > fileSize)
96 {
97 Print(L"section header table extends beyond file bounds!\n");
98 return EFI_INVALID_PARAMETER;
99 }
100
101 kernel->shdrs = AllocatePool(shdrTableSize);
102 if (kernel->shdrs == NULL)
103 {
104 Print(L"failed to allocate %llu bytes for section headers!\n", shdrTableSize);
105 return EFI_OUT_OF_RESOURCES;
106 }
107
108 EFI_STATUS status = uefi_call_wrapper(file->SetPosition, 2, file, kernel->header.shdrOffset);
109 if (EFI_ERROR(status))
110 {
111 Print(L"failed to seek to section header table (0x%x)!\n", status);
112 return status;
113 }
114
115 status = uefi_call_wrapper(file->Read, 3, file, &shdrTableSize, kernel->shdrs);
116 if (EFI_ERROR(status))
117 {
118 Print(L"failed to read section header table (0x%x)!\n", status);
119 return status;
120 }
121
122 kernel->shdrCount = kernel->header.shdrAmount;
123 return EFI_SUCCESS;
124}
125
127{
128 if (kernel == NULL || kernel->shdrs == NULL)
129 {
130 return NULL;
131 }
132
133 for (uint32_t i = 0; i < kernel->shdrCount; i++)
134 {
135 elf_shdr_t* shdr = (elf_shdr_t*)((uint64_t)kernel->shdrs + (i * kernel->header.shdrSize));
136 if (shdr->type == sectionType)
137 {
138 return shdr;
139 }
140 }
141
142 return NULL;
143}
144
145static EFI_STATUS load_symbol_table(EFI_FILE* file, boot_kernel_t* kernel, uint64_t fileSize)
146{
147 if (file == NULL || kernel == NULL)
148 {
149 return EFI_INVALID_PARAMETER;
150 }
151
152 elf_shdr_t* symtabSection = find_section_by_type(kernel, ELF_SHDR_TYPE_SYMTAB);
153 if (symtabSection == NULL)
154 {
155 symtabSection = find_section_by_type(kernel, ELF_SHDR_TYPE_DYNSYM);
156 if (symtabSection == NULL)
157 {
158 kernel->symbols = NULL;
159 kernel->symbolCount = 0;
160 kernel->stringTable = NULL;
161 kernel->stringTableSize = 0;
162 return EFI_SUCCESS;
163 }
164 }
165
166 if (!is_valid_shdr(symtabSection, fileSize))
167 {
168 Print(L"invalid symbol table section!\n");
169 return EFI_INVALID_PARAMETER;
170 }
171
172 if (symtabSection->entrySize == 0 || symtabSection->entrySize < sizeof(elf_sym_t))
173 {
174 Print(L"invalid symbol table entry size (%llu)!\n", symtabSection->entrySize);
175 return EFI_INVALID_PARAMETER;
176 }
177
178 uint32_t symbolCount = symtabSection->size / symtabSection->entrySize;
179 if (symbolCount == 0)
180 {
181 kernel->symbols = NULL;
182 kernel->symbolCount = 0;
183 kernel->stringTable = NULL;
184 kernel->stringTableSize = 0;
185 return EFI_SUCCESS;
186 }
187
188 kernel->symbols = AllocatePool(symtabSection->size);
189 if (kernel->symbols == NULL)
190 {
191 Print(L"failed to allocate %llu bytes for symbol table!\n", symtabSection->size);
192 return EFI_OUT_OF_RESOURCES;
193 }
194
195 EFI_STATUS status = uefi_call_wrapper(file->SetPosition, 2, file, symtabSection->offset);
196 if (EFI_ERROR(status))
197 {
198 Print(L"failed to seek to symbol table (0x%x)!\n", status);
199 return status;
200 }
201
202 status = uefi_call_wrapper(file->Read, 3, file, &symtabSection->size, kernel->symbols);
203 if (EFI_ERROR(status))
204 {
205 Print(L"failed to read symbol table (0x%x)!\n", status);
206 return status;
207 }
208
209 kernel->symbolCount = symbolCount;
210
211 if (symtabSection->link < kernel->shdrCount)
212 {
213 elf_shdr_t* strtabSection =
214 (elf_shdr_t*)((uint64_t)kernel->shdrs + (symtabSection->link * kernel->header.shdrSize));
215
216 if (strtabSection->type == ELF_SHDR_TYPE_STRTAB && is_valid_shdr(strtabSection, fileSize))
217 {
218 kernel->stringTable = AllocatePool(strtabSection->size);
219 if (kernel->stringTable == NULL)
220 {
221 Print(L"failed to allocate %llu bytes for string table!\n", strtabSection->size);
222 return EFI_OUT_OF_RESOURCES;
223 }
224
225 status = uefi_call_wrapper(file->SetPosition, 2, file, strtabSection->offset);
226 if (EFI_ERROR(status))
227 {
228 Print(L"failed to seek to string table (0x%x)!\n", status);
229 return status;
230 }
231
232 status = uefi_call_wrapper(file->Read, 3, file, &strtabSection->size, kernel->stringTable);
233 if (EFI_ERROR(status))
234 {
235 Print(L"failed to read string table (0x%x)!\n", status);
236 return status;
237 }
238
239 kernel->stringTableSize = strtabSection->size;
240 }
241 }
242
243 if (kernel->stringTable == NULL)
244 {
245 kernel->stringTableSize = 0;
246 }
247
248 return EFI_SUCCESS;
249}
250
251static EFI_STATUS load_kernel_segments(EFI_FILE* file, uintptr_t physStart, uintptr_t virtStart,
252 uint64_t kernelPageAmount, const elf_phdr_t* phdrs, const elf_hdr_t* header, uint64_t fileSize)
253{
254 if (file == NULL || phdrs == NULL || header == NULL)
255 {
256 return EFI_INVALID_PARAMETER;
257 }
258
259 for (uint32_t i = 0; i < header->phdrAmount; i++)
260 {
261 const elf_phdr_t* phdr = (const elf_phdr_t*)((uint64_t)phdrs + (i * header->phdrSize));
262
263 if (phdr->type == ELF_PHDR_TYPE_LOAD)
264 {
265 if (!is_valid_phdr(phdr, fileSize))
266 {
267 return EFI_INVALID_PARAMETER;
268 }
269
270 EFI_STATUS status = uefi_call_wrapper(file->SetPosition, 2, file, phdr->offset);
271 if (EFI_ERROR(status))
272 {
273 return status;
274 }
275
276 uintptr_t dest = physStart + (phdr->virtAddr - virtStart);
277 if (dest < physStart || dest + phdr->memorySize > physStart + kernelPageAmount * PAGE_SIZE)
278 {
279 return EFI_INVALID_PARAMETER;
280 }
281
282 SetMem((VOID*)dest, phdr->memorySize, 0);
283
284 if (phdr->fileSize > 0)
285 {
286 status = uefi_call_wrapper(file->Read, 3, file, &phdr->fileSize, (void*)dest);
287 if (EFI_ERROR(status))
288 {
289 return status;
290 }
291 }
292 }
293 }
294
295 return EFI_SUCCESS;
296}
297
298EFI_STATUS kernel_load(boot_kernel_t* kernel, EFI_FILE* rootHandle)
299{
300 if (kernel == NULL)
301 {
302 return EFI_INVALID_PARAMETER;
303 }
304
305 SetMem(kernel, sizeof(boot_kernel_t), 0);
306
307 Print(L"Loading kernel... ");
308
309 EFI_FILE* kernelDir = NULL;
310 EFI_FILE* file = NULL;
311 EFI_STATUS status = EFI_SUCCESS;
312 uint64_t kernelPageAmount = 0;
313
314 status = uefi_call_wrapper(rootHandle->Open, 5, rootHandle, &kernelDir, L"kernel", EFI_FILE_MODE_READ, 0);
315 if (EFI_ERROR(status))
316 {
317 Print(L"failed to open boot directory (0x%x)!\n", status);
318 goto cleanup;
319 }
320
321 status = uefi_call_wrapper(kernelDir->Open, 5, kernelDir, &file, L"kernel", EFI_FILE_MODE_READ, 0);
322 if (EFI_ERROR(status))
323 {
324 Print(L"failed to open kernel file (0x%x)!\n", status);
325 goto cleanup;
326 }
327
328 EFI_FILE_INFO* fileInfo = LibFileInfo(file);
329 if (fileInfo == NULL)
330 {
331 Print(L"failed to get kernel file info (0x%x)!\n", status);
332 status = EFI_LOAD_ERROR;
333 goto cleanup;
334 }
335 uint64_t fileSize = fileInfo->FileSize;
336 FreePool(fileInfo);
337
338 if (fileSize < sizeof(elf_hdr_t))
339 {
340 Print(L"kernel file too small (%llu bytes)!\n", fileSize);
341 status = EFI_INVALID_PARAMETER;
342 goto cleanup;
343 }
344
345 uint64_t elfHdrSize = sizeof(elf_hdr_t);
346 status = uefi_call_wrapper(file->Read, 3, file, &elfHdrSize, &kernel->header);
347 if (EFI_ERROR(status))
348 {
349 Print(L"failed to read ELF header (0x%x)!\n", status);
350 goto cleanup;
351 }
352
353 if (!ELF_IS_VALID(&kernel->header))
354 {
355 Print(L"invalid ELF header in kernel file!\n");
356 status = EFI_INVALID_PARAMETER;
357 goto cleanup;
358 }
359
360 if (kernel->header.phdrAmount == 0)
361 {
362 Print(L"no program headers in kernel ELF!\n");
363 status = EFI_INVALID_PARAMETER;
364 goto cleanup;
365 }
366
367 if (kernel->header.phdrSize < sizeof(elf_phdr_t))
368 {
369 Print(L"invalid program header size (%u)!\n", kernel->header.phdrSize);
370 status = EFI_INVALID_PARAMETER;
371 goto cleanup;
372 }
373
374 uint64_t phdrTableSize = (uint64_t)kernel->header.phdrAmount * kernel->header.phdrSize;
375 if (phdrTableSize > fileSize || kernel->header.phdrOffset > fileSize ||
376 kernel->header.phdrOffset + phdrTableSize > fileSize)
377 {
378 Print(L"program header table extends beyond file bounds!\n");
379 status = EFI_INVALID_PARAMETER;
380 goto cleanup;
381 }
382
383 kernel->phdrs = AllocatePool(phdrTableSize);
384 if (kernel->phdrs == NULL)
385 {
386 Print(L"failed to allocate %llu bytes for program headers!\n", phdrTableSize);
387 status = EFI_OUT_OF_RESOURCES;
388 goto cleanup;
389 }
390
391 status = uefi_call_wrapper(file->SetPosition, 2, file, kernel->header.phdrOffset);
392 if (EFI_ERROR(status))
393 {
394 Print(L"failed to seek to program header table (0x%x)!\n", status);
395 goto cleanup;
396 }
397
398 status = uefi_call_wrapper(file->Read, 3, file, &phdrTableSize, kernel->phdrs);
399 if (EFI_ERROR(status))
400 {
401 Print(L"failed to read program header table (0x%x)!\n", status);
402 goto cleanup;
403 }
404
405 Print(L"sections... ");
406 status = load_section_headers(file, kernel, fileSize);
407 if (EFI_ERROR(status))
408 {
409 Print(L"failed to load section headers (0x%x)!\n", status);
410 goto cleanup;
411 }
412
413 Print(L"symbols... ");
414 status = load_symbol_table(file, kernel, fileSize);
415 if (EFI_ERROR(status))
416 {
417 Print(L"failed to load symbol table (0x%x)!\n", status);
418 goto cleanup;
419 }
420
421 uintptr_t virtStart = 0;
422 uintptr_t virtEnd = 0;
423 status = determine_kernel_bounds(kernel->phdrs, &kernel->header, phdrTableSize, &virtStart, &virtEnd);
424 if (EFI_ERROR(status))
425 {
426 Print(L"failed to determine kernel bounds (0x%x)!\n", status);
427 goto cleanup;
428 }
429
430 uint64_t kernelSize = virtEnd - virtStart;
431 kernelPageAmount = BYTES_TO_PAGES(kernelSize);
432
433 uintptr_t physStart;
434 status =
435 uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiReservedMemoryType, kernelPageAmount, &physStart);
436 if (EFI_ERROR(status))
437 {
438 Print(L"failed to allocate %llu pages for kernel (0x%x)!\n", kernelPageAmount, status);
439 goto cleanup;
440 }
441
442 kernel->virtStart = virtStart;
443 kernel->physStart = physStart;
444 kernel->entry = (void*)kernel->header.entry;
445 kernel->size = kernelPageAmount * EFI_PAGE_SIZE;
446
447 Print(L"phys=0x%llx virt=0x%llx size=%llu KB... ", kernel->physStart, kernel->virtStart, kernel->size / 1024);
448
449 status = load_kernel_segments(file, (uintptr_t)kernel->physStart, kernel->virtStart, kernelPageAmount,
450 kernel->phdrs, &kernel->header, fileSize);
451 if (EFI_ERROR(status))
452 {
453 Print(L"failed to load kernel segments (0x%x)!\n", status);
454 goto cleanup;
455 }
456
457 if (kernel->symbols != NULL && kernel->symbolCount > 0)
458 {
459 Print(L"loaded %u symbols... ", kernel->symbolCount);
460 }
461
462 Print(L"done!\n");
463 status = EFI_SUCCESS;
464
465cleanup:
466 if (status != EFI_SUCCESS)
467 {
468 if (kernel->phdrs != NULL)
469 {
470 FreePool(kernel->phdrs);
471 kernel->phdrs = NULL;
472 }
473 if (kernel->shdrs != NULL)
474 {
475 FreePool(kernel->shdrs);
476 kernel->shdrs = NULL;
477 }
478 if (kernel->symbols != NULL)
479 {
480 FreePool(kernel->symbols);
481 kernel->symbols = NULL;
482 }
483 if (kernel->stringTable != NULL)
484 {
485 FreePool(kernel->stringTable);
486 kernel->stringTable = NULL;
487 }
488 }
489
490 if (kernel->physStart != 0 && EFI_ERROR(status))
491 {
492 uefi_call_wrapper(BS->FreePages, 3, kernel->physStart, kernelPageAmount);
493 kernel->physStart = 0;
494 }
495 if (file != NULL)
496 {
497 uefi_call_wrapper(file->Close, 1, file);
498 }
499 if (kernelDir != NULL)
500 {
501 uefi_call_wrapper(kernelDir->Close, 1, kernelDir);
502 }
503
504 return status;
505}
#define ELF_SHDR_TYPE_DYNSYM
Definition elf.h:144
#define ELF_SHDR_TYPE_STRTAB
Definition elf.h:136
#define ELF_IS_VALID(hdr)
Checks the validity of an ELF header.
Definition elf.h:263
#define ELF_SHDR_TYPE_NOBITS
Definition elf.h:141
#define ELF_SHDR_TYPE_SYMTAB
Definition elf.h:135
uint32_t elf_shdr_type_t
Definition elf.h:132
#define ELF_PHDR_TYPE_LOAD
Definition elf.h:108
#define MIN(x, y)
Definition math.h:16
#define MAX(x, y)
Definition math.h:15
#define PAGE_SIZE
Memory page size.
Definition proc.h:140
#define BYTES_TO_PAGES(amount)
Convert bytes to pages.
Definition proc.h:151
#define NULL
Pointer error value.
Definition NULL.h:23
static EFI_STATUS determine_kernel_bounds(const elf_phdr_t *phdrs, const elf_hdr_t *header, uint64_t phdrTableSize, uintptr_t *virtStart, uintptr_t *virtEnd)
Definition kernel.c:42
static EFI_STATUS load_section_headers(EFI_FILE *file, boot_kernel_t *kernel, uint64_t fileSize)
Definition kernel.c:79
static elf_shdr_t * find_section_by_type(boot_kernel_t *kernel, elf_shdr_type_t sectionType)
Definition kernel.c:126
static EFI_STATUS load_symbol_table(EFI_FILE *file, boot_kernel_t *kernel, uint64_t fileSize)
Definition kernel.c:145
static BOOLEAN is_valid_shdr(const elf_shdr_t *shdr, uint64_t fileSize)
Definition kernel.c:24
static BOOLEAN is_valid_phdr(const elf_phdr_t *phdr, uint64_t fileSize)
Definition kernel.c:9
static EFI_STATUS load_kernel_segments(EFI_FILE *file, uintptr_t physStart, uintptr_t virtStart, uint64_t kernelPageAmount, const elf_phdr_t *phdrs, const elf_hdr_t *header, uint64_t fileSize)
Definition kernel.c:251
EFI_STATUS kernel_load(boot_kernel_t *kernel, EFI_FILE *rootHandle)
Definition kernel.c:298
static dentry_t * file
Definition log_file.c:17
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
#define UINT64_MAX
Definition stdint.h:74
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
elf_sym_t * symbols
Definition boot_info.h:84
uintptr_t virtStart
Definition boot_info.h:89
elf_phdr_t * phdrs
Definition boot_info.h:81
char * stringTable
Definition boot_info.h:86
uint64_t stringTableSize
Definition boot_info.h:87
elf_hdr_t header
Definition boot_info.h:80
uint32_t symbolCount
Definition boot_info.h:85
uintptr_t physStart
Definition boot_info.h:88
elf_shdr_t * shdrs
Definition boot_info.h:82
uint64_t size
Definition boot_info.h:91
uint32_t shdrCount
Definition boot_info.h:83
void(* entry)(boot_info_t *)
Definition boot_info.h:90
ELF file header.
Definition elf.h:276
uint16_t shdrSize
Definition elf.h:288
uint16_t shdrAmount
Definition elf.h:289
uint64_t shdrOffset
Definition elf.h:283
uint16_t phdrAmount
Definition elf.h:287
uint64_t entry
Definition elf.h:281
uint64_t phdrOffset
Definition elf.h:282
uint16_t phdrSize
Definition elf.h:286
ELF program header.
Definition elf.h:299
uint64_t fileSize
Definition elf.h:305
uint64_t offset
Definition elf.h:302
uint64_t memorySize
Definition elf.h:306
uint64_t virtAddr
Definition elf.h:303
elf_phdr_type_t type
Definition elf.h:300
ELF section header.
Definition elf.h:316
uint64_t size
Definition elf.h:322
uint64_t offset
Definition elf.h:321
uint32_t link
Definition elf.h:323
elf_shdr_type_t type
Definition elf.h:318
uint64_t entrySize
Definition elf.h:326
ELF symbol table entry.
Definition elf.h:335