summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/Makefile3
-rw-r--r--arch/arm/kernel/calls.S6
-rw-r--r--arch/arm/kernel/devtree.c148
-rw-r--r--arch/arm/kernel/entry-armv.S6
-rw-r--r--arch/arm/kernel/entry-common.S2
-rw-r--r--arch/arm/kernel/fiq.c45
-rw-r--r--arch/arm/kernel/fiqasm.S49
-rw-r--r--arch/arm/kernel/head-common.S24
-rw-r--r--arch/arm/kernel/head.S15
-rw-r--r--arch/arm/kernel/kprobes-decode.c777
-rw-r--r--arch/arm/kernel/kprobes.c3
-rw-r--r--arch/arm/kernel/leds.c28
-rw-r--r--arch/arm/kernel/perf_event.c8
-rw-r--r--arch/arm/kernel/ptrace.c356
-rw-r--r--arch/arm/kernel/setup.c103
-rw-r--r--arch/arm/kernel/signal.c90
-rw-r--r--arch/arm/kernel/smp.c15
-rw-r--r--arch/arm/kernel/sys_oabi-compat.c2
-rw-r--r--arch/arm/kernel/time.c35
-rw-r--r--arch/arm/kernel/traps.c5
-rw-r--r--arch/arm/kernel/vmlinux.lds.S2
21 files changed, 1111 insertions, 611 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 8d95446150a..a5b31af5c2b 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -24,7 +24,7 @@ obj-$(CONFIG_OC_ETM) += etm.o
obj-$(CONFIG_ISA_DMA_API) += dma.o
obj-$(CONFIG_ARCH_ACORN) += ecard.o
-obj-$(CONFIG_FIQ) += fiq.o
+obj-$(CONFIG_FIQ) += fiq.o fiqasm.o
obj-$(CONFIG_MODULES) += armksyms.o module.o
obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
@@ -44,6 +44,7 @@ obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_ARM_UNWIND) += unwind.o
obj-$(CONFIG_HAVE_TCM) += tcm.o
+obj-$(CONFIG_OF) += devtree.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o
CFLAGS_swp_emulate.o := -Wa,-march=armv7-a
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 5c26eccef99..80f7896cc01 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -379,6 +379,12 @@
CALL(sys_fanotify_init)
CALL(sys_fanotify_mark)
CALL(sys_prlimit64)
+/* 370 */ CALL(sys_name_to_handle_at)
+ CALL(sys_open_by_handle_at)
+ CALL(sys_clock_adjtime)
+ CALL(sys_syncfs)
+ CALL(sys_sendmmsg)
+/* 375 */ CALL(sys_setns)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
new file mode 100644
index 00000000000..0cdd7b456cb
--- /dev/null
+++ b/arch/arm/kernel/devtree.c
@@ -0,0 +1,148 @@
+/*
+ * linux/arch/arm/kernel/devtree.c
+ *
+ * Copyright (C) 2009 Canonical Ltd. <jeremy.kerr@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+void __init early_init_dt_add_memory_arch(u64 base, u64 size)
+{
+ arm_add_memory(base, size);
+}
+
+void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
+{
+ return alloc_bootmem_align(size, align);
+}
+
+void __init arm_dt_memblock_reserve(void)
+{
+ u64 *reserve_map, base, size;
+
+ if (!initial_boot_params)
+ return;
+
+ /* Reserve the dtb region */
+ memblock_reserve(virt_to_phys(initial_boot_params),
+ be32_to_cpu(initial_boot_params->totalsize));
+
+ /*
+ * Process the reserve map. This will probably overlap the initrd
+ * and dtb locations which are already reserved, but overlaping
+ * doesn't hurt anything
+ */
+ reserve_map = ((void*)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_mem_rsvmap);
+ while (1) {
+ base = be64_to_cpup(reserve_map++);
+ size = be64_to_cpup(reserve_map++);
+ if (!size)
+ break;
+ memblock_reserve(base, size);
+ }
+}
+
+/**
+ * setup_machine_fdt - Machine setup when an dtb was passed to the kernel
+ * @dt_phys: physical address of dt blob
+ *
+ * If a dtb was passed to the kernel in r2, then use it to choose the
+ * correct machine_desc and to setup the system.
+ */
+struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
+{
+ struct boot_param_header *devtree;
+ struct machine_desc *mdesc, *mdesc_best = NULL;
+ unsigned int score, mdesc_score = ~1;
+ unsigned long dt_root;
+ const char *model;
+
+ if (!dt_phys)
+ return NULL;
+
+ devtree = phys_to_virt(dt_phys);
+
+ /* check device tree validity */
+ if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
+ return NULL;
+
+ /* Search the mdescs for the 'best' compatible value match */
+ initial_boot_params = devtree;
+ dt_root = of_get_flat_dt_root();
+ for_each_machine_desc(mdesc) {
+ score = of_flat_dt_match(dt_root, mdesc->dt_compat);
+ if (score > 0 && score < mdesc_score) {
+ mdesc_best = mdesc;
+ mdesc_score = score;
+ }
+ }
+ if (!mdesc_best) {
+ const char *prop;
+ long size;
+
+ early_print("\nError: unrecognized/unsupported "
+ "device tree compatible list:\n[ ");
+
+ prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
+ while (size > 0) {
+ early_print("'%s' ", prop);
+ size -= strlen(prop) + 1;
+ prop += strlen(prop) + 1;
+ }
+ early_print("]\n\n");
+
+ dump_machine_table(); /* does not return */
+ }
+
+ model = of_get_flat_dt_prop(dt_root, "model", NULL);
+ if (!model)
+ model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
+ if (!model)
+ model = "<unknown>";
+ pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);
+
+ /* Retrieve various information from the /chosen node */
+ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
+ /* Initialize {size,address}-cells info */
+ of_scan_flat_dt(early_init_dt_scan_root, NULL);
+ /* Setup memory, calling early_init_dt_add_memory_arch */
+ of_scan_flat_dt(early_init_dt_scan_memory, NULL);
+
+ /* Change machine number to match the mdesc we're using */
+ __machine_arch_type = mdesc_best->nr;
+
+ return mdesc_best;
+}
+
+/**
+ * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
+ *
+ * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
+ * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
+ * supported.
+ */
+unsigned int irq_create_of_mapping(struct device_node *controller,
+ const u32 *intspec, unsigned int intsize)
+{
+ return intspec[0];
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index e8d88567680..90c62cd51ca 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -435,6 +435,10 @@ __irq_usr:
usr_entry
kuser_cmpxchg_check
+#ifdef CONFIG_IRQSOFF_TRACER
+ bl trace_hardirqs_off
+#endif
+
get_thread_info tsk
#ifdef CONFIG_PREEMPT
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
@@ -453,7 +457,7 @@ __irq_usr:
#endif
mov why, #0
- b ret_to_user
+ b ret_to_user_from_irq
UNWIND(.fnend )
ENDPROC(__irq_usr)
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 1e7b04a40a3..b2a27b6b004 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -64,6 +64,7 @@ work_resched:
ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq @ disable interrupts
+ENTRY(ret_to_user_from_irq)
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne work_pending
@@ -75,6 +76,7 @@ no_work_pending:
arch_ret_to_user r1, lr
restore_user_regs fast = 0, offset = 0
+ENDPROC(ret_to_user_from_irq)
ENDPROC(ret_to_user)
/*
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index e72dc34eea1..4c164ece589 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -89,47 +89,6 @@ void set_fiq_handler(void *start, unsigned int length)
flush_icache_range(0x1c, 0x1c + length);
}
-/*
- * Taking an interrupt in FIQ mode is death, so both these functions
- * disable irqs for the duration. Note - these functions are almost
- * entirely coded in assembly.
- */
-void __naked set_fiq_regs(struct pt_regs *regs)
-{
- register unsigned long tmp;
- asm volatile (
- "mov ip, sp\n\
- stmfd sp!, {fp, ip, lr, pc}\n\
- sub fp, ip, #4\n\
- mrs %0, cpsr\n\
- msr cpsr_c, %2 @ select FIQ mode\n\
- mov r0, r0\n\
- ldmia %1, {r8 - r14}\n\
- msr cpsr_c, %0 @ return to SVC mode\n\
- mov r0, r0\n\
- ldmfd sp, {fp, sp, pc}"
- : "=&r" (tmp)
- : "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
-}
-
-void __naked get_fiq_regs(struct pt_regs *regs)
-{
- register unsigned long tmp;
- asm volatile (
- "mov ip, sp\n\
- stmfd sp!, {fp, ip, lr, pc}\n\
- sub fp, ip, #4\n\
- mrs %0, cpsr\n\
- msr cpsr_c, %2 @ select FIQ mode\n\
- mov r0, r0\n\
- stmia %1, {r8 - r14}\n\
- msr cpsr_c, %0 @ return to SVC mode\n\
- mov r0, r0\n\
- ldmfd sp, {fp, sp, pc}"
- : "=&r" (tmp)
- : "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
-}
-
int claim_fiq(struct fiq_handler *f)
{
int ret = 0;
@@ -174,8 +133,8 @@ void disable_fiq(int fiq)
}
EXPORT_SYMBOL(set_fiq_handler);
-EXPORT_SYMBOL(set_fiq_regs);
-EXPORT_SYMBOL(get_fiq_regs);
+EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */
+EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */
EXPORT_SYMBOL(claim_fiq);
EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
diff --git a/arch/arm/kernel/fiqasm.S b/arch/arm/kernel/fiqasm.S
new file mode 100644
index 00000000000..207f9d65201
--- /dev/null
+++ b/arch/arm/kernel/fiqasm.S
@@ -0,0 +1,49 @@
+/*
+ * linux/arch/arm/kernel/fiqasm.S
+ *
+ * Derived from code originally in linux/arch/arm/kernel/fiq.c:
+ *
+ * Copyright (C) 1998 Russell King
+ * Copyright (C) 1998, 1999 Phil Blundell
+ * Copyright (C) 2011, Linaro Limited
+ *
+ * FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
+ *
+ * FIQ support re-written by Russell King to be more generic
+ *
+ * v7/Thumb-2 compatibility modifications by Linaro Limited, 2011.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * Taking an interrupt in FIQ mode is death, so both these functions
+ * disable irqs for the duration.
+ */
+
+ENTRY(__set_fiq_regs)
+ mov r2, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ mrs r1, cpsr
+ msr cpsr_c, r2 @ select FIQ mode
+ mov r0, r0 @ avoid hazard prior to ARMv4
+ ldmia r0!, {r8 - r12}
+ ldr sp, [r0], #4
+ ldr lr, [r0]
+ msr cpsr_c, r1 @ return to SVC mode
+ mov r0, r0 @ avoid hazard prior to ARMv4
+ mov pc, lr
+ENDPROC(__set_fiq_regs)
+
+ENTRY(__get_fiq_regs)
+ mov r2, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ mrs r1, cpsr
+ msr cpsr_c, r2 @ select FIQ mode
+ mov r0, r0 @ avoid hazard prior to ARMv4
+ stmia r0!, {r8 - r12}
+ str sp, [r0], #4
+ str lr, [r0]
+ msr cpsr_c, r1 @ return to SVC mode
+ mov r0, r0 @ avoid hazard prior to ARMv4
+ mov pc, lr
+ENDPROC(__get_fiq_regs)
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index c84b57d27d0..854bd22380d 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -15,6 +15,12 @@
#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define OF_DT_MAGIC 0xd00dfeed
+#else
+#define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */
+#endif
+
/*
* Exception handling. Something went wrong and we can't proceed. We
* ought to tell the user, but since we don't have any guarantee that
@@ -28,20 +34,26 @@
/* Determine validity of the r2 atags pointer. The heuristic requires
* that the pointer be aligned, in the first 16k of physical RAM and
- * that the ATAG_CORE marker is first and present. Future revisions
+ * that the ATAG_CORE marker is first and present. If CONFIG_OF_FLATTREE
+ * is selected, then it will also accept a dtb pointer. Future revisions
* of this function may be more lenient with the physical address and
* may also be able to move the ATAGS block if necessary.
*
* Returns:
- * r2 either valid atags pointer, or zero
+ * r2 either valid atags pointer, valid dtb pointer, or zero
* r5, r6 corrupted
*/
__vet_atags:
tst r2, #0x3 @ aligned?
bne 1f
- ldr r5, [r2, #0] @ is first tag ATAG_CORE?
- cmp r5, #ATAG_CORE_SIZE
+ ldr r5, [r2, #0]
+#ifdef CONFIG_OF_FLATTREE
+ ldr r6, =OF_DT_MAGIC @ is it a DTB?
+ cmp r5, r6
+ beq 2f
+#endif
+ cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE?
cmpne r5, #ATAG_CORE_SIZE_EMPTY
bne 1f
ldr r5, [r2, #4]
@@ -49,7 +61,7 @@ __vet_atags:
cmp r5, r6
bne 1f
- mov pc, lr @ atag pointer is ok
+2: mov pc, lr @ atag/dtb pointer is ok
1: mov r2, #0
mov pc, lr
@@ -61,7 +73,7 @@ ENDPROC(__vet_atags)
*
* r0 = cp#15 control register
* r1 = machine ID
- * r2 = atags pointer
+ * r2 = atags/dtb pointer
* r9 = processor ID
*/
__INIT
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index c9173cfbbc7..278c1b0ebb2 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -59,7 +59,7 @@
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
- * r1 = machine nr, r2 = atags pointer.
+ * r1 = machine nr, r2 = atags or dtb pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
@@ -91,7 +91,7 @@ ENTRY(stext)
#endif
/*
- * r1 = machine no, r2 = atags,
+ * r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
bl __vet_atags
@@ -113,6 +113,7 @@ ENTRY(stext)
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(1f) @ return (PIC) address
+ mov r8, r4 @ set TTBR1 to swapper_pg_dir
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( mov pc, r12 )
@@ -302,8 +303,10 @@ ENTRY(secondary_startup)
*/
adr r4, __secondary_data
ldmia r4, {r5, r7, r12} @ address to jump to after
- sub r4, r4, r5 @ mmu has been enabled
- ldr r4, [r7, r4] @ get secondary_data.pgdir
+ sub lr, r4, r5 @ mmu has been enabled
+ ldr r4, [r7, lr] @ get secondary_data.pgdir
+ add r7, r7, #4
+ ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir
adr lr, BSYM(__enable_mmu) @ return address
mov r13, r12 @ __secondary_switched address
ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor
@@ -339,7 +342,7 @@ __secondary_data:
*
* r0 = cp#15 control register
* r1 = machine ID
- * r2 = atags pointer
+ * r2 = atags or dtb pointer
* r4 = page table pointer
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
@@ -376,7 +379,7 @@ ENDPROC(__enable_mmu)
*
* r0 = cp#15 control register
* r1 = machine ID
- * r2 = atags pointer
+ * r2 = atags or dtb pointer
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
*
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c
index 23891317dc4..15eeff6aea0 100644
--- a/arch/arm/kernel/kprobes-decode.c
+++ b/arch/arm/kernel/kprobes-decode.c
@@ -34,9 +34,6 @@
*
* *) If the PC is written to by the instruction, the
* instruction must be fully simulated in software.
- * If it is a conditional instruction, the handler
- * will use insn[0] to copy its condition code to
- * set r0 to 1 and insn[1] to "mov pc, lr" to return.
*
* *) Otherwise, a modified form of the instruction is
* directly executed. Its handler calls the
@@ -68,13 +65,17 @@
#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+#define is_r15(insn, bitpos) (((insn) & (0xf << bitpos)) == (0xf << bitpos))
+
+/*
+ * Test if load/store instructions writeback the address register.
+ * if P (bit 24) == 0 or W (bit 21) == 1
+ */
+#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000)
+
#define PSR_fs (PSR_f|PSR_s)
#define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */
-#define SET_R0_TRUE_INSTRUCTION 0xe3a00001 /* mov r0, #1 */
-
-#define truecc_insn(insn) (((insn) & 0xf0000000) | \
- (SET_R0_TRUE_INSTRUCTION & 0x0fffffff))
typedef long (insn_0arg_fn_t)(void);
typedef long (insn_1arg_fn_t)(long);
@@ -419,14 +420,10 @@ insnslot_llret_4arg_rwflags(long r0, long r1, long r2, long r3, long *cpsr,
static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
int disp = branch_displacement(insn);
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
if (insn & (1 << 24))
regs->ARM_lr = iaddr + 4;
@@ -446,14 +443,10 @@ static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
int rm = insn & 0xf;
long rmv = regs->uregs[rm];
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
if (insn & (1 << 5))
regs->ARM_lr = (long)p->addr + 4;
@@ -463,9 +456,16 @@ static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
regs->ARM_cpsr |= PSR_T_BIT;
}
+static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
+{
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ unsigned long mask = 0xf8ff03df; /* Mask out execution state */
+ regs->uregs[rd] = regs->ARM_cpsr & mask;
+}
+
static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
int rn = (insn >> 16) & 0xf;
int lbit = insn & (1 << 20);
@@ -476,9 +476,6 @@ static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
int reg_bit_vector;
int reg_count;
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
reg_count = 0;
reg_bit_vector = insn & 0xffff;
while (reg_bit_vector) {
@@ -510,11 +507,6 @@ static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
-
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
regs->ARM_pc = (long)p->addr + str_pc_offset;
simulate_ldm1stm1(p, regs);
regs->ARM_pc = (long)p->addr + 4;
@@ -525,24 +517,16 @@ static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
regs->uregs[12] = regs->uregs[13];
}
-static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs)
-{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
- long rnv = regs->uregs[rn];
-
- /* Save Rn in case of writeback. */
- regs->uregs[rn] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
-}
-
static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf; /* rm may be invalid, don't care. */
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
/* Not following the C calling convention here, so need asm(). */
__asm__ __volatile__ (
@@ -554,29 +538,36 @@ static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
"str r0, %[rn] \n\t" /* in case of writeback */
"str r2, %[rd0] \n\t"
"str r3, %[rd1] \n\t"
- : [rn] "+m" (regs->uregs[rn]),
+ : [rn] "+m" (rnv),
[rd0] "=m" (regs->uregs[rd]),
[rd1] "=m" (regs->uregs[rd+1])
- : [rm] "m" (regs->uregs[rm]),
+ : [rm] "m" (rmv),
[cpsr] "r" (regs->ARM_cpsr),
[i_fn] "r" (i_fn)
: "r0", "r1", "r2", "r3", "lr", "cc"
);
+ if (is_writeback(insn))
+ regs->uregs[rn] = rnv;
}
static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs)
{
insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
- long rnv = regs->uregs[rn];
- long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
+ /* rm/rmv may be invalid, don't care. */
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rnv_wb;
- regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
+ rnv_wb = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
regs->uregs[rd+1],
regs->ARM_cpsr, i_fn);
+ if (is_writeback(insn))
+ regs->uregs[rn] = rnv_wb;
}
static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
@@ -630,31 +621,6 @@ static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs)
regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */
}
-static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)
-{
- insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- union reg_pair fnr;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
-
- fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn);
- regs->uregs[rn] = fnr.r0;
- regs->uregs[rd] = fnr.r1;
-}
-
-static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs)
-{
- insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- long rnv = regs->uregs[rn];
- long rdv = regs->uregs[rd];
-
- insnslot_2arg_rflags(rnv, rdv, regs->ARM_cpsr, i_fn);
-}
-
static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
@@ -688,32 +654,32 @@ static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs)
insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
}
-static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes emulate_nop(struct kprobe *p, struct pt_regs *regs)
{
- insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
-
- regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
}
-static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes
+emulate_rd12_modify(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int ird = (insn >> 12) & 0xf;
+ int rd = (insn >> 12) & 0xf;
+ long rdv = regs->uregs[rd];
- insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn);
+ regs->uregs[rd] = insnslot_1arg_rflags(rdv, regs->ARM_cpsr, i_fn);
}
-static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes
+emulate_rd12rn0_modify(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
+ int rd = (insn >> 12) & 0xf;
+ int rn = insn & 0xf;
+ long rdv = regs->uregs[rd];
long rnv = regs->uregs[rn];
- insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
+ regs->uregs[rd] = insnslot_2arg_rflags(rdv, rnv, regs->ARM_cpsr, i_fn);
}
static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs)
@@ -819,6 +785,17 @@ emulate_alu_imm_rwflags(struct kprobe *p, struct pt_regs *regs)
}
static void __kprobes
+emulate_alu_tests_imm(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
+
+ insnslot_1arg_rwflags(rnv, &regs->ARM_cpsr, i_fn);
+}
+
+static void __kprobes
emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs)
{
insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
@@ -854,14 +831,34 @@ emulate_alu_rwflags(struct kprobe *p, struct pt_regs *regs)
insnslot_3arg_rwflags(rnv, rmv, rsv, &regs->ARM_cpsr, i_fn);
}
+static void __kprobes
+emulate_alu_tests(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
+ int rn = (insn >> 16) & 0xf;
+ int rs = (insn >> 8) & 0xf; /* rs/rsv may be invalid, don't care. */
+ int rm = insn & 0xf;
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rsv = regs->uregs[rs];
+
+ insnslot_3arg_rwflags(rnv, rmv, rsv, &regs->ARM_cpsr, i_fn);
+}
+
static enum kprobe_insn __kprobes
prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
- int ibit = (insn & (1 << 26)) ? 25 : 22;
+ int not_imm = (insn & (1 << 26)) ? (insn & (1 << 25))
+ : (~insn & (1 << 22));
+
+ if (is_writeback(insn) && is_r15(insn, 16))
+ return INSN_REJECTED; /* Writeback to PC */
insn &= 0xfff00fff;
insn |= 0x00001000; /* Rn = r0, Rd = r1 */
- if (insn & (1 << ibit)) {
+ if (not_imm) {
insn &= ~0xf;
insn |= 2; /* Rm = r2 */
}
@@ -871,20 +868,40 @@ prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi)
}
static enum kprobe_insn __kprobes
-prep_emulate_rd12rm0(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+prep_emulate_rd12_modify(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
- insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
+ insn &= 0xffff0fff; /* Rd = r0 */
asi->insn[0] = insn;
- asi->insn_handler = emulate_rd12rm0;
+ asi->insn_handler = emulate_rd12_modify;
return INSN_GOOD;
}
static enum kprobe_insn __kprobes
-prep_emulate_rd12(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+prep_emulate_rd12rn0_modify(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
{
- insn &= 0xffff0fff; /* Rd = r0 */
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
+ insn &= 0xffff0ff0; /* Rd = r0 */
+ insn |= 0x00000001; /* Rn = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd12rn0_modify;
+ return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+prep_emulate_rd12rm0(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
+ insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
asi->insn[0] = insn;
- asi->insn_handler = emulate_rd12;
+ asi->insn_handler = emulate_rd12rm0;
return INSN_GOOD;
}
@@ -892,6 +909,9 @@ static enum kprobe_insn __kprobes
prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */
insn |= 0x00000001; /* Rm = r1 */
asi->insn[0] = insn;
@@ -903,6 +923,9 @@ static enum kprobe_insn __kprobes
prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 16))
+ return INSN_REJECTED; /* Rd is PC */
+
insn &= 0xfff0f0f0; /* Rd = r0, Rs = r0 */
insn |= 0x00000001; /* Rm = r1 */
asi->insn[0] = insn;
@@ -914,6 +937,9 @@ static enum kprobe_insn __kprobes
prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 16))
+ return INSN_REJECTED; /* Rd is PC */
+
insn &= 0xfff000f0; /* Rd = r0, Rn = r0 */
insn |= 0x00000102; /* Rs = r1, Rm = r2 */
asi->insn[0] = insn;
@@ -925,6 +951,9 @@ static enum kprobe_insn __kprobes
prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 16) || is_r15(insn, 12))
+ return INSN_REJECTED; /* RdHi or RdLo is PC */
+
insn &= 0xfff000f0; /* RdHi = r0, RdLo = r1 */
insn |= 0x00001203; /* Rs = r2, Rm = r3 */
asi->insn[0] = insn;
@@ -945,20 +974,13 @@ prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn,
static enum kprobe_insn __kprobes
space_1111(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
- /* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx */
- /* RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx */
- /* SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx */
- if ((insn & 0xfff30020) == 0xf1020000 ||
- (insn & 0xfe500f00) == 0xf8100a00 ||
- (insn & 0xfe5f0f00) == 0xf84d0500)
- return INSN_REJECTED;
-
- /* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */
- if ((insn & 0xfd700000) == 0xf4500000) {
- insn &= 0xfff0ffff; /* Rn = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = emulate_rn16;
- return INSN_GOOD;
+ /* memory hint : 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx : */
+ /* PLDI : 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx : */
+ /* PLDW : 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx : */
+ /* PLD : 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx : */
+ if ((insn & 0xfe300000) == 0xf4100000) {
+ asi->insn_handler = emulate_nop;
+ return INSN_GOOD_NO_SLOT;
}
/* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */
@@ -967,41 +989,22 @@ space_1111(kprobe_opcode_t insn, struct arch_specific_insn *asi)
return INSN_GOOD_NO_SLOT;
}
- /* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
- /* CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
- if ((insn & 0xffff00f0) == 0xf1010000 ||
- (insn & 0xff000010) == 0xfe000000) {
- asi->insn[0] = insn;
- asi->insn_handler = emulate_none;
- return INSN_GOOD;
- }
+ /* CPS : 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
+ /* SETEND: 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
+ /* SRS : 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
+ /* RFE : 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
+
+ /* Coprocessor instructions... */
/* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */
/* MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */
- if ((insn & 0xffe00000) == 0xfc400000) {
- insn &= 0xfff00fff; /* Rn = r0 */
- insn |= 0x00001000; /* Rd = r1 */
- asi->insn[0] = insn;
- asi->insn_handler =
- (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
- return INSN_GOOD;
- }
+ /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ /* CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
- /* STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
- if ((insn & 0xfe000000) == 0xfc000000) {
- insn &= 0xfff0ffff; /* Rn = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = emulate_ldcstc;
- return INSN_GOOD;
- }
-
- /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
- /* MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- insn &= 0xffff0fff; /* Rd = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
- return INSN_GOOD;
+ return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
@@ -1010,19 +1013,18 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */
if ((insn & 0x0f900010) == 0x01000000) {
- /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
- /* MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
- if ((insn & 0x0ff000f0) == 0x01200020 ||
- (insn & 0x0fb000f0) == 0x01200000)
- return INSN_REJECTED;
-
- /* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx */
- if ((insn & 0x0fb00010) == 0x01000000)
- return prep_emulate_rd12(insn, asi);
+ /* MRS cpsr : cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
+ if ((insn & 0x0ff000f0) == 0x01000000) {
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+ asi->insn_handler = simulate_mrs;
+ return INSN_GOOD_NO_SLOT;
+ }
/* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
if ((insn & 0x0ff00090) == 0x01400080)
- return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn,
+ asi);
/* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
/* SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
@@ -1031,24 +1033,29 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
return prep_emulate_rd16rs8rm0_wflags(insn, asi);
/* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx : Q */
- /* SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 0x00 xxxx : Q */
- return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ /* SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx : Q */
+ if ((insn & 0x0ff00090) == 0x01000080 ||
+ (insn & 0x0ff000b0) == 0x01200080)
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
+ /* MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
+ /* MRS spsr : cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */
+ /* Other instruction encodings aren't yet defined */
+ return INSN_REJECTED;
}
/* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */
else if ((insn & 0x0f900090) == 0x01000010) {
- /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
- if ((insn & 0xfff000f0) == 0xe1200070)
- return INSN_REJECTED;
-
/* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
/* BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
if ((insn & 0x0ff000d0) == 0x01200010) {
- asi->insn[0] = truecc_insn(insn);
+ if ((insn & 0x0ff000ff) == 0x0120003f)
+ return INSN_REJECTED; /* BLX pc */
asi->insn_handler = simulate_blx2bx;
- return INSN_GOOD;
+ return INSN_GOOD_NO_SLOT;
}
/* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
@@ -1059,17 +1066,27 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* QSUB : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q */
/* QDADD : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q */
/* QDSUB : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q */
- return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ if ((insn & 0x0f9000f0) == 0x01000050)
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+
+ /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+ /* SMC : cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */
+
+ /* Other instruction encodings aren't yet defined */
+ return INSN_REJECTED;
}
/* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */
- else if ((insn & 0x0f000090) == 0x00000090) {
+ else if ((insn & 0x0f0000f0) == 0x00000090) {
/* MUL : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx : */
/* MULS : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc */
/* MLA : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx : */
/* MLAS : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc */
/* UMAAL : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx : */
+ /* undef : cccc 0000 0101 xxxx xxxx xxxx 1001 xxxx : */
+ /* MLS : cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx : */
+ /* undef : cccc 0000 0111 xxxx xxxx xxxx 1001 xxxx : */
/* UMULL : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx : */
/* UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc */
/* UMLAL : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx : */
@@ -1078,13 +1095,15 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc */
/* SMLAL : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx : */
/* SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc */
- if ((insn & 0x0fe000f0) == 0x00000090) {
- return prep_emulate_rd16rs8rm0_wflags(insn, asi);
- } else if ((insn & 0x0fe000f0) == 0x00200090) {
- return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
- } else {
- return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
- }
+ if ((insn & 0x00d00000) == 0x00500000)
+ return INSN_REJECTED;
+ else if ((insn & 0x00e00000) == 0x00000000)
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ else if ((insn & 0x00a00000) == 0x00200000)
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ else
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn,
+ asi);
}
/* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */
@@ -1092,23 +1111,45 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx */
/* SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx */
- /* LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx */
- /* STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx */
+ /* ??? : cccc 0001 0x01 xxxx xxxx xxxx 1001 xxxx */
+ /* ??? : cccc 0001 0x10 xxxx xxxx xxxx 1001 xxxx */
+ /* ??? : cccc 0001 0x11 xxxx xxxx xxxx 1001 xxxx */
/* STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx */
/* LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx */
+ /* STREXD: cccc 0001 1010 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREXD: cccc 0001 1011 xxxx xxxx xxxx 1001 xxxx */
+ /* STREXB: cccc 0001 1100 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREXB: cccc 0001 1101 xxxx xxxx xxxx 1001 xxxx */
+ /* STREXH: cccc 0001 1110 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREXH: cccc 0001 1111 xxxx xxxx xxxx 1001 xxxx */
+
+ /* LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx */
+ /* STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx */
/* LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx */
/* STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx */
/* LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx */
/* LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx */
- if ((insn & 0x0fb000f0) == 0x01000090) {
- /* SWP/SWPB */
- return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ if ((insn & 0x0f0000f0) == 0x01000090) {
+ if ((insn & 0x0fb000f0) == 0x01000090) {
+ /* SWP/SWPB */
+ return prep_emulate_rd12rn16rm0_wflags(insn,
+ asi);
+ } else {
+ /* STREX/LDREX variants and unallocaed space */
+ return INSN_REJECTED;
+ }
+
} else if ((insn & 0x0e1000d0) == 0x00000d0) {
/* STRD/LDRD */
+ if ((insn & 0x0000e000) == 0x0000e000)
+ return INSN_REJECTED; /* Rd is LR or PC */
+ if (is_writeback(insn) && is_r15(insn, 16))
+ return INSN_REJECTED; /* Writeback to PC */
+
insn &= 0xfff00fff;
insn |= 0x00002000; /* Rn = r0, Rd = r2 */
- if (insn & (1 << 22)) {
- /* I bit */
+ if (!(insn & (1 << 22))) {
+ /* Register index */
insn &= ~0xf;
insn |= 1; /* Rm = r1 */
}
@@ -1118,6 +1159,9 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
return INSN_GOOD;
}
+ /* LDRH/STRH/LDRSB/LDRSH */
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
return prep_emulate_ldr_str(insn, asi);
}
@@ -1125,7 +1169,7 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/*
* ALU op with S bit and Rd == 15 :
- * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
+ * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
*/
if ((insn & 0x0e10f000) == 0x0010f000)
return INSN_REJECTED;
@@ -1154,22 +1198,61 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
insn |= 0x00000200; /* Rs = r2 */
}
asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
+
+ if ((insn & 0x0f900000) == 0x01100000) {
+ /*
+ * TST : cccc 0001 0001 xxxx xxxx xxxx xxxx xxxx
+ * TEQ : cccc 0001 0011 xxxx xxxx xxxx xxxx xxxx
+ * CMP : cccc 0001 0101 xxxx xxxx xxxx xxxx xxxx
+ * CMN : cccc 0001 0111 xxxx xxxx xxxx xxxx xxxx
+ */
+ asi->insn_handler = emulate_alu_tests;
+ } else {
+ /* ALU ops which write to Rd */
+ asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
emulate_alu_rwflags : emulate_alu_rflags;
+ }
return INSN_GOOD;
}
static enum kprobe_insn __kprobes
space_cccc_001x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
+ /* MOVW : cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
+ /* MOVT : cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
+ if ((insn & 0x0fb00000) == 0x03000000)
+ return prep_emulate_rd12_modify(insn, asi);
+
+ /* hints : cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
+ if ((insn & 0x0fff0000) == 0x03200000) {
+ unsigned op2 = insn & 0x000000ff;
+ if (op2 == 0x01 || op2 == 0x04) {
+ /* YIELD : cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
+ /* SEV : cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_none;
+ return INSN_GOOD;
+ } else if (op2 <= 0x03) {
+ /* NOP : cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
+ /* WFE : cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
+ /* WFI : cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
+ /*
+ * We make WFE and WFI true NOPs to avoid stalls due
+ * to missing events whilst processing the probe.
+ */
+ asi->insn_handler = emulate_nop;
+ return INSN_GOOD_NO_SLOT;
+ }
+ /* For DBG and unallocated hints it's safest to reject them */
+ return INSN_REJECTED;
+ }
+
/*
* MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx
- * Undef : cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx
* ALU op with S bit and Rd == 15 :
* cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx
*/
if ((insn & 0x0fb00000) == 0x03200000 || /* MSR */
- (insn & 0x0ff00000) == 0x03400000 || /* Undef */
(insn & 0x0e10f000) == 0x0210f000) /* ALU s-bit, R15 */
return INSN_REJECTED;
@@ -1180,10 +1263,22 @@ space_cccc_001x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
* *S (bit 20) updates condition codes
* ADC/SBC/RSC reads the C flag
*/
- insn &= 0xffff0fff; /* Rd = r0 */
+ insn &= 0xfff00fff; /* Rn = r0 and Rd = r0 */
asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
+
+ if ((insn & 0x0f900000) == 0x03100000) {
+ /*
+ * TST : cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx
+ * TEQ : cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx
+ * CMP : cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx
+ * CMN : cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx
+ */
+ asi->insn_handler = emulate_alu_tests_imm;
+ } else {
+ /* ALU ops which write to Rd */
+ asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
emulate_alu_imm_rwflags : emulate_alu_imm_rflags;
+ }
return INSN_GOOD;
}
@@ -1192,6 +1287,8 @@ space_cccc_0110__1(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */
if ((insn & 0x0ff000f0) == 0x068000b0) {
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */
insn |= 0x00000001; /* Rm = r1 */
asi->insn[0] = insn;
@@ -1205,6 +1302,8 @@ space_cccc_0110__1(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q */
if ((insn & 0x0fa00030) == 0x06a00010 ||
(insn & 0x0fb000f0) == 0x06a00030) {
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
asi->insn[0] = insn;
asi->insn_handler = emulate_sat;
@@ -1213,57 +1312,101 @@ space_cccc_0110__1(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* REV : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
/* REV16 : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
+ /* RBIT : cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
/* REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
if ((insn & 0x0ff00070) == 0x06b00030 ||
- (insn & 0x0ff000f0) == 0x06f000b0)
+ (insn & 0x0ff00070) == 0x06f00030)
return prep_emulate_rd12rm0(insn, asi);
+ /* ??? : cccc 0110 0000 xxxx xxxx xxxx xxx1 xxxx : */
/* SADD16 : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE */
/* SADDSUBX : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE */
/* SSUBADDX : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE */
/* SSUB16 : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE */
/* SADD8 : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE */
+ /* ??? : cccc 0110 0001 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0001 xxxx xxxx xxxx 1101 xxxx : */
/* SSUB8 : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE */
/* QADD16 : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx : */
/* QADDSUBX : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx : */
/* QSUBADDX : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx : */
/* QSUB16 : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx : */
/* QADD8 : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0010 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0010 xxxx xxxx xxxx 1101 xxxx : */
/* QSUB8 : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx : */
/* SHADD16 : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx : */
/* SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx : */
/* SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx : */
/* SHSUB16 : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx : */
/* SHADD8 : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0011 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0011 xxxx xxxx xxxx 1101 xxxx : */
/* SHSUB8 : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx : */
+ /* ??? : cccc 0110 0100 xxxx xxxx xxxx xxx1 xxxx : */
/* UADD16 : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE */
/* UADDSUBX : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE */
/* USUBADDX : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE */
/* USUB16 : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE */
/* UADD8 : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE */
+ /* ??? : cccc 0110 0101 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0101 xxxx xxxx xxxx 1101 xxxx : */
/* USUB8 : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE */
/* UQADD16 : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx : */
/* UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx : */
/* UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx : */
/* UQSUB16 : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx : */
/* UQADD8 : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0110 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0110 xxxx xxxx xxxx 1101 xxxx : */
/* UQSUB8 : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx : */
/* UHADD16 : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx : */
/* UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx : */
/* UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx : */
/* UHSUB16 : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx : */
/* UHADD8 : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0111 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0111 xxxx xxxx xxxx 1101 xxxx : */
/* UHSUB8 : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx : */
+ if ((insn & 0x0f800010) == 0x06000010) {
+ if ((insn & 0x00300000) == 0x00000000 ||
+ (insn & 0x000000e0) == 0x000000a0 ||
+ (insn & 0x000000e0) == 0x000000c0)
+ return INSN_REJECTED; /* Unallocated space */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ }
+
/* PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx : */
/* PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx : */
+ if ((insn & 0x0ff00030) == 0x06800010)
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+
/* SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx : */
- /* SXTB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTB16 : cccc 0110 1000 1111 xxxx xxxx 0111 xxxx : */
+ /* ??? : cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx : */
/* SXTAB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTB : cccc 0110 1010 1111 xxxx xxxx 0111 xxxx : */
/* SXTAH : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTH : cccc 0110 1011 1111 xxxx xxxx 0111 xxxx : */
/* UXTAB16 : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx : */
+ /* UXTB16 : cccc 0110 1100 1111 xxxx xxxx 0111 xxxx : */
+ /* ??? : cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx : */
/* UXTAB : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx : */
+ /* UXTB : cccc 0110 1110 1111 xxxx xxxx 0111 xxxx : */
/* UXTAH : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx : */
- return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ /* UXTH : cccc 0110 1111 1111 xxxx xxxx 0111 xxxx : */
+ if ((insn & 0x0f8000f0) == 0x06800070) {
+ if ((insn & 0x00300000) == 0x00100000)
+ return INSN_REJECTED; /* Unallocated space */
+
+ if ((insn & 0x000f0000) == 0x000f0000)
+ return prep_emulate_rd12rm0(insn, asi);
+ else
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ }
+
+ /* Other instruction encodings aren't yet defined */
+ return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
@@ -1273,29 +1416,49 @@ space_cccc_0111__1(kprobe_opcode_t insn, struct arch_specific_insn *asi)
if ((insn & 0x0ff000f0) == 0x03f000f0)
return INSN_REJECTED;
- /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
- /* USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
- if ((insn & 0x0ff000f0) == 0x07800010)
- return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
-
/* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
/* SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
if ((insn & 0x0ff00090) == 0x07400010)
return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
/* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q */
+ /* SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q */
/* SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q */
+ /* SMUSD : cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx : */
/* SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx : */
- /* SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : */
+ /* SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : */
+ /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx : */
+ /* USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx : */
if ((insn & 0x0ff00090) == 0x07000010 ||
(insn & 0x0ff000d0) == 0x07500010 ||
- (insn & 0x0ff000d0) == 0x075000d0)
+ (insn & 0x0ff000f0) == 0x07800010) {
+
+ if ((insn & 0x0000f000) == 0x0000f000)
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ else
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ }
+
+ /* SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : */
+ if ((insn & 0x0ff000d0) == 0x075000d0)
return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
- /* SMUSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx : */
- /* SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q */
- /* SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : */
- return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ /* SBFX : cccc 0111 101x xxxx xxxx xxxx x101 xxxx : */
+ /* UBFX : cccc 0111 111x xxxx xxxx xxxx x101 xxxx : */
+ if ((insn & 0x0fa00070) == 0x07a00050)
+ return prep_emulate_rd12rm0(insn, asi);
+
+ /* BFI : cccc 0111 110x xxxx xxxx xxxx x001 xxxx : */
+ /* BFC : cccc 0111 110x xxxx xxxx xxxx x001 1111 : */
+ if ((insn & 0x0fe00070) == 0x07c00010) {
+
+ if ((insn & 0x0000000f) == 0x0000000f)
+ return prep_emulate_rd12_modify(insn, asi);
+ else
+ return prep_emulate_rd12rn0_modify(insn, asi);
+ }
+
+ return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
@@ -1309,6 +1472,10 @@ space_cccc_01xx(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* STRB : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx */
/* STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */
/* STRT : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */
+
+ if ((insn & 0x00500000) == 0x00500000 && is_r15(insn, 12))
+ return INSN_REJECTED; /* LDRB into PC */
+
return prep_emulate_ldr_str(insn, asi);
}
@@ -1323,10 +1490,9 @@ space_cccc_100x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
/* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
/* STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
- asi->insn[0] = truecc_insn(insn);
asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */
simulate_stm1_pc : simulate_ldm1stm1;
- return INSN_GOOD;
+ return INSN_GOOD_NO_SLOT;
}
static enum kprobe_insn __kprobes
@@ -1334,58 +1500,117 @@ space_cccc_101x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/* B : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
/* BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
- asi->insn[0] = truecc_insn(insn);
asi->insn_handler = simulate_bbl;
- return INSN_GOOD;
+ return INSN_GOOD_NO_SLOT;
}
static enum kprobe_insn __kprobes
-space_cccc_1100_010x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+space_cccc_11xx(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
+ /* Coprocessor instructions... */
/* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */
/* MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */
- insn &= 0xfff00fff;
- insn |= 0x00001000; /* Rn = r0, Rd = r1 */
- asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
- return INSN_GOOD;
+ /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
+
+ /* SVC : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
+
+ return INSN_REJECTED;
}
-static enum kprobe_insn __kprobes
-space_cccc_110x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static unsigned long __kprobes __check_eq(unsigned long cpsr)
{
- /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
- /* STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
- insn &= 0xfff0ffff; /* Rn = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = emulate_ldcstc;
- return INSN_GOOD;
+ return cpsr & PSR_Z_BIT;
}
-static enum kprobe_insn __kprobes
-space_cccc_111x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static unsigned long __kprobes __check_ne(unsigned long cpsr)
{
- /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
- /* SWI : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
- if ((insn & 0xfff000f0) == 0xe1200070 ||
- (insn & 0x0f000000) == 0x0f000000)
- return INSN_REJECTED;
+ return (~cpsr) & PSR_Z_BIT;
+}
- /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
- if ((insn & 0x0f000010) == 0x0e000000) {
- asi->insn[0] = insn;
- asi->insn_handler = emulate_none;
- return INSN_GOOD;
- }
+static unsigned long __kprobes __check_cs(unsigned long cpsr)
+{
+ return cpsr & PSR_C_BIT;
+}
- /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
- /* MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- insn &= 0xffff0fff; /* Rd = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
- return INSN_GOOD;
+static unsigned long __kprobes __check_cc(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_mi(unsigned long cpsr)
+{
+ return cpsr & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_pl(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_vs(unsigned long cpsr)
+{
+ return cpsr & PSR_V_BIT;
+}
+
+static unsigned long __kprobes __check_vc(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_V_BIT;
+}
+
+static unsigned long __kprobes __check_hi(unsigned long cpsr)
+{
+ cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return cpsr & PSR_C_BIT;
}
+static unsigned long __kprobes __check_ls(unsigned long cpsr)
+{
+ cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return (~cpsr) & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_ge(unsigned long cpsr)
+{
+ cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return (~cpsr) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_lt(unsigned long cpsr)
+{
+ cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return cpsr & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_gt(unsigned long cpsr)
+{
+ unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
+ return (~temp) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_le(unsigned long cpsr)
+{
+ unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
+ return temp & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_al(unsigned long cpsr)
+{
+ return true;
+}
+
+static kprobe_check_cc * const condition_checks[16] = {
+ &__check_eq, &__check_ne, &__check_cs, &__check_cc,
+ &__check_mi, &__check_pl, &__check_vs, &__check_vc,
+ &__check_hi, &__check_ls, &__check_ge, &__check_lt,
+ &__check_gt, &__check_le, &__check_al, &__check_al
+};
+
/* Return:
* INSN_REJECTED If instruction is one not allowed to kprobe,
* INSN_GOOD If instruction is supported and uses instruction slot,
@@ -1401,133 +1626,45 @@ space_cccc_111x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
enum kprobe_insn __kprobes
arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
+ asi->insn_check_cc = condition_checks[insn>>28];
asi->insn[1] = KPROBE_RETURN_INSTRUCTION;
- if ((insn & 0xf0000000) == 0xf0000000) {
+ if ((insn & 0xf0000000) == 0xf0000000)
return space_1111(insn, asi);
- } else if ((insn & 0x0e000000) == 0x00000000) {
+ else if ((insn & 0x0e000000) == 0x00000000)
return space_cccc_000x(insn, asi);
- } else if ((insn & 0x0e000000) == 0x02000000) {
+ else if ((insn & 0x0e000000) == 0x02000000)
return space_cccc_001x(insn, asi);
- } else if ((insn & 0x0f000010) == 0x06000010) {
+ else if ((insn & 0x0f000010) == 0x06000010)
return space_cccc_0110__1(insn, asi);
- } else if ((insn & 0x0f000010) == 0x07000010) {
+ else if ((insn & 0x0f000010) == 0x07000010)
return space_cccc_0111__1(insn, asi);
- } else if ((insn & 0x0c000000) == 0x04000000) {
+ else if ((insn & 0x0c000000) == 0x04000000)
return space_cccc_01xx(insn, asi);
- } else if ((insn & 0x0e000000) == 0x08000000) {
+ else if ((insn & 0x0e000000) == 0x08000000)
return space_cccc_100x(insn, asi);
- } else if ((insn & 0x0e000000) == 0x0a000000) {
+ else if ((insn & 0x0e000000) == 0x0a000000)
return space_cccc_101x(insn, asi);
- } else if ((insn & 0x0fe00000) == 0x0c400000) {
-
- return space_cccc_1100_010x(insn, asi);
-
- } else if ((insn & 0x0e000000) == 0x0c000000) {
-
- return space_cccc_110x(insn, asi);
-
- }
-
- return space_cccc_111x(insn, asi);
+ return space_cccc_11xx(insn, asi);
}
void __init arm_kprobe_decode_init(void)
{
find_str_pc_offset();
}
-
-
-/*
- * All ARM instructions listed below.
- *
- * Instructions and their general purpose registers are given.
- * If a particular register may not use R15, it is prefixed with a "!".
- * If marked with a "*" means the value returned by reading R15
- * is implementation defined.
- *
- * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ
- * TST: Rd, Rn, Rm, !Rs
- * BX: Rm
- * BLX(2): !Rm
- * BX: Rm (R15 legal, but discouraged)
- * BXJ: !Rm,
- * CLZ: !Rd, !Rm
- * CPY: Rd, Rm
- * LDC/2,STC/2 immediate offset & unindex: Rn
- * LDC/2,STC/2 immediate pre/post-indexed: !Rn
- * LDM(1/3): !Rn, register_list
- * LDM(2): !Rn, !register_list
- * LDR,STR,PLD immediate offset: Rd, Rn
- * LDR,STR,PLD register offset: Rd, Rn, !Rm
- * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm
- * LDR,STR immediate pre/post-indexed: Rd, !Rn
- * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm
- * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm
- * LDRB,STRB immediate offset: !Rd, Rn
- * LDRB,STRB register offset: !Rd, Rn, !Rm
- * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm
- * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn
- * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn
- * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn
- * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm
- * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn
- * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm
- * LDREX: !Rd, !Rn
- * MCR/2: !Rd
- * MCRR/2,MRRC/2: !Rd, !Rn
- * MLA: !Rd, !Rn, !Rm, !Rs
- * MOV: Rd
- * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register)
- * MRS,MSR: !Rd
- * MUL: !Rd, !Rm, !Rs
- * PKH{BT,TB}: !Rd, !Rn, !Rm
- * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn
- * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn
- * REV/16/SH: !Rd, !Rm
- * RFE: !Rn
- * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm
- * SEL: !Rd, !Rn, !Rm
- * SMLA<x><y>,SMLA{D,W<y>},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs
- * SMLAL<x><y>,SMLA{D,LD},SMLSLD,SMMULL,SMULW<y>: !RdHi, !RdLo, !Rm, !Rs
- * SMMUL,SMUAD,SMUL<x><y>,SMUSD: !Rd, !Rm, !Rs
- * SSAT/16: !Rd, !Rm
- * STM(1/2): !Rn, register_list* (R15 in reg list not recommended)
- * STRT immediate pre/post-indexed: Rd*, !Rn
- * STRT register pre/post-indexed: Rd*, !Rn, !Rm
- * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm
- * STREX: !Rd, !Rn, !Rm
- * SWP/B: !Rd, !Rn, !Rm
- * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm
- * {S,U}XT{B,B16,H}: !Rd, !Rm
- * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs
- * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs
- *
- * May transfer control by writing R15 (possible mode changes or alternate
- * mode accesses marked by "*"):
- * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY,
- * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI*
- *
- * Instructions that do not take general registers, nor transfer control:
- * CDP/2, SETEND, SRS*
- */
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index 2ba7deb3072..1656c87501c 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -134,7 +134,8 @@ static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{
regs->ARM_pc += 4;
- p->ainsn.insn_handler(p, regs);
+ if (p->ainsn.insn_check_cc(regs->ARM_cpsr))
+ p->ainsn.insn_handler(p, regs);
}
/*
diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c
index 31a316c1777..0f107dcb034 100644
--- a/arch/arm/kernel/leds.c
+++ b/arch/arm/kernel/leds.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysdev.h>
+#include <linux/syscore_ops.h>
#include <asm/leds.h>
@@ -69,36 +70,37 @@ static ssize_t leds_store(struct sys_device *dev,
static SYSDEV_ATTR(event, 0200, NULL, leds_store);
-static int leds_suspend(struct sys_device *dev, pm_message_t state)
+static struct sysdev_class leds_sysclass = {
+ .name = "leds",
+};
+
+static struct sys_device leds_device = {
+ .id = 0,
+ .cls = &leds_sysclass,
+};
+
+static int leds_suspend(void)
{
leds_event(led_stop);
return 0;
}
-static int leds_resume(struct sys_device *dev)
+static void leds_resume(void)
{
leds_event(led_start);
- return 0;
}
-static int leds_shutdown(struct sys_device *dev)
+static void leds_shutdown(void)
{
leds_event(led_halted);
- return 0;
}
-static struct sysdev_class leds_sysclass = {
- .name = "leds",
+static struct syscore_ops leds_syscore_ops = {
.shutdown = leds_shutdown,
.suspend = leds_suspend,
.resume = leds_resume,
};
-static struct sys_device leds_device = {
- .id = 0,
- .cls = &leds_sysclass,
-};
-
static int __init leds_init(void)
{
int ret;
@@ -107,6 +109,8 @@ static int __init leds_init(void)
ret = sysdev_register(&leds_device);
if (ret == 0)
ret = sysdev_create_file(&leds_device, &attr_event);
+ if (ret == 0)
+ register_syscore_ops(&leds_syscore_ops);
return ret;
}
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 979da3947f4..d53c0abc4dd 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -560,11 +560,6 @@ static int armpmu_event_init(struct perf_event *event)
event->destroy = hw_perf_event_destroy;
if (!atomic_inc_not_zero(&active_events)) {
- if (atomic_read(&active_events) > armpmu->num_events) {
- atomic_dec(&active_events);
- return -ENOSPC;
- }
-
mutex_lock(&pmu_reserve_mutex);
if (atomic_read(&active_events) == 0) {
err = armpmu_reserve_hardware();
@@ -746,7 +741,8 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
tail = (struct frame_tail __user *)regs->ARM_fp - 1;
- while (tail && !((unsigned long)tail & 0x3))
+ while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
+ tail && !((unsigned long)tail & 0x3))
tail = user_backtrace(tail, entry);
}
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 2bf27f364d0..97260060bf2 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -21,6 +21,7 @@
#include <linux/uaccess.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
+#include <linux/regset.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -308,58 +309,6 @@ static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
return put_user_reg(tsk, off >> 2, val);
}
-/*
- * Get all user integer registers.
- */
-static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
-{
- struct pt_regs *regs = task_pt_regs(tsk);
-
- return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
-}
-
-/*
- * Set all user integer registers.
- */
-static int ptrace_setregs(struct task_struct *tsk, void __user *uregs)
-{
- struct pt_regs newregs;
- int ret;
-
- ret = -EFAULT;
- if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) {
- struct pt_regs *regs = task_pt_regs(tsk);
-
- ret = -EINVAL;
- if (valid_user_regs(&newregs)) {
- *regs = newregs;
- ret = 0;
- }
- }
-
- return ret;
-}
-
-/*
- * Get the child FPU state.
- */
-static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp)
-{
- return copy_to_user(ufp, &task_thread_info(tsk)->fpstate,
- sizeof(struct user_fp)) ? -EFAULT : 0;
-}
-
-/*
- * Set the child FPU state.
- */
-static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp)
-{
- struct thread_info *thread = task_thread_info(tsk);
- thread->used_cp[1] = thread->used_cp[2] = 1;
- return copy_from_user(&thread->fpstate, ufp,
- sizeof(struct user_fp)) ? -EFAULT : 0;
-}
-
#ifdef CONFIG_IWMMXT
/*
@@ -418,56 +367,6 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
}
#endif
-#ifdef CONFIG_VFP
-/*
- * Get the child VFP state.
- */
-static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data)
-{
- struct thread_info *thread = task_thread_info(tsk);
- union vfp_state *vfp = &thread->vfpstate;
- struct user_vfp __user *ufp = data;
-
- vfp_sync_hwstate(thread);
-
- /* copy the floating point registers */
- if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs,
- sizeof(vfp->hard.fpregs)))
- return -EFAULT;
-
- /* copy the status and control register */
- if (put_user(vfp->hard.fpscr, &ufp->fpscr))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Set the child VFP state.
- */
-static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data)
-{
- struct thread_info *thread = task_thread_info(tsk);
- union vfp_state *vfp = &thread->vfpstate;
- struct user_vfp __user *ufp = data;
-
- vfp_sync_hwstate(thread);
-
- /* copy the floating point registers */
- if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs,
- sizeof(vfp->hard.fpregs)))
- return -EFAULT;
-
- /* copy the status and control register */
- if (get_user(vfp->hard.fpscr, &ufp->fpscr))
- return -EFAULT;
-
- vfp_flush_hwstate(thread);
-
- return 0;
-}
-#endif
-
#ifdef CONFIG_HAVE_HW_BREAKPOINT
/*
* Convert a virtual register number into an index for a thread_info
@@ -694,6 +593,219 @@ out:
}
#endif
+/* regset get/set implementations */
+
+static int gpr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ regs,
+ 0, sizeof(*regs));
+}
+
+static int gpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+ struct pt_regs newregs;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &newregs,
+ 0, sizeof(newregs));
+ if (ret)
+ return ret;
+
+ if (!valid_user_regs(&newregs))
+ return -EINVAL;
+
+ *task_pt_regs(target) = newregs;
+ return 0;
+}
+
+static int fpa_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &task_thread_info(target)->fpstate,
+ 0, sizeof(struct user_fp));
+}
+
+static int fpa_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct thread_info *thread = task_thread_info(target);
+
+ thread->used_cp[1] = thread->used_cp[2] = 1;
+
+ return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &thread->fpstate,
+ 0, sizeof(struct user_fp));
+}
+
+#ifdef CONFIG_VFP
+/*
+ * VFP register get/set implementations.
+ *
+ * With respect to the kernel, struct user_fp is divided into three chunks:
+ * 16 or 32 real VFP registers (d0-d15 or d0-31)
+ * These are transferred to/from the real registers in the task's
+ * vfp_hard_struct. The number of registers depends on the kernel
+ * configuration.
+ *
+ * 16 or 0 fake VFP registers (d16-d31 or empty)
+ * i.e., the user_vfp structure has space for 32 registers even if
+ * the kernel doesn't have them all.
+ *
+ * vfp_get() reads this chunk as zero where applicable
+ * vfp_set() ignores this chunk
+ *
+ * 1 word for the FPSCR
+ *
+ * The bounds-checking logic built into user_regset_copyout and friends
+ * means that we can make a simple sequence of calls to map the relevant data
+ * to/from the specified slice of the user regset structure.
+ */
+static int vfp_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+ struct thread_info *thread = task_thread_info(target);
+ struct vfp_hard_struct const *vfp = &thread->vfpstate.hard;
+ const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs);
+ const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr);
+
+ vfp_sync_hwstate(thread);
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &vfp->fpregs,
+ user_fpregs_offset,
+ user_fpregs_offset + sizeof(vfp->fpregs));
+ if (ret)
+ return ret;
+
+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ user_fpregs_offset + sizeof(vfp->fpregs),
+ user_fpscr_offset);
+ if (ret)
+ return ret;
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &vfp->fpscr,
+ user_fpscr_offset,
+ user_fpscr_offset + sizeof(vfp->fpscr));
+}
+
+/*
+ * For vfp_set() a read-modify-write is done on the VFP registers,
+ * in order to avoid writing back a half-modified set of registers on
+ * failure.
+ */
+static int vfp_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+ struct thread_info *thread = task_thread_info(target);
+ struct vfp_hard_struct new_vfp = thread->vfpstate.hard;
+ const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs);
+ const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr);
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &new_vfp.fpregs,
+ user_fpregs_offset,
+ user_fpregs_offset + sizeof(new_vfp.fpregs));
+ if (ret)
+ return ret;
+
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ user_fpregs_offset + sizeof(new_vfp.fpregs),
+ user_fpscr_offset);
+ if (ret)
+ return ret;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &new_vfp.fpscr,
+ user_fpscr_offset,
+ user_fpscr_offset + sizeof(new_vfp.fpscr));
+ if (ret)
+ return ret;
+
+ vfp_sync_hwstate(thread);
+ thread->vfpstate.hard = new_vfp;
+ vfp_flush_hwstate(thread);
+
+ return 0;
+}
+#endif /* CONFIG_VFP */
+
+enum arm_regset {
+ REGSET_GPR,
+ REGSET_FPR,
+#ifdef CONFIG_VFP
+ REGSET_VFP,
+#endif
+};
+
+static const struct user_regset arm_regsets[] = {
+ [REGSET_GPR] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = ELF_NGREG,
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .get = gpr_get,
+ .set = gpr_set
+ },
+ [REGSET_FPR] = {
+ /*
+ * For the FPA regs in fpstate, the real fields are a mixture
+ * of sizes, so pretend that the registers are word-sized:
+ */
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct user_fp) / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .get = fpa_get,
+ .set = fpa_set
+ },
+#ifdef CONFIG_VFP
+ [REGSET_VFP] = {
+ /*
+ * Pretend that the VFP regs are word-sized, since the FPSCR is
+ * a single word dangling at the end of struct user_vfp:
+ */
+ .core_note_type = NT_ARM_VFP,
+ .n = ARM_VFPREGS_SIZE / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .get = vfp_get,
+ .set = vfp_set
+ },
+#endif /* CONFIG_VFP */
+};
+
+static const struct user_regset_view user_arm_view = {
+ .name = "arm", .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI,
+ .regsets = arm_regsets, .n = ARRAY_SIZE(arm_regsets)
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+ return &user_arm_view;
+}
+
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
@@ -710,19 +822,31 @@ long arch_ptrace(struct task_struct *child, long request,
break;
case PTRACE_GETREGS:
- ret = ptrace_getregs(child, datap);
+ ret = copy_regset_to_user(child,
+ &user_arm_view, REGSET_GPR,
+ 0, sizeof(struct pt_regs),
+ datap);
break;
case PTRACE_SETREGS:
- ret = ptrace_setregs(child, datap);
+ ret = copy_regset_from_user(child,
+ &user_arm_view, REGSET_GPR,
+ 0, sizeof(struct pt_regs),
+ datap);
break;
case PTRACE_GETFPREGS:
- ret = ptrace_getfpregs(child, datap);
+ ret = copy_regset_to_user(child,
+ &user_arm_view, REGSET_FPR,
+ 0, sizeof(union fp_state),
+ datap);
break;
-
+
case PTRACE_SETFPREGS:
- ret = ptrace_setfpregs(child, datap);
+ ret = copy_regset_from_user(child,
+ &user_arm_view, REGSET_FPR,
+ 0, sizeof(union fp_state),
+ datap);
break;
#ifdef CONFIG_IWMMXT
@@ -757,22 +881,36 @@ long arch_ptrace(struct task_struct *child, long request,
#ifdef CONFIG_VFP
case PTRACE_GETVFPREGS:
- ret = ptrace_getvfpregs(child, datap);
+ ret = copy_regset_to_user(child,
+ &user_arm_view, REGSET_VFP,
+ 0, ARM_VFPREGS_SIZE,
+ datap);
break;
case PTRACE_SETVFPREGS:
- ret = ptrace_setvfpregs(child, datap);
+ ret = copy_regset_from_user(child,
+ &user_arm_view, REGSET_VFP,
+ 0, ARM_VFPREGS_SIZE,
+ datap);
break;
#endif
#ifdef CONFIG_HAVE_HW_BREAKPOINT
case PTRACE_GETHBPREGS:
+ if (ptrace_get_breakpoints(child) < 0)
+ return -ESRCH;
+
ret = ptrace_gethbpregs(child, addr,
(unsigned long __user *)data);
+ ptrace_put_breakpoints(child);
break;
case PTRACE_SETHBPREGS:
+ if (ptrace_get_breakpoints(child) < 0)
+ return -ESRCH;
+
ret = ptrace_sethbpregs(child, addr,
(unsigned long __user *)data);
+ ptrace_put_breakpoints(child);
break;
#endif
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 006c1e884ea..ed11fb08b05 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -20,6 +20,7 @@
#include <linux/screen_info.h>
#include <linux/init.h>
#include <linux/kexec.h>
+#include <linux/of_fdt.h>
#include <linux/crash_dump.h>
#include <linux/root_dev.h>
#include <linux/cpu.h>
@@ -42,6 +43,7 @@
#include <asm/cachetype.h>
#include <asm/tlbflush.h>
+#include <asm/prom.h>
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
@@ -309,7 +311,7 @@ static void __init cacheid_init(void)
*/
extern struct proc_info_list *lookup_processor_type(unsigned int);
-static void __init early_print(const char *str, ...)
+void __init early_print(const char *str, ...)
{
extern void printascii(const char *);
char buf[256];
@@ -439,25 +441,12 @@ void cpu_init(void)
: "r14");
}
-static struct machine_desc * __init setup_machine(unsigned int nr)
+void __init dump_machine_table(void)
{
- extern struct machine_desc __arch_info_begin[], __arch_info_end[];
struct machine_desc *p;
- /*
- * locate machine in the list of supported machines.
- */
- for (p = __arch_info_begin; p < __arch_info_end; p++)
- if (nr == p->nr) {
- printk("Machine: %s\n", p->name);
- return p;
- }
-
- early_print("\n"
- "Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n"
- "Available machine support:\n\nID (hex)\tNAME\n", nr);
-
- for (p = __arch_info_begin; p < __arch_info_end; p++)
+ early_print("Available machine support:\n\nID (hex)\tNAME\n");
+ for_each_machine_desc(p)
early_print("%08x\t%s\n", p->nr, p->name);
early_print("\nPlease check your kernel config and/or bootloader.\n");
@@ -466,7 +455,7 @@ static struct machine_desc * __init setup_machine(unsigned int nr)
/* can't use cpu_relax() here as it may require MMU setup */;
}
-static int __init arm_add_memory(phys_addr_t start, unsigned long size)
+int __init arm_add_memory(phys_addr_t start, unsigned long size)
{
struct membank *bank = &meminfo.bank[meminfo.nr_banks];
@@ -672,11 +661,16 @@ __tagtable(ATAG_REVISION, parse_tag_revision);
static int __init parse_tag_cmdline(const struct tag *tag)
{
-#ifndef CONFIG_CMDLINE_FORCE
- strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
-#else
+#if defined(CONFIG_CMDLINE_EXTEND)
+ strlcat(default_command_line, " ", COMMAND_LINE_SIZE);
+ strlcat(default_command_line, tag->u.cmdline.cmdline,
+ COMMAND_LINE_SIZE);
+#elif defined(CONFIG_CMDLINE_FORCE)
pr_warning("Ignoring tag cmdline (using the default kernel command line)\n");
-#endif /* CONFIG_CMDLINE_FORCE */
+#else
+ strlcpy(default_command_line, tag->u.cmdline.cmdline,
+ COMMAND_LINE_SIZE);
+#endif
return 0;
}
@@ -796,23 +790,29 @@ static void __init squash_mem_tags(struct tag *tag)
tag->hdr.tag = ATAG_NONE;
}
-void __init setup_arch(char **cmdline_p)
+static struct machine_desc * __init setup_machine_tags(unsigned int nr)
{
struct tag *tags = (struct tag *)&init_tags;
- struct machine_desc *mdesc;
+ struct machine_desc *mdesc = NULL, *p;
char *from = default_command_line;
init_tags.mem.start = PHYS_OFFSET;
- unwind_init();
-
- setup_processor();
- mdesc = setup_machine(machine_arch_type);
- machine_desc = mdesc;
- machine_name = mdesc->name;
+ /*
+ * locate machine in the list of supported machines.
+ */
+ for_each_machine_desc(p)
+ if (nr == p->nr) {
+ printk("Machine: %s\n", p->name);
+ mdesc = p;
+ break;
+ }
- if (mdesc->soft_reboot)
- reboot_setup("s");
+ if (!mdesc) {
+ early_print("\nError: unrecognized/unsupported machine ID"
+ " (r1 = 0x%08x).\n\n", nr);
+ dump_machine_table(); /* does not return */
+ }
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
@@ -844,8 +844,17 @@ void __init setup_arch(char **cmdline_p)
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
#endif
- if (tags->hdr.tag != ATAG_CORE)
+
+ if (tags->hdr.tag != ATAG_CORE) {
+#if defined(CONFIG_OF)
+ /*
+ * If CONFIG_OF is set, then assume this is a reasonably
+ * modern system that should pass boot parameters
+ */
+ early_print("Warning: Neither atags nor dtb found\n");
+#endif
tags = (struct tag *)&init_tags;
+ }
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
@@ -857,14 +866,34 @@ void __init setup_arch(char **cmdline_p)
parse_tags(tags);
}
+ /* parse_early_param needs a boot_command_line */
+ strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
+
+ return mdesc;
+}
+
+
+void __init setup_arch(char **cmdline_p)
+{
+ struct machine_desc *mdesc;
+
+ unwind_init();
+
+ setup_processor();
+ mdesc = setup_machine_fdt(__atags_pointer);
+ if (!mdesc)
+ mdesc = setup_machine_tags(machine_arch_type);
+ machine_desc = mdesc;
+ machine_name = mdesc->name;
+
+ if (mdesc->soft_reboot)
+ reboot_setup("s");
+
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = (unsigned long) _end;
- /* parse_early_param needs a boot_command_line */
- strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
-
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
@@ -876,6 +905,8 @@ void __init setup_arch(char **cmdline_p)
paging_init(mdesc);
request_standard_resources(mdesc);
+ unflatten_device_tree();
+
#ifdef CONFIG_SMP
if (is_smp())
smp_init_cpus();
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index cb839831764..0340224cf73 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -597,19 +597,13 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
return err;
}
-static inline void setup_syscall_restart(struct pt_regs *regs)
-{
- regs->ARM_r0 = regs->ARM_ORIG_r0;
- regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
-}
-
/*
* OK, we're invoking a handler
*/
static int
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset,
- struct pt_regs * regs, int syscall)
+ struct pt_regs * regs)
{
struct thread_info *thread = current_thread_info();
struct task_struct *tsk = current;
@@ -617,26 +611,6 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
int ret;
/*
- * If we were from a system call, check for system call restarting...
- */
- if (syscall) {
- switch (regs->ARM_r0) {
- case -ERESTART_RESTARTBLOCK:
- case -ERESTARTNOHAND:
- regs->ARM_r0 = -EINTR;
- break;
- case -ERESTARTSYS:
- if (!(ka->sa.sa_flags & SA_RESTART)) {
- regs->ARM_r0 = -EINTR;
- break;
- }
- /* fallthrough */
- case -ERESTARTNOINTR:
- setup_syscall_restart(regs);
- }
- }
-
- /*
* translate the signal
*/
if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap)
@@ -685,6 +659,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
*/
static void do_signal(struct pt_regs *regs, int syscall)
{
+ unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
struct k_sigaction ka;
siginfo_t info;
int signr;
@@ -698,18 +673,61 @@ static void do_signal(struct pt_regs *regs, int syscall)
if (!user_mode(regs))
return;
+ /*
+ * If we were from a system call, check for system call restarting...
+ */
+ if (syscall) {
+ continue_addr = regs->ARM_pc;
+ restart_addr = continue_addr - (thumb_mode(regs) ? 2 : 4);
+ retval = regs->ARM_r0;
+
+ /*
+ * Prepare for system call restart. We do this here so that a
+ * debugger will see the already changed PSW.
+ */
+ switch (retval) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs->ARM_r0 = regs->ARM_ORIG_r0;
+ regs->ARM_pc = restart_addr;
+ break;
+ case -ERESTART_RESTARTBLOCK:
+ regs->ARM_r0 = -EINTR;
+ break;
+ }
+ }
+
if (try_to_freeze())
goto no_signal;
+ /*
+ * Get the signal to deliver. When running under ptrace, at this
+ * point the debugger may change all our registers ...
+ */
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
sigset_t *oldset;
+ /*
+ * Depending on the signal settings we may need to revert the
+ * decision to restart the system call. But skip this if a
+ * debugger has chosen to restart at a different PC.
+ */
+ if (regs->ARM_pc == restart_addr) {
+ if (retval == -ERESTARTNOHAND
+ || (retval == -ERESTARTSYS
+ && !(ka.sa.sa_flags & SA_RESTART))) {
+ regs->ARM_r0 = -EINTR;
+ regs->ARM_pc = continue_addr;
+ }
+ }
+
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked;
- if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) {
+ if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
/*
* A signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
@@ -723,11 +741,14 @@ static void do_signal(struct pt_regs *regs, int syscall)
}
no_signal:
- /*
- * No signal to deliver to the process - restart the syscall.
- */
if (syscall) {
- if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
+ /*
+ * Handle restarting a different system call. As above,
+ * if a debugger has chosen to restart at a different PC,
+ * ignore the restart.
+ */
+ if (retval == -ERESTART_RESTARTBLOCK
+ && regs->ARM_pc == continue_addr) {
if (thumb_mode(regs)) {
regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
regs->ARM_pc -= 2;
@@ -750,11 +771,6 @@ static void do_signal(struct pt_regs *regs, int syscall)
#endif
}
}
- if (regs->ARM_r0 == -ERESTARTNOHAND ||
- regs->ARM_r0 == -ERESTARTSYS ||
- regs->ARM_r0 == -ERESTARTNOINTR) {
- setup_syscall_restart(regs);
- }
/* If there's no signal to deliver, we just put the saved sigmask
* back.
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 8fe05ad932e..344e52b16c8 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -105,6 +105,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
*/
secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
secondary_data.pgdir = virt_to_phys(pgd);
+ secondary_data.swapper_pg_dir = virt_to_phys(swapper_pg_dir);
__cpuc_flush_dcache_area(&secondary_data, sizeof(secondary_data));
outer_clean_range(__pa(&secondary_data), __pa(&secondary_data + 1));
@@ -376,6 +377,13 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
+static void (*smp_cross_call)(const struct cpumask *, unsigned int);
+
+void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
+{
+ smp_cross_call = fn;
+}
+
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
smp_cross_call(mask, IPI_CALL_FUNC);
@@ -479,7 +487,7 @@ static void broadcast_timer_set_mode(enum clock_event_mode mode,
{
}
-static void broadcast_timer_setup(struct clock_event_device *evt)
+static void __cpuinit broadcast_timer_setup(struct clock_event_device *evt)
{
evt->name = "dummy_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT |
@@ -560,10 +568,7 @@ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs)
break;
case IPI_RESCHEDULE:
- /*
- * nothing more to do - eveything is
- * done on the interrupt return path
- */
+ scheduler_ipi();
break;
case IPI_CALL_FUNC:
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
index 4ad8da15ef2..af0aaebf4de 100644
--- a/arch/arm/kernel/sys_oabi-compat.c
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -311,7 +311,7 @@ asmlinkage long sys_oabi_semtimedop(int semid,
long err;
int i;
- if (nsops < 1)
+ if (nsops < 1 || nsops > SEMOPM)
return -EINVAL;
sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL);
if (!sops)
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
index 1ff46cabc7e..cb634c3e28e 100644
--- a/arch/arm/kernel/time.c
+++ b/arch/arm/kernel/time.c
@@ -21,7 +21,7 @@
#include <linux/timex.h>
#include <linux/errno.h>
#include <linux/profile.h>
-#include <linux/sysdev.h>
+#include <linux/syscore_ops.h>
#include <linux/timer.h>
#include <linux/irq.h>
@@ -115,48 +115,37 @@ void timer_tick(void)
#endif
#if defined(CONFIG_PM) && !defined(CONFIG_GENERIC_CLOCKEVENTS)
-static int timer_suspend(struct sys_device *dev, pm_message_t state)
+static int timer_suspend(void)
{
- struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
-
- if (timer->suspend != NULL)
- timer->suspend();
+ if (system_timer->suspend)
+ system_timer->suspend();
return 0;
}
-static int timer_resume(struct sys_device *dev)
+static void timer_resume(void)
{
- struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
-
- if (timer->resume != NULL)
- timer->resume();
-
- return 0;
+ if (system_timer->resume)
+ system_timer->resume();
}
#else
#define timer_suspend NULL
#define timer_resume NULL
#endif
-static struct sysdev_class timer_sysclass = {
- .name = "timer",
+static struct syscore_ops timer_syscore_ops = {
.suspend = timer_suspend,
.resume = timer_resume,
};
-static int __init timer_init_sysfs(void)
+static int __init timer_init_syscore_ops(void)
{
- int ret = sysdev_class_register(&timer_sysclass);
- if (ret == 0) {
- system_timer->dev.cls = &timer_sysclass;
- ret = sysdev_register(&system_timer->dev);
- }
+ register_syscore_ops(&timer_syscore_ops);
- return ret;
+ return 0;
}
-device_initcall(timer_init_sysfs);
+device_initcall(timer_init_syscore_ops);
void __init time_init(void)
{
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 3b54ad19d48..6807cb1e76d 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -139,7 +139,7 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
fs = get_fs();
set_fs(KERNEL_DS);
- for (i = -4; i < 1; i++) {
+ for (i = -4; i < 1 + !!thumb; i++) {
unsigned int val, bad;
if (thumb)
@@ -234,7 +234,6 @@ static int __die(const char *str, int err, struct thread_info *thread, struct pt
printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
str, err, ++die_counter);
- sysfs_printk_last_file();
/* trap and error numbers are mostly meaningless on ARM */
ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV);
@@ -564,7 +563,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
if (!pmd_present(*pmd))
goto bad_access;
pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
- if (!pte_present(*pte) || !pte_dirty(*pte)) {
+ if (!pte_present(*pte) || !pte_write(*pte) || !pte_dirty(*pte)) {
pte_unmap_unlock(pte, ptl);
goto bad_access;
}
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index b4348e62ef0..e5287f21bad 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -82,7 +82,7 @@ SECTIONS
#endif
}
- PERCPU(32, PAGE_SIZE)
+ PERCPU_SECTION(32)
#ifndef CONFIG_XIP_KERNEL
. = ALIGN(PAGE_SIZE);