/*
* Copyright 2011 Tilera Corporation. All Rights Reserved.
*
* 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, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*
* Linux interrupt vectors.
*/
#include <linux/linkage.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/init.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/irqflags.h>
#include <asm/asm-offsets.h>
#include <asm/types.h>
#include <asm/traps.h>
#include <asm/signal.h>
#include <hv/hypervisor.h>
#include <arch/abi.h>
#include <arch/interrupts.h>
#include <arch/spr_def.h>
#define PTREGS_PTR(reg, ptreg) addli reg, sp, C_ABI_SAVE_AREA_SIZE + (ptreg)
#define PTREGS_OFFSET_SYSCALL PTREGS_OFFSET_REG(TREG_SYSCALL_NR)
#if CONFIG_KERNEL_PL == 1 || CONFIG_KERNEL_PL == 2
/*
* Set "result" non-zero if ex1 holds the PL of the kernel
* (with or without ICS being set). Note this works only
* because we never find the PL at level 3.
*/
# define IS_KERNEL_EX1(result, ex1) andi result, ex1, CONFIG_KERNEL_PL
#else
# error Recode IS_KERNEL_EX1 for CONFIG_KERNEL_PL
#endif
.macro push_reg reg, ptr=sp, delta=-8
{
st \ptr, \reg
addli \ptr, \ptr, \delta
}
.endm
.macro pop_reg reg, ptr=sp, delta=8
{
ld \reg, \ptr
addli \ptr, \ptr, \delta
}
.endm
.macro pop_reg_zero reg, zreg, ptr=sp, delta=8
{
move \zreg, zero
ld \reg, \ptr
addi \ptr, \ptr, \delta
}
.endm
.macro push_extra_callee_saves reg
PTREGS_PTR(\reg, PTREGS_OFFSET_REG(51))
push_reg r51, \reg
push_reg r50, \reg
push_reg r49, \reg
push_reg r48, \reg
push_reg r47, \reg
push_reg r46, \reg
push_reg r45, \reg
push_reg r44, \reg
push_reg r43, \reg
push_reg r42, \reg
push_reg r41, \reg
push_reg r40, \reg
push_reg r39, \reg
push_reg r38, \reg
push_reg r37, \reg
push_reg r36, \reg
push_reg r35, \reg
push_reg r34, \reg, PTREGS_OFFSET_BASE - PTREGS_OFFSET_REG(34)
.endm
.macro panic str
.pushsection .rodata, "a"
1:
.asciz "\str"
.popsection
{
moveli r0, hw2_last(1b)
}
{
shl16insli r0, r0, hw1(1b)
}
{
shl16insli r0, r0, hw0(1b)
jal panic
}
.endm
/*
* Unalign data exception fast handling: In order to handle
* unaligned data access, a fast JIT version is generated and stored
* in a specific area in user space. We first need to do a quick poke
* to see if the JIT is available. We use certain bits in the fault
* PC (3 to 9 is used for 16KB page size) as index to address the JIT
* code area. The first 64bit word is the fault PC, and the 2nd one is
* the fault bundle itself. If these 2 words both match, then we
* directly "iret" to JIT code. If not, a slow path is invoked to
* generate new JIT code. Note: the current JIT code WILL be
* overwritten if it existed. So, ideally we can handle 128 unalign
* fixups via JIT. For lookup efficiency and to effectively support
* tight loops with multiple unaligned reference, a simple
* direct-mapped cache is used.
*
* SPR_EX_CONTEXT_K_0 is modified to return to JIT code.
* SPR_EX_CONTEXT_K_1 has ICS set.
* SPR_EX_CONTEXT_0_0 is setup to user program's next PC.
* SPR_EX_CONTEXT_0_1 = 0.
*/
.macro int_hand_unalign_fast vecnum, vecname
.org (\vecnum << 8)
intvec_\vecname:
/* Put r3 in SPR_SYSTEM_SAVE_K_1. */
mtspr SPR_SYSTEM_SAVE_K_1, r3
mfspr r3, SPR_EX_CONTEXT_K_1
/*
* Examine if exception comes from user without ICS set.
* If not, just go directly to the slow path.
*/
bnez r3, hand_unalign_slow_nonuser
mfspr r3, SPR_SYSTEM_SAVE_K_0
/* Get &thread_info->unalign_jit_tmp[0] in r3. */
bfexts r3, r3, 0, CPU_SHIFT-1
mm r3, zero, LOG2_THREAD_SIZE, 63
addli r3, r3, THREAD_INFO_UNALIGN_JIT_TMP_OFFSET
/*
* Save r0, r1, r2 into thread_info array r3 points to
* from low to high memory in order.
*/
st_add r3, r0, 8
st_add r3, r1, 8
{
st_add r3, r2, 8
andi r2, sp, 7
}
/* Save stored r3 value so we can revert it on a page fault. */
mfspr r1, SPR_SYSTEM_SAVE_K_1
st r3, r1
{
/* Generate a SIGBUS if sp is not 8-byte aligned. */
bnez r2, hand_unalign_slow_badsp
}
/*
* Get the thread_info in r0; load r1 with pc. Set the low bit of sp
* as an indicator to the page fault code in case we fault.
*/
{
ori sp, sp, 1
mfspr r1, SPR_EX_CONTEXT_K_0
}
/* Add the jit_info offset in thread_info; extract r1 [3:9] into r2. */
{
addli r0, r3, THREAD_INFO_UNALIGN_JIT_BASE_OFFSET - \
(THREAD_INFO_UNALIGN_JIT_TMP_OFFSET + (3 * 8))
bfextu r2, r1, 3, (2 + PAGE_SHIFT - UNALIGN_JIT_SHIFT)
}
/* Load the jit_info; multiply r2 by 128. */
{
ld r0, r0
shli r2, r2, UNALIGN_JIT_SHIFT
}
/*
* If r0 is NULL, the JIT page is not mapped, so go to slow path;
* add offset r2 to r0 at the same time.
*/
{
beqz r0, hand_unalign_slow
add r2, r0, r2
}
/*
* We are loading from userspace (both the JIT info PC and
* instruction word, and the instruction word we executed)
* and since either could fault while holding the interrupt
* critical section, we must tag this region and check it in
* do_page_fault() to handle it properly.
*/
ENTRY(__start_unalign_asm_code)
/* Load first word of JIT in r0 and increment r2 by 8. */
ld_add r0, r2, 8
/*
* Compare the PC with the 1st word in JIT; load the fault bundle
* into r1.
*/
{
cmpeq r0, r0, r1
ld r1, r1
}
/* Go to slow path if PC doesn't match. */
beqz r0, hand_unalign_slow
/*
* Load the 2nd word of JIT, which is supposed to be the fault
* bundle for a cache hit. Increment r2; after this bundle r2 will
* point to the potential start of the JIT code we want to run.
*/
ld_add r0, r2, 8
/* No further accesses to userspace are done after this point. */
ENTRY(__end_unalign_asm_code)
/* Com
|