aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel/kgdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/tile/kernel/kgdb.c')
-rw-r--r--arch/tile/kernel/kgdb.c497
1 files changed, 0 insertions, 497 deletions
diff --git a/arch/tile/kernel/kgdb.c b/arch/tile/kernel/kgdb.c
deleted file mode 100644
index d4eb5fb2df9d..000000000000
--- a/arch/tile/kernel/kgdb.c
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * Copyright 2013 Tilera Corporation. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for
- * more details.
- *
- * TILE-Gx KGDB support.
- */
-
-#include <linux/ptrace.h>
-#include <linux/kgdb.h>
-#include <linux/kdebug.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-#include <linux/sched/task_stack.h>
-
-#include <asm/cacheflush.h>
-
-static tile_bundle_bits singlestep_insn = TILEGX_BPT_BUNDLE | DIE_SSTEPBP;
-static unsigned long stepped_addr;
-static tile_bundle_bits stepped_instr;
-
-struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
- { "r0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0])},
- { "r1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1])},
- { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2])},
- { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3])},
- { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4])},
- { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5])},
- { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6])},
- { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7])},
- { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8])},
- { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9])},
- { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10])},
- { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11])},
- { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12])},
- { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13])},
- { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14])},
- { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15])},
- { "r16", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[16])},
- { "r17", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[17])},
- { "r18", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[18])},
- { "r19", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[19])},
- { "r20", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[20])},
- { "r21", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[21])},
- { "r22", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[22])},
- { "r23", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[23])},
- { "r24", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[24])},
- { "r25", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[25])},
- { "r26", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[26])},
- { "r27", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[27])},
- { "r28", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[28])},
- { "r29", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[29])},
- { "r30", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[30])},
- { "r31", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[31])},
- { "r32", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[32])},
- { "r33", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[33])},
- { "r34", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[34])},
- { "r35", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[35])},
- { "r36", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[36])},
- { "r37", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[37])},
- { "r38", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[38])},
- { "r39", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[39])},
- { "r40", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[40])},
- { "r41", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[41])},
- { "r42", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[42])},
- { "r43", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[43])},
- { "r44", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[44])},
- { "r45", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[45])},
- { "r46", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[46])},
- { "r47", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[47])},
- { "r48", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[48])},
- { "r49", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[49])},
- { "r50", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[50])},
- { "r51", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[51])},
- { "r52", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[52])},
- { "tp", GDB_SIZEOF_REG, offsetof(struct pt_regs, tp)},
- { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp)},
- { "lr", GDB_SIZEOF_REG, offsetof(struct pt_regs, lr)},
- { "sn", GDB_SIZEOF_REG, -1},
- { "idn0", GDB_SIZEOF_REG, -1},
- { "idn1", GDB_SIZEOF_REG, -1},
- { "udn0", GDB_SIZEOF_REG, -1},
- { "udn1", GDB_SIZEOF_REG, -1},
- { "udn2", GDB_SIZEOF_REG, -1},
- { "udn3", GDB_SIZEOF_REG, -1},
- { "zero", GDB_SIZEOF_REG, -1},
- { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, pc)},
- { "faultnum", GDB_SIZEOF_REG, offsetof(struct pt_regs, faultnum)},
-};
-
-char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
-{
- if (regno >= DBG_MAX_REG_NUM || regno < 0)
- return NULL;
-
- if (dbg_reg_def[regno].offset != -1)
- memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
- dbg_reg_def[regno].size);
- else
- memset(mem, 0, dbg_reg_def[regno].size);
- return dbg_reg_def[regno].name;
-}
-
-int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
-{
- if (regno >= DBG_MAX_REG_NUM || regno < 0)
- return -EINVAL;
-
- if (dbg_reg_def[regno].offset != -1)
- memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
- dbg_reg_def[regno].size);
- return 0;
-}
-
-/*
- * Similar to pt_regs_to_gdb_regs() except that process is sleeping and so
- * we may not be able to get all the info.
- */
-void
-sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
-{
- struct pt_regs *thread_regs;
- const int NGPRS = TREG_LAST_GPR + 1;
-
- if (task == NULL)
- return;
-
- thread_regs = task_pt_regs(task);
- memcpy(gdb_regs, thread_regs, NGPRS * sizeof(unsigned long));
- memset(&gdb_regs[NGPRS], 0,
- (TILEGX_PC_REGNUM - NGPRS) * sizeof(unsigned long));
- gdb_regs[TILEGX_PC_REGNUM] = thread_regs->pc;
- gdb_regs[TILEGX_FAULTNUM_REGNUM] = thread_regs->faultnum;
-}
-
-void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
-{
- regs->pc = pc;
-}
-
-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();
-}
-
-/*
- * Convert a kernel address to the writable kernel text mapping.
- */
-static unsigned long writable_address(unsigned long addr)
-{
- unsigned long ret = 0;
-
- if (core_kernel_text(addr))
- ret = ktext_writable_addr(addr);
- else if (is_module_text_address(addr))
- ret = addr;
- else
- pr_err("Unknown virtual address 0x%lx\n", addr);
-
- return ret;
-}
-
-/*
- * Calculate the new address for after a step.
- */
-static unsigned long get_step_address(struct pt_regs *regs)
-{
- int src_reg;
- int jump_off;
- int br_off;
- unsigned long addr;
- unsigned int opcode;
- tile_bundle_bits bundle;
-
- /* Move to the next instruction by default. */
- addr = regs->pc + TILEGX_BUNDLE_SIZE_IN_BYTES;
- bundle = *(unsigned long *)instruction_pointer(regs);
-
- /* 0: X mode, Otherwise: Y mode. */
- if (bundle & TILEGX_BUNDLE_MODE_MASK) {
- if (get_Opcode_Y1(bundle) == RRR_1_OPCODE_Y1 &&
- get_RRROpcodeExtension_Y1(bundle) ==
- UNARY_RRR_1_OPCODE_Y1) {
- opcode = get_UnaryOpcodeExtension_Y1(bundle);
-
- switch (opcode) {
- case JALR_UNARY_OPCODE_Y1:
- case JALRP_UNARY_OPCODE_Y1:
- case JR_UNARY_OPCODE_Y1:
- case JRP_UNARY_OPCODE_Y1:
- src_reg = get_SrcA_Y1(bundle);
- dbg_get_reg(src_reg, &addr, regs);
- break;
- }
- }
- } else if (get_Opcode_X1(bundle) == RRR_0_OPCODE_X1) {
- if (get_RRROpcodeExtension_X1(bundle) ==
- UNARY_RRR_0_OPCODE_X1) {
- opcode = get_UnaryOpcodeExtension_X1(bundle);
-
- switch (opcode) {
- case JALR_UNARY_OPCODE_X1:
- case JALRP_UNARY_OPCODE_X1:
- case JR_UNARY_OPCODE_X1:
- case JRP_UNARY_OPCODE_X1:
- src_reg = get_SrcA_X1(bundle);
- dbg_get_reg(src_reg, &addr, regs);
- break;
- }
- }
- } else if (get_Opcode_X1(bundle) == JUMP_OPCODE_X1) {
- opcode = get_JumpOpcodeExtension_X1(bundle);
-
- switch (opcode) {
- case JAL_JUMP_OPCODE_X1:
- case J_JUMP_OPCODE_X1:
- jump_off = sign_extend(get_JumpOff_X1(bundle), 27);
- addr = regs->pc +
- (jump_off << TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES);
- break;
- }
- } else if (get_Opcode_X1(bundle) == BRANCH_OPCODE_X1) {
- br_off = 0;
- opcode = get_BrType_X1(bundle);
-
- switch (opcode) {
- case BEQZT_BRANCH_OPCODE_X1:
- case BEQZ_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) == 0)
- br_off = get_BrOff_X1(bundle);
- break;
- case BGEZT_BRANCH_OPCODE_X1:
- case BGEZ_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) >= 0)
- br_off = get_BrOff_X1(bundle);
- break;
- case BGTZT_BRANCH_OPCODE_X1:
- case BGTZ_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) > 0)
- br_off = get_BrOff_X1(bundle);
- break;
- case BLBCT_BRANCH_OPCODE_X1:
- case BLBC_BRANCH_OPCODE_X1:
- if (!(get_SrcA_X1(bundle) & 1))
- br_off = get_BrOff_X1(bundle);
- break;
- case BLBST_BRANCH_OPCODE_X1:
- case BLBS_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) & 1)
- br_off = get_BrOff_X1(bundle);
- break;
- case BLEZT_BRANCH_OPCODE_X1:
- case BLEZ_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) <= 0)
- br_off = get_BrOff_X1(bundle);
- break;
- case BLTZT_BRANCH_OPCODE_X1:
- case BLTZ_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) < 0)
- br_off = get_BrOff_X1(bundle);
- break;
- case BNEZT_BRANCH_OPCODE_X1:
- case BNEZ_BRANCH_OPCODE_X1:
- if (get_SrcA_X1(bundle) != 0)
- br_off = get_BrOff_X1(bundle);
- break;
- }
-
- if (br_off != 0) {
- br_off = sign_extend(br_off, 17);
- addr = regs->pc +
- (br_off << TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES);
- }
- }
-
- return addr;
-}
-
-/*
- * Replace the next instruction after the current instruction with a
- * breakpoint instruction.
- */
-static void do_single_step(struct pt_regs *regs)
-{
- unsigned long addr_wr;
-
- /* Determine where the target instruction will send us to. */
- stepped_addr = get_step_address(regs);
- probe_kernel_read((char *)&stepped_instr, (char *)stepped_addr,
- BREAK_INSTR_SIZE);
-
- addr_wr = writable_address(stepped_addr);
- probe_kernel_write((char *)addr_wr, (char *)&singlestep_insn,
- BREAK_INSTR_SIZE);
- smp_wmb();
- flush_icache_range(stepped_addr, stepped_addr + BREAK_INSTR_SIZE);
-}
-
-static void undo_single_step(struct pt_regs *regs)
-{
- unsigned long addr_wr;
-
- if (stepped_instr == 0)
- return;
-
- addr_wr = writable_address(stepped_addr);
- probe_kernel_write((char *)addr_wr, (char *)&stepped_instr,
- BREAK_INSTR_SIZE);
- stepped_instr = 0;
- smp_wmb();
- flush_icache_range(stepped_addr, stepped_addr + BREAK_INSTR_SIZE);
-}
-
-/*
- * Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
- * then try to fall into the debugger.
- */
-static int
-kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
-{
- int ret;
- unsigned long flags;
- struct die_args *args = (struct die_args *)ptr;
- struct pt_regs *regs = args->regs;
-
-#ifdef CONFIG_KPROBES
- /*
- * Return immediately if the kprobes fault notifier has set
- * DIE_PAGE_FAULT.
- */
- if (cmd == DIE_PAGE_FAULT)
- return NOTIFY_DONE;
-#endif /* CONFIG_KPROBES */
-
- switch (cmd) {
- case DIE_BREAK:
- case DIE_COMPILED_BPT:
- break;
- case DIE_SSTEPBP:
- local_irq_save(flags);
- kgdb_handle_exception(0, SIGTRAP, 0, regs);
- local_irq_restore(flags);
- return NOTIFY_STOP;
- default:
- /* Userspace events, ignore. */
- if (user_mode(regs))
- return NOTIFY_DONE;
- }
-
- local_irq_save(flags);
- ret = kgdb_handle_exception(args->trapnr, args->signr, args->err, regs);
- local_irq_restore(flags);
- if (ret)
- return NOTIFY_DONE;
-
- return NOTIFY_STOP;
-}
-
-static struct notifier_block kgdb_notifier = {
- .notifier_call = kgdb_notify,
-};
-
-/*
- * kgdb_arch_handle_exception - Handle architecture specific GDB packets.
- * @vector: The error vector of the exception that happened.
- * @signo: The signal number of the exception that happened.
- * @err_code: The error code of the exception that happened.
- * @remcom_in_buffer: The buffer of the packet we have read.
- * @remcom_out_buffer: The buffer of %BUFMAX bytes to write a packet into.
- * @regs: The &struct pt_regs of the current process.
- *
- * This function MUST handle the 'c' and 's' command packets,
- * as well packets to set / remove a hardware breakpoint, if used.
- * If there are additional packets which the hardware needs to handle,
- * they are handled here. The code should return -1 if it wants to
- * process more packets, and a %0 or %1 if it wants to exit from the
- * kgdb callback.
- */
-int kgdb_arch_handle_exception(int vector, int signo, int err_code,
- char *remcom_in_buffer, char *remcom_out_buffer,
- struct pt_regs *regs)
-{
- char *ptr;
- unsigned long address;
-
- /* Undo any stepping we may have done. */
- undo_single_step(regs);
-
- switch (remcom_in_buffer[0]) {
- case 'c':
- case 's':
- case 'D':
- case 'k':
- /*
- * Try to read optional parameter, pc unchanged if no parm.
- * If this was a compiled-in breakpoint, we need to move
- * to the next instruction or we will just breakpoint
- * over and over again.
- */
- ptr = &remcom_in_buffer[1];
- if (kgdb_hex2long(&ptr, &address))
- regs->pc = address;
- else if (*(unsigned long *)regs->pc == compiled_bpt)
- regs->pc += BREAK_INSTR_SIZE;
-
- if (remcom_in_buffer[0] == 's') {
- do_single_step(regs);
- kgdb_single_step = 1;
- atomic_set(&kgdb_cpu_doing_single_step,
- raw_smp_processor_id());
- } else
- atomic_set(&kgdb_cpu_doing_single_step, -1);
-
- return 0;
- }
-
- return -1; /* this means that we do not want to exit from the handler */
-}
-
-struct kgdb_arch arch_kgdb_ops;
-
-/*
- * kgdb_arch_init - Perform any architecture specific initialization.
- *
- * This function will handle the initialization of any architecture
- * specific callbacks.
- */
-int kgdb_arch_init(void)
-{
- tile_bundle_bits bundle = TILEGX_BPT_BUNDLE;
-
- memcpy(arch_kgdb_ops.gdb_bpt_instr, &bundle, BREAK_INSTR_SIZE);
- return register_die_notifier(&kgdb_notifier);
-}
-
-/*
- * kgdb_arch_exit - Perform any architecture specific uninitialization.
- *
- * This function will handle the uninitialization of any architecture
- * specific callbacks, for dynamic registration and unregistration.
- */
-void kgdb_arch_exit(void)
-{
- unregister_die_notifier(&kgdb_notifier);
-}
-
-int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
-{
- int err;
- unsigned long addr_wr = writable_address(bpt->bpt_addr);
-
- if (addr_wr == 0)
- return -1;
-
- err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
- BREAK_INSTR_SIZE);
- if (err)
- return err;
-
- err = probe_kernel_write((char *)addr_wr, arch_kgdb_ops.gdb_bpt_instr,
- BREAK_INSTR_SIZE);
- smp_wmb();
- flush_icache_range((unsigned long)bpt->bpt_addr,
- (unsigned long)bpt->bpt_addr + BREAK_INSTR_SIZE);
- return err;
-}
-
-int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
-{
- int err;
- unsigned long addr_wr = writable_address(bpt->bpt_addr);
-
- if (addr_wr == 0)
- return -1;
-
- err = probe_kernel_write((char *)addr_wr, (char *)bpt->saved_instr,
- BREAK_INSTR_SIZE);
- smp_wmb();
- flush_icache_range((unsigned long)bpt->bpt_addr,
- (unsigned long)bpt->bpt_addr + BREAK_INSTR_SIZE);
- return err;
-}