Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,39 @@
#
# Makefile for the Linux/Xtensa kernel.
#
extra-y := head.o vmlinux.lds
obj-y := align.o coprocessor.o entry.o irq.o pci-dma.o platform.o process.o \
ptrace.o setup.o signal.o stacktrace.o syscall.o time.o traps.o \
vectors.o
obj-$(CONFIG_KGDB) += xtensa-stub.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
obj-$(CONFIG_SMP) += smp.o mxhead.o
AFLAGS_head.o += -mtext-section-literals
# In the Xtensa architecture, assembly generates literals which must always
# precede the L32R instruction with a relative offset less than 256 kB.
# Therefore, the .text and .literal section must be combined in parenthesis
# in the linker script, such as: *(.literal .text).
#
# We need to post-process the generated vmlinux.lds scripts to convert
# *(xxx.text) to *(xxx.literal xxx.text) for the following text sections:
# .text .ref.text .*init.text .*exit.text .text.*
#
# Replicate rules in scripts/Makefile.build
sed-y = -e 's/\*(\(\.[a-z]*it\|\.ref\|\)\.text)/*(\1.literal \1.text)/g' \
-e 's/\.text\.unlikely/.literal.unlikely .text.unlikely/g' \
-e 's/\*(\(\.text\.[a-z]*\))/*(\1.literal \1)/g'
quiet_cmd__cpp_lds_S = LDS $@
cmd__cpp_lds_S = $(CPP) $(cpp_flags) -P -C -Uxtensa -D__ASSEMBLY__ $< \
| sed $(sed-y) >$@
$(obj)/vmlinux.lds: $(src)/vmlinux.lds.S FORCE
$(call if_changed_dep,_cpp_lds_S)

488
arch/xtensa/kernel/align.S Normal file
View file

@ -0,0 +1,488 @@
/*
* 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.
* Copyright (C) 2014 Cadence Design Systems 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/current.h>
#include <asm/asm-offsets.h>
#include <asm/processor.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: a3
* depc: a2, original value saved on stack (PT_DEPC)
* excsave_1: dispatch table
*
* 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
s32i a0, a2, PT_AREG2
s32i a3, a2, PT_AREG3
rsr a3, excsave1
movi a4, fast_unaligned_fixup
s32i a4, a3, EXC_TABLE_FIXUP
/* 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, epc1 # 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_DENSITY
_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
/* 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_DENSITY
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:
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
/* We cannot handle this exception. */
.extern _kernel_exception
.Linvalid_instruction_load:
.Linvalid_instruction_store:
movi a4, 0
rsr a3, excsave1
s32i a4, a3, EXC_TABLE_FIXUP
/* 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 a0, PS_UM_BIT, 2f # jump if user mode
movi a0, _kernel_exception
jx a0
2: movi a0, _user_exception
jx a0
1: # a7: instruction pointer, a4: instruction, a3: value
movi a6, 0 # mask: ffffffff:00000000
#if XCHAL_HAVE_DENSITY
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:
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 a8, 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, a8 # mask
__sh a8, a3 # shift value
or a5, a5, a8 # or with original value
#ifdef UNALIGNED_USER_EXCEPTION
s32e a5, a4, -8
l32e a8, a4, -4
#else
s32i a5, a4, 0 # store
l32i a8, a4, 4 # same for upper address word
#endif
__sl a5, a3
and a6, a8, a6
or a6, a6, a5
#ifdef UNALIGNED_USER_EXCEPTION
s32e a6, a4, -4
#else
s32i a6, a4, 4
#endif
.Lexit:
#if XCHAL_HAVE_LOOPS
rsr a4, lend # check if we reached LEND
bne a7, a4, 1f
rsr a4, lcount # and LCOUNT != 0
beqz a4, 1f
addi a4, a4, -1 # decrement LCOUNT and set
rsr a7, lbeg # set PC to LBEGIN
wsr a4, lcount
#endif
1: wsr a7, epc1 # skip emulated instruction
/* Update icount if we're single-stepping in userspace. */
rsr a4, icountlevel
beqz a4, 1f
bgeui a4, LOCKLEVEL + 1, 1f
rsr a4, icount
addi a4, a4, 1
wsr a4, icount
1:
movi a4, 0
rsr a3, excsave1
s32i a4, a3, EXC_TABLE_FIXUP
/* Restore working register */
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
l32i a3, a2, PT_AREG3
/* restore SAR and return */
wsr a0, sar
l32i a0, a2, PT_AREG0
l32i a2, a2, PT_AREG2
rfe
ENDPROC(fast_unaligned)
ENTRY(fast_unaligned_fixup)
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
wsr a3, excsave1
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
l32i a0, a2, PT_AREG2
xsr a0, depc # restore depc and a0
wsr a0, sar
rsr a0, exccause
s32i a0, a2, PT_DEPC # mark as a regular exception
rsr a0, ps
bbsi.l a0, PS_UM_BIT, 1f # jump if user mode
rsr a0, exccause
addx4 a0, a0, a3 # find entry in table
l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler
l32i a3, a2, PT_AREG3
jx a0
1:
rsr a0, exccause
addx4 a0, a0, a3 # find entry in table
l32i a0, a0, EXC_TABLE_FAST_USER # load handler
l32i a3, a2, PT_AREG3
jx a0
ENDPROC(fast_unaligned_fixup)
#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */

View file

@ -0,0 +1,113 @@
/*
* 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 <asm/coprocessor.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/thread_info.h>
#include <linux/ptrace.h>
#include <linux/mm.h>
#include <linux/kbuild.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>
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_ICOUNTLEVEL, offsetof (struct pt_regs, icountlevel));
DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
DEFINE(PT_SCOMPARE1, offsetof(struct pt_regs, scompare1));
DEFINE(PT_THREADPTR, offsetof(struct pt_regs, threadptr));
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]));
DEFINE(PT_XTREGS_OPT, offsetof(struct pt_regs, xtregs_opt));
DEFINE(XTREGS_OPT_SIZE, sizeof(xtregs_opt_t));
/* struct task_struct */
DEFINE(TASK_PTRACE, offsetof (struct task_struct, ptrace));
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, stack));
DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct));
/* 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_CPENABLE, offsetof (struct thread_info, cpenable));
#if XTENSA_HAVE_COPROCESSORS
DEFINE(THREAD_XTREGS_CP0, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP1, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP2, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP3, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP4, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP5, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP6, offsetof (struct thread_info, xtregs_cp));
DEFINE(THREAD_XTREGS_CP7, offsetof (struct thread_info, xtregs_cp));
#endif
DEFINE(THREAD_XTREGS_USER, offsetof (struct thread_info, xtregs_user));
DEFINE(XTREGS_USER_SIZE, sizeof(xtregs_user_t));
DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, \
thread.current_ds));
/* struct mm_struct */
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));
/* struct page */
DEFINE(PAGE_FLAGS, offsetof(struct page, flags));
/* constants */
DEFINE(_CLONE_VM, CLONE_VM);
DEFINE(_CLONE_UNTRACED, CLONE_UNTRACED);
DEFINE(PG_ARCH_1, PG_arch_1);
return 0;
}

View file

@ -0,0 +1,355 @@
/*
* 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 - 2007 Tensilica Inc.
*/
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/processor.h>
#include <asm/coprocessor.h>
#include <asm/thread_info.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/ptrace.h>
#include <asm/current.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/signal.h>
#include <asm/tlbflush.h>
/*
* Entry condition:
*
* a0: trashed, original value saved on stack (PT_AREG0)
* a1: a1
* a2: new stack pointer, original in DEPC
* a3: a3
* depc: a2, original value saved on stack (PT_DEPC)
* excsave_1: dispatch table
*
* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
*/
/* IO protection is currently unsupported. */
ENTRY(fast_io_protect)
wsr a0, excsave1
movi a0, unrecoverable_exception
callx0 a0
ENDPROC(fast_io_protect)
#if XTENSA_HAVE_COPROCESSORS
/*
* Macros for lazy context switch.
*/
#define SAVE_CP_REGS(x) \
.align 4; \
.Lsave_cp_regs_cp##x: \
.if XTENSA_HAVE_COPROCESSOR(x); \
xchal_cp##x##_store a2 a4 a5 a6 a7; \
.endif; \
jx a0
#define SAVE_CP_REGS_TAB(x) \
.if XTENSA_HAVE_COPROCESSOR(x); \
.long .Lsave_cp_regs_cp##x - .Lsave_cp_regs_jump_table; \
.else; \
.long 0; \
.endif; \
.long THREAD_XTREGS_CP##x
#define LOAD_CP_REGS(x) \
.align 4; \
.Lload_cp_regs_cp##x: \
.if XTENSA_HAVE_COPROCESSOR(x); \
xchal_cp##x##_load a2 a4 a5 a6 a7; \
.endif; \
jx a0
#define LOAD_CP_REGS_TAB(x) \
.if XTENSA_HAVE_COPROCESSOR(x); \
.long .Lload_cp_regs_cp##x - .Lload_cp_regs_jump_table; \
.else; \
.long 0; \
.endif; \
.long THREAD_XTREGS_CP##x
SAVE_CP_REGS(0)
SAVE_CP_REGS(1)
SAVE_CP_REGS(2)
SAVE_CP_REGS(3)
SAVE_CP_REGS(4)
SAVE_CP_REGS(5)
SAVE_CP_REGS(6)
SAVE_CP_REGS(7)
LOAD_CP_REGS(0)
LOAD_CP_REGS(1)
LOAD_CP_REGS(2)
LOAD_CP_REGS(3)
LOAD_CP_REGS(4)
LOAD_CP_REGS(5)
LOAD_CP_REGS(6)
LOAD_CP_REGS(7)
.align 4
.Lsave_cp_regs_jump_table:
SAVE_CP_REGS_TAB(0)
SAVE_CP_REGS_TAB(1)
SAVE_CP_REGS_TAB(2)
SAVE_CP_REGS_TAB(3)
SAVE_CP_REGS_TAB(4)
SAVE_CP_REGS_TAB(5)
SAVE_CP_REGS_TAB(6)
SAVE_CP_REGS_TAB(7)
.Lload_cp_regs_jump_table:
LOAD_CP_REGS_TAB(0)
LOAD_CP_REGS_TAB(1)
LOAD_CP_REGS_TAB(2)
LOAD_CP_REGS_TAB(3)
LOAD_CP_REGS_TAB(4)
LOAD_CP_REGS_TAB(5)
LOAD_CP_REGS_TAB(6)
LOAD_CP_REGS_TAB(7)
/*
* coprocessor_save(buffer, index)
* a2 a3
* coprocessor_load(buffer, index)
* a2 a3
*
* Save or load coprocessor registers for coprocessor 'index'.
* The register values are saved to or loaded from them 'buffer' address.
*
* Note that these functions don't update the coprocessor_owner information!
*
*/
ENTRY(coprocessor_save)
entry a1, 32
s32i a0, a1, 0
movi a0, .Lsave_cp_regs_jump_table
addx8 a3, a3, a0
l32i a3, a3, 0
beqz a3, 1f
add a0, a0, a3
callx0 a0
1: l32i a0, a1, 0
retw
ENDPROC(coprocessor_save)
ENTRY(coprocessor_load)
entry a1, 32
s32i a0, a1, 0
movi a0, .Lload_cp_regs_jump_table
addx4 a3, a3, a0
l32i a3, a3, 0
beqz a3, 1f
add a0, a0, a3
callx0 a0
1: l32i a0, a1, 0
retw
ENDPROC(coprocessor_load)
/*
* coprocessor_flush(struct task_info*, index)
* a2 a3
* coprocessor_restore(struct task_info*, index)
* a2 a3
*
* Save or load coprocessor registers for coprocessor 'index'.
* The register values are saved to or loaded from the coprocessor area
* inside the task_info structure.
*
* Note that these functions don't update the coprocessor_owner information!
*
*/
ENTRY(coprocessor_flush)
entry a1, 32
s32i a0, a1, 0
movi a0, .Lsave_cp_regs_jump_table
addx8 a3, a3, a0
l32i a4, a3, 4
l32i a3, a3, 0
add a2, a2, a4
beqz a3, 1f
add a0, a0, a3
callx0 a0
1: l32i a0, a1, 0
retw
ENDPROC(coprocessor_flush)
ENTRY(coprocessor_restore)
entry a1, 32
s32i a0, a1, 0
movi a0, .Lload_cp_regs_jump_table
addx4 a3, a3, a0
l32i a4, a3, 4
l32i a3, a3, 0
add a2, a2, a4
beqz a3, 1f
add a0, a0, a3
callx0 a0
1: l32i a0, a1, 0
retw
ENDPROC(coprocessor_restore)
/*
* Entry condition:
*
* a0: trashed, original value saved on stack (PT_AREG0)
* a1: a1
* a2: new stack pointer, original in DEPC
* a3: a3
* depc: a2, original value saved on stack (PT_DEPC)
* excsave_1: dispatch table
*
* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
*/
ENTRY(fast_coprocessor_double)
wsr a0, excsave1
movi a0, unrecoverable_exception
callx0 a0
ENDPROC(fast_coprocessor_double)
ENTRY(fast_coprocessor)
/* Save remaining registers a1-a3 and SAR */
s32i a3, a2, PT_AREG3
rsr a3, sar
s32i a1, a2, PT_AREG1
s32i a3, a2, PT_SAR
mov a1, a2
rsr a2, depc
s32i a2, a1, PT_AREG2
/*
* The hal macros require up to 4 temporary registers. We use a3..a6.
*/
s32i a4, a1, PT_AREG4
s32i a5, a1, PT_AREG5
s32i a6, a1, PT_AREG6
/* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
rsr a3, exccause
addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
/* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/
ssl a3 # SAR: 32 - coprocessor_number
movi a2, 1
rsr a0, cpenable
sll a2, a2
or a0, a0, a2
wsr a0, cpenable
rsync
/* Retrieve previous owner. (a3 still holds CP number) */
movi a0, coprocessor_owner # list of owners
addx4 a0, a3, a0 # entry for CP
l32i a4, a0, 0
beqz a4, 1f # skip 'save' if no previous owner
/* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
l32i a5, a4, THREAD_CPENABLE
xor a5, a5, a2 # (1 << cp-id) still in a2
s32i a5, a4, THREAD_CPENABLE
/*
* Get context save area and 'call' save routine.
* (a4 still holds previous owner (thread_info), a3 CP number)
*/
movi a5, .Lsave_cp_regs_jump_table
movi a0, 2f # a0: 'return' address
addx8 a3, a3, a5 # a3: coprocessor number
l32i a2, a3, 4 # a2: xtregs offset
l32i a3, a3, 0 # a3: jump offset
add a2, a2, a4
add a4, a3, a5 # a4: address of save routine
jx a4
/* Note that only a0 and a1 were preserved. */
2: rsr a3, exccause
addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
movi a0, coprocessor_owner
addx4 a0, a3, a0
/* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */
1: GET_THREAD_INFO (a4, a1)
s32i a4, a0, 0
/* Get context save area and 'call' load routine. */
movi a5, .Lload_cp_regs_jump_table
movi a0, 1f
addx8 a3, a3, a5
l32i a2, a3, 4 # a2: xtregs offset
l32i a3, a3, 0 # a3: jump offset
add a2, a2, a4
add a4, a3, a5
jx a4
/* Restore all registers and return from exception handler. */
1: l32i a6, a1, PT_AREG6
l32i a5, a1, PT_AREG5
l32i a4, a1, PT_AREG4
l32i a0, a1, PT_SAR
l32i a3, a1, PT_AREG3
l32i a2, a1, PT_AREG2
wsr a0, sar
l32i a0, a1, PT_AREG0
l32i a1, a1, PT_AREG1
rfe
ENDPROC(fast_coprocessor)
.data
ENTRY(coprocessor_owner)
.fill XCHAL_CP_MAX, 4, 0
END(coprocessor_owner)
#endif /* XTENSA_HAVE_COPROCESSORS */

1938
arch/xtensa/kernel/entry.S Normal file

File diff suppressed because it is too large Load diff

375
arch/xtensa/kernel/head.S Normal file
View file

