aboutsummaryrefslogtreecommitdiff
/* arm mode, cortex-a7 compatibility
 *
 * _boot is entry point for the kernel.
 *
 * Kernel copies it's embedded stage 2 to address 0x0 and jumps to
 * it (to the reset handler). Registers r0 - r2 are arguments for
 * the kernel, but we're not using them for now.
 *
 * This file is based on (and almost identical with) loader_stage1.S
 */

.global _boot
_boot:
        // Only let the first core execute
        mrc p15, 0, r3, c0, c0, 5
        and r3, r3, #3
        cmp r3, #0
        beq proceed
	// this is a kind of blef - races can theoretically still
	// occur when the main core overwrites this part of memory
	wfe

	// we'll use the size of stage1 to determine where we have free
	// space after it. We'll then copy our atags/fdt there, so
	// it doesn't get overwritten by stage2 we deploy at 0x0
atags_magic:
	.word 0x54410001
	
proceed:
	// load the second word of structure passed to us through r2;
	// if it's atags, it's second word should be the magic number
	// Btw, location of ATAGS is always 0x100.
	ldr r3, [r2, #4]
	adr r4, atags_magic
	ldr r4, [r4]

	// compare second word of assumed atags with magic number
	// to see, if it's really atags and not sth else (i.e. fdt)
	cmp r3, r4

	// normally at start r0 contains value 0;
	// value 3 in r0 would tell stage2 code, we found no atags :(
	movne r0, #3
	bne stage2_blob_copying

	// if atags was found, copying of it takes place here
	
	// the following loop finds, where atags ends
	// r3 shall point to currently looked-at tag
	mov r3, r2

find_end_of_atags_loop:
	// load first word of tag header to r4 (it contains tag size)
	ldr r4, [r3]
	// make r3 point at the next tag (by adding 4*tag_size to it)
	add r3, r4, lsl #2

	// load second word of tag header to r5 (it contains tag type)
	ldr r5, [r3, #4]

	// if tag value is 0, it is the last tag
	cmp r5, #0
	bne find_end_of_atags_loop

	add r3, #8 // make r3 point at the end of last tag
	sub r3, r2 // get atags size in r3
	
	// at this pont r2 and r3 point at start and size of atags,
	// respectively; now we'll compute, where we're going to have
	// free space to put atags in; we want to put atags either
	// right after our blob or, if if it doesn't fit between
	// blob end and the address stage1 is loaded at, after stage1

	// get blob size to r5
	adr r5, blob_size
	ldr r5, [r5]

	// we could only copy atags to a 4-aligned address
	mov r6, #4
	bl aling_r5_to_r6

	// compute where atags copied right after blob would end
	add r6, r5, r3
	// we can only overwrite stuff before the copying loop
	adr r7, copy_atags_loop
	cmp r6, r7
	ble copy_atags

	// atags wouldn't fit - use memory after stage1 as destination
	adr r5, _boot
	adr r6, stage1_size
	ldr r6, [r6]
	add r5, r6
	mov r6, #4
	bl aling_r5_to_r6
	
copy_atags:
	// now copy atags (r2 - atags start; r3 - atags size;
	// r5 - destination; r4 - iterator; r6 - buffor)
	mov r4, #0

copy_atags_loop:
	ldr r6, [r2, r4]
	str r6, [r5, r4]
	add r4, #4
	cmp r4, r3
	blo copy_atags_loop

	mov r2, r5 // place the new atags address in r2
	b stage2_blob_copying // atags stuff done; proceed

// mini-function, that does what the label says; clobbers r7
aling_r5_to_r6:
	sub r5, #1
	sub r7, r6, #1
	bic r5, r7
	add r5, r6
	mov pc, lr

	
stage2_blob_copying: // copy stage2 of the kernel to address 0x0

	// first, load address of stage2_start to r3 (a PIC way)
	adr r3, stage2_start

	// load destination address for stage2 code to r4
	mov r4, #0

	// load blob size to r5
	// The size might get too big for an immediate value, so
	// we load it from memory.
	adr r5, blob_size
	ldr r5, [r5]

	// r6 is the counter - counts the bytes copied
	mov r6, #0

	// This initial piece of code might get overwritten when we
	// copy stage2, so the actual copying loop shall be after
	// stage2 blob. We want this asm code to be PIC, so we're
	// computing address of stage2_end into r7.
	add r7, r3, r5
	bx r7

blob_size:
	.word stage2_end - stage2_start
stage1_size:
	.word stage1_end - _boot

.align 4
stage2_start:
	.incbin "kernel_stage2.img"
stage2_end:

	// each word of the blob is loaded to r7 and stored
	// from r7 to it's destination in a loop
loop:
	ldr r7, [r3, r6]
	str r7, [r4, r6]
	add r6, r6, #4
	cmp r6, r5
	blo loop

        // Call stage2 of the kernel (branch to 0x0,
	// which is the reset handler).
        bx r4
	
stage1_end: