1
0
Fork 0

[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: Chris Zankel <chris@zankel.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
hifive-unleashed-5.1
Chris Zankel 2005-06-23 22:01:16 -07:00 committed by Linus Torvalds
parent 4bedea9454
commit 5a0015d626
23 changed files with 8627 additions and 0 deletions

View File

@ -0,0 +1,18 @@
#
# Makefile for the Linux/Xtensa kernel.
#
extra-y := head.o vmlinux.lds
obj-y := align.o entry.o irq.o coprocessor.o process.o ptrace.o semaphore.o \
setup.o signal.o syscalls.o time.o traps.o vectors.o platform.o \
pci-dma.o
## windowspill.o
obj-$(CONFIG_KGDB) += xtensa-stub.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o

View File

@ -0,0 +1,459 @@
/*
* arch/xtensa/kernel/align.S
*
* Handle unalignment exceptions in kernel space.
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of
* this archive for more details.
*
* Copyright (C) 2001 - 2005 Tensilica, Inc.
*
* Rewritten by Chris Zankel <chris@zankel.net>
*
* Based on work from Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* and Marc Gauthier <marc@tensilica.com, marc@alimni.uwaterloo.ca>
*/
#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/ptrace.h>
#include <asm/current.h>
#include <asm/offsets.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/thread_info.h>
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
/* First-level exception handler for unaligned exceptions.
*
* Note: This handler works only for kernel exceptions. Unaligned user
* access should get a seg fault.
*/
/* Big and little endian 16-bit values are located in
* different halves of a register. HWORD_START helps to
* abstract the notion of extracting a 16-bit value from a
* register.
* We also have to define new shifting instructions because
* lsb and msb are on 'opposite' ends in a register for
* different endian machines.
*
* Assume a memory region in ascending address:
* 0 1 2 3|4 5 6 7
*
* When loading one word into a register, the content of that register is:
* LE 3 2 1 0, 7 6 5 4
* BE 0 1 2 3, 4 5 6 7
*
* Masking the bits of the higher/lower address means:
* LE X X 0 0, 0 0 X X
* BE 0 0 X X, X X 0 0
*
* Shifting to higher/lower addresses, means:
* LE shift left / shift right
* BE shift right / shift left
*
* Extracting 16 bits from a 32 bit reg. value to higher/lower address means:
* LE mask 0 0 X X / shift left
* BE shift left / mask 0 0 X X
*/
#define UNALIGNED_USER_EXCEPTION
#if XCHAL_HAVE_BE
#define HWORD_START 16
#define INSN_OP0 28
#define INSN_T 24
#define INSN_OP1 16
.macro __src_b r, w0, w1; src \r, \w0, \w1; .endm
.macro __ssa8 r; ssa8b \r; .endm
.macro __ssa8r r; ssa8l \r; .endm
.macro __sh r, s; srl \r, \s; .endm
.macro __sl r, s; sll \r, \s; .endm
.macro __exth r, s; extui \r, \s, 0, 16; .endm
.macro __extl r, s; slli \r, \s, 16; .endm
#else
#define HWORD_START 0
#define INSN_OP0 0
#define INSN_T 4
#define INSN_OP1 12
.macro __src_b r, w0, w1; src \r, \w1, \w0; .endm
.macro __ssa8 r; ssa8l \r; .endm
.macro __ssa8r r; ssa8b \r; .endm
.macro __sh r, s; sll \r, \s; .endm
.macro __sl r, s; srl \r, \s; .endm
.macro __exth r, s; slli \r, \s, 16; .endm
.macro __extl r, s; extui \r, \s, 0, 16; .endm
#endif
/*
* xxxx xxxx = imm8 field
* yyyy = imm4 field
* ssss = s field
* tttt = t field
*
* 16 0
* -------------------
* L32I.N yyyy ssss tttt 1000
* S32I.N yyyy ssss tttt 1001
*
* 23 0
* -----------------------------
* res 0000 0010
* L16UI xxxx xxxx 0001 ssss tttt 0010
* L32I xxxx xxxx 0010 ssss tttt 0010
* XXX 0011 ssss tttt 0010
* XXX 0100 ssss tttt 0010
* S16I xxxx xxxx 0101 ssss tttt 0010
* S32I xxxx xxxx 0110 ssss tttt 0010
* XXX 0111 ssss tttt 0010
* XXX 1000 ssss tttt 0010
* L16SI xxxx xxxx 1001 ssss tttt 0010
* XXX 1010 0010
* **L32AI xxxx xxxx 1011 ssss tttt 0010 unsupported
* XXX 1100 0010
* XXX 1101 0010
* XXX 1110 0010
* **S32RI xxxx xxxx 1111 ssss tttt 0010 unsupported
* -----------------------------
* ^ ^ ^
* sub-opcode (NIBBLE_R) -+ | |
* t field (NIBBLE_T) -----------+ |
* major opcode (NIBBLE_OP0) --------------+
*/
#define OP0_L32I_N 0x8 /* load immediate narrow */
#define OP0_S32I_N 0x9 /* store immediate narrow */
#define OP1_SI_MASK 0x4 /* OP1 bit set for stores */
#define OP1_SI_BIT 2 /* OP1 bit number for stores */
#define OP1_L32I 0x2
#define OP1_L16UI 0x1
#define OP1_L16SI 0x9
#define OP1_L32AI 0xb
#define OP1_S32I 0x6
#define OP1_S16I 0x5
#define OP1_S32RI 0xf
/*
* Entry condition:
*
* a0: trashed, original value saved on stack (PT_AREG0)
* a1: a1
* a2: new stack pointer, original in DEPC
* a3: dispatch table
* depc: a2, original value saved on stack (PT_DEPC)
* excsave_1: a3
*
* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
*/
ENTRY(fast_unaligned)
/* Note: We don't expect the address to be aligned on a word
* boundary. After all, the processor generated that exception
* and it would be a hardware fault.
*/
/* Save some working register */
s32i a4, a2, PT_AREG4
s32i a5, a2, PT_AREG5
s32i a6, a2, PT_AREG6
s32i a7, a2, PT_AREG7
s32i a8, a2, PT_AREG8
rsr a0, DEPC
xsr a3, EXCSAVE_1
s32i a0, a2, PT_AREG2
s32i a3, a2, PT_AREG3
/* Keep value of SAR in a0 */
rsr a0, SAR
rsr a8, EXCVADDR # load unaligned memory address
/* Now, identify one of the following load/store instructions.
*
* The only possible danger of a double exception on the
* following l32i instructions is kernel code in vmalloc
* memory. The processor was just executing at the EPC_1
* address, and indeed, already fetched the instruction. That
* guarantees a TLB mapping, which hasn't been replaced by
* this unaligned exception handler that uses only static TLB
* mappings. However, high-level interrupt handlers might
* modify TLB entries, so for the generic case, we register a
* TABLE_FIXUP handler here, too.
*/
/* a3...a6 saved on stack, a2 = SP */
/* Extract the instruction that caused the unaligned access. */
rsr a7, EPC_1 # load exception address
movi a3, ~3
and a3, a3, a7 # mask lower bits
l32i a4, a3, 0 # load 2 words
l32i a5, a3, 4
__ssa8 a7
__src_b a4, a4, a5 # a4 has the instruction
/* Analyze the instruction (load or store?). */
extui a5, a4, INSN_OP0, 4 # get insn.op0 nibble
#if XCHAL_HAVE_NARROW
_beqi a5, OP0_L32I_N, .Lload # L32I.N, jump
addi a6, a5, -OP0_S32I_N
_beqz a6, .Lstore # S32I.N, do a store
#endif
/* 'store indicator bit' not set, jump */
_bbci.l a4, OP1_SI_BIT + INSN_OP1, .Lload
/* Store: Jump to table entry to get the value in the source register.*/
.Lstore:movi a5, .Lstore_table # table
extui a6, a4, INSN_T, 4 # get source register
addx8 a5, a6, a5
jx a5 # jump into table
/* Invalid instruction, CRITICAL! */
.Linvalid_instruction_load:
j .Linvalid_instruction
/* Load: Load memory address. */
.Lload: movi a3, ~3
and a3, a3, a8 # align memory address
__ssa8 a8
#ifdef UNALIGNED_USER_EXCEPTION
addi a3, a3, 8
l32e a5, a3, -8
l32e a6, a3, -4
#else
l32i a5, a3, 0
l32i a6, a3, 4
#endif
__src_b a3, a5, a6 # a3 has the data word
#if XCHAL_HAVE_NARROW
addi a7, a7, 2 # increment PC (assume 16-bit insn)
extui a5, a4, INSN_OP0, 4
_beqi a5, OP0_L32I_N, 1f # l32i.n: jump
addi a7, a7, 1
#else
addi a7, a7, 3
#endif
extui a5, a4, INSN_OP1, 4
_beqi a5, OP1_L32I, 1f # l32i: jump
extui a3, a3, 0, 16 # extract lower 16 bits
_beqi a5, OP1_L16UI, 1f
addi a5, a5, -OP1_L16SI
_bnez a5, .Linvalid_instruction_load
/* sign extend value */
slli a3, a3, 16
srai a3, a3, 16
/* Set target register. */
1:
#if XCHAL_HAVE_LOOP
rsr a3, LEND # check if we reached LEND
bne a7, a3, 1f
rsr a3, LCOUNT # and LCOUNT != 0
beqz a3, 1f
addi a3, a3, -1 # decrement LCOUNT and set
rsr a7, LBEG # set PC to LBEGIN
wsr a3, LCOUNT
#endif
1: wsr a7, EPC_1 # skip load instruction
extui a4, a4, INSN_T, 4 # extract target register
movi a5, .Lload_table
addx8 a4, a4, a5
jx a4 # jump to entry for target register
.align 8
.Lload_table:
s32i a3, a2, PT_AREG0; _j .Lexit; .align 8
mov a1, a3; _j .Lexit; .align 8 # fishy??
s32i a3, a2, PT_AREG2; _j .Lexit; .align 8
s32i a3, a2, PT_AREG3; _j .Lexit; .align 8
s32i a3, a2, PT_AREG4; _j .Lexit; .align 8
s32i a3, a2, PT_AREG5; _j .Lexit; .align 8
s32i a3, a2, PT_AREG6; _j .Lexit; .align 8
s32i a3, a2, PT_AREG7; _j .Lexit; .align 8
s32i a3, a2, PT_AREG8; _j .Lexit; .align 8
mov a9, a3 ; _j .Lexit; .align 8
mov a10, a3 ; _j .Lexit; .align 8
mov a11, a3 ; _j .Lexit; .align 8
mov a12, a3 ; _j .Lexit; .align 8
mov a13, a3 ; _j .Lexit; .align 8
mov a14, a3 ; _j .Lexit; .align 8
mov a15, a3 ; _j .Lexit; .align 8
.Lstore_table:
l32i a3, a2, PT_AREG0; _j 1f; .align 8
mov a3, a1; _j 1f; .align 8 # fishy??
l32i a3, a2, PT_AREG2; _j 1f; .align 8
l32i a3, a2, PT_AREG3; _j 1f; .align 8
l32i a3, a2, PT_AREG4; _j 1f; .align 8
l32i a3, a2, PT_AREG5; _j 1f; .align 8
l32i a3, a2, PT_AREG6; _j 1f; .align 8
l32i a3, a2, PT_AREG7; _j 1f; .align 8
l32i a3, a2, PT_AREG8; _j 1f; .align 8
mov a3, a9 ; _j 1f; .align 8
mov a3, a10 ; _j 1f; .align 8
mov a3, a11 ; _j 1f; .align 8
mov a3, a12 ; _j 1f; .align 8
mov a3, a13 ; _j 1f; .align 8
mov a3, a14 ; _j 1f; .align 8
mov a3, a15 ; _j 1f; .align 8
1: # a7: instruction pointer, a4: instruction, a3: value
movi a6, 0 # mask: ffffffff:00000000
#if XCHAL_HAVE_NARROW
addi a7, a7, 2 # incr. PC,assume 16-bit instruction
extui a5, a4, INSN_OP0, 4 # extract OP0
addi a5, a5, -OP0_S32I_N
_beqz a5, 1f # s32i.n: jump
addi a7, a7, 1 # increment PC, 32-bit instruction
#else
addi a7, a7, 3 # increment PC, 32-bit instruction
#endif
extui a5, a4, INSN_OP1, 4 # extract OP1
_beqi a5, OP1_S32I, 1f # jump if 32 bit store
_bnei a5, OP1_S16I, .Linvalid_instruction_store
movi a5, -1
__extl a3, a3 # get 16-bit value
__exth a6, a5 # get 16-bit mask ffffffff:ffff0000
/* Get memory address */
1:
#if XCHAL_HAVE_LOOP
rsr a3, LEND # check if we reached LEND
bne a7, a3, 1f
rsr a3, LCOUNT # and LCOUNT != 0
beqz a3, 1f
addi a3, a3, -1 # decrement LCOUNT and set
rsr a7, LBEG # set PC to LBEGIN
wsr a3, LCOUNT
#endif
1: wsr a7, EPC_1 # skip store instruction
movi a4, ~3
and a4, a4, a8 # align memory address
/* Insert value into memory */
movi a5, -1 # mask: ffffffff:XXXX0000
#ifdef UNALIGNED_USER_EXCEPTION
addi a4, a4, 8
#endif
__ssa8r a8
__src_b a7, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
__src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE)
#ifdef UNALIGNED_USER_EXCEPTION
l32e a5, a4, -8
#else
l32i a5, a4, 0 # load lower address word
#endif
and a5, a5, a7 # mask
__sh a7, a3 # shift value
or a5, a5, a7 # or with original value
#ifdef UNALIGNED_USER_EXCEPTION
s32e a5, a4, -8
l32e a7, a4, -4
#else
s32i a5, a4, 0 # store
l32i a7, a4, 4 # same for upper address word
#endif
__sl a5, a3
and a6, a7, a6
or a6, a6, a5
#ifdef UNALIGNED_USER_EXCEPTION
s32e a6, a4, -4
#else
s32i a6, a4, 4
#endif
/* Done. restore stack and return */
.Lexit:
movi a4, 0
rsr a3, EXCSAVE_1
s32i a4, a3, EXC_TABLE_FIXUP
/* Restore working register */
l32i a7, a2, PT_AREG7
l32i a6, a2, PT_AREG6
l32i a5, a2, PT_AREG5
l32i a4, a2, PT_AREG4
l32i a3, a2, PT_AREG3
/* restore SAR and return */
wsr a0, SAR
l32i a0, a2, PT_AREG0
l32i a2, a2, PT_AREG2
rfe
/* We cannot handle this exception. */
.extern _kernel_exception
.Linvalid_instruction_store:
.Linvalid_instruction:
/* Restore a4...a8 and SAR, set SP, and jump to default exception. */
l32i a8, a2, PT_AREG8
l32i a7, a2, PT_AREG7
l32i a6, a2, PT_AREG6
l32i a5, a2, PT_AREG5
l32i a4, a2, PT_AREG4
wsr a0, SAR
mov a1, a2
rsr a0, PS
bbsi.l a2, PS_UM_SHIFT, 1f # jump if user mode
movi a0, _kernel_exception
jx a0
1: movi a0, _user_exception
jx a0
#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */

View File

@ -0,0 +1,94 @@
/*
* arch/xtensa/kernel/asm-offsets.c
*
* Generates definitions from c-type structures used by assembly sources.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
*/
#include <asm/processor.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/stddef.h>
#include <linux/thread_info.h>
#include <linux/ptrace.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#define DEFINE(sym, val) asm volatile("\n->" #sym " %0 " #val : : "i" (val))
#define BLANK() asm volatile("\n->" : : )
int main(void)
{
/* struct pt_regs */
DEFINE(PT_PC, offsetof (struct pt_regs, pc));
DEFINE(PT_PS, offsetof (struct pt_regs, ps));
DEFINE(PT_DEPC, offsetof (struct pt_regs, depc));
DEFINE(PT_EXCCAUSE, offsetof (struct pt_regs, exccause));
DEFINE(PT_EXCVADDR, offsetof (struct pt_regs, excvaddr));
DEFINE(PT_DEBUGCAUSE, offsetof (struct pt_regs, debugcause));
DEFINE(PT_WMASK, offsetof (struct pt_regs, wmask));
DEFINE(PT_LBEG, offsetof (struct pt_regs, lbeg));
DEFINE(PT_LEND, offsetof (struct pt_regs, lend));
DEFINE(PT_LCOUNT, offsetof (struct pt_regs, lcount));
DEFINE(PT_SAR, offsetof (struct pt_regs, sar));
DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0]));
DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0]));
DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1]));
DEFINE(PT_AREG2, offsetof (struct pt_regs, areg[2]));
DEFINE(PT_AREG3, offsetof (struct pt_regs, areg[3]));
DEFINE(PT_AREG4, offsetof (struct pt_regs, areg[4]));
DEFINE(PT_AREG5, offsetof (struct pt_regs, areg[5]));
DEFINE(PT_AREG6, offsetof (struct pt_regs, areg[6]));
DEFINE(PT_AREG7, offsetof (struct pt_regs, areg[7]));
DEFINE(PT_AREG8, offsetof (struct pt_regs, areg[8]));
DEFINE(PT_AREG9, offsetof (struct pt_regs, areg[9]));
DEFINE(PT_AREG10, offsetof (struct pt_regs, areg[10]));
DEFINE(PT_AREG11, offsetof (struct pt_regs, areg[11]));
DEFINE(PT_AREG12, offsetof (struct pt_regs, areg[12]));
DEFINE(PT_AREG13, offsetof (struct pt_regs, areg[13]));
DEFINE(PT_AREG14, offsetof (struct pt_regs, areg[14]));
DEFINE(PT_AREG15, offsetof (struct pt_regs, areg[15]));
DEFINE(PT_WINDOWBASE, offsetof (struct pt_regs, windowbase));
DEFINE(PT_WINDOWSTART, offsetof(struct pt_regs, windowstart));
DEFINE(PT_SIZE, sizeof(struct pt_regs));
DEFINE(PT_AREG_END, offsetof (struct pt_regs, areg[XCHAL_NUM_AREGS]));
DEFINE(PT_USER_SIZE, offsetof(struct pt_regs, areg[XCHAL_NUM_AREGS]));
BLANK();
/* struct task_struct */
DEFINE(TASK_PTRACE, offsetof (struct task_struct, ptrace));
DEFINE(TASK_MM, offsetof (struct task_struct, mm));
DEFINE(TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm));
DEFINE(TASK_PID, offsetof (struct task_struct, pid));
DEFINE(TASK_THREAD, offsetof (struct task_struct, thread));
DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, thread_info));
DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct));
BLANK();
/* struct thread_info (offset from start_struct) */
DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra));
DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp));
DEFINE(THREAD_CP_SAVE, offsetof (struct task_struct, thread.cp_save));
DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds));
BLANK();
/* struct mm_struct */
DEFINE(MM_USERS, offsetof(struct mm_struct, mm_users));
DEFINE(MM_PGD, offsetof (struct mm_struct, pgd));
DEFINE(MM_CONTEXT, offsetof (struct mm_struct, context));
BLANK();
DEFINE(PT_SINGLESTEP_BIT, PT_SINGLESTEP_BIT);
return 0;
}

View File

@ -0,0 +1,201 @@
/*
* arch/xtensa/kernel/coprocessor.S
*
* Xtensa processor configuration-specific table of coprocessor and
* other custom register layout information.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2003 - 2005 Tensilica Inc.
*
* Marc Gauthier <marc@tensilica.com> <marc@alumni.uwaterloo.ca>
*/
/*
* This module contains a table that describes the layout of the various
* custom registers and states associated with each coprocessor, as well
* as those not associated with any coprocessor ("extra state").
* This table is included with core dumps and is available via the ptrace
* interface, allowing the layout of such register/state information to
* be modified in the kernel without affecting the debugger. Each
* register or state is identified using a 32-bit "libdb target number"
* assigned when the Xtensa processor is generated.
*/
#include <linux/config.h>
#include <linux/linkage.h>
#include <asm/processor.h>
#if XCHAL_HAVE_CP
#define CP_LAST ((XCHAL_CP_MAX - 1) * COPROCESSOR_INFO_SIZE)
ENTRY(release_coprocessors)
entry a1, 16
# a2: task
movi a3, 1 << XCHAL_CP_MAX # a3: coprocessor-bit
movi a4, coprocessor_info+CP_LAST # a4: owner-table
# a5: tmp
movi a6, 0 # a6: 0
rsil a7, LOCKLEVEL # a7: PS
1: /* Check if task is coprocessor owner of coprocessor[i]. */
l32i a5, a4, COPROCESSOR_INFO_OWNER
srli a3, a3, 1
beqz a3, 1f
addi a4, a4, -8
beq a2, a5, 1b
/* Found an entry: Clear entry CPENABLE bit to disable CP. */
rsr a5, CPENABLE
s32i a6, a4, COPROCESSOR_INFO_OWNER
xor a5, a3, a5
wsr a5, CPENABLE
bnez a3, 1b
1: wsr a7, PS
rsync
retw
ENTRY(disable_coprocessor)
entry sp, 16
rsil a7, LOCKLEVEL
rsr a3, CPENABLE
movi a4, 1
ssl a2
sll a4, a4
and a4, a3, a4
xor a3, a3, a4
wsr a3, CPENABLE
wsr a7, PS
rsync
retw
ENTRY(enable_coprocessor)
entry sp, 16
rsil a7, LOCKLEVEL
rsr a3, CPENABLE
movi a4, 1
ssl a2
sll a4, a4
or a3, a3, a4
wsr a3, CPENABLE
wsr a7, PS
rsync
retw
#endif
ENTRY(save_coprocessor_extra)
entry sp, 16
xchal_extra_store_funcbody
retw
ENTRY(restore_coprocessor_extra)
entry sp, 16
xchal_extra_load_funcbody
retw
ENTRY(save_coprocessor_registers)
entry sp, 16
xchal_cpi_store_funcbody
retw
ENTRY(restore_coprocessor_registers)
entry sp, 16
xchal_cpi_load_funcbody
retw
/*
* The Xtensa compile-time HAL (core.h) XCHAL_*_SA_CONTENTS_LIBDB macros
* describe the contents of coprocessor & extra save areas in terms of
* undefined CONTENTS_LIBDB_{SREG,UREG,REGF} macros. We define these
* latter macros here; they expand into a table of the format we want.
* The general format is:
*
* CONTENTS_LIBDB_SREG(libdbnum, offset, size, align, rsv1, name, sregnum,
* bitmask, rsv2, rsv3)
* CONTENTS_LIBDB_UREG(libdbnum, offset, size, align, rsv1, name, uregnum,
* bitmask, rsv2, rsv3)
* CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index,
* numentries, contentsize, regname_base,
* regfile_name, rsv2, rsv3)
*
* For this table, we only care about the <libdbnum>, <offset> and <size>
* fields.
*/
/* Map all XCHAL CONTENTS macros to the reg_entry asm macro defined below: */
#define CONTENTS_LIBDB_SREG(libdbnum,offset,size,align,rsv1,name,sregnum, \
bitmask, rsv2, rsv3) \
reg_entry libdbnum, offset, size ;
#define CONTENTS_LIBDB_UREG(libdbnum,offset,size,align,rsv1,name,uregnum, \
bitmask, rsv2, rsv3) \
reg_entry libdbnum, offset, size ;
#define CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index, \
numentries, contentsize, regname_base, \
regfile_name, rsv2, rsv3) \
reg_entry libdbnum, offset, size ;
/* A single table entry: */
.macro reg_entry libdbnum, offset, size
.ifne (__last_offset-(__last_group_offset+\offset))
/* padding entry */
.word (0xFC000000+__last_offset-(__last_group_offset+\offset))
.endif
.word \libdbnum /* actual entry */
.set __last_offset, __last_group_offset+\offset+\size
.endm /* reg_entry */
/* Table entry that marks the beginning of a group (coprocessor or "extra"): */
.macro reg_group cpnum, num_entries, align
.set __last_group_offset, (__last_offset + \align- 1) & -\align
.ifne \num_entries
.word 0xFD000000+(\cpnum<<16)+\num_entries
.endif
.endm /* reg_group */
/*
* Register info tables.
*/
.section .rodata, "a"
.globl _xtensa_reginfo_tables
.globl _xtensa_reginfo_table_size
.align 4
_xtensa_reginfo_table_size:
.word _xtensa_reginfo_table_end - _xtensa_reginfo_tables
_xtensa_reginfo_tables:
.set __last_offset, 0
reg_group 0xFF, XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM, XCHAL_EXTRA_SA_ALIGN
XCHAL_EXTRA_SA_CONTENTS_LIBDB
reg_group 0, XCHAL_CP0_SA_CONTENTS_LIBDB_NUM, XCHAL_CP0_SA_ALIGN
XCHAL_CP0_SA_CONTENTS_LIBDB
reg_group 1, XCHAL_CP1_SA_CONTENTS_LIBDB_NUM, XCHAL_CP1_SA_ALIGN
XCHAL_CP1_SA_CONTENTS_LIBDB
reg_group 2, XCHAL_CP2_SA_CONTENTS_LIBDB_NUM, XCHAL_CP2_SA_ALIGN
XCHAL_CP2_SA_CONTENTS_LIBDB
reg_group 3, XCHAL_CP3_SA_CONTENTS_LIBDB_NUM, XCHAL_CP3_SA_ALIGN
XCHAL_CP3_SA_CONTENTS_LIBDB
reg_group 4, XCHAL_CP4_SA_CONTENTS_LIBDB_NUM, XCHAL_CP4_SA_ALIGN
XCHAL_CP4_SA_CONTENTS_LIBDB
reg_group 5, XCHAL_CP5_SA_CONTENTS_LIBDB_NUM, XCHAL_CP5_SA_ALIGN
XCHAL_CP5_SA_CONTENTS_LIBDB
reg_group 6, XCHAL_CP6_SA_CONTENTS_LIBDB_NUM, XCHAL_CP6_SA_ALIGN
XCHAL_CP6_SA_CONTENTS_LIBDB
reg_group 7, XCHAL_CP7_SA_CONTENTS_LIBDB_NUM, XCHAL_CP7_SA_ALIGN
XCHAL_CP7_SA_CONTENTS_LIBDB
.word 0xFC000000 /* invalid register number,marks end of table*/
_xtensa_reginfo_table_end:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
/*
* arch/xtensa/kernel/head.S
*
* Xtensa Processor startup code.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
* Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Kevin Chea
*/
#include <xtensa/cacheasm.h>
#include <linux/config.h>
#include <asm/processor.h>
#include <asm/page.h>
/*
* This module contains the entry code for kernel images. It performs the
* minimal setup needed to call the generic C routines.
*
* Prerequisites:
*
* - The kernel image has been loaded to the actual address where it was
* compiled to.
* - a2 contains either 0 or a pointer to a list of boot parameters.
* (see setup.c for more details)
*
*/
.macro iterate from, to , cmd
.ifeq ((\to - \from) & ~0xfff)
\cmd \from
iterate "(\from+1)", \to, \cmd
.endif
.endm
/*
* _start
*
* The bootloader passes a pointer to a list of boot parameters in a2.
*/
/* The first bytes of the kernel image must be an instruction, so we
* manually allocate and define the literal constant we need for a jx
* instruction.
*/
.section .head.text, "ax"
.globl _start
_start: _j 2f
.align 4
1: .word _startup
2: l32r a0, 1b
jx a0
.text
.align 4
_startup:
/* Disable interrupts and exceptions. */
movi a0, XCHAL_PS_EXCM_MASK
wsr a0, PS
/* Preserve the pointer to the boot parameter list in EXCSAVE_1 */
wsr a2, EXCSAVE_1
/* Start with a fresh windowbase and windowstart. */
movi a1, 1
movi a0, 0
wsr a1, WINDOWSTART
wsr a0, WINDOWBASE
rsync
/* Set a0 to 0 for the remaining initialization. */
movi a0, 0
/* Clear debugging registers. */
#if XCHAL_HAVE_DEBUG
wsr a0, IBREAKENABLE
wsr a0, ICOUNT
movi a1, 15
wsr a0, ICOUNTLEVEL
.macro reset_dbreak num
wsr a0, DBREAKC + \num
.endm
iterate 0, XCHAL_NUM_IBREAK-1, reset_dbreak
#endif
/* Clear CCOUNT (not really necessary, but nice) */
wsr a0, CCOUNT # not really necessary, but nice
/* Disable zero-loops. */
#if XCHAL_HAVE_LOOPS
wsr a0, LCOUNT
#endif
/* Disable all timers. */
.macro reset_timer num
wsr a0, CCOMPARE_0 + \num
.endm
iterate 0, XCHAL_NUM_TIMERS-1, reset_timer
/* Interrupt initialization. */
movi a2, XCHAL_INTTYPE_MASK_SOFTWARE | XCHAL_INTTYPE_MASK_EXTERN_EDGE
wsr a0, INTENABLE
wsr a2, INTCLEAR
/* Disable coprocessors. */
#if XCHAL_CP_NUM > 0
wsr a0, CPENABLE
#endif
/* Set PS.INTLEVEL=1, PS.WOE=0, kernel stack, PS.EXCM=0
*
* Note: PS.EXCM must be cleared before using any loop
* instructions; otherwise, they are silently disabled, and
* at most one iteration of the loop is executed.
*/
movi a1, 1
wsr a1, PS
rsync
/* Initialize the caches.
* Does not include flushing writeback d-cache.
* a6, a7 are just working registers (clobbered).
*/
icache_reset a2, a3
dcache_reset a2, a3
/* Unpack data sections
*
* The linker script used to build the Linux kernel image
* creates a table located at __boot_reloc_table_start
* that contans the information what data needs to be unpacked.
*
* Uses a2-a7.
*/
movi a2, __boot_reloc_table_start
movi a3, __boot_reloc_table_end
1: beq a2, a3, 3f # no more entries?
l32i a4, a2, 0 # start destination (in RAM)
l32i a5, a2, 4 # end desination (in RAM)
l32i a6, a2, 8 # start source (in ROM)
addi a2, a2, 12 # next entry
beq a4, a5, 1b # skip, empty entry
beq a4, a6, 1b # skip, source and dest. are the same
2: l32i a7, a6, 0 # load word
addi a6, a6, 4
s32i a7, a4, 0 # store word
addi a4, a4, 4
bltu a4, a5, 2b
j 1b
3:
/* All code and initialized data segments have been copied.
* Now clear the BSS segment.
*/
movi a2, _bss_start # start of BSS
movi a3, _bss_end # end of BSS
1: addi a2, a2, 4
s32i a0, a2, 0
blt a2, a3, 1b
#if XCHAL_DCACHE_IS_WRITEBACK
/* After unpacking, flush the writeback cache to memory so the
* instructions/data are available.
*/
dcache_writeback_all a2, a3
#endif
/* Setup stack and enable window exceptions (keep irqs disabled) */
movi a1, init_thread_union
addi a1, a1, KERNEL_STACK_SIZE
movi a2, 0x00040001 # WOE=1, INTLEVEL=1, UM=0
wsr a2, PS # (enable reg-windows; progmode stack)
rsync
/* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/
movi a2, debug_exception
wsr a2, EXCSAVE + XCHAL_DEBUGLEVEL
/* Set up EXCSAVE[1] to point to the exc_table. */
movi a6, exc_table
xsr a6, EXCSAVE_1
/* init_arch kick-starts the linux kernel */
movi a4, init_arch
callx4 a4
movi a4, start_kernel
callx4 a4
should_never_return:
j should_never_return
/* Define some common data structures here. We define them
* here in this assembly file due to their unusual alignment
* requirements.
*/
.comm swapper_pg_dir,PAGE_SIZE,PAGE_SIZE
.comm empty_bad_page_table,PAGE_SIZE,PAGE_SIZE
.comm empty_bad_page,PAGE_SIZE,PAGE_SIZE
.comm empty_zero_page,PAGE_SIZE,PAGE_SIZE

View File

@ -0,0 +1,192 @@
/*
* linux/arch/xtensa/kernel/irq.c
*
* Xtensa built-in interrupt controller and some generic functions copied
* from i386.
*
* Copyright (C) 2002 - 2005 Tensilica, Inc.
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
*
*
* Chris Zankel <chris@zankel.net>
* Kevin Chea
*
*/
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel_stat.h>
#include <asm/uaccess.h>
#include <asm/platform.h>
static void enable_xtensa_irq(unsigned int irq);
static void disable_xtensa_irq(unsigned int irq);
static void mask_and_ack_xtensa(unsigned int irq);
static void end_xtensa_irq(unsigned int irq);
static unsigned int cached_irq_mask;
atomic_t irq_err_count;
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves.
*/
void ack_bad_irq(unsigned int irq)
{
printk("unexpected IRQ trap at vector %02x\n", irq);
}
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
unsigned int do_IRQ(int irq, struct pt_regs *regs)
{
irq_enter();
#ifdef CONFIG_DEBUG_STACKOVERFLOW
/* Debugging check for stack overflow: is there less than 1KB free? */
{
unsigned long sp;
__asm__ __volatile__ ("mov %0, a1\n" : "=a" (sp));
sp &= THREAD_SIZE - 1;
if (unlikely(sp < (sizeof(thread_info) + 1024)))
printk("Stack overflow in do_IRQ: %ld\n",
sp - sizeof(struct thread_info));
}
#endif
__do_IRQ(irq, regs);
irq_exit();
return 1;
}
/*
* Generic, controller-independent functions:
*/
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *) v, j;
struct irqaction * action;
unsigned long flags;
if (i == 0) {
seq_printf(p, " ");
for (j=0; j<NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "CPU%d ",j);
seq_putc(p, '\n');
}
if (i < NR_IRQS) {
spin_lock_irqsave(&irq_desc[i].lock, flags);
action = irq_desc[i].action;
if (!action)
goto skip;
seq_printf(p, "%3d: ",i);
#ifndef CONFIG_SMP
seq_printf(p, "%10u ", kstat_irqs(i));
#else
for (j = 0; j < NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
#endif
seq_printf(p, " %14s", irq_desc[i].handler->typename);
seq_printf(p, " %s", action->name);
for (action=action->next; action; action = action->next)
seq_printf(p, ", %s", action->name);
seq_putc(p, '\n');
skip:
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
} else if (i == NR_IRQS) {
seq_printf(p, "NMI: ");
for (j = 0; j < NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "%10u ", nmi_count(j));
seq_putc(p, '\n');
seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
}
return 0;
}
/* shutdown is same as "disable" */
#define shutdown_xtensa_irq disable_xtensa_irq
static unsigned int startup_xtensa_irq(unsigned int irq)
{
enable_xtensa_irq(irq);
return 0; /* never anything pending */
}
static struct hw_interrupt_type xtensa_irq_type = {
"Xtensa-IRQ",
startup_xtensa_irq,
shutdown_xtensa_irq,
enable_xtensa_irq,
disable_xtensa_irq,
mask_and_ack_xtensa,
end_xtensa_irq
};
static inline void mask_irq(unsigned int irq)
{
cached_irq_mask &= ~(1 << irq);
set_sr (cached_irq_mask, INTENABLE);
}
static inline void unmask_irq(unsigned int irq)
{
cached_irq_mask |= 1 << irq;
set_sr (cached_irq_mask, INTENABLE);
}
static void disable_xtensa_irq(unsigned int irq)
{
unsigned long flags;
local_save_flags(flags);
mask_irq(irq);
local_irq_restore(flags);
}
static void enable_xtensa_irq(unsigned int irq)
{
unsigned long flags;
local_save_flags(flags);
unmask_irq(irq);
local_irq_restore(flags);
}
static void mask_and_ack_xtensa(unsigned int irq)
{
disable_xtensa_irq(irq);
}
static void end_xtensa_irq(unsigned int irq)
{
enable_xtensa_irq(irq);
}
void __init init_IRQ(void)
{
int i;
for (i=0; i < XTENSA_NR_IRQS; i++)
irq_desc[i].handler = &xtensa_irq_type;
cached_irq_mask = 0;
platform_init_irq();
}

View File

@ -0,0 +1,78 @@
/*
* arch/xtensa/kernel/platform.c
*
* Module support.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
*
*/
#include <linux/module.h>
#include <linux/moduleloader.h>
#include <linux/elf.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/cache.h>
LIST_HEAD(module_buf_list);
void *module_alloc(unsigned long size)
{
panic("module_alloc not implemented");
}
void module_free(struct module *mod, void *module_region)
{
panic("module_free not implemented");
}
int module_frob_arch_sections(Elf32_Ehdr *hdr,
Elf32_Shdr *sechdrs,
char *secstrings,
struct module *me)
{
panic("module_frob_arch_sections not implemented");
}
int apply_relocate(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *module)
{
panic ("apply_relocate not implemented");
}
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *module)
{
panic("apply_relocate_add not implemented");
}
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
{
panic ("module_finalize not implemented");
}
void module_arch_cleanup(struct module *mod)
{
panic("module_arch_cleanup not implemented");
}
struct bug_entry *module_find_bug(unsigned long bugaddr)
{
panic("module_find_bug not implemented");
}

View File

@ -0,0 +1,73 @@
/*
* arch/xtensa/pci-dma.c
*
* DMA coherent memory allocation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* Copyright (C) 2002 - 2005 Tensilica Inc.
*
* Based on version for i386.
*
* Chris Zankel <chris@zankel.net>
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
*/
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/cacheflush.h>
/*
* Note: We assume that the full memory space is always mapped to 'kseg'
* Otherwise we have to use page attributes (not implemented).
*/
void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp)
{
void *ret;
/* ignore region speicifiers */
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
if (dev == NULL || (*dev->dma_mask < 0xffffffff))
gfp |= GFP_DMA;
ret = (void *)__get_free_pages(gfp, get_order(size));
if (ret != NULL) {
memset(ret, 0, size);
*handle = virt_to_bus(ret);
}
return (void*) BYPASS_ADDR((unsigned long)ret);
}
void dma_free_coherent(struct device *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
free_pages(CACHED_ADDR((unsigned long)vaddr), get_order(size));
}
void consistent_sync(void *vaddr, size_t size, int direction)
{
switch (direction) {
case PCI_DMA_NONE:
BUG();
case PCI_DMA_FROMDEVICE: /* invalidate only */
__invalidate_dcache_range((unsigned long)vaddr,
(unsigned long)size);
break;
case PCI_DMA_TODEVICE: /* writeback only */
case PCI_DMA_BIDIRECTIONAL: /* writeback and invalidate */
__flush_invalidate_dcache_range((unsigned long)vaddr,
(unsigned long)size);
break;
}
}

View File

