rpi-hal 0.10.0

Hardware abstraction layer for bare-metal Raspberry Pi: BCM2836/BCM2837 (Pi 2, Pi 3), with preliminary BCM2711 (Pi 4), BCM2835 (Pi 1, Pi Zero) and BCM2712 (Pi 5) support
Documentation
ENTRY(_start)

SECTIONS
{
    /* Standard load address for a 32-bit ARM kernel.img on the Pi. */
    . = 0x8000;

    .text :
    {
        KEEP(*(.text.boot))
        *(.text .text.*)
    }

    .rodata :
    {
        *(.rodata .rodata.*)
    }

    .data :
    {
        *(.data .data.*)
    }

    /* Sizes of the regions reserved below. Each is overridable from
       outside without editing this script -- a consumer passes e.g.
       `-Wl,--defsym=__stack_size=0x400000` in its own `rustflags` and
       that definition wins, since `DEFINED` sees it before this
       assignment runs. */
    __stack_size      = DEFINED(__stack_size)      ? __stack_size      : 0x100000;
    __stack_slack     = DEFINED(__stack_slack)     ? __stack_slack     : 0x200000;
    __irq_stack_size  = DEFINED(__irq_stack_size)  ? __irq_stack_size  : 0x10000;
    __abt_stack_size  = DEFINED(__abt_stack_size)  ? __abt_stack_size  : 0x8000;
    __und_stack_size  = DEFINED(__und_stack_size)  ? __und_stack_size  : 0x8000;
    __fiq_stack_size  = DEFINED(__fiq_stack_size)  ? __fiq_stack_size  : 0x8000;

    /* The stacks, reserved here rather than left to grow down from the
       load address.

       `boot.s` used to set `sp` to `_start` itself, which made the size
       of a program's stack a consequence of where the firmware loads the
       image: 32 KiB below 0x8000 on AArch32, 512 KiB below 0x80000 on
       AArch64. A number nobody chose, differing 16-fold between the two
       architectures, discoverable only by reading the boot assembly, and
       invisible until an application does enough real work to outgrow it
       -- at which point it presents as an unexplained hang somewhere
       else entirely. It also grew *downward through low memory*, where
       the firmware leaves the ATAGs and (on AArch64) the armstub8 spin
       table `multicore` uses to start cores 1-3.

       Reserving the region here fixes the size at a number in a file,
       the same on both architectures, and puts it somewhere nothing else
       claims. `NOLOAD` means it costs no image bytes; on a board with at
       least 1 GiB (every Pi this crate supports) the few MiB of address
       space are noise.

       `__stack_slack` below the stack is the guard region: the `mmu`
       feature leaves it with no translation (see
       `mmu::install_stack_guard`), so an overflow faults on the
       instruction that causes it rather than walking into
       .data/.rodata/.text and corrupting the running program.

       2 MiB-aligned because that is the coarser of the two granules in
       play -- an AArch64 block, against AArch32's 1 MiB section -- so
       the region is a whole number of descriptors on either
       architecture and no real memory shares one with it. Shrinking it
       below one granule leaves no guard at all: the region is rounded
       inward to whole descriptors. */
    .stack (NOLOAD) : ALIGN(0x200000)
    {
        __stack_slack_bottom = .;
        . += __stack_slack;

        /* Main (SVC) mode. */
        __stack_bottom = .;
        . += __stack_size;
        __stack_top = .;

        /* The banked modes, each with its own region: adjacent to the
           main stack rather than carved out of the middle of it. The
           previous arrangement set the IRQ stack to `_start -
           IRQ_STACK_SIZE`, 4 KiB into the region main mode was growing
           through, so any main-mode frame deeper than 4 KiB sat in
           memory the first interrupt would push onto.

           ABT/UND/FIQ get stacks so that a real `__unhandled_exception`
           -- this crate's under `fault-report`, or an application's
           overriding the weak default in fault_fallback.s -- can be
           ordinary Rust: those handlers run in their own mode, on their own
           banked `sp`, and pushing onto one that was never initialized
           faults again immediately -- reporting nothing, which is the
           failure the override exists to fix. */
        . += __irq_stack_size;
        __irq_stack_top = .;
        . += __abt_stack_size;
        __abt_stack_top = .;
        . += __und_stack_size;
        __und_stack_top = .;
        . += __fiq_stack_size;
        __fiq_stack_top = .;
    }

    .bss (NOLOAD) :
    {
        /* stmia-based zeroing in boot.s requires a word-aligned start;
           without this, __bss_start lands on whatever non-aligned byte
           .data happened to end on, and an unaligned stm data-aborts
           with no vector table to catch it. Every example so far has
           happened to land aligned by coincidence of code size — this
           was a latent bug, caught when rpi-loader's size shifted it
           off by one byte. */
        . = ALIGN(4);
        __bss_start = .;
        *(.bss .bss.*)
        *(COMMON)
        /* Word-align the end too, not just the start above. boot.s's
           stmia zeroing already requires the aligned start; aligning the
           end as well keeps __bss_end word-aligned, which is what
           consumers that build on top of it expect — e.g. a heap placed
           at __bss_end (see examples/heap_alloc.rs) then starts on a
           clean boundary instead of whatever odd byte .bss happened to
           end on. */
        . = ALIGN(4);
        __bss_end = .;
    }

    /DISCARD/ :
    {
        *(.comment)
        *(.note.*)
        *(.ARM.attributes)
        *(.ARM.exidx*)
        *(.ARM.extab*)
    }
}