forked from Minki/linux
[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>
This commit is contained in:
parent
4bedea9454
commit
5a0015d626
18
arch/xtensa/kernel/Makefile
Normal file
18
arch/xtensa/kernel/Makefile
Normal 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
|
||||
|
||||
|
459
arch/xtensa/kernel/align.S
Normal file
459
arch/xtensa/kernel/align.S
Normal 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 */
|
||||
|
94
arch/xtensa/kernel/asm-offsets.c
Normal file
94
arch/xtensa/kernel/asm-offsets.c
Normal 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;
|
||||
}
|
||||
|
||||
|
201
arch/xtensa/kernel/coprocessor.S
Normal file
201
arch/xtensa/kernel/coprocessor.S
Normal 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:
|
||||
|
1996
arch/xtensa/kernel/entry.S
Normal file
1996
arch/xtensa/kernel/entry.S
Normal file
File diff suppressed because it is too large
Load Diff
237
arch/xtensa/kernel/head.S
Normal file
237
arch/xtensa/kernel/head.S
Normal 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
|
||||
|
192
arch/xtensa/kernel/irq.c
Normal file
192
arch/xtensa/kernel/irq.c
Normal 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();
|
||||
}
|
78
arch/xtensa/kernel/module.c
Normal file
78
arch/xtensa/kernel/module.c
Normal 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");
|
||||
}
|
73
arch/xtensa/kernel/pci-dma.c
Normal file
73
arch/xtensa/kernel/pci-dma.c
Normal 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;
|
||||
}
|
||||
}
|
563
arch/xtensa/kernel/pci.c
Normal file
563
arch/xtensa/kernel/pci.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
49
arch/xtensa/kernel/platform.c
Normal file
49
arch/xtensa/kernel/platform.c
Normal 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
|
||||
|
482
arch/xtensa/kernel/process.c
Normal file
482
arch/xtensa/kernel/process.c
Normal 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],
|
||||
®s->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);
|
||||
}
|
407
arch/xtensa/kernel/ptrace.c
Normal file
407
arch/xtensa/kernel/ptrace.c
Normal 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;
|
||||
}
|
||||
}
|
226
arch/xtensa/kernel/semaphore.c
Normal file
226
arch/xtensa/kernel/semaphore.c
Normal 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);
|
||||
}
|
520
arch/xtensa/kernel/setup.c
Normal file
520
arch/xtensa/kernel/setup.c
Normal 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 */
|
||||
|
713
arch/xtensa/kernel/signal.c
Normal file
713
arch/xtensa/kernel/signal.c
Normal 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(¤t->sighand->siglock);
|
||||
saveset = current->blocked;
|
||||
siginitset(¤t->blocked, mask);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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(¤t->sighand->siglock);
|
||||
saveset = current->blocked;
|
||||
current->blocked = newset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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 = ¤t->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 = ¤t->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(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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 = ¤t->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(¤t->sighand->siglock);
|
||||
sigorsets(¤t->blocked, ¤t->blocked, &ka.sa.sa_mask);
|
||||
sigaddset(¤t->blocked, signr);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
return 1;
|
||||
}
|
418
arch/xtensa/kernel/syscalls.c
Normal file
418
arch/xtensa/kernel/syscalls.c
Normal 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(¤t->mm->mmap_sem);
|
||||
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
|
||||
up_write(¤t->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;
|
||||
}
|
||||
|
248
arch/xtensa/kernel/syscalls.h
Normal file
248
arch/xtensa/kernel/syscalls.h
Normal 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 */
|
227
arch/xtensa/kernel/time.c
Normal file
227
arch/xtensa/kernel/time.c
Normal 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
|
||||
|
498
arch/xtensa/kernel/traps.c
Normal file
498
arch/xtensa/kernel/traps.c
Normal 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);
|
||||
}
|
||||
|
||||
|
464
arch/xtensa/kernel/vectors.S
Normal file
464
arch/xtensa/kernel/vectors.S
Normal 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
|
||||
|
||||
|
341
arch/xtensa/kernel/vmlinux.lds.S
Normal file
341
arch/xtensa/kernel/vmlinux.lds.S
Normal 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*)
|
||||
}
|
||||
}
|
123
arch/xtensa/kernel/xtensa_ksyms.c
Normal file
123
arch/xtensa/kernel/xtensa_ksyms.c
Normal 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);
|
Loading…
Reference in New Issue
Block a user