@ -0,0 +1,375 @@
/*
* 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 - 2008 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 <asm/processor.h>
#include <asm/page.h>
#include <asm/cacheasm.h>
#include <asm/initialize_mmu.h>
#include <asm/mxregs.h>
#include <linux/init.h>
#include <linux/linkage.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)
*
*/
/*
* _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.
*/
__HEAD
.begin no-absolute-literals
ENTRY(_start)
/* Preserve the pointer to the boot parameter list in EXCSAVE_1 */
wsr a2, excsave1
_j _SetupOCD
.align 4
.literal_position
.Lstartup:
.word _startup
.align 4
_SetupOCD:
/*
* Initialize WB, WS, and clear PS.EXCM (to allow loop instructions).
* Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow
* xt-gdb to single step via DEBUG exceptions received directly
* by ocd.
*/
movi a1, 1
movi a0, 0
wsr a1, windowstart
wsr a0, windowbase
rsync
movi a1, LOCKLEVEL
wsr a1, ps
rsync
.global _SetupMMU
_SetupMMU:
Offset = _SetupMMU - _start
#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
initialize_mmu
#if defined(CONFIG_MMU) && XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY
rsr a2, excsave1
movi a3, 0x08000000
bgeu a2, a3, 1f
movi a3, 0xd0000000
add a2, a2, a3
wsr a2, excsave1
1:
#endif
#endif
.end no-absolute-literals
l32r a0, .Lstartup
jx a0
ENDPROC(_start)
__REF
.literal_position
ENTRY(_startup)
/* Set a0 to 0 for the remaining initialization. */
movi a0, 0
/* Clear debugging registers. */
#if XCHAL_HAVE_DEBUG
#if XCHAL_NUM_IBREAK > 0
wsr a0, ibreakenable
#endif
wsr a0, icount
movi a1, 15
wsr a0, icountlevel
.set _index, 0
.rept XCHAL_NUM_DBREAK - 1
wsr a0, SREG_DBREAKC + _index
.set _index, _index + 1
.endr
#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. */
.set _index, 0
.rept XCHAL_NUM_TIMERS
wsr a0, SREG_CCOMPARE + _index
.set _index, _index + 1
.endr
/* Interrupt initialization. */
movi a2, XCHAL_INTTYPE_MASK_SOFTWARE | XCHAL_INTTYPE_MASK_EXTERN_EDGE
wsr a0, intenable
wsr a2, intclear
/* Disable coprocessors. */
#if XCHAL_HAVE_CP
wsr a0, cpenable
#endif
/* Initialize the caches.
* a2, a3 are just working registers (clobbered).
*/
#if XCHAL_DCACHE_LINE_LOCKABLE
___unlock_dcache_all a2 a3
#endif
#if XCHAL_ICACHE_LINE_LOCKABLE
___unlock_icache_all a2 a3
#endif
___invalidate_dcache_all a2 a3
___invalidate_icache_all a2 a3
isync
#ifdef CONFIG_HAVE_SMP
movi a2, CCON # MX External Register to Configure Cache
movi a3, 1
wer a3, a2
#endif
/* Setup stack and enable window exceptions (keep irqs disabled) */
movi a1, start_info
l32i a1, a1, 0
movi a2, (1 << PS_WOE_BIT) | LOCKLEVEL
# WOE=1, INTLEVEL=LOCKLEVEL, 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, SREG_EXCSAVE + XCHAL_DEBUGLEVEL
#ifdef CONFIG_SMP
/*
* Notice that we assume with SMP that cores have PRID
* supported by the cores.
*/
rsr a2, prid
bnez a2, .Lboot_secondary
#endif /* CONFIG_SMP */
/* 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_stop # end of BSS
__loopt a2, a3, a4, 2
s32i a0, a2, 0
__endla a2, a4, 4
#if XCHAL_DCACHE_IS_WRITEBACK
/* After unpacking, flush the writeback cache to memory so the
* instructions/data are available.
*/
___flush_dcache_all a2 a3
#endif
memw
isync
___invalidate_icache_all a2 a3
isync
movi a6, 0
xsr a6, excsave1
/* 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
#ifdef CONFIG_SMP
.Lboot_secondary:
movi a2, cpu_start_ccount
1:
l32i a3, a2, 0
beqi a3, 0, 1b
movi a3, 0
s32i a3, a2, 0
memw
1:
l32i a3, a2, 0
beqi a3, 0, 1b
wsr a3, ccount
movi a3, 0
s32i a3, a2, 0
memw
movi a6, 0
wsr a6, excsave1
movi a4, secondary_start_kernel
callx4 a4
j should_never_return
#endif /* CONFIG_SMP */
ENDPROC(_startup)
#ifdef CONFIG_HOTPLUG_CPU
ENTRY(cpu_restart)
#if XCHAL_DCACHE_IS_WRITEBACK
___flush_invalidate_dcache_all a2 a3
#else
___invalidate_dcache_all a2 a3
#endif
memw
movi a2, CCON # MX External Register to Configure Cache
movi a3, 0
wer a3, a2
extw
rsr a0, prid
neg a2, a0
movi a3, cpu_start_id
s32i a2, a3, 0
#if XCHAL_DCACHE_IS_WRITEBACK
dhwbi a3, 0
#endif
1:
l32i a2, a3, 0
dhi a3, 0
bne a2, a0, 1b
/*
* Initialize WB, WS, and clear PS.EXCM (to allow loop instructions).
* Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow
* xt-gdb to single step via DEBUG exceptions received directly
* by ocd.
*/
movi a1, 1
movi a0, 0
wsr a1, windowstart
wsr a0, windowbase
rsync
movi a1, LOCKLEVEL
wsr a1, ps
rsync
j _startup
ENDPROC(cpu_restart)
#endif /* CONFIG_HOTPLUG_CPU */
/*
* DATA section
*/
.section ".data.init.refok"
.align 4
ENTRY(start_info)
.long init_thread_union + KERNEL_STACK_SIZE
/*
* BSS section
*/
__PAGE_ALIGNED_BSS
#ifdef CONFIG_MMU
ENTRY(swapper_pg_dir)
.fill PAGE_SIZE, 1, 0
END(swapper_pg_dir)
#endif
ENTRY(empty_zero_page)
.fill PAGE_SIZE, 1, 0
END(empty_zero_page)

188
arch/xtensa/kernel/irq.c Normal file
View file

@ -0,0 +1,188 @@
/*
* linux/arch/xtensa/kernel/irq.c
*
* Xtensa built-in interrupt controller and some generic functions copied
* from i386.
*
* Copyright (C) 2002 - 2013 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 <linux/irqchip.h>
#include <linux/irqchip/xtensa-mx.h>
#include <linux/irqchip/xtensa-pic.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <asm/mxregs.h>
#include <asm/uaccess.h>
#include <asm/platform.h>
atomic_t irq_err_count;
asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
{
int irq = irq_find_mapping(NULL, hwirq);
if (hwirq >= NR_IRQS) {
printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
__func__, hwirq);
}
#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
generic_handle_irq(irq);
}
int arch_show_interrupts(struct seq_file *p, int prec)
{
#ifdef CONFIG_SMP
show_ipi_list(p, prec);
#endif
seq_printf(p, "%*s: ", prec, "ERR");
seq_printf(p, "%10u\n", atomic_read(&irq_err_count));
return 0;
}
int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize,
unsigned long int_irq, unsigned long ext_irq,
unsigned long *out_hwirq, unsigned int *out_type)
{
if (WARN_ON(intsize < 1 || intsize > 2))
return -EINVAL;
if (intsize == 2 && intspec[1] == 1) {
int_irq = xtensa_map_ext_irq(ext_irq);
if (int_irq < XCHAL_NUM_INTERRUPTS)
*out_hwirq = int_irq;
else
return -EINVAL;
} else {
*out_hwirq = int_irq;
}
*out_type = IRQ_TYPE_NONE;
return 0;
}
int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
struct irq_chip *irq_chip = d->host_data;
u32 mask = 1 << hw;
if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) {
irq_set_chip_and_handler_name(irq, irq_chip,
handle_simple_irq, "level");
irq_set_status_flags(irq, IRQ_LEVEL);
} else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) {
irq_set_chip_and_handler_name(irq, irq_chip,
handle_edge_irq, "edge");
irq_clear_status_flags(irq, IRQ_LEVEL);
} else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) {
irq_set_chip_and_handler_name(irq, irq_chip,
handle_level_irq, "level");
irq_set_status_flags(irq, IRQ_LEVEL);
} else if (mask & XCHAL_INTTYPE_MASK_TIMER) {
irq_set_chip_and_handler_name(irq, irq_chip,
handle_percpu_irq, "timer");
irq_clear_status_flags(irq, IRQ_LEVEL);
} else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */
/* XCHAL_INTTYPE_MASK_NMI */
irq_set_chip_and_handler_name(irq, irq_chip,
handle_level_irq, "level");
irq_set_status_flags(irq, IRQ_LEVEL);
}
return 0;
}
unsigned xtensa_map_ext_irq(unsigned ext_irq)
{
unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE |
XCHAL_INTTYPE_MASK_EXTERN_LEVEL;
unsigned i;
for (i = 0; mask; ++i, mask >>= 1) {
if ((mask & 1) && ext_irq-- == 0)
return i;
}
return XCHAL_NUM_INTERRUPTS;
}
unsigned xtensa_get_ext_irq_no(unsigned irq)
{
unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
XCHAL_INTTYPE_MASK_EXTERN_LEVEL) &
((1u << irq) - 1);
return hweight32(mask);
}
void __init init_IRQ(void)
{
#ifdef CONFIG_OF
irqchip_init();
#else
#ifdef CONFIG_HAVE_SMP
xtensa_mx_init_legacy(NULL);
#else
xtensa_pic_init_legacy(NULL);
#endif
#endif
#ifdef CONFIG_SMP
ipi_init();
#endif
variant_init_irq();
}
#ifdef CONFIG_HOTPLUG_CPU
/*
* The CPU has been marked offline. Migrate IRQs off this CPU. If
* the affinity settings do not allow other CPUs, force them onto any
* available CPU.
*/
void migrate_irqs(void)
{
unsigned int i, cpu = smp_processor_id();
for_each_active_irq(i) {
struct irq_data *data = irq_get_irq_data(i);
unsigned int newcpu;
if (irqd_is_per_cpu(data))
continue;
if (!cpumask_test_cpu(cpu, data->affinity))
continue;
newcpu = cpumask_any_and(data->affinity, cpu_online_mask);
if (newcpu >= nr_cpu_ids) {
pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n",
i, cpu);
cpumask_setall(data->affinity);
}
irq_set_affinity(i, data->affinity);
}
}
#endif /* CONFIG_HOTPLUG_CPU */

View file

@ -0,0 +1,50 @@
/*
* arch/xtensa/kernel/mcount.S
*
* Xtensa specific mcount 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) 2013 Tensilica Inc.
*/
#include <linux/linkage.h>
#include <asm/ftrace.h>
/*
* Entry condition:
*
* a2: a0 of the caller
*/
ENTRY(_mcount)
entry a1, 16
movi a4, ftrace_trace_function
l32i a4, a4, 0
movi a3, ftrace_stub
bne a3, a4, 1f
retw
1: xor a7, a2, a1
movi a3, 0x3fffffff
and a7, a7, a3
xor a7, a7, a1
xor a6, a0, a1
and a6, a6, a3
xor a6, a6, a1
addi a6, a6, -MCOUNT_INSN_SIZE
callx4 a4
retw
ENDPROC(_mcount)
ENTRY(ftrace_stub)
entry a1, 16
retw
ENDPROC(ftrace_stub)

192
arch/xtensa/kernel/module.c Normal file
View file

@ -0,0 +1,192 @@
/*
* arch/xtensa/kernel/module.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 - 2006 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>
#undef DEBUG_RELOCATE
static int
decode_calln_opcode (unsigned char *location)
{
#ifdef __XTENSA_EB__
return (location[0] & 0xf0) == 0x50;
#endif
#ifdef __XTENSA_EL__
return (location[0] & 0xf) == 0x5;
#endif
}
static int
decode_l32r_opcode (unsigned char *location)
{
#ifdef __XTENSA_EB__
return (location[0] & 0xf0) == 0x10;
#endif
#ifdef __XTENSA_EL__
return (location[0] & 0xf) == 0x1;
#endif
}
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *mod)
{
unsigned int i;
Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
Elf32_Sym *sym;
unsigned char *location;
uint32_t value;
#ifdef DEBUG_RELOCATE
printk("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
#endif
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
location = (char *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rela[i].r_offset;
sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ ELF32_R_SYM(rela[i].r_info);
value = sym->st_value + rela[i].r_addend;
switch (ELF32_R_TYPE(rela[i].r_info)) {
case R_XTENSA_NONE:
case R_XTENSA_DIFF8:
case R_XTENSA_DIFF16:
case R_XTENSA_DIFF32:
case R_XTENSA_ASM_EXPAND:
break;
case R_XTENSA_32:
case R_XTENSA_PLT:
*(uint32_t *)location += value;
break;
case R_XTENSA_SLOT0_OP:
if (decode_calln_opcode(location)) {
value -= ((unsigned long)location & -4) + 4;
if ((value & 3) != 0 ||
((value + (1 << 19)) >> 20) != 0) {
printk("%s: relocation out of range, "
"section %d reloc %d "
"sym '%s'\n",
mod->name, relsec, i,
strtab + sym->st_name);
return -ENOEXEC;
}
value = (signed int)value >> 2;
#ifdef __XTENSA_EB__
location[0] = ((location[0] & ~0x3) |
((value >> 16) & 0x3));
location[1] = (value >> 8) & 0xff;
location[2] = value & 0xff;
#endif
#ifdef __XTENSA_EL__
location[0] = ((location[0] & ~0xc0) |
((value << 6) & 0xc0));
location[1] = (value >> 2) & 0xff;
location[2] = (value >> 10) & 0xff;
#endif
} else if (decode_l32r_opcode(location)) {
value -= (((unsigned long)location + 3) & -4);
if ((value & 3) != 0 ||
(signed int)value >> 18 != -1) {
printk("%s: relocation out of range, "
"section %d reloc %d "
"sym '%s'\n",
mod->name, relsec, i,
strtab + sym->st_name);
return -ENOEXEC;
}
value = (signed int)value >> 2;
#ifdef __XTENSA_EB__
location[1] = (value >> 8) & 0xff;
location[2] = value & 0xff;
#endif
#ifdef __XTENSA_EL__
location[1] = value & 0xff;
location[2] = (value >> 8) & 0xff;
#endif
}
/* FIXME: Ignore any other opcodes. The Xtensa
assembler currently assumes that the linker will
always do relaxation and so all PC-relative
operands need relocations. (The assembler also
writes out the tentative PC-relative values,
assuming no link-time relaxation, so it is usually
safe to ignore the relocations.) If the
assembler's "--no-link-relax" flag can be made to
work, and if all kernel modules can be assembled
with that flag, then unexpected relocations could
be detected here. */
break;
case R_XTENSA_SLOT1_OP:
case R_XTENSA_SLOT2_OP:
case R_XTENSA_SLOT3_OP:
case R_XTENSA_SLOT4_OP:
case R_XTENSA_SLOT5_OP:
case R_XTENSA_SLOT6_OP:
case R_XTENSA_SLOT7_OP:
case R_XTENSA_SLOT8_OP:
case R_XTENSA_SLOT9_OP:
case R_XTENSA_SLOT10_OP:
case R_XTENSA_SLOT11_OP:
case R_XTENSA_SLOT12_OP:
case R_XTENSA_SLOT13_OP:
case R_XTENSA_SLOT14_OP:
printk("%s: unexpected FLIX relocation: %u\n",
mod->name,
ELF32_R_TYPE(rela[i].r_info));
return -ENOEXEC;
case R_XTENSA_SLOT0_ALT:
case R_XTENSA_SLOT1_ALT:
case R_XTENSA_SLOT2_ALT:
case R_XTENSA_SLOT3_ALT:
case R_XTENSA_SLOT4_ALT:
case R_XTENSA_SLOT5_ALT:
case R_XTENSA_SLOT6_ALT:
case R_XTENSA_SLOT7_ALT:
case R_XTENSA_SLOT8_ALT:
case R_XTENSA_SLOT9_ALT:
case R_XTENSA_SLOT10_ALT:
case R_XTENSA_SLOT11_ALT:
case R_XTENSA_SLOT12_ALT:
case R_XTENSA_SLOT13_ALT:
case R_XTENSA_SLOT14_ALT:
printk("%s: unexpected ALT relocation: %u\n",
mod->name,
ELF32_R_TYPE(rela[i].r_info));
return -ENOEXEC;
default:
printk("%s: unexpected relocation: %u\n",
mod->name,
ELF32_R_TYPE(rela[i].r_info));
return -ENOEXEC;
}
}
return 0;
}

