forked from yabits/ebcvm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
efi.c
229 lines (197 loc) · 7.77 KB
/
efi.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#include "ebcvm.h"
#include "efi.h"
#define ConOut_OutputString_MAGIC 0xdbec42e5063aa942
#define ConIn_Reset_MAGIC 0x59928d645b6e0de4
#define ConIn_ReadKeyStroke_MAGIC 0xa1b4e93c32d682d0
#define BS_AllocatePool_MAGIC 0xd61390cb796062bb
#define BS_WaitForEvent_MAGIC 0x5b9857eeaa9427f2
static size_t calc_offset(size_t size) {
return size + size % ARCH_BYTES;
}
static void bs_wait_for_event(vm *_vm) {
uint64_t stack_top = _vm->regs->regs[R0];
uint64_t ret_addr = read_mem64(_vm->mem, stack_top);
/* number_of_events = stack_top + 16 */
/* events = stack_top + 24 */
uint64_t index = read_mem64(_vm->mem, stack_top + 32);
write_mem64(_vm->mem, index, 0);
// TODO: do this
_vm->regs->regs[R7] = EFI_SUCCESS;
/* XXX: MOVqq R0, R0 (+2, 0) */
_vm->regs->regs[R0] += ARCH_BYTES * 2;
_vm->regs->regs[IP] = ret_addr;
}
static void bs_allocate_pool(vm *_vm) {
uint64_t stack_top = _vm->regs->regs[R0];
uint64_t ret_addr = read_mem64(_vm->mem, stack_top);
/* pool_size = stack_top + 16 */
uint64_t size = read_mem64(_vm->mem, stack_top + 24);
uint64_t buffer = read_mem64(_vm->mem, stack_top + 32);
if (size > FLAGS_heap)
raise_except(MEMORY, "out of memory", __FILE__, __LINE__);
/* FIXME: Add sane memory allocator */
uint64_t heap_addr = 0xffffffffffffffff;
for (int i = 0; i < _vm->memmap_size; i++) {
if (_vm->memmap[i].mem_type == MEM_HEAP)
heap_addr = _vm->memmap[i].addr;
}
if (heap_addr == 0xffffffffffffffff)
raise_except(MEMORY, "heap not found", __FILE__, __LINE__);
write_mem64(_vm->mem, buffer, heap_addr);
_vm->regs->regs[R7] = EFI_SUCCESS;
/* XXX: MOVqq R0, R0 (+2, 0) */
_vm->regs->regs[R0] += ARCH_BYTES * 2;
_vm->regs->regs[IP] = ret_addr;
}
static void conin_reset(vm *_vm) {
uint64_t stack_top = _vm->regs->regs[R0];
uint64_t ret_addr = read_mem64(_vm->mem, stack_top);
// TODO: do this
_vm->regs->regs[R7] = EFI_SUCCESS;
/* XXX: MOVqq R0, R0 (+2, 0) */
_vm->regs->regs[R0] += ARCH_BYTES * 2;
_vm->regs->regs[IP] = ret_addr;
}
static void conin_read_key_stroke(vm *_vm) {
uint64_t stack_top = _vm->regs->regs[R0];
uint64_t ret_addr = read_mem64(_vm->mem, stack_top);
/* this = stack_top + 16 */
uint64_t key = read_mem64(_vm->mem, stack_top + 24);
int c = fgetc(stdin);
if (c == EOF)
c = 0x00;
/* FIXME: encode ASCII to UTF-8 */
uint64_t offset = 0;
write_mem16(_vm->mem, key + offset, (UINT16)0x00);
offset += calc_offset(sizeof(UINT16));
write_mem16(_vm->mem, key + offset, (CHAR16)c);
_vm->regs->regs[R7] = EFI_SUCCESS;
/* XXX: MOVqq R0, R0 (+2, 0) */
_vm->regs->regs[R0] += ARCH_BYTES * 2;
_vm->regs->regs[IP] = ret_addr;
}
/* FIXME: below native code emulation supports only 64-bit machine */
static void conout_output_string(vm *_vm) {
uint64_t stack_top = _vm->regs->regs[R0];
uint64_t ret_addr = read_mem64(_vm->mem, stack_top);
/* this = stack_top + 16 */
uint64_t string = read_mem64(_vm->mem, stack_top + 24);
for (uint64_t p = string; read_mem16(_vm->mem, p) != 0x0000; p += 2) {
char c = (char)(read_mem16(_vm->mem, p) & 0x00ff);
fputc(c, stdout);
}
_vm->regs->regs[R7] = EFI_SUCCESS;
/* XXX: MOVqq R0, R0 (+2, 0) */
_vm->regs->regs[R0] += ARCH_BYTES * 2;
_vm->regs->regs[IP] = ret_addr;
}
static void set_efi_system_table(uint64_t table, uint64_t addrs[], vm *_vm) {
uint64_t offset = 0;
offset += calc_offset(sizeof(EFI_TABLE_HEADER)); /* Hdr */
write_mem64(_vm->mem, table + offset, addrs[0]);
offset += calc_offset(sizeof(VOID_PTR)); /* FirmwareVendor */
offset += calc_offset(sizeof(UINT32)); /* FirmwareRevision */
offset += calc_offset(sizeof(EFI_HANDLE)); /* ConsoleInHandle */
write_mem64(_vm->mem, table + offset, addrs[1]);
offset += calc_offset(sizeof(VOID_PTR)); /* ConIn */
offset += calc_offset(sizeof(EFI_HANDLE)); /* ConsoleOutHandle */
write_mem64(_vm->mem, table + offset, addrs[2]);
offset += calc_offset(sizeof(VOID_PTR)); /* ConOut */
offset += calc_offset(sizeof(EFI_HANDLE)); /* StandardErrorHandle */
write_mem64(_vm->mem, table + offset, addrs[3]);
offset += calc_offset(sizeof(VOID_PTR)); /* StdErr */
write_mem64(_vm->mem, table + offset, addrs[4]);
offset += calc_offset(sizeof(VOID_PTR)); /* RuntimeServices */
write_mem64(_vm->mem, table + offset, addrs[5]);
offset += calc_offset(sizeof(VOID_PTR)); /* BootServices */
/* FIXME: NumberOfTableEntries */
/* FIXME: ConfigurationTable */
}
static void set_efi_conin(uint64_t conin, vm *_vm) {
uint64_t offset = 0;
write_mem64(_vm->mem, conin + offset, ConIn_Reset_MAGIC);
offset += calc_offset(sizeof(VOID_PTR)); /* Reset */
write_mem64(_vm->mem, conin + offset, ConIn_ReadKeyStroke_MAGIC);
}
static void set_efi_conout(uint64_t conout, vm *_vm) {
uint64_t offset = 0;
offset += calc_offset(sizeof(VOID_PTR)); /* Reset */
write_mem64(_vm->mem, conout + offset, ConOut_OutputString_MAGIC);
}
static void set_efi_bs(uint64_t bs, vm *_vm) {
uint64_t offset = 0;
offset += calc_offset(sizeof(EFI_TABLE_HEADER)); /* Hdr */
offset += calc_offset(sizeof(VOID_PTR)); /* RaiseTPL */
offset += calc_offset(sizeof(VOID_PTR)); /* RestoreTPL */
offset += calc_offset(sizeof(VOID_PTR)); /* AllocatePages */
offset += calc_offset(sizeof(VOID_PTR)); /* FreePages */
offset += calc_offset(sizeof(VOID_PTR)); /* GetMemoryMap */
write_mem64(_vm->mem, bs + offset, BS_AllocatePool_MAGIC);
offset += calc_offset(sizeof(VOID_PTR));
offset += calc_offset(sizeof(VOID_PTR)); /* FreePool */
offset += calc_offset(sizeof(VOID_PTR)); /* CreateEvent */
offset += calc_offset(sizeof(VOID_PTR)); /* SetTimer */
write_mem64(_vm->mem, bs + offset, BS_WaitForEvent_MAGIC);
}
vm *load_efi(uint64_t addr, vm *_vm) {
/* calculate addresses */
uint64_t params_addr = addr;
uint64_t table_addr = params_addr + sizeof(EFI_MAIN_PARAMETERS);
/* FIXME: FirmwareVendor */
uint64_t conin_addr = table_addr + sizeof(EFI_SYSTEM_TABLE);
uint64_t conout_addr = conin_addr + sizeof(EFI_SIMPLE_TEXT_IN_PROTOCOL);
uint64_t stderr_addr = conout_addr + sizeof(EFI_SIMPLE_TEXT_OUT_PROTOCOL);
uint64_t runtime_addr = stderr_addr + sizeof(EFI_SIMPLE_TEXT_OUT_PROTOCOL);
uint64_t boot_addr = runtime_addr + sizeof(EFI_RUNTIME_SERVICES);
/* FIXME: ConfigurationTable */
size_t size = boot_addr + sizeof(EFI_BOOT_SERVICES) - addr;
/* setup pointers */
uint64_t addrs[] = {
0, conin_addr, conout_addr, stderr_addr, runtime_addr, boot_addr, 0,
};
set_efi_system_table(table_addr, addrs, _vm);
set_efi_conin(conin_addr, _vm);
set_efi_conout(conout_addr, _vm);
set_efi_bs(boot_addr, _vm);
_vm->memmap_size += 1;
_vm->memmap = realloc(_vm->memmap, sizeof(memmap) * _vm->memmap_size);
if (!_vm->memmap)
goto fail;
_vm->memmap[_vm->memmap_size - 1].mem_type = MEM_EFI;
_vm->memmap[_vm->memmap_size - 1].addr = addr;
_vm->memmap[_vm->memmap_size - 1].size = size;
/* XXX: PUSH64 SystemTable */
_vm->regs->regs[R0] -= 8;
write_mem64(_vm->mem, _vm->regs->regs[R0], table_addr);
/* XXX: PUSH64 ImageHandle */
_vm->regs->regs[R0] -= 8;
write_mem64(_vm->mem, _vm->regs->regs[R0], 0x0000000000000000);
/* XXX: PUSH64 RET_MAGIC */
_vm->regs->regs[R0] -= 16;
write_mem64(_vm->mem, _vm->regs->regs[R0], RET_MAGIC);
return _vm;
fail:
error("could not load efi");
return NULL;
}
void handle_excall(uint64_t code, vm *_vm) {
switch (code) {
case ConIn_Reset_MAGIC:
conin_reset(_vm);
break;
case ConIn_ReadKeyStroke_MAGIC:
conin_read_key_stroke(_vm);
break;
case ConOut_OutputString_MAGIC:
conout_output_string(_vm);
break;
case BS_AllocatePool_MAGIC:
bs_allocate_pool(_vm);
break;
case BS_WaitForEvent_MAGIC:
bs_wait_for_event(_vm);
break;
default:
raise_except(UNDEF, "invalid excall", __FILE__, __LINE__);
}
}