| // SPDX-License-Identifier: GPL-2.0-only |
| |
| #include <linux/jump_label.h> |
| #include <linux/kernel.h> |
| #include <linux/memory.h> |
| #include <linux/mutex.h> |
| #include <linux/uaccess.h> |
| #include <asm/cacheflush.h> |
| |
| #define NOP32_HI 0xc400 |
| #define NOP32_LO 0x4820 |
| #define BSR_LINK 0xe000 |
| |
| void arch_jump_label_transform(struct jump_entry *entry, |
| enum jump_label_type type) |
| { |
| unsigned long addr = jump_entry_code(entry); |
| u16 insn[2]; |
| int ret = 0; |
| |
| if (type == JUMP_LABEL_JMP) { |
| long offset = jump_entry_target(entry) - jump_entry_code(entry); |
| |
| if (WARN_ON(offset & 1 || offset < -67108864 || offset >= 67108864)) |
| return; |
| |
| offset = offset >> 1; |
| |
| insn[0] = BSR_LINK | |
| ((uint16_t)((unsigned long) offset >> 16) & 0x3ff); |
| insn[1] = (uint16_t)((unsigned long) offset & 0xffff); |
| } else { |
| insn[0] = NOP32_HI; |
| insn[1] = NOP32_LO; |
| } |
| |
| ret = copy_to_kernel_nofault((void *)addr, insn, 4); |
| WARN_ON(ret); |
| |
| flush_icache_range(addr, addr + 4); |
| } |
| |
| void arch_jump_label_transform_static(struct jump_entry *entry, |
| enum jump_label_type type) |
| { |
| /* |
| * We use the same instructions in the arch_static_branch and |
| * arch_static_branch_jump inline functions, so there's no |
| * need to patch them up here. |
| * The core will call arch_jump_label_transform when those |
| * instructions need to be replaced. |
| */ |
| arch_jump_label_transform(entry, type); |
| } |