U-Boot bootloader with boot menu
History
During early startup, the RPi2 and 3 firmware expects (by default) a file kernel7.img
on the boot partition that should contain a valid kernel and boots it with the
arguments in cmdline.txt
. However, multiple kernels can be installed and the
user would have to manually update the file kernel7.img
depending on the desired
kernel. To provide a convenient environment, I tried to installed Grub on the
device which automatically boots the most recent kernel. Unfortunately, I was
not able to get it to work on the RPi2 although a version for ARM exists. Hence,
I built U-Boot for the RPi2 and wrote a small script update-uboot
similar to the
handy update-grub
that looks for available kernels and initrd images and creates
an U-Boot config that presents a boot menu at startup and boots the most recent
kernel after a timeout of 2 seconds.
While developing this menu, the RPi2 freezed randomly during U-Boot execution.
It turned out that the default scriptaddr variable caused the boot menu to be
loaded at address 0x0
but something else is using the memory in this area and
hence caused problems. After I changed the address to 0x00100000
the boot
process worked smoothly. With U-Boot 201601, this has been fixed upstream.
Configuration
The U-Boot package installs a script into /etc/kernel/postinst.d/
that calls
/usr/sbin/update-uboot
after a kernel package was installed. First,
update-uboot
loads the following configuration file:
/etc/uboot.cfg:
### configure on which devices the u-boot output is shown
### e.g., to enable HDMI output also for non-error messages, add ",lcd" below
# UBOOT_STDOUT=serial
### override device tree set by firmware
# UBOOT_DEVICE_TREE=
### default entry in the menu. Entries are enumerated as "bootX" where X is the
### X-th entry in the menu. Default is "boot0", i.e. the first entry
# UBOOT_DEFAULT=boot0
### timeout after which the default entry will be booted
# UBOOT_MENU_TIMEOUT=2
### default kernel arguments. If a file /boot/cmdline-${kernel_version} exists,
### its content overrides the default arguments
# UBOOT_KERNEL_ARGS="rootfstype=btrfs console=ttyAMA0,115200 console=tty1 selinux=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 kgdboc=ttyAMA0,115200 elevator=noop quiet splash logo.nologo"
### if set to 1, prepend the default kernel arguments with the kernel arguments
### set by the firmware
# UBOOT_PREPEND_FW_KERNEL_ARGS=1
### partition with the root filesystem (will be passed to the kernel)
### default is the second partition on the SD card
# UBOOT_ROOT_PART=/dev/mmcblk0p2
### include a custom configuration file, e.g., to generate custom bootloader
### entries. See uboot.user.cfg for an example. Make sure this file is executable
# UBOOT_USER_CFG=/etc/uboot.user.cfg
### if GPIO pin 21 is high, execute UBOOT_FAILSAFE_COMMAND. If this command does
### return, normal boot procedure continues.
# UBOOT_FAILSAFE_GPIO_PIN=21
### If set, this file should be removed by the booted operating system. If this
### file is still present during the next boot, u-boot will execute
### UBOOT_FAILSAFE_COMMAND. If UBOOT_FAILSAFE_COMMAND returns, normal boot
#### procedure continues.
# UBOOT_FAILSAFE_FILE=boot-failure-check
### run script failsafe.scr (see update-uboot how to generate such a script)
# UBOOT_FAILSAFE_COMMAND="fatload mmc 0:1 0x02050000 failsafe.scr; source 0x02050000"
### run the second kernel in the menu
# UBOOT_FAILSAFE_COMMAND="run boot1"
### look for and execute a boot script on ${target}, where target can be:
### dhcp, mmc0, pxe or usb0
# UBOOT_FAILSAFE_COMMAND="run bootcmd_${target}"
In case you are upgrading the uboot package, please add the following line to /boot/config.txt:
device_tree_address=0x00000100
This will instruct the RPi's firmware to load the device tree (you can think of this as a replacement of the BIOS on PCs) to a fixed address until uboot can determine the varying address used by recent firmwares automatically.
Handling faults
Starting with version 201601, the update-uboot script contains two mechanisms
to alter the boot process if the default process somehow fails. If activated,
the first mechanism checks a GPIO pin if it is high and if yes, the U-Boot
command in the UBOOT_FAILSAFE_COMMAND
variable is executed during boot
instead. The default config above contains three examples, the first loads and
executes a failsafe.scr script instead of the normal boot.scr from the SD card
(see update-uboot
how such a script is generated). The second command would
start the second boot entry and the third command would start the original
U-Boot boot process that looks for executable scripts on the given device/target.
If UBOOT_FAILSAFE_FILE
is set, U-Boot will write a file with the given name
to the boot partition. If the file does not already exist, normal boot procedure
continues. If the file exists, the chosen UBOOT_FAILSAFE_COMMAND
will be
executed instead. If you want to use this mechanism, make sure the operating
system will remove the file after a successfull boot. For example, you could put
rm /boot/boot-failure-check
into /etc/rc.local.
Boot logo
With version 201601, this U-Boot package also contains experimental splash image
support. Call /usr/bin/uboot-create-logo.sh
with a PNG or JPG image and the
script will create a splash.bmp
file in the current work directory. Copy this
file to /boot
and U-Boot will automatically load this image. Make sure you
have installed the netpbm
package before calling the logo script. Please note
that RPi2+3 support is not complete yet, e.g., drawing an image might take
around 1 second.
Booting with U-Boot
After the U-Boot initialization messages, you will see a menu like this on the serial console:
Available boot options:
-----------------------
"run boot0" will boot vmlinuz-4.1.16-rpi
"run boot1" will boot vmlinuz-4.1.15-rpi
writing boot-failure-check
1 bytes written
Will execute "run boot0" in 2 seconds, abort with CTRL+c...
By default, output to HDMI screen is disabled and only in case of an error a message is shown. This can be changed by setting UBOOT_STDOUT in /etc/uboot.cfg accordingly.
Please note, while this U-Boot version is compiled with USB keyboard support, it seems like such keyboards do not work yet. This might change with improving support of the RPi2. Hence, to manually alter the boot process, you will either need a serial connection or you have to use one of the failsafe mechanisms above, e.g., connect a switch or button to the GPIO pins.
Serial console with the RPi3
In the default configuration of the RPi3, the pins that were previously connected to the UART interface for the serial console are now connected to the Bluetooth interface. This change can be undone by loading a device tree overlay in the config.txt but this overlay is only applied by the Linux kernel currently. Hence, you can use the serial console with Linux but you cannot see the menu or control uboot over the UART interface yet until support for applying the overlay is added to uboot.
The system-on-chip of the RPi2 and 3 has two UART interfaces: a full-featured interface (PL011) and a limited interface (called mini UART). In the RPi2, the PL011 interface is connected to the UART GPIO pins 14 and 15. In the RPi3, the PL011 interface is connected to the Bluetooth device. In order to use the serial console on the RPi3, you have two choices. Either you disable the Bluetooth device and reroute the UART interface to GPIO pins 14 and 15, or you attach the Bluetooth module to the mini UART.
To apply one of the approaches, ensure you have installed at least rpi-firmware-20160309 installed (or download the overlay files from the official GitHub repository) and then add one of the following to /boot/config.txt:
dtoverlay=pi3-disable-bt
dtoverlay=pi3-miniuart-bt