/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.  All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
 3. The name of the company may not be used to endorse or promote
    products derived from this software without specific prior written
    permission.

 THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include "newlib.h"
#include "svc.h"

/* ANSI concatenation macros.  */
#define CONCAT(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a ## b

#ifdef __USER_LABEL_PREFIX__
#define FUNCTION( name ) CONCAT (__USER_LABEL_PREFIX__, name)
#else
#error __USER_LABEL_PREFIX is not defined
#endif

#ifdef HAVE_INITFINI_ARRAY
#define _init	__libc_init_array
#define _fini	__libc_fini_array
#endif

/* In ELF64, the large addressing model is used and R_AARCH64_ABS64
   reloc is generated to relocate a 64-bit address.  Since 64-bit
   relocation is not available in ELF32, in order to have
   a single code path for both ELF64 and ELF32 classes, we synthesize
   a 64-bit relocation by using R_AARCH64_P32_ABS32 on one of the two
   .word directives, depending on the endianness.  */

.macro GEN_DWORD name
#if defined(__ILP32__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	.word \name
	.word 0
#elif defined(__ILP32__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
	.word 0
	.word \name
#else
	.dword \name
#endif
.endm

/* Help tackle the pointer size difference between ELF64 and ELF32.  */
#ifdef __ILP32__
#define PTR_REG(n)	w##n
#define PTR_SIZE	4
#define PTR_LOG_SIZE	2
#else
#define PTR_REG(n)	x##n
#define PTR_SIZE	8
#define PTR_LOG_SIZE	3
#endif

	.text
.macro FUNC_START name
	.global \name
\name:
.endm

	.align	2

	FUNC_START	_mainCRTStartup
	FUNC_START	_start

