summaryrefslogtreecommitdiffstats
path: root/arch/xtensa
diff options
context:
space:
mode:
authorChris Zankel <chris@zankel.net>2008-02-12 13:17:07 -0800
committerChris Zankel <chris@zankel.net>2008-02-13 17:41:43 -0800
commitc658eac628aa8df040dfe614556d95e6da3a9ffb (patch)
treee2211e1d5c894c29e92d4c744f504b38410efe41 /arch/xtensa
parent71d28e6c285548106f551fde13ca6d589433d843 (diff)
[XTENSA] Add support for configurable registers and coprocessors
The Xtensa architecture allows to define custom instructions and registers. Registers that are bound to a coprocessor are only accessible if the corresponding enable bit is set, which allows to implement a 'lazy' context switch mechanism. Other registers needs to be saved and restore at the time of the context switch or during interrupt handling. This patch adds support for these additional states: - save and restore registers that are used by the compiler upon interrupt entry and exit. - context switch additional registers unbound to any coprocessor - 'lazy' context switch of registers bound to a coprocessor - ptrace interface to provide access to additional registers - update configuration files in include/asm-xtensa/variant-fsf Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa')
-rw-r--r--arch/xtensa/kernel/asm-offsets.c16
-rw-r--r--arch/xtensa/kernel/coprocessor.S443
-rw-r--r--arch/xtensa/kernel/entry.S295
-rw-r--r--arch/xtensa/kernel/process.c261
-rw-r--r--arch/xtensa/kernel/ptrace.c347
-rw-r--r--arch/xtensa/kernel/signal.c65
-rw-r--r--arch/xtensa/kernel/traps.c16
7 files changed, 713 insertions, 730 deletions
diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c
index 5d9ef515ca1e..ef63adadf7f4 100644
--- a/arch/xtensa/kernel/asm-offsets.c
+++ b/arch/xtensa/kernel/asm-offsets.c
@@ -63,6 +63,8 @@ int main(void)
DEFINE(PT_SIZE, sizeof(struct pt_regs));
DEFINE(PT_AREG_END, offsetof (struct pt_regs, areg[XCHAL_NUM_AREGS]));
DEFINE(PT_USER_SIZE, offsetof(struct pt_regs, areg[XCHAL_NUM_AREGS]));
+ DEFINE(PT_XTREGS_OPT, offsetof(struct pt_regs, xtregs_opt));
+ DEFINE(XTREGS_OPT_SIZE, sizeof(xtregs_opt_t));
/* struct task_struct */
DEFINE(TASK_PTRACE, offsetof (struct task_struct, ptrace));
@@ -76,7 +78,19 @@ int main(void)
/* 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_CPENABLE, offsetof (struct thread_info, cpenable));
+#if XTENSA_HAVE_COPROCESSORS
+ DEFINE(THREAD_XTREGS_CP0, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP1, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP2, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP3, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP4, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP5, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP6, offsetof (struct thread_info, xtregs_cp));
+ DEFINE(THREAD_XTREGS_CP7, offsetof (struct thread_info, xtregs_cp));
+#endif
+ DEFINE(THREAD_XTREGS_USER, offsetof (struct thread_info, xtregs_user));
+ DEFINE(XTREGS_USER_SIZE, sizeof(xtregs_user_t));
DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds));
/* struct mm_struct */
diff --git a/arch/xtensa/kernel/coprocessor.S b/arch/xtensa/kernel/coprocessor.S
index 01bcb9fcfcbd..2bc1e145c0a4 100644
--- a/arch/xtensa/kernel/coprocessor.S
+++ b/arch/xtensa/kernel/coprocessor.S
@@ -8,193 +8,328 @@
* 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>
+ * Copyright (C) 2003 - 2007 Tensilica Inc.
*/
-/*
- * 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/linkage.h>
+#include <asm/asm-offsets.h>
#include <asm/processor.h>
+#include <asm/coprocessor.h>
+#include <asm/thread_info.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+#include <asm/tlbflush.h>
-#if XCHAL_HAVE_CP
+/*
+ * 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
+ */
-#define CP_LAST ((XCHAL_CP_MAX - 1) * COPROCESSOR_INFO_SIZE)
+/* IO protection is currently unsupported. */
-ENTRY(release_coprocessors)
+ENTRY(fast_io_protect)
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
- 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
+#if XTENSA_HAVE_COPROCESSORS
-1: /* Check if task is coprocessor owner of coprocessor[i]. */
+/*
+ * Macros for lazy context switch.
+ */
- l32i a5, a4, COPROCESSOR_INFO_OWNER
- srli a3, a3, 1
- beqz a3, 1f
- addi a4, a4, -8
- beq a2, a5, 1b
+#define SAVE_CP_REGS(x) \
+ .align 4; \
+ .Lsave_cp_regs_cp##x: \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ xchal_cp##x##_store a2 a4 a5 a6 a7; \
+ .endif; \
+ jx a0
- /* Found an entry: Clear entry CPENABLE bit to disable CP. */
+#define SAVE_CP_REGS_TAB(x) \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ .long .Lsave_cp_regs_cp##x - .Lsave_cp_regs_jump_table; \
+ .else; \
+ .long 0; \
+ .endif; \
+ .long THREAD_XTREGS_CP##x
- rsr a5, CPENABLE
- s32i a6, a4, COPROCESSOR_INFO_OWNER
- xor a5, a3, a5
- wsr a5, CPENABLE
- bnez a3, 1b
+#define LOAD_CP_REGS(x) \
+ .align 4; \
+ .Lload_cp_regs_cp##x: \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ xchal_cp##x##_load a2 a4 a5 a6 a7; \
+ .endif; \
+ jx a0
-1: wsr a7, PS
- rsync
- retw
+#define LOAD_CP_REGS_TAB(x) \
+ .if XTENSA_HAVE_COPROCESSOR(x); \
+ .long .Lload_cp_regs_cp##x - .Lload_cp_regs_jump_table; \
+ .else; \
+ .long 0; \
+ .endif; \
+ .long THREAD_XTREGS_CP##x
+ SAVE_CP_REGS(0)
+ SAVE_CP_REGS(1)
+ SAVE_CP_REGS(2)
+ SAVE_CP_REGS(3)
+ SAVE_CP_REGS(4)
+ SAVE_CP_REGS(5)
+ SAVE_CP_REGS(6)
+ SAVE_CP_REGS(7)
-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
+ LOAD_CP_REGS(0)
+ LOAD_CP_REGS(1)
+ LOAD_CP_REGS(2)
+ LOAD_CP_REGS(3)
+ LOAD_CP_REGS(4)
+ LOAD_CP_REGS(5)
+ LOAD_CP_REGS(6)
+ LOAD_CP_REGS(7)
-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
+ .align 4
+.Lsave_cp_regs_jump_table:
+ SAVE_CP_REGS_TAB(0)
+ SAVE_CP_REGS_TAB(1)
+ SAVE_CP_REGS_TAB(2)
+ SAVE_CP_REGS_TAB(3)
+ SAVE_CP_REGS_TAB(4)
+ SAVE_CP_REGS_TAB(5)
+ SAVE_CP_REGS_TAB(6)
+ SAVE_CP_REGS_TAB(7)
+.Lload_cp_regs_jump_table:
+ LOAD_CP_REGS_TAB(0)
+ LOAD_CP_REGS_TAB(1)
+ LOAD_CP_REGS_TAB(2)
+ LOAD_CP_REGS_TAB(3)
+ LOAD_CP_REGS_TAB(4)
+ LOAD_CP_REGS_TAB(5)
+ LOAD_CP_REGS_TAB(6)
+ LOAD_CP_REGS_TAB(7)
-ENTRY(save_coprocessor_extra)
- entry sp, 16
- xchal_extra_store_funcbody
- retw
+/*
+ * coprocessor_save(buffer, index)
+ * a2 a3
+ * coprocessor_load(buffer, index)
+ * a2 a3
+ *
+ * Save or load coprocessor registers for coprocessor 'index'.
+ * The register values are saved to or loaded from them 'buffer' address.
+ *
+ * Note that these functions don't update the coprocessor_owner information!
+ *
+ */
-ENTRY(restore_coprocessor_extra)
- entry sp, 16
- xchal_extra_load_funcbody
+ENTRY(coprocessor_save)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lsave_cp_regs_jump_table
+ addx8 a3, a3, a0
+ l32i a3, a3, 0
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
retw
-ENTRY(save_coprocessor_registers)
- entry sp, 16
- xchal_cpi_store_funcbody
+ENTRY(coprocessor_load)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lload_cp_regs_jump_table
+ addx4 a3, a3, a0
+ l32i a3, a3, 0
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
retw
-ENTRY(restore_coprocessor_registers)
- entry sp, 16
- xchal_cpi_load_funcbody
+/*
+ * coprocessor_flush(struct task_info*, index)
+ * a2 a3
+ * coprocessor_restore(struct task_info*, index)
+ * a2 a3
+ *
+ * Save or load coprocessor registers for coprocessor 'index'.
+ * The register values are saved to or loaded from the coprocessor area
+ * inside the task_info structure.
+ *
+ * Note that these functions don't update the coprocessor_owner information!
+ *
+ */
+
+
+ENTRY(coprocessor_flush)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lsave_cp_regs_jump_table
+ addx8 a3, a3, a0
+ l32i a4, a3, 4
+ l32i a3, a3, 0
+ add a2, a2, a4
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
retw
+ENTRY(coprocessor_restore)
+ entry a1, 32
+ s32i a0, a1, 0
+ movi a0, .Lload_cp_regs_jump_table
+ addx4 a3, a3, a0
+ l32i a4, a3, 4
+ l32i a3, a3, 0
+ add a2, a2, a4
+ beqz a3, 1f
+ add a0, a0, a3
+ callx0 a0
+1: l32i a0, a1, 0
+ retw
/*
- * 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:
+ * Entry condition:
*
- * 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)
+ * 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
*
- * For this table, we only care about the <libdbnum>, <offset> and <size>
- * fields.
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
*/
-/* 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 */
+ENTRY(fast_coprocessor_double)
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
-/*
- * 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:
-#endif
+ENTRY(fast_coprocessor)
+
+ /* Save remaining registers a1-a3 and SAR */
+
+ xsr a3, EXCSAVE_1
+ s32i a3, a2, PT_AREG3
+ rsr a3, SAR
+ s32i a1, a2, PT_AREG1
+ s32i a3, a2, PT_SAR
+ mov a1, a2
+ rsr a2, DEPC
+ s32i a2, a1, PT_AREG2
+
+ /*
+ * The hal macros require up to 4 temporary registers. We use a3..a6.
+ */
+
+ s32i a4, a1, PT_AREG4
+ s32i a5, a1, PT_AREG5
+ s32i a6, a1, PT_AREG6
+
+ /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
+
+ rsr a3, EXCCAUSE
+ addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
+
+ /* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/
+
+ ssl a3 # SAR: 32 - coprocessor_number
+ movi a2, 1
+ rsr a0, CPENABLE
+ sll a2, a2
+ or a0, a0, a2
+ wsr a0, CPENABLE
+ rsync
+
+ /* Retrieve previous owner. (a3 still holds CP number) */
+
+ movi a0, coprocessor_owner # list of owners
+ addx4 a0, a3, a0 # entry for CP
+ l32i a4, a0, 0
+
+ beqz a4, 1f # skip 'save' if no previous owner
+
+ /* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
+
+ l32i a5, a4, THREAD_CPENABLE
+ xor a5, a5, a2 # (1 << cp-id) still in a2
+ s32i a5, a4, THREAD_CPENABLE
+
+ /*
+ * Get context save area and 'call' save routine.
+ * (a4 still holds previous owner (thread_info), a3 CP number)
+ */
+
+ movi a5, .Lsave_cp_regs_jump_table
+ movi a0, 2f # a0: 'return' address
+ addx8 a3, a3, a5 # a3: coprocessor number
+ l32i a2, a3, 4 # a2: xtregs offset
+ l32i a3, a3, 0 # a3: jump offset
+ add a2, a2, a4
+ add a4, a3, a5 # a4: address of save routine
+ jx a4
+
+ /* Note that only a0 and a1 were preserved. */
+
+2: rsr a3, EXCCAUSE
+ addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
+ movi a0, coprocessor_owner
+ addx4 a0, a3, a0
+
+ /* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */
+
+1: GET_THREAD_INFO (a4, a1)
+ s32i a4, a0, 0
+
+ /* Get context save area and 'call' load routine. */
+
+ movi a5, .Lload_cp_regs_jump_table
+ movi a0, 1f
+ addx8 a3, a3, a5
+ l32i a2, a3, 4 # a2: xtregs offset
+ l32i a3, a3, 0 # a3: jump offset
+ add a2, a2, a4
+ add a4, a3, a5
+ jx a4
+
+ /* Restore all registers and return from exception handler. */
+
+1: l32i a6, a1, PT_AREG6
+ l32i a5, a1, PT_AREG5
+ l32i a4, a1, PT_AREG4
+
+ l32i a0, a1, PT_SAR
+ l32i a3, a1, PT_AREG3
+ l32i a2, a1, PT_AREG2
+ wsr a0, SAR
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+
+ rfe
+
+ .data
+ENTRY(coprocessor_owner)
+ .fill XCHAL_CP_MAX, 4, 0
+
+#endif /* XTENSA_HAVE_COPROCESSORS */
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index b51ddb0dcf28..24770b6a5e4c 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -25,6 +25,7 @@
#include <asm/page.h>
#include <asm/signal.h>
#include <asm/tlbflush.h>
+#include <asm/variant/tie-asm.h>
/* Unimplemented features. */
@@ -213,19 +214,7 @@ _user_exception:
/* 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. */
+2: /* Now, jump to the common exception handler. */
j common_exception
@@ -381,6 +370,10 @@ common_exception:
s32i a2, a1, PT_LBEG
s32i a3, a1, PT_LEND
+ /* Save optional registers. */
+
+ save_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT
+
/* Go to second-level dispatcher. Set up parameters to pass to the
* exception handler and call the exception handler.
*/
@@ -452,22 +445,6 @@ common_exception_return:
4: /* a2 holds GET_CURRENT(a2,a1) */
-#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
@@ -614,6 +591,12 @@ kernel_exception_exit:
common_exception_exit:
+ /* Restore optional registers. */
+
+ load_xtregs_opt a1 a3 a4 a5 a6 a7 PT_XTREGS_OPT
+
+ /* Restore address registers. */
+
_bbsi.l a2, 1, 1f
l32i a4, a1, PT_AREG4
l32i a5, a1, PT_AREG5
@@ -1146,7 +1129,6 @@ CATCH
* 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)
@@ -1162,29 +1144,31 @@ ENTRY(fast_syscall_spill_registers)
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
+ s32i a4, a2, PT_AREG4
+ s32i a0, a2, PT_AREG5 # store SAR to PT_AREG5
/* The spill routine might clobber a7, a11, and a15. */
- s32i a7, a2, PT_AREG5
- s32i a11, a2, PT_AREG6
- s32i a15, a2, PT_AREG7
+ s32i a7, a2, PT_AREG7
+ s32i a11, a2, PT_AREG11
+ s32i a15, a2, PT_AREG15
- call0 _spill_registers # destroys a3, DEPC, and SAR
+ call0 _spill_registers # destroys a3, a4, and SAR
/* Advance PC, restore registers and SAR, and return from exception. */
- l32i a3, a2, PT_AREG4
+ l32i a3, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
l32i a0, a2, PT_AREG0
wsr a3, SAR
l32i a3, a2, PT_AREG3
/* Restore clobbered registers. */
- l32i a7, a2, PT_AREG5
- l32i a11, a2, PT_AREG6
- l32i a15, a2, PT_AREG7
+ l32i a7, a2, PT_AREG7
+ l32i a11, a2, PT_AREG11
+ l32i a15, a2, PT_AREG15
movi a2, 0
rfe
@@ -1257,9 +1241,9 @@ fast_syscall_spill_registers_fixup:
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
+ 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:
@@ -1297,7 +1281,7 @@ fast_syscall_spill_registers_fixup_return:
* This is not a real function. The following conditions must be met:
*
* - must be called with call0.
- * - uses DEPC, a3 and SAR.
+ * - uses a3, a4 and SAR.
* - the last 'valid' register of each frame are clobbered.
* - the caller must have registered a fixup handler
* (or be inside a critical section)
@@ -1309,41 +1293,39 @@ 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.
+ * Rotate ws right so that a4 = yyxxxwww1.
*/
- wsr a2, DEPC # preserve a2
- rsr a2, WINDOWBASE
+ rsr a4, WINDOWBASE
rsr a3, WINDOWSTART # a3 = xxxwww1yy
- ssr a2 # holds WB
- slli a2, a3, WSBITS
- or a3, a3, a2 # a3 = xxxwww1yyxxxwww1yy
+ ssr a4 # holds WB
+ slli a4, a3, WSBITS
+ or a3, a3, a4 # a3 = xxxwww1yyxxxwww1yy
srl a3, a3 # a3 = 00xxxwww1yyxxxwww1
/* We are done if there are no more than the current register frame. */
extui a3, a3, 1, WSBITS-1 # a3 = 0yyxxxwww
- movi a2, (1 << (WSBITS-1))
+ movi a4, (1 << (WSBITS-1))
_beqz a3, .Lnospill # only one active frame? jump
/* We want 1 at the top, so that we return to the current windowbase */
- or a3, a3, a2 # 1yyxxxwww
+ or a3, a3, a4 # 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
+ neg a4, a3
+ and a3, a4, a3 # first bit set from right: 000010000
- ffs_ws a2, a3 # a2: shifts to skip empty frames
+ ffs_ws a4, a3 # a4: 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.
+ sub a4, a3, a4 # WSBITS-a4:number of 0-bits from right
+ ssr a4 # save in SAR for later.
rsr a3, WINDOWBASE
- add a3, a3, a2
- rsr a2, DEPC # restore a2
+ add a3, a3, a4
wsr a3, WINDOWBASE
rsync
@@ -1373,7 +1355,6 @@ ENTRY(_spill_registers)
j .Lc12c
.Lnospill:
- rsr a2, DEPC
ret
.Lloop: _bbsi.l a3, 1, .Lc4
@@ -1810,154 +1791,6 @@ ENTRY(fast_store_prohibited)
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 */
-
/*
* System Calls.
*
@@ -2066,20 +1899,36 @@ ENTRY(_switch_to)
entry a1, 16
- mov a4, a3 # preserve a3
+ mov a12, a2 # preserve 'prev' (a2)
+ mov a13, a3 # and 'next' (a3)
- s32i a0, a2, THREAD_RA # save return address
- s32i a1, a2, THREAD_SP # save stack pointer
+ l32i a4, a2, TASK_THREAD_INFO
+ l32i a5, a3, TASK_THREAD_INFO
- /* Disable ints while we manipulate the stack pointer; spill regs. */
+ save_xtregs_user a4 a6 a8 a9 a10 a11 THREAD_XTREGS_USER
- movi a5, (1 << PS_EXCM_BIT) | LOCKLEVEL
- xsr a5, PS
+ s32i a0, a12, THREAD_RA # save return address
+ s32i a1, a12, THREAD_SP # save stack pointer
+
+ /* Disable ints while we manipulate the stack pointer. */
+
+ movi a14, (1 << PS_EXCM_BIT) | LOCKLEVEL
+ xsr a14, PS
rsr a3, EXCSAVE_1
rsync
s32i a3, a3, EXC_TABLE_FIXUP /* enter critical section */
- call0 _spill_registers
+ /* Switch CPENABLE */
+
+#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
+ l32i a3, a5, THREAD_CPENABLE
+ xsr a3, CPENABLE
+ s32i a3, a4, THREAD_CPENABLE
+#endif
+
+ /* Flush register file. */
+
+ call0 _spill_registers # destroys a3, a4, and SAR
/* Set kernel stack (and leave critical section)
* Note: It's save to set it here. The stack will not be overwritten
@@ -2087,19 +1936,21 @@ ENTRY(_switch_to)
* 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
+ movi a6, 0
+ addi a7, a5, PT_REGS_OFFSET
+ s32i a6, a3, EXC_TABLE_FIXUP
+ s32i a7, a3, EXC_TABLE_KSTK
/* restore context of the task that 'next' addresses */
- l32i a0, a4, THREAD_RA /* restore return address */
- l32i a1, a4, THREAD_SP /* restore stack pointer */
+ l32i a0, a13, THREAD_RA # restore return address
+ l32i a1, a13, THREAD_SP # restore stack pointer
+
+ load_xtregs_user a5 a6 a8 a9 a10 a11 THREAD_XTREGS_USER
- wsr a5, PS
+ wsr a14, PS
+ mov a2, a12 # return 'prev'
rsync
retw
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index 026138d641a4..9185597eb6a0 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -52,6 +52,55 @@ void (*pm_power_off)(void) = NULL;
EXPORT_SYMBOL(pm_power_off);
+#if XTENSA_HAVE_COPROCESSORS
+
+void coprocessor_release_all(struct thread_info *ti)
+{
+ unsigned long cpenable;
+ int i;
+
+ /* Make sure we don't switch tasks during this operation. */
+
+ preempt_disable();
+
+ /* Walk through all cp owners and release it for the requested one. */
+
+ cpenable = ti->cpenable;
+
+ for (i = 0; i < XCHAL_CP_MAX; i++) {
+ if (coprocessor_owner[i] == ti) {
+ coprocessor_owner[i] = 0;
+ cpenable &= ~(1 << i);
+ }
+ }
+
+ ti->cpenable = cpenable;
+ coprocessor_clear_cpenable();
+
+ preempt_enable();
+}
+
+void coprocessor_flush_all(struct thread_info *ti)
+{
+ unsigned long cpenable;
+ int i;
+
+ preempt_disable();
+
+ cpenable = ti->cpenable;
+
+ for (i = 0; i < XCHAL_CP_MAX; i++) {
+ if ((cpenable & 1) != 0 && coprocessor_owner[i] == ti)
+ coprocessor_flush(ti, i);
+ cpenable >>= 1;
+ }
+
+ preempt_enable();
+}
+
+#endif
+
+
/*
* Powermanagement idle function, if any is provided by the platform.
*/
@@ -71,15 +120,36 @@ void cpu_idle(void)
}
/*
- * Free current thread data structures etc..
+ * This is called when the thread calls exit().
*/
-
void exit_thread(void)
{
+#if XTENSA_HAVE_COPROCESSORS
+ coprocessor_release_all(current_thread_info());
+#endif
}
+/*
+ * Flush thread state. This is called when a thread does an execve()
+ * Note that we flush coprocessor registers for the case execve fails.
+ */
void flush_thread(void)
{
+#if XTENSA_HAVE_COPROCESSORS
+ struct thread_info *ti = current_thread_info();
+ coprocessor_flush_all(ti);
+ coprocessor_release_all(ti);
+#endif
+}
+
+/*
+ * This is called before the thread is copied.
+ */
+void prepare_to_copy(struct task_struct *tsk)
+{
+#if XTENSA_HAVE_COPROCESSORS
+ coprocessor_flush_all(task_thread_info(tsk));
+#endif
}
/*
@@ -107,6 +177,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs *childregs;
+ struct thread_info *ti;
unsigned long tos;
int user_mode = user_mode(regs);
@@ -128,13 +199,14 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
p->set_child_tid = p->clear_child_tid = NULL;
p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
p->thread.sp = (unsigned long)childregs;
+
if (user_mode(regs)) {
int len = childregs->wmask & ~0xf;
childregs->areg[1] = usp;
memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
&regs->areg[XCHAL_NUM_AREGS - len/4], len);
-
+// FIXME: we need to set THREADPTR in thread_info...
if (clone_flags & CLONE_SETTLS)
childregs->areg[2] = childregs->areg[6];
@@ -142,6 +214,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
/* In kernel space, we start a new thread with a new stack. */
childregs->wmask = 1;
}
+
+#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
+ ti = task_thread_info(p);
+ ti->cpenable = 0;
+#endif
+
return 0;
}
@@ -179,10 +257,6 @@ unsigned long get_wchan(struct task_struct *p)
}
/*
- * 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
@@ -191,9 +265,20 @@ unsigned long get_wchan(struct task_struct *p)
*
*/
-void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
- struct task_struct *tsk)
+void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
{
+ unsigned long wb, ws, wm;
+ int live, last;
+
+ wb = regs->windowbase;
+ ws = regs->windowstart;
+ wm = regs->wmask;
+ ws = ((ws >> wb) | (ws << (WSBITS - wb))) & ((1 << WSBITS) - 1);
+
+ /* Don't leak any random bits. */
+