U-Boot and booting Linux
Introduction
The goal of this laboratory is to configure and harden U-Boot, use it to boot the Linux kernel and modify Buildroot to generate a different image.
For this lab, you will continue working on your current buildroot2 file tree.
As a reminder, please create a report in the form of a markdown file describing the explanations, the manipulations carried out, and the answers to the questions. It is essential that your report contains enough explanations so that we can reproduce exactly what you did and the results you obtained. If you have config files, source files, etc., put them on your git and reference them in your report.
Question 1: customizing U-Boot with a fragment file
a) First, start by modifying Buildroot’s U-Boot configuration so that U-Boot uses the following configuration fragment file (which doesn’t exist yet): board/friendlyarm/nanopi-neo-plus2/uboot-extras.config.
b) Second, in order to have a bit more time to enter U-Boot after powering-up your board, change U-Boot delay before automatic booting from 2 seconds to 10.
c) Third, modify U-Boot configuration to change the default prompt from:
=>
NanoPi #
d) Finally, we would like these two changes to be in the U-Boot fragment file specified above so that it could be applied to a fresh Buildroot install. Make sure that the configuration in the fragment file overrides U-Boot’s configuration displayed by menuconfig. You can check this by having different values between menuconfig and the fragment file. Verify that U-Boot’s configuration file created in output/build/uboot-xx/.config has the values present in your fragment file.
Generate a SD card image with these changes and test it on your board to make sure you obtain the desired behavior.
Question 2: FIT image for kernel and FDT - part 1
In your current Buildroot configuration, the boot.scr script loads and starts the Linux kernel and FDT separately:
setenv bootargs console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rootwait
fatload mmc 0 $kernel_addr_r Image
fatload mmc 0 $fdt_addr_r sun50i-h5-nanopi-neo-plus2.dtb
booti $kernel_addr_r - $fdt_addr_r
Furthermore:
=> echo $kernel_addr_r
0x40080000
=> echo $fdt_addr_r
0x4FA00000
a) In output/images/, create the Image Tree Source kernel_fdt.its file which must include the Linux kernel (Image) and your board’s FDT (sun50i-h5-nanopi-neo-plus2.dtb).
b) In the same directory, manually create the Image Tree Blob kernel_fdt.itb from the Image Tree Source. We want to take advantage of the FIT image format, so make sure to add SHA256 checksums to both entries, the kernel and the FDT.
c) Now that you have an Image Tree Blob, you must add it to your SD card image. Modify genimage.cfg in order to add the FIT image you just created to the SD card image. Make sure the kernel and FDT are not present on the VFAT filesystem as they’re not needed anymore. Once done, generate a new SD card image.
d) Boot on your new SD card image and get to the U-Boot prompt. Check that the VFAT filesystem only contains kernel_fdt.itb and boot.scr. Load the FIT image at address 0x50000000 in DRAM and use iminfo to inspect its content. Make sure you see both entries: the kernel and the FDT. On your host system, compute the SHA256 checksums for kernel and FDT and check they actually match the ones displayed by iminfo.
e) Proceed with booting the Linux kernel… Unfortunately, it should fail with the following error:
Image too large: increase CONFIG_SYS_BOOTM_LEN
Question 3: FIT image for kernel and FDT - part 2
a) Inspect the sunxi-common.h file as it should give you some clue regarding the previous error. What’s the current bootm size limit for your board, in MB?
b) Modify U-Boot’s code by hand in output/build/... by increasing the current limit so that bootm can properly boot your kernel. What’s the new limit?
c) Recompile U-Boot, deploy a new SD card image and check that you are now able to successfully boot the kernel with bootm.
d) In order for this U-Boot modification to be persistent, create a patch for it. Then, rebuild U-Boot from scratch (including fetching and patching) to validate the patch you just created was applied to U-Boot’ sources.
Question 4: filesystem type change
We are now interested to change the type of filesystem present in the SD card’s first partition. Currently, it’s a VFAT filesystem, but we would like it to be an ext4 filesystem instead.
To accomplish this, the following steps need to be performed:
a) U-Boot’s configuration must be changed to not save the environment as U-Boot doesn’t support saving the environment to an ext4 filesystem. Modify the existing U-Boot fragment file uboot-extras.config to store these new changes.
b) The boot.cmd script is reading from a VFAT filesystem. Change its code so that it reads from an ext4 filesystem instead.
c) Remember that Buildroot executes post-build.sh (for your board) before creating the filesystem images and genimage.sh (which runs genimage using genimage.cfg configuration) after creating the filesystem images in order to create the final SD card image. Beware that genimage cannot populate an ext4 filesystem as it can with a VFAT filesystem (the files attribute present for a VFAT filesystem in genimage.cfg doesn’t exist for ext4). You can find genimage’s documentation here. The consequence of this is that you must manually create an ext4 filesystem image.
Consequently, update genimage.cfg to avoid creating a VFAT filesystem and configure it to deploy an ext4 filesystem in the boot partition. Then, update post-build.sh to generate boot.scr and the FIT image corresponding to Question 2b, and to create the ext4 filesystem image mentioned previously. The ext4 filesystem should have the same size as the previous VFAT one. Although the filesystem in the first partition will be ext4 instead of VFAT, the partition type can remain the same (FAT32). The method you must use to create the ext4 image is as follow:
- Create an empty image file (on a Linux system, this is equivalent to an empty physical device)
- Create a filesystem on that empty image
- Mount the filesystem
- Populate the filesystem with the desired file tree
- Unmount the filesystem
Make sure that post-build.sh is independent of buildroot’s root directory in the filesystem (for instance, nothing should be hardcoded to read /workspace/buildroot2/...).
The following tips might be useful too: /dev/zero is an infinite source of zeroes, an empty image file can be created with dd, and an ext4 filesystem can be created with mkfs.ext4.
d) As usual, restart a build of the SD card image, but make sure to delete the relevant images files before hand to ensure that a new SD card image will be generated from scratch and that your changes are applied. If everything works as expected, the Linux kernel should boot automatically after 10 seconds.
Question 5: hardening U-Boot
a) Locate the directory containing the binaries of Buildroot’s cross-compilation toolchain for your target (hint: host). Next, write a small C program that smashes the stack. Cross compile it with the flag that always emits code for stack overflow checks (canary). Verify that the generated executable matches your target architecture.
b) Dissassemble the executable and study its assembly code. Can you determine whether the compiler added the canary code (i.e. stack overflow checks) to your executable?
c) Before starting this part, note the exact size of your current u-boot.bin. What’s its size in bytes?
Now, we would like to harden our U-Boot by adding some stack smashing protection code with -fstack-protector-strong. Unfortunately, enabling “Stack Smashing Protection” in Buildroot’s configuration doesn’t apply to U-Boot. We must therefore edit U-boot’s compilation options directly in order to emits code for stack overflow checks. However, solely enabling compilation flags to generate stack protection checks isn’t enough as the linker complains about __stack_chk_guard and __stack_chk_fail symbols not being defined. This is because U-Boot is compiled with baremetal options (no OS support) and thus the required supporting code is missing. Below are the necessary steps to solve this problem, but keep in mind that later, you’ll have to create a patch with these changes:
- Modify U-Boot’s root
Makefileto add stack protection checks with the-fstack-protector-strongoption (hint: search for-fno-stack-protector) - Add a new source file
common/stackprot.cwhich must contain the implementation of the__stack_chk_failfunction and the__stack_chk_guardglobal variable (hint: get some inspiration from U-Boot 2023.07 source code here) - Modify U-Boot
common/Makefileso thatstackprot.ois added to the list of object files to be linked (hint: investigate theobj-yvariable)
d) Rebuild U-Boot and confirm that both:
- the C compiler was actually called with the right option to add the canary code
- the U-Boot binary actually contains code for stack overflow checks
What’s the size in bytes of this new u-boot.bin? How much bigger is it, in %, compared to the previous version without stack overflow checks?
e) Create a new U-Boot patch that adds stack smashing protection to most function. Then, rebuild U-Boot by making sure to apply the patch and generate a new SD card image for your target. Finally, check that your target system still works as expected with this new SD card.
Question 6: Buildroot integration
Update your configuration so that generating a new SD image from scratch leads to the exact same behavior as what you obtained by the end of Question 5. In other words, your configuration should be robust enough so that running the following commands will produce the same sdcard.img file as before:
cd /workspace/buildroot2
make clean
make -j4
After flashing a new SD card with the image above, and inserting it into your target, make sure it behaves as expected and boots sucessfully after 10 seconds.