What are TPL and UEFI Boot Stages in Linux–Embedded Linux Course In Hyderabad

PREV_LEC | NEXT_LEC

TPL and UEFI Boot Stages
The final handoff to Linux, on classic SoCs and on UEFI firmware — part of our free embedded Linux course

If you’ve followed this free embedded Linux course from the start, you already know how a processor wakes up, runs ROM code, and loads a small SPL into on-chip SRAM. This lecture picks up right where that left off: the final stage before Linux actually starts running, called the TPL (Tertiary Program Loader), and how the same idea plays out on UEFI-based systems. Understanding this stage matters because it’s where most of the “why won’t my board boot” problems in real embedded Linux development actually live.

TPL bootloader
U-Boot
UEFI boot
free embedded linux course
free linux kernel development course

What You Will Learn

  • Role of the TPL in a multi-stage boot sequence
  • How a full bootloader like U-Boot loads a kernel image, device tree, and initramfs
  • How UEFI firmware boots Linux differently from a classic SoC boot ROM
  • A hands-on U-Boot boot script you can try on QEMU
  • Common mistakes that stall a board right before the kernel banner

Prerequisites

This lecture assumes you’re comfortable with the earlier stages covered in this free embedded Linux course — specifically, what a reset vector is, what the SPL does, and why boot ROM code exists. You should also have basic familiarity with a Linux shell and, ideally, QEMU installed if you want to follow the hands-on section.

What Does the TPL Actually Do?

By the time control reaches the TPL, the processor has already run its boot ROM code and a small first-stage loader has brought up enough SDRAM to be useful. The TPL is the first piece of software in the whole boot chain that behaves like a “real” program — it has a command-line shell, drivers for storage devices, network support, and enough intelligence to make decisions about what to boot and how.

In most embedded Linux designs, the TPL is U-Boot or Barebox. Its one real job is simple to state even though the mechanics are not: get a kernel image, an optional device tree blob, and an optional initial RAM disk into memory at the right addresses, then jump to the kernel’s entry point. After that jump, the bootloader’s memory is typically overwritten or ignored — it does not participate in normal Linux operation.

Memory Layout at the End of the TPL Stage
+———————————————+
| SDRAM |
| |
| 0x8800_0000 +————————-+ |
| | TPL (U-Boot / Barebox) | |
| +————————-+ |
| | initramfs (optional) | |
| +————————-+ |
| | device tree blob (DTB) | |
| +————————-+ |
| | Linux kernel Image | |
| 0x8000_0000 +————————-+ |
| |
+———————————————+
| SoC on-chip SRAM: earlier-stage loader (SPL) |
| SoC Boot ROM: fixed, unmodifiable code |
+———————————————+

Notice the arrow of control here is one-directional. The boot ROM starts the SPL, the SPL starts the TPL, and the TPL starts the kernel — each stage trusts the layer above it just enough to hand off memory addresses and then gets out of the way. This layered handoff is exactly why embedded engineers taking a free linux kernel development course need to understand bootloaders before they ever touch a device driver: a kernel that never gets a correct memory address for its device tree will not boot, no matter how correct the driver code is.

The TPL’s Command-Line Shell

Unlike the SPL, the TPL usually exposes an interactive shell over a serial console. This is where most embedded developers spend their early bring-up time. Typical tasks performed from this shell include:

Task Typical U-Boot Command Why It Matters
Load a kernel image from storage fatload mmc 0:1 0x80000000 Image Places the kernel at the address Linux expects
Load a device tree blob fatload mmc 0:1 0x82000000 board.dtb Describes the hardware to the kernel
Set the kernel command line setenv bootargs console=ttyAMA0 root=/dev/mmcblk0p2 Controls early kernel behavior
Jump to the kernel booti 0x80000000 - 0x82000000 Hands off execution permanently
Flash new boot images mmc write / nand write Field updates without reflashing the whole board

Most production boards also configure an autoboot countdown, so the same commands run automatically without a human at the console — the interactive shell exists mainly for development, recovery, and field debugging.

Booting With UEFI Firmware

Most x86 embedded PCs, and a growing number of ARM designs, don’t use a vendor-specific SoC boot ROM at all — they use UEFI firmware instead. The three-phase idea from earlier in this free embedded linux course still applies, but the concrete mechanics change:

Phase Classic SoC Boot UEFI Boot
Phase 1 Boot ROM loads SPL from fixed flash offset UEFI boot manager firmware loads from NOR flash or on-chip ROM
Phase 2 SPL loads TPL into SDRAM Boot manager loads boot firmware from the EFI System Partition (ESP), disk, or PXE
Phase 3 (TPL) U-Boot / Barebox loads kernel + DTB + initramfs GRUB2 or a systemd-boot style loader loads the Linux kernel and initramfs

The ESP is worth calling out specifically because it trips up a lot of people new to embedded Linux development: it must be formatted as FAT32, and it’s identified by a fixed, standardized partition type GUID rather than by any vendor-specific convention. The bootloader binary itself lives at a predictable path inside that partition, named after the target machine architecture — for example, an x86_64 build’s loader binary sits under a fixed EFI boot directory using a machine-specific filename.

