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

jump_label.c (2206B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (C) 2018 Cadence Design Systems Inc.
      3
      4#include <linux/cpu.h>
      5#include <linux/jump_label.h>
      6#include <linux/kernel.h>
      7#include <linux/memory.h>
      8#include <linux/stop_machine.h>
      9#include <linux/types.h>
     10
     11#include <asm/cacheflush.h>
     12
     13#define J_OFFSET_MASK 0x0003ffff
     14#define J_SIGN_MASK (~(J_OFFSET_MASK >> 1))
     15
     16#if defined(__XTENSA_EL__)
     17#define J_INSN 0x6
     18#define NOP_INSN 0x0020f0
     19#elif defined(__XTENSA_EB__)
     20#define J_INSN 0x60000000
     21#define NOP_INSN 0x0f020000
     22#else
     23#error Unsupported endianness.
     24#endif
     25
     26struct patch {
     27	atomic_t cpu_count;
     28	unsigned long addr;
     29	size_t sz;
     30	const void *data;
     31};
     32
     33static void local_patch_text(unsigned long addr, const void *data, size_t sz)
     34{
     35	memcpy((void *)addr, data, sz);
     36	local_flush_icache_range(addr, addr + sz);
     37}
     38
     39static int patch_text_stop_machine(void *data)
     40{
     41	struct patch *patch = data;
     42
     43	if (atomic_inc_return(&patch->cpu_count) == num_online_cpus()) {
     44		local_patch_text(patch->addr, patch->data, patch->sz);
     45		atomic_inc(&patch->cpu_count);
     46	} else {
     47		while (atomic_read(&patch->cpu_count) <= num_online_cpus())
     48			cpu_relax();
     49		__invalidate_icache_range(patch->addr, patch->sz);
     50	}
     51	return 0;
     52}
     53
     54static void patch_text(unsigned long addr, const void *data, size_t sz)
     55{
     56	if (IS_ENABLED(CONFIG_SMP)) {
     57		struct patch patch = {
     58			.cpu_count = ATOMIC_INIT(0),
     59			.addr = addr,
     60			.sz = sz,
     61			.data = data,
     62		};
     63		stop_machine_cpuslocked(patch_text_stop_machine,
     64					&patch, cpu_online_mask);
     65	} else {
     66		unsigned long flags;
     67
     68		local_irq_save(flags);
     69		local_patch_text(addr, data, sz);
     70		local_irq_restore(flags);
     71	}
     72}
     73
     74void arch_jump_label_transform(struct jump_entry *e,
     75			       enum jump_label_type type)
     76{
     77	u32 d = (jump_entry_target(e) - (jump_entry_code(e) + 4));
     78	u32 insn;
     79
     80	/* Jump only works within 128K of the J instruction. */
     81	BUG_ON(!((d & J_SIGN_MASK) == 0 ||
     82		 (d & J_SIGN_MASK) == J_SIGN_MASK));
     83
     84	if (type == JUMP_LABEL_JMP) {
     85#if defined(__XTENSA_EL__)
     86		insn = ((d & J_OFFSET_MASK) << 6) | J_INSN;
     87#elif defined(__XTENSA_EB__)
     88		insn = ((d & J_OFFSET_MASK) << 8) | J_INSN;
     89#endif
     90	} else {
     91		insn = NOP_INSN;
     92	}
     93
     94	patch_text(jump_entry_code(e), &insn, JUMP_LABEL_NOP_SIZE);
     95}