Linking a devicetree blob to a kernel image

Home / blog / Linking a devicetree blob to a kernel image

This post describes how to modify the Linux kernel ARM setup code, so a devicetree can be selected at compile time and linked in the uncompressed kernel image.

The problem

customer: We want to use an uncompressed kernel image on our new ARM-based product. We believe it might help speed up the boot.
me: Sure, that’s doable.
customer: And we also want to hard-code the devicetree blob inside the kernel image.
me: Hm, I think that might be doable. There’s probably an option somewhere.

Simple task, right? Well, not exactly. In fact, after a little bit of research, I found out it wasn’t as simple as turning on some kernel option. Embedded Linux isn’t as easy as it could be.

Many architectures allow to choose a devicetree to be built and linked against the kernel image. This is handy in many situations, and can be useful even in production. Notably, ARM is an exception! There is a way to append a devicetree blob to the compressed kernel image, but it’s quite hackish and it’s not available for uncompressed booting.

That said, embedding a devicetree blob to a kernel image is a simple task, and modifying the early ARM code to use it is even simpler. As I mentioned earlier, other architectures do this, so it’s just a matter of porting the mechanism to ARM.

Simply put, the idea consists in:

(1) Compile a devicetree source.
(2) Link the resulting devicetree blob against the kernel image.
(3) Modify the early ARM architecture setup code to use it.

Linking the devicetree

The first step is to compile a devicetree source into a devicetree blob. We want to have some flexibility and allow the user to select a devicetree source to be compiled and linked, so let’s add some user options. What follows is a little bit of (simplified) Makefile magic. I won’t explain it in detail, but just keep in mind that there are new user options, so this will need some Kconfig magic too.

Now that we have the devicetree blob, we want to put it in a section. The generic section in include/asm-generic/ contains a KERNEL_DTB section. Let’s see how it looks:

Great, this is just what we need. All we need to do is put a devicetree blob in a .dtb.init.rodata section. We will have a symbol (the __dtb_start variable) pointing at its starting address.

So, let’s create an assembler source code file and use the incbin [1] assembler directive to include the devicetree blob. We will add a new file called linked_dtb.S.

Using the linked devicetree

At this point, we have a devicetree blob inside our kernel image, and we can access it using __dtb_start. The last step is easier than expected; all that’s needed is to modify the code to call setup_machine_fdt on __dtb_start, like this:

As can be seen, this patch calls setup_machine_fdt(virt_to_phys(__dtb_start)), but it also calls setup_machine_tags(__atags_pointer). The latter is not really needed, because a devicetree fully describes the platform, and also carries the boot arguments. However, it’s useful to parse ARM ATAGs. ATAGs are used by the bootloader to pass boot arguments (among other information). If you are interested in the juicy details, ATAGs are described in detail on an article by Vincent Sanders [2].

Putting it all together

After all this hard work, let’s take a look at a full patch:


It’s important to note how easy it was to hack the inner guts of the ARM early setup code, modifying something such as the devicetree to use for booting. In fact, this is one of the most important reasons to use and support open source software such as Linux: it is always possible to customize a component to meet a specific requirement. Sometimes it is even easier than expected!

If you want more information about booting an uncompressed kernel image, there’s a good article by Free Electrons [3].

Happy hacking, and don’t hesitate to drop us a line if you have any questions about this procedure.



Leave a Comment