View file

@ -0,0 +1,85 @@
/*
* Xtensa Secondary Processors 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 - 2013 Tensilica Inc.
*
* Joe Taylor <joe@tensilica.com>
* Chris Zankel <chris@zankel.net>
* Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
* Pete Delaney <piet@tensilica.com>
*/
#include <linux/linkage.h>
#include <asm/cacheasm.h>
#include <asm/initialize_mmu.h>
#include <asm/mxregs.h>
#include <asm/regs.h>
.section .SecondaryResetVector.text, "ax"
ENTRY(_SecondaryResetVector)
_j _SetupOCD
.begin no-absolute-literals
.literal_position
_SetupOCD:
/*
* Initialize WB, WS, and clear PS.EXCM (to allow loop instructions).
* Set Interrupt Level just below XCHAL_DEBUGLEVEL to allow
* xt-gdb to single step via DEBUG exceptions received directly
* by ocd.
*/
movi a1, 1
movi a0, 0
wsr a1, windowstart
wsr a0, windowbase
rsync
movi a1, LOCKLEVEL
wsr a1, ps
rsync
_SetupMMU:
Offset = _SetupMMU - _SecondaryResetVector
#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
initialize_mmu
#endif
/*
* Start Secondary Processors with NULL pointer to boot params.
*/
movi a2, 0 # a2 == NULL
movi a3, _startup
jx a3
.end no-absolute-literals
.section .SecondaryResetVector.remapped_text, "ax"
.global _RemappedSecondaryResetVector
.org 0 # Need to do org before literals
_RemappedSecondaryResetVector:
.begin no-absolute-literals
.literal_position
_j _RemappedSetupMMU
. = _RemappedSecondaryResetVector + Offset
_RemappedSetupMMU:
#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
initialize_mmu
#endif
.end no-absolute-literals

View file

@ -0,0 +1,98 @@
/*
* arch/xtensa/kernel/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 <linux/gfp.h>
#include <linux/module.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,gfp_t flag)
{
unsigned long ret;
unsigned long uncached = 0;
/* ignore region speicifiers */
flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
flag |= GFP_DMA;
ret = (unsigned long)__get_free_pages(flag, get_order(size));
if (ret == 0)
return NULL;
/* We currently don't support coherent memory outside KSEG */
BUG_ON(ret < XCHAL_KSEG_CACHED_VADDR ||
ret > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
if (ret != 0) {
memset((void*) ret, 0, size);
uncached = ret+XCHAL_KSEG_BYPASS_VADDR-XCHAL_KSEG_CACHED_VADDR;
*handle = virt_to_bus((void*)ret);
__flush_invalidate_dcache_range(ret, size);
}
return (void*)uncached;
}
EXPORT_SYMBOL(dma_alloc_coherent);
void dma_free_coherent(struct device *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
unsigned long addr = (unsigned long)vaddr +
XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
BUG_ON(addr < XCHAL_KSEG_CACHED_VADDR ||
addr > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
free_pages(addr, get_order(size));
}
EXPORT_SYMBOL(dma_free_coherent);
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;
}
}
EXPORT_SYMBOL(consistent_sync);

370
arch/xtensa/kernel/pci.c Normal file
View file

