Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

metag: System Calls

Add metag system call and gateway page interfaces. The metag
architecture port uses the generic system call numbers from
asm-generic/unistd.h, as well as a user gateway page mapped at
0x6ffff000 which contains fast atomic primitives (depending on SMP) and
a fast method of accessing TLS data.

System calls use the SWITCH instruction with the immediate 0x440001 to
signal a system call.

Signed-off-by: James Hogan <james.hogan@imgtec.com>

+508
+11
arch/metag/include/asm/mman.h
··· 1 + #ifndef __METAG_MMAN_H__ 2 + #define __METAG_MMAN_H__ 3 + 4 + #include <uapi/asm/mman.h> 5 + 6 + #ifndef __ASSEMBLY__ 7 + #define arch_mmap_check metag_mmap_check 8 + int metag_mmap_check(unsigned long addr, unsigned long len, 9 + unsigned long flags); 10 + #endif 11 + #endif /* __METAG_MMAN_H__ */
+104
arch/metag/include/asm/syscall.h
··· 1 + /* 2 + * Access to user system call parameters and results 3 + * 4 + * Copyright (C) 2008 Imagination Technologies Ltd. 5 + * 6 + * This copyrighted material is made available to anyone wishing to use, 7 + * modify, copy, or redistribute it subject to the terms and conditions 8 + * of the GNU General Public License v.2. 9 + * 10 + * See asm-generic/syscall.h for descriptions of what we must do here. 11 + */ 12 + 13 + #ifndef _ASM_METAG_SYSCALL_H 14 + #define _ASM_METAG_SYSCALL_H 15 + 16 + #include <linux/sched.h> 17 + #include <linux/err.h> 18 + #include <linux/uaccess.h> 19 + 20 + #include <asm/switch.h> 21 + 22 + static inline long syscall_get_nr(struct task_struct *task, 23 + struct pt_regs *regs) 24 + { 25 + unsigned long insn; 26 + 27 + /* 28 + * FIXME there's no way to find out how we got here other than to 29 + * examine the memory at the PC to see if it is a syscall 30 + * SWITCH instruction. 31 + */ 32 + if (get_user(insn, (unsigned long *)(regs->ctx.CurrPC - 4))) 33 + return -1; 34 + 35 + if (insn == __METAG_SW_ENCODING(SYS)) 36 + return regs->ctx.DX[0].U1; 37 + else 38 + return -1L; 39 + } 40 + 41 + static inline void syscall_rollback(struct task_struct *task, 42 + struct pt_regs *regs) 43 + { 44 + /* do nothing */ 45 + } 46 + 47 + static inline long syscall_get_error(struct task_struct *task, 48 + struct pt_regs *regs) 49 + { 50 + unsigned long error = regs->ctx.DX[0].U0; 51 + return IS_ERR_VALUE(error) ? error : 0; 52 + } 53 + 54 + static inline long syscall_get_return_value(struct task_struct *task, 55 + struct pt_regs *regs) 56 + { 57 + return regs->ctx.DX[0].U0; 58 + } 59 + 60 + static inline void syscall_set_return_value(struct task_struct *task, 61 + struct pt_regs *regs, 62 + int error, long val) 63 + { 64 + regs->ctx.DX[0].U0 = (long) error ?: val; 65 + } 66 + 67 + static inline void syscall_get_arguments(struct task_struct *task, 68 + struct pt_regs *regs, 69 + unsigned int i, unsigned int n, 70 + unsigned long *args) 71 + { 72 + unsigned int reg, j; 73 + BUG_ON(i + n > 6); 74 + 75 + for (j = i, reg = 6 - i; j < (i + n); j++, reg--) { 76 + if (reg % 2) 77 + args[j] = regs->ctx.DX[(reg + 1) / 2].U0; 78 + else 79 + args[j] = regs->ctx.DX[reg / 2].U1; 80 + } 81 + } 82 + 83 + static inline void syscall_set_arguments(struct task_struct *task, 84 + struct pt_regs *regs, 85 + unsigned int i, unsigned int n, 86 + const unsigned long *args) 87 + { 88 + unsigned int reg; 89 + BUG_ON(i + n > 6); 90 + 91 + for (reg = 6 - i; i < (i + n); i++, reg--) { 92 + if (reg % 2) 93 + regs->ctx.DX[(reg + 1) / 2].U0 = args[i]; 94 + else 95 + regs->ctx.DX[reg / 2].U1 = args[i]; 96 + } 97 + } 98 + 99 + #define NR_syscalls __NR_syscalls 100 + 101 + /* generic syscall table */ 102 + extern const void *sys_call_table[]; 103 + 104 + #endif /* _ASM_METAG_SYSCALL_H */
+39
arch/metag/include/asm/syscalls.h
··· 1 + #ifndef _ASM_METAG_SYSCALLS_H 2 + #define _ASM_METAG_SYSCALLS_H 3 + 4 + #include <linux/compiler.h> 5 + #include <linux/linkage.h> 6 + #include <linux/types.h> 7 + #include <linux/signal.h> 8 + 9 + /* kernel/signal.c */ 10 + #define sys_rt_sigreturn sys_rt_sigreturn 11 + asmlinkage long sys_rt_sigreturn(void); 12 + 13 + #include <asm-generic/syscalls.h> 14 + 15 + /* kernel/sys_metag.c */ 16 + asmlinkage int sys_metag_setglobalbit(char __user *, int); 17 + asmlinkage void sys_metag_set_fpu_flags(unsigned int); 18 + asmlinkage int sys_metag_set_tls(void __user *); 19 + asmlinkage void *sys_metag_get_tls(void); 20 + 21 + asmlinkage long sys_truncate64_metag(const char __user *, unsigned long, 22 + unsigned long); 23 + asmlinkage long sys_ftruncate64_metag(unsigned int, unsigned long, 24 + unsigned long); 25 + asmlinkage long sys_fadvise64_64_metag(int, unsigned long, unsigned long, 26 + unsigned long, unsigned long, int); 27 + asmlinkage long sys_readahead_metag(int, unsigned long, unsigned long, size_t); 28 + asmlinkage ssize_t sys_pread64_metag(unsigned long, char __user *, size_t, 29 + unsigned long, unsigned long); 30 + asmlinkage ssize_t sys_pwrite64_metag(unsigned long, char __user *, size_t, 31 + unsigned long, unsigned long); 32 + asmlinkage long sys_sync_file_range_metag(int, unsigned long, unsigned long, 33 + unsigned long, unsigned long, 34 + unsigned int); 35 + 36 + int do_work_pending(struct pt_regs *regs, unsigned int thread_flags, 37 + int syscall); 38 + 39 + #endif /* _ASM_METAG_SYSCALLS_H */
+12
arch/metag/include/asm/unistd.h
··· 1 + /* 2 + * Copyright (C) 2012 Imagination Technologies Ltd. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #include <uapi/asm/unistd.h> 11 + 12 + #define __ARCH_WANT_SYS_CLONE
+44
arch/metag/include/asm/user_gateway.h
··· 1 + /* 2 + * Copyright (C) 2010 Imagination Technologies 3 + */ 4 + 5 + #ifndef __ASM_METAG_USER_GATEWAY_H 6 + #define __ASM_METAG_USER_GATEWAY_H 7 + 8 + #include <asm/page.h> 9 + 10 + /* Page of kernel code accessible to userspace. */ 11 + #define USER_GATEWAY_PAGE 0x6ffff000 12 + /* Offset of TLS pointer array in gateway page. */ 13 + #define USER_GATEWAY_TLS 0x100 14 + 15 + #ifndef __ASSEMBLY__ 16 + 17 + extern char __user_gateway_start; 18 + extern char __user_gateway_end; 19 + 20 + /* Kernel mapping of the gateway page. */ 21 + extern void *gateway_page; 22 + 23 + static inline void set_gateway_tls(void __user *tls_ptr) 24 + { 25 + void **gateway_tls = (void **)(gateway_page + USER_GATEWAY_TLS + 26 + hard_processor_id() * 4); 27 + 28 + *gateway_tls = (__force void *)tls_ptr; 29 + #ifdef CONFIG_METAG_META12 30 + /* Avoid cache aliases on virtually tagged cache. */ 31 + __builtin_dcache_flush((void *)USER_GATEWAY_PAGE + USER_GATEWAY_TLS + 32 + hard_processor_id() * sizeof(void *)); 33 + #endif 34 + } 35 + 36 + extern int __kuser_get_tls(void); 37 + extern char *__kuser_get_tls_end[]; 38 + 39 + extern int __kuser_cmpxchg(int, int, unsigned long *); 40 + extern char *__kuser_cmpxchg_end[]; 41 + 42 + #endif 43 + 44 + #endif
+21
arch/metag/include/uapi/asm/unistd.h
··· 1 + /* 2 + * Copyright (C) 2012 Imagination Technologies Ltd. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + /* Use the standard ABI for syscalls. */ 11 + #include <asm-generic/unistd.h> 12 + 13 + /* metag-specific syscalls. */ 14 + #define __NR_metag_setglobalbit (__NR_arch_specific_syscall + 1) 15 + __SYSCALL(__NR_metag_setglobalbit, sys_metag_setglobalbit) 16 + #define __NR_metag_set_fpu_flags (__NR_arch_specific_syscall + 2) 17 + __SYSCALL(__NR_metag_set_fpu_flags, sys_metag_set_fpu_flags) 18 + #define __NR_metag_set_tls (__NR_arch_specific_syscall + 3) 19 + __SYSCALL(__NR_metag_set_tls, sys_metag_set_tls) 20 + #define __NR_metag_get_tls (__NR_arch_specific_syscall + 4) 21 + __SYSCALL(__NR_metag_get_tls, sys_metag_get_tls)
+180
arch/metag/kernel/sys_metag.c
··· 1 + /* 2 + * This file contains various random system calls that 3 + * have a non-standard calling sequence on the Linux/Meta 4 + * platform. 5 + */ 6 + 7 + #include <linux/errno.h> 8 + #include <linux/sched.h> 9 + #include <linux/mm.h> 10 + #include <linux/syscalls.h> 11 + #include <linux/mman.h> 12 + #include <linux/file.h> 13 + #include <linux/fs.h> 14 + #include <linux/uaccess.h> 15 + #include <linux/unistd.h> 16 + #include <asm/cacheflush.h> 17 + #include <asm/core_reg.h> 18 + #include <asm/global_lock.h> 19 + #include <asm/switch.h> 20 + #include <asm/syscall.h> 21 + #include <asm/syscalls.h> 22 + #include <asm/user_gateway.h> 23 + 24 + #define merge_64(hi, lo) ((((unsigned long long)(hi)) << 32) + \ 25 + ((lo) & 0xffffffffUL)) 26 + 27 + int metag_mmap_check(unsigned long addr, unsigned long len, 28 + unsigned long flags) 29 + { 30 + /* We can't have people trying to write to the bottom of the 31 + * memory map, there are mysterious unspecified things there that 32 + * we don't want people trampling on. 33 + */ 34 + if ((flags & MAP_FIXED) && (addr < TASK_UNMAPPED_BASE)) 35 + return -EINVAL; 36 + 37 + return 0; 38 + } 39 + 40 + asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, 41 + unsigned long prot, unsigned long flags, 42 + unsigned long fd, unsigned long pgoff) 43 + { 44 + /* The shift for mmap2 is constant, regardless of PAGE_SIZE setting. */ 45 + if (pgoff & ((1 << (PAGE_SHIFT - 12)) - 1)) 46 + return -EINVAL; 47 + 48 + pgoff >>= PAGE_SHIFT - 12; 49 + 50 + return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff); 51 + } 52 + 53 + asmlinkage int sys_metag_setglobalbit(char __user *addr, int mask) 54 + { 55 + char tmp; 56 + int ret = 0; 57 + unsigned int flags; 58 + 59 + if (!((__force unsigned int)addr >= LINCORE_BASE)) 60 + return -EFAULT; 61 + 62 + __global_lock2(flags); 63 + 64 + metag_data_cache_flush((__force void *)addr, sizeof(mask)); 65 + 66 + ret = __get_user(tmp, addr); 67 + if (ret) 68 + goto out; 69 + tmp |= mask; 70 + ret = __put_user(tmp, addr); 71 + 72 + metag_data_cache_flush((__force void *)addr, sizeof(mask)); 73 + 74 + out: 75 + __global_unlock2(flags); 76 + 77 + return ret; 78 + } 79 + 80 + #define TXDEFR_FPU_MASK ((0x1f << 16) | 0x1f) 81 + 82 + asmlinkage void sys_metag_set_fpu_flags(unsigned int flags) 83 + { 84 + unsigned int temp; 85 + 86 + flags &= TXDEFR_FPU_MASK; 87 + 88 + temp = __core_reg_get(TXDEFR); 89 + temp &= ~TXDEFR_FPU_MASK; 90 + temp |= flags; 91 + __core_reg_set(TXDEFR, temp); 92 + } 93 + 94 + asmlinkage int sys_metag_set_tls(void __user *ptr) 95 + { 96 + current->thread.tls_ptr = ptr; 97 + set_gateway_tls(ptr); 98 + 99 + return 0; 100 + } 101 + 102 + asmlinkage void *sys_metag_get_tls(void) 103 + { 104 + return (__force void *)current->thread.tls_ptr; 105 + } 106 + 107 + asmlinkage long sys_truncate64_metag(const char __user *path, unsigned long lo, 108 + unsigned long hi) 109 + { 110 + return sys_truncate64(path, merge_64(hi, lo)); 111 + } 112 + 113 + asmlinkage long sys_ftruncate64_metag(unsigned int fd, unsigned long lo, 114 + unsigned long hi) 115 + { 116 + return sys_ftruncate64(fd, merge_64(hi, lo)); 117 + } 118 + 119 + asmlinkage long sys_fadvise64_64_metag(int fd, unsigned long offs_lo, 120 + unsigned long offs_hi, 121 + unsigned long len_lo, 122 + unsigned long len_hi, int advice) 123 + { 124 + return sys_fadvise64_64(fd, merge_64(offs_hi, offs_lo), 125 + merge_64(len_hi, len_lo), advice); 126 + } 127 + 128 + asmlinkage long sys_readahead_metag(int fd, unsigned long lo, unsigned long hi, 129 + size_t count) 130 + { 131 + return sys_readahead(fd, merge_64(hi, lo), count); 132 + } 133 + 134 + asmlinkage ssize_t sys_pread64_metag(unsigned long fd, char __user *buf, 135 + size_t count, unsigned long lo, 136 + unsigned long hi) 137 + { 138 + return sys_pread64(fd, buf, count, merge_64(hi, lo)); 139 + } 140 + 141 + asmlinkage ssize_t sys_pwrite64_metag(unsigned long fd, char __user *buf, 142 + size_t count, unsigned long lo, 143 + unsigned long hi) 144 + { 145 + return sys_pwrite64(fd, buf, count, merge_64(hi, lo)); 146 + } 147 + 148 + asmlinkage long sys_sync_file_range_metag(int fd, unsigned long offs_lo, 149 + unsigned long offs_hi, 150 + unsigned long len_lo, 151 + unsigned long len_hi, 152 + unsigned int flags) 153 + { 154 + return sys_sync_file_range(fd, merge_64(offs_hi, offs_lo), 155 + merge_64(len_hi, len_lo), flags); 156 + } 157 + 158 + /* Provide the actual syscall number to call mapping. */ 159 + #undef __SYSCALL 160 + #define __SYSCALL(nr, call) [nr] = (call), 161 + 162 + /* 163 + * We need wrappers for anything with unaligned 64bit arguments 164 + */ 165 + #define sys_truncate64 sys_truncate64_metag 166 + #define sys_ftruncate64 sys_ftruncate64_metag 167 + #define sys_fadvise64_64 sys_fadvise64_64_metag 168 + #define sys_readahead sys_readahead_metag 169 + #define sys_pread64 sys_pread64_metag 170 + #define sys_pwrite64 sys_pwrite64_metag 171 + #define sys_sync_file_range sys_sync_file_range_metag 172 + 173 + /* 174 + * Note that we can't include <linux/unistd.h> here since the header 175 + * guard will defeat us; <asm/unistd.h> checks for __SYSCALL as well. 176 + */ 177 + const void *sys_call_table[__NR_syscalls] = { 178 + [0 ... __NR_syscalls-1] = sys_ni_syscall, 179 + #include <asm/unistd.h> 180 + };
+97
arch/metag/kernel/user_gateway.S
··· 1 + /* 2 + * Copyright (C) 2010 Imagination Technologies Ltd. 3 + * 4 + * This file contains code that can be accessed from userspace and can 5 + * access certain kernel data structures without the overhead of a system 6 + * call. 7 + */ 8 + 9 + #include <asm/metag_regs.h> 10 + #include <asm/user_gateway.h> 11 + 12 + /* 13 + * User helpers. 14 + * 15 + * These are segment of kernel provided user code reachable from user space 16 + * at a fixed address in kernel memory. This is used to provide user space 17 + * with some operations which require kernel help because of unimplemented 18 + * native feature and/or instructions in some Meta CPUs. The idea is for 19 + * this code to be executed directly in user mode for best efficiency but 20 + * which is too intimate with the kernel counter part to be left to user 21 + * libraries. The kernel reserves the right to change this code as needed 22 + * without warning. Only the entry points and their results are guaranteed 23 + * to be stable. 24 + * 25 + * Each segment is 64-byte aligned. This mechanism should be used only for 26 + * for things that are really small and justified, and not be abused freely. 27 + */ 28 + .text 29 + .global ___user_gateway_start 30 + ___user_gateway_start: 31 + 32 + /* get_tls 33 + * Offset: 0 34 + * Description: Get the TLS pointer for this process. 35 + */ 36 + .global ___kuser_get_tls 37 + .type ___kuser_get_tls,function 38 + ___kuser_get_tls: 39 + MOVT D1Ar1,#HI(USER_GATEWAY_PAGE + USER_GATEWAY_TLS) 40 + ADD D1Ar1,D1Ar1,#LO(USER_GATEWAY_PAGE + USER_GATEWAY_TLS) 41 + MOV D1Ar3,TXENABLE 42 + AND D1Ar3,D1Ar3,#(TXENABLE_THREAD_BITS) 43 + LSR D1Ar3,D1Ar3,#(TXENABLE_THREAD_S - 2) 44 + GETD D0Re0,[D1Ar1+D1Ar3] 45 + ___kuser_get_tls_end: /* Beyond this point the read will complete */ 46 + MOV PC,D1RtP 47 + .size ___kuser_get_tls,.-___kuser_get_tls 48 + .global ___kuser_get_tls_end 49 + 50 + /* cmpxchg 51 + * Offset: 64 52 + * Description: Replace the value at 'ptr' with 'newval' if the current 53 + * value is 'oldval'. Return zero if we succeeded, 54 + * non-zero otherwise. 55 + * 56 + * Reference prototype: 57 + * 58 + * int __kuser_cmpxchg(int oldval, int newval, unsigned long *ptr) 59 + * 60 + */ 61 + .balign 64 62 + .global ___kuser_cmpxchg 63 + .type ___kuser_cmpxchg,function 64 + ___kuser_cmpxchg: 65 + #ifdef CONFIG_SMP 66 + /* 67 + * We must use LNKGET/LNKSET with an SMP kernel because the other method 68 + * does not provide atomicity across multiple CPUs. 69 + */ 70 + 0: LNKGETD D0Re0,[D1Ar3] 71 + CMP D0Re0,D1Ar1 72 + LNKSETDZ [D1Ar3],D0Ar2 73 + BNZ 1f 74 + DEFR D0Re0,TXSTAT 75 + ANDT D0Re0,D0Re0,#HI(0x3f000000) 76 + CMPT D0Re0,#HI(0x02000000) 77 + BNE 0b 78 + #ifdef CONFIG_METAG_LNKGET_AROUND_CACHE 79 + DCACHE [D1Ar3], D0Re0 80 + #endif 81 + 1: MOV D0Re0,#1 82 + XORZ D0Re0,D0Re0,D0Re0 83 + MOV PC,D1RtP 84 + #else 85 + GETD D0Re0,[D1Ar3] 86 + CMP D0Re0,D1Ar1 87 + SETDZ [D1Ar3],D0Ar2 88 + ___kuser_cmpxchg_end: /* Beyond this point the write will complete */ 89 + MOV D0Re0,#1 90 + XORZ D0Re0,D0Re0,D0Re0 91 + MOV PC,D1RtP 92 + #endif /* CONFIG_SMP */ 93 + .size ___kuser_cmpxchg,.-___kuser_cmpxchg 94 + .global ___kuser_cmpxchg_end 95 + 96 + .global ___user_gateway_end 97 + ___user_gateway_end: