cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hw_breakpoint_constraints.c (4020B)


      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
    135	if (__get_user_instr(*instr, (void __user *)regs->nip))
    136		return;
    137
    138	analyse_instr(&op, regs, *instr);
    139	*type = GETTYPE(op.type);
    140	*ea = op.ea;
    141
    142	if (!(regs->msr & MSR_64BIT))
    143		*ea &= 0xffffffffUL;
    144
    145
    146	*size = GETSIZE(op.type);
    147	if (*type == CACHEOP) {
    148		*size = l1_dcache_bytes();
    149		*ea &= ~(*size - 1);
    150	} else if (*type == LOAD_VMX || *type == STORE_VMX) {
    151		*ea &= ~(*size - 1);
    152	}
    153}