U-Boot¶
Important
Make sure you have read Adding new device types first.
U-Boot is a very common sight on embedded devices. It is a very capable piece of Open Source software that supports a diverse range of devices out of the box, and it is easily configurable and modifiable to add support for new devices too. Comprehensive documentation is available on the U-Boot website.
If your new device includes U-Boot then this can be a useful beginning. However, the build of U-Boot on the device may potentially hinder integration due to the wide range of configuration options and behavioral changes available inside a patched U-Boot build.
See also
Generally, the more components of U-Boot that are disabled or removed from a vendor build, the harder it will be to integrate. If you are able to fully script a U-Boot process from interrupting the bootloader to booting a kernel of your own choice, this will greatly assist in integrating the device into LAVA.
To start the device integration of a device using U-Boot, you must at least have a complete list of all the commands which are necessary to interrupt U-Boot and manually boot the device.
Common U-Boot support¶
The following elements are common to all U-Boot integration projects. Some information may need to be obtained from the default vendor configuration as shipped on the device. interrupt U-Boot and take a copy of all the settings before you start integration work. Some elements need to be considered during the development or updating of the build of U-Boot on the device itself. Sometimes there might be limitations in the hardware itself. Most devices which can use mainline U-Boot can be integrated into LAVA. Where mainline support is available, it is recommended to replace the vendor-supplied version of U-Boot with a mainline build.
Make sure that the U-Boot environment can be modified and that changes are persistent across reboots.
Read and understand how
base-uboot.jinja2
operates to follow how the various values will be used to create the U-Boot boot commands. Not all of the logic in thebase-uboot.jinja2
template is covered in this section.Create a unit test and ensure that the final rendered device configuration matches the needs of the device.
See also
Developing using device-type templates, Jinja2 support and Testing new device-type templates
Configuration¶
Ensure that the U-Boot build supports a string which can be used to interrupt
U-Boot and that once interrupted, the prompt is set to a usable string like
=>
or uboot#
etc. Make sure that the configuration supports TFTP using
commands sent over the serial port. The timeout for interrupting the boot
process must be configurable. Typically, the timeout should be at least 5
seconds in the default configuration. There is no need to configure a working
automatic boot once the timeout expires. Whilst having a default boot is useful
during development, it can be simpler in automation to simply drop to the
bootloader prompt if the bootloader could not be interrupted successfully
within the timeout. This avoids wasting time watching the device boot into a
system which is not the system intended by the test writer. (The default boot
would likely fail to support the correct auto-login
or, worse, could allow
login and then fail to run any tests as the overlay will not have been deployed
to that system. Either option is confusing when trying to debug why the test
job failed as the actual error is close to the top of the LAVA test logs, not
at the end after all of the boot messages have been sent.)
Prompts¶
Two prompts are required in the device configuration:
interrupt_prompt: {{ interrupt_prompt|default('Hit any key to stop autoboot') }}
bootloader_prompt: {{ bootloader_prompt|default('=>') }}
The interrupt_prompt
is seen first as the device boots and then waits for
the timeout before attempting to boot automatically.
The bootloader_prompt
is seen after the bootloader has been interrupted and
after each command is sent to the bootloader.
Many U-Boot configurations use the same prompt strings as the defaults
in base-uboot.jinja2
, as shown above.
Interrupting U-Boot¶
The default behavior when interrupting U-Boot is to send a single newline character. This behavior is controlled with the following variables:
interrupt_char: "{{ interrupt_char | default('') }}"
interrupt-newline: {{ uboot_interrupt_newline | default(True) }}
If U-Boot requires a special character, set interrupt_char
accordingly. For
example, set to SPACE by setting the following in the device template.
{% set uboot_interrupt_character = ' ' %}
If interrupt_char
is used, LAVA will still send it followed by a newline.
To prevent the newline from being sent, disable uboot_interrupt_newline
.
{% set uboot_interrupt_newline = False %}
Troubleshooting Interrupting U-Boot¶
An extra newline during U-Boot interruption can cause LAVA to send U-Boot
commands before the previous command completes. The error message *** ERROR:
`serverip' not set
may be seen, due to the delay of the dhcp
command,
which preceded the setenv serverip
command, causing the latter to be sent
too soon. If U-Boot interrupt does not need a newline to be sent, set
uboot_interrupt_newline to False in the device template.
Load addresses¶
U-Boot typically requires the load addresses to be specified in the commands
used to load and execute the downloaded kernel, ramdisk and DTB. The
initial load addresses can be obtained from the device in uEnv.txt
or in
the saved environment of the default U-Boot configuration (via printenv
).
The load addresses may need changes later to support larger ramdisks or
kernels. Some U-Boot devices use different load addresses according to the
kernel to be booted, so each address can be specified separately or mapped to
an existing value.
{% set bootm_kernel_addr = '0x40007000' %}
{% set bootm_ramdisk_addr = '0x45000000' %}
{% set bootm_dtb_addr = '0x41f00000' %}
{% set bootz_kernel_addr = bootm_kernel_addr %}
{% set bootz_ramdisk_addr = bootm_ramdisk_addr %}
{% set bootz_dtb_addr = bootm_dtb_addr %}
Required configuration¶
At a minimum, any new U-Boot device requires the following pieces of configuration:
console device - There seems to be no standard or default here, so every request needs to specify the argument to pass to
console=
on the kernel command line, including baud rate.load addresses - Kernel, ramdisk and DTB load addresses.
mkimage arch - the architecture value to pass to mkimage when preparing modified uImage or uboot headers.
MAC address - if the MAC address is not pre-configured as a guaranteed unique address, a way of setting a fixed and unique MAC address must be provided.
boot methods -
booti
,bootz
andbootm
- which ones are supported on this device?prompts - What is the configured U-Boot prompt on the required build of U-Boot for the device. Has the autoboot prompt been modified and if so, what is the autoboot prompt?
Booting the kernel¶
When this goes wrong, the infamous Bad Linux magic
error can be seen.
Retrieve the available boot methods from the existing U-Boot configuration,
typically one or more of bootz
, booti
or bootm
.
If booti_kernel_addr
is set, image
parameters will be set for the
ramdisk and the DTB.
If bootm_kernel_addr
is set, uimage
parameters will be set for the
ramdisk and the DTB.
If bootz_kernel_addr
is set, zimage
parameters will be set for the
ramdisk and the DTB.
U-Boot bootargs¶
U-Boot uses the bootargs
(“boot arguments”) variable to specify the command
line when booting a Linux kernel. This can be critical in determining whether a
device boots at all or whether particular hardware is available in the booted
system. Equally, some bootargs settings can be entirely cosmetic and simply add
(or silence) messages during the boot process. Experiment with your board to
work out which bootargs are mandatory for all boots, which are useful as
defaults but which can be omitted for some test jobs and which are entirely
optional.
Mandatory bootargs need to be put into the template as hard-coded
strings. Useful bootargs can be set as the default value of
{{base_kernel_args}}
. Optional bootargs can be left as comments
for test writers to supply via the job context and then added
to the bootargs using {{extra_kernel_args}}
.
See also
Using mkimage¶
U-Boot typically requires use of the mkimage
binary in various ways. Most
commonly, a test job which only boots a ramdisk needs to have the LAVA overlay
added to the ramdisk.
Many devices require a U-Boot header on the ramdisk. The device configuration
controls how to add a new U-Boot header when LAVA needs to modify the
downloaded ramdisk (to add modules or a test shell overlay). The device
configuration deploy parameters use the default add-header: u-boot
setting
from base-uboot.jinja2.
mkimage
creates a different header for arm
than for arm64
. The
uboot_mkimage_arch
value will need to be set according to the requirements
of the device.
Note
Most ARMv7 devices will use arm
as the architecture and most
ARMv8 devices will use arm64
, but this is not always the case. For
example, the APM Mustang is an arm64 device but the U-Boot build on the
Mustang pre-dates arm64 support in mainline U-Boot. It uses {% set
uboot_mkimage_arch = 'arm' %}
Vendor builds¶
Not all devices have mainline U-Boot support and the configurability of the U-Boot source code means that some vendor-supplied builds of U-Boot may behave very differently to those found on other U-Boot devices. Do not assume that options and commands in existing U-Boot devices will always have any equivalent in a vendor build of U-Boot.
Network support¶
Network support in U-Boot is essential for any useful automation.
Specifically, TFTP
support in U-Boot needs to work to use any of the
existing U-Boot support in LAVA V2.
Additional U-Boot support¶
Some developers integrating new U-Boot devices may need to consider more elements of U-Boot behavior and configuration.
Filesystem support¶
Filesystem support in U-Boot is optional, but will be required for
secondary media. Check if U-Boot on the device
supports the filesystems you want to use, fat or ext2|3|4. Check if your U-Boot
has limits on the sizes of the filesystems it supports. In some cases, it may
be necessary to use a separate small /boot
partition to make things work.
Interface names¶
Some configurations of U-Boot may change how interfaces like SATA are accessed
by U-Boot. For secondary media support or to read files from an attached
storage device, you will need to find out how the U-Boot describes that storage
interface (e.g. sata
, scsi
, usb
, mmc
).
Initializing subsystems¶
Some U-Boot devices will not enable some of the onboard storage or peripheral
devices without explicitly initializing them first. Some may need other
subsystems to be initialized first - for example the Panda needs usb start
before networking will work, as the onboard network interface is attached via
USB.
Appending the DTB¶
Some U-Boot configurations support loading a DTB for the device separately, but
not all. If your U-Boot does not support this, you will need to append the DTB
to the kernel instead. This will obviously affect the commands used to boot
your device (e.g. tftp
, loadm
or bootm
), but also remember that you
will need to generate this combined image file ready for use on the device.