ARM Bootloader


The bootloader is the program that runs in an embedded processor immediately after a reset of the processor and is responsible for initializing the connected devices and starting the main software (for example, the kernel of an OS). The ARM bootloader defines the vector table of the computing environment.

Assume that the main program is coded in C and the entry function is called entry as shown below:

int entry(void)
{
  return 0;
}

which is compiled for the ARM Cortex-M0 (ARMv6-M architecture) target like this:

arm-none-eabi-gcc -c -mcpu=cortex-m0 hello_world.c -o hw-entry.o


We also create an assembly file (hw-startup.s) that specifies the vector table, including the reset handler:

/* Vector table for bootloader. Assemble via
       arm-none-eabi-as -mcpu=cortex-m0 hw-startup.s -o hw-startup.o
*/
.section INTERRUPT_VECTOR, "x"
.global _Reset
_Reset:
  b Reset_Handler /* Reset */
  b .             /* Undefined */
  b .             /* SWI */
  b .             /* Prefetch Abort */
  b .             /* Data Abort */
  b .             /* Reserved */
  b .             /* IRQ */
  b .             /* FIQ */

Reset_Handler:
  /* In Thumb mode we can not load into SP directly.
  Only into the eight low registers */ 
  ldr r1, =stack_top
  mov sp, r1
  /* In ARM mode we can simply write
  ldr sp, =stack_top
  */
  bl entry
  b .

and assemble like this:

arm-none-eabi-as -mcpu=cortex-m0 hw-startup.s -o hw-startup.o


The object files are linked into an executable Linux binary in ELF format like this:

arm-none-eabi-ld -T hw-boot.ld hw-entry.o hw-startup.o -o hello_world.elf

where hw-boot.ld is the linker file that defines where the code from the object files will be placed into the executable:

ENTRY(_Reset)
SECTIONS
{
  . = 0x0;
  .text : {
    hw-startup.o (INTERRUPT_VECTOR)
    *(.text)
  }
  .data : { *(.data) }
  .bss : { *(.bss COMMON) }
  . = ALIGN(8);
  . = . + 0x100; /* allocating 4 KB for stack memory */
  stack_top = .;
}

The ELF file can be inspected with the readelf tool like this:

arm-none-eabi-readelf -A hello_world.elf
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "Cortex-M0"
  Tag_CPU_arch: v6S-M
  Tag_CPU_arch_profile: Microcontroller
  Tag_THUMB_ISA_use: Thumb-1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_enum_size: small

and disassembled via objdump like this:

arm-none-eabi-objdump -d hello_world.elf

hello_world.elf:     file format elf32-littlearm


Disassembly of section .text:

00000000 <_Reset>:
   0:	e006      	b.n	10 <Reset_Handler>
   2:	e7fe      	b.n	2 <_Reset+0x2>
   4:	e7fe      	b.n	4 <_Reset+0x4>
   6:	e7fe      	b.n	6 <_Reset+0x6>
   8:	e7fe      	b.n	8 <_Reset+0x8>
   a:	e7fe      	b.n	a <_Reset+0xa>
   c:	e7fe      	b.n	c <_Reset+0xc>
   e:	e7fe      	b.n	e <_Reset+0xe>

00000010 <Reset_Handler>:
  10:	4902      	ldr	r1, [pc, #8]	; (1c <Reset_Handler+0xc>)
  12:	468d      	mov	sp, r1
  14:	f000 f804 	bl	20 <entry>
  18:	e7fe      	b.n	18 <Reset_Handler+0x8>
  1a:	0000      	.short	0x0000
  1c:	00000130 	.word	0x00000130

00000020 <entry>:
  20:	b580      	push	{r7, lr}
  22:	af00      	add	r7, sp, #0
  24:	2300      	movs	r3, #0
  26:	0018      	movs	r0, r3
  28:	46bd      	mov	sp, r7
  2a:	bd80      	pop	{r7, pc}


Finally, objcopy is used to remove the ELF header and convert the file into an executable that can be run as-is on the Cortex-M0.

arm-none-eabi-objcopy -O binary hello_world.elf hello_world.bin