1 /* $OpenBSD: undefined.c,v 1.17 2023/01/06 19:23:53 miod Exp $ */
2 /* $NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $ */
3
4 /*
5 * Copyright (c) 2001 Ben Harris.
6 * Copyright (c) 1995 Mark Brinicombe.
7 * Copyright (c) 1995 Brini.
8 * All rights reserved.
9 *
10 * This code is derived from software written for Brini by Mark Brinicombe
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Brini.
23 * 4. The name of the company nor the name of the author may be used to
24 * endorse or promote products derived from this software without specific
25 * prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 * RiscBSD kernel project
40 *
41 * undefined.c
42 *
43 * Fault handler
44 *
45 * Created : 06/01/95
46 */
47
48 #include <sys/param.h>
49
50 #include <sys/malloc.h>
51 #include <sys/queue.h>
52 #include <sys/signal.h>
53 #include <sys/signalvar.h>
54 #include <sys/systm.h>
55 #include <sys/proc.h>
56 #include <sys/user.h>
57
58 #include <uvm/uvm_extern.h>
59
60 #include <machine/cpu.h>
61 #include <machine/frame.h>
62 #include <arm/undefined.h>
63 #include <arm/vfp.h>
64 #include <machine/trap.h>
65
66
67 static int gdb_trapper(u_int, u_int, struct trapframe *, int, uint32_t);
68
69 LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS];
70
71
72 void *
install_coproc_handler(int coproc,undef_handler_t handler)73 install_coproc_handler(int coproc, undef_handler_t handler)
74 {
75 struct undefined_handler *uh;
76
77 KASSERT(coproc >= 0 && coproc < MAX_COPROCS);
78 KASSERT(handler != NULL); /* Used to be legal. */
79
80 /* XXX: M_TEMP??? */
81 uh = (struct undefined_handler *)malloc(sizeof(*uh), M_TEMP, M_WAITOK);
82 uh->uh_handler = handler;
83 install_coproc_handler_static(coproc, uh);
84 return uh;
85 }
86
87 void
install_coproc_handler_static(int coproc,struct undefined_handler * uh)88 install_coproc_handler_static(int coproc, struct undefined_handler *uh)
89 {
90
91 LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link);
92 }
93
94 void
remove_coproc_handler(void * cookie)95 remove_coproc_handler(void *cookie)
96 {
97 struct undefined_handler *uh = cookie;
98
99 LIST_REMOVE(uh, uh_link);
100 free(uh, M_TEMP, 0);
101 }
102
103
104 static int
gdb_trapper(u_int addr,u_int insn,struct trapframe * frame,int code,uint32_t fpexc)105 gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code, uint32_t fpexc)
106 {
107 union sigval sv;
108 struct proc *p;
109 p = (curproc == NULL) ? &proc0 : curproc;
110
111 if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
112 if (code == FAULT_USER) {
113 sv.sival_int = addr;
114 trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv);
115 return 0;
116 }
117 }
118 return 1;
119 }
120
121 static struct undefined_handler gdb_uh;
122
123 void
undefined_init(void)124 undefined_init(void)
125 {
126 int loop;
127
128 /* Not actually necessary -- the initialiser is just NULL */
129 for (loop = 0; loop < MAX_COPROCS; ++loop)
130 LIST_INIT(&undefined_handlers[loop]);
131
132 /* Install handler for GDB breakpoints */
133 gdb_uh.uh_handler = gdb_trapper;
134 install_coproc_handler_static(0, &gdb_uh);
135 }
136
137
138 void
undefinedinstruction(trapframe_t * frame)139 undefinedinstruction(trapframe_t *frame)
140 {
141 struct proc *p;
142 u_int fault_pc;
143 int fault_instruction;
144 int fault_code;
145 int coprocessor;
146 struct undefined_handler *uh;
147 uint32_t fpexc;
148 #ifdef VERBOSE_ARM32
149 int s;
150 #endif
151 union sigval sv;
152
153 /* Before enabling interrupts, save FPU state */
154 fpexc = vfp_save();
155
156 /* Enable interrupts if they were enabled before the exception. */
157 if (!(frame->tf_spsr & PSR_I))
158 enable_interrupts(PSR_I);
159
160 frame->tf_pc -= INSN_SIZE;
161 fault_pc = frame->tf_pc;
162
163 /* Get the current proc structure or proc0 if there is none. */
164 p = (curproc == NULL) ? &proc0 : curproc;
165
166 /*
167 * Fetch the opcode and Make sure the program counter is correctly
168 * aligned.
169 */
170 if (copyin32((void *)fault_pc, &fault_instruction) != 0) {
171 /* Give the user an illegal instruction signal. */
172 sv.sival_int = (u_int32_t) fault_pc;
173 trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
174 userret(p);
175 return;
176 }
177
178 /* Update vmmeter statistics */
179 uvmexp.traps++;
180
181 /* Check for coprocessor instruction */
182
183 /*
184 * According to the datasheets you only need to look at bit 27 of the
185 * instruction to tell the difference between an undefined
186 * instruction and a coprocessor instruction following an undefined
187 * instruction trap.
188 */
189
190 coprocessor = 0;
191 if ((fault_instruction & (1 << 27)) != 0)
192 coprocessor = (fault_instruction >> 8) & 0x0f;
193 else { /* check for special instructions */
194 if (((fault_instruction & 0xfe000000) == 0xf2000000) ||
195 ((fault_instruction & 0xff100000) == 0xf4000000))
196 coprocessor = 10; /* vfp / simd */
197 }
198
199 if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
200 /*
201 * Modify the fault_code to reflect the USR/SVC state at
202 * time of fault.
203 */
204 fault_code = FAULT_USER;
205 p->p_addr->u_pcb.pcb_tf = frame;
206 } else
207 fault_code = 0;
208
209 /* OK this is were we do something about the instruction. */
210 LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link)
211 if (uh->uh_handler(fault_pc, fault_instruction, frame,
212 fault_code, fpexc) == 0)
213 break;
214
215 if (uh == NULL) {
216 /* Fault has not been handled */
217
218 #ifdef VERBOSE_ARM32
219 s = spltty();
220
221 if ((fault_instruction & 0x0f000010) == 0x0e000000) {
222 printf("CDP\n");
223 disassemble(fault_pc);
224 } else if ((fault_instruction & 0x0e000000) == 0x0c000000) {
225 printf("LDC/STC\n");
226 disassemble(fault_pc);
227 } else if ((fault_instruction & 0x0f000010) == 0x0e000010) {
228 printf("MRC/MCR\n");
229 disassemble(fault_pc);
230 } else if ((fault_instruction & ~INSN_COND_MASK)
231 != (KERNEL_BREAKPOINT & ~INSN_COND_MASK)) {
232 printf("Undefined instruction\n");
233 disassemble(fault_pc);
234 }
235
236 splx(s);
237 #endif
238
239 if ((fault_code & FAULT_USER) == 0) {
240 printf("Undefined instruction in kernel\n");
241 #ifdef DDB
242 db_enter();
243 #endif
244 }
245
246 sv.sival_int = frame->tf_pc;
247 trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
248 }
249
250 if ((fault_code & FAULT_USER) == 0)
251 return;
252
253 userret(p);
254 }
255