sys_ia64.c 8.41 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4
/*
 * This file contains various system calls that have different calling
 * conventions on different platforms.
 *
David Mosberger's avatar
David Mosberger committed
5 6
 * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co
 *	David Mosberger-Tang <davidm@hpl.hp.com>
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 */
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/sched.h>
#include <linux/file.h>		/* doh, must come after sched.h... */
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/highuid.h>

#include <asm/shmparam.h>
#include <asm/uaccess.h>

22 23 24 25 26 27
#ifdef CONFIG_HUGETLB_PAGE
# define SHMLBA_HPAGE		HPAGE_SIZE
# define COLOR_HALIGN(addr)	(((addr) + SHMLBA_HPAGE - 1) & ~(SHMLBA_HPAGE - 1))
# define TASK_HPAGE_BASE	((REGION_HPAGE << REGION_SHIFT) | HPAGE_SIZE)
#endif

Linus Torvalds's avatar
Linus Torvalds committed
28
unsigned long
Linus Torvalds's avatar
Linus Torvalds committed
29 30
arch_get_unmapped_area (struct file *filp, unsigned long addr, unsigned long len,
			unsigned long pgoff, unsigned long flags)
Linus Torvalds's avatar
Linus Torvalds committed
31
{
Linus Torvalds's avatar
Linus Torvalds committed
32
	long map_shared = (flags & MAP_SHARED);
Linus Torvalds's avatar
Linus Torvalds committed
33 34
	unsigned long align_mask = PAGE_SIZE - 1;
	struct vm_area_struct * vmm;
Linus Torvalds's avatar
Linus Torvalds committed
35 36

	if (len > RGN_MAP_LIMIT)
Linus Torvalds's avatar
Linus Torvalds committed
37
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
38 39 40
	if (!addr)
		addr = TASK_UNMAPPED_BASE;

Linus Torvalds's avatar
Linus Torvalds committed
41 42 43 44 45 46 47 48 49 50
	if (map_shared && (TASK_SIZE > 0xfffffffful))
		/*
		 * For 64-bit tasks, align shared segments to 1MB to avoid potential
		 * performance penalty due to virtual aliasing (see ASDM).  For 32-bit
		 * tasks, we prefer to avoid exhausting the address space too quickly by
		 * limiting alignment to a single page.
		 */
		align_mask = SHMLBA - 1;

	addr = (addr + align_mask) & ~align_mask;
Linus Torvalds's avatar
Linus Torvalds committed
51 52 53 54

	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
		/* At this point:  (!vmm || addr < vmm->vm_end). */
		if (TASK_SIZE - len < addr)
Linus Torvalds's avatar
Linus Torvalds committed
55
			return -ENOMEM;
56
		if (REGION_OFFSET(addr) + len > RGN_MAP_LIMIT)	/* no risk of overflow here... */
Linus Torvalds's avatar
Linus Torvalds committed
57
			return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
58 59
		if (!vmm || addr + len <= vmm->vm_start)
			return addr;
Linus Torvalds's avatar
Linus Torvalds committed
60
		addr = (vmm->vm_end + align_mask) & ~align_mask;
Linus Torvalds's avatar
Linus Torvalds committed
61 62 63 64
	}
}

asmlinkage long
65
ia64_getpriority (int which, int who)
Linus Torvalds's avatar
Linus Torvalds committed
66 67 68 69 70 71
{
	extern long sys_getpriority (int, int);
	long prio;

	prio = sys_getpriority(which, who);
	if (prio >= 0) {
72
		force_successful_syscall_return();
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76 77 78 79 80 81 82 83 84 85
		prio = 20 - prio;
	}
	return prio;
}

/* XXX obsolete, but leave it here until the old libc is gone... */
asmlinkage unsigned long
sys_getpagesize (void)
{
	return PAGE_SIZE;
}

asmlinkage unsigned long
86
ia64_shmat (int shmid, void *shmaddr, int shmflg)
Linus Torvalds's avatar
Linus Torvalds committed
87 88 89 90 91 92 93 94
{
	unsigned long raddr;
	int retval;

	retval = sys_shmat(shmid, shmaddr, shmflg, &raddr);
	if (retval < 0)
		return retval;

95
	force_successful_syscall_return();
Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99
	return raddr;
}

