forked from Imagelibrary/rtems
1217 lines
38 KiB
C
1217 lines
38 KiB
C
/*
|
|
* Copyright (c) 2024 Contemporary Software
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#define TARGET_DEBUG 0
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/score/threadimpl.h>
|
|
|
|
#include "rtems-debugger-target.h"
|
|
#include "rtems-debugger-threads.h"
|
|
|
|
#include <rtems/powerpc/registers.h>
|
|
|
|
/*
|
|
* Hardware breakpoints. Limited by hardware
|
|
*/
|
|
#define RTEMS_DEBUGGER_HWBREAK_NUM 4
|
|
|
|
/*
|
|
* Number of registers.
|
|
*/
|
|
#define RTEMS_DEBUGGER_NUMREGS 72
|
|
|
|
/*
|
|
* Debugger registers layout.
|
|
*/
|
|
#define REG_R0 0
|
|
#define REG_R1 1
|
|
#define REG_R2 2
|
|
#define REG_R3 3
|
|
#define REG_R4 4
|
|
#define REG_R5 5
|
|
#define REG_R6 6
|
|
#define REG_R7 7
|
|
#define REG_R8 8
|
|
#define REG_R9 9
|
|
#define REG_R10 10
|
|
#define REG_R11 11
|
|
#define REG_R12 12
|
|
#define REG_R13 13
|
|
#define REG_R14 14
|
|
#define REG_R15 15
|
|
#define REG_R16 16
|
|
#define REG_R17 17
|
|
#define REG_R18 18
|
|
#define REG_R19 19
|
|
#define REG_R20 20
|
|
#define REG_R21 21
|
|
#define REG_R22 22
|
|
#define REG_R23 23
|
|
#define REG_R24 24
|
|
#define REG_R25 25
|
|
#define REG_R26 26
|
|
#define REG_R27 27
|
|
#define REG_R28 28
|
|
#define REG_R29 29
|
|
#define REG_R30 30
|
|
#define REG_R31 31
|
|
#define REG_F0 32
|
|
#define REG_F1 33
|
|
#define REG_F2 34
|
|
#define REG_F3 35
|
|
#define REG_F4 36
|
|
#define REG_F5 37
|
|
#define REG_F6 38
|
|
#define REG_F7 39
|
|
#define REG_F8 40
|
|
#define REG_F9 41
|
|
#define REG_F10 42
|
|
#define REG_F11 43
|
|
#define REG_F12 44
|
|
#define REG_F13 45
|
|
#define REG_F14 46
|
|
#define REG_F15 47
|
|
#define REG_F16 48
|
|
#define REG_F17 49
|
|
#define REG_F18 50
|
|
#define REG_F19 51
|
|
#define REG_F20 52
|
|
#define REG_F21 53
|
|
#define REG_F22 54
|
|
#define REG_F23 55
|
|
#define REG_F24 56
|
|
#define REG_F25 57
|
|
#define REG_F26 58
|
|
#define REG_F27 59
|
|
#define REG_F28 60
|
|
#define REG_F29 61
|
|
#define REG_F30 62
|
|
#define REG_F31 63
|
|
#define REG_PC 64
|
|
#define REG_MSR 65
|
|
#define REG_CND 66
|
|
#define REG_LR 67
|
|
#define REG_CNT 68
|
|
#define REG_XER 69
|
|
#define REG_ACC 70
|
|
#define REG_SPEFSCR 71
|
|
|
|
/**
|
|
* Register offset table with the total as the last entry.
|
|
*
|
|
* Check this table in gdb with the command:
|
|
*
|
|
* maint print remote-registers
|
|
*
|
|
* The important column is the Rmt Nr and g/G offset
|
|
*/
|
|
|
|
/*
|
|
* MPC604/MPC750
|
|
*
|
|
* From the MVME2700 executable (main print architecture):
|
|
*
|
|
* gdbarch_dump: bfd_arch_info = powerpc:common
|
|
*
|
|
*/
|
|
static const size_t ppc_common_reg_offsets[RTEMS_DEBUGGER_NUMREGS + 1] =
|
|
{
|
|
0, /* REG_R0 4 uint32_t */
|
|
4, /* REG_R1 4 uint32_t */
|
|
8, /* REG_R2 4 uint32_t */
|
|
12, /* REG_R3 4 uint32_t */
|
|
16, /* REG_R4 4 uint32_t */
|
|
20, /* REG_R5 4 uint32_t */
|
|
24, /* REG_R6 4 uint32_t */
|
|
28, /* REG_R7 4 uint32_t */
|
|
32, /* REG_R8 4 uint32_t */
|
|
36, /* REG_R9 4 uint32_t */
|
|
40, /* REG_R10 4 uint32_t */
|
|
44, /* REG_R11 4 uint32_t */
|
|
48, /* REG_R12 4 uint32_t */
|
|
52, /* REG_R13 4 uint32_t */
|
|
56, /* REG_R14 4 uint32_t */
|
|
60, /* REG_R15 4 uint32_t */
|
|
64, /* REG_R16 4 uint32_t */
|
|
68, /* REG_R17 4 uint32_t */
|
|
72, /* REG_R18 4 uint32_t */
|
|
76, /* REG_R19 4 uint32_t */
|
|
80, /* REG_R20 4 uint32_t */
|
|
84, /* REG_R21 4 uint32_t */
|
|
88, /* REG_R22 4 uint32_t */
|
|
92, /* REG_R23 4 uint32_t */
|
|
96, /* REG_R24 4 uint32_t */
|
|
100, /* REG_R25 4 uint32_t */
|
|
104, /* REG_R26 4 uint32_t */
|
|
108, /* REG_R27 4 uint32_t */
|
|
112, /* REG_R28 4 uint32_t */
|
|
116, /* REG_R29 4 uint32_t */
|
|
120, /* REG_R30 4 uint32_t */
|
|
124, /* REG_R31 4 uint32_t */
|
|
128, /* REG_F0 8 long */
|
|
136, /* REG_F1 8 long */
|
|
144, /* REG_F2 8 long */
|
|
152, /* REG_F3 8 long */
|
|
160, /* REG_F4 8 long */
|
|
168, /* REG_F5 8 long */
|
|
176, /* REG_F6 8 long */
|
|
184, /* REG_F7 8 long */
|
|
192, /* REG_F8 8 long */
|
|
200, /* REG_F9 8 long */
|
|
208, /* REG_F10 8 long */
|
|
216, /* REG_F11 8 long */
|
|
224, /* REG_F12 8 long */
|
|
232, /* REG_F13 8 long */
|
|
240, /* REG_F14 8 long */
|
|
248, /* REG_F15 8 long */
|
|
256, /* REG_F16 8 long */
|
|
264, /* REG_F17 8 long */
|
|
272, /* REG_F18 8 long */
|
|
280, /* REG_F19 8 long */
|
|
288, /* REG_F20 8 long */
|
|
296, /* REG_F21 8 long */
|
|
304, /* REG_F22 8 long */
|
|
312, /* REG_F23 8 long */
|
|
320, /* REG_F24 8 long */
|
|
328, /* REG_F25 8 long */
|
|
336, /* REG_F26 8 long */
|
|
344, /* REG_F27 8 long */
|
|
352, /* REG_F28 8 long */
|
|
360, /* REG_F29 8 long */
|
|
368, /* REG_F30 8 long */
|
|
376, /* REG_F31 8 long */
|
|
384, /* REG_PC 4 *1 */
|
|
388, /* REG_MSR 4 uint32_t */
|
|
392, /* REG_CND 4 uint32_t */
|
|
396, /* REG_LR 4 *1 */
|
|
400, /* REG_CNT 4 uint32_t */
|
|
404, /* REG_XER 4 uint32_t */
|
|
408, /* REG_ACC no present */
|
|
408, /* REG_SPEFSCR 4 long */
|
|
412 /* total size */
|
|
};
|
|
|
|
/*
|
|
* MPC604/MPC750
|
|
*
|
|
* From the MVME2700 executable:
|
|
*
|
|
* gdbarch_dump: bfd_arch_info = powerpc:e500
|
|
*
|
|
* Note:
|
|
* No REG_F?? registers defined. The EV?? registers are defined
|
|
* and can be viewed in GDB with `info all-registers` however
|
|
* there is no remote protocol mapping I can see and the data being
|
|
* display in the EV?? rergisters is an alias of the normal register
|
|
* data.
|
|
*/
|
|
static const size_t ppc_e500_reg_offsets[RTEMS_DEBUGGER_NUMREGS + 1] =
|
|
{
|
|
0, /* REG_R0 4 uint32_t */
|
|
4, /* REG_R1 4 uint32_t */
|
|
8, /* REG_R2 4 uint32_t */
|
|
12, /* REG_R3 4 uint32_t */
|
|
16, /* REG_R4 4 uint32_t */
|
|
20, /* REG_R5 4 uint32_t */
|
|
24, /* REG_R6 4 uint32_t */
|
|
28, /* REG_R7 4 uint32_t */
|
|
32, /* REG_R8 4 uint32_t */
|
|
36, /* REG_R9 4 uint32_t */
|
|
40, /* REG_R10 4 uint32_t */
|
|
44, /* REG_R11 4 uint32_t */
|
|
48, /* REG_R12 4 uint32_t */
|
|
52, /* REG_R13 4 uint32_t */
|
|
56, /* REG_R14 4 uint32_t */
|
|
60, /* REG_R15 4 uint32_t */
|
|
64, /* REG_R16 4 uint32_t */
|
|
68, /* REG_R17 4 uint32_t */
|
|
72, /* REG_R18 4 uint32_t */
|
|
76, /* REG_R19 4 uint32_t */
|
|
80, /* REG_R20 4 uint32_t */
|
|
84, /* REG_R21 4 uint32_t */
|
|
88, /* REG_R22 4 uint32_t */
|
|
92, /* REG_R23 4 uint32_t */
|
|
96, /* REG_R24 4 uint32_t */
|
|
100, /* REG_R25 4 uint32_t */
|
|
104, /* REG_R26 4 uint32_t */
|
|
108, /* REG_R27 4 uint32_t */
|
|
112, /* REG_R28 4 uint32_t */
|
|
116, /* REG_R29 4 uint32_t */
|
|
120, /* REG_R30 4 uint32_t */
|
|
124, /* REG_R31 4 uint32_t */
|
|
128, /* REG_F0 4 long */
|
|
132, /* REG_F1 4 long */
|
|
136, /* REG_F2 4 long */
|
|
140, /* REG_F3 4 long */
|
|
144, /* REG_F4 4 long */
|
|
148, /* REG_F5 4 long */
|
|
152, /* REG_F6 4 long */
|
|
156, /* REG_F7 4 long */
|
|
160, /* REG_F8 4 long */
|
|
164, /* REG_F9 4 long */
|
|
168, /* REG_F10 4 long */
|
|
172, /* REG_F11 4 long */
|
|
176, /* REG_F12 4 long */
|
|
180, /* REG_F13 4 long */
|
|
184, /* REG_F14 4 long */
|
|
188, /* REG_F15 4 long */
|
|
192, /* REG_F16 4 long */
|
|
196, /* REG_F17 4 long */
|
|
200, /* REG_F18 4 long */
|
|
204, /* REG_F19 4 long */
|
|
208, /* REG_F20 4 long */
|
|
212, /* REG_F21 4 long */
|
|
216, /* REG_F22 4 long */
|
|
220, /* REG_F23 4 long */
|
|
224, /* REG_F24 4 long */
|
|
228, /* REG_F25 4 long */
|
|
232, /* REG_F26 4 long */
|
|
236, /* REG_F27 4 long */
|
|
240, /* REG_F28 4 long */
|
|
244, /* REG_F29 4 long */
|
|
248, /* REG_F30 4 long */
|
|
252, /* REG_F31 4 long */
|
|
256, /* REG_PC 4 *1 */
|
|
260, /* REG_MSR 4 uint32_t */
|
|
264, /* REG_CND 4 uint32_t */
|
|
268, /* REG_LR 4 *1 */
|
|
272, /* REG_CNT 4 uint32_t */
|
|
276, /* REG_XER 4 uint32_t */
|
|
280, /* REG_ACC 8 long long */
|
|
288, /* REG_SPEFSCR 4 long */
|
|
292 /* total size */
|
|
};
|
|
|
|
static const size_t* ppc_reg_offsets;
|
|
|
|
/*
|
|
* Number of bytes of registers.
|
|
*/
|
|
#define RTEMS_DEBUGGER_NUMREGBYTES ppc_reg_offsets[RTEMS_DEBUGGER_NUMREGS]
|
|
|
|
/*
|
|
* Exception handler to hook.
|
|
*/
|
|
typedef CPU_Exception_frame BSP_Exception_frame;
|
|
typedef void (*exception_handler_t)(BSP_Exception_frame*);
|
|
extern exception_handler_t globalExceptHdl;
|
|
|
|
/**
|
|
* The `sc` instruction
|
|
*/
|
|
#define TARGET_BKPT 0x0ce00000
|
|
static const uint8_t breakpoint[4] = { 0x0c, 0xe0, 0x00, 0x00 };
|
|
|
|
/**
|
|
* Target lock.
|
|
*/
|
|
RTEMS_INTERRUPT_LOCK_DEFINE(static, target_lock, "target_lock")
|
|
|
|
/**
|
|
* The orginal exception handler.
|
|
*/
|
|
static void (*orig_currentExcHandler)(CPU_Exception_frame* frame);
|
|
|
|
#if TARGET_DEBUG
|
|
#include <rtems/bspIo.h>
|
|
static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
|
static void
|
|
target_printk(const char* format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
vprintk(format, ap);
|
|
va_end(ap);
|
|
}
|
|
#else
|
|
#define target_printk(_fmt, ...)
|
|
#endif
|
|
|
|
/*
|
|
* The CPU Ident code is taken from libcpu/cpuIndent.h because
|
|
* this cpukit code cannot reach over into the BSP headers.
|
|
* Adding the code here is not optimal but it solves the need.
|
|
*/
|
|
#define ASM_RESET_VECTOR 0x01
|
|
#define ASM_MACH_VECTOR 0x02
|
|
#define ASM_PROT_VECTOR 0x03
|
|
#define ASM_ISI_VECTOR 0x04
|
|
#define ASM_EXT_VECTOR 0x05
|
|
#define ASM_ALIGN_VECTOR 0x06
|
|
#define ASM_PROG_VECTOR 0x07
|
|
#define ASM_FLOAT_VECTOR 0x08
|
|
#define ASM_DEC_VECTOR 0x09
|
|
#define ASM_SYS_VECTOR 0x0C
|
|
#define ASM_TRACE_VECTOR 0x0D
|
|
|
|
#define ASM_60X_VEC_VECTOR 0x0A
|
|
#define ASM_60X_PERFMON_VECTOR 0x0F
|
|
#define ASM_60X_IMISS_VECTOR 0x10
|
|
#define ASM_60X_DLMISS_VECTOR 0x11
|
|
#define ASM_60X_DSMISS_VECTOR 0x12
|
|
#define ASM_60X_ADDR_VECTOR 0x13
|
|
#define ASM_60X_SYSMGMT_VECTOR 0x14
|
|
#define ASM_60X_VEC_ASSIST_VECTOR 0x16
|
|
#define ASM_60X_ITM_VECTOR 0x17
|
|
|
|
#define LAST_VALID_EXC 0x1F
|
|
|
|
typedef enum
|
|
{
|
|
PPC_601 = 0x1,
|
|
PPC_5XX = 0x2,
|
|
PPC_603 = 0x3,
|
|
PPC_604 = 0x4,
|
|
PPC_603e = 0x6,
|
|
PPC_603ev = 0x7,
|
|
PPC_750 = 0x8,
|
|
PPC_750_IBM = 0x7000,
|
|
PPC_604e = 0x9,
|
|
PPC_604r = 0xA,
|
|
PPC_7400 = 0xC,
|
|
PPC_405 = 0x2001, /* Xilinx Virtex-II Pro or -4 */
|
|
PPC_405EX = 0x1291, /* + 405EXr */
|
|
PPC_405GP = 0x4011, /* + 405CR */
|
|
PPC_405GPr = 0x5091,
|
|
PPC_405EZ = 0x4151,
|
|
PPC_405EP = 0x5121,
|
|
PPC_440 = 0x7ff2, /* Xilinx Virtex-5*/
|
|
PPC_7455 = 0x8001, /* Kate Feng */
|
|
PPC_7457 = 0x8002,
|
|
PPC_620 = 0x16,
|
|
PPC_860 = 0x50,
|
|
PPC_821 = PPC_860,
|
|
PPC_823 = PPC_860,
|
|
PPC_8260 = 0x81,
|
|
PPC_8240 = PPC_8260,
|
|
PPC_8245 = 0x8081,
|
|
PPC_8540 = 0x8020,
|
|
PPC_e500v2 = 0x8021,
|
|
PPC_e6500 = 0x8040,
|
|
PPC_603le = 0x8082, /* 603le core, in MGT5100 and MPC5200 */
|
|
PPC_e300c1 = 0x8083, /* e300c1 core, in MPC83xx*/
|
|
PPC_e300c2 = 0x8084, /* e300c2 core */
|
|
PPC_e300c3 = 0x8085, /* e300c3 core */
|
|
PPC_e200z0 = 0x8170,
|
|
PPC_e200z1 = 0x8140,
|
|
PPC_e200z4 = 0x8150,
|
|
PPC_e200z6 = 0x8110,
|
|
PPC_e200z7 = 0x8160,
|
|
PPC_PSIM = 0xfffe, /* GDB PowerPC simulator -- fake version */
|
|
PPC_UNKNOWN = 0xffff
|
|
} ppc_cpu_id;
|
|
|
|
#define PPC_BOOKE_405 1 /* almost like booke but with some significant differences */
|
|
#define PPC_BOOKE_STD 2
|
|
#define PPC_BOOKE_E500 3 /* bookE with extensions */
|
|
|
|
typedef struct {
|
|
volatile bool type_1_complete;
|
|
volatile uint32_t msr;
|
|
} powerpc_stepping;
|
|
|
|
static ppc_cpu_id ppc_cpu;
|
|
static int is_bookE;
|
|
static powerpc_stepping stepping_instr;
|
|
|
|
#define xppc_read_spr(reg, val) \
|
|
__asm__ __volatile__("mfspr %0,"#reg : "=r" (val))
|
|
#define xppc_write_spr(reg, val) \
|
|
__asm__ __volatile__("mtspr "#reg",%0" : : "r" (val))
|
|
#define ppc_read_spr(reg, val) xppc_read_spr(reg, val)
|
|
#define ppc_write_spr(reg, val) xppc_write_spr(reg, val)
|
|
|
|
static const char *ppc_get_cpu_type_name(ppc_cpu_id cpu)
|
|
{
|
|
switch (cpu) {
|
|
case PPC_405: return "PPC405";
|
|
case PPC_405GP: return "PPC405GP";
|
|
case PPC_405EX: return "PPC405EX";
|
|
case PPC_440: return "PPC440";
|
|
case PPC_601: return "MPC601";
|
|
case PPC_5XX: return "MPC5XX";
|
|
case PPC_603: return "MPC603";
|
|
case PPC_603ev: return "MPC603ev";
|
|
case PPC_604: return "MPC604";
|
|
case PPC_750: return "MPC750";
|
|
case PPC_750_IBM: return "IBM PPC750";
|
|
case PPC_7400: return "MPC7400";
|
|
case PPC_7455: return "MPC7455";
|
|
case PPC_7457: return "MPC7457";
|
|
case PPC_603le: return "MPC603le";
|
|
case PPC_604e: return "MPC604e";
|
|
case PPC_604r: return "MPC604r";
|
|
case PPC_620: return "MPC620";
|
|
case PPC_860: return "MPC860";
|
|
case PPC_8260: return "MPC8260";
|
|
case PPC_8245: return "MPC8245";
|
|
case PPC_8540: return "MPC8540";
|
|
case PPC_PSIM: return "PSIM";
|
|
case PPC_e200z0: return "e200z0";
|
|
case PPC_e200z1: return "e200z1";
|
|
case PPC_e200z4: return "e200z4";
|
|
case PPC_e200z6: return "e200z6";
|
|
case PPC_e200z7: return "e200z7";
|
|
case PPC_e500v2: return "e500v2";
|
|
case PPC_e6500: return "e6500";
|
|
default:
|
|
break;
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
static bool ppc_is_bookE(void)
|
|
{
|
|
return is_bookE != 0;
|
|
}
|
|
|
|
static bool ppc_is_bookE_405(void)
|
|
{
|
|
return is_bookE == PPC_BOOKE_405;
|
|
}
|
|
|
|
static int ppc_probe_cpu_type(void)
|
|
{
|
|
/*
|
|
* cpu types listed here have the lowermost nibble as a version identifier
|
|
* we will tweak them to the standard version
|
|
*/
|
|
const uint32_t ppc_cpu_id_version_nibble[] = {
|
|
PPC_e200z0,
|
|
PPC_e200z1,
|
|
PPC_e200z4,
|
|
PPC_e200z6,
|
|
PPC_e200z7
|
|
};
|
|
#define NUM_CPU_ID_VERSION \
|
|
(sizeof(ppc_cpu_id_version_nibble) / sizeof(ppc_cpu_id_version_nibble[0]))
|
|
|
|
uint32_t pvr;
|
|
int i;
|
|
|
|
ppc_read_spr(PPC_PVR, pvr);
|
|
pvr >>= 16;
|
|
|
|
/*
|
|
* apply tweaks to ignore version
|
|
*/
|
|
for (i = 0; i < NUM_CPU_ID_VERSION; ++i) {
|
|
if ((pvr & 0xfff0) == (ppc_cpu_id_version_nibble[i] & 0xfff0)) {
|
|
pvr = ppc_cpu_id_version_nibble[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
ppc_cpu = (ppc_cpu_id) pvr;
|
|
|
|
switch (pvr) {
|
|
case PPC_405:
|
|
case PPC_405GP:
|
|
case PPC_405EX:
|
|
case PPC_440:
|
|
case PPC_601:
|
|
case PPC_5XX:
|
|
case PPC_603:
|
|
case PPC_603ev:
|
|
case PPC_603le:
|
|
case PPC_604:
|
|
case PPC_604r:
|
|
case PPC_750:
|
|
case PPC_750_IBM:
|
|
case PPC_7400:
|
|
case PPC_7455:
|
|
case PPC_7457:
|
|
case PPC_604e:
|
|
case PPC_620:
|
|
case PPC_860:
|
|
case PPC_8260:
|
|
case PPC_8245:
|
|
case PPC_PSIM:
|
|
case PPC_8540:
|
|
case PPC_e200z0:
|
|
case PPC_e200z1:
|
|
case PPC_e200z4:
|
|
case PPC_e200z6:
|
|
case PPC_e200z7:
|
|
case PPC_e300c1:
|
|
case PPC_e300c2:
|
|
case PPC_e300c3:
|
|
case PPC_e500v2:
|
|
case PPC_e6500:
|
|
break;
|
|
default:
|
|
rtems_debugger_printf("rtems-db: powerpc: unknown CPU\n");
|
|
return -1;
|
|
}
|
|
|
|
switch (ppc_cpu) {
|
|
case PPC_405:
|
|
case PPC_405GP:
|
|
case PPC_405EX:
|
|
is_bookE = PPC_BOOKE_405;
|
|
break;
|
|
case PPC_440:
|
|
is_bookE = PPC_BOOKE_STD;
|
|
break;
|
|
case PPC_8540:
|
|
case PPC_e200z0:
|
|
case PPC_e200z1:
|
|
case PPC_e200z4:
|
|
case PPC_e200z6:
|
|
case PPC_e200z7:
|
|
case PPC_e500v2:
|
|
case PPC_e6500:
|
|
is_bookE = PPC_BOOKE_E500;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rtems_debugger_printf("rtems-db: powerpc: %s %s\n",
|
|
ppc_get_cpu_type_name(ppc_cpu),
|
|
ppc_is_bookE() ? "(book E)" : "");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ppc_set_dbsr(uint32_t dbsr)
|
|
{
|
|
if (ppc_is_bookE()) {
|
|
if (ppc_is_bookE_405()) {
|
|
ppc_write_spr(0x3f0, dbsr);
|
|
} else {
|
|
ppc_write_spr(BOOKE_DBSR, dbsr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ppc_set_dbcr0(uint32_t dbcr)
|
|
{
|
|
if (ppc_is_bookE()) {
|
|
if (ppc_is_bookE_405()) {
|
|
ppc_write_spr(0x3f2, dbcr);
|
|
} else {
|
|
ppc_write_spr(BOOKE_DBCR0, dbcr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ppc_debug_probe(rtems_debugger_target* target)
|
|
{
|
|
if (ppc_probe_cpu_type() != 0) {
|
|
return -1;
|
|
}
|
|
ppc_set_dbsr(0xffffffffUL);
|
|
ppc_set_dbcr0(0);
|
|
return 0;
|
|
}
|
|
|
|
static int ppc_code_writer(void* address, const void* data, size_t size) {
|
|
uint32_t current_level;
|
|
uint32_t addr;
|
|
uint32_t val;
|
|
addr = (uint32_t) (intptr_t) address;
|
|
val = *((uint32_t*) data);
|
|
target_printk("[] powerpc: code_ writer: %08x -> %p size=%zu\n", val, address, size);
|
|
if (size != 4) {
|
|
rtems_debugger_printf("rtems-db: powerpc: invalid code write size: size=%zu\n", size);
|
|
return -1;
|
|
}
|
|
/*
|
|
* Disable interrupts and MMU to work around write-protection.
|
|
*
|
|
* This hack is due to the lack of a proper MMU API for the older
|
|
* PPC hardware. Normally libdebugger makes the code section
|
|
* read/write so breakpoint insertion and removal is fast. The lack
|
|
* of a MMU API and the way some of the BSPs MMU is set up means it
|
|
* not easy to update the BSPs.
|
|
*
|
|
* The following is based on Till's `do_patch` implementation.
|
|
*/
|
|
current_level = ppc_interrupt_disable();
|
|
asm volatile(
|
|
" mfmsr 0 \n"
|
|
" andc 7,0,%0 \n"
|
|
" mtmsr 7 \n" /* msr is exec. synchronizing; rval access complete */
|
|
" isync \n" /* context sync.; DR off after this */
|
|
" stw %2,0(%1) \n"
|
|
" dcbst 0,%1 \n" /* write out data cache line (addr) */
|
|
" icbi 0,%1 \n" /* invalidate instr. cache line (addr) */
|
|
" mtmsr 0 \n" /* msr is exec. synchr.; mem access completed */
|
|
" sync \n" /* probably not necessary */
|
|
" isync \n" /* context sync.; MMU on after this */
|
|
/* add 'key' to input operands to make sure this asm is not
|
|
* moved around
|
|
*/
|
|
::"r"(ppc_is_bookE() ? 0 : MSR_DR), "b"(addr), "r"(val), "r"(current_level)
|
|
:"r0","r7");
|
|
ppc_interrupt_enable(current_level);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_configure(rtems_debugger_target* target)
|
|
{
|
|
if (is_bookE) {
|
|
ppc_reg_offsets = ppc_e500_reg_offsets;
|
|
} else {
|
|
ppc_reg_offsets = ppc_common_reg_offsets;
|
|
}
|
|
target->capabilities = (RTEMS_DEBUGGER_TARGET_CAP_SWBREAK);
|
|
target->reg_num = RTEMS_DEBUGGER_NUMREGS;
|
|
target->reg_offset = ppc_reg_offsets;
|
|
target->breakpoint = &breakpoint[0];
|
|
target->breakpoint_size = sizeof(breakpoint);
|
|
target->code_writer = ppc_code_writer;
|
|
return ppc_debug_probe(target);
|
|
}
|
|
|
|
static void powerpc_print_exception_frame(CPU_Exception_frame* frame) {
|
|
uintptr_t* gpr;
|
|
int r;
|
|
target_printk("[} frame = %08" PRIx32 " sig=%d (0x%" PRIx32 ")\n",
|
|
(uint32_t) frame,
|
|
rtems_debugger_target_exception_to_signal(frame),
|
|
frame->_EXC_number);
|
|
#ifndef __SPE__
|
|
target_printk("[} SRR0 = %08" PRIx32 " SRR1 = %08" PRIx32 "\n",
|
|
frame->EXC_SRR0, frame->EXC_SRR1);
|
|
#else
|
|
target_printk("[} SRR0 = %08" PRIx32 " SRR1 = %08" PRIx32 \
|
|
" SPEFSCR = %08" PRIx32 " ACC = %08" PRIx32 "\n",
|
|
frame->EXC_SRR0, frame->EXC_SRR1,
|
|
frame->EXC_SPEFSCR, frame->EXC_ACC);
|
|
#endif
|
|
target_printk("[} LR = %08" PRIx32 " CR = %08" PRIx32 \
|
|
" XER = %08" PRIx32 " CTR = %08" PRIx32 "\n",
|
|
frame->EXC_LR, frame->EXC_CR, frame->EXC_XER, frame->EXC_CTR);
|
|
gpr = &frame->GPR0;
|
|
for (r= 0; r < 32; r += 4, gpr += 4) {
|
|
target_printk("[} R%-2d = %08" PRIx32 " R%-2d = %08" PRIx32 \
|
|
" R%-2d = %08" PRIx32 " R%-2d = %08" PRIx32 "\n",
|
|
r, *gpr, r + 1, *(gpr + 1), r + 2,
|
|
*(gpr + 2), r + 3, *(gpr + 3));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NXP BOOK_EUM.PDF Programmeing Note, Chapter 9 Debug Facilities
|
|
*
|
|
* There are two classes of debug exception types:
|
|
*
|
|
* Type 1: exception before instruction
|
|
* Type 2: exception after instruction
|
|
*
|
|
* Almost all debug exceptions fall into the first category. That is,
|
|
* they all take the interrupt upon encountering an instruction having
|
|
* the exception without updating any architectural state (other than
|
|
* DBSR, CSRR0, CSRR1, MSR) for that instruction.
|
|
*
|
|
* The CSRR0 for this type of exception points to the instruction that
|
|
* encountered the exception. This includes IAC, DAC, branch taken,
|
|
* etc.
|
|
*
|
|
* The only exception which fall into the second category is the
|
|
* instruction complete debug exception. This exception is taken upon
|
|
* completing and updating one instruction and then pointing CSRR0 to
|
|
* the next instruction to execute.
|
|
*/
|
|
static bool
|
|
powerpc_stepping_exception(CPU_Exception_frame* frame)
|
|
{
|
|
bool r = false;
|
|
frame->EXC_SRR1 &= ~MSR_SE;
|
|
switch (frame->_EXC_number) {
|
|
default:
|
|
stepping_instr.type_1_complete = false;
|
|
break;
|
|
case ASM_TRACE_VECTOR:
|
|
if (stepping_instr.type_1_complete) {
|
|
/*
|
|
* Type 2 exception after instruction
|
|
*/
|
|
stepping_instr.type_1_complete = false;
|
|
frame->EXC_SRR1 |= stepping_instr.msr;
|
|
} else {
|
|
stepping_instr.type_1_complete = true;
|
|
frame->EXC_SRR1 &= ~ppc_interrupt_get_disable_mask();
|
|
frame->EXC_SRR1 |= MSR_SE;
|
|
r = true;
|
|
}
|
|
break;
|
|
case ASM_PROG_VECTOR:
|
|
/*
|
|
* Breakpoint.
|
|
*/
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
powerpc_stepping_frame(CPU_Exception_frame* frame)
|
|
{
|
|
if (ppc_is_bookE()) {
|
|
stepping_instr.type_1_complete = false;
|
|
} else {
|
|
stepping_instr.type_1_complete = true;
|
|
}
|
|
stepping_instr.msr =
|
|
frame->EXC_SRR1 & ppc_interrupt_get_disable_mask();;
|
|
frame->EXC_SRR1 &= ~ppc_interrupt_get_disable_mask();
|
|
frame->EXC_SRR1 |= MSR_SE;
|
|
}
|
|
|
|
static void
|
|
target_exception(BSP_Exception_frame* bsp_frame)
|
|
{
|
|
CPU_Exception_frame* frame = bsp_frame;
|
|
|
|
target_printk("[} powerpc target exc: entry\n");
|
|
powerpc_print_exception_frame(frame);
|
|
|
|
if (!powerpc_stepping_exception(bsp_frame)) {
|
|
switch (rtems_debugger_target_exception(frame)) {
|
|
case rtems_debugger_target_exc_consumed:
|
|
default:
|
|
break;
|
|
case rtems_debugger_target_exc_step:
|
|
powerpc_stepping_frame(frame);
|
|
break;
|
|
case rtems_debugger_target_exc_cascade:
|
|
if (orig_currentExcHandler != NULL) {
|
|
orig_currentExcHandler(bsp_frame);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
rtems_debugger_is_int_reg(size_t reg)
|
|
{
|
|
const size_t size = ppc_reg_offsets[reg + 1] - ppc_reg_offsets[reg];
|
|
return size == RTEMS_DEBUGGER_NUMREGBYTES;
|
|
}
|
|
|
|
static void
|
|
rtems_debugger_set_int_reg(rtems_debugger_thread* thread,
|
|
const uintptr_t reg,
|
|
const uint32_t value)
|
|
{
|
|
const size_t offset = ppc_reg_offsets[reg];
|
|
/*
|
|
* Use memcpy to avoid alignment issues.
|
|
*/
|
|
memcpy(&thread->registers[offset], &value, sizeof(uintptr_t));
|
|
}
|
|
|
|
static const uintptr_t
|
|
rtems_debugger_get_int_reg(rtems_debugger_thread* thread, const size_t reg)
|
|
{
|
|
const size_t offset = ppc_reg_offsets[reg];
|
|
uintptr_t value;
|
|
memcpy(&value, &thread->registers[offset], sizeof(uintptr_t));
|
|
return value;
|
|
}
|
|
|
|
static void rtems_debugger_acquire_exc(void) {
|
|
if (orig_currentExcHandler == NULL) {
|
|
orig_currentExcHandler = globalExceptHdl;
|
|
globalExceptHdl = target_exception;
|
|
}
|
|
}
|
|
|
|
static void rtems_debugger_release_exc(void) {
|
|
if (orig_currentExcHandler != NULL) {
|
|
globalExceptHdl = orig_currentExcHandler;
|
|
}
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_enable(void)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
rtems_interrupt_lock_context lock_context;
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
rtems_debugger_acquire_exc();
|
|
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_disable(void)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
rtems_interrupt_lock_context lock_context;
|
|
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
|
ppc_set_dbsr(0xffffffffUL);
|
|
ppc_set_dbcr0(0);
|
|
rtems_debugger_release_exc();
|
|
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_read_regs(rtems_debugger_thread* thread)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
if (!rtems_debugger_thread_flag(thread,
|
|
RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID)) {
|
|
static const uintptr_t good_address = (uintptr_t) &good_address;
|
|
size_t i;
|
|
|
|
for (i = 0; i < rtems_debugger_target_reg_num(); ++i) {
|
|
if (rtems_debugger_is_int_reg(i)) {
|
|
rtems_debugger_set_int_reg(thread, i, (uintptr_t) &good_address);
|
|
}
|
|
}
|
|
|
|
if (thread->frame) {
|
|
CPU_Exception_frame* frame = thread->frame;
|
|
rtems_debugger_set_int_reg(thread, REG_R0, frame->GPR0);
|
|
rtems_debugger_set_int_reg(thread, REG_R1, frame->GPR1);
|
|
rtems_debugger_set_int_reg(thread, REG_R2, frame->GPR2);
|
|
rtems_debugger_set_int_reg(thread, REG_R3, frame->GPR3);
|
|
rtems_debugger_set_int_reg(thread, REG_R4, frame->GPR4);
|
|
rtems_debugger_set_int_reg(thread, REG_R5, frame->GPR5);
|
|
rtems_debugger_set_int_reg(thread, REG_R6, frame->GPR6);
|
|
rtems_debugger_set_int_reg(thread, REG_R7, frame->GPR7);
|
|
rtems_debugger_set_int_reg(thread, REG_R8, frame->GPR8);
|
|
rtems_debugger_set_int_reg(thread, REG_R9, frame->GPR9);
|
|
rtems_debugger_set_int_reg(thread, REG_R10, frame->GPR10);
|
|
rtems_debugger_set_int_reg(thread, REG_R11, frame->GPR11);
|
|
rtems_debugger_set_int_reg(thread, REG_R12, frame->GPR12);
|
|
rtems_debugger_set_int_reg(thread, REG_R13, frame->GPR13);
|
|
rtems_debugger_set_int_reg(thread, REG_R14, frame->GPR14);
|
|
rtems_debugger_set_int_reg(thread, REG_R15, frame->GPR15);
|
|
rtems_debugger_set_int_reg(thread, REG_R16, frame->GPR16);
|
|
rtems_debugger_set_int_reg(thread, REG_R17, frame->GPR17);
|
|
rtems_debugger_set_int_reg(thread, REG_R18, frame->GPR18);
|
|
rtems_debugger_set_int_reg(thread, REG_R19, frame->GPR19);
|
|
rtems_debugger_set_int_reg(thread, REG_R20, frame->GPR20);
|
|
rtems_debugger_set_int_reg(thread, REG_R21, frame->GPR21);
|
|
rtems_debugger_set_int_reg(thread, REG_R22, frame->GPR22);
|
|
rtems_debugger_set_int_reg(thread, REG_R23, frame->GPR23);
|
|
rtems_debugger_set_int_reg(thread, REG_R24, frame->GPR24);
|
|
rtems_debugger_set_int_reg(thread, REG_R25, frame->GPR25);
|
|
rtems_debugger_set_int_reg(thread, REG_R26, frame->GPR26);
|
|
rtems_debugger_set_int_reg(thread, REG_R27, frame->GPR27);
|
|
rtems_debugger_set_int_reg(thread, REG_R28, frame->GPR28);
|
|
rtems_debugger_set_int_reg(thread, REG_R29, frame->GPR29);
|
|
rtems_debugger_set_int_reg(thread, REG_R30, frame->GPR30);
|
|
rtems_debugger_set_int_reg(thread, REG_R31, frame->GPR31);
|
|
|
|
rtems_debugger_set_int_reg(thread, REG_PC, frame->EXC_SRR0);
|
|
rtems_debugger_set_int_reg(thread, REG_MSR, frame->EXC_SRR1);
|
|
rtems_debugger_set_int_reg(thread, REG_CND, frame->EXC_CR);
|
|
rtems_debugger_set_int_reg(thread, REG_LR, frame->EXC_LR);
|
|
rtems_debugger_set_int_reg(thread, REG_CNT, frame->EXC_CTR);
|
|
rtems_debugger_set_int_reg(thread, REG_XER, frame->EXC_XER);
|
|
|
|
/*
|
|
* Get the signal from the frame.
|
|
*/
|
|
thread->signal = rtems_debugger_target_exception_to_signal(frame);
|
|
}
|
|
else {
|
|
ppc_context* thread_ctx = ppc_get_context(&thread->tcb->Registers);
|
|
rtems_debugger_set_int_reg(thread, REG_R1, thread_ctx->gpr1);
|
|
rtems_debugger_set_int_reg(thread, REG_R14, thread_ctx->gpr14);
|
|
rtems_debugger_set_int_reg(thread, REG_R15, thread_ctx->gpr15);
|
|
rtems_debugger_set_int_reg(thread, REG_R16, thread_ctx->gpr16);
|
|
rtems_debugger_set_int_reg(thread, REG_R17, thread_ctx->gpr17);
|
|
rtems_debugger_set_int_reg(thread, REG_R18, thread_ctx->gpr18);
|
|
rtems_debugger_set_int_reg(thread, REG_R19, thread_ctx->gpr19);
|
|
rtems_debugger_set_int_reg(thread, REG_R20, thread_ctx->gpr20);
|
|
rtems_debugger_set_int_reg(thread, REG_R21, thread_ctx->gpr21);
|
|
rtems_debugger_set_int_reg(thread, REG_R22, thread_ctx->gpr22);
|
|
rtems_debugger_set_int_reg(thread, REG_R23, thread_ctx->gpr23);
|
|
rtems_debugger_set_int_reg(thread, REG_R24, thread_ctx->gpr24);
|
|
rtems_debugger_set_int_reg(thread, REG_R25, thread_ctx->gpr25);
|
|
rtems_debugger_set_int_reg(thread, REG_R26, thread_ctx->gpr26);
|
|
rtems_debugger_set_int_reg(thread, REG_R27, thread_ctx->gpr27);
|
|
rtems_debugger_set_int_reg(thread, REG_R28, thread_ctx->gpr28);
|
|
rtems_debugger_set_int_reg(thread, REG_R29, thread_ctx->gpr29);
|
|
rtems_debugger_set_int_reg(thread, REG_R30, thread_ctx->gpr30);
|
|
rtems_debugger_set_int_reg(thread, REG_R31, thread_ctx->gpr31);
|
|
|
|
rtems_debugger_set_int_reg(thread, REG_PC, thread_ctx->lr);
|
|
rtems_debugger_set_int_reg(thread, REG_MSR, thread_ctx->msr);
|
|
rtems_debugger_set_int_reg(thread, REG_CND, thread_ctx->cr);
|
|
rtems_debugger_set_int_reg(thread, REG_LR, thread_ctx->lr);
|
|
rtems_debugger_set_int_reg(thread, REG_CNT, 0);
|
|
rtems_debugger_set_int_reg(thread, REG_XER, 0);
|
|
rtems_debugger_set_int_reg(thread, REG_SPEFSCR, 0);
|
|
|
|
/*
|
|
* Blocked threads have no signal.
|
|
*/
|
|
thread->signal = 0;
|
|
}
|
|
|
|
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID;
|
|
thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_write_regs(rtems_debugger_thread* thread)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
if (rtems_debugger_thread_flag(thread,
|
|
RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY)) {
|
|
/*
|
|
* Only write to debugger controlled threads. Do not touch the registers
|
|
* for threads blocked in the context switcher.
|
|
*/
|
|
if (rtems_debugger_thread_flag(thread,
|
|
RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
|
|
CPU_Exception_frame* frame = thread->frame;
|
|
frame->GPR0 = rtems_debugger_get_int_reg(thread, REG_R0);
|
|
frame->GPR1 = rtems_debugger_get_int_reg(thread, REG_R1);
|
|
frame->GPR2 = rtems_debugger_get_int_reg(thread, REG_R2);
|
|
frame->GPR3 = rtems_debugger_get_int_reg(thread, REG_R3);
|
|
frame->GPR4 = rtems_debugger_get_int_reg(thread, REG_R4);
|
|
frame->GPR5 = rtems_debugger_get_int_reg(thread, REG_R5);
|
|
frame->GPR6 = rtems_debugger_get_int_reg(thread, REG_R6);
|
|
frame->GPR7 = rtems_debugger_get_int_reg(thread, REG_R7);
|
|
frame->GPR8 = rtems_debugger_get_int_reg(thread, REG_R8);
|
|
frame->GPR9 = rtems_debugger_get_int_reg(thread, REG_R9);
|
|
frame->GPR10 = rtems_debugger_get_int_reg(thread, REG_R10);
|
|
frame->GPR11 = rtems_debugger_get_int_reg(thread, REG_R11);
|
|
frame->GPR12 = rtems_debugger_get_int_reg(thread, REG_R12);
|
|
frame->GPR13 = rtems_debugger_get_int_reg(thread, REG_R13);
|
|
frame->GPR14 = rtems_debugger_get_int_reg(thread, REG_R14);
|
|
frame->GPR15 = rtems_debugger_get_int_reg(thread, REG_R15);
|
|
frame->GPR16 = rtems_debugger_get_int_reg(thread, REG_R16);
|
|
frame->GPR17 = rtems_debugger_get_int_reg(thread, REG_R17);
|
|
frame->GPR18 = rtems_debugger_get_int_reg(thread, REG_R18);
|
|
frame->GPR19 = rtems_debugger_get_int_reg(thread, REG_R19);
|
|
frame->GPR20 = rtems_debugger_get_int_reg(thread, REG_R20);
|
|
frame->GPR21 = rtems_debugger_get_int_reg(thread, REG_R21);
|
|
frame->GPR22 = rtems_debugger_get_int_reg(thread, REG_R22);
|
|
frame->GPR23 = rtems_debugger_get_int_reg(thread, REG_R23);
|
|
frame->GPR24 = rtems_debugger_get_int_reg(thread, REG_R24);
|
|
frame->GPR25 = rtems_debugger_get_int_reg(thread, REG_R25);
|
|
frame->GPR26 = rtems_debugger_get_int_reg(thread, REG_R26);
|
|
frame->GPR27 = rtems_debugger_get_int_reg(thread, REG_R27);
|
|
frame->GPR28 = rtems_debugger_get_int_reg(thread, REG_R28);
|
|
frame->GPR29 = rtems_debugger_get_int_reg(thread, REG_R29);
|
|
frame->GPR30 = rtems_debugger_get_int_reg(thread, REG_R30);
|
|
frame->GPR31 = rtems_debugger_get_int_reg(thread, REG_R31);
|
|
|
|
frame->EXC_SRR0 = rtems_debugger_get_int_reg(thread, REG_PC);
|
|
frame->EXC_SRR1 = rtems_debugger_get_int_reg(thread, REG_MSR);
|
|
frame->EXC_CR = rtems_debugger_get_int_reg(thread, REG_CND);
|
|
frame->EXC_LR = rtems_debugger_get_int_reg(thread, REG_LR);
|
|
frame->EXC_CTR = rtems_debugger_get_int_reg(thread, REG_CNT);
|
|
frame->EXC_XER = rtems_debugger_get_int_reg(thread, REG_XER);
|
|
}
|
|
thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t
|
|
rtems_debugger_target_reg_pc(rtems_debugger_thread* thread)
|
|
{
|
|
int r;
|
|
r = rtems_debugger_target_read_regs(thread);
|
|
if (r >= 0) {
|
|
return rtems_debugger_get_int_reg(thread, REG_PC);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t
|
|
rtems_debugger_target_frame_pc(CPU_Exception_frame* frame)
|
|
{
|
|
return (uintptr_t) frame->EXC_SRR0;
|
|
}
|
|
|
|
uintptr_t
|
|
rtems_debugger_target_reg_sp(rtems_debugger_thread* thread)
|
|
{
|
|
int r;
|
|
r = rtems_debugger_target_read_regs(thread);
|
|
if (r >= 0) {
|
|
return rtems_debugger_get_int_reg(thread, REG_R1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t
|
|
rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread)
|
|
{
|
|
ppc_context* thread_ctx = ppc_get_context(&thread->tcb->Registers);
|
|
return (DB_UINT) thread_ctx->gpr1;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
if (rtems_debugger_thread_flag(thread,
|
|
RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
|
|
CPU_Exception_frame* frame = thread->frame;
|
|
/*
|
|
* There maybe no frame, ie connect and then enter `si`
|
|
*/
|
|
if (frame != NULL) {
|
|
/*
|
|
* Single step instructions with interrupts masked to avoid
|
|
* stepping into an interrupt handler.
|
|
*/
|
|
if ((frame->EXC_SRR1 & MSR_EE) == 0) {
|
|
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED;
|
|
}
|
|
powerpc_stepping_frame(frame);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
|
|
{
|
|
int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
|
|
switch (frame->_EXC_number) {
|
|
case ASM_MACH_VECTOR:
|
|
sig = RTEMS_DEBUGGER_SIGNAL_BUS;
|
|
break;
|
|
case ASM_PROT_VECTOR:
|
|
case ASM_ISI_VECTOR:
|
|
case ASM_ALIGN_VECTOR:
|
|
case ASM_60X_IMISS_VECTOR:
|
|
case ASM_60X_DLMISS_VECTOR:
|
|
case ASM_60X_DSMISS_VECTOR:
|
|
sig = RTEMS_DEBUGGER_SIGNAL_SEGV;
|
|
break;
|
|
case ASM_PROG_VECTOR:
|
|
sig = RTEMS_DEBUGGER_SIGNAL_TRAP;
|
|
break;
|
|
case ASM_FLOAT_VECTOR:
|
|
sig = RTEMS_DEBUGGER_SIGNAL_FPE;
|
|
break;
|
|
case ASM_DEC_VECTOR:
|
|
sig = RTEMS_DEBUGGER_SIGNAL_ALRM;
|
|
break;
|
|
case ASM_SYS_VECTOR:
|
|
case ASM_TRACE_VECTOR:
|
|
sig = RTEMS_DEBUGGER_SIGNAL_TRAP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
void
|
|
rtems_debugger_target_exception_print(CPU_Exception_frame* frame)
|
|
{
|
|
uintptr_t* gpr = &frame->GPR0;
|
|
int r = 0;
|
|
#ifndef __SPE__
|
|
rtems_debugger_printf("SRR0 = %08" PRIx32 " SRR1 = %08" PRIx32 "\n",
|
|
frame->EXC_SRR0, frame->EXC_SRR1);
|
|
#else
|
|
rtems_debugger_printf("SRR0 = %08" PRIx32 " SRR1 = %08" PRIx32 \
|
|
" SPEFSCR = %08" PRIx32 " ACC = %08" PRIx32 "\n",
|
|
frame->EXC_SRR0, frame->EXC_SRR1,
|
|
frame->EXC_SPEFSCR, frame->EXC_ACC);
|
|
#endif
|
|
rtems_debugger_printf("LR = %08" PRIx32 " CR = %08" PRIx32 \
|
|
" XER = %08" PRIx32 " CTR = %08" PRIx32 "\n",
|
|
frame->EXC_LR, frame->EXC_CR,
|
|
frame->EXC_XER, frame->EXC_CTR);
|
|
gpr = &frame->GPR0;
|
|
for (r= 0; r < 32; r += 4, gpr += 4) {
|
|
rtems_debugger_printf("R%-2d = %08" PRIx32 " R%-2d = %08" PRIx32 \
|
|
" R%-2d = %08" PRIx32 " R%-2d = %08" PRIx32 "\n",
|
|
r, *gpr, r + 1, *(gpr + 1), r + 2,
|
|
*(gpr + 2), r + 3, *(gpr + 3));
|
|
}
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_hwbreak_insert(void)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
/*
|
|
* Do nothing, load on exit of the exception handler.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_hwbreak_remove(void)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
|
|
bool insert,
|
|
uintptr_t addr,
|
|
DB_UINT kind)
|
|
{
|
|
target_printk("]] rtems-db: powerpc: %s\n", __func__);
|
|
/*
|
|
* To do.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
|
|
{
|
|
/*
|
|
* Flush the data cache and invalidate the instruction cache.
|
|
*/
|
|
rtems_cache_flush_multiple_data_lines(
|
|
swbreak->address,
|
|
sizeof( breakpoint )
|
|
);
|
|
rtems_cache_instruction_sync_after_code_change(
|
|
swbreak->address,
|
|
sizeof( breakpoint )
|
|
);
|
|
return 0;
|
|
}
|