@ -0,0 +1,563 @@
/*
* arch/xtensa/pcibios.c
*
* PCI bios-type initialisation for PCI machines
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* Copyright (C) 2001-2005 Tensilica Inc.
*
* Based largely on work from Cort (ppc/kernel/pci.c)
* IO functions copied from sparc.
*
* Chris Zankel <chris@zankel.net>
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/bootmem.h>
#include <asm/pci-bridge.h>
#include <asm/platform.h>
#undef DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
/* PCI Controller */
/*
* pcibios_alloc_controller
* pcibios_enable_device
* pcibios_fixups
* pcibios_align_resource
* pcibios_fixup_bus
* pcibios_setup
* pci_bus_add_device
* pci_mmap_page_range
*/
struct pci_controller* pci_ctrl_head;
struct pci_controller** pci_ctrl_tail = &pci_ctrl_head;
static int pci_bus_count;
static void pcibios_fixup_resources(struct pci_dev* dev);
#if 0 // FIXME
struct pci_fixup pcibios_fixups[] = {
{ DECLARE_PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources },
{ 0 }
};
#endif
void
pcibios_update_resource(struct pci_dev *dev, struct resource *root,
struct resource *res, int resource)
{
u32 new, check, mask;
int reg;
struct pci_controller* pci_ctrl = dev->sysdata;
new = res->start;
if (pci_ctrl && res->flags & IORESOURCE_IO) {
new -= pci_ctrl->io_space.base;
}
new |= (res->flags & PCI_REGION_FLAG_MASK);
if (resource < 6) {
reg = PCI_BASE_ADDRESS_0 + 4*resource;
} else if (resource == PCI_ROM_RESOURCE) {
res->flags |= PCI_ROM_ADDRESS_ENABLE;
reg = dev->rom_base_reg;
} else {
/* Somebody might have asked allocation of a non-standard resource */
return;
}
pci_write_config_dword(dev, reg, new);
pci_read_config_dword(dev, reg, &check);
mask = (new & PCI_BASE_ADDRESS_SPACE_IO) ?
PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
if ((new ^ check) & mask) {
printk(KERN_ERR "PCI: Error while updating region "
"%s/%d (%08x != %08x)\n", dev->slot_name, resource,
new, check);
}
}
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
* addresses to be allocated in the 0x000-0x0ff region
* modulo 0x400.
*
* Why? Because some silly external IO cards only decode
* the low 10 bits of the IO address. The 0x00-0xff region
* is reserved for motherboard devices that decode all 16
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
* but we want to try to avoid allocating at 0x2900-0x2bff
* which might have be mirrored at 0x0100-0x03ff..
*/
void
pcibios_align_resource(void *data, struct resource *res, unsigned long size,
unsigned long align)
{
struct pci_dev *dev = data;
if (res->flags & IORESOURCE_IO) {
unsigned long start = res->start;
if (size > 0x100) {
printk(KERN_ERR "PCI: I/O Region %s/%d too large"
" (%ld bytes)\n", dev->slot_name,
dev->resource - res, size);
}
if (start & 0x300) {
start = (start + 0x3ff) & ~0x3ff;
res->start = start;
}
}
}
int
pcibios_enable_resources(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for(idx=0; idx<6; idx++) {
r = &dev->resource[idx];
if (!r->start && r->end) {
printk (KERN_ERR "PCI: Device %s not available because "
"of resource collisions\n", dev->slot_name);
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (dev->resource[PCI_ROM_RESOURCE].start)
cmd |= PCI_COMMAND_MEMORY;
if (cmd != old_cmd) {
printk("PCI: Enabling device %s (%04x -> %04x)\n",
dev->slot_name, old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
struct pci_controller * __init pcibios_alloc_controller(void)
{
struct pci_controller *pci_ctrl;
pci_ctrl = (struct pci_controller *)alloc_bootmem(sizeof(*pci_ctrl));
memset(pci_ctrl, 0, sizeof(struct pci_controller));
*pci_ctrl_tail = pci_ctrl;
pci_ctrl_tail = &pci_ctrl->next;
return pci_ctrl;
}
static int __init pcibios_init(void)
{
struct pci_controller *pci_ctrl;
struct pci_bus *bus;
int next_busno = 0, i;
printk("PCI: Probing PCI hardware\n");
/* Scan all of the recorded PCI controllers. */
for (pci_ctrl = pci_ctrl_head; pci_ctrl; pci_ctrl = pci_ctrl->next) {
pci_ctrl->last_busno = 0xff;
bus = pci_scan_bus(pci_ctrl->first_busno, pci_ctrl->ops,
pci_ctrl);
if (pci_ctrl->io_resource.flags) {
unsigned long offs;
offs = (unsigned long)pci_ctrl->io_space.base;
pci_ctrl->io_resource.start += offs;
pci_ctrl->io_resource.end += offs;
bus->resource[0] = &pci_ctrl->io_resource;
}
for (i = 0; i < 3; ++i)
if (pci_ctrl->mem_resources[i].flags)
bus->resource[i+1] =&pci_ctrl->mem_resources[i];
pci_ctrl->bus = bus;
pci_ctrl->last_busno = bus->subordinate;
if (next_busno <= pci_ctrl->last_busno)
next_busno = pci_ctrl->last_busno+1;
}
pci_bus_count = next_busno;
return platform_pcibios_fixup();
}
subsys_initcall(pcibios_init);
void __init pcibios_fixup_bus(struct pci_bus *bus)
{
struct pci_controller *pci_ctrl = bus->sysdata;
struct resource *res;
unsigned long io_offset;
int i;
io_offset = (unsigned long)pci_ctrl->io_space.base;
if (bus->parent == NULL) {
/* this is a host bridge - fill in its resources */
pci_ctrl->bus = bus;
bus->resource[0] = res = &pci_ctrl->io_resource;
if (!res->flags) {
if (io_offset)
printk (KERN_ERR "I/O resource not set for host"
" bridge %d\n", pci_ctrl->index);
res->start = 0;
res->end = IO_SPACE_LIMIT;
res->flags = IORESOURCE_IO;
}
res->start += io_offset;
res->end += io_offset;
for (i = 0; i < 3; i++) {
res = &pci_ctrl->mem_resources[i];
if (!res->flags) {
if (i > 0)
continue;
printk(KERN_ERR "Memory resource not set for "
"host bridge %d\n", pci_ctrl->index);
res->start = 0;
res->end = ~0U;
res->flags = IORESOURCE_MEM;
}
bus->resource[i+1] = res;
}
} else {
/* This is a subordinate bridge */
pci_read_bridge_bases(bus);
for (i = 0; i < 4; i++) {
if ((res = bus->resource[i]) == NULL || !res->flags)
continue;
if (io_offset && (res->flags & IORESOURCE_IO)) {
res->start += io_offset;
res->end += io_offset;
}
}
}
}
char __init *pcibios_setup(char *str)
{
return str;
}
/* the next one is stolen from the alpha port... */
void __init
pcibios_update_irq(struct pci_dev *dev, int irq)
{
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
}
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for (idx=0; idx<6; idx++) {
r = &dev->resource[idx];
if (!r->start && r->end) {
printk(KERN_ERR "PCI: Device %s not available because "
"of resource collisions\n", dev->slot_name);
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (cmd != old_cmd) {
printk("PCI: Enabling device %s (%04x -> %04x)\n",
dev->slot_name, old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
#ifdef CONFIG_PROC_FS
/*
* Return the index of the PCI controller for device pdev.
*/
int
pci_controller_num(struct pci_dev *dev)
{
struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
return pci_ctrl->index;
}
#endif /* CONFIG_PROC_FS */
static void
pcibios_fixup_resources(struct pci_dev *dev)
{
struct pci_controller* pci_ctrl = (struct pci_controller *)dev->sysdata;
int i;
unsigned long offset;
if (!pci_ctrl) {
printk(KERN_ERR "No pci_ctrl for PCI dev %s!\n",dev->slot_name);
return;
}
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
struct resource *res = dev->resource + i;
if (!res->start || !res->flags)
continue;
if (res->end == 0xffffffff) {
DBG("PCI:%s Resource %d [%08lx-%08lx] is unassigned\n",
dev->slot_name, i, res->start, res->end);
res->end -= res->start;
res->start = 0;
continue;
}
offset = 0;
if (res->flags & IORESOURCE_IO)
offset = (unsigned long) pci_ctrl->io_space.base;
else if (res->flags & IORESOURCE_MEM)
offset = (unsigned long) pci_ctrl->mem_space.base;
if (offset != 0) {
res->start += offset;
res->end += offset;
#ifdef DEBUG
printk("Fixup res %d (%lx) of dev %s: %lx -> %lx\n",
i, res->flags, dev->slot_name,
res->start - offset, res->start);
#endif
}
}
}
/*
* Platform support for /proc/bus/pci/X/Y mmap()s,
* modelled on the sparc64 implementation by Dave Miller.
* -- paulus.
*/
/*
* Adjust vm_pgoff of VMA such that it is the physical page offset
* corresponding to the 32-bit pci bus offset for DEV requested by the user.
*
* Basically, the user finds the base address for his device which he wishes
* to mmap. They read the 32-bit value from the config space base register,
* add whatever PAGE_SIZE multiple offset they wish, and feed this into the
* offset parameter of mmap on /proc/bus/pci/XXX for that device.
*
* Returns negative error code on failure, zero on success.
*/
static __inline__ int
__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state)
{
struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long io_offset = 0;
int i, res_bit;
if (pci_ctrl == 0)
return -EINVAL; /* should never happen */
/* If memory, add on the PCI bridge address offset */
if (mmap_state == pci_mmap_mem) {
res_bit = IORESOURCE_MEM;
} else {
io_offset = (unsigned long)pci_ctrl->io_space.base;
offset += io_offset;
res_bit = IORESOURCE_IO;
}
/*
* Check that the offset requested corresponds to one of the
* resources of the device.
*/
for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
struct resource *rp = &dev->resource[i];
int flags = rp->flags;
/* treat ROM as memory (should be already) */
if (i == PCI_ROM_RESOURCE)
flags |= IORESOURCE_MEM;
/* Active and same type? */
if ((flags & res_bit) == 0)
continue;
/* In the range of this resource? */
if (offset < (rp->start & PAGE_MASK) || offset > rp->end)
continue;
/* found it! construct the final physical address */
if (mmap_state == pci_mmap_io)
offset += pci_ctrl->io_space.start - io_offset;
vma->vm_pgoff = offset >> PAGE_SHIFT;
return 0;
}
return -EINVAL;
}
/*
* Set vm_flags of VMA, as appropriate for this architecture, for a pci device
* mapping.
*/
static __inline__ void
__pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state)
{
vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO;
}
/*
* Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
* device mapping.
*/
static __inline__ void
__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state, int write_combine)
{
int prot = pgprot_val(vma->vm_page_prot);
/* Set to write-through */
prot &= ~_PAGE_NO_CACHE;
#if 0
if (!write_combine)
prot |= _PAGE_WRITETHRU;
#endif
vma->vm_page_prot = __pgprot(prot);
}
/*
* Perform the actual remap of the pages for a PCI device mapping, as
* appropriate for this architecture. The region in the process to map
* is described by vm_start and vm_end members of VMA, the base physical
* address is found in vm_pgoff.
* The pci device structure is provided so that architectures may make mapping
* decisions on a per-device or per-bus basis.
*
* Returns a negative error code on failure, zero on success.
*/
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state,
int write_combine)
{
int ret;
ret = __pci_mmap_make_offset(dev, vma, mmap_state);
if (ret < 0)
return ret;
__pci_mmap_set_flags(dev, vma, mmap_state);
__pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine);
ret = io_remap_page_range(vma, vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
return ret;
}
/*
* This probably belongs here rather than ioport.c because
* we do not want this crud linked into SBus kernels.
* Also, think for a moment about likes of floppy.c that
* include architecture specific parts. They may want to redefine ins/outs.
*
* We do not use horroble macroses here because we want to
* advance pointer by sizeof(size).
*/
void outsb(unsigned long addr, const void *src, unsigned long count) {
while (count) {
count -= 1;
writeb(*(const char *)src, addr);
src += 1;
addr += 1;
}
}
void outsw(unsigned long addr, const void *src, unsigned long count) {
while (count) {
count -= 2;
writew(*(const short *)src, addr);
src += 2;
addr += 2;
}
}
void outsl(unsigned long addr, const void *src, unsigned long count) {
while (count) {
count -= 4;
writel(*(const long *)src, addr);
src += 4;
addr += 4;
}
}
void insb(unsigned long addr, void *dst, unsigned long count) {
while (count) {
count -= 1;
*(unsigned char *)dst = readb(addr);
dst += 1;
addr += 1;
}
}
void insw(unsigned long addr, void *dst, unsigned long count) {
while (count) {
count -= 2;
*(unsigned short *)dst = readw(addr);
dst += 2;
addr += 2;
}
}
void insl(unsigned long addr, void *dst, unsigned long count) {
while (count) {
count -= 4;
/*
* XXX I am sure we are in for an unaligned trap here.
*/
*(unsigned long *)dst = readl(addr);
dst += 4;
addr += 4;
}
}

View File

@ -0,0 +1,49 @@
/*
* arch/xtensa/kernel/platform.c
*
* Default platform functions.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <asm/platform.h>
#include <asm/timex.h>
#define _F(r,f,a,b) \
r __platform_##f a b; \
r platform_##f a __attribute__((weak, alias("__platform_"#f)))
/*
* Default functions that are used if no platform specific function is defined.
* (Please, refer to include/asm-xtensa/platform.h for more information)
*/
_F(void, setup, (char** cmd), { });
_F(void, init_irq, (void), { });
_F(void, restart, (void), { while(1); });
_F(void, halt, (void), { while(1); });
_F(void, power_off, (void), { while(1); });
_F(void, idle, (void), { __asm__ __volatile__ ("waiti 0" ::: "memory"); });
_F(void, heartbeat, (void), { });
_F(int, pcibios_fixup, (void), { return 0; });
_F(int, get_rtc_time, (time_t* t), { return 0; });
_F(int, set_rtc_time, (time_t t), { return 0; });
#if CONFIG_XTENSA_CALIBRATE_CCOUNT
_F(void, calibrate_ccount, (void),
{
printk ("ERROR: Cannot calibrate cpu frequency! Assuming 100MHz.\n");
ccount_per_jiffy = 100 * (1000000UL/HZ);
});
#endif

View File

@ -0,0 +1,482 @@
// TODO verify coprocessor handling
/*
* arch/xtensa/kernel/process.c
*
* Xtensa Processor version.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Chris Zankel <chris@zankel.net>
* Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Kevin Chea
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/elf.h>
#include <linux/init.h>
#include <linux/prctl.h>
#include <linux/init_task.h>
#include <linux/module.h>
#include <linux/mqueue.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/platform.h>
#include <asm/mmu.h>
#include <asm/irq.h>
#include <asm/atomic.h>
#include <asm/offsets.h>
#include <asm/coprocessor.h>
extern void ret_from_fork(void);
static struct fs_struct init_fs = INIT_FS;
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
struct mm_struct init_mm = INIT_MM(init_mm);
EXPORT_SYMBOL(init_mm);
union thread_union init_thread_union
__attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);
struct task_struct *current_set[NR_CPUS] = {&init_task, };
#if XCHAL_CP_NUM > 0
/*
* Coprocessor ownership.
*/
coprocessor_info_t coprocessor_info[] = {
{ 0, XTENSA_CPE_CP0_OFFSET },
{ 0, XTENSA_CPE_CP1_OFFSET },
{ 0, XTENSA_CPE_CP2_OFFSET },
{ 0, XTENSA_CPE_CP3_OFFSET },
{ 0, XTENSA_CPE_CP4_OFFSET },
{ 0, XTENSA_CPE_CP5_OFFSET },
{ 0, XTENSA_CPE_CP6_OFFSET },
{ 0, XTENSA_CPE_CP7_OFFSET },
};
#endif
/*
* Powermanagement idle function, if any is provided by the platform.
*/
void cpu_idle(void)
{
local_irq_enable();
/* endless idle loop with no priority at all */
while (1) {
while (!need_resched())
platform_idle();
preempt_enable();
schedule();
}
}
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
release_coprocessors(current); /* Empty macro if no CPs are defined */
}
void flush_thread(void)
{
release_coprocessors(current); /* Empty macro if no CPs are defined */
}
/*
* Copy thread.
*
* The stack layout for the new thread looks like this:
*
* +------------------------+ <- sp in childregs (= tos)
* | childregs |
* +------------------------+ <- thread.sp = sp in dummy-frame
* | dummy-frame | (saved in dummy-frame spill-area)
* +------------------------+
*
* We create a dummy frame to return to ret_from_fork:
* a0 points to ret_from_fork (simulating a call4)
* sp points to itself (thread.sp)
* a2, a3 are unused.
*
* Note: This is a pristine frame, so we don't need any spill region on top of
* childregs.
*/
int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
unsigned long unused,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs *childregs;
unsigned long tos;
int user_mode = user_mode(regs);
/* Set up new TSS. */
tos = (unsigned long)p->thread_info + THREAD_SIZE;
if (user_mode)
childregs = (struct pt_regs*)(tos - PT_USER_SIZE);
else
childregs = (struct pt_regs*)tos - 1;
*childregs = *regs;
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
*((int*)childregs - 3) = (unsigned long)childregs;
*((int*)childregs - 4) = 0;
childregs->areg[1] = tos;
childregs->areg[2] = 0;
p->set_child_tid = p->clear_child_tid = NULL;
p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
p->thread.sp = (unsigned long)childregs;
if (user_mode(regs)) {
int len = childregs->wmask & ~0xf;
childregs->areg[1] = usp;
memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
&regs->areg[XCHAL_NUM_AREGS - len/4], len);
if (clone_flags & CLONE_SETTLS)
childregs->areg[2] = childregs->areg[6];
} else {
/* In kernel space, we start a new thread with a new stack. */
childregs->wmask = 1;
}
return 0;
}
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
long retval;
__asm__ __volatile__
("mov a5, %4\n\t" /* preserve fn in a5 */
"mov a6, %3\n\t" /* preserve and setup arg in a6 */
"movi a2, %1\n\t" /* load __NR_clone for syscall*/
"mov a3, sp\n\t" /* sp check and sys_clone */
"mov a4, %5\n\t" /* load flags for syscall */
"syscall\n\t"
"beq a3, sp, 1f\n\t" /* branch if parent */
"callx4 a5\n\t" /* call fn */
"movi a2, %2\n\t" /* load __NR_exit for syscall */
"mov a3, a6\n\t" /* load fn return value */
"syscall\n"
"1:\n\t"
"mov %0, a2\n\t" /* parent returns zero */
:"=r" (retval)
:"i" (__NR_clone), "i" (__NR_exit),
"r" (arg), "r" (fn),
"r" (flags | CLONE_VM)
: "a2", "a3", "a4", "a5", "a6" );
return retval;
}
/*
* These bracket the sleeping functions..
*/
unsigned long get_wchan(struct task_struct *p)
{
unsigned long sp, pc;
unsigned long stack_page = (unsigned long) p->thread_info;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
sp = p->thread.sp;
pc = MAKE_PC_FROM_RA(p->thread.ra, p->thread.sp);
do {
if (sp < stack_page + sizeof(struct task_struct) ||
sp >= (stack_page + THREAD_SIZE) ||
pc == 0)
return 0;
if (!in_sched_functions(pc))
return pc;
/* Stack layout: sp-4: ra, sp-3: sp' */
pc = MAKE_PC_FROM_RA(*(unsigned long*)sp - 4, sp);
sp = *(unsigned long *)sp - 3;
} while (count++ < 16);
return 0;
}
/*
* do_copy_regs() gathers information from 'struct pt_regs' and
* 'current->thread.areg[]' to fill in the xtensa_gregset_t
* structure.
*
* xtensa_gregset_t and 'struct pt_regs' are vastly different formats
* of processor registers. Besides different ordering,
* xtensa_gregset_t contains non-live register information that
* 'struct pt_regs' does not. Exception handling (primarily) uses
* 'struct pt_regs'. Core files and ptrace use xtensa_gregset_t.
*
*/
void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
struct task_struct *tsk)
{
int i, n, wb_offset;
elfregs->xchal_config_id0 = XCHAL_HW_CONFIGID0;
elfregs->xchal_config_id1 = XCHAL_HW_CONFIGID1;
__asm__ __volatile__ ("rsr %0, 176\n" : "=a" (i));
elfregs->cpux = i;
__asm__ __volatile__ ("rsr %0, 208\n" : "=a" (i));
elfregs->cpuy = i;
/* Note: PS.EXCM is not set while user task is running; its
* being set in regs->ps is for exception handling convenience.
*/
elfregs->pc = regs->pc;
elfregs->ps = (regs->ps & ~XCHAL_PS_EXCM_MASK);
elfregs->exccause = regs->exccause;
elfregs->excvaddr = regs->excvaddr;
elfregs->windowbase = regs->windowbase;
elfregs->windowstart = regs->windowstart;
elfregs->lbeg = regs->lbeg;
elfregs->lend = regs->lend;
elfregs->lcount = regs->lcount;
elfregs->sar = regs->sar;
elfregs->syscall = regs->syscall;
/* Copy register file.
* The layout looks like this:
*
* | a0 ... a15 | Z ... Z | arX ... arY |
* current window unused saved frames
*/
memset (elfregs->ar, 0, sizeof(elfregs->ar));
wb_offset = regs->windowbase * 4;
n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16;
for (i = 0; i < n; i++)
elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i];
n = (regs->wmask >> 4) * 4;
for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--)
elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i];
}
void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
{
do_copy_regs ((xtensa_gregset_t *)elfregs, regs, current);
}
/* The inverse of do_copy_regs(). No error or sanity checking. */
void do_restore_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
struct task_struct *tsk)
{
int i, n, wb_offset;
/* Note: PS.EXCM is not set while user task is running; it
* needs to be set in regs->ps is for exception handling convenience.
*/
regs->pc = elfregs->pc;
regs->ps = (elfregs->ps | XCHAL_PS_EXCM_MASK);
regs->exccause = elfregs->exccause;
regs->excvaddr = elfregs->excvaddr;
regs->windowbase = elfregs->windowbase;
regs->windowstart = elfregs->windowstart;
regs->lbeg = elfregs->lbeg;
regs->lend = elfregs->lend;
regs->lcount = elfregs->lcount;
regs->sar = elfregs->sar;
regs->syscall = elfregs->syscall;
/* Clear everything. */
memset (regs->areg, 0, sizeof(regs->areg));
/* Copy regs from live window frame. */
wb_offset = regs->windowbase * 4;
n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16;
for (i = 0; i < n; i++)
regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i];
n = (regs->wmask >> 4) * 4;
for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--)
regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i];
}
/*
* do_save_fpregs() gathers information from 'struct pt_regs' and
* 'current->thread' to fill in the elf_fpregset_t structure.
*
* Core files and ptrace use elf_fpregset_t.
*/
void do_save_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
struct task_struct *tsk)
{
#if XCHAL_HAVE_CP
extern unsigned char _xtensa_reginfo_tables[];
extern unsigned _xtensa_reginfo_table_size;
int i;
unsigned long flags;
/* Before dumping coprocessor state from memory,
* ensure any live coprocessor contents for this
* task are first saved to memory:
*/
local_irq_save(flags);
for (i = 0; i < XCHAL_CP_MAX; i++) {
if (tsk == coprocessor_info[i].owner) {
enable_coprocessor(i);
save_coprocessor_registers(
tsk->thread.cp_save+coprocessor_info[i].offset,i);
disable_coprocessor(i);
}
}
local_irq_restore(flags);
/* Now dump coprocessor & extra state: */
memcpy((unsigned char*)fpregs,
_xtensa_reginfo_tables, _xtensa_reginfo_table_size);
memcpy((unsigned char*)fpregs + _xtensa_reginfo_table_size,
tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
#endif
}
/*
* The inverse of do_save_fpregs().
* Copies coprocessor and extra state from fpregs into regs and tsk->thread.
* Returns 0 on success, non-zero if layout doesn't match.
*/
int do_restore_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
struct task_struct *tsk)
{
#if XCHAL_HAVE_CP
extern unsigned char _xtensa_reginfo_tables[];
extern unsigned _xtensa_reginfo_table_size;
int i;
unsigned long flags;
/* Make sure save area layouts match.
* FIXME: in the future we could allow restoring from
* a different layout of the same registers, by comparing
* fpregs' table with _xtensa_reginfo_tables and matching
* entries and copying registers one at a time.
* Not too sure yet whether that's very useful.
*/
if( memcmp((unsigned char*)fpregs,
_xtensa_reginfo_tables, _xtensa_reginfo_table_size) ) {
return -1;
}
/* Before restoring coprocessor state from memory,
* ensure any live coprocessor contents for this
* task are first invalidated.
*/
local_irq_save(flags);
for (i = 0; i < XCHAL_CP_MAX; i++) {
if (tsk == coprocessor_info[i].owner) {
enable_coprocessor(i);
save_coprocessor_registers(
tsk->thread.cp_save+coprocessor_info[i].offset,i);
coprocessor_info[i].owner = 0;
disable_coprocessor(i);
}
}
local_irq_restore(flags);
/* Now restore coprocessor & extra state: */
memcpy(tsk->thread.cp_save,
(unsigned char*)fpregs + _xtensa_reginfo_table_size,
XTENSA_CP_EXTRA_SIZE);
#endif
return 0;
}
/*
* Fill in the CP structure for a core dump for a particular task.
*/
int
dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r)
{
/* see asm/coprocessor.h for this magic number 16 */
#if TOTAL_CPEXTRA_SIZE > 16
do_save_fpregs (r, regs, task);
/* For now, bit 16 means some extra state may be present: */
// FIXME!! need to track to return more accurate mask
return 0x10000 | XCHAL_CP_MASK;
#else
return 0; /* no coprocessors active on this processor */
#endif
}
/*
* Fill in the CP structure for a core dump.
* This includes any FPU coprocessor.
* Here, we dump all coprocessors, and other ("extra") custom state.
*
* This function is called by elf_core_dump() in fs/binfmt_elf.c
* (in which case 'regs' comes from calls to do_coredump, see signals.c).
*/
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
{
return dump_task_fpu(regs, current, r);
}

View File

@ -0,0 +1,407 @@
// TODO some minor issues
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Chris Zankel <chris@zankel.net>
* Scott Foehner<sfoehner@yahoo.com>,
* Kevin Chea
* Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/security.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/ptrace.h>
#include <asm/elf.h>
#define TEST_KERNEL // verify kernel operations FIXME: remove
/*
* Called by kernel/ptrace.c when detaching..
*
* Make sure single step bits etc are not set.
*/
void ptrace_disable(struct task_struct *child)
{
/* Nothing to do.. */
}
int sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
int ret = -EPERM;
lock_kernel();
#if 0
if ((int)request != 1)
printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
(int) request, (int) pid, (unsigned long) addr,
(unsigned long) data);
#endif
if (request == PTRACE_TRACEME) {
/* Are we already being traced? */
if (current->ptrace & PT_PTRACED)
goto out;
if ((ret = security_ptrace(current->parent, current)))
goto out;
/* Set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
ret = 0;
goto out;
}
ret = -ESRCH;
read_lock(&tasklist_lock);
child = find_task_by_pid(pid);
if (child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
goto out;
if (request == PTRACE_ATTACH) {
ret = ptrace_attach(child);
goto out_tsk;
}
if ((ret = ptrace_check_attach(child, request == PTRACE_KILL)) < 0)
goto out_tsk;
switch (request) {
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA:
{
unsigned long tmp;
int copied;
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
ret = -EIO;
if (copied != sizeof(tmp))
break;
ret = put_user(tmp,(unsigned long *) data);
goto out;
}
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR:
{
struct pt_regs *regs;
unsigned long tmp;
regs = xtensa_pt_regs(child);
tmp = 0; /* Default return value. */
switch(addr) {
case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
{
int ar = addr - REG_AR_BASE - regs->windowbase * 4;
ar &= (XCHAL_NUM_AREGS - 1);
if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
tmp = regs->areg[ar];
else
ret = -EIO;
break;
}
case REG_A_BASE ... REG_A_BASE + 15:
tmp = regs->areg[addr - REG_A_BASE];
break;
case REG_PC:
tmp = regs->pc;
break;
case REG_PS:
/* Note: PS.EXCM is not set while user task is running;
* its being set in regs is for exception handling
* convenience. */
tmp = (regs->ps & ~XCHAL_PS_EXCM_MASK);
break;
case REG_WB:
tmp = regs->windowbase;
break;
case REG_WS:
tmp = regs->windowstart;
break;
case REG_LBEG:
tmp = regs->lbeg;
break;
case REG_LEND:
tmp = regs->lend;
break;
case REG_LCOUNT:
tmp = regs->lcount;
break;
case REG_SAR:
tmp = regs->sar;
break;
case REG_DEPC:
tmp = regs->depc;
break;
case REG_EXCCAUSE:
tmp = regs->exccause;
break;
case REG_EXCVADDR:
tmp = regs->excvaddr;
break;
case SYSCALL_NR:
tmp = regs->syscall;
break;
default:
tmp = 0;
ret = -EIO;
goto out;
}
ret = put_user(tmp, (unsigned long *) data);
goto out;
}
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
if (access_process_vm(child, addr, &data, sizeof(data), 1)
== sizeof(data))
break;
ret = -EIO;
goto out;
case PTRACE_POKEUSR:
{
struct pt_regs *regs;
regs = xtensa_pt_regs(child);
switch (addr) {
case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
{
int ar = addr - REG_AR_BASE - regs->windowbase * 4;
if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data;
else
ret = -EIO;
break;
}
case REG_A_BASE ... REG_A_BASE + 15:
regs->areg[addr - REG_A_BASE] = data;
break;
case REG_PC:
regs->pc = data;
break;
case SYSCALL_NR:
regs->syscall = data;
break;
#ifdef TEST_KERNEL
case REG_WB:
regs->windowbase = data;
break;
case REG_WS:
regs->windowstart = data;
break;
#endif
default:
/* The rest are not allowed. */
ret = -EIO;
break;
}
break;
}
/* continue and stop at next (return from) syscall */
case PTRACE_SYSCALL:
case PTRACE_CONT: /* restart after signal. */
{
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* Make sure the single step bit is not set. */
child->ptrace &= ~PT_SINGLESTEP;
wake_up_process(child);
ret = 0;
break;
}
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL:
ret = 0;
if (child->state == EXIT_ZOMBIE) /* already dead */
break;
child->exit_code = SIGKILL;
child->ptrace &= ~PT_SINGLESTEP;
wake_up_process(child);
break;
case PTRACE_SINGLESTEP:
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->ptrace |= PT_SINGLESTEP;
child->exit_code = data;
wake_up_process(child);
ret = 0;
break;
case PTRACE_GETREGS:
{
/* 'data' points to user memory in which to write.
* Mainly due to the non-live register values, we
* reformat the register values into something more
* standard. For convenience, we use the handy
* elf_gregset_t format. */
xtensa_gregset_t format;
struct pt_regs *regs = xtensa_pt_regs(child);
do_copy_regs (&format, regs, child);
/* Now, copy to user space nice and easy... */
ret = 0;
if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t)))
ret = -EFAULT;
break;
}
case PTRACE_SETREGS:
{
/* 'data' points to user memory that contains the new
* values in the elf_gregset_t format. */
xtensa_gregset_t format;
struct pt_regs *regs = xtensa_pt_regs(child);
if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){
ret = -EFAULT;
break;
}
/* FIXME: Perhaps we want some sanity checks on
* these user-space values? See ARM version. Are
* debuggers a security concern? */
do_restore_regs (&format, regs, child);
ret = 0;
break;
}
case PTRACE_GETFPREGS:
{
/* 'data' points to user memory in which to write.
* For convenience, we use the handy
* elf_fpregset_t format. */
elf_fpregset_t fpregs;
struct pt_regs *regs = xtensa_pt_regs(child);
do_save_fpregs (&fpregs, regs, child);
/* Now, copy to user space nice and easy... */
ret = 0;
if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t)))
ret = -EFAULT;
break;
}
case PTRACE_SETFPREGS:
{
/* 'data' points to user memory that contains the new
* values in the elf_fpregset_t format.
*/
elf_fpregset_t fpregs;
struct pt_regs *regs = xtensa_pt_regs(child);
ret = 0;
if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) {
ret = -EFAULT;
break;
}
if (do_restore_fpregs (&fpregs, regs, child))
ret = -EIO;
break;
}
case PTRACE_GETFPREGSIZE:
/* 'data' points to 'unsigned long' set to the size
* of elf_fpregset_t
*/
ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data);
break;
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
default:
ret = ptrace_request(child, request, addr, data);
goto out;
}
out_tsk:
put_task_struct(child);
out:
unlock_kernel();
return ret;
}
void do_syscall_trace(void)
{
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/*
* The 0x80 provides a way for the tracing parent to distinguish
* between a syscall stop and SIGTRAP delivery
*/
ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
}

View File

@ -0,0 +1,226 @@
/*
* arch/xtensa/kernel/semaphore.c
*
* Generic semaphore code. Buyer beware. Do your own specific changes
* in <asm/semaphore-helper.h>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Chris Zankel <chris@zankel.net>
* Marc Gauthier<marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Kevin Chea
*/
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <asm/semaphore.h>
#include <asm/errno.h>
/*
* These two _must_ execute atomically wrt each other.
*/
static __inline__ void wake_one_more(struct semaphore * sem)
{
atomic_inc((atomic_t *)&sem->sleepers);
}
static __inline__ int waking_non_zero(struct semaphore *sem)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&semaphore_wake_lock, flags);
if (sem->sleepers > 0) {
sem->sleepers--;
ret = 1;
}
spin_unlock_irqrestore(&semaphore_wake_lock, flags);
return ret;
}
/*
* waking_non_zero_interruptible:
* 1 got the lock
* 0 go to sleep
* -EINTR interrupted
*
* We must undo the sem->count down_interruptible() increment while we are
* protected by the spinlock in order to make atomic this atomic_inc() with the
* atomic_read() in wake_one_more(), otherwise we can race. -arca
*/
static __inline__ int waking_non_zero_interruptible(struct semaphore *sem,
struct task_struct *tsk)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&semaphore_wake_lock, flags);
if (sem->sleepers > 0) {
sem->sleepers--;
ret = 1;
} else if (signal_pending(tsk)) {
atomic_inc(&sem->count);
ret = -EINTR;
}
spin_unlock_irqrestore(&semaphore_wake_lock, flags);
return ret;
}
/*
* waking_non_zero_trylock:
* 1 failed to lock
* 0 got the lock
*
* We must undo the sem->count down_trylock() increment while we are
* protected by the spinlock in order to make atomic this atomic_inc() with the
* atomic_read() in wake_one_more(), otherwise we can race. -arca
*/
static __inline__ int waking_non_zero_trylock(struct semaphore *sem)
{
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&semaphore_wake_lock, flags);
if (sem->sleepers <= 0)
atomic_inc(&sem->count);
else {
sem->sleepers--;
ret = 0;
}
spin_unlock_irqrestore(&semaphore_wake_lock, flags);
return ret;
}
spinlock_t semaphore_wake_lock;
/*
* Semaphores are implemented using a two-way counter:
* The "count" variable is decremented for each process
* that tries to sleep, while the "waking" variable is
* incremented when the "up()" code goes to wake up waiting
* processes.
*
* Notably, the inline "up()" and "down()" functions can
* efficiently test if they need to do any extra work (up
* needs to do something only if count was negative before
* the increment operation.
*
* waking_non_zero() (from asm/semaphore.h) must execute
* atomically.
*
* When __up() is called, the count was negative before
* incrementing it, and we need to wake up somebody.
*
* This routine adds one to the count of processes that need to
* wake up and exit. ALL waiting processes actually wake up but
* only the one that gets to the "waking" field first will gate
* through and acquire the semaphore. The others will go back
* to sleep.
*
* Note that these functions are only called when there is
* contention on the lock, and as such all this is the
* "non-critical" part of the whole semaphore business. The
* critical part is the inline stuff in <asm/semaphore.h>
* where we want to avoid any extra jumps and calls.
*/
void __up(struct semaphore *sem)
{
wake_one_more(sem);
wake_up(&sem->wait);
}
/*
* Perform the "down" function. Return zero for semaphore acquired,
* return negative for signalled out of the function.
*
* If called from __down, the return is ignored and the wait loop is
* not interruptible. This means that a task waiting on a semaphore
* using "down()" cannot be killed until someone does an "up()" on
* the semaphore.
*
* If called from __down_interruptible, the return value gets checked
* upon return. If the return value is negative then the task continues
* with the negative value in the return register (it can be tested by
* the caller).
*
* Either form may be used in conjunction with "up()".
*
*/
#define DOWN_VAR \
struct task_struct *tsk = current; \
wait_queue_t wait; \
init_waitqueue_entry(&wait, tsk);
#define DOWN_HEAD(task_state) \
\
\
tsk->state = (task_state); \
add_wait_queue(&sem->wait, &wait); \
\
/* \
* Ok, we're set up. sem->count is known to be less than zero \
* so we must wait. \
* \
* We can let go the lock for purposes of waiting. \
* We re-acquire it after awaking so as to protect \
* all semaphore operations. \
* \
* If "up()" is called before we call waking_non_zero() then \
* we will catch it right away. If it is called later then \
* we will have to go through a wakeup cycle to catch it. \
* \
* Multiple waiters contend for the semaphore lock to see \
* who gets to gate through and who has to wait some more. \
*/ \
for (;;) {
#define DOWN_TAIL(task_state) \
tsk->state = (task_state); \
} \
tsk->state = TASK_RUNNING; \
remove_wait_queue(&sem->wait, &wait);
void __sched __down(struct semaphore * sem)
{
DOWN_VAR
DOWN_HEAD(TASK_UNINTERRUPTIBLE)
if (waking_non_zero(sem))
break;
schedule();
DOWN_TAIL(TASK_UNINTERRUPTIBLE)
}
int __sched __down_interruptible(struct semaphore * sem)
{
int ret = 0;
DOWN_VAR
DOWN_HEAD(TASK_INTERRUPTIBLE)
ret = waking_non_zero_interruptible(sem, tsk);
if (ret)
{
if (ret == 1)
/* ret != 0 only if we get interrupted -arca */
ret = 0;
break;
}
schedule();
DOWN_TAIL(TASK_INTERRUPTIBLE)
return ret;
}
int __down_trylock(struct semaphore * sem)
{
return waking_non_zero_trylock(sem);
}

View File

