-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Mischa Jonker
authored and
Vineet Gupta
committed
Feb 15, 2013
1 parent
3f2ccf3
commit 31f64f2
Showing
6 changed files
with
276 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: 2e651ea1596b0ee25af4fcdc4cd13cbb33ffc254 | ||
refs/heads/master: f46121bd26d7957866739313f1e098a682e8d3e4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
#ifndef __ARC_KGDB_H__ | ||
#define __ARC_KGDB_H__ | ||
|
||
#ifdef CONFIG_KGDB | ||
|
||
#include <asm/user.h> | ||
|
||
/* to ensure compatibility with Linux 2.6.35, we don't implement the get/set | ||
* register API yet */ | ||
#undef DBG_MAX_REG_NUM | ||
|
||
#define GDB_MAX_REGS 39 | ||
|
||
#define BREAK_INSTR_SIZE 2 | ||
#define CACHE_FLUSH_IS_SAFE 1 | ||
#define NUMREGBYTES (GDB_MAX_REGS * 4) | ||
#define BUFMAX 2048 | ||
|
||
static inline void arch_kgdb_breakpoint(void) | ||
{ | ||
__asm__ __volatile__ ("trap_s 0x4\n"); | ||
} | ||
|
||
extern void kgdb_trap(struct pt_regs *regs, int param); | ||
|
||
enum arc700_linux_regnums { | ||
_R0 = 0, | ||
_R1, _R2, _R3, _R4, _R5, _R6, _R7, _R8, _R9, _R10, _R11, _R12, _R13, | ||
_R14, _R15, _R16, _R17, _R18, _R19, _R20, _R21, _R22, _R23, _R24, | ||
_R25, _R26, | ||
_BTA = 27, | ||
_LP_START = 28, | ||
_LP_END = 29, | ||
_LP_COUNT = 30, | ||
_STATUS32 = 31, | ||
_BLINK = 32, | ||
_FP = 33, | ||
__SP = 34, | ||
_EFA = 35, | ||
_RET = 36, | ||
_ORIG_R8 = 37, | ||
_STOP_PC = 38 | ||
}; | ||
|
||
#else | ||
static inline void kgdb_trap(struct pt_regs *regs, int param) | ||
{ | ||
} | ||
#endif | ||
|
||
#endif /* __ARC_KGDB_H__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/* | ||
* 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 <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, int param) | ||
{ | ||
/* 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 (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; | ||
} | ||
|
||
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 | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters