/* * Low-level system-call handling, trap handlers and context-switching * * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> * Copyright (C) 2008-2009 PetaLogix * Copyright (C) 2003 John Williams <jwilliams@itee.uq.edu.au> * Copyright (C) 2001,2002 NEC Corporation * Copyright (C) 2001,2002 Miles Bader <miles@gnu.org> * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this * archive for more details. * * Written by Miles Bader <miles@gnu.org> * Heavily modified by John Williams for Microblaze */ #include <linux/sys.h>#include <linux/linkage.h> #include <asm/entry.h>#include <asm/current.h>#include <asm/processor.h>#include <asm/exceptions.h>#include <asm/asm-offsets.h>#include <asm/thread_info.h> #include <asm/page.h>#include <asm/unistd.h> #include <linux/errno.h>#include <asm/signal.h> #undef DEBUG #ifdef DEBUG/* Create space for syscalls counting. */.section .data.global syscall_debug_table.align 4syscall_debug_table: .space (__NR_syscalls * 4)#endif /* DEBUG */ #define C_ENTRY(name) .globl name; .align 4; name /* * Various ways of setting and clearing BIP in flags reg. * This is mucky, but necessary using microblaze version that * allows msr ops to write to BIP */#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR .macro clear_bip msrclr r0, MSR_BIP .endm .macro set_bip msrset r0, MSR_BIP .endm .macro clear_eip msrclr r0, MSR_EIP .endm .macro set_ee msrset r0, MSR_EE .endm .macro disable_irq msrclr r0, MSR_IE .endm .macro enable_irq msrset r0, MSR_IE .endm .macro set_ums msrset r0, MSR_UMS msrclr r0, MSR_VMS .endm .macro set_vms msrclr r0, MSR_UMS msrset r0, MSR_VMS .endm .macro clear_ums msrclr r0, MSR_UMS .endm .macro clear_vms_ums msrclr r0, MSR_VMS | MSR_UMS .endm#else .macro clear_bip mfs r11, rmsr andi r11, r11, ~MSR_BIP mts rmsr, r11 .endm .macro set_bip mfs r11, rmsr ori r11, r11, MSR_BIP mts rmsr, r11 .endm .macro clear_eip mfs r11, rmsr andi r11, r11, ~MSR_EIP mts rmsr, r11 .endm .macro set_ee mfs r11, rmsr ori r11, r11, MSR_EE mts rmsr, r11 .endm .macro disable_irq mfs r11, rmsr andi r11, r11, ~MSR_IE mts rmsr, r11 .endm .macro enable_irq mfs r11, rmsr ori r11, r11, MSR_IE mts rmsr, r11 .endm .macro set_ums mfs r11, rmsr ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 .endm .macro set_vms mfs r11, rmsr ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 .endm .macro clear_ums mfs r11, rmsr andni r11, r11, MSR_UMS mts rmsr,r11 .endm .macro clear_vms_ums mfs r11, rmsr andni r11, r11, (MSR_VMS|MSR_UMS) mts rmsr,r11 .endm#endif /* Define how to call high-level functions. With MMU, virtual mode must be * enabled when calling the high-level function. Clobbers R11. * VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL */ /* turn on virtual protected mode save */#define VM_ON \ set_ums; \ rted r0, 2f; \ nop; \2: /* turn off virtual protected mode save and user mode save*/#define VM_OFF \ clear_vms_ums; \ rted r0, TOPHYS(1f); \ nop; \1: #define SAVE_REGS \ swi r2, r1, PT_R2; /* Save SDA */ \ swi r3, r1, PT_R3; \ swi r4, r1, PT_R4; \ swi r5, r1, PT_R5; \ swi r6, r1, PT_R6; \ swi r7, r1, PT_R7; \ swi r8, r1, PT_R8; \ swi r9, r1, PT_R9; \ swi r10, r1, PT_R10; \ swi r11, r1, PT_R11; /* save clobbered regs after rval */\ swi r12, r1, PT_R12; \ swi r13, r1, PT_R13; /* Save SDA2 */ \ swi r14, r1, PT_PC; /* PC, before IRQ/trap */ \ swi r15, r1, PT_R15; /* Save LP */ \ swi r16, r1, PT_R16; \ swi r17, r1, PT_R17; \ swi r18, r1, PT_R18; /* Save asm scratch reg */ \ swi r19, r1, PT_R19; \ swi r20, r1, PT_R20; \ swi r21, r1, PT_R21; \ swi r22, r1, PT_R22; \ swi r23, r1, PT_R23; \ swi r24, r1, PT_R24; \ swi r25, r1, PT_R25; \ swi r26, r1, PT_R26; \ swi r27, r1, PT_R27; \ swi r28, r1, PT_R28; \ swi r29, r1, PT_R29; \ swi r30, r1, PT_R30; \ swi r31, r1, PT_R31; /* Save current task reg */ \ mfs r11, rmsr; /* save MSR */ \ swi r11, r1, PT_MSR; #define RESTORE_REGS \ lwi r11, r1, PT_MSR; \ mts rmsr , r11; \ lwi r2, r1, PT_R2; /* restore SDA */ \ lwi r3, r1, PT_R3; \ lwi r4, r1, PT_R4; \ lwi r5, r1, PT_R5; \ lwi r6, r1, PT_R6; \ lwi r7, r1, PT_R7; \ lwi r8, r1, PT_R8; \ lwi r9, r1, PT_R9; \ lwi r10, r1, PT_R10; \ lwi r11, r1, PT_R11; /* restore clobbered regs after rval */\ lwi r12, r1, PT_R12; \ lwi r13, r1, PT_R13; /* restore SDA2 */ \ lwi r14, r1, PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\ lwi r15, r1, PT_R15; /* restore LP */ \ lwi r16, r1, PT_R16; \ lwi r17, r1, PT_R17; \ lwi r18, r1, PT_R18; /* restore asm scratch reg */ \ lwi r19, r1, PT_R19; \ lwi r20, r1, PT_R20; \ lwi r21, r1, PT_R21; \ lwi r22, r1, PT_R22; \ lwi r23, r1, PT_R23; \ lwi r24, r1, PT_R24; \ lwi r25, r1, PT_R25; \ lwi r26, r1, PT_R26; \ lwi r27, r1, PT_R27; \ lwi r28, r1, PT_R28; \ lwi r29, r1, PT_R29; \ lwi r30, r1, PT_R30; \ lwi r31, r1, PT_R31; /* Restore cur task reg */ #define SAVE_STATE \ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \ /* See if already in kernel mode.*/ \ mfs r1, rmsr; \ andi r1, r1, MSR_UMS; \ bnei r1, 1f; \ /* Kernel-mode state save. */ \ /* Reload kernel stack-ptr. */ \ lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ /* FIXME: I can add these two lines to one */ \ /* tophys(r1,r1); */ \ /* addik r1, r1, -PT_SIZE; */ \ addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \ SAVE_REGS \ brid 2f; \ swi r1, r1, PT_MODE; \1: /* User-mode state save. */ \ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ tophys(r1,r1); \ lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ /* MS these three instructions can be added to one */ \ /* addik r1, r1, THREAD_SIZE; */ \ /* tophys(r1,r1); */ \ /* addik r1, r1, -PT_SIZE; */ \ addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \ SAVE_REGS \ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ swi r11, r1, PT_R1; /* Store user SP. */ \ swi r0, r1, PT_MODE; /* Was in user-mode. */ \ /* MS: I am clearing UMS even in case when I come from kernel space */ \ clear_ums; \2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); .text /* * User trap. * * System calls are handled here. * * Syscall protocol: * Syscall number in r12, args in r5-r10 * Return value in r3 * * Trap entered via brki instruction, so BIP bit is set, and interrupts * are masked. This is nice, means we don't have to CLI before state save */C_ENTRY(_user_exception): swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ addi r14, r14, 4 /* return address is 4 byte after call */ mfs r1, rmsr nop andi r1, r1, MSR_UMS bnei r1, 1f /* Kernel-mode state save - kernel execve */ lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ tophys(r1,r1); addik r1, r1, -PT_SIZE; /* Make room on the stack. */ SAVE_REGS swi r1, r1, PT_MODE; /* pt_regs -> kernel mode */ brid 2f; nop; /* Fill delay slot */ /* User-mode state save. */1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ tophys(r1,r1); lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct *//* calculate kernel stack pointer from task struct 8k */ addik r1, r1, THREAD_SIZE; tophys(r1,r1); addik r1, r1, -PT_SIZE; /* Make room on the stack. */ SAVE_REGS swi r0, r1, PT_R3 swi r0, r1, PT_R4 swi r0, r1, PT_MODE; /* Was in user-mode. */ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); swi r11, r1, PT_R1; /* Store user SP. */ clear_ums;2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* Save away the syscall number. */ swi r12, r1, PT_R0; tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8*//* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. The LP * register should point to the location where * the called function should return. [note that MAKE_SYS_CALL uses label 1] */ /* Step into virtual mode */ rtbd r0, 3f nop3: lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */ lwi r11, r11, TI_FLAGS /* get flags in thread info */ andi r11, r11, _TIF_WORK_SYSCALL_MASK beqi r11, 4f addik r3, r0, -ENOSYS swi r3, r1, PT_R3 brlid r15, do_syscall_trace_enter addik r5, r1, PT_R0 # do_syscall_trace_enter returns the new syscall nr. addk r12, r0, r3 lwi r5, r1, PT_R5; lwi r6, r1, PT_R6; lwi r7, r1, PT_R7; lwi r8, r1, PT_R8; lwi r9, r1, PT_R9; lwi r10, r1, PT_R10;4:/* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. * The LP register should point to the location where the called function * should return. [note that MAKE_SYS_CALL uses label 1] */ /* See if the system call number is valid */ addi r11, r12, -__NR_syscalls; bgei r11,5f; /* Figure out which function to use for this system call. */ /* Note Microblaze barrel shift is optional, so don't rely on it */ add r12, r12, r12; /* convert num -> ptr */ add r12, r12, r12; #ifdef DEBUG /* Trac syscalls and stored them to syscall_debug_table */ /* The first syscall location stores total syscall number */ lwi r3, r0, syscall_debug_table addi r3, r3, 1 swi r3, r0, syscall_debug_table lwi r3, r12, syscall_debug_table addi r3, r3, 1 swi r3, r12, syscall_debug_table#endif # Find and jump into the syscall handler. lwi r12, r12, sys_call_table /* where the trap should return need -8 to adjust for rtsd r15, 8 */ addi r15, r0, ret_from_trap-8 bra r12 /* The syscall number is invalid, return an error. */5: rtsd r15, 8; /* looks like a normal subroutine return */ addi r3, r0, -ENOSYS; /* Entry point used to return from a syscall/trap *//* We re-enable BIP bit before state restore */C_ENTRY(ret_from_trap): swi r3, r1, PT_R3 swi r4, r1, PT_R4 lwi r11, r1, PT_MODE;/* See if returning to kernel mode, if so, skip resched &c. */ bnei r11, 2f; /* We're returning to user mode, so check for various conditions that * trigger rescheduling. */ /* FIXME: Restructure all these flag checks. */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_WORK_SYSCALL_MASK beqi r11, 1f brlid r15, do_syscall_trace_leave addik r5, r1, PT_R01: /* We're returning to user mode, so check for various conditions that * trigger rescheduling. */ /* get thread info from current task */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_NEED_RESCHED; beqi r11, 5f; bralid r15, schedule; /* Call scheduler */ nop; /* delay slot */ /* Maybe handle a signal */5: /* get thread info from current task*/ lwi r11, CURRENT_TASK, TS_THREAD_INFO; lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_SIGPENDING; beqi r11, 1f; /* Signals to handle, handle them */ addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */ addi r7, r0, 1; /* Arg 3: int in_syscall */ bralid r15, do_signal; /* Handle any signals */ add r6, r0, r0; /* Arg 2: sigset_t *oldset */ /* Finally, return to user state. */1: set_bip; /* Ints masked for state restore */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); RESTORE_REGS; addik r1, r1, PT_SIZE /* Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */ bri 6f; /* Return to kernel state. */2: set_bip; /* Ints masked for state restore */ VM_OFF; tophys(r1,r1); RESTORE_REGS; addik r1, r1, PT_SIZE /* Clean up stack space. */ tovirt(r1,r1);6:TRAP_return: /* Make global symbol for debugging */ rtbd r14, 0; /* Instructions to return from an IRQ */ nop; /* These syscalls need access to the struct pt_regs on the stack, so we implement them in assembly (they're basically all wrappers anyway). */ C_ENTRY(sys_fork_wrapper): addi r5, r0, SIGCHLD /* Arg 0: flags */ lwi r6, r1, PT_R1 /* Arg 1: child SP (use parent's) */ addik r7, r1, 0 /* Arg 2: parent context */ add r8. r0, r0 /* Arg 3: (unused) */ add r9, r0, r0; /* Arg 4: (unused) */ brid do_fork /* Do real work (tail-call) */ add r10, r0, r0; /* Arg 5: (unused) */ /* This the initial entry point for a new child thread, with an appropriate stack in place that makes it look the the child is in the middle of an syscall. This function is actually `returned to' from switch_thread (copy_thread makes ret_from_fork the return address in each new thread's saved context). */C_ENTRY(ret_from_fork): bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ add r3, r5, r0; /* switch_thread returns the prev task */ /* ( in the delay slot ) */ brid ret_from_trap; /* Do normal trap return */ add r3, r0, r0; /* Child's fork call should return 0. */ C_ENTRY(sys_vfork): brid microblaze_vfork /* Do real work (tail-call) */ addik r5, r1, 0 C_ENTRY(sys_clone): bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */ lwi r6, r1, PT_R1; /* If so, use paret's stack ptr */1: addik r7, r1, 0; /* Arg 2: parent context */ add r8, r0, r0; /* Arg 3: (unused) */ add r9, r0, r0; /* Arg 4: (unused) */ brid do_fork /* Do real work (tail-call) */ add r10, r0, r0; /* Arg 5: (unused) */ Lines 1–500 of 1040. Load more
/* * Low-level system-call handling, trap handlers and context-switching * * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> * Copyright (C) 2008-2009 PetaLogix * Copyright (C) 2003 John Williams <jwilliams@itee.uq.edu.au> * Copyright (C) 2001,2002 NEC Corporation * Copyright (C) 2001,2002 Miles Bader <miles@gnu.org> * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this * archive for more details. * * Written by Miles Bader <miles@gnu.org> * Heavily modified by John Williams for Microblaze */ #include <linux/sys.h> #include <linux/linkage.h> #include <asm/entry.h> #include <asm/current.h> #include <asm/processor.h> #include <asm/exceptions.h> #include <asm/asm-offsets.h> #include <asm/thread_info.h> #include <asm/page.h> #include <asm/unistd.h> #include <linux/errno.h> #include <asm/signal.h> #undef DEBUG #ifdef DEBUG /* Create space for syscalls counting. */ .section .data .global syscall_debug_table .align 4 syscall_debug_table: .space (__NR_syscalls * 4) #endif /* DEBUG */ #define C_ENTRY(name) .globl name; .align 4; name /* * Various ways of setting and clearing BIP in flags reg. * This is mucky, but necessary using microblaze version that * allows msr ops to write to BIP */ #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR .macro clear_bip msrclr r0, MSR_BIP .endm .macro set_bip msrset r0, MSR_BIP .endm .macro clear_eip msrclr r0, MSR_EIP .endm .macro set_ee msrset r0, MSR_EE .endm .macro disable_irq msrclr r0, MSR_IE .endm .macro enable_irq msrset r0, MSR_IE .endm .macro set_ums msrset r0, MSR_UMS msrclr r0, MSR_VMS .endm .macro set_vms msrclr r0, MSR_UMS msrset r0, MSR_VMS .endm .macro clear_ums msrclr r0, MSR_UMS .endm .macro clear_vms_ums msrclr r0, MSR_VMS | MSR_UMS .endm #else .macro clear_bip mfs r11, rmsr andi r11, r11, ~MSR_BIP mts rmsr, r11 .endm .macro set_bip mfs r11, rmsr ori r11, r11, MSR_BIP mts rmsr, r11 .endm .macro clear_eip mfs r11, rmsr andi r11, r11, ~MSR_EIP mts rmsr, r11 .endm .macro set_ee mfs r11, rmsr ori r11, r11, MSR_EE mts rmsr, r11 .endm .macro disable_irq mfs r11, rmsr andi r11, r11, ~MSR_IE mts rmsr, r11 .endm .macro enable_irq mfs r11, rmsr ori r11, r11, MSR_IE mts rmsr, r11 .endm .macro set_ums mfs r11, rmsr ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 .endm .macro set_vms mfs r11, rmsr ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 .endm .macro clear_ums mfs r11, rmsr andni r11, r11, MSR_UMS mts rmsr,r11 .endm .macro clear_vms_ums mfs r11, rmsr andni r11, r11, (MSR_VMS|MSR_UMS) mts rmsr,r11 .endm #endif /* Define how to call high-level functions. With MMU, virtual mode must be * enabled when calling the high-level function. Clobbers R11. * VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL */ /* turn on virtual protected mode save */ #define VM_ON \ set_ums; \ rted r0, 2f; \ nop; \ 2: /* turn off virtual protected mode save and user mode save*/ #define VM_OFF \ clear_vms_ums; \ rted r0, TOPHYS(1f); \ nop; \ 1: #define SAVE_REGS \ swi r2, r1, PT_R2; /* Save SDA */ \ swi r3, r1, PT_R3; \ swi r4, r1, PT_R4; \ swi r5, r1, PT_R5; \ swi r6, r1, PT_R6; \ swi r7, r1, PT_R7; \ swi r8, r1, PT_R8; \ swi r9, r1, PT_R9; \ swi r10, r1, PT_R10; \ swi r11, r1, PT_R11; /* save clobbered regs after rval */\ swi r12, r1, PT_R12; \ swi r13, r1, PT_R13; /* Save SDA2 */ \ swi r14, r1, PT_PC; /* PC, before IRQ/trap */ \ swi r15, r1, PT_R15; /* Save LP */ \ swi r16, r1, PT_R16; \ swi r17, r1, PT_R17; \ swi r18, r1, PT_R18; /* Save asm scratch reg */ \ swi r19, r1, PT_R19; \ swi r20, r1, PT_R20; \ swi r21, r1, PT_R21; \ swi r22, r1, PT_R22; \ swi r23, r1, PT_R23; \ swi r24, r1, PT_R24; \ swi r25, r1, PT_R25; \ swi r26, r1, PT_R26; \ swi r27, r1, PT_R27; \ swi r28, r1, PT_R28; \ swi r29, r1, PT_R29; \ swi r30, r1, PT_R30; \ swi r31, r1, PT_R31; /* Save current task reg */ \ mfs r11, rmsr; /* save MSR */ \ swi r11, r1, PT_MSR; #define RESTORE_REGS \ lwi r11, r1, PT_MSR; \ mts rmsr , r11; \ lwi r2, r1, PT_R2; /* restore SDA */ \ lwi r3, r1, PT_R3; \ lwi r4, r1, PT_R4; \ lwi r5, r1, PT_R5; \ lwi r6, r1, PT_R6; \ lwi r7, r1, PT_R7; \ lwi r8, r1, PT_R8; \ lwi r9, r1, PT_R9; \ lwi r10, r1, PT_R10; \ lwi r11, r1, PT_R11; /* restore clobbered regs after rval */\ lwi r12, r1, PT_R12; \ lwi r13, r1, PT_R13; /* restore SDA2 */ \ lwi r14, r1, PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\ lwi r15, r1, PT_R15; /* restore LP */ \ lwi r16, r1, PT_R16; \ lwi r17, r1, PT_R17; \ lwi r18, r1, PT_R18; /* restore asm scratch reg */ \ lwi r19, r1, PT_R19; \ lwi r20, r1, PT_R20; \ lwi r21, r1, PT_R21; \ lwi r22, r1, PT_R22; \ lwi r23, r1, PT_R23; \ lwi r24, r1, PT_R24; \ lwi r25, r1, PT_R25; \ lwi r26, r1, PT_R26; \ lwi r27, r1, PT_R27; \ lwi r28, r1, PT_R28; \ lwi r29, r1, PT_R29; \ lwi r30, r1, PT_R30; \ lwi r31, r1, PT_R31; /* Restore cur task reg */ #define SAVE_STATE \ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \ /* See if already in kernel mode.*/ \ mfs r1, rmsr; \ andi r1, r1, MSR_UMS; \ bnei r1, 1f; \ /* Kernel-mode state save. */ \ /* Reload kernel stack-ptr. */ \ lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ /* FIXME: I can add these two lines to one */ \ /* tophys(r1,r1); */ \ /* addik r1, r1, -PT_SIZE; */ \ addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \ SAVE_REGS \ brid 2f; \ swi r1, r1, PT_MODE; \ 1: /* User-mode state save. */ \ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ tophys(r1,r1); \ lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ /* MS these three instructions can be added to one */ \ /* addik r1, r1, THREAD_SIZE; */ \ /* tophys(r1,r1); */ \ /* addik r1, r1, -PT_SIZE; */ \ addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \ SAVE_REGS \ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ swi r11, r1, PT_R1; /* Store user SP. */ \ swi r0, r1, PT_MODE; /* Was in user-mode. */ \ /* MS: I am clearing UMS even in case when I come from kernel space */ \ clear_ums; \ 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); .text /* * User trap. * * System calls are handled here. * * Syscall protocol: * Syscall number in r12, args in r5-r10 * Return value in r3 * * Trap entered via brki instruction, so BIP bit is set, and interrupts * are masked. This is nice, means we don't have to CLI before state save */ C_ENTRY(_user_exception): swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ addi r14, r14, 4 /* return address is 4 byte after call */ mfs r1, rmsr nop andi r1, r1, MSR_UMS bnei r1, 1f /* Kernel-mode state save - kernel execve */ lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ tophys(r1,r1); addik r1, r1, -PT_SIZE; /* Make room on the stack. */ SAVE_REGS swi r1, r1, PT_MODE; /* pt_regs -> kernel mode */ brid 2f; nop; /* Fill delay slot */ /* User-mode state save. */ 1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ tophys(r1,r1); lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */ /* calculate kernel stack pointer from task struct 8k */ addik r1, r1, THREAD_SIZE; tophys(r1,r1); addik r1, r1, -PT_SIZE; /* Make room on the stack. */ SAVE_REGS swi r0, r1, PT_R3 swi r0, r1, PT_R4 swi r0, r1, PT_MODE; /* Was in user-mode. */ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); swi r11, r1, PT_R1; /* Store user SP. */ clear_ums; 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* Save away the syscall number. */ swi r12, r1, PT_R0; tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8*/ /* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. The LP * register should point to the location where * the called function should return. [note that MAKE_SYS_CALL uses label 1] */ /* Step into virtual mode */ rtbd r0, 3f nop 3: lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */ lwi r11, r11, TI_FLAGS /* get flags in thread info */ andi r11, r11, _TIF_WORK_SYSCALL_MASK beqi r11, 4f addik r3, r0, -ENOSYS swi r3, r1, PT_R3 brlid r15, do_syscall_trace_enter addik r5, r1, PT_R0 # do_syscall_trace_enter returns the new syscall nr. addk r12, r0, r3 lwi r5, r1, PT_R5; lwi r6, r1, PT_R6; lwi r7, r1, PT_R7; lwi r8, r1, PT_R8; lwi r9, r1, PT_R9; lwi r10, r1, PT_R10; 4: /* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. * The LP register should point to the location where the called function * should return. [note that MAKE_SYS_CALL uses label 1] */ /* See if the system call number is valid */ addi r11, r12, -__NR_syscalls; bgei r11,5f; /* Figure out which function to use for this system call. */ /* Note Microblaze barrel shift is optional, so don't rely on it */ add r12, r12, r12; /* convert num -> ptr */ add r12, r12, r12; #ifdef DEBUG /* Trac syscalls and stored them to syscall_debug_table */ /* The first syscall location stores total syscall number */ lwi r3, r0, syscall_debug_table addi r3, r3, 1 swi r3, r0, syscall_debug_table lwi r3, r12, syscall_debug_table addi r3, r3, 1 swi r3, r12, syscall_debug_table #endif # Find and jump into the syscall handler. lwi r12, r12, sys_call_table /* where the trap should return need -8 to adjust for rtsd r15, 8 */ addi r15, r0, ret_from_trap-8 bra r12 /* The syscall number is invalid, return an error. */ 5: rtsd r15, 8; /* looks like a normal subroutine return */ addi r3, r0, -ENOSYS; /* Entry point used to return from a syscall/trap */ /* We re-enable BIP bit before state restore */ C_ENTRY(ret_from_trap): swi r3, r1, PT_R3 swi r4, r1, PT_R4 lwi r11, r1, PT_MODE; /* See if returning to kernel mode, if so, skip resched &c. */ bnei r11, 2f; /* We're returning to user mode, so check for various conditions that * trigger rescheduling. */ /* FIXME: Restructure all these flag checks. */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_WORK_SYSCALL_MASK beqi r11, 1f brlid r15, do_syscall_trace_leave addik r5, r1, PT_R0 1: /* We're returning to user mode, so check for various conditions that * trigger rescheduling. */ /* get thread info from current task */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_NEED_RESCHED; beqi r11, 5f; bralid r15, schedule; /* Call scheduler */ nop; /* delay slot */ /* Maybe handle a signal */ 5: /* get thread info from current task*/ lwi r11, CURRENT_TASK, TS_THREAD_INFO; lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_SIGPENDING; beqi r11, 1f; /* Signals to handle, handle them */ addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */ addi r7, r0, 1; /* Arg 3: int in_syscall */ bralid r15, do_signal; /* Handle any signals */ add r6, r0, r0; /* Arg 2: sigset_t *oldset */ /* Finally, return to user state. */ 1: set_bip; /* Ints masked for state restore */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); RESTORE_REGS; addik r1, r1, PT_SIZE /* Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */ bri 6f; /* Return to kernel state. */ 2: set_bip; /* Ints masked for state restore */ VM_OFF; tophys(r1,r1); RESTORE_REGS; addik r1, r1, PT_SIZE /* Clean up stack space. */ tovirt(r1,r1); 6: TRAP_return: /* Make global symbol for debugging */ rtbd r14, 0; /* Instructions to return from an IRQ */ nop; /* These syscalls need access to the struct pt_regs on the stack, so we implement them in assembly (they're basically all wrappers anyway). */ C_ENTRY(sys_fork_wrapper): addi r5, r0, SIGCHLD /* Arg 0: flags */ lwi r6, r1, PT_R1 /* Arg 1: child SP (use parent's) */ addik r7, r1, 0 /* Arg 2: parent context */ add r8. r0, r0 /* Arg 3: (unused) */ add r9, r0, r0; /* Arg 4: (unused) */ brid do_fork /* Do real work (tail-call) */ add r10, r0, r0; /* Arg 5: (unused) */ /* This the initial entry point for a new child thread, with an appropriate stack in place that makes it look the the child is in the middle of an syscall. This function is actually `returned to' from switch_thread (copy_thread makes ret_from_fork the return address in each new thread's saved context). */ C_ENTRY(ret_from_fork): bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ add r3, r5, r0; /* switch_thread returns the prev task */ /* ( in the delay slot ) */ brid ret_from_trap; /* Do normal trap return */ add r3, r0, r0; /* Child's fork call should return 0. */ C_ENTRY(sys_vfork): brid microblaze_vfork /* Do real work (tail-call) */ addik r5, r1, 0 C_ENTRY(sys_clone): bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */ lwi r6, r1, PT_R1; /* If so, use paret's stack ptr */ 1: addik r7, r1, 0; /* Arg 2: parent context */ add r8, r0, r0; /* Arg 3: (unused) */ add r9, r0, r0; /* Arg 4: (unused) */ brid do_fork /* Do real work (tail-call) */ add r10, r0, r0; /* Arg 5: (unused) */