0001
0002 #include <linux/kernel.h>
0003 #include <linux/uaccess.h>
0004 #include <linux/sched.h>
0005 #include <asm/hw_breakpoint.h>
0006 #include <asm/sstep.h>
0007 #include <asm/cache.h>
0008
0009 static bool dar_in_user_range(unsigned long dar, struct arch_hw_breakpoint *info)
0010 {
0011 return ((info->address <= dar) && (dar - info->address < info->len));
0012 }
0013
0014 static bool ea_user_range_overlaps(unsigned long ea, int size,
0015 struct arch_hw_breakpoint *info)
0016 {
0017 return ((ea < info->address + info->len) &&
0018 (ea + size > info->address));
0019 }
0020
0021 static bool dar_in_hw_range(unsigned long dar, struct arch_hw_breakpoint *info)
0022 {
0023 unsigned long hw_start_addr, hw_end_addr;
0024
0025 hw_start_addr = ALIGN_DOWN(info->address, HW_BREAKPOINT_SIZE);
0026 hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE);
0027
0028 return ((hw_start_addr <= dar) && (hw_end_addr > dar));
0029 }
0030
0031 static bool ea_hw_range_overlaps(unsigned long ea, int size,
0032 struct arch_hw_breakpoint *info)
0033 {
0034 unsigned long hw_start_addr, hw_end_addr;
0035 unsigned long align_size = HW_BREAKPOINT_SIZE;
0036
0037
0038
0039
0040
0041 if (!cpu_has_feature(CPU_FTR_ARCH_31) && size == 16)
0042 align_size = HW_BREAKPOINT_SIZE_QUADWORD;
0043
0044 hw_start_addr = ALIGN_DOWN(info->address, align_size);
0045 hw_end_addr = ALIGN(info->address + info->len, align_size);
0046
0047 return ((ea < hw_end_addr) && (ea + size > hw_start_addr));
0048 }
0049
0050
0051
0052
0053
0054
0055
0056 static bool check_dawrx_constraints(struct pt_regs *regs, int type,
0057 struct arch_hw_breakpoint *info)
0058 {
0059 if (OP_IS_LOAD(type) && !(info->type & HW_BRK_TYPE_READ))
0060 return false;
0061
0062
0063
0064
0065
0066
0067 if ((OP_IS_STORE(type) || type == CACHEOP) && !(info->type & HW_BRK_TYPE_WRITE))
0068 return false;
0069
0070 if (is_kernel_addr(regs->nip) && !(info->type & HW_BRK_TYPE_KERNEL))
0071 return false;
0072
0073 if (user_mode(regs) && !(info->type & HW_BRK_TYPE_USER))
0074 return false;
0075
0076 return true;
0077 }
0078
0079
0080
0081
0082
0083 bool wp_check_constraints(struct pt_regs *regs, ppc_inst_t instr,
0084 unsigned long ea, int type, int size,
0085 struct arch_hw_breakpoint *info)
0086 {
0087 bool in_user_range = dar_in_user_range(regs->dar, info);
0088 bool dawrx_constraints;
0089
0090
0091
0092
0093
0094 if (IS_ENABLED(CONFIG_PPC_8xx)) {
0095 if (!in_user_range)
0096 info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
0097 return true;
0098 }
0099
0100 if (unlikely(ppc_inst_equal(instr, ppc_inst(0)))) {
0101 if (cpu_has_feature(CPU_FTR_ARCH_31) &&
0102 !dar_in_hw_range(regs->dar, info))
0103 return false;
0104
0105 return true;
0106 }
0107
0108 dawrx_constraints = check_dawrx_constraints(regs, type, info);
0109
0110 if (type == UNKNOWN) {
0111 if (cpu_has_feature(CPU_FTR_ARCH_31) &&
0112 !dar_in_hw_range(regs->dar, info))
0113 return false;
0114
0115 return dawrx_constraints;
0116 }
0117
0118 if (ea_user_range_overlaps(ea, size, info))
0119 return dawrx_constraints;
0120
0121 if (ea_hw_range_overlaps(ea, size, info)) {
0122 if (dawrx_constraints) {
0123 info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
0124 return true;
0125 }
0126 }
0127 return false;
0128 }
0129
0130 void wp_get_instr_detail(struct pt_regs *regs, ppc_inst_t *instr,
0131 int *type, int *size, unsigned long *ea)
0132 {
0133 struct instruction_op op;
0134
0135 if (__get_user_instr(*instr, (void __user *)regs->nip))
0136 return;
0137
0138 analyse_instr(&op, regs, *instr);
0139 *type = GETTYPE(op.type);
0140 *ea = op.ea;
0141
0142 if (!(regs->msr & MSR_64BIT))
0143 *ea &= 0xffffffffUL;
0144
0145
0146 *size = GETSIZE(op.type);
0147 if (*type == CACHEOP) {
0148 *size = l1_dcache_bytes();
0149 *ea &= ~(*size - 1);
0150 } else if (*type == LOAD_VMX || *type == STORE_VMX) {
0151 *ea &= ~(*size - 1);
0152 }
0153 }