提交 3ad55155 编写于 作者: R Russell King

Merge branch 'devel-stable' into for-next

Conflicts:
	arch/arm/kernel/entry-armv.S
Kernel-provided User Helpers
============================
These are segment of kernel provided user code reachable from user space
at a fixed address in kernel memory. This is used to provide user space
with some operations which require kernel help because of unimplemented
native feature and/or instructions in many ARM CPUs. The idea is for this
code to be executed directly in user mode for best efficiency but which is
too intimate with the kernel counter part to be left to user libraries.
In fact this code might even differ from one CPU to another depending on
the available instruction set, or whether it is a SMP systems. In other
words, the kernel reserves the right to change this code as needed without
warning. Only the entry points and their results as documented here are
guaranteed to be stable.
This is different from (but doesn't preclude) a full blown VDSO
implementation, however a VDSO would prevent some assembly tricks with
constants that allows for efficient branching to those code segments. And
since those code segments only use a few cycles before returning to user
code, the overhead of a VDSO indirect far call would add a measurable
overhead to such minimalistic operations.
User space is expected to bypass those helpers and implement those things
inline (either in the code emitted directly by the compiler, or part of
the implementation of a library call) when optimizing for a recent enough
processor that has the necessary native support, but only if resulting
binaries are already to be incompatible with earlier ARM processors due to
useage of similar native instructions for other things. In other words
don't make binaries unable to run on earlier processors just for the sake
of not using these kernel helpers if your compiled code is not going to
use new instructions for other purpose.
New helpers may be added over time, so an older kernel may be missing some
helpers present in a newer kernel. For this reason, programs must check
the value of __kuser_helper_version (see below) before assuming that it is
safe to call any particular helper. This check should ideally be
performed only once at process startup time, and execution aborted early
if the required helpers are not provided by the kernel version that
process is running on.
kuser_helper_version
--------------------
Location: 0xffff0ffc
Reference declaration:
extern int32_t __kuser_helper_version;
Definition:
This field contains the number of helpers being implemented by the
running kernel. User space may read this to determine the availability
of a particular helper.
Usage example:
#define __kuser_helper_version (*(int32_t *)0xffff0ffc)
void check_kuser_version(void)
{
if (__kuser_helper_version < 2) {
fprintf(stderr, "can't do atomic operations, kernel too old\n");
abort();
}
}
Notes:
User space may assume that the value of this field never changes
during the lifetime of any single process. This means that this
field can be read once during the initialisation of a library or
startup phase of a program.
kuser_get_tls
-------------
Location: 0xffff0fe0
Reference prototype:
void * __kuser_get_tls(void);
Input:
lr = return address
Output:
r0 = TLS value
Clobbered registers:
none
Definition:
Get the TLS value as previously set via the __ARM_NR_set_tls syscall.
Usage example:
typedef void * (__kuser_get_tls_t)(void);
#define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0)
void foo()
{
void *tls = __kuser_get_tls();
printf("TLS = %p\n", tls);
}
Notes:
- Valid only if __kuser_helper_version >= 1 (from kernel version 2.6.12).
kuser_cmpxchg
-------------
Location: 0xffff0fc0
Reference prototype:
int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr);
Input:
r0 = oldval
r1 = newval
r2 = ptr
lr = return address
Output:
r0 = success code (zero or non-zero)
C flag = set if r0 == 0, clear if r0 != 0
Clobbered registers:
r3, ip, flags
Definition:
Atomically store newval in *ptr only if *ptr is equal to oldval.
Return zero if *ptr was changed or non-zero if no exchange happened.
The C flag is also set if *ptr was changed to allow for assembly
optimization in the calling code.
Usage example:
typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
#define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0)
int atomic_add(volatile int *ptr, int val)
{
int old, new;
do {
old = *ptr;
new = old + val;
} while(__kuser_cmpxchg(old, new, ptr));
return new;
}
Notes:
- This routine already includes memory barriers as needed.
- Valid only if __kuser_helper_version >= 2 (from kernel version 2.6.12).
kuser_memory_barrier
--------------------
Location: 0xffff0fa0
Reference prototype:
void __kuser_memory_barrier(void);
Input:
lr = return address
Output:
none
Clobbered registers:
none
Definition:
Apply any needed memory barrier to preserve consistency with data modified
manually and __kuser_cmpxchg usage.
Usage example:
typedef void (__kuser_dmb_t)(void);
#define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0)
Notes:
- Valid only if __kuser_helper_version >= 3 (from kernel version 2.6.15).
kuser_cmpxchg64
---------------
Location: 0xffff0f60
Reference prototype:
int __kuser_cmpxchg64(const int64_t *oldval,
const int64_t *newval,
volatile int64_t *ptr);
Input:
r0 = pointer to oldval
r1 = pointer to newval
r2 = pointer to target value
lr = return address
Output:
r0 = success code (zero or non-zero)
C flag = set if r0 == 0, clear if r0 != 0
Clobbered registers:
r3, lr, flags
Definition:
Atomically store the 64-bit value pointed by *newval in *ptr only if *ptr
is equal to the 64-bit value pointed by *oldval. Return zero if *ptr was
changed or non-zero if no exchange happened.
The C flag is also set if *ptr was changed to allow for assembly
optimization in the calling code.
Usage example:
typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval,
const int64_t *newval,
volatile int64_t *ptr);
#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60)
int64_t atomic_add64(volatile int64_t *ptr, int64_t val)
{
int64_t old, new;
do {
old = *ptr;
new = old + val;
} while(__kuser_cmpxchg64(&old, &new, ptr));
return new;
}
Notes:
- This routine already includes memory barriers as needed.
- Due to the length of this sequence, this spans 2 conventional kuser
"slots", therefore 0xffff0f80 is not used as a valid entry point.
- Valid only if __kuser_helper_version >= 5 (from kernel version 3.1).
...@@ -10,7 +10,7 @@ config ARM ...@@ -10,7 +10,7 @@ config ARM
select GENERIC_ATOMIC64 if (CPU_V6 || !CPU_32v6K || !AEABI) select GENERIC_ATOMIC64 if (CPU_V6 || !CPU_32v6K || !AEABI)
select HAVE_OPROFILE if (HAVE_PERF_EVENTS) select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
select HAVE_ARCH_KGDB select HAVE_ARCH_KGDB
select HAVE_KPROBES if (!XIP_KERNEL && !THUMB2_KERNEL) select HAVE_KPROBES if !XIP_KERNEL
select HAVE_KRETPROBES if (HAVE_KPROBES) select HAVE_KRETPROBES if (HAVE_KPROBES)
select HAVE_FUNCTION_TRACER if (!XIP_KERNEL) select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL) select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
......
...@@ -293,4 +293,13 @@ ...@@ -293,4 +293,13 @@
.macro ldrusr, reg, ptr, inc, cond=al, rept=1, abort=9001f .macro ldrusr, reg, ptr, inc, cond=al, rept=1, abort=9001f
usracc ldr, \reg, \ptr, \inc, \cond, \rept, \abort usracc ldr, \reg, \ptr, \inc, \cond, \rept, \abort
.endm .endm
/* Utility macro for declaring string literals */
.macro string name:req, string
.type \name , #object
\name:
.asciz "\string"
.size \name , . - \name
.endm
#endif /* __ASM_ASSEMBLER_H__ */ #endif /* __ASM_ASSEMBLER_H__ */
#ifndef __ASM_ARM_DMA_H #ifndef __ASM_ARM_DMA_H
#define __ASM_ARM_DMA_H #define __ASM_ARM_DMA_H
#include <asm/memory.h>
/* /*
* This is the maximum virtual address which can be DMA'd from. * This is the maximum virtual address which can be DMA'd from.
*/ */
#ifndef ARM_DMA_ZONE_SIZE #ifndef CONFIG_ZONE_DMA
#define MAX_DMA_ADDRESS 0xffffffff #define MAX_DMA_ADDRESS 0xffffffffUL
#else #else
#define MAX_DMA_ADDRESS (PAGE_OFFSET + ARM_DMA_ZONE_SIZE) #define MAX_DMA_ADDRESS ({ \
extern unsigned long arm_dma_zone_size; \
arm_dma_zone_size ? \
(PAGE_OFFSET + arm_dma_zone_size) : 0xffffffffUL; })
#endif #endif
#ifdef CONFIG_ISA_DMA_API #ifdef CONFIG_ISA_DMA_API
......
...@@ -4,22 +4,26 @@ ...@@ -4,22 +4,26 @@
/* /*
* HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
*/ */
#define HWCAP_SWP 1 #define HWCAP_SWP (1 << 0)
#define HWCAP_HALF 2 #define HWCAP_HALF (1 << 1)
#define HWCAP_THUMB 4 #define HWCAP_THUMB (1 << 2)
#define HWCAP_26BIT 8 /* Play it safe */ #define HWCAP_26BIT (1 << 3) /* Play it safe */
#define HWCAP_FAST_MULT 16 #define HWCAP_FAST_MULT (1 << 4)
#define HWCAP_FPA 32 #define HWCAP_FPA (1 << 5)
#define HWCAP_VFP 64 #define HWCAP_VFP (1 << 6)
#define HWCAP_EDSP 128 #define HWCAP_EDSP (1 << 7)
#define HWCAP_JAVA 256 #define HWCAP_JAVA (1 << 8)
#define HWCAP_IWMMXT 512 #define HWCAP_IWMMXT (1 << 9)
#define HWCAP_CRUNCH 1024 #define HWCAP_CRUNCH (1 << 10)
#define HWCAP_THUMBEE 2048 #define HWCAP_THUMBEE (1 << 11)
#define HWCAP_NEON 4096 #define HWCAP_NEON (1 << 12)
#define HWCAP_VFPv3 8192 #define HWCAP_VFPv3 (1 << 13)
#define HWCAP_VFPv3D16 16384 #define HWCAP_VFPv3D16 (1 << 14)
#define HWCAP_TLS 32768 #define HWCAP_TLS (1 << 15)
#define HWCAP_VFPv4 (1 << 16)
#define HWCAP_IDIVA (1 << 17)
#define HWCAP_IDIVT (1 << 18)
#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
#if defined(__KERNEL__) && !defined(__ASSEMBLY__) #if defined(__KERNEL__) && !defined(__ASSEMBLY__)
/* /*
......
...@@ -24,12 +24,6 @@ ...@@ -24,12 +24,6 @@
#define MAX_INSN_SIZE 2 #define MAX_INSN_SIZE 2
#define MAX_STACK_SIZE 64 /* 32 would probably be OK */ #define MAX_STACK_SIZE 64 /* 32 would probably be OK */
/*
* This undefined instruction must be unique and
* reserved solely for kprobes' use.
*/
#define KPROBE_BREAKPOINT_INSTRUCTION 0xe7f001f8
#define regs_return_value(regs) ((regs)->ARM_r0) #define regs_return_value(regs) ((regs)->ARM_r0)
#define flush_insn_slot(p) do { } while (0) #define flush_insn_slot(p) do { } while (0)
#define kretprobe_blacklist_size 0 #define kretprobe_blacklist_size 0
...@@ -38,14 +32,17 @@ typedef u32 kprobe_opcode_t; ...@@ -38,14 +32,17 @@ typedef u32 kprobe_opcode_t;
struct kprobe; struct kprobe;
typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *); typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
typedef unsigned long (kprobe_check_cc)(unsigned long); typedef unsigned long (kprobe_check_cc)(unsigned long);
typedef void (kprobe_insn_singlestep_t)(struct kprobe *, struct pt_regs *);
typedef void (kprobe_insn_fn_t)(void);
/* Architecture specific copy of original instruction. */ /* Architecture specific copy of original instruction. */
struct arch_specific_insn { struct arch_specific_insn {
kprobe_opcode_t *insn; kprobe_opcode_t *insn;
kprobe_insn_handler_t *insn_handler; kprobe_insn_handler_t *insn_handler;
kprobe_check_cc *insn_check_cc; kprobe_check_cc *insn_check_cc;
kprobe_insn_singlestep_t *insn_singlestep;
kprobe_insn_fn_t *insn_fn;
}; };
struct prev_kprobe { struct prev_kprobe {
...@@ -62,20 +59,9 @@ struct kprobe_ctlblk { ...@@ -62,20 +59,9 @@ struct kprobe_ctlblk {
}; };
void arch_remove_kprobe(struct kprobe *); void arch_remove_kprobe(struct kprobe *);
void kretprobe_trampoline(void);
int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr); int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);
int kprobe_exceptions_notify(struct notifier_block *self, int kprobe_exceptions_notify(struct notifier_block *self,
unsigned long val, void *data); unsigned long val, void *data);
enum kprobe_insn {
INSN_REJECTED,
INSN_GOOD,
INSN_GOOD_NO_SLOT
};
enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
struct arch_specific_insn *);
void __init arm_kprobe_decode_init(void);
#endif /* _ARM_KPROBES_H */ #endif /* _ARM_KPROBES_H */
...@@ -23,6 +23,10 @@ struct machine_desc { ...@@ -23,6 +23,10 @@ struct machine_desc {
unsigned int nr_irqs; /* number of IRQs */ unsigned int nr_irqs; /* number of IRQs */
#ifdef CONFIG_ZONE_DMA
unsigned long dma_zone_size; /* size of DMA-able area */
#endif
unsigned int video_start; /* start of video RAM */ unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */ unsigned int video_end; /* end of video RAM */
......
...@@ -24,6 +24,8 @@ enum arm_perf_pmu_ids { ...@@ -24,6 +24,8 @@ enum arm_perf_pmu_ids {
ARM_PERF_PMU_ID_V6MP, ARM_PERF_PMU_ID_V6MP,
ARM_PERF_PMU_ID_CA8, ARM_PERF_PMU_ID_CA8,
ARM_PERF_PMU_ID_CA9, ARM_PERF_PMU_ID_CA9,
ARM_PERF_PMU_ID_CA5,
ARM_PERF_PMU_ID_CA15,
ARM_NUM_PMU_IDS, ARM_NUM_PMU_IDS,
}; };
......
...@@ -69,8 +69,9 @@ ...@@ -69,8 +69,9 @@
#define PSR_c 0x000000ff /* Control */ #define PSR_c 0x000000ff /* Control */
/* /*
* ARMv7 groups of APSR bits * ARMv7 groups of PSR bits
*/ */
#define APSR_MASK 0xf80f0000 /* N, Z, C, V, Q and GE flags */
#define PSR_ISET_MASK 0x01000010 /* ISA state (J, T) mask */ #define PSR_ISET_MASK 0x01000010 /* ISA state (J, T) mask */
#define PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */ #define PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */
#define PSR_ENDIAN_MASK 0x00000200 /* Endianness state mask */ #define PSR_ENDIAN_MASK 0x00000200 /* Endianness state mask */
...@@ -199,6 +200,14 @@ extern unsigned long profile_pc(struct pt_regs *regs); ...@@ -199,6 +200,14 @@ extern unsigned long profile_pc(struct pt_regs *regs);
#define predicate(x) ((x) & 0xf0000000) #define predicate(x) ((x) & 0xf0000000)
#define PREDICATE_ALWAYS 0xe0000000 #define PREDICATE_ALWAYS 0xe0000000
/*
* True if instr is a 32-bit thumb instruction. This works if instr
* is the first or only half-word of a thumb instruction. It also works
* when instr holds all 32-bits of a wide thumb instruction if stored
* in the form (first_half<<16)|(second_half)
*/
#define is_wide_instruction(instr) ((unsigned)(instr) >= 0xe800)
/* /*
* kprobe-based event tracer support * kprobe-based event tracer support
*/ */
......
...@@ -37,7 +37,12 @@ obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o ...@@ -37,7 +37,12 @@ obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o
ifdef CONFIG_THUMB2_KERNEL
obj-$(CONFIG_KPROBES) += kprobes-thumb.o
else
obj-$(CONFIG_KPROBES) += kprobes-arm.o
endif
obj-$(CONFIG_ATAGS_PROC) += atags.o obj-$(CONFIG_ATAGS_PROC) += atags.o
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
......
...@@ -377,7 +377,7 @@ ENDPROC(__pabt_svc) ...@@ -377,7 +377,7 @@ ENDPROC(__pabt_svc)
.endm .endm
.macro kuser_cmpxchg_check .macro kuser_cmpxchg_check
#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG) #if !defined(CONFIG_CPU_32v6K) && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
#ifndef CONFIG_MMU #ifndef CONFIG_MMU
#warning "NPTL on non MMU needs fixing" #warning "NPTL on non MMU needs fixing"
#else #else
...@@ -386,7 +386,7 @@ ENDPROC(__pabt_svc) ...@@ -386,7 +386,7 @@ ENDPROC(__pabt_svc)
@ perform a quick test inline since it should be false @ perform a quick test inline since it should be false
@ 99.9999% of the time. The rest is done out of line. @ 99.9999% of the time. The rest is done out of line.
cmp r4, #TASK_SIZE cmp r4, #TASK_SIZE
blhs kuser_cmpxchg_fixup blhs kuser_cmpxchg64_fixup
#endif #endif
#endif #endif
.endm .endm
...@@ -701,31 +701,12 @@ ENDPROC(__switch_to) ...@@ -701,31 +701,12 @@ ENDPROC(__switch_to)
/* /*
* User helpers. * User helpers.
* *
* These are segment of kernel provided user code reachable from user space
* at a fixed address in kernel memory. This is used to provide user space
* with some operations which require kernel help because of unimplemented
* native feature and/or instructions in many ARM CPUs. The idea is for
* this code to be executed directly in user mode for best efficiency but
* which is too intimate with the kernel counter part to be left to user
* libraries. In fact this code might even differ from one CPU to another
* depending on the available instruction set and restrictions like on
* SMP systems. In other words, the kernel reserves the right to change
* this code as needed without warning. Only the entry points and their
* results are guaranteed to be stable.
*
* Each segment is 32-byte aligned and will be moved to the top of the high * Each segment is 32-byte aligned and will be moved to the top of the high
* vector page. New segments (if ever needed) must be added in front of * vector page. New segments (if ever needed) must be added in front of
* existing ones. This mechanism should be used only for things that are * existing ones. This mechanism should be used only for things that are
* really small and justified, and not be abused freely. * really small and justified, and not be abused freely.
* *
* User space is expected to implement those things inline when optimizing * See Documentation/arm/kernel_user_helpers.txt for formal definitions.
* for a processor that has the necessary native support, but only if such
* resulting binaries are already to be incompatible with earlier ARM
* processors due to the use of unsupported instructions other than what
* is provided here. In other words don't make binaries unable to run on
* earlier processors just for the sake of not using these kernel helpers
* if your compiled code is not going to use the new instructions for other
* purpose.
*/ */
THUMB( .arm ) THUMB( .arm )
...@@ -742,96 +723,103 @@ ENDPROC(__switch_to) ...@@ -742,96 +723,103 @@ ENDPROC(__switch_to)
__kuser_helper_start: __kuser_helper_start:
/* /*
* Reference prototype: * Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
* * kuser "slots", therefore 0xffff0f80 is not used as a valid entry point.
* void __kernel_memory_barrier(void)
*
* Input:
*
* lr = return address
*
* Output:
*
* none
*
* Clobbered:
*
* none
*
* Definition and user space usage example:
*
* typedef void (__kernel_dmb_t)(void);
* #define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
*
* Apply any needed memory barrier to preserve consistency with data modified
* manually and __kuser_cmpxchg usage.
*
* This could be used as follows:
*
* #define __kernel_dmb() \
* asm volatile ( "mov r0, #0xffff0fff; mov lr, pc; sub pc, r0, #95" \
* : : : "r0", "lr","cc" )
*/ */
__kuser_memory_barrier: @ 0xffff0fa0 __kuser_cmpxchg64: @ 0xffff0f60
#if defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
/*
* Poor you. No fast solution possible...
* The kernel itself must perform the operation.
* A special ghost syscall is used for that (see traps.c).
*/
stmfd sp!, {r7, lr}
ldr r7, 1f @ it's 20 bits
swi __ARM_NR_cmpxchg64
ldmfd sp!, {r7, pc}
1: .word __ARM_NR_cmpxchg64
#elif defined(CONFIG_CPU_32v6K)
stmfd sp!, {r4, r5, r6, r7}
ldrd r4, r5, [r0] @ load old val
ldrd r6, r7, [r1] @ load new val
smp_dmb arm
1: ldrexd r0, r1, [r2] @ load current val
eors r3, r0, r4 @ compare with oldval (1)
eoreqs r3, r1, r5 @ compare with oldval (2)
strexdeq r3, r6, r7, [r2] @ store newval if eq
teqeq r3, #1 @ success?
beq 1b @ if no then retry
smp_dmb arm smp_dmb arm
rsbs r0, r3, #0 @ set returned val and C flag
ldmfd sp!, {r4, r5, r6, r7}
bx lr
#elif !defined(CONFIG_SMP)
#ifdef CONFIG_MMU
/*
* The only thing that can break atomicity in this cmpxchg64
* implementation is either an IRQ or a data abort exception
* causing another process/thread to be scheduled in the middle of
* the critical sequence. The same strategy as for cmpxchg is used.
*/
stmfd sp!, {r4, r5, r6, lr}
ldmia r0, {r4, r5} @ load old val
ldmia r1, {r6, lr} @ load new val
1: ldmia r2, {r0, r1} @ load current val
eors r3, r0, r4 @ compare with oldval (1)
eoreqs r3, r1, r5 @ compare with oldval (2)
2: stmeqia r2, {r6, lr} @ store newval if eq
rsbs r0, r3, #0 @ set return val and C flag
ldmfd sp!, {r4, r5, r6, pc}
.text
kuser_cmpxchg64_fixup:
@ Called from kuser_cmpxchg_fixup.
@ r4 = address of interrupted insn (must be preserved).
@ sp = saved regs. r7 and r8 are clobbered.
@ 1b = first critical insn, 2b = last critical insn.
@ If r4 >= 1b and r4 <= 2b then saved pc_usr is set to 1b.
mov r7, #0xffff0fff
sub r7, r7, #(0xffff0fff - (0xffff0f60 + (1b - __kuser_cmpxchg64)))
subs r8, r4, r7
rsbcss r8, r8, #(2b - 1b)
strcs r7, [sp, #S_PC]
#if __LINUX_ARM_ARCH__ < 6
bcc kuser_cmpxchg32_fixup
#endif
mov pc, lr
.previous
#else
#warning "NPTL on non MMU needs fixing"
mov r0, #-1
adds r0, r0, #0
usr_ret lr usr_ret lr
#endif
#else
#error "incoherent kernel configuration"
#endif
/* pad to next slot */
.rept (16 - (. - __kuser_cmpxchg64)/4)
.word 0
.endr
.align 5 .align 5
/* __kuser_memory_barrier: @ 0xffff0fa0
* Reference prototype: smp_dmb arm
* usr_ret lr
* int __kernel_cmpxchg(int oldval, int newval, int *ptr)
* .align 5
* Input:
*
* r0 = oldval
* r1 = newval
* r2 = ptr
* lr = return address
*
* Output:
*
* r0 = returned value (zero or non-zero)
* C flag = set if r0 == 0, clear if r0 != 0
*
* Clobbered:
*
* r3, ip, flags
*
* Definition and user space usage example:
*
* typedef int (__kernel_cmpxchg_t)(int oldval, int newval, int *ptr);
* #define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
*
* Atomically store newval in *ptr if *ptr is equal to oldval for user space.
* Return zero if *ptr was changed or non-zero if no exchange happened.
* The C flag is also set if *ptr was changed to allow for assembly
* optimization in the calling code.
*
* Notes:
*
* - This routine already includes memory barriers as needed.
*
* For example, a user space atomic_add implementation could look like this:
*
* #define atomic_add(ptr, val) \
* ({ register unsigned int *__ptr asm("r2") = (ptr); \
* register unsigned int __result asm("r1"); \
* asm volatile ( \
* "1: @ atomic_add\n\t" \
* "ldr r0, [r2]\n\t" \
* "mov r3, #0xffff0fff\n\t" \
* "add lr, pc, #4\n\t" \
* "add r1, r0, %2\n\t" \
* "add pc, r3, #(0xffff0fc0 - 0xffff0fff)\n\t" \
* "bcc 1b" \
* : "=&r" (__result) \
* : "r" (__ptr), "rIL" (val) \
* : "r0","r3","ip","lr","cc","memory" ); \
* __result; })
*/
__kuser_cmpxchg: @ 0xffff0fc0 __kuser_cmpxchg: @ 0xffff0fc0
...@@ -868,7 +856,7 @@ __kuser_cmpxchg: @ 0xffff0fc0 ...@@ -868,7 +856,7 @@ __kuser_cmpxchg: @ 0xffff0fc0
usr_ret lr usr_ret lr
.text .text
kuser_cmpxchg_fixup: kuser_cmpxchg32_fixup:
@ Called from kuser_cmpxchg_check macro. @ Called from kuser_cmpxchg_check macro.
@ r4 = address of interrupted insn (must be preserved). @ r4 = address of interrupted insn (must be preserved).
@ sp = saved regs. r7 and r8 are clobbered. @ sp = saved regs. r7 and r8 are clobbered.
...@@ -906,39 +894,6 @@ kuser_cmpxchg_fixup: ...@@ -906,39 +894,6 @@ kuser_cmpxchg_fixup:
.align 5 .align 5
/*
* Reference prototype:
*
* int __kernel_get_tls(void)
*
* Input:
*
* lr = return address
*
* Output:
*
* r0 = TLS value
*
* Clobbered:
*
* none
*
* Definition and user space usage example:
*
* typedef int (__kernel_get_tls_t)(void);
* #define __kernel_get_tls (*(__kernel_get_tls_t *)0xffff0fe0)
*
* Get the TLS value as previously set via the __ARM_NR_set_tls syscall.
*
* This could be used as follows:
*
* #define __kernel_get_tls() \
* ({ register unsigned int __val asm("r0"); \
* asm( "mov r0, #0xffff0fff; mov lr, pc; sub pc, r0, #31" \
* : "=r" (__val) : : "lr","cc" ); \
* __val; })
*/
__kuser_get_tls: @ 0xffff0fe0 __kuser_get_tls: @ 0xffff0fe0
ldr r0, [pc, #(16 - 8)] @ read TLS, set in kuser_get_tls_init ldr r0, [pc, #(16 - 8)] @ read TLS, set in kuser_get_tls_init
usr_ret lr usr_ret lr
...@@ -947,19 +902,6 @@ __kuser_get_tls: @ 0xffff0fe0 ...@@ -947,19 +902,6 @@ __kuser_get_tls: @ 0xffff0fe0
.word 0 @ 0xffff0ff0 software TLS value, then .word 0 @ 0xffff0ff0 software TLS value, then
.endr @ pad up to __kuser_helper_version .endr @ pad up to __kuser_helper_version
/*
* Reference declaration:
*
* extern unsigned int __kernel_helper_version;
*
* Definition and user space usage example:
*
* #define __kernel_helper_version (*(unsigned int *)0xffff0ffc)
*
* User space may read this to determine the curent number of helpers
* available.
*/
__kuser_helper_version: @ 0xffff0ffc __kuser_helper_version: @ 0xffff0ffc
.word ((__kuser_helper_end - __kuser_helper_start) >> 5) .word ((__kuser_helper_end - __kuser_helper_start) >> 5)
......
...@@ -121,15 +121,13 @@ ...@@ -121,15 +121,13 @@
.endm .endm
#else /* CONFIG_THUMB2_KERNEL */ #else /* CONFIG_THUMB2_KERNEL */
.macro svc_exit, rpsr .macro svc_exit, rpsr
ldr lr, [sp, #S_SP] @ top of the stack
ldrd r0, r1, [sp, #S_LR] @ calling lr and pc
clrex @ clear the exclusive monitor clrex @ clear the exclusive monitor
ldr r0, [sp, #S_SP] @ top of the stack stmdb lr!, {r0, r1, \rpsr} @ calling lr and rfe context
ldr r1, [sp, #S_PC] @ return address
tst r0, #4 @ orig stack 8-byte aligned?
stmdb r0, {r1, \rpsr} @ rfe context
ldmia sp, {r0 - r12} ldmia sp, {r0 - r12}
ldr lr, [sp, #S_LR] mov sp, lr
addeq sp, sp, #S_FRAME_SIZE - 8 @ aligned ldr lr, [sp], #4
addne sp, sp, #S_FRAME_SIZE - 4 @ not aligned
rfeia sp! rfeia sp!
.endm .endm
......
/*
* arch/arm/kernel/kprobes-decode.c
*
* Copyright (C) 2006, 2007 Motorola Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
/*
* We do not have hardware single-stepping on ARM, This
* effort is further complicated by the ARM not having a
* "next PC" register. Instructions that change the PC
* can't be safely single-stepped in a MP environment, so
* we have a lot of work to do:
*
* In the prepare phase:
* *) If it is an instruction that does anything
* with the CPU mode, we reject it for a kprobe.
* (This is out of laziness rather than need. The
* instructions could be simulated.)
*
* *) Otherwise, decode the instruction rewriting its
* registers to take fixed, ordered registers and
* setting a handler for it to run the instruction.
*
* In the execution phase by an instruction's handler:
*
* *) If the PC is written to by the instruction, the
* instruction must be fully simulated in software.
*
* *) Otherwise, a modified form of the instruction is
* directly executed. Its handler calls the
* instruction in insn[0]. In insn[1] is a
* "mov pc, lr" to return.
*
* Before calling, load up the reordered registers
* from the original instruction's registers. If one
* of the original input registers is the PC, compute
* and adjust the appropriate input register.
*
* After call completes, copy the output registers to
* the original instruction's original registers.
*
* We don't use a real breakpoint instruction since that
* would have us in the kernel go from SVC mode to SVC
* mode losing the link register. Instead we use an
* undefined instruction. To simplify processing, the
* undefined instruction used for kprobes must be reserved
* exclusively for kprobes use.
*
* TODO: ifdef out some instruction decoding based on architecture.
*/
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include "kprobes.h"
#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
#if __LINUX_ARM_ARCH__ >= 6
#define BLX(reg) "blx "reg" \n\t"
#else
#define BLX(reg) "mov lr, pc \n\t" \
"mov pc, "reg" \n\t"
#endif
/*
* To avoid the complications of mimicing single-stepping on a
* processor without a Next-PC or a single-step mode, and to
* avoid having to deal with the side-effects of boosting, we
* simulate or emulate (almost) all ARM instructions.
*
* "Simulation" is where the instruction's behavior is duplicated in
* C code. "Emulation" is where the original instruction is rewritten
* and executed, often by altering its registers.
*
* By having all behavior of the kprobe'd instruction completed before
* returning from the kprobe_handler(), all locks (scheduler and
* interrupt) can safely be released. There is no need for secondary
* breakpoints, no race with MP or preemptable kernels, nor having to
* clean up resources counts at a later time impacting overall system
* performance. By rewriting the instruction, only the minimum registers
* need to be loaded and saved back optimizing performance.
*
* Calling the insnslot_*_rwflags version of a function doesn't hurt
* anything even when the CPSR flags aren't updated by the
* instruction. It's just a little slower in return for saving
* a little space by not having a duplicate function that doesn't
* update the flags. (The same optimization can be said for
* instructions that do or don't perform register writeback)
* Also, instructions can either read the flags, only write the
* flags, or read and write the flags. To save combinations
* rather than for sheer performance, flag functions just assume
* read and write of flags.
*/
static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
int disp = branch_displacement(insn);
if (insn & (1 << 24))
regs->ARM_lr = iaddr + 4;
regs->ARM_pc = iaddr + 8 + disp;
}
static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
int disp = branch_displacement(insn);
regs->ARM_lr = iaddr + 4;
regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
regs->ARM_cpsr |= PSR_T_BIT;
}
static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rm = insn & 0xf;
long rmv = regs->uregs[rm];
if (insn & (1 << 5))
regs->ARM_lr = (long)p->addr + 4;
regs->ARM_pc = rmv & ~0x1;
regs->ARM_cpsr &= ~PSR_T_BIT;
if (rmv & 0x1)
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_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
{
regs->uregs[12] = regs->uregs[13];
}
static void __kprobes
emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = (unsigned long)p->addr + 8;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rtv asm("r0") = regs->uregs[rt];
register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
register unsigned long rnv asm("r2") = (rn == 15) ? pc
: regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
[fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rt] = rtv;
regs->uregs[rt+1] = rt2v;
if (is_writeback(insn))
regs->uregs[rn] = rnv;
}
static void __kprobes
emulate_ldr(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = (unsigned long)p->addr + 8;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rtv asm("r0");
register unsigned long rnv asm("r2") = (rn == 15) ? pc
: regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rtv), "=r" (rnv)
: "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
if (rt == 15)
load_write_pc(rtv, regs);
else
regs->uregs[rt] = rtv;
if (is_writeback(insn))
regs->uregs[rn] = rnv;
}
static void __kprobes
emulate_str(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
unsigned long rnpc = (unsigned long)p->addr + 8;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
: regs->uregs[rt];
register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
: regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rnv)
: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
if (is_writeback(insn))
regs->uregs[rn] = rnv;
}
static void __kprobes
emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = (unsigned long)p->addr + 8;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
int rs = (insn >> 8) & 0xf;
register unsigned long rdv asm("r0") = regs->uregs[rd];
register unsigned long rnv asm("r2") = (rn == 15) ? pc
: regs->uregs[rn];
register unsigned long rmv asm("r3") = (rm == 15) ? pc
: regs->uregs[rm];
register unsigned long rsv asm("r1") = regs->uregs[rs];
unsigned long cpsr = regs->ARM_cpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
BLX("%[fn]")
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
"1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
if (rd == 15)
alu_write_pc(rdv, regs);
else
regs->uregs[rd] = rdv;
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
static void __kprobes
emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rdv asm("r0") = regs->uregs[rd];
register unsigned long rnv asm("r2") = regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
unsigned long cpsr = regs->ARM_cpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
BLX("%[fn]")
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv),
"1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rd] = rdv;
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
static void __kprobes
emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 16) & 0xf;
int rn = (insn >> 12) & 0xf;
int rm = insn & 0xf;
int rs = (insn >> 8) & 0xf;
register unsigned long rdv asm("r2") = regs->uregs[rd];
register unsigned long rnv asm("r0") = regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
register unsigned long rsv asm("r1") = regs->uregs[rs];
unsigned long cpsr = regs->ARM_cpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
BLX("%[fn]")
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
"1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rd] = rdv;
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
static void __kprobes
emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 12) & 0xf;
int rm = insn & 0xf;
register unsigned long rdv asm("r0") = regs->uregs[rd];
register unsigned long rmv asm("r3") = regs->uregs[rm];
__asm__ __volatile__ (
BLX("%[fn]")
: "=r" (rdv)
: "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rd] = rdv;
}
static void __kprobes
emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rdlo = (insn >> 12) & 0xf;
int rdhi = (insn >> 16) & 0xf;
int rn = insn & 0xf;
int rm = (insn >> 8) & 0xf;
register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
register unsigned long rnv asm("r3") = regs->uregs[rn];
register unsigned long rmv asm("r1") = regs->uregs[rm];
unsigned long cpsr = regs->ARM_cpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
BLX("%[fn]")
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
"2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rdlo] = rdlov;
regs->uregs[rdhi] = rdhiv;
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
/*
* For the instruction masking and comparisons in all the "space_*"
* functions below, Do _not_ rearrange the order of tests unless
* you're very, very sure of what you are doing. For the sake of
* efficiency, the masks for some tests sometimes assume other test
* have been done prior to them so the number of patterns to test
* for an instruction set can be as broad as possible to reduce the
* number of tests needed.
*/
static const union decode_item arm_1111_table[] = {
/* Unconditional instructions */
/* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */
/* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
/* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
/* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
DECODE_SIMULATE (0xfe300000, 0xf4100000, kprobe_simulate_nop),
/* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
/* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
/* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
/* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
DECODE_SIMULATE (0xfe300010, 0xf6100000, kprobe_simulate_nop),
/* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
DECODE_SIMULATE (0xfe000000, 0xfa000000, simulate_blx1),
/* 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 */
/* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */
/* 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 */
/* Other unallocated instructions... */
DECODE_END
};
static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
/* Miscellaneous instructions */
/* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
DECODE_SIMULATEX(0x0ff000f0, 0x01000000, simulate_mrs,
REGS(0, NOPC, 0, 0, 0)),
/* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
DECODE_SIMULATE (0x0ff000f0, 0x01200010, simulate_blx2bx),
/* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
DECODE_SIMULATEX(0x0ff000f0, 0x01200030, simulate_blx2bx,
REGS(0, 0, 0, 0, NOPC)),
/* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
DECODE_EMULATEX (0x0ff000f0, 0x01600010, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, NOPC)),
/* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
/* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
/* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
/* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
DECODE_EMULATEX (0x0f9000f0, 0x01000050, emulate_rd12rn16rm0_rwflags_nopc,
REGS(NOPC, NOPC, 0, 0, NOPC)),
/* 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 */
/* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
/* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */
/* And unallocated instructions... */
DECODE_END
};
static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
/* Halfword multiply and multiply-accumulate */
/* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
DECODE_EMULATEX (0x0ff00090, 0x01400080, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
/* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
DECODE_OR (0x0ff000b0, 0x012000a0),
/* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
DECODE_EMULATEX (0x0ff00090, 0x01600080, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, 0, NOPC, 0, NOPC)),
/* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
DECODE_OR (0x0ff00090, 0x01000080),
/* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
DECODE_EMULATEX (0x0ff000b0, 0x01200080, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
DECODE_END
};
static const union decode_item arm_cccc_0000_____1001_table[] = {
/* Multiply and multiply-accumulate */
/* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
/* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
DECODE_EMULATEX (0x0fe000f0, 0x00000090, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, 0, NOPC, 0, NOPC)),
/* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
/* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
DECODE_OR (0x0fe000f0, 0x00200090),
/* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
DECODE_EMULATEX (0x0ff000f0, 0x00600090, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
/* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
DECODE_OR (0x0ff000f0, 0x00400090),
/* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */
/* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */
/* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */
/* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */
/* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */
/* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
/* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
/* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
DECODE_EMULATEX (0x0f8000f0, 0x00800090, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
DECODE_END
};
static const union decode_item arm_cccc_0001_____1001_table[] = {
/* Synchronization primitives */
/* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
DECODE_EMULATEX (0x0fb000f0, 0x01000090, emulate_rd12rn16rm0_rwflags_nopc,
REGS(NOPC, NOPC, 0, 0, NOPC)),
/* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
/* And unallocated instructions... */
DECODE_END
};
static const union decode_item arm_cccc_000x_____1xx1_table[] = {
/* Extra load/store instructions */
/* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */
/* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */
/* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */
/* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */
/* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */
DECODE_REJECT (0x0f200090, 0x00200090),
/* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */
DECODE_REJECT (0x0e10e0d0, 0x0000e0d0),
/* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
/* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
DECODE_EMULATEX (0x0e5000d0, 0x000000d0, emulate_ldrdstrd,
REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
/* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
/* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
DECODE_EMULATEX (0x0e5000d0, 0x004000d0, emulate_ldrdstrd,
REGS(NOPCWB, NOPCX, 0, 0, 0)),
/* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
DECODE_EMULATEX (0x0e5000f0, 0x000000b0, emulate_str,
REGS(NOPCWB, NOPC, 0, 0, NOPC)),
/* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
/* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
/* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
DECODE_EMULATEX (0x0e500090, 0x00100090, emulate_ldr,
REGS(NOPCWB, NOPC, 0, 0, NOPC)),
/* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
DECODE_EMULATEX (0x0e5000f0, 0x004000b0, emulate_str,
REGS(NOPCWB, NOPC, 0, 0, 0)),
/* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
/* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
/* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
DECODE_EMULATEX (0x0e500090, 0x00500090, emulate_ldr,
REGS(NOPCWB, NOPC, 0, 0, 0)),
DECODE_END
};
static const union decode_item arm_cccc_000x_table[] = {
/* Data-processing (register) */
/* <op>S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */
DECODE_REJECT (0x0e10f000, 0x0010f000),
/* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */
DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, simulate_mov_ipsp),
/* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
/* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
/* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
/* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
DECODE_EMULATEX (0x0f900010, 0x01100000, emulate_rd12rn16rm0rs8_rwflags,
REGS(ANY, 0, 0, 0, ANY)),
/* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
/* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
DECODE_EMULATEX (0x0fa00010, 0x01a00000, emulate_rd12rn16rm0rs8_rwflags,
REGS(0, ANY, 0, 0, ANY)),
/* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
/* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */
/* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */
/* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */
/* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */
/* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */
/* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */
/* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
/* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
/* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
DECODE_EMULATEX (0x0e000010, 0x00000000, emulate_rd12rn16rm0rs8_rwflags,
REGS(ANY, ANY, 0, 0, ANY)),
/* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
/* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
/* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
/* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
DECODE_EMULATEX (0x0f900090, 0x01100010, emulate_rd12rn16rm0rs8_rwflags,
REGS(ANY, 0, NOPC, 0, ANY)),
/* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
/* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
DECODE_EMULATEX (0x0fa00090, 0x01a00010, emulate_rd12rn16rm0rs8_rwflags,
REGS(0, ANY, NOPC, 0, ANY)),
/* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
/* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */
/* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */
/* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */
/* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */
/* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */
/* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */
/* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
/* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
/* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
DECODE_EMULATEX (0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
REGS(ANY, ANY, NOPC, 0, ANY)),
DECODE_END
};
static const union decode_item arm_cccc_001x_table[] = {
/* Data-processing (immediate) */
/* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
/* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0fb00000, 0x03000000, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, 0)),
/* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
DECODE_OR (0x0fff00ff, 0x03200001),
/* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
DECODE_EMULATE (0x0fff00ff, 0x03200004, kprobe_emulate_none),
/* 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 */
DECODE_SIMULATE (0x0fff00fc, 0x03200000, kprobe_simulate_nop),
/* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
/* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
/* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0x0fb00000, 0x03200000),
/* <op>S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */
DECODE_REJECT (0x0e10f000, 0x0210f000),
/* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */
/* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
/* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
/* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0f900000, 0x03100000, emulate_rd12rn16rm0rs8_rwflags,
REGS(ANY, 0, 0, 0, 0)),
/* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
/* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0fa00000, 0x03a00000, emulate_rd12rn16rm0rs8_rwflags,
REGS(0, ANY, 0, 0, 0)),
/* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
/* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */
/* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */
/* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */
/* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */
/* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */
/* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */
/* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
/* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
/* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0e000000, 0x02000000, emulate_rd12rn16rm0rs8_rwflags,
REGS(ANY, ANY, 0, 0, 0)),
DECODE_END
};
static const union decode_item arm_cccc_0110_____xxx1_table[] = {
/* Media instructions */
/* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
DECODE_EMULATEX (0x0ff000f0, 0x068000b0, emulate_rd12rn16rm0_rwflags_nopc,
REGS(NOPC, NOPC, 0, 0, NOPC)),
/* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
/* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */
DECODE_OR(0x0fa00030, 0x06a00010),
/* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
/* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
DECODE_EMULATEX (0x0fb000f0, 0x06a00030, emulate_rd12rn16rm0_rwflags_nopc,
REGS(0, NOPC, 0, 0, NOPC)),
/* 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 */
DECODE_EMULATEX (0x0fb00070, 0x06b00030, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, NOPC)),
/* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
DECODE_REJECT (0x0fb00010, 0x06000010),
/* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */
DECODE_REJECT (0x0f8000f0, 0x060000b0),
/* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */
DECODE_REJECT (0x0f8000f0, 0x060000d0),
/* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */
/* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */
/* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */
/* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */
/* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */
/* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */
/* 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 */
/* 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 */
/* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */
/* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */
/* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */
/* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */
/* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */
/* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */
/* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */
/* 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 */
/* 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 */
/* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */
DECODE_EMULATEX (0x0f800010, 0x06000010, emulate_rd12rn16rm0_rwflags_nopc,
REGS(NOPC, NOPC, 0, 0, NOPC)),
/* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
/* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
DECODE_EMULATEX (0x0ff00030, 0x06800010, emulate_rd12rn16rm0_rwflags_nopc,
REGS(NOPC, NOPC, 0, 0, NOPC)),
/* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
/* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */
DECODE_REJECT (0x0fb000f0, 0x06900070),
/* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */
/* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */
/* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */
/* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
/* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
/* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, NOPC)),
/* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */
/* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */
/* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */
/* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */
/* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */
/* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */
DECODE_EMULATEX (0x0f8000f0, 0x06800070, emulate_rd12rn16rm0_rwflags_nopc,
REGS(NOPCX, NOPC, 0, 0, NOPC)),
DECODE_END
};
static const union decode_item arm_cccc_0111_____xxx1_table[] = {
/* Media instructions */
/* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
DECODE_REJECT (0x0ff000f0, 0x07f000f0),
/* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
/* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
DECODE_EMULATEX (0x0ff00090, 0x07400010, emulate_rdlo12rdhi16rn0rm8_rwflags_nopc,
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
/* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
/* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */
DECODE_OR (0x0ff0f090, 0x0700f010),
/* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
DECODE_OR (0x0ff0f0d0, 0x0750f010),
/* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, 0, NOPC, 0, NOPC)),
/* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
/* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */
DECODE_OR (0x0ff00090, 0x07000010),
/* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
DECODE_OR (0x0ff000d0, 0x07500010),
/* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
DECODE_EMULATEX (0x0ff000f0, 0x07800010, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
/* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
DECODE_EMULATEX (0x0ff000d0, 0x075000d0, emulate_rd16rn12rm0rs8_rwflags_nopc,
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
/* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
/* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
DECODE_EMULATEX (0x0fa00070, 0x07a00050, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, NOPC)),
/* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */
DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, 0)),
/* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
DECODE_EMULATEX (0x0fe00070, 0x07c00010, emulate_rd12rm0_noflags_nopc,
REGS(0, NOPC, 0, 0, NOPCX)),
DECODE_END
};
static const union decode_item arm_cccc_01xx_table[] = {
/* Load/store word and unsigned byte */
/* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0x0c40f000, 0x0440f000),
/* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */
/* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */
/* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */
/* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0x0d200000, 0x04200000),
/* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
/* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0e100000, 0x04000000, emulate_str,
REGS(NOPCWB, ANY, 0, 0, 0)),
/* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
/* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0e100000, 0x04100000, emulate_ldr,
REGS(NOPCWB, ANY, 0, 0, 0)),
/* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
/* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0e100000, 0x06000000, emulate_str,
REGS(NOPCWB, ANY, 0, 0, NOPC)),
/* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
/* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0x0e100000, 0x06100000, emulate_ldr,
REGS(NOPCWB, ANY, 0, 0, NOPC)),
DECODE_END
};
static const union decode_item arm_cccc_100x_table[] = {
/* Block data transfer instructions */
/* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
/* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
DECODE_CUSTOM (0x0e400000, 0x08000000, kprobe_decode_ldmstm),
/* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
/* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
/* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */
DECODE_END
};
const union decode_item kprobe_decode_arm_table[] = {
/*
* Unconditional instructions
* 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table),
/*
* Miscellaneous instructions
* cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx
*/
DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table),
/*
* Halfword multiply and multiply-accumulate
* cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx
*/
DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table),
/*
* Multiply and multiply-accumulate
* cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx
*/
DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table),
/*
* Synchronization primitives
* cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx
*/
DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table),
/*
* Extra load/store instructions
* cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx
*/
DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table),
/*
* Data-processing (register)
* cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx
* Data-processing (register-shifted register)
* cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx
*/
DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table),
/*
* Data-processing (immediate)
* cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table),
/*
* Media instructions
* cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx
*/
DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table),
DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table),
/*
* Load/store word and unsigned byte
* cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table),
/*
* Block data transfer instructions
* cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table),
/* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
/* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
DECODE_SIMULATE (0x0e000000, 0x0a000000, simulate_bbl),
/*
* Supervisor Call, and coprocessor instructions
*/
/* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */
/* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */
/* 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 */
DECODE_REJECT (0x0c000000, 0x0c000000),
DECODE_END
};
static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_pc += 4;
p->ainsn.insn_handler(p, regs);
}
/* Return:
* INSN_REJECTED If instruction is one not allowed to kprobe,
* INSN_GOOD If instruction is supported and uses instruction slot,
* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
*
* For instructions we don't want to kprobe (INSN_REJECTED return result):
* These are generally ones that modify the processor state making
* them "hard" to simulate such as switches processor modes or
* make accesses in alternate modes. Any of these could be simulated
* if the work was put into it, but low return considering they
* should also be very rare.
*/
enum kprobe_insn __kprobes
arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
asi->insn_singlestep = arm_singlestep;
asi->insn_check_cc = kprobe_condition_checks[insn>>28];
return kprobe_decode_insn(insn, asi, kprobe_decode_arm_table, false);
}
/*
* arch/arm/kernel/kprobes-common.c
*
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
*
* Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
* Copyright (C) 2006, 2007 Motorola Inc.
*
* 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/kernel.h>
#include <linux/kprobes.h>
#include "kprobes.h"
#ifndef find_str_pc_offset
/*
* For STR and STM instructions, an ARM core may choose to use either
* a +8 or a +12 displacement from the current instruction's address.
* Whichever value is chosen for a given core, it must be the same for
* both instructions and may not change. This function measures it.
*/
int str_pc_offset;
void __init find_str_pc_offset(void)
{
int addr, scratch, ret;
__asm__ (
"sub %[ret], pc, #4 \n\t"
"str pc, %[addr] \n\t"
"ldr %[scr], %[addr] \n\t"
"sub %[ret], %[scr], %[ret] \n\t"
: [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr));
str_pc_offset = ret;
}
#endif /* !find_str_pc_offset */
#ifndef test_load_write_pc_interworking
bool load_write_pc_interworks;
void __init test_load_write_pc_interworking(void)
{
int arch = cpu_architecture();
BUG_ON(arch == CPU_ARCH_UNKNOWN);
load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T;
}
#endif /* !test_load_write_pc_interworking */
#ifndef test_alu_write_pc_interworking
bool alu_write_pc_interworks;
void __init test_alu_write_pc_interworking(void)
{
int arch = cpu_architecture();
BUG_ON(arch == CPU_ARCH_UNKNOWN);
alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7;
}
#endif /* !test_alu_write_pc_interworking */
void __init arm_kprobe_decode_init(void)
{
find_str_pc_offset();
test_load_write_pc_interworking();
test_alu_write_pc_interworking();
}
static unsigned long __kprobes __check_eq(unsigned long cpsr)
{
return cpsr & PSR_Z_BIT;
}
static unsigned long __kprobes __check_ne(unsigned long cpsr)
{
return (~cpsr) & PSR_Z_BIT;
}
static unsigned long __kprobes __check_cs(unsigned long cpsr)
{
return cpsr & PSR_C_BIT;
}
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;
}
kprobe_check_cc * const kprobe_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
};
void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
{
}
void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
{
p->ainsn.insn_fn();
}
static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rn = (insn >> 16) & 0xf;
int lbit = insn & (1 << 20);
int wbit = insn & (1 << 21);
int ubit = insn & (1 << 23);
int pbit = insn & (1 << 24);
long *addr = (long *)regs->uregs[rn];
int reg_bit_vector;
int reg_count;
reg_count = 0;
reg_bit_vector = insn & 0xffff;
while (reg_bit_vector) {
reg_bit_vector &= (reg_bit_vector - 1);
++reg_count;
}
if (!ubit)
addr -= reg_count;
addr += (!pbit == !ubit);
reg_bit_vector = insn & 0xffff;
while (reg_bit_vector) {
int reg = __ffs(reg_bit_vector);
reg_bit_vector &= (reg_bit_vector - 1);
if (lbit)
regs->uregs[reg] = *addr++;
else
*addr++ = regs->uregs[reg];
}
if (wbit) {
if (!ubit)
addr -= reg_count;
addr -= (!pbit == !ubit);
regs->uregs[rn] = (long)addr;
}
}
static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_pc = (long)p->addr + str_pc_offset;
simulate_ldm1stm1(p, regs);
regs->ARM_pc = (long)p->addr + 4;
}
static void __kprobes simulate_ldm1_pc(struct kprobe *p, struct pt_regs *regs)
{
simulate_ldm1stm1(p, regs);
load_write_pc(regs->ARM_pc, regs);
}
static void __kprobes
emulate_generic_r0_12_noflags(struct kprobe *p, struct pt_regs *regs)
{
register void *rregs asm("r1") = regs;
register void *rfn asm("lr") = p->ainsn.insn_fn;
__asm__ __volatile__ (
"stmdb sp!, {%[regs], r11} \n\t"
"ldmia %[regs], {r0-r12} \n\t"
#if __LINUX_ARM_ARCH__ >= 6
"blx %[fn] \n\t"
#else
"str %[fn], [sp, #-4]! \n\t"
"adr lr, 1f \n\t"
"ldr pc, [sp], #4 \n\t"
"1: \n\t"
#endif
"ldr lr, [sp], #4 \n\t" /* lr = regs */
"stmia lr, {r0-r12} \n\t"
"ldr r11, [sp], #4 \n\t"
: [regs] "=r" (rregs), [fn] "=r" (rfn)
: "0" (rregs), "1" (rfn)
: "r0", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r12", "memory", "cc"
);
}
static void __kprobes
emulate_generic_r2_14_noflags(struct kprobe *p, struct pt_regs *regs)
{
emulate_generic_r0_12_noflags(p, (struct pt_regs *)(regs->uregs+2));
}
static void __kprobes
emulate_ldm_r3_15(struct kprobe *p, struct pt_regs *regs)
{
emulate_generic_r0_12_noflags(p, (struct pt_regs *)(regs->uregs+3));
load_write_pc(regs->ARM_pc, regs);
}
enum kprobe_insn __kprobes
kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
kprobe_insn_handler_t *handler = 0;
unsigned reglist = insn & 0xffff;
int is_ldm = insn & 0x100000;
int rn = (insn >> 16) & 0xf;
if (rn <= 12 && (reglist & 0xe000) == 0) {
/* Instruction only uses registers in the range R0..R12 */
handler = emulate_generic_r0_12_noflags;
} else if (rn >= 2 && (reglist & 0x8003) == 0) {
/* Instruction only uses registers in the range R2..R14 */
rn -= 2;
reglist >>= 2;
handler = emulate_generic_r2_14_noflags;
} else if (rn >= 3 && (reglist & 0x0007) == 0) {
/* Instruction only uses registers in the range R3..R15 */
if (is_ldm && (reglist & 0x8000)) {
rn -= 3;
reglist >>= 3;
handler = emulate_ldm_r3_15;
}
}
if (handler) {
/* We can emulate the instruction in (possibly) modified form */
asi->insn[0] = (insn & 0xfff00000) | (rn << 16) | reglist;
asi->insn_handler = handler;
return INSN_GOOD;
}
/* Fallback to slower simulation... */
if (reglist & 0x8000)
handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc;
else
handler = simulate_ldm1stm1;
asi->insn_handler = handler;
return INSN_GOOD_NO_SLOT;
}
/*
* Prepare an instruction slot to receive an instruction for emulating.
* This is done by placing a subroutine return after the location where the
* instruction will be placed. We also modify ARM instructions to be
* unconditional as the condition code will already be checked before any
* emulation handler is called.
*/
static kprobe_opcode_t __kprobes
prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
bool thumb)
{
#ifdef CONFIG_THUMB2_KERNEL
if (thumb) {
u16 *thumb_insn = (u16 *)asi->insn;
thumb_insn[1] = 0x4770; /* Thumb bx lr */
thumb_insn[2] = 0x4770; /* Thumb bx lr */
return insn;
}
asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
#else
asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
#endif
/* Make an ARM instruction unconditional */
if (insn < 0xe0000000)
insn = (insn | 0xe0000000) & ~0x10000000;
return insn;
}
/*
* Write a (probably modified) instruction into the slot previously prepared by
* prepare_emulated_insn
*/
static void __kprobes
set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
bool thumb)
{
#ifdef CONFIG_THUMB2_KERNEL
if (thumb) {
u16 *ip = (u16 *)asi->insn;
if (is_wide_instruction(insn))
*ip++ = insn >> 16;
*ip++ = insn;
return;
}
#endif
asi->insn[0] = insn;
}
/*
* When we modify the register numbers encoded in an instruction to be emulated,
* the new values come from this define. For ARM and 32-bit Thumb instructions
* this gives...
*
* bit position 16 12 8 4 0
* ---------------+---+---+---+---+---+
* register r2 r0 r1 -- r3
*/
#define INSN_NEW_BITS 0x00020103
/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
#define INSN_SAMEAS16_BITS 0x22222222
/*
* Validate and modify each of the registers encoded in an instruction.
*
* Each nibble in regs contains a value from enum decode_reg_type. For each
* non-zero value, the corresponding nibble in pinsn is validated and modified
* according to the type.
*/
static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
{
kprobe_opcode_t insn = *pinsn;
kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
for (; regs != 0; regs >>= 4, mask <<= 4) {
kprobe_opcode_t new_bits = INSN_NEW_BITS;
switch (regs & 0xf) {
case REG_TYPE_NONE:
/* Nibble not a register, skip to next */
continue;
case REG_TYPE_ANY:
/* Any register is allowed */
break;
case REG_TYPE_SAMEAS16:
/* Replace register with same as at bit position 16 */
new_bits = INSN_SAMEAS16_BITS;
break;
case REG_TYPE_SP:
/* Only allow SP (R13) */
if ((insn ^ 0xdddddddd) & mask)
goto reject;
break;
case REG_TYPE_PC:
/* Only allow PC (R15) */
if ((insn ^ 0xffffffff) & mask)
goto reject;
break;
case REG_TYPE_NOSP:
/* Reject SP (R13) */
if (((insn ^ 0xdddddddd) & mask) == 0)
goto reject;
break;
case REG_TYPE_NOSPPC:
case REG_TYPE_NOSPPCX:
/* Reject SP and PC (R13 and R15) */
if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
goto reject;
break;
case REG_TYPE_NOPCWB:
if (!is_writeback(insn))
break; /* No writeback, so any register is OK */
/* fall through... */
case REG_TYPE_NOPC:
case REG_TYPE_NOPCX:
/* Reject PC (R15) */
if (((insn ^ 0xffffffff) & mask) == 0)
goto reject;
break;
}
/* Replace value of nibble with new register number... */
insn &= ~mask;
insn |= new_bits & mask;
}
*pinsn = insn;
return true;
reject:
return false;
}
static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
[DECODE_TYPE_TABLE] = sizeof(struct decode_table),
[DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom),
[DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate),
[DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate),
[DECODE_TYPE_OR] = sizeof(struct decode_or),
[DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
};
/*
* kprobe_decode_insn operates on data tables in order to decode an ARM
* architecture instruction onto which a kprobe has been placed.
*
* These instruction decoding tables are a concatenation of entries each
* of which consist of one of the following structs:
*
* decode_table
* decode_custom
* decode_simulate
* decode_emulate
* decode_or
* decode_reject
*
* Each of these starts with a struct decode_header which has the following
* fields:
*
* type_regs
* mask
* value
*
* The least significant DECODE_TYPE_BITS of type_regs contains a value
* from enum decode_type, this indicates which of the decode_* structs
* the entry contains. The value DECODE_TYPE_END indicates the end of the
* table.
*
* When the table is parsed, each entry is checked in turn to see if it
* matches the instruction to be decoded using the test:
*
* (insn & mask) == value
*
* If no match is found before the end of the table is reached then decoding
* fails with INSN_REJECTED.
*
* When a match is found, decode_regs() is called to validate and modify each
* of the registers encoded in the instruction; the data it uses to do this
* is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
* to fail with INSN_REJECTED.
*
* Once the instruction has passed the above tests, further processing
* depends on the type of the table entry's decode struct.
*
*/
int __kprobes
kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
const union decode_item *table, bool thumb)
{
const struct decode_header *h = (struct decode_header *)table;
const struct decode_header *next;
bool matched = false;
insn = prepare_emulated_insn(insn, asi, thumb);
for (;; h = next) {
enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
if (type == DECODE_TYPE_END)
return INSN_REJECTED;
next = (struct decode_header *)
((uintptr_t)h + decode_struct_sizes[type]);
if (!matched && (insn & h->mask.bits) != h->value.bits)
continue;
if (!decode_regs(&insn, regs))
return INSN_REJECTED;
switch (type) {
case DECODE_TYPE_TABLE: {
struct decode_table *d = (struct decode_table *)h;
next = (struct decode_header *)d->table.table;
break;
}
case DECODE_TYPE_CUSTOM: {
struct decode_custom *d = (struct decode_custom *)h;
return (*d->decoder.decoder)(insn, asi);
}
case DECODE_TYPE_SIMULATE: {
struct decode_simulate *d = (struct decode_simulate *)h;
asi->insn_handler = d->handler.handler;
return INSN_GOOD_NO_SLOT;
}
case DECODE_TYPE_EMULATE: {
struct decode_emulate *d = (struct decode_emulate *)h;
asi->insn_handler = d->handler.handler;
set_emulated_insn(insn, asi, thumb);
return INSN_GOOD;
}
case DECODE_TYPE_OR:
matched = true;
break;
case DECODE_TYPE_REJECT:
default:
return INSN_REJECTED;
}
}
}
/*
* arch/arm/kernel/kprobes-decode.c
*
* Copyright (C) 2006, 2007 Motorola Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
/*
* We do not have hardware single-stepping on ARM, This
* effort is further complicated by the ARM not having a
* "next PC" register. Instructions that change the PC
* can't be safely single-stepped in a MP environment, so
* we have a lot of work to do:
*
* In the prepare phase:
* *) If it is an instruction that does anything
* with the CPU mode, we reject it for a kprobe.
* (This is out of laziness rather than need. The
* instructions could be simulated.)
*
* *) Otherwise, decode the instruction rewriting its
* registers to take fixed, ordered registers and
* setting a handler for it to run the instruction.
*
* In the execution phase by an instruction's handler:
*
* *) If the PC is written to by the instruction, the
* instruction must be fully simulated in software.
*
* *) Otherwise, a modified form of the instruction is
* directly executed. Its handler calls the
* instruction in insn[0]. In insn[1] is a
* "mov pc, lr" to return.
*
* Before calling, load up the reordered registers
* from the original instruction's registers. If one
* of the original input registers is the PC, compute
* and adjust the appropriate input register.
*
* After call completes, copy the output registers to
* the original instruction's original registers.
*
* We don't use a real breakpoint instruction since that
* would have us in the kernel go from SVC mode to SVC
* mode losing the link register. Instead we use an
* undefined instruction. To simplify processing, the
* undefined instruction used for kprobes must be reserved
* exclusively for kprobes use.
*
* TODO: ifdef out some instruction decoding based on architecture.
*/
#include <linux/kernel.h>
#include <linux/kprobes.h>
#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
#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 */
typedef long (insn_0arg_fn_t)(void);
typedef long (insn_1arg_fn_t)(long);
typedef long (insn_2arg_fn_t)(long, long);
typedef long (insn_3arg_fn_t)(long, long, long);
typedef long (insn_4arg_fn_t)(long, long, long, long);
typedef long long (insn_llret_0arg_fn_t)(void);
typedef long long (insn_llret_3arg_fn_t)(long, long, long);
typedef long long (insn_llret_4arg_fn_t)(long, long, long, long);
union reg_pair {
long long dr;
#ifdef __LITTLE_ENDIAN
struct { long r0, r1; };
#else
struct { long r1, r0; };
#endif
};
/*
* For STR and STM instructions, an ARM core may choose to use either
* a +8 or a +12 displacement from the current instruction's address.
* Whichever value is chosen for a given core, it must be the same for
* both instructions and may not change. This function measures it.
*/
static int str_pc_offset;
static void __init find_str_pc_offset(void)
{
int addr, scratch, ret;
__asm__ (
"sub %[ret], pc, #4 \n\t"
"str pc, %[addr] \n\t"
"ldr %[scr], %[addr] \n\t"
"sub %[ret], %[scr], %[ret] \n\t"
: [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr));
str_pc_offset = ret;
}
/*
* The insnslot_?arg_r[w]flags() functions below are to keep the
* msr -> *fn -> mrs instruction sequences indivisible so that
* the state of the CPSR flags aren't inadvertently modified
* just before or just after the call.
*/
static inline long __kprobes
insnslot_0arg_rflags(long cpsr, insn_0arg_fn_t *fn)
{
register long ret asm("r0");
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret)
: [cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
return ret;
}
static inline long long __kprobes
insnslot_llret_0arg_rflags(long cpsr, insn_llret_0arg_fn_t *fn)
{
register long ret0 asm("r0");
register long ret1 asm("r1");
union reg_pair fnr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret0), "=r" (ret1)
: [cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
fnr.r0 = ret0;
fnr.r1 = ret1;
return fnr.dr;
}
static inline long __kprobes
insnslot_1arg_rflags(long r0, long cpsr, insn_1arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long ret asm("r0");
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret)
: "0" (rr0), [cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
return ret;
}
static inline long __kprobes
insnslot_2arg_rflags(long r0, long r1, long cpsr, insn_2arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long ret asm("r0");
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret)
: "0" (rr0), "r" (rr1),
[cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
return ret;
}
static inline long __kprobes
insnslot_3arg_rflags(long r0, long r1, long r2, long cpsr, insn_3arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long rr2 asm("r2") = r2;
register long ret asm("r0");
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret)
: "0" (rr0), "r" (rr1), "r" (rr2),
[cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
return ret;
}
static inline long long __kprobes
insnslot_llret_3arg_rflags(long r0, long r1, long r2, long cpsr,
insn_llret_3arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long rr2 asm("r2") = r2;
register long ret0 asm("r0");
register long ret1 asm("r1");
union reg_pair fnr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret0), "=r" (ret1)
: "0" (rr0), "r" (rr1), "r" (rr2),
[cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
fnr.r0 = ret0;
fnr.r1 = ret1;
return fnr.dr;
}
static inline long __kprobes
insnslot_4arg_rflags(long r0, long r1, long r2, long r3, long cpsr,
insn_4arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long rr2 asm("r2") = r2;
register long rr3 asm("r3") = r3;
register long ret asm("r0");
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
: "=r" (ret)
: "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3),
[cpsr] "r" (cpsr), [fn] "r" (fn)
: "lr", "cc"
);
return ret;
}
static inline long __kprobes
insnslot_1arg_rwflags(long r0, long *cpsr, insn_1arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long ret asm("r0");
long oldcpsr = *cpsr;
long newcpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[oldcpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
"mrs %[newcpsr], cpsr \n\t"
: "=r" (ret), [newcpsr] "=r" (newcpsr)
: "0" (rr0), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
: "lr", "cc"
);
*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
return ret;
}
static inline long __kprobes
insnslot_2arg_rwflags(long r0, long r1, long *cpsr, insn_2arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long ret asm("r0");
long oldcpsr = *cpsr;
long newcpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[oldcpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
"mrs %[newcpsr], cpsr \n\t"
: "=r" (ret), [newcpsr] "=r" (newcpsr)
: "0" (rr0), "r" (rr1), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
: "lr", "cc"
);
*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
return ret;
}
static inline long __kprobes
insnslot_3arg_rwflags(long r0, long r1, long r2, long *cpsr,
insn_3arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long rr2 asm("r2") = r2;
register long ret asm("r0");
long oldcpsr = *cpsr;
long newcpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[oldcpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
"mrs %[newcpsr], cpsr \n\t"
: "=r" (ret), [newcpsr] "=r" (newcpsr)
: "0" (rr0), "r" (rr1), "r" (rr2),
[oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
: "lr", "cc"
);
*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
return ret;
}
static inline long __kprobes
insnslot_4arg_rwflags(long r0, long r1, long r2, long r3, long *cpsr,
insn_4arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long rr2 asm("r2") = r2;
register long rr3 asm("r3") = r3;
register long ret asm("r0");
long oldcpsr = *cpsr;
long newcpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[oldcpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
"mrs %[newcpsr], cpsr \n\t"
: "=r" (ret), [newcpsr] "=r" (newcpsr)
: "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3),
[oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
: "lr", "cc"
);
*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
return ret;
}
static inline long long __kprobes
insnslot_llret_4arg_rwflags(long r0, long r1, long r2, long r3, long *cpsr,
insn_llret_4arg_fn_t *fn)
{
register long rr0 asm("r0") = r0;
register long rr1 asm("r1") = r1;
register long rr2 asm("r2") = r2;
register long rr3 asm("r3") = r3;
register long ret0 asm("r0");
register long ret1 asm("r1");
long oldcpsr = *cpsr;
long newcpsr;
union reg_pair fnr;
__asm__ __volatile__ (
"msr cpsr_fs, %[oldcpsr] \n\t"
"mov lr, pc \n\t"
"mov pc, %[fn] \n\t"
"mrs %[newcpsr], cpsr \n\t"
: "=r" (ret0), "=r" (ret1), [newcpsr] "=r" (newcpsr)
: "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3),
[oldcpsr] "r" (oldcpsr), [fn] "r" (fn)
: "lr", "cc"
);
*cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs);
fnr.r0 = ret0;
fnr.r1 = ret1;
return fnr.dr;
}
/*
* To avoid the complications of mimicing single-stepping on a
* processor without a Next-PC or a single-step mode, and to
* avoid having to deal with the side-effects of boosting, we
* simulate or emulate (almost) all ARM instructions.
*
* "Simulation" is where the instruction's behavior is duplicated in
* C code. "Emulation" is where the original instruction is rewritten
* and executed, often by altering its registers.
*
* By having all behavior of the kprobe'd instruction completed before
* returning from the kprobe_handler(), all locks (scheduler and
* interrupt) can safely be released. There is no need for secondary
* breakpoints, no race with MP or preemptable kernels, nor having to
* clean up resources counts at a later time impacting overall system
* performance. By rewriting the instruction, only the minimum registers
* need to be loaded and saved back optimizing performance.
*
* Calling the insnslot_*_rwflags version of a function doesn't hurt
* anything even when the CPSR flags aren't updated by the
* instruction. It's just a little slower in return for saving
* a little space by not having a duplicate function that doesn't
* update the flags. (The same optimization can be said for
* instructions that do or don't perform register writeback)
* Also, instructions can either read the flags, only write the
* flags, or read and write the flags. To save combinations
* rather than for sheer performance, flag functions just assume
* read and write of flags.
*/
static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
int disp = branch_displacement(insn);
if (insn & (1 << 24))
regs->ARM_lr = iaddr + 4;
regs->ARM_pc = iaddr + 8 + disp;
}
static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
int disp = branch_displacement(insn);
regs->ARM_lr = iaddr + 4;
regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
regs->ARM_cpsr |= PSR_T_BIT;
}
static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rm = insn & 0xf;
long rmv = regs->uregs[rm];
if (insn & (1 << 5))
regs->ARM_lr = (long)p->addr + 4;
regs->ARM_pc = rmv & ~0x1;
regs->ARM_cpsr &= ~PSR_T_BIT;
if (rmv & 0x1)
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)
{
kprobe_opcode_t insn = p->opcode;
int rn = (insn >> 16) & 0xf;
int lbit = insn & (1 << 20);
int wbit = insn & (1 << 21);
int ubit = insn & (1 << 23);
int pbit = insn & (1 << 24);
long *addr = (long *)regs->uregs[rn];
int reg_bit_vector;
int reg_count;
reg_count = 0;
reg_bit_vector = insn & 0xffff;
while (reg_bit_vector) {
reg_bit_vector &= (reg_bit_vector - 1);
++reg_count;
}
if (!ubit)
addr -= reg_count;
addr += (!pbit == !ubit);
reg_bit_vector = insn & 0xffff;
while (reg_bit_vector) {
int reg = __ffs(reg_bit_vector);
reg_bit_vector &= (reg_bit_vector - 1);
if (lbit)
regs->uregs[reg] = *addr++;
else
*addr++ = regs->uregs[reg];
}
if (wbit) {
if (!ubit)
addr -= reg_count;
addr -= (!pbit == !ubit);
regs->uregs[rn] = (long)addr;
}
}
static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_pc = (long)p->addr + str_pc_offset;
simulate_ldm1stm1(p, regs);
regs->ARM_pc = (long)p->addr + 4;
}
static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
{
regs->uregs[12] = regs->uregs[13];
}
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__ (
"ldr r0, %[rn] \n\t"
"ldr r1, %[rm] \n\t"
"msr cpsr_fs, %[cpsr]\n\t"
"mov lr, pc \n\t"
"mov pc, %[i_fn] \n\t"
"str r0, %[rn] \n\t" /* in case of writeback */
"str r2, %[rd0] \n\t"
"str r3, %[rd1] \n\t"
: [rn] "+m" (rnv),
[rd0] "=m" (regs->uregs[rd]),
[rd1] "=m" (regs->uregs[rd+1])
: [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 = (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;
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)
{
insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
long ppc = (long)p->addr + 8;
union reg_pair fnr;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
long rdv;
long rnv = (rn == 15) ? ppc : regs->uregs[rn];
long rmv = (rm == 15) ? ppc : regs->uregs[rm];
long cpsr = regs->ARM_cpsr;
fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn);
if (rn != 15)
regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */
rdv = fnr.r1;
if (rd == 15) {
#if __LINUX_ARM_ARCH__ >= 5
cpsr &= ~PSR_T_BIT;
if (rdv & 0x1)
cpsr |= PSR_T_BIT;
regs->ARM_cpsr = cpsr;
rdv &= ~0x1;
#else
rdv &= ~0x2;
#endif
}
regs->uregs[rd] = rdv;
}
static void __kprobes emulate_str(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 iaddr = (long)p->addr;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
long rdv = (rd == 15) ? iaddr + str_pc_offset : regs->uregs[rd];
long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn];
long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
long rnv_wb;
rnv_wb = insnslot_3arg_rflags(rnv, rdv, rmv, regs->ARM_cpsr, i_fn);
if (rn != 15)
regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */
}
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];
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 12) & 0xf;
int rm = insn & 0xf;
long rmv = regs->uregs[rm];
/* Writes Q flag */
regs->uregs[rd] = insnslot_1arg_rwflags(rmv, &regs->ARM_cpsr, i_fn);
}
static void __kprobes emulate_sel(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;
int rm = insn & 0xf;
long rnv = regs->uregs[rn];
long rmv = regs->uregs[rm];
/* Reads GE bits */
regs->uregs[rd] = insnslot_2arg_rflags(rnv, rmv, regs->ARM_cpsr, i_fn);
}
static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs)
{
insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
}
static void __kprobes emulate_nop(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 rd = (insn >> 12) & 0xf;
long rdv = regs->uregs[rd];
regs->uregs[rd] = insnslot_1arg_rflags(rdv, regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_rd12rn0_modify(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 & 0xf;
long rdv = regs->uregs[rd];
long rnv = regs->uregs[rn];
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)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 12) & 0xf;
int rm = insn & 0xf;
long rmv = regs->uregs[rm];
regs->uregs[rd] = insnslot_1arg_rflags(rmv, regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_rd12rn16rm0_rwflags(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;
int rm = insn & 0xf;
long rnv = regs->uregs[rn];
long rmv = regs->uregs[rm];
regs->uregs[rd] =
insnslot_2arg_rwflags(rnv, rmv, &regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_rd16rn12rs8rm0_rwflags(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;
int rd = (insn >> 16) & 0xf;
int rn = (insn >> 12) & 0xf;
int rs = (insn >> 8) & 0xf;
int rm = insn & 0xf;
long rnv = regs->uregs[rn];
long rsv = regs->uregs[rs];
long rmv = regs->uregs[rm];
regs->uregs[rd] =
insnslot_3arg_rwflags(rnv, rsv, rmv, &regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_rd16rs8rm0_rwflags(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 >> 16) & 0xf;
int rs = (insn >> 8) & 0xf;
int rm = insn & 0xf;
long rsv = regs->uregs[rs];
long rmv = regs->uregs[rm];
regs->uregs[rd] =
insnslot_2arg_rwflags(rsv, rmv, &regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_rdhi16rdlo12rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
{
insn_llret_4arg_fn_t *i_fn = (insn_llret_4arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
union reg_pair fnr;
int rdhi = (insn >> 16) & 0xf;
int rdlo = (insn >> 12) & 0xf;
int rs = (insn >> 8) & 0xf;
int rm = insn & 0xf;
long rsv = regs->uregs[rs];
long rmv = regs->uregs[rm];
fnr.dr = insnslot_llret_4arg_rwflags(regs->uregs[rdhi],
regs->uregs[rdlo], rsv, rmv,
&regs->ARM_cpsr, i_fn);
regs->uregs[rdhi] = fnr.r0;
regs->uregs[rdlo] = fnr.r1;
}
static void __kprobes
emulate_alu_imm_rflags(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 rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
regs->uregs[rd] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_alu_imm_rwflags(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 rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
regs->uregs[rd] = insnslot_1arg_rwflags(rnv, &regs->ARM_cpsr, i_fn);
}
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];
kprobe_opcode_t insn = p->opcode;
long ppc = (long)p->addr + 8;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */
int rs = (insn >> 8) & 0xf; /* 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];
regs->uregs[rd] =
insnslot_3arg_rflags(rnv, rmv, rsv, regs->ARM_cpsr, i_fn);
}
static void __kprobes
emulate_alu_rwflags(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 rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */
int rs = (insn >> 8) & 0xf; /* 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];
regs->uregs[rd] =
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 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 (not_imm) {
insn &= ~0xf;
insn |= 2; /* Rm = r2 */
}
asi->insn[0] = insn;
asi->insn_handler = (insn & (1 << 20)) ? emulate_ldr : emulate_str;
return INSN_GOOD;
}
static enum kprobe_insn __kprobes
prep_emulate_rd12_modify(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
if (is_r15(insn, 12))
return INSN_REJECTED; /* Rd is PC */
insn &= 0xffff0fff; /* Rd = r0 */
asi->insn[0] = insn;
asi->insn_handler = emulate_rd12_modify;
return INSN_GOOD;
}
static enum kprobe_insn __kprobes
prep_emulate_rd12rn0_modify(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
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_rd12rm0;
return INSN_GOOD;
}
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;
asi->insn_handler = emulate_rd12rn16rm0_rwflags;
return INSN_GOOD;
}
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;
asi->insn_handler = emulate_rd16rs8rm0_rwflags;
return INSN_GOOD;
}
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;
asi->insn_handler = emulate_rd16rn12rs8rm0_rwflags;
return INSN_GOOD;
}
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;
asi->insn_handler = emulate_rdhi16rdlo12rs8rm0_rwflags;
return INSN_GOOD;
}
/*
* For the instruction masking and comparisons in all the "space_*"
* functions below, Do _not_ rearrange the order of tests unless
* you're very, very sure of what you are doing. For the sake of
* efficiency, the masks for some tests sometimes assume other test
* have been done prior to them so the number of patterns to test
* for an instruction set can be as broad as possible to reduce the
* number of tests needed.
*/
static enum kprobe_insn __kprobes
space_1111(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/* 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 : */
if ((insn & 0xfe000000) == 0xfa000000) {
asi->insn_handler = simulate_blx1;
return INSN_GOOD_NO_SLOT;
}
/* 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) */
/* 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 */
return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
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) {
/* 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);
/* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
/* SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
if ((insn & 0x0ff000b0) == 0x012000a0 ||
(insn & 0x0ff00090) == 0x01600080)
return prep_emulate_rd16rs8rm0_wflags(insn, asi);
/* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx : Q */
/* 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) {
/* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
/* BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
if ((insn & 0x0ff000d0) == 0x01200010) {
if ((insn & 0x0ff000ff) == 0x0120003f)
return INSN_REJECTED; /* BLX pc */
asi->insn_handler = simulate_blx2bx;
return INSN_GOOD_NO_SLOT;
}
/* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
if ((insn & 0x0ff000f0) == 0x01600010)
return prep_emulate_rd12rm0(insn, asi);
/* QADD : cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx :Q */
/* 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 */
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 & 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 : */
/* UMLALS : cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx :cc */
/* SMULL : cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx : */
/* 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 & 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 */
else if ((insn & 0x0e000090) == 0x00000090) {
/* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx */
/* SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 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 & 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))) {
/* Register index */
insn &= ~0xf;
insn |= 1; /* Rm = r1 */
}
asi->insn[0] = insn;
asi->insn_handler =
(insn & (1 << 5)) ? emulate_strd : emulate_ldrd;
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);
}
/* cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx */
/*
* ALU op with S bit and Rd == 15 :
* cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
*/
if ((insn & 0x0e10f000) == 0x0010f000)
return INSN_REJECTED;
/*
* "mov ip, sp" is the most common kprobe'd instruction by far.
* Check and optimize for it explicitly.
*/
if (insn == 0xe1a0c00d) {
asi->insn_handler = simulate_mov_ipsp;
return INSN_GOOD_NO_SLOT;
}
/*
* Data processing: Immediate-shift / Register-shift
* ALU op : cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx
* CPY : cccc 0001 1010 xxxx xxxx 0000 0000 xxxx
* MOV : cccc 0001 101x xxxx xxxx xxxx xxxx xxxx
* *S (bit 20) updates condition codes
* ADC/SBC/RSC reads the C flag
*/
insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */
insn |= 0x00000001; /* Rm = r1 */
if (insn & 0x010) {
insn &= 0xfffff0ff; /* register shift */
insn |= 0x00000200; /* Rs = r2 */
}
asi->insn[0] = insn;
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
* ALU op with S bit and Rd == 15 :
* cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx
*/
if ((insn & 0x0fb00000) == 0x03200000 || /* MSR */
(insn & 0x0e10f000) == 0x0210f000) /* ALU s-bit, R15 */
return INSN_REJECTED;
/*
* Data processing: 32-bit Immediate
* ALU op : cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
* MOV : cccc 0011 101x xxxx xxxx xxxx xxxx xxxx
* *S (bit 20) updates condition codes
* ADC/SBC/RSC reads the C flag
*/
insn &= 0xfff00fff; /* Rn = r0 and Rd = r0 */
asi->insn[0] = insn;
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;
}
static enum kprobe_insn __kprobes
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;
asi->insn_handler = emulate_sel;
return INSN_GOOD;
}
/* SSAT : cccc 0110 101x xxxx xxxx xxxx xx01 xxxx :Q */
/* USAT : cccc 0110 111x xxxx xxxx xxxx xx01 xxxx :Q */
/* SSAT16 : cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx :Q */
/* 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;
return INSN_GOOD;
}
/* 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 & 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 : */
/* 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 : */
/* 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
space_cccc_0111__1(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/* Undef : cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
if ((insn & 0x0ff000f0) == 0x03f000f0)
return INSN_REJECTED;
/* 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 : */
/* 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 & 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);
/* 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
space_cccc_01xx(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/* LDR : cccc 01xx x0x1 xxxx xxxx xxxx xxxx xxxx */
/* LDRB : cccc 01xx x1x1 xxxx xxxx xxxx xxxx xxxx */
/* LDRBT : cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */
/* LDRT : cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */
/* STR : cccc 01xx x0x0 xxxx xxxx xxxx xxxx xxxx */
/* 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);
}
static enum kprobe_insn __kprobes
space_cccc_100x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/* LDM(2) : cccc 100x x101 xxxx 0xxx xxxx xxxx xxxx */
/* LDM(3) : cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */
if ((insn & 0x0e708000) == 0x85000000 ||
(insn & 0x0e508000) == 0x85010000)
return INSN_REJECTED;
/* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
/* STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */
simulate_stm1_pc : simulate_ldm1stm1;
return INSN_GOOD_NO_SLOT;
}
static enum kprobe_insn __kprobes
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_handler = simulate_bbl;
return INSN_GOOD_NO_SLOT;
}
static enum kprobe_insn __kprobes
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) */
/* 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 unsigned long __kprobes __check_eq(unsigned long cpsr)
{
return cpsr & PSR_Z_BIT;
}
static unsigned long __kprobes __check_ne(unsigned long cpsr)
{
return (~cpsr) & PSR_Z_BIT;
}
static unsigned long __kprobes __check_cs(unsigned long cpsr)
{
return cpsr & PSR_C_BIT;
}
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,
* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
*
* For instructions we don't want to kprobe (INSN_REJECTED return result):
* These are generally ones that modify the processor state making
* them "hard" to simulate such as switches processor modes or
* make accesses in alternate modes. Any of these could be simulated
* if the work was put into it, but low return considering they
* should also be very rare.
*/
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)
return space_1111(insn, asi);
else if ((insn & 0x0e000000) == 0x00000000)
return space_cccc_000x(insn, asi);
else if ((insn & 0x0e000000) == 0x02000000)
return space_cccc_001x(insn, asi);
else if ((insn & 0x0f000010) == 0x06000010)
return space_cccc_0110__1(insn, asi);
else if ((insn & 0x0f000010) == 0x07000010)
return space_cccc_0111__1(insn, asi);
else if ((insn & 0x0c000000) == 0x04000000)
return space_cccc_01xx(insn, asi);
else if ((insn & 0x0e000000) == 0x08000000)
return space_cccc_100x(insn, asi);
else if ((insn & 0x0e000000) == 0x0a000000)
return space_cccc_101x(insn, asi);
return space_cccc_11xx(insn, asi);
}
void __init arm_kprobe_decode_init(void)
{
find_str_pc_offset();
}
/*
* arch/arm/kernel/kprobes-thumb.c
*
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
*
* 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/kernel.h>
#include <linux/kprobes.h>
#include "kprobes.h"
/*
* True if current instruction is in an IT block.
*/
#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000)
/*
* Return the condition code to check for the currently executing instruction.
* This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if
* in_it_block returns true.
*/
#define current_cond(cpsr) ((cpsr >> 12) & 0xf)
/*
* Return the PC value for a probe in thumb code.
* This is the address of the probed instruction plus 4.
* We subtract one because the address will have bit zero set to indicate
* a pointer to thumb code.
*/
static inline unsigned long __kprobes thumb_probe_pc(struct kprobe *p)
{
return (unsigned long)p->addr - 1 + 4;
}
static void __kprobes
t32_simulate_table_branch(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn];
unsigned long rmv = regs->uregs[rm];
unsigned int halfwords;
if (insn & 0x10) /* TBH */
halfwords = ((u16 *)rnv)[rmv];
else /* TBB */
halfwords = ((u8 *)rnv)[rmv];
regs->ARM_pc = pc + 2 * halfwords;
}
static void __kprobes
t32_simulate_mrs(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 8) & 0xf;
unsigned long mask = 0xf8ff03df; /* Mask out execution state */
regs->uregs[rd] = regs->ARM_cpsr & mask;
}
static void __kprobes
t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
long offset = insn & 0x7ff; /* imm11 */
offset += (insn & 0x003f0000) >> 5; /* imm6 */
offset += (insn & 0x00002000) << 4; /* J1 */
offset += (insn & 0x00000800) << 7; /* J2 */
offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */
regs->ARM_pc = pc + (offset * 2);
}
static enum kprobe_insn __kprobes
t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
int cc = (insn >> 22) & 0xf;
asi->insn_check_cc = kprobe_condition_checks[cc];
asi->insn_handler = t32_simulate_cond_branch;
return INSN_GOOD_NO_SLOT;
}
static void __kprobes
t32_simulate_branch(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
long offset = insn & 0x7ff; /* imm11 */
offset += (insn & 0x03ff0000) >> 5; /* imm10 */
offset += (insn & 0x00002000) << 9; /* J1 */
offset += (insn & 0x00000800) << 10; /* J2 */
if (insn & 0x04000000)
offset -= 0x00800000; /* Apply sign bit */
else
offset ^= 0x00600000; /* Invert J1 and J2 */
if (insn & (1 << 14)) {
/* BL or BLX */
regs->ARM_lr = (unsigned long)p->addr + 4;
if (!(insn & (1 << 12))) {
/* BLX so switch to ARM mode */
regs->ARM_cpsr &= ~PSR_T_BIT;
pc &= ~3;
}
}
regs->ARM_pc = pc + (offset * 2);
}
static void __kprobes
t32_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long addr = thumb_probe_pc(p) & ~3;
int rt = (insn >> 12) & 0xf;
unsigned long rtv;
long offset = insn & 0xfff;
if (insn & 0x00800000)
addr += offset;
else
addr -= offset;
if (insn & 0x00400000) {
/* LDR */
rtv = *(unsigned long *)addr;
if (rt == 15) {
bx_write_pc(rtv, regs);
return;
}
} else if (insn & 0x00200000) {
/* LDRH */
if (insn & 0x01000000)
rtv = *(s16 *)addr;
else
rtv = *(u16 *)addr;
} else {
/* LDRB */
if (insn & 0x01000000)
rtv = *(s8 *)addr;
else
rtv = *(u8 *)addr;
}
regs->uregs[rt] = rtv;
}
static enum kprobe_insn __kprobes
t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
enum kprobe_insn ret = kprobe_decode_ldmstm(insn, asi);
/* Fixup modified instruction to have halfwords in correct order...*/
insn = asi->insn[0];
((u16 *)asi->insn)[0] = insn >> 16;
((u16 *)asi->insn)[1] = insn & 0xffff;
return ret;
}
static void __kprobes
t32_emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p) & ~3;
int rt1 = (insn >> 12) & 0xf;
int rt2 = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
register unsigned long rt1v asm("r0") = regs->uregs[rt1];
register unsigned long rt2v asm("r1") = regs->uregs[rt2];
register unsigned long rnv asm("r2") = (rn == 15) ? pc
: regs->uregs[rn];
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rt1v), "=r" (rt2v), "=r" (rnv)
: "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
if (rn != 15)
regs->uregs[rn] = rnv; /* Writeback base register */
regs->uregs[rt1] = rt1v;
regs->uregs[rt2] = rt2v;
}
static void __kprobes
t32_emulate_ldrstr(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rt = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rtv asm("r0") = regs->uregs[rt];
register unsigned long rnv asm("r2") = regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rtv), "=r" (rnv)
: "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rn] = rnv; /* Writeback base register */
if (rt == 15) /* Can't be true for a STR as they aren't allowed */
bx_write_pc(rtv, regs);
else
regs->uregs[rt] = rtv;
}
static void __kprobes
t32_emulate_rd8rn16rm0_rwflags(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rdv asm("r1") = regs->uregs[rd];
register unsigned long rnv asm("r2") = regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
unsigned long cpsr = regs->ARM_cpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"blx %[fn] \n\t"
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdv), [cpsr] "=r" (cpsr)
: "0" (rdv), "r" (rnv), "r" (rmv),
"1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rd] = rdv;
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
static void __kprobes
t32_emulate_rd8pc16_noflags(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
int rd = (insn >> 8) & 0xf;
register unsigned long rdv asm("r1") = regs->uregs[rd];
register unsigned long rnv asm("r2") = pc & ~3;
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rdv)
: "0" (rdv), "r" (rnv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rd] = rdv;
}
static void __kprobes
t32_emulate_rd8rn16_noflags(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rd = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
register unsigned long rdv asm("r1") = regs->uregs[rd];
register unsigned long rnv asm("r2") = regs->uregs[rn];
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rdv)
: "0" (rdv), "r" (rnv), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rd] = rdv;
}
static void __kprobes
t32_emulate_rdlo12rdhi8rn16rm0_noflags(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rdlo = (insn >> 12) & 0xf;
int rdhi = (insn >> 8) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
register unsigned long rdhiv asm("r1") = regs->uregs[rdhi];
register unsigned long rnv asm("r2") = regs->uregs[rn];
register unsigned long rmv asm("r3") = regs->uregs[rm];
__asm__ __volatile__ (
"blx %[fn]"
: "=r" (rdlov), "=r" (rdhiv)
: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
[fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
regs->uregs[rdlo] = rdlov;
regs->uregs[rdhi] = rdhiv;
}
/* These emulation encodings are functionally equivalent... */
#define t32_emulate_rd8rn16rm0ra12_noflags \
t32_emulate_rdlo12rdhi8rn16rm0_noflags
static const union decode_item t32_table_1110_100x_x0xx[] = {
/* Load/store multiple instructions */
/* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xfe4f0000, 0xe80f0000),
/* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */
/* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xffc00000, 0xe8000000),
/* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */
/* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xffc00000, 0xe9800000),
/* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */
DECODE_REJECT (0xfe508000, 0xe8008000),
/* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */
DECODE_REJECT (0xfe50c000, 0xe810c000),
/* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */
DECODE_REJECT (0xfe402000, 0xe8002000),
/* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */
/* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
/* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
/* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
DECODE_CUSTOM (0xfe400000, 0xe8000000, t32_decode_ldmstm),
DECODE_END
};
static const union decode_item t32_table_1110_100x_x1xx[] = {
/* Load/store dual, load/store exclusive, table branch */
/* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */
/* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */
DECODE_OR (0xff600000, 0xe8600000),
/* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
/* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xff400000, 0xe9400000, t32_emulate_ldrdstrd,
REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
/* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
/* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, t32_simulate_table_branch,
REGS(NOSP, 0, 0, 0, NOSPPC)),
/* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
/* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */
/* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */
/* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */
/* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */
/* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */
/* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */
/* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */
/* And unallocated instructions... */
DECODE_END
};
static const union decode_item t32_table_1110_101x[] = {
/* Data-processing (shifted register) */
/* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
/* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
DECODE_EMULATEX (0xff700f00, 0xea100f00, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, 0, 0, NOSPPC)),
/* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
DECODE_OR (0xfff00f00, 0xeb100f00),
/* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
DECODE_EMULATEX (0xfff00f00, 0xebb00f00, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOPC, 0, 0, 0, NOSPPC)),
/* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
/* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xffcf0000, 0xea4f0000, t32_emulate_rd8rn16rm0_rwflags,
REGS(0, 0, NOSPPC, 0, NOSPPC)),
/* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
/* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xffa00000, 0xeaa00000),
/* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xffe00000, 0xeb200000),
/* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xffe00000, 0xeb800000),
/* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xffe00000, 0xebe00000),
/* ADD/SUB SP, SP, Rm, LSL #0..3 */
/* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, t32_emulate_rd8rn16rm0_rwflags,
REGS(SP, 0, SP, 0, NOSPPC)),
/* ADD/SUB SP, SP, Rm, shift */
/* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */
DECODE_REJECT (0xff4f0f00, 0xeb0d0d00),
/* ADD/SUB Rd, SP, Rm, shift */
/* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, t32_emulate_rd8rn16rm0_rwflags,
REGS(SP, 0, NOPC, 0, NOSPPC)),
/* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
/* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */
/* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */
/* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */
/* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */
/* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */
/* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */
/* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */
/* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
/* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
/* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfe000000, 0xea000000, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
DECODE_END
};
static const union decode_item t32_table_1111_0x0x___0[] = {
/* Data-processing (modified immediate) */
/* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
/* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
DECODE_EMULATEX (0xfb708f00, 0xf0100f00, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, 0, 0, 0)),
/* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
DECODE_OR (0xfbf08f00, 0xf1100f00),
/* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOPC, 0, 0, 0, 0)),
/* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
/* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, t32_emulate_rd8rn16rm0_rwflags,
REGS(0, 0, NOSPPC, 0, 0)),
/* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
DECODE_REJECT (0xfbe08000, 0xf0a00000),
/* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */
/* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */
DECODE_REJECT (0xfbc08000, 0xf0c00000),
/* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */
DECODE_REJECT (0xfbe08000, 0xf1200000),
/* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */
DECODE_REJECT (0xfbe08000, 0xf1800000),
/* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */
DECODE_REJECT (0xfbe08000, 0xf1e00000),
/* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
/* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, t32_emulate_rd8rn16rm0_rwflags,
REGS(SP, 0, NOPC, 0, 0)),
/* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
/* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */
/* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */
/* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */
/* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */
/* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */
/* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */
/* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
/* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
/* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfa008000, 0xf0000000, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, NOSPPC, 0, 0)),
DECODE_END
};
static const union decode_item t32_table_1111_0x1x___0[] = {
/* Data-processing (plain binary immediate) */
/* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
DECODE_OR (0xfbff8000, 0xf20f0000),
/* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfbff8000, 0xf2af0000, t32_emulate_rd8pc16_noflags,
REGS(PC, 0, NOSPPC, 0, 0)),
/* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
DECODE_OR (0xfbff8f00, 0xf20d0d00),
/* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, t32_emulate_rd8rn16_noflags,
REGS(SP, 0, SP, 0, 0)),
/* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
DECODE_OR (0xfbf08000, 0xf2000000),
/* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfbf08000, 0xf2a00000, t32_emulate_rd8rn16_noflags,
REGS(NOPCX, 0, NOSPPC, 0, 0)),
/* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
/* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfb708000, 0xf2400000, t32_emulate_rd8rn16_noflags,
REGS(0, 0, NOSPPC, 0, 0)),
/* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
/* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
/* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
/* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfb508000, 0xf3000000, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, NOSPPC, 0, 0)),
/* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
/* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfb708000, 0xf3400000, t32_emulate_rd8rn16_noflags,
REGS(NOSPPC, 0, NOSPPC, 0, 0)),
/* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfbff8000, 0xf36f0000, t32_emulate_rd8rn16_noflags,
REGS(0, 0, NOSPPC, 0, 0)),
/* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfbf08000, 0xf3600000, t32_emulate_rd8rn16_noflags,
REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
DECODE_END
};
static const union decode_item t32_table_1111_0xxx___1[] = {
/* Branches and miscellaneous control */
/* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */
DECODE_OR (0xfff0d7ff, 0xf3a08001),
/* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */
DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
/* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */
/* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */
/* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */
DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
/* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
REGS(0, 0, NOSPPC, 0, 0)),
/*
* Unsupported instructions
* 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx
*
* MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx
* DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx
* Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx
* CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx
* CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx
* BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx
* SUBS PC,LR,#<imm8> 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx
* MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx
* SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx
* UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx
* ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx
*/
DECODE_REJECT (0xfb80d000, 0xf3808000),
/* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
DECODE_CUSTOM (0xf800d000, 0xf0008000, t32_decode_cond_branch),
/* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
DECODE_OR (0xf800d001, 0xf000c000),
/* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
/* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
DECODE_SIMULATE (0xf8009000, 0xf0009000, t32_simulate_branch),
DECODE_END
};
static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
/* Memory hints */
/* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */
/* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */
DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
/* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
DECODE_OR (0xffd0f000, 0xf890f000),
/* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */
DECODE_OR (0xffd0ff00, 0xf810fc00),
/* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
DECODE_OR (0xfff0f000, 0xf990f000),
/* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
REGS(NOPCX, 0, 0, 0, 0)),
/* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
DECODE_OR (0xffd0ffc0, 0xf810f000),
/* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */
DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
REGS(NOPCX, 0, 0, 0, NOSPPC)),
/* Other unallocated instructions... */
DECODE_END
};
static const union decode_item t32_table_1111_100x[] = {
/* Store/Load single data item */
/* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xfe600000, 0xf8600000),
/* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xfff00000, 0xf9500000),
/* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */
DECODE_REJECT (0xfe800d00, 0xf8000800),
/* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */
/* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */
/* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */
/* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */
/* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */
/* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */
/* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */
/* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */
DECODE_REJECT (0xfe800f00, 0xf8000e00),
/* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */
DECODE_REJECT (0xff1f0000, 0xf80f0000),
/* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */
DECODE_REJECT (0xff10f000, 0xf800f000),
/* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, t32_simulate_ldr_literal,
REGS(PC, ANY, 0, 0, 0)),
/* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
/* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */
DECODE_OR (0xffe00800, 0xf8400800),
/* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
/* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xffe00000, 0xf8c00000, t32_emulate_ldrstr,
REGS(NOPCX, ANY, 0, 0, 0)),
/* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
/* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
DECODE_EMULATEX (0xffe00fc0, 0xf8400000, t32_emulate_ldrstr,
REGS(NOPCX, ANY, 0, 0, NOSPPC)),
/* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
/* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
/* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
/* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfe5f0000, 0xf81f0000, t32_simulate_ldr_literal,
REGS(PC, NOSPPCX, 0, 0, 0)),
/* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
/* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */
/* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */
/* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */
/* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */
/* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */
DECODE_OR (0xfec00800, 0xf8000800),
/* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */
/* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */
/* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */
/* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
/* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
/* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
DECODE_EMULATEX (0xfec00000, 0xf8800000, t32_emulate_ldrstr,
REGS(NOPCX, NOSPPCX, 0, 0, 0)),
/* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
/* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
/* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */
/* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
/* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
/* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
DECODE_EMULATEX (0xfe800fc0, 0xf8000000, t32_emulate_ldrstr,
REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
/* Other unallocated instructions... */
DECODE_END
};
static const union decode_item t32_table_1111_1010___1111[] = {
/* Data-processing (register) */
/* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */
DECODE_REJECT (0xffe0f080, 0xfa60f080),
/* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */
/* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */
/* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */
/* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
/* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
/* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, t32_emulate_rd8rn16rm0_rwflags,
REGS(0, 0, NOSPPC, 0, NOSPPC)),
/* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */
DECODE_REJECT (0xff80f0b0, 0xfa80f030),
/* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */
DECODE_REJECT (0xffb0f080, 0xfab0f000),
/* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */
/* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */
/* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */
/* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */
/* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */
/* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */
/* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */
/* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */
/* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */
/* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */
/* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */
/* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */
/* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */
/* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */
/* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */
/* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */
/* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */
/* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */
/* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */
/* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */
/* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */
/* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */
/* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */
/* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */
/* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */
/* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */
/* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */
/* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */
/* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */
/* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */
/* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */
/* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */
/* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */
/* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */
/* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */
/* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */
DECODE_OR (0xff80f080, 0xfa80f000),
/* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */
/* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */
/* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */
/* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */
/* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */
/* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */
DECODE_OR (0xff80f080, 0xfa00f080),
/* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */
/* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */
/* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */
/* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */
DECODE_OR (0xfff0f0c0, 0xfa80f080),
/* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
DECODE_OR (0xfff0f0f0, 0xfaa0f080),
/* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */
/* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
/* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
/* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
/* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
DECODE_OR (0xfff0f0f0, 0xfab0f080),
/* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */
/* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
/* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
/* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, t32_emulate_rd8rn16_noflags,
REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
/* Other unallocated instructions... */
DECODE_END
};
static const union decode_item t32_table_1111_1011_0[] = {
/* Multiply, multiply accumulate, and absolute difference */
/* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */
DECODE_REJECT (0xfff0f0f0, 0xfb00f010),
/* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */
DECODE_REJECT (0xfff0f0f0, 0xfb70f010),
/* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */
DECODE_OR (0xfff0f0c0, 0xfb10f000),
/* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */
/* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */
/* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */
/* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
/* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
/* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, t32_emulate_rd8rn16rm0_rwflags,
REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
/* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
DECODE_REJECT (0xfff000f0, 0xfb700010),
/* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */
DECODE_OR (0xfff000c0, 0xfb100000),
/* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */
/* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */
/* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */
/* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */
/* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */
/* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
/* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
/* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
DECODE_EMULATEX (0xff8000c0, 0xfb000000, t32_emulate_rd8rn16rm0ra12_noflags,
REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
/* Other unallocated instructions... */
DECODE_END
};
static const union decode_item t32_table_1111_1011_1[] = {
/* Long multiply, long multiply accumulate, and divide */
/* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */
DECODE_OR (0xfff000f0, 0xfbe00060),
/* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */
DECODE_OR (0xfff000c0, 0xfbc00080),
/* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */
/* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */
DECODE_OR (0xffe000e0, 0xfbc000c0),
/* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */
/* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
/* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
/* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
DECODE_EMULATEX (0xff9000f0, 0xfb800000, t32_emulate_rdlo12rdhi8rn16rm0_noflags,
REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
/* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
/* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */
/* Other unallocated instructions... */
DECODE_END
};
const union decode_item kprobe_decode_thumb32_table[] = {
/*
* Load/store multiple instructions
* 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx),
/*
* Load/store dual, load/store exclusive, table branch
* 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx),
/*
* Data-processing (shifted register)
* 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x),
/*
* Coprocessor instructions
* 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_REJECT (0xfc000000, 0xec000000),
/*
* Data-processing (modified immediate)
* 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0),
/*
* Data-processing (plain binary immediate)
* 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0),
/*
* Branches and miscellaneous control
* 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1),
/*
* Advanced SIMD element or structure load/store instructions
* 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx
*/
DECODE_REJECT (0xff100000, 0xf9000000),
/*
* Memory hints
* 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx
*/
DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111),
/*
* Store single data item
* 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx
* Load single data items
* 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x),
/*
* Data-processing (register)
* 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx
*/
DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111),
/*
* Multiply, multiply accumulate, and absolute difference
* 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0),
/*
* Long multiply, long multiply accumulate, and divide
* 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1),
/*
* Coprocessor instructions
* 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx
*/
DECODE_END
};
static void __kprobes
t16_simulate_bxblx(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
int rm = (insn >> 3) & 0xf;
unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm];
if (insn & (1 << 7)) /* BLX ? */
regs->ARM_lr = (unsigned long)p->addr + 2;
bx_write_pc(rmv, regs);
}
static void __kprobes
t16_simulate_ldr_literal(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long* base = (unsigned long *)(thumb_probe_pc(p) & ~3);
long index = insn & 0xff;
int rt = (insn >> 8) & 0x7;
regs->uregs[rt] = base[index];
}
static void __kprobes
t16_simulate_ldrstr_sp_relative(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long* base = (unsigned long *)regs->ARM_sp;
long index = insn & 0xff;
int rt = (insn >> 8) & 0x7;
if (insn & 0x800) /* LDR */
regs->uregs[rt] = base[index];
else /* STR */
base[index] = regs->uregs[rt];
}
static void __kprobes
t16_simulate_reladr(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long base = (insn & 0x800) ? regs->ARM_sp
: (thumb_probe_pc(p) & ~3);
long offset = insn & 0xff;
int rt = (insn >> 8) & 0x7;
regs->uregs[rt] = base + offset * 4;
}
static void __kprobes
t16_simulate_add_sp_imm(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
long imm = insn & 0x7f;
if (insn & 0x80) /* SUB */
regs->ARM_sp -= imm * 4;
else /* ADD */
regs->ARM_sp += imm * 4;
}
static void __kprobes
t16_simulate_cbz(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
int rn = insn & 0x7;
kprobe_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn;
if (nonzero & 0x800) {
long i = insn & 0x200;
long imm5 = insn & 0xf8;
unsigned long pc = thumb_probe_pc(p);
regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2);
}
}
static void __kprobes
t16_simulate_it(struct kprobe *p, struct pt_regs *regs)
{
/*
* The 8 IT state bits are split into two parts in CPSR:
* ITSTATE<1:0> are in CPSR<26:25>
* ITSTATE<7:2> are in CPSR<15:10>
* The new IT state is in the lower byte of insn.
*/
kprobe_opcode_t insn = p->opcode;
unsigned long cpsr = regs->ARM_cpsr;
cpsr &= ~PSR_IT_MASK;
cpsr |= (insn & 0xfc) << 8;
cpsr |= (insn & 0x03) << 25;
regs->ARM_cpsr = cpsr;
}
static void __kprobes
t16_singlestep_it(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_pc += 2;
t16_simulate_it(p, regs);
}
static enum kprobe_insn __kprobes
t16_decode_it(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
asi->insn_singlestep = t16_singlestep_it;
return INSN_GOOD_NO_SLOT;
}
static void __kprobes
t16_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
long offset = insn & 0x7f;
offset -= insn & 0x80; /* Apply sign bit */
regs->ARM_pc = pc + (offset * 2);
}
static enum kprobe_insn __kprobes
t16_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
int cc = (insn >> 8) & 0xf;
asi->insn_check_cc = kprobe_condition_checks[cc];
asi->insn_handler = t16_simulate_cond_branch;
return INSN_GOOD_NO_SLOT;
}
static void __kprobes
t16_simulate_branch(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
long offset = insn & 0x3ff;
offset -= insn & 0x400; /* Apply sign bit */
regs->ARM_pc = pc + (offset * 2);
}
static unsigned long __kprobes
t16_emulate_loregs(struct kprobe *p, struct pt_regs *regs)
{
unsigned long oldcpsr = regs->ARM_cpsr;
unsigned long newcpsr;
__asm__ __volatile__ (
"msr cpsr_fs, %[oldcpsr] \n\t"
"ldmia %[regs], {r0-r7} \n\t"
"blx %[fn] \n\t"
"stmia %[regs], {r0-r7} \n\t"
"mrs %[newcpsr], cpsr \n\t"
: [newcpsr] "=r" (newcpsr)
: [oldcpsr] "r" (oldcpsr), [regs] "r" (regs),
[fn] "r" (p->ainsn.insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"lr", "memory", "cc"
);
return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK);
}
static void __kprobes
t16_emulate_loregs_rwflags(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_cpsr = t16_emulate_loregs(p, regs);
}
static void __kprobes
t16_emulate_loregs_noitrwflags(struct kprobe *p, struct pt_regs *regs)
{
unsigned long cpsr = t16_emulate_loregs(p, regs);
if (!in_it_block(cpsr))
regs->ARM_cpsr = cpsr;
}
static void __kprobes
t16_emulate_hiregs(struct kprobe *p, struct pt_regs *regs)
{
kprobe_opcode_t insn = p->opcode;
unsigned long pc = thumb_probe_pc(p);
int rdn = (insn & 0x7) | ((insn & 0x80) >> 4);
int rm = (insn >> 3) & 0xf;
register unsigned long rdnv asm("r1");
register unsigned long rmv asm("r0");
unsigned long cpsr = regs->ARM_cpsr;
rdnv = (rdn == 15) ? pc : regs->uregs[rdn];
rmv = (rm == 15) ? pc : regs->uregs[rm];
__asm__ __volatile__ (
"msr cpsr_fs, %[cpsr] \n\t"
"blx %[fn] \n\t"
"mrs %[cpsr], cpsr \n\t"
: "=r" (rdnv), [cpsr] "=r" (cpsr)
: "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
: "lr", "memory", "cc"
);
if (rdn == 15)
rdnv &= ~1;
regs->uregs[rdn] = rdnv;
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}
static enum kprobe_insn __kprobes
t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
insn &= ~0x00ff;
insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
((u16 *)asi->insn)[0] = insn;
asi->insn_handler = t16_emulate_hiregs;
return INSN_GOOD;
}
static void __kprobes
t16_emulate_push(struct kprobe *p, struct pt_regs *regs)
{
__asm__ __volatile__ (
"ldr r9, [%[regs], #13*4] \n\t"
"ldr r8, [%[regs], #14*4] \n\t"
"ldmia %[regs], {r0-r7} \n\t"
"blx %[fn] \n\t"
"str r9, [%[regs], #13*4] \n\t"
:
: [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
"lr", "memory", "cc"
);
}
static enum kprobe_insn __kprobes
t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/*
* To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
* and call it with R9=SP and LR in the register list represented
* by R8.
*/
((u16 *)asi->insn)[0] = 0xe929; /* 1st half STMDB R9!,{} */
((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */
asi->insn_handler = t16_emulate_push;
return INSN_GOOD;
}
static void __kprobes
t16_emulate_pop_nopc(struct kprobe *p, struct pt_regs *regs)
{
__asm__ __volatile__ (
"ldr r9, [%[regs], #13*4] \n\t"
"ldmia %[regs], {r0-r7} \n\t"
"blx %[fn] \n\t"
"stmia %[regs], {r0-r7} \n\t"
"str r9, [%[regs], #13*4] \n\t"
:
: [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9",
"lr", "memory", "cc"
);
}
static void __kprobes
t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs)
{
register unsigned long pc asm("r8");
__asm__ __volatile__ (
"ldr r9, [%[regs], #13*4] \n\t"
"ldmia %[regs], {r0-r7} \n\t"
"blx %[fn] \n\t"
"stmia %[regs], {r0-r7} \n\t"
"str r9, [%[regs], #13*4] \n\t"
: "=r" (pc)
: [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9",
"lr", "memory", "cc"
);
bx_write_pc(pc, regs);
}
static enum kprobe_insn __kprobes
t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
/*
* To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
* and call it with R9=SP and PC in the register list represented
* by R8.
*/
((u16 *)asi->insn)[0] = 0xe8b9; /* 1st half LDMIA R9!,{} */
((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */
asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc
: t16_emulate_pop_nopc;
return INSN_GOOD;
}
static const union decode_item t16_table_1011[] = {
/* Miscellaneous 16-bit instructions */
/* ADD (SP plus immediate) 1011 0000 0xxx xxxx */
/* SUB (SP minus immediate) 1011 0000 1xxx xxxx */
DECODE_SIMULATE (0xff00, 0xb000, t16_simulate_add_sp_imm),
/* CBZ 1011 00x1 xxxx xxxx */
/* CBNZ 1011 10x1 xxxx xxxx */
DECODE_SIMULATE (0xf500, 0xb100, t16_simulate_cbz),
/* SXTH 1011 0010 00xx xxxx */
/* SXTB 1011 0010 01xx xxxx */
/* UXTH 1011 0010 10xx xxxx */
/* UXTB 1011 0010 11xx xxxx */
/* REV 1011 1010 00xx xxxx */
/* REV16 1011 1010 01xx xxxx */
/* ??? 1011 1010 10xx xxxx */
/* REVSH 1011 1010 11xx xxxx */
DECODE_REJECT (0xffc0, 0xba80),
DECODE_EMULATE (0xf500, 0xb000, t16_emulate_loregs_rwflags),
/* PUSH 1011 010x xxxx xxxx */
DECODE_CUSTOM (0xfe00, 0xb400, t16_decode_push),
/* POP 1011 110x xxxx xxxx */
DECODE_CUSTOM (0xfe00, 0xbc00, t16_decode_pop),
/*
* If-Then, and hints
* 1011 1111 xxxx xxxx
*/
/* YIELD 1011 1111 0001 0000 */
DECODE_OR (0xffff, 0xbf10),
/* SEV 1011 1111 0100 0000 */
DECODE_EMULATE (0xffff, 0xbf40, kprobe_emulate_none),
/* NOP 1011 1111 0000 0000 */
/* WFE 1011 1111 0010 0000 */
/* WFI 1011 1111 0011 0000 */
DECODE_SIMULATE (0xffcf, 0xbf00, kprobe_simulate_nop),
/* Unassigned hints 1011 1111 xxxx 0000 */
DECODE_REJECT (0xff0f, 0xbf00),
/* IT 1011 1111 xxxx xxxx */
DECODE_CUSTOM (0xff00, 0xbf00, t16_decode_it),
/* SETEND 1011 0110 010x xxxx */
/* CPS 1011 0110 011x xxxx */
/* BKPT 1011 1110 xxxx xxxx */
/* And unallocated instructions... */
DECODE_END
};
const union decode_item kprobe_decode_thumb16_table[] = {
/*
* Shift (immediate), add, subtract, move, and compare
* 00xx xxxx xxxx xxxx
*/
/* CMP (immediate) 0010 1xxx xxxx xxxx */
DECODE_EMULATE (0xf800, 0x2800, t16_emulate_loregs_rwflags),
/* ADD (register) 0001 100x xxxx xxxx */
/* SUB (register) 0001 101x xxxx xxxx */
/* LSL (immediate) 0000 0xxx xxxx xxxx */
/* LSR (immediate) 0000 1xxx xxxx xxxx */
/* ASR (immediate) 0001 0xxx xxxx xxxx */
/* ADD (immediate, Thumb) 0001 110x xxxx xxxx */
/* SUB (immediate, Thumb) 0001 111x xxxx xxxx */
/* MOV (immediate) 0010 0xxx xxxx xxxx */
/* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */
/* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */
DECODE_EMULATE (0xc000, 0x0000, t16_emulate_loregs_noitrwflags),
/*
* 16-bit Thumb data-processing instructions
* 0100 00xx xxxx xxxx
*/
/* TST (register) 0100 0010 00xx xxxx */
DECODE_EMULATE (0xffc0, 0x4200, t16_emulate_loregs_rwflags),
/* CMP (register) 0100 0010 10xx xxxx */
/* CMN (register) 0100 0010 11xx xxxx */
DECODE_EMULATE (0xff80, 0x4280, t16_emulate_loregs_rwflags),
/* AND (register) 0100 0000 00xx xxxx */
/* EOR (register) 0100 0000 01xx xxxx */
/* LSL (register) 0100 0000 10xx xxxx */
/* LSR (register) 0100 0000 11xx xxxx */
/* ASR (register) 0100 0001 00xx xxxx */
/* ADC (register) 0100 0001 01xx xxxx */
/* SBC (register) 0100 0001 10xx xxxx */
/* ROR (register) 0100 0001 11xx xxxx */
/* RSB (immediate) 0100 0010 01xx xxxx */
/* ORR (register) 0100 0011 00xx xxxx */
/* MUL 0100 0011 00xx xxxx */
/* BIC (register) 0100 0011 10xx xxxx */
/* MVN (register) 0100 0011 10xx xxxx */
DECODE_EMULATE (0xfc00, 0x4000, t16_emulate_loregs_noitrwflags),
/*
* Special data instructions and branch and exchange
* 0100 01xx xxxx xxxx
*/
/* BLX pc 0100 0111 1111 1xxx */
DECODE_REJECT (0xfff8, 0x47f8),
/* BX (register) 0100 0111 0xxx xxxx */
/* BLX (register) 0100 0111 1xxx xxxx */
DECODE_SIMULATE (0xff00, 0x4700, t16_simulate_bxblx),
/* ADD pc, pc 0100 0100 1111 1111 */
DECODE_REJECT (0xffff, 0x44ff),
/* ADD (register) 0100 0100 xxxx xxxx */
/* CMP (register) 0100 0101 xxxx xxxx */
/* MOV (register) 0100 0110 xxxx xxxx */
DECODE_CUSTOM (0xfc00, 0x4400, t16_decode_hiregs),
/*
* Load from Literal Pool
* LDR (literal) 0100 1xxx xxxx xxxx
*/
DECODE_SIMULATE (0xf800, 0x4800, t16_simulate_ldr_literal),
/*
* 16-bit Thumb Load/store instructions
* 0101 xxxx xxxx xxxx
* 011x xxxx xxxx xxxx
* 100x xxxx xxxx xxxx
*/
/* STR (register) 0101 000x xxxx xxxx */
/* STRH (register) 0101 001x xxxx xxxx */
/* STRB (register) 0101 010x xxxx xxxx */
/* LDRSB (register) 0101 011x xxxx xxxx */
/* LDR (register) 0101 100x xxxx xxxx */
/* LDRH (register) 0101 101x xxxx xxxx */
/* LDRB (register) 0101 110x xxxx xxxx */
/* LDRSH (register) 0101 111x xxxx xxxx */
/* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */
/* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */
/* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */
/* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */
DECODE_EMULATE (0xc000, 0x4000, t16_emulate_loregs_rwflags),
/* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */
/* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */
DECODE_EMULATE (0xf000, 0x8000, t16_emulate_loregs_rwflags),
/* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */
/* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */
DECODE_SIMULATE (0xf000, 0x9000, t16_simulate_ldrstr_sp_relative),
/*
* Generate PC-/SP-relative address
* ADR (literal) 1010 0xxx xxxx xxxx
* ADD (SP plus immediate) 1010 1xxx xxxx xxxx
*/
DECODE_SIMULATE (0xf000, 0xa000, t16_simulate_reladr),
/*
* Miscellaneous 16-bit instructions
* 1011 xxxx xxxx xxxx
*/
DECODE_TABLE (0xf000, 0xb000, t16_table_1011),
/* STM 1100 0xxx xxxx xxxx */
/* LDM 1100 1xxx xxxx xxxx */
DECODE_EMULATE (0xf000, 0xc000, t16_emulate_loregs_rwflags),
/*
* Conditional branch, and Supervisor Call
*/
/* Permanently UNDEFINED 1101 1110 xxxx xxxx */
/* SVC 1101 1111 xxxx xxxx */
DECODE_REJECT (0xfe00, 0xde00),
/* Conditional branch 1101 xxxx xxxx xxxx */
DECODE_CUSTOM (0xf000, 0xd000, t16_decode_cond_branch),
/*
* Unconditional branch
* B 1110 0xxx xxxx xxxx
*/
DECODE_SIMULATE (0xf800, 0xe000, t16_simulate_branch),
DECODE_END
};
static unsigned long __kprobes thumb_check_cc(unsigned long cpsr)
{
if (unlikely(in_it_block(cpsr)))
return kprobe_condition_checks[current_cond(cpsr)](cpsr);
return true;
}
static void __kprobes thumb16_singlestep(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_pc += 2;
p->ainsn.insn_handler(p, regs);
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
}
static void __kprobes thumb32_singlestep(struct kprobe *p, struct pt_regs *regs)
{
regs->ARM_pc += 4;
p->ainsn.insn_handler(p, regs);
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
}
enum kprobe_insn __kprobes
thumb16_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
asi->insn_singlestep = thumb16_singlestep;
asi->insn_check_cc = thumb_check_cc;
return kprobe_decode_insn(insn, asi, kprobe_decode_thumb16_table, true);
}
enum kprobe_insn __kprobes
thumb32_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
asi->insn_singlestep = thumb32_singlestep;
asi->insn_check_cc = thumb_check_cc;
return kprobe_decode_insn(insn, asi, kprobe_decode_thumb32_table, true);
}
...@@ -28,14 +28,16 @@ ...@@ -28,14 +28,16 @@
#include <asm/traps.h> #include <asm/traps.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include "kprobes.h"
#define MIN_STACK_SIZE(addr) \ #define MIN_STACK_SIZE(addr) \
min((unsigned long)MAX_STACK_SIZE, \ min((unsigned long)MAX_STACK_SIZE, \
(unsigned long)current_thread_info() + THREAD_START_SP - (addr)) (unsigned long)current_thread_info() + THREAD_START_SP - (addr))
#define flush_insns(addr, cnt) \ #define flush_insns(addr, size) \
flush_icache_range((unsigned long)(addr), \ flush_icache_range((unsigned long)(addr), \
(unsigned long)(addr) + \ (unsigned long)(addr) + \
sizeof(kprobe_opcode_t) * (cnt)) (size))
/* Used as a marker in ARM_pc to note when we're in a jprobe. */ /* Used as a marker in ARM_pc to note when we're in a jprobe. */
#define JPROBE_MAGIC_ADDR 0xffffffff #define JPROBE_MAGIC_ADDR 0xffffffff
...@@ -49,16 +51,35 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) ...@@ -49,16 +51,35 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
kprobe_opcode_t insn; kprobe_opcode_t insn;
kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
unsigned long addr = (unsigned long)p->addr; unsigned long addr = (unsigned long)p->addr;
bool thumb;
kprobe_decode_insn_t *decode_insn;
int is; int is;
if (addr & 0x3 || in_exception_text(addr)) if (in_exception_text(addr))
return -EINVAL; return -EINVAL;
#ifdef CONFIG_THUMB2_KERNEL
thumb = true;
addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
insn = ((u16 *)addr)[0];
if (is_wide_instruction(insn)) {
insn <<= 16;
insn |= ((u16 *)addr)[1];
decode_insn = thumb32_kprobe_decode_insn;
} else
decode_insn = thumb16_kprobe_decode_insn;
#else /* !CONFIG_THUMB2_KERNEL */
thumb = false;
if (addr & 0x3)
return -EINVAL;
insn = *p->addr; insn = *p->addr;
decode_insn = arm_kprobe_decode_insn;
#endif
p->opcode = insn; p->opcode = insn;
p->ainsn.insn = tmp_insn; p->ainsn.insn = tmp_insn;
switch (arm_kprobe_decode_insn(insn, &p->ainsn)) { switch ((*decode_insn)(insn, &p->ainsn)) {
case INSN_REJECTED: /* not supported */ case INSN_REJECTED: /* not supported */
return -EINVAL; return -EINVAL;
...@@ -68,7 +89,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) ...@@ -68,7 +89,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
return -ENOMEM; return -ENOMEM;
for (is = 0; is < MAX_INSN_SIZE; ++is) for (is = 0; is < MAX_INSN_SIZE; ++is)
p->ainsn.insn[is] = tmp_insn[is]; p->ainsn.insn[is] = tmp_insn[is];
flush_insns(p->ainsn.insn, MAX_INSN_SIZE); flush_insns(p->ainsn.insn,
sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE);
p->ainsn.insn_fn = (kprobe_insn_fn_t *)
((uintptr_t)p->ainsn.insn | thumb);
break; break;
case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */
...@@ -79,24 +103,88 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) ...@@ -79,24 +103,88 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
return 0; return 0;
} }
#ifdef CONFIG_THUMB2_KERNEL
/*
* For a 32-bit Thumb breakpoint spanning two memory words we need to take
* special precautions to insert the breakpoint atomically, especially on SMP
* systems. This is achieved by calling this arming function using stop_machine.
*/
static int __kprobes set_t32_breakpoint(void *addr)
{
((u16 *)addr)[0] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION >> 16;
((u16 *)addr)[1] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION & 0xffff;
flush_insns(addr, 2*sizeof(u16));
return 0;
}
void __kprobes arch_arm_kprobe(struct kprobe *p)
{
uintptr_t addr = (uintptr_t)p->addr & ~1; /* Remove any Thumb flag */
if (!is_wide_instruction(p->opcode)) {
*(u16 *)addr = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
flush_insns(addr, sizeof(u16));
} else if (addr & 2) {
/* A 32-bit instruction spanning two words needs special care */
stop_machine(set_t32_breakpoint, (void *)addr, &cpu_online_map);
} else {
/* Word aligned 32-bit instruction can be written atomically */
u32 bkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
#ifndef __ARMEB__ /* Swap halfwords for little-endian */
bkp = (bkp >> 16) | (bkp << 16);
#endif
*(u32 *)addr = bkp;
flush_insns(addr, sizeof(u32));
}
}
#else /* !CONFIG_THUMB2_KERNEL */
void __kprobes arch_arm_kprobe(struct kprobe *p) void __kprobes arch_arm_kprobe(struct kprobe *p)
{ {
*p->addr = KPROBE_BREAKPOINT_INSTRUCTION; kprobe_opcode_t insn = p->opcode;
flush_insns(p->addr, 1); kprobe_opcode_t brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
if (insn >= 0xe0000000)
brkp |= 0xe0000000; /* Unconditional instruction */
else
brkp |= insn & 0xf0000000; /* Copy condition from insn */
*p->addr = brkp;
flush_insns(p->addr, sizeof(p->addr[0]));
} }
#endif /* !CONFIG_THUMB2_KERNEL */
/* /*
* The actual disarming is done here on each CPU and synchronized using * The actual disarming is done here on each CPU and synchronized using
* stop_machine. This synchronization is necessary on SMP to avoid removing * stop_machine. This synchronization is necessary on SMP to avoid removing
* a probe between the moment the 'Undefined Instruction' exception is raised * a probe between the moment the 'Undefined Instruction' exception is raised
* and the moment the exception handler reads the faulting instruction from * and the moment the exception handler reads the faulting instruction from
* memory. * memory. It is also needed to atomically set the two half-words of a 32-bit
* Thumb breakpoint.
*/ */
int __kprobes __arch_disarm_kprobe(void *p) int __kprobes __arch_disarm_kprobe(void *p)
{ {
struct kprobe *kp = p; struct kprobe *kp = p;
#ifdef CONFIG_THUMB2_KERNEL
u16 *addr = (u16 *)((uintptr_t)kp->addr & ~1);
kprobe_opcode_t insn = kp->opcode;
unsigned int len;
if (is_wide_instruction(insn)) {
((u16 *)addr)[0] = insn>>16;
((u16 *)addr)[1] = insn;
len = 2*sizeof(u16);
} else {
((u16 *)addr)[0] = insn;
len = sizeof(u16);
}
flush_insns(addr, len);
#else /* !CONFIG_THUMB2_KERNEL */
*kp->addr = kp->opcode; *kp->addr = kp->opcode;
flush_insns(kp->addr, 1); flush_insns(kp->addr, sizeof(kp->addr[0]));
#endif
return 0; return 0;
} }
...@@ -130,12 +218,24 @@ static void __kprobes set_current_kprobe(struct kprobe *p) ...@@ -130,12 +218,24 @@ static void __kprobes set_current_kprobe(struct kprobe *p)
__get_cpu_var(current_kprobe) = p; __get_cpu_var(current_kprobe) = p;
} }
static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs, static void __kprobes
struct kprobe_ctlblk *kcb) singlestep_skip(struct kprobe *p, struct pt_regs *regs)
{ {
#ifdef CONFIG_THUMB2_KERNEL
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
if (is_wide_instruction(p->opcode))
regs->ARM_pc += 4; regs->ARM_pc += 4;
if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) else
p->ainsn.insn_handler(p, regs); regs->ARM_pc += 2;
#else
regs->ARM_pc += 4;
#endif
}
static inline void __kprobes
singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
{
p->ainsn.insn_singlestep(p, regs);
} }
/* /*
...@@ -149,11 +249,23 @@ void __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -149,11 +249,23 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
{ {
struct kprobe *p, *cur; struct kprobe *p, *cur;
struct kprobe_ctlblk *kcb; struct kprobe_ctlblk *kcb;
kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc;
kcb = get_kprobe_ctlblk(); kcb = get_kprobe_ctlblk();
cur = kprobe_running(); cur = kprobe_running();
p = get_kprobe(addr);
#ifdef CONFIG_THUMB2_KERNEL
/*
* First look for a probe which was registered using an address with
* bit 0 set, this is the usual situation for pointers to Thumb code.
* If not found, fallback to looking for one with bit 0 clear.
*/
p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1));
if (!p)
p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
#else /* ! CONFIG_THUMB2_KERNEL */
p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
#endif
if (p) { if (p) {
if (cur) { if (cur) {
...@@ -173,7 +285,8 @@ void __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -173,7 +285,8 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
/* impossible cases */ /* impossible cases */
BUG(); BUG();
} }
} else { } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
/* Probe hit and conditional execution check ok. */
set_current_kprobe(p); set_current_kprobe(p);
kcb->kprobe_status = KPROBE_HIT_ACTIVE; kcb->kprobe_status = KPROBE_HIT_ACTIVE;
...@@ -193,6 +306,13 @@ void __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -193,6 +306,13 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
} }
reset_current_kprobe(); reset_current_kprobe();
} }
} else {
/*
* Probe hit but conditional execution check failed,
* so just skip the instruction and continue as if
* nothing had happened.
*/
singlestep_skip(p, regs);
} }
} else if (cur) { } else if (cur) {
/* We probably hit a jprobe. Call its break handler. */ /* We probably hit a jprobe. Call its break handler. */
...@@ -300,7 +420,11 @@ void __naked __kprobes kretprobe_trampoline(void) ...@@ -300,7 +420,11 @@ void __naked __kprobes kretprobe_trampoline(void)
"bl trampoline_handler \n\t" "bl trampoline_handler \n\t"
"mov lr, r0 \n\t" "mov lr, r0 \n\t"
"ldmia sp!, {r0 - r11} \n\t" "ldmia sp!, {r0 - r11} \n\t"
#ifdef CONFIG_THUMB2_KERNEL
"bx lr \n\t"
#else
"mov pc, lr \n\t" "mov pc, lr \n\t"
#endif
: : : "memory"); : : : "memory");
} }
...@@ -378,11 +502,22 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) ...@@ -378,11 +502,22 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
struct jprobe *jp = container_of(p, struct jprobe, kp); struct jprobe *jp = container_of(p, struct jprobe, kp);
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
long sp_addr = regs->ARM_sp; long sp_addr = regs->ARM_sp;
long cpsr;
kcb->jprobe_saved_regs = *regs; kcb->jprobe_saved_regs = *regs;
memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
regs->ARM_pc = (long)jp->entry; regs->ARM_pc = (long)jp->entry;
regs->ARM_cpsr |= PSR_I_BIT;
cpsr = regs->ARM_cpsr | PSR_I_BIT;
#ifdef CONFIG_THUMB2_KERNEL
/* Set correct Thumb state in cpsr */
if (regs->ARM_pc & 1)
cpsr |= PSR_T_BIT;
else
cpsr &= ~PSR_T_BIT;
#endif
regs->ARM_cpsr = cpsr;
preempt_disable(); preempt_disable();
return 1; return 1;
} }
...@@ -404,7 +539,12 @@ void __kprobes jprobe_return(void) ...@@ -404,7 +539,12 @@ void __kprobes jprobe_return(void)
* This is to prevent any simulated instruction from writing * This is to prevent any simulated instruction from writing
* over the regs when they are accessing the stack. * over the regs when they are accessing the stack.
*/ */
#ifdef CONFIG_THUMB2_KERNEL
"sub r0, %0, %1 \n\t"
"mov sp, r0 \n\t"
#else
"sub sp, %0, %1 \n\t" "sub sp, %0, %1 \n\t"
#endif
"ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t" "ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t"
"str %0, [sp, %2] \n\t" "str %0, [sp, %2] \n\t"
"str r0, [sp, %3] \n\t" "str r0, [sp, %3] \n\t"
...@@ -415,15 +555,28 @@ void __kprobes jprobe_return(void) ...@@ -415,15 +555,28 @@ void __kprobes jprobe_return(void)
* Return to the context saved by setjmp_pre_handler * Return to the context saved by setjmp_pre_handler
* and restored by longjmp_break_handler. * and restored by longjmp_break_handler.
*/ */
#ifdef CONFIG_THUMB2_KERNEL
"ldr lr, [sp, %2] \n\t" /* lr = saved sp */
"ldrd r0, r1, [sp, %5] \n\t" /* r0,r1 = saved lr,pc */
"ldr r2, [sp, %4] \n\t" /* r2 = saved psr */
"stmdb lr!, {r0, r1, r2} \n\t" /* push saved lr and */
/* rfe context */
"ldmia sp, {r0 - r12} \n\t"
"mov sp, lr \n\t"
"ldr lr, [sp], #4 \n\t"
"rfeia sp! \n\t"
#else
"ldr r0, [sp, %4] \n\t" "ldr r0, [sp, %4] \n\t"
"msr cpsr_cxsf, r0 \n\t" "msr cpsr_cxsf, r0 \n\t"
"ldmia sp, {r0 - pc} \n\t" "ldmia sp, {r0 - pc} \n\t"
#endif
: :
: "r" (kcb->jprobe_saved_regs.ARM_sp), : "r" (kcb->jprobe_saved_regs.ARM_sp),
"I" (sizeof(struct pt_regs) * 2), "I" (sizeof(struct pt_regs) * 2),
"J" (offsetof(struct pt_regs, ARM_sp)), "J" (offsetof(struct pt_regs, ARM_sp)),
"J" (offsetof(struct pt_regs, ARM_pc)), "J" (offsetof(struct pt_regs, ARM_pc)),
"J" (offsetof(struct pt_regs, ARM_cpsr)) "J" (offsetof(struct pt_regs, ARM_cpsr)),
"J" (offsetof(struct pt_regs, ARM_lr))
: "memory", "cc"); : "memory", "cc");
} }
...@@ -460,17 +613,44 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p) ...@@ -460,17 +613,44 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
return 0; return 0;
} }
static struct undef_hook kprobes_break_hook = { #ifdef CONFIG_THUMB2_KERNEL
static struct undef_hook kprobes_thumb16_break_hook = {
.instr_mask = 0xffff,
.instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION,
.cpsr_mask = MODE_MASK,
.cpsr_val = SVC_MODE,
.fn = kprobe_trap_handler,
};
static struct undef_hook kprobes_thumb32_break_hook = {
.instr_mask = 0xffffffff, .instr_mask = 0xffffffff,
.instr_val = KPROBE_BREAKPOINT_INSTRUCTION, .instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION,
.cpsr_mask = MODE_MASK,
.cpsr_val = SVC_MODE,
.fn = kprobe_trap_handler,
};
#else /* !CONFIG_THUMB2_KERNEL */
static struct undef_hook kprobes_arm_break_hook = {
.instr_mask = 0x0fffffff,
.instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION,
.cpsr_mask = MODE_MASK, .cpsr_mask = MODE_MASK,
.cpsr_val = SVC_MODE, .cpsr_val = SVC_MODE,
.fn = kprobe_trap_handler, .fn = kprobe_trap_handler,
}; };
#endif /* !CONFIG_THUMB2_KERNEL */
int __init arch_init_kprobes() int __init arch_init_kprobes()
{ {
arm_kprobe_decode_init(); arm_kprobe_decode_init();
register_undef_hook(&kprobes_break_hook); #ifdef CONFIG_THUMB2_KERNEL
register_undef_hook(&kprobes_thumb16_break_hook);
register_undef_hook(&kprobes_thumb32_break_hook);
#else
register_undef_hook(&kprobes_arm_break_hook);
#endif
return 0; return 0;
} }
/*
* arch/arm/kernel/kprobes.h
*
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
*
* Some contents moved here from arch/arm/include/asm/kprobes.h which is
* Copyright (C) 2006, 2007 Motorola Inc.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#ifndef _ARM_KERNEL_KPROBES_H
#define _ARM_KERNEL_KPROBES_H
/*
* These undefined instructions must be unique and
* reserved solely for kprobes' use.
*/
#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0x07f001f8
#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18
#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018
enum kprobe_insn {
INSN_REJECTED,
INSN_GOOD,
INSN_GOOD_NO_SLOT
};
typedef enum kprobe_insn (kprobe_decode_insn_t)(kprobe_opcode_t,
struct arch_specific_insn *);
#ifdef CONFIG_THUMB2_KERNEL
enum kprobe_insn thumb16_kprobe_decode_insn(kprobe_opcode_t,
struct arch_specific_insn *);
enum kprobe_insn thumb32_kprobe_decode_insn(kprobe_opcode_t,
struct arch_specific_insn *);
#else /* !CONFIG_THUMB2_KERNEL */
enum kprobe_insn arm_kprobe_decode_insn(kprobe_opcode_t,
struct arch_specific_insn *);
#endif
void __init arm_kprobe_decode_init(void);
extern kprobe_check_cc * const kprobe_condition_checks[16];
#if __LINUX_ARM_ARCH__ >= 7
/* str_pc_offset is architecturally defined from ARMv7 onwards */
#define str_pc_offset 8
#define find_str_pc_offset()
#else /* __LINUX_ARM_ARCH__ < 7 */
/* We need a run-time check to determine str_pc_offset */
extern int str_pc_offset;
void __init find_str_pc_offset(void);
#endif
/*
* Update ITSTATE after normal execution of an IT block instruction.
*
* The 8 IT state bits are split into two parts in CPSR:
* ITSTATE<1:0> are in CPSR<26:25>
* ITSTATE<7:2> are in CPSR<15:10>
*/
static inline unsigned long it_advance(unsigned long cpsr)
{
if ((cpsr & 0x06000400) == 0) {
/* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */
cpsr &= ~PSR_IT_MASK;
} else {
/* We need to shift left ITSTATE<4:0> */
const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */
unsigned long it = cpsr & mask;
it <<= 1;
it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */
it &= mask;
cpsr &= ~mask;
cpsr |= it;
}
return cpsr;
}
static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
{
long cpsr = regs->ARM_cpsr;
if (pcv & 0x1) {
cpsr |= PSR_T_BIT;
pcv &= ~0x1;
} else {
cpsr &= ~PSR_T_BIT;
pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */
}
regs->ARM_cpsr = cpsr;
regs->ARM_pc = pcv;
}
#if __LINUX_ARM_ARCH__ >= 6
/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */
#define load_write_pc_interworks true
#define test_load_write_pc_interworking()
#else /* __LINUX_ARM_ARCH__ < 6 */
/* We need run-time testing to determine if load_write_pc() should interwork. */
extern bool load_write_pc_interworks;
void __init test_load_write_pc_interworking(void);
#endif
static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs)
{
if (load_write_pc_interworks)
bx_write_pc(pcv, regs);
else
regs->ARM_pc = pcv;
}
#if __LINUX_ARM_ARCH__ >= 7
#define alu_write_pc_interworks true
#define test_alu_write_pc_interworking()
#elif __LINUX_ARM_ARCH__ <= 5
/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */
#define alu_write_pc_interworks false
#define test_alu_write_pc_interworking()
#else /* __LINUX_ARM_ARCH__ == 6 */
/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */
extern bool alu_write_pc_interworks;
void __init test_alu_write_pc_interworking(void);
#endif /* __LINUX_ARM_ARCH__ == 6 */
static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
{
if (alu_write_pc_interworks)
bx_write_pc(pcv, regs);
else
regs->ARM_pc = pcv;
}
void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
enum kprobe_insn __kprobes
kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
/*
* 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)
/*
* The following definitions and macros are used to build instruction
* decoding tables for use by kprobe_decode_insn.
*
* These tables are a concatenation of entries each of which consist of one of
* the decode_* structs. All of the fields in every type of decode structure
* are of the union type decode_item, therefore the entire decode table can be
* viewed as an array of these and declared like:
*
* static const union decode_item table_name[] = {};
*
* In order to construct each entry in the table, macros are used to
* initialise a number of sequential decode_item values in a layout which
* matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct
* decode_simulate by initialising four decode_item objects like this...
*
* {.bits = _type},
* {.bits = _mask},
* {.bits = _value},
* {.handler = _handler},
*
* Initialising a specified member of the union means that the compiler
* will produce a warning if the argument is of an incorrect type.
*
* Below is a list of each of the macros used to initialise entries and a
* description of the action performed when that entry is matched to an
* instruction. A match is found when (instruction & mask) == value.
*
* DECODE_TABLE(mask, value, table)
* Instruction decoding jumps to parsing the new sub-table 'table'.
*
* DECODE_CUSTOM(mask, value, decoder)
* The custom function 'decoder' is called to the complete decoding
* of an instruction.
*
* DECODE_SIMULATE(mask, value, handler)
* Set the probes instruction handler to 'handler', this will be used
* to simulate the instruction when the probe is hit. Decoding returns
* with INSN_GOOD_NO_SLOT.
*
* DECODE_EMULATE(mask, value, handler)
* Set the probes instruction handler to 'handler', this will be used
* to emulate the instruction when the probe is hit. The modified
* instruction (see below) is placed in the probes instruction slot so it
* may be called by the emulation code. Decoding returns with INSN_GOOD.
*
* DECODE_REJECT(mask, value)
* Instruction decoding fails with INSN_REJECTED
*
* DECODE_OR(mask, value)
* This allows the mask/value test of multiple table entries to be
* logically ORed. Once an 'or' entry is matched the decoding action to
* be performed is that of the next entry which isn't an 'or'. E.g.
*
* DECODE_OR (mask1, value1)
* DECODE_OR (mask2, value2)
* DECODE_SIMULATE (mask3, value3, simulation_handler)
*
* This means that if any of the three mask/value pairs match the
* instruction being decoded, then 'simulation_handler' will be used
* for it.
*
* Both the SIMULATE and EMULATE macros have a second form which take an
* additional 'regs' argument.
*
* DECODE_SIMULATEX(mask, value, handler, regs)
* DECODE_EMULATEX (mask, value, handler, regs)
*
* These are used to specify what kind of CPU register is encoded in each of the
* least significant 5 nibbles of the instruction being decoded. The regs value
* is specified using the REGS macro, this takes any of the REG_TYPE_* values
* from enum decode_reg_type as arguments; only the '*' part of the name is
* given. E.g.
*
* REGS(0, ANY, NOPC, 0, ANY)
*
* This indicates an instruction is encoded like:
*
* bits 19..16 ignore
* bits 15..12 any register allowed here
* bits 11.. 8 any register except PC allowed here
* bits 7.. 4 ignore
* bits 3.. 0 any register allowed here
*
* This register specification is checked after a decode table entry is found to
* match an instruction (through the mask/value test). Any invalid register then
* found in the instruction will cause decoding to fail with INSN_REJECTED. In
* the above example this would happen if bits 11..8 of the instruction were
* 1111, indicating R15 or PC.
*
* As well as checking for legal combinations of registers, this data is also
* used to modify the registers encoded in the instructions so that an
* emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.)
*
* Here is a real example which matches ARM instructions of the form
* "AND <Rd>,<Rn>,<Rm>,<shift> <Rs>"
*
* DECODE_EMULATEX (0x0e000090, 0x00000010, emulate_rd12rn16rm0rs8_rwflags,
* REGS(ANY, ANY, NOPC, 0, ANY)),
* ^ ^ ^ ^
* Rn Rd Rs Rm
*
* Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because
* Rs == R15
*
* Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the
* instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into
* the kprobes instruction slot. This can then be called later by the handler
* function emulate_rd12rn16rm0rs8_rwflags in order to simulate the instruction.
*/
enum decode_type {
DECODE_TYPE_END,
DECODE_TYPE_TABLE,
DECODE_TYPE_CUSTOM,
DECODE_TYPE_SIMULATE,
DECODE_TYPE_EMULATE,
DECODE_TYPE_OR,
DECODE_TYPE_REJECT,
NUM_DECODE_TYPES /* Must be last enum */
};
#define DECODE_TYPE_BITS 4
#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1)
enum decode_reg_type {
REG_TYPE_NONE = 0, /* Not a register, ignore */
REG_TYPE_ANY, /* Any register allowed */
REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */
REG_TYPE_SP, /* Register must be SP */
REG_TYPE_PC, /* Register must be PC */
REG_TYPE_NOSP, /* Register must not be SP */
REG_TYPE_NOSPPC, /* Register must not be SP or PC */
REG_TYPE_NOPC, /* Register must not be PC */
REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */
/* The following types are used when the encoding for PC indicates
* another instruction form. This distiction only matters for test
* case coverage checks.
*/
REG_TYPE_NOPCX, /* Register must not be PC */
REG_TYPE_NOSPPCX, /* Register must not be SP or PC */
/* Alias to allow '0' arg to be used in REGS macro. */
REG_TYPE_0 = REG_TYPE_NONE
};
#define REGS(r16, r12, r8, r4, r0) \
((REG_TYPE_##r16) << 16) + \
((REG_TYPE_##r12) << 12) + \
((REG_TYPE_##r8) << 8) + \
((REG_TYPE_##r4) << 4) + \
(REG_TYPE_##r0)
union decode_item {
u32 bits;
const union decode_item *table;
kprobe_insn_handler_t *handler;
kprobe_decode_insn_t *decoder;
};
#define DECODE_END \
{.bits = DECODE_TYPE_END}
struct decode_header {
union decode_item type_regs;
union decode_item mask;
union decode_item value;
};
#define DECODE_HEADER(_type, _mask, _value, _regs) \
{.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \
{.bits = (_mask)}, \
{.bits = (_value)}
struct decode_table {
struct decode_header header;
union decode_item table;
};
#define DECODE_TABLE(_mask, _value, _table) \
DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \
{.table = (_table)}
struct decode_custom {
struct decode_header header;
union decode_item decoder;
};
#define DECODE_CUSTOM(_mask, _value, _decoder) \
DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \
{.decoder = (_decoder)}
struct decode_simulate {
struct decode_header header;
union decode_item handler;
};
#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \
DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \
{.handler = (_handler)}
#define DECODE_SIMULATE(_mask, _value, _handler) \
DECODE_SIMULATEX(_mask, _value, _handler, 0)
struct decode_emulate {
struct decode_header header;
union decode_item handler;
};
#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \
DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \
{.handler = (_handler)}
#define DECODE_EMULATE(_mask, _value, _handler) \
DECODE_EMULATEX(_mask, _value, _handler, 0)
struct decode_or {
struct decode_header header;
};
#define DECODE_OR(_mask, _value) \
DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0)
struct decode_reject {
struct decode_header header;
};
#define DECODE_REJECT(_mask, _value) \
DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
int kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
const union decode_item *table, bool thumb16);
#endif /* _ARM_KERNEL_KPROBES_H */
...@@ -662,6 +662,12 @@ init_hw_perf_events(void) ...@@ -662,6 +662,12 @@ init_hw_perf_events(void)
case 0xC090: /* Cortex-A9 */ case 0xC090: /* Cortex-A9 */
armpmu = armv7_a9_pmu_init(); armpmu = armv7_a9_pmu_init();
break; break;
case 0xC050: /* Cortex-A5 */
armpmu = armv7_a5_pmu_init();
break;
case 0xC0F0: /* Cortex-A15 */
armpmu = armv7_a15_pmu_init();
break;
} }
/* Intel CPUs [xscale]. */ /* Intel CPUs [xscale]. */
} else if (0x69 == implementor) { } else if (0x69 == implementor) {
......
...@@ -17,17 +17,23 @@ ...@@ -17,17 +17,23 @@
*/ */
#ifdef CONFIG_CPU_V7 #ifdef CONFIG_CPU_V7
/* Common ARMv7 event types */ /*
* Common ARMv7 event types
*
* Note: An implementation may not be able to count all of these events
* but the encodings are considered to be `reserved' in the case that
* they are not available.
*/
enum armv7_perf_types { enum armv7_perf_types {
ARMV7_PERFCTR_PMNC_SW_INCR = 0x00, ARMV7_PERFCTR_PMNC_SW_INCR = 0x00,
ARMV7_PERFCTR_IFETCH_MISS = 0x01, ARMV7_PERFCTR_IFETCH_MISS = 0x01,
ARMV7_PERFCTR_ITLB_MISS = 0x02, ARMV7_PERFCTR_ITLB_MISS = 0x02,
ARMV7_PERFCTR_DCACHE_REFILL = 0x03, ARMV7_PERFCTR_DCACHE_REFILL = 0x03, /* L1 */
ARMV7_PERFCTR_DCACHE_ACCESS = 0x04, ARMV7_PERFCTR_DCACHE_ACCESS = 0x04, /* L1 */
ARMV7_PERFCTR_DTLB_REFILL = 0x05, ARMV7_PERFCTR_DTLB_REFILL = 0x05,
ARMV7_PERFCTR_DREAD = 0x06, ARMV7_PERFCTR_DREAD = 0x06,
ARMV7_PERFCTR_DWRITE = 0x07, ARMV7_PERFCTR_DWRITE = 0x07,
ARMV7_PERFCTR_INSTR_EXECUTED = 0x08,
ARMV7_PERFCTR_EXC_TAKEN = 0x09, ARMV7_PERFCTR_EXC_TAKEN = 0x09,
ARMV7_PERFCTR_EXC_EXECUTED = 0x0A, ARMV7_PERFCTR_EXC_EXECUTED = 0x0A,
ARMV7_PERFCTR_CID_WRITE = 0x0B, ARMV7_PERFCTR_CID_WRITE = 0x0B,
...@@ -39,21 +45,30 @@ enum armv7_perf_types { ...@@ -39,21 +45,30 @@ enum armv7_perf_types {
*/ */
ARMV7_PERFCTR_PC_WRITE = 0x0C, ARMV7_PERFCTR_PC_WRITE = 0x0C,
ARMV7_PERFCTR_PC_IMM_BRANCH = 0x0D, ARMV7_PERFCTR_PC_IMM_BRANCH = 0x0D,
ARMV7_PERFCTR_PC_PROC_RETURN = 0x0E,
ARMV7_PERFCTR_UNALIGNED_ACCESS = 0x0F, ARMV7_PERFCTR_UNALIGNED_ACCESS = 0x0F,
/* These events are defined by the PMUv2 supplement (ARM DDI 0457A). */
ARMV7_PERFCTR_PC_BRANCH_MIS_PRED = 0x10, ARMV7_PERFCTR_PC_BRANCH_MIS_PRED = 0x10,
ARMV7_PERFCTR_CLOCK_CYCLES = 0x11, ARMV7_PERFCTR_CLOCK_CYCLES = 0x11,
ARMV7_PERFCTR_PC_BRANCH_PRED = 0x12,
ARMV7_PERFCTR_PC_BRANCH_MIS_USED = 0x12, ARMV7_PERFCTR_MEM_ACCESS = 0x13,
ARMV7_PERFCTR_L1_ICACHE_ACCESS = 0x14,
ARMV7_PERFCTR_L1_DCACHE_WB = 0x15,
ARMV7_PERFCTR_L2_DCACHE_ACCESS = 0x16,
ARMV7_PERFCTR_L2_DCACHE_REFILL = 0x17,
ARMV7_PERFCTR_L2_DCACHE_WB = 0x18,
ARMV7_PERFCTR_BUS_ACCESS = 0x19,
ARMV7_PERFCTR_MEMORY_ERROR = 0x1A,
ARMV7_PERFCTR_INSTR_SPEC = 0x1B,
ARMV7_PERFCTR_TTBR_WRITE = 0x1C,
ARMV7_PERFCTR_BUS_CYCLES = 0x1D,
ARMV7_PERFCTR_CPU_CYCLES = 0xFF ARMV7_PERFCTR_CPU_CYCLES = 0xFF
}; };
/* ARMv7 Cortex-A8 specific event types */ /* ARMv7 Cortex-A8 specific event types */
enum armv7_a8_perf_types { enum armv7_a8_perf_types {
ARMV7_PERFCTR_INSTR_EXECUTED = 0x08,
ARMV7_PERFCTR_PC_PROC_RETURN = 0x0E,
ARMV7_PERFCTR_WRITE_BUFFER_FULL = 0x40, ARMV7_PERFCTR_WRITE_BUFFER_FULL = 0x40,
ARMV7_PERFCTR_L2_STORE_MERGED = 0x41, ARMV7_PERFCTR_L2_STORE_MERGED = 0x41,
ARMV7_PERFCTR_L2_STORE_BUFF = 0x42, ARMV7_PERFCTR_L2_STORE_BUFF = 0x42,
...@@ -138,6 +153,39 @@ enum armv7_a9_perf_types { ...@@ -138,6 +153,39 @@ enum armv7_a9_perf_types {
ARMV7_PERFCTR_PLE_RQST_PROG = 0xA5 ARMV7_PERFCTR_PLE_RQST_PROG = 0xA5
}; };
/* ARMv7 Cortex-A5 specific event types */
enum armv7_a5_perf_types {
ARMV7_PERFCTR_IRQ_TAKEN = 0x86,
ARMV7_PERFCTR_FIQ_TAKEN = 0x87,
ARMV7_PERFCTR_EXT_MEM_RQST = 0xc0,
ARMV7_PERFCTR_NC_EXT_MEM_RQST = 0xc1,
ARMV7_PERFCTR_PREFETCH_LINEFILL = 0xc2,
ARMV7_PERFCTR_PREFETCH_LINEFILL_DROP = 0xc3,
ARMV7_PERFCTR_ENTER_READ_ALLOC = 0xc4,
ARMV7_PERFCTR_READ_ALLOC = 0xc5,
ARMV7_PERFCTR_STALL_SB_FULL = 0xc9,
};
/* ARMv7 Cortex-A15 specific event types */
enum armv7_a15_perf_types {
ARMV7_PERFCTR_L1_DCACHE_READ_ACCESS = 0x40,
ARMV7_PERFCTR_L1_DCACHE_WRITE_ACCESS = 0x41,
ARMV7_PERFCTR_L1_DCACHE_READ_REFILL = 0x42,
ARMV7_PERFCTR_L1_DCACHE_WRITE_REFILL = 0x43,
ARMV7_PERFCTR_L1_DTLB_READ_REFILL = 0x4C,
ARMV7_PERFCTR_L1_DTLB_WRITE_REFILL = 0x4D,
ARMV7_PERFCTR_L2_DCACHE_READ_ACCESS = 0x50,
ARMV7_PERFCTR_L2_DCACHE_WRITE_ACCESS = 0x51,
ARMV7_PERFCTR_L2_DCACHE_READ_REFILL = 0x52,
ARMV7_PERFCTR_L2_DCACHE_WRITE_REFILL = 0x53,
ARMV7_PERFCTR_SPEC_PC_WRITE = 0x76,
};
/* /*
* Cortex-A8 HW events mapping * Cortex-A8 HW events mapping
* *
...@@ -207,11 +255,6 @@ static const unsigned armv7_a8_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] ...@@ -207,11 +255,6 @@ static const unsigned armv7_a8_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
}, },
}, },
[C(DTLB)] = { [C(DTLB)] = {
/*
* Only ITLB misses and DTLB refills are supported.
* If users want the DTLB refills misses a raw counter
* must be used.
*/
[C(OP_READ)] = { [C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL, [C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL,
...@@ -323,11 +366,6 @@ static const unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] ...@@ -323,11 +366,6 @@ static const unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
}, },
}, },
[C(DTLB)] = { [C(DTLB)] = {
/*
* Only ITLB misses and DTLB refills are supported.
* If users want the DTLB refills misses a raw counter
* must be used.
*/
[C(OP_READ)] = { [C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL, [C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL,
...@@ -373,6 +411,242 @@ static const unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] ...@@ -373,6 +411,242 @@ static const unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
}, },
}; };
/*
* Cortex-A5 HW events mapping
*/
static const unsigned armv7_a5_perf_map[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
[PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED,
[PERF_COUNT_HW_CACHE_MISSES] = HW_OP_UNSUPPORTED,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
[PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
[PERF_COUNT_HW_BUS_CYCLES] = HW_OP_UNSUPPORTED,
};
static const unsigned armv7_a5_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
[C(L1D)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_DCACHE_ACCESS,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_DCACHE_REFILL,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_DCACHE_ACCESS,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_DCACHE_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_PREFETCH_LINEFILL,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_PREFETCH_LINEFILL_DROP,
},
},
[C(L1I)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(RESULT_MISS)] = ARMV7_PERFCTR_IFETCH_MISS,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(RESULT_MISS)] = ARMV7_PERFCTR_IFETCH_MISS,
},
/*
* The prefetch counters don't differentiate between the I
* side and the D side.
*/
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_PREFETCH_LINEFILL,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_PREFETCH_LINEFILL_DROP,
},
},
[C(LL)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(DTLB)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_DTLB_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(ITLB)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_ITLB_MISS,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_ITLB_MISS,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(BPU)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
};
/*
* Cortex-A15 HW events mapping
*/
static const unsigned armv7_a15_perf_map[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
[PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED,
[PERF_COUNT_HW_CACHE_MISSES] = HW_OP_UNSUPPORTED,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_SPEC_PC_WRITE,
[PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
[PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_BUS_CYCLES,
};
static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
[C(L1D)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_L1_DCACHE_READ_ACCESS,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_L1_DCACHE_READ_REFILL,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_L1_DCACHE_WRITE_ACCESS,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_L1_DCACHE_WRITE_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(L1I)] = {
/*
* Not all performance counters differentiate between read
* and write accesses/misses so we're not always strictly
* correct, but it's the best we can do. Writes and reads get
* combined in these cases.
*/
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(RESULT_MISS)] = ARMV7_PERFCTR_IFETCH_MISS,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_L1_ICACHE_ACCESS,
[C(RESULT_MISS)] = ARMV7_PERFCTR_IFETCH_MISS,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(LL)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_L2_DCACHE_READ_ACCESS,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_L2_DCACHE_READ_REFILL,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)]
= ARMV7_PERFCTR_L2_DCACHE_WRITE_ACCESS,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_L2_DCACHE_WRITE_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(DTLB)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_L1_DTLB_READ_REFILL,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_L1_DTLB_WRITE_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(ITLB)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_ITLB_MISS,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = ARMV7_PERFCTR_ITLB_MISS,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
[C(BPU)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = ARMV7_PERFCTR_PC_BRANCH_PRED,
[C(RESULT_MISS)]
= ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
[C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED,
},
},
};
/* /*
* Perf Events counters * Perf Events counters
*/ */
...@@ -905,6 +1179,26 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void) ...@@ -905,6 +1179,26 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void)
armv7pmu.num_events = armv7_read_num_pmnc_events(); armv7pmu.num_events = armv7_read_num_pmnc_events();
return &armv7pmu; return &armv7pmu;
} }
static const struct arm_pmu *__init armv7_a5_pmu_init(void)
{
armv7pmu.id = ARM_PERF_PMU_ID_CA5;
armv7pmu.name = "ARMv7 Cortex-A5";
armv7pmu.cache_map = &armv7_a5_perf_cache_map;
armv7pmu.event_map = &armv7_a5_perf_map;
armv7pmu.num_events = armv7_read_num_pmnc_events();
return &armv7pmu;
}
static const struct arm_pmu *__init armv7_a15_pmu_init(void)
{
armv7pmu.id = ARM_PERF_PMU_ID_CA15;
armv7pmu.name = "ARMv7 Cortex-A15";
armv7pmu.cache_map = &armv7_a15_perf_cache_map;
armv7pmu.event_map = &armv7_a15_perf_map;
armv7pmu.num_events = armv7_read_num_pmnc_events();
return &armv7pmu;
}
#else #else
static const struct arm_pmu *__init armv7_a8_pmu_init(void) static const struct arm_pmu *__init armv7_a8_pmu_init(void)
{ {
...@@ -915,4 +1209,14 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void) ...@@ -915,4 +1209,14 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void)
{ {
return NULL; return NULL;
} }
static const struct arm_pmu *__init armv7_a5_pmu_init(void)
{
return NULL;
}
static const struct arm_pmu *__init armv7_a15_pmu_init(void)
{
return NULL;
}
#endif /* CONFIG_CPU_V7 */ #endif /* CONFIG_CPU_V7 */
...@@ -228,34 +228,12 @@ static struct undef_hook thumb_break_hook = { ...@@ -228,34 +228,12 @@ static struct undef_hook thumb_break_hook = {
.fn = break_trap, .fn = break_trap,
}; };
static int thumb2_break_trap(struct pt_regs *regs, unsigned int instr)
{
unsigned int instr2;
void __user *pc;
/* Check the second half of the instruction. */
pc = (void __user *)(instruction_pointer(regs) + 2);
if (processor_mode(regs) == SVC_MODE) {
instr2 = *(u16 *) pc;
} else {
get_user(instr2, (u16 __user *)pc);
}
if (instr2 == 0xa000) {
ptrace_break(current, regs);
return 0;
} else {
return 1;
}
}
static struct undef_hook thumb2_break_hook = { static struct undef_hook thumb2_break_hook = {
.instr_mask = 0xffff, .instr_mask = 0xffffffff,
.instr_val = 0xf7f0, .instr_val = 0xf7f0a000,
.cpsr_mask = PSR_T_BIT, .cpsr_mask = PSR_T_BIT,
.cpsr_val = PSR_T_BIT, .cpsr_val = PSR_T_BIT,
.fn = thumb2_break_trap, .fn = break_trap,
}; };
static int __init ptrace_break_init(void) static int __init ptrace_break_init(void)
......
...@@ -919,6 +919,12 @@ void __init setup_arch(char **cmdline_p) ...@@ -919,6 +919,12 @@ void __init setup_arch(char **cmdline_p)
tcm_init(); tcm_init();
#ifdef CONFIG_ZONE_DMA
if (mdesc->dma_zone_size) {
extern unsigned long arm_dma_zone_size;
arm_dma_zone_size = mdesc->dma_zone_size;
}
#endif
#ifdef CONFIG_MULTI_IRQ_HANDLER #ifdef CONFIG_MULTI_IRQ_HANDLER
handle_arch_irq = mdesc->handle_irq; handle_arch_irq = mdesc->handle_irq;
#endif #endif
...@@ -980,6 +986,10 @@ static const char *hwcap_str[] = { ...@@ -980,6 +986,10 @@ static const char *hwcap_str[] = {
"neon", "neon",
"vfpv3", "vfpv3",
"vfpv3d16", "vfpv3d16",
"tls",
"vfpv4",
"idiva",
"idivt",
NULL NULL
}; };
......
...@@ -355,9 +355,24 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) ...@@ -355,9 +355,24 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
pc = (void __user *)instruction_pointer(regs); pc = (void __user *)instruction_pointer(regs);
if (processor_mode(regs) == SVC_MODE) { if (processor_mode(regs) == SVC_MODE) {
#ifdef CONFIG_THUMB2_KERNEL
if (thumb_mode(regs)) {
instr = ((u16 *)pc)[0];
if (is_wide_instruction(instr)) {
instr <<= 16;
instr |= ((u16 *)pc)[1];
}
} else
#endif
instr = *(u32 *) pc; instr = *(u32 *) pc;
} else if (thumb_mode(regs)) { } else if (thumb_mode(regs)) {
get_user(instr, (u16 __user *)pc); get_user(instr, (u16 __user *)pc);
if (is_wide_instruction(instr)) {
unsigned int instr2;
get_user(instr2, (u16 __user *)pc+1);
instr <<= 16;
instr |= instr2;
}
} else { } else {
get_user(instr, (u32 __user *)pc); get_user(instr, (u32 __user *)pc);
} }
......
...@@ -681,4 +681,5 @@ MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP-L137/AM17x EVM") ...@@ -681,4 +681,5 @@ MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP-L137/AM17x EVM")
.init_irq = cp_intc_init, .init_irq = cp_intc_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = da830_evm_init, .init_machine = da830_evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -1261,4 +1261,5 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") ...@@ -1261,4 +1261,5 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM")
.init_irq = cp_intc_init, .init_irq = cp_intc_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = da850_evm_init, .init_machine = da850_evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -356,4 +356,5 @@ MACHINE_START(DAVINCI_DM355_EVM, "DaVinci DM355 EVM") ...@@ -356,4 +356,5 @@ MACHINE_START(DAVINCI_DM355_EVM, "DaVinci DM355 EVM")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = dm355_evm_init, .init_machine = dm355_evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -275,4 +275,5 @@ MACHINE_START(DM355_LEOPARD, "DaVinci DM355 leopard") ...@@ -275,4 +275,5 @@ MACHINE_START(DM355_LEOPARD, "DaVinci DM355 leopard")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = dm355_leopard_init, .init_machine = dm355_leopard_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -617,5 +617,6 @@ MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") ...@@ -617,5 +617,6 @@ MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = dm365_evm_init, .init_machine = dm365_evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -717,4 +717,5 @@ MACHINE_START(DAVINCI_EVM, "DaVinci DM644x EVM") ...@@ -717,4 +717,5 @@ MACHINE_START(DAVINCI_EVM, "DaVinci DM644x EVM")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = davinci_evm_init, .init_machine = davinci_evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -802,6 +802,7 @@ MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") ...@@ -802,6 +802,7 @@ MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = evm_init, .init_machine = evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM")
...@@ -810,5 +811,6 @@ MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM") ...@@ -810,5 +811,6 @@ MACHINE_START(DAVINCI_DM6467TEVM, "DaVinci DM6467T EVM")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = evm_init, .init_machine = evm_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -570,4 +570,5 @@ MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") ...@@ -570,4 +570,5 @@ MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808")
.init_irq = cp_intc_init, .init_irq = cp_intc_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = mityomapl138_init, .init_machine = mityomapl138_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -277,4 +277,5 @@ MACHINE_START(NEUROS_OSD2, "Neuros OSD2") ...@@ -277,4 +277,5 @@ MACHINE_START(NEUROS_OSD2, "Neuros OSD2")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = davinci_ntosd2_init, .init_machine = davinci_ntosd2_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -343,4 +343,5 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") ...@@ -343,4 +343,5 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard")
.init_irq = cp_intc_init, .init_irq = cp_intc_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = omapl138_hawk_init, .init_machine = omapl138_hawk_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -156,4 +156,5 @@ MACHINE_START(SFFSDR, "Lyrtech SFFSDR") ...@@ -156,4 +156,5 @@ MACHINE_START(SFFSDR, "Lyrtech SFFSDR")
.init_irq = davinci_irq_init, .init_irq = davinci_irq_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = davinci_sffsdr_init, .init_machine = davinci_sffsdr_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -282,4 +282,5 @@ MACHINE_START(TNETV107X, "TNETV107X EVM") ...@@ -282,4 +282,5 @@ MACHINE_START(TNETV107X, "TNETV107X EVM")
.init_irq = cp_intc_init, .init_irq = cp_intc_init,
.timer = &davinci_timer, .timer = &davinci_timer,
.init_machine = tnetv107x_evm_board_init, .init_machine = tnetv107x_evm_board_init,
.dma_zone_size = SZ_128M,
MACHINE_END MACHINE_END
...@@ -41,11 +41,4 @@ ...@@ -41,11 +41,4 @@
*/ */
#define CONSISTENT_DMA_SIZE (14<<20) #define CONSISTENT_DMA_SIZE (14<<20)
/*
* Restrict DMA-able region to workaround silicon bug. The bug
* restricts buffers available for DMA to video hardware to be
* below 128M
*/
#define ARM_DMA_ZONE_SIZE SZ_128M
#endif /* __ASM_ARCH_MEMORY_H */ #endif /* __ASM_ARCH_MEMORY_H */
...@@ -33,4 +33,5 @@ MACHINE_START(H7201, "Hynix GMS30C7201") ...@@ -33,4 +33,5 @@ MACHINE_START(H7201, "Hynix GMS30C7201")
.map_io = h720x_map_io, .map_io = h720x_map_io,
.init_irq = h720x_init_irq, .init_irq = h720x_init_irq,
.timer = &h7201_timer, .timer = &h7201_timer,
.dma_zone_size = SZ_256M,
MACHINE_END MACHINE_END
...@@ -76,4 +76,5 @@ MACHINE_START(H7202, "Hynix HMS30C7202") ...@@ -76,4 +76,5 @@ MACHINE_START(H7202, "Hynix HMS30C7202")
.init_irq = h7202_init_irq, .init_irq = h7202_init_irq,
.timer = &h7202_timer, .timer = &h7202_timer,
.init_machine = init_eval_h7202, .init_machine = init_eval_h7202,
.dma_zone_size = SZ_256M,
MACHINE_END MACHINE_END
...@@ -8,11 +8,4 @@ ...@@ -8,11 +8,4 @@
#define __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H
#define PLAT_PHYS_OFFSET UL(0x40000000) #define PLAT_PHYS_OFFSET UL(0x40000000)
/*
* This is the maximum DMA address that can be DMAd to.
* There should not be more than (0xd0000000 - 0xc0000000)
* bytes of RAM.
*/
#define ARM_DMA_ZONE_SIZE SZ_256M
#endif #endif
...@@ -169,6 +169,9 @@ MACHINE_START(AVILA, "Gateworks Avila Network Platform") ...@@ -169,6 +169,9 @@ MACHINE_START(AVILA, "Gateworks Avila Network Platform")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = avila_init, .init_machine = avila_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
/* /*
...@@ -184,6 +187,9 @@ MACHINE_START(LOFT, "Giant Shoulder Inc Loft board") ...@@ -184,6 +187,9 @@ MACHINE_START(LOFT, "Giant Shoulder Inc Loft board")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = avila_init, .init_machine = avila_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -114,6 +114,9 @@ MACHINE_START(ADI_COYOTE, "ADI Engineering Coyote") ...@@ -114,6 +114,9 @@ MACHINE_START(ADI_COYOTE, "ADI Engineering Coyote")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = coyote_init, .init_machine = coyote_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
......
...@@ -284,4 +284,7 @@ MACHINE_START(DSMG600, "D-Link DSM-G600 RevA") ...@@ -284,4 +284,7 @@ MACHINE_START(DSMG600, "D-Link DSM-G600 RevA")
.init_irq = ixp4xx_init_irq, .init_irq = ixp4xx_init_irq,
.timer = &dsmg600_timer, .timer = &dsmg600_timer,
.init_machine = dsmg600_init, .init_machine = dsmg600_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -275,5 +275,8 @@ MACHINE_START(FSG, "Freecom FSG-3") ...@@ -275,5 +275,8 @@ MACHINE_START(FSG, "Freecom FSG-3")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = fsg_init, .init_machine = fsg_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -101,5 +101,8 @@ MACHINE_START(GATEWAY7001, "Gateway 7001 AP") ...@@ -101,5 +101,8 @@ MACHINE_START(GATEWAY7001, "Gateway 7001 AP")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = gateway7001_init, .init_machine = gateway7001_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -501,4 +501,7 @@ MACHINE_START(GORAMO_MLR, "MultiLink") ...@@ -501,4 +501,7 @@ MACHINE_START(GORAMO_MLR, "MultiLink")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = gmlr_init, .init_machine = gmlr_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -169,6 +169,9 @@ MACHINE_START(GTWX5715, "Gemtek GTWX5715 (Linksys WRV54G)") ...@@ -169,6 +169,9 @@ MACHINE_START(GTWX5715, "Gemtek GTWX5715 (Linksys WRV54G)")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = gtwx5715_init, .init_machine = gtwx5715_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -14,8 +14,4 @@ ...@@ -14,8 +14,4 @@
*/ */
#define PLAT_PHYS_OFFSET UL(0x00000000) #define PLAT_PHYS_OFFSET UL(0x00000000)
#ifdef CONFIG_PCI
#define ARM_DMA_ZONE_SIZE SZ_64M
#endif
#endif #endif
...@@ -258,6 +258,9 @@ MACHINE_START(IXDP425, "Intel IXDP425 Development Platform") ...@@ -258,6 +258,9 @@ MACHINE_START(IXDP425, "Intel IXDP425 Development Platform")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = ixdp425_init, .init_machine = ixdp425_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -269,6 +272,9 @@ MACHINE_START(IXDP465, "Intel IXDP465 Development Platform") ...@@ -269,6 +272,9 @@ MACHINE_START(IXDP465, "Intel IXDP465 Development Platform")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = ixdp425_init, .init_machine = ixdp425_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -280,6 +286,9 @@ MACHINE_START(IXCDP1100, "Intel IXCDP1100 Development Platform") ...@@ -280,6 +286,9 @@ MACHINE_START(IXCDP1100, "Intel IXCDP1100 Development Platform")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = ixdp425_init, .init_machine = ixdp425_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -291,5 +300,8 @@ MACHINE_START(KIXRP435, "Intel KIXRP435 Reference Platform") ...@@ -291,5 +300,8 @@ MACHINE_START(KIXRP435, "Intel KIXRP435 Reference Platform")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = ixdp425_init, .init_machine = ixdp425_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -319,4 +319,7 @@ MACHINE_START(NAS100D, "Iomega NAS 100d") ...@@ -319,4 +319,7 @@ MACHINE_START(NAS100D, "Iomega NAS 100d")
.init_irq = ixp4xx_init_irq, .init_irq = ixp4xx_init_irq,
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.init_machine = nas100d_init, .init_machine = nas100d_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -305,4 +305,7 @@ MACHINE_START(NSLU2, "Linksys NSLU2") ...@@ -305,4 +305,7 @@ MACHINE_START(NSLU2, "Linksys NSLU2")
.init_irq = ixp4xx_init_irq, .init_irq = ixp4xx_init_irq,
.timer = &nslu2_timer, .timer = &nslu2_timer,
.init_machine = nslu2_init, .init_machine = nslu2_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -241,4 +241,7 @@ MACHINE_START(ARCOM_VULCAN, "Arcom/Eurotech Vulcan") ...@@ -241,4 +241,7 @@ MACHINE_START(ARCOM_VULCAN, "Arcom/Eurotech Vulcan")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = vulcan_init, .init_machine = vulcan_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -102,5 +102,8 @@ MACHINE_START(WG302V2, "Netgear WG302 v2 / WAG302 v2") ...@@ -102,5 +102,8 @@ MACHINE_START(WG302V2, "Netgear WG302 v2 / WAG302 v2")
.timer = &ixp4xx_timer, .timer = &ixp4xx_timer,
.boot_params = 0x0100, .boot_params = 0x0100,
.init_machine = wg302v2_init, .init_machine = wg302v2_init,
#if defined(CONFIG_PCI)
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
#endif #endif
...@@ -518,4 +518,7 @@ MACHINE_START(ARMCORE, "Compulab CM-X2XX") ...@@ -518,4 +518,7 @@ MACHINE_START(ARMCORE, "Compulab CM-X2XX")
.init_irq = cmx2xx_init_irq, .init_irq = cmx2xx_init_irq,
.timer = &pxa_timer, .timer = &pxa_timer,
.init_machine = cmx2xx_init, .init_machine = cmx2xx_init,
#ifdef CONFIG_PCI
.dma_zone_size = SZ_64M,
#endif
MACHINE_END MACHINE_END
...@@ -17,8 +17,4 @@ ...@@ -17,8 +17,4 @@
*/ */
#define PLAT_PHYS_OFFSET UL(0xa0000000) #define PLAT_PHYS_OFFSET UL(0xa0000000)
#if defined(CONFIG_MACH_ARMCORE) && defined(CONFIG_PCI)
#define ARM_DMA_ZONE_SIZE SZ_64M
#endif
#endif #endif
...@@ -29,10 +29,6 @@ ...@@ -29,10 +29,6 @@
#define PLAT_PHYS_OFFSET UL(0x00000000) #define PLAT_PHYS_OFFSET UL(0x00000000)
#endif #endif
#ifdef CONFIG_ZONE_DMA
#define ARM_DMA_ZONE_SIZE SZ_256M
#endif
#ifdef CONFIG_SPARSEMEM #ifdef CONFIG_SPARSEMEM
/* /*
......
...@@ -470,4 +470,7 @@ MACHINE_START(REALVIEW_EB, "ARM-RealView EB") ...@@ -470,4 +470,7 @@ MACHINE_START(REALVIEW_EB, "ARM-RealView EB")
.init_irq = gic_init_irq, .init_irq = gic_init_irq,
.timer = &realview_eb_timer, .timer = &realview_eb_timer,
.init_machine = realview_eb_init, .init_machine = realview_eb_init,
#ifdef CONFIG_ZONE_DMA
.dma_zone_size = SZ_256M,
#endif
MACHINE_END MACHINE_END
...@@ -365,4 +365,7 @@ MACHINE_START(REALVIEW_PB1176, "ARM-RealView PB1176") ...@@ -365,4 +365,7 @@ MACHINE_START(REALVIEW_PB1176, "ARM-RealView PB1176")
.init_irq = gic_init_irq, .init_irq = gic_init_irq,
.timer = &realview_pb1176_timer, .timer = &realview_pb1176_timer,
.init_machine = realview_pb1176_init, .init_machine = realview_pb1176_init,
#ifdef CONFIG_ZONE_DMA
.dma_zone_size = SZ_256M,
#endif
MACHINE_END MACHINE_END
...@@ -367,4 +367,7 @@ MACHINE_START(REALVIEW_PB11MP, "ARM-RealView PB11MPCore") ...@@ -367,4 +367,7 @@ MACHINE_START(REALVIEW_PB11MP, "ARM-RealView PB11MPCore")
.init_irq = gic_init_irq, .init_irq = gic_init_irq,
.timer = &realview_pb11mp_timer, .timer = &realview_pb11mp_timer,
.init_machine = realview_pb11mp_init, .init_machine = realview_pb11mp_init,
#ifdef CONFIG_ZONE_DMA
.dma_zone_size = SZ_256M,
#endif
MACHINE_END MACHINE_END
...@@ -317,4 +317,7 @@ MACHINE_START(REALVIEW_PBA8, "ARM-RealView PB-A8") ...@@ -317,4 +317,7 @@ MACHINE_START(REALVIEW_PBA8, "ARM-RealView PB-A8")
.init_irq = gic_init_irq, .init_irq = gic_init_irq,
.timer = &realview_pba8_timer, .timer = &realview_pba8_timer,
.init_machine = realview_pba8_init, .init_machine = realview_pba8_init,
#ifdef CONFIG_ZONE_DMA
.dma_zone_size = SZ_256M,
#endif
MACHINE_END MACHINE_END
...@@ -400,4 +400,7 @@ MACHINE_START(REALVIEW_PBX, "ARM-RealView PBX") ...@@ -400,4 +400,7 @@ MACHINE_START(REALVIEW_PBX, "ARM-RealView PBX")
.init_irq = gic_init_irq, .init_irq = gic_init_irq,
.timer = &realview_pbx_timer, .timer = &realview_pbx_timer,
.init_machine = realview_pbx_init, .init_machine = realview_pbx_init,
#ifdef CONFIG_ZONE_DMA
.dma_zone_size = SZ_256M,
#endif
MACHINE_END MACHINE_END
...@@ -453,4 +453,7 @@ MACHINE_START(ASSABET, "Intel-Assabet") ...@@ -453,4 +453,7 @@ MACHINE_START(ASSABET, "Intel-Assabet")
.init_irq = sa1100_init_irq, .init_irq = sa1100_init_irq,
.timer = &sa1100_timer, .timer = &sa1100_timer,
.init_machine = assabet_init, .init_machine = assabet_init,
#ifdef CONFIG_SA1111
.dma_zone_size = SZ_1M,
#endif
MACHINE_END MACHINE_END
...@@ -306,4 +306,7 @@ MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4") ...@@ -306,4 +306,7 @@ MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4")
.map_io = badge4_map_io, .map_io = badge4_map_io,
.init_irq = sa1100_init_irq, .init_irq = sa1100_init_irq,
.timer = &sa1100_timer, .timer = &sa1100_timer,
#ifdef CONFIG_SA1111
.dma_zone_size = SZ_1M,
#endif
MACHINE_END MACHINE_END
...@@ -14,10 +14,6 @@ ...@@ -14,10 +14,6 @@
*/ */
#define PLAT_PHYS_OFFSET UL(0xc0000000) #define PLAT_PHYS_OFFSET UL(0xc0000000)
#ifdef CONFIG_SA1111
#define ARM_DMA_ZONE_SIZE SZ_1M
#endif
/* /*
* Because of the wide memory address space between physical RAM banks on the * Because of the wide memory address space between physical RAM banks on the
* SA1100, it's much convenient to use Linux's SparseMEM support to implement * SA1100, it's much convenient to use Linux's SparseMEM support to implement
......
...@@ -369,4 +369,7 @@ MACHINE_START(JORNADA720, "HP Jornada 720") ...@@ -369,4 +369,7 @@ MACHINE_START(JORNADA720, "HP Jornada 720")
.init_irq = sa1100_init_irq, .init_irq = sa1100_init_irq,
.timer = &sa1100_timer, .timer = &sa1100_timer,
.init_machine = jornada720_mach_init, .init_machine = jornada720_mach_init,
#ifdef CONFIG_SA1111
.dma_zone_size = SZ_1M,
#endif
MACHINE_END MACHINE_END
...@@ -156,4 +156,5 @@ MACHINE_START(SHARK, "Shark") ...@@ -156,4 +156,5 @@ MACHINE_START(SHARK, "Shark")
.map_io = shark_map_io, .map_io = shark_map_io,
.init_irq = shark_init_irq, .init_irq = shark_init_irq,
.timer = &shark_timer, .timer = &shark_timer,
.dma_zone_size = SZ_4M,
MACHINE_END MACHINE_END
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
*/ */
#define PLAT_PHYS_OFFSET UL(0x08000000) #define PLAT_PHYS_OFFSET UL(0x08000000)
#define ARM_DMA_ZONE_SIZE SZ_4M
/* /*
* Cache flushing area * Cache flushing area
*/ */
......
...@@ -242,16 +242,5 @@ ENDPROC(fa_dma_unmap_area) ...@@ -242,16 +242,5 @@ ENDPROC(fa_dma_unmap_area)
__INITDATA __INITDATA
.type fa_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(fa_cache_fns) define_cache_functions fa
.long fa_flush_icache_all
.long fa_flush_kern_cache_all
.long fa_flush_user_cache_all
.long fa_flush_user_cache_range
.long fa_coherent_kern_range
.long fa_coherent_user_range
.long fa_flush_kern_dcache_area
.long fa_dma_map_area
.long fa_dma_unmap_area
.long fa_dma_flush_range
.size fa_cache_fns, . - fa_cache_fns
...@@ -129,16 +129,5 @@ ENDPROC(v3_dma_map_area) ...@@ -129,16 +129,5 @@ ENDPROC(v3_dma_map_area)
__INITDATA __INITDATA
.type v3_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(v3_cache_fns) define_cache_functions v3
.long v3_flush_icache_all
.long v3_flush_kern_cache_all
.long v3_flush_user_cache_all
.long v3_flush_user_cache_range
.long v3_coherent_kern_range
.long v3_coherent_user_range
.long v3_flush_kern_dcache_area
.long v3_dma_map_area
.long v3_dma_unmap_area
.long v3_dma_flush_range
.size v3_cache_fns, . - v3_cache_fns
...@@ -141,16 +141,5 @@ ENDPROC(v4_dma_map_area) ...@@ -141,16 +141,5 @@ ENDPROC(v4_dma_map_area)
__INITDATA __INITDATA
.type v4_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(v4_cache_fns) define_cache_functions v4
.long v4_flush_icache_all
.long v4_flush_kern_cache_all
.long v4_flush_user_cache_all
.long v4_flush_user_cache_range
.long v4_coherent_kern_range
.long v4_coherent_user_range
.long v4_flush_kern_dcache_area
.long v4_dma_map_area
.long v4_dma_unmap_area
.long v4_dma_flush_range
.size v4_cache_fns, . - v4_cache_fns
...@@ -253,16 +253,5 @@ ENDPROC(v4wb_dma_unmap_area) ...@@ -253,16 +253,5 @@ ENDPROC(v4wb_dma_unmap_area)
__INITDATA __INITDATA
.type v4wb_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(v4wb_cache_fns) define_cache_functions v4wb
.long v4wb_flush_icache_all
.long v4wb_flush_kern_cache_all
.long v4wb_flush_user_cache_all
.long v4wb_flush_user_cache_range
.long v4wb_coherent_kern_range
.long v4wb_coherent_user_range
.long v4wb_flush_kern_dcache_area
.long v4wb_dma_map_area
.long v4wb_dma_unmap_area
.long v4wb_dma_flush_range
.size v4wb_cache_fns, . - v4wb_cache_fns
...@@ -197,16 +197,5 @@ ENDPROC(v4wt_dma_map_area) ...@@ -197,16 +197,5 @@ ENDPROC(v4wt_dma_map_area)
__INITDATA __INITDATA
.type v4wt_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(v4wt_cache_fns) define_cache_functions v4wt
.long v4wt_flush_icache_all
.long v4wt_flush_kern_cache_all
.long v4wt_flush_user_cache_all
.long v4wt_flush_user_cache_range
.long v4wt_coherent_kern_range
.long v4wt_coherent_user_range
.long v4wt_flush_kern_dcache_area
.long v4wt_dma_map_area
.long v4wt_dma_unmap_area
.long v4wt_dma_flush_range
.size v4wt_cache_fns, . - v4wt_cache_fns
...@@ -330,16 +330,5 @@ ENDPROC(v6_dma_unmap_area) ...@@ -330,16 +330,5 @@ ENDPROC(v6_dma_unmap_area)
__INITDATA __INITDATA
.type v6_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(v6_cache_fns) define_cache_functions v6
.long v6_flush_icache_all
.long v6_flush_kern_cache_all
.long v6_flush_user_cache_all
.long v6_flush_user_cache_range
.long v6_coherent_kern_range
.long v6_coherent_user_range
.long v6_flush_kern_dcache_area
.long v6_dma_map_area
.long v6_dma_unmap_area
.long v6_dma_flush_range
.size v6_cache_fns, . - v6_cache_fns
...@@ -325,16 +325,5 @@ ENDPROC(v7_dma_unmap_area) ...@@ -325,16 +325,5 @@ ENDPROC(v7_dma_unmap_area)
__INITDATA __INITDATA
.type v7_cache_fns, #object @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
ENTRY(v7_cache_fns) define_cache_functions v7
.long v7_flush_icache_all
.long v7_flush_kern_cache_all
.long v7_flush_user_cache_all
.long v7_flush_user_cache_range
.long v7_coherent_kern_range
.long v7_coherent_user_range
.long v7_flush_kern_dcache_area
.long v7_dma_map_area
.long v7_dma_unmap_area
.long v7_dma_flush_range
.size v7_cache_fns, . - v7_cache_fns
...@@ -212,6 +212,10 @@ static void __init arm_bootmem_init(unsigned long start_pfn, ...@@ -212,6 +212,10 @@ static void __init arm_bootmem_init(unsigned long start_pfn,
} }
#ifdef CONFIG_ZONE_DMA #ifdef CONFIG_ZONE_DMA
unsigned long arm_dma_zone_size __read_mostly;
EXPORT_SYMBOL(arm_dma_zone_size);
/* /*
* The DMA mask corresponding to the maximum bus address allocatable * The DMA mask corresponding to the maximum bus address allocatable
* using GFP_DMA. The default here places no restriction on DMA * using GFP_DMA. The default here places no restriction on DMA
...@@ -275,19 +279,17 @@ static void __init arm_bootmem_free(unsigned long min, unsigned long max_low, ...@@ -275,19 +279,17 @@ static void __init arm_bootmem_free(unsigned long min, unsigned long max_low,
#endif #endif
} }
#ifdef ARM_DMA_ZONE_SIZE #ifdef CONFIG_ZONE_DMA
#ifndef CONFIG_ZONE_DMA
#error ARM_DMA_ZONE_SIZE set but no DMA zone to limit allocations
#endif
/* /*
* Adjust the sizes according to any special requirements for * Adjust the sizes according to any special requirements for
* this machine type. * this machine type.
*/ */
if (arm_dma_zone_size) {
arm_adjust_dma_zone(zone_size, zhole_size, arm_adjust_dma_zone(zone_size, zhole_size,
ARM_DMA_ZONE_SIZE >> PAGE_SHIFT); arm_dma_zone_size >> PAGE_SHIFT);
arm_dma_limit = PHYS_OFFSET + arm_dma_zone_size - 1;
arm_dma_limit = PHYS_OFFSET + ARM_DMA_ZONE_SIZE - 1; } else
arm_dma_limit = 0xffffffff;
#endif #endif
free_area_init_node(0, zone_size, min, zhole_size); free_area_init_node(0, zone_size, min, zhole_size);
......
...@@ -364,17 +364,8 @@ ENTRY(arm1020_dma_unmap_area) ...@@ -364,17 +364,8 @@ ENTRY(arm1020_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm1020_dma_unmap_area) ENDPROC(arm1020_dma_unmap_area)
ENTRY(arm1020_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm1020_flush_icache_all define_cache_functions arm1020
.long arm1020_flush_kern_cache_all
.long arm1020_flush_user_cache_all
.long arm1020_flush_user_cache_range
.long arm1020_coherent_kern_range
.long arm1020_coherent_user_range
.long arm1020_flush_kern_dcache_area
.long arm1020_dma_map_area
.long arm1020_dma_unmap_area
.long arm1020_dma_flush_range
.align 5 .align 5
ENTRY(cpu_arm1020_dcache_clean_area) ENTRY(cpu_arm1020_dcache_clean_area)
...@@ -477,38 +468,14 @@ arm1020_crval: ...@@ -477,38 +468,14 @@ arm1020_crval:
crval clear=0x0000593f, mmuset=0x00003935, ucset=0x00001930 crval clear=0x0000593f, mmuset=0x00003935, ucset=0x00001930
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
define_processor_functions arm1020, dabort=v4t_early_abort, pabort=legacy_pabort
/*
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm1020_processor_functions, #object
arm1020_processor_functions:
.word v4t_early_abort
.word legacy_pabort
.word cpu_arm1020_proc_init
.word cpu_arm1020_proc_fin
.word cpu_arm1020_reset
.word cpu_arm1020_do_idle
.word cpu_arm1020_dcache_clean_area
.word cpu_arm1020_switch_mm
.word cpu_arm1020_set_pte_ext
.word 0
.word 0
.word 0
.size arm1020_processor_functions, . - arm1020_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5t"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5t"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm1020_name, #object .type cpu_arm1020_name, #object
cpu_arm1020_name: cpu_arm1020_name:
......
...@@ -350,17 +350,8 @@ ENTRY(arm1020e_dma_unmap_area) ...@@ -350,17 +350,8 @@ ENTRY(arm1020e_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm1020e_dma_unmap_area) ENDPROC(arm1020e_dma_unmap_area)
ENTRY(arm1020e_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm1020e_flush_icache_all define_cache_functions arm1020e
.long arm1020e_flush_kern_cache_all
.long arm1020e_flush_user_cache_all
.long arm1020e_flush_user_cache_range
.long arm1020e_coherent_kern_range
.long arm1020e_coherent_user_range
.long arm1020e_flush_kern_dcache_area
.long arm1020e_dma_map_area
.long arm1020e_dma_unmap_area
.long arm1020e_dma_flush_range
.align 5 .align 5
ENTRY(cpu_arm1020e_dcache_clean_area) ENTRY(cpu_arm1020e_dcache_clean_area)
...@@ -458,43 +449,14 @@ arm1020e_crval: ...@@ -458,43 +449,14 @@ arm1020e_crval:
crval clear=0x00007f3f, mmuset=0x00003935, ucset=0x00001930 crval clear=0x00007f3f, mmuset=0x00003935, ucset=0x00001930
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm1020e, dabort=v4t_early_abort, pabort=legacy_pabort
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm1020e_processor_functions, #object
arm1020e_processor_functions:
.word v4t_early_abort
.word legacy_pabort
.word cpu_arm1020e_proc_init
.word cpu_arm1020e_proc_fin
.word cpu_arm1020e_reset
.word cpu_arm1020e_do_idle
.word cpu_arm1020e_dcache_clean_area
.word cpu_arm1020e_switch_mm
.word cpu_arm1020e_set_pte_ext
.word 0
.word 0
.word 0
.size arm1020e_processor_functions, . - arm1020e_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5te" string cpu_arm1020e_name, "ARM1020E"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm1020e_name, #object
cpu_arm1020e_name:
.asciz "ARM1020E"
.size cpu_arm1020e_name, . - cpu_arm1020e_name
.align .align
......
...@@ -339,17 +339,8 @@ ENTRY(arm1022_dma_unmap_area) ...@@ -339,17 +339,8 @@ ENTRY(arm1022_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm1022_dma_unmap_area) ENDPROC(arm1022_dma_unmap_area)
ENTRY(arm1022_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm1022_flush_icache_all define_cache_functions arm1022
.long arm1022_flush_kern_cache_all
.long arm1022_flush_user_cache_all
.long arm1022_flush_user_cache_range
.long arm1022_coherent_kern_range
.long arm1022_coherent_user_range
.long arm1022_flush_kern_dcache_area
.long arm1022_dma_map_area
.long arm1022_dma_unmap_area
.long arm1022_dma_flush_range
.align 5 .align 5
ENTRY(cpu_arm1022_dcache_clean_area) ENTRY(cpu_arm1022_dcache_clean_area)
...@@ -441,43 +432,14 @@ arm1022_crval: ...@@ -441,43 +432,14 @@ arm1022_crval:
crval clear=0x00007f3f, mmuset=0x00003935, ucset=0x00001930 crval clear=0x00007f3f, mmuset=0x00003935, ucset=0x00001930
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm1022, dabort=v4t_early_abort, pabort=legacy_pabort
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm1022_processor_functions, #object
arm1022_processor_functions:
.word v4t_early_abort
.word legacy_pabort
.word cpu_arm1022_proc_init
.word cpu_arm1022_proc_fin
.word cpu_arm1022_reset
.word cpu_arm1022_do_idle
.word cpu_arm1022_dcache_clean_area
.word cpu_arm1022_switch_mm
.word cpu_arm1022_set_pte_ext
.word 0
.word 0
.word 0
.size arm1022_processor_functions, . - arm1022_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5te" string cpu_arm1022_name, "ARM1022"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm1022_name, #object
cpu_arm1022_name:
.asciz "ARM1022"
.size cpu_arm1022_name, . - cpu_arm1022_name
.align .align
......
...@@ -333,17 +333,8 @@ ENTRY(arm1026_dma_unmap_area) ...@@ -333,17 +333,8 @@ ENTRY(arm1026_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm1026_dma_unmap_area) ENDPROC(arm1026_dma_unmap_area)
ENTRY(arm1026_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm1026_flush_icache_all define_cache_functions arm1026
.long arm1026_flush_kern_cache_all
.long arm1026_flush_user_cache_all
.long arm1026_flush_user_cache_range
.long arm1026_coherent_kern_range
.long arm1026_coherent_user_range
.long arm1026_flush_kern_dcache_area
.long arm1026_dma_map_area
.long arm1026_dma_unmap_area
.long arm1026_dma_flush_range
.align 5 .align 5
ENTRY(cpu_arm1026_dcache_clean_area) ENTRY(cpu_arm1026_dcache_clean_area)
...@@ -436,45 +427,15 @@ arm1026_crval: ...@@ -436,45 +427,15 @@ arm1026_crval:
crval clear=0x00007f3f, mmuset=0x00003935, ucset=0x00001934 crval clear=0x00007f3f, mmuset=0x00003935, ucset=0x00001934
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm1026, dabort=v5t_early_abort, pabort=legacy_pabort
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm1026_processor_functions, #object
arm1026_processor_functions:
.word v5t_early_abort
.word legacy_pabort
.word cpu_arm1026_proc_init
.word cpu_arm1026_proc_fin
.word cpu_arm1026_reset
.word cpu_arm1026_do_idle
.word cpu_arm1026_dcache_clean_area
.word cpu_arm1026_switch_mm
.word cpu_arm1026_set_pte_ext
.word 0
.word 0
.word 0
.size arm1026_processor_functions, . - arm1026_processor_functions
.section .rodata .section .rodata
.type cpu_arch_name, #object string cpu_arch_name, "armv5tej"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5tej"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.align .align
string cpu_arm1026_name, "ARM1026EJ-S"
.type cpu_arm1026_name, #object
cpu_arm1026_name:
.asciz "ARM1026EJ-S"
.size cpu_arm1026_name, . - cpu_arm1026_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
......
...@@ -267,159 +267,57 @@ __arm7_setup: mov r0, #0 ...@@ -267,159 +267,57 @@ __arm7_setup: mov r0, #0
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm6, dabort=cpu_arm6_data_abort, pabort=legacy_pabort
* come through these define_processor_functions arm7, dabort=cpu_arm7_data_abort, pabort=legacy_pabort
*/
.type arm6_processor_functions, #object
ENTRY(arm6_processor_functions)
.word cpu_arm6_data_abort
.word legacy_pabort
.word cpu_arm6_proc_init
.word cpu_arm6_proc_fin
.word cpu_arm6_reset
.word cpu_arm6_do_idle
.word cpu_arm6_dcache_clean_area
.word cpu_arm6_switch_mm
.word cpu_arm6_set_pte_ext
.word 0
.word 0
.word 0
.size arm6_processor_functions, . - arm6_processor_functions
/*
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm7_processor_functions, #object
ENTRY(arm7_processor_functions)
.word cpu_arm7_data_abort
.word legacy_pabort
.word cpu_arm7_proc_init
.word cpu_arm7_proc_fin
.word cpu_arm7_reset
.word cpu_arm7_do_idle
.word cpu_arm7_dcache_clean_area
.word cpu_arm7_switch_mm
.word cpu_arm7_set_pte_ext
.word 0
.word 0
.word 0
.size arm7_processor_functions, . - arm7_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv3"
cpu_arch_name: .asciz "armv3" string cpu_elf_name, "v3"
.size cpu_arch_name, . - cpu_arch_name string cpu_arm6_name, "ARM6"
string cpu_arm610_name, "ARM610"
.type cpu_elf_name, #object string cpu_arm7_name, "ARM7"
cpu_elf_name: .asciz "v3" string cpu_arm710_name, "ARM710"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm6_name, #object
cpu_arm6_name: .asciz "ARM6"
.size cpu_arm6_name, . - cpu_arm6_name
.type cpu_arm610_name, #object
cpu_arm610_name:
.asciz "ARM610"
.size cpu_arm610_name, . - cpu_arm610_name
.type cpu_arm7_name, #object
cpu_arm7_name: .asciz "ARM7"
.size cpu_arm7_name, . - cpu_arm7_name
.type cpu_arm710_name, #object
cpu_arm710_name:
.asciz "ARM710"
.size cpu_arm710_name, . - cpu_arm710_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __arm6_proc_info, #object .macro arm67_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req, \
__arm6_proc_info: cpu_mm_mmu_flags:req, cpu_flush:req, cpu_proc_funcs:req
.long 0x41560600 .type __\name\()_proc_info, #object
.long 0xfffffff0 __\name\()_proc_info:
.long 0x00000c1e .long \cpu_val
.long \cpu_mask
.long \cpu_mm_mmu_flags
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_BIT4 | \ PMD_BIT4 | \
PMD_SECT_AP_WRITE | \ PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ PMD_SECT_AP_READ
b __arm6_setup b \cpu_flush
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP | HWCAP_26BIT .long HWCAP_SWP | HWCAP_26BIT
.long cpu_arm6_name .long \cpu_name
.long arm6_processor_functions .long \cpu_proc_funcs
.long v3_tlb_fns .long v3_tlb_fns
.long v3_user_fns .long v3_user_fns
.long v3_cache_fns .long v3_cache_fns
.size __arm6_proc_info, . - __arm6_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
.type __arm610_proc_info, #object
__arm610_proc_info: arm67_proc_info arm6, 0x41560600, 0xfffffff0, cpu_arm6_name, \
.long 0x41560610 0x00000c1e, __arm6_setup, arm6_processor_functions
.long 0xfffffff0 arm67_proc_info arm610, 0x41560610, 0xfffffff0, cpu_arm610_name, \
.long 0x00000c1e 0x00000c1e, __arm6_setup, arm6_processor_functions
.long PMD_TYPE_SECT | \ arm67_proc_info arm7, 0x41007000, 0xffffff00, cpu_arm7_name, \
PMD_BIT4 | \ 0x00000c1e, __arm7_setup, arm7_processor_functions
PMD_SECT_AP_WRITE | \ arm67_proc_info arm710, 0x41007100, 0xfff8ff00, cpu_arm710_name, \
PMD_SECT_AP_READ PMD_TYPE_SECT | \
b __arm6_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_26BIT
.long cpu_arm610_name
.long arm6_processor_functions
.long v3_tlb_fns
.long v3_user_fns
.long v3_cache_fns
.size __arm610_proc_info, . - __arm610_proc_info
.type __arm7_proc_info, #object
__arm7_proc_info:
.long 0x41007000
.long 0xffffff00
.long 0x00000c1e
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm7_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_26BIT
.long cpu_arm7_name
.long arm7_processor_functions
.long v3_tlb_fns
.long v3_user_fns
.long v3_cache_fns
.size __arm7_proc_info, . - __arm7_proc_info
.type __arm710_proc_info, #object
__arm710_proc_info:
.long 0x41007100
.long 0xfff8ff00
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \ PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \ PMD_SECT_CACHEABLE | \
PMD_BIT4 | \ PMD_BIT4 | \
PMD_SECT_AP_WRITE | \ PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ PMD_SECT_AP_READ, \
.long PMD_TYPE_SECT | \ __arm7_setup, arm7_processor_functions
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm7_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_26BIT
.long cpu_arm710_name
.long arm7_processor_functions
.long v3_tlb_fns
.long v3_user_fns
.long v3_cache_fns
.size __arm710_proc_info, . - __arm710_proc_info
...@@ -169,46 +169,15 @@ arm720_crval: ...@@ -169,46 +169,15 @@ arm720_crval:
crval clear=0x00002f3f, mmuset=0x0000213d, ucset=0x00000130 crval clear=0x00002f3f, mmuset=0x0000213d, ucset=0x00000130
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm720, dabort=v4t_late_abort, pabort=legacy_pabort
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm720_processor_functions, #object
ENTRY(arm720_processor_functions)
.word v4t_late_abort
.word legacy_pabort
.word cpu_arm720_proc_init
.word cpu_arm720_proc_fin
.word cpu_arm720_reset
.word cpu_arm720_do_idle
.word cpu_arm720_dcache_clean_area
.word cpu_arm720_switch_mm
.word cpu_arm720_set_pte_ext
.word 0
.word 0
.word 0
.size arm720_processor_functions, . - arm720_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: .asciz "armv4t" string cpu_elf_name, "v4"
.size cpu_arch_name, . - cpu_arch_name string cpu_arm710_name, "ARM710T"
string cpu_arm720_name, "ARM720T"
.type cpu_elf_name, #object
cpu_elf_name: .asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm710_name, #object
cpu_arm710_name:
.asciz "ARM710T"
.size cpu_arm710_name, . - cpu_arm710_name
.type cpu_arm720_name, #object
cpu_arm720_name:
.asciz "ARM720T"
.size cpu_arm720_name, . - cpu_arm720_name
.align .align
...@@ -218,10 +187,11 @@ cpu_arm720_name: ...@@ -218,10 +187,11 @@ cpu_arm720_name:
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __arm710_proc_info, #object .macro arm720_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req, cpu_flush:req
__arm710_proc_info: .type __\name\()_proc_info,#object
.long 0x41807100 @ cpu_val __\name\()_proc_info:
.long 0xffffff00 @ cpu_mask .long \cpu_val
.long \cpu_mask
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \ PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \ PMD_SECT_CACHEABLE | \
...@@ -232,38 +202,17 @@ __arm710_proc_info: ...@@ -232,38 +202,17 @@ __arm710_proc_info:
PMD_BIT4 | \ PMD_BIT4 | \
PMD_SECT_AP_WRITE | \ PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ PMD_SECT_AP_READ
b __arm710_setup @ cpu_flush b \cpu_flush @ cpu_flush
.long cpu_arch_name @ arch_name .long cpu_arch_name @ arch_name
.long cpu_elf_name @ elf_name .long cpu_elf_name @ elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB @ elf_hwcap .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB @ elf_hwcap
.long cpu_arm710_name @ name .long \cpu_name
.long arm720_processor_functions .long arm720_processor_functions
.long v4_tlb_fns .long v4_tlb_fns
.long v4wt_user_fns .long v4wt_user_fns
.long v4_cache_fns .long v4_cache_fns
.size __arm710_proc_info, . - __arm710_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
.type __arm720_proc_info, #object arm720_proc_info arm710, 0x41807100, 0xffffff00, cpu_arm710_name, __arm710_setup
__arm720_proc_info: arm720_proc_info arm720, 0x41807200, 0xffffff00, cpu_arm720_name, __arm720_setup
.long 0x41807200 @ cpu_val
.long 0xffffff00 @ cpu_mask
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm720_setup @ cpu_flush
.long cpu_arch_name @ arch_name
.long cpu_elf_name @ elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB @ elf_hwcap
.long cpu_arm720_name @ name
.long arm720_processor_functions
.long v4_tlb_fns
.long v4wt_user_fns
.long v4_cache_fns
.size __arm720_proc_info, . - __arm720_proc_info
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include "proc-macros.S"
.text .text
/* /*
* cpu_arm740_proc_init() * cpu_arm740_proc_init()
...@@ -115,42 +117,14 @@ __arm740_setup: ...@@ -115,42 +117,14 @@ __arm740_setup:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm740, dabort=v4t_late_abort, pabort=legacy_pabort, nommu=1
* come through these
*/
.type arm740_processor_functions, #object
ENTRY(arm740_processor_functions)
.word v4t_late_abort
.word legacy_pabort
.word cpu_arm740_proc_init
.word cpu_arm740_proc_fin
.word cpu_arm740_reset
.word cpu_arm740_do_idle
.word cpu_arm740_dcache_clean_area
.word cpu_arm740_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm740_processor_functions, . - arm740_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4" string cpu_arm740_name, "ARM740T"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm740_name, #object
cpu_arm740_name:
.ascii "ARM740T"
.size cpu_arm740_name, . - cpu_arm740_name
.align .align
...@@ -170,5 +144,3 @@ __arm740_proc_info: ...@@ -170,5 +144,3 @@ __arm740_proc_info:
.long 0 .long 0
.long v3_cache_fns @ cache model .long v3_cache_fns @ cache model
.size __arm740_proc_info, . - __arm740_proc_info .size __arm740_proc_info, . - __arm740_proc_info
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include "proc-macros.S"
.text .text
/* /*
* cpu_arm7tdmi_proc_init() * cpu_arm7tdmi_proc_init()
...@@ -55,197 +57,57 @@ __arm7tdmi_setup: ...@@ -55,197 +57,57 @@ __arm7tdmi_setup:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm7tdmi, dabort=v4t_late_abort, pabort=legacy_pabort, nommu=1
* come through these
*/
.type arm7tdmi_processor_functions, #object
ENTRY(arm7tdmi_processor_functions)
.word v4t_late_abort
.word legacy_pabort
.word cpu_arm7tdmi_proc_init
.word cpu_arm7tdmi_proc_fin
.word cpu_arm7tdmi_reset
.word cpu_arm7tdmi_do_idle
.word cpu_arm7tdmi_dcache_clean_area
.word cpu_arm7tdmi_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm7tdmi_processor_functions, . - arm7tdmi_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4t" string cpu_arm7tdmi_name, "ARM7TDMI"
.size cpu_arch_name, . - cpu_arch_name string cpu_triscenda7_name, "Triscend-A7x"
string cpu_at91_name, "Atmel-AT91M40xxx"
.type cpu_elf_name, #object string cpu_s3c3410_name, "Samsung-S3C3410"
cpu_elf_name: string cpu_s3c44b0x_name, "Samsung-S3C44B0x"
.asciz "v4" string cpu_s3c4510b_name, "Samsung-S3C4510B"
.size cpu_elf_name, . - cpu_elf_name string cpu_s3c4530_name, "Samsung-S3C4530"
string cpu_netarm_name, "NETARM"
.type cpu_arm7tdmi_name, #object
cpu_arm7tdmi_name:
.asciz "ARM7TDMI"
.size cpu_arm7tdmi_name, . - cpu_arm7tdmi_name
.type cpu_triscenda7_name, #object
cpu_triscenda7_name:
.asciz "Triscend-A7x"
.size cpu_triscenda7_name, . - cpu_triscenda7_name
.type cpu_at91_name, #object
cpu_at91_name:
.asciz "Atmel-AT91M40xxx"
.size cpu_at91_name, . - cpu_at91_name
.type cpu_s3c3410_name, #object
cpu_s3c3410_name:
.asciz "Samsung-S3C3410"
.size cpu_s3c3410_name, . - cpu_s3c3410_name
.type cpu_s3c44b0x_name, #object
cpu_s3c44b0x_name:
.asciz "Samsung-S3C44B0x"
.size cpu_s3c44b0x_name, . - cpu_s3c44b0x_name
.type cpu_s3c4510b, #object
cpu_s3c4510b_name:
.asciz "Samsung-S3C4510B"
.size cpu_s3c4510b_name, . - cpu_s3c4510b_name
.type cpu_s3c4530_name, #object
cpu_s3c4530_name:
.asciz "Samsung-S3C4530"
.size cpu_s3c4530_name, . - cpu_s3c4530_name
.type cpu_netarm_name, #object
cpu_netarm_name:
.asciz "NETARM"
.size cpu_netarm_name, . - cpu_netarm_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __arm7tdmi_proc_info, #object .macro arm7tdmi_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req, \
__arm7tdmi_proc_info: extra_hwcaps=0
.long 0x41007700 .type __\name\()_proc_info, #object
.long 0xfff8ff00 __\name\()_proc_info:
.long 0 .long \cpu_val
.long 0 .long \cpu_mask
b __arm7tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_26BIT
.long cpu_arm7tdmi_name
.long arm7tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __arm7tdmi_proc_info, . - __arm7tdmi_proc_info
.type __triscenda7_proc_info, #object
__triscenda7_proc_info:
.long 0x0001d2ff
.long 0x0001ffff
.long 0
.long 0
b __arm7tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_triscenda7_name
.long arm7tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __triscenda7_proc_info, . - __triscenda7_proc_info
.type __at91_proc_info, #object
__at91_proc_info:
.long 0x14000040
.long 0xfff000e0
.long 0
.long 0
b __arm7tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_at91_name
.long arm7tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __at91_proc_info, . - __at91_proc_info
.type __s3c4510b_proc_info, #object
__s3c4510b_proc_info:
.long 0x36365000
.long 0xfffff000
.long 0
.long 0
b __arm7tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_s3c4510b_name
.long arm7tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __s3c4510b_proc_info, . - __s3c4510b_proc_info
.type __s3c4530_proc_info, #object
__s3c4530_proc_info:
.long 0x4c000000
.long 0xfff000e0
.long 0
.long 0
b __arm7tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_s3c4530_name
.long arm7tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __s3c4530_proc_info, . - __s3c4530_proc_info
.type __s3c3410_proc_info, #object
__s3c3410_proc_info:
.long 0x34100000
.long 0xffff0000
.long 0
.long 0
b __arm7tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_s3c3410_name
.long arm7tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __s3c3410_proc_info, . - __s3c3410_proc_info
.type __s3c44b0x_proc_info, #object
__s3c44b0x_proc_info:
.long 0x44b00000
.long 0xffff0000
.long 0 .long 0
.long 0 .long 0
b __arm7tdmi_setup b __arm7tdmi_setup
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT .long HWCAP_SWP | HWCAP_26BIT | ( \extra_hwcaps )
.long cpu_s3c44b0x_name .long \cpu_name
.long arm7tdmi_processor_functions .long arm7tdmi_processor_functions
.long 0 .long 0
.long 0 .long 0
.long v4_cache_fns .long v4_cache_fns
.size __s3c44b0x_proc_info, . - __s3c44b0x_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
arm7tdmi_proc_info arm7tdmi, 0x41007700, 0xfff8ff00, \
cpu_arm7tdmi_name
arm7tdmi_proc_info triscenda7, 0x0001d2ff, 0x0001ffff, \
cpu_triscenda7_name, extra_hwcaps=HWCAP_THUMB
arm7tdmi_proc_info at91, 0x14000040, 0xfff000e0, \
cpu_at91_name, extra_hwcaps=HWCAP_THUMB
arm7tdmi_proc_info s3c4510b, 0x36365000, 0xfffff000, \
cpu_s3c4510b_name, extra_hwcaps=HWCAP_THUMB
arm7tdmi_proc_info s3c4530, 0x4c000000, 0xfff000e0, \
cpu_s3c4530_name, extra_hwcaps=HWCAP_THUMB
arm7tdmi_proc_info s3c3410, 0x34100000, 0xffff0000, \
cpu_s3c3410_name, extra_hwcaps=HWCAP_THUMB
arm7tdmi_proc_info s3c44b0x, 0x44b00000, 0xffff0000, \
cpu_s3c44b0x_name, extra_hwcaps=HWCAP_THUMB
...@@ -315,18 +315,8 @@ ENTRY(arm920_dma_unmap_area) ...@@ -315,18 +315,8 @@ ENTRY(arm920_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm920_dma_unmap_area) ENDPROC(arm920_dma_unmap_area)
ENTRY(arm920_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm920_flush_icache_all define_cache_functions arm920
.long arm920_flush_kern_cache_all
.long arm920_flush_user_cache_all
.long arm920_flush_user_cache_range
.long arm920_coherent_kern_range
.long arm920_coherent_user_range
.long arm920_flush_kern_dcache_area
.long arm920_dma_map_area
.long arm920_dma_unmap_area
.long arm920_dma_flush_range
#endif #endif
...@@ -416,9 +406,6 @@ ENTRY(cpu_arm920_do_resume) ...@@ -416,9 +406,6 @@ ENTRY(cpu_arm920_do_resume)
PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE
b cpu_resume_mmu b cpu_resume_mmu
ENDPROC(cpu_arm920_do_resume) ENDPROC(cpu_arm920_do_resume)
#else
#define cpu_arm920_do_suspend 0
#define cpu_arm920_do_resume 0
#endif #endif
__CPUINIT __CPUINIT
...@@ -450,43 +437,14 @@ arm920_crval: ...@@ -450,43 +437,14 @@ arm920_crval:
crval clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130 crval clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm920, dabort=v4t_early_abort, pabort=legacy_pabort, suspend=1
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm920_processor_functions, #object
arm920_processor_functions:
.word v4t_early_abort
.word legacy_pabort
.word cpu_arm920_proc_init
.word cpu_arm920_proc_fin
.word cpu_arm920_reset
.word cpu_arm920_do_idle
.word cpu_arm920_dcache_clean_area
.word cpu_arm920_switch_mm
.word cpu_arm920_set_pte_ext
.word cpu_arm920_suspend_size
.word cpu_arm920_do_suspend
.word cpu_arm920_do_resume
.size arm920_processor_functions, . - arm920_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4t" string cpu_arm920_name, "ARM920T"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm920_name, #object
cpu_arm920_name:
.asciz "ARM920T"
.size cpu_arm920_name, . - cpu_arm920_name
.align .align
......
...@@ -317,18 +317,8 @@ ENTRY(arm922_dma_unmap_area) ...@@ -317,18 +317,8 @@ ENTRY(arm922_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm922_dma_unmap_area) ENDPROC(arm922_dma_unmap_area)
ENTRY(arm922_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm922_flush_icache_all define_cache_functions arm922
.long arm922_flush_kern_cache_all
.long arm922_flush_user_cache_all
.long arm922_flush_user_cache_range
.long arm922_coherent_kern_range
.long arm922_coherent_user_range
.long arm922_flush_kern_dcache_area
.long arm922_dma_map_area
.long arm922_dma_unmap_area
.long arm922_dma_flush_range
#endif #endif
...@@ -420,43 +410,14 @@ arm922_crval: ...@@ -420,43 +410,14 @@ arm922_crval:
crval clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130 crval clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm922, dabort=v4t_early_abort, pabort=legacy_pabort
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm922_processor_functions, #object
arm922_processor_functions:
.word v4t_early_abort
.word legacy_pabort
.word cpu_arm922_proc_init
.word cpu_arm922_proc_fin
.word cpu_arm922_reset
.word cpu_arm922_do_idle
.word cpu_arm922_dcache_clean_area
.word cpu_arm922_switch_mm
.word cpu_arm922_set_pte_ext
.word 0
.word 0
.word 0
.size arm922_processor_functions, . - arm922_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4t" string cpu_arm922_name, "ARM922T"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm922_name, #object
cpu_arm922_name:
.asciz "ARM922T"
.size cpu_arm922_name, . - cpu_arm922_name
.align .align
......
...@@ -372,17 +372,8 @@ ENTRY(arm925_dma_unmap_area) ...@@ -372,17 +372,8 @@ ENTRY(arm925_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm925_dma_unmap_area) ENDPROC(arm925_dma_unmap_area)
ENTRY(arm925_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm925_flush_icache_all define_cache_functions arm925
.long arm925_flush_kern_cache_all
.long arm925_flush_user_cache_all
.long arm925_flush_user_cache_range
.long arm925_coherent_kern_range
.long arm925_coherent_user_range
.long arm925_flush_kern_dcache_area
.long arm925_dma_map_area
.long arm925_dma_unmap_area
.long arm925_dma_flush_range
ENTRY(cpu_arm925_dcache_clean_area) ENTRY(cpu_arm925_dcache_clean_area)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
...@@ -487,52 +478,24 @@ arm925_crval: ...@@ -487,52 +478,24 @@ arm925_crval:
crval clear=0x00007f3f, mmuset=0x0000313d, ucset=0x00001130 crval clear=0x00007f3f, mmuset=0x0000313d, ucset=0x00001130
__INITDATA __INITDATA
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
/* define_processor_functions arm925, dabort=v4t_early_abort, pabort=legacy_pabort
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
.type arm925_processor_functions, #object
arm925_processor_functions:
.word v4t_early_abort
.word legacy_pabort
.word cpu_arm925_proc_init
.word cpu_arm925_proc_fin
.word cpu_arm925_reset
.word cpu_arm925_do_idle
.word cpu_arm925_dcache_clean_area
.word cpu_arm925_switch_mm
.word cpu_arm925_set_pte_ext
.word 0
.word 0
.word 0
.size arm925_processor_functions, . - arm925_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4t" string cpu_arm925_name, "ARM925T"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm925_name, #object
cpu_arm925_name:
.asciz "ARM925T"
.size cpu_arm925_name, . - cpu_arm925_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __arm925_proc_info,#object .macro arm925_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req, cache
__arm925_proc_info: .type __\name\()_proc_info,#object
.long 0x54029250 __\name\()_proc_info:
.long 0xfffffff0 .long \cpu_val
.long \cpu_mask
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_BIT4 | \ PMD_BIT4 | \
PMD_SECT_AP_WRITE | \ PMD_SECT_AP_WRITE | \
...@@ -550,27 +513,8 @@ __arm925_proc_info: ...@@ -550,27 +513,8 @@ __arm925_proc_info:
.long v4wbi_tlb_fns .long v4wbi_tlb_fns
.long v4wb_user_fns .long v4wb_user_fns
.long arm925_cache_fns .long arm925_cache_fns
.size __arm925_proc_info, . - __arm925_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
.type __arm915_proc_info,#object arm925_proc_info arm925, 0x54029250, 0xfffffff0, cpu_arm925_name
__arm915_proc_info: arm925_proc_info arm915, 0x54029150, 0xfffffff0, cpu_arm925_name
.long 0x54029150
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm925_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
.long cpu_arm925_name
.long arm925_processor_functions
.long v4wbi_tlb_fns
.long v4wb_user_fns
.long arm925_cache_fns
.size __arm925_proc_info, . - __arm925_proc_info
...@@ -335,17 +335,8 @@ ENTRY(arm926_dma_unmap_area) ...@@ -335,17 +335,8 @@ ENTRY(arm926_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm926_dma_unmap_area) ENDPROC(arm926_dma_unmap_area)
ENTRY(arm926_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm926_flush_icache_all define_cache_functions arm926
.long arm926_flush_kern_cache_all
.long arm926_flush_user_cache_all
.long arm926_flush_user_cache_range
.long arm926_coherent_kern_range
.long arm926_coherent_user_range
.long arm926_flush_kern_dcache_area
.long arm926_dma_map_area
.long arm926_dma_unmap_area
.long arm926_dma_flush_range
ENTRY(cpu_arm926_dcache_clean_area) ENTRY(cpu_arm926_dcache_clean_area)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
...@@ -430,9 +421,6 @@ ENTRY(cpu_arm926_do_resume) ...@@ -430,9 +421,6 @@ ENTRY(cpu_arm926_do_resume)
PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE
b cpu_resume_mmu b cpu_resume_mmu
ENDPROC(cpu_arm926_do_resume) ENDPROC(cpu_arm926_do_resume)
#else
#define cpu_arm926_do_suspend 0
#define cpu_arm926_do_resume 0
#endif #endif
__CPUINIT __CPUINIT
...@@ -475,42 +463,14 @@ arm926_crval: ...@@ -475,42 +463,14 @@ arm926_crval:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm926, dabort=v5tj_early_abort, pabort=legacy_pabort, suspend=1
* come through these
*/
.type arm926_processor_functions, #object
arm926_processor_functions:
.word v5tj_early_abort
.word legacy_pabort
.word cpu_arm926_proc_init
.word cpu_arm926_proc_fin
.word cpu_arm926_reset
.word cpu_arm926_do_idle
.word cpu_arm926_dcache_clean_area
.word cpu_arm926_switch_mm
.word cpu_arm926_set_pte_ext
.word cpu_arm926_suspend_size
.word cpu_arm926_do_suspend
.word cpu_arm926_do_resume
.size arm926_processor_functions, . - arm926_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5tej"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5tej" string cpu_arm926_name, "ARM926EJ-S"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm926_name, #object
cpu_arm926_name:
.asciz "ARM926EJ-S"
.size cpu_arm926_name, . - cpu_arm926_name
.align .align
......
...@@ -264,17 +264,8 @@ ENTRY(arm940_dma_unmap_area) ...@@ -264,17 +264,8 @@ ENTRY(arm940_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm940_dma_unmap_area) ENDPROC(arm940_dma_unmap_area)
ENTRY(arm940_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm940_flush_icache_all define_cache_functions arm940
.long arm940_flush_kern_cache_all
.long arm940_flush_user_cache_all
.long arm940_flush_user_cache_range
.long arm940_coherent_kern_range
.long arm940_coherent_user_range
.long arm940_flush_kern_dcache_area
.long arm940_dma_map_area
.long arm940_dma_unmap_area
.long arm940_dma_flush_range
__CPUINIT __CPUINIT
...@@ -348,42 +339,14 @@ __arm940_setup: ...@@ -348,42 +339,14 @@ __arm940_setup:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm940, dabort=nommu_early_abort, pabort=legacy_pabort, nommu=1
* come through these
*/
.type arm940_processor_functions, #object
ENTRY(arm940_processor_functions)
.word nommu_early_abort
.word legacy_pabort
.word cpu_arm940_proc_init
.word cpu_arm940_proc_fin
.word cpu_arm940_reset
.word cpu_arm940_do_idle
.word cpu_arm940_dcache_clean_area
.word cpu_arm940_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm940_processor_functions, . - arm940_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4t" string cpu_arm940_name, "ARM940T"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm940_name, #object
cpu_arm940_name:
.ascii "ARM940T"
.size cpu_arm940_name, . - cpu_arm940_name
.align .align
......
...@@ -306,18 +306,8 @@ ENTRY(arm946_dma_unmap_area) ...@@ -306,18 +306,8 @@ ENTRY(arm946_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(arm946_dma_unmap_area) ENDPROC(arm946_dma_unmap_area)
ENTRY(arm946_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long arm946_flush_icache_all define_cache_functions arm946
.long arm946_flush_kern_cache_all
.long arm946_flush_user_cache_all
.long arm946_flush_user_cache_range
.long arm946_coherent_kern_range
.long arm946_coherent_user_range
.long arm946_flush_kern_dcache_area
.long arm946_dma_map_area
.long arm946_dma_unmap_area
.long arm946_dma_flush_range
ENTRY(cpu_arm946_dcache_clean_area) ENTRY(cpu_arm946_dcache_clean_area)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
...@@ -403,43 +393,14 @@ __arm946_setup: ...@@ -403,43 +393,14 @@ __arm946_setup:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm946, dabort=nommu_early_abort, pabort=legacy_pabort, nommu=1
* come through these
*/
.type arm946_processor_functions, #object
ENTRY(arm946_processor_functions)
.word nommu_early_abort
.word legacy_pabort
.word cpu_arm946_proc_init
.word cpu_arm946_proc_fin
.word cpu_arm946_reset
.word cpu_arm946_do_idle
.word cpu_arm946_dcache_clean_area
.word cpu_arm946_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm946_processor_functions, . - arm946_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5t"
.asciz "armv5te" string cpu_arm946_name, "ARM946E-S"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5t"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm946_name, #object
cpu_arm946_name:
.ascii "ARM946E-S"
.size cpu_arm946_name, . - cpu_arm946_name
.align .align
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include "proc-macros.S"
.text .text
/* /*
* cpu_arm9tdmi_proc_init() * cpu_arm9tdmi_proc_init()
...@@ -55,82 +57,38 @@ __arm9tdmi_setup: ...@@ -55,82 +57,38 @@ __arm9tdmi_setup:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions arm9tdmi, dabort=nommu_early_abort, pabort=legacy_pabort, nommu=1
* come through these
*/
.type arm9tdmi_processor_functions, #object
ENTRY(arm9tdmi_processor_functions)
.word nommu_early_abort
.word legacy_pabort
.word cpu_arm9tdmi_proc_init
.word cpu_arm9tdmi_proc_fin
.word cpu_arm9tdmi_reset
.word cpu_arm9tdmi_do_idle
.word cpu_arm9tdmi_dcache_clean_area
.word cpu_arm9tdmi_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm9tdmi_processor_functions, . - arm9tdmi_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4t"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4t" string cpu_arm9tdmi_name, "ARM9TDMI"
.size cpu_arch_name, . - cpu_arch_name string cpu_p2001_name, "P2001"
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_arm9tdmi_name, #object
cpu_arm9tdmi_name:
.asciz "ARM9TDMI"
.size cpu_arm9tdmi_name, . - cpu_arm9tdmi_name
.type cpu_p2001_name, #object
cpu_p2001_name:
.asciz "P2001"
.size cpu_p2001_name, . - cpu_p2001_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __arm9tdmi_proc_info, #object .macro arm9tdmi_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req
__arm9tdmi_proc_info: .type __\name\()_proc_info, #object
.long 0x41009900 __\name\()_proc_info:
.long 0xfff8ff00 .long \cpu_val
.long \cpu_mask
.long 0 .long 0
.long 0 .long 0
b __arm9tdmi_setup b __arm9tdmi_setup
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT .long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_arm9tdmi_name .long \cpu_name
.long arm9tdmi_processor_functions .long arm9tdmi_processor_functions
.long 0 .long 0
.long 0 .long 0
.long v4_cache_fns .long v4_cache_fns
.size __arm9tdmi_proc_info, . - __arm9tdmi_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
.type __p2001_proc_info, #object arm9tdmi_proc_info arm9tdmi, 0x41009900, 0xfff8ff00, cpu_arm9tdmi_name
__p2001_proc_info: arm9tdmi_proc_info p2001, 0x41029000, 0xffffffff, cpu_p2001_name
.long 0x41029000
.long 0xffffffff
.long 0
.long 0
b __arm9tdmi_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
.long cpu_p2001_name
.long arm9tdmi_processor_functions
.long 0
.long 0
.long v4_cache_fns
.size __p2001_proc_info, . - __p2001_proc_info
...@@ -180,42 +180,14 @@ fa526_cr1_set: ...@@ -180,42 +180,14 @@ fa526_cr1_set:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions fa526, dabort=v4_early_abort, pabort=legacy_pabort
* come through these
*/
.type fa526_processor_functions, #object
fa526_processor_functions:
.word v4_early_abort
.word legacy_pabort
.word cpu_fa526_proc_init
.word cpu_fa526_proc_fin
.word cpu_fa526_reset
.word cpu_fa526_do_idle
.word cpu_fa526_dcache_clean_area
.word cpu_fa526_switch_mm
.word cpu_fa526_set_pte_ext
.word 0
.word 0
.word 0
.size fa526_processor_functions, . - fa526_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4" string cpu_fa526_name, "FA526"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_fa526_name, #object
cpu_fa526_name:
.asciz "FA526"
.size cpu_fa526_name, . - cpu_fa526_name
.align .align
......
...@@ -411,29 +411,28 @@ ENTRY(feroceon_dma_unmap_area) ...@@ -411,29 +411,28 @@ ENTRY(feroceon_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(feroceon_dma_unmap_area) ENDPROC(feroceon_dma_unmap_area)
ENTRY(feroceon_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long feroceon_flush_icache_all define_cache_functions feroceon
.long feroceon_flush_kern_cache_all
.long feroceon_flush_user_cache_all .macro range_alias basename
.long feroceon_flush_user_cache_range .globl feroceon_range_\basename
.long feroceon_coherent_kern_range .type feroceon_range_\basename , %function
.long feroceon_coherent_user_range .equ feroceon_range_\basename , feroceon_\basename
.long feroceon_flush_kern_dcache_area .endm
.long feroceon_dma_map_area
.long feroceon_dma_unmap_area /*
.long feroceon_dma_flush_range * Most of the cache functions are unchanged for this case.
* Export suitable alias symbols for the unchanged functions:
ENTRY(feroceon_range_cache_fns) */
.long feroceon_flush_icache_all range_alias flush_icache_all
.long feroceon_flush_kern_cache_all range_alias flush_user_cache_all
.long feroceon_flush_user_cache_all range_alias flush_kern_cache_all
.long feroceon_flush_user_cache_range range_alias flush_user_cache_range
.long feroceon_coherent_kern_range range_alias coherent_kern_range
.long feroceon_coherent_user_range range_alias coherent_user_range
.long feroceon_range_flush_kern_dcache_area range_alias dma_unmap_area
.long feroceon_range_dma_map_area
.long feroceon_dma_unmap_area define_cache_functions feroceon_range
.long feroceon_range_dma_flush_range
.align 5 .align 5
ENTRY(cpu_feroceon_dcache_clean_area) ENTRY(cpu_feroceon_dcache_clean_area)
...@@ -539,93 +538,27 @@ feroceon_crval: ...@@ -539,93 +538,27 @@ feroceon_crval:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions feroceon, dabort=v5t_early_abort, pabort=legacy_pabort
* come through these
*/
.type feroceon_processor_functions, #object
feroceon_processor_functions:
.word v5t_early_abort
.word legacy_pabort
.word cpu_feroceon_proc_init
.word cpu_feroceon_proc_fin
.word cpu_feroceon_reset
.word cpu_feroceon_do_idle
.word cpu_feroceon_dcache_clean_area
.word cpu_feroceon_switch_mm
.word cpu_feroceon_set_pte_ext
.word 0
.word 0
.word 0
.size feroceon_processor_functions, . - feroceon_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5te" string cpu_feroceon_name, "Feroceon"
.size cpu_arch_name, . - cpu_arch_name string cpu_88fr531_name, "Feroceon 88FR531-vd"
string cpu_88fr571_name, "Feroceon 88FR571-vd"
.type cpu_elf_name, #object string cpu_88fr131_name, "Feroceon 88FR131"
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_feroceon_name, #object
cpu_feroceon_name:
.asciz "Feroceon"
.size cpu_feroceon_name, . - cpu_feroceon_name
.type cpu_88fr531_name, #object
cpu_88fr531_name:
.asciz "Feroceon 88FR531-vd"
.size cpu_88fr531_name, . - cpu_88fr531_name
.type cpu_88fr571_name, #object
cpu_88fr571_name:
.asciz "Feroceon 88FR571-vd"
.size cpu_88fr571_name, . - cpu_88fr571_name
.type cpu_88fr131_name, #object
cpu_88fr131_name:
.asciz "Feroceon 88FR131"
.size cpu_88fr131_name, . - cpu_88fr131_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
#ifdef CONFIG_CPU_FEROCEON_OLD_ID .macro feroceon_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req, cache:req
.type __feroceon_old_id_proc_info,#object .type __\name\()_proc_info,#object
__feroceon_old_id_proc_info: __\name\()_proc_info:
.long 0x41009260 .long \cpu_val
.long 0xff00fff0 .long \cpu_mask
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __feroceon_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_feroceon_name
.long feroceon_processor_functions
.long v4wbi_tlb_fns
.long feroceon_user_fns
.long feroceon_cache_fns
.size __feroceon_old_id_proc_info, . - __feroceon_old_id_proc_info
#endif
.type __88fr531_proc_info,#object
__88fr531_proc_info:
.long 0x56055310
.long 0xfffffff0
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \ PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \ PMD_SECT_CACHEABLE | \
...@@ -640,59 +573,22 @@ __88fr531_proc_info: ...@@ -640,59 +573,22 @@ __88fr531_proc_info:
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_88fr531_name .long \cpu_name
.long feroceon_processor_functions .long feroceon_processor_functions
.long v4wbi_tlb_fns .long v4wbi_tlb_fns
.long feroceon_user_fns .long feroceon_user_fns
.long feroceon_cache_fns .long \cache
.size __88fr531_proc_info, . - __88fr531_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
.type __88fr571_proc_info,#object #ifdef CONFIG_CPU_FEROCEON_OLD_ID
__88fr571_proc_info: feroceon_proc_info feroceon_old_id, 0x41009260, 0xff00fff0, \
.long 0x56155710 cpu_name=cpu_feroceon_name, cache=feroceon_cache_fns
.long 0xfffffff0 #endif
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __feroceon_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_88fr571_name
.long feroceon_processor_functions
.long v4wbi_tlb_fns
.long feroceon_user_fns
.long feroceon_range_cache_fns
.size __88fr571_proc_info, . - __88fr571_proc_info
.type __88fr131_proc_info,#object feroceon_proc_info 88fr531, 0x56055310, 0xfffffff0, cpu_88fr531_name, \
__88fr131_proc_info: cache=feroceon_cache_fns
.long 0x56251310 feroceon_proc_info 88fr571, 0x56155710, 0xfffffff0, cpu_88fr571_name, \
.long 0xfffffff0 cache=feroceon_range_cache_fns
.long PMD_TYPE_SECT | \ feroceon_proc_info 88fr131, 0x56251310, 0xfffffff0, cpu_88fr131_name, \
PMD_SECT_BUFFERABLE | \ cache=feroceon_range_cache_fns
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __feroceon_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_88fr131_name
.long feroceon_processor_functions
.long v4wbi_tlb_fns
.long feroceon_user_fns
.long feroceon_range_cache_fns
.size __88fr131_proc_info, . - __88fr131_proc_info
...@@ -254,3 +254,71 @@ ...@@ -254,3 +254,71 @@
mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line
mcr p15, 0, ip, c7, c10, 4 @ data write barrier mcr p15, 0, ip, c7, c10, 4 @ data write barrier
.endm .endm
.macro define_processor_functions name:req, dabort:req, pabort:req, nommu=0, suspend=0
.type \name\()_processor_functions, #object
.align 2
ENTRY(\name\()_processor_functions)
.word \dabort
.word \pabort
.word cpu_\name\()_proc_init
.word cpu_\name\()_proc_fin
.word cpu_\name\()_reset
.word cpu_\name\()_do_idle
.word cpu_\name\()_dcache_clean_area
.word cpu_\name\()_switch_mm
.if \nommu
.word 0
.else
.word cpu_\name\()_set_pte_ext
.endif
.if \suspend
.word cpu_\name\()_suspend_size
#ifdef CONFIG_PM_SLEEP
.word cpu_\name\()_do_suspend
.word cpu_\name\()_do_resume
#else
.word 0
.word 0
#endif
.else
.word 0
.word 0
.word 0
.endif
.size \name\()_processor_functions, . - \name\()_processor_functions
.endm
.macro define_cache_functions name:req
.align 2
.type \name\()_cache_fns, #object
ENTRY(\name\()_cache_fns)
.long \name\()_flush_icache_all
.long \name\()_flush_kern_cache_all
.long \name\()_flush_user_cache_all
.long \name\()_flush_user_cache_range
.long \name\()_coherent_kern_range
.long \name\()_coherent_user_range
.long \name\()_flush_kern_dcache_area
.long \name\()_dma_map_area
.long \name\()_dma_unmap_area
.long \name\()_dma_flush_range
.size \name\()_cache_fns, . - \name\()_cache_fns
.endm
.macro define_tlb_functions name:req, flags_up:req, flags_smp
.type \name\()_tlb_fns, #object
ENTRY(\name\()_tlb_fns)
.long \name\()_flush_user_tlb_range
.long \name\()_flush_kern_tlb_range
.ifnb \flags_smp
ALT_SMP(.long \flags_smp )
ALT_UP(.long \flags_up )
.else
.long \flags_up
.endif
.size \name\()_tlb_fns, . - \name\()_tlb_fns
.endm
...@@ -92,6 +92,17 @@ ENTRY(cpu_mohawk_do_idle) ...@@ -92,6 +92,17 @@ ENTRY(cpu_mohawk_do_idle)
mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt
mov pc, lr mov pc, lr
/*
* flush_icache_all()
*
* Unconditionally clean and invalidate the entire icache.
*/
ENTRY(mohawk_flush_icache_all)
mov r0, #0
mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
mov pc, lr
ENDPROC(mohawk_flush_icache_all)
/* /*
* flush_user_cache_all() * flush_user_cache_all()
* *
...@@ -288,16 +299,8 @@ ENTRY(mohawk_dma_unmap_area) ...@@ -288,16 +299,8 @@ ENTRY(mohawk_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(mohawk_dma_unmap_area) ENDPROC(mohawk_dma_unmap_area)
ENTRY(mohawk_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long mohawk_flush_kern_cache_all define_cache_functions mohawk
.long mohawk_flush_user_cache_all
.long mohawk_flush_user_cache_range
.long mohawk_coherent_kern_range
.long mohawk_coherent_user_range
.long mohawk_flush_kern_dcache_area
.long mohawk_dma_map_area
.long mohawk_dma_unmap_area
.long mohawk_dma_flush_range
ENTRY(cpu_mohawk_dcache_clean_area) ENTRY(cpu_mohawk_dcache_clean_area)
1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry
...@@ -373,42 +376,14 @@ mohawk_crval: ...@@ -373,42 +376,14 @@ mohawk_crval:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions mohawk, dabort=v5t_early_abort, pabort=legacy_pabort
* come through these
*/
.type mohawk_processor_functions, #object
mohawk_processor_functions:
.word v5t_early_abort
.word legacy_pabort
.word cpu_mohawk_proc_init
.word cpu_mohawk_proc_fin
.word cpu_mohawk_reset
.word cpu_mohawk_do_idle
.word cpu_mohawk_dcache_clean_area
.word cpu_mohawk_switch_mm
.word cpu_mohawk_set_pte_ext
.word 0
.word 0
.word 0
.size mohawk_processor_functions, . - mohawk_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5te" string cpu_mohawk_name, "Marvell 88SV331x"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_mohawk_name, #object
cpu_mohawk_name:
.asciz "Marvell 88SV331x"
.size cpu_mohawk_name, . - cpu_mohawk_name
.align .align
......
...@@ -187,43 +187,14 @@ sa110_crval: ...@@ -187,43 +187,14 @@ sa110_crval:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions sa110, dabort=v4_early_abort, pabort=legacy_pabort
* come through these
*/
.type sa110_processor_functions, #object
ENTRY(sa110_processor_functions)
.word v4_early_abort
.word legacy_pabort
.word cpu_sa110_proc_init
.word cpu_sa110_proc_fin
.word cpu_sa110_reset
.word cpu_sa110_do_idle
.word cpu_sa110_dcache_clean_area
.word cpu_sa110_switch_mm
.word cpu_sa110_set_pte_ext
.word 0
.word 0
.word 0
.size sa110_processor_functions, . - sa110_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv4"
cpu_arch_name: string cpu_elf_name, "v4"
.asciz "armv4" string cpu_sa110_name, "StrongARM-110"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_sa110_name, #object
cpu_sa110_name:
.asciz "StrongARM-110"
.size cpu_sa110_name, . - cpu_sa110_name
.align .align
......
...@@ -198,9 +198,6 @@ ENTRY(cpu_sa1100_do_resume) ...@@ -198,9 +198,6 @@ ENTRY(cpu_sa1100_do_resume)
PMD_SECT_CACHEABLE | PMD_SECT_AP_WRITE PMD_SECT_CACHEABLE | PMD_SECT_AP_WRITE
b cpu_resume_mmu b cpu_resume_mmu
ENDPROC(cpu_sa1100_do_resume) ENDPROC(cpu_sa1100_do_resume)
#else
#define cpu_sa1100_do_suspend 0
#define cpu_sa1100_do_resume 0
#endif #endif
__CPUINIT __CPUINIT
...@@ -233,60 +230,29 @@ sa1100_crval: ...@@ -233,60 +230,29 @@ sa1100_crval:
__INITDATA __INITDATA
/*
* Purpose : Function pointers used to access above functions - all calls
* come through these
*/
/* /*
* SA1100 and SA1110 share the same function calls * SA1100 and SA1110 share the same function calls
*/ */
.type sa1100_processor_functions, #object
ENTRY(sa1100_processor_functions)
.word v4_early_abort
.word legacy_pabort
.word cpu_sa1100_proc_init
.word cpu_sa1100_proc_fin
.word cpu_sa1100_reset
.word cpu_sa1100_do_idle
.word cpu_sa1100_dcache_clean_area
.word cpu_sa1100_switch_mm
.word cpu_sa1100_set_pte_ext
.word cpu_sa1100_suspend_size
.word cpu_sa1100_do_suspend
.word cpu_sa1100_do_resume
.size sa1100_processor_functions, . - sa1100_processor_functions
.section ".rodata"
.type cpu_arch_name, #object @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
cpu_arch_name: define_processor_functions sa1100, dabort=v4_early_abort, pabort=legacy_pabort, suspend=1
.asciz "armv4"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object .section ".rodata"
cpu_elf_name:
.asciz "v4"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_sa1100_name, #object
cpu_sa1100_name:
.asciz "StrongARM-1100"
.size cpu_sa1100_name, . - cpu_sa1100_name
.type cpu_sa1110_name, #object string cpu_arch_name, "armv4"
cpu_sa1110_name: string cpu_elf_name, "v4"
.asciz "StrongARM-1110" string cpu_sa1100_name, "StrongARM-1100"
.size cpu_sa1110_name, . - cpu_sa1110_name string cpu_sa1110_name, "StrongARM-1110"
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __sa1100_proc_info,#object .macro sa1100_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req
__sa1100_proc_info: .type __\name\()_proc_info,#object
.long 0x4401a110 __\name\()_proc_info:
.long 0xfffffff0 .long \cpu_val
.long \cpu_mask
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \ PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \ PMD_SECT_CACHEABLE | \
...@@ -299,32 +265,13 @@ __sa1100_proc_info: ...@@ -299,32 +265,13 @@ __sa1100_proc_info:
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT
.long cpu_sa1100_name .long \cpu_name
.long sa1100_processor_functions .long sa1100_processor_functions
.long v4wb_tlb_fns .long v4wb_tlb_fns
.long v4_mc_user_fns .long v4_mc_user_fns
.long v4wb_cache_fns .long v4wb_cache_fns
.size __sa1100_proc_info, . - __sa1100_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
.type __sa1110_proc_info,#object sa1100_proc_info sa1100, 0x4401a110, 0xfffffff0, cpu_sa1100_name
__sa1110_proc_info: sa1100_proc_info sa1110, 0x6901b110, 0xfffffff0, cpu_sa1110_name
.long 0x6901b110
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __sa1100_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT
.long cpu_sa1110_name
.long sa1100_processor_functions
.long v4wb_tlb_fns
.long v4_mc_user_fns
.long v4wb_cache_fns
.size __sa1110_proc_info, . - __sa1110_proc_info
...@@ -56,6 +56,11 @@ ENTRY(cpu_v6_proc_fin) ...@@ -56,6 +56,11 @@ ENTRY(cpu_v6_proc_fin)
*/ */
.align 5 .align 5
ENTRY(cpu_v6_reset) ENTRY(cpu_v6_reset)
mrc p15, 0, r1, c1, c0, 0 @ ctrl register
bic r1, r1, #0x1 @ ...............m
mcr p15, 0, r1, c1, c0, 0 @ disable MMU
mov r1, #0
mcr p15, 0, r1, c7, c5, 4 @ ISB
mov pc, r0 mov pc, r0
/* /*
...@@ -164,16 +169,9 @@ ENDPROC(cpu_v6_do_resume) ...@@ -164,16 +169,9 @@ ENDPROC(cpu_v6_do_resume)
cpu_resume_l1_flags: cpu_resume_l1_flags:
ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_SMP) ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_SMP)
ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_UP) ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_UP)
#else
#define cpu_v6_do_suspend 0
#define cpu_v6_do_resume 0
#endif #endif
string cpu_v6_name, "ARMv6-compatible processor"
.type cpu_v6_name, #object
cpu_v6_name:
.asciz "ARMv6-compatible processor"
.size cpu_v6_name, . - cpu_v6_name
.align .align
...@@ -239,33 +237,13 @@ v6_crval: ...@@ -239,33 +237,13 @@ v6_crval:
__INITDATA __INITDATA
.type v6_processor_functions, #object @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
ENTRY(v6_processor_functions) define_processor_functions v6, dabort=v6_early_abort, pabort=v6_pabort, suspend=1
.word v6_early_abort
.word v6_pabort
.word cpu_v6_proc_init
.word cpu_v6_proc_fin
.word cpu_v6_reset
.word cpu_v6_do_idle
.word cpu_v6_dcache_clean_area
.word cpu_v6_switch_mm
.word cpu_v6_set_pte_ext
.word cpu_v6_suspend_size
.word cpu_v6_do_suspend
.word cpu_v6_do_resume
.size v6_processor_functions, . - v6_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv6"
cpu_arch_name: string cpu_elf_name, "v6"
.asciz "armv6"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v6"
.size cpu_elf_name, . - cpu_elf_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
......
...@@ -58,9 +58,16 @@ ENDPROC(cpu_v7_proc_fin) ...@@ -58,9 +58,16 @@ ENDPROC(cpu_v7_proc_fin)
* to what would be the reset vector. * to what would be the reset vector.
* *
* - loc - location to jump to for soft reset * - loc - location to jump to for soft reset
*
* This code must be executed using a flat identity mapping with
* caches disabled.
*/ */
.align 5 .align 5
ENTRY(cpu_v7_reset) ENTRY(cpu_v7_reset)
mrc p15, 0, r1, c1, c0, 0 @ ctrl register
bic r1, r1, #0x1 @ ...............m
mcr p15, 0, r1, c1, c0, 0 @ disable MMU
isb
mov pc, r0 mov pc, r0
ENDPROC(cpu_v7_reset) ENDPROC(cpu_v7_reset)
...@@ -173,8 +180,7 @@ ENTRY(cpu_v7_set_pte_ext) ...@@ -173,8 +180,7 @@ ENTRY(cpu_v7_set_pte_ext)
mov pc, lr mov pc, lr
ENDPROC(cpu_v7_set_pte_ext) ENDPROC(cpu_v7_set_pte_ext)
cpu_v7_name: string cpu_v7_name, "ARMv7 Processor"
.ascii "ARMv7 Processor"
.align .align
/* /*
...@@ -257,9 +263,6 @@ ENDPROC(cpu_v7_do_resume) ...@@ -257,9 +263,6 @@ ENDPROC(cpu_v7_do_resume)
cpu_resume_l1_flags: cpu_resume_l1_flags:
ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_SMP) ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_SMP)
ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_UP) ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_FLAGS_UP)
#else
#define cpu_v7_do_suspend 0
#define cpu_v7_do_resume 0
#endif #endif
__CPUINIT __CPUINIT
...@@ -279,13 +282,20 @@ cpu_resume_l1_flags: ...@@ -279,13 +282,20 @@ cpu_resume_l1_flags:
* It is assumed that: * It is assumed that:
* - cache type register is implemented * - cache type register is implemented
*/ */
__v7_ca5mp_setup:
__v7_ca9mp_setup: __v7_ca9mp_setup:
mov r10, #(1 << 0) @ TLB ops broadcasting
b 1f
__v7_ca15mp_setup:
mov r10, #0
1:
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
ALT_SMP(mrc p15, 0, r0, c1, c0, 1) ALT_SMP(mrc p15, 0, r0, c1, c0, 1)
ALT_UP(mov r0, #(1 << 6)) @ fake it for UP ALT_UP(mov r0, #(1 << 6)) @ fake it for UP
tst r0, #(1 << 6) @ SMP/nAMP mode enabled? tst r0, #(1 << 6) @ SMP/nAMP mode enabled?
orreq r0, r0, #(1 << 6) | (1 << 0) @ Enable SMP/nAMP mode and orreq r0, r0, #(1 << 6) @ Enable SMP/nAMP mode
mcreq p15, 0, r0, c1, c0, 1 @ TLB ops broadcasting orreq r0, r0, r10 @ Enable CPU-specific SMP bits
mcreq p15, 0, r0, c1, c0, 1
#endif #endif
__v7_setup: __v7_setup:
adr r12, __v7_setup_stack @ the local stack adr r12, __v7_setup_stack @ the local stack
...@@ -411,66 +421,69 @@ __v7_setup_stack: ...@@ -411,66 +421,69 @@ __v7_setup_stack:
__INITDATA __INITDATA
.type v7_processor_functions, #object @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
ENTRY(v7_processor_functions) define_processor_functions v7, dabort=v7_early_abort, pabort=v7_pabort, suspend=1
.word v7_early_abort
.word v7_pabort
.word cpu_v7_proc_init
.word cpu_v7_proc_fin
.word cpu_v7_reset
.word cpu_v7_do_idle
.word cpu_v7_dcache_clean_area
.word cpu_v7_switch_mm
.word cpu_v7_set_pte_ext
.word cpu_v7_suspend_size
.word cpu_v7_do_suspend
.word cpu_v7_do_resume
.size v7_processor_functions, . - v7_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv7"
cpu_arch_name: string cpu_elf_name, "v7"
.asciz "armv7"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v7"
.size cpu_elf_name, . - cpu_elf_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __v7_ca9mp_proc_info, #object /*
__v7_ca9mp_proc_info: * Standard v7 proc info content
.long 0x410fc090 @ Required ID value */
.long 0xff0ffff0 @ Mask for ID .macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0
ALT_SMP(.long \ ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
PMD_TYPE_SECT | \ PMD_FLAGS_SMP | \mm_mmuflags)
PMD_SECT_AP_WRITE | \ ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
PMD_SECT_AP_READ | \ PMD_FLAGS_UP | \mm_mmuflags)
PMD_FLAGS_SMP) .long PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_AP_WRITE | \
ALT_UP(.long \ PMD_SECT_AP_READ | \io_mmuflags
PMD_TYPE_SECT | \ W(b) \initfunc
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | \
PMD_FLAGS_UP)
.long PMD_TYPE_SECT | \
PMD_SECT_XN | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
W(b) __v7_ca9mp_setup
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_TLS .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \
HWCAP_EDSP | HWCAP_TLS | \hwcaps
.long cpu_v7_name .long cpu_v7_name
.long v7_processor_functions .long v7_processor_functions
.long v7wbi_tlb_fns .long v7wbi_tlb_fns
.long v6_user_fns .long v6_user_fns
.long v7_cache_fns .long v7_cache_fns
.endm
/*
* ARM Ltd. Cortex A5 processor.
*/
.type __v7_ca5mp_proc_info, #object
__v7_ca5mp_proc_info:
.long 0x410fc050
.long 0xff0ffff0
__v7_proc __v7_ca5mp_setup
.size __v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info
/*
* ARM Ltd. Cortex A9 processor.
*/
.type __v7_ca9mp_proc_info, #object
__v7_ca9mp_proc_info:
.long 0x410fc090
.long 0xff0ffff0
__v7_proc __v7_ca9mp_setup
.size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info .size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info
/*
* ARM Ltd. Cortex A15 processor.
*/
.type __v7_ca15mp_proc_info, #object
__v7_ca15mp_proc_info:
.long 0x410fc0f0
.long 0xff0ffff0
__v7_proc __v7_ca15mp_setup, hwcaps = HWCAP_IDIV
.size __v7_ca15mp_proc_info, . - __v7_ca15mp_proc_info
/* /*
* Match any ARMv7 processor core. * Match any ARMv7 processor core.
*/ */
...@@ -478,27 +491,5 @@ __v7_ca9mp_proc_info: ...@@ -478,27 +491,5 @@ __v7_ca9mp_proc_info:
__v7_proc_info: __v7_proc_info:
.long 0x000f0000 @ Required ID value .long 0x000f0000 @ Required ID value
.long 0x000f0000 @ Mask for ID .long 0x000f0000 @ Mask for ID
ALT_SMP(.long \ __v7_proc __v7_setup
PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | \
PMD_FLAGS_SMP)
ALT_UP(.long \
PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | \
PMD_FLAGS_UP)
.long PMD_TYPE_SECT | \
PMD_SECT_XN | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
W(b) __v7_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_TLS
.long cpu_v7_name
.long v7_processor_functions
.long v7wbi_tlb_fns
.long v6_user_fns
.long v7_cache_fns
.size __v7_proc_info, . - __v7_proc_info .size __v7_proc_info, . - __v7_proc_info
...@@ -335,17 +335,8 @@ ENTRY(xsc3_dma_unmap_area) ...@@ -335,17 +335,8 @@ ENTRY(xsc3_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(xsc3_dma_unmap_area) ENDPROC(xsc3_dma_unmap_area)
ENTRY(xsc3_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long xsc3_flush_icache_all define_cache_functions xsc3
.long xsc3_flush_kern_cache_all
.long xsc3_flush_user_cache_all
.long xsc3_flush_user_cache_range
.long xsc3_coherent_kern_range
.long xsc3_coherent_user_range
.long xsc3_flush_kern_dcache_area
.long xsc3_dma_map_area
.long xsc3_dma_unmap_area
.long xsc3_dma_flush_range
ENTRY(cpu_xsc3_dcache_clean_area) ENTRY(cpu_xsc3_dcache_clean_area)
1: mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line 1: mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line
...@@ -454,9 +445,6 @@ ENTRY(cpu_xsc3_do_resume) ...@@ -454,9 +445,6 @@ ENTRY(cpu_xsc3_do_resume)
ldr r3, =0x542e @ section flags ldr r3, =0x542e @ section flags
b cpu_resume_mmu b cpu_resume_mmu
ENDPROC(cpu_xsc3_do_resume) ENDPROC(cpu_xsc3_do_resume)
#else
#define cpu_xsc3_do_suspend 0
#define cpu_xsc3_do_resume 0
#endif #endif
__CPUINIT __CPUINIT
...@@ -503,52 +491,24 @@ xsc3_crval: ...@@ -503,52 +491,24 @@ xsc3_crval:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions xsc3, dabort=v5t_early_abort, pabort=legacy_pabort, suspend=1
* come through these
*/
.type xsc3_processor_functions, #object
ENTRY(xsc3_processor_functions)
.word v5t_early_abort
.word legacy_pabort
.word cpu_xsc3_proc_init
.word cpu_xsc3_proc_fin
.word cpu_xsc3_reset
.word cpu_xsc3_do_idle
.word cpu_xsc3_dcache_clean_area
.word cpu_xsc3_switch_mm
.word cpu_xsc3_set_pte_ext
.word cpu_xsc3_suspend_size
.word cpu_xsc3_do_suspend
.word cpu_xsc3_do_resume
.size xsc3_processor_functions, . - xsc3_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5te" string cpu_xsc3_name, "XScale-V3 based processor"
.size cpu_arch_name, . - cpu_arch_name
.type cpu_elf_name, #object
cpu_elf_name:
.asciz "v5"
.size cpu_elf_name, . - cpu_elf_name
.type cpu_xsc3_name, #object
cpu_xsc3_name:
.asciz "XScale-V3 based processor"
.size cpu_xsc3_name, . - cpu_xsc3_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __xsc3_proc_info,#object .macro xsc3_proc_info name:req, cpu_val:req, cpu_mask:req
__xsc3_proc_info: .type __\name\()_proc_info,#object
.long 0x69056000 __\name\()_proc_info:
.long 0xffffe000 .long \cpu_val
.long \cpu_mask
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \ PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \ PMD_SECT_CACHEABLE | \
...@@ -566,29 +526,10 @@ __xsc3_proc_info: ...@@ -566,29 +526,10 @@ __xsc3_proc_info:
.long v4wbi_tlb_fns .long v4wbi_tlb_fns
.long xsc3_mc_user_fns .long xsc3_mc_user_fns
.long xsc3_cache_fns .long xsc3_cache_fns
.size __xsc3_proc_info, . - __xsc3_proc_info .size __\name\()_proc_info, . - __\name\()_proc_info
.endm
/* Note: PXA935 changed its implementor ID from Intel to Marvell */ xsc3_proc_info xsc3, 0x69056000, 0xffffe000
.type __xsc3_pxa935_proc_info,#object /* Note: PXA935 changed its implementor ID from Intel to Marvell */
__xsc3_pxa935_proc_info: xsc3_proc_info xsc3_pxa935, 0x56056000, 0xffffe000
.long 0x56056000
.long 0xffffe000
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xsc3_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_xsc3_name
.long xsc3_processor_functions
.long v4wbi_tlb_fns
.long xsc3_mc_user_fns
.long xsc3_cache_fns
.size __xsc3_pxa935_proc_info, . - __xsc3_pxa935_proc_info
...@@ -390,12 +390,12 @@ ENDPROC(xscale_dma_map_area) ...@@ -390,12 +390,12 @@ ENDPROC(xscale_dma_map_area)
* - size - size of region * - size - size of region
* - dir - DMA direction * - dir - DMA direction
*/ */
ENTRY(xscale_dma_a0_map_area) ENTRY(xscale_80200_A0_A1_dma_map_area)
add r1, r1, r0 add r1, r1, r0
teq r2, #DMA_TO_DEVICE teq r2, #DMA_TO_DEVICE
beq xscale_dma_clean_range beq xscale_dma_clean_range
b xscale_dma_flush_range b xscale_dma_flush_range
ENDPROC(xscale_dma_a0_map_area) ENDPROC(xscale_80200_A0_A1_dma_map_area)
/* /*
* dma_unmap_area(start, size, dir) * dma_unmap_area(start, size, dir)
...@@ -407,17 +407,8 @@ ENTRY(xscale_dma_unmap_area) ...@@ -407,17 +407,8 @@ ENTRY(xscale_dma_unmap_area)
mov pc, lr mov pc, lr
ENDPROC(xscale_dma_unmap_area) ENDPROC(xscale_dma_unmap_area)
ENTRY(xscale_cache_fns) @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
.long xscale_flush_icache_all define_cache_functions xscale
.long xscale_flush_kern_cache_all
.long xscale_flush_user_cache_all
.long xscale_flush_user_cache_range
.long xscale_coherent_kern_range
.long xscale_coherent_user_range
.long xscale_flush_kern_dcache_area
.long xscale_dma_map_area
.long xscale_dma_unmap_area
.long xscale_dma_flush_range
/* /*
* On stepping A0/A1 of the 80200, invalidating D-cache by line doesn't * On stepping A0/A1 of the 80200, invalidating D-cache by line doesn't
...@@ -432,16 +423,28 @@ ENTRY(xscale_cache_fns) ...@@ -432,16 +423,28 @@ ENTRY(xscale_cache_fns)
* revision January 22, 2003, available at: * revision January 22, 2003, available at:
* http://www.intel.com/design/iio/specupdt/273415.htm * http://www.intel.com/design/iio/specupdt/273415.htm
*/ */
ENTRY(xscale_80200_A0_A1_cache_fns) .macro a0_alias basename
.long xscale_flush_kern_cache_all .globl xscale_80200_A0_A1_\basename
.long xscale_flush_user_cache_all .type xscale_80200_A0_A1_\basename , %function
.long xscale_flush_user_cache_range .equ xscale_80200_A0_A1_\basename , xscale_\basename
.long xscale_coherent_kern_range .endm
.long xscale_coherent_user_range
.long xscale_flush_kern_dcache_area /*
.long xscale_dma_a0_map_area * Most of the cache functions are unchanged for these processor revisions.
.long xscale_dma_unmap_area * Export suitable alias symbols for the unchanged functions:
.long xscale_dma_flush_range */
a0_alias flush_icache_all
a0_alias flush_user_cache_all
a0_alias flush_kern_cache_all
a0_alias flush_user_cache_range
a0_alias coherent_kern_range
a0_alias coherent_user_range
a0_alias flush_kern_dcache_area
a0_alias dma_flush_range
a0_alias dma_unmap_area
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
define_cache_functions xscale_80200_A0_A1
ENTRY(cpu_xscale_dcache_clean_area) ENTRY(cpu_xscale_dcache_clean_area)
1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry
...@@ -551,9 +554,6 @@ ENTRY(cpu_xscale_do_resume) ...@@ -551,9 +554,6 @@ ENTRY(cpu_xscale_do_resume)
PMD_SECT_CACHEABLE | PMD_SECT_AP_WRITE PMD_SECT_CACHEABLE | PMD_SECT_AP_WRITE
b cpu_resume_mmu b cpu_resume_mmu
ENDPROC(cpu_xscale_do_resume) ENDPROC(cpu_xscale_do_resume)
#else
#define cpu_xscale_do_suspend 0
#define cpu_xscale_do_resume 0
#endif #endif
__CPUINIT __CPUINIT
...@@ -587,209 +587,38 @@ xscale_crval: ...@@ -587,209 +587,38 @@ xscale_crval:
__INITDATA __INITDATA
/* @ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
* Purpose : Function pointers used to access above functions - all calls define_processor_functions xscale, dabort=v5t_early_abort, pabort=legacy_pabort, suspend=1
* come through these
*/
.type xscale_processor_functions, #object
ENTRY(xscale_processor_functions)
.word v5t_early_abort
.word legacy_pabort
.word cpu_xscale_proc_init
.word cpu_xscale_proc_fin
.word cpu_xscale_reset
.word cpu_xscale_do_idle
.word cpu_xscale_dcache_clean_area
.word cpu_xscale_switch_mm
.word cpu_xscale_set_pte_ext
.word cpu_xscale_suspend_size
.word cpu_xscale_do_suspend
.word cpu_xscale_do_resume
.size xscale_processor_functions, . - xscale_processor_functions
.section ".rodata" .section ".rodata"
.type cpu_arch_name, #object string cpu_arch_name, "armv5te"
cpu_arch_name: string cpu_elf_name, "v5"
.asciz "armv5te"
.size cpu_arch_name, . - cpu_arch_name string cpu_80200_A0_A1_name, "XScale-80200 A0/A1"
string cpu_80200_name, "XScale-80200"
.type cpu_elf_name, #object string cpu_80219_name, "XScale-80219"
cpu_elf_name: string cpu_8032x_name, "XScale-IOP8032x Family"
.asciz "v5" string cpu_8033x_name, "XScale-IOP8033x Family"
.size cpu_elf_name, . - cpu_elf_name string cpu_pxa250_name, "XScale-PXA250"
string cpu_pxa210_name, "XScale-PXA210"
.type cpu_80200_A0_A1_name, #object string cpu_ixp42x_name, "XScale-IXP42x Family"
cpu_80200_A0_A1_name: string cpu_ixp43x_name, "XScale-IXP43x Family"
.asciz "XScale-80200 A0/A1" string cpu_ixp46x_name, "XScale-IXP46x Family"
.size cpu_80200_A0_A1_name, . - cpu_80200_A0_A1_name string cpu_ixp2400_name, "XScale-IXP2400"
string cpu_ixp2800_name, "XScale-IXP2800"
.type cpu_80200_name, #object string cpu_pxa255_name, "XScale-PXA255"
cpu_80200_name: string cpu_pxa270_name, "XScale-PXA270"
.asciz "XScale-80200"
.size cpu_80200_name, . - cpu_80200_name
.type cpu_80219_name, #object
cpu_80219_name:
.asciz "XScale-80219"
.size cpu_80219_name, . - cpu_80219_name
.type cpu_8032x_name, #object
cpu_8032x_name:
.asciz "XScale-IOP8032x Family"
.size cpu_8032x_name, . - cpu_8032x_name
.type cpu_8033x_name, #object
cpu_8033x_name:
.asciz "XScale-IOP8033x Family"
.size cpu_8033x_name, . - cpu_8033x_name
.type cpu_pxa250_name, #object
cpu_pxa250_name:
.asciz "XScale-PXA250"
.size cpu_pxa250_name, . - cpu_pxa250_name
.type cpu_pxa210_name, #object
cpu_pxa210_name:
.asciz "XScale-PXA210"
.size cpu_pxa210_name, . - cpu_pxa210_name
.type cpu_ixp42x_name, #object
cpu_ixp42x_name:
.asciz "XScale-IXP42x Family"
.size cpu_ixp42x_name, . - cpu_ixp42x_name
.type cpu_ixp43x_name, #object
cpu_ixp43x_name:
.asciz "XScale-IXP43x Family"
.size cpu_ixp43x_name, . - cpu_ixp43x_name
.type cpu_ixp46x_name, #object
cpu_ixp46x_name:
.asciz "XScale-IXP46x Family"
.size cpu_ixp46x_name, . - cpu_ixp46x_name
.type cpu_ixp2400_name, #object
cpu_ixp2400_name:
.asciz "XScale-IXP2400"
.size cpu_ixp2400_name, . - cpu_ixp2400_name
.type cpu_ixp2800_name, #object
cpu_ixp2800_name:
.asciz "XScale-IXP2800"
.size cpu_ixp2800_name, . - cpu_ixp2800_name
.type cpu_pxa255_name, #object
cpu_pxa255_name:
.asciz "XScale-PXA255"
.size cpu_pxa255_name, . - cpu_pxa255_name
.type cpu_pxa270_name, #object
cpu_pxa270_name:
.asciz "XScale-PXA270"
.size cpu_pxa270_name, . - cpu_pxa270_name
.align .align
.section ".proc.info.init", #alloc, #execinstr .section ".proc.info.init", #alloc, #execinstr
.type __80200_A0_A1_proc_info,#object .macro xscale_proc_info name:req, cpu_val:req, cpu_mask:req, cpu_name:req, cache
__80200_A0_A1_proc_info: .type __\name\()_proc_info,#object
.long 0x69052000 __\name\()_proc_info:
.long 0xfffffffe .long \cpu_val
.long PMD_TYPE_SECT | \ .long \cpu_mask
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_80200_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_80200_A0_A1_cache_fns
.size __80200_A0_A1_proc_info, . - __80200_A0_A1_proc_info
.type __80200_proc_info,#object
__80200_proc_info:
.long 0x69052000
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_80200_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __80200_proc_info, . - __80200_proc_info
.type __80219_proc_info,#object
__80219_proc_info:
.long 0x69052e20
.long 0xffffffe0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_80219_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __80219_proc_info, . - __80219_proc_info
.type __8032x_proc_info,#object
__8032x_proc_info:
.long 0x69052420
.long 0xfffff7e0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_8032x_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __8032x_proc_info, . - __8032x_proc_info
.type __8033x_proc_info,#object
__8033x_proc_info:
.long 0x69054010
.long 0xfffffd30
.long PMD_TYPE_SECT | \ .long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \ PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \ PMD_SECT_CACHEABLE | \
...@@ -802,217 +631,30 @@ __8033x_proc_info: ...@@ -802,217 +631,30 @@ __8033x_proc_info:
.long cpu_arch_name .long cpu_arch_name
.long cpu_elf_name .long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_8033x_name .long \cpu_name
.long xscale_processor_functions .long xscale_processor_functions
.long v4wbi_tlb_fns .long v4wbi_tlb_fns
.long xscale_mc_user_fns .long xscale_mc_user_fns
.ifb \cache
.long xscale_cache_fns .long xscale_cache_fns
.size __8033x_proc_info, . - __8033x_proc_info .else
.long \cache
.type __pxa250_proc_info,#object .endif
__pxa250_proc_info: .size __\name\()_proc_info, . - __\name\()_proc_info
.long 0x69052100 .endm
.long 0xfffff7f0
.long PMD_TYPE_SECT | \ xscale_proc_info 80200_A0_A1, 0x69052000, 0xfffffffe, cpu_80200_name, \
PMD_SECT_BUFFERABLE | \ cache=xscale_80200_A0_A1_cache_fns
PMD_SECT_CACHEABLE | \ xscale_proc_info 80200, 0x69052000, 0xfffffff0, cpu_80200_name
PMD_SECT_AP_WRITE | \ xscale_proc_info 80219, 0x69052e20, 0xffffffe0, cpu_80219_name
PMD_SECT_AP_READ xscale_proc_info 8032x, 0x69052420, 0xfffff7e0, cpu_8032x_name
.long PMD_TYPE_SECT | \ xscale_proc_info 8033x, 0x69054010, 0xfffffd30, cpu_8033x_name
PMD_SECT_AP_WRITE | \ xscale_proc_info pxa250, 0x69052100, 0xfffff7f0, cpu_pxa250_name
PMD_SECT_AP_READ xscale_proc_info pxa210, 0x69052120, 0xfffff3f0, cpu_pxa210_name
b __xscale_setup xscale_proc_info ixp2400, 0x69054190, 0xfffffff0, cpu_ixp2400_name
.long cpu_arch_name xscale_proc_info ixp2800, 0x690541a0, 0xfffffff0, cpu_ixp2800_name
.long cpu_elf_name xscale_proc_info ixp42x, 0x690541c0, 0xffffffc0, cpu_ixp42x_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP xscale_proc_info ixp43x, 0x69054040, 0xfffffff0, cpu_ixp43x_name
.long cpu_pxa250_name xscale_proc_info ixp46x, 0x69054200, 0xffffff00, cpu_ixp46x_name
.long xscale_processor_functions xscale_proc_info pxa255, 0x69052d00, 0xfffffff0, cpu_pxa255_name
.long v4wbi_tlb_fns xscale_proc_info pxa270, 0x69054110, 0xfffffff0, cpu_pxa270_name
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __pxa250_proc_info, . - __pxa250_proc_info
.type __pxa210_proc_info,#object
__pxa210_proc_info:
.long 0x69052120
.long 0xfffff3f0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_pxa210_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __pxa210_proc_info, . - __pxa210_proc_info
.type __ixp2400_proc_info, #object
__ixp2400_proc_info:
.long 0x69054190
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_ixp2400_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __ixp2400_proc_info, . - __ixp2400_proc_info
.type __ixp2800_proc_info, #object
__ixp2800_proc_info:
.long 0x690541a0
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_ixp2800_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __ixp2800_proc_info, . - __ixp2800_proc_info
.type __ixp42x_proc_info, #object
__ixp42x_proc_info:
.long 0x690541c0
.long 0xffffffc0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_ixp42x_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __ixp42x_proc_info, . - __ixp42x_proc_info
.type __ixp43x_proc_info, #object
__ixp43x_proc_info:
.long 0x69054040
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_ixp43x_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __ixp43x_proc_info, . - __ixp43x_proc_info
.type __ixp46x_proc_info, #object
__ixp46x_proc_info:
.long 0x69054200
.long 0xffffff00
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_ixp46x_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __ixp46x_proc_info, . - __ixp46x_proc_info
.type __pxa255_proc_info,#object
__pxa255_proc_info:
.long 0x69052d00
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_pxa255_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __pxa255_proc_info, . - __pxa255_proc_info
.type __pxa270_proc_info,#object
__pxa270_proc_info:
.long 0x69054110
.long 0xfffffff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
.long cpu_pxa270_name
.long xscale_processor_functions
.long v4wbi_tlb_fns
.long xscale_mc_user_fns
.long xscale_cache_fns
.size __pxa270_proc_info, . - __pxa270_proc_info
...@@ -65,9 +65,5 @@ ENTRY(fa_flush_kern_tlb_range) ...@@ -65,9 +65,5 @@ ENTRY(fa_flush_kern_tlb_range)
__INITDATA __INITDATA
.type fa_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(fa_tlb_fns) define_tlb_functions fa, fa_tlb_flags
.long fa_flush_user_tlb_range
.long fa_flush_kern_tlb_range
.long fa_tlb_flags
.size fa_tlb_fns, . - fa_tlb_fns
...@@ -44,9 +44,5 @@ ENTRY(v3_flush_kern_tlb_range) ...@@ -44,9 +44,5 @@ ENTRY(v3_flush_kern_tlb_range)
__INITDATA __INITDATA
.type v3_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(v3_tlb_fns) define_tlb_functions v3, v3_tlb_flags
.long v3_flush_user_tlb_range
.long v3_flush_kern_tlb_range
.long v3_tlb_flags
.size v3_tlb_fns, . - v3_tlb_fns
...@@ -57,9 +57,5 @@ ENTRY(v4_flush_user_tlb_range) ...@@ -57,9 +57,5 @@ ENTRY(v4_flush_user_tlb_range)
__INITDATA __INITDATA
.type v4_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(v4_tlb_fns) define_tlb_functions v4, v4_tlb_flags
.long v4_flush_user_tlb_range
.long v4_flush_kern_tlb_range
.long v4_tlb_flags
.size v4_tlb_fns, . - v4_tlb_fns
...@@ -69,9 +69,5 @@ ENTRY(v4wb_flush_kern_tlb_range) ...@@ -69,9 +69,5 @@ ENTRY(v4wb_flush_kern_tlb_range)
__INITDATA __INITDATA
.type v4wb_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(v4wb_tlb_fns) define_tlb_functions v4wb, v4wb_tlb_flags
.long v4wb_flush_user_tlb_range
.long v4wb_flush_kern_tlb_range
.long v4wb_tlb_flags
.size v4wb_tlb_fns, . - v4wb_tlb_fns
...@@ -60,9 +60,5 @@ ENTRY(v4wbi_flush_kern_tlb_range) ...@@ -60,9 +60,5 @@ ENTRY(v4wbi_flush_kern_tlb_range)
__INITDATA __INITDATA
.type v4wbi_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(v4wbi_tlb_fns) define_tlb_functions v4wbi, v4wbi_tlb_flags
.long v4wbi_flush_user_tlb_range
.long v4wbi_flush_kern_tlb_range
.long v4wbi_tlb_flags
.size v4wbi_tlb_fns, . - v4wbi_tlb_fns
...@@ -88,9 +88,5 @@ ENTRY(v6wbi_flush_kern_tlb_range) ...@@ -88,9 +88,5 @@ ENTRY(v6wbi_flush_kern_tlb_range)
__INIT __INIT
.type v6wbi_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(v6wbi_tlb_fns) define_tlb_functions v6wbi, v6wbi_tlb_flags
.long v6wbi_flush_user_tlb_range
.long v6wbi_flush_kern_tlb_range
.long v6wbi_tlb_flags
.size v6wbi_tlb_fns, . - v6wbi_tlb_fns
...@@ -79,10 +79,5 @@ ENDPROC(v7wbi_flush_kern_tlb_range) ...@@ -79,10 +79,5 @@ ENDPROC(v7wbi_flush_kern_tlb_range)
__INIT __INIT
.type v7wbi_tlb_fns, #object /* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
ENTRY(v7wbi_tlb_fns) define_tlb_functions v7wbi, v7wbi_tlb_flags_up, flags_smp=v7wbi_tlb_flags_smp
.long v7wbi_flush_user_tlb_range
.long v7wbi_flush_kern_tlb_range
ALT_SMP(.long v7wbi_tlb_flags_smp)
ALT_UP(.long v7wbi_tlb_flags_up)
.size v7wbi_tlb_fns, . - v7wbi_tlb_fns
...@@ -597,7 +597,6 @@ static int __init vfp_init(void) ...@@ -597,7 +597,6 @@ static int __init vfp_init(void)
elf_hwcap |= HWCAP_VFPv3D16; elf_hwcap |= HWCAP_VFPv3D16;
} }
#endif #endif
#ifdef CONFIG_NEON
/* /*
* Check for the presence of the Advanced SIMD * Check for the presence of the Advanced SIMD
* load/store instructions, integer and single * load/store instructions, integer and single
...@@ -605,10 +604,13 @@ static int __init vfp_init(void) ...@@ -605,10 +604,13 @@ static int __init vfp_init(void)
* for NEON if the hardware has the MVFR registers. * for NEON if the hardware has the MVFR registers.
*/ */
if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) { if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) {
#ifdef CONFIG_NEON
if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100) if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100)
elf_hwcap |= HWCAP_NEON; elf_hwcap |= HWCAP_NEON;
}
#endif #endif
if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000)
elf_hwcap |= HWCAP_VFPv4;
}
} }
return 0; return 0;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册