wiki:uboot

U-Boot Bootloader

Gateworks uses U-Boot (the Universal Boot Loader) as a primary bootloader for all current product families. In some cases U-Boot is used as the 'secondary program loader' as well as the 'primary program loader'.

The purpose of a bootloader is to load the Operating System (OS) and execute it. In terms of a Linux based OS this means loading the kernel image, device-tree blobs (optional), and initial ramdisk images (optional) into DRAM and executing the kernel with any necessary kernel arguments. Often this is done in an OS specific fashion via a U-Boot bootscript stored along-side the kernel Image.

Features and Peripheral support

U-Boot has many features and peripheral support but it is not intended to be a full OS so some features do not work as well as they would on a Linux based OS for example. The intention of U-Boot is to provide enough support to load and execute the kernel therefore support for things like USB peripherals and high performance networking and I/O is not a priority and should not be expected.

Many features can be enabled by re-building and re-installing U-Boot. For more information on building U-Boot see the product specific BSP pages.

Product Family specific notes

Malibu

The Marvell CN913x based Malibu product family boots via a BOOT ROM internal to the CN913x which loads ARM Trusted Firmware (ATF) which contains a U-Boot payload image. In this case initial SoC chip and DRAM configuration is done by components in the ARM Trusted firmware

see also malibu/boot

Venice

The NXP i.MX 8M Mini/Nano/Plus based Venice product family boots via a BOOT ROM internal to the i.MX8M which loads U-Boot SPL (Secondary Program Loader) which then loads U-Boot proper. In this case initial SoC chip and DRAM configuration is done by U-Boot SPL.

see also venice/boot

Newport

The Marvell CN803x based Newport product family boots via a BOOT ROM internal to the CN803 which loads Marvell's 'Board Disagnostics Kit' (BDK) (which we call the SPL) which in turn loads the ATF and U-Boot from a payload inside the ATF. In this case initial SoC chip and DRAM configuration is done by the Marvell BDK software.

see also newport/boot

Ventana

The NXP i.MX 6Q/DL based Ventana product family boots via a BOOT ROM internal to the i.MX6 which loads U-Boot SPL (Secondary Program Loader) which then loads U-Boot proper. In this case initial SoC chip and DRAM configuration is done by U-Boot SPL.

Ventana U-Boot enables and supports PCI and the PCI based i210 NIC found on several of the Ventana boards. see also ventana/bootloader

Environment variables and scripts

U-Boot uses environment variables to configure much of its functionality. U-Boot environment variables can also be treated as scripts and therefore the environment dictates how U-Boot achieves its goal of loading and executing the OS.

You can use env commands to get or set env vars. For example:

  • Get a var:
    env print model
    
  • Set a var:
    env set quiet 1
    
  • Load default values (does not save):
    env default -f -a
    
  • Save current env:
    env save
    

If you want to erase the env:

env erase

Some notable env variables used in U-Boot:

Category Name Purpose
boot preboot script executed before 'Hit any key to abort' if CONFIG_USE_PREBOOT=y
boot bootcmd script executed at boot
boot bootdelay number of seconds to wait for a key to abort automatic boot (if 0 will not wait)
console baudrate baudrate of serial console
console device of serial console
network ethprime primary ethernet device (used at boot)
network ethaddr mac addr of first eth interface
network eth1addr mac addr of second eth interface
network ethact active ethernet device (current)
network ipaddr local ip address
network serverip ip address of server for tftp commands
network netmask netmask address (optional)
network gatewayip ip address of gateway (optional)
network ethrotate if set to 'no' will not automatically rotate through network devices on network operations
network dhcp issue a DHCP request for network configuration (set 'autoload=no' to disable attempting to load an image)
distro config boot_targets ordered list of boot targets (ie 'mmc1 mmc2 usb0 usb1')
distro config boot_prefixes ordered list of prefixes of boot scripts (ie '/ /boot')
distro config boot_scripts ordered list of boot script names (ie 'boot.scr')
automatic board board model (automatically set during boot)
automatic serial# board serial number (automatically set during boot)

U-Boot env location, format, and accessing outside of U-Boot

There are several U-Boot config items (CONFIG_ENV_*) that specify where and how the U-Boot env is stored. Typically Gateworks configures 32KiB redundant env located at the end of the boot firmware. This means that U-Boot flips back and forth between the two env areas so that if a power-cut occurs during a 'saveenv' command it will default to the previous env saved.

There are several applications built by U-Boot that facilitate accessing U-Boot env outside of U-Boot:

  • fw_printenv / fw_setenv - uses a config file to allow env get/set from Linux
  • mkenvimage - creates a binary image from a source file in the format of name=value

