]> err.no Git - linux-2.6/commitdiff
[PATCH] xtensa: Architecture support for Tensilica Xtensa Part 3
authorChris Zankel <czankel@tensilica.com>
Fri, 24 Jun 2005 05:01:16 +0000 (22:01 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 24 Jun 2005 07:05:21 +0000 (00:05 -0700)
The attached patches provides part 3 of an architecture implementation for the
Tensilica Xtensa CPU series.

Signed-off-by: Chris Zankel <chris@zankel.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
23 files changed:
arch/xtensa/kernel/Makefile [new file with mode: 0644]
arch/xtensa/kernel/align.S [new file with mode: 0644]
arch/xtensa/kernel/asm-offsets.c [new file with mode: 0644]
arch/xtensa/kernel/coprocessor.S [new file with mode: 0644]
arch/xtensa/kernel/entry.S [new file with mode: 0644]
arch/xtensa/kernel/head.S [new file with mode: 0644]
arch/xtensa/kernel/irq.c [new file with mode: 0644]
arch/xtensa/kernel/module.c [new file with mode: 0644]
arch/xtensa/kernel/pci-dma.c [new file with mode: 0644]
arch/xtensa/kernel/pci.c [new file with mode: 0644]
arch/xtensa/kernel/platform.c [new file with mode: 0644]
arch/xtensa/kernel/process.c [new file with mode: 0644]
arch/xtensa/kernel/ptrace.c [new file with mode: 0644]
arch/xtensa/kernel/semaphore.c [new file with mode: 0644]
arch/xtensa/kernel/setup.c [new file with mode: 0644]
arch/xtensa/kernel/signal.c [new file with mode: 0644]
arch/xtensa/kernel/syscalls.c [new file with mode: 0644]
arch/xtensa/kernel/syscalls.h [new file with mode: 0644]
arch/xtensa/kernel/time.c [new file with mode: 0644]
arch/xtensa/kernel/traps.c [new file with mode: 0644]
arch/xtensa/kernel/vectors.S [new file with mode: 0644]
arch/xtensa/kernel/vmlinux.lds.S [new file with mode: 0644]
arch/xtensa/kernel/xtensa_ksyms.c [new file with mode: 0644]

diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile
new file mode 100644 (file)
index 0000000..d573017
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# 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 semaphore.o  \
+        setup.o signal.o syscalls.o time.o traps.o vectors.o platform.o  \
+        pci-dma.o
+
+## windowspill.o
+
+obj-$(CONFIG_KGDB) += xtensa-stub.o
+obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
+
+
diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S
new file mode 100644 (file)
index 0000000..74b1e90
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * 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/ptrace.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/offsets.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/thread_info.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_NARROW
+       _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_NARROW
+       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_LOOP
+       rsr     a3, LEND                # check if we reached LEND
+       bne     a7, a3, 1f
+       rsr     a3, LCOUNT              # and LCOUNT != 0
+       beqz    a3, 1f
+       addi    a3, a3, -1              # decrement LCOUNT and set
+       rsr     a7, LBEG                # set PC to LBEGIN
+       wsr     a3, 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_NARROW
+       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_LOOP
+       rsr     a3, LEND                # check if we reached LEND
+       bne     a7, a3, 1f
+       rsr     a3, LCOUNT              # and LCOUNT != 0
+       beqz    a3, 1f
+       addi    a3, a3, -1              # decrement LCOUNT and set
+       rsr     a7, LBEG                # set PC to LBEGIN
+       wsr     a3, 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    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_SHIFT, 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 (file)
index 0000000..840cd9a
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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 <linux/types.h>
+#include <linux/sched.h>
+#include <linux/stddef.h>
+#include <linux/thread_info.h>
+#include <linux/ptrace.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+#define DEFINE(sym, val) asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+#define BLANK() asm volatile("\n->" : : )
+
+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_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]));
+       BLANK();
+
+       /* 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, thread_info));
+       DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct));
+       BLANK();
+
+       /* 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_CP_SAVE, offsetof (struct task_struct, thread.cp_save));
+       DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds));
+       BLANK();
+
+       /* 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));
+       BLANK();
+       DEFINE(PT_SINGLESTEP_BIT, PT_SINGLESTEP_BIT);
+       return 0;
+}
+
+
diff --git a/arch/xtensa/kernel/coprocessor.S b/arch/xtensa/kernel/coprocessor.S
new file mode 100644 (file)
index 0000000..356192a
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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 - 2005 Tensilica Inc.
+ *
+ * Marc Gauthier <marc@tensilica.com> <marc@alumni.uwaterloo.ca>
+ */
+
+/*
+ * This module contains a table that describes the layout of the various
+ * custom registers and states associated with each coprocessor, as well
+ * as those not associated with any coprocessor ("extra state").
+ * This table is included with core dumps and is available via the ptrace
+ * interface, allowing the layout of such register/state information to
+ * be modified in the kernel without affecting the debugger.  Each
+ * register or state is identified using a 32-bit "libdb target number"
+ * assigned when the Xtensa processor is generated.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/processor.h>
+
+#if XCHAL_HAVE_CP
+
+#define CP_LAST ((XCHAL_CP_MAX - 1) * COPROCESSOR_INFO_SIZE)
+
+ENTRY(release_coprocessors)
+
+       entry   a1, 16
+                                               # a2: task
+       movi    a3, 1 << XCHAL_CP_MAX           # a3: coprocessor-bit
+       movi    a4, coprocessor_info+CP_LAST    # a4: owner-table
+                                               # a5: tmp
+       movi    a6, 0                           # a6: 0
+       rsil    a7, LOCKLEVEL                   # a7: PS
+
+1:     /* Check if task is coprocessor owner of coprocessor[i]. */
+
+       l32i    a5, a4, COPROCESSOR_INFO_OWNER
+       srli    a3, a3, 1
+       beqz    a3, 1f
+       addi    a4, a4, -8
+       beq     a2, a5, 1b
+
+       /* Found an entry: Clear entry CPENABLE bit to disable CP. */
+
+       rsr     a5, CPENABLE
+       s32i    a6, a4, COPROCESSOR_INFO_OWNER
+       xor     a5, a3, a5
+       wsr     a5, CPENABLE
+
+       bnez    a3, 1b
+
+1:     wsr     a7, PS
+       rsync
+       retw
+
+
+ENTRY(disable_coprocessor)
+       entry   sp, 16
+       rsil    a7, LOCKLEVEL
+       rsr     a3, CPENABLE
+       movi    a4, 1
+       ssl     a2
+       sll     a4, a4
+       and     a4, a3, a4
+       xor     a3, a3, a4
+       wsr     a3, CPENABLE
+       wsr     a7, PS
+       rsync
+       retw
+
+ENTRY(enable_coprocessor)
+       entry   sp, 16
+       rsil    a7, LOCKLEVEL
+       rsr     a3, CPENABLE
+       movi    a4, 1
+       ssl     a2
+       sll     a4, a4
+       or      a3, a3, a4
+       wsr     a3, CPENABLE
+       wsr     a7, PS
+       rsync
+       retw
+
+#endif
+
+ENTRY(save_coprocessor_extra)
+       entry   sp, 16
+       xchal_extra_store_funcbody
+       retw
+
+ENTRY(restore_coprocessor_extra)
+       entry   sp, 16
+       xchal_extra_load_funcbody
+       retw
+
+ENTRY(save_coprocessor_registers)
+       entry   sp, 16
+       xchal_cpi_store_funcbody
+       retw
+
+ENTRY(restore_coprocessor_registers)
+       entry   sp, 16
+       xchal_cpi_load_funcbody
+       retw
+
+
+/*
+ *  The Xtensa compile-time HAL (core.h) XCHAL_*_SA_CONTENTS_LIBDB macros
+ *  describe the contents of coprocessor & extra save areas in terms of
+ *  undefined CONTENTS_LIBDB_{SREG,UREG,REGF} macros.  We define these
+ *  latter macros here; they expand into a table of the format we want.
+ *  The general format is:
+ *
+ *     CONTENTS_LIBDB_SREG(libdbnum, offset, size, align, rsv1, name, sregnum,
+ *                         bitmask, rsv2, rsv3)
+ *     CONTENTS_LIBDB_UREG(libdbnum, offset, size, align, rsv1, name, uregnum,
+ *                         bitmask, rsv2, rsv3)
+ *     CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index,
+ *                         numentries, contentsize, regname_base,
+ *                         regfile_name, rsv2, rsv3)
+ *
+ *  For this table, we only care about the <libdbnum>, <offset> and <size>
+ *  fields.
+ */
+
+/*  Map all XCHAL CONTENTS macros to the reg_entry asm macro defined below:  */
+
+#define CONTENTS_LIBDB_SREG(libdbnum,offset,size,align,rsv1,name,sregnum,     \
+                           bitmask, rsv2, rsv3)                              \
+               reg_entry libdbnum, offset, size ;
+#define CONTENTS_LIBDB_UREG(libdbnum,offset,size,align,rsv1,name,uregnum,     \
+                           bitmask, rsv2, rsv3)                              \
+               reg_entry libdbnum, offset, size ;
+#define CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index, \
+                           numentries, contentsize, regname_base,            \
+                           regfile_name, rsv2, rsv3)                         \
+               reg_entry libdbnum, offset, size ;
+
+/* A single table entry: */
+       .macro  reg_entry       libdbnum, offset, size
+        .ifne  (__last_offset-(__last_group_offset+\offset))
+         /* padding entry */
+         .word (0xFC000000+__last_offset-(__last_group_offset+\offset))
+        .endif
+        .word  \libdbnum                               /* actual entry */
+        .set   __last_offset, __last_group_offset+\offset+\size
+       .endm   /* reg_entry */
+
+
+/* Table entry that marks the beginning of a group (coprocessor or "extra"): */
+       .macro  reg_group       cpnum, num_entries, align
+        .set   __last_group_offset, (__last_offset + \align- 1) & -\align
+        .ifne  \num_entries
+         .word 0xFD000000+(\cpnum<<16)+\num_entries
+        .endif
+       .endm   /* reg_group */
+
+/*
+ * Register info tables.
+ */
+
+       .section .rodata, "a"
+       .globl  _xtensa_reginfo_tables
+       .globl  _xtensa_reginfo_table_size
+       .align  4
+_xtensa_reginfo_table_size:
+       .word   _xtensa_reginfo_table_end - _xtensa_reginfo_tables
+
+_xtensa_reginfo_tables:
+       .set    __last_offset, 0
+       reg_group 0xFF, XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM, XCHAL_EXTRA_SA_ALIGN
+       XCHAL_EXTRA_SA_CONTENTS_LIBDB
+       reg_group 0, XCHAL_CP0_SA_CONTENTS_LIBDB_NUM, XCHAL_CP0_SA_ALIGN
+       XCHAL_CP0_SA_CONTENTS_LIBDB
+       reg_group 1, XCHAL_CP1_SA_CONTENTS_LIBDB_NUM, XCHAL_CP1_SA_ALIGN
+       XCHAL_CP1_SA_CONTENTS_LIBDB
+       reg_group 2, XCHAL_CP2_SA_CONTENTS_LIBDB_NUM, XCHAL_CP2_SA_ALIGN
+       XCHAL_CP2_SA_CONTENTS_LIBDB
+       reg_group 3, XCHAL_CP3_SA_CONTENTS_LIBDB_NUM, XCHAL_CP3_SA_ALIGN
+       XCHAL_CP3_SA_CONTENTS_LIBDB
+       reg_group 4, XCHAL_CP4_SA_CONTENTS_LIBDB_NUM, XCHAL_CP4_SA_ALIGN
+       XCHAL_CP4_SA_CONTENTS_LIBDB
+       reg_group 5, XCHAL_CP5_SA_CONTENTS_LIBDB_NUM, XCHAL_CP5_SA_ALIGN
+       XCHAL_CP5_SA_CONTENTS_LIBDB
+       reg_group 6, XCHAL_CP6_SA_CONTENTS_LIBDB_NUM, XCHAL_CP6_SA_ALIGN
+       XCHAL_CP6_SA_CONTENTS_LIBDB
+       reg_group 7, XCHAL_CP7_SA_CONTENTS_LIBDB_NUM, XCHAL_CP7_SA_ALIGN
+       XCHAL_CP7_SA_CONTENTS_LIBDB
+       .word   0xFC000000      /* invalid register number,marks end of table*/
+_xtensa_reginfo_table_end:
+
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
new file mode 100644 (file)
index 0000000..c64a01f
--- /dev/null
@@ -0,0 +1,1996 @@
+/*
+ * 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-2005 by Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/offsets.h>
+#include <asm/processor.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 <xtensa/coreasm.h>
+
+/* Unimplemented features. */
+
+#undef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+#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 adress. 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
+       wsr     a2, ICOUNTLEVEL
+       s32i    a3, a1, PT_SAR
+
+       /* 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 regiser frames starting from the top fiel 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:
+#if XCHAL_EXTRA_SA_SIZE
+
+       /* For user exceptions, save the extra state into the user's TCB.
+        * Note: We must assume that xchal_extra_store_funcbody destroys a2..a15
+        */
+
+       GET_CURRENT(a2,a1)
+       addi    a2, a2, THREAD_CP_SAVE
+       xchal_extra_store_funcbody
+#endif
+
+       /* 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 adress. 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
+       wsr     a2, ICOUNTLEVEL
+       s32i    a3, a1, PT_SAR
+
+       /* 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 EXCVADDR, DEBUGCAUSE, and PC, and clear LCOUNT */
+
+       rsr     a2, DEBUGCAUSE
+       rsr     a3, EPC_1
+       s32i    a2, a1, PT_DEBUGCAUSE
+       s32i    a3, a1, PT_PC
+
+       rsr     a3, EXCVADDR
+       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, PS_WOE_MASK
+       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
+
+       /* 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
+       _bbsi.l a3, PS_UM_SHIFT, 2f
+       j       kernel_exception_exit
+
+       /* 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.
+        */
+
+2:     wsr     a3, PS          /* disable interrupts */
+
+       /* Check for signals (keep interrupts disabled while we read TI_FLAGS)
+        * Note: PS.INTLEVEL = 0, PS.EXCM = 1
+        */
+
+       GET_THREAD_INFO(a2,a1)
+       l32i    a4, a2, TI_FLAGS
+
+       /* Enable interrupts again.
+        * Note: When we get here, we certainly have handled any interrupts.
+        *       (Hint: There is only one user exception frame on stack)
+        */
+
+       movi    a3, PS_WOE_MASK
+
+       _bbsi.l a4, TIF_NEED_RESCHED, 3f
+       _bbci.l a4, TIF_SIGPENDING, 4f
+
+#ifndef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+       l32i    a4, a1, PT_DEPC
+       bgeui   a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f
+#endif
+
+       /* Reenable interrupts and call do_signal() */
+
+       wsr     a3, PS
+       movi    a4, do_signal   # int do_signal(struct pt_regs*, sigset_t*)
+       mov     a6, a1
+       movi    a7, 0
+       callx4  a4
+       j       1b
+
+3:     /* Reenable interrupts and reschedule */
+
+       wsr     a3, PS
+       movi    a4, schedule    # void schedule (void)
+       callx4  a4
+       j       1b
+
+       /* Restore the state of the task and return from the exception. */
+
+
+       /* If we are returning from a user exception, and the process
+        * to run next has PT_SINGLESTEP set, we want to setup
+        * ICOUNT and ICOUNTLEVEL to step one instruction.
+        * PT_SINGLESTEP is set by sys_ptrace (ptrace.c)
+        */
+
+4:     /* a2 holds GET_CURRENT(a2,a1)  */
+
+       l32i    a3, a2, TI_TASK
+       l32i    a3, a3, TASK_PTRACE
+       bbci.l  a3, PT_SINGLESTEP_BIT, 1f # jump if single-step flag is not set
+
+       movi    a3, -2                  # PT_SINGLESTEP flag is set,
+       movi    a4, 1                   # icountlevel of 1 means it won't
+       wsr     a3, ICOUNT              # start counting until after rfe
+       wsr     a4, ICOUNTLEVEL         # so setup icount & icountlevel.
+       isync
+
+1:
+
+#if XCHAL_EXTRA_SA_SIZE
+
+       /* For user exceptions, restore the extra state from the user's TCB. */
+
+       /* Note: a2 still contains GET_CURRENT(a2,a1) */
+       addi    a2, a2, THREAD_CP_SAVE
+       xchal_extra_load_funcbody
+
+       /* We must assume that xchal_extra_store_funcbody destroys
+        * registers a2..a15.  FIXME, this list can eventually be
+        * reduced once real register requirements of the macro are
+        * finalized. */
+
+#endif /* XCHAL_EXTRA_SA_SIZE */
+
+
+       /* 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:
+
+       /* Disable interrupts (a3 holds PT_PS) */
+
+       wsr     a3, PS
+
+#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:
+
+       _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
+
+       /* 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_SHIFT, 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_SHIFT
+       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_SHIFT, 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, PS_WOE_MASK | 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
+
+       addi    a0, a0, -__NR_sysxtensa
+       _beqz   a0, fast_syscall_sysxtensa
+
+       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
+
+       addi    a0, a0, -__NR_sysxtensa
+       _beqz   a0, fast_syscall_sysxtensa
+
+       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 (XTENSA_ATOMIC_SET, ptr, val, unused);
+ * int sysxtensa (XTENSA_ATOMIC_ADD, ptr, val, unused);
+ * int sysxtensa (XTENSA_ATOMIC_EXG_ADD, ptr, val, unused);
+ * int sysxtensa (XTENSA_ATOMIC_CMP_SWP, ptr, oldval, newval);
+ * a2                    a6              a3    a4      a5
+ *
+ * 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
+ *
+ * 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 preceeding 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_sysxtensa)
+
+       _beqz   a6, 1f
+       _blti   a6, SYSXTENSA_COUNT, 2f
+
+1:     j       user_exception
+
+2:     xsr     a3, EXCSAVE_1           # restore a3, excsave1
+       s32i    a7, a2, PT_AREG7
+
+       movi    a7, 4                   # sizeof(unsigned int)
+       verify_area a3, a7, a0, a2, .Leac
+
+       _beqi   a6, SYSXTENSA_ATOMIC_SET, .Lset
+       _beqi   a6, SYSXTENSA_ATOMIC_EXG_ADD, .Lexg
+       _beqi   a6, SYSXTENSA_ATOMIC_ADD, .Ladd
+
+       /* Fall through for SYSXTENSA_ATOMIC_CMP_SWP */
+
+.Lswp: /* Atomic compare and swap */
+
+TRY    l32i    a7, a3, 0               # read old value
+       bne     a7, a4, 1f              # same as old value? jump
+       s32i    a5, a3, 0               # different, modify value
+       movi    a7, 1                   # and return 1
+       j       .Lret
+
+1:     movi    a7, 0                   # same values: return 0
+       j       .Lret
+
+.Ladd: /* Atomic add */
+.Lexg: /* Atomic (exchange) add */
+
+TRY    l32i    a7, a3, 0
+       add     a4, a4, a7
+       s32i    a4, a3, 0
+       j       .Lret
+
+.Lset: /* Atomic set */
+
+TRY    l32i    a7, a3, 0               # read old value as return value
+       s32i    a4, a3, 0               # write new value
+
+.Lret: mov     a0, a2
+       mov     a2, a7
+       l32i    a7, a0, PT_AREG7
+       l32i    a3, a0, PT_AREG3
+       l32i    a0, a0, PT_AREG0
+       rfe
+
+CATCH
+.Leac: movi    a7, -EFAULT
+       j       .Lret
+
+
+
+/* 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.
+ * Note: We don't need to save a2 in depc (return value)
+ */
+
+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    a0, a2, PT_AREG4        # store SAR to PT_AREG4
+       s32i    a3, a2, PT_AREG3
+
+       /* The spill routine might clobber a7, a11, and a15. */
+
+       s32i    a7, a2, PT_AREG5
+       s32i    a11, a2, PT_AREG6
+       s32i    a15, a2, PT_AREG7
+
+       call0   _spill_registers        # destroys a3, DEPC, and SAR
+
+       /* Advance PC, restore registers and SAR, and return from exception. */
+
+       l32i    a3, a2, PT_AREG4
+       l32i    a0, a2, PT_AREG0
+       wsr     a3, SAR
+       l32i    a3, a2, PT_AREG3
+
+       /* Restore clobbered registers. */
+
+       l32i    a7, a2, PT_AREG5
+       l32i    a11, a2, PT_AREG6
+       l32i    a15, a2, PT_AREG7
+
+       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.
+        */
+
+#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+       /* Restore registers we precautiously saved.
+        * We have the value of the 'right' a3
+        */
+
+       l32i    a7, a2, PT_AREG5
+       l32i    a11, a2, PT_AREG6
+       l32i    a15, a2, PT_AREG7
+#endif
+
+       /* 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
+
+#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+       /* Save registers again that might be clobbered. */
+
+       s32i    a7, a2, PT_AREG5
+       s32i    a11, a2, PT_AREG6
+       s32i    a15, a2, PT_AREG7
+#endif
+
+       /* 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 DEPC, a3 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 a2 = yyxxxwww1.
+        */
+
+       wsr     a2, DEPC                # preserve a2
+       rsr     a2, WINDOWBASE
+       rsr     a3, WINDOWSTART
+       ssr     a2                      # holds WB
+       slli    a2, a3, WSBITS
+       or      a3, a3, a2              # a2 = xxxwww1yyxxxwww1yy
+       srl     a3, a3
+
+       /* We are done if there are no more than the current register frame. */
+
+       extui   a3, a3, 1, WSBITS-2     # a3 = 0yyxxxwww
+       movi    a2, (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, a2              # 1yyxxxwww
+
+       /* Skip empty frames - get 'oldest' WINDOWSTART-bit. */
+
+       wsr     a3, WINDOWSTART         # save shifted windowstart
+       neg     a2, a3
+       and     a3, a2, a3              # first bit set from right: 000010000
+
+       ffs_ws  a2, a3                  # a2: shifts to skip empty frames
+       movi    a3, WSBITS
+       sub     a2, a3, a2              # WSBITS-a2:number of 0-bits from right
+       ssr     a2                      # save in SAR for later.
+
+       rsr     a3, WINDOWBASE
+       add     a3, a3, a2
+       rsr     a2, DEPC                # restore a2
+       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
+
+.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
+
+.Lnospill:
+       jx      a0
+
+.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_SHIFT, 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, PS_WOE_MASK | 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
+
+/*
+ * 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
+
+8:     rsr     a1, EXCVADDR            # fault address
+       _PGD_OFFSET(a0, a1, a1)
+       l32i    a0, a0, 0               # read pmdval
+       //beqi  a0, _PAGE_USER, 2f
+       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_KERNEL
+        */
+
+       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
+
+       rsr     a1, PTEVADDR
+       srli    a1, a1, PAGE_SHIFT
+       slli    a1, a1, PAGE_SHIFT      # ptevaddr & PAGE_MASK
+       addi    a1, a1, DTLB_WAY_PGTABLE        # ... + way_number
+
+       wdtlb   a0, a1
+       dsync
+
+       /* Exit critical section. */
+
+       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
+
+2:     /* Invalid PGD, default exception handling */
+
+       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_SHIFT, 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
+       //beqi  a0, _PAGE_USER, 2f      # FIXME use _PAGE_INVALID
+       beqz    a0, 2f
+
+       _PTE_OFFSET(a0, a1, a4)
+       l32i    a4, a0, 0               # read pteval
+       movi    a1, _PAGE_VALID | _PAGE_RW
+       bnall   a4, a1, 2f
+
+       movi    a1, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_WRENABLE
+       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
+       beqz    a0, 1f
+       idtlb   a0              // FIXME do we need this?
+       wdtlb   a4, a0
+1:
+
+       /* 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_SHIFT, 1f
+       j       _kernel_exception
+1:     j       _user_exception
+
+
+#if XCHAL_EXTRA_SA_SIZE
+
+#warning fast_coprocessor untested
+
+/*
+ * 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)
+
+       /* Fatal if we are in a double exception. */
+
+       l32i    a0, a2, PT_DEPC
+       _bgeui  a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_coprocessor_double
+
+       /* Save some registers a1, a3, a4, SAR */
+
+       xsr     a3, EXCSAVE_1
+       s32i    a3, a2, PT_AREG3
+       rsr     a3, SAR
+       s32i    a4, a2, PT_AREG4
+       s32i    a1, a2, PT_AREG1
+       s32i    a5, a1, PT_AREG5
+       s32i    a3, a2, PT_SAR
+       mov     a1, a2
+
+       /* Currently, the HAL macros only guarantee saving a0 and a1.
+        * These can and will be refined in the future, but for now,
+        * just save the remaining registers of a2...a15.
+        */
+       s32i    a6, a1, PT_AREG6
+       s32i    a7, a1, PT_AREG7
+       s32i    a8, a1, PT_AREG8
+       s32i    a9, a1, PT_AREG9
+       s32i    a10, a1, PT_AREG10
+       s32i    a11, a1, PT_AREG11
+       s32i    a12, a1, PT_AREG12
+       s32i    a13, a1, PT_AREG13
+       s32i    a14, a1, PT_AREG14
+       s32i    a15, a1, PT_AREG15
+
+       /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
+
+       rsr     a0, EXCCAUSE
+       addi    a3, a0, -XCHAL_EXCCAUSE_COPROCESSOR0_DISABLED
+
+       /* Set corresponding CPENABLE bit */
+
+       movi    a4, 1
+       ssl     a3                      # SAR: 32 - coprocessor_number
+       rsr     a5, CPENABLE
+       sll     a4, a4
+       or      a4, a5, a4
+       wsr     a4, CPENABLE
+       rsync
+       movi    a5, coprocessor_info    # list of owner and offset into cp_save
+       addx8   a0, a4, a5              # entry for CP
+
+       bne     a4, a5, .Lload          # bit wasn't set before, cp not in use
+
+       /* Now compare the current task with the owner of the coprocessor.
+        * If they are the same, there is no reason to save or restore any
+        * coprocessor state. Having already enabled the coprocessor,
+        * branch ahead to return.
+        */
+       GET_CURRENT(a5,a1)
+       l32i    a4, a0, COPROCESSOR_INFO_OWNER  # a4: current owner for this CP
+       beq     a4, a5, .Ldone
+
+       /* Find location to dump current coprocessor state:
+        *  task_struct->task_cp_save_offset + coprocessor_offset[coprocessor]
+        *
+        * Note: a0 pointer to the entry in the coprocessor owner table,
+        *       a3 coprocessor number,
+         *      a4 current owner of coprocessor.
+        */
+       l32i    a5, a0, COPROCESSOR_INFO_OFFSET
+       addi    a2, a4, THREAD_CP_SAVE
+       add     a2, a2, a5
+
+       /* Store current coprocessor states. (a5 still has CP number) */
+
+       xchal_cpi_store_funcbody
+
+       /* The macro might have destroyed a3 (coprocessor number), but
+        * SAR still has 32 - coprocessor_number!
+        */
+       movi    a3, 32
+       rsr     a4, SAR
+       sub     a3, a3, a4
+
+.Lload:        /* A new task now owns the corpocessors. Save its TCB pointer into
+        * the coprocessor owner table.
+        *
+        * Note: a0 pointer to the entry in the coprocessor owner table,
+        *       a3 coprocessor number.
+        */
+       GET_CURRENT(a4,a1)
+       s32i    a4, a0, 0
+
+       /* Find location from where to restore the current coprocessor state.*/
+
+       l32i    a5, a0, COPROCESSOR_INFO_OFFSET
+       addi    a2, a4, THREAD_CP_SAVE
+       add     a2, a2, a4
+
+       xchal_cpi_load_funcbody
+
+       /* We must assume that the xchal_cpi_store_funcbody macro destroyed
+        * registers a2..a15.
+        */
+
+.Ldone:        l32i    a15, a1, PT_AREG15
+       l32i    a14, a1, PT_AREG14
+       l32i    a13, a1, PT_AREG13
+       l32i    a12, a1, PT_AREG12
+       l32i    a11, a1, PT_AREG11
+       l32i    a10, a1, PT_AREG10
+       l32i    a9, a1, PT_AREG9
+       l32i    a8, a1, PT_AREG8
+       l32i    a7, a1, PT_AREG7
+       l32i    a6, a1, PT_AREG6
+       l32i    a5, a1, PT_AREG5
+       l32i    a4, a1, PT_AREG4
+       l32i    a3, a1, PT_AREG3
+       l32i    a2, a1, PT_AREG2
+       l32i    a0, a1, PT_AREG0
+       l32i    a1, a1, PT_AREG1
+
+       rfe
+
+#endif /* XCHAL_EXTRA_SA_SIZE */
+
+/*
+ * Task switch.
+ *
+ * struct task*  _switch_to (struct task* prev, struct task* next)
+ *         a2                              a2                 a3
+ */
+
+ENTRY(_switch_to)
+
+       entry   a1, 16
+
+       mov     a4, a3                  # preserve a3
+
+       s32i    a0, a2, THREAD_RA       # save return address
+       s32i    a1, a2, THREAD_SP       # save stack pointer
+
+       /* Disable ints while we manipulate the stack pointer; spill regs. */
+
+       movi    a5, PS_EXCM_MASK | LOCKLEVEL
+       xsr     a5, PS
+       rsr     a3, EXCSAVE_1
+       rsync
+       s32i    a3, a3, EXC_TABLE_FIXUP /* enter critical section */
+
+       call0   _spill_registers
+
+       /* 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.
+        */
+
+       l32i    a0, a4, TASK_THREAD_INFO
+       rsr     a3, EXCSAVE_1           # exc_table
+       movi    a1, 0
+       addi    a0, a0, PT_REGS_OFFSET
+       s32i    a1, a3, EXC_TABLE_FIXUP
+       s32i    a0, a3, EXC_TABLE_KSTK
+
+       /* restore context of the task that 'next' addresses */
+
+       l32i    a0, a4, THREAD_RA       /* restore return address */
+       l32i    a1, a4, THREAD_SP       /* restore stack pointer */
+
+       wsr     a5, PS
+       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
+       callx4  a4
+
+       j       common_exception_return
+
+
+
+/*
+ * Table of syscalls
+ */
+
+.data
+.align  4
+.global sys_call_table
+sys_call_table:
+
+#define SYSCALL(call, narg) .word call
+#include "syscalls.h"
+
+/*
+ * Number of arguments of each syscall
+ */
+
+.global sys_narg_table
+sys_narg_table:
+
+#undef SYSCALL
+#define SYSCALL(call, narg) .byte narg
+#include "syscalls.h"
+
diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S
new file mode 100644 (file)
index 0000000..6e9b522
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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 <xtensa/cacheasm.h>
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/page.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)
+ *
+ */
+
+       .macro  iterate from, to , cmd
+               .ifeq   ((\to - \from) & ~0xfff)
+                       \cmd    \from
+                       iterate "(\from+1)", \to, \cmd
+               .endif
+       .endm
+
+/*
+ *  _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.
+        */
+
+       .section .head.text, "ax"
+       .globl _start
+_start:        _j      2f
+       .align  4
+1:     .word   _startup
+2:     l32r    a0, 1b
+       jx      a0
+
+       .text
+       .align 4
+_startup:
+
+       /* Disable interrupts and exceptions. */
+
+       movi    a0, XCHAL_PS_EXCM_MASK
+       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
+
+       .macro reset_dbreak num
+       wsr     a0, DBREAKC + \num
+       .endm
+
+        iterate 0, XCHAL_NUM_IBREAK-1, reset_dbreak
+#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. */
+
+       .macro  reset_timer     num
+       wsr     a0, CCOMPARE_0 + \num
+       .endm
+       iterate 0, XCHAL_NUM_TIMERS-1, reset_timer
+
+       /* 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.
+        *  Does not include flushing writeback d-cache.
+        *  a6, a7 are just working registers (clobbered).
+        */
+
+       icache_reset  a2, a3
+       dcache_reset  a2, a3
+
+       /* 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_end    # end of BSS
+
+1:     addi    a2, a2, 4
+       s32i    a0, a2, 0
+       blt     a2, a3, 1b
+
+#if XCHAL_DCACHE_IS_WRITEBACK
+
+       /* After unpacking, flush the writeback cache to memory so the
+        * instructions/data are available.
+        */
+
+       dcache_writeback_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
+
+       /* Define some common data structures here.  We define them
+        * here in this assembly file due to their unusual alignment
+        * requirements.
+        */
+
+       .comm   swapper_pg_dir,PAGE_SIZE,PAGE_SIZE
+       .comm   empty_bad_page_table,PAGE_SIZE,PAGE_SIZE
+       .comm   empty_bad_page,PAGE_SIZE,PAGE_SIZE
+       .comm   empty_zero_page,PAGE_SIZE,PAGE_SIZE
+
diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c
new file mode 100644 (file)
index 0000000..4cbf6d9
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * linux/arch/xtensa/kernel/irq.c
+ *
+ * Xtensa built-in interrupt controller and some generic functions copied
+ * from i386.
+ *
+ * Copyright (C) 2002 - 2005 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 void enable_xtensa_irq(unsigned int irq);
+static void disable_xtensa_irq(unsigned int irq);
+static void mask_and_ack_xtensa(unsigned int irq);
+static void end_xtensa_irq(unsigned int irq);
+
+static unsigned int cached_irq_mask;
+
+atomic_t irq_err_count;
+
+/*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves.
+ */
+void ack_bad_irq(unsigned int irq)
+{
+          printk("unexpected IRQ trap at vector %02x\n", irq);
+}
+
+/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ */
+
+unsigned int  do_IRQ(int irq, struct pt_regs *regs)
+{
+       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
+
+       __do_IRQ(irq, regs);
+
+       irq_exit();
+
+       return 1;
+}
+
+/*
+ * Generic, controller-independent functions:
+ */
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+       int i = *(loff_t *) v, j;
+       struct irqaction * action;
+       unsigned long flags;
+
+       if (i == 0) {
+               seq_printf(p, "           ");
+               for (j=0; j<NR_CPUS; j++)
+                       if (cpu_online(j))
+                               seq_printf(p, "CPU%d       ",j);
+               seq_putc(p, '\n');
+       }
+
+       if (i < NR_IRQS) {
+               spin_lock_irqsave(&irq_desc[i].lock, flags);
+               action = irq_desc[i].action;
+               if (!action)
+                       goto skip;
+               seq_printf(p, "%3d: ",i);
+#ifndef CONFIG_SMP
+               seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+               for (j = 0; j < NR_CPUS; j++)
+                       if (cpu_online(j))
+                               seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
+#endif
+               seq_printf(p, " %14s", irq_desc[i].handler->typename);
+               seq_printf(p, "  %s", action->name);
+
+               for (action=action->next; action; action = action->next)
+                       seq_printf(p, ", %s", action->name);
+
+               seq_putc(p, '\n');
+skip:
+               spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+       } else if (i == NR_IRQS) {
+               seq_printf(p, "NMI: ");
+               for (j = 0; j < NR_CPUS; j++)
+                       if (cpu_online(j))
+                               seq_printf(p, "%10u ", nmi_count(j));
+               seq_putc(p, '\n');
+               seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
+       }
+       return 0;
+}
+/* shutdown is same as "disable" */
+#define shutdown_xtensa_irq disable_xtensa_irq
+
+static unsigned int startup_xtensa_irq(unsigned int irq)
+{
+       enable_xtensa_irq(irq);
+       return 0;               /* never anything pending */
+}
+
+static struct hw_interrupt_type xtensa_irq_type = {
+       "Xtensa-IRQ",
+       startup_xtensa_irq,
+       shutdown_xtensa_irq,
+       enable_xtensa_irq,
+       disable_xtensa_irq,
+       mask_and_ack_xtensa,
+       end_xtensa_irq
+};
+
+static inline void mask_irq(unsigned int irq)
+{
+       cached_irq_mask &= ~(1 << irq);
+       set_sr (cached_irq_mask, INTENABLE);
+}
+
+static inline void unmask_irq(unsigned int irq)
+{
+       cached_irq_mask |= 1 << irq;
+       set_sr (cached_irq_mask, INTENABLE);
+}
+
+static void disable_xtensa_irq(unsigned int irq)
+{
+       unsigned long flags;
+       local_save_flags(flags);
+       mask_irq(irq);
+       local_irq_restore(flags);
+}
+
+static void enable_xtensa_irq(unsigned int irq)
+{
+       unsigned long flags;
+       local_save_flags(flags);
+       unmask_irq(irq);
+       local_irq_restore(flags);
+}
+
+static void mask_and_ack_xtensa(unsigned int irq)
+{
+        disable_xtensa_irq(irq);
+}
+
+static void end_xtensa_irq(unsigned int irq)
+{
+        enable_xtensa_irq(irq);
+}
+
+
+void __init init_IRQ(void)
+{
+       int i;
+
+       for (i=0; i < XTENSA_NR_IRQS; i++)
+               irq_desc[i].handler = &xtensa_irq_type;
+
+       cached_irq_mask = 0;
+
+       platform_init_irq();
+}
diff --git a/arch/xtensa/kernel/module.c b/arch/xtensa/kernel/module.c
new file mode 100644 (file)
index 0000000..d1683cf
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * arch/xtensa/kernel/platform.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 - 2005 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>
+
+LIST_HEAD(module_buf_list);
+
+void *module_alloc(unsigned long size)
+{
+  panic("module_alloc not implemented");
+}
+
+void module_free(struct module *mod, void *module_region)
+{
+  panic("module_free not implemented");
+}
+
+int module_frob_arch_sections(Elf32_Ehdr *hdr,
+                             Elf32_Shdr *sechdrs,
+                             char *secstrings,
+                             struct module *me)
+{
+  panic("module_frob_arch_sections not implemented");
+}
+
+int apply_relocate(Elf32_Shdr *sechdrs,
+                  const char *strtab,
+                  unsigned int symindex,
+                  unsigned int relsec,
+                  struct module *module)
+{
+  panic ("apply_relocate not implemented");
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+                      const char *strtab,
+                      unsigned int symindex,
+                      unsigned int relsec,
+                      struct module *module)
+{
+  panic("apply_relocate_add not implemented");
+}
+
+int module_finalize(const Elf_Ehdr *hdr,
+                   const Elf_Shdr *sechdrs,
+                   struct module *me)
+{
+  panic ("module_finalize not implemented");
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+  panic("module_arch_cleanup not implemented");
+}
+
+struct bug_entry *module_find_bug(unsigned long bugaddr)
+{
+  panic("module_find_bug not implemented");
+}
diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c
new file mode 100644 (file)
index 0000000..84fde25
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * arch/xtensa/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 <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, int gfp)
+{
+       void *ret;
+
+       /* ignore region speicifiers */
+       gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+       if (dev == NULL || (*dev->dma_mask < 0xffffffff))
+               gfp |= GFP_DMA;
+       ret = (void *)__get_free_pages(gfp, get_order(size));
+
+       if (ret != NULL) {
+               memset(ret, 0, size);
+               *handle = virt_to_bus(ret);
+       }
+       return (void*) BYPASS_ADDR((unsigned long)ret);
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size,
+                        void *vaddr, dma_addr_t dma_handle)
+{
+       free_pages(CACHED_ADDR((unsigned long)vaddr), 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 (file)
index 0000000..d29a816
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * arch/xtensa/pcibios.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/config.h>
+#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;
+
+static void pcibios_fixup_resources(struct pci_dev* dev);
+
+#if 0 // FIXME
+struct pci_fixup pcibios_fixups[] = {
+       { DECLARE_PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources },
+       { 0 }
+};
+#endif
+
+void
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+                       struct resource *res, int resource)
+{
+       u32 new, check, mask;
+       int reg;
+       struct pci_controller* pci_ctrl = dev->sysdata;
+
+       new = res->start;
+       if (pci_ctrl && res->flags & IORESOURCE_IO) {
+               new -= pci_ctrl->io_space.base;
+       }
+       new |= (res->flags & PCI_REGION_FLAG_MASK);
+       if (resource < 6) {
+               reg = PCI_BASE_ADDRESS_0 + 4*resource;
+       } else if (resource == PCI_ROM_RESOURCE) {
+               res->flags |= PCI_ROM_ADDRESS_ENABLE;
+               reg = dev->rom_base_reg;
+       } else {
+       /* Somebody might have asked allocation of a non-standard resource */
+               return;
+       }
+
+       pci_write_config_dword(dev, reg, new);
+       pci_read_config_dword(dev, reg, &check);
+       mask = (new & PCI_BASE_ADDRESS_SPACE_IO) ?
+               PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
+
+       if ((new ^ check) & mask) {
+               printk(KERN_ERR "PCI: Error while updating region "
+                      "%s/%d (%08x != %08x)\n", dev->slot_name, resource,
+                      new, check);
+       }
+}
+
+/*
+ * 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..
+ */
+void
+pcibios_align_resource(void *data, struct resource *res, unsigned long size,
+                      unsigned long align)
+{
+       struct pci_dev *dev = data;
+
+       if (res->flags & IORESOURCE_IO) {
+               unsigned long start = res->start;
+
+               if (size > 0x100) {
+                       printk(KERN_ERR "PCI: I/O Region %s/%d too large"
+                              " (%ld bytes)\n", dev->slot_name,
+                              dev->resource - res, size);
+               }
+
+               if (start & 0x300) {
+                       start = (start + 0x3ff) & ~0x3ff;
+                       res->start = 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", dev->slot_name);
+                       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",
+                       dev->slot_name, 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 int __init pcibios_init(void)
+{
+       struct pci_controller *pci_ctrl;
+       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;
+               bus = pci_scan_bus(pci_ctrl->first_busno, pci_ctrl->ops,
+                                  pci_ctrl);
+               if (pci_ctrl->io_resource.flags) {
+                       unsigned long offs;
+
+                       offs = (unsigned long)pci_ctrl->io_space.base;
+                       pci_ctrl->io_resource.start += offs;
+                       pci_ctrl->io_resource.end += offs;
+                       bus->resource[0] = &pci_ctrl->io_resource;
+               }
+               for (i = 0; i < 3; ++i)
+                       if (pci_ctrl->mem_resources[i].flags)
+                               bus->resource[i+1] =&pci_ctrl->mem_resources[i];
+               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)
+{
+       struct pci_controller *pci_ctrl = bus->sysdata;
+       struct resource *res;
+       unsigned long io_offset;
+       int i;
+
+       io_offset = (unsigned long)pci_ctrl->io_space.base;
+       if (bus->parent == NULL) {
+               /* this is a host bridge - fill in its resources */
+               pci_ctrl->bus = bus;
+
+               bus->resource[0] = 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;
+
+               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;
+                       }
+                       bus->resource[i+1] = res;
+               }
+       } else {
+               /* This is a subordinate bridge */
+               pci_read_bridge_bases(bus);
+
+               for (i = 0; i < 4; i++) {
+                       if ((res = bus->resource[i]) == NULL || !res->flags)
+                               continue;
+                       if (io_offset && (res->flags & IORESOURCE_IO)) {
+                               res->start += io_offset;
+                               res->end += io_offset;
+                       }
+               }
+       }
+}
+
+char __init *pcibios_setup(char *str)
+{
+       return str;
+}
+
+/* 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", dev->slot_name);
+                       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",
+                      dev->slot_name, 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 */
+
+
+static void
+pcibios_fixup_resources(struct pci_dev *dev)
+{
+       struct pci_controller* pci_ctrl = (struct pci_controller *)dev->sysdata;
+       int i;
+       unsigned long offset;
+
+       if (!pci_ctrl) {
+               printk(KERN_ERR "No pci_ctrl for PCI dev %s!\n",dev->slot_name);
+               return;
+       }
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               struct resource *res = dev->resource + i;
+               if (!res->start || !res->flags)
+                       continue;
+               if (res->end == 0xffffffff) {
+                       DBG("PCI:%s Resource %d [%08lx-%08lx] is unassigned\n",
+                           dev->slot_name, i, res->start, res->end);
+                       res->end -= res->start;
+                       res->start = 0;
+                       continue;
+               }
+               offset = 0;
+               if (res->flags & IORESOURCE_IO)
+                       offset = (unsigned long) pci_ctrl->io_space.base;
+               else if (res->flags & IORESOURCE_MEM)
+                       offset = (unsigned long) pci_ctrl->mem_space.base;
+
+               if (offset != 0) {
+                       res->start += offset;
+                       res->end += offset;
+#ifdef DEBUG
+                       printk("Fixup res %d (%lx) of dev %s: %lx -> %lx\n",
+                              i, res->flags, dev->slot_name,
+                              res->start - offset, res->start);
+#endif
+               }
+       }
+}
+
+/*
+ * 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_flags of VMA, as appropriate for this architecture, for a pci device
+ * mapping.
+ */
+static __inline__ void
+__pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma,
+                    enum pci_mmap_state mmap_state)
+{
+       vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO;
+}
+
+/*
+ * 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_flags(dev, vma, mmap_state);
+       __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine);
+
+       ret = io_remap_page_range(vma, vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT,
+                              vma->vm_end - vma->vm_start, vma->vm_page_prot);
+
+       return ret;
+}
+
+/*
+ * This probably belongs here rather than ioport.c because
+ * we do not want this crud linked into SBus kernels.
+ * Also, think for a moment about likes of floppy.c that
+ * include architecture specific parts. They may want to redefine ins/outs.
+ *
+ * We do not use horroble macroses here because we want to
+ * advance pointer by sizeof(size).
+ */
+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/platform.c b/arch/xtensa/kernel/platform.c
new file mode 100644 (file)
index 0000000..cf13627
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <asm/platform.h>
+#include <asm/timex.h>
+
+#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; });
+_F(int, get_rtc_time, (time_t* t), { return 0; });
+_F(int, set_rtc_time, (time_t t), { return 0; });
+
+#if 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 (file)
index 0000000..4099703
--- /dev/null
@@ -0,0 +1,482 @@
+// TODO        verify coprocessor handling
+/*
+ * 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/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.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 <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/mmu.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+#include <asm/offsets.h>
+#include <asm/coprocessor.h>
+
+extern void ret_from_fork(void);
+
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+struct mm_struct init_mm = INIT_MM(init_mm);
+EXPORT_SYMBOL(init_mm);
+
+union thread_union init_thread_union
+       __attribute__((__section__(".data.init_task"))) =
+{ INIT_THREAD_INFO(init_task) };
+
+struct task_struct init_task = INIT_TASK(init_task);
+EXPORT_SYMBOL(init_task);
+
+struct task_struct *current_set[NR_CPUS] = {&init_task, };
+
+
+#if XCHAL_CP_NUM > 0
+
+/*
+ * Coprocessor ownership.
+ */
+
+coprocessor_info_t coprocessor_info[] = {
+       { 0, XTENSA_CPE_CP0_OFFSET },
+       { 0, XTENSA_CPE_CP1_OFFSET },
+       { 0, XTENSA_CPE_CP2_OFFSET },
+       { 0, XTENSA_CPE_CP3_OFFSET },
+       { 0, XTENSA_CPE_CP4_OFFSET },
+       { 0, XTENSA_CPE_CP5_OFFSET },
+       { 0, XTENSA_CPE_CP6_OFFSET },
+       { 0, XTENSA_CPE_CP7_OFFSET },
+};
+
+#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();
+               preempt_enable();
+               schedule();
+       }
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+
+void exit_thread(void)
+{
+       release_coprocessors(current);  /* Empty macro if no CPs are defined */
+}
+
+void flush_thread(void)
+{
+       release_coprocessors(current);  /* Empty macro if no CPs are defined */
+}
+
+/*
+ * 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(int nr, unsigned long clone_flags, unsigned long usp,
+               unsigned long unused,
+                struct task_struct * p, struct pt_regs * regs)
+{
+       struct pt_regs *childregs;
+       unsigned long tos;
+       int user_mode = user_mode(regs);
+
+       /* Set up new TSS. */
+       tos = (unsigned long)p->thread_info + 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);
+
+               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;
+       }
+       return 0;
+}
+
+
+/*
+ * Create a kernel thread
+ */
+
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+       long retval;
+       __asm__ __volatile__
+               ("mov           a5, %4\n\t" /* preserve fn in a5 */
+                "mov           a6, %3\n\t" /* preserve and setup arg in a6 */
+                "movi          a2, %1\n\t" /* load __NR_clone for syscall*/
+                "mov           a3, sp\n\t" /* sp check and sys_clone */
+                "mov           a4, %5\n\t" /* load flags for syscall */
+                "syscall\n\t"
+                "beq           a3, sp, 1f\n\t" /* branch if parent */
+                "callx4        a5\n\t"     /* call fn */
+                "movi          a2, %2\n\t" /* load __NR_exit for syscall */
+                "mov           a3, a6\n\t" /* load fn return value */
+                "syscall\n"
+                "1:\n\t"
+                "mov           %0, a2\n\t" /* parent returns zero */
+                :"=r" (retval)
+                :"i" (__NR_clone), "i" (__NR_exit),
+                "r" (arg), "r" (fn),
+                "r" (flags | CLONE_VM)
+                : "a2", "a3", "a4", "a5", "a6" );
+       return retval;
+}
+
+
+/*
+ * These bracket the sleeping functions..
+ */
+
+unsigned long get_wchan(struct task_struct *p)
+{
+       unsigned long sp, pc;
+       unsigned long stack_page = (unsigned long) p->thread_info;
+       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;
+}
+
+/*
+ * do_copy_regs() gathers information from 'struct pt_regs' and
+ * 'current->thread.areg[]' to fill in the xtensa_gregset_t
+ * structure.
+ *
+ * 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 do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
+                  struct task_struct *tsk)
+{
+       int i, n, wb_offset;
+
+       elfregs->xchal_config_id0 = XCHAL_HW_CONFIGID0;
+       elfregs->xchal_config_id1 = XCHAL_HW_CONFIGID1;
+
+       __asm__ __volatile__ ("rsr  %0, 176\n" : "=a" (i));
+       elfregs->cpux = i;
+       __asm__ __volatile__ ("rsr  %0, 208\n" : "=a" (i));
+       elfregs->cpuy = i;
+
+       /* 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 & ~XCHAL_PS_EXCM_MASK);
+       elfregs->exccause       = regs->exccause;
+       elfregs->excvaddr       = regs->excvaddr;
+       elfregs->windowbase     = regs->windowbase;
+       elfregs->windowstart    = regs->windowstart;
+       elfregs->lbeg           = regs->lbeg;
+       elfregs->lend           = regs->lend;
+       elfregs->lcount         = regs->lcount;
+       elfregs->sar            = regs->sar;
+       elfregs->syscall        = regs->syscall;
+
+       /* Copy register file.
+        * The layout looks like this:
+        *
+        * |  a0 ... a15  | Z ... Z |  arX ... arY  |
+        *  current window  unused    saved frames
+        */
+
+       memset (elfregs->ar, 0, sizeof(elfregs->ar));
+
+       wb_offset = regs->windowbase * 4;
+       n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16;
+
+       for (i = 0; i < n; i++)
+               elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i];
+
+       n = (regs->wmask >> 4) * 4;
+
+       for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--)
+               elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i];
+}
+
+void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
+{
+       do_copy_regs ((xtensa_gregset_t *)elfregs, regs, current);
+}
+
+
+/* The inverse of do_copy_regs().  No error or sanity checking. */
+
+void do_restore_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
+                     struct task_struct *tsk)
+{
+       int i, n, wb_offset;
+
+       /* Note:  PS.EXCM is not set while user task is running; it
+        * needs to be set in regs->ps is for exception handling convenience.
+        */
+
+       regs->pc                = elfregs->pc;
+       regs->ps                = (elfregs->ps | XCHAL_PS_EXCM_MASK);
+       regs->exccause          = elfregs->exccause;
+       regs->excvaddr          = elfregs->excvaddr;
+       regs->windowbase        = elfregs->windowbase;
+       regs->windowstart       = elfregs->windowstart;
+       regs->lbeg              = elfregs->lbeg;
+       regs->lend              = elfregs->lend;
+       regs->lcount            = elfregs->lcount;
+       regs->sar               = elfregs->sar;
+       regs->syscall   = elfregs->syscall;
+
+       /* Clear everything. */
+
+       memset (regs->areg, 0, sizeof(regs->areg));
+
+       /* Copy regs from live window frame. */
+
+       wb_offset = regs->windowbase * 4;
+       n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16;
+
+       for (i = 0; i < n; i++)
+               regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i];
+
+       n = (regs->wmask >> 4) * 4;
+
+       for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--)
+               regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i];
+}
+
+/*
+ * do_save_fpregs() gathers information from 'struct pt_regs' and
+ * 'current->thread' to fill in the elf_fpregset_t structure.
+ *
+ * Core files and ptrace use elf_fpregset_t.
+ */
+
+void do_save_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
+                    struct task_struct *tsk)
+{
+#if XCHAL_HAVE_CP
+
+       extern unsigned char    _xtensa_reginfo_tables[];
+       extern unsigned         _xtensa_reginfo_table_size;
+       int i;
+       unsigned long flags;
+
+       /* Before dumping coprocessor state from memory,
+        * ensure any live coprocessor contents for this
+        * task are first saved to memory:
+        */
+       local_irq_save(flags);
+
+       for (i = 0; i < XCHAL_CP_MAX; i++) {
+               if (tsk == coprocessor_info[i].owner) {
+                       enable_coprocessor(i);
+                       save_coprocessor_registers(
+                           tsk->thread.cp_save+coprocessor_info[i].offset,i);
+                       disable_coprocessor(i);
+               }
+       }
+
+       local_irq_restore(flags);
+
+       /* Now dump coprocessor & extra state: */
+       memcpy((unsigned char*)fpregs,
+               _xtensa_reginfo_tables, _xtensa_reginfo_table_size);
+       memcpy((unsigned char*)fpregs + _xtensa_reginfo_table_size,
+               tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
+#endif
+}
+
+/*
+ * The inverse of do_save_fpregs().
+ * Copies coprocessor and extra state from fpregs into regs and tsk->thread.
+ * Returns 0 on success, non-zero if layout doesn't match.
+ */
+
+int  do_restore_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
+                       struct task_struct *tsk)
+{
+#if XCHAL_HAVE_CP
+
+       extern unsigned char    _xtensa_reginfo_tables[];
+       extern unsigned         _xtensa_reginfo_table_size;
+       int i;
+       unsigned long flags;
+
+       /* Make sure save area layouts match.
+        * FIXME:  in the future we could allow restoring from
+        * a different layout of the same registers, by comparing
+        * fpregs' table with _xtensa_reginfo_tables and matching
+        * entries and copying registers one at a time.
+        * Not too sure yet whether that's very useful.
+        */
+
+       if( memcmp((unsigned char*)fpregs,
+               _xtensa_reginfo_tables, _xtensa_reginfo_table_size) ) {
+           return -1;
+       }
+
+       /* Before restoring coprocessor state from memory,
+        * ensure any live coprocessor contents for this
+        * task are first invalidated.
+        */
+
+       local_irq_save(flags);
+
+       for (i = 0; i < XCHAL_CP_MAX; i++) {
+               if (tsk == coprocessor_info[i].owner) {
+                       enable_coprocessor(i);
+                       save_coprocessor_registers(
+                           tsk->thread.cp_save+coprocessor_info[i].offset,i);
+                       coprocessor_info[i].owner = 0;
+                       disable_coprocessor(i);
+               }
+       }
+
+       local_irq_restore(flags);
+
+       /*  Now restore coprocessor & extra state:  */
+
+       memcpy(tsk->thread.cp_save,
+               (unsigned char*)fpregs + _xtensa_reginfo_table_size,
+               XTENSA_CP_EXTRA_SIZE);
+#endif
+       return 0;
+}
+/*
+ * Fill in the CP structure for a core dump for a particular task.
+ */
+
+int
+dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r)
+{
+/* see asm/coprocessor.h for this magic number 16 */
+#if TOTAL_CPEXTRA_SIZE > 16
+       do_save_fpregs (r, regs, task);
+
+       /*  For now, bit 16 means some extra state may be present:  */
+// FIXME!! need to track to return more accurate mask
+       return 0x10000 | XCHAL_CP_MASK;
+#else
+       return 0;       /* no coprocessors active on this processor */
+#endif
+}
+
+/*
+ * Fill in the CP structure for a core dump.
+ * This includes any FPU coprocessor.
+ * Here, we dump all coprocessors, and other ("extra") custom state.
+ *
+ * This function is called by elf_core_dump() in fs/binfmt_elf.c
+ * (in which case 'regs' comes from calls to do_coredump, see signals.c).
+ */
+int  dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
+{
+       return dump_task_fpu(regs, current, r);
+}
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
new file mode 100644 (file)
index 0000000..9ef07a4
--- /dev/null
@@ -0,0 +1,407 @@
+// 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 - 2005  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/config.h>
+#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/smp_lock.h>
+#include <linux/security.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/elf.h>
+
+#define TEST_KERNEL    // verify kernel operations FIXME: remove
+
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+
+void ptrace_disable(struct task_struct *child)
+{
+       /* Nothing to do.. */
+}
+
+int sys_ptrace(long request, long pid, long addr, long data)
+{
+       struct task_struct *child;
+       int ret = -EPERM;
+
+       lock_kernel();
+
+#if 0
+       if ((int)request != 1)
+       printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
+              (int) request, (int) pid, (unsigned long) addr,
+              (unsigned long) data);
+#endif
+
+       if (request == PTRACE_TRACEME) {
+
+               /* Are we already being traced? */
+
+               if (current->ptrace & PT_PTRACED)
+                       goto out;
+
+               if ((ret = security_ptrace(current->parent, current)))
+                       goto out;
+
+               /* Set the ptrace bit in the process flags. */
+
+               current->ptrace |= PT_PTRACED;
+               ret = 0;
+               goto out;
+       }
+
+       ret = -ESRCH;
+       read_lock(&tasklist_lock);
+       child = find_task_by_pid(pid);
+       if (child)
+               get_task_struct(child);
+       read_unlock(&tasklist_lock);
+       if (!child)
+               goto out;
+
+       ret = -EPERM;
+       if (pid == 1)           /* you may not mess with init */
+               goto out;
+
+       if (request == PTRACE_ATTACH) {
+               ret = ptrace_attach(child);
+               goto out_tsk;
+       }
+
+       if ((ret = ptrace_check_attach(child, request == PTRACE_KILL)) < 0)
+               goto out_tsk;
+
+       switch (request) {
+       case PTRACE_PEEKTEXT: /* read word at location addr. */
+       case PTRACE_PEEKDATA:
+       {
+               unsigned long tmp;
+               int copied;
+
+               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+               ret = -EIO;
+               if (copied != sizeof(tmp))
+                       break;
+               ret = put_user(tmp,(unsigned long *) data);
+
+               goto out;
+       }
+
+       /* Read the word at location addr in the USER area.  */
+
+       case PTRACE_PEEKUSR:
+               {
+               struct pt_regs *regs;
+               unsigned long tmp;
+
+               regs = xtensa_pt_regs(child);
+               tmp = 0;  /* Default return value. */
+
+               switch(addr) {
+
+               case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
+                       {
+                       int ar = addr - REG_AR_BASE - regs->windowbase * 4;
+                       ar &= (XCHAL_NUM_AREGS - 1);
+                       if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
+                               tmp = regs->areg[ar];
+                       else
+                               ret = -EIO;
+                       break;
+                       }
+               case REG_A_BASE ... REG_A_BASE + 15:
+                       tmp = regs->areg[addr - 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 & ~XCHAL_PS_EXCM_MASK);
+                       break;
+               case REG_WB:
+                       tmp = regs->windowbase;
+                       break;
+               case REG_WS:
+                       tmp = regs->windowstart;
+                       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 REG_DEPC:
+                       tmp = regs->depc;
+                       break;
+               case REG_EXCCAUSE:
+                       tmp = regs->exccause;
+                       break;
+               case REG_EXCVADDR:
+                       tmp = regs->excvaddr;
+                       break;
+               case SYSCALL_NR:
+                       tmp = regs->syscall;
+                       break;
+               default:
+                       tmp = 0;
+                       ret = -EIO;
+                       goto out;
+               }
+               ret = put_user(tmp, (unsigned long *) data);
+               goto out;
+               }
+
+       case PTRACE_POKETEXT: /* write the word at location addr. */
+       case PTRACE_POKEDATA:
+               if (access_process_vm(child, addr, &data, sizeof(data), 1)
+                   == sizeof(data))
+                       break;
+               ret = -EIO;
+               goto out;
+
+       case PTRACE_POKEUSR:
+               {
+               struct pt_regs *regs;
+               regs = xtensa_pt_regs(child);
+
+               switch (addr) {
+               case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
+                       {
+                       int ar = addr - REG_AR_BASE - regs->windowbase * 4;
+                       if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
+                               regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data;
+                       else
+                               ret = -EIO;
+                       break;
+                       }
+               case REG_A_BASE ... REG_A_BASE + 15:
+                       regs->areg[addr - REG_A_BASE] = data;
+                       break;
+               case REG_PC:
+                       regs->pc = data;
+                       break;
+               case SYSCALL_NR:
+                       regs->syscall = data;
+                       break;
+#ifdef TEST_KERNEL
+               case REG_WB:
+                       regs->windowbase = data;
+                       break;
+               case REG_WS:
+                       regs->windowstart = data;
+                       break;
+#endif
+
+               default:
+                       /* The rest are not allowed. */
+                       ret = -EIO;
+                       break;
+               }
+               break;
+               }
+
+       /* continue and stop at next (return from) syscall */
+       case PTRACE_SYSCALL:
+       case PTRACE_CONT: /* restart after signal. */
+       {
+               ret = -EIO;
+               if ((unsigned long) data > _NSIG)
+                       break;
+               if (request == PTRACE_SYSCALL)
+                       set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               else
+                       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               child->exit_code = data;
+               /* Make sure the single step bit is not set. */
+               child->ptrace &= ~PT_SINGLESTEP;
+               wake_up_process(child);
+               ret = 0;
+               break;
+       }
+
+       /*
+        * make the child exit.  Best I can do is send it a sigkill.
+        * perhaps it should be put in the status that it wants to
+        * exit.
+        */
+       case PTRACE_KILL:
+               ret = 0;
+               if (child->state == EXIT_ZOMBIE)        /* already dead */
+                       break;
+               child->exit_code = SIGKILL;
+               child->ptrace &= ~PT_SINGLESTEP;
+               wake_up_process(child);
+               break;
+
+       case PTRACE_SINGLESTEP:
+               ret = -EIO;
+               if ((unsigned long) data > _NSIG)
+                       break;
+               clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               child->ptrace |= PT_SINGLESTEP;
+               child->exit_code = data;
+               wake_up_process(child);
+               ret = 0;
+               break;
+
+       case PTRACE_GETREGS:
+       {
+               /* 'data' points to user memory in which to write.
+                * Mainly due to the non-live register values, we
+                * reformat the register values into something more
+                * standard.  For convenience, we use the handy
+                * elf_gregset_t format. */
+
+               xtensa_gregset_t format;
+               struct pt_regs *regs = xtensa_pt_regs(child);
+
+               do_copy_regs (&format, regs, child);
+
+               /* Now, copy to user space nice and easy... */
+               ret = 0;
+               if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t)))
+                       ret = -EFAULT;
+               break;
+       }
+
+       case PTRACE_SETREGS:
+       {
+               /* 'data' points to user memory that contains the new
+                * values in the elf_gregset_t format. */
+
+               xtensa_gregset_t format;
+               struct pt_regs *regs = xtensa_pt_regs(child);
+
+               if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){
+                       ret = -EFAULT;
+                       break;
+               }
+
+               /* FIXME: Perhaps we want some sanity checks on
+                * these user-space values?  See ARM version.  Are
+                * debuggers a security concern? */
+
+               do_restore_regs (&format, regs, child);
+
+               ret = 0;
+               break;
+       }
+
+       case PTRACE_GETFPREGS:
+       {
+               /* 'data' points to user memory in which to write.
+                * For convenience, we use the handy
+                * elf_fpregset_t format. */
+
+               elf_fpregset_t fpregs;
+               struct pt_regs *regs = xtensa_pt_regs(child);
+
+               do_save_fpregs (&fpregs, regs, child);
+
+               /* Now, copy to user space nice and easy... */
+               ret = 0;
+               if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t)))
+                       ret = -EFAULT;
+
+               break;
+       }
+
+       case PTRACE_SETFPREGS:
+       {
+               /* 'data' points to user memory that contains the new
+                * values in the elf_fpregset_t format.
+                */
+               elf_fpregset_t fpregs;
+               struct pt_regs *regs = xtensa_pt_regs(child);
+
+               ret = 0;
+               if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               if (do_restore_fpregs (&fpregs, regs, child))
+                       ret = -EIO;
+               break;
+       }
+
+       case PTRACE_GETFPREGSIZE:
+               /* 'data' points to 'unsigned long' set to the size
+                * of elf_fpregset_t
+                */
+               ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data);
+               break;
+
+       case PTRACE_DETACH: /* detach a process that was attached. */
+               ret = ptrace_detach(child, data);
+               break;
+
+       default:
+               ret = ptrace_request(child, request, addr, data);
+               goto out;
+       }
+out_tsk:
+       put_task_struct(child);
+out:
+       unlock_kernel();
+       return ret;
+}
+
+void do_syscall_trace(void)
+{
+       if (!test_thread_flag(TIF_SYSCALL_TRACE))
+               return;
+
+       if (!(current->ptrace & PT_PTRACED))
+               return;
+
+       /*
+        * 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;
+       }
+}
diff --git a/arch/xtensa/kernel/semaphore.c b/arch/xtensa/kernel/semaphore.c
new file mode 100644 (file)
index 0000000..d40f4b1
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * arch/xtensa/kernel/semaphore.c
+ *
+ * Generic semaphore code. Buyer beware. Do your own specific changes
+ * in <asm/semaphore-helper.h>
+ *
+ * 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/sched.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <asm/semaphore.h>
+#include <asm/errno.h>
+
+/*
+ * These two _must_ execute atomically wrt each other.
+ */
+
+static __inline__ void wake_one_more(struct semaphore * sem)
+{
+       atomic_inc((atomic_t *)&sem->sleepers);
+}
+
+static __inline__ int waking_non_zero(struct semaphore *sem)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&semaphore_wake_lock, flags);
+       if (sem->sleepers > 0) {
+               sem->sleepers--;
+               ret = 1;
+       }
+       spin_unlock_irqrestore(&semaphore_wake_lock, flags);
+       return ret;
+}
+
+/*
+ * waking_non_zero_interruptible:
+ *     1       got the lock
+ *     0       go to sleep
+ *     -EINTR  interrupted
+ *
+ * We must undo the sem->count down_interruptible() increment while we are
+ * protected by the spinlock in order to make atomic this atomic_inc() with the
+ * atomic_read() in wake_one_more(), otherwise we can race. -arca
+ */
+
+static __inline__ int waking_non_zero_interruptible(struct semaphore *sem,
+                                               struct task_struct *tsk)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&semaphore_wake_lock, flags);
+       if (sem->sleepers > 0) {
+               sem->sleepers--;
+               ret = 1;
+       } else if (signal_pending(tsk)) {
+               atomic_inc(&sem->count);
+               ret = -EINTR;
+       }
+       spin_unlock_irqrestore(&semaphore_wake_lock, flags);
+       return ret;
+}
+
+/*
+ * waking_non_zero_trylock:
+ *     1       failed to lock
+ *     0       got the lock
+ *
+ * We must undo the sem->count down_trylock() increment while we are
+ * protected by the spinlock in order to make atomic this atomic_inc() with the
+ * atomic_read() in wake_one_more(), otherwise we can race. -arca
+ */
+
+static __inline__ int waking_non_zero_trylock(struct semaphore *sem)
+{
+       unsigned long flags;
+       int ret = 1;
+
+       spin_lock_irqsave(&semaphore_wake_lock, flags);
+       if (sem->sleepers <= 0)
+               atomic_inc(&sem->count);
+       else {
+               sem->sleepers--;
+               ret = 0;
+       }
+       spin_unlock_irqrestore(&semaphore_wake_lock, flags);
+       return ret;
+}
+
+spinlock_t semaphore_wake_lock;
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to sleep, while the "waking" variable is
+ * incremented when the "up()" code goes to wake up waiting
+ * processes.
+ *
+ * Notably, the inline "up()" and "down()" functions can
+ * efficiently test if they need to do any extra work (up
+ * needs to do something only if count was negative before
+ * the increment operation.
+ *
+ * waking_non_zero() (from asm/semaphore.h) must execute
+ * atomically.
+ *
+ * When __up() is called, the count was negative before
+ * incrementing it, and we need to wake up somebody.
+ *
+ * This routine adds one to the count of processes that need to
+ * wake up and exit.  ALL waiting processes actually wake up but
+ * only the one that gets to the "waking" field first will gate
+ * through and acquire the semaphore.  The others will go back
+ * to sleep.
+ *
+ * Note that these functions are only called when there is
+ * contention on the lock, and as such all this is the
+ * "non-critical" part of the whole semaphore business. The
+ * critical part is the inline stuff in <asm/semaphore.h>
+ * where we want to avoid any extra jumps and calls.
+ */
+
+void __up(struct semaphore *sem)
+{
+       wake_one_more(sem);
+       wake_up(&sem->wait);
+}
+
+/*
+ * Perform the "down" function.  Return zero for semaphore acquired,
+ * return negative for signalled out of the function.
+ *
+ * If called from __down, the return is ignored and the wait loop is
+ * not interruptible.  This means that a task waiting on a semaphore
+ * using "down()" cannot be killed until someone does an "up()" on
+ * the semaphore.
+ *
+ * If called from __down_interruptible, the return value gets checked
+ * upon return.  If the return value is negative then the task continues
+ * with the negative value in the return register (it can be tested by
+ * the caller).
+ *
+ * Either form may be used in conjunction with "up()".
+ *
+ */
+
+#define DOWN_VAR                               \
+       struct task_struct *tsk = current;      \
+       wait_queue_t wait;                      \
+       init_waitqueue_entry(&wait, tsk);
+
+#define DOWN_HEAD(task_state)                                          \
+                                                                       \
+                                                                       \
+       tsk->state = (task_state);                                      \
+       add_wait_queue(&sem->wait, &wait);                              \
+                                                                       \
+       /*                                                              \
+        * Ok, we're set up.  sem->count is known to be less than zero  \
+        * so we must wait.                                             \
+        *                                                              \
+        * We can let go the lock for purposes of waiting.              \
+        * We re-acquire it after awaking so as to protect              \
+        * all semaphore operations.                                    \
+        *                                                              \
+        * If "up()" is called before we call waking_non_zero() then    \
+        * we will catch it right away.  If it is called later then     \
+        * we will have to go through a wakeup cycle to catch it.       \
+        *                                                              \
+        * Multiple waiters contend for the semaphore lock to see       \
+        * who gets to gate through and who has to wait some more.      \
+        */                                                             \
+       for (;;) {
+
+#define DOWN_TAIL(task_state)                  \
+               tsk->state = (task_state);      \
+       }                                       \
+       tsk->state = TASK_RUNNING;              \
+       remove_wait_queue(&sem->wait, &wait);
+
+void __sched __down(struct semaphore * sem)
+{
+       DOWN_VAR
+       DOWN_HEAD(TASK_UNINTERRUPTIBLE)
+       if (waking_non_zero(sem))
+               break;
+       schedule();
+       DOWN_TAIL(TASK_UNINTERRUPTIBLE)
+}
+
+int __sched __down_interruptible(struct semaphore * sem)
+{
+       int ret = 0;
+       DOWN_VAR
+       DOWN_HEAD(TASK_INTERRUPTIBLE)
+
+       ret = waking_non_zero_interruptible(sem, tsk);
+       if (ret)
+       {
+               if (ret == 1)
+                       /* ret != 0 only if we get interrupted -arca */
+                       ret = 0;
+               break;
+       }
+       schedule();
+       DOWN_TAIL(TASK_INTERRUPTIBLE)
+       return ret;
+}
+
+int __down_trylock(struct semaphore * sem)
+{
+       return waking_non_zero_trylock(sem);
+}
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
new file mode 100644 (file)
index 0000000..1f5bf5d
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * arch/xtensa/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/config.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.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/system.h>
+#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 <xtensa/config/system.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
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+extern struct ide_ops no_ide_ops;
+struct ide_ops *ide_ops;
+#endif
+
+extern struct rtc_ops no_rtc_ops;
+struct rtc_ops *rtc_ops;
+
+#ifdef CONFIG_PC_KEYB
+extern struct kbd_ops no_kbd_ops;
+struct kbd_ops *kbd_ops;
+#endif
+
+#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 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
+
+extern void init_mmu(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(saved_command_line, command_line, COMMAND_LINE_SIZE);
+       saved_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();
+
+#ifdef CONFIG_VT
+# if defined(CONFIG_VGA_CONSOLE)
+       conswitchp = &vga_con;
+# elif defined(CONFIG_DUMMY_CONSOLE)
+       conswitchp = &dummy_con;
+# endif
+#endif
+
+#if 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_RELEASE_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);
+
+       /* Coprocessors */
+#if XCHAL_HAVE_CP
+       seq_printf(f, "coprocessors\t: %d\n", XCHAL_CP_NUM);
+#else
+       seq_printf(f, "coprocessors\t: none\n");
+#endif
+
+       /* {I,D}{RAM,ROM} and XLMI */
+       seq_printf(f,"inst ROMs\t: %d\n"
+                    "inst RAMs\t: %d\n"
+                    "data ROMs\t: %d\n"
+                    "data RAMs\t: %d\n"
+                    "XLMI ports\t: %d\n",
+                    XCHAL_NUM_IROM,
+                    XCHAL_NUM_IRAM,
+                    XCHAL_NUM_DROM,
+                    XCHAL_NUM_DRAM,
+                    XCHAL_NUM_XLMI);
+
+       /* 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);
+
+       /* MMU */
+       seq_printf(f,"ASID bits\t: %d\n"
+                    "ASID invalid\t: %d\n"
+                    "ASID kernel\t: %d\n"
+                    "rings\t\t: %d\n"
+                    "itlb ways\t: %d\n"
+                    "itlb AR ways\t: %d\n"
+                    "dtlb ways\t: %d\n"
+                    "dtlb AR ways\t: %d\n",
+                    XCHAL_MMU_ASID_BITS,
+                    XCHAL_MMU_ASID_INVALID,
+                    XCHAL_MMU_ASID_KERNEL,
+                    XCHAL_MMU_RINGS,
+                    XCHAL_ITLB_WAYS,
+                    XCHAL_ITLB_ARF_WAYS,
+                    XCHAL_DTLB_WAYS,
+                    XCHAL_DTLB_ARF_WAYS);
+
+       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)
+{
+}
+
+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 (file)
index 0000000..df6e1e1
--- /dev/null
@@ -0,0 +1,713 @@
+// TODO coprocessor stuff
+/*
+ *  linux/arch/xtensa/kernel/signal.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  1997-11-28  Modified for POSIX.1b signals by Richard Henderson
+ *
+ *  Joe Taylor <joe@tensilica.com>
+ *  Chris Zankel <chris@zankel.net>
+ *
+ *
+ *
+ */
+
+#include <xtensa/config/core.h>
+#include <xtensa/hal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/personality.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+#define DEBUG_SIG  0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options,
+                         struct rusage * ru);
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+extern struct task_struct *coproc_owners[];
+
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+
+int sys_sigsuspend(struct pt_regs *regs)
+{
+       old_sigset_t mask = (old_sigset_t) regs->areg[3];
+       sigset_t saveset;
+
+       mask &= _BLOCKABLE;
+       spin_lock_irq(&current->sighand->siglock);
+       saveset = current->blocked;
+       siginitset(&current->blocked, mask);
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       regs->areg[2] = -EINTR;
+       while (1) {
+               current->state = TASK_INTERRUPTIBLE;
+               schedule();
+               if (do_signal(regs, &saveset))
+                       return -EINTR;
+       }
+}
+
+asmlinkage int
+sys_rt_sigsuspend(struct pt_regs *regs)
+{
+       sigset_t *unewset = (sigset_t *) regs->areg[4];
+       size_t sigsetsize = (size_t) regs->areg[3];
+       sigset_t saveset, newset;
+       /* XXX: Don't preclude handling different sized sigset_t's.  */
+       if (sigsetsize != sizeof(sigset_t))
+               return -EINVAL;
+
+       if (copy_from_user(&newset, unewset, sizeof(newset)))
+               return -EFAULT;
+       sigdelsetmask(&newset, ~_BLOCKABLE);
+       spin_lock_irq(&current->sighand->siglock);
+       saveset = current->blocked;
+       current->blocked = newset;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       regs->areg[2] = -EINTR;
+       while (1) {
+               current->state = TASK_INTERRUPTIBLE;
+               schedule();
+               if (do_signal(regs, &saveset))
+                       return -EINTR;
+       }
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction *act,
+             struct old_sigaction *oact)
+{
+       struct k_sigaction new_ka, old_ka;
+       int ret;
+
+       if (act) {
+               old_sigset_t mask;
+               if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+                   __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+                   __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+                       return -EFAULT;
+               __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+               __get_user(mask, &act->sa_mask);
+               siginitset(&new_ka.sa.sa_mask, mask);
+       }
+
+       ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+       if (!ret && oact) {
+               if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+                   __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+                   __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+                       return -EFAULT;
+               __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+               __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+       }
+
+       return ret;
+}
+
+asmlinkage int
+sys_sigaltstack(struct pt_regs *regs)
+{
+       const stack_t *uss = (stack_t *) regs->areg[4];
+       stack_t *uoss = (stack_t *) regs->areg[3];
+
+       if (regs->depc > 64)
+               panic ("Double exception sys_sigreturn\n");
+
+
+       return do_sigaltstack(uss, uoss, regs->areg[1]);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+       struct sigcontext sc;
+       struct _cpstate cpstate;
+       unsigned long extramask[_NSIG_WORDS-1];
+       unsigned char retcode[6];
+       unsigned int reserved[4]; /* Reserved area for chaining */
+       unsigned int window[4]; /* Window of 4 registers for initial context */
+};
+
+struct rt_sigframe
+{
+       struct siginfo info;
+       struct ucontext uc;
+       struct _cpstate cpstate;
+       unsigned char retcode[6];
+       unsigned int reserved[4]; /* Reserved area for chaining */
+       unsigned int window[4]; /* Window of 4 registers for initial context */
+};
+
+extern void release_all_cp (struct task_struct *);
+
+
+// FIXME restore_cpextra
+static inline int
+restore_cpextra (struct _cpstate *buf)
+{
+#if 0
+       /* 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. */
+
+       struct task_struct *tsk = current;
+       release_all_cp(tsk);
+       return __copy_from_user(tsk->thread.cpextra, buf, TOTAL_CPEXTRA_SIZE);
+#endif
+       return 0;
+}
+
+/* Note: We don't copy double exception 'tregs', 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
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
+{
+       struct thread_struct *thread;
+       unsigned int err = 0;
+       unsigned long ps;
+       struct _cpstate *buf;
+
+#define COPY(x)        err |= __get_user(regs->x, &sc->sc_##x)
+       COPY(pc);
+       COPY(depc);
+       COPY(wmask);
+       COPY(lbeg);
+       COPY(lend);
+       COPY(lcount);
+       COPY(sar);
+       COPY(windowbase);
+       COPY(windowstart);
+#undef COPY
+
+       /* 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 & ~XCHAL_PS_CALLINC_MASK)
+               | (ps & XCHAL_PS_CALLINC_MASK);
+
+       /* Additional corruption checks */
+
+       if ((regs->windowbase >= (XCHAL_NUM_AREGS/4))
+       || ((regs->windowstart & ~((1<<(XCHAL_NUM_AREGS/4)) - 1)) != 0) )
+               err = 1;
+       if ((regs->lcount > 0)
+       && ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )
+               err = 1;
+
+       /* Restore extended register state.
+        * See struct thread_struct in processor.h.
+        */
+       thread = &current->thread;
+
+       err |= __copy_from_user (regs->areg, sc->sc_areg, XCHAL_NUM_AREGS*4);
+       err |= __get_user(buf, &sc->sc_cpstate);
+       if (buf) {
+               if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
+                       goto badframe;
+               err |= restore_cpextra(buf);
+       }
+
+       regs->syscall = -1;             /* disable syscall checks */
+       return err;
+
+badframe:
+       return 1;
+}
+
+static inline void
+flush_my_cpstate(struct task_struct *tsk)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+
+#if 0  // FIXME
+       for (i = 0; i < XCHAL_CP_NUM; i++) {
+               if (tsk == coproc_owners[i]) {
+                       xthal_validate_cp(i);
+                       xthal_save_cpregs(tsk->thread.cpregs_ptr[i], i);
+
+                       /* Invalidate and "disown" the cp to allow
+                        * callers the chance to reset cp state in the
+                        * task_struct. */
+
+                       xthal_invalidate_cp(i);
+                       coproc_owners[i] = 0;
+               }
+       }
+#endif
+       local_irq_restore(flags);
+}
+
+/* Return codes:
+       0:  nothing saved
+       1:  stuff to save, successful
+       -1:  stuff to save, error happened
+*/
+static int
+save_cpextra (struct _cpstate *buf)
+{
+#if (XCHAL_EXTRA_SA_SIZE == 0) && (XCHAL_CP_NUM == 0)
+       return 0;
+#else
+
+       /* FIXME: If a task has never used a coprocessor, there is
+        * no need to save and restore anything.  Tracking this
+        * information would allow us to optimize this section.
+        * Perhaps we can use current->used_math or (current->flags &
+        * PF_USEDFPU) or define a new field in the thread
+        * structure. */
+
+       /* We flush any live, task-owned cp state to the task_struct,
+        * then copy it all to the sigframe.  Then we clear all
+        * cp/extra state in the task_struct, effectively
+        * clearing/resetting all cp/extra state for the signal
+        * handler (cp-exception handling will load these new values
+        * into the cp/extra registers.)  This step is important for
+        * things like a floating-point cp, where the OS must reset
+        * the FCR to the default rounding mode. */
+
+       int err = 0;
+       struct task_struct *tsk = current;
+
+       flush_my_cpstate(tsk);
+       /* Note that we just copy everything: 'extra' and 'cp' state together.*/
+       err |= __copy_to_user(buf, tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
+       memset(tsk->thread.cp_save, 0, XTENSA_CP_EXTRA_SIZE);
+
+#if (XTENSA_CP_EXTRA_SIZE == 0)
+#error Sanity check on memset above, cpextra_size should not be zero.
+#endif
+
+       return err ? -1 : 1;
+#endif
+}
+
+static int
+setup_sigcontext(struct sigcontext *sc, struct _cpstate *cpstate,
+                struct pt_regs *regs, unsigned long mask)
+{
+       struct thread_struct *thread;
+       int err = 0;
+
+//printk("setup_sigcontext\n");
+#define COPY(x)        err |= __put_user(regs->x, &sc->sc_##x)
+       COPY(pc);
+       COPY(ps);
+       COPY(depc);
+       COPY(wmask);
+       COPY(lbeg);
+       COPY(lend);
+       COPY(lcount);
+       COPY(sar);
+       COPY(windowbase);
+       COPY(windowstart);
+#undef COPY
+
+       /* Save extended register state.
+        * See struct thread_struct in processor.h.
+        */
+       thread = &current->thread;
+       err |= __copy_to_user (sc->sc_areg, regs->areg, XCHAL_NUM_AREGS * 4);
+       err |= save_cpextra(cpstate);
+       err |= __put_user(err ? NULL : cpstate, &sc->sc_cpstate);
+       /* non-iBCS2 extensions.. */
+       err |= __put_user(mask, &sc->oldmask);
+
+       return err;
+}
+
+asmlinkage int sys_sigreturn(struct pt_regs *regs)
+{
+       struct sigframe *frame = (struct sigframe *)regs->areg[1];
+       sigset_t set;
+       if (regs->depc > 64)
+               panic ("Double exception sys_sigreturn\n");
+
+       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+               goto badframe;
+
+       if (__get_user(set.sig[0], &frame->sc.oldmask)
+           || (_NSIG_WORDS > 1
+               && __copy_from_user(&set.sig[1], &frame->extramask,
+                                   sizeof(frame->extramask))))
+               goto badframe;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if (restore_sigcontext(regs, &frame->sc))
+               goto badframe;
+       return regs->areg[2];
+
+badframe:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
+{
+       struct rt_sigframe *frame = (struct rt_sigframe *)regs->areg[1];
+       sigset_t set;
+       stack_t st;
+       int ret;
+       if (regs->depc > 64)
+       {
+               printk("!!!!!!! DEPC !!!!!!!\n");
+               return 0;
+       }
+
+       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+               goto badframe;
+
+       if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+               goto badframe;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+               goto badframe;
+       ret = regs->areg[2];
+
+       if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+               goto badframe;
+       /* It is more difficult to avoid calling this function than to
+          call it and ignore errors.  */
+       do_sigaltstack(&st, NULL, regs->areg[1]);
+
+       return ret;
+
+badframe:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+/*
+ * Determine which stack to use..
+ */
+static inline void *
+get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
+{
+       if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp))
+               sp = current->sas_ss_sp + current->sas_ss_size;
+
+       return (void *)((sp - frame_size) & -16ul);
+}
+
+#define USE_SIGRETURN          0
+#define USE_RT_SIGRETURN       1
+
+static int
+gen_return_code(unsigned char *codemem, unsigned int use_rt_sigreturn)
+{
+       unsigned int retcall;
+       int err = 0;
+
+#if 0
+       /* Ignoring SA_RESTORER for now; it's supposed to be obsolete,
+        * and the xtensa glibc doesn't use it.
+        */
+       if (ka->sa.sa_flags & SA_RESTORER) {
+               regs->pr = (unsigned long) ka->sa.sa_restorer;
+       } else
+#endif /* 0 */
+       {
+
+#if (__NR_sigreturn > 255) || (__NR_rt_sigreturn > 255)
+
+/* 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.
+ */
+
+#error Generating the MOVI instruction below breaks!
+#endif
+
+               retcall = use_rt_sigreturn ? __NR_rt_sigreturn : __NR_sigreturn;
+
+#ifdef __XTENSA_EB__   /* Big Endian version */
+               /* Generate instruction:  MOVI a2, retcall */
+               err |= __put_user(0x22, &codemem[0]);
+               err |= __put_user(0x0a, &codemem[1]);
+               err |= __put_user(retcall, &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, retcall */
+               err |= __put_user(0x22, &codemem[0]);
+               err |= __put_user(0xa0, &codemem[1]);
+               err |= __put_user(retcall, &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)
+               __flush_invalidate_cache_range((unsigned long)codemem, 6UL);
+
+       return err;
+}
+
+static void
+set_thread_state(struct pt_regs *regs, void *stack, unsigned char *retaddr,
+       void *handler, unsigned long arg1, void *arg2, void *arg3)
+{
+       /* Set up registers for signal handler */
+       start_thread(regs, (unsigned long) handler, (unsigned long) stack);
+
+       /* Set up a stack frame for a call4
+        * Note: PS.CALLINC is set to one by start_thread
+        */
+       regs->areg[4] = (((unsigned long) retaddr) & 0x3fffffff) | 0x40000000;
+       regs->areg[6] = arg1;
+       regs->areg[7] = (unsigned long) arg2;
+       regs->areg[8] = (unsigned long) arg3;
+}
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+                       sigset_t *set, struct pt_regs *regs)
+{
+       struct sigframe *frame;
+       int err = 0;
+       int signal;
+
+       frame = get_sigframe(ka, regs->areg[1], sizeof(*frame));
+       if (regs->depc > 64)
+       {
+               printk("!!!!!!! DEPC !!!!!!!\n");
+               return;
+       }
+
+
+       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;
+
+       err |= setup_sigcontext(&frame->sc, &frame->cpstate, regs, set->sig[0]);
+
+       if (_NSIG_WORDS > 1) {
+               err |= __copy_to_user(frame->extramask, &set->sig[1],
+                                     sizeof(frame->extramask));
+       }
+
+       /* Create sys_sigreturn syscall in stack frame */
+       err |= gen_return_code(frame->retcode, USE_SIGRETURN);
+
+       if (err)
+               goto give_sigsegv;
+
+       /* Create signal handler execution context.
+        * Return context not modified until this point.
+        */
+       set_thread_state(regs, frame, frame->retcode,
+               ka->sa.sa_handler, signal, &frame->sc, NULL);
+
+       /* 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 deliver (%s:%d): signal=%d sp=%p pc=%08x\n",
+               current->comm, current->pid, signal, frame, regs->pc);
+#endif
+
+       return;
+
+give_sigsegv:
+       if (sig == SIGSEGV)
+               ka->sa.sa_handler = SIG_DFL;
+       force_sig(SIGSEGV, current);
+}
+
+static void setup_rt_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;
+
+       frame = get_sigframe(ka, regs->areg[1], sizeof(*frame));
+       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;
+
+       err |= copy_siginfo_to_user(&frame->info, info);
+
+       /* Create the ucontext.  */
+       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->uc.uc_mcontext, &frame->cpstate,
+                               regs, set->sig[0]);
+       err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+       /* Create sys_rt_sigreturn syscall in stack frame */
+       err |= gen_return_code(frame->retcode, USE_RT_SIGRETURN);
+
+       if (err)
+               goto give_sigsegv;
+
+       /* Create signal handler execution context.
+        * Return context not modified until this point.
+        */
+       set_thread_state(regs, frame, frame->retcode,
+               ka->sa.sa_handler, signal, &frame->info, &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;
+
+give_sigsegv:
+       if (sig == SIGSEGV)
+               ka->sa.sa_handler = SIG_DFL;
+       force_sig(SIGSEGV, current);
+}
+
+
+
+/*
+ * 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 (!oldset)
+               oldset = &current->blocked;
+
+       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+
+       /* Are we from a system call? */
+       if (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;
+               }
+       }
+
+       if (signr == 0)
+               return 0;               /* no signals delivered */
+
+       /* Whee!  Actually deliver the signal.  */
+
+       /* Set up the stack frame */
+       if (ka.sa.sa_flags & SA_SIGINFO)
+               setup_rt_frame(signr, &ka, &info, oldset, regs);
+       else
+               setup_frame(signr, &ka, oldset, regs);
+
+       if (ka.sa.sa_flags & SA_ONESHOT)
+               ka.sa.sa_handler = SIG_DFL;
+
+       if (!(ka.sa.sa_flags & SA_NODEFER)) {
+               spin_lock_irq(&current->sighand->siglock);
+               sigorsets(&current->blocked, &current->blocked, &ka.sa.sa_mask);
+               sigaddset(&current->blocked, signr);
+               recalc_sigpending();
+               spin_unlock_irq(&current->sighand->siglock);
+       }
+       return 1;
+}
diff --git a/arch/xtensa/kernel/syscalls.c b/arch/xtensa/kernel/syscalls.c
new file mode 100644 (file)
index 0000000..abc8ed6
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * 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
+ *
+ */
+
+#define DEBUG  0
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <linux/unistd.h>
+#include <linux/stringify.h>
+#include <linux/syscalls.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/mman.h>
+#include <asm/shmparam.h>
+#include <asm/page.h>
+#include <asm/ipc.h>
+
+extern void do_syscall_trace(void);
+typedef int (*syscall_t)(void *a0,...);
+extern int (*do_syscalls)(struct pt_regs *regs, syscall_t fun,
+                                    int narg);
+extern syscall_t sys_call_table[];
+extern unsigned char sys_narg_table[];
+
+/*
+ * sys_pipe() is the normal C calling standard for creating a pipe. It's not
+ * the way unix traditional does this, though.
+ */
+
+int sys_pipe(int __user *userfds)
+{
+       int fd[2];
+       int error;
+
+       error = do_pipe(fd);
+       if (!error) {
+               if (copy_to_user(userfds, fd, 2 * sizeof(int)))
+                       error = -EFAULT;
+       }
+       return error;
+}
+
+/*
+ * Common code for old and new mmaps.
+ */
+
+static inline long do_mmap2(unsigned long addr, unsigned long len,
+                           unsigned long prot, unsigned long flags,
+                           unsigned long fd, unsigned long pgoff)
+{
+       int error = -EBADF;
+       struct file * file = NULL;
+
+       flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+       if (!(flags & MAP_ANONYMOUS)) {
+               file = fget(fd);
+               if (!file)
+                       goto out;
+       }
+
+       down_write(&current->mm->mmap_sem);
+       error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+       up_write(&current->mm->mmap_sem);
+
+       if (file)
+               fput(file);
+out:
+       return error;
+}
+
+unsigned long old_mmap(unsigned long addr, size_t len, int prot,
+                      int flags, int fd, off_t offset)
+{
+       return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+}
+
+long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
+              unsigned long flags, unsigned long fd, unsigned long pgoff)
+{
+       return do_mmap2(addr, len, prot, flags, fd, pgoff);
+}
+
+int sys_fork(struct pt_regs *regs)
+{
+       return do_fork(SIGCHLD, regs->areg[1], regs, 0, NULL, NULL);
+}
+
+int sys_vfork(struct pt_regs *regs)
+{
+       return do_fork(CLONE_VFORK|CLONE_VM|SIGCHLD, regs->areg[1],
+                      regs, 0, NULL, NULL);
+}
+
+int sys_clone(struct pt_regs *regs)
+{
+       unsigned long clone_flags;
+       unsigned long newsp;
+       int __user *parent_tidptr, *child_tidptr;
+       clone_flags = regs->areg[4];
+       newsp = regs->areg[3];
+       parent_tidptr = (int __user *)regs->areg[5];
+       child_tidptr = (int __user *)regs->areg[6];
+       if (!newsp)
+               newsp = regs->areg[1];
+       return do_fork(clone_flags,newsp,regs,0,parent_tidptr,child_tidptr);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+
+int sys_execve(struct pt_regs *regs)
+{
+       int error;
+       char * filename;
+
+       filename = getname((char *) (long)regs->areg[5]);
+       error = PTR_ERR(filename);
+       if (IS_ERR(filename))
+               goto out;
+       error = do_execve(filename, (char **) (long)regs->areg[3],
+                         (char **) (long)regs->areg[4], regs);
+       putname(filename);
+
+out:
+       return error;
+}
+
+int sys_uname(struct old_utsname * name)
+{
+       if (name && !copy_to_user(name, &system_utsname, sizeof (*name)))
+               return 0;
+       return -EFAULT;
+}
+
+int sys_olduname(struct oldold_utsname * name)
+{
+       int error;
+
+       if (!name)
+               return -EFAULT;
+       if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+               return -EFAULT;
+
+       error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
+       error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
+       error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
+       error -= __put_user(0,name->release+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
+       error -= __put_user(0,name->version+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
+       error -= __put_user(0,name->machine+__OLD_UTS_LEN);
+
+       return error ? -EFAULT : 0;
+}
+
+
+/*
+ * Build the string table for the builtin "poor man's strace".
+ */
+
+#if DEBUG
+#define SYSCALL(fun, narg) #fun,
+static char *sfnames[] = {
+#include "syscalls.h"
+};
+#undef SYS
+#endif
+
+void system_call (struct pt_regs *regs)
+{
+       syscall_t syscall;
+       unsigned long parm0, parm1, parm2, parm3, parm4, parm5;
+       int nargs, res;
+       unsigned int syscallnr;
+       int ps;
+
+#if DEBUG
+       int i;
+       unsigned long parms[6];
+       char *sysname;
+#endif
+
+       regs->syscall = regs->areg[2];
+
+       do_syscall_trace();
+
+       /* Have to load after syscall_trace because strace
+        * sometimes changes regs->syscall.
+        */
+       syscallnr = regs->syscall;
+
+       parm0 = parm1 = parm2 = parm3 = parm4 = parm5 = 0;
+
+       /* Restore interrupt level to syscall invoker's.
+        * If this were in assembly, we wouldn't disable
+        * interrupts in the first place:
+        */
+       local_save_flags (ps);
+       local_irq_restore((ps & ~XCHAL_PS_INTLEVEL_MASK) |
+                         (regs->ps & XCHAL_PS_INTLEVEL_MASK) );
+
+       if (syscallnr > __NR_Linux_syscalls) {
+               regs->areg[2] = -ENOSYS;
+               return;
+       }
+
+       syscall = sys_call_table[syscallnr];
+       nargs = sys_narg_table[syscallnr];
+
+       if (syscall == NULL) {
+               regs->areg[2] = -ENOSYS;
+               return;
+       }
+
+       /* There shouldn't be more than six arguments in the table! */
+
+       if (nargs > 6)
+               panic("Internal error - too many syscall arguments (%d)!\n",
+                     nargs);
+
+       /* Linux takes system-call arguments in registers.  The ABI
+         * and Xtensa software conventions require the system-call
+         * number in a2.  If an argument exists in a2, we move it to
+         * the next available register.  Note that for improved
+         * efficiency, we do NOT shift all parameters down one
+         * register to maintain the original order.
+        *
+         * At best case (zero arguments), we just write the syscall
+         * number to a2.  At worst case (1 to 6 arguments), we move
+         * the argument in a2 to the next available register, then
+         * write the syscall number to a2.
+        *
+         * For clarity, the following truth table enumerates all
+         * possibilities.
+        *
+         * arguments   syscall number  arg0, arg1, arg2, arg3, arg4, arg5
+         * ---------   --------------  ----------------------------------
+        *      0             a2
+        *      1             a2        a3
+        *      2             a2        a4,   a3
+        *      3             a2        a5,   a3,   a4
+        *      4             a2        a6,   a3,   a4,   a5
+        *      5             a2        a7,   a3,   a4,   a5,   a6
+        *      6             a2        a8,   a3,   a4,   a5,   a6,   a7
+        */
+       if (nargs) {
+               parm0 = regs->areg[nargs+2];
+               parm1 = regs->areg[3];
+               parm2 = regs->areg[4];
+               parm3 = regs->areg[5];
+               parm4 = regs->areg[6];
+               parm5 = regs->areg[7];
+       } else /* nargs == 0 */
+               parm0 = (unsigned long) regs;
+
+#if DEBUG
+       parms[0] = parm0;
+       parms[1] = parm1;
+       parms[2] = parm2;
+       parms[3] = parm3;
+       parms[4] = parm4;
+       parms[5] = parm5;
+
+       sysname = sfnames[syscallnr];
+       if (strncmp(sysname, "sys_", 4) == 0)
+               sysname = sysname + 4;
+
+       printk("\017SYSCALL:I:%x:%d:%s  %s(", regs->pc, current->pid,
+              current->comm, sysname);
+       for (i = 0; i < nargs; i++)
+               printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
+       printk(")\n");
+#endif
+
+       res = syscall((void *)parm0, parm1, parm2, parm3, parm4, parm5);
+
+#if DEBUG
+       printk("\017SYSCALL:O:%d:%s  %s(",current->pid, current->comm, sysname);
+       for (i = 0; i < nargs; i++)
+               printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
+       if (res < 4096)
+               printk(") = %d\n", res);
+       else
+               printk(") = %#x\n", res);
+#endif /* DEBUG */
+
+       regs->areg[2] = res;
+       do_syscall_trace();
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+
+int sys_ipc (uint call, int first, int second,
+                       int third, void __user *ptr, long fifth)
+{
+       int version, ret;
+
+       version = call >> 16; /* hack for backward compatibility */
+       call &= 0xffff;
+       ret = -ENOSYS;
+
+       switch (call) {
+       case SEMOP:
+               ret = sys_semtimedop (first, (struct sembuf __user *)ptr,
+                                    second, NULL);
+               break;
+
+       case SEMTIMEDOP:
+               ret = sys_semtimedop (first, (struct sembuf __user *)ptr,
+                                     second, (const struct timespec *) fifth);
+               break;
+
+       case SEMGET:
+               ret = sys_semget (first, second, third);
+               break;
+
+       case SEMCTL: {
+               union semun fourth;
+
+               if (ptr && !get_user(fourth.__pad, (void *__user *) ptr))
+                       ret = sys_semctl (first, second, third, fourth);
+               break;
+               }
+
+       case MSGSND:
+               ret = sys_msgsnd (first, (struct msgbuf __user*) ptr,
+                                 second, third);
+               break;
+
+       case MSGRCV:
+               switch (version) {
+               case 0: {
+                       struct ipc_kludge tmp;
+
+                       if (ptr && !copy_from_user(&tmp,
+                                          (struct ipc_kludge *) ptr,
+                                          sizeof (tmp)))
+                               ret = sys_msgrcv (first, tmp.msgp, second,
+                                                 tmp.msgtyp, third);
+                       break;
+                       }
+
+               default:
+                       ret = sys_msgrcv (first, (struct msgbuf __user *) ptr,
+                                         second, 0, third);
+                       break;
+               }
+               break;
+
+       case MSGGET:
+               ret = sys_msgget ((key_t) first, second);
+               break;
+
+       case MSGCTL:
+               ret = sys_msgctl (first, second, (struct msqid_ds __user*) ptr);
+               break;
+
+       case SHMAT: {
+               ulong raddr;
+               ret = do_shmat (first, (char __user *) ptr, second, &raddr);
+
+               if (!ret)
+                       ret = put_user (raddr, (ulong __user *) third);
+
+               break;
+               }
+
+       case SHMDT:
+               ret = sys_shmdt ((char __user *)ptr);
+               break;
+
+       case SHMGET:
+               ret = sys_shmget (first, second, third);
+               break;
+
+       case SHMCTL:
+               ret = sys_shmctl (first, second, (struct shmid_ds __user*) ptr);
+               break;
+       }
+       return ret;
+}
+
diff --git a/arch/xtensa/kernel/syscalls.h b/arch/xtensa/kernel/syscalls.h
new file mode 100644 (file)
index 0000000..5b3f75f
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * arch/xtensa/kernel/syscalls.h
+ *
+ * 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, 1996, 1997, 1998 by Ralf Baechle
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Changes by Joe Taylor <joe@tensilica.com>
+ */
+
+/*
+ * This file is being included twice - once to build a list of all
+ * syscalls and once to build a table of how many arguments each syscall
+ * accepts.  Syscalls that receive a pointer to the saved registers are
+ * marked as having zero arguments.
+ *
+ * The binary compatibility calls are in a separate list.
+ *
+ * Entry '0' used to be system_call.  It's removed to disable indirect
+ * system calls for now so user tasks can't recurse.  See mips'
+ * sys_syscall for a comparable example.
+ */
+
+SYSCALL(0, 0)                          /* 00 */
+
+SYSCALL(sys_exit, 1)
+SYSCALL(sys_fork, 0)
+SYSCALL(sys_read, 3)
+SYSCALL(sys_write, 3)
+SYSCALL(sys_open, 3)                   /* 05 */
+SYSCALL(sys_close, 1)
+SYSCALL(sys_waitpid, 3)
+SYSCALL(sys_creat, 2)
+SYSCALL(sys_link, 2)
+SYSCALL(sys_unlink, 1)                 /* 10 */
+SYSCALL(sys_execve, 0)
+SYSCALL(sys_chdir, 1)
+SYSCALL(sys_time, 1)
+SYSCALL(sys_mknod, 3)
+SYSCALL(sys_chmod, 2)                  /* 15 */
+SYSCALL(sys_lchown, 3)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_stat, 2)
+SYSCALL(sys_lseek, 3)
+SYSCALL(sys_getpid, 0)                 /* 20 */
+SYSCALL(sys_mount, 5)
+SYSCALL(sys_oldumount, 1)
+SYSCALL(sys_setuid, 1)
+SYSCALL(sys_getuid, 0)
+SYSCALL(sys_stime, 1)                  /* 25 */
+SYSCALL(sys_ptrace, 4)
+SYSCALL(sys_alarm, 1)
+SYSCALL(sys_fstat, 2)
+SYSCALL(sys_pause, 0)
+SYSCALL(sys_utime, 2)                  /* 30 */
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_access, 2)
+SYSCALL(sys_nice, 1)
+SYSCALL(sys_ni_syscall, 0)             /* 35 */
+SYSCALL(sys_sync, 0)
+SYSCALL(sys_kill, 2)
+SYSCALL(sys_rename, 2)
+SYSCALL(sys_mkdir, 2)
+SYSCALL(sys_rmdir, 1)                  /* 40 */
+SYSCALL(sys_dup, 1)
+SYSCALL(sys_pipe, 1)
+SYSCALL(sys_times, 1)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_brk, 1)                    /* 45 */
+SYSCALL(sys_setgid, 1)
+SYSCALL(sys_getgid, 0)
+SYSCALL(sys_ni_syscall, 0)             /* was signal(2) */
+SYSCALL(sys_geteuid, 0)
+SYSCALL(sys_getegid, 0)                        /* 50 */
+SYSCALL(sys_acct, 1)
+SYSCALL(sys_umount, 2)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_ioctl, 3)
+SYSCALL(sys_fcntl, 3)                  /* 55 */
+SYSCALL(sys_ni_syscall, 2)
+SYSCALL(sys_setpgid, 2)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_olduname, 1)
+SYSCALL(sys_umask, 1)                  /* 60 */
+SYSCALL(sys_chroot, 1)
+SYSCALL(sys_ustat, 2)
+SYSCALL(sys_dup2, 2)
+SYSCALL(sys_getppid, 0)
+SYSCALL(sys_getpgrp, 0)                        /* 65 */
+SYSCALL(sys_setsid, 0)
+SYSCALL(sys_sigaction, 3)
+SYSCALL(sys_sgetmask, 0)
+SYSCALL(sys_ssetmask, 1)
+SYSCALL(sys_setreuid, 2)               /* 70 */
+SYSCALL(sys_setregid, 2)
+SYSCALL(sys_sigsuspend, 0)
+SYSCALL(sys_sigpending, 1)
+SYSCALL(sys_sethostname, 2)
+SYSCALL(sys_setrlimit, 2)              /* 75 */
+SYSCALL(sys_getrlimit, 2)
+SYSCALL(sys_getrusage, 2)
+SYSCALL(sys_gettimeofday, 2)
+SYSCALL(sys_settimeofday, 2)
+SYSCALL(sys_getgroups, 2)              /* 80 */
+SYSCALL(sys_setgroups, 2)
+SYSCALL(sys_ni_syscall, 0)              /* old_select */
+SYSCALL(sys_symlink, 2)
+SYSCALL(sys_lstat, 2)
+SYSCALL(sys_readlink, 3)               /* 85 */
+SYSCALL(sys_uselib, 1)
+SYSCALL(sys_swapon, 2)
+SYSCALL(sys_reboot, 3)
+SYSCALL(old_readdir, 3)
+SYSCALL(old_mmap, 6)                   /* 90 */
+SYSCALL(sys_munmap, 2)
+SYSCALL(sys_truncate, 2)
+SYSCALL(sys_ftruncate, 2)
+SYSCALL(sys_fchmod, 2)
+SYSCALL(sys_fchown, 3)                 /* 95 */
+SYSCALL(sys_getpriority, 2)
+SYSCALL(sys_setpriority, 3)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_statfs, 2)
+SYSCALL(sys_fstatfs, 2)                        /* 100 */
+SYSCALL(sys_ni_syscall, 3)
+SYSCALL(sys_socketcall, 2)
+SYSCALL(sys_syslog, 3)
+SYSCALL(sys_setitimer, 3)
+SYSCALL(sys_getitimer, 2)              /* 105 */
+SYSCALL(sys_newstat, 2)
+SYSCALL(sys_newlstat, 2)
+SYSCALL(sys_newfstat, 2)
+SYSCALL(sys_uname, 1)
+SYSCALL(sys_ni_syscall, 0)             /* 110 */
+SYSCALL(sys_vhangup, 0)
+SYSCALL(sys_ni_syscall, 0)             /* was sys_idle() */
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_wait4, 4)
+SYSCALL(sys_swapoff, 1)                        /* 115 */
+SYSCALL(sys_sysinfo, 1)
+SYSCALL(sys_ipc, 5)                    /* 6 really, but glibc uses only 5) */
+SYSCALL(sys_fsync, 1)
+SYSCALL(sys_sigreturn, 0)
+SYSCALL(sys_clone, 0)                  /* 120 */
+SYSCALL(sys_setdomainname, 2)
+SYSCALL(sys_newuname, 1)
+SYSCALL(sys_ni_syscall, 0)             /* sys_modify_ldt */
+SYSCALL(sys_adjtimex, 1)
+SYSCALL(sys_mprotect, 3)               /* 125 */
+SYSCALL(sys_sigprocmask, 3)
+SYSCALL(sys_ni_syscall, 2)             /* old sys_create_module */
+SYSCALL(sys_init_module, 2)
+SYSCALL(sys_delete_module, 1)
+SYSCALL(sys_ni_syscall, 1)             /* old sys_get_kernel_sysm */   /* 130 */
+SYSCALL(sys_quotactl, 0)
+SYSCALL(sys_getpgid, 1)
+SYSCALL(sys_fchdir, 1)
+SYSCALL(sys_bdflush, 2)
+SYSCALL(sys_sysfs, 3)                  /* 135 */
+SYSCALL(sys_personality, 1)
+SYSCALL(sys_ni_syscall, 0)             /* for afs_syscall */
+SYSCALL(sys_setfsuid, 1)
+SYSCALL(sys_setfsgid, 1)
+SYSCALL(sys_llseek, 5)                 /* 140 */
+SYSCALL(sys_getdents, 3)
+SYSCALL(sys_select, 5)
+SYSCALL(sys_flock, 2)
+SYSCALL(sys_msync, 3)
+SYSCALL(sys_readv, 3)                  /* 145 */
+SYSCALL(sys_writev, 3)
+SYSCALL(sys_ni_syscall, 3)
+SYSCALL(sys_ni_syscall, 3)
+SYSCALL(sys_ni_syscall, 4)             /* handled in fast syscall handler. */
+SYSCALL(sys_ni_syscall, 0)             /* 150 */
+SYSCALL(sys_getsid, 1)
+SYSCALL(sys_fdatasync, 1)
+SYSCALL(sys_sysctl, 1)
+SYSCALL(sys_mlock, 2)
+SYSCALL(sys_munlock, 2)                        /* 155 */
+SYSCALL(sys_mlockall, 1)
+SYSCALL(sys_munlockall, 0)
+SYSCALL(sys_sched_setparam,2)
+SYSCALL(sys_sched_getparam,2)
+SYSCALL(sys_sched_setscheduler,3)      /* 160 */
+SYSCALL(sys_sched_getscheduler,1)
+SYSCALL(sys_sched_yield,0)
+SYSCALL(sys_sched_get_priority_max,1)
+SYSCALL(sys_sched_get_priority_min,1)
+SYSCALL(sys_sched_rr_get_interval,2)   /* 165 */
+SYSCALL(sys_nanosleep,2)
+SYSCALL(sys_mremap,4)
+SYSCALL(sys_accept, 3)
+SYSCALL(sys_bind, 3)
+SYSCALL(sys_connect, 3)                        /* 170 */
+SYSCALL(sys_getpeername, 3)
+SYSCALL(sys_getsockname, 3)
+SYSCALL(sys_getsockopt, 5)
+SYSCALL(sys_listen, 2)
+SYSCALL(sys_recv, 4)                   /* 175 */
+SYSCALL(sys_recvfrom, 6)
+SYSCALL(sys_recvmsg, 3)
+SYSCALL(sys_send, 4)
+SYSCALL(sys_sendmsg, 3)
+SYSCALL(sys_sendto, 6)                 /* 180 */
+SYSCALL(sys_setsockopt, 5)
+SYSCALL(sys_shutdown, 2)
+SYSCALL(sys_socket, 3)
+SYSCALL(sys_socketpair, 4)
+SYSCALL(sys_setresuid, 3)              /* 185 */
+SYSCALL(sys_getresuid, 3)
+SYSCALL(sys_ni_syscall, 5)             /* old sys_query_module */
+SYSCALL(sys_poll, 3)
+SYSCALL(sys_nfsservctl, 3)
+SYSCALL(sys_setresgid, 3)              /* 190 */
+SYSCALL(sys_getresgid, 3)
+SYSCALL(sys_prctl, 5)
+SYSCALL(sys_rt_sigreturn, 0)
+SYSCALL(sys_rt_sigaction, 4)
+SYSCALL(sys_rt_sigprocmask, 4)         /* 195 */
+SYSCALL(sys_rt_sigpending, 2)
+SYSCALL(sys_rt_sigtimedwait, 4)
+SYSCALL(sys_rt_sigqueueinfo, 3)
+SYSCALL(sys_rt_sigsuspend, 0)
+SYSCALL(sys_pread64, 5)                        /* 200 */
+SYSCALL(sys_pwrite64, 5)
+SYSCALL(sys_chown, 3)
+SYSCALL(sys_getcwd, 2)
+SYSCALL(sys_capget, 2)
+SYSCALL(sys_capset, 2)                 /* 205 */
+SYSCALL(sys_sigaltstack, 0)
+SYSCALL(sys_sendfile, 4)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_mmap2, 6)                  /* 210 */
+SYSCALL(sys_truncate64, 2)
+SYSCALL(sys_ftruncate64, 2)
+SYSCALL(sys_stat64, 2)
+SYSCALL(sys_lstat64, 2)
+SYSCALL(sys_fstat64, 2)                        /* 215 */
+SYSCALL(sys_pivot_root, 2)
+SYSCALL(sys_mincore, 3)
+SYSCALL(sys_madvise, 3)
+SYSCALL(sys_getdents64, 3)
+SYSCALL(sys_vfork, 0)                  /* 220 */
diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c
new file mode 100644 (file)
index 0000000..e07287d
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * 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/config.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/timex.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>
+
+
+extern volatile unsigned long wall_jiffies;
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+EXPORT_SYMBOL(jiffies_64);
+
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+EXPORT_SYMBOL(rtc_lock);
+
+
+#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
+unsigned long ccount_per_jiffy;                /* per 1/HZ */
+unsigned long ccount_nsec;             /* nsec per ccount increment */
+#endif
+
+unsigned int last_ccount_stamp;
+static long last_rtc_update = 0;
+
+/*
+ * Scheduler clock - returns current tim in nanosec units.
+ */
+
+unsigned long long sched_clock(void)
+{
+       return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct irqaction timer_irqaction = {
+       .handler =      timer_interrupt,
+       .flags =        SA_INTERRUPT,
+       .name =         "timer",
+};
+
+void __init time_init(void)
+{
+       time_t sec_o, sec_n = 0;
+
+       /* The platform must provide a function to calibrate the processor
+        * speed for the CALIBRATE.
+        */
+
+#if 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
+
+       /* Set time from RTC (if provided) */
+
+       if (platform_get_rtc_time(&sec_o) == 0)
+               while (platform_get_rtc_time(&sec_n))
+                       if (sec_o != sec_n)
+                               break;
+
+       xtime.tv_nsec = 0;
+       last_rtc_update = xtime.tv_sec = sec_n;
+       last_ccount_stamp = get_ccount();
+
+       set_normalized_timespec(&wall_to_monotonic,
+               -xtime.tv_sec, -xtime.tv_nsec);
+
+       /* Initialize the linux timer interrupt. */
+
+       setup_irq(LINUX_TIMER_INT, &timer_irqaction);
+       set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY);
+}
+
+
+int do_settimeofday(struct timespec *tv)
+{
+       time_t wtm_sec, sec = tv->tv_sec;
+       long wtm_nsec, nsec = tv->tv_nsec;
+       unsigned long ccount;
+
+       if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+               return -EINVAL;
+
+       write_seqlock_irq(&xtime_lock);
+
+       /* This is revolting. We need to set "xtime" correctly. However, the
+        * value in this location is the value at the most recent update of
+        * wall time.  Discover what correction gettimeofday() would have
+        * made, and then undo it!
+        */
+       ccount = get_ccount();
+       nsec -= (ccount - last_ccount_stamp) * CCOUNT_NSEC;
+       nsec -= (jiffies - wall_jiffies) * CCOUNT_PER_JIFFY * CCOUNT_NSEC;
+
+       wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+       wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+       set_normalized_timespec(&xtime, sec, nsec);
+       set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+       time_adjust = 0;                /* stop active adjtime() */
+       time_status |= STA_UNSYNC;
+       time_maxerror = NTP_PHASE_LIMIT;
+       time_esterror = NTP_PHASE_LIMIT;
+       write_sequnlock_irq(&xtime_lock);
+       return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+
+void do_gettimeofday(struct timeval *tv)
+{
+       unsigned long flags;
+       unsigned long sec, usec, delta, lost, seq;
+
+       do {
+               seq = read_seqbegin_irqsave(&xtime_lock, flags);
+
+               delta = get_ccount() - last_ccount_stamp;
+               sec = xtime.tv_sec;
+               usec = (xtime.tv_nsec / NSEC_PER_USEC);
+
+               lost = jiffies - wall_jiffies;
+
+       } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
+
+       usec += lost * (1000000UL/HZ) + (delta * CCOUNT_NSEC) / NSEC_PER_USEC;
+       for (; usec >= 1000000; sec++, usec -= 1000000)
+               ;
+
+       tv->tv_sec = sec;
+       tv->tv_usec = usec;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+/*
+ * The timer interrupt is called HZ times per second.
+ */
+
+irqreturn_t timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+
+       unsigned long next;
+
+       next = get_linux_timer();
+
+again:
+       while ((signed long)(get_ccount() - next) > 0) {
+
+               profile_tick(CPU_PROFILING, regs);
+#ifndef CONFIG_SMP
+               update_process_times(user_mode(regs));
+#endif
+
+               write_seqlock(&xtime_lock);
+
+               last_ccount_stamp = next;
+               next += CCOUNT_PER_JIFFY;
+               do_timer (regs); /* Linux handler in kernel/timer.c */
+
+               if ((time_status & STA_UNSYNC) == 0 &&
+                   xtime.tv_sec - last_rtc_update >= 659 &&
+                   abs((xtime.tv_nsec/1000)-(1000000-1000000/HZ))<5000000/HZ &&
+                   jiffies - wall_jiffies == 1) {
+
+                       if (platform_set_rtc_time(xtime.tv_sec+1) == 0)
+                               last_rtc_update = xtime.tv_sec+1;
+                       else
+                               /* Do it again in 60 s */
+                               last_rtc_update += 60;
+               }
+               write_sequnlock(&xtime_lock);
+       }
+
+       /* NOTE: writing CCOMPAREn clears the interrupt.  */
+
+       set_linux_timer (next);
+
+       /* Make sure we didn't miss any tick... */
+
+       if ((signed long)(get_ccount() - next) > 0)
+               goto again;
+
+       /* Allow platform to do something usefull (Wdog). */
+
+       platform_heartbeat();
+
+       return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_GENERIC_CALIBRATE_DELAY
+void __devinit 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 (file)
index 0000000..804246e
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * 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 <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)                                                 \
+{ XCHAL_EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor }
+
+typedef struct {
+       int cause;
+       int fast;
+       void* handler;
+} dispatch_init_table_t;
+
+dispatch_init_table_t __init dispatch_init_table[] = {
+
+{ XCHAL_EXCCAUSE_ILLEGAL_INSTRUCTION,  0,         do_illegal_instruction},
+{ XCHAL_EXCCAUSE_SYSTEM_CALL,          KRNL,      fast_syscall_kernel },
+{ XCHAL_EXCCAUSE_SYSTEM_CALL,          USER,      fast_syscall_user },
+{ XCHAL_EXCCAUSE_SYSTEM_CALL,          0,         system_call },
+/* XCHAL_EXCCAUSE_INSTRUCTION_FETCH unhandled */
+/* XCHAL_EXCCAUSE_LOAD_STORE_ERROR unhandled*/
+{ XCHAL_EXCCAUSE_LEVEL1_INTERRUPT,     0,         do_interrupt },
+{ XCHAL_EXCCAUSE_ALLOCA,               USER|KRNL, fast_alloca },
+/* XCHAL_EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */
+/* XCHAL_EXCCAUSE_PRIVILEGED unhandled */
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+#ifdef CONFIG_UNALIGNED_USER
+{ XCHAL_EXCCAUSE_UNALIGNED,            USER,      fast_unaligned },
+#else
+{ XCHAL_EXCCAUSE_UNALIGNED,            0,         do_unaligned_user },
+#endif
+{ XCHAL_EXCCAUSE_UNALIGNED,            KRNL,      fast_unaligned },
+#endif
+{ XCHAL_EXCCAUSE_ITLB_MISS,            0,         do_page_fault },
+{ XCHAL_EXCCAUSE_ITLB_MISS,            USER|KRNL, fast_second_level_miss},
+{ XCHAL_EXCCAUSE_ITLB_MULTIHIT,                0,         do_multihit },
+{ XCHAL_EXCCAUSE_ITLB_PRIVILEGE,       0,         do_page_fault },
+/* XCHAL_EXCCAUSE_SIZE_RESTRICTION unhandled */
+{ XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE,        0,         do_page_fault },
+{ XCHAL_EXCCAUSE_DTLB_MISS,            USER|KRNL, fast_second_level_miss},
+{ XCHAL_EXCCAUSE_DTLB_MISS,            0,         do_page_fault },
+{ XCHAL_EXCCAUSE_DTLB_MULTIHIT,                0,         do_multihit },
+{ XCHAL_EXCCAUSE_DTLB_PRIVILEGE,       0,         do_page_fault },
+/* XCHAL_EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */
+{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE,        USER|KRNL, fast_store_prohibited },
+{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE,        0,         do_page_fault },
+{ XCHAL_EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0,         do_page_fault },
+/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */
+#if (XCHAL_CP_MASK & 1)
+COPROCESSOR(0),
+#endif
+#if (XCHAL_CP_MASK & 2)
+COPROCESSOR(1),
+#endif
+#if (XCHAL_CP_MASK & 4)
+COPROCESSOR(2),
+#endif
+#if (XCHAL_CP_MASK & 8)
+COPROCESSOR(3),
+#endif
+#if (XCHAL_CP_MASK & 16)
+COPROCESSOR(4),
+#endif
+#if (XCHAL_CP_MASK & 32)
+COPROCESSOR(5),
+#endif
+#if (XCHAL_CP_MASK & 64)
+COPROCESSOR(6),
+#endif
+#if (XCHAL_CP_MASK & 128)
+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, current->pid, 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, current->pid, 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, current->pid, 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 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 < 32; i++) {
+               if (wmask & (1 << (i / 4)))
+                       break;
+               if ((i % 8) == 0)
+                       printk ("\n" KERN_INFO "a%02d: ", i);
+               printk("%08lx ", regs->areg[i]);
+       }
+       printk("\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);
+}
+
+void show_trace(struct task_struct *task, unsigned long *sp)
+{
+       unsigned long a0, a1, pc;
+       unsigned long sp_start, sp_end;
+
+       a1 = (unsigned long)sp;
+
+       if (a1 == 0)
+               __asm__ __volatile__ ("mov %0, a1\n" : "=a"(a1));
+
+
+       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 == 0)
+               __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
+
+       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?' ':'>'));
+       }
+}
+
+spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
+
+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]);
+
+       spin_unlock_irq(&die_lock);
+
+       if (in_interrupt())
+               panic("Fatal exception in interrupt");
+
+       if (panic_on_oops) {
+               printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(5 * HZ);
+               panic("Fatal exception");
+       }
+       do_exit(err);
+}
+
+
diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S
new file mode 100644 (file)
index 0000000..81808f0
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * 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/ptrace.h>
+#include <asm/current.h>
+#include <asm/offsets.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+
+
+/*
+ * 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_SHIFT, .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, XSHAL_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, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
+       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!
+        */
+
+       .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, XCHAL_EXCCAUSE_ITLB_MISS, 1f
+       addi    a3, a3, -XCHAL_EXCCAUSE_DTLB_MISS
+       bnez    a3, .Lunrecoverable
+1:     movi    a3, fast_second_level_miss_double_kernel
+       jx      a3
+
+       /* 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 (file)
index 0000000..476b2b5
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * 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 <linux/config.h>
+#define _NOCLANGUAGE
+#include <xtensa/config/core.h>
+#include <xtensa/config/system.h>
+OUTPUT_ARCH(xtensa)
+ENTRY(_start)
+
+#if XCHAL_MEMORY_ORDER == XTHAL_BIGENDIAN
+jiffies = jiffies_64 + 4;
+#else
+jiffies = jiffies_64;
+#endif
+
+#define KERNELOFFSET 0x1000
+
+/* 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
+{
+  . = XCHAL_KSEG_CACHED_VADDR + KERNELOFFSET;
+  /* .text section */
+
+  _text = .;
+  _stext = .;
+  _ftext = .;
+
+  .text :
+  {
+    /* The .head.text section must be the first section! */
+    *(.head.text)
+    *(.literal .text)
+    *(.srom.text)
+    VMLINUX_SYMBOL(__sched_text_start) = .;
+    *(.sched.text.literal .sched.text)
+    VMLINUX_SYMBOL(__sched_text_end) = .;
+    VMLINUX_SYMBOL(__lock_text_start) = .;
+    *(.spinlock.text.literal .spinlock.text)
+    VMLINUX_SYMBOL(__lock_text_end) = .;
+
+  }
+  _etext = .;
+
+  . = ALIGN(16);
+
+  RODATA
+
+  /*  Relocation table */
+
+  . = ALIGN(16);
+  __boot_reloc_table_start = ABSOLUTE(.);
+
+  __relocate : {
+
+    RELOCATE_ENTRY(_WindowVectors_text,
+                  .WindowVectors.text);
+#if 0
+    RELOCATE_ENTRY(_KernelExceptionVector_literal,
+                  .KernelExceptionVector.literal);
+#endif
+    RELOCATE_ENTRY(_KernelExceptionVector_text,
+                  .KernelExceptionVector.text);
+#if 0
+    RELOCATE_ENTRY(_UserExceptionVector_literal,
+                  .UserExceptionVector.literal);
+#endif
+    RELOCATE_ENTRY(_UserExceptionVector_text,
+                  .UserExceptionVector.text);
+    RELOCATE_ENTRY(_DoubleExceptionVector_literal,
+                  .DoubleExceptionVector.literal);
+    RELOCATE_ENTRY(_DoubleExceptionVector_text,
+                  .DoubleExceptionVector.text);
+  }
+  __boot_reloc_table_end = ABSOLUTE(.) ;
+
+  .fixup   : { *(.fixup) }
+
+  . = ALIGN(16);
+
+  __ex_table : {
+    __start___ex_table = .;
+    *(__ex_table)
+    __stop___ex_table = .;
+  }
+
+  /* Data section */
+
+  . = ALIGN(XCHAL_ICACHE_LINESIZE);
+  _fdata = .;
+  .data :
+  {
+    *(.data) CONSTRUCTORS
+    . = ALIGN(XCHAL_ICACHE_LINESIZE);
+    *(.data.cacheline_aligned)
+  }
+
+  _edata = .;
+
+  /* The initial task */
+  . = ALIGN(8192);
+  .data.init_task : { *(.data.init_task) }
+
+  /* Initialization code and data: */
+
+  . = ALIGN(1<<XCHAL_MMU_MIN_PTE_PAGE_SIZE);
+  __init_begin = .;
+  .init.text : {
+       _sinittext = .;
+       *(.init.text.literal) *(.init.text)
+       _einittext = .;
+  }
+
+  .init.data :
+  {
+    *(.init.data)
+    . = ALIGN(0x4);
+    __tagtable_begin = .;
+    *(.taglist)
+    __tagtable_end = .;
+  }
+
+  . = ALIGN(XCHAL_ICACHE_LINESIZE);
+
+  __setup_start = .;
+  .init.setup : { *(.init.setup) }
+  __setup_end = .;
+
+  __initcall_start = .;
+  .initcall.init : {
+       *(.initcall1.init)
+       *(.initcall2.init)
+       *(.initcall3.init)
+       *(.initcall4.init)
+       *(.initcall5.init)
+       *(.initcall6.init)
+       *(.initcall7.init)
+  }
+  __initcall_end = .;
+
+  __con_initcall_start = .;
+  .con_initcall.init : { *(.con_initcall.init) }
+  __con_initcall_end = .;
+
+  SECURITY_INIT
+
+  . = ALIGN(4);
+
+  __start___ftr_fixup = .;
+  __ftr_fixup : { *(__ftr_fixup) }
+  __stop___ftr_fixup = .;
+
+  . = ALIGN(32);
+  __per_cpu_start = .;
+  .data.percpu  : { *(.data.percpu) }
+  __per_cpu_end = .;
+
+  . = ALIGN(4096);
+  __initramfs_start =.;
+  .init.ramfs : { *(.init.ramfs) }
+  __initramfs_end = .;
+
+  /* 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_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL) - 4,
+                 SIZEOF(.WindowVectors.text),
+                 .WindowVectors.text)
+  SECTION_VECTOR (_DebugInterruptVector_text,
+                 .DebugInterruptVector.text,
+                 XCHAL_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL),
+                 4,
+                 .DebugInterruptVector.literal)
+  SECTION_VECTOR (_KernelExceptionVector_literal,
+                 .KernelExceptionVector.literal,
+                 XCHAL_KERNELEXC_VECTOR_VADDR - 4,
+                 SIZEOF(.DebugInterruptVector.text),
+                 .DebugInterruptVector.text)
+  SECTION_VECTOR (_KernelExceptionVector_text,
+                 .KernelExceptionVector.text,
+                 XCHAL_KERNELEXC_VECTOR_VADDR,
+                 4,
+                 .KernelExceptionVector.literal)
+  SECTION_VECTOR (_UserExceptionVector_literal,
+                 .UserExceptionVector.literal,
+                 XCHAL_USEREXC_VECTOR_VADDR - 4,
+                 SIZEOF(.KernelExceptionVector.text),
+                 .KernelExceptionVector.text)
+  SECTION_VECTOR (_UserExceptionVector_text,
+                 .UserExceptionVector.text,
+                 XCHAL_USEREXC_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(1<<XCHAL_MMU_MIN_PTE_PAGE_SIZE);
+
+  __init_end = .;
+
+  . = ALIGN(8192);
+
+  /* BSS section */
+  _bss_start = .;
+  .sbss : { *(.sbss) *(.scommon) }
+  .bss : { *(COMMON) *(.bss) }
+  _bss_end = .;
+  _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)
+  }
+
+
+  /* Sections to be discarded */
+  /DISCARD/ :
+  {
+        *(.text.exit)
+       *(.text.exit.literal)
+        *(.data.exit)
+        *(.exitcall.exit)
+  }
+
+
+  .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*)
+  }
+}
diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c
new file mode 100644 (file)
index 0000000..efae56a
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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/config.h>
+#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 <linux/pci.h>
+#include <linux/ide.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>
+#include <asm/semaphore.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(memcmp);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(strstr);
+
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+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);
+
+/*
+ * Semaphore operations
+ */
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_interruptible);
+EXPORT_SYMBOL(__down_trylock);
+EXPORT_SYMBOL(__up);
+
+#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(get_wchan);
+
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);