Enabling Kernel Image Verification in U-Boot using HAB

Hello Toradex Team,

I’m in the process of enabling Secure Boot using HAB on a Colibri i.MX6ULL.

I have tested the HAB verification of U-Boot and kernel images using the U-Boot commands provided by the HAB library (hab_status and hab_auth_img), and it works properly.

Now that the development is over, I want to go ahead and ‘close’ the device so that only images (U-Boot and kernel) signed with proper signature will be allowed to run. The U-Boot is getting authenticated on startup as the ROM code does it since Secure Boot is enabled on the device. However, I also want the U-Boot to verify kernel image, and prevent booting (or halt) if the image could not be authenticated.

I’m aware that the HAB library provides API to do it, but I prefer a staright-forward U-Boot configuration that would do the job without code modification, if that’s the standard way.

If there is no such way, I would like to get details on how the API should be used to implement it and how people normally deal with kernel authentication in U-Boot. Please also provide references or examples if this is the case.

TDX Info:

Software summary
------------------------------------------------------------
Bootloader:               U-Boot
Kernel version:           6.1.55-00020-g6db5276489af #1 SMP Sat Sep 23 09:11:13 UTC 2023
Distro name:              NAME="Test Base (base distro for Test)"
Distro version:           VERSION_ID=6.4.0-devel-20240919081949-build.0
Hostname:                 -
------------------------------------------------------------

Hardware info
------------------------------------------------------------
HW model:                 Customized Test DTB to run on Colibri Evaluation Board V3 v1.1
Toradex version:          0044 V1.1A
Serial number:            15102979
Processor arch:           armv7l
------------------------------------------------------------

Here are some additional info that might be useful:

  • Module: 0044 Colibri iMX6ULL 512MB IT V1.1A
  • U-Boot version: U-Boot 2022.07-6.4.0-devel+git.e092e3250270 (Jul 11 2022 - 13:42:58 +0000)
  • Kernel image type: zImage
  • Build system: Yocto 4 (Kirkstone)

Please share your advices and suggestions as quickly as possible.

Thank you
Ajith

Hi, I’m the Toradex AI Assistant.

I might make mistakes. Our support team will step in if needed. If I answered your question, please tick the “Solution” checkbox. For follow-up questions to me, please use @ToradexAI in your post.

Did you like my answer? :+1: :-1:


Hello Ajith,

Thank you for your detailed description and for providing the necessary hardware and software details. Let me address your queries regarding kernel (zImage) authentication with HAB in U-Boot on the Colibri iMX6ULL.

U-Boot Kernel Authentication: Configuration vs Code Modification

1. U-Boot Environment-Only Approach

There is no direct, out-of-the-box U-Boot configuration or environment variable that will automatically verify the kernel image before booting for the i.MX6ULL. However, you can leverage U-Boot commands—including hab_auth_img—directly in your bootcmd environment variable. This allows enforcing authentication without patching U-Boot source code.

General procedure:

  • Load the kernel into RAM as usual.
  • Authenticate the loaded image with hab_auth_img.
  • Only proceed to boot the kernel if authentication is successful; otherwise, halt.

Example U-Boot commands sequence:

setenv load_kernel 'load mmc 0:1 ${loadaddr} zImage'
setenv kernel_size <your_kernel_size>      # replace with actual size of signed image (dec or hex)
setenv kernel_ivt_offset <ivt_offset>      # replace with actual IVT offset (hex)
setenv auth_kernel 'hab_auth_img ${loadaddr} ${kernel_size} ${kernel_ivt_offset}'

setenv bootcmd 'run load_kernel && run auth_kernel && bootz ${loadaddr} - ${fdt_addr_r}; echo "Kernel authentication failed!"; while true; do; done'
  • Replace <your_kernel_size> and <ivt_offset> with your actual values. The IVT offset and size are critical and must match your signed image layout.

This sequence will:

  • Load the kernel.
  • Authenticate kernel using HAB.
  • Boot if authentication passes.
  • Output an error and halt in a loop if authentication fails.

You can adjust the error handling part (while true; do; done) as needed.

References:
U-Boot environment variables

2. Using the HAB API in U-Boot Source

If you require more advanced handling (different behaviors, UI, etc.), you’ll need to modify U-Boot source code to call the HAB API functions for image authentication during your board’s boot sequence. This is more complex and is typically only necessary if environment scripting is insufficient.

Usual pattern in code:

  • Load kernel image into RAM.
  • Call the HAB authenticate function with image location, size, and IVT offset.
  • Check return value; decide whether to halt or boot.

Most use cases are well served by the boot-script/environment method, as shown above.

3. Field Proven Example

You can see a practical implementation for another Toradex i.MX8MM module involving kernel authentication here:
Secure Boot on iMX8MM Example

Adjust the memory addresses and image parameters for the i.MX6ULL as appropriate for your layout.

4. More Information


Summary

  • No code change is strictly necessary: You can implement kernel verification in bootcmd via hab_auth_img.
  • Customize your bootcmd to load, authenticate, and only boot a valid kernel image. Halt or error otherwise.
  • Ensure you have correct addresses, offsets, and use a signed kernel image (with appended IVT, CSF, etc.).

Let me know if you require a sample with your specific partition layout or if you need help calculating IVT offsets/sizes.

Best regards,
Toradex AI Assistant.

Thank you for the response.

The hab_auth_img requires two parameters (apart from load address) that can vary based on the actual image:

  1. Image size
  2. Offset to IVT

Setting these in the environment (maybe for each image build) is not a viable option. Does the HAB library provide a way to handle this?

Note: I would like to have the support team in for the remaining discussion.

Thank you
Ajith

Image size of last file loaded you may find in filesize variable.

Offset to IVT is more tricky. I don’t know if NXP explained it clearly in some AN, but I saw somewhere that they pad image size to nearest 4k boundary, put at the end IVT, and then sign resulting image. Since CSF binary is pretty always shorter than 4k boundary, you just calculate the top most 4k boundary offset in the image to get IVT offset back.

Edit: oops, correction. CSF binary is more than 4k, but less than 8k. So, provided your image size is padded at the end to nearest 4k, IVT is attached further, CSF binary attecjed next, your IVT offset may be calculated from filesize like this
(filesize + 4095) & ~4095 - 8192

HAB encrypted binaries (which is not available on iMX6ULL), need further attach DEK blob. This kind of requires further padding signed image to next 4k. To make IVT offset calculation compatible encrypted vs unencrypted, size of unencrypted signed images should be 4k aligned, size of encrypted image should be not 4k aligned. And IVT offset formula for both is
filesize & ~4095 - 8192

for example
image size prior to signing 0x78755
pad image with zeros to image size 0x79000 (IVT offset 0x79000)
image size with IVT 0x79020
sign image, add csf at the end, image size 0x7ADCF
pad resulting image with zeros to next 4k, size: 0x7B000

for encrypted image add DEK blob and get size something like 0x7B020

in both cases, encrypted/unencrypted, IVT offset is at filesize & ~4095 - 8192 = 0x79000

Hi Edward,

Thank you for the reply.

That’s how I’m currently doing the IVT offset calculation. In my case, CSF binary is less than 4K, so I could manage with that. However, giving 8K (for CSF + padding) is better considering any further additions to the CSF commands.