The Ubuntu u-boot-tools package provides fw_printenv and fw_setenv but a config file must exist in /etc/fw_env.config (or specified via the -c parameter) which describe the env location and size:

  • Venice: (U-Boot env is at the top of 4MB on emmc boot0)
    # Device               offset          Env. size
    /dev/mmcblk2boot0      0x3f0000        0x8000
    /dev/mmcblk2boot0      0x3f8000        0x8000
    
    • Note that at one point Venice stored its boot firmware on the emmc user hardware partition and its env at the top of 16M on emmc user). If you are using v2023.04 or above its at the device and offset above
  • Malibu: (U-Boot env is at the top of 4MB on emmc boot0)
    # Device               offset          Env. size
    /dev/mmcblk0boot0      0x3f0000        0x8000
    /dev/mmcblk0boot0      0x3f8000        0x8000
    
    • Note that at one point in time Venice stored its boot firmware on the emmc user hardware partition and its env at the top of 16M on emmc user).
  • Newport:
    # Device               offset          Env. size
    /dev/mmcblk0           0xff0000        0x8000
    /dev/mmcblk0           0xff8000        0x8000
    
  • Ventana (NAND boot device):
    • cat /proc/mtd
      dev:    size   erasesize  name
      mtd0: 01000000 00040000 "uboot"
      mtd1: 00100000 00040000 "env"
      mtd2: 7ef00000 00040000 "rootfs"
      
    • cat /etc/fw_env.config
      # device  offset size erasesize
      /dev/mtd1 0x0 0x20000 0x40000
      /dev/mtd1 0x80000 0x20000 0x40000
      
    • make sure /dev/mtd1 is your env partition
  • Ventana (MMC boot device)
    # device  offset size erasesize
    /dev/mmcblk0 0xb1400 0x20000 0x20000
    /dev/mmcblk0 0xd1400 0x20000 0x20000
    

This will define the device being used, offset according to flash layout, size from flash map, and size of erase block (unless the device is a file). Ventana U-boot environment is redundant hence two entries, both locations need their variables updated.

Note that in certain circumstances the Linux representation of the boot device can change - please ensure that the devices specified do indeed point to your boot device.

It is important to understand the meaning of the Warning: Bad CRC, using default environment message. This means that the non-volatile env area specified by the config file is empty or corrupt and that the built-in env within U-Boot will be used which is not appropriate. Note that Gateworks Ventana boards ship with an empty env area and use built-in defaults so if you wish to use the u-boot-tools to access the env from Linux first do a 'saveenv' from U-Boot to provide a valid default env.

If you use fw_setenv on an environment in this state it will properly set the variable you specify and all other variables will continue to use their built-in default values within U-Boot. Essentially you are 'overriding' the defaults as you would expect.

Example usage:

# display environment 
fw_printenv -c fw_env.config 
# set environment variable 
fw_setenv  -c fw_env.config foo bar #foo being variable name bar being value variable will be set to
#second example 
fw_setenv  -c fw_env.config ipaddr 192.168.1.10
#print single environment variable
fw_printenv  -c fw_env.config foo
#second example
fw_printenv ipaddr

Boot Flow

Boot flow in U-Boot refers to how specifically U-Boot loads and passed control to the OS.

After U-Boot initializes it does the following

  • executes contents of 'preboot' env variable (if configured with CONFIG_USE_PREBOOT)
  • based on the contents of 'bootdelay' env variable it prints 'Hit any key to stop autoboot:' and counts down the number of seconds specified (if set to 0 it will skip this and not allow you to stop execution manually)
  • executes contents of 'bootcmd' env variable

Distro Config

A set of scripts referred to as 'distro config' can be used to scan bootable partitions on a set of specified devices for boot scripts and execute the first one it finds.

This configuration is used on Newport, Venice, and Malibu product families (not Ventana which pre-dated this concept)

This feature provides a well defined U-Boot env intended to make it easier for distro maintainers to develop compatible bootscripts. This primarily entails a set of 'boot scripts' and variables that control them.

Ultimately this U-Boot environment is looking for a U-Boot boot script on a 'bootable' partition (partitions with the 'boot' flag enabled). It searches in this order with these rules:

  • boot_targets - list of target device type/nums to search: defaults to mmc1 mmc0 usb0 usb1 pxe dhcp
  • devplist - dynamically created list of all partitions flagged as 'bootable'
  • boot_prefixes - list of directories within a partition searched for bootscripts
  • boot_scripts - list of boot script names searched for

The boot device order is specified by the boot_targets env variable which typically defaults to something like mmc1 mmc0 usb0 usb1 pxe dhcp. For example, to limit OS booting to only mmc device 2 you would 'setenv boot_targets mmc 2; saveenv'.

The Distro-Config environment supports legacy uImage scripts (it does not support FIT images with scripts).

Boot script

A boot script is a U-Boot specific set of commands wrapped inside a U-Boot binary header which can be loaded from a device or filesystem and executed via the 'source <address>' command.

You can create these with the mkimage tool from U-Boot as such:

mkimage -A arm64 -T script -C none -d boot.txt boot.scr

The bootscript can be updated at runtime on the Linux target. For example:

mkimage -A arm64 -T script -C none -d boot.txt /boot/boot.scr

When writing bootscripts compatible with Generic Distro Config you can assume the following env variables:

  • devtype - the device type the script was loaded from (mmc|usb|sata)
  • devnum - the device number the script was loaded from (ie 0 for mmc0, 1 for mmc1, etc)
  • distro_bootpart - the partition number the script was loaded from (ie 0, 1, etc)
  • fdtcontroladdr - the address the device-tree is at (Note that the Malibu bootloader contains a static version of the board device-tree)
  • kernel_addr_r - address where kernel can be loaded
  • bootargs - default bootargs to pass to the kernel - you probably want to add to this and not overwrite it
  • console - the serial console device to pass to the kernel

Additionally you should note the following:

  • use load/ls/save commands which support FAT/ext filesystem types automatically instead of the fs specific commands
  • if using a root filesystem that is not supported by the bootloader (ie F2FS or BTRFS) you can place your bootscript and kernel image in the FAT12 filesystem on partition 1 of the boot device. This filesystem is part of the 16MB 'Boot Firmware' image. If doing so you will need to compress the kernel and package it into a FIT image in order to fit it in the available space.

Flattened Image Tree (FIT) images

The U-Boot bootloader supports Flattened Image Tree (FIT) images which expand greatly on the legacy U-Boot image (uImage) format by allowing multiple binary blobs within an image. These blobs can be kernel images, ramdisk images, device-tree blobs, and bootloader scripts. Each image can also be optionally compressed (meaning U-Boot will decompress it) and check-sumed with a variety of hash mechanisms (meaning U-Boot will verify the image before using it).

Quick summary of FIT Images:

  • introduced to resolve limitations with original single-image formats and follow-on multi-image format supported by UBoot bootm (boot memory)
  • uses power of the Device-Tree-Compiler (DTC)
  • FIT .itb files can be created with mkimage by passing in a .its file which in device-tree notation describes the images
  • U-Boot supports FIT with several commands:
    • source <addr>:<name> # source a script by name from FIT image in memory
    • iminfo <fitaddress> # print all the info contained in a FIT image in memory and verify (just not boot it)
    • imextract <fitaddress> <item> <addr> # extract item (ie kernel@1) to addr
    • bootm <fitaddress>[#conf] - $fdtcontroladdr # boot default or 'conf' configuration (ie #config@1)
    • bootm start <fitaddress>[#conf] - $fdtcontroladdr # boot from memory a specific configuration (or default configuration) from FIT image

Example:

  • kernel.its with a single compressed kernel for ARM64
    /dts-v1/;
    / {
            description = "Simple image with single Linux kernel";
            #address-cells = <1>;
            images {
                    kernel@1 {
                            description = "Linux kernel";
                            data = /incbin/("./Image.gz");
                            type = "kernel";
                            arch = "arm64";
                            os = "linux";
                            compression = "gzip";
                            load = <0x40200000>;
                            entry = <0x40200000>;
                            hash@1 {
                                    algo = "sha256";
                            };
                    };
            };
    
            configurations {
                    default = "conf@1";
                    conf@1 {
                            description = "Boot Linux kernel";
                            kernel = "kernel@1";
                    };
            };
    };
    
  • create image:
    cp arch/arm64/boot/Image .
    gzip Image
    mkimage -f kernel.its /tftpboot/kernel.itb
    
  • boot the default configuration from U-Boot:
    tftpboot $loadaddr kernel.itb && bootm $loadaddr - $fdtcontroladdr
    

References:

General Purpose I/O (GPIO)

The gpio command provides the ability to list and configure board-specific GPIO's.

Examples:

  • List GPIO's:
    u-boot=> gpio status
    Bank GPIO1_:
    GPIO1_0: output: 0 [x] rs485_term.gpio-hog
    GPIO1_1: input: 0 [x] mipi_gpio4.gpio-hog
    GPIO1_5: output: 0 [x] regulator-wifi-en.gpio
    GPIO1_6: output: 0 [x] pci_usb_sel.gpio-hog
    GPIO1_7: input: 1 [x] dio0.gpio-hog
    GPIO1_8: output: 0 [x] regulator-usb-otg2.gpio
    GPIO1_9: input: 1 [x] dio1.gpio-hog
    GPIO1_12: output: 0 [x] regulator-usb-otg1.gpio
    
    Bank GPIO2_:
    GPIO2_12: input: 1 [x] mmc@30b50000.cd-gpios
    
    Bank GPIO3_:
    GPIO3_0: output: 0 [x] ethernet@30be0000.phy-reset-gpios
    
    Bank GPIO4_:
    GPIO4_0: output: 0 [x] rs485_en.gpio-hog
    GPIO4_1: input: 0 [x] mipi_gpio3.gpio-hog
    GPIO4_2: output: 0 [x] rs485_hd.gpio-hog
    GPIO4_3: input: 0 [x] mipi_gpio2.gpio-hog
    GPIO4_4: input: 0 [x] mipi_gpio1.gpio-hog
    GPIO4_7: output: 1 [x] pci_wdis#.gpio-hog
    
    Bank GPIO5_:
    GPIO5_4: output: 0 [x] led-1.gpios
    GPIO5_5: output: 0 [x] led-0.gpios
    
  • set GPIO named DIO1 to logic 1:
    u-boot=> gpio set dio1
    gpio: pin dio1 (gpio 9) value is 1
    
  • set GPIO named DIO1 to logic 0:
    u-boot=> gpio clr dio1
    gpio: pin dio1 (gpio 9) value is 0
    
  • read GPIO DIO1 logic level:
    u-boot=> gpio input dio1
    gpio: pin dio1 (gpio 9) value is 0
    

LED support

If LED support is enabled you can configure board LED's via U-Boot:

  • List LED's:
    u-boot=> led list
    led-0           off
    led-1           off
    
  • turn an LED on:
    u-boot=> led led-0 on
    
  • turn an LED off:
    u-boot=> led led-0 off
    

Note that many Gateworks boards have a front-panel bi-color LED which is supported by two distinct LED's for 'red' and 'green'. In this case you must enable one and disable the other (setting both 'on' is the same as setting both 'off').

GSC support

The gsc command that will let you interact with the Gateworks System controller on Gateworks boards.

Examples:

  • see basic info
    u-boot=> gsc  
    GSCv3   : v61 0x1d6f RST:VIN Thermal protection:enabled at 96C 
    RTC     : 1970-01-05  22:36:56 UTC
    Model   : GW7301-01-CF
    Serial  : 928012
    MFGDate : 01-09-2023
    SOM     : GW7001-F 928012 01-09-2023
    BASE    : GW7301-C 878707 05-04-2022
    
  • disable primary power supply for 5 seconds
    u-boot=> gsc sleep 5
    GSC Sleeping for 5 seconds
    
  • Show hardware monitor values:
    u-boot=> gsc hwmon
    temp    : 47.9C
    vdd_bat : 3.216V
    vdd_vin : 16.724V
    vdd_adc1: 0.000V
    vdd_adc2: 0.000V
    vdd_dram: 1.119V
    vdd_1p2 : 1.192V
    vdd_1p0 : 0.000V
    vdd_2p5 : 0.000V
    vdd_3p3 : 3.272V
    vdd_0p95: 0.938V
    vdd_1p8 : 1.793V
    vdd_gsc : 2.950V
    
    • Note that the voltage rails connected to the GSC hardware monitor vary per board

Network

U-Boot network support exists for the network controllers found within the SoC of the board. If PCI is supported by the product family there are a few PCI based network controller drivers as well.

Because U-Boot is not a full OS and does not support background drivers network operations occur based on various network commands such as 'tftpboot', 'dhcp'. The network interface used for these commands is specified by the 'ethact' env variable. The 'net list' command available in newer versions of U-Boot can help identify the names of the network interface that can be used.

To see a table showing the physical network interface mapping to U-Boot devices refer to:

The following U-Boot environment variables are used with networking:

  • ipaddr - local IP address
  • serverip - TFTP server IP
  • netmask - Netmask
  • gatewayip - Gateway IP if needed
  • ethact - controls which interface is currently active
  • ethprime - controls which interface is used first
  • ethrotate - when set to 'no' uboot does not go through all available network interfaces and instead just stays at the currently selected interface (ethact)
  • filesize - gets set to the size of data transferred
  • netretry - when set to 'no' each network operation will either success or fail without retrying. When set to 'once' the operation will fail only when all available network interfaces have been tried once without success.

DHCP

The dhcp command (if enabled) can use the DHCP protocol to configure networking.

Examples:

  • use DHCP to configure networking:
    u-boot=> dhcp
    BOOTP broadcast 1
    BOOTP broadcast 2
    BOOTP broadcast 3
    DHCP client bound to address 172.24.20.160 (1047 ms)
    

A successful bind will set the following:

  • ipaddr
  • netmask
  • gatewayip
  • serverip

The following variables affect dhcp:

  • autoload - if set will attempt to transfer a file from the server

DNS

The dns command (if enabled) can use the DNS protocol to perform a name to IP lookup. You must setn the 'dnsip' env variable to an accessible DNS server.

Examples:

  • use DNS to lookup dev.gateworks.com
    u-boot=> dhcp
    BOOTP broadcast 1
    DHCP client bound to address 172.24.20.160 (1046 ms)
    u-boot=> setenv dnsip 8.8.8.8 
    u-boot=> dns dev.gateworks.com
    108.161.129.64
    

TFTP

The tftpboot command has been the primary method for transferring files from a server for many years.

The following U-Boot environment variables (as well as the general network variables above) are used with tftp:

  • tftpsrcport - UDP source port (if not set uses default)
  • tftpdstport - UDP dest port (if not set uses well known port 69)
  • tftpblocksize - if not set will use TFTP server's default block size
  • tftptimeout - retransmission timeout for TFTP packets in ms (min value is 1000, default is 5000)

Examples:

  • fetch a file via TFTP to a memory address specified by loadaddr:
    u-boot=> tftpboot $loadaddr 192.168.1.146:file
    

The filesize variable will be set to the number of bytes transferred

NFS

The NFS command (if enabled) can transfer a file from an NFS server to a memory address.

Examples:

  • fetch a file via NFS to a memory address specified by loadaddr:
    u-boot=> nfs $loadaddr 172.24.21.238:/venice/firmware-venice-imx8mp.bin
    

The filesize variable will be set to the number of bytes transferred

WGET

The TCP protocol and wget command (if enabled) were added to U-Boot v2023.01 to support fetching files via HTTP however until very recently it was plagued with a couple of issues. These issues have been resolved and support has been enabled for venice U-Boot

Examples:

  • fetch a file via HTTP to a memory address specified by loadaddr:
    u-boot=> wget $loadaddr 172.24.21.238:/venice/firmware-venice-imx8mp.bin
    HTTP/1.0 200 OK| | | | | | | | | | | | | | | | | | | | | | 
    Packets received 2900, Transfer Successful
    Bytes transferred = 4194336 (400020 hex)
    

The filesize variable will be set to the number of bytes transferred

Note that only HTTP is supported.

Generic File System and File Management support for Block storage devices

For modern versions of U-Boot there are several commands that work with files which support multiple file systems and device interfaces eliminating the need for you to know what filesystem is used and avoid using the deprecated filesystem specific commands. For these commands you specify the interface name (ie mmc,usb,sata) , a device number and optionally a partition.

  • load <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]] - load a file to memory
  • save <interface> [<dev[:part]> <addr> <filename> bytes [pos] - save a file from memory
  • ls <interface> [<dev[:part]> [directory]] - list files on a device/directory
  • ln <interface> <dev[:part]> target linkname - create a symlink
  • size <interface> <dev[:part] <filename> - determine size of a file (saved in 'filesize' env variable)

The 'part' command can be used to work with partition tables (both GPT and MBR if enabled):

  • part uuid <interface> <dev>:<part> - print partition UUID
  • part uuid <interface> <dev>:<part> <varname> - set varname to partition UUID
  • part list <interface> <dev> - print partition table
  • help part - see more options

Note that for the usb interface you must start the interface first with a 'usb start' operation. USB bus enumeration does not happen in the background.

MMC (eMMC and microSD)

The 'mmc' command supports various low level operations on MultiMediaCard bus devices such as eMMC and microSD when not working at the fileystem layer. When working at this level you must set the current mmc device manually. Examples:

  • mmc list - list available device numbers
    u-boot=> mmc list
    FSL_SDHC: 0
    FSL_SDHC: 1
    FSL_SDHC: 2 (eMMC)
    
  • mmc dev [dev] [part] [mode] - show or set the current mmc device
    u-boot=> mmc dev 2
    switch to partitions #0, OK
    mmc2(part 0) is current device
    u-boot=> mmc dev
    switch to partitions #0, OK
    mmc2(part 0) is current device
    
  • mmc info - show info for currently selected device:
    u-boot=> mmc info
    Device: FSL_SDHC
    Manufacturer ID: 70
    OEM: 0
    Name: IB2964
    Bus Speed: 200000000
    Mode: HS400 (200MHz)
    Rd Block Len: 512
    MMC version 5.1
    High Capacity: Yes
    Capacity: 58.4 GiB
    Bus Width: 8-bit DDR
    Erase Group Size: 512 KiB
    HC WP Group Size: 8 MiB
    User Capacity: 58.4 GiB
    Boot Capacity: 4 MiB ENH
    RPMB Capacity: 4 MiB ENH
    Boot area 0 is not write protected
    Boot area 1 is not write protected
    
  • mmc help - show more options

USB

The 'usb' command supports various low level operations on Universal Serial Bus hosts and devices when not working at the fileystem layer. When working at this level you must set the current usb device manually. Additionally you must use the 'usb start' command to initialize the USB subsystem and enumerate the bus. USB bus enumeration does not happen in the background. Examples:

  • usb start - start and scan the USB controllers
  • usb tree - show a tree view of USB enumerated devices
  • help usb - more USB commands and options

Example Usage:

u-boot=> usb start && usb tree
starting USB...
Bus usb@32e40000: Bus usb@32e50000: USB EHCI 1.00
scanning bus usb@32e40000 for devices... 1 USB Device(s) found
scanning bus usb@32e50000 for devices... 4 USB Device(s) found
       scanning usb for storage devices... 1 Storage Device(s) found
USB device tree:
  1  Hub (480 Mb/s, 0mA)
     u-boot EHCI Host Controller 
   
  1  Hub (480 Mb/s, 0mA)
  |  u-boot EHCI Host Controller 
  |
  +-2  Hub (480 Mb/s, 0mA)
    |  Microchip Tech USB2744 
    |
    +-3  Mass Storage (480 Mb/s, 100mA)
    |     CENTON USB 52D0CE92
    |  
    +-4  Vendor specific (480 Mb/s, 0mA)
         Microchip Tech Hub Controller 

USB Device support (via USB Host mode)

A very small set of USB devices are supported in U-Boot depending on the version and configuration. Typically the following devices are used during development:

  • USB Network Adapters
  • USB Mass Storage

USB Network Adapters

A very small set of USB network adapters are supported in U-Boot depending on the version and configuration:

Depending on the version of U-Boot more devices may be available

To use these use the following U-Boot configuration:

  1. scan USB bus for supported devices:
    usb start
    
    • If you have a supported USB device attached you will see a message to that effect
  2. set active ethernet device (use 'net list' to see a list of device names)
    setenv ethact asx0
    
  3. Use networking as needed:
    ping 192.168.1.254
    tftp ${loadaddr} ${file}
    

USB Mass Storage (UMS) Device

USB Mass Storage (UMS) device support is not as robust as it is on Linux. Specifically some older devices that have support for various quirks in Linux may not work in U-Boot.

Older UMS devices sometimes fail to enumerate on the bus. If you encounter this you can try the following:

  • see if it enumerates on a different port (sometimes enumerating through an on-board USB HUB can be problematic). If your board has a USB OTG or Type-C connector those interfaces do not go through a USB HUB so try your UMS device there
  • try setting the 'usb_pgood_delay' env variable to something like 2000 before you do your 'usb start'. This represents a millisecond delay between enabling the VBUS regulator and enumerating the bus

Example:

u-boot=> usb start && usb tree
starting USB...
Bus usb@32e40000: Bus usb@32e50000: USB EHCI 1.00
scanning bus usb@32e40000 for devices... 1 USB Device(s) found
scanning bus usb@32e50000 for devices... 4 USB Device(s) found
       scanning usb for storage devices... 1 Storage Device(s) found
USB device tree:
  1  Hub (480 Mb/s, 0mA)
     u-boot EHCI Host Controller 
   
  1  Hub (480 Mb/s, 0mA)
  |  u-boot EHCI Host Controller 
  |
  +-2  Hub (480 Mb/s, 0mA)
    |  Microchip Tech USB2744 
    |
    +-3  Mass Storage (480 Mb/s, 100mA)
    |     CENTON USB 52D0CE92
    |  
    +-4  Vendor specific (480 Mb/s, 0mA)
         Microchip Tech Hub Controller 
       
u-boot=> usb storage
  Device 0: Vendor: CENTON   Rev: 8.07 Prod:                 
            Type: Removable Hard Disk
            Capacity: 61500.0 MB = 60.0 GB (125952000 x 512)
u-boot=> ls usb 0:1 boot
<DIR>       4096 .
<DIR>       4096 ..
             860 boot.scr
        10407524 Image

USB Device Gadget support

Very limited support for USB device/gadget mode exists

USB Mass Storage (UMS) Gadget

The 'ums' command can be used to allow a host PC to access (slowly!) a block storage device on your target board.

On boards that support this with a USB OTG or Type-C connector cabled to a PC USB host you can use:

  • ums <usb_controller> [<devtype>] <dev[:part]> - usb controller is the number of the usb controller (ie 0 or 1) to present the interface on while devtype, dev, and part specify the interface (devtype), device number (dev), and optional partition to expose via the USB Mass Storage device class

Examples:

  • expose mmc device 0 on USB host controller 0 as a USB Mass Storage device:
    usb start && ums 0 mmc 0
    

This requires CONFIG_USB_FUNCTION_MASS_STORAGE to be enabled and a USB host controller that can operate in gadget mode

USB CDC ACM (Serial) Gadget

You can use USB in device mode for console input/output to a host PC which supports the standard CDC ACM USB driver:

  • Examples:
    • use only ACM for console
      setenv stderr usbacm && setenv stdout usbacm && setenv stdin usbacm
      
    • use both ACM as well as original env based UART serial console (requires CONFIG_CONSOLE_MUX=y):
      setenv stderr ${stderr},usbacm && setenv stdout ${stdout},usbacm && setenv stdin ${stdin},usbacm
      

After doing this on your host PC the device is connected to with a USB-C to Host cable you should see a USB ACM device enumerate and can use it:

  • example on a Linux PC you will see the following in syslog (dmesg):
    usb 3-10.5.5: new high-speed USB device number 21 using xhci_hcd
    usb 3-10.5.5: New USB device found, idVendor=0525, idProduct=a4a5, bcdDevice= 2.21
    usb 3-10.5.5: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    usb 3-10.5.5: Product: USB download gadget
    usb 3-10.5.5: Manufacturer: Gateworks
    usb 3-10.5.5: SerialNumber: 445096
    usb-storage 3-10.5.5:1.0: USB Mass Storage device detected
    usb-storage 3-10.5.5:1.0: Quirks match for vid 0525 pid a4a5: 10000
    cdc_acm 3-10.5.5:1.0: ttyACM0: USB ACM device
    
    # connect to ttyACM0 with a terminal program such as screen/minicom/picocom
    screen /dev/ttyACM0
    

This requires CONFIG_USB_FUNCTION_ACM to be enabled and a USB host controller that can operate in gadget mode

PCI

PCI bus support is enabled on the Ventana family due to the fact that at least one Ventana board model has only PCI based network interfaces.

NetConsole (access U-Boot console from network)

U-Boot does not contain any TCP implementation and as such there is no 'telnet server' or 'telnetd' support. There is however something similar called 'NetConsole' which will allow stdin/stdout/stderr to be directed to a UDP network port. If you set this up you can use the Linux 'nc' or 'netcat' tool or use the the 'netconsole' shell script provided in tools/netconsole (which uses these tools) to talk to U-Boot's interpreter from a Linux host.

Using NetConsole, the paradigm is reversed from the telnet/ssh perspective a bit such that you need to configure U-Boot to listen to a specifc IP address of a server.

To enable NetConsole you must do the following:

  • U-Boot:
    • configure networking: For example have a network interface supported by U-boot, set ipaddr env variable and serverip env variable (make sure you can ping $serverip):
      setenv ipaddr 192.168.1.1 # local ip
      setenv serverip 192.168.1.146 # host ip running netcat/netconsole
      
    • set the ncip address to your server:
      setenv ncip ${serverip}
      
    • set stdin/stdout/stderr as desired to nc:
      setenv stdin nc; setenv stdout nc; setenv stderr nc
      
    • (optional) if you want these changed persistent, do a saveenv:
      saveenv
      
  • Linux host:
    • make sure you have netcat (either nc or netcat applications)
    • grab the netconsole shell script from U-Boot's tools directory:
      wget https://raw.githubusercontent.com/Gateworks/u-boot-imx6/gateworks_v2015.04/tools/netconsole
      chmod +x netconsole
      
    • use netconsole to listen to your target IP address for input/output:
      netconsole 192.168.1.1
      
      • Note that netconsole remaps the interrupt from Cntl-C to Cntl-T so that you can use Cntl-C over the network console

For a bootloader configuration that sits waiting for network commands from a specific host but with a timeout you can use the preboot env variable to execute a script prior to bootcmd such as:

setenv serverip 192.168.1.146 # your server ip
setenv ipaddr 192.168.1.1 # your local ip
setenv netretry no
setenv preboot 'echo "Looking for server at $serverip..."; \
 if ping $serverip; then setenv ncip ${serverip}; setenv bootdelay 10; \
 echo "Starting NetConsole to ${ncip} and waiting for ${bootdelay} seconds..."; \
 setenv stdin nc; setenv stdout nc; setenv stderr nc; \
 fi'
saveenv
  • Note that we set netretry to 'no' which causes network operations to not retry (otherwise the ping will go forever). This could also be set to 'once' if you wish to cycle through all available network interfaces (such as on-board NIC's as well as USB nics) instead of just 'ethprime'.

This only pertains to input/output of the U-Boot environment. Once the bootloader jumps to the kernel, the kernel is in charge of what to do about its input/output, which is controlled via the 'console' kernel cmdline. Note that there is a CONFIG_NETCONSOLE option in the kernel that uses a 'netconsole' kernel cmdline however that option is not enabled in the Gateworks kernels by default

References:

Installing Large Disk Images on Systems with Limited RAM

In certain scenarios, devices with limited RAM face challenges when updating firmware or system images stored on MMC flash storage. Attempting to install large disk images directly may fail due to memory constraints. To address this limitation, a practical approach involves splitting the disk image into smaller chunks and updating the device incrementally. This wiki section documents a solution for how to install these larger than RAM disk images inside of u-boot; on Gateworks SBCs.

Spliting the Disk Image Into Segments

Before proceeding with the update process, the disk image needs to be split into smaller parts. This can be achieved using the Linux split command, making sure to decompress the image if it's compressed. The key requirement for the process outlined here is that the file must be divided into segments that are evenly divisible by 512-byte blocks (so any multiple of KB or MB is fine). This ensures that the subsequent calculation of block count, as demonstrated in the script in the following section. Here are the steps to split our BSP disk image as an example:

# If the image is compressed, extract it first
gunzip jammy-venice.img.gz

# Split the image into parts of 500MB each named 'jammy-venice.img.part<x>'
split -d -b 500M jammy-venice.img jammy-venice.img.part

Downloading and Installing the Segments

To handle the segmented installation of the disk image, we will utilize a U-Boot script specifically designed for devices with limited RAM. This script orchestrates the sequential installation of each segmented portion of the disk image. Here's how to create and use the U-Boot script:

cat <<\EOF> storage_split_update
setenv storage_split_update 'if itest.s x == "x${splitfile}"; then
    # Check if the ${splitfile} environment variable is set
    echo "The following environment variables need to be set in order for this script to run:"
    echo "    splitfile    - full path file name prefix of the splitted files (eg file.img for file.img.part00)"
    exit 1
fi

# Attempt to fetch the ${splitfile} to update storage
echo "Attempting to fetch ${splitfile} to update storage"
setenv 0 0
setexpr i 0
setexpr offset 0

# Iterate over segments to write to Storage
while itest $i -le 99; do
    # Add leading zero to $i if necessary
    itest $i -gt 9 && setenv 0

    if tftpboot $loadaddr ${splitfile}.part${0}${i}; then
        # Calculate block count
        setexpr blkcnt $filesize + 0x1ff
        setexpr blkcnt $blkcnt / 0x200

        # Write segment to Storage
        if ${iface} write ${loadaddr} ${offset} ${blkcnt}; then
            echo "Successfully wrote segment ${i} of ${filesize} bytes"
            setexpr offset ${offset} + ${blkcnt}
            setexpr i ${i} + 1
            # setexpr results in a hex number so convert to dec
            setexpr rem ${i} % 0x10; itest ${rem} -eq 0x0a && setexpr i ${i} + 6
        else
            echo "Error writing segment ${i} of ${filesize} bytes"
            exit 1
        fi
    elif itest ${i} -eq 0; then
        echo "ERROR: image file ${splitfile}.part${0}${i} not found or it is too big to fit in memory"
        exit 1
    else
        echo "SUCCESS: ${splitfile} was successfully written to ${iface} storage device"
        exit 0
    fi
done'
EOF

U-Boot requires scripts to be in a specific binary format for execution with the source command. To compile the script into this format, we use the mkimage tool, which is provided as part of the U-Boot distribution.

mkimage -A arm64 -T script -C none -d storage_split_update ustorage_split_update

Once you have the u-boot source-able script and your split image segments saved to your tftp server; you can now install your large disk Image onto your target board.

cp storage_split_update your_tftp_server
cp jammy-venice.img.part* your_tftp_server

Installing

Before running the update script, you need to download it from your tftp server to your target board, and you need to specify the name of the split image file that you want to use.

#Set your tftp server IP 
u-boot=> dhcp
u-boot=> setenv serverip <YOUR_SERVER_IP>
#Load the script, being sure to use full path name
u-boot=> tftpboot /venice/ustorage_split_update
u-boot=> source $loadaddr
#Run the script & install the segments
u-boot=> setenv splitfile /venice/jammy-venice.img  

Depending on which storage device you want to write your Image to, you need to set the device accordingly then you can run the script. Here is the general use as well as a specific use case:

For MMC:

#mmc dev <device#> <hardware_partition#>
# hardware partition: 0=user 1=boot0 2=boot1
u-boot=> mmc list # show mmc devs
u-boot=> mmc dev 2 0
u-boot=> setenv iface mmc

For USB:

#usb dev <device#> <hardware_partition#>
u-boot=> usb stop && usb start && usb storage # scan and show storage devs
u-boot=> usb dev 0 0
u-boot=> setenv iface usb

For SATA:

#sata dev <device#>
u-boot=> sata init && sata device # scan and show storage devs
u-boot=> sata dev 0
u-boot=> setenv iface sata

For NVMe:

#nvme dev <device#>
u-boot=> pci enum && nvme scan && nvme info # scan and show storage devs
u-boot=> nvme dev 0
u-boot=> setenv iface nvme

To initiate the instillation after you have properly selected the storage device your want:

run storage_split_update

Once the image download and installation process is complete, you can proceed to use the board as usual.

u-boot=> boot
Last modified 6 weeks ago Last modified on 11/07/2024 06:43:48 PM
Note: See TracWiki for help on using the wiki.