1// SPDX-License-Identifier: GPL-2.0+
2#include <linux/kernel.h>
3#include <linux/uaccess.h>
4#include <linux/sched.h>
5#include <asm/hw_breakpoint.h>
6#include <asm/sstep.h>
7#include <asm/cache.h>
8
9static bool dar_in_user_range(unsigned long dar, struct arch_hw_breakpoint *info)
10{
11	return ((info->address <= dar) && (dar - info->address < info->len));
12}
13
14static bool ea_user_range_overlaps(unsigned long ea, int size,
15				   struct arch_hw_breakpoint *info)
16{
17	return ((ea < info->address + info->len) &&
18		(ea + size > info->address));
19}
20
21static bool dar_in_hw_range(unsigned long dar, struct arch_hw_breakpoint *info)
22{
23	unsigned long hw_start_addr, hw_end_addr;
24
25	hw_start_addr = ALIGN_DOWN(info->address, HW_BREAKPOINT_SIZE);
26	hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE);
27
28	return ((hw_start_addr <= dar) && (hw_end_addr > dar));
29}
30
31static bool ea_hw_range_overlaps(unsigned long ea, int size,
32				 struct arch_hw_breakpoint *info)
33{
34	unsigned long hw_start_addr, hw_end_addr;
35	unsigned long align_size = HW_BREAKPOINT_SIZE;
36
37	/*
38	 * On p10 predecessors, quadword is handle differently then
39	 * other instructions.
40	 */
41	if (!cpu_has_feature(CPU_FTR_ARCH_31) && size == 16)
42		align_size = HW_BREAKPOINT_SIZE_QUADWORD;
43
44	hw_start_addr = ALIGN_DOWN(info->address, align_size);
45	hw_end_addr = ALIGN(info->address + info->len, align_size);
46
47	return ((ea < hw_end_addr) && (ea + size > hw_start_addr));
48}
49
50/*
51 * If hw has multiple DAWR registers, we also need to check all
52 * dawrx constraint bits to confirm this is _really_ a valid event.
53 * If type is UNKNOWN, but privilege level matches, consider it as
54 * a positive match.
55 */
56static bool check_dawrx_constraints(struct pt_regs *regs, int type,
57				    struct arch_hw_breakpoint *info)
58{
59	if (OP_IS_LOAD(type) && !(info->type & HW_BRK_TYPE_READ))
60		return false;
61
62	/*
63	 * The Cache Management instructions other than dcbz never
64	 * cause a match. i.e. if type is CACHEOP, the instruction
65	 * is dcbz, and dcbz is treated as Store.
66	 */
67	if ((OP_IS_STORE(type) || type == CACHEOP) && !(info->type & HW_BRK_TYPE_WRITE))
68		return false;
69
70	if (is_kernel_addr(regs->nip) && !(info->type & HW_BRK_TYPE_KERNEL))
71		return false;
72
73	if (user_mode(regs) && !(info->type & HW_BRK_TYPE_USER))
74		return false;
75
76	return true;
77}
78
79/*
80 * Return true if the event is valid wrt dawr configuration,
81 * including extraneous exception. Otherwise return false.
82 */
83bool wp_check_constraints(struct pt_regs *regs, ppc_inst_t instr,
84			  unsigned long ea, int type, int size,
85			  struct arch_hw_breakpoint *info)
86{
87	bool in_user_range = dar_in_user_range(regs->dar, info);
88	bool dawrx_constraints;
89
90	/*
91	 * 8xx supports only one breakpoint and thus we can
92	 * unconditionally return true.
93	 */
94	if (IS_ENABLED(CONFIG_PPC_8xx)) {
95		if (!in_user_range)
96			info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
97		return true;
98	}
99
100	if (unlikely(ppc_inst_equal(instr, ppc_inst(0)))) {
101		if (cpu_has_feature(CPU_FTR_ARCH_31) &&
102		    !dar_in_hw_range(regs->dar, info))
103			return false;
104
105		return true;
106	}
107
108	dawrx_constraints = check_dawrx_constraints(regs, type, info);
109
110	if (type == UNKNOWN) {
111		if (cpu_has_feature(CPU_FTR_ARCH_31) &&
112		    !dar_in_hw_range(regs->dar, info))
113			return false;
114
115		return dawrx_constraints;
116	}
117
118	if (ea_user_range_overlaps(ea, size, info))
119		return dawrx_constraints;
120
121	if (ea_hw_range_overlaps(ea, size, info)) {
122		if (dawrx_constraints) {
123			info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
124			return true;
125		}
126	}
127	return false;
128}
129
130void wp_get_instr_detail(struct pt_regs *regs, ppc_inst_t *instr,
131			 int *type, int *size, unsigned long *ea)
132{
133	struct instruction_op op;
134	int err;
135
136	pagefault_disable();
137	err = __get_user_instr(*instr, (void __user *)regs->nip);
138	pagefault_enable();
139
140	if (err)
141		return;
142
143	analyse_instr(&op, regs, *instr);
144	*type = GETTYPE(op.type);
145	*ea = op.ea;
146
147	if (!(regs->msr & MSR_64BIT))
148		*ea &= 0xffffffffUL;
149
150
151	*size = GETSIZE(op.type);
152	if (*type == CACHEOP) {
153		*size = l1_dcache_bytes();
154		*ea &= ~(*size - 1);
155	} else if (*type == LOAD_VMX || *type == STORE_VMX) {
156		*ea &= ~(*size - 1);
157	}
158}
159