1 /*        $NetBSD: undefined.c,v 1.75 2023/10/05 19:41:03 ad Exp $    */
2 
3 /*
4  * Copyright (c) 2001 Ben Harris.
5  * Copyright (c) 1995 Mark Brinicombe.
6  * Copyright (c) 1995 Brini.
7  * All rights reserved.
8  *
9  * This code is derived from software written for Brini by Mark Brinicombe
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by Brini.
22  * 4. The name of the company nor the name of the author may be used to
23  *    endorse or promote products derived from this software without specific
24  *    prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
27  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * RiscBSD kernel project
39  *
40  * undefined.c
41  *
42  * Fault handler
43  *
44  * Created      : 06/01/95
45  */
46 
47 #include "opt_cputypes.h"
48 #include "opt_ddb.h"
49 #include "opt_dtrace.h"
50 #include "opt_kgdb.h"
51 
52 #include <sys/cdefs.h>
53 __KERNEL_RCSID(0, "$NetBSD: undefined.c,v 1.75 2023/10/05 19:41:03 ad Exp $");
54 
55 #include <sys/param.h>
56 #include <sys/cpu.h>
57 #ifdef KGDB
58 #include <sys/kgdb.h>
59 #endif
60 #include <sys/kmem.h>
61 #include <sys/proc.h>
62 #include <sys/queue.h>
63 #include <sys/signal.h>
64 #include <sys/systm.h>
65 
66 #include <uvm/uvm_extern.h>
67 
68 #include <arm/locore.h>
69 #include <arm/undefined.h>
70 
71 #include <machine/pcb.h>
72 #include <machine/trap.h>
73 
74 #ifdef VERBOSE_ARM32
75 #include <arch/arm/arm/disassem.h>
76 #endif
77 
78 #ifdef DDB
79 #include <ddb/db_output.h>
80 #include <machine/db_machdep.h>
81 #endif
82 
83 static int gdb_trapper(u_int, u_int, struct trapframe *, int);
84 
85 LIST_HEAD(, undefined_handler) undefined_handlers[NUM_UNKNOWN_HANDLERS];
86 
87 
88 void *
install_coproc_handler(int coproc,undef_handler_t handler)89 install_coproc_handler(int coproc, undef_handler_t handler)
90 {
91           struct undefined_handler *uh;
92 
93           KASSERT(coproc >= 0 && coproc < NUM_UNKNOWN_HANDLERS);
94           KASSERT(handler != NULL); /* Used to be legal. */
95 
96           uh = kmem_alloc(sizeof(*uh), KM_SLEEP);
97           uh->uh_handler = handler;
98           install_coproc_handler_static(coproc, uh);
99           return uh;
100 }
101 
102 void
replace_coproc_handler(int coproc,undef_handler_t handler)103 replace_coproc_handler(int coproc, undef_handler_t handler)
104 {
105           LIST_INIT(&undefined_handlers[coproc]);
106           install_coproc_handler(coproc, handler);
107 }
108 
109 void
install_coproc_handler_static(int coproc,struct undefined_handler * uh)110 install_coproc_handler_static(int coproc, struct undefined_handler *uh)
111 {
112 
113           LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
114 }
115 
116 void
remove_coproc_handler(void * cookie)117 remove_coproc_handler(void *cookie)
118 {
119           struct undefined_handler *uh = cookie;
120 
121           LIST_REMOVE(uh, uh_link);
122           kmem_free(uh, sizeof(*uh));
123 }
124 
125 static int
cp15_trapper(u_int addr,u_int insn,struct trapframe * tf,int code)126 cp15_trapper(u_int addr, u_int insn, struct trapframe *tf, int code)
127 {
128           struct lwp * const l = curlwp;
129 
130 #if defined(THUMB_CODE) && !defined(CPU_ARMV7)
131           if (tf->tf_spsr & PSR_T_bit)
132                     return 1;
133 #endif
134           if (code != FAULT_USER)
135                     return 1;
136 
137           /*
138            * Don't overwrite sp, pc, etc.
139            */
140           const u_int regno = (insn >> 12) & 15;
141           if (regno == 13 || regno == 15)
142                     return 1;
143 
144           /*
145            * Get a pointer to the register used in the instruction to be emulated.
146            */
147           register_t * const regp = &tf->tf_r0 + regno;
148 
149           /*
150            * Handle MRC p15, 0, <Rd>, c13, c0, 3 (Read User read-only thread id)
151            */
152           if ((insn & 0xffff0fff) == 0xee1d0f70) {
153                     *regp = (uintptr_t)l->l_private;
154                     tf->tf_pc += INSN_SIZE;
155                     curcpu()->ci_und_cp15_ev.ev_count++;
156                     return 0;
157           }
158 
159           /*
160            * Handle {MRC,MCR} p15, 0, <Rd>, c13, c0, 2 (User read/write thread id)
161            */
162           if ((insn & 0xffef0fff) == 0xee0d0f50) {
163                     struct pcb * const pcb = lwp_getpcb(l);
164                     if (insn & 0x00100000)
165                               *regp = pcb->pcb_user_pid_rw;
166                     else
167                               pcb->pcb_user_pid_rw = *regp;
168                     tf->tf_pc += INSN_SIZE;
169                     curcpu()->ci_und_cp15_ev.ev_count++;
170                     return 0;
171           }
172 
173           return 1;
174 }
175 
176 static int
gdb_trapper(u_int addr,u_int insn,struct trapframe * tf,int code)177 gdb_trapper(u_int addr, u_int insn, struct trapframe *tf, int code)
178 {
179           struct lwp * const l = curlwp;
180 
181 #ifdef THUMB_CODE
182           if (tf->tf_spsr & PSR_T_bit) {
183                     if (insn == GDB_THUMB_BREAKPOINT)
184                               goto bkpt;
185           }
186           else
187 #endif
188           {
189                     if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
190 #ifdef THUMB_CODE
191                     bkpt:
192 #endif
193                               if (code == FAULT_USER) {
194                                         ksiginfo_t ksi;
195 
196                                         KSI_INIT_TRAP(&ksi);
197                                         ksi.ksi_signo = SIGTRAP;
198                                         ksi.ksi_code = TRAP_BRKPT;
199                                         ksi.ksi_addr = (uint32_t *)addr;
200                                         ksi.ksi_trap = 0;
201                                         trapsignal(l, &ksi);
202                                         return 0;
203                               }
204 #ifdef KGDB
205                               return !kgdb_trap(T_BREAKPOINT, tf);
206 #endif
207                     }
208           }
209           return 1;
210 }
211 
212 #ifdef FPU_VFP
213 /*
214  * Used to test for a VFP. The following function is installed as a coproc10
215  * handler on the undefined instruction vector and then we issue a VFP
216  * instruction. If ci_vfd_id is set to zero then the VFP did not handle
217  * the instruction so must be absent, or disabled.
218  */
219 
220 static int
vfp_test(u_int address,u_int insn,trapframe_t * frame,int fault_code)221 vfp_test(u_int address, u_int insn, trapframe_t *frame, int fault_code)
222 {
223           struct cpu_info * const ci = curcpu();
224 
225           frame->tf_pc += INSN_SIZE;
226           ci->ci_vfp_id = 0;
227 
228           return 0;
229 }
230 #endif
231 
232 static struct undefined_handler cp15_uh = {
233           .uh_handler = cp15_trapper,
234 };
235 static struct undefined_handler gdb_uh = {
236           .uh_handler = gdb_trapper,
237 };
238 #ifdef THUMB_CODE
239 static struct undefined_handler gdb_uh_thumb = {
240           .uh_handler = gdb_trapper,
241 };
242 #endif
243 #ifdef FPU_VFP
244 struct undefined_handler vfptest_uh = {
245           .uh_handler = vfp_test,
246 };
247 #endif
248 
249 #ifdef KDTRACE_HOOKS
250 #include <sys/dtrace_bsd.h>
251 
252 /* Not used for now, but needed for dtrace/fbt modules */
253 dtrace_doubletrap_func_t      dtrace_doubletrap_func = NULL;
254 dtrace_trap_func_t            dtrace_trap_func = NULL;
255 
256 int (* dtrace_invop_jump_addr)(struct trapframe *);
257 
258 static int
dtrace_trapper(u_int addr,struct trapframe * frame)259 dtrace_trapper(u_int addr, struct trapframe *frame)
260 {
261           u_int insn = read_insn(addr, false);
262 
263           if (dtrace_invop_jump_addr == NULL)
264                     return 1;
265 
266           if (!DTRACE_IS_BREAKPOINT(insn))
267                     return 1;
268 
269           /* cond value is encoded in the low nibble */
270           if (!arm_cond_ok_p(__SHIFTIN(insn, INSN_COND_MASK), frame->tf_spsr)) {
271                     frame->tf_pc += INSN_SIZE;
272                     return 0;
273           }
274 
275           dtrace_invop_jump_addr(frame);
276           return 0;
277 }
278 #endif
279 
280 void
undefined_init(void)281 undefined_init(void)
282 {
283           int loop;
284 
285           /* Not actually necessary -- the initialiser is just NULL */
286           for (loop = 0; loop < NUM_UNKNOWN_HANDLERS; ++loop)
287                     LIST_INIT(&undefined_handlers[loop]);
288 
289           /* Install handler for CP15 emulation */
290           install_coproc_handler_static(SYSTEM_COPROC, &cp15_uh);
291 
292           /* Install handler for GDB breakpoints */
293           install_coproc_handler_static(CORE_UNKNOWN_HANDLER, &gdb_uh);
294 #ifdef THUMB_CODE
295           install_coproc_handler_static(THUMB_UNKNOWN_HANDLER, &gdb_uh_thumb);
296 #endif
297 #ifdef FPU_VFP
298           install_coproc_handler_static(VFP_COPROC, &vfptest_uh);
299 #endif
300 }
301 
302 void
undefinedinstruction(trapframe_t * tf)303 undefinedinstruction(trapframe_t *tf)
304 {
305           struct lwp *l;
306           vaddr_t fault_pc;
307           int fault_instruction;
308           int fault_code;
309           int coprocessor;
310           int user;
311           struct undefined_handler *uh;
312 
313           curcpu()->ci_und_ev.ev_count++;
314 
315 #ifdef KDTRACE_HOOKS
316           if ((tf->tf_spsr & PSR_MODE) != PSR_USR32_MODE) {
317                     tf->tf_pc -= INSN_SIZE;
318                     if (dtrace_trapper(tf->tf_pc, tf) == 0)
319                               return;
320                     tf->tf_pc += INSN_SIZE; /* Reset for the rest code */
321           }
322 #endif
323 
324           /* Enable interrupts if they were enabled before the exception. */
325           restore_interrupts(tf->tf_spsr & IF32_bits);
326 
327 #ifdef THUMB_CODE
328           if (tf->tf_spsr & PSR_T_bit)
329                     tf->tf_pc -= THUMB_INSN_SIZE;
330           else
331 #endif
332           {
333                     tf->tf_pc -= INSN_SIZE;
334           }
335 
336           fault_pc = tf->tf_pc;
337 
338           /* Get the current lwp/proc structure or lwp0/proc0 if there is none. */
339           l = curlwp;
340 
341           if ((tf->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
342                     user = 1;
343           } else
344                     user = 0;
345 
346 
347 #ifdef THUMB_CODE
348           if (tf->tf_spsr & PSR_T_bit) {
349                     fault_instruction = read_thumb_insn(fault_pc, user);
350                     if (fault_instruction >= 0xe000) {
351                               fault_instruction = (fault_instruction << 16)
352                                   | read_thumb_insn(fault_pc + 2, user);
353                     }
354           }
355           else
356 #endif
357           {
358                     /*
359                      * Make sure the program counter is correctly aligned so we
360                      * don't take an alignment fault trying to read the opcode.
361                      */
362                     if (__predict_false((fault_pc & 3) != 0)) {
363                               ksiginfo_t ksi;
364                               /* Give the user an illegal instruction signal. */
365                               KSI_INIT_TRAP(&ksi);
366                               ksi.ksi_signo = SIGILL;
367                               ksi.ksi_code = ILL_ILLOPC;
368                               ksi.ksi_addr = (uint32_t *)(intptr_t) fault_pc;
369                               trapsignal(l, &ksi);
370                               userret(l);
371                               return;
372                     }
373                     /*
374                      * Should use ufetch_32() here .. but in the interests of
375                      * squeezing every bit of speed we will just use
376                      * read_insn(). We know the instruction can be read
377                      * as was just executed so this will never fail unless
378                      * the kernel is screwed up in which case it does
379                      * not really matter does it?
380                      */
381                     fault_instruction = read_insn(fault_pc, user);
382           }
383 
384           curcpu()->ci_data.cpu_ntrap++;
385 
386 #ifdef THUMB_CODE
387           if ((tf->tf_spsr & PSR_T_bit) && !CPU_IS_ARMV7_P()) {
388                     coprocessor = THUMB_UNKNOWN_HANDLER;
389           }
390           else
391 #endif
392           {
393                     /* Check for coprocessor instruction */
394 
395                     /*
396                      * According to the datasheets you only need to look at
397                      * bit 27 of the instruction to tell the difference
398                      * between and undefined instruction and a coprocessor
399                      * instruction following an undefined instruction trap.
400                      *
401                      * ARMv5 adds undefined instructions in the NV space,
402                      * even when bit 27 is set.
403                      */
404 
405                     if ((fault_instruction & (1 << 27)) != 0
406                         && (fault_instruction & 0xf0000000) != 0xf0000000) {
407                               coprocessor = (fault_instruction >> 8) & 0x0f;
408 #ifdef THUMB_CODE
409                     } else if ((tf->tf_spsr & PSR_T_bit) && !CPU_IS_ARMV7_P()) {
410                               coprocessor = THUMB_UNKNOWN_HANDLER;
411 #endif
412                     } else {
413                               coprocessor = CORE_UNKNOWN_HANDLER;
414                     }
415           }
416 
417           if (user) {
418                     /*
419                      * Modify the fault_code to reflect the USR/SVC state at
420                      * time of fault.
421                      */
422                     fault_code = FAULT_USER;
423                     KASSERTMSG(tf == lwp_trapframe(l), "tf %p vs %p", tf,
424                         lwp_trapframe(l));
425           } else
426                     fault_code = 0;
427 
428           /* OK this is were we do something about the instruction. */
429           LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link) {
430                     int ret = uh->uh_handler(fault_pc, fault_instruction, tf,
431                         fault_code);
432 
433                     if (ret == 0)
434                               break;
435           }
436 
437           if (uh == NULL) {
438                     /* Fault has not been handled */
439                     ksiginfo_t ksi;
440 
441 #ifdef VERBOSE_ARM32
442                     int s = spltty();
443 
444                     if ((fault_instruction & 0x0f000010) == 0x0e000000) {
445                               printf("CDP\n");
446                               disassemble(fault_pc);
447                     } else if ((fault_instruction & 0x0e000000) == 0x0c000000) {
448                               printf("LDC/STC\n");
449                               disassemble(fault_pc);
450                     } else if ((fault_instruction & 0x0f000010) == 0x0e000010) {
451                               printf("MRC/MCR\n");
452                               disassemble(fault_pc);
453                     } else if ((fault_instruction & ~INSN_COND_MASK)
454                                != (KERNEL_BREAKPOINT & ~INSN_COND_MASK)) {
455                               printf("Undefined instruction\n");
456                               disassemble(fault_pc);
457                     }
458 
459                     splx(s);
460 #endif
461 
462                     if ((fault_code & FAULT_USER) == 0) {
463 #ifdef DDB
464                               db_printf("Undefined instruction %#x in kernel at %#lx (LR %#x SP %#x)\n",
465                                   fault_instruction, fault_pc, tf->tf_svc_lr, tf->tf_svc_sp);
466                               kdb_trap(T_FAULT, tf);
467 #else
468                               panic("undefined instruction %#x in kernel at %#lx", fault_instruction, fault_pc);
469 #endif
470                     }
471                     KSI_INIT_TRAP(&ksi);
472                     ksi.ksi_signo = SIGILL;
473                     ksi.ksi_code = ILL_ILLOPC;
474                     ksi.ksi_addr = (uint32_t *)fault_pc;
475                     ksi.ksi_trap = fault_instruction;
476                     trapsignal(l, &ksi);
477           }
478 
479           if ((fault_code & FAULT_USER) == 0)
480                     return;
481 
482           userret(l);
483 }
484