Saturday, 3 December 2016

The U-Boot loader

Introduction
      U-Boot is a typical free software project
      Freely available at http://www.denx.de/wiki/U-Boot
      Documentation available at http://www.denx.de/wiki/U-Boot/Documentation
      The latest development source code is available in a Git repository:
      Development and discussions happen around an open mailing-list http://lists.denx.de/pipermail/u-boot/
      Since the end of 2008, it follows a fixed-interval release schedule. Every two months, a new version is released. Versions are named YYYY.MM.

U-Boot configuration
      Get the source code from the website, and uncompress it
      The include/configs/ directory contains one configuration file for each supported board
      It defines the CPU type, the peripherals and their configuration, the memory mapping, the U-Boot features that should be compiled in, etc.
      It is a simple .h file that sets pre-processor constants. See the README file for the documentation of these constants.
      Assuming that your board is already supported by U-Boot, there should be one file corresponding to your board, for example include/configs/igep0020.h
      This file can also be adjusted to add or remove features from U-Boot

U-boot configuration file
      /* CPU configuration */
      #define CONFIG_ARMV7 1
      #define CONFIG_OMAP 1
      #define CONFIG_OMAP34XX 1
      #define CONFIG_OMAP3430 1
      #define CONFIG_OMAP3_IGEP0020 1
      [...]
      /* Memory configuration */
      #define CONFIG_NR_DRAM_BANKS 2
      #define PHYS_SDRAM_1 OMAP34XX_SDRC_CS0
      #define PHYS_SDRAM_1_SIZE (32 << 20)
      #define PHYS_SDRAM_2 OMAP34XX_SDRC_CS1
      [...]
      /* USB configuration */
      #define CONFIG_MUSB_UDC 1
      #define CONFIG_USB_OMAP3 1
      #define CONFIG_TWL4030_USB 1
      [...]

Configuring and compiling U-Boot
      U-Boot must be configured before being compiled
      $make BOARDNAME_config
      Where BOARDNAME is usually the name of the configuration file in include/configs/ without the .h
      Make sure that the cross-compiler is available in PATH
      Compile U-Boot, by specifying the cross-compiler prefix.
      Example, if your cross-compiler executable is arm-linux-gcc:
            make CROSS_COMPILE=arm-linux-
      The result is a u-boot.bin file, which is the U-Boot image

Installing the U-Boot
      U-Boot must usually be installed in flash memory to be executed by the hardware. Depending on the hardware, the installation of U-Boot is done in a different way:
     The CPU provides some kind of specific boot monitor with which you can communicate through serial port or USB using a specific protocol
     If U-Boot is already installed,  then it can be used to flash a new version of U-Boot. However, be careful: if the new version of U-Boot doesn't work, the board is unusable
     The board provides a JTAG interface, which allows to write to the flash memory remotely, without any system running on the board. It also allows to rescue a board if the bootloader doesn't work.

U-boot prompt
      Connect the target to the host through a serial console.
      Power-up the board. On the serial console, you will see something like:
U-Boot 2010.06-2 (May 13 2011 - 12:13:22)
OMAP3630/3730-GP ES2.0, CPU-OPP2, L3-165MHz
IGEP v2 board + LPDDR/ONENAND
I2C: ready
DRAM: 512 MiB
Muxed OneNAND(DDP) 512MB 1.8V 16-bit (0x58)
OneNAND version = 0x0031
Chip support all block unlock
OneNAND: 512 MiB
OneNAND: Read environment from 0x00200000
In: serial
[...]
Net: smc911x-0
U-Boot #
      The U-Boot shell offers a set of commands. We will study the most important ones, see the documentation for a complete reference or the “help” command.

Information commands
      Flash information (NOR and SPI ash)
U-Boot> flinfo
DataFlash:AT45DB021
Nb pages: 1024
Page Size: 264
Size= 270336 bytes
Logical address: 0xC0000000
Area 0: C0000000 to C0001FFF (RO) Bootstrap
Area 1: C0002000 to C0003FFF Environment
Area 2: C0004000 to C0041FFF (RO) U-Boot
      NAND ash information
U-Boot> nand info
Device 0: NAND 256MiB 3,3V 8-bit, sector size 128 KiB
      Version details
U-Boot> version
U-Boot 2009.08 (Nov 15 2009 - 14:48:35)
      Those details will vary from one board to the other (according to the U-Boot configuration and hardware devices)
      The exact set of commands depends on the U-Boot conguration
      help and help command
      Load
      “boot”, runs the default boot command, stored in bootcmd
      “bootm <address>” , starts a kernel image loaded at the given address in RAM
      ext2load, loads a file from an ext2 filesystem to RAM
      tftp, loads a file from the network to RAM
      ping, to test the network
      “nand”, to erase, read and write contents to the NAND
      “erase, protect, cp”, to erase, modify protection and write to a NOR flash
      md, displays memory contents. Can be useful to check the contents loaded in memory, or to look at hardware registers.
      mm, modifies memory contents.

