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