summaryrefslogtreecommitdiffstats
path: root/sys/contrib/octeon-sdk/cvmx-debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/octeon-sdk/cvmx-debug.c')
-rw-r--r--sys/contrib/octeon-sdk/cvmx-debug.c1600
1 files changed, 1600 insertions, 0 deletions
diff --git a/sys/contrib/octeon-sdk/cvmx-debug.c b/sys/contrib/octeon-sdk/cvmx-debug.c
new file mode 100644
index 0000000..223f7eb
--- /dev/null
+++ b/sys/contrib/octeon-sdk/cvmx-debug.c
@@ -0,0 +1,1600 @@
+/***********************license start***************
+ * Copyright (c) 2003-2010 Cavium Inc. (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 Inc. 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 INC. 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
+ * NOTE: CARE SHOULD BE TAKE WHEN USING STD C LIBRARY FUNCTIONS IN
+ * THIS FILE IF SOMEONE PUTS A BREAKPOINT ON THOSE FUNCTIONS
+ * DEBUGGING WILL FAIL.
+ *
+ * <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 "cvmx.h"
+#include "cvmx-debug.h"
+#include "cvmx-bootmem.h"
+#include "cvmx-core.h"
+#include "cvmx-coremask.h"
+#include "octeon-boot-info.h"
+#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 CVMX_BUILD_FOR_TOOLCHAIN
+extern int cvmx_interrupt_in_isr;
+#else
+#define cvmx_interrupt_in_isr 0
+#endif
+
+#else
+uint64_t __cvmx_debug_save_regs_area_all[CVMX_MAX_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[CVMX_MAX_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[CVMX_MAX_CORES];
+#define __cvmx_debug_mode_exception_occured __cvmx_debug_mode_exception_occured_all[cvmx_get_core_num()]
+
+static char cvmx_debug_stack_all[CVMX_MAX_CORES][8*1024] __attribute ((aligned (16)));
+char *__cvmx_debug_stack_top_all[CVMX_MAX_CORES];
+
+#define cvmx_interrupt_in_isr 0
+
+#endif
+
+
+static size_t cvmx_debug_strlen (const char *str)
+{
+ size_t size = 0;
+ while (*str)
+ {
+ size++;
+ str++;
+ }
+ return size;
+}
+static void cvmx_debug_strcpy (char *dest, const char *src)
+{
+ while (*src)
+ {
+ *dest = *src;
+ src++;
+ dest++;
+ }
+ *dest = 0;
+}
+
+static void cvmx_debug_memcpy_align (void *dest, const void *src, int size) __attribute__ ((__noinline__));
+static void cvmx_debug_memcpy_align (void *dest, const void *src, int size)
+{
+ long long *dest1 = (long long*)dest;
+ const long long *src1 = (const long long*)src;
+ int i;
+ if (size == 40)
+ {
+ long long a0, a1, a2, a3, a4;
+ a0 = src1[0];
+ a1 = src1[1];
+ a2 = src1[2];
+ a3 = src1[3];
+ a4 = src1[4];
+ dest1[0] = a0;
+ dest1[1] = a1;
+ dest1[2] = a2;
+ dest1[3] = a3;
+ dest1[4] = a4;
+ return;
+ }
+ for(i = 0;i < size;i+=8)
+ {
+ *dest1 = *src1;
+ dest1++;
+ src1++;
+ }
+}
+
+
+static inline uint32_t cvmx_debug_core_mask(void)
+{
+#ifndef CVMX_BUILD_FOR_LINUX_KERNEL
+#ifdef CVMX_BUILD_FOR_TOOLCHAIN
+ 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)
+{
+ cvmx_debug_memcpy_align(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;
+ cvmx_debug_memcpy_align(&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_global_ptr (void *ptr)
+{
+ uint64_t phys = cvmx_ptr_to_phys (ptr);
+ cvmx_debug_globals_t *p;
+ /* Since at this point, TLBs are not mapped 1 to 1, we should just use KSEG0 accesses. */
+ p = CASTPTR(cvmx_debug_globals_t, CVMX_ADD_SEG32(CVMX_MIPS32_SPACE_KSEG0, phys));
+ memset (p, 0, sizeof(cvmx_debug_globals_t));
+ p->version = CVMX_DEBUG_GLOBALS_VERSION;
+ p->tlb_entries = cvmx_core_get_tlb_entries();
+}
+
+static void cvmx_debug_init_globals(void)
+{
+ uint64_t phys;
+ void *ptr;
+
+ if (cvmx_debug_globals)
+ return;
+ ptr = cvmx_bootmem_alloc_named_range_once(sizeof(cvmx_debug_globals_t), 0, /* KSEG0 max, 512MB=*/0/*1024*1024*512*/, 8,
+ CVMX_DEBUG_GLOBALS_BLOCK_NAME, cvmx_debug_init_global_ptr);
+ phys = cvmx_ptr_to_phys (ptr);
+
+ /* Since TLBs are not always mapped 1 to 1, we should just use access via KSEG0. */
+ 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);
+}
+
+
+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, uint64_t hi, uint64_t lo);
+
+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 < CVMX_MAX_CORES; core1++)
+ {
+ if ((1u<<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 |= (1u << core);
+ state.core_finished &= ~(1u << 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 < CVMX_MAX_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 const char cvmx_debug_hexchar[] = "0123456789ABCDEF";
+/* Put the hex value of t into str. */
+static void cvmx_debug_int8_to_strhex(char *str, unsigned char t)
+{
+ str[0] = cvmx_debug_hexchar[(t>>4)&0xf];
+ str[1] = cvmx_debug_hexchar[t&0xF];
+ str[2] = 0;
+}
+
+static void cvmx_debug_int64_to_strhex(char *str, uint64_t t)
+{
+ str[0] = cvmx_debug_hexchar[(t>>60)&0xF];
+ str[1] = cvmx_debug_hexchar[(t>>56)&0xF];
+ str[2] = cvmx_debug_hexchar[(t>>52)&0xF];
+ str[3] = cvmx_debug_hexchar[(t>>48)&0xF];
+ str[4] = cvmx_debug_hexchar[(t>>44)&0xF];
+ str[5] = cvmx_debug_hexchar[(t>>40)&0xF];
+ str[6] = cvmx_debug_hexchar[(t>>36)&0xF];
+ str[7] = cvmx_debug_hexchar[(t>>32)&0xF];
+ str[8] = cvmx_debug_hexchar[(t>>28)&0xF];
+ str[9] = cvmx_debug_hexchar[(t>>24)&0xF];
+ str[10] = cvmx_debug_hexchar[(t>>20)&0xF];
+ str[11] = cvmx_debug_hexchar[(t>>16)&0xF];
+ str[12] = cvmx_debug_hexchar[(t>>12)&0xF];
+ str[13] = cvmx_debug_hexchar[(t>>8)&0xF];
+ str[14] = cvmx_debug_hexchar[(t>>4)&0xF];
+ str[15] = cvmx_debug_hexchar[(t>>0)&0xF];
+ str[16] = 0;
+}
+
+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_putcorepacket(char *buf, int core)
+{
+ char *tmp = "!Core XX ";
+ int tmpsize = cvmx_debug_strlen(tmp);
+ int bufsize = cvmx_debug_strlen(buf);
+ char *packet = __builtin_alloca(tmpsize + bufsize + 1);
+ cvmx_debug_strcpy(packet, tmp);
+ cvmx_debug_strcpy(&packet[tmpsize], buf);
+ if (core < 10)
+ {
+ packet[6] = ' ';
+ packet[7] = core + '0';
+ }
+ else if (core < 20)
+ {
+ packet[6] = '1';
+ packet[7] = core - 10 + '0';
+ }
+ else if (core < 30)
+ {
+ packet[6] = '2';
+ packet[7] = core - 20 + '0';
+ }
+ else
+ {
+ packet[6] = '3';
+ packet[7] = core - 30 + '0';
+ }
+ return cvmx_debug_putpacket_noformat(packet);
+}
+
+/* Put a buf followed by an integer formated as a hex. */
+static int cvmx_debug_putpacket_hexint(char *buf, uint64_t value)
+{
+ size_t size = cvmx_debug_strlen(buf);
+ char *packet = __builtin_alloca(size + 16 + 1);
+ cvmx_debug_strcpy(packet, buf);
+ cvmx_debug_int64_to_strhex(&packet[size], value);
+ return cvmx_debug_putpacket_noformat(packet);
+}
+
+static int cvmx_debug_active_core(cvmx_debug_state_t state, unsigned core)
+{
+ return state.active_cores & (1u << 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;
+}
+
+
+/**
+ * Routines to handle hex data
+ *
+ * @param ch
+ * @return
+ */
+static inline int cvmx_debug_hex(char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f'))
+ return(ch - 'a' + 10);
+ if ((ch >= '0') && (ch <= '9'))
+ return(ch - '0');
+ if ((ch >= 'A') && (ch <= 'F'))
+ return(ch - 'A' + 10);
+ return(-1);
+}
+
+/**
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ *
+ * @param ptr
+ * @param intValue
+ * @return
+ */
+static int cvmx_debug_hexToLong(const char **ptr, uint64_t *intValue)
+{
+ int numChars = 0;
+ long hexValue;
+
+ *intValue = 0;
+ while (**ptr)
+ {
+ hexValue = cvmx_debug_hex(**ptr);
+ if (hexValue < 0)
+ break;
+
+ *intValue = (*intValue << 4) | hexValue;
+ numChars ++;
+
+ (*ptr)++;
+ }
+
+ return(numChars);
+}
+
+/**
+ * Initialize the performance counter control registers.
+ *
+ */
+static void cvmx_debug_set_perf_control_reg (volatile cvmx_debug_core_context_t *context, int perf_event, int perf_counter)
+{
+ 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(const 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 */
+ {
+ uint64_t core;
+ if (!cvmx_debug_hexToLong(&buf, &core))
+ {
+ cvmx_debug_putpacket_noformat("!Uknown core. Focus not changed.");
+ }
+ /* 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. */
+ else if (state.handler_cores & (1u << 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_hexint("F", 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_hexint("F", 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_hexint("J", (unsigned)state.step_isr);
+ break;
+
+
+ case 'I': /* Set the active cores */
+ {
+ uint64_t active_cores;
+ if (!cvmx_debug_hexToLong(&buf, &active_cores))
+ active_cores = 0;
+ /* 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 & (1u << state.focus_core)) == 0)
+ {
+ cvmx_debug_putpacket_noformat("!Focus core was added to the masked.");
+ state.active_cores |= 1u << 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_hexint("I", 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_hexint("A", state.step_all);
+ break;
+
+ case 'g': /* read a register from global place. */
+ {
+ volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context();
+ uint64_t regno;
+ volatile uint64_t *reg;
+
+ /* Get the register number to read */
+ if (!cvmx_debug_hexToLong(&buf, &regno))
+ {
+ cvmx_debug_printf("Register number cannot be read.\n");
+ cvmx_debug_putpacket_hexint("", 0xDEADBEEF);
+ break;
+ }
+
+ reg = cvmx_debug_regnum_to_context_ref(regno, context);
+ if (!reg)
+ {
+ cvmx_debug_printf("Register #%d is not valid\n", (int)regno);
+ cvmx_debug_putpacket_hexint("", 0xDEADBEEF);
+ break;
+ }
+ cvmx_debug_putpacket_hexint("", *reg);
+ }
+ break;
+
+ case 'G': /* set the value of a register. */
+ {
+ volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context();
+ uint64_t regno;
+ volatile uint64_t *reg;
+ uint64_t value;
+
+ /* Get the register number to write. It should be followed by
+ a comma */
+ if (!cvmx_debug_hexToLong(&buf, &regno)
+ || (*buf++ != ',')
+ || !cvmx_debug_hexToLong(&buf, &value))
+ {
+ 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", (int)regno);
+ goto error_packet;
+ }
+ *reg = value;
+ }
+ break;
+
+ case 'm': /* Memory read. mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ {
+ uint64_t addr, i, length;
+ unsigned char *ptr;
+ char *reply;
+
+ /* Get the memory address, a comma, and the length */
+ if (!cvmx_debug_hexToLong(&buf, &addr)
+ || (*buf++ != ',')
+ || !cvmx_debug_hexToLong(&buf, &length))
+ {
+ 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", (long long)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;
+ cvmx_debug_int8_to_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 */
+ {
+ uint64_t addr, i, length;
+ unsigned char *ptr;
+
+ if (!cvmx_debug_hexToLong(&buf, &addr)
+ || *buf++ != ','
+ || !cvmx_debug_hexToLong(&buf, &length)
+ || *buf++ != ':')
+ {
+ cvmx_debug_printf("M packet corrupt: %s\n", buf);
+ goto error_packet;
+ }
+
+ ptr = (unsigned char *)(long)addr;
+ for (i = 0; i < length; i++)
+ {
+ int n, n1;
+ unsigned char c;
+
+ n = cvmx_debug_hex(buf[i * 2]);
+ n1 = cvmx_debug_hex(buf[i * 2 + 1]);
+ c = (n << 4) | n1;
+
+ if (n == -1 || n1 == -1)
+ {
+ cvmx_debug_printf("M packet corrupt: %s\n", &buf[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. */
+ {
+ uint64_t perf_event = 0;
+ char encoded_counter = *buf++;
+ uint64_t counter;
+ volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context();
+
+ /* Ignore errors from the packet. */
+ cvmx_debug_hexToLong(&buf, &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(context, perf_event, counter);
+ break;
+
+ case '3': /* Get performance counter0 event. */
+ case '4': /* Get performance counter1 event. */
+ {
+ cvmx_core_perf_control_t c;
+ char outpacket[16*2 +2];
+ counter = encoded_counter - '3';
+ /* Pass performance counter0 event and counter to
+ the debugger. */
+ c.u32 = context->cop0.perfctrl[counter];
+ cvmx_debug_int64_to_strhex(outpacket, context->cop0.perfval[counter]);
+ outpacket[16] = ',';
+ cvmx_debug_int64_to_strhex(&outpacket[17], c.s.event);
+ outpacket[33] = 0;
+ cvmx_debug_putpacket_noformat(outpacket);
+ }
+ 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
+ };
+
+ uint64_t num, size;
+ uint64_t addr;
+ uint64_t type;
+ char bp_type = *buf++;
+ const int BE = 1, TE = 4;
+ volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context();
+
+ if (!cvmx_debug_hexToLong(&buf, &num)
+ || *buf++ != ','
+ || !cvmx_debug_hexToLong(&buf, &addr))
+ {
+ cvmx_debug_printf("Z packet corrupt: %s\n", &packet[1]);
+ goto error_packet;
+ }
+
+ switch (bp_type)
+ {
+ case 'i': // Instruction hardware breakpoint
+ if (num > 4)
+ {
+ cvmx_debug_printf("Z packet corrupt: %s\n", &packet[1]);
+ 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 (num > 4
+ || *buf++ != ','
+ || !cvmx_debug_hexToLong(&buf, &size)
+ || *buf++ != ','
+ || !cvmx_debug_hexToLong(&buf, &type)
+ || type > WP_ACCESS
+ || type < WP_LOAD)
+ {
+ cvmx_debug_printf("Z packet corrupt: %s\n", &packet[1]);
+ 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", &packet[1]);
+ goto error_packet;
+ }
+ }
+ break;
+
+ case 'z': /* Remove hardware breakpoint: z[di]NN..N remove NN..Nth
+breakpoint. */
+ {
+ uint64_t num;
+ char bp_type = *buf++;
+ volatile cvmx_debug_core_context_t *context = cvmx_debug_core_context();
+
+ if (!cvmx_debug_hexToLong(&buf, &num) || 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 & (1u<<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_putcorepacket("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 & (1u << 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 & (1u << 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_hexint("T8:", (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)
+{
+ /* NOTE this reads directly from the state array for speed reasons
+ and we don't change the array. */
+ do {
+ asm("": : : "memory");
+ } while (cvmx_debug_globals->state[offsetof(cvmx_debug_state_t, step_all)/sizeof(uint32_t)]
+ && cvmx_debug_globals->state[offsetof(cvmx_debug_state_t, handler_cores)/sizeof(uint32_t)] != 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 < 2400; 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 |= (1u << 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 {
+ unsigned oldfocus = state.focus_core;
+ state = cvmx_debug_get_state();
+ /* Note the focus core can change in this loop. */
+ if (__cvmx_debug_in_focus(state, core))
+ {
+ /* If the focus has changed and the old focus has exited, then send a signal
+ that we should stop if step_all is off. */
+ if (oldfocus != state.focus_core && ((1u << oldfocus) & state.core_finished)
+ && !state.step_all)
+ cvmx_debug_send_stop_reason(debug_reg, context);
+
+ 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 ^= (1u << 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, uint64_t hi, uint64_t lo)
+{
+ unsigned i;
+ cvmx_debug_memcpy_align ((char *) context->regs, __cvmx_debug_save_regs_area, sizeof(context->regs));
+ context->lo = lo;
+ context->hi = 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)
+{
+ uint64_t hi, lo;
+ int i;
+ cvmx_debug_memcpy_align (__cvmx_debug_save_regs_area, (char *) context->regs, sizeof(context->regs));
+ /* 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);
+ lo = context->lo;
+ hi = context->hi;
+ asm("mtlo %0" :: "r"(lo));
+ asm("mthi %0" :: "r"(hi));
+}
+
+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 (uint64_t lo, uint64_t hi)
+{
+ 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, hi, lo);
+
+ {
+ 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, 1u << 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;
+
+ if (!cvmx_debug_globals) return;
+ 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 ^= (1u << coreid);
+ state.core_finished |= (1u <<coreid);
+ cvmx_debug_update_state(state);
+
+ /* Tell the user the core has finished. */
+ if (state.ever_been_in_debug)
+ cvmx_debug_putcorepacket("finished.", 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_MAX_CORES; newcore++)
+ {
+ if (state.known_cores & (1u<<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();
+}
OpenPOWER on IntegriCloud