The two most common Linux-side UEFI boot loaders are GRUB2, which is extremely widely used but ships under a GPL variant with some secure-boot licensing friction, and lighter systemd-integrated boot managers, which trade some flexibility for simplicity. Functionally both do the same job as U-Boot’s booti command: locate a kernel image and optional initramfs, then jump to it.

Hands-On: A Minimal TPL Boot Script on QEMU

Rather than reproducing the book’s board-specific example, let’s build our own tiny, original boot script and test it entirely inside QEMU — no physical hardware required. This mirrors exactly what a free linux kernel development course exercise should feel like: reproducible, and safe to break.

# 1. Build a minimal ARM kernel and a matching virt device tree
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc) zImage dtbs

# 2. Build U-Boot for the QEMU ARM "virt" target
make qemu_arm_defconfig
make -j$(nproc)

# 3. Boot U-Boot on QEMU and drop into its shell
qemu-system-arm -M virt -m 512M -nographic \
  -bios u-boot.bin \
  -kernel /dev/null

# 4. From the U-Boot shell (interactive), run our own boot script
setenv ep_kernel_addr 0x42000000
setenv ep_fdt_addr    0x43000000
setenv bootargs "console=ttyAMA0 earlycon root=/dev/vda rw"
tftpboot ${ep_kernel_addr} zImage
tftpboot ${ep_fdt_addr} vexpress-v2p-ca9.dtb
bootz ${ep_kernel_addr} - ${ep_fdt_addr}

Expected console output once the jump succeeds:

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 6.x.x (ep-course-build)
[    0.000000] CPU: ARMv7 Processor [410fc090] revision 0
[    0.000000] Machine model: ARM Versatile Express
[    0.100000] Kernel command line: console=ttyAMA0 earlycon root=/dev/vda rw

The important thing to notice is the sequencing: nothing in this script assumes the kernel or DTB is baked into the image — both are loaded to explicit addresses by name, exactly the same pattern the memory diagram above describes, just driven from TFTP instead of an SD card for convenience under QEMU.

Common Mistakes and Troubleshooting

Kernel loads but the board hangs with no banner

Almost always a load-address mismatch between what the TPL loaded and what the kernel’s decompressor expects. Double-check your platform’s documented kernel load address.

“Wrong FDT magic” or similar device tree error

The device tree blob wasn’t loaded, or was loaded to the wrong address, or doesn’t match the board at all. This is one of the most common early-bring-up errors in embedded Linux development.

UEFI system won’t find the bootloader

The ESP wasn’t formatted as FAT32, or the loader binary isn’t at the expected fixed path for that architecture. UEFI firmware will not search arbitrary locations.

Best Practices

  • Keep kernel and DTB load addresses documented per board, not memorized
  • Verify image checksums before booting in production boot scripts
  • Never disable secure boot checks just to unblock local testing
  • Keep autoboot timeouts long enough to interrupt during bring-up

Summary and Key Takeaways

The TPL is the last piece of firmware standing between power-on and Linux — it loads the kernel, device tree, and initramfs into memory and then steps aside permanently. UEFI systems reach the same outcome through a standardized ESP-and-GUID mechanism instead of vendor-specific boot ROM logic, but the underlying job is identical. Getting comfortable with this handoff is one of the most practical skills you can build in any free embedded linux course, because it’s where the majority of real-world “board won’t boot” bugs actually live.

Conclusion

Once the TPL hands off to the kernel, our next lecture in this free linux kernel development course looks at exactly what information crosses that boundary — the kernel command line, the device tree pointer, and the initial RAM disk — and why the device tree replaced older, more fragile ways of describing hardware to Linux.

FAQ

What is a TPL in embedded Linux boot terminology?

TPL stands for Tertiary Program Loader — the third-stage, full-featured bootloader (commonly U-Boot or Barebox) that loads the Linux kernel, device tree, and initramfs into RAM before jumping to the kernel.

Is U-Boot the only TPL option?

No. Barebox is a common alternative on embedded ARM boards, and on UEFI systems GRUB2 or systemd-integrated boot managers serve the equivalent role.

Why does UEFI require a FAT32 partition?

The EFI System Partition standard mandates FAT32 so that UEFI firmware from any vendor can reliably read the bootloader binary without vendor-specific filesystem drivers.

Does the bootloader stay running once Linux boots?

No. Once control jumps to the kernel, the bootloader’s memory is typically reclaimed or overwritten and plays no further role in system operation.

Can I test bootloader behavior without real hardware?

Yes — QEMU can emulate common ARM and x86 targets, letting you test U-Boot and kernel boot scripts safely, as shown in this lecture’s hands-on section.

What happens if the device tree address is wrong?

The kernel will typically fail early with an invalid FDT magic error, since it cannot parse hardware information from that memory location.

Is GRUB2 required for UEFI Linux boots?

No — it’s the most common choice, but any UEFI-compliant loader capable of finding and starting a Linux kernel image will work.

Continue This Free Embedded Linux Course

Next up: how the kernel command line, device tree, and initramfs actually cross the bootloader-to-kernel boundary.

PREV_LEC | NEXT_LEC

 

Leave a Reply

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