@ -0,0 +1,520 @@
/*
* arch/xtensa/setup.c
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1995 Linus Torvalds
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Kevin Chea
* Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/tty.h>
#include <linux/bootmem.h>
#include <linux/kernel.h>
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
# include <linux/console.h>
#endif
#ifdef CONFIG_RTC
# include <linux/timex.h>
#endif
#ifdef CONFIG_PROC_FS
# include <linux/seq_file.h>
#endif
#include <asm/system.h>
#include <asm/bootparam.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/timex.h>
#include <asm/platform.h>
#include <asm/page.h>
#include <asm/setup.h>
#include <xtensa/config/system.h>
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
struct screen_info screen_info = { 0, 24, 0, 0, 0, 80, 0, 0, 0, 24, 1, 16};
#endif
#ifdef CONFIG_BLK_DEV_FD
extern struct fd_ops no_fd_ops;
struct fd_ops *fd_ops;
#endif
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
extern struct ide_ops no_ide_ops;
struct ide_ops *ide_ops;
#endif
extern struct rtc_ops no_rtc_ops;
struct rtc_ops *rtc_ops;
#ifdef CONFIG_PC_KEYB
extern struct kbd_ops no_kbd_ops;
struct kbd_ops *kbd_ops;
#endif
#ifdef CONFIG_BLK_DEV_INITRD
extern void *initrd_start;
extern void *initrd_end;
extern void *__initrd_start;
extern void *__initrd_end;
int initrd_is_mapped = 0;
extern int initrd_below_start_ok;
#endif
unsigned char aux_device_present;
extern unsigned long loops_per_jiffy;
/* Command line specified as configuration option. */
static char command_line[COMMAND_LINE_SIZE];
#ifdef CONFIG_CMDLINE_BOOL
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
#endif
sysmem_info_t __initdata sysmem;
#ifdef CONFIG_BLK_DEV_INITRD
int initrd_is_mapped;
#endif
extern void init_mmu(void);
/*
* Boot parameter parsing.
*
* The Xtensa port uses a list of variable-sized tags to pass data to
* the kernel. The first tag must be a BP_TAG_FIRST tag for the list
* to be recognised. The list is terminated with a zero-sized
* BP_TAG_LAST tag.
*/
typedef struct tagtable {
u32 tag;
int (*parse)(const bp_tag_t*);
} tagtable_t;
#define __tagtable(tag, fn) static tagtable_t __tagtable_##fn \
__attribute__((unused, __section__(".taglist"))) = { tag, fn }
/* parse current tag */
static int __init parse_tag_mem(const bp_tag_t *tag)
{
meminfo_t *mi = (meminfo_t*)(tag->data);
if (mi->type != MEMORY_TYPE_CONVENTIONAL)
return -1;
if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) {
printk(KERN_WARNING
"Ignoring memory bank 0x%08lx size %ldKB\n",
(unsigned long)mi->start,
(unsigned long)mi->end - (unsigned long)mi->start);
return -EINVAL;
}
sysmem.bank[sysmem.nr_banks].type = mi->type;
sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start);
sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_SIZE;
sysmem.nr_banks++;
return 0;
}
__tagtable(BP_TAG_MEMORY, parse_tag_mem);
#ifdef CONFIG_BLK_DEV_INITRD
static int __init parse_tag_initrd(const bp_tag_t* tag)
{
meminfo_t* mi;
mi = (meminfo_t*)(tag->data);
initrd_start = (void*)(mi->start);
initrd_end = (void*)(mi->end);
return 0;
}
__tagtable(BP_TAG_INITRD, parse_tag_initrd);
#endif /* CONFIG_BLK_DEV_INITRD */
static int __init parse_tag_cmdline(const bp_tag_t* tag)
{
strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE);
command_line[COMMAND_LINE_SIZE - 1] = '\0';
return 0;
}
__tagtable(BP_TAG_COMMAND_LINE, parse_tag_cmdline);
static int __init parse_bootparam(const bp_tag_t* tag)
{
extern tagtable_t __tagtable_begin, __tagtable_end;
tagtable_t *t;
/* Boot parameters must start with a BP_TAG_FIRST tag. */
if (tag->id != BP_TAG_FIRST) {
printk(KERN_WARNING "Invalid boot parameters!\n");
return 0;
}
tag = (bp_tag_t*)((unsigned long)tag + sizeof(bp_tag_t) + tag->size);
/* Parse all tags. */
while (tag != NULL && tag->id != BP_TAG_LAST) {
for (t = &__tagtable_begin; t < &__tagtable_end; t++) {
if (tag->id == t->tag) {
t->parse(tag);
break;
}
}
if (t == &__tagtable_end)
printk(KERN_WARNING "Ignoring tag "
"0x%08x\n", tag->id);
tag = (bp_tag_t*)((unsigned long)(tag + 1) + tag->size);
}
return 0;
}
/*
* Initialize architecture. (Early stage)
*/
void __init init_arch(bp_tag_t *bp_start)
{
#ifdef CONFIG_BLK_DEV_INITRD
initrd_start = &__initrd_start;
initrd_end = &__initrd_end;
#endif
sysmem.nr_banks = 0;
#ifdef CONFIG_CMDLINE_BOOL
strcpy(command_line, default_command_line);
#endif
/* Parse boot parameters */
if (bp_start)
parse_bootparam(bp_start);
if (sysmem.nr_banks == 0) {
sysmem.nr_banks = 1;
sysmem.bank[0].start = PLATFORM_DEFAULT_MEM_START;
sysmem.bank[0].end = PLATFORM_DEFAULT_MEM_START
+ PLATFORM_DEFAULT_MEM_SIZE;
}
/* Early hook for platforms */
platform_init(bp_start);
/* Initialize MMU. */
init_mmu();
}
/*
* Initialize system. Setup memory and reserve regions.
*/
extern char _end;
extern char _stext;
extern char _WindowVectors_text_start;
extern char _WindowVectors_text_end;
extern char _DebugInterruptVector_literal_start;
extern char _DebugInterruptVector_text_end;
extern char _KernelExceptionVector_literal_start;
extern char _KernelExceptionVector_text_end;
extern char _UserExceptionVector_literal_start;
extern char _UserExceptionVector_text_end;
extern char _DoubleExceptionVector_literal_start;
extern char _DoubleExceptionVector_text_end;
void __init setup_arch(char **cmdline_p)
{
extern int mem_reserve(unsigned long, unsigned long, int);
extern void bootmem_init(void);
memcpy(saved_command_line, command_line, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
*cmdline_p = command_line;
/* Reserve some memory regions */
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start < initrd_end) {
initrd_is_mapped = mem_reserve(__pa(initrd_start),
__pa(initrd_end), 0);
initrd_below_start_ok = 1;
} else {
initrd_start = 0;
}
#endif
mem_reserve(__pa(&_stext),__pa(&_end), 1);
mem_reserve(__pa(&_WindowVectors_text_start),
__pa(&_WindowVectors_text_end), 0);
mem_reserve(__pa(&_DebugInterruptVector_literal_start),
__pa(&_DebugInterruptVector_text_end), 0);
mem_reserve(__pa(&_KernelExceptionVector_literal_start),
__pa(&_KernelExceptionVector_text_end), 0);
mem_reserve(__pa(&_UserExceptionVector_literal_start),
__pa(&_UserExceptionVector_text_end), 0);
mem_reserve(__pa(&_DoubleExceptionVector_literal_start),
__pa(&_DoubleExceptionVector_text_end), 0);
bootmem_init();
platform_setup(cmdline_p);
paging_init();
#ifdef CONFIG_VT
# if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
# elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
# endif
#endif
#if CONFIG_PCI
platform_pcibios_init();
#endif
}
void machine_restart(char * cmd)
{
platform_restart();
}
void machine_halt(void)
{
platform_halt();
while (1);
}
void machine_power_off(void)
{
platform_power_off();
while (1);
}
#ifdef CONFIG_PROC_FS
/*
* Display some core information through /proc/cpuinfo.
*/
static int
c_show(struct seq_file *f, void *slot)
{
/* high-level stuff */
seq_printf(f,"processor\t: 0\n"
"vendor_id\t: Tensilica\n"
"model\t\t: Xtensa " XCHAL_HW_RELEASE_NAME "\n"
"core ID\t\t: " XCHAL_CORE_ID "\n"
"build ID\t: 0x%x\n"
"byte order\t: %s\n"
"cpu MHz\t\t: %lu.%02lu\n"
"bogomips\t: %lu.%02lu\n",
XCHAL_BUILD_UNIQUE_ID,
XCHAL_HAVE_BE ? "big" : "little",
CCOUNT_PER_JIFFY/(1000000/HZ),
(CCOUNT_PER_JIFFY/(10000/HZ)) % 100,
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
seq_printf(f,"flags\t\t: "
#if XCHAL_HAVE_NMI
"nmi "
#endif
#if XCHAL_HAVE_DEBUG
"debug "
# if XCHAL_HAVE_OCD
"ocd "
# endif
#endif
#if XCHAL_HAVE_DENSITY
"density "
#endif
#if XCHAL_HAVE_BOOLEANS
"boolean "
#endif
#if XCHAL_HAVE_LOOPS
"loop "
#endif
#if XCHAL_HAVE_NSA
"nsa "
#endif
#if XCHAL_HAVE_MINMAX
"minmax "
#endif
#if XCHAL_HAVE_SEXT
"sext "
#endif
#if XCHAL_HAVE_CLAMPS
"clamps "
#endif
#if XCHAL_HAVE_MAC16
"mac16 "
#endif
#if XCHAL_HAVE_MUL16
"mul16 "
#endif
#if XCHAL_HAVE_MUL32
"mul32 "
#endif
#if XCHAL_HAVE_MUL32_HIGH
"mul32h "
#endif
#if XCHAL_HAVE_FP
"fpu "
#endif
"\n");
/* Registers. */
seq_printf(f,"physical aregs\t: %d\n"
"misc regs\t: %d\n"
"ibreak\t\t: %d\n"
"dbreak\t\t: %d\n",
XCHAL_NUM_AREGS,
XCHAL_NUM_MISC_REGS,
XCHAL_NUM_IBREAK,
XCHAL_NUM_DBREAK);
/* Interrupt. */
seq_printf(f,"num ints\t: %d\n"
"ext ints\t: %d\n"
"int levels\t: %d\n"
"timers\t\t: %d\n"
"debug level\t: %d\n",
XCHAL_NUM_INTERRUPTS,
XCHAL_NUM_EXTINTERRUPTS,
XCHAL_NUM_INTLEVELS,
XCHAL_NUM_TIMERS,
XCHAL_DEBUGLEVEL);
/* Coprocessors */
#if XCHAL_HAVE_CP
seq_printf(f, "coprocessors\t: %d\n", XCHAL_CP_NUM);
#else
seq_printf(f, "coprocessors\t: none\n");
#endif
/* {I,D}{RAM,ROM} and XLMI */
seq_printf(f,"inst ROMs\t: %d\n"
"inst RAMs\t: %d\n"
"data ROMs\t: %d\n"
"data RAMs\t: %d\n"
"XLMI ports\t: %d\n",
XCHAL_NUM_IROM,
XCHAL_NUM_IRAM,
XCHAL_NUM_DROM,
XCHAL_NUM_DRAM,
XCHAL_NUM_XLMI);
/* Cache */
seq_printf(f,"icache line size: %d\n"
"icache ways\t: %d\n"
"icache size\t: %d\n"
"icache flags\t: "
#if XCHAL_ICACHE_LINE_LOCKABLE
"lock"
#endif
"\n"
"dcache line size: %d\n"
"dcache ways\t: %d\n"
"dcache size\t: %d\n"
"dcache flags\t: "
#if XCHAL_DCACHE_IS_WRITEBACK
"writeback"
#endif
#if XCHAL_DCACHE_LINE_LOCKABLE
"lock"
#endif
"\n",
XCHAL_ICACHE_LINESIZE,
XCHAL_ICACHE_WAYS,
XCHAL_ICACHE_SIZE,
XCHAL_DCACHE_LINESIZE,
XCHAL_DCACHE_WAYS,
XCHAL_DCACHE_SIZE);
/* MMU */
seq_printf(f,"ASID bits\t: %d\n"
"ASID invalid\t: %d\n"
"ASID kernel\t: %d\n"
"rings\t\t: %d\n"
"itlb ways\t: %d\n"
"itlb AR ways\t: %d\n"
"dtlb ways\t: %d\n"
"dtlb AR ways\t: %d\n",
XCHAL_MMU_ASID_BITS,
XCHAL_MMU_ASID_INVALID,
XCHAL_MMU_ASID_KERNEL,
XCHAL_MMU_RINGS,
XCHAL_ITLB_WAYS,
XCHAL_ITLB_ARF_WAYS,
XCHAL_DTLB_WAYS,
XCHAL_DTLB_ARF_WAYS);
return 0;
}
/*
* We show only CPU #0 info.
*/
static void *
c_start(struct seq_file *f, loff_t *pos)
{
return (void *) ((*pos == 0) ? (void *)1 : NULL);
}
static void *
c_next(struct seq_file *f, void *v, loff_t *pos)
{
return NULL;
}
static void
c_stop(struct seq_file *f, void *v)
{
}
struct seq_operations cpuinfo_op =
{
start: c_start,
next: c_next,
stop: c_stop,
show: c_show
};
#endif /* CONFIG_PROC_FS */

View File

@ -0,0 +1,713 @@
// TODO coprocessor stuff
/*
* linux/arch/xtensa/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
* 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
*
* Joe Taylor <joe@tensilica.com>
* Chris Zankel <chris@zankel.net>
*
*
*
*/
#include <xtensa/config/core.h>
#include <xtensa/hal.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/personality.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
#define DEBUG_SIG 0
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options,
struct rusage * ru);
asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
extern struct task_struct *coproc_owners[];
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
int sys_sigsuspend(struct pt_regs *regs)
{
old_sigset_t mask = (old_sigset_t) regs->areg[3];
sigset_t saveset;
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
regs->areg[2] = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (do_signal(regs, &saveset))
return -EINTR;
}
}
asmlinkage int
sys_rt_sigsuspend(struct pt_regs *regs)
{
sigset_t *unewset = (sigset_t *) regs->areg[4];
size_t sigsetsize = (size_t) regs->areg[3];
sigset_t saveset, newset;
/* XXX: Don't preclude handling different sized sigset_t's. */
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
if (copy_from_user(&newset, unewset, sizeof(newset)))
return -EFAULT;
sigdelsetmask(&newset, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked;
current->blocked = newset;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
regs->areg[2] = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (do_signal(regs, &saveset))
return -EINTR;
}
}
asmlinkage int
sys_sigaction(int sig, const struct old_sigaction *act,
struct old_sigaction *oact)
{
struct k_sigaction new_ka, old_ka;
int ret;
if (act) {
old_sigset_t mask;
if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
__get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
return -EFAULT;
__get_user(new_ka.sa.sa_flags, &act->sa_flags);
__get_user(mask, &act->sa_mask);
siginitset(&new_ka.sa.sa_mask, mask);
}
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
if (!ret && oact) {
if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
__put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
return -EFAULT;
__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
}
return ret;
}
asmlinkage int
sys_sigaltstack(struct pt_regs *regs)
{
const stack_t *uss = (stack_t *) regs->areg[4];
stack_t *uoss = (stack_t *) regs->areg[3];
if (regs->depc > 64)
panic ("Double exception sys_sigreturn\n");
return do_sigaltstack(uss, uoss, regs->areg[1]);
}
/*
* Do a signal return; undo the signal stack.
*/
struct sigframe
{
struct sigcontext sc;
struct _cpstate cpstate;
unsigned long extramask[_NSIG_WORDS-1];
unsigned char retcode[6];
unsigned int reserved[4]; /* Reserved area for chaining */
unsigned int window[4]; /* Window of 4 registers for initial context */
};
struct rt_sigframe
{
struct siginfo info;
struct ucontext uc;
struct _cpstate cpstate;
unsigned char retcode[6];
unsigned int reserved[4]; /* Reserved area for chaining */
unsigned int window[4]; /* Window of 4 registers for initial context */
};
extern void release_all_cp (struct task_struct *);
// FIXME restore_cpextra
static inline int
restore_cpextra (struct _cpstate *buf)
{
#if 0
/* The signal handler may have used coprocessors in which
* case they are still enabled. We disable them to force a
* reloading of the original task's CP state by the lazy
* context-switching mechanisms of CP exception handling.
* Also, we essentially discard any coprocessor state that the
* signal handler created. */
struct task_struct *tsk = current;
release_all_cp(tsk);
return __copy_from_user(tsk->thread.cpextra, buf, TOTAL_CPEXTRA_SIZE);
#endif
return 0;
}
/* Note: We don't copy double exception 'tregs', we have to finish double exc. first before we return to signal handler! This dbl.exc.handler might cause another double exception, but I think we are fine as the situation is the same as if we had returned to the signal handerl and got an interrupt immediately...
*/
static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
{
struct thread_struct *thread;
unsigned int err = 0;
unsigned long ps;
struct _cpstate *buf;
#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
COPY(pc);
COPY(depc);
COPY(wmask);
COPY(lbeg);
COPY(lend);
COPY(lcount);
COPY(sar);
COPY(windowbase);
COPY(windowstart);
#undef COPY
/* For PS, restore only PS.CALLINC.
* Assume that all other bits are either the same as for the signal
* handler, or the user mode value doesn't matter (e.g. PS.OWB).
*/
err |= __get_user(ps, &sc->sc_ps);
regs->ps = (regs->ps & ~XCHAL_PS_CALLINC_MASK)
| (ps & XCHAL_PS_CALLINC_MASK);
/* Additional corruption checks */
if ((regs->windowbase >= (XCHAL_NUM_AREGS/4))
|| ((regs->windowstart & ~((1<<(XCHAL_NUM_AREGS/4)) - 1)) != 0) )
err = 1;
if ((regs->lcount > 0)
&& ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )
err = 1;
/* Restore extended register state.
* See struct thread_struct in processor.h.
*/
thread = &current->thread;
err |= __copy_from_user (regs->areg, sc->sc_areg, XCHAL_NUM_AREGS*4);
err |= __get_user(buf, &sc->sc_cpstate);
if (buf) {
if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
goto badframe;
err |= restore_cpextra(buf);
}
regs->syscall = -1; /* disable syscall checks */
return err;
badframe:
return 1;
}
static inline void
flush_my_cpstate(struct task_struct *tsk)
{
unsigned long flags;
local_irq_save(flags);
#if 0 // FIXME
for (i = 0; i < XCHAL_CP_NUM; i++) {
if (tsk == coproc_owners[i]) {
xthal_validate_cp(i);
xthal_save_cpregs(tsk->thread.cpregs_ptr[i], i);
/* Invalidate and "disown" the cp to allow
* callers the chance to reset cp state in the
* task_struct. */
xthal_invalidate_cp(i);
coproc_owners[i] = 0;
}
}
#endif
local_irq_restore(flags);
}
/* Return codes:
0: nothing saved
1: stuff to save, successful
-1: stuff to save, error happened
*/
static int
save_cpextra (struct _cpstate *buf)
{
#if (XCHAL_EXTRA_SA_SIZE == 0) && (XCHAL_CP_NUM == 0)
return 0;
#else
/* FIXME: If a task has never used a coprocessor, there is
* no need to save and restore anything. Tracking this
* information would allow us to optimize this section.
* Perhaps we can use current->used_math or (current->flags &
* PF_USEDFPU) or define a new field in the thread
* structure. */
/* We flush any live, task-owned cp state to the task_struct,
* then copy it all to the sigframe. Then we clear all
* cp/extra state in the task_struct, effectively
* clearing/resetting all cp/extra state for the signal
* handler (cp-exception handling will load these new values
* into the cp/extra registers.) This step is important for
* things like a floating-point cp, where the OS must reset
* the FCR to the default rounding mode. */
int err = 0;
struct task_struct *tsk = current;
flush_my_cpstate(tsk);
/* Note that we just copy everything: 'extra' and 'cp' state together.*/
err |= __copy_to_user(buf, tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
memset(tsk->thread.cp_save, 0, XTENSA_CP_EXTRA_SIZE);
#if (XTENSA_CP_EXTRA_SIZE == 0)
#error Sanity check on memset above, cpextra_size should not be zero.
#endif
return err ? -1 : 1;
#endif
}
static int
setup_sigcontext(struct sigcontext *sc, struct _cpstate *cpstate,
struct pt_regs *regs, unsigned long mask)
{
struct thread_struct *thread;
int err = 0;
//printk("setup_sigcontext\n");
#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
COPY(pc);
COPY(ps);
COPY(depc);
COPY(wmask);
COPY(lbeg);
COPY(lend);
COPY(lcount);
COPY(sar);
COPY(windowbase);
COPY(windowstart);
#undef COPY
/* Save extended register state.
* See struct thread_struct in processor.h.
*/
thread = &current->thread;
err |= __copy_to_user (sc->sc_areg, regs->areg, XCHAL_NUM_AREGS * 4);
err |= save_cpextra(cpstate);
err |= __put_user(err ? NULL : cpstate, &sc->sc_cpstate);
/* non-iBCS2 extensions.. */
err |= __put_user(mask, &sc->oldmask);
return err;
}
asmlinkage int sys_sigreturn(struct pt_regs *regs)
{
struct sigframe *frame = (struct sigframe *)regs->areg[1];
sigset_t set;
if (regs->depc > 64)
panic ("Double exception sys_sigreturn\n");
if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__get_user(set.sig[0], &frame->sc.oldmask)
|| (_NSIG_WORDS > 1
&& __copy_from_user(&set.sig[1], &frame->extramask,
sizeof(frame->extramask))))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->sc))
goto badframe;
return regs->areg[2];
badframe:
force_sig(SIGSEGV, current);
return 0;
}
asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
{
struct rt_sigframe *frame = (struct rt_sigframe *)regs->areg[1];
sigset_t set;
stack_t st;
int ret;
if (regs->depc > 64)
{
printk("!!!!!!! DEPC !!!!!!!\n");
return 0;
}
if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
ret = regs->areg[2];
if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
goto badframe;
/* It is more difficult to avoid calling this function than to
call it and ignore errors. */
do_sigaltstack(&st, NULL, regs->areg[1]);
return ret;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/*
* Set up a signal frame.
*/
/*
* Determine which stack to use..
*/
static inline void *
get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
{
if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp))
sp = current->sas_ss_sp + current->sas_ss_size;
return (void *)((sp - frame_size) & -16ul);
}
#define USE_SIGRETURN 0
#define USE_RT_SIGRETURN 1
static int
gen_return_code(unsigned char *codemem, unsigned int use_rt_sigreturn)
{
unsigned int retcall;
int err = 0;
#if 0
/* Ignoring SA_RESTORER for now; it's supposed to be obsolete,
* and the xtensa glibc doesn't use it.
*/
if (ka->sa.sa_flags & SA_RESTORER) {
regs->pr = (unsigned long) ka->sa.sa_restorer;
} else
#endif /* 0 */
{
#if (__NR_sigreturn > 255) || (__NR_rt_sigreturn > 255)
/* The 12-bit immediate is really split up within the 24-bit MOVI
* instruction. As long as the above system call numbers fit within
* 8-bits, the following code works fine. See the Xtensa ISA for
* details.
*/
#error Generating the MOVI instruction below breaks!
#endif
retcall = use_rt_sigreturn ? __NR_rt_sigreturn : __NR_sigreturn;
#ifdef __XTENSA_EB__ /* Big Endian version */
/* Generate instruction: MOVI a2, retcall */
err |= __put_user(0x22, &codemem[0]);
err |= __put_user(0x0a, &codemem[1]);
err |= __put_user(retcall, &codemem[2]);
/* Generate instruction: SYSCALL */
err |= __put_user(0x00, &codemem[3]);
err |= __put_user(0x05, &codemem[4]);
err |= __put_user(0x00, &codemem[5]);
#elif defined __XTENSA_EL__ /* Little Endian version */
/* Generate instruction: MOVI a2, retcall */
err |= __put_user(0x22, &codemem[0]);
err |= __put_user(0xa0, &codemem[1]);
err |= __put_user(retcall, &codemem[2]);
/* Generate instruction: SYSCALL */
err |= __put_user(0x00, &codemem[3]);
err |= __put_user(0x50, &codemem[4]);
err |= __put_user(0x00, &codemem[5]);
#else
#error Must use compiler for Xtensa processors.
#endif
}
/* Flush generated code out of the data cache */
if (err == 0)
__flush_invalidate_cache_range((unsigned long)codemem, 6UL);
return err;
}
static void
set_thread_state(struct pt_regs *regs, void *stack, unsigned char *retaddr,
void *handler, unsigned long arg1, void *arg2, void *arg3)
{
/* Set up registers for signal handler */
start_thread(regs, (unsigned long) handler, (unsigned long) stack);
/* Set up a stack frame for a call4
* Note: PS.CALLINC is set to one by start_thread
*/
regs->areg[4] = (((unsigned long) retaddr) & 0x3fffffff) | 0x40000000;
regs->areg[6] = arg1;
regs->areg[7] = (unsigned long) arg2;
regs->areg[8] = (unsigned long) arg3;
}
static void setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs *regs)
{
struct sigframe *frame;
int err = 0;
int signal;
frame = get_sigframe(ka, regs->areg[1], sizeof(*frame));
if (regs->depc > 64)
{
printk("!!!!!!! DEPC !!!!!!!\n");
return;
}
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
signal = current_thread_info()->exec_domain
&& current_thread_info()->exec_domain->signal_invmap
&& sig < 32
? current_thread_info()->exec_domain->signal_invmap[sig]
: sig;
err |= setup_sigcontext(&frame->sc, &frame->cpstate, regs, set->sig[0]);
if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
/* Create sys_sigreturn syscall in stack frame */
err |= gen_return_code(frame->retcode, USE_SIGRETURN);
if (err)
goto give_sigsegv;
/* Create signal handler execution context.
* Return context not modified until this point.
*/
set_thread_state(regs, frame, frame->retcode,
ka->sa.sa_handler, signal, &frame->sc, NULL);
/* Set access mode to USER_DS. Nomenclature is outdated, but
* functionality is used in uaccess.h
*/
set_fs(USER_DS);
#if DEBUG_SIG
printk("SIG deliver (%s:%d): signal=%d sp=%p pc=%08x\n",
current->comm, current->pid, signal, frame, regs->pc);
#endif
return;
give_sigsegv:
if (sig == SIGSEGV)
ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
}
static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe *frame;
int err = 0;
int signal;
frame = get_sigframe(ka, regs->areg[1], sizeof(*frame));
if (regs->depc > 64)
panic ("Double exception sys_sigreturn\n");
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
signal = current_thread_info()->exec_domain
&& current_thread_info()->exec_domain->signal_invmap
&& sig < 32
? current_thread_info()->exec_domain->signal_invmap[sig]
: sig;
err |= copy_siginfo_to_user(&frame->info, info);
/* Create the ucontext. */
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(0, &frame->uc.uc_link);
err |= __put_user((void *)current->sas_ss_sp,
&frame->uc.uc_stack.ss_sp);
err |= __put_user(sas_ss_flags(regs->areg[1]),
&frame->uc.uc_stack.ss_flags);
err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->cpstate,
regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
/* Create sys_rt_sigreturn syscall in stack frame */
err |= gen_return_code(frame->retcode, USE_RT_SIGRETURN);
if (err)
goto give_sigsegv;
/* Create signal handler execution context.
* Return context not modified until this point.
*/
set_thread_state(regs, frame, frame->retcode,
ka->sa.sa_handler, signal, &frame->info, &frame->uc);
/* Set access mode to USER_DS. Nomenclature is outdated, but
* functionality is used in uaccess.h
*/
set_fs(USER_DS);
#if DEBUG_SIG
printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n",
current->comm, current->pid, signal, frame, regs->pc);
#endif
return;
give_sigsegv:
if (sig == SIGSEGV)
ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Note that we go through the signals twice: once to check the signals that
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
int do_signal(struct pt_regs *regs, sigset_t *oldset)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
if (!oldset)
oldset = &current->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
/* Are we from a system call? */
if (regs->syscall >= 0) {
/* If so, check system call restarting.. */
switch (regs->areg[2]) {
case ERESTARTNOHAND:
case ERESTART_RESTARTBLOCK:
regs->areg[2] = -EINTR;
break;
case ERESTARTSYS:
if (!(ka.sa.sa_flags & SA_RESTART)) {
regs->areg[2] = -EINTR;
break;
}
/* fallthrough */
case ERESTARTNOINTR:
regs->areg[2] = regs->syscall;
regs->pc -= 3;
}
}
if (signr == 0)
return 0; /* no signals delivered */
/* Whee! Actually deliver the signal. */
/* Set up the stack frame */
if (ka.sa.sa_flags & SA_SIGINFO)
setup_rt_frame(signr, &ka, &info, oldset, regs);
else
setup_frame(signr, &ka, oldset, regs);
if (ka.sa.sa_flags & SA_ONESHOT)
ka.sa.sa_handler = SIG_DFL;
if (!(ka.sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked, &ka.sa.sa_mask);
sigaddset(&current->blocked, signr);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return 1;
}

