Jack Li's Blog

Write bootloader for raspberry pi 3

We need three files:

  • kernel.ld: define the memory layout and tell the hardware where to find entry.S.
  • entry.S: run on single core, do initialization and jump to start.c.
  • start.c: first code.

Compile to the kernel image:

  1. Link entry.S and kernel.ld to create kernel.elf
  2. Build kernel.img by kernel.elf
  3. Boot qemu with kernel.img

kernel.ld #

  • ENTRY( _xxx ): tell system to find _entry for the following execution
  • 0x80000: first code will put at 0x800000 because 0 ~ 0x80000 are for io-mapping in arm64
  • text: put codes
  • data: initialized variable
  • bss: uinitialized variable
ENTRY( _entry )

SECTIONS
{
  . = 0x80000;
  .text :
  {
    *(.text)
  }
  .data :
  {
    *(.data)
  }
  .bss ALIGN(16) (NOLOAD) :
  {
    __bss_start = .;

    *(.bss)

    __bss_end = .;
  }
  _end = .;
}

entry.S #

  • global _entry: let kernel.ld locate
.section .text
.global _entry

_entry:
  mrs     x1, MPIDR_EL1     // Read Register to x1
  and     x1, x1, #3        // Extract cpu_id from x1
  cbz     x1, 2f            // If cpu=0, jump to 2

1:
  wfe                       // Wait for Event instruction
  b       1b                // Jump to 1

2:
  ldr     x1, =_entry       // Load the address of _entry to x1
  mov     sp, x1            // move stack pointer to x1

  // clear bss
  ldr     x1, =__bss_start  // Load __bss_start to x1
  ldr     x2, =__bss_end    // Load __bss_end to x2

3:
  cmp     x1, x2            // Compare x1 and x2
  beq     4f                // if x1=x2, jump to 4
  str     xzr, [x1], #8
  sub     x2, x2, #1
  cbnz    x2, 3b

4:
  bl      start             // Jump to start function in C
  b       1b                // Halt this core if return

start.c #

void
start()
{
    while(1){}
}

Makefile #

TOOLCHAIN_PREFIX = aarch64-linux-gnu-
CC = $(TOOLCHAIN_PREFIX)gcc
LD = $(TOOLCHAIN_PREFIX)ld
OBJCPY = $(TOOLCHAIN_PREFIX)objcopy

CFLAGS = -Wall

SRCS = start.c
OBJS = $(SRCS:.c=.o)

all: clean kernel.img

entry.o: entry.S
	$(CC) $(CFLAGS) -c entry.S -o entry.o

# Rule to compile .c files to .o files
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# Linking the object files to create kernel.elf
kernel.elf: entry.o
	$(LD) -T kernel.ld -o kernel.elf entry.o

# Convert kernel.elf to kernel.img
kernel.img: kernel.elf
	$(OBJCPY) -O binary kernel.elf kernel.img

clean:
	rm -f *.o kernel.elf kernel.img

qemu: kernel.img
	qemu-system-aarch64 -M raspi3b -kernel kernel.img -display none -serial null -serial stdio