diff --git a/README.adoc b/README.adoc index 1d488f2..75567ae 100644 --- a/README.adoc +++ b/README.adoc @@ -19482,6 +19482,27 @@ It is not possible to call those C functions from the examples that don't use a For this reason, we tend to create examples with bootloaders, as it is easier to write them portably. +=== Baremetal linker script + +For things to work in baremetal, we often have to layout memory in specific ways. + +Notably, since we start with <> disabled, there are more constraints on where memory can or cannot go. + +Especially for C programs, this memory layout is specified by a "linker script", which is present at: link:baremetal/link.ld[] + +Note how our linker script also exposes some symbols to C: + +.... +lkmc_heap_low = .; +lkmc_heap_top = .; +.... + +Those for example are required to implement `malloc` in Newlib. We can play with those variables more explicitly with link:baremetal/linker_variables.c[]: + +.... +./run --arch aarch64 --baremetal baremetal/linker_variables.c +.... + === Semihosting Semihosting is a publicly documented interface specified by ARM Holdings that allows us to do some magic operations very useful in development. diff --git a/baremetal/arch/aarch64/multicore.c b/baremetal/arch/aarch64/multicore.c index f371a8b..7ae918d 100644 --- a/baremetal/arch/aarch64/multicore.c +++ b/baremetal/arch/aarch64/multicore.c @@ -18,7 +18,7 @@ __asm__( " bne .Lsleep_forever\n" /* Prepare the stack for CPU1. This is what we need * this assembly function for. */ -" ldr x0, =(stack_top - 0x1000)\n" +" ldr x0, =(lkmc_stack_top - 0x1000)\n" " mov sp, x0\n" " bl main_cpu1\n" ".Lsleep_forever:\n" diff --git a/baremetal/arch/arm/multicore.c b/baremetal/arch/arm/multicore.c index 9c7599f..c2a3338 100644 --- a/baremetal/arch/arm/multicore.c +++ b/baremetal/arch/arm/multicore.c @@ -11,7 +11,7 @@ __asm__( "lkmc_cpu_not_0:\n" " cmp r0, 1\n" " bne .Lsleep_forever\n" -" ldr sp, =(stack_top - 0x1000)\n" +" ldr sp, =(lkmc_stack_top - 0x1000)\n" " bl main_cpu1\n" ".Lsleep_forever:\n" " wfe\n" diff --git a/baremetal/lib/aarch64.S b/baremetal/lib/aarch64.S index 8e175f3..683be09 100644 --- a/baremetal/lib/aarch64.S +++ b/baremetal/lib/aarch64.S @@ -20,7 +20,7 @@ _start: isb /* Prepare the stack for main, mandatory for C code. */ - ldr x0, =stack_top + ldr x0, =lkmc_stack_top mov sp, x0 /* https://cirosantilli.com/linux-kernel-module-cheat#magic-failure-string */ diff --git a/baremetal/lib/arm.S b/baremetal/lib/arm.S index 6f015d1..8fd7345 100644 --- a/baremetal/lib/arm.S +++ b/baremetal/lib/arm.S @@ -8,7 +8,7 @@ _start: bne lkmc_cpu_not_0 /* Prepare the stack for main, mandatory for C code. */ - ldr sp, =stack_top + ldr sp, =lkmc_stack_top /* Enable floating point. * Code copied from: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0409h/CHDEGGFF.html diff --git a/baremetal/lib/syscalls.c b/baremetal/lib/syscalls.c index 66268d2..9aa4494 100644 --- a/baremetal/lib/syscalls.c +++ b/baremetal/lib/syscalls.c @@ -12,8 +12,8 @@ enum { UART_FR_RXFE = 0x10, }; -extern char heap_low; -extern char heap_top; +extern char lkmc_heap_low; +extern char lkmc_heap_top; char *heap_end = 0; @@ -90,10 +90,10 @@ _CLOCK_T_ _times_r (struct _reent *r, struct tms *ptms) { caddr_t _sbrk(int incr) { char *prev_heap_end; if (heap_end == 0) { - heap_end = &heap_low; + heap_end = &lkmc_heap_low; } prev_heap_end = heap_end; - if (heap_end + incr > &heap_top) { + if (heap_end + incr > &lkmc_heap_top) { /* Heap and stack collision */ return (caddr_t)0; } diff --git a/baremetal/link.ld b/baremetal/link.ld index 063ef8b..7796bd1 100644 --- a/baremetal/link.ld +++ b/baremetal/link.ld @@ -1,3 +1,4 @@ +/* https://cirosantilli.com/linux-kernel-module-cheat#baremetal-linker-script */ ENTRY(_start) SECTIONS { @@ -10,10 +11,18 @@ SECTIONS } /* gem5 uses the bss as a measure of the kernel size. */ .bss : { *(.bss) } - heap_low = .; + /* Fix the addresses of everything that comes after, no matter + * the exact size of the code present in .text. This allows us to + * place CLI arguments in memory at a known location! */ + . = ADDR(.text) + 0x1000000; + lkmc_heap_low = .; . = . + 0x1000000; - heap_top = .; + lkmc_heap_top = .; . = . + 0x1000000; - stack_top = .; + lkmc_stack_top = .; + . = . + 0x1000000; + lkmc_argv = .; + . = . + 0x4; + lkmc_argc = .; } diff --git a/baremetal/linker_variables.c b/baremetal/linker_variables.c new file mode 100644 index 0000000..4b67472 --- /dev/null +++ b/baremetal/linker_variables.c @@ -0,0 +1,16 @@ +/* https://cirosantilli.com/linux-kernel-module-cheat#baremetal-linker-script */ + +#include +#include +#include + +extern int32_t lkmc_argc; +extern int32_t lkmc_heap_low; + +int main(int argc, char **argv) { + (void)argc; + (void)argv; + printf("&lkmc_heap_low %p\n", (void*)&lkmc_heap_low); + printf("&lkmc_argc %p\n", (void*)&lkmc_argc); + printf("lkmc_argc %" PRId32 "\n", lkmc_argc); +} diff --git a/build-baremetal b/build-baremetal index 4158b2a..4721d33 100755 --- a/build-baremetal +++ b/build-baremetal @@ -102,6 +102,7 @@ Build the baremetal examples with crosstool-NG. ) cc_flags.extend([ '-Wl,--section-start=.text={:#x}'.format(entry_address), LF, + '-Wl,--section-start=.lkmc_memory={:#x}'.format(entry_address + 0x1000000), LF, '-T', self.env['baremetal_link_script'], LF, ]) with thread_pool.ThreadPool(