View File

@ -0,0 +1,418 @@
/*
* arch/xtensa/kernel/syscall.c
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
* Copyright (C) 2000 Silicon Graphics, Inc.
* Copyright (C) 1995 - 2000 by Ralf Baechle
*
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Chris Zankel <chris@zankel.net>
* Kevin Chea
*
*/
#define DEBUG 0
#include <linux/config.h>
#include <linux/linkage.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/mman.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/utsname.h>
#include <linux/unistd.h>
#include <linux/stringify.h>
#include <linux/syscalls.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <asm/ptrace.h>
#include <asm/signal.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <asm/mman.h>
#include <asm/shmparam.h>
#include <asm/page.h>
#include <asm/ipc.h>
extern void do_syscall_trace(void);
typedef int (*syscall_t)(void *a0,...);
extern int (*do_syscalls)(struct pt_regs *regs, syscall_t fun,
int narg);
extern syscall_t sys_call_table[];
extern unsigned char sys_narg_table[];
/*
* sys_pipe() is the normal C calling standard for creating a pipe. It's not
* the way unix traditional does this, though.
*/
int sys_pipe(int __user *userfds)
{
int fd[2];
int error;
error = do_pipe(fd);
if (!error) {
if (copy_to_user(userfds, fd, 2 * sizeof(int)))
error = -EFAULT;
}
return error;
}
/*
* Common code for old and new mmaps.
*/
static inline long do_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
int error = -EBADF;
struct file * file = NULL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
if (!(flags & MAP_ANONYMOUS)) {
file = fget(fd);
if (!file)
goto out;
}
down_write(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->mm->mmap_sem);
if (file)
fput(file);
out:
return error;
}
unsigned long old_mmap(unsigned long addr, size_t len, int prot,
int flags, int fd, off_t offset)
{
return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
}
long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
unsigned long flags, unsigned long fd, unsigned long pgoff)
{
return do_mmap2(addr, len, prot, flags, fd, pgoff);
}
int sys_fork(struct pt_regs *regs)
{
return do_fork(SIGCHLD, regs->areg[1], regs, 0, NULL, NULL);
}
int sys_vfork(struct pt_regs *regs)
{
return do_fork(CLONE_VFORK|CLONE_VM|SIGCHLD, regs->areg[1],
regs, 0, NULL, NULL);
}
int sys_clone(struct pt_regs *regs)
{
unsigned long clone_flags;
unsigned long newsp;
int __user *parent_tidptr, *child_tidptr;
clone_flags = regs->areg[4];
newsp = regs->areg[3];
parent_tidptr = (int __user *)regs->areg[5];
child_tidptr = (int __user *)regs->areg[6];
if (!newsp)
newsp = regs->areg[1];
return do_fork(clone_flags,newsp,regs,0,parent_tidptr,child_tidptr);
}
/*
* sys_execve() executes a new program.
*/
int sys_execve(struct pt_regs *regs)
{
int error;
char * filename;
filename = getname((char *) (long)regs->areg[5]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) (long)regs->areg[3],
(char **) (long)regs->areg[4], regs);
putname(filename);
out:
return error;
}
int sys_uname(struct old_utsname * name)
{
if (name && !copy_to_user(name, &system_utsname, sizeof (*name)))
return 0;
return -EFAULT;
}
int sys_olduname(struct oldold_utsname * name)
{
int error;
if (!name)
return -EFAULT;
if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
return -EFAULT;
error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
error -= __put_user(0,name->release+__OLD_UTS_LEN);
error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
error -= __put_user(0,name->version+__OLD_UTS_LEN);
error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
error -= __put_user(0,name->machine+__OLD_UTS_LEN);
return error ? -EFAULT : 0;
}
/*
* Build the string table for the builtin "poor man's strace".
*/
#if DEBUG
#define SYSCALL(fun, narg) #fun,
static char *sfnames[] = {
#include "syscalls.h"
};
#undef SYS
#endif
void system_call (struct pt_regs *regs)
{
syscall_t syscall;
unsigned long parm0, parm1, parm2, parm3, parm4, parm5;
int nargs, res;
unsigned int syscallnr;
int ps;
#if DEBUG
int i;
unsigned long parms[6];
char *sysname;
#endif
regs->syscall = regs->areg[2];
do_syscall_trace();
/* Have to load after syscall_trace because strace
* sometimes changes regs->syscall.
*/
syscallnr = regs->syscall;
parm0 = parm1 = parm2 = parm3 = parm4 = parm5 = 0;
/* Restore interrupt level to syscall invoker's.
* If this were in assembly, we wouldn't disable
* interrupts in the first place:
*/
local_save_flags (ps);
local_irq_restore((ps & ~XCHAL_PS_INTLEVEL_MASK) |
(regs->ps & XCHAL_PS_INTLEVEL_MASK) );
if (syscallnr > __NR_Linux_syscalls) {
regs->areg[2] = -ENOSYS;
return;
}
syscall = sys_call_table[syscallnr];
nargs = sys_narg_table[syscallnr];
if (syscall == NULL) {
regs->areg[2] = -ENOSYS;
return;
}
/* There shouldn't be more than six arguments in the table! */
if (nargs > 6)
panic("Internal error - too many syscall arguments (%d)!\n",
nargs);
/* Linux takes system-call arguments in registers. The ABI
* and Xtensa software conventions require the system-call
* number in a2. If an argument exists in a2, we move it to
* the next available register. Note that for improved
* efficiency, we do NOT shift all parameters down one
* register to maintain the original order.
*
* At best case (zero arguments), we just write the syscall
* number to a2. At worst case (1 to 6 arguments), we move
* the argument in a2 to the next available register, then
* write the syscall number to a2.
*
* For clarity, the following truth table enumerates all
* possibilities.
*
* arguments syscall number arg0, arg1, arg2, arg3, arg4, arg5
* --------- -------------- ----------------------------------
* 0 a2
* 1 a2 a3
* 2 a2 a4, a3
* 3 a2 a5, a3, a4
* 4 a2 a6, a3, a4, a5
* 5 a2 a7, a3, a4, a5, a6
* 6 a2 a8, a3, a4, a5, a6, a7
*/
if (nargs) {
parm0 = regs->areg[nargs+2];
parm1 = regs->areg[3];
parm2 = regs->areg[4];
parm3 = regs->areg[5];
parm4 = regs->areg[6];
parm5 = regs->areg[7];
} else /* nargs == 0 */
parm0 = (unsigned long) regs;
#if DEBUG
parms[0] = parm0;
parms[1] = parm1;
parms[2] = parm2;
parms[3] = parm3;
parms[4] = parm4;
parms[5] = parm5;
sysname = sfnames[syscallnr];
if (strncmp(sysname, "sys_", 4) == 0)
sysname = sysname + 4;
printk("\017SYSCALL:I:%x:%d:%s %s(", regs->pc, current->pid,
current->comm, sysname);
for (i = 0; i < nargs; i++)
printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
printk(")\n");
#endif
res = syscall((void *)parm0, parm1, parm2, parm3, parm4, parm5);
#if DEBUG
printk("\017SYSCALL:O:%d:%s %s(",current->pid, current->comm, sysname);
for (i = 0; i < nargs; i++)
printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
if (res < 4096)
printk(") = %d\n", res);
else
printk(") = %#x\n", res);
#endif /* DEBUG */
regs->areg[2] = res;
do_syscall_trace();
}
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
*
* This is really horribly ugly.
*/
int sys_ipc (uint call, int first, int second,
int third, void __user *ptr, long fifth)
{
int version, ret;
version = call >> 16; /* hack for backward compatibility */
call &= 0xffff;
ret = -ENOSYS;
switch (call) {
case SEMOP:
ret = sys_semtimedop (first, (struct sembuf __user *)ptr,
second, NULL);
break;
case SEMTIMEDOP:
ret = sys_semtimedop (first, (struct sembuf __user *)ptr,
second, (const struct timespec *) fifth);
break;
case SEMGET:
ret = sys_semget (first, second, third);
break;
case SEMCTL: {
union semun fourth;
if (ptr && !get_user(fourth.__pad, (void *__user *) ptr))
ret = sys_semctl (first, second, third, fourth);
break;
}
case MSGSND:
ret = sys_msgsnd (first, (struct msgbuf __user*) ptr,
second, third);
break;
case MSGRCV:
switch (version) {
case 0: {
struct ipc_kludge tmp;
if (ptr && !copy_from_user(&tmp,
(struct ipc_kludge *) ptr,
sizeof (tmp)))
ret = sys_msgrcv (first, tmp.msgp, second,
tmp.msgtyp, third);
break;
}
default:
ret = sys_msgrcv (first, (struct msgbuf __user *) ptr,
second, 0, third);
break;
}
break;
case MSGGET:
ret = sys_msgget ((key_t) first, second);
break;
case MSGCTL:
ret = sys_msgctl (first, second, (struct msqid_ds __user*) ptr);
break;
case SHMAT: {
ulong raddr;
ret = do_shmat (first, (char __user *) ptr, second, &raddr);
if (!ret)
ret = put_user (raddr, (ulong __user *) third);
break;
}
case SHMDT:
ret = sys_shmdt ((char __user *)ptr);
break;
case SHMGET:
ret = sys_shmget (first, second, third);
break;
case SHMCTL:
ret = sys_shmctl (first, second, (struct shmid_ds __user*) ptr);
break;
}
return ret;
}

View File

@ -0,0 +1,248 @@
/*
* arch/xtensa/kernel/syscalls.h
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1995, 1996, 1997, 1998 by Ralf Baechle
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Changes by Joe Taylor <joe@tensilica.com>
*/
/*
* This file is being included twice - once to build a list of all
* syscalls and once to build a table of how many arguments each syscall
* accepts. Syscalls that receive a pointer to the saved registers are
* marked as having zero arguments.
*
* The binary compatibility calls are in a separate list.
*
* Entry '0' used to be system_call. It's removed to disable indirect
* system calls for now so user tasks can't recurse. See mips'
* sys_syscall for a comparable example.
*/
SYSCALL(0, 0) /* 00 */
SYSCALL(sys_exit, 1)
SYSCALL(sys_fork, 0)
SYSCALL(sys_read, 3)
SYSCALL(sys_write, 3)
SYSCALL(sys_open, 3) /* 05 */
SYSCALL(sys_close, 1)
SYSCALL(sys_waitpid, 3)
SYSCALL(sys_creat, 2)
SYSCALL(sys_link, 2)
SYSCALL(sys_unlink, 1) /* 10 */
SYSCALL(sys_execve, 0)
SYSCALL(sys_chdir, 1)
SYSCALL(sys_time, 1)
SYSCALL(sys_mknod, 3)
SYSCALL(sys_chmod, 2) /* 15 */
SYSCALL(sys_lchown, 3)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_stat, 2)
SYSCALL(sys_lseek, 3)
SYSCALL(sys_getpid, 0) /* 20 */
SYSCALL(sys_mount, 5)
SYSCALL(sys_oldumount, 1)
SYSCALL(sys_setuid, 1)
SYSCALL(sys_getuid, 0)
SYSCALL(sys_stime, 1) /* 25 */
SYSCALL(sys_ptrace, 4)
SYSCALL(sys_alarm, 1)
SYSCALL(sys_fstat, 2)
SYSCALL(sys_pause, 0)
SYSCALL(sys_utime, 2) /* 30 */
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_access, 2)
SYSCALL(sys_nice, 1)
SYSCALL(sys_ni_syscall, 0) /* 35 */
SYSCALL(sys_sync, 0)
SYSCALL(sys_kill, 2)
SYSCALL(sys_rename, 2)
SYSCALL(sys_mkdir, 2)
SYSCALL(sys_rmdir, 1) /* 40 */
SYSCALL(sys_dup, 1)
SYSCALL(sys_pipe, 1)
SYSCALL(sys_times, 1)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_brk, 1) /* 45 */
SYSCALL(sys_setgid, 1)
SYSCALL(sys_getgid, 0)
SYSCALL(sys_ni_syscall, 0) /* was signal(2) */
SYSCALL(sys_geteuid, 0)
SYSCALL(sys_getegid, 0) /* 50 */
SYSCALL(sys_acct, 1)
SYSCALL(sys_umount, 2)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_ioctl, 3)
SYSCALL(sys_fcntl, 3) /* 55 */
SYSCALL(sys_ni_syscall, 2)
SYSCALL(sys_setpgid, 2)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_olduname, 1)
SYSCALL(sys_umask, 1) /* 60 */
SYSCALL(sys_chroot, 1)
SYSCALL(sys_ustat, 2)
SYSCALL(sys_dup2, 2)
SYSCALL(sys_getppid, 0)
SYSCALL(sys_getpgrp, 0) /* 65 */
SYSCALL(sys_setsid, 0)
SYSCALL(sys_sigaction, 3)
SYSCALL(sys_sgetmask, 0)
SYSCALL(sys_ssetmask, 1)
SYSCALL(sys_setreuid, 2) /* 70 */
SYSCALL(sys_setregid, 2)
SYSCALL(sys_sigsuspend, 0)
SYSCALL(sys_sigpending, 1)
SYSCALL(sys_sethostname, 2)
SYSCALL(sys_setrlimit, 2) /* 75 */
SYSCALL(sys_getrlimit, 2)
SYSCALL(sys_getrusage, 2)
SYSCALL(sys_gettimeofday, 2)
SYSCALL(sys_settimeofday, 2)
SYSCALL(sys_getgroups, 2) /* 80 */
SYSCALL(sys_setgroups, 2)
SYSCALL(sys_ni_syscall, 0) /* old_select */
SYSCALL(sys_symlink, 2)
SYSCALL(sys_lstat, 2)
SYSCALL(sys_readlink, 3) /* 85 */
SYSCALL(sys_uselib, 1)
SYSCALL(sys_swapon, 2)
SYSCALL(sys_reboot, 3)
SYSCALL(old_readdir, 3)
SYSCALL(old_mmap, 6) /* 90 */
SYSCALL(sys_munmap, 2)
SYSCALL(sys_truncate, 2)
SYSCALL(sys_ftruncate, 2)
SYSCALL(sys_fchmod, 2)
SYSCALL(sys_fchown, 3) /* 95 */
SYSCALL(sys_getpriority, 2)
SYSCALL(sys_setpriority, 3)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_statfs, 2)
SYSCALL(sys_fstatfs, 2) /* 100 */
SYSCALL(sys_ni_syscall, 3)
SYSCALL(sys_socketcall, 2)
SYSCALL(sys_syslog, 3)
SYSCALL(sys_setitimer, 3)
SYSCALL(sys_getitimer, 2) /* 105 */
SYSCALL(sys_newstat, 2)
SYSCALL(sys_newlstat, 2)
SYSCALL(sys_newfstat, 2)
SYSCALL(sys_uname, 1)
SYSCALL(sys_ni_syscall, 0) /* 110 */
SYSCALL(sys_vhangup, 0)
SYSCALL(sys_ni_syscall, 0) /* was sys_idle() */
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_wait4, 4)
SYSCALL(sys_swapoff, 1) /* 115 */
SYSCALL(sys_sysinfo, 1)
SYSCALL(sys_ipc, 5) /* 6 really, but glibc uses only 5) */
SYSCALL(sys_fsync, 1)
SYSCALL(sys_sigreturn, 0)
SYSCALL(sys_clone, 0) /* 120 */
SYSCALL(sys_setdomainname, 2)
SYSCALL(sys_newuname, 1)
SYSCALL(sys_ni_syscall, 0) /* sys_modify_ldt */
SYSCALL(sys_adjtimex, 1)
SYSCALL(sys_mprotect, 3) /* 125 */
SYSCALL(sys_sigprocmask, 3)
SYSCALL(sys_ni_syscall, 2) /* old sys_create_module */
SYSCALL(sys_init_module, 2)
SYSCALL(sys_delete_module, 1)
SYSCALL(sys_ni_syscall, 1) /* old sys_get_kernel_sysm */ /* 130 */
SYSCALL(sys_quotactl, 0)
SYSCALL(sys_getpgid, 1)
SYSCALL(sys_fchdir, 1)
SYSCALL(sys_bdflush, 2)
SYSCALL(sys_sysfs, 3) /* 135 */
SYSCALL(sys_personality, 1)
SYSCALL(sys_ni_syscall, 0) /* for afs_syscall */
SYSCALL(sys_setfsuid, 1)
SYSCALL(sys_setfsgid, 1)
SYSCALL(sys_llseek, 5) /* 140 */
SYSCALL(sys_getdents, 3)
SYSCALL(sys_select, 5)
SYSCALL(sys_flock, 2)
SYSCALL(sys_msync, 3)
SYSCALL(sys_readv, 3) /* 145 */
SYSCALL(sys_writev, 3)
SYSCALL(sys_ni_syscall, 3)
SYSCALL(sys_ni_syscall, 3)
SYSCALL(sys_ni_syscall, 4) /* handled in fast syscall handler. */
SYSCALL(sys_ni_syscall, 0) /* 150 */
SYSCALL(sys_getsid, 1)
SYSCALL(sys_fdatasync, 1)
SYSCALL(sys_sysctl, 1)
SYSCALL(sys_mlock, 2)
SYSCALL(sys_munlock, 2) /* 155 */
SYSCALL(sys_mlockall, 1)
SYSCALL(sys_munlockall, 0)
SYSCALL(sys_sched_setparam,2)
SYSCALL(sys_sched_getparam,2)
SYSCALL(sys_sched_setscheduler,3) /* 160 */
SYSCALL(sys_sched_getscheduler,1)
SYSCALL(sys_sched_yield,0)
SYSCALL(sys_sched_get_priority_max,1)
SYSCALL(sys_sched_get_priority_min,1)
SYSCALL(sys_sched_rr_get_interval,2) /* 165 */
SYSCALL(sys_nanosleep,2)
SYSCALL(sys_mremap,4)
SYSCALL(sys_accept, 3)
SYSCALL(sys_bind, 3)
SYSCALL(sys_connect, 3) /* 170 */
SYSCALL(sys_getpeername, 3)
SYSCALL(sys_getsockname, 3)
SYSCALL(sys_getsockopt, 5)
SYSCALL(sys_listen, 2)
SYSCALL(sys_recv, 4) /* 175 */
SYSCALL(sys_recvfrom, 6)
SYSCALL(sys_recvmsg, 3)
SYSCALL(sys_send, 4)
SYSCALL(sys_sendmsg, 3)
SYSCALL(sys_sendto, 6) /* 180 */
SYSCALL(sys_setsockopt, 5)
SYSCALL(sys_shutdown, 2)
SYSCALL(sys_socket, 3)
SYSCALL(sys_socketpair, 4)
SYSCALL(sys_setresuid, 3) /* 185 */
SYSCALL(sys_getresuid, 3)
SYSCALL(sys_ni_syscall, 5) /* old sys_query_module */
SYSCALL(sys_poll, 3)
SYSCALL(sys_nfsservctl, 3)
SYSCALL(sys_setresgid, 3) /* 190 */
SYSCALL(sys_getresgid, 3)
SYSCALL(sys_prctl, 5)
SYSCALL(sys_rt_sigreturn, 0)
SYSCALL(sys_rt_sigaction, 4)
SYSCALL(sys_rt_sigprocmask, 4) /* 195 */
SYSCALL(sys_rt_sigpending, 2)
SYSCALL(sys_rt_sigtimedwait, 4)
SYSCALL(sys_rt_sigqueueinfo, 3)
SYSCALL(sys_rt_sigsuspend, 0)
SYSCALL(sys_pread64, 5) /* 200 */
SYSCALL(sys_pwrite64, 5)
SYSCALL(sys_chown, 3)
SYSCALL(sys_getcwd, 2)
SYSCALL(sys_capget, 2)
SYSCALL(sys_capset, 2) /* 205 */
SYSCALL(sys_sigaltstack, 0)
SYSCALL(sys_sendfile, 4)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_ni_syscall, 0)
SYSCALL(sys_mmap2, 6) /* 210 */
SYSCALL(sys_truncate64, 2)
SYSCALL(sys_ftruncate64, 2)
SYSCALL(sys_stat64, 2)
SYSCALL(sys_lstat64, 2)
SYSCALL(sys_fstat64, 2) /* 215 */
SYSCALL(sys_pivot_root, 2)
SYSCALL(sys_mincore, 3)
SYSCALL(sys_madvise, 3)
SYSCALL(sys_getdents64, 3)
SYSCALL(sys_vfork, 0) /* 220 */

View File

