aboutsummaryrefslogtreecommitdiff
path: root/src/arm/PL1/loader
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm/PL1/loader')
-rw-r--r--src/arm/PL1/loader/loader_stage1.S55
-rw-r--r--src/arm/PL1/loader/loader_stage1_linker.ld16
-rw-r--r--src/arm/PL1/loader/loader_stage2.c33
-rw-r--r--src/arm/PL1/loader/loader_stage2_linker.ld16
4 files changed, 120 insertions, 0 deletions
diff --git a/src/arm/PL1/loader/loader_stage1.S b/src/arm/PL1/loader/loader_stage1.S
new file mode 100644
index 0000000..69d78c5
--- /dev/null
+++ b/src/arm/PL1/loader/loader_stage1.S
@@ -0,0 +1,55 @@
+/* arm mode, cortex-a7 compatibility
+ *
+ * _boot is entry point for the loader.
+ *
+ * Loader copies it's embedded stage 2 to address 0x4000
+ * and jumps to it. Registers r0 - r2 are arguments for the kernel
+ * and should be left intact.
+ */
+
+.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
+
+proceed:
+ // copy stage2 of the loader to address 0x4000
+
+ // 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, #0x4000
+
+ // load blob size to r5
+ mov r5, #(stage2_end - stage2_start)
+
+ // r6 is the counter - counts the bytes copied
+ mov r6, #0
+
+ // 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
+
+ // Initialize the stack
+ // _stack_top is defined in loader_stage1_linker.ld
+ ldr sp, =_stack_top
+
+ // Call stage2 of the loader (branch to 0x4000)
+ bx r4
+
+.align 4
+stage2_start:
+ .incbin "loader_stage2.img"
+stage2_end:
diff --git a/src/arm/PL1/loader/loader_stage1_linker.ld b/src/arm/PL1/loader/loader_stage1_linker.ld
new file mode 100644
index 0000000..711fcbf
--- /dev/null
+++ b/src/arm/PL1/loader/loader_stage1_linker.ld
@@ -0,0 +1,16 @@
+ENTRY(_boot)
+
+SECTIONS
+{
+ /* see linker.ld for details */
+ . = 0x2000000;
+
+ __start = .;
+ loader_stage1 :
+ {
+ KEEP(loader_stage1.o)
+ }
+ __end = .;
+
+ _stack_top = 0x8000;
+}
diff --git a/src/arm/PL1/loader/loader_stage2.c b/src/arm/PL1/loader/loader_stage2.c
new file mode 100644
index 0000000..fc3ae1c
--- /dev/null
+++ b/src/arm/PL1/loader/loader_stage2.c
@@ -0,0 +1,33 @@
+#include <stddef.h>
+#include <stdint.h>
+#include "uart.h"
+#include "io.h"
+#include "global.h"
+
+void *const kernel_load_addr = ((void*) 0x8000);
+
+void _stage2_main(uint32_t r0, uint32_t r1, uint32_t atags)
+{
+ uart_init();
+
+ // get kernel size via uart (little endian)
+ uint32_t b0, b1, b2, b3;
+
+ b0 = getchar();
+ b1 = getchar();
+ b2 = getchar();
+ b3 = getchar();
+
+ uint32_t kernel_size = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
+
+ // load kernel at kernel_load_addr
+ char *dst = kernel_load_addr, *end = dst + kernel_size;
+
+ while (dst < end)
+ *(dst++) = getchar();
+
+ // jump to kernel
+ ((void(*)(uint32_t, uint32_t, uint32_t)) kernel_load_addr)
+ (r0, r1, atags);
+}
+
diff --git a/src/arm/PL1/loader/loader_stage2_linker.ld b/src/arm/PL1/loader/loader_stage2_linker.ld
new file mode 100644
index 0000000..33e79e9
--- /dev/null
+++ b/src/arm/PL1/loader/loader_stage2_linker.ld
@@ -0,0 +1,16 @@
+ENTRY(_stage2_main)
+
+SECTIONS
+{
+ /* see loader_stage1.S for details */
+ . = 0x4000;
+
+ __start = .;
+ loader_stage2 :
+ {
+ KEEP(loader_stage2.o(.text))
+ loader_stage2.o
+ uart.o
+ }
+ __end = .;
+}