/*
 * mips-setup-simple.S - code to execute before kernel to handle command line
 * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
 * Copyright (C) 2007 Tvblob s.r.l.
 *
 * derived from Albert Herranz idea (ppc) adding command line support
 * (boot_notes structure)
 *
 * This source code is licensed under the GNU General Public License,
 * Version 2.  See the file COPYING for more details.
 */

/*
 * Only suitable for platforms booting with MMU turned off.
 * -- Albert Herranz
 */
#include "regdef.h"

/* returns  t0 = relocated address of sym */
/* modifies t1 t2 */
/* sym must not be global or this will not work (at least AFAIK up to now) */
#define RELOC_SYM(sym)                                                 \
	move    t0,ra;          /* save ra */                           \
	bal 1f;                                                         \
1:                                                                     \
	move    t1,ra;          /* now t1 is 1b (where we are now) */   \
	move    ra,t0;          /* restore ra */                        \
	lui     t2,%hi(1b);                                             \
	ori     t2,t2,%lo(1b);                                          \
	lui     t0,%hi(sym);                                            \
	ori     t0,t0,%lo(sym);                                         \
	sub     t0,t0,t2;       /* t0 = offset between sym and 1b */    \
	add     t0,t1,t0;       /* t0 = actual address in memory */

	.data
	.globl setup_simple_start
setup_simple_start:

	/* should perform here any required setup */

	/* Initialize GOT pointer (verify if needed) */
	bal     1f
	nop
	.word   _GLOBAL_OFFSET_TABLE_
	1:
	move    gp, ra
	lw      t1, 0(ra)
	move    gp, t1

	/* spr8 relocation */
	RELOC_SYM(spr8)

	move    t4,t0           // save pointer to kernel start addr
	lw      t3,0(t0)        // save kernel start address

	/* spr9 relocation */
	RELOC_SYM(spr9)
	lw      a0,0(t0)        // load argc

	// this code is to be changed if boot_notes struct changes
	lw      t2,12(t4)       // t2 is size of boot_notes struct
	addi    t2,t2,3
	srl     t2,t2,2
	sll     v1,t2,2         // v1 = size of boot_notes struct
				// aligned to word boundary

	addi    t0,t4,0x20      // t0 contains the address of "kexec" string
	add     v0,t4,v1        // v0 points to last word of boot_notes
	addi    v0,v0,8         // v0 points to address after boot_notes
	sw      t0,0(v0)        // store pointer to "kexec" string there

	lw      t2,-8(t0)       // t2 is size of "kexec" string in bytes
	addi    t2,t2,3
	srl     t2,t2,2
	sll     v1,t2,2         // v1 = size of "kexec" string
				// aligned to word boundary
	add     t2,t0,v1
	addi    t0,t2,4         // t0 points to size of version string

	lw      t2,0(t0)        // t2 is size of version string in bytes
	addi    t2,t2,3
	srl     t2,t2,2
	sll     v1,t2,2         // v1 = size of version string
				// aligned to word boundary

	addi    t0,t0,8         // t0 points to version string
	add     t0,t0,v1        // t0 points to start of command_line record
	addi    t0,t0,12        // t0 points command line

	sw      t0,4(v0)        // store pointer to command line

	move    a1,v0           // load argv
	li      a2,0
	li      a3,0

	jr      t3
	nop

	.balign 4
	.globl setup_simple_regs
setup_simple_regs:
spr8:	.long 0x00000000
spr9:	.long 0x00000000

setup_simple_end:

	.globl setup_simple_size
setup_simple_size:
	.long setup_simple_end - setup_simple_start

