summaryrefslogtreecommitdiff
path: root/ecos/packages/hal/powerpc/arch/current/src/ppc_stub.c
blob: 76f91371466c69597b382e2112cba6b7b23b7064 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
//========================================================================
//
//      ppc_stub.c
//
//      Helper functions for stub, generic to all PowerPC processors
//
//========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2007 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     Red Hat, jskov
// Contributors:  Red Hat, jskov, gthomas
// Date:          1998-08-20
// Purpose:       
// Description:   Helper functions for stub, generic to all PowerPC processors
// Usage:         
//
//####DESCRIPTIONEND####
//
//========================================================================

#include <stddef.h>

#include <pkgconf/hal.h>

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS

#define CYGARC_HAL_COMMON_EXPORT_CPU_MACROS
#include <cyg/hal/ppc_regs.h>

#include <cyg/hal/hal_stub.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>

#ifdef CYGNUM_HAL_NO_VECTOR_TRACE
#define USE_BREAKPOINTS_FOR_SINGLE_STEP
#endif

#ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT
#include <cyg/hal/dbg-threads-api.h>    // dbg_currthread_id
#endif

#ifndef OFFSETOF
#define OFFSETOF(_struct_, _member_) (int)((char *)(&(((_struct_*)0)->_member_))-(char *)((_struct_*)0))
#endif

/* Given a trap value TRAP, return the corresponding signal. */

int __computeSignal (unsigned int trap_number)
{
    switch (trap_number)
    {
    case CYGNUM_HAL_VECTOR_MACHINE_CHECK:
        /* Machine check */
    case CYGNUM_HAL_VECTOR_DSI:
        /* Data access */
        return SIGSEGV;
      
    case CYGNUM_HAL_VECTOR_ISI:
        /* Instruction access (Ifetch) */
    case CYGNUM_HAL_VECTOR_ALIGNMENT:
        /* Data access */
        return SIGBUS;
                    
    case CYGNUM_HAL_VECTOR_INTERRUPT:
        /* External interrupt */
      return SIGINT;

    case CYGNUM_HAL_VECTOR_TRACE:
        /* Instruction trace */
        return SIGTRAP;
      
    case CYGNUM_HAL_VECTOR_PROGRAM:
#ifdef CYGPKG_HAL_POWERPC_PPC40x
        // The 40x is b0rken, returning 0 for these bits. Translate to
        // SIGTRAP to allow thread debugging.
        return SIGTRAP;
#elif defined(CYGHWR_HAL_POWERPC_BOOK_E)
        // for Book E processor, we just translate all PROGRAM
        // exceptions into SIGTRAP so that breakpoints work.
        return SIGTRAP;
#else
        // The register PS contains the value of SRR1 at the time of
        // exception entry. Bits 11-15 contain information about the
        // cause of the exception. Bits 16-31 the PS (MSR) state.
#ifdef USE_BREAKPOINTS_FOR_SINGLE_STEP
        if (__is_single_step(get_register(PC))) {
            return SIGTRAP;
        }
#endif
        switch ((get_register (PS) >> 17) & 0xf){
        case 1:                         /* trap */
            return SIGTRAP;
        case 2:                         /* privileged instruction */
        case 4:                         /* illegal instruction */
            return SIGILL;
        case 8:                         /* floating point */
            return SIGFPE;
        default:                        /* should never happen! */
            return SIGILL;
        }            
#endif

    case CYGNUM_HAL_VECTOR_RESERVED_A:
    case CYGNUM_HAL_VECTOR_RESERVED_B:
        return SIGILL;

    case CYGNUM_HAL_VECTOR_FP_UNAVAILABLE:
        /* FPU disabled */
    case CYGNUM_HAL_VECTOR_FP_ASSIST:
        /* FPU assist */
        return SIGFPE;

    case CYGNUM_HAL_VECTOR_DECREMENTER:
        /* Decrementer alarm */
        return SIGALRM;

    case CYGNUM_HAL_VECTOR_SYSTEM_CALL:
        /* System call */
        return SIGSYS;

#if defined(CYGPKG_HAL_POWERPC_MPC8xx) || defined(CYGPKG_HAL_POWERPC_MPC5xx)
    case CYGNUM_HAL_VECTOR_SW_EMUL:
        /* A SW_EMUL is generated instead of PROGRAM for illegal
           instructions. */
        return SIGILL;

    case CYGNUM_HAL_VECTOR_DATA_BP:
    case CYGNUM_HAL_VECTOR_INSTRUCTION_BP:
    case CYGNUM_HAL_VECTOR_PERIPHERAL_BP:
    case CYGNUM_HAL_VECTOR_NMI:
        /* Developer port debugging exceptions. */
        return SIGTRAP;

#if defined(CYGNUM_HAL_VECTOR_ITLB_MISS)	
    case CYGNUM_HAL_VECTOR_ITLB_MISS:
        /* Software reload of TLB required. */
        return SIGTRAP;
#endif
#if defined(CYGNUM_HAL_VECTOR_DTLB_MISS)	
    case CYGNUM_HAL_VECTOR_DTLB_MISS:
        /* Software reload of TLB required. */
        return SIGTRAP;
#endif
    case CYGNUM_HAL_VECTOR_ITLB_ERROR:
        /* Invalid instruction access. */
        return SIGBUS;

    case CYGNUM_HAL_VECTOR_DTLB_ERROR:
        /* Invalid data access. */
        return SIGSEGV;
#endif // defined(CYGPKG_HAL_POWERPC_MPC8xx)
        
    default:
        return SIGTERM;
    }
}


/* Return the trap number corresponding to the last-taken trap. */

int __get_trap_number (void)
{
    // The vector is not not part of the GDB register set so get it
    // directly from the save context.
    return _hal_registers->vector >> CYGHWR_HAL_POWERPC_VECTOR_ALIGNMENT;
}

/* Set the currently-saved pc register value to PC. This also updates NPC
   as needed. */

void set_pc (target_register_t pc)
{
    put_register (PC, pc);
}

#ifdef CYGHWR_HAL_POWERPC_FPU
static int
reg_offset(regnames_t reg)
{
  // We let the compiler determine the offsets in order to avoid all
  // possible alignment problems
  int base_offset;
  // 32 general purpose registers
  if(reg < F0)   return reg * 4;

  // first sixteen floating point regs
  base_offset = OFFSETOF(GDB_Registers, f0);
  if(reg < F16)  return base_offset + ((reg - F0) * 8);

  // last sixteen floating point regs
  base_offset = OFFSETOF(GDB_Registers, f16);
  if(reg < PC) 	 return base_offset + ((reg - F16) * 8);

  // Other 32 bit regs
  if(reg < PS)   return(OFFSETOF(GDB_Registers, pc));
  if(reg < CND)  return(OFFSETOF(GDB_Registers, msr));
  if(reg < LR)   return(OFFSETOF(GDB_Registers, cr));
  if(reg < CNT)  return(OFFSETOF(GDB_Registers, lr));
  if(reg < XER)  return(OFFSETOF(GDB_Registers, ctr));
  if(reg < MQ)   return(OFFSETOF(GDB_Registers, xer));
  
  return OFFSETOF(GDB_Registers, mq);
}

// Return the currently-saved value corresponding to register REG of
// the exception context.
target_register_t
get_register (regnames_t reg)
{
   target_register_t val;
   int offset = reg_offset(reg);

   if (REGSIZE(reg) > sizeof(target_register_t))
   return -1;

   val = _registers[offset/sizeof(target_register_t)];

   return val;
}

// Store VALUE in the register corresponding to WHICH in the exception
// context.
void
put_register (regnames_t which, target_register_t value)
{
   int offset = reg_offset(which);

   if (REGSIZE(which) > sizeof(target_register_t))
   return;

   _registers[offset/sizeof(target_register_t)] = value;
}

// Write the contents of register WHICH into VALUE as raw bytes. This
// is only used for registers larger than sizeof(target_register_t).
// Return non-zero if it is a valid register.
int
get_register_as_bytes (regnames_t which, char *value)
{
  int offset = reg_offset(which);

  memcpy (value, (char *)_registers + offset, REGSIZE(which));
  return 1;
}

// Alter the contents of saved register WHICH to contain VALUE. This
// is only used for registers larger than sizeof(target_register_t).
// Return non-zero if it is a valid register.
int
put_register_as_bytes (regnames_t which, char *value)
{
  int offset = reg_offset(which);

  memcpy ((char *)_registers + offset, value, REGSIZE(which));
  return 1;
}
#endif

/*----------------------------------------------------------------------
 * Single-step support
 */

/* Set things up so that the next user resume will execute one instruction.
   This may be done by setting breakpoints or setting a single step flag
   in the saved user registers, for example. */

#ifdef USE_BREAKPOINTS_FOR_SINGLE_STEP

#if (HAL_BREAKINST_SIZE == 1)
typedef cyg_uint8 t_inst;
#elif (HAL_BREAKINST_SIZE == 2)
typedef cyg_uint16 t_inst;
#elif (HAL_BREAKINST_SIZE == 4)
typedef cyg_uint32 t_inst;
#else
#error "Don't know how to handle that size"
#endif

typedef struct
{
  t_inst *targetAddr;
  t_inst savedInstr;
} instrBuffer;

static instrBuffer sstep_instr[2];
static target_register_t irq_state = 0;

static void 
__insert_break(int indx, target_register_t pc)
{
    sstep_instr[indx].targetAddr = (t_inst *)pc;
    sstep_instr[indx].savedInstr = *(t_inst *)pc;
    *(t_inst*)pc = (t_inst)HAL_BREAKINST;
    __data_cache(CACHE_FLUSH);
    __instruction_cache(CACHE_FLUSH);
}

static void 
__remove_break(int indx)
{
    if (sstep_instr[indx].targetAddr != 0) {
        *(sstep_instr[indx].targetAddr) = sstep_instr[indx].savedInstr;
        sstep_instr[indx].targetAddr = 0;
        __data_cache(CACHE_FLUSH);
        __instruction_cache(CACHE_FLUSH);
    }
}

