2 * Copyright (c) 2016 The FreeBSD Foundation
5 * This software was developed by Konstantin Belousov under sponsorship
6 * from the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <machine/cpufunc.h>
36 #include <machine/psl.h>
37 #include <machine/segments.h>
38 #include <machine/frame.h>
39 #include <machine/tss.h>
44 #include "bootstrap.h"
45 #include "loader_efi.h"
51 * This code catches exceptions but forwards hardware interrupts to
52 * handlers installed by firmware. It differentiates exceptions
53 * vs. interrupts by presence of the error code on the stack, which
54 * causes different stack pointer value on trap handler entry.
56 * Use kernel layout for the trapframe just to not be original.
58 * Use free IST slot in existing TSS, or create our own TSS if
59 * firmware did not configured any, to have stack switched to
60 * IST-specified one, e.g. to handle #SS. If hand-off cannot find
61 * unused IST slot, or create a new descriptor in GDT, we bail out.
64 static struct region_descriptor fw_idt
; /* Descriptor for pristine fw IDT */
65 static struct region_descriptor loader_idt
;/* Descriptor for loader
67 static EFI_PHYSICAL_ADDRESS lidt_pa
; /* Address of loader shadow IDT */
68 static EFI_PHYSICAL_ADDRESS tss_pa
; /* Address of TSS */
69 static EFI_PHYSICAL_ADDRESS exc_stack_pa
;/* Address of IST stack for loader */
70 EFI_PHYSICAL_ADDRESS exc_rsp
; /* %rsp value on our IST stack when
72 EFI_PHYSICAL_ADDRESS fw_intr_handlers
[NUM_EXC
]; /* fw handlers for < 32 IDT
74 static int intercepted
[NUM_EXC
];
75 static int ist
; /* IST for exception handlers */
76 static uint32_t tss_fw_seg
; /* Fw TSS segment */
77 static uint32_t loader_tss
; /* Loader TSS segment */
78 static struct region_descriptor fw_gdt
; /* Descriptor of pristine GDT */
79 static EFI_PHYSICAL_ADDRESS loader_gdt_pa
; /* Address of loader shadow GDT */
81 void report_exc(struct trapframe
*tf
);
83 report_exc(struct trapframe
*tf
)
87 * printf() depends on loader runtime and UEFI firmware health
88 * to produce the console output, in case of exception, the
89 * loader or firmware runtime may fail to support the printf().
91 printf("===================================================="
92 "============================\n");
93 printf("Exception %u\n", tf
->tf_trapno
);
94 printf("ss 0x%04hx cs 0x%04hx ds 0x%04hx es 0x%04hx fs 0x%04hx "
96 (uint16_t)tf
->tf_ss
, (uint16_t)tf
->tf_cs
, (uint16_t)tf
->tf_ds
,
97 (uint16_t)tf
->tf_es
, (uint16_t)tf
->tf_fs
, (uint16_t)tf
->tf_gs
);
98 printf("err 0x%08x rfl 0x%08x addr 0x%016lx\n"
99 "rsp 0x%016lx rip 0x%016lx\n",
100 (uint32_t)tf
->tf_err
, (uint32_t)tf
->tf_rflags
, tf
->tf_addr
,
101 tf
->tf_rsp
, tf
->tf_rip
);
103 "rdi 0x%016lx rsi 0x%016lx rdx 0x%016lx\n"
104 "rcx 0x%016lx r8 0x%016lx r9 0x%016lx\n"
105 "rax 0x%016lx rbx 0x%016lx rbp 0x%016lx\n"
106 "r10 0x%016lx r11 0x%016lx r12 0x%016lx\n"
107 "r13 0x%016lx r14 0x%016lx r15 0x%016lx\n",
108 tf
->tf_rdi
, tf
->tf_rsi
, tf
->tf_rdx
, tf
->tf_rcx
, tf
->tf_r8
,
109 tf
->tf_r9
, tf
->tf_rax
, tf
->tf_rbx
, tf
->tf_rbp
, tf
->tf_r10
,
110 tf
->tf_r11
, tf
->tf_r12
, tf
->tf_r13
, tf
->tf_r14
, tf
->tf_r15
);
111 printf("Machine stopped.\n");
115 prepare_exception(unsigned idx
, uint64_t my_handler
,
116 int ist_use_table
[static NUM_IST
])
118 struct gate_descriptor
*fw_idt_e
, *loader_idt_e
;
120 fw_idt_e
= &((struct gate_descriptor
*)fw_idt
.rd_base
)[idx
];
121 loader_idt_e
= &((struct gate_descriptor
*)loader_idt
.rd_base
)[idx
];
122 fw_intr_handlers
[idx
] = fw_idt_e
->gd_looffset
+
123 (fw_idt_e
->gd_hioffset
<< 16);
124 intercepted
[idx
] = 1;
125 ist_use_table
[fw_idt_e
->gd_ist
]++;
126 loader_idt_e
->gd_looffset
= my_handler
;
127 loader_idt_e
->gd_hioffset
= my_handler
>> 16;
129 * We reuse uefi selector for the code segment for the exception
130 * handler code, while the reason for the fault might be the
131 * corruption of that gdt entry. On the other hand, allocating
132 * our own descriptor might be not much better, if gdt is corrupted.
134 loader_idt_e
->gd_selector
= fw_idt_e
->gd_selector
;
135 loader_idt_e
->gd_ist
= 0;
136 loader_idt_e
->gd_type
= SDT_SYSIGT
;
137 loader_idt_e
->gd_dpl
= 0;
138 loader_idt_e
->gd_p
= 1;
139 loader_idt_e
->gd_xx
= 0;
140 loader_idt_e
->sd_xx1
= 0;
142 #define PREPARE_EXCEPTION(N) \
143 extern char EXC##N##_handler[]; \
144 prepare_exception(N, (uintptr_t)EXC##N##_handler, ist_use_table);
151 BS
->FreePages(lidt_pa
, EFI_SIZE_TO_PAGES(fw_idt
.rd_limit
));
154 if (exc_stack_pa
!= 0) {
155 BS
->FreePages(exc_stack_pa
, 1);
158 if (tss_pa
!= 0 && tss_fw_seg
== 0) {
159 BS
->FreePages(tss_pa
, EFI_SIZE_TO_PAGES(sizeof(struct
163 if (loader_gdt_pa
!= 0) {
164 BS
->FreePages(tss_pa
, 2);
172 efi_setup_tss(struct region_descriptor
*gdt
, uint32_t loader_tss_idx
,
173 struct amd64tss
**tss
)
176 struct system_segment_descriptor
*tss_desc
;
178 tss_desc
= (struct system_segment_descriptor
*)(gdt
->rd_base
+
179 (loader_tss_idx
<< 3));
180 status
= BS
->AllocatePages(AllocateAnyPages
, EfiLoaderData
,
181 EFI_SIZE_TO_PAGES(sizeof(struct amd64tss
)), &tss_pa
);
182 if (EFI_ERROR(status
)) {
183 printf("efi_setup_tss: AllocatePages tss error %lu\n",
184 EFI_ERROR_CODE(status
));
187 *tss
= (struct amd64tss
*)tss_pa
;
188 bzero(*tss
, sizeof(**tss
));
189 tss_desc
->sd_lolimit
= sizeof(struct amd64tss
);
190 tss_desc
->sd_lobase
= tss_pa
;
191 tss_desc
->sd_type
= SDT_SYSTSS
;
192 tss_desc
->sd_dpl
= 0;
194 tss_desc
->sd_hilimit
= sizeof(struct amd64tss
) >> 16;
195 tss_desc
->sd_gran
= 0;
196 tss_desc
->sd_hibase
= tss_pa
>> 24;
197 tss_desc
->sd_xx0
= 0;
198 tss_desc
->sd_xx1
= 0;
199 tss_desc
->sd_mbz
= 0;
200 tss_desc
->sd_xx2
= 0;
205 efi_redirect_exceptions(void)
207 int ist_use_table
[NUM_IST
];
208 struct gate_descriptor
*loader_idt_e
;
209 struct system_segment_descriptor
*tss_desc
, *gdt_desc
;
210 struct amd64tss
*tss
;
211 struct region_descriptor
*gdt_rd
, loader_gdt
;
217 status
= BS
->AllocatePages(AllocateAnyPages
, EfiLoaderData
,
218 EFI_SIZE_TO_PAGES(fw_idt
.rd_limit
), &lidt_pa
);
219 if (EFI_ERROR(status
)) {
220 printf("efi_redirect_exceptions: AllocatePages IDT error %lu\n",
221 EFI_ERROR_CODE(status
));
225 status
= BS
->AllocatePages(AllocateAnyPages
, EfiLoaderData
, 1,
227 if (EFI_ERROR(status
)) {
228 printf("efi_redirect_exceptions: AllocatePages stk error %lu\n",
229 EFI_ERROR_CODE(status
));
234 loader_idt
.rd_limit
= fw_idt
.rd_limit
;
235 bcopy((void *)fw_idt
.rd_base
, (void *)loader_idt
.rd_base
,
236 loader_idt
.rd_limit
);
237 bzero(ist_use_table
, sizeof(ist_use_table
));
238 bzero(fw_intr_handlers
, sizeof(fw_intr_handlers
));
239 bzero(intercepted
, sizeof(intercepted
));
242 tss_fw_seg
= read_tr();
244 if (tss_fw_seg
== 0) {
245 for (i
= 2; (i
<< 3) + sizeof(*gdt_desc
) <= fw_gdt
.rd_limit
;
247 gdt_desc
= (struct system_segment_descriptor
*)(
248 fw_gdt
.rd_base
+ (i
<< 3));
249 if (gdt_desc
->sd_type
== 0 && gdt_desc
->sd_mbz
== 0) {
254 if (gdt_rd
== NULL
) {
256 printf("efi_redirect_exceptions: all slots "
257 "in gdt are used\n");
261 loader_gdt
.rd_limit
= roundup2(fw_gdt
.rd_limit
+
262 sizeof(struct system_segment_descriptor
),
263 sizeof(struct system_segment_descriptor
)) - 1;
264 i
= (loader_gdt
.rd_limit
+ 1 -
265 sizeof(struct system_segment_descriptor
)) /
266 sizeof(struct system_segment_descriptor
) * 2;
267 status
= BS
->AllocatePages(AllocateAnyPages
,
269 EFI_SIZE_TO_PAGES(loader_gdt
.rd_limit
),
271 if (EFI_ERROR(status
)) {
272 printf("efi_setup_tss: AllocatePages gdt error "
273 "%lu\n", EFI_ERROR_CODE(status
));
278 loader_gdt
.rd_base
= loader_gdt_pa
;
279 bzero((void *)loader_gdt
.rd_base
, loader_gdt
.rd_limit
);
280 bcopy((void *)fw_gdt
.rd_base
,
281 (void *)loader_gdt
.rd_base
, fw_gdt
.rd_limit
);
282 gdt_rd
= &loader_gdt
;
285 if (!efi_setup_tss(gdt_rd
, i
, &tss
)) {
291 tss_desc
= (struct system_segment_descriptor
*)((char *)
292 fw_gdt
.rd_base
+ tss_fw_seg
);
293 if (tss_desc
->sd_type
!= SDT_SYSTSS
&&
294 tss_desc
->sd_type
!= SDT_SYSBSY
) {
295 printf("LTR points to non-TSS descriptor\n");
299 tss_pa
= tss_desc
->sd_lobase
+ (tss_desc
->sd_hibase
<< 16);
300 tss
= (struct amd64tss
*)tss_pa
;
301 tss_desc
->sd_type
= SDT_SYSTSS
; /* unbusy */
304 PREPARE_EXCEPTION(0);
305 PREPARE_EXCEPTION(1);
306 PREPARE_EXCEPTION(2);
307 PREPARE_EXCEPTION(3);
308 PREPARE_EXCEPTION(4);
309 PREPARE_EXCEPTION(5);
310 PREPARE_EXCEPTION(6);
311 PREPARE_EXCEPTION(7);
312 PREPARE_EXCEPTION(8);
313 PREPARE_EXCEPTION(9);
314 PREPARE_EXCEPTION(10);
315 PREPARE_EXCEPTION(11);
316 PREPARE_EXCEPTION(12);
317 PREPARE_EXCEPTION(13);
318 PREPARE_EXCEPTION(14);
319 PREPARE_EXCEPTION(16);
320 PREPARE_EXCEPTION(17);
321 PREPARE_EXCEPTION(18);
322 PREPARE_EXCEPTION(19);
323 PREPARE_EXCEPTION(20);
325 exc_rsp
= exc_stack_pa
+ PAGE_SIZE
-
326 (6 /* hw exception frame */ + 3 /* scratch regs */) * 8;
328 /* Find free IST and use it */
329 for (ist
= 1; ist
< NUM_IST
; ist
++) {
330 if (ist_use_table
[ist
] == 0)
333 if (ist
== NUM_IST
) {
334 printf("efi_redirect_exceptions: all ISTs used\n");
339 for (i
= 0; i
< NUM_EXC
; i
++) {
340 loader_idt_e
= &((struct gate_descriptor
*)loader_idt
.
343 loader_idt_e
->gd_ist
= ist
;
345 (&(tss
->tss_ist1
))[ist
- 1] = exc_stack_pa
+ PAGE_SIZE
;
347 /* Switch to new IDT */
348 rfl
= intr_disable();
349 if (loader_gdt_pa
!= 0)
350 bare_lgdt(&loader_gdt
);
359 efi_unredirect_exceptions(void)
366 rfl
= intr_disable();
368 (&(((struct amd64tss
*)tss_pa
)->tss_ist1
))[ist
- 1] = 0;
369 if (loader_gdt_pa
!= 0)
379 command_grab_faults(int argc
, char *argv
[])
383 res
= efi_redirect_exceptions();
388 COMMAND_SET(grap_faults
, "grab_faults", "grab faults", command_grab_faults
);
391 command_ungrab_faults(int argc
, char *argv
[])
394 efi_unredirect_exceptions();
397 COMMAND_SET(ungrab_faults
, "ungrab_faults", "ungrab faults",
398 command_ungrab_faults
);
401 command_fault(int argc
, char *argv
[])
407 COMMAND_SET(fault
, "fault", "generate fault", command_fault
);