/* * 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 #include #include #include #include #include "rtems-debugger-target.h" #include "rtems-debugger-threads.h" #include /* * 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 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; }