]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/trap.c
MFV: file 5.33
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / trap.c
1 /*-
2  * Copyright (c) 2014 Andrew Turner
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/pioctl.h>
37 #include <sys/proc.h>
38 #include <sys/ptrace.h>
39 #include <sys/syscall.h>
40 #include <sys/sysent.h>
41 #ifdef KDB
42 #include <sys/kdb.h>
43 #endif
44
45 #include <vm/vm.h>
46 #include <vm/pmap.h>
47 #include <vm/vm_kern.h>
48 #include <vm/vm_map.h>
49 #include <vm/vm_param.h>
50 #include <vm/vm_extern.h>
51
52 #include <machine/frame.h>
53 #include <machine/pcb.h>
54 #include <machine/pcpu.h>
55 #include <machine/undefined.h>
56
57 #ifdef KDTRACE_HOOKS
58 #include <sys/dtrace_bsd.h>
59 #endif
60
61 #ifdef VFP
62 #include <machine/vfp.h>
63 #endif
64
65 #ifdef KDB
66 #include <machine/db_machdep.h>
67 #endif
68
69 #ifdef DDB
70 #include <ddb/db_output.h>
71 #endif
72
73 extern register_t fsu_intr_fault;
74
75 /* Called from exception.S */
76 void do_el1h_sync(struct thread *, struct trapframe *);
77 void do_el0_sync(struct thread *, struct trapframe *);
78 void do_el0_error(struct trapframe *);
79 static void print_registers(struct trapframe *frame);
80
81 int (*dtrace_invop_jump_addr)(struct trapframe *);
82
83 static __inline void
84 call_trapsignal(struct thread *td, int sig, int code, void *addr)
85 {
86         ksiginfo_t ksi;
87
88         ksiginfo_init_trap(&ksi);
89         ksi.ksi_signo = sig;
90         ksi.ksi_code = code;
91         ksi.ksi_addr = addr;
92         trapsignal(td, &ksi);
93 }
94
95 int
96 cpu_fetch_syscall_args(struct thread *td)
97 {
98         struct proc *p;
99         register_t *ap;
100         struct syscall_args *sa;
101         int nap;
102
103         nap = 8;
104         p = td->td_proc;
105         ap = td->td_frame->tf_x;
106         sa = &td->td_sa;
107
108         sa->code = td->td_frame->tf_x[8];
109
110         if (sa->code == SYS_syscall || sa->code == SYS___syscall) {
111                 sa->code = *ap++;
112                 nap--;
113         }
114
115         if (p->p_sysent->sv_mask)
116                 sa->code &= p->p_sysent->sv_mask;
117         if (sa->code >= p->p_sysent->sv_size)
118                 sa->callp = &p->p_sysent->sv_table[0];
119         else
120                 sa->callp = &p->p_sysent->sv_table[sa->code];
121
122         sa->narg = sa->callp->sy_narg;
123         memcpy(sa->args, ap, nap * sizeof(register_t));
124         if (sa->narg > nap)
125                 panic("ARM64TODO: Could we have more than 8 args?");
126
127         td->td_retval[0] = 0;
128         td->td_retval[1] = 0;
129
130         return (0);
131 }
132
133 #include "../../kern/subr_syscall.c"
134
135 static void
136 svc_handler(struct thread *td, struct trapframe *frame)
137 {
138         int error;
139
140         if ((frame->tf_esr & ESR_ELx_ISS_MASK) == 0) {
141                 error = syscallenter(td);
142                 syscallret(td, error);
143         } else {
144                 call_trapsignal(td, SIGILL, ILL_ILLOPN, (void *)frame->tf_elr);
145                 userret(td, frame);
146         }
147 }
148
149 static void
150 data_abort(struct thread *td, struct trapframe *frame, uint64_t esr,
151     uint64_t far, int lower)
152 {
153         struct vm_map *map;
154         struct proc *p;
155         struct pcb *pcb;
156         vm_prot_t ftype;
157         vm_offset_t va;
158         int error, sig, ucode;
159 #ifdef KDB
160         bool handled;
161 #endif
162
163         /*
164          * According to the ARMv8-A rev. A.g, B2.10.5 "Load-Exclusive
165          * and Store-Exclusive instruction usage restrictions", state
166          * of the exclusive monitors after data abort exception is unknown.
167          */
168         clrex();
169
170 #ifdef KDB
171         if (kdb_active) {
172                 kdb_reenter();
173                 return;
174         }
175 #endif
176
177         pcb = td->td_pcb;
178         p = td->td_proc;
179         if (lower)
180                 map = &p->p_vmspace->vm_map;
181         else {
182                 /* The top bit tells us which range to use */
183                 if (far >= VM_MAXUSER_ADDRESS) {
184                         map = kernel_map;
185                 } else {
186                         map = &p->p_vmspace->vm_map;
187                         if (map == NULL)
188                                 map = kernel_map;
189                 }
190         }
191
192         if (pmap_fault(map->pmap, esr, far) == KERN_SUCCESS)
193                 return;
194
195         KASSERT(td->td_md.md_spinlock_count == 0,
196             ("data abort with spinlock held"));
197         if (td->td_critnest != 0 || WITNESS_CHECK(WARN_SLEEPOK |
198             WARN_GIANTOK, NULL, "Kernel page fault") != 0) {
199                 print_registers(frame);
200                 printf(" far: %16lx\n", far);
201                 printf(" esr:         %.8lx\n", esr);
202                 panic("data abort in critical section or under mutex");
203         }
204
205         va = trunc_page(far);
206         ftype = ((esr >> 6) & 1) ? VM_PROT_READ | VM_PROT_WRITE : VM_PROT_READ;
207
208         /* Fault in the page. */
209         error = vm_fault(map, va, ftype, VM_FAULT_NORMAL);
210         if (error != KERN_SUCCESS) {
211                 if (lower) {
212                         sig = SIGSEGV;
213                         if (error == KERN_PROTECTION_FAILURE)
214                                 ucode = SEGV_ACCERR;
215                         else
216                                 ucode = SEGV_MAPERR;
217                         call_trapsignal(td, sig, ucode, (void *)far);
218                 } else {
219                         if (td->td_intr_nesting_level == 0 &&
220                             pcb->pcb_onfault != 0) {
221                                 frame->tf_x[0] = error;
222                                 frame->tf_elr = pcb->pcb_onfault;
223                                 return;
224                         }
225
226                         printf("Fatal data abort:\n");
227                         print_registers(frame);
228                         printf(" far: %16lx\n", far);
229                         printf(" esr:         %.8lx\n", esr);
230
231 #ifdef KDB
232                         if (debugger_on_panic) {
233                                 kdb_why = KDB_WHY_TRAP;
234                                 handled = kdb_trap(ESR_ELx_EXCEPTION(esr), 0,
235                                     frame);
236                                 kdb_why = KDB_WHY_UNSET;
237                                 if (handled)
238                                         return;
239                         }
240 #endif
241                         panic("vm_fault failed: %lx", frame->tf_elr);
242                 }
243         }
244
245         if (lower)
246                 userret(td, frame);
247 }
248
249 static void
250 print_registers(struct trapframe *frame)
251 {
252         u_int reg;
253
254         for (reg = 0; reg < nitems(frame->tf_x); reg++) {
255                 printf(" %sx%d: %16lx\n", (reg < 10) ? " " : "", reg,
256                     frame->tf_x[reg]);
257         }
258         printf("  sp: %16lx\n", frame->tf_sp);
259         printf("  lr: %16lx\n", frame->tf_lr);
260         printf(" elr: %16lx\n", frame->tf_elr);
261         printf("spsr:         %8x\n", frame->tf_spsr);
262 }
263
264 void
265 do_el1h_sync(struct thread *td, struct trapframe *frame)
266 {
267         struct trapframe *oframe;
268         uint32_t exception;
269         uint64_t esr, far;
270
271         /* Read the esr register to get the exception details */
272         esr = frame->tf_esr;
273         exception = ESR_ELx_EXCEPTION(esr);
274
275 #ifdef KDTRACE_HOOKS
276         if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, exception))
277                 return;
278 #endif
279
280         CTR4(KTR_TRAP,
281             "do_el1_sync: curthread: %p, esr %lx, elr: %lx, frame: %p", td,
282             esr, frame->tf_elr, frame);
283
284         oframe = td->td_frame;
285
286         switch (exception) {
287         case EXCP_BRK:
288         case EXCP_WATCHPT_EL1:
289         case EXCP_SOFTSTP_EL1:
290                 break;
291         default:
292                 td->td_frame = frame;
293                 break;
294         }
295
296         switch(exception) {
297         case EXCP_FP_SIMD:
298         case EXCP_TRAP_FP:
299 #ifdef VFP
300                 if ((td->td_pcb->pcb_fpflags & PCB_FP_KERN) != 0) {
301                         vfp_restore_state();
302                 } else
303 #endif
304                 {
305                         print_registers(frame);
306                         printf(" esr:         %.8lx\n", esr);
307                         panic("VFP exception in the kernel");
308                 }
309                 break;
310         case EXCP_INSN_ABORT:
311         case EXCP_DATA_ABORT:
312                 far = READ_SPECIALREG(far_el1);
313                 intr_enable();
314                 data_abort(td, frame, esr, far, 0);
315                 break;
316         case EXCP_BRK:
317 #ifdef KDTRACE_HOOKS
318                 if ((esr & ESR_ELx_ISS_MASK) == 0x40d && \
319                     dtrace_invop_jump_addr != 0) {
320                         dtrace_invop_jump_addr(frame);
321                         break;
322                 }
323 #endif
324 #ifdef KDB
325                 kdb_trap(exception, 0,
326                     (td->td_frame != NULL) ? td->td_frame : frame);
327 #else
328                 panic("No debugger in kernel.\n");
329 #endif
330                 frame->tf_elr += 4;
331                 break;
332         case EXCP_WATCHPT_EL1:
333         case EXCP_SOFTSTP_EL1:
334 #ifdef KDB
335                 kdb_trap(exception, 0,
336                     (td->td_frame != NULL) ? td->td_frame : frame);
337 #else
338                 panic("No debugger in kernel.\n");
339 #endif
340                 break;
341         case EXCP_UNKNOWN:
342                 if (undef_insn(1, frame))
343                         break;
344                 /* FALLTHROUGH */
345         default:
346                 print_registers(frame);
347                 panic("Unknown kernel exception %x esr_el1 %lx\n", exception,
348                     esr);
349         }
350
351         td->td_frame = oframe;
352 }
353
354 void
355 do_el0_sync(struct thread *td, struct trapframe *frame)
356 {
357         pcpu_bp_harden bp_harden;
358         uint32_t exception;
359         uint64_t esr, far;
360
361         /* Check we have a sane environment when entering from userland */
362         KASSERT((uintptr_t)get_pcpu() >= VM_MIN_KERNEL_ADDRESS,
363             ("Invalid pcpu address from userland: %p (tpidr %lx)",
364              get_pcpu(), READ_SPECIALREG(tpidr_el1)));
365
366         esr = frame->tf_esr;
367         exception = ESR_ELx_EXCEPTION(esr);
368         switch (exception) {
369         case EXCP_INSN_ABORT_L:
370                 far = READ_SPECIALREG(far_el1);
371
372                 /*
373                  * Userspace may be trying to train the branch predictor to
374                  * attack the kernel. If we are on a CPU affected by this
375                  * call the handler to clear the branch predictor state.
376                  */
377                 if (far > VM_MAXUSER_ADDRESS) {
378                         bp_harden = PCPU_GET(bp_harden);
379                         if (bp_harden != NULL)
380                                 bp_harden();
381                 }
382                 break;
383         case EXCP_UNKNOWN:
384         case EXCP_DATA_ABORT_L:
385         case EXCP_DATA_ABORT:
386                 far = READ_SPECIALREG(far_el1);
387                 break;
388         }
389         intr_enable();
390
391         CTR4(KTR_TRAP,
392             "do_el0_sync: curthread: %p, esr %lx, elr: %lx, frame: %p", td, esr,
393             frame->tf_elr, frame);
394
395         switch(exception) {
396         case EXCP_FP_SIMD:
397         case EXCP_TRAP_FP:
398 #ifdef VFP
399                 vfp_restore_state();
400 #else
401                 panic("VFP exception in userland");
402 #endif
403                 break;
404         case EXCP_SVC32:
405         case EXCP_SVC64:
406                 svc_handler(td, frame);
407                 break;
408         case EXCP_INSN_ABORT_L:
409         case EXCP_DATA_ABORT_L:
410         case EXCP_DATA_ABORT:
411                 data_abort(td, frame, esr, far, 1);
412                 break;
413         case EXCP_UNKNOWN:
414                 if (!undef_insn(0, frame))
415                         call_trapsignal(td, SIGILL, ILL_ILLTRP, (void *)far);
416                 userret(td, frame);
417                 break;
418         case EXCP_SP_ALIGN:
419                 call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_sp);
420                 userret(td, frame);
421                 break;
422         case EXCP_PC_ALIGN:
423                 call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_elr);
424                 userret(td, frame);
425                 break;
426         case EXCP_BRK:
427                 call_trapsignal(td, SIGTRAP, TRAP_BRKPT, (void *)frame->tf_elr);
428                 userret(td, frame);
429                 break;
430         case EXCP_MSR:
431                 call_trapsignal(td, SIGILL, ILL_PRVOPC, (void *)frame->tf_elr); 
432                 userret(td, frame);
433                 break;
434         case EXCP_SOFTSTP_EL0:
435                 td->td_frame->tf_spsr &= ~PSR_SS;
436                 td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP;
437                 WRITE_SPECIALREG(MDSCR_EL1,
438                     READ_SPECIALREG(MDSCR_EL1) & ~DBG_MDSCR_SS);
439                 call_trapsignal(td, SIGTRAP, TRAP_TRACE,
440                     (void *)frame->tf_elr);
441                 userret(td, frame);
442                 break;
443         default:
444                 call_trapsignal(td, SIGBUS, BUS_OBJERR, (void *)frame->tf_elr);
445                 userret(td, frame);
446                 break;
447         }
448
449         KASSERT((td->td_pcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0,
450             ("Kernel VFP flags set while entering userspace"));
451         KASSERT(
452             td->td_pcb->pcb_fpusaved == &td->td_pcb->pcb_fpustate,
453             ("Kernel VFP state in use when entering userspace"));
454 }
455
456 void
457 do_el0_error(struct trapframe *frame)
458 {
459
460         panic("ARM64TODO: do_el0_error");
461 }
462