@ -0,0 +1,370 @@
/*
* arch/xtensa/kernel/pci.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/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
* 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;
/*
* 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..
*/
resource_size_t
pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
if (res->flags & IORESOURCE_IO) {
if (size > 0x100) {
pr_err("PCI: I/O Region %s/%d too large (%u bytes)\n",
pci_name(dev), dev->resource - res,
size);
}
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
}
return 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", pci_name(dev));
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",
pci_name(dev), 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 void __init pci_controller_apertures(struct pci_controller *pci_ctrl,
struct list_head *resources)
{
struct resource *res;
unsigned long io_offset;
int i;
io_offset = (unsigned long)pci_ctrl->io_space.base;
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;
pci_add_resource_offset(resources, res, 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;
}
pci_add_resource(resources, res);
}
}
static int __init pcibios_init(void)
{
struct pci_controller *pci_ctrl;
struct list_head resources;
struct pci_bus *bus;
int next_busno = 0;
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;
INIT_LIST_HEAD(&resources);
pci_controller_apertures(pci_ctrl, &resources);
bus = pci_scan_root_bus(NULL, pci_ctrl->first_busno,
pci_ctrl->ops, pci_ctrl, &resources);
pci_ctrl->bus = bus;
pci_ctrl->last_busno = bus->busn_res.end;
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 pcibios_fixup_bus(struct pci_bus *bus)
{
if (bus->parent) {
/* This is a subordinate bridge */
pci_read_bridge_bases(bus);
}
}
void pcibios_set_master(struct pci_dev *dev)
{
/* No special bus mastering setup handling */
}
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", pci_name(dev));
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",
pci_name(dev), 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 */
/*
* 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_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 = (prot & _PAGE_CA_MASK) | _PAGE_CA_WT;
#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_pgprot(dev, vma, mmap_state, write_combine);
ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,vma->vm_page_prot);
return ret;
}

View file

@ -0,0 +1,46 @@
/*
* 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/types.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <asm/platform.h>
#include <asm/timex.h>
#include <asm/param.h> /* HZ */
#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, 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(void, pcibios_init, (void), { });
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
_F(void, calibrate_ccount, (void),
{
pr_err("ERROR: Cannot calibrate cpu frequency! Assuming 10MHz.\n");
ccount_freq = 10 * 1000000UL;
});
#endif

View file

@ -0,0 +1,356 @@
/*
* 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/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.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 <linux/fs.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/platform.h>
#include <asm/mmu.h>
#include <asm/irq.h>
#include <linux/atomic.h>
#include <asm/asm-offsets.h>
#include <asm/regs.h>
extern void ret_from_fork(void);
extern void ret_from_kernel_thread(void);
struct task_struct *current_set[NR_CPUS] = {&init_task, };
void (*pm_power_off)(void) = NULL;
EXPORT_SYMBOL(pm_power_off);
#if XTENSA_HAVE_COPROCESSORS
void coprocessor_release_all(struct thread_info *ti)
{
unsigned long cpenable;
int i;
/* Make sure we don't switch tasks during this operation. */
preempt_disable();
/* Walk through all cp owners and release it for the requested one. */
cpenable = ti->cpenable;
for (i = 0; i < XCHAL_CP_MAX; i++) {
if (coprocessor_owner[i] == ti) {
coprocessor_owner[i] = 0;
cpenable &= ~(1 << i);
}
}
ti->cpenable = cpenable;
coprocessor_clear_cpenable();
preempt_enable();
}
void coprocessor_flush_all(struct thread_info *ti)
{
unsigned long cpenable;
int i;
preempt_disable();
cpenable = ti->cpenable;
for (i = 0; i < XCHAL_CP_MAX; i++) {
if ((cpenable & 1) != 0 && coprocessor_owner[i] == ti)
coprocessor_flush(ti, i);
cpenable >>= 1;
}
preempt_enable();
}
#endif
/*
* Powermanagement idle function, if any is provided by the platform.
*/
void arch_cpu_idle(void)
{
platform_idle();
}
/*
* This is called when the thread calls exit().
*/
void exit_thread(void)
{
#if XTENSA_HAVE_COPROCESSORS
coprocessor_release_all(current_thread_info());
#endif
}
/*
* Flush thread state. This is called when a thread does an execve()
* Note that we flush coprocessor registers for the case execve fails.
*/
void flush_thread(void)
{
#if XTENSA_HAVE_COPROCESSORS
struct thread_info *ti = current_thread_info();
coprocessor_flush_all(ti);
coprocessor_release_all(ti);
#endif
}
/*
* this gets called so that we can store coprocessor state into memory and
* copy the current task into the new thread.
*/
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
#if XTENSA_HAVE_COPROCESSORS
coprocessor_flush_all(task_thread_info(src));
#endif
*dst = *src;
return 0;
}
/*
* Copy thread.
*
* There are two modes in which this function is called:
* 1) Userspace thread creation,
* regs != NULL, usp_thread_fn is userspace stack pointer.
* It is expected to copy parent regs (in case CLONE_VM is not set
* in the clone_flags) and set up passed usp in the childregs.
* 2) Kernel thread creation,
* regs == NULL, usp_thread_fn is the function to run in the new thread
* and thread_fn_arg is its parameter.
* childregs are not used for the kernel threads.
*
* The stack layout for the new thread looks like this:
*
* +------------------------+
* | childregs |
* +------------------------+ <- thread.sp = sp in dummy-frame
* | dummy-frame | (saved in dummy-frame spill-area)
* +------------------------+
*
* We create a dummy frame to return to either ret_from_fork or
* ret_from_kernel_thread:
* a0 points to ret_from_fork/ret_from_kernel_thread (simulating a call4)
* sp points to itself (thread.sp)
* a2, a3 are unused for userspace threads,
* a2 points to thread_fn, a3 holds thread_fn arg for kernel threads.
*
* Note: This is a pristine frame, so we don't need any spill region on top of
* childregs.
*
* The fun part: if we're keeping the same VM (i.e. cloning a thread,
* not an entire process), we're normally given a new usp, and we CANNOT share
* any live address register windows. If we just copy those live frames over,
* the two threads (parent and child) will overflow the same frames onto the
* parent stack at different times, likely corrupting the parent stack (esp.
* if the parent returns from functions that called clone() and calls new
* ones, before the child overflows its now old copies of its parent windows).
* One solution is to spill windows to the parent stack, but that's fairly
* involved. Much simpler to just not copy those live frames across.
*/
int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn,
unsigned long thread_fn_arg, struct task_struct *p)
{
struct pt_regs *childregs = task_pt_regs(p);
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
struct thread_info *ti;
#endif
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
*((int*)childregs - 3) = (unsigned long)childregs;
*((int*)childregs - 4) = 0;
p->thread.sp = (unsigned long)childregs;
if (!(p->flags & PF_KTHREAD)) {
struct pt_regs *regs = current_pt_regs();
unsigned long usp = usp_thread_fn ?
usp_thread_fn : regs->areg[1];
p->thread.ra = MAKE_RA_FOR_CALL(
(unsigned long)ret_from_fork, 0x1);
/* This does not copy all the regs.
* In a bout of brilliance or madness,
* ARs beyond a0-a15 exist past the end of the struct.
*/
*childregs = *regs;
childregs->areg[1] = usp;
childregs->areg[2] = 0;
/* When sharing memory with the parent thread, the child
usually starts on a pristine stack, so we have to reset
windowbase, windowstart and wmask.
(Note that such a new thread is required to always create
an initial call4 frame)
The exception is vfork, where the new thread continues to
run on the parent's stack until it calls execve. This could
be a call8 or call12, which requires a legal stack frame
of the previous caller for the overflow handlers to work.
(Note that it's always legal to overflow live registers).
In this case, ensure to spill at least the stack pointer
of that frame. */
if (clone_flags & CLONE_VM) {
/* check that caller window is live and same stack */
int len = childregs->wmask & ~0xf;
if (regs->areg[1] == usp && len != 0) {
int callinc = (regs->areg[0] >> 30) & 3;
int caller_ars = XCHAL_NUM_AREGS - callinc * 4;
put_user(regs->areg[caller_ars+1],
(unsigned __user*)(usp - 12));
}
childregs->wmask = 1;
childregs->windowstart = 1;
childregs->windowbase = 0;
} else {
int len = childregs->wmask & ~0xf;
memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
&regs->areg[XCHAL_NUM_AREGS - len/4], len);
}
/* The thread pointer is passed in the '4th argument' (= a5) */
if (clone_flags & CLONE_SETTLS)
childregs->threadptr = childregs->areg[5];
} else {
p->thread.ra = MAKE_RA_FOR_CALL(
(unsigned long)ret_from_kernel_thread, 1);
/* pass parameters to ret_from_kernel_thread:
* a2 = thread_fn, a3 = thread_fn arg
*/
*((int *)childregs - 1) = thread_fn_arg;
*((int *)childregs - 2) = usp_thread_fn;
/* Childregs are only used when we're going to userspace
* in which case start_thread will set them up.
*/
}
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
ti = task_thread_info(p);
ti->cpenable = 0;
#endif
return 0;
}
/*
* These bracket the sleeping functions..
*/
unsigned long get_wchan(struct task_struct *p)
{
unsigned long sp, pc;
unsigned long stack_page = (unsigned long) task_stack_page(p);
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;
}
/*
* 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 xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
{
unsigned long wb, ws, wm;
int live, last;
wb = regs->windowbase;
ws = regs->windowstart;
wm = regs->wmask;
ws = ((ws >> wb) | (ws << (WSBITS - wb))) & ((1 << WSBITS) - 1);
/* Don't leak any random bits. */
memset(elfregs, 0, sizeof(*elfregs));
/* 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 & ~(1 << PS_EXCM_BIT));
elfregs->lbeg = regs->lbeg;
elfregs->lend = regs->lend;
elfregs->lcount = regs->lcount;
elfregs->sar = regs->sar;
elfregs->windowstart = ws;
live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
last = XCHAL_NUM_AREGS - (wm >> 4) * 4;
memcpy(elfregs->a, regs->areg, live * 4);
memcpy(elfregs->a + last, regs->areg + last, (wm >> 4) * 16);
}
int dump_fpu(void)
{
return 0;
}

354
arch/xtensa/kernel/ptrace.c Normal file
View file

@ -0,0 +1,354 @@
// 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 - 2007 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/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/smp.h>
#include <linux/security.h>
#include <linux/signal.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include <asm/ptrace.h>
#include <asm/elf.h>
#include <asm/coprocessor.h>
void user_enable_single_step(struct task_struct *child)
{
child->ptrace |= PT_SINGLESTEP;
}
void user_disable_single_step(struct task_struct *child)
{
child->ptrace &= ~PT_SINGLESTEP;
}
/*
* Called by kernel/ptrace.c when detaching to disable single stepping.
*/
void ptrace_disable(struct task_struct *child)
{
/* Nothing to do.. */
}
int ptrace_getregs(struct task_struct *child, void __user *uregs)
{
struct pt_regs *regs = task_pt_regs(child);
xtensa_gregset_t __user *gregset = uregs;
unsigned long wb = regs->windowbase;
int i;
if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
return -EIO;
__put_user(regs->pc, &gregset->pc);
__put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps);
__put_user(regs->lbeg, &gregset->lbeg);
__put_user(regs->lend, &gregset->lend);
__put_user(regs->lcount, &gregset->lcount);
__put_user(regs->windowstart, &gregset->windowstart);
__put_user(regs->windowbase, &gregset->windowbase);
__put_user(regs->threadptr, &gregset->threadptr);
for (i = 0; i < XCHAL_NUM_AREGS; i++)
__put_user(regs->areg[i],
gregset->a + ((wb * 4 + i) % XCHAL_NUM_AREGS));
return 0;
}
int ptrace_setregs(struct task_struct *child, void __user *uregs)
{
struct pt_regs *regs = task_pt_regs(child);
xtensa_gregset_t *gregset = uregs;
const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
unsigned long ps;
unsigned long wb, ws;
if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
return -EIO;
__get_user(regs->pc, &gregset->pc);
__get_user(ps, &gregset->ps);
__get_user(regs->lbeg, &gregset->lbeg);
__get_user(regs->lend, &gregset->lend);
__get_user(regs->lcount, &gregset->lcount);
__get_user(ws, &gregset->windowstart);
__get_user(wb, &gregset->windowbase);
__get_user(regs->threadptr, &gregset->threadptr);
regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
if (wb >= XCHAL_NUM_AREGS / 4)
return -EFAULT;
if (wb != regs->windowbase || ws != regs->windowstart) {
unsigned long rotws, wmask;
rotws = (((ws | (ws << WSBITS)) >> wb) &
((1 << WSBITS) - 1)) & ~1;
wmask = ((rotws ? WSBITS + 1 - ffs(rotws) : 0) << 4) |
(rotws & 0xF) | 1;
regs->windowbase = wb;
regs->windowstart = ws;
regs->wmask = wmask;
}
if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4,
gregset->a, wb * 16))
return -EFAULT;
if (__copy_from_user(regs->areg, gregset->a + wb * 4,
(WSBITS - wb) * 16))
return -EFAULT;
return 0;
}
int ptrace_getxregs(struct task_struct *child, void __user *uregs)
{
struct pt_regs *regs = task_pt_regs(child);
struct thread_info *ti = task_thread_info(child);
elf_xtregs_t __user *xtregs = uregs;
int ret = 0;
if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
return -EIO;
#if XTENSA_HAVE_COPROCESSORS
/* Flush all coprocessor registers to memory. */
coprocessor_flush_all(ti);
ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
sizeof(xtregs_coprocessor_t));
#endif
ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
sizeof(xtregs->opt));
ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user,
sizeof(xtregs->user));
return ret ? -EFAULT : 0;
}
int ptrace_setxregs(struct task_struct *child, void __user *uregs)
{
struct thread_info *ti = task_thread_info(child);
struct pt_regs *regs = task_pt_regs(child);
elf_xtregs_t *xtregs = uregs;
int ret = 0;
if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
return -EFAULT;
#if XTENSA_HAVE_COPROCESSORS
/* Flush all coprocessors before we overwrite them. */
coprocessor_flush_all(ti);
coprocessor_release_all(ti);
ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
sizeof(xtregs_coprocessor_t));
#endif
ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
sizeof(xtregs->opt));
ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user,
sizeof(xtregs->user));
return ret ? -EFAULT : 0;
}
int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret)
{
struct pt_regs *regs;
unsigned long tmp;
regs = task_pt_regs(child);
tmp = 0; /* Default return value. */
switch(regno) {
case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
tmp = regs->areg[regno - REG_AR_BASE];
break;
case REG_A_BASE ... REG_A_BASE + 15:
tmp = regs->areg[regno - 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 & ~(1 << PS_EXCM_BIT));
break;
case REG_WB:
break; /* tmp = 0 */
case REG_WS:
{
unsigned long wb = regs->windowbase;
unsigned long ws = regs->windowstart;
tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1);
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 SYSCALL_NR:
tmp = regs->syscall;
break;
default:
return -EIO;
}
return put_user(tmp, ret);
}
int ptrace_pokeusr(struct task_struct *child, long regno, long val)
{
struct pt_regs *regs;
regs = task_pt_regs(child);
switch (regno) {
case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
regs->areg[regno - REG_AR_BASE] = val;
break;
case REG_A_BASE ... REG_A_BASE + 15:
regs->areg[regno - REG_A_BASE] = val;
break;
case REG_PC:
regs->pc = val;
break;
case SYSCALL_NR:
regs->syscall = val;
break;
default:
return -EIO;
}
return 0;
}
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret = -EPERM;
void __user *datap = (void __user *) data;
switch (request) {
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA:
ret = generic_ptrace_peekdata(child, addr, data);
break;
case PTRACE_PEEKUSR: /* read register specified by addr. */
ret = ptrace_peekusr(child, addr, datap);
break;
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
ret = generic_ptrace_pokedata(child, addr, data);
break;
case PTRACE_POKEUSR: /* write register specified by addr. */
ret = ptrace_pokeusr(child, addr, data);
break;
case PTRACE_GETREGS:
ret = ptrace_getregs(child, datap);
break;
case PTRACE_SETREGS:
ret = ptrace_setregs(child, datap);
break;
case PTRACE_GETXTREGS:
ret = ptrace_getxregs(child, datap);
break;
case PTRACE_SETXTREGS:
ret = ptrace_setxregs(child, datap);
break;
default:
ret = ptrace_request(child, request, addr, data);
break;
}
return ret;
}
void do_syscall_trace(void)
{
/*
* 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;
}
}
void do_syscall_trace_enter(struct pt_regs *regs)
{
if (test_thread_flag(TIF_SYSCALL_TRACE)
&& (current->ptrace & PT_PTRACED))
do_syscall_trace();
#if 0
audit_syscall_entry(...);
#endif
}
void do_syscall_trace_leave(struct pt_regs *regs)
{
if ((test_thread_flag(TIF_SYSCALL_TRACE))
&& (current->ptrace & PT_PTRACED))
do_syscall_trace();
}

731
arch/xtensa/kernel/setup.c Normal file
View file

@ -0,0 +1,731 @@
/*
* arch/xtensa/kernel/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/errno.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/screen_info.h>
#include <linux/bootmem.h>
#include <linux/kernel.h>
#include <linux/percpu.h>
#include <linux/clk-provider.h>
#include <linux/cpu.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.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/bootparam.h>
#include <asm/mmu_context.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 <asm/param.h>
#include <asm/traps.h>
#include <asm/smp.h>
#include <asm/sysmem.h>
#include <platform/hardware.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
extern struct rtc_ops no_rtc_ops;
struct rtc_ops *rtc_ops;
#ifdef CONFIG_BLK_DEV_INITRD
extern unsigned long initrd_start;
extern unsigned long initrd_end;
int initrd_is_mapped = 0;
extern int initrd_below_start_ok;
#endif
#ifdef CONFIG_OF
void *dtb_start = __dtb_start;
#endif
unsigned char aux_device_present;
extern unsigned long loops_per_jiffy;
/* Command line specified as configuration option. */
static char __initdata command_line[COMMAND_LINE_SIZE];
#ifdef CONFIG_CMDLINE_BOOL
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
#endif
/*
* 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__((used, section(".taglist"))) = { tag, fn }
/* parse current tag */
static int __init parse_tag_mem(const bp_tag_t *tag)
{
struct bp_meminfo *mi = (struct bp_meminfo *)(tag->data);
if (mi->type != MEMORY_TYPE_CONVENTIONAL)
return -1;
return add_sysmem_bank(mi->start, mi->end);
}
__tagtable(BP_TAG_MEMORY, parse_tag_mem);
#ifdef CONFIG_BLK_DEV_INITRD
static int __init parse_tag_initrd(const bp_tag_t* tag)
{
struct bp_meminfo *mi = (struct bp_meminfo *)(tag->data);
initrd_start = (unsigned long)__va(mi->start);
initrd_end = (unsigned long)__va(mi->end);
return 0;
}
__tagtable(BP_TAG_INITRD, parse_tag_initrd);
#ifdef CONFIG_OF
static int __init parse_tag_fdt(const bp_tag_t *tag)
{
dtb_start = __va(tag->data[0]);
return 0;
}
__tagtable(BP_TAG_FDT, parse_tag_fdt);
#endif /* CONFIG_OF */
#endif /* CONFIG_BLK_DEV_INITRD */
static int __init parse_tag_cmdline(const bp_tag_t* tag)
{
strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE);
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;
}
#ifdef CONFIG_OF
bool __initdata dt_memory_scan = false;
#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY
unsigned long xtensa_kio_paddr = XCHAL_KIO_DEFAULT_PADDR;
EXPORT_SYMBOL(xtensa_kio_paddr);
static int __init xtensa_dt_io_area(unsigned long node, const char *uname,
int depth, void *data)
{
const __be32 *ranges;
int len;
if (depth > 1)
return 0;
if (!of_flat_dt_is_compatible(node, "simple-bus"))
return 0;
ranges = of_get_flat_dt_prop(node, "ranges", &len);
if (!ranges)
return 1;
if (len == 0)
return 1;
xtensa_kio_paddr = of_read_ulong(ranges+1, 1);
/* round down to nearest 256MB boundary */
xtensa_kio_paddr &= 0xf0000000;
return 1;
}
#else
static int __init xtensa_dt_io_area(unsigned long node, const char *uname,
int depth, void *data)
{
return 1;
}
#endif
void __init early_init_dt_add_memory_arch(u64 base, u64 size)
{
if (!dt_memory_scan)
return;
size &= PAGE_MASK;
add_sysmem_bank(base, base + size);
}
void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
return __alloc_bootmem(size, align, 0);
}
void __init early_init_devtree(void *params)
{
if (sysmem.nr_banks == 0)
dt_memory_scan = true;
early_init_dt_scan(params);
of_scan_flat_dt(xtensa_dt_io_area, NULL);
if (!command_line[0])
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
}
static int __init xtensa_device_probe(void)
{
of_clk_init(NULL);
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
return 0;
}
device_initcall(xtensa_device_probe);
#endif /* CONFIG_OF */
/*
* Initialize architecture. (Early stage)
*/
void __init init_arch(bp_tag_t *bp_start)
{
/* Parse boot parameters */
if (bp_start)
parse_bootparam(bp_start);
#ifdef CONFIG_OF
early_init_devtree(dtb_start);
#endif
if (sysmem.nr_banks == 0) {
add_sysmem_bank(PLATFORM_DEFAULT_MEM_START,
PLATFORM_DEFAULT_MEM_START +
PLATFORM_DEFAULT_MEM_SIZE);
}
#ifdef CONFIG_CMDLINE_BOOL
if (!command_line[0])
strlcpy(command_line, default_command_line, COMMAND_LINE_SIZE);
#endif
/* 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;
#if XCHAL_EXCM_LEVEL >= 2
extern char _Level2InterruptVector_text_start;
extern char _Level2InterruptVector_text_end;
#endif
#if XCHAL_EXCM_LEVEL >= 3
extern char _Level3InterruptVector_text_start;
extern char _Level3InterruptVector_text_end;
#endif
#if XCHAL_EXCM_LEVEL >= 4
extern char _Level4InterruptVector_text_start;
extern char _Level4InterruptVector_text_end;
#endif
#if XCHAL_EXCM_LEVEL >= 5
extern char _Level5InterruptVector_text_start;
extern char _Level5InterruptVector_text_end;
#endif
#if XCHAL_EXCM_LEVEL >= 6
extern char _Level6InterruptVector_text_start;
extern char _Level6InterruptVector_text_end;
#endif
#ifdef CONFIG_S32C1I_SELFTEST
#if XCHAL_HAVE_S32C1I
static int __initdata rcw_word, rcw_probe_pc, rcw_exc;
/*
* Basic atomic compare-and-swap, that records PC of S32C1I for probing.
*
* If *v == cmp, set *v = set. Return previous *v.
*/
static inline int probed_compare_swap(int *v, int cmp, int set)
{
int tmp;
__asm__ __volatile__(
" movi %1, 1f\n"
" s32i %1, %4, 0\n"
" wsr %2, scompare1\n"
"1: s32c1i %0, %3, 0\n"
: "=a" (set), "=&a" (tmp)
: "a" (cmp), "a" (v), "a" (&rcw_probe_pc), "0" (set)
: "memory"
);
return set;
}
/* Handle probed exception */
static void __init do_probed_exception(struct pt_regs *regs,
unsigned long exccause)
{
if (regs->pc == rcw_probe_pc) { /* exception on s32c1i ? */
regs->pc += 3; /* skip the s32c1i instruction */
rcw_exc = exccause;
} else {
do_unhandled(regs, exccause);
}
}
/* Simple test of S32C1I (soc bringup assist) */
static int __init check_s32c1i(void)
{
int n, cause1, cause2;
void *handbus, *handdata, *handaddr; /* temporarily saved handlers */
rcw_probe_pc = 0;
handbus = trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR,
do_probed_exception);
handdata = trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR,
do_probed_exception);
handaddr = trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR,
do_probed_exception);
/* First try an S32C1I that does not store: */
rcw_exc = 0;
rcw_word = 1;
n = probed_compare_swap(&rcw_word, 0, 2);
cause1 = rcw_exc;
/* took exception? */
if (cause1 != 0) {
/* unclean exception? */
if (n != 2 || rcw_word != 1)
panic("S32C1I exception error");
} else if (rcw_word != 1 || n != 1) {
panic("S32C1I compare error");
}
/* Then an S32C1I that stores: */
rcw_exc = 0;
rcw_word = 0x1234567;
n = probed_compare_swap(&rcw_word, 0x1234567, 0xabcde);
cause2 = rcw_exc;
if (cause2 != 0) {
/* unclean exception? */
if (n != 0xabcde || rcw_word != 0x1234567)
panic("S32C1I exception error (b)");
} else if (rcw_word != 0xabcde || n != 0x1234567) {
panic("S32C1I store error");
}
/* Verify consistency of exceptions: */
if (cause1 || cause2) {
pr_warn("S32C1I took exception %d, %d\n", cause1, cause2);
/* If emulation of S32C1I upon bus error gets implemented,
we can get rid of this panic for single core (not SMP) */
panic("S32C1I exceptions not currently supported");
}
if (cause1 != cause2)
panic("inconsistent S32C1I exceptions");
trap_set_handler(EXCCAUSE_LOAD_STORE_ERROR, handbus);
trap_set_handler(EXCCAUSE_LOAD_STORE_DATA_ERROR, handdata);
trap_set_handler(EXCCAUSE_LOAD_STORE_ADDR_ERROR, handaddr);
return 0;
}
#else /* XCHAL_HAVE_S32C1I */
/* This condition should not occur with a commercially deployed processor.
Display reminder for early engr test or demo chips / FPGA bitstreams */
static int __init check_s32c1i(void)
{
pr_warn("Processor configuration lacks atomic compare-and-swap support!\n");
return 0;
}
#endif /* XCHAL_HAVE_S32C1I */
early_initcall(check_s32c1i);
#endif /* CONFIG_S32C1I_SELFTEST */
void __init setup_arch(char **cmdline_p)
{
strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
*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) == 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);
#if XCHAL_EXCM_LEVEL >= 2
mem_reserve(__pa(&_Level2InterruptVector_text_start),
__pa(&_Level2InterruptVector_text_end), 0);
#endif
#if XCHAL_EXCM_LEVEL >= 3
mem_reserve(__pa(&_Level3InterruptVector_text_start),
__pa(&_Level3InterruptVector_text_end), 0);
#endif
#if XCHAL_EXCM_LEVEL >= 4
mem_reserve(__pa(&_Level4InterruptVector_text_start),
__pa(&_Level4InterruptVector_text_end), 0);
#endif
#if XCHAL_EXCM_LEVEL >= 5
mem_reserve(__pa(&_Level5InterruptVector_text_start),
__pa(&_Level5InterruptVector_text_end), 0);
#endif
#if XCHAL_EXCM_LEVEL >= 6
mem_reserve(__pa(&_Level6InterruptVector_text_start),
__pa(&_Level6InterruptVector_text_end), 0);
#endif
parse_early_param();
bootmem_init();
unflatten_and_copy_device_tree();
platform_setup(cmdline_p);
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
paging_init();
zones_init();
#ifdef CONFIG_VT
# if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
# elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
# endif
#endif
#ifdef CONFIG_PCI
platform_pcibios_init();
#endif
}
static DEFINE_PER_CPU(struct cpu, cpu_data);
static int __init topology_init(void)
{
int i;
for_each_possible_cpu(i) {
struct cpu *cpu = &per_cpu(cpu_data, i);
cpu->hotpluggable = !!i;
register_cpu(cpu, i);
}
return 0;
}
subsys_initcall(topology_init);
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)
{
char buf[NR_CPUS * 5];
cpulist_scnprintf(buf, sizeof(buf), cpu_online_mask);
/* high-level stuff */
seq_printf(f, "CPU count\t: %u\n"
"CPU list\t: %s\n"
"vendor_id\t: Tensilica\n"
"model\t\t: Xtensa " XCHAL_HW_VERSION_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",
num_online_cpus(),
buf,
XCHAL_BUILD_UNIQUE_ID,
XCHAL_HAVE_BE ? "big" : "little",
ccount_freq/1000000,
(ccount_freq/10000) % 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
#if XCHAL_HAVE_S32C1I
"s32c1i "
#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);
/* 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);
return 0;
}
/*
* We show only CPU #0 info.
*/
static void *
c_start(struct seq_file *f, loff_t *pos)
{
return (*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)
{
}
const struct seq_operations cpuinfo_op =
{
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = c_show,
};
#endif /* CONFIG_PROC_FS */

511
arch/xtensa/kernel/signal.c Normal file
View file

@ -0,0 +1,511 @@
/*
* arch/xtensa/kernel/signal.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, 2006 Tensilica Inc.
* Copyright (C) 1991, 1992 Linus Torvalds
* 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
*
* Chris Zankel <chris@zankel.net>
* Joe Taylor <joe@tensilica.com>
*/
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/personality.h>
#include <linux/tracehook.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/coprocessor.h>
#include <asm/unistd.h>
#define DEBUG_SIG 0
extern struct task_struct *coproc_owners[];
struct rt_sigframe
{
struct siginfo info;
struct ucontext uc;
struct {
xtregs_opt_t opt;
xtregs_user_t user;
#if XTENSA_HAVE_COPROCESSORS
xtregs_coprocessor_t cp;
#endif
} xtregs;
unsigned char retcode[6];
unsigned int window[4];
};
/*
* Flush register windows stored in pt_regs to stack.
* Returns 1 for errors.
*/
int
flush_window_regs_user(struct pt_regs *regs)
{
const unsigned long ws = regs->windowstart;
const unsigned long wb = regs->windowbase;
unsigned long sp = 0;
unsigned long wm;
int err = 1;
int base;
/* Return if no other frames. */
if (regs->wmask == 1)
return 0;
/* Rotate windowmask and skip empty frames. */
wm = (ws >> wb) | (ws << (XCHAL_NUM_AREGS / 4 - wb));
base = (XCHAL_NUM_AREGS / 4) - (regs->wmask >> 4);
/* For call8 or call12 frames, we need the previous stack pointer. */
if ((regs->wmask & 2) == 0)
if (__get_user(sp, (int*)(regs->areg[base * 4 + 1] - 12)))
goto errout;
/* Spill frames to stack. */
while (base < XCHAL_NUM_AREGS / 4) {
int m = (wm >> base);
int inc = 0;
/* Save registers a4..a7 (call8) or a4...a11 (call12) */
if (m & 2) { /* call4 */
inc = 1;
} else if (m & 4) { /* call8 */
if (copy_to_user((void*)(sp - 32),
&regs->areg[(base + 1) * 4], 16))
goto errout;
inc = 2;
} else if (m & 8) { /* call12 */
if (copy_to_user((void*)(sp - 48),
&regs->areg[(base + 1) * 4], 32))
goto errout;
inc = 3;
}
/* Save current frame a0..a3 under next SP */
sp = regs->areg[((base + inc) * 4 + 1) % XCHAL_NUM_AREGS];
if (copy_to_user((void*)(sp - 16), &regs->areg[base * 4], 16))
goto errout;
/* Get current stack pointer for next loop iteration. */
sp = regs->areg[base * 4 + 1];
base += inc;
}
regs->wmask = 1;
regs->windowstart = 1 << wb;
return 0;
errout:
return err;
}
/*
* Note: We don't copy double exception 'regs', 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
setup_sigcontext(struct rt_sigframe __user *frame, struct pt_regs *regs)
{
struct sigcontext __user *sc = &frame->uc.uc_mcontext;
struct thread_info *ti = current_thread_info();
int err = 0;
#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
COPY(pc);
COPY(ps);
COPY(lbeg);
COPY(lend);
COPY(lcount);
COPY(sar);
#undef COPY
err |= flush_window_regs_user(regs);
err |= __copy_to_user (sc->sc_a, regs->areg, 16 * 4);
err |= __put_user(0, &sc->sc_xtregs);
if (err)
return err;
#if XTENSA_HAVE_COPROCESSORS
coprocessor_flush_all(ti);
coprocessor_release_all(ti);
err |= __copy_to_user(&frame->xtregs.cp, &ti->xtregs_cp,
sizeof (frame->xtregs.cp));
#endif
err |= __copy_to_user(&frame->xtregs.opt, &regs->xtregs_opt,
sizeof (xtregs_opt_t));
err |= __copy_to_user(&frame->xtregs.user, &ti->xtregs_user,
sizeof (xtregs_user_t));
err |= __put_user(err ? NULL : &frame->xtregs, &sc->sc_xtregs);
return err;
}
static int
restore_sigcontext(struct pt_regs *regs, struct rt_sigframe __user *frame)
{
struct sigcontext __user *sc = &frame->uc.uc_mcontext;
struct thread_info *ti = current_thread_info();
unsigned int err = 0;
unsigned long ps;
#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
COPY(pc);
COPY(lbeg);
COPY(lend);
COPY(lcount);
COPY(sar);
#undef COPY
/* All registers were flushed to stack. Start with a prestine frame. */
regs->wmask = 1;
regs->windowbase = 0;
regs->windowstart = 1;
regs->syscall = -1; /* disable syscall checks */
/* 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 & ~PS_CALLINC_MASK) | (ps & PS_CALLINC_MASK);
/* Additional corruption checks */
if ((regs->lcount > 0)
&& ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )
err = 1;
err |= __copy_from_user(regs->areg, sc->sc_a, 16 * 4);
if (err)
return err;
/* 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. */
#if XTENSA_HAVE_COPROCESSORS
coprocessor_release_all(ti);
err |= __copy_from_user(&ti->xtregs_cp, &frame->xtregs.cp,
sizeof (frame->xtregs.cp));
#endif
err |= __copy_from_user(&ti->xtregs_user, &frame->xtregs.user,
sizeof (xtregs_user_t));
err |= __copy_from_user(&regs->xtregs_opt, &frame->xtregs.opt,
sizeof (xtregs_opt_t));
return err;
}
/*
* Do a signal return; undo the signal stack.
*/
asmlinkage long xtensa_rt_sigreturn(long a0, long a1, long a2, long a3,
long a4, long a5, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
sigset_t set;
int ret;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
if (regs->depc > 64)
panic("rt_sigreturn in double exception!\n");
frame = (struct rt_sigframe __user *) regs->areg[1];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(regs, frame))
goto badframe;
ret = regs->areg[2];
if (restore_altstack(&frame->uc.uc_stack))
goto badframe;
return ret;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/*
* Set up a signal frame.
*/
static int
gen_return_code(unsigned char *codemem)
{
int err = 0;
/*
* 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.
*/
#if __NR_rt_sigreturn > 255
# error Generating the MOVI instruction below breaks!
#endif
#ifdef __XTENSA_EB__ /* Big Endian version */
/* Generate instruction: MOVI a2, __NR_rt_sigreturn */
err |= __put_user(0x22, &codemem[0]);
err |= __put_user(0x0a, &codemem[1]);
err |= __put_user(__NR_rt_sigreturn, &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, __NR_rt_sigreturn */
err |= __put_user(0x22, &codemem[0]);
err |= __put_user(0xa0, &codemem[1]);
err |= __put_user(__NR_rt_sigreturn, &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) {
__invalidate_icache_range((unsigned long)codemem, 6UL);
__flush_invalidate_dcache_range((unsigned long)codemem, 6UL);
}
return err;
}
static int setup_frame(struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs)
{
struct rt_sigframe *frame;
int err = 0, sig = ksig->sig;
int signal;
unsigned long sp, ra, tp;
sp = regs->areg[1];
if ((ksig->ka.sa.sa_flags & SA_ONSTACK) != 0 && sas_ss_flags(sp) == 0) {
sp = current->sas_ss_sp + current->sas_ss_size;
}
frame = (void *)((sp - sizeof(*frame)) & -16ul);
if (regs->depc > 64)
panic ("Double exception sys_sigreturn\n");
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) {
return -EFAULT;
}
signal = current_thread_info()->exec_domain
&& current_thread_info()->exec_domain->signal_invmap
&& sig < 32
? current_thread_info()->exec_domain->signal_invmap[sig]
: sig;
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
}
/* Create the user context. */
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(0, &frame->uc.uc_link);
err |= __save_altstack(&frame->uc.uc_stack, regs->areg[1]);
err |= setup_sigcontext(frame, regs);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
ra = (unsigned long)ksig->ka.sa.sa_restorer;
} else {
/* Create sys_rt_sigreturn syscall in stack frame */
err |= gen_return_code(frame->retcode);
if (err) {
return -EFAULT;
}
ra = (unsigned long) frame->retcode;
}
/*
* Create signal handler execution context.
* Return context not modified until this point.
*/
/* Set up registers for signal handler; preserve the threadptr */
tp = regs->threadptr;
start_thread(regs, (unsigned long) ksig->ka.sa.sa_handler,
(unsigned long) frame);
/* Set up a stack frame for a call4
* Note: PS.CALLINC is set to one by start_thread
*/
regs->areg[4] = (((unsigned long) ra) & 0x3fffffff) | 0x40000000;
regs->areg[6] = (unsigned long) signal;
regs->areg[7] = (unsigned long) &frame->info;
regs->areg[8] = (unsigned long) &frame->uc;
regs->threadptr = tp;
/* 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 0;
}
/*
* 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.
*/
static void do_signal(struct pt_regs *regs)
{
struct ksignal ksig;
task_pt_regs(current)->icountlevel = 0;
if (get_signal(&ksig)) {
int ret;
/* Are we from a system call? */
if ((signed)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 (!(ksig.ka.sa.sa_flags & SA_RESTART)) {
regs->areg[2] = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->areg[2] = regs->syscall;
regs->pc -= 3;
break;
default:
/* nothing to do */
if (regs->areg[2] != 0)
break;
}
}
/* Whee! Actually deliver the signal. */
/* Set up the stack frame */
ret = setup_frame(&ksig, sigmask_to_save(), regs);
signal_setup_done(ret, &ksig, 0);
if (current->ptrace & PT_SINGLESTEP)
task_pt_regs(current)->icountlevel = 1;
return;
}
/* Did we come from a system call? */
if ((signed) regs->syscall >= 0) {
/* Restart the system call - no handlers present */
switch (regs->areg[2]) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->areg[2] = regs->syscall;
regs->pc -= 3;
break;
case -ERESTART_RESTARTBLOCK:
regs->areg[2] = __NR_restart_syscall;
regs->pc -= 3;
break;
}
}
/* If there's no signal to deliver, we just restore the saved mask. */
restore_saved_sigmask();
if (current->ptrace & PT_SINGLESTEP)
task_pt_regs(current)->icountlevel = 1;
return;
}
void do_notify_resume(struct pt_regs *regs)
{
if (test_thread_flag(TIF_SIGPENDING))
do_signal(regs);
if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
tracehook_notify_resume(regs);
}

