/*1* Low-level system-call handling, trap handlers and context-switching2*3* Copyright (C) 2008-2009 Michal Simek <[email protected]>4* Copyright (C) 2008-2009 PetaLogix5* Copyright (C) 2003 John Williams <[email protected]>6* Copyright (C) 2001,2002 NEC Corporation7* Copyright (C) 2001,2002 Miles Bader <[email protected]>8*9* This file is subject to the terms and conditions of the GNU General10* Public License. See the file COPYING in the main directory of this11* archive for more details.12*13* Written by Miles Bader <[email protected]>14* Heavily modified by John Williams for Microblaze15*/1617#include <linux/sys.h>18#include <linux/linkage.h>1920#include <asm/entry.h>21#include <asm/current.h>22#include <asm/processor.h>23#include <asm/exceptions.h>24#include <asm/asm-offsets.h>25#include <asm/thread_info.h>2627#include <asm/page.h>28#include <asm/unistd.h>2930#include <linux/errno.h>31#include <asm/signal.h>3233#undef DEBUG3435#ifdef DEBUG36/* Create space for syscalls counting. */37.section .data38.global syscall_debug_table39.align 440syscall_debug_table:41.space (__NR_syscalls * 4)42#endif /* DEBUG */4344#define C_ENTRY(name) .globl name; .align 4; name4546/*47* Various ways of setting and clearing BIP in flags reg.48* This is mucky, but necessary using microblaze version that49* allows msr ops to write to BIP50*/51#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR52.macro clear_bip53msrclr r0, MSR_BIP54.endm5556.macro set_bip57msrset r0, MSR_BIP58.endm5960.macro clear_eip61msrclr r0, MSR_EIP62.endm6364.macro set_ee65msrset r0, MSR_EE66.endm6768.macro disable_irq69msrclr r0, MSR_IE70.endm7172.macro enable_irq73msrset r0, MSR_IE74.endm7576.macro set_ums77msrset r0, MSR_UMS78msrclr r0, MSR_VMS79.endm8081.macro set_vms82msrclr r0, MSR_UMS83msrset r0, MSR_VMS84.endm8586.macro clear_ums87msrclr r0, MSR_UMS88.endm8990.macro clear_vms_ums91msrclr r0, MSR_VMS | MSR_UMS92.endm93#else94.macro clear_bip95mfs r11, rmsr96andi r11, r11, ~MSR_BIP97mts rmsr, r1198.endm99100.macro set_bip101mfs r11, rmsr102ori r11, r11, MSR_BIP103mts rmsr, r11104.endm105106.macro clear_eip107mfs r11, rmsr108andi r11, r11, ~MSR_EIP109mts rmsr, r11110.endm111112.macro set_ee113mfs r11, rmsr114ori r11, r11, MSR_EE115mts rmsr, r11116.endm117118.macro disable_irq119mfs r11, rmsr120andi r11, r11, ~MSR_IE121mts rmsr, r11122.endm123124.macro enable_irq125mfs r11, rmsr126ori r11, r11, MSR_IE127mts rmsr, r11128.endm129130.macro set_ums131mfs r11, rmsr132ori r11, r11, MSR_VMS133andni r11, r11, MSR_UMS134mts rmsr, r11135.endm136137.macro set_vms138mfs r11, rmsr139ori r11, r11, MSR_VMS140andni r11, r11, MSR_UMS141mts rmsr, r11142.endm143144.macro clear_ums145mfs r11, rmsr146andni r11, r11, MSR_UMS147mts rmsr,r11148.endm149150.macro clear_vms_ums151mfs r11, rmsr152andni r11, r11, (MSR_VMS|MSR_UMS)153mts rmsr,r11154.endm155#endif156157/* Define how to call high-level functions. With MMU, virtual mode must be158* enabled when calling the high-level function. Clobbers R11.159* VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL160*/161162/* turn on virtual protected mode save */163#define VM_ON \164set_ums; \165rted r0, 2f; \166nop; \1672:168169/* turn off virtual protected mode save and user mode save*/170#define VM_OFF \171clear_vms_ums; \172rted r0, TOPHYS(1f); \173nop; \1741:175176#define SAVE_REGS \177swi r2, r1, PT_R2; /* Save SDA */ \178swi r3, r1, PT_R3; \179swi r4, r1, PT_R4; \180swi r5, r1, PT_R5; \181swi r6, r1, PT_R6; \182swi r7, r1, PT_R7; \183swi r8, r1, PT_R8; \184swi r9, r1, PT_R9; \185swi r10, r1, PT_R10; \186swi r11, r1, PT_R11; /* save clobbered regs after rval */\187swi r12, r1, PT_R12; \188swi r13, r1, PT_R13; /* Save SDA2 */ \189swi r14, r1, PT_PC; /* PC, before IRQ/trap */ \190swi r15, r1, PT_R15; /* Save LP */ \191swi r16, r1, PT_R16; \192swi r17, r1, PT_R17; \193swi r18, r1, PT_R18; /* Save asm scratch reg */ \194swi r19, r1, PT_R19; \195swi r20, r1, PT_R20; \196swi r21, r1, PT_R21; \197swi r22, r1, PT_R22; \198swi r23, r1, PT_R23; \199swi r24, r1, PT_R24; \200swi r25, r1, PT_R25; \201swi r26, r1, PT_R26; \202swi r27, r1, PT_R27; \203swi r28, r1, PT_R28; \204swi r29, r1, PT_R29; \205swi r30, r1, PT_R30; \206swi r31, r1, PT_R31; /* Save current task reg */ \207mfs r11, rmsr; /* save MSR */ \208swi r11, r1, PT_MSR;209210#define RESTORE_REGS \211lwi r11, r1, PT_MSR; \212mts rmsr , r11; \213lwi r2, r1, PT_R2; /* restore SDA */ \214lwi r3, r1, PT_R3; \215lwi r4, r1, PT_R4; \216lwi r5, r1, PT_R5; \217lwi r6, r1, PT_R6; \218lwi r7, r1, PT_R7; \219lwi r8, r1, PT_R8; \220lwi r9, r1, PT_R9; \221lwi r10, r1, PT_R10; \222lwi r11, r1, PT_R11; /* restore clobbered regs after rval */\223lwi r12, r1, PT_R12; \224lwi r13, r1, PT_R13; /* restore SDA2 */ \225lwi r14, r1, PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\226lwi r15, r1, PT_R15; /* restore LP */ \227lwi r16, r1, PT_R16; \228lwi r17, r1, PT_R17; \229lwi r18, r1, PT_R18; /* restore asm scratch reg */ \230lwi r19, r1, PT_R19; \231lwi r20, r1, PT_R20; \232lwi r21, r1, PT_R21; \233lwi r22, r1, PT_R22; \234lwi r23, r1, PT_R23; \235lwi r24, r1, PT_R24; \236lwi r25, r1, PT_R25; \237lwi r26, r1, PT_R26; \238lwi r27, r1, PT_R27; \239lwi r28, r1, PT_R28; \240lwi r29, r1, PT_R29; \241lwi r30, r1, PT_R30; \242lwi r31, r1, PT_R31; /* Restore cur task reg */243244#define SAVE_STATE \245swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \246/* See if already in kernel mode.*/ \247mfs r1, rmsr; \248andi r1, r1, MSR_UMS; \249bnei r1, 1f; \250/* Kernel-mode state save. */ \251/* Reload kernel stack-ptr. */ \252lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \253/* FIXME: I can add these two lines to one */ \254/* tophys(r1,r1); */ \255/* addik r1, r1, -PT_SIZE; */ \256addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \257SAVE_REGS \258brid 2f; \259swi r1, r1, PT_MODE; \2601: /* User-mode state save. */ \261lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\262tophys(r1,r1); \263lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \264/* MS these three instructions can be added to one */ \265/* addik r1, r1, THREAD_SIZE; */ \266/* tophys(r1,r1); */ \267/* addik r1, r1, -PT_SIZE; */ \268addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \269SAVE_REGS \270lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \271swi r11, r1, PT_R1; /* Store user SP. */ \272swi r0, r1, PT_MODE; /* Was in user-mode. */ \273/* MS: I am clearing UMS even in case when I come from kernel space */ \274clear_ums; \2752: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));276277.text278279/*280* User trap.281*282* System calls are handled here.283*284* Syscall protocol:285* Syscall number in r12, args in r5-r10286* Return value in r3287*288* Trap entered via brki instruction, so BIP bit is set, and interrupts289* are masked. This is nice, means we don't have to CLI before state save290*/291C_ENTRY(_user_exception):292swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */293addi r14, r14, 4 /* return address is 4 byte after call */294295mfs r1, rmsr296nop297andi r1, r1, MSR_UMS298bnei r1, 1f299300/* Kernel-mode state save - kernel execve */301lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/302tophys(r1,r1);303304addik r1, r1, -PT_SIZE; /* Make room on the stack. */305SAVE_REGS306307swi r1, r1, PT_MODE; /* pt_regs -> kernel mode */308brid 2f;309nop; /* Fill delay slot */310311/* User-mode state save. */3121:313lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */314tophys(r1,r1);315lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */316/* calculate kernel stack pointer from task struct 8k */317addik r1, r1, THREAD_SIZE;318tophys(r1,r1);319320addik r1, r1, -PT_SIZE; /* Make room on the stack. */321SAVE_REGS322swi r0, r1, PT_R3323swi r0, r1, PT_R4324325swi r0, r1, PT_MODE; /* Was in user-mode. */326lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));327swi r11, r1, PT_R1; /* Store user SP. */328clear_ums;3292: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));330/* Save away the syscall number. */331swi r12, r1, PT_R0;332tovirt(r1,r1)333334/* where the trap should return need -8 to adjust for rtsd r15, 8*/335/* Jump to the appropriate function for the system call number in r12336* (r12 is not preserved), or return an error if r12 is not valid. The LP337* register should point to the location where338* the called function should return. [note that MAKE_SYS_CALL uses label 1] */339340/* Step into virtual mode */341rtbd r0, 3f342nop3433:344lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */345lwi r11, r11, TI_FLAGS /* get flags in thread info */346andi r11, r11, _TIF_WORK_SYSCALL_MASK347beqi r11, 4f348349addik r3, r0, -ENOSYS350swi r3, r1, PT_R3351brlid r15, do_syscall_trace_enter352addik r5, r1, PT_R0353354# do_syscall_trace_enter returns the new syscall nr.355addk r12, r0, r3356lwi r5, r1, PT_R5;357lwi r6, r1, PT_R6;358lwi r7, r1, PT_R7;359lwi r8, r1, PT_R8;360lwi r9, r1, PT_R9;361lwi r10, r1, PT_R10;3624:363/* Jump to the appropriate function for the system call number in r12364* (r12 is not preserved), or return an error if r12 is not valid.365* The LP register should point to the location where the called function366* should return. [note that MAKE_SYS_CALL uses label 1] */367/* See if the system call number is valid */368addi r11, r12, -__NR_syscalls;369bgei r11,5f;370/* Figure out which function to use for this system call. */371/* Note Microblaze barrel shift is optional, so don't rely on it */372add r12, r12, r12; /* convert num -> ptr */373add r12, r12, r12;374375#ifdef DEBUG376/* Trac syscalls and stored them to syscall_debug_table */377/* The first syscall location stores total syscall number */378lwi r3, r0, syscall_debug_table379addi r3, r3, 1380swi r3, r0, syscall_debug_table381lwi r3, r12, syscall_debug_table382addi r3, r3, 1383swi r3, r12, syscall_debug_table384#endif385386# Find and jump into the syscall handler.387lwi r12, r12, sys_call_table388/* where the trap should return need -8 to adjust for rtsd r15, 8 */389addi r15, r0, ret_from_trap-8390bra r12391392/* The syscall number is invalid, return an error. */3935:394rtsd r15, 8; /* looks like a normal subroutine return */395addi r3, r0, -ENOSYS;396397/* Entry point used to return from a syscall/trap */398/* We re-enable BIP bit before state restore */399C_ENTRY(ret_from_trap):400swi r3, r1, PT_R3401swi r4, r1, PT_R4402403lwi r11, r1, PT_MODE;404/* See if returning to kernel mode, if so, skip resched &c. */405bnei r11, 2f;406/* We're returning to user mode, so check for various conditions that407* trigger rescheduling. */408/* FIXME: Restructure all these flag checks. */409lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */410lwi r11, r11, TI_FLAGS; /* get flags in thread info */411andi r11, r11, _TIF_WORK_SYSCALL_MASK412beqi r11, 1f413414brlid r15, do_syscall_trace_leave415addik r5, r1, PT_R04161:417/* We're returning to user mode, so check for various conditions that418* trigger rescheduling. */419/* get thread info from current task */420lwi r11, CURRENT_TASK, TS_THREAD_INFO;421lwi r11, r11, TI_FLAGS; /* get flags in thread info */422andi r11, r11, _TIF_NEED_RESCHED;423beqi r11, 5f;424425bralid r15, schedule; /* Call scheduler */426nop; /* delay slot */427428/* Maybe handle a signal */4295: /* get thread info from current task*/430lwi r11, CURRENT_TASK, TS_THREAD_INFO;431lwi r11, r11, TI_FLAGS; /* get flags in thread info */432andi r11, r11, _TIF_SIGPENDING;433beqi r11, 1f; /* Signals to handle, handle them */434435addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */436addi r7, r0, 1; /* Arg 3: int in_syscall */437bralid r15, do_signal; /* Handle any signals */438add r6, r0, r0; /* Arg 2: sigset_t *oldset */439440/* Finally, return to user state. */4411: set_bip; /* Ints masked for state restore */442swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */443VM_OFF;444tophys(r1,r1);445RESTORE_REGS;446addik r1, r1, PT_SIZE /* Clean up stack space. */447lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */448bri 6f;449450/* Return to kernel state. */4512: set_bip; /* Ints masked for state restore */452VM_OFF;453tophys(r1,r1);454RESTORE_REGS;455addik r1, r1, PT_SIZE /* Clean up stack space. */456tovirt(r1,r1);4576:458TRAP_return: /* Make global symbol for debugging */459rtbd r14, 0; /* Instructions to return from an IRQ */460nop;461462463/* These syscalls need access to the struct pt_regs on the stack, so we464implement them in assembly (they're basically all wrappers anyway). */465466C_ENTRY(sys_fork_wrapper):467addi r5, r0, SIGCHLD /* Arg 0: flags */468lwi r6, r1, PT_R1 /* Arg 1: child SP (use parent's) */469addik r7, r1, 0 /* Arg 2: parent context */470add r8. r0, r0 /* Arg 3: (unused) */471add r9, r0, r0; /* Arg 4: (unused) */472brid do_fork /* Do real work (tail-call) */473add r10, r0, r0; /* Arg 5: (unused) */474475/* This the initial entry point for a new child thread, with an appropriate476stack in place that makes it look the the child is in the middle of an477syscall. This function is actually `returned to' from switch_thread478(copy_thread makes ret_from_fork the return address in each new thread's479saved context). */480C_ENTRY(ret_from_fork):481bralid r15, schedule_tail; /* ...which is schedule_tail's arg */482add r3, r5, r0; /* switch_thread returns the prev task */483/* ( in the delay slot ) */484brid ret_from_trap; /* Do normal trap return */485add r3, r0, r0; /* Child's fork call should return 0. */486487C_ENTRY(sys_vfork):488brid microblaze_vfork /* Do real work (tail-call) */489addik r5, r1, 0490491C_ENTRY(sys_clone):492bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */493lwi r6, r1, PT_R1; /* If so, use paret's stack ptr */4941: addik r7, r1, 0; /* Arg 2: parent context */495add r8, r0, r0; /* Arg 3: (unused) */496add r9, r0, r0; /* Arg 4: (unused) */497brid do_fork /* Do real work (tail-call) */498add r10, r0, r0; /* Arg 5: (unused) */499500C_ENTRY(sys_execve):501brid microblaze_execve; /* Do real work (tail-call).*/502addik r8, r1, 0; /* add user context as 4th arg */503504C_ENTRY(sys_rt_sigreturn_wrapper):505brid sys_rt_sigreturn /* Do real work */506addik r5, r1, 0; /* add user context as 1st arg */507508/*509* HW EXCEPTION rutine start510*/511C_ENTRY(full_exception_trap):512/* adjust exception address for privileged instruction513* for finding where is it */514addik r17, r17, -4515SAVE_STATE /* Save registers */516/* PC, before IRQ/trap - this is one instruction above */517swi r17, r1, PT_PC;518tovirt(r1,r1)519/* FIXME this can be store directly in PT_ESR reg.520* I tested it but there is a fault */521/* where the trap should return need -8 to adjust for rtsd r15, 8 */522addik r15, r0, ret_from_exc - 8523mfs r6, resr524mfs r7, rfsr; /* save FSR */525mts rfsr, r0; /* Clear sticky fsr */526rted r0, full_exception527addik r5, r1, 0 /* parameter struct pt_regs * regs */528529/*530* Unaligned data trap.531*532* Unaligned data trap last on 4k page is handled here.533*534* Trap entered via exception, so EE bit is set, and interrupts535* are masked. This is nice, means we don't have to CLI before state save536*537* The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S"538*/539C_ENTRY(unaligned_data_trap):540/* MS: I have to save r11 value and then restore it because541* set_bit, clear_eip, set_ee use r11 as temp register if MSR542* instructions are not used. We don't need to do if MSR instructions543* are used and they use r0 instead of r11.544* I am using ENTRY_SP which should be primary used only for stack545* pointer saving. */546swi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));547set_bip; /* equalize initial state for all possible entries */548clear_eip;549set_ee;550lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));551SAVE_STATE /* Save registers.*/552/* PC, before IRQ/trap - this is one instruction above */553swi r17, r1, PT_PC;554tovirt(r1,r1)555/* where the trap should return need -8 to adjust for rtsd r15, 8 */556addik r15, r0, ret_from_exc-8557mfs r3, resr /* ESR */558mfs r4, rear /* EAR */559rtbd r0, _unaligned_data_exception560addik r7, r1, 0 /* parameter struct pt_regs * regs */561562/*563* Page fault traps.564*565* If the real exception handler (from hw_exception_handler.S) didn't find566* the mapping for the process, then we're thrown here to handle such situation.567*568* Trap entered via exceptions, so EE bit is set, and interrupts569* are masked. This is nice, means we don't have to CLI before state save570*571* Build a standard exception frame for TLB Access errors. All TLB exceptions572* will bail out to this point if they can't resolve the lightweight TLB fault.573*574* The C function called is in "arch/microblaze/mm/fault.c", declared as:575* void do_page_fault(struct pt_regs *regs,576* unsigned long address,577* unsigned long error_code)578*/579/* data and intruction trap - which is choose is resolved int fault.c */580C_ENTRY(page_fault_data_trap):581SAVE_STATE /* Save registers.*/582/* PC, before IRQ/trap - this is one instruction above */583swi r17, r1, PT_PC;584tovirt(r1,r1)585/* where the trap should return need -8 to adjust for rtsd r15, 8 */586addik r15, r0, ret_from_exc-8587mfs r6, rear /* parameter unsigned long address */588mfs r7, resr /* parameter unsigned long error_code */589rted r0, do_page_fault590addik r5, r1, 0 /* parameter struct pt_regs * regs */591592C_ENTRY(page_fault_instr_trap):593SAVE_STATE /* Save registers.*/594/* PC, before IRQ/trap - this is one instruction above */595swi r17, r1, PT_PC;596tovirt(r1,r1)597/* where the trap should return need -8 to adjust for rtsd r15, 8 */598addik r15, r0, ret_from_exc-8599mfs r6, rear /* parameter unsigned long address */600ori r7, r0, 0 /* parameter unsigned long error_code */601rted r0, do_page_fault602addik r5, r1, 0 /* parameter struct pt_regs * regs */603604/* Entry point used to return from an exception. */605C_ENTRY(ret_from_exc):606lwi r11, r1, PT_MODE;607bnei r11, 2f; /* See if returning to kernel mode, */608/* ... if so, skip resched &c. */609610/* We're returning to user mode, so check for various conditions that611trigger rescheduling. */612lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */613lwi r11, r11, TI_FLAGS; /* get flags in thread info */614andi r11, r11, _TIF_NEED_RESCHED;615beqi r11, 5f;616617/* Call the scheduler before returning from a syscall/trap. */618bralid r15, schedule; /* Call scheduler */619nop; /* delay slot */620621/* Maybe handle a signal */6225: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */623lwi r11, r11, TI_FLAGS; /* get flags in thread info */624andi r11, r11, _TIF_SIGPENDING;625beqi r11, 1f; /* Signals to handle, handle them */626627/*628* Handle a signal return; Pending signals should be in r18.629*630* Not all registers are saved by the normal trap/interrupt entry631* points (for instance, call-saved registers (because the normal632* C-compiler calling sequence in the kernel makes sure they're633* preserved), and call-clobbered registers in the case of634* traps), but signal handlers may want to examine or change the635* complete register state. Here we save anything not saved by636* the normal entry sequence, so that it may be safely restored637* (in a possibly modified form) after do_signal returns. */638addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */639addi r7, r0, 0; /* Arg 3: int in_syscall */640bralid r15, do_signal; /* Handle any signals */641add r6, r0, r0; /* Arg 2: sigset_t *oldset */642643/* Finally, return to user state. */6441: set_bip; /* Ints masked for state restore */645swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */646VM_OFF;647tophys(r1,r1);648649RESTORE_REGS;650addik r1, r1, PT_SIZE /* Clean up stack space. */651652lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */653bri 6f;654/* Return to kernel state. */6552: set_bip; /* Ints masked for state restore */656VM_OFF;657tophys(r1,r1);658RESTORE_REGS;659addik r1, r1, PT_SIZE /* Clean up stack space. */660661tovirt(r1,r1);6626:663EXC_return: /* Make global symbol for debugging */664rtbd r14, 0; /* Instructions to return from an IRQ */665nop;666667/*668* HW EXCEPTION rutine end669*/670671/*672* Hardware maskable interrupts.673*674* The stack-pointer (r1) should have already been saved to the memory675* location PER_CPU(ENTRY_SP).676*/677C_ENTRY(_interrupt):678/* MS: we are in physical address */679/* Save registers, switch to proper stack, convert SP to virtual.*/680swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))681/* MS: See if already in kernel mode. */682mfs r1, rmsr683nop684andi r1, r1, MSR_UMS685bnei r1, 1f686687/* Kernel-mode state save. */688lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))689tophys(r1,r1); /* MS: I have in r1 physical address where stack is */690/* save registers */691/* MS: Make room on the stack -> activation record */692addik r1, r1, -PT_SIZE;693SAVE_REGS694brid 2f;695swi r1, r1, PT_MODE; /* 0 - user mode, 1 - kernel mode */6961:697/* User-mode state save. */698/* MS: get the saved current */699lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE));700tophys(r1,r1);701lwi r1, r1, TS_THREAD_INFO;702addik r1, r1, THREAD_SIZE;703tophys(r1,r1);704/* save registers */705addik r1, r1, -PT_SIZE;706SAVE_REGS707/* calculate mode */708swi r0, r1, PT_MODE;709lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));710swi r11, r1, PT_R1;711clear_ums;7122:713lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));714tovirt(r1,r1)715addik r15, r0, irq_call;716irq_call:rtbd r0, do_IRQ;717addik r5, r1, 0;718719/* MS: we are in virtual mode */720ret_from_irq:721lwi r11, r1, PT_MODE;722bnei r11, 2f;723724lwi r11, CURRENT_TASK, TS_THREAD_INFO;725lwi r11, r11, TI_FLAGS; /* MS: get flags from thread info */726andi r11, r11, _TIF_NEED_RESCHED;727beqi r11, 5f728bralid r15, schedule;729nop; /* delay slot */730731/* Maybe handle a signal */7325: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* MS: get thread info */733lwi r11, r11, TI_FLAGS; /* get flags in thread info */734andi r11, r11, _TIF_SIGPENDING;735beqid r11, no_intr_resched736/* Handle a signal return; Pending signals should be in r18. */737addi r7, r0, 0; /* Arg 3: int in_syscall */738addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */739bralid r15, do_signal; /* Handle any signals */740add r6, r0, r0; /* Arg 2: sigset_t *oldset */741742/* Finally, return to user state. */743no_intr_resched:744/* Disable interrupts, we are now committed to the state restore */745disable_irq746swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE);747VM_OFF;748tophys(r1,r1);749RESTORE_REGS750addik r1, r1, PT_SIZE /* MS: Clean up stack space. */751lwi r1, r1, PT_R1 - PT_SIZE;752bri 6f;753/* MS: Return to kernel state. */7542:755#ifdef CONFIG_PREEMPT756lwi r11, CURRENT_TASK, TS_THREAD_INFO;757/* MS: get preempt_count from thread info */758lwi r5, r11, TI_PREEMPT_COUNT;759bgti r5, restore;760761lwi r5, r11, TI_FLAGS; /* get flags in thread info */762andi r5, r5, _TIF_NEED_RESCHED;763beqi r5, restore /* if zero jump over */764765preempt:766/* interrupts are off that's why I am calling preempt_chedule_irq */767bralid r15, preempt_schedule_irq768nop769lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */770lwi r5, r11, TI_FLAGS; /* get flags in thread info */771andi r5, r5, _TIF_NEED_RESCHED;772bnei r5, preempt /* if non zero jump to resched */773restore:774#endif775VM_OFF /* MS: turn off MMU */776tophys(r1,r1)777RESTORE_REGS778addik r1, r1, PT_SIZE /* MS: Clean up stack space. */779tovirt(r1,r1);7806:781IRQ_return: /* MS: Make global symbol for debugging */782rtid r14, 0783nop784785/*786* Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18787* and call handling function with saved pt_regs788*/789C_ENTRY(_debug_exception):790/* BIP bit is set on entry, no interrupts can occur */791swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))792793mfs r1, rmsr794nop795andi r1, r1, MSR_UMS796bnei r1, 1f797/* MS: Kernel-mode state save - kgdb */798lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/799800/* BIP bit is set on entry, no interrupts can occur */801addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE;802SAVE_REGS;803/* save all regs to pt_reg structure */804swi r0, r1, PT_R0; /* R0 must be saved too */805swi r14, r1, PT_R14 /* rewrite saved R14 value */806swi r16, r1, PT_PC; /* PC and r16 are the same */807/* save special purpose registers to pt_regs */808mfs r11, rear;809swi r11, r1, PT_EAR;810mfs r11, resr;811swi r11, r1, PT_ESR;812mfs r11, rfsr;813swi r11, r1, PT_FSR;814815/* stack pointer is in physical address at it is decrease816* by PT_SIZE but we need to get correct R1 value */817addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + PT_SIZE;818swi r11, r1, PT_R1819/* MS: r31 - current pointer isn't changed */820tovirt(r1,r1)821#ifdef CONFIG_KGDB822addi r5, r1, 0 /* pass pt_reg address as the first arg */823addik r15, r0, dbtrap_call; /* return address */824rtbd r0, microblaze_kgdb_break825nop;826#endif827/* MS: Place handler for brki from kernel space if KGDB is OFF.828* It is very unlikely that another brki instruction is called. */829bri 0830831/* MS: User-mode state save - gdb */8321: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */833tophys(r1,r1);834lwi r1, r1, TS_THREAD_INFO; /* get the thread info */835addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */836tophys(r1,r1);837838addik r1, r1, -PT_SIZE; /* Make room on the stack. */839SAVE_REGS;840swi r16, r1, PT_PC; /* Save LP */841swi r0, r1, PT_MODE; /* Was in user-mode. */842lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));843swi r11, r1, PT_R1; /* Store user SP. */844lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));845tovirt(r1,r1)846set_vms;847addik r5, r1, 0;848addik r15, r0, dbtrap_call;849dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */850rtbd r0, sw_exception851nop852853/* MS: The first instruction for the second part of the gdb/kgdb */854set_bip; /* Ints masked for state restore */855lwi r11, r1, PT_MODE;856bnei r11, 2f;857/* MS: Return to user space - gdb */858/* Get current task ptr into r11 */859lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */860lwi r11, r11, TI_FLAGS; /* get flags in thread info */861andi r11, r11, _TIF_NEED_RESCHED;862beqi r11, 5f;863864/* Call the scheduler before returning from a syscall/trap. */865bralid r15, schedule; /* Call scheduler */866nop; /* delay slot */867868/* Maybe handle a signal */8695: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */870lwi r11, r11, TI_FLAGS; /* get flags in thread info */871andi r11, r11, _TIF_SIGPENDING;872beqi r11, 1f; /* Signals to handle, handle them */873874addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */875addi r7, r0, 0; /* Arg 3: int in_syscall */876bralid r15, do_signal; /* Handle any signals */877add r6, r0, r0; /* Arg 2: sigset_t *oldset */878879/* Finally, return to user state. */8801: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */881VM_OFF;882tophys(r1,r1);883/* MS: Restore all regs */884RESTORE_REGS885addik r1, r1, PT_SIZE /* Clean up stack space */886lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */887DBTRAP_return_user: /* MS: Make global symbol for debugging */888rtbd r16, 0; /* MS: Instructions to return from a debug trap */889nop;890891/* MS: Return to kernel state - kgdb */8922: VM_OFF;893tophys(r1,r1);894/* MS: Restore all regs */895RESTORE_REGS896lwi r14, r1, PT_R14;897lwi r16, r1, PT_PC;898addik r1, r1, PT_SIZE; /* MS: Clean up stack space */899tovirt(r1,r1);900DBTRAP_return_kernel: /* MS: Make global symbol for debugging */901rtbd r16, 0; /* MS: Instructions to return from a debug trap */902nop;903904905ENTRY(_switch_to)906/* prepare return value */907addk r3, r0, CURRENT_TASK908909/* save registers in cpu_context */910/* use r11 and r12, volatile registers, as temp register */911/* give start of cpu_context for previous process */912addik r11, r5, TI_CPU_CONTEXT913swi r1, r11, CC_R1914swi r2, r11, CC_R2915/* skip volatile registers.916* they are saved on stack when we jumped to _switch_to() */917/* dedicated registers */918swi r13, r11, CC_R13919swi r14, r11, CC_R14920swi r15, r11, CC_R15921swi r16, r11, CC_R16922swi r17, r11, CC_R17923swi r18, r11, CC_R18924/* save non-volatile registers */925swi r19, r11, CC_R19926swi r20, r11, CC_R20927swi r21, r11, CC_R21928swi r22, r11, CC_R22929swi r23, r11, CC_R23930swi r24, r11, CC_R24931swi r25, r11, CC_R25932swi r26, r11, CC_R26933swi r27, r11, CC_R27934swi r28, r11, CC_R28935swi r29, r11, CC_R29936swi r30, r11, CC_R30937/* special purpose registers */938mfs r12, rmsr939swi r12, r11, CC_MSR940mfs r12, rear941swi r12, r11, CC_EAR942mfs r12, resr943swi r12, r11, CC_ESR944mfs r12, rfsr945swi r12, r11, CC_FSR946947/* update r31, the current-give me pointer to task which will be next */948lwi CURRENT_TASK, r6, TI_TASK949/* stored it to current_save too */950swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE)951952/* get new process' cpu context and restore */953/* give me start where start context of next task */954addik r11, r6, TI_CPU_CONTEXT955956/* non-volatile registers */957lwi r30, r11, CC_R30958lwi r29, r11, CC_R29959lwi r28, r11, CC_R28960lwi r27, r11, CC_R27961lwi r26, r11, CC_R26962lwi r25, r11, CC_R25963lwi r24, r11, CC_R24964lwi r23, r11, CC_R23965lwi r22, r11, CC_R22966lwi r21, r11, CC_R21967lwi r20, r11, CC_R20968lwi r19, r11, CC_R19969/* dedicated registers */970lwi r18, r11, CC_R18971lwi r17, r11, CC_R17972lwi r16, r11, CC_R16973lwi r15, r11, CC_R15974lwi r14, r11, CC_R14975lwi r13, r11, CC_R13976/* skip volatile registers */977lwi r2, r11, CC_R2978lwi r1, r11, CC_R1979980/* special purpose registers */981lwi r12, r11, CC_FSR982mts rfsr, r12983lwi r12, r11, CC_MSR984mts rmsr, r12985986rtsd r15, 8987nop988989ENTRY(_reset)990brai 0; /* Jump to reset vector */991992/* These are compiled and loaded into high memory, then993* copied into place in mach_early_setup */994.section .init.ivt, "ax"995#if CONFIG_MANUAL_RESET_VECTOR996.org 0x0997brai CONFIG_MANUAL_RESET_VECTOR998#endif999.org 0x81000brai TOPHYS(_user_exception); /* syscall handler */1001.org 0x101002brai TOPHYS(_interrupt); /* Interrupt handler */1003.org 0x181004brai TOPHYS(_debug_exception); /* debug trap handler */1005.org 0x201006brai TOPHYS(_hw_exception_handler); /* HW exception handler */10071008.section .rodata,"a"1009#include "syscall_table.S"10101011syscall_table_size=(.-sys_call_table)10121013type_SYSCALL:1014.ascii "SYSCALL\0"1015type_IRQ:1016.ascii "IRQ\0"1017type_IRQ_PREEMPT:1018.ascii "IRQ (PREEMPTED)\0"1019type_SYSCALL_PREEMPT:1020.ascii " SYSCALL (PREEMPTED)\0"10211022/*1023* Trap decoding for stack unwinder1024* Tuples are (start addr, end addr, string)1025* If return address lies on [start addr, end addr],1026* unwinder displays 'string'1027*/10281029.align 41030.global microblaze_trap_handlers1031microblaze_trap_handlers:1032/* Exact matches come first */1033.word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL1034.word ret_from_irq ; .word ret_from_irq ; .word type_IRQ1035/* Fuzzy matches go here */1036.word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT1037.word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT1038/* End of table */1039.word 0 ; .word 0 ; .word 0104010411042