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).

FieldTypeDescription
magicuint32_tMust be 0xB007B007.
board_revisionuint32_tBoard revision from VideoCore mailbox. 0 on QEMU if mailbox does not provide it.
arm_mem_sizeuint32_tARM-accessible RAM size in bytes. From mailbox.
kernel_load_addruint32_tAddress where the payload was copied (same as NKRN load_addr).
kernel_entry_addruint32_tEntry address the bootloader jumped to (same as NKRN entry_addr).
kernel_sizeuint32_tPayload size in bytes (same as NKRN image_size).
bootloader_versionchar[16]Null-terminated string (e.g. “Neutron-1.0”).
kernel_versionchar[16]Kernel version from the NKRN header, formatted as “v.” (e.g. “v1.0”). Null-terminated string.

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_t structure (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_MAGIC before 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.cfg packed into the NKRN header by pack_kernel.py.

The test kernel in test_kernel/ uses the boot_info_t *info pointer 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.