A quick tour into the bootloader

As stated at the beginning of this chapter, using the serial console, we can get access to the bootloader.

Actually, all the developer kits presented in this book have two bootloaders: a pre-bootloader or Secondary Program Loader (SPL), named MLO for the BeagleBone Black, boot.bin for SAMA5D3 Xplained, and SPL for the Wandboard, which initializes the hardware components, such as the RAM and some mass storage devices, and bootloader named U-Boot for all boards, which is the real bootloader that initializes almost all the peripherals and has support for, among other things, booting over network and a scriptable shell through which basic commands can be given. Now the one million dollar question is: why should a developer be able to manage the bootloader too?

Well the answers are more than one; however, the most important ones are:

  • By passing a well-formed command line to the kernel, we can change some functionalities in the running filesystem.
  • From the bootloader, we can easily manage a factory restore method (it is usually made with a hidden button in a tiny hole on the system's box. By keeping that button pressed while powering up the system, the user can cause the whole system to reset to its factory defaults).
  • Through the bootloader, we can decide which device to use to perform a boot. For instance, we can force a boot from a microSD or from a USB key.

So now let's see how we can get the U-Boot's prompt using one kit since they're running the same U-Boot version (the following messages are from SAMA5D3 Xplained).

Just after the power up, we will see some interesting messages on the serial console:

RomBOOT
U-Boot SPL 2016.03-dirty (Apr 15 2016 - 19:51:18)
Trying to boot from MMC
reading u-boot.img
U-Boot 2016.03-dirty (Apr 15 2016 - 19:51:18 +0200)
CPU: SAMA5D36
Crystal frequency: 12 MHz
CPU clock : 528 MHz
Master clock : 132 MHz
DRAM: 256 MiB
NAND: 256 MiB
MMC: mci: 0
reading uboot.env
** Unable to read "uboot.env" from mmc0:1 **
Using default environment
In: serial
Out: serial
Err: serial
Net: gmac0
Error: gmac0 address not set.
, macb0
Error: macb0 address not set.
Hit any key to stop autoboot: 1

At this time, we have less than 1 second to strike the Enter key to stop the countdown and get the U-Boot prompt shown as follows:

=>

Well, now we can get a list of the available commands using the help command:

=> help
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
...
usb - USB sub-system
usbboot - boot from USB device
version - print monitor, compiler and linker version

As you can see, the list is quite long; however, due to spacing reasons, we cannot report or explain all commands, so we'll take a look at the most important ones.

Tip

For further information regarding the U-Boot bootloader, you may take a look at the user manual at http://www.denx.de/wiki/DULG/Manual .

The help command can also be used to get more information about a command:

=> help help
help - print command description/usage
Usage:
help
 - print brief description of all commands
help command ...
- print detailed usage of 'command'
Tip

Note that most of the commands will display their helping message when executed without any arguments. Of course, this behavior is not respected by those commands that execute with no arguments.

The environment

Before starting to take a look at the commands, we should first see one of the most important features of U-Boot: the environment. We can store whatever we need to accomplish a safe system boot in the environment. We can store variables, commands, and even complete scripts in it!

To check the environment content, we can use the print command:

=> print arch=arm baudrate=115200 board=sama5d3_xplained board_name=sama5d3_xplained bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 ro rootwait bootcmd=if test ! -n ${dtb_name}; then setenv dtb_name at91-${board_na
me}.dtb; fi; fatload mmc 0:1 0x21000000 /dtbs/${dtb_name}; fatload mmc
 0:1 0x22000000 zImage; bootz 0x22000000 - 0x21000000 bootdelay=1 cpu=armv7 ethact=gmac0 soc=at91 vendor=atmel Environment size: 412/16380 bytes 

If we need to inspect a specific variable, we can use the print command:

=> print baudrate 
baudrate=115200

We can also inspect a complete script using the print command again:

=> print bootcmd
bootcmd=if test ! -n ${dtb_name}; then setenv dtb_name at91-${board_na
me}.dtb; fi; fatload mmc 0:1 0x21000000 /dtbs/${dtb_name}; fatload mmc
 0:1 0x22000000 zImage; bootz 0x22000000 - 0x21000000
Tip

The bootcmd command is the default boot command that is executed each time the system starts.

The command output is quite cryptic due the fact that the newline (\n) characters are missing (although U-Boot doesn't need them to correctly interpret a script); however, to make the output more readable, the preceding output has been rewritten here with the necessary newline characters:

if test ! -n ${dtb_name}; then
    setenv dtb_name at91-${board_name}.dtb;
fi;
fatload mmc 0:1 0x21000000 /dtbs/${dtb_name};
fatload mmc 0:1 0x22000000 zImage;
bootz 0x22000000 - 0x21000000

In this case, we cannot properly talk about man pages, but if we use the help command, we can take a kind of them.

Tip

Note that the print command is just a short form of the real command printenv.

To write/modify an environment variable, we can use the setenv command:

=> setenv myvar 12345
=> print myvar
myvar=12345

We can read the variable content by prefixing its name with the $ character:

=> echo "myvar is set to: $myvar"
myvar is set to: 12345

In a similar manner, to write a script, we can use this:

=> setenv myscript 'while sleep 1 ; do echo "1 second is passed away.. ." ; done'
Tip

Note that we used the two ' characters to delimitate the script commands! This is to prevent U-Boot from doing some variable replacement before storing the script (it's something similar to what we do when we use the Bash shell's variable substitution).

Again, over here, we did not add the newlines; however, this time, the script is quite simple and readable. In fact, with the newline characters, the output should appear as follows:

while sleep 1 ; do
    echo "1 second is passed away..." ;
done

In the end, we can run a by using the run command, as follows:

=> run myscript 
1 second is passed away...
1 second is passed away...
1 second is passed away...
...
Note

We can stop the script by hitting Ctr l + C . The environment is reset each time the system starts, but it can be altered by modifying the environment file in the microSD (refer to the next section).

In case we made some errors, don't panic! We can edit the variable with this command:

=> env edit myscript
edit: while sleep 1 ; do echo "1 second is passed away..." ; done

Now we can do all the required modifications to the script in an easy manner.

Managing the storage devices

The main goal of a bootloader is to load the kernel into the memory and then execute it; to do that, we must be able to get access to all the storage devices of the board where the kernel can be located. Our boards have several storage devices; however, in this book, we'll see only two of them: MMC (or eMMC) and NAND flash.

MMC

To show MMC (Multi Media Card) management in U-Boot, we are going to use the BeagleBone Black since it has both an eMMC and a microSD port on board. As already stated in the first chapter, we're able to choose our booting device simply using the user button, so it's very important to discover how we can manage these devices, so let's power on our BeagleBone Black and then stop the bootloader by pressing the spacebar within two seconds, as shown here:

U-Boot SPL 2016.03-dirty (Apr 15 2016 - 13:44:25)
Trying to boot from MMC
bad magic
U-Boot 2016.03-dirty (Apr 15 2016 - 13:44:25 +0200)
 Watchdog enabled
I2C: ready
DRAM: 512 MiB
Reset Source: Global warm SW reset has occurred.
Reset Source: Power-on reset has occurred.
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
Using default environment
Net: <ethaddr> not set. Validating first E-fuse MAC
cpsw, usb_ether
Press SPACE to abort autoboot in 2 seconds
=>

Now, as already done earlier with SAMA5D3 Xplained, we can use the help command to see which are the MMC-related commands. We discover that the MMC support is implemented with the mmcinfo and mmc commands. The former can be used to get some useful information about the microSD/MMC present on the selected MMC slot, while the latter is used to effectively manage the microSD.

Let's look at some examples.

We know that our BeagleBone Black has an onboard eMMC on MMC slot 1, so to get some information about that device, we should first select the MMC slot to be examined using the following command:

=> mmc dev 1
switch to partitions #0, OK
mmc1(part 0) is current device

Then, we can ask for the MMC device information using the mmcinfo command:

=> mmcinfo
Device: OMAP SD/MMC
Manufacturer ID: 70
OEM: 100
Name: MMC04
Tran Speed: 52000000
Rd Block Len: 512
MMC version 4.5
High Capacity: Yes
Capacity: 3.6 GiB
Bus Width: 4-bit
Erase Group Size: 512 KiB
HC WP Group Size: 4 MiB
User Capacity: 3.6 GiB
Boot Capacity: 2 MiB ENH
RPMB Capacity: 128 KiB ENH

In the same manner, we can examine the alternate booting microSD we built in Chapter 1, Installing the Developing System, and that we used to boot the system. Here is the output that appears on my system:

=> mmc dev 0
switch to partitions #0, OK
mmc0 is current device
=> mmcinfo 
Device: OMAP SD/MMC
Manufacturer ID: 41
OEM: 3432
Name: SD4GB
Tran Speed: 50000000
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 3.7 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes

Now we can examine the microSD partition table by using the following command:

=> mmc part
Partition Map for MMC device 0 -- Partition Type: DOS
Part Start Sector Num Sectors UUID Type
1 2048 7772160 5697a348-01 83 Boot

We get only one partition where our Debian filesystem is located.

Let's examine the / (root) directory using another command that's useful in listing the content of a EXT4 filesystem, which is the command etx4ls, as shown here:

=> ext4ls mmc 0:1
<DIR> 4096 .
<DIR> 4096 ..
<DIR> 16384 lost+found
<DIR> 4096 opt
<DIR> 4096 boot
<DIR> 4096 lib
<DIR> 4096 sys
<DIR> 4096 home
<DIR> 4096 mnt
<DIR> 4096 dev
...
<DIR> 4096 sbin
<DIR> 4096 proc
<DIR> 4096 tmp

We found the root directory of a Debian OS, and we can use the following command to read the contents of the /boot directory:

=> ext4ls mmc 0:1 /boot
<DIR> 4096 .
<DIR> 4096 ..
 726 uEnv.txt
 7408440 vmlinuz-4.4.7-bone9
<DIR> 4096 dtbs
<DIR> 4096 uboot
 9089599 initrd.img-4.4.7-bone9

Now, as an example, in order to import the uEnv.txt file content, we can use the load command:

=> load mmc 0:1 $loadaddr /boot/uEnv.txt
726 bytes read in 28 ms (24.4 KiB/s)
Tip

Note that the value for the loadaddr variable is usually defined in the default environment (that is, the default built in the U-Boot image at the compilation stage) or using the uEnv.txt configuration file.

The command loads a file from the microSD into the RAM, and then we can parse it and store the data into the environment using the env command:

=> env import -t $loadaddr $filesize
Tip

In contrast with the earlier loadaddr variable, the filesize variable is dynamically set after each file manipulation command's execution, for instance, the just used load command.

To save a variable/command in the environment (in a way that the new value is reloaded at the next boot), we can use U-Boot itself, but the procedure is quite complex and, in my humble opinion, the quickest and simplest way to do it is by just putting the microSD on a host PC and then changing the file on it!

In any case, we can take a look at the read data using the md command, as follows:

=> md $loadaddr
82000000: 64616f6c 72646461 3878303d 30303032 loadaddr=0x82000
82000010: 0a303030 61746466 3d726464 38387830 000.fdtaddr=0x88
82000020: 30303030 720a3030 64646164 78303d72 000000.rdaddr=0x
82000030: 38303838 30303030 6e690a0a 64727469 88080000..initrd
82000040: 6769685f 78303d68 66666666 66666666 _high=0xffffffff
82000050: 7464660a 6769685f 78303d68 66666666 .fdt_high=0xffff
82000060: 66666666 636d6d0a 746f6f72 65642f3d ffff.mmcroot=/de
82000070: 6d6d2f76 6b6c6263 0a317030 616f6c0a v/mmcblk0p1..loa
82000080: 6d697864 3d656761 64616f6c 636d6d20 dximage=load mmc
82000090: 313a3020 6c7b2420 6164616f 7d726464 0:1 ${loadaddr}
820000a0: 6f622f20 762f746f 6e696c6d 242d7a75 /boot/vmlinuz-$
820000b0: 616e757b 725f656d 6f6c0a7d 66786461 {uname_r}.loadxf
820000c0: 6c3d7464 2064616f 20636d6d 20313a30 dt=load mmc 0:1
820000d0: 64667b24 64646174 2f207d72 746f6f62 ${fdtaddr} /boot
820000e0: 6274642f 7b242f73 6d616e75 7d725f65 /dtbs/${uname_r}
820000f0: 667b242f 69667464 0a7d656c 64616f6c /${fdtfile}.load

In this manner, we do a memory dump at the address specified by the loadaddr variable, where we just loaded the /boot/uEnv.txt file content.

Managing the flash

The flash memories are very useful when we don't need relatively small storage devices and we want to keep the cost of a board very low.

In the past, they represented the only (and valid) solution for reliable mass memory devices for embedded systems due to the fact that they can work in very hostile environments (temperatures under 0° C or above 80° C and dusty air) and they have no moving parts that dramatically increase the life cycle of the system.

Nowadays, they are almost replaced by eMMC memories, but they are still present on really compact and low-power systems. In this book, the only boards where we can find them are SAMA5D3 Xplained, so let's switch to this board again.

Tip

In reality, eMMC or MMC (which is not a soldered version of an eMMC) is flash memory, but it takes a specific name because there is a flash controller inside the chip that actually manages the internal flash memory. So, using these devices, we can unload our embedded kits' CPUs of the duty to manage the flash.

The flash memory in our board is a NAND flash, which is a technology that uses floating gate transistors (just like the NOR flash) but is connected in a way that resembles a NAND gate.

Tip

For further information on these devices, a good starting point can be found at https://en.wikipedia.org/wiki/Flash_memory#NAND_flash .

To get some information on mounted chips, we can use the nand info command, as follows:

=> nand info
Device 0: nand0, sector size 128 KiB
 Page size 2048 b
 OOB size 64 b
Erase size 131072 b
subpagesize 2048 b
options 0x 200
bbt options 0x 8000

In this case, we have just one NAND device, so we don't need to use the nand device command to select one, as we did earlier regarding the MMC devices on our BeagleBone Black.

The check for the bad blocks (that is, those parts of the chip that are broken), we can use the following command:

=> nand bad
Device 0 bad blocks:
00c80000
00ca0000

These blocks will never be used to store our data.

Now the last three useful commands to manage the flash content are nand erase, nand read, and nand write. The first command is split into two subcommands: nand erase.part, which erases an entire MTD partition, and nand erase.chip, which erases the entire chip.

Tip

As you know for sure, a flash device needs the erase command because before writing a block, we must erase it! Refer to https://en.wikipedia.org/wiki/Flash_memory#Limitations for an explanation of the problem.

Then, the nand.read and nand.write commands are used as they are expected, that is, to read or write a flash block. At the moment, we are not going to add examples for these actions since we still have no valid data to store in the flash; however, we're going to show how these commands can be used in the Managing a MTD device section in Chapter 5, Setting up an Embedded OS .

Also note that in the NAND flash, we can store the current environment in a similar manner as earlier with the MMC/eMMC using the saveenv command; however, in order to work, this command must be correctly configured inside the U-Boot code by the developer.

Tip

These topics are not covered in this book due to space issues and because they are almost accomplished by the board manufacturer, so we can consider them already-fixed-up. However, you can take a look at the configuration that defines CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE and friends in U-Boot's repository for further information.

GPIO management

The General Purpose Input Output (GPIO) signals are input output pins with no special purpose defined; when a developer needs one of them working as an input pin or as an output pin (or another function), they can easily reconfigure the CPU in order to accommodate their needs (GPIOs will be widely presented in Chapter 6, General Purposes Input Output signals - GPIO ).

Managing GPIO from early booting stages can be useful in selecting a specific mode of functioning: for instance, in a system that normally boots from the NAND flash, we can decide to completely erase it rewrite its contents from a file read by the MMC if a GPIO is set; otherwise, we do a normal boot.

The command to manage GPIO is gpio, and to try usage of this command, we can use the BeagleBone Black board where we can use this command to control the user LEDs. As for the other GPIO lines of the BeagleBone Black, they are mapped as follows:

So we can easily deduce that in order to toggle LED USR0, we can use the following commands:

=> gpio toggle 53
gpio: pin 53 (gpio 53) value is 1
=> gpio toggle 53
gpio: pin 53 (gpio 53) value is 0

Of course, we can turn the LED on and off simply using the set and clear options, respectively, while the input option can be used to read the input status of the related GPIO line.

Tip

You can take a look at what these LEDs are for at:  http://beagleboard.org/getting-started .

Accessing an I2C device

Another useful device class to get access to in early booting stages is I2C devices; in fact, these devices are commonly used to expand the CPU's peripheral set and they can be used for a large variety of purposes that, under some circumstances, must be read or set up during the boot (GPIO will be widely presented in Chapter 9Inter-Integrated Circuit - I2C ).

As for GPIO, I2C devices are completely managed by the i2c command, and to test this command, we have to continue using the BeagleBone Black since it is the only one that has some onboard I2C devices. The list of this devices can be obtained by some simple steps; first of all, let's display a list of all available I2C buses:

=> i2c bus
Bus 0: omap24_0
Bus 1: omap24_1
Bus 2: omap24_2

By taking a look at the board's schematics, we can discover that the bus where these devices are connected to is omap24_0, so let's set it as the current bus using the following command:

=> i2c dev 0
Setting bus to 0

Now we can ask the system to probe all connected devices for us with the following command:

=> i2c probe
Valid chip addresses: 24 34 50

Great! We found three devices; now we can try to read some data from them; in particular, we can try to read the onboard EEPROM content using the i2c md command at address 50 (which is the hexadecimal EEPROM's address):

=> i2c md 0x50 0x0.2 0x20
0000: aa 55 33 ee 41 33 33 35 42 4e 4c 54 30 30 43 30 .U3.A335BNLT00C0
0010: 33 32 31 34 42 42 42 4b 30 37 31 36 ff ff ff ff 3214BBBK0716....

With the preceding command, we asked to dump data from the device at address 0x50 starting from address 0x0 (expressed as a word thanks to the .2 specifier) displaying 0x20 bytes as the output.

In this output, we can recognize the header (bytes 0xaa 0x55 0x33 0xee) and then the board version.

Tip

More information on the BeagleBone Black's EEPROM contents can be taken from the BeagleBone Black's user manual.

Loading files from the network

Another useful U-Boot feature is the ability to load a file from a network connection. This feature used during the developing stages helps the developer avoid having to continuously plug and unplug the microSD card from the system; in fact, as we saw in the first chapter, the system needs the bootloaders and kernel images to start up and, in case of errors during the development of these components, we will need to replace them frequently till they are OK. Well, supposing that at least the networking function works in our U-Boot release, we can use it to load a new image in the system memory.

Let's suppose that our kernel image is not functioning as well as we need it to be; we can set up U-Boot in order to load the kernel image from the network and then boot it to do all the required tests.

The command used to do this action is tftp. This command uses the Trivial File Transfer Protocol (TFTP) protocol to download a file from a remote server.

Tip

You may get more information on the TFTP protocol at:  https://it.wikipedia.org/wiki/Trivial_File_Transfer_Protocol .

The remote server, of course, is our host PC where we have to install a proper package with the following command:

$ sudo aptitude install tftpd
Tip

It may happen that during the installation, we get the following message:

 Note: xinetd currently is not fully supported
 by update-inetd.
 Please consult /usr/share/doc/xinetd/README.De
 bian and itox(8).

In this case, we have to add a file named tftp into the /etc/xinetd.d directory by hand. The file should hold the following code:

    # default: yes
    # description: The tftp server serves files using
    #    the Trivial File Transfer
    #    Protocol. The tftp protocol is often used to
    #    boot diskless workstations, download
    #    configuration files to network-aware
    #    printers, and to start the installation
    #    process for some operating systems.
    service tftp
    {
         disable     = no
         socket_type = dgram
         protocol    = udp
         wait        = yes
         user        = root
         server      = /usr/sbin/in.tftpd
         server_args = -s /srv/tftpboot
    }

Then, we have to create the /srv/tftpboot directory, as follows:

 $ sudo mkdir /srv/tftpboot

Then, restart the xinetd daemon with the usual command, as follows:

 $ sudo /etc/init.d/xinetd restart
 [ ok ] Restarting xinetd (via systemctl): xinetd.se
 rvice. 

When the installation is finished, we should have a new process listening on UDP port 69:

$ netstat -lnp | grep ':\<69\>'
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
udp 0 0 0.0.0.0:69 0.0.0.0:* 

Also, the default tftpd root directory should be /srv/tftpboot, which is obviously empty:

$ ls -l /srv/tftpboot/
total 0

OK; let's copy our kernel image as we did in Debian 8 (jessie) for Wandboard section in Chapter 1 , Installing the Developing System :

$ sudo cp deploy/4.4.7-armv7-x6.zImage /srv/tftpboot/vmlinuz-4.4.7-arm v7-x6
Tip

Note that the sudo usage in the preceding command may not be needed in every system. I used it just because my host PC didn't properly install the daemon, as reported in earlier tip section.

Now in our Wandboard, we have to set up the ipaddr environment variable to be able to ping our host PC first. As an example, my host PC has the following network configuration:

$ ifconfig enp0s3
enp0s3 Link encap:Ethernet HWaddr 08:00:27:22:d2:ed 
 inet addr:192.168.32.43 Bcast:192.168.32.255 Mask:255.255. 255.0
 inet6 addr: fe80::a00:27ff:fe22:d2ed/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:986 errors:0 dropped:0 overruns:0 frame:0
 TX packets:687 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:226853 (226.8 KB) TX bytes:100360 (100.3 KB)
Tip

Note that on your system, both the Ethernet card name and IP address settings may vary, so you have to change the settings in order to fit your LAN configuration.

Also, my DHCP server leaves the IP addresses from 192.168.32.10 to 192.168.32.40 available for my embedded boards, so we can do the following setting in Wandboard:

=> setenv ipaddr 192.168.32.25

Now, if everything works well, we should be able to ping my host PC:

=> ping 192.168.32.43
Using FEC device
host 192.168.32.43 is alive

Great! At this point, we can try to load a file from the host PC, so we have to assign the TFTP server's IP address to the serverip variable, as follows:

=> setenv serverip 192.168.32.43

Then, we can load our kernel image using the following command:

=> tftpboot ${loadaddr} vmlinuz-4.4.7-armv7-x6 
Using FEC device
TFTP from server 192.168.32.43; our IP address is 192.168.32.25
Filename 'vmlinuz-4.4.7-armv7-x6'.
Load address: 0x12000000
Loading: #############################################################
 #############################################################
 ...
 #############################
 688.5 KiB/s
done
Bytes transferred = 5802912 (588ba0 hex)

Perfect, we did it! Now you can use the just download kernel image to continue your developing.

Tip

At the moment, this mode of operation is not explained here, but it will be explained in detail in the next chapter.

Before ending this chapter, let we address the fact that the Wandboard used only one Ethernet port, so it's quite obvious that whatever settings we do in U-Boot are referred to that device, but what happens if we have more that one device? For example, our SAMA5D3 Xplained has two Ethernet ports; how can we manage this setup if we wish to use the tftp command described earlier?

To answer this question, we have to switch the developer kit and power up SAMA5D3 Xplained. After the boot, we have to stop U-Boot and then we have to display the environment:

=> print arch=arm baudrate=115200 board=sama5d3_xplained board_name=sama5d3_xplained bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 ro rootwait bootcmd=if test ! -n ${dtb_name}; then setenv dtb_name at91-${board_na me}.dtb; fi; fatload mmc 0:1 0x21000000 /dtbs/${dtb_name}; fatload mmc 0:1 0x22000000 zImage; bootz 0x22000000 - 0x21000000 bootdelay=1 cpu=armv7 ethact=gmac0 soc=at91 vendor=atmel Environment size: 412/16380 bytes

Looking at the defined variables, we notice that one is named ethact; this is the variable that specifies the currently used Ethernet port. In the preceding output, we read that the system is set to gmac0, which is the gigabit Ethernet port (the port is labeled ETH0/GETH on the board).

So, if we repeat the preceding setup, we should be able to ping our host PC, as did earlier:

=> setenv ipaddr 192.168.32.25
=> ping 192.168.32.43
gmac0: PHY present at 7
gmac0: Starting autonegotiation...
gmac0: Autonegotiation complete
gmac0: link up, 100Mbps full-duplex (lpa: 0x45e1)
host 192.168.32.43 is alive
Tip

If we get the following error, it's because the Ethernet port has no default MAC address:

 *** ERROR: `ethaddr' not set
 ping failed; host 192.168.32.43 is not alive

In this case we, have to set a random one ourselves with the following command:

 => setenv ethaddr 3e:36:65:ba:6f:be

Then, we can repeat the ping command.

Now we can repeat the ping command through the other Ethernet port (the one labeled ETH1 on the board) by changing the ethact variable, as follows:

=> setenv ethact macb0
Tip

The name we have to use is usually specified in the developer kit's documentation.

Now we can repeat the ping command:

=> ping 192.168.32.43
macb0: PHY present at 0
macb0: Starting autonegotiation...
macb0: Autonegotiation complete
macb0: link up, 100Mbps full-duplex (lpa: 0x45e1)
host 192.168.32.43 is alive
Tip

If we got an error regarding the missing definition of the ethaddr variable, we should get the following one, which is related to the eth1addr variable:      *** ERROR: `eth1addr' not set      ping failed; host 192.168.32.43 is not alive This is in case we have to act in the same manner as earlier.

The last note is about the fact that usually, U-Boot will automatically switch the active port in case the autonegotiation fails. If our system has ethact set as gmac0 but the cable is plugged into the ETH1 port, we get the following output:

=> ping 192.168.32.25
gmac0: PHY present at 7
gmac0: Starting autonegotiation...
gmac0: Autonegotiation timed out (status=0x7949)
gmac0: link down (status: 0x7949)
macb0: PHY present at 0
macb0: Starting autonegotiation...
macb0: Autonegotiation complete
macb0: link up, 100Mbps full-duplex (lpa: 0x45e1)
host 192.168.32.43 is alive
Note

We may experience some troubles regarding the networking support with the U-Boot version we're using in this book with SAMA5D3 Xplained. This is because this U-Boot release is not well aligned with the official one from Atmel. If so, don't worry, we can still use the official U-Boot release at:  http://www.at91.com/linux4sam/bin/view/Linux4SAM/Sama5d3XplainedMainPage#Build_U_Boot_from_sources . That will work for sure!

The kernel command line

Before closing our tour of the bootloader, we should take a look at the way U-Boot uses to pass a command line to the kernel. This data is very important because it can be used to configure the kernel and pass some instruction to the user's programs on the root filesystem.

These arguments are stored in the bootargs variable and its setting depends on the board we are using. For example, if power on our Wandboard and, as done earlier, we stop its bootloader, as shown earlier, wecan see thatthis variable is not set at all:

=> print bootargs
## Error: "bootargs" not defined

This is because its content is set up by the booting scripts that are not executed if we stop the boot. On our system, by carefully reading the U-Boot environment, we can discover that sooner or later, the run mmcargs command is called.

Note

This is because U-Boot automatically executes the script held in the bootcmd variable.

This command is written as follows:

=> print mmcargs
mmcargs=setenv bootargs console=${console} ${optargs} root=${mmcroot} rootfstype
=${mmcrootfstype} ${cmdline}

This is where the kernel command line is built. As a useful exercise, you can now try to understand which are the values used for all the earlier variables; the only thing we wish to add is that we can add our custom settings using the optargs variable.

For instance, if we wish to set the loglevel kernel (that is the lower kernel message priority showed on the serial console, as shown in the Managing the kernel messages section), we can set optargs to this:

=> setenv optargs 'loglevel=8'

And then we ask to continue the boot:

=> boot

Once the system has been restarted, we can verify the new setting by looking into the booting messages of the kernel; we should see a line as shown here:

Kernel command line: console=ttymxc0,115200 loglevel=8 root=/dev/mmcbl k0p1 ro rootfstype=ext4 rootwait

This can be checked by looking at the procfs file, which holds a copy of the kernel command line, that is, the /proc/cmdline file using the following command:

root@wb:~# cat /proc/cmdline
console=ttymxc0,115200 loglevel=8 root=/dev/mmcblk0p1 ro rootfstype=ex t4 rootwait
Tip

More information regarding the kernel command line and its parameters can be found in the kernel tree in the Documentation/kernel-parameters.txt file or online at:  https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt .