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

ptrace-noadv.c (7763B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2
      3#include <linux/regset.h>
      4#include <linux/hw_breakpoint.h>
      5
      6#include <asm/debug.h>
      7
      8#include "ptrace-decl.h"
      9
     10void user_enable_single_step(struct task_struct *task)
     11{
     12	struct pt_regs *regs = task->thread.regs;
     13
     14	if (regs != NULL)
     15		regs_set_return_msr(regs, (regs->msr & ~MSR_BE) | MSR_SE);
     16	set_tsk_thread_flag(task, TIF_SINGLESTEP);
     17}
     18
     19void user_enable_block_step(struct task_struct *task)
     20{
     21	struct pt_regs *regs = task->thread.regs;
     22
     23	if (regs != NULL)
     24		regs_set_return_msr(regs, (regs->msr & ~MSR_SE) | MSR_BE);
     25	set_tsk_thread_flag(task, TIF_SINGLESTEP);
     26}
     27
     28void user_disable_single_step(struct task_struct *task)
     29{
     30	struct pt_regs *regs = task->thread.regs;
     31
     32	if (regs != NULL)
     33		regs_set_return_msr(regs, regs->msr & ~(MSR_SE | MSR_BE));
     34
     35	clear_tsk_thread_flag(task, TIF_SINGLESTEP);
     36}
     37
     38void ppc_gethwdinfo(struct ppc_debug_info *dbginfo)
     39{
     40	dbginfo->version = 1;
     41	dbginfo->num_instruction_bps = 0;
     42	if (ppc_breakpoint_available())
     43		dbginfo->num_data_bps = nr_wp_slots();
     44	else
     45		dbginfo->num_data_bps = 0;
     46	dbginfo->num_condition_regs = 0;
     47	dbginfo->data_bp_alignment = sizeof(long);
     48	dbginfo->sizeof_condition = 0;
     49	if (IS_ENABLED(CONFIG_HAVE_HW_BREAKPOINT)) {
     50		dbginfo->features = PPC_DEBUG_FEATURE_DATA_BP_RANGE;
     51		if (dawr_enabled())
     52			dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_DAWR;
     53	} else {
     54		dbginfo->features = 0;
     55	}
     56	if (cpu_has_feature(CPU_FTR_ARCH_31))
     57		dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_ARCH_31;
     58}
     59
     60int ptrace_get_debugreg(struct task_struct *child, unsigned long addr,
     61			unsigned long __user *datalp)
     62{
     63	unsigned long dabr_fake;
     64
     65	/* We only support one DABR and no IABRS at the moment */
     66	if (addr > 0)
     67		return -EINVAL;
     68	dabr_fake = ((child->thread.hw_brk[0].address & (~HW_BRK_TYPE_DABR)) |
     69		     (child->thread.hw_brk[0].type & HW_BRK_TYPE_DABR));
     70	return put_user(dabr_fake, datalp);
     71}
     72
     73/*
     74 * ptrace_set_debugreg() fakes DABR and DABR is only one. So even if
     75 * internal hw supports more than one watchpoint, we support only one
     76 * watchpoint with this interface.
     77 */
     78int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, unsigned long data)
     79{
     80#ifdef CONFIG_HAVE_HW_BREAKPOINT
     81	int ret;
     82	struct thread_struct *thread = &task->thread;
     83	struct perf_event *bp;
     84	struct perf_event_attr attr;
     85#endif /* CONFIG_HAVE_HW_BREAKPOINT */
     86	bool set_bp = true;
     87	struct arch_hw_breakpoint hw_brk;
     88
     89	/* For ppc64 we support one DABR and no IABR's at the moment (ppc64).
     90	 *  For embedded processors we support one DAC and no IAC's at the
     91	 *  moment.
     92	 */
     93	if (addr > 0)
     94		return -EINVAL;
     95
     96	/* The bottom 3 bits in dabr are flags */
     97	if ((data & ~0x7UL) >= TASK_SIZE)
     98		return -EIO;
     99
    100	/* For processors using DABR (i.e. 970), the bottom 3 bits are flags.
    101	 *  It was assumed, on previous implementations, that 3 bits were
    102	 *  passed together with the data address, fitting the design of the
    103	 *  DABR register, as follows:
    104	 *
    105	 *  bit 0: Read flag
    106	 *  bit 1: Write flag
    107	 *  bit 2: Breakpoint translation
    108	 *
    109	 *  Thus, we use them here as so.
    110	 */
    111
    112	/* Ensure breakpoint translation bit is set */
    113	if (data && !(data & HW_BRK_TYPE_TRANSLATE))
    114		return -EIO;
    115	hw_brk.address = data & (~HW_BRK_TYPE_DABR);
    116	hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
    117	hw_brk.len = DABR_MAX_LEN;
    118	hw_brk.hw_len = DABR_MAX_LEN;
    119	set_bp = (data) && (hw_brk.type & HW_BRK_TYPE_RDWR);
    120#ifdef CONFIG_HAVE_HW_BREAKPOINT
    121	bp = thread->ptrace_bps[0];
    122	if (!set_bp) {
    123		if (bp) {
    124			unregister_hw_breakpoint(bp);
    125			thread->ptrace_bps[0] = NULL;
    126		}
    127		return 0;
    128	}
    129	if (bp) {
    130		attr = bp->attr;
    131		attr.bp_addr = hw_brk.address;
    132		attr.bp_len = DABR_MAX_LEN;
    133		arch_bp_generic_fields(hw_brk.type, &attr.bp_type);
    134
    135		/* Enable breakpoint */
    136		attr.disabled = false;
    137
    138		ret =  modify_user_hw_breakpoint(bp, &attr);
    139		if (ret)
    140			return ret;
    141
    142		thread->ptrace_bps[0] = bp;
    143		thread->hw_brk[0] = hw_brk;
    144		return 0;
    145	}
    146
    147	/* Create a new breakpoint request if one doesn't exist already */
    148	hw_breakpoint_init(&attr);
    149	attr.bp_addr = hw_brk.address;
    150	attr.bp_len = DABR_MAX_LEN;
    151	arch_bp_generic_fields(hw_brk.type,
    152			       &attr.bp_type);
    153
    154	thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
    155					       ptrace_triggered, NULL, task);
    156	if (IS_ERR(bp)) {
    157		thread->ptrace_bps[0] = NULL;
    158		return PTR_ERR(bp);
    159	}
    160
    161#else /* !CONFIG_HAVE_HW_BREAKPOINT */
    162	if (set_bp && (!ppc_breakpoint_available()))
    163		return -ENODEV;
    164#endif /* CONFIG_HAVE_HW_BREAKPOINT */
    165	task->thread.hw_brk[0] = hw_brk;
    166	return 0;
    167}
    168
    169#ifdef CONFIG_HAVE_HW_BREAKPOINT
    170static int find_empty_ptrace_bp(struct thread_struct *thread)
    171{
    172	int i;
    173
    174	for (i = 0; i < nr_wp_slots(); i++) {
    175		if (!thread->ptrace_bps[i])
    176			return i;
    177	}
    178	return -1;
    179}
    180#endif
    181
    182static int find_empty_hw_brk(struct thread_struct *thread)
    183{
    184	int i;
    185
    186	for (i = 0; i < nr_wp_slots(); i++) {
    187		if (!thread->hw_brk[i].address)
    188			return i;
    189	}
    190	return -1;
    191}
    192
    193long ppc_set_hwdebug(struct task_struct *child, struct ppc_hw_breakpoint *bp_info)
    194{
    195	int i;
    196#ifdef CONFIG_HAVE_HW_BREAKPOINT
    197	int len = 0;
    198	struct thread_struct *thread = &child->thread;
    199	struct perf_event *bp;
    200	struct perf_event_attr attr;
    201#endif /* CONFIG_HAVE_HW_BREAKPOINT */
    202	struct arch_hw_breakpoint brk;
    203
    204	if (bp_info->version != 1)
    205		return -ENOTSUPP;
    206	/*
    207	 * We only support one data breakpoint
    208	 */
    209	if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||
    210	    (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 ||
    211	    bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
    212		return -EINVAL;
    213
    214	if ((unsigned long)bp_info->addr >= TASK_SIZE)
    215		return -EIO;
    216
    217	brk.address = ALIGN_DOWN(bp_info->addr, HW_BREAKPOINT_SIZE);
    218	brk.type = HW_BRK_TYPE_TRANSLATE | HW_BRK_TYPE_PRIV_ALL;
    219	brk.len = DABR_MAX_LEN;
    220	brk.hw_len = DABR_MAX_LEN;
    221	if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
    222		brk.type |= HW_BRK_TYPE_READ;
    223	if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
    224		brk.type |= HW_BRK_TYPE_WRITE;
    225#ifdef CONFIG_HAVE_HW_BREAKPOINT
    226	if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
    227		len = bp_info->addr2 - bp_info->addr;
    228	else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
    229		len = 1;
    230	else
    231		return -EINVAL;
    232
    233	i = find_empty_ptrace_bp(thread);
    234	if (i < 0)
    235		return -ENOSPC;
    236
    237	/* Create a new breakpoint request if one doesn't exist already */
    238	hw_breakpoint_init(&attr);
    239	attr.bp_addr = (unsigned long)bp_info->addr;
    240	attr.bp_len = len;
    241	arch_bp_generic_fields(brk.type, &attr.bp_type);
    242
    243	bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, child);
    244	thread->ptrace_bps[i] = bp;
    245	if (IS_ERR(bp)) {
    246		thread->ptrace_bps[i] = NULL;
    247		return PTR_ERR(bp);
    248	}
    249
    250	return i + 1;
    251#endif /* CONFIG_HAVE_HW_BREAKPOINT */
    252
    253	if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
    254		return -EINVAL;
    255
    256	i = find_empty_hw_brk(&child->thread);
    257	if (i < 0)
    258		return -ENOSPC;
    259
    260	if (!ppc_breakpoint_available())
    261		return -ENODEV;
    262
    263	child->thread.hw_brk[i] = brk;
    264
    265	return i + 1;
    266}
    267
    268long ppc_del_hwdebug(struct task_struct *child, long data)
    269{
    270#ifdef CONFIG_HAVE_HW_BREAKPOINT
    271	int ret = 0;
    272	struct thread_struct *thread = &child->thread;
    273	struct perf_event *bp;
    274#endif /* CONFIG_HAVE_HW_BREAKPOINT */
    275	if (data < 1 || data > nr_wp_slots())
    276		return -EINVAL;
    277
    278#ifdef CONFIG_HAVE_HW_BREAKPOINT
    279	bp = thread->ptrace_bps[data - 1];
    280	if (bp) {
    281		unregister_hw_breakpoint(bp);
    282		thread->ptrace_bps[data - 1] = NULL;
    283	} else {
    284		ret = -ENOENT;
    285	}
    286	return ret;
    287#else /* CONFIG_HAVE_HW_BREAKPOINT */
    288	if (!(child->thread.hw_brk[data - 1].flags & HW_BRK_FLAG_DISABLED) &&
    289	    child->thread.hw_brk[data - 1].address == 0)
    290		return -ENOENT;
    291
    292	child->thread.hw_brk[data - 1].address = 0;
    293	child->thread.hw_brk[data - 1].type = 0;
    294	child->thread.hw_brk[data - 1].flags = 0;
    295#endif /* CONFIG_HAVE_HW_BREAKPOINT */
    296
    297	return 0;
    298}