608
arch/xtensa/kernel/smp.c Normal file
View file

@ -0,0 +1,608 @@
/*
* Xtensa SMP support 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) 2008 - 2013 Tensilica Inc.
*
* Chris Zankel <chris@zankel.net>
* Joe Taylor <joe@tensilica.com>
* Pete Delaney <piet@tensilica.com
*/
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/seq_file.h>
#include <linux/smp.h>
#include <linux/thread_info.h>
#include <asm/cacheflush.h>
#include <asm/kdebug.h>
#include <asm/mmu_context.h>
#include <asm/mxregs.h>
#include <asm/platform.h>
#include <asm/tlbflush.h>
#include <asm/traps.h>
#ifdef CONFIG_SMP
# if XCHAL_HAVE_S32C1I == 0
# error "The S32C1I option is required for SMP."
# endif
#endif
static void system_invalidate_dcache_range(unsigned long start,
unsigned long size);
static void system_flush_invalidate_dcache_range(unsigned long start,
unsigned long size);
/* IPI (Inter Process Interrupt) */
#define IPI_IRQ 0
static irqreturn_t ipi_interrupt(int irq, void *dev_id);
static struct irqaction ipi_irqaction = {
.handler = ipi_interrupt,
.flags = IRQF_PERCPU,
.name = "ipi",
};
void ipi_init(void)
{
unsigned irq = irq_create_mapping(NULL, IPI_IRQ);
setup_irq(irq, &ipi_irqaction);
}
static inline unsigned int get_core_count(void)
{
/* Bits 18..21 of SYSCFGID contain the core count minus 1. */
unsigned int syscfgid = get_er(SYSCFGID);
return ((syscfgid >> 18) & 0xf) + 1;
}
static inline int get_core_id(void)
{
/* Bits 0...18 of SYSCFGID contain the core id */
unsigned int core_id = get_er(SYSCFGID);
return core_id & 0x3fff;
}
void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned i;
for (i = 0; i < max_cpus; ++i)
set_cpu_present(i, true);
}
void __init smp_init_cpus(void)
{
unsigned i;
unsigned int ncpus = get_core_count();
unsigned int core_id = get_core_id();
pr_info("%s: Core Count = %d\n", __func__, ncpus);
pr_info("%s: Core Id = %d\n", __func__, core_id);
for (i = 0; i < ncpus; ++i)
set_cpu_possible(i, true);
}
void __init smp_prepare_boot_cpu(void)
{
unsigned int cpu = smp_processor_id();
BUG_ON(cpu != 0);
cpu_asid_cache(cpu) = ASID_USER_FIRST;
}
void __init smp_cpus_done(unsigned int max_cpus)
{
}
static int boot_secondary_processors = 1; /* Set with xt-gdb via .xt-gdb */
static DECLARE_COMPLETION(cpu_running);
void secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
unsigned int cpu = smp_processor_id();
init_mmu();
#ifdef CONFIG_DEBUG_KERNEL
if (boot_secondary_processors == 0) {
pr_debug("%s: boot_secondary_processors:%d; Hanging cpu:%d\n",
__func__, boot_secondary_processors, cpu);
for (;;)
__asm__ __volatile__ ("waiti " __stringify(LOCKLEVEL));
}
pr_debug("%s: boot_secondary_processors:%d; Booting cpu:%d\n",
__func__, boot_secondary_processors, cpu);
#endif
/* Init EXCSAVE1 */
secondary_trap_init();
/* All kernel threads share the same mm context. */
atomic_inc(&mm->mm_users);
atomic_inc(&mm->mm_count);
current->active_mm = mm;
cpumask_set_cpu(cpu, mm_cpumask(mm));
enter_lazy_tlb(mm, current);
preempt_disable();
trace_hardirqs_off();
calibrate_delay();
notify_cpu_starting(cpu);
secondary_init_irq();
local_timer_setup(cpu);
set_cpu_online(cpu, true);
local_irq_enable();
complete(&cpu_running);
cpu_startup_entry(CPUHP_ONLINE);
}
static void mx_cpu_start(void *p)
{
unsigned cpu = (unsigned)p;
unsigned long run_stall_mask = get_er(MPSCORE);
set_er(run_stall_mask & ~(1u << cpu), MPSCORE);
pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n",
__func__, cpu, run_stall_mask, get_er(MPSCORE));
}
static void mx_cpu_stop(void *p)
{
unsigned cpu = (unsigned)p;
unsigned long run_stall_mask = get_er(MPSCORE);
set_er(run_stall_mask | (1u << cpu), MPSCORE);
pr_debug("%s: cpu: %d, run_stall_mask: %lx ---> %lx\n",
__func__, cpu, run_stall_mask, get_er(MPSCORE));
}
#ifdef CONFIG_HOTPLUG_CPU
unsigned long cpu_start_id __cacheline_aligned;
#endif
unsigned long cpu_start_ccount;
static int boot_secondary(unsigned int cpu, struct task_struct *ts)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
unsigned long ccount;
int i;
#ifdef CONFIG_HOTPLUG_CPU
cpu_start_id = cpu;
system_flush_invalidate_dcache_range(
(unsigned long)&cpu_start_id, sizeof(cpu_start_id));
#endif
smp_call_function_single(0, mx_cpu_start, (void *)cpu, 1);
for (i = 0; i < 2; ++i) {
do
ccount = get_ccount();
while (!ccount);
cpu_start_ccount = ccount;
while (time_before(jiffies, timeout)) {
mb();
if (!cpu_start_ccount)
break;
}
if (cpu_start_ccount) {
smp_call_function_single(0, mx_cpu_stop,
(void *)cpu, 1);
cpu_start_ccount = 0;
return -EIO;
}
}
return 0;
}
int __cpu_up(unsigned int cpu, struct task_struct *idle)
{
int ret = 0;
if (cpu_asid_cache(cpu) == 0)
cpu_asid_cache(cpu) = ASID_USER_FIRST;
start_info.stack = (unsigned long)task_pt_regs(idle);
wmb();
pr_debug("%s: Calling wakeup_secondary(cpu:%d, idle:%p, sp: %08lx)\n",
__func__, cpu, idle, start_info.stack);
ret = boot_secondary(cpu, idle);
if (ret == 0) {
wait_for_completion_timeout(&cpu_running,
msecs_to_jiffies(1000));
if (!cpu_online(cpu))
ret = -EIO;
}
if (ret)
pr_err("CPU %u failed to boot\n", cpu);
return ret;
}
#ifdef CONFIG_HOTPLUG_CPU
/*
* __cpu_disable runs on the processor to be shutdown.
*/
int __cpu_disable(void)
{
unsigned int cpu = smp_processor_id();
/*
* Take this CPU offline. Once we clear this, we can't return,
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online(cpu, false);
/*
* OK - migrate IRQs away from this CPU
*/
migrate_irqs();
/*
* Flush user cache and TLB mappings, and then remove this CPU
* from the vm mask set of all processes.
*/
local_flush_cache_all();
local_flush_tlb_all();
invalidate_page_directory();
clear_tasks_mm_cpumask(cpu);
return 0;
}
static void platform_cpu_kill(unsigned int cpu)
{
smp_call_function_single(0, mx_cpu_stop, (void *)cpu, true);
}
/*
* called on the thread which is asking for a CPU to be shutdown -
* waits until shutdown has completed, or it is timed out.
*/
void __cpu_die(unsigned int cpu)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
while (time_before(jiffies, timeout)) {
system_invalidate_dcache_range((unsigned long)&cpu_start_id,
sizeof(cpu_start_id));
if (cpu_start_id == -cpu) {
platform_cpu_kill(cpu);
return;
}
}
pr_err("CPU%u: unable to kill\n", cpu);
}
void arch_cpu_idle_dead(void)
{
cpu_die();
}
/*
* Called from the idle thread for the CPU which has been shutdown.
*
* Note that we disable IRQs here, but do not re-enable them
* before returning to the caller. This is also the behaviour
* of the other hotplug-cpu capable cores, so presumably coming
* out of idle fixes this.
*/
void __ref cpu_die(void)
{
idle_task_exit();
local_irq_disable();
__asm__ __volatile__(
" movi a2, cpu_restart\n"
" jx a2\n");
}
#endif /* CONFIG_HOTPLUG_CPU */
enum ipi_msg_type {
IPI_RESCHEDULE = 0,
IPI_CALL_FUNC,
IPI_CPU_STOP,
IPI_MAX
};
static const struct {
const char *short_text;
const char *long_text;
} ipi_text[] = {
{ .short_text = "RES", .long_text = "Rescheduling interrupts" },
{ .short_text = "CAL", .long_text = "Function call interrupts" },
{ .short_text = "DIE", .long_text = "CPU shutdown interrupts" },
};
struct ipi_data {
unsigned long ipi_count[IPI_MAX];
};
static DEFINE_PER_CPU(struct ipi_data, ipi_data);
static void send_ipi_message(const struct cpumask *callmask,
enum ipi_msg_type msg_id)
{
int index;
unsigned long mask = 0;
for_each_cpu(index, callmask)
if (index != smp_processor_id())
mask |= 1 << index;
set_er(mask, MIPISET(msg_id));
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
send_ipi_message(mask, IPI_CALL_FUNC);
}
void arch_send_call_function_single_ipi(int cpu)
{
send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC);
}
void smp_send_reschedule(int cpu)
{
send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
}
void smp_send_stop(void)
{
struct cpumask targets;
cpumask_copy(&targets, cpu_online_mask);
cpumask_clear_cpu(smp_processor_id(), &targets);
send_ipi_message(&targets, IPI_CPU_STOP);
}
static void ipi_cpu_stop(unsigned int cpu)
{
set_cpu_online(cpu, false);
machine_halt();
}
irqreturn_t ipi_interrupt(int irq, void *dev_id)
{
unsigned int cpu = smp_processor_id();
struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
unsigned int msg;
unsigned i;
msg = get_er(MIPICAUSE(cpu));
for (i = 0; i < IPI_MAX; i++)
if (msg & (1 << i)) {
set_er(1 << i, MIPICAUSE(cpu));
++ipi->ipi_count[i];
}
if (msg & (1 << IPI_RESCHEDULE))
scheduler_ipi();
if (msg & (1 << IPI_CALL_FUNC))
generic_smp_call_function_interrupt();
if (msg & (1 << IPI_CPU_STOP))
ipi_cpu_stop(cpu);
return IRQ_HANDLED;
}
void show_ipi_list(struct seq_file *p, int prec)
{
unsigned int cpu;
unsigned i;
for (i = 0; i < IPI_MAX; ++i) {
seq_printf(p, "%*s:", prec, ipi_text[i].short_text);
for_each_online_cpu(cpu)
seq_printf(p, " %10lu",
per_cpu(ipi_data, cpu).ipi_count[i]);
seq_printf(p, " %s\n", ipi_text[i].long_text);
}
}
int setup_profiling_timer(unsigned int multiplier)
{
pr_debug("setup_profiling_timer %d\n", multiplier);
return 0;
}
/* TLB flush functions */
struct flush_data {
struct vm_area_struct *vma;
unsigned long addr1;
unsigned long addr2;
};
static void ipi_flush_tlb_all(void *arg)
{
local_flush_tlb_all();
}
void flush_tlb_all(void)
{
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
}
static void ipi_flush_tlb_mm(void *arg)
{
local_flush_tlb_mm(arg);
}
void flush_tlb_mm(struct mm_struct *mm)
{
on_each_cpu(ipi_flush_tlb_mm, mm, 1);
}
static void ipi_flush_tlb_page(void *arg)
{
struct flush_data *fd = arg;
local_flush_tlb_page(fd->vma, fd->addr1);
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
struct flush_data fd = {
.vma = vma,
.addr1 = addr,
};
on_each_cpu(ipi_flush_tlb_page, &fd, 1);
}
static void ipi_flush_tlb_range(void *arg)
{
struct flush_data *fd = arg;
local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
}
void flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
struct flush_data fd = {
.vma = vma,
.addr1 = start,
.addr2 = end,
};
on_each_cpu(ipi_flush_tlb_range, &fd, 1);
}
static void ipi_flush_tlb_kernel_range(void *arg)
{
struct flush_data *fd = arg;
local_flush_tlb_kernel_range(fd->addr1, fd->addr2);
}
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
struct flush_data fd = {
.addr1 = start,
.addr2 = end,
};
on_each_cpu(ipi_flush_tlb_kernel_range, &fd, 1);
}
/* Cache flush functions */
static void ipi_flush_cache_all(void *arg)
{
local_flush_cache_all();
}
void flush_cache_all(void)
{
on_each_cpu(ipi_flush_cache_all, NULL, 1);
}
static void ipi_flush_cache_page(void *arg)
{
struct flush_data *fd = arg;
local_flush_cache_page(fd->vma, fd->addr1, fd->addr2);
}
void flush_cache_page(struct vm_area_struct *vma,
unsigned long address, unsigned long pfn)
{
struct flush_data fd = {
.vma = vma,
.addr1 = address,
.addr2 = pfn,
};
on_each_cpu(ipi_flush_cache_page, &fd, 1);
}
static void ipi_flush_cache_range(void *arg)
{
struct flush_data *fd = arg;
local_flush_cache_range(fd->vma, fd->addr1, fd->addr2);
}
void flush_cache_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
struct flush_data fd = {
.vma = vma,
.addr1 = start,
.addr2 = end,
};
on_each_cpu(ipi_flush_cache_range, &fd, 1);
}
static void ipi_flush_icache_range(void *arg)
{
struct flush_data *fd = arg;
local_flush_icache_range(fd->addr1, fd->addr2);
}
void flush_icache_range(unsigned long start, unsigned long end)
{
struct flush_data fd = {
.addr1 = start,
.addr2 = end,
};
on_each_cpu(ipi_flush_icache_range, &fd, 1);
}
EXPORT_SYMBOL(flush_icache_range);
/* ------------------------------------------------------------------------- */
static void ipi_invalidate_dcache_range(void *arg)
{
struct flush_data *fd = arg;
__invalidate_dcache_range(fd->addr1, fd->addr2);
}
static void system_invalidate_dcache_range(unsigned long start,
unsigned long size)
{
struct flush_data fd = {
.addr1 = start,
.addr2 = size,
};
on_each_cpu(ipi_invalidate_dcache_range, &fd, 1);
}
static void ipi_flush_invalidate_dcache_range(void *arg)
{
struct flush_data *fd = arg;
__flush_invalidate_dcache_range(fd->addr1, fd->addr2);
}
static void system_flush_invalidate_dcache_range(unsigned long start,
unsigned long size)
{
struct flush_data fd = {
.addr1 = start,
.addr2 = size,
};
on_each_cpu(ipi_flush_invalidate_dcache_range, &fd, 1);
}

View file

@ -0,0 +1,120 @@
/*
* arch/xtensa/kernel/stacktrace.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 - 2013 Tensilica Inc.
*/
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/stacktrace.h>
#include <asm/stacktrace.h>
#include <asm/traps.h>
void walk_stackframe(unsigned long *sp,
int (*fn)(struct stackframe *frame, void *data),
void *data)
{
unsigned long a0, a1;
unsigned long sp_end;
a1 = (unsigned long)sp;
sp_end = ALIGN(a1, THREAD_SIZE);
spill_registers();
while (a1 < sp_end) {
struct stackframe frame;
sp = (unsigned long *)a1;
a0 = *(sp - 4);
a1 = *(sp - 3);
if (a1 <= (unsigned long)sp)
break;
frame.pc = MAKE_PC_FROM_RA(a0, a1);
frame.sp = a1;
if (fn(&frame, data))
return;
}
}
#ifdef CONFIG_STACKTRACE
struct stack_trace_data {
struct stack_trace *trace;
unsigned skip;
};
static int stack_trace_cb(struct stackframe *frame, void *data)
{
struct stack_trace_data *trace_data = data;
struct stack_trace *trace = trace_data->trace;
if (trace_data->skip) {
--trace_data->skip;
return 0;
}
if (!kernel_text_address(frame->pc))
return 0;
trace->entries[trace->nr_entries++] = frame->pc;
return trace->nr_entries >= trace->max_entries;
}
void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
{
struct stack_trace_data trace_data = {
.trace = trace,
.skip = trace->skip,
};
walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
void save_stack_trace(struct stack_trace *trace)
{
save_stack_trace_tsk(current, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
#endif
#ifdef CONFIG_FRAME_POINTER
struct return_addr_data {
unsigned long addr;
unsigned skip;
};
static int return_address_cb(struct stackframe *frame, void *data)
{
struct return_addr_data *r = data;
if (r->skip) {
--r->skip;
return 0;
}
if (!kernel_text_address(frame->pc))
return 0;
r->addr = frame->pc;
return 1;
}
unsigned long return_address(unsigned level)
{
struct return_addr_data r = {
.skip = level + 1,
};
walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
return r.addr;
}
EXPORT_SYMBOL(return_address);
#endif

View file

@ -0,0 +1,95 @@
/*
* 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
*
*/
#include <asm/uaccess.h>
#include <asm/syscall.h>
#include <asm/unistd.h>
#include <linux/linkage.h>
#include <linux/stringify.h>
#include <linux/errno.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/mman.h>
#include <linux/shm.h>
typedef void (*syscall_t)(void);
syscall_t sys_call_table[__NR_syscall_count] /* FIXME __cacheline_aligned */= {
[0 ... __NR_syscall_count - 1] = (syscall_t)&sys_ni_syscall,
#define __SYSCALL(nr,symbol,nargs) [ nr ] = (syscall_t)symbol,
#include <uapi/asm/unistd.h>
};
#define COLOUR_ALIGN(addr, pgoff) \
((((addr) + SHMLBA - 1) & ~(SHMLBA - 1)) + \
(((pgoff) << PAGE_SHIFT) & (SHMLBA - 1)))
asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg)
{
unsigned long ret;
long err;
err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA);
if (err)
return err;
return (long)ret;
}
asmlinkage long xtensa_fadvise64_64(int fd, int advice,
unsigned long long offset, unsigned long long len)
{
return sys_fadvise64_64(fd, offset, len, advice);
}
unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff, unsigned long flags)
{
struct vm_area_struct *vmm;
if (flags & MAP_FIXED) {
/* We do not accept a shared mapping if it would violate
* cache aliasing constraints.
*/
if ((flags & MAP_SHARED) &&
((addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1)))
return -EINVAL;
return addr;
}
if (len > TASK_SIZE)
return -ENOMEM;
if (!addr)
addr = TASK_UNMAPPED_BASE;
if (flags & MAP_SHARED)
addr = COLOUR_ALIGN(addr, pgoff);
else
addr = PAGE_ALIGN(addr);
for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
/* At this point: (!vmm || addr < vmm->vm_end). */
if (TASK_SIZE - len < addr)
return -ENOMEM;
if (!vmm || addr + len <= vmm->vm_start)
return addr;
addr = vmm->vm_end;
if (flags & MAP_SHARED)
addr = COLOUR_ALIGN(addr, pgoff);
}
}

181
arch/xtensa/kernel/time.c Normal file
View file

@ -0,0 +1,181 @@
/*
* 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/errno.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/clocksource.h>
#include <linux/clockchips.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 <linux/irqdomain.h>
#include <linux/sched_clock.h>
#include <asm/timex.h>
#include <asm/platform.h>
unsigned long ccount_freq; /* ccount Hz */
EXPORT_SYMBOL(ccount_freq);
static cycle_t ccount_read(struct clocksource *cs)
{
return (cycle_t)get_ccount();
}
static u64 notrace ccount_sched_clock_read(void)
{
return get_ccount();
}
static struct clocksource ccount_clocksource = {
.name = "ccount",
.rating = 200,
.read = ccount_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int ccount_timer_set_next_event(unsigned long delta,
struct clock_event_device *dev);
static void ccount_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt);
struct ccount_timer {
struct clock_event_device evt;
int irq_enabled;
char name[24];
};
static DEFINE_PER_CPU(struct ccount_timer, ccount_timer);
static int ccount_timer_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
unsigned long flags, next;
int ret = 0;
local_irq_save(flags);
next = get_ccount() + delta;
set_linux_timer(next);
if (next - get_ccount() > delta)
ret = -ETIME;
local_irq_restore(flags);
return ret;
}
static void ccount_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
struct ccount_timer *timer =
container_of(evt, struct ccount_timer, evt);
/*
* There is no way to disable the timer interrupt at the device level,
* only at the intenable register itself. Since enable_irq/disable_irq
* calls are nested, we need to make sure that these calls are
* balanced.
*/
switch (mode) {
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
if (timer->irq_enabled) {
disable_irq(evt->irq);
timer->irq_enabled = 0;
}
break;
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_ONESHOT:
if (!timer->irq_enabled) {
enable_irq(evt->irq);
timer->irq_enabled = 1;
}
default:
break;
}
}
static irqreturn_t timer_interrupt(int irq, void *dev_id);
static struct irqaction timer_irqaction = {
.handler = timer_interrupt,
.flags = IRQF_TIMER,
.name = "timer",
};
void local_timer_setup(unsigned cpu)
{
struct ccount_timer *timer = &per_cpu(ccount_timer, cpu);
struct clock_event_device *clockevent = &timer->evt;
timer->irq_enabled = 1;
clockevent->name = timer->name;
snprintf(timer->name, sizeof(timer->name), "ccount_clockevent_%u", cpu);
clockevent->features = CLOCK_EVT_FEAT_ONESHOT;
clockevent->rating = 300;
clockevent->set_next_event = ccount_timer_set_next_event;
clockevent->set_mode = ccount_timer_set_mode;
clockevent->cpumask = cpumask_of(cpu);
clockevent->irq = irq_create_mapping(NULL, LINUX_TIMER_INT);
if (WARN(!clockevent->irq, "error: can't map timer irq"))
return;
clockevents_config_and_register(clockevent, ccount_freq,
0xf, 0xffffffff);
}
void __init time_init(void)
{
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
printk("Calibrating CPU frequency ");
platform_calibrate_ccount();
printk("%d.%02d MHz\n", (int)ccount_freq/1000000,
(int)(ccount_freq/10000)%100);
#else
ccount_freq = CONFIG_XTENSA_CPU_CLOCK*1000000UL;
#endif
clocksource_register_hz(&ccount_clocksource, ccount_freq);
local_timer_setup(0);
setup_irq(this_cpu_ptr(&ccount_timer)->evt.irq, &timer_irqaction);
sched_clock_register(ccount_sched_clock_read, 32, ccount_freq);
clocksource_of_init();
}
/*
* The timer interrupt is called HZ times per second.
*/
irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &this_cpu_ptr(&ccount_timer)->evt;
set_linux_timer(get_linux_timer());
evt->event_handler(evt);
/* Allow platform to do something useful (Wdog). */
platform_heartbeat();
return IRQ_HANDLED;
}
#ifndef CONFIG_GENERIC_CALIBRATE_DELAY
void calibrate_delay(void)
{
loops_per_jiffy = ccount_freq / HZ;
printk("Calibrating delay loop (skipped)... "
"%lu.%02lu BogoMIPS preset\n",
loops_per_jiffy/(1000000/HZ),
(loops_per_jiffy/(10000/HZ)) % 100);
}
#endif

