/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2012  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/offsets.h>
#include <grub/symbol.h>
#include <grub/machine/kernel.h>

/*
 * GRUB is called from U-Boot as a Linux Kernel type image, which
 * means among other things that it always enters in ARM state.
 *
 *
 * Overview of GRUB image layout:
 *
 * _start:
 *              Entry point (1 ARM branch instruction, to "codestart")
 * grub_total_module_size:
 *              Data field: Size of included module blob
 *              (when generated by grub-mkimage)
 * codestart:
 *              Remainder of statically-linked executable code and data.
 * __bss_start:
 *              Start of included module blob.
 *              Also where global/static variables are located.
 * _end:
 *              End of bss region (but not necessarily module blob).
 * <overflow>:
 *              Any part of the module blob that extends beyond _end.
 * <modules>:
 *              Loadable modules, post relocation.
 * <stack>:     
 * <heap>:
 */
	
	.text
	.arm
FUNCTION(_start)
	b	codestart
	
	@ Size of final image integrated module blob - set by grub-mkimage
	. = _start + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE
VARIABLE(grub_total_module_size)
	.long 	0

FUNCTION(codestart)
	@ Store context: Machine ID, atags/dtb, ...
	@ U-Boot API signature is stored on the U-Boot heap
	@ Stack pointer used as start address for signature probing
	mov	r12, sp
	ldr	sp, =entry_state
	push	{r4-r12,lr}	@ store U-Boot context (sp in r12)

	@ Put kernel parameters aside until we can store them (further down)
	mov	r4, r1		@ machine type
	mov	r5, r2		@ boot data

	@ Modules have been stored as a blob in BSS,
	@ they need to be manually relocated to _end or
	@ (__bss_start + grub_total_module_size), whichever greater.
	ldr	r0, =EXT_C(__bss_start)		@ src
	ldr	r1, =EXT_C(_end)		@ dst = End of BSS
	ldr	r2, grub_total_module_size	@ blob size
	add	r3, r0, r2			@ blob end
	cmp	r1, r3				@ _end < blob end?
	movlt	r1, r3				@ dst = blob end + blob size
	
1:	ldr	r3, [r0], #4 			@ r3 = *src++ 
	str	r3, [r1], #4			@ *dst++ = r3 
	subs	r2, #4				@ remaining -= 4
	bne	1b				@ while remaining != 0
	
	@ Set up a new stack, beyond the end of copied modules.
	ldr	r3, =GRUB_KERNEL_MACHINE_STACK_SIZE
	add	r3, r1, r3	@ Place stack beyond end of modules
	and	sp, r3, #~0x7	@ Ensure 8-byte alignment

	@ Since we _are_ the C run-time, we need to manually zero the BSS
	@ region before continuing
	ldr	r0, =EXT_C(__bss_start)
	ldr	r1, =EXT_C(_end)
	mov	r2, #0
1:	str	r2, [r0], #4
	cmp	r0, r1
	bne	1b

	@ Global variables now accessible - store kernel parameters in memory
	ldr     r12, =EXT_C(uboot_machine_type)
	str     r4, [r12]
	ldr     r12, =EXT_C(uboot_boot_data)
	str     r5, [r12]
	
	b	grub_main

	/*
	 * uboot_syscall():
	 *   This function is effectively a veneer, so it cannot
	 *   modify the stack or corrupt any registers other than
	 *   r12 (ip). Furthermore it needs to restore r8 for
	 *   U-Boot (Global Data Pointer) and preserve it for Grub.
	 */
FUNCTION(uboot_syscall)
	ldr	ip, =transition_space
	stm	ip, {r8, lr}
	ldr	ip, =gd_backup
	ldr	r8, [ip]
	ldr	ip, =uboot_syscall_ptr
	mov	lr, pc
	ldr	pc, [ip]
	ldr	ip, =gd_backup
	str	r8, [ip]
	ldr	ip, =transition_space
	ldm	ip, {r8, lr}
	bx	lr
	
FUNCTION(uboot_return)
	ldr	sp, =entry_state_end
	pop	{r4-r12, lr}
	mov	sp, r12
	bx	lr

	
	.data
	.align	3	@ 8-byte alignment for stack
@ U-boot context stack space
entry_state_end:	
	.long	0	@ r4
	.long	0	@ r5
	.long	0	@ r6
	.long	0	@ r7
gd_backup:	
	.long	0	@ r8 - U-Boot global data pointer
	.long	0	@ r9
	.long	0	@ r10
	.long	0	@ r11
VARIABLE(uboot_search_hint)@ U-Boot stack pointer - 
	.long	0	@ also API signature address hint.
	.long	0	@ lr
entry_state:		@ backup for U-Boot context

@ GRUB context stack space
transition_space:	
	.long	0	@ r8
	.long	0	@ lr

VARIABLE(uboot_syscall_ptr)
	.long	0	@

	.end
