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};
181