Before jumping to the kernel, the Neutron bootloader writes a small structure at a fixed physical address and passes a pointer to it in register x0. The kernel can use this to discover load address, entry address, kernel size, board revision, and ARM memory size without hardcoding addresses.
Location and Magic
Address: Physical address 0x1000 (BOOT_INFO_ADDR in include/neutron.h).
Magic: The first field is magic and must equal 0xB007B007 (BOOT_INFO_MAGIC). If the kernel reads a pointer from x0 and finds a different magic value, it can treat the boot as not coming from Neutron (e.g. direct load by GPU or another bootloader).
Structure Layout (boot_info_t)
The layout is defined in include/neutron.h. Both the bootloader and the test kernel include this header so they stay in sync. Your own kernel can also include it (or duplicate the layout if you intentionally want to decouple).
| Field | Type | Description |
|---|---|---|
| magic | uint32_t | Must be 0xB007B007. |
| board_revision | uint32_t | Board revision from VideoCore mailbox. 0 on QEMU if mailbox does not provide it. |
| arm_mem_size | uint32_t | ARM-accessible RAM size in bytes. From mailbox. |
| kernel_load_addr | uint32_t | Address where the payload was copied (same as NKRN load_addr). |
| kernel_entry_addr | uint32_t | Entry address the bootloader jumped to (same as NKRN entry_addr). |
| kernel_size | uint32_t | Payload size in bytes (same as NKRN image_size). |
| bootloader_version | char[16] | Null-terminated string (e.g. “Neutron-1.0”). |
| kernel_version | char[16] | Kernel version from the NKRN header, formatted as “v |
The structure is packed (no padding). Total size is 4 + 4 + 4 + 4 + 4 + 4 + 16 + 16 = 56 bytes (plus any padding to alignment if you add fields in a custom build).
Device Tree Blob (DTB) and x1 Register
The bootloader follows the ARM boot protocol convention where the kernel entry point is called with additional boot parameters in registers beyond x0:
- x0 = Physical address of
boot_info_tstructure (0x1000) - x1 = Device Tree Blob (DTB) address passed by the ARM firmware
The DTB address is received by the bootloader from the ARM firmware (GPU) in the x0 register during bootloader startup. The bootloader then preserves this address and passes it to the kernel in the x1 register when jumping to the kernel entry point.
For kernel implementers: If your kernel needs to use the device tree (e.g. for hardware discovery or to support different hardware variants), read the DTB address from x1 during kernel startup. Save x1 in a callee-saved register early (e.g. x20) before clobbering x1.
How the Kernel Receives It
- The bootloader calls the kernel entry point with x0 = physical address of this structure (0x1000) and x1 = DTB address from ARM firmware.
- The kernel entry (e.g. in assembly) should save both x0 and x1 (e.g. in callee-saved registers) before setting up the stack, then pass x0 as the first argument to the C entry (e.g.
kernel_main(boot_info_t *info)) and preserve x1 for potential DTB processing. - In C, the first argument corresponds to x0, so the prototype can be
void kernel_main(boot_info_t *info).
Using the Fields
- magic: Check that it equals
BOOT_INFO_MAGICbefore trusting other fields. - board_revision, arm_mem_size: Use for board-specific or memory-sizing logic. On real hardware these are set from the mailbox; on QEMU they may be zero.
- kernel_load_addr, kernel_entry_addr, kernel_size: Useful for introspection or for passing to secondary code. Usually the kernel is linked to run at the same address as load_addr.
- bootloader_version: For logging or compatibility checks with the bootloader.
- kernel_version: For logging or version tracking. Contains the kernel version string from
build.cfgpacked into the NKRN header bypack_kernel.py.
The test kernel in
test_kernel/uses theboot_info_t *infopointer passed by the bootloader (x0) and prints the fields from that structure; it does not hardcode the board revision, memory size, or kernel addresses. It does still initialise UART/GPIO on its own.
If the kernel is entered without going through Neutron (e.g. loaded directly by the GPU at 0x80000), x0 may not point to a valid boot_info_t. Always check the magic before dereferencing.
See Building a Kernel for how to set up the kernel entry and receive x0.