diff options
author | Juli Mallett <jmallett@FreeBSD.org> | 2010-11-28 06:20:41 +0000 |
---|---|---|
committer | Juli Mallett <jmallett@FreeBSD.org> | 2010-11-28 06:20:41 +0000 |
commit | 7a1e830cbcd5d33aa7fcdd8732e6ea26510508fd (patch) | |
tree | b8a47c127e9100c1078d4e43f050dccc3b15f210 /cvmx-debug.c | |
parent | 1c305b501145f696d3597fb9b5b2091caaa6f67c (diff) | |
download | src-7a1e830cbcd5d33aa7fcdd8732e6ea26510508fd.tar.gz src-7a1e830cbcd5d33aa7fcdd8732e6ea26510508fd.zip |
Import Cavium Octeon SDK 2.0 Simple Executive from cnusers.org.vendor/octeon-sdk/2.0.0
Notes
Notes:
svn path=/vendor-sys/octeon-sdk/dist/; revision=215976
svn path=/vendor-sys/octeon-sdk/2.0.0/; revision=215977; tag=vendor/octeon-sdk/2.0.0
Diffstat (limited to 'cvmx-debug.c')
-rw-r--r-- | cvmx-debug.c | 1436 |
1 files changed, 1436 insertions, 0 deletions
diff --git a/cvmx-debug.c b/cvmx-debug.c new file mode 100644 index 000000000000..e615cbc343cb --- /dev/null +++ b/cvmx-debug.c @@ -0,0 +1,1436 @@ +/***********************license start*************** + * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights + * reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + + * * Neither the name of Cavium Networks nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + + * This Software, including technical data, may be subject to U.S. export control + * laws, including the U.S. Export Administration Act and its associated + * regulations, and may be subject to export or import regulations in other + * countries. + + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" + * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR + * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO + * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR + * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM + * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, + * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF + * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR + * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. + ***********************license end**************************************/ + + +/* + * @file + * + * Interface to debug exception handler + * + * <hr>$Revision: 50060 $<hr> + */ + +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL +#include <linux/module.h> +#include <asm/octeon/octeon.h> +#include <asm/octeon/cvmx.h> +#include <asm/octeon/cvmx-debug.h> +#include <asm/octeon/cvmx-core.h> +#include <asm/octeon/cvmx-bootmem.h> +#include <asm/octeon/octeon-boot-info.h> +#else +#include <stdint.h> +#include "executive-config.h" +#include "cvmx.h" +#include "cvmx-debug.h" +#include "cvmx-bootmem.h" +#include "cvmx-core.h" +#include "cvmx-coremask.h" + +#ifndef __OCTEON_NEWLIB__ +#include "../../bootloader/u-boot/include/octeon_mem_map.h" +#else +#include "octeon-boot-info.h" +#endif + +#endif + +#ifdef CVMX_DEBUG_LOGGING +# undef CVMX_DEBUG_LOGGING +# define CVMX_DEBUG_LOGGING 1 +#else +# define CVMX_DEBUG_LOGGING 0 +#endif + +#ifndef CVMX_DEBUG_ATTACH +# define CVMX_DEBUG_ATTACH 1 +#endif + +#define CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_STATUS (0xFFFFFFFFFF301000ull) +#define CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ADDRESS(num) (0xFFFFFFFFFF301100ull + 0x100 * (num)) +#define CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ADDRESS_MASK(num) (0xFFFFFFFFFF301108ull + 0x100 * (num)) +#define CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ASID(num) (0xFFFFFFFFFF301110ull + 0x100 * (num)) +#define CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_CONTROL(num) (0xFFFFFFFFFF301118ull + 0x100 * (num)) + +#define CVMX_DEBUG_HW_DATA_BREAKPOINT_STATUS (0xFFFFFFFFFF302000ull) +#define CVMX_DEBUG_HW_DATA_BREAKPOINT_ADDRESS(num) (0xFFFFFFFFFF302100ull + 0x100 * (num)) +#define CVMX_DEBUG_HW_DATA_BREAKPOINT_ADDRESS_MASK(num) (0xFFFFFFFFFF302108ull + 0x100 * (num)) +#define CVMX_DEBUG_HW_DATA_BREAKPOINT_ASID(num) (0xFFFFFFFFFF302110ull + 0x100 * (num)) +#define CVMX_DEBUG_HW_DATA_BREAKPOINT_CONTROL(num) (0xFFFFFFFFFF302118ull + 0x100 * (num)) + +#define ERET_INSN 0x42000018U /* Hexcode for eret */ +#define ISR_DELAY_COUNTER 120000000 /* Could be tuned down */ + +extern cvmx_debug_comm_t cvmx_debug_uart_comm; +extern cvmx_debug_comm_t cvmx_debug_remote_comm; +static const cvmx_debug_comm_t *cvmx_debug_comms[COMM_SIZE] = {&cvmx_debug_uart_comm, &cvmx_debug_remote_comm}; + + + +static cvmx_debug_globals_t *cvmx_debug_globals; + +/** + * @file + * + */ + +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL +uint64_t __cvmx_debug_save_regs_area[32]; + +volatile uint64_t __cvmx_debug_mode_exception_ignore; +volatile uint64_t __cvmx_debug_mode_exception_occured; + +static char cvmx_debug_stack[8*1024] __attribute ((aligned (16))); +char *__cvmx_debug_stack_top = &cvmx_debug_stack[8*1024]; + +#ifndef __OCTEON_NEWLIB__ +extern int cvmx_interrupt_in_isr; +#else +#define cvmx_interrupt_in_isr 0 +#endif + +#else +uint64_t __cvmx_debug_save_regs_area_all[OCTEON_NUM_CORES][32]; +#define __cvmx_debug_save_regs_area __cvmx_debug_save_regs_area_all[cvmx_get_core_num()] + +volatile uint64_t __cvmx_debug_mode_exception_ignore_all[OCTEON_NUM_CORES]; +#define __cvmx_debug_mode_exception_ignore __cvmx_debug_mode_exception_ignore_all[cvmx_get_core_num()] +volatile uint64_t __cvmx_debug_mode_exception_occured_all[OCTEON_NUM_CORES]; +#define __cvmx_debug_mode_exception_occured __cvmx_debug_mode_exception_occured_all[cvmx_get_core_num()] + +static char cvmx_debug_stack_all[OCTEON_NUM_CORES][8*1024] __attribute ((aligned (16))); +char *__cvmx_debug_stack_top_all[OCTEON_NUM_CORES]; + +#define cvmx_interrupt_in_isr 0 + +#endif + + +static inline uint32_t cvmx_debug_core_mask(void) +{ +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL +#ifdef __OCTEON_NEWLIB__ + extern int __octeon_core_mask; + return __octeon_core_mask; +#endif +return cvmx_sysinfo_get()->core_mask; +#else +return octeon_get_boot_coremask (); +#endif +} + +static inline void cvmx_debug_update_state(cvmx_debug_state_t state) +{ + memcpy(cvmx_debug_globals->state, &state, sizeof(cvmx_debug_state_t)); +} + +static inline cvmx_debug_state_t cvmx_debug_get_state(void) +{ + cvmx_debug_state_t state; + memcpy(&state, cvmx_debug_globals->state, sizeof(cvmx_debug_state_t)); + return state; +} + +static void cvmx_debug_printf(char *format, ...) __attribute__((format(__printf__, 1, 2))); +static void cvmx_debug_printf(char *format, ...) +{ + va_list ap; + + if (!CVMX_DEBUG_LOGGING) + return; + + va_start(ap, format); + cvmx_dvprintf(format, ap); + va_end(ap); +} + +static inline int __cvmx_debug_in_focus(cvmx_debug_state_t state, unsigned core) +{ + return state.focus_core == core; +} + +static void cvmx_debug_install_handler(unsigned core) +{ + extern void __cvmx_debug_handler_stage2(void); + int32_t *trampoline = CASTPTR(int32_t, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, BOOTLOADER_DEBUG_TRAMPOLINE_CORE)); + trampoline += core; + + *trampoline = (int32_t)(long)&__cvmx_debug_handler_stage2; + + cvmx_debug_printf("Debug handled installed on core %d at %p\n", core, trampoline); +} + +static int cvmx_debug_enabled(void) +{ + return cvmx_debug_booted() || CVMX_DEBUG_ATTACH; +} + +static void cvmx_debug_init_globals(void) +{ + int toclear = 0; + uint64_t phys; + void *a; + + if (cvmx_debug_globals) + return; + + if (cvmx_get_core_num() != 0) + { + volatile size_t i; + /* Delay here just enough for the writing of the version. */ + for(i = 0; i < sizeof(cvmx_debug_globals_t)/2 + 8; i++) + ; + } + + a = cvmx_bootmem_alloc_named(sizeof(cvmx_debug_globals_t), 8, CVMX_DEBUG_GLOBALS_BLOCK_NAME); + if (a) + { + phys = cvmx_ptr_to_phys(a); + toclear = 1; + } + else + { + const cvmx_bootmem_named_block_desc_t *debug_globals_nblk; + debug_globals_nblk = cvmx_bootmem_find_named_block (CVMX_DEBUG_GLOBALS_BLOCK_NAME); + phys = debug_globals_nblk->base_addr; + } + cvmx_debug_globals = CASTPTR(cvmx_debug_globals_t, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, phys)); + cvmx_debug_printf("Debug named block at %p\n", cvmx_debug_globals); + if (toclear) + cvmx_debug_printf("Debug named block cleared\n"); + + if (toclear) + { + memset (cvmx_debug_globals, 0, sizeof(cvmx_debug_globals_t)); + cvmx_debug_globals->version = CVMX_DEBUG_GLOBALS_VERSION; + cvmx_debug_globals->tlb_entries = cvmx_core_get_tlb_entries(); + } + else + { + volatile size_t i; + /* Delay here just enough for the writing of the version. */ + for(i = 0; i < sizeof(cvmx_debug_globals_t) + 8; i++) + ; + } +} + + +static void cvmx_debug_globals_check_version(void) +{ + if (cvmx_debug_globals->version != CVMX_DEBUG_GLOBALS_VERSION) + { + cvmx_dprintf("Wrong version on the globals struct spinining; expected %d, got: %d.\n", (int)CVMX_DEBUG_GLOBALS_VERSION, (int)(cvmx_debug_globals->version)); +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL + panic("Wrong version.\n"); +#endif + while (1) + ; + } +} + +static inline volatile cvmx_debug_core_context_t *cvmx_debug_core_context(void); +static inline void cvmx_debug_save_core_context(volatile cvmx_debug_core_context_t *context); + +void cvmx_debug_init(void) +{ + cvmx_debug_state_t state; + int core; + const cvmx_debug_comm_t *comm; + cvmx_spinlock_t *lock; + unsigned int coremask = cvmx_debug_core_mask(); + + if (!cvmx_debug_enabled()) + return; + + cvmx_debug_init_globals(); + +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL + // Put a barrier until all cores have got to this point. + cvmx_coremask_barrier_sync(coremask); +#endif + cvmx_debug_globals_check_version(); + + + comm = cvmx_debug_comms[cvmx_debug_globals->comm_type]; + lock = &cvmx_debug_globals->lock; + + core = cvmx_get_core_num(); +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL + /* Install the debugger handler on the cores. */ + { + int core1 = 0; + for (core1 = 0; core1 < OCTEON_NUM_CORES; core1++) + { + if ((1<<core1) & coremask) + cvmx_debug_install_handler(core1); + } + } +#else + cvmx_debug_install_handler(core); +#endif + + if (comm->init) + comm->init(); + + { + cvmx_spinlock_lock(lock); + state = cvmx_debug_get_state(); +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL + state.known_cores |= coremask; + state.core_finished &= ~coremask; +#else + state.known_cores |= (1 << core); + state.core_finished &= ~(1 << core); +#endif + cvmx_debug_update_state(state); + cvmx_spinlock_unlock(lock); + } + +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL + // Put a barrier until all cores have got to this point. + cvmx_coremask_barrier_sync(coremask); + + if (cvmx_coremask_first_core(coremask)) +#endif + { + cvmx_debug_printf("cvmx_debug_init core: %d\n", core); + state = cvmx_debug_get_state(); + state.focus_core = core; + state.active_cores = state.known_cores; + state.focus_switch = 1; + state.step_isr = 1; + cvmx_debug_printf("Known cores at init: 0x%x\n", (int)state.known_cores); + cvmx_debug_update_state(state); + + /* Initialize __cvmx_debug_stack_top_all. */ +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL + { + int i; + for (i = 0; i < OCTEON_NUM_CORES; i++) + __cvmx_debug_stack_top_all[i] = &cvmx_debug_stack_all[i][8*1024]; + } +#endif + cvmx_debug_globals->init_complete = 1; + CVMX_SYNCW; + } + while (!cvmx_debug_globals->init_complete) + { + /* Spin waiting for init to complete */ + } + + if (cvmx_debug_booted()) + cvmx_debug_trigger_exception(); + + /* Install the break handler after might tripper the debugger exception. */ +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL + if (cvmx_coremask_first_core(coremask)) +#endif + { + if (comm->install_break_handler) + comm->install_break_handler(); + } +} + +static int cvmx_debug_putpacket_noformat(char *packet); + +static __attribute__ ((format (printf, 1, 2))) int cvmx_debug_putpacket(char *format, ...) +{ + va_list ap; + size_t n; + char packet[CVMX_DEBUG_MAX_RESPONSE_SIZE]; + + if (cvmx_debug_comms[cvmx_debug_globals->comm_type]->putpacket == NULL) + return 0; + + va_start(ap, format); + n = vsnprintf(packet, sizeof(packet), format, ap); + va_end(ap); + + if (n >= sizeof(packet)) + { + cvmx_debug_printf("packet truncated (needed %d bytes): %s\n", (int)n, packet); + return 0; + } + return cvmx_debug_putpacket_noformat(packet); +} + +static int cvmx_debug_putpacket_noformat(char *packet) +{ + if (cvmx_debug_comms[cvmx_debug_globals->comm_type]->putpacket == NULL) + return 0; + cvmx_debug_printf("Reply: %s\n", packet); + return cvmx_debug_comms[cvmx_debug_globals->comm_type]->putpacket(packet); +} + +static int cvmx_debug_active_core(cvmx_debug_state_t state, int core) +{ + return state.active_cores & (1 << core); +} + +static volatile cvmx_debug_core_context_t *cvmx_debug_core_context(void) +{ + return &cvmx_debug_globals->contextes[cvmx_get_core_num()]; +} + +static volatile uint64_t *cvmx_debug_regnum_to_context_ref(int regnum, volatile cvmx_debug_core_context_t *context) +{ + /* Must be kept in sync with mips_octeon_reg_names in gdb/mips-tdep.c. */ + if (regnum < 32) + return &context->regs[regnum]; + switch (regnum) + { + case 32: return &context->cop0.status; + case 33: return &context->lo; + case 34: return &context->hi; + case 35: return &context->cop0.badvaddr; + case 36: return &context->cop0.cause; + case 37: return &context->cop0.depc; + default: return NULL; + } +} + +static int cvmx_debug_probe_load(unsigned char *ptr, unsigned char *result) +{ + volatile unsigned char *p = ptr; + int ok; + unsigned char tem; + + { + __cvmx_debug_mode_exception_ignore = 1; + __cvmx_debug_mode_exception_occured = 0; + /* We don't handle debug-mode exceptions in delay slots. Avoid them. */ + asm volatile (".set push \n\t" + ".set noreorder \n\t" + "nop \n\t" + "lbu %0, %1 \n\t" + "nop \n\t" + ".set pop" : "=r"(tem) : "m"(*p)); + ok = __cvmx_debug_mode_exception_occured == 0; + __cvmx_debug_mode_exception_ignore = 0; + __cvmx_debug_mode_exception_occured = 0; + *result = tem; + } + return ok; +} + +static int cvmx_debug_probe_store(unsigned char *ptr) +{ + volatile unsigned char *p = ptr; + int ok; + + __cvmx_debug_mode_exception_ignore = 1; + __cvmx_debug_mode_exception_occured = 0; + /* We don't handle debug-mode exceptions in delay slots. Avoid them. */ + asm volatile (".set push \n\t" + ".set noreorder \n\t" + "nop \n\t" + "sb $0, %0 \n\t" + "nop \n\t" + ".set pop" : "=m"(*p)); + ok = __cvmx_debug_mode_exception_occured == 0; + + __cvmx_debug_mode_exception_ignore = 0; + __cvmx_debug_mode_exception_occured = 0; + return ok; +} + +/* Put the hex value of t into str. */ +static void strhex(char *str, unsigned char t) +{ + char a[] = "0123456789ABCDEF"; + str[0] = a[(t>>4)]; + str[1] = a[t&0xF]; + str[2] = 0; +} + +/** + * Initialize the performance counter control registers. + * + */ +static void cvmx_debug_set_perf_control_reg (int perf_event, int perf_counter) +{ + volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context(); + cvmx_core_perf_control_t control; + + control.u32 = 0; + control.s.u = 1; + control.s.s = 1; + control.s.k = 1; + control.s.ex = 1; + control.s.w = 1; + control.s.m = 1 - perf_counter; + control.s.event = perf_event; + + context->cop0.perfctrl[perf_counter] = control.u32; +} + +static cvmx_debug_command_t cvmx_debug_process_packet(char *packet) +{ + const char *buf = packet; + cvmx_debug_command_t result = COMMAND_NOP; + cvmx_debug_state_t state = cvmx_debug_get_state(); + + /* A one letter command code represents what to do. */ + switch (*buf++) + { + case '?': /* What protocol do I speak? */ + cvmx_debug_putpacket_noformat("S0A"); + break; + + case '\003': /* Control-C */ + cvmx_debug_putpacket_noformat("T9"); + break; + + case 'F': /* Change the focus core */ + { + int core; + sscanf(buf, "%x", &core); + + /* Only cores in the exception handler may become the focus. + If a core not in the exception handler got focus the + debugger would hang since nobody would talk to it. */ + if (state.handler_cores & (1 << core)) + { + /* Focus change reply must be sent before the focus + changes. Otherwise the new focus core will eat our ACK + from the debugger. */ + cvmx_debug_putpacket("F%02x", core); + cvmx_debug_comms[cvmx_debug_globals->comm_type]->change_core(state.focus_core, core); + state.focus_core = core; + cvmx_debug_update_state(state); + break; + } + else + cvmx_debug_putpacket_noformat("!Core is not in the exception handler. Focus not changed."); + /* Nothing changed, so we send back the old value */ + } + /* fall through */ + case 'f': /* Get the focus core */ + cvmx_debug_putpacket("F%02x", (unsigned)state.focus_core); + break; + + case 'J': /* Set the flag for skip-over-isr in Single-Stepping mode */ + { + if (*buf == '1') + state.step_isr = 1; /* Step in ISR */ + else + state.step_isr = 0; /* Step over ISR */ + cvmx_debug_update_state(state); + } + /* Fall through. The reply to the set step-isr command is the + same as the get step-isr command */ + + case 'j': /* Reply with step_isr status */ + cvmx_debug_putpacket("J%x", (unsigned)state.step_isr); + break; + + + case 'I': /* Set the active cores */ + { + long long active_cores; + sscanf(buf, "%llx", &active_cores); + /* Limit the active mask to the known to exist cores */ + state.active_cores = active_cores & state.known_cores; + + /* Lazy user hack to have 0 be all cores */ + if (state.active_cores == 0) + state.active_cores = state.known_cores; + + /* The focus core must be in the active_cores mask */ + if ((state.active_cores & (1 << state.focus_core)) == 0) + { + cvmx_debug_putpacket_noformat("!Focus core was added to the masked."); + state.active_cores |= 1 << state.focus_core; + } + + cvmx_debug_update_state(state); + } + /* Fall through. The reply to the set active cores command is the + same as the get active cores command */ + + case 'i': /* Get the active cores */ + cvmx_debug_putpacket("I%llx", (long long) state.active_cores); + break; + + case 'A': /* Setting the step mode all or one */ + { + if (*buf == '1') + state.step_all = 1; /* A step or continue will start all cores */ + else + state.step_all = 0; /* A step or continue only affects the focus core */ + cvmx_debug_update_state(state); + } + /* Fall through. The reply to the set step-all command is the + same as the get step-all command */ + + case 'a': /* Getting the current step mode */ + cvmx_debug_putpacket("A%x", (unsigned)state.step_all); + break; + + case 'g': /* read a register from global place. */ + { + volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context(); + int regno; + volatile uint64_t *reg; + + /* Get the register number to read */ + sscanf(buf, "%x", ®no); + + reg = cvmx_debug_regnum_to_context_ref(regno, context); + if (!reg) + cvmx_debug_printf("Register #%d is not valid\n", regno); + cvmx_debug_putpacket("%llx", (unsigned long long) *reg); + } + break; + + case 'G': /* set the value of a register. */ + { + volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context(); + int regno; + volatile uint64_t *reg; + long long value; + + /* Get the register number to read */ + if (sscanf(buf, "%x,%llx", ®no, &value) != 2) + { + cvmx_debug_printf("G packet corrupt: %s\n", buf); + goto error_packet; + } + + reg = cvmx_debug_regnum_to_context_ref(regno, context); + if (!reg) + { + cvmx_debug_printf("Register #%d is not valid\n", regno); + goto error_packet; + } + *reg = value; + } + break; + + case 'm': /* Memory read. mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + { + long long addr, i, length; + unsigned char *ptr; + char *reply; + + if (sscanf(buf, "%llx,%llx", &addr, &length) != 2) + { + cvmx_debug_printf("m packet corrupt: %s\n", buf); + goto error_packet; + } + if (length >= 1024) + { + cvmx_debug_printf("m packet length out of range: %lld\n", length); + goto error_packet; + } + + reply = __builtin_alloca(length * 2 + 1); + ptr = (unsigned char *)(long)addr; + for (i = 0; i < length; i++) + { + /* Probe memory. If not accessible fail. */ + unsigned char t; + if (!cvmx_debug_probe_load(&ptr[i], &t)) + goto error_packet; + strhex(&reply[i * 2], t); + } + cvmx_debug_putpacket_noformat(reply); + } + break; + + case 'M': /* Memory write. MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + { + long long addr, i, length; + unsigned char *ptr; + char value[1024]; + + if (sscanf(buf, "%llx,%llx:%1024s", &addr, &length, value) != 3) + { + cvmx_debug_printf("M packet corrupt: %s\n", buf); + goto error_packet; + } + + ptr = (unsigned char *)(long)addr; + for (i = 0; i < length; i++) + { + int c; + int n; + char tempstr[3] = {0, 0, 0}; + memcpy (tempstr, &value[i * 2], 2); + + n = sscanf(tempstr, "%2x", &c); + if (n != 1) + { + cvmx_debug_printf("M packet corrupt: %s\n", &value[i * 2]); + goto error_packet; + } + /* Probe memory. If not accessible fail. */ + if (!cvmx_debug_probe_store(&ptr[i])) + { + cvmx_debug_printf("M cannot write: %p\n", &ptr[i]); + goto error_packet; + } + ptr[i] = c; + } + cvmx_debug_putpacket_noformat("+"); + } + break; + + case 'e': /* Set/get performance counter events. e[1234]XX..X: [01] + is the performance counter to set X is the performance + event. [34] is to get the same thing. */ + { + int perf_event = 0; + int counter, encoded_counter; + volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context(); + sscanf(buf, "%1d%x", &encoded_counter, &perf_event); + + switch (encoded_counter) + { + case 1: /* Set performance counter0 event. */ + case 2: /* Set performance counter1 event. */ + + counter = encoded_counter - 1; + context->cop0.perfval[counter] = 0; + cvmx_debug_set_perf_control_reg(perf_event, counter); + break; + + case 3: /* Get performance counter0 event. */ + case 4: /* Get performance counter1 event. */ + { + cvmx_core_perf_control_t c; + counter = encoded_counter - 3; + /* Pass performance counter0 event and counter to + the debugger. */ + c.u32 = context->cop0.perfctrl[counter]; + cvmx_debug_putpacket("%llx,%llx", (long long) context->cop0.perfval[counter], (long long) c.s.event); + } + break; + } + } + break; + +#if 0 + case 't': /* Return the trace buffer read data register contents. */ + { + uint64_t tra_data; + uint64_t tra_ctl; + char tmp[64]; + + /* If trace buffer is disabled no trace data information is available. */ + if ((tra_ctl & 0x1) == 0) + { + cvmx_debug_putpacket_noformat("!Trace buffer not enabled\n"); + cvmx_debug_putpacket_noformat("t"); + } + else + { + cvmx_debug_putpacket_noformat("!Trace buffer is enabled\n"); + tra_data = cvmx_read_csr(OCTEON_TRA_READ_DATA); + mem2hex (&tra_data, tmp, 8); + strcpy (debug_output_buffer, "t"); + strcat (debug_output_buffer, tmp); + cvmx_debug_putpacket_noformat(debug_output_buffer); + } + } + break; +#endif + + case 'Z': /* Insert hardware breakpoint: Z[di]NN..N,AA.A, [di] data or + instruction, NN..Nth at address AA..A */ + { + enum type + { + WP_LOAD = 1, + WP_STORE = 2, + WP_ACCESS = 3 + }; + + int num, size; + long long addr; + enum type type; + char bp_type; + const int BE = 1, TE = 4; + int n; + volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context(); + + n = sscanf(buf, "%c%x,%llx,%x,%x", &bp_type, &num, &addr, &size, &type); + switch (bp_type) + { + case 'i': // Instruction hardware breakpoint + if (n != 3 || num > 4) + { + cvmx_debug_printf("Z packet corrupt: %s\n", buf); + goto error_packet; + } + + context->hw_ibp.address[num] = addr; + context->hw_ibp.address_mask[num] = 0; + context->hw_ibp.asid[num] = 0; + context->hw_ibp.control[num] = BE | TE; + break; + + case 'd': // Data hardware breakpoint + { + uint64_t dbc = 0xff0 | BE | TE; + uint64_t dbm; + if (n != 5 || num > 4) + { + cvmx_debug_printf("Z packet corrupt: %s\n", buf); + goto error_packet; + } + + /* Set DBC[BE,TE,BLM]. */ + context->hw_dbp.address[num] = addr; + context->hw_dbp.asid[num] = 0; + + dbc |= type == WP_STORE ? 0x1000 : type == WP_LOAD ? 0x2000 : 0; + /* Mask the bits depending on the size for + debugger to stop while accessing parts of the + memory location. */ + dbm = (size == 8) ? 0x7 : ((size == 4) ? 3 + : (size == 2) ? 1 : 0); + context->hw_dbp.address_mask[num] = dbm; + context->hw_dbp.control[num] = dbc; + break; + } + default: + cvmx_debug_printf("z packet corrupt: %s\n", buf); + goto error_packet; + } + } + break; + + case 'z': /* Remove hardware breakpoint: z[di]NN..N remove NN..Nth +breakpoint. */ + { + int num; + char bp_type; + volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context(); + + if (sscanf(buf, "%c%x", &bp_type, &num) != 2 || num > 4) + { + cvmx_debug_printf("z packet corrupt: %s\n", buf); + goto error_packet; + } + + switch (bp_type) + { + case 'i': // Instruction hardware breakpoint + context->hw_ibp.address[num] = 0; + context->hw_ibp.address_mask[num] = 0; + context->hw_ibp.asid[num] = 0; + context->hw_ibp.control[num] = 0; + break; + case 'd': // Data hardware breakpoint + context->hw_dbp.address[num] = 0; + context->hw_dbp.address_mask[num] = 0; + context->hw_dbp.asid[num] = 0; + context->hw_dbp.control[num] = 0; + break; + default: + cvmx_debug_printf("z packet corrupt: %s\n", buf); + goto error_packet; + } + } + break; + + case 's': /* Single step. sAA..AA Step one instruction from AA..AA (optional) */ + result = COMMAND_STEP; + break; + + case 'c': /* Continue. cAA..AA Continue at address AA..AA (optional) */ + result = COMMAND_CONTINUE; + break; + + case '+': /* Don't know. I think it is a communications sync */ + /* Ignoring this command */ + break; + + default: + cvmx_debug_printf("Unknown debug command: %s\n", buf - 1); +error_packet: + cvmx_debug_putpacket_noformat("-"); + break; + } + + return result; +} + +static cvmx_debug_command_t cvmx_debug_process_next_packet(void) +{ + char packet[CVMX_DEBUG_MAX_REQUEST_SIZE]; + if (cvmx_debug_comms[cvmx_debug_globals->comm_type]->getpacket(packet, CVMX_DEBUG_MAX_REQUEST_SIZE)) + { + cvmx_debug_printf("Request: %s\n", packet); + return cvmx_debug_process_packet(packet); + } + return COMMAND_NOP; +} + +/* If a core isn't in the active core mask we need to start him up again. We + can only do this if the core didn't hit a breakpoint or single step. If the + core hit CVMX_CIU_DINT interrupt (generally happens when while executing + _exit() at the end of the program). Remove the core from known cores so + that when the cores in active core mask are done executing the program, the + focus will not be transfered to this core. */ + +static int cvmx_debug_stop_core(cvmx_debug_state_t state, unsigned core, cvmx_debug_register_t *debug_reg, int proxy) +{ + if (!cvmx_debug_active_core(state, core) && !debug_reg->s.dbp && !debug_reg->s.dss && (debug_reg->s.dint != 1)) + { + debug_reg->s.sst = 0; + cvmx_debug_printf("Core #%d not in active cores, continuing.\n", core); + return 0; + } + if ((state.core_finished & (1<<core)) && proxy) + return 0; + return 1; +} + +/* check to see if current exc is single-stepped and that no other exc + was also simultaneously noticed. */ +static int cvmx_debug_single_step_exc(cvmx_debug_register_t *debug_reg) +{ + if (debug_reg->s.dss && !debug_reg->s.dib && !debug_reg->s.dbp && !debug_reg->s.ddbs && !debug_reg->s.ddbl) + return 1; + return 0; +} + +static void cvmx_debug_set_focus_core(cvmx_debug_state_t *state, int core) +{ + if (state->ever_been_in_debug) + cvmx_debug_putpacket("!Core %2x taking focus.", core); + cvmx_debug_comms[cvmx_debug_globals->comm_type]->change_core (state->focus_core, core); + state->focus_core = core; +} + +static void cvmx_debug_may_elect_as_focus_core(cvmx_debug_state_t *state, int core, cvmx_debug_register_t *debug_reg) +{ + /* If another core has already elected itself as the focus core, we're late. */ + if (state->handler_cores & (1 << state->focus_core)) + return; + + /* If we hit a breakpoint, elect ourselves. */ + if (debug_reg->s.dib || debug_reg->s.dbp || debug_reg->s.ddbs || debug_reg->s.ddbl) + cvmx_debug_set_focus_core(state, core); + + /* It is possible the focus core has completed processing and exited the + program. When this happens the focus core will not be in + known_cores. If this is the case we need to elect a new focus. */ + if ((state->known_cores & (1 << state->focus_core)) == 0) + cvmx_debug_set_focus_core(state, core); +} + +static void cvmx_debug_send_stop_reason(cvmx_debug_register_t *debug_reg, volatile cvmx_debug_core_context_t *context) +{ + /* Handle Debug Data Breakpoint Store/Load Exception. */ + if (debug_reg->s.ddbs || debug_reg->s.ddbl) + cvmx_debug_putpacket("T8:%x", (int) context->hw_dbp.status); + else + cvmx_debug_putpacket_noformat("T9"); +} + + +static void cvmx_debug_clear_status(volatile cvmx_debug_core_context_t *context) +{ + /* SW needs to clear the BreakStatus bits after a watchpoint is hit or on + reset. */ + context->hw_dbp.status &= ~0x3fff; + + /* Clear MCD0, which is write-1-to-clear. */ + context->cop0.multicoredebug |= 1; +} + +static void cvmx_debug_sync_up_cores(void) +{ + cvmx_debug_state_t state; + do { + state = cvmx_debug_get_state(); + } while (state.step_all && state.handler_cores != 0); +} + +/* Delay the focus core a little if it is likely another core needs to steal + focus. Once we enter the main loop focus can't be stolen */ +static void cvmx_debug_delay_focus_core(cvmx_debug_state_t state, unsigned core, cvmx_debug_register_t *debug_reg) +{ + volatile int i; + if (debug_reg->s.dss || debug_reg->s.dbp || core != state.focus_core) + return; + for (i = 0; i < 24000; i++) + { + asm volatile (".set push \n\t" + ".set noreorder \n\t" + "nop \n\t" + "nop \n\t" + "nop \n\t" + "nop \n\t" + ".set pop"); + /* Spin giving the breakpoint core time to steal focus */ + } + +} + +/* If this core was single-stepping in a group, + && it was not the last focus-core, + && last focus-core happens to be inside an ISR, blocking focus-switch + then burn some cycles, to avoid unnecessary focus toggles. */ +static void cvmx_debug_delay_isr_core(unsigned core, uint32_t depc, int single_stepped_exc_only, + cvmx_debug_state_t state) +{ + volatile uint64_t i; + if(!single_stepped_exc_only || state.step_isr || core == state.focus_core || state.focus_switch) + return; + + cvmx_debug_printf ("Core #%u spinning for focus at 0x%x\n", core, (unsigned int)depc); + + for(i = ISR_DELAY_COUNTER; i > 0 ; i--) + { + state = cvmx_debug_get_state(); + /* Spin giving the focus core time to service ISR */ + /* But cut short the loop, if we can. Shrink down i, only once. */ + if (i > 600000 && state.focus_switch) + i = 500000; + } + +} + +static int cvmx_debug_perform_proxy(cvmx_debug_register_t *debug_reg, volatile cvmx_debug_core_context_t *context) +{ + unsigned core = cvmx_get_core_num(); + cvmx_debug_state_t state = cvmx_debug_get_state(); + cvmx_debug_command_t command = COMMAND_NOP; + int single_stepped_exc_only = cvmx_debug_single_step_exc (debug_reg); + + /* All cores should respect the focus core if it has to + stop focus switching while servicing an interrupt. + If the system is single-stepping, then the following + code path is valid. If the current core tripped on a + break-point or some other error while going through + an ISR, then we shouldn't be returning unconditionally. + In that case (non-single-step case) we must enter + the debugger exception stub fully. */ + if (!state.step_isr && (cvmx_interrupt_in_isr || (context->cop0.status & 0x2ULL)) && single_stepped_exc_only) + { + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + state = cvmx_debug_get_state(); + /* If this is the focus core, switch off focus switching + till ISR_DELAY_COUNTER. This will let focus core + keep the focus until the ISR is completed. */ + if(state.focus_switch && core == state.focus_core) + { + cvmx_debug_printf ("Core #%u stopped focus stealing at 0x%llx\n", core, (unsigned long long)context->cop0.depc); + state.focus_switch = 0; + } + /* Alow other cores to steal focus. + Focus core has completed ISR. */ + if (*(uint32_t*)((__SIZE_TYPE__)context->cop0.depc) == ERET_INSN && core == state.focus_core) + { + cvmx_debug_printf ("Core #%u resumed focus stealing at 0x%llx\n", core, (unsigned long long)context->cop0.depc); + state.focus_switch = 1; + } + cvmx_debug_update_state(state); + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + cvmx_debug_printf ("Core #%u resumed skipping isr.\n", core); + return 0; + } + + /* Delay the focus core a little if it is likely another core needs to + steal focus. Once we enter the main loop focus can't be stolen */ + cvmx_debug_delay_focus_core(state, core, debug_reg); + + cvmx_debug_delay_isr_core (core, context->cop0.depc, single_stepped_exc_only, state); + + /* The following section of code does two critical things. First, it + populates the handler_cores bitmask of all cores in the exception + handler. Only one core at a time can update this field. Second it + changes the focus core if needed. */ + { + cvmx_debug_printf("Core #%d stopped\n", core); + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + state = cvmx_debug_get_state(); + + state.handler_cores |= (1 << core); + cvmx_debug_may_elect_as_focus_core(&state, core, debug_reg); + +/* Push all updates before exiting the critical section */ + state.focus_switch = 1; + cvmx_debug_update_state(state); + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + } + if (__cvmx_debug_in_focus(state, core)) + cvmx_debug_send_stop_reason(debug_reg, context); + + do { + state = cvmx_debug_get_state(); + /* Note the focus core can change in this loop. */ + if (__cvmx_debug_in_focus(state, core)) + { + command = cvmx_debug_process_next_packet(); + state = cvmx_debug_get_state(); + /* When resuming let the other cores resume as well with + step-all. */ + if (command != COMMAND_NOP && state.step_all) + { + state.command = command; + cvmx_debug_update_state(state); + } + } + /* When steping all cores, update the non focus core's command too. */ + else if (state.step_all) + command = state.command; + + /* If we did not get a command and the communication changed return, + we are changing the communications. */ + if (command == COMMAND_NOP && cvmx_debug_globals->comm_changed) + { + /* FIXME, this should a sync not based on cvmx_coremask_barrier_sync. */ +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL + /* Sync up. */ + cvmx_coremask_barrier_sync(state.handler_cores); +#endif + return 1; + } + } while (command == COMMAND_NOP); + + debug_reg->s.sst = command == COMMAND_STEP; + cvmx_debug_printf("Core #%d running\n", core); + + { + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + state = cvmx_debug_get_state(); + state.handler_cores ^= (1 << core); + cvmx_debug_update_state(state); + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + } + + cvmx_debug_sync_up_cores(); + /* Now that all cores are out, reset the command. */ + if (__cvmx_debug_in_focus(state, core)) + { + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + state = cvmx_debug_get_state(); + state.command = COMMAND_NOP; + cvmx_debug_update_state(state); + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + } + return 0; +} + +static void cvmx_debug_save_core_context(volatile cvmx_debug_core_context_t *context) +{ + unsigned i; + memcpy((char *) context->regs, __cvmx_debug_save_regs_area, sizeof(context->regs)); + asm("mflo %0" : "=r"(context->lo)); + asm("mfhi %0" : "=r"(context->hi)); + CVMX_MF_COP0(context->cop0.index, COP0_INDEX); + CVMX_MF_COP0(context->cop0.entrylo[0], COP0_ENTRYLO0); + CVMX_MF_COP0(context->cop0.entrylo[1], COP0_ENTRYLO1); + CVMX_MF_COP0(context->cop0.entryhi, COP0_ENTRYHI); + CVMX_MF_COP0(context->cop0.pagemask, COP0_PAGEMASK); + CVMX_MF_COP0(context->cop0.status, COP0_STATUS); + CVMX_MF_COP0(context->cop0.cause, COP0_CAUSE); + CVMX_MF_COP0(context->cop0.debug, COP0_DEBUG); + CVMX_MF_COP0(context->cop0.multicoredebug, COP0_MULTICOREDEBUG); + CVMX_MF_COP0(context->cop0.perfval[0], COP0_PERFVALUE0); + CVMX_MF_COP0(context->cop0.perfval[1], COP0_PERFVALUE1); + CVMX_MF_COP0(context->cop0.perfctrl[0], COP0_PERFCONTROL0); + CVMX_MF_COP0(context->cop0.perfctrl[1], COP0_PERFCONTROL1); + /* Save DEPC and DESAVE since debug-mode exceptions (see + debug_probe_{load,store}) can clobber these. */ + CVMX_MF_COP0(context->cop0.depc, COP0_DEPC); + CVMX_MF_COP0(context->cop0.desave, COP0_DESAVE); + + context->hw_ibp.status = cvmx_read_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_STATUS); + for (i = 0; i < 4; i++) + { + context->hw_ibp.address[i] = cvmx_read_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ADDRESS(i)); + context->hw_ibp.address_mask[i] = cvmx_read_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ADDRESS_MASK(i)); + context->hw_ibp.asid[i] = cvmx_read_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ASID(i)); + context->hw_ibp.control[i] = cvmx_read_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_CONTROL(i)); + } + + context->hw_dbp.status = cvmx_read_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_STATUS); + for (i = 0; i < 4; i++) + { + context->hw_dbp.address[i] = cvmx_read_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_ADDRESS(i)); + context->hw_dbp.address_mask[i] = cvmx_read_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_ADDRESS_MASK(i)); + context->hw_dbp.asid[i] = cvmx_read_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_ASID(i)); + context->hw_dbp.control[i] = cvmx_read_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_CONTROL(i)); + } + + for (i = 0; i < cvmx_debug_globals->tlb_entries; i++) + { + CVMX_MT_COP0(i, COP0_INDEX); + asm volatile ("tlbr"); + CVMX_MF_COP0(context->tlbs[i].entrylo[0], COP0_ENTRYLO0); + CVMX_MF_COP0(context->tlbs[i].entrylo[1], COP0_ENTRYLO1); + CVMX_MF_COP0(context->tlbs[i].entryhi, COP0_ENTRYHI); + CVMX_MF_COP0(context->tlbs[i].pagemask, COP0_PAGEMASK); + } + CVMX_SYNCW; +} + +static void cvmx_debug_restore_core_context(volatile cvmx_debug_core_context_t *context) +{ + int i; + memcpy(__cvmx_debug_save_regs_area, (char *) context->regs, sizeof(context->regs)); + asm("mtlo %0" :: "r"(context->lo)); + asm("mthi %0" :: "r"(context->hi)); + /* We don't change the TLB so no need to restore it. */ + cvmx_write_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_STATUS, context->hw_dbp.status); + for (i = 0; i < 4; i++) + { + cvmx_write_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_ADDRESS(i), context->hw_dbp.address[i]); + cvmx_write_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_ADDRESS_MASK(i), context->hw_dbp.address_mask[i]); + cvmx_write_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_ASID(i), context->hw_dbp.asid[i]); + cvmx_write_csr(CVMX_DEBUG_HW_DATA_BREAKPOINT_CONTROL(i), context->hw_dbp.control[i]); + } + cvmx_write_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_STATUS, context->hw_ibp.status); + for (i = 0; i < 4; i++) + { + cvmx_write_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ADDRESS(i), context->hw_ibp.address[i]); + cvmx_write_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ADDRESS_MASK(i), context->hw_ibp.address_mask[i]); + cvmx_write_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_ASID(i), context->hw_ibp.asid[i]); + cvmx_write_csr(CVMX_DEBUG_HW_INSTRUCTION_BREAKPOINT_CONTROL(i), context->hw_ibp.control[i]); + } + CVMX_MT_COP0(context->cop0.index, COP0_INDEX); + CVMX_MT_COP0(context->cop0.entrylo[0], COP0_ENTRYLO0); + CVMX_MT_COP0(context->cop0.entrylo[1], COP0_ENTRYLO1); + CVMX_MT_COP0(context->cop0.entryhi, COP0_ENTRYHI); + CVMX_MT_COP0(context->cop0.pagemask, COP0_PAGEMASK); + CVMX_MT_COP0(context->cop0.status, COP0_STATUS); + CVMX_MT_COP0(context->cop0.cause, COP0_CAUSE); + CVMX_MT_COP0(context->cop0.debug, COP0_DEBUG); + CVMX_MT_COP0(context->cop0.multicoredebug, COP0_MULTICOREDEBUG); + CVMX_MT_COP0(context->cop0.perfval[0], COP0_PERFVALUE0); + CVMX_MT_COP0(context->cop0.perfval[1], COP0_PERFVALUE1); + CVMX_MT_COP0(context->cop0.perfctrl[0], COP0_PERFCONTROL0); + CVMX_MT_COP0(context->cop0.perfctrl[1], COP0_PERFCONTROL1); + CVMX_MT_COP0(context->cop0.depc, COP0_DEPC); + CVMX_MT_COP0(context->cop0.desave, COP0_DESAVE); +} + +static inline void cvmx_debug_print_cause(volatile cvmx_debug_core_context_t *context) +{ + if (!CVMX_DEBUG_LOGGING) + return; + if (context->cop0.multicoredebug & 1) + cvmx_dprintf("MCD0 was pulsed\n"); + if (context->cop0.multicoredebug & (1 << 16)) + cvmx_dprintf("Exception %lld in Debug Mode\n", (long long)((context->cop0.debug >> 10) & 0x1f)); + if (context->cop0.debug & (1 << 19)) + cvmx_dprintf("DDBSImpr\n"); + if (context->cop0.debug & (1 << 18)) + cvmx_dprintf("DDBLImpr\n"); + if (context->cop0.debug & (1 << 5)) + cvmx_dprintf("DINT\n"); + if (context->cop0.debug & (1 << 4)) + cvmx_dprintf("Debug Instruction Breakpoint (DIB) exception\n"); + if (context->cop0.debug & (1 << 3)) + cvmx_dprintf("Debug Date Break Store (DDBS) exception\n"); + if (context->cop0.debug & (1 << 2)) + cvmx_dprintf("Debug Date Break Load (DDBL) exception\n"); + if (context->cop0.debug & (1 << 1)) + cvmx_dprintf("Debug Breakpoint (DBp) exception\n"); + if (context->cop0.debug & (1 << 0)) + cvmx_dprintf("Debug Single Step (DSS) exception\n"); +} + +void __cvmx_debug_handler_stage3 (void) +{ + volatile cvmx_debug_core_context_t *context; + int comms_changed = 0; + + cvmx_debug_printf("Entering debug exception handler\n"); + cvmx_debug_printf("Debug named block at %p\n", cvmx_debug_globals); + if (__cvmx_debug_mode_exception_occured) + { + uint64_t depc; + CVMX_MF_COP0(depc, COP0_DEPC); + cvmx_dprintf("Unexpected debug-mode exception occured at 0x%llx, 0x%llx spinning\n", (long long) depc, (long long)(__cvmx_debug_mode_exception_occured)); +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL + panic("Unexpected debug-mode exception occured at 0x%llx, 0x%llx\n", (long long) depc, (long long)(__cvmx_debug_mode_exception_occured)); +#endif + while (1) + ; + } + + context = cvmx_debug_core_context(); + cvmx_debug_save_core_context(context); + + { + cvmx_debug_state_t state; + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + state = cvmx_debug_get_state(); + state.ever_been_in_debug = 1; + cvmx_debug_update_state (state); + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + } + cvmx_debug_print_cause(context); + + do + { + int needs_proxy; + comms_changed = 0; + /* If the communication changes, change it. */ + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + if (cvmx_debug_globals->comm_changed) + { + cvmx_debug_printf("Communication changed: %d\n", (int)cvmx_debug_globals->comm_changed); + if (cvmx_debug_globals->comm_changed > COMM_SIZE) + { + cvmx_dprintf("Unknown communication spinning: %lld > %d.\n", (long long)cvmx_debug_globals->comm_changed, (int)(COMM_SIZE)); +#ifdef CVMX_BUILD_FOR_LINUX_KERNEL + panic("Unknown communication.\n"); +#endif + while (1) + ; + } + cvmx_debug_globals->comm_type = cvmx_debug_globals->comm_changed - 1; + cvmx_debug_globals->comm_changed = 0; + } + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + needs_proxy = cvmx_debug_comms[cvmx_debug_globals->comm_type]->needs_proxy; + + { + cvmx_debug_register_t debug_reg; + cvmx_debug_state_t state; + unsigned core = cvmx_get_core_num(); + + state = cvmx_debug_get_state(); + debug_reg.u64 = context->cop0.debug; + /* All cores stop on any exception. See if we want nothing from this and + it should resume. This needs to be done for non proxy based debugging + so that some non active-cores can control the other cores. */ + if (!cvmx_debug_stop_core(state, core, &debug_reg, needs_proxy)) + { + context->cop0.debug = debug_reg.u64; + break; + } + } + + if (needs_proxy) + { + cvmx_debug_register_t debug_reg; + debug_reg.u64 = context->cop0.debug; + cvmx_debug_printf("Starting to proxy\n"); + comms_changed = cvmx_debug_perform_proxy(&debug_reg, context); + context->cop0.debug = debug_reg.u64; + } + else + { + cvmx_debug_printf("Starting to wait for remote host\n"); + cvmx_debug_comms[cvmx_debug_globals->comm_type]->wait_for_resume(context, cvmx_debug_get_state()); + } + } while (comms_changed); + + cvmx_debug_clear_status(context); + + cvmx_debug_restore_core_context(context); + cvmx_debug_printf("Exiting debug exception handler\n"); +} + +void cvmx_debug_trigger_exception(void) +{ + /* Set CVMX_CIU_DINT to enter debug exception handler. */ + cvmx_write_csr (CVMX_CIU_DINT, 1 << cvmx_get_core_num ()); + /* Perform an immediate read after every write to an RSL register to force + the write to complete. It doesn't matter what RSL read we do, so we + choose CVMX_MIO_BOOT_BIST_STAT because it is fast and harmless */ + cvmx_read_csr (CVMX_MIO_BOOT_BIST_STAT); +} + +/** + * Inform debugger about the end of the program. This is + * called from crt0 after all the C cleanup code finishes. + * Our current stack is the C one, not the debug exception + * stack. */ +void cvmx_debug_finish(void) +{ + unsigned coreid = cvmx_get_core_num(); + cvmx_debug_state_t state; + + cvmx_debug_printf ("Debug _exit reached!, core %d, cvmx_debug_globals = %p\n", coreid, cvmx_debug_globals); + +#ifndef CVMX_BUILD_FOR_LINUX_KERNEL + fflush (stdout); + fflush (stderr); +#endif + + cvmx_spinlock_lock(&cvmx_debug_globals->lock); + state = cvmx_debug_get_state(); + state.known_cores ^= (1 << coreid); + state.core_finished |= (1<<coreid); + cvmx_debug_update_state(state); + + /* Tell the user the core has finished. */ + if (state.ever_been_in_debug) + cvmx_debug_putpacket("!Core %d finish.", coreid); + + /* Notify the debugger if all cores have completed the program */ + if ((cvmx_debug_core_mask () & state.core_finished) == cvmx_debug_core_mask ()) + { + cvmx_debug_printf("All cores done!\n"); + if (state.ever_been_in_debug) + cvmx_debug_putpacket_noformat("D0"); + } + if (state.focus_core == coreid && state.known_cores != 0) + { + /* Loop through cores looking for someone to handle interrupts. + Since we already check that known_cores is non zero, this + should always find a core */ + unsigned newcore; + for (newcore = 0; newcore < CVMX_DEBUG_MAX_CORES; newcore++) + { + if (state.known_cores & (1<<newcore)) + { + cvmx_debug_printf("Routing uart interrupts to Core #%u.\n", newcore); + cvmx_debug_set_focus_core(&state, newcore); + cvmx_debug_update_state(state); + break; + } + } + } + cvmx_spinlock_unlock(&cvmx_debug_globals->lock); + + /* If we ever been in the debug, report to it that we have exited the core. */ + if (state.ever_been_in_debug) + cvmx_debug_trigger_exception(); +} |