[U-Boot] [PATCH] KGDB set / remove breakpoints

This patch extends the current KGDB logic to handle 'Z' and 'z' GDB packets for setting or removing breakpoints.
Two weak functions have been added to the kgdb_stub.c: arch_kgdb_set_sw_break() and arch_kgdb_remove_sw_break() could be overrode by the arch implementations.
Please note, after applying this patch, those architectures, which already enabled KGDB support, have to create a new asm/kgdb.h and define the length of the break instruction (BREAK_INSTR_SIZE) in that file.
Signed-off-by: Tonny Tzeng tonny.tzeng@gmail.com --- common/kgdb.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ common/kgdb_stubs.c | 12 +++++++ include/kgdb.h | 31 ++++++++++++++++++ 3 files changed, 128 insertions(+), 0 deletions(-)
diff --git a/common/kgdb.c b/common/kgdb.c index 0531452..66378e5 100644 --- a/common/kgdb.c +++ b/common/kgdb.c @@ -220,6 +220,85 @@ hexToInt(char **ptr, int *intValue) return (numChars); }
+/* + * Holds information about breakpoints in a kernel. These breakpoints are + * added and removed by gdb. + */ +static struct kgdb_bkpt kgdb_break[KGDB_MAX_BREAKPOINTS]; + +static int kgdb_set_sw_break(int addr) +{ + int i, breakno = -1; + struct kgdb_bkpt *bkpt; + + for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { + if ((kgdb_break[i].state == BP_SET) && + (kgdb_break[i].bpt_addr == addr)) + return -KGDBERR_BPEXIST; + if ((kgdb_break[i].state == BP_REMOVED) && + (kgdb_break[i].bpt_addr == addr)) { + breakno = i; + break; + } + } + if (breakno == -1) + for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { + if (kgdb_break[i].state == BP_UNDEFINED) { + breakno = i; + break; + } + } + if (breakno == -1) + return -KGDBERR_BPNOENT; + + bkpt = kgdb_break + breakno; + bkpt->state = BP_SET; + bkpt->type = BP_BREAKPOINT; + bkpt->bpt_addr = addr; + arch_kgdb_set_sw_break(bkpt); + + return 0; +} + +static int kgdb_remove_sw_break(int addr) +{ + int i; + struct kgdb_bkpt *bkpt; + + for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { + bkpt = kgdb_break + i; + if ((bkpt->state == BP_SET) && (bkpt->bpt_addr == addr)) { + bkpt->state = BP_REMOVED; + arch_kgdb_remove_sw_break(bkpt); + return 0; + } + } + return -KGDBERR_BPNOENT; +} + +/* Handle the 'z' or 'Z' breakpoint remove or set packets */ +static void gdb_cmd_break(kgdb_data *kdp) +{ + /* + * Since GDB-5.3, it's been drafted that '0' is a software + * breakpoint, '1' is a hardware breakpoint, so let's do that. + */ + char *bpt_type = &remcomInBuffer[1]; + char *ptr = &remcomInBuffer[2]; + int addr, length; + + if (*bpt_type != '0') + return; /* Unsupported. */ + if (*ptr++ != ',' || !hexToInt(&ptr, &addr) || + *ptr++ != ',' || !hexToInt(&ptr, &length)) { + kgdb_error(KGDBERR_BADPARAMS); + } + + if ((remcomInBuffer[0] == 'Z' && kgdb_set_sw_break(addr) == 0) || + (remcomInBuffer[0] == 'z' && kgdb_remove_sw_break(addr) == 0)) + strcpy(remcomOutBuffer, "OK"); +} + /* scan for the sequence $<data>#<checksum> */ static void getpacket(char *buffer) @@ -341,7 +420,9 @@ handle_exception (struct pt_regs *regs)
kgdb_interruptible(0);
+#ifdef KGDB_DEBUG printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs)); +#endif
if (kgdb_setjmp(error_jmp_buf) != 0) panic("kgdb: error or fault in entry init!\n"); @@ -516,6 +597,10 @@ handle_exception (struct pt_regs *regs) kgdb_error(KGDBERR_BADPARAMS); } break; + case 'Z': /* [Z|z]N,AA..AA,LLLL Set/Remove breakpoint type N */ + case 'z': /* LLLL bytes at address AA.AA return OK */ + gdb_cmd_break(&kd); + break; } /* switch */
if (errnum != 0) diff --git a/common/kgdb_stubs.c b/common/kgdb_stubs.c index 19b0c18..2b3f424 100644 --- a/common/kgdb_stubs.c +++ b/common/kgdb_stubs.c @@ -45,6 +45,18 @@ void kgdb_interruptible(int yes) }
__attribute__((weak)) +void arch_kgdb_set_sw_break(struct kgdb_bkpt *bkpt) +{ + return; +} + +__attribute__((weak)) +void arch_kgdb_remove_sw_break(struct kgdb_bkpt *bkpt) +{ + return; +} + +__attribute__((weak)) void kgdb_flush_cache_range(void *from, void *to) { flush_cache((unsigned long)from, (unsigned long)(to - from)); diff --git a/include/kgdb.h b/include/kgdb.h index f543cd6..82ae8ab 100644 --- a/include/kgdb.h +++ b/include/kgdb.h @@ -2,12 +2,41 @@ #define __KGDB_H__
#include <asm/ptrace.h> +#include <asm/kgdb.h>
#define KGDBERR_BADPARAMS 1 #define KGDBERR_NOTHEXDIG 2 #define KGDBERR_MEMFAULT 3 #define KGDBERR_NOSPACE 4 #define KGDBERR_ALIGNFAULT 5 +#define KGDBERR_BPEXIST 6 +#define KGDBERR_BPNOENT 7 + +#ifndef KGDB_MAX_BREAKPOINTS +#define KGDB_MAX_BREAKPOINTS 1000 +#endif + +enum kgdb_bptype { + BP_BREAKPOINT = 0, + BP_HARDWARE_BREAKPOINT, + BP_WRITE_WATCHPOINT, + BP_READ_WATCHPOINT, + BP_ACCESS_WATCHPOINT +}; + +enum kgdb_bpstate { + BP_UNDEFINED = 0, + BP_REMOVED, + BP_SET, + BP_ACTIVE +}; + +struct kgdb_bkpt { + unsigned long bpt_addr; + unsigned char saved_instr[BREAK_INSTR_SIZE]; + enum kgdb_bptype type; + enum kgdb_bpstate state; +};
#define KGDBDATA_MAXREGS 8 #define KGDBDATA_MAXPRIV 8 @@ -56,6 +85,8 @@ extern void kgdb_putreg(struct pt_regs *, int, char *, int); extern void kgdb_putregs(struct pt_regs *, char *, int); extern int kgdb_trap(struct pt_regs *); extern void kgdb_breakpoint(int argc, char *argv[]); +extern void arch_kgdb_set_sw_break(struct kgdb_bkpt *); +extern void arch_kgdb_remove_sw_break(struct kgdb_bkpt *);
/* these functions are provided by the platform serial driver */ extern void kgdb_serial_init(void);

On Saturday 17 April 2010 13:20:11 Tonny Tzeng wrote:
This patch extends the current KGDB logic to handle 'Z' and 'z' GDB packets for setting or removing breakpoints.
Two weak functions have been added to the kgdb_stub.c: arch_kgdb_set_sw_break() and arch_kgdb_remove_sw_break() could be overrode by the arch implementations.
Please note, after applying this patch, those architectures, which already enabled KGDB support, have to create a new asm/kgdb.h and define the length of the break instruction (BREAK_INSTR_SIZE) in that file.
i dont think breaking build is a good idea. i would have the code simply disable itself if BREAK_INSTR_SIZE isnt set.
+/*
- Holds information about breakpoints in a kernel. These breakpoints are
- added and removed by gdb.
- */
+static struct kgdb_bkpt kgdb_break[KGDB_MAX_BREAKPOINTS];
use a space between the type and the name, not a tab
+static int kgdb_set_sw_break(int addr) +{
- int i, breakno = -1;
- struct kgdb_bkpt *bkpt;
- for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
if ((kgdb_break[i].state == BP_SET) &&
(kgdb_break[i].bpt_addr == addr))
return -KGDBERR_BPEXIST;
if ((kgdb_break[i].state == BP_REMOVED) &&
(kgdb_break[i].bpt_addr == addr)) {
breakno = i;
break;
}
- }
there are a few places that loop over the structure to find a bpt by its addr. would probably be better to write a helper function: static int kgdb_find_break_by_addr(int addr) { int i;
for (i = 0; i < KGDB_MAX_BREAKPOINTS; ++i) if (kgdb_break[i].bpt_addr == addr) return i;
return -1; }
then this function becomes a lot simpler: breakno = kgdb_find_break_by_addr(addr); if (breakno != -1) { if (kgdb_break[breakno].state == BP_SET) return ..... .... } else { ....
+#ifndef KGDB_MAX_BREAKPOINTS +#define KGDB_MAX_BREAKPOINTS 1000 +#endif
+struct kgdb_bkpt {
- unsigned long bpt_addr;
- unsigned char saved_instr[BREAK_INSTR_SIZE];
- enum kgdb_bptype type;
- enum kgdb_bpstate state;
+};
the kgdb_bkpt is going to be about 16 bytes, and you're setting a default of 1000 ? that seems like an excessively large number that will simply waste memory (16k of it). please use something much smaller like say 10 or 20.
also, for struct packing, it'd be better if the saved_instr was at the end of the struct so it doesnt force an alignment hole in the middle.
the define also needs a CONFIG_ type prefix on it. -mike

Thanks for the review, Mike.
Please note, after applying this patch, those architectures, which already enabled KGDB support, have to create a new asm/kgdb.h and define the length of the break instruction (BREAK_INSTR_SIZE) in that file.
i dont think breaking build is a good idea. i would have the code simply disable itself if BREAK_INSTR_SIZE isnt set.
That's why I used ifndef in the previous patch, and set BREAK_INSTR_SIZE to 4 if it isn't set, but I thought you prefer "an undefined size should be used as a tip that the arch doesnt yet support break points"?
In Linux kgdb code, there is an arch independent header file include/linux/kgdb.h, which includes arch dependent header asm/kgdb.h. I am trying to include this concept here, unfortunately current u-boot code only has linux/kgdb.h, if we want to define arch specific code in asm/kgdb.h, we may need to include asm/kgdb.h from linux/kgdb.h, so it looks to me it will definitely break other arch, do you have any suggestion for that?
Best Regards, Tonny

On Monday 19 April 2010 04:54:50 Tonny Tzeng wrote:
Please note, after applying this patch, those architectures, which already enabled KGDB support, have to create a new asm/kgdb.h and define the length of the break instruction (BREAK_INSTR_SIZE) in that file.
i dont think breaking build is a good idea. i would have the code simply disable itself if BREAK_INSTR_SIZE isnt set.
That's why I used ifndef in the previous patch, and set BREAK_INSTR_SIZE to 4 if it isn't set, but I thought you prefer "an undefined size should be used as a tip that the arch doesnt yet support break points"?
i meant have the functions always return an error. have the header do: #ifndef BREAK_INSTR_SIZE #define BREAK_INSTR_SIZE 0 #endif
and then in gdb_cmd_break(), do something at the top like: if (BREAK_INSTR_SIZE == 0) return;
In Linux kgdb code, there is an arch independent header file include/linux/kgdb.h, which includes arch dependent header asm/kgdb.h. I am trying to include this concept here, unfortunately current u-boot code only has linux/kgdb.h, if we want to define arch specific code in asm/kgdb.h, we may need to include asm/kgdb.h from linux/kgdb.h, so it looks to me it will definitely break other arch, do you have any suggestion for that?
just post a patch to add asm/kgdb.h stubs for the few arches that you dont implement yourself. i think only blackfin/i386/ppc support kgdb atm. -mike

This patchset adds KGDB support for ARM platforms.
Since the KGDB support for ARM is through the undef instruction handler, we need to add code to the startup file to restore the user context after exception. Unfortunately, each ARM variant has his own start.S, so please reference the patch designed for arm720 to add the 'bad_restore_user_regs' logic to your ARM start-up file.
v1 - initial patchset v2 - (a) move code, which should be common to all arch, to common/kgdb.c; add two new weak funcs to common/kgdb_stubs.c, and separate these common code to the patch "KGDB set / remove breakpoints". (b) remove the watchdog func during the board initialization. (c) flush all cache after mangling instructions.
Best Regards, Tonny

Signed-off-by: Tonny Tzeng tonny.tzeng@gmail.com --- arch/arm/lib/board.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c index f5660a9..f2b028c 100644 --- a/arch/arm/lib/board.c +++ b/arch/arm/lib/board.c @@ -381,6 +381,10 @@ void start_armboot (void) /* miscellaneous platform dependent initialisations */ misc_init_r (); #endif +#if defined(CONFIG_CMD_KGDB) + puts("KGDB: "); + kgdb_init(); +#endif
/* enable exceptions */ enable_interrupts ();

Signed-off-by: Tonny Tzeng tonny.tzeng@gmail.com --- arch/arm/lib/interrupts.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/arch/arm/lib/interrupts.c b/arch/arm/lib/interrupts.c index 1f2b815..6ee4309 100644 --- a/arch/arm/lib/interrupts.c +++ b/arch/arm/lib/interrupts.c @@ -37,6 +37,9 @@
#include <common.h> #include <asm/proc-armv/ptrace.h> +#ifdef CONFIG_CMD_KGDB +#include <kgdb.h> +#endif
#ifdef CONFIG_USE_IRQ DECLARE_GLOBAL_DATA_PTR; @@ -137,6 +140,12 @@ void show_regs (struct pt_regs *regs)
void do_undefined_instruction (struct pt_regs *pt_regs) { +#ifdef CONFIG_CMD_KGDB + if (*(unsigned long *)(instruction_pointer(pt_regs) - 4) == KGDB_COMPILED_BREAK) { + (*debugger_exception_handler)(pt_regs); + return; + } +#endif printf ("undefined instruction\n"); show_regs (pt_regs); bad_mode ();

Signed-off-by: Tonny Tzeng tonny.tzeng@gmail.com --- arch/arm/include/asm/kgdb.h | 66 +++++++++++++++ arch/arm/lib/Makefile | 1 + arch/arm/lib/kgdb.c | 194 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/kgdb.h create mode 100644 arch/arm/lib/kgdb.c
diff --git a/arch/arm/include/asm/kgdb.h b/arch/arm/include/asm/kgdb.h new file mode 100644 index 0000000..dfca31c --- /dev/null +++ b/arch/arm/include/asm/kgdb.h @@ -0,0 +1,66 @@ +/* + * ARM KGDB support + * + * Author: Deepak Saxena dsaxena@mvista.com + * + * Copyright (C) 2002 MontaVista Software Inc. + * + */ +#ifndef __ARM_KGDB_H__ +#define __ARM_KGDB_H__ + +#define BREAK_INSTR_SIZE 4 +#define KGDB_COMPILED_BREAK 0xe7ffdeff + +#ifndef __ASSEMBLY__ + +static inline void arch_kgdb_breakpoint(void) +{ + asm(".word 0xe7ffdeff"); +} + +#endif /* !__ASSEMBLY__ */ + +/* + * From Kevin Hilman: + * + * gdb is expecting the following registers layout. + * + * r0-r15: 1 long word each + * f0-f7: unused, 3 long words each !! + * fps: unused, 1 long word + * cpsr: 1 long word + * + * Even though f0-f7 and fps are not used, they need to be + * present in the registers sent for correct processing in + * the host-side gdb. + * + * In particular, it is crucial that CPSR is in the right place, + * otherwise gdb will not be able to correctly interpret stepping over + * conditional branches. + */ +#define _GP_REGS 16 +#define _FP_REGS 8 +#define _EXTRA_REGS 2 +#define GDB_MAX_REGS (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS) + +#define _R0 0 +#define _R1 1 +#define _R2 2 +#define _R3 3 +#define _R4 4 +#define _R5 5 +#define _R6 6 +#define _R7 7 +#define _R8 8 +#define _R9 9 +#define _R10 10 +#define _FP 11 +#define _IP 12 +#define _SPT 13 +#define _LR 14 +#define _PC 15 +#define _CPSR (GDB_MAX_REGS - 1) + +#endif /* __ASM_KGDB_H__ */ + diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 0293348..36fe528 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -44,6 +44,7 @@ COBJS-y += cache-cp15.o endif COBJS-y += interrupts.o COBJS-y += reset.o +COBJS-$(CONFIG_CMD_KGDB) += kgdb.o
SRCS := $(GLSOBJS:.o=.S) $(GLCOBJS:.o=.c) \ $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) diff --git a/arch/arm/lib/kgdb.c b/arch/arm/lib/kgdb.c new file mode 100644 index 0000000..f7044f6 --- /dev/null +++ b/arch/arm/lib/kgdb.c @@ -0,0 +1,194 @@ +#include <common.h> +#include <asm-generic/signal.h> +#include <kgdb.h> + +/* + * kgdb_breakpoint - generate breakpoint exception + * + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void kgdb_breakpoint (int argc, char *argv[]) +{ + arch_kgdb_breakpoint(); +} + +int kgdb_setjmp(long *buf) +{ + asm(" stmia r0, {r0-r14}\n \ + str lr,[r0, #60]\n \ + mrs r1,cpsr\n \ + str r1,[r0,#64]\n \ + ldr r1,[r0,#4]\n \ + "); + return 0; +} + +void kgdb_longjmp(long *buf, int val) +{ + asm(" str r1,[r0]\n \ + ldr r1,[r0, #64]\n \ + msr spsr,r1\n \ + ldmia r0,{r0-pc}^\n \ + "); +} + +int kgdb_trap(struct pt_regs *regs) +{ + return SIGTRAP; +} + +void kgdb_enter(struct pt_regs *regs, kgdb_data *kdp) +{ + kdp->sigval = kgdb_trap(regs); + kdp->nregs = 0; +} + +void kgdb_exit(struct pt_regs *regs, kgdb_data *kdp) +{ +} + +int kgdb_getregs(struct pt_regs *regs, char *buf, int max) +{ + int i; + unsigned long *gdb_regs = (unsigned long *)buf; + + if (max < (GDB_MAX_REGS * sizeof(unsigned long))) + kgdb_error(KGDBERR_NOSPACE); + + /* Initialize all to zero. */ + for (i = 0; i < GDB_MAX_REGS; i++) + gdb_regs[i] = 0; + + gdb_regs[_R0] = regs->ARM_r0; + gdb_regs[_R1] = regs->ARM_r1; + gdb_regs[_R2] = regs->ARM_r2; + gdb_regs[_R3] = regs->ARM_r3; + gdb_regs[_R4] = regs->ARM_r4; + gdb_regs[_R5] = regs->ARM_r5; + gdb_regs[_R6] = regs->ARM_r6; + gdb_regs[_R7] = regs->ARM_r7; + gdb_regs[_R8] = regs->ARM_r8; + gdb_regs[_R9] = regs->ARM_r9; + gdb_regs[_R10] = regs->ARM_r10; + gdb_regs[_FP] = regs->ARM_fp; + gdb_regs[_IP] = regs->ARM_ip; + gdb_regs[_SPT] = regs->ARM_sp; + gdb_regs[_LR] = regs->ARM_lr; + gdb_regs[_PC] = regs->ARM_pc; + gdb_regs[_CPSR] = regs->ARM_cpsr; + + return GDB_MAX_REGS *sizeof(unsigned long); +} + +void kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length) +{ + unsigned long val, *ptr = (unsigned long *)buf; + + if (regno < 0 || GDB_MAX_REGS <= regno) + kgdb_error(KGDBERR_BADPARAMS); + else { + if (length < 4) + kgdb_error(KGDBERR_NOSPACE); + } + if ((unsigned long)ptr & 3) + kgdb_error(KGDBERR_ALIGNFAULT); + else + val = *ptr; + + switch (regno) { + case _R0: + regs->ARM_r0 = val; break; + case _R1: + regs->ARM_r1 = val; break; + case _R2: + regs->ARM_r2 = val; break; + case _R3: + regs->ARM_r3 = val; break; + case _R4: + regs->ARM_r4 = val; break; + case _R5: + regs->ARM_r5 = val; break; + case _R6: + regs->ARM_r6 = val; break; + case _R7: + regs->ARM_r7 = val; break; + case _R8: + regs->ARM_r8 = val; break; + case _R9: + regs->ARM_r9 = val; break; + case _R10: + regs->ARM_r10 = val; break; + case _FP: + regs->ARM_fp = val; break; + case _IP: + regs->ARM_ip = val; break; + case _SPT: + regs->ARM_sp = val; break; + case _LR: + regs->ARM_lr = val; break; + case _PC: + regs->ARM_pc = val; break; + case 0x19: + regs->ARM_cpsr = val; break; + default: + kgdb_error(KGDBERR_BADPARAMS); + } +} + +void kgdb_putregs(struct pt_regs *regs, char *buf, int length) +{ + unsigned long *gdb_regs = (unsigned long *)buf; + + if (length < (GDB_MAX_REGS *sizeof(unsigned long))) + kgdb_error(KGDBERR_NOSPACE); + + if ((unsigned long)gdb_regs & 3) + kgdb_error(KGDBERR_ALIGNFAULT); + + regs->ARM_r0 = gdb_regs[_R0]; + regs->ARM_r1 = gdb_regs[_R1]; + regs->ARM_r2 = gdb_regs[_R2]; + regs->ARM_r3 = gdb_regs[_R3]; + regs->ARM_r4 = gdb_regs[_R4]; + regs->ARM_r5 = gdb_regs[_R5]; + regs->ARM_r6 = gdb_regs[_R6]; + regs->ARM_r7 = gdb_regs[_R7]; + regs->ARM_r8 = gdb_regs[_R8]; + regs->ARM_r9 = gdb_regs[_R9]; + regs->ARM_r10 = gdb_regs[_R10]; + regs->ARM_fp = gdb_regs[_FP]; + regs->ARM_ip = gdb_regs[_IP]; + regs->ARM_sp = gdb_regs[_SPT]; + regs->ARM_lr = gdb_regs[_LR]; + regs->ARM_pc = gdb_regs[_PC]; + regs->ARM_cpsr = gdb_regs[_CPSR]; +} + +void kgdb_interruptible(int yes) +{ +#ifdef CONFIG_USE_IRQ + if (yes) + enable_interrupts(); + else + disable_interrupts(); +#endif +} + +void arch_kgdb_set_sw_break(struct kgdb_bkpt *bkpt) +{ + int addr = bkpt->bpt_addr; + memcpy(bkpt->saved_instr, (unsigned long *)addr, BREAK_INSTR_SIZE); + *(unsigned long *)addr = KGDB_COMPILED_BREAK; + kgdb_flush_cache_all(); +} + +void arch_kgdb_remove_sw_break(struct kgdb_bkpt *bkpt) +{ + int addr = bkpt->bpt_addr; + memcpy((unsigned long *)addr, bkpt->saved_instr, BREAK_INSTR_SIZE); + kgdb_flush_cache_all(); +} +

Signed-off-by: Tonny Tzeng tonny.tzeng@gmail.com --- arch/arm/cpu/arm720t/start.S | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/arch/arm/cpu/arm720t/start.S b/arch/arm/cpu/arm720t/start.S index 90b7240..fd06298 100644 --- a/arch/arm/cpu/arm720t/start.S +++ b/arch/arm/cpu/arm720t/start.S @@ -459,6 +459,12 @@ lock_loop: mov r0, sp .endm
+ .macro bad_restore_user_regs + ldr lr, [sp, #S_PSR] @ Get SVC cpsr + msr spsr_cxsf, lr + ldmia sp, {r0 - pc}^ @ Restore SVC registers + .endm + .macro irq_save_user_regs sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0-r12 @@ -510,6 +516,7 @@ undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction + bad_restore_user_regs
.align 5 software_interrupt:

I was looking at using this code for Marvell's ARM-like platform. Was this patch ever accepted in uboot? I don't see it in the current git tree.
Thanks! Ben Baron Oracle Development Engineer.

splitting patches is good, but you cant use the same exact summary for each one with no changelog -mike
participants (3)
-
Ben Baron
-
Mike Frysinger
-
Tonny Tzeng