123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- /*
- * 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;
- }
- 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
- };
|