The bootloader includes several minimal drivers for the BCM2837/BCM2710 peripherals. All use memory-mapped I/O at the addresses defined in include/platform.h.

UART (PL011)

Purpose: Serial console for debug and user output. Wired to GPIO 14 (TXD0) and GPIO 15 (RXD0), alternate function 0.

Base address: 0x3F201000 (UART0_BASE).

Initialisation: uart_init() disables the UART, configures GPIO 14/15 via the GPIO driver, clears pending interrupts, sets baud rate divisors (IBRD=26, FBRD=3 for 115200 at 48 MHz), enables 8N1 and FIFO, masks interrupts, then enables the UART with TX and RX enabled.

Usage: uart_putc(), uart_puts(), uart_getc(), uart_printf() and helpers. Newline is translated to CR LF for terminal compatibility.

The test kernel reinitialises the UART in its own code; it does not rely on the bootloader leaving the UART in a particular state after the jump.

GPIO

Purpose: Set pin function and pull-up/down. Used by the UART driver to assign pins 14 and 15 to UART0.

Base address: 0x3F200000 (GPIO_BASE).

Functions: gpio_set_func(pin, func) writes the appropriate GPFSEL register. gpio_set_pull(pin, pull) uses the GPPUD and GPPUDCLK sequence with timed delays as required by the BCM2835/2837 documentation. gpio_set, gpio_clear, gpio_get control or read output level.

Register offsets: GPFSEL1 (pins 10–19), GPPUD, GPPUDCLK0/1, and set/clear/level registers are used as in the Broadcom peripheral specification.

Mailbox

Purpose: Communicate with the VideoCore (GPU) to obtain board revision and ARM memory size.

Base address: 0x3F00B880 (MBOX_BASE). Registers: read, status, write. Status bits: full (0x80000000), empty (0x40000000).

Protocol: The buffer must be 16-byte aligned. Lower 4 bits of the written value are the channel number (8 for property tags). The bootloader uses mbox_get_board_revision() and mbox_get_arm_mem_size(), which build a small tag buffer and call mbox_call(buf, MBOX_CH_PROP).

On QEMU raspi3b, mailbox responses may be zero or minimal. On real hardware, board revision and memory size are populated and can be used by the kernel.

SD Card (EMMC/SDHCI)

Purpose: Read blocks from the SD card so that the FAT32 driver can read the partition and file system.

Interface: The driver uses the Arasan SDHCI-style registers defined in include/sdcard.h (EMMC_* macros). Initialisation follows the standard sequence: reset, clock, CMD0, CMD8, ACMD41, CMD2, CMD3, CMD7, CMD16. Block reads use CMD17/18 and the data FIFO.

API: sdcard_init() returns SD_OK or an error code. sdcard_read_block(lba, buf) and sdcard_read_blocks(lba, count, buf) read 512-byte sectors. For SDHC, LBA is the block number; for SDSC, the argument is the byte address (LBA * 512).

The QEMU raspi3b machine may wire the SD card to the SDHOST controller at 0x3F202000 rather than the EMMC controller. The current driver and header use the EMMC register set. If SD init fails in QEMU, verify which controller your QEMU version uses and that the driver matches.

FAT32

Purpose: Read the first partition as FAT32 and load a file by name (e.g. ATOM.BIN) into a buffer.

Implementation: fat32_mount() reads sector 0 (MBR), finds the first partition, reads its first sector (VBR/BPB), and parses the BPB for cluster size, FAT position, and root directory. fat32_read_file(filename, dest, dest_size, bytes_read) searches the root directory for the given 8.3 name (case-insensitive), follows the cluster chain, and reads the file contents into dest. No subdirectories; no write support.

Limits: File size is limited by FAT32_MAX_FILE_SIZE (e.g. 8 MiB) and by the destination buffer. The kernel image must fit in the staging area (0x100000, size up to KERNEL_MAX_SIZE).

Dependencies

  • UART depends on GPIO for pin muxing.
  • FAT32 depends on the SD card driver for sector reads.
  • Main bootloader code depends on UART, mailbox, SD, and FAT32; it does not use GPIO directly except through UART init.

For memory map and register addresses, see Memory Map. For build and inclusion of these sources, see Build Configuration.