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