Commit 5a0015d6 authored by Chris Zankel's avatar Chris Zankel Committed by Linus Torvalds

[PATCH] xtensa: Architecture support for Tensilica Xtensa Part 3

The attached patches provides part 3 of an architecture implementation for the
Tensilica Xtensa CPU series.
Signed-off-by: default avatarChris Zankel <chris@zankel.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 4bedea94
#
# 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
/*
* 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 */
/*
* 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;
}
/*
* 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:
/*
* 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"
/*
* 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
/*
* 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();
}
/*
* 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");
}
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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
// 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);
}
// 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;
}
}
/*
* 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);
}
/*
* 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 */
// 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;
}
/*
* 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;
}
/*
* 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 */
/*
* 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
/*
* 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);
}
/*
* 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
/*
* 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*)
}
}
/*
* 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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment