Quantcast
Channel: Boundary Devices
Viewing all articles
Browse latest Browse all 391

U-Boot on i.MX6

$
0
0
There are a lot of posts in this blog that describe the state of U-Boot for our i.MX6 boards, but most of them describe the history. They were designed to help the early adopters make transitions as we switched from the Freescale U-Boot 2009.08 to main-line and added display support.

Things have stabilized, and in this post, we’ll recap the current state of affairs to provide the new user a quick Getting started guide.

Here are some things you should know about the U-Boot that we ship with our i.MX6 boards:
  • Our boards boot to a serial EEPROM. The i.MX6 processor has a set of internal fuses that control the boot device, and we program these for SPI-NOR. There are a variety of ways to over-ride this, but we don’t recommend them except for very specific needs.
  • The primary U-Boot console is the serial port on a DB-9 labelled console. It’s configured for direct connection to common USB serial adapters (i.e. as DCE). The baud rate is 115200. 8 data bits, no parity, no flow control. If you hit the any key right after power-on, you’ll abort the automatic boot process and get a U-Boot prompt on the serial port.
  • Very new U-Boot images have support for USB keyboard. See this post for details. If you boot without a working 6x_bootscript on SD card or SATA, you’ll get a prompt on the display and USB keyboard in addition to the serial port.
  • Our images will boot from either SD card slot or SATA. The default bootcmd string is set to iterate through all of the boot media on your board, looking for the file 6x_bootscript in partition 1. If found, the script will be run (and won’t return if successful). Note that some userspaces may require the use of a certain boot media. Consult the corresponding documentation for details.
  • Our images will boot from FAT, ext2, ext3, or ext4 filesystems. The default bootcmd variable will try FAT first, then ext2 (which supports ext3 and ext4).
  • If you power-on a device with one of our displays connected, you should get an on-screen image. Display support has been in U-Boot for a while now, and you should see a display without any SD card present.
  • Our default bootcmd is set to run 6x_bootscript. To elaborate, we leave the precise boot instructions to the userspace image itself, since different distributions package things differently and require at least different kernel command-lines. We accomplish this by having a bootcmd which simply loads a distribution-specific startup script (6x_bootscript) from the first partition of either SD card or SATA drive.
  • We provide sample boot scripts for Android and typical non-Android use. You can find these in the directory corresponding to your board in files named 6x_bootscript*. These are just samples though. You will likely need to tailor then for your use in production, because
  • The default boot scripts try to detect your display. There are some notes in this post but from a high-level, suffice it to say that these boot scripts try to provide an easy out-of-the-box experience.
  • Boot scripts are compiled text files consisting of a series of U-Boot commands. The language supports the use of conditionals, so you can test for the presence of files, environment variables, and the like.
  • We have an online tool for compiling boot scripts here that takes care of the arcane mkimage command-line and removes carriage-returns, which the U-Boot hush parser doesn’t like.
  • Don’t assume that you need to re-compile and re-install U-Boot to match your userspace. We routinely boot Linaro, Buildroot, LTIB, Ubuntu, Debian, Timesys, Android, QNX, and Windows Embedded Compact 7 operating systems without re-programming U-Boot. You should only need to re-install U-boot to take advantage of a new feature or to fix a bug. The U in U-Boot stands for Universal and it comes pretty darn close. Many userspace builders will build a U-Boot image for you, but that’s primarily because other platforms are configured for boot to raw SD card and require it. Our boards don’t.
  • Use the 6x_upgrade script and the upgradeu environment variable if you do need an upgrade. You can copy 6x_upgrade and u-boot.imx to an SD card, and upgrade like this:
    U-Boot > run upgradeu
    ...
    check U-Boot
    320748 bytes read in 145 ms (2.1 MiB/s)
    read 0x4e4ec bytes from SD card
    SF: Detected SST25VF016B with page size 4 KiB, total 2 MiB
    probed SPI ROM
    byte at 0x1200000d (0xf0) != byte at 0x1240000d (0xfc)
    Total of 13 byte(s) were the same
    Need U-Boot upgrade
    Program in 5 seconds
    5
    4
    3
    2
    1
    erasing
    programming
    verifying
    Total of 320748 byte(s) were the same
    ---- U-Boot upgraded. reset
    
  • If you’re booting to SD card or SATA, you shouldn’t need to set any environment variables before booting. Our sample boot scripts configure most things automatically, and you should generally do the same. If you need to customize the boot environment (usually the bootargs variable), you’ll generally want to hack the boot script instead. This is easier to copy to other machines, and fits nicely into a source repository.
  • If you’re booting over NFS during development, you should hack your environment variables. See the notes below for typical usage.
  • Restore your factory defaults with clearenv.
    U-Boot > run clearenv
    
    Or better yet, use env default -f -a. We only learned of this nice feature recently, and will likely discard clearenv in the future.

    U-Boot > env default -f -a
    
  • Our U-Boot source code is on Github. We aim to have all of the code for our standard products in main-line U-Boot, but generally have at least a few patches in flight.
  • Our production branch contains the version that we’re currently shipping for new orders and is typically the most stable and recommended branch.
  • Our staging branch contains the version that we’re prepping for the next release. It may have some bugs, but also may have some new features of interest.


Boot flow

The process of booting an i.MX6 involves multiple steps:
  1. At power-on or reset, the processor starts executing code from on-chip ROM. This code reads the Boot Mode switches, and if they’re in the normal position (Boot from fuses), goes on to read the fuses to find out which media to search for a bootable image.
    In our case, it will see that we have the fuses programmed for serial EEPROM, and should find a U-Boot image at offset 0×400 in that device.
  2. The code in on-chip ROM will read the U-Boot preamble, configuring registers for DDR, then copy U-Boot into DDR and begin U-Boot execution.
  3. U-Boot will wait for bootdelay seconds for a user to abort the boot with a keystroke on the console (the serial port).
  4. If no keystroke is present, U-Boot will execute a set of commands stored in the environment variable bootcmd.
  5. As mentioned above, our default bootcmd is configured to iterate through all bootable media (SATA and both SD cards), looking for 6x_bootscript. If found, the commands inside are executed. In the normal case, these commands will never return because an O/S will be launched.

Unbricking

If step 1 above sees the Boot mode pins in the Serial Boot position, or doesn’t find a valid image in the serial EEPROM, the code in the on-chip ROM will enter serial download mode.

This mode allows a user to download a valid boot image over the USB OTG port, providing a robust means of recovery.

If you connect the USB OTG port to a Linux-based machine (including i.MX6 devices), you can see this in the output of lsusb:
~/$ lsusb
...
Bus 001 Device 009: ID 15a2:0054 Freescale Semiconductor, Inc. i.MX6Q SystemOnChip in RecoveryMode
We wrote a Linux-based tool called imx_usb that supports this protocol, so you can supply a U-Boot image to the device from the command-line like so:
~/imx_usb_loader$ sudo ./imx_usb u-boot.imx
The sudo is needed to provide access to the raw USB device, and the command-line parameter can be any fully-formed i.MX6 image. In the example above, we’re supplying the U-Boot binary.

You can find more details in the our post on un-bricking an i.MX6.

The Freescale Manufacturing tool does something similar in the first stage, but requires USB OTG support in U-Boot itself, and this is not yet supported in the main-line code base.

The notes below provide some additional details for advanced users. Most users can ignore them.

How to build

Assuming that you have a cross-compiler for armv7 named arm-none-linux-gnueabi-gcc, you can get and compile U-Boot like this:
~$ git clone git://github.com/boundarydevices/u-boot-imx6.git
...
Resolving deltas: 100% (156593/156593), done.
~$ cd u-boot-imx6
~/u-boot-imx6$ git checkout origin/production -b production
~/u-boot-imx6$ export ARCH=arm
~/u-boot-imx6$ export CROSS_COMPILE=arm-none-linux-gnueabi-
~/u-boot-imx6$ make nitrogen6q_config 
Configuring for nitrogen6q - Board: nitrogen6x, Options: IMX_CONFIG=board/boundary/nitrogen6x/nitrogen6q.cfg,MX6Q,DDR_MB=1024
~/u-boot-imx6$ make all
Generating include/autoconf.mk
...
~/u-boot-imx6$ ls -l u-boot.imx 
-rw-rw-r-- 1 user group 312572 Nov 26 11:48 u-boot.imx
Key bits embedded in the snippet above include:
  • We selected nitrogen6x_config.
    This is used for both of our most popular boards, the Nitrogen6X and SABRE Lite.
  • We selected the production branch.
  • The output is in the file u-boot.imx
Refer to this post for more detail.

Basics of launching Linux

In order to boot Linux using U-Boot, you usually need only two things:
  • A working kernel, and
  • A filesystem containing init
But you’ll generally need a third:
  • A set of kernel parameters passed via the bootargs environment variable in U-Boot.
The kernel image is typically stored in a file named uImage and is most commonly stored in the /boot directory of a filesystem, but these are guidelines, not rules. The only important thing is that U-Boot is able to load the kernel into RAM.

The filesystem used at startup (generally referred to as the root filesystem) could be a typical ext2, ext3, ext4 filesystem on SD card or SATA, an accessible NFS share, or a RAM disk image. The only important piece is that you can tell the kernel how to find it at startup time.

Kernel startup is almost always invoked using the bootm command under U-Boot. The first parameter to bootm is the address of a kernel as shown in this example:

U-Boot > mmc dev 0
U-Boot > ext2load mmc 0 10800000 /boot/uImage
U-Boot > bootm 10800000
This simple example shows how to load /boot/uImage into memory address 0x10800000 and launch it.

But wait. We haven’t provided a filesystem reference in this example.

When you invoke bootm using a single parameter, you’ll need to specify the root filesystem indirectly through the bootargs environment variable as shown below. This example tells the kernel to wait for the root filesystem to become available, and that the root filesystem is the partition /dev/mmcblk0p1 (the first partition of the first SD card enumerated on the system).

U-Boot > setenv bootargs $bootargs rootwait root=/dev/mmcblk0p1
U-Boot > mmc dev 0
U-Boot > ext2load mmc 0 10800000 /boot/uImage
U-Boot > bootm 10800000
This is precisely what’s done in the default boot script board/boundary/nitrogen6x/6x_bootscript.txt.

To boot a RAM disk for the root filesystem, you add a second parameter to the bootm command with the load address of the RAM disk and omit the root= clause from bootargs. The following example illustrates this:

U-Boot > mmc dev 0
U-Boot > ext2load mmc 0 10800000 /boot/uImage
U-Boot > ext2load mmc 0 12800000 /boot/uramdisk.img
U-Boot > bootm 10800000 12800000
RAM disks are used in the default Android boot script (board/boundary/nitrogen6x/6x_bootscript_android.txt), but are also useful for other distributions. We used one to put together this image for exposing storage across USB.

Note that there is a third parameter for bootm that’s required for use of the main-line Linux kernel. It supplies something called a device tree, which is used to make the kernel a bit more generic. If you’re working with main-line, it’s likely that you have access to this, so we won’t go into the details here.

How to boot over NFS

Booting over NFS is a straightforward extension of this that simply replaces the root= clause in bootargs with root=/dev/nfs. It does require a couple of additional parameters to the kernel, though:
parametertypical value
nfsroothostip:/path/to/rootfs,options
ipdhcp
The first parameter, nfsroot= tells the kernel where to find a root filesystem and what options to pass to the NFS mount process. A typical value for the entire clause might be nfsroot=192.168.0.42:/home/user/imx-android,v3,tcp.

The second parameter, ip= is needed because the kernel needs to have an IP address in order to access the network. An IP address is normally done as a part of the userspace startup, but in the case of an NFS root, we can’t wait for that because of the chicken-and-egg problem.

