1fe89bd2bSVincent Chen // SPDX-License-Identifier: GPL-2.0-only 2fe89bd2bSVincent Chen /* 3fe89bd2bSVincent Chen * Copyright (C) 2020 SiFive 4fe89bd2bSVincent Chen */ 5fe89bd2bSVincent Chen 6fe89bd2bSVincent Chen #include <linux/ptrace.h> 7fe89bd2bSVincent Chen #include <linux/kdebug.h> 8fe89bd2bSVincent Chen #include <linux/bug.h> 9fe89bd2bSVincent Chen #include <linux/kgdb.h> 10fe89bd2bSVincent Chen #include <linux/irqflags.h> 11fe89bd2bSVincent Chen #include <linux/string.h> 12fe89bd2bSVincent Chen #include <asm/cacheflush.h> 13fe89bd2bSVincent Chen 14fe89bd2bSVincent Chen enum { 15fe89bd2bSVincent Chen NOT_KGDB_BREAK = 0, 16fe89bd2bSVincent Chen KGDB_SW_BREAK, 17fe89bd2bSVincent Chen KGDB_COMPILED_BREAK, 18fe89bd2bSVincent Chen }; 19fe89bd2bSVincent Chen 20fe89bd2bSVincent Chen struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { 21fe89bd2bSVincent Chen {DBG_REG_ZERO, GDB_SIZEOF_REG, -1}, 22fe89bd2bSVincent Chen {DBG_REG_RA, GDB_SIZEOF_REG, offsetof(struct pt_regs, ra)}, 23fe89bd2bSVincent Chen {DBG_REG_SP, GDB_SIZEOF_REG, offsetof(struct pt_regs, sp)}, 24fe89bd2bSVincent Chen {DBG_REG_GP, GDB_SIZEOF_REG, offsetof(struct pt_regs, gp)}, 25fe89bd2bSVincent Chen {DBG_REG_TP, GDB_SIZEOF_REG, offsetof(struct pt_regs, tp)}, 26fe89bd2bSVincent Chen {DBG_REG_T0, GDB_SIZEOF_REG, offsetof(struct pt_regs, t0)}, 27fe89bd2bSVincent Chen {DBG_REG_T1, GDB_SIZEOF_REG, offsetof(struct pt_regs, t1)}, 28fe89bd2bSVincent Chen {DBG_REG_T2, GDB_SIZEOF_REG, offsetof(struct pt_regs, t2)}, 29fe89bd2bSVincent Chen {DBG_REG_FP, GDB_SIZEOF_REG, offsetof(struct pt_regs, s0)}, 30fe89bd2bSVincent Chen {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)}, 31fe89bd2bSVincent Chen {DBG_REG_A0, GDB_SIZEOF_REG, offsetof(struct pt_regs, a0)}, 32fe89bd2bSVincent Chen {DBG_REG_A1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)}, 33fe89bd2bSVincent Chen {DBG_REG_A2, GDB_SIZEOF_REG, offsetof(struct pt_regs, a2)}, 34fe89bd2bSVincent Chen {DBG_REG_A3, GDB_SIZEOF_REG, offsetof(struct pt_regs, a3)}, 35fe89bd2bSVincent Chen {DBG_REG_A4, GDB_SIZEOF_REG, offsetof(struct pt_regs, a4)}, 36fe89bd2bSVincent Chen {DBG_REG_A5, GDB_SIZEOF_REG, offsetof(struct pt_regs, a5)}, 37fe89bd2bSVincent Chen {DBG_REG_A6, GDB_SIZEOF_REG, offsetof(struct pt_regs, a6)}, 38fe89bd2bSVincent Chen {DBG_REG_A7, GDB_SIZEOF_REG, offsetof(struct pt_regs, a7)}, 39fe89bd2bSVincent Chen {DBG_REG_S2, GDB_SIZEOF_REG, offsetof(struct pt_regs, s2)}, 40fe89bd2bSVincent Chen {DBG_REG_S3, GDB_SIZEOF_REG, offsetof(struct pt_regs, s3)}, 41fe89bd2bSVincent Chen {DBG_REG_S4, GDB_SIZEOF_REG, offsetof(struct pt_regs, s4)}, 42fe89bd2bSVincent Chen {DBG_REG_S5, GDB_SIZEOF_REG, offsetof(struct pt_regs, s5)}, 43fe89bd2bSVincent Chen {DBG_REG_S6, GDB_SIZEOF_REG, offsetof(struct pt_regs, s6)}, 44fe89bd2bSVincent Chen {DBG_REG_S7, GDB_SIZEOF_REG, offsetof(struct pt_regs, s7)}, 45fe89bd2bSVincent Chen {DBG_REG_S8, GDB_SIZEOF_REG, offsetof(struct pt_regs, s8)}, 46fe89bd2bSVincent Chen {DBG_REG_S9, GDB_SIZEOF_REG, offsetof(struct pt_regs, s9)}, 47fe89bd2bSVincent Chen {DBG_REG_S10, GDB_SIZEOF_REG, offsetof(struct pt_regs, s10)}, 48fe89bd2bSVincent Chen {DBG_REG_S11, GDB_SIZEOF_REG, offsetof(struct pt_regs, s11)}, 49fe89bd2bSVincent Chen {DBG_REG_T3, GDB_SIZEOF_REG, offsetof(struct pt_regs, t3)}, 50fe89bd2bSVincent Chen {DBG_REG_T4, GDB_SIZEOF_REG, offsetof(struct pt_regs, t4)}, 51fe89bd2bSVincent Chen {DBG_REG_T5, GDB_SIZEOF_REG, offsetof(struct pt_regs, t5)}, 52fe89bd2bSVincent Chen {DBG_REG_T6, GDB_SIZEOF_REG, offsetof(struct pt_regs, t6)}, 53fe89bd2bSVincent Chen {DBG_REG_EPC, GDB_SIZEOF_REG, offsetof(struct pt_regs, epc)}, 54fe89bd2bSVincent Chen }; 55fe89bd2bSVincent Chen 56fe89bd2bSVincent Chen char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) 57fe89bd2bSVincent Chen { 58fe89bd2bSVincent Chen if (regno >= DBG_MAX_REG_NUM || regno < 0) 59fe89bd2bSVincent Chen return NULL; 60fe89bd2bSVincent Chen 61fe89bd2bSVincent Chen if (dbg_reg_def[regno].offset != -1) 62fe89bd2bSVincent Chen memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, 63fe89bd2bSVincent Chen dbg_reg_def[regno].size); 64fe89bd2bSVincent Chen else 65fe89bd2bSVincent Chen memset(mem, 0, dbg_reg_def[regno].size); 66fe89bd2bSVincent Chen return dbg_reg_def[regno].name; 67fe89bd2bSVincent Chen } 68fe89bd2bSVincent Chen 69fe89bd2bSVincent Chen int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) 70fe89bd2bSVincent Chen { 71fe89bd2bSVincent Chen if (regno >= DBG_MAX_REG_NUM || regno < 0) 72fe89bd2bSVincent Chen return -EINVAL; 73fe89bd2bSVincent Chen 74fe89bd2bSVincent Chen if (dbg_reg_def[regno].offset != -1) 75fe89bd2bSVincent Chen memcpy((void *)regs + dbg_reg_def[regno].offset, mem, 76fe89bd2bSVincent Chen dbg_reg_def[regno].size); 77fe89bd2bSVincent Chen return 0; 78fe89bd2bSVincent Chen } 79fe89bd2bSVincent Chen 80fe89bd2bSVincent Chen void 81fe89bd2bSVincent Chen sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) 82fe89bd2bSVincent Chen { 83fe89bd2bSVincent Chen /* Initialize to zero */ 84fe89bd2bSVincent Chen memset((char *)gdb_regs, 0, NUMREGBYTES); 85fe89bd2bSVincent Chen 86fe89bd2bSVincent Chen gdb_regs[DBG_REG_SP_OFF] = task->thread.sp; 87fe89bd2bSVincent Chen gdb_regs[DBG_REG_FP_OFF] = task->thread.s[0]; 88fe89bd2bSVincent Chen gdb_regs[DBG_REG_S1_OFF] = task->thread.s[1]; 89fe89bd2bSVincent Chen gdb_regs[DBG_REG_S2_OFF] = task->thread.s[2]; 90fe89bd2bSVincent Chen gdb_regs[DBG_REG_S3_OFF] = task->thread.s[3]; 91fe89bd2bSVincent Chen gdb_regs[DBG_REG_S4_OFF] = task->thread.s[4]; 92fe89bd2bSVincent Chen gdb_regs[DBG_REG_S5_OFF] = task->thread.s[5]; 93fe89bd2bSVincent Chen gdb_regs[DBG_REG_S6_OFF] = task->thread.s[6]; 94fe89bd2bSVincent Chen gdb_regs[DBG_REG_S7_OFF] = task->thread.s[7]; 95fe89bd2bSVincent Chen gdb_regs[DBG_REG_S8_OFF] = task->thread.s[8]; 96fe89bd2bSVincent Chen gdb_regs[DBG_REG_S9_OFF] = task->thread.s[10]; 97fe89bd2bSVincent Chen gdb_regs[DBG_REG_S10_OFF] = task->thread.s[11]; 98fe89bd2bSVincent Chen gdb_regs[DBG_REG_EPC_OFF] = task->thread.ra; 99fe89bd2bSVincent Chen } 100fe89bd2bSVincent Chen 101fe89bd2bSVincent Chen void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) 102fe89bd2bSVincent Chen { 103fe89bd2bSVincent Chen regs->epc = pc; 104fe89bd2bSVincent Chen } 105fe89bd2bSVincent Chen 106fe89bd2bSVincent Chen static inline void kgdb_arch_update_addr(struct pt_regs *regs, 107fe89bd2bSVincent Chen char *remcom_in_buffer) 108fe89bd2bSVincent Chen { 109fe89bd2bSVincent Chen unsigned long addr; 110fe89bd2bSVincent Chen char *ptr; 111fe89bd2bSVincent Chen 112fe89bd2bSVincent Chen ptr = &remcom_in_buffer[1]; 113fe89bd2bSVincent Chen if (kgdb_hex2long(&ptr, &addr)) 114fe89bd2bSVincent Chen regs->epc = addr; 115fe89bd2bSVincent Chen } 116fe89bd2bSVincent Chen 117fe89bd2bSVincent Chen int kgdb_arch_handle_exception(int vector, int signo, int err_code, 118fe89bd2bSVincent Chen char *remcom_in_buffer, char *remcom_out_buffer, 119fe89bd2bSVincent Chen struct pt_regs *regs) 120fe89bd2bSVincent Chen { 121fe89bd2bSVincent Chen int err = 0; 122fe89bd2bSVincent Chen 123fe89bd2bSVincent Chen switch (remcom_in_buffer[0]) { 124fe89bd2bSVincent Chen case 'c': 125fe89bd2bSVincent Chen case 'D': 126fe89bd2bSVincent Chen case 'k': 127fe89bd2bSVincent Chen if (remcom_in_buffer[0] == 'c') 128fe89bd2bSVincent Chen kgdb_arch_update_addr(regs, remcom_in_buffer); 129fe89bd2bSVincent Chen break; 130fe89bd2bSVincent Chen default: 131fe89bd2bSVincent Chen err = -1; 132fe89bd2bSVincent Chen } 133fe89bd2bSVincent Chen 134fe89bd2bSVincent Chen return err; 135fe89bd2bSVincent Chen } 136fe89bd2bSVincent Chen 137fe89bd2bSVincent Chen int kgdb_riscv_kgdbbreak(unsigned long addr) 138fe89bd2bSVincent Chen { 139fe89bd2bSVincent Chen if (atomic_read(&kgdb_setting_breakpoint)) 140fe89bd2bSVincent Chen if (addr == (unsigned long)&kgdb_compiled_break) 141fe89bd2bSVincent Chen return KGDB_COMPILED_BREAK; 142fe89bd2bSVincent Chen 143fe89bd2bSVincent Chen return kgdb_has_hit_break(addr); 144fe89bd2bSVincent Chen } 145fe89bd2bSVincent Chen 146fe89bd2bSVincent Chen static int kgdb_riscv_notify(struct notifier_block *self, unsigned long cmd, 147fe89bd2bSVincent Chen void *ptr) 148fe89bd2bSVincent Chen { 149fe89bd2bSVincent Chen struct die_args *args = (struct die_args *)ptr; 150fe89bd2bSVincent Chen struct pt_regs *regs = args->regs; 151fe89bd2bSVincent Chen unsigned long flags; 152fe89bd2bSVincent Chen int type; 153fe89bd2bSVincent Chen 154fe89bd2bSVincent Chen if (user_mode(regs)) 155fe89bd2bSVincent Chen return NOTIFY_DONE; 156fe89bd2bSVincent Chen 157fe89bd2bSVincent Chen type = kgdb_riscv_kgdbbreak(regs->epc); 158fe89bd2bSVincent Chen if (type == NOT_KGDB_BREAK && cmd == DIE_TRAP) 159fe89bd2bSVincent Chen return NOTIFY_DONE; 160fe89bd2bSVincent Chen 161fe89bd2bSVincent Chen local_irq_save(flags); 162fe89bd2bSVincent Chen if (kgdb_handle_exception(1, args->signr, cmd, regs)) 163fe89bd2bSVincent Chen return NOTIFY_DONE; 164fe89bd2bSVincent Chen 165fe89bd2bSVincent Chen if (type == KGDB_COMPILED_BREAK) 166fe89bd2bSVincent Chen regs->epc += 4; 167fe89bd2bSVincent Chen 168fe89bd2bSVincent Chen local_irq_restore(flags); 169fe89bd2bSVincent Chen 170fe89bd2bSVincent Chen return NOTIFY_STOP; 171fe89bd2bSVincent Chen } 172fe89bd2bSVincent Chen 173fe89bd2bSVincent Chen static struct notifier_block kgdb_notifier = { 174fe89bd2bSVincent Chen .notifier_call = kgdb_riscv_notify, 175fe89bd2bSVincent Chen }; 176fe89bd2bSVincent Chen 177fe89bd2bSVincent Chen int kgdb_arch_init(void) 178fe89bd2bSVincent Chen { 179fe89bd2bSVincent Chen register_die_notifier(&kgdb_notifier); 180fe89bd2bSVincent Chen 181fe89bd2bSVincent Chen return 0; 182fe89bd2bSVincent Chen } 183fe89bd2bSVincent Chen 184fe89bd2bSVincent Chen void kgdb_arch_exit(void) 185fe89bd2bSVincent Chen { 186fe89bd2bSVincent Chen unregister_die_notifier(&kgdb_notifier); 187fe89bd2bSVincent Chen } 188fe89bd2bSVincent Chen 189fe89bd2bSVincent Chen /* 190fe89bd2bSVincent Chen * Global data 191fe89bd2bSVincent Chen */ 192fe89bd2bSVincent Chen #ifdef CONFIG_RISCV_ISA_C 193fe89bd2bSVincent Chen const struct kgdb_arch arch_kgdb_ops = { 194fe89bd2bSVincent Chen .gdb_bpt_instr = {0x02, 0x90}, /* c.ebreak */ 195fe89bd2bSVincent Chen }; 196fe89bd2bSVincent Chen #else 197fe89bd2bSVincent Chen const struct kgdb_arch arch_kgdb_ops = { 198fe89bd2bSVincent Chen .gdb_bpt_instr = {0x73, 0x00, 0x10, 0x00}, /* ebreak */ 199fe89bd2bSVincent Chen }; 200fe89bd2bSVincent Chen #endif 201