/* Copyright (C) 2000 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#include <_lfs_64.h>
#define _ERRNO_H
#include <bits/errno.h>
#include <sys/syscall.h>
#include <bits/arm_asm.h>
#include <bits/arm_bx.h>

#ifdef __NR_mmap2

/* The mmap2 system call takes six arguments, all in registers.  */
.text
.global mmap64
.type mmap64,%function
.align 2

#ifdef __ARM_EABI__
#if defined(THUMB1_ONLY)
.thumb_func
mmap64:
#ifdef __ARMEB__
/* Offsets are after pushing 3 words.  */
# define LOW_OFFSET  12 + 8 + 4
# define HIGH_OFFSET 12 + 8 + 0
#else
# define LOW_OFFSET  12 + 8 + 0
# define HIGH_OFFSET 12 + 8 + 4
#endif
	push	{r4, r5, r6}
	ldr	r6, [sp, $LOW_OFFSET]
	ldr	r5, [sp, $HIGH_OFFSET]
	lsl	r4, r6, #20		@ check that offset is page-aligned
	bne	.Linval
	lsr	r4, r5, #12		@ check for overflow
	bne	.Linval
	@ compose page offset
	lsr	r6, r6, #12
	lsl	r5, r5, #20
	orr	r5, r5, r6
	ldr	r4, [sp, #8]		@ load fd
	DO_CALL (mmap2)
	ldr	r1, =0xfffff000
	cmp	r0, r1
	bcs	.Lerror
	bx	lr
.Linval:
	ldr	r0, =-EINVAL
	pop	{r4, r5, r6}
.Lerror:
	push	{r3, lr}
	bl	__syscall_error
	POP_RET
.pool
#else /* !THUMB1_ONLY */
mmap64:
#ifdef __ARMEB__
# define LOW_OFFSET      8 + 4
/* The initial + 4 is for the stack postdecrement.  */
# define HIGH_OFFSET 4 + 8 + 0
#else
# define LOW_OFFSET      8 + 0
# define HIGH_OFFSET 4 + 8 + 4
#endif
	ldr	ip, [sp, $LOW_OFFSET]
	str	r5, [sp, #-4]!
	ldr	r5, [sp, $HIGH_OFFSET]
	str	r4, [sp, #-4]!
	movs	r4, ip, lsl $20		@ check that offset is page-aligned
	mov	ip, ip, lsr $12
	IT(t, eq)
	moveqs	r4, r5, lsr $12		@ check for overflow
	bne	.Linval
	ldr	r4, [sp, $8]		@ load fd
	orr	r5, ip, r5, lsl $20	@ compose page offset
	DO_CALL (mmap2)
	cmn	r0, $4096
	ldmfd	sp!, {r4, r5}
	IT(t, cc)
	BXC(cc, lr)
	b	__syscall_error
.Linval:
	mov	r0, $-EINVAL
	ldmfd	sp!, {r4, r5}
	b	__syscall_error
#endif
#else /* !__ARM_EABI__ */
mmap64:
	stmfd	sp!, {r4, r5, lr}
	ldr	r5, [sp, $16]
	ldr	r4, [sp, $12]
	movs	ip, r5, lsl $20		@ check that offset is page-aligned
	bne	.Linval
	ldr	ip, [sp, $20]
	mov	r5, r5, lsr $12
	orr	r5, r5, ip, lsl $20	@ compose page offset
	movs	ip, ip, lsr $12
	bne	.Linval			@ check for overflow
	mov	ip, r0
	DO_CALL (mmap2)
	cmn	r0, $4096
	ldmccfd	sp!, {r4, r5, pc}
	cmn	r0, $ENOSYS
	ldmnefd	sp!, {r4, r5, lr}
	bne	__error
	/* The current kernel does not support mmap2.  Fall back to plain
	   mmap if the offset is small enough.  */
	ldr	r5, [sp, $20]
	mov	r0, ip			@ first arg was clobbered
	teq	r5, $0
	ldmeqfd	sp!, {r4, r5, lr}
	beq	HIDDEN_JUMPTARGET(mmap)
.Linval:
	mov	r0, $-EINVAL
	ldmfd	sp!, {r4, r5, lr}
	b	__error

__error:
	b	__syscall_error
#endif
.size mmap64,.-mmap64

#endif