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