aboutsummaryrefslogtreecommitdiff
path: root/arch/blackfin/kernel/kgdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/blackfin/kernel/kgdb.c')
-rw-r--r--arch/blackfin/kernel/kgdb.c397
1 files changed, 56 insertions, 341 deletions
diff --git a/arch/blackfin/kernel/kgdb.c b/arch/blackfin/kernel/kgdb.c
index b163f6d3330..fa53faeeb0e 100644
--- a/arch/blackfin/kernel/kgdb.c
+++ b/arch/blackfin/kernel/kgdb.c
@@ -6,42 +6,10 @@
* Licensed under the GPL-2 or later.
*/
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
#include <linux/ptrace.h> /* for linux pt_regs struct */
#include <linux/kgdb.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/irq.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
-#include <asm/traps.h>
-#include <asm/blackfin.h>
-#include <asm/dma.h>
-
-/* Put the error code here just in case the user cares. */
-int gdb_bfin_errcode;
-/* Likewise, the vector number here (since GDB only gets the signal
- number through the usual means, and that's not very specific). */
-int gdb_bfin_vector = -1;
-
-#if KGDB_MAX_NO_CPUS != 8
-#error change the definition of slavecpulocks
-#endif
-
-#define IN_MEM(addr, size, l1_addr, l1_size) \
-({ \
- unsigned long __addr = (unsigned long)(addr); \
- (l1_size && __addr >= l1_addr && __addr + (size) <= l1_addr + l1_size); \
-})
-#define ASYNC_BANK_SIZE \
- (ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \
- ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE)
+#include <asm/irq_regs.h>
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
@@ -99,7 +67,7 @@ void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
gdb_regs[BFIN_RETN] = regs->retn;
gdb_regs[BFIN_RETE] = regs->rete;
gdb_regs[BFIN_PC] = regs->pc;
- gdb_regs[BFIN_CC] = 0;
+ gdb_regs[BFIN_CC] = (regs->astat >> 5) & 1;
gdb_regs[BFIN_EXTRA1] = 0;
gdb_regs[BFIN_EXTRA2] = 0;
gdb_regs[BFIN_EXTRA3] = 0;
@@ -166,7 +134,7 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
regs->lb1 = gdb_regs[BFIN_LB1];
regs->usp = gdb_regs[BFIN_USP];
regs->syscfg = gdb_regs[BFIN_SYSCFG];
- regs->retx = gdb_regs[BFIN_PC];
+ regs->retx = gdb_regs[BFIN_RETX];
regs->retn = gdb_regs[BFIN_RETN];
regs->rete = gdb_regs[BFIN_RETE];
regs->pc = gdb_regs[BFIN_PC];
@@ -178,7 +146,7 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
#endif
}
-struct hw_breakpoint {
+static struct hw_breakpoint {
unsigned int occupied:1;
unsigned int skip:1;
unsigned int enabled:1;
@@ -188,7 +156,7 @@ struct hw_breakpoint {
unsigned int addr;
} breakinfo[HW_WATCHPOINT_NUM];
-int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
+static int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
{
int breakno;
int bfin_type;
@@ -214,7 +182,7 @@ int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
return -ENOSPC;
}
- /* Becasue hardware data watchpoint impelemented in current
+ /* Because hardware data watchpoint impelemented in current
* Blackfin can not trigger an exception event as the hardware
* instrction watchpoint does, we ignaore all data watch point here.
* They can be turned on easily after future blackfin design
@@ -235,7 +203,7 @@ int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
return -ENOSPC;
}
-int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
+static int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
{
int breakno;
int bfin_type;
@@ -263,7 +231,7 @@ int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
return 0;
}
-void bfin_remove_all_hw_break(void)
+static void bfin_remove_all_hw_break(void)
{
int breakno;
@@ -275,7 +243,7 @@ void bfin_remove_all_hw_break(void)
breakinfo[breakno].type = TYPE_DATA_WATCHPOINT;
}
-void bfin_correct_hw_break(void)
+static void bfin_correct_hw_break(void)
{
int breakno;
unsigned int wpiactl = 0;
@@ -353,7 +321,7 @@ void bfin_correct_hw_break(void)
}
}
-void kgdb_disable_hw_debug(struct pt_regs *regs)
+static void bfin_disable_hw_debug(struct pt_regs *regs)
{
/* Disable hardware debugging while we are in kgdb */
bfin_write_WPIACTL(0);
@@ -362,6 +330,9 @@ void kgdb_disable_hw_debug(struct pt_regs *regs)
}
#ifdef CONFIG_SMP
+extern void generic_exec_single(int cpu, struct call_single_data *data, int wait);
+static struct call_single_data kgdb_smp_ipi_data[NR_CPUS];
+
void kgdb_passive_cpu_callback(void *info)
{
kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
@@ -369,20 +340,36 @@ void kgdb_passive_cpu_callback(void *info)
void kgdb_roundup_cpus(unsigned long flags)
{
- smp_call_function(kgdb_passive_cpu_callback, NULL, 0);
+ unsigned int cpu;
+
+ for (cpu = cpumask_first(cpu_online_mask); cpu < nr_cpu_ids;
+ cpu = cpumask_next(cpu, cpu_online_mask)) {
+ kgdb_smp_ipi_data[cpu].func = kgdb_passive_cpu_callback;
+ generic_exec_single(cpu, &kgdb_smp_ipi_data[cpu], 0);
+ }
}
void kgdb_roundup_cpu(int cpu, unsigned long flags)
{
- smp_call_function_single(cpu, kgdb_passive_cpu_callback, NULL, 0);
+ generic_exec_single(cpu, &kgdb_smp_ipi_data[cpu], 0);
}
#endif
-void kgdb_post_primary_code(struct pt_regs *regs, int eVector, int err_code)
+#ifdef CONFIG_IPIPE
+static unsigned long kgdb_arch_imask;
+#endif
+
+void kgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code)
{
- /* Master processor is completely in the debugger */
- gdb_bfin_vector = eVector;
- gdb_bfin_errcode = err_code;
+ if (kgdb_single_step)
+ preempt_enable();
+
+#ifdef CONFIG_IPIPE
+ if (kgdb_arch_imask) {
+ cpu_pda[raw_smp_processor_id()].ex_imask = kgdb_arch_imask;
+ kgdb_arch_imask = 0;
+ }
+#endif
}
int kgdb_arch_handle_exception(int vector, int signo,
@@ -428,6 +415,12 @@ int kgdb_arch_handle_exception(int vector, int signo,
* kgdb_single_step > 0 means in single step mode
*/
kgdb_single_step = i + 1;
+
+ preempt_disable();
+#ifdef CONFIG_IPIPE
+ kgdb_arch_imask = cpu_pda[raw_smp_processor_id()].ex_imask;
+ cpu_pda[raw_smp_processor_id()].ex_imask = 0;
+#endif
}
bfin_correct_hw_break();
@@ -439,264 +432,22 @@ int kgdb_arch_handle_exception(int vector, int signo,
struct kgdb_arch arch_kgdb_ops = {
.gdb_bpt_instr = {0xa1},
-#ifdef CONFIG_SMP
- .flags = KGDB_HW_BREAKPOINT|KGDB_THR_PROC_SWAP,
-#else
.flags = KGDB_HW_BREAKPOINT,
-#endif
.set_hw_breakpoint = bfin_set_hw_break,
.remove_hw_breakpoint = bfin_remove_hw_break,
+ .disable_hw_break = bfin_disable_hw_debug,
.remove_all_hw_break = bfin_remove_all_hw_break,
.correct_hw_break = bfin_correct_hw_break,
};
-static int 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;
-}
-
-static int validate_memory_access_address(unsigned long addr, int size)
-{
- int cpu = raw_smp_processor_id();
-
- if (size < 0)
- return EFAULT;
- if (addr >= 0x1000 && (addr + size) <= physical_mem_end)
- return 0;
- if (addr >= SYSMMR_BASE)
- return 0;
- if (IN_MEM(addr, size, ASYNC_BANK0_BASE, ASYNC_BANK_SIZE))
- return 0;
- if (cpu == 0) {
- if (IN_MEM(addr, size, L1_SCRATCH_START, L1_SCRATCH_LENGTH))
- return 0;
- if (IN_MEM(addr, size, L1_CODE_START, L1_CODE_LENGTH))
- return 0;
- if (IN_MEM(addr, size, L1_DATA_A_START, L1_DATA_A_LENGTH))
- return 0;
- if (IN_MEM(addr, size, L1_DATA_B_START, L1_DATA_B_LENGTH))
- return 0;
-#ifdef CONFIG_SMP
- } else if (cpu == 1) {
- if (IN_MEM(addr, size, COREB_L1_SCRATCH_START, L1_SCRATCH_LENGTH))
- return 0;
- if (IN_MEM(addr, size, COREB_L1_CODE_START, L1_CODE_LENGTH))
- return 0;
- if (IN_MEM(addr, size, COREB_L1_DATA_A_START, L1_DATA_A_LENGTH))
- return 0;
- if (IN_MEM(addr, size, COREB_L1_DATA_B_START, L1_DATA_B_LENGTH))
- return 0;
-#endif
- }
-
- if (IN_MEM(addr, size, L2_START, L2_LENGTH))
- return 0;
-
- return EFAULT;
-}
-
-/*
- * Convert the memory pointed to by mem into hex, placing result in buf.
- * Return a pointer to the last char put in buf (null). May return an error.
- */
-int kgdb_mem2hex(char *mem, char *buf, int count)
-{
- char *tmp;
- int err = 0;
- unsigned char *pch;
- unsigned short mmr16;
- unsigned long mmr32;
- int cpu = raw_smp_processor_id();
-
- if (validate_memory_access_address((unsigned long)mem, count))
- return EFAULT;
-
- /*
- * We use the upper half of buf as an intermediate buffer for the
- * raw memory copy. Hex conversion will work against this one.
- */
- tmp = buf + count;
-
- if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
- switch (count) {
- case 2:
- if ((unsigned int)mem % 2 == 0) {
- mmr16 = *(unsigned short *)mem;
- pch = (unsigned char *)&mmr16;
- *tmp++ = *pch++;
- *tmp++ = *pch++;
- tmp -= 2;
- } else
- err = EFAULT;
- break;
- case 4:
- if ((unsigned int)mem % 4 == 0) {
- mmr32 = *(unsigned long *)mem;
- pch = (unsigned char *)&mmr32;
- *tmp++ = *pch++;
- *tmp++ = *pch++;
- *tmp++ = *pch++;
- *tmp++ = *pch++;
- tmp -= 4;
- } else
- err = EFAULT;
- break;
- default:
- err = EFAULT;
- }
- } else if ((cpu == 0 && IN_MEM(mem, count, L1_CODE_START, L1_CODE_LENGTH))
-#ifdef CONFIG_SMP
- || (cpu == 1 && IN_MEM(mem, count, COREB_L1_CODE_START, L1_CODE_LENGTH))
-#endif
- ) {
- /* access L1 instruction SRAM*/
- if (dma_memcpy(tmp, mem, count) == NULL)
- err = EFAULT;
- } else
- err = probe_kernel_read(tmp, mem, count);
-
- if (!err) {
- while (count > 0) {
- buf = pack_hex_byte(buf, *tmp);
- tmp++;
- count--;
- }
-
- *buf = 0;
- }
-
- return err;
-}
-
-/*
- * Copy the binary array pointed to by buf into mem. Fix $, #, and
- * 0x7d escaped with 0x7d. Return a pointer to the character after
- * the last byte written.
- */
-int kgdb_ebin2mem(char *buf, char *mem, int count)
-{
- char *tmp_old;
- char *tmp_new;
- unsigned short *mmr16;
- unsigned long *mmr32;
- int err = 0;
- int size = 0;
- int cpu = raw_smp_processor_id();
-
- tmp_old = tmp_new = buf;
-
- while (count-- > 0) {
- if (*tmp_old == 0x7d)
- *tmp_new = *(++tmp_old) ^ 0x20;
- else
- *tmp_new = *tmp_old;
- tmp_new++;
- tmp_old++;
- size++;
- }
-
- if (validate_memory_access_address((unsigned long)mem, size))
- return EFAULT;
-
- if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
- switch (size) {
- case 2:
- if ((unsigned int)mem % 2 == 0) {
- mmr16 = (unsigned short *)buf;
- *(unsigned short *)mem = *mmr16;
- } else
- return EFAULT;
- break;
- case 4:
- if ((unsigned int)mem % 4 == 0) {
- mmr32 = (unsigned long *)buf;
- *(unsigned long *)mem = *mmr32;
- } else
- return EFAULT;
- break;
- default:
- return EFAULT;
- }
- } else if ((cpu == 0 && IN_MEM(mem, count, L1_CODE_START, L1_CODE_LENGTH))
-#ifdef CONFIG_SMP
- || (cpu == 1 && IN_MEM(mem, count, COREB_L1_CODE_START, L1_CODE_LENGTH))
-#endif
- ) {
- /* access L1 instruction SRAM */
- if (dma_memcpy(mem, buf, size) == NULL)
- err = EFAULT;
- } else
- err = probe_kernel_write(mem, buf, size);
-
- return err;
-}
-
-/*
- * Convert the hex array pointed to by buf into binary to be placed in mem.
- * Return a pointer to the character AFTER the last byte written.
- * May return an error.
- */
-int kgdb_hex2mem(char *buf, char *mem, int count)
-{
- char *tmp_raw;
- char *tmp_hex;
- unsigned short *mmr16;
- unsigned long *mmr32;
- int cpu = raw_smp_processor_id();
-
- if (validate_memory_access_address((unsigned long)mem, count))
- return EFAULT;
-
- /*
- * We use the upper half of buf as an intermediate buffer for the
- * raw memory that is converted from hex.
- */
- tmp_raw = buf + count * 2;
-
- tmp_hex = tmp_raw - 1;
- while (tmp_hex >= buf) {
- tmp_raw--;
- *tmp_raw = hex(*tmp_hex--);
- *tmp_raw |= hex(*tmp_hex--) << 4;
- }
-
- if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
- switch (count) {
- case 2:
- if ((unsigned int)mem % 2 == 0) {
- mmr16 = (unsigned short *)tmp_raw;
- *(unsigned short *)mem = *mmr16;
- } else
- return EFAULT;
- break;
- case 4:
- if ((unsigned int)mem % 4 == 0) {
- mmr32 = (unsigned long *)tmp_raw;
- *(unsigned long *)mem = *mmr32;
- } else
- return EFAULT;
- break;
- default:
- return EFAULT;
- }
- } else if ((cpu == 0 && IN_MEM(mem, count, L1_CODE_START, L1_CODE_LENGTH))
-#ifdef CONFIG_SMP
- || (cpu == 1 && IN_MEM(mem, count, COREB_L1_CODE_START, L1_CODE_LENGTH))
-#endif
- ) {
- /* access L1 instruction SRAM */
- if (dma_memcpy(mem, tmp_raw, count) == NULL)
- return EFAULT;
- } else
- return probe_kernel_write(mem, tmp_raw, count);
- return 0;
-}
+#define IN_MEM(addr, size, l1_addr, l1_size) \
+({ \
+ unsigned long __addr = (unsigned long)(addr); \
+ (l1_size && __addr >= l1_addr && __addr + (size) <= l1_addr + l1_size); \
+})
+#define ASYNC_BANK_SIZE \
+ (ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \
+ ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE)
int kgdb_validate_break_address(unsigned long addr)
{
@@ -715,56 +466,20 @@ int kgdb_validate_break_address(unsigned long addr)
if (IN_MEM(addr, BREAK_INSTR_SIZE, L2_START, L2_LENGTH))
return 0;
- return EFAULT;
+ return -EFAULT;
}
-int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
+void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{
- int err;
- int cpu = raw_smp_processor_id();
-
- if ((cpu == 0 && IN_MEM(addr, BREAK_INSTR_SIZE, L1_CODE_START, L1_CODE_LENGTH))
-#ifdef CONFIG_SMP
- || (cpu == 1 && IN_MEM(addr, BREAK_INSTR_SIZE, COREB_L1_CODE_START, L1_CODE_LENGTH))
-#endif
- ) {
- /* access L1 instruction SRAM */
- if (dma_memcpy(saved_instr, (void *)addr, BREAK_INSTR_SIZE)
- == NULL)
- return -EFAULT;
-
- if (dma_memcpy((void *)addr, arch_kgdb_ops.gdb_bpt_instr,
- BREAK_INSTR_SIZE) == NULL)
- return -EFAULT;
-
- return 0;
- } else {
- err = probe_kernel_read(saved_instr, (char *)addr,
- BREAK_INSTR_SIZE);
- if (err)
- return err;
-
- return probe_kernel_write((char *)addr,
- arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
- }
-}
-
-int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
-{
- if (IN_MEM(addr, BREAK_INSTR_SIZE, L1_CODE_START, L1_CODE_LENGTH)) {
- /* access L1 instruction SRAM */
- if (dma_memcpy((void *)addr, bundle, BREAK_INSTR_SIZE) == NULL)
- return -EFAULT;
-
- return 0;
- } else
- return probe_kernel_write((char *)addr,
- (char *)bundle, BREAK_INSTR_SIZE);
+ regs->retx = ip;
}
int kgdb_arch_init(void)
{
kgdb_single_step = 0;
+#ifdef CONFIG_IPIPE
+ kgdb_arch_imask = 0;
+#endif
bfin_remove_all_hw_break();
return 0;