/* Start by setting up a stack */

	/*  Issue Angel SVC to read memory info.

	    ptr to ptr to 4 words to receive data.  */
	adr	x1, .LC0
	mov	w0, #AngelSVC_Reason_HeapInfo
	AngelSVCAsm AngelSVC

	/* Initialise the stack pointer */

	/* We currently choose to use the heap_limit field rather than
	   stack_base because the AEM validation model
	   returns sane values in the heap fields, but 0 in the stack
	   fields.  Note on the VE AEM model it is necessary to pass
	   command line options to the AEM in order to define the values
	   exposed here in the HeapInfo Angel call.  */
	ldr	x0, .LC0		/* point at returned values */
	ldr	x1, [x0, #8]		/* get heap_limit */
#ifdef __ILP32__
	/* Sanity check on the heap base.  */
	ldr	x0, [x0]		/* get heap_base */
	tst	x0, #0xffffffff00000000
	beq	1f
	/* Exit with 1 if the heap base is not within the 32-bit address
	   space.  */
	mov	x0, ADP_Stopped_ApplicationExit & 0xff
	movk	x0, ADP_Stopped_ApplicationExit >> 16, lsl #16
	adrp	x1, HeapBase	/* Reuse to construct the parameter block.  */
	add	x1, x1, #:lo12:HeapBase
	str	x0, [x1]
	mov	x0, 1
	str	x0, [x1, #8]
	mov	w0, #AngelSVC_Reason_ReportException
	AngelSVCAsm AngelSVC
1:
	/* For the sake of safety, set the stack base to the top end of
	   the 32-bit address space if the returned value from the
	   Angel API call is larger than or equal to 4 GiB.  */
	tst	x1, #0xffffffff00000000
	csinv	w1, w1, wzr, eq
#endif

	/* Ensure quad-word stack alignment.  */
	and	x0, x1, #~15
	mov	sp, x0

	/* Setup an initial dummy frame with saved fp=0 and saved lr=0 */
	mov	x29, 0
	stp	x29, x29, [sp, #-16]!
	mov	x29, sp

       /* Initialize exception vector table, flatmap, etc.  */
        bl      FUNCTION (_cpu_init_hook)

	/* Zero the memory in the .bss section.  */
	ldr	x0, .LC1		/* First arg: start of memory block */
	mov	w1, #0			/* Second arg: fill value */
	ldr	x2, .LC2
	sub	x2, x2, x0		/* Third arg: length of block */
	bl	FUNCTION (memset)

	/* Need to set up standard file handles */
	bl	FUNCTION (initialise_monitor_handles)

	/* .init and .fini sections are used to create constructors
	   and destructors.  Here we call the _init function and arrange
	   for _fini to be called at program exit.  */
	ldr	x0, .Lfini
	bl	FUNCTION (atexit)

	bl	FUNCTION (_init)

	/* Fetch and parse the command line.  */
	adr	x1, .Lcmdline		/* Command line descriptor.  */
	mov	w0, #AngelSVC_Reason_GetCmdLine
	AngelSVCAsm AngelSVC
	ldr	x8, .Lcmdline

	mov	x0, #0		/* argc */
	mov	x1, sp		/* argv */
	ldr	x2, .Lenvp	/* envp */

	/* Put NULL at end of argv array.  */
	str	PTR_REG (0), [x1, #-PTR_SIZE]!

	/* Skip leading blanks.  */
.Lnext: ldrb	w3, [x8], #1
	cbz	w3, .Lendstr
	cmp	w3, #' '
	b.eq	.Lnext

	mov	w4, #' '	/* Terminator is space.  */

	/* See whether we are scanning a quoted string by checking for
	   opening quote (" or ').  */
	subs	w9, w3, #'\"'
	sub	x8, x8, #1	/* Backup if no match.  */
	ccmp	w9, #('\'' - '\"'), 0x4 /* FLG_Z */, ne
	csel	w4, w3, w4, eq	/* Terminator = quote if match.  */
	cinc	x8, x8, eq

	/* Push arg pointer to argv, and bump argc.  */
	str	PTR_REG (8), [x1, #-PTR_SIZE]!
	add	x0, x0, #1

	/* Find end of arg string.  */
1:	ldrb	w3, [x8], #1
	cbz	w3, .Lendstr
	cmp	w4, w3		/* Reached terminator?  */
	b.ne	1b

	/* Terminate the arg string with NUL char.  */
	mov	w4, #0
	strb	w4, [x8, #-1]
	b	.Lnext

	/* Reverse argv array.  */
.Lendstr:
	add	x3, x1, #0			/* sp = &argv[0] */
	add	x4, x1, w0, uxtw #PTR_LOG_SIZE	/* ep = &argv[argc] */
	cmp	x4, x3
	b.lo	2f
1:	ldr	PTR_REG (5), [x4, #-PTR_SIZE]	/* PTR_REG (5) = ep[-1] */
	ldr	PTR_REG (6), [x3]		/* PTR_REG (6) = *sp */
	str	PTR_REG (6), [x4, #-PTR_SIZE]!	/* *--ep = PTR_REG (6) */
	str	PTR_REG (5), [x3], #PTR_SIZE	/* *sp++ = PTR_REG (5) */
	cmp	x4, x3
	b.hi	1b
2:
	/* Move sp to the 16B boundary below argv.  */
	and	x4, x1, ~15
	mov	sp, x4

	bl	FUNCTION (main)

	b	FUNCTION (exit)		/* Cannot return.  */

/* Function initializing exception vector table, flatmap, etc.
   Declared as weak symbol so that user can override this definition
   by linking in their own version of the function.  */
	.weak FUNCTION (_cpu_init_hook)
FUNCTION (_cpu_init_hook):
	ret

	.align 3
.LC0:
	GEN_DWORD HeapBase
.LC1:
	GEN_DWORD __bss_start__
.LC2:
	GEN_DWORD __bss_end__
.Lfini:
	GEN_DWORD FUNCTION(_fini)
.Lenvp:
	GEN_DWORD env
.Lcmdline:
	GEN_DWORD CommandLine
	.dword	255

/*  Workspace for Angel calls.  */
	.data
	.align 3
/*  Data returned by monitor SVC.  */
#if defined(__ILP32__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
	.set __stack_base__, StackBase + 4
#else
	.set __stack_base__, StackBase
#endif
	.global	__stack_base__
HeapBase:	.dword	0
HeapLimit:	.dword	0
StackBase:	.dword	0
StackLimit:	.dword	0
env:		.dword	0	/* Dummy environment array */
CommandLine:	.space	256,0	/*  Maximum length of 255 chars handled.  */