Environment variable commands
      U-Boot can be configured through environment variables, which affect the behaviour of the different commands.
      Environment variables are loaded from Flash to RAM at U-Boot startup, can be modified and saved back to Flash for persistence
      There is a dedicated location in Flash to store U-Boot environment, defined in the board configuration file
      Commands to manipulate environment variables:
      printenv, shows all variables
      printenv <variable-name>, shows the value of one variable
      setenv <variable-name> <variable- value>, changes the value of a variable, only in RAM
      saveenv, saves to Flash the current state of the environment
Sample output of printenv
u-boot # printenv
baudrate=19200
ethaddr=00:40:95:36:35:33
netmask=255.255.255.0
ipaddr=10.0.0.11
serverip=10.0.0.1
stdin=serial
stdout=serial
stderr=serial
u-boot # printenv serverip
serverip=10.0.0.2
u-boot # setenv serverip 10.0.0.100
u-boot # saveenv

Important u-boot env variables
      bootcmd, contains the command that U-Boot will automatically execute at boot time after a configurable delay, if the process is not interrupted
      bootargs, contains the arguments passed to the Linux kernel, covered later
      serverip, the IP address of the server that U-Boot will contact for network related commands
      ipaddr, the IP address that U-Boot will use
      netmask, the network mask to contact the server
      ethaddr, the MAC address, can only be set once
      bootdelay, the delay in seconds before which U-Boot runs bootcmd
      autostart, if yes, U-Boot starts automatically an image that has been loaded into memory

Transferring files to the target
      U-Boot is mostly used to load and boot a kernel image, but it also allows to change the kernel image and the root filesystem stored in flash.
      Files must be exchanged between the target and the development workstation. This is possible:
     Through the network if the target has an Ethernet connection, and U-Boot contains a driver for the Ethernet chip. This is the fastest and most efficient solution.
     Through a USB key, if U-Boot support the USB controller of  your platform
     Through a SD or microSD card, if U-Boot supports the MMC controller of your platform
      Through the serial port

Tftp  
      Network transfer from the development workstation and U-Boot on the target takes place through TFTP
     Trivial File Transfer Protocol
     Somewhat similar to FTP, but without authentication and over UDP
      A TFTP server is needed on the development workstation
      sudo apt-get install tftpd-hpa
      All files in /var/lib/tftpboot are then visible through TFTP
      A TFTP client is available in the tftp-hpa package, for testing
      A TFTP client is integrated into U-Boot
     Congure the ipaddr and serverip environment variables
     Use tftp <address> <filename> to load a file


Kernel Building

Building the kernel
      The kernel build system (termed as kbuild) is bundled along with the kernel sources.
      The kbuild system is based on the GNU make, hence all the commands are given to “make”
      The kernel build procedure is divided into four steps:
      Setting up cross-development environment
      Configuration process
      Building the object files and linking them to make the kernel image
      Building dynamic loadable modules
      Setting up cross-development environment
      As linux supports many architectures, the kbuild procedure should be configured for the architecture for which the kernel image and modules are being built.

Configuration process
      This is the component selection procedure
      The list of what software needs to go to into the kernel and what can be compiled as modules can be specified using this step.
      At the end of this step, kbuild records this information in a set of known files, so that the rest of kbuild is aware of the selected components.
      Component selection objects are normally
     Processor selection
     Board selection
     Driver selection
     Some generic kernel options

Front ends to the configuration procedure
      make config
     This is a complicated way of configuring because this throws the component selection on your terminal and its time consuming
      make menuconfig
     This is the curses-based front end to the kbuild procedure
     This is useful on hosts that do not have accesses to a graphics display
      make xconfig
     Graphical front end
      make oldconfig
     This option allows the build to retain defaults from an existing configuration and prompt only for new changes.

Building the object files and linking them to make the kernel image
      Once component selection is done, the next step to build the kernel image
      The name of kernel image is “vmlinux” and is the output if you just type “make” command.
      Additional post processing steps are needed such as compressing the kernel image, adding bootstrapping code and so on.
      The post processing actually creates the image that can be used on the target

Building dynamically loadable modules
      The command “make modules” will do the job of building the kernel modules.

Understanding the build procedure
      Top-level Makefile
     This makefile in kernel sources is responsible for building both the kernel image and the modules.
     It does so by recursively descending into the subdirectories of the kernel source tree
     The list of subdirectories that need to be entered into depends on the component selection
      Every architecture (the processor port) needs to export a list of components for selection during the configuration process.
     Any processor flavor. If your architecture is defined as ARM, then need to define the ARM flavor
     The hardware board
     Any board specific hardware configuration
     Kernel subsystem components, more or less remain uniform across all architectures
      Each architecture needs to export an architecture-specific Makefile. The list of build information is unique to every architecture
     The subdirectories that need to be visited for building the kernel
     The flags that needs to be passed to various tools
     The post processing steps once the image is built
     These are supplied in the architecture specific Makefile in the arch/$(ARCH) subdirectory

