diff options
Diffstat (limited to 'target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S')
-rw-r--r-- | target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S | 694 |
1 files changed, 0 insertions, 694 deletions
diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S b/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S deleted file mode 100644 index 870f66c8f4..0000000000 --- a/target/linux/ubicom32/files/arch/ubicom32/kernel/ubicom32_syscall.S +++ /dev/null @@ -1,694 +0,0 @@ -/* - * arch/ubicom32/kernel/ubicom32_syscall.S - * <TODO: Replace with short file description> - * - * (C) Copyright 2009, Ubicom, Inc. - * - * This file is part of the Ubicom32 Linux Kernel Port. - * - * The Ubicom32 Linux Kernel Port is free software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * The Ubicom32 Linux Kernel Port is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Ubicom32 Linux Kernel Port. If not, - * see <http://www.gnu.org/licenses/>. - * - * Ubicom32 implementation derived from (with many thanks): - * arch/m68knommu - * arch/blackfin - * arch/parisc - */ -#include <linux/sys.h> -#include <linux/linkage.h> -#include <linux/unistd.h> - -#include <asm/ubicom32-common.h> -#include <asm/thread_info.h> -#include <asm/asm-offsets.h> -#include <asm/range-protect.h> - -/* - * __old_system_call() - */ - .section .old_syscall_entry.text, "ax", @progbits -#ifdef CONFIG_OLD_40400010_SYSTEM_CALL -__old_system_call: - call a3, system_call - .size __old_system_call, . - __old_system_call ; -#else - /* - * something that will crash the userspace application, but - * should not take down the kernel, if protection is enabled - * this will never even get executed. - */ - .long 0xFABBCCDE ; illegal instruction - bkpt #-1 ; we will never get here -#endif - -/* - * system_call() - */ - .section .syscall_entry.text, "ax", @progbits - .global system_call -system_call: - /* - * Regular ABI rules for function calls apply for syscall. d8 holds - * the syscall number. We will use that to index into the syscall table. - * d0 - d5 hold the parameters. - * - * First we get the current thread_info and swap to the kernel stack. - * This is done by reading the current thread and looking up the ksp - * from the sw_ksp array and storing it in a3. - * - * Then we reserve space for the syscall context a struct pt_regs and - * save it using a4 initially and later as sp. - * Once sp is set to the kernel sp we can leave the critical section. - * - * For the user case the kernel stack will have the following layout. - * - * a3 ksp[0] +-----------------------+ - * | Thread info area | - * | struct thread_info | - * +-----------------------+ - * : : - * | Kernel Stack Area | - * | | - * a4 / sp >>> +-----------------------+ - * | Context save area | - * | struct pt_reg | - * ksp[THREAD_SIZE-8] +-----------------------+ - * | 8 Byte Buffer Zone | - * ksp[THREAD_SIZE] +-----------------------+ - - * - * For kernel syscalls the layout is as follows. - * - * a3 ksp[0] +-----------------------+ - * | Thread info area | - * | struct thread_info | - * +-----------------------+ - * : : - * | Kernel Stack Area | - * | | - * a4 / sp >>> +-----------------------+ - * | Context save area | - * | struct pt_reg | - * sp at syscall entry +-----------------------+ - * | Callers Kernel Stack | - * : : - * - * Once the context is saved we optionally call syscall_trace and setup - * the exit routine and jump to the syscall. - */ - - /* - * load the base address for sw_ksp into a3 - * Note.. we cannot access it just yet as protection is still on. - */ - moveai a3, #%hi(sw_ksp) - lea.1 a3, %lo(sw_ksp)(a3) - - /* - * Enter critical section . - * - * The 'critical' aspects here are the switching the to the ksp and - * changing the protection registers, these both use per thread - * information so we need to protect from a context switch. For now this - * is done using the global atomic lock. - */ - atomic_lock_acquire - - thread_get_self d15 ; Load current thread number -#ifdef CONFIG_PROTECT_KERNEL - lsl.4 d9, #1, d15 ; Convert to thread bit - enable_kernel_ranges d9 -#endif - /* - * in order to reduce the size of code in the syscall section we get - * out of it right now - */ - call a4, __system_call_bottom_half - .size system_call, . - system_call - - .section .text.__system_call_bottom_half, "ax", @progbits -__system_call_bottom_half: - - /* - * We need to Determine if this is a kernel syscall or user syscall. - * Start by loading the pointer for the thread_info structure for the - * current process in to a3. - */ - move.4 a3, (a3, d15) ; a3 = sw_ksp[d15] - - /* - * Now if this is a kernel thread the same value can be a acheived by - * masking off the lower bits on the current stack pointer. - */ - movei d9, #(~(ASM_THREAD_SIZE-1)) ; load mask - and.4 d9, sp, d9 ; apply mask - - /* - * d9 now has the masked version of the sp. If this is identical to - * what is in a3 then don't switch to ksp as we are already in the - * kernel. - */ - sub.4 #0, a3, d9 - - /* - * if d9 and a3 are not equal. We are usespace and have to shift to - * ksp. - */ - jmpne.t 1f - - /* - * Kernel Syscall. - * - * The kernel has called this routine. We have to pdec space for pt_regs - * from sp. - */ - pdec a4, PT_SIZE(sp) ; a4 = ksp - PT_SIZE - jmpt.t 2f - - /* - * Userspace Syscall. - * - * Add THREAD_SIZE and subtract PT_SIZE to create the proper ksp - */ -1: movei d15, #(ASM_THREAD_SIZE - 8 - PT_SIZE) - lea.1 a4, (a3, d15) ; a4 = ksp + d15 - - /* - * Replace user stack pointer with kernel stack pointer (a4) - * Load -1 into frame_type in save area to indicate this is system call - * frame. - */ -2: move.4 PT_A7(a4), a7 ; Save old sp/A7 on kernel stack - move.4 PT_FRAME_TYPE(a4), #-1 ; Set the frame type. - move.4 sp, a4 ; Change to ksp. - /* - * We are now officially back in the kernel! - */ - - /* - * Now that we are on the ksp we can leave the critical section - */ - atomic_lock_release - - /* - * We need to save a0 because we need to be able to restore it in - * the event that we need to handle a signal. It's not generally - * a callee-saved register but is the GOT pointer. - */ - move.4 PT_A0(sp), a0 ; Save A0 on kernel stack - - /* - * We still need to save d10-d13, a1, a2, a5, a6 in the kernel frame - * for this process, we also save the system call params in the case of - * syscall restart. (note a7 was saved above) - */ - move.4 PT_A1(sp), a1 ; Save A1 on kernel stack - move.4 PT_A2(sp), a2 ; Save A2 on kernel stack - move.4 PT_A5(sp), a5 ; Save A5 on kernel stack - move.4 PT_A6(sp), a6 ; Save A6 on kernel stack - move.4 PT_PC(sp), a5 ; Save A5 at the PC location - move.4 PT_D10(sp), d10 ; Save D10 on kernel stack - move.4 PT_D11(sp), d11 ; Save D11 on kernel stack - move.4 PT_D12(sp), d12 ; Save D12 on kernel stack - move.4 PT_D13(sp), d13 ; Save D13 on kernel stack - - /* - * Now save the syscall parameters - */ - move.4 PT_D0(sp), d0 ; Save d0 on kernel stack - move.4 PT_ORIGINAL_D0(sp), d0 ; Save d0 on kernel stack - move.4 PT_D1(sp), d1 ; Save d1 on kernel stack - move.4 PT_D2(sp), d2 ; Save d2 on kernel stack - move.4 PT_D3(sp), d3 ; Save d3 on kernel stack - move.4 PT_D4(sp), d4 ; Save d4 on kernel stack - move.4 PT_D5(sp), d5 ; Save d5 on kernel stack - move.4 PT_D8(sp), d8 ; Save d8 on kernel stack - - /* - * Test if syscalls are being traced and if they are jump to syscall - * trace (it will comeback here) - */ - btst TI_FLAGS(a3), #ASM_TIF_SYSCALL_TRACE - jmpne.f .Lsystem_call__trace -.Lsystem_call__trace_complete: - /* - * Check for a valid call number [ 0 <= syscall_number < NR_syscalls ] - */ - cmpi d8, #0 - jmplt.f 3f - cmpi d8, #NR_syscalls - jmplt.t 4f - - /* - * They have passed an invalid number. Call sys_ni_syscall staring by - * load a4 with the base address of sys_ni_syscall - */ -3: moveai a4, #%hi(sys_ni_syscall) - lea.1 a4, %lo(sys_ni_syscall)(a4) - jmpt.t 5f ; Jump to regular processing - - /* - * Validated syscall, load the syscall table base address into a3 and - * read the syscall ptr out. - */ -4: moveai a3, #%hi(sys_call_table) - lea.1 a3, %lo(sys_call_table)(a3) ; a3 = sys_call_table - move.4 a4, (a3, d8) ; a4 = sys_call_table[d8] - - /* - * Before calling the syscall, setup a5 so that syscall_exit is called - * on return from syscall - */ -5: moveai a5, #%hi(syscall_exit) ; Setup return address - lea.1 a5, %lo(syscall_exit)(a5) ; from system call - - /* - * If the syscall is __NR_rt_rigreturn then we have to test d1 to - * figure out if we have to change change the return routine to restore - * all registers. - */ - cmpi d8, #__NR_rt_sigreturn - jmpeq.f 6f - - /* - * Launch system call (it will return through a5 - syscall_exit) - */ - calli a3, 0(a4) - - /* - * System call is rt_sigreturn. Test d1. If it is 1 we have to - * change the return address to restore_all_registers - */ -6: cmpi d1, #1 - jmpne.t 7f - - moveai a5, #%hi(restore_all_registers) ; Setup return address - lea.1 a5, %lo(restore_all_registers)(a5) ; to restore_all_registers. - - /* - * Launch system call (it will return through a5) - */ -7: calli a3, 0(a4) ; Launch system call - -.Lsystem_call__trace: - /* - * Syscalls are being traced. - * Call syscall_trace, (return here) - */ - call a5, syscall_trace - - /* - * Restore syscall state (it would have been discarded during the - * syscall trace) - */ - move.4 d0, PT_D0(sp) ; Restore d0 from kernel stack - move.4 d1, PT_D1(sp) ; Restore d1 from kernel stack - move.4 d2, PT_D2(sp) ; Restore d2 from kernel stack - move.4 d3, PT_D3(sp) ; Restore d3 from kernel stack - move.4 d4, PT_D4(sp) ; Restore d4 from kernel stack - move.4 d5, PT_D5(sp) ; Restore d5 from kernel stack - /* add this back if we ever have a syscall with 7 args */ - move.4 d8, PT_D8(sp) ; Restore d8 from kernel stack - - /* - * return to syscall - */ - jmpt.t .Lsystem_call__trace_complete - .size __system_call_bottom_half, . - __system_call_bottom_half - -/* - * syscall_exit() - */ - .section .text.syscall_exit - .global syscall_exit -syscall_exit: - /* - * d0 contains the return value. We should move that into the kernel - * stack d0 location. We will be transitioning from kernel to user - * mode. Test the flags and see if we have to call schedule. If we are - * going to truly exit then all that has to be done is that from the - * kernel stack we have to restore d0, a0, a1, a2, a5, a6 and sp (a7)bb - * and then return via a5. - */ - - /* - * Save d0 to pt_regs - */ - move.4 PT_D0(sp), d0 ; Save d0 into the kernel stack - - /* - * load the thread_info structure by masking off the THREAD_SIZE - * bits. - * - * Note: we used to push a1, but now we don't as we are going - * to eventually restore it to the userspace a1. - */ - movei d9, #(~(ASM_THREAD_SIZE-1)) - and.4 a1, sp, d9 - - /* - * Are any interesting bits set on TI flags, if there are jump - * aside to post_processing. - */ - move.4 d9, #(_TIF_SYSCALL_TRACE | _TIF_NEED_RESCHED | _TIF_SIGPENDING) - and.4 #0, TI_FLAGS(a1), d9 - jmpne.f .Lsyscall_exit__post_processing ; jump to handler -.Lsyscall_exit__post_processing_complete: - - move.4 d0, PT_D0(sp) ; Restore D0 from kernel stack - move.4 d1, PT_D1(sp) ; Restore d1 from kernel stack - move.4 d2, PT_D2(sp) ; Restore d2 from kernel stack - move.4 d3, PT_D3(sp) ; Restore d3 from kernel stack - move.4 d4, PT_D4(sp) ; Restore d4 from kernel stack - move.4 d5, PT_D5(sp) ; Restore d5 from kernel stack - move.4 d8, PT_D8(sp) ; Restore d8 from kernel stack - move.4 d10, PT_D10(sp) ; Restore d10 from kernel stack - move.4 d11, PT_D11(sp) ; Restore d11 from kernel stack - move.4 d12, PT_D12(sp) ; Restore d12 from kernel stack - move.4 d13, PT_D13(sp) ; Restore d13 from kernel stack - move.4 a1, PT_A1(sp) ; Restore A1 from kernel stack - move.4 a2, PT_A2(sp) ; Restore A2 from kernel stack - move.4 a5, PT_A5(sp) ; Restore A5 from kernel stack - move.4 a6, PT_A6(sp) ; Restore A6 from kernel stack - move.4 a0, PT_A0(sp) ; Restore A6 from kernel stack - - /* - * this is only for debug, and could be removed for production builds - */ - move.4 PT_FRAME_TYPE(sp), #0 ; invalidate frame_type - -#ifdef CONFIG_PROTECT_KERNEL - - call a4, __syscall_exit_bottom_half - - .section .kernel_unprotected, "ax", @progbits -__syscall_exit_bottom_half: - /* - * Enter critical section - */ - atomic_lock_acquire - disable_kernel_ranges_for_current d15 -#endif - /* - * Lastly restore userspace stack ptr - * - * Note: that when protection is on we need to hold the lock around the - * stack swap as well because otherwise the protection could get - * inadvertently disabled again at the end of a context switch. - */ - move.4 a7, PT_A7(sp) ; Restore A7 from kernel stack - - /* - * We are now officially back in userspace! - */ - -#ifdef CONFIG_PROTECT_KERNEL - /* - * Leave critical section and return to user space. - */ - atomic_lock_release -#endif - calli a5, 0(a5) ; Back to userspace code. - - bkpt #-1 ; we will never get here - - /* - * Post syscall processing. (unlikely part of syscall_exit) - * - * Are we tracing syscalls. If TIF_SYSCALL_TRACE is set, call - * syscall_trace routine and return here. - */ - .section .text.syscall_exit, "ax", @progbits -.Lsyscall_exit__post_processing: - btst TI_FLAGS(a1), #ASM_TIF_SYSCALL_TRACE - jmpeq.t 1f - call a5, syscall_trace - - /* - * Do we need to resched ie call schedule. If TIF_NEED_RESCHED is set, - * call the scheduler, it will come back here. - */ -1: btst TI_FLAGS(a1), #ASM_TIF_NEED_RESCHED - jmpeq.t 2f - call a5, schedule - - /* - * Do we need to post a signal, if TIF_SIGPENDING is set call the - * do_signal. - */ -2: btst TI_FLAGS(a1), #ASM_TIF_SIGPENDING - jmpeq.t .Lsyscall_exit__post_processing_complete - - /* - * setup the do signal call - */ - move.4 d0, #0 ; oldset pointer is NULL - lea.1 d1, (sp) ; d1 is the regs pointer. - call a5, do_signal - - jmpt.t .Lsyscall_exit__post_processing_complete - -/* .size syscall_exit, . - syscall_exit */ - -/* - * kernel_execve() - * kernel_execv is called when we the kernel is starting a - * userspace application. - */ - .section .kernel_unprotected, "ax", @progbits - .global kernel_execve -kernel_execve: - move.4 -4(sp)++, a5 ; Save return address - /* - * Call execve - */ - movei d8, #__NR_execve ; call execve - call a5, system_call - move.4 a5, (sp)4++ - - /* - * protection was enabled again at syscall exit, but we want - * to return to kernel so we enable it again. - */ -#ifdef CONFIG_PROTECT_KERNEL - /* - * We are entering the kernel so we need to disable the protection. - * Enter critical section, disable ranges and leave critical section. - */ - call a3, __enable_kernel_ranges ; and jump back to kernel -#else - ret a5 ; jump back to the kernel -#endif - - .size kernel_execve, . - kernel_execve - -/* - * signal_trampoline() - * - * Deals with transitioning from to userspace signal handlers and returning - * to userspace, only called from the kernel. - * - */ - .section .kernel_unprotected, "ax", @progbits - .global signal_trampoline -signal_trampoline: - /* - * signal_trampoline is called when we are jumping from the kernel to - * the userspace signal handler. - * - * The following registers are relevant. (set setup_rt_frame) - * sp is the user space stack not the kernel stack - * d0 = signal number - * d1 = siginfo_t * - * d2 = ucontext * - * d3 = the user space signal handler - * a0 is set to the GOT if userspace application is FDPIC, otherwise 0 - * a3 is set to the FD for the signal if userspace application is FDPIC - */ -#ifdef CONFIG_PROTECT_KERNEL - /* - * We are leaving the kernel so we need to enable the protection. - * Enter critical section, disable ranges and leave critical section. - */ - atomic_lock_acquire ; Enter critical section - disable_kernel_ranges_for_current d15 ; disable kernel ranges - atomic_lock_release ; Leave critical section -#endif - /* - * The signal handler pointer is in register d3 so tranfer it to a4 and - * call it - */ - movea a4, d3 ; signal handler - calli a5, 0(a4) - - /* - * Return to userspace through rt_syscall which is stored on top of the - * stack d1 contains ret_via_interrupt status. - */ - move.4 d8, (sp) ; d8 (syscall #) = rt_syscall - move.4 d1, 4(sp) ; d1 = ret_via_interrupt - call a5, system_call ; as we are 'in' the kernel - ; we can call kernel_syscall - - bkpt #-1 ; will never get here. - .size signal_trampoline, . - signal_trampoline - -/* - * kernel_thread_helper() - * - * Entry point for kernel threads (only referenced by kernel_thread()). - * - * On execution d0 will be 0, d1 will be the argument to be passed to the - * kernel function. - * d2 contains the kernel function that needs to get called. - * d3 will contain address to do_exit which needs to get moved into a5. - * - * On return from fork the child thread d0 will be 0. We call this dummy - * function which in turn loads the argument - */ - .section .kernel_unprotected, "ax", @progbits - .global kernel_thread_helper -kernel_thread_helper: - /* - * Create a kernel thread. This is called from ret_from_vfork (a - * userspace return routine) so we need to put it in an unprotected - * section and re-enable protection before calling the vector in d2. - */ - -#ifdef CONFIG_PROTECT_KERNEL - /* - * We are entering the kernel so we need to disable the protection. - * Enter critical section, disable ranges and leave critical section. - */ - call a5, __enable_kernel_ranges -#endif - /* - * Move argument for kernel function into d0, and set a5 return address - * (a5) to do_exit and return through a2 - */ - move.4 d0, d1 ; d0 = arg - move.4 a5, d3 ; a5 = do_exit - ret d2 ; call function ptr in d2 - .size kernel_thread_helper, . - kernel_thread_helper - -#ifdef CONFIG_PROTECT_KERNEL - .section .kernel_unprotected, "ax", @progbits -__enable_kernel_ranges: - atomic_lock_acquire ; Enter critical section - enable_kernel_ranges_for_current d15 - atomic_lock_release ; Leave critical section - calli a5, 0(a5) - .size __enable_kernel_ranges, . - __enable_kernel_ranges - -#endif - -/* - * The following system call intercept functions where we setup the - * input to the real system call. In all cases these are just taking - * the current sp which is pointing to pt_regs and pushing it into the - * last arg of the system call. - * - * i.e. the public definition of sys_execv is - * sys_execve( char *name, - * char **argv, - * char **envp ) - * but process.c defines it as - * sys_execve( char *name, - * char **argv, - * char **envp, - * struct pt_regs *regs ) - * - * so execve_intercept needs to populate the 4th arg with pt_regs*, - * which is the stack pointer as we know we must be coming out of - * system_call - * - * The intercept vectors are referenced by syscalltable.S - */ - -/* - * execve_intercept() - */ - .section .text.execve_intercept, "ax", @progbits - .global execve_intercept -execve_intercept: - move.4 d3, sp ; Save pt_regs address - call a3, sys_execve - - .size execve_intercept, . - execve_intercept - -/* - * vfork_intercept() - */ - .section .text.vfork_intercept, "ax", @progbits - .global vfork_intercept -vfork_intercept: - move.4 d0, sp ; Save pt_regs address - call a3, sys_vfork - - .size vfork_intercept, . - vfork_intercept - -/* - * clone_intercept() - */ - .section .text.clone_intercept, "ax", @progbits - .global clone_intercept -clone_intercept: - move.4 d2, sp ; Save pt_regs address - call a3, sys_clone - - .size clone_intercept, . - clone_intercept - -/* - * sys_sigsuspend() - */ - .section .text.sigclone_intercept, "ax", @progbits - .global sys_sigsuspend -sys_sigsuspend: - move.4 d0, sp ; Pass pointer to pt_regs in d0 - call a3, do_sigsuspend - - .size sys_sigsuspend, . - sys_sigsuspend - -/* - * sys_rt_sigsuspend() - */ - .section .text.sys_rt_sigsuspend, "ax", @progbits - .global sys_rt_sigsuspend -sys_rt_sigsuspend: - move.4 d0, sp ; Pass pointer to pt_regs in d0 - call a3, do_rt_sigsuspend - - .size sys_rt_sigsuspend, . - sys_rt_sigsuspend - -/* - * sys_rt_sigreturn() - */ - .section .text.sys_rt_sigreturn, "ax", @progbits - .global sys_rt_sigreturn -sys_rt_sigreturn: - move.4 d0, sp ; Pass pointer to pt_regs in d0 - call a3, do_rt_sigreturn - - .size sys_rt_sigreturn, . - sys_rt_sigreturn - -/* - * sys_sigaltstack() - */ - .section .text.sys_sigaltstack, "ax", @progbits - .global sys_sigaltstack -sys_sigaltstack: - move.4 d0, sp ; Pass pointer to pt_regs in d0 - call a3, do_sys_sigaltstack - - .size sys_sigaltstack, . - sys_sigaltstack |