diff options
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | loader_stage1.c | 30 | ||||
-rw-r--r-- | loader_stage1.ld | 47 | ||||
-rw-r--r-- | loader_stage2.c | 40 | ||||
-rw-r--r-- | loader_stage2.ld | 44 |
5 files changed, 186 insertions, 1 deletions
@@ -17,13 +17,37 @@ kernel.elf : boot.o kernel.o uart.o kernel7.img : kernel.elf arm-none-eabi-objcopy $^ -O binary $@ +loader_stage2.o : loader_stage2.c + arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ + +loader_stage2.elf : loader_stage2.o uart.o + arm-none-eabi-gcc -T loader_stage2.ld -o $@ -ffreestanding -O2 -nostdlib $^ -lgcc + +loader_stage2.img : loader_stage2.elf + arm-none-eabi-objcopy $^ -O binary $@ + +loader_stage2_embeddable.o : loader_stage2.img + arm-none-eabi-objcopy -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata $^ $@ + +loader_stage1.o : loader_stage1.c + arm-none-eabi-gcc $(CFLAGS) -c $^ -o $@ + +loader.elf : boot.o loader_stage1.o uart.o loader_stage2_embeddable.o + arm-none-eabi-gcc -T loader_stage1.ld -o $@ -ffreestanding -O2 -nostdlib $^ -lgcc + +loader.img : loader.elf + arm-none-eabi-objcopy $^ -O binary $@ + qemu-elf : kernel.elf qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^ qemu-bin : kernel7.img qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^ +qemu-loader : loader.img + qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel $^ + clean : - -rm kernel7.img kernel.elf boot.o kernel.o uart.o + -rm *.img *.elf *.o .PHONY: all qemu-elf qemu-bin clean diff --git a/loader_stage1.c b/loader_stage1.c new file mode 100644 index 0000000..9492747 --- /dev/null +++ b/loader_stage1.c @@ -0,0 +1,30 @@ +#include <stddef.h> +#include <stdint.h> +#include <uart.h> +#include <global.h> + +char *const stage2_addr = ((void*) 0x4000); + +// there's one tricky thing about embedding file in executable; +// mainly, symbols are visible to c code as extern chars, but the actual +// values are their adresses... see the code below +extern char + _binary_loader_stage2_img_start, + _binary_loader_stage2_img_end, + _binary_loader_stage2_img_size; + +void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) +{ + // uart_init() call to be moved to stage2... + uart_init(); + uart_puts("Hello, bootloader World!\r\n"); + + // stage2 of the bootloader is a blob embedded in executable; + // copy it over to it's destination place + // TODO implement a memcpy() somewhere and use it instead of loops + for (size_t i = 0; i < (size_t) &_binary_loader_stage2_img_size; i++) + stage2_addr[i] = (&_binary_loader_stage2_img_start)[i]; + + // jump to stage2 + ((void(*)(uint32_t, uint32_t, uint32_t))stage2_addr)(r0, r1, atags); +} diff --git a/loader_stage1.ld b/loader_stage1.ld new file mode 100644 index 0000000..ce11095 --- /dev/null +++ b/loader_stage1.ld @@ -0,0 +1,47 @@ +ENTRY(_start) + +SECTIONS +{ + /* Starts at LOADER_ADDR. */ + /* Warning! Internet says RPis in 32-bit mode load binary at 0x8000! */ + /* My experiments do, however, show, that qemu emulating RPi2 */ + /* loads it at 0x10000! (took some pain to find out) */ + . = 0x10000; + /* For AArch64, use . = 0x80000; Unless this too is wrong */ + __start = .; + __text_start = .; + .text : + { + KEEP(*(.text.boot)) + *(.text) + } + . = ALIGN(4096); /* align to page size */ + __text_end = .; + + __rodata_start = .; + .rodata : + { + *(.rodata) + } + . = ALIGN(4096); /* align to page size */ + __rodata_end = .; + + __data_start = .; + .data : + { + *(.data) + } + . = ALIGN(4096); /* align to page size */ + __data_end = .; + + __bss_start = .; + .bss : + { + bss = .; + *(.bss) + } + . = ALIGN(4096); /* align to page size */ + __bss_end = .; + __bss_size = __bss_end - __bss_start; + __end = .; +} diff --git a/loader_stage2.c b/loader_stage2.c new file mode 100644 index 0000000..98ce571 --- /dev/null +++ b/loader_stage2.c @@ -0,0 +1,40 @@ +#include <stddef.h> +#include <stdint.h> +#include <uart.h> +#include <global.h> + +void *const kernel_load_addr = ((void*) 0x8000); + +void __attribute__((section(".text.stage2main"))) +stage2(uint32_t r0, uint32_t r1, uint32_t atags) +{ + // Declare as unused + (void) r0; + (void) r1; + (void) atags; + + uart_puts("hello stage2!\n\r"); + + // get kernel size via uart (little endian) + uint32_t b0, b1, b2, b3; + + b0 = uart_getc(); + b1 = uart_getc(); + b2 = uart_getc(); + b3 = uart_getc(); + + 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++) = uart_getc(); + + // jump to kernel + // TODO also forward arguments (r0, r1, atags) + asm volatile("bx %0" :: "r" (kernel_load_addr) : "memory"); +} + +void *const _start = ((void*) stage2); // for linker script + diff --git a/loader_stage2.ld b/loader_stage2.ld new file mode 100644 index 0000000..8f215e9 --- /dev/null +++ b/loader_stage2.ld @@ -0,0 +1,44 @@ +ENTRY(_start) + +SECTIONS +{ + /* stage2 bootloader gets loaded at 0x4000 */ + . = 0x4000; + __start = .; + __text_start = .; + .text : + { + /* have entry point at the beginning */ + KEEP(*(.text.stage2main)) + *(.text) + } + . = ALIGN(4096); /* align to page size */ + __text_end = .; + + __rodata_start = .; + .rodata : + { + *(.rodata) + } + . = ALIGN(4096); /* align to page size */ + __rodata_end = .; + + __data_start = .; + .data : + { + *(.data) + } + . = ALIGN(4096); /* align to page size */ + __data_end = .; + + __bss_start = .; + .bss : + { + bss = .; + *(.bss) + } + . = ALIGN(4096); /* align to page size */ + __bss_end = .; + __bss_size = __bss_end - __bss_start; + __end = .; +} |