microblaze: Add KGDB support
Kgdb uses brki r16, 0x18 instruction to call low level _debug_exception function which save current state to pt_regs and call microblaze_kgdb_break function. _debug_exception should be called only from the kernel space. User space calling is not supported because user application debugging uses different handling. pt_regs_to_gdb_regs loads additional special registers which can't be changed * Enable KGDB in Kconfig * Remove ancient not-tested KGDB support * Remove ancient _debug_exception code from entry.S Only MMU KGDB support is supported. Signed-off-by: Michal Simek <monstr@monstr.eu> CC: Jason Wessel <jason.wessel@windriver.com> CC: John Williams <john.williams@petalogix.com> CC: Edgar E. Iglesias <edgar.iglesias@petalogix.com> CC: linux-kernel@vger.kernel.org Acked-by: Jason Wessel <jason.wessel@windriver.com>
This commit is contained in:
parent
751f1605e0
commit
2d5973cb5a
|
@ -14,6 +14,7 @@ config MICROBLAZE
|
|||
select USB_ARCH_HAS_EHCI
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select HAVE_OPROFILE
|
||||
select HAVE_ARCH_KGDB
|
||||
select HAVE_DMA_ATTRS
|
||||
select HAVE_DMA_API_DEBUG
|
||||
select TRACING_SUPPORT
|
||||
|
|
|
@ -69,22 +69,6 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,
|
|||
void die(const char *str, struct pt_regs *fp, long err);
|
||||
void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
|
||||
|
||||
#if defined(CONFIG_KGDB)
|
||||
void (*debugger)(struct pt_regs *regs);
|
||||
int (*debugger_bpt)(struct pt_regs *regs);
|
||||
int (*debugger_sstep)(struct pt_regs *regs);
|
||||
int (*debugger_iabr_match)(struct pt_regs *regs);
|
||||
int (*debugger_dabr_match)(struct pt_regs *regs);
|
||||
void (*debugger_fault_handler)(struct pt_regs *regs);
|
||||
#else
|
||||
#define debugger(regs) do { } while (0)
|
||||
#define debugger_bpt(regs) 0
|
||||
#define debugger_sstep(regs) 0
|
||||
#define debugger_iabr_match(regs) 0
|
||||
#define debugger_dabr_match(regs) 0
|
||||
#define debugger_fault_handler ((void (*)(struct pt_regs *))0)
|
||||
#endif
|
||||
|
||||
#endif /*__ASSEMBLY__ */
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _ASM_MICROBLAZE_EXCEPTIONS_H */
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#ifdef __KERNEL__
|
||||
#ifndef __MICROBLAZE_KGDB_H__
|
||||
#define __MICROBLAZE_KGDB_H__
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define CACHE_FLUSH_IS_SAFE 1
|
||||
#define BUFMAX 2048
|
||||
|
||||
/*
|
||||
* 32 32-bit general purpose registers (r0-r31)
|
||||
* 6 32-bit special registers (pc, msr, ear, esr, fsr, btr)
|
||||
* 12 32-bit PVR
|
||||
* 7 32-bit MMU Regs (redr, rpid, rzpr, rtlbx, rtlbsx, rtlblo, rtlbhi)
|
||||
* ------
|
||||
* 57 registers
|
||||
*/
|
||||
#define NUMREGBYTES (57 * 4)
|
||||
|
||||
#define BREAK_INSTR_SIZE 4
|
||||
static inline void arch_kgdb_breakpoint(void)
|
||||
{
|
||||
__asm__ __volatile__("brki r16, 0x18;");
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __MICROBLAZE_KGDB_H__ */
|
||||
#endif /* __KERNEL__ */
|
|
@ -28,5 +28,6 @@ obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o
|
|||
obj-$(CONFIG_MMU) += misc.o
|
||||
obj-$(CONFIG_STACKTRACE) += stacktrace.o
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o
|
||||
obj-$(CONFIG_KGDB) += kgdb.o
|
||||
|
||||
obj-y += entry$(MMU).o
|
||||
|
|
|
@ -745,11 +745,8 @@ IRQ_return: /* MS: Make global symbol for debugging */
|
|||
nop
|
||||
|
||||
/*
|
||||
* `Debug' trap
|
||||
* We enter dbtrap in "BIP" (breakpoint) mode.
|
||||
* So we exit the breakpoint mode with an 'rtbd' and proceed with the
|
||||
* original dbtrap.
|
||||
* however, wait to save state first
|
||||
* Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18
|
||||
* and call handling function with saved pt_regs
|
||||
*/
|
||||
C_ENTRY(_debug_exception):
|
||||
/* BIP bit is set on entry, no interrupts can occur */
|
||||
|
@ -759,18 +756,44 @@ C_ENTRY(_debug_exception):
|
|||
nop
|
||||
andi r1, r1, MSR_UMS
|
||||
bnei r1, 1f
|
||||
/* Kernel-mode state save. */
|
||||
/* MS: Kernel-mode state save - kgdb */
|
||||
lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
|
||||
tophys(r1,r1);
|
||||
|
||||
addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */
|
||||
/* BIP bit is set on entry, no interrupts can occur */
|
||||
addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE;
|
||||
SAVE_REGS;
|
||||
/* save all regs to pt_reg structure */
|
||||
swi r0, r1, PTO+PT_R0; /* R0 must be saved too */
|
||||
swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */
|
||||
swi r16, r1, PTO+PT_R16
|
||||
swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */
|
||||
swi r17, r1, PTO+PT_R17
|
||||
/* save special purpose registers to pt_regs */
|
||||
mfs r11, rear;
|
||||
swi r11, r1, PTO+PT_EAR;
|
||||
mfs r11, resr;
|
||||
swi r11, r1, PTO+PT_ESR;
|
||||
mfs r11, rfsr;
|
||||
swi r11, r1, PTO+PT_FSR;
|
||||
|
||||
swi r1, r1, PTO + PT_MODE;
|
||||
brid 2f;
|
||||
nop; /* Fill delay slot */
|
||||
1: /* User-mode state save. */
|
||||
lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
|
||||
/* stack pointer is in physical address at it is decrease
|
||||
* by STATE_SAVE_SIZE but we need to get correct R1 value */
|
||||
addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE;
|
||||
swi r11, r1, PTO+PT_R1
|
||||
/* MS: r31 - current pointer isn't changed */
|
||||
tovirt(r1,r1)
|
||||
#ifdef CONFIG_KGDB
|
||||
addi r5, r1, PTO /* pass pt_reg address as the first arg */
|
||||
la r15, r0, dbtrap_call; /* return address */
|
||||
rtbd r0, microblaze_kgdb_break
|
||||
nop;
|
||||
#endif
|
||||
/* MS: Place handler for brki from kernel space if KGDB is OFF.
|
||||
* It is very unlikely that another brki instruction is called. */
|
||||
bri 0
|
||||
|
||||
/* MS: User-mode state save - gdb */
|
||||
1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
|
||||
tophys(r1,r1);
|
||||
lwi r1, r1, TS_THREAD_INFO; /* get the thread info */
|
||||
addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */
|
||||
|
@ -781,36 +804,32 @@ C_ENTRY(_debug_exception):
|
|||
swi r17, r1, PTO+PT_R17;
|
||||
swi r16, r1, PTO+PT_R16;
|
||||
swi r16, r1, PTO+PT_PC; /* Save LP */
|
||||
|
||||
swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */
|
||||
lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
|
||||
swi r11, r1, PTO+PT_R1; /* Store user SP. */
|
||||
2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
||||
lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
|
||||
tovirt(r1,r1)
|
||||
|
||||
set_vms;
|
||||
addik r5, r1, PTO;
|
||||
addik r15, r0, dbtrap_call;
|
||||
dbtrap_call: /* return point for kernel/user entry */
|
||||
dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
|
||||
rtbd r0, sw_exception
|
||||
nop
|
||||
|
||||
set_bip; /* Ints masked for state restore*/
|
||||
/* MS: The first instruction for the second part of the gdb/kgdb */
|
||||
set_bip; /* Ints masked for state restore */
|
||||
lwi r11, r1, PTO + PT_MODE;
|
||||
bnei r11, 2f;
|
||||
|
||||
/* MS: Return to user space - gdb */
|
||||
/* Get current task ptr into r11 */
|
||||
lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */
|
||||
lwi r11, r11, TI_FLAGS; /* get flags in thread info */
|
||||
andi r11, r11, _TIF_NEED_RESCHED;
|
||||
beqi r11, 5f;
|
||||
|
||||
/* Call the scheduler before returning from a syscall/trap. */
|
||||
|
||||
/* Call the scheduler before returning from a syscall/trap. */
|
||||
bralid r15, schedule; /* Call scheduler */
|
||||
nop; /* delay slot */
|
||||
/* XXX Is PT_DTRACE handling needed here? */
|
||||
/* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here. */
|
||||
|
||||
/* Maybe handle a signal */
|
||||
5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */
|
||||
|
@ -818,48 +837,37 @@ dbtrap_call: /* return point for kernel/user entry */
|
|||
andi r11, r11, _TIF_SIGPENDING;
|
||||
beqi r11, 1f; /* Signals to handle, handle them */
|
||||
|
||||
/* Handle a signal return; Pending signals should be in r18. */
|
||||
/* Not all registers are saved by the normal trap/interrupt entry
|
||||
points (for instance, call-saved registers (because the normal
|
||||
C-compiler calling sequence in the kernel makes sure they're
|
||||
preserved), and call-clobbered registers in the case of
|
||||
traps), but signal handlers may want to examine or change the
|
||||
complete register state. Here we save anything not saved by
|
||||
the normal entry sequence, so that it may be safely restored
|
||||
(in a possibly modified form) after do_signal returns. */
|
||||
|
||||
addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
|
||||
addi r7, r0, 0; /* Arg 3: int in_syscall */
|
||||
bralid r15, do_signal; /* Handle any signals */
|
||||
add r6, r0, r0; /* Arg 2: sigset_t *oldset */
|
||||
|
||||
|
||||
/* Finally, return to user state. */
|
||||
1:
|
||||
swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
|
||||
1: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
|
||||
VM_OFF;
|
||||
tophys(r1,r1);
|
||||
|
||||
/* MS: Restore all regs */
|
||||
RESTORE_REGS
|
||||
lwi r17, r1, PTO+PT_R17;
|
||||
lwi r16, r1, PTO+PT_R16;
|
||||
addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
|
||||
addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space */
|
||||
lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */
|
||||
DBTRAP_return_user: /* MS: Make global symbol for debugging */
|
||||
rtbd r16, 0; /* MS: Instructions to return from a debug trap */
|
||||
nop;
|
||||
|
||||
|
||||
lwi r1, r1, PT_R1 - PT_SIZE;
|
||||
/* Restore user stack pointer. */
|
||||
bri 6f;
|
||||
|
||||
/* Return to kernel state. */
|
||||
/* MS: Return to kernel state - kgdb */
|
||||
2: VM_OFF;
|
||||
tophys(r1,r1);
|
||||
/* MS: Restore all regs */
|
||||
RESTORE_REGS
|
||||
addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
|
||||
|
||||
lwi r14, r1, PTO+PT_R14;
|
||||
lwi r16, r1, PTO+PT_PC;
|
||||
lwi r17, r1, PTO+PT_R17;
|
||||
addik r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */
|
||||
tovirt(r1,r1);
|
||||
6:
|
||||
DBTRAP_return: /* Make global symbol for debugging */
|
||||
rtbd r16, 0; /* Instructions to return from an IRQ */
|
||||
DBTRAP_return_kernel: /* MS: Make global symbol for debugging */
|
||||
rtbd r16, 0; /* MS: Instructions to return from a debug trap */
|
||||
nop;
|
||||
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
|
|||
siginfo_t info;
|
||||
|
||||
if (kernel_mode(regs)) {
|
||||
debugger(regs);
|
||||
die("Exception in kernel mode", regs, signr);
|
||||
}
|
||||
info.si_signo = signr;
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Microblaze KGDB support
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/pvr.h>
|
||||
|
||||
#define GDB_REG 0
|
||||
#define GDB_PC 32
|
||||
#define GDB_MSR 33
|
||||
#define GDB_EAR 34
|
||||
#define GDB_ESR 35
|
||||
#define GDB_FSR 36
|
||||
#define GDB_BTR 37
|
||||
#define GDB_PVR 38
|
||||
#define GDB_REDR 50
|
||||
#define GDB_RPID 51
|
||||
#define GDB_RZPR 52
|
||||
#define GDB_RTLBX 53
|
||||
#define GDB_RTLBSX 54 /* mfs can't read it */
|
||||
#define GDB_RTLBLO 55
|
||||
#define GDB_RTLBHI 56
|
||||
|
||||
/* keep pvr separately because it is unchangeble */
|
||||
struct pvr_s pvr;
|
||||
|
||||
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||
{
|
||||
int i;
|
||||
unsigned long *pt_regb = (unsigned long *)regs;
|
||||
int temp;
|
||||
/* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */
|
||||
for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++)
|
||||
gdb_regs[i] = pt_regb[i];
|
||||
|
||||
/* Branch target register can't be changed */
|
||||
__asm__ __volatile__ ("mfs %0, rbtr;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_BTR] = temp;
|
||||
|
||||
/* pvr part - we have 11 pvr regs */
|
||||
for (i = 0; i < sizeof(struct pvr_s)/4; i++)
|
||||
gdb_regs[GDB_PVR + i] = pvr.pvr[i];
|
||||
|
||||
/* read special registers - can't be changed */
|
||||
__asm__ __volatile__ ("mfs %0, redr;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_REDR] = temp;
|
||||
__asm__ __volatile__ ("mfs %0, rpid;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_RPID] = temp;
|
||||
__asm__ __volatile__ ("mfs %0, rzpr;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_RZPR] = temp;
|
||||
__asm__ __volatile__ ("mfs %0, rtlbx;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_RTLBX] = temp;
|
||||
__asm__ __volatile__ ("mfs %0, rtlblo;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_RTLBLO] = temp;
|
||||
__asm__ __volatile__ ("mfs %0, rtlbhi;" : "=r"(temp) : );
|
||||
gdb_regs[GDB_RTLBHI] = temp;
|
||||
}
|
||||
|
||||
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||
{
|
||||
int i;
|
||||
unsigned long *pt_regb = (unsigned long *)regs;
|
||||
|
||||
/* pt_regs and gdb_regs have the same 37 values.
|
||||
* The rest of gdb_regs are unused and can't be changed.
|
||||
* r0 register value can't be changed too. */
|
||||
for (i = 1; i < (sizeof(struct pt_regs) / 4) - 1; i++)
|
||||
pt_regb[i] = gdb_regs[i];
|
||||
}
|
||||
|
||||
void microblaze_kgdb_break(struct pt_regs *regs)
|
||||
{
|
||||
if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0)
|
||||
return 0;
|
||||
|
||||
/* Jump over the first arch_kgdb_breakpoint which is barrier to
|
||||
* get kgdb work. The same solution is used for powerpc */
|
||||
if (*(u32 *) (regs->pc) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
|
||||
regs->pc += BREAK_INSTR_SIZE;
|
||||
}
|
||||
|
||||
/* untested */
|
||||
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
|
||||
{
|
||||
int i;
|
||||
unsigned long *pt_regb = (unsigned long *)(p->thread.regs);
|
||||
|
||||
/* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */
|
||||
for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++)
|
||||
gdb_regs[i] = pt_regb[i];
|
||||
|
||||
/* pvr part - we have 11 pvr regs */
|
||||
for (i = 0; i < sizeof(struct pvr_s)/4; i++)
|
||||
gdb_regs[GDB_PVR + i] = pvr.pvr[i];
|
||||
}
|
||||
|
||||
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||
{
|
||||
regs->pc = ip;
|
||||
}
|
||||
|
||||
int kgdb_arch_handle_exception(int vector, int signo, int err_code,
|
||||
char *remcom_in_buffer, char *remcom_out_buffer,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
char *ptr;
|
||||
unsigned long address;
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
switch (remcom_in_buffer[0]) {
|
||||
case 'c':
|
||||
/* handle the optional parameter */
|
||||
ptr = &remcom_in_buffer[1];
|
||||
if (kgdb_hex2long(&ptr, &address))
|
||||
regs->pc = address;
|
||||
|
||||
return 0;
|
||||
}
|
||||
return -1; /* this means that we do not want to exit from the handler */
|
||||
}
|
||||
|
||||
int kgdb_arch_init(void)
|
||||
{
|
||||
get_pvr(&pvr); /* Fill PVR structure */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kgdb_arch_exit(void)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
/*
|
||||
* Global data
|
||||
*/
|
||||
struct kgdb_arch arch_kgdb_ops = {
|
||||
.gdb_bpt_instr = {0xba, 0x0c, 0x00, 0x18}, /* brki r16, 0x18 */
|
||||
};
|
|
@ -37,10 +37,6 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <asm/exceptions.h>
|
||||
|
||||
#if defined(CONFIG_KGDB)
|
||||
int debugger_kernel_faults = 1;
|
||||
#endif
|
||||
|
||||
static unsigned long pte_misses; /* updated by do_page_fault() */
|
||||
static unsigned long pte_errors; /* updated by do_page_fault() */
|
||||
|
||||
|
@ -81,10 +77,6 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
|
|||
}
|
||||
|
||||
/* kernel has accessed a bad area */
|
||||
#if defined(CONFIG_KGDB)
|
||||
if (debugger_kernel_faults)
|
||||
debugger(regs);
|
||||
#endif
|
||||
die("kernel access of bad area", regs, sig);
|
||||
}
|
||||
|
||||
|
@ -115,13 +107,6 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
|
|||
if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11)
|
||||
is_write = 0;
|
||||
|
||||
#if defined(CONFIG_KGDB)
|
||||
if (debugger_fault_handler && regs->trap == 0x300) {
|
||||
debugger_fault_handler(regs);
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_KGDB */
|
||||
|
||||
if (unlikely(in_atomic() || !mm)) {
|
||||
if (kernel_mode(regs))
|
||||
goto bad_area_nosemaphore;
|
||||
|
|
Loading…
Reference in New Issue