18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PA-RISC KGDB support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kgdb.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <linux/notifier.h> 138c2ecf20Sopenharmony_ci#include <linux/kdebug.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 168c2ecf20Sopenharmony_ci#include <asm/traps.h> 178c2ecf20Sopenharmony_ci#include <asm/processor.h> 188c2ecf20Sopenharmony_ci#include <asm/patch.h> 198c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciconst struct kgdb_arch arch_kgdb_ops = { 228c2ecf20Sopenharmony_ci .gdb_bpt_instr = { 0x03, 0xff, 0xa0, 0x1f } 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int __kgdb_notify(struct die_args *args, unsigned long cmd) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct pt_regs *regs = args->regs; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (kgdb_handle_exception(1, args->signr, cmd, regs)) 308c2ecf20Sopenharmony_ci return NOTIFY_DONE; 318c2ecf20Sopenharmony_ci return NOTIFY_STOP; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int kgdb_notify(struct notifier_block *self, 358c2ecf20Sopenharmony_ci unsigned long cmd, void *ptr) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci unsigned long flags; 388c2ecf20Sopenharmony_ci int ret; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci local_irq_save(flags); 418c2ecf20Sopenharmony_ci ret = __kgdb_notify(ptr, cmd); 428c2ecf20Sopenharmony_ci local_irq_restore(flags); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return ret; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct notifier_block kgdb_notifier = { 488c2ecf20Sopenharmony_ci .notifier_call = kgdb_notify, 498c2ecf20Sopenharmony_ci .priority = -INT_MAX, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ciint kgdb_arch_init(void) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return register_die_notifier(&kgdb_notifier); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_civoid kgdb_arch_exit(void) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci unregister_die_notifier(&kgdb_notifier); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct parisc_gdb_regs *gr = (struct parisc_gdb_regs *)gdb_regs; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci memset(gr, 0, sizeof(struct parisc_gdb_regs)); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci memcpy(gr->gpr, regs->gr, sizeof(gr->gpr)); 698c2ecf20Sopenharmony_ci memcpy(gr->fr, regs->fr, sizeof(gr->fr)); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci gr->sr0 = regs->sr[0]; 728c2ecf20Sopenharmony_ci gr->sr1 = regs->sr[1]; 738c2ecf20Sopenharmony_ci gr->sr2 = regs->sr[2]; 748c2ecf20Sopenharmony_ci gr->sr3 = regs->sr[3]; 758c2ecf20Sopenharmony_ci gr->sr4 = regs->sr[4]; 768c2ecf20Sopenharmony_ci gr->sr5 = regs->sr[5]; 778c2ecf20Sopenharmony_ci gr->sr6 = regs->sr[6]; 788c2ecf20Sopenharmony_ci gr->sr7 = regs->sr[7]; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci gr->sar = regs->sar; 818c2ecf20Sopenharmony_ci gr->iir = regs->iir; 828c2ecf20Sopenharmony_ci gr->isr = regs->isr; 838c2ecf20Sopenharmony_ci gr->ior = regs->ior; 848c2ecf20Sopenharmony_ci gr->ipsw = regs->ipsw; 858c2ecf20Sopenharmony_ci gr->cr27 = regs->cr27; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci gr->iaoq_f = regs->iaoq[0]; 888c2ecf20Sopenharmony_ci gr->iasq_f = regs->iasq[0]; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci gr->iaoq_b = regs->iaoq[1]; 918c2ecf20Sopenharmony_ci gr->iasq_b = regs->iasq[1]; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_civoid gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct parisc_gdb_regs *gr = (struct parisc_gdb_regs *)gdb_regs; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci memcpy(regs->gr, gr->gpr, sizeof(regs->gr)); 1008c2ecf20Sopenharmony_ci memcpy(regs->fr, gr->fr, sizeof(regs->fr)); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci regs->sr[0] = gr->sr0; 1038c2ecf20Sopenharmony_ci regs->sr[1] = gr->sr1; 1048c2ecf20Sopenharmony_ci regs->sr[2] = gr->sr2; 1058c2ecf20Sopenharmony_ci regs->sr[3] = gr->sr3; 1068c2ecf20Sopenharmony_ci regs->sr[4] = gr->sr4; 1078c2ecf20Sopenharmony_ci regs->sr[5] = gr->sr5; 1088c2ecf20Sopenharmony_ci regs->sr[6] = gr->sr6; 1098c2ecf20Sopenharmony_ci regs->sr[7] = gr->sr7; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci regs->sar = gr->sar; 1128c2ecf20Sopenharmony_ci regs->iir = gr->iir; 1138c2ecf20Sopenharmony_ci regs->isr = gr->isr; 1148c2ecf20Sopenharmony_ci regs->ior = gr->ior; 1158c2ecf20Sopenharmony_ci regs->ipsw = gr->ipsw; 1168c2ecf20Sopenharmony_ci regs->cr27 = gr->cr27; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci regs->iaoq[0] = gr->iaoq_f; 1198c2ecf20Sopenharmony_ci regs->iasq[0] = gr->iasq_f; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci regs->iaoq[1] = gr->iaoq_b; 1228c2ecf20Sopenharmony_ci regs->iasq[1] = gr->iasq_b; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_civoid sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, 1268c2ecf20Sopenharmony_ci struct task_struct *task) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct pt_regs *regs = task_pt_regs(task); 1298c2ecf20Sopenharmony_ci unsigned long gr30, iaoq; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci gr30 = regs->gr[30]; 1328c2ecf20Sopenharmony_ci iaoq = regs->iaoq[0]; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci regs->gr[30] = regs->ksp; 1358c2ecf20Sopenharmony_ci regs->iaoq[0] = regs->kpc; 1368c2ecf20Sopenharmony_ci pt_regs_to_gdb_regs(gdb_regs, regs); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci regs->gr[30] = gr30; 1398c2ecf20Sopenharmony_ci regs->iaoq[0] = iaoq; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void step_instruction_queue(struct pt_regs *regs) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci regs->iaoq[0] = regs->iaoq[1]; 1468c2ecf20Sopenharmony_ci regs->iaoq[1] += 4; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_civoid kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci regs->iaoq[0] = ip; 1528c2ecf20Sopenharmony_ci regs->iaoq[1] = ip + 4; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int ret = copy_from_kernel_nofault(bpt->saved_instr, 1588c2ecf20Sopenharmony_ci (char *)bpt->bpt_addr, BREAK_INSTR_SIZE); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci __patch_text((void *)bpt->bpt_addr, 1638c2ecf20Sopenharmony_ci *(unsigned int *)&arch_kgdb_ops.gdb_bpt_instr); 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciint kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci __patch_text((void *)bpt->bpt_addr, *(unsigned int *)&bpt->saved_instr); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciint kgdb_arch_handle_exception(int trap, int signo, 1748c2ecf20Sopenharmony_ci int err_code, char *inbuf, char *outbuf, 1758c2ecf20Sopenharmony_ci struct pt_regs *regs) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci unsigned long addr; 1788c2ecf20Sopenharmony_ci char *p = inbuf + 1; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci switch (inbuf[0]) { 1818c2ecf20Sopenharmony_ci case 'D': 1828c2ecf20Sopenharmony_ci case 'c': 1838c2ecf20Sopenharmony_ci case 'k': 1848c2ecf20Sopenharmony_ci kgdb_contthread = NULL; 1858c2ecf20Sopenharmony_ci kgdb_single_step = 0; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (kgdb_hex2long(&p, &addr)) 1888c2ecf20Sopenharmony_ci kgdb_arch_set_pc(regs, addr); 1898c2ecf20Sopenharmony_ci else if (trap == 9 && regs->iir == 1908c2ecf20Sopenharmony_ci PARISC_KGDB_COMPILED_BREAK_INSN) 1918c2ecf20Sopenharmony_ci step_instruction_queue(regs); 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci case 's': 1948c2ecf20Sopenharmony_ci kgdb_single_step = 1; 1958c2ecf20Sopenharmony_ci if (kgdb_hex2long(&p, &addr)) { 1968c2ecf20Sopenharmony_ci kgdb_arch_set_pc(regs, addr); 1978c2ecf20Sopenharmony_ci } else if (trap == 9 && regs->iir == 1988c2ecf20Sopenharmony_ci PARISC_KGDB_COMPILED_BREAK_INSN) { 1998c2ecf20Sopenharmony_ci step_instruction_queue(regs); 2008c2ecf20Sopenharmony_ci mtctl(-1, 0); 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci mtctl(0, 0); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci regs->gr[0] |= PSW_R; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci return -1; 2098c2ecf20Sopenharmony_ci} 210