^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * linux/arch/arm/kernel/swp_emulate.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2009 ARM Limited
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * __user_* functions adapted from include/asm/uaccess.h
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Implements emulation of the SWP/SWPB instructions using load-exclusive and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * store-exclusive for processors that have them disabled (or future ones that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * might not implement them).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * Where: Rt = destination
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * Rt2 = source
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * Rn = address
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/proc_fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/seq_file.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/sched/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/syscalls.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/perf_event.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <asm/opcodes.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <asm/system_info.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <asm/traps.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * Error-checking SWP macros implemented using ldrex{b}/strex{b}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define __user_swpX_asm(data, addr, res, temp, B) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) __asm__ __volatile__( \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) "0: ldrex"B" %2, [%3]\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) "1: strex"B" %0, %1, [%3]\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) " cmp %0, #0\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) " moveq %1, %2\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) " movne %0, %4\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) "2:\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) " .section .text.fixup,\"ax\"\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) " .align 2\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) "3: mov %0, %5\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) " b 2b\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) " .previous\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) " .section __ex_table,\"a\"\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) " .align 3\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) " .long 0b, 3b\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) " .long 1b, 3b\n" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) " .previous" \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) : "=&r" (res), "+r" (data), "=&r" (temp) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) : "cc", "memory")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #define __user_swp_asm(data, addr, res, temp) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) __user_swpX_asm(data, addr, res, temp, "")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #define __user_swpb_asm(data, addr, res, temp) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) __user_swpX_asm(data, addr, res, temp, "b")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * Macros/defines for extracting register numbers from instruction.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) #define EXTRACT_REG_NUM(instruction, offset) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) (((instruction) & (0xf << (offset))) >> (offset))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) #define RN_OFFSET 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) #define RT_OFFSET 12
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) #define RT2_OFFSET 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * Bit 22 of the instruction encoding distinguishes between
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * the SWP and SWPB variants (bit set means SWPB).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #define TYPE_SWPB (1 << 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static unsigned long swpcounter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static unsigned long swpbcounter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static unsigned long abtcounter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) static pid_t previous_pid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static int proc_status_show(struct seq_file *m, void *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) seq_printf(m, "Emulated SWP:\t\t%lu\n", swpcounter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) seq_printf(m, "Emulated SWPB:\t\t%lu\n", swpbcounter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) seq_printf(m, "Aborted SWP{B}:\t\t%lu\n", abtcounter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (previous_pid != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) seq_printf(m, "Last process:\t\t%d\n", previous_pid);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) * Set up process info to signal segmentation fault - called on access error.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) static void set_segfault(struct pt_regs *regs, unsigned long addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) int si_code;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) mmap_read_lock(current->mm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (find_vma(current->mm, addr) == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) si_code = SEGV_MAPERR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) si_code = SEGV_ACCERR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) mmap_read_unlock(current->mm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) pr_debug("SWP{B} emulation: access caused memory abort!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) arm_notify_die("Illegal memory access", regs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) SIGSEGV, si_code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) (void __user *)instruction_pointer(regs),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 0, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) abtcounter++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static int emulate_swpX(unsigned int address, unsigned int *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) unsigned int type)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) unsigned int res = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if ((type != TYPE_SWPB) && (address & 0x3)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) /* SWP to unaligned address not permitted */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) pr_debug("SWP instruction on unaligned pointer!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) unsigned long temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) unsigned int __ua_flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) __ua_flags = uaccess_save_and_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if (type == TYPE_SWPB)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) __user_swpb_asm(*data, address, res, temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) __user_swp_asm(*data, address, res, temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) uaccess_restore(__ua_flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) if (likely(res != -EAGAIN) || signal_pending(current))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) cond_resched();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (res == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) if (type == TYPE_SWPB)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) swpbcounter++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) swpcounter++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) * swp_handler logs the id of calling process, dissects the instruction, sanity
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) * checks the memory location, calls emulate_swpX for the actual operation and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) * deals with fixup/error handling before returning
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static int swp_handler(struct pt_regs *regs, unsigned int instr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) unsigned int address, destreg, data, type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) unsigned int res = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) res = arm_check_condition(instr, regs->ARM_cpsr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) switch (res) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) case ARM_OPCODE_CONDTEST_PASS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) case ARM_OPCODE_CONDTEST_FAIL:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) /* Condition failed - return to next instruction */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) regs->ARM_pc += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) case ARM_OPCODE_CONDTEST_UNCOND:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) /* If unconditional encoding - not a SWP, undef */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if (current->pid != previous_pid) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) current->comm, (unsigned long)current->pid);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) previous_pid = current->pid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) type = instr & TYPE_SWPB;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) EXTRACT_REG_NUM(instr, RN_OFFSET), address,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) /* Check access in reasonable access range for both SWP and SWPB */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) if (!access_ok((void __user *)(address & ~3), 4)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) pr_debug("SWP{B} emulation: access to %p not allowed!\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) (void *)address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) res = -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) res = emulate_swpX(address, &data, type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) if (res == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) * On successful emulation, revert the adjustment to the PC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) * made in kernel/traps.c in order to resume execution at the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) * instruction following the SWP{B}.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) regs->ARM_pc += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) regs->uregs[destreg] = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) } else if (res == -EFAULT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) * Memory errors do not mean emulation failed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) * Set up signal info to return SEGV, then return OK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) set_segfault(regs, address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) * Only emulate SWP/SWPB executed in ARM state/User mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) static struct undef_hook swp_hook = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) .instr_mask = 0x0fb00ff0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) .instr_val = 0x01000090,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) .cpsr_val = USR_MODE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) .fn = swp_handler
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) * Register handler and create status file in /proc/cpu
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) * Invoked as late_initcall, since not needed before init spawned.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) static int __init swp_emulation_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) if (cpu_architecture() < CPU_ARCH_ARMv7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) #ifdef CONFIG_PROC_FS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) if (!proc_create_single("cpu/swp_emulation", S_IRUGO, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) proc_status_show))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) #endif /* CONFIG_PROC_FS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) pr_notice("Registering SWP/SWPB emulation handler\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) register_undef_hook(&swp_hook);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) late_initcall(swp_emulation_init);