free linux development course
free linux device drivers course
free linux kernel development course
mkimage boot image
U-Boot has no filesystem of its own and no concept of an ELF loader like Linux does. Before it can boot anything,
it needs a simple, self-describing wrapper around the raw kernel binary — architecture, load address, entry point,
and a checksum, all in one small header. That wrapper is built by a tool called mkimage, and this
lecture in our free embedded Linux course covers exactly how it works and how to build one from
scratch.
What You Will Learn
- Why U-Boot needs a wrapped image instead of a raw binary
- The legacy uImage format and its header fields
- Building a uImage with mkimage, end to end
- Why FIT images have mostly replaced the legacy format
- Inspecting an image with U-Boot’s own tools
Prerequisites
This lecture builds on the U-Boot environment variables covered previously in this free linux device
drivers course — you’ll see ${} variable expansion reused here. You should also have a Linux
kernel build (even a small cross-compiled one) available to wrap.
Why Wrapping Is Necessary
A raw kernel binary, whether it’s zImage, Image, or a compressed variant, is just a
stream of bytes to U-Boot. Without extra information, U-Boot has no way to know which CPU architecture the binary
targets, where in RAM to place it, where execution should jump to once it’s loaded, or whether the bytes are even
intact after being copied over a network or read from flash. The legacy U-Boot image format solves this by
prepending a fixed 64-byte header to the payload before anything is written to storage.
Anatomy of a Legacy uImage Header
| Field | Purpose |
|---|---|
| Magic number | Identifies the file as a valid U-Boot image |
| Header CRC | Checksum of the header itself |
| Architecture | e.g. ARM, ARM64, RISC-V, x86 |
| OS | e.g. Linux, VxWorks, standalone |
| Image type | Kernel, ramdisk, multi-file, script, FIT |
| Compression | none, gzip, lzo, etc. |
| Load address | Where the payload should be placed in RAM before execution |
| Entry point | Where execution jumps to after loading |
| Data size & CRC | Length and checksum of the actual payload |
Note that load address and entry point are usually — but not always — the same value; they differ for images that
need self-relocation logic before running.
Building a uImage With mkimage
The command-line tool exposes each of these header fields as a flag. A minimal invocation for an ARM Linux kernel
looks like this:
mkimage -A arm -O linux -T kernel -C none \
-a 0x82000000 -e 0x82000000 \
-n 'ep-demo-kernel' \
-d Image ep-uImage
Breaking that down:
-A arm # target architecture
-O linux # target operating system
-T kernel # image type (kernel, ramdisk, script, etc.)
-C none # compression already applied, or none
-a 0x82000000 # load address in RAM
-e 0x82000000 # entry point (jump target after load)
-n 'name' # free-text image name, shown by iminfo
-d Image # input payload file
ep-uImage # output file name
Running it produces output confirming the header was written and giving you the final image size:
$ mkimage -A arm -O linux -T kernel -C none -a 0x82000000 -e 0x82000000 \
-n 'ep-demo-kernel' -d Image ep-uImage
Image Name: ep-demo-kernel
Created: Wed Aug 12 09:41:02 2026
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 9842176 Bytes = 9614.41 KiB = 9.39 MiB
Load Address: 82000000
Entry Point: 82000000
Inspecting an Image
Once the image is on your board’s storage, U-Boot’s own iminfo command parses and verifies the
header without booting it — useful for confirming a transfer completed correctly before committing to a boot
attempt:
U-Boot# iminfo ${ep_kernel_addr}
## Checking Image at 82000000 ...
Legacy image found
Image Name: ep-demo-kernel
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 9842176 Bytes = 9.4 MiB
Load Address: 82000000
Entry Point: 82000000
Verifying Checksum ... OK
FIT Images: The Modern Replacement
The legacy header above only wraps a single payload. Most current boards use a Flattened Image Tree
(FIT) instead, which packages a kernel, one or more device trees, and optionally a ramdisk into a single
signed, verifiable structure described by an .its source file. mkimage builds FIT images the same way,
just with a different flag:
mkimage -f ep-demo.its ep-demo.itb
A minimal .its source looks like this:
/dts-v1/;
/ {
description = "EP demo FIT image";
images {
kernel-1 {
description = "Linux kernel";
data = /incbin/("Image");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x82000000>;
entry = <0x82000000>;
};
fdt-1 {
description = "Board device tree";
data = /incbin/("board.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
};
};
configurations {
default = "conf-1";
conf-1 {
description = "Default boot configuration";
kernel = "kernel-1";
fdt = "fdt-1";
};
};
};
FIT’s real advantage over the legacy format is that it supports cryptographic signing of individual images and
configurations, which is the basis for U-Boot’s verified boot chain — something the legacy uImage format was never
designed to do.
+——————+ +—————————+
| 64-byte header | | FDT-based image tree |
| (arch, load addr, | | – kernel-1 { data… } |
| entry, checksum) | | – fdt-1 { data… } |
+——————+ | – ramdisk-1{ data… } |
| kernel payload | | – configurations { |
+——————+ | conf-1 { kernel, |
| fdt, |
| signature }|
| } |
+—————————+
one payload multiple payloads,
no signing optional signing
Common Mistakes and Troubleshooting
| Mistake | Symptom | Fix |
|---|---|---|
Wrong -A architecture |
U-Boot refuses to boot the image | Match the flag to your actual target CPU, not the host |
| Load address collides with U-Boot itself | Board hangs or resets during boot | Check your board’s memory map before choosing addresses |
Forgetting -C matches actual compression |
“Bad Data CRC” or boot failure | Set -C to whatever compression, if any, was actually applied to the payload |
| Corrupted transfer to the board | iminfo reports checksum failure |
Re-transfer the file; verify with iminfo before booting |
Best Practices
- Always run
iminfoon a freshly transferred image before attemptingbootm/booti. - Prefer FIT images for any new board bring-up — legacy uImage is still supported mainly for backward compatibility.
- Keep load addresses documented per board, since they depend on RAM layout, not on U-Boot itself.
- Version your
.itssource files alongside your kernel build scripts so image structure is reproducible.
Security Considerations
FIT image signing, combined with U-Boot’s verified boot support, lets a board refuse to run any kernel or device
tree that wasn’t signed by a trusted key — the foundation of a secure boot chain. The legacy uImage format has no
equivalent protection; its checksum only detects corruption, not tampering.
Summary and Key Takeaways
- mkimage wraps a raw kernel/ramdisk/dtb payload with metadata U-Boot needs to boot it safely.
- The legacy uImage format uses a fixed 64-byte header with a single payload.
- FIT images bundle multiple payloads and support cryptographic signing for secure boot.
iminfolets you verify an image’s integrity before committing to a boot.
Conclusion
mkimage is the bridge between “I have a compiled kernel” and “U-Boot can actually run it.” With a wrapped image
in hand, the next step in this free linux kernel development course is getting that image onto
your board’s storage in the first place — which is exactly what the next lecture covers.
FAQ
Do I need mkimage if I only use FIT images?
Yes — mkimage builds both legacy uImages and FIT images; the -f flag simply switches it to
FIT mode using an .its description file instead of individual header flags.
What happens if load address and entry point differ?
The payload is placed in RAM at the load address, but execution jumps to the entry point instead — used for
images with a small relocation stub that runs before the real kernel entry.
Can mkimage compress the payload itself?
No, mkimage only records which compression was used in the header; you must compress the payload yourself first
(e.g. with gzip) and then tell mkimage via -C.
Why would I choose FIT over legacy uImage on a new board?
FIT supports multiple images, device tree selection, and cryptographic signing — legacy uImage supports none
of these, so nearly all new designs use FIT.
What does the header CRC actually protect against?
It detects accidental corruption of the header or payload during storage or transfer — it is not a security
mechanism and does not prevent a malicious or unauthorized image from booting.
Continue the Free Embedded Linux Bootloader Course