517
arch/xtensa/kernel/traps.c Normal file
View file

@ -0,0 +1,517 @@
/*
* 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 - 2013 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 <linux/delay.h>
#include <linux/hardirq.h>
#include <asm/stacktrace.h>
#include <asm/ptrace.h>
#include <asm/timex.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/traps.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) \
{ EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor }
typedef struct {
int cause;
int fast;
void* handler;
} dispatch_init_table_t;
static dispatch_init_table_t __initdata dispatch_init_table[] = {
{ EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction},
{ EXCCAUSE_SYSTEM_CALL, KRNL, fast_syscall_kernel },
{ EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user },
{ EXCCAUSE_SYSTEM_CALL, 0, system_call },
/* EXCCAUSE_INSTRUCTION_FETCH unhandled */
/* EXCCAUSE_LOAD_STORE_ERROR unhandled*/
{ EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt },
{ EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca },
/* EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */
/* EXCCAUSE_PRIVILEGED unhandled */
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
#ifdef CONFIG_XTENSA_UNALIGNED_USER
{ EXCCAUSE_UNALIGNED, USER, fast_unaligned },
#endif
{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
{ EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
#endif
#ifdef CONFIG_MMU
{ EXCCAUSE_ITLB_MISS, 0, do_page_fault },
{ EXCCAUSE_ITLB_MISS, USER|KRNL, fast_second_level_miss},
{ EXCCAUSE_ITLB_MULTIHIT, 0, do_multihit },
{ EXCCAUSE_ITLB_PRIVILEGE, 0, do_page_fault },
/* EXCCAUSE_SIZE_RESTRICTION unhandled */
{ EXCCAUSE_FETCH_CACHE_ATTRIBUTE, 0, do_page_fault },
{ EXCCAUSE_DTLB_MISS, USER|KRNL, fast_second_level_miss},
{ EXCCAUSE_DTLB_MISS, 0, do_page_fault },
{ EXCCAUSE_DTLB_MULTIHIT, 0, do_multihit },
{ EXCCAUSE_DTLB_PRIVILEGE, 0, do_page_fault },
/* EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */
{ EXCCAUSE_STORE_CACHE_ATTRIBUTE, USER|KRNL, fast_store_prohibited },
{ EXCCAUSE_STORE_CACHE_ATTRIBUTE, 0, do_page_fault },
{ EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0, do_page_fault },
#endif /* CONFIG_MMU */
/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */
#if XTENSA_HAVE_COPROCESSOR(0)
COPROCESSOR(0),
#endif
#if XTENSA_HAVE_COPROCESSOR(1)
COPROCESSOR(1),
#endif
#if XTENSA_HAVE_COPROCESSOR(2)
COPROCESSOR(2),
#endif
#if XTENSA_HAVE_COPROCESSOR(3)
COPROCESSOR(3),
#endif
#if XTENSA_HAVE_COPROCESSOR(4)
COPROCESSOR(4),
#endif
#if XTENSA_HAVE_COPROCESSOR(5)
COPROCESSOR(5),
#endif
#if XTENSA_HAVE_COPROCESSOR(6)
COPROCESSOR(6),
#endif
#if XTENSA_HAVE_COPROCESSOR(7)
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.
*/
DEFINE_PER_CPU(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, task_pid_nr(current), 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);
}
/*
* IRQ handler.
*/
extern void do_IRQ(int, struct pt_regs *);
void do_interrupt(struct pt_regs *regs)
{
static const unsigned int_level_mask[] = {
0,
XCHAL_INTLEVEL1_MASK,
XCHAL_INTLEVEL2_MASK,
XCHAL_INTLEVEL3_MASK,
XCHAL_INTLEVEL4_MASK,
XCHAL_INTLEVEL5_MASK,
XCHAL_INTLEVEL6_MASK,
XCHAL_INTLEVEL7_MASK,
};
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
for (;;) {
unsigned intread = get_sr(interrupt);
unsigned intenable = get_sr(intenable);
unsigned int_at_level = intread & intenable;
unsigned level;
for (level = LOCKLEVEL; level > 0; --level) {
if (int_at_level & int_level_mask[level]) {
int_at_level &= int_level_mask[level];
break;
}
}
if (level == 0)
break;
do_IRQ(__ffs(int_at_level), regs);
}
irq_exit();
set_irq_regs(old_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, task_pid_nr(current), 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
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, task_pid_nr(current), 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
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);
}
static void set_handler(int idx, void *handler)
{
unsigned int cpu;
for_each_possible_cpu(cpu)
per_cpu(exc_table, cpu)[idx] = (unsigned long)handler;
}
/* Set exception C handler - for temporary use when probing exceptions */
void * __init trap_set_handler(int cause, void *handler)
{
void *previous = (void *)per_cpu(exc_table, 0)[
EXC_TABLE_DEFAULT / 4 + cause];
set_handler(EXC_TABLE_DEFAULT / 4 + cause, handler);
return previous;
}
static void trap_init_excsave(void)
{
unsigned long excsave1 = (unsigned long)this_cpu_ptr(exc_table);
__asm__ __volatile__("wsr %0, excsave1\n" : : "a" (excsave1));
}
/*
* 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.
*/
void __init 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. */
trap_init_excsave();
}
#ifdef CONFIG_SMP
void secondary_trap_init(void)
{
trap_init_excsave();
}
#endif
/*
* This function dumps the current valid window frame and other base registers.
*/
void show_regs(struct pt_regs * regs)
{
int i, wmask;
show_regs_print_info(KERN_DEFAULT);
wmask = regs->wmask & ~1;
for (i = 0; i < 16; i++) {
if ((i % 8) == 0)
printk(KERN_INFO "a%02d:", i);
printk(KERN_CONT " %08lx", regs->areg[i]);
}
printk(KERN_CONT "\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);
}
static int show_trace_cb(struct stackframe *frame, void *data)
{
if (kernel_text_address(frame->pc)) {
printk(" [<%08lx>] ", frame->pc);
print_symbol("%s\n", frame->pc);
}
return 0;
}
void show_trace(struct task_struct *task, unsigned long *sp)
{
if (!sp)
sp = stack_pointer(task);
printk("Call Trace:");
#ifdef CONFIG_KALLSYMS
printk("\n");
#endif
walk_stackframe(sp, show_trace_cb, NULL);
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)
sp = stack_pointer(task);
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 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?' ':'>'));
}
}
DEFINE_SPINLOCK(die_lock);
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]);
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irq(&die_lock);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
do_exit(err);
}

View file

