1587064b6SPunit Agrawal /* 2587064b6SPunit Agrawal * Copyright (C) 2014 ARM Limited 3587064b6SPunit Agrawal * 4587064b6SPunit Agrawal * This program is free software; you can redistribute it and/or modify 5587064b6SPunit Agrawal * it under the terms of the GNU General Public License version 2 as 6587064b6SPunit Agrawal * published by the Free Software Foundation. 7587064b6SPunit Agrawal */ 8587064b6SPunit Agrawal 9c852f320SPunit Agrawal #include <linux/cpu.h> 10587064b6SPunit Agrawal #include <linux/init.h> 11587064b6SPunit Agrawal #include <linux/list.h> 12bd35a4adSPunit Agrawal #include <linux/perf_event.h> 13bd35a4adSPunit Agrawal #include <linux/sched.h> 14587064b6SPunit Agrawal #include <linux/slab.h> 15587064b6SPunit Agrawal #include <linux/sysctl.h> 16587064b6SPunit Agrawal 17bd35a4adSPunit Agrawal #include <asm/insn.h> 18bd35a4adSPunit Agrawal #include <asm/opcodes.h> 19bd35a4adSPunit Agrawal #include <asm/system_misc.h> 20587064b6SPunit Agrawal #include <asm/traps.h> 21bd35a4adSPunit Agrawal #include <asm/uaccess.h> 22587064b6SPunit Agrawal 23587064b6SPunit Agrawal /* 24587064b6SPunit Agrawal * The runtime support for deprecated instruction support can be in one of 25587064b6SPunit Agrawal * following three states - 26587064b6SPunit Agrawal * 27587064b6SPunit Agrawal * 0 = undef 28587064b6SPunit Agrawal * 1 = emulate (software emulation) 29587064b6SPunit Agrawal * 2 = hw (supported in hardware) 30587064b6SPunit Agrawal */ 31587064b6SPunit Agrawal enum insn_emulation_mode { 32587064b6SPunit Agrawal INSN_UNDEF, 33587064b6SPunit Agrawal INSN_EMULATE, 34587064b6SPunit Agrawal INSN_HW, 35587064b6SPunit Agrawal }; 36587064b6SPunit Agrawal 37587064b6SPunit Agrawal enum legacy_insn_status { 38587064b6SPunit Agrawal INSN_DEPRECATED, 39587064b6SPunit Agrawal INSN_OBSOLETE, 40587064b6SPunit Agrawal }; 41587064b6SPunit Agrawal 42587064b6SPunit Agrawal struct insn_emulation_ops { 43587064b6SPunit Agrawal const char *name; 44587064b6SPunit Agrawal enum legacy_insn_status status; 45587064b6SPunit Agrawal struct undef_hook *hooks; 46587064b6SPunit Agrawal int (*set_hw_mode)(bool enable); 47587064b6SPunit Agrawal }; 48587064b6SPunit Agrawal 49587064b6SPunit Agrawal struct insn_emulation { 50587064b6SPunit Agrawal struct list_head node; 51587064b6SPunit Agrawal struct insn_emulation_ops *ops; 52587064b6SPunit Agrawal int current_mode; 53587064b6SPunit Agrawal int min; 54587064b6SPunit Agrawal int max; 55587064b6SPunit Agrawal }; 56587064b6SPunit Agrawal 57587064b6SPunit Agrawal static LIST_HEAD(insn_emulation); 58587064b6SPunit Agrawal static int nr_insn_emulated; 59587064b6SPunit Agrawal static DEFINE_RAW_SPINLOCK(insn_emulation_lock); 60587064b6SPunit Agrawal 61587064b6SPunit Agrawal static void register_emulation_hooks(struct insn_emulation_ops *ops) 62587064b6SPunit Agrawal { 63587064b6SPunit Agrawal struct undef_hook *hook; 64587064b6SPunit Agrawal 65587064b6SPunit Agrawal BUG_ON(!ops->hooks); 66587064b6SPunit Agrawal 67587064b6SPunit Agrawal for (hook = ops->hooks; hook->instr_mask; hook++) 68587064b6SPunit Agrawal register_undef_hook(hook); 69587064b6SPunit Agrawal 70587064b6SPunit Agrawal pr_notice("Registered %s emulation handler\n", ops->name); 71587064b6SPunit Agrawal } 72587064b6SPunit Agrawal 73587064b6SPunit Agrawal static void remove_emulation_hooks(struct insn_emulation_ops *ops) 74587064b6SPunit Agrawal { 75587064b6SPunit Agrawal struct undef_hook *hook; 76587064b6SPunit Agrawal 77587064b6SPunit Agrawal BUG_ON(!ops->hooks); 78587064b6SPunit Agrawal 79587064b6SPunit Agrawal for (hook = ops->hooks; hook->instr_mask; hook++) 80587064b6SPunit Agrawal unregister_undef_hook(hook); 81587064b6SPunit Agrawal 82587064b6SPunit Agrawal pr_notice("Removed %s emulation handler\n", ops->name); 83587064b6SPunit Agrawal } 84587064b6SPunit Agrawal 85587064b6SPunit Agrawal static int update_insn_emulation_mode(struct insn_emulation *insn, 86587064b6SPunit Agrawal enum insn_emulation_mode prev) 87587064b6SPunit Agrawal { 88587064b6SPunit Agrawal int ret = 0; 89587064b6SPunit Agrawal 90587064b6SPunit Agrawal switch (prev) { 91587064b6SPunit Agrawal case INSN_UNDEF: /* Nothing to be done */ 92587064b6SPunit Agrawal break; 93587064b6SPunit Agrawal case INSN_EMULATE: 94587064b6SPunit Agrawal remove_emulation_hooks(insn->ops); 95587064b6SPunit Agrawal break; 96587064b6SPunit Agrawal case INSN_HW: 97587064b6SPunit Agrawal if (insn->ops->set_hw_mode) { 98587064b6SPunit Agrawal insn->ops->set_hw_mode(false); 99587064b6SPunit Agrawal pr_notice("Disabled %s support\n", insn->ops->name); 100587064b6SPunit Agrawal } 101587064b6SPunit Agrawal break; 102587064b6SPunit Agrawal } 103587064b6SPunit Agrawal 104587064b6SPunit Agrawal switch (insn->current_mode) { 105587064b6SPunit Agrawal case INSN_UNDEF: 106587064b6SPunit Agrawal break; 107587064b6SPunit Agrawal case INSN_EMULATE: 108587064b6SPunit Agrawal register_emulation_hooks(insn->ops); 109587064b6SPunit Agrawal break; 110587064b6SPunit Agrawal case INSN_HW: 111587064b6SPunit Agrawal if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true)) 112587064b6SPunit Agrawal pr_notice("Enabled %s support\n", insn->ops->name); 113587064b6SPunit Agrawal else 114587064b6SPunit Agrawal ret = -EINVAL; 115587064b6SPunit Agrawal break; 116587064b6SPunit Agrawal } 117587064b6SPunit Agrawal 118587064b6SPunit Agrawal return ret; 119587064b6SPunit Agrawal } 120587064b6SPunit Agrawal 121587064b6SPunit Agrawal static void register_insn_emulation(struct insn_emulation_ops *ops) 122587064b6SPunit Agrawal { 123587064b6SPunit Agrawal unsigned long flags; 124587064b6SPunit Agrawal struct insn_emulation *insn; 125587064b6SPunit Agrawal 126587064b6SPunit Agrawal insn = kzalloc(sizeof(*insn), GFP_KERNEL); 127587064b6SPunit Agrawal insn->ops = ops; 128587064b6SPunit Agrawal insn->min = INSN_UNDEF; 129587064b6SPunit Agrawal 130587064b6SPunit Agrawal switch (ops->status) { 131587064b6SPunit Agrawal case INSN_DEPRECATED: 132587064b6SPunit Agrawal insn->current_mode = INSN_EMULATE; 133587064b6SPunit Agrawal insn->max = INSN_HW; 134587064b6SPunit Agrawal break; 135587064b6SPunit Agrawal case INSN_OBSOLETE: 136587064b6SPunit Agrawal insn->current_mode = INSN_UNDEF; 137587064b6SPunit Agrawal insn->max = INSN_EMULATE; 138587064b6SPunit Agrawal break; 139587064b6SPunit Agrawal } 140587064b6SPunit Agrawal 141587064b6SPunit Agrawal raw_spin_lock_irqsave(&insn_emulation_lock, flags); 142587064b6SPunit Agrawal list_add(&insn->node, &insn_emulation); 143587064b6SPunit Agrawal nr_insn_emulated++; 144587064b6SPunit Agrawal raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); 145587064b6SPunit Agrawal 146587064b6SPunit Agrawal /* Register any handlers if required */ 147587064b6SPunit Agrawal update_insn_emulation_mode(insn, INSN_UNDEF); 148587064b6SPunit Agrawal } 149587064b6SPunit Agrawal 150587064b6SPunit Agrawal static int emulation_proc_handler(struct ctl_table *table, int write, 151587064b6SPunit Agrawal void __user *buffer, size_t *lenp, 152587064b6SPunit Agrawal loff_t *ppos) 153587064b6SPunit Agrawal { 154587064b6SPunit Agrawal int ret = 0; 155587064b6SPunit Agrawal struct insn_emulation *insn = (struct insn_emulation *) table->data; 156587064b6SPunit Agrawal enum insn_emulation_mode prev_mode = insn->current_mode; 157587064b6SPunit Agrawal 158587064b6SPunit Agrawal table->data = &insn->current_mode; 159587064b6SPunit Agrawal ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); 160587064b6SPunit Agrawal 161587064b6SPunit Agrawal if (ret || !write || prev_mode == insn->current_mode) 162587064b6SPunit Agrawal goto ret; 163587064b6SPunit Agrawal 164587064b6SPunit Agrawal ret = update_insn_emulation_mode(insn, prev_mode); 165587064b6SPunit Agrawal if (!ret) { 166587064b6SPunit Agrawal /* Mode change failed, revert to previous mode. */ 167587064b6SPunit Agrawal insn->current_mode = prev_mode; 168587064b6SPunit Agrawal update_insn_emulation_mode(insn, INSN_UNDEF); 169587064b6SPunit Agrawal } 170587064b6SPunit Agrawal ret: 171587064b6SPunit Agrawal table->data = insn; 172587064b6SPunit Agrawal return ret; 173587064b6SPunit Agrawal } 174587064b6SPunit Agrawal 175587064b6SPunit Agrawal static struct ctl_table ctl_abi[] = { 176587064b6SPunit Agrawal { 177587064b6SPunit Agrawal .procname = "abi", 178587064b6SPunit Agrawal .mode = 0555, 179587064b6SPunit Agrawal }, 180587064b6SPunit Agrawal { } 181587064b6SPunit Agrawal }; 182587064b6SPunit Agrawal 183587064b6SPunit Agrawal static void register_insn_emulation_sysctl(struct ctl_table *table) 184587064b6SPunit Agrawal { 185587064b6SPunit Agrawal unsigned long flags; 186587064b6SPunit Agrawal int i = 0; 187587064b6SPunit Agrawal struct insn_emulation *insn; 188587064b6SPunit Agrawal struct ctl_table *insns_sysctl, *sysctl; 189587064b6SPunit Agrawal 190587064b6SPunit Agrawal insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1), 191587064b6SPunit Agrawal GFP_KERNEL); 192587064b6SPunit Agrawal 193587064b6SPunit Agrawal raw_spin_lock_irqsave(&insn_emulation_lock, flags); 194587064b6SPunit Agrawal list_for_each_entry(insn, &insn_emulation, node) { 195587064b6SPunit Agrawal sysctl = &insns_sysctl[i]; 196587064b6SPunit Agrawal 197587064b6SPunit Agrawal sysctl->mode = 0644; 198587064b6SPunit Agrawal sysctl->maxlen = sizeof(int); 199587064b6SPunit Agrawal 200587064b6SPunit Agrawal sysctl->procname = insn->ops->name; 201587064b6SPunit Agrawal sysctl->data = insn; 202587064b6SPunit Agrawal sysctl->extra1 = &insn->min; 203587064b6SPunit Agrawal sysctl->extra2 = &insn->max; 204587064b6SPunit Agrawal sysctl->proc_handler = emulation_proc_handler; 205587064b6SPunit Agrawal i++; 206587064b6SPunit Agrawal } 207587064b6SPunit Agrawal raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); 208587064b6SPunit Agrawal 209587064b6SPunit Agrawal table->child = insns_sysctl; 210587064b6SPunit Agrawal register_sysctl_table(table); 211587064b6SPunit Agrawal } 212587064b6SPunit Agrawal 213587064b6SPunit Agrawal /* 214bd35a4adSPunit Agrawal * Implement emulation of the SWP/SWPB instructions using load-exclusive and 215bd35a4adSPunit Agrawal * store-exclusive. 216bd35a4adSPunit Agrawal * 217bd35a4adSPunit Agrawal * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] 218bd35a4adSPunit Agrawal * Where: Rt = destination 219bd35a4adSPunit Agrawal * Rt2 = source 220bd35a4adSPunit Agrawal * Rn = address 221bd35a4adSPunit Agrawal */ 222bd35a4adSPunit Agrawal 223bd35a4adSPunit Agrawal /* 224bd35a4adSPunit Agrawal * Error-checking SWP macros implemented using ldxr{b}/stxr{b} 225bd35a4adSPunit Agrawal */ 226bd35a4adSPunit Agrawal #define __user_swpX_asm(data, addr, res, temp, B) \ 227bd35a4adSPunit Agrawal __asm__ __volatile__( \ 228bd35a4adSPunit Agrawal " mov %w2, %w1\n" \ 229bd35a4adSPunit Agrawal "0: ldxr"B" %w1, [%3]\n" \ 230bd35a4adSPunit Agrawal "1: stxr"B" %w0, %w2, [%3]\n" \ 231bd35a4adSPunit Agrawal " cbz %w0, 2f\n" \ 232bd35a4adSPunit Agrawal " mov %w0, %w4\n" \ 233bd35a4adSPunit Agrawal "2:\n" \ 234bd35a4adSPunit Agrawal " .pushsection .fixup,\"ax\"\n" \ 235bd35a4adSPunit Agrawal " .align 2\n" \ 236bd35a4adSPunit Agrawal "3: mov %w0, %w5\n" \ 237bd35a4adSPunit Agrawal " b 2b\n" \ 238bd35a4adSPunit Agrawal " .popsection" \ 239bd35a4adSPunit Agrawal " .pushsection __ex_table,\"a\"\n" \ 240bd35a4adSPunit Agrawal " .align 3\n" \ 241bd35a4adSPunit Agrawal " .quad 0b, 3b\n" \ 242bd35a4adSPunit Agrawal " .quad 1b, 3b\n" \ 243bd35a4adSPunit Agrawal " .popsection" \ 244bd35a4adSPunit Agrawal : "=&r" (res), "+r" (data), "=&r" (temp) \ 245bd35a4adSPunit Agrawal : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ 246bd35a4adSPunit Agrawal : "memory") 247bd35a4adSPunit Agrawal 248bd35a4adSPunit Agrawal #define __user_swp_asm(data, addr, res, temp) \ 249bd35a4adSPunit Agrawal __user_swpX_asm(data, addr, res, temp, "") 250bd35a4adSPunit Agrawal #define __user_swpb_asm(data, addr, res, temp) \ 251bd35a4adSPunit Agrawal __user_swpX_asm(data, addr, res, temp, "b") 252bd35a4adSPunit Agrawal 253bd35a4adSPunit Agrawal /* 254bd35a4adSPunit Agrawal * Bit 22 of the instruction encoding distinguishes between 255bd35a4adSPunit Agrawal * the SWP and SWPB variants (bit set means SWPB). 256bd35a4adSPunit Agrawal */ 257bd35a4adSPunit Agrawal #define TYPE_SWPB (1 << 22) 258bd35a4adSPunit Agrawal 259bd35a4adSPunit Agrawal /* 260bd35a4adSPunit Agrawal * Set up process info to signal segmentation fault - called on access error. 261bd35a4adSPunit Agrawal */ 262bd35a4adSPunit Agrawal static void set_segfault(struct pt_regs *regs, unsigned long addr) 263bd35a4adSPunit Agrawal { 264bd35a4adSPunit Agrawal siginfo_t info; 265bd35a4adSPunit Agrawal 266bd35a4adSPunit Agrawal down_read(¤t->mm->mmap_sem); 267bd35a4adSPunit Agrawal if (find_vma(current->mm, addr) == NULL) 268bd35a4adSPunit Agrawal info.si_code = SEGV_MAPERR; 269bd35a4adSPunit Agrawal else 270bd35a4adSPunit Agrawal info.si_code = SEGV_ACCERR; 271bd35a4adSPunit Agrawal up_read(¤t->mm->mmap_sem); 272bd35a4adSPunit Agrawal 273bd35a4adSPunit Agrawal info.si_signo = SIGSEGV; 274bd35a4adSPunit Agrawal info.si_errno = 0; 275bd35a4adSPunit Agrawal info.si_addr = (void *) instruction_pointer(regs); 276bd35a4adSPunit Agrawal 277bd35a4adSPunit Agrawal pr_debug("SWP{B} emulation: access caused memory abort!\n"); 278bd35a4adSPunit Agrawal arm64_notify_die("Illegal memory access", regs, &info, 0); 279bd35a4adSPunit Agrawal } 280bd35a4adSPunit Agrawal 281bd35a4adSPunit Agrawal static int emulate_swpX(unsigned int address, unsigned int *data, 282bd35a4adSPunit Agrawal unsigned int type) 283bd35a4adSPunit Agrawal { 284bd35a4adSPunit Agrawal unsigned int res = 0; 285bd35a4adSPunit Agrawal 286bd35a4adSPunit Agrawal if ((type != TYPE_SWPB) && (address & 0x3)) { 287bd35a4adSPunit Agrawal /* SWP to unaligned address not permitted */ 288bd35a4adSPunit Agrawal pr_debug("SWP instruction on unaligned pointer!\n"); 289bd35a4adSPunit Agrawal return -EFAULT; 290bd35a4adSPunit Agrawal } 291bd35a4adSPunit Agrawal 292bd35a4adSPunit Agrawal while (1) { 293bd35a4adSPunit Agrawal unsigned long temp; 294bd35a4adSPunit Agrawal 295bd35a4adSPunit Agrawal if (type == TYPE_SWPB) 296bd35a4adSPunit Agrawal __user_swpb_asm(*data, address, res, temp); 297bd35a4adSPunit Agrawal else 298bd35a4adSPunit Agrawal __user_swp_asm(*data, address, res, temp); 299bd35a4adSPunit Agrawal 300bd35a4adSPunit Agrawal if (likely(res != -EAGAIN) || signal_pending(current)) 301bd35a4adSPunit Agrawal break; 302bd35a4adSPunit Agrawal 303bd35a4adSPunit Agrawal cond_resched(); 304bd35a4adSPunit Agrawal } 305bd35a4adSPunit Agrawal 306bd35a4adSPunit Agrawal return res; 307bd35a4adSPunit Agrawal } 308bd35a4adSPunit Agrawal 309bd35a4adSPunit Agrawal /* 310bd35a4adSPunit Agrawal * swp_handler logs the id of calling process, dissects the instruction, sanity 311bd35a4adSPunit Agrawal * checks the memory location, calls emulate_swpX for the actual operation and 312bd35a4adSPunit Agrawal * deals with fixup/error handling before returning 313bd35a4adSPunit Agrawal */ 314bd35a4adSPunit Agrawal static int swp_handler(struct pt_regs *regs, u32 instr) 315bd35a4adSPunit Agrawal { 316bd35a4adSPunit Agrawal u32 destreg, data, type, address = 0; 317bd35a4adSPunit Agrawal int rn, rt2, res = 0; 318bd35a4adSPunit Agrawal 319bd35a4adSPunit Agrawal perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); 320bd35a4adSPunit Agrawal 321bd35a4adSPunit Agrawal type = instr & TYPE_SWPB; 322bd35a4adSPunit Agrawal 323bd35a4adSPunit Agrawal switch (arm_check_condition(instr, regs->pstate)) { 324bd35a4adSPunit Agrawal case ARM_OPCODE_CONDTEST_PASS: 325bd35a4adSPunit Agrawal break; 326bd35a4adSPunit Agrawal case ARM_OPCODE_CONDTEST_FAIL: 327bd35a4adSPunit Agrawal /* Condition failed - return to next instruction */ 328bd35a4adSPunit Agrawal goto ret; 329bd35a4adSPunit Agrawal case ARM_OPCODE_CONDTEST_UNCOND: 330bd35a4adSPunit Agrawal /* If unconditional encoding - not a SWP, undef */ 331bd35a4adSPunit Agrawal return -EFAULT; 332bd35a4adSPunit Agrawal default: 333bd35a4adSPunit Agrawal return -EINVAL; 334bd35a4adSPunit Agrawal } 335bd35a4adSPunit Agrawal 336bd35a4adSPunit Agrawal rn = aarch32_insn_extract_reg_num(instr, A32_RN_OFFSET); 337bd35a4adSPunit Agrawal rt2 = aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET); 338bd35a4adSPunit Agrawal 339bd35a4adSPunit Agrawal address = (u32)regs->user_regs.regs[rn]; 340bd35a4adSPunit Agrawal data = (u32)regs->user_regs.regs[rt2]; 341bd35a4adSPunit Agrawal destreg = aarch32_insn_extract_reg_num(instr, A32_RT_OFFSET); 342bd35a4adSPunit Agrawal 343bd35a4adSPunit Agrawal pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", 344bd35a4adSPunit Agrawal rn, address, destreg, 345bd35a4adSPunit Agrawal aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data); 346bd35a4adSPunit Agrawal 347bd35a4adSPunit Agrawal /* Check access in reasonable access range for both SWP and SWPB */ 348bd35a4adSPunit Agrawal if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { 349bd35a4adSPunit Agrawal pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n", 350bd35a4adSPunit Agrawal address); 351bd35a4adSPunit Agrawal goto fault; 352bd35a4adSPunit Agrawal } 353bd35a4adSPunit Agrawal 354bd35a4adSPunit Agrawal res = emulate_swpX(address, &data, type); 355bd35a4adSPunit Agrawal if (res == -EFAULT) 356bd35a4adSPunit Agrawal goto fault; 357bd35a4adSPunit Agrawal else if (res == 0) 358bd35a4adSPunit Agrawal regs->user_regs.regs[destreg] = data; 359bd35a4adSPunit Agrawal 360bd35a4adSPunit Agrawal ret: 361bd35a4adSPunit Agrawal pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n", 362bd35a4adSPunit Agrawal current->comm, (unsigned long)current->pid, regs->pc); 363bd35a4adSPunit Agrawal 364bd35a4adSPunit Agrawal regs->pc += 4; 365bd35a4adSPunit Agrawal return 0; 366bd35a4adSPunit Agrawal 367bd35a4adSPunit Agrawal fault: 368bd35a4adSPunit Agrawal set_segfault(regs, address); 369bd35a4adSPunit Agrawal 370bd35a4adSPunit Agrawal return 0; 371bd35a4adSPunit Agrawal } 372bd35a4adSPunit Agrawal 373bd35a4adSPunit Agrawal /* 374bd35a4adSPunit Agrawal * Only emulate SWP/SWPB executed in ARM state/User mode. 375bd35a4adSPunit Agrawal * The kernel must be SWP free and SWP{B} does not exist in Thumb. 376bd35a4adSPunit Agrawal */ 377bd35a4adSPunit Agrawal static struct undef_hook swp_hooks[] = { 378bd35a4adSPunit Agrawal { 379bd35a4adSPunit Agrawal .instr_mask = 0x0fb00ff0, 380bd35a4adSPunit Agrawal .instr_val = 0x01000090, 381bd35a4adSPunit Agrawal .pstate_mask = COMPAT_PSR_MODE_MASK, 382bd35a4adSPunit Agrawal .pstate_val = COMPAT_PSR_MODE_USR, 383bd35a4adSPunit Agrawal .fn = swp_handler 384bd35a4adSPunit Agrawal }, 385bd35a4adSPunit Agrawal { } 386bd35a4adSPunit Agrawal }; 387bd35a4adSPunit Agrawal 388bd35a4adSPunit Agrawal static struct insn_emulation_ops swp_ops = { 389bd35a4adSPunit Agrawal .name = "swp", 390bd35a4adSPunit Agrawal .status = INSN_OBSOLETE, 391bd35a4adSPunit Agrawal .hooks = swp_hooks, 392bd35a4adSPunit Agrawal .set_hw_mode = NULL, 393bd35a4adSPunit Agrawal }; 394bd35a4adSPunit Agrawal 395c852f320SPunit Agrawal static int cp15barrier_handler(struct pt_regs *regs, u32 instr) 396c852f320SPunit Agrawal { 397c852f320SPunit Agrawal perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); 398c852f320SPunit Agrawal 399c852f320SPunit Agrawal switch (arm_check_condition(instr, regs->pstate)) { 400c852f320SPunit Agrawal case ARM_OPCODE_CONDTEST_PASS: 401c852f320SPunit Agrawal break; 402c852f320SPunit Agrawal case ARM_OPCODE_CONDTEST_FAIL: 403c852f320SPunit Agrawal /* Condition failed - return to next instruction */ 404c852f320SPunit Agrawal goto ret; 405c852f320SPunit Agrawal case ARM_OPCODE_CONDTEST_UNCOND: 406c852f320SPunit Agrawal /* If unconditional encoding - not a barrier instruction */ 407c852f320SPunit Agrawal return -EFAULT; 408c852f320SPunit Agrawal default: 409c852f320SPunit Agrawal return -EINVAL; 410c852f320SPunit Agrawal } 411c852f320SPunit Agrawal 412c852f320SPunit Agrawal switch (aarch32_insn_mcr_extract_crm(instr)) { 413c852f320SPunit Agrawal case 10: 414c852f320SPunit Agrawal /* 415c852f320SPunit Agrawal * dmb - mcr p15, 0, Rt, c7, c10, 5 416c852f320SPunit Agrawal * dsb - mcr p15, 0, Rt, c7, c10, 4 417c852f320SPunit Agrawal */ 418c852f320SPunit Agrawal if (aarch32_insn_mcr_extract_opc2(instr) == 5) 419c852f320SPunit Agrawal dmb(sy); 420c852f320SPunit Agrawal else 421c852f320SPunit Agrawal dsb(sy); 422c852f320SPunit Agrawal break; 423c852f320SPunit Agrawal case 5: 424c852f320SPunit Agrawal /* 425c852f320SPunit Agrawal * isb - mcr p15, 0, Rt, c7, c5, 4 426c852f320SPunit Agrawal * 427c852f320SPunit Agrawal * Taking an exception or returning from one acts as an 428c852f320SPunit Agrawal * instruction barrier. So no explicit barrier needed here. 429c852f320SPunit Agrawal */ 430c852f320SPunit Agrawal break; 431c852f320SPunit Agrawal } 432c852f320SPunit Agrawal 433c852f320SPunit Agrawal ret: 434c852f320SPunit Agrawal pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n", 435c852f320SPunit Agrawal current->comm, (unsigned long)current->pid, regs->pc); 436c852f320SPunit Agrawal 437c852f320SPunit Agrawal regs->pc += 4; 438c852f320SPunit Agrawal return 0; 439c852f320SPunit Agrawal } 440c852f320SPunit Agrawal 441c852f320SPunit Agrawal #define SCTLR_EL1_CP15BEN (1 << 5) 442c852f320SPunit Agrawal 443c852f320SPunit Agrawal static inline void config_sctlr_el1(u32 clear, u32 set) 444c852f320SPunit Agrawal { 445c852f320SPunit Agrawal u32 val; 446c852f320SPunit Agrawal 447c852f320SPunit Agrawal asm volatile("mrs %0, sctlr_el1" : "=r" (val)); 448c852f320SPunit Agrawal val &= ~clear; 449c852f320SPunit Agrawal val |= set; 450c852f320SPunit Agrawal asm volatile("msr sctlr_el1, %0" : : "r" (val)); 451c852f320SPunit Agrawal } 452c852f320SPunit Agrawal 453c852f320SPunit Agrawal static void enable_cp15_ben(void *info) 454c852f320SPunit Agrawal { 455c852f320SPunit Agrawal config_sctlr_el1(0, SCTLR_EL1_CP15BEN); 456c852f320SPunit Agrawal } 457c852f320SPunit Agrawal 458c852f320SPunit Agrawal static void disable_cp15_ben(void *info) 459c852f320SPunit Agrawal { 460c852f320SPunit Agrawal config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); 461c852f320SPunit Agrawal } 462c852f320SPunit Agrawal 463c852f320SPunit Agrawal static int cpu_hotplug_notify(struct notifier_block *b, 464c852f320SPunit Agrawal unsigned long action, void *hcpu) 465c852f320SPunit Agrawal { 466c852f320SPunit Agrawal switch (action) { 467c852f320SPunit Agrawal case CPU_STARTING: 468c852f320SPunit Agrawal case CPU_STARTING_FROZEN: 469c852f320SPunit Agrawal enable_cp15_ben(NULL); 470c852f320SPunit Agrawal return NOTIFY_DONE; 471c852f320SPunit Agrawal case CPU_DYING: 472c852f320SPunit Agrawal case CPU_DYING_FROZEN: 473c852f320SPunit Agrawal disable_cp15_ben(NULL); 474c852f320SPunit Agrawal return NOTIFY_DONE; 475c852f320SPunit Agrawal } 476c852f320SPunit Agrawal 477c852f320SPunit Agrawal return NOTIFY_OK; 478c852f320SPunit Agrawal } 479c852f320SPunit Agrawal 480c852f320SPunit Agrawal static struct notifier_block cpu_hotplug_notifier = { 481c852f320SPunit Agrawal .notifier_call = cpu_hotplug_notify, 482c852f320SPunit Agrawal }; 483c852f320SPunit Agrawal 484c852f320SPunit Agrawal static int cp15_barrier_set_hw_mode(bool enable) 485c852f320SPunit Agrawal { 486c852f320SPunit Agrawal if (enable) { 487c852f320SPunit Agrawal register_cpu_notifier(&cpu_hotplug_notifier); 488c852f320SPunit Agrawal on_each_cpu(enable_cp15_ben, NULL, true); 489c852f320SPunit Agrawal } else { 490c852f320SPunit Agrawal unregister_cpu_notifier(&cpu_hotplug_notifier); 491c852f320SPunit Agrawal on_each_cpu(disable_cp15_ben, NULL, true); 492c852f320SPunit Agrawal } 493c852f320SPunit Agrawal 494c852f320SPunit Agrawal return true; 495c852f320SPunit Agrawal } 496c852f320SPunit Agrawal 497c852f320SPunit Agrawal static struct undef_hook cp15_barrier_hooks[] = { 498c852f320SPunit Agrawal { 499c852f320SPunit Agrawal .instr_mask = 0x0fff0fdf, 500c852f320SPunit Agrawal .instr_val = 0x0e070f9a, 501c852f320SPunit Agrawal .pstate_mask = COMPAT_PSR_MODE_MASK, 502c852f320SPunit Agrawal .pstate_val = COMPAT_PSR_MODE_USR, 503c852f320SPunit Agrawal .fn = cp15barrier_handler, 504c852f320SPunit Agrawal }, 505c852f320SPunit Agrawal { 506c852f320SPunit Agrawal .instr_mask = 0x0fff0fff, 507c852f320SPunit Agrawal .instr_val = 0x0e070f95, 508c852f320SPunit Agrawal .pstate_mask = COMPAT_PSR_MODE_MASK, 509c852f320SPunit Agrawal .pstate_val = COMPAT_PSR_MODE_USR, 510c852f320SPunit Agrawal .fn = cp15barrier_handler, 511c852f320SPunit Agrawal }, 512c852f320SPunit Agrawal { } 513c852f320SPunit Agrawal }; 514c852f320SPunit Agrawal 515c852f320SPunit Agrawal static struct insn_emulation_ops cp15_barrier_ops = { 516c852f320SPunit Agrawal .name = "cp15_barrier", 517c852f320SPunit Agrawal .status = INSN_DEPRECATED, 518c852f320SPunit Agrawal .hooks = cp15_barrier_hooks, 519c852f320SPunit Agrawal .set_hw_mode = cp15_barrier_set_hw_mode, 520c852f320SPunit Agrawal }; 521c852f320SPunit Agrawal 522bd35a4adSPunit Agrawal /* 523587064b6SPunit Agrawal * Invoked as late_initcall, since not needed before init spawned. 524587064b6SPunit Agrawal */ 525587064b6SPunit Agrawal static int __init armv8_deprecated_init(void) 526587064b6SPunit Agrawal { 527bd35a4adSPunit Agrawal if (IS_ENABLED(CONFIG_SWP_EMULATION)) 528bd35a4adSPunit Agrawal register_insn_emulation(&swp_ops); 529bd35a4adSPunit Agrawal 530c852f320SPunit Agrawal if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION)) 531c852f320SPunit Agrawal register_insn_emulation(&cp15_barrier_ops); 532c852f320SPunit Agrawal 533587064b6SPunit Agrawal register_insn_emulation_sysctl(ctl_abi); 534587064b6SPunit Agrawal 535587064b6SPunit Agrawal return 0; 536587064b6SPunit Agrawal } 537587064b6SPunit Agrawal 538587064b6SPunit Agrawal late_initcall(armv8_deprecated_init); 539