SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Bootloader Code Example
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm

Bootloader Code Example

Graham Kruk
Last updated: September 8, 2023 12:51 pm
Graham Kruk 6 Min Read
Share
SHARE

A bootloader is a small piece of code that runs before any operating system is loaded. It is responsible for initializing hardware, setting up memory, and loading the operating system kernel and initial ramdisk into memory so that the operating system can boot. Understanding bootloader code can help developers customize and modify the boot process for their systems.

Contents
Bootloader StagesExample Bootloader Code FlowC Code ExampleBootloader Programming TipsConclusion

Bootloader Stages

A typical bootloader performs the following stages:

  1. Initialize hardware: The bootloader initializes key hardware components like the clock, memory controllers, GPIO pins, UARTs, and interrupt controllers.
  2. Setup memory: It sets up the different types of memory like DRAM, SRAM, and memory mapped peripherals. This includes configuring timings and memory controllers.
  3. Load kernel image: It loads the kernel image file (e.g. zImage on Linux) into RAM from boot media like flash memory or SD card.
  4. Uncompress kernel: If the kernel image is compressed, the bootloader uncompresses it into RAM.
  5. Load ramdisk: An initial ramdisk (initrd) with essential drivers and files is loaded into RAM.
  6. Kernel boot parameters: Any required kernel parameters like root partition info are set.
  7. Jump to kernel: Finally control is transferred to the kernel code by jumping to its entry point.

Example Bootloader Code Flow

Here is an example outline of the key functions in a simple C-based bootloader code:

  1. Reset handler: This code runs first after a reset and sets up the processor modes, stack pointer, and zeroes out .bss section.
  2. Initialize hardware: Clock, memory controllers, GPIOs, UARTs etc are initialized.
  3. Enable MMU: If an MMU is present, it is enabled to setup virtual memory.
  4. Load kernel image: Reads kernel binary from storage into RAM.
  5. Parse kernel image: Checks kernel headermagic number, architecture etc.
  6. Uncompress kernel: If kernel is compressed, uncompress into RAM.
  7. Load ramdisk: Optional ramdisk is loaded into RAM.
  8. Pass boot parameters: Passes information like ramdisk location in RAM to kernel.
  9. Flush caches: Clears caches to ensure no stale data exists.
  10. Jump to kernel: Transfers control to kernel entry point.

C Code Example

Here is a simplified C code example implementing some key stages of a bootloader: // Bootloader entry point void bootloader_start(void) { // Initialize hardware – clocks, memory, etc hardware_init(); // Initialize UART for debug prints uart_init(); // Initialize DRAM and map memory controllers dram_init(); // Enable MMU to setup virtual memory mmu_init(); // Read kernel image from storage (e.g. SD card) read_kernel(); // Uncompress kernel if needed uncompress_kernel(); // Load ramdisk into memory load_ramdisk(); // Pass boot parameters to kernel boot_params.ramdisk_addr = RAMDISK_ADDRESS; boot_params.ramdisk_size = ramdisk_size; // Flush caches flush_caches(); // Jump to kernel entry point jump_to_kernel(kernel_entry); } // Hardware init function void hardware_init() { // Init clock, gpio, interrupt control, timers } // Read compressed kernel from SD card void read_kernel() { // Open SD card device sd_fd = sd_open(“sd0”); // Seek to kernel position on card sd_seek(sd_fd, KERNEL_START_SECTOR); // Read compressed kernel into RAM buffer sd_read(sd_fd, kernel_buf, KERNEL_SIZE); // Close SD card device sd_close(sd_fd); } // Uncompress kernel void uncompress_kernel() { // Check kernel header magic if(kernel_buf[0] == 0x1F && kernel_buf[1] == 0x8B) { // Kernel is gzip compressed, uncompress it ungzip(kernel_buf, kernel_uncompressed_buf); } } // Load ramdisk file into memory void load_ramdisk() { // Similar SD card read logic to load ramdisk // into reserved RAM address } // Flush caches before jumping to kernel void flush_caches() { // Implementation depends on architecture // ARMv7 might just need flushing of // data cache and branch predictor } // Jump to kernel entry point void jump_to_kernel(uint32_t entry_point) { // Pass pointer to boot params structure register uint32_t r0 asm(“r0”) = (uint32_t)&boot_params; // Jump to kernel entry point asm volatile(“mov pc, %0” : : “r” (entry_point)); }

Bootloader Programming Tips

Here are some tips for developing bootloader code:

  • Use clear modular code organization with well defined interfaces.
  • Optimize for size – avoid higher level libraries and large stack usage.
  • Minimize initialization code to speed up boot time.
  • Use hardware abstraction layer for portability across SoCs.
  • Debug with LED blink codes, serial prints and/or debug registers.
  • Check return values and error codes from hardware accesses.
  • Verify memory interfaces thoroughly – incorrect configs can lockup SoC.
  • Test end-to-end frequently on actual hardware.
  • Allow passing optional boot configurations like UART baudrate.
  • Support features like recovery mode and firmware updates.

Conclusion

The bootloader performs the critical task of system initialization and loading the kernel. Understanding typical bootloader functionality like hardware init, memory setup, kernel loading and jumping to kernel entry helps developers customize their system boot process. Using well structured, modular code as per the hardware capabilities results in a robust, optimized bootloader. With thoughtful programming and sufficient testing on target hardware, developers can build effective bootloaders tailored to their system requirements.

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article How to Write a Bootloader for a Microcontroller
Next Article stm32 bootloader example
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

Watchdog Timer Architecture

A watchdog timer (WDT) is an important component in many…

8 Min Read

What instruction set do Cortex M processors use?

Cortex-M processors from Arm use the Thumb instruction set, which…

6 Min Read

ARM Cortex-M0 Boot Sequence

The ARM Cortex-M0 is a 32-bit RISC processor core designed…

8 Min Read

Cortex-m0 interrupt_demo simulation issue

The Cortex-M0 processor from ARM is one of the most…

6 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account