Configuration process
      Every kernel subsection defines the rules for configuration in a separate files, “Kconfig”
      A configuration item is stored as a name=value pair.
      The name of configuration item starts with a CONFIG_ prefix. The rest of the name is the component name defined in the configuration file.
      The following values the configuration variable can have:
     Bool : configuration variable can have value “y” or “n”
     Tristate:  can have “y”, “n” or “m” (for module)
     String: Any ASCII string
     Integer: Any decimal number
      User to prompt for assigning value to this variable else default value is assigned to this variable.
      Dependencies can be created while defining the variable
      Each configuration can have Help text
      The kbuild exports the list of selected components by creating the “.config” file in top directory kernel sources.
      This file contains the list of selected components in name = value format.
      While evaluating a source file as a built candidate, the component value field is used to find out if the component should be built as mofule or directly linked to kernel.
      Lets assume for e.g in drivers/net directory a configuration variable CONFIG_SAMPLE=y
      In drivers/net/Makefile there will be line
            obj-$(CONFIG_SAMPLE)+= sample.o
      When this makefile is encounted, the kbuild will transfer it into
            obj-y=sample.o, because .config file has defined CONFIG_SAMPLE=y
      The kernel build has a rule to build obj-y, hence this file is chosen to be built into the kernel
      Obj-m=sample.o, the rule to build the kernem modules.

Make config
make config
scripts/kconfig/conf arch/i386/Kconfig
*
* Linux Kernel Configuration
*
* Code maturity level options
*
Prompt for development and/or incomplete code/drivers (EXPERIMENTAL) [Y/n/?]
Y
*
* General setup
*

Default configuration options
      The kernel contains more than 2000 options, therefore the configuration can be very time consuming, an alternative is “default configuration”
      Every kernel comes with the “default” configuration.
      This configuration is loosely based on the defaults that the kernel maintainer of that architecture feels are the best options to be used. In some cases, it is merely the configuration that is used by the kernel maintainer himself for his personal machines. This is true for the i386 architecture, where the default kernel configuration matches closely what Linus Torvalds uses for his main development machine.
      $ make defconfig
      A huge number of configuration options will scroll quickly by the screen, and a “.config” file will be written out and placed in the kernel directory. The kernel is
      now successfully configured, but it should be customized to your machine in order to make sure it will operate correctly.

Building the kernel
      Now that you have created a kernel configuration that you wish to use, you need to build the kernel.
      $ make
CHK include/linux/version.h
UPD include/linux/version.h
SYMLINK include/asm -> include/asm-i386
SPLIT include/linux/autoconf.h -> include/config/*
CC arch/i386/kernel/asm-offsets.s
GEN include/asm-i386/asm-offsets.h
CC scripts/mod/empty.o
HOSTCC scripts/mod/mk_elfconfig
MKELF scripts/mod/elfconfig.h
HOSTCC scripts/mod/file2alias.o
HOSTCC scripts/mod/modpost.o
HOSTCC scripts/mod/sumversion.o
HOSTLD scripts/mod/modpost
HOSTCC scripts/kallsyms
HOSTCC scripts/conmakehash
HOSTCC scripts/bin2c
CC init/main.o

Building portion of the kernel
      When doing kernel development, sometimes you wish to build only a specific subdirectory or a single file within the whole kernel tree.
      $ make M=drivers/usb/serial
      Source in one place and output in another
      “O” option to make to put all compiled code into the separate output directory
      $make O=`pwd`/output_dir

Building and installing modules
      If you have built any modules and want to use use this method to install a kernel,
      first enter: # make modules_install
      This will install all the modules that you have built and place them in the proper location in the filesystemfor the new kernel to properly find. Modules are placed in the /lib/modules/kernel_version directory, where kernel_version is the kernel version of the new kernel you have just built.
      After the modules have been successfully installed, the main kernel image must be
      installed:
      # make install
This will kick off the following process:
1. The kernel build system will verify that the kernel has been successfully built properly.
2. The build system will install the static kernel portion into the /boot directory and name this executable file based on the kernel version of the built kernel.
3. Any needed initial ramdisk images will be automatically created, using the modules that have just been installed during the modules_install phase.
4. The boot loader program will be properly notified that a new kernel is present, and it will be added to the appropriate menu so the user can select it the next time the machine is booted.
5. After this is finished, the kernel is successfully installed, and you can safely reboot and try out your new kernel image. Note that this installation does not overwrite any older kernel images, so if there is a problem with your new kernel image, the old kernel can be selected at boot time.