summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/.gitignore1
-rw-r--r--arch/arm/kernel/Makefile49
-rw-r--r--arch/arm/kernel/Makefile.orig48
-rw-r--r--arch/arm/kernel/armksyms.c188
-rw-r--r--arch/arm/kernel/arthur.c94
-rw-r--r--arch/arm/kernel/asm-offsets.c116
-rw-r--r--arch/arm/kernel/atags.c83
-rw-r--r--arch/arm/kernel/atags.h5
-rw-r--r--arch/arm/kernel/bios32.c723
-rw-r--r--arch/arm/kernel/calls.S379
-rw-r--r--arch/arm/kernel/compat.c226
-rw-r--r--arch/arm/kernel/compat.h13
-rw-r--r--arch/arm/kernel/crunch-bits.S305
-rw-r--r--arch/arm/kernel/crunch.c83
-rw-r--r--arch/arm/kernel/debug.S142
-rw-r--r--arch/arm/kernel/dma-isa.c221
-rw-r--r--arch/arm/kernel/dma.c251
-rw-r--r--arch/arm/kernel/early_printk.c57
-rw-r--r--arch/arm/kernel/ecard.c1241
-rw-r--r--arch/arm/kernel/ecard.h69
-rw-r--r--arch/arm/kernel/elf.c83
-rw-r--r--arch/arm/kernel/entry-armv.S1204
-rw-r--r--arch/arm/kernel/entry-common.S476
-rw-r--r--arch/arm/kernel/entry-header.S66
-rw-r--r--arch/arm/kernel/fiq.c181
-rw-r--r--arch/arm/kernel/ftrace.c103
-rw-r--r--arch/arm/kernel/head-common.S260
-rw-r--r--arch/arm/kernel/head-nommu.S90
-rw-r--r--arch/arm/kernel/head.S330
-rw-r--r--arch/arm/kernel/init_task.c43
-rw-r--r--arch/arm/kernel/io.c50
-rw-r--r--arch/arm/kernel/irq.c209
-rw-r--r--arch/arm/kernel/isa.c74
-rw-r--r--arch/arm/kernel/iwmmxt.S317
-rw-r--r--arch/arm/kernel/kgdb.c199
-rw-r--r--arch/arm/kernel/kprobes-decode.c1529
-rw-r--r--arch/arm/kernel/kprobes.c453
-rw-r--r--arch/arm/kernel/machine_kexec.c80
-rw-r--r--arch/arm/kernel/module.c163
-rw-r--r--arch/arm/kernel/process.c394
-rw-r--r--arch/arm/kernel/ptrace.c820
-rw-r--r--arch/arm/kernel/ptrace.h51
-rw-r--r--arch/arm/kernel/relocate_kernel.S79
-rw-r--r--arch/arm/kernel/setup.c861
-rw-r--r--arch/arm/kernel/signal.c706
-rw-r--r--arch/arm/kernel/signal.h12
-rw-r--r--arch/arm/kernel/smp.c659
-rw-r--r--arch/arm/kernel/stacktrace.c96
-rw-r--r--arch/arm/kernel/stacktrace.h9
-rw-r--r--arch/arm/kernel/sys_arm.c312
-rw-r--r--arch/arm/kernel/sys_oabi-compat.c464
-rw-r--r--arch/arm/kernel/thumbee.c81
-rw-r--r--arch/arm/kernel/time.c391
-rw-r--r--arch/arm/kernel/traps.c734
-rw-r--r--arch/arm/kernel/vmlinux.lds.S192
-rw-r--r--arch/arm/kernel/xscale-cp0.c179
56 files changed, 16244 insertions, 0 deletions
diff --git a/arch/arm/kernel/.gitignore b/arch/arm/kernel/.gitignore
new file mode 100644
index 0000000..c5f676c
--- /dev/null
+++ b/arch/arm/kernel/.gitignore
@@ -0,0 +1 @@
+vmlinux.lds
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
new file mode 100644
index 0000000..3f69b5e
--- /dev/null
+++ b/arch/arm/kernel/Makefile
@@ -0,0 +1,49 @@
+#
+# Makefile for the linux kernel.
+#
+
+AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+
+ifdef CONFIG_DYNAMIC_FTRACE
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+# Object file lists.
+
+obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \
+ process.o ptrace.o setup.o signal.o \
+ sys_arm.o stacktrace.o time.o traps.o
+
+obj-$(CONFIG_ISA_DMA_API) += dma.o
+obj-$(CONFIG_ARCH_ACORN) += ecard.o
+obj-$(CONFIG_FIQ) += fiq.o
+obj-$(CONFIG_MODULES) += armksyms.o module.o
+obj-$(CONFIG_ARTHUR) += arthur.o
+obj-$(CONFIG_ISA_DMA) += dma-isa.o
+obj-$(CONFIG_PCI) += bios32.o isa.o
+obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
+obj-$(CONFIG_ATAGS_PROC) += atags.o
+obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
+obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
+obj-$(CONFIG_KGDB) += kgdb.o
+
+obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
+AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
+
+obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
+obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
+obj-$(CONFIG_IWMMXT) += iwmmxt.o
+AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
+
+ifneq ($(CONFIG_ARCH_EBSA110),y)
+ obj-y += io.o
+endif
+
+head-y := head$(MMUEXT).o
+obj-$(CONFIG_DEBUG_LL) += debug.o
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+
+extra-y := $(head-y) init_task.o vmlinux.lds
diff --git a/arch/arm/kernel/Makefile.orig b/arch/arm/kernel/Makefile.orig
new file mode 100644
index 0000000..4305345
--- /dev/null
+++ b/arch/arm/kernel/Makefile.orig
@@ -0,0 +1,48 @@
+#
+# Makefile for the linux kernel.
+#
+
+AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+
+ifdef CONFIG_DYNAMIC_FTRACE
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+# Object file lists.
+
+obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \
+ process.o ptrace.o setup.o signal.o \
+ sys_arm.o stacktrace.o time.o traps.o
+
+obj-$(CONFIG_ISA_DMA_API) += dma.o
+obj-$(CONFIG_ARCH_ACORN) += ecard.o
+obj-$(CONFIG_FIQ) += fiq.o
+obj-$(CONFIG_MODULES) += armksyms.o module.o
+obj-$(CONFIG_ARTHUR) += arthur.o
+obj-$(CONFIG_ISA_DMA) += dma-isa.o
+obj-$(CONFIG_PCI) += bios32.o isa.o
+obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
+obj-$(CONFIG_ATAGS_PROC) += atags.o
+obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
+obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
+obj-$(CONFIG_KGDB) += kgdb.o
+
+obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
+AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
+
+obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
+obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
+obj-$(CONFIG_IWMMXT) += iwmmxt.o
+AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
+
+ifneq ($(CONFIG_ARCH_EBSA110),y)
+ obj-y += io.o
+endif
+
+head-y := head$(MMUEXT).o
+obj-$(CONFIG_DEBUG_LL) += debug.o
+
+extra-y := $(head-y) init_task.o vmlinux.lds
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
new file mode 100644
index 0000000..23af3c9
--- /dev/null
+++ b/arch/arm/kernel/armksyms.c
@@ -0,0 +1,188 @@
+/*
+ * linux/arch/arm/kernel/armksyms.c
+ *
+ * Copyright (C) 2000 Russell King
+ *
+ * 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/module.h>
+#include <linux/string.h>
+#include <linux/cryptohash.h>
+#include <linux/delay.h>
+#include <linux/in6.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/checksum.h>
+#include <asm/system.h>
+#include <asm/ftrace.h>
+
+/*
+ * libgcc functions - functions that are used internally by the
+ * compiler... (prototypes are not correct though, but that
+ * doesn't really matter since they're not versioned).
+ */
+extern void __ashldi3(void);
+extern void __ashrdi3(void);
+extern void __divsi3(void);
+extern void __lshrdi3(void);
+extern void __modsi3(void);
+extern void __muldi3(void);
+extern void __ucmpdi2(void);
+extern void __udivsi3(void);
+extern void __umodsi3(void);
+extern void __do_div64(void);
+
+extern void __aeabi_idiv(void);
+extern void __aeabi_idivmod(void);
+extern void __aeabi_lasr(void);
+extern void __aeabi_llsl(void);
+extern void __aeabi_llsr(void);
+extern void __aeabi_lmul(void);
+extern void __aeabi_uidiv(void);
+extern void __aeabi_uidivmod(void);
+extern void __aeabi_ulcmp(void);
+
+extern void fpundefinstr(void);
+extern void fp_enter(void);
+
+/*
+ * This has a special calling convention; it doesn't
+ * modify any of the usual registers, except for LR.
+ */
+#define EXPORT_CRC_ALIAS(sym) __CRC_SYMBOL(sym, "")
+
+#define EXPORT_SYMBOL_ALIAS(sym,orig) \
+ EXPORT_CRC_ALIAS(sym) \
+ static const struct kernel_symbol __ksymtab_##sym \
+ __used __attribute__((section("__ksymtab"))) = \
+ { (unsigned long)&orig, #sym };
+
+/*
+ * floating point math emulator support.
+ * These symbols will never change their calling convention...
+ */
+EXPORT_SYMBOL_ALIAS(kern_fp_enter,fp_enter);
+EXPORT_SYMBOL_ALIAS(fp_printk,printk);
+EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig);
+
+EXPORT_SYMBOL(__backtrace);
+
+ /* platform dependent support */
+EXPORT_SYMBOL(__udelay);
+EXPORT_SYMBOL(__const_udelay);
+
+ /* networking */
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(csum_partial_copy_from_user);
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
+EXPORT_SYMBOL(__csum_ipv6_magic);
+
+ /* io */
+#ifndef __raw_readsb
+EXPORT_SYMBOL(__raw_readsb);
+#endif
+#ifndef __raw_readsw
+EXPORT_SYMBOL(__raw_readsw);
+#endif
+#ifndef __raw_readsl
+EXPORT_SYMBOL(__raw_readsl);
+#endif
+#ifndef __raw_writesb
+EXPORT_SYMBOL(__raw_writesb);
+#endif
+#ifndef __raw_writesw
+EXPORT_SYMBOL(__raw_writesw);
+#endif
+#ifndef __raw_writesl
+EXPORT_SYMBOL(__raw_writesl);
+#endif
+
+ /* string / mem functions */
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(__memzero);
+
+ /* user mem (segment) */
+EXPORT_SYMBOL(__strnlen_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+
+#ifdef CONFIG_MMU
+EXPORT_SYMBOL(copy_page);
+
+EXPORT_SYMBOL(__copy_from_user);
+EXPORT_SYMBOL(__copy_to_user);
+EXPORT_SYMBOL(__clear_user);
+
+EXPORT_SYMBOL(__get_user_1);
+EXPORT_SYMBOL(__get_user_2);
+EXPORT_SYMBOL(__get_user_4);
+
+EXPORT_SYMBOL(__put_user_1);
+EXPORT_SYMBOL(__put_user_2);
+EXPORT_SYMBOL(__put_user_4);
+EXPORT_SYMBOL(__put_user_8);
+#endif
+
+ /* crypto hash */
+EXPORT_SYMBOL(sha_transform);
+
+ /* gcc lib functions */
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__divsi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__modsi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__ucmpdi2);
+EXPORT_SYMBOL(__udivsi3);
+EXPORT_SYMBOL(__umodsi3);
+EXPORT_SYMBOL(__do_div64);
+
+#ifdef CONFIG_AEABI
+EXPORT_SYMBOL(__aeabi_idiv);
+EXPORT_SYMBOL(__aeabi_idivmod);
+EXPORT_SYMBOL(__aeabi_lasr);
+EXPORT_SYMBOL(__aeabi_llsl);
+EXPORT_SYMBOL(__aeabi_llsr);
+EXPORT_SYMBOL(__aeabi_lmul);
+EXPORT_SYMBOL(__aeabi_uidiv);
+EXPORT_SYMBOL(__aeabi_uidivmod);
+EXPORT_SYMBOL(__aeabi_ulcmp);
+#endif
+
+ /* bitops */
+EXPORT_SYMBOL(_set_bit_le);
+EXPORT_SYMBOL(_test_and_set_bit_le);
+EXPORT_SYMBOL(_clear_bit_le);
+EXPORT_SYMBOL(_test_and_clear_bit_le);
+EXPORT_SYMBOL(_change_bit_le);
+EXPORT_SYMBOL(_test_and_change_bit_le);
+EXPORT_SYMBOL(_find_first_zero_bit_le);
+EXPORT_SYMBOL(_find_next_zero_bit_le);
+EXPORT_SYMBOL(_find_first_bit_le);
+EXPORT_SYMBOL(_find_next_bit_le);
+
+#ifdef __ARMEB__
+EXPORT_SYMBOL(_set_bit_be);
+EXPORT_SYMBOL(_test_and_set_bit_be);
+EXPORT_SYMBOL(_clear_bit_be);
+EXPORT_SYMBOL(_test_and_clear_bit_be);
+EXPORT_SYMBOL(_change_bit_be);
+EXPORT_SYMBOL(_test_and_change_bit_be);
+EXPORT_SYMBOL(_find_first_zero_bit_be);
+EXPORT_SYMBOL(_find_next_zero_bit_be);
+EXPORT_SYMBOL(_find_first_bit_be);
+EXPORT_SYMBOL(_find_next_bit_be);
+#endif
+
+#ifdef CONFIG_FUNCTION_TRACER
+EXPORT_SYMBOL(mcount);
+#endif
diff --git a/arch/arm/kernel/arthur.c b/arch/arm/kernel/arthur.c
new file mode 100644
index 0000000..321c529
--- /dev/null
+++ b/arch/arm/kernel/arthur.c
@@ -0,0 +1,94 @@
+/*
+ * linux/arch/arm/kernel/arthur.c
+ *
+ * Copyright (C) 1998, 1999, 2000, 2001 Philip Blundell
+ *
+ * Arthur personality
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/personality.h>
+#include <linux/stddef.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+#include <asm/ptrace.h>
+
+/* Arthur doesn't have many signals, and a lot of those that it does
+ have don't map easily to any Linux equivalent. Never mind. */
+
+#define ARTHUR_SIGABRT 1
+#define ARTHUR_SIGFPE 2
+#define ARTHUR_SIGILL 3
+#define ARTHUR_SIGINT 4
+#define ARTHUR_SIGSEGV 5
+#define ARTHUR_SIGTERM 6
+#define ARTHUR_SIGSTAK 7
+#define ARTHUR_SIGUSR1 8
+#define ARTHUR_SIGUSR2 9
+#define ARTHUR_SIGOSERROR 10
+
+static unsigned long arthur_to_linux_signals[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31
+};
+
+static unsigned long linux_to_arthur_signals[32] = {
+ 0, -1, ARTHUR_SIGINT, -1,
+ ARTHUR_SIGILL, 5, ARTHUR_SIGABRT, 7,
+ ARTHUR_SIGFPE, 9, ARTHUR_SIGUSR1, ARTHUR_SIGSEGV,
+ ARTHUR_SIGUSR2, 13, 14, ARTHUR_SIGTERM,
+ 16, 17, 18, 19,
+ 20, 21, 22, 23,
+ 24, 25, 26, 27,
+ 28, 29, 30, 31
+};
+
+static void arthur_lcall7(int nr, struct pt_regs *regs)
+{
+ struct siginfo info;
+ info.si_signo = SIGSWI;
+ info.si_errno = nr;
+ /* Bounce it to the emulator */
+ send_sig_info(SIGSWI, &info, current);
+}
+
+static struct exec_domain arthur_exec_domain = {
+ .name = "Arthur",
+ .handler = arthur_lcall7,
+ .pers_low = PER_RISCOS,
+ .pers_high = PER_RISCOS,
+ .signal_map = arthur_to_linux_signals,
+ .signal_invmap = linux_to_arthur_signals,
+ .module = THIS_MODULE,
+};
+
+/*
+ * We could do with some locking to stop Arthur being removed while
+ * processes are using it.
+ */
+
+static int __init arthur_init(void)
+{
+ return register_exec_domain(&arthur_exec_domain);
+}
+
+static void __exit arthur_exit(void)
+{
+ unregister_exec_domain(&arthur_exec_domain);
+}
+
+module_init(arthur_init);
+module_exit(arthur_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
new file mode 100644
index 0000000..4a88125
--- /dev/null
+++ b/arch/arm/kernel/asm-offsets.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 1995-2003 Russell King
+ * 2001-2002 Keith Owens
+ *
+ * Generate definitions needed by assembly language modules.
+ * This code generates raw asm output which is post-processed to extract
+ * and format the required data.
+ *
+ * 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/sched.h>
+#include <linux/mm.h>
+#include <asm/mach/arch.h>
+#include <asm/thread_info.h>
+#include <asm/memory.h>
+#include <asm/procinfo.h>
+#include <linux/kbuild.h>
+
+/*
+ * Make sure that the compiler and target are compatible.
+ */
+#if defined(__APCS_26__)
+#error Sorry, your compiler targets APCS-26 but this kernel requires APCS-32
+#endif
+/*
+ * GCC 3.0, 3.1: general bad code generation.
+ * GCC 3.2.0: incorrect function argument offset calculation.
+ * GCC 3.2.x: miscompiles NEW_AUX_ENT in fs/binfmt_elf.c
+ * (http://gcc.gnu.org/PR8896) and incorrect structure
+ * initialisation in fs/jffs2/erase.c
+ */
+#if (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
+#error Your compiler is too buggy; it is known to miscompile kernels.
+#error Known good compilers: 3.3
+#endif
+
+int main(void)
+{
+ DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
+ BLANK();
+ DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
+ DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
+ DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
+ DEFINE(TI_TASK, offsetof(struct thread_info, task));
+ DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
+ DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
+ DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain));
+ DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
+ DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp));
+ DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value));
+ DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
+ DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
+#ifdef CONFIG_ARM_THUMBEE
+ DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state));
+#endif
+#ifdef CONFIG_IWMMXT
+ DEFINE(TI_IWMMXT_STATE, offsetof(struct thread_info, fpstate.iwmmxt));
+#endif
+#ifdef CONFIG_CRUNCH
+ DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate));
+#endif
+ BLANK();
+ DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
+ DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
+ DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
+ DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
+ DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
+ DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
+ DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
+ DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
+ DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
+ DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
+ DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
+ DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
+ DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
+ DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
+ DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
+ DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
+ DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
+ DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
+ DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
+ BLANK();
+#ifdef CONFIG_CPU_HAS_ASID
+ DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id));
+ BLANK();
+#endif
+ DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
+ DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
+ BLANK();
+ DEFINE(VM_EXEC, VM_EXEC);
+ BLANK();
+ DEFINE(PAGE_SZ, PAGE_SIZE);
+ BLANK();
+ DEFINE(SYS_ERROR0, 0x9f0000);
+ BLANK();
+ DEFINE(SIZEOF_MACHINE_DESC, sizeof(struct machine_desc));
+ DEFINE(MACHINFO_TYPE, offsetof(struct machine_desc, nr));
+ DEFINE(MACHINFO_NAME, offsetof(struct machine_desc, name));
+ DEFINE(MACHINFO_PHYSIO, offsetof(struct machine_desc, phys_io));
+ DEFINE(MACHINFO_PGOFFIO, offsetof(struct machine_desc, io_pg_offst));
+ BLANK();
+ DEFINE(PROC_INFO_SZ, sizeof(struct proc_info_list));
+ DEFINE(PROCINFO_INITFUNC, offsetof(struct proc_info_list, __cpu_flush));
+ DEFINE(PROCINFO_MM_MMUFLAGS, offsetof(struct proc_info_list, __cpu_mm_mmu_flags));
+ DEFINE(PROCINFO_IO_MMUFLAGS, offsetof(struct proc_info_list, __cpu_io_mmu_flags));
+ BLANK();
+#ifdef MULTI_DABORT
+ DEFINE(PROCESSOR_DABT_FUNC, offsetof(struct processor, _data_abort));
+#endif
+#ifdef MULTI_PABORT
+ DEFINE(PROCESSOR_PABT_FUNC, offsetof(struct processor, _prefetch_abort));
+#endif
+ return 0;
+}
diff --git a/arch/arm/kernel/atags.c b/arch/arm/kernel/atags.c
new file mode 100644
index 0000000..42a1a14
--- /dev/null
+++ b/arch/arm/kernel/atags.c
@@ -0,0 +1,83 @@
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <asm/setup.h>
+#include <asm/types.h>
+#include <asm/page.h>
+
+struct buffer {
+ size_t size;
+ char data[];
+};
+
+static int
+read_buffer(char* page, char** start, off_t off, int count,
+ int* eof, void* data)
+{
+ struct buffer *buffer = (struct buffer *)data;
+
+ if (off >= buffer->size) {
+ *eof = 1;
+ return 0;
+ }
+
+ count = min((int) (buffer->size - off), count);
+
+ memcpy(page, &buffer->data[off], count);
+
+ return count;
+}
+
+#define BOOT_PARAMS_SIZE 1536
+static char __initdata atags_copy[BOOT_PARAMS_SIZE];
+
+void __init save_atags(const struct tag *tags)
+{
+ memcpy(atags_copy, tags, sizeof(atags_copy));
+}
+
+static int __init init_atags_procfs(void)
+{
+ /*
+ * This cannot go into save_atags() because kmalloc and proc don't work
+ * yet when it is called.
+ */
+ struct proc_dir_entry *tags_entry;
+ struct tag *tag = (struct tag *)atags_copy;
+ struct buffer *b;
+ size_t size;
+
+ if (tag->hdr.tag != ATAG_CORE) {
+ printk(KERN_INFO "No ATAGs?");
+ return -EINVAL;
+ }
+
+ for (; tag->hdr.size; tag = tag_next(tag))
+ ;
+
+ /* include the terminating ATAG_NONE */
+ size = (char *)tag - atags_copy + sizeof(struct tag_header);
+
+ WARN_ON(tag->hdr.tag != ATAG_NONE);
+
+ b = kmalloc(sizeof(*b) + size, GFP_KERNEL);
+ if (!b)
+ goto nomem;
+
+ b->size = size;
+ memcpy(b->data, atags_copy, size);
+
+ tags_entry = create_proc_read_entry("atags", 0400,
+ NULL, read_buffer, b);
+
+ if (!tags_entry)
+ goto nomem;
+
+ return 0;
+
+nomem:
+ kfree(b);
+ printk(KERN_ERR "Exporting ATAGs: not enough memory\n");
+
+ return -ENOMEM;
+}
+arch_initcall(init_atags_procfs);
diff --git a/arch/arm/kernel/atags.h b/arch/arm/kernel/atags.h
new file mode 100644
index 0000000..e5f028d
--- /dev/null
+++ b/arch/arm/kernel/atags.h
@@ -0,0 +1,5 @@
+#ifdef CONFIG_ATAGS_PROC
+extern void save_atags(struct tag *tags);
+#else
+static inline void save_atags(struct tag *tags) { }
+#endif
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
new file mode 100644
index 0000000..17a59b6
--- /dev/null
+++ b/arch/arm/kernel/bios32.c
@@ -0,0 +1,723 @@
+/*
+ * linux/arch/arm/kernel/bios32.c
+ *
+ * PCI bios-type initialisation for PCI machines
+ *
+ * Bits taken from various places.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/pci.h>
+
+static int debug_pci;
+static int use_firmware;
+
+/*
+ * We can't use pci_find_device() here since we are
+ * called from interrupt context.
+ */
+static void pcibios_bus_report_status(struct pci_bus *bus, u_int status_mask, int warn)
+{
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ u16 status;
+
+ /*
+ * ignore host bridge - we handle
+ * that separately
+ */
+ if (dev->bus->number == 0 && dev->devfn == 0)
+ continue;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+ if (status == 0xffff)
+ continue;
+
+ if ((status & status_mask) == 0)
+ continue;
+
+ /* clear the status errors */
+ pci_write_config_word(dev, PCI_STATUS, status & status_mask);
+
+ if (warn)
+ printk("(%s: %04X) ", pci_name(dev), status);
+ }
+
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ if (dev->subordinate)
+ pcibios_bus_report_status(dev->subordinate, status_mask, warn);
+}
+
+void pcibios_report_status(u_int status_mask, int warn)
+{
+ struct list_head *l;
+
+ list_for_each(l, &pci_root_buses) {
+ struct pci_bus *bus = pci_bus_b(l);
+
+ pcibios_bus_report_status(bus, status_mask, warn);
+ }
+}
+
+/*
+ * We don't use this to fix the device, but initialisation of it.
+ * It's not the correct use for this, but it works.
+ * Note that the arbiter/ISA bridge appears to be buggy, specifically in
+ * the following area:
+ * 1. park on CPU
+ * 2. ISA bridge ping-pong
+ * 3. ISA bridge master handling of target RETRY
+ *
+ * Bug 3 is responsible for the sound DMA grinding to a halt. We now
+ * live with bug 2.
+ */
+static void __devinit pci_fixup_83c553(struct pci_dev *dev)
+{
+ /*
+ * Set memory region to start at address 0, and enable IO
+ */
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_IO);
+
+ dev->resource[0].end -= dev->resource[0].start;
+ dev->resource[0].start = 0;
+
+ /*
+ * All memory requests from ISA to be channelled to PCI
+ */
+ pci_write_config_byte(dev, 0x48, 0xff);
+
+ /*
+ * Enable ping-pong on bus master to ISA bridge transactions.
+ * This improves the sound DMA substantially. The fixed
+ * priority arbiter also helps (see below).
+ */
+ pci_write_config_byte(dev, 0x42, 0x01);
+
+ /*
+ * Enable PCI retry
+ */
+ pci_write_config_byte(dev, 0x40, 0x22);
+
+ /*
+ * We used to set the arbiter to "park on last master" (bit
+ * 1 set), but unfortunately the CyberPro does not park the
+ * bus. We must therefore park on CPU. Unfortunately, this
+ * may trigger yet another bug in the 553.
+ */
+ pci_write_config_byte(dev, 0x83, 0x02);
+
+ /*
+ * Make the ISA DMA request lowest priority, and disable
+ * rotating priorities completely.
+ */
+ pci_write_config_byte(dev, 0x80, 0x11);
+ pci_write_config_byte(dev, 0x81, 0x00);
+
+ /*
+ * Route INTA input to IRQ 11, and set IRQ11 to be level
+ * sensitive.
+ */
+ pci_write_config_word(dev, 0x44, 0xb000);
+ outb(0x08, 0x4d1);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_83C553, pci_fixup_83c553);
+
+static void __devinit pci_fixup_unassign(struct pci_dev *dev)
+{
+ dev->resource[0].end -= dev->resource[0].start;
+ dev->resource[0].start = 0;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_89C940F, pci_fixup_unassign);
+
+/*
+ * Prevent the PCI layer from seeing the resources allocated to this device
+ * if it is the host bridge by marking it as such. These resources are of
+ * no consequence to the PCI layer (they are handled elsewhere).
+ */
+static void __devinit pci_fixup_dec21285(struct pci_dev *dev)
+{
+ int i;
+
+ if (dev->devfn == 0) {
+ dev->class &= 0xff;
+ dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ dev->resource[i].start = 0;
+ dev->resource[i].end = 0;
+ dev->resource[i].flags = 0;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, pci_fixup_dec21285);
+
+/*
+ * Same as above. The PrPMC800 carrier board for the PrPMC1100
+ * card maps the host-bridge @ 00:01:00 for some reason and it
+ * ends up getting scanned. Note that we only want to do this
+ * fixup when we find the IXP4xx on a PrPMC system, which is why
+ * we check the machine type. We could be running on a board
+ * with an IXP4xx target device and we don't want to kill the
+ * resources in that case.
+ */
+static void __devinit pci_fixup_prpmc1100(struct pci_dev *dev)
+{
+ int i;
+
+ if (machine_is_prpmc1100()) {
+ dev->class &= 0xff;
+ dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ dev->resource[i].start = 0;
+ dev->resource[i].end = 0;
+ dev->resource[i].flags = 0;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IXP4XX, pci_fixup_prpmc1100);
+
+/*
+ * PCI IDE controllers use non-standard I/O port decoding, respect it.
+ */
+static void __devinit pci_fixup_ide_bases(struct pci_dev *dev)
+{
+ struct resource *r;
+ int i;
+
+ if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
+ return;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ r = dev->resource + i;
+ if ((r->start & ~0x80) == 0x374) {
+ r->start |= 2;
+ r->end = r->start;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
+
+/*
+ * Put the DEC21142 to sleep
+ */
+static void __devinit pci_fixup_dec21142(struct pci_dev *dev)
+{
+ pci_write_config_dword(dev, 0x40, 0x80000000);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, pci_fixup_dec21142);
+
+/*
+ * The CY82C693 needs some rather major fixups to ensure that it does
+ * the right thing. Idea from the Alpha people, with a few additions.
+ *
+ * We ensure that the IDE base registers are set to 1f0/3f4 for the
+ * primary bus, and 170/374 for the secondary bus. Also, hide them
+ * from the PCI subsystem view as well so we won't try to perform
+ * our own auto-configuration on them.
+ *
+ * In addition, we ensure that the PCI IDE interrupts are routed to
+ * IRQ 14 and IRQ 15 respectively.
+ *
+ * The above gets us to a point where the IDE on this device is
+ * functional. However, The CY82C693U _does not work_ in bus
+ * master mode without locking the PCI bus solid.
+ */
+static void __devinit pci_fixup_cy82c693(struct pci_dev *dev)
+{
+ if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
+ u32 base0, base1;
+
+ if (dev->class & 0x80) { /* primary */
+ base0 = 0x1f0;
+ base1 = 0x3f4;
+ } else { /* secondary */
+ base0 = 0x170;
+ base1 = 0x374;
+ }
+
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0,
+ base0 | PCI_BASE_ADDRESS_SPACE_IO);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_1,
+ base1 | PCI_BASE_ADDRESS_SPACE_IO);
+
+ dev->resource[0].start = 0;
+ dev->resource[0].end = 0;
+ dev->resource[0].flags = 0;
+
+ dev->resource[1].start = 0;
+ dev->resource[1].end = 0;
+ dev->resource[1].flags = 0;
+ } else if (PCI_FUNC(dev->devfn) == 0) {
+ /*
+ * Setup IDE IRQ routing.
+ */
+ pci_write_config_byte(dev, 0x4b, 14);
+ pci_write_config_byte(dev, 0x4c, 15);
+
+ /*
+ * Disable FREQACK handshake, enable USB.
+ */
+ pci_write_config_byte(dev, 0x4d, 0x41);
+
+ /*
+ * Enable PCI retry, and PCI post-write buffer.
+ */
+ pci_write_config_byte(dev, 0x44, 0x17);
+
+ /*
+ * Enable ISA master and DMA post write buffering.
+ */
+ pci_write_config_byte(dev, 0x45, 0x03);
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, pci_fixup_cy82c693);
+
+static void __init pci_fixup_it8152(struct pci_dev *dev)
+{
+ int i;
+ /* fixup for ITE 8152 devices */
+ /* FIXME: add defines for class 0x68000 and 0x80103 */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST ||
+ dev->class == 0x68000 ||
+ dev->class == 0x80103) {
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ dev->resource[i].start = 0;
+ dev->resource[i].end = 0;
+ dev->resource[i].flags = 0;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8152, pci_fixup_it8152);
+
+
+
+void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ if (debug_pci)
+ printk("PCI: Assigning IRQ %02d to %s\n", irq, pci_name(dev));
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
+
+/*
+ * If the bus contains any of these devices, then we must not turn on
+ * parity checking of any kind. Currently this is CyberPro 20x0 only.
+ */
+static inline int pdev_bad_for_parity(struct pci_dev *dev)
+{
+ return ((dev->vendor == PCI_VENDOR_ID_INTERG &&
+ (dev->device == PCI_DEVICE_ID_INTERG_2000 ||
+ dev->device == PCI_DEVICE_ID_INTERG_2010)) ||
+ (dev->vendor == PCI_VENDOR_ID_ITE &&
+ dev->device == PCI_DEVICE_ID_ITE_8152));
+
+}
+
+/*
+ * Adjust the device resources from bus-centric to Linux-centric.
+ */
+static void __devinit
+pdev_fixup_device_resources(struct pci_sys_data *root, struct pci_dev *dev)
+{
+ resource_size_t offset;
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ if (dev->resource[i].start == 0)
+ continue;
+ if (dev->resource[i].flags & IORESOURCE_MEM)
+ offset = root->mem_offset;
+ else
+ offset = root->io_offset;
+
+ dev->resource[i].start += offset;
+ dev->resource[i].end += offset;
+ }
+}
+
+static void __devinit
+pbus_assign_bus_resources(struct pci_bus *bus, struct pci_sys_data *root)
+{
+ struct pci_dev *dev = bus->self;
+ int i;
+
+ if (!dev) {
+ /*
+ * Assign root bus resources.
+ */
+ for (i = 0; i < 3; i++)
+ bus->resource[i] = root->resource[i];
+ }
+}
+
+/*
+ * pcibios_fixup_bus - Called after each bus is probed,
+ * but before its children are examined.
+ */
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+ struct pci_sys_data *root = bus->sysdata;
+ struct pci_dev *dev;
+ u16 features = PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_FAST_BACK;
+
+ pbus_assign_bus_resources(bus, root);
+
+ /*
+ * Walk the devices on this bus, working out what we can
+ * and can't support.
+ */
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ u16 status;
+
+ pdev_fixup_device_resources(root, dev);
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+
+ /*
+ * If any device on this bus does not support fast back
+ * to back transfers, then the bus as a whole is not able
+ * to support them. Having fast back to back transfers
+ * on saves us one PCI cycle per transaction.
+ */
+ if (!(status & PCI_STATUS_FAST_BACK))
+ features &= ~PCI_COMMAND_FAST_BACK;
+
+ if (pdev_bad_for_parity(dev))
+ features &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+
+ switch (dev->class >> 8) {
+ case PCI_CLASS_BRIDGE_PCI:
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status);
+ status |= PCI_BRIDGE_CTL_PARITY|PCI_BRIDGE_CTL_MASTER_ABORT;
+ status &= ~(PCI_BRIDGE_CTL_BUS_RESET|PCI_BRIDGE_CTL_FAST_BACK);
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status);
+ break;
+
+ case PCI_CLASS_BRIDGE_CARDBUS:
+ pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL, &status);
+ status |= PCI_CB_BRIDGE_CTL_PARITY|PCI_CB_BRIDGE_CTL_MASTER_ABORT;
+ pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL, status);
+ break;
+ }
+ }
+
+ /*
+ * Now walk the devices again, this time setting them up.
+ */
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ u16 cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ cmd |= features;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
+ L1_CACHE_BYTES >> 2);
+ }
+
+ /*
+ * Propagate the flags to the PCI bridge.
+ */
+ if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ if (features & PCI_COMMAND_FAST_BACK)
+ bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK;
+ if (features & PCI_COMMAND_PARITY)
+ bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY;
+ }
+
+ /*
+ * Report what we did for this bus
+ */
+ printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n",
+ bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
+}
+
+/*
+ * Convert from Linux-centric to bus-centric addresses for bridge devices.
+ */
+void
+pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
+ struct resource *res)
+{
+ struct pci_sys_data *root = dev->sysdata;
+ unsigned long offset = 0;
+
+ if (res->flags & IORESOURCE_IO)
+ offset = root->io_offset;
+ if (res->flags & IORESOURCE_MEM)
+ offset = root->mem_offset;
+
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+
+void __devinit
+pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
+ struct pci_bus_region *region)
+{
+ struct pci_sys_data *root = dev->sysdata;
+ unsigned long offset = 0;
+
+ if (res->flags & IORESOURCE_IO)
+ offset = root->io_offset;
+ if (res->flags & IORESOURCE_MEM)
+ offset = root->mem_offset;
+
+ res->start = region->start + offset;
+ res->end = region->end + offset;
+}
+
+#ifdef CONFIG_HOTPLUG
+EXPORT_SYMBOL(pcibios_fixup_bus);
+EXPORT_SYMBOL(pcibios_resource_to_bus);
+EXPORT_SYMBOL(pcibios_bus_to_resource);
+#endif
+
+/*
+ * This is the standard PCI-PCI bridge swizzling algorithm:
+ *
+ * Dev: 0 1 2 3
+ * A A B C D
+ * B B C D A
+ * C C D A B
+ * D D A B C
+ * ^^^^^^^^^^ irq pin on bridge
+ */
+u8 __devinit pci_std_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+ int pin = *pinp - 1;
+
+ while (dev->bus->self) {
+ pin = (pin + PCI_SLOT(dev->devfn)) & 3;
+ /*
+ * move up the chain of bridges,
+ * swizzling as we go.
+ */
+ dev = dev->bus->self;
+ }
+ *pinp = pin + 1;
+
+ return PCI_SLOT(dev->devfn);
+}
+
+/*
+ * Swizzle the device pin each time we cross a bridge.
+ * This might update pin and returns the slot number.
+ */
+static u8 __devinit pcibios_swizzle(struct pci_dev *dev, u8 *pin)
+{
+ struct pci_sys_data *sys = dev->sysdata;
+ int slot = 0, oldpin = *pin;
+
+ if (sys->swizzle)
+ slot = sys->swizzle(dev, pin);
+
+ if (debug_pci)
+ printk("PCI: %s swizzling pin %d => pin %d slot %d\n",
+ pci_name(dev), oldpin, *pin, slot);
+
+ return slot;
+}
+
+/*
+ * Map a slot/pin to an IRQ.
+ */
+static int pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pci_sys_data *sys = dev->sysdata;
+ int irq = -1;
+
+ if (sys->map_irq)
+ irq = sys->map_irq(dev, slot, pin);
+
+ if (debug_pci)
+ printk("PCI: %s mapping slot %d pin %d => irq %d\n",
+ pci_name(dev), slot, pin, irq);
+
+ return irq;
+}
+
+static void __init pcibios_init_hw(struct hw_pci *hw)
+{
+ struct pci_sys_data *sys = NULL;
+ int ret;
+ int nr, busnr;
+
+ for (nr = busnr = 0; nr < hw->nr_controllers; nr++) {
+ sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
+ if (!sys)
+ panic("PCI: unable to allocate sys data!");
+
+ sys->hw = hw;
+ sys->busnr = busnr;
+ sys->swizzle = hw->swizzle;
+ sys->map_irq = hw->map_irq;
+ sys->resource[0] = &ioport_resource;
+ sys->resource[1] = &iomem_resource;
+
+ ret = hw->setup(nr, sys);
+
+ if (ret > 0) {
+ sys->bus = hw->scan(nr, sys);
+
+ if (!sys->bus)
+ panic("PCI: unable to scan bus!");
+
+ busnr = sys->bus->subordinate + 1;
+
+ list_add(&sys->node, &hw->buses);
+ } else {
+ kfree(sys);
+ if (ret < 0)
+ break;
+ }
+ }
+}
+
+void __init pci_common_init(struct hw_pci *hw)
+{
+ struct pci_sys_data *sys;
+
+ INIT_LIST_HEAD(&hw->buses);
+
+ if (hw->preinit)
+ hw->preinit();
+ pcibios_init_hw(hw);
+ if (hw->postinit)
+ hw->postinit();
+
+ pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
+
+ list_for_each_entry(sys, &hw->buses, node) {
+ struct pci_bus *bus = sys->bus;
+
+ if (!use_firmware) {
+ /*
+ * Size the bridge windows.
+ */
+ pci_bus_size_bridges(bus);
+
+ /*
+ * Assign resources.
+ */
+ pci_bus_assign_resources(bus);
+ }
+
+ /*
+ * Tell drivers about devices found.
+ */
+ pci_bus_add_devices(bus);
+ }
+}
+
+char * __init pcibios_setup(char *str)
+{
+ if (!strcmp(str, "debug")) {
+ debug_pci = 1;
+ return NULL;
+ } else if (!strcmp(str, "firmware")) {
+ use_firmware = 1;
+ return NULL;
+ }
+ return str;
+}
+
+/*
+ * From arch/i386/kernel/pci-i386.c:
+ *
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might be mirrored at 0x0100-0x03ff..
+ */
+void pcibios_align_resource(void *data, struct resource *res,
+ resource_size_t size, resource_size_t align)
+{
+ resource_size_t start = res->start;
+
+ if (res->flags & IORESOURCE_IO && start & 0x300)
+ start = (start + 0x3ff) & ~0x3ff;
+
+ res->start = (start + align - 1) & ~(align - 1);
+}
+
+/**
+ * pcibios_enable_device - Enable I/O and memory.
+ * @dev: PCI device to be enabled
+ */
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx = 0; idx < 6; idx++) {
+ /* Only set up the requested stuff */
+ if (!(mask & (1 << idx)))
+ continue;
+
+ r = dev->resource + idx;
+ if (!r->start && r->end) {
+ printk(KERN_ERR "PCI: Device %s not available because"
+ " of resource collisions\n", pci_name(dev));
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+
+ /*
+ * Bridges (eg, cardbus bridges) need to be fully enabled
+ */
+ if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
+ cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+
+ if (cmd != old_cmd) {
+ printk("PCI: enabling device %s (%04x -> %04x)\n",
+ pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state, int write_combine)
+{
+ struct pci_sys_data *root = dev->sysdata;
+ unsigned long phys;
+
+ if (mmap_state == pci_mmap_io) {
+ return -EINVAL;
+ } else {
+ phys = vma->vm_pgoff + (root->mem_offset >> PAGE_SHIFT);
+ }
+
+ /*
+ * Mark this as IO
+ */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start, phys,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
new file mode 100644
index 0000000..9ca8d13
--- /dev/null
+++ b/arch/arm/kernel/calls.S
@@ -0,0 +1,379 @@
+/*
+ * linux/arch/arm/kernel/calls.S
+ *
+ * Copyright (C) 1995-2005 Russell King
+ *
+ * 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 file is included thrice in entry-common.S
+ */
+/* 0 */ CALL(sys_restart_syscall)
+ CALL(sys_exit)
+ CALL(sys_fork_wrapper)
+ CALL(sys_read)
+ CALL(sys_write)
+/* 5 */ CALL(sys_open)
+ CALL(sys_close)
+ CALL(sys_ni_syscall) /* was sys_waitpid */
+ CALL(sys_creat)
+ CALL(sys_link)
+/* 10 */ CALL(sys_unlink)
+ CALL(sys_execve_wrapper)
+ CALL(sys_chdir)
+ CALL(OBSOLETE(sys_time)) /* used by libc4 */
+ CALL(sys_mknod)
+/* 15 */ CALL(sys_chmod)
+ CALL(sys_lchown16)
+ CALL(sys_ni_syscall) /* was sys_break */
+ CALL(sys_ni_syscall) /* was sys_stat */
+ CALL(sys_lseek)
+/* 20 */ CALL(sys_getpid)
+ CALL(sys_mount)
+ CALL(OBSOLETE(sys_oldumount)) /* used by libc4 */
+ CALL(sys_setuid16)
+ CALL(sys_getuid16)
+/* 25 */ CALL(OBSOLETE(sys_stime))
+ CALL(sys_ptrace)
+ CALL(OBSOLETE(sys_alarm)) /* used by libc4 */
+ CALL(sys_ni_syscall) /* was sys_fstat */
+ CALL(sys_pause)
+/* 30 */ CALL(OBSOLETE(sys_utime)) /* used by libc4 */
+ CALL(sys_ni_syscall) /* was sys_stty */
+ CALL(sys_ni_syscall) /* was sys_getty */
+ CALL(sys_access)
+ CALL(sys_nice)
+/* 35 */ CALL(sys_ni_syscall) /* was sys_ftime */
+ CALL(sys_sync)
+ CALL(sys_kill)
+ CALL(sys_rename)
+ CALL(sys_mkdir)
+/* 40 */ CALL(sys_rmdir)
+ CALL(sys_dup)
+ CALL(sys_pipe)
+ CALL(sys_times)
+ CALL(sys_ni_syscall) /* was sys_prof */
+/* 45 */ CALL(sys_brk)
+ CALL(sys_setgid16)
+ CALL(sys_getgid16)
+ CALL(sys_ni_syscall) /* was sys_signal */
+ CALL(sys_geteuid16)
+/* 50 */ CALL(sys_getegid16)
+ CALL(sys_acct)
+ CALL(sys_umount)
+ CALL(sys_ni_syscall) /* was sys_lock */
+ CALL(sys_ioctl)
+/* 55 */ CALL(sys_fcntl)
+ CALL(sys_ni_syscall) /* was sys_mpx */
+ CALL(sys_setpgid)
+ CALL(sys_ni_syscall) /* was sys_ulimit */
+ CALL(sys_ni_syscall) /* was sys_olduname */
+/* 60 */ CALL(sys_umask)
+ CALL(sys_chroot)
+ CALL(sys_ustat)
+ CALL(sys_dup2)
+ CALL(sys_getppid)
+/* 65 */ CALL(sys_getpgrp)
+ CALL(sys_setsid)
+ CALL(sys_sigaction)
+ CALL(sys_ni_syscall) /* was sys_sgetmask */
+ CALL(sys_ni_syscall) /* was sys_ssetmask */
+/* 70 */ CALL(sys_setreuid16)
+ CALL(sys_setregid16)
+ CALL(sys_sigsuspend_wrapper)
+ CALL(sys_sigpending)
+ CALL(sys_sethostname)
+/* 75 */ CALL(sys_setrlimit)
+ CALL(OBSOLETE(sys_old_getrlimit)) /* used by libc4 */
+ CALL(sys_getrusage)
+ CALL(sys_gettimeofday)
+ CALL(sys_settimeofday)
+/* 80 */ CALL(sys_getgroups16)
+ CALL(sys_setgroups16)
+ CALL(OBSOLETE(old_select)) /* used by libc4 */
+ CALL(sys_symlink)
+ CALL(sys_ni_syscall) /* was sys_lstat */
+/* 85 */ CALL(sys_readlink)
+ CALL(sys_uselib)
+ CALL(sys_swapon)
+ CALL(sys_reboot)
+ CALL(OBSOLETE(sys_old_readdir)) /* used by libc4 */
+/* 90 */ CALL(OBSOLETE(old_mmap)) /* used by libc4 */
+ CALL(sys_munmap)
+ CALL(sys_truncate)
+ CALL(sys_ftruncate)
+ CALL(sys_fchmod)
+/* 95 */ CALL(sys_fchown16)
+ CALL(sys_getpriority)
+ CALL(sys_setpriority)
+ CALL(sys_ni_syscall) /* was sys_profil */
+ CALL(sys_statfs)
+/* 100 */ CALL(sys_fstatfs)
+ CALL(sys_ni_syscall) /* sys_ioperm */
+ CALL(OBSOLETE(ABI(sys_socketcall, sys_oabi_socketcall)))
+ CALL(sys_syslog)
+ CALL(sys_setitimer)
+/* 105 */ CALL(sys_getitimer)
+ CALL(sys_newstat)
+ CALL(sys_newlstat)
+ CALL(sys_newfstat)
+ CALL(sys_ni_syscall) /* was sys_uname */
+/* 110 */ CALL(sys_ni_syscall) /* was sys_iopl */
+ CALL(sys_vhangup)
+ CALL(sys_ni_syscall)
+ CALL(OBSOLETE(sys_syscall)) /* call a syscall */
+ CALL(sys_wait4)
+/* 115 */ CALL(sys_swapoff)
+ CALL(sys_sysinfo)
+ CALL(OBSOLETE(ABI(sys_ipc, sys_oabi_ipc)))
+ CALL(sys_fsync)
+ CALL(sys_sigreturn_wrapper)
+/* 120 */ CALL(sys_clone_wrapper)
+ CALL(sys_setdomainname)
+ CALL(sys_newuname)
+ CALL(sys_ni_syscall) /* modify_ldt */
+ CALL(sys_adjtimex)
+/* 125 */ CALL(sys_mprotect)
+ CALL(sys_sigprocmask)
+ CALL(sys_ni_syscall) /* was sys_create_module */
+ CALL(sys_init_module)
+ CALL(sys_delete_module)
+/* 130 */ CALL(sys_ni_syscall) /* was sys_get_kernel_syms */
+ CALL(sys_quotactl)
+ CALL(sys_getpgid)
+ CALL(sys_fchdir)
+ CALL(sys_bdflush)
+/* 135 */ CALL(sys_sysfs)
+ CALL(sys_personality)
+ CALL(sys_ni_syscall) /* reserved for afs_syscall */
+ CALL(sys_setfsuid16)
+ CALL(sys_setfsgid16)
+/* 140 */ CALL(sys_llseek)
+ CALL(sys_getdents)
+ CALL(sys_select)
+ CALL(sys_flock)
+ CALL(sys_msync)
+/* 145 */ CALL(sys_readv)
+ CALL(sys_writev)
+ CALL(sys_getsid)
+ CALL(sys_fdatasync)
+ CALL(sys_sysctl)
+/* 150 */ CALL(sys_mlock)
+ CALL(sys_munlock)
+ CALL(sys_mlockall)
+ CALL(sys_munlockall)
+ CALL(sys_sched_setparam)
+/* 155 */ CALL(sys_sched_getparam)
+ CALL(sys_sched_setscheduler)
+ CALL(sys_sched_getscheduler)
+ CALL(sys_sched_yield)
+ CALL(sys_sched_get_priority_max)
+/* 160 */ CALL(sys_sched_get_priority_min)
+ CALL(sys_sched_rr_get_interval)
+ CALL(sys_nanosleep)
+ CALL(sys_arm_mremap)
+ CALL(sys_setresuid16)
+/* 165 */ CALL(sys_getresuid16)
+ CALL(sys_ni_syscall) /* vm86 */
+ CALL(sys_ni_syscall) /* was sys_query_module */
+ CALL(sys_poll)
+ CALL(sys_nfsservctl)
+/* 170 */ CALL(sys_setresgid16)
+ CALL(sys_getresgid16)
+ CALL(sys_prctl)
+ CALL(sys_rt_sigreturn_wrapper)
+ CALL(sys_rt_sigaction)
+/* 175 */ CALL(sys_rt_sigprocmask)
+ CALL(sys_rt_sigpending)
+ CALL(sys_rt_sigtimedwait)
+ CALL(sys_rt_sigqueueinfo)
+ CALL(sys_rt_sigsuspend_wrapper)
+/* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64))
+ CALL(ABI(sys_pwrite64, sys_oabi_pwrite64))
+ CALL(sys_chown16)
+ CALL(sys_getcwd)
+ CALL(sys_capget)
+/* 185 */ CALL(sys_capset)
+ CALL(sys_sigaltstack_wrapper)
+ CALL(sys_sendfile)
+ CALL(sys_ni_syscall) /* getpmsg */
+ CALL(sys_ni_syscall) /* putpmsg */
+/* 190 */ CALL(sys_vfork_wrapper)
+ CALL(sys_getrlimit)
+ CALL(sys_mmap2)
+ CALL(ABI(sys_truncate64, sys_oabi_truncate64))
+ CALL(ABI(sys_ftruncate64, sys_oabi_ftruncate64))
+/* 195 */ CALL(ABI(sys_stat64, sys_oabi_stat64))
+ CALL(ABI(sys_lstat64, sys_oabi_lstat64))
+ CALL(ABI(sys_fstat64, sys_oabi_fstat64))
+ CALL(sys_lchown)
+ CALL(sys_getuid)
+/* 200 */ CALL(sys_getgid)
+ CALL(sys_geteuid)
+ CALL(sys_getegid)
+ CALL(sys_setreuid)
+ CALL(sys_setregid)
+/* 205 */ CALL(sys_getgroups)
+ CALL(sys_setgroups)
+ CALL(sys_fchown)
+ CALL(sys_setresuid)
+ CALL(sys_getresuid)
+/* 210 */ CALL(sys_setresgid)
+ CALL(sys_getresgid)
+ CALL(sys_chown)
+ CALL(sys_setuid)
+ CALL(sys_setgid)
+/* 215 */ CALL(sys_setfsuid)
+ CALL(sys_setfsgid)
+ CALL(sys_getdents64)
+ CALL(sys_pivot_root)
+ CALL(sys_mincore)
+/* 220 */ CALL(sys_madvise)
+ CALL(ABI(sys_fcntl64, sys_oabi_fcntl64))
+ CALL(sys_ni_syscall) /* TUX */
+ CALL(sys_ni_syscall)
+ CALL(sys_gettid)
+/* 225 */ CALL(ABI(sys_readahead, sys_oabi_readahead))
+ CALL(sys_setxattr)
+ CALL(sys_lsetxattr)
+ CALL(sys_fsetxattr)
+ CALL(sys_getxattr)
+/* 230 */ CALL(sys_lgetxattr)
+ CALL(sys_fgetxattr)
+ CALL(sys_listxattr)
+ CALL(sys_llistxattr)
+ CALL(sys_flistxattr)
+/* 235 */ CALL(sys_removexattr)
+ CALL(sys_lremovexattr)
+ CALL(sys_fremovexattr)
+ CALL(sys_tkill)
+ CALL(sys_sendfile64)
+/* 240 */ CALL(sys_futex)
+ CALL(sys_sched_setaffinity)
+ CALL(sys_sched_getaffinity)
+ CALL(sys_io_setup)
+ CALL(sys_io_destroy)
+/* 245 */ CALL(sys_io_getevents)
+ CALL(sys_io_submit)
+ CALL(sys_io_cancel)
+ CALL(sys_exit_group)
+ CALL(sys_lookup_dcookie)
+/* 250 */ CALL(sys_epoll_create)
+ CALL(ABI(sys_epoll_ctl, sys_oabi_epoll_ctl))
+ CALL(ABI(sys_epoll_wait, sys_oabi_epoll_wait))
+ CALL(sys_remap_file_pages)
+ CALL(sys_ni_syscall) /* sys_set_thread_area */
+/* 255 */ CALL(sys_ni_syscall) /* sys_get_thread_area */
+ CALL(sys_set_tid_address)
+ CALL(sys_timer_create)
+ CALL(sys_timer_settime)
+ CALL(sys_timer_gettime)
+/* 260 */ CALL(sys_timer_getoverrun)
+ CALL(sys_timer_delete)
+ CALL(sys_clock_settime)
+ CALL(sys_clock_gettime)
+ CALL(sys_clock_getres)
+/* 265 */ CALL(sys_clock_nanosleep)
+ CALL(sys_statfs64_wrapper)
+ CALL(sys_fstatfs64_wrapper)
+ CALL(sys_tgkill)
+ CALL(sys_utimes)
+/* 270 */ CALL(sys_arm_fadvise64_64)
+ CALL(sys_pciconfig_iobase)
+ CALL(sys_pciconfig_read)
+ CALL(sys_pciconfig_write)
+ CALL(sys_mq_open)
+/* 275 */ CALL(sys_mq_unlink)
+ CALL(sys_mq_timedsend)
+ CALL(sys_mq_timedreceive)
+ CALL(sys_mq_notify)
+ CALL(sys_mq_getsetattr)
+/* 280 */ CALL(sys_waitid)
+ CALL(sys_socket)
+ CALL(ABI(sys_bind, sys_oabi_bind))
+ CALL(ABI(sys_connect, sys_oabi_connect))
+ CALL(sys_listen)
+/* 285 */ CALL(sys_accept)
+ CALL(sys_getsockname)
+ CALL(sys_getpeername)
+ CALL(sys_socketpair)
+ CALL(sys_send)
+/* 290 */ CALL(ABI(sys_sendto, sys_oabi_sendto))
+ CALL(sys_recv)
+ CALL(sys_recvfrom)
+ CALL(sys_shutdown)
+ CALL(sys_setsockopt)
+/* 295 */ CALL(sys_getsockopt)
+ CALL(ABI(sys_sendmsg, sys_oabi_sendmsg))
+ CALL(sys_recvmsg)
+ CALL(ABI(sys_semop, sys_oabi_semop))
+ CALL(sys_semget)
+/* 300 */ CALL(sys_semctl)
+ CALL(sys_msgsnd)
+ CALL(sys_msgrcv)
+ CALL(sys_msgget)
+ CALL(sys_msgctl)
+/* 305 */ CALL(sys_shmat)
+ CALL(sys_shmdt)
+ CALL(sys_shmget)
+ CALL(sys_shmctl)
+ CALL(sys_add_key)
+/* 310 */ CALL(sys_request_key)
+ CALL(sys_keyctl)
+ CALL(ABI(sys_semtimedop, sys_oabi_semtimedop))
+/* vserver */ CALL(sys_ni_syscall)
+ CALL(sys_ioprio_set)
+/* 315 */ CALL(sys_ioprio_get)
+ CALL(sys_inotify_init)
+ CALL(sys_inotify_add_watch)
+ CALL(sys_inotify_rm_watch)
+ CALL(sys_mbind)
+/* 320 */ CALL(sys_get_mempolicy)
+ CALL(sys_set_mempolicy)
+ CALL(sys_openat)
+ CALL(sys_mkdirat)
+ CALL(sys_mknodat)
+/* 325 */ CALL(sys_fchownat)
+ CALL(sys_futimesat)
+ CALL(ABI(sys_fstatat64, sys_oabi_fstatat64))
+ CALL(sys_unlinkat)
+ CALL(sys_renameat)
+/* 330 */ CALL(sys_linkat)
+ CALL(sys_symlinkat)
+ CALL(sys_readlinkat)
+ CALL(sys_fchmodat)
+ CALL(sys_faccessat)
+/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */
+ CALL(sys_ni_syscall) /* eventually ppoll */
+ CALL(sys_unshare)
+ CALL(sys_set_robust_list)
+ CALL(sys_get_robust_list)
+/* 340 */ CALL(sys_splice)
+ CALL(sys_sync_file_range2)
+ CALL(sys_tee)
+ CALL(sys_vmsplice)
+ CALL(sys_move_pages)
+/* 345 */ CALL(sys_getcpu)
+ CALL(sys_ni_syscall) /* eventually epoll_pwait */
+ CALL(sys_kexec_load)
+ CALL(sys_utimensat)
+ CALL(sys_signalfd)
+/* 350 */ CALL(sys_timerfd_create)
+ CALL(sys_eventfd)
+ CALL(sys_fallocate)
+ CALL(sys_timerfd_settime)
+ CALL(sys_timerfd_gettime)
+/* 355 */ CALL(sys_signalfd4)
+ CALL(sys_eventfd2)
+ CALL(sys_epoll_create1)
+ CALL(sys_dup3)
+ CALL(sys_pipe2)
+/* 360 */ CALL(sys_inotify_init1)
+#ifndef syscalls_counted
+.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
+#define syscalls_counted
+#endif
+.rept syscalls_padding
+ CALL(sys_ni_syscall)
+.endr
diff --git a/arch/arm/kernel/compat.c b/arch/arm/kernel/compat.c
new file mode 100644
index 0000000..0a13854
--- /dev/null
+++ b/arch/arm/kernel/compat.c
@@ -0,0 +1,226 @@
+/*
+ * linux/arch/arm/kernel/compat.c
+ *
+ * Copyright (C) 2001 Russell King
+ *
+ * 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.
+ *
+ * We keep the old params compatibility cruft in one place (here)
+ * so we don't end up with lots of mess around other places.
+ *
+ * NOTE:
+ * The old struct param_struct is deprecated, but it will be kept in
+ * the kernel for 5 years from now (2001). This will allow boot loaders
+ * to convert to the new struct tag way.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/page.h>
+
+#include <asm/mach/arch.h>
+
+#include "compat.h"
+
+/*
+ * Usage:
+ * - do not go blindly adding fields, add them at the end
+ * - when adding fields, don't rely on the address until
+ * a patch from me has been released
+ * - unused fields should be zero (for future expansion)
+ * - this structure is relatively short-lived - only
+ * guaranteed to contain useful data in setup_arch()
+ *
+ * This is the old deprecated way to pass parameters to the kernel
+ */
+struct param_struct {
+ union {
+ struct {
+ unsigned long page_size; /* 0 */
+ unsigned long nr_pages; /* 4 */
+ unsigned long ramdisk_size; /* 8 */
+ unsigned long flags; /* 12 */
+#define FLAG_READONLY 1
+#define FLAG_RDLOAD 4
+#define FLAG_RDPROMPT 8
+ unsigned long rootdev; /* 16 */
+ unsigned long video_num_cols; /* 20 */
+ unsigned long video_num_rows; /* 24 */
+ unsigned long video_x; /* 28 */
+ unsigned long video_y; /* 32 */
+ unsigned long memc_control_reg; /* 36 */
+ unsigned char sounddefault; /* 40 */
+ unsigned char adfsdrives; /* 41 */
+ unsigned char bytes_per_char_h; /* 42 */
+ unsigned char bytes_per_char_v; /* 43 */
+ unsigned long pages_in_bank[4]; /* 44 */
+ unsigned long pages_in_vram; /* 60 */
+ unsigned long initrd_start; /* 64 */
+ unsigned long initrd_size; /* 68 */
+ unsigned long rd_start; /* 72 */
+ unsigned long system_rev; /* 76 */
+ unsigned long system_serial_low; /* 80 */
+ unsigned long system_serial_high; /* 84 */
+ unsigned long mem_fclk_21285; /* 88 */
+ } s;
+ char unused[256];
+ } u1;
+ union {
+ char paths[8][128];
+ struct {
+ unsigned long magic;
+ char n[1024 - sizeof(unsigned long)];
+ } s;
+ } u2;
+ char commandline[COMMAND_LINE_SIZE];
+};
+
+static struct tag * __init memtag(struct tag *tag, unsigned long start, unsigned long size)
+{
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_MEM;
+ tag->hdr.size = tag_size(tag_mem32);
+ tag->u.mem.size = size;
+ tag->u.mem.start = start;
+
+ return tag;
+}
+
+static void __init build_tag_list(struct param_struct *params, void *taglist)
+{
+ struct tag *tag = taglist;
+
+ if (params->u1.s.page_size != PAGE_SIZE) {
+ printk(KERN_WARNING "Warning: bad configuration page, "
+ "trying to continue\n");
+ return;
+ }
+
+ printk(KERN_DEBUG "Converting old-style param struct to taglist\n");
+
+#ifdef CONFIG_ARCH_NETWINDER
+ if (params->u1.s.nr_pages != 0x02000 &&
+ params->u1.s.nr_pages != 0x04000 &&
+ params->u1.s.nr_pages != 0x08000 &&
+ params->u1.s.nr_pages != 0x10000) {
+ printk(KERN_WARNING "Warning: bad NeTTrom parameters "
+ "detected, using defaults\n");
+
+ params->u1.s.nr_pages = 0x1000; /* 16MB */
+ params->u1.s.ramdisk_size = 0;
+ params->u1.s.flags = FLAG_READONLY;
+ params->u1.s.initrd_start = 0;
+ params->u1.s.initrd_size = 0;
+ params->u1.s.rd_start = 0;
+ }
+#endif
+
+ tag->hdr.tag = ATAG_CORE;
+ tag->hdr.size = tag_size(tag_core);
+ tag->u.core.flags = params->u1.s.flags & FLAG_READONLY;
+ tag->u.core.pagesize = params->u1.s.page_size;
+ tag->u.core.rootdev = params->u1.s.rootdev;
+
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_RAMDISK;
+ tag->hdr.size = tag_size(tag_ramdisk);
+ tag->u.ramdisk.flags = (params->u1.s.flags & FLAG_RDLOAD ? 1 : 0) |
+ (params->u1.s.flags & FLAG_RDPROMPT ? 2 : 0);
+ tag->u.ramdisk.size = params->u1.s.ramdisk_size;
+ tag->u.ramdisk.start = params->u1.s.rd_start;
+
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_INITRD;
+ tag->hdr.size = tag_size(tag_initrd);
+ tag->u.initrd.start = params->u1.s.initrd_start;
+ tag->u.initrd.size = params->u1.s.initrd_size;
+
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_SERIAL;
+ tag->hdr.size = tag_size(tag_serialnr);
+ tag->u.serialnr.low = params->u1.s.system_serial_low;
+ tag->u.serialnr.high = params->u1.s.system_serial_high;
+
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_REVISION;
+ tag->hdr.size = tag_size(tag_revision);
+ tag->u.revision.rev = params->u1.s.system_rev;
+
+#ifdef CONFIG_ARCH_ACORN
+ if (machine_is_riscpc()) {
+ int i;
+ for (i = 0; i < 4; i++)
+ tag = memtag(tag, PHYS_OFFSET + (i << 26),
+ params->u1.s.pages_in_bank[i] * PAGE_SIZE);
+ } else
+#endif
+ tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE);
+
+#ifdef CONFIG_FOOTBRIDGE
+ if (params->u1.s.mem_fclk_21285) {
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_MEMCLK;
+ tag->hdr.size = tag_size(tag_memclk);
+ tag->u.memclk.fmemclk = params->u1.s.mem_fclk_21285;
+ }
+#endif
+
+#ifdef CONFIG_ARCH_EBSA285
+ if (machine_is_ebsa285()) {
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_VIDEOTEXT;
+ tag->hdr.size = tag_size(tag_videotext);
+ tag->u.videotext.x = params->u1.s.video_x;
+ tag->u.videotext.y = params->u1.s.video_y;
+ tag->u.videotext.video_page = 0;
+ tag->u.videotext.video_mode = 0;
+ tag->u.videotext.video_cols = params->u1.s.video_num_cols;
+ tag->u.videotext.video_ega_bx = 0;
+ tag->u.videotext.video_lines = params->u1.s.video_num_rows;
+ tag->u.videotext.video_isvga = 1;
+ tag->u.videotext.video_points = 8;
+ }
+#endif
+
+#ifdef CONFIG_ARCH_ACORN
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_ACORN;
+ tag->hdr.size = tag_size(tag_acorn);
+ tag->u.acorn.memc_control_reg = params->u1.s.memc_control_reg;
+ tag->u.acorn.vram_pages = params->u1.s.pages_in_vram;
+ tag->u.acorn.sounddefault = params->u1.s.sounddefault;
+ tag->u.acorn.adfsdrives = params->u1.s.adfsdrives;
+#endif
+
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_CMDLINE;
+ tag->hdr.size = (strlen(params->commandline) + 3 +
+ sizeof(struct tag_header)) >> 2;
+ strcpy(tag->u.cmdline.cmdline, params->commandline);
+
+ tag = tag_next(tag);
+ tag->hdr.tag = ATAG_NONE;
+ tag->hdr.size = 0;
+
+ memmove(params, taglist, ((int)tag) - ((int)taglist) +
+ sizeof(struct tag_header));
+}
+
+void __init convert_to_tag_list(struct tag *tags)
+{
+ struct param_struct *params = (struct param_struct *)tags;
+ build_tag_list(params, &params->u2);
+}
+
+void __init squash_mem_tags(struct tag *tag)
+{
+ for (; tag->hdr.size; tag = tag_next(tag))
+ if (tag->hdr.tag == ATAG_MEM)
+ tag->hdr.tag = ATAG_NONE;
+}
diff --git a/arch/arm/kernel/compat.h b/arch/arm/kernel/compat.h
new file mode 100644
index 0000000..27e61a6
--- /dev/null
+++ b/arch/arm/kernel/compat.h
@@ -0,0 +1,13 @@
+/*
+ * linux/arch/arm/kernel/compat.h
+ *
+ * Copyright (C) 2001 Russell King
+ *
+ * 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.
+*/
+
+extern void convert_to_tag_list(struct tag *tags);
+
+extern void squash_mem_tags(struct tag *tag);
diff --git a/arch/arm/kernel/crunch-bits.S b/arch/arm/kernel/crunch-bits.S
new file mode 100644
index 0000000..0ec9bb4
--- /dev/null
+++ b/arch/arm/kernel/crunch-bits.S
@@ -0,0 +1,305 @@
+/*
+ * arch/arm/kernel/crunch-bits.S
+ * Cirrus MaverickCrunch context switching and handling
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *
+ * Shamelessly stolen from the iWMMXt code by Nicolas Pitre, which is
+ * Copyright (c) 2003-2004, MontaVista Software, 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/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <mach/ep93xx-regs.h>
+
+/*
+ * We can't use hex constants here due to a bug in gas.
+ */
+#define CRUNCH_MVDX0 0
+#define CRUNCH_MVDX1 8
+#define CRUNCH_MVDX2 16
+#define CRUNCH_MVDX3 24
+#define CRUNCH_MVDX4 32
+#define CRUNCH_MVDX5 40
+#define CRUNCH_MVDX6 48
+#define CRUNCH_MVDX7 56
+#define CRUNCH_MVDX8 64
+#define CRUNCH_MVDX9 72
+#define CRUNCH_MVDX10 80
+#define CRUNCH_MVDX11 88
+#define CRUNCH_MVDX12 96
+#define CRUNCH_MVDX13 104
+#define CRUNCH_MVDX14 112
+#define CRUNCH_MVDX15 120
+#define CRUNCH_MVAX0L 128
+#define CRUNCH_MVAX0M 132
+#define CRUNCH_MVAX0H 136
+#define CRUNCH_MVAX1L 140
+#define CRUNCH_MVAX1M 144
+#define CRUNCH_MVAX1H 148
+#define CRUNCH_MVAX2L 152
+#define CRUNCH_MVAX2M 156
+#define CRUNCH_MVAX2H 160
+#define CRUNCH_MVAX3L 164
+#define CRUNCH_MVAX3M 168
+#define CRUNCH_MVAX3H 172
+#define CRUNCH_DSPSC 176
+
+#define CRUNCH_SIZE 184
+
+ .text
+
+/*
+ * Lazy switching of crunch coprocessor context
+ *
+ * r10 = struct thread_info pointer
+ * r9 = ret_from_exception
+ * lr = undefined instr exit
+ *
+ * called from prefetch exception handler with interrupts disabled
+ */
+ENTRY(crunch_task_enable)
+ ldr r8, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr
+
+ ldr r1, [r8, #0x80]
+ tst r1, #0x00800000 @ access to crunch enabled?
+ movne pc, lr @ if so no business here
+ mov r3, #0xaa @ unlock syscon swlock
+ str r3, [r8, #0xc0]
+ orr r1, r1, #0x00800000 @ enable access to crunch
+ str r1, [r8, #0x80]
+
+ ldr r3, =crunch_owner
+ add r0, r10, #TI_CRUNCH_STATE @ get task crunch save area
+ ldr r2, [sp, #60] @ current task pc value
+ ldr r1, [r3] @ get current crunch owner
+ str r0, [r3] @ this task now owns crunch
+ sub r2, r2, #4 @ adjust pc back
+ str r2, [sp, #60]
+
+ ldr r2, [r8, #0x80]
+ mov r2, r2 @ flush out enable (@@@)
+
+ teq r1, #0 @ test for last ownership
+ mov lr, r9 @ normal exit from exception
+ beq crunch_load @ no owner, skip save
+
+crunch_save:
+ cfstr64 mvdx0, [r1, #CRUNCH_MVDX0] @ save 64b registers
+ cfstr64 mvdx1, [r1, #CRUNCH_MVDX1]
+ cfstr64 mvdx2, [r1, #CRUNCH_MVDX2]
+ cfstr64 mvdx3, [r1, #CRUNCH_MVDX3]
+ cfstr64 mvdx4, [r1, #CRUNCH_MVDX4]
+ cfstr64 mvdx5, [r1, #CRUNCH_MVDX5]
+ cfstr64 mvdx6, [r1, #CRUNCH_MVDX6]
+ cfstr64 mvdx7, [r1, #CRUNCH_MVDX7]
+ cfstr64 mvdx8, [r1, #CRUNCH_MVDX8]
+ cfstr64 mvdx9, [r1, #CRUNCH_MVDX9]
+ cfstr64 mvdx10, [r1, #CRUNCH_MVDX10]
+ cfstr64 mvdx11, [r1, #CRUNCH_MVDX11]
+ cfstr64 mvdx12, [r1, #CRUNCH_MVDX12]
+ cfstr64 mvdx13, [r1, #CRUNCH_MVDX13]
+ cfstr64 mvdx14, [r1, #CRUNCH_MVDX14]
+ cfstr64 mvdx15, [r1, #CRUNCH_MVDX15]
+
+#ifdef __ARMEB__
+#error fix me for ARMEB
+#endif
+
+ cfmv32al mvfx0, mvax0 @ save 72b accumulators
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX0L]
+ cfmv32am mvfx0, mvax0
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX0M]
+ cfmv32ah mvfx0, mvax0
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX0H]
+ cfmv32al mvfx0, mvax1
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX1L]
+ cfmv32am mvfx0, mvax1
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX1M]
+ cfmv32ah mvfx0, mvax1
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX1H]
+ cfmv32al mvfx0, mvax2
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX2L]
+ cfmv32am mvfx0, mvax2
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX2M]
+ cfmv32ah mvfx0, mvax2
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX2H]
+ cfmv32al mvfx0, mvax3
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX3L]
+ cfmv32am mvfx0, mvax3
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX3M]
+ cfmv32ah mvfx0, mvax3
+ cfstr32 mvfx0, [r1, #CRUNCH_MVAX3H]
+
+ cfmv32sc mvdx0, dspsc @ save status word
+ cfstr64 mvdx0, [r1, #CRUNCH_DSPSC]
+
+ teq r0, #0 @ anything to load?
+ cfldr64eq mvdx0, [r1, #CRUNCH_MVDX0] @ mvdx0 was clobbered
+ moveq pc, lr
+
+crunch_load:
+ cfldr64 mvdx0, [r0, #CRUNCH_DSPSC] @ load status word
+ cfmvsc32 dspsc, mvdx0
+
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX0L] @ load 72b accumulators
+ cfmval32 mvax0, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX0M]
+ cfmvam32 mvax0, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX0H]
+ cfmvah32 mvax0, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX1L]
+ cfmval32 mvax1, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX1M]
+ cfmvam32 mvax1, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX1H]
+ cfmvah32 mvax1, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX2L]
+ cfmval32 mvax2, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX2M]
+ cfmvam32 mvax2, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX2H]
+ cfmvah32 mvax2, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX3L]
+ cfmval32 mvax3, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX3M]
+ cfmvam32 mvax3, mvfx0
+ cfldr32 mvfx0, [r0, #CRUNCH_MVAX3H]
+ cfmvah32 mvax3, mvfx0
+
+ cfldr64 mvdx0, [r0, #CRUNCH_MVDX0] @ load 64b registers
+ cfldr64 mvdx1, [r0, #CRUNCH_MVDX1]
+ cfldr64 mvdx2, [r0, #CRUNCH_MVDX2]
+ cfldr64 mvdx3, [r0, #CRUNCH_MVDX3]
+ cfldr64 mvdx4, [r0, #CRUNCH_MVDX4]
+ cfldr64 mvdx5, [r0, #CRUNCH_MVDX5]
+ cfldr64 mvdx6, [r0, #CRUNCH_MVDX6]
+ cfldr64 mvdx7, [r0, #CRUNCH_MVDX7]
+ cfldr64 mvdx8, [r0, #CRUNCH_MVDX8]
+ cfldr64 mvdx9, [r0, #CRUNCH_MVDX9]
+ cfldr64 mvdx10, [r0, #CRUNCH_MVDX10]
+ cfldr64 mvdx11, [r0, #CRUNCH_MVDX11]
+ cfldr64 mvdx12, [r0, #CRUNCH_MVDX12]
+ cfldr64 mvdx13, [r0, #CRUNCH_MVDX13]
+ cfldr64 mvdx14, [r0, #CRUNCH_MVDX14]
+ cfldr64 mvdx15, [r0, #CRUNCH_MVDX15]
+
+ mov pc, lr
+
+/*
+ * Back up crunch regs to save area and disable access to them
+ * (mainly for gdb or sleep mode usage)
+ *
+ * r0 = struct thread_info pointer of target task or NULL for any
+ */
+ENTRY(crunch_task_disable)
+ stmfd sp!, {r4, r5, lr}
+
+ mrs ip, cpsr
+ orr r2, ip, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, r2
+
+ ldr r4, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr
+
+ ldr r3, =crunch_owner
+ add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
+ ldr r1, [r3] @ get current crunch owner
+ teq r1, #0 @ any current owner?
+ beq 1f @ no: quit
+ teq r0, #0 @ any owner?
+ teqne r1, r2 @ or specified one?
+ bne 1f @ no: quit
+
+ ldr r5, [r4, #0x80] @ enable access to crunch
+ mov r2, #0xaa
+ str r2, [r4, #0xc0]
+ orr r5, r5, #0x00800000
+ str r5, [r4, #0x80]
+
+ mov r0, #0 @ nothing to load
+ str r0, [r3] @ no more current owner
+ ldr r2, [r4, #0x80] @ flush out enable (@@@)
+ mov r2, r2
+ bl crunch_save
+
+ mov r2, #0xaa @ disable access to crunch
+ str r2, [r4, #0xc0]
+ bic r5, r5, #0x00800000
+ str r5, [r4, #0x80]
+ ldr r5, [r4, #0x80] @ flush out enable (@@@)
+ mov r5, r5
+
+1: msr cpsr_c, ip @ restore interrupt mode
+ ldmfd sp!, {r4, r5, pc}
+
+/*
+ * Copy crunch state to given memory address
+ *
+ * r0 = struct thread_info pointer of target task
+ * r1 = memory address where to store crunch state
+ *
+ * this is called mainly in the creation of signal stack frames
+ */
+ENTRY(crunch_task_copy)
+ mrs ip, cpsr
+ orr r2, ip, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, r2
+
+ ldr r3, =crunch_owner
+ add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
+ ldr r3, [r3] @ get current crunch owner
+ teq r2, r3 @ does this task own it...
+ beq 1f
+
+ @ current crunch values are in the task save area
+ msr cpsr_c, ip @ restore interrupt mode
+ mov r0, r1
+ mov r1, r2
+ mov r2, #CRUNCH_SIZE
+ b memcpy
+
+1: @ this task owns crunch regs -- grab a copy from there
+ mov r0, #0 @ nothing to load
+ mov r3, lr @ preserve return address
+ bl crunch_save
+ msr cpsr_c, ip @ restore interrupt mode
+ mov pc, r3
+
+/*
+ * Restore crunch state from given memory address
+ *
+ * r0 = struct thread_info pointer of target task
+ * r1 = memory address where to get crunch state from
+ *
+ * this is used to restore crunch state when unwinding a signal stack frame
+ */
+ENTRY(crunch_task_restore)
+ mrs ip, cpsr
+ orr r2, ip, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, r2
+
+ ldr r3, =crunch_owner
+ add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
+ ldr r3, [r3] @ get current crunch owner
+ teq r2, r3 @ does this task own it...
+ beq 1f
+
+ @ this task doesn't own crunch regs -- use its save area
+ msr cpsr_c, ip @ restore interrupt mode
+ mov r0, r2
+ mov r2, #CRUNCH_SIZE
+ b memcpy
+
+1: @ this task owns crunch regs -- load them directly
+ mov r0, r1
+ mov r1, #0 @ nothing to save
+ mov r3, lr @ preserve return address
+ bl crunch_load
+ msr cpsr_c, ip @ restore interrupt mode
+ mov pc, r3
diff --git a/arch/arm/kernel/crunch.c b/arch/arm/kernel/crunch.c
new file mode 100644
index 0000000..99995c2
--- /dev/null
+++ b/arch/arm/kernel/crunch.c
@@ -0,0 +1,83 @@
+/*
+ * arch/arm/kernel/crunch.c
+ * Cirrus MaverickCrunch context switching and handling
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <mach/ep93xx-regs.h>
+#include <asm/thread_notify.h>
+
+struct crunch_state *crunch_owner;
+
+void crunch_task_release(struct thread_info *thread)
+{
+ local_irq_disable();
+ if (crunch_owner == &thread->crunchstate)
+ crunch_owner = NULL;
+ local_irq_enable();
+}
+
+static int crunch_enabled(u32 devcfg)
+{
+ return !!(devcfg & EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE);
+}
+
+static int crunch_do(struct notifier_block *self, unsigned long cmd, void *t)
+{
+ struct thread_info *thread = (struct thread_info *)t;
+ struct crunch_state *crunch_state;
+ u32 devcfg;
+
+ crunch_state = &thread->crunchstate;
+
+ switch (cmd) {
+ case THREAD_NOTIFY_FLUSH:
+ memset(crunch_state, 0, sizeof(*crunch_state));
+
+ /*
+ * FALLTHROUGH: Ensure we don't try to overwrite our newly
+ * initialised state information on the first fault.
+ */
+
+ case THREAD_NOTIFY_RELEASE:
+ crunch_task_release(thread);
+ break;
+
+ case THREAD_NOTIFY_SWITCH:
+ devcfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
+ if (crunch_enabled(devcfg) || crunch_owner == crunch_state) {
+ devcfg ^= EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE;
+ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+ __raw_writel(devcfg, EP93XX_SYSCON_DEVICE_CONFIG);
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block crunch_notifier_block = {
+ .notifier_call = crunch_do,
+};
+
+static int __init crunch_init(void)
+{
+ thread_register_notifier(&crunch_notifier_block);
+ elf_hwcap |= HWCAP_CRUNCH;
+
+ return 0;
+}
+
+late_initcall(crunch_init);
diff --git a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S
new file mode 100644
index 0000000..f53c582
--- /dev/null
+++ b/arch/arm/kernel/debug.S
@@ -0,0 +1,142 @@
+/*
+ * linux/arch/arm/kernel/debug.S
+ *
+ * Copyright (C) 1994-1999 Russell King
+ *
+ * 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.
+ *
+ * 32-bit debugging code
+ */
+#include <linux/linkage.h>
+
+ .text
+
+/*
+ * Some debugging routines (useful if you've got MM problems and
+ * printk isn't working). For DEBUGGING ONLY!!! Do not leave
+ * references to these in a production kernel!
+ */
+
+#if defined(CONFIG_DEBUG_ICEDCC)
+ @@ debug using ARM EmbeddedICE DCC channel
+
+#if defined(CONFIG_CPU_V6)
+
+ .macro addruart, rx
+ .endm
+
+ .macro senduart, rd, rx
+ mcr p14, 0, \rd, c0, c5, 0
+ .endm
+
+ .macro busyuart, rd, rx
+1001:
+ mrc p14, 0, \rx, c0, c1, 0
+ tst \rx, #0x20000000
+ beq 1001b
+ .endm
+
+ .macro waituart, rd, rx
+ mov \rd, #0x2000000
+1001:
+ subs \rd, \rd, #1
+ bmi 1002f
+ mrc p14, 0, \rx, c0, c1, 0
+ tst \rx, #0x20000000
+ bne 1001b
+1002:
+ .endm
+
+#else
+
+ .macro addruart, rx
+ .endm
+
+ .macro senduart, rd, rx
+ mcr p14, 0, \rd, c1, c0, 0
+ .endm
+
+ .macro busyuart, rd, rx
+1001:
+ mrc p14, 0, \rx, c0, c0, 0
+ tst \rx, #2
+ beq 1001b
+
+ .endm
+
+ .macro waituart, rd, rx
+ mov \rd, #0x2000000
+1001:
+ subs \rd, \rd, #1
+ bmi 1002f
+ mrc p14, 0, \rx, c0, c0, 0
+ tst \rx, #2
+ bne 1001b
+1002:
+ .endm
+
+#endif /* CONFIG_CPU_V6 */
+
+#else
+#include <mach/debug-macro.S>
+#endif /* CONFIG_DEBUG_ICEDCC */
+
+/*
+ * Useful debugging routines
+ */
+ENTRY(printhex8)
+ mov r1, #8
+ b printhex
+ENDPROC(printhex8)
+
+ENTRY(printhex4)
+ mov r1, #4
+ b printhex
+ENDPROC(printhex4)
+
+ENTRY(printhex2)
+ mov r1, #2
+printhex: adr r2, hexbuf
+ add r3, r2, r1
+ mov r1, #0
+ strb r1, [r3]
+1: and r1, r0, #15
+ mov r0, r0, lsr #4
+ cmp r1, #10
+ addlt r1, r1, #'0'
+ addge r1, r1, #'a' - 10
+ strb r1, [r3, #-1]!
+ teq r3, r2
+ bne 1b
+ mov r0, r2
+ b printascii
+ENDPROC(printhex2)
+
+ .ltorg
+
+ENTRY(printascii)
+ addruart r3
+ b 2f
+1: waituart r2, r3
+ senduart r1, r3
+ busyuart r2, r3
+ teq r1, #'\n'
+ moveq r1, #'\r'
+ beq 1b
+2: teq r0, #0
+ ldrneb r1, [r0], #1
+ teqne r1, #0
+ bne 1b
+ mov pc, lr
+ENDPROC(printascii)
+
+ENTRY(printch)
+ addruart r3
+ mov r1, r0
+ mov r0, #0
+ b 1b
+ENDPROC(printch)
+
+hexbuf: .space 16
diff --git a/arch/arm/kernel/dma-isa.c b/arch/arm/kernel/dma-isa.c
new file mode 100644
index 0000000..4a3a504
--- /dev/null
+++ b/arch/arm/kernel/dma-isa.c
@@ -0,0 +1,221 @@
+/*
+ * linux/arch/arm/kernel/dma-isa.c
+ *
+ * Copyright (C) 1999-2000 Russell King
+ *
+ * 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.
+ *
+ * ISA DMA primitives
+ * Taken from various sources, including:
+ * linux/include/asm/dma.h: Defines for using and allocating dma channels.
+ * Written by Hennus Bergman, 1992.
+ * High DMA channel support & info by Hannu Savolainen and John Boyd,
+ * Nov. 1992.
+ * arch/arm/kernel/dma-ebsa285.c
+ * Copyright (C) 1998 Phil Blundell
+ */
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+
+#define ISA_DMA_MODE_READ 0x44
+#define ISA_DMA_MODE_WRITE 0x48
+#define ISA_DMA_MODE_CASCADE 0xc0
+#define ISA_DMA_AUTOINIT 0x10
+
+#define ISA_DMA_MASK 0
+#define ISA_DMA_MODE 1
+#define ISA_DMA_CLRFF 2
+#define ISA_DMA_PGHI 3
+#define ISA_DMA_PGLO 4
+#define ISA_DMA_ADDR 5
+#define ISA_DMA_COUNT 6
+
+static unsigned int isa_dma_port[8][7] = {
+ /* MASK MODE CLRFF PAGE_HI PAGE_LO ADDR COUNT */
+ { 0x0a, 0x0b, 0x0c, 0x487, 0x087, 0x00, 0x01 },
+ { 0x0a, 0x0b, 0x0c, 0x483, 0x083, 0x02, 0x03 },
+ { 0x0a, 0x0b, 0x0c, 0x481, 0x081, 0x04, 0x05 },
+ { 0x0a, 0x0b, 0x0c, 0x482, 0x082, 0x06, 0x07 },
+ { 0xd4, 0xd6, 0xd8, 0x000, 0x000, 0xc0, 0xc2 },
+ { 0xd4, 0xd6, 0xd8, 0x48b, 0x08b, 0xc4, 0xc6 },
+ { 0xd4, 0xd6, 0xd8, 0x489, 0x089, 0xc8, 0xca },
+ { 0xd4, 0xd6, 0xd8, 0x48a, 0x08a, 0xcc, 0xce }
+};
+
+static int isa_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+ unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
+ int count;
+
+ count = 1 + inb(io_port);
+ count |= inb(io_port) << 8;
+
+ return channel < 4 ? count : (count << 1);
+}
+
+static void isa_enable_dma(dmach_t channel, dma_t *dma)
+{
+ if (dma->invalid) {
+ unsigned long address, length;
+ unsigned int mode;
+ enum dma_data_direction direction;
+
+ mode = channel & 3;
+ switch (dma->dma_mode & DMA_MODE_MASK) {
+ case DMA_MODE_READ:
+ mode |= ISA_DMA_MODE_READ;
+ direction = DMA_FROM_DEVICE;
+ break;
+
+ case DMA_MODE_WRITE:
+ mode |= ISA_DMA_MODE_WRITE;
+ direction = DMA_TO_DEVICE;
+ break;
+
+ case DMA_MODE_CASCADE:
+ mode |= ISA_DMA_MODE_CASCADE;
+ direction = DMA_BIDIRECTIONAL;
+ break;
+
+ default:
+ direction = DMA_NONE;
+ break;
+ }
+
+ if (!dma->sg) {
+ /*
+ * Cope with ISA-style drivers which expect cache
+ * coherence.
+ */
+ dma->sg = &dma->buf;
+ dma->sgcount = 1;
+ dma->buf.length = dma->count;
+ dma->buf.dma_address = dma_map_single(NULL,
+ dma->addr, dma->count,
+ direction);
+ }
+
+ address = dma->buf.dma_address;
+ length = dma->buf.length - 1;
+
+ outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
+ outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
+
+ if (channel >= 4) {
+ address >>= 1;
+ length >>= 1;
+ }
+
+ outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
+
+ outb(address, isa_dma_port[channel][ISA_DMA_ADDR]);
+ outb(address >> 8, isa_dma_port[channel][ISA_DMA_ADDR]);
+
+ outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
+ outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
+
+ if (dma->dma_mode & DMA_AUTOINIT)
+ mode |= ISA_DMA_AUTOINIT;
+
+ outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
+ dma->invalid = 0;
+ }
+ outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
+}
+
+static void isa_disable_dma(dmach_t channel, dma_t *dma)
+{
+ outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
+}
+
+static struct dma_ops isa_dma_ops = {
+ .type = "ISA",
+ .enable = isa_enable_dma,
+ .disable = isa_disable_dma,
+ .residue = isa_get_dma_residue,
+};
+
+static struct resource dma_resources[] = { {
+ .name = "dma1",
+ .start = 0x0000,
+ .end = 0x000f
+}, {
+ .name = "dma low page",
+ .start = 0x0080,
+ .end = 0x008f
+}, {
+ .name = "dma2",
+ .start = 0x00c0,
+ .end = 0x00df
+}, {
+ .name = "dma high page",
+ .start = 0x0480,
+ .end = 0x048f
+} };
+
+void __init isa_init_dma(dma_t *dma)
+{
+ /*
+ * Try to autodetect presence of an ISA DMA controller.
+ * We do some minimal initialisation, and check that
+ * channel 0's DMA address registers are writeable.
+ */
+ outb(0xff, 0x0d);
+ outb(0xff, 0xda);
+
+ /*
+ * Write high and low address, and then read them back
+ * in the same order.
+ */
+ outb(0x55, 0x00);
+ outb(0xaa, 0x00);
+
+ if (inb(0) == 0x55 && inb(0) == 0xaa) {
+ int channel, i;
+
+ for (channel = 0; channel < 8; channel++) {
+ dma[channel].d_ops = &isa_dma_ops;
+ isa_disable_dma(channel, NULL);
+ }
+
+ outb(0x40, 0x0b);
+ outb(0x41, 0x0b);
+ outb(0x42, 0x0b);
+ outb(0x43, 0x0b);
+
+ outb(0xc0, 0xd6);
+ outb(0x41, 0xd6);
+ outb(0x42, 0xd6);
+ outb(0x43, 0xd6);
+
+ outb(0, 0xd4);
+
+ outb(0x10, 0x08);
+ outb(0x10, 0xd0);
+
+ /*
+ * Is this correct? According to my documentation, it
+ * doesn't appear to be. It should be:
+ * outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
+ */
+ outb(0x30, 0x40b);
+ outb(0x31, 0x40b);
+ outb(0x32, 0x40b);
+ outb(0x33, 0x40b);
+ outb(0x31, 0x4d6);
+ outb(0x32, 0x4d6);
+ outb(0x33, 0x4d6);
+
+ request_dma(DMA_ISA_CASCADE, "cascade");
+
+ for (i = 0; i < ARRAY_SIZE(dma_resources); i++)
+ request_resource(&ioport_resource, dma_resources + i);
+ }
+}
diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c
new file mode 100644
index 0000000..d006085
--- /dev/null
+++ b/arch/arm/kernel/dma.c
@@ -0,0 +1,251 @@
+/*
+ * linux/arch/arm/kernel/dma.c
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * 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.
+ *
+ * Front-end to the DMA handling. This handles the allocation/freeing
+ * of DMA channels, and provides a unified interface to the machines
+ * DMA facilities.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+
+#include <asm/dma.h>
+
+#include <asm/mach/dma.h>
+
+DEFINE_SPINLOCK(dma_spin_lock);
+EXPORT_SYMBOL(dma_spin_lock);
+
+static dma_t dma_chan[MAX_DMA_CHANNELS];
+
+/*
+ * Request DMA channel
+ *
+ * On certain platforms, we have to allocate an interrupt as well...
+ */
+int request_dma(dmach_t channel, const char *device_id)
+{
+ dma_t *dma = dma_chan + channel;
+ int ret;
+
+ if (channel >= MAX_DMA_CHANNELS || !dma->d_ops)
+ goto bad_dma;
+
+ if (xchg(&dma->lock, 1) != 0)
+ goto busy;
+
+ dma->device_id = device_id;
+ dma->active = 0;
+ dma->invalid = 1;
+
+ ret = 0;
+ if (dma->d_ops->request)
+ ret = dma->d_ops->request(channel, dma);
+
+ if (ret)
+ xchg(&dma->lock, 0);
+
+ return ret;
+
+bad_dma:
+ printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel);
+ return -EINVAL;
+
+busy:
+ return -EBUSY;
+}
+EXPORT_SYMBOL(request_dma);
+
+/*
+ * Free DMA channel
+ *
+ * On certain platforms, we have to free interrupt as well...
+ */
+void free_dma(dmach_t channel)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (channel >= MAX_DMA_CHANNELS || !dma->d_ops)
+ goto bad_dma;
+
+ if (dma->active) {
+ printk(KERN_ERR "dma%d: freeing active DMA\n", channel);
+ dma->d_ops->disable(channel, dma);
+ dma->active = 0;
+ }
+
+ if (xchg(&dma->lock, 0) != 0) {
+ if (dma->d_ops->free)
+ dma->d_ops->free(channel, dma);
+ return;
+ }
+
+ printk(KERN_ERR "dma%d: trying to free free DMA\n", channel);
+ return;
+
+bad_dma:
+ printk(KERN_ERR "dma: trying to free DMA%d\n", channel);
+}
+EXPORT_SYMBOL(free_dma);
+
+/* Set DMA Scatter-Gather list
+ */
+void set_dma_sg (dmach_t channel, struct scatterlist *sg, int nr_sg)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (dma->active)
+ printk(KERN_ERR "dma%d: altering DMA SG while "
+ "DMA active\n", channel);
+
+ dma->sg = sg;
+ dma->sgcount = nr_sg;
+ dma->invalid = 1;
+}
+EXPORT_SYMBOL(set_dma_sg);
+
+/* Set DMA address
+ *
+ * Copy address to the structure, and set the invalid bit
+ */
+void __set_dma_addr (dmach_t channel, void *addr)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (dma->active)
+ printk(KERN_ERR "dma%d: altering DMA address while "
+ "DMA active\n", channel);
+
+ dma->sg = NULL;
+ dma->addr = addr;
+ dma->invalid = 1;
+}
+EXPORT_SYMBOL(__set_dma_addr);
+
+/* Set DMA byte count
+ *
+ * Copy address to the structure, and set the invalid bit
+ */
+void set_dma_count (dmach_t channel, unsigned long count)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (dma->active)
+ printk(KERN_ERR "dma%d: altering DMA count while "
+ "DMA active\n", channel);
+
+ dma->sg = NULL;
+ dma->count = count;
+ dma->invalid = 1;
+}
+EXPORT_SYMBOL(set_dma_count);
+
+/* Set DMA direction mode
+ */
+void set_dma_mode (dmach_t channel, dmamode_t mode)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (dma->active)
+ printk(KERN_ERR "dma%d: altering DMA mode while "
+ "DMA active\n", channel);
+
+ dma->dma_mode = mode;
+ dma->invalid = 1;
+}
+EXPORT_SYMBOL(set_dma_mode);
+
+/* Enable DMA channel
+ */
+void enable_dma (dmach_t channel)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (!dma->lock)
+ goto free_dma;
+
+ if (dma->active == 0) {
+ dma->active = 1;
+ dma->d_ops->enable(channel, dma);
+ }
+ return;
+
+free_dma:
+ printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel);
+ BUG();
+}
+EXPORT_SYMBOL(enable_dma);
+
+/* Disable DMA channel
+ */
+void disable_dma (dmach_t channel)
+{
+ dma_t *dma = dma_chan + channel;
+
+ if (!dma->lock)
+ goto free_dma;
+
+ if (dma->active == 1) {
+ dma->active = 0;
+ dma->d_ops->disable(channel, dma);
+ }
+ return;
+
+free_dma:
+ printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel);
+ BUG();
+}
+EXPORT_SYMBOL(disable_dma);
+
+/*
+ * Is the specified DMA channel active?
+ */
+int dma_channel_active(dmach_t channel)
+{
+ return dma_chan[channel].active;
+}
+EXPORT_SYMBOL(dma_channel_active);
+
+void set_dma_page(dmach_t channel, char pagenr)
+{
+ printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel);
+}
+EXPORT_SYMBOL(set_dma_page);
+
+void set_dma_speed(dmach_t channel, int cycle_ns)
+{
+ dma_t *dma = dma_chan + channel;
+ int ret = 0;
+
+ if (dma->d_ops->setspeed)
+ ret = dma->d_ops->setspeed(channel, dma, cycle_ns);
+ dma->speed = ret;
+}
+EXPORT_SYMBOL(set_dma_speed);
+
+int get_dma_residue(dmach_t channel)
+{
+ dma_t *dma = dma_chan + channel;
+ int ret = 0;
+
+ if (dma->d_ops->residue)
+ ret = dma->d_ops->residue(channel, dma);
+
+ return ret;
+}
+EXPORT_SYMBOL(get_dma_residue);
+
+static int __init init_dma(void)
+{
+ arch_dma_init(dma_chan);
+ return 0;
+}
+
+core_initcall(init_dma);
diff --git a/arch/arm/kernel/early_printk.c b/arch/arm/kernel/early_printk.c
new file mode 100644
index 0000000..85aa2b2
--- /dev/null
+++ b/arch/arm/kernel/early_printk.c
@@ -0,0 +1,57 @@
+/*
+ * linux/arch/arm/kernel/early_printk.c
+ *
+ * Copyright (C) 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * 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/console.h>
+#include <linux/init.h>
+
+extern void printch(int);
+
+static void early_write(const char *s, unsigned n)
+{
+ while (n-- > 0) {
+ if (*s == '\n')
+ printch('\r');
+ printch(*s);
+ s++;
+ }
+}
+
+static void early_console_write(struct console *con, const char *s, unsigned n)
+{
+ early_write(s, n);
+}
+
+static struct console early_console = {
+ .name = "earlycon",
+ .write = early_console_write,
+ .flags = CON_PRINTBUFFER | CON_BOOT,
+ .index = -1,
+};
+
+asmlinkage void early_printk(const char *fmt, ...)
+{
+ char buf[512];
+ int n;
+ va_list ap;
+
+ va_start(ap, fmt);
+ n = vscnprintf(buf, sizeof(buf), fmt, ap);
+ early_write(buf, n);
+ va_end(ap);
+}
+
+static int __init setup_early_printk(char *buf)
+{
+ register_console(&early_console);
+ return 0;
+}
+
+early_param("earlyprintk", setup_early_printk);
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c
new file mode 100644
index 0000000..60c079d
--- /dev/null
+++ b/arch/arm/kernel/ecard.c
@@ -0,0 +1,1241 @@
+/*
+ * linux/arch/arm/kernel/ecard.c
+ *
+ * Copyright 1995-2001 Russell King
+ *
+ * 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.
+ *
+ * Find all installed expansion cards, and handle interrupts from them.
+ *
+ * Created from information from Acorns RiscOS3 PRMs
+ *
+ * 08-Dec-1996 RMK Added code for the 9'th expansion card - the ether
+ * podule slot.
+ * 06-May-1997 RMK Added blacklist for cards whose loader doesn't work.
+ * 12-Sep-1997 RMK Created new handling of interrupt enables/disables
+ * - cards can now register their own routine to control
+ * interrupts (recommended).
+ * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled
+ * on reset from Linux. (Caused cards not to respond
+ * under RiscOS without hard reset).
+ * 15-Feb-1998 RMK Added DMA support
+ * 12-Sep-1998 RMK Added EASI support
+ * 10-Jan-1999 RMK Run loaders in a simulated RISC OS environment.
+ * 17-Apr-1999 RMK Support for EASI Type C cycles.
+ */
+#define ECARD_C
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/reboot.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/mmu_context.h>
+#include <asm/mach/irq.h>
+#include <asm/tlbflush.h>
+
+#include "ecard.h"
+
+#ifndef CONFIG_ARCH_RPC
+#define HAVE_EXPMASK
+#endif
+
+struct ecard_request {
+ void (*fn)(struct ecard_request *);
+ ecard_t *ec;
+ unsigned int address;
+ unsigned int length;
+ unsigned int use_loader;
+ void *buffer;
+ struct completion *complete;
+};
+
+struct expcard_blacklist {
+ unsigned short manufacturer;
+ unsigned short product;
+ const char *type;
+};
+
+static ecard_t *cards;
+static ecard_t *slot_to_expcard[MAX_ECARDS];
+static unsigned int ectcr;
+#ifdef HAS_EXPMASK
+static unsigned int have_expmask;
+#endif
+
+/* List of descriptions of cards which don't have an extended
+ * identification, or chunk directories containing a description.
+ */
+static struct expcard_blacklist __initdata blacklist[] = {
+ { MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
+};
+
+asmlinkage extern int
+ecard_loader_reset(unsigned long base, loader_t loader);
+asmlinkage extern int
+ecard_loader_read(int off, unsigned long base, loader_t loader);
+
+static inline unsigned short ecard_getu16(unsigned char *v)
+{
+ return v[0] | v[1] << 8;
+}
+
+static inline signed long ecard_gets24(unsigned char *v)
+{
+ return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
+}
+
+static inline ecard_t *slot_to_ecard(unsigned int slot)
+{
+ return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
+}
+
+/* ===================== Expansion card daemon ======================== */
+/*
+ * Since the loader programs on the expansion cards need to be run
+ * in a specific environment, create a separate task with this
+ * environment up, and pass requests to this task as and when we
+ * need to.
+ *
+ * This should allow 99% of loaders to be called from Linux.
+ *
+ * From a security standpoint, we trust the card vendors. This
+ * may be a misplaced trust.
+ */
+static void ecard_task_reset(struct ecard_request *req)
+{
+ struct expansion_card *ec = req->ec;
+ struct resource *res;
+
+ res = ec->slot_no == 8
+ ? &ec->resource[ECARD_RES_MEMC]
+ : ec->easi
+ ? &ec->resource[ECARD_RES_EASI]
+ : &ec->resource[ECARD_RES_IOCSYNC];
+
+ ecard_loader_reset(res->start, ec->loader);
+}
+
+static void ecard_task_readbytes(struct ecard_request *req)
+{
+ struct expansion_card *ec = req->ec;
+ unsigned char *buf = req->buffer;
+ unsigned int len = req->length;
+ unsigned int off = req->address;
+
+ if (ec->slot_no == 8) {
+ void __iomem *base = (void __iomem *)
+ ec->resource[ECARD_RES_MEMC].start;
+
+ /*
+ * The card maintains an index which increments the address
+ * into a 4096-byte page on each access. We need to keep
+ * track of the counter.
+ */
+ static unsigned int index;
+ unsigned int page;
+
+ page = (off >> 12) * 4;
+ if (page > 256 * 4)
+ return;
+
+ off &= 4095;
+
+ /*
+ * If we are reading offset 0, or our current index is
+ * greater than the offset, reset the hardware index counter.
+ */
+ if (off == 0 || index > off) {
+ writeb(0, base);
+ index = 0;
+ }
+
+ /*
+ * Increment the hardware index counter until we get to the
+ * required offset. The read bytes are discarded.
+ */
+ while (index < off) {
+ readb(base + page);
+ index += 1;
+ }
+
+ while (len--) {
+ *buf++ = readb(base + page);
+ index += 1;
+ }
+ } else {
+ unsigned long base = (ec->easi
+ ? &ec->resource[ECARD_RES_EASI]
+ : &ec->resource[ECARD_RES_IOCSYNC])->start;
+ void __iomem *pbase = (void __iomem *)base;
+
+ if (!req->use_loader || !ec->loader) {
+ off *= 4;
+ while (len--) {
+ *buf++ = readb(pbase + off);
+ off += 4;
+ }
+ } else {
+ while(len--) {
+ /*
+ * The following is required by some
+ * expansion card loader programs.
+ */
+ *(unsigned long *)0x108 = 0;
+ *buf++ = ecard_loader_read(off++, base,
+ ec->loader);
+ }
+ }
+ }
+
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(ecard_wait);
+static struct ecard_request *ecard_req;
+static DEFINE_MUTEX(ecard_mutex);
+
+/*
+ * Set up the expansion card daemon's page tables.
+ */
+static void ecard_init_pgtables(struct mm_struct *mm)
+{
+ struct vm_area_struct vma;
+
+ /* We want to set up the page tables for the following mapping:
+ * Virtual Physical
+ * 0x03000000 0x03000000
+ * 0x03010000 unmapped
+ * 0x03210000 0x03210000
+ * 0x03400000 unmapped
+ * 0x08000000 0x08000000
+ * 0x10000000 unmapped
+ *
+ * FIXME: we don't follow this 100% yet.
+ */
+ pgd_t *src_pgd, *dst_pgd;
+
+ src_pgd = pgd_offset(mm, (unsigned long)IO_BASE);
+ dst_pgd = pgd_offset(mm, IO_START);
+
+ memcpy(dst_pgd, src_pgd, sizeof(pgd_t) * (IO_SIZE / PGDIR_SIZE));
+
+ src_pgd = pgd_offset(mm, EASI_BASE);
+ dst_pgd = pgd_offset(mm, EASI_START);
+
+ memcpy(dst_pgd, src_pgd, sizeof(pgd_t) * (EASI_SIZE / PGDIR_SIZE));
+
+ vma.vm_mm = mm;
+
+ flush_tlb_range(&vma, IO_START, IO_START + IO_SIZE);
+ flush_tlb_range(&vma, EASI_START, EASI_START + EASI_SIZE);
+}
+
+static int ecard_init_mm(void)
+{
+ struct mm_struct * mm = mm_alloc();
+ struct mm_struct *active_mm = current->active_mm;
+
+ if (!mm)
+ return -ENOMEM;
+
+ current->mm = mm;
+ current->active_mm = mm;
+ activate_mm(active_mm, mm);
+ mmdrop(active_mm);
+ ecard_init_pgtables(mm);
+ return 0;
+}
+
+static int
+ecard_task(void * unused)
+{
+ /*
+ * Allocate a mm. We're not a lazy-TLB kernel task since we need
+ * to set page table entries where the user space would be. Note
+ * that this also creates the page tables. Failure is not an
+ * option here.
+ */
+ if (ecard_init_mm())
+ panic("kecardd: unable to alloc mm\n");
+
+ while (1) {
+ struct ecard_request *req;
+
+ wait_event_interruptible(ecard_wait, ecard_req != NULL);
+
+ req = xchg(&ecard_req, NULL);
+ if (req != NULL) {
+ req->fn(req);
+ complete(req->complete);
+ }
+ }
+}
+
+/*
+ * Wake the expansion card daemon to action our request.
+ *
+ * FIXME: The test here is not sufficient to detect if the
+ * kcardd is running.
+ */
+static void ecard_call(struct ecard_request *req)
+{
+ DECLARE_COMPLETION_ONSTACK(completion);
+
+ req->complete = &completion;
+
+ mutex_lock(&ecard_mutex);
+ ecard_req = req;
+ wake_up(&ecard_wait);
+
+ /*
+ * Now wait for kecardd to run.
+ */
+ wait_for_completion(&completion);
+ mutex_unlock(&ecard_mutex);
+}
+
+/* ======================= Mid-level card control ===================== */
+
+static void
+ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+{
+ struct ecard_request req;
+
+ req.fn = ecard_task_readbytes;
+ req.ec = ec;
+ req.address = off;
+ req.length = len;
+ req.use_loader = useld;
+ req.buffer = addr;
+
+ ecard_call(&req);
+}
+
+int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+{
+ struct ex_chunk_dir excd;
+ int index = 16;
+ int useld = 0;
+
+ if (!ec->cid.cd)
+ return 0;
+
+ while(1) {
+ ecard_readbytes(&excd, ec, index, 8, useld);
+ index += 8;
+ if (c_id(&excd) == 0) {
+ if (!useld && ec->loader) {
+ useld = 1;
+ index = 0;
+ continue;
+ }
+ return 0;
+ }
+ if (c_id(&excd) == 0xf0) { /* link */
+ index = c_start(&excd);
+ continue;
+ }
+ if (c_id(&excd) == 0x80) { /* loader */
+ if (!ec->loader) {
+ ec->loader = kmalloc(c_len(&excd),
+ GFP_KERNEL);
+ if (ec->loader)
+ ecard_readbytes(ec->loader, ec,
+ (int)c_start(&excd),
+ c_len(&excd), useld);
+ else
+ return 0;
+ }
+ continue;
+ }
+ if (c_id(&excd) == id && num-- == 0)
+ break;
+ }
+
+ if (c_id(&excd) & 0x80) {
+ switch (c_id(&excd) & 0x70) {
+ case 0x70:
+ ecard_readbytes((unsigned char *)excd.d.string, ec,
+ (int)c_start(&excd), c_len(&excd),
+ useld);
+ break;
+ case 0x00:
+ break;
+ }
+ }
+ cd->start_offset = c_start(&excd);
+ memcpy(cd->d.string, excd.d.string, 256);
+ return 1;
+}
+
+/* ======================= Interrupt control ============================ */
+
+static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
+{
+#ifdef HAS_EXPMASK
+ if (irqnr < 4 && have_expmask) {
+ have_expmask |= 1 << irqnr;
+ __raw_writeb(have_expmask, EXPMASK_ENABLE);
+ }
+#endif
+}
+
+static void ecard_def_irq_disable(ecard_t *ec, int irqnr)
+{
+#ifdef HAS_EXPMASK
+ if (irqnr < 4 && have_expmask) {
+ have_expmask &= ~(1 << irqnr);
+ __raw_writeb(have_expmask, EXPMASK_ENABLE);
+ }
+#endif
+}
+
+static int ecard_def_irq_pending(ecard_t *ec)
+{
+ return !ec->irqmask || readb(ec->irqaddr) & ec->irqmask;
+}
+
+static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
+{
+ panic("ecard_def_fiq_enable called - impossible");
+}
+
+static void ecard_def_fiq_disable(ecard_t *ec, int fiqnr)
+{
+ panic("ecard_def_fiq_disable called - impossible");
+}
+
+static int ecard_def_fiq_pending(ecard_t *ec)
+{
+ return !ec->fiqmask || readb(ec->fiqaddr) & ec->fiqmask;
+}
+
+static expansioncard_ops_t ecard_default_ops = {
+ ecard_def_irq_enable,
+ ecard_def_irq_disable,
+ ecard_def_irq_pending,
+ ecard_def_fiq_enable,
+ ecard_def_fiq_disable,
+ ecard_def_fiq_pending
+};
+
+/*
+ * Enable and disable interrupts from expansion cards.
+ * (interrupts are disabled for these functions).
+ *
+ * They are not meant to be called directly, but via enable/disable_irq.
+ */
+static void ecard_irq_unmask(unsigned int irqnr)
+{
+ ecard_t *ec = slot_to_ecard(irqnr - 32);
+
+ if (ec) {
+ if (!ec->ops)
+ ec->ops = &ecard_default_ops;
+
+ if (ec->claimed && ec->ops->irqenable)
+ ec->ops->irqenable(ec, irqnr);
+ else
+ printk(KERN_ERR "ecard: rejecting request to "
+ "enable IRQs for %d\n", irqnr);
+ }
+}
+
+static void ecard_irq_mask(unsigned int irqnr)
+{
+ ecard_t *ec = slot_to_ecard(irqnr - 32);
+
+ if (ec) {
+ if (!ec->ops)
+ ec->ops = &ecard_default_ops;
+
+ if (ec->ops && ec->ops->irqdisable)
+ ec->ops->irqdisable(ec, irqnr);
+ }
+}
+
+static struct irq_chip ecard_chip = {
+ .name = "ECARD",
+ .ack = ecard_irq_mask,
+ .mask = ecard_irq_mask,
+ .unmask = ecard_irq_unmask,
+};
+
+void ecard_enablefiq(unsigned int fiqnr)
+{
+ ecard_t *ec = slot_to_ecard(fiqnr);
+
+ if (ec) {
+ if (!ec->ops)
+ ec->ops = &ecard_default_ops;
+
+ if (ec->claimed && ec->ops->fiqenable)
+ ec->ops->fiqenable(ec, fiqnr);
+ else
+ printk(KERN_ERR "ecard: rejecting request to "
+ "enable FIQs for %d\n", fiqnr);
+ }
+}
+
+void ecard_disablefiq(unsigned int fiqnr)
+{
+ ecard_t *ec = slot_to_ecard(fiqnr);
+
+ if (ec) {
+ if (!ec->ops)
+ ec->ops = &ecard_default_ops;
+
+ if (ec->ops->fiqdisable)
+ ec->ops->fiqdisable(ec, fiqnr);
+ }
+}
+
+static void ecard_dump_irq_state(void)
+{
+ ecard_t *ec;
+
+ printk("Expansion card IRQ state:\n");
+
+ for (ec = cards; ec; ec = ec->next) {
+ if (ec->slot_no == 8)
+ continue;
+
+ printk(" %d: %sclaimed, ",
+ ec->slot_no, ec->claimed ? "" : "not ");
+
+ if (ec->ops && ec->ops->irqpending &&
+ ec->ops != &ecard_default_ops)
+ printk("irq %spending\n",
+ ec->ops->irqpending(ec) ? "" : "not ");
+ else
+ printk("irqaddr %p, mask = %02X, status = %02X\n",
+ ec->irqaddr, ec->irqmask, readb(ec->irqaddr));
+ }
+}
+
+static void ecard_check_lockup(struct irq_desc *desc)
+{
+ static unsigned long last;
+ static int lockup;
+
+ /*
+ * If the timer interrupt has not run since the last million
+ * unrecognised expansion card interrupts, then there is
+ * something seriously wrong. Disable the expansion card
+ * interrupts so at least we can continue.
+ *
+ * Maybe we ought to start a timer to re-enable them some time
+ * later?
+ */
+ if (last == jiffies) {
+ lockup += 1;
+ if (lockup > 1000000) {
+ printk(KERN_ERR "\nInterrupt lockup detected - "
+ "disabling all expansion card interrupts\n");
+
+ desc->chip->mask(IRQ_EXPANSIONCARD);
+ ecard_dump_irq_state();
+ }
+ } else
+ lockup = 0;
+
+ /*
+ * If we did not recognise the source of this interrupt,
+ * warn the user, but don't flood the user with these messages.
+ */
+ if (!last || time_after(jiffies, last + 5*HZ)) {
+ last = jiffies;
+ printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
+ ecard_dump_irq_state();
+ }
+}
+
+static void
+ecard_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ ecard_t *ec;
+ int called = 0;
+
+ desc->chip->mask(irq);
+ for (ec = cards; ec; ec = ec->next) {
+ int pending;
+
+ if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8)
+ continue;
+
+ if (ec->ops && ec->ops->irqpending)
+ pending = ec->ops->irqpending(ec);
+ else
+ pending = ecard_default_ops.irqpending(ec);
+
+ if (pending) {
+ generic_handle_irq(ec->irq);
+ called ++;
+ }
+ }
+ desc->chip->unmask(irq);
+
+ if (called == 0)
+ ecard_check_lockup(desc);
+}
+
+#ifdef HAS_EXPMASK
+static unsigned char priority_masks[] =
+{
+ 0xf0, 0xf1, 0xf3, 0xf7, 0xff, 0xff, 0xff, 0xff
+};
+
+static unsigned char first_set[] =
+{
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
+};
+
+static void
+ecard_irqexp_handler(unsigned int irq, struct irq_desc *desc)
+{
+ const unsigned int statusmask = 15;
+ unsigned int status;
+
+ status = __raw_readb(EXPMASK_STATUS) & statusmask;
+ if (status) {
+ unsigned int slot = first_set[status];
+ ecard_t *ec = slot_to_ecard(slot);
+
+ if (ec->claimed) {
+ /*
+ * this ugly code is so that we can operate a
+ * prioritorising system:
+ *
+ * Card 0 highest priority
+ * Card 1
+ * Card 2
+ * Card 3 lowest priority
+ *
+ * Serial cards should go in 0/1, ethernet/scsi in 2/3
+ * otherwise you will lose serial data at high speeds!
+ */
+ generic_handle_irq(ec->irq);
+ } else {
+ printk(KERN_WARNING "card%d: interrupt from unclaimed "
+ "card???\n", slot);
+ have_expmask &= ~(1 << slot);
+ __raw_writeb(have_expmask, EXPMASK_ENABLE);
+ }
+ } else
+ printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
+}
+
+static int __init ecard_probeirqhw(void)
+{
+ ecard_t *ec;
+ int found;
+
+ __raw_writeb(0x00, EXPMASK_ENABLE);
+ __raw_writeb(0xff, EXPMASK_STATUS);
+ found = (__raw_readb(EXPMASK_STATUS) & 15) == 0;
+ __raw_writeb(0xff, EXPMASK_ENABLE);
+
+ if (found) {
+ printk(KERN_DEBUG "Expansion card interrupt "
+ "management hardware found\n");
+
+ /* for each card present, set a bit to '1' */
+ have_expmask = 0x80000000;
+
+ for (ec = cards; ec; ec = ec->next)
+ have_expmask |= 1 << ec->slot_no;
+
+ __raw_writeb(have_expmask, EXPMASK_ENABLE);
+ }
+
+ return found;
+}
+#else
+#define ecard_irqexp_handler NULL
+#define ecard_probeirqhw() (0)
+#endif
+
+#ifndef IO_EC_MEMC8_BASE
+#define IO_EC_MEMC8_BASE 0
+#endif
+
+static unsigned int __ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
+{
+ unsigned long address = 0;
+ int slot = ec->slot_no;
+
+ if (ec->slot_no == 8)
+ return IO_EC_MEMC8_BASE;
+
+ ectcr &= ~(1 << slot);
+
+ switch (type) {
+ case ECARD_MEMC:
+ if (slot < 4)
+ address = IO_EC_MEMC_BASE + (slot << 12);
+ break;
+
+ case ECARD_IOC:
+ if (slot < 4)
+ address = IO_EC_IOC_BASE + (slot << 12);
+#ifdef IO_EC_IOC4_BASE
+ else
+ address = IO_EC_IOC4_BASE + ((slot - 4) << 12);
+#endif
+ if (address)
+ address += speed << 17;
+ break;
+
+#ifdef IO_EC_EASI_BASE
+ case ECARD_EASI:
+ address = IO_EC_EASI_BASE + (slot << 22);
+ if (speed == ECARD_FAST)
+ ectcr |= 1 << slot;
+ break;
+#endif
+ default:
+ break;
+ }
+
+#ifdef IOMD_ECTCR
+ iomd_writeb(ectcr, IOMD_ECTCR);
+#endif
+ return address;
+}
+
+static int ecard_prints(struct seq_file *m, ecard_t *ec)
+{
+ seq_printf(m, " %d: %s ", ec->slot_no, ec->easi ? "EASI" : " ");
+
+ if (ec->cid.id == 0) {
+ struct in_chunk_dir incd;
+
+ seq_printf(m, "[%04X:%04X] ",
+ ec->cid.manufacturer, ec->cid.product);
+
+ if (!ec->card_desc && ec->cid.cd &&
+ ecard_readchunk(&incd, ec, 0xf5, 0)) {
+ ec->card_desc = kmalloc(strlen(incd.d.string)+1, GFP_KERNEL);
+
+ if (ec->card_desc)
+ strcpy((char *)ec->card_desc, incd.d.string);
+ }
+
+ seq_printf(m, "%s\n", ec->card_desc ? ec->card_desc : "*unknown*");
+ } else
+ seq_printf(m, "Simple card %d\n", ec->cid.id);
+
+ return 0;
+}
+
+static int ecard_devices_proc_show(struct seq_file *m, void *v)
+{
+ ecard_t *ec = cards;
+
+ while (ec) {
+ ecard_prints(m, ec);
+ ec = ec->next;
+ }
+ return 0;
+}
+
+static int ecard_devices_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ecard_devices_proc_show, NULL);
+}
+
+static const struct file_operations bus_ecard_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = ecard_devices_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct proc_dir_entry *proc_bus_ecard_dir = NULL;
+
+static void ecard_proc_init(void)
+{
+ proc_bus_ecard_dir = proc_mkdir("bus/ecard", NULL);
+ proc_create("devices", 0, proc_bus_ecard_dir, &bus_ecard_proc_fops);
+}
+
+#define ec_set_resource(ec,nr,st,sz) \
+ do { \
+ (ec)->resource[nr].name = dev_name(&ec->dev); \
+ (ec)->resource[nr].start = st; \
+ (ec)->resource[nr].end = (st) + (sz) - 1; \
+ (ec)->resource[nr].flags = IORESOURCE_MEM; \
+ } while (0)
+
+static void __init ecard_free_card(struct expansion_card *ec)
+{
+ int i;
+
+ for (i = 0; i < ECARD_NUM_RESOURCES; i++)
+ if (ec->resource[i].flags)
+ release_resource(&ec->resource[i]);
+
+ kfree(ec);
+}
+
+static struct expansion_card *__init ecard_alloc_card(int type, int slot)
+{
+ struct expansion_card *ec;
+ unsigned long base;
+ int i;
+
+ ec = kzalloc(sizeof(ecard_t), GFP_KERNEL);
+ if (!ec) {
+ ec = ERR_PTR(-ENOMEM);
+ goto nomem;
+ }
+
+ ec->slot_no = slot;
+ ec->easi = type == ECARD_EASI;
+ ec->irq = NO_IRQ;
+ ec->fiq = NO_IRQ;
+ ec->dma = NO_DMA;
+ ec->ops = &ecard_default_ops;
+
+ snprintf(ec->dev.bus_id, sizeof(ec->dev.bus_id), "ecard%d", slot);
+ ec->dev.parent = NULL;
+ ec->dev.bus = &ecard_bus_type;
+ ec->dev.dma_mask = &ec->dma_mask;
+ ec->dma_mask = (u64)0xffffffff;
+ ec->dev.coherent_dma_mask = ec->dma_mask;
+
+ if (slot < 4) {
+ ec_set_resource(ec, ECARD_RES_MEMC,
+ PODSLOT_MEMC_BASE + (slot << 14),
+ PODSLOT_MEMC_SIZE);
+ base = PODSLOT_IOC0_BASE + (slot << 14);
+ } else
+ base = PODSLOT_IOC4_BASE + ((slot - 4) << 14);
+
+#ifdef CONFIG_ARCH_RPC
+ if (slot < 8) {
+ ec_set_resource(ec, ECARD_RES_EASI,
+ PODSLOT_EASI_BASE + (slot << 24),
+ PODSLOT_EASI_SIZE);
+ }
+
+ if (slot == 8) {
+ ec_set_resource(ec, ECARD_RES_MEMC, NETSLOT_BASE, NETSLOT_SIZE);
+ } else
+#endif
+
+ for (i = 0; i <= ECARD_RES_IOCSYNC - ECARD_RES_IOCSLOW; i++)
+ ec_set_resource(ec, i + ECARD_RES_IOCSLOW,
+ base + (i << 19), PODSLOT_IOC_SIZE);
+
+ for (i = 0; i < ECARD_NUM_RESOURCES; i++) {
+ if (ec->resource[i].flags &&
+ request_resource(&iomem_resource, &ec->resource[i])) {
+ dev_err(&ec->dev, "resource(s) not available\n");
+ ec->resource[i].end -= ec->resource[i].start;
+ ec->resource[i].start = 0;
+ ec->resource[i].flags = 0;
+ }
+ }
+
+ nomem:
+ return ec;
+}
+
+static ssize_t ecard_show_irq(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ return sprintf(buf, "%u\n", ec->irq);
+}
+
+static ssize_t ecard_show_dma(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ return sprintf(buf, "%u\n", ec->dma);
+}
+
+static ssize_t ecard_show_resources(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ char *str = buf;
+ int i;
+
+ for (i = 0; i < ECARD_NUM_RESOURCES; i++)
+ str += sprintf(str, "%08x %08x %08lx\n",
+ ec->resource[i].start,
+ ec->resource[i].end,
+ ec->resource[i].flags);
+
+ return str - buf;
+}
+
+static ssize_t ecard_show_vendor(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ return sprintf(buf, "%u\n", ec->cid.manufacturer);
+}
+
+static ssize_t ecard_show_device(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ return sprintf(buf, "%u\n", ec->cid.product);
+}
+
+static ssize_t ecard_show_type(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ return sprintf(buf, "%s\n", ec->easi ? "EASI" : "IOC");
+}
+
+static struct device_attribute ecard_dev_attrs[] = {
+ __ATTR(device, S_IRUGO, ecard_show_device, NULL),
+ __ATTR(dma, S_IRUGO, ecard_show_dma, NULL),
+ __ATTR(irq, S_IRUGO, ecard_show_irq, NULL),
+ __ATTR(resource, S_IRUGO, ecard_show_resources, NULL),
+ __ATTR(type, S_IRUGO, ecard_show_type, NULL),
+ __ATTR(vendor, S_IRUGO, ecard_show_vendor, NULL),
+ __ATTR_NULL,
+};
+
+
+int ecard_request_resources(struct expansion_card *ec)
+{
+ int i, err = 0;
+
+ for (i = 0; i < ECARD_NUM_RESOURCES; i++) {
+ if (ecard_resource_end(ec, i) &&
+ !request_mem_region(ecard_resource_start(ec, i),
+ ecard_resource_len(ec, i),
+ ec->dev.driver->name)) {
+ err = -EBUSY;
+ break;
+ }
+ }
+
+ if (err) {
+ while (i--)
+ if (ecard_resource_end(ec, i))
+ release_mem_region(ecard_resource_start(ec, i),
+ ecard_resource_len(ec, i));
+ }
+ return err;
+}
+EXPORT_SYMBOL(ecard_request_resources);
+
+void ecard_release_resources(struct expansion_card *ec)
+{
+ int i;
+
+ for (i = 0; i < ECARD_NUM_RESOURCES; i++)
+ if (ecard_resource_end(ec, i))
+ release_mem_region(ecard_resource_start(ec, i),
+ ecard_resource_len(ec, i));
+}
+EXPORT_SYMBOL(ecard_release_resources);
+
+void ecard_setirq(struct expansion_card *ec, const struct expansion_card_ops *ops, void *irq_data)
+{
+ ec->irq_data = irq_data;
+ barrier();
+ ec->ops = ops;
+}
+EXPORT_SYMBOL(ecard_setirq);
+
+void __iomem *ecardm_iomap(struct expansion_card *ec, unsigned int res,
+ unsigned long offset, unsigned long maxsize)
+{
+ unsigned long start = ecard_resource_start(ec, res);
+ unsigned long end = ecard_resource_end(ec, res);
+
+ if (offset > (end - start))
+ return NULL;
+
+ start += offset;
+ if (maxsize && end - start > maxsize)
+ end = start + maxsize;
+
+ return devm_ioremap(&ec->dev, start, end - start);
+}
+EXPORT_SYMBOL(ecardm_iomap);
+
+/*
+ * Probe for an expansion card.
+ *
+ * If bit 1 of the first byte of the card is set, then the
+ * card does not exist.
+ */
+static int __init
+ecard_probe(int slot, card_type_t type)
+{
+ ecard_t **ecp;
+ ecard_t *ec;
+ struct ex_ecid cid;
+ int i, rc;
+
+ ec = ecard_alloc_card(type, slot);
+ if (IS_ERR(ec)) {
+ rc = PTR_ERR(ec);
+ goto nomem;
+ }
+
+ rc = -ENODEV;
+ if ((ec->podaddr = __ecard_address(ec, type, ECARD_SYNC)) == 0)
+ goto nodev;
+
+ cid.r_zero = 1;
+ ecard_readbytes(&cid, ec, 0, 16, 0);
+ if (cid.r_zero)
+ goto nodev;
+
+ ec->cid.id = cid.r_id;
+ ec->cid.cd = cid.r_cd;
+ ec->cid.is = cid.r_is;
+ ec->cid.w = cid.r_w;
+ ec->cid.manufacturer = ecard_getu16(cid.r_manu);
+ ec->cid.product = ecard_getu16(cid.r_prod);
+ ec->cid.country = cid.r_country;
+ ec->cid.irqmask = cid.r_irqmask;
+ ec->cid.irqoff = ecard_gets24(cid.r_irqoff);
+ ec->cid.fiqmask = cid.r_fiqmask;
+ ec->cid.fiqoff = ecard_gets24(cid.r_fiqoff);
+ ec->fiqaddr =
+ ec->irqaddr = (void __iomem *)ioaddr(ec->podaddr);
+
+ if (ec->cid.is) {
+ ec->irqmask = ec->cid.irqmask;
+ ec->irqaddr += ec->cid.irqoff;
+ ec->fiqmask = ec->cid.fiqmask;
+ ec->fiqaddr += ec->cid.fiqoff;
+ } else {
+ ec->irqmask = 1;
+ ec->fiqmask = 4;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); i++)
+ if (blacklist[i].manufacturer == ec->cid.manufacturer &&
+ blacklist[i].product == ec->cid.product) {
+ ec->card_desc = blacklist[i].type;
+ break;
+ }
+
+ /*
+ * hook the interrupt handlers
+ */
+ if (slot < 8) {
+ ec->irq = 32 + slot;
+ set_irq_chip(ec->irq, &ecard_chip);
+ set_irq_handler(ec->irq, handle_level_irq);
+ set_irq_flags(ec->irq, IRQF_VALID);
+ }
+
+#ifdef IO_EC_MEMC8_BASE
+ if (slot == 8)
+ ec->irq = 11;
+#endif
+#ifdef CONFIG_ARCH_RPC
+ /* On RiscPC, only first two slots have DMA capability */
+ if (slot < 2)
+ ec->dma = 2 + slot;
+#endif
+
+ for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
+
+ *ecp = ec;
+ slot_to_expcard[slot] = ec;
+
+ device_register(&ec->dev);
+
+ return 0;
+
+ nodev:
+ ecard_free_card(ec);
+ nomem:
+ return rc;
+}
+
+/*
+ * Initialise the expansion card system.
+ * Locate all hardware - interrupt management and
+ * actual cards.
+ */
+static int __init ecard_init(void)
+{
+ struct task_struct *task;
+ int slot, irqhw;
+
+ task = kthread_run(ecard_task, NULL, "kecardd");
+ if (IS_ERR(task)) {
+ printk(KERN_ERR "Ecard: unable to create kernel thread: %ld\n",
+ PTR_ERR(task));
+ return PTR_ERR(task);
+ }
+
+ printk("Probing expansion cards\n");
+
+ for (slot = 0; slot < 8; slot ++) {
+ if (ecard_probe(slot, ECARD_EASI) == -ENODEV)
+ ecard_probe(slot, ECARD_IOC);
+ }
+
+#ifdef IO_EC_MEMC8_BASE
+ ecard_probe(8, ECARD_IOC);
+#endif
+
+ irqhw = ecard_probeirqhw();
+
+ set_irq_chained_handler(IRQ_EXPANSIONCARD,
+ irqhw ? ecard_irqexp_handler : ecard_irq_handler);
+
+ ecard_proc_init();
+
+ return 0;
+}
+
+subsys_initcall(ecard_init);
+
+/*
+ * ECARD "bus"
+ */
+static const struct ecard_id *
+ecard_match_device(const struct ecard_id *ids, struct expansion_card *ec)
+{
+ int i;
+
+ for (i = 0; ids[i].manufacturer != 65535; i++)
+ if (ec->cid.manufacturer == ids[i].manufacturer &&
+ ec->cid.product == ids[i].product)
+ return ids + i;
+
+ return NULL;
+}
+
+static int ecard_drv_probe(struct device *dev)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct ecard_driver *drv = ECARD_DRV(dev->driver);
+ const struct ecard_id *id;
+ int ret;
+
+ id = ecard_match_device(drv->id_table, ec);
+
+ ec->claimed = 1;
+ ret = drv->probe(ec, id);
+ if (ret)
+ ec->claimed = 0;
+ return ret;
+}
+
+static int ecard_drv_remove(struct device *dev)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct ecard_driver *drv = ECARD_DRV(dev->driver);
+
+ drv->remove(ec);
+ ec->claimed = 0;
+
+ /*
+ * Restore the default operations. We ensure that the
+ * ops are set before we change the data.
+ */
+ ec->ops = &ecard_default_ops;
+ barrier();
+ ec->irq_data = NULL;
+
+ return 0;
+}
+
+/*
+ * Before rebooting, we must make sure that the expansion card is in a
+ * sensible state, so it can be re-detected. This means that the first
+ * page of the ROM must be visible. We call the expansion cards reset
+ * handler, if any.
+ */
+static void ecard_drv_shutdown(struct device *dev)
+{
+ struct expansion_card *ec = ECARD_DEV(dev);
+ struct ecard_driver *drv = ECARD_DRV(dev->driver);
+ struct ecard_request req;
+
+ if (dev->driver) {
+ if (drv->shutdown)
+ drv->shutdown(ec);
+ ec->claimed = 0;
+ }
+
+ /*
+ * If this card has a loader, call the reset handler.
+ */
+ if (ec->loader) {
+ req.fn = ecard_task_reset;
+ req.ec = ec;
+ ecard_call(&req);
+ }
+}
+
+int ecard_register_driver(struct ecard_driver *drv)
+{
+ drv->drv.bus = &ecard_bus_type;
+
+ return driver_register(&drv->drv);
+}
+
+void ecard_remove_driver(struct ecard_driver *drv)
+{
+ driver_unregister(&drv->drv);
+}
+
+static int ecard_match(struct device *_dev, struct device_driver *_drv)
+{
+ struct expansion_card *ec = ECARD_DEV(_dev);
+ struct ecard_driver *drv = ECARD_DRV(_drv);
+ int ret;
+
+ if (drv->id_table) {
+ ret = ecard_match_device(drv->id_table, ec) != NULL;
+ } else {
+ ret = ec->cid.id == drv->id;
+ }
+
+ return ret;
+}
+
+struct bus_type ecard_bus_type = {
+ .name = "ecard",
+ .dev_attrs = ecard_dev_attrs,
+ .match = ecard_match,
+ .probe = ecard_drv_probe,
+ .remove = ecard_drv_remove,
+ .shutdown = ecard_drv_shutdown,
+};
+
+static int ecard_bus_init(void)
+{
+ return bus_register(&ecard_bus_type);
+}
+
+postcore_initcall(ecard_bus_init);
+
+EXPORT_SYMBOL(ecard_readchunk);
+EXPORT_SYMBOL(ecard_register_driver);
+EXPORT_SYMBOL(ecard_remove_driver);
+EXPORT_SYMBOL(ecard_bus_type);
diff --git a/arch/arm/kernel/ecard.h b/arch/arm/kernel/ecard.h
new file mode 100644
index 0000000..4642d43
--- /dev/null
+++ b/arch/arm/kernel/ecard.h
@@ -0,0 +1,69 @@
+/*
+ * ecard.h
+ *
+ * Copyright 2007 Russell King
+ *
+ * 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.
+ */
+
+/* Definitions internal to ecard.c - for it's use only!!
+ *
+ * External expansion card header as read from the card
+ */
+struct ex_ecid {
+ unsigned char r_irq:1;
+ unsigned char r_zero:1;
+ unsigned char r_fiq:1;
+ unsigned char r_id:4;
+ unsigned char r_a:1;
+
+ unsigned char r_cd:1;
+ unsigned char r_is:1;
+ unsigned char r_w:2;
+ unsigned char r_r1:4;
+
+ unsigned char r_r2:8;
+
+ unsigned char r_prod[2];
+
+ unsigned char r_manu[2];
+
+ unsigned char r_country;
+
+ unsigned char r_fiqmask;
+ unsigned char r_fiqoff[3];
+
+ unsigned char r_irqmask;
+ unsigned char r_irqoff[3];
+};
+
+/*
+ * Chunk directory entry as read from the card
+ */
+struct ex_chunk_dir {
+ unsigned char r_id;
+ unsigned char r_len[3];
+ unsigned long r_start;
+ union {
+ char string[256];
+ char data[1];
+ } d;
+#define c_id(x) ((x)->r_id)
+#define c_len(x) ((x)->r_len[0]|((x)->r_len[1]<<8)|((x)->r_len[2]<<16))
+#define c_start(x) ((x)->r_start)
+};
+
+typedef enum ecard_type { /* Cards address space */
+ ECARD_IOC,
+ ECARD_MEMC,
+ ECARD_EASI
+} card_type_t;
+
+typedef enum { /* Speed for ECARD_IOC space */
+ ECARD_SLOW = 0,
+ ECARD_MEDIUM = 1,
+ ECARD_FAST = 2,
+ ECARD_SYNC = 3
+} card_speed_t;
diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c
new file mode 100644
index 0000000..8484909
--- /dev/null
+++ b/arch/arm/kernel/elf.c
@@ -0,0 +1,83 @@
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/personality.h>
+#include <linux/binfmts.h>
+#include <linux/elf.h>
+
+int elf_check_arch(const struct elf32_hdr *x)
+{
+ unsigned int eflags;
+
+ /* Make sure it's an ARM executable */
+ if (x->e_machine != EM_ARM)
+ return 0;
+
+ /* Make sure the entry address is reasonable */
+ if (x->e_entry & 1) {
+ if (!(elf_hwcap & HWCAP_THUMB))
+ return 0;
+ } else if (x->e_entry & 3)
+ return 0;
+
+ eflags = x->e_flags;
+ if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) {
+ unsigned int flt_fmt;
+
+ /* APCS26 is only allowed if the CPU supports it */
+ if ((eflags & EF_ARM_APCS_26) && !(elf_hwcap & HWCAP_26BIT))
+ return 0;
+
+ flt_fmt = eflags & (EF_ARM_VFP_FLOAT | EF_ARM_SOFT_FLOAT);
+
+ /* VFP requires the supporting code */
+ if (flt_fmt == EF_ARM_VFP_FLOAT && !(elf_hwcap & HWCAP_VFP))
+ return 0;
+ }
+ return 1;
+}
+EXPORT_SYMBOL(elf_check_arch);
+
+void elf_set_personality(const struct elf32_hdr *x)
+{
+ unsigned int eflags = x->e_flags;
+ unsigned int personality = PER_LINUX_32BIT;
+
+ /*
+ * APCS-26 is only valid for OABI executables
+ */
+ if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) {
+ if (eflags & EF_ARM_APCS_26)
+ personality = PER_LINUX;
+ }
+
+ set_personality(personality);
+
+ /*
+ * Since the FPA coprocessor uses CP1 and CP2, and iWMMXt uses CP0
+ * and CP1, we only enable access to the iWMMXt coprocessor if the
+ * binary is EABI or softfloat (and thus, guaranteed not to use
+ * FPA instructions.)
+ */
+ if (elf_hwcap & HWCAP_IWMMXT &&
+ eflags & (EF_ARM_EABI_MASK | EF_ARM_SOFT_FLOAT)) {
+ set_thread_flag(TIF_USING_IWMMXT);
+ } else {
+ clear_thread_flag(TIF_USING_IWMMXT);
+ }
+}
+EXPORT_SYMBOL(elf_set_personality);
+
+/*
+ * Set READ_IMPLIES_EXEC if:
+ * - the binary requires an executable stack
+ * - we're running on a CPU which doesn't support NX.
+ */
+int arm_elf_read_implies_exec(const struct elf32_hdr *x, int executable_stack)
+{
+ if (executable_stack != EXSTACK_ENABLE_X)
+ return 1;
+ if (cpu_architecture() <= CPU_ARCH_ARMv6)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(arm_elf_read_implies_exec);
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
new file mode 100644
index 0000000..77b0474
--- /dev/null
+++ b/arch/arm/kernel/entry-armv.S
@@ -0,0 +1,1204 @@
+/*
+ * linux/arch/arm/kernel/entry-armv.S
+ *
+ * Copyright (C) 1996,1997,1998 Russell King.
+ * ARM700 fix by Matthew Godbolt (linux-user@willothewisp.demon.co.uk)
+ * nommu support by Hyok S. Choi (hyok.choi@samsung.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Low-level vector interface routines
+ *
+ * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction
+ * that causes it to save wrong values... Be aware!
+ */
+
+#include <asm/memory.h>
+#include <asm/glue.h>
+#include <asm/vfpmacros.h>
+#include <mach/entry-macro.S>
+#include <asm/thread_notify.h>
+
+#include "entry-header.S"
+
+/*
+ * Interrupt handling. Preserves r7, r8, r9
+ */
+ .macro irq_handler
+ get_irqnr_preamble r5, lr
+1: get_irqnr_and_base r0, r6, r5, lr
+ movne r1, sp
+ @
+ @ routine called with r0 = irq number, r1 = struct pt_regs *
+ @
+ adrne lr, 1b
+ bne asm_do_IRQ
+
+#ifdef CONFIG_SMP
+ /*
+ * XXX
+ *
+ * this macro assumes that irqstat (r6) and base (r5) are
+ * preserved from get_irqnr_and_base above
+ */
+ test_for_ipi r0, r6, r5, lr
+ movne r0, sp
+ adrne lr, 1b
+ bne do_IPI
+
+#ifdef CONFIG_LOCAL_TIMERS
+ test_for_ltirq r0, r6, r5, lr
+ movne r0, sp
+ adrne lr, 1b
+ bne do_local_timer
+#endif
+#endif
+
+ .endm
+
+#ifdef CONFIG_KPROBES
+ .section .kprobes.text,"ax",%progbits
+#else
+ .text
+#endif
+
+/*
+ * Invalid mode handlers
+ */
+ .macro inv_entry, reason
+ sub sp, sp, #S_FRAME_SIZE
+ stmib sp, {r1 - lr}
+ mov r1, #\reason
+ .endm
+
+__pabt_invalid:
+ inv_entry BAD_PREFETCH
+ b common_invalid
+ENDPROC(__pabt_invalid)
+
+__dabt_invalid:
+ inv_entry BAD_DATA
+ b common_invalid
+ENDPROC(__dabt_invalid)
+
+__irq_invalid:
+ inv_entry BAD_IRQ
+ b common_invalid
+ENDPROC(__irq_invalid)
+
+__und_invalid:
+ inv_entry BAD_UNDEFINSTR
+
+ @
+ @ XXX fall through to common_invalid
+ @
+
+@
+@ common_invalid - generic code for failed exception (re-entrant version of handlers)
+@
+common_invalid:
+ zero_fp
+
+ ldmia r0, {r4 - r6}
+ add r0, sp, #S_PC @ here for interlock avoidance
+ mov r7, #-1 @ "" "" "" ""
+ str r4, [sp] @ save preserved r0
+ stmia r0, {r5 - r7} @ lr_<exception>,
+ @ cpsr_<exception>, "old_r0"
+
+ mov r0, sp
+ b bad_mode
+ENDPROC(__und_invalid)
+
+/*
+ * SVC mode handlers
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5)
+#define SPFIX(code...) code
+#else
+#define SPFIX(code...)
+#endif
+
+ .macro svc_entry, stack_hole=0
+ sub sp, sp, #(S_FRAME_SIZE + \stack_hole)
+ SPFIX( tst sp, #4 )
+ SPFIX( bicne sp, sp, #4 )
+ stmib sp, {r1 - r12}
+
+ ldmia r0, {r1 - r3}
+ add r5, sp, #S_SP @ here for interlock avoidance
+ mov r4, #-1 @ "" "" "" ""
+ add r0, sp, #(S_FRAME_SIZE + \stack_hole)
+ SPFIX( addne r0, r0, #4 )
+ str r1, [sp] @ save the "real" r0 copied
+ @ from the exception stack
+
+ mov r1, lr
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r0 - sp_svc
+ @ r1 - lr_svc
+ @ r2 - lr_<exception>, already fixed up for correct return/restart
+ @ r3 - spsr_<exception>
+ @ r4 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ stmia r5, {r0 - r4}
+ .endm
+
+ .align 5
+__dabt_svc:
+ svc_entry
+
+ @
+ @ get ready to re-enable interrupts if appropriate
+ @
+ mrs r9, cpsr
+ tst r3, #PSR_I_BIT
+ biceq r9, r9, #PSR_I_BIT
+
+ @
+ @ Call the processor-specific abort handler:
+ @
+ @ r2 - aborted context pc
+ @ r3 - aborted context cpsr
+ @
+ @ The abort handler must return the aborted address in r0, and
+ @ the fault status register in r1. r9 must be preserved.
+ @
+#ifdef MULTI_DABORT
+ ldr r4, .LCprocfns
+ mov lr, pc
+ ldr pc, [r4, #PROCESSOR_DABT_FUNC]
+#else
+ bl CPU_DABORT_HANDLER
+#endif
+
+ @
+ @ set desired IRQ state, then call main handler
+ @
+ msr cpsr_c, r9
+ mov r2, sp
+ bl do_DataAbort
+
+ @
+ @ IRQs off again before pulling preserved data off the stack
+ @
+ disable_irq
+
+ @
+ @ restore SPSR and restart the instruction
+ @
+ ldr r0, [sp, #S_PSR]
+ msr spsr_cxsf, r0
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ENDPROC(__dabt_svc)
+
+ .align 5
+__irq_svc:
+ svc_entry
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+#ifdef CONFIG_PREEMPT
+ get_thread_info tsk
+ ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
+ add r7, r8, #1 @ increment it
+ str r7, [tsk, #TI_PREEMPT]
+#endif
+
+ irq_handler
+#ifdef CONFIG_PREEMPT
+ str r8, [tsk, #TI_PREEMPT] @ restore preempt count
+ ldr r0, [tsk, #TI_FLAGS] @ get flags
+ teq r8, #0 @ if preempt count != 0
+ movne r0, #0 @ force flags to 0
+ tst r0, #_TIF_NEED_RESCHED
+ blne svc_preempt
+#endif
+ ldr r0, [sp, #S_PSR] @ irqs are already disabled
+ msr spsr_cxsf, r0
+#ifdef CONFIG_TRACE_IRQFLAGS
+ tst r0, #PSR_I_BIT
+ bleq trace_hardirqs_on
+#endif
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ENDPROC(__irq_svc)
+
+ .ltorg
+
+#ifdef CONFIG_PREEMPT
+svc_preempt:
+ mov r8, lr
+1: bl preempt_schedule_irq @ irq en/disable is done inside
+ ldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGS
+ tst r0, #_TIF_NEED_RESCHED
+ moveq pc, r8 @ go again
+ b 1b
+#endif
+
+ .align 5
+__und_svc:
+#ifdef CONFIG_KPROBES
+ @ If a kprobe is about to simulate a "stmdb sp..." instruction,
+ @ it obviously needs free stack space which then will belong to
+ @ the saved context.
+ svc_entry 64
+#else
+ svc_entry
+#endif
+
+ @
+ @ call emulation code, which returns using r9 if it has emulated
+ @ the instruction, or the more conventional lr if we are to treat
+ @ this as a real undefined instruction
+ @
+ @ r0 - instruction
+ @
+ ldr r0, [r2, #-4]
+ adr r9, 1f
+ bl call_fpe
+
+ mov r0, sp @ struct pt_regs *regs
+ bl do_undefinstr
+
+ @
+ @ IRQs off again before pulling preserved data off the stack
+ @
+1: disable_irq
+
+ @
+ @ restore SPSR and restart the instruction
+ @
+ ldr lr, [sp, #S_PSR] @ Get SVC cpsr
+ msr spsr_cxsf, lr
+ ldmia sp, {r0 - pc}^ @ Restore SVC registers
+ENDPROC(__und_svc)
+
+ .align 5
+__pabt_svc:
+ svc_entry
+
+ @
+ @ re-enable interrupts if appropriate
+ @
+ mrs r9, cpsr
+ tst r3, #PSR_I_BIT
+ biceq r9, r9, #PSR_I_BIT
+
+ @
+ @ set args, then call main handler
+ @
+ @ r0 - address of faulting instruction
+ @ r1 - pointer to registers on stack
+ @
+#ifdef MULTI_PABORT
+ mov r0, r2 @ pass address of aborted instruction.
+ ldr r4, .LCprocfns
+ mov lr, pc
+ ldr pc, [r4, #PROCESSOR_PABT_FUNC]
+#else
+ CPU_PABORT_HANDLER(r0, r2)
+#endif
+ msr cpsr_c, r9 @ Maybe enable interrupts
+ mov r1, sp @ regs
+ bl do_PrefetchAbort @ call abort handler
+
+ @
+ @ IRQs off again before pulling preserved data off the stack
+ @
+ disable_irq
+
+ @
+ @ restore SPSR and restart the instruction
+ @
+ ldr r0, [sp, #S_PSR]
+ msr spsr_cxsf, r0
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ENDPROC(__pabt_svc)
+
+ .align 5
+.LCcralign:
+ .word cr_alignment
+#ifdef MULTI_DABORT
+.LCprocfns:
+ .word processor
+#endif
+.LCfp:
+ .word fp_enter
+
+/*
+ * User mode handlers
+ *
+ * EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
+ */
+
+#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
+#error "sizeof(struct pt_regs) must be a multiple of 8"
+#endif
+
+ .macro usr_entry
+ sub sp, sp, #S_FRAME_SIZE
+ stmib sp, {r1 - r12}
+
+ ldmia r0, {r1 - r3}
+ add r0, sp, #S_PC @ here for interlock avoidance
+ mov r4, #-1 @ "" "" "" ""
+
+ str r1, [sp] @ save the "real" r0 copied
+ @ from the exception stack
+
+ @
+ @ We are now ready to fill in the remaining blanks on the stack:
+ @
+ @ r2 - lr_<exception>, already fixed up for correct return/restart
+ @ r3 - spsr_<exception>
+ @ r4 - orig_r0 (see pt_regs definition in ptrace.h)
+ @
+ @ Also, separately save sp_usr and lr_usr
+ @
+ stmia r0, {r2 - r4}
+ stmdb r0, {sp, lr}^
+
+ @
+ @ Enable the alignment trap while in kernel mode
+ @
+ alignment_trap r0
+
+ @
+ @ Clear FP to mark the first stack frame
+ @
+ zero_fp
+ .endm
+
+ .macro kuser_cmpxchg_check
+#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
+#ifndef CONFIG_MMU
+#warning "NPTL on non MMU needs fixing"
+#else
+ @ Make sure our user space atomic helper is restarted
+ @ if it was interrupted in a critical region. Here we
+ @ perform a quick test inline since it should be false
+ @ 99.9999% of the time. The rest is done out of line.
+ cmp r2, #TASK_SIZE
+ blhs kuser_cmpxchg_fixup
+#endif
+#endif
+ .endm
+
+ .align 5
+__dabt_usr:
+ usr_entry
+ kuser_cmpxchg_check
+
+ @
+ @ Call the processor-specific abort handler:
+ @
+ @ r2 - aborted context pc
+ @ r3 - aborted context cpsr
+ @
+ @ The abort handler must return the aborted address in r0, and
+ @ the fault status register in r1.
+ @
+#ifdef MULTI_DABORT
+ ldr r4, .LCprocfns
+ mov lr, pc
+ ldr pc, [r4, #PROCESSOR_DABT_FUNC]
+#else
+ bl CPU_DABORT_HANDLER
+#endif
+
+ @
+ @ IRQs on, then call the main handler
+ @
+ enable_irq
+ mov r2, sp
+ adr lr, ret_from_exception
+ b do_DataAbort
+ENDPROC(__dabt_usr)
+
+ .align 5
+__irq_usr:
+ usr_entry
+ kuser_cmpxchg_check
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+ get_thread_info tsk
+#ifdef CONFIG_PREEMPT
+ ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
+ add r7, r8, #1 @ increment it
+ str r7, [tsk, #TI_PREEMPT]
+#endif
+
+ irq_handler
+#ifdef CONFIG_PREEMPT
+ ldr r0, [tsk, #TI_PREEMPT]
+ str r8, [tsk, #TI_PREEMPT]
+ teq r0, r7
+ strne r0, [r0, -r0]
+#endif
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_on
+#endif
+
+ mov why, #0
+ b ret_to_user
+ENDPROC(__irq_usr)
+
+ .ltorg
+
+ .align 5
+__und_usr:
+ usr_entry
+
+ @
+ @ fall through to the emulation code, which returns using r9 if
+ @ it has emulated the instruction, or the more conventional lr
+ @ if we are to treat this as a real undefined instruction
+ @
+ @ r0 - instruction
+ @
+ adr r9, ret_from_exception
+ adr lr, __und_usr_unknown
+ tst r3, #PSR_T_BIT @ Thumb mode?
+ subeq r4, r2, #4 @ ARM instr at LR - 4
+ subne r4, r2, #2 @ Thumb instr at LR - 2
+1: ldreqt r0, [r4]
+ beq call_fpe
+ @ Thumb instruction
+#if __LINUX_ARM_ARCH__ >= 7
+2: ldrht r5, [r4], #2
+ and r0, r5, #0xf800 @ mask bits 111x x... .... ....
+ cmp r0, #0xe800 @ 32bit instruction if xx != 0
+ blo __und_usr_unknown
+3: ldrht r0, [r4]
+ add r2, r2, #2 @ r2 is PC + 2, make it PC + 4
+ orr r0, r0, r5, lsl #16
+#else
+ b __und_usr_unknown
+#endif
+ENDPROC(__und_usr)
+
+ @
+ @ fallthrough to call_fpe
+ @
+
+/*
+ * The out of line fixup for the ldrt above.
+ */
+ .section .fixup, "ax"
+4: mov pc, r9
+ .previous
+ .section __ex_table,"a"
+ .long 1b, 4b
+#if __LINUX_ARM_ARCH__ >= 7
+ .long 2b, 4b
+ .long 3b, 4b
+#endif
+ .previous
+
+/*
+ * Check whether the instruction is a co-processor instruction.
+ * If yes, we need to call the relevant co-processor handler.
+ *
+ * Note that we don't do a full check here for the co-processor
+ * instructions; all instructions with bit 27 set are well
+ * defined. The only instructions that should fault are the
+ * co-processor instructions. However, we have to watch out
+ * for the ARM6/ARM7 SWI bug.
+ *
+ * NEON is a special case that has to be handled here. Not all
+ * NEON instructions are co-processor instructions, so we have
+ * to make a special case of checking for them. Plus, there's
+ * five groups of them, so we have a table of mask/opcode pairs
+ * to check against, and if any match then we branch off into the
+ * NEON handler code.
+ *
+ * Emulators may wish to make use of the following registers:
+ * r0 = instruction opcode.
+ * r2 = PC+4
+ * r9 = normal "successful" return address
+ * r10 = this threads thread_info structure.
+ * lr = unrecognised instruction return address
+ */
+ @
+ @ Fall-through from Thumb-2 __und_usr
+ @
+#ifdef CONFIG_NEON
+ adr r6, .LCneon_thumb_opcodes
+ b 2f
+#endif
+call_fpe:
+#ifdef CONFIG_NEON
+ adr r6, .LCneon_arm_opcodes
+2:
+ ldr r7, [r6], #4 @ mask value
+ cmp r7, #0 @ end mask?
+ beq 1f
+ and r8, r0, r7
+ ldr r7, [r6], #4 @ opcode bits matching in mask
+ cmp r8, r7 @ NEON instruction?
+ bne 2b
+ get_thread_info r10
+ mov r7, #1
+ strb r7, [r10, #TI_USED_CP + 10] @ mark CP#10 as used
+ strb r7, [r10, #TI_USED_CP + 11] @ mark CP#11 as used
+ b do_vfp @ let VFP handler handle this
+1:
+#endif
+ tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27
+ tstne r0, #0x04000000 @ bit 26 set on both ARM and Thumb-2
+#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710)
+ and r8, r0, #0x0f000000 @ mask out op-code bits
+ teqne r8, #0x0f000000 @ SWI (ARM6/7 bug)?
+#endif
+ moveq pc, lr
+ get_thread_info r10 @ get current thread
+ and r8, r0, #0x00000f00 @ mask out CP number
+ mov r7, #1
+ add r6, r10, #TI_USED_CP
+ strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[]
+#ifdef CONFIG_IWMMXT
+ @ Test if we need to give access to iWMMXt coprocessors
+ ldr r5, [r10, #TI_FLAGS]
+ rsbs r7, r8, #(1 << 8) @ CP 0 or 1 only
+ movcss r7, r5, lsr #(TIF_USING_IWMMXT + 1)
+ bcs iwmmxt_task_enable
+#endif
+ add pc, pc, r8, lsr #6
+ mov r0, r0
+
+ mov pc, lr @ CP#0
+ b do_fpe @ CP#1 (FPE)
+ b do_fpe @ CP#2 (FPE)
+ mov pc, lr @ CP#3
+#ifdef CONFIG_CRUNCH
+ b crunch_task_enable @ CP#4 (MaverickCrunch)
+ b crunch_task_enable @ CP#5 (MaverickCrunch)
+ b crunch_task_enable @ CP#6 (MaverickCrunch)
+#else
+ mov pc, lr @ CP#4
+ mov pc, lr @ CP#5
+ mov pc, lr @ CP#6
+#endif
+ mov pc, lr @ CP#7
+ mov pc, lr @ CP#8
+ mov pc, lr @ CP#9
+#ifdef CONFIG_VFP
+ b do_vfp @ CP#10 (VFP)
+ b do_vfp @ CP#11 (VFP)
+#else
+ mov pc, lr @ CP#10 (VFP)
+ mov pc, lr @ CP#11 (VFP)
+#endif
+ mov pc, lr @ CP#12
+ mov pc, lr @ CP#13
+ mov pc, lr @ CP#14 (Debug)
+ mov pc, lr @ CP#15 (Control)
+
+#ifdef CONFIG_NEON
+ .align 6
+
+.LCneon_arm_opcodes:
+ .word 0xfe000000 @ mask
+ .word 0xf2000000 @ opcode
+
+ .word 0xff100000 @ mask
+ .word 0xf4000000 @ opcode
+
+ .word 0x00000000 @ mask
+ .word 0x00000000 @ opcode
+
+.LCneon_thumb_opcodes:
+ .word 0xef000000 @ mask
+ .word 0xef000000 @ opcode
+
+ .word 0xff100000 @ mask
+ .word 0xf9000000 @ opcode
+
+ .word 0x00000000 @ mask
+ .word 0x00000000 @ opcode
+#endif
+
+do_fpe:
+ enable_irq
+ ldr r4, .LCfp
+ add r10, r10, #TI_FPSTATE @ r10 = workspace
+ ldr pc, [r4] @ Call FP module USR entry point
+
+/*
+ * The FP module is called with these registers set:
+ * r0 = instruction
+ * r2 = PC+4
+ * r9 = normal "successful" return address
+ * r10 = FP workspace
+ * lr = unrecognised FP instruction return address
+ */
+
+ .data
+ENTRY(fp_enter)
+ .word no_fp
+ .previous
+
+no_fp: mov pc, lr
+
+__und_usr_unknown:
+ mov r0, sp
+ adr lr, ret_from_exception
+ b do_undefinstr
+ENDPROC(__und_usr_unknown)
+
+ .align 5
+__pabt_usr:
+ usr_entry
+
+#ifdef MULTI_PABORT
+ mov r0, r2 @ pass address of aborted instruction.
+ ldr r4, .LCprocfns
+ mov lr, pc
+ ldr pc, [r4, #PROCESSOR_PABT_FUNC]
+#else
+ CPU_PABORT_HANDLER(r0, r2)
+#endif
+ enable_irq @ Enable interrupts
+ mov r1, sp @ regs
+ bl do_PrefetchAbort @ call abort handler
+ /* fall through */
+/*
+ * This is the return code to user mode for abort handlers
+ */
+ENTRY(ret_from_exception)
+ get_thread_info tsk
+ mov why, #0
+ b ret_to_user
+ENDPROC(__pabt_usr)
+ENDPROC(ret_from_exception)
+
+/*
+ * Register switch for ARMv3 and ARMv4 processors
+ * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
+ * previous and next are guaranteed not to be the same.
+ */
+ENTRY(__switch_to)
+ add ip, r1, #TI_CPU_SAVE
+ ldr r3, [r2, #TI_TP_VALUE]
+ stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack
+#ifdef CONFIG_MMU
+ ldr r6, [r2, #TI_CPU_DOMAIN]
+#endif
+#if __LINUX_ARM_ARCH__ >= 6
+#ifdef CONFIG_CPU_32v6K
+ clrex
+#else
+ strex r5, r4, [ip] @ Clear exclusive monitor
+#endif
+#endif
+#if defined(CONFIG_HAS_TLS_REG)
+ mcr p15, 0, r3, c13, c0, 3 @ set TLS register
+#elif !defined(CONFIG_TLS_REG_EMUL)
+ mov r4, #0xffff0fff
+ str r3, [r4, #-15] @ TLS val at 0xffff0ff0
+#endif
+#ifdef CONFIG_MMU
+ mcr p15, 0, r6, c3, c0, 0 @ Set domain register
+#endif
+ mov r5, r0
+ add r4, r2, #TI_CPU_SAVE
+ ldr r0, =thread_notify_head
+ mov r1, #THREAD_NOTIFY_SWITCH
+ bl atomic_notifier_call_chain
+ mov r0, r5
+ ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
+ENDPROC(__switch_to)
+
+ __INIT
+
+/*
+ * 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
+ * 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
+ * really small and justified, and not be abused freely.
+ *
+ * User space is expected to implement those things inline when optimizing
+ * 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.
+ */
+
+ .macro usr_ret, reg
+#ifdef CONFIG_ARM_THUMB
+ bx \reg
+#else
+ mov pc, \reg
+#endif
+ .endm
+
+ .align 5
+ .globl __kuser_helper_start
+__kuser_helper_start:
+
+/*
+ * Reference prototype:
+ *
+ * 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
+
+#if __LINUX_ARM_ARCH__ >= 6 && defined(CONFIG_SMP)
+ mcr p15, 0, r0, c7, c10, 5 @ dmb
+#endif
+ usr_ret lr
+
+ .align 5
+
+/*
+ * Reference prototype:
+ *
+ * int __kernel_cmpxchg(int oldval, int newval, int *ptr)
+ *
+ * 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
+
+#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}
+ mov r7, #0xff00 @ 0xfff0 into r7 for EABI
+ orr r7, r7, #0xf0
+ swi #0x9ffff0
+ ldmfd sp!, {r7, pc}
+
+#elif __LINUX_ARM_ARCH__ < 6
+
+#ifdef CONFIG_MMU
+
+ /*
+ * The only thing that can break atomicity in this cmpxchg
+ * implementation is either an IRQ or a data abort exception
+ * causing another process/thread to be scheduled in the middle
+ * of the critical sequence. To prevent this, code is added to
+ * the IRQ and data abort exception handlers to set the pc back
+ * to the beginning of the critical section if it is found to be
+ * within that critical section (see kuser_cmpxchg_fixup).
+ */
+1: ldr r3, [r2] @ load current val
+ subs r3, r3, r0 @ compare with oldval
+2: streq r1, [r2] @ store newval if eq
+ rsbs r0, r3, #0 @ set return val and C flag
+ usr_ret lr
+
+ .text
+kuser_cmpxchg_fixup:
+ @ Called from kuser_cmpxchg_check macro.
+ @ r2 = address of interrupted insn (must be preserved).
+ @ sp = saved regs. r7 and r8 are clobbered.
+ @ 1b = first critical insn, 2b = last critical insn.
+ @ If r2 >= 1b and r2 <= 2b then saved pc_usr is set to 1b.
+ mov r7, #0xffff0fff
+ sub r7, r7, #(0xffff0fff - (0xffff0fc0 + (1b - __kuser_cmpxchg)))
+ subs r8, r2, r7
+ rsbcss r8, r8, #(2b - 1b)
+ strcs r7, [sp, #S_PC]
+ mov pc, lr
+ .previous
+
+#else
+#warning "NPTL on non MMU needs fixing"
+ mov r0, #-1
+ adds r0, r0, #0
+ usr_ret lr
+#endif
+
+#else
+
+#ifdef CONFIG_SMP
+ mcr p15, 0, r0, c7, c10, 5 @ dmb
+#endif
+1: ldrex r3, [r2]
+ subs r3, r3, r0
+ strexeq r3, r1, [r2]
+ teqeq r3, #1
+ beq 1b
+ rsbs r0, r3, #0
+ /* beware -- each __kuser slot must be 8 instructions max */
+#ifdef CONFIG_SMP
+ b __kuser_memory_barrier
+#else
+ usr_ret lr
+#endif
+
+#endif
+
+ .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
+
+#if !defined(CONFIG_HAS_TLS_REG) && !defined(CONFIG_TLS_REG_EMUL)
+ ldr r0, [pc, #(16 - 8)] @ TLS stored at 0xffff0ff0
+#else
+ mrc p15, 0, r0, c13, c0, 3 @ read TLS register
+#endif
+ usr_ret lr
+
+ .rep 5
+ .word 0 @ pad up to __kuser_helper_version
+ .endr
+
+/*
+ * 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
+ .word ((__kuser_helper_end - __kuser_helper_start) >> 5)
+
+ .globl __kuser_helper_end
+__kuser_helper_end:
+
+
+/*
+ * Vector stubs.
+ *
+ * This code is copied to 0xffff0200 so we can use branches in the
+ * vectors, rather than ldr's. Note that this code must not
+ * exceed 0x300 bytes.
+ *
+ * Common stub entry macro:
+ * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ *
+ * SP points to a minimal amount of processor-private memory, the address
+ * of which is copied into r0 for the mode specific abort handler.
+ */
+ .macro vector_stub, name, mode, correction=0
+ .align 5
+
+vector_\name:
+ .if \correction
+ sub lr, lr, #\correction
+ .endif
+
+ @
+ @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
+ @ (parent CPSR)
+ @
+ stmia sp, {r0, lr} @ save r0, lr
+ mrs lr, spsr
+ str lr, [sp, #8] @ save spsr
+
+ @
+ @ Prepare for SVC32 mode. IRQs remain disabled.
+ @
+ mrs r0, cpsr
+ eor r0, r0, #(\mode ^ SVC_MODE)
+ msr spsr_cxsf, r0
+
+ @
+ @ the branch table must immediately follow this code
+ @
+ and lr, lr, #0x0f
+ mov r0, sp
+ ldr lr, [pc, lr, lsl #2]
+ movs pc, lr @ branch to handler in SVC mode
+ENDPROC(vector_\name)
+ .endm
+
+ .globl __stubs_start
+__stubs_start:
+/*
+ * Interrupt dispatcher
+ */
+ vector_stub irq, IRQ_MODE, 4
+
+ .long __irq_usr @ 0 (USR_26 / USR_32)
+ .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
+ .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
+ .long __irq_svc @ 3 (SVC_26 / SVC_32)
+ .long __irq_invalid @ 4
+ .long __irq_invalid @ 5
+ .long __irq_invalid @ 6
+ .long __irq_invalid @ 7
+ .long __irq_invalid @ 8
+ .long __irq_invalid @ 9
+ .long __irq_invalid @ a
+ .long __irq_invalid @ b
+ .long __irq_invalid @ c
+ .long __irq_invalid @ d
+ .long __irq_invalid @ e
+ .long __irq_invalid @ f
+
+/*
+ * Data abort dispatcher
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+ vector_stub dabt, ABT_MODE, 8
+
+ .long __dabt_usr @ 0 (USR_26 / USR_32)
+ .long __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
+ .long __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
+ .long __dabt_svc @ 3 (SVC_26 / SVC_32)
+ .long __dabt_invalid @ 4
+ .long __dabt_invalid @ 5
+ .long __dabt_invalid @ 6
+ .long __dabt_invalid @ 7
+ .long __dabt_invalid @ 8
+ .long __dabt_invalid @ 9
+ .long __dabt_invalid @ a
+ .long __dabt_invalid @ b
+ .long __dabt_invalid @ c
+ .long __dabt_invalid @ d
+ .long __dabt_invalid @ e
+ .long __dabt_invalid @ f
+
+/*
+ * Prefetch abort dispatcher
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+ vector_stub pabt, ABT_MODE, 4
+
+ .long __pabt_usr @ 0 (USR_26 / USR_32)
+ .long __pabt_invalid @ 1 (FIQ_26 / FIQ_32)
+ .long __pabt_invalid @ 2 (IRQ_26 / IRQ_32)
+ .long __pabt_svc @ 3 (SVC_26 / SVC_32)
+ .long __pabt_invalid @ 4
+ .long __pabt_invalid @ 5
+ .long __pabt_invalid @ 6
+ .long __pabt_invalid @ 7
+ .long __pabt_invalid @ 8
+ .long __pabt_invalid @ 9
+ .long __pabt_invalid @ a
+ .long __pabt_invalid @ b
+ .long __pabt_invalid @ c
+ .long __pabt_invalid @ d
+ .long __pabt_invalid @ e
+ .long __pabt_invalid @ f
+
+/*
+ * Undef instr entry dispatcher
+ * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+ vector_stub und, UND_MODE
+
+ .long __und_usr @ 0 (USR_26 / USR_32)
+ .long __und_invalid @ 1 (FIQ_26 / FIQ_32)
+ .long __und_invalid @ 2 (IRQ_26 / IRQ_32)
+ .long __und_svc @ 3 (SVC_26 / SVC_32)
+ .long __und_invalid @ 4
+ .long __und_invalid @ 5
+ .long __und_invalid @ 6
+ .long __und_invalid @ 7
+ .long __und_invalid @ 8
+ .long __und_invalid @ 9
+ .long __und_invalid @ a
+ .long __und_invalid @ b
+ .long __und_invalid @ c
+ .long __und_invalid @ d
+ .long __und_invalid @ e
+ .long __und_invalid @ f
+
+ .align 5
+
+/*=============================================================================
+ * Undefined FIQs
+ *-----------------------------------------------------------------------------
+ * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
+ * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
+ * Basically to switch modes, we *HAVE* to clobber one register... brain
+ * damage alert! I don't think that we can execute any code in here in any
+ * other mode than FIQ... Ok you can switch to another mode, but you can't
+ * get out of that mode without clobbering one register.
+ */
+vector_fiq:
+ disable_fiq
+ subs pc, lr, #4
+
+/*=============================================================================
+ * Address exception handler
+ *-----------------------------------------------------------------------------
+ * These aren't too critical.
+ * (they're not supposed to happen, and won't happen in 32-bit data mode).
+ */
+
+vector_addrexcptn:
+ b vector_addrexcptn
+
+/*
+ * We group all the following data together to optimise
+ * for CPUs with separate I & D caches.
+ */
+ .align 5
+
+.LCvswi:
+ .word vector_swi
+
+ .globl __stubs_end
+__stubs_end:
+
+ .equ stubs_offset, __vectors_start + 0x200 - __stubs_start
+
+ .globl __vectors_start
+__vectors_start:
+ swi SYS_ERROR0
+ b vector_und + stubs_offset
+ ldr pc, .LCvswi + stubs_offset
+ b vector_pabt + stubs_offset
+ b vector_dabt + stubs_offset
+ b vector_addrexcptn + stubs_offset
+ b vector_irq + stubs_offset
+ b vector_fiq + stubs_offset
+
+ .globl __vectors_end
+__vectors_end:
+
+ .data
+
+ .globl cr_alignment
+ .globl cr_no_alignment
+cr_alignment:
+ .space 4
+cr_no_alignment:
+ .space 4
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
new file mode 100644
index 0000000..06269ea
--- /dev/null
+++ b/arch/arm/kernel/entry-common.S
@@ -0,0 +1,476 @@
+/*
+ * linux/arch/arm/kernel/entry-common.S
+ *
+ * Copyright (C) 2000 Russell King
+ *
+ * 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 <asm/unistd.h>
+#include <asm/ftrace.h>
+#include <mach/entry-macro.S>
+
+#include "entry-header.S"
+
+
+ .align 5
+/*
+ * This is the fast syscall return path. We do as little as
+ * possible here, and this includes saving r0 back into the SVC
+ * stack.
+ */
+ret_fast_syscall:
+ disable_irq @ disable interrupts
+ ldr r1, [tsk, #TI_FLAGS]
+ tst r1, #_TIF_WORK_MASK
+ bne fast_work_pending
+
+ /* perform architecture specific actions before user return */
+ arch_ret_to_user r1, lr
+
+ @ fast_restore_user_regs
+ ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr
+ ldr lr, [sp, #S_OFF + S_PC]! @ get pc
+ msr spsr_cxsf, r1 @ save in spsr_svc
+ ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
+ mov r0, r0
+ add sp, sp, #S_FRAME_SIZE - S_PC
+ movs pc, lr @ return & move spsr_svc into cpsr
+
+/*
+ * Ok, we need to do extra processing, enter the slow path.
+ */
+fast_work_pending:
+ str r0, [sp, #S_R0+S_OFF]! @ returned r0
+work_pending:
+ tst r1, #_TIF_NEED_RESCHED
+ bne work_resched
+ tst r1, #_TIF_SIGPENDING
+ beq no_work_pending
+ mov r0, sp @ 'regs'
+ mov r2, why @ 'syscall'
+ bl do_notify_resume
+ b ret_slow_syscall @ Check work again
+
+work_resched:
+ bl schedule
+/*
+ * "slow" syscall return path. "why" tells us if this was a real syscall.
+ */
+ENTRY(ret_to_user)
+ret_slow_syscall:
+ disable_irq @ disable interrupts
+ ldr r1, [tsk, #TI_FLAGS]
+ tst r1, #_TIF_WORK_MASK
+ bne work_pending
+no_work_pending:
+ /* perform architecture specific actions before user return */
+ arch_ret_to_user r1, lr
+
+ @ slow_restore_user_regs
+ ldr r1, [sp, #S_PSR] @ get calling cpsr
+ ldr lr, [sp, #S_PC]! @ get pc
+ msr spsr_cxsf, r1 @ save in spsr_svc
+ ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
+ mov r0, r0
+ add sp, sp, #S_FRAME_SIZE - S_PC
+ movs pc, lr @ return & move spsr_svc into cpsr
+ENDPROC(ret_to_user)
+
+/*
+ * This is how we return from a fork.
+ */
+ENTRY(ret_from_fork)
+ bl schedule_tail
+ get_thread_info tsk
+ ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing
+ mov why, #1
+ tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
+ beq ret_slow_syscall
+ mov r1, sp
+ mov r0, #1 @ trace exit [IP = 1]
+ bl syscall_trace
+ b ret_slow_syscall
+ENDPROC(ret_from_fork)
+
+ .equ NR_syscalls,0
+#define CALL(x) .equ NR_syscalls,NR_syscalls+1
+#include "calls.S"
+#undef CALL
+#define CALL(x) .long x
+
+#ifdef CONFIG_FUNCTION_TRACER
+#ifdef CONFIG_DYNAMIC_FTRACE
+ENTRY(mcount)
+ stmdb sp!, {r0-r3, lr}
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
+
+ .globl mcount_call
+mcount_call:
+ bl ftrace_stub
+ ldmia sp!, {r0-r3, pc}
+
+ENTRY(ftrace_caller)
+ stmdb sp!, {r0-r3, lr}
+ ldr r1, [fp, #-4]
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
+
+ .globl ftrace_call
+ftrace_call:
+ bl ftrace_stub
+ ldmia sp!, {r0-r3, pc}
+
+#else
+
+ENTRY(mcount)
+ stmdb sp!, {r0-r3, lr}
+ ldr r0, =ftrace_trace_function
+ ldr r2, [r0]
+ adr r0, ftrace_stub
+ cmp r0, r2
+ bne trace
+ ldmia sp!, {r0-r3, pc}
+
+trace:
+ ldr r1, [fp, #-4]
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
+ mov lr, pc
+ mov pc, r2
+ ldmia sp!, {r0-r3, pc}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+ .globl ftrace_stub
+ftrace_stub:
+ mov pc, lr
+
+#endif /* CONFIG_FUNCTION_TRACER */
+
+/*=============================================================================
+ * SWI handler
+ *-----------------------------------------------------------------------------
+ */
+
+ /* If we're optimising for StrongARM the resulting code won't
+ run on an ARM7 and we can save a couple of instructions.
+ --pb */
+#ifdef CONFIG_CPU_ARM710
+#define A710(code...) code
+.Larm710bug:
+ ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
+ mov r0, r0
+ add sp, sp, #S_FRAME_SIZE
+ subs pc, lr, #4
+#else
+#define A710(code...)
+#endif
+
+ .align 5
+ENTRY(vector_swi)
+ sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - r12} @ Calling r0 - r12
+ add r8, sp, #S_PC
+ stmdb r8, {sp, lr}^ @ Calling sp, lr
+ mrs r8, spsr @ called from non-FIQ mode, so ok.
+ str lr, [sp, #S_PC] @ Save calling PC
+ str r8, [sp, #S_PSR] @ Save CPSR
+ str r0, [sp, #S_OLD_R0] @ Save OLD_R0
+ zero_fp
+
+ /*
+ * Get the system call number.
+ */
+
+#if defined(CONFIG_OABI_COMPAT)
+
+ /*
+ * If we have CONFIG_OABI_COMPAT then we need to look at the swi
+ * value to determine if it is an EABI or an old ABI call.
+ */
+#ifdef CONFIG_ARM_THUMB
+ tst r8, #PSR_T_BIT
+ movne r10, #0 @ no thumb OABI emulation
+ ldreq r10, [lr, #-4] @ get SWI instruction
+#else
+ ldr r10, [lr, #-4] @ get SWI instruction
+ A710( and ip, r10, #0x0f000000 @ check for SWI )
+ A710( teq ip, #0x0f000000 )
+ A710( bne .Larm710bug )
+#endif
+
+#elif defined(CONFIG_AEABI)
+
+ /*
+ * Pure EABI user space always put syscall number into scno (r7).
+ */
+ A710( ldr ip, [lr, #-4] @ get SWI instruction )
+ A710( and ip, ip, #0x0f000000 @ check for SWI )
+ A710( teq ip, #0x0f000000 )
+ A710( bne .Larm710bug )
+
+#elif defined(CONFIG_ARM_THUMB)
+
+ /* Legacy ABI only, possibly thumb mode. */
+ tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
+ addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
+ ldreq scno, [lr, #-4]
+
+#else
+
+ /* Legacy ABI only. */
+ ldr scno, [lr, #-4] @ get SWI instruction
+ A710( and ip, scno, #0x0f000000 @ check for SWI )
+ A710( teq ip, #0x0f000000 )
+ A710( bne .Larm710bug )
+
+#endif
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr ip, __cr_alignment
+ ldr ip, [ip]
+ mcr p15, 0, ip, c1, c0 @ update control register
+#endif
+ enable_irq
+
+ get_thread_info tsk
+ adr tbl, sys_call_table @ load syscall table pointer
+ ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
+
+#if defined(CONFIG_OABI_COMPAT)
+ /*
+ * If the swi argument is zero, this is an EABI call and we do nothing.
+ *
+ * If this is an old ABI call, get the syscall number into scno and
+ * get the old ABI syscall table address.
+ */
+ bics r10, r10, #0xff000000
+ eorne scno, r10, #__NR_OABI_SYSCALL_BASE
+ ldrne tbl, =sys_oabi_call_table
+#elif !defined(CONFIG_AEABI)
+ bic scno, scno, #0xff000000 @ mask off SWI op-code
+ eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
+#endif
+
+ stmdb sp!, {r4, r5} @ push fifth and sixth args
+ tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
+ bne __sys_trace
+
+ cmp scno, #NR_syscalls @ check upper syscall limit
+ adr lr, ret_fast_syscall @ return address
+ ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
+
+ add r1, sp, #S_OFF
+2: mov why, #0 @ no longer a real syscall
+ cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
+ eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back
+ bcs arm_syscall
+ b sys_ni_syscall @ not private func
+ENDPROC(vector_swi)
+
+ /*
+ * This is the really slow path. We're going to be doing
+ * context switches, and waiting for our parent to respond.
+ */
+__sys_trace:
+ mov r2, scno
+ add r1, sp, #S_OFF
+ mov r0, #0 @ trace entry [IP = 0]
+ bl syscall_trace
+
+ adr lr, __sys_trace_return @ return address
+ mov scno, r0 @ syscall number (possibly new)
+ add r1, sp, #S_R0 + S_OFF @ pointer to regs
+ cmp scno, #NR_syscalls @ check upper syscall limit
+ ldmccia r1, {r0 - r3} @ have to reload r0 - r3
+ ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
+ b 2b
+
+__sys_trace_return:
+ str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
+ mov r2, scno
+ mov r1, sp
+ mov r0, #1 @ trace exit [IP = 1]
+ bl syscall_trace
+ b ret_slow_syscall
+
+ .align 5
+#ifdef CONFIG_ALIGNMENT_TRAP
+ .type __cr_alignment, #object
+__cr_alignment:
+ .word cr_alignment
+#endif
+ .ltorg
+
+/*
+ * This is the syscall table declaration for native ABI syscalls.
+ * With EABI a couple syscalls are obsolete and defined as sys_ni_syscall.
+ */
+#define ABI(native, compat) native
+#ifdef CONFIG_AEABI
+#define OBSOLETE(syscall) sys_ni_syscall
+#else
+#define OBSOLETE(syscall) syscall
+#endif
+
+ .type sys_call_table, #object
+ENTRY(sys_call_table)
+#include "calls.S"
+#undef ABI
+#undef OBSOLETE
+
+/*============================================================================
+ * Special system call wrappers
+ */
+@ r0 = syscall number
+@ r8 = syscall table
+sys_syscall:
+ bic scno, r0, #__NR_OABI_SYSCALL_BASE
+ cmp scno, #__NR_syscall - __NR_SYSCALL_BASE
+ cmpne scno, #NR_syscalls @ check range
+ stmloia sp, {r5, r6} @ shuffle args
+ movlo r0, r1
+ movlo r1, r2
+ movlo r2, r3
+ movlo r3, r4
+ ldrlo pc, [tbl, scno, lsl #2]
+ b sys_ni_syscall
+ENDPROC(sys_syscall)
+
+sys_fork_wrapper:
+ add r0, sp, #S_OFF
+ b sys_fork
+ENDPROC(sys_fork_wrapper)
+
+sys_vfork_wrapper:
+ add r0, sp, #S_OFF
+ b sys_vfork
+ENDPROC(sys_vfork_wrapper)
+
+sys_execve_wrapper:
+ add r3, sp, #S_OFF
+ b sys_execve
+ENDPROC(sys_execve_wrapper)
+
+sys_clone_wrapper:
+ add ip, sp, #S_OFF
+ str ip, [sp, #4]
+ b sys_clone
+ENDPROC(sys_clone_wrapper)
+
+sys_sigsuspend_wrapper:
+ add r3, sp, #S_OFF
+ b sys_sigsuspend
+ENDPROC(sys_sigsuspend_wrapper)
+
+sys_rt_sigsuspend_wrapper:
+ add r2, sp, #S_OFF
+ b sys_rt_sigsuspend
+ENDPROC(sys_rt_sigsuspend_wrapper)
+
+sys_sigreturn_wrapper:
+ add r0, sp, #S_OFF
+ b sys_sigreturn
+ENDPROC(sys_sigreturn_wrapper)
+
+sys_rt_sigreturn_wrapper:
+ add r0, sp, #S_OFF
+ b sys_rt_sigreturn
+ENDPROC(sys_rt_sigreturn_wrapper)
+
+sys_sigaltstack_wrapper:
+ ldr r2, [sp, #S_OFF + S_SP]
+ b do_sigaltstack
+ENDPROC(sys_sigaltstack_wrapper)
+
+sys_statfs64_wrapper:
+ teq r1, #88
+ moveq r1, #84
+ b sys_statfs64
+ENDPROC(sys_statfs64_wrapper)
+
+sys_fstatfs64_wrapper:
+ teq r1, #88
+ moveq r1, #84
+ b sys_fstatfs64
+ENDPROC(sys_fstatfs64_wrapper)
+
+/*
+ * Note: off_4k (r5) is always units of 4K. If we can't do the requested
+ * offset, we return EINVAL.
+ */
+sys_mmap2:
+#if PAGE_SHIFT > 12
+ tst r5, #PGOFF_MASK
+ moveq r5, r5, lsr #PAGE_SHIFT - 12
+ streq r5, [sp, #4]
+ beq do_mmap2
+ mov r0, #-EINVAL
+ mov pc, lr
+#else
+ str r5, [sp, #4]
+ b do_mmap2
+#endif
+ENDPROC(sys_mmap2)
+
+ENTRY(pabort_ifar)
+ mrc p15, 0, r0, cr6, cr0, 2
+ENTRY(pabort_noifar)
+ mov pc, lr
+ENDPROC(pabort_ifar)
+ENDPROC(pabort_noifar)
+
+#ifdef CONFIG_OABI_COMPAT
+
+/*
+ * These are syscalls with argument register differences
+ */
+
+sys_oabi_pread64:
+ stmia sp, {r3, r4}
+ b sys_pread64
+ENDPROC(sys_oabi_pread64)
+
+sys_oabi_pwrite64:
+ stmia sp, {r3, r4}
+ b sys_pwrite64
+ENDPROC(sys_oabi_pwrite64)
+
+sys_oabi_truncate64:
+ mov r3, r2
+ mov r2, r1
+ b sys_truncate64
+ENDPROC(sys_oabi_truncate64)
+
+sys_oabi_ftruncate64:
+ mov r3, r2
+ mov r2, r1
+ b sys_ftruncate64
+ENDPROC(sys_oabi_ftruncate64)
+
+sys_oabi_readahead:
+ str r3, [sp]
+ mov r3, r2
+ mov r2, r1
+ b sys_readahead
+ENDPROC(sys_oabi_readahead)
+
+/*
+ * Let's declare a second syscall table for old ABI binaries
+ * using the compatibility syscall entries.
+ */
+#define ABI(native, compat) compat
+#define OBSOLETE(syscall) syscall
+
+ .type sys_oabi_call_table, #object
+ENTRY(sys_oabi_call_table)
+#include "calls.S"
+#undef ABI
+#undef OBSOLETE
+
+#endif
+
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
new file mode 100644
index 0000000..87ab4e1
--- /dev/null
+++ b/arch/arm/kernel/entry-header.S
@@ -0,0 +1,66 @@
+#include <linux/init.h>
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/errno.h>
+#include <asm/thread_info.h>
+
+@ Bad Abort numbers
+@ -----------------
+@
+#define BAD_PREFETCH 0
+#define BAD_DATA 1
+#define BAD_ADDREXCPTN 2
+#define BAD_IRQ 3
+#define BAD_UNDEFINSTR 4
+
+@
+@ Most of the stack format comes from struct pt_regs, but with
+@ the addition of 8 bytes for storing syscall args 5 and 6.
+@ This _must_ remain a multiple of 8 for EABI.
+@
+#define S_OFF 8
+
+/*
+ * The SWI code relies on the fact that R0 is at the bottom of the stack
+ * (due to slow/fast restore user regs).
+ */
+#if S_R0 != 0
+#error "Please fix"
+#endif
+
+ .macro zero_fp
+#ifdef CONFIG_FRAME_POINTER
+ mov fp, #0
+#endif
+ .endm
+
+ .macro get_thread_info, rd
+ mov \rd, sp, lsr #13
+ mov \rd, \rd, lsl #13
+ .endm
+
+ .macro alignment_trap, rtemp
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr \rtemp, .LCcralign
+ ldr \rtemp, [\rtemp]
+ mcr p15, 0, \rtemp, c1, c0
+#endif
+ .endm
+
+
+/*
+ * These are the registers used in the syscall handler, and allow us to
+ * have in theory up to 7 arguments to a function - r0 to r6.
+ *
+ * r7 is reserved for the system call number for thumb mode.
+ *
+ * Note that tbl == why is intentional.
+ *
+ * We must set at least "tsk" and "why" when calling ret_with_reschedule.
+ */
+scno .req r7 @ syscall number
+tbl .req r8 @ syscall table pointer
+why .req r8 @ Linux syscall (!= 0)
+tsk .req r9 @ current thread_info
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
new file mode 100644
index 0000000..36f81d9
--- /dev/null
+++ b/arch/arm/kernel/fiq.c
@@ -0,0 +1,181 @@
+/*
+ * linux/arch/arm/kernel/fiq.c
+ *
+ * Copyright (C) 1998 Russell King
+ * Copyright (C) 1998, 1999 Phil Blundell
+ *
+ * FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
+ *
+ * FIQ support re-written by Russell King to be more generic
+ *
+ * We now properly support a method by which the FIQ handlers can
+ * be stacked onto the vector. We still do not support sharing
+ * the FIQ vector itself.
+ *
+ * Operation is as follows:
+ * 1. Owner A claims FIQ:
+ * - default_fiq relinquishes control.
+ * 2. Owner A:
+ * - inserts code.
+ * - sets any registers,
+ * - enables FIQ.
+ * 3. Owner B claims FIQ:
+ * - if owner A has a relinquish function.
+ * - disable FIQs.
+ * - saves any registers.
+ * - returns zero.
+ * 4. Owner B:
+ * - inserts code.
+ * - sets any registers,
+ * - enables FIQ.
+ * 5. Owner B releases FIQ:
+ * - Owner A is asked to reacquire FIQ:
+ * - inserts code.
+ * - restores saved registers.
+ * - enables FIQ.
+ * 6. Goto 3
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+
+#include <asm/cacheflush.h>
+#include <asm/fiq.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+static unsigned long no_fiq_insn;
+
+/* Default reacquire function
+ * - we always relinquish FIQ control
+ * - we always reacquire FIQ control
+ */
+static int fiq_def_op(void *ref, int relinquish)
+{
+ if (!relinquish)
+ set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
+
+ return 0;
+}
+
+static struct fiq_handler default_owner = {
+ .name = "default",
+ .fiq_op = fiq_def_op,
+};
+
+static struct fiq_handler *current_fiq = &default_owner;
+
+int show_fiq_list(struct seq_file *p, void *v)
+{
+ if (current_fiq != &default_owner)
+ seq_printf(p, "FIQ: %s\n", current_fiq->name);
+
+ return 0;
+}
+
+void set_fiq_handler(void *start, unsigned int length)
+{
+ memcpy((void *)0xffff001c, start, length);
+ flush_icache_range(0xffff001c, 0xffff001c + length);
+ if (!vectors_high())
+ flush_icache_range(0x1c, 0x1c + length);
+}
+
+/*
+ * Taking an interrupt in FIQ mode is death, so both these functions
+ * disable irqs for the duration. Note - these functions are almost
+ * entirely coded in assembly.
+ */
+void __attribute__((naked)) set_fiq_regs(struct pt_regs *regs)
+{
+ register unsigned long tmp;
+ asm volatile (
+ "mov ip, sp\n\
+ stmfd sp!, {fp, ip, lr, pc}\n\
+ sub fp, ip, #4\n\
+ mrs %0, cpsr\n\
+ msr cpsr_c, %2 @ select FIQ mode\n\
+ mov r0, r0\n\
+ ldmia %1, {r8 - r14}\n\
+ msr cpsr_c, %0 @ return to SVC mode\n\
+ mov r0, r0\n\
+ ldmfd sp, {fp, sp, pc}"
+ : "=&r" (tmp)
+ : "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
+}
+
+void __attribute__((naked)) get_fiq_regs(struct pt_regs *regs)
+{
+ register unsigned long tmp;
+ asm volatile (
+ "mov ip, sp\n\
+ stmfd sp!, {fp, ip, lr, pc}\n\
+ sub fp, ip, #4\n\
+ mrs %0, cpsr\n\
+ msr cpsr_c, %2 @ select FIQ mode\n\
+ mov r0, r0\n\
+ stmia %1, {r8 - r14}\n\
+ msr cpsr_c, %0 @ return to SVC mode\n\
+ mov r0, r0\n\
+ ldmfd sp, {fp, sp, pc}"
+ : "=&r" (tmp)
+ : "r" (&regs->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
+}
+
+int claim_fiq(struct fiq_handler *f)
+{
+ int ret = 0;
+
+ if (current_fiq) {
+ ret = -EBUSY;
+
+ if (current_fiq->fiq_op != NULL)
+ ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
+ }
+
+ if (!ret) {
+ f->next = current_fiq;
+ current_fiq = f;
+ }
+
+ return ret;
+}
+
+void release_fiq(struct fiq_handler *f)
+{
+ if (current_fiq != f) {
+ printk(KERN_ERR "%s FIQ trying to release %s FIQ\n",
+ f->name, current_fiq->name);
+ dump_stack();
+ return;
+ }
+
+ do
+ current_fiq = current_fiq->next;
+ while (current_fiq->fiq_op(current_fiq->dev_id, 0));
+}
+
+void enable_fiq(int fiq)
+{
+ enable_irq(fiq + FIQ_START);
+}
+
+void disable_fiq(int fiq)
+{
+ disable_irq(fiq + FIQ_START);
+}
+
+EXPORT_SYMBOL(set_fiq_handler);
+EXPORT_SYMBOL(set_fiq_regs);
+EXPORT_SYMBOL(get_fiq_regs);
+EXPORT_SYMBOL(claim_fiq);
+EXPORT_SYMBOL(release_fiq);
+EXPORT_SYMBOL(enable_fiq);
+EXPORT_SYMBOL(disable_fiq);
+
+void __init init_FIQ(void)
+{
+ no_fiq_insn = *(unsigned long *)0xffff001c;
+}
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
new file mode 100644
index 0000000..6c90479
--- /dev/null
+++ b/arch/arm/kernel/ftrace.c
@@ -0,0 +1,103 @@
+/*
+ * Dynamic function tracing support.
+ *
+ * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com>
+ *
+ * For licencing details, see COPYING.
+ *
+ * Defines low-level handling of mcount calls when the kernel
+ * is compiled with the -pg flag. When using dynamic ftrace, the
+ * mcount call-sites get patched lazily with NOP till they are
+ * enabled. All code mutation routines here take effect atomically.
+ */
+
+#include <linux/ftrace.h>
+
+#include <asm/cacheflush.h>
+#include <asm/ftrace.h>
+
+#define PC_OFFSET 8
+#define BL_OPCODE 0xeb000000
+#define BL_OFFSET_MASK 0x00ffffff
+
+static unsigned long bl_insn;
+static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */
+
+unsigned char *ftrace_nop_replace(void)
+{
+ return (char *)&NOP;
+}
+
+/* construct a branch (BL) instruction to addr */
+unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr)
+{
+ long offset;
+
+ offset = (long)addr - (long)(pc + PC_OFFSET);
+ if (unlikely(offset < -33554432 || offset > 33554428)) {
+ /* Can't generate branches that far (from ARM ARM). Ftrace
+ * doesn't generate branches outside of kernel text.
+ */
+ WARN_ON_ONCE(1);
+ return NULL;
+ }
+ offset = (offset >> 2) & BL_OFFSET_MASK;
+ bl_insn = BL_OPCODE | offset;
+ return (unsigned char *)&bl_insn;
+}
+
+int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned long err = 0, replaced = 0, old, new;
+
+ old = *(unsigned long *)old_code;
+ new = *(unsigned long *)new_code;
+
+ __asm__ __volatile__ (
+ "1: ldr %1, [%2] \n"
+ " cmp %1, %4 \n"
+ "2: streq %3, [%2] \n"
+ " cmpne %1, %3 \n"
+ " movne %0, #2 \n"
+ "3:\n"
+
+ ".section .fixup, \"ax\"\n"
+ "4: mov %0, #1 \n"
+ " b 3b \n"
+ ".previous\n"
+
+ ".section __ex_table, \"a\"\n"
+ " .long 1b, 4b \n"
+ " .long 2b, 4b \n"
+ ".previous\n"
+
+ : "=r"(err), "=r"(replaced)
+ : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced)
+ : "memory");
+
+ if (!err && (replaced == old))
+ flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
+
+ return err;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ int ret;
+ unsigned long pc, old;
+ unsigned char *new;
+
+ pc = (unsigned long)&ftrace_call;
+ memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(pc, (unsigned long)func);
+ ret = ftrace_modify_code(pc, (unsigned char *)&old, new);
+ return ret;
+}
+
+/* run from kstop_machine */
+int __init ftrace_dyn_arch_init(void *data)
+{
+ ftrace_mcount_set(data);
+ return 0;
+}
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
new file mode 100644
index 0000000..bde52df
--- /dev/null
+++ b/arch/arm/kernel/head-common.S
@@ -0,0 +1,260 @@
+/*
+ * linux/arch/arm/kernel/head-common.S
+ *
+ * Copyright (C) 1994-2002 Russell King
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * 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.
+ *
+ */
+
+#define ATAG_CORE 0x54410001
+#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
+
+ .type __switch_data, %object
+__switch_data:
+ .long __mmap_switched
+ .long __data_loc @ r4
+ .long __data_start @ r5
+ .long __bss_start @ r6
+ .long _end @ r7
+ .long processor_id @ r4
+ .long __machine_arch_type @ r5
+ .long __atags_pointer @ r6
+ .long cr_alignment @ r7
+ .long init_thread_union + THREAD_START_SP @ sp
+
+/*
+ * The following fragment of code is executed with the MMU on in MMU mode,
+ * and uses absolute addresses; this is not position independent.
+ *
+ * r0 = cp#15 control register
+ * r1 = machine ID
+ * r2 = atags pointer
+ * r9 = processor ID
+ */
+__mmap_switched:
+ adr r3, __switch_data + 4
+
+ ldmia r3!, {r4, r5, r6, r7}
+ cmp r4, r5 @ Copy data segment if needed
+1: cmpne r5, r6
+ ldrne fp, [r4], #4
+ strne fp, [r5], #4
+ bne 1b
+
+ mov fp, #0 @ Clear BSS (and zero fp)
+1: cmp r6, r7
+ strcc fp, [r6],#4
+ bcc 1b
+
+ ldmia r3, {r4, r5, r6, r7, sp}
+ str r9, [r4] @ Save processor ID
+ str r1, [r5] @ Save machine type
+ str r2, [r6] @ Save atags pointer
+ bic r4, r0, #CR_A @ Clear 'A' bit
+ stmia r7, {r0, r4} @ Save control register values
+ b start_kernel
+ENDPROC(__mmap_switched)
+
+/*
+ * Exception handling. Something went wrong and we can't proceed. We
+ * ought to tell the user, but since we don't have any guarantee that
+ * we're even running on the right architecture, we do virtually nothing.
+ *
+ * If CONFIG_DEBUG_LL is set we try to print out something about the error
+ * and hope for the best (useful if bootloader fails to pass a proper
+ * machine ID for example).
+ */
+__error_p:
+#ifdef CONFIG_DEBUG_LL
+ adr r0, str_p1
+ bl printascii
+ mov r0, r9
+ bl printhex8
+ adr r0, str_p2
+ bl printascii
+ b __error
+str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x"
+str_p2: .asciz ").\n"
+ .align
+#endif
+ENDPROC(__error_p)
+
+__error_a:
+#ifdef CONFIG_DEBUG_LL
+ mov r4, r1 @ preserve machine ID
+ adr r0, str_a1
+ bl printascii
+ mov r0, r4
+ bl printhex8
+ adr r0, str_a2
+ bl printascii
+ adr r3, 3f
+ ldmia r3, {r4, r5, r6} @ get machine desc list
+ sub r4, r3, r4 @ get offset between virt&phys
+ add r5, r5, r4 @ convert virt addresses to
+ add r6, r6, r4 @ physical address space
+1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type
+ bl printhex8
+ mov r0, #'\t'
+ bl printch
+ ldr r0, [r5, #MACHINFO_NAME] @ get machine name
+ add r0, r0, r4
+ bl printascii
+ mov r0, #'\n'
+ bl printch
+ add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
+ cmp r5, r6
+ blo 1b
+ adr r0, str_a3
+ bl printascii
+ b __error
+ENDPROC(__error_a)
+
+str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x"
+str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
+str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n"
+ .align
+#endif
+
+__error:
+#ifdef CONFIG_ARCH_RPC
+/*
+ * Turn the screen red on a error - RiscPC only.
+ */
+ mov r0, #0x02000000
+ mov r3, #0x11
+ orr r3, r3, r3, lsl #8
+ orr r3, r3, r3, lsl #16
+ str r3, [r0], #4
+ str r3, [r0], #4
+ str r3, [r0], #4
+ str r3, [r0], #4
+#endif
+1: mov r0, r0
+ b 1b
+ENDPROC(__error)
+
+
+/*
+ * Read processor ID register (CP#15, CR0), and look up in the linker-built
+ * supported processor list. Note that we can't use the absolute addresses
+ * for the __proc_info lists since we aren't running with the MMU on
+ * (and therefore, we are not in the correct address space). We have to
+ * calculate the offset.
+ *
+ * r9 = cpuid
+ * Returns:
+ * r3, r4, r6 corrupted
+ * r5 = proc_info pointer in physical address space
+ * r9 = cpuid (preserved)
+ */
+__lookup_processor_type:
+ adr r3, 3f
+ ldmda r3, {r5 - r7}
+ sub r3, r3, r7 @ get offset between virt&phys
+ add r5, r5, r3 @ convert virt addresses to
+ add r6, r6, r3 @ physical address space
+1: ldmia r5, {r3, r4} @ value, mask
+ and r4, r4, r9 @ mask wanted bits
+ teq r3, r4
+ beq 2f
+ add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
+ cmp r5, r6
+ blo 1b
+ mov r5, #0 @ unknown processor
+2: mov pc, lr
+ENDPROC(__lookup_processor_type)
+
+/*
+ * This provides a C-API version of the above function.
+ */
+ENTRY(lookup_processor_type)
+ stmfd sp!, {r4 - r7, r9, lr}
+ mov r9, r0
+ bl __lookup_processor_type
+ mov r0, r5
+ ldmfd sp!, {r4 - r7, r9, pc}
+ENDPROC(lookup_processor_type)
+
+/*
+ * Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for
+ * more information about the __proc_info and __arch_info structures.
+ */
+ .long __proc_info_begin
+ .long __proc_info_end
+3: .long .
+ .long __arch_info_begin
+ .long __arch_info_end
+
+/*
+ * Lookup machine architecture in the linker-build list of architectures.
+ * Note that we can't use the absolute addresses for the __arch_info
+ * lists since we aren't running with the MMU on (and therefore, we are
+ * not in the correct address space). We have to calculate the offset.
+ *
+ * r1 = machine architecture number
+ * Returns:
+ * r3, r4, r6 corrupted
+ * r5 = mach_info pointer in physical address space
+ */
+__lookup_machine_type:
+ adr r3, 3b
+ ldmia r3, {r4, r5, r6}
+ sub r3, r3, r4 @ get offset between virt&phys
+ add r5, r5, r3 @ convert virt addresses to
+ add r6, r6, r3 @ physical address space
+1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
+ teq r3, r1 @ matches loader number?
+ beq 2f @ found
+ add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
+ cmp r5, r6
+ blo 1b
+ mov r5, #0 @ unknown machine
+2: mov pc, lr
+ENDPROC(__lookup_machine_type)
+
+/*
+ * This provides a C-API version of the above function.
+ */
+ENTRY(lookup_machine_type)
+ stmfd sp!, {r4 - r6, lr}
+ mov r1, r0
+ bl __lookup_machine_type
+ mov r0, r5
+ ldmfd sp!, {r4 - r6, pc}
+ENDPROC(lookup_machine_type)
+
+/* Determine validity of the r2 atags pointer. The heuristic requires
+ * that the pointer be aligned, in the first 16k of physical RAM and
+ * that the ATAG_CORE marker is first and present. Future revisions
+ * of this function may be more lenient with the physical address and
+ * may also be able to move the ATAGS block if necessary.
+ *
+ * r8 = machinfo
+ *
+ * Returns:
+ * r2 either valid atags pointer, or zero
+ * r5, r6 corrupted
+ */
+__vet_atags:
+ tst r2, #0x3 @ aligned?
+ bne 1f
+
+ ldr r5, [r2, #0] @ is first tag ATAG_CORE?
+ subs r5, r5, #ATAG_CORE_SIZE
+ bne 1f
+ ldr r5, [r2, #4]
+ ldr r6, =ATAG_CORE
+ cmp r5, r6
+ bne 1f
+
+ mov pc, lr @ atag pointer is ok
+
+1: mov r2, #0
+ mov pc, lr
+ENDPROC(__vet_atags)
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
new file mode 100644
index 0000000..cc87e17
--- /dev/null
+++ b/arch/arm/kernel/head-nommu.S
@@ -0,0 +1,90 @@
+/*
+ * linux/arch/arm/kernel/head-nommu.S
+ *
+ * Copyright (C) 1994-2002 Russell King
+ * Copyright (C) 2003-2006 Hyok S. Choi
+ *
+ * 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.
+ *
+ * Common kernel startup code (non-paged MM)
+ *
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/system.h>
+
+/*
+ * Kernel startup entry point.
+ * ---------------------------
+ *
+ * This is normally called from the decompressor code. The requirements
+ * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
+ * r1 = machine nr.
+ *
+ * See linux/arch/arm/tools/mach-types for the complete list of machine
+ * numbers for r1.
+ *
+ */
+ .section ".text.head", "ax"
+ENTRY(stext)
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
+ @ and irqs disabled
+#ifndef CONFIG_CPU_CP15
+ ldr r9, =CONFIG_PROCESSOR_ID
+#else
+ mrc p15, 0, r9, c0, c0 @ get processor id
+#endif
+ bl __lookup_processor_type @ r5=procinfo r9=cpuid
+ movs r10, r5 @ invalid processor (r5=0)?
+ beq __error_p @ yes, error 'p'
+ bl __lookup_machine_type @ r5=machinfo
+ movs r8, r5 @ invalid machine (r5=0)?
+ beq __error_a @ yes, error 'a'
+
+ ldr r13, __switch_data @ address to jump to after
+ @ the initialization is done
+ adr lr, __after_proc_init @ return (PIC) address
+ add pc, r10, #PROCINFO_INITFUNC
+ENDPROC(stext)
+
+/*
+ * Set the Control Register and Read the process ID.
+ */
+__after_proc_init:
+#ifdef CONFIG_CPU_CP15
+ mrc p15, 0, r0, c1, c0, 0 @ read control reg
+#ifdef CONFIG_ALIGNMENT_TRAP
+ orr r0, r0, #CR_A
+#else
+ bic r0, r0, #CR_A
+#endif
+#ifdef CONFIG_CPU_DCACHE_DISABLE
+ bic r0, r0, #CR_C
+#endif
+#ifdef CONFIG_CPU_BPREDICT_DISABLE
+ bic r0, r0, #CR_Z
+#endif
+#ifdef CONFIG_CPU_ICACHE_DISABLE
+ bic r0, r0, #CR_I
+#endif
+#ifdef CONFIG_CPU_HIGH_VECTOR
+ orr r0, r0, #CR_V
+#else
+ bic r0, r0, #CR_V
+#endif
+ mcr p15, 0, r0, c1, c0, 0 @ write control reg
+#endif /* CONFIG_CPU_CP15 */
+
+ mov pc, r13 @ clear the BSS and jump
+ @ to start_kernel
+ENDPROC(__after_proc_init)
+ .ltorg
+
+#include "head-common.S"
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
new file mode 100644
index 0000000..21e17dc
--- /dev/null
+++ b/arch/arm/kernel/head.S
@@ -0,0 +1,330 @@
+/*
+ * linux/arch/arm/kernel/head.S
+ *
+ * Copyright (C) 1994-2002 Russell King
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * 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.
+ *
+ * Kernel startup code for all 32-bit CPUs
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/memory.h>
+#include <asm/thread_info.h>
+#include <asm/system.h>
+
+#if (PHYS_OFFSET & 0x001fffff)
+#error "PHYS_OFFSET must be at an even 2MiB boundary!"
+#endif
+
+#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
+#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
+
+
+/*
+ * swapper_pg_dir is the virtual address of the initial page table.
+ * We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must
+ * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect
+ * the least significant 16 bits to be 0x8000, but we could probably
+ * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
+ */
+#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
+#error KERNEL_RAM_VADDR must start at 0xXXXX8000
+#endif
+
+ .globl swapper_pg_dir
+ .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
+
+ .macro pgtbl, rd
+ ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
+ .endm
+
+#ifdef CONFIG_XIP_KERNEL
+#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
+#define KERNEL_END _edata_loc
+#else
+#define KERNEL_START KERNEL_RAM_VADDR
+#define KERNEL_END _end
+#endif
+
+/*
+ * Kernel startup entry point.
+ * ---------------------------
+ *
+ * This is normally called from the decompressor code. The requirements
+ * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
+ * r1 = machine nr, r2 = atags pointer.
+ *
+ * This code is mostly position independent, so if you link the kernel at
+ * 0xc0008000, you call this at __pa(0xc0008000).
+ *
+ * See linux/arch/arm/tools/mach-types for the complete list of machine
+ * numbers for r1.
+ *
+ * We're trying to keep crap to a minimum; DO NOT add any machine specific
+ * crap here - that's what the boot loader (or in extreme, well justified
+ * circumstances, zImage) is for.
+ */
+ .section ".text.head", "ax"
+ENTRY(stext)
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
+ @ and irqs disabled
+ mrc p15, 0, r9, c0, c0 @ get processor id
+ bl __lookup_processor_type @ r5=procinfo r9=cpuid
+ movs r10, r5 @ invalid processor (r5=0)?
+ beq __error_p @ yes, error 'p'
+ bl __lookup_machine_type @ r5=machinfo
+ movs r8, r5 @ invalid machine (r5=0)?
+ beq __error_a @ yes, error 'a'
+ bl __vet_atags
+ bl __create_page_tables
+
+ /*
+ * The following calls CPU specific code in a position independent
+ * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
+ * xxx_proc_info structure selected by __lookup_machine_type
+ * above. On return, the CPU will be ready for the MMU to be
+ * turned on, and r0 will hold the CPU control register value.
+ */
+ ldr r13, __switch_data @ address to jump to after
+ @ mmu has been enabled
+ adr lr, __enable_mmu @ return (PIC) address
+ add pc, r10, #PROCINFO_INITFUNC
+ENDPROC(stext)
+
+#if defined(CONFIG_SMP)
+ENTRY(secondary_startup)
+ /*
+ * Common entry point for secondary CPUs.
+ *
+ * Ensure that we're in SVC mode, and IRQs are disabled. Lookup
+ * the processor type - there is no need to check the machine type
+ * as it has already been validated by the primary processor.
+ */
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
+ mrc p15, 0, r9, c0, c0 @ get processor id
+ bl __lookup_processor_type
+ movs r10, r5 @ invalid processor?
+ moveq r0, #'p' @ yes, error 'p'
+ beq __error
+
+ /*
+ * Use the page tables supplied from __cpu_up.
+ */
+ adr r4, __secondary_data
+ ldmia r4, {r5, r7, r13} @ address to jump to after
+ sub r4, r4, r5 @ mmu has been enabled
+ ldr r4, [r7, r4] @ get secondary_data.pgdir
+ adr lr, __enable_mmu @ return address
+ add pc, r10, #PROCINFO_INITFUNC @ initialise processor
+ @ (return control reg)
+ENDPROC(secondary_startup)
+
+ /*
+ * r6 = &secondary_data
+ */
+ENTRY(__secondary_switched)
+ ldr sp, [r7, #4] @ get secondary_data.stack
+ mov fp, #0
+ b secondary_start_kernel
+ENDPROC(__secondary_switched)
+
+ .type __secondary_data, %object
+__secondary_data:
+ .long .
+ .long secondary_data
+ .long __secondary_switched
+#endif /* defined(CONFIG_SMP) */
+
+
+
+/*
+ * Setup common bits before finally enabling the MMU. Essentially
+ * this is just loading the page table pointer and domain access
+ * registers.
+ */
+__enable_mmu:
+#ifdef CONFIG_ALIGNMENT_TRAP
+ orr r0, r0, #CR_A
+#else
+ bic r0, r0, #CR_A
+#endif
+#ifdef CONFIG_CPU_DCACHE_DISABLE
+ bic r0, r0, #CR_C
+#endif
+#ifdef CONFIG_CPU_BPREDICT_DISABLE
+ bic r0, r0, #CR_Z
+#endif
+#ifdef CONFIG_CPU_ICACHE_DISABLE
+ bic r0, r0, #CR_I
+#endif
+ mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
+ domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
+ domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
+ domain_val(DOMAIN_IO, DOMAIN_CLIENT))
+ mcr p15, 0, r5, c3, c0, 0 @ load domain access register
+ mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
+ b __turn_mmu_on
+ENDPROC(__enable_mmu)
+
+/*
+ * Enable the MMU. This completely changes the structure of the visible
+ * memory space. You will not be able to trace execution through this.
+ * If you have an enquiry about this, *please* check the linux-arm-kernel
+ * mailing list archives BEFORE sending another post to the list.
+ *
+ * r0 = cp#15 control register
+ * r13 = *virtual* address to jump to upon completion
+ *
+ * other registers depend on the function called upon completion
+ */
+ .align 5
+__turn_mmu_on:
+ mov r0, r0
+ mcr p15, 0, r0, c1, c0, 0 @ write control reg
+ mrc p15, 0, r3, c0, c0, 0 @ read id reg
+ mov r3, r3
+ mov r3, r3
+ mov pc, r13
+ENDPROC(__turn_mmu_on)
+
+
+/*
+ * Setup the initial page tables. We only setup the barest
+ * amount which are required to get the kernel running, which
+ * generally means mapping in the kernel code.
+ *
+ * r8 = machinfo
+ * r9 = cpuid
+ * r10 = procinfo
+ *
+ * Returns:
+ * r0, r3, r6, r7 corrupted
+ * r4 = physical page table address
+ */
+__create_page_tables:
+ pgtbl r4 @ page table address
+
+ /*
+ * Clear the 16K level 1 swapper page table
+ */
+ mov r0, r4
+ mov r3, #0
+ add r6, r0, #0x4000
+1: str r3, [r0], #4
+ str r3, [r0], #4
+ str r3, [r0], #4
+ str r3, [r0], #4
+ teq r0, r6
+ bne 1b
+
+ ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
+
+ /*
+ * Create identity mapping for first MB of kernel to
+ * cater for the MMU enable. This identity mapping
+ * will be removed by paging_init(). We use our current program
+ * counter to determine corresponding section base address.
+ */
+ mov r6, pc, lsr #20 @ start of kernel section
+ orr r3, r7, r6, lsl #20 @ flags + kernel base
+ str r3, [r4, r6, lsl #2] @ identity mapping
+
+ /*
+ * Now setup the pagetables for our kernel direct
+ * mapped region.
+ */
+ add r0, r4, #(KERNEL_START & 0xff000000) >> 18
+ str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
+ ldr r6, =(KERNEL_END - 1)
+ add r0, r0, #4
+ add r6, r4, r6, lsr #18
+1: cmp r0, r6
+ add r3, r3, #1 << 20
+ strls r3, [r0], #4
+ bls 1b
+
+#ifdef CONFIG_XIP_KERNEL
+ /*
+ * Map some ram to cover our .data and .bss areas.
+ */
+ orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
+ .if (KERNEL_RAM_PADDR & 0x00f00000)
+ orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
+ .endif
+ add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
+ str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
+ ldr r6, =(_end - 1)
+ add r0, r0, #4
+ add r6, r4, r6, lsr #18
+1: cmp r0, r6
+ add r3, r3, #1 << 20
+ strls r3, [r0], #4
+ bls 1b
+#endif
+
+ /*
+ * Then map first 1MB of ram in case it contains our boot params.
+ */
+ add r0, r4, #PAGE_OFFSET >> 18
+ orr r6, r7, #(PHYS_OFFSET & 0xff000000)
+ .if (PHYS_OFFSET & 0x00f00000)
+ orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
+ .endif
+ str r6, [r0]
+
+#ifdef CONFIG_DEBUG_LL
+ ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
+ /*
+ * Map in IO space for serial debugging.
+ * This allows debug messages to be output
+ * via a serial console before paging_init.
+ */
+ ldr r3, [r8, #MACHINFO_PGOFFIO]
+ add r0, r4, r3
+ rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
+ cmp r3, #0x0800 @ limit to 512MB
+ movhi r3, #0x0800
+ add r6, r0, r3
+ ldr r3, [r8, #MACHINFO_PHYSIO]
+ orr r3, r3, r7
+1: str r3, [r0], #4
+ add r3, r3, #1 << 20
+ teq r0, r6
+ bne 1b
+#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
+ /*
+ * If we're using the NetWinder or CATS, we also need to map
+ * in the 16550-type serial port for the debug messages
+ */
+ add r0, r4, #0xff000000 >> 18
+ orr r3, r7, #0x7c000000
+ str r3, [r0]
+#endif
+#ifdef CONFIG_ARCH_RPC
+ /*
+ * Map in screen at 0x02000000 & SCREEN2_BASE
+ * Similar reasons here - for debug. This is
+ * only for Acorn RiscPC architectures.
+ */
+ add r0, r4, #0x02000000 >> 18
+ orr r3, r7, #0x02000000
+ str r3, [r0]
+ add r0, r4, #0xd8000000 >> 18
+ str r3, [r0]
+#endif
+#endif
+ mov pc, lr
+ENDPROC(__create_page_tables)
+ .ltorg
+
+#include "head-common.S"
diff --git a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c
new file mode 100644
index 0000000..0bbf806
--- /dev/null
+++ b/arch/arm/kernel/init_task.c
@@ -0,0 +1,43 @@
+/*
+ * linux/arch/arm/kernel/init_task.c
+ */
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/mqueue.h>
+#include <linux/uaccess.h>
+
+#include <asm/pgtable.h>
+
+static struct fs_struct init_fs = INIT_FS;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+struct mm_struct init_mm = INIT_MM(init_mm);
+
+EXPORT_SYMBOL(init_mm);
+
+/*
+ * Initial thread structure.
+ *
+ * We need to make sure that this is 8192-byte aligned due to the
+ * way process stacks are handled. This is done by making sure
+ * the linker maps this in the .text segment right after head.S,
+ * and making head.S ensure the proper alignment.
+ *
+ * The things we do for performance..
+ */
+union thread_union init_thread_union
+ __attribute__((__section__(".data.init_task"))) =
+ { INIT_THREAD_INFO(init_task) };
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_task);
diff --git a/arch/arm/kernel/io.c b/arch/arm/kernel/io.c
new file mode 100644
index 0000000..f447030
--- /dev/null
+++ b/arch/arm/kernel/io.c
@@ -0,0 +1,50 @@
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+/*
+ * Copy data from IO memory space to "real" memory space.
+ * This needs to be optimized.
+ */
+void _memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
+{
+ unsigned char *t = to;
+ while (count) {
+ count--;
+ *t = readb(from);
+ t++;
+ from++;
+ }
+}
+
+/*
+ * Copy data from "real" memory space to IO memory space.
+ * This needs to be optimized.
+ */
+void _memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
+{
+ const unsigned char *f = from;
+ while (count) {
+ count--;
+ writeb(*f, to);
+ f++;
+ to++;
+ }
+}
+
+/*
+ * "memset" on IO memory space.
+ * This needs to be optimized.
+ */
+void _memset_io(volatile void __iomem *dst, int c, size_t count)
+{
+ while (count) {
+ count--;
+ writeb(c, dst);
+ dst++;
+ }
+}
+
+EXPORT_SYMBOL(_memcpy_fromio);
+EXPORT_SYMBOL(_memcpy_toio);
+EXPORT_SYMBOL(_memset_io);
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
new file mode 100644
index 0000000..2f3eb79
--- /dev/null
+++ b/arch/arm/kernel/irq.c
@@ -0,0 +1,209 @@
+/*
+ * linux/arch/arm/kernel/irq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ * Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
+ *
+ * Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation.
+ * Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and
+ * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains the code used by various IRQ handling routines:
+ * asking for different IRQ's should be done through these routines
+ * instead of just grabbing them. Thus setups with different IRQ numbers
+ * shouldn't result in any weird surprises, and installing new handlers
+ * should be easier.
+ *
+ * IRQ's are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/kallsyms.h>
+#include <linux/proc_fs.h>
+
+#include <asm/system.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+
+/*
+ * No architecture-specific irq_finish function defined in arm/arch/irqs.h.
+ */
+#ifndef irq_finish
+#define irq_finish(irq) do { } while (0)
+#endif
+
+void (*init_arch_irq)(void) __initdata = NULL;
+unsigned long irq_err_count;
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v, cpu;
+ struct irqaction * action;
+ unsigned long flags;
+
+ if (i == 0) {
+ char cpuname[12];
+
+ seq_printf(p, " ");
+ for_each_present_cpu(cpu) {
+ sprintf(cpuname, "CPU%d", cpu);
+ seq_printf(p, " %10s", cpuname);
+ }
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto unlock;
+
+ seq_printf(p, "%3d: ", i);
+ for_each_present_cpu(cpu)
+ seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
+ seq_printf(p, " %10s", irq_desc[i].chip->name ? : "-");
+ seq_printf(p, " %s", action->name);
+ for (action = action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+
+ seq_putc(p, '\n');
+unlock:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ } else if (i == NR_IRQS) {
+#ifdef CONFIG_ARCH_ACORN
+ show_fiq_list(p, v);
+#endif
+#ifdef CONFIG_SMP
+ show_ipi_list(p);
+ show_local_irqs(p);
+#endif
+ seq_printf(p, "Err: %10lu\n", irq_err_count);
+ }
+ return 0;
+}
+
+/* Handle bad interrupts */
+static struct irq_desc bad_irq_desc = {
+ .handle_irq = handle_bad_irq,
+ .lock = SPIN_LOCK_UNLOCKED
+};
+
+/*
+ * do_IRQ handles all hardware IRQ's. Decoded IRQs should not
+ * come via this function. Instead, they should provide their
+ * own 'handler'
+ */
+asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
+{
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ irq_enter();
+
+ /*
+ * Some hardware gives randomly wrong interrupts. Rather
+ * than crashing, do something sensible.
+ */
+ if (irq >= NR_IRQS)
+ handle_bad_irq(irq, &bad_irq_desc);
+ else
+ generic_handle_irq(irq);
+
+ /* AT91 specific workaround */
+ irq_finish(irq);
+
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
+void set_irq_flags(unsigned int irq, unsigned int iflags)
+{
+ struct irq_desc *desc;
+ unsigned long flags;
+
+ if (irq >= NR_IRQS) {
+ printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
+ return;
+ }
+
+ desc = irq_desc + irq;
+ spin_lock_irqsave(&desc->lock, flags);
+ desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ if (iflags & IRQF_VALID)
+ desc->status &= ~IRQ_NOREQUEST;
+ if (iflags & IRQF_PROBE)
+ desc->status &= ~IRQ_NOPROBE;
+ if (!(iflags & IRQF_NOAUTOEN))
+ desc->status &= ~IRQ_NOAUTOEN;
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+void __init init_IRQ(void)
+{
+ int irq;
+
+ for (irq = 0; irq < NR_IRQS; irq++)
+ irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
+
+#ifdef CONFIG_SMP
+ bad_irq_desc.affinity = CPU_MASK_ALL;
+ bad_irq_desc.cpu = smp_processor_id();
+#endif
+ init_arch_irq();
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu)
+{
+ pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu);
+
+ spin_lock_irq(&desc->lock);
+ desc->chip->set_affinity(irq, cpumask_of_cpu(cpu));
+ spin_unlock_irq(&desc->lock);
+}
+
+/*
+ * The CPU has been marked offline. Migrate IRQs off this CPU. If
+ * the affinity settings do not allow other CPUs, force them onto any
+ * available CPU.
+ */
+void migrate_irqs(void)
+{
+ unsigned int i, cpu = smp_processor_id();
+
+ for (i = 0; i < NR_IRQS; i++) {
+ struct irq_desc *desc = irq_desc + i;
+
+ if (desc->cpu == cpu) {
+ unsigned int newcpu = any_online_cpu(desc->affinity);
+
+ if (newcpu == NR_CPUS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n",
+ i, cpu);
+
+ cpus_setall(desc->affinity);
+ newcpu = any_online_cpu(desc->affinity);
+ }
+
+ route_irq(desc, i, newcpu);
+ }
+ }
+}
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/arm/kernel/isa.c b/arch/arm/kernel/isa.c
new file mode 100644
index 0000000..50a30bc
--- /dev/null
+++ b/arch/arm/kernel/isa.c
@@ -0,0 +1,74 @@
+/*
+ * linux/arch/arm/kernel/isa.c
+ *
+ * Copyright (C) 1999 Phil Blundell
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * ISA shared memory and I/O port support, and is required to support
+ * iopl, inb, outb and friends in userspace via glibc emulation.
+ */
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+#include <linux/init.h>
+
+static unsigned int isa_membase, isa_portbase, isa_portshift;
+
+static ctl_table ctl_isa_vars[4] = {
+ {
+ .ctl_name = BUS_ISA_MEM_BASE,
+ .procname = "membase",
+ .data = &isa_membase,
+ .maxlen = sizeof(isa_membase),
+ .mode = 0444,
+ .proc_handler = &proc_dointvec,
+ }, {
+ .ctl_name = BUS_ISA_PORT_BASE,
+ .procname = "portbase",
+ .data = &isa_portbase,
+ .maxlen = sizeof(isa_portbase),
+ .mode = 0444,
+ .proc_handler = &proc_dointvec,
+ }, {
+ .ctl_name = BUS_ISA_PORT_SHIFT,
+ .procname = "portshift",
+ .data = &isa_portshift,
+ .maxlen = sizeof(isa_portshift),
+ .mode = 0444,
+ .proc_handler = &proc_dointvec,
+ }, {0}
+};
+
+static struct ctl_table_header *isa_sysctl_header;
+
+static ctl_table ctl_isa[2] = {
+ {
+ .ctl_name = CTL_BUS_ISA,
+ .procname = "isa",
+ .mode = 0555,
+ .child = ctl_isa_vars,
+ }, {0}
+};
+
+static ctl_table ctl_bus[2] = {
+ {
+ .ctl_name = CTL_BUS,
+ .procname = "bus",
+ .mode = 0555,
+ .child = ctl_isa,
+ }, {0}
+};
+
+void __init
+register_isa_ports(unsigned int membase, unsigned int portbase, unsigned int portshift)
+{
+ isa_membase = membase;
+ isa_portbase = portbase;
+ isa_portshift = portshift;
+ isa_sysctl_header = register_sysctl_table(ctl_bus);
+}
diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S
new file mode 100644
index 0000000..b63b528
--- /dev/null
+++ b/arch/arm/kernel/iwmmxt.S
@@ -0,0 +1,317 @@
+/*
+ * linux/arch/arm/kernel/iwmmxt.S
+ *
+ * XScale iWMMXt (Concan) context switching and handling
+ *
+ * Initial code:
+ * Copyright (c) 2003, Intel Corporation
+ *
+ * Full lazy switching support, optimizations and more, by Nicolas Pitre
+* Copyright (c) 2003-2004, MontaVista Software, 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/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+
+#define MMX_WR0 (0x00)
+#define MMX_WR1 (0x08)
+#define MMX_WR2 (0x10)
+#define MMX_WR3 (0x18)
+#define MMX_WR4 (0x20)
+#define MMX_WR5 (0x28)
+#define MMX_WR6 (0x30)
+#define MMX_WR7 (0x38)
+#define MMX_WR8 (0x40)
+#define MMX_WR9 (0x48)
+#define MMX_WR10 (0x50)
+#define MMX_WR11 (0x58)
+#define MMX_WR12 (0x60)
+#define MMX_WR13 (0x68)
+#define MMX_WR14 (0x70)
+#define MMX_WR15 (0x78)
+#define MMX_WCSSF (0x80)
+#define MMX_WCASF (0x84)
+#define MMX_WCGR0 (0x88)
+#define MMX_WCGR1 (0x8C)
+#define MMX_WCGR2 (0x90)
+#define MMX_WCGR3 (0x94)
+
+#define MMX_SIZE (0x98)
+
+ .text
+
+/*
+ * Lazy switching of Concan coprocessor context
+ *
+ * r10 = struct thread_info pointer
+ * r9 = ret_from_exception
+ * lr = undefined instr exit
+ *
+ * called from prefetch exception handler with interrupts disabled
+ */
+
+ENTRY(iwmmxt_task_enable)
+
+ mrc p15, 0, r2, c15, c1, 0
+ tst r2, #0x3 @ CP0 and CP1 accessible?
+ movne pc, lr @ if so no business here
+ orr r2, r2, #0x3 @ enable access to CP0 and CP1
+ mcr p15, 0, r2, c15, c1, 0
+
+ ldr r3, =concan_owner
+ add r0, r10, #TI_IWMMXT_STATE @ get task Concan save area
+ ldr r2, [sp, #60] @ current task pc value
+ ldr r1, [r3] @ get current Concan owner
+ str r0, [r3] @ this task now owns Concan regs
+ sub r2, r2, #4 @ adjust pc back
+ str r2, [sp, #60]
+
+ mrc p15, 0, r2, c2, c0, 0
+ mov r2, r2 @ cpwait
+
+ teq r1, #0 @ test for last ownership
+ mov lr, r9 @ normal exit from exception
+ beq concan_load @ no owner, skip save
+
+concan_save:
+
+ tmrc r2, wCon
+
+ @ CUP? wCx
+ tst r2, #0x1
+ beq 1f
+
+concan_dump:
+
+ wstrw wCSSF, [r1, #MMX_WCSSF]
+ wstrw wCASF, [r1, #MMX_WCASF]
+ wstrw wCGR0, [r1, #MMX_WCGR0]
+ wstrw wCGR1, [r1, #MMX_WCGR1]
+ wstrw wCGR2, [r1, #MMX_WCGR2]
+ wstrw wCGR3, [r1, #MMX_WCGR3]
+
+1: @ MUP? wRn
+ tst r2, #0x2
+ beq 2f
+
+ wstrd wR0, [r1, #MMX_WR0]
+ wstrd wR1, [r1, #MMX_WR1]
+ wstrd wR2, [r1, #MMX_WR2]
+ wstrd wR3, [r1, #MMX_WR3]
+ wstrd wR4, [r1, #MMX_WR4]
+ wstrd wR5, [r1, #MMX_WR5]
+ wstrd wR6, [r1, #MMX_WR6]
+ wstrd wR7, [r1, #MMX_WR7]
+ wstrd wR8, [r1, #MMX_WR8]
+ wstrd wR9, [r1, #MMX_WR9]
+ wstrd wR10, [r1, #MMX_WR10]
+ wstrd wR11, [r1, #MMX_WR11]
+ wstrd wR12, [r1, #MMX_WR12]
+ wstrd wR13, [r1, #MMX_WR13]
+ wstrd wR14, [r1, #MMX_WR14]
+ wstrd wR15, [r1, #MMX_WR15]
+
+2: teq r0, #0 @ anything to load?
+ moveq pc, lr
+
+concan_load:
+
+ @ Load wRn
+ wldrd wR0, [r0, #MMX_WR0]
+ wldrd wR1, [r0, #MMX_WR1]
+ wldrd wR2, [r0, #MMX_WR2]
+ wldrd wR3, [r0, #MMX_WR3]
+ wldrd wR4, [r0, #MMX_WR4]
+ wldrd wR5, [r0, #MMX_WR5]
+ wldrd wR6, [r0, #MMX_WR6]
+ wldrd wR7, [r0, #MMX_WR7]
+ wldrd wR8, [r0, #MMX_WR8]
+ wldrd wR9, [r0, #MMX_WR9]
+ wldrd wR10, [r0, #MMX_WR10]
+ wldrd wR11, [r0, #MMX_WR11]
+ wldrd wR12, [r0, #MMX_WR12]
+ wldrd wR13, [r0, #MMX_WR13]
+ wldrd wR14, [r0, #MMX_WR14]
+ wldrd wR15, [r0, #MMX_WR15]
+
+ @ Load wCx
+ wldrw wCSSF, [r0, #MMX_WCSSF]
+ wldrw wCASF, [r0, #MMX_WCASF]
+ wldrw wCGR0, [r0, #MMX_WCGR0]
+ wldrw wCGR1, [r0, #MMX_WCGR1]
+ wldrw wCGR2, [r0, #MMX_WCGR2]
+ wldrw wCGR3, [r0, #MMX_WCGR3]
+
+ @ clear CUP/MUP (only if r1 != 0)
+ teq r1, #0
+ mov r2, #0
+ moveq pc, lr
+ tmcr wCon, r2
+ mov pc, lr
+
+/*
+ * Back up Concan regs to save area and disable access to them
+ * (mainly for gdb or sleep mode usage)
+ *
+ * r0 = struct thread_info pointer of target task or NULL for any
+ */
+
+ENTRY(iwmmxt_task_disable)
+
+ stmfd sp!, {r4, lr}
+
+ mrs ip, cpsr
+ orr r2, ip, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, r2
+
+ ldr r3, =concan_owner
+ add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
+ ldr r1, [r3] @ get current Concan owner
+ teq r1, #0 @ any current owner?
+ beq 1f @ no: quit
+ teq r0, #0 @ any owner?
+ teqne r1, r2 @ or specified one?
+ bne 1f @ no: quit
+
+ mrc p15, 0, r4, c15, c1, 0
+ orr r4, r4, #0x3 @ enable access to CP0 and CP1
+ mcr p15, 0, r4, c15, c1, 0
+ mov r0, #0 @ nothing to load
+ str r0, [r3] @ no more current owner
+ mrc p15, 0, r2, c2, c0, 0
+ mov r2, r2 @ cpwait
+ bl concan_save
+
+ bic r4, r4, #0x3 @ disable access to CP0 and CP1
+ mcr p15, 0, r4, c15, c1, 0
+ mrc p15, 0, r2, c2, c0, 0
+ mov r2, r2 @ cpwait
+
+1: msr cpsr_c, ip @ restore interrupt mode
+ ldmfd sp!, {r4, pc}
+
+/*
+ * Copy Concan state to given memory address
+ *
+ * r0 = struct thread_info pointer of target task
+ * r1 = memory address where to store Concan state
+ *
+ * this is called mainly in the creation of signal stack frames
+ */
+
+ENTRY(iwmmxt_task_copy)
+
+ mrs ip, cpsr
+ orr r2, ip, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, r2
+
+ ldr r3, =concan_owner
+ add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
+ ldr r3, [r3] @ get current Concan owner
+ teq r2, r3 @ does this task own it...
+ beq 1f
+
+ @ current Concan values are in the task save area
+ msr cpsr_c, ip @ restore interrupt mode
+ mov r0, r1
+ mov r1, r2
+ mov r2, #MMX_SIZE
+ b memcpy
+
+1: @ this task owns Concan regs -- grab a copy from there
+ mov r0, #0 @ nothing to load
+ mov r2, #3 @ save all regs
+ mov r3, lr @ preserve return address
+ bl concan_dump
+ msr cpsr_c, ip @ restore interrupt mode
+ mov pc, r3
+
+/*
+ * Restore Concan state from given memory address
+ *
+ * r0 = struct thread_info pointer of target task
+ * r1 = memory address where to get Concan state from
+ *
+ * this is used to restore Concan state when unwinding a signal stack frame
+ */
+
+ENTRY(iwmmxt_task_restore)
+
+ mrs ip, cpsr
+ orr r2, ip, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, r2
+
+ ldr r3, =concan_owner
+ add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
+ ldr r3, [r3] @ get current Concan owner
+ bic r2, r2, #0x7 @ 64-bit alignment
+ teq r2, r3 @ does this task own it...
+ beq 1f
+
+ @ this task doesn't own Concan regs -- use its save area
+ msr cpsr_c, ip @ restore interrupt mode
+ mov r0, r2
+ mov r2, #MMX_SIZE
+ b memcpy
+
+1: @ this task owns Concan regs -- load them directly
+ mov r0, r1
+ mov r1, #0 @ don't clear CUP/MUP
+ mov r3, lr @ preserve return address
+ bl concan_load
+ msr cpsr_c, ip @ restore interrupt mode
+ mov pc, r3
+
+/*
+ * Concan handling on task switch
+ *
+ * r0 = next thread_info pointer
+ *
+ * Called only from the iwmmxt notifier with task preemption disabled.
+ */
+ENTRY(iwmmxt_task_switch)
+
+ mrc p15, 0, r1, c15, c1, 0
+ tst r1, #0x3 @ CP0 and CP1 accessible?
+ bne 1f @ yes: block them for next task
+
+ ldr r2, =concan_owner
+ add r3, r0, #TI_IWMMXT_STATE @ get next task Concan save area
+ ldr r2, [r2] @ get current Concan owner
+ teq r2, r3 @ next task owns it?
+ movne pc, lr @ no: leave Concan disabled
+
+1: eor r1, r1, #3 @ flip Concan access
+ mcr p15, 0, r1, c15, c1, 0
+
+ mrc p15, 0, r1, c2, c0, 0
+ sub pc, lr, r1, lsr #32 @ cpwait and return
+
+/*
+ * Remove Concan ownership of given task
+ *
+ * r0 = struct thread_info pointer
+ */
+ENTRY(iwmmxt_task_release)
+
+ mrs r2, cpsr
+ orr ip, r2, #PSR_I_BIT @ disable interrupts
+ msr cpsr_c, ip
+ ldr r3, =concan_owner
+ add r0, r0, #TI_IWMMXT_STATE @ get task Concan save area
+ ldr r1, [r3] @ get current Concan owner
+ eors r0, r0, r1 @ if equal...
+ streq r0, [r3] @ then clear ownership
+ msr cpsr_c, r2 @ restore interrupts
+ mov pc, lr
+
+ .data
+concan_owner:
+ .word 0
+
diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c
new file mode 100644
index 0000000..ba8ccfe
--- /dev/null
+++ b/arch/arm/kernel/kgdb.c
@@ -0,0 +1,199 @@
+/*
+ * arch/arm/kernel/kgdb.c
+ *
+ * ARM KGDB support
+ *
+ * Copyright (c) 2002-2004 MontaVista Software, Inc
+ * Copyright (c) 2008 Wind River Systems, Inc.
+ *
+ * Authors: George Davis <davis_g@mvista.com>
+ * Deepak Saxena <dsaxena@plexity.net>
+ */
+#include <linux/kgdb.h>
+#include <asm/traps.h>
+
+/* Make a local copy of the registers passed into the handler (bletch) */
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ int regno;
+
+ /* Initialize all to zero. */
+ for (regno = 0; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ gdb_regs[_R0] = kernel_regs->ARM_r0;
+ gdb_regs[_R1] = kernel_regs->ARM_r1;
+ gdb_regs[_R2] = kernel_regs->ARM_r2;
+ gdb_regs[_R3] = kernel_regs->ARM_r3;
+ gdb_regs[_R4] = kernel_regs->ARM_r4;
+ gdb_regs[_R5] = kernel_regs->ARM_r5;
+ gdb_regs[_R6] = kernel_regs->ARM_r6;
+ gdb_regs[_R7] = kernel_regs->ARM_r7;
+ gdb_regs[_R8] = kernel_regs->ARM_r8;
+ gdb_regs[_R9] = kernel_regs->ARM_r9;
+ gdb_regs[_R10] = kernel_regs->ARM_r10;
+ gdb_regs[_FP] = kernel_regs->ARM_fp;
+ gdb_regs[_IP] = kernel_regs->ARM_ip;
+ gdb_regs[_SPT] = kernel_regs->ARM_sp;
+ gdb_regs[_LR] = kernel_regs->ARM_lr;
+ gdb_regs[_PC] = kernel_regs->ARM_pc;
+ gdb_regs[_CPSR] = kernel_regs->ARM_cpsr;
+}
+
+/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ kernel_regs->ARM_r0 = gdb_regs[_R0];
+ kernel_regs->ARM_r1 = gdb_regs[_R1];
+ kernel_regs->ARM_r2 = gdb_regs[_R2];
+ kernel_regs->ARM_r3 = gdb_regs[_R3];
+ kernel_regs->ARM_r4 = gdb_regs[_R4];
+ kernel_regs->ARM_r5 = gdb_regs[_R5];
+ kernel_regs->ARM_r6 = gdb_regs[_R6];
+ kernel_regs->ARM_r7 = gdb_regs[_R7];
+ kernel_regs->ARM_r8 = gdb_regs[_R8];
+ kernel_regs->ARM_r9 = gdb_regs[_R9];
+ kernel_regs->ARM_r10 = gdb_regs[_R10];
+ kernel_regs->ARM_fp = gdb_regs[_FP];
+ kernel_regs->ARM_ip = gdb_regs[_IP];
+ kernel_regs->ARM_sp = gdb_regs[_SPT];
+ kernel_regs->ARM_lr = gdb_regs[_LR];
+ kernel_regs->ARM_pc = gdb_regs[_PC];
+ kernel_regs->ARM_cpsr = gdb_regs[_CPSR];
+}
+
+void
+sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
+{
+ struct pt_regs *thread_regs;
+ int regno;
+
+ /* Just making sure... */
+ if (task == NULL)
+ return;
+
+ /* Initialize to zero */
+ for (regno = 0; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ /* Otherwise, we have only some registers from switch_to() */
+ thread_regs = task_pt_regs(task);
+ gdb_regs[_R0] = thread_regs->ARM_r0;
+ gdb_regs[_R1] = thread_regs->ARM_r1;
+ gdb_regs[_R2] = thread_regs->ARM_r2;
+ gdb_regs[_R3] = thread_regs->ARM_r3;
+ gdb_regs[_R4] = thread_regs->ARM_r4;
+ gdb_regs[_R5] = thread_regs->ARM_r5;
+ gdb_regs[_R6] = thread_regs->ARM_r6;
+ gdb_regs[_R7] = thread_regs->ARM_r7;
+ gdb_regs[_R8] = thread_regs->ARM_r8;
+ gdb_regs[_R9] = thread_regs->ARM_r9;
+ gdb_regs[_R10] = thread_regs->ARM_r10;
+ gdb_regs[_FP] = thread_regs->ARM_fp;
+ gdb_regs[_IP] = thread_regs->ARM_ip;
+ gdb_regs[_SPT] = thread_regs->ARM_sp;
+ gdb_regs[_LR] = thread_regs->ARM_lr;
+ gdb_regs[_PC] = thread_regs->ARM_pc;
+ gdb_regs[_CPSR] = thread_regs->ARM_cpsr;
+}
+
+static int compiled_break;
+
+int kgdb_arch_handle_exception(int exception_vector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ unsigned long addr;
+ char *ptr;
+
+ switch (remcom_in_buffer[0]) {
+ case 'D':
+ case 'k':
+ case 'c':
+ /*
+ * Try to read optional parameter, pc unchanged if no parm.
+ * If this was a compiled breakpoint, we need to move
+ * to the next instruction or we will just breakpoint
+ * over and over again.
+ */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ linux_regs->ARM_pc = addr;
+ else if (compiled_break == 1)
+ linux_regs->ARM_pc += 4;
+
+ compiled_break = 0;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr)
+{
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+
+ return 0;
+}
+
+static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr)
+{
+ compiled_break = 1;
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+
+ return 0;
+}
+
+static struct undef_hook kgdb_brkpt_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KGDB_BREAKINST,
+ .fn = kgdb_brk_fn
+};
+
+static struct undef_hook kgdb_compiled_brkpt_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KGDB_COMPILED_BREAK,
+ .fn = kgdb_compiled_brk_fn
+};
+
+/**
+ * kgdb_arch_init - Perform any architecture specific initalization.
+ *
+ * This function will handle the initalization of any architecture
+ * specific callbacks.
+ */
+int kgdb_arch_init(void)
+{
+ register_undef_hook(&kgdb_brkpt_hook);
+ register_undef_hook(&kgdb_compiled_brkpt_hook);
+
+ return 0;
+}
+
+/**
+ * kgdb_arch_exit - Perform any architecture specific uninitalization.
+ *
+ * This function will handle the uninitalization of any architecture
+ * specific callbacks, for dynamic registration and unregistration.
+ */
+void kgdb_arch_exit(void)
+{
+ unregister_undef_hook(&kgdb_brkpt_hook);
+ unregister_undef_hook(&kgdb_compiled_brkpt_hook);
+}
+
+/*
+ * Register our undef instruction hooks with ARM undef core.
+ * We regsiter a hook specifically looking for the KGB break inst
+ * and we handle the normal undef case within the do_undefinstr
+ * handler.
+ */
+struct kgdb_arch arch_kgdb_ops = {
+#ifndef __ARMEB__
+ .gdb_bpt_instr = {0xfe, 0xde, 0xff, 0xe7}
+#else /* ! __ARMEB__ */
+ .gdb_bpt_instr = {0xe7, 0xff, 0xde, 0xfe}
+#endif
+};
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c
new file mode 100644
index 0000000..da1f949
--- /dev/null
+++ b/arch/arm/kernel/kprobes-decode.c
@@ -0,0 +1,1529 @@
+/*
+ * 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.
+ * If it is a conditional instruction, the handler
+ * will use insn[0] to copy its condition code to
+ * set r0 to 1 and insn[1] to "mov pc, lr" to return.
+ *
+ * *) Otherwise, a modified form of the instruction is
+ * directly executed. Its handler calls the
+ * 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 PSR_fs (PSR_f|PSR_s)
+
+#define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */
+#define SET_R0_TRUE_INSTRUCTION 0xe3a00001 /* mov r0, #1 */
+
+#define truecc_insn(insn) (((insn) & 0xf0000000) | \
+ (SET_R0_TRUE_INSTRUCTION & 0x0fffffff))
+
+typedef long (insn_0arg_fn_t)(void);
+typedef long (insn_1arg_fn_t)(long);
+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)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ long iaddr = (long)p->addr;
+ int disp = branch_displacement(insn);
+
+ if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+ return;
+
+ if (insn & (1 << 24))
+ regs->ARM_lr = iaddr + 4;
+
+ 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)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rm = insn & 0xf;
+ long rmv = regs->uregs[rm];
+
+ if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+ return;
+
+ if (insn & (1 << 5))
+ regs->ARM_lr = (long)p->addr + 4;
+
+ regs->ARM_pc = rmv & ~0x1;
+ regs->ARM_cpsr &= ~PSR_T_BIT;
+ if (rmv & 0x1)
+ regs->ARM_cpsr |= PSR_T_BIT;
+}
+
+static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ int lbit = insn & (1 << 20);
+ 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;
+
+ if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+ return;
+
+ 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)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+
+ if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
+ return;
+
+ regs->ARM_pc = (long)p->addr + str_pc_offset;
+ simulate_ldm1stm1(p, regs);
+ regs->ARM_pc = (long)p->addr + 4;
+}
+
+static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
+{
+ regs->uregs[12] = regs->uregs[13];
+}
+
+static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = regs->uregs[rn];
+
+ /* Save Rn in case of writeback. */
+ regs->uregs[rn] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
+}
+
+static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf; /* rm may be invalid, don't care. */
+
+ /* 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" (regs->uregs[rn]),
+ [rd0] "=m" (regs->uregs[rd]),
+ [rd1] "=m" (regs->uregs[rd+1])
+ : [rm] "m" (regs->uregs[rm]),
+ [cpsr] "r" (regs->ARM_cpsr),
+ [i_fn] "r" (i_fn)
+ : "r0", "r1", "r2", "r3", "lr", "cc"
+ );
+}
+
+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;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+
+ regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
+ regs->uregs[rd+1],
+ regs->ARM_cpsr, i_fn);
+}
+
+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;
+ union reg_pair fnr;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ int rm = insn & 0xf;
+ long rdv;
+ long rnv = regs->uregs[rn];
+ long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+ long cpsr = regs->ARM_cpsr;
+
+ fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn);
+ 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. */
+
+ /* Save Rn in case of writeback. */
+ regs->uregs[rn] =
+ insnslot_3arg_rflags(rnv, rdv, rmv, regs->ARM_cpsr, i_fn);
+}
+
+static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ union reg_pair fnr;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+
+ fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn);
+ regs->uregs[rn] = fnr.r0;
+ regs->uregs[rd] = fnr.r1;
+}
+
+static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = regs->uregs[rn];
+ long rdv = regs->uregs[rd];
+
+ insnslot_2arg_rflags(rnv, rdv, regs->ARM_cpsr, i_fn);
+}
+
+static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ 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_rd12(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+
+ regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
+}
+
+static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int ird = (insn >> 12) & 0xf;
+
+ insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn);
+}
+
+static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = regs->uregs[rn];
+
+ insnslot_1arg_rflags(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_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 enum kprobe_insn __kprobes
+prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ int ibit = (insn & (1 << 26)) ? 25 : 22;
+
+ insn &= 0xfff00fff;
+ insn |= 0x00001000; /* Rn = r0, Rd = r1 */
+ if (insn & (1 << ibit)) {
+ 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_rd12rm0(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ 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_rd12(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ insn &= 0xffff0fff; /* Rd = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd12;
+ return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ /* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx */
+ /* RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx */
+ /* SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx */
+ if ((insn & 0xfff30020) == 0xf1020000 ||
+ (insn & 0xfe500f00) == 0xf8100a00 ||
+ (insn & 0xfe5f0f00) == 0xf84d0500)
+ return INSN_REJECTED;
+
+ /* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */
+ if ((insn & 0xfd700000) == 0xf4500000) {
+ insn &= 0xfff0ffff; /* Rn = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rn16;
+ return INSN_GOOD;
+ }
+
+ /* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */
+ if ((insn & 0xfe000000) == 0xfa000000) {
+ asi->insn_handler = simulate_blx1;
+ return INSN_GOOD_NO_SLOT;
+ }
+
+ /* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
+ /* CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ if ((insn & 0xffff00f0) == 0xf1010000 ||
+ (insn & 0xff000010) == 0xfe000000) {
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_none;
+ return INSN_GOOD;
+ }
+
+ /* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */
+ /* MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */
+ if ((insn & 0xffe00000) == 0xfc400000) {
+ insn &= 0xfff00fff; /* Rn = r0 */
+ insn |= 0x00001000; /* Rd = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler =
+ (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
+ return INSN_GOOD;
+ }
+
+ /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ if ((insn & 0xfe000000) == 0xfc000000) {
+ insn &= 0xfff0ffff; /* Rn = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_ldcstc;
+ return INSN_GOOD;
+ }
+
+ /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
+ insn &= 0xffff0fff; /* Rd = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
+ return INSN_GOOD;
+}
+
+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) {
+
+ /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
+ /* MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
+ if ((insn & 0x0ff000f0) == 0x01200020 ||
+ (insn & 0x0fb000f0) == 0x01200000)
+ return INSN_REJECTED;
+
+ /* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx */
+ if ((insn & 0x0fb00010) == 0x01000000)
+ return prep_emulate_rd12(insn, asi);
+
+ /* 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 0x00 xxxx : Q */
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ }
+
+ /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */
+ else if ((insn & 0x0f900090) == 0x01000010) {
+
+ /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+ if ((insn & 0xfff000f0) == 0xe1200070)
+ return INSN_REJECTED;
+
+ /* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
+ /* BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
+ if ((insn & 0x0ff000d0) == 0x01200010) {
+ asi->insn[0] = truecc_insn(insn);
+ asi->insn_handler = simulate_blx2bx;
+ return INSN_GOOD;
+ }
+
+ /* 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 */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ }
+
+ /* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */
+ else if ((insn & 0x0f000090) == 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 : */
+ /* 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 & 0x0fe000f0) == 0x00000090) {
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ } else if ((insn & 0x0fe000f0) == 0x00200090) {
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ } else {
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+ }
+ }
+
+ /* 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 */
+ /* LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx */
+ /* STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx */
+ /* STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx */
+ /* LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx */
+ /* STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx */
+ /* LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx */
+ /* LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx */
+ if ((insn & 0x0fb000f0) == 0x01000090) {
+ /* SWP/SWPB */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ } else if ((insn & 0x0e1000d0) == 0x00000d0) {
+ /* STRD/LDRD */
+ insn &= 0xfff00fff;
+ insn |= 0x00002000; /* Rn = r0, Rd = r2 */
+ if (insn & (1 << 22)) {
+ /* I bit */
+ insn &= ~0xf;
+ insn |= 1; /* Rm = r1 */
+ }
+ asi->insn[0] = insn;
+ asi->insn_handler =
+ (insn & (1 << 5)) ? emulate_strd : emulate_ldrd;
+ return INSN_GOOD;
+ }
+
+ 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;
+ 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)
+{
+ /*
+ * MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx
+ * Undef : cccc 0011 0x00 xxxx xxxx xxxx xxxx xxxx
+ * ALU op with S bit and Rd == 15 :
+ * cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx
+ */
+ if ((insn & 0x0f900000) == 0x03200000 || /* MSR & Undef */
+ (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, Rd = r0 */
+ asi->insn[0] = insn;
+ 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) {
+ 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) {
+ 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 */
+ /* REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
+ if ((insn & 0x0ff00070) == 0x06b00030 ||
+ (insn & 0x0ff000f0) == 0x06f000b0)
+ return prep_emulate_rd12rm0(insn, asi);
+
+ /* 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 */
+ /* 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 : */
+ /* 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 :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 */
+ /* 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 : */
+ /* 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 : */
+ /* PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx : */
+ /* PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx : */
+ /* SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTB : cccc 0110 1010 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 : */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+}
+
+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;
+
+ /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
+ /* USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
+ if ((insn & 0x0ff000f0) == 0x07800010)
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ /* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
+ /* SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
+ if ((insn & 0x0ff00090) == 0x07400010)
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+
+ /* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q */
+ /* SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q */
+ /* SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx : */
+ /* SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : */
+ if ((insn & 0x0ff00090) == 0x07000010 ||
+ (insn & 0x0ff000d0) == 0x07500010 ||
+ (insn & 0x0ff000d0) == 0x075000d0)
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ /* SMUSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx : */
+ /* SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q */
+ /* SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : */
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+}
+
+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 */
+ 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[0] = truecc_insn(insn);
+ asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */
+ simulate_stm1_pc : simulate_ldm1stm1;
+ return INSN_GOOD;
+}
+
+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[0] = truecc_insn(insn);
+ asi->insn_handler = simulate_bbl;
+ return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+space_cccc_1100_010x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ /* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */
+ /* MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */
+ insn &= 0xfff00fff;
+ insn |= 0x00001000; /* Rn = r0, Rd = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
+ return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+space_cccc_110x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ insn &= 0xfff0ffff; /* Rn = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_ldcstc;
+ return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+space_cccc_111x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+ /* SWI : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
+ if ((insn & 0xfff000f0) == 0xe1200070 ||
+ (insn & 0x0f000000) == 0x0f000000)
+ return INSN_REJECTED;
+
+ /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ if ((insn & 0x0f000010) == 0x0e000000) {
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_none;
+ return INSN_GOOD;
+ }
+
+ /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
+ insn &= 0xffff0fff; /* Rd = r0 */
+ asi->insn[0] = insn;
+ asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
+ return INSN_GOOD;
+}
+
+/* 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[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);
+
+ } else if ((insn & 0x0fe00000) == 0x0c400000) {
+
+ return space_cccc_1100_010x(insn, asi);
+
+ } else if ((insn & 0x0e000000) == 0x0c400000) {
+
+ return space_cccc_110x(insn, asi);
+
+ }
+
+ return space_cccc_111x(insn, asi);
+}
+
+void __init arm_kprobe_decode_init(void)
+{
+ find_str_pc_offset();
+}
+
+
+/*
+ * All ARM instructions listed below.
+ *
+ * Instructions and their general purpose registers are given.
+ * If a particular register may not use R15, it is prefixed with a "!".
+ * If marked with a "*" means the value returned by reading R15
+ * is implementation defined.
+ *
+ * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ
+ * TST: Rd, Rn, Rm, !Rs
+ * BX: Rm
+ * BLX(2): !Rm
+ * BX: Rm (R15 legal, but discouraged)
+ * BXJ: !Rm,
+ * CLZ: !Rd, !Rm
+ * CPY: Rd, Rm
+ * LDC/2,STC/2 immediate offset & unindex: Rn
+ * LDC/2,STC/2 immediate pre/post-indexed: !Rn
+ * LDM(1/3): !Rn, register_list
+ * LDM(2): !Rn, !register_list
+ * LDR,STR,PLD immediate offset: Rd, Rn
+ * LDR,STR,PLD register offset: Rd, Rn, !Rm
+ * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm
+ * LDR,STR immediate pre/post-indexed: Rd, !Rn
+ * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm
+ * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm
+ * LDRB,STRB immediate offset: !Rd, Rn
+ * LDRB,STRB register offset: !Rd, Rn, !Rm
+ * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm
+ * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn
+ * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn
+ * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn
+ * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm
+ * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn
+ * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm
+ * LDREX: !Rd, !Rn
+ * MCR/2: !Rd
+ * MCRR/2,MRRC/2: !Rd, !Rn
+ * MLA: !Rd, !Rn, !Rm, !Rs
+ * MOV: Rd
+ * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register)
+ * MRS,MSR: !Rd
+ * MUL: !Rd, !Rm, !Rs
+ * PKH{BT,TB}: !Rd, !Rn, !Rm
+ * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn
+ * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn
+ * REV/16/SH: !Rd, !Rm
+ * RFE: !Rn
+ * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm
+ * SEL: !Rd, !Rn, !Rm
+ * SMLA<x><y>,SMLA{D,W<y>},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs
+ * SMLAL<x><y>,SMLA{D,LD},SMLSLD,SMMULL,SMULW<y>: !RdHi, !RdLo, !Rm, !Rs
+ * SMMUL,SMUAD,SMUL<x><y>,SMUSD: !Rd, !Rm, !Rs
+ * SSAT/16: !Rd, !Rm
+ * STM(1/2): !Rn, register_list* (R15 in reg list not recommended)
+ * STRT immediate pre/post-indexed: Rd*, !Rn
+ * STRT register pre/post-indexed: Rd*, !Rn, !Rm
+ * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm
+ * STREX: !Rd, !Rn, !Rm
+ * SWP/B: !Rd, !Rn, !Rm
+ * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm
+ * {S,U}XT{B,B16,H}: !Rd, !Rm
+ * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs
+ * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs
+ *
+ * May transfer control by writing R15 (possible mode changes or alternate
+ * mode accesses marked by "*"):
+ * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY,
+ * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI*
+ *
+ * Instructions that do not take general registers, nor transfer control:
+ * CDP/2, SETEND, SRS*
+ */
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
new file mode 100644
index 0000000..3f9abe0
--- /dev/null
+++ b/arch/arm/kernel/kprobes.c
@@ -0,0 +1,453 @@
+/*
+ * arch/arm/kernel/kprobes.c
+ *
+ * Kprobes on ARM
+ *
+ * Abhishek Sagar <sagar.abhishek@gmail.com>
+ * Copyright (C) 2006, 2007 Motorola Inc.
+ *
+ * Nicolas Pitre <nico@marvell.com>
+ * Copyright (C) 2007 Marvell Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include <asm/traps.h>
+#include <asm/cacheflush.h>
+
+#define MIN_STACK_SIZE(addr) \
+ min((unsigned long)MAX_STACK_SIZE, \
+ (unsigned long)current_thread_info() + THREAD_START_SP - (addr))
+
+#define flush_insns(addr, cnt) \
+ flush_icache_range((unsigned long)(addr), \
+ (unsigned long)(addr) + \
+ sizeof(kprobe_opcode_t) * (cnt))
+
+/* Used as a marker in ARM_pc to note when we're in a jprobe. */
+#define JPROBE_MAGIC_ADDR 0xffffffff
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ kprobe_opcode_t insn;
+ kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
+ unsigned long addr = (unsigned long)p->addr;
+ int is;
+
+ if (addr & 0x3 || in_exception_text(addr))
+ return -EINVAL;
+
+ insn = *p->addr;
+ p->opcode = insn;
+ p->ainsn.insn = tmp_insn;
+
+ switch (arm_kprobe_decode_insn(insn, &p->ainsn)) {
+ case INSN_REJECTED: /* not supported */
+ return -EINVAL;
+
+ case INSN_GOOD: /* instruction uses slot */
+ p->ainsn.insn = get_insn_slot();
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+ for (is = 0; is < MAX_INSN_SIZE; ++is)
+ p->ainsn.insn[is] = tmp_insn[is];
+ flush_insns(p->ainsn.insn, MAX_INSN_SIZE);
+ break;
+
+ case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */
+ p->ainsn.insn = NULL;
+ break;
+ }
+
+ return 0;
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ *p->addr = KPROBE_BREAKPOINT_INSTRUCTION;
+ flush_insns(p->addr, 1);
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ *p->addr = p->opcode;
+ flush_insns(p->addr, 1);
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+ if (p->ainsn.insn) {
+ mutex_lock(&kprobe_mutex);
+ free_insn_slot(p->ainsn.insn, 0);
+ mutex_unlock(&kprobe_mutex);
+ p->ainsn.insn = NULL;
+ }
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+ __get_cpu_var(current_kprobe) = p;
+}
+
+static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb)
+{
+ regs->ARM_pc += 4;
+ p->ainsn.insn_handler(p, regs);
+}
+
+/*
+ * Called with IRQs disabled. IRQs must remain disabled from that point
+ * all the way until processing this kprobe is complete. The current
+ * kprobes implementation cannot process more than one nested level of
+ * kprobe, and that level is reserved for user kprobe handlers, so we can't
+ * risk encountering a new kprobe in an interrupt handler.
+ */
+void __kprobes kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p, *cur;
+ struct kprobe_ctlblk *kcb;
+ kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc;
+
+ kcb = get_kprobe_ctlblk();
+ cur = kprobe_running();
+ p = get_kprobe(addr);
+
+ if (p) {
+ if (cur) {
+ /* Kprobe is pending, so we're recursing. */
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /* A pre- or post-handler probe got us here. */
+ kprobes_inc_nmissed_count(p);
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ singlestep(p, regs, kcb);
+ restore_previous_kprobe(kcb);
+ break;
+ default:
+ /* impossible cases */
+ BUG();
+ }
+ } else {
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ /*
+ * If we have no pre-handler or it returned 0, we
+ * continue with normal processing. If we have a
+ * pre-handler and it returned non-zero, it prepped
+ * for calling the break_handler below on re-entry,
+ * so get out doing nothing more here.
+ */
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ singlestep(p, regs, kcb);
+ if (p->post_handler) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ p->post_handler(p, regs, 0);
+ }
+ reset_current_kprobe();
+ }
+ }
+ } else if (cur) {
+ /* We probably hit a jprobe. Call its break handler. */
+ if (cur->break_handler && cur->break_handler(cur, regs)) {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ singlestep(cur, regs, kcb);
+ if (cur->post_handler) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ cur->post_handler(cur, regs, 0);
+ }
+ }
+ reset_current_kprobe();
+ } else {
+ /*
+ * The probe was removed and a race is in progress.
+ * There is nothing we can do about it. Let's restart
+ * the instruction. By the time we can restart, the
+ * real instruction will be there.
+ */
+ }
+}
+
+static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ kprobe_handler(regs);
+ local_irq_restore(flags);
+ return 0;
+}
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the PC to point back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ regs->ARM_pc = (long)cur->addr;
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ } else {
+ reset_current_kprobe();
+ }
+ break;
+
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * We increment the nmissed count for accounting,
+ * we can also use npre/npostfault count for accounting
+ * these specific fault cases.
+ */
+ kprobes_inc_nmissed_count(cur);
+
+ /*
+ * We come here because instructions in the pre/post
+ * handler caused the page_fault, this could happen
+ * if handler tries to access user space by
+ * copy_from_user(), get_user() etc. Let the
+ * user-specified handler try to fix it.
+ */
+ if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
+ return 1;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ /*
+ * notify_die() is currently never called on ARM,
+ * so this callback is currently empty.
+ */
+ return NOTIFY_DONE;
+}
+
+/*
+ * When a retprobed function returns, trampoline_handler() is called,
+ * calling the kretprobe's handler. We construct a struct pt_regs to
+ * give a view of registers r0-r11 to the user return-handler. This is
+ * not a complete pt_regs structure, but that should be plenty sufficient
+ * for kretprobe handlers which should normally be interested in r0 only
+ * anyway.
+ */
+void __naked __kprobes kretprobe_trampoline(void)
+{
+ __asm__ __volatile__ (
+ "stmdb sp!, {r0 - r11} \n\t"
+ "mov r0, sp \n\t"
+ "bl trampoline_handler \n\t"
+ "mov lr, r0 \n\t"
+ "ldmia sp!, {r0 - r11} \n\t"
+ "mov pc, lr \n\t"
+ : : : "memory");
+}
+
+/* Called from kretprobe_trampoline */
+static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri = NULL;
+ struct hlist_head *head, empty_rp;
+ struct hlist_node *node, *tmp;
+ unsigned long flags, orig_ret_address = 0;
+ unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
+
+ INIT_HLIST_HEAD(&empty_rp);
+ kretprobe_hash_lock(current, &head, &flags);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because multiple functions in the call path have
+ * a return probe installed on them, and/or more than one return
+ * probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * kretprobe_trampoline
+ */
+ hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ if (ri->rp && ri->rp->handler) {
+ __get_cpu_var(current_kprobe) = &ri->rp->kp;
+ get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
+ ri->rp->handler(ri, regs);
+ __get_cpu_var(current_kprobe) = NULL;
+ }
+
+ orig_ret_address = (unsigned long)ri->ret_addr;
+ recycle_rp_inst(ri, &empty_rp);
+
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
+ kretprobe_hash_unlock(current, &flags);
+
+ hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+ hlist_del(&ri->hlist);
+ kfree(ri);
+ }
+
+ return (void *)orig_ret_address;
+}
+
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
+
+ /* Replace the return addr with trampoline addr. */
+ regs->ARM_lr = (unsigned long)&kretprobe_trampoline;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ long sp_addr = regs->ARM_sp;
+
+ kcb->jprobe_saved_regs = *regs;
+ memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
+ regs->ARM_pc = (long)jp->entry;
+ regs->ARM_cpsr |= PSR_I_BIT;
+ preempt_disable();
+ return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ __asm__ __volatile__ (
+ /*
+ * Setup an empty pt_regs. Fill SP and PC fields as
+ * they're needed by longjmp_break_handler.
+ */
+ "sub sp, %0, %1 \n\t"
+ "ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t"
+ "str %0, [sp, %2] \n\t"
+ "str r0, [sp, %3] \n\t"
+ "mov r0, sp \n\t"
+ "bl kprobe_handler \n\t"
+
+ /*
+ * Return to the context saved by setjmp_pre_handler
+ * and restored by longjmp_break_handler.
+ */
+ "ldr r0, [sp, %4] \n\t"
+ "msr cpsr_cxsf, r0 \n\t"
+ "ldmia sp, {r0 - pc} \n\t"
+ :
+ : "r" (kcb->jprobe_saved_regs.ARM_sp),
+ "I" (sizeof(struct pt_regs)),
+ "J" (offsetof(struct pt_regs, ARM_sp)),
+ "J" (offsetof(struct pt_regs, ARM_pc)),
+ "J" (offsetof(struct pt_regs, ARM_cpsr))
+ : "memory", "cc");
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ long stack_addr = kcb->jprobe_saved_regs.ARM_sp;
+ long orig_sp = regs->ARM_sp;
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+ if (regs->ARM_pc == JPROBE_MAGIC_ADDR) {
+ if (orig_sp != stack_addr) {
+ struct pt_regs *saved_regs =
+ (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp;
+ printk("current sp %lx does not match saved sp %lx\n",
+ orig_sp, stack_addr);
+ printk("Saved registers for jprobe %p\n", jp);
+ show_regs(saved_regs);
+ printk("Current registers\n");
+ show_regs(regs);
+ BUG();
+ }
+ *regs = kcb->jprobe_saved_regs;
+ memcpy((void *)stack_addr, kcb->jprobes_stack,
+ MIN_STACK_SIZE(stack_addr));
+ preempt_enable_no_resched();
+ return 1;
+ }
+ return 0;
+}
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+ return 0;
+}
+
+static struct undef_hook kprobes_break_hook = {
+ .instr_mask = 0xffffffff,
+ .instr_val = KPROBE_BREAKPOINT_INSTRUCTION,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = SVC_MODE,
+ .fn = kprobe_trap_handler,
+};
+
+int __init arch_init_kprobes()
+{
+ arm_kprobe_decode_init();
+ register_undef_hook(&kprobes_break_hook);
+ return 0;
+}
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c
new file mode 100644
index 0000000..440dc62
--- /dev/null
+++ b/arch/arm/kernel/machine_kexec.c
@@ -0,0 +1,80 @@
+/*
+ * machine_kexec.c - handle transition of Linux booting another kernel
+ */
+
+#include <linux/mm.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/io.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+#include <asm/mach-types.h>
+
+const extern unsigned char relocate_new_kernel[];
+const extern unsigned int relocate_new_kernel_size;
+
+extern void setup_mm_for_reboot(char mode);
+
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_indirection_page;
+extern unsigned long kexec_mach_type;
+extern unsigned long kexec_boot_atags;
+
+/*
+ * Provide a dummy crash_notes definition while crash dump arrives to arm.
+ * This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
+ */
+
+int machine_kexec_prepare(struct kimage *image)
+{
+ return 0;
+}
+
+void machine_kexec_cleanup(struct kimage *image)
+{
+}
+
+void machine_shutdown(void)
+{
+}
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
+void machine_kexec(struct kimage *image)
+{
+ unsigned long page_list;
+ unsigned long reboot_code_buffer_phys;
+ void *reboot_code_buffer;
+
+
+ page_list = image->head & PAGE_MASK;
+
+ /* we need both effective and real address here */
+ reboot_code_buffer_phys =
+ page_to_pfn(image->control_code_page) << PAGE_SHIFT;
+ reboot_code_buffer = page_address(image->control_code_page);
+
+ /* Prepare parameters for reboot_code_buffer*/
+ kexec_start_address = image->start;
+ kexec_indirection_page = page_list;
+ kexec_mach_type = machine_arch_type;
+ kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
+
+ /* copy our kernel relocation code to the control code page */
+ memcpy(reboot_code_buffer,
+ relocate_new_kernel, relocate_new_kernel_size);
+
+
+ flush_icache_range((unsigned long) reboot_code_buffer,
+ (unsigned long) reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE);
+ printk(KERN_INFO "Bye!\n");
+
+ cpu_proc_fin();
+ setup_mm_for_reboot(0); /* mode is not used, so just pass 0*/
+ cpu_reset(reboot_code_buffer_phys);
+}
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
new file mode 100644
index 0000000..b8d965d
--- /dev/null
+++ b/arch/arm/kernel/module.c
@@ -0,0 +1,163 @@
+/*
+ * linux/arch/arm/kernel/module.c
+ *
+ * Copyright (C) 2002 Russell King.
+ * Modified for nommu by Hyok S. Choi
+ *
+ * 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.
+ *
+ * Module allocation method suggested by Andi Kleen.
+ */
+#include <linux/module.h>
+#include <linux/moduleloader.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_XIP_KERNEL
+/*
+ * The XIP kernel text is mapped in the module area for modules and
+ * some other stuff to work without any indirect relocations.
+ * MODULES_VADDR is redefined here and not in asm/memory.h to avoid
+ * recompiling the whole kernel when CONFIG_XIP_KERNEL is turned on/off.
+ */
+extern void _etext;
+#undef MODULES_VADDR
+#define MODULES_VADDR (((unsigned long)&_etext + ~PGDIR_MASK) & PGDIR_MASK)
+#endif
+
+#ifdef CONFIG_MMU
+void *module_alloc(unsigned long size)
+{
+ struct vm_struct *area;
+
+ size = PAGE_ALIGN(size);
+ if (!size)
+ return NULL;
+
+ area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
+ if (!area)
+ return NULL;
+
+ return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);
+}
+#else /* CONFIG_MMU */
+void *module_alloc(unsigned long size)
+{
+ return size == 0 ? NULL : vmalloc(size);
+}
+#endif /* !CONFIG_MMU */
+
+void module_free(struct module *module, void *region)
+{
+ vfree(region);
+}
+
+int module_frob_arch_sections(Elf_Ehdr *hdr,
+ Elf_Shdr *sechdrs,
+ char *secstrings,
+ struct module *mod)
+{
+ return 0;
+}
+
+int
+apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
+ unsigned int relindex, struct module *module)
+{
+ Elf32_Shdr *symsec = sechdrs + symindex;
+ Elf32_Shdr *relsec = sechdrs + relindex;
+ Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
+ Elf32_Rel *rel = (void *)relsec->sh_addr;
+ unsigned int i;
+
+ for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
+ unsigned long loc;
+ Elf32_Sym *sym;
+ s32 offset;
+
+ offset = ELF32_R_SYM(rel->r_info);
+ if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) {
+ printk(KERN_ERR "%s: bad relocation, section %d reloc %d\n",
+ module->name, relindex, i);
+ return -ENOEXEC;
+ }
+
+ sym = ((Elf32_Sym *)symsec->sh_addr) + offset;
+
+ if (rel->r_offset < 0 || rel->r_offset > dstsec->sh_size - sizeof(u32)) {
+ printk(KERN_ERR "%s: out of bounds relocation, "
+ "section %d reloc %d offset %d size %d\n",
+ module->name, relindex, i, rel->r_offset,
+ dstsec->sh_size);
+ return -ENOEXEC;
+ }
+
+ loc = dstsec->sh_addr + rel->r_offset;
+
+ switch (ELF32_R_TYPE(rel->r_info)) {
+ case R_ARM_ABS32:
+ *(u32 *)loc += sym->st_value;
+ break;
+
+ case R_ARM_PC24:
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ offset = (*(u32 *)loc & 0x00ffffff) << 2;
+ if (offset & 0x02000000)
+ offset -= 0x04000000;
+
+ offset += sym->st_value - loc;
+ if (offset & 3 ||
+ offset <= (s32)0xfe000000 ||
+ offset >= (s32)0x02000000) {
+ printk(KERN_ERR
+ "%s: relocation out of range, section "
+ "%d reloc %d sym '%s'\n", module->name,
+ relindex, i, strtab + sym->st_name);
+ return -ENOEXEC;
+ }
+
+ offset >>= 2;
+
+ *(u32 *)loc &= 0xff000000;
+ *(u32 *)loc |= offset & 0x00ffffff;
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown relocation: %u\n",
+ module->name, ELF32_R_TYPE(rel->r_info));
+ return -ENOEXEC;
+ }
+ }
+ return 0;
+}
+
+int
+apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec, struct module *module)
+{
+ printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
+ module->name);
+ return -ENOEXEC;
+}
+
+int
+module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ struct module *module)
+{
+ return 0;
+}
+
+void
+module_arch_cleanup(struct module *mod)
+{
+}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
new file mode 100644
index 0000000..d3ea6fa
--- /dev/null
+++ b/arch/arm/kernel/process.c
@@ -0,0 +1,394 @@
+/*
+ * linux/arch/arm/kernel/process.c
+ *
+ * Copyright (C) 1996-2000 Russell King - Converted to ARM.
+ * Original Copyright (C) 1995 Linus Torvalds
+ *
+ * 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 <stdarg.h>
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+#include <linux/kallsyms.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/elfcore.h>
+#include <linux/pm.h>
+#include <linux/tick.h>
+#include <linux/utsname.h>
+#include <linux/uaccess.h>
+
+#include <asm/leds.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/thread_notify.h>
+#include <asm/mach/time.h>
+
+static const char *processor_modes[] = {
+ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
+ "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
+ "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
+ "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
+};
+
+static const char *isa_modes[] = {
+ "ARM" , "Thumb" , "Jazelle", "ThumbEE"
+};
+
+extern void setup_mm_for_reboot(char mode);
+
+static volatile int hlt_counter;
+
+#include <mach/system.h>
+
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+EXPORT_SYMBOL(disable_hlt);
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
+EXPORT_SYMBOL(enable_hlt);
+
+static int __init nohlt_setup(char *__unused)
+{
+ hlt_counter = 1;
+ return 1;
+}
+
+static int __init hlt_setup(char *__unused)
+{
+ hlt_counter = 0;
+ return 1;
+}
+
+__setup("nohlt", nohlt_setup);
+__setup("hlt", hlt_setup);
+
+void arm_machine_restart(char mode)
+{
+ /*
+ * Clean and disable cache, and turn off interrupts
+ */
+ cpu_proc_fin();
+
+ /*
+ * Tell the mm system that we are going to reboot -
+ * we may need it to insert some 1:1 mappings so that
+ * soft boot works.
+ */
+ setup_mm_for_reboot(mode);
+
+ /*
+ * Now call the architecture specific reboot code.
+ */
+ arch_reset(mode);
+
+ /*
+ * Whoops - the architecture was unable to reboot.
+ * Tell the user!
+ */
+ mdelay(1000);
+ printk("Reboot failed -- System halted\n");
+ while (1);
+}
+
+/*
+ * Function pointers to optional machine specific functions
+ */
+void (*pm_idle)(void);
+EXPORT_SYMBOL(pm_idle);
+
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+void (*arm_pm_restart)(char str) = arm_machine_restart;
+EXPORT_SYMBOL_GPL(arm_pm_restart);
+
+
+/*
+ * This is our default idle handler. We need to disable
+ * interrupts here to ensure we don't miss a wakeup call.
+ */
+static void default_idle(void)
+{
+ if (hlt_counter)
+ cpu_relax();
+ else {
+ local_irq_disable();
+ if (!need_resched())
+ arch_idle();
+ local_irq_enable();
+ }
+}
+
+/*
+ * The idle thread. We try to conserve power, while trying to keep
+ * overall latency low. The architecture specific idle is passed
+ * a value to indicate the level of "idleness" of the system.
+ */
+void cpu_idle(void)
+{
+ local_fiq_enable();
+
+ /* endless idle loop with no priority at all */
+ while (1) {
+ void (*idle)(void) = pm_idle;
+
+#ifdef CONFIG_HOTPLUG_CPU
+ if (cpu_is_offline(smp_processor_id())) {
+ leds_event(led_idle_start);
+ cpu_die();
+ }
+#endif
+
+ if (!idle)
+ idle = default_idle;
+ leds_event(led_idle_start);
+ tick_nohz_stop_sched_tick(1);
+ while (!need_resched())
+ idle();
+ leds_event(led_idle_end);
+ tick_nohz_restart_sched_tick();
+ preempt_enable_no_resched();
+ schedule();
+ preempt_disable();
+ }
+}
+
+static char reboot_mode = 'h';
+
+int __init reboot_setup(char *str)
+{
+ reboot_mode = str[0];
+ return 1;
+}
+
+__setup("reboot=", reboot_setup);
+
+void machine_halt(void)
+{
+}
+
+
+void machine_power_off(void)
+{
+ if (pm_power_off)
+ pm_power_off();
+}
+
+void machine_restart(char * __unused)
+{
+ arm_pm_restart(reboot_mode);
+}
+
+void __show_regs(struct pt_regs *regs)
+{
+ unsigned long flags;
+ char buf[64];
+
+ printk("CPU: %d %s (%s %.*s)\n",
+ smp_processor_id(), print_tainted(), init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ print_symbol("PC is at %s\n", instruction_pointer(regs));
+ print_symbol("LR is at %s\n", regs->ARM_lr);
+ printk("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n"
+ "sp : %08lx ip : %08lx fp : %08lx\n",
+ regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr,
+ regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
+ printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
+ regs->ARM_r10, regs->ARM_r9,
+ regs->ARM_r8);
+ printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
+ regs->ARM_r7, regs->ARM_r6,
+ regs->ARM_r5, regs->ARM_r4);
+ printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
+ regs->ARM_r3, regs->ARM_r2,
+ regs->ARM_r1, regs->ARM_r0);
+
+ flags = regs->ARM_cpsr;
+ buf[0] = flags & PSR_N_BIT ? 'N' : 'n';
+ buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
+ buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
+ buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
+ buf[4] = '\0';
+
+ printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n",
+ buf, interrupts_enabled(regs) ? "n" : "ff",
+ fast_interrupts_enabled(regs) ? "n" : "ff",
+ processor_modes[processor_mode(regs)],
+ isa_modes[isa_mode(regs)],
+ get_fs() == get_ds() ? "kernel" : "user");
+#ifdef CONFIG_CPU_CP15
+ {
+ unsigned int ctrl;
+
+ buf[0] = '\0';
+#ifdef CONFIG_CPU_CP15_MMU
+ {
+ unsigned int transbase, dac;
+ asm("mrc p15, 0, %0, c2, c0\n\t"
+ "mrc p15, 0, %1, c3, c0\n"
+ : "=r" (transbase), "=r" (dac));
+ snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x",
+ transbase, dac);
+ }
+#endif
+ asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl));
+
+ printk("Control: %08x%s\n", ctrl, buf);
+ }
+#endif
+}
+
+void show_regs(struct pt_regs * regs)
+{
+ printk("\n");
+ printk("Pid: %d, comm: %20s\n", task_pid_nr(current), current->comm);
+ __show_regs(regs);
+ __backtrace();
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+}
+
+ATOMIC_NOTIFIER_HEAD(thread_notify_head);
+
+EXPORT_SYMBOL_GPL(thread_notify_head);
+
+void flush_thread(void)
+{
+ struct thread_info *thread = current_thread_info();
+ struct task_struct *tsk = current;
+
+ memset(thread->used_cp, 0, sizeof(thread->used_cp));
+ memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
+ memset(&thread->fpstate, 0, sizeof(union fp_state));
+
+ thread_notify(THREAD_NOTIFY_FLUSH, thread);
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+ struct thread_info *thread = task_thread_info(dead_task);
+
+ thread_notify(THREAD_NOTIFY_RELEASE, thread);
+}
+
+asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
+
+int
+copy_thread(int nr, unsigned long clone_flags, unsigned long stack_start,
+ unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs)
+{
+ struct thread_info *thread = task_thread_info(p);
+ struct pt_regs *childregs = task_pt_regs(p);
+
+ *childregs = *regs;
+ childregs->ARM_r0 = 0;
+ childregs->ARM_sp = stack_start;
+
+ memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
+ thread->cpu_context.sp = (unsigned long)childregs;
+ thread->cpu_context.pc = (unsigned long)ret_from_fork;
+
+ if (clone_flags & CLONE_SETTLS)
+ thread->tp_value = regs->ARM_r3;
+
+ return 0;
+}
+
+/*
+ * fill in the fpe structure for a core dump...
+ */
+int dump_fpu (struct pt_regs *regs, struct user_fp *fp)
+{
+ struct thread_info *thread = current_thread_info();
+ int used_math = thread->used_cp[1] | thread->used_cp[2];
+
+ if (used_math)
+ memcpy(fp, &thread->fpstate.soft, sizeof (*fp));
+
+ return used_math != 0;
+}
+EXPORT_SYMBOL(dump_fpu);
+
+/*
+ * Shuffle the argument into the correct register before calling the
+ * thread function. r1 is the thread argument, r2 is the pointer to
+ * the thread function, and r3 points to the exit function.
+ */
+extern void kernel_thread_helper(void);
+asm( ".section .text\n"
+" .align\n"
+" .type kernel_thread_helper, #function\n"
+"kernel_thread_helper:\n"
+" mov r0, r1\n"
+" mov lr, r3\n"
+" mov pc, r2\n"
+" .size kernel_thread_helper, . - kernel_thread_helper\n"
+" .previous");
+
+/*
+ * Create a kernel thread.
+ */
+pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+
+ regs.ARM_r1 = (unsigned long)arg;
+ regs.ARM_r2 = (unsigned long)fn;
+ regs.ARM_r3 = (unsigned long)do_exit;
+ regs.ARM_pc = (unsigned long)kernel_thread_helper;
+ regs.ARM_cpsr = SVC_MODE;
+
+ return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
+}
+EXPORT_SYMBOL(kernel_thread);
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long fp, lr;
+ unsigned long stack_start, stack_end;
+ int count = 0;
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ stack_start = (unsigned long)end_of_stack(p);
+ stack_end = (unsigned long)task_stack_page(p) + THREAD_SIZE;
+
+ fp = thread_saved_fp(p);
+ do {
+ if (fp < stack_start || fp > stack_end)
+ return 0;
+ lr = ((unsigned long *)fp)[-1];
+ if (!in_sched_functions(lr))
+ return lr;
+ fp = *(unsigned long *) (fp - 12);
+ } while (count ++ < 16);
+ return 0;
+}
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
new file mode 100644
index 0000000..df653ea
--- /dev/null
+++ b/arch/arm/kernel/ptrace.c
@@ -0,0 +1,820 @@
+/*
+ * linux/arch/arm/kernel/ptrace.c
+ *
+ * By Ross Biro 1/23/92
+ * edited by Linus Torvalds
+ * ARM modifications Copyright (C) 2000 Russell King
+ *
+ * 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/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/security.h>
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/uaccess.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+
+#include "ptrace.h"
+
+#define REG_PC 15
+#define REG_PSR 16
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+#if 0
+/*
+ * Breakpoint SWI instruction: SWI &9F0001
+ */
+#define BREAKINST_ARM 0xef9f0001
+#define BREAKINST_THUMB 0xdf00 /* fill this in later */
+#else
+/*
+ * New breakpoints - use an undefined instruction. The ARM architecture
+ * reference manual guarantees that the following instruction space
+ * will produce an undefined instruction exception on all CPUs:
+ *
+ * ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
+ * Thumb: 1101 1110 xxxx xxxx
+ */
+#define BREAKINST_ARM 0xe7f001f0
+#define BREAKINST_THUMB 0xde01
+#endif
+
+/*
+ * this routine will get a word off of the processes privileged stack.
+ * the offset is how far from the base addr as stored in the THREAD.
+ * this routine assumes that all the privileged stacks are in our
+ * data space.
+ */
+static inline long get_user_reg(struct task_struct *task, int offset)
+{
+ return task_pt_regs(task)->uregs[offset];
+}
+
+/*
+ * this routine will put a word on the processes privileged stack.
+ * the offset is how far from the base addr as stored in the THREAD.
+ * this routine assumes that all the privileged stacks are in our
+ * data space.
+ */
+static inline int
+put_user_reg(struct task_struct *task, int offset, long data)
+{
+ struct pt_regs newregs, *regs = task_pt_regs(task);
+ int ret = -EINVAL;
+
+ newregs = *regs;
+ newregs.uregs[offset] = data;
+
+ if (valid_user_regs(&newregs)) {
+ regs->uregs[offset] = data;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static inline int
+read_u32(struct task_struct *task, unsigned long addr, u32 *res)
+{
+ int ret;
+
+ ret = access_process_vm(task, addr, res, sizeof(*res), 0);
+
+ return ret == sizeof(*res) ? 0 : -EIO;
+}
+
+static inline int
+read_instr(struct task_struct *task, unsigned long addr, u32 *res)
+{
+ int ret;
+
+ if (addr & 1) {
+ u16 val;
+ ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0);
+ ret = ret == sizeof(val) ? 0 : -EIO;
+ *res = val;
+ } else {
+ u32 val;
+ ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0);
+ ret = ret == sizeof(val) ? 0 : -EIO;
+ *res = val;
+ }
+ return ret;
+}
+
+/*
+ * Get value of register `rn' (in the instruction)
+ */
+static unsigned long
+ptrace_getrn(struct task_struct *child, unsigned long insn)
+{
+ unsigned int reg = (insn >> 16) & 15;
+ unsigned long val;
+
+ val = get_user_reg(child, reg);
+ if (reg == 15)
+ val += 8;
+
+ return val;
+}
+
+/*
+ * Get value of operand 2 (in an ALU instruction)
+ */
+static unsigned long
+ptrace_getaluop2(struct task_struct *child, unsigned long insn)
+{
+ unsigned long val;
+ int shift;
+ int type;
+
+ if (insn & 1 << 25) {
+ val = insn & 255;
+ shift = (insn >> 8) & 15;
+ type = 3;
+ } else {
+ val = get_user_reg (child, insn & 15);
+
+ if (insn & (1 << 4))
+ shift = (int)get_user_reg (child, (insn >> 8) & 15);
+ else
+ shift = (insn >> 7) & 31;
+
+ type = (insn >> 5) & 3;
+ }
+
+ switch (type) {
+ case 0: val <<= shift; break;
+ case 1: val >>= shift; break;
+ case 2:
+ val = (((signed long)val) >> shift);
+ break;
+ case 3:
+ val = (val >> shift) | (val << (32 - shift));
+ break;
+ }
+ return val;
+}
+
+/*
+ * Get value of operand 2 (in a LDR instruction)
+ */
+static unsigned long
+ptrace_getldrop2(struct task_struct *child, unsigned long insn)
+{
+ unsigned long val;
+ int shift;
+ int type;
+
+ val = get_user_reg(child, insn & 15);
+ shift = (insn >> 7) & 31;
+ type = (insn >> 5) & 3;
+
+ switch (type) {
+ case 0: val <<= shift; break;
+ case 1: val >>= shift; break;
+ case 2:
+ val = (((signed long)val) >> shift);
+ break;
+ case 3:
+ val = (val >> shift) | (val << (32 - shift));
+ break;
+ }
+ return val;
+}
+
+#define OP_MASK 0x01e00000
+#define OP_AND 0x00000000
+#define OP_EOR 0x00200000
+#define OP_SUB 0x00400000
+#define OP_RSB 0x00600000
+#define OP_ADD 0x00800000
+#define OP_ADC 0x00a00000
+#define OP_SBC 0x00c00000
+#define OP_RSC 0x00e00000
+#define OP_ORR 0x01800000
+#define OP_MOV 0x01a00000
+#define OP_BIC 0x01c00000
+#define OP_MVN 0x01e00000
+
+static unsigned long
+get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
+{
+ u32 alt = 0;
+
+ switch (insn & 0x0e000000) {
+ case 0x00000000:
+ case 0x02000000: {
+ /*
+ * data processing
+ */
+ long aluop1, aluop2, ccbit;
+
+ if ((insn & 0x0fffffd0) == 0x012fff10) {
+ /*
+ * bx or blx
+ */
+ alt = get_user_reg(child, insn & 15);
+ break;
+ }
+
+
+ if ((insn & 0xf000) != 0xf000)
+ break;
+
+ aluop1 = ptrace_getrn(child, insn);
+ aluop2 = ptrace_getaluop2(child, insn);
+ ccbit = get_user_reg(child, REG_PSR) & PSR_C_BIT ? 1 : 0;
+
+ switch (insn & OP_MASK) {
+ case OP_AND: alt = aluop1 & aluop2; break;
+ case OP_EOR: alt = aluop1 ^ aluop2; break;
+ case OP_SUB: alt = aluop1 - aluop2; break;
+ case OP_RSB: alt = aluop2 - aluop1; break;
+ case OP_ADD: alt = aluop1 + aluop2; break;
+ case OP_ADC: alt = aluop1 + aluop2 + ccbit; break;
+ case OP_SBC: alt = aluop1 - aluop2 + ccbit; break;
+ case OP_RSC: alt = aluop2 - aluop1 + ccbit; break;
+ case OP_ORR: alt = aluop1 | aluop2; break;
+ case OP_MOV: alt = aluop2; break;
+ case OP_BIC: alt = aluop1 & ~aluop2; break;
+ case OP_MVN: alt = ~aluop2; break;
+ }
+ break;
+ }
+
+ case 0x04000000:
+ case 0x06000000:
+ /*
+ * ldr
+ */
+ if ((insn & 0x0010f000) == 0x0010f000) {
+ unsigned long base;
+
+ base = ptrace_getrn(child, insn);
+ if (insn & 1 << 24) {
+ long aluop2;
+
+ if (insn & 0x02000000)
+ aluop2 = ptrace_getldrop2(child, insn);
+ else
+ aluop2 = insn & 0xfff;
+
+ if (insn & 1 << 23)
+ base += aluop2;
+ else
+ base -= aluop2;
+ }
+ read_u32(child, base, &alt);
+ }
+ break;
+
+ case 0x08000000:
+ /*
+ * ldm
+ */
+ if ((insn & 0x00108000) == 0x00108000) {
+ unsigned long base;
+ unsigned int nr_regs;
+
+ if (insn & (1 << 23)) {
+ nr_regs = hweight16(insn & 65535) << 2;
+
+ if (!(insn & (1 << 24)))
+ nr_regs -= 4;
+ } else {
+ if (insn & (1 << 24))
+ nr_regs = -4;
+ else
+ nr_regs = 0;
+ }
+
+ base = ptrace_getrn(child, insn);
+
+ read_u32(child, base + nr_regs, &alt);
+ break;
+ }
+ break;
+
+ case 0x0a000000: {
+ /*
+ * bl or b
+ */
+ signed long displ;
+ /* It's a branch/branch link: instead of trying to
+ * figure out whether the branch will be taken or not,
+ * we'll put a breakpoint at both locations. This is
+ * simpler, more reliable, and probably not a whole lot
+ * slower than the alternative approach of emulating the
+ * branch.
+ */
+ displ = (insn & 0x00ffffff) << 8;
+ displ = (displ >> 6) + 8;
+ if (displ != 0 && displ != 4)
+ alt = pc + displ;
+ }
+ break;
+ }
+
+ return alt;
+}
+
+static int
+swap_insn(struct task_struct *task, unsigned long addr,
+ void *old_insn, void *new_insn, int size)
+{
+ int ret;
+
+ ret = access_process_vm(task, addr, old_insn, size, 0);
+ if (ret == size)
+ ret = access_process_vm(task, addr, new_insn, size, 1);
+ return ret;
+}
+
+static void
+add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr)
+{
+ int nr = dbg->nsaved;
+
+ if (nr < 2) {
+ u32 new_insn = BREAKINST_ARM;
+ int res;
+
+ res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4);
+
+ if (res == 4) {
+ dbg->bp[nr].address = addr;
+ dbg->nsaved += 1;
+ }
+ } else
+ printk(KERN_ERR "ptrace: too many breakpoints\n");
+}
+
+/*
+ * Clear one breakpoint in the user program. We copy what the hardware
+ * does and use bit 0 of the address to indicate whether this is a Thumb
+ * breakpoint or an ARM breakpoint.
+ */
+static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp)
+{
+ unsigned long addr = bp->address;
+ union debug_insn old_insn;
+ int ret;
+
+ if (addr & 1) {
+ ret = swap_insn(task, addr & ~1, &old_insn.thumb,
+ &bp->insn.thumb, 2);
+
+ if (ret != 2 || old_insn.thumb != BREAKINST_THUMB)
+ printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at "
+ "0x%08lx (0x%04x)\n", task->comm,
+ task_pid_nr(task), addr, old_insn.thumb);
+ } else {
+ ret = swap_insn(task, addr & ~3, &old_insn.arm,
+ &bp->insn.arm, 4);
+
+ if (ret != 4 || old_insn.arm != BREAKINST_ARM)
+ printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at "
+ "0x%08lx (0x%08x)\n", task->comm,
+ task_pid_nr(task), addr, old_insn.arm);
+ }
+}
+
+void ptrace_set_bpt(struct task_struct *child)
+{
+ struct pt_regs *regs;
+ unsigned long pc;
+ u32 insn;
+ int res;
+
+ regs = task_pt_regs(child);
+ pc = instruction_pointer(regs);
+
+ if (thumb_mode(regs)) {
+ printk(KERN_WARNING "ptrace: can't handle thumb mode\n");
+ return;
+ }
+
+ res = read_instr(child, pc, &insn);
+ if (!res) {
+ struct debug_info *dbg = &child->thread.debug;
+ unsigned long alt;
+
+ dbg->nsaved = 0;
+
+ alt = get_branch_address(child, pc, insn);
+ if (alt)
+ add_breakpoint(child, dbg, alt);
+
+ /*
+ * Note that we ignore the result of setting the above
+ * breakpoint since it may fail. When it does, this is
+ * not so much an error, but a forewarning that we may
+ * be receiving a prefetch abort shortly.
+ *
+ * If we don't set this breakpoint here, then we can
+ * lose control of the thread during single stepping.
+ */
+ if (!alt || predicate(insn) != PREDICATE_ALWAYS)
+ add_breakpoint(child, dbg, pc + 4);
+ }
+}
+
+/*
+ * Ensure no single-step breakpoint is pending. Returns non-zero
+ * value if child was being single-stepped.
+ */
+void ptrace_cancel_bpt(struct task_struct *child)
+{
+ int i, nsaved = child->thread.debug.nsaved;
+
+ child->thread.debug.nsaved = 0;
+
+ if (nsaved > 2) {
+ printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
+ nsaved = 2;
+ }
+
+ for (i = 0; i < nsaved; i++)
+ clear_breakpoint(child, &child->thread.debug.bp[i]);
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ */
+void ptrace_disable(struct task_struct *child)
+{
+ single_step_disable(child);
+}
+
+/*
+ * Handle hitting a breakpoint.
+ */
+void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ ptrace_cancel_bpt(tsk);
+
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_BRKPT;
+ info.si_addr = (void __user *)instruction_pointer(regs);
+
+ force_sig_info(SIGTRAP, &info, tsk);
+}
+
+static int break_trap(struct pt_regs *regs, unsigned int instr)
+{
+ ptrace_break(current, regs);
+ return 0;
+}
+
+static struct undef_hook arm_break_hook = {
+ .instr_mask = 0x0fffffff,
+ .instr_val = 0x07f001f0,
+ .cpsr_mask = PSR_T_BIT,
+ .cpsr_val = 0,
+ .fn = break_trap,
+};
+
+static struct undef_hook thumb_break_hook = {
+ .instr_mask = 0xffff,
+ .instr_val = 0xde01,
+ .cpsr_mask = PSR_T_BIT,
+ .cpsr_val = PSR_T_BIT,
+ .fn = break_trap,
+};
+
+static int __init ptrace_break_init(void)
+{
+ register_undef_hook(&arm_break_hook);
+ register_undef_hook(&thumb_break_hook);
+ return 0;
+}
+
+core_initcall(ptrace_break_init);
+
+/*
+ * Read the word at offset "off" into the "struct user". We
+ * actually access the pt_regs stored on the kernel stack.
+ */
+static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
+ unsigned long __user *ret)
+{
+ unsigned long tmp;
+
+ if (off & 3 || off >= sizeof(struct user))
+ return -EIO;
+
+ tmp = 0;
+ if (off < sizeof(struct pt_regs))
+ tmp = get_user_reg(tsk, off >> 2);
+
+ return put_user(tmp, ret);
+}
+
+/*
+ * Write the word at offset "off" into "struct user". We
+ * actually access the pt_regs stored on the kernel stack.
+ */
+static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
+ unsigned long val)
+{
+ if (off & 3 || off >= sizeof(struct user))
+ return -EIO;
+
+ if (off >= sizeof(struct pt_regs))
+ return 0;
+
+ return put_user_reg(tsk, off >> 2, val);
+}
+
+/*
+ * Get all user integer registers.
+ */
+static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
+{
+ struct pt_regs *regs = task_pt_regs(tsk);
+
+ return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
+}
+
+/*
+ * Set all user integer registers.
+ */
+static int ptrace_setregs(struct task_struct *tsk, void __user *uregs)
+{
+ struct pt_regs newregs;
+ int ret;
+
+ ret = -EFAULT;
+ if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) {
+ struct pt_regs *regs = task_pt_regs(tsk);
+
+ ret = -EINVAL;
+ if (valid_user_regs(&newregs)) {
+ *regs = newregs;
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Get the child FPU state.
+ */
+static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp)
+{
+ return copy_to_user(ufp, &task_thread_info(tsk)->fpstate,
+ sizeof(struct user_fp)) ? -EFAULT : 0;
+}
+
+/*
+ * Set the child FPU state.
+ */
+static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp)
+{
+ struct thread_info *thread = task_thread_info(tsk);
+ thread->used_cp[1] = thread->used_cp[2] = 1;
+ return copy_from_user(&thread->fpstate, ufp,
+ sizeof(struct user_fp)) ? -EFAULT : 0;
+}
+
+#ifdef CONFIG_IWMMXT
+
+/*
+ * Get the child iWMMXt state.
+ */
+static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp)
+{
+ struct thread_info *thread = task_thread_info(tsk);
+
+ if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
+ return -ENODATA;
+ iwmmxt_task_disable(thread); /* force it to ram */
+ return copy_to_user(ufp, &thread->fpstate.iwmmxt, IWMMXT_SIZE)
+ ? -EFAULT : 0;
+}
+
+/*
+ * Set the child iWMMXt state.
+ */
+static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp)
+{
+ struct thread_info *thread = task_thread_info(tsk);
+
+ if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
+ return -EACCES;
+ iwmmxt_task_release(thread); /* force a reload */
+ return copy_from_user(&thread->fpstate.iwmmxt, ufp, IWMMXT_SIZE)
+ ? -EFAULT : 0;
+}
+
+#endif
+
+#ifdef CONFIG_CRUNCH
+/*
+ * Get the child Crunch state.
+ */
+static int ptrace_getcrunchregs(struct task_struct *tsk, void __user *ufp)
+{
+ struct thread_info *thread = task_thread_info(tsk);
+
+ crunch_task_disable(thread); /* force it to ram */
+ return copy_to_user(ufp, &thread->crunchstate, CRUNCH_SIZE)
+ ? -EFAULT : 0;
+}
+
+/*
+ * Set the child Crunch state.
+ */
+static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
+{
+ struct thread_info *thread = task_thread_info(tsk);
+
+ crunch_task_release(thread); /* force a reload */
+ return copy_from_user(&thread->crunchstate, ufp, CRUNCH_SIZE)
+ ? -EFAULT : 0;
+}
+#endif
+
+long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+{
+ int ret;
+
+ switch (request) {
+ /*
+ * read word at location "addr" in the child process.
+ */
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ ret = generic_ptrace_peekdata(child, addr, data);
+ break;
+
+ case PTRACE_PEEKUSR:
+ ret = ptrace_read_user(child, addr, (unsigned long __user *)data);
+ break;
+
+ /*
+ * write the word at location addr.
+ */
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ ret = generic_ptrace_pokedata(child, addr, data);
+ break;
+
+ case PTRACE_POKEUSR:
+ ret = ptrace_write_user(child, addr, data);
+ break;
+
+ /*
+ * continue/restart and stop at next (return from) syscall
+ */
+ case PTRACE_SYSCALL:
+ case PTRACE_CONT:
+ ret = -EIO;
+ if (!valid_signal(data))
+ break;
+ if (request == PTRACE_SYSCALL)
+ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ else
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ child->exit_code = data;
+ single_step_disable(child);
+ wake_up_process(child);
+ ret = 0;
+ break;
+
+ /*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL:
+ single_step_disable(child);
+ if (child->exit_state != EXIT_ZOMBIE) {
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ }
+ ret = 0;
+ break;
+
+ /*
+ * execute single instruction.
+ */
+ case PTRACE_SINGLESTEP:
+ ret = -EIO;
+ if (!valid_signal(data))
+ break;
+ single_step_enable(child);
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ child->exit_code = data;
+ /* give it a chance to run. */
+ wake_up_process(child);
+ ret = 0;
+ break;
+
+ case PTRACE_GETREGS:
+ ret = ptrace_getregs(child, (void __user *)data);
+ break;
+
+ case PTRACE_SETREGS:
+ ret = ptrace_setregs(child, (void __user *)data);
+ break;
+
+ case PTRACE_GETFPREGS:
+ ret = ptrace_getfpregs(child, (void __user *)data);
+ break;
+
+ case PTRACE_SETFPREGS:
+ ret = ptrace_setfpregs(child, (void __user *)data);
+ break;
+
+#ifdef CONFIG_IWMMXT
+ case PTRACE_GETWMMXREGS:
+ ret = ptrace_getwmmxregs(child, (void __user *)data);
+ break;
+
+ case PTRACE_SETWMMXREGS:
+ ret = ptrace_setwmmxregs(child, (void __user *)data);
+ break;
+#endif
+
+ case PTRACE_GET_THREAD_AREA:
+ ret = put_user(task_thread_info(child)->tp_value,
+ (unsigned long __user *) data);
+ break;
+
+ case PTRACE_SET_SYSCALL:
+ task_thread_info(child)->syscall = data;
+ ret = 0;
+ break;
+
+#ifdef CONFIG_CRUNCH
+ case PTRACE_GETCRUNCHREGS:
+ ret = ptrace_getcrunchregs(child, (void __user *)data);
+ break;
+
+ case PTRACE_SETCRUNCHREGS:
+ ret = ptrace_setcrunchregs(child, (void __user *)data);
+ break;
+#endif
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ return ret;
+}
+
+asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
+{
+ unsigned long ip;
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return scno;
+ if (!(current->ptrace & PT_PTRACED))
+ return scno;
+
+ /*
+ * Save IP. IP is used to denote syscall entry/exit:
+ * IP = 0 -> entry, = 1 -> exit
+ */
+ ip = regs->ARM_ip;
+ regs->ARM_ip = why;
+
+ current_thread_info()->syscall = scno;
+
+ /* the 0x80 provides a way for the tracing parent to distinguish
+ between a syscall stop and SIGTRAP delivery */
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0));
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (current->exit_code) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+ regs->ARM_ip = ip;
+
+ return current_thread_info()->syscall;
+}
diff --git a/arch/arm/kernel/ptrace.h b/arch/arm/kernel/ptrace.h
new file mode 100644
index 0000000..def3b61
--- /dev/null
+++ b/arch/arm/kernel/ptrace.h
@@ -0,0 +1,51 @@
+/*
+ * linux/arch/arm/kernel/ptrace.h
+ *
+ * Copyright (C) 2000-2003 Russell King
+ *
+ * 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/ptrace.h>
+
+extern void ptrace_cancel_bpt(struct task_struct *);
+extern void ptrace_set_bpt(struct task_struct *);
+extern void ptrace_break(struct task_struct *, struct pt_regs *);
+
+/*
+ * make sure single-step breakpoint is gone.
+ */
+static inline void single_step_disable(struct task_struct *task)
+{
+ task->ptrace &= ~PT_SINGLESTEP;
+ ptrace_cancel_bpt(task);
+}
+
+static inline void single_step_enable(struct task_struct *task)
+{
+ task->ptrace |= PT_SINGLESTEP;
+}
+
+/*
+ * Send SIGTRAP if we're single-stepping
+ */
+static inline void single_step_trap(struct task_struct *task)
+{
+ if (task->ptrace & PT_SINGLESTEP) {
+ ptrace_cancel_bpt(task);
+ send_sig(SIGTRAP, task, 1);
+ }
+}
+
+static inline void single_step_clear(struct task_struct *task)
+{
+ if (task->ptrace & PT_SINGLESTEP)
+ ptrace_cancel_bpt(task);
+}
+
+static inline void single_step_set(struct task_struct *task)
+{
+ if (task->ptrace & PT_SINGLESTEP)
+ ptrace_set_bpt(task);
+}
diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S
new file mode 100644
index 0000000..61930eb
--- /dev/null
+++ b/arch/arm/kernel/relocate_kernel.S
@@ -0,0 +1,79 @@
+/*
+ * relocate_kernel.S - put the kernel image in place to boot
+ */
+
+#include <asm/kexec.h>
+
+ .globl relocate_new_kernel
+relocate_new_kernel:
+
+ ldr r0,kexec_indirection_page
+ ldr r1,kexec_start_address
+
+
+0: /* top, read another word for the indirection page */
+ ldr r3, [r0],#4
+
+ /* Is it a destination page. Put destination address to r4 */
+ tst r3,#1,0
+ beq 1f
+ bic r4,r3,#1
+ b 0b
+1:
+ /* Is it an indirection page */
+ tst r3,#2,0
+ beq 1f
+ bic r0,r3,#2
+ b 0b
+1:
+
+ /* are we done ? */
+ tst r3,#4,0
+ beq 1f
+ b 2f
+
+1:
+ /* is it source ? */
+ tst r3,#8,0
+ beq 0b
+ bic r3,r3,#8
+ mov r6,#1024
+9:
+ ldr r5,[r3],#4
+ str r5,[r4],#4
+ subs r6,r6,#1
+ bne 9b
+ b 0b
+
+2:
+ /* Jump to relocated kernel */
+ mov lr,r1
+ mov r0,#0
+ ldr r1,kexec_mach_type
+ ldr r2,kexec_boot_atags
+ mov pc,lr
+
+ .globl kexec_start_address
+kexec_start_address:
+ .long 0x0
+
+ .globl kexec_indirection_page
+kexec_indirection_page:
+ .long 0x0
+
+ .globl kexec_mach_type
+kexec_mach_type:
+ .long 0x0
+
+ /* phy addr of the atags for the new kernel */
+ .globl kexec_boot_atags
+kexec_boot_atags:
+ .long 0x0
+
+relocate_new_kernel_end:
+
+ .globl relocate_new_kernel_size
+relocate_new_kernel_size:
+ .long relocate_new_kernel_end - relocate_new_kernel
+
+
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
new file mode 100644
index 0000000..1f1eecc
--- /dev/null
+++ b/arch/arm/kernel/setup.c
@@ -0,0 +1,861 @@
+/*
+ * linux/arch/arm/kernel/setup.c
+ *
+ * Copyright (C) 1995-2001 Russell King
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/utsname.h>
+#include <linux/initrd.h>
+#include <linux/console.h>
+#include <linux/bootmem.h>
+#include <linux/seq_file.h>
+#include <linux/screen_info.h>
+#include <linux/init.h>
+#include <linux/root_dev.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/fs.h>
+
+#include <asm/cpu.h>
+#include <asm/cputype.h>
+#include <asm/elf.h>
+#include <asm/procinfo.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/cacheflush.h>
+#include <asm/cachetype.h>
+#include <asm/tlbflush.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <asm/traps.h>
+
+#include "compat.h"
+#include "atags.h"
+
+#ifndef MEM_SIZE
+#define MEM_SIZE (16*1024*1024)
+#endif
+
+#if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE)
+char fpe_type[8];
+
+static int __init fpe_setup(char *line)
+{
+ memcpy(fpe_type, line, 8);
+ return 1;
+}
+
+__setup("fpe=", fpe_setup);
+#endif
+
+extern void paging_init(struct meminfo *, struct machine_desc *desc);
+extern void reboot_setup(char *str);
+extern void _text, _etext, __data_start, _edata, _end;
+
+unsigned int processor_id;
+EXPORT_SYMBOL(processor_id);
+unsigned int __machine_arch_type;
+EXPORT_SYMBOL(__machine_arch_type);
+unsigned int cacheid;
+EXPORT_SYMBOL(cacheid);
+
+unsigned int __atags_pointer __initdata;
+
+unsigned int system_rev;
+EXPORT_SYMBOL(system_rev);
+
+unsigned int system_serial_low;
+EXPORT_SYMBOL(system_serial_low);
+
+unsigned int system_serial_high;
+EXPORT_SYMBOL(system_serial_high);
+
+unsigned int elf_hwcap;
+EXPORT_SYMBOL(elf_hwcap);
+
+
+#ifdef MULTI_CPU
+struct processor processor;
+#endif
+#ifdef MULTI_TLB
+struct cpu_tlb_fns cpu_tlb;
+#endif
+#ifdef MULTI_USER
+struct cpu_user_fns cpu_user;
+#endif
+#ifdef MULTI_CACHE
+struct cpu_cache_fns cpu_cache;
+#endif
+#ifdef CONFIG_OUTER_CACHE
+struct outer_cache_fns outer_cache;
+#endif
+
+struct stack {
+ u32 irq[3];
+ u32 abt[3];
+ u32 und[3];
+} ____cacheline_aligned;
+
+static struct stack stacks[NR_CPUS];
+
+char elf_platform[ELF_PLATFORM_SIZE];
+EXPORT_SYMBOL(elf_platform);
+
+static struct meminfo meminfo __initdata = { 0, };
+static const char *cpu_name;
+static const char *machine_name;
+static char __initdata command_line[COMMAND_LINE_SIZE];
+
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
+static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
+#define ENDIANNESS ((char)endian_test.l)
+
+DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
+
+/*
+ * Standard memory resources
+ */
+static struct resource mem_res[] = {
+ {
+ .name = "Video RAM",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .name = "Kernel text",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .name = "Kernel data",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+#define video_ram mem_res[0]
+#define kernel_code mem_res[1]
+#define kernel_data mem_res[2]
+
+static struct resource io_res[] = {
+ {
+ .name = "reserved",
+ .start = 0x3bc,
+ .end = 0x3be,
+ .flags = IORESOURCE_IO | IORESOURCE_BUSY
+ },
+ {
+ .name = "reserved",
+ .start = 0x378,
+ .end = 0x37f,
+ .flags = IORESOURCE_IO | IORESOURCE_BUSY
+ },
+ {
+ .name = "reserved",
+ .start = 0x278,
+ .end = 0x27f,
+ .flags = IORESOURCE_IO | IORESOURCE_BUSY
+ }
+};
+
+#define lp0 io_res[0]
+#define lp1 io_res[1]
+#define lp2 io_res[2]
+
+static const char *proc_arch[] = {
+ "undefined/unknown",
+ "3",
+ "4",
+ "4T",
+ "5",
+ "5T",
+ "5TE",
+ "5TEJ",
+ "6TEJ",
+ "7",
+ "?(11)",
+ "?(12)",
+ "?(13)",
+ "?(14)",
+ "?(15)",
+ "?(16)",
+ "?(17)",
+};
+
+int cpu_architecture(void)
+{
+ int cpu_arch;
+
+ if ((read_cpuid_id() & 0x0008f000) == 0) {
+ cpu_arch = CPU_ARCH_UNKNOWN;
+ } else if ((read_cpuid_id() & 0x0008f000) == 0x00007000) {
+ cpu_arch = (read_cpuid_id() & (1 << 23)) ? CPU_ARCH_ARMv4T : CPU_ARCH_ARMv3;
+ } else if ((read_cpuid_id() & 0x00080000) == 0x00000000) {
+ cpu_arch = (read_cpuid_id() >> 16) & 7;
+ if (cpu_arch)
+ cpu_arch += CPU_ARCH_ARMv3;
+ } else if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) {
+ unsigned int mmfr0;
+
+ /* Revised CPUID format. Read the Memory Model Feature
+ * Register 0 and check for VMSAv7 or PMSAv7 */
+ asm("mrc p15, 0, %0, c0, c1, 4"
+ : "=r" (mmfr0));
+ if ((mmfr0 & 0x0000000f) == 0x00000003 ||
+ (mmfr0 & 0x000000f0) == 0x00000030)
+ cpu_arch = CPU_ARCH_ARMv7;
+ else if ((mmfr0 & 0x0000000f) == 0x00000002 ||
+ (mmfr0 & 0x000000f0) == 0x00000020)
+ cpu_arch = CPU_ARCH_ARMv6;
+ else
+ cpu_arch = CPU_ARCH_UNKNOWN;
+ } else
+ cpu_arch = CPU_ARCH_UNKNOWN;
+
+ return cpu_arch;
+}
+
+static void __init cacheid_init(void)
+{
+ unsigned int cachetype = read_cpuid_cachetype();
+ unsigned int arch = cpu_architecture();
+
+ if (arch >= CPU_ARCH_ARMv7) {
+ cacheid = CACHEID_VIPT_NONALIASING;
+ if ((cachetype & (3 << 14)) == 1 << 14)
+ cacheid |= CACHEID_ASID_TAGGED;
+ } else if (arch >= CPU_ARCH_ARMv6) {
+ if (cachetype & (1 << 23))
+ cacheid = CACHEID_VIPT_ALIASING;
+ else
+ cacheid = CACHEID_VIPT_NONALIASING;
+ } else {
+ cacheid = CACHEID_VIVT;
+ }
+
+ printk("CPU: %s data cache, %s instruction cache\n",
+ cache_is_vivt() ? "VIVT" :
+ cache_is_vipt_aliasing() ? "VIPT aliasing" :
+ cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown",
+ cache_is_vivt() ? "VIVT" :
+ icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" :
+ cache_is_vipt_aliasing() ? "VIPT aliasing" :
+ cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown");
+}
+
+/*
+ * These functions re-use the assembly code in head.S, which
+ * already provide the required functionality.
+ */
+extern struct proc_info_list *lookup_processor_type(unsigned int);
+extern struct machine_desc *lookup_machine_type(unsigned int);
+
+static void __init setup_processor(void)
+{
+ struct proc_info_list *list;
+
+ /*
+ * locate processor in the list of supported processor
+ * types. The linker builds this table for us from the
+ * entries in arch/arm/mm/proc-*.S
+ */
+ list = lookup_processor_type(read_cpuid_id());
+ if (!list) {
+ printk("CPU configuration botched (ID %08x), unable "
+ "to continue.\n", read_cpuid_id());
+ while (1);
+ }
+
+ cpu_name = list->cpu_name;
+
+#ifdef MULTI_CPU
+ processor = *list->proc;
+#endif
+#ifdef MULTI_TLB
+ cpu_tlb = *list->tlb;
+#endif
+#ifdef MULTI_USER
+ cpu_user = *list->user;
+#endif
+#ifdef MULTI_CACHE
+ cpu_cache = *list->cache;
+#endif
+
+ printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",
+ cpu_name, read_cpuid_id(), read_cpuid_id() & 15,
+ proc_arch[cpu_architecture()], cr_alignment);
+
+ sprintf(init_utsname()->machine, "%s%c", list->arch_name, ENDIANNESS);
+ sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
+ elf_hwcap = list->elf_hwcap;
+#ifndef CONFIG_ARM_THUMB
+ elf_hwcap &= ~HWCAP_THUMB;
+#endif
+
+ cacheid_init();
+ cpu_proc_init();
+}
+
+/*
+ * cpu_init - initialise one CPU.
+ *
+ * cpu_init sets up the per-CPU stacks.
+ */
+void cpu_init(void)
+{
+ unsigned int cpu = smp_processor_id();
+ struct stack *stk = &stacks[cpu];
+
+ if (cpu >= NR_CPUS) {
+ printk(KERN_CRIT "CPU%u: bad primary CPU number\n", cpu);
+ BUG();
+ }
+
+ /*
+ * setup stacks for re-entrant exception handlers
+ */
+ __asm__ (
+ "msr cpsr_c, %1\n\t"
+ "add sp, %0, %2\n\t"
+ "msr cpsr_c, %3\n\t"
+ "add sp, %0, %4\n\t"
+ "msr cpsr_c, %5\n\t"
+ "add sp, %0, %6\n\t"
+ "msr cpsr_c, %7"
+ :
+ : "r" (stk),
+ "I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
+ "I" (offsetof(struct stack, irq[0])),
+ "I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
+ "I" (offsetof(struct stack, abt[0])),
+ "I" (PSR_F_BIT | PSR_I_BIT | UND_MODE),
+ "I" (offsetof(struct stack, und[0])),
+ "I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
+ : "r14");
+}
+
+static struct machine_desc * __init setup_machine(unsigned int nr)
+{
+ struct machine_desc *list;
+
+ /*
+ * locate machine in the list of supported machines.
+ */
+ list = lookup_machine_type(nr);
+ if (!list) {
+ printk("Machine configuration botched (nr %d), unable "
+ "to continue.\n", nr);
+ while (1);
+ }
+
+ printk("Machine: %s\n", list->name);
+
+ return list;
+}
+
+static void __init arm_add_memory(unsigned long start, unsigned long size)
+{
+ struct membank *bank;
+
+ /*
+ * Ensure that start/size are aligned to a page boundary.
+ * Size is appropriately rounded down, start is rounded up.
+ */
+ size -= start & ~PAGE_MASK;
+
+ bank = &meminfo.bank[meminfo.nr_banks++];
+
+ bank->start = PAGE_ALIGN(start);
+ bank->size = size & PAGE_MASK;
+ bank->node = PHYS_TO_NID(start);
+}
+
+/*
+ * Pick out the memory size. We look for mem=size@start,
+ * where start and size are "size[KkMm]"
+ */
+static void __init early_mem(char **p)
+{
+ static int usermem __initdata = 0;
+ unsigned long size, start;
+
+ /*
+ * If the user specifies memory size, we
+ * blow away any automatically generated
+ * size.
+ */
+ if (usermem == 0) {
+ usermem = 1;
+ meminfo.nr_banks = 0;
+ }
+
+ start = PHYS_OFFSET;
+ size = memparse(*p, p);
+ if (**p == '@')
+ start = memparse(*p + 1, p);
+
+ arm_add_memory(start, size);
+}
+__early_param("mem=", early_mem);
+
+/*
+ * Initial parsing of the command line.
+ */
+static void __init parse_cmdline(char **cmdline_p, char *from)
+{
+ char c = ' ', *to = command_line;
+ int len = 0;
+
+ for (;;) {
+ if (c == ' ') {
+ extern struct early_params __early_begin, __early_end;
+ struct early_params *p;
+
+ for (p = &__early_begin; p < &__early_end; p++) {
+ int arglen = strlen(p->arg);
+
+ if (memcmp(from, p->arg, arglen) == 0) {
+ if (to != command_line)
+ to -= 1;
+ from += arglen;
+ p->fn(&from);
+
+ while (*from != ' ' && *from != '\0')
+ from++;
+ break;
+ }
+ }
+ }
+ c = *from++;
+ if (!c)
+ break;
+ if (COMMAND_LINE_SIZE <= ++len)
+ break;
+ *to++ = c;
+ }
+ *to = '\0';
+ *cmdline_p = command_line;
+}
+
+static void __init
+setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
+{
+#ifdef CONFIG_BLK_DEV_RAM
+ extern int rd_size, rd_image_start, rd_prompt, rd_doload;
+
+ rd_image_start = image_start;
+ rd_prompt = prompt;
+ rd_doload = doload;
+
+ if (rd_sz)
+ rd_size = rd_sz;
+#endif
+}
+
+static void __init
+request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
+{
+ struct resource *res;
+ int i;
+
+ kernel_code.start = virt_to_phys(&_text);
+ kernel_code.end = virt_to_phys(&_etext - 1);
+ kernel_data.start = virt_to_phys(&__data_start);
+ kernel_data.end = virt_to_phys(&_end - 1);
+
+ for (i = 0; i < mi->nr_banks; i++) {
+ if (mi->bank[i].size == 0)
+ continue;
+
+ res = alloc_bootmem_low(sizeof(*res));
+ res->name = "System RAM";
+ res->start = mi->bank[i].start;
+ res->end = mi->bank[i].start + mi->bank[i].size - 1;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ request_resource(&iomem_resource, res);
+
+ if (kernel_code.start >= res->start &&
+ kernel_code.end <= res->end)
+ request_resource(res, &kernel_code);
+ if (kernel_data.start >= res->start &&
+ kernel_data.end <= res->end)
+ request_resource(res, &kernel_data);
+ }
+
+ if (mdesc->video_start) {
+ video_ram.start = mdesc->video_start;
+ video_ram.end = mdesc->video_end;
+ request_resource(&iomem_resource, &video_ram);
+ }
+
+ /*
+ * Some machines don't have the possibility of ever
+ * possessing lp0, lp1 or lp2
+ */
+ if (mdesc->reserve_lp0)
+ request_resource(&ioport_resource, &lp0);
+ if (mdesc->reserve_lp1)
+ request_resource(&ioport_resource, &lp1);
+ if (mdesc->reserve_lp2)
+ request_resource(&ioport_resource, &lp2);
+}
+
+/*
+ * Tag parsing.
+ *
+ * This is the new way of passing data to the kernel at boot time. Rather
+ * than passing a fixed inflexible structure to the kernel, we pass a list
+ * of variable-sized tags to the kernel. The first tag must be a ATAG_CORE
+ * tag for the list to be recognised (to distinguish the tagged list from
+ * a param_struct). The list is terminated with a zero-length tag (this tag
+ * is not parsed in any way).
+ */
+static int __init parse_tag_core(const struct tag *tag)
+{
+ if (tag->hdr.size > 2) {
+ if ((tag->u.core.flags & 1) == 0)
+ root_mountflags &= ~MS_RDONLY;
+ ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
+ }
+ return 0;
+}
+
+__tagtable(ATAG_CORE, parse_tag_core);
+
+static int __init parse_tag_mem32(const struct tag *tag)
+{
+ if (meminfo.nr_banks >= NR_BANKS) {
+ printk(KERN_WARNING
+ "Ignoring memory bank 0x%08x size %dKB\n",
+ tag->u.mem.start, tag->u.mem.size / 1024);
+ return -EINVAL;
+ }
+ arm_add_memory(tag->u.mem.start, tag->u.mem.size);
+ return 0;
+}
+
+__tagtable(ATAG_MEM, parse_tag_mem32);
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+struct screen_info screen_info = {
+ .orig_video_lines = 30,
+ .orig_video_cols = 80,
+ .orig_video_mode = 0,
+ .orig_video_ega_bx = 0,
+ .orig_video_isVGA = 1,
+ .orig_video_points = 8
+};
+
+static int __init parse_tag_videotext(const struct tag *tag)
+{
+ screen_info.orig_x = tag->u.videotext.x;
+ screen_info.orig_y = tag->u.videotext.y;
+ screen_info.orig_video_page = tag->u.videotext.video_page;
+ screen_info.orig_video_mode = tag->u.videotext.video_mode;
+ screen_info.orig_video_cols = tag->u.videotext.video_cols;
+ screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx;
+ screen_info.orig_video_lines = tag->u.videotext.video_lines;
+ screen_info.orig_video_isVGA = tag->u.videotext.video_isvga;
+ screen_info.orig_video_points = tag->u.videotext.video_points;
+ return 0;
+}
+
+__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
+#endif
+
+static int __init parse_tag_ramdisk(const struct tag *tag)
+{
+ setup_ramdisk((tag->u.ramdisk.flags & 1) == 0,
+ (tag->u.ramdisk.flags & 2) == 0,
+ tag->u.ramdisk.start, tag->u.ramdisk.size);
+ return 0;
+}
+
+__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
+
+static int __init parse_tag_serialnr(const struct tag *tag)
+{
+ system_serial_low = tag->u.serialnr.low;
+ system_serial_high = tag->u.serialnr.high;
+ return 0;
+}
+
+__tagtable(ATAG_SERIAL, parse_tag_serialnr);
+
+static int __init parse_tag_revision(const struct tag *tag)
+{
+ system_rev = tag->u.revision.rev;
+ return 0;
+}
+
+__tagtable(ATAG_REVISION, parse_tag_revision);
+
+static int __init parse_tag_cmdline(const struct tag *tag)
+{
+ strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
+ return 0;
+}
+
+__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
+
+/*
+ * Scan the tag table for this tag, and call its parse function.
+ * The tag table is built by the linker from all the __tagtable
+ * declarations.
+ */
+static int __init parse_tag(const struct tag *tag)
+{
+ extern struct tagtable __tagtable_begin, __tagtable_end;
+ struct tagtable *t;
+
+ for (t = &__tagtable_begin; t < &__tagtable_end; t++)
+ if (tag->hdr.tag == t->tag) {
+ t->parse(tag);
+ break;
+ }
+
+ return t < &__tagtable_end;
+}
+
+/*
+ * Parse all tags in the list, checking both the global and architecture
+ * specific tag tables.
+ */
+static void __init parse_tags(const struct tag *t)
+{
+ for (; t->hdr.size; t = tag_next(t))
+ if (!parse_tag(t))
+ printk(KERN_WARNING
+ "Ignoring unrecognised tag 0x%08x\n",
+ t->hdr.tag);
+}
+
+/*
+ * This holds our defaults.
+ */
+static struct init_tags {
+ struct tag_header hdr1;
+ struct tag_core core;
+ struct tag_header hdr2;
+ struct tag_mem32 mem;
+ struct tag_header hdr3;
+} init_tags __initdata = {
+ { tag_size(tag_core), ATAG_CORE },
+ { 1, PAGE_SIZE, 0xff },
+ { tag_size(tag_mem32), ATAG_MEM },
+ { MEM_SIZE, PHYS_OFFSET },
+ { 0, ATAG_NONE }
+};
+
+static void (*init_machine)(void) __initdata;
+
+static int __init customize_machine(void)
+{
+ /* customizes platform devices, or adds new ones */
+ if (init_machine)
+ init_machine();
+ return 0;
+}
+arch_initcall(customize_machine);
+
+void __init setup_arch(char **cmdline_p)
+{
+ struct tag *tags = (struct tag *)&init_tags;
+ struct machine_desc *mdesc;
+ char *from = default_command_line;
+
+ setup_processor();
+ mdesc = setup_machine(machine_arch_type);
+ machine_name = mdesc->name;
+
+ if (mdesc->soft_reboot)
+ reboot_setup("s");
+
+ if (__atags_pointer)
+ tags = phys_to_virt(__atags_pointer);
+ else if (mdesc->boot_params)
+ tags = phys_to_virt(mdesc->boot_params);
+
+ /*
+ * If we have the old style parameters, convert them to
+ * a tag list.
+ */
+ if (tags->hdr.tag != ATAG_CORE)
+ convert_to_tag_list(tags);
+ if (tags->hdr.tag != ATAG_CORE)
+ tags = (struct tag *)&init_tags;
+
+ if (mdesc->fixup)
+ mdesc->fixup(mdesc, tags, &from, &meminfo);
+
+ if (tags->hdr.tag == ATAG_CORE) {
+ if (meminfo.nr_banks != 0)
+ squash_mem_tags(tags);
+ save_atags(tags);
+ parse_tags(tags);
+ }
+
+ init_mm.start_code = (unsigned long) &_text;
+ init_mm.end_code = (unsigned long) &_etext;
+ init_mm.end_data = (unsigned long) &_edata;
+ init_mm.brk = (unsigned long) &_end;
+
+ memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
+ boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
+ parse_cmdline(cmdline_p, from);
+ paging_init(&meminfo, mdesc);
+ request_standard_resources(&meminfo, mdesc);
+
+#ifdef CONFIG_SMP
+ smp_init_cpus();
+#endif
+
+ cpu_init();
+
+ /*
+ * Set up various architecture-specific pointers
+ */
+ init_arch_irq = mdesc->init_irq;
+ system_timer = mdesc->timer;
+ init_machine = mdesc->init_machine;
+
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
+ conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+#endif
+#endif
+ early_trap_init();
+}
+
+
+static int __init topology_init(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct cpuinfo_arm *cpuinfo = &per_cpu(cpu_data, cpu);
+ cpuinfo->cpu.hotpluggable = 1;
+ register_cpu(&cpuinfo->cpu, cpu);
+ }
+
+ return 0;
+}
+
+subsys_initcall(topology_init);
+
+static const char *hwcap_str[] = {
+ "swp",
+ "half",
+ "thumb",
+ "26bit",
+ "fastmult",
+ "fpa",
+ "vfp",
+ "edsp",
+ "java",
+ "iwmmxt",
+ "crunch",
+ NULL
+};
+
+static int c_show(struct seq_file *m, void *v)
+{
+ int i;
+
+ seq_printf(m, "Processor\t: %s rev %d (%s)\n",
+ cpu_name, read_cpuid_id() & 15, elf_platform);
+
+#if defined(CONFIG_SMP)
+ for_each_online_cpu(i) {
+ /*
+ * glibc reads /proc/cpuinfo to determine the number of
+ * online processors, looking for lines beginning with
+ * "processor". Give glibc what it expects.
+ */
+ seq_printf(m, "processor\t: %d\n", i);
+ seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n",
+ per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),
+ (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);
+ }
+#else /* CONFIG_SMP */
+ seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
+ loops_per_jiffy / (500000/HZ),
+ (loops_per_jiffy / (5000/HZ)) % 100);
+#endif
+
+ /* dump out the processor features */
+ seq_puts(m, "Features\t: ");
+
+ for (i = 0; hwcap_str[i]; i++)
+ if (elf_hwcap & (1 << i))
+ seq_printf(m, "%s ", hwcap_str[i]);
+
+ seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24);
+ seq_printf(m, "CPU architecture: %s\n", proc_arch[cpu_architecture()]);
+
+ if ((read_cpuid_id() & 0x0008f000) == 0x00000000) {
+ /* pre-ARM7 */
+ seq_printf(m, "CPU part\t: %07x\n", read_cpuid_id() >> 4);
+ } else {
+ if ((read_cpuid_id() & 0x0008f000) == 0x00007000) {
+ /* ARM7 */
+ seq_printf(m, "CPU variant\t: 0x%02x\n",
+ (read_cpuid_id() >> 16) & 127);
+ } else {
+ /* post-ARM7 */
+ seq_printf(m, "CPU variant\t: 0x%x\n",
+ (read_cpuid_id() >> 20) & 15);
+ }
+ seq_printf(m, "CPU part\t: 0x%03x\n",
+ (read_cpuid_id() >> 4) & 0xfff);
+ }
+ seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15);
+
+ seq_puts(m, "\n");
+
+ seq_printf(m, "Hardware\t: %s\n", machine_name);
+ seq_printf(m, "Revision\t: %04x\n", system_rev);
+ seq_printf(m, "Serial\t\t: %08x%08x\n",
+ system_serial_high, system_serial_low);
+
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_show
+};
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
new file mode 100644
index 0000000..80b8b5c
--- /dev/null
+++ b/arch/arm/kernel/signal.c
@@ -0,0 +1,706 @@
+/*
+ * linux/arch/arm/kernel/signal.c
+ *
+ * Copyright (C) 1995-2002 Russell King
+ *
+ * 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/errno.h>
+#include <linux/signal.h>
+#include <linux/personality.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+
+#include <asm/elf.h>
+#include <asm/cacheflush.h>
+#include <asm/ucontext.h>
+#include <asm/unistd.h>
+
+#include "ptrace.h"
+#include "signal.h"
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+/*
+ * For ARM syscalls, we encode the syscall number into the instruction.
+ */
+#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE))
+#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE))
+
+/*
+ * With EABI, the syscall number has to be loaded into r7.
+ */
+#define MOV_R7_NR_SIGRETURN (0xe3a07000 | (__NR_sigreturn - __NR_SYSCALL_BASE))
+#define MOV_R7_NR_RT_SIGRETURN (0xe3a07000 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
+
+/*
+ * For Thumb syscalls, we pass the syscall number via r7. We therefore
+ * need two 16-bit instructions.
+ */
+#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE))
+#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
+
+const unsigned long sigreturn_codes[7] = {
+ MOV_R7_NR_SIGRETURN, SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN,
+ MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
+};
+
+static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
+
+/*
+ * atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs)
+{
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ regs->ARM_r0 = -EINTR;
+
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(&saveset, regs, 0))
+ return regs->ARM_r0;
+ }
+}
+
+asmlinkage int
+sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs)
+{
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ regs->ARM_r0 = -EINTR;
+
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(&saveset, regs, 0))
+ return regs->ARM_r0;
+ }
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction __user *act,
+ struct old_sigaction __user *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+ return -EFAULT;
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+ return -EFAULT;
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_CRUNCH
+static int preserve_crunch_context(struct crunch_sigframe *frame)
+{
+ char kbuf[sizeof(*frame) + 8];
+ struct crunch_sigframe *kframe;
+
+ /* the crunch context must be 64 bit aligned */
+ kframe = (struct crunch_sigframe *)((unsigned long)(kbuf + 8) & ~7);
+ kframe->magic = CRUNCH_MAGIC;
+ kframe->size = CRUNCH_STORAGE_SIZE;
+ crunch_task_copy(current_thread_info(), &kframe->storage);
+ return __copy_to_user(frame, kframe, sizeof(*frame));
+}
+
+static int restore_crunch_context(struct crunch_sigframe *frame)
+{
+ char kbuf[sizeof(*frame) + 8];
+ struct crunch_sigframe *kframe;
+
+ /* the crunch context must be 64 bit aligned */
+ kframe = (struct crunch_sigframe *)((unsigned long)(kbuf + 8) & ~7);
+ if (__copy_from_user(kframe, frame, sizeof(*frame)))
+ return -1;
+ if (kframe->magic != CRUNCH_MAGIC ||
+ kframe->size != CRUNCH_STORAGE_SIZE)
+ return -1;
+ crunch_task_restore(current_thread_info(), &kframe->storage);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_IWMMXT
+
+static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame)
+{
+ char kbuf[sizeof(*frame) + 8];
+ struct iwmmxt_sigframe *kframe;
+
+ /* the iWMMXt context must be 64 bit aligned */
+ kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
+ kframe->magic = IWMMXT_MAGIC;
+ kframe->size = IWMMXT_STORAGE_SIZE;
+ iwmmxt_task_copy(current_thread_info(), &kframe->storage);
+ return __copy_to_user(frame, kframe, sizeof(*frame));
+}
+
+static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
+{
+ char kbuf[sizeof(*frame) + 8];
+ struct iwmmxt_sigframe *kframe;
+
+ /* the iWMMXt context must be 64 bit aligned */
+ kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
+ if (__copy_from_user(kframe, frame, sizeof(*frame)))
+ return -1;
+ if (kframe->magic != IWMMXT_MAGIC ||
+ kframe->size != IWMMXT_STORAGE_SIZE)
+ return -1;
+ iwmmxt_task_restore(current_thread_info(), &kframe->storage);
+ return 0;
+}
+
+#endif
+
+/*
+ * Do a signal return; undo the signal stack. These are aligned to 64-bit.
+ */
+struct sigframe {
+ struct ucontext uc;
+ unsigned long retcode[2];
+};
+
+struct rt_sigframe {
+ struct siginfo info;
+ struct sigframe sig;
+};
+
+static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
+{
+ struct aux_sigframe __user *aux;
+ sigset_t set;
+ int err;
+
+ err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
+ if (err == 0) {
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+
+ __get_user_error(regs->ARM_r0, &sf->uc.uc_mcontext.arm_r0, err);
+ __get_user_error(regs->ARM_r1, &sf->uc.uc_mcontext.arm_r1, err);
+ __get_user_error(regs->ARM_r2, &sf->uc.uc_mcontext.arm_r2, err);
+ __get_user_error(regs->ARM_r3, &sf->uc.uc_mcontext.arm_r3, err);
+ __get_user_error(regs->ARM_r4, &sf->uc.uc_mcontext.arm_r4, err);
+ __get_user_error(regs->ARM_r5, &sf->uc.uc_mcontext.arm_r5, err);
+ __get_user_error(regs->ARM_r6, &sf->uc.uc_mcontext.arm_r6, err);
+ __get_user_error(regs->ARM_r7, &sf->uc.uc_mcontext.arm_r7, err);
+ __get_user_error(regs->ARM_r8, &sf->uc.uc_mcontext.arm_r8, err);
+ __get_user_error(regs->ARM_r9, &sf->uc.uc_mcontext.arm_r9, err);
+ __get_user_error(regs->ARM_r10, &sf->uc.uc_mcontext.arm_r10, err);
+ __get_user_error(regs->ARM_fp, &sf->uc.uc_mcontext.arm_fp, err);
+ __get_user_error(regs->ARM_ip, &sf->uc.uc_mcontext.arm_ip, err);
+ __get_user_error(regs->ARM_sp, &sf->uc.uc_mcontext.arm_sp, err);
+ __get_user_error(regs->ARM_lr, &sf->uc.uc_mcontext.arm_lr, err);
+ __get_user_error(regs->ARM_pc, &sf->uc.uc_mcontext.arm_pc, err);
+ __get_user_error(regs->ARM_cpsr, &sf->uc.uc_mcontext.arm_cpsr, err);
+
+ err |= !valid_user_regs(regs);
+
+ aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
+#ifdef CONFIG_CRUNCH
+ if (err == 0)
+ err |= restore_crunch_context(&aux->crunch);
+#endif
+#ifdef CONFIG_IWMMXT
+ if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
+ err |= restore_iwmmxt_context(&aux->iwmmxt);
+#endif
+#ifdef CONFIG_VFP
+// if (err == 0)
+// err |= vfp_restore_state(&sf->aux.vfp);
+#endif
+
+ return err;
+}
+
+asmlinkage int sys_sigreturn(struct pt_regs *regs)
+{
+ struct sigframe __user *frame;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (regs->ARM_sp & 7)
+ goto badframe;
+
+ frame = (struct sigframe __user *)regs->ARM_sp;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+
+ if (restore_sigframe(regs, frame))
+ goto badframe;
+
+ single_step_trap(current);
+
+ return regs->ARM_r0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (regs->ARM_sp & 7)
+ goto badframe;
+
+ frame = (struct rt_sigframe __user *)regs->ARM_sp;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+
+ if (restore_sigframe(regs, &frame->sig))
+ goto badframe;
+
+ if (do_sigaltstack(&frame->sig.uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT)
+ goto badframe;
+
+ single_step_trap(current);
+
+ return regs->ARM_r0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+static int
+setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set)
+{
+ struct aux_sigframe __user *aux;
+ int err = 0;
+
+ __put_user_error(regs->ARM_r0, &sf->uc.uc_mcontext.arm_r0, err);
+ __put_user_error(regs->ARM_r1, &sf->uc.uc_mcontext.arm_r1, err);
+ __put_user_error(regs->ARM_r2, &sf->uc.uc_mcontext.arm_r2, err);
+ __put_user_error(regs->ARM_r3, &sf->uc.uc_mcontext.arm_r3, err);
+ __put_user_error(regs->ARM_r4, &sf->uc.uc_mcontext.arm_r4, err);
+ __put_user_error(regs->ARM_r5, &sf->uc.uc_mcontext.arm_r5, err);
+ __put_user_error(regs->ARM_r6, &sf->uc.uc_mcontext.arm_r6, err);
+ __put_user_error(regs->ARM_r7, &sf->uc.uc_mcontext.arm_r7, err);
+ __put_user_error(regs->ARM_r8, &sf->uc.uc_mcontext.arm_r8, err);
+ __put_user_error(regs->ARM_r9, &sf->uc.uc_mcontext.arm_r9, err);
+ __put_user_error(regs->ARM_r10, &sf->uc.uc_mcontext.arm_r10, err);
+ __put_user_error(regs->ARM_fp, &sf->uc.uc_mcontext.arm_fp, err);
+ __put_user_error(regs->ARM_ip, &sf->uc.uc_mcontext.arm_ip, err);
+ __put_user_error(regs->ARM_sp, &sf->uc.uc_mcontext.arm_sp, err);
+ __put_user_error(regs->ARM_lr, &sf->uc.uc_mcontext.arm_lr, err);
+ __put_user_error(regs->ARM_pc, &sf->uc.uc_mcontext.arm_pc, err);
+ __put_user_error(regs->ARM_cpsr, &sf->uc.uc_mcontext.arm_cpsr, err);
+
+ __put_user_error(current->thread.trap_no, &sf->uc.uc_mcontext.trap_no, err);
+ __put_user_error(current->thread.error_code, &sf->uc.uc_mcontext.error_code, err);
+ __put_user_error(current->thread.address, &sf->uc.uc_mcontext.fault_address, err);
+ __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
+
+ err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
+
+ aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
+#ifdef CONFIG_CRUNCH
+ if (err == 0)
+ err |= preserve_crunch_context(&aux->crunch);
+#endif
+#ifdef CONFIG_IWMMXT
+ if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
+ err |= preserve_iwmmxt_context(&aux->iwmmxt);
+#endif
+#ifdef CONFIG_VFP
+// if (err == 0)
+// err |= vfp_save_state(&sf->aux.vfp);
+#endif
+ __put_user_error(0, &aux->end_magic, err);
+
+ return err;
+}
+
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
+{
+ unsigned long sp = regs->ARM_sp;
+ void __user *frame;
+
+ /*
+ * This is the X/Open sanctioned signal stack switching.
+ */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ /*
+ * ATPCS B01 mandates 8-byte alignment
+ */
+ frame = (void __user *)((sp - framesize) & ~7);
+
+ /*
+ * Check that we can actually write to the signal frame.
+ */
+ if (!access_ok(VERIFY_WRITE, frame, framesize))
+ frame = NULL;
+
+ return frame;
+}
+
+static int
+setup_return(struct pt_regs *regs, struct k_sigaction *ka,
+ unsigned long __user *rc, void __user *frame, int usig)
+{
+ unsigned long handler = (unsigned long)ka->sa.sa_handler;
+ unsigned long retcode;
+ int thumb = 0;
+ unsigned long cpsr = regs->ARM_cpsr & ~PSR_f;
+
+ /*
+ * Maybe we need to deliver a 32-bit signal to a 26-bit task.
+ */
+ if (ka->sa.sa_flags & SA_THIRTYTWO)
+ cpsr = (cpsr & ~MODE_MASK) | USR_MODE;
+
+#ifdef CONFIG_ARM_THUMB
+ if (elf_hwcap & HWCAP_THUMB) {
+ /*
+ * The LSB of the handler determines if we're going to
+ * be using THUMB or ARM mode for this signal handler.
+ */
+ thumb = handler & 1;
+
+ if (thumb)
+ cpsr |= PSR_T_BIT;
+ else
+ cpsr &= ~PSR_T_BIT;
+ }
+#endif
+
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ retcode = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ unsigned int idx = thumb << 1;
+
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ idx += 3;
+
+ if (__put_user(sigreturn_codes[idx], rc) ||
+ __put_user(sigreturn_codes[idx+1], rc+1))
+ return 1;
+
+ if (cpsr & MODE32_BIT) {
+ /*
+ * 32-bit code can use the new high-page
+ * signal return code support.
+ */
+ retcode = KERN_SIGRETURN_CODE + (idx << 2) + thumb;
+ } else {
+ /*
+ * Ensure that the instruction cache sees
+ * the return code written onto the stack.
+ */
+ flush_icache_range((unsigned long)rc,
+ (unsigned long)(rc + 2));
+
+ retcode = ((unsigned long)rc) + thumb;
+ }
+ }
+
+ regs->ARM_r0 = usig;
+ regs->ARM_sp = (unsigned long)frame;
+ regs->ARM_lr = retcode;
+ regs->ARM_pc = handler;
+ regs->ARM_cpsr = cpsr;
+
+ return 0;
+}
+
+static int
+setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs)
+{
+ struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
+ int err = 0;
+
+ if (!frame)
+ return 1;
+
+ /*
+ * Set uc.uc_flags to a value which sc.trap_no would never have.
+ */
+ __put_user_error(0x5ac3c35a, &frame->uc.uc_flags, err);
+
+ err |= setup_sigframe(frame, regs, set);
+ if (err == 0)
+ err = setup_return(regs, ka, frame->retcode, frame, usig);
+
+ return err;
+}
+
+static int
+setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
+ stack_t stack;
+ int err = 0;
+
+ if (!frame)
+ return 1;
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ __put_user_error(0, &frame->sig.uc.uc_flags, err);
+ __put_user_error(NULL, &frame->sig.uc.uc_link, err);
+
+ memset(&stack, 0, sizeof(stack));
+ stack.ss_sp = (void __user *)current->sas_ss_sp;
+ stack.ss_flags = sas_ss_flags(regs->ARM_sp);
+ stack.ss_size = current->sas_ss_size;
+ err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack));
+
+ err |= setup_sigframe(&frame->sig, regs, set);
+ if (err == 0)
+ err = setup_return(regs, ka, frame->sig.retcode, frame, usig);
+
+ if (err == 0) {
+ /*
+ * For realtime signals we must also set the second and third
+ * arguments for the signal handler.
+ * -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
+ */
+ regs->ARM_r1 = (unsigned long)&frame->info;
+ regs->ARM_r2 = (unsigned long)&frame->sig.uc;
+ }
+
+ return err;
+}
+
+static inline void restart_syscall(struct pt_regs *regs)
+{
+ regs->ARM_r0 = regs->ARM_ORIG_r0;
+ regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static void
+handle_signal(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset,
+ struct pt_regs * regs, int syscall)
+{
+ struct thread_info *thread = current_thread_info();
+ struct task_struct *tsk = current;
+ int usig = sig;
+ int ret;
+
+ /*
+ * If we were from a system call, check for system call restarting...
+ */
+ if (syscall) {
+ switch (regs->ARM_r0) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ regs->ARM_r0 = -EINTR;
+ break;
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->ARM_r0 = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ restart_syscall(regs);
+ }
+ }
+
+ /*
+ * translate the signal
+ */
+ if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap)
+ usig = thread->exec_domain->signal_invmap[usig];
+
+ /*
+ * Set up the stack frame
+ */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = setup_rt_frame(usig, ka, info, oldset, regs);
+ else
+ ret = setup_frame(usig, ka, oldset, regs);
+
+ /*
+ * Check that the resulting registers are actually sane.
+ */
+ ret |= !valid_user_regs(regs);
+
+ if (ret != 0) {
+ force_sigsegv(sig, tsk);
+ return;
+ }
+
+ /*
+ * Block the signal if we were successful.
+ */
+ spin_lock_irq(&tsk->sighand->siglock);
+ sigorsets(&tsk->blocked, &tsk->blocked,
+ &ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(&tsk->blocked, sig);
+ recalc_sigpending();
+ spin_unlock_irq(&tsk->sighand->siglock);
+
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
+{
+ struct k_sigaction ka;
+ siginfo_t info;
+ int signr;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 0;
+
+ if (try_to_freeze())
+ goto no_signal;
+
+ single_step_clear(current);
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ handle_signal(signr, &ka, &info, oldset, regs, syscall);
+ single_step_set(current);
+ return 1;
+ }
+
+ no_signal:
+ /*
+ * No signal to deliver to the process - restart the syscall.
+ */
+ if (syscall) {
+ if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
+ if (thumb_mode(regs)) {
+ regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
+ regs->ARM_pc -= 2;
+ } else {
+#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT)
+ regs->ARM_r7 = __NR_restart_syscall;
+ regs->ARM_pc -= 4;
+#else
+ u32 __user *usp;
+ u32 swival = __NR_restart_syscall;
+
+ regs->ARM_sp -= 12;
+ usp = (u32 __user *)regs->ARM_sp;
+
+ /*
+ * Either we supports OABI only, or we have
+ * EABI with the OABI compat layer enabled.
+ * In the later case we don't know if user
+ * space is EABI or not, and if not we must
+ * not clobber r7. Always using the OABI
+ * syscall solves that issue and works for
+ * all those cases.
+ */
+ swival = swival - __NR_SYSCALL_BASE + __NR_OABI_SYSCALL_BASE;
+
+ put_user(regs->ARM_pc, &usp[0]);
+ /* swi __NR_restart_syscall */
+ put_user(0xef000000 | swival, &usp[1]);
+ /* ldr pc, [sp], #12 */
+ put_user(0xe49df00c, &usp[2]);
+
+ flush_icache_range((unsigned long)usp,
+ (unsigned long)(usp + 3));
+
+ regs->ARM_pc = regs->ARM_sp + 4;
+#endif
+ }
+ }
+ if (regs->ARM_r0 == -ERESTARTNOHAND ||
+ regs->ARM_r0 == -ERESTARTSYS ||
+ regs->ARM_r0 == -ERESTARTNOINTR) {
+ restart_syscall(regs);
+ }
+ }
+ single_step_set(current);
+ return 0;
+}
+
+asmlinkage void
+do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
+{
+ if (thread_flags & _TIF_SIGPENDING)
+ do_signal(&current->blocked, regs, syscall);
+}
diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h
new file mode 100644
index 0000000..27beece
--- /dev/null
+++ b/arch/arm/kernel/signal.h
@@ -0,0 +1,12 @@
+/*
+ * linux/arch/arm/kernel/signal.h
+ *
+ * Copyright (C) 2005 Russell King.
+ *
+ * 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.
+ */
+#define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500)
+
+extern const unsigned long sigreturn_codes[7];
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
new file mode 100644
index 0000000..e42a749
--- /dev/null
+++ b/arch/arm/kernel/smp.c
@@ -0,0 +1,659 @@
+/*
+ * linux/arch/arm/kernel/smp.c
+ *
+ * Copyright (C) 2002 ARM Limited, All Rights Reserved.
+ *
+ * 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/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/cache.h>
+#include <linux/profile.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/cacheflush.h>
+#include <asm/cpu.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/processor.h>
+#include <asm/tlbflush.h>
+#include <asm/ptrace.h>
+
+/*
+ * bitmask of present and online CPUs.
+ * The present bitmask indicates that the CPU is physically present.
+ * The online bitmask indicates that the CPU is up and running.
+ */
+cpumask_t cpu_possible_map;
+EXPORT_SYMBOL(cpu_possible_map);
+cpumask_t cpu_online_map;
+EXPORT_SYMBOL(cpu_online_map);
+
+/*
+ * as from 2.5, kernels no longer have an init_tasks structure
+ * so we need some other way of telling a new secondary core
+ * where to place its SVC stack
+ */
+struct secondary_data secondary_data;
+
+/*
+ * structures for inter-processor calls
+ * - A collection of single bit ipi messages.
+ */
+struct ipi_data {
+ spinlock_t lock;
+ unsigned long ipi_count;
+ unsigned long bits;
+};
+
+static DEFINE_PER_CPU(struct ipi_data, ipi_data) = {
+ .lock = SPIN_LOCK_UNLOCKED,
+};
+
+enum ipi_msg_type {
+ IPI_TIMER,
+ IPI_RESCHEDULE,
+ IPI_CALL_FUNC,
+ IPI_CALL_FUNC_SINGLE,
+ IPI_CPU_STOP,
+};
+
+int __cpuinit __cpu_up(unsigned int cpu)
+{
+ struct cpuinfo_arm *ci = &per_cpu(cpu_data, cpu);
+ struct task_struct *idle = ci->idle;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ int ret;
+
+ /*
+ * Spawn a new process manually, if not already done.
+ * Grab a pointer to its task struct so we can mess with it
+ */
+ if (!idle) {
+ idle = fork_idle(cpu);
+ if (IS_ERR(idle)) {
+ printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
+ return PTR_ERR(idle);
+ }
+ ci->idle = idle;
+ }
+
+ /*
+ * Allocate initial page tables to allow the new CPU to
+ * enable the MMU safely. This essentially means a set
+ * of our "standard" page tables, with the addition of
+ * a 1:1 mapping for the physical address of the kernel.
+ */
+ pgd = pgd_alloc(&init_mm);
+ pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET);
+ *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) |
+ PMD_TYPE_SECT | PMD_SECT_AP_WRITE);
+
+ /*
+ * We need to tell the secondary core where to find
+ * its stack and the page tables.
+ */
+ secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
+ secondary_data.pgdir = virt_to_phys(pgd);
+ wmb();
+
+ /*
+ * Now bring the CPU into our world.
+ */
+ ret = boot_secondary(cpu, idle);
+ if (ret == 0) {
+ unsigned long timeout;
+
+ /*
+ * CPU was successfully started, wait for it
+ * to come online or time out.
+ */
+ timeout = jiffies + HZ;
+ while (time_before(jiffies, timeout)) {
+ if (cpu_online(cpu))
+ break;
+
+ udelay(10);
+ barrier();
+ }
+
+ if (!cpu_online(cpu))
+ ret = -EIO;
+ }
+
+ secondary_data.stack = NULL;
+ secondary_data.pgdir = 0;
+
+ *pmd = __pmd(0);
+ pgd_free(&init_mm, pgd);
+
+ if (ret) {
+ printk(KERN_CRIT "CPU%u: processor failed to boot\n", cpu);
+
+ /*
+ * FIXME: We need to clean up the new idle thread. --rmk
+ */
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ * __cpu_disable runs on the processor to be shutdown.
+ */
+int __cpuexit __cpu_disable(void)
+{
+ unsigned int cpu = smp_processor_id();
+ struct task_struct *p;
+ int ret;
+
+ ret = mach_cpu_disable(cpu);
+ if (ret)
+ return ret;
+
+ /*
+ * Take this CPU offline. Once we clear this, we can't return,
+ * and we must not schedule until we're ready to give up the cpu.
+ */
+ cpu_clear(cpu, cpu_online_map);
+
+ /*
+ * OK - migrate IRQs away from this CPU
+ */
+ migrate_irqs();
+
+ /*
+ * Stop the local timer for this CPU.
+ */
+ local_timer_stop(cpu);
+
+ /*
+ * Flush user cache and TLB mappings, and then remove this CPU
+ * from the vm mask set of all processes.
+ */
+ flush_cache_all();
+ local_flush_tlb_all();
+
+ read_lock(&tasklist_lock);
+ for_each_process(p) {
+ if (p->mm)
+ cpu_clear(cpu, p->mm->cpu_vm_mask);
+ }
+ read_unlock(&tasklist_lock);
+
+ return 0;
+}
+
+/*
+ * called on the thread which is asking for a CPU to be shutdown -
+ * waits until shutdown has completed, or it is timed out.
+ */
+void __cpuexit __cpu_die(unsigned int cpu)
+{
+ if (!platform_cpu_kill(cpu))
+ printk("CPU%u: unable to kill\n", cpu);
+}
+
+/*
+ * Called from the idle thread for the CPU which has been shutdown.
+ *
+ * Note that we disable IRQs here, but do not re-enable them
+ * before returning to the caller. This is also the behaviour
+ * of the other hotplug-cpu capable cores, so presumably coming
+ * out of idle fixes this.
+ */
+void __cpuexit cpu_die(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ local_irq_disable();
+ idle_task_exit();
+
+ /*
+ * actual CPU shutdown procedure is at least platform (if not
+ * CPU) specific
+ */
+ platform_cpu_die(cpu);
+
+ /*
+ * Do not return to the idle loop - jump back to the secondary
+ * cpu initialisation. There's some initialisation which needs
+ * to be repeated to undo the effects of taking the CPU offline.
+ */
+ __asm__("mov sp, %0\n"
+ " b secondary_start_kernel"
+ :
+ : "r" (task_stack_page(current) + THREAD_SIZE - 8));
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/*
+ * This is the secondary CPU boot entry. We're using this CPUs
+ * idle thread stack, but a set of temporary page tables.
+ */
+asmlinkage void __cpuinit secondary_start_kernel(void)
+{
+ struct mm_struct *mm = &init_mm;
+ unsigned int cpu = smp_processor_id();
+
+ printk("CPU%u: Booted secondary processor\n", cpu);
+
+ /*
+ * All kernel threads share the same mm context; grab a
+ * reference and switch to it.
+ */
+ atomic_inc(&mm->mm_users);
+ atomic_inc(&mm->mm_count);
+ current->active_mm = mm;
+ cpu_set(cpu, mm->cpu_vm_mask);
+ cpu_switch_mm(mm->pgd, mm);
+ enter_lazy_tlb(mm, current);
+ local_flush_tlb_all();
+
+ cpu_init();
+ preempt_disable();
+
+ /*
+ * Give the platform a chance to do its own initialisation.
+ */
+ platform_secondary_init(cpu);
+
+ /*
+ * Enable local interrupts.
+ */
+ notify_cpu_starting(cpu);
+ local_irq_enable();
+ local_fiq_enable();
+
+ /*
+ * Setup local timer for this CPU.
+ */
+ local_timer_setup(cpu);
+
+ calibrate_delay();
+
+ smp_store_cpu_info(cpu);
+
+ /*
+ * OK, now it's safe to let the boot CPU continue
+ */
+ cpu_set(cpu, cpu_online_map);
+
+ /*
+ * OK, it's off to the idle thread for us
+ */
+ cpu_idle();
+}
+
+/*
+ * Called by both boot and secondaries to move global data into
+ * per-processor storage.
+ */
+void __cpuinit smp_store_cpu_info(unsigned int cpuid)
+{
+ struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpuid);
+
+ cpu_info->loops_per_jiffy = loops_per_jiffy;
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+ int cpu;
+ unsigned long bogosum = 0;
+
+ for_each_online_cpu(cpu)
+ bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy;
+
+ printk(KERN_INFO "SMP: Total of %d processors activated "
+ "(%lu.%02lu BogoMIPS).\n",
+ num_online_cpus(),
+ bogosum / (500000/HZ),
+ (bogosum / (5000/HZ)) % 100);
+}
+
+void __init smp_prepare_boot_cpu(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ per_cpu(cpu_data, cpu).idle = current;
+}
+
+static void send_ipi_message(cpumask_t callmap, enum ipi_msg_type msg)
+{
+ unsigned long flags;
+ unsigned int cpu;
+
+ local_irq_save(flags);
+
+ for_each_cpu_mask(cpu, callmap) {
+ struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+
+ spin_lock(&ipi->lock);
+ ipi->bits |= 1 << msg;
+ spin_unlock(&ipi->lock);
+ }
+
+ /*
+ * Call the platform specific cross-CPU call function.
+ */
+ smp_cross_call(callmap);
+
+ local_irq_restore(flags);
+}
+
+void arch_send_call_function_ipi(cpumask_t mask)
+{
+ send_ipi_message(mask, IPI_CALL_FUNC);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ send_ipi_message(cpumask_of_cpu(cpu), IPI_CALL_FUNC_SINGLE);
+}
+
+void show_ipi_list(struct seq_file *p)
+{
+ unsigned int cpu;
+
+ seq_puts(p, "IPI:");
+
+ for_each_present_cpu(cpu)
+ seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count);
+
+ seq_putc(p, '\n');
+}
+
+void show_local_irqs(struct seq_file *p)
+{
+ unsigned int cpu;
+
+ seq_printf(p, "LOC: ");
+
+ for_each_present_cpu(cpu)
+ seq_printf(p, "%10u ", irq_stat[cpu].local_timer_irqs);
+
+ seq_putc(p, '\n');
+}
+
+static void ipi_timer(void)
+{
+ irq_enter();
+ local_timer_interrupt();
+ irq_exit();
+}
+
+#ifdef CONFIG_LOCAL_TIMERS
+asmlinkage void __exception do_local_timer(struct pt_regs *regs)
+{
+ struct pt_regs *old_regs = set_irq_regs(regs);
+ int cpu = smp_processor_id();
+
+ if (local_timer_ack()) {
+ irq_stat[cpu].local_timer_irqs++;
+ ipi_timer();
+ }
+
+ set_irq_regs(old_regs);
+}
+#endif
+
+static DEFINE_SPINLOCK(stop_lock);
+
+/*
+ * ipi_cpu_stop - handle IPI from smp_send_stop()
+ */
+static void ipi_cpu_stop(unsigned int cpu)
+{
+ spin_lock(&stop_lock);
+ printk(KERN_CRIT "CPU%u: stopping\n", cpu);
+ dump_stack();
+ spin_unlock(&stop_lock);
+
+ cpu_clear(cpu, cpu_online_map);
+
+ local_fiq_disable();
+ local_irq_disable();
+
+ while (1)
+ cpu_relax();
+}
+
+/*
+ * Main handler for inter-processor interrupts
+ *
+ * For ARM, the ipimask now only identifies a single
+ * category of IPI (Bit 1 IPIs have been replaced by a
+ * different mechanism):
+ *
+ * Bit 0 - Inter-processor function call
+ */
+asmlinkage void __exception do_IPI(struct pt_regs *regs)
+{
+ unsigned int cpu = smp_processor_id();
+ struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ ipi->ipi_count++;
+
+ for (;;) {
+ unsigned long msgs;
+
+ spin_lock(&ipi->lock);
+ msgs = ipi->bits;
+ ipi->bits = 0;
+ spin_unlock(&ipi->lock);
+
+ if (!msgs)
+ break;
+
+ do {
+ unsigned nextmsg;
+
+ nextmsg = msgs & -msgs;
+ msgs &= ~nextmsg;
+ nextmsg = ffz(~nextmsg);
+
+ switch (nextmsg) {
+ case IPI_TIMER:
+ ipi_timer();
+ break;
+
+ case IPI_RESCHEDULE:
+ /*
+ * nothing more to do - eveything is
+ * done on the interrupt return path
+ */
+ break;
+
+ case IPI_CALL_FUNC:
+ generic_smp_call_function_interrupt();
+ break;
+
+ case IPI_CALL_FUNC_SINGLE:
+ generic_smp_call_function_single_interrupt();
+ break;
+
+ case IPI_CPU_STOP:
+ ipi_cpu_stop(cpu);
+ break;
+
+ default:
+ printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
+ cpu, nextmsg);
+ break;
+ }
+ } while (msgs);
+ }
+
+ set_irq_regs(old_regs);
+}
+
+void smp_send_reschedule(int cpu)
+{
+ send_ipi_message(cpumask_of_cpu(cpu), IPI_RESCHEDULE);
+}
+
+void smp_send_timer(void)
+{
+ cpumask_t mask = cpu_online_map;
+ cpu_clear(smp_processor_id(), mask);
+ send_ipi_message(mask, IPI_TIMER);
+}
+
+void smp_timer_broadcast(cpumask_t mask)
+{
+ send_ipi_message(mask, IPI_TIMER);
+}
+
+void smp_send_stop(void)
+{
+ cpumask_t mask = cpu_online_map;
+ cpu_clear(smp_processor_id(), mask);
+ send_ipi_message(mask, IPI_CPU_STOP);
+}
+
+/*
+ * not supported here
+ */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return -EINVAL;
+}
+
+static int
+on_each_cpu_mask(void (*func)(void *), void *info, int wait, cpumask_t mask)
+{
+ int ret = 0;
+
+ preempt_disable();
+
+ ret = smp_call_function_mask(mask, func, info, wait);
+ if (cpu_isset(smp_processor_id(), mask))
+ func(info);
+
+ preempt_enable();
+
+ return ret;
+}
+
+/**********************************************************************/
+
+/*
+ * TLB operations
+ */
+struct tlb_args {
+ struct vm_area_struct *ta_vma;
+ unsigned long ta_start;
+ unsigned long ta_end;
+};
+
+static inline void ipi_flush_tlb_all(void *ignored)
+{
+ local_flush_tlb_all();
+}
+
+static inline void ipi_flush_tlb_mm(void *arg)
+{
+ struct mm_struct *mm = (struct mm_struct *)arg;
+
+ local_flush_tlb_mm(mm);
+}
+
+static inline void ipi_flush_tlb_page(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_page(ta->ta_vma, ta->ta_start);
+}
+
+static inline void ipi_flush_tlb_kernel_page(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_kernel_page(ta->ta_start);
+}
+
+static inline void ipi_flush_tlb_range(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
+}
+
+static inline void ipi_flush_tlb_kernel_range(void *arg)
+{
+ struct tlb_args *ta = (struct tlb_args *)arg;
+
+ local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
+}
+
+void flush_tlb_all(void)
+{
+ on_each_cpu(ipi_flush_tlb_all, NULL, 1);
+}
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ cpumask_t mask = mm->cpu_vm_mask;
+
+ on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, mask);
+}
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
+{
+ cpumask_t mask = vma->vm_mm->cpu_vm_mask;
+ struct tlb_args ta;
+
+ ta.ta_vma = vma;
+ ta.ta_start = uaddr;
+
+ on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mask);
+}
+
+void flush_tlb_kernel_page(unsigned long kaddr)
+{
+ struct tlb_args ta;
+
+ ta.ta_start = kaddr;
+
+ on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1);
+}
+
+void flush_tlb_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ cpumask_t mask = vma->vm_mm->cpu_vm_mask;
+ struct tlb_args ta;
+
+ ta.ta_vma = vma;
+ ta.ta_start = start;
+ ta.ta_end = end;
+
+ on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, mask);
+}
+
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ struct tlb_args ta;
+
+ ta.ta_start = start;
+ ta.ta_end = end;
+
+ on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
+}
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
new file mode 100644
index 0000000..fc650f6
--- /dev/null
+++ b/arch/arm/kernel/stacktrace.c
@@ -0,0 +1,96 @@
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+
+#include "stacktrace.h"
+
+int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high,
+ int (*fn)(struct stackframe *, void *), void *data)
+{
+ struct stackframe *frame;
+
+ do {
+ /*
+ * Check current frame pointer is within bounds
+ */
+ if (fp < (low + 12) || fp + 4 >= high)
+ break;
+
+ frame = (struct stackframe *)(fp - 12);
+
+ if (fn(frame, data))
+ break;
+
+ /*
+ * Update the low bound - the next frame must always
+ * be at a higher address than the current frame.
+ */
+ low = fp + 4;
+ fp = frame->fp;
+ } while (fp);
+
+ return 0;
+}
+EXPORT_SYMBOL(walk_stackframe);
+
+#ifdef CONFIG_STACKTRACE
+struct stack_trace_data {
+ struct stack_trace *trace;
+ unsigned int no_sched_functions;
+ unsigned int skip;
+};
+
+static int save_trace(struct stackframe *frame, void *d)
+{
+ struct stack_trace_data *data = d;
+ struct stack_trace *trace = data->trace;
+ unsigned long addr = frame->lr;
+
+ if (data->no_sched_functions && in_sched_functions(addr))
+ return 0;
+ if (data->skip) {
+ data->skip--;
+ return 0;
+ }
+
+ trace->entries[trace->nr_entries++] = addr;
+
+ return trace->nr_entries >= trace->max_entries;
+}
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ struct stack_trace_data data;
+ unsigned long fp, base;
+
+ data.trace = trace;
+ data.skip = trace->skip;
+ base = (unsigned long)task_stack_page(tsk);
+
+ if (tsk != current) {
+#ifdef CONFIG_SMP
+ /*
+ * What guarantees do we have here that 'tsk'
+ * is not running on another CPU?
+ */
+ BUG();
+#else
+ data.no_sched_functions = 1;
+ fp = thread_saved_fp(tsk);
+#endif
+ } else {
+ data.no_sched_functions = 0;
+ asm("mov %0, fp" : "=r" (fp));
+ }
+
+ walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data);
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+
+void save_stack_trace(struct stack_trace *trace)
+{
+ save_stack_trace_tsk(current, trace);
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+#endif
diff --git a/arch/arm/kernel/stacktrace.h b/arch/arm/kernel/stacktrace.h
new file mode 100644
index 0000000..e9fd20c
--- /dev/null
+++ b/arch/arm/kernel/stacktrace.h
@@ -0,0 +1,9 @@
+struct stackframe {
+ unsigned long fp;
+ unsigned long sp;
+ unsigned long lr;
+ unsigned long pc;
+};
+
+int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high,
+ int (*fn)(struct stackframe *, void *), void *data);
diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c
new file mode 100644
index 0000000..b3ec641
--- /dev/null
+++ b/arch/arm/kernel/sys_arm.c
@@ -0,0 +1,312 @@
+/*
+ * linux/arch/arm/kernel/sys_arm.c
+ *
+ * Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
+ * Copyright (C) 1995, 1996 Russell King.
+ *
+ * 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 file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/arm
+ * platform.
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/ipc.h>
+#include <linux/uaccess.h>
+
+extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
+ unsigned long new_len, unsigned long flags,
+ unsigned long new_addr);
+
+/* common code for old and new mmaps */
+inline long do_mmap2(
+ unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ int error = -EINVAL;
+ struct file * file = NULL;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+ if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
+ goto out;
+
+ error = -EBADF;
+ if (!(flags & MAP_ANONYMOUS)) {
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(&current->mm->mmap_sem);
+
+ if (file)
+ fput(file);
+out:
+ return error;
+}
+
+struct mmap_arg_struct {
+ unsigned long addr;
+ unsigned long len;
+ unsigned long prot;
+ unsigned long flags;
+ unsigned long fd;
+ unsigned long offset;
+};
+
+asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
+{
+ int error = -EFAULT;
+ struct mmap_arg_struct a;
+
+ if (copy_from_user(&a, arg, sizeof(a)))
+ goto out;
+
+ error = -EINVAL;
+ if (a.offset & ~PAGE_MASK)
+ goto out;
+
+ error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+out:
+ return error;
+}
+
+asmlinkage unsigned long
+sys_arm_mremap(unsigned long addr, unsigned long old_len,
+ unsigned long new_len, unsigned long flags,
+ unsigned long new_addr)
+{
+ unsigned long ret = -EINVAL;
+
+ if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
+ goto out;
+
+ down_write(&current->mm->mmap_sem);
+ ret = do_mremap(addr, old_len, new_len, flags, new_addr);
+ up_write(&current->mm->mmap_sem);
+
+out:
+ return ret;
+}
+
+/*
+ * Perform the select(nd, in, out, ex, tv) and mmap() system
+ * calls.
+ */
+
+struct sel_arg_struct {
+ unsigned long n;
+ fd_set __user *inp, *outp, *exp;
+ struct timeval __user *tvp;
+};
+
+asmlinkage int old_select(struct sel_arg_struct __user *arg)
+{
+ struct sel_arg_struct a;
+
+ if (copy_from_user(&a, arg, sizeof(a)))
+ return -EFAULT;
+ /* sys_select() does the appropriate kernel locking */
+ return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
+}
+
+#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+asmlinkage int sys_ipc(uint call, int first, int second, int third,
+ void __user *ptr, long fifth)
+{
+ int version, ret;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ switch (call) {
+ case SEMOP:
+ return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
+ case SEMTIMEDOP:
+ return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
+ (const struct timespec __user *)fifth);
+
+ case SEMGET:
+ return sys_semget (first, second, third);
+ case SEMCTL: {
+ union semun fourth;
+ if (!ptr)
+ return -EINVAL;
+ if (get_user(fourth.__pad, (void __user * __user *) ptr))
+ return -EFAULT;
+ return sys_semctl (first, second, third, fourth);
+ }
+
+ case MSGSND:
+ return sys_msgsnd(first, (struct msgbuf __user *) ptr,
+ second, third);
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+ if (!ptr)
+ return -EINVAL;
+ if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr,
+ sizeof (tmp)))
+ return -EFAULT;
+ return sys_msgrcv (first, tmp.msgp, second,
+ tmp.msgtyp, third);
+ }
+ default:
+ return sys_msgrcv (first,
+ (struct msgbuf __user *) ptr,
+ second, fifth, third);
+ }
+ case MSGGET:
+ return sys_msgget ((key_t) first, second);
+ case MSGCTL:
+ return sys_msgctl(first, second, (struct msqid_ds __user *)ptr);
+
+ case SHMAT:
+ switch (version) {
+ default: {
+ ulong raddr;
+ ret = do_shmat(first, (char __user *)ptr, second, &raddr);
+ if (ret)
+ return ret;
+ return put_user(raddr, (ulong __user *)third);
+ }
+ case 1: /* Of course, we don't support iBCS2! */
+ return -EINVAL;
+ }
+ case SHMDT:
+ return sys_shmdt ((char __user *)ptr);
+ case SHMGET:
+ return sys_shmget (first, second, third);
+ case SHMCTL:
+ return sys_shmctl (first, second,
+ (struct shmid_ds __user *) ptr);
+ default:
+ return -ENOSYS;
+ }
+}
+#endif
+
+/* Fork a new task - this creates a new program thread.
+ * This is called indirectly via a small wrapper
+ */
+asmlinkage int sys_fork(struct pt_regs *regs)
+{
+#ifdef CONFIG_MMU
+ return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
+#else
+ /* can not support in nommu mode */
+ return(-EINVAL);
+#endif
+}
+
+/* Clone a task - this clones the calling program thread.
+ * This is called indirectly via a small wrapper
+ */
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
+ int __user *parent_tidptr, int tls_val,
+ int __user *child_tidptr, struct pt_regs *regs)
+{
+ if (!newsp)
+ newsp = regs->ARM_sp;
+
+ return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
+}
+
+asmlinkage int sys_vfork(struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
+}
+
+/* sys_execve() executes a new program.
+ * This is called indirectly via a small wrapper
+ */
+asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv,
+ char __user * __user *envp, struct pt_regs *regs)
+{
+ int error;
+ char * filename;
+
+ filename = getname(filenamei);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, argv, envp, regs);
+ putname(filename);
+out:
+ return error;
+}
+
+int kernel_execve(const char *filename, char *const argv[], char *const envp[])
+{
+ struct pt_regs regs;
+ int ret;
+
+ memset(&regs, 0, sizeof(struct pt_regs));
+ ret = do_execve((char *)filename, (char __user * __user *)argv,
+ (char __user * __user *)envp, &regs);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Save argc to the register structure for userspace.
+ */
+ regs.ARM_r0 = ret;
+
+ /*
+ * We were successful. We won't be returning to our caller, but
+ * instead to user space by manipulating the kernel stack.
+ */
+ asm( "add r0, %0, %1\n\t"
+ "mov r1, %2\n\t"
+ "mov r2, %3\n\t"
+ "bl memmove\n\t" /* copy regs to top of stack */
+ "mov r8, #0\n\t" /* not a syscall */
+ "mov r9, %0\n\t" /* thread structure */
+ "mov sp, r0\n\t" /* reposition stack pointer */
+ "b ret_to_user"
+ :
+ : "r" (current_thread_info()),
+ "Ir" (THREAD_START_SP - sizeof(regs)),
+ "r" (&regs),
+ "Ir" (sizeof(regs))
+ : "r0", "r1", "r2", "r3", "ip", "lr", "memory");
+
+ out:
+ return ret;
+}
+EXPORT_SYMBOL(kernel_execve);
+
+/*
+ * Since loff_t is a 64 bit type we avoid a lot of ABI hassle
+ * with a different argument ordering.
+ */
+asmlinkage long sys_arm_fadvise64_64(int fd, int advice,
+ loff_t offset, loff_t len)
+{
+ return sys_fadvise64_64(fd, offset, len, advice);
+}
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
new file mode 100644
index 0000000..42623db
--- /dev/null
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -0,0 +1,464 @@
+/*
+ * arch/arm/kernel/sys_oabi-compat.c
+ *
+ * Compatibility wrappers for syscalls that are used from
+ * old ABI user space binaries with an EABI kernel.
+ *
+ * Author: Nicolas Pitre
+ * Created: Oct 7, 2005
+ * Copyright: MontaVista Software, 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.
+ */
+
+/*
+ * The legacy ABI and the new ARM EABI have different rules making some
+ * syscalls incompatible especially with structure arguments.
+ * Most notably, Eabi says 64-bit members should be 64-bit aligned instead of
+ * simply word aligned. EABI also pads structures to the size of the largest
+ * member it contains instead of the invariant 32-bit.
+ *
+ * The following syscalls are affected:
+ *
+ * sys_stat64:
+ * sys_lstat64:
+ * sys_fstat64:
+ * sys_fstatat64:
+ *
+ * struct stat64 has different sizes and some members are shifted
+ * Compatibility wrappers are needed for them and provided below.
+ *
+ * sys_fcntl64:
+ *
+ * struct flock64 has different sizes and some members are shifted
+ * A compatibility wrapper is needed and provided below.
+ *
+ * sys_statfs64:
+ * sys_fstatfs64:
+ *
+ * struct statfs64 has extra padding with EABI growing its size from
+ * 84 to 88. This struct is now __attribute__((packed,aligned(4)))
+ * with a small assembly wrapper to force the sz argument to 84 if it is 88
+ * to avoid copying the extra padding over user space unexpecting it.
+ *
+ * sys_newuname:
+ *
+ * struct new_utsname has no padding with EABI. No problem there.
+ *
+ * sys_epoll_ctl:
+ * sys_epoll_wait:
+ *
+ * struct epoll_event has its second member shifted also affecting the
+ * structure size. Compatibility wrappers are needed and provided below.
+ *
+ * sys_ipc:
+ * sys_semop:
+ * sys_semtimedop:
+ *
+ * struct sembuf loses its padding with EABI. Since arrays of them are
+ * used they have to be copyed to remove the padding. Compatibility wrappers
+ * provided below.
+ *
+ * sys_bind:
+ * sys_connect:
+ * sys_sendmsg:
+ * sys_sendto:
+ * sys_socketcall:
+ *
+ * struct sockaddr_un loses its padding with EABI. Since the size of the
+ * structure is used as a validation test in unix_mkname(), we need to
+ * change the length argument to 110 whenever it is 112. Compatibility
+ * wrappers provided below.
+ */
+
+#include <linux/syscalls.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/eventpoll.h>
+#include <linux/sem.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/ipc.h>
+#include <linux/uaccess.h>
+
+struct oldabi_stat64 {
+ unsigned long long st_dev;
+ unsigned int __pad1;
+ unsigned long __st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ unsigned long st_uid;
+ unsigned long st_gid;
+
+ unsigned long long st_rdev;
+ unsigned int __pad2;
+
+ long long st_size;
+ unsigned long st_blksize;
+ unsigned long long st_blocks;
+
+ unsigned long st_atime;
+ unsigned long st_atime_nsec;
+
+ unsigned long st_mtime;
+ unsigned long st_mtime_nsec;
+
+ unsigned long st_ctime;
+ unsigned long st_ctime_nsec;
+
+ unsigned long long st_ino;
+} __attribute__ ((packed,aligned(4)));
+
+static long cp_oldabi_stat64(struct kstat *stat,
+ struct oldabi_stat64 __user *statbuf)
+{
+ struct oldabi_stat64 tmp;
+
+ tmp.st_dev = huge_encode_dev(stat->dev);
+ tmp.__pad1 = 0;
+ tmp.__st_ino = stat->ino;
+ tmp.st_mode = stat->mode;
+ tmp.st_nlink = stat->nlink;
+ tmp.st_uid = stat->uid;
+ tmp.st_gid = stat->gid;
+ tmp.st_rdev = huge_encode_dev(stat->rdev);
+ tmp.st_size = stat->size;
+ tmp.st_blocks = stat->blocks;
+ tmp.__pad2 = 0;
+ tmp.st_blksize = stat->blksize;
+ tmp.st_atime = stat->atime.tv_sec;
+ tmp.st_atime_nsec = stat->atime.tv_nsec;
+ tmp.st_mtime = stat->mtime.tv_sec;
+ tmp.st_mtime_nsec = stat->mtime.tv_nsec;
+ tmp.st_ctime = stat->ctime.tv_sec;
+ tmp.st_ctime_nsec = stat->ctime.tv_nsec;
+ tmp.st_ino = stat->ino;
+ return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
+
+asmlinkage long sys_oabi_stat64(char __user * filename,
+ struct oldabi_stat64 __user * statbuf)
+{
+ struct kstat stat;
+ int error = vfs_stat(filename, &stat);
+ if (!error)
+ error = cp_oldabi_stat64(&stat, statbuf);
+ return error;
+}
+
+asmlinkage long sys_oabi_lstat64(char __user * filename,
+ struct oldabi_stat64 __user * statbuf)
+{
+ struct kstat stat;
+ int error = vfs_lstat(filename, &stat);
+ if (!error)
+ error = cp_oldabi_stat64(&stat, statbuf);
+ return error;
+}
+
+asmlinkage long sys_oabi_fstat64(unsigned long fd,
+ struct oldabi_stat64 __user * statbuf)
+{
+ struct kstat stat;
+ int error = vfs_fstat(fd, &stat);
+ if (!error)
+ error = cp_oldabi_stat64(&stat, statbuf);
+ return error;
+}
+
+asmlinkage long sys_oabi_fstatat64(int dfd,
+ char __user *filename,
+ struct oldabi_stat64 __user *statbuf,
+ int flag)
+{
+ struct kstat stat;
+ int error = -EINVAL;
+
+ if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0)
+ goto out;
+
+ if (flag & AT_SYMLINK_NOFOLLOW)
+ error = vfs_lstat_fd(dfd, filename, &stat);
+ else
+ error = vfs_stat_fd(dfd, filename, &stat);
+
+ if (!error)
+ error = cp_oldabi_stat64(&stat, statbuf);
+
+out:
+ return error;
+}
+
+struct oabi_flock64 {
+ short l_type;
+ short l_whence;
+ loff_t l_start;
+ loff_t l_len;
+ pid_t l_pid;
+} __attribute__ ((packed,aligned(4)));
+
+asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
+ unsigned long arg)
+{
+ struct oabi_flock64 user;
+ struct flock64 kernel;
+ mm_segment_t fs = USER_DS; /* initialized to kill a warning */
+ unsigned long local_arg = arg;
+ int ret;
+
+ switch (cmd) {
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+ if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
+ sizeof(user)))
+ return -EFAULT;
+ kernel.l_type = user.l_type;
+ kernel.l_whence = user.l_whence;
+ kernel.l_start = user.l_start;
+ kernel.l_len = user.l_len;
+ kernel.l_pid = user.l_pid;
+ local_arg = (unsigned long)&kernel;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ }
+
+ ret = sys_fcntl64(fd, cmd, local_arg);
+
+ switch (cmd) {
+ case F_GETLK64:
+ if (!ret) {
+ user.l_type = kernel.l_type;
+ user.l_whence = kernel.l_whence;
+ user.l_start = kernel.l_start;
+ user.l_len = kernel.l_len;
+ user.l_pid = kernel.l_pid;
+ if (copy_to_user((struct oabi_flock64 __user *)arg,
+ &user, sizeof(user)))
+ ret = -EFAULT;
+ }
+ case F_SETLK64:
+ case F_SETLKW64:
+ set_fs(fs);
+ }
+
+ return ret;
+}
+
+struct oabi_epoll_event {
+ __u32 events;
+ __u64 data;
+} __attribute__ ((packed,aligned(4)));
+
+asmlinkage long sys_oabi_epoll_ctl(int epfd, int op, int fd,
+ struct oabi_epoll_event __user *event)
+{
+ struct oabi_epoll_event user;
+ struct epoll_event kernel;
+ mm_segment_t fs;
+ long ret;
+
+ if (op == EPOLL_CTL_DEL)
+ return sys_epoll_ctl(epfd, op, fd, NULL);
+ if (copy_from_user(&user, event, sizeof(user)))
+ return -EFAULT;
+ kernel.events = user.events;
+ kernel.data = user.data;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_epoll_ctl(epfd, op, fd, &kernel);
+ set_fs(fs);
+ return ret;
+}
+
+asmlinkage long sys_oabi_epoll_wait(int epfd,
+ struct oabi_epoll_event __user *events,
+ int maxevents, int timeout)
+{
+ struct epoll_event *kbuf;
+ mm_segment_t fs;
+ long ret, err, i;
+
+ if (maxevents <= 0 || maxevents > (INT_MAX/sizeof(struct epoll_event)))
+ return -EINVAL;
+ kbuf = kmalloc(sizeof(*kbuf) * maxevents, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_epoll_wait(epfd, kbuf, maxevents, timeout);
+ set_fs(fs);
+ err = 0;
+ for (i = 0; i < ret; i++) {
+ __put_user_error(kbuf[i].events, &events->events, err);
+ __put_user_error(kbuf[i].data, &events->data, err);
+ events++;
+ }
+ kfree(kbuf);
+ return err ? -EFAULT : ret;
+}
+
+struct oabi_sembuf {
+ unsigned short sem_num;
+ short sem_op;
+ short sem_flg;
+ unsigned short __pad;
+};
+
+asmlinkage long sys_oabi_semtimedop(int semid,
+ struct oabi_sembuf __user *tsops,
+ unsigned nsops,
+ const struct timespec __user *timeout)
+{
+ struct sembuf *sops;
+ struct timespec local_timeout;
+ long err;
+ int i;
+
+ if (nsops < 1)
+ return -EINVAL;
+ sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL);
+ if (!sops)
+ return -ENOMEM;
+ err = 0;
+ for (i = 0; i < nsops; i++) {
+ __get_user_error(sops[i].sem_num, &tsops->sem_num, err);
+ __get_user_error(sops[i].sem_op, &tsops->sem_op, err);
+ __get_user_error(sops[i].sem_flg, &tsops->sem_flg, err);
+ tsops++;
+ }
+ if (timeout) {
+ /* copy this as well before changing domain protection */
+ err |= copy_from_user(&local_timeout, timeout, sizeof(*timeout));
+ timeout = &local_timeout;
+ }
+ if (err) {
+ err = -EFAULT;
+ } else {
+ mm_segment_t fs = get_fs();
+ set_fs(KERNEL_DS);
+ err = sys_semtimedop(semid, sops, nsops, timeout);
+ set_fs(fs);
+ }
+ kfree(sops);
+ return err;
+}
+
+asmlinkage long sys_oabi_semop(int semid, struct oabi_sembuf __user *tsops,
+ unsigned nsops)
+{
+ return sys_oabi_semtimedop(semid, tsops, nsops, NULL);
+}
+
+extern asmlinkage int sys_ipc(uint call, int first, int second, int third,
+ void __user *ptr, long fifth);
+
+asmlinkage int sys_oabi_ipc(uint call, int first, int second, int third,
+ void __user *ptr, long fifth)
+{
+ switch (call & 0xffff) {
+ case SEMOP:
+ return sys_oabi_semtimedop(first,
+ (struct oabi_sembuf __user *)ptr,
+ second, NULL);
+ case SEMTIMEDOP:
+ return sys_oabi_semtimedop(first,
+ (struct oabi_sembuf __user *)ptr,
+ second,
+ (const struct timespec __user *)fifth);
+ default:
+ return sys_ipc(call, first, second, third, ptr, fifth);
+ }
+}
+
+asmlinkage long sys_oabi_bind(int fd, struct sockaddr __user *addr, int addrlen)
+{
+ sa_family_t sa_family;
+ if (addrlen == 112 &&
+ get_user(sa_family, &addr->sa_family) == 0 &&
+ sa_family == AF_UNIX)
+ addrlen = 110;
+ return sys_bind(fd, addr, addrlen);
+}
+
+asmlinkage long sys_oabi_connect(int fd, struct sockaddr __user *addr, int addrlen)
+{
+ sa_family_t sa_family;
+ if (addrlen == 112 &&
+ get_user(sa_family, &addr->sa_family) == 0 &&
+ sa_family == AF_UNIX)
+ addrlen = 110;
+ return sys_connect(fd, addr, addrlen);
+}
+
+asmlinkage long sys_oabi_sendto(int fd, void __user *buff,
+ size_t len, unsigned flags,
+ struct sockaddr __user *addr,
+ int addrlen)
+{
+ sa_family_t sa_family;
+ if (addrlen == 112 &&
+ get_user(sa_family, &addr->sa_family) == 0 &&
+ sa_family == AF_UNIX)
+ addrlen = 110;
+ return sys_sendto(fd, buff, len, flags, addr, addrlen);
+}
+
+asmlinkage long sys_oabi_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
+{
+ struct sockaddr __user *addr;
+ int msg_namelen;
+ sa_family_t sa_family;
+ if (msg &&
+ get_user(msg_namelen, &msg->msg_namelen) == 0 &&
+ msg_namelen == 112 &&
+ get_user(addr, &msg->msg_name) == 0 &&
+ get_user(sa_family, &addr->sa_family) == 0 &&
+ sa_family == AF_UNIX)
+ {
+ /*
+ * HACK ALERT: there is a limit to how much backward bending
+ * we should do for what is actually a transitional
+ * compatibility layer. This already has known flaws with
+ * a few ioctls that we don't intend to fix. Therefore
+ * consider this blatent hack as another one... and take care
+ * to run for cover. In most cases it will "just work fine".
+ * If it doesn't, well, tough.
+ */
+ put_user(110, &msg->msg_namelen);
+ }
+ return sys_sendmsg(fd, msg, flags);
+}
+
+asmlinkage long sys_oabi_socketcall(int call, unsigned long __user *args)
+{
+ unsigned long r = -EFAULT, a[6];
+
+ switch (call) {
+ case SYS_BIND:
+ if (copy_from_user(a, args, 3 * sizeof(long)) == 0)
+ r = sys_oabi_bind(a[0], (struct sockaddr __user *)a[1], a[2]);
+ break;
+ case SYS_CONNECT:
+ if (copy_from_user(a, args, 3 * sizeof(long)) == 0)
+ r = sys_oabi_connect(a[0], (struct sockaddr __user *)a[1], a[2]);
+ break;
+ case SYS_SENDTO:
+ if (copy_from_user(a, args, 6 * sizeof(long)) == 0)
+ r = sys_oabi_sendto(a[0], (void __user *)a[1], a[2], a[3],
+ (struct sockaddr __user *)a[4], a[5]);
+ break;
+ case SYS_SENDMSG:
+ if (copy_from_user(a, args, 3 * sizeof(long)) == 0)
+ r = sys_oabi_sendmsg(a[0], (struct msghdr __user *)a[1], a[2]);
+ break;
+ default:
+ r = sys_socketcall(call, args);
+ }
+
+ return r;
+}
diff --git a/arch/arm/kernel/thumbee.c b/arch/arm/kernel/thumbee.c
new file mode 100644
index 0000000..df3f6b7
--- /dev/null
+++ b/arch/arm/kernel/thumbee.c
@@ -0,0 +1,81 @@
+/*
+ * arch/arm/kernel/thumbee.c
+ *
+ * Copyright (C) 2008 ARM Limited
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/thread_notify.h>
+
+/*
+ * Access to the ThumbEE Handler Base register
+ */
+static inline unsigned long teehbr_read()
+{
+ unsigned long v;
+ asm("mrc p14, 6, %0, c1, c0, 0\n" : "=r" (v));
+ return v;
+}
+
+static inline void teehbr_write(unsigned long v)
+{
+ asm("mcr p14, 6, %0, c1, c0, 0\n" : : "r" (v));
+}
+
+static int thumbee_notifier(struct notifier_block *self, unsigned long cmd, void *t)
+{
+ struct thread_info *thread = t;
+
+ switch (cmd) {
+ case THREAD_NOTIFY_FLUSH:
+ thread->thumbee_state = 0;
+ break;
+ case THREAD_NOTIFY_SWITCH:
+ current_thread_info()->thumbee_state = teehbr_read();
+ teehbr_write(thread->thumbee_state);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block thumbee_notifier_block = {
+ .notifier_call = thumbee_notifier,
+};
+
+static int __init thumbee_init(void)
+{
+ unsigned long pfr0;
+ unsigned int cpu_arch = cpu_architecture();
+
+ if (cpu_arch < CPU_ARCH_ARMv7)
+ return 0;
+
+ /* processor feature register 0 */
+ asm("mrc p15, 0, %0, c0, c1, 0\n" : "=r" (pfr0));
+ if ((pfr0 & 0x0000f000) != 0x00001000)
+ return 0;
+
+ printk(KERN_INFO "ThumbEE CPU extension supported.\n");
+ elf_hwcap |= HWCAP_THUMBEE;
+ thread_register_notifier(&thumbee_notifier_block);
+
+ return 0;
+}
+
+late_initcall(thumbee_init);
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
new file mode 100644
index 0000000..c68b44a
--- /dev/null
+++ b/arch/arm/kernel/time.c
@@ -0,0 +1,391 @@
+/*
+ * linux/arch/arm/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ * Modifications for ARM (C) 1994-2001 Russell King
+ *
+ * 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 file contains the ARM-specific time handling details:
+ * reading the RTC at bootup, etc...
+ *
+ * 1994-07-02 Alan Modra
+ * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
+ * 1998-12-20 Updated NTP code according to technical memorandum Jan '96
+ * "A Kernel Model for Precision Timekeeping" by Dave Mills
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/timex.h>
+#include <linux/errno.h>
+#include <linux/profile.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/irq.h>
+
+#include <linux/mc146818rtc.h>
+
+#include <asm/leds.h>
+#include <asm/thread_info.h>
+#include <asm/mach/time.h>
+
+/*
+ * Our system timer.
+ */
+struct sys_timer *system_timer;
+
+#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
+/* this needs a better home */
+DEFINE_SPINLOCK(rtc_lock);
+
+#ifdef CONFIG_RTC_DRV_CMOS_MODULE
+EXPORT_SYMBOL(rtc_lock);
+#endif
+#endif /* pc-style 'CMOS' RTC support */
+
+/* change this if you have some constant time drift */
+#define USECS_PER_JIFFY (1000000/HZ)
+
+#ifdef CONFIG_SMP
+unsigned long profile_pc(struct pt_regs *regs)
+{
+ unsigned long fp, pc = instruction_pointer(regs);
+
+ if (in_lock_functions(pc)) {
+ fp = regs->ARM_fp;
+ pc = ((unsigned long *)fp)[-1];
+ }
+
+ return pc;
+}
+EXPORT_SYMBOL(profile_pc);
+#endif
+
+/*
+ * hook for setting the RTC's idea of the current time.
+ */
+int (*set_rtc)(void);
+
+#ifndef CONFIG_GENERIC_TIME
+static unsigned long dummy_gettimeoffset(void)
+{
+ return 0;
+}
+#endif
+
+static unsigned long next_rtc_update;
+
+/*
+ * If we have an externally synchronized linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. set_rtc() has to be
+ * called as close as possible to 500 ms before the new second
+ * starts.
+ */
+static inline void do_set_rtc(void)
+{
+ if (!ntp_synced() || set_rtc == NULL)
+ return;
+
+ if (next_rtc_update &&
+ time_before((unsigned long)xtime.tv_sec, next_rtc_update))
+ return;
+
+ if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) &&
+ xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1))
+ return;
+
+ if (set_rtc())
+ /*
+ * rtc update failed. Try again in 60s
+ */
+ next_rtc_update = xtime.tv_sec + 60;
+ else
+ next_rtc_update = xtime.tv_sec + 660;
+}
+
+#ifdef CONFIG_LEDS
+
+static void dummy_leds_event(led_event_t evt)
+{
+}
+
+void (*leds_event)(led_event_t) = dummy_leds_event;
+
+struct leds_evt_name {
+ const char name[8];
+ int on;
+ int off;
+};
+
+static const struct leds_evt_name evt_names[] = {
+ { "amber", led_amber_on, led_amber_off },
+ { "blue", led_blue_on, led_blue_off },
+ { "green", led_green_on, led_green_off },
+ { "red", led_red_on, led_red_off },
+};
+
+static ssize_t leds_store(struct sys_device *dev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret = -EINVAL, len = strcspn(buf, " ");
+
+ if (len > 0 && buf[len] == '\0')
+ len--;
+
+ if (strncmp(buf, "claim", len) == 0) {
+ leds_event(led_claim);
+ ret = size;
+ } else if (strncmp(buf, "release", len) == 0) {
+ leds_event(led_release);
+ ret = size;
+ } else {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(evt_names); i++) {
+ if (strlen(evt_names[i].name) != len ||
+ strncmp(buf, evt_names[i].name, len) != 0)
+ continue;
+ if (strncmp(buf+len, " on", 3) == 0) {
+ leds_event(evt_names[i].on);
+ ret = size;
+ } else if (strncmp(buf+len, " off", 4) == 0) {
+ leds_event(evt_names[i].off);
+ ret = size;
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+static SYSDEV_ATTR(event, 0200, NULL, leds_store);
+
+static int leds_suspend(struct sys_device *dev, pm_message_t state)
+{
+ leds_event(led_stop);
+ return 0;
+}
+
+static int leds_resume(struct sys_device *dev)
+{
+ leds_event(led_start);
+ return 0;
+}
+
+static int leds_shutdown(struct sys_device *dev)
+{
+ leds_event(led_halted);
+ return 0;
+}
+
+static struct sysdev_class leds_sysclass = {
+ .name = "leds",
+ .shutdown = leds_shutdown,
+ .suspend = leds_suspend,
+ .resume = leds_resume,
+};
+
+static struct sys_device leds_device = {
+ .id = 0,
+ .cls = &leds_sysclass,
+};
+
+static int __init leds_init(void)
+{
+ int ret;
+ ret = sysdev_class_register(&leds_sysclass);
+ if (ret == 0)
+ ret = sysdev_register(&leds_device);
+ if (ret == 0)
+ ret = sysdev_create_file(&leds_device, &attr_event);
+ return ret;
+}
+
+device_initcall(leds_init);
+
+EXPORT_SYMBOL(leds_event);
+#endif
+
+#ifdef CONFIG_LEDS_TIMER
+static inline void do_leds(void)
+{
+ static unsigned int count = HZ/2;
+
+ if (--count == 0) {
+ count = HZ/2;
+ leds_event(led_timer);
+ }
+}
+#else
+#define do_leds()
+#endif
+
+#ifndef CONFIG_GENERIC_TIME
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+ unsigned long seq;
+ unsigned long usec, sec;
+
+ do {
+ seq = read_seqbegin_irqsave(&xtime_lock, flags);
+ usec = system_timer->offset();
+ sec = xtime.tv_sec;
+ usec += xtime.tv_nsec / 1000;
+ } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
+
+ /* usec may have gone up a lot: be safe */
+ while (usec >= 1000000) {
+ usec -= 1000000;
+ sec++;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+int do_settimeofday(struct timespec *tv)
+{
+ time_t wtm_sec, sec = tv->tv_sec;
+ long wtm_nsec, nsec = tv->tv_nsec;
+
+ if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ write_seqlock_irq(&xtime_lock);
+ /*
+ * This is revolting. We need to set "xtime" correctly. However, the
+ * value in this location is the value at the most recent update of
+ * wall time. Discover what correction gettimeofday() would have
+ * done, and then undo it!
+ */
+ nsec -= system_timer->offset() * NSEC_PER_USEC;
+
+ wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+ wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+ set_normalized_timespec(&xtime, sec, nsec);
+ set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+ ntp_clear();
+ write_sequnlock_irq(&xtime_lock);
+ clock_was_set();
+ return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+#endif /* !CONFIG_GENERIC_TIME */
+
+/**
+ * save_time_delta - Save the offset between system time and RTC time
+ * @delta: pointer to timespec to store delta
+ * @rtc: pointer to timespec for current RTC time
+ *
+ * Return a delta between the system time and the RTC time, such
+ * that system time can be restored later with restore_time_delta()
+ */
+void save_time_delta(struct timespec *delta, struct timespec *rtc)
+{
+ set_normalized_timespec(delta,
+ xtime.tv_sec - rtc->tv_sec,
+ xtime.tv_nsec - rtc->tv_nsec);
+}
+EXPORT_SYMBOL(save_time_delta);
+
+/**
+ * restore_time_delta - Restore the current system time
+ * @delta: delta returned by save_time_delta()
+ * @rtc: pointer to timespec for current RTC time
+ */
+void restore_time_delta(struct timespec *delta, struct timespec *rtc)
+{
+ struct timespec ts;
+
+ set_normalized_timespec(&ts,
+ delta->tv_sec + rtc->tv_sec,
+ delta->tv_nsec + rtc->tv_nsec);
+
+ do_settimeofday(&ts);
+}
+EXPORT_SYMBOL(restore_time_delta);
+
+#ifndef CONFIG_GENERIC_CLOCKEVENTS
+/*
+ * Kernel system timer support.
+ */
+void timer_tick(void)
+{
+ profile_tick(CPU_PROFILING);
+ do_leds();
+ do_set_rtc();
+ write_seqlock(&xtime_lock);
+ do_timer(1);
+ write_sequnlock(&xtime_lock);
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(get_irq_regs()));
+#endif
+}
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_GENERIC_CLOCKEVENTS)
+static int timer_suspend(struct sys_device *dev, pm_message_t state)
+{
+ struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
+
+ if (timer->suspend != NULL)
+ timer->suspend();
+
+ return 0;
+}
+
+static int timer_resume(struct sys_device *dev)
+{
+ struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
+
+ if (timer->resume != NULL)
+ timer->resume();
+
+ return 0;
+}
+#else
+#define timer_suspend NULL
+#define timer_resume NULL
+#endif
+
+static struct sysdev_class timer_sysclass = {
+ .name = "timer",
+ .suspend = timer_suspend,
+ .resume = timer_resume,
+};
+
+static int __init timer_init_sysfs(void)
+{
+ int ret = sysdev_class_register(&timer_sysclass);
+ if (ret == 0) {
+ system_timer->dev.cls = &timer_sysclass;
+ ret = sysdev_register(&system_timer->dev);
+ }
+
+ return ret;
+}
+
+device_initcall(timer_init_sysfs);
+
+void __init time_init(void)
+{
+#ifndef CONFIG_GENERIC_TIME
+ if (system_timer->offset == NULL)
+ system_timer->offset = dummy_gettimeoffset;
+#endif
+ system_timer->init();
+}
+
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
new file mode 100644
index 0000000..79abc4d
--- /dev/null
+++ b/arch/arm/kernel/traps.c
@@ -0,0 +1,734 @@
+/*
+ * linux/arch/arm/kernel/traps.c
+ *
+ * Copyright (C) 1995-2002 Russell King
+ * Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
+ *
+ * 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.
+ *
+ * 'traps.c' handles hardware exceptions after we have saved some state in
+ * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably
+ * kill the offending process.
+ */
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/personality.h>
+#include <linux/kallsyms.h>
+#include <linux/delay.h>
+#include <linux/hardirq.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+
+#include <asm/atomic.h>
+#include <asm/cacheflush.h>
+#include <asm/system.h>
+#include <asm/unistd.h>
+#include <asm/traps.h>
+
+#include "ptrace.h"
+#include "signal.h"
+
+static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
+
+#ifdef CONFIG_DEBUG_USER
+unsigned int user_debug;
+
+static int __init user_debug_setup(char *str)
+{
+ get_option(&str, &user_debug);
+ return 1;
+}
+__setup("user_debug=", user_debug_setup);
+#endif
+
+static void dump_mem(const char *str, unsigned long bottom, unsigned long top);
+
+void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
+{
+#ifdef CONFIG_KALLSYMS
+ printk("[<%08lx>] ", where);
+ print_symbol("(%s) ", where);
+ printk("from [<%08lx>] ", from);
+ print_symbol("(%s)\n", from);
+#else
+ printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
+#endif
+
+ if (in_exception_text(where))
+ dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
+}
+
+/*
+ * Stack pointers should always be within the kernels view of
+ * physical memory. If it is not there, then we can't dump
+ * out any information relating to the stack.
+ */
+static int verify_stack(unsigned long sp)
+{
+ if (sp < PAGE_OFFSET ||
+ (sp > (unsigned long)high_memory && high_memory != NULL))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Dump out the contents of some memory nicely...
+ */
+static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
+{
+ unsigned long p = bottom & ~31;
+ mm_segment_t fs;
+ int i;
+
+ /*
+ * We need to switch to kernel mode so that we can use __get_user
+ * to safely read from kernel space. Note that we now dump the
+ * code first, just in case the backtrace kills us.
+ */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
+
+ for (p = bottom & ~31; p < top;) {
+ printk("%04lx: ", p & 0xffff);
+
+ for (i = 0; i < 8; i++, p += 4) {
+ unsigned int val;
+
+ if (p < bottom || p >= top)
+ printk(" ");
+ else {
+ __get_user(val, (unsigned long *)p);
+ printk("%08x ", val);
+ }
+ }
+ printk ("\n");
+ }
+
+ set_fs(fs);
+}
+
+static void dump_instr(struct pt_regs *regs)
+{
+ unsigned long addr = instruction_pointer(regs);
+ const int thumb = thumb_mode(regs);
+ const int width = thumb ? 4 : 8;
+ mm_segment_t fs;
+ int i;
+
+ /*
+ * We need to switch to kernel mode so that we can use __get_user
+ * to safely read from kernel space. Note that we now dump the
+ * code first, just in case the backtrace kills us.
+ */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ printk("Code: ");
+ for (i = -4; i < 1; i++) {
+ unsigned int val, bad;
+
+ if (thumb)
+ bad = __get_user(val, &((u16 *)addr)[i]);
+ else
+ bad = __get_user(val, &((u32 *)addr)[i]);
+
+ if (!bad)
+ printk(i == 0 ? "(%0*x) " : "%0*x ", width, val);
+ else {
+ printk("bad PC value.");
+ break;
+ }
+ }
+ printk("\n");
+
+ set_fs(fs);
+}
+
+static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
+{
+ unsigned int fp;
+ int ok = 1;
+
+ printk("Backtrace: ");
+ fp = regs->ARM_fp;
+ if (!fp) {
+ printk("no frame pointer");
+ ok = 0;
+ } else if (verify_stack(fp)) {
+ printk("invalid frame pointer 0x%08x", fp);
+ ok = 0;
+ } else if (fp < (unsigned long)end_of_stack(tsk))
+ printk("frame pointer underflow");
+ printk("\n");
+
+ if (ok)
+ c_backtrace(fp, processor_mode(regs));
+}
+
+void dump_stack(void)
+{
+ __backtrace();
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+void show_stack(struct task_struct *tsk, unsigned long *sp)
+{
+ unsigned long fp;
+
+ if (!tsk)
+ tsk = current;
+
+ if (tsk != current)
+ fp = thread_saved_fp(tsk);
+ else
+ asm("mov %0, fp" : "=r" (fp) : : "cc");
+
+ c_backtrace(fp, 0x10);
+ barrier();
+}
+
+#ifdef CONFIG_PREEMPT
+#define S_PREEMPT " PREEMPT"
+#else
+#define S_PREEMPT ""
+#endif
+#ifdef CONFIG_SMP
+#define S_SMP " SMP"
+#else
+#define S_SMP ""
+#endif
+
+static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs)
+{
+ struct task_struct *tsk = thread->task;
+ static int die_counter;
+
+ printk("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
+ str, err, ++die_counter);
+ print_modules();
+ __show_regs(regs);
+ printk("Process %s (pid: %d, stack limit = 0x%p)\n",
+ tsk->comm, task_pid_nr(tsk), thread + 1);
+
+ if (!user_mode(regs) || in_interrupt()) {
+ dump_mem("Stack: ", regs->ARM_sp,
+ THREAD_SIZE + (unsigned long)task_stack_page(tsk));
+ dump_backtrace(regs, tsk);
+ dump_instr(regs);
+ }
+}
+
+DEFINE_SPINLOCK(die_lock);
+
+/*
+ * This function is protected against re-entrancy.
+ */
+NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
+{
+ struct thread_info *thread = current_thread_info();
+
+ oops_enter();
+
+ console_verbose();
+ spin_lock_irq(&die_lock);
+ bust_spinlocks(1);
+ __die(str, err, thread, regs);
+ bust_spinlocks(0);
+ add_taint(TAINT_DIE);
+ spin_unlock_irq(&die_lock);
+
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+
+ if (panic_on_oops)
+ panic("Fatal exception");
+
+ oops_exit();
+ do_exit(SIGSEGV);
+}
+
+void arm_notify_die(const char *str, struct pt_regs *regs,
+ struct siginfo *info, unsigned long err, unsigned long trap)
+{
+ if (user_mode(regs)) {
+ current->thread.error_code = err;
+ current->thread.trap_no = trap;
+
+ force_sig_info(info->si_signo, info, current);
+ } else {
+ die(str, regs, err);
+ }
+}
+
+static LIST_HEAD(undef_hook);
+static DEFINE_SPINLOCK(undef_lock);
+
+void register_undef_hook(struct undef_hook *hook)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&undef_lock, flags);
+ list_add(&hook->node, &undef_hook);
+ spin_unlock_irqrestore(&undef_lock, flags);
+}
+
+void unregister_undef_hook(struct undef_hook *hook)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&undef_lock, flags);
+ list_del(&hook->node);
+ spin_unlock_irqrestore(&undef_lock, flags);
+}
+
+static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
+{
+ struct undef_hook *hook;
+ unsigned long flags;
+ int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL;
+
+ spin_lock_irqsave(&undef_lock, flags);
+ list_for_each_entry(hook, &undef_hook, node)
+ if ((instr & hook->instr_mask) == hook->instr_val &&
+ (regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val)
+ fn = hook->fn;
+ spin_unlock_irqrestore(&undef_lock, flags);
+
+ return fn ? fn(regs, instr) : 1;
+}
+
+asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
+{
+ unsigned int correction = thumb_mode(regs) ? 2 : 4;
+ unsigned int instr;
+ siginfo_t info;
+ void __user *pc;
+
+ /*
+ * According to the ARM ARM, PC is 2 or 4 bytes ahead,
+ * depending whether we're in Thumb mode or not.
+ * Correct this offset.
+ */
+ regs->ARM_pc -= correction;
+
+ pc = (void __user *)instruction_pointer(regs);
+
+ if (processor_mode(regs) == SVC_MODE) {
+ instr = *(u32 *) pc;
+ } else if (thumb_mode(regs)) {
+ get_user(instr, (u16 __user *)pc);
+ } else {
+ get_user(instr, (u32 __user *)pc);
+ }
+
+ if (call_undef_hook(regs, instr) == 0)
+ return;
+
+#ifdef CONFIG_DEBUG_USER
+ if (user_debug & UDBG_UNDEFINED) {
+ printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
+ current->comm, task_pid_nr(current), pc);
+ dump_instr(regs);
+ }
+#endif
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLOPC;
+ info.si_addr = pc;
+
+ arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
+}
+
+asmlinkage void do_unexp_fiq (struct pt_regs *regs)
+{
+ printk("Hmm. Unexpected FIQ received, but trying to continue\n");
+ printk("You may have a hardware problem...\n");
+}
+
+/*
+ * bad_mode handles the impossible case in the vectors. If you see one of
+ * these, then it's extremely serious, and could mean you have buggy hardware.
+ * It never returns, and never tries to sync. We hope that we can at least
+ * dump out some state information...
+ */
+asmlinkage void bad_mode(struct pt_regs *regs, int reason)
+{
+ console_verbose();
+
+ printk(KERN_CRIT "Bad mode in %s handler detected\n", handler[reason]);
+
+ die("Oops - bad mode", regs, 0);
+ local_irq_disable();
+ panic("bad mode");
+}
+
+static int bad_syscall(int n, struct pt_regs *regs)
+{
+ struct thread_info *thread = current_thread_info();
+ siginfo_t info;
+
+ if (current->personality != PER_LINUX &&
+ current->personality != PER_LINUX_32BIT &&
+ thread->exec_domain->handler) {
+ thread->exec_domain->handler(n, regs);
+ return regs->ARM_r0;
+ }
+
+#ifdef CONFIG_DEBUG_USER
+ if (user_debug & UDBG_SYSCALL) {
+ printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
+ task_pid_nr(current), current->comm, n);
+ dump_instr(regs);
+ }
+#endif
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLTRP;
+ info.si_addr = (void __user *)instruction_pointer(regs) -
+ (thumb_mode(regs) ? 2 : 4);
+
+ arm_notify_die("Oops - bad syscall", regs, &info, n, 0);
+
+ return regs->ARM_r0;
+}
+
+static inline void
+do_cache_op(unsigned long start, unsigned long end, int flags)
+{
+ struct vm_area_struct *vma;
+
+ if (end < start || flags)
+ return;
+
+ vma = find_vma(current->active_mm, start);
+ if (vma && vma->vm_start < end) {
+ if (start < vma->vm_start)
+ start = vma->vm_start;
+ if (end > vma->vm_end)
+ end = vma->vm_end;
+
+ flush_cache_user_range(vma, start, end);
+ }
+}
+
+/*
+ * Handle all unrecognised system calls.
+ * 0x9f0000 - 0x9fffff are some more esoteric system calls
+ */
+#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
+asmlinkage int arm_syscall(int no, struct pt_regs *regs)
+{
+ struct thread_info *thread = current_thread_info();
+ siginfo_t info;
+
+ if ((no >> 16) != (__ARM_NR_BASE>> 16))
+ return bad_syscall(no, regs);
+
+ switch (no & 0xffff) {
+ case 0: /* branch through 0 */
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = NULL;
+
+ arm_notify_die("branch through zero", regs, &info, 0, 0);
+ return 0;
+
+ case NR(breakpoint): /* SWI BREAK_POINT */
+ regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
+ ptrace_break(current, regs);
+ return regs->ARM_r0;
+
+ /*
+ * Flush a region from virtual address 'r0' to virtual address 'r1'
+ * _exclusive_. There is no alignment requirement on either address;
+ * user space does not need to know the hardware cache layout.
+ *
+ * r2 contains flags. It should ALWAYS be passed as ZERO until it
+ * is defined to be something else. For now we ignore it, but may
+ * the fires of hell burn in your belly if you break this rule. ;)
+ *
+ * (at a later date, we may want to allow this call to not flush
+ * various aspects of the cache. Passing '0' will guarantee that
+ * everything necessary gets flushed to maintain consistency in
+ * the specified region).
+ */
+ case NR(cacheflush):
+ do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);
+ return 0;
+
+ case NR(usr26):
+ if (!(elf_hwcap & HWCAP_26BIT))
+ break;
+ regs->ARM_cpsr &= ~MODE32_BIT;
+ return regs->ARM_r0;
+
+ case NR(usr32):
+ if (!(elf_hwcap & HWCAP_26BIT))
+ break;
+ regs->ARM_cpsr |= MODE32_BIT;
+ return regs->ARM_r0;
+
+ case NR(set_tls):
+ thread->tp_value = regs->ARM_r0;
+#if defined(CONFIG_HAS_TLS_REG)
+ asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );
+#elif !defined(CONFIG_TLS_REG_EMUL)
+ /*
+ * User space must never try to access this directly.
+ * Expect your app to break eventually if you do so.
+ * The user helper at 0xffff0fe0 must be used instead.
+ * (see entry-armv.S for details)
+ */
+ *((unsigned int *)0xffff0ff0) = regs->ARM_r0;
+#endif
+ return 0;
+
+#ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG
+ /*
+ * Atomically store r1 in *r2 if *r2 is equal to r0 for user space.
+ * Return zero in r0 if *MEM was changed or non-zero if no exchange
+ * happened. Also set the user C flag accordingly.
+ * If access permissions have to be fixed up then non-zero is
+ * returned and the operation has to be re-attempted.
+ *
+ * *NOTE*: This is a ghost syscall private to the kernel. Only the
+ * __kuser_cmpxchg code in entry-armv.S should be aware of its
+ * existence. Don't ever use this from user code.
+ */
+ case 0xfff0:
+ for (;;) {
+ extern void do_DataAbort(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs);
+ unsigned long val;
+ unsigned long addr = regs->ARM_r2;
+ struct mm_struct *mm = current->mm;
+ pgd_t *pgd; pmd_t *pmd; pte_t *pte;
+ spinlock_t *ptl;
+
+ regs->ARM_cpsr &= ~PSR_C_BIT;
+ down_read(&mm->mmap_sem);
+ pgd = pgd_offset(mm, addr);
+ if (!pgd_present(*pgd))
+ goto bad_access;
+ pmd = pmd_offset(pgd, addr);
+ if (!pmd_present(*pmd))
+ goto bad_access;
+ pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
+ if (!pte_present(*pte) || !pte_dirty(*pte)) {
+ pte_unmap_unlock(pte, ptl);
+ goto bad_access;
+ }
+ val = *(unsigned long *)addr;
+ val -= regs->ARM_r0;
+ if (val == 0) {
+ *(unsigned long *)addr = regs->ARM_r1;
+ regs->ARM_cpsr |= PSR_C_BIT;
+ }
+ pte_unmap_unlock(pte, ptl);
+ up_read(&mm->mmap_sem);
+ return val;
+
+ bad_access:
+ up_read(&mm->mmap_sem);
+ /* simulate a write access fault */
+ do_DataAbort(addr, 15 + (1 << 11), regs);
+ }
+#endif
+
+ default:
+ /* Calls 9f00xx..9f07ff are defined to return -ENOSYS
+ if not implemented, rather than raising SIGILL. This
+ way the calling program can gracefully determine whether
+ a feature is supported. */
+ if (no <= 0x7ff)
+ return -ENOSYS;
+ break;
+ }
+#ifdef CONFIG_DEBUG_USER
+ /*
+ * experience shows that these seem to indicate that
+ * something catastrophic has happened
+ */
+ if (user_debug & UDBG_SYSCALL) {
+ printk("[%d] %s: arm syscall %d\n",
+ task_pid_nr(current), current->comm, no);
+ dump_instr(regs);
+ if (user_mode(regs)) {
+ __show_regs(regs);
+ c_backtrace(regs->ARM_fp, processor_mode(regs));
+ }
+ }
+#endif
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLTRP;
+ info.si_addr = (void __user *)instruction_pointer(regs) -
+ (thumb_mode(regs) ? 2 : 4);
+
+ arm_notify_die("Oops - bad syscall(2)", regs, &info, no, 0);
+ return 0;
+}
+
+#ifdef CONFIG_TLS_REG_EMUL
+
+/*
+ * We might be running on an ARMv6+ processor which should have the TLS
+ * register but for some reason we can't use it, or maybe an SMP system
+ * using a pre-ARMv6 processor (there are apparently a few prototypes like
+ * that in existence) and therefore access to that register must be
+ * emulated.
+ */
+
+static int get_tp_trap(struct pt_regs *regs, unsigned int instr)
+{
+ int reg = (instr >> 12) & 15;
+ if (reg == 15)
+ return 1;
+ regs->uregs[reg] = current_thread_info()->tp_value;
+ regs->ARM_pc += 4;
+ return 0;
+}
+
+static struct undef_hook arm_mrc_hook = {
+ .instr_mask = 0x0fff0fff,
+ .instr_val = 0x0e1d0f70,
+ .cpsr_mask = PSR_T_BIT,
+ .cpsr_val = 0,
+ .fn = get_tp_trap,
+};
+
+static int __init arm_mrc_hook_init(void)
+{
+ register_undef_hook(&arm_mrc_hook);
+ return 0;
+}
+
+late_initcall(arm_mrc_hook_init);
+
+#endif
+
+void __bad_xchg(volatile void *ptr, int size)
+{
+ printk("xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n",
+ __builtin_return_address(0), ptr, size);
+ BUG();
+}
+EXPORT_SYMBOL(__bad_xchg);
+
+/*
+ * A data abort trap was taken, but we did not handle the instruction.
+ * Try to abort the user program, or panic if it was the kernel.
+ */
+asmlinkage void
+baddataabort(int code, unsigned long instr, struct pt_regs *regs)
+{
+ unsigned long addr = instruction_pointer(regs);
+ siginfo_t info;
+
+#ifdef CONFIG_DEBUG_USER
+ if (user_debug & UDBG_BADABORT) {
+ printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n",
+ task_pid_nr(current), current->comm, code, instr);
+ dump_instr(regs);
+ show_pte(current->mm, addr);
+ }
+#endif
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLOPC;
+ info.si_addr = (void __user *)addr;
+
+ arm_notify_die("unknown data abort code", regs, &info, instr, 0);
+}
+
+void __attribute__((noreturn)) __bug(const char *file, int line)
+{
+ printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
+ *(int *)0 = 0;
+
+ /* Avoid "noreturn function does return" */
+ for (;;);
+}
+EXPORT_SYMBOL(__bug);
+
+void __readwrite_bug(const char *fn)
+{
+ printk("%s called, but not implemented\n", fn);
+ BUG();
+}
+EXPORT_SYMBOL(__readwrite_bug);
+
+void __pte_error(const char *file, int line, unsigned long val)
+{
+ printk("%s:%d: bad pte %08lx.\n", file, line, val);
+}
+
+void __pmd_error(const char *file, int line, unsigned long val)
+{
+ printk("%s:%d: bad pmd %08lx.\n", file, line, val);
+}
+
+void __pgd_error(const char *file, int line, unsigned long val)
+{
+ printk("%s:%d: bad pgd %08lx.\n", file, line, val);
+}
+
+asmlinkage void __div0(void)
+{
+ printk("Division by zero in kernel.\n");
+ dump_stack();
+}
+EXPORT_SYMBOL(__div0);
+
+void abort(void)
+{
+ BUG();
+
+ /* if that doesn't kill us, halt */
+ panic("Oops failed to kill thread");
+}
+EXPORT_SYMBOL(abort);
+
+void __init trap_init(void)
+{
+ return;
+}
+
+void __init early_trap_init(void)
+{
+ unsigned long vectors = CONFIG_VECTORS_BASE;
+ extern char __stubs_start[], __stubs_end[];
+ extern char __vectors_start[], __vectors_end[];
+ extern char __kuser_helper_start[], __kuser_helper_end[];
+ int kuser_sz = __kuser_helper_end - __kuser_helper_start;
+
+ /*
+ * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
+ * into the vector page, mapped at 0xffff0000, and ensure these
+ * are visible to the instruction stream.
+ */
+ memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
+ memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
+ memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
+
+ /*
+ * Copy signal return handlers into the vector page, and
+ * set sigreturn to be a pointer to these.
+ */
+ memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
+ sizeof(sigreturn_codes));
+
+ flush_icache_range(vectors, vectors + PAGE_SIZE);
+ modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
+}
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
new file mode 100644
index 0000000..4898bdc
--- /dev/null
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -0,0 +1,192 @@
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/thread_info.h>
+#include <asm/memory.h>
+
+OUTPUT_ARCH(arm)
+ENTRY(stext)
+
+#ifndef __ARMEB__
+jiffies = jiffies_64;
+#else
+jiffies = jiffies_64 + 4;
+#endif
+
+SECTIONS
+{
+#ifdef CONFIG_XIP_KERNEL
+ . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
+#else
+ . = PAGE_OFFSET + TEXT_OFFSET;
+#endif
+ .text.head : {
+ _stext = .;
+ _sinittext = .;
+ *(.text.head)
+ }
+
+ .init : { /* Init code and data */
+ INIT_TEXT
+ _einittext = .;
+ __proc_info_begin = .;
+ *(.proc.info.init)
+ __proc_info_end = .;
+ __arch_info_begin = .;
+ *(.arch.info.init)
+ __arch_info_end = .;
+ __tagtable_begin = .;
+ *(.taglist.init)
+ __tagtable_end = .;
+ . = ALIGN(16);
+ __setup_start = .;
+ *(.init.setup)
+ __setup_end = .;
+ __early_begin = .;
+ *(.early_param.init)
+ __early_end = .;
+ __initcall_start = .;
+ INITCALLS
+ __initcall_end = .;
+ __con_initcall_start = .;
+ *(.con_initcall.init)
+ __con_initcall_end = .;
+ __security_initcall_start = .;
+ *(.security_initcall.init)
+ __security_initcall_end = .;
+#ifdef CONFIG_BLK_DEV_INITRD
+ . = ALIGN(32);
+ __initramfs_start = .;
+ usr/built-in.o(.init.ramfs)
+ __initramfs_end = .;
+#endif
+ . = ALIGN(4096);
+ __per_cpu_start = .;
+ *(.data.percpu)
+ *(.data.percpu.shared_aligned)
+ __per_cpu_end = .;
+#ifndef CONFIG_XIP_KERNEL
+ __init_begin = _stext;
+ INIT_DATA
+ . = ALIGN(4096);
+ __init_end = .;
+#endif
+ }
+
+ /DISCARD/ : { /* Exit code and data */
+ EXIT_TEXT
+ EXIT_DATA
+ *(.exitcall.exit)
+#ifndef CONFIG_MMU
+ *(.fixup)
+ *(__ex_table)
+#endif
+ }
+
+ .text : { /* Real text segment */
+ _text = .; /* Text and read-only data */
+ __exception_text_start = .;
+ *(.exception.text)
+ __exception_text_end = .;
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ KPROBES_TEXT
+#ifdef CONFIG_MMU
+ *(.fixup)
+#endif
+ *(.gnu.warning)
+ *(.rodata)
+ *(.rodata.*)
+ *(.glue_7)
+ *(.glue_7t)
+ *(.got) /* Global offset table */
+ }
+
+ RODATA
+
+ _etext = .; /* End of text and rodata section */
+
+#ifdef CONFIG_XIP_KERNEL
+ __data_loc = ALIGN(4); /* location in binary */
+ . = PAGE_OFFSET + TEXT_OFFSET;
+#else
+ . = ALIGN(THREAD_SIZE);
+ __data_loc = .;
+#endif
+
+ .data : AT(__data_loc) {
+ __data_start = .; /* address in memory */
+
+ /*
+ * first, the init task union, aligned
+ * to an 8192 byte boundary.
+ */
+ *(.data.init_task)
+
+#ifdef CONFIG_XIP_KERNEL
+ . = ALIGN(4096);
+ __init_begin = .;
+ INIT_DATA
+ . = ALIGN(4096);
+ __init_end = .;
+#endif
+
+ . = ALIGN(4096);
+ __nosave_begin = .;
+ *(.data.nosave)
+ . = ALIGN(4096);
+ __nosave_end = .;
+
+ /*
+ * then the cacheline aligned data
+ */
+ . = ALIGN(32);
+ *(.data.cacheline_aligned)
+
+ /*
+ * The exception fixup table (might need resorting at runtime)
+ */
+ . = ALIGN(32);
+ __start___ex_table = .;
+#ifdef CONFIG_MMU
+ *(__ex_table)
+#endif
+ __stop___ex_table = .;
+
+ /*
+ * and the usual data section
+ */
+ DATA_DATA
+ CONSTRUCTORS
+
+ _edata = .;
+ }
+ _edata_loc = __data_loc + SIZEOF(.data);
+
+ .bss : {
+ __bss_start = .; /* BSS */
+ *(.bss)
+ *(COMMON)
+ _end = .;
+ }
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+}
+
+/*
+ * These must never be empty
+ * If you have to comment these two assert statements out, your
+ * binutils is too old (for other reasons as well)
+ */
+ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
+ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")
diff --git a/arch/arm/kernel/xscale-cp0.c b/arch/arm/kernel/xscale-cp0.c
new file mode 100644
index 0000000..17127db
--- /dev/null
+++ b/arch/arm/kernel/xscale-cp0.c
@@ -0,0 +1,179 @@
+/*
+ * linux/arch/arm/kernel/xscale-cp0.c
+ *
+ * XScale DSP and iWMMXt coprocessor context switching and handling
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <asm/thread_notify.h>
+
+static inline void dsp_save_state(u32 *state)
+{
+ __asm__ __volatile__ (
+ "mrrc p0, 0, %0, %1, c0\n"
+ : "=r" (state[0]), "=r" (state[1]));
+}
+
+static inline void dsp_load_state(u32 *state)
+{
+ __asm__ __volatile__ (
+ "mcrr p0, 0, %0, %1, c0\n"
+ : : "r" (state[0]), "r" (state[1]));
+}
+
+static int dsp_do(struct notifier_block *self, unsigned long cmd, void *t)
+{
+ struct thread_info *thread = t;
+
+ switch (cmd) {
+ case THREAD_NOTIFY_FLUSH:
+ thread->cpu_context.extra[0] = 0;
+ thread->cpu_context.extra[1] = 0;
+ break;
+
+ case THREAD_NOTIFY_SWITCH:
+ dsp_save_state(current_thread_info()->cpu_context.extra);
+ dsp_load_state(thread->cpu_context.extra);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block dsp_notifier_block = {
+ .notifier_call = dsp_do,
+};
+
+
+#ifdef CONFIG_IWMMXT
+static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t)
+{
+ struct thread_info *thread = t;
+
+ switch (cmd) {
+ case THREAD_NOTIFY_FLUSH:
+ /*
+ * flush_thread() zeroes thread->fpstate, so no need
+ * to do anything here.
+ *
+ * FALLTHROUGH: Ensure we don't try to overwrite our newly
+ * initialised state information on the first fault.
+ */
+
+ case THREAD_NOTIFY_RELEASE:
+ iwmmxt_task_release(thread);
+ break;
+
+ case THREAD_NOTIFY_SWITCH:
+ iwmmxt_task_switch(thread);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block iwmmxt_notifier_block = {
+ .notifier_call = iwmmxt_do,
+};
+#endif
+
+
+static u32 __init xscale_cp_access_read(void)
+{
+ u32 value;
+
+ __asm__ __volatile__ (
+ "mrc p15, 0, %0, c15, c1, 0\n\t"
+ : "=r" (value));
+
+ return value;
+}
+
+static void __init xscale_cp_access_write(u32 value)
+{
+ u32 temp;
+
+ __asm__ __volatile__ (
+ "mcr p15, 0, %1, c15, c1, 0\n\t"
+ "mrc p15, 0, %0, c15, c1, 0\n\t"
+ "mov %0, %0\n\t"
+ "sub pc, pc, #4\n\t"
+ : "=r" (temp) : "r" (value));
+}
+
+/*
+ * Detect whether we have a MAC coprocessor (40 bit register) or an
+ * iWMMXt coprocessor (64 bit registers) by loading 00000100:00000000
+ * into a coprocessor register and reading it back, and checking
+ * whether the upper word survived intact.
+ */
+static int __init cpu_has_iwmmxt(void)
+{
+ u32 lo;
+ u32 hi;
+
+ /*
+ * This sequence is interpreted by the DSP coprocessor as:
+ * mar acc0, %2, %3
+ * mra %0, %1, acc0
+ *
+ * And by the iWMMXt coprocessor as:
+ * tmcrr wR0, %2, %3
+ * tmrrc %0, %1, wR0
+ */
+ __asm__ __volatile__ (
+ "mcrr p0, 0, %2, %3, c0\n"
+ "mrrc p0, 0, %0, %1, c0\n"
+ : "=r" (lo), "=r" (hi)
+ : "r" (0), "r" (0x100));
+
+ return !!hi;
+}
+
+
+/*
+ * If we detect that the CPU has iWMMXt (and CONFIG_IWMMXT=y), we
+ * disable CP0/CP1 on boot, and let call_fpe() and the iWMMXt lazy
+ * switch code handle iWMMXt context switching. If on the other
+ * hand the CPU has a DSP coprocessor, we keep access to CP0 enabled
+ * all the time, and save/restore acc0 on context switch in non-lazy
+ * fashion.
+ */
+static int __init xscale_cp0_init(void)
+{
+ u32 cp_access;
+
+ cp_access = xscale_cp_access_read() & ~3;
+ xscale_cp_access_write(cp_access | 1);
+
+ if (cpu_has_iwmmxt()) {
+#ifndef CONFIG_IWMMXT
+ printk(KERN_WARNING "CAUTION: XScale iWMMXt coprocessor "
+ "detected, but kernel support is missing.\n");
+#else
+ printk(KERN_INFO "XScale iWMMXt coprocessor detected.\n");
+ elf_hwcap |= HWCAP_IWMMXT;
+ thread_register_notifier(&iwmmxt_notifier_block);
+#endif
+ } else {
+ printk(KERN_INFO "XScale DSP coprocessor detected.\n");
+ thread_register_notifier(&dsp_notifier_block);
+ cp_access |= 1;
+ }
+
+ xscale_cp_access_write(cp_access);
+
+ return 0;
+}
+
+late_initcall(xscale_cp0_init);
OpenPOWER on IntegriCloud