asmlinkage unsigned long
100
ia64_brk (unsigned long brk)
Linus Torvalds's avatar
Linus Torvalds committed
101 102 103 104 105 106 107 108 109 110
{
	extern int vm_enough_memory (long pages);
	unsigned long rlim, retval, newbrk, oldbrk;
	struct mm_struct *mm = current->mm;

	/*
	 * Most of this replicates the code in sys_brk() except for an additional safety
	 * check and the clearing of r8.  However, we can't call sys_brk() because we need
	 * to acquire the mmap_sem before we can do the test...
	 */
Linus Torvalds's avatar
Linus Torvalds committed
111
	down_write(&mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
112 113 114 115 116 117 118 119 120 121

	if (brk < mm->end_code)
		goto out;
	newbrk = PAGE_ALIGN(brk);
	oldbrk = PAGE_ALIGN(mm->brk);
	if (oldbrk == newbrk)
		goto set_brk;

	/* Always allow shrinking brk. */
	if (brk <= mm->brk) {
David Mosberger's avatar
David Mosberger committed
122
		if (!do_munmap(mm, newbrk, oldbrk-newbrk))
Linus Torvalds's avatar
Linus Torvalds committed
123 124 125 126 127
			goto set_brk;
		goto out;
	}

	/* Check against unimplemented/unmapped addresses: */
128
	if ((newbrk - oldbrk) > RGN_MAP_LIMIT || REGION_OFFSET(newbrk) > RGN_MAP_LIMIT)
Linus Torvalds's avatar
Linus Torvalds committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
		goto out;

	/* Check against rlimit.. */
	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
	if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
		goto out;

	/* Check against existing mmap mappings. */
	if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
		goto out;

	/* Ok, looks good - let it rip. */
	if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
		goto out;
set_brk:
	mm->brk = brk;
out:
	retval = mm->brk;
Linus Torvalds's avatar
Linus Torvalds committed
147
	up_write(&mm->mmap_sem);
148
	force_successful_syscall_return();
Linus Torvalds's avatar
Linus Torvalds committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	return retval;
}

/*
 * On IA-64, we return the two file descriptors in ret0 and ret1 (r8
 * and r9) as this is faster than doing a copy_to_user().
 */
asmlinkage long
sys_pipe (long arg0, long arg1, long arg2, long arg3,
	  long arg4, long arg5, long arg6, long arg7, long stack)
{
	struct pt_regs *regs = (struct pt_regs *) &stack;
	int fd[2];
	int retval;

	retval = do_pipe(fd);
	if (retval)
		goto out;
	retval = fd[0];
	regs->r9 = fd[1];
  out:
	return retval;
}

static inline unsigned long
do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
{
	unsigned long roff;
	struct file *file = 0;

Linus Torvalds's avatar
Linus Torvalds committed
179 180 181 182 183 184
	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
	if (!(flags & MAP_ANONYMOUS)) {
		file = fget(fd);
		if (!file)
			return -EBADF;

Linus Torvalds's avatar
Linus Torvalds committed
185 186 187 188
		if (!file->f_op || !file->f_op->mmap) {
			addr = -ENODEV;
			goto out;
		}
Linus Torvalds's avatar
Linus Torvalds committed
189 190
	}

Linus Torvalds's avatar
Linus Torvalds committed
191
	/*
Linus Torvalds's avatar
Linus Torvalds committed
192 193
	 * A zero mmap always succeeds in Linux, independent of whether or not the
	 * remaining arguments are valid.
Linus Torvalds's avatar
Linus Torvalds committed
194
	 */
Linus Torvalds's avatar
Linus Torvalds committed
195 196
	len = PAGE_ALIGN(len);
	if (len == 0)
Linus Torvalds's avatar
Linus Torvalds committed
197
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
198

David Mosberger's avatar
David Mosberger committed
199 200 201 202 203
	/*
	 * Don't permit mappings into unmapped space, the virtual page table of a region,
	 * or across a region boundary.  Note: RGN_MAP_LIMIT is equal to 2^n-PAGE_SIZE
	 * (for some integer n <= 61) and len > 0.
	 */
204
	roff = REGION_OFFSET(addr);
David Mosberger's avatar
David Mosberger committed
205
	if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len))) {
Linus Torvalds's avatar
Linus Torvalds committed
206 207 208
		addr = -EINVAL;
		goto out;
	}
Linus Torvalds's avatar
Linus Torvalds committed
209

Linus Torvalds's avatar
Linus Torvalds committed
210
	down_write(&current->mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
211
	addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
Linus Torvalds's avatar
Linus Torvalds committed
212
	up_write(&current->mm->mmap_sem);
Linus Torvalds's avatar
Linus Torvalds committed
213

Linus Torvalds's avatar
Linus Torvalds committed
214
out:	if (file)
Linus Torvalds's avatar
Linus Torvalds committed
215 216 217 218 219 220 221 222 223 224
		fput(file);
	return addr;
}

