aboutsummaryrefslogtreecommitdiff
path: root/arch/tile/kernel
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2011-05-04 14:38:26 -0400
committerChris Metcalf <cmetcalf@tilera.com>2011-05-12 15:52:12 -0400
commit18aecc2b645bbb07851b196452a2af314222069b (patch)
tree959f765f69af01046c6e26db12b45c3390799d3e /arch/tile/kernel
parentbe84cb43833ee40a42e08f5425d20310f16229c7 (diff)
arch/tile: finish enabling support for TILE-Gx 64-bit chip
This support was partially present in the existing code (look for "__tilegx__" ifdefs) but with this change you can build a working kernel using the TILE-Gx toolchain and ARCH=tilegx. Most of these files are new, generally adding a foo_64.c file where previously there was just a foo_32.c file. The ARCH=tilegx directive redirects to arch/tile, not arch/tilegx, using the existing SRCARCH mechanism in the top-level Makefile. Changes to existing files: - <asm/bitops.h> and <asm/bitops_32.h> changed to factor the include of <asm-generic/bitops/non-atomic.h> in the common header. - <asm/compat.h> and arch/tile/kernel/compat.c changed to remove the "const" markers I had put on compat_sys_execve() when trying to match some recent similar changes to the non-compat execve. It turns out the compat version wasn't "upgraded" to use const. - <asm/opcode-tile_64.h> and <asm/opcode_constants_64.h> were previously included accidentally, with the 32-bit contents. Now they have the proper 64-bit contents. Finally, I had to hack the existing hacky drivers/input/input-compat.h to add yet another "#ifdef" for INPUT_COMPAT_TEST (same as x86_64). Signed-off-by: Chris Metcalf <cmetcalf@tilera.com> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> [drivers/input]
Diffstat (limited to 'arch/tile/kernel')
-rw-r--r--arch/tile/kernel/futex_64.S55
-rw-r--r--arch/tile/kernel/head_64.S269
-rw-r--r--arch/tile/kernel/intvec_64.S1231
-rw-r--r--arch/tile/kernel/process.c4
-rw-r--r--arch/tile/kernel/regs_64.S145
-rw-r--r--arch/tile/kernel/tile-desc_64.c2200
6 files changed, 3902 insertions, 2 deletions
diff --git a/arch/tile/kernel/futex_64.S b/arch/tile/kernel/futex_64.S
new file mode 100644
index 00000000000..f465d1eda20
--- /dev/null
+++ b/arch/tile/kernel/futex_64.S
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ * Atomically access user memory, but use MMU to avoid propagating
+ * kernel exceptions.
+ */
+
+#include <linux/linkage.h>
+#include <asm/errno.h>
+#include <asm/futex.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+
+/*
+ * Provide a set of atomic memory operations supporting <asm/futex.h>.
+ *
+ * r0: user address to manipulate
+ * r1: new value to write, or for cmpxchg, old value to compare against
+ * r2: (cmpxchg only) new value to write
+ *
+ * Return __get_user struct, r0 with value, r1 with error.
+ */
+#define FUTEX_OP(name, ...) \
+STD_ENTRY(futex_##name) \
+ __VA_ARGS__; \
+ { \
+ move r1, zero; \
+ jrp lr \
+ }; \
+ STD_ENDPROC(futex_##name); \
+ .pushsection __ex_table,"a"; \
+ .quad 1b, get_user_fault; \
+ .popsection
+
+ .pushsection .fixup,"ax"
+get_user_fault:
+ { movei r1, -EFAULT; jrp lr }
+ ENDPROC(get_user_fault)
+ .popsection
+
+FUTEX_OP(cmpxchg, mtspr CMPEXCH_VALUE, r1; 1: cmpexch4 r0, r0, r2)
+FUTEX_OP(set, 1: exch4 r0, r0, r1)
+FUTEX_OP(add, 1: fetchadd4 r0, r0, r1)
+FUTEX_OP(or, 1: fetchor4 r0, r0, r1)
+FUTEX_OP(andn, nor r1, r1, zero; 1: fetchand4 r0, r0, r1)
diff --git a/arch/tile/kernel/head_64.S b/arch/tile/kernel/head_64.S
new file mode 100644
index 00000000000..6bc3a932fe4
--- /dev/null
+++ b/arch/tile/kernel/head_64.S
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ *
+ * TILE startup code.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+#include <asm/asm-offsets.h>
+#include <hv/hypervisor.h>
+#include <arch/chip.h>
+#include <arch/spr_def.h>
+
+/*
+ * This module contains the entry code for kernel images. It performs the
+ * minimal setup needed to call the generic C routines.
+ */
+
+ __HEAD
+ENTRY(_start)
+ /* Notify the hypervisor of what version of the API we want */
+ {
+ movei r1, TILE_CHIP
+ movei r2, TILE_CHIP_REV
+ }
+ {
+ moveli r0, _HV_VERSION
+ jal hv_init
+ }
+ /* Get a reasonable default ASID in r0 */
+ {
+ move r0, zero
+ jal hv_inquire_asid
+ }
+
+ /*
+ * Install the default page table. The relocation required to
+ * statically define the table is a bit too complex, so we have
+ * to plug in the pointer from the L0 to the L1 table by hand.
+ * We only do this on the first cpu to boot, though, since the
+ * other CPUs should see a properly-constructed page table.
+ */
+ {
+ v4int_l r2, zero, r0 /* ASID for hv_install_context */
+ moveli r4, hw1_last(swapper_pgprot - PAGE_OFFSET)
+ }
+ {
+ shl16insli r4, r4, hw0(swapper_pgprot - PAGE_OFFSET)
+ }
+ {
+ ld r1, r4 /* access_pte for hv_install_context */
+ }
+ {
+ moveli r0, hw1_last(.Lsv_data_pmd - PAGE_OFFSET)
+ moveli r6, hw1_last(temp_data_pmd - PAGE_OFFSET)
+ }
+ {
+ /* After initializing swapper_pgprot, HV_PTE_GLOBAL is set. */
+ bfextu r7, r1, HV_PTE_INDEX_GLOBAL, HV_PTE_INDEX_GLOBAL
+ inv r4
+ }
+ bnez r7, .Lno_write
+ {
+ shl16insli r0, r0, hw0(.Lsv_data_pmd - PAGE_OFFSET)
+ shl16insli r6, r6, hw0(temp_data_pmd - PAGE_OFFSET)
+ }
+ {
+ /* Cut off the low bits of the PT address. */
+ shrui r6, r6, HV_LOG2_PAGE_TABLE_ALIGN
+ /* Start with our access pte. */
+ move r5, r1
+ }
+ {
+ /* Stuff the address into the page table pointer slot of the PTE. */
+ bfins r5, r6, HV_PTE_INDEX_PTFN, \
+ HV_PTE_INDEX_PTFN + HV_PTE_PTFN_BITS - 1
+ }
+ {
+ /* Store the L0 data PTE. */
+ st r0, r5
+ addli r6, r6, (temp_code_pmd - temp_data_pmd) >> \
+ HV_LOG2_PAGE_TABLE_ALIGN
+ }
+ {
+ addli r0, r0, .Lsv_code_pmd - .Lsv_data_pmd
+ bfins r5, r6, HV_PTE_INDEX_PTFN, \
+ HV_PTE_INDEX_PTFN + HV_PTE_PTFN_BITS - 1
+ }
+ /* Store the L0 code PTE. */
+ st r0, r5
+
+.Lno_write:
+ moveli lr, hw2_last(1f)
+ {
+ shl16insli lr, lr, hw1(1f)
+ moveli r0, hw1_last(swapper_pg_dir - PAGE_OFFSET)
+ }
+ {
+ shl16insli lr, lr, hw0(1f)
+ shl16insli r0, r0, hw0(swapper_pg_dir - PAGE_OFFSET)
+ }
+ {
+ move r3, zero
+ j hv_install_context
+ }
+1:
+
+ /* Install the interrupt base. */
+ moveli r0, hw2_last(MEM_SV_START)
+ shl16insli r0, r0, hw1(MEM_SV_START)
+ shl16insli r0, r0, hw0(MEM_SV_START)
+ mtspr SPR_INTERRUPT_VECTOR_BASE_K, r0
+
+ /*
+ * Get our processor number and save it away in SAVE_K_0.
+ * Extract stuff from the topology structure: r4 = y, r6 = x,
+ * r5 = width. FIXME: consider whether we want to just make these
+ * 64-bit values (and if so fix smp_topology write below, too).
+ */
+ jal hv_inquire_topology
+ {
+ v4int_l r5, zero, r1 /* r5 = width */
+ shrui r4, r0, 32 /* r4 = y */
+ }
+ {
+ v4int_l r6, zero, r0 /* r6 = x */
+ mul_lu_lu r4, r4, r5
+ }
+ {
+ add r4, r4, r6 /* r4 == cpu == y*width + x */
+ }
+
+#ifdef CONFIG_SMP
+ /*
+ * Load up our per-cpu offset. When the first (master) tile
+ * boots, this value is still zero, so we will load boot_pc
+ * with start_kernel, and boot_sp with init_stack + THREAD_SIZE.
+ * The master tile initializes the per-cpu offset array, so that
+ * when subsequent (secondary) tiles boot, they will instead load
+ * from their per-cpu versions of boot_sp and boot_pc.
+ */
+ moveli r5, hw2_last(__per_cpu_offset)
+ shl16insli r5, r5, hw1(__per_cpu_offset)
+ shl16insli r5, r5, hw0(__per_cpu_offset)
+ shl3add r5, r4, r5
+ ld r5, r5
+ bnez r5, 1f
+
+ /*
+ * Save the width and height to the smp_topology variable
+ * for later use.
+ */
+ moveli r0, hw2_last(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET)
+ shl16insli r0, r0, hw1(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET)
+ shl16insli r0, r0, hw0(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET)
+ st r0, r1
+1:
+#else
+ move r5, zero
+#endif
+
+ /* Load and go with the correct pc and sp. */
+ {
+ moveli r1, hw2_last(boot_sp)
+ moveli r0, hw2_last(boot_pc)
+ }
+ {
+ shl16insli r1, r1, hw1(boot_sp)
+ shl16insli r0, r0, hw1(boot_pc)
+ }
+ {
+ shl16insli r1, r1, hw0(boot_sp)
+ shl16insli r0, r0, hw0(boot_pc)
+ }
+ {
+ add r1, r1, r5
+ add r0, r0, r5
+ }
+ ld r0, r0
+ ld sp, r1
+ or r4, sp, r4
+ mtspr SPR_SYSTEM_SAVE_K_0, r4 /* save ksp0 + cpu */
+ addi sp, sp, -STACK_TOP_DELTA
+ {
+ move lr, zero /* stop backtraces in the called function */
+ jr r0
+ }
+ ENDPROC(_start)
+
+__PAGE_ALIGNED_BSS
+ .align PAGE_SIZE
+ENTRY(empty_zero_page)
+ .fill PAGE_SIZE,1,0
+ END(empty_zero_page)
+
+ .macro PTE cpa, bits1
+ .quad HV_PTE_PAGE | HV_PTE_DIRTY | HV_PTE_PRESENT | HV_PTE_ACCESSED |\
+ HV_PTE_GLOBAL | (HV_PTE_MODE_CACHE_NO_L3 << HV_PTE_INDEX_MODE) |\
+ (\bits1) | (HV_CPA_TO_PFN(\cpa) << HV_PTE_INDEX_PFN)
+ .endm
+
+__PAGE_ALIGNED_DATA
+ .align PAGE_SIZE
+ENTRY(swapper_pg_dir)
+ .org swapper_pg_dir + HV_L0_INDEX(PAGE_OFFSET) * HV_PTE_SIZE
+.Lsv_data_pmd:
+ .quad 0 /* PTE temp_data_pmd - PAGE_OFFSET, 0 */
+ .org swapper_pg_dir + HV_L0_INDEX(MEM_SV_START) * HV_PTE_SIZE
+.Lsv_code_pmd:
+ .quad 0 /* PTE temp_code_pmd - PAGE_OFFSET, 0 */
+ .org swapper_pg_dir + HV_L0_SIZE
+ END(swapper_pg_dir)
+
+ .align HV_PAGE_TABLE_ALIGN
+ENTRY(temp_data_pmd)
+ /*
+ * We fill the PAGE_OFFSET pmd with huge pages with
+ * VA = PA + PAGE_OFFSET. We remap things with more precise access
+ * permissions later.
+ */
+ .set addr, 0
+ .rept HV_L1_ENTRIES
+ PTE addr, HV_PTE_READABLE | HV_PTE_WRITABLE
+ .set addr, addr + HV_PAGE_SIZE_LARGE
+ .endr
+ .org temp_data_pmd + HV_L1_SIZE
+ END(temp_data_pmd)
+
+ .align HV_PAGE_TABLE_ALIGN
+ENTRY(temp_code_pmd)
+ /*
+ * We fill the MEM_SV_START pmd with huge pages with
+ * VA = PA + PAGE_OFFSET. We remap things with more precise access
+ * permissions later.
+ */
+ .set addr, 0
+ .rept HV_L1_ENTRIES
+ PTE addr, HV_PTE_READABLE | HV_PTE_EXECUTABLE
+ .set addr, addr + HV_PAGE_SIZE_LARGE
+ .endr
+ .org temp_code_pmd + HV_L1_SIZE
+ END(temp_code_pmd)
+
+ /*
+ * Isolate swapper_pgprot to its own cache line, since each cpu
+ * starting up will read it using VA-is-PA and local homing.
+ * This would otherwise likely conflict with other data on the cache
+ * line, once we have set its permanent home in the page tables.
+ */
+ __INITDATA
+ .align CHIP_L2_LINE_SIZE()
+ENTRY(swapper_pgprot)
+ .quad HV_PTE_PRESENT | (HV_PTE_MODE_CACHE_NO_L3 << HV_PTE_INDEX_MODE)
+ .align CHIP_L2_LINE_SIZE()
+ END(swapper_pgprot)
diff --git a/arch/tile/kernel/intvec_64.S b/arch/tile/kernel/intvec_64.S
new file mode 100644
index 00000000000..79c93e10ba2
--- /dev/null
+++ b/arch/tile/kernel/intvec_64.S
@@ -0,0 +1,1231 @@
+/*
+ * 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 <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/irqflags.h>
+#include <asm/asm-offsets.h>
+#include <asm/types.h>
+#include <hv/hypervisor.h>
+#include <arch/abi.h>
+#include <arch/interrupts.h>
+#include <arch/spr_def.h>
+
+#ifdef CONFIG_PREEMPT
+# error "No support for kernel preemption currently"
+#endif
+
+#define PTREGS_PTR(reg, ptreg) addli reg, sp, C_ABI_SAVE_AREA_SIZE + (ptreg)
+
+#define PTREGS_OFFSET_SYSCALL PTREGS_OFFSET_REG(TREG_SYSCALL_NR)
+
+
+ .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
+
+
+#ifdef __COLLECT_LINKER_FEEDBACK__
+ .pushsection .text.intvec_feedback,"ax"
+intvec_feedback:
+ .popsection
+#endif
+
+ /*
+ * Default interrupt handler.
+ *
+ * vecnum is where we'll put this code.
+ * c_routine is the C routine we'll call.
+ *
+ * The C routine is passed two arguments:
+ * - A pointer to the pt_regs state.
+ * - The interrupt vector number.
+ *
+ * The "processing" argument specifies the code for processing
+ * the interrupt. Defaults to "handle_interrupt".
+ */
+ .macro int_hand vecnum, vecname, c_routine, processing=handle_interrupt
+ .org (\vecnum << 8)
+intvec_\vecname:
+ /* Temporarily save a register so we have somewhere to work. */
+
+ mtspr SPR_SYSTEM_SAVE_K_1, r0
+ mfspr r0, SPR_EX_CONTEXT_K_1
+
+ andi r0, r0, SPR_EX_CONTEXT_1_1__PL_MASK /* mask off ICS */
+
+ .ifc \vecnum, INT_DOUBLE_FAULT
+ /*
+ * For double-faults from user-space, fall through to the normal
+ * register save and stack setup path. Otherwise, it's the
+ * hypervisor giving us one last chance to dump diagnostics, and we
+ * branch to the kernel_double_fault routine to do so.
+ */
+ beqz r0, 1f
+ j _kernel_double_fault
+1:
+ .else
+ /*
+ * If we're coming from user-space, then set sp to the top of
+ * the kernel stack. Otherwise, assume sp is already valid.
+ */
+ {
+ bnez r0, 0f
+ move r0, sp
+ }
+ .endif
+
+ .ifc \c_routine, do_page_fault
+ /*
+ * The page_fault handler may be downcalled directly by the
+ * hypervisor even when Linux is running and has ICS set.
+ *
+ * In this case the contents of EX_CONTEXT_K_1 reflect the
+ * previous fault and can't be relied on to choose whether or
+ * not to reinitialize the stack pointer. So we add a test
+ * to see whether SYSTEM_SAVE_K_2 has the high bit set,
+ * and if so we don't reinitialize sp, since we must be coming
+ * from Linux. (In fact the precise case is !(val & ~1),
+ * but any Linux PC has to have the high bit set.)
+ *
+ * Note that the hypervisor *always* sets SYSTEM_SAVE_K_2 for
+ * any path that turns into a downcall to one of our TLB handlers.
+ *
+ * FIXME: if we end up never using this path, perhaps we should
+ * prevent the hypervisor from generating downcalls in this case.
+ * The advantage of getting a downcall is we can panic in Linux.
+ */
+ mfspr r0, SPR_SYSTEM_SAVE_K_2
+ {
+ bltz r0, 0f /* high bit in S_S_1_2 is for a PC to use */
+ move r0, sp
+ }
+ .endif
+
+
+ /*
+ * SYSTEM_SAVE_K_0 holds the cpu number in the low bits, and
+ * the current stack top in the higher bits. So we recover
+ * our stack top by just masking off the low bits, then
+ * point sp at the top aligned address on the actual stack page.
+ */
+ mfspr r0, SPR_SYSTEM_SAVE_K_0
+ mm r0, zero, LOG2_THREAD_SIZE, 63
+
+0:
+ /*
+ * Align the stack mod 64 so we can properly predict what
+ * cache lines we need to write-hint to reduce memory fetch
+ * latency as we enter the kernel. The layout of memory is
+ * as follows, with cache line 0 at the lowest VA, and cache
+ * line 8 just below the r0 value this "andi" computes.
+ * Note that we never write to cache line 8, and we skip
+ * cache lines 1-3 for syscalls.
+ *
+ * cache line 8: ptregs padding (two words)
+ * cache line 7: sp, lr, pc, ex1, faultnum, orig_r0, flags, cmpexch
+ * cache line 6: r46...r53 (tp)
+ * cache line 5: r38...r45
+ * cache line 4: r30...r37
+ * cache line 3: r22...r29
+ * cache line 2: r14...r21
+ * cache line 1: r6...r13
+ * cache line 0: 2 x frame, r0..r5
+ */
+ andi r0, r0, -64
+
+ /*
+ * Push the first four registers on the stack, so that we can set
+ * them to vector-unique values before we jump to the common code.
+ *
+ * Registers are pushed on the stack as a struct pt_regs,
+ * with the sp initially just above the struct, and when we're
+ * done, sp points to the base of the struct, minus
+ * C_ABI_SAVE_AREA_SIZE, so we can directly jal to C code.
+ *
+ * This routine saves just the first four registers, plus the
+ * stack context so we can do proper backtracing right away,
+ * and defers to handle_interrupt to save the rest.
+ * The backtracer needs pc, ex1, lr, sp, r52, and faultnum.
+ */
+ addli r0, r0, PTREGS_OFFSET_LR - (PTREGS_SIZE + KSTK_PTREGS_GAP)
+ wh64 r0 /* cache line 7 */
+ {
+ st r0, lr
+ addli r0, r0, PTREGS_OFFSET_SP - PTREGS_OFFSET_LR
+ }
+ {
+ st r0, sp
+ addli sp, r0, PTREGS_OFFSET_REG(52) - PTREGS_OFFSET_SP
+ }
+ wh64 sp /* cache line 6 */
+ {
+ st sp, r52
+ addli sp, sp, PTREGS_OFFSET_REG(1) - PTREGS_OFFSET_REG(52)
+ }
+ wh64 sp /* cache line 0 */
+ {
+ st sp, r1
+ addli sp, sp, PTREGS_OFFSET_REG(2) - PTREGS_OFFSET_REG(1)
+ }
+ {
+ st sp, r2
+ addli sp, sp, PTREGS_OFFSET_REG(3) - PTREGS_OFFSET_REG(2)
+ }
+ {
+ st sp, r3
+ addli sp, sp, PTREGS_OFFSET_PC - PTREGS_OFFSET_REG(3)
+ }
+ mfspr r0, SPR_EX_CONTEXT_K_0
+ .ifc \processing,handle_syscall
+ /*
+ * Bump the saved PC by one bundle so that when we return, we won't
+ * execute the same swint instruction again. We need to do this while
+ * we're in the critical section.
+ */
+ addi r0, r0, 8
+ .endif
+ {
+ st sp, r0
+ addli sp, sp, PTREGS_OFFSET_EX1 - PTREGS_OFFSET_PC
+ }
+ mfspr r0, SPR_EX_CONTEXT_K_1
+ {
+ st sp, r0
+ addi sp, sp, PTREGS_OFFSET_FAULTNUM - PTREGS_OFFSET_EX1
+ /*
+ * Use r0 for syscalls so it's a temporary; use r1 for interrupts
+ * so that it gets passed through unchanged to the handler routine.
+ * Note that the .if conditional confusingly spans bundles.
+ */
+ .ifc \processing,handle_syscall
+ movei r0, \vecnum
+ }
+ {
+ st sp, r0
+ .else
+ movei r1, \vecnum
+ }
+ {
+ st sp, r1
+ .endif
+ addli sp, sp, PTREGS_OFFSET_REG(0) - PTREGS_OFFSET_FAULTNUM
+ }
+ mfspr r0, SPR_SYSTEM_SAVE_K_1 /* Original r0 */
+ {
+ st sp, r0
+ addi sp, sp, -PTREGS_OFFSET_REG(0) - 8
+ }
+ {
+ st sp, zero /* write zero into "Next SP" frame pointer */
+ addi sp, sp, -8 /* leave SP pointing at bottom of frame */
+ }
+ .ifc \processing,handle_syscall
+ j handle_syscall
+ .else
+ /* Capture per-interrupt SPR context to registers. */
+ .ifc \c_routine, do_page_fault
+ mfspr r2, SPR_SYSTEM_SAVE_K_3 /* address of page fault */
+ mfspr r3, SPR_SYSTEM_SAVE_K_2 /* info about page fault */
+ .else
+ .ifc \vecnum, INT_ILL_TRANS
+ mfspr r2, ILL_TRANS_REASON
+ .else
+ .ifc \vecnum, INT_DOUBLE_FAULT
+ mfspr r2, SPR_SYSTEM_SAVE_K_2 /* double fault info from HV */
+ .else
+ .ifc \c_routine, do_trap
+ mfspr r2, GPV_REASON
+ .else
+ .ifc \c_routine, op_handle_perf_interrupt
+ mfspr r2, PERF_COUNT_STS
+#if CHIP_HAS_AUX_PERF_COUNTERS()
+ .else
+ .ifc \c_routine, op_handle_aux_perf_interrupt
+ mfspr r2, AUX_PERF_COUNT_STS
+ .endif
+#endif
+ .endif
+ .endif
+ .endif
+ .endif
+ .endif
+ /* Put function pointer in r0 */
+ moveli r0, hw2_last(\c_routine)
+ shl16insli r0, r0, hw1(\c_routine)
+ {
+ shl16insli r0, r0, hw0(\c_routine)
+ j \processing
+ }
+ .endif
+ ENDPROC(intvec_\vecname)
+
+#ifdef __COLLECT_LINKER_FEEDBACK__
+ .pushsection .text.intvec_feedback,"ax"
+ .org (\vecnum << 5)
+ FEEDBACK_ENTER_EXPLICIT(intvec_\vecname, .intrpt1, 1 << 8)
+ jrp lr
+ .popsection
+#endif
+
+ .endm
+
+
+ /*
+ * Save the rest of the registers that we didn't save in the actual
+ * vector itself. We can't use r0-r10 inclusive here.
+ */
+ .macro finish_interrupt_save, function
+
+ /* If it's a syscall, save a proper orig_r0, otherwise just zero. */
+ PTREGS_PTR(r52, PTREGS_OFFSET_ORIG_R0)
+ {
+ .ifc \function,handle_syscall
+ st r52, r0
+ .else
+ st r52, zero
+ .endif
+ PTREGS_PTR(r52, PTREGS_OFFSET_TP)
+ }
+ st r52, tp
+ {
+ mfspr tp, CMPEXCH_VALUE
+ PTREGS_PTR(r52, PTREGS_OFFSET_CMPEXCH)
+ }
+
+ /*
+ * For ordinary syscalls, we save neither caller- nor callee-
+ * save registers, since the syscall invoker doesn't expect the
+ * caller-saves to be saved, and the called kernel functions will
+ * take care of saving the callee-saves for us.
+ *
+ * For interrupts we save just the caller-save registers. Saving
+ * them is required (since the "caller" can't save them). Again,
+ * the called kernel functions will restore the callee-save
+ * registers for us appropriately.
+ *
+ * On return, we normally restore nothing special for syscalls,
+ * and just the caller-save registers for interrupts.
+ *
+ * However, there are some important caveats to all this:
+ *
+ * - We always save a few callee-save registers to give us
+ * some scratchpad registers to carry across function calls.
+ *
+ * - fork/vfork/etc require us to save all the callee-save
+ * registers, which we do in PTREGS_SYSCALL_ALL_REGS, below.
+ *
+ * - We always save r0..r5 and r10 for syscalls, since we need
+ * to reload them a bit later for the actual kernel call, and
+ * since we might need them for -ERESTARTNOINTR, etc.
+ *
+ * - Before invoking a signal handler, we save the unsaved
+ * callee-save registers so they are visible to the
+ * signal handler or any ptracer.
+ *
+ * - If the unsaved callee-save registers are modified, we set
+ * a bit in pt_regs so we know to reload them from pt_regs
+ * and not just rely on the kernel function unwinding.
+ * (Done for ptrace register writes and SA_SIGINFO handler.)
+ */
+ {
+ st r52, tp
+ PTREGS_PTR(r52, PTREGS_OFFSET_REG(33))
+ }
+ wh64 r52 /* cache line 4 */
+ push_reg r33, r52
+ push_reg r32, r52
+ push_reg r31, r52
+ .ifc \function,handle_syscall
+ push_reg r30, r52, PTREGS_OFFSET_SYSCALL - PTREGS_OFFSET_REG(30)
+ push_reg TREG_SYSCALL_NR_NAME, r52, \
+ PTREGS_OFFSET_REG(5) - PTREGS_OFFSET_SYSCALL
+ .else
+
+ push_reg r30, r52, PTREGS_OFFSET_REG(29) - PTREGS_OFFSET_REG(30)
+ wh64 r52 /* cache line 3 */
+ push_reg r29, r52
+ push_reg r28, r52
+ push_reg r27, r52
+ push_reg r26, r52
+ push_reg r25, r52
+ push_reg r24, r52
+ push_reg r23, r52
+ push_reg r22, r52
+ wh64 r52 /* cache line 2 */
+ push_reg r21, r52
+ push_reg r20, r52
+ push_reg r19, r52
+ push_reg r18, r52
+ push_reg r17, r52
+ push_reg r16, r52
+ push_reg r15, r52
+ push_reg r14, r52
+ wh64 r52 /* cache line 1 */
+ push_reg r13, r52
+ push_reg r12, r52
+ push_reg r11, r52
+ push_reg r10, r52
+ push_reg r9, r52
+ push_reg r8, r52
+ push_reg r7, r52
+ push_reg r6, r52
+
+ .endif
+
+ push_reg r5, r52
+ st r52, r4
+
+ /* Load tp with our per-cpu offset. */
+#ifdef CONFIG_SMP
+ {
+ mfspr r20, SPR_SYSTEM_SAVE_K_0
+ moveli r21, hw2_last(__per_cpu_offset)
+ }
+ {
+ shl16insli r21, r21, hw1(__per_cpu_offset)
+ bfextu r20, r20, 0, LOG2_THREAD_SIZE-1
+ }
+ shl16insli r21, r21, hw0(__per_cpu_offset)
+ shl3add r20, r20, r21
+ ld tp, r20
+#else
+ move tp, zero
+#endif
+
+ /*
+ * If we will be returning to the kernel, we will need to
+ * reset the interrupt masks to the state they had before.
+ * Set DISABLE_IRQ in flags iff we came from PL1 with irqs disabled.
+ */
+ mfspr r32, SPR_EX_CONTEXT_K_1
+ {
+ andi r32, r32, SPR_EX_CONTEXT_1_1__PL_MASK /* mask off ICS */
+ PTREGS_PTR(r21, PTREGS_OFFSET_FLAGS)
+ }
+ beqzt r32, 1f /* zero if from user space */
+ IRQS_DISABLED(r32) /* zero if irqs enabled */
+#if PT_FLAGS_DISABLE_IRQ != 1
+# error Value of IRQS_DISABLED used to set PT_FLAGS_DISABLE_IRQ; fix
+#endif
+1:
+ .ifnc \function,handle_syscall
+ /* Record the fact that we saved the caller-save registers above. */
+ ori r32, r32, PT_FLAGS_CALLER_SAVES
+ .endif
+ st r21, r32
+
+#ifdef __COLLECT_LINKER_FEEDBACK__
+ /*
+ * Notify the feedback routines that we were in the
+ * appropriate fixed interrupt vector area. Note that we
+ * still have ICS set at this point, so we can't invoke any
+ * atomic operations or we will panic. The feedback
+ * routines internally preserve r0..r10 and r30 up.
+ */
+ .ifnc \function,handle_syscall
+ shli r20, r1, 5
+ .else
+ moveli r20, INT_SWINT_1 << 5
+ .endif
+ moveli r21, hw2_last(intvec_feedback)
+ shl16insli r21, r21, hw1(intvec_feedback)
+ shl16insli r21, r21, hw0(intvec_feedback)
+ add r20, r20, r21
+ jalr r20
+
+ /* And now notify the feedback routines that we are here. */
+ FEEDBACK_ENTER(\function)
+#endif
+
+ /*
+ * we've captured enough state to the stack (including in
+ * particular our EX_CONTEXT state) that we can now release
+ * the interrupt critical section and replace it with our
+ * standard "interrupts disabled" mask value. This allows
+ * synchronous interrupts (and profile interrupts) to punch
+ * through from this point onwards.
+ */
+ .ifc \function,handle_nmi
+ IRQ_DISABLE_ALL(r20)
+ .else
+ IRQ_DISABLE(r20, r21)
+ .endif
+ mtspr INTERRUPT_CRITICAL_SECTION, zero
+
+ /*
+ * Prepare the first 256 stack bytes to be rapidly accessible
+ * without having to fetch the background data.
+ */
+ addi r52, sp, -64
+ {
+ wh64 r52
+ addi r52, r52, -64
+ }
+ {
+ wh64 r52
+ addi r52, r52, -64
+ }
+ {
+ wh64 r52
+ addi r52, r52, -64
+ }
+ wh64 r52
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ .ifnc \function,handle_nmi
+ /*
+ * We finally have enough state set up to notify the irq
+ * tracing code that irqs were disabled on entry to the handler.
+ * The TRACE_IRQS_OFF call clobbers registers r0-r29.
+ * For syscalls, we already have the register state saved away
+ * on the stack, so we don't bother to do any register saves here,
+ * and later we pop the registers back off the kernel stack.
+ * For interrupt handlers, save r0-r3 in callee-saved registers.
+ */
+ .ifnc \function,handle_syscall
+ { move r30, r0; move r31, r1 }
+ { move r32, r2; move r33, r3 }
+ .endif
+ TRACE_IRQS_OFF
+ .ifnc \function,handle_syscall
+ { move r0, r30; move r1, r31 }
+ { move r2, r32; move r3, r33 }
+ .endif
+ .endif
+#endif
+
+ .endm
+
+ /*
+ * Redispatch a downcall.
+ */
+ .macro dc_dispatch vecnum, vecname
+ .org (\vecnum << 8)
+intvec_\vecname:
+ j hv_downcall_dispatch
+ ENDPROC(intvec_\vecname)
+ .endm
+
+ /*
+ * Common code for most interrupts. The C function we're eventually
+ * going to is in r0, and the faultnum is in r1; the original
+ * values for those registers are on the stack.
+ */
+ .pushsection .text.handle_interrupt,"ax"
+handle_interrupt:
+ finish_interrupt_save handle_interrupt
+
+ /* Jump to the C routine; it should enable irqs as soon as possible. */
+ {
+ jalr r0
+ PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
+ }
+ FEEDBACK_REENTER(handle_interrupt)
+ {
+ movei r30, 0 /* not an NMI */
+ j interrupt_return
+ }
+ STD_ENDPROC(handle_interrupt)
+
+/*
+ * This routine takes a boolean in r30 indicating if this is an NMI.
+ * If so, we also expect a boolean in r31 indicating whether to
+ * re-enable the oprofile interrupts.
+ */
+STD_ENTRY(interrupt_return)
+ /* If we're resuming to kernel space, don't check thread flags. */
+ {
+ bnez r30, .Lrestore_all /* NMIs don't special-case user-space */
+ PTREGS_PTR(r29, PTREGS_OFFSET_EX1)
+ }
+ ld r29, r29
+ andi r29, r29, SPR_EX_CONTEXT_1_1__PL_MASK /* mask off ICS */
+ {
+ beqzt r29, .Lresume_userspace
+ PTREGS_PTR(r29, PTREGS_OFFSET_PC)
+ }
+
+ /* If we're resuming to _cpu_idle_nap, bump PC forward by 8. */
+ moveli r27, hw2_last(_cpu_idle_nap)
+ {
+ ld r28, r29
+ shl16insli r27, r27, hw1(_cpu_idle_nap)
+ }
+ {
+ shl16insli r27, r27, hw0(_cpu_idle_nap)
+ }
+ {
+ cmpeq r27, r27, r28
+ }
+ {
+ blbc r27, .Lrestore_all
+ addi r28, r28, 8
+ }
+ st r29, r28
+ j .Lrestore_all
+
+.Lresume_userspace:
+ FEEDBACK_REENTER(interrupt_return)
+
+ /*
+ * Disable interrupts so as to make sure we don't
+ * miss an interrupt that sets any of the thread flags (like
+ * need_resched or sigpending) between sampling and the iret.
+ * Routines like schedule() or do_signal() may re-enable
+ * interrupts before returning.
+ */
+ IRQ_DISABLE(r20, r21)
+ TRACE_IRQS_OFF /* Note: clobbers registers r0-r29 */
+
+ /* Get base of stack in r32; note r30/31 are used as arguments here. */
+ GET_THREAD_INFO(r32)
+
+
+ /* Check to see if there is any work to do before returning to user. */
+ {
+ addi r29, r32, THREAD_INFO_FLAGS_OFFSET
+ moveli r1, hw1_last(_TIF_ALLWORK_MASK)
+ }
+ {
+ ld r29, r29
+ shl16insli r1, r1, hw0(_TIF_ALLWORK_MASK)
+ }
+ and r1, r29, r1
+ beqzt r1, .Lrestore_all
+
+ /*
+ * Make sure we have all the registers saved for signal
+ * handling or single-step. Call out to C code to figure out
+ * exactly what we need to do for each flag bit, then if
+ * necessary, reload the flags and recheck.
+ */
+ push_extra_callee_saves r0
+ {
+ PTREGS_PTR(r0, PTREGS_OFFSET_BASE)
+ jal do_work_pending
+ }
+ bnez r0, .Lresume_userspace
+
+ /*
+ * In the NMI case we
+ * omit the call to single_process_check_nohz, which normally checks
+ * to see if we should start or stop the scheduler tick, because
+ * we can't call arbitrary Linux code from an NMI context.
+ * We always call the homecache TLB deferral code to re-trigger
+ * the deferral mechanism.
+ *
+ * The other chunk of responsibility this code has is to reset the
+ * interrupt masks appropriately to reset irqs and NMIs. We have
+ * to call TRACE_IRQS_OFF and TRACE_IRQS_ON to support all the
+ * lockdep-type stuff, but we can't set ICS until afterwards, since
+ * ICS can only be used in very tight chunks of code to avoid
+ * tripping over various assertions that it is off.
+ */
+.Lrestore_all:
+ PTREGS_PTR(r0, PTREGS_OFFSET_EX1)
+ {
+ ld r0, r0
+ PTREGS_PTR(r32, PTREGS_OFFSET_FLAGS)
+ }
+ {
+ andi r0, r0, SPR_EX_CONTEXT_1_1__PL_MASK
+ ld r32, r32
+ }
+ bnez r0, 1f
+ j 2f
+#if PT_FLAGS_DISABLE_IRQ != 1
+# error Assuming PT_FLAGS_DISABLE_IRQ == 1 so we can use blbct below
+#endif
+1: blbct r32, 2f
+ IRQ_DISABLE(r20,r21)
+ TRACE_IRQS_OFF
+ movei r0, 1
+ mtspr INTERRUPT_CRITICAL_SECTION, r0
+ beqzt r30, .Lrestore_regs
+ j 3f
+2: TRACE_IRQS_ON
+ movei r0, 1
+ mtspr INTERRUPT_CRITICAL_SECTION, r0
+ IRQ_ENABLE(r20, r21)
+ beqzt r30, .Lrestore_regs
+3:
+
+
+ /*
+ * We now commit to returning from this interrupt, since we will be
+ * doing things like setting EX_CONTEXT SPRs and unwinding the stack
+ * frame. No calls should be made to any other code after this point.
+ * This code should only be entered with ICS set.
+ * r32 must still be set to ptregs.flags.
+ * We launch loads to each cache line separately first, so we can
+ * get some parallelism out of the memory subsystem.
+ * We start zeroing caller-saved registers throughout, since
+ * that will save some cycles if this turns out to be a syscall.
+ */
+.Lrestore_regs:
+ FEEDBACK_REENTER(interrupt_return) /* called from elsewhere */
+
+ /*
+ * Rotate so we have one high bit and one low bit to test.
+ * - low bit says whether to restore all the callee-saved registers,
+ * or just r30-r33, and r52 up.
+ * - high bit (i.e. sign bit) says whether to restore all the
+ * caller-saved registers, or just r0.
+ */
+#if PT_FLAGS_CALLER_SAVES != 2 || PT_FLAGS_RESTORE_REGS != 4
+# error Rotate trick does not work :-)
+#endif