]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/hal/mips/arch/v2_0/src/mips-stub.c
Initial revision
[karo-tx-redboot.git] / packages / hal / mips / arch / v2_0 / src / mips-stub.c
1 //========================================================================
2 //
3 //      mips-stub.h
4 //
5 //      Helper functions for stub, generic to all MIPS processors
6 //
7 //========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):     Red Hat, nickg
44 // Contributors:  Red Hat, nickg
45 // Date:          1998-06-08
46 // Purpose:       
47 // Description:   Helper functions for stub, generic to all MIPS processors
48 // Usage:         
49 //
50 //####DESCRIPTIONEND####
51 //
52 //========================================================================
53
54 #include <stddef.h>
55
56 #include <pkgconf/hal.h>
57
58 #ifdef CYGPKG_REDBOOT
59 #include <pkgconf/redboot.h>
60 #endif
61
62 #ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
63
64 #include <cyg/hal/hal_stub.h>
65
66 #define CYGARC_HAL_COMMON_EXPORT_CPU_MACROS
67 #include <cyg/hal/mips-regs.h>
68
69 #include <cyg/hal/hal_arch.h>
70 #include <cyg/hal/hal_intr.h>
71 #include <cyg/hal/mips_opcode.h>
72
73 typedef unsigned long t_inst;
74
75 /*----------------------------------------------------------------------
76  * Asynchronous interrupt support
77  */
78
79 static struct
80 {
81   t_inst *targetAddr;
82   t_inst savedInstr;
83 } asyncBuffer;
84
85 /* Called to asynchronously interrupt a running program.
86    Must be passed address of instruction interrupted.
87    This is typically called in response to a debug port
88    receive interrupt.
89 */
90
91 void
92 install_async_breakpoint(void *epc)
93 {
94   long gp_save;
95
96   /* This may be called from a separately linked program,
97      so we need to save and restore the incoming gp register
98      and setup our stub local gp register. 
99      Alternatively, we could skip this foolishness if we
100      compiled libstub with "-G 0". */
101
102   __asm__ volatile ( "move   %0,$28\n"
103                      ".extern _gp\n"
104                      "la     $28,_gp\n"
105                      : "=r" (gp_save) );
106
107   asyncBuffer.targetAddr = epc;
108   asyncBuffer.savedInstr = *(t_inst *)epc;
109   *(t_inst *)epc = *(t_inst *)_breakinst;
110   __instruction_cache(CACHE_FLUSH);
111   __data_cache(CACHE_FLUSH);
112
113   __asm__  volatile ( "move   $28,%0\n" :: "r"(gp_save) );
114 }
115
116 /*--------------------------------------------------------------------*/
117 /* Given a trap value TRAP, return the corresponding signal. */
118
119 int __computeSignal (unsigned int trap_number)
120 {
121   switch (trap_number)
122     {
123     case EXC_INT:
124       /* External interrupt */
125       return SIGINT;
126
127     case EXC_RI:
128       /* Reserved instruction */
129     case EXC_CPU:
130       /* Coprocessor unusable */
131       return SIGILL;
132
133     case EXC_BP:
134       /* Break point */
135       if (asyncBuffer.targetAddr != NULL)
136         {
137           /* BP installed by serial driver to stop running program */
138           *asyncBuffer.targetAddr = asyncBuffer.savedInstr;
139           __instruction_cache(CACHE_FLUSH);
140           __data_cache(CACHE_FLUSH);
141           asyncBuffer.targetAddr = NULL;
142           return SIGINT;
143         }
144       return SIGTRAP;
145
146     case EXC_OVF:
147       /* Arithmetic overflow */
148     case EXC_TRAP:
149       /* Trap exception */
150     case EXC_FPE:
151       /* Floating Point Exception */
152       return SIGFPE;
153
154     case EXC_IBE:
155       /* Bus error (Ifetch) */
156     case EXC_DBE:
157       /* Bus error (data load or store) */
158       return SIGBUS;
159
160     case EXC_MOD:
161       /* TLB modification exception */
162     case EXC_TLBL:
163       /* TLB miss (Load or Ifetch) */
164     case EXC_TLBS:
165       /* TLB miss (Store) */
166     case EXC_ADEL:
167       /* Address error (Load or Ifetch) */
168     case EXC_ADES:
169       /* Address error (Store) */
170       return SIGSEGV;
171
172     case EXC_SYS:
173       /* System call */
174       return SIGSYS;
175
176     default:
177       return SIGTERM;
178     }
179 }
180
181 /* Return the trap number corresponding to the last-taken trap. */
182
183 int __get_trap_number (void)
184 {
185   return (get_register (CAUSE) & CAUSE_EXCMASK) >> CAUSE_EXCSHIFT;
186 }
187
188 #if defined(CYGSEM_REDBOOT_BSP_SYSCALLS)
189 int __is_bsp_syscall(void) 
190 {
191     return __get_trap_number() == EXC_SYS;
192 }
193 #endif
194
195 /* Set the currently-saved pc register value to PC. This also updates NPC
196    as needed. */
197
198 void set_pc (target_register_t pc)
199 {
200   put_register (PC, pc);
201 }
202
203
204 /*----------------------------------------------------------------------
205  * Single-step support
206  */
207
208 /* Saved instruction data for single step support.  */
209
210 static struct
211 {
212   t_inst *targetAddr;
213   t_inst savedInstr;
214 } instrBuffer;
215
216
217 /* Set things up so that the next user resume will execute one instruction.
218    This may be done by setting breakpoints or setting a single step flag
219    in the saved user registers, for example. */
220
221 void __single_step (void)
222 {
223   InstFmt inst;
224   t_inst *pc = (t_inst *) get_register (PC);
225
226   instrBuffer.targetAddr = pc + 1;              /* set default */
227
228   inst.word = *pc;                              /* read the next instruction  */
229
230 //diag_printf("pc %08x %08x\n",pc,inst.word);
231  
232   switch (inst.RType.op) {                    /* override default if branch */
233     case OP_SPECIAL:
234       switch (inst.RType.func) {
235         case OP_JR:
236         case OP_JALR:
237           instrBuffer.targetAddr = (t_inst *) get_register (inst.RType.rs);
238           break;
239       };
240       break;
241
242     case OP_REGIMM:
243       switch (inst.IType.rt) {
244         case OP_BLTZ:
245         case OP_BLTZL:
246         case OP_BLTZAL:
247         case OP_BLTZALL:
248             if ((int)get_register (inst.IType.rs) < 0 )
249             instrBuffer.targetAddr =
250               (t_inst *)(((signed short)inst.IType.imm<<2)
251                                         + (get_register (PC) + 4));
252           else
253             instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
254           break;
255         case OP_BGEZ:
256         case OP_BGEZL:
257         case OP_BGEZAL:
258         case OP_BGEZALL:
259             if ((int)get_register (inst.IType.rs) >= 0 )
260             instrBuffer.targetAddr =
261               (t_inst *)(((signed short)inst.IType.imm<<2)
262                                         + (get_register (PC) + 4));
263           else
264             instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
265           break;
266       };
267       break;
268
269     case OP_J:
270     case OP_JAL:
271       instrBuffer.targetAddr =
272         (t_inst *)((inst.JType.target<<2)
273                    + ((get_register (PC) + 4)&0xf0000000));
274       break;
275
276     case OP_BEQ:
277     case OP_BEQL:
278       if (get_register (inst.IType.rs) == get_register (inst.IType.rt))
279         instrBuffer.targetAddr =
280           (t_inst *)(((signed short)inst.IType.imm<<2)
281                      + (get_register (PC) + 4));
282       else
283         instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
284       break;
285     case OP_BNE:
286     case OP_BNEL:
287       if (get_register (inst.IType.rs) != get_register (inst.IType.rt))
288         instrBuffer.targetAddr =
289           (t_inst *)(((signed short)inst.IType.imm<<2)
290                      + (get_register (PC) + 4));
291       else
292         instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
293       break;
294     case OP_BLEZ:
295     case OP_BLEZL:
296         if ((int)get_register (inst.IType.rs) <= 0)
297         instrBuffer.targetAddr =
298           (t_inst *)(((signed short)inst.IType.imm<<2) + (get_register (PC) + 4));
299       else
300         instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
301       break;
302     case OP_BGTZ:
303     case OP_BGTZL:
304         if ((int)get_register (inst.IType.rs) > 0)
305         instrBuffer.targetAddr =
306           (t_inst *)(((signed short)inst.IType.imm<<2) + (get_register (PC) + 4));
307       else
308         instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
309       break;
310
311 #ifdef CYGHWR_HAL_MIPS_FPU
312
313     case OP_COP1:
314       if (inst.RType.rs == OP_BC)
315           switch (inst.RType.rt) {
316             case COPz_BCF:
317             case COPz_BCFL:
318                 if (get_register (FCR31) & FCR31_C)
319                 instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
320               else
321                 instrBuffer.targetAddr =
322                   (t_inst *)(((signed short)inst.IType.imm<<2)
323                                             + (get_register (PC) + 4));
324               break;
325             case COPz_BCT:
326             case COPz_BCTL:
327                 if (get_register (FCR31) & FCR31_C)
328                 instrBuffer.targetAddr =
329                   (t_inst *)(((signed short)inst.IType.imm<<2)
330                                             + (get_register (PC) + 4));
331               else
332                 instrBuffer.targetAddr = (t_inst *)(get_register (PC) + 8);
333               break;
334           };
335       break;
336 #endif
337
338   }
339 }
340
341
342 /* Clear the single-step state. */
343
344 void __clear_single_step (void)
345 {
346 //diag_printf("clear_ss ta %08x\n",instrBuffer.targetAddr);
347   if (instrBuffer.targetAddr != NULL)
348     {
349       *instrBuffer.targetAddr = instrBuffer.savedInstr;
350       instrBuffer.targetAddr = NULL;
351     }
352   instrBuffer.savedInstr = NOP_INSTR;
353 }
354
355
356 void __install_breakpoints ()
357 {
358 //diag_printf("install_bpt ta %08x\n",instrBuffer.targetAddr);
359   if (instrBuffer.targetAddr != NULL)
360     {
361       instrBuffer.savedInstr = *instrBuffer.targetAddr;
362       *instrBuffer.targetAddr = __break_opcode ();
363 //diag_printf("ta %08x si %08x *ta %08x\n",
364 //            instrBuffer.targetAddr,instrBuffer.savedInstr,*instrBuffer.targetAddr);
365
366       /* Ensure that the planted breakpoint makes it out to memory and
367          is subsequently loaded into the instruction cache.
368       */
369       __data_cache (CACHE_FLUSH) ;
370       __instruction_cache (CACHE_FLUSH) ;
371     }
372
373   /* Install the breakpoints in the breakpoint list */
374   __install_breakpoint_list();
375 }
376
377 void __clear_breakpoints (void)
378 {
379   __clear_breakpoint_list();
380 }
381
382
383 /* If the breakpoint we hit is in the breakpoint() instruction, return a
384    non-zero value. */
385
386 int
387 __is_breakpoint_function ()
388 {
389     return get_register (PC) == (target_register_t)(unsigned long)&_breakinst;
390 }
391
392
393 /* Skip the current instruction.  Since this is only called by the
394    stub when the PC points to a breakpoint or trap instruction,
395    we can safely just skip 4. */
396
397 void __skipinst (void)
398 {
399     put_register (PC, get_register (PC) + 4);
400 }
401
402 #endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS