1 /*
2  * Copyright (c) 2009 Corey Tabaka
3  * Copyright (c) 2015-2018 Intel Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files
7  * (the "Software"), to deal in the Software without restriction,
8  * including without limitation the rights to use, copy, modify, merge,
9  * publish, distribute, sublicense, and/or sell copies of the Software,
10  * and to permit persons to whom the Software is furnished to do so,
11  * subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include <debug.h>
25 #include <trace.h>
26 #include <arch/x86.h>
27 #include <arch/x86/exceptions.h>
28 #include <arch/fpu.h>
29 #include <kernel/thread.h>
30 #include <lib/trusty/trusty_app.h>
31 #include <platform.h>
32 #include <inttypes.h>
33 
34 struct fault_handler_table_entry {
35     int64_t rip;
36     int64_t fault_handler;
37 };
38 
39 extern struct fault_handler_table_entry __fault_handler_table_start[];
40 extern struct fault_handler_table_entry __fault_handler_table_end[];
41 
42 /**
43  * prel_to_abs_u64() - Convert a position-relative value to an absolute.
44  * @ptr: Pointer to a 64-bit position-relative value.
45  * @result: Pointer to the location for the result.
46  *
47  * Return: %true in case of success, %false for overflow.
48  */
prel_to_abs_u64(const int64_t * ptr,uint64_t * result)49 static inline bool prel_to_abs_u64(const int64_t* ptr, uint64_t* result) {
50     return !__builtin_add_overflow((uintptr_t)ptr, *ptr, result);
51 }
52 
check_fault_handler_table(x86_iframe_t * frame)53 static bool check_fault_handler_table(x86_iframe_t *frame)
54 {
55     struct fault_handler_table_entry *fault_handler;
56 
57     for (fault_handler = __fault_handler_table_start;
58             fault_handler < __fault_handler_table_end;
59             fault_handler++) {
60         uint64_t addr;
61         if (!prel_to_abs_u64(&fault_handler->rip, &addr)) {
62             /* Invalid entry, ignore it */
63             continue;
64         }
65         if (addr == frame->ip) {
66             if (!prel_to_abs_u64(&fault_handler->fault_handler, &addr)) {
67                 /*
68                  * An entry with an invalid handler address. We don't expect
69                  * another entry with the same pc, so we break out of
70                  * the loop early.
71                  */
72                 return false;
73             }
74 
75             frame->ip = addr;
76             return true;
77         }
78     }
79     return false;
80 }
81 
82 extern enum handler_return platform_irq(x86_iframe_t *frame);
83 
dump_fault_frame(x86_iframe_t * frame)84 static void dump_fault_frame(x86_iframe_t *frame)
85 {
86 #if ARCH_X86_32
87     dprintf(CRITICAL, " CS:     %04x EIP: %08x EFL: %08x CR2: %08x\n",
88             frame->cs, frame->ip, frame->flags, x86_get_cr2());
89     dprintf(CRITICAL, "EAX: %08x ECX: %08x EDX: %08x EBX: %08x\n",
90             frame->ax, frame->cx, frame->dx, frame->bx);
91     dprintf(CRITICAL, "ESP: %08x EBP: %08x ESI: %08x EDI: %08x\n",
92             frame->sp, frame->bp, frame->si, frame->di);
93     dprintf(CRITICAL, " DS:     %04x  ES:     %04x  FS:   %04x  GS:     %04x\n",
94             frame->ds, frame->es, frame->fs, frame->gs);
95 #elif ARCH_X86_64
96     dprintf(CRITICAL, " CS:              %4" PRIx64 " RIP: %16" PRIx64 " EFL: %16" PRIx64 " CR2: %16" PRIx64 "\n",
97             frame->cs, frame->ip, frame->flags, x86_get_cr2());
98     dprintf(CRITICAL, " RAX: %16" PRIx64 " RBX: %16" PRIx64 " RCX: %16" PRIx64 " RDX: %16" PRIx64 "\n",
99             frame->ax, frame->bx, frame->cx, frame->dx);
100     dprintf(CRITICAL, " RSI: %16" PRIx64 " RDI: %16" PRIx64 " RBP: %16" PRIx64 " RSP: %16" PRIx64 "\n",
101             frame->si, frame->di, frame->bp, frame->user_sp);
102     dprintf(CRITICAL, "  R8: %16" PRIx64 "  R9: %16" PRIx64 " R10: %16" PRIx64 " R11: %16" PRIx64 "\n",
103             frame->r8, frame->r9, frame->r10, frame->r11);
104     dprintf(CRITICAL, " R12: %16" PRIx64 " R13: %16" PRIx64 " R14: %16" PRIx64 " R15: %16" PRIx64 "\n",
105             frame->r12, frame->r13, frame->r14, frame->r15);
106     dprintf(CRITICAL, "errc: %16" PRIx64 "\n",
107             frame->err_code);
108 #endif
109 
110     // dump the bottom of the current stack
111     addr_t stack = (addr_t) frame;
112 
113     if (stack != 0) {
114         dprintf(CRITICAL, "bottom of stack at 0x%08x:\n", (unsigned int)stack);
115         hexdump((void *)stack, 512);
116     }
117 }
118 
exception_die(x86_iframe_t * frame,const char * msg)119 static void exception_die(x86_iframe_t *frame, const char *msg)
120 {
121     dprintf(CRITICAL, "%s", msg);
122     dump_fault_frame(frame);
123 
124     panic("die");
125     for (;;) {
126         x86_cli();
127         x86_hlt();
128     }
129 }
130 
x86_syscall_handler(x86_iframe_t * frame)131 void x86_syscall_handler(x86_iframe_t *frame)
132 {
133     exception_die(frame, "unhandled syscall, halting\n");
134 }
135 
x86_gpf_handler(x86_iframe_t * frame)136 void x86_gpf_handler(x86_iframe_t *frame)
137 {
138     exception_die(frame, "unhandled gpf, halting\n");
139 }
140 
x86_invop_handler(x86_iframe_t * frame)141 void x86_invop_handler(x86_iframe_t *frame)
142 {
143     exception_die(frame, "unhandled invalid op, halting\n");
144 }
145 
x86_unhandled_exception(x86_iframe_t * frame)146 void x86_unhandled_exception(x86_iframe_t *frame)
147 {
148     printf("vector %u\n", (uint)frame->vector);
149     exception_die(frame, "unhandled exception, halting\n");
150 }
151 
x86_pfe_handler(x86_iframe_t * frame)152 void x86_pfe_handler(x86_iframe_t *frame)
153 {
154     /* Handle a page fault exception */
155     uint32_t error_code;
156     thread_t *current_thread;
157     error_code = frame->err_code;
158 
159     if (check_fault_handler_table(frame)) {
160         return;
161     }
162 
163 #ifdef PAGE_FAULT_DEBUG_INFO
164     dprintf(CRITICAL, "<PAGE FAULT> Instruction Pointer   = 0x%x:0x%x\n",
165             (unsigned int)frame->cs & X86_8BYTE_MASK,
166             (unsigned int)frame->ip);
167     dprintf(CRITICAL, "<PAGE FAULT> Stack Pointer         = 0x%x:0x%x\n",
168             (unsigned int)frame->user_ss & X86_8BYTE_MASK,
169             (unsigned int)frame->user_sp);
170     dprintf(CRITICAL, "<PAGE FAULT> Fault Linear Address = 0x%x\n",
171             (unsigned int)x86_get_cr2());
172     dprintf(CRITICAL, "<PAGE FAULT> Error Code Value      = 0x%x\n",
173             error_code);
174     dprintf(CRITICAL, "<PAGE FAULT> Error Code Type = %s %s %s%s, %s\n",
175             error_code & PFEX_U ? "user" : "supervisor",
176             error_code & PFEX_W ? "write" : "read",
177             error_code & PFEX_I ? "instruction" : "data",
178             error_code & PFEX_RSV ? " rsv" : "",
179             error_code & PFEX_P ? "protection violation" : "page not present");
180 #endif
181 
182     current_thread = get_current_thread();
183     dump_thread(current_thread);
184 
185     if (error_code & PFEX_U) {
186         // User mode page fault
187         switch (error_code) {
188             case 4:
189             case 5:
190             case 6:
191             case 7:
192             default:
193                 arch_enable_ints();
194                 trusty_app_crash(error_code, 0, 0);
195                 break;
196         }
197     } else {
198         // Supervisor mode page fault
199         switch (error_code) {
200 
201             case 0:
202             case 1:
203             case 2:
204             case 3:
205             default:
206                 exception_die(frame, "Page Fault exception, halting\n");
207                 break;
208         }
209     }
210 }
211 
212 /* top level x86 exception handler for most exceptions and irqs */
x86_exception_handler(x86_iframe_t * frame)213 void x86_exception_handler(x86_iframe_t *frame)
214 {
215     // get the current vector
216     unsigned int vector = frame->vector;
217 
218     THREAD_STATS_INC(interrupts);
219 
220     // deliver the interrupt
221     enum handler_return ret = INT_NO_RESCHEDULE;
222 
223     switch (vector) {
224         case INT_GP_FAULT:
225             x86_gpf_handler(frame);
226             break;
227 
228         case INT_INVALID_OP:
229             x86_invop_handler(frame);
230             break;
231 
232         case INT_PAGE_FAULT:
233             x86_pfe_handler(frame);
234             break;
235 
236         case INT_DEV_NA_EX:
237 #if X86_WITH_FPU
238             fpu_dev_na_handler();
239 #endif
240             break;
241 
242         case INT_NMI:
243             /*
244              * Don't trust GS for NMI exceptions. The NMI exception could
245              * trigger right before swap_gs in the exception entry code.
246              */
247             x86_check_and_fix_gs();
248             x86_unhandled_exception(frame);
249             break;
250 
251         case INT_DOUBLE_FAULT:
252             /*
253              * Don't trust GS for double fault exceptions. If a bug allowed
254              * user-space to run with a near full kernel stack (in TSS:RSP0),
255              * then a double fault might occur after the switch to the kernel
256              * CS, but before runs swap_gs in the original exception handler.
257              */
258             x86_check_and_fix_gs();
259             exception_die(frame, "double fault (kernel stack overflow?)\n");
260             break;
261 
262         case INT_MF: { /* x87 floating point math fault */
263             uint16_t fsw;
264             __asm__ __volatile__("fnstsw %0" : "=m" (fsw));
265             TRACEF("fsw 0x%hx\n", fsw);
266             exception_die(frame, "x87 math fault\n");
267             //asm volatile("fnclex");
268             break;
269         }
270         case INT_XM: { /* simd math fault */
271             uint32_t mxcsr;
272             __asm__ __volatile__("stmxcsr %0" : "=m" (mxcsr));
273             TRACEF("mxcsr 0x%x\n", mxcsr);
274             exception_die(frame, "simd math fault\n");
275             break;
276         }
277         case INT_DIVIDE_0:
278         case INT_DEBUG_EX:
279         case INT_STACK_FAULT:
280         case 3:
281         default:
282             x86_unhandled_exception(frame);
283             break;
284 
285         /* pass the rest of the irq vectors to the platform */
286         case 0x20 ... 255:
287             ret = platform_irq(frame);
288     }
289 
290     if (ret != INT_NO_RESCHEDULE)
291         thread_preempt();
292 }
293 
294