1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- // SPDX-License-Identifier: GPL-2.0
- // Copyright (C) 2018 Cadence Design Systems Inc.
- #include <linux/cpu.h>
- #include <linux/jump_label.h>
- #include <linux/kernel.h>
- #include <linux/memory.h>
- #include <linux/stop_machine.h>
- #include <linux/types.h>
- #include <asm/cacheflush.h>
- #define J_OFFSET_MASK 0x0003ffff
- #define J_SIGN_MASK (~(J_OFFSET_MASK >> 1))
- #if defined(__XTENSA_EL__)
- #define J_INSN 0x6
- #define NOP_INSN 0x0020f0
- #elif defined(__XTENSA_EB__)
- #define J_INSN 0x60000000
- #define NOP_INSN 0x0f020000
- #else
- #error Unsupported endianness.
- #endif
- struct patch {
- atomic_t cpu_count;
- unsigned long addr;
- size_t sz;
- const void *data;
- };
- static void local_patch_text(unsigned long addr, const void *data, size_t sz)
- {
- memcpy((void *)addr, data, sz);
- local_flush_icache_range(addr, addr + sz);
- }
- static int patch_text_stop_machine(void *data)
- {
- struct patch *patch = data;
- if (atomic_inc_return(&patch->cpu_count) == num_online_cpus()) {
- local_patch_text(patch->addr, patch->data, patch->sz);
- atomic_inc(&patch->cpu_count);
- } else {
- while (atomic_read(&patch->cpu_count) <= num_online_cpus())
- cpu_relax();
- __invalidate_icache_range(patch->addr, patch->sz);
- }
- return 0;
- }
- static void patch_text(unsigned long addr, const void *data, size_t sz)
- {
- if (IS_ENABLED(CONFIG_SMP)) {
- struct patch patch = {
- .cpu_count = ATOMIC_INIT(0),
- .addr = addr,
- .sz = sz,
- .data = data,
- };
- stop_machine_cpuslocked(patch_text_stop_machine,
- &patch, cpu_online_mask);
- } else {
- unsigned long flags;
- local_irq_save(flags);
- local_patch_text(addr, data, sz);
- local_irq_restore(flags);
- }
- }
- void arch_jump_label_transform(struct jump_entry *e,
- enum jump_label_type type)
- {
- u32 d = (jump_entry_target(e) - (jump_entry_code(e) + 4));
- u32 insn;
- /* Jump only works within 128K of the J instruction. */
- BUG_ON(!((d & J_SIGN_MASK) == 0 ||
- (d & J_SIGN_MASK) == J_SIGN_MASK));
- if (type == JUMP_LABEL_JMP) {
- #if defined(__XTENSA_EL__)
- insn = ((d & J_OFFSET_MASK) << 6) | J_INSN;
- #elif defined(__XTENSA_EB__)
- insn = ((d & J_OFFSET_MASK) << 8) | J_INSN;
- #endif
- } else {
- insn = NOP_INSN;
- }
- patch_text(jump_entry_code(e), &insn, JUMP_LABEL_NOP_SIZE);
- }
|