* sparc-desc.c: New file.
[external/binutils.git] / sim / sparc / trap64.c
1 /* sparc64 trap support
2    Copyright (C) 1999 Cygnus Solutions.  */
3
4 #define WANT_CPU sparc64
5 #define WANT_CPU_SPARC64
6
7 #include "sim-main.h"
8 #include "targ-vals.h"
9
10 /* Indicate a window overflow has occured.  */
11
12 void
13 sparc64_window_overflow (SIM_CPU *cpu, IADDR pc)
14 {
15   SIM_DESC sd = CPU_STATE (cpu);
16
17   if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT)
18     sparc64_hw_trap (cpu, pc, TRAP32_WINDOW_OVERFLOW);
19   else
20     sparc64_hw_trap (cpu, pc, TRAP32_SIM_SPILL);
21 }
22
23 /* Indicate a window underflow has occured.  */
24
25 void
26 sparc64_window_underflow (SIM_CPU *cpu, IADDR pc)
27 {
28   SIM_DESC sd = CPU_STATE (cpu);
29
30   if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT)
31     sparc64_hw_trap (cpu, pc, TRAP32_WINDOW_UNDERFLOW);
32   else
33     sparc64_hw_trap (cpu, pc, TRAP32_SIM_FILL);
34 }
35
36 void
37 sparc64_invalid_insn (SIM_CPU * cpu, IADDR pc)
38 {
39   sparc64_hw_trap (cpu, pc, TRAP32_ILLEGAL_INSN);
40 }
41
42 void
43 sparc64_core_signal (SIM_DESC sd, SIM_CPU *cpu, sim_cia pc,
44                      unsigned int map, int nr_bytes, address_word addr,
45                      transfer_type transfer, sim_core_signals sig)
46 {
47   sparc64_hw_trap (cpu, pc,
48                    map == exec_map
49                    ? TRAP32_INSTRUCTION_ACCESS
50                    : TRAP32_DATA_ACCESS);
51 }
52 \f
53 /* Handle hardware generated traps when --environment=operating.  */
54
55 static void
56 sparc64_hw_trap_oper (SIM_CPU *current_cpu, IADDR pc, TRAP32_TYPE trap)
57 {
58   SIM_DESC sd = CPU_STATE (current_cpu);
59   IADDR new_pc = (GET_H_TBR () & 0xfffff000) | (trap << 4);
60   USI psr = GET_H_PSR ();
61
62   psr &= ~PSR_ET;
63   psr = (psr & ~PSR_PS) | (psr & PSR_S ? PSR_PS : 0);
64   psr |= PSR_S;
65   psr = (psr & ~PSR_CWP) | NEXT_WIN (psr & PSR_CWP);
66   SET_H_PSR (psr);
67   SET_H_GR (H_GR__L1, GET_H_PC ());
68   SET_H_GR (H_GR__L2, GET_H_NPC ());
69
70   SET_H_ANNUL_P (0);
71
72   /* The wrtbr insn doesn't affect the tt part so SET_H_TBR doesn't either
73      (??? doesn't *have* to be this way though).
74      Therefore we can't use SET_H_TBR here.  */
75   CPU (h_tbr) = new_pc;
76   SET_H_PC (new_pc);
77   SET_H_NPC (new_pc + 4);
78
79   sim_engine_restart (CPU_STATE (current_cpu), current_cpu, NULL, new_pc);
80 }
81
82 /* Handle hardware generated traps when --environment=user.  */
83
84 static void
85 sparc64_hw_trap_user (SIM_CPU *current_cpu, IADDR pc, TRAP32_TYPE trap)
86 {
87   SIM_DESC sd = CPU_STATE (current_cpu);
88
89   switch (trap)
90     {
91     case TRAP32_SIM_SPILL :
92       /* The CWP-1 window is invalid.  */
93       {
94         int win = NEXT_WIN (GET_H_CWP ());
95         int next_win = NEXT_WIN (win);
96         int nwindows = GET_NWINDOWS ();
97         unsigned int mask = (1 << nwindows) - 1;
98         unsigned int wim = GET_H_WIM ();
99
100         /* There's no need to flush `current_regs' here, `next_win' can't
101            refer to it.  */
102         sparc64_flush_regwin (current_cpu, pc, next_win, 0 /* error ok (?) */);
103         /* Rotate WIM right one.  */
104         wim = ((wim & mask) >> 1) | (wim << (nwindows - 1));
105         SET_H_WIM (wim & mask);
106         return;
107       }
108
109     case TRAP32_SIM_FILL :
110       /* The CWP+1 window is invalid.  */
111       {
112         int win = PREV_WIN (GET_H_CWP ());
113         int nwindows = GET_NWINDOWS ();
114         unsigned int mask = (1 << nwindows) - 1;
115         unsigned int wim = GET_H_WIM ();
116
117         /* Load caller's caller's window.
118            There's no need to flush `current_regs' as `win' can't
119            refer to it.  */
120         sparc64_load_regwin (current_cpu, pc, win);
121         /* Rotate WIM left one.  */
122         wim = (wim << 1) | ((wim & mask) >> (nwindows - 1));
123         SET_H_WIM (wim & mask);
124         return;
125       }
126     }
127
128   sim_io_eprintf (sd, "Received trap %d\n", trap);
129   sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGABRT);
130 }
131
132 /* Handle hardware generated traps.  */
133
134 void
135 sparc64_hw_trap (SIM_CPU *current_cpu, IADDR pc, TRAP32_TYPE trap)
136 {
137   SIM_DESC sd = CPU_STATE (current_cpu);
138
139   if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT)
140     sparc64_hw_trap_oper (current_cpu, pc, trap);
141   else
142     sparc64_hw_trap_user (current_cpu, pc, trap);
143 }
144 \f
145 /* Handle the trap insn when --environment=operating.  */
146
147 static IADDR
148 sparc64_sw_trap_oper (SIM_CPU *current_cpu, IADDR pc, SI rs1, SI rs2_simm13)
149 {
150   SIM_DESC sd = CPU_STATE (current_cpu);
151   int trap = 128 + ((rs1 + rs2_simm13) & 127);
152   IADDR new_pc;
153
154   /* ??? Quick hack to have breakpoints work with gdb+"target sim" until
155      other things are working.  */
156   if (trap == TRAP32_BREAKPOINT)
157     sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
158
159   if (! GET_H_ET ())
160     {
161       /* Enter error mode.
162          ??? wip, need to remain compatible with erc32 for now.  */
163       int i0 = GET_H_GR (H_GR__I0);
164       int i1 = GET_H_GR (H_GR__I1);
165
166       if (i1 == LIBGLOSS_EXIT_MAGIC)
167         sim_engine_halt (sd, current_cpu, NULL, pc, sim_exited, i0);
168       else
169         {
170           sim_io_eprintf (sd, "Unexpected program termination, pc=0x%x\n",
171                           (int) pc);
172           sim_engine_halt (sd, current_cpu, NULL, pc,
173                            sim_signalled, SIM_SIGABRT);
174         }
175     }
176
177   SET_H_ET (0);
178   SET_H_PSR ((GET_H_PSR () & ~(PSR_CWP | PSR_PS))
179              | PSR_S
180              | (GET_H_S () ? PSR_PS : 0)
181              | (NEXT_WIN (GET_H_CWP ())));
182   SET_H_GR (H_GR__L1, GET_H_PC ());
183   SET_H_GR (H_GR__L2, GET_H_NPC ());
184   /* The wrtbr insn doesn't affect the tt part so SET_H_TBR doesn't either
185      (??? doesn't *have* to be this way though).
186      Therefore we can't use SET_H_TBR here.  */
187   CPU (h_tbr) = new_pc = ((GET_H_TBR () & 0xfffff000)
188                           | (trap << 4));
189   return new_pc;
190 }
191 \f
192 /* Subroutine of sparc64_do_trap to read target memory.  */
193
194 static int
195 syscall_read_mem (host_callback *cb, CB_SYSCALL *sc,
196                   unsigned long taddr, char *buf, int bytes)
197 {
198   SIM_DESC sd = (SIM_DESC) sc->p1;
199   SIM_CPU *cpu = (SIM_CPU *) sc->p2;
200
201   return sim_core_read_buffer (sd, cpu, read_map, buf, taddr, bytes);
202 }
203
204 /* Subroutine of sparc64_do_trap to write target memory.  */
205
206 static int
207 syscall_write_mem (host_callback *cb, CB_SYSCALL *sc,
208                    unsigned long taddr, const char *buf, int bytes)
209 {
210   SIM_DESC sd = (SIM_DESC) sc->p1;
211   SIM_CPU *cpu = (SIM_CPU *) sc->p2;
212
213   return sim_core_write_buffer (sd, cpu, write_map, buf, taddr, bytes);
214 }
215
216 /* Handle the trap insn when --environment=user.  */
217
218 static IADDR
219 sparc64_sw_trap_user (SIM_CPU *current_cpu, IADDR pc, SI rs1, SI rs2_simm13)
220 {
221   SIM_DESC sd = CPU_STATE (current_cpu);
222   int trap = 128 + ((rs1 + rs2_simm13) & 127);
223   IADDR new_pc = pc + 4;
224
225   switch (trap)
226     {
227     case TRAP32_SYSCALL :
228       /* FIXME: Later make trap number runtime selectable.  */
229       {
230         CB_SYSCALL s;
231
232         CB_SYSCALL_INIT (&s);
233         s.func = a_sparc_h_gr_get (current_cpu, 8);
234         s.arg1 = a_sparc_h_gr_get (current_cpu, 9);
235         s.arg2 = a_sparc_h_gr_get (current_cpu, 10);
236         s.arg3 = a_sparc_h_gr_get (current_cpu, 11);
237         if (s.func == TARGET_SYS_exit)
238           {
239             /* Tell sim_resume program called exit().  */
240             sim_engine_halt (sd, current_cpu, NULL, pc, sim_exited, s.arg1);
241           }
242         s.p1 = (PTR) sd;
243         s.p2 = (PTR) current_cpu;
244         s.read_mem = syscall_read_mem;
245         s.write_mem = syscall_write_mem;
246         cb_syscall (STATE_CALLBACK (CPU_STATE (current_cpu)), &s);
247         a_sparc_h_gr_set (current_cpu, 10, s.errcode);
248         a_sparc_h_gr_set (current_cpu, 8, s.result);
249         a_sparc_h_gr_set (current_cpu, 9, s.result2);
250         break;
251       }
252
253     case TRAP32_BREAKPOINT :
254       sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
255
256     case TRAP32_DIVIDE_0 :
257       sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGFPE);
258
259     case TRAP32_FLUSH_REGWIN :
260       sparc64_flush_regwins (current_cpu, pc, 0 /* error ok */);
261       break;
262
263     default :
264       sim_io_eprintf (sd, "Unsupported trap %d\n", trap);
265       sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGILL);
266     }
267
268   return new_pc;
269 }
270
271 /* Called from the semantic code to handle the trap instruction.  */
272
273 IADDR
274 sparc64_sw_trap (SIM_CPU *current_cpu, IADDR pc, SI rs1, SI rs2_simm13)
275 {
276   SIM_DESC sd = CPU_STATE (current_cpu);
277   IADDR new_pc;
278
279   if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT)
280     new_pc = sparc64_sw_trap_oper (current_cpu, pc, rs1, rs2_simm13);
281   else
282     new_pc = sparc64_sw_trap_user (current_cpu, pc, rs1, rs2_simm13);
283   return new_pc;
284 }
285 \f
286 /* Handle the rett insn.  */
287
288 IADDR
289 sparc64_do_rett (SIM_CPU *current_cpu, IADDR pc, SI rs1, SI rs2_simm13)
290 {
291   int psr = GET_H_PSR ();
292
293   /* FIXME: check for trap conditions.  */
294
295   SET_H_PSR ((psr & ~(PSR_S + PSR_CWP))
296              | ((psr & PSR_PS) ? PSR_S : 0)
297              | PSR_ET
298              | PREV_WIN (psr & PSR_CWP));
299
300   if (TRACE_INSN_P (current_cpu)) /* FIXME */
301     {
302       trace_result (current_cpu, "sp", 'x', GET_H_GR (H_GR__SP));
303       trace_result (current_cpu, "fp", 'x', GET_H_GR (H_GR__FP));
304       trace_result (current_cpu, "cwp", 'x', GET_H_CWP ());
305     }
306
307   return rs1 + rs2_simm13;
308 }