/*
 * mmap2() is like mmap() except that the offset is expressed in units
 * of PAGE_SIZE (instead of bytes).  This allows to mmap2() (pieces
 * of) files that are larger than the address space of the CPU.
 */
asmlinkage unsigned long
225
sys_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, long pgoff)
Linus Torvalds's avatar
Linus Torvalds committed
226 227 228
{
	addr = do_mmap2(addr, len, prot, flags, fd, pgoff);
	if (!IS_ERR((void *) addr))
229
		force_successful_syscall_return();
Linus Torvalds's avatar
Linus Torvalds committed
230 231 232 233
	return addr;
}

asmlinkage unsigned long
234
sys_mmap (unsigned long addr, unsigned long len, int prot, int flags, int fd, long off)
Linus Torvalds's avatar
Linus Torvalds committed
235 236 237 238 239 240
{
	if ((off & ~PAGE_MASK) != 0)
		return -EINVAL;

	addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
	if (!IS_ERR((void *) addr))
241
		force_successful_syscall_return();
Linus Torvalds's avatar
Linus Torvalds committed
242 243 244
	return addr;
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
#ifdef CONFIG_HUGETLB_PAGE

asmlinkage unsigned long
sys_alloc_hugepages (int key, unsigned long addr, size_t len, int prot, int flag)
{
	struct mm_struct *mm = current->mm;
	long retval;
	extern int alloc_hugetlb_pages (int, unsigned long, unsigned long, int, int);

	if ((key < 0) || (len & (HPAGE_SIZE - 1)))
		return -EINVAL;

	if (addr && ((REGION_NUMBER(addr) != REGION_HPAGE) || (addr & (HPAGE_SIZE - 1))))
		addr = TASK_HPAGE_BASE;

	if (!addr)
		addr = TASK_HPAGE_BASE;
	down_write(&mm->mmap_sem);
	{
		retval = arch_get_unmapped_area(NULL, COLOR_HALIGN(addr), len, 0, 0);
		if (retval != -ENOMEM)
			retval = alloc_hugetlb_pages(key, retval, len, prot, flag);
	}
	up_write(&mm->mmap_sem);

	if (IS_ERR((void *) retval))
		return retval;

	force_successful_syscall_return();
	return retval;
}

asmlinkage int
sys_free_hugepages (unsigned long  addr)
{
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma;
	extern int free_hugepages(struct vm_area_struct *);
	int retval;

	down_write(&mm->mmap_sem);
	{
287 288 289 290 291
		vma = find_vma(mm, addr);
		if (!vma || !is_vm_hugetlb_page(vma) || (vma->vm_start != addr))
			retval = -EINVAL;
			goto out;

292 293 294 295 296 297
		spin_lock(&mm->page_table_lock);
		{
			retval = free_hugepages(vma);
		}
		spin_unlock(&mm->page_table_lock);
	}
298
out:
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	up_write(&mm->mmap_sem);
	return retval;
}

#else /* !CONFIG_HUGETLB_PAGE */

asmlinkage unsigned long
sys_alloc_hugepages (int key, size_t addr, unsigned long len, int prot, int flag)
{
	return -ENOSYS;
}

asmlinkage unsigned long
sys_free_hugepages (unsigned long  addr)
{
	return -ENOSYS;
}

#endif /* !CONFIG_HUGETLB_PAGE */

Linus Torvalds's avatar
Linus Torvalds committed
319 320 321
asmlinkage long
sys_vm86 (long arg0, long arg1, long arg2, long arg3)
{
Linus Torvalds's avatar
Linus Torvalds committed
322 323
	printk(KERN_ERR "sys_vm86(%lx, %lx, %lx, %lx)!\n", arg0, arg1, arg2, arg3);
	return -ENOSYS;
Linus Torvalds's avatar
Linus Torvalds committed
324 325 326
}

asmlinkage unsigned long
327
ia64_create_module (const char *name_user, size_t size)
Linus Torvalds's avatar
Linus Torvalds committed
328 329 330 331 332 333
{
	extern unsigned long sys_create_module (const char *, size_t);
	unsigned long   addr;

	addr = sys_create_module (name_user, size);
	if (!IS_ERR((void *) addr))
334
		force_successful_syscall_return();
Linus Torvalds's avatar
Linus Torvalds committed
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
	return addr;
}

#ifndef CONFIG_PCI

asmlinkage long
sys_pciconfig_read (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len,
		    void *buf)
{
	return -ENOSYS;
}

asmlinkage long
sys_pciconfig_write (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len,
		     void *buf)
{
	return -ENOSYS;
}

#endif /* CONFIG_PCI */