xref: /openbmc/linux/arch/arm/kernel/kgdb.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25cbad0ebSJason Wessel /*
35cbad0ebSJason Wessel  * arch/arm/kernel/kgdb.c
45cbad0ebSJason Wessel  *
55cbad0ebSJason Wessel  * ARM KGDB support
65cbad0ebSJason Wessel  *
75cbad0ebSJason Wessel  * Copyright (c) 2002-2004 MontaVista Software, Inc
85cbad0ebSJason Wessel  * Copyright (c) 2008 Wind River Systems, Inc.
95cbad0ebSJason Wessel  *
105cbad0ebSJason Wessel  * Authors:  George Davis <davis_g@mvista.com>
115cbad0ebSJason Wessel  *           Deepak Saxena <dsaxena@plexity.net>
125cbad0ebSJason Wessel  */
135d8614ccSWill Deacon #include <linux/irq.h>
1462a0309cSJason Wessel #include <linux/kdebug.h>
155cbad0ebSJason Wessel #include <linux/kgdb.h>
1623a4e405SDoug Anderson #include <linux/uaccess.h>
1723a4e405SDoug Anderson 
18fca08f32SWang Nan #include <asm/patch.h>
195cbad0ebSJason Wessel #include <asm/traps.h>
205cbad0ebSJason Wessel 
2122eeef4bSJason Wessel struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
225cbad0ebSJason Wessel {
2322eeef4bSJason Wessel 	{ "r0", 4, offsetof(struct pt_regs, ARM_r0)},
2422eeef4bSJason Wessel 	{ "r1", 4, offsetof(struct pt_regs, ARM_r1)},
2522eeef4bSJason Wessel 	{ "r2", 4, offsetof(struct pt_regs, ARM_r2)},
2622eeef4bSJason Wessel 	{ "r3", 4, offsetof(struct pt_regs, ARM_r3)},
2722eeef4bSJason Wessel 	{ "r4", 4, offsetof(struct pt_regs, ARM_r4)},
2822eeef4bSJason Wessel 	{ "r5", 4, offsetof(struct pt_regs, ARM_r5)},
2922eeef4bSJason Wessel 	{ "r6", 4, offsetof(struct pt_regs, ARM_r6)},
3022eeef4bSJason Wessel 	{ "r7", 4, offsetof(struct pt_regs, ARM_r7)},
3122eeef4bSJason Wessel 	{ "r8", 4, offsetof(struct pt_regs, ARM_r8)},
3222eeef4bSJason Wessel 	{ "r9", 4, offsetof(struct pt_regs, ARM_r9)},
3322eeef4bSJason Wessel 	{ "r10", 4, offsetof(struct pt_regs, ARM_r10)},
3422eeef4bSJason Wessel 	{ "fp", 4, offsetof(struct pt_regs, ARM_fp)},
3522eeef4bSJason Wessel 	{ "ip", 4, offsetof(struct pt_regs, ARM_ip)},
3622eeef4bSJason Wessel 	{ "sp", 4, offsetof(struct pt_regs, ARM_sp)},
3722eeef4bSJason Wessel 	{ "lr", 4, offsetof(struct pt_regs, ARM_lr)},
3822eeef4bSJason Wessel 	{ "pc", 4, offsetof(struct pt_regs, ARM_pc)},
3922eeef4bSJason Wessel 	{ "f0", 12, -1 },
4022eeef4bSJason Wessel 	{ "f1", 12, -1 },
4122eeef4bSJason Wessel 	{ "f2", 12, -1 },
4222eeef4bSJason Wessel 	{ "f3", 12, -1 },
4322eeef4bSJason Wessel 	{ "f4", 12, -1 },
4422eeef4bSJason Wessel 	{ "f5", 12, -1 },
4522eeef4bSJason Wessel 	{ "f6", 12, -1 },
4622eeef4bSJason Wessel 	{ "f7", 12, -1 },
4722eeef4bSJason Wessel 	{ "fps", 4, -1 },
4822eeef4bSJason Wessel 	{ "cpsr", 4, offsetof(struct pt_regs, ARM_cpsr)},
4922eeef4bSJason Wessel };
505cbad0ebSJason Wessel 
dbg_get_reg(int regno,void * mem,struct pt_regs * regs)5122eeef4bSJason Wessel char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
5222eeef4bSJason Wessel {
5322eeef4bSJason Wessel 	if (regno >= DBG_MAX_REG_NUM || regno < 0)
5422eeef4bSJason Wessel 		return NULL;
555cbad0ebSJason Wessel 
5622eeef4bSJason Wessel 	if (dbg_reg_def[regno].offset != -1)
5722eeef4bSJason Wessel 		memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
5822eeef4bSJason Wessel 		       dbg_reg_def[regno].size);
5922eeef4bSJason Wessel 	else
6022eeef4bSJason Wessel 		memset(mem, 0, dbg_reg_def[regno].size);
6122eeef4bSJason Wessel 	return dbg_reg_def[regno].name;
625cbad0ebSJason Wessel }
635cbad0ebSJason Wessel 
dbg_set_reg(int regno,void * mem,struct pt_regs * regs)6422eeef4bSJason Wessel int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
655cbad0ebSJason Wessel {
6622eeef4bSJason Wessel 	if (regno >= DBG_MAX_REG_NUM || regno < 0)
6722eeef4bSJason Wessel 		return -EINVAL;
6822eeef4bSJason Wessel 
6922eeef4bSJason Wessel 	if (dbg_reg_def[regno].offset != -1)
7022eeef4bSJason Wessel 		memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
7122eeef4bSJason Wessel 		       dbg_reg_def[regno].size);
7222eeef4bSJason Wessel 	return 0;
735cbad0ebSJason Wessel }
745cbad0ebSJason Wessel 
755cbad0ebSJason Wessel void
sleeping_thread_to_gdb_regs(unsigned long * gdb_regs,struct task_struct * task)765cbad0ebSJason Wessel sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
775cbad0ebSJason Wessel {
78001bf455SDoug Anderson 	struct thread_info *ti;
795cbad0ebSJason Wessel 	int regno;
805cbad0ebSJason Wessel 
815cbad0ebSJason Wessel 	/* Just making sure... */
825cbad0ebSJason Wessel 	if (task == NULL)
835cbad0ebSJason Wessel 		return;
845cbad0ebSJason Wessel 
855cbad0ebSJason Wessel 	/* Initialize to zero */
86834b2964SRabin Vincent 	for (regno = 0; regno < GDB_MAX_REGS; regno++)
875cbad0ebSJason Wessel 		gdb_regs[regno] = 0;
885cbad0ebSJason Wessel 
895cbad0ebSJason Wessel 	/* Otherwise, we have only some registers from switch_to() */
90001bf455SDoug Anderson 	ti			= task_thread_info(task);
91001bf455SDoug Anderson 	gdb_regs[_R4]		= ti->cpu_context.r4;
92001bf455SDoug Anderson 	gdb_regs[_R5]		= ti->cpu_context.r5;
93001bf455SDoug Anderson 	gdb_regs[_R6]		= ti->cpu_context.r6;
94001bf455SDoug Anderson 	gdb_regs[_R7]		= ti->cpu_context.r7;
95001bf455SDoug Anderson 	gdb_regs[_R8]		= ti->cpu_context.r8;
96001bf455SDoug Anderson 	gdb_regs[_R9]		= ti->cpu_context.r9;
97001bf455SDoug Anderson 	gdb_regs[_R10]		= ti->cpu_context.sl;
98001bf455SDoug Anderson 	gdb_regs[_FP]		= ti->cpu_context.fp;
99001bf455SDoug Anderson 	gdb_regs[_SPT]		= ti->cpu_context.sp;
100001bf455SDoug Anderson 	gdb_regs[_PC]		= ti->cpu_context.pc;
1015cbad0ebSJason Wessel }
1025cbad0ebSJason Wessel 
kgdb_arch_set_pc(struct pt_regs * regs,unsigned long pc)103dcc78711SJason Wessel void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
104dcc78711SJason Wessel {
105dcc78711SJason Wessel 	regs->ARM_pc = pc;
106dcc78711SJason Wessel }
107dcc78711SJason Wessel 
1085cbad0ebSJason Wessel static int compiled_break;
1095cbad0ebSJason Wessel 
kgdb_arch_handle_exception(int exception_vector,int signo,int err_code,char * remcom_in_buffer,char * remcom_out_buffer,struct pt_regs * linux_regs)1105cbad0ebSJason Wessel int kgdb_arch_handle_exception(int exception_vector, int signo,
1115cbad0ebSJason Wessel 			       int err_code, char *remcom_in_buffer,
1125cbad0ebSJason Wessel 			       char *remcom_out_buffer,
1135cbad0ebSJason Wessel 			       struct pt_regs *linux_regs)
1145cbad0ebSJason Wessel {
1155cbad0ebSJason Wessel 	unsigned long addr;
1165cbad0ebSJason Wessel 	char *ptr;
1175cbad0ebSJason Wessel 
1185cbad0ebSJason Wessel 	switch (remcom_in_buffer[0]) {
1195cbad0ebSJason Wessel 	case 'D':
1205cbad0ebSJason Wessel 	case 'k':
1215cbad0ebSJason Wessel 	case 'c':
1225cbad0ebSJason Wessel 		/*
1235cbad0ebSJason Wessel 		 * Try to read optional parameter, pc unchanged if no parm.
1245cbad0ebSJason Wessel 		 * If this was a compiled breakpoint, we need to move
1255cbad0ebSJason Wessel 		 * to the next instruction or we will just breakpoint
1265cbad0ebSJason Wessel 		 * over and over again.
1275cbad0ebSJason Wessel 		 */
1285cbad0ebSJason Wessel 		ptr = &remcom_in_buffer[1];
1295cbad0ebSJason Wessel 		if (kgdb_hex2long(&ptr, &addr))
1305cbad0ebSJason Wessel 			linux_regs->ARM_pc = addr;
1315cbad0ebSJason Wessel 		else if (compiled_break == 1)
1325cbad0ebSJason Wessel 			linux_regs->ARM_pc += 4;
1335cbad0ebSJason Wessel 
1345cbad0ebSJason Wessel 		compiled_break = 0;
1355cbad0ebSJason Wessel 
1365cbad0ebSJason Wessel 		return 0;
1375cbad0ebSJason Wessel 	}
1385cbad0ebSJason Wessel 
1395cbad0ebSJason Wessel 	return -1;
1405cbad0ebSJason Wessel }
1415cbad0ebSJason Wessel 
kgdb_brk_fn(struct pt_regs * regs,unsigned int instr)1425cbad0ebSJason Wessel static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
1435cbad0ebSJason Wessel {
1445cbad0ebSJason Wessel 	kgdb_handle_exception(1, SIGTRAP, 0, regs);
1455cbad0ebSJason Wessel 
1465cbad0ebSJason Wessel 	return 0;
1475cbad0ebSJason Wessel }
1485cbad0ebSJason Wessel 
kgdb_compiled_brk_fn(struct pt_regs * regs,unsigned int instr)1495cbad0ebSJason Wessel static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
1505cbad0ebSJason Wessel {
1515cbad0ebSJason Wessel 	compiled_break = 1;
1525cbad0ebSJason Wessel 	kgdb_handle_exception(1, SIGTRAP, 0, regs);
1535cbad0ebSJason Wessel 
1545cbad0ebSJason Wessel 	return 0;
1555cbad0ebSJason Wessel }
1565cbad0ebSJason Wessel 
157*d920eaa4SRussell King (Oracle) static struct undef_hook kgdb_brkpt_arm_hook = {
1585cbad0ebSJason Wessel 	.instr_mask		= 0xffffffff,
1595cbad0ebSJason Wessel 	.instr_val		= KGDB_BREAKINST,
160*d920eaa4SRussell King (Oracle) 	.cpsr_mask		= PSR_T_BIT | MODE_MASK,
1616bf755dbSOmar Sandoval 	.cpsr_val		= SVC_MODE,
1625cbad0ebSJason Wessel 	.fn			= kgdb_brk_fn
1635cbad0ebSJason Wessel };
1645cbad0ebSJason Wessel 
165*d920eaa4SRussell King (Oracle) static struct undef_hook kgdb_brkpt_thumb_hook = {
166*d920eaa4SRussell King (Oracle) 	.instr_mask		= 0xffff,
167*d920eaa4SRussell King (Oracle) 	.instr_val		= KGDB_BREAKINST & 0xffff,
168*d920eaa4SRussell King (Oracle) 	.cpsr_mask		= PSR_T_BIT | MODE_MASK,
169*d920eaa4SRussell King (Oracle) 	.cpsr_val		= PSR_T_BIT | SVC_MODE,
170*d920eaa4SRussell King (Oracle) 	.fn			= kgdb_brk_fn
171*d920eaa4SRussell King (Oracle) };
172*d920eaa4SRussell King (Oracle) 
173*d920eaa4SRussell King (Oracle) static struct undef_hook kgdb_compiled_brkpt_arm_hook = {
1745cbad0ebSJason Wessel 	.instr_mask		= 0xffffffff,
1755cbad0ebSJason Wessel 	.instr_val		= KGDB_COMPILED_BREAK,
176*d920eaa4SRussell King (Oracle) 	.cpsr_mask		= PSR_T_BIT | MODE_MASK,
1776bf755dbSOmar Sandoval 	.cpsr_val		= SVC_MODE,
1785cbad0ebSJason Wessel 	.fn			= kgdb_compiled_brk_fn
1795cbad0ebSJason Wessel };
1805cbad0ebSJason Wessel 
181*d920eaa4SRussell King (Oracle) static struct undef_hook kgdb_compiled_brkpt_thumb_hook = {
182*d920eaa4SRussell King (Oracle) 	.instr_mask		= 0xffff,
183*d920eaa4SRussell King (Oracle) 	.instr_val		= KGDB_COMPILED_BREAK & 0xffff,
184*d920eaa4SRussell King (Oracle) 	.cpsr_mask		= PSR_T_BIT | MODE_MASK,
185*d920eaa4SRussell King (Oracle) 	.cpsr_val		= PSR_T_BIT | SVC_MODE,
186*d920eaa4SRussell King (Oracle) 	.fn			= kgdb_compiled_brk_fn
187*d920eaa4SRussell King (Oracle) };
188*d920eaa4SRussell King (Oracle) 
__kgdb_notify(struct die_args * args,unsigned long cmd)18962a0309cSJason Wessel static int __kgdb_notify(struct die_args *args, unsigned long cmd)
19062a0309cSJason Wessel {
19162a0309cSJason Wessel 	struct pt_regs *regs = args->regs;
19262a0309cSJason Wessel 
19362a0309cSJason Wessel 	if (kgdb_handle_exception(1, args->signr, cmd, regs))
19462a0309cSJason Wessel 		return NOTIFY_DONE;
19562a0309cSJason Wessel 	return NOTIFY_STOP;
19662a0309cSJason Wessel }
19762a0309cSJason Wessel static int
kgdb_notify(struct notifier_block * self,unsigned long cmd,void * ptr)19862a0309cSJason Wessel kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
19962a0309cSJason Wessel {
20062a0309cSJason Wessel 	unsigned long flags;
20162a0309cSJason Wessel 	int ret;
20262a0309cSJason Wessel 
20362a0309cSJason Wessel 	local_irq_save(flags);
20462a0309cSJason Wessel 	ret = __kgdb_notify(ptr, cmd);
20562a0309cSJason Wessel 	local_irq_restore(flags);
20662a0309cSJason Wessel 
20762a0309cSJason Wessel 	return ret;
20862a0309cSJason Wessel }
20962a0309cSJason Wessel 
21062a0309cSJason Wessel static struct notifier_block kgdb_notifier = {
21162a0309cSJason Wessel 	.notifier_call	= kgdb_notify,
21262a0309cSJason Wessel 	.priority	= -INT_MAX,
21362a0309cSJason Wessel };
21462a0309cSJason Wessel 
21562a0309cSJason Wessel 
2165cbad0ebSJason Wessel /**
2175cbad0ebSJason Wessel  *	kgdb_arch_init - Perform any architecture specific initalization.
2185cbad0ebSJason Wessel  *
2195cbad0ebSJason Wessel  *	This function will handle the initalization of any architecture
2205cbad0ebSJason Wessel  *	specific callbacks.
2215cbad0ebSJason Wessel  */
kgdb_arch_init(void)2225cbad0ebSJason Wessel int kgdb_arch_init(void)
2235cbad0ebSJason Wessel {
22462a0309cSJason Wessel 	int ret = register_die_notifier(&kgdb_notifier);
22562a0309cSJason Wessel 
22662a0309cSJason Wessel 	if (ret != 0)
22762a0309cSJason Wessel 		return ret;
22862a0309cSJason Wessel 
229*d920eaa4SRussell King (Oracle) 	register_undef_hook(&kgdb_brkpt_arm_hook);
230*d920eaa4SRussell King (Oracle) 	register_undef_hook(&kgdb_brkpt_thumb_hook);
231*d920eaa4SRussell King (Oracle) 	register_undef_hook(&kgdb_compiled_brkpt_arm_hook);
232*d920eaa4SRussell King (Oracle) 	register_undef_hook(&kgdb_compiled_brkpt_thumb_hook);
2335cbad0ebSJason Wessel 
2345cbad0ebSJason Wessel 	return 0;
2355cbad0ebSJason Wessel }
2365cbad0ebSJason Wessel 
2375cbad0ebSJason Wessel /**
2385cbad0ebSJason Wessel  *	kgdb_arch_exit - Perform any architecture specific uninitalization.
2395cbad0ebSJason Wessel  *
2405cbad0ebSJason Wessel  *	This function will handle the uninitalization of any architecture
2415cbad0ebSJason Wessel  *	specific callbacks, for dynamic registration and unregistration.
2425cbad0ebSJason Wessel  */
kgdb_arch_exit(void)2435cbad0ebSJason Wessel void kgdb_arch_exit(void)
2445cbad0ebSJason Wessel {
245*d920eaa4SRussell King (Oracle) 	unregister_undef_hook(&kgdb_brkpt_arm_hook);
246*d920eaa4SRussell King (Oracle) 	unregister_undef_hook(&kgdb_brkpt_thumb_hook);
247*d920eaa4SRussell King (Oracle) 	unregister_undef_hook(&kgdb_compiled_brkpt_arm_hook);
248*d920eaa4SRussell King (Oracle) 	unregister_undef_hook(&kgdb_compiled_brkpt_thumb_hook);
24962a0309cSJason Wessel 	unregister_die_notifier(&kgdb_notifier);
2505cbad0ebSJason Wessel }
2515cbad0ebSJason Wessel 
kgdb_arch_set_breakpoint(struct kgdb_bkpt * bpt)25223a4e405SDoug Anderson int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
25323a4e405SDoug Anderson {
25423a4e405SDoug Anderson 	int err;
25523a4e405SDoug Anderson 
25623a4e405SDoug Anderson 	/* patch_text() only supports int-sized breakpoints */
25723a4e405SDoug Anderson 	BUILD_BUG_ON(sizeof(int) != BREAK_INSTR_SIZE);
25823a4e405SDoug Anderson 
259fe557319SChristoph Hellwig 	err = copy_from_kernel_nofault(bpt->saved_instr, (char *)bpt->bpt_addr,
26023a4e405SDoug Anderson 				BREAK_INSTR_SIZE);
26123a4e405SDoug Anderson 	if (err)
26223a4e405SDoug Anderson 		return err;
26323a4e405SDoug Anderson 
2647ae85dc7SDoug Anderson 	/* Machine is already stopped, so we can use __patch_text() directly */
2657ae85dc7SDoug Anderson 	__patch_text((void *)bpt->bpt_addr,
26623a4e405SDoug Anderson 		     *(unsigned int *)arch_kgdb_ops.gdb_bpt_instr);
26723a4e405SDoug Anderson 
26823a4e405SDoug Anderson 	return err;
26923a4e405SDoug Anderson }
27023a4e405SDoug Anderson 
kgdb_arch_remove_breakpoint(struct kgdb_bkpt * bpt)27123a4e405SDoug Anderson int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
27223a4e405SDoug Anderson {
2737ae85dc7SDoug Anderson 	/* Machine is already stopped, so we can use __patch_text() directly */
2747ae85dc7SDoug Anderson 	__patch_text((void *)bpt->bpt_addr, *(unsigned int *)bpt->saved_instr);
27523a4e405SDoug Anderson 
27623a4e405SDoug Anderson 	return 0;
27723a4e405SDoug Anderson }
27823a4e405SDoug Anderson 
2795cbad0ebSJason Wessel /*
2805cbad0ebSJason Wessel  * Register our undef instruction hooks with ARM undef core.
281ad61dd30SStephen Boyd  * We register a hook specifically looking for the KGB break inst
2825cbad0ebSJason Wessel  * and we handle the normal undef case within the do_undefinstr
2835cbad0ebSJason Wessel  * handler.
2845cbad0ebSJason Wessel  */
285cc028297SChristophe Leroy const struct kgdb_arch arch_kgdb_ops = {
2865cbad0ebSJason Wessel #ifndef __ARMEB__
2875cbad0ebSJason Wessel 	.gdb_bpt_instr		= {0xfe, 0xde, 0xff, 0xe7}
2885cbad0ebSJason Wessel #else /* ! __ARMEB__ */
2895cbad0ebSJason Wessel 	.gdb_bpt_instr		= {0xe7, 0xff, 0xde, 0xfe}
2905cbad0ebSJason Wessel #endif
2915cbad0ebSJason Wessel };
292