arc supports kgdb, but need update -- add function kgdb_roundup_cpus(), or can not pass compiling. At present, add the simple generic one just like other architectures(e.g. tile, mips ...). The related error (with allmodconfig): kernel/built-in.o: In function `kgdb_cpu_enter': kernel/debug/debug_core.c:580: undefined reference to `kgdb_roundup_cpus' Signed-off-by: Chen Gang <gang.chen@asianux.com> Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
		
			
				
	
	
		
			218 lines
		
	
	
	
		
			5.2 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
	
		
			5.2 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * kgdb support for ARC
 | 
						|
 *
 | 
						|
 * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License version 2 as
 | 
						|
 * published by the Free Software Foundation.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/kgdb.h>
 | 
						|
#include <linux/sched.h>
 | 
						|
#include <asm/disasm.h>
 | 
						|
#include <asm/cacheflush.h>
 | 
						|
 | 
						|
static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
 | 
						|
			struct callee_regs *cregs)
 | 
						|
{
 | 
						|
	int regno;
 | 
						|
 | 
						|
	for (regno = 0; regno <= 26; regno++)
 | 
						|
		gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs);
 | 
						|
 | 
						|
	for (regno = 27; regno < GDB_MAX_REGS; regno++)
 | 
						|
		gdb_regs[regno] = 0;
 | 
						|
 | 
						|
	gdb_regs[_FP]		= kernel_regs->fp;
 | 
						|
	gdb_regs[__SP]		= kernel_regs->sp;
 | 
						|
	gdb_regs[_BLINK]	= kernel_regs->blink;
 | 
						|
	gdb_regs[_RET]		= kernel_regs->ret;
 | 
						|
	gdb_regs[_STATUS32]	= kernel_regs->status32;
 | 
						|
	gdb_regs[_LP_COUNT]	= kernel_regs->lp_count;
 | 
						|
	gdb_regs[_LP_END]	= kernel_regs->lp_end;
 | 
						|
	gdb_regs[_LP_START]	= kernel_regs->lp_start;
 | 
						|
	gdb_regs[_BTA]		= kernel_regs->bta;
 | 
						|
	gdb_regs[_STOP_PC]	= kernel_regs->ret;
 | 
						|
}
 | 
						|
 | 
						|
static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
 | 
						|
			struct callee_regs *cregs)
 | 
						|
{
 | 
						|
	int regno;
 | 
						|
 | 
						|
	for (regno = 0; regno <= 26; regno++)
 | 
						|
		set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs);
 | 
						|
 | 
						|
	kernel_regs->fp		= gdb_regs[_FP];
 | 
						|
	kernel_regs->sp		= gdb_regs[__SP];
 | 
						|
	kernel_regs->blink	= gdb_regs[_BLINK];
 | 
						|
	kernel_regs->ret	= gdb_regs[_RET];
 | 
						|
	kernel_regs->status32	= gdb_regs[_STATUS32];
 | 
						|
	kernel_regs->lp_count	= gdb_regs[_LP_COUNT];
 | 
						|
	kernel_regs->lp_end	= gdb_regs[_LP_END];
 | 
						|
	kernel_regs->lp_start	= gdb_regs[_LP_START];
 | 
						|
	kernel_regs->bta	= gdb_regs[_BTA];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
 | 
						|
{
 | 
						|
	to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
 | 
						|
		current->thread.callee_reg);
 | 
						|
}
 | 
						|
 | 
						|
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
 | 
						|
{
 | 
						|
	from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
 | 
						|
		current->thread.callee_reg);
 | 
						|
}
 | 
						|
 | 
						|
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
 | 
						|
				 struct task_struct *task)
 | 
						|
{
 | 
						|
	if (task)
 | 
						|
		to_gdb_regs(gdb_regs, task_pt_regs(task),
 | 
						|
			(struct callee_regs *) task->thread.callee_reg);
 | 
						|
}
 | 
						|
 | 
						|
struct single_step_data_t {
 | 
						|
	uint16_t opcode[2];
 | 
						|
	unsigned long address[2];
 | 
						|
	int is_branch;
 | 
						|
	int armed;
 | 
						|
} single_step_data;
 | 
						|
 | 
						|
static void undo_single_step(struct pt_regs *regs)
 | 
						|
{
 | 
						|
	if (single_step_data.armed) {
 | 
						|
		int i;
 | 
						|
 | 
						|
		for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) {
 | 
						|
			memcpy((void *) single_step_data.address[i],
 | 
						|
				&single_step_data.opcode[i],
 | 
						|
				BREAK_INSTR_SIZE);
 | 
						|
 | 
						|
			flush_icache_range(single_step_data.address[i],
 | 
						|
				single_step_data.address[i] +
 | 
						|
				BREAK_INSTR_SIZE);
 | 
						|
		}
 | 
						|
		single_step_data.armed = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void place_trap(unsigned long address, void *save)
 | 
						|
{
 | 
						|
	memcpy(save, (void *) address, BREAK_INSTR_SIZE);
 | 
						|
	memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr,
 | 
						|
		BREAK_INSTR_SIZE);
 | 
						|
	flush_icache_range(address, address + BREAK_INSTR_SIZE);
 | 
						|
}
 | 
						|
 | 
						|
static void do_single_step(struct pt_regs *regs)
 | 
						|
{
 | 
						|
	single_step_data.is_branch = disasm_next_pc((unsigned long)
 | 
						|
		regs->ret, regs, (struct callee_regs *)
 | 
						|
		current->thread.callee_reg,
 | 
						|
		&single_step_data.address[0],
 | 
						|
		&single_step_data.address[1]);
 | 
						|
 | 
						|
	place_trap(single_step_data.address[0], &single_step_data.opcode[0]);
 | 
						|
 | 
						|
	if (single_step_data.is_branch) {
 | 
						|
		place_trap(single_step_data.address[1],
 | 
						|
			&single_step_data.opcode[1]);
 | 
						|
	}
 | 
						|
 | 
						|
	single_step_data.armed++;
 | 
						|
}
 | 
						|
 | 
						|
int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
 | 
						|
			       char *remcomInBuffer, char *remcomOutBuffer,
 | 
						|
			       struct pt_regs *regs)
 | 
						|
{
 | 
						|
	unsigned long addr;
 | 
						|
	char *ptr;
 | 
						|
 | 
						|
	undo_single_step(regs);
 | 
						|
 | 
						|
	switch (remcomInBuffer[0]) {
 | 
						|
	case 's':
 | 
						|
	case 'c':
 | 
						|
		ptr = &remcomInBuffer[1];
 | 
						|
		if (kgdb_hex2long(&ptr, &addr))
 | 
						|
			regs->ret = addr;
 | 
						|
 | 
						|
	case 'D':
 | 
						|
	case 'k':
 | 
						|
		atomic_set(&kgdb_cpu_doing_single_step, -1);
 | 
						|
 | 
						|
		if (remcomInBuffer[0] == 's') {
 | 
						|
			do_single_step(regs);
 | 
						|
			atomic_set(&kgdb_cpu_doing_single_step,
 | 
						|
				   smp_processor_id());
 | 
						|
		}
 | 
						|
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
 | 
						|
{
 | 
						|
	return instruction_pointer(regs);
 | 
						|
}
 | 
						|
 | 
						|
int kgdb_arch_init(void)
 | 
						|
{
 | 
						|
	single_step_data.armed = 0;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void kgdb_trap(struct pt_regs *regs)
 | 
						|
{
 | 
						|
	/* trap_s 3 is used for breakpoints that overwrite existing
 | 
						|
	 * instructions, while trap_s 4 is used for compiled breakpoints.
 | 
						|
	 *
 | 
						|
	 * with trap_s 3 breakpoints the original instruction needs to be
 | 
						|
	 * restored and continuation needs to start at the location of the
 | 
						|
	 * breakpoint.
 | 
						|
	 *
 | 
						|
	 * with trap_s 4 (compiled) breakpoints, continuation needs to
 | 
						|
	 * start after the breakpoint.
 | 
						|
	 */
 | 
						|
	if (regs->ecr_param == 3)
 | 
						|
		instruction_pointer(regs) -= BREAK_INSTR_SIZE;
 | 
						|
 | 
						|
	kgdb_handle_exception(1, SIGTRAP, 0, regs);
 | 
						|
}
 | 
						|
 | 
						|
void kgdb_arch_exit(void)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
 | 
						|
{
 | 
						|
	instruction_pointer(regs) = ip;
 | 
						|
}
 | 
						|
 | 
						|
static void kgdb_call_nmi_hook(void *ignored)
 | 
						|
{
 | 
						|
	kgdb_nmicallback(raw_smp_processor_id(), NULL);
 | 
						|
}
 | 
						|
 | 
						|
void kgdb_roundup_cpus(unsigned long flags)
 | 
						|
{
 | 
						|
	local_irq_enable();
 | 
						|
	smp_call_function(kgdb_call_nmi_hook, NULL, 0);
 | 
						|
	local_irq_disable();
 | 
						|
}
 | 
						|
 | 
						|
struct kgdb_arch arch_kgdb_ops = {
 | 
						|
	/* breakpoint instruction: TRAP_S 0x3 */
 | 
						|
#ifdef CONFIG_CPU_BIG_ENDIAN
 | 
						|
	.gdb_bpt_instr		= {0x78, 0x7e},
 | 
						|
#else
 | 
						|
	.gdb_bpt_instr		= {0x7e, 0x78},
 | 
						|
#endif
 | 
						|
};
 |