/*1* Copyright 2011 Tilera Corporation. All Rights Reserved.2*3* This program is free software; you can redistribute it and/or4* modify it under the terms of the GNU General Public License5* as published by the Free Software Foundation, version 2.6*7* This program is distributed in the hope that it will be useful, but8* WITHOUT ANY WARRANTY; without even the implied warranty of9* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or10* NON INFRINGEMENT. See the GNU General Public License for11* more details.12*/1314#include <linux/linkage.h>15#include <asm/system.h>16#include <asm/ptrace.h>17#include <asm/asm-offsets.h>18#include <arch/spr_def.h>19#include <asm/processor.h>2021/*22* See <asm/system.h>; called with prev and next task_struct pointers.23* "prev" is returned in r0 for _switch_to and also for ret_from_fork.24*25* We want to save pc/sp in "prev", and get the new pc/sp from "next".26* We also need to save all the callee-saved registers on the stack.27*28* Intel enables/disables access to the hardware cycle counter in29* seccomp (secure computing) environments if necessary, based on30* has_secure_computing(). We might want to do this at some point,31* though it would require virtualizing the other SPRs under WORLD_ACCESS.32*33* Since we're saving to the stack, we omit sp from this list.34* And for parallels with other architectures, we save lr separately,35* in the thread_struct itself (as the "pc" field).36*37* This code also needs to be aligned with process.c copy_thread()38*/3940#if CALLEE_SAVED_REGS_COUNT != 2441# error Mismatch between <asm/system.h> and kernel/entry.S42#endif43#define FRAME_SIZE ((2 + CALLEE_SAVED_REGS_COUNT) * 8)4445#define SAVE_REG(r) { st r12, r; addi r12, r12, 8 }46#define LOAD_REG(r) { ld r, r12; addi r12, r12, 8 }47#define FOR_EACH_CALLEE_SAVED_REG(f) \48f(r30); f(r31); \49f(r32); f(r33); f(r34); f(r35); f(r36); f(r37); f(r38); f(r39); \50f(r40); f(r41); f(r42); f(r43); f(r44); f(r45); f(r46); f(r47); \51f(r48); f(r49); f(r50); f(r51); f(r52);5253STD_ENTRY_SECTION(__switch_to, .sched.text)54{55move r10, sp56st sp, lr57}58{59addli r11, sp, -FRAME_SIZE + 860addli sp, sp, -FRAME_SIZE61}62{63st r11, r1064addli r4, r1, TASK_STRUCT_THREAD_KSP_OFFSET65}66{67ld r13, r4 /* Load new sp to a temp register early. */68addi r12, sp, 1669}70FOR_EACH_CALLEE_SAVED_REG(SAVE_REG)71addli r3, r0, TASK_STRUCT_THREAD_KSP_OFFSET72{73st r3, sp74addli r3, r0, TASK_STRUCT_THREAD_PC_OFFSET75}76{77st r3, lr78addli r4, r1, TASK_STRUCT_THREAD_PC_OFFSET79}80{81ld lr, r482addi r12, r13, 1683}84{85/* Update sp and ksp0 simultaneously to avoid backtracer warnings. */86move sp, r1387mtspr SPR_SYSTEM_SAVE_K_0, r288}89FOR_EACH_CALLEE_SAVED_REG(LOAD_REG)90.L__switch_to_pc:91{92addli sp, sp, FRAME_SIZE93jrp lr /* r0 is still valid here, so return it */94}95STD_ENDPROC(__switch_to)9697/* Return a suitable address for the backtracer for suspended threads */98STD_ENTRY_SECTION(get_switch_to_pc, .sched.text)99lnk r0100{101addli r0, r0, .L__switch_to_pc - .102jrp lr103}104STD_ENDPROC(get_switch_to_pc)105106STD_ENTRY(get_pt_regs)107.irp reg, r0, r1, r2, r3, r4, r5, r6, r7, \108r8, r9, r10, r11, r12, r13, r14, r15, \109r16, r17, r18, r19, r20, r21, r22, r23, \110r24, r25, r26, r27, r28, r29, r30, r31, \111r32, r33, r34, r35, r36, r37, r38, r39, \112r40, r41, r42, r43, r44, r45, r46, r47, \113r48, r49, r50, r51, r52, tp, sp114{115st r0, \reg116addi r0, r0, 8117}118.endr119{120st r0, lr121addi r0, r0, PTREGS_OFFSET_PC - PTREGS_OFFSET_LR122}123lnk r1124{125st r0, r1126addi r0, r0, PTREGS_OFFSET_EX1 - PTREGS_OFFSET_PC127}128mfspr r1, INTERRUPT_CRITICAL_SECTION129shli r1, r1, SPR_EX_CONTEXT_1_1__ICS_SHIFT130ori r1, r1, KERNEL_PL131{132st r0, r1133addi r0, r0, PTREGS_OFFSET_FAULTNUM - PTREGS_OFFSET_EX1134}135{136st r0, zero /* clear faultnum */137addi r0, r0, PTREGS_OFFSET_ORIG_R0 - PTREGS_OFFSET_FAULTNUM138}139{140st r0, zero /* clear orig_r0 */141addli r0, r0, -PTREGS_OFFSET_ORIG_R0 /* restore r0 to base */142}143jrp lr144STD_ENDPROC(get_pt_regs)145146147