@ -0,0 +1,227 @@
/*
* arch/xtensa/kernel/time.c
*
* Timer and clock support.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/profile.h>
#include <linux/delay.h>
#include <asm/timex.h>
#include <asm/platform.h>
extern volatile unsigned long wall_jiffies;
u64 jiffies_64 = INITIAL_JIFFIES;
EXPORT_SYMBOL(jiffies_64);
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
EXPORT_SYMBOL(rtc_lock);
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
unsigned long ccount_per_jiffy; /* per 1/HZ */
unsigned long ccount_nsec; /* nsec per ccount increment */
#endif
unsigned int last_ccount_stamp;
static long last_rtc_update = 0;
/*
* Scheduler clock - returns current tim in nanosec units.
*/
unsigned long long sched_clock(void)
{
return (unsigned long long)jiffies * (1000000000 / HZ);
}
static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static struct irqaction timer_irqaction = {
.handler = timer_interrupt,
.flags = SA_INTERRUPT,
.name = "timer",
};
void __init time_init(void)
{
time_t sec_o, sec_n = 0;
/* The platform must provide a function to calibrate the processor
* speed for the CALIBRATE.
*/
#if CONFIG_XTENSA_CALIBRATE_CCOUNT
printk("Calibrating CPU frequency ");
platform_calibrate_ccount();
printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ),
(int)(ccount_per_jiffy/(10000/HZ))%100);
#endif
/* Set time from RTC (if provided) */
if (platform_get_rtc_time(&sec_o) == 0)
while (platform_get_rtc_time(&sec_n))
if (sec_o != sec_n)
break;
xtime.tv_nsec = 0;
last_rtc_update = xtime.tv_sec = sec_n;
last_ccount_stamp = get_ccount();
set_normalized_timespec(&wall_to_monotonic,
-xtime.tv_sec, -xtime.tv_nsec);
/* Initialize the linux timer interrupt. */
setup_irq(LINUX_TIMER_INT, &timer_irqaction);
set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY);
}
int do_settimeofday(struct timespec *tv)
{
time_t wtm_sec, sec = tv->tv_sec;
long wtm_nsec, nsec = tv->tv_nsec;
unsigned long ccount;
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set "xtime" correctly. However, the
* value in this location is the value at the most recent update of
* wall time. Discover what correction gettimeofday() would have
* made, and then undo it!
*/
ccount = get_ccount();
nsec -= (ccount - last_ccount_stamp) * CCOUNT_NSEC;
nsec -= (jiffies - wall_jiffies) * CCOUNT_PER_JIFFY * CCOUNT_NSEC;
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq(&xtime_lock);
return 0;
}
EXPORT_SYMBOL(do_settimeofday);
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
unsigned long sec, usec, delta, lost, seq;
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
delta = get_ccount() - last_ccount_stamp;
sec = xtime.tv_sec;
usec = (xtime.tv_nsec / NSEC_PER_USEC);
lost = jiffies - wall_jiffies;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
usec += lost * (1000000UL/HZ) + (delta * CCOUNT_NSEC) / NSEC_PER_USEC;
for (; usec >= 1000000; sec++, usec -= 1000000)
;
tv->tv_sec = sec;
tv->tv_usec = usec;
}
EXPORT_SYMBOL(do_gettimeofday);
/*
* The timer interrupt is called HZ times per second.
*/
irqreturn_t timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long next;
next = get_linux_timer();
again:
while ((signed long)(get_ccount() - next) > 0) {
profile_tick(CPU_PROFILING, regs);
#ifndef CONFIG_SMP
update_process_times(user_mode(regs));
#endif
write_seqlock(&xtime_lock);
last_ccount_stamp = next;
next += CCOUNT_PER_JIFFY;
do_timer (regs); /* Linux handler in kernel/timer.c */
if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec - last_rtc_update >= 659 &&
abs((xtime.tv_nsec/1000)-(1000000-1000000/HZ))<5000000/HZ &&
jiffies - wall_jiffies == 1) {
if (platform_set_rtc_time(xtime.tv_sec+1) == 0)
last_rtc_update = xtime.tv_sec+1;
else
/* Do it again in 60 s */
last_rtc_update += 60;
}
write_sequnlock(&xtime_lock);
}
/* NOTE: writing CCOMPAREn clears the interrupt. */
set_linux_timer (next);
/* Make sure we didn't miss any tick... */
if ((signed long)(get_ccount() - next) > 0)
goto again;
/* Allow platform to do something usefull (Wdog). */
platform_heartbeat();
return IRQ_HANDLED;
}
#ifndef CONFIG_GENERIC_CALIBRATE_DELAY
void __devinit calibrate_delay(void)
{
loops_per_jiffy = CCOUNT_PER_JIFFY;
printk("Calibrating delay loop (skipped)... "
"%lu.%02lu BogoMIPS preset\n",
loops_per_jiffy/(1000000/HZ),
(loops_per_jiffy/(10000/HZ)) % 100);
}
#endif

View File

@ -0,0 +1,498 @@
/*
* arch/xtensa/kernel/traps.c
*
* Exception handling.
*
* Derived from code with the following copyrights:
* Copyright (C) 1994 - 1999 by Ralf Baechle
* Modified for R3000 by Paul M. Antoine, 1995, 1996
* Complete output from die() by Ulf Carlsson, 1998
* Copyright (C) 1999 Silicon Graphics, Inc.
*
* Essentially rewritten for the Xtensa architecture port.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
* Chris Zankel <chris@zankel.net>
* Marc Gauthier<marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Kevin Chea
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stringify.h>
#include <linux/kallsyms.h>
#include <asm/ptrace.h>
#include <asm/timex.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#ifdef CONFIG_KGDB
extern int gdb_enter;
extern int return_from_debug_flag;
#endif
/*
* Machine specific interrupt handlers
*/
extern void kernel_exception(void);
extern void user_exception(void);
extern void fast_syscall_kernel(void);
extern void fast_syscall_user(void);
extern void fast_alloca(void);
extern void fast_unaligned(void);
extern void fast_second_level_miss(void);
extern void fast_store_prohibited(void);
extern void fast_coprocessor(void);
extern void do_illegal_instruction (struct pt_regs*);
extern void do_interrupt (struct pt_regs*);
extern void do_unaligned_user (struct pt_regs*);
extern void do_multihit (struct pt_regs*, unsigned long);
extern void do_page_fault (struct pt_regs*, unsigned long);
extern void do_debug (struct pt_regs*);
extern void system_call (struct pt_regs*);
/*
* The vector table must be preceded by a save area (which
* implies it must be in RAM, unless one places RAM immediately
* before a ROM and puts the vector at the start of the ROM (!))
*/
#define KRNL 0x01
#define USER 0x02
#define COPROCESSOR(x) \
{ XCHAL_EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor }
typedef struct {
int cause;
int fast;
void* handler;
} dispatch_init_table_t;
dispatch_init_table_t __init dispatch_init_table[] = {
{ XCHAL_EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction},
{ XCHAL_EXCCAUSE_SYSTEM_CALL, KRNL, fast_syscall_kernel },
{ XCHAL_EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user },
{ XCHAL_EXCCAUSE_SYSTEM_CALL, 0, system_call },
/* XCHAL_EXCCAUSE_INSTRUCTION_FETCH unhandled */
/* XCHAL_EXCCAUSE_LOAD_STORE_ERROR unhandled*/
{ XCHAL_EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt },
{ XCHAL_EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca },
/* XCHAL_EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */
/* XCHAL_EXCCAUSE_PRIVILEGED unhandled */
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
#ifdef CONFIG_UNALIGNED_USER
{ XCHAL_EXCCAUSE_UNALIGNED, USER, fast_unaligned },
#else
{ XCHAL_EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
#endif
{ XCHAL_EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
#endif
{ XCHAL_EXCCAUSE_ITLB_MISS, 0, do_page_fault },
{ XCHAL_EXCCAUSE_ITLB_MISS, USER|KRNL, fast_second_level_miss},
{ XCHAL_EXCCAUSE_ITLB_MULTIHIT, 0, do_multihit },
{ XCHAL_EXCCAUSE_ITLB_PRIVILEGE, 0, do_page_fault },
/* XCHAL_EXCCAUSE_SIZE_RESTRICTION unhandled */
{ XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE, 0, do_page_fault },
{ XCHAL_EXCCAUSE_DTLB_MISS, USER|KRNL, fast_second_level_miss},
{ XCHAL_EXCCAUSE_DTLB_MISS, 0, do_page_fault },
{ XCHAL_EXCCAUSE_DTLB_MULTIHIT, 0, do_multihit },
{ XCHAL_EXCCAUSE_DTLB_PRIVILEGE, 0, do_page_fault },
/* XCHAL_EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */
{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE, USER|KRNL, fast_store_prohibited },
{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE, 0, do_page_fault },
{ XCHAL_EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0, do_page_fault },
/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */
#if (XCHAL_CP_MASK & 1)
COPROCESSOR(0),
#endif
#if (XCHAL_CP_MASK & 2)
COPROCESSOR(1),
#endif
#if (XCHAL_CP_MASK & 4)
COPROCESSOR(2),
#endif
#if (XCHAL_CP_MASK & 8)
COPROCESSOR(3),
#endif
#if (XCHAL_CP_MASK & 16)
COPROCESSOR(4),
#endif
#if (XCHAL_CP_MASK & 32)
COPROCESSOR(5),
#endif
#if (XCHAL_CP_MASK & 64)
COPROCESSOR(6),
#endif
#if (XCHAL_CP_MASK & 128)
COPROCESSOR(7),
#endif
{ EXCCAUSE_MAPPED_DEBUG, 0, do_debug },
{ -1, -1, 0 }
};
/* The exception table <exc_table> serves two functions:
* 1. it contains three dispatch tables (fast_user, fast_kernel, default-c)
* 2. it is a temporary memory buffer for the exception handlers.
*/
unsigned long exc_table[EXC_TABLE_SIZE/4];
void die(const char*, struct pt_regs*, long);
static inline void
__die_if_kernel(const char *str, struct pt_regs *regs, long err)
{
if (!user_mode(regs))
die(str, regs, err);
}
/*
* Unhandled Exceptions. Kill user task or panic if in kernel space.
*/
void do_unhandled(struct pt_regs *regs, unsigned long exccause)
{
__die_if_kernel("Caught unhandled exception - should not happen",
regs, SIGKILL);
/* If in user mode, send SIGILL signal to current process */
printk("Caught unhandled exception in '%s' "
"(pid = %d, pc = %#010lx) - should not happen\n"
"\tEXCCAUSE is %ld\n",
current->comm, current->pid, regs->pc, exccause);
force_sig(SIGILL, current);
}
/*
* Multi-hit exception. This if fatal!
*/
void do_multihit(struct pt_regs *regs, unsigned long exccause)
{
die("Caught multihit exception", regs, SIGKILL);
}
/*
* Level-1 interrupt.
* We currently have no priority encoding.
*/
unsigned long ignored_level1_interrupts;
extern void do_IRQ(int, struct pt_regs *);
void do_interrupt (struct pt_regs *regs)
{
unsigned long intread = get_sr (INTREAD);
unsigned long intenable = get_sr (INTENABLE);
int i, mask;
/* Handle all interrupts (no priorities).
* (Clear the interrupt before processing, in case it's
* edge-triggered or software-generated)
*/
for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) {
if (mask & (intread & intenable)) {
set_sr (mask, INTCLEAR);
do_IRQ (i,regs);
}
}
}
/*
* Illegal instruction. Fatal if in kernel space.
*/
void
do_illegal_instruction(struct pt_regs *regs)
{
__die_if_kernel("Illegal instruction in kernel", regs, SIGKILL);
/* If in user mode, send SIGILL signal to current process. */
printk("Illegal Instruction in '%s' (pid = %d, pc = %#010lx)\n",
current->comm, current->pid, regs->pc);
force_sig(SIGILL, current);
}
/*
* Handle unaligned memory accesses from user space. Kill task.
*
* If CONFIG_UNALIGNED_USER is not set, we don't allow unaligned memory
* accesses causes from user space.
*/
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
#ifndef CONFIG_UNALIGNED_USER
void
do_unaligned_user (struct pt_regs *regs)
{
siginfo_t info;
__die_if_kernel("Unhandled unaligned exception in kernel",
regs, SIGKILL);
current->thread.bad_vaddr = regs->excvaddr;
current->thread.error_code = -3;
printk("Unaligned memory access to %08lx in '%s' "
"(pid = %d, pc = %#010lx)\n",
regs->excvaddr, current->comm, current->pid, regs->pc);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
info.si_addr = (void *) regs->excvaddr;
force_sig_info(SIGSEGV, &info, current);
}
#endif
#endif
void
do_debug(struct pt_regs *regs)
{
#ifdef CONFIG_KGDB
/* If remote debugging is configured AND enabled, we give control to
* kgdb. Otherwise, we fall through, perhaps giving control to the
* native debugger.
*/
if (gdb_enter) {
extern void gdb_handle_exception(struct pt_regs *);
gdb_handle_exception(regs);
return_from_debug_flag = 1;
return;
}
#endif
__die_if_kernel("Breakpoint in kernel", regs, SIGKILL);
/* If in user mode, send SIGTRAP signal to current process */
force_sig(SIGTRAP, current);
}
/*
* Initialize dispatch tables.
*
* The exception vectors are stored compressed the __init section in the
* dispatch_init_table. This function initializes the following three tables
* from that compressed table:
* - fast user first dispatch table for user exceptions
* - fast kernel first dispatch table for kernel exceptions
* - default C-handler C-handler called by the default fast handler.
*
* See vectors.S for more details.
*/
#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler))
void trap_init(void)
{
int i;
/* Setup default vectors. */
for(i = 0; i < 64; i++) {
set_handler(EXC_TABLE_FAST_USER/4 + i, user_exception);
set_handler(EXC_TABLE_FAST_KERNEL/4 + i, kernel_exception);
set_handler(EXC_TABLE_DEFAULT/4 + i, do_unhandled);
}
/* Setup specific handlers. */
for(i = 0; dispatch_init_table[i].cause >= 0; i++) {
int fast = dispatch_init_table[i].fast;
int cause = dispatch_init_table[i].cause;
void *handler = dispatch_init_table[i].handler;
if (fast == 0)
set_handler (EXC_TABLE_DEFAULT/4 + cause, handler);
if (fast && fast & USER)
set_handler (EXC_TABLE_FAST_USER/4 + cause, handler);
if (fast && fast & KRNL)
set_handler (EXC_TABLE_FAST_KERNEL/4 + cause, handler);
}
/* Initialize EXCSAVE_1 to hold the address of the exception table. */
i = (unsigned long)exc_table;
__asm__ __volatile__("wsr %0, "__stringify(EXCSAVE_1)"\n" : : "a" (i));
}
/*
* This function dumps the current valid window frame and other base registers.
*/
void show_regs(struct pt_regs * regs)
{
int i, wmask;
wmask = regs->wmask & ~1;
for (i = 0; i < 32; i++) {
if (wmask & (1 << (i / 4)))
break;
if ((i % 8) == 0)
printk ("\n" KERN_INFO "a%02d: ", i);
printk("%08lx ", regs->areg[i]);
}
printk("\n");
printk("pc: %08lx, ps: %08lx, depc: %08lx, excvaddr: %08lx\n",
regs->pc, regs->ps, regs->depc, regs->excvaddr);
printk("lbeg: %08lx, lend: %08lx lcount: %08lx, sar: %08lx\n",
regs->lbeg, regs->lend, regs->lcount, regs->sar);
if (user_mode(regs))
printk("wb: %08lx, ws: %08lx, wmask: %08lx, syscall: %ld\n",
regs->windowbase, regs->windowstart, regs->wmask,
regs->syscall);
}
void show_trace(struct task_struct *task, unsigned long *sp)
{
unsigned long a0, a1, pc;
unsigned long sp_start, sp_end;
a1 = (unsigned long)sp;
if (a1 == 0)
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(a1));
sp_start = a1 & ~(THREAD_SIZE-1);
sp_end = sp_start + THREAD_SIZE;
printk("Call Trace:");
#ifdef CONFIG_KALLSYMS
printk("\n");
#endif
spill_registers();
while (a1 > sp_start && a1 < sp_end) {
sp = (unsigned long*)a1;
a0 = *(sp - 4);
a1 = *(sp - 3);
if (a1 <= (unsigned long) sp)
break;
pc = MAKE_PC_FROM_RA(a0, a1);
if (kernel_text_address(pc)) {
printk(" [<%08lx>] ", pc);
print_symbol("%s\n", pc);
}
}
printk("\n");
}
/*
* This routine abuses get_user()/put_user() to reference pointers
* with at least a bit of error checking ...
*/
static int kstack_depth_to_print = 24;
void show_stack(struct task_struct *task, unsigned long *sp)
{
int i = 0;
unsigned long *stack;
if (sp == 0)
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
stack = sp;
printk("\nStack: ");
for (i = 0; i < kstack_depth_to_print; i++) {
if (kstack_end(sp))
break;
if (i && ((i % 8) == 0))
printk("\n ");
printk("%08lx ", *sp++);
}
printk("\n");
show_trace(task, stack);
}
void dump_stack(void)
{
show_stack(current, NULL);
}
EXPORT_SYMBOL(dump_stack);
void show_code(unsigned int *pc)
{
long i;
printk("\nCode:");
for(i = -3 ; i < 6 ; i++) {
unsigned long insn;
if (__get_user(insn, pc + i)) {
printk(" (Bad address in pc)\n");
break;
}
printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
}
}
spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
void die(const char * str, struct pt_regs * regs, long err)
{
static int die_counter;
int nl = 0;
console_verbose();
spin_lock_irq(&die_lock);
printk("%s: sig: %ld [#%d]\n", str, err, ++die_counter);
#ifdef CONFIG_PREEMPT
printk("PREEMPT ");
nl = 1;
#endif
if (nl)
printk("\n");
show_regs(regs);
if (!user_mode(regs))
show_stack(NULL, (unsigned long*)regs->areg[1]);
spin_unlock_irq(&die_lock);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops) {
printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(5 * HZ);
panic("Fatal exception");
}
do_exit(err);
}

View File

@ -0,0 +1,464 @@
/*
* arch/xtensa/kernel/vectors.S
*
* This file contains all exception vectors (user, kernel, and double),
* as well as the window vectors (overflow and underflow), and the debug
* vector. These are the primary vectors executed by the processor if an
* exception occurs.
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of
* this archive for more details.
*
* Copyright (C) 2005 Tensilica, Inc.
*
* Chris Zankel <chris@zankel.net>
*
*/
/*
* We use a two-level table approach. The user and kernel exception vectors
* use a first-level dispatch table to dispatch the exception to a registered
* fast handler or the default handler, if no fast handler was registered.
* The default handler sets up a C-stack and dispatches the exception to a
* registerd C handler in the second-level dispatch table.
*
* Fast handler entry condition:
*
* a0: trashed, original value saved on stack (PT_AREG0)
* a1: a1
* a2: new stack pointer, original value in depc
* a3: dispatch table
* depc: a2, original value saved on stack (PT_DEPC)
* excsave_1: a3
*
* The value for PT_DEPC saved to stack also functions as a boolean to
* indicate that the exception is either a double or a regular exception:
*
* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception
* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
*
* Note: Neither the kernel nor the user exception handler generate literals.
*
*/
#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/ptrace.h>
#include <asm/current.h>
#include <asm/offsets.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/thread_info.h>
#include <asm/processor.h>
/*
* User exception vector. (Exceptions with PS.UM == 1, PS.EXCM == 0)
*
* We get here when an exception occurred while we were in userland.
* We switch to the kernel stack and jump to the first level handler
* associated to the exception cause.
*
* Note: the saved kernel stack pointer (EXC_TABLE_KSTK) is already
* decremented by PT_USER_SIZE.
*/
.section .UserExceptionVector.text, "ax"
ENTRY(_UserExceptionVector)
xsr a3, EXCSAVE_1 # save a3 and get dispatch table
wsr a2, DEPC # save a2
l32i a2, a3, EXC_TABLE_KSTK # load kernel stack to a2
s32i a0, a2, PT_AREG0 # save a0 to ESF
rsr a0, EXCCAUSE # retrieve exception cause
s32i a0, a2, PT_DEPC # mark it as a regular exception
addx4 a0, a0, a3 # find entry in table
l32i a0, a0, EXC_TABLE_FAST_USER # load handler
jx a0
/*
* Kernel exception vector. (Exceptions with PS.UM == 0, PS.EXCM == 0)
*
* We get this exception when we were already in kernel space.
* We decrement the current stack pointer (kernel) by PT_SIZE and
* jump to the first-level handler associated with the exception cause.
*
* Note: we need to preserve space for the spill region.
*/
.section .KernelExceptionVector.text, "ax"
ENTRY(_KernelExceptionVector)
xsr a3, EXCSAVE_1 # save a3, and get dispatch table
wsr a2, DEPC # save a2
addi a2, a1, -16-PT_SIZE # adjust stack pointer
s32i a0, a2, PT_AREG0 # save a0 to ESF
rsr a0, EXCCAUSE # retrieve exception cause
s32i a0, a2, PT_DEPC # mark it as a regular exception
addx4 a0, a0, a3 # find entry in table
l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler address
jx a0
/*
* Double exception vector (Exceptions with PS.EXCM == 1)
* We get this exception when another exception occurs while were are
* already in an exception, such as window overflow/underflow exception,
* or 'expected' exceptions, for example memory exception when we were trying
* to read data from an invalid address in user space.
*
* Note that this vector is never invoked for level-1 interrupts, because such
* interrupts are disabled (masked) when PS.EXCM is set.
*
* We decode the exception and take the appropriate action. However, the
* double exception vector is much more careful, because a lot more error
* cases go through the double exception vector than through the user and
* kernel exception vectors.
*
* Occasionally, the kernel expects a double exception to occur. This usually
* happens when accessing user-space memory with the user's permissions
* (l32e/s32e instructions). The kernel state, though, is not always suitable
* for immediate transfer of control to handle_double, where "normal" exception
* processing occurs. Also in kernel mode, TLB misses can occur if accessing
* vmalloc memory, possibly requiring repair in a double exception handler.
*
* The variable at TABLE_FIXUP offset from the pointer in EXCSAVE_1 doubles as
* a boolean variable and a pointer to a fixup routine. If the variable
* EXC_TABLE_FIXUP is non-zero, this handler jumps to that address. A value of
* zero indicates to use the default kernel/user exception handler.
* There is only one exception, when the value is identical to the exc_table
* label, the kernel is in trouble. This mechanism is used to protect critical
* sections, mainly when the handler writes to the stack to assert the stack
* pointer is valid. Once the fixup/default handler leaves that area, the
* EXC_TABLE_FIXUP variable is reset to the fixup handler or zero.
*
* Procedures wishing to use this mechanism should set EXC_TABLE_FIXUP to the
* nonzero address of a fixup routine before it could cause a double exception
* and reset it before it returns.
*
* Some other things to take care of when a fast exception handler doesn't
* specify a particular fixup handler but wants to use the default handlers:
*
* - The original stack pointer (in a1) must not be modified. The fast
* exception handler should only use a2 as the stack pointer.
*
* - If the fast handler manipulates the stack pointer (in a2), it has to
* register a valid fixup handler and cannot use the default handlers.
*
* - The handler can use any other generic register from a3 to a15, but it
* must save the content of these registers to stack (PT_AREG3...PT_AREGx)
*
* - These registers must be saved before a double exception can occur.
*
* - If we ever implement handling signals while in double exceptions, the
* number of registers a fast handler has saved (excluding a0 and a1) must
* be written to PT_AREG1. (1 if only a3 is used, 2 for a3 and a4, etc. )
*
* The fixup handlers are special handlers:
*
* - Fixup entry conditions differ from regular exceptions:
*
* a0: DEPC
* a1: a1
* a2: trashed, original value in EXC_TABLE_DOUBLE_A2
* a3: exctable
* depc: a0
* excsave_1: a3
*
* - When the kernel enters the fixup handler, it still assumes it is in a
* critical section, so EXC_TABLE_FIXUP variable is set to exc_table.
* The fixup handler, therefore, has to re-register itself as the fixup
* handler before it returns from the double exception.
*
* - Fixup handler can share the same exception frame with the fast handler.
* The kernel stack pointer is not changed when entering the fixup handler.
*
* - Fixup handlers can jump to the default kernel and user exception
* handlers. Before it jumps, though, it has to setup a exception frame
* on stack. Because the default handler resets the register fixup handler
* the fixup handler must make sure that the default handler returns to
* it instead of the exception address, so it can re-register itself as
* the fixup handler.
*
* In case of a critical condition where the kernel cannot recover, we jump
* to unrecoverable_exception with the following entry conditions.
* All registers a0...a15 are unchanged from the last exception, except:
*
* a0: last address before we jumped to the unrecoverable_exception.
* excsave_1: a0
*
*
* See the handle_alloca_user and spill_registers routines for example clients.
*
* FIXME: Note: we currently don't allow signal handling coming from a double
* exception, so the item markt with (*) is not required.
*/
.section .DoubleExceptionVector.text, "ax"
.begin literal_prefix .DoubleExceptionVector
ENTRY(_DoubleExceptionVector)
/* Deliberately destroy excsave (don't assume it's value was valid). */
wsr a3, EXCSAVE_1 # save a3
/* Check for kernel double exception (usually fatal). */
rsr a3, PS
_bbci.l a3, PS_UM_SHIFT, .Lksp
/* Check if we are currently handling a window exception. */
/* Note: We don't need to indicate that we enter a critical section. */
xsr a0, DEPC # get DEPC, save a0
movi a3, XCHAL_WINDOW_VECTORS_VADDR
_bltu a0, a3, .Lfixup
addi a3, a3, XSHAL_WINDOW_VECTORS_SIZE
_bgeu a0, a3, .Lfixup
/* Window overflow/underflow exception. Get stack pointer. */
mov a3, a2
movi a2, exc_table
l32i a2, a2, EXC_TABLE_KSTK
/* Check for overflow/underflow exception, jump if overflow. */
_bbci.l a0, 6, .Lovfl
/* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */
/* Restart window underflow exception.
* We return to the instruction in user space that caused the window
* underflow exception. Therefore, we change window base to the value
* before we entered the window underflow exception and prepare the
* registers to return as if we were coming from a regular exception
* by changing depc (in a0).
* Note: We can trash the current window frame (a0...a3) and depc!
*/
wsr a2, DEPC # save stack pointer temporarily
rsr a0, PS
extui a0, a0, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
wsr a0, WINDOWBASE
rsync
/* We are now in the previous window frame. Save registers again. */
xsr a2, DEPC # save a2 and get stack pointer
s32i a0, a2, PT_AREG0
wsr a3, EXCSAVE_1 # save a3
movi a3, exc_table
rsr a0, EXCCAUSE
s32i a0, a2, PT_DEPC # mark it as a regular exception
addx4 a0, a0, a3
l32i a0, a0, EXC_TABLE_FAST_USER
jx a0
.Lfixup:/* Check for a fixup handler or if we were in a critical section. */
/* a0: depc, a1: a1, a2: a2, a3: trashed, depc: a0, excsave1: a3 */
movi a3, exc_table
s32i a2, a3, EXC_TABLE_DOUBLE_SAVE # temporary variable
/* Enter critical section. */
l32i a2, a3, EXC_TABLE_FIXUP
s32i a3, a3, EXC_TABLE_FIXUP
beq a2, a3, .Lunrecoverable_fixup # critical!
beqz a2, .Ldflt # no handler was registered
/* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave: a3 */
jx a2
.Ldflt: /* Get stack pointer. */
l32i a3, a3, EXC_TABLE_DOUBLE_SAVE
addi a2, a3, -PT_USER_SIZE
.Lovfl: /* Jump to default handlers. */
/* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */
xsr a3, DEPC
s32i a0, a2, PT_DEPC
s32i a3, a2, PT_AREG0
/* a0: avail, a1: a1, a2: kstk, a3: avail, depc: a2, excsave: a3 */
movi a3, exc_table
rsr a0, EXCCAUSE
addx4 a0, a0, a3
l32i a0, a0, EXC_TABLE_FAST_USER
jx a0
/*
* We only allow the ITLB miss exception if we are in kernel space.
* All other exceptions are unexpected and thus unrecoverable!
*/
.extern fast_second_level_miss_double_kernel
.Lksp: /* a0: a0, a1: a1, a2: a2, a3: trashed, depc: depc, excsave: a3 */
rsr a3, EXCCAUSE
beqi a3, XCHAL_EXCCAUSE_ITLB_MISS, 1f
addi a3, a3, -XCHAL_EXCCAUSE_DTLB_MISS
bnez a3, .Lunrecoverable
1: movi a3, fast_second_level_miss_double_kernel
jx a3
/* Critical! We can't handle this situation. PANIC! */
.extern unrecoverable_exception
.Lunrecoverable_fixup:
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
xsr a0, DEPC
.Lunrecoverable:
rsr a3, EXCSAVE_1
wsr a0, EXCSAVE_1
movi a0, unrecoverable_exception
callx0 a0
.end literal_prefix
/*
* Debug interrupt vector
*
* There is not much space here, so simply jump to another handler.
* EXCSAVE[DEBUGLEVEL] has been set to that handler.
*/
.section .DebugInterruptVector.text, "ax"
ENTRY(_DebugInterruptVector)
xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL
jx a0
/* Window overflow and underflow handlers.
* The handlers must be 64 bytes apart, first starting with the underflow
* handlers underflow-4 to underflow-12, then the overflow handlers
* overflow-4 to overflow-12.
*
* Note: We rerun the underflow handlers if we hit an exception, so
* we try to access any page that would cause a page fault early.
*/
.section .WindowVectors.text, "ax"
/* 4-Register Window Overflow Vector (Handler) */
.align 64
.global _WindowOverflow4
_WindowOverflow4:
s32e a0, a5, -16
s32e a1, a5, -12
s32e a2, a5, -8
s32e a3, a5, -4
rfwo
/* 4-Register Window Underflow Vector (Handler) */
.align 64
.global _WindowUnderflow4
_WindowUnderflow4:
l32e a0, a5, -16
l32e a1, a5, -12
l32e a2, a5, -8
l32e a3, a5, -4
rfwu
/* 8-Register Window Overflow Vector (Handler) */
.align 64
.global _WindowOverflow8
_WindowOverflow8:
s32e a0, a9, -16
l32e a0, a1, -12
s32e a2, a9, -8
s32e a1, a9, -12
s32e a3, a9, -4
s32e a4, a0, -32
s32e a5, a0, -28
s32e a6, a0, -24
s32e a7, a0, -20
rfwo
/* 8-Register Window Underflow Vector (Handler) */
.align 64
.global _WindowUnderflow8
_WindowUnderflow8:
l32e a1, a9, -12
l32e a0, a9, -16
l32e a7, a1, -12
l32e a2, a9, -8
l32e a4, a7, -32
l32e a3, a9, -4
l32e a5, a7, -28
l32e a6, a7, -24
l32e a7, a7, -20
rfwu
/* 12-Register Window Overflow Vector (Handler) */
.align 64
.global _WindowOverflow12
_WindowOverflow12:
s32e a0, a13, -16
l32e a0, a1, -12
s32e a1, a13, -12
s32e a2, a13, -8
s32e a3, a13, -4
s32e a4, a0, -48
s32e a5, a0, -44
s32e a6, a0, -40
s32e a7, a0, -36
s32e a8, a0, -32
s32e a9, a0, -28
s32e a10, a0, -24
s32e a11, a0, -20
rfwo
/* 12-Register Window Underflow Vector (Handler) */
.align 64
.global _WindowUnderflow12
_WindowUnderflow12:
l32e a1, a13, -12
l32e a0, a13, -16
l32e a11, a1, -12
l32e a2, a13, -8
l32e a4, a11, -48
l32e a8, a11, -32
l32e a3, a13, -4
l32e a5, a11, -44
l32e a6, a11, -40
l32e a7, a11, -36
l32e a9, a11, -28
l32e a10, a11, -24
l32e a11, a11, -20
rfwu
.text

View File

@ -0,0 +1,341 @@
/*
* arch/xtensa/kernel/vmlinux.lds.S
*
* Xtensa linker script
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
* Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
*/
#include <asm-generic/vmlinux.lds.h>
#include <linux/config.h>
#define _NOCLANGUAGE
#include <xtensa/config/core.h>
#include <xtensa/config/system.h>
OUTPUT_ARCH(xtensa)
ENTRY(_start)
#if XCHAL_MEMORY_ORDER == XTHAL_BIGENDIAN
jiffies = jiffies_64 + 4;
#else
jiffies = jiffies_64;
#endif
#define KERNELOFFSET 0x1000
/* Note: In the following macros, it would be nice to specify only the
vector name and section kind and construct "sym" and "section" using
CPP concatenation, but that does not work reliably. Concatenating a
string with "." produces an invalid token. CPP will not print a
warning because it thinks this is an assembly file, but it leaves
them as multiple tokens and there may or may not be whitespace
between them. */
/* Macro for a relocation entry */
#define RELOCATE_ENTRY(sym, section) \
LONG(sym ## _start); \
LONG(sym ## _end); \
LONG(LOADADDR(section))
/* Macro to define a section for a vector.
*
* Use of the MIN function catches the types of errors illustrated in
* the following example:
*
* Assume the section .DoubleExceptionVector.literal is completely
* full. Then a programmer adds code to .DoubleExceptionVector.text
* that produces another literal. The final literal position will
* overlay onto the first word of the adjacent code section
* .DoubleExceptionVector.text. (In practice, the literals will
* overwrite the code, and the first few instructions will be
* garbage.)
*/
#define SECTION_VECTOR(sym, section, addr, max_prevsec_size, prevsec) \
section addr : AT((MIN(LOADADDR(prevsec) + max_prevsec_size, \
LOADADDR(prevsec) + SIZEOF(prevsec)) + 3) & ~ 3) \
{ \
. = ALIGN(4); \
sym ## _start = ABSOLUTE(.); \
*(section) \
sym ## _end = ABSOLUTE(.); \
}
/*
* Mapping of input sections to output sections when linking.
*/
SECTIONS
{
. = XCHAL_KSEG_CACHED_VADDR + KERNELOFFSET;
/* .text section */
_text = .;
_stext = .;
_ftext = .;
.text :
{
/* The .head.text section must be the first section! */
*(.head.text)
*(.literal .text)
*(.srom.text)
VMLINUX_SYMBOL(__sched_text_start) = .;
*(.sched.text.literal .sched.text)
VMLINUX_SYMBOL(__sched_text_end) = .;
VMLINUX_SYMBOL(__lock_text_start) = .;
*(.spinlock.text.literal .spinlock.text)
VMLINUX_SYMBOL(__lock_text_end) = .;
}
_etext = .;
. = ALIGN(16);
RODATA
/* Relocation table */
. = ALIGN(16);
__boot_reloc_table_start = ABSOLUTE(.);
__relocate : {
RELOCATE_ENTRY(_WindowVectors_text,
.WindowVectors.text);
#if 0
RELOCATE_ENTRY(_KernelExceptionVector_literal,
.KernelExceptionVector.literal);
#endif
RELOCATE_ENTRY(_KernelExceptionVector_text,
.KernelExceptionVector.text);
#if 0
RELOCATE_ENTRY(_UserExceptionVector_literal,
.UserExceptionVector.literal);
#endif
RELOCATE_ENTRY(_UserExceptionVector_text,
.UserExceptionVector.text);
RELOCATE_ENTRY(_DoubleExceptionVector_literal,
.DoubleExceptionVector.literal);
RELOCATE_ENTRY(_DoubleExceptionVector_text,
.DoubleExceptionVector.text);
}
__boot_reloc_table_end = ABSOLUTE(.) ;
.fixup : { *(.fixup) }
. = ALIGN(16);
__ex_table : {
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
}
/* Data section */
. = ALIGN(XCHAL_ICACHE_LINESIZE);
_fdata = .;
.data :
{
*(.data) CONSTRUCTORS
. = ALIGN(XCHAL_ICACHE_LINESIZE);
*(.data.cacheline_aligned)
}
_edata = .;
/* The initial task */
. = ALIGN(8192);
.data.init_task : { *(.data.init_task) }
/* Initialization code and data: */
. = ALIGN(1<<XCHAL_MMU_MIN_PTE_PAGE_SIZE);
__init_begin = .;
.init.text : {
_sinittext = .;
*(.init.text.literal) *(.init.text)
_einittext = .;
}
.init.data :
{
*(.init.data)
. = ALIGN(0x4);
__tagtable_begin = .;
*(.taglist)
__tagtable_end = .;
}
. = ALIGN(XCHAL_ICACHE_LINESIZE);
__setup_start = .;
.init.setup : { *(.init.setup) }
__setup_end = .;
__initcall_start = .;
.initcall.init : {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
__con_initcall_start = .;
.con_initcall.init : { *(.con_initcall.init) }
__con_initcall_end = .;
SECURITY_INIT
. = ALIGN(4);
__start___ftr_fixup = .;
__ftr_fixup : { *(__ftr_fixup) }
__stop___ftr_fixup = .;
. = ALIGN(32);
__per_cpu_start = .;
.data.percpu : { *(.data.percpu) }
__per_cpu_end = .;
. = ALIGN(4096);
__initramfs_start =.;
.init.ramfs : { *(.init.ramfs) }
__initramfs_end = .;
/* We need this dummy segment here */
. = ALIGN(4);
.dummy : { LONG(0) }
/* The vectors are relocated to the real position at startup time */
SECTION_VECTOR (_WindowVectors_text,
.WindowVectors.text,
XCHAL_WINDOW_VECTORS_VADDR, 4,
.dummy)
SECTION_VECTOR (_DebugInterruptVector_literal,
.DebugInterruptVector.literal,
XCHAL_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL) - 4,
SIZEOF(.WindowVectors.text),
.WindowVectors.text)
SECTION_VECTOR (_DebugInterruptVector_text,
.DebugInterruptVector.text,
XCHAL_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL),
4,
.DebugInterruptVector.literal)
SECTION_VECTOR (_KernelExceptionVector_literal,
.KernelExceptionVector.literal,
XCHAL_KERNELEXC_VECTOR_VADDR - 4,
SIZEOF(.DebugInterruptVector.text),
.DebugInterruptVector.text)
SECTION_VECTOR (_KernelExceptionVector_text,
.KernelExceptionVector.text,
XCHAL_KERNELEXC_VECTOR_VADDR,
4,
.KernelExceptionVector.literal)
SECTION_VECTOR (_UserExceptionVector_literal,
.UserExceptionVector.literal,
XCHAL_USEREXC_VECTOR_VADDR - 4,
SIZEOF(.KernelExceptionVector.text),
.KernelExceptionVector.text)
SECTION_VECTOR (_UserExceptionVector_text,
.UserExceptionVector.text,
XCHAL_USEREXC_VECTOR_VADDR,
4,
.UserExceptionVector.literal)
SECTION_VECTOR (_DoubleExceptionVector_literal,
.DoubleExceptionVector.literal,
XCHAL_DOUBLEEXC_VECTOR_VADDR - 16,
SIZEOF(.UserExceptionVector.text),
.UserExceptionVector.text)
SECTION_VECTOR (_DoubleExceptionVector_text,
.DoubleExceptionVector.text,
XCHAL_DOUBLEEXC_VECTOR_VADDR,
32,
.DoubleExceptionVector.literal)
. = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
. = ALIGN(1<<XCHAL_MMU_MIN_PTE_PAGE_SIZE);
__init_end = .;
. = ALIGN(8192);
/* BSS section */
_bss_start = .;
.sbss : { *(.sbss) *(.scommon) }
.bss : { *(COMMON) *(.bss) }
_bss_end = .;
_end = .;
/* only used by the boot loader */
. = ALIGN(0x10);
.bootstrap : { *(.bootstrap.literal .bootstrap.text .bootstrap.data) }
. = ALIGN(0x1000);
__initrd_start = .;
.initrd : { *(.initrd) }
__initrd_end = .;
.ResetVector.text XCHAL_RESET_VECTOR_VADDR :
{
*(.ResetVector.text)
}
/* Sections to be discarded */
/DISCARD/ :
{
*(.text.exit)
*(.text.exit.literal)
*(.data.exit)
*(.exitcall.exit)
}
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.xt.insn 0 :
{
*(.xt.insn)
*(.gnu.linkonce.x*)
}
.xt.lit 0 :
{
*(.xt.lit)
*(.gnu.linkonce.p*)
}
}

View File

@ -0,0 +1,123 @@
/*
* arch/xtensa/kernel/xtensa_ksyms.c
*
* Export Xtensa-specific functions for loadable modules.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 - 2005 Tensilica Inc.
*
* Joe Taylor <joe@tensilica.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <linux/in6.h>
#include <linux/pci.h>
#include <linux/ide.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/semaphore.h>
#ifdef CONFIG_BLK_DEV_FD
#include <asm/floppy.h>
#endif
#ifdef CONFIG_NET
#include <net/checksum.h>
#endif /* CONFIG_NET */
/*
* String functions
*/
EXPORT_SYMBOL(memcmp);
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(memchr);
EXPORT_SYMBOL(strcat);
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strpbrk);
EXPORT_SYMBOL(strncat);
EXPORT_SYMBOL(strnlen);
EXPORT_SYMBOL(strrchr);
EXPORT_SYMBOL(strstr);
EXPORT_SYMBOL(enable_irq);
EXPORT_SYMBOL(disable_irq);
EXPORT_SYMBOL(kernel_thread);
/*
* gcc internal math functions
*/
extern long long __ashrdi3(long long, int);
extern long long __ashldi3(long long, int);
extern long long __lshrdi3(long long, int);
extern int __divsi3(int, int);
extern int __modsi3(int, int);
extern long long __muldi3(long long, long long);
extern int __mulsi3(int, int);
extern unsigned int __udivsi3(unsigned int, unsigned int);
extern unsigned int __umodsi3(unsigned int, unsigned int);
extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__ashrdi3);
EXPORT_SYMBOL(__lshrdi3);
EXPORT_SYMBOL(__divsi3);
EXPORT_SYMBOL(__modsi3);
EXPORT_SYMBOL(__muldi3);
EXPORT_SYMBOL(__mulsi3);
EXPORT_SYMBOL(__udivsi3);
EXPORT_SYMBOL(__umodsi3);
EXPORT_SYMBOL(__udivdi3);
EXPORT_SYMBOL(__umoddi3);
/*
* Semaphore operations
*/
EXPORT_SYMBOL(__down);
EXPORT_SYMBOL(__down_interruptible);
EXPORT_SYMBOL(__down_trylock);
EXPORT_SYMBOL(__up);
#ifdef CONFIG_NET
/*
* Networking support
*/
EXPORT_SYMBOL(csum_partial_copy_generic);
#endif /* CONFIG_NET */
/*
* Architecture-specific symbols
*/
EXPORT_SYMBOL(__xtensa_copy_user);
/*
* Kernel hacking ...
*/
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
// FIXME EXPORT_SYMBOL(screen_info);
#endif
EXPORT_SYMBOL(get_wchan);
EXPORT_SYMBOL(outsb);
EXPORT_SYMBOL(outsw);
EXPORT_SYMBOL(outsl);
EXPORT_SYMBOL(insb);
EXPORT_SYMBOL(insw);
EXPORT_SYMBOL(insl);