@ -0,0 +1,782 @@
/*
* 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 - 2008 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/current.h>
#include <asm/asm-offsets.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/thread_info.h>
#include <asm/vectors.h>
#define WINDOW_VECTORS_SIZE 0x180
/*
* 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, excsave1 # 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
xsr a3, excsave1 # restore a3 and dispatch table
jx a0
ENDPROC(_UserExceptionVector)
/*
* 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, excsave1 # 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
xsr a3, excsave1 # restore a3 and dispatch table
jx a0
ENDPROC(_KernelExceptionVector)
/*
* 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_SAVE
* 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
.globl _DoubleExceptionVector_WindowUnderflow
.globl _DoubleExceptionVector_WindowOverflow
ENTRY(_DoubleExceptionVector)
xsr a3, excsave1
s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
/* Check for kernel double exception (usually fatal). */
rsr a2, ps
_bbci.l a2, PS_UM_BIT, .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 a2, WINDOW_VECTORS_VADDR
_bltu a0, a2, .Lfixup
addi a2, a2, WINDOW_VECTORS_SIZE
_bgeu a0, a2, .Lfixup
/* Window overflow/underflow exception. Get stack pointer. */
l32i a2, a3, EXC_TABLE_KSTK
/* Check for overflow/underflow exception, jump if overflow. */
bbci.l a0, 6, _DoubleExceptionVector_WindowOverflow
/*
* Restart window underflow exception.
* Currently:
* depc = orig a0,
* a0 = orig DEPC,
* a2 = new sp based on KSTK from exc_table
* a3 = excsave_1
* excsave_1 = orig a3
*
* 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!
*/
_DoubleExceptionVector_WindowUnderflow:
xsr a3, excsave1
wsr a2, depc # save stack pointer temporarily
rsr a0, ps
extui a0, a0, PS_OWB_SHIFT, PS_OWB_WIDTH
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
xsr a3, excsave1
rsr a0, exccause
s32i a0, a2, PT_DEPC # mark it as a regular exception
addx4 a0, a0, a3
xsr a3, excsave1
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!
*/
#ifdef CONFIG_MMU
.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, EXCCAUSE_ITLB_MISS, 1f
addi a3, a3, -EXCCAUSE_DTLB_MISS
bnez a3, .Lunrecoverable
1: movi a3, fast_second_level_miss_double_kernel
jx a3
#else
.equ .Lksp, .Lunrecoverable
#endif
/* 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, excsave1
wsr a0, excsave1
movi a0, unrecoverable_exception
callx0 a0
.Lfixup:/* Check for a fixup handler or if we were in a critical section. */
/* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave1: a3 */
/* Enter critical section. */
l32i a2, a3, EXC_TABLE_FIXUP
s32i a3, a3, EXC_TABLE_FIXUP
beq a2, a3, .Lunrecoverable_fixup # critical section
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 a2, a3, EXC_TABLE_DOUBLE_SAVE
addi a2, a2, -PT_USER_SIZE
/* a0: depc, a1: a1, a2: kstk, a3: exctable, depc: a0, excsave: a3 */
s32i a0, a2, PT_DEPC
l32i a0, a3, EXC_TABLE_DOUBLE_SAVE
xsr a0, depc
s32i a0, a2, PT_AREG0
/* a0: avail, a1: a1, a2: kstk, a3: exctable, depc: a2, excsave: a3 */
rsr a0, exccause
addx4 a0, a0, a3
xsr a3, excsave1
l32i a0, a0, EXC_TABLE_FAST_USER
jx a0
/*
* Restart window OVERFLOW exception.
* Currently:
* depc = orig a0,
* a0 = orig DEPC,
* a2 = new sp based on KSTK from exc_table
* a3 = EXCSAVE_1
* excsave_1 = orig a3
*
* We return to the instruction in user space that caused the window
* overflow exception. Therefore, we change window base to the value
* before we entered the window overflow exception and prepare the
* registers to return as if we were coming from a regular exception
* by changing DEPC (in a0).
*
* NOTE: We CANNOT trash the current window frame (a0...a3), but we
* can clobber depc.
*
* The tricky part here is that overflow8 and overflow12 handlers
* save a0, then clobber a0. To restart the handler, we have to restore
* a0 if the double exception was past the point where a0 was clobbered.
*
* To keep things simple, we take advantage of the fact all overflow
* handlers save a0 in their very first instruction. If DEPC was past
* that instruction, we can safely restore a0 from where it was saved
* on the stack.
*
* a0: depc, a1: a1, a2: kstk, a3: exc_table, depc: a0, excsave1: a3
*/
_DoubleExceptionVector_WindowOverflow:
extui a2, a0, 0, 6 # get offset into 64-byte vector handler
beqz a2, 1f # if at start of vector, don't restore
addi a0, a0, -128
bbsi.l a0, 8, 1f # don't restore except for overflow 8 and 12
/*
* This fixup handler is for the extremely unlikely case where the
* overflow handler's reference thru a0 gets a hardware TLB refill
* that bumps out the (distinct, aliasing) TLB entry that mapped its
* prior references thru a9/a13, and where our reference now thru
* a9/a13 gets a 2nd-level miss exception (not hardware TLB refill).
*/
movi a2, window_overflow_restore_a0_fixup
s32i a2, a3, EXC_TABLE_FIXUP
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
xsr a3, excsave1
bbsi.l a0, 7, 2f
/*
* Restore a0 as saved by _WindowOverflow8().
*/
l32e a0, a9, -16
wsr a0, depc # replace the saved a0
j 3f
2:
/*
* Restore a0 as saved by _WindowOverflow12().
*/
l32e a0, a13, -16
wsr a0, depc # replace the saved a0
3:
xsr a3, excsave1
movi a0, 0
s32i a0, a3, EXC_TABLE_FIXUP
s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
1:
/*
* Restore WindowBase while leaving all address registers restored.
* We have to use ROTW for this, because WSR.WINDOWBASE requires
* an address register (which would prevent restore).
*
* Window Base goes from 0 ... 7 (Module 8)
* Window Start is 8 bits; Ex: (0b1010 1010):0x55 from series of call4s
*/
rsr a0, ps
extui a0, a0, PS_OWB_SHIFT, PS_OWB_WIDTH
rsr a2, windowbase
sub a0, a2, a0
extui a0, a0, 0, 3
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
xsr a3, excsave1
beqi a0, 1, .L1pane
beqi a0, 3, .L3pane
rsr a0, depc
rotw -2
/*
* We are now in the user code's original window frame.
* Process the exception as a user exception as if it was
* taken by the user code.
*
* This is similar to the user exception vector,
* except that PT_DEPC isn't set to EXCCAUSE.
*/
1:
xsr a3, excsave1
wsr a2, depc
l32i a2, a3, EXC_TABLE_KSTK
s32i a0, a2, PT_AREG0
rsr a0, exccause
s32i a0, a2, PT_DEPC
_DoubleExceptionVector_handle_exception:
addi a0, a0, -EXCCAUSE_UNALIGNED
beqz a0, 2f
addx4 a0, a0, a3
l32i a0, a0, EXC_TABLE_FAST_USER + 4 * EXCCAUSE_UNALIGNED
xsr a3, excsave1
jx a0
2:
movi a0, user_exception
xsr a3, excsave1
jx a0
.L1pane:
rsr a0, depc
rotw -1
j 1b
.L3pane:
rsr a0, depc
rotw -3
j 1b
ENDPROC(_DoubleExceptionVector)
/*
* Fixup handler for TLB miss in double exception handler for window owerflow.
* We get here with windowbase set to the window that was being spilled and
* a0 trashed. a0 bit 7 determines if this is a call8 (bit clear) or call12
* (bit set) window.
*
* We do the following here:
* - go to the original window retaining a0 value;
* - set up exception stack to return back to appropriate a0 restore code
* (we'll need to rotate window back and there's no place to save this
* information, use different return address for that);
* - handle the exception;
* - go to the window that was being spilled;
* - set up window_overflow_restore_a0_fixup as a fixup routine;
* - reload a0;
* - restore the original window;
* - reset the default fixup routine;
* - return to user. By the time we get to this fixup handler all information
* about the conditions of the original double exception that happened in
* the window overflow handler is lost, so we just return to userspace to
* retry overflow from start.
*
* a0: value of depc, original value in depc
* a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE
* a3: exctable, original value in excsave1
*/
ENTRY(window_overflow_restore_a0_fixup)
rsr a0, ps
extui a0, a0, PS_OWB_SHIFT, PS_OWB_WIDTH
rsr a2, windowbase
sub a0, a2, a0
extui a0, a0, 0, 3
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
xsr a3, excsave1
_beqi a0, 1, .Lhandle_1
_beqi a0, 3, .Lhandle_3
.macro overflow_fixup_handle_exception_pane n
rsr a0, depc
rotw -\n
xsr a3, excsave1
wsr a2, depc
l32i a2, a3, EXC_TABLE_KSTK
s32i a0, a2, PT_AREG0
movi a0, .Lrestore_\n
s32i a0, a2, PT_DEPC
rsr a0, exccause
j _DoubleExceptionVector_handle_exception
.endm
overflow_fixup_handle_exception_pane 2
.Lhandle_1:
overflow_fixup_handle_exception_pane 1
.Lhandle_3:
overflow_fixup_handle_exception_pane 3
.macro overflow_fixup_restore_a0_pane n
rotw \n
/* Need to preserve a0 value here to be able to handle exception
* that may occur on a0 reload from stack. It may occur because
* TLB miss handler may not be atomic and pointer to page table
* may be lost before we get here. There are no free registers,
* so we need to use EXC_TABLE_DOUBLE_SAVE area.
*/
xsr a3, excsave1
s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
movi a2, window_overflow_restore_a0_fixup
s32i a2, a3, EXC_TABLE_FIXUP
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
xsr a3, excsave1
bbsi.l a0, 7, 1f
l32e a0, a9, -16
j 2f
1:
l32e a0, a13, -16
2:
rotw -\n
.endm
.Lrestore_2:
overflow_fixup_restore_a0_pane 2
.Lset_default_fixup:
xsr a3, excsave1
s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
movi a2, 0
s32i a2, a3, EXC_TABLE_FIXUP
l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
xsr a3, excsave1
rfe
.Lrestore_1:
overflow_fixup_restore_a0_pane 1
j .Lset_default_fixup
.Lrestore_3:
overflow_fixup_restore_a0_pane 3
j .Lset_default_fixup
ENDPROC(window_overflow_restore_a0_fixup)
.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, SREG_EXCSAVE + XCHAL_DEBUGLEVEL
jx a0
ENDPROC(_DebugInterruptVector)
/*
* Medium priority level interrupt vectors
*
* Each takes less than 16 (0x10) bytes, no literals, by placing
* the extra 8 bytes that would otherwise be required in the window
* vectors area where there is space. With relocatable vectors,
* all vectors are within ~ 4 kB range of each other, so we can
* simply jump (J) to another vector without having to use JX.
*
* common_exception code gets current IRQ level in PS.INTLEVEL
* and preserves it for the IRQ handling time.
*/
.macro irq_entry_level level
.if XCHAL_EXCM_LEVEL >= \level
.section .Level\level\()InterruptVector.text, "ax"
ENTRY(_Level\level\()InterruptVector)
wsr a0, excsave2
rsr a0, epc\level
wsr a0, epc1
movi a0, EXCCAUSE_LEVEL1_INTERRUPT
wsr a0, exccause
rsr a0, eps\level
# branch to user or kernel vector
j _SimulateUserKernelVectorException
.endif
.endm
irq_entry_level 2
irq_entry_level 3
irq_entry_level 4
irq_entry_level 5
irq_entry_level 6
/* 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.
*/
#define ENTRY_ALIGN64(name) \
.globl name; \
.align 64; \
name:
.section .WindowVectors.text, "ax"
/* 4-Register Window Overflow Vector (Handler) */
ENTRY_ALIGN64(_WindowOverflow4)
s32e a0, a5, -16
s32e a1, a5, -12
s32e a2, a5, -8
s32e a3, a5, -4
rfwo
ENDPROC(_WindowOverflow4)
#if XCHAL_EXCM_LEVEL >= 2
/* Not a window vector - but a convenient location
* (where we know there's space) for continuation of
* medium priority interrupt dispatch code.
* On entry here, a0 contains PS, and EPC2 contains saved a0:
*/
.align 4
_SimulateUserKernelVectorException:
addi a0, a0, (1 << PS_EXCM_BIT)
wsr a0, ps
bbsi.l a0, PS_UM_BIT, 1f # branch if user mode
rsr a0, excsave2 # restore a0
j _KernelExceptionVector # simulate kernel vector exception
1: rsr a0, excsave2 # restore a0
j _UserExceptionVector # simulate user vector exception
#endif
/* 4-Register Window Underflow Vector (Handler) */
ENTRY_ALIGN64(_WindowUnderflow4)
l32e a0, a5, -16
l32e a1, a5, -12
l32e a2, a5, -8
l32e a3, a5, -4
rfwu
ENDPROC(_WindowUnderflow4)
/* 8-Register Window Overflow Vector (Handler) */
ENTRY_ALIGN64(_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
ENDPROC(_WindowOverflow8)
/* 8-Register Window Underflow Vector (Handler) */
ENTRY_ALIGN64(_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
ENDPROC(_WindowUnderflow8)
/* 12-Register Window Overflow Vector (Handler) */
ENTRY_ALIGN64(_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
ENDPROC(_WindowOverflow12)
/* 12-Register Window Underflow Vector (Handler) */
ENTRY_ALIGN64(_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
ENDPROC(_WindowUnderflow12)
.text

View file

@ -0,0 +1,370 @@
/*
* 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 - 2008 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 <asm/page.h>
#include <asm/thread_info.h>
#include <asm/vectors.h>
#include <variant/core.h>
#include <platform/hardware.h>
OUTPUT_ARCH(xtensa)
ENTRY(_start)
#ifdef __XTENSA_EB__
jiffies = jiffies_64 + 4;
#else
jiffies = jiffies_64;
#endif
#ifndef KERNELOFFSET
#define KERNELOFFSET 0xd0003000
#endif
/* 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
{
. = KERNELOFFSET;
/* .text section */
_text = .;
_stext = .;
.text :
{
/* The HEAD_TEXT section must be the first section! */
HEAD_TEXT
TEXT_TEXT
VMLINUX_SYMBOL(__sched_text_start) = .;
*(.sched.literal .sched.text)
VMLINUX_SYMBOL(__sched_text_end) = .;
VMLINUX_SYMBOL(__lock_text_start) = .;
*(.spinlock.literal .spinlock.text)
VMLINUX_SYMBOL(__lock_text_end) = .;
}
_etext = .;
PROVIDE (etext = .);
. = ALIGN(16);
RODATA
/* Relocation table */
.fixup : { *(.fixup) }
EXCEPTION_TABLE(16)
/* Data section */
_sdata = .;
RW_DATA_SECTION(XCHAL_ICACHE_LINESIZE, PAGE_SIZE, THREAD_SIZE)
_edata = .;
/* Initialization code and data: */
. = ALIGN(PAGE_SIZE);
__init_begin = .;
INIT_TEXT_SECTION(PAGE_SIZE)
.init.data :
{
INIT_DATA
. = ALIGN(0x4);
__tagtable_begin = .;
*(.taglist)
__tagtable_end = .;
. = ALIGN(16);
__boot_reloc_table_start = ABSOLUTE(.);
RELOCATE_ENTRY(_WindowVectors_text,
.WindowVectors.text);
#if XCHAL_EXCM_LEVEL >= 2
RELOCATE_ENTRY(_Level2InterruptVector_text,
.Level2InterruptVector.text);
#endif
#if XCHAL_EXCM_LEVEL >= 3
RELOCATE_ENTRY(_Level3InterruptVector_text,
.Level3InterruptVector.text);
#endif
#if XCHAL_EXCM_LEVEL >= 4
RELOCATE_ENTRY(_Level4InterruptVector_text,
.Level4InterruptVector.text);
#endif
#if XCHAL_EXCM_LEVEL >= 5
RELOCATE_ENTRY(_Level5InterruptVector_text,
.Level5InterruptVector.text);
#endif
#if XCHAL_EXCM_LEVEL >= 6
RELOCATE_ENTRY(_Level6InterruptVector_text,
.Level6InterruptVector.text);
#endif
RELOCATE_ENTRY(_KernelExceptionVector_text,
.KernelExceptionVector.text);
RELOCATE_ENTRY(_UserExceptionVector_text,
.UserExceptionVector.text);
RELOCATE_ENTRY(_DoubleExceptionVector_literal,
.DoubleExceptionVector.literal);
RELOCATE_ENTRY(_DoubleExceptionVector_text,
.DoubleExceptionVector.text);
RELOCATE_ENTRY(_DebugInterruptVector_text,
.DebugInterruptVector.text);
#if defined(CONFIG_SMP)
RELOCATE_ENTRY(_SecondaryResetVector_literal,
.SecondaryResetVector.literal);
RELOCATE_ENTRY(_SecondaryResetVector_text,
.SecondaryResetVector.text);
#endif
__boot_reloc_table_end = ABSOLUTE(.) ;
INIT_SETUP(XCHAL_ICACHE_LINESIZE)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
PERCPU_SECTION(XCHAL_ICACHE_LINESIZE)
/* 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,
WINDOW_VECTORS_VADDR, 4,
.dummy)
SECTION_VECTOR (_DebugInterruptVector_literal,
.DebugInterruptVector.literal,
DEBUG_VECTOR_VADDR - 4,
SIZEOF(.WindowVectors.text),
.WindowVectors.text)
SECTION_VECTOR (_DebugInterruptVector_text,
.DebugInterruptVector.text,
DEBUG_VECTOR_VADDR,
4,
.DebugInterruptVector.literal)
#undef LAST
#define LAST .DebugInterruptVector.text
#if XCHAL_EXCM_LEVEL >= 2
SECTION_VECTOR (_Level2InterruptVector_text,
.Level2InterruptVector.text,
INTLEVEL2_VECTOR_VADDR,
SIZEOF(LAST), LAST)
# undef LAST
# define LAST .Level2InterruptVector.text
#endif
#if XCHAL_EXCM_LEVEL >= 3
SECTION_VECTOR (_Level3InterruptVector_text,
.Level3InterruptVector.text,
INTLEVEL3_VECTOR_VADDR,
SIZEOF(LAST), LAST)
# undef LAST
# define LAST .Level3InterruptVector.text
#endif
#if XCHAL_EXCM_LEVEL >= 4
SECTION_VECTOR (_Level4InterruptVector_text,
.Level4InterruptVector.text,
INTLEVEL4_VECTOR_VADDR,
SIZEOF(LAST), LAST)
# undef LAST
# define LAST .Level4InterruptVector.text
#endif
#if XCHAL_EXCM_LEVEL >= 5
SECTION_VECTOR (_Level5InterruptVector_text,
.Level5InterruptVector.text,
INTLEVEL5_VECTOR_VADDR,
SIZEOF(LAST), LAST)
# undef LAST
# define LAST .Level5InterruptVector.text
#endif
#if XCHAL_EXCM_LEVEL >= 6
SECTION_VECTOR (_Level6InterruptVector_text,
.Level6InterruptVector.text,
INTLEVEL6_VECTOR_VADDR,
SIZEOF(LAST), LAST)
# undef LAST
# define LAST .Level6InterruptVector.text
#endif
SECTION_VECTOR (_KernelExceptionVector_literal,
.KernelExceptionVector.literal,
KERNEL_VECTOR_VADDR - 4,
SIZEOF(LAST), LAST)
#undef LAST
SECTION_VECTOR (_KernelExceptionVector_text,
.KernelExceptionVector.text,
KERNEL_VECTOR_VADDR,
4,
.KernelExceptionVector.literal)
SECTION_VECTOR (_UserExceptionVector_literal,
.UserExceptionVector.literal,
USER_VECTOR_VADDR - 4,
SIZEOF(.KernelExceptionVector.text),
.KernelExceptionVector.text)
SECTION_VECTOR (_UserExceptionVector_text,
.UserExceptionVector.text,
USER_VECTOR_VADDR,
4,
.UserExceptionVector.literal)
SECTION_VECTOR (_DoubleExceptionVector_literal,
.DoubleExceptionVector.literal,
DOUBLEEXC_VECTOR_VADDR - 48,
SIZEOF(.UserExceptionVector.text),
.UserExceptionVector.text)
SECTION_VECTOR (_DoubleExceptionVector_text,
.DoubleExceptionVector.text,
DOUBLEEXC_VECTOR_VADDR,
48,
.DoubleExceptionVector.literal)
. = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
#if defined(CONFIG_SMP)
SECTION_VECTOR (_SecondaryResetVector_literal,
.SecondaryResetVector.literal,
RESET_VECTOR1_VADDR - 4,
SIZEOF(.DoubleExceptionVector.text),
.DoubleExceptionVector.text)
SECTION_VECTOR (_SecondaryResetVector_text,
.SecondaryResetVector.text,
RESET_VECTOR1_VADDR,
4,
.SecondaryResetVector.literal)
. = LOADADDR(.SecondaryResetVector.text)+SIZEOF(.SecondaryResetVector.text);
#endif
. = ALIGN(PAGE_SIZE);
__init_end = .;
BSS_SECTION(0, 8192, 0)
_end = .;
/* only used by the boot loader */
. = ALIGN(0x10);
.bootstrap : { *(.bootstrap.literal .bootstrap.text .bootstrap.data) }
.ResetVector.text RESET_VECTOR_VADDR :
{
*(.ResetVector.text)
}
/*
* This is a remapped copy of the Secondary Reset Vector Code.
* It keeps gdb in sync with the PC after switching
* to the temporary mapping used while setting up
* the V2 MMU mappings for Linux.
*
* Only debug information about this section is put in the kernel image.
*/
.SecondaryResetVector.remapped_text 0x46000000 (INFO):
{
*(.SecondaryResetVector.remapped_text)
}
.xt.lit : { *(.xt.lit) }
.xt.prop : { *(.xt.prop) }
.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*)
}
/* Sections to be discarded */
DISCARDS
/DISCARD/ : { *(.exit.literal) }
}

View file

@ -0,0 +1,136 @@
/*
* 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/module.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/checksum.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/ftrace.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(memset);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(__strncpy_user);
EXPORT_SYMBOL(clear_page);
EXPORT_SYMBOL(copy_page);
EXPORT_SYMBOL(empty_zero_page);
/*
* 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);
extern int __ucmpdi2(int, int);
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);
EXPORT_SYMBOL(__ucmpdi2);
void __xtensa_libgcc_window_spill(void)
{
BUG();
}
EXPORT_SYMBOL(__xtensa_libgcc_window_spill);
unsigned long __sync_fetch_and_and_4(unsigned long *p, unsigned long v)
{
BUG();
}
EXPORT_SYMBOL(__sync_fetch_and_and_4);
unsigned long __sync_fetch_and_or_4(unsigned long *p, unsigned long v)
{
BUG();
}
EXPORT_SYMBOL(__sync_fetch_and_or_4);
#ifdef CONFIG_NET
/*
* Networking support
*/
EXPORT_SYMBOL(csum_partial);
EXPORT_SYMBOL(csum_partial_copy_generic);
#endif /* CONFIG_NET */
/*
* Architecture-specific symbols
*/
EXPORT_SYMBOL(__xtensa_copy_user);
EXPORT_SYMBOL(__invalidate_icache_range);
/*
* Kernel hacking ...
*/
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
// FIXME EXPORT_SYMBOL(screen_info);
#endif
EXPORT_SYMBOL(outsb);
EXPORT_SYMBOL(outsw);
EXPORT_SYMBOL(outsl);
EXPORT_SYMBOL(insb);
EXPORT_SYMBOL(insw);
EXPORT_SYMBOL(insl);
extern long common_exception_return;
EXPORT_SYMBOL(common_exception_return);
#ifdef CONFIG_FUNCTION_TRACER
EXPORT_SYMBOL(_mcount);
#endif
EXPORT_SYMBOL(__invalidate_dcache_range);
#if XCHAL_DCACHE_IS_WRITEBACK
EXPORT_SYMBOL(__flush_dcache_range);
#endif