traps.c revision 1.1 1 1.1 christos /* OpenRISC exception, interrupts, syscall and trap support
2 1.1 christos Copyright (C) 2017-2019 Free Software Foundation, Inc.
3 1.1 christos
4 1.1 christos This file is part of GDB, the GNU debugger.
5 1.1 christos
6 1.1 christos This program is free software; you can redistribute it and/or modify
7 1.1 christos it under the terms of the GNU General Public License as published by
8 1.1 christos the Free Software Foundation; either version 3 of the License, or
9 1.1 christos (at your option) any later version.
10 1.1 christos
11 1.1 christos This program is distributed in the hope that it will be useful,
12 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
13 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 1.1 christos GNU General Public License for more details.
15 1.1 christos
16 1.1 christos You should have received a copy of the GNU General Public License
17 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 1.1 christos
19 1.1 christos #define WANT_CPU_OR1K32BF
20 1.1 christos #define WANT_CPU
21 1.1 christos
22 1.1 christos #include "sim-main.h"
23 1.1 christos #include "cgen-ops.h"
24 1.1 christos
25 1.1 christos /* Implement the sim invalid instruction function. This will set the error
26 1.1 christos effective address to that of the invalid instruction then call the
27 1.1 christos exception handler. */
28 1.1 christos
29 1.1 christos SEM_PC
30 1.1 christos sim_engine_invalid_insn (SIM_CPU *current_cpu, IADDR cia, SEM_PC vpc)
31 1.1 christos {
32 1.1 christos SET_H_SYS_EEAR0 (cia);
33 1.1 christos
34 1.1 christos #ifdef WANT_CPU_OR1K32BF
35 1.1 christos or1k32bf_exception (current_cpu, cia, EXCEPT_ILLEGAL);
36 1.1 christos #endif
37 1.1 christos
38 1.1 christos return vpc;
39 1.1 christos }
40 1.1 christos
41 1.1 christos /* Generate the appropriate OpenRISC fpu exception based on the status code from
42 1.1 christos the sim fpu. */
43 1.1 christos void
44 1.1 christos or1k32bf_fpu_error (CGEN_FPU* fpu, int status)
45 1.1 christos {
46 1.1 christos SIM_CPU *current_cpu = (SIM_CPU *)fpu->owner;
47 1.1 christos
48 1.1 christos /* If floating point exceptions are enabled. */
49 1.1 christos if (GET_H_SYS_FPCSR_FPEE() != 0)
50 1.1 christos {
51 1.1 christos /* Set all of the status flag bits. */
52 1.1 christos if (status
53 1.1 christos & (sim_fpu_status_invalid_snan
54 1.1 christos | sim_fpu_status_invalid_qnan
55 1.1 christos | sim_fpu_status_invalid_isi
56 1.1 christos | sim_fpu_status_invalid_idi
57 1.1 christos | sim_fpu_status_invalid_zdz
58 1.1 christos | sim_fpu_status_invalid_imz
59 1.1 christos | sim_fpu_status_invalid_cvi
60 1.1 christos | sim_fpu_status_invalid_cmp
61 1.1 christos | sim_fpu_status_invalid_sqrt))
62 1.1 christos SET_H_SYS_FPCSR_IVF (1);
63 1.1 christos
64 1.1 christos if (status & sim_fpu_status_invalid_snan)
65 1.1 christos SET_H_SYS_FPCSR_SNF (1);
66 1.1 christos
67 1.1 christos if (status & sim_fpu_status_invalid_qnan)
68 1.1 christos SET_H_SYS_FPCSR_QNF (1);
69 1.1 christos
70 1.1 christos if (status & sim_fpu_status_overflow)
71 1.1 christos SET_H_SYS_FPCSR_OVF (1);
72 1.1 christos
73 1.1 christos if (status & sim_fpu_status_underflow)
74 1.1 christos SET_H_SYS_FPCSR_UNF (1);
75 1.1 christos
76 1.1 christos if (status
77 1.1 christos & (sim_fpu_status_invalid_isi
78 1.1 christos | sim_fpu_status_invalid_idi))
79 1.1 christos SET_H_SYS_FPCSR_INF (1);
80 1.1 christos
81 1.1 christos if (status & sim_fpu_status_invalid_div0)
82 1.1 christos SET_H_SYS_FPCSR_DZF (1);
83 1.1 christos
84 1.1 christos if (status & sim_fpu_status_inexact)
85 1.1 christos SET_H_SYS_FPCSR_IXF (1);
86 1.1 christos
87 1.1 christos /* If any of the exception bits were actually set. */
88 1.1 christos if (GET_H_SYS_FPCSR()
89 1.1 christos & (SPR_FIELD_MASK_SYS_FPCSR_IVF
90 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_SNF
91 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_QNF
92 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_OVF
93 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_UNF
94 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_INF
95 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_DZF
96 1.1 christos | SPR_FIELD_MASK_SYS_FPCSR_IXF))
97 1.1 christos {
98 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu);
99 1.1 christos
100 1.1 christos /* If the sim is running in fast mode, i.e. not profiling,
101 1.1 christos per-instruction callbacks are not triggered which would allow
102 1.1 christos us to track the PC. This means we cannot track which
103 1.1 christos instruction caused the FPU error. */
104 1.1 christos if (STATE_RUN_FAST_P (sd) == 1)
105 1.1 christos sim_io_eprintf
106 1.1 christos (sd, "WARNING: ignoring fpu error caught in fast mode.\n");
107 1.1 christos else
108 1.1 christos or1k32bf_exception (current_cpu, GET_H_SYS_PPC (), EXCEPT_FPE);
109 1.1 christos }
110 1.1 christos }
111 1.1 christos }
112 1.1 christos
113 1.1 christos
114 1.1 christos /* Implement the OpenRISC exception function. This is mostly used by the
115 1.1 christos CGEN generated files. For example, this is used when handling a
116 1.1 christos overflow exception during a multiplication instruction. */
117 1.1 christos
118 1.1 christos void
119 1.1 christos or1k32bf_exception (sim_cpu *current_cpu, USI pc, USI exnum)
120 1.1 christos {
121 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu);
122 1.1 christos
123 1.1 christos if (exnum == EXCEPT_TRAP)
124 1.1 christos {
125 1.1 christos /* Trap, used for breakpoints, sends control back to gdb breakpoint
126 1.1 christos handling. */
127 1.1 christos sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
128 1.1 christos }
129 1.1 christos else
130 1.1 christos {
131 1.1 christos
132 1.1 christos /* Calculate the exception program counter. */
133 1.1 christos switch (exnum)
134 1.1 christos {
135 1.1 christos case EXCEPT_RESET:
136 1.1 christos break;
137 1.1 christos
138 1.1 christos case EXCEPT_FPE:
139 1.1 christos case EXCEPT_SYSCALL:
140 1.1 christos SET_H_SYS_EPCR0 (pc + 4 - (current_cpu->delay_slot ? 4 : 0));
141 1.1 christos break;
142 1.1 christos
143 1.1 christos case EXCEPT_BUSERR:
144 1.1 christos case EXCEPT_ALIGN:
145 1.1 christos case EXCEPT_ILLEGAL:
146 1.1 christos case EXCEPT_RANGE:
147 1.1 christos SET_H_SYS_EPCR0 (pc - (current_cpu->delay_slot ? 4 : 0));
148 1.1 christos break;
149 1.1 christos
150 1.1 christos default:
151 1.1 christos sim_io_error (sd, "unexpected exception 0x%x raised at PC 0x%08x",
152 1.1 christos exnum, pc);
153 1.1 christos break;
154 1.1 christos }
155 1.1 christos
156 1.1 christos /* Store the current SR into ESR0. */
157 1.1 christos SET_H_SYS_ESR0 (GET_H_SYS_SR ());
158 1.1 christos
159 1.1 christos /* Indicate in SR if the failed instruction is in delay slot or not. */
160 1.1 christos SET_H_SYS_SR_DSX (current_cpu->delay_slot);
161 1.1 christos
162 1.1 christos current_cpu->next_delay_slot = 0;
163 1.1 christos
164 1.1 christos /* Jump program counter into handler. */
165 1.1 christos IADDR handler_pc =
166 1.1 christos (GET_H_SYS_SR_EPH ()? 0xf0000000 : 0x00000000) + (exnum << 8);
167 1.1 christos
168 1.1 christos sim_engine_restart (sd, current_cpu, NULL, handler_pc);
169 1.1 christos }
170 1.1 christos }
171 1.1 christos
172 1.1 christos /* Implement the return from exception instruction. This is used to return
173 1.1 christos the CPU to its previous state from within an exception handler. */
174 1.1 christos
175 1.1 christos void
176 1.1 christos or1k32bf_rfe (sim_cpu *current_cpu)
177 1.1 christos {
178 1.1 christos SET_H_SYS_SR (GET_H_SYS_ESR0 ());
179 1.1 christos SET_H_SYS_SR_FO (1);
180 1.1 christos
181 1.1 christos current_cpu->next_delay_slot = 0;
182 1.1 christos
183 1.1 christos sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL,
184 1.1 christos GET_H_SYS_EPCR0 ());
185 1.1 christos }
186 1.1 christos
187 1.1 christos /* Implement the move from SPR instruction. This is used to read from the
188 1.1 christos CPU's special purpose registers. */
189 1.1 christos
190 1.1 christos USI
191 1.1 christos or1k32bf_mfspr (sim_cpu *current_cpu, USI addr)
192 1.1 christos {
193 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu);
194 1.1 christos
195 1.1 christos if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ())
196 1.1 christos {
197 1.1 christos sim_io_eprintf (sd, "WARNING: l.mfspr in user mode (SR 0x%x)\n",
198 1.1 christos GET_H_SYS_SR ());
199 1.1 christos return 0;
200 1.1 christos }
201 1.1 christos
202 1.1 christos if (addr >= NUM_SPR)
203 1.1 christos goto bad_address;
204 1.1 christos
205 1.1 christos SI val = GET_H_SPR (addr);
206 1.1 christos
207 1.1 christos switch (addr)
208 1.1 christos {
209 1.1 christos
210 1.1 christos case SPR_ADDR (SYS, VR):
211 1.1 christos case SPR_ADDR (SYS, UPR):
212 1.1 christos case SPR_ADDR (SYS, CPUCFGR):
213 1.1 christos case SPR_ADDR (SYS, SR):
214 1.1 christos case SPR_ADDR (SYS, PPC):
215 1.1 christos case SPR_ADDR (SYS, FPCSR):
216 1.1 christos case SPR_ADDR (SYS, EPCR0):
217 1.1 christos case SPR_ADDR (MAC, MACLO):
218 1.1 christos case SPR_ADDR (MAC, MACHI):
219 1.1 christos break;
220 1.1 christos
221 1.1 christos default:
222 1.1 christos if (addr < SPR_ADDR (SYS, GPR0) || addr > SPR_ADDR (SYS, GPR511))
223 1.1 christos goto bad_address;
224 1.1 christos break;
225 1.1 christos
226 1.1 christos }
227 1.1 christos
228 1.1 christos return val;
229 1.1 christos
230 1.1 christos bad_address:
231 1.1 christos sim_io_eprintf (sd, "WARNING: l.mfspr with invalid SPR address 0x%x\n", addr);
232 1.1 christos return 0;
233 1.1 christos
234 1.1 christos }
235 1.1 christos
236 1.1 christos /* Implement the move to SPR instruction. This is used to write too the
237 1.1 christos CPU's special purpose registers. */
238 1.1 christos
239 1.1 christos void
240 1.1 christos or1k32bf_mtspr (sim_cpu *current_cpu, USI addr, USI val)
241 1.1 christos {
242 1.1 christos SIM_DESC sd = CPU_STATE (current_cpu);
243 1.1 christos
244 1.1 christos if (!GET_H_SYS_SR_SM () && !GET_H_SYS_SR_SUMRA ())
245 1.1 christos {
246 1.1 christos sim_io_eprintf
247 1.1 christos (sd, "WARNING: l.mtspr with address 0x%x in user mode (SR 0x%x)\n",
248 1.1 christos addr, GET_H_SYS_SR ());
249 1.1 christos return;
250 1.1 christos }
251 1.1 christos
252 1.1 christos if (addr >= NUM_SPR)
253 1.1 christos goto bad_address;
254 1.1 christos
255 1.1 christos switch (addr)
256 1.1 christos {
257 1.1 christos
258 1.1 christos case SPR_ADDR (SYS, FPCSR):
259 1.1 christos case SPR_ADDR (SYS, EPCR0):
260 1.1 christos case SPR_ADDR (SYS, ESR0):
261 1.1 christos case SPR_ADDR (MAC, MACHI):
262 1.1 christos case SPR_ADDR (MAC, MACLO):
263 1.1 christos SET_H_SPR (addr, val);
264 1.1 christos break;
265 1.1 christos
266 1.1 christos case SPR_ADDR (SYS, SR):
267 1.1 christos SET_H_SPR (addr, val);
268 1.1 christos SET_H_SYS_SR_FO (1);
269 1.1 christos break;
270 1.1 christos
271 1.1 christos case SPR_ADDR (SYS, NPC):
272 1.1 christos current_cpu->next_delay_slot = 0;
273 1.1 christos
274 1.1 christos sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL, val);
275 1.1 christos break;
276 1.1 christos
277 1.1 christos case SPR_ADDR (TICK, TTMR):
278 1.1 christos /* Allow some registers to be silently cleared. */
279 1.1 christos if (val != 0)
280 1.1 christos sim_io_eprintf
281 1.1 christos (sd, "WARNING: l.mtspr to SPR address 0x%x with invalid value 0x%x\n",
282 1.1 christos addr, val);
283 1.1 christos break;
284 1.1 christos
285 1.1 christos default:
286 1.1 christos if (addr >= SPR_ADDR (SYS, GPR0) && addr <= SPR_ADDR (SYS, GPR511))
287 1.1 christos SET_H_SPR (addr, val);
288 1.1 christos else
289 1.1 christos goto bad_address;
290 1.1 christos break;
291 1.1 christos
292 1.1 christos }
293 1.1 christos
294 1.1 christos return;
295 1.1 christos
296 1.1 christos bad_address:
297 1.1 christos sim_io_eprintf (sd, "WARNING: l.mtspr with invalid SPR address 0x%x\n", addr);
298 1.1 christos
299 1.1 christos }
300