int
__is_single_step(target_register_t pc)
{
    return (sstep_instr[0].targetAddr == (t_inst *)pc) ||
        (sstep_instr[1].targetAddr == (t_inst *)pc);
}


// Compute the target address for this instruction, if the instruction
// is some sort of branch/flow change.

struct xl_form {
    unsigned int op : 6;
    unsigned int bo : 5;
    unsigned int bi : 5;
    unsigned int reserved : 5;
    unsigned int xo : 10;
    unsigned int lk : 1;
};

struct i_form {
    unsigned int op : 6;
    signed   int li : 24;
    unsigned int aa : 1;
    unsigned int lk : 1;
};

struct b_form {
    unsigned int op : 6;
    unsigned int bo : 5;
    unsigned int bi : 5;
    signed   int bd : 14;
    unsigned int aa : 1;
    unsigned int lk : 1;
};

union ppc_insn {
    unsigned int   word;
    struct i_form  i;
    struct b_form  b;
    struct xl_form xl;
};

static target_register_t
__branch_pc(target_register_t pc)
{
    union ppc_insn insn;

    insn.word = *(t_inst *)pc;

    // Decode the instruction to determine the instruction which will follow
    // Note: there are holes in this process, but the important ones work
    switch (insn.i.op) {
    case 16:
	/* bcx */
	if (insn.b.aa) {
	    return (target_register_t)(insn.b.bd << 2);
        } else {
	    return (target_register_t)((insn.b.bd << 2) + (long)pc);
        }
    case 18:
	/* bx */
	if (insn.i.aa) {
	    return (target_register_t)(insn.i.li << 2);
        } else {
	    return (target_register_t)((insn.i.li << 2) + (long)pc);
        }
    case 19:
	if (insn.xl.reserved == 0) {
	    if (insn.xl.xo == 528) {
		/* bcctrx */
                return (target_register_t)(get_register(CNT) & ~3);
	    } else if (insn.xl.xo == 16) {
		/* bclrx */
                return (target_register_t)(get_register(LR) & ~3);
	    }
	}
	break;
    default:
	break;
    }
    return (pc+4);
}

void __single_step(void)
{
    target_register_t msr = get_register(PS);
    target_register_t pc = get_register(PC);
    target_register_t next_pc = __branch_pc(pc);

    // Disable interrupts.
    irq_state = msr & MSR_EE;
    msr &= ~MSR_EE;
    put_register (PS, msr);

    // Set a breakpoint at the next instruction
    __insert_break(0, pc+4);
    if (next_pc != (pc+4)) {
        __insert_break(1, next_pc);
    }
}

/* Clear the single-step state. */

void __clear_single_step(void)
{
    target_register_t msr = get_register (PS);

    // Restore interrupt state.
    // FIXME: Should check whether the executed instruction changed the
    // interrupt state - or single-stepping a MSR changing instruction
    // may result in a wrong EE. Not a very likely scenario though.
    msr |= irq_state;

    // This function is called much more than its counterpart
    // __single_step.  Only re-enable interrupts if they where
    // disabled during the previous cal to __single_step. Otherwise,
    // this function only makes "extra sure" that no trace or branch
    // exception will happen.
    irq_state = 0;

    put_register (PS, msr);

    // Remove breakpoints
    __remove_break(0);
    __remove_break(1);
}

#else

static target_register_t irq_state = 0;

void __single_step (void)
{
    target_register_t msr = get_register (PS);

    // Set single-step flag in the exception context.
    msr |= (MSR_SE | MSR_BE);
    // Disable interrupts.
    irq_state = msr & MSR_EE;
    msr &= ~MSR_EE;

    put_register (PS, msr);
}

/* Clear the single-step state. */

void __clear_single_step (void)
{
    target_register_t msr = get_register (PS);

    // Clear single-step flag in the exception context.
    msr &= ~(MSR_SE | MSR_BE);
    // Restore interrupt state.
    // FIXME: Should check whether the executed instruction changed the
    // interrupt state - or single-stepping a MSR changing instruction
    // may result in a wrong EE. Not a very likely scenario though.
    msr |= irq_state;

    // This function is called much more than its counterpart
    // __single_step.  Only re-enable interrupts if they where
    // disabled during the previous cal to __single_step. Otherwise,
    // this function only makes "extra sure" that no trace or branch
    // exception will happen.
    irq_state = 0;

    put_register (PS, msr);
}
#endif

void __install_breakpoints (void)
{
    /* NOP since single-step HW exceptions are used instead of
       breakpoints. */
}

void __clear_breakpoints (void)
{
}


/* If the breakpoint we hit is in the breakpoint() instruction, return a
   non-zero value. */

int
__is_breakpoint_function ()
{
    return get_register (PC) == (target_register_t)&_breakinst;
}


/* Skip the current instruction.  Since this is only called by the
   stub when the PC points to a breakpoint or trap instruction,
   we can safely just skip 4. */

void __skipinst (void)
{
    put_register (PC, get_register (PC) + 4);
}

#endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS