Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/efi/loader/arch/amd64/trap.c
34914 views
1
/*-
2
* Copyright (c) 2016 The FreeBSD Foundation
3
*
4
* This software was developed by Konstantin Belousov under sponsorship
5
* from the FreeBSD Foundation.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <stand.h>
30
#include <string.h>
31
#include <sys/param.h>
32
#include <machine/cpufunc.h>
33
#include <machine/psl.h>
34
#include <machine/segments.h>
35
#include <machine/frame.h>
36
#include <machine/tss.h>
37
38
#include <efi.h>
39
#include <efilib.h>
40
41
#include "bootstrap.h"
42
#include "loader_efi.h"
43
44
#define NUM_IST 8
45
#define NUM_EXC 32
46
47
/*
48
* This code catches exceptions but forwards hardware interrupts to
49
* handlers installed by firmware. It differentiates exceptions
50
* vs. interrupts by presence of the error code on the stack, which
51
* causes different stack pointer value on trap handler entry.
52
*
53
* Use kernel layout for the trapframe just to not be original.
54
*
55
* Use free IST slot in existing TSS, or create our own TSS if
56
* firmware did not configured any, to have stack switched to
57
* IST-specified one, e.g. to handle #SS. If hand-off cannot find
58
* unused IST slot, or create a new descriptor in GDT, we bail out.
59
*/
60
61
static struct region_descriptor fw_idt; /* Descriptor for pristine fw IDT */
62
static struct region_descriptor loader_idt;/* Descriptor for loader
63
shadow IDT */
64
static EFI_PHYSICAL_ADDRESS lidt_pa; /* Address of loader shadow IDT */
65
static EFI_PHYSICAL_ADDRESS tss_pa; /* Address of TSS */
66
static EFI_PHYSICAL_ADDRESS exc_stack_pa;/* Address of IST stack for loader */
67
EFI_PHYSICAL_ADDRESS exc_rsp; /* %rsp value on our IST stack when
68
exception happens */
69
EFI_PHYSICAL_ADDRESS fw_intr_handlers[NUM_EXC]; /* fw handlers for < 32 IDT
70
vectors */
71
static int intercepted[NUM_EXC];
72
static int ist; /* IST for exception handlers */
73
static uint32_t tss_fw_seg; /* Fw TSS segment */
74
static uint32_t loader_tss; /* Loader TSS segment */
75
static struct region_descriptor fw_gdt; /* Descriptor of pristine GDT */
76
static EFI_PHYSICAL_ADDRESS loader_gdt_pa; /* Address of loader shadow GDT */
77
78
struct frame {
79
struct frame *fr_savfp;
80
uintptr_t fr_savpc;
81
};
82
83
void report_exc(struct trapframe *tf);
84
void
85
report_exc(struct trapframe *tf)
86
{
87
struct frame *fp;
88
uintptr_t pc, base;
89
char buf[80];
90
int ret;
91
92
base = (uintptr_t)boot_img->ImageBase;
93
/*
94
* printf() depends on loader runtime and UEFI firmware health
95
* to produce the console output, in case of exception, the
96
* loader or firmware runtime may fail to support the printf().
97
*/
98
printf("===================================================="
99
"============================\n");
100
printf("Exception %u\n", tf->tf_trapno);
101
printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
102
"gs 0x%04hx\n",
103
(uint16_t)tf->tf_ss, (uint16_t)tf->tf_cs, (uint16_t)tf->tf_ds,
104
(uint16_t)tf->tf_es, (uint16_t)tf->tf_fs, (uint16_t)tf->tf_gs);
105
printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
106
"rsp 0x%016lx rip 0x%016lx\n",
107
(uint32_t)tf->tf_err, (uint32_t)tf->tf_rflags, tf->tf_addr,
108
tf->tf_rsp, tf->tf_rip);
109
printf(
110
"rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
111
"rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"
112
"rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
113
"r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
114
"r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
115
tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_rcx, tf->tf_r8,
116
tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
117
tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
118
119
fp = (struct frame *)tf->tf_rbp;
120
pc = tf->tf_rip;
121
122
printf("Stack trace:\n");
123
pager_open();
124
while (fp != NULL || pc != 0) {
125
char *source = "PC";
126
127
if (pc >= base && pc < base + boot_img->ImageSize) {
128
pc -= base;
129
source = "loader PC";
130
}
131
(void) snprintf(buf, sizeof (buf), "FP %016lx: %s 0x%016lx\n",
132
(uintptr_t)fp, source, pc);
133
if (pager_output(buf))
134
break;
135
136
if (fp != NULL)
137
fp = fp->fr_savfp;
138
139
if (fp != NULL)
140
pc = fp->fr_savpc;
141
else
142
pc = 0;
143
}
144
pager_close();
145
printf("Machine stopped.\n");
146
}
147
148
static void
149
prepare_exception(unsigned idx, uint64_t my_handler,
150
int ist_use_table[static NUM_IST])
151
{
152
struct gate_descriptor *fw_idt_e, *loader_idt_e;
153
154
fw_idt_e = &((struct gate_descriptor *)fw_idt.rd_base)[idx];
155
loader_idt_e = &((struct gate_descriptor *)loader_idt.rd_base)[idx];
156
fw_intr_handlers[idx] = fw_idt_e->gd_looffset +
157
(fw_idt_e->gd_hioffset << 16);
158
intercepted[idx] = 1;
159
ist_use_table[fw_idt_e->gd_ist]++;
160
loader_idt_e->gd_looffset = my_handler;
161
loader_idt_e->gd_hioffset = my_handler >> 16;
162
/*
163
* We reuse uefi selector for the code segment for the exception
164
* handler code, while the reason for the fault might be the
165
* corruption of that gdt entry. On the other hand, allocating
166
* our own descriptor might be not much better, if gdt is corrupted.
167
*/
168
loader_idt_e->gd_selector = fw_idt_e->gd_selector;
169
loader_idt_e->gd_ist = 0;
170
loader_idt_e->gd_type = SDT_SYSIGT;
171
loader_idt_e->gd_dpl = 0;
172
loader_idt_e->gd_p = 1;
173
loader_idt_e->gd_xx = 0;
174
loader_idt_e->sd_xx1 = 0;
175
}
176
#define PREPARE_EXCEPTION(N) \
177
extern char EXC##N##_handler[]; \
178
prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
179
180
static void
181
free_tables(void)
182
{
183
184
if (lidt_pa != 0) {
185
BS->FreePages(lidt_pa, EFI_SIZE_TO_PAGES(fw_idt.rd_limit));
186
lidt_pa = 0;
187
}
188
if (exc_stack_pa != 0) {
189
BS->FreePages(exc_stack_pa, 1);
190
exc_stack_pa = 0;
191
}
192
if (tss_pa != 0 && tss_fw_seg == 0) {
193
BS->FreePages(tss_pa, EFI_SIZE_TO_PAGES(sizeof(struct
194
amd64tss)));
195
tss_pa = 0;
196
}
197
if (loader_gdt_pa != 0) {
198
BS->FreePages(tss_pa, 2);
199
loader_gdt_pa = 0;
200
}
201
ist = 0;
202
loader_tss = 0;
203
}
204
205
static int
206
efi_setup_tss(struct region_descriptor *gdt, uint32_t loader_tss_idx,
207
struct amd64tss **tss)
208
{
209
EFI_STATUS status;
210
struct system_segment_descriptor *tss_desc;
211
212
tss_desc = (struct system_segment_descriptor *)(gdt->rd_base +
213
(loader_tss_idx << 3));
214
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
215
EFI_SIZE_TO_PAGES(sizeof(struct amd64tss)), &tss_pa);
216
if (EFI_ERROR(status)) {
217
printf("efi_setup_tss: AllocatePages tss error %lu\n",
218
EFI_ERROR_CODE(status));
219
return (0);
220
}
221
*tss = (struct amd64tss *)tss_pa;
222
bzero(*tss, sizeof(**tss));
223
tss_desc->sd_lolimit = sizeof(struct amd64tss);
224
tss_desc->sd_lobase = tss_pa;
225
tss_desc->sd_type = SDT_SYSTSS;
226
tss_desc->sd_dpl = 0;
227
tss_desc->sd_p = 1;
228
tss_desc->sd_hilimit = sizeof(struct amd64tss) >> 16;
229
tss_desc->sd_gran = 0;
230
tss_desc->sd_hibase = tss_pa >> 24;
231
tss_desc->sd_xx0 = 0;
232
tss_desc->sd_xx1 = 0;
233
tss_desc->sd_mbz = 0;
234
tss_desc->sd_xx2 = 0;
235
return (1);
236
}
237
238
static int
239
efi_redirect_exceptions(void)
240
{
241
int ist_use_table[NUM_IST];
242
struct gate_descriptor *loader_idt_e;
243
struct system_segment_descriptor *tss_desc, *gdt_desc;
244
struct amd64tss *tss;
245
struct region_descriptor *gdt_rd, loader_gdt;
246
uint32_t i;
247
EFI_STATUS status;
248
register_t rfl;
249
250
sidt(&fw_idt);
251
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
252
EFI_SIZE_TO_PAGES(fw_idt.rd_limit), &lidt_pa);
253
if (EFI_ERROR(status)) {
254
printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
255
EFI_ERROR_CODE(status));
256
lidt_pa = 0;
257
return (0);
258
}
259
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 1,
260
&exc_stack_pa);
261
if (EFI_ERROR(status)) {
262
printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
263
EFI_ERROR_CODE(status));
264
exc_stack_pa = 0;
265
free_tables();
266
return (0);
267
}
268
loader_idt.rd_limit = fw_idt.rd_limit;
269
bcopy((void *)fw_idt.rd_base, (void *)loader_idt.rd_base,
270
loader_idt.rd_limit);
271
bzero(ist_use_table, sizeof(ist_use_table));
272
bzero(fw_intr_handlers, sizeof(fw_intr_handlers));
273
bzero(intercepted, sizeof(intercepted));
274
275
sgdt(&fw_gdt);
276
tss_fw_seg = read_tr();
277
gdt_rd = NULL;
278
if (tss_fw_seg == 0) {
279
for (i = 2; (i << 3) + sizeof(*gdt_desc) <= fw_gdt.rd_limit;
280
i += 2) {
281
gdt_desc = (struct system_segment_descriptor *)(
282
fw_gdt.rd_base + (i << 3));
283
if (gdt_desc->sd_type == 0 && gdt_desc->sd_mbz == 0) {
284
gdt_rd = &fw_gdt;
285
break;
286
}
287
}
288
if (gdt_rd == NULL) {
289
if (i >= 8190) {
290
printf("efi_redirect_exceptions: all slots "
291
"in gdt are used\n");
292
free_tables();
293
return (0);
294
}
295
loader_gdt.rd_limit = roundup2(fw_gdt.rd_limit +
296
sizeof(struct system_segment_descriptor),
297
sizeof(struct system_segment_descriptor)) - 1;
298
i = (loader_gdt.rd_limit + 1 -
299
sizeof(struct system_segment_descriptor)) /
300
sizeof(struct system_segment_descriptor) * 2;
301
status = BS->AllocatePages(AllocateAnyPages,
302
EfiLoaderData,
303
EFI_SIZE_TO_PAGES(loader_gdt.rd_limit),
304
&loader_gdt_pa);
305
if (EFI_ERROR(status)) {
306
printf("efi_setup_tss: AllocatePages gdt error "
307
"%lu\n", EFI_ERROR_CODE(status));
308
loader_gdt_pa = 0;
309
free_tables();
310
return (0);
311
}
312
loader_gdt.rd_base = loader_gdt_pa;
313
bzero((void *)loader_gdt.rd_base, loader_gdt.rd_limit);
314
bcopy((void *)fw_gdt.rd_base,
315
(void *)loader_gdt.rd_base, fw_gdt.rd_limit);
316
gdt_rd = &loader_gdt;
317
}
318
loader_tss = i << 3;
319
if (!efi_setup_tss(gdt_rd, i, &tss)) {
320
tss_pa = 0;
321
free_tables();
322
return (0);
323
}
324
} else {
325
tss_desc = (struct system_segment_descriptor *)((char *)
326
fw_gdt.rd_base + tss_fw_seg);
327
if (tss_desc->sd_type != SDT_SYSTSS &&
328
tss_desc->sd_type != SDT_SYSBSY) {
329
printf("LTR points to non-TSS descriptor\n");
330
free_tables();
331
return (0);
332
}
333
tss_pa = tss_desc->sd_lobase + (tss_desc->sd_hibase << 16);
334
tss = (struct amd64tss *)tss_pa;
335
tss_desc->sd_type = SDT_SYSTSS; /* unbusy */
336
}
337
338
PREPARE_EXCEPTION(0);
339
PREPARE_EXCEPTION(1);
340
PREPARE_EXCEPTION(2);
341
PREPARE_EXCEPTION(3);
342
PREPARE_EXCEPTION(4);
343
PREPARE_EXCEPTION(5);
344
PREPARE_EXCEPTION(6);
345
PREPARE_EXCEPTION(7);
346
PREPARE_EXCEPTION(8);
347
PREPARE_EXCEPTION(9);
348
PREPARE_EXCEPTION(10);
349
PREPARE_EXCEPTION(11);
350
PREPARE_EXCEPTION(12);
351
PREPARE_EXCEPTION(13);
352
PREPARE_EXCEPTION(14);
353
PREPARE_EXCEPTION(16);
354
PREPARE_EXCEPTION(17);
355
PREPARE_EXCEPTION(18);
356
PREPARE_EXCEPTION(19);
357
PREPARE_EXCEPTION(20);
358
359
exc_rsp = exc_stack_pa + EFI_PAGE_SIZE -
360
(6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
361
362
/* Find free IST and use it */
363
for (ist = 1; ist < NUM_IST; ist++) {
364
if (ist_use_table[ist] == 0)
365
break;
366
}
367
if (ist == NUM_IST) {
368
printf("efi_redirect_exceptions: all ISTs used\n");
369
free_tables();
370
lidt_pa = 0;
371
return (0);
372
}
373
for (i = 0; i < NUM_EXC; i++) {
374
loader_idt_e = &((struct gate_descriptor *)loader_idt.
375
rd_base)[i];
376
if (intercepted[i])
377
loader_idt_e->gd_ist = ist;
378
}
379
(&(tss->tss_ist1))[ist - 1] = exc_stack_pa + EFI_PAGE_SIZE;
380
381
/* Switch to new IDT */
382
rfl = intr_disable();
383
if (loader_gdt_pa != 0)
384
bare_lgdt(&loader_gdt);
385
if (loader_tss != 0)
386
ltr(loader_tss);
387
lidt(&loader_idt);
388
intr_restore(rfl);
389
return (1);
390
}
391
392
static void
393
efi_unredirect_exceptions(void)
394
{
395
register_t rfl;
396
397
if (lidt_pa == 0)
398
return;
399
400
rfl = intr_disable();
401
if (ist != 0)
402
(&(((struct amd64tss *)tss_pa)->tss_ist1))[ist - 1] = 0;
403
if (loader_gdt_pa != 0)
404
bare_lgdt(&fw_gdt);
405
if (loader_tss != 0)
406
ltr(tss_fw_seg);
407
lidt(&fw_idt);
408
intr_restore(rfl);
409
free_tables();
410
}
411
412
static int
413
command_grab_faults(int argc, char *argv[])
414
{
415
int res;
416
417
res = efi_redirect_exceptions();
418
if (!res)
419
printf("failed\n");
420
return (CMD_OK);
421
}
422
COMMAND_SET(grap_faults, "grab_faults", "grab faults", command_grab_faults);
423
424
static int
425
command_ungrab_faults(int argc, char *argv[])
426
{
427
428
efi_unredirect_exceptions();
429
return (CMD_OK);
430
}
431
COMMAND_SET(ungrab_faults, "ungrab_faults", "ungrab faults",
432
command_ungrab_faults);
433
434
static int
435
command_fault(int argc, char *argv[])
436
{
437
438
__asm("ud2");
439
return (CMD_OK);
440
}
441
COMMAND_SET(fault, "fault", "generate fault", command_fault);
442
443