This repository has been archived by the owner on Nov 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
elf.c
93 lines (87 loc) · 3.19 KB
/
elf.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include "bootloader.h"
struct elf_executable_header {
uint32_t elf_magic;
uint8_t bits;
uint8_t endian;
uint8_t header_ver;
uint8_t abi;
uint8_t unused[8];
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entry;
uint64_t phoff;
uint64_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
} __attribute__((packed));
struct elf_program_header {
uint32_t type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr; // unused
uint64_t filesz;
uint64_t memsz;
uint64_t align;
} __attribute__((packed));
static void elf_error()
{
boot_error("Kernel executable file format invalid");
}
static void read_file_or_error(const char *name, uint32_t offset, uint32_t amount, void *buffer)
{
if (!read_file(name, offset, amount, buffer)) elf_error();
}
void load_kernel(const char *filename)
{
if (!file_exists(filename))
boot_error("Kernel not found");
struct elf_executable_header file_header;
read_file_or_error(filename, 0, sizeof(struct elf_executable_header), &file_header);
if (file_header.elf_magic != 0x464C457F) elf_error();
if (file_header.bits != 2) elf_error(); // need to be 64-bit
if (file_header.machine != 0x3E) elf_error(); // x86-64
for (int i = 0; i < file_header.phnum; i++) {
uint32_t phoffset = file_header.phoff + i * file_header.phentsize;
struct elf_program_header prog_header;
read_file_or_error(filename, phoffset, sizeof(struct elf_program_header), &prog_header);
if (prog_header.type != 1) continue; // load segment
uint64_t begin_page = prog_header.vaddr;
begin_page -= begin_page & 0xFFF;
uint64_t end_page = prog_header.vaddr + prog_header.memsz;
if (end_page & 0xFFF) end_page += 4096 - (end_page & 0xFFF);
for (uint64_t page = begin_page; page < end_page; page += 0x1000) {
char *ptr = map_new_page(page);
if (page >= prog_header.vaddr + prog_header.filesz) continue;
uint64_t file_off, file_size;
if (page < prog_header.vaddr) {
file_off = prog_header.offset;
file_size = prog_header.filesz;
if (file_size > page + 0x1000 - prog_header.vaddr) {
file_size = page + 0x1000 - prog_header.vaddr;
}
read_file_or_error(filename, file_off, file_size, ptr + (prog_header.vaddr - page));
} else {
file_off = prog_header.offset + page - prog_header.vaddr;
file_size = 0x1000;
if (prog_header.vaddr + prog_header.filesz < page + 0x1000) {
file_size = prog_header.vaddr + prog_header.filesz - page;
}
read_file_or_error(filename, file_off, file_size, ptr);
}
}
}
void *boot_info = map_new_page(0xFFFFFFFF81000000ULL - 0x1000ULL);
load_boot_info(boot_info);
/* Map the stack. */
for (uint64_t i = 2; i <= 16; i++) {
map_new_page(0xFFFFFFFF81000000ULL - 0x1000ULL * i);
}
jump_to_64bit(file_header.entry);
}