summaryrefslogtreecommitdiff
path: root/arch/xtensa/kernel
diff options
context:
space:
mode:
authorSrikant Patnaik2015-01-11 12:28:04 +0530
committerSrikant Patnaik2015-01-11 12:28:04 +0530
commit871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch)
tree8718f573808810c2a1e8cb8fb6ac469093ca2784 /arch/xtensa/kernel
parent9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff)
downloadFOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized. Changes are basically to make it look like kernel structure.
Diffstat (limited to 'arch/xtensa/kernel')
-rw-r--r--arch/xtensa/kernel/Makefile34
-rw-r--r--arch/xtensa/kernel/align.S455
-rw-r--r--arch/xtensa/kernel/asm-offsets.c111
-rw-r--r--arch/xtensa/kernel/coprocessor.S335
-rw-r--r--arch/xtensa/kernel/entry.S1960
-rw-r--r--arch/xtensa/kernel/head.S244
-rw-r--r--arch/xtensa/kernel/init_task.c31
-rw-r--r--arch/xtensa/kernel/io.c75
-rw-r--r--arch/xtensa/kernel/irq.c151
-rw-r--r--arch/xtensa/kernel/module.c192
-rw-r--r--arch/xtensa/kernel/pci-dma.c94
-rw-r--r--arch/xtensa/kernel/pci.c384
-rw-r--r--arch/xtensa/kernel/platform.c47
-rw-r--r--arch/xtensa/kernel/process.c336
-rw-r--r--arch/xtensa/kernel/ptrace.c346
-rw-r--r--arch/xtensa/kernel/setup.c480
-rw-r--r--arch/xtensa/kernel/signal.c565
-rw-r--r--arch/xtensa/kernel/syscall.c57
-rw-r--r--arch/xtensa/kernel/time.c117
-rw-r--r--arch/xtensa/kernel/traps.c527
-rw-r--r--arch/xtensa/kernel/vectors.S468
-rw-r--r--arch/xtensa/kernel/vmlinux.lds.S272
-rw-r--r--arch/xtensa/kernel/xtensa_ksyms.c97
23 files changed, 7378 insertions, 0 deletions
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile
new file mode 100644
index 00000000..2d2728b3
--- /dev/null
+++ b/arch/xtensa/kernel/Makefile
@@ -0,0 +1,34 @@
+#
+# Makefile for the Linux/Xtensa kernel.
+#
+
+extra-y := head.o vmlinux.lds
+
+obj-y := align.o entry.o irq.o coprocessor.o process.o ptrace.o \
+ setup.o signal.o syscall.o time.o traps.o vectors.o platform.o \
+ pci-dma.o init_task.o io.o
+
+obj-$(CONFIG_KGDB) += xtensa-stub.o
+obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
+
+# In the Xtensa architecture, assembly generates literals which must always
+# precede the L32R instruction with a relative offset less than 256 kB.
+# Therefore, the .text and .literal section must be combined in parenthesis
+# in the linker script, such as: *(.literal .text).
+#
+# We need to post-process the generated vmlinux.lds scripts to convert
+# *(xxx.text) to *(xxx.literal xxx.text) for the following text sections:
+# .text .ref.text .*init.text .*exit.text .text.*
+#
+# Replicate rules in scripts/Makefile.build
+
+sed-y = -e 's/\*(\(\.[a-z]*it\|\.ref\|\)\.text)/*(\1.literal \1.text)/g' \
+ -e 's/\*(\(\.text\.[a-z]*\))/*(\1.literal \1)/g'
+
+quiet_cmd__cpp_lds_S = LDS $@
+ cmd__cpp_lds_S = $(CPP) $(cpp_flags) -P -C -Uxtensa -D__ASSEMBLY__ $< \
+ | sed $(sed-y) >$@
+
+$(obj)/vmlinux.lds: $(src)/vmlinux.lds.S FORCE
+ $(call if_changed_dep,_cpp_lds_S)
diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S
new file mode 100644
index 00000000..33d6e9d2
--- /dev/null
+++ b/arch/xtensa/kernel/align.S
@@ -0,0 +1,455 @@
+/*
+ * arch/xtensa/kernel/align.S
+ *
+ * Handle unalignment exceptions in kernel space.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of
+ * this archive for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica, Inc.
+ *
+ * Rewritten by Chris Zankel <chris@zankel.net>
+ *
+ * Based on work from Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * and Marc Gauthier <marc@tensilica.com, marc@alimni.uwaterloo.ca>
+ */
+
+#include <linux/linkage.h>
+#include <asm/current.h>
+#include <asm/asm-offsets.h>
+#include <asm/processor.h>
+
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+
+/* First-level exception handler for unaligned exceptions.
+ *
+ * Note: This handler works only for kernel exceptions. Unaligned user
+ * access should get a seg fault.
+ */
+
+/* Big and little endian 16-bit values are located in
+ * different halves of a register. HWORD_START helps to
+ * abstract the notion of extracting a 16-bit value from a
+ * register.
+ * We also have to define new shifting instructions because
+ * lsb and msb are on 'opposite' ends in a register for
+ * different endian machines.
+ *
+ * Assume a memory region in ascending address:
+ * 0 1 2 3|4 5 6 7
+ *
+ * When loading one word into a register, the content of that register is:
+ * LE 3 2 1 0, 7 6 5 4
+ * BE 0 1 2 3, 4 5 6 7
+ *
+ * Masking the bits of the higher/lower address means:
+ * LE X X 0 0, 0 0 X X
+ * BE 0 0 X X, X X 0 0
+ *
+ * Shifting to higher/lower addresses, means:
+ * LE shift left / shift right
+ * BE shift right / shift left
+ *
+ * Extracting 16 bits from a 32 bit reg. value to higher/lower address means:
+ * LE mask 0 0 X X / shift left
+ * BE shift left / mask 0 0 X X
+ */
+
+#define UNALIGNED_USER_EXCEPTION
+
+#if XCHAL_HAVE_BE
+
+#define HWORD_START 16
+#define INSN_OP0 28
+#define INSN_T 24
+#define INSN_OP1 16
+
+.macro __src_b r, w0, w1; src \r, \w0, \w1; .endm
+.macro __ssa8 r; ssa8b \r; .endm
+.macro __ssa8r r; ssa8l \r; .endm
+.macro __sh r, s; srl \r, \s; .endm
+.macro __sl r, s; sll \r, \s; .endm
+.macro __exth r, s; extui \r, \s, 0, 16; .endm
+.macro __extl r, s; slli \r, \s, 16; .endm
+
+#else
+
+#define HWORD_START 0
+#define INSN_OP0 0
+#define INSN_T 4
+#define INSN_OP1 12
+
+.macro __src_b r, w0, w1; src \r, \w1, \w0; .endm
+.macro __ssa8 r; ssa8l \r; .endm
+.macro __ssa8r r; ssa8b \r; .endm
+.macro __sh r, s; sll \r, \s; .endm
+.macro __sl r, s; srl \r, \s; .endm
+.macro __exth r, s; slli \r, \s, 16; .endm
+.macro __extl r, s; extui \r, \s, 0, 16; .endm
+
+#endif
+
+/*
+ * xxxx xxxx = imm8 field
+ * yyyy = imm4 field
+ * ssss = s field
+ * tttt = t field
+ *
+ * 16 0
+ * -------------------
+ * L32I.N yyyy ssss tttt 1000
+ * S32I.N yyyy ssss tttt 1001
+ *
+ * 23 0
+ * -----------------------------
+ * res 0000 0010
+ * L16UI xxxx xxxx 0001 ssss tttt 0010
+ * L32I xxxx xxxx 0010 ssss tttt 0010
+ * XXX 0011 ssss tttt 0010
+ * XXX 0100 ssss tttt 0010
+ * S16I xxxx xxxx 0101 ssss tttt 0010
+ * S32I xxxx xxxx 0110 ssss tttt 0010
+ * XXX 0111 ssss tttt 0010
+ * XXX 1000 ssss tttt 0010
+ * L16SI xxxx xxxx 1001 ssss tttt 0010
+ * XXX 1010 0010
+ * **L32AI xxxx xxxx 1011 ssss tttt 0010 unsupported
+ * XXX 1100 0010
+ * XXX 1101 0010
+ * XXX 1110 0010
+ * **S32RI xxxx xxxx 1111 ssss tttt 0010 unsupported
+ * -----------------------------
+ * ^ ^ ^
+ * sub-opcode (NIBBLE_R) -+ | |
+ * t field (NIBBLE_T) -----------+ |
+ * major opcode (NIBBLE_OP0) --------------+
+ */
+
+#define OP0_L32I_N 0x8 /* load immediate narrow */
+#define OP0_S32I_N 0x9 /* store immediate narrow */
+#define OP1_SI_MASK 0x4 /* OP1 bit set for stores */
+#define OP1_SI_BIT 2 /* OP1 bit number for stores */
+
+#define OP1_L32I 0x2
+#define OP1_L16UI 0x1
+#define OP1_L16SI 0x9
+#define OP1_L32AI 0xb
+
+#define OP1_S32I 0x6
+#define OP1_S16I 0x5
+#define OP1_S32RI 0xf
+
+/*
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+
+ENTRY(fast_unaligned)
+
+ /* Note: We don't expect the address to be aligned on a word
+ * boundary. After all, the processor generated that exception
+ * and it would be a hardware fault.
+ */
+
+ /* Save some working register */
+
+ s32i a4, a2, PT_AREG4
+ s32i a5, a2, PT_AREG5
+ s32i a6, a2, PT_AREG6
+ s32i a7, a2, PT_AREG7
+ s32i a8, a2, PT_AREG8
+
+ rsr a0, DEPC
+ xsr a3, EXCSAVE_1
+ s32i a0, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+
+ /* Keep value of SAR in a0 */
+
+ rsr a0, SAR
+ rsr a8, EXCVADDR # load unaligned memory address
+
+ /* Now, identify one of the following load/store instructions.
+ *
+ * The only possible danger of a double exception on the
+ * following l32i instructions is kernel code in vmalloc
+ * memory. The processor was just executing at the EPC_1
+ * address, and indeed, already fetched the instruction. That
+ * guarantees a TLB mapping, which hasn't been replaced by
+ * this unaligned exception handler that uses only static TLB
+ * mappings. However, high-level interrupt handlers might
+ * modify TLB entries, so for the generic case, we register a
+ * TABLE_FIXUP handler here, too.
+ */
+
+ /* a3...a6 saved on stack, a2 = SP */
+
+ /* Extract the instruction that caused the unaligned access. */
+
+ rsr a7, EPC_1 # load exception address
+ movi a3, ~3
+ and a3, a3, a7 # mask lower bits
+
+ l32i a4, a3, 0 # load 2 words
+ l32i a5, a3, 4
+
+ __ssa8 a7
+ __src_b a4, a4, a5 # a4 has the instruction
+
+ /* Analyze the instruction (load or store?). */
+
+ extui a5, a4, INSN_OP0, 4 # get insn.op0 nibble
+
+#if XCHAL_HAVE_DENSITY
+ _beqi a5, OP0_L32I_N, .Lload # L32I.N, jump
+ addi a6, a5, -OP0_S32I_N
+ _beqz a6, .Lstore # S32I.N, do a store
+#endif
+ /* 'store indicator bit' not set, jump */
+ _bbci.l a4, OP1_SI_BIT + INSN_OP1, .Lload
+
+ /* Store: Jump to table entry to get the value in the source register.*/
+
+.Lstore:movi a5, .Lstore_table # table
+ extui a6, a4, INSN_T, 4 # get source register
+ addx8 a5, a6, a5
+ jx a5 # jump into table
+
+ /* Invalid instruction, CRITICAL! */
+.Linvalid_instruction_load:
+ j .Linvalid_instruction
+
+ /* Load: Load memory address. */
+
+.Lload: movi a3, ~3
+ and a3, a3, a8 # align memory address
+
+ __ssa8 a8
+#ifdef UNALIGNED_USER_EXCEPTION
+ addi a3, a3, 8
+ l32e a5, a3, -8
+ l32e a6, a3, -4
+#else
+ l32i a5, a3, 0
+ l32i a6, a3, 4
+#endif
+ __src_b a3, a5, a6 # a3 has the data word
+
+#if XCHAL_HAVE_DENSITY
+ addi a7, a7, 2 # increment PC (assume 16-bit insn)
+
+ extui a5, a4, INSN_OP0, 4
+ _beqi a5, OP0_L32I_N, 1f # l32i.n: jump
+
+ addi a7, a7, 1
+#else
+ addi a7, a7, 3
+#endif
+
+ extui a5, a4, INSN_OP1, 4
+ _beqi a5, OP1_L32I, 1f # l32i: jump
+
+ extui a3, a3, 0, 16 # extract lower 16 bits
+ _beqi a5, OP1_L16UI, 1f
+ addi a5, a5, -OP1_L16SI
+ _bnez a5, .Linvalid_instruction_load
+
+ /* sign extend value */
+
+ slli a3, a3, 16
+ srai a3, a3, 16
+
+ /* Set target register. */
+
+1:
+
+#if XCHAL_HAVE_LOOPS
+ rsr a5, LEND # check if we reached LEND
+ bne a7, a5, 1f
+ rsr a5, LCOUNT # and LCOUNT != 0
+ beqz a5, 1f
+ addi a5, a5, -1 # decrement LCOUNT and set
+ rsr a7, LBEG # set PC to LBEGIN
+ wsr a5, LCOUNT
+#endif
+
+1: wsr a7, EPC_1 # skip load instruction
+ extui a4, a4, INSN_T, 4 # extract target register
+ movi a5, .Lload_table
+ addx8 a4, a4, a5
+ jx a4 # jump to entry for target register
+
+ .align 8
+.Lload_table:
+ s32i a3, a2, PT_AREG0; _j .Lexit; .align 8
+ mov a1, a3; _j .Lexit; .align 8 # fishy??
+ s32i a3, a2, PT_AREG2; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG3; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG4; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG5; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG6; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG7; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG8; _j .Lexit; .align 8
+ mov a9, a3 ; _j .Lexit; .align 8
+ mov a10, a3 ; _j .Lexit; .align 8
+ mov a11, a3 ; _j .Lexit; .align 8
+ mov a12, a3 ; _j .Lexit; .align 8
+ mov a13, a3 ; _j .Lexit; .align 8
+ mov a14, a3 ; _j .Lexit; .align 8
+ mov a15, a3 ; _j .Lexit; .align 8
+
+.Lstore_table:
+ l32i a3, a2, PT_AREG0; _j 1f; .align 8
+ mov a3, a1; _j 1f; .align 8 # fishy??
+ l32i a3, a2, PT_AREG2; _j 1f; .align 8
+ l32i a3, a2, PT_AREG3; _j 1f; .align 8
+ l32i a3, a2, PT_AREG4; _j 1f; .align 8
+ l32i a3, a2, PT_AREG5; _j 1f; .align 8
+ l32i a3, a2, PT_AREG6; _j 1f; .align 8
+ l32i a3, a2, PT_AREG7; _j 1f; .align 8
+ l32i a3, a2, PT_AREG8; _j 1f; .align 8
+ mov a3, a9 ; _j 1f; .align 8
+ mov a3, a10 ; _j 1f; .align 8
+ mov a3, a11 ; _j 1f; .align 8
+ mov a3, a12 ; _j 1f; .align 8
+ mov a3, a13 ; _j 1f; .align 8
+ mov a3, a14 ; _j 1f; .align 8
+ mov a3, a15 ; _j 1f; .align 8
+
+1: # a7: instruction pointer, a4: instruction, a3: value
+
+ movi a6, 0 # mask: ffffffff:00000000
+
+#if XCHAL_HAVE_DENSITY
+ addi a7, a7, 2 # incr. PC,assume 16-bit instruction
+
+ extui a5, a4, INSN_OP0, 4 # extract OP0
+ addi a5, a5, -OP0_S32I_N
+ _beqz a5, 1f # s32i.n: jump
+
+ addi a7, a7, 1 # increment PC, 32-bit instruction
+#else
+ addi a7, a7, 3 # increment PC, 32-bit instruction
+#endif
+
+ extui a5, a4, INSN_OP1, 4 # extract OP1
+ _beqi a5, OP1_S32I, 1f # jump if 32 bit store
+ _bnei a5, OP1_S16I, .Linvalid_instruction_store
+
+ movi a5, -1
+ __extl a3, a3 # get 16-bit value
+ __exth a6, a5 # get 16-bit mask ffffffff:ffff0000
+
+ /* Get memory address */
+
+1:
+#if XCHAL_HAVE_LOOPS
+ rsr a4, LEND # check if we reached LEND
+ bne a7, a4, 1f
+ rsr a4, LCOUNT # and LCOUNT != 0
+ beqz a4, 1f
+ addi a4, a4, -1 # decrement LCOUNT and set
+ rsr a7, LBEG # set PC to LBEGIN
+ wsr a4, LCOUNT
+#endif
+
+1: wsr a7, EPC_1 # skip store instruction
+ movi a4, ~3
+ and a4, a4, a8 # align memory address
+
+ /* Insert value into memory */
+
+ movi a5, -1 # mask: ffffffff:XXXX0000
+#ifdef UNALIGNED_USER_EXCEPTION
+ addi a4, a4, 8
+#endif
+
+ __ssa8r a8
+ __src_b a7, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
+ __src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE)
+#ifdef UNALIGNED_USER_EXCEPTION
+ l32e a5, a4, -8
+#else
+ l32i a5, a4, 0 # load lower address word
+#endif
+ and a5, a5, a7 # mask
+ __sh a7, a3 # shift value
+ or a5, a5, a7 # or with original value
+#ifdef UNALIGNED_USER_EXCEPTION
+ s32e a5, a4, -8
+ l32e a7, a4, -4
+#else
+ s32i a5, a4, 0 # store
+ l32i a7, a4, 4 # same for upper address word
+#endif
+ __sl a5, a3
+ and a6, a7, a6
+ or a6, a6, a5
+#ifdef UNALIGNED_USER_EXCEPTION
+ s32e a6, a4, -4
+#else
+ s32i a6, a4, 4
+#endif
+
+ /* Done. restore stack and return */
+
+.Lexit:
+ movi a4, 0
+ rsr a3, EXCSAVE_1
+ s32i a4, a3, EXC_TABLE_FIXUP
+
+ /* Restore working register */
+
+ l32i a8, a2, PT_AREG8
+ l32i a7, a2, PT_AREG7
+ l32i a6, a2, PT_AREG6
+ l32i a5, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ l32i a3, a2, PT_AREG3
+
+ /* restore SAR and return */
+
+ wsr a0, SAR
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_AREG2
+ rfe
+
+ /* We cannot handle this exception. */
+
+ .extern _kernel_exception
+.Linvalid_instruction_store:
+.Linvalid_instruction:
+
+ /* Restore a4...a8 and SAR, set SP, and jump to default exception. */
+
+ l32i a8, a2, PT_AREG8
+ l32i a7, a2, PT_AREG7
+ l32i a6, a2, PT_AREG6
+ l32i a5, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ wsr a0, SAR
+ mov a1, a2
+
+ rsr a0, PS
+ bbsi.l a2, PS_UM_BIT, 1f # jump if user mode
+
+ movi a0, _kernel_exception
+ jx a0
+
+1: movi a0, _user_exception
+ jx a0
+
+
+#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */
+
diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c
new file mode 100644
index 00000000..7dc3f915
--- /dev/null
+++ b/arch/xtensa/kernel/asm-offsets.c
@@ -0,0 +1,111 @@
+/*
+ * arch/xtensa/kernel/asm-offsets.c
+ *
+ * Generates definitions from c-type structures used by assembly sources.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <asm/processor.h>
+#include <asm/coprocessor.h>
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/thread_info.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/kbuild.h>
+
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+
+int main(void)
+{
+ /* struct pt_regs */
+ DEFINE(PT_PC, offsetof (struct pt_regs, pc));
+ DEFINE(PT_PS, offsetof (struct pt_regs, ps));
+ DEFINE(PT_DEPC, offsetof (struct pt_regs, depc));
+ DEFINE(PT_EXCCAUSE, offsetof (struct pt_regs, exccause));
+ DEFINE(PT_EXCVADDR, offsetof (struct pt_regs, excvaddr));
+ DEFINE(PT_DEBUGCAUSE, offsetof (struct pt_regs, debugcause));
+ DEFINE(PT_WMASK, offsetof (struct pt_regs, wmask));
+ DEFINE(PT_LBEG, offsetof (struct pt_regs, lbeg));
+ DEFINE(PT_LEND, offsetof (struct pt_regs, lend));
+ DEFINE(PT_LCOUNT, offsetof (struct pt_regs, lcount));
+ DEFINE(PT_SAR, offsetof (struct pt_regs, sar));
+ DEFINE(PT_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel));
+ DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
+ DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0]));
+ DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0]));
+ DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1]));
+ DEFINE(PT_AREG2, offsetof (struct pt_regs, areg[2]));
+ DEFINE(PT_AREG3, offsetof (struct pt_regs, areg[3]));
+ DEFINE(PT_AREG4, offsetof (struct pt_regs, areg[4]));
+ DEFINE(PT_AREG5, offsetof (struct pt_regs, areg[5]));
+ DEFINE(PT_AREG6, offsetof (struct pt_regs, areg[6]));
+ DEFINE(PT_AREG7, offsetof (struct pt_regs, areg[7]));
+ DEFINE(PT_AREG8, offsetof (struct pt_regs, areg[8]));
+ DEFINE(PT_AREG9, offsetof (struct pt_regs, areg[9]));
+ DEFINE(PT_AREG10, offsetof (struct pt_regs, areg[10]));
+ DEFINE(PT_AREG11, offsetof (struct pt_regs, areg[11]));
+ DEFINE(PT_AREG12, offsetof (struct pt_regs, areg[12]));
+ DEFINE(PT_AREG13, offsetof (struct pt_regs, areg[13]));
+ DEFINE(PT_AREG14, offsetof (struct pt_regs, areg[14]));
+ DEFINE(PT_AREG15, offsetof (struct pt_regs, areg[15]));
+ DEFINE(PT_WINDOWBASE, offsetof (struct pt_regs, windowbase));
+ DEFINE(PT_WINDOWSTART, offsetof(struct pt_regs, windowstart));
+ DEFINE(PT_SIZE, sizeof(struct pt_regs));
+ DEFINE(PT_AREG_END, offsetof (struct pt_regs, areg[XCHAL_NUM_AREGS]));
+ DEFINE(PT_USER_SIZE, offsetof(struct pt_regs, areg[XCHAL_NUM_AREGS]));
+ DEFINE(PT_XTREGS_OPT, offsetof(struct pt_regs, xtregs_opt));
+ DEFINE(XTREGS_OPT_SIZE, sizeof(xtregs_opt_t));
+
+ /* struct task_struct */
+ DEFINE(TASK_PTRACE, offsetof (struct task_struct, ptrace));
+ DEFINE(TASK_MM, offsetof (struct task_struct, mm));
+ DEFINE(TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm));
+ DEFINE(TASK_PID, offsetof (struct task_struct, pid));
+ DEFINE(TASK_THREAD, offsetof (struct task_struct, thread));
+ DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, stack));
+ DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct));
+
+ /* struct thread_info (offset from start_struct) */
+ DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra));
+ DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp));
+ DEFINE(THREAD_CPENABLE, offsetof (struct thread_info, cpenable));
+#if XTENSA_HAVE_COPROCESSORS
+ DEFINE(THREAD_XTREGS_CP0, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP1, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP2, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP3, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP4, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP5, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP6, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP7, offsetof (struct thread_info, xtregs_cp));
+#endif
+ DEFINE(THREAD_XTREGS_USER, offsetof (struct thread_info, xtregs_user));
+ DEFINE(XTREGS_USER_SIZE, sizeof(xtregs_user_t));
+ DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds));
+
+ /* struct mm_struct */
+ DEFINE(MM_USERS, offsetof(struct mm_struct, mm_users));
+ DEFINE(MM_PGD, offsetof (struct mm_struct, pgd));
+ DEFINE(MM_CONTEXT, offsetof (struct mm_struct, context));
+
+ /* struct page */
+ DEFINE(PAGE_FLAGS, offsetof(struct page, flags));
+
+ /* constants */
+ DEFINE(_CLONE_VM, CLONE_VM);
+ DEFINE(_CLONE_UNTRACED, CLONE_UNTRACED);
+ DEFINE(PG_ARCH_1, PG_arch_1);
+
+ return 0;
+}
+
diff --git a/arch/xtensa/kernel/coprocessor.S b/arch/xtensa/kernel/coprocessor.S
new file mode 100644
index 00000000..2bc1e145
--- /dev/null
+++ b/arch/xtensa/kernel/coprocessor.S
@@ -0,0 +1,335 @@
+/*
+ * arch/xtensa/kernel/coprocessor.S
+ *
+ * Xtensa processor configuration-specific table of coprocessor and
+ * other custom register layout information.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 - 2007 Tensilica Inc.
+ */
+
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/processor.h>
+#include <asm/coprocessor.h>
+#include <asm/thread_info.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+#include <asm/tlbflush.h>
+
+/*
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+/* IO protection is currently unsupported. */
+
+ENTRY(fast_io_protect)
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+#if XTENSA_HAVE_COPROCESSORS
+
+/*
+ * Macros for lazy context switch.
+ */
+
+#define SAVE_CP_REGS(x) \
+ .align 4; \
+ .Lsave_cp_regs_cp##x: \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ xchal_cp##x##_store a2 a4 a5 a6 a7; \
+ .endif; \
+ jx a0
+
+#define SAVE_CP_REGS_TAB(x) \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ .long .Lsave_cp_regs_cp##x - .Lsave_cp_regs_jump_table; \
+ .else; \
+ .long 0; \
+ .endif; \
+ .long THREAD_XTREGS_CP##x
+
+
+#define LOAD_CP_REGS(x) \
+ .align 4; \
+ .Lload_cp_regs_cp##x: \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ xchal_cp##x##_load a2 a4 a5 a6 a7; \
+ .endif; \
+ jx a0
+
+#define LOAD_CP_REGS_TAB(x) \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ .long .Lload_cp_regs_cp##x - .Lload_cp_regs_jump_table; \
+ .else; \
+ .long 0; \
+ .endif; \
+ .long THREAD_XTREGS_CP##x
+
+ SAVE_CP_REGS(0)
+ SAVE_CP_REGS(1)
+ SAVE_CP_REGS(2)
+ SAVE_CP_REGS(3)
+ SAVE_CP_REGS(4)
+ SAVE_CP_REGS(5)
+ SAVE_CP_REGS(6)
+ SAVE_CP_REGS(7)
+
+ LOAD_CP_REGS(0)
+ LOAD_CP_REGS(1)
+ LOAD_CP_REGS(2)
+ LOAD_CP_REGS(3)
+ LOAD_CP_REGS(4)
+ LOAD_CP_REGS(5)
+ LOAD_CP_REGS(6)
+ LOAD_CP_REGS(7)
+
+ .align 4
+.Lsave_cp_regs_jump_table:
+ SAVE_CP_REGS_TAB(0)
+ SAVE_CP_REGS_TAB(1)
+ SAVE_CP_REGS_TAB(2)
+ SAVE_CP_REGS_TAB(3)
+ SAVE_CP_REGS_TAB(4)
+ SAVE_CP_REGS_TAB(5)
+ SAVE_CP_REGS_TAB(6)
+ SAVE_CP_REGS_TAB(7)
+
+.Lload_cp_regs_jump_table:
+ LOAD_CP_REGS_TAB(0)
+ LOAD_CP_REGS_TAB(1)
+ LOAD_CP_REGS_TAB(2)
+ LOAD_CP_REGS_TAB(3)
+ LOAD_CP_REGS_TAB(4)
+ LOAD_CP_REGS_TAB(5)
+ LOAD_CP_REGS_TAB(6)
+ LOAD_CP_REGS_TAB(7)
+
+/*
+ * coprocessor_save(buffer, index)
+ * a2 a3
+ * coprocessor_load(buffer, index)
+ * a2 a3
+ *
+ * Save or load coprocessor registers for coprocessor 'index'.
+ * The register values are saved to or loaded from them 'buffer' address.
+ *
+ * Note that these functions don't update the coprocessor_owner information!
+ *
+ */
+
+ENTRY(coprocessor_save)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lsave_cp_regs_jump_table
+ addx8 a3, a3, a0
+ l32i a3, a3, 0
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
+ retw
+
+ENTRY(coprocessor_load)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lload_cp_regs_jump_table
+ addx4 a3, a3, a0
+ l32i a3, a3, 0
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
+ retw
+
+/*
+ * coprocessor_flush(struct task_info*, index)
+ * a2 a3
+ * coprocessor_restore(struct task_info*, index)
+ * a2 a3
+ *
+ * Save or load coprocessor registers for coprocessor 'index'.
+ * The register values are saved to or loaded from the coprocessor area
+ * inside the task_info structure.
+ *
+ * Note that these functions don't update the coprocessor_owner information!
+ *
+ */
+
+
+ENTRY(coprocessor_flush)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lsave_cp_regs_jump_table
+ addx8 a3, a3, a0
+ l32i a4, a3, 4
+ l32i a3, a3, 0
+ add a2, a2, a4
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
+ retw
+
+ENTRY(coprocessor_restore)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lload_cp_regs_jump_table
+ addx4 a3, a3, a0
+ l32i a4, a3, 4
+ l32i a3, a3, 0
+ add a2, a2, a4
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
+ retw
+
+/*
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+ENTRY(fast_coprocessor_double)
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+
+ENTRY(fast_coprocessor)
+
+ /* Save remaining registers a1-a3 and SAR */
+
+ xsr a3, EXCSAVE_1
+ s32i a3, a2, PT_AREG3
+ rsr a3, SAR
+ s32i a1, a2, PT_AREG1
+ s32i a3, a2, PT_SAR
+ mov a1, a2
+ rsr a2, DEPC
+ s32i a2, a1, PT_AREG2
+
+ /*
+ * The hal macros require up to 4 temporary registers. We use a3..a6.
+ */
+
+ s32i a4, a1, PT_AREG4
+ s32i a5, a1, PT_AREG5
+ s32i a6, a1, PT_AREG6
+
+ /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
+
+ rsr a3, EXCCAUSE
+ addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
+
+ /* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/
+
+ ssl a3 # SAR: 32 - coprocessor_number
+ movi a2, 1
+ rsr a0, CPENABLE
+ sll a2, a2
+ or a0, a0, a2
+ wsr a0, CPENABLE
+ rsync
+
+ /* Retrieve previous owner. (a3 still holds CP number) */
+
+ movi a0, coprocessor_owner # list of owners
+ addx4 a0, a3, a0 # entry for CP
+ l32i a4, a0, 0
+
+ beqz a4, 1f # skip 'save' if no previous owner
+
+ /* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
+
+ l32i a5, a4, THREAD_CPENABLE
+ xor a5, a5, a2 # (1 << cp-id) still in a2
+ s32i a5, a4, THREAD_CPENABLE
+
+ /*
+ * Get context save area and 'call' save routine.
+ * (a4 still holds previous owner (thread_info), a3 CP number)
+ */
+
+ movi a5, .Lsave_cp_regs_jump_table
+ movi a0, 2f # a0: 'return' address
+ addx8 a3, a3, a5 # a3: coprocessor number
+ l32i a2, a3, 4 # a2: xtregs offset
+ l32i a3, a3, 0 # a3: jump offset
+ add a2, a2, a4
+ add a4, a3, a5 # a4: address of save routine
+ jx a4
+
+ /* Note that only a0 and a1 were preserved. */
+
+2: rsr a3, EXCCAUSE
+ addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
+ movi a0, coprocessor_owner
+ addx4 a0, a3, a0
+
+ /* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */
+
+1: GET_THREAD_INFO (a4, a1)
+ s32i a4, a0, 0
+
+ /* Get context save area and 'call' load routine. */
+
+ movi a5, .Lload_cp_regs_jump_table
+ movi a0, 1f
+ addx8 a3, a3, a5
+ l32i a2, a3, 4 # a2: xtregs offset
+ l32i a3, a3, 0 # a3: jump offset
+ add a2, a2, a4
+ add a4, a3, a5
+ jx a4
+
+ /* Restore all registers and return from exception handler. */
+
+1: l32i a6, a1, PT_AREG6
+ l32i a5, a1, PT_AREG5
+ l32i a4, a1, PT_AREG4
+
+ l32i a0, a1, PT_SAR
+ l32i a3, a1, PT_AREG3
+ l32i a2, a1, PT_AREG2
+ wsr a0, SAR
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+
+ rfe
+
+ .data
+ENTRY(coprocessor_owner)
+ .fill XCHAL_CP_MAX, 4, 0
+
+#endif /* XTENSA_HAVE_COPROCESSORS */
+
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
new file mode 100644
index 00000000..6223f334
--- /dev/null
+++ b/arch/xtensa/kernel/entry.S
@@ -0,0 +1,1960 @@
+/*
+ * arch/xtensa/kernel/entry.S
+ *
+ * Low-level exception handling
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004-2007 by Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/processor.h>
+#include <asm/coprocessor.h>
+#include <asm/thread_info.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+#include <asm/tlbflush.h>
+#include <variant/tie-asm.h>
+
+/* Unimplemented features. */
+
+#undef KERNEL_STACK_OVERFLOW_CHECK
+#undef PREEMPTIBLE_KERNEL
+#undef ALLOCA_EXCEPTION_IN_IRAM
+
+/* Not well tested.
+ *
+ * - fast_coprocessor
+ */
+
+/*
+ * Macro to find first bit set in WINDOWBASE from the left + 1
+ *
+ * 100....0 -> 1
+ * 010....0 -> 2
+ * 000....1 -> WSBITS
+ */
+
+ .macro ffs_ws bit mask
+
+#if XCHAL_HAVE_NSA
+ nsau \bit, \mask # 32-WSBITS ... 31 (32 iff 0)
+ addi \bit, \bit, WSBITS - 32 + 1 # uppest bit set -> return 1
+#else
+ movi \bit, WSBITS
+#if WSBITS > 16
+ _bltui \mask, 0x10000, 99f
+ addi \bit, \bit, -16
+ extui \mask, \mask, 16, 16
+#endif
+#if WSBITS > 8
+99: _bltui \mask, 0x100, 99f
+ addi \bit, \bit, -8
+ srli \mask, \mask, 8
+#endif
+99: _bltui \mask, 0x10, 99f
+ addi \bit, \bit, -4
+ srli \mask, \mask, 4
+99: _bltui \mask, 0x4, 99f
+ addi \bit, \bit, -2
+ srli \mask, \mask, 2
+99: _bltui \mask, 0x2, 99f
+ addi \bit, \bit, -1
+99:
+
+#endif
+ .endm
+
+/* ----------------- DEFAULT FIRST LEVEL EXCEPTION HANDLERS ----------------- */
+
+/*
+ * First-level exception handler for user exceptions.
+ * Save some special registers, extra states and all registers in the AR
+ * register file that were in use in the user task, and jump to the common
+ * exception code.
+ * We save SAR (used to calculate WMASK), and WB and WS (we don't have to
+ * save them for kernel exceptions).
+ *
+ * Entry condition for user_exception:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original value in depc
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Entry condition for _user_exception:
+ *
+ * a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC
+ * excsave has been restored, and
+ * stack pointer (a1) has been set.
+ *
+ * Note: _user_exception might be at an odd address. Don't use call0..call12
+ */
+
+ENTRY(user_exception)
+
+ /* Save a2, a3, and depc, restore excsave_1 and set SP. */
+
+ xsr a3, EXCSAVE_1
+ rsr a0, DEPC
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ mov a1, a2
+
+ .globl _user_exception
+_user_exception:
+
+ /* Save SAR and turn off single stepping */
+
+ movi a2, 0
+ rsr a3, SAR
+ xsr a2, ICOUNTLEVEL
+ s32i a3, a1, PT_SAR
+ s32i a2, a1, PT_ICOUNTLEVEL
+
+ /* Rotate ws so that the current windowbase is at bit0. */
+ /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */
+
+ rsr a2, WINDOWBASE
+ rsr a3, WINDOWSTART
+ ssr a2
+ s32i a2, a1, PT_WINDOWBASE
+ s32i a3, a1, PT_WINDOWSTART
+ slli a2, a3, 32-WSBITS
+ src a2, a3, a2
+ srli a2, a2, 32-WSBITS
+ s32i a2, a1, PT_WMASK # needed for restoring registers
+
+ /* Save only live registers. */
+
+ _bbsi.l a2, 1, 1f
+ s32i a4, a1, PT_AREG4
+ s32i a5, a1, PT_AREG5
+ s32i a6, a1, PT_AREG6
+ s32i a7, a1, PT_AREG7
+ _bbsi.l a2, 2, 1f
+ s32i a8, a1, PT_AREG8
+ s32i a9, a1, PT_AREG9
+ s32i a10, a1, PT_AREG10
+ s32i a11, a1, PT_AREG11
+ _bbsi.l a2, 3, 1f
+ s32i a12, a1, PT_AREG12
+ s32i a13, a1, PT_AREG13
+ s32i a14, a1, PT_AREG14
+ s32i a15, a1, PT_AREG15
+ _bnei a2, 1, 1f # only one valid frame?
+
+ /* Only one valid frame, skip saving regs. */
+
+ j 2f
+
+ /* Save the remaining registers.
+ * We have to save all registers up to the first '1' from
+ * the right, except the current frame (bit 0).
+ * Assume a2 is: 001001000110001
+ * All register frames starting from the top field to the marked '1'
+ * must be saved.
+ */
+
+1: addi a3, a2, -1 # eliminate '1' in bit 0: yyyyxxww0
+ neg a3, a3 # yyyyxxww0 -> YYYYXXWW1+1
+ and a3, a3, a2 # max. only one bit is set
+
+ /* Find number of frames to save */
+
+ ffs_ws a0, a3 # number of frames to the '1' from left
+
+ /* Store information into WMASK:
+ * bits 0..3: xxx1 masked lower 4 bits of the rotated windowstart,
+ * bits 4...: number of valid 4-register frames
+ */
+
+ slli a3, a0, 4 # number of frames to save in bits 8..4
+ extui a2, a2, 0, 4 # mask for the first 16 registers
+ or a2, a3, a2
+ s32i a2, a1, PT_WMASK # needed when we restore the reg-file
+
+ /* Save 4 registers at a time */
+
+1: rotw -1
+ s32i a0, a5, PT_AREG_END - 16
+ s32i a1, a5, PT_AREG_END - 12
+ s32i a2, a5, PT_AREG_END - 8
+ s32i a3, a5, PT_AREG_END - 4
+ addi a0, a4, -1
+ addi a1, a5, -16
+ _bnez a0, 1b
+
+ /* WINDOWBASE still in SAR! */
+
+ rsr a2, SAR # original WINDOWBASE
+ movi a3, 1
+ ssl a2
+ sll a3, a3
+ wsr a3, WINDOWSTART # set corresponding WINDOWSTART bit
+ wsr a2, WINDOWBASE # and WINDOWSTART
+ rsync
+
+ /* We are back to the original stack pointer (a1) */
+
+2: /* Now, jump to the common exception handler. */
+
+ j common_exception
+
+
+/*
+ * First-level exit handler for kernel exceptions
+ * Save special registers and the live window frame.
+ * Note: Even though we changes the stack pointer, we don't have to do a
+ * MOVSP here, as we do that when we return from the exception.
+ * (See comment in the kernel exception exit code)
+ *
+ * Entry condition for kernel_exception:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Entry condition for _kernel_exception:
+ *
+ * a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC
+ * excsave has been restored, and
+ * stack pointer (a1) has been set.
+ *
+ * Note: _kernel_exception might be at an odd address. Don't use call0..call12
+ */
+
+ENTRY(kernel_exception)
+
+ /* Save a0, a2, a3, DEPC and set SP. */
+
+ xsr a3, EXCSAVE_1 # restore a3, excsave_1
+ rsr a0, DEPC # get a2
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ mov a1, a2
+
+ .globl _kernel_exception
+_kernel_exception:
+
+ /* Save SAR and turn off single stepping */
+
+ movi a2, 0
+ rsr a3, SAR
+ xsr a2, ICOUNTLEVEL
+ s32i a3, a1, PT_SAR
+ s32i a2, a1, PT_ICOUNTLEVEL
+
+ /* Rotate ws so that the current windowbase is at bit0. */
+ /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */
+
+ rsr a2, WINDOWBASE # don't need to save these, we only
+ rsr a3, WINDOWSTART # need shifted windowstart: windowmask
+ ssr a2
+ slli a2, a3, 32-WSBITS
+ src a2, a3, a2
+ srli a2, a2, 32-WSBITS
+ s32i a2, a1, PT_WMASK # needed for kernel_exception_exit
+
+ /* Save only the live window-frame */
+
+ _bbsi.l a2, 1, 1f
+ s32i a4, a1, PT_AREG4
+ s32i a5, a1, PT_AREG5
+ s32i a6, a1, PT_AREG6
+ s32i a7, a1, PT_AREG7
+ _bbsi.l a2, 2, 1f
+ s32i a8, a1, PT_AREG8
+ s32i a9, a1, PT_AREG9
+ s32i a10, a1, PT_AREG10
+ s32i a11, a1, PT_AREG11
+ _bbsi.l a2, 3, 1f
+ s32i a12, a1, PT_AREG12
+ s32i a13, a1, PT_AREG13
+ s32i a14, a1, PT_AREG14
+ s32i a15, a1, PT_AREG15
+
+1:
+
+#ifdef KERNEL_STACK_OVERFLOW_CHECK
+
+ /* Stack overflow check, for debugging */
+ extui a2, a1, TASK_SIZE_BITS,XX
+ movi a3, SIZE??
+ _bge a2, a3, out_of_stack_panic
+
+#endif
+
+/*
+ * This is the common exception handler.
+ * We get here from the user exception handler or simply by falling through
+ * from the kernel exception handler.
+ * Save the remaining special registers, switch to kernel mode, and jump
+ * to the second-level exception handler.
+ *
+ */
+
+common_exception:
+
+ /* Save some registers, disable loops and clear the syscall flag. */
+
+ rsr a2, DEBUGCAUSE
+ rsr a3, EPC_1
+ s32i a2, a1, PT_DEBUGCAUSE
+ s32i a3, a1, PT_PC
+
+ movi a2, -1
+ rsr a3, EXCVADDR
+ s32i a2, a1, PT_SYSCALL
+ movi a2, 0
+ s32i a3, a1, PT_EXCVADDR
+ xsr a2, LCOUNT
+ s32i a2, a1, PT_LCOUNT
+
+ /* It is now save to restore the EXC_TABLE_FIXUP variable. */
+
+ rsr a0, EXCCAUSE
+ movi a3, 0
+ rsr a2, EXCSAVE_1
+ s32i a0, a1, PT_EXCCAUSE
+ s32i a3, a2, EXC_TABLE_FIXUP
+
+ /* All unrecoverable states are saved on stack, now, and a1 is valid,
+ * so we can allow exceptions and interrupts (*) again.
+ * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
+ *
+ * (*) We only allow interrupts if PS.INTLEVEL was not set to 1 before
+ * (interrupts disabled) and if this exception is not an interrupt.
+ */
+
+ rsr a3, PS
+ addi a0, a0, -4
+ movi a2, 1
+ extui a3, a3, 0, 1 # a3 = PS.INTLEVEL[0]
+ moveqz a3, a2, a0 # a3 = 1 iff interrupt exception
+ movi a2, 1 << PS_WOE_BIT
+ or a3, a3, a2
+ rsr a0, EXCCAUSE
+ xsr a3, PS
+
+ s32i a3, a1, PT_PS # save ps
+
+ /* Save LBEG, LEND */
+
+ rsr a2, LBEG
+ rsr a3, LEND
+ s32i a2, a1, PT_LBEG
+ s32i a3, a1, PT_LEND
+
+ /* Save optional registers. */
+
+ save_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT
+
+ /* Go to second-level dispatcher. Set up parameters to pass to the
+ * exception handler and call the exception handler.
+ */
+
+ movi a4, exc_table
+ mov a6, a1 # pass stack frame
+ mov a7, a0 # pass EXCCAUSE
+ addx4 a4, a0, a4
+ l32i a4, a4, EXC_TABLE_DEFAULT # load handler
+
+ /* Call the second-level handler */
+
+ callx4 a4
+
+ /* Jump here for exception exit */
+
+common_exception_return:
+
+ /* Jump if we are returning from kernel exceptions. */
+
+1: l32i a3, a1, PT_PS
+ _bbci.l a3, PS_UM_BIT, 4f
+
+ /* Specific to a user exception exit:
+ * We need to check some flags for signal handling and rescheduling,
+ * and have to restore WB and WS, extra states, and all registers
+ * in the register file that were in use in the user task.
+ * Note that we don't disable interrupts here.
+ */
+
+ GET_THREAD_INFO(a2,a1)
+ l32i a4, a2, TI_FLAGS
+
+ _bbsi.l a4, TIF_NEED_RESCHED, 3f
+ _bbci.l a4, TIF_SIGPENDING, 4f
+
+ l32i a4, a1, PT_DEPC
+ bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f
+
+ /* Call do_signal() */
+
+ movi a4, do_signal # int do_signal(struct pt_regs*, sigset_t*)
+ mov a6, a1
+ movi a7, 0
+ callx4 a4
+ j 1b
+
+3: /* Reschedule */
+
+ movi a4, schedule # void schedule (void)
+ callx4 a4
+ j 1b
+
+4: /* Restore optional registers. */
+
+ load_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT
+
+ wsr a3, PS /* disable interrupts */
+
+ _bbci.l a3, PS_UM_BIT, kernel_exception_exit
+
+user_exception_exit:
+
+ /* Restore the state of the task and return from the exception. */
+
+ /* Switch to the user thread WINDOWBASE. Save SP temporarily in DEPC */
+
+ l32i a2, a1, PT_WINDOWBASE
+ l32i a3, a1, PT_WINDOWSTART
+ wsr a1, DEPC # use DEPC as temp storage
+ wsr a3, WINDOWSTART # restore WINDOWSTART
+ ssr a2 # preserve user's WB in the SAR
+ wsr a2, WINDOWBASE # switch to user's saved WB
+ rsync
+ rsr a1, DEPC # restore stack pointer
+ l32i a2, a1, PT_WMASK # register frames saved (in bits 4...9)
+ rotw -1 # we restore a4..a7
+ _bltui a6, 16, 1f # only have to restore current window?
+
+ /* The working registers are a0 and a3. We are restoring to
+ * a4..a7. Be careful not to destroy what we have just restored.
+ * Note: wmask has the format YYYYM:
+ * Y: number of registers saved in groups of 4
+ * M: 4 bit mask of first 16 registers
+ */
+
+ mov a2, a6
+ mov a3, a5
+
+2: rotw -1 # a0..a3 become a4..a7
+ addi a3, a7, -4*4 # next iteration
+ addi a2, a6, -16 # decrementing Y in WMASK
+ l32i a4, a3, PT_AREG_END + 0
+ l32i a5, a3, PT_AREG_END + 4
+ l32i a6, a3, PT_AREG_END + 8
+ l32i a7, a3, PT_AREG_END + 12
+ _bgeui a2, 16, 2b
+
+ /* Clear unrestored registers (don't leak anything to user-land */
+
+1: rsr a0, WINDOWBASE
+ rsr a3, SAR
+ sub a3, a0, a3
+ beqz a3, 2f
+ extui a3, a3, 0, WBBITS
+
+1: rotw -1
+ addi a3, a7, -1
+ movi a4, 0
+ movi a5, 0
+ movi a6, 0
+ movi a7, 0
+ bgei a3, 1, 1b
+
+ /* We are back were we were when we started.
+ * Note: a2 still contains WMASK (if we've returned to the original
+ * frame where we had loaded a2), or at least the lower 4 bits
+ * (if we have restored WSBITS-1 frames).
+ */
+
+2: j common_exception_exit
+
+ /* This is the kernel exception exit.
+ * We avoided to do a MOVSP when we entered the exception, but we
+ * have to do it here.
+ */
+
+kernel_exception_exit:
+
+#ifdef PREEMPTIBLE_KERNEL
+
+#ifdef CONFIG_PREEMPT
+
+ /*
+ * Note: We've just returned from a call4, so we have
+ * at least 4 addt'l regs.
+ */
+
+ /* Check current_thread_info->preempt_count */
+
+ GET_THREAD_INFO(a2)
+ l32i a3, a2, TI_PREEMPT
+ bnez a3, 1f
+
+ l32i a2, a2, TI_FLAGS
+
+1:
+
+#endif
+
+#endif
+
+ /* Check if we have to do a movsp.
+ *
+ * We only have to do a movsp if the previous window-frame has
+ * been spilled to the *temporary* exception stack instead of the
+ * task's stack. This is the case if the corresponding bit in
+ * WINDOWSTART for the previous window-frame was set before
+ * (not spilled) but is zero now (spilled).
+ * If this bit is zero, all other bits except the one for the
+ * current window frame are also zero. So, we can use a simple test:
+ * 'and' WINDOWSTART and WINDOWSTART-1:
+ *
+ * (XXXXXX1[0]* - 1) AND XXXXXX1[0]* = XXXXXX0[0]*
+ *
+ * The result is zero only if one bit was set.
+ *
+ * (Note: We might have gone through several task switches before
+ * we come back to the current task, so WINDOWBASE might be
+ * different from the time the exception occurred.)
+ */
+
+ /* Test WINDOWSTART before and after the exception.
+ * We actually have WMASK, so we only have to test if it is 1 or not.
+ */
+
+ l32i a2, a1, PT_WMASK
+ _beqi a2, 1, common_exception_exit # Spilled before exception,jump
+
+ /* Test WINDOWSTART now. If spilled, do the movsp */
+
+ rsr a3, WINDOWSTART
+ addi a0, a3, -1
+ and a3, a3, a0
+ _bnez a3, common_exception_exit
+
+ /* Do a movsp (we returned from a call4, so we have at least a0..a7) */
+
+ addi a0, a1, -16
+ l32i a3, a0, 0
+ l32i a4, a0, 4
+ s32i a3, a1, PT_SIZE+0
+ s32i a4, a1, PT_SIZE+4
+ l32i a3, a0, 8
+ l32i a4, a0, 12
+ s32i a3, a1, PT_SIZE+8
+ s32i a4, a1, PT_SIZE+12
+
+ /* Common exception exit.
+ * We restore the special register and the current window frame, and
+ * return from the exception.
+ *
+ * Note: We expect a2 to hold PT_WMASK
+ */
+
+common_exception_exit:
+
+ /* Restore address registers. */
+
+ _bbsi.l a2, 1, 1f
+ l32i a4, a1, PT_AREG4
+ l32i a5, a1, PT_AREG5
+ l32i a6, a1, PT_AREG6
+ l32i a7, a1, PT_AREG7
+ _bbsi.l a2, 2, 1f
+ l32i a8, a1, PT_AREG8
+ l32i a9, a1, PT_AREG9
+ l32i a10, a1, PT_AREG10
+ l32i a11, a1, PT_AREG11
+ _bbsi.l a2, 3, 1f
+ l32i a12, a1, PT_AREG12
+ l32i a13, a1, PT_AREG13
+ l32i a14, a1, PT_AREG14
+ l32i a15, a1, PT_AREG15
+
+ /* Restore PC, SAR */
+
+1: l32i a2, a1, PT_PC
+ l32i a3, a1, PT_SAR
+ wsr a2, EPC_1
+ wsr a3, SAR
+
+ /* Restore LBEG, LEND, LCOUNT */
+
+ l32i a2, a1, PT_LBEG
+ l32i a3, a1, PT_LEND
+ wsr a2, LBEG
+ l32i a2, a1, PT_LCOUNT
+ wsr a3, LEND
+ wsr a2, LCOUNT
+
+ /* We control single stepping through the ICOUNTLEVEL register. */
+
+ l32i a2, a1, PT_ICOUNTLEVEL
+ movi a3, -2
+ wsr a2, ICOUNTLEVEL
+ wsr a3, ICOUNT
+
+ /* Check if it was double exception. */
+
+ l32i a0, a1, PT_DEPC
+ l32i a3, a1, PT_AREG3
+ l32i a2, a1, PT_AREG2
+ _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
+
+ /* Restore a0...a3 and return */
+
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+ rfe
+
+1: wsr a0, DEPC
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+ rfde
+
+/*
+ * Debug exception handler.
+ *
+ * Currently, we don't support KGDB, so only user application can be debugged.
+ *
+ * When we get here, a0 is trashed and saved to excsave[debuglevel]
+ */
+
+ENTRY(debug_exception)
+
+ rsr a0, EPS + XCHAL_DEBUGLEVEL
+ bbsi.l a0, PS_EXCM_BIT, 1f # exception mode
+
+ /* Set EPC_1 and EXCCAUSE */
+
+ wsr a2, DEPC # save a2 temporarily
+ rsr a2, EPC + XCHAL_DEBUGLEVEL
+ wsr a2, EPC_1
+
+ movi a2, EXCCAUSE_MAPPED_DEBUG
+ wsr a2, EXCCAUSE
+
+ /* Restore PS to the value before the debug exc but with PS.EXCM set.*/
+
+ movi a2, 1 << PS_EXCM_BIT
+ or a2, a0, a2
+ movi a0, debug_exception # restore a3, debug jump vector
+ wsr a2, PS
+ xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL
+
+ /* Switch to kernel/user stack, restore jump vector, and save a0 */
+
+ bbsi.l a2, PS_UM_BIT, 2f # jump if user mode
+
+ addi a2, a1, -16-PT_SIZE # assume kernel stack
+ s32i a0, a2, PT_AREG0
+ movi a0, 0
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ xsr a0, DEPC
+ s32i a3, a2, PT_AREG3
+ s32i a0, a2, PT_AREG2
+ mov a1, a2
+ j _kernel_exception
+
+2: rsr a2, EXCSAVE_1
+ l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer
+ s32i a0, a2, PT_AREG0
+ movi a0, 0
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_DEPC
+ xsr a0, DEPC
+ s32i a3, a2, PT_AREG3
+ s32i a0, a2, PT_AREG2
+ mov a1, a2
+ j _user_exception
+
+ /* Debug exception while in exception mode. */
+1: j 1b // FIXME!!
+
+
+/*
+ * We get here in case of an unrecoverable exception.
+ * The only thing we can do is to be nice and print a panic message.
+ * We only produce a single stack frame for panic, so ???
+ *
+ *
+ * Entry conditions:
+ *
+ * - a0 contains the caller address; original value saved in excsave1.
+ * - the original a0 contains a valid return address (backtrace) or 0.
+ * - a2 contains a valid stackpointer
+ *
+ * Notes:
+ *
+ * - If the stack pointer could be invalid, the caller has to setup a
+ * dummy stack pointer (e.g. the stack of the init_task)
+ *
+ * - If the return address could be invalid, the caller has to set it
+ * to 0, so the backtrace would stop.
+ *
+ */
+ .align 4
+unrecoverable_text:
+ .ascii "Unrecoverable error in exception handler\0"
+
+ENTRY(unrecoverable_exception)
+
+ movi a0, 1
+ movi a1, 0
+
+ wsr a0, WINDOWSTART
+ wsr a1, WINDOWBASE
+ rsync
+
+ movi a1, (1 << PS_WOE_BIT) | 1
+ wsr a1, PS
+ rsync
+
+ movi a1, init_task
+ movi a0, 0
+ addi a1, a1, PT_REGS_OFFSET
+
+ movi a4, panic
+ movi a6, unrecoverable_text
+
+ callx4 a4
+
+1: j 1b
+
+
+/* -------------------------- FAST EXCEPTION HANDLERS ----------------------- */
+
+/*
+ * Fast-handler for alloca exceptions
+ *
+ * The ALLOCA handler is entered when user code executes the MOVSP
+ * instruction and the caller's frame is not in the register file.
+ * In this case, the caller frame's a0..a3 are on the stack just
+ * below sp (a1), and this handler moves them.
+ *
+ * For "MOVSP <ar>,<as>" without destination register a1, this routine
+ * simply moves the value from <as> to <ar> without moving the save area.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+#if XCHAL_HAVE_BE
+#define _EXTUI_MOVSP_SRC(ar) extui ar, ar, 4, 4
+#define _EXTUI_MOVSP_DST(ar) extui ar, ar, 0, 4
+#else
+#define _EXTUI_MOVSP_SRC(ar) extui ar, ar, 0, 4
+#define _EXTUI_MOVSP_DST(ar) extui ar, ar, 4, 4
+#endif
+
+ENTRY(fast_alloca)
+
+ /* We shouldn't be in a double exception. */
+
+ l32i a0, a2, PT_DEPC
+ _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, .Lunhandled_double
+
+ rsr a0, DEPC # get a2
+ s32i a4, a2, PT_AREG4 # save a4 and
+ s32i a0, a2, PT_AREG2 # a2 to stack
+
+ /* Exit critical section. */
+
+ movi a0, 0
+ s32i a0, a3, EXC_TABLE_FIXUP
+
+ /* Restore a3, excsave_1 */
+
+ xsr a3, EXCSAVE_1 # make sure excsave_1 is valid for dbl.
+ rsr a4, EPC_1 # get exception address
+ s32i a3, a2, PT_AREG3 # save a3 to stack
+
+#ifdef ALLOCA_EXCEPTION_IN_IRAM
+#error iram not supported
+#else
+ /* Note: l8ui not allowed in IRAM/IROM!! */
+ l8ui a0, a4, 1 # read as(src) from MOVSP instruction
+#endif
+ movi a3, .Lmovsp_src
+ _EXTUI_MOVSP_SRC(a0) # extract source register number
+ addx8 a3, a0, a3
+ jx a3
+
+.Lunhandled_double:
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+ .align 8
+.Lmovsp_src:
+ l32i a3, a2, PT_AREG0; _j 1f; .align 8
+ mov a3, a1; _j 1f; .align 8
+ l32i a3, a2, PT_AREG2; _j 1f; .align 8
+ l32i a3, a2, PT_AREG3; _j 1f; .align 8
+ l32i a3, a2, PT_AREG4; _j 1f; .align 8
+ mov a3, a5; _j 1f; .align 8
+ mov a3, a6; _j 1f; .align 8
+ mov a3, a7; _j 1f; .align 8
+ mov a3, a8; _j 1f; .align 8
+ mov a3, a9; _j 1f; .align 8
+ mov a3, a10; _j 1f; .align 8
+ mov a3, a11; _j 1f; .align 8
+ mov a3, a12; _j 1f; .align 8
+ mov a3, a13; _j 1f; .align 8
+ mov a3, a14; _j 1f; .align 8
+ mov a3, a15; _j 1f; .align 8
+
+1:
+
+#ifdef ALLOCA_EXCEPTION_IN_IRAM
+#error iram not supported
+#else
+ l8ui a0, a4, 0 # read ar(dst) from MOVSP instruction
+#endif
+ addi a4, a4, 3 # step over movsp
+ _EXTUI_MOVSP_DST(a0) # extract destination register
+ wsr a4, EPC_1 # save new epc_1
+
+ _bnei a0, 1, 1f # no 'movsp a1, ax': jump
+
+ /* Move the save area. This implies the use of the L32E
+ * and S32E instructions, because this move must be done with
+ * the user's PS.RING privilege levels, not with ring 0
+ * (kernel's) privileges currently active with PS.EXCM
+ * set. Note that we have stil registered a fixup routine with the
+ * double exception vector in case a double exception occurs.
+ */
+
+ /* a0,a4:avail a1:old user stack a2:exc. stack a3:new user stack. */
+
+ l32e a0, a1, -16
+ l32e a4, a1, -12
+ s32e a0, a3, -16
+ s32e a4, a3, -12
+ l32e a0, a1, -8
+ l32e a4, a1, -4
+ s32e a0, a3, -8
+ s32e a4, a3, -4
+
+ /* Restore stack-pointer and all the other saved registers. */
+
+ mov a1, a3
+
+ l32i a4, a2, PT_AREG4
+ l32i a3, a2, PT_AREG3
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_AREG2
+ rfe
+
+ /* MOVSP <at>,<as> was invoked with <at> != a1.
+ * Because the stack pointer is not being modified,
+ * we should be able to just modify the pointer
+ * without moving any save area.
+ * The processor only traps these occurrences if the
+ * caller window isn't live, so unfortunately we can't
+ * use this as an alternate trap mechanism.
+ * So we just do the move. This requires that we
+ * resolve the destination register, not just the source,
+ * so there's some extra work.
+ * (PERHAPS NOT REALLY NEEDED, BUT CLEANER...)
+ */
+
+ /* a0 dst-reg, a1 user-stack, a2 stack, a3 value of src reg. */
+
+1: movi a4, .Lmovsp_dst
+ addx8 a4, a0, a4
+ jx a4
+
+ .align 8
+.Lmovsp_dst:
+ s32i a3, a2, PT_AREG0; _j 1f; .align 8
+ mov a1, a3; _j 1f; .align 8
+ s32i a3, a2, PT_AREG2; _j 1f; .align 8
+ s32i a3, a2, PT_AREG3; _j 1f; .align 8
+ s32i a3, a2, PT_AREG4; _j 1f; .align 8
+ mov a5, a3; _j 1f; .align 8
+ mov a6, a3; _j 1f; .align 8
+ mov a7, a3; _j 1f; .align 8
+ mov a8, a3; _j 1f; .align 8
+ mov a9, a3; _j 1f; .align 8
+ mov a10, a3; _j 1f; .align 8
+ mov a11, a3; _j 1f; .align 8
+ mov a12, a3; _j 1f; .align 8
+ mov a13, a3; _j 1f; .align 8
+ mov a14, a3; _j 1f; .align 8
+ mov a15, a3; _j 1f; .align 8
+
+1: l32i a4, a2, PT_AREG4
+ l32i a3, a2, PT_AREG3
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_AREG2
+ rfe
+
+
+/*
+ * fast system calls.
+ *
+ * WARNING: The kernel doesn't save the entire user context before
+ * handling a fast system call. These functions are small and short,
+ * usually offering some functionality not available to user tasks.
+ *
+ * BE CAREFUL TO PRESERVE THE USER'S CONTEXT.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ */
+
+ENTRY(fast_syscall_kernel)
+
+ /* Skip syscall. */
+
+ rsr a0, EPC_1
+ addi a0, a0, 3
+ wsr a0, EPC_1
+
+ l32i a0, a2, PT_DEPC
+ bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_syscall_unrecoverable
+
+ rsr a0, DEPC # get syscall-nr
+ _beqz a0, fast_syscall_spill_registers
+ _beqi a0, __NR_xtensa, fast_syscall_xtensa
+
+ j kernel_exception
+
+ENTRY(fast_syscall_user)
+
+ /* Skip syscall. */
+
+ rsr a0, EPC_1
+ addi a0, a0, 3
+ wsr a0, EPC_1
+
+ l32i a0, a2, PT_DEPC
+ bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_syscall_unrecoverable
+
+ rsr a0, DEPC # get syscall-nr
+ _beqz a0, fast_syscall_spill_registers
+ _beqi a0, __NR_xtensa, fast_syscall_xtensa
+
+ j user_exception
+
+ENTRY(fast_syscall_unrecoverable)
+
+ /* Restore all states. */
+
+ l32i a0, a2, PT_AREG0 # restore a0
+ xsr a2, DEPC # restore a2, depc
+ rsr a3, EXCSAVE_1
+
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+
+
+/*
+ * sysxtensa syscall handler
+ *
+ * int sysxtensa (SYS_XTENSA_ATOMIC_SET, ptr, val, unused);
+ * int sysxtensa (SYS_XTENSA_ATOMIC_ADD, ptr, val, unused);
+ * int sysxtensa (SYS_XTENSA_ATOMIC_EXG_ADD, ptr, val, unused);
+ * int sysxtensa (SYS_XTENSA_ATOMIC_CMP_SWP, ptr, oldval, newval);
+ * a2 a6 a3 a4 a5
+ *
+ * Entry condition:
+ *
+ * a0: a2 (syscall-nr), original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in a0 and DEPC
+ * a3: dispatch table, original in excsave_1
+ * a4..a15: unchanged
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Note: we don't have to save a2; a2 holds the return value
+ *
+ * We use the two macros TRY and CATCH:
+ *
+ * TRY adds an entry to the __ex_table fixup table for the immediately
+ * following instruction.
+ *
+ * CATCH catches any exception that occurred at one of the preceding TRY
+ * statements and continues from there
+ *
+ * Usage TRY l32i a0, a1, 0
+ * <other code>
+ * done: rfe
+ * CATCH <set return code>
+ * j done
+ */
+
+#define TRY \
+ .section __ex_table, "a"; \
+ .word 66f, 67f; \
+ .text; \
+66:
+
+#define CATCH \
+67:
+
+ENTRY(fast_syscall_xtensa)
+
+ xsr a3, EXCSAVE_1 # restore a3, excsave1
+
+ s32i a7, a2, PT_AREG7 # we need an additional register
+ movi a7, 4 # sizeof(unsigned int)
+ access_ok a3, a7, a0, a2, .Leac # a0: scratch reg, a2: sp
+
+ addi a6, a6, -1 # assuming SYS_XTENSA_ATOMIC_SET = 1
+ _bgeui a6, SYS_XTENSA_COUNT - 1, .Lill
+ _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP - 1, .Lnswp
+
+ /* Fall through for ATOMIC_CMP_SWP. */
+
+.Lswp: /* Atomic compare and swap */
+
+TRY l32i a0, a3, 0 # read old value
+ bne a0, a4, 1f # same as old value? jump
+TRY s32i a5, a3, 0 # different, modify value
+ l32i a7, a2, PT_AREG7 # restore a7
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, 1 # and return 1
+ addi a6, a6, 1 # restore a6 (really necessary?)
+ rfe
+
+1: l32i a7, a2, PT_AREG7 # restore a7
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, 0 # return 0 (note that we cannot set
+ addi a6, a6, 1 # restore a6 (really necessary?)
+ rfe
+
+.Lnswp: /* Atomic set, add, and exg_add. */
+
+TRY l32i a7, a3, 0 # orig
+ add a0, a4, a7 # + arg
+ moveqz a0, a4, a6 # set
+TRY s32i a0, a3, 0 # write new value
+
+ mov a0, a2
+ mov a2, a7
+ l32i a7, a0, PT_AREG7 # restore a7
+ l32i a0, a0, PT_AREG0 # restore a0
+ addi a6, a6, 1 # restore a6 (really necessary?)
+ rfe
+
+CATCH
+.Leac: l32i a7, a2, PT_AREG7 # restore a7
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, -EFAULT
+ rfe
+
+.Lill: l32i a7, a2, PT_AREG0 # restore a7
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, -EINVAL
+ rfe
+
+
+
+
+/* fast_syscall_spill_registers.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * Note: We assume the stack pointer is EXC_TABLE_KSTK in the fixup handler.
+ */
+
+ENTRY(fast_syscall_spill_registers)
+
+ /* Register a FIXUP handler (pass current wb as a parameter) */
+
+ movi a0, fast_syscall_spill_registers_fixup
+ s32i a0, a3, EXC_TABLE_FIXUP
+ rsr a0, WINDOWBASE
+ s32i a0, a3, EXC_TABLE_PARAM
+
+ /* Save a3 and SAR on stack. */
+
+ rsr a0, SAR
+ xsr a3, EXCSAVE_1 # restore a3 and excsave_1
+ s32i a3, a2, PT_AREG3
+ s32i a4, a2, PT_AREG4
+ s32i a0, a2, PT_AREG5 # store SAR to PT_AREG5
+
+ /* The spill routine might clobber a7, a11, and a15. */
+
+ s32i a7, a2, PT_AREG7
+ s32i a11, a2, PT_AREG11
+ s32i a15, a2, PT_AREG15
+
+ call0 _spill_registers # destroys a3, a4, and SAR
+
+ /* Advance PC, restore registers and SAR, and return from exception. */
+
+ l32i a3, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ l32i a0, a2, PT_AREG0
+ wsr a3, SAR
+ l32i a3, a2, PT_AREG3
+
+ /* Restore clobbered registers. */
+
+ l32i a7, a2, PT_AREG7
+ l32i a11, a2, PT_AREG11
+ l32i a15, a2, PT_AREG15
+
+ movi a2, 0
+ rfe
+
+/* Fixup handler.
+ *
+ * We get here if the spill routine causes an exception, e.g. tlb miss.
+ * We basically restore WINDOWBASE and WINDOWSTART to the condition when
+ * we entered the spill routine and jump to the user exception handler.
+ *
+ * a0: value of depc, original value in depc
+ * a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE
+ * a3: exctable, original value in excsave1
+ */
+
+fast_syscall_spill_registers_fixup:
+
+ rsr a2, WINDOWBASE # get current windowbase (a2 is saved)
+ xsr a0, DEPC # restore depc and a0
+ ssl a2 # set shift (32 - WB)
+
+ /* We need to make sure the current registers (a0-a3) are preserved.
+ * To do this, we simply set the bit for the current window frame
+ * in WS, so that the exception handlers save them to the task stack.
+ */
+
+ rsr a3, EXCSAVE_1 # get spill-mask
+ slli a2, a3, 1 # shift left by one
+
+ slli a3, a2, 32-WSBITS
+ src a2, a2, a3 # a1 = xxwww1yyxxxwww1yy......
+ wsr a2, WINDOWSTART # set corrected windowstart
+
+ movi a3, exc_table
+ l32i a2, a3, EXC_TABLE_DOUBLE_SAVE # restore a2
+ l32i a3, a3, EXC_TABLE_PARAM # original WB (in user task)
+
+ /* Return to the original (user task) WINDOWBASE.
+ * We leave the following frame behind:
+ * a0, a1, a2 same
+ * a3: trashed (saved in excsave_1)
+ * depc: depc (we have to return to that address)
+ * excsave_1: a3
+ */
+
+ wsr a3, WINDOWBASE
+ rsync
+
+ /* We are now in the original frame when we entered _spill_registers:
+ * a0: return address
+ * a1: used, stack pointer
+ * a2: kernel stack pointer
+ * a3: available, saved in EXCSAVE_1
+ * depc: exception address
+ * excsave: a3
+ * Note: This frame might be the same as above.
+ */
+
+ /* Setup stack pointer. */
+
+ addi a2, a2, -PT_USER_SIZE
+ s32i a0, a2, PT_AREG0
+
+ /* Make sure we return to this fixup handler. */
+
+ movi a3, fast_syscall_spill_registers_fixup_return
+ s32i a3, a2, PT_DEPC # setup depc
+
+ /* Jump to the exception handler. */
+
+ movi a3, exc_table
+ rsr a0, EXCCAUSE
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_USER # load handler
+ jx a0
+
+fast_syscall_spill_registers_fixup_return:
+
+ /* When we return here, all registers have been restored (a2: DEPC) */
+
+ wsr a2, DEPC # exception address
+
+ /* Restore fixup handler. */
+
+ xsr a3, EXCSAVE_1
+ movi a2, fast_syscall_spill_registers_fixup
+ s32i a2, a3, EXC_TABLE_FIXUP
+ rsr a2, WINDOWBASE
+ s32i a2, a3, EXC_TABLE_PARAM
+ l32i a2, a3, EXC_TABLE_KSTK
+
+ /* Load WB at the time the exception occurred. */
+
+ rsr a3, SAR # WB is still in SAR
+ neg a3, a3
+ wsr a3, WINDOWBASE
+ rsync
+
+ /* Restore a3 and return. */
+
+ movi a3, exc_table
+ xsr a3, EXCSAVE_1
+
+ rfde
+
+
+/*
+ * spill all registers.
+ *
+ * This is not a real function. The following conditions must be met:
+ *
+ * - must be called with call0.
+ * - uses a3, a4 and SAR.
+ * - the last 'valid' register of each frame are clobbered.
+ * - the caller must have registered a fixup handler
+ * (or be inside a critical section)
+ * - PS_EXCM must be set (PS_WOE cleared?)
+ */
+
+ENTRY(_spill_registers)
+
+ /*
+ * Rotate ws so that the current windowbase is at bit 0.
+ * Assume ws = xxxwww1yy (www1 current window frame).
+ * Rotate ws right so that a4 = yyxxxwww1.
+ */
+
+ rsr a4, WINDOWBASE
+ rsr a3, WINDOWSTART # a3 = xxxwww1yy
+ ssr a4 # holds WB
+ slli a4, a3, WSBITS
+ or a3, a3, a4 # a3 = xxxwww1yyxxxwww1yy
+ srl a3, a3 # a3 = 00xxxwww1yyxxxwww1
+
+ /* We are done if there are no more than the current register frame. */
+
+ extui a3, a3, 1, WSBITS-1 # a3 = 0yyxxxwww
+ movi a4, (1 << (WSBITS-1))
+ _beqz a3, .Lnospill # only one active frame? jump
+
+ /* We want 1 at the top, so that we return to the current windowbase */
+
+ or a3, a3, a4 # 1yyxxxwww
+
+ /* Skip empty frames - get 'oldest' WINDOWSTART-bit. */
+
+ wsr a3, WINDOWSTART # save shifted windowstart
+ neg a4, a3
+ and a3, a4, a3 # first bit set from right: 000010000
+
+ ffs_ws a4, a3 # a4: shifts to skip empty frames
+ movi a3, WSBITS
+ sub a4, a3, a4 # WSBITS-a4:number of 0-bits from right
+ ssr a4 # save in SAR for later.
+
+ rsr a3, WINDOWBASE
+ add a3, a3, a4
+ wsr a3, WINDOWBASE
+ rsync
+
+ rsr a3, WINDOWSTART
+ srl a3, a3 # shift windowstart
+
+ /* WB is now just one frame below the oldest frame in the register
+ window. WS is shifted so the oldest frame is in bit 0, thus, WB
+ and WS differ by one 4-register frame. */
+
+ /* Save frames. Depending what call was used (call4, call8, call12),
+ * we have to save 4,8. or 12 registers.
+ */
+
+ _bbsi.l a3, 1, .Lc4
+ _bbsi.l a3, 2, .Lc8
+
+ /* Special case: we have a call12-frame starting at a4. */
+
+ _bbci.l a3, 3, .Lc12 # bit 3 shouldn't be zero! (Jump to Lc12 first)
+
+ s32e a4, a1, -16 # a1 is valid with an empty spill area
+ l32e a4, a5, -12
+ s32e a8, a4, -48
+ mov a8, a4
+ l32e a4, a1, -16
+ j .Lc12c
+
+.Lnospill:
+ ret
+
+.Lloop: _bbsi.l a3, 1, .Lc4
+ _bbci.l a3, 2, .Lc12
+
+.Lc8: s32e a4, a13, -16
+ l32e a4, a5, -12
+ s32e a8, a4, -32
+ s32e a5, a13, -12
+ s32e a6, a13, -8
+ s32e a7, a13, -4
+ s32e a9, a4, -28
+ s32e a10, a4, -24
+ s32e a11, a4, -20
+
+ srli a11, a3, 2 # shift windowbase by 2
+ rotw 2
+ _bnei a3, 1, .Lloop
+
+.Lexit: /* Done. Do the final rotation, set WS, and return. */
+
+ rotw 1
+ rsr a3, WINDOWBASE
+ ssl a3
+ movi a3, 1
+ sll a3, a3
+ wsr a3, WINDOWSTART
+ ret
+
+.Lc4: s32e a4, a9, -16
+ s32e a5, a9, -12
+ s32e a6, a9, -8
+ s32e a7, a9, -4
+
+ srli a7, a3, 1
+ rotw 1
+ _bnei a3, 1, .Lloop
+ j .Lexit
+
+.Lc12: _bbci.l a3, 3, .Linvalid_mask # bit 2 shouldn't be zero!
+
+ /* 12-register frame (call12) */
+
+ l32e a2, a5, -12
+ s32e a8, a2, -48
+ mov a8, a2
+
+.Lc12c: s32e a9, a8, -44
+ s32e a10, a8, -40
+ s32e a11, a8, -36
+ s32e a12, a8, -32
+ s32e a13, a8, -28
+ s32e a14, a8, -24
+ s32e a15, a8, -20
+ srli a15, a3, 3
+
+ /* The stack pointer for a4..a7 is out of reach, so we rotate the
+ * window, grab the stackpointer, and rotate back.
+ * Alternatively, we could also use the following approach, but that
+ * makes the fixup routine much more complicated:
+ * rotw 1
+ * s32e a0, a13, -16
+ * ...
+ * rotw 2
+ */
+
+ rotw 1
+ mov a5, a13
+ rotw -1
+
+ s32e a4, a9, -16
+ s32e a5, a9, -12
+ s32e a6, a9, -8
+ s32e a7, a9, -4
+
+ rotw 3
+
+ _beqi a3, 1, .Lexit
+ j .Lloop
+
+.Linvalid_mask:
+
+ /* We get here because of an unrecoverable error in the window
+ * registers. If we are in user space, we kill the application,
+ * however, this condition is unrecoverable in kernel space.
+ */
+
+ rsr a0, PS
+ _bbci.l a0, PS_UM_BIT, 1f
+
+ /* User space: Setup a dummy frame and kill application.
+ * Note: We assume EXC_TABLE_KSTK contains a valid stack pointer.
+ */
+
+ movi a0, 1
+ movi a1, 0
+
+ wsr a0, WINDOWSTART
+ wsr a1, WINDOWBASE
+ rsync
+
+ movi a0, 0
+
+ movi a3, exc_table
+ l32i a1, a3, EXC_TABLE_KSTK
+ wsr a3, EXCSAVE_1
+
+ movi a4, (1 << PS_WOE_BIT) | 1
+ wsr a4, PS
+ rsync
+
+ movi a6, SIGSEGV
+ movi a4, do_exit
+ callx4 a4
+
+1: /* Kernel space: PANIC! */
+
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0 # should not return
+1: j 1b
+
+#ifdef CONFIG_MMU
+/*
+ * We should never get here. Bail out!
+ */
+
+ENTRY(fast_second_level_miss_double_kernel)
+
+1: movi a0, unrecoverable_exception
+ callx0 a0 # should not return
+1: j 1b
+
+/* First-level entry handler for user, kernel, and double 2nd-level
+ * TLB miss exceptions. Note that for now, user and kernel miss
+ * exceptions share the same entry point and are handled identically.
+ *
+ * An old, less-efficient C version of this function used to exist.
+ * We include it below, interleaved as comments, for reference.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+ENTRY(fast_second_level_miss)
+
+ /* Save a1. Note: we don't expect a double exception. */
+
+ s32i a1, a2, PT_AREG1
+
+ /* We need to map the page of PTEs for the user task. Find
+ * the pointer to that page. Also, it's possible for tsk->mm
+ * to be NULL while tsk->active_mm is nonzero if we faulted on
+ * a vmalloc address. In that rare case, we must use
+ * active_mm instead to avoid a fault in this handler. See
+ *
+ * http://mail.nl.linux.org/linux-mm/2002-08/msg00258.html
+ * (or search Internet on "mm vs. active_mm")
+ *
+ * if (!mm)
+ * mm = tsk->active_mm;
+ * pgd = pgd_offset (mm, regs->excvaddr);
+ * pmd = pmd_offset (pgd, regs->excvaddr);
+ * pmdval = *pmd;
+ */
+
+ GET_CURRENT(a1,a2)
+ l32i a0, a1, TASK_MM # tsk->mm
+ beqz a0, 9f
+
+
+ /* We deliberately destroy a3 that holds the exception table. */
+
+8: rsr a3, EXCVADDR # fault address
+ _PGD_OFFSET(a0, a3, a1)
+ l32i a0, a0, 0 # read pmdval
+ beqz a0, 2f
+
+ /* Read ptevaddr and convert to top of page-table page.
+ *
+ * vpnval = read_ptevaddr_register() & PAGE_MASK;
+ * vpnval += DTLB_WAY_PGTABLE;
+ * pteval = mk_pte (virt_to_page(pmd_val(pmdval)), PAGE_KERNEL);
+ * write_dtlb_entry (pteval, vpnval);
+ *
+ * The messy computation for 'pteval' above really simplifies
+ * into the following:
+ *
+ * pteval = ((pmdval - PAGE_OFFSET) & PAGE_MASK) | PAGE_DIRECTORY
+ */
+
+ movi a1, -PAGE_OFFSET
+ add a0, a0, a1 # pmdval - PAGE_OFFSET
+ extui a1, a0, 0, PAGE_SHIFT # ... & PAGE_MASK
+ xor a0, a0, a1
+
+ movi a1, _PAGE_DIRECTORY
+ or a0, a0, a1 # ... | PAGE_DIRECTORY
+
+ /*
+ * We utilize all three wired-ways (7-9) to hold pmd translations.
+ * Memory regions are mapped to the DTLBs according to bits 28 and 29.
+ * This allows to map the three most common regions to three different
+ * DTLBs:
+ * 0,1 -> way 7 program (0040.0000) and virtual (c000.0000)
+ * 2 -> way 8 shared libaries (2000.0000)
+ * 3 -> way 0 stack (3000.0000)
+ */
+
+ extui a3, a3, 28, 2 # addr. bit 28 and 29 0,1,2,3
+ rsr a1, PTEVADDR
+ addx2 a3, a3, a3 # -> 0,3,6,9
+ srli a1, a1, PAGE_SHIFT
+ extui a3, a3, 2, 2 # -> 0,0,1,2
+ slli a1, a1, PAGE_SHIFT # ptevaddr & PAGE_MASK
+ addi a3, a3, DTLB_WAY_PGD
+ add a1, a1, a3 # ... + way_number
+
+3: wdtlb a0, a1
+ dsync
+
+ /* Exit critical section. */
+
+4: movi a3, exc_table # restore a3
+ movi a0, 0
+ s32i a0, a3, EXC_TABLE_FIXUP
+
+ /* Restore the working registers, and return. */
+
+ l32i a0, a2, PT_AREG0
+ l32i a1, a2, PT_AREG1
+ l32i a2, a2, PT_DEPC
+ xsr a3, EXCSAVE_1
+
+ bgeui a2, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
+
+ /* Restore excsave1 and return. */
+
+ rsr a2, DEPC
+ rfe
+
+ /* Return from double exception. */
+
+1: xsr a2, DEPC
+ esync
+ rfde
+
+9: l32i a0, a1, TASK_ACTIVE_MM # unlikely case mm == 0
+ j 8b
+
+#if (DCACHE_WAY_SIZE > PAGE_SIZE)
+
+2: /* Special case for cache aliasing.
+ * We (should) only get here if a clear_user_page, copy_user_page
+ * or the aliased cache flush functions got preemptively interrupted
+ * by another task. Re-establish temporary mapping to the
+ * TLBTEMP_BASE areas.
+ */
+
+ /* We shouldn't be in a double exception */
+
+ l32i a0, a2, PT_DEPC
+ bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, 2f
+
+ /* Make sure the exception originated in the special functions */
+
+ movi a0, __tlbtemp_mapping_start
+ rsr a3, EPC_1
+ bltu a3, a0, 2f
+ movi a0, __tlbtemp_mapping_end
+ bgeu a3, a0, 2f
+
+ /* Check if excvaddr was in one of the TLBTEMP_BASE areas. */
+
+ movi a3, TLBTEMP_BASE_1
+ rsr a0, EXCVADDR
+ bltu a0, a3, 2f
+
+ addi a1, a0, -(2 << (DCACHE_ALIAS_ORDER + PAGE_SHIFT))
+ bgeu a1, a3, 2f
+
+ /* Check if we have to restore an ITLB mapping. */
+
+ movi a1, __tlbtemp_mapping_itlb
+ rsr a3, EPC_1
+ sub a3, a3, a1
+
+ /* Calculate VPN */
+
+ movi a1, PAGE_MASK
+ and a1, a1, a0
+
+ /* Jump for ITLB entry */
+
+ bgez a3, 1f
+
+ /* We can use up to two TLBTEMP areas, one for src and one for dst. */
+
+ extui a3, a0, PAGE_SHIFT + DCACHE_ALIAS_ORDER, 1
+ add a1, a3, a1
+
+ /* PPN is in a6 for the first TLBTEMP area and in a7 for the second. */
+
+ mov a0, a6
+ movnez a0, a7, a3
+ j 3b
+
+ /* ITLB entry. We only use dst in a6. */
+
+1: witlb a6, a1
+ isync
+ j 4b
+
+
+#endif // DCACHE_WAY_SIZE > PAGE_SIZE
+
+
+2: /* Invalid PGD, default exception handling */
+
+ movi a3, exc_table
+ rsr a1, DEPC
+ xsr a3, EXCSAVE_1
+ s32i a1, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ mov a1, a2
+
+ rsr a2, PS
+ bbsi.l a2, PS_UM_BIT, 1f
+ j _kernel_exception
+1: j _user_exception
+
+
+/*
+ * StoreProhibitedException
+ *
+ * Update the pte and invalidate the itlb mapping for this pte.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+ENTRY(fast_store_prohibited)
+
+ /* Save a1 and a4. */
+
+ s32i a1, a2, PT_AREG1
+ s32i a4, a2, PT_AREG4
+
+ GET_CURRENT(a1,a2)
+ l32i a0, a1, TASK_MM # tsk->mm
+ beqz a0, 9f
+
+8: rsr a1, EXCVADDR # fault address
+ _PGD_OFFSET(a0, a1, a4)
+ l32i a0, a0, 0
+ beqz a0, 2f
+
+ /* Note that we assume _PAGE_WRITABLE_BIT is only set if pte is valid.*/
+
+ _PTE_OFFSET(a0, a1, a4)
+ l32i a4, a0, 0 # read pteval
+ bbci.l a4, _PAGE_WRITABLE_BIT, 2f
+
+ movi a1, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_HW_WRITE
+ or a4, a4, a1
+ rsr a1, EXCVADDR
+ s32i a4, a0, 0
+
+ /* We need to flush the cache if we have page coloring. */
+#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
+ dhwb a0, 0
+#endif
+ pdtlb a0, a1
+ wdtlb a4, a0
+
+ /* Exit critical section. */
+
+ movi a0, 0
+ s32i a0, a3, EXC_TABLE_FIXUP
+
+ /* Restore the working registers, and return. */
+
+ l32i a4, a2, PT_AREG4
+ l32i a1, a2, PT_AREG1
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_DEPC
+
+ /* Restore excsave1 and a3. */
+
+ xsr a3, EXCSAVE_1
+ bgeui a2, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
+
+ rsr a2, DEPC
+ rfe
+
+ /* Double exception. Restore FIXUP handler and return. */
+
+1: xsr a2, DEPC
+ esync
+ rfde
+
+9: l32i a0, a1, TASK_ACTIVE_MM # unlikely case mm == 0
+ j 8b
+
+2: /* If there was a problem, handle fault in C */
+
+ rsr a4, DEPC # still holds a2
+ xsr a3, EXCSAVE_1
+ s32i a4, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ l32i a4, a2, PT_AREG4
+ mov a1, a2
+
+ rsr a2, PS
+ bbsi.l a2, PS_UM_BIT, 1f
+ j _kernel_exception
+1: j _user_exception
+#endif /* CONFIG_MMU */
+
+/*
+ * System Calls.
+ *
+ * void system_call (struct pt_regs* regs, int exccause)
+ * a2 a3
+ */
+
+ENTRY(system_call)
+ entry a1, 32
+
+ /* regs->syscall = regs->areg[2] */
+
+ l32i a3, a2, PT_AREG2
+ mov a6, a2
+ movi a4, do_syscall_trace_enter
+ s32i a3, a2, PT_SYSCALL
+ callx4 a4
+
+ /* syscall = sys_call_table[syscall_nr] */
+
+ movi a4, sys_call_table;
+ movi a5, __NR_syscall_count
+ movi a6, -ENOSYS
+ bgeu a3, a5, 1f
+
+ addx4 a4, a3, a4
+ l32i a4, a4, 0
+ movi a5, sys_ni_syscall;
+ beq a4, a5, 1f
+
+ /* Load args: arg0 - arg5 are passed via regs. */
+
+ l32i a6, a2, PT_AREG6
+ l32i a7, a2, PT_AREG3
+ l32i a8, a2, PT_AREG4
+ l32i a9, a2, PT_AREG5
+ l32i a10, a2, PT_AREG8
+ l32i a11, a2, PT_AREG9
+
+ /* Pass one additional argument to the syscall: pt_regs (on stack) */
+ s32i a2, a1, 0
+
+ callx4 a4
+
+1: /* regs->areg[2] = return_value */
+
+ s32i a6, a2, PT_AREG2
+ movi a4, do_syscall_trace_leave
+ mov a6, a2
+ callx4 a4
+ retw
+
+
+/*
+ * Create a kernel thread
+ *
+ * int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+ * a2 a2 a3 a4
+ */
+
+ENTRY(kernel_thread)
+ entry a1, 16
+
+ mov a5, a2 # preserve fn over syscall
+ mov a7, a3 # preserve args over syscall
+
+ movi a3, _CLONE_VM | _CLONE_UNTRACED
+ movi a2, __NR_clone
+ or a6, a4, a3 # arg0: flags
+ mov a3, a1 # arg1: sp
+ syscall
+
+ beq a3, a1, 1f # branch if parent
+ mov a6, a7 # args
+ callx4 a5 # fn(args)
+
+ movi a2, __NR_exit
+ syscall # return value of fn(args) still in a6
+
+1: retw
+
+/*
+ * Do a system call from kernel instead of calling sys_execve, so we end up
+ * with proper pt_regs.
+ *
+ * int kernel_execve(const char *fname, char *const argv[], charg *const envp[])
+ * a2 a2 a3 a4
+ */
+
+ENTRY(kernel_execve)
+ entry a1, 16
+ mov a6, a2 # arg0 is in a6
+ movi a2, __NR_execve
+ syscall
+
+ retw
+
+/*
+ * Task switch.
+ *
+ * struct task* _switch_to (struct task* prev, struct task* next)
+ * a2 a2 a3
+ */
+
+ENTRY(_switch_to)
+
+ entry a1, 16
+
+ mov a12, a2 # preserve 'prev' (a2)
+ mov a13, a3 # and 'next' (a3)
+
+ l32i a4, a2, TASK_THREAD_INFO
+ l32i a5, a3, TASK_THREAD_INFO
+
+ save_xtregs_user a4 a6 a8 a9 a10 a11 THREAD_XTREGS_USER
+
+ s32i a0, a12, THREAD_RA # save return address
+ s32i a1, a12, THREAD_SP # save stack pointer
+
+ /* Disable ints while we manipulate the stack pointer. */
+
+ movi a14, (1 << PS_EXCM_BIT) | LOCKLEVEL
+ xsr a14, PS
+ rsr a3, EXCSAVE_1
+ rsync
+ s32i a3, a3, EXC_TABLE_FIXUP /* enter critical section */
+
+ /* Switch CPENABLE */
+
+#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
+ l32i a3, a5, THREAD_CPENABLE
+ xsr a3, CPENABLE
+ s32i a3, a4, THREAD_CPENABLE
+#endif
+
+ /* Flush register file. */
+
+ call0 _spill_registers # destroys a3, a4, and SAR
+
+ /* Set kernel stack (and leave critical section)
+ * Note: It's save to set it here. The stack will not be overwritten
+ * because the kernel stack will only be loaded again after
+ * we return from kernel space.
+ */
+
+ rsr a3, EXCSAVE_1 # exc_table
+ movi a6, 0
+ addi a7, a5, PT_REGS_OFFSET
+ s32i a6, a3, EXC_TABLE_FIXUP
+ s32i a7, a3, EXC_TABLE_KSTK
+
+ /* restore context of the task that 'next' addresses */
+
+ l32i a0, a13, THREAD_RA # restore return address
+ l32i a1, a13, THREAD_SP # restore stack pointer
+
+ load_xtregs_user a5 a6 a8 a9 a10 a11 THREAD_XTREGS_USER
+
+ wsr a14, PS
+ mov a2, a12 # return 'prev'
+ rsync
+
+ retw
+
+
+ENTRY(ret_from_fork)
+
+ /* void schedule_tail (struct task_struct *prev)
+ * Note: prev is still in a6 (return value from fake call4 frame)
+ */
+ movi a4, schedule_tail
+ callx4 a4
+
+ movi a4, do_syscall_trace_leave
+ mov a6, a1
+ callx4 a4
+
+ j common_exception_return
+
diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S
new file mode 100644
index 00000000..3ef91a73
--- /dev/null
+++ b/arch/xtensa/kernel/head.S
@@ -0,0 +1,244 @@
+/*
+ * arch/xtensa/kernel/head.S
+ *
+ * Xtensa Processor startup code.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Kevin Chea
+ */
+
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/cacheasm.h>
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+
+/*
+ * This module contains the entry code for kernel images. It performs the
+ * minimal setup needed to call the generic C routines.
+ *
+ * Prerequisites:
+ *
+ * - The kernel image has been loaded to the actual address where it was
+ * compiled to.
+ * - a2 contains either 0 or a pointer to a list of boot parameters.
+ * (see setup.c for more details)
+ *
+ */
+
+/*
+ * _start
+ *
+ * The bootloader passes a pointer to a list of boot parameters in a2.
+ */
+
+ /* The first bytes of the kernel image must be an instruction, so we
+ * manually allocate and define the literal constant we need for a jx
+ * instruction.
+ */
+
+ __HEAD
+ .globl _start
+_start: _j 2f
+ .align 4
+1: .word _startup
+2: l32r a0, 1b
+ jx a0
+
+ .section .init.text, "ax"
+ .align 4
+_startup:
+
+ /* Disable interrupts and exceptions. */
+
+ movi a0, LOCKLEVEL
+ wsr a0, PS
+
+ /* Preserve the pointer to the boot parameter list in EXCSAVE_1 */
+
+ wsr a2, EXCSAVE_1
+
+ /* Start with a fresh windowbase and windowstart. */
+
+ movi a1, 1
+ movi a0, 0
+ wsr a1, WINDOWSTART
+ wsr a0, WINDOWBASE
+ rsync
+
+ /* Set a0 to 0 for the remaining initialization. */
+
+ movi a0, 0
+
+ /* Clear debugging registers. */
+
+#if XCHAL_HAVE_DEBUG
+ wsr a0, IBREAKENABLE
+ wsr a0, ICOUNT
+ movi a1, 15
+ wsr a0, ICOUNTLEVEL
+
+ .set _index, 0
+ .rept XCHAL_NUM_DBREAK - 1
+ wsr a0, DBREAKC + _index
+ .set _index, _index + 1
+ .endr
+#endif
+
+ /* Clear CCOUNT (not really necessary, but nice) */
+
+ wsr a0, CCOUNT # not really necessary, but nice
+
+ /* Disable zero-loops. */
+
+#if XCHAL_HAVE_LOOPS
+ wsr a0, LCOUNT
+#endif
+
+ /* Disable all timers. */
+
+ .set _index, 0
+ .rept XCHAL_NUM_TIMERS - 1
+ wsr a0, CCOMPARE + _index
+ .set _index, _index + 1
+ .endr
+
+ /* Interrupt initialization. */
+
+ movi a2, XCHAL_INTTYPE_MASK_SOFTWARE | XCHAL_INTTYPE_MASK_EXTERN_EDGE
+ wsr a0, INTENABLE
+ wsr a2, INTCLEAR
+
+ /* Disable coprocessors. */
+
+#if XCHAL_CP_NUM > 0
+ wsr a0, CPENABLE
+#endif
+
+ /* Set PS.INTLEVEL=1, PS.WOE=0, kernel stack, PS.EXCM=0
+ *
+ * Note: PS.EXCM must be cleared before using any loop
+ * instructions; otherwise, they are silently disabled, and
+ * at most one iteration of the loop is executed.
+ */
+
+ movi a1, 1
+ wsr a1, PS
+ rsync
+
+ /* Initialize the caches.
+ * a2, a3 are just working registers (clobbered).
+ */
+
+#if XCHAL_DCACHE_LINE_LOCKABLE
+ ___unlock_dcache_all a2 a3
+#endif
+
+#if XCHAL_ICACHE_LINE_LOCKABLE
+ ___unlock_icache_all a2 a3
+#endif
+
+ ___invalidate_dcache_all a2 a3
+ ___invalidate_icache_all a2 a3
+
+ isync
+
+ /* Unpack data sections
+ *
+ * The linker script used to build the Linux kernel image
+ * creates a table located at __boot_reloc_table_start
+ * that contans the information what data needs to be unpacked.
+ *
+ * Uses a2-a7.
+ */
+
+ movi a2, __boot_reloc_table_start
+ movi a3, __boot_reloc_table_end
+
+1: beq a2, a3, 3f # no more entries?
+ l32i a4, a2, 0 # start destination (in RAM)
+ l32i a5, a2, 4 # end desination (in RAM)
+ l32i a6, a2, 8 # start source (in ROM)
+ addi a2, a2, 12 # next entry
+ beq a4, a5, 1b # skip, empty entry
+ beq a4, a6, 1b # skip, source and dest. are the same
+
+2: l32i a7, a6, 0 # load word
+ addi a6, a6, 4
+ s32i a7, a4, 0 # store word
+ addi a4, a4, 4
+ bltu a4, a5, 2b
+ j 1b
+
+3:
+ /* All code and initialized data segments have been copied.
+ * Now clear the BSS segment.
+ */
+
+ movi a2, __bss_start # start of BSS
+ movi a3, __bss_stop # end of BSS
+
+ __loopt a2, a3, a4, 2
+ s32i a0, a2, 0
+ __endla a2, a4, 4
+
+#if XCHAL_DCACHE_IS_WRITEBACK
+
+ /* After unpacking, flush the writeback cache to memory so the
+ * instructions/data are available.
+ */
+
+ ___flush_dcache_all a2 a3
+#endif
+
+ /* Setup stack and enable window exceptions (keep irqs disabled) */
+
+ movi a1, init_thread_union
+ addi a1, a1, KERNEL_STACK_SIZE
+
+ movi a2, 0x00040001 # WOE=1, INTLEVEL=1, UM=0
+ wsr a2, PS # (enable reg-windows; progmode stack)
+ rsync
+
+ /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/
+
+ movi a2, debug_exception
+ wsr a2, EXCSAVE + XCHAL_DEBUGLEVEL
+
+ /* Set up EXCSAVE[1] to point to the exc_table. */
+
+ movi a6, exc_table
+ xsr a6, EXCSAVE_1
+
+ /* init_arch kick-starts the linux kernel */
+
+ movi a4, init_arch
+ callx4 a4
+
+ movi a4, start_kernel
+ callx4 a4
+
+should_never_return:
+ j should_never_return
+
+
+/*
+ * BSS section
+ */
+
+__PAGE_ALIGNED_BSS
+#ifdef CONFIG_MMU
+ENTRY(swapper_pg_dir)
+ .fill PAGE_SIZE, 1, 0
+#endif
+ENTRY(empty_zero_page)
+ .fill PAGE_SIZE, 1, 0
diff --git a/arch/xtensa/kernel/init_task.c b/arch/xtensa/kernel/init_task.c
new file mode 100644
index 00000000..cd122fb7
--- /dev/null
+++ b/arch/xtensa/kernel/init_task.c
@@ -0,0 +1,31 @@
+/*
+ * arch/xtensa/kernel/init_task.c
+ *
+ * Xtensa Processor version.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2007 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/module.h>
+#include <linux/mqueue.h>
+
+#include <asm/uaccess.h>
+
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+union thread_union init_thread_union __init_task_data =
+ { INIT_THREAD_INFO(init_task) };
+
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_task);
diff --git a/arch/xtensa/kernel/io.c b/arch/xtensa/kernel/io.c
new file mode 100644
index 00000000..5b65269b
--- /dev/null
+++ b/arch/xtensa/kernel/io.c
@@ -0,0 +1,75 @@
+/*
+ * arch/xtensa/io.c
+ *
+ * IO primitives
+ *
+ * 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.
+ *
+ * Copied from sparc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+void outsb(unsigned long addr, const void *src, unsigned long count) {
+ while (count) {
+ count -= 1;
+ writeb(*(const char *)src, addr);
+ src += 1;
+ addr += 1;
+ }
+}
+
+void outsw(unsigned long addr, const void *src, unsigned long count) {
+ while (count) {
+ count -= 2;
+ writew(*(const short *)src, addr);
+ src += 2;
+ addr += 2;
+ }
+}
+
+void outsl(unsigned long addr, const void *src, unsigned long count) {
+ while (count) {
+ count -= 4;
+ writel(*(const long *)src, addr);
+ src += 4;
+ addr += 4;
+ }
+}
+
+void insb(unsigned long addr, void *dst, unsigned long count) {
+ while (count) {
+ count -= 1;
+ *(unsigned char *)dst = readb(addr);
+ dst += 1;
+ addr += 1;
+ }
+}
+
+void insw(unsigned long addr, void *dst, unsigned long count) {
+ while (count) {
+ count -= 2;
+ *(unsigned short *)dst = readw(addr);
+ dst += 2;
+ addr += 2;
+ }
+}
+
+void insl(unsigned long addr, void *dst, unsigned long count) {
+ while (count) {
+ count -= 4;
+ /*
+ * XXX I am sure we are in for an unaligned trap here.
+ */
+ *(unsigned long *)dst = readl(addr);
+ dst += 4;
+ addr += 4;
+ }
+}
diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c
new file mode 100644
index 00000000..4340ee07
--- /dev/null
+++ b/arch/xtensa/kernel/irq.c
@@ -0,0 +1,151 @@
+/*
+ * linux/arch/xtensa/kernel/irq.c
+ *
+ * Xtensa built-in interrupt controller and some generic functions copied
+ * from i386.
+ *
+ * Copyright (C) 2002 - 2006 Tensilica, Inc.
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
+ *
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Kevin Chea
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/uaccess.h>
+#include <asm/platform.h>
+
+static unsigned int cached_irq_mask;
+
+atomic_t irq_err_count;
+
+/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ */
+
+asmlinkage void do_IRQ(int irq, struct pt_regs *regs)
+{
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ if (irq >= NR_IRQS) {
+ printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
+ __func__, irq);
+ }
+
+ irq_enter();
+
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ /* Debugging check for stack overflow: is there less than 1KB free? */
+ {
+ unsigned long sp;
+
+ __asm__ __volatile__ ("mov %0, a1\n" : "=a" (sp));
+ sp &= THREAD_SIZE - 1;
+
+ if (unlikely(sp < (sizeof(thread_info) + 1024)))
+ printk("Stack overflow in do_IRQ: %ld\n",
+ sp - sizeof(struct thread_info));
+ }
+#endif
+ generic_handle_irq(irq);
+
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+ seq_printf(p, "%*s: ", prec, "ERR");
+ seq_printf(p, "%10u\n", atomic_read(&irq_err_count));
+ return 0;
+}
+
+static void xtensa_irq_mask(struct irq_data *d)
+{
+ cached_irq_mask &= ~(1 << d->irq);
+ set_sr (cached_irq_mask, INTENABLE);
+}
+
+static void xtensa_irq_unmask(struct irq_data *d)
+{
+ cached_irq_mask |= 1 << d->irq;
+ set_sr (cached_irq_mask, INTENABLE);
+}
+
+static void xtensa_irq_enable(struct irq_data *d)
+{
+ variant_irq_enable(d->irq);
+ xtensa_irq_unmask(d->irq);
+}
+
+static void xtensa_irq_disable(struct irq_data *d)
+{
+ xtensa_irq_mask(d->irq);
+ variant_irq_disable(d->irq);
+}
+
+static void xtensa_irq_ack(struct irq_data *d)
+{
+ set_sr(1 << d->irq, INTCLEAR);
+}
+
+static int xtensa_irq_retrigger(struct irq_data *d)
+{
+ set_sr (1 << d->irq, INTSET);
+ return 1;
+}
+
+
+static struct irq_chip xtensa_irq_chip = {
+ .name = "xtensa",
+ .irq_enable = xtensa_irq_enable,
+ .irq_disable = xtensa_irq_disable,
+ .irq_mask = xtensa_irq_mask,
+ .irq_unmask = xtensa_irq_unmask,
+ .irq_ack = xtensa_irq_ack,
+ .irq_retrigger = xtensa_irq_retrigger,
+};
+
+void __init init_IRQ(void)
+{
+ int index;
+
+ for (index = 0; index < XTENSA_NR_IRQS; index++) {
+ int mask = 1 << index;
+
+ if (mask & XCHAL_INTTYPE_MASK_SOFTWARE)
+ irq_set_chip_and_handler(index, &xtensa_irq_chip,
+ handle_simple_irq);
+
+ else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE)
+ irq_set_chip_and_handler(index, &xtensa_irq_chip,
+ handle_edge_irq);
+
+ else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL)
+ irq_set_chip_and_handler(index, &xtensa_irq_chip,
+ handle_level_irq);
+
+ else if (mask & XCHAL_INTTYPE_MASK_TIMER)
+ irq_set_chip_and_handler(index, &xtensa_irq_chip,
+ handle_edge_irq);
+
+ else /* XCHAL_INTTYPE_MASK_WRITE_ERROR */
+ /* XCHAL_INTTYPE_MASK_NMI */
+
+ irq_set_chip_and_handler(index, &xtensa_irq_chip,
+ handle_level_irq);
+ }
+
+ cached_irq_mask = 0;
+
+ variant_init_irq();
+}
diff --git a/arch/xtensa/kernel/module.c b/arch/xtensa/kernel/module.c
new file mode 100644
index 00000000..451dda92
--- /dev/null
+++ b/arch/xtensa/kernel/module.c
@@ -0,0 +1,192 @@
+/*
+ * arch/xtensa/kernel/module.c
+ *
+ * Module support.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2006 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/cache.h>
+
+#undef DEBUG_RELOCATE
+
+static int
+decode_calln_opcode (unsigned char *location)
+{
+#ifdef __XTENSA_EB__
+ return (location[0] & 0xf0) == 0x50;
+#endif
+#ifdef __XTENSA_EL__
+ return (location[0] & 0xf) == 0x5;
+#endif
+}
+
+static int
+decode_l32r_opcode (unsigned char *location)
+{
+#ifdef __XTENSA_EB__
+ return (location[0] & 0xf0) == 0x10;
+#endif
+#ifdef __XTENSA_EL__
+ return (location[0] & 0xf) == 0x1;
+#endif
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *mod)
+{
+ unsigned int i;
+ Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
+ Elf32_Sym *sym;
+ unsigned char *location;
+ uint32_t value;
+
+#ifdef DEBUG_RELOCATE
+ printk("Applying relocate section %u to %u\n", relsec,
+ sechdrs[relsec].sh_info);
+#endif
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
+ location = (char *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rela[i].r_offset;
+ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ + ELF32_R_SYM(rela[i].r_info);
+ value = sym->st_value + rela[i].r_addend;
+
+ switch (ELF32_R_TYPE(rela[i].r_info)) {
+ case R_XTENSA_NONE:
+ case R_XTENSA_DIFF8:
+ case R_XTENSA_DIFF16:
+ case R_XTENSA_DIFF32:
+ case R_XTENSA_ASM_EXPAND:
+ break;
+
+ case R_XTENSA_32:
+ case R_XTENSA_PLT:
+ *(uint32_t *)location += value;
+ break;
+
+ case R_XTENSA_SLOT0_OP:
+ if (decode_calln_opcode(location)) {
+ value -= ((unsigned long)location & -4) + 4;
+ if ((value & 3) != 0 ||
+ ((value + (1 << 19)) >> 20) != 0) {
+ printk("%s: relocation out of range, "
+ "section %d reloc %d "
+ "sym '%s'\n",
+ mod->name, relsec, i,
+ strtab + sym->st_name);
+ return -ENOEXEC;
+ }
+ value = (signed int)value >> 2;
+#ifdef __XTENSA_EB__
+ location[0] = ((location[0] & ~0x3) |
+ ((value >> 16) & 0x3));
+ location[1] = (value >> 8) & 0xff;
+ location[2] = value & 0xff;
+#endif
+#ifdef __XTENSA_EL__
+ location[0] = ((location[0] & ~0xc0) |
+ ((value << 6) & 0xc0));
+ location[1] = (value >> 2) & 0xff;
+ location[2] = (value >> 10) & 0xff;
+#endif
+ } else if (decode_l32r_opcode(location)) {
+ value -= (((unsigned long)location + 3) & -4);
+ if ((value & 3) != 0 ||
+ (signed int)value >> 18 != -1) {
+ printk("%s: relocation out of range, "
+ "section %d reloc %d "
+ "sym '%s'\n",
+ mod->name, relsec, i,
+ strtab + sym->st_name);
+ return -ENOEXEC;
+ }
+ value = (signed int)value >> 2;
+
+#ifdef __XTENSA_EB__
+ location[1] = (value >> 8) & 0xff;
+ location[2] = value & 0xff;
+#endif
+#ifdef __XTENSA_EL__
+ location[1] = value & 0xff;
+ location[2] = (value >> 8) & 0xff;
+#endif
+ }
+ /* FIXME: Ignore any other opcodes. The Xtensa
+ assembler currently assumes that the linker will
+ always do relaxation and so all PC-relative
+ operands need relocations. (The assembler also
+ writes out the tentative PC-relative values,
+ assuming no link-time relaxation, so it is usually
+ safe to ignore the relocations.) If the
+ assembler's "--no-link-relax" flag can be made to
+ work, and if all kernel modules can be assembled
+ with that flag, then unexpected relocations could
+ be detected here. */
+ break;
+
+ case R_XTENSA_SLOT1_OP:
+ case R_XTENSA_SLOT2_OP:
+ case R_XTENSA_SLOT3_OP:
+ case R_XTENSA_SLOT4_OP:
+ case R_XTENSA_SLOT5_OP:
+ case R_XTENSA_SLOT6_OP:
+ case R_XTENSA_SLOT7_OP:
+ case R_XTENSA_SLOT8_OP:
+ case R_XTENSA_SLOT9_OP:
+ case R_XTENSA_SLOT10_OP:
+ case R_XTENSA_SLOT11_OP:
+ case R_XTENSA_SLOT12_OP:
+ case R_XTENSA_SLOT13_OP:
+ case R_XTENSA_SLOT14_OP:
+ printk("%s: unexpected FLIX relocation: %u\n",
+ mod->name,
+ ELF32_R_TYPE(rela[i].r_info));
+ return -ENOEXEC;
+
+ case R_XTENSA_SLOT0_ALT:
+ case R_XTENSA_SLOT1_ALT:
+ case R_XTENSA_SLOT2_ALT:
+ case R_XTENSA_SLOT3_ALT:
+ case R_XTENSA_SLOT4_ALT:
+ case R_XTENSA_SLOT5_ALT:
+ case R_XTENSA_SLOT6_ALT:
+ case R_XTENSA_SLOT7_ALT:
+ case R_XTENSA_SLOT8_ALT:
+ case R_XTENSA_SLOT9_ALT:
+ case R_XTENSA_SLOT10_ALT:
+ case R_XTENSA_SLOT11_ALT:
+ case R_XTENSA_SLOT12_ALT:
+ case R_XTENSA_SLOT13_ALT:
+ case R_XTENSA_SLOT14_ALT:
+ printk("%s: unexpected ALT relocation: %u\n",
+ mod->name,
+ ELF32_R_TYPE(rela[i].r_info));
+ return -ENOEXEC;
+
+ default:
+ printk("%s: unexpected relocation: %u\n",
+ mod->name,
+ ELF32_R_TYPE(rela[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+ return 0;
+}
diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c
new file mode 100644
index 00000000..2783fda7
--- /dev/null
+++ b/arch/xtensa/kernel/pci-dma.c
@@ -0,0 +1,94 @@
+/*
+ * arch/xtensa/kernel/pci-dma.c
+ *
+ * DMA coherent memory allocation.
+ *
+ * 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.
+ *
+ * Copyright (C) 2002 - 2005 Tensilica Inc.
+ *
+ * Based on version for i386.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/gfp.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+
+/*
+ * Note: We assume that the full memory space is always mapped to 'kseg'
+ * Otherwise we have to use page attributes (not implemented).
+ */
+
+void *
+dma_alloc_coherent(struct device *dev,size_t size,dma_addr_t *handle,gfp_t flag)
+{
+ unsigned long ret;
+ unsigned long uncached = 0;
+
+ /* ignore region speicifiers */
+
+ flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+ if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
+ flag |= GFP_DMA;
+ ret = (unsigned long)__get_free_pages(flag, get_order(size));
+
+ if (ret == 0)
+ return NULL;
+
+ /* We currently don't support coherent memory outside KSEG */
+
+ if (ret < XCHAL_KSEG_CACHED_VADDR
+ || ret >= XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE)
+ BUG();
+
+
+ if (ret != 0) {
+ memset((void*) ret, 0, size);
+ uncached = ret+XCHAL_KSEG_BYPASS_VADDR-XCHAL_KSEG_CACHED_VADDR;
+ *handle = virt_to_bus((void*)ret);
+ __flush_invalidate_dcache_range(ret, size);
+ }
+
+ return (void*)uncached;
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ long addr=(long)vaddr+XCHAL_KSEG_CACHED_VADDR-XCHAL_KSEG_BYPASS_VADDR;
+
+ if (addr < 0 || addr >= XCHAL_KSEG_SIZE)
+ BUG();
+
+ free_pages(addr, get_order(size));
+}
+
+
+void consistent_sync(void *vaddr, size_t size, int direction)
+{
+ switch (direction) {
+ case PCI_DMA_NONE:
+ BUG();
+ case PCI_DMA_FROMDEVICE: /* invalidate only */
+ __invalidate_dcache_range((unsigned long)vaddr,
+ (unsigned long)size);
+ break;
+
+ case PCI_DMA_TODEVICE: /* writeback only */
+ case PCI_DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ __flush_invalidate_dcache_range((unsigned long)vaddr,
+ (unsigned long)size);
+ break;
+ }
+}
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
new file mode 100644
index 00000000..eb30e356
--- /dev/null
+++ b/arch/xtensa/kernel/pci.c
@@ -0,0 +1,384 @@
+/*
+ * arch/xtensa/kernel/pci.c
+ *
+ * PCI bios-type initialisation for PCI machines
+ *
+ * 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.
+ *
+ * Copyright (C) 2001-2005 Tensilica Inc.
+ *
+ * Based largely on work from Cort (ppc/kernel/pci.c)
+ * IO functions copied from sparc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bootmem.h>
+
+#include <asm/pci-bridge.h>
+#include <asm/platform.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/* PCI Controller */
+
+
+/*
+ * pcibios_alloc_controller
+ * pcibios_enable_device
+ * pcibios_fixups
+ * pcibios_align_resource
+ * pcibios_fixup_bus
+ * pcibios_setup
+ * pci_bus_add_device
+ * pci_mmap_page_range
+ */
+
+struct pci_controller* pci_ctrl_head;
+struct pci_controller** pci_ctrl_tail = &pci_ctrl_head;
+
+static int pci_bus_count;
+
+/*
+ * 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 have be mirrored at 0x0100-0x03ff..
+ */
+resource_size_t
+pcibios_align_resource(void *data, const struct resource *res,
+ resource_size_t size, resource_size_t align)
+{
+ struct pci_dev *dev = data;
+ resource_size_t start = res->start;
+
+ if (res->flags & IORESOURCE_IO) {
+ if (size > 0x100) {
+ printk(KERN_ERR "PCI: I/O Region %s/%d too large"
+ " (%ld bytes)\n", pci_name(dev),
+ dev->resource - res, size);
+ }
+
+ if (start & 0x300)
+ start = (start + 0x3ff) & ~0x3ff;
+ }
+
+ return start;
+}
+
+int
+pcibios_enable_resources(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++) {
+ 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;
+ }
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ cmd |= 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;
+}
+
+struct pci_controller * __init pcibios_alloc_controller(void)
+{
+ struct pci_controller *pci_ctrl;
+
+ pci_ctrl = (struct pci_controller *)alloc_bootmem(sizeof(*pci_ctrl));
+ memset(pci_ctrl, 0, sizeof(struct pci_controller));
+
+ *pci_ctrl_tail = pci_ctrl;
+ pci_ctrl_tail = &pci_ctrl->next;
+
+ return pci_ctrl;
+}
+
+static void __init pci_controller_apertures(struct pci_controller *pci_ctrl,
+ struct list_head *resources)
+{
+ struct resource *res;
+ unsigned long io_offset;
+ int i;
+
+ io_offset = (unsigned long)pci_ctrl->io_space.base;
+ res = &pci_ctrl->io_resource;
+ if (!res->flags) {
+ if (io_offset)
+ printk (KERN_ERR "I/O resource not set for host"
+ " bridge %d\n", pci_ctrl->index);
+ res->start = 0;
+ res->end = IO_SPACE_LIMIT;
+ res->flags = IORESOURCE_IO;
+ }
+ res->start += io_offset;
+ res->end += io_offset;
+ pci_add_resource_offset(resources, res, io_offset);
+
+ for (i = 0; i < 3; i++) {
+ res = &pci_ctrl->mem_resources[i];
+ if (!res->flags) {
+ if (i > 0)
+ continue;
+ printk(KERN_ERR "Memory resource not set for "
+ "host bridge %d\n", pci_ctrl->index);
+ res->start = 0;
+ res->end = ~0U;
+ res->flags = IORESOURCE_MEM;
+ }
+ pci_add_resource(resources, res);
+ }
+}
+
+static int __init pcibios_init(void)
+{
+ struct pci_controller *pci_ctrl;
+ struct list_head resources;
+ struct pci_bus *bus;
+ int next_busno = 0, i;
+
+ printk("PCI: Probing PCI hardware\n");
+
+ /* Scan all of the recorded PCI controllers. */
+ for (pci_ctrl = pci_ctrl_head; pci_ctrl; pci_ctrl = pci_ctrl->next) {
+ pci_ctrl->last_busno = 0xff;
+ INIT_LIST_HEAD(&resources);
+ pci_controller_apertures(pci_ctrl, &resources);
+ bus = pci_scan_root_bus(NULL, pci_ctrl->first_busno,
+ pci_ctrl->ops, pci_ctrl, &resources);
+ pci_ctrl->bus = bus;
+ pci_ctrl->last_busno = bus->subordinate;
+ if (next_busno <= pci_ctrl->last_busno)
+ next_busno = pci_ctrl->last_busno+1;
+ }
+ pci_bus_count = next_busno;
+
+ return platform_pcibios_fixup();
+}
+
+subsys_initcall(pcibios_init);
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+ if (bus->parent) {
+ /* This is a subordinate bridge */
+ pci_read_bridge_bases(bus);
+ }
+}
+
+char __init *pcibios_setup(char *str)
+{
+ return str;
+}
+
+void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
+
+/* the next one is stolen from the alpha port... */
+
+void __init
+pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
+
+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++) {
+ 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;
+ }
+ 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;
+}
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Return the index of the PCI controller for device pdev.
+ */
+
+int
+pci_controller_num(struct pci_dev *dev)
+{
+ struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
+ return pci_ctrl->index;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * Platform support for /proc/bus/pci/X/Y mmap()s,
+ * modelled on the sparc64 implementation by Dave Miller.
+ * -- paulus.
+ */
+
+/*
+ * Adjust vm_pgoff of VMA such that it is the physical page offset
+ * corresponding to the 32-bit pci bus offset for DEV requested by the user.
+ *
+ * Basically, the user finds the base address for his device which he wishes
+ * to mmap. They read the 32-bit value from the config space base register,
+ * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
+ * offset parameter of mmap on /proc/bus/pci/XXX for that device.
+ *
+ * Returns negative error code on failure, zero on success.
+ */
+static __inline__ int
+__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long io_offset = 0;
+ int i, res_bit;
+
+ if (pci_ctrl == 0)
+ return -EINVAL; /* should never happen */
+
+ /* If memory, add on the PCI bridge address offset */
+ if (mmap_state == pci_mmap_mem) {
+ res_bit = IORESOURCE_MEM;
+ } else {
+ io_offset = (unsigned long)pci_ctrl->io_space.base;
+ offset += io_offset;
+ res_bit = IORESOURCE_IO;
+ }
+
+ /*
+ * Check that the offset requested corresponds to one of the
+ * resources of the device.
+ */
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+ struct resource *rp = &dev->resource[i];
+ int flags = rp->flags;
+
+ /* treat ROM as memory (should be already) */
+ if (i == PCI_ROM_RESOURCE)
+ flags |= IORESOURCE_MEM;
+
+ /* Active and same type? */
+ if ((flags & res_bit) == 0)
+ continue;
+
+ /* In the range of this resource? */
+ if (offset < (rp->start & PAGE_MASK) || offset > rp->end)
+ continue;
+
+ /* found it! construct the final physical address */
+ if (mmap_state == pci_mmap_io)
+ offset += pci_ctrl->io_space.start - io_offset;
+ vma->vm_pgoff = offset >> PAGE_SHIFT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
+ * device mapping.
+ */
+static __inline__ void
+__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state, int write_combine)
+{
+ int prot = pgprot_val(vma->vm_page_prot);
+
+ /* Set to write-through */
+ prot &= ~_PAGE_NO_CACHE;
+#if 0
+ if (!write_combine)
+ prot |= _PAGE_WRITETHRU;
+#endif
+ vma->vm_page_prot = __pgprot(prot);
+}
+
+/*
+ * Perform the actual remap of the pages for a PCI device mapping, as
+ * appropriate for this architecture. The region in the process to map
+ * is described by vm_start and vm_end members of VMA, the base physical
+ * address is found in vm_pgoff.
+ * The pci device structure is provided so that architectures may make mapping
+ * decisions on a per-device or per-bus basis.
+ *
+ * Returns a negative error code on failure, zero on success.
+ */
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state,
+ int write_combine)
+{
+ int ret;
+
+ ret = __pci_mmap_make_offset(dev, vma, mmap_state);
+ if (ret < 0)
+ return ret;
+
+ __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine);
+
+ ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,vma->vm_page_prot);
+
+ return ret;
+}
diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c
new file mode 100644
index 00000000..1b91a97f
--- /dev/null
+++ b/arch/xtensa/kernel/platform.c
@@ -0,0 +1,47 @@
+/*
+ * arch/xtensa/kernel/platform.c
+ *
+ * Default platform functions.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <asm/platform.h>
+#include <asm/timex.h>
+#include <asm/param.h> /* HZ */
+
+#define _F(r,f,a,b) \
+ r __platform_##f a b; \
+ r platform_##f a __attribute__((weak, alias("__platform_"#f)))
+
+/*
+ * Default functions that are used if no platform specific function is defined.
+ * (Please, refer to include/asm-xtensa/platform.h for more information)
+ */
+
+_F(void, setup, (char** cmd), { });
+_F(void, init_irq, (void), { });
+_F(void, restart, (void), { while(1); });
+_F(void, halt, (void), { while(1); });
+_F(void, power_off, (void), { while(1); });
+_F(void, idle, (void), { __asm__ __volatile__ ("waiti 0" ::: "memory"); });
+_F(void, heartbeat, (void), { });
+_F(int, pcibios_fixup, (void), { return 0; });
+
+#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
+_F(void, calibrate_ccount, (void),
+{
+ printk ("ERROR: Cannot calibrate cpu frequency! Assuming 100MHz.\n");
+ ccount_per_jiffy = 100 * (1000000UL/HZ);
+});
+#endif
+
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
new file mode 100644
index 00000000..6a2d6edf
--- /dev/null
+++ b/arch/xtensa/kernel/process.c
@@ -0,0 +1,336 @@
+/*
+ * arch/xtensa/kernel/process.c
+ *
+ * Xtensa Processor version.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Kevin Chea
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/elf.h>
+#include <linux/init.h>
+#include <linux/prctl.h>
+#include <linux/init_task.h>
+#include <linux/module.h>
+#include <linux/mqueue.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/mmu.h>
+#include <asm/irq.h>
+#include <linux/atomic.h>
+#include <asm/asm-offsets.h>
+#include <asm/regs.h>
+
+extern void ret_from_fork(void);
+
+struct task_struct *current_set[NR_CPUS] = {&init_task, };
+
+void (*pm_power_off)(void) = NULL;
+EXPORT_SYMBOL(pm_power_off);
+
+
+#if XTENSA_HAVE_COPROCESSORS
+
+void coprocessor_release_all(struct thread_info *ti)
+{
+ unsigned long cpenable;
+ int i;
+
+ /* Make sure we don't switch tasks during this operation. */
+
+ preempt_disable();
+
+ /* Walk through all cp owners and release it for the requested one. */
+
+ cpenable = ti->cpenable;
+
+ for (i = 0; i < XCHAL_CP_MAX; i++) {
+ if (coprocessor_owner[i] == ti) {
+ coprocessor_owner[i] = 0;
+ cpenable &= ~(1 << i);
+ }
+ }
+
+ ti->cpenable = cpenable;
+ coprocessor_clear_cpenable();
+
+ preempt_enable();
+}
+
+void coprocessor_flush_all(struct thread_info *ti)
+{
+ unsigned long cpenable;
+ int i;
+
+ preempt_disable();
+
+ cpenable = ti->cpenable;
+
+ for (i = 0; i < XCHAL_CP_MAX; i++) {
+ if ((cpenable & 1) != 0 && coprocessor_owner[i] == ti)
+ coprocessor_flush(ti, i);
+ cpenable >>= 1;
+ }
+
+ preempt_enable();
+}
+
+#endif
+
+
+/*
+ * Powermanagement idle function, if any is provided by the platform.
+ */
+
+void cpu_idle(void)
+{
+ local_irq_enable();
+
+ /* endless idle loop with no priority at all */
+ while (1) {
+ while (!need_resched())
+ platform_idle();
+ schedule_preempt_disabled();
+ }
+}
+
+/*
+ * This is called when the thread calls exit().
+ */
+void exit_thread(void)
+{
+#if XTENSA_HAVE_COPROCESSORS
+ coprocessor_release_all(current_thread_info());
+#endif
+}
+
+/*
+ * Flush thread state. This is called when a thread does an execve()
+ * Note that we flush coprocessor registers for the case execve fails.
+ */
+void flush_thread(void)
+{
+#if XTENSA_HAVE_COPROCESSORS
+ struct thread_info *ti = current_thread_info();
+ coprocessor_flush_all(ti);
+ coprocessor_release_all(ti);
+#endif
+}
+
+/*
+ * This is called before the thread is copied.
+ */
+void prepare_to_copy(struct task_struct *tsk)
+{
+#if XTENSA_HAVE_COPROCESSORS
+ coprocessor_flush_all(task_thread_info(tsk));
+#endif
+}
+
+/*
+ * Copy thread.
+ *
+ * The stack layout for the new thread looks like this:
+ *
+ * +------------------------+ <- sp in childregs (= tos)
+ * | childregs |
+ * +------------------------+ <- thread.sp = sp in dummy-frame
+ * | dummy-frame | (saved in dummy-frame spill-area)
+ * +------------------------+
+ *
+ * We create a dummy frame to return to ret_from_fork:
+ * a0 points to ret_from_fork (simulating a call4)
+ * sp points to itself (thread.sp)
+ * a2, a3 are unused.
+ *
+ * Note: This is a pristine frame, so we don't need any spill region on top of
+ * childregs.
+ */
+
+int copy_thread(unsigned long clone_flags, unsigned long usp,
+ unsigned long unused,
+ struct task_struct * p, struct pt_regs * regs)
+{
+ struct pt_regs *childregs;
+ struct thread_info *ti;
+ unsigned long tos;
+ int user_mode = user_mode(regs);
+
+ /* Set up new TSS. */
+ tos = (unsigned long)task_stack_page(p) + THREAD_SIZE;
+ if (user_mode)
+ childregs = (struct pt_regs*)(tos - PT_USER_SIZE);
+ else
+ childregs = (struct pt_regs*)tos - 1;
+
+ *childregs = *regs;
+
+ /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
+ *((int*)childregs - 3) = (unsigned long)childregs;
+ *((int*)childregs - 4) = 0;
+
+ childregs->areg[1] = tos;
+ childregs->areg[2] = 0;
+ p->set_child_tid = p->clear_child_tid = NULL;
+ p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
+ p->thread.sp = (unsigned long)childregs;
+
+ if (user_mode(regs)) {
+
+ int len = childregs->wmask & ~0xf;
+ childregs->areg[1] = usp;
+ memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
+ &regs->areg[XCHAL_NUM_AREGS - len/4], len);
+// FIXME: we need to set THREADPTR in thread_info...
+ if (clone_flags & CLONE_SETTLS)
+ childregs->areg[2] = childregs->areg[6];
+
+ } else {
+ /* In kernel space, we start a new thread with a new stack. */
+ childregs->wmask = 1;
+ }
+
+#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
+ ti = task_thread_info(p);
+ ti->cpenable = 0;
+#endif
+
+ return 0;
+}
+
+
+/*
+ * These bracket the sleeping functions..
+ */
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long sp, pc;
+ unsigned long stack_page = (unsigned long) task_stack_page(p);
+ int count = 0;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ sp = p->thread.sp;
+ pc = MAKE_PC_FROM_RA(p->thread.ra, p->thread.sp);
+
+ do {
+ if (sp < stack_page + sizeof(struct task_struct) ||
+ sp >= (stack_page + THREAD_SIZE) ||
+ pc == 0)
+ return 0;
+ if (!in_sched_functions(pc))
+ return pc;
+
+ /* Stack layout: sp-4: ra, sp-3: sp' */
+
+ pc = MAKE_PC_FROM_RA(*(unsigned long*)sp - 4, sp);
+ sp = *(unsigned long *)sp - 3;
+ } while (count++ < 16);
+ return 0;
+}
+
+/*
+ * xtensa_gregset_t and 'struct pt_regs' are vastly different formats
+ * of processor registers. Besides different ordering,
+ * xtensa_gregset_t contains non-live register information that
+ * 'struct pt_regs' does not. Exception handling (primarily) uses
+ * 'struct pt_regs'. Core files and ptrace use xtensa_gregset_t.
+ *
+ */
+
+void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
+{
+ unsigned long wb, ws, wm;
+ int live, last;
+
+ wb = regs->windowbase;
+ ws = regs->windowstart;
+ wm = regs->wmask;
+ ws = ((ws >> wb) | (ws << (WSBITS - wb))) & ((1 << WSBITS) - 1);
+
+ /* Don't leak any random bits. */
+
+ memset(elfregs, 0, sizeof (elfregs));
+
+ /* Note: PS.EXCM is not set while user task is running; its
+ * being set in regs->ps is for exception handling convenience.
+ */
+
+ elfregs->pc = regs->pc;
+ elfregs->ps = (regs->ps & ~(1 << PS_EXCM_BIT));
+ elfregs->lbeg = regs->lbeg;
+ elfregs->lend = regs->lend;
+ elfregs->lcount = regs->lcount;
+ elfregs->sar = regs->sar;
+ elfregs->windowstart = ws;
+
+ live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
+ last = XCHAL_NUM_AREGS - (wm >> 4) * 4;
+ memcpy(elfregs->a, regs->areg, live * 4);
+ memcpy(elfregs->a + last, regs->areg + last, (wm >> 4) * 16);
+}
+
+int dump_fpu(void)
+{
+ return 0;
+}
+
+asmlinkage
+long xtensa_clone(unsigned long clone_flags, unsigned long newsp,
+ void __user *parent_tid, void *child_tls,
+ void __user *child_tid, long a5,
+ struct pt_regs *regs)
+{
+ if (!newsp)
+ newsp = regs->areg[1];
+ return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);
+}
+
+/*
+ * xtensa_execve() executes a new program.
+ */
+
+asmlinkage
+long xtensa_execve(const char __user *name,
+ const char __user *const __user *argv,
+ const char __user *const __user *envp,
+ long a3, long a4, long a5,
+ struct pt_regs *regs)
+{
+ long error;
+ char * filename;
+
+ filename = getname(name);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, argv, envp, regs);
+ putname(filename);
+out:
+ return error;
+}
+
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
new file mode 100644
index 00000000..33eea4c1
--- /dev/null
+++ b/arch/xtensa/kernel/ptrace.c
@@ -0,0 +1,346 @@
+// TODO some minor issues
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2007 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Scott Foehner<sfoehner@yahoo.com>,
+ * Kevin Chea
+ * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/security.h>
+#include <linux/signal.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/elf.h>
+#include <asm/coprocessor.h>
+
+
+void user_enable_single_step(struct task_struct *child)
+{
+ child->ptrace |= PT_SINGLESTEP;
+}
+
+void user_disable_single_step(struct task_struct *child)
+{
+ child->ptrace &= ~PT_SINGLESTEP;
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching to disable single stepping.
+ */
+
+void ptrace_disable(struct task_struct *child)
+{
+ /* Nothing to do.. */
+}
+
+int ptrace_getregs(struct task_struct *child, void __user *uregs)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+ xtensa_gregset_t __user *gregset = uregs;
+ unsigned long wm = regs->wmask;
+ unsigned long wb = regs->windowbase;
+ int live, i;
+
+ if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
+ return -EIO;
+
+ __put_user(regs->pc, &gregset->pc);
+ __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps);
+ __put_user(regs->lbeg, &gregset->lbeg);
+ __put_user(regs->lend, &gregset->lend);
+ __put_user(regs->lcount, &gregset->lcount);
+ __put_user(regs->windowstart, &gregset->windowstart);
+ __put_user(regs->windowbase, &gregset->windowbase);
+
+ live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
+
+ for (i = 0; i < live; i++)
+ __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS));
+ for (i = XCHAL_NUM_AREGS - (wm >> 4) * 4; i < XCHAL_NUM_AREGS; i++)
+ __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS));
+
+ return 0;
+}
+
+int ptrace_setregs(struct task_struct *child, void __user *uregs)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+ xtensa_gregset_t *gregset = uregs;
+ const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
+ unsigned long ps;
+ unsigned long wb;
+
+ if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
+ return -EIO;
+
+ __get_user(regs->pc, &gregset->pc);
+ __get_user(ps, &gregset->ps);
+ __get_user(regs->lbeg, &gregset->lbeg);
+ __get_user(regs->lend, &gregset->lend);
+ __get_user(regs->lcount, &gregset->lcount);
+ __get_user(regs->windowstart, &gregset->windowstart);
+ __get_user(wb, &gregset->windowbase);
+
+ regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
+
+ if (wb >= XCHAL_NUM_AREGS / 4)
+ return -EFAULT;
+
+ regs->windowbase = wb;
+
+ if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4,
+ gregset->a, wb * 16))
+ return -EFAULT;
+
+ if (__copy_from_user(regs->areg, gregset->a + wb*4, (WSBITS-wb) * 16))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+int ptrace_getxregs(struct task_struct *child, void __user *uregs)
+{
+ struct pt_regs *regs = task_pt_regs(child);
+ struct thread_info *ti = task_thread_info(child);
+ elf_xtregs_t __user *xtregs = uregs;
+ int ret = 0;
+
+ if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
+ return -EIO;
+
+#if XTENSA_HAVE_COPROCESSORS
+ /* Flush all coprocessor registers to memory. */
+ coprocessor_flush_all(ti);
+ ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
+ sizeof(xtregs_coprocessor_t));
+#endif
+ ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
+ sizeof(xtregs->opt));
+ ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user,
+ sizeof(xtregs->user));
+
+ return ret ? -EFAULT : 0;
+}
+
+int ptrace_setxregs(struct task_struct *child, void __user *uregs)
+{
+ struct thread_info *ti = task_thread_info(child);
+ struct pt_regs *regs = task_pt_regs(child);
+ elf_xtregs_t *xtregs = uregs;
+ int ret = 0;
+
+ if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
+ return -EFAULT;
+
+#if XTENSA_HAVE_COPROCESSORS
+ /* Flush all coprocessors before we overwrite them. */
+ coprocessor_flush_all(ti);
+ coprocessor_release_all(ti);
+
+ ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
+ sizeof(xtregs_coprocessor_t));
+#endif
+ ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
+ sizeof(xtregs->opt));
+ ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user,
+ sizeof(xtregs->user));
+
+ return ret ? -EFAULT : 0;
+}
+
+int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret)
+{
+ struct pt_regs *regs;
+ unsigned long tmp;
+
+ regs = task_pt_regs(child);
+ tmp = 0; /* Default return value. */
+
+ switch(regno) {
+
+ case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
+ tmp = regs->areg[regno - REG_AR_BASE];
+ break;
+
+ case REG_A_BASE ... REG_A_BASE + 15:
+ tmp = regs->areg[regno - REG_A_BASE];
+ break;
+
+ case REG_PC:
+ tmp = regs->pc;
+ break;
+
+ case REG_PS:
+ /* Note: PS.EXCM is not set while user task is running;
+ * its being set in regs is for exception handling
+ * convenience. */
+ tmp = (regs->ps & ~(1 << PS_EXCM_BIT));
+ break;
+
+ case REG_WB:
+ break; /* tmp = 0 */
+
+ case REG_WS:
+ {
+ unsigned long wb = regs->windowbase;
+ unsigned long ws = regs->windowstart;
+ tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1);
+ break;
+ }
+ case REG_LBEG:
+ tmp = regs->lbeg;
+ break;
+
+ case REG_LEND:
+ tmp = regs->lend;
+ break;
+
+ case REG_LCOUNT:
+ tmp = regs->lcount;
+ break;
+
+ case REG_SAR:
+ tmp = regs->sar;
+ break;
+
+ case SYSCALL_NR:
+ tmp = regs->syscall;
+ break;
+
+ default:
+ return -EIO;
+ }
+ return put_user(tmp, ret);
+}
+
+int ptrace_pokeusr(struct task_struct *child, long regno, long val)
+{
+ struct pt_regs *regs;
+ regs = task_pt_regs(child);
+
+ switch (regno) {
+ case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
+ regs->areg[regno - REG_AR_BASE] = val;
+ break;
+
+ case REG_A_BASE ... REG_A_BASE + 15:
+ regs->areg[regno - REG_A_BASE] = val;
+ break;
+
+ case REG_PC:
+ regs->pc = val;
+ break;
+
+ case SYSCALL_NR:
+ regs->syscall = val;
+ break;
+
+ default:
+ return -EIO;
+ }
+ return 0;
+}
+
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ int ret = -EPERM;
+ void __user *datap = (void __user *) data;
+
+ switch (request) {
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA:
+ ret = generic_ptrace_peekdata(child, addr, data);
+ break;
+
+ case PTRACE_PEEKUSR: /* read register specified by addr. */
+ ret = ptrace_peekusr(child, addr, datap);
+ break;
+
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ ret = generic_ptrace_pokedata(child, addr, data);
+ break;
+
+ case PTRACE_POKEUSR: /* write register specified by addr. */
+ ret = ptrace_pokeusr(child, addr, data);
+ break;
+
+ case PTRACE_GETREGS:
+ ret = ptrace_getregs(child, datap);
+ break;
+
+ case PTRACE_SETREGS:
+ ret = ptrace_setregs(child, datap);
+ break;
+
+ case PTRACE_GETXTREGS:
+ ret = ptrace_getxregs(child, datap);
+ break;
+
+ case PTRACE_SETXTREGS:
+ ret = ptrace_setxregs(child, datap);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ return ret;
+}
+
+void do_syscall_trace(void)
+{
+ /*
+ * 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;
+ }
+}
+
+void do_syscall_trace_enter(struct pt_regs *regs)
+{
+ if (test_thread_flag(TIF_SYSCALL_TRACE)
+ && (current->ptrace & PT_PTRACED))
+ do_syscall_trace();
+
+#if 0
+ audit_syscall_entry(current, AUDIT_ARCH_XTENSA..);
+#endif
+}
+
+void do_syscall_trace_leave(struct pt_regs *regs)
+{
+ if ((test_thread_flag(TIF_SYSCALL_TRACE))
+ && (current->ptrace & PT_PTRACED))
+ do_syscall_trace();
+}
+
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
new file mode 100644
index 00000000..17e746f7
--- /dev/null
+++ b/arch/xtensa/kernel/setup.c
@@ -0,0 +1,480 @@
+/*
+ * arch/xtensa/kernel/setup.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Kevin Chea
+ * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/screen_info.h>
+#include <linux/bootmem.h>
+#include <linux/kernel.h>
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+# include <linux/console.h>
+#endif
+
+#ifdef CONFIG_RTC
+# include <linux/timex.h>
+#endif
+
+#ifdef CONFIG_PROC_FS
+# include <linux/seq_file.h>
+#endif
+
+#include <asm/bootparam.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/timex.h>
+#include <asm/platform.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/param.h>
+
+#include <platform/hardware.h>
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+struct screen_info screen_info = { 0, 24, 0, 0, 0, 80, 0, 0, 0, 24, 1, 16};
+#endif
+
+#ifdef CONFIG_BLK_DEV_FD
+extern struct fd_ops no_fd_ops;
+struct fd_ops *fd_ops;
+#endif
+
+extern struct rtc_ops no_rtc_ops;
+struct rtc_ops *rtc_ops;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+extern void *initrd_start;
+extern void *initrd_end;
+extern void *__initrd_start;
+extern void *__initrd_end;
+int initrd_is_mapped = 0;
+extern int initrd_below_start_ok;
+#endif
+
+unsigned char aux_device_present;
+extern unsigned long loops_per_jiffy;
+
+/* Command line specified as configuration option. */
+
+static char __initdata command_line[COMMAND_LINE_SIZE];
+
+#ifdef CONFIG_CMDLINE_BOOL
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
+#endif
+
+sysmem_info_t __initdata sysmem;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+int initrd_is_mapped;
+#endif
+
+#ifdef CONFIG_MMU
+extern void init_mmu(void);
+#else
+static inline void init_mmu(void) { }
+#endif
+
+extern void zones_init(void);
+
+/*
+ * Boot parameter parsing.
+ *
+ * The Xtensa port uses a list of variable-sized tags to pass data to
+ * the kernel. The first tag must be a BP_TAG_FIRST tag for the list
+ * to be recognised. The list is terminated with a zero-sized
+ * BP_TAG_LAST tag.
+ */
+
+typedef struct tagtable {
+ u32 tag;
+ int (*parse)(const bp_tag_t*);
+} tagtable_t;
+
+#define __tagtable(tag, fn) static tagtable_t __tagtable_##fn \
+ __attribute__((unused, __section__(".taglist"))) = { tag, fn }
+
+/* parse current tag */
+
+static int __init parse_tag_mem(const bp_tag_t *tag)
+{
+ meminfo_t *mi = (meminfo_t*)(tag->data);
+
+ if (mi->type != MEMORY_TYPE_CONVENTIONAL)
+ return -1;
+
+ if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) {
+ printk(KERN_WARNING
+ "Ignoring memory bank 0x%08lx size %ldKB\n",
+ (unsigned long)mi->start,
+ (unsigned long)mi->end - (unsigned long)mi->start);
+ return -EINVAL;
+ }
+ sysmem.bank[sysmem.nr_banks].type = mi->type;
+ sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start);
+ sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_SIZE;
+ sysmem.nr_banks++;
+
+ return 0;
+}
+
+__tagtable(BP_TAG_MEMORY, parse_tag_mem);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static int __init parse_tag_initrd(const bp_tag_t* tag)
+{
+ meminfo_t* mi;
+ mi = (meminfo_t*)(tag->data);
+ initrd_start = (void*)(mi->start);
+ initrd_end = (void*)(mi->end);
+
+ return 0;
+}
+
+__tagtable(BP_TAG_INITRD, parse_tag_initrd);
+
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+static int __init parse_tag_cmdline(const bp_tag_t* tag)
+{
+ strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE);
+ command_line[COMMAND_LINE_SIZE - 1] = '\0';
+ return 0;
+}
+
+__tagtable(BP_TAG_COMMAND_LINE, parse_tag_cmdline);
+
+static int __init parse_bootparam(const bp_tag_t* tag)
+{
+ extern tagtable_t __tagtable_begin, __tagtable_end;
+ tagtable_t *t;
+
+ /* Boot parameters must start with a BP_TAG_FIRST tag. */
+
+ if (tag->id != BP_TAG_FIRST) {
+ printk(KERN_WARNING "Invalid boot parameters!\n");
+ return 0;
+ }
+
+ tag = (bp_tag_t*)((unsigned long)tag + sizeof(bp_tag_t) + tag->size);
+
+ /* Parse all tags. */
+
+ while (tag != NULL && tag->id != BP_TAG_LAST) {
+ for (t = &__tagtable_begin; t < &__tagtable_end; t++) {
+ if (tag->id == t->tag) {
+ t->parse(tag);
+ break;
+ }
+ }
+ if (t == &__tagtable_end)
+ printk(KERN_WARNING "Ignoring tag "
+ "0x%08x\n", tag->id);
+ tag = (bp_tag_t*)((unsigned long)(tag + 1) + tag->size);
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize architecture. (Early stage)
+ */
+
+void __init init_arch(bp_tag_t *bp_start)
+{
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ initrd_start = &__initrd_start;
+ initrd_end = &__initrd_end;
+#endif
+
+ sysmem.nr_banks = 0;
+
+#ifdef CONFIG_CMDLINE_BOOL
+ strcpy(command_line, default_command_line);
+#endif
+
+ /* Parse boot parameters */
+
+ if (bp_start)
+ parse_bootparam(bp_start);
+
+ if (sysmem.nr_banks == 0) {
+ sysmem.nr_banks = 1;
+ sysmem.bank[0].start = PLATFORM_DEFAULT_MEM_START;
+ sysmem.bank[0].end = PLATFORM_DEFAULT_MEM_START
+ + PLATFORM_DEFAULT_MEM_SIZE;
+ }
+
+ /* Early hook for platforms */
+
+ platform_init(bp_start);
+
+ /* Initialize MMU. */
+
+ init_mmu();
+}
+
+/*
+ * Initialize system. Setup memory and reserve regions.
+ */
+
+extern char _end;
+extern char _stext;
+extern char _WindowVectors_text_start;
+extern char _WindowVectors_text_end;
+extern char _DebugInterruptVector_literal_start;
+extern char _DebugInterruptVector_text_end;
+extern char _KernelExceptionVector_literal_start;
+extern char _KernelExceptionVector_text_end;
+extern char _UserExceptionVector_literal_start;
+extern char _UserExceptionVector_text_end;
+extern char _DoubleExceptionVector_literal_start;
+extern char _DoubleExceptionVector_text_end;
+
+void __init setup_arch(char **cmdline_p)
+{
+ extern int mem_reserve(unsigned long, unsigned long, int);
+ extern void bootmem_init(void);
+
+ memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
+ boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
+ *cmdline_p = command_line;
+
+ /* Reserve some memory regions */
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start < initrd_end) {
+ initrd_is_mapped = mem_reserve(__pa(initrd_start),
+ __pa(initrd_end), 0);
+ initrd_below_start_ok = 1;
+ } else {
+ initrd_start = 0;
+ }
+#endif
+
+ mem_reserve(__pa(&_stext),__pa(&_end), 1);
+
+ mem_reserve(__pa(&_WindowVectors_text_start),
+ __pa(&_WindowVectors_text_end), 0);
+
+ mem_reserve(__pa(&_DebugInterruptVector_literal_start),
+ __pa(&_DebugInterruptVector_text_end), 0);
+
+ mem_reserve(__pa(&_KernelExceptionVector_literal_start),
+ __pa(&_KernelExceptionVector_text_end), 0);
+
+ mem_reserve(__pa(&_UserExceptionVector_literal_start),
+ __pa(&_UserExceptionVector_text_end), 0);
+
+ mem_reserve(__pa(&_DoubleExceptionVector_literal_start),
+ __pa(&_DoubleExceptionVector_text_end), 0);
+
+ bootmem_init();
+
+ platform_setup(cmdline_p);
+
+
+ paging_init();
+ zones_init();
+
+#ifdef CONFIG_VT
+# if defined(CONFIG_VGA_CONSOLE)
+ conswitchp = &vga_con;
+# elif defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+# endif
+#endif
+
+#ifdef CONFIG_PCI
+ platform_pcibios_init();
+#endif
+}
+
+void machine_restart(char * cmd)
+{
+ platform_restart();
+}
+
+void machine_halt(void)
+{
+ platform_halt();
+ while (1);
+}
+
+void machine_power_off(void)
+{
+ platform_power_off();
+ while (1);
+}
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Display some core information through /proc/cpuinfo.
+ */
+
+static int
+c_show(struct seq_file *f, void *slot)
+{
+ /* high-level stuff */
+ seq_printf(f,"processor\t: 0\n"
+ "vendor_id\t: Tensilica\n"
+ "model\t\t: Xtensa " XCHAL_HW_VERSION_NAME "\n"
+ "core ID\t\t: " XCHAL_CORE_ID "\n"
+ "build ID\t: 0x%x\n"
+ "byte order\t: %s\n"
+ "cpu MHz\t\t: %lu.%02lu\n"
+ "bogomips\t: %lu.%02lu\n",
+ XCHAL_BUILD_UNIQUE_ID,
+ XCHAL_HAVE_BE ? "big" : "little",
+ CCOUNT_PER_JIFFY/(1000000/HZ),
+ (CCOUNT_PER_JIFFY/(10000/HZ)) % 100,
+ loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ)) % 100);
+
+ seq_printf(f,"flags\t\t: "
+#if XCHAL_HAVE_NMI
+ "nmi "
+#endif
+#if XCHAL_HAVE_DEBUG
+ "debug "
+# if XCHAL_HAVE_OCD
+ "ocd "
+# endif
+#endif
+#if XCHAL_HAVE_DENSITY
+ "density "
+#endif
+#if XCHAL_HAVE_BOOLEANS
+ "boolean "
+#endif
+#if XCHAL_HAVE_LOOPS
+ "loop "
+#endif
+#if XCHAL_HAVE_NSA
+ "nsa "
+#endif
+#if XCHAL_HAVE_MINMAX
+ "minmax "
+#endif
+#if XCHAL_HAVE_SEXT
+ "sext "
+#endif
+#if XCHAL_HAVE_CLAMPS
+ "clamps "
+#endif
+#if XCHAL_HAVE_MAC16
+ "mac16 "
+#endif
+#if XCHAL_HAVE_MUL16
+ "mul16 "
+#endif
+#if XCHAL_HAVE_MUL32
+ "mul32 "
+#endif
+#if XCHAL_HAVE_MUL32_HIGH
+ "mul32h "
+#endif
+#if XCHAL_HAVE_FP
+ "fpu "
+#endif
+ "\n");
+
+ /* Registers. */
+ seq_printf(f,"physical aregs\t: %d\n"
+ "misc regs\t: %d\n"
+ "ibreak\t\t: %d\n"
+ "dbreak\t\t: %d\n",
+ XCHAL_NUM_AREGS,
+ XCHAL_NUM_MISC_REGS,
+ XCHAL_NUM_IBREAK,
+ XCHAL_NUM_DBREAK);
+
+
+ /* Interrupt. */
+ seq_printf(f,"num ints\t: %d\n"
+ "ext ints\t: %d\n"
+ "int levels\t: %d\n"
+ "timers\t\t: %d\n"
+ "debug level\t: %d\n",
+ XCHAL_NUM_INTERRUPTS,
+ XCHAL_NUM_EXTINTERRUPTS,
+ XCHAL_NUM_INTLEVELS,
+ XCHAL_NUM_TIMERS,
+ XCHAL_DEBUGLEVEL);
+
+ /* Cache */
+ seq_printf(f,"icache line size: %d\n"
+ "icache ways\t: %d\n"
+ "icache size\t: %d\n"
+ "icache flags\t: "
+#if XCHAL_ICACHE_LINE_LOCKABLE
+ "lock"
+#endif
+ "\n"
+ "dcache line size: %d\n"
+ "dcache ways\t: %d\n"
+ "dcache size\t: %d\n"
+ "dcache flags\t: "
+#if XCHAL_DCACHE_IS_WRITEBACK
+ "writeback"
+#endif
+#if XCHAL_DCACHE_LINE_LOCKABLE
+ "lock"
+#endif
+ "\n",
+ XCHAL_ICACHE_LINESIZE,
+ XCHAL_ICACHE_WAYS,
+ XCHAL_ICACHE_SIZE,
+ XCHAL_DCACHE_LINESIZE,
+ XCHAL_DCACHE_WAYS,
+ XCHAL_DCACHE_SIZE);
+
+ return 0;
+}
+
+/*
+ * We show only CPU #0 info.
+ */
+static void *
+c_start(struct seq_file *f, loff_t *pos)
+{
+ return (void *) ((*pos == 0) ? (void *)1 : NULL);
+}
+
+static void *
+c_next(struct seq_file *f, void *v, loff_t *pos)
+{
+ return NULL;
+}
+
+static void
+c_stop(struct seq_file *f, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op =
+{
+ start: c_start,
+ next: c_next,
+ stop: c_stop,
+ show: c_show
+};
+
+#endif /* CONFIG_PROC_FS */
+
diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c
new file mode 100644
index 00000000..d78869a0
--- /dev/null
+++ b/arch/xtensa/kernel/signal.c
@@ -0,0 +1,565 @@
+/*
+ * arch/xtensa/kernel/signal.c
+ *
+ * Default platform functions.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005, 2006 Tensilica Inc.
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com>
+ */
+
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/personality.h>
+#include <linux/freezer.h>
+
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/coprocessor.h>
+#include <asm/unistd.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+extern struct task_struct *coproc_owners[];
+
+struct rt_sigframe
+{
+ struct siginfo info;
+ struct ucontext uc;
+ struct {
+ xtregs_opt_t opt;
+ xtregs_user_t user;
+#if XTENSA_HAVE_COPROCESSORS
+ xtregs_coprocessor_t cp;
+#endif
+ } xtregs;
+ unsigned char retcode[6];
+ unsigned int window[4];
+};
+
+/*
+ * Flush register windows stored in pt_regs to stack.
+ * Returns 1 for errors.
+ */
+
+int
+flush_window_regs_user(struct pt_regs *regs)
+{
+ const unsigned long ws = regs->windowstart;
+ const unsigned long wb = regs->windowbase;
+ unsigned long sp = 0;
+ unsigned long wm;
+ int err = 1;
+ int base;
+
+ /* Return if no other frames. */
+
+ if (regs->wmask == 1)
+ return 0;
+
+ /* Rotate windowmask and skip empty frames. */
+
+ wm = (ws >> wb) | (ws << (XCHAL_NUM_AREGS / 4 - wb));
+ base = (XCHAL_NUM_AREGS / 4) - (regs->wmask >> 4);
+
+ /* For call8 or call12 frames, we need the previous stack pointer. */
+
+ if ((regs->wmask & 2) == 0)
+ if (__get_user(sp, (int*)(regs->areg[base * 4 + 1] - 12)))
+ goto errout;
+
+ /* Spill frames to stack. */
+
+ while (base < XCHAL_NUM_AREGS / 4) {
+
+ int m = (wm >> base);
+ int inc = 0;
+
+ /* Save registers a4..a7 (call8) or a4...a11 (call12) */
+
+ if (m & 2) { /* call4 */
+ inc = 1;
+
+ } else if (m & 4) { /* call8 */
+ if (copy_to_user((void*)(sp - 32),
+ &regs->areg[(base + 1) * 4], 16))
+ goto errout;
+ inc = 2;
+
+ } else if (m & 8) { /* call12 */
+ if (copy_to_user((void*)(sp - 48),
+ &regs->areg[(base + 1) * 4], 32))
+ goto errout;
+ inc = 3;
+ }
+
+ /* Save current frame a0..a3 under next SP */
+
+ sp = regs->areg[((base + inc) * 4 + 1) % XCHAL_NUM_AREGS];
+ if (copy_to_user((void*)(sp - 16), &regs->areg[base * 4], 16))
+ goto errout;
+
+ /* Get current stack pointer for next loop iteration. */
+
+ sp = regs->areg[base * 4 + 1];
+ base += inc;
+ }
+
+ regs->wmask = 1;
+ regs->windowstart = 1 << wb;
+
+ return 0;
+
+errout:
+ return err;
+}
+
+/*
+ * Note: We don't copy double exception 'regs', we have to finish double exc.
+ * first before we return to signal handler! This dbl.exc.handler might cause
+ * another double exception, but I think we are fine as the situation is the
+ * same as if we had returned to the signal handerl and got an interrupt
+ * immediately...
+ */
+
+static int
+setup_sigcontext(struct rt_sigframe __user *frame, struct pt_regs *regs)
+{
+ struct sigcontext __user *sc = &frame->uc.uc_mcontext;
+ struct thread_info *ti = current_thread_info();
+ int err = 0;
+
+#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
+ COPY(pc);
+ COPY(ps);
+ COPY(lbeg);
+ COPY(lend);
+ COPY(lcount);
+ COPY(sar);
+#undef COPY
+
+ err |= flush_window_regs_user(regs);
+ err |= __copy_to_user (sc->sc_a, regs->areg, 16 * 4);
+ err |= __put_user(0, &sc->sc_xtregs);
+
+ if (err)
+ return err;
+
+#if XTENSA_HAVE_COPROCESSORS
+ coprocessor_flush_all(ti);
+ coprocessor_release_all(ti);
+ err |= __copy_to_user(&frame->xtregs.cp, &ti->xtregs_cp,
+ sizeof (frame->xtregs.cp));
+#endif
+ err |= __copy_to_user(&frame->xtregs.opt, &regs->xtregs_opt,
+ sizeof (xtregs_opt_t));
+ err |= __copy_to_user(&frame->xtregs.user, &ti->xtregs_user,
+ sizeof (xtregs_user_t));
+
+ err |= __put_user(err ? NULL : &frame->xtregs, &sc->sc_xtregs);
+
+ return err;
+}
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct rt_sigframe __user *frame)
+{
+ struct sigcontext __user *sc = &frame->uc.uc_mcontext;
+ struct thread_info *ti = current_thread_info();
+ unsigned int err = 0;
+ unsigned long ps;
+
+#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
+ COPY(pc);
+ COPY(lbeg);
+ COPY(lend);
+ COPY(lcount);
+ COPY(sar);
+#undef COPY
+
+ /* All registers were flushed to stack. Start with a prestine frame. */
+
+ regs->wmask = 1;
+ regs->windowbase = 0;
+ regs->windowstart = 1;
+
+ regs->syscall = -1; /* disable syscall checks */
+
+ /* For PS, restore only PS.CALLINC.
+ * Assume that all other bits are either the same as for the signal
+ * handler, or the user mode value doesn't matter (e.g. PS.OWB).
+ */
+ err |= __get_user(ps, &sc->sc_ps);
+ regs->ps = (regs->ps & ~PS_CALLINC_MASK) | (ps & PS_CALLINC_MASK);
+
+ /* Additional corruption checks */
+
+ if ((regs->lcount > 0)
+ && ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )
+ err = 1;
+
+ err |= __copy_from_user(regs->areg, sc->sc_a, 16 * 4);
+
+ if (err)
+ return err;
+
+ /* The signal handler may have used coprocessors in which
+ * case they are still enabled. We disable them to force a
+ * reloading of the original task's CP state by the lazy
+ * context-switching mechanisms of CP exception handling.
+ * Also, we essentially discard any coprocessor state that the
+ * signal handler created. */
+
+#if XTENSA_HAVE_COPROCESSORS
+ coprocessor_release_all(ti);
+ err |= __copy_from_user(&ti->xtregs_cp, &frame->xtregs.cp,
+ sizeof (frame->xtregs.cp));
+#endif
+ err |= __copy_from_user(&ti->xtregs_user, &frame->xtregs.user,
+ sizeof (xtregs_user_t));
+ err |= __copy_from_user(&regs->xtregs_opt, &frame->xtregs.opt,
+ sizeof (xtregs_opt_t));
+
+ return err;
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+asmlinkage long xtensa_rt_sigreturn(long a0, long a1, long a2, long a3,
+ long a4, long a5, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ sigset_t set;
+ int ret;
+
+ if (regs->depc > 64)
+ panic("rt_sigreturn in double exception!\n");
+
+ frame = (struct rt_sigframe __user *) regs->areg[1];
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ set_current_blocked(&set);
+
+ if (restore_sigcontext(regs, frame))
+ goto badframe;
+
+ ret = regs->areg[2];
+
+ if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->areg[1]) == -EFAULT)
+ goto badframe;
+
+ return ret;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+gen_return_code(unsigned char *codemem)
+{
+ int err = 0;
+
+ /*
+ * The 12-bit immediate is really split up within the 24-bit MOVI
+ * instruction. As long as the above system call numbers fit within
+ * 8-bits, the following code works fine. See the Xtensa ISA for
+ * details.
+ */
+
+#if __NR_rt_sigreturn > 255
+# error Generating the MOVI instruction below breaks!
+#endif
+
+#ifdef __XTENSA_EB__ /* Big Endian version */
+ /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
+ err |= __put_user(0x22, &codemem[0]);
+ err |= __put_user(0x0a, &codemem[1]);
+ err |= __put_user(__NR_rt_sigreturn, &codemem[2]);
+ /* Generate instruction: SYSCALL */
+ err |= __put_user(0x00, &codemem[3]);
+ err |= __put_user(0x05, &codemem[4]);
+ err |= __put_user(0x00, &codemem[5]);
+
+#elif defined __XTENSA_EL__ /* Little Endian version */
+ /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
+ err |= __put_user(0x22, &codemem[0]);
+ err |= __put_user(0xa0, &codemem[1]);
+ err |= __put_user(__NR_rt_sigreturn, &codemem[2]);
+ /* Generate instruction: SYSCALL */
+ err |= __put_user(0x00, &codemem[3]);
+ err |= __put_user(0x50, &codemem[4]);
+ err |= __put_user(0x00, &codemem[5]);
+#else
+# error Must use compiler for Xtensa processors.
+#endif
+
+ /* Flush generated code out of the data cache */
+
+ if (err == 0) {
+ __invalidate_icache_range((unsigned long)codemem, 6UL);
+ __flush_invalidate_dcache_range((unsigned long)codemem, 6UL);
+ }
+
+ return err;
+}
+
+
+static int setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe *frame;
+ int err = 0;
+ int signal;
+ unsigned long sp, ra;
+
+ sp = regs->areg[1];
+
+ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) {
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ frame = (void *)((sp - sizeof(*frame)) & -16ul);
+
+ if (regs->depc > 64)
+ panic ("Double exception sys_sigreturn\n");
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) {
+ goto give_sigsegv;
+ }
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ if (ka->sa.sa_flags & SA_SIGINFO) {
+ err |= copy_siginfo_to_user(&frame->info, info);
+ }
+
+ /* Create the user context. */
+
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user((void *)current->sas_ss_sp,
+ &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->areg[1]),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= setup_sigcontext(frame, regs);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ ra = (unsigned long)ka->sa.sa_restorer;
+ } else {
+
+ /* Create sys_rt_sigreturn syscall in stack frame */
+
+ err |= gen_return_code(frame->retcode);
+
+ if (err) {
+ goto give_sigsegv;
+ }
+ ra = (unsigned long) frame->retcode;
+ }
+
+ /*
+ * Create signal handler execution context.
+ * Return context not modified until this point.
+ */
+
+ /* Set up registers for signal handler */
+ start_thread(regs, (unsigned long) ka->sa.sa_handler,
+ (unsigned long) frame);
+
+ /* Set up a stack frame for a call4
+ * Note: PS.CALLINC is set to one by start_thread
+ */
+ regs->areg[4] = (((unsigned long) ra) & 0x3fffffff) | 0x40000000;
+ regs->areg[6] = (unsigned long) signal;
+ regs->areg[7] = (unsigned long) &frame->info;
+ regs->areg[8] = (unsigned long) &frame->uc;
+
+ /* Set access mode to USER_DS. Nomenclature is outdated, but
+ * functionality is used in uaccess.h
+ */
+ set_fs(USER_DS);
+
+#if DEBUG_SIG
+ printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n",
+ current->comm, current->pid, signal, frame, regs->pc);
+#endif
+
+ return 0;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+ return -EFAULT;
+}
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+
+asmlinkage long xtensa_rt_sigsuspend(sigset_t __user *unewset,
+ size_t sigsetsize,
+ long a2, long a3, long a4, long a5,
+ 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);
+ saveset = current->blocked;
+ set_current_blocked(&newset);
+
+ regs->areg[2] = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(regs, &saveset))
+ return -EINTR;
+ }
+}
+
+asmlinkage long xtensa_sigaltstack(const stack_t __user *uss,
+ stack_t __user *uoss,
+ long a2, long a3, long a4, long a5,
+ struct pt_regs *regs)
+{
+ return do_sigaltstack(uss, uoss, regs->areg[1]);
+}
+
+
+
+/*
+ * 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.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+
+ if (!user_mode(regs))
+ return 0;
+
+ if (try_to_freeze())
+ goto no_signal;
+
+ if (!oldset)
+ oldset = &current->blocked;
+
+ task_pt_regs(current)->icountlevel = 0;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+
+ if (signr > 0) {
+ int ret;
+
+ /* Are we from a system call? */
+
+ if ((signed)regs->syscall >= 0) {
+
+ /* If so, check system call restarting.. */
+
+ switch (regs->areg[2]) {
+ case -ERESTARTNOHAND:
+ case -ERESTART_RESTARTBLOCK:
+ regs->areg[2] = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka.sa.sa_flags & SA_RESTART)) {
+ regs->areg[2] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ regs->areg[2] = regs->syscall;
+ regs->pc -= 3;
+ break;
+
+ default:
+ /* nothing to do */
+ if (regs->areg[2] != 0)
+ break;
+ }
+ }
+
+ /* Whee! Actually deliver the signal. */
+ /* Set up the stack frame */
+ ret = setup_frame(signr, &ka, &info, oldset, regs);
+ if (ret)
+ return ret;
+
+ block_sigmask(&ka, signr);
+ if (current->ptrace & PT_SINGLESTEP)
+ task_pt_regs(current)->icountlevel = 1;
+
+ return 1;
+ }
+
+no_signal:
+ /* Did we come from a system call? */
+ if ((signed) regs->syscall >= 0) {
+ /* Restart the system call - no handlers present */
+ switch (regs->areg[2]) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs->areg[2] = regs->syscall;
+ regs->pc -= 3;
+ break;
+ case -ERESTART_RESTARTBLOCK:
+ regs->areg[2] = __NR_restart_syscall;
+ regs->pc -= 3;
+ break;
+ }
+ }
+ if (current->ptrace & PT_SINGLESTEP)
+ task_pt_regs(current)->icountlevel = 1;
+ return 0;
+}
+
diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c
new file mode 100644
index 00000000..816e6d0d
--- /dev/null
+++ b/arch/xtensa/kernel/syscall.c
@@ -0,0 +1,57 @@
+/*
+ * arch/xtensa/kernel/syscall.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Copyright (C) 1995 - 2000 by Ralf Baechle
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Chris Zankel <chris@zankel.net>
+ * Kevin Chea
+ *
+ */
+#include <asm/uaccess.h>
+#include <asm/syscall.h>
+#include <asm/unistd.h>
+#include <linux/linkage.h>
+#include <linux/stringify.h>
+#include <linux/errno.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mman.h>
+#include <linux/shm.h>
+
+typedef void (*syscall_t)(void);
+
+syscall_t sys_call_table[__NR_syscall_count] /* FIXME __cacheline_aligned */= {
+ [0 ... __NR_syscall_count - 1] = (syscall_t)&sys_ni_syscall,
+
+#undef __SYSCALL
+#define __SYSCALL(nr,symbol,nargs) [ nr ] = (syscall_t)symbol,
+#undef _XTENSA_UNISTD_H
+#undef __KERNEL_SYSCALLS__
+#include <asm/unistd.h>
+};
+
+asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg)
+{
+ unsigned long ret;
+ long err;
+
+ err = do_shmat(shmid, shmaddr, shmflg, &ret);
+ if (err)
+ return err;
+ return (long)ret;
+}
+
+asmlinkage long xtensa_fadvise64_64(int fd, int advice, unsigned long long offset, unsigned long long len)
+{
+ return sys_fadvise64_64(fd, offset, len, advice);
+}
+
diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c
new file mode 100644
index 00000000..ac62f9cf
--- /dev/null
+++ b/arch/xtensa/kernel/time.c
@@ -0,0 +1,117 @@
+/*
+ * arch/xtensa/kernel/time.c
+ *
+ * Timer and clock support.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/profile.h>
+#include <linux/delay.h>
+
+#include <asm/timex.h>
+#include <asm/platform.h>
+
+#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
+unsigned long ccount_per_jiffy; /* per 1/HZ */
+unsigned long nsec_per_ccount; /* nsec per ccount increment */
+#endif
+
+static cycle_t ccount_read(void)
+{
+ return (cycle_t)get_ccount();
+}
+
+static struct clocksource ccount_clocksource = {
+ .name = "ccount",
+ .rating = 200,
+ .read = ccount_read,
+ .mask = CLOCKSOURCE_MASK(32),
+};
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id);
+static struct irqaction timer_irqaction = {
+ .handler = timer_interrupt,
+ .flags = IRQF_DISABLED,
+ .name = "timer",
+};
+
+void __init time_init(void)
+{
+#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
+ printk("Calibrating CPU frequency ");
+ platform_calibrate_ccount();
+ printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ),
+ (int)(ccount_per_jiffy/(10000/HZ))%100);
+#endif
+ clocksource_register_hz(&ccount_clocksource, CCOUNT_PER_JIFFY * HZ);
+
+ /* Initialize the linux timer interrupt. */
+
+ setup_irq(LINUX_TIMER_INT, &timer_irqaction);
+ set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY);
+}
+
+/*
+ * The timer interrupt is called HZ times per second.
+ */
+
+irqreturn_t timer_interrupt (int irq, void *dev_id)
+{
+
+ unsigned long next;
+
+ next = get_linux_timer();
+
+again:
+ while ((signed long)(get_ccount() - next) > 0) {
+
+ profile_tick(CPU_PROFILING);
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(get_irq_regs()));
+#endif
+
+ xtime_update(1); /* Linux handler in kernel/time/timekeeping */
+
+ /* Note that writing CCOMPARE clears the interrupt. */
+
+ next += CCOUNT_PER_JIFFY;
+ set_linux_timer(next);
+ }
+
+ /* Allow platform to do something useful (Wdog). */
+
+ platform_heartbeat();
+
+ /* Make sure we didn't miss any tick... */
+
+ if ((signed long)(get_ccount() - next) > 0)
+ goto again;
+
+ return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_GENERIC_CALIBRATE_DELAY
+void __cpuinit calibrate_delay(void)
+{
+ loops_per_jiffy = CCOUNT_PER_JIFFY;
+ printk("Calibrating delay loop (skipped)... "
+ "%lu.%02lu BogoMIPS preset\n",
+ loops_per_jiffy/(1000000/HZ),
+ (loops_per_jiffy/(10000/HZ)) % 100);
+}
+#endif
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c
new file mode 100644
index 00000000..bc1e14cf
--- /dev/null
+++ b/arch/xtensa/kernel/traps.c
@@ -0,0 +1,527 @@
+/*
+ * arch/xtensa/kernel/traps.c
+ *
+ * Exception handling.
+ *
+ * Derived from code with the following copyrights:
+ * Copyright (C) 1994 - 1999 by Ralf Baechle
+ * Modified for R3000 by Paul M. Antoine, 1995, 1996
+ * Complete output from die() by Ulf Carlsson, 1998
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * Essentially rewritten for the Xtensa architecture port.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier<marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Kevin Chea
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include <linux/kallsyms.h>
+#include <linux/delay.h>
+#include <linux/hardirq.h>
+
+#include <asm/ptrace.h>
+#include <asm/timex.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+
+#ifdef CONFIG_KGDB
+extern int gdb_enter;
+extern int return_from_debug_flag;
+#endif
+
+/*
+ * Machine specific interrupt handlers
+ */
+
+extern void kernel_exception(void);
+extern void user_exception(void);
+
+extern void fast_syscall_kernel(void);
+extern void fast_syscall_user(void);
+extern void fast_alloca(void);
+extern void fast_unaligned(void);
+extern void fast_second_level_miss(void);
+extern void fast_store_prohibited(void);
+extern void fast_coprocessor(void);
+
+extern void do_illegal_instruction (struct pt_regs*);
+extern void do_interrupt (struct pt_regs*);
+extern void do_unaligned_user (struct pt_regs*);
+extern void do_multihit (struct pt_regs*, unsigned long);
+extern void do_page_fault (struct pt_regs*, unsigned long);
+extern void do_debug (struct pt_regs*);
+extern void system_call (struct pt_regs*);
+
+/*
+ * The vector table must be preceded by a save area (which
+ * implies it must be in RAM, unless one places RAM immediately
+ * before a ROM and puts the vector at the start of the ROM (!))
+ */
+
+#define KRNL 0x01
+#define USER 0x02
+
+#define COPROCESSOR(x) \
+{ EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor }
+
+typedef struct {
+ int cause;
+ int fast;
+ void* handler;
+} dispatch_init_table_t;
+
+static dispatch_init_table_t __initdata dispatch_init_table[] = {
+
+{ EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction},
+{ EXCCAUSE_SYSTEM_CALL, KRNL, fast_syscall_kernel },
+{ EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user },
+{ EXCCAUSE_SYSTEM_CALL, 0, system_call },
+/* EXCCAUSE_INSTRUCTION_FETCH unhandled */
+/* EXCCAUSE_LOAD_STORE_ERROR unhandled*/
+{ EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt },
+{ EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca },
+/* EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */
+/* EXCCAUSE_PRIVILEGED unhandled */
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+#ifdef CONFIG_UNALIGNED_USER
+{ EXCCAUSE_UNALIGNED, USER, fast_unaligned },
+#else
+{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
+#endif
+{ EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
+#endif
+#ifdef CONFIG_MMU
+{ EXCCAUSE_ITLB_MISS, 0, do_page_fault },
+{ EXCCAUSE_ITLB_MISS, USER|KRNL, fast_second_level_miss},
+{ EXCCAUSE_ITLB_MULTIHIT, 0, do_multihit },
+{ EXCCAUSE_ITLB_PRIVILEGE, 0, do_page_fault },
+/* EXCCAUSE_SIZE_RESTRICTION unhandled */
+{ EXCCAUSE_FETCH_CACHE_ATTRIBUTE, 0, do_page_fault },
+{ EXCCAUSE_DTLB_MISS, USER|KRNL, fast_second_level_miss},
+{ EXCCAUSE_DTLB_MISS, 0, do_page_fault },
+{ EXCCAUSE_DTLB_MULTIHIT, 0, do_multihit },
+{ EXCCAUSE_DTLB_PRIVILEGE, 0, do_page_fault },
+/* EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */
+{ EXCCAUSE_STORE_CACHE_ATTRIBUTE, USER|KRNL, fast_store_prohibited },
+{ EXCCAUSE_STORE_CACHE_ATTRIBUTE, 0, do_page_fault },
+{ EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0, do_page_fault },
+#endif /* CONFIG_MMU */
+/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */
+#if XTENSA_HAVE_COPROCESSOR(0)
+COPROCESSOR(0),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(1)
+COPROCESSOR(1),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(2)
+COPROCESSOR(2),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(3)
+COPROCESSOR(3),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(4)
+COPROCESSOR(4),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(5)
+COPROCESSOR(5),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(6)
+COPROCESSOR(6),
+#endif
+#if XTENSA_HAVE_COPROCESSOR(7)
+COPROCESSOR(7),
+#endif
+{ EXCCAUSE_MAPPED_DEBUG, 0, do_debug },
+{ -1, -1, 0 }
+
+};
+
+/* The exception table <exc_table> serves two functions:
+ * 1. it contains three dispatch tables (fast_user, fast_kernel, default-c)
+ * 2. it is a temporary memory buffer for the exception handlers.
+ */
+
+unsigned long exc_table[EXC_TABLE_SIZE/4];
+
+void die(const char*, struct pt_regs*, long);
+
+static inline void
+__die_if_kernel(const char *str, struct pt_regs *regs, long err)
+{
+ if (!user_mode(regs))
+ die(str, regs, err);
+}
+
+/*
+ * Unhandled Exceptions. Kill user task or panic if in kernel space.
+ */
+
+void do_unhandled(struct pt_regs *regs, unsigned long exccause)
+{
+ __die_if_kernel("Caught unhandled exception - should not happen",
+ regs, SIGKILL);
+
+ /* If in user mode, send SIGILL signal to current process */
+ printk("Caught unhandled exception in '%s' "
+ "(pid = %d, pc = %#010lx) - should not happen\n"
+ "\tEXCCAUSE is %ld\n",
+ current->comm, task_pid_nr(current), regs->pc, exccause);
+ force_sig(SIGILL, current);
+}
+
+/*
+ * Multi-hit exception. This if fatal!
+ */
+
+void do_multihit(struct pt_regs *regs, unsigned long exccause)
+{
+ die("Caught multihit exception", regs, SIGKILL);
+}
+
+/*
+ * Level-1 interrupt.
+ * We currently have no priority encoding.
+ */
+
+unsigned long ignored_level1_interrupts;
+extern void do_IRQ(int, struct pt_regs *);
+
+void do_interrupt (struct pt_regs *regs)
+{
+ unsigned long intread = get_sr (INTREAD);
+ unsigned long intenable = get_sr (INTENABLE);
+ int i, mask;
+
+ /* Handle all interrupts (no priorities).
+ * (Clear the interrupt before processing, in case it's
+ * edge-triggered or software-generated)
+ */
+
+ for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) {
+ if (mask & (intread & intenable)) {
+ set_sr (mask, INTCLEAR);
+ do_IRQ (i,regs);
+ }
+ }
+}
+
+/*
+ * Illegal instruction. Fatal if in kernel space.
+ */
+
+void
+do_illegal_instruction(struct pt_regs *regs)
+{
+ __die_if_kernel("Illegal instruction in kernel", regs, SIGKILL);
+
+ /* If in user mode, send SIGILL signal to current process. */
+
+ printk("Illegal Instruction in '%s' (pid = %d, pc = %#010lx)\n",
+ current->comm, task_pid_nr(current), regs->pc);
+ force_sig(SIGILL, current);
+}
+
+
+/*
+ * Handle unaligned memory accesses from user space. Kill task.
+ *
+ * If CONFIG_UNALIGNED_USER is not set, we don't allow unaligned memory
+ * accesses causes from user space.
+ */
+
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+#ifndef CONFIG_UNALIGNED_USER
+void
+do_unaligned_user (struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ __die_if_kernel("Unhandled unaligned exception in kernel",
+ regs, SIGKILL);
+
+ current->thread.bad_vaddr = regs->excvaddr;
+ current->thread.error_code = -3;
+ printk("Unaligned memory access to %08lx in '%s' "
+ "(pid = %d, pc = %#010lx)\n",
+ regs->excvaddr, current->comm, task_pid_nr(current), regs->pc);
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void *) regs->excvaddr;
+ force_sig_info(SIGSEGV, &info, current);
+
+}
+#endif
+#endif
+
+void
+do_debug(struct pt_regs *regs)
+{
+#ifdef CONFIG_KGDB
+ /* If remote debugging is configured AND enabled, we give control to
+ * kgdb. Otherwise, we fall through, perhaps giving control to the
+ * native debugger.
+ */
+
+ if (gdb_enter) {
+ extern void gdb_handle_exception(struct pt_regs *);
+ gdb_handle_exception(regs);
+ return_from_debug_flag = 1;
+ return;
+ }
+#endif
+
+ __die_if_kernel("Breakpoint in kernel", regs, SIGKILL);
+
+ /* If in user mode, send SIGTRAP signal to current process */
+
+ force_sig(SIGTRAP, current);
+}
+
+
+/*
+ * Initialize dispatch tables.
+ *
+ * The exception vectors are stored compressed the __init section in the
+ * dispatch_init_table. This function initializes the following three tables
+ * from that compressed table:
+ * - fast user first dispatch table for user exceptions
+ * - fast kernel first dispatch table for kernel exceptions
+ * - default C-handler C-handler called by the default fast handler.
+ *
+ * See vectors.S for more details.
+ */
+
+#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler))
+
+void __init trap_init(void)
+{
+ int i;
+
+ /* Setup default vectors. */
+
+ for(i = 0; i < 64; i++) {
+ set_handler(EXC_TABLE_FAST_USER/4 + i, user_exception);
+ set_handler(EXC_TABLE_FAST_KERNEL/4 + i, kernel_exception);
+ set_handler(EXC_TABLE_DEFAULT/4 + i, do_unhandled);
+ }
+
+ /* Setup specific handlers. */
+
+ for(i = 0; dispatch_init_table[i].cause >= 0; i++) {
+
+ int fast = dispatch_init_table[i].fast;
+ int cause = dispatch_init_table[i].cause;
+ void *handler = dispatch_init_table[i].handler;
+
+ if (fast == 0)
+ set_handler (EXC_TABLE_DEFAULT/4 + cause, handler);
+ if (fast && fast & USER)
+ set_handler (EXC_TABLE_FAST_USER/4 + cause, handler);
+ if (fast && fast & KRNL)
+ set_handler (EXC_TABLE_FAST_KERNEL/4 + cause, handler);
+ }
+
+ /* Initialize EXCSAVE_1 to hold the address of the exception table. */
+
+ i = (unsigned long)exc_table;
+ __asm__ __volatile__("wsr %0, "__stringify(EXCSAVE_1)"\n" : : "a" (i));
+}
+
+/*
+ * This function dumps the current valid window frame and other base registers.
+ */
+
+void show_regs(struct pt_regs * regs)
+{
+ int i, wmask;
+
+ wmask = regs->wmask & ~1;
+
+ for (i = 0; i < 16; i++) {
+ if ((i % 8) == 0)
+ printk(KERN_INFO "a%02d:", i);
+ printk(KERN_CONT " %08lx", regs->areg[i]);
+ }
+ printk(KERN_CONT "\n");
+
+ printk("pc: %08lx, ps: %08lx, depc: %08lx, excvaddr: %08lx\n",
+ regs->pc, regs->ps, regs->depc, regs->excvaddr);
+ printk("lbeg: %08lx, lend: %08lx lcount: %08lx, sar: %08lx\n",
+ regs->lbeg, regs->lend, regs->lcount, regs->sar);
+ if (user_mode(regs))
+ printk("wb: %08lx, ws: %08lx, wmask: %08lx, syscall: %ld\n",
+ regs->windowbase, regs->windowstart, regs->wmask,
+ regs->syscall);
+}
+
+static __always_inline unsigned long *stack_pointer(struct task_struct *task)
+{
+ unsigned long *sp;
+
+ if (!task || task == current)
+ __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
+ else
+ sp = (unsigned long *)task->thread.sp;
+
+ return sp;
+}
+
+static inline void spill_registers(void)
+{
+ unsigned int a0, ps;
+
+ __asm__ __volatile__ (
+ "movi a14," __stringify (PS_EXCM_BIT) " | 1\n\t"
+ "mov a12, a0\n\t"
+ "rsr a13," __stringify(SAR) "\n\t"
+ "xsr a14," __stringify(PS) "\n\t"
+ "movi a0, _spill_registers\n\t"
+ "rsync\n\t"
+ "callx0 a0\n\t"
+ "mov a0, a12\n\t"
+ "wsr a13," __stringify(SAR) "\n\t"
+ "wsr a14," __stringify(PS) "\n\t"
+ :: "a" (&a0), "a" (&ps)
+ : "a2", "a3", "a4", "a7", "a11", "a12", "a13", "a14", "a15", "memory");
+}
+
+void show_trace(struct task_struct *task, unsigned long *sp)
+{
+ unsigned long a0, a1, pc;
+ unsigned long sp_start, sp_end;
+
+ if (sp)
+ a1 = (unsigned long)sp;
+ else
+ a1 = (unsigned long)stack_pointer(task);
+
+ sp_start = a1 & ~(THREAD_SIZE-1);
+ sp_end = sp_start + THREAD_SIZE;
+
+ printk("Call Trace:");
+#ifdef CONFIG_KALLSYMS
+ printk("\n");
+#endif
+ spill_registers();
+
+ while (a1 > sp_start && a1 < sp_end) {
+ sp = (unsigned long*)a1;
+
+ a0 = *(sp - 4);
+ a1 = *(sp - 3);
+
+ if (a1 <= (unsigned long) sp)
+ break;
+
+ pc = MAKE_PC_FROM_RA(a0, a1);
+
+ if (kernel_text_address(pc)) {
+ printk(" [<%08lx>] ", pc);
+ print_symbol("%s\n", pc);
+ }
+ }
+ printk("\n");
+}
+
+/*
+ * This routine abuses get_user()/put_user() to reference pointers
+ * with at least a bit of error checking ...
+ */
+
+static int kstack_depth_to_print = 24;
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+ int i = 0;
+ unsigned long *stack;
+
+ if (!sp)
+ sp = stack_pointer(task);
+ stack = sp;
+
+ printk("\nStack: ");
+
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (kstack_end(sp))
+ break;
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("%08lx ", *sp++);
+ }
+ printk("\n");
+ show_trace(task, stack);
+}
+
+void dump_stack(void)
+{
+ show_stack(current, NULL);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+
+void show_code(unsigned int *pc)
+{
+ long i;
+
+ printk("\nCode:");
+
+ for(i = -3 ; i < 6 ; i++) {
+ unsigned long insn;
+ if (__get_user(insn, pc + i)) {
+ printk(" (Bad address in pc)\n");
+ break;
+ }
+ printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
+ }
+}
+
+DEFINE_SPINLOCK(die_lock);
+
+void die(const char * str, struct pt_regs * regs, long err)
+{
+ static int die_counter;
+ int nl = 0;
+
+ console_verbose();
+ spin_lock_irq(&die_lock);
+
+ printk("%s: sig: %ld [#%d]\n", str, err, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+ nl = 1;
+#endif
+ if (nl)
+ printk("\n");
+ show_regs(regs);
+ if (!user_mode(regs))
+ show_stack(NULL, (unsigned long*)regs->areg[1]);
+
+ add_taint(TAINT_DIE);
+ spin_unlock_irq(&die_lock);
+
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+
+ if (panic_on_oops)
+ panic("Fatal exception");
+
+ do_exit(err);
+}
+
+
diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S
new file mode 100644
index 00000000..70066e35
--- /dev/null
+++ b/arch/xtensa/kernel/vectors.S
@@ -0,0 +1,468 @@
+/*
+ * arch/xtensa/kernel/vectors.S
+ *
+ * This file contains all exception vectors (user, kernel, and double),
+ * as well as the window vectors (overflow and underflow), and the debug
+ * vector. These are the primary vectors executed by the processor if an
+ * exception occurs.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of
+ * this archive for more details.
+ *
+ * Copyright (C) 2005 Tensilica, Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+/*
+ * We use a two-level table approach. The user and kernel exception vectors
+ * use a first-level dispatch table to dispatch the exception to a registered
+ * fast handler or the default handler, if no fast handler was registered.
+ * The default handler sets up a C-stack and dispatches the exception to a
+ * registerd C handler in the second-level dispatch table.
+ *
+ * Fast handler entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original value in depc
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * The value for PT_DEPC saved to stack also functions as a boolean to
+ * indicate that the exception is either a double or a regular exception:
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Note: Neither the kernel nor the user exception handler generate literals.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/asm-offsets.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+
+#define WINDOW_VECTORS_SIZE 0x180
+
+
+/*
+ * User exception vector. (Exceptions with PS.UM == 1, PS.EXCM == 0)
+ *
+ * We get here when an exception occurred while we were in userland.
+ * We switch to the kernel stack and jump to the first level handler
+ * associated to the exception cause.
+ *
+ * Note: the saved kernel stack pointer (EXC_TABLE_KSTK) is already
+ * decremented by PT_USER_SIZE.
+ */
+
+ .section .UserExceptionVector.text, "ax"
+
+ENTRY(_UserExceptionVector)
+
+ xsr a3, EXCSAVE_1 # save a3 and get dispatch table
+ wsr a2, DEPC # save a2
+ l32i a2, a3, EXC_TABLE_KSTK # load kernel stack to a2
+ s32i a0, a2, PT_AREG0 # save a0 to ESF
+ rsr a0, EXCCAUSE # retrieve exception cause
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_USER # load handler
+ jx a0
+
+/*
+ * Kernel exception vector. (Exceptions with PS.UM == 0, PS.EXCM == 0)
+ *
+ * We get this exception when we were already in kernel space.
+ * We decrement the current stack pointer (kernel) by PT_SIZE and
+ * jump to the first-level handler associated with the exception cause.
+ *
+ * Note: we need to preserve space for the spill region.
+ */
+
+ .section .KernelExceptionVector.text, "ax"
+
+ENTRY(_KernelExceptionVector)
+
+ xsr a3, EXCSAVE_1 # save a3, and get dispatch table
+ wsr a2, DEPC # save a2
+ addi a2, a1, -16-PT_SIZE # adjust stack pointer
+ s32i a0, a2, PT_AREG0 # save a0 to ESF
+ rsr a0, EXCCAUSE # retrieve exception cause
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler address
+ jx a0
+
+
+/*
+ * Double exception vector (Exceptions with PS.EXCM == 1)
+ * We get this exception when another exception occurs while were are
+ * already in an exception, such as window overflow/underflow exception,
+ * or 'expected' exceptions, for example memory exception when we were trying
+ * to read data from an invalid address in user space.
+ *
+ * Note that this vector is never invoked for level-1 interrupts, because such
+ * interrupts are disabled (masked) when PS.EXCM is set.
+ *
+ * We decode the exception and take the appropriate action. However, the
+ * double exception vector is much more careful, because a lot more error
+ * cases go through the double exception vector than through the user and
+ * kernel exception vectors.
+ *
+ * Occasionally, the kernel expects a double exception to occur. This usually
+ * happens when accessing user-space memory with the user's permissions
+ * (l32e/s32e instructions). The kernel state, though, is not always suitable
+ * for immediate transfer of control to handle_double, where "normal" exception
+ * processing occurs. Also in kernel mode, TLB misses can occur if accessing
+ * vmalloc memory, possibly requiring repair in a double exception handler.
+ *
+ * The variable at TABLE_FIXUP offset from the pointer in EXCSAVE_1 doubles as
+ * a boolean variable and a pointer to a fixup routine. If the variable
+ * EXC_TABLE_FIXUP is non-zero, this handler jumps to that address. A value of
+ * zero indicates to use the default kernel/user exception handler.
+ * There is only one exception, when the value is identical to the exc_table
+ * label, the kernel is in trouble. This mechanism is used to protect critical
+ * sections, mainly when the handler writes to the stack to assert the stack
+ * pointer is valid. Once the fixup/default handler leaves that area, the
+ * EXC_TABLE_FIXUP variable is reset to the fixup handler or zero.
+ *
+ * Procedures wishing to use this mechanism should set EXC_TABLE_FIXUP to the
+ * nonzero address of a fixup routine before it could cause a double exception
+ * and reset it before it returns.
+ *
+ * Some other things to take care of when a fast exception handler doesn't
+ * specify a particular fixup handler but wants to use the default handlers:
+ *
+ * - The original stack pointer (in a1) must not be modified. The fast
+ * exception handler should only use a2 as the stack pointer.
+ *
+ * - If the fast handler manipulates the stack pointer (in a2), it has to
+ * register a valid fixup handler and cannot use the default handlers.
+ *
+ * - The handler can use any other generic register from a3 to a15, but it
+ * must save the content of these registers to stack (PT_AREG3...PT_AREGx)
+ *
+ * - These registers must be saved before a double exception can occur.
+ *
+ * - If we ever implement handling signals while in double exceptions, the
+ * number of registers a fast handler has saved (excluding a0 and a1) must
+ * be written to PT_AREG1. (1 if only a3 is used, 2 for a3 and a4, etc. )
+ *
+ * The fixup handlers are special handlers:
+ *
+ * - Fixup entry conditions differ from regular exceptions:
+ *
+ * a0: DEPC
+ * a1: a1
+ * a2: trashed, original value in EXC_TABLE_DOUBLE_A2
+ * a3: exctable
+ * depc: a0
+ * excsave_1: a3
+ *
+ * - When the kernel enters the fixup handler, it still assumes it is in a
+ * critical section, so EXC_TABLE_FIXUP variable is set to exc_table.
+ * The fixup handler, therefore, has to re-register itself as the fixup
+ * handler before it returns from the double exception.
+ *
+ * - Fixup handler can share the same exception frame with the fast handler.
+ * The kernel stack pointer is not changed when entering the fixup handler.
+ *
+ * - Fixup handlers can jump to the default kernel and user exception
+ * handlers. Before it jumps, though, it has to setup a exception frame
+ * on stack. Because the default handler resets the register fixup handler
+ * the fixup handler must make sure that the default handler returns to
+ * it instead of the exception address, so it can re-register itself as
+ * the fixup handler.
+ *
+ * In case of a critical condition where the kernel cannot recover, we jump
+ * to unrecoverable_exception with the following entry conditions.
+ * All registers a0...a15 are unchanged from the last exception, except:
+ *
+ * a0: last address before we jumped to the unrecoverable_exception.
+ * excsave_1: a0
+ *
+ *
+ * See the handle_alloca_user and spill_registers routines for example clients.
+ *
+ * FIXME: Note: we currently don't allow signal handling coming from a double
+ * exception, so the item markt with (*) is not required.
+ */
+
+ .section .DoubleExceptionVector.text, "ax"
+ .begin literal_prefix .DoubleExceptionVector
+
+ENTRY(_DoubleExceptionVector)
+
+ /* Deliberately destroy excsave (don't assume it's value was valid). */
+
+ wsr a3, EXCSAVE_1 # save a3
+
+ /* Check for kernel double exception (usually fatal). */
+
+ rsr a3, PS
+ _bbci.l a3, PS_UM_BIT, .Lksp
+
+ /* Check if we are currently handling a window exception. */
+ /* Note: We don't need to indicate that we enter a critical section. */
+
+ xsr a0, DEPC # get DEPC, save a0
+
+ movi a3, XCHAL_WINDOW_VECTORS_VADDR
+ _bltu a0, a3, .Lfixup
+ addi a3, a3, WINDOW_VECTORS_SIZE
+ _bgeu a0, a3, .Lfixup
+
+ /* Window overflow/underflow exception. Get stack pointer. */
+
+ mov a3, a2
+ movi a2, exc_table
+ l32i a2, a2, EXC_TABLE_KSTK
+
+ /* Check for overflow/underflow exception, jump if overflow. */
+
+ _bbci.l a0, 6, .Lovfl
+
+ /* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */
+
+ /* Restart window underflow exception.
+ * We return to the instruction in user space that caused the window
+ * underflow exception. Therefore, we change window base to the value
+ * before we entered the window underflow exception and prepare the
+ * registers to return as if we were coming from a regular exception
+ * by changing depc (in a0).
+ * Note: We can trash the current window frame (a0...a3) and depc!
+ */
+
+ wsr a2, DEPC # save stack pointer temporarily
+ rsr a0, PS
+ extui a0, a0, PS_OWB_SHIFT, 4
+ wsr a0, WINDOWBASE
+ rsync
+
+ /* We are now in the previous window frame. Save registers again. */
+
+ xsr a2, DEPC # save a2 and get stack pointer
+ s32i a0, a2, PT_AREG0
+
+ wsr a3, EXCSAVE_1 # save a3
+ movi a3, exc_table
+
+ rsr a0, EXCCAUSE
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ addx4 a0, a0, a3
+ l32i a0, a0, EXC_TABLE_FAST_USER
+ jx a0
+
+.Lfixup:/* Check for a fixup handler or if we were in a critical section. */
+
+ /* a0: depc, a1: a1, a2: a2, a3: trashed, depc: a0, excsave1: a3 */
+
+ movi a3, exc_table
+ s32i a2, a3, EXC_TABLE_DOUBLE_SAVE # temporary variable
+
+ /* Enter critical section. */
+
+ l32i a2, a3, EXC_TABLE_FIXUP
+ s32i a3, a3, EXC_TABLE_FIXUP
+ beq a2, a3, .Lunrecoverable_fixup # critical!
+ beqz a2, .Ldflt # no handler was registered
+
+ /* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave: a3 */
+
+ jx a2
+
+.Ldflt: /* Get stack pointer. */
+
+ l32i a3, a3, EXC_TABLE_DOUBLE_SAVE
+ addi a2, a3, -PT_USER_SIZE
+
+.Lovfl: /* Jump to default handlers. */
+
+ /* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */
+
+ xsr a3, DEPC
+ s32i a0, a2, PT_DEPC
+ s32i a3, a2, PT_AREG0
+
+ /* a0: avail, a1: a1, a2: kstk, a3: avail, depc: a2, excsave: a3 */
+
+ movi a3, exc_table
+ rsr a0, EXCCAUSE
+ addx4 a0, a0, a3
+ l32i a0, a0, EXC_TABLE_FAST_USER
+ jx a0
+
+ /*
+ * We only allow the ITLB miss exception if we are in kernel space.
+ * All other exceptions are unexpected and thus unrecoverable!
+ */
+
+#ifdef CONFIG_MMU
+ .extern fast_second_level_miss_double_kernel
+
+.Lksp: /* a0: a0, a1: a1, a2: a2, a3: trashed, depc: depc, excsave: a3 */
+
+ rsr a3, EXCCAUSE
+ beqi a3, EXCCAUSE_ITLB_MISS, 1f
+ addi a3, a3, -EXCCAUSE_DTLB_MISS
+ bnez a3, .Lunrecoverable
+1: movi a3, fast_second_level_miss_double_kernel
+ jx a3
+#else
+.equ .Lksp, .Lunrecoverable
+#endif
+
+ /* Critical! We can't handle this situation. PANIC! */
+
+ .extern unrecoverable_exception
+
+.Lunrecoverable_fixup:
+ l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
+ xsr a0, DEPC
+
+.Lunrecoverable:
+ rsr a3, EXCSAVE_1
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+ .end literal_prefix
+
+
+/*
+ * Debug interrupt vector
+ *
+ * There is not much space here, so simply jump to another handler.
+ * EXCSAVE[DEBUGLEVEL] has been set to that handler.
+ */
+
+ .section .DebugInterruptVector.text, "ax"
+
+ENTRY(_DebugInterruptVector)
+ xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL
+ jx a0
+
+
+
+/* Window overflow and underflow handlers.
+ * The handlers must be 64 bytes apart, first starting with the underflow
+ * handlers underflow-4 to underflow-12, then the overflow handlers
+ * overflow-4 to overflow-12.
+ *
+ * Note: We rerun the underflow handlers if we hit an exception, so
+ * we try to access any page that would cause a page fault early.
+ */
+
+ .section .WindowVectors.text, "ax"
+
+
+/* 4-Register Window Overflow Vector (Handler) */
+
+ .align 64
+.global _WindowOverflow4
+_WindowOverflow4:
+ s32e a0, a5, -16
+ s32e a1, a5, -12
+ s32e a2, a5, -8
+ s32e a3, a5, -4
+ rfwo
+
+
+/* 4-Register Window Underflow Vector (Handler) */
+
+ .align 64
+.global _WindowUnderflow4
+_WindowUnderflow4:
+ l32e a0, a5, -16
+ l32e a1, a5, -12
+ l32e a2, a5, -8
+ l32e a3, a5, -4
+ rfwu
+
+
+/* 8-Register Window Overflow Vector (Handler) */
+
+ .align 64
+.global _WindowOverflow8
+_WindowOverflow8:
+ s32e a0, a9, -16
+ l32e a0, a1, -12
+ s32e a2, a9, -8
+ s32e a1, a9, -12
+ s32e a3, a9, -4
+ s32e a4, a0, -32
+ s32e a5, a0, -28
+ s32e a6, a0, -24
+ s32e a7, a0, -20
+ rfwo
+
+/* 8-Register Window Underflow Vector (Handler) */
+
+ .align 64
+.global _WindowUnderflow8
+_WindowUnderflow8:
+ l32e a1, a9, -12
+ l32e a0, a9, -16
+ l32e a7, a1, -12
+ l32e a2, a9, -8
+ l32e a4, a7, -32
+ l32e a3, a9, -4
+ l32e a5, a7, -28
+ l32e a6, a7, -24
+ l32e a7, a7, -20
+ rfwu
+
+
+/* 12-Register Window Overflow Vector (Handler) */
+
+ .align 64
+.global _WindowOverflow12
+_WindowOverflow12:
+ s32e a0, a13, -16
+ l32e a0, a1, -12
+ s32e a1, a13, -12
+ s32e a2, a13, -8
+ s32e a3, a13, -4
+ s32e a4, a0, -48
+ s32e a5, a0, -44
+ s32e a6, a0, -40
+ s32e a7, a0, -36
+ s32e a8, a0, -32
+ s32e a9, a0, -28
+ s32e a10, a0, -24
+ s32e a11, a0, -20
+ rfwo
+
+/* 12-Register Window Underflow Vector (Handler) */
+
+ .align 64
+.global _WindowUnderflow12
+_WindowUnderflow12:
+ l32e a1, a13, -12
+ l32e a0, a13, -16
+ l32e a11, a1, -12
+ l32e a2, a13, -8
+ l32e a4, a11, -48
+ l32e a8, a11, -32
+ l32e a3, a13, -4
+ l32e a5, a11, -44
+ l32e a6, a11, -40
+ l32e a7, a11, -36
+ l32e a9, a11, -28
+ l32e a10, a11, -24
+ l32e a11, a11, -20
+ rfwu
+
+ .text
+
+
diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S
new file mode 100644
index 00000000..88ecea3f
--- /dev/null
+++ b/arch/xtensa/kernel/vmlinux.lds.S
@@ -0,0 +1,272 @@
+/*
+ * arch/xtensa/kernel/vmlinux.lds.S
+ *
+ * Xtensa linker script
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ */
+
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+
+#include <variant/core.h>
+#include <platform/hardware.h>
+OUTPUT_ARCH(xtensa)
+ENTRY(_start)
+
+#ifdef __XTENSA_EB__
+jiffies = jiffies_64 + 4;
+#else
+jiffies = jiffies_64;
+#endif
+
+#ifndef KERNELOFFSET
+#define KERNELOFFSET 0xd0001000
+#endif
+
+/* Note: In the following macros, it would be nice to specify only the
+ vector name and section kind and construct "sym" and "section" using
+ CPP concatenation, but that does not work reliably. Concatenating a
+ string with "." produces an invalid token. CPP will not print a
+ warning because it thinks this is an assembly file, but it leaves
+ them as multiple tokens and there may or may not be whitespace
+ between them. */
+
+/* Macro for a relocation entry */
+
+#define RELOCATE_ENTRY(sym, section) \
+ LONG(sym ## _start); \
+ LONG(sym ## _end); \
+ LONG(LOADADDR(section))
+
+/* Macro to define a section for a vector.
+ *
+ * Use of the MIN function catches the types of errors illustrated in
+ * the following example:
+ *
+ * Assume the section .DoubleExceptionVector.literal is completely
+ * full. Then a programmer adds code to .DoubleExceptionVector.text
+ * that produces another literal. The final literal position will
+ * overlay onto the first word of the adjacent code section
+ * .DoubleExceptionVector.text. (In practice, the literals will
+ * overwrite the code, and the first few instructions will be
+ * garbage.)
+ */
+
+#define SECTION_VECTOR(sym, section, addr, max_prevsec_size, prevsec) \
+ section addr : AT((MIN(LOADADDR(prevsec) + max_prevsec_size, \
+ LOADADDR(prevsec) + SIZEOF(prevsec)) + 3) & ~ 3) \
+ { \
+ . = ALIGN(4); \
+ sym ## _start = ABSOLUTE(.); \
+ *(section) \
+ sym ## _end = ABSOLUTE(.); \
+ }
+
+/*
+ * Mapping of input sections to output sections when linking.
+ */
+
+SECTIONS
+{
+ . = KERNELOFFSET;
+ /* .text section */
+
+ _text = .;
+ _stext = .;
+ _ftext = .;
+
+ .text :
+ {
+ /* The HEAD_TEXT section must be the first section! */
+ HEAD_TEXT
+ TEXT_TEXT
+ VMLINUX_SYMBOL(__sched_text_start) = .;
+ *(.sched.literal .sched.text)
+ VMLINUX_SYMBOL(__sched_text_end) = .;
+ VMLINUX_SYMBOL(__lock_text_start) = .;
+ *(.spinlock.literal .spinlock.text)
+ VMLINUX_SYMBOL(__lock_text_end) = .;
+
+ }
+ _etext = .;
+ PROVIDE (etext = .);
+
+ . = ALIGN(16);
+
+ RODATA
+
+ /* Relocation table */
+
+ .fixup : { *(.fixup) }
+
+ EXCEPTION_TABLE(16)
+ /* Data section */
+
+ _fdata = .;
+ RW_DATA_SECTION(XCHAL_ICACHE_LINESIZE, PAGE_SIZE, THREAD_SIZE)
+ _edata = .;
+
+ /* Initialization code and data: */
+
+ . = ALIGN(PAGE_SIZE);
+ __init_begin = .;
+ INIT_TEXT_SECTION(PAGE_SIZE)
+
+ .init.data :
+ {
+ INIT_DATA
+ . = ALIGN(0x4);
+ __tagtable_begin = .;
+ *(.taglist)
+ __tagtable_end = .;
+
+ . = ALIGN(16);
+ __boot_reloc_table_start = ABSOLUTE(.);
+
+ RELOCATE_ENTRY(_WindowVectors_text,
+ .WindowVectors.text);
+ RELOCATE_ENTRY(_KernelExceptionVector_text,
+ .KernelExceptionVector.text);
+ RELOCATE_ENTRY(_UserExceptionVector_text,
+ .UserExceptionVector.text);
+ RELOCATE_ENTRY(_DoubleExceptionVector_literal,
+ .DoubleExceptionVector.literal);
+ RELOCATE_ENTRY(_DoubleExceptionVector_text,
+ .DoubleExceptionVector.text);
+ RELOCATE_ENTRY(_DebugInterruptVector_text,
+ .DebugInterruptVector.text);
+
+ __boot_reloc_table_end = ABSOLUTE(.) ;
+
+ INIT_SETUP(XCHAL_ICACHE_LINESIZE)
+ INIT_CALLS
+ CON_INITCALL
+ SECURITY_INITCALL
+ INIT_RAM_FS
+ }
+
+ PERCPU_SECTION(XCHAL_ICACHE_LINESIZE)
+
+ /* We need this dummy segment here */
+
+ . = ALIGN(4);
+ .dummy : { LONG(0) }
+
+ /* The vectors are relocated to the real position at startup time */
+
+ SECTION_VECTOR (_WindowVectors_text,
+ .WindowVectors.text,
+ XCHAL_WINDOW_VECTORS_VADDR, 4,
+ .dummy)
+ SECTION_VECTOR (_DebugInterruptVector_literal,
+ .DebugInterruptVector.literal,
+ XCHAL_DEBUG_VECTOR_VADDR - 4,
+ SIZEOF(.WindowVectors.text),
+ .WindowVectors.text)
+ SECTION_VECTOR (_DebugInterruptVector_text,
+ .DebugInterruptVector.text,
+ XCHAL_DEBUG_VECTOR_VADDR,
+ 4,
+ .DebugInterruptVector.literal)
+ SECTION_VECTOR (_KernelExceptionVector_literal,
+ .KernelExceptionVector.literal,
+ XCHAL_KERNEL_VECTOR_VADDR - 4,
+ SIZEOF(.DebugInterruptVector.text),
+ .DebugInterruptVector.text)
+ SECTION_VECTOR (_KernelExceptionVector_text,
+ .KernelExceptionVector.text,
+ XCHAL_KERNEL_VECTOR_VADDR,
+ 4,
+ .KernelExceptionVector.literal)
+ SECTION_VECTOR (_UserExceptionVector_literal,
+ .UserExceptionVector.literal,
+ XCHAL_USER_VECTOR_VADDR - 4,
+ SIZEOF(.KernelExceptionVector.text),
+ .KernelExceptionVector.text)
+ SECTION_VECTOR (_UserExceptionVector_text,
+ .UserExceptionVector.text,
+ XCHAL_USER_VECTOR_VADDR,
+ 4,
+ .UserExceptionVector.literal)
+ SECTION_VECTOR (_DoubleExceptionVector_literal,
+ .DoubleExceptionVector.literal,
+ XCHAL_DOUBLEEXC_VECTOR_VADDR - 16,
+ SIZEOF(.UserExceptionVector.text),
+ .UserExceptionVector.text)
+ SECTION_VECTOR (_DoubleExceptionVector_text,
+ .DoubleExceptionVector.text,
+ XCHAL_DOUBLEEXC_VECTOR_VADDR,
+ 32,
+ .DoubleExceptionVector.literal)
+
+ . = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
+ . = ALIGN(PAGE_SIZE);
+
+ __init_end = .;
+
+ BSS_SECTION(0, 8192, 0)
+
+ _end = .;
+
+ /* only used by the boot loader */
+
+ . = ALIGN(0x10);
+ .bootstrap : { *(.bootstrap.literal .bootstrap.text .bootstrap.data) }
+
+ . = ALIGN(0x1000);
+ __initrd_start = .;
+ .initrd : { *(.initrd) }
+ __initrd_end = .;
+
+ .ResetVector.text XCHAL_RESET_VECTOR_VADDR :
+ {
+ *(.ResetVector.text)
+ }
+
+ .xt.lit : { *(.xt.lit) }
+ .xt.prop : { *(.xt.prop) }
+
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ .xt.insn 0 :
+ {
+ *(.xt.insn)
+ *(.gnu.linkonce.x*)
+ }
+
+ .xt.lit 0 :
+ {
+ *(.xt.lit)
+ *(.gnu.linkonce.p*)
+ }
+
+ /* Sections to be discarded */
+ DISCARDS
+ /DISCARD/ : { *(.exit.literal) }
+}
diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c
new file mode 100644
index 00000000..c9a7c5b7
--- /dev/null
+++ b/arch/xtensa/kernel/xtensa_ksyms.c
@@ -0,0 +1,97 @@
+/*
+ * arch/xtensa/kernel/xtensa_ksyms.c
+ *
+ * Export Xtensa-specific functions for loadable modules.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com>
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/in6.h>
+
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#ifdef CONFIG_BLK_DEV_FD
+#include <asm/floppy.h>
+#endif
+#ifdef CONFIG_NET
+#include <net/checksum.h>
+#endif /* CONFIG_NET */
+
+
+/*
+ * String functions
+ */
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memmove);
+
+EXPORT_SYMBOL(kernel_thread);
+
+/*
+ * gcc internal math functions
+ */
+extern long long __ashrdi3(long long, int);
+extern long long __ashldi3(long long, int);
+extern long long __lshrdi3(long long, int);
+extern int __divsi3(int, int);
+extern int __modsi3(int, int);
+extern long long __muldi3(long long, long long);
+extern int __mulsi3(int, int);
+extern unsigned int __udivsi3(unsigned int, unsigned int);
+extern unsigned int __umodsi3(unsigned int, unsigned int);
+extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
+extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
+
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__divsi3);
+EXPORT_SYMBOL(__modsi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__mulsi3);
+EXPORT_SYMBOL(__udivsi3);
+EXPORT_SYMBOL(__umodsi3);
+EXPORT_SYMBOL(__udivdi3);
+EXPORT_SYMBOL(__umoddi3);
+
+#ifdef CONFIG_NET
+/*
+ * Networking support
+ */
+EXPORT_SYMBOL(csum_partial_copy_generic);
+#endif /* CONFIG_NET */
+
+/*
+ * Architecture-specific symbols
+ */
+EXPORT_SYMBOL(__xtensa_copy_user);
+
+/*
+ * Kernel hacking ...
+ */
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+// FIXME EXPORT_SYMBOL(screen_info);
+#endif
+
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);