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