Putting it all together, we can boot over NFS like this:
U-Boot > setenv bootargs $bootargs rootwait root=/dev/nfs
U-Boot > setenv bootargs $bootargs nfsroot=192.168.0.42:/home/user/imx-android,v3,tcp
U-Boot > setenv bootargs $bootargs ip=dhcp
U-Boot > mmc dev 0
U-Boot > ext2load mmc 0 10800000 /boot/uImage
U-Boot > bootm 10800000
Note that this example doesn’t completely boot over the network, though. The kernel is still loaded from an ext2/3/4 filesystem on partition 1 of the SD card. This brings up the next question:

Loading a file over TFTP

U-Boot contains a number of networking commands, and support for a number of protocols.

The most common are the dhcp and tftp commands, and we generally use the dhcp command to acquire an IP address and transfer a file in a single command like so:
U-Boot > dhcp 10800000 192.168.0.62:uImage
This command will acquire an IP address using DHCP, then request a file named uImage and load it into memory address 0x10800000.

When used in conjunction with the NFS boot arguments, this provides a single, relatively command line to be used for booting:
U-Boot > dhcp 10800000 192.168.0.62:uImage && bootm 10800000
You may now be understanding why I mentioned the saving of environment variables when booting NFS. There are a number of parameters to provide, including
  • the IP address of the TFTP server, and
  • the IP address and path to the NFS filesystem
If you’re going to save all of these, you might as well just over-write the bootargs variable entirely, and the bootcmd variable while you’re at it.

You can always run clearenv when you’re done.

Important clauses for bootargs:

To recap and expand on the notes above, here are a set of known variables that you might want to set in bootargs under U-Boot:
nametypical valueNotes
consolettymxc1,115200This tells the kernel to send kernel messages to /dev/ttymxc1, the serial console port. You almost always want this set during development, though you might consider turning it off in production, since it can slow the boot process.
enable_wait_modefalseThis variable controls the behavior of the idle loop in the Linux kernel and you may see system stalls without this value set.
root=/dev/mmcblk0p1
/dev/sda1
See notes above
video=mxcfb0:dev=hdmi,1280x720M@60,if=RGB24
mxcfb0:dev=ldb,LDB-XGA,if=RGB666
mxcfb0:dev=ldb,1024x600M@60,if=RGB666
mxcfb0:dev=lcd,CLAA-WVGA,if=RGB666
Defines the display(s). These should be numbered starting at mxcfb0 through mxcfb2 and will translate into a number of device nodes as described in this post.
consoleblank0This variable controls the amount of idle time before the console device goes to sleep. Use consoleblank=0 to disable blanking on idle.
vmalloc400MThis controls the amount of memory available to the kernel for dynamic allocation. You’ll normally want this to be 400M or so on a system running a graphical U/I with accelerated video and graphics.
fbmem28MThis controls the amount of memory allocated for each frame-buffer.
ipdhcp
192.168.0.44:::255.255.255.0
Refer to the documentation for details.
nfsroot192.168.0.62:/path/to/rootfsRefer to the documentation for details.
androidboot.consolettymxc1Tells Android about the console
androidboot.hardwarefreescaleAndroid needs this. Details elsewhere

Booting Yocto

Thanks largely to the efforts of the team at O.S. Systems, the current builds of Yocto using the meta-fsl repositories have a custom boot script that specifies partition 2 for the root filesystem instead of partition 1.

Their efforts show the right thing to do: have the boot script built as a part of the userspace, but not U-Boot itself.

Booting Windows Embedded Compact 7

Coming soon

Booting QNX

Details coming soon, but the general process involves the use of the gocommand:
mmc dev ${disk} && ext2load mmc ${disk} 10800000 /path/to/ifs.ifs && go 10800000

Booting Debian

Details coming soon, but also available on eewiki.

.


Viewing all articles
Browse latest Browse all 391

Trending Articles