aboutsummaryrefslogtreecommitdiffstats
path: root/arch/metag
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2012-10-09 05:54:51 -0400
committerJames Hogan <james.hogan@imgtec.com>2013-03-02 15:09:49 -0500
commit26025bbfbba33a9425be1b89eccb4664ea4c17b6 (patch)
treef8a7f754525812c4d758b638d9bcade297af67da /arch/metag
parent5698c50d9da4ab2f57d98c64ea97675dcaf2a608 (diff)
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>
Diffstat (limited to 'arch/metag')
-rw-r--r--arch/metag/include/asm/mman.h11
-rw-r--r--arch/metag/include/asm/syscall.h104
-rw-r--r--arch/metag/include/asm/syscalls.h39
-rw-r--r--arch/metag/include/asm/unistd.h12
-rw-r--r--arch/metag/include/asm/user_gateway.h44
-rw-r--r--arch/metag/include/uapi/asm/unistd.h21
-rw-r--r--arch/metag/kernel/sys_metag.c180
-rw-r--r--arch/metag/kernel/user_gateway.S97
8 files changed, 508 insertions, 0 deletions
diff --git a/arch/metag/include/asm/mman.h b/arch/metag/include/asm/mman.h
new file mode 100644
index 000000000000..17999dba9275
--- /dev/null
+++ b/arch/metag/include/asm/mman.h
@@ -0,0 +1,11 @@
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
8int metag_mmap_check(unsigned long addr, unsigned long len,
9 unsigned long flags);
10#endif
11#endif /* __METAG_MMAN_H__ */
diff --git a/arch/metag/include/asm/syscall.h b/arch/metag/include/asm/syscall.h
new file mode 100644
index 000000000000..24fc97939f77
--- /dev/null
+++ b/arch/metag/include/asm/syscall.h
@@ -0,0 +1,104 @@
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
22static 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
41static inline void syscall_rollback(struct task_struct *task,
42 struct pt_regs *regs)
43{
44 /* do nothing */
45}
46
47static 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
54static 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
60static 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
67static 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
83static 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 */
102extern const void *sys_call_table[];
103
104#endif /* _ASM_METAG_SYSCALL_H */
diff --git a/arch/metag/include/asm/syscalls.h b/arch/metag/include/asm/syscalls.h
new file mode 100644
index 000000000000..a02b95556522
--- /dev/null
+++ b/arch/metag/include/asm/syscalls.h
@@ -0,0 +1,39 @@
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
11asmlinkage long sys_rt_sigreturn(void);
12
13#include <asm-generic/syscalls.h>
14
15/* kernel/sys_metag.c */
16asmlinkage int sys_metag_setglobalbit(char __user *, int);
17asmlinkage void sys_metag_set_fpu_flags(unsigned int);
18asmlinkage int sys_metag_set_tls(void __user *);
19asmlinkage void *sys_metag_get_tls(void);
20
21asmlinkage long sys_truncate64_metag(const char __user *, unsigned long,
22 unsigned long);
23asmlinkage long sys_ftruncate64_metag(unsigned int, unsigned long,
24 unsigned long);
25asmlinkage long sys_fadvise64_64_metag(int, unsigned long, unsigned long,
26 unsigned long, unsigned long, int);
27asmlinkage long sys_readahead_metag(int, unsigned long, unsigned long, size_t);
28asmlinkage ssize_t sys_pread64_metag(unsigned long, char __user *, size_t,
29 unsigned long, unsigned long);
30asmlinkage ssize_t sys_pwrite64_metag(unsigned long, char __user *, size_t,
31 unsigned long, unsigned long);
32asmlinkage long sys_sync_file_range_metag(int, unsigned long, unsigned long,
33 unsigned long, unsigned long,
34 unsigned int);
35
36int do_work_pending(struct pt_regs *regs, unsigned int thread_flags,
37 int syscall);
38
39#endif /* _ASM_METAG_SYSCALLS_H */
diff --git a/arch/metag/include/asm/unistd.h b/arch/metag/include/asm/unistd.h
new file mode 100644
index 000000000000..32955a18fb32
--- /dev/null
+++ b/arch/metag/include/asm/unistd.h
@@ -0,0 +1,12 @@
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
diff --git a/arch/metag/include/asm/user_gateway.h b/arch/metag/include/asm/user_gateway.h
new file mode 100644
index 000000000000..e404c09e3b74
--- /dev/null
+++ b/arch/metag/include/asm/user_gateway.h
@@ -0,0 +1,44 @@
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
17extern char __user_gateway_start;
18extern char __user_gateway_end;
19
20/* Kernel mapping of the gateway page. */
21extern void *gateway_page;
22
23static 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
36extern int __kuser_get_tls(void);
37extern char *__kuser_get_tls_end[];
38
39extern int __kuser_cmpxchg(int, int, unsigned long *);
40extern char *__kuser_cmpxchg_end[];
41
42#endif
43
44#endif
diff --git a/arch/metag/include/uapi/asm/unistd.h b/arch/metag/include/uapi/asm/unistd.h
new file mode 100644
index 000000000000..b80b8e899d22
--- /dev/null
+++ b/arch/metag/include/uapi/asm/unistd.h
@@ -0,0 +1,21 @@
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)
diff --git a/arch/metag/kernel/sys_metag.c b/arch/metag/kernel/sys_metag.c
new file mode 100644
index 000000000000..efe833a452f7
--- /dev/null
+++ b/arch/metag/kernel/sys_metag.c
@@ -0,0 +1,180 @@
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
27int 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
40asmlinkage 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
53asmlinkage 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
74out:
75 __global_unlock2(flags);
76
77 return ret;
78}
79
80#define TXDEFR_FPU_MASK ((0x1f << 16) | 0x1f)
81
82asmlinkage 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
94asmlinkage 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
102asmlinkage void *sys_metag_get_tls(void)
103{
104 return (__force void *)current->thread.tls_ptr;
105}
106
107asmlinkage 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
113asmlinkage 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
119asmlinkage 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
128asmlinkage 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
134asmlinkage 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
141asmlinkage 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
148asmlinkage 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 */
177const void *sys_call_table[__NR_syscalls] = {
178 [0 ... __NR_syscalls-1] = sys_ni_syscall,
179#include <asm/unistd.h>
180};
diff --git a/arch/metag/kernel/user_gateway.S b/arch/metag/kernel/user_gateway.S
new file mode 100644
index 000000000000..7167f3e8db6b
--- /dev/null
+++ b/arch/metag/kernel/user_gateway.S
@@ -0,0 +1,97 @@
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 */
700: 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
811: 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: