/*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>29#include <asm/xilinx_mb_manager.h>3031#include <linux/errno.h>32#include <asm/signal.h>33#include <asm/mmu.h>3435#undef DEBUG3637#ifdef DEBUG38/* Create space for syscalls counting. */39.section .data40.global syscall_debug_table41.align 442syscall_debug_table:43.space (__NR_syscalls * 4)44#endif /* DEBUG */4546#define C_ENTRY(name) .globl name; .align 4; name4748/*49* Various ways of setting and clearing BIP in flags reg.50* This is mucky, but necessary using microblaze version that51* allows msr ops to write to BIP52*/53#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR54.macro clear_bip55msrclr r0, MSR_BIP56.endm5758.macro set_bip59msrset r0, MSR_BIP60.endm6162.macro clear_eip63msrclr r0, MSR_EIP64.endm6566.macro set_ee67msrset r0, MSR_EE68.endm6970.macro disable_irq71msrclr r0, MSR_IE72.endm7374.macro enable_irq75msrset r0, MSR_IE76.endm7778.macro set_ums79msrset r0, MSR_UMS80msrclr r0, MSR_VMS81.endm8283.macro set_vms84msrclr r0, MSR_UMS85msrset r0, MSR_VMS86.endm8788.macro clear_ums89msrclr r0, MSR_UMS90.endm9192.macro clear_vms_ums93msrclr r0, MSR_VMS | MSR_UMS94.endm95#else96.macro clear_bip97mfs r11, rmsr98andi r11, r11, ~MSR_BIP99mts rmsr, r11100.endm101102.macro set_bip103mfs r11, rmsr104ori r11, r11, MSR_BIP105mts rmsr, r11106.endm107108.macro clear_eip109mfs r11, rmsr110andi r11, r11, ~MSR_EIP111mts rmsr, r11112.endm113114.macro set_ee115mfs r11, rmsr116ori r11, r11, MSR_EE117mts rmsr, r11118.endm119120.macro disable_irq121mfs r11, rmsr122andi r11, r11, ~MSR_IE123mts rmsr, r11124.endm125126.macro enable_irq127mfs r11, rmsr128ori r11, r11, MSR_IE129mts rmsr, r11130.endm131132.macro set_ums133mfs r11, rmsr134ori r11, r11, MSR_VMS135andni r11, r11, MSR_UMS136mts rmsr, r11137.endm138139.macro set_vms140mfs r11, rmsr141ori r11, r11, MSR_VMS142andni r11, r11, MSR_UMS143mts rmsr, r11144.endm145146.macro clear_ums147mfs r11, rmsr148andni r11, r11, MSR_UMS149mts rmsr,r11150.endm151152.macro clear_vms_ums153mfs r11, rmsr154andni r11, r11, (MSR_VMS|MSR_UMS)155mts rmsr,r11156.endm157#endif158159/* Define how to call high-level functions. With MMU, virtual mode must be160* enabled when calling the high-level function. Clobbers R11.161* VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL162*/163164/* turn on virtual protected mode save */165#define VM_ON \166set_ums; \167rted r0, 2f; \168nop; \1692:170171/* turn off virtual protected mode save and user mode save*/172#define VM_OFF \173clear_vms_ums; \174rted r0, TOPHYS(1f); \175nop; \1761:177178#define SAVE_REGS \179swi r2, r1, PT_R2; /* Save SDA */ \180swi r3, r1, PT_R3; \181swi r4, r1, PT_R4; \182swi r5, r1, PT_R5; \183swi r6, r1, PT_R6; \184swi r7, r1, PT_R7; \185swi r8, r1, PT_R8; \186swi r9, r1, PT_R9; \187swi r10, r1, PT_R10; \188swi r11, r1, PT_R11; /* save clobbered regs after rval */\189swi r12, r1, PT_R12; \190swi r13, r1, PT_R13; /* Save SDA2 */ \191swi r14, r1, PT_PC; /* PC, before IRQ/trap */ \192swi r15, r1, PT_R15; /* Save LP */ \193swi r16, r1, PT_R16; \194swi r17, r1, PT_R17; \195swi r18, r1, PT_R18; /* Save asm scratch reg */ \196swi r19, r1, PT_R19; \197swi r20, r1, PT_R20; \198swi r21, r1, PT_R21; \199swi r22, r1, PT_R22; \200swi r23, r1, PT_R23; \201swi r24, r1, PT_R24; \202swi r25, r1, PT_R25; \203swi r26, r1, PT_R26; \204swi r27, r1, PT_R27; \205swi r28, r1, PT_R28; \206swi r29, r1, PT_R29; \207swi r30, r1, PT_R30; \208swi r31, r1, PT_R31; /* Save current task reg */ \209mfs r11, rmsr; /* save MSR */ \210swi r11, r1, PT_MSR;211212#define RESTORE_REGS_GP \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 RESTORE_REGS \245lwi r11, r1, PT_MSR; \246mts rmsr , r11; \247RESTORE_REGS_GP248249#define RESTORE_REGS_RTBD \250lwi r11, r1, PT_MSR; \251andni r11, r11, MSR_EIP; /* clear EIP */ \252ori r11, r11, MSR_EE | MSR_BIP; /* set EE and BIP */ \253mts rmsr , r11; \254RESTORE_REGS_GP255256#define SAVE_STATE \257swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \258/* See if already in kernel mode.*/ \259mfs r1, rmsr; \260andi r1, r1, MSR_UMS; \261bnei r1, 1f; \262/* Kernel-mode state save. */ \263/* Reload kernel stack-ptr. */ \264lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \265/* FIXME: I can add these two lines to one */ \266/* tophys(r1,r1); */ \267/* addik r1, r1, -PT_SIZE; */ \268addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \269SAVE_REGS \270brid 2f; \271swi r1, r1, PT_MODE; \2721: /* User-mode state save. */ \273lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\274tophys(r1,r1); \275lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \276/* MS these three instructions can be added to one */ \277/* addik r1, r1, THREAD_SIZE; */ \278/* tophys(r1,r1); */ \279/* addik r1, r1, -PT_SIZE; */ \280addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \281SAVE_REGS \282lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \283swi r11, r1, PT_R1; /* Store user SP. */ \284swi r0, r1, PT_MODE; /* Was in user-mode. */ \285/* MS: I am clearing UMS even in case when I come from kernel space */ \286clear_ums; \2872: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));288289.text290291.extern cpuinfo292293C_ENTRY(mb_flush_dcache):294addik r1, r1, -PT_SIZE295SAVE_REGS296297addik r3, r0, cpuinfo298lwi r7, r3, CI_DCS299lwi r8, r3, CI_DCL300sub r9, r7, r83011:302wdc.flush r9, r0303bgtid r9, 1b304addk r9, r9, r8305306RESTORE_REGS307addik r1, r1, PT_SIZE308rtsd r15, 8309nop310311C_ENTRY(mb_invalidate_icache):312addik r1, r1, -PT_SIZE313SAVE_REGS314315addik r3, r0, cpuinfo316lwi r7, r3, CI_ICS317lwi r8, r3, CI_ICL318sub r9, r7, r83191:320wic r9, r0321bgtid r9, 1b322addk r9, r9, r8323324RESTORE_REGS325addik r1, r1, PT_SIZE326rtsd r15, 8327nop328329/*330* User trap.331*332* System calls are handled here.333*334* Syscall protocol:335* Syscall number in r12, args in r5-r10336* Return value in r3337*338* Trap entered via brki instruction, so BIP bit is set, and interrupts339* are masked. This is nice, means we don't have to CLI before state save340*/341C_ENTRY(_user_exception):342swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */343addi r14, r14, 4 /* return address is 4 byte after call */344345lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */346tophys(r1,r1);347lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */348/* calculate kernel stack pointer from task struct 8k */349addik r1, r1, THREAD_SIZE;350tophys(r1,r1);351352addik r1, r1, -PT_SIZE; /* Make room on the stack. */353SAVE_REGS354swi r0, r1, PT_R3355swi r0, r1, PT_R4356357swi r0, r1, PT_MODE; /* Was in user-mode. */358lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));359swi r11, r1, PT_R1; /* Store user SP. */360clear_ums;3612: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));362/* Save away the syscall number. */363swi r12, r1, PT_R0;364tovirt(r1,r1)365366/* where the trap should return need -8 to adjust for rtsd r15, 8*/367/* Jump to the appropriate function for the system call number in r12368* (r12 is not preserved), or return an error if r12 is not valid. The LP369* register should point to the location where370* the called function should return. [note that MAKE_SYS_CALL uses label 1] */371372/* Step into virtual mode */373rtbd r0, 3f374nop3753:376lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */377lwi r11, r11, TI_FLAGS /* get flags in thread info */378andi r11, r11, _TIF_WORK_SYSCALL_MASK379beqi r11, 4f380381addik r3, r0, -ENOSYS382swi r3, r1, PT_R3383brlid r15, do_syscall_trace_enter384addik r5, r1, PT_R0385386# do_syscall_trace_enter returns the new syscall nr.387addk r12, r0, r3388lwi r5, r1, PT_R5;389lwi r6, r1, PT_R6;390lwi r7, r1, PT_R7;391lwi r8, r1, PT_R8;392lwi r9, r1, PT_R9;393lwi r10, r1, PT_R10;3944:395/* Jump to the appropriate function for the system call number in r12396* (r12 is not preserved), or return an error if r12 is not valid.397* The LP register should point to the location where the called function398* should return. [note that MAKE_SYS_CALL uses label 1] */399/* See if the system call number is valid */400blti r12, 5f401addi r11, r12, -__NR_syscalls;402bgei r11, 5f;403/* Figure out which function to use for this system call. */404/* Note Microblaze barrel shift is optional, so don't rely on it */405add r12, r12, r12; /* convert num -> ptr */406add r12, r12, r12;407addi r30, r0, 1 /* restarts allowed */408409#ifdef DEBUG410/* Trac syscalls and stored them to syscall_debug_table */411/* The first syscall location stores total syscall number */412lwi r3, r0, syscall_debug_table413addi r3, r3, 1414swi r3, r0, syscall_debug_table415lwi r3, r12, syscall_debug_table416addi r3, r3, 1417swi r3, r12, syscall_debug_table418#endif419420# Find and jump into the syscall handler.421lwi r12, r12, sys_call_table422/* where the trap should return need -8 to adjust for rtsd r15, 8 */423addi r15, r0, ret_from_trap-8424bra r12425426/* The syscall number is invalid, return an error. */4275:428braid ret_from_trap429addi r3, r0, -ENOSYS;430431/* Entry point used to return from a syscall/trap */432/* We re-enable BIP bit before state restore */433C_ENTRY(ret_from_trap):434swi r3, r1, PT_R3435swi r4, r1, PT_R4436437lwi r11, r1, PT_MODE;438/* See if returning to kernel mode, if so, skip resched &c. */439bnei r11, 2f;440/* We're returning to user mode, so check for various conditions that441* trigger rescheduling. */442/* FIXME: Restructure all these flag checks. */443lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */444lwi r11, r11, TI_FLAGS; /* get flags in thread info */445andi r11, r11, _TIF_WORK_SYSCALL_MASK446beqi r11, 1f447448brlid r15, do_syscall_trace_leave449addik r5, r1, PT_R04501:451/* We're returning to user mode, so check for various conditions that452* trigger rescheduling. */453/* get thread info from current task */454lwi r11, CURRENT_TASK, TS_THREAD_INFO;455lwi r19, r11, TI_FLAGS; /* get flags in thread info */456andi r11, r19, _TIF_NEED_RESCHED;457beqi r11, 5f;458459bralid r15, schedule; /* Call scheduler */460nop; /* delay slot */461bri 1b462463/* Maybe handle a signal */4645:465andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;466beqi r11, 4f; /* Signals to handle, handle them */467468addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */469bralid r15, do_notify_resume; /* Handle any signals */470add r6, r30, r0; /* Arg 2: int in_syscall */471add r30, r0, r0 /* no more restarts */472bri 1b473474/* Finally, return to user state. */4754: set_bip; /* Ints masked for state restore */476swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */477VM_OFF;478tophys(r1,r1);479RESTORE_REGS_RTBD;480addik r1, r1, PT_SIZE /* Clean up stack space. */481lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */482bri 6f;483484/* Return to kernel state. */4852: set_bip; /* Ints masked for state restore */486VM_OFF;487tophys(r1,r1);488RESTORE_REGS_RTBD;489addik r1, r1, PT_SIZE /* Clean up stack space. */490tovirt(r1,r1);4916:492TRAP_return: /* Make global symbol for debugging */493rtbd r14, 0; /* Instructions to return from an IRQ */494nop;495496497/* This the initial entry point for a new child thread, with an appropriate498stack in place that makes it look like the child is in the middle of a499syscall. This function is actually `returned to' from switch_thread500(copy_thread makes ret_from_fork the return address in each new thread's501saved context). */502C_ENTRY(ret_from_fork):503bralid r15, schedule_tail; /* ...which is schedule_tail's arg */504add r5, r3, r0; /* switch_thread returns the prev task */505/* ( in the delay slot ) */506brid ret_from_trap; /* Do normal trap return */507add r3, r0, r0; /* Child's fork call should return 0. */508509C_ENTRY(ret_from_kernel_thread):510bralid r15, schedule_tail; /* ...which is schedule_tail's arg */511add r5, r3, r0; /* switch_thread returns the prev task */512/* ( in the delay slot ) */513brald r15, r20 /* fn was left in r20 */514addk r5, r0, r19 /* ... and argument - in r19 */515brid ret_from_trap516add r3, r0, r0517518C_ENTRY(sys_rt_sigreturn_wrapper):519addik r30, r0, 0 /* no restarts */520brid sys_rt_sigreturn /* Do real work */521addik r5, r1, 0; /* add user context as 1st arg */522523/*524* HW EXCEPTION rutine start525*/526C_ENTRY(full_exception_trap):527/* adjust exception address for privileged instruction528* for finding where is it */529addik r17, r17, -4530SAVE_STATE /* Save registers */531/* PC, before IRQ/trap - this is one instruction above */532swi r17, r1, PT_PC;533tovirt(r1,r1)534/* FIXME this can be store directly in PT_ESR reg.535* I tested it but there is a fault */536/* where the trap should return need -8 to adjust for rtsd r15, 8 */537addik r15, r0, ret_from_exc - 8538mfs r6, resr539mfs r7, rfsr; /* save FSR */540mts rfsr, r0; /* Clear sticky fsr */541rted r0, full_exception542addik r5, r1, 0 /* parameter struct pt_regs * regs */543544/*545* Unaligned data trap.546*547* Unaligned data trap last on 4k page is handled here.548*549* Trap entered via exception, so EE bit is set, and interrupts550* are masked. This is nice, means we don't have to CLI before state save551*552* The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S"553*/554C_ENTRY(unaligned_data_trap):555/* MS: I have to save r11 value and then restore it because556* set_bit, clear_eip, set_ee use r11 as temp register if MSR557* instructions are not used. We don't need to do if MSR instructions558* are used and they use r0 instead of r11.559* I am using ENTRY_SP which should be primary used only for stack560* pointer saving. */561swi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));562set_bip; /* equalize initial state for all possible entries */563clear_eip;564set_ee;565lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));566SAVE_STATE /* Save registers.*/567/* PC, before IRQ/trap - this is one instruction above */568swi r17, r1, PT_PC;569tovirt(r1,r1)570/* where the trap should return need -8 to adjust for rtsd r15, 8 */571addik r15, r0, ret_from_exc-8572mfs r3, resr /* ESR */573mfs r4, rear /* EAR */574rtbd r0, _unaligned_data_exception575addik r7, r1, 0 /* parameter struct pt_regs * regs */576577/*578* Page fault traps.579*580* If the real exception handler (from hw_exception_handler.S) didn't find581* the mapping for the process, then we're thrown here to handle such situation.582*583* Trap entered via exceptions, so EE bit is set, and interrupts584* are masked. This is nice, means we don't have to CLI before state save585*586* Build a standard exception frame for TLB Access errors. All TLB exceptions587* will bail out to this point if they can't resolve the lightweight TLB fault.588*589* The C function called is in "arch/microblaze/mm/fault.c", declared as:590* void do_page_fault(struct pt_regs *regs,591* unsigned long address,592* unsigned long error_code)593*/594/* data and intruction trap - which is choose is resolved int fault.c */595C_ENTRY(page_fault_data_trap):596SAVE_STATE /* Save registers.*/597/* PC, before IRQ/trap - this is one instruction above */598swi r17, r1, PT_PC;599tovirt(r1,r1)600/* where the trap should return need -8 to adjust for rtsd r15, 8 */601addik r15, r0, ret_from_exc-8602mfs r6, rear /* parameter unsigned long address */603mfs r7, resr /* parameter unsigned long error_code */604rted r0, do_page_fault605addik r5, r1, 0 /* parameter struct pt_regs * regs */606607C_ENTRY(page_fault_instr_trap):608SAVE_STATE /* Save registers.*/609/* PC, before IRQ/trap - this is one instruction above */610swi r17, r1, PT_PC;611tovirt(r1,r1)612/* where the trap should return need -8 to adjust for rtsd r15, 8 */613addik r15, r0, ret_from_exc-8614mfs r6, rear /* parameter unsigned long address */615ori r7, r0, 0 /* parameter unsigned long error_code */616rted r0, do_page_fault617addik r5, r1, 0 /* parameter struct pt_regs * regs */618619/* Entry point used to return from an exception. */620C_ENTRY(ret_from_exc):621lwi r11, r1, PT_MODE;622bnei r11, 2f; /* See if returning to kernel mode, */623/* ... if so, skip resched &c. */624625/* We're returning to user mode, so check for various conditions that626trigger rescheduling. */6271:628lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */629lwi r19, r11, TI_FLAGS; /* get flags in thread info */630andi r11, r19, _TIF_NEED_RESCHED;631beqi r11, 5f;632633/* Call the scheduler before returning from a syscall/trap. */634bralid r15, schedule; /* Call scheduler */635nop; /* delay slot */636bri 1b637638/* Maybe handle a signal */6395: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;640beqi r11, 4f; /* Signals to handle, handle them */641642/*643* Handle a signal return; Pending signals should be in r18.644*645* Not all registers are saved by the normal trap/interrupt entry646* points (for instance, call-saved registers (because the normal647* C-compiler calling sequence in the kernel makes sure they're648* preserved), and call-clobbered registers in the case of649* traps), but signal handlers may want to examine or change the650* complete register state. Here we save anything not saved by651* the normal entry sequence, so that it may be safely restored652* (in a possibly modified form) after do_notify_resume returns. */653addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */654bralid r15, do_notify_resume; /* Handle any signals */655addi r6, r0, 0; /* Arg 2: int in_syscall */656bri 1b657658/* Finally, return to user state. */6594: set_bip; /* Ints masked for state restore */660swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */661VM_OFF;662tophys(r1,r1);663664RESTORE_REGS_RTBD;665addik r1, r1, PT_SIZE /* Clean up stack space. */666667lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */668bri 6f;669/* Return to kernel state. */6702: set_bip; /* Ints masked for state restore */671VM_OFF;672tophys(r1,r1);673RESTORE_REGS_RTBD;674addik r1, r1, PT_SIZE /* Clean up stack space. */675676tovirt(r1,r1);6776:678EXC_return: /* Make global symbol for debugging */679rtbd r14, 0; /* Instructions to return from an IRQ */680nop;681682/*683* HW EXCEPTION rutine end684*/685686/*687* Hardware maskable interrupts.688*689* The stack-pointer (r1) should have already been saved to the memory690* location PER_CPU(ENTRY_SP).691*/692C_ENTRY(_interrupt):693/* MS: we are in physical address */694/* Save registers, switch to proper stack, convert SP to virtual.*/695swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))696/* MS: See if already in kernel mode. */697mfs r1, rmsr698nop699andi r1, r1, MSR_UMS700bnei r1, 1f701702/* Kernel-mode state save. */703lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))704tophys(r1,r1); /* MS: I have in r1 physical address where stack is */705/* save registers */706/* MS: Make room on the stack -> activation record */707addik r1, r1, -PT_SIZE;708SAVE_REGS709brid 2f;710swi r1, r1, PT_MODE; /* 0 - user mode, 1 - kernel mode */7111:712/* User-mode state save. */713/* MS: get the saved current */714lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE));715tophys(r1,r1);716lwi r1, r1, TS_THREAD_INFO;717addik r1, r1, THREAD_SIZE;718tophys(r1,r1);719/* save registers */720addik r1, r1, -PT_SIZE;721SAVE_REGS722/* calculate mode */723swi r0, r1, PT_MODE;724lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));725swi r11, r1, PT_R1;726clear_ums;7272:728lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));729tovirt(r1,r1)730addik r15, r0, irq_call;731irq_call:rtbd r0, do_IRQ;732addik r5, r1, 0;733734/* MS: we are in virtual mode */735ret_from_irq:736lwi r11, r1, PT_MODE;737bnei r11, 2f;7387391:740lwi r11, CURRENT_TASK, TS_THREAD_INFO;741lwi r19, r11, TI_FLAGS; /* MS: get flags from thread info */742andi r11, r19, _TIF_NEED_RESCHED;743beqi r11, 5f744bralid r15, schedule;745nop; /* delay slot */746bri 1b747748/* Maybe handle a signal */7495: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;750beqid r11, no_intr_resched751/* Handle a signal return; Pending signals should be in r18. */752addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */753bralid r15, do_notify_resume; /* Handle any signals */754addi r6, r0, 0; /* Arg 2: int in_syscall */755bri 1b756757/* Finally, return to user state. */758no_intr_resched:759/* Disable interrupts, we are now committed to the state restore */760disable_irq761swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE);762VM_OFF;763tophys(r1,r1);764RESTORE_REGS765addik r1, r1, PT_SIZE /* MS: Clean up stack space. */766lwi r1, r1, PT_R1 - PT_SIZE;767bri 6f;768/* MS: Return to kernel state. */7692:770#ifdef CONFIG_PREEMPTION771lwi r11, CURRENT_TASK, TS_THREAD_INFO;772/* MS: get preempt_count from thread info */773lwi r5, r11, TI_PREEMPT_COUNT;774bgti r5, restore;775776lwi r5, r11, TI_FLAGS; /* get flags in thread info */777andi r5, r5, _TIF_NEED_RESCHED;778beqi r5, restore /* if zero jump over */779780/* interrupts are off that's why I am calling preempt_chedule_irq */781bralid r15, preempt_schedule_irq782nop783restore:784#endif785VM_OFF /* MS: turn off MMU */786tophys(r1,r1)787RESTORE_REGS788addik r1, r1, PT_SIZE /* MS: Clean up stack space. */789tovirt(r1,r1);7906:791IRQ_return: /* MS: Make global symbol for debugging */792rtid r14, 0793nop794795#ifdef CONFIG_MB_MANAGER796797#define PT_PID PT_SIZE798#define PT_TLBI PT_SIZE + 4799#define PT_ZPR PT_SIZE + 8800#define PT_TLBL0 PT_SIZE + 12801#define PT_TLBH0 PT_SIZE + 16802803C_ENTRY(_xtmr_manager_reset):804lwi r1, r0, xmb_manager_stackpointer805806/* Restore MSR */807lwi r2, r1, PT_MSR808mts rmsr, r2809bri 4810811/* restore Special purpose registers */812lwi r2, r1, PT_PID813mts rpid, r2814815lwi r2, r1, PT_TLBI816mts rtlbx, r2817818lwi r2, r1, PT_ZPR819mts rzpr, r2820821#if CONFIG_XILINX_MICROBLAZE0_USE_FPU822lwi r2, r1, PT_FSR823mts rfsr, r2824#endif825826/* restore all the tlb's */827addik r3, r0, TOPHYS(tlb_skip)828addik r6, r0, PT_TLBL0829addik r7, r0, PT_TLBH0830restore_tlb:831add r6, r6, r1832add r7, r7, r1833lwi r2, r6, 0834mts rtlblo, r2835lwi r2, r7, 0836mts rtlbhi, r2837addik r6, r6, 4838addik r7, r7, 4839bgtid r3, restore_tlb840addik r3, r3, -1841842lwi r5, r0, TOPHYS(xmb_manager_dev)843lwi r8, r0, TOPHYS(xmb_manager_reset_callback)844set_vms845/* return from reset need -8 to adjust for rtsd r15, 8 */846addik r15, r0, ret_from_reset - 8847rtbd r8, 0848nop849850ret_from_reset:851set_bip /* Ints masked for state restore */852VM_OFF853/* MS: Restore all regs */854RESTORE_REGS855lwi r14, r1, PT_R14856lwi r16, r1, PT_PC857addik r1, r1, PT_SIZE + 36858rtbd r16, 0859nop860861/*862* Break handler for MB Manager. Enter to _xmb_manager_break by863* injecting fault in one of the TMR Microblaze core.864* FIXME: This break handler supports getting865* called from kernel space only.866*/867C_ENTRY(_xmb_manager_break):868/*869* Reserve memory in the stack for context store/restore870* (which includes memory for storing tlbs (max two tlbs))871*/872addik r1, r1, -PT_SIZE - 36873swi r1, r0, xmb_manager_stackpointer874SAVE_REGS875swi r14, r1, PT_R14 /* rewrite saved R14 value */876swi r16, r1, PT_PC; /* PC and r16 are the same */877878lwi r6, r0, TOPHYS(xmb_manager_baseaddr)879lwi r7, r0, TOPHYS(xmb_manager_crval)880/*881* When the break vector gets asserted because of error injection,882* the break signal must be blocked before exiting from the883* break handler, below code configures the tmr manager884* control register to block break signal.885*/886swi r7, r6, 0887888/* Save the special purpose registers */889mfs r2, rpid890swi r2, r1, PT_PID891892mfs r2, rtlbx893swi r2, r1, PT_TLBI894895mfs r2, rzpr896swi r2, r1, PT_ZPR897898#if CONFIG_XILINX_MICROBLAZE0_USE_FPU899mfs r2, rfsr900swi r2, r1, PT_FSR901#endif902mfs r2, rmsr903swi r2, r1, PT_MSR904905/* Save all the tlb's */906addik r3, r0, TOPHYS(tlb_skip)907addik r6, r0, PT_TLBL0908addik r7, r0, PT_TLBH0909save_tlb:910add r6, r6, r1911add r7, r7, r1912mfs r2, rtlblo913swi r2, r6, 0914mfs r2, rtlbhi915swi r2, r7, 0916addik r6, r6, 4917addik r7, r7, 4918bgtid r3, save_tlb919addik r3, r3, -1920921lwi r5, r0, TOPHYS(xmb_manager_dev)922lwi r8, r0, TOPHYS(xmb_manager_callback)923/* return from break need -8 to adjust for rtsd r15, 8 */924addik r15, r0, ret_from_break - 8925rtbd r8, 0926nop927928ret_from_break:929/* flush the d-cache */930bralid r15, mb_flush_dcache931nop932933/*934* To make sure microblaze i-cache is in a proper state935* invalidate the i-cache.936*/937bralid r15, mb_invalidate_icache938nop939940set_bip; /* Ints masked for state restore */941VM_OFF;942mbar 1943mbar 2944bri 4945suspend946nop947#endif948949/*950* Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18951* and call handling function with saved pt_regs952*/953C_ENTRY(_debug_exception):954/* BIP bit is set on entry, no interrupts can occur */955swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP))956957mfs r1, rmsr958nop959andi r1, r1, MSR_UMS960bnei r1, 1f961/* MS: Kernel-mode state save - kgdb */962lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/963964/* BIP bit is set on entry, no interrupts can occur */965addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE;966SAVE_REGS;967/* save all regs to pt_reg structure */968swi r0, r1, PT_R0; /* R0 must be saved too */969swi r14, r1, PT_R14 /* rewrite saved R14 value */970swi r16, r1, PT_PC; /* PC and r16 are the same */971/* save special purpose registers to pt_regs */972mfs r11, rear;973swi r11, r1, PT_EAR;974mfs r11, resr;975swi r11, r1, PT_ESR;976mfs r11, rfsr;977swi r11, r1, PT_FSR;978979/* stack pointer is in physical address at it is decrease980* by PT_SIZE but we need to get correct R1 value */981addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + PT_SIZE;982swi r11, r1, PT_R1983/* MS: r31 - current pointer isn't changed */984tovirt(r1,r1)985#ifdef CONFIG_KGDB986addi r5, r1, 0 /* pass pt_reg address as the first arg */987addik r15, r0, dbtrap_call; /* return address */988rtbd r0, microblaze_kgdb_break989nop;990#endif991/* MS: Place handler for brki from kernel space if KGDB is OFF.992* It is very unlikely that another brki instruction is called. */993bri 0994995/* MS: User-mode state save - gdb */9961: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */997tophys(r1,r1);998lwi r1, r1, TS_THREAD_INFO; /* get the thread info */999addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */1000tophys(r1,r1);10011002addik r1, r1, -PT_SIZE; /* Make room on the stack. */1003SAVE_REGS;1004swi r16, r1, PT_PC; /* Save LP */1005swi r0, r1, PT_MODE; /* Was in user-mode. */1006lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));1007swi r11, r1, PT_R1; /* Store user SP. */1008lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));1009tovirt(r1,r1)1010set_vms;1011addik r5, r1, 0;1012addik r15, r0, dbtrap_call;1013dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */1014rtbd r0, sw_exception1015nop10161017/* MS: The first instruction for the second part of the gdb/kgdb */1018set_bip; /* Ints masked for state restore */1019lwi r11, r1, PT_MODE;1020bnei r11, 2f;1021/* MS: Return to user space - gdb */10221:1023/* Get current task ptr into r11 */1024lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */1025lwi r19, r11, TI_FLAGS; /* get flags in thread info */1026andi r11, r19, _TIF_NEED_RESCHED;1027beqi r11, 5f;10281029/* Call the scheduler before returning from a syscall/trap. */1030bralid r15, schedule; /* Call scheduler */1031nop; /* delay slot */1032bri 1b10331034/* Maybe handle a signal */10355: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;1036beqi r11, 4f; /* Signals to handle, handle them */10371038addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */1039bralid r15, do_notify_resume; /* Handle any signals */1040addi r6, r0, 0; /* Arg 2: int in_syscall */1041bri 1b10421043/* Finally, return to user state. */10444: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */1045VM_OFF;1046tophys(r1,r1);1047/* MS: Restore all regs */1048RESTORE_REGS_RTBD1049addik r1, r1, PT_SIZE /* Clean up stack space */1050lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */1051DBTRAP_return_user: /* MS: Make global symbol for debugging */1052rtbd r16, 0; /* MS: Instructions to return from a debug trap */1053nop;10541055/* MS: Return to kernel state - kgdb */10562: VM_OFF;1057tophys(r1,r1);1058/* MS: Restore all regs */1059RESTORE_REGS_RTBD1060lwi r14, r1, PT_R14;1061lwi r16, r1, PT_PC;1062addik r1, r1, PT_SIZE; /* MS: Clean up stack space */1063tovirt(r1,r1);1064DBTRAP_return_kernel: /* MS: Make global symbol for debugging */1065rtbd r16, 0; /* MS: Instructions to return from a debug trap */1066nop;106710681069ENTRY(_switch_to)1070/* prepare return value */1071addk r3, r0, CURRENT_TASK10721073/* save registers in cpu_context */1074/* use r11 and r12, volatile registers, as temp register */1075/* give start of cpu_context for previous process */1076addik r11, r5, TI_CPU_CONTEXT1077swi r1, r11, CC_R11078swi r2, r11, CC_R21079/* skip volatile registers.1080* they are saved on stack when we jumped to _switch_to() */1081/* dedicated registers */1082swi r13, r11, CC_R131083swi r14, r11, CC_R141084swi r15, r11, CC_R151085swi r16, r11, CC_R161086swi r17, r11, CC_R171087swi r18, r11, CC_R181088/* save non-volatile registers */1089swi r19, r11, CC_R191090swi r20, r11, CC_R201091swi r21, r11, CC_R211092swi r22, r11, CC_R221093swi r23, r11, CC_R231094swi r24, r11, CC_R241095swi r25, r11, CC_R251096swi r26, r11, CC_R261097swi r27, r11, CC_R271098swi r28, r11, CC_R281099swi r29, r11, CC_R291100swi r30, r11, CC_R301101/* special purpose registers */1102mfs r12, rmsr1103swi r12, r11, CC_MSR1104mfs r12, rear1105swi r12, r11, CC_EAR1106mfs r12, resr1107swi r12, r11, CC_ESR1108mfs r12, rfsr1109swi r12, r11, CC_FSR11101111/* update r31, the current-give me pointer to task which will be next */1112lwi CURRENT_TASK, r6, TI_TASK1113/* stored it to current_save too */1114swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE)11151116/* get new process' cpu context and restore */1117/* give me start where start context of next task */1118addik r11, r6, TI_CPU_CONTEXT11191120/* non-volatile registers */1121lwi r30, r11, CC_R301122lwi r29, r11, CC_R291123lwi r28, r11, CC_R281124lwi r27, r11, CC_R271125lwi r26, r11, CC_R261126lwi r25, r11, CC_R251127lwi r24, r11, CC_R241128lwi r23, r11, CC_R231129lwi r22, r11, CC_R221130lwi r21, r11, CC_R211131lwi r20, r11, CC_R201132lwi r19, r11, CC_R191133/* dedicated registers */1134lwi r18, r11, CC_R181135lwi r17, r11, CC_R171136lwi r16, r11, CC_R161137lwi r15, r11, CC_R151138lwi r14, r11, CC_R141139lwi r13, r11, CC_R131140/* skip volatile registers */1141lwi r2, r11, CC_R21142lwi r1, r11, CC_R111431144/* special purpose registers */1145lwi r12, r11, CC_FSR1146mts rfsr, r121147lwi r12, r11, CC_MSR1148mts rmsr, r1211491150rtsd r15, 81151nop11521153#ifdef CONFIG_MB_MANAGER1154.global xmb_inject_err1155.section .text1156.align 21157.ent xmb_inject_err1158.type xmb_inject_err, @function1159xmb_inject_err:1160addik r1, r1, -PT_SIZE1161SAVE_REGS11621163/* Switch to real mode */1164VM_OFF;1165set_bip;1166mbar 11167mbar 21168bralid r15, XMB_INJECT_ERR_OFFSET1169nop;11701171/* enable virtual mode */1172set_vms;1173/* barrier for instructions and data accesses */1174mbar 11175mbar 21176/*1177* Enable Interrupts, Virtual Protected Mode, equalize1178* initial state for all possible entries.1179*/1180rtbd r0, 1f1181nop;11821:1183RESTORE_REGS1184addik r1, r1, PT_SIZE1185rtsd r15, 8;1186nop;1187.end xmb_inject_err11881189.section .data1190.global xmb_manager_dev1191.global xmb_manager_baseaddr1192.global xmb_manager_crval1193.global xmb_manager_callback1194.global xmb_manager_reset_callback1195.global xmb_manager_stackpointer1196.align 41197xmb_manager_dev:1198.long 01199xmb_manager_baseaddr:1200.long 01201xmb_manager_crval:1202.long 01203xmb_manager_callback:1204.long 01205xmb_manager_reset_callback:1206.long 01207xmb_manager_stackpointer:1208.long 012091210/*1211* When the break vector gets asserted because of error injection,1212* the break signal must be blocked before exiting from the1213* break handler, Below api updates the manager address and1214* control register and error count callback arguments,1215* which will be used by the break handler to block the1216* break and call the callback function.1217*/1218.global xmb_manager_register1219.section .text1220.align 21221.ent xmb_manager_register1222.type xmb_manager_register, @function1223xmb_manager_register:1224swi r5, r0, xmb_manager_baseaddr1225swi r6, r0, xmb_manager_crval1226swi r7, r0, xmb_manager_callback1227swi r8, r0, xmb_manager_dev1228swi r9, r0, xmb_manager_reset_callback12291230rtsd r15, 8;1231nop;1232.end xmb_manager_register1233#endif12341235ENTRY(_reset)1236VM_OFF1237brai 0; /* Jump to reset vector */12381239/* These are compiled and loaded into high memory, then1240* copied into place in mach_early_setup */1241.section .init.ivt, "ax"1242#if CONFIG_MANUAL_RESET_VECTOR && !defined(CONFIG_MB_MANAGER)1243.org 0x01244brai CONFIG_MANUAL_RESET_VECTOR1245#elif defined(CONFIG_MB_MANAGER)1246.org 0x01247brai TOPHYS(_xtmr_manager_reset);1248#endif1249.org 0x81250brai TOPHYS(_user_exception); /* syscall handler */1251.org 0x101252brai TOPHYS(_interrupt); /* Interrupt handler */1253#ifdef CONFIG_MB_MANAGER1254.org 0x181255brai TOPHYS(_xmb_manager_break); /* microblaze manager break handler */1256#else1257.org 0x181258brai TOPHYS(_debug_exception); /* debug trap handler */1259#endif1260.org 0x201261brai TOPHYS(_hw_exception_handler); /* HW exception handler */12621263#ifdef CONFIG_MB_MANAGER1264/*1265* For TMR Inject API which injects the error should1266* be executed from LMB.1267* TMR Inject is programmed with address of 0x200 so that1268* when program counter matches with this address error will1269* be injected. 0x200 is expected to be next available bram1270* offset, hence used for this api.1271*/1272.org XMB_INJECT_ERR_OFFSET1273xmb_inject_error:1274nop1275rtsd r15, 81276nop1277#endif12781279.section .rodata,"a"1280#include "syscall_table.S"12811282syscall_table_size=(.-sys_call_table)12831284type_SYSCALL:1285.ascii "SYSCALL\0"1286type_IRQ:1287.ascii "IRQ\0"1288type_IRQ_PREEMPT:1289.ascii "IRQ (PREEMPTED)\0"1290type_SYSCALL_PREEMPT:1291.ascii " SYSCALL (PREEMPTED)\0"12921293/*1294* Trap decoding for stack unwinder1295* Tuples are (start addr, end addr, string)1296* If return address lies on [start addr, end addr],1297* unwinder displays 'string'1298*/12991300.align 41301.global microblaze_trap_handlers1302microblaze_trap_handlers:1303/* Exact matches come first */1304.word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL1305.word ret_from_irq ; .word ret_from_irq ; .word type_IRQ1306/* Fuzzy matches go here */1307.word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT1308.word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT1309/* End of table */1310.word 0 ; .word 0 ; .word 0131113121313