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