Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
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      * On p10 predecessors, quadword is handle differently then
0039      * other instructions.
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  * If hw has multiple DAWR registers, we also need to check all
0052  * dawrx constraint bits to confirm this is _really_ a valid event.
0053  * If type is UNKNOWN, but privilege level matches, consider it as
0054  * a positive match.
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      * The Cache Management instructions other than dcbz never
0064      * cause a match. i.e. if type is CACHEOP, the instruction
0065      * is dcbz, and dcbz is treated as Store.
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  * Return true if the event is valid wrt dawr configuration,
0081  * including extraneous exception. Otherwise return false.
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      * 8xx supports only one breakpoint and thus we can
0092      * unconditionally return true.
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 }