162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * LoongArch KGDB support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/hw_breakpoint.h>
962306a36Sopenharmony_ci#include <linux/kdebug.h>
1062306a36Sopenharmony_ci#include <linux/kgdb.h>
1162306a36Sopenharmony_ci#include <linux/processor.h>
1262306a36Sopenharmony_ci#include <linux/ptrace.h>
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/smp.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/cacheflush.h>
1762306a36Sopenharmony_ci#include <asm/fpu.h>
1862306a36Sopenharmony_ci#include <asm/hw_breakpoint.h>
1962306a36Sopenharmony_ci#include <asm/inst.h>
2062306a36Sopenharmony_ci#include <asm/irq_regs.h>
2162306a36Sopenharmony_ci#include <asm/ptrace.h>
2262306a36Sopenharmony_ci#include <asm/sigcontext.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciint kgdb_watch_activated;
2562306a36Sopenharmony_cistatic unsigned int stepped_opcode;
2662306a36Sopenharmony_cistatic unsigned long stepped_address;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
2962306a36Sopenharmony_ci	{ "r0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) },
3062306a36Sopenharmony_ci	{ "r1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) },
3162306a36Sopenharmony_ci	{ "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) },
3262306a36Sopenharmony_ci	{ "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) },
3362306a36Sopenharmony_ci	{ "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) },
3462306a36Sopenharmony_ci	{ "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) },
3562306a36Sopenharmony_ci	{ "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) },
3662306a36Sopenharmony_ci	{ "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) },
3762306a36Sopenharmony_ci	{ "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) },
3862306a36Sopenharmony_ci	{ "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) },
3962306a36Sopenharmony_ci	{ "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) },
4062306a36Sopenharmony_ci	{ "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) },
4162306a36Sopenharmony_ci	{ "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) },
4262306a36Sopenharmony_ci	{ "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) },
4362306a36Sopenharmony_ci	{ "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) },
4462306a36Sopenharmony_ci	{ "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) },
4562306a36Sopenharmony_ci	{ "r16", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[16]) },
4662306a36Sopenharmony_ci	{ "r17", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[17]) },
4762306a36Sopenharmony_ci	{ "r18", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[18]) },
4862306a36Sopenharmony_ci	{ "r19", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[19]) },
4962306a36Sopenharmony_ci	{ "r20", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[20]) },
5062306a36Sopenharmony_ci	{ "r21", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[21]) },
5162306a36Sopenharmony_ci	{ "r22", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[22]) },
5262306a36Sopenharmony_ci	{ "r23", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[23]) },
5362306a36Sopenharmony_ci	{ "r24", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[24]) },
5462306a36Sopenharmony_ci	{ "r25", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[25]) },
5562306a36Sopenharmony_ci	{ "r26", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[26]) },
5662306a36Sopenharmony_ci	{ "r27", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[27]) },
5762306a36Sopenharmony_ci	{ "r28", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[28]) },
5862306a36Sopenharmony_ci	{ "r29", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[29]) },
5962306a36Sopenharmony_ci	{ "r30", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[30]) },
6062306a36Sopenharmony_ci	{ "r31", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[31]) },
6162306a36Sopenharmony_ci	{ "orig_a0", GDB_SIZEOF_REG, offsetof(struct pt_regs, orig_a0) },
6262306a36Sopenharmony_ci	{ "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, csr_era) },
6362306a36Sopenharmony_ci	{ "badv", GDB_SIZEOF_REG, offsetof(struct pt_regs, csr_badvaddr) },
6462306a36Sopenharmony_ci	{ "f0", GDB_SIZEOF_REG, 0 },
6562306a36Sopenharmony_ci	{ "f1", GDB_SIZEOF_REG, 1 },
6662306a36Sopenharmony_ci	{ "f2", GDB_SIZEOF_REG, 2 },
6762306a36Sopenharmony_ci	{ "f3", GDB_SIZEOF_REG, 3 },
6862306a36Sopenharmony_ci	{ "f4", GDB_SIZEOF_REG, 4 },
6962306a36Sopenharmony_ci	{ "f5", GDB_SIZEOF_REG, 5 },
7062306a36Sopenharmony_ci	{ "f6", GDB_SIZEOF_REG, 6 },
7162306a36Sopenharmony_ci	{ "f7", GDB_SIZEOF_REG, 7 },
7262306a36Sopenharmony_ci	{ "f8", GDB_SIZEOF_REG, 8 },
7362306a36Sopenharmony_ci	{ "f9", GDB_SIZEOF_REG, 9 },
7462306a36Sopenharmony_ci	{ "f10", GDB_SIZEOF_REG, 10 },
7562306a36Sopenharmony_ci	{ "f11", GDB_SIZEOF_REG, 11 },
7662306a36Sopenharmony_ci	{ "f12", GDB_SIZEOF_REG, 12 },
7762306a36Sopenharmony_ci	{ "f13", GDB_SIZEOF_REG, 13 },
7862306a36Sopenharmony_ci	{ "f14", GDB_SIZEOF_REG, 14 },
7962306a36Sopenharmony_ci	{ "f15", GDB_SIZEOF_REG, 15 },
8062306a36Sopenharmony_ci	{ "f16", GDB_SIZEOF_REG, 16 },
8162306a36Sopenharmony_ci	{ "f17", GDB_SIZEOF_REG, 17 },
8262306a36Sopenharmony_ci	{ "f18", GDB_SIZEOF_REG, 18 },
8362306a36Sopenharmony_ci	{ "f19", GDB_SIZEOF_REG, 19 },
8462306a36Sopenharmony_ci	{ "f20", GDB_SIZEOF_REG, 20 },
8562306a36Sopenharmony_ci	{ "f21", GDB_SIZEOF_REG, 21 },
8662306a36Sopenharmony_ci	{ "f22", GDB_SIZEOF_REG, 22 },
8762306a36Sopenharmony_ci	{ "f23", GDB_SIZEOF_REG, 23 },
8862306a36Sopenharmony_ci	{ "f24", GDB_SIZEOF_REG, 24 },
8962306a36Sopenharmony_ci	{ "f25", GDB_SIZEOF_REG, 25 },
9062306a36Sopenharmony_ci	{ "f26", GDB_SIZEOF_REG, 26 },
9162306a36Sopenharmony_ci	{ "f27", GDB_SIZEOF_REG, 27 },
9262306a36Sopenharmony_ci	{ "f28", GDB_SIZEOF_REG, 28 },
9362306a36Sopenharmony_ci	{ "f29", GDB_SIZEOF_REG, 29 },
9462306a36Sopenharmony_ci	{ "f30", GDB_SIZEOF_REG, 30 },
9562306a36Sopenharmony_ci	{ "f31", GDB_SIZEOF_REG, 31 },
9662306a36Sopenharmony_ci	{ "fcc0", 1, 0 },
9762306a36Sopenharmony_ci	{ "fcc1", 1, 1 },
9862306a36Sopenharmony_ci	{ "fcc2", 1, 2 },
9962306a36Sopenharmony_ci	{ "fcc3", 1, 3 },
10062306a36Sopenharmony_ci	{ "fcc4", 1, 4 },
10162306a36Sopenharmony_ci	{ "fcc5", 1, 5 },
10262306a36Sopenharmony_ci	{ "fcc6", 1, 6 },
10362306a36Sopenharmony_ci	{ "fcc7", 1, 7 },
10462306a36Sopenharmony_ci	{ "fcsr", 4, 0 },
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cichar *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int reg_offset, reg_size;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (regno < 0 || regno >= DBG_MAX_REG_NUM)
11262306a36Sopenharmony_ci		return NULL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	reg_offset = dbg_reg_def[regno].offset;
11562306a36Sopenharmony_ci	reg_size = dbg_reg_def[regno].size;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (reg_offset == -1)
11862306a36Sopenharmony_ci		goto out;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Handle general-purpose/orig_a0/pc/badv registers */
12162306a36Sopenharmony_ci	if (regno <= DBG_PT_REGS_END) {
12262306a36Sopenharmony_ci		memcpy(mem, (void *)regs + reg_offset, reg_size);
12362306a36Sopenharmony_ci		goto out;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!(regs->csr_euen & CSR_EUEN_FPEN))
12762306a36Sopenharmony_ci		goto out;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	save_fp(current);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Handle FP registers */
13262306a36Sopenharmony_ci	switch (regno) {
13362306a36Sopenharmony_ci	case DBG_FCSR:				/* Process the fcsr */
13462306a36Sopenharmony_ci		memcpy(mem, (void *)&current->thread.fpu.fcsr, reg_size);
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	case DBG_FCC_BASE ... DBG_FCC_END:	/* Process the fcc */
13762306a36Sopenharmony_ci		memcpy(mem, (void *)&current->thread.fpu.fcc + reg_offset, reg_size);
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	case DBG_FPR_BASE ... DBG_FPR_END:	/* Process the fpr */
14062306a36Sopenharmony_ci		memcpy(mem, (void *)&current->thread.fpu.fpr[reg_offset], reg_size);
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci	default:
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciout:
14762306a36Sopenharmony_ci	return dbg_reg_def[regno].name;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ciint dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int reg_offset, reg_size;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (regno < 0 || regno >= DBG_MAX_REG_NUM)
15562306a36Sopenharmony_ci		return -EINVAL;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	reg_offset = dbg_reg_def[regno].offset;
15862306a36Sopenharmony_ci	reg_size = dbg_reg_def[regno].size;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (reg_offset == -1)
16162306a36Sopenharmony_ci		return 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Handle general-purpose/orig_a0/pc/badv registers */
16462306a36Sopenharmony_ci	if (regno <= DBG_PT_REGS_END) {
16562306a36Sopenharmony_ci		memcpy((void *)regs + reg_offset, mem, reg_size);
16662306a36Sopenharmony_ci		return 0;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (!(regs->csr_euen & CSR_EUEN_FPEN))
17062306a36Sopenharmony_ci		return 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Handle FP registers */
17362306a36Sopenharmony_ci	switch (regno) {
17462306a36Sopenharmony_ci	case DBG_FCSR:				/* Process the fcsr */
17562306a36Sopenharmony_ci		memcpy((void *)&current->thread.fpu.fcsr, mem, reg_size);
17662306a36Sopenharmony_ci		break;
17762306a36Sopenharmony_ci	case DBG_FCC_BASE ... DBG_FCC_END:	/* Process the fcc */
17862306a36Sopenharmony_ci		memcpy((void *)&current->thread.fpu.fcc + reg_offset, mem, reg_size);
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	case DBG_FPR_BASE ... DBG_FPR_END:	/* Process the fpr */
18162306a36Sopenharmony_ci		memcpy((void *)&current->thread.fpu.fpr[reg_offset], mem, reg_size);
18262306a36Sopenharmony_ci		break;
18362306a36Sopenharmony_ci	default:
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	restore_fp(current);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * Similar to regs_to_gdb_regs() except that process is sleeping and so
19462306a36Sopenharmony_ci * we may not be able to get all the info.
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_civoid sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	/* Initialize to zero */
19962306a36Sopenharmony_ci	memset((char *)gdb_regs, 0, NUMREGBYTES);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_RA] = p->thread.reg01;
20262306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_TP] = (long)p;
20362306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_SP] = p->thread.reg03;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* S0 - S8 */
20662306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S0] = p->thread.reg23;
20762306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S1] = p->thread.reg24;
20862306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S2] = p->thread.reg25;
20962306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S3] = p->thread.reg26;
21062306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S4] = p->thread.reg27;
21162306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S5] = p->thread.reg28;
21262306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S6] = p->thread.reg29;
21362306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S7] = p->thread.reg30;
21462306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_S8] = p->thread.reg31;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * PC use return address (RA), i.e. the moment after return from __switch_to()
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	gdb_regs[DBG_LOONGARCH_PC] = p->thread.reg01;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_civoid kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	regs->csr_era = pc;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_civoid arch_kgdb_breakpoint(void)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	__asm__ __volatile__ (			\
23062306a36Sopenharmony_ci		".globl kgdb_breakinst\n\t"	\
23162306a36Sopenharmony_ci		"nop\n"				\
23262306a36Sopenharmony_ci		"kgdb_breakinst:\tbreak 2\n\t"); /* BRK_KDB = 2 */
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/*
23662306a36Sopenharmony_ci * Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
23762306a36Sopenharmony_ci * then try to fall into the debugger
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_cistatic int kgdb_loongarch_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct die_args *args = (struct die_args *)ptr;
24262306a36Sopenharmony_ci	struct pt_regs *regs = args->regs;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Userspace events, ignore. */
24562306a36Sopenharmony_ci	if (user_mode(regs))
24662306a36Sopenharmony_ci		return NOTIFY_DONE;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!kgdb_io_module_registered)
24962306a36Sopenharmony_ci		return NOTIFY_DONE;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (atomic_read(&kgdb_active) != -1)
25262306a36Sopenharmony_ci		kgdb_nmicallback(smp_processor_id(), regs);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs))
25562306a36Sopenharmony_ci		return NOTIFY_DONE;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (atomic_read(&kgdb_setting_breakpoint))
25862306a36Sopenharmony_ci		if (regs->csr_era == (unsigned long)&kgdb_breakinst)
25962306a36Sopenharmony_ci			regs->csr_era += LOONGARCH_INSN_SIZE;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return NOTIFY_STOP;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cibool kgdb_breakpoint_handler(struct pt_regs *regs)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct die_args args = {
26762306a36Sopenharmony_ci		.regs	= regs,
26862306a36Sopenharmony_ci		.str	= "Break",
26962306a36Sopenharmony_ci		.err	= BRK_KDB,
27062306a36Sopenharmony_ci		.trapnr = read_csr_excode(),
27162306a36Sopenharmony_ci		.signr	= SIGTRAP,
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return (kgdb_loongarch_notify(NULL, DIE_TRAP, &args) == NOTIFY_STOP) ? true : false;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct notifier_block kgdb_notifier = {
27962306a36Sopenharmony_ci	.notifier_call = kgdb_loongarch_notify,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic inline void kgdb_arch_update_addr(struct pt_regs *regs,
28362306a36Sopenharmony_ci					 char *remcom_in_buffer)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	unsigned long addr;
28662306a36Sopenharmony_ci	char *ptr;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ptr = &remcom_in_buffer[1];
28962306a36Sopenharmony_ci	if (kgdb_hex2long(&ptr, &addr))
29062306a36Sopenharmony_ci		regs->csr_era = addr;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/* Calculate the new address for after a step */
29462306a36Sopenharmony_cistatic int get_step_address(struct pt_regs *regs, unsigned long *next_addr)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	char cj_val;
29762306a36Sopenharmony_ci	unsigned int si, si_l, si_h, rd, rj, cj;
29862306a36Sopenharmony_ci	unsigned long pc = instruction_pointer(regs);
29962306a36Sopenharmony_ci	union loongarch_instruction *ip = (union loongarch_instruction *)pc;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (pc & 3) {
30262306a36Sopenharmony_ci		pr_warn("%s: invalid pc 0x%lx\n", __func__, pc);
30362306a36Sopenharmony_ci		return -EINVAL;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	*next_addr = pc + LOONGARCH_INSN_SIZE;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	si_h = ip->reg0i26_format.immediate_h;
30962306a36Sopenharmony_ci	si_l = ip->reg0i26_format.immediate_l;
31062306a36Sopenharmony_ci	switch (ip->reg0i26_format.opcode) {
31162306a36Sopenharmony_ci	case b_op:
31262306a36Sopenharmony_ci		*next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 27);
31362306a36Sopenharmony_ci		return 0;
31462306a36Sopenharmony_ci	case bl_op:
31562306a36Sopenharmony_ci		*next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 27);
31662306a36Sopenharmony_ci		regs->regs[1] = pc + LOONGARCH_INSN_SIZE;
31762306a36Sopenharmony_ci		return 0;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	rj = ip->reg1i21_format.rj;
32162306a36Sopenharmony_ci	cj = (rj & 0x07) + DBG_FCC_BASE;
32262306a36Sopenharmony_ci	si_l = ip->reg1i21_format.immediate_l;
32362306a36Sopenharmony_ci	si_h = ip->reg1i21_format.immediate_h;
32462306a36Sopenharmony_ci	dbg_get_reg(cj, &cj_val, regs);
32562306a36Sopenharmony_ci	switch (ip->reg1i21_format.opcode) {
32662306a36Sopenharmony_ci	case beqz_op:
32762306a36Sopenharmony_ci		if (regs->regs[rj] == 0)
32862306a36Sopenharmony_ci			*next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
32962306a36Sopenharmony_ci		return 0;
33062306a36Sopenharmony_ci	case bnez_op:
33162306a36Sopenharmony_ci		if (regs->regs[rj] != 0)
33262306a36Sopenharmony_ci			*next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
33362306a36Sopenharmony_ci		return 0;
33462306a36Sopenharmony_ci	case bceqz_op: /* bceqz_op = bcnez_op */
33562306a36Sopenharmony_ci		if (((rj & 0x18) == 0x00) && !cj_val) /* bceqz */
33662306a36Sopenharmony_ci			*next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
33762306a36Sopenharmony_ci		if (((rj & 0x18) == 0x08) && cj_val) /* bcnez */
33862306a36Sopenharmony_ci			*next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
33962306a36Sopenharmony_ci		return 0;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	rj = ip->reg2i16_format.rj;
34362306a36Sopenharmony_ci	rd = ip->reg2i16_format.rd;
34462306a36Sopenharmony_ci	si = ip->reg2i16_format.immediate;
34562306a36Sopenharmony_ci	switch (ip->reg2i16_format.opcode) {
34662306a36Sopenharmony_ci	case beq_op:
34762306a36Sopenharmony_ci		if (regs->regs[rj] == regs->regs[rd])
34862306a36Sopenharmony_ci			*next_addr = pc + sign_extend64(si << 2, 17);
34962306a36Sopenharmony_ci		return 0;
35062306a36Sopenharmony_ci	case bne_op:
35162306a36Sopenharmony_ci		if (regs->regs[rj] != regs->regs[rd])
35262306a36Sopenharmony_ci			*next_addr = pc + sign_extend64(si << 2, 17);
35362306a36Sopenharmony_ci		return 0;
35462306a36Sopenharmony_ci	case blt_op:
35562306a36Sopenharmony_ci		if ((long)regs->regs[rj] < (long)regs->regs[rd])
35662306a36Sopenharmony_ci			*next_addr = pc + sign_extend64(si << 2, 17);
35762306a36Sopenharmony_ci		return 0;
35862306a36Sopenharmony_ci	case bge_op:
35962306a36Sopenharmony_ci		if ((long)regs->regs[rj] >= (long)regs->regs[rd])
36062306a36Sopenharmony_ci			*next_addr = pc + sign_extend64(si << 2, 17);
36162306a36Sopenharmony_ci		return 0;
36262306a36Sopenharmony_ci	case bltu_op:
36362306a36Sopenharmony_ci		if (regs->regs[rj] < regs->regs[rd])
36462306a36Sopenharmony_ci			*next_addr = pc + sign_extend64(si << 2, 17);
36562306a36Sopenharmony_ci		return 0;
36662306a36Sopenharmony_ci	case bgeu_op:
36762306a36Sopenharmony_ci		if (regs->regs[rj] >= regs->regs[rd])
36862306a36Sopenharmony_ci			*next_addr = pc + sign_extend64(si << 2, 17);
36962306a36Sopenharmony_ci		return 0;
37062306a36Sopenharmony_ci	case jirl_op:
37162306a36Sopenharmony_ci		regs->regs[rd] = pc + LOONGARCH_INSN_SIZE;
37262306a36Sopenharmony_ci		*next_addr = regs->regs[rj] + sign_extend64(si << 2, 17);
37362306a36Sopenharmony_ci		return 0;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int do_single_step(struct pt_regs *regs)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int error = 0;
38262306a36Sopenharmony_ci	unsigned long addr = 0; /* Determine where the target instruction will send us to */
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	error = get_step_address(regs, &addr);
38562306a36Sopenharmony_ci	if (error)
38662306a36Sopenharmony_ci		return error;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Store the opcode in the stepped address */
38962306a36Sopenharmony_ci	error = get_kernel_nofault(stepped_opcode, (void *)addr);
39062306a36Sopenharmony_ci	if (error)
39162306a36Sopenharmony_ci		return error;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	stepped_address = addr;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* Replace the opcode with the break instruction */
39662306a36Sopenharmony_ci	error = copy_to_kernel_nofault((void *)stepped_address,
39762306a36Sopenharmony_ci				       arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
39862306a36Sopenharmony_ci	flush_icache_range(addr, addr + BREAK_INSTR_SIZE);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (error) {
40162306a36Sopenharmony_ci		stepped_opcode = 0;
40262306a36Sopenharmony_ci		stepped_address = 0;
40362306a36Sopenharmony_ci	} else {
40462306a36Sopenharmony_ci		kgdb_single_step = 1;
40562306a36Sopenharmony_ci		atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id());
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return error;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/* Undo a single step */
41262306a36Sopenharmony_cistatic void undo_single_step(struct pt_regs *regs)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	if (stepped_opcode) {
41562306a36Sopenharmony_ci		copy_to_kernel_nofault((void *)stepped_address,
41662306a36Sopenharmony_ci				       (void *)&stepped_opcode, BREAK_INSTR_SIZE);
41762306a36Sopenharmony_ci		flush_icache_range(stepped_address, stepped_address + BREAK_INSTR_SIZE);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	stepped_opcode = 0;
42162306a36Sopenharmony_ci	stepped_address = 0;
42262306a36Sopenharmony_ci	kgdb_single_step = 0;
42362306a36Sopenharmony_ci	atomic_set(&kgdb_cpu_doing_single_step, -1);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciint kgdb_arch_handle_exception(int vector, int signo, int err_code,
42762306a36Sopenharmony_ci			       char *remcom_in_buffer, char *remcom_out_buffer,
42862306a36Sopenharmony_ci			       struct pt_regs *regs)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	int ret = 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	undo_single_step(regs);
43362306a36Sopenharmony_ci	regs->csr_prmd |= CSR_PRMD_PWE;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	switch (remcom_in_buffer[0]) {
43662306a36Sopenharmony_ci	case 'D':
43762306a36Sopenharmony_ci	case 'k':
43862306a36Sopenharmony_ci		regs->csr_prmd &= ~CSR_PRMD_PWE;
43962306a36Sopenharmony_ci		fallthrough;
44062306a36Sopenharmony_ci	case 'c':
44162306a36Sopenharmony_ci		kgdb_arch_update_addr(regs, remcom_in_buffer);
44262306a36Sopenharmony_ci		break;
44362306a36Sopenharmony_ci	case 's':
44462306a36Sopenharmony_ci		kgdb_arch_update_addr(regs, remcom_in_buffer);
44562306a36Sopenharmony_ci		ret = do_single_step(regs);
44662306a36Sopenharmony_ci		break;
44762306a36Sopenharmony_ci	default:
44862306a36Sopenharmony_ci		ret = -1;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return ret;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic struct hw_breakpoint {
45562306a36Sopenharmony_ci	unsigned int		enabled;
45662306a36Sopenharmony_ci	unsigned long		addr;
45762306a36Sopenharmony_ci	int			len;
45862306a36Sopenharmony_ci	int			type;
45962306a36Sopenharmony_ci	struct perf_event	* __percpu *pev;
46062306a36Sopenharmony_ci} breakinfo[LOONGARCH_MAX_BRP];
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int hw_break_reserve_slot(int breakno)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int cpu, cnt = 0;
46562306a36Sopenharmony_ci	struct perf_event **pevent;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	for_each_online_cpu(cpu) {
46862306a36Sopenharmony_ci		cnt++;
46962306a36Sopenharmony_ci		pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
47062306a36Sopenharmony_ci		if (dbg_reserve_bp_slot(*pevent))
47162306a36Sopenharmony_ci			goto fail;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return 0;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cifail:
47762306a36Sopenharmony_ci	for_each_online_cpu(cpu) {
47862306a36Sopenharmony_ci		cnt--;
47962306a36Sopenharmony_ci		if (!cnt)
48062306a36Sopenharmony_ci			break;
48162306a36Sopenharmony_ci		pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
48262306a36Sopenharmony_ci		dbg_release_bp_slot(*pevent);
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return -1;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int hw_break_release_slot(int breakno)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	int cpu;
49162306a36Sopenharmony_ci	struct perf_event **pevent;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (dbg_is_early)
49462306a36Sopenharmony_ci		return 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	for_each_online_cpu(cpu) {
49762306a36Sopenharmony_ci		pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
49862306a36Sopenharmony_ci		if (dbg_release_bp_slot(*pevent))
49962306a36Sopenharmony_ci			/*
50062306a36Sopenharmony_ci			 * The debugger is responsible for handing the retry on
50162306a36Sopenharmony_ci			 * remove failure.
50262306a36Sopenharmony_ci			 */
50362306a36Sopenharmony_ci			return -1;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	int i;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++)
51462306a36Sopenharmony_ci		if (!breakinfo[i].enabled)
51562306a36Sopenharmony_ci			break;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (i == LOONGARCH_MAX_BRP)
51862306a36Sopenharmony_ci		return -1;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	switch (bptype) {
52162306a36Sopenharmony_ci	case BP_HARDWARE_BREAKPOINT:
52262306a36Sopenharmony_ci		breakinfo[i].type = HW_BREAKPOINT_X;
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci	case BP_READ_WATCHPOINT:
52562306a36Sopenharmony_ci		breakinfo[i].type = HW_BREAKPOINT_R;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	case BP_WRITE_WATCHPOINT:
52862306a36Sopenharmony_ci		breakinfo[i].type = HW_BREAKPOINT_W;
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	case BP_ACCESS_WATCHPOINT:
53162306a36Sopenharmony_ci		breakinfo[i].type = HW_BREAKPOINT_RW;
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	default:
53462306a36Sopenharmony_ci		return -1;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	switch (len) {
53862306a36Sopenharmony_ci	case 1:
53962306a36Sopenharmony_ci		breakinfo[i].len = HW_BREAKPOINT_LEN_1;
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci	case 2:
54262306a36Sopenharmony_ci		breakinfo[i].len = HW_BREAKPOINT_LEN_2;
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci	case 4:
54562306a36Sopenharmony_ci		breakinfo[i].len = HW_BREAKPOINT_LEN_4;
54662306a36Sopenharmony_ci		break;
54762306a36Sopenharmony_ci	case 8:
54862306a36Sopenharmony_ci		breakinfo[i].len = HW_BREAKPOINT_LEN_8;
54962306a36Sopenharmony_ci		break;
55062306a36Sopenharmony_ci	default:
55162306a36Sopenharmony_ci		return -1;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	breakinfo[i].addr = addr;
55562306a36Sopenharmony_ci	if (hw_break_reserve_slot(i)) {
55662306a36Sopenharmony_ci		breakinfo[i].addr = 0;
55762306a36Sopenharmony_ci		return -1;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci	breakinfo[i].enabled = 1;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return 0;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	int i;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++)
56962306a36Sopenharmony_ci		if (breakinfo[i].addr == addr && breakinfo[i].enabled)
57062306a36Sopenharmony_ci			break;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (i == LOONGARCH_MAX_BRP)
57362306a36Sopenharmony_ci		return -1;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (hw_break_release_slot(i)) {
57662306a36Sopenharmony_ci		pr_err("Cannot remove hw breakpoint at %lx\n", addr);
57762306a36Sopenharmony_ci		return -1;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci	breakinfo[i].enabled = 0;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return 0;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void kgdb_disable_hw_break(struct pt_regs *regs)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	int i;
58762306a36Sopenharmony_ci	int cpu = raw_smp_processor_id();
58862306a36Sopenharmony_ci	struct perf_event *bp;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++) {
59162306a36Sopenharmony_ci		if (!breakinfo[i].enabled)
59262306a36Sopenharmony_ci			continue;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
59562306a36Sopenharmony_ci		if (bp->attr.disabled == 1)
59662306a36Sopenharmony_ci			continue;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		arch_uninstall_hw_breakpoint(bp);
59962306a36Sopenharmony_ci		bp->attr.disabled = 1;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Disable hardware debugging while we are in kgdb */
60362306a36Sopenharmony_ci	csr_xchg32(0, CSR_CRMD_WE, LOONGARCH_CSR_CRMD);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic void kgdb_remove_all_hw_break(void)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	int i;
60962306a36Sopenharmony_ci	int cpu = raw_smp_processor_id();
61062306a36Sopenharmony_ci	struct perf_event *bp;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++) {
61362306a36Sopenharmony_ci		if (!breakinfo[i].enabled)
61462306a36Sopenharmony_ci			continue;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
61762306a36Sopenharmony_ci		if (!bp->attr.disabled) {
61862306a36Sopenharmony_ci			arch_uninstall_hw_breakpoint(bp);
61962306a36Sopenharmony_ci			bp->attr.disabled = 1;
62062306a36Sopenharmony_ci			continue;
62162306a36Sopenharmony_ci		}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci		if (hw_break_release_slot(i))
62462306a36Sopenharmony_ci			pr_err("KGDB: hw bpt remove failed %lx\n", breakinfo[i].addr);
62562306a36Sopenharmony_ci		breakinfo[i].enabled = 0;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	csr_xchg32(0, CSR_CRMD_WE, LOONGARCH_CSR_CRMD);
62962306a36Sopenharmony_ci	kgdb_watch_activated = 0;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic void kgdb_correct_hw_break(void)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	int i, activated = 0;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++) {
63762306a36Sopenharmony_ci		struct perf_event *bp;
63862306a36Sopenharmony_ci		int val;
63962306a36Sopenharmony_ci		int cpu = raw_smp_processor_id();
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		if (!breakinfo[i].enabled)
64262306a36Sopenharmony_ci			continue;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
64562306a36Sopenharmony_ci		if (bp->attr.disabled != 1)
64662306a36Sopenharmony_ci			continue;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		bp->attr.bp_addr = breakinfo[i].addr;
64962306a36Sopenharmony_ci		bp->attr.bp_len = breakinfo[i].len;
65062306a36Sopenharmony_ci		bp->attr.bp_type = breakinfo[i].type;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		val = hw_breakpoint_arch_parse(bp, &bp->attr, counter_arch_bp(bp));
65362306a36Sopenharmony_ci		if (val)
65462306a36Sopenharmony_ci			return;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		val = arch_install_hw_breakpoint(bp);
65762306a36Sopenharmony_ci		if (!val)
65862306a36Sopenharmony_ci			bp->attr.disabled = 0;
65962306a36Sopenharmony_ci		activated = 1;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	csr_xchg32(activated ? CSR_CRMD_WE : 0, CSR_CRMD_WE, LOONGARCH_CSR_CRMD);
66362306a36Sopenharmony_ci	kgdb_watch_activated = activated;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ciconst struct kgdb_arch arch_kgdb_ops = {
66762306a36Sopenharmony_ci	.gdb_bpt_instr		= {0x02, 0x00, break_op >> 1, 0x00}, /* BRK_KDB = 2 */
66862306a36Sopenharmony_ci	.flags			= KGDB_HW_BREAKPOINT,
66962306a36Sopenharmony_ci	.set_hw_breakpoint	= kgdb_set_hw_break,
67062306a36Sopenharmony_ci	.remove_hw_breakpoint	= kgdb_remove_hw_break,
67162306a36Sopenharmony_ci	.disable_hw_break	= kgdb_disable_hw_break,
67262306a36Sopenharmony_ci	.remove_all_hw_break	= kgdb_remove_all_hw_break,
67362306a36Sopenharmony_ci	.correct_hw_break	= kgdb_correct_hw_break,
67462306a36Sopenharmony_ci};
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ciint kgdb_arch_init(void)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	return register_die_notifier(&kgdb_notifier);
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_civoid kgdb_arch_late(void)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	int i, cpu;
68462306a36Sopenharmony_ci	struct perf_event_attr attr;
68562306a36Sopenharmony_ci	struct perf_event **pevent;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	hw_breakpoint_init(&attr);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	attr.bp_addr = (unsigned long)kgdb_arch_init;
69062306a36Sopenharmony_ci	attr.bp_len = HW_BREAKPOINT_LEN_4;
69162306a36Sopenharmony_ci	attr.bp_type = HW_BREAKPOINT_W;
69262306a36Sopenharmony_ci	attr.disabled = 1;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++) {
69562306a36Sopenharmony_ci		if (breakinfo[i].pev)
69662306a36Sopenharmony_ci			continue;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL, NULL);
69962306a36Sopenharmony_ci		if (IS_ERR((void * __force)breakinfo[i].pev)) {
70062306a36Sopenharmony_ci			pr_err("kgdb: Could not allocate hw breakpoints.\n");
70162306a36Sopenharmony_ci			breakinfo[i].pev = NULL;
70262306a36Sopenharmony_ci			return;
70362306a36Sopenharmony_ci		}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		for_each_online_cpu(cpu) {
70662306a36Sopenharmony_ci			pevent = per_cpu_ptr(breakinfo[i].pev, cpu);
70762306a36Sopenharmony_ci			if (pevent[0]->destroy) {
70862306a36Sopenharmony_ci				pevent[0]->destroy = NULL;
70962306a36Sopenharmony_ci				release_bp_slot(*pevent);
71062306a36Sopenharmony_ci			}
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_civoid kgdb_arch_exit(void)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	int i;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	for (i = 0; i < LOONGARCH_MAX_BRP; i++) {
72062306a36Sopenharmony_ci		if (breakinfo[i].pev) {
72162306a36Sopenharmony_ci			unregister_wide_hw_breakpoint(breakinfo[i].pev);
72262306a36Sopenharmony_ci			breakinfo[i].pev = NULL;
72362306a36Sopenharmony_ci		}
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	unregister_die_notifier(&kgdb_notifier);
72762306a36Sopenharmony_ci}
728