Table of Contents
This document is under active development!

If you find errors or omissions in this document, please don’t hesitate to send me a mail.

This journal assumes you are comfortable with a linux environment as most of the published work here uses an Ubuntu distribution as the work station.

Introduction

This journal was started in order to document my exploration of embedded linux using a popular low-cost platform i.e. the BeagleBone Black. The end objective is to become well versed with embedded linux development on an ARM based embedded device.

1. About The Linux Kernel

I’m doing a (free) operating system (just a hobby, won’t be big and professional like gnu) for 386(486) AT clones. This has been brewing since April and is starting to get ready. I’d like any feedback on things people like/dislike in minix, as my OS resembles it somewhat (same physical layout of the file-system (due to practical reasons) among other things).
— Linus Torvald: 25th August 1991

The Linux kernel started off as a hobby project and by a Finnish national Linus Torvalds. It is today the OS powering the largest number of computers on the planet. It is used in phones, tablets, laptops, netbooks, routers, desktops, servers, supercomputers, embedded devices, consumer electronics, etc.

The development of the Linux kernel is carried out by an army of dynamic open source developers, the majority of which comprise of hobbyists. Significant contribution to the kernel is now coming from companies using the kernel. The development is still spearheaded by Linus who is employed by the Linux Foundation.

2. About BeagleBoard

BeagleBoard.org Foundation is a non-profit corporation which promotes open source software and open hardware. It was started by enthusiasts from TI with the aim of providing a powerful platform to design embedded solutions. The BeagleBone Black is their fourth board till date which offers a lot of possibilities in a small credit card size form factor.

3. About Free Electrons

Free Electrons is an engineering company consisting of embedded linux experts who support companies using Embedded Linux. They additionally conduct training workshops for companies on different topics such as Linux kernel and device driver development, Android system development, Yocto Project and OpenEmbedded development, etc. This journal uses the Free Electrons training material which they publish online to explore Embedded Linux with the BeagleBone Black board from TI.

Training and Workbench Setup

This section covers the setup software and hardware used in this journal. The sources of the training material and references are mentioned as well as details about how to procure the hardware.

4. Hardware

4.1. BeagleBone Black

The BeagleBone Black is the newest member in the BeagleBoard family. The board is designed to be a low sized small form factor board. The size of the board is comparable to the size of a credit card and it offers expansion headers to add headers called as capes similar to the Raspberry Pi.

BeagleBoneBlackREV A5A
Beagle Bone Black Revision A5A Board

The table below highlights the key onboard components of the board along with the connectors available on the board. The diagram of the table below is taken from the BeagleBone Black System Reference Manual.

BeagleBoneBlackFeatures
Beagle Bone Black Feature Table

5. Software

5.1. Ubuntu

To work with an embedded system you need a work station on which you can perform the various tasks that are required in the development life cycle. These tasks include:

  1. Editing your build scripts and source code

  2. Cross-compiling your source code for the embedded target

  3. Transferring or accessing the cross-compiled application and libraries to or from the embedded target

  4. Collecting debug information from the target

  5. Communicating with the target remotely using its interfaces like serial, USB, network, etc..

In this document we use the popular Debian based Linux operating system, Ubuntu as our work station for all the tasks listed above. Ubuntu can be easily downloaded and installed on any PC or laptop.

Do not use a virtual machine runnning Ubuntu as your workstation

This document uses Ubuntu 14.04 running on a HP laptop. Use of a similar environment through a virtual machine runnning on VMWare or Oracle VirtualBox is not recommended.

5.2. GIT

The source code management tool used by the Linux kernel community is GIT. To use GIT we need to install the packages required on our work station using the Advanced Packaging Tool(APT) using a command line terminal.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ sudo apt-get install git gitk git-email

Once the packages are successfully installed we will need to configure GIT with some basic information about our name and email address

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ git config --global user.name Conrad Gomes
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ git config --global user.email conrad.s.j.gomes@gmail.com

Further infomation about GIT can be obtained at:
http://git-scm.com/.

5.3. Free Electrons

Since we are going through the training material provided by Free Electrons we’ll need to download their slides and lab data from their website link:
http://free-electrons.com/training/kernel/

As Free Electrons continues to improve on their training material, this journal will be based on the version available at the time of its writing:

The Kernel Source Code

This section covers details about the Linux Kernel source code. We will go through the source code, its structure and characteristics.

6. Offical and Unofficial Kernel Sources

The official source of the Linux Kernel is available at:
https://www.kernel.org/

The sources present in this website do not represent the entire spectrum of features and development that is taking place. Since the kernel is logically divided into sub-systems, each sub-system is maintained by a designated individual who has been involved with the sub-system and is trusted by Linus. So when the merge window opens these individuals who are termed as "maintainers" send pull requests to Linus to take in the patches from their repositories for merging with the mainline kernel tree. In some cases if the subsystem is large it may be divided into smaller subsystems which are managed by individuals designated as "sub-maintainers".

The official development repository for some sub-systems are given below:

  1. MTD
    Website: http://www.linux-mtd.infradead.org/index.html
    GIT: git://git.infradead.org/linux-mtd.git

  2. MIPS
    Website: http://www.linux-mips.org/wiki/Main_Page
    GIT: git://git.linux-mips.org/pub/scm/ralf/linux.git

  3. USB
    Website: http://www.linux-usb.org/
    GIT: git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/patches.git

6.1. Cloning the Linux Tree With GIT

Now that GIT is present in the workstation we can get the main development tree of the Linux kernel as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

And if you’re in a corporarte environment or if your firewall blocks out the network port for git you can use http instead as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ git clone http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

The whole process should take a while so you can go for a small coffee break and come back. Comparitively using git is recommended as it is faster than http

If you happen to have a copy of the Linux GIT repository all you have to do is pull in the latest changes

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cd ~/git/linux
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git checkout master
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git pull

Once you have the Linux GIT repository you can pull the latest changes by by running git pull.

6.2. Using Stable Releases

Typically when we are developing a project we reuse multiple projects to build our application on top of. Similarly since we will be learing about Embedded Linux we cannot use the tip of the tree as it is the latest but not the stablest version of the kernel.

With GIT we don’t have to clone the whole repository all over again. Instead we can add a reference to a remote tree to our existing clone and fetch all the commits which are unique in that repository. As the stable release is derived from the mainline tree we can add a remote to our repository as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git remote -v (1)
origin        git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git (fetch)
origin        git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git (push)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git remote add stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git remote -v
origin        git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git (fetch)
origin        git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git (push)
stable        git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git (fetch) (3)
stable        git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git (push)
1 git remote -v lists the remotes. By default the git repository from which the repository was cloned will be the main remote
2 git remote add adds a new remote with the name stable
3 git remote -v lists the new added remote

The last part is fetching the unique commits in the stable remote. This command should take a while.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git fetch stable

7. Why Are The Sources So Big?

One of the reasons why cloning the kernel sources takes so long is that the Linux Kernel source code is BIG. This is because the Kernel source code contains many subsystems, frameworks, drivers, network protocols and supports many different processor architectures.

7.1. Size Comparison of Different Kernel Source Directories

If we check the disk usage per directory in the Linux Kernel source code we get the distribution below. We’ll go through the type of source code in each of those directories in a later section.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ du -s ./*/ | sort -nr
3084600        ./drivers/
723496        ./net/
589520        ./fs/
275636        ./arch/
260960        ./sound/
84020        ./kernel/
52264        ./security/
38628        ./include/
36340        ./crypto/
28968        ./Documentation/
27616        ./lib/
25984        ./mm/
17768        ./block/
8920        ./firmware/
8440        ./tools/
4356        ./scripts/
3760        ./ipc/
3720        ./init/
2596        ./virt/
248        ./samples/
92        ./usr/

8. Programming Religion

The Linux Kernel is written primarily in C with a little assembly code too. The source code is written in a version of C supported by Gnu Compiler Collection or GCC. Therefore the Linux Kernel source can not be compiled with all C compilers.

The assembly code comprises of small sections of code and is basically the GCC’s "AT&T-style" syntax of target architecture which will run the kernel.

Even though the Linux Kernel has certain frameworks designed with Object Oriented Principles in mind it is not written in C. For further understanding on why C is still not used please see the following link: http://www.tux.org/lkml/#s15-3

And on a lighter note …​

In fact, in Linux we did try C++ once already, back in 1992.

It sucks. Trust me - writing kernel code in C++ is a BLOODY STUPID IDEA.

The fact is, C++ compilers are not trustworthy. They were even worse in 1992, but some fundamental facts haven’t changed:

  • the whole C++ exception handling thing is fundamentally broken. It’s especially broken for kernels.

  • any compiler or language that likes to hide things like memory allocations behind your back just isn’t a good choice for a kernel.

  • you can write object-oriented code (useful for filesystems etc) in C, without the crap that is C++.

In general, I’d say that anybody who designs his kernel modules for C is either (a) looking for problems (b) a C bigot that can’t see what he is writing is really just C anyway (c) was given an assignment in CS class to do so.

Feel free to make up (d).

— Linus Torvalds
19 Jan 2004

9. No C Library

The Linux Kernel is a single program which has its own routines to perform common functions. It does not use any user space library like stdlib, rather it has equivalent functions that enable it to achieve the same results.

In place of the standard C functions like printf(), memset(), malloc() there are functions like printk(), memset(),kmalloc() in the source code.

10. Portability

One of the Linux Kernel key features is portability and hardware support. It supports a wide variety of architectures and to achieve this the source code should be portable across architectures. The architecture specific code is all located in the arch/ directory. The remaining code in all the other directories has to be portable across all architectures.

To achieve portability there are hardware abstraction API for specific features:

  1. Endianess

    • cpu_to_be32()

    • cpu_to_le32()

    • be32_to_cpu()

    • le32_to_cpu()

  2. I/O Memory Access

  3. Memory barriers

  4. DMA API to flush and invalidate caches

Since the Linux Kernel is designed to run on any processor the use of floating point expressions is not allowed. As an example consider the most popular embedded architecture i.e. ARM, it does not have a floating point unit.

11. Linux Internal API

One of the main reasons for having drivers in-tree i.e. present along with the sources of the Linux Kernel is that the internal Linux API may be changed at any point in time and if a change is proposed and implemented the developer responsible for the API change will also have to take the ownership of changing all the modules and drivers which use the changed API. In the case of an out-of-tree driver the work will be owned by the driver owner and any time a change occurs the driver will not compile with the latest kernel source code.

Having said that the Linux Kernel external API i.e. kernel to userspace API like system calls, /proc, /sys does not change and is considered to protect the user space applications who depend on it.

12. Is It Free?

The Linux Kernel is licensed under GNU General Public License version 2. This license defines the Linux Kernel as Free Software as defined by the Free Software Foundation.

  1. If you redistribute the software you have to do so under the same license irrespective of whether it is modified or unmodified.

  2. If you make modifications to the Linux Kernel you have to release it under the same license.

  3. You only have to do so when your device with the kernel start getting distributed

  4. You only have to license it to your customers and not necessarily the whole world.

  5. It is illegal to distribute a binary kernel with statically compiled proprietary drivers.

  6. Proprietary drivers are frowned upon by the Linux Kernel community as it goes against the philosophy of the GPL license.

13. Advantages Of GPL Drivers

  1. It is possible to reuse software from other GPL drivers to write a new GPL driver

  2. A GPL driver has more contributors, testers, reviewers and maintainers thereby making it more robust.

  3. Once the driver is accepted it is easily shipped and distributed by others who are using the Linux Kernel.

  4. A pre-compiled driver will always have to catch up with the latest kernel devlopments leaving users of the driver at a loss as they can’t upgrade their kernel with ease in order to use the latest source with new features

  5. Making a driver GPL compliant avoids any potential legal hastles

14. Advantages Of In-Tree Kernel Drivers

  1. Acceptance of a driver into the mainline kernel is a step that must be done by developers who have developed a GPL compatible driver.

  2. This allows the developer to release the ownership of maintaining the kernel driver to the community. This reduces the cost of maintainence.

  3. The source of the kernel driver is easily accessible by anyone, as the kernel code is widely published.

15. User Space Device Drivers

It is possible to develop a user space device driver. There are several scenarios in which a user space device driver is developed:

  1. The device driver does not depend on any of the frameworks exposed by the Linux Kernel.

  2. The device driver is used by only one application and is not required by any other application.

  3. The kernel provides a simple interface with which the user space device driver can control and read the hardware for which it is developed.

15.1. Examples Of User Space Device Drivers

Certain busses have interfaces exposed by the kernel which can be used to develop a user space device driver if the hardware is connected to that bus:

  1. USB with libusb, http://www.libusb.org/

  2. SPI wiht spidev, Documentation/spi/spidev

  3. I2C with i2cdev, Documentation/i2c/dev-interface

  4. Memory-mapped devices with UIO, including interrupt handling, http://free-electrons.com/kerneldoc/latest/DocBook/uio-howto/

On certain SOCs the vendor also provides a user space device driver along with a kernel driver which has access to other processors in the SOC which are running a firmware for highly specialized applications.

15.2. The Good And The Bad Of User Space Device Drivers

The Good

  1. The driver can be written in any programming language or script.

  2. The driver can be kept proprietary.

  3. The driver runs in user space as an application or daemon.

  4. The driver cannot bring down the kernel.

The Bad

  1. Handling interrupts from the hardware is non-trivial resulting in some sort of polling mechanism.

  2. The interrupt latency is larger when compared to a kernel device driver.

16. What’s In The Sources?

We’ll briefly go through each of the sources in the Linux Source Code and try to get an understanding of the overall structure of the source tree. Each directory is a placeholder for certain code, scripts and files which serve to make up the Linux Kernel project.

arch/<ARCH>

Architecture specific code. All code that has anything to do with the processor the kernel is running on is present in this directory

  • arch/<ARCH>/mach-<machine>, machine/board specific code

  • arch/<ARCH>/include/asm, architecture-specific headers

  • arch/<ARCH>boot/dts, Device Tree source files for certain architecture

block/

Code relate to block device drivers for hard disk drives and others

COPYING

License of the Linux Kernel.

CREDITS

Who Did what?

crypto/

Cryptographic libraries

Documentation/

Documentation for all things about the Linux Kernel

drivers/

Device drivers except for sound which has its own directory below

firmware/

Legacy: firmware images extracted from old drivers

fs/

Source code for various filesystems (ext2/ubifs/etc..)

include/

Kernel headers

include/linux/

Linux Kernel core headers

include/uapi/

User space API headers

init/

Code related to the kernel initaliazation. Includes the main.c

ipc/

Code responsible for allowing inter process communication

Kbuild

Part of the build system

Kconfig

Top level description file for configuration parameters

kernel/

The core of the Linux Kernel

lib/

Useful library routines (crc32…​)

MAINTAINERS

Maintainers of different subsystems of the kernel

Makefile

Top level makefile

mm/

Memory management code

net/

Network support code

README

Overview and building instructions. Read once atleast.

REPORTING-BUGS

Procedure to report bugs with the Linux Kernel

samples/

Sample code of usage of frameworks and kernel code

scripts/

Useful scripts for internal or external use

security/

Support for security features like SELinux

sound/

Sound support code and drivers

tools/

Code for various user space tools

usr/

Code to generate an initramfs cpio archive file

virt/

Virtualization support (KVM)

17. Browsing The Sources

One of the most common tasks required by any developer is the ability to browse a project and search for:

  1. A specific symbol such as a function name or variable name

  2. The calling function of a function

  3. The function definition using a function call point

  4. An include file in the project from its declaration in source code

  5. A pattern of text

17.1. Cscope

One such tool is Cscope which allows us to browse the Linux source code with ease from editors like vim, emacs and also independently using only cscope.

17.2. LXR

This is a generic indexing tool and code browser which is available as a web service. It supports both C and C++ and it makes it easy to search for declarations, definitions and symbols. A good examples of LXR with the Linux Kernel in action is through the Free Electrons LXR Site and further information abouit LXR can be obtained from its sourceforge page.

18. LAB 1 : Getting Accustomed To Browsing The Sources

This is a hands on session taken from the Free Electrons labs with the following objectives
  1. Create a branch based on a remote tree to explore a particular stable kernel version (from the stable kernel tree).

  2. Explore the sources in search for files, function headers or other kinds of information. . .

  3. Browse the kernel sources with tools like cscope and LXR.

18.1. Creating A Branch To A Particular Stable Kernel Version

In order to get the list of branches on our stable remote tree we have to enter the Linux Kernel source tree and use the git branch command as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cd ~/git/linux
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git branch -a
* master        (1)
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/stable/linux-2.6.11.y                (2)
  remotes/stable/linux-2.6.12.y
.
.
  remotes/stable/linux-3.9.y
  remotes/stable/master
1 Our source code is currently pointing to the master branch
2 Remote stable branch remotes/stable/linux-2.6.11.y

We will be working with the 3.13 stable branch and so we will use the remote branch remotes/stable/linux-3.13.y from the list of branches displayed.

Before we do anything let us check the version of our master branch using the top level Makefile in the source code. Using vim or your favourite editor or head examine the first few lines of the Makefile

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ head Makefile
VERSION = 3
PATCHLEVEL = 18
SUBLEVEL = 0
EXTRAVERSION = -rc4
NAME = Diseased Newt
.
.

We can see the version of our master branch is at 3.18.0 -rc4 and the name of the release is "Diseased Newt". Now let us create a local branch starting from the stable remote branch of 3.13.y. The following command uses git checkout to checkout the stable remote branch stable/linux-3.13.y as a local branch with the name 3.13.y.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git checkout -b 3.13.y stable/linux-3.13.y        (1)
Checking out files: 100% (27044/27044), done.
Branch 3.13.y set up to track remote branch linux-3.13.y from stable.
Switched to a new branch '3.13.y'        (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git branch -a        (3)
* 3.13.y        (4)
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
.
.
1 Command to checkout the stable remote branch as a local branch
2 The switch to the new branch takes place successfully
3 We list all the branches again
4 The git repository now points to the 3.13.y local branch

Once again let us examine the first few lines of the top level Makefile. We can now see the version is at 3.13.11 and the name of the release is "One Giant Leap for Frogkind". So we have successfully managed to create a branch pointing to a stable release of the Linux Kernel source code.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ head Makefile
VERSION = 3
PATCHLEVEL = 13
SUBLEVEL = 11
EXTRAVERSION =
NAME = One Giant Leap for Frogkind
.
.

18.2. Searching Tools

There are several tools that can be used to browse the kernel code and search. We will demonstrate the commands used with examples taken from the labs.

18.2.1. Using Find

The find utility can be used to search for a specific file name. The only catch being the name or pattern of the file needs to be known. For instance say you want to locate the logo of Linux in the source code.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ find . -name "*.gif" -o -name "*.jpg" -o -name "*.png" -type f
./Documentation/logo.gif

We use popular file formats to locate pictures in the source code and coincidentally there is one file in the Documentation directory with the name logo.gif.

18.2.2. Using Git-Grep

The git-grep command can be used to search within a git project. For instance if we want to search for the name of the maintainer of MVNETA network driver we would use it as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git grep MVNETA                (1)
MAINTAINERS:MARVELL MVNETA ETHERNET DRIVER                         (2)
arch/arm/configs/mvebu_defconfig:CONFIG_MVNETA=y
drivers/net/ethernet/marvell/Kconfig:     This driver is used by the MV643XX_ETH and MVNETA drivers.
drivers/net/ethernet/marvell/Kconfig:config MVNETA
.
.
.
1 We search for MVNETA with git grep
2 We get the maintainers as MARVELL for MVNETA ETHERNET DRIVER

To get line numbers for the references of the regex being searched we have to set the environment for git. This can be done locally (--local) specific to the git project or globally(--global) for all git projects on the workstation.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git config --local grep.lineNumber true                (1)
1 Enabling line numbers in the search in my local linux git clone

It is possible to search in a specific branch of the project with git-grep. For instance let us try to find the platform_device_register function in all header files in the linux project in the branch remotes/stable/linux-3.7.y

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git grep -e platform_device_register remotes/stable/linux-3.7.y -- '*.h'         (1)
remotes/stable/linux-3.7.y:arch/arm/mach-ux500/devices-common.h:99:     return platform_device_register_full(&pdevinfo);
remotes/stable/linux-3.7.y:arch/arm/mach-ux500/devices-common.h:123:    return platform_device_register_full(&pdevinfo);
remotes/stable/linux-3.7.y:arch/arm/mach-ux500/devices-common.h:140:    platform_device_register_full(&pdevinfo);
remotes/stable/linux-3.7.y:arch/arm/mach-ux500/devices-db8500.h:26:     return platform_device_register_resndata(parent, "nmk-ske-keypad", -1,
remotes/stable/linux-3.7.y:arch/arm/plat-mxc/include/mach/devices-common.h:31:  return platform_device_register_full(&pdevinfo);
remotes/stable/linux-3.7.y:include/linux/platform_device.h:43:extern int platform_device_register(struct platform_device *);                (2)
remotes/stable/linux-3.7.y:include/linux/platform_device.h:69:extern struct platform_device *platform_device_register_full(
remotes/stable/linux-3.7.y:include/linux/platform_device.h:73: * platform_device_register_resndata - add a platform-level device with
remotes/stable/linux-3.7.y:include/linux/platform_device.h:86:static inline struct platform_device *platform_device_register_resndata(
remotes/stable/linux-3.7.y:include/linux/platform_device.h:102: return platform_device_register_full(&pdevinfo);
remotes/stable/linux-3.7.y:include/linux/platform_device.h:106: * platform_device_register_simple - add a platform-level device and its resources
remotes/stable/linux-3.7.y:include/linux/platform_device.h:127:static inline struct platform_device *platform_device_register_simple(
remotes/stable/linux-3.7.y:include/linux/platform_device.h:131: return platform_device_register_resndata(NULL, name, id,
remotes/stable/linux-3.7.y:include/linux/platform_device.h:136: * platform_device_register_data - add a platform-level device with platform-specific data
remotes/stable/linux-3.7.y:include/linux/platform_device.h:151:static inline struct platform_device *platform_device_register_data(
remotes/stable/linux-3.7.y:include/linux/platform_device.h:155: return platform_device_register_resndata(parent, name, id,
1 Expression searches for platform_device_register declaration in remotes/stable/linux-3.7.y
2 The function is declared on line 43 in include/linux/platform_device.h in the branch linux-3.7.y

If we compare it to one of the older stable branches of remotes/stable/linux-2.6.11.y we get fewer header files with reference to the function name.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/git/linux$ git grep -e platform_device_register remotes/stable/linux-2.6.11.y -- '*.h'        (1)
remotes/stable/linux-2.6.11.y:include/asm-ppc/ppc_sys.h:54:/* Update all memory resources by paddr, call before platform_device_register */
remotes/stable/linux-2.6.11.y:include/asm-ppc/ppc_sys.h:58:/* Get platform_data pointer out of platform device, call before platform_device_register */
remotes/stable/linux-2.6.11.y:include/linux/device.h:380:extern int platform_device_register(struct platform_device *);                        (2)
remotes/stable/linux-2.6.11.y:include/linux/device.h:392:extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
1 Expression searches for platform_device_register declaration in remotes/stable/linux-2.6.11.y
2 The function is declared on line 380 in include/linux/platform_device.h in the branch linux-2.6.11.y

18.2.3. Using Linux Cross Reference

We can make use of an automated tool like Linux Cross Reference or LXR as well:

19. Configuring The Kernel

The kernel source code contains code to support many filesystems, device drivers, network protocols, architectures, etc. The source code can be configured to chose which features are required based on the type of applications that will be run in user space.

Additionally the kernel configuration will also support test code that may be run to validate device drivers in the system. For example the MTD system has several kernel modules which can be loaded to validate the implementation of the mtd device driver code for the flash storage in the system.

To support this type of configuration there are a series of Makefiles present in the kernel source code. However to start the configuraton and build we would only be required to work with the top level Makefile.

There are various targets defined in the top level Makefile which can control the configuration, build and installation of the Linux kernel.

To get a sense of the number of targets available we can run make help to see all the targets.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make help | head
Cleaning targets:
  clean                  - Remove most generated files but keep the config and
                    enough build support to build external modules
  mrproper          - Remove all generated files + config + various backup files
  distclean          - mrproper + remove editor backup and patch files

Configuration targets:
  config          - Update current config utilising a line-oriented program
  nconfig         - Update current config utilising a ncurses menu based program
  menuconfig          - Update current config utilising a menu based program
.
.
.

19.1. Kernel Configuration

The process of configuring the Linux Kernel includes modifying the configuration file located at the root of the source code. This file is named .config. The dot at the beginning of the file name indicates that it is a hidden file.

The syntax of this file is in the form of simple key value pairs as shown in the example below:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ head .config        (1)
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 3.12.0-rc7 Kernel Configuration
#
# CONFIG_64BIT is not set                (2)
CONFIG_X86_32=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf32-i386"
CONFIG_ARCH_DEFCONFIG="arch/x86/configs/i386_defconfig"
.
.
.
1 Command to display the first lines of the .config file
2 # is used to comment out key values in the configuration file

An important point to note is that because options have dependencies it is not advisable to edit the .config file by hand. Preferably use the available configuration interfaces.

Graphical Interfaces

make xconfig OR make gconfig

Text/Shell Interfaces

make menuconfig OR make nconfig

It doesn’t make any difference which is used and we can shift between either of the interfaces as they all edit the same .config file.

19.1.1. Kernel Configuration In The System

The configuration of a GNU/Linux distribution is usually present along with the kernel image in the /boot/ directory.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ uname -r                (1)
3.13.0-45-generic                                                        (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ ls -l /boot/config-3.13.0-45-generic         (3)
-rw-r--r-- 1 root root 169818 Jan 14 01:53 /boot/config-3.13.0-45-generic                        (4)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$
1 uname -r is the command to get the kernel running on the system
2 The kernel running is 3.13.0-45-generic
3 Listing the configuration file of this kernel in /boot/
4 The configuration file is config-3.13.0-45-generic

19.2. Configuring Features As Modules

Upon configuring the kernel source and completion of the build we get a single image which represents the kernel and all the features it is configured for. However it is possible to configure some of the features such as device drivers, filesystems, driver tests, etc. as separate entities called kernel modules.

By configuring certain features as modules we are able to keep the size of the kernel to a minimum. Kernel modules can be loaded from user space to support certain applications on execution or on insertion of certain devices into the system buses like USB, PCI, etc..

Therefore in the configuration of certain features it is possible to select if the feature needs to be compiled as a kernel module. All kernel modules will have to be stored in a file system and will have to be loaded into the running kernel by some user space application or script.

An important point to note in choosing if a feature should be compiled as a module is the latency with which the feature needs to be activated from boot of the system. As the kernel module is stored in a filesystem, it will not be loadable until the filesystem is mounted in the kernel.

19.3. Kernel Option Types

When selecting different features and configuring the kernel we come across different types based on the information required to complete the configuration.

bool

true or false to indicate presence or absence of the feature respectively.

tristate

true or false similar to bool option types and also a third state i.e. module to indicate it is a kernel module.

int

If an integer value is required in the configuration of the feature.

hex

If a hexadecimal value is required in the configuration of the feature.

string

If a string value is requried in the configuration of the feature.

19.4. Kernel Option Dependencies

There will be dependencies between different kernel objects. To describe the dependency there are two types:

depends on dependencies

The option that is dependent on another remains invisible until the later is enabled.

select dependencies

The option on selection automatically selects the object on which it depends on in the configuration.

19.5. Graphical Configuration Interface xconfig

The xconfig configuration utitlity which uses Qt is invoked when running make xconfig in the root directory. If we try to invoke it we get the following error:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cd ~/Git/linux
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make xconfig
  CHECK   qt                                                                (1)
* Unable to find the QT4 tool qmake. Trying to use QT3
*
* Unable to find any QT installation. Please make sure that
* the QT4 or QT3 development package is correctly installed and
* either qmake can be found or install pkg-config or set
* the QTDIR environment variable to the correct location.                (2)
*
make[1]: *** No rule to make target `scripts/kconfig/.tmp_qtcheck', needed by `scripts/kconfig/qconf.o'.  Stop.
make: *** [xconfig] Error 2
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$
1 The target rule checks for qt
2 A nice description of what is probably wrong with our Ubuntu distribution

Ok we need to install Qt in our system. The dependencies are libqt4-dev and g++. For older kernel sources the dependencies are libqt3-mt-dev.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ sudo apt-get install libqt4-dev g++        (1)
Reading package lists... Done
Building dependency tree
Reading state information... Done
.
.
.
Setting up libqtwebkit-dev (2.3.2-0ubuntu7) ...
Processing triggers for libc-bin (2.19-0ubuntu6.5) ...
1 Installing the prerequisites

Again we try running make xconfig and see the graphical interface as shown in the screen capture below:

make xconfig screenshot
Screenshot of xconfig interface

It is possible to search for a particular feature using the search interface. This can be invoked with a CTRL + F keyboard combination.

19.6. Graphical Configuration Interface gconfig

Another graphical interface is the gconfig target.This GTK based configuration gives the following error when we invoke it:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make gconfig                (1)
*
* Unable to find the GTK+ installation. Please make sure that
* the GTK+ 2.0 development package is correctly installed...
* You need gtk+-2.0, glib-2.0 and libglade-2.0.                                        (2)
*
make[1]: *** No rule to make target `scripts/kconfig/.tmp_gtkcheck', needed by `scripts/kconfig/gconf.o'.  Stop.
make: *** [gconfig] Error 2
1 We invoke the target gconfig of the root directory makefile
2 A helpful message indicates a missing GTK+ installation in our Ubun

In this case we have to install the debian package libglade2-dev

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ sudo apt-get install libglade2-dev
[sudo] password for conrad:
no talloc stackframe at ../source3/param/loadparm.c:4864, leaking memory
Reading package lists... Done
Building dependency tree
Reading state information... Done
.
.
.

19.7. Text Configuration Interface menuconfig

This configuration interface requires no graphical interface and only requires the libncurses-dev debian package to be installed. This interface is popular with other projects such as Linux Target Image Builder (LTIB), Busybox, OpenWrt, etc.. It works well enough for us to ignore the graphical interfaces. It is brought up using a make menuconfig command in the root directory.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make menuconfig

We get the following screen shot one the interface is invoked from the shell.

make menuconfig screenshot
Screenshot of menuconfig interface

Searching with the menuconfig interface is done by hitting the '/' key similar to vim. Once the search page is displayed we can enter a key word for the search.

make menuconfig search imx
Screenshot of menuconfig search

The results of the search are displayed as follows:

make menuconfig search imx results
Screenshot of menuconfig search results

19.8. Text Configuration Interface nconfig

Another similar test based configuration interface is nconfig with the same dependency on libncurses_dev debian package. Again to invoke the interface we will have to use make nconfig.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make nconfig

We get the following screen shot once the interface is invoked from the shell.

make nconfig screenshot
Screenshot of nconfig interface

19.9. make oldconfig

If we are upgrading to a newer release and use the .config file from an older release of the Linux kernel then we need to run make oldconfig. This will inform us of the configuration settings that are irrelevant in the newer release and if there are newer features or parameters then it will prompt us asking for appropriate values for these settings.

Another scenario in which make oldconfig may come in use is if we modify the .config file by hand.

19.10. Reverting To A Previous Configuration

If we mess up our configuration and build a kernel that is unusable then we can revert to the older configuration that the kernel was built. This is done by copyting the .config.old file which gets created if we use any of the configuration interfaces available. All the interfaces save a copy of the existing configuration file .config as a back up in .config.old

20. Compiling The Kernel

After configuring the kernel with one of the configuration interfaces we can proceed to build the kernel by issuing a make command in the root directory. If blessed with multiple CPU cores then the build can be speed up using a make -j 4 command which instructs make to run 4 jobs in parallel.

After the build the following will be generated:

vmlinux

Raw and uncompressed image which can be used for debugging purposes. This image cannot be booted.

arch/<ARCH>/boot/*Image

The final kernel image which can be booted e.g. bzImage for x86 and zImage for ARM. There may also be compressed images generated.

arch/<ARCH>/boot/dts/*.dtb

Compiled Device Tree files for certain architectures. This will be loaded by the bootloader before the kernel image.

kernel modules(*.ko)

This will be generated in the directory corresponding to the driver/feature for which module type of configuration option was selected.

21. Kernel Installation

A kernel compiled for the host machine on which it is built can be installed in the system by issuing a make install after the build is successful. To install the kernel image we would required root permissions.

The installation includes the following:

/boot/vmlinuz-<version>

The compressed kernel image. This is copied from the arch/<ARCH>/boot directory.

/boot/System.map-<version>

This file stores the kernel symbols along with their addresses and will be handy in the event of a Kernel panic

/boot/config-<version>

This is the configuration file .config saved along with the compiled kernel

The installation may also reconfigured the bootloader to take the new kernel settings so that on the next boot the new kernel will be visible.

21.1. Kernel Module Installation

Along with the kernel the compiled modules will also have to be installed in the system. To achieve this there is a target modules_install which can be executed after executing the install target of the root makefile.

The kernel modules and related files are installed in the /lib/modules/<version>/ directory. If we explore this directory we will see the following:

kernel/

This directory contains a directory structure similar to the kernel source code. The kernel modules will be saved in the same directory structure as the source from which they were built.

modules.alias

Aliases for the modules for loading utilities. An example of the contents of this file is given below:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ head /lib/modules/3.13.0-45-generic/modules.alias
# Aliases extracted from modules themselves.
alias char-major-10-134 apm
alias aes-asm aes_i586
alias aes aes_i586
alias twofish-asm twofish_i586
alias twofish twofish_i586
alias salsa20-asm salsa20_i586
alias salsa20 salsa20_i586
alias serpent serpent_sse2_i586
alias aes aesni_intel
modules.dep

Highlights the dependencies between modules. This will be used by modprobe to choose which kernel modules have to be loaded before loading a particular module. In the example below mce_inject.ko has no dependency and can be loaded without any issue. But twofish-i586.ko depends on twofish_common.ko which must be loaded first.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ head /lib/modules/3.13.0-45-generic/modules.dep
kernel/arch/x86/kernel/cpu/mcheck/mce-inject.ko:
kernel/arch/x86/kernel/msr.ko:
kernel/arch/x86/kernel/cpuid.ko:
kernel/arch/x86/kernel/apm.ko:
kernel/arch/x86/crypto/glue_helper.ko:
kernel/arch/x86/crypto/aes-i586.ko:
kernel/arch/x86/crypto/twofish-i586.ko: kernel/crypto/twofish_common.ko
kernel/arch/x86/crypto/salsa20-i586.ko:
kernel/arch/x86/crypto/serpent-sse2-i586.ko: kernel/crypto/xts.ko kernel/crypto/serpent_generic.ko kernel/crypto/lrw.ko kernel/crypto/gf128mul.ko kernel/arch/x86/crypto/glue_helper.ko kernel/crypto/ablk_helper.ko kernel/crypto/cryptd.ko
kernel/arch/x86/crypto/aesni-intel.ko: kernel/arch/x86/crypto/aes-i586.ko kernel/crypto/xts.ko kernel/crypto/lrw.ko kernel/crypto/gf128mul.ko kernel/crypto/ablk_helper.ko kernel/crypto/cryptd.ko
modules.symbols

Describes the kernel module to which a symbol belongs. It can be useful during debugging of a Kernel panic. For example we can see that cfg80211_report_obss_beacon belongs to the cfg80211 kernel module.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ head /lib/modules/3.13.0-45-generic/modules.symbols
# Aliases for symbols, used by symbol_request().
alias symbol:cfg80211_report_obss_beacon cfg80211
alias symbol:drm_dp_link_train_channel_eq_delay drm_kms_helper
alias symbol:VBoxHost_RTThreadPreemptDisable vboxdrv
alias symbol:__twofish_setkey twofish_common
alias symbol:get_wd_exp_mode_sd bpctl_mod
alias symbol:hsi_register_controller hsi
alias symbol:mlx4_db_free mlx4_core
alias symbol:sdhci_remove_host sdhci
alias symbol:videobuf_dma_init_kernel videobuf_dma_sg

21.2. Cleaning Up

There are several targets that are used to clean up files that have been generated by the configuration and compilation of the Linux kernel source code.

make clean

This will remove all the generated object code files to allow us to rebuilt the kernel.

make mrproper

Remove all the generated files including the configuration file .config. It may be used if we are rebuilding the kernel source code for a different architecture.

make distclean

This target is used to remove editor backup files. It is mainly used when generating patches.

22. Cross-Compiling The Kernel

When we work with embedded targets such as the Beagle Bone Black board we have to compile the kernel for the architecture of that board i.e. ARM. A kernel compiled on our x86 workstation will not execute on the Beagle Bone Black because it is compiled for an x86 architecture.

The process of compiling the kernel on our work station for another architecture is referred to as cross-compilation. The kernel can be compiled on the Beagle Bone Black if it has a toolchain installed on it. However as the processing power of embedded targets is much lower than that of PCs and servers it becomes more efficient to employ a cross-compilation toolchain for embedded development.

A cross-compiler can be recognized by its prefix which indicates the system for which it will compile the source code. For example mips-linux-gcc indicates that the cross-compiler will generate a binary that can be executed on a MIPS based architecture whereas a arm-linux-gnueabi-gcc indicates that the cross-compiler will generate a binary that can be executed on an ARM based architecture.

To specify the architecture for which the kernel source code is to be compiled we have to pass a variable ARCH to the top level makefile. This should map to any of the subdirectories in the arch/ directory of the kernel source code e.g. arm.

To specify the cross compilation toolchain we have to pass the CROSS_COMPILE variable which represents the prefix of the toolchain e.g. mips-linux- or arm-linux-gnueabi-.

22.1. Predefined Configuration Files

Many times when working with embedded boards we don’t set the configuration of a particular board from scratch. It is easier if there is a predefined configuration with which we can start from and there is in most cases. We get a list of predefined configurations from make help which allows us to set the .config to a particular configuration for a specific board.

For instance if we run make viper_defconfig it will overwrite the .config file with the file from arch/arm/configs/viper_defconfig. To get a list of default configurations we can either use make help or the find utility as shown below:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ find . -name *_defconfig |head
./arch/arc/configs/tb10x_defconfig
./arch/arc/configs/fpga_defconfig
./arch/arc/configs/fpga_noramfs_defconfig
./arch/arc/configs/nsimosci_defconfig
./arch/mn10300/configs/asb2364_defconfig
.
.
.
./arch/ia64/configs/sim_defconfig
./arch/ia64/configs/tiger_defconfig
./arch/xtensa/configs/iss_defconfig
./arch/xtensa/configs/s6105_defconfig
./arch/xtensa/configs/common_defconfig

Once we’ve loaded a default configuration which is basically a minimal configuration it’s time to tailor the configuration to our specifications. This will include running one of the configuration interfaces like make menuconfig. We see the settings of the default configuration in the interface.

22.2. Saving Our Default Configuration

We also have the ability to save our configuration once it is tailored, as a default configuration. This can be done by running the command make savedefconfig. Running this target causes make to save the .config file with a name defconfig. We can move the new defconfig file to the architecture configs directory with an appropriate name.

  • mv defconfig arch/<ARCH>/configs/my_favourite_config

Overall the choice of starting from a default minimal configuration lies with the developer. We can also start from scratch but be mindful about selection of the CPU selection and the correct device drivers.

22.3. Device Tree

Most embedded platforms have non-discoverable hardware which exists as part of the SOC. This hardware has to be described to the kernel in the form of code or a special hardware description language called Device Tree.

The Device Tree language is used only recently in certain architectures such as ARM, PowerPC, ARC, etc.. The Device Tree source file is compiled into a binary called Device Tree Blob by a compiler and passed to the kernel through the bootloader. Each board will have its own unique hardware and therefore will have a unique device tree source file.

The Device Tree files will be located in the /arch/<ARCH>/boot/dts/ folder. The bootloader has to have the capability to load the Device Tree Blob and the kernel image before starting execution of the kernel.

22.4. Steps To Build and Install A Kernel

  1. Configure the kernel source code using make menuconfig

  2. Build the kernel using make with <ARCH> and <CROSS_COMPILE> set if building for and embedded platform.

  3. Copy the built image from arch/<ARCH>/boot/.

    1. The image name will depend on the architecture e.g. bzImage, zImage, etc..

  4. Install the kernel image. For an embedded platform do NOT run make install. It is better to simply copy the built kernel image.

    1. The installation can be customized by editing the arch/<ARCH>/boot/install.sh script.

  5. Install the modules. For an embedded platform do NOT run make modules_install without the INSTALL_MOD_PATH=<dir>/ variable. This ensures we don’t mess around with the system kernel modules.

22.5. Using U-Boot

A popular bootloader with embedded platforms is U-Boot. U-Boot works with a format of kernel image called uImage. This is generated from the zImage using a make uImage target execution for ARM. Newer versions of U-Boot also support booting of the ARM based kernel image zImage directly.

Some ARM platforms require the LOADADDR to be passed along with the make uImage target execution. The address associated with this variable represents the physical memory where the image is executed in.

U-Boot also has the ability to load the Device Tree Blob in memory and pass it to the kernel. The boot process follows the steps

  1. Load the kernel zImage or uImage at address X

  2. Load the <board>.dtb file at Y

  3. Boot the kernel with bootz X -Y or bootm X - Y. The - indicates no initramfs.

22.6. Kernel Command Line

The kernel takes in a list of arguments which can affect its behavior at run time. These argument can be hardcoded during the configuration of the kernel source code by setting the CONFIG_CMDLINE option.

The command line argument can also be passed to the kernel at boot up using an environment variable like bootargs in the case of U-Boot.

There are several kernel arguments documented in Documentation/kernel-parameters.txt. We typically set:

  1. root = for the root filesystem

  2. console = for the destination of the kernel messages

23. LAB 2 : Setting Up The Beaglebone Black Board

This is a hands on session taken from the Free Electrons labs with the following objectives
  1. Getting familiar with the BeagleBone Black board

  2. Knowing what technical documentation is available

  3. Installing the lab data from Free Electrons

  4. Access the board through the serial line.

  5. Configure the U-boot bootloader to download files onto the board using trivial file transfer protocol (tftp)

23.1. Getting Comfortable With The Board

At this point we have to get familiar with our board. It is good to go through the features of the board given in section 4.0 of the BeagleBone Black System Reference Manual.

23.1.1. Connectors, LEDs and Switches

CONN REVA5A
Beagle Bone Black Connectors, LEDs and Switches
5V DC Power Connector

The main DC input which accepts 5V power. Suitable for more power hungry applications.

Power Down Button Switch

Signals to the processor to initiate the power down sequence. It is used to power down the board.

Boot Button Switch

Force the a boot from the micro-SD card by removing and reapplying power to the board.

Reset Button Switch

Reset the processor.

USB Client Connector

Mini USB port at the bottom of the board. It can be used to power the board and setup a network connection.

10/100 Ethernet Connector

The LAN interface.

Debug Serial Header Connector

The header interface for the serial port.

MicroSD Slot Connector

Slot at the bottom of the board to insert a micro-SD card.

MicroHDMI Slot Connector

Slot at the bottom of the board to insert a micro-HDMI cable to interface to a display.

USB Host Connector

This can be used to connect a USB device like keyboard, mouse, BT dongle, WiFi dongle, etc..

23.1.2. System Key Components

COMP A5A
BeagleBone Black system key components
Sitara Processor

ARM architecture OMAP System On Chip(SOC) from Texas Instruments. This is XAM3359AZCZ100 for revision A5A to A6A and XAM3359AZCZ100 for revision B and above. Only the A4 revision boards had the AM3352 processor.

512MB DDR3 RAM

Micron 512MB DDR3L or Kingston 512MB DDR3 Dual Data Rate RAM.

TPS65217C PMIC

It is the power management IC which supplies the power rails to the different components on board.

SMSC Ethernet PHY

The physical interface to the ethernet network.

Micron eMMC

This was 2GB till revision B and changed to 4GB in revision C.

HDMI Framer

Provides the HDMI control for and HDMI or DVI-D display with adaptor.

23.2. Downloading The Technical Documentation

Design is not possible without documentation, so we download the documents which will help us in the lab sessions. The following are needed:

Tht System Reference Manual describes the details about the design of the board and is available on this site here
The latest document should be available at: https://github.com/CircuitCo/BeagleBone-Black/blob/master/BBB_SRM.pdf?raw=true.

The datasheet of the TI AM335x SoCs is useful to see the PIN assignments later when we want to configure the pinmux settings and is available on this site here
The original link is at the TI website at: http://www.ti.com/lit/ds/symlink/am3359.pdf

The last document is the Technical Reference Manual(TRM) of the TI AM335x SoCs. At over 4000 pages it describes the internal IP design of the chip. It is available here.
The same document can be retrieved from the TI website at : http://www.ti.com/product/am3359

23.3. Installing The Free Electrons Lab Data

We will be using the lab data available from Free Electrons to setup our BeagleBone Black. First make sure the lab data is downloaded. The lab data which was available at the time of writing this journal is here.

We’ll have to uncompress the file with sudo permissions and change the permissions of the resulting folder. The prime reason being that the package contains system device node files for the NFS root filesystem:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo tar xvJf linux-kernel-labs.tar.xz         (1)
[sudo] password for conrad:
no talloc stackframe at ../source3/param/loadparm.c:4864, leaking memory
linux-kernel-labs/
linux-kernel-labs/src/
linux-kernel-labs/src/patches/
.
.
.
linux-kernel-labs/modules/nfsroot/sbin/route
linux-kernel-labs/modules/nfsroot/sbin/runlevel
linux-kernel-labs/git/
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo chown -R conrad:conrad linux-kernel-labs        (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ ls -l
total 7756
drwxrwxr-x 6 conrad conrad    4096 Mar 22 17:38 linux-kernel-labs        (3)
-rw-rw-r-- 1 conrad conrad 7931316 Mar 22 18:57 linux-kernel-labs.tar.xz
-rw-rw-r-- 1 conrad conrad      87 Mar 22 18:55 Readme.txt
1 Command to untar and decompress the package
2 Command to change the owner and group to the user name in this case conrad
3 Listing of the directory shows the owner and group has been set appropriately

The xz extension of the package indicates that it requires XZ compression utility which if not available on your system can be upgraded as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo apt-get install xz-utils

23.4. Making A Bootable MicroSD Card

Now we deviate slightly from the Free Electrons lab slides and first prepare our board as per the instructions provided in the linux-kernel-labs/bootloader/beaglebone-black/README.txt file. The bootable micro-SD card will automatically format the on board eMMC device.

Take the micro-SD card and insert it into a micro-SD adapter/reader like the one shown in the image below:

sdcard microsd adaptor sd
Micro SD card adapter

This memory card reader/adapter should be inserted into the SD card slot available. If your system has a micro-SD card slot then please use that directly. On checking the kernel logs with dmesg we should be able to identify the card detected in the system. If a micro-SD card slot is available then the system should register it as a /dev/mmcblk0 whereas in this case with a memory card reader we see it as /dev/sdb. The following shows the kernel logs:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ dmesg
.
.
.
[127595.272118] usb 1-2: new high-speed USB device number 6 using ehci-pci
[127595.405640] usb 1-2: New USB device found, idVendor=058f, idProduct=6366
[127595.405650] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[127595.405658] usb 1-2: Product: Mass Storage Device
[127595.405665] usb 1-2: Manufacturer: Generic
[127595.405671] usb 1-2: SerialNumber: 058F63666433
[127595.406226] usb-storage 1-2:1.0: USB Mass Storage device detected
[127595.407830] scsi9 : usb-storage 1-2:1.0
[127596.532963] scsi 9:0:0:0: Direct-Access     Multiple Card  Reader     1.00 PQ: 0 ANSI: 0
[127596.533754] sd 9:0:0:0: Attached scsi generic sg1 type 0
[127598.192274] sd 9:0:0:0: [sdb] 7744512 512-byte logical blocks: (3.96 GB/3.69 GiB) (1)
[127598.193263] sd 9:0:0:0: [sdb] Write Protect is off
[127598.193269] sd 9:0:0:0: [sdb] Mode Sense: 03 00 00 00
[127598.194256] sd 9:0:0:0: [sdb] No Caching mode page found
[127598.194259] sd 9:0:0:0: [sdb] Assuming drive cache: write through
[127598.199023] sd 9:0:0:0: [sdb] No Caching mode page found
[127598.199028] sd 9:0:0:0: [sdb] Assuming drive cache: write through
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ ls -l /dev/sdb         (2)
brw-rw---- 1 root disk 8, 16 Mar 22 21:09 /dev/sdb
1 We see the device attached as sdb
2 The device node has been created successfully as /dev/sdb

We will have to first partition the micro-SD card using the sfdisk utility which is part of the util-linux APT package. This tool helps us to list the partitions of a device, check the sizes of the partitions, check the partitions on a device and re-partition a device. We must be extra careful when we use such a tool as it could also cause damage to our workstation system if we select the wrong device file unintentionally.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ sudo sfdisk --in-order --Linux --unit M /dev/sdb << EOF                (1)
> 1,48,0xE,*
> ,,,-
> EOF
Checking that no-one is using this disk right now ...
BLKRRPART: Device or resource busy                                                (2)

This disk is currently in use - repartitioning is probably a bad idea.
Umount all file systems, and swapoff all swap partitions on this disk.
Use the --no-reread flag to suppress this check.
Use the --force flag to overrule all checks.
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ mount                (3)
/dev/sda1 on / type ext4 (rw,errors=remount-ro)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
none on /sys/fs/cgroup type tmpfs (rw)
none on /sys/fs/fuse/connections type fusectl (rw)
none on /sys/kernel/debug type debugfs (rw)
none on /sys/kernel/security type securityfs (rw)
udev on /dev type devtmpfs (rw,mode=0755)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
none on /run/shm type tmpfs (rw,nosuid,nodev)
none on /run/user type tmpfs (rw,noexec,nosuid,nodev,size=104857600,mode=0755)
none on /sys/fs/pstore type pstore (rw)
rpc_pipefs on /run/rpc_pipefs type rpc_pipefs (rw)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noexec,nosuid,nodev)
systemd on /sys/fs/cgroup/systemd type cgroup (rw,noexec,nosuid,nodev,none,name=systemd)
nfsd on /proc/fs/nfsd type nfsd (rw)
gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,user=conrad)
/dev/sdb1 on /media/conrad/boot type vfat (rw,nosuid,nodev,uid=1000,gid=1000,shortname=mixed,dmask=0077,utf8=1,showexec,flush,uhelper=udisks2) (4)
1 The command to re-partition the /devsdb device with sfdisk. The options --in-order indicates that the partitions are in order in the input. --Linux tells sfdisk to ignore all warnings irrelevant for Linux.
2 The device is apparently busy.
3 We do a mount to check if it is mounted
4 We see that a partition is mounted in our Workstation at /media/conrad/boot

If the micro-SD card is already partitioned and formated it may be auto mounted by our work station. We will have to un-mount all the partitions before we can proceed.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ sudo umount /media/conrad/boot         (1)
[sudo] password for conrad:
no talloc stackframe at ../source3/param/loadparm.c:4864, leaking memory
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ mount                (2)
/dev/sda1 on / type ext4 (rw,errors=remount-ro)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
none on /sys/fs/cgroup type tmpfs (rw)
none on /sys/fs/fuse/connections type fusectl (rw)
none on /sys/kernel/debug type debugfs (rw)
none on /sys/kernel/security type securityfs (rw)
udev on /dev type devtmpfs (rw,mode=0755)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
none on /run/shm type tmpfs (rw,nosuid,nodev)
none on /run/user type tmpfs (rw,noexec,nosuid,nodev,size=104857600,mode=0755)
none on /sys/fs/pstore type pstore (rw)
rpc_pipefs on /run/rpc_pipefs type rpc_pipefs (rw)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noexec,nosuid,nodev)
systemd on /sys/fs/cgroup/systemd type cgroup (rw,noexec,nosuid,nodev,none,name=systemd)
nfsd on /proc/fs/nfsd type nfsd (rw)
gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,user=conrad)
1 We have to unmount the /dev/sdb1 from the mount point i.e. /media/conrad/boot
2 We check to see if anything else is mounted again

Again we attempt to repartition the micro-SD card

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ sudo sfdisk --in-order --Linux --unit M /dev/sdb << EOF
1,48,0xE,*
,,,-
EOF                                                                                        (1)
Checking that no-one is using this disk right now ...                                        (2)
OK

Disk /dev/sdb: 1023 cylinders, 122 heads, 62 sectors/track
Old situation:                                                                                (3)
Units = mebibytes of 1048576 bytes, blocks of 1024 bytes, counting from 0

   Device Boot Start   End    MiB    #blocks   Id  System
/dev/sdb1   *     1     48     48      49152    e  W95 FAT16 (LBA)
/dev/sdb2        49   3780   3732    3821568   83  Linux
/dev/sdb3         0      -      0          0    0  Empty
/dev/sdb4         0      -      0          0    0  Empty
New situation:                                                                                (4)
Units = mebibytes of 1048576 bytes, blocks of 1024 bytes, counting from 0

   Device Boot Start   End    MiB    #blocks   Id  System
/dev/sdb1   *     1     48     48      49152    e  W95 FAT16 (LBA)
/dev/sdb2        49   3780   3732    3821568   83  Linux
/dev/sdb3         0      -      0          0    0  Empty
/dev/sdb4         0      -      0          0    0  Empty
Successfully wrote the new partition table

Re-reading the partition table ...
BLKRRPART: Device or resource busy
The command to re-read the partition table failed.
Run partprobe(8), kpartx(8) or reboot your system now,
before using mkfs
If you created or changed a DOS partition, /dev/foo7, say, then use dd(1)
to zero the first 512 bytes:  dd if=/dev/zero of=/dev/foo7 bs=512 count=1
(See fdisk(8).)
1 The sfdisk utility is invoked supplying the information about the partitions
2 sfdisk checking to see that no one is using the disk
3 The old partition map is displayed first. This will vary based on the history of the micro-SD card
4 The new partition map is displayed. The first partition is a W95 FAT16 one which is 48 MB. This is the first line of input to sfdisk. The remaining has been converted to a Linux partition.

We will have to format the first partition of the disk using the mkfs.vfat partition.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ sudo mkfs.vfat -F 16 /dev/sdb1 -n boot        (1)
[sudo] password for conrad:
no talloc stackframe at ../source3/param/loadparm.c:4864, leaking memory
mkfs.fat 3.0.26 (2014-03-07)
mkfs.fat: warning - lowercase labels might not work properly with DOS or Windows
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ echo $?                        (2)
0
1 mkfs.vfat is run on the partition /dev/sdb1. The label of the partition is set to boot with the -n option and the -F option specifies the type of file allocation tables used (12, 16 or 32 bit).
2 Checks the return value of the command

We now remove and re-insert the micro-SD card into the system to see if it gets detected and automatically mounted. It does and we see that Ubuntu opens up the directory located in /media/conrad/boot.

beagleboneblacktux media boot automounted
The first partition /dev/sdb1 has been automounted successfully at /media/conrad/boot
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria$ mount        (1)
/dev/sda1 on / type ext4 (rw,errors=remount-ro)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
none on /sys/fs/cgroup type tmpfs (rw)
none on /sys/fs/fuse/connections type fusectl (rw)
none on /sys/kernel/debug type debugfs (rw)
none on /sys/kernel/security type securityfs (rw)
udev on /dev type devtmpfs (rw,mode=0755)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
none on /run/shm type tmpfs (rw,nosuid,nodev)
none on /run/user type tmpfs (rw,noexec,nosuid,nodev,size=104857600,mode=0755)
none on /sys/fs/pstore type pstore (rw)
rpc_pipefs on /run/rpc_pipefs type rpc_pipefs (rw)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noexec,nosuid,nodev)
systemd on /sys/fs/cgroup/systemd type cgroup (rw,noexec,nosuid,nodev,none,name=systemd)
nfsd on /proc/fs/nfsd type nfsd (rw)
gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,user=conrad)
/dev/sdb1 on /media/conrad/boot type vfat (rw,nosuid,nodev,uid=1000,gid=1000,shortname=mixed,dmask=0077,utf8=1,showexec,flush,uhelper=udisks2)        (2)
1 We use mount to check explicitly what’s there in the system
2 Our partition has been mounted at /media/conrad/boot

We will finally have to copy the files from the lab data folder to this partition and un mount the device.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs/bootloader/beaglebone-black$ cp am335x-boneblack.dtb MLO MBR u-boot.img MLO.final u-boot.img.final uEnv.txt uImage /media/conrad/boot/                (1)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs/bootloader/beaglebone-black$ umount /media/conrad/boot        (2)
1 Copying the necessary files from the Free Electrons lab data folder which we unpacked earlier
2 Unmounting the mounted partition
beagleboneblacktux bootable microSD contents
The contents of the bootable micro-SD card

We can now safely eject or remove the micro-SD card from the work station.

23.4.1. Source For Binaries

The binaries that are copied can be built from source however we’re not going to do that for now. Instructions to build them are given in the linux-kernel-labs/bootloader/beaglebone-black/README.txt of the lab data downloaded.

23.5. Reflashing The eMMC With The micro-SD Card

The bootable micro-SD card will now be used to reflash the eMMC device to get it ready for the lab session. The process is short but the steps maybe a bit confusing so follow the pictures to nail it down correctly.

23.5.1. Insert The micro-SD Card

This step is self-explanatory. The bootable micro-SD card has to be inserted into the micro-SD card slot on the BeagleBone Black board. The micro-SD card has 8 contacts with a golden hue which are at the bottom of the card. The picture below shows the top of the micro-SD card which is placed in the slot. All that is left is to press it into the slot until a click is felt.

beagleboneblacktux microsd in slot
The micro-SD card is placed in the slot waiting to be fully inserted

23.5.2. Pressing The Boot Switch

After inserting the micro-SD card we have to press the boot switch which is located near the micro-SD card slot as shown in the picture below. Also note that the micro-SD card has been inserted properly into its slot.

beagleboneblacktux boot switch
The boot switch located near the micro-SD card slot
beagleboneblacktux boot switch pressed
The boot switch has to be pressed before applying power

23.5.3. Applying Power

The last step is to apply power i.e. either through the USB connector or power connector. Make sure your power supply is built for 5V 1A output before inserting it into the power supply connector. You can depress the boot switch after 1 second after applying power. On applying power the leds will start blinking. The entire reflashing process takes about 20 to 30 seconds. At the end of the process all 4 leds will be on as shown:

beagleboneblacktux all leds on successful reflash
The reflash operation was successful as all 4 leds are on after 20s

23.5.4. Troubleshooting

In case there is an issue with the process and the 4 leds do not light up after a minute then try again. If it still fails then go through the steps given in the lab data folder i.e. linux-kernel-labs/bootloader/beaglebone-black/README.txt. The procedure given here has been taken from that document. There’s a section on "Fixing issues (if any)" which might help.

23.6. Setting Up Serial Communication With The Board

The debug serial header connector has been descibed in the Getting Comfortable With The Board section and should be easy to locate with the labelled image in that section. It is a 1x6 header. Serial capability is provided by UART0 of the processor. It would be good to read the section on the debug serial header given in the Technical Reference Manual.

The only two signals available are TX and RX on the connector and the levels on these signals is 3.3V. A FTDI USB to serial cable is recommended as this serves to provide a serial port to PCs/Laptops making use of the available USB port. The FTDI chip translates the USB data to serial and vice versa. There are several provided in the elinux.org website link at:
http://elinux.org/Beagleboard:BeagleBone_Black_Serial.

23.6.1. Rhydolabz FTDI USB To Serial Breakout Board

In this journal a breakout board was purchased from Rhydolabz. There are several boards available but one without a 1x6 connector was chosen. All the signals of the FTDI can be exposed by soldering a bergstrip pin-out for advanced users but for our use case GND, RX and TX are provided with an easy to access 4 pin connector. The board is also capable of outputing both 5V and 3.3V. This is controlled by soldering the 3.3V leads at the back of the breakout board. The board can be picked up from:
http://www.rhydolabz.com/index.php?main_page=product_info&cPath=80&products_id=1090

rhydolabz ftdi usb to serial interface module
Rhydolabz breakout board with mini USB cable

23.6.2. Connecting The Breakout Board

The FTDI breakout board comes with a Grove 4 pin Female jumper to 4 pin conversion cable. Each of the cables can be connected to female connectors to be slotted into the serial debug header pins. We need only the GND, RXD and TXD signals from the board. Before connecting the board signals make sure the 3.3V leads are shorted at the bottom of the board. The board from Rhydolabz comes with the 5V lead shorted and must be converted for the Beagle Bone Black.

  1. Connect the GND cable of the FTDI breakout board to pin 1 of the serial header.

  2. Next connect RXD of the FTDI breakout board to pin 5 which is the TX of the serial debug header.

  3. Finally connect TXD of the board to pin 4 which is the RX of the serial debug header.

It is always good to understand the specifications of the connectors whenever interfacing electronic circuits. In this case we know that the BeagleBone Black takes 3.3V from the System Reference Manual. If a different cable is to be used check and see if its connector is compatible with the header. The figure below shows the setup where GND is the orange cable on the right, next RXD is the brown cable followed by the TXD which is the red cable.

beagleboneblacktux rhydolabz ftdi serial debug connection
Rhydolabz breakout board serial connection to BeagleBone Black 1x6 header

Once the connections are in place between the BeagleBone Black serial debug header and the FTDI cable or breakout board then connect the USB cable to the breakout board. The picture shows that the board lights up.

beagleboneblacktux rhydolabz ftdi mini usb connection
Rhydolabz breakout board mini USB connection

The linux kernel running on the workstation should register the new USB device connected. We can probe the kernel logs to see if there is any activity using dmesg.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs$ dmesg
.
.
.
[60269.932101] usb 6-1: new full-speed USB device number 2 using uhci_hcd
[60270.125794] usb 6-1: New USB device found, idVendor=0403, idProduct=6001
[60270.125804] usb 6-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[60270.125812] usb 6-1: Product: FT232R USB UART
[60270.125819] usb 6-1: Manufacturer: FTDI
[60270.125825] usb 6-1: SerialNumber: A602I2CN
[60270.212583] usbcore: registered new interface driver usbserial
[60270.212599] usbcore: registered new interface driver usbserial_generic
[60270.212611] usbserial: USB Serial support registered for generic
[60270.230288] usbcore: registered new interface driver ftdi_sio
[60270.230305] usbserial: USB Serial support registered for FTDI USB Serial Device
[60270.230972] ftdi_sio 6-1:1.0: FTDI USB Serial Device converter detected
[60270.231033] usb 6-1: Detected FT232RL
[60270.231036] usb 6-1: Number of endpoints 2
[60270.231039] usb 6-1: Endpoint 1 MaxPacketSize 64
[60270.231041] usb 6-1: Endpoint 2 MaxPacketSize 64
[60270.231043] usb 6-1: Setting MaxPacketSize 64
[60270.233850] usb 6-1: FTDI USB Serial Device converter now attached to ttyUSB0        (1)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs$ ls -l /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 Mar 25 22:28 /dev/ttyUSB0        (2)
1 The device has been recognized as a tty device and is named ttyUSB0
2 A device node /dev/ttyUSB0 is created in the root filesystem

23.6.3. Accessing The Serial Port With Picocom

We can now access the serial port with a terminal application like picocom. This can be installed as follows:

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs$ sudo apt-get install picocom        (1)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs$ sudo adduser $USER dialout        (2)
1 Installing picocom with apt-get
2 Adding $USER to the dialout group to use picocom without sudo. $USER is set to the username i.e conrad in the above case.

We can now start picocom and connect it to the /dev/ttyUSB0 device which was created earlier. The baud rate is specified with the -b option. The default serial port settings for the board are:

  1. Baud 115,200

  2. Bits 8

  3. Parity N

  4. Stop Bits 1

  5. Handshake None

After starting the picocom application we should be able to see the serial port is opened and the settings should be the default settings. If they are not then try to get the settings by providing options to picocom. Once we get the desired settings we can apply power to the connected BeagleBone Black. At this point we will boot up the board for the first time and should see the serial logs of the bootloader U-Boot. The boot process can be interrupted by hitting any key on the keyboard and allowing us to configure the U-Boot environment.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/techeuphoria/quests/beagleboneblacktux/free_electrons_linux_kernel$ picocom -b 115200 /dev/ttyUSB0 (1)
picocom v1.7

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv
imap is        :
omap is        :
emap is        : crcrlf,delbs,

Terminal ready        (2)
 
U-Boot SPL 2013.10 (Nov 28 2013 - 06:36:11)        (3)
reading args
spl: error reading image args, err - -1
reading u-boot.img
reading u-boot.img


U-Boot 2013.10 (Nov 28 2013 - 06:36:11)

I2C:   ready
DRAM:  512 MiB
WARNING: Caches not enabled
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Using default environment

Net:   <ethaddr> not set. Validating first E-fuse MAC
cpsw, usb_ether
Hit any key to stop autoboot:  0         (4)
U-Boot#
1 We run the picocom application setting the baud rate to 115200 and choosing the device as /dev/ttyUSB0
2 picocom shows that the terminal is ready after printing the serial port settings
3 On application of power to the BeagleBone Black this is the first print from U-Boot
4 We can interrupt U-boot before it tries to boot the kernel by hitting any key

23.6.4. Setting Default Environment Variables For U-Boot

We need to make sure the version of the U-Boot running is 2013.10. We use the command version to get the information.

U-Boot#     version        (1)

U-Boot 2013.10 (Nov 28 2013 - 06:36:11)                (2)
arm-linux-gnueabi-gcc (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3
GNU ld (GNU Binutils for Ubuntu) 2.23.52.20130913
1 version checks the version of U-Boot in the system
2 Our version has to be 2013.10 to save the U-Boot environment to the eMMC storage

Finally we reset the settings of the U-Boot environment to its default values using the command env default -f -a as shown below. Finally saveenv is used to save the environment settings whenever we want to retain it across reboots.

U-Boot# env default -f -a                (1)
## Resetting to default environment
U-Boot# saveenv                         (2)
Saving Environment to MMC...                (3)
Writing to redundant MMC(1)... done
U-Boot#
1 Resetting the environment variables to a default value
2 Saving the environment values
3 The environment values are saved to the eMMC device

23.7. Setting Up Ethernet Communication With The Board

We will now need to setup the board in such a way so as to allow us to download files to its system memory using U-Boot. To do this we will use Trivial File Transfer Protocol (TFTP) through an ethernet cable. Our board will be the TFTP client and we will configure our Ubuntu work station to act as a TFTP server. TFTP can be installed as shown below using APT.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo apt-get install tftpd-hpa

Now we will connect an ethernet cable between our workstation and the BeagleBone Black ethernet connector. If the workstation does not have any more connections then we will have to use a USB ethernet adapter. In this case we have an unused connection so we will connect the cable as shown.

beagleboneblacktux ethernet connection
BeagleBone Black connected to ethernet cable

We next have to configure the network interface on the workstation side. Click on the network manager tasklet on the desktop and select Edit Connections.

beagleboneblacktux ethernet screenshot edit connections
Screen shot of Edit Connections selected in the network manager tasklet

Click on the Add button on the left and then Create.. an ethernet connection.

beagleboneblacktux ethernet screenshot add new connection
Screen shot of adding a new ethernet conneciton

Edit the new ethernet connection by changing its name to BBB. Change the IPV4 settings by selecting the method as manual. And finally add the static address as 192.168.0.1 and netmask as 255.255.255.0. There’s no need to add a gateway but if the cursor is in the textbox enter 0.0.0.0. Save the settings and the interface is set up on the workstation.

beagleboneblacktux ethernet screenshot edit new connection
Screen shot of editing IPV4 settings of the new ethernet connection

Now that we have our workstation ethernet interface configured we can setup networking on U-Boot’s side. We can print the environment to check what the variables are set to by running printenv which will give out the values of all the variables in the U-Boot environment. In our case with the default environment values we see that the IP address and TFTP server IP is not set.

U-Boot#
U-Boot# printenv        (1)
arch=arm
baudrate=115200
board=am335x
board_name=A335BNLT
board_rev=00A6
.
.
.
stderr=serial
stdin=serial
stdout=serial
usbnet_devaddr=90:59:af:49:c8:ef
vendor=ti
ver=U-Boot 2013.10 (Nov 28 2013 - 06:36:11)

Environment size: 3471/131067 bytes
U-Boot# printenv ipaddr                (2)
## Error: "ipaddr" not defined
U-Boot# printenv serverip        (3)
## Error: "serverip" not defined
1 Print all the environment variables
2 Print value of the IP address
3 Print value of TFTP server IP

We will set the server IP to the static IP we assigned to our ethernet interface on our workstation and IP address to a different value as follows:

U-Boot# setenv ipaddr 192.168.0.100        (1)
U-Boot# printenv ipaddr
ipaddr=192.168.0.100
U-Boot# setenv serverip 192.168.0.1        (2)
U-Boot# printenv serverip
serverip=192.168.0.1
U-Boot# saveenv                                (3)
Saving Environment to MMC...
Writing to MMC(1)... done
1 Setting the IP address to 192.168.0.100
2 Setting the server IP to the workstation ethernet interface IP
3 Saving the environment variables

The next step is to test the connection by creating a file called testfile.txt in /var/lib/tftpboot/. We will attempt to download the file through the tftp command in U-Boot.

U-Boot# tftp 0x81000000 testfile.txt        (1)
link up on port 0, speed 100, full duplex
Using cpsw device
TFTP from server 192.168.0.1; our IP address is 192.168.0.100
Filename 'testfile.txt'.
Load address: 0x81000000
Loading: T T T T T T T T
Abort                (2)
1 The tftp command takes a memory address in our BBB system and the file name as parameters.
2 We abort the command with CTRL-C after a while as nothing seems to be happening.

This may happen if the tftp service has not been started properly. So we can restart the service using the following command on the workstation.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo service tftpd-hpa restart
tftpd-hpa stop/waiting
tftpd-hpa start/running, process 12562

Now when we try the tftp command again in the U-Boot command prompt we are able to successfully download the file.

U-Boot# tftp 0x81000000 testfile.txt
link up on port 0, speed 100, full duplex
Using cpsw device
TFTP from server 192.168.0.1; our IP address is 192.168.0.100
Filename 'testfile.txt'.
Load address: 0x81000000
Loading: #
         6.8 KiB/s
done
Bytes transferred = 23 (17 hex)                (1)
U-Boot# md 0x81000000                        (2)
81000000: 67616542 6f42656c 4220656e 6b63616c    BeagleBone Black
81000010: 61776120 fa0a2179 3477dfe5 36c5771d     away!....w4.w.6
81000020: ddfddd53 acb6fdf7 bff3df47 8df4e90d    S.......G.......
81000030: 51bbd157 4bbfd90f 5f723ff2 ae63b73f    W..Q...K.?r_?.c.
81000040: e777dbbd ff1f5774 d43f27c9 bcefd75d    ..w.tW...'?.]...
81000050: fdb7eff5 fd7ff7bd 6c7e5ebc df50576c    .........^~llWP.
81000060: f6fdad24 e24de9dd fdaf3f3f 97cfffbe    $.....M.??......
81000070: fec7fc3d fbed8ff7 bfdd7bbe 57df759c    =........{...u.W
81000080: 7fbbd68d 3cb2b137 a71377cf 1754bdff    ....7..<.w....T.
81000090: ef4cf775 bbee4a85 fe75553d f137bfec    u.L..J..=Uu...7.
810000a0: f9997e33 f5f77735 df4cbffb dd7d4d49    3~..5w....L.IM}.
810000b0: f077f636 b5e5d555 5c7fb5f7 d7f69374    6.w.U......\t...
810000c0: f27f9d5f f1d4fd65 74a5db11 b5b6734d    _...e......tMs..
810000d0: 77d75f6f 3ef5757e d327f1d7 91255fd7    o_.w~u.>..'.._%.
810000e0: f5c47f7f fe5d77fe 445ebfdd ed78dbbf    .....w]...^D..x.
810000f0: 7fb36aff 4fffe7bf d7bd7f5f 9fdffe6d    .j.....O_...m...
U-Boot#
1 The file was successfully transferred this time.
2 We read the contents of the load address at 0x81000000 and see "BeagleBone Black away!" as expected.

We have successfully setup U-Boot to download the kernel.

24. LAB 3 : Cross Compiling The Kernel And Booting It From The Workstation

This is a hands on session taken from the Free Electrons labs with the following objectives
  1. Cross-compile the Linux kernel for the ARM platform

  2. Boot the cross-compiled kernel with a NFS root filesystem, using the workstation as a NFS server

24.1. Enabling Networking To Speed Up Embedded Development

In the previous lab we enabled network connectivity between the board and workstation by configuring the U-Boot environment appropriately. This enables us to build our binaries on the workstation and test it on the board without having to flash the board which saves time as well as prolongs the life of the eMMC device or micro-SD card. We will also enable a NFS type of root filesystem which will allow us to test cross-compiled modules on board.

lab setup implementation
Lab setup implementation

24.2. Installing The Prerequisites

We will need to install a couple of packages before we can proceed with the lab.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs/src$ sudo apt-get install libqt4-dev g++ u-boot-tools        (1)
.
.
.
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs/src$ sudo apt-get install gcc-arm-linux-gnueabi        (2)
.
.
.
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training/linux-kernel-labs/src$ dpkg -L gcc-arm-linux-gnueabi                        (3)
/.
/usr
/usr/bin
/usr/share
/usr/share/doc
/usr/share/man
/usr/share/man/man1
/usr/bin/arm-linux-gnueabi-gcc
/usr/bin/arm-linux-gnueabi-gcov
/usr/share/doc/gcc-arm-linux-gnueabi
/usr/share/man/man1/arm-linux-gnueabi-gcov.1.gz
/usr/share/man/man1/arm-linux-gnueabi-gcc.1.gz
1 libtqy4-dev, g++ are required for make xconfig. u-boot-tools are required for mkuimage
2 gcc-arm-linux-gnueabi is the cross compiler toolchain from Linaro
3 Path and name of the cross-compiler can be determined by examining the cross-compiler installed.

24.3. Configuring The Kernel

We start with configuring the kernel source code with a default configuration. Since our board has a processor(AM335x) which belongs to the OMAP2 family we will use a default configuration file for that board i.e. omap2plus_defconfig.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ find arch/arm/configs/ -iname '*omap*'
arch/arm/configs/omap2plus_defconfig        (1)
arch/arm/configs/omap1_defconfig
arch/arm/configs/da8xx_omapl_defconfig
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- omap2plus_defconfig        (2)
#
# configuration written to .config
#
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$
1 The default configuration for omap2 boards
2 Setting the default configuration to omap2dplus_defconfig

Now configure the kernel to use a NFS root filesystem. This can be done with any of the configuration targets i.e. menuconfig, nconfig, gconfig or xconfig.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
scripts/kconfig/mconf Kconfig
.
.

To identify the configuration option for NFS root filesystem search for CONFIG_ROOT_NFS once the menuconfig screen appears.

beagleboneblacktux search config root nfs screenshot
Searching for CONFIG_ROOT_NFS

We see that CONFIG_ROOT_NFS can be reached through "File systems" and "Network File Systems"

beagleboneblacktux found config root nfs screenshot
CONFIG_ROOT_NFS is under File systems and Network File Systems
beagleboneblacktux config root nfs filesystems select
We select File systems
beagleboneblacktux config root nfs network file systems select
We select Network File Systems
beagleboneblacktux config root nfs root file system on nfs select
We select Root file system on NFS

24.4. Cross-compiling The Kernel

Now it is time to cross-compile the kernel after we have configured it correctly. We will first do a clean on the source code to make sure we’re compiling fresh

24.4.1. Clean The Sources

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- clean
  CLEAN   .
  CLEAN   arch/arm/kernel
  CLEAN   drivers/eisa
  CLEAN   drivers/gpu/drm/radeon
  CLEAN   drivers/net/wan
  CLEAN   drivers/scsi/aic7xxx
  CLEAN   drivers/scsi
  CLEAN   drivers/tty/vt
  CLEAN   drivers/video/logo
  CLEAN   firmware
  CLEAN   kernel/debug/kdb
  CLEAN   kernel
  CLEAN   lib/raid6
  CLEAN   lib
  CLEAN   security/apparmor
  CLEAN   security/selinux
  CLEAN   usr
  CLEAN   arch/arm/boot/compressed
  CLEAN   arch/arm/boot/dts
  CLEAN   arch/arm/boot
  CLEAN   .tmp_versions

We compile the source code by invoking make with no target and by passing ARCH as arm and CROSS_COMPILE as arm-linux-gnueabi-. The compilation process may take a while.

24.4.2. Build zImage

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-                (1)
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  HOSTCC  scripts/basic/fixdep
make[1]: `include/generated/mach-types.h' is up to date.
  CC      kernel/bounds.s
.
.
.
  CC      sound/soc/omap/snd-soc-omap3pandora.mod.o
  LD [M]  sound/soc/omap/snd-soc-omap3pandora.ko
  CC      sound/soc/snd-soc-core.mod.o
  LD [M]  sound/soc/snd-soc-core.ko
  CC      sound/soundcore.mod.o
  LD [M]  sound/soundcore.ko
  CC      sound/usb/snd-usb-audio.mod.o
  LD [M]  sound/usb/snd-usb-audio.ko
  CC      sound/usb/snd-usbmidi-lib.mod.o
  LD [M]  sound/usb/snd-usbmidi-lib.ko
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ ls -l arch/arm/boot/
total 12828
drwxrwxr-x 2 conrad conrad    4096 Nov 12 23:07 bootp
drwxrwxr-x 2 conrad conrad    4096 Mar 30 21:28 compressed
drwxrwxr-x 4 conrad conrad   36864 Mar 30 21:28 dts
-rwxrwxr-x 1 conrad conrad 8843712 Mar 30 21:28 Image
-rw-rw-r-- 1 conrad conrad    1648 Oct 19  2013 install.sh
-rw-rw-r-- 1 conrad conrad    3148 Oct 19  2013 Makefile
-rwxrwxr-x 1 conrad conrad 4229384 Mar 30 21:28 zImage                                        (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ file arch/arm/boot/zImage
arch/arm/boot/zImage: Linux kernel ARM boot executable zImage (little-endian)                (3)
1 The cross-compilation command in the kernel source directory
2 The zImage generated after cross-compilation
3 We check the type of file of zImage using the file command

24.4.3. Build uImage

In order to boot the kernel image with U-Boot we need to convert it into a special format called uImage. The kernel Makefile has a target that can do this and which uses mkimage tool found in the u-boot-tools package. The file will contain the zImage kernel and information about load address of the kernel. The load address will vary based on the platform for the BeagleBone Black board it is 0x80008000.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make LOADADDR=0x80008000 uImage                (1)
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
make[1]: `include/generated/mach-types.h' is up to date.
  CALL    scripts/checksyscalls.sh
  CHK     include/generated/compile.h
  CHK     kernel/config_data.h
  Kernel: arch/arm/boot/Image is ready
  Kernel: arch/arm/boot/zImage is ready
  UIMAGE  arch/arm/boot/uImage
Image Name:   Linux-3.13.11
Created:      Tue Mar 31 21:50:14 2015
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    4229384 Bytes = 4130.26 kB = 4.03 MB
Load Address: 80008000
Entry Point:  80008000
  Image arch/arm/boot/uImage is ready                                (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$
1 Invoking the uImage target with LOADADDR set to 0x80008000
2 The image is located at arch/arm/boot/uImage

24.4.4. Generate The Device Tree Binaries

We also need to generate the Device Tree Binaries (DTBs)

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ make dtbs

24.4.5. Copy To TFTP Server Home Directory

Finally we can copy the uImage and the am335x-boneblack.dtb files to the TFTP server home directory i.e. /var/lib/tftpboot.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ cp arch/arm/boot/dts/am335x-boneblack.dtb /var/lib/tftpboot/.
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/Git/linux$ cp arch/arm/boot/uImage /var/lib/tftpboot/.

24.5. Setting Up The NFS Server

We will now need to setup our workstation as a NFS server. This will require installation of the nfs-kernel-server package.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo apt-get install nfs-kernel-server

Once installed we need to edit the /etc/exports file to configure the server to allow a NFS client to connect to it and mount a directory. In this case we want to configure the NFS server in such a way so as to allow our BeagleBone Black platform to mount the root filesystem which is there in our workstation lab data directory.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ cat /etc/exports                 (1)
# /etc/exports: the access control list for filesystems which may be exported
#                to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/home/conrad/fe-kernel-training/linux-kernel-labs/modules/nfsroot 192.168.0.100(rw,no_root_squash,no_subtree_check)        (2)
1 After editing the file we cat its contents
2 The directory of nfsroot is exported to IP 192.168.0.100 which is our BBB IP

Finally before we boot make sure to restart the NFS server as shown below. If there are any errors then the server will refuse to start and error logs will appear. The /etc/exports file must be revisited and the syntax of the line must be inspected and corrected. Fix the errors and ensure the NFS server restarts before proceeding.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo service nfs-kernel-server restart
 * Stopping NFS kernel daemon                                 [ OK ]
 * Unexporting directories for NFS kernel daemon...     [ OK ]
 * Exporting directories for NFS kernel daemon...       [ OK ]
 * Starting NFS kernel daemon                           [ OK ]
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$

24.6. Boot The System

We will now proceed to boot the system. Make sure the BeagleBone Black is connected to the USB to serial connector correctly as was described in Lab2. Also make sure your etherned cable is connected between the board and your workstation. On powering up the board interrupt the boot sequence so that we have access to the U-Boot console.

24.6.1. Setting The bootargs

We will now change the bootargs environment variable to instruct it to boot the root filesystem from a NFS server and also we will pass the consoled that it is to use. These are standard kernel parameters which can change the way the kernel boots the system.

U-Boot 2013.10 (Nov 28 2013 - 06:36:11)

I2C:   ready
DRAM:  512 MiB
WARNING: Caches not enabled
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Net:   cpsw, usb_ether
Hit any key to stop autoboot:  0
U-Boot#
U-Boot# setenv bootargs root=/dev/nfs rw ip=192.168.0.100 console=ttyO0 nfsroot=192.168.0.1:/home/conrad/fe-kernel-training/linux-kernel-labs/modules/nfsroot        (1)
U-Boot# printenv bootargs
bootargs=root=/dev/nfs rw ip=192.168.0.100 console=ttyO0 nfsroot=192.168.0.1:/home/conrad/fe-kernel-training/linux-kernel-labs/modules/nfsroot
U-Boot#
1 We use setenv to fix the bootargs.

24.6.2. Explanation Of The Bootargs

root=/dev/nfs

We set the root filesystem device as /dev/nfs to indicate that NFS is to be used. rw::The root filesystem should be mounted with read and write capabilities

ip=192.168.0.100

The IP of the BeagleBone Black board should be 192.168.0.100 before mounting the NFS root filesystem

console=ttyO0

The console to be used is serial port 0. The character before the 0 is 'O' as in "OMAP".

nfsroot=192.168.0.1:/home/conrad/fe-kernel-training/linux-kernel-labs/modules/nfsroot

The NFS root filesystem server IP and path of the directory. This is similar to the workstation settings. The IP is the workstation ethernet static IP and the path is the same as that in the /etc/exports.

24.6.3. Download The Kernel Image And Device Tree Binary

The first step is to download the uImage via tftp. Before downloading it is good to restart the TFTP server on the workstation.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ sudo service tftpd-hpa restart
tftpd-hpa stop/waiting
tftpd-hpa start/running, process 4204
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$

Next on the U-Boot console we can download the uImage as we learnt in the previous lab. We download it to address 0x81000000.

U-Boot# tftp 0x81000000 uImage                                (1)
link up on port 0, speed 100, full duplex
Using cpsw device
TFTP from server 192.168.0.1; our IP address is 192.168.0.100
Filename 'uImage'.
Load address: 0x81000000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #############################
         1.3 MiB/s
done
Bytes transferred = 4229448 (408948 hex)                (2)
1 Command to download uImage
2 4229448 bytes successfully transferred.

Next we download the device tree binary for the BeagleBone Black board to address 0x82000000

U-Boot# tftp 0x82000000 am335x-boneblack.dtb                 (1)
link up on port 0, speed 100, full duplex
Using cpsw device
TFTP from server 192.168.0.1; our IP address is 192.168.0.100
Filename 'am335x-boneblack.dtb'.
Load address: 0x82000000
Loading: ##
         485.4 KiB/s
done
Bytes transferred = 17911 (45f7 hex)                        (2)
U-Boot#
1 Command to download am335x-boneblack.dtb
2 17911 bytes successfully transferred.

24.6.4. Boot The Kernel

Finally we can boot the kernel from the U-Boot prompt passing the address of the uImage and the address of the device tree binary. The complete log file is available here. The credentials for the login prompt are username as root and no password.

(1)
U-Boot# bootm 0x81000000 - 0x82000000
## Booting kernel from Legacy Image at 81000000 ...
   Image Name:   Linux-3.13.11
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    4229384 Bytes = 4 MiB
   Load Address: 80008000
   Entry Point:  80008000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 82000000
   Booting using the fdt blob at 0x82000000
   Loading Kernel Image ... OK
   Using Device Tree in place at 82000000, end 820075f6

Starting kernel ...
.
.
(2)
[    5.028652] libphy: 4a101000.mdio:00 - Link is Up - 100/Full
[    5.068073] IP-Config: Guessing netmask 255.255.255.0
[    5.073695] IP-Config: Complete:
[    5.077074]      device=eth0, hwaddr=90:59:af:49:c8:ef, ipaddr=192.168.0.100, mask=255.255.255.0, gw=255.255.255.255
[    5.088130]      host=192.168.0.100, domain=, nis-domain=(none)
[    5.094313]      bootserver=255.255.255.255, rootserver=192.168.0.1, rootpath=
[    5.120355] VFS: Mounted root (nfs filesystem) on device 0:13.
[    5.142936] devtmpfs: mounted
[    5.146640] Freeing unused kernel memory: 384K (c0771000 - c07d1000)
Starting logging: OK
Initializing random number generator... [    5.795678] random: dd urandom read with 56 bits of entropy available
done.
Starting network...
ip: RTNETLINK answers: File exists
Starting dropbear sshd: OK

Welcome to Buildroot
buildroot login: root
#
#
#
#
1 Successfully starting the kernel
2 The NFS root filesystem has been successfully mounted giving us access to a root terminal.

If at all the kernel fails to mount the NFS root filesystem take a look at the /var/log/syslog file.

24.6.5. Automating The Boot System

It is cumbersome to repeat the sequence of U-Boot commands at the console everytime we want to boot our system. We can automate the boot process by changing the bootcmd environment variable of U-Boot. Before doing so we can save the older value of bootcmd in another variable of our choice. Reset the board by pressing the "Reset" switch. Interrupt the U-Boot autoprompt sequence to gain access to the U-Boot terminal.

U-Boot# printenv bootcmd        (1)
bootcmd=run findfdt; run mmcboot;setenv mmcdev 1; setenv bootpart 1:2; run mmcboot;run nandboot;
U-Boot#
U-Boot# setenv bootcmd_orig 'run findfdt; run mmcboot;setenv mmcdev 1; setenv bootpart 1:2; run mmcboot;run nandboot;'        (2)
U-Boot# printenv bootcmd_orig
bootcmd_orig=run findfdt; run mmcboot;setenv mmcdev 1; setenv bootpart 1:2; run mmcboot;run nandboot;
U-Boot#
U-Boot# setenv bootcmd 'tftp 0x81000000 uImage; tftp 0x82000000 am335x-boneblack.dtb; bootm 0x81000000 - 0x82000000'        (3)
U-Boot# printenv bootcmd
bootcmd=tftp 0x81000000 uImage; tftp 0x82000000 am335x-boneblack.dtb; bootm 0x81000000 - 0x82000000
U-Boot# saveenv                        (4)
Saving Environment to MMC...
Writing to MMC(1)... done
1 Checking the value of bootcmd
2 Note the use of single quotes for the value of the command
3 Setting the new value of bootcmd
4 Saving the environment changes

The bootcmd can be changed based on requirements. It can also be changed to boot the uImage from the eMMC flash if it is stored there. As a last step power cycle the board to check if the system boots up as was done previously. If there is an issue in the boot sequence then review the bootcmd carefully.

24.6.6. Halting The Sytem And Disconnecting Picocom

We have to remember that the BeagleBone Black is a development board which may not be as robust as other products. To be on the safer side it is good to shutdown the system correctly. To shutdown the system we can issue the halt command in the terminal.

buildroot login: root
#
#
# halt                                        (1)
The system is going down NOW!
Sent SIGTERM to all processes
Sent SIGKILL to all processes
Requesting system halt
[  202.908940] reboot: System halted        (2)
1 On issuing halt wait for the system to shutdown
2 The last log received

Finally picocom can be disengaged carefully by keying the "espace character" which by default is "C-a" and then by hitting "x".

Linux Kernel Modules

This section covers details about Linux Kernel modules. Some features of the kernel can be loaded as modules after it boots up. We will understand what a module is and when it is used. We will also write, compile and test our very own kernel module.

25. When Do We Use Kernel Modules

In the Configuring Features As Modules section we learnt about Kernel modules and how we can configure certain features of the kernel source code to be compiled as separate binaries that can be loaded into the kernel after it boots up. These modules can also exist independent of the kernel source code in another source path and can be compiled with the headers of the kernel so long as the API used in the module are compatible with the kernel source code.

We use kernel modules when:

  1. We are interested in developing a feature of the kernel independently. This allows us to make changes to the module source code, load and test it without having to reboot the kernel. If the source code were a part of the kernel source code we would have to reboot the kernel everytime we wanted to test the code.

  2. We want to minimize the size of the kernel image. In most cases if the system is a general purpose system then the drivers and features required by certain applications do not have to be present in the kernel from boot. They can be loaded on demand. In embedded systems the kernel is sometimes stored on a limited storage device like NAND flash on a partition. The size of the partition of the kernel will depend on the size of the kernel used for the applications of that system.

  3. We want to minimize the boot time to improve system responsiveness after a reboot. If features that are not required immediately after boot are part of the kernel then their initialization will be add to the boot time. We can delay their initialization using kernel modules.

26. Determining Kernel Module Dependencies

Kernel modules cannot be loaded if the modules they depend on have not been loaded into the kernel. To check the module dependencies of a particular module, read the module information using modinfo. Apart from other useful information about the Kernel module modinfo will also give a list of dependencies in the depends field. We will check the module dependency for one of the kernel modules in our workstation as an example.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ modinfo mac80211        (1)
filename:       /lib/modules/3.13.0-45-generic/kernel/net/mac80211/mac80211.ko
license:        GPL
description:    IEEE 802.11 subsystem
srcversion:     385697223F8285F67C93A06
depends:        cfg80211        (2)
intree:         Y
vermagic:       3.13.0-45-generic SMP mod_unload modversions 686
signer:         Magrathea: Glacier signing key
sig_key:        36:89:BF:48:AF:50:2C:BA:FE:71:E5:C2:5D:6C:55:34:0B:7F:13:FF
sig_hashalgo:   sha512
parm:           max_nullfunc_tries:Maximum nullfunc tx tries before disconnecting (reason 4). (int)
parm:           max_probe_tries:Maximum probe tries before disconnecting (reason 4). (int)
parm:           beacon_loss_count:Number of beacon intervals before we decide beacon was lost. (int)
parm:           probe_wait_ms:Maximum time(ms) to wait for probe response before disconnecting (reason 4). (int)
parm:           ieee80211_default_rc_algo:Default rate control algorithm for mac80211 to use (charp)
1 We use modinfo to check the information about mac80211
2 We see the dependency is module cfg80211

The utility modinfo can be also given the full path file name of a kernel module if it is not loaded into the system

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ modinfo /lib/modules/3.13.0-45-generic/kernel/net/mac80211/mac80211.ko        (1)
filename:       /lib/modules/3.13.0-45-generic/kernel/net/mac80211/mac80211.ko
license:        GPL
description:    IEEE 802.11 subsystem
srcversion:     385697223F8285F67C93A06
depends:        cfg80211
intree:         Y
vermagic:       3.13.0-45-generic SMP mod_unload modversions 686
signer:         Magrathea: Glacier signing key
sig_key:        36:89:BF:48:AF:50:2C:BA:FE:71:E5:C2:5D:6C:55:34:0B:7F:13:FF
sig_hashalgo:   sha512
parm:           max_nullfunc_tries:Maximum nullfunc tx tries before disconnecting (reason 4). (int)
parm:           max_probe_tries:Maximum probe tries before disconnecting (reason 4). (int)
parm:           beacon_loss_count:Number of beacon intervals before we decide beacon was lost. (int)
parm:           probe_wait_ms:Maximum time(ms) to wait for probe response before disconnecting (reason 4). (int)
parm:           ieee80211_default_rc_algo:Default rate control algorithm for mac80211 to use (charp)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$
1 We give the full path file name of the kernel module

The kernel module dependencies are described in the /lib/modules/<kernel-version>/modules.dep file which is generated when running the make modules_install command.

27. Inserting Kernel Modules

To insert a module into the kernel we have to use the insmod command. As a kernel module has the ability to affect the running kernel it requires administrative privileges in order to insert the module. The full path of the kernel module must be given e.g. sudo insmod <module_path>.ko

It may so happen that the kernel module may refuse to load and insmod will exit without giving sufficient details. In such a case we can inspect the kernel logs using dmesg to understand what is failing.

Another method of inserting the module is to use modprobe. With this method all dependent modules are also loaded into the kernel before loading the module required. To do this modprobe determines the dependency tree of the module and loads them in order. The dependent modules are searched in the /lib/modules/<version>/. Based on the module name the corresponding object file name is searched.

To list the modules which are inserted into the kernel we can use the lsmod utility. Another way is to cat the contents of /proc/modules.

28. Removing Kernel Modules

We can remove a module which has been inserted and is live as part of the kernel if no other process or kernel module is using it. To do this we have to use the rmmod utility with administrator privileges e.g. sudo rmmod <module name>.

Another way of removing the kernel module is with modprobe e.g. modprobe -r <module name>. This will attempt to remove the module and all the dependent modules which are no longer needed after removing the module.

29. Passing Parameters To Modules

A kernel module can take parameters before loading it so as to change its behavior during execution. The parameters of a kernel module can be determined with the modinfo utility.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~/fe-kernel-training$ modinfo mac80211        (1)
filename:       /lib/modules/3.13.0-45-generic/kernel/net/mac80211/mac80211.ko
license:        GPL
description:    IEEE 802.11 subsystem
srcversion:     385697223F8285F67C93A06
depends:        cfg80211
intree:         Y
vermagic:       3.13.0-45-generic SMP mod_unload modversions 686
signer:         Magrathea: Glacier signing key
sig_key:        36:89:BF:48:AF:50:2C:BA:FE:71:E5:C2:5D:6C:55:34:0B:7F:13:FF
sig_hashalgo:   sha512
parm:           max_nullfunc_tries:Maximum nullfunc tx tries before disconnecting (reason 4). (int)        (2)
parm:           max_probe_tries:Maximum probe tries before disconnecting (reason 4). (int)                (3)
parm:           beacon_loss_count:Number of beacon intervals before we decide beacon was lost. (int)        (4)
parm:           probe_wait_ms:Maximum time(ms) to wait for probe response before disconnecting (reason 4). (int)        (5)
parm:           ieee80211_default_rc_algo:Default rate control algorithm for mac80211 to use (charp)        (6)
1 We use modinfo to check the information about mac80211
2 Parameter max_nullfunc_tries indicated with the parm field.
3 Parameter max_probe_tries indicated with the parm field.
4 Parameter beacon_loss_count indicated with the parm field.
5 Parameter probe_wait_ms indicated with the parm field.
6 Parameter ieee80211_default_rc_algo indicated with the parm field.

There are several ways of passing parameters to the kernel module:

  1. Through insmod:
    sudo insmod ./snd-intel8x0m index=-2

  2. Through modprobe by setting parameters in /etc/modprobe.conf or any file in /etc/modprobe.d/ as follows:
    options snd-intel8x0m index=-2

  3. Through the kernel command line, when the driver is built as part of the kernel:
    snd-intel8x0m index=-2

    1. snd-intel8x0m is the kernel module name

    2. index is the parameter

    3. -2 is the value

To check the current values of modules already loaded in the kernel check the /sys/module/<name>/parameters directory. Each file in it represents a parameter and its content is the value of that parameter for the running module.

conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ ls /sys/module/snd/parameters/
cards_limit  debug  major  slots                                                (1)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cat /sys/module/snd/parameters/cards_limit
1                                                                                (2)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cat /sys/module/snd/parameters/debug
1                                                                                (3)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cat /sys/module/snd/parameters/major
116                                                                                (4)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$ cat /sys/module/snd/parameters/slots
(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null)                                                (5)
conrad@conrad-HP-Pavilion-dm3-Notebook-PC:~$
1 Listing the parameters cards_limit, debug, major, slots
2 Checking the values passed to the parameter card_limit
3 Checking the values passed to the parameter debug
4 Checking the values passed to the parameter major
5 Checking the values passed to the parameter slots

30. Developing Kernel Modules

We will first take a look at a sample source code of a Linux kernel module.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
(1)

static int __init sample_module_init(void)        (2)
{
        pr_alert("Wassup!\n");
        return 0;
}

static void __exit sample_module_exit(void)        (4)
{
        pr_alert("Cya Later!\n");
}

module_init(sample_module_init);                (3)
module_exit(sample_module_exit);                (5)

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Sample Module");
MODULE_AUTHOR("Newbie");
1 Only headers specific to the Linux kernel, no access to the standard C library
2 Defining an initialization function, returns an int to indicate the status of the initialization when insmod is used to load the module.
3 The macro module_init is used to register the initialization function for the module.
4 Defining a cleanup function for the module.
5 The macro module_exit is used to register the cleanup function for the module.
6 Metadata for the module defining the license type, description and the author.

The source code is in C but it has some special symbols which are not used in user space application programming:

__init

This indicates that the module initializaiton function sample_module_init can be removed after initialization. The function is called only once when the module starts up and thereafter is not used. This optimizes the memory usage of the kernel.

__exit

This indicates that the function is discarded if the module is compiled statically into the kernel or if module unloading is disabled. Its primary use is for loadable kernel modules.

It should be noted that the name of the initialization and cleanup functions can be anything however the module name is often used to form the functions (<modulename>_init()/<modulename_exit()).

30.1. Symbols Exported To Modules

A kernel module can use functions and variables defined in other parts of the Linux kernel source code so long as they are exported. There are two macros that are used to export the symbol names that can be used by other modules:

EXPORT_SYMBOL(symbolname)

Exports a function or variable to all modules

EXPORT_SYMBOL_GPL(symbolname)

Exports a function or variable to only GPL modules

symbols exported to modules
Symbols exported to modules

The diagram illustrates the difference between exporting a symbol with EXPORT_SYMBOL versus EXPORT_SYMBOL_GPL. Symbols exported by EXPORT_SYMBOL_GPL(func3, func4) can be used by only GPL modules whereas symbols exported by EXPORT_SYMBOL can be used by both GPL and Non-GPL modules. A module gets it’s license from the MODULE_LICENSE macro added in it’s source code along with the other metadata.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int __init sample_module_init(void)
.
.
.
MODULE_LICENSE("GPL");                                (1)
MODULE_DESCRIPTION("Sample Module");
MODULE_AUTHOR("Newbie");
1 Module license is GPL

The kernel can only use symbols defined in its source code. It cannot use symbols exported by modules even if they are GPL licensed modules. Symbols exported with EXPORT_SYMBOL_GPL can be used only by GPL licensed modules. Symbols exported with EXPORT_SYMBOL can be used by GPL and Non-GPL licensed modules

30.2. About Module Licenses

The MODULE_LICENSE macro is used to define the license of the module. It is used to restrict the functions that a module can use if it is not a GPL licensed module. GPL licensed modules have access to all exported symbols whereas Non-GPL licensed modules can only access symbols exported with EXPORT_SYMBOL.

Another use for module licenses is to identify issues comming from proprietary modules. Typically kernel developers do not fix issues coming from proprietary modules or drivers which cause kernel crashes and OOPSes.

#ifndef __LICENSE_H
#define __LICENSE_H

static inline int license_is_gpl_compatible(const char *license)
{
        return (strcmp(license, "GPL") == 0
                || strcmp(license, "GPL v2") == 0
                || strcmp(license, "GPL and additional rights") == 0
                || strcmp(license, "Dual BSD/GPL") == 0
                || strcmp(license, "Dual MIT/GPL") == 0
                || strcmp(license, "Dual MPL/GPL") == 0);
}

#endif

GPL compatible licenses are defined in the include/linux/license.h file as shown above. These include GPL, GPL v2, GPL and additional rights, Dual BSD/GPL, Dual MIT/GPL or Dual MPL/GPL. All other licenses are considered as proprietary.

30.3. Compiling Modules Out Of Tree

The source code is maintained separately outside the kernel source code. It is easier to modify the source code, however adapting the module source code to changes in the kernel API means that the maintainer has the onus of keeping it upto date with the changing kernel. One disadvantage is that the module cannot be built statically with the kernel. The following snippet shows the Makefile that can be used to build an out of tree kernel module/driver.

ifneq ($(KERNELRELEASE),)
obj-m := wassup.o
else
KDIR := /path/to/kernel/sources

all:
        $(MAKE) -C $(KDIR) M=$$PWD        (1)
endif
1 Rule for the target all

When compiled out of tree the Makefile does not have KERNELRELEASE defined and therefore KDIR is set to the path to either:

  1. Full kernel directory

  2. Kernel Headers Directory

The kernel Makefile gets invoked as KDIR is set and the rule for the target all will change to the KDIR first and call the corresponding Makefile in that path. Additionally $PWD is passed as an argument value in M to the kernel Makefile. The double $$ is required to pass $PWD as the value to M.

The kernel Makefile knows that it has to compile a module and because there is a value for M it can locate where the module Makefile is present. This time the kernel Makefile calls the module Makefile and the value of KERNELRELEASE is defined. The kernel uses the definition of obj-m to identify the module to be compiled which in our case is wassup.o. In this way the module Makefile given above is invoked twice, the first time from the module directory and second time by the kernel Makefile.

The kernel directory pointed to by KDIR in the module makefile must be configured as there will be differences between different configurations of the kernel. The module compiled against a specific version will only load in that version and configuration of the kernel. If there is a mismatch then insmod/modprobe will crib with an output "Invalid module format".

30.4. Compiling Modules Inside Of The Kernel Tree

The source code is integrated inside the kernel source code. It can be integrated with the configuration and compilation process of the kernel. The module can be built statically with the kernel source code.

To add a new driver or module to the kernel source tree we have to place the source file in the appropriate source directory. For example we’ll take a look at the USB serial navman driver. The source code for the driver/module should be present in a single file. If the driver is really big then it should have its own directory. The source file for the navman driver is in drivers/usb/serial/navman.c

To configure the kernel to include a module or driver we need to add information in the drivers/usb/serial/Kconfig file in the same directory. The snippet below shows that the navman driver has a kernel option type of tristate which means it can be enabled, disabled or configured as a module.

.
.
config USB_SERIAL_NAVMAN
        tristate "USB Navman GPS device"
        help
          To compile this driver as a module, choose M here: the
          module will be called navman.
.
.

Next we take a look at the drivers/usb/serial/Makefile which has a line based on the drivers/usb/serial/Kconfig setting. The name of the configuration field CONFIG_USB_SERIAL_NAVMAN is derived from the KConfig settings. The value of CONFIG_USB_SERIAL_NAVMAN will be based on the configuration applied upon invoking make menuconfig. After configuring the kernel and enabling th module/driver we can build it by running make.

.
.
obj-$(CONFIG_USB_SERIAL_MOS7840)                += mos7840.o
obj-$(CONFIG_USB_SERIAL_NAVMAN)                 += navman.o
obj-$(CONFIG_USB_SERIAL_OMNINET)                += omninet.o
.
.

Further information about the Kernel build process and incorporating newer files to the source can be obtained at:
Documentation/kbuild