Wednesday 24 October 2018

Linux Kernel Cross Compilation

Introduction

There are several reasons to cross-compile the Linux kernel:
  1. Compiling on a native CPU and hardware requires a wide range of devices, which is not so practical. Furthermore, the actual hardware is often not suitable for the workload of kernel compilation due to a slow CPU, small memory, etc.
  2. Hardware emulation (e.g., with qemu) can be a viable substitution if the slowness can be tolerated. However, how to setup the compilation environment is another challenge, as it requires at least a preferably Linux-flavored OS, the bash and make ecosystem, and most importantly, the toolchain.
  3. Cross-compiling on a powerful host enables quick detection of kernel compile errors and config errors, as shown in the stable queue builds project. Coupled with emulation, testing on non-native architectures becomes easier as well.
For my personal use, I would like to see the kernel build process on each architecture, capture the compilation flags of each files, collect their linking information, and finally see if I can mine some insights out of it. In this case, given the complexity of parsing the Kconfig and Makefile, probably the best way is to actually build the kernel and dump the verbose version of the build log.
Fortunately, with a modern Linux release (like Ubuntu or Fedora), all we need to cross-compile the kernel is a toolchain that can produce binaries for another CPU architecture.

Toolchain

On cross-compiling the kernel, we only need two things: binutils, and gcc, and there are generally two ways to obtain them:
  1. install pre-compiled packages
  2. build from source
Installing pre-compiled packages can be a hassle-free way if you don't want to get your hands dirty. In fact, for the following architectures: arm, aarch64, hppa, hppa64, m68k, mips, mips64, powerpc, powerpc64, s390x, sh4, sparc64, you can directly obtain them via apt-get from the official repositories. For example,
apt-get install binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu
Toolchains for other architectures such as blackfin, c6x, and tile can be obtained from the Fedora repo and converted to .deb packages as shown in the tutorial in 2013.
However, in case you want to compile the toolchain from source, for whatever reasons you might have, such as using the latest version or a customized version of gcc, you might follow the following steps.
As a head up, I am running a standard Ubuntu 16.04.3 LTS release with kernel version 4.4.0. The host gcc and binutils (i.e., the gcc and binutils used to build the toolchain) are the default ones with the Ubuntu release, which is in version 5.4.0 and 2.27 respectively. However, I don't see major obstacles in applying it to other combinations of OS, gcc, and binutils versions as long as they are recent enough to build the toolchain.
Before diving into the building process, let's setup some environment variables. Part of them are for convenience reasons, while others are necessary in the build process.
export TARGET=aarch64-unknown-linux-gnu     # replace with your intended target
export PREFIX="$HOME/opt/cross/aarch64"     # replace with your intended path
export PATH="$PREFIX/bin:$PATH"
The TARGET variable should be a target triplet which is used by the autoconf system. Their valid values can be found in the config.guess script.
The last step is necessary. By adding the installation prefix to the the PATH of the current shell session, we ensure the gcc is able to detect our new binutils once we have built them.

binutils

The source code for binutils can be obtained from the GNU servers:
# for stable releases
BINUTILS_VERSION=2.29.1
wget ftp://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.xz

# for development branch
git clone git://sourceware.org/git/binutils-gdb.git
After that, modify the source code as you wish and build it with the following steps.
cd $BINUTILS_BUILD

$BINUTILS_SOURCE/configure \
  --target=$TARGET \
  --prefix=$PREFIX \
  --with-sysroot \
  --disable-nls \
  --disable-werror

make
make install
Since we have multiple targets to build, it is better to have a separate build directory for each target.
--disable-nls tells binutils not to include native language support. This is optional, but reduces dependencies and compile time.
--with-sysroot tells binutils to enable sysroot support in the cross-compiler by pointing it to a default empty directory. By default the linker refuses to use sysroots for no good technical reason, while gcc is able to handle both cases at runtime.
--disable-werror behaves exactly as the name suggests, do not add -Werror in the flag.
After installation, the binaries aarch64-unknown-linux-gnu-{as/ar/ld/...} should exist in $PATH.
These instructions apply to binutils version 2.29.1, which is the latest release at the time of writing.

gcc

Similar to binutils. the source code for gcc can be obtained from the GNU servers too:
# for stable releases
GCC_VERSION=7.2.0
wget ftp://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.xz

# for development branch
git clone git://gcc.gnu.org/git/gcc.git
After that, modify the source code as you wish and build it with the following steps.
cd $GCC_SOURCE
./contrib/download_prerequisites

cd $GCC_BUILD
$GCC_SOURCE/configure \
  --target=$TARGET \
  --prefix=$PREFIX \
  --enable-languages=c,c++ \
  --without-headers \
  --disable-nls \
  --disable-shared \
  --disable-decimal-float \
  --disable-threads \
  --disable-libmudflap \
  --disable-libssp \
  --disable-libgomp \
  --disable-libquadmath \
  --disable-libatomic \
  --disable-libmpx \
  --disable-libcc1

make all-gcc
make install-gcc 
Downloading the pre-requisites (gmp, mpfr, mpc, isl) areis necessary as gcc needs these packages for compilation. This is handled seamlessly with the download_prerequisites script.
--enable-languages=c,c++ tells gcc not to build frontends for other languages like Fortran, Java, etc.
--without-headers tells gcc not to rely on any C library being present for the target.
--disable-<package> tells gcc not to build those packages as they will not be needed in kernel compilation.
More importantly, we should not simply make all as that would build way too much (for example, the libgcc, libc, libstdc++, etc), which are not needed at all. All we need is the compiler itself which can be built by make all-gcc.
Another important note is that in order for gcc to lookup the correct set of binutils, both $TARGET and $PREFIX must be exactly the same when configuring binutils and gcc. For example, aarch64-unknown-linux-gnu-gcc will lookup aarch64-unknown-linux-gnu-as in the same directory: merely putting aarch64-unknown-linux-gnu-as in $PATHis not enough.
The instructions apply to gcc version 7.2.0, which is the latest release at the time of writing.
After the whole process, the following files should present in the $PREFIX/bin directory, and also in the $PATH.
aarch64-unknown-linux-gnu-addr2line
aarch64-unknown-linux-gnu-ar
aarch64-unknown-linux-gnu-as
aarch64-unknown-linux-gnu-c++
aarch64-unknown-linux-gnu-c++filt
aarch64-unknown-linux-gnu-cpp
aarch64-unknown-linux-gnu-elfedit
aarch64-unknown-linux-gnu-g++
aarch64-unknown-linux-gnu-gcc
aarch64-unknown-linux-gnu-gcc-7.2.0
aarch64-unknown-linux-gnu-gcc-ar
aarch64-unknown-linux-gnu-gcc-nm
aarch64-unknown-linux-gnu-gcc-ranlib
aarch64-unknown-linux-gnu-gcov
aarch64-unknown-linux-gnu-gcov-dump
aarch64-unknown-linux-gnu-gcov-tool
aarch64-unknown-linux-gnu-gprof
aarch64-unknown-linux-gnu-ld
aarch64-unknown-linux-gnu-ld.bfd
aarch64-unknown-linux-gnu-nm
aarch64-unknown-linux-gnu-objcopy
aarch64-unknown-linux-gnu-objdump
aarch64-unknown-linux-gnu-ranlib
aarch64-unknown-linux-gnu-readelf
aarch64-unknown-linux-gnu-size
aarch64-unknown-linux-gnu-strings
aarch64-unknown-linux-gnu-strip

Kernel Build

With the toolchain ready, cross-compiling the kernel involves two extra steps:
  1. Find the architecture name ($KERNEL_ARCH) in kernel source tree
    • they are typically Located in arch/* in the kernel source tree.
  2. Find the configuration ($KERNEL_CONF) for each sub-arch (e.g., 32-bit vs 64-bit, big endian vs little endian, etc)
    • make ARCH=$KERNEL_ARCH help will show some hints.
    • if there is no specific machine config, the defconfig should work.
    • if there are available machine configs, choosing from an existing config seems to be more hassle free compared with doing a manual menuconfig.
Once we find the values for $KERNEL_ARCH and $KERNEL_CONF, cross-compiling the kernel is as easy as the following:
cd $KERNEL_SOURCE
make ARCH=$KERNEL_ARCH O=$KERNEL_BUILD $KERNEL_CONF

cd $KERNEL_BUILD
make ARCH=$KERNEL_ARCH CROSS_COMPILE=$TARGET- V=1 vmlinux modules
For example, in the case of building the aarch64 kernel, the command should look like the following:
cd $KERNEL_SOURCE
make ARCH=arm64 O=$KERNEL_BUILD defconfig

cd $KERNEL_BUILD
make ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- V=1 vmlinux modules
Of course we can always speed up the build process with the make -j flags.
The instructions apply to Linux kernel version 4.13.5, which is the latest release at the time of writing.

Results

In total, I have currently cross-compiled the kernel for 7 architectures and 12 sub-archs, with the procedure described above. The result is summarized in the following table:
Architecture Name $TARGET $KERNEL_ARCH $KERNEL_CONF
x86 (32-bit) i386 i386-pc-linux-gnu x86 i386_defconfig
x86 (64-bit) x86_64 x86_64-pc-linux-gnu x86 x86_64_defconfig
arm (32-bit) arm armv7-unknown-linux-gnueabi arm multi_v7_defconfig
arm (64-bit) aarch64 aarch64-unknown-linux-gnu arm64 defconfig
powerpc (32-bit) ppc powerpcle-unknown-linux-gnu powerpc pmac32_defconfig
powerpc (64-bit) ppc64 powerpc64le-unknown-linux-gnu powerpc ppc64le_defconfig
sparc (32-bit) sparc sparc-unknown-linux-gnu sparc sparc32_defconfig
sparc (64-bit) sparc64 sparc64-unknown-linux-gnu sparc sparc64_defconfig
mips (32-bit) mips mips-unknown-linux-gnu mips 32r6_defconfig
mips (64-bit) mips64 mips64-unknown-linux-gnu mips 64r6_defconfig
s390x (64-bit) s390x s390x-ibm-linux-gnu s390 default_defconfig
ia64 (64-bit) ia64 ia64-unknown-linux-gnu ia64 generic_defconfig
I have also tried building an allyesconfig kernel and succeeded in 7 architectures, as shown in the following table. Unfortunately, ia64 failed miserably with error message Error: Operand 2 of 'adds' should be a 14-bit integer (-8192-8191), and the error seems to have been there for more than 7 months.
Architecture Name $TARGET $KERNEL_ARCH $KERNEL_CONF
x86 (64-bit) x86_64 x86_64-pc-linux-gnu x86 allyesconfig
arm (32-bit) arm armv7-unknown-linux-gnueabi arm allyesconfig
arm (64-bit) aarch64 aarch64-unknown-linux-gnu arm64 allyesconfig
powerpc (64-bit) ppc64 powerpc64-unknown-linux-gnu powerpc allyesconfig
sparc (64-bit) sparc64 sparc64-unknown-linux-gnu sparc allyesconfig
mips (64-bit) mips64 mips64-unknown-linux-gnu mips allyesconfig
s390x (64-bit) s390x s390x-ibm-linux-gnu s390 allyesconfig
In case you want to try out, you can directly feed the corresponding values for $TARGET, $KERNEL_ARCH, and $KERNEL_CONF into the scripts above and test.

Wednesday 5 September 2018

The Top Twelve Open Source Artificial Intelligence Tools

Artificial intelligence now pervades many parts of our lives and is here to stay for the foreseeable future. This bird’s eye view of the top open source AI tools serves as a reminder of how AI has already impacted our lives.
 
Imagine the digital future, in which you are dining at a restaurant. In this future scenario, right from the food served on your plate to the waiter who attends to you, everything has been arranged according to your taste and personality traits, without you being asked a single question. Imagine a world in which even the angle at which a plate is served at this restaurant or the smile of the waiter can be calculated with potential suggestions to suit your personality or mood. In such a world, if you are trying to buy a pair of shoes, you’d make a few swipes on your smartphone and all the available shoes in your favourite colour, with the accurate shape of the sole and height of heel, will show up on your screen. All this thanks to machine learning (ML) — and the possibilities are endless.

Artificial intelligence or AI has now penetrated many aspects of our lives. Let’s talk about 2018! All these voice recognition systems we use to search on Google and command our smartphones are the beginning of a new age. We have not yet understood the full potential of AI yet. A limited version of AI technology is already fitted inside our cars. Apple’s Siri, Google’s OK Google, and Amazon’s Echo services are already in use, analysing your tone and voice along with answering your query. These AI-backed services have a natural language processing system and are able to respond to the commands. You can now get directions to a location and look for a specific restaurant, all by just swiping your fingers on a screen!

Driverless cars are now on trial and the population of robots is on the rise, as they are expected to replace humans in certain fields in the very near future. You could expect robot hotel receptionists and robots to clean our houses, reducing us to couch potatoes. Oh well, let’s stick to the brighter side of things!

ClickHere to read more!

Friday 31 August 2018

Linux File System Structure

A standard Linux distribution follows the directory structure as provided below with Diagram and explanation.

 
Each of the above directory (which is a file, at the first place) contains important information, required for booting to device drivers, configuration files, etc. Describing briefly the purpose of each directory, we are starting hierarchically.
  1. /bin : All the executable binary programs (file) required during booting, repairing, files required to run into single-user-mode, and other important, basic commands viz., cat, du, df, tar, rpm, wc, history, etc.
  2. /boot : Holds important files during boot-up process, including Linux Kernel.
  3. /dev : Contains device files for all the hardware devices on the machine e.g., cdrom, cpu, etc
  4. /etc : Contains Application’s configuration files, startup, shutdown, start, stop script for every individual program.
  5. /home : Home directory of the users. Every time a new user is created, a directory in the name of user is created within home directory which contains other directories like Desktop, Downloads, Documents, etc.
  6. /lib : The Lib directory contains kernel modules and shared library images required to boot the system and run commands in root file system.
  7. /lost+found : This Directory is installed during installation of Linux, useful for recovering files which may be broken due to unexpected shut-down.
  8. /media : Temporary mount directory is created for removable devices viz., media/cdrom.
  9. /mnt : Temporary mount directory for mounting file system.
  10. /opt : Optional is abbreviated as opt. Contains third party application software. Viz., Java, etc.
  11. /proc : A virtual and pseudo file-system which contains information about running process with a particular Process-id aka pid.
  12. /root : This is the home directory of root user and should never be confused with ‘/
  13. /run : This directory is the only clean solution for early-runtime-dir problem.
  14. /sbin : Contains binary executable programs, required by System Administrator, for Maintenance. Viz., iptables, fdisk, ifconfig, swapon, reboot, etc.
  15. /srv : Service is abbreviated as ‘srv‘. This directory contains server specific and service related files.
  16. /sys : Modern Linux distributions include a /sys directory as a virtual filesystem, which stores and allows modification of the devices connected to the system.
  17. /tmp :System’s Temporary Directory, Accessible by users and root. Stores temporary files for user and system, till next boot.
  18. /usr : Contains executable binaries, documentation, source code, libraries for second level program.
  19. /var : Stands for variable. The contents of this file is expected to grow. This directory contains log, lock, spool, mail and temp files.

Exploring Important file, their location and their Usability

Linux is a complex system which requires a more complex and efficient way to start, stop, maintain and reboot a system unlike Windows. There is a well defined configuration files, binaries, man pages, info files, etc. for every process in Linux.
  1. /boot/vmlinuz : The Linux Kernel file.
  2. /dev/hda : Device file for the first IDE HDD (Hard Disk Drive)
  3. /dev/hdc : Device file for the IDE Cdrom, commonly
  4. /dev/null : A pseudo device, that don’t exist. Sometime garbage output is redirected to /dev/null, so that it gets lost, forever.
  5. /etc/bashrc : Contains system defaults and aliases used by bash shell.
  6. /etc/crontab : A shell script to run specified commands on a predefined time Interval.
  7. /etc/exports : Information of the file system available on network.
  8. /etc/fstab : Information of Disk Drive and their mount point.
  9. /etc/group : Information of Security Group.
  10. /etc/grub.conf : grub bootloader configuration file.
  11. /etc/init.d : Service startup Script.
  12. /etc/lilo.conf : lilo bootloader configuration file.
  13. /etc/hosts : Information of Ip addresses and corresponding host names.
  14. /etc/hosts.allow : List of hosts allowed to access services on the local machine.
  15. /etc/host.deny : List of hosts denied to access services on the local machine.
  16. /etc/inittab : INIT process and their interaction at various run level.
  17. /etc/issue : Allows to edit the pre-login message.
  18. /etc/modules.conf : Configuration files for system modules.
  19. /etc/motd : motd stands for Message Of The Day, The Message users gets upon login.
  20. /etc/mtab : Currently mounted blocks information.
  21. /etc/passwd : Contains password of system users in a shadow file, a security implementation.
  22. /etc/printcap : Printer Information
  23. /etc/profile : Bash shell defaults
  24. /etc/profile.d : Application script, executed after login.
  25. /etc/rc.d : Information about run level specific script.
  26. /etc/rc.d/init.d : Run Level Initialisation Script.
  27. /etc/resolv.conf : Domain Name Servers (DNS) being used by System.
  28. /etc/securetty : Terminal List, where root login is possible.
  29. /etc/skel : Script that populates new user home directory.
  30. /etc/termcap : An ASCII file that defines the behaviour of Terminal, console and printers.
  31. /etc/X11 : Configuration files of X-window System.
  32. /usr/bin : Normal user executable commands.
  33. /usr/bin/X11 : Binaries of X windows System.
  34. /usr/include : Contains include files used by ‘c‘ program.
  35. /usr/share : Shared directories of man files, info files, etc.
  36. /usr/lib : Library files which are required during program compilation.
  37. /usr/sbin : Commands for Super User, for System Administration.
  38. /proc/cpuinfo : CPU Information
  39. /proc/filesystems : File-system Information being used currently.
  40. /proc/interrupts : Information about the current interrupts being utilised currently.
  41. /proc/ioports : Contains all the Input/Output addresses used by devices on the server.
  42. /proc/meminfo : Memory Usages Information.
  43. /proc/modules : Currently using kernel module.
  44. /proc/mount : Mounted File-system Information.
  45. /proc/stat : Detailed Statistics of the current System.
  46. /proc/swaps : Swap File Information.
  47. /version : Linux Version Information.
  48. /var/log/lastlog : log of last boot process.
  49. /var/log/messages : log of messages produced by syslog daemon at boot.
  50. /var/log/wtmp : list login time and duration of each user on the system currently.
That’s all for now. Keep connected to Tecmint for any News and post related to Linux and Foss world. Stay healthy and Don’t forget to give your value-able comments in comment section.

Tuesday 7 August 2018

Booting with an NFS Root Filesystem

It is possible to boot most EMAC OE systems using NFS (Network File System) as the root filesystem. This method can be especially useful during development where the root filesystem is changing frequently. This can save time as well as wear on the on-board flash device. This page describes the steps required to boot over NFS. 

Prerequisites

Some prerequisites must be met prior to being able to boot a root filesystem over NFS.

TFTP Server

A TFTP server is required to host the kernel image. For information on installing a TFTP server, see Installing TFTP server.

NFS Server

To boot an EMAC OE system over NFS, an NFS server must be available on the local network. This is often the same machine that is being used for software development. EMAC recommends using the nfs-kernel-server package available on most desktop Linux distributions if setting up a new NFS server. Once the server has been installed, export a directory to use as the root filesystem. This is often done using the /etc/exports file. This document assumes that the root filesystem for the board will be located at /srv/nfs/rootfs on the NFS server.
For more information on setting up an NFS server, see Setting up an NFS File Server.

Root Filesystem

A complete root filesystem for the EMAC OE system to boot from must be stored on the NFS server. The NFS server must be configured to allow clients to access this filesystem. The root filesystem does not have to be the directory shared by the NFS filesystem; it can be in a subdirectory, which means many root filesystems can be shared by one NFS server.

Configuring the Client to Boot from NFS

The method used to configure a client to boot with an NFS mounted root filesystem depends on the type of bootloader the machine uses. Configuring U-Boot for booting over NFS is covered here.

Configuring U-Boot

A series of U-Boot commands are used to tell U-Boot to boot an NFS filesystem:
U-Boot> set autoload no
U-Boot> dhcp
DHCP client bound to address 10.0.0.100
Here, the first command, set autoload no, tells U-Boot not to try to load an image after they've gotten an IP address from the DHCP server. The second command, dhcp, tells U-Boot to use DHCP to acquire an IP address. In this example, the DHCP server assigned 10.0.0.100 as the IP address for the machine.
U-Boot> set serverip 10.0.0.20
This line sets the IP address of the TFTP server to 10.0.0.20. This is the server which hosts the Linux kernel which will be used by the machine.
U-Boot> setenv bootargs console=${console} root=/dev/nfs rootfstype=nfs ip=dhcp nfsroot=10.0.0.20:/srv/nfs/test_fileystem
This line sets up the environment needed to boot from the NFS server. These options will be passed to the Linux kernel when booting it. The console=${console} part tells Linux to use the console setting from the U-Boot environment variable; this will usually be something along the lines of console=ttyS0,115200n8. The root=/dev/nfs directive tells Linux to instantiate with the virtual device, /dev/nfs, as the root filesystem. The rootfstype=nfs directive tells Linux that the root filesystem is of the NFS variety. The ip=dhcp directive tells Linux to acquire an IP address by requesting one from the DHCP server. The nfsroot=10.0.0.20:/srv/nfs/test_filesystem directive tells Linux to look for an NFS server at 10.0.0.20 and to mount the /srv/nfs/test_filesystem directory as the root filesystem for the machine.
U-Boot> tftp ${loadaddr} uImage
...
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #####################
This line tells U-Boot to load the kernel from the TFTP server, whose IP address was set earlier with the set serverip 10.0.0.20 command. The filename U-Boot requests from the TFTP server is uImage, and it stores it in the memory location pointed to by loadaddr. The text which follows displays U-Boot's progress in loading the kernel into RAM. When the U-Boot prompt returns, the environment is fully setup and ready to boot from an NFS root filesystem.
U-Boot> bootm ${loadaddr}
The bootm command tells U-Boot to boot from RAM. This means it boots the kernel which was just loaded into RAM, passing the commandline arguments specified by bootargs to the Linux kernel as it does so.
The machine should now boot, and show output on its console as it does so.


Conclusion

Using NFS for the root filesystem allows boards to boot quickly into test versions of a filesystem, provides a tool for remotely backing up the filesystem installed on an embedded machine, can provide a central filesystem image for many boards which can allow updating one filesystem to update all boards which use it simultaneously, and more. Setting up a machine to boot to a root filesystem via NFS requires little work; most of the work is in setting up the server. The ability to boot a root filesystem over NFS provides a powerful tool for many applications.


Copied from this link

Wednesday 25 July 2018

Timesys Getting Started Guide for Freescale i.MX28 EVK

THIS IS AVAILABLE IN TIMESYS 

link: https://linuxlink.timesys.com/docs/gsg

 

Contents


Introduction

This document will describe in detail the procedures for booting a Linux kernel image and mounting a root file system over NFS on the Freescale i.MX28 EVK.

Prerequisites

Host Requirements

To properly boot a board using software from Timesys, your host machine must meet the following requirements:
  • Modern GNU/Linux Distribution. While you can use nearly any modern Linux distribution released in the last 24 months, Timesys recommends one of the following:
    • Ubuntu (Most recent release or LTS)
    • Fedora (Most recent release)
  • An internet connection on the Development Host.
  • Root or sudo permission on the Development Host.
  • A copy of the Linux Kernel (uImage) and Root File System (rootfs.tar.gz) for the Target Board downloaded from Factory. These are found in the output directory of your online build, or in the directory build_armv5l-timesys-linux-<libc>/images/ on the command line.
  • If you are booting your root file system over the network, you will need two network cards installed and configured in the Development Host. One to communicate normally with your LAN/WAN while installing host packages, the other to communicate solely with the target board.
  • An available serial port on your Development Host.

Target Requirements

To boot the Freescale i.MX28 EVK, you will need the following items:

  • Freescale i.MX28 EVK Board
  • Serial NULL Modem Cable
  • Ethernet Crossover Cable or Ethernet hub/switch and Ethernet Patch Cables
Once you have all of the necessary components, you should perform the following steps:

  1. Connect the debug port of the board to the serial port of your workstation using the null modem cable.
  2. If you are using a cross-over cable, connect the Ethernet port of the board to the second Ethernet port of your workstation.
  3. If you are using an Ethernet hub or switch, connect the board to the hub with a straight-through Ethernet cable, then connect the hub to the second Ethernet port of your workstation.
  4. Connect the power supply to your board.

Preparing the Target

Configuring Serial Communication

The i.MX28EVK uses a serial debug port to communicate with the host machine. The commands discussed in this section are meant to be performed by a privileged user account. This requires the root login or prepending each command with sudo.

Using Minicom

  1. Start minicom on your host machine in configuration mode. As root:
    # minicom -o -s -w
  2. A menu of configuration should appear. Use the Down-arrow key to scroll down and select the Serial port setup option, and press Enter.
  3. Verify that the listed serial port is the same one that is connected to the target board. If it is not, press A, and enter the correct device. This is /dev/ttyS0 on most Linux distributions.
  4. Set the Bps/Par/Bits option by pressing the letter E and using the next menu to set the appropriate values. You press the key that corresponds to the value 115200, then press Enter.
  5. Set Hardware flow control to No using the F key.
  6. Set Software flow control to No using the G key.
  7. Press Enter to return to the main configuration menu, and then press Esc to exit this menu.
  8. Reset the board, and wait for a moment. If you do not see output from the board, press Enter several times until you see the prompt. If you do not see any output from the board, and have verified that the serial terminal connection is setup correctly, contact your board vendor.
TIP: If you experience an error similar to Device /dev/ttyS0 is locked when starting minicom, it usually means that another process is using the serial port (which is usually another instance of minicom). You can find the process that is currently using the serial port by executing the following:

# fuser /dev/ttyS0
/dev/ttyS0:         28358

# ps 28358
  PID TTY      STAT  TIME COMMAND
  28923 pts/0    S+    0:00 minicom
This process can also be killed directly with fuser as root. Please use this command with caution:

# fuser -k /dev/ttyS0

Using GNU Screen

To quickly connect to a board using Gnu Screen, execute the following:

# screen /dev/ttyS0 115200
For more information about using screen, please consult the man page, or view the manual online at http://www.gnu.org/software/screen/manual/screen.html



Configuring the Network Interface

Finding and Changing the MAC Address

The MAC address on the i.MX28EVK is set by the ethaddr environment variable in U-Boot. If ethaddr is not set, it can be set using the setenv command.
Example

> setenv ethaddr 12:34:56:78:9a:bc
The MAC Address can be found using the printenv command in U-Boot.
Example

> printenv
baudrate=115200
bootfile="uImage"
stdin=serial
stdout=serial
stderr=serial
ethaddr=12:34:56:78:9a:bc
NOTE Once the MAC address has been set, it cannot be changed without destroying the entire U-Boot environment.

Changing the IP Address

The IP address can be set manually by modifying the ipaddr environment variable, or automatically using the dhcp or bootp commands.

Preparing the Host

Setting up the network

The commands discussed in this section are meant to be performed by a privileged user account. This requires the root login or prepending each command with sudo.

Identify the network adapter connecting the Host to the Target

Timesys only supports direct Ethernet connections from the development Host to the Target board. Ideally, the development Host would have two network adapters; one adapter to connect to your LAN, and one Ethernet card to connect directly to the Target board with a crossover cable or Ethernet hub. If your development Host only has one network interface it must be directly connected to the Target board. The Ethernet adapter connected directly to the target board must be:
  • Configured with a proper static IP address and Subnet Mask.
  • Connected directly to the target board with either a crossover cable or its own Ethernet hub.
From a command prompt issue the command:
# /sbin/ifconfig
Each interface will report its IP address, Subnet Mask, and Default Gateway information:
eth0 Link encap:Ethernet HWaddr 00:19:bb:49:ff:0e      
        inet addr:192.168.3.244 Bcast:192.168.3.255 Mask:255.255.254.0
        inet6 addr: fe80::219:bbff:fe49:ff0e/64 Scope:Link      
        UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1      
        RX packets:57214 errors:0 dropped:0 overruns:0 frame:0      
        TX packets:47272 errors:0 dropped:0 overruns:0 carrier:0
        collisions:0 txqueuelen:1000      
        RX bytes:43109083 (41.1 MB) TX bytes:6308206 (6.0 MB)
        Interrupt:16
eth1 Link encap:Ethernet HWaddr 00:10:b5:4a:c1:a9      
        inet addr:10.0.0.1 Bcast:10.0.0.255 Mask:255.0.0.0      
        UP BROADCAST MULTICAST MTU:1500 Metric:1      
        RX packets:0 errors:0 dropped:0 overruns:0 frame:0      
        TX packets:0 errors:0 dropped:0 overruns:0 carrier:0     
        collisions:0 txqueuelen:1000      
        RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)      
        Interrupt:21 Base address:0x1100
lo  Link encap:Local Loopback      
        inet addr:127.0.0.1 Mask:255.0.0.0      
        inet6 addr: ::1/128 Scope:Host      
        UP LOOPBACK RUNNING MTU:16436 Metric:1      
        RX packets:1974 errors:0 dropped:0 overruns:0 frame:0      
        TX packets:1974 errors:0 dropped:0 overruns:0 carrier:0     
        collisions:0 txqueuelen:0      
        RX bytes:226637 (221.3 KB) TX bytes:226637 (221.3 KB)
Note the IP address and Subnet Mask of the appropriate network connection. You will use this to configure the DHCP server.

Installing the server daemons on the development host

  • On Ubuntu 11.04 and newer:
    # apt-get install xinetd tftp tftpd isc-dhcp-server \
                      nfs-kernel-server portmap
  • On Ubuntu 10.11 and older:
    # apt-get install xinetd tftp tftpd dhcp3-server \
                      nfs-kernel-server portmap
    NOTE: Older versions of Ubuntu use nfs-common and nfs-user-server in place of nfs-kernel-server
  • On Fedora Core:
    # yum install xinetd tftp tftp-server dhcp nfs-utils
Important:
After installing these packages the DHCP server software may start automatically. Having the DHCP server running while you are connected to a LAN can interfere with the operation of other computers. After the DHCP service installs and starts issue these commands to stop the DHCP service and prevent it from starting automatically at boot:

  • To stop the dhcp service:
    • On Ubuntu 11.04 and newer:
      # service isc-dhcp-server stop
    • On Ubuntu 10.11 and older:
      # service dhcp3-server stop
    • On Fedora Core:
      # /etc/init.d/dhcp stop
  • To prevent the service from starting automatically:
    • On Ubuntu 11.04 and newer:
      # chmod 644 /etc/init.d/isc-dhcp-server
    • On Ubuntu 10.11 and older:
      # chmod 644 /etc/init.d/dhcp3-server
    • On Fedora Core:
      1. Click the System Menu
      2. Select Administration
      3. Select Services
      4. Select dhcpd
      5. Click the Customize button
      6. Uncheck Runlevel 2, 3, 4 and 5

Disable SELinux and Firewall on Fedora Core

On Fedora Core, SELinux and the firewall will interfere with many of the services that are needed to work with the target board. These should be disabled before continuing.
Generally Ubuntu does not have these services running by default.

  1. Disable SELinux:
    1. Click the System Menu
    2. Select Administration
    3. Select SELinux Management
    4. Change System Default Enforcing Mode to Disabled
  2. Disable Firewall:
    1. Click the System Menu
    2. Select Administration
    3. Select Services
    4. Select iptables
    5. Click the Customize button
    6. uncheck Runlevel 2, 3, 4 and 5

Setting up DHCP


  1. Edit the dhcpd configuration file:
    • On Ubuntu, edit /etc/dhcp/dhcpd.conf and include the following lines (note: on older versions of Ubuntu this file is at either /etc/dhcp3/dhcpd.conf or /etc/dhcpd.conf):
      subnet 10.0.0.0 netmask 255.0.0.0 {
       host targetboard {
        fixed-address 10.0.0.10;
        hardware ethernet 12:34:56:78:9a:bc;
        option root-path "/full/path/to/rfs";
        filename "uImage";
       }
      }
    • On Fedora Core, edit /etc/dhcpd.conf and include the following lines:
      ddns-update-style ad-hoc;
      subnet 10.0.0.0 netmask 255.0.0.0 {
       host targetboard {
        fixed-address 10.0.0.10;
        hardware ethernet 12:34:56:78:9a:bc;
        option root-path "/full/path/to/rfs";
        next-server 10.0.0.1;
        filename "uImage";
       }
      }
  2. Test the DHCP server on the network card that is connected to your development board. For this example assume eth1. This command will start the DHCP server in the foreground and output any status or error messages to the screen.
    • On Ubuntu up to 12.04 LTS:
      # service dhcp3-server restart
    • On Ubuntu 12.04 LTS and later:
      # service isc-dhcp-server restart
    • On Fedora Core:
      # /usr/sbin/dhcpd -d eth1
    • It is recommended to start the DHCP server in this manner each time you need to boot your Target board.

Setting up TFTP


  1. Edit the xinetd.conf file
    • On Ubuntu, edit /etc/xinetd.conf and add the following lines just above the line that reads includedir /etc/xinetd.d.
      service tftp
      {
       socket_type = dgram
       protocol = udp
       wait = yes
       user = root
       server = /usr/sbin/in.tftpd
       server_args = -s /tftpboot
       disable = no
      }
    • On Fedora Core, the tftp-server package creates a /etc/xinetd.d/tftp file. Edit this file and change the disable line from yes to no. The contents of the file are:
      service tftp
      {
       socket_type     = dgram
       protocol      = udp
       wait       = yes
       user       = root
       server       = /usr/sbin/in.tftpd
       server_args     = -s /tftpboot
       disable      = no
       per_source      = 11
       cps       = 100 2
       flags       = IPv4
      }
  2. Create the /tftpboot folder if it does not exist:
    # mkdir /tftpboot
  3. Copy the kernel image to the /tftpboot directory:
    # cp /path/to/kernel/image/uImage \
                      /tftpboot/uImage
    NOTE Also copy other files that are required for booting, such as a device tree blob, to /tftpboot.
  4. Restart the xinetd server with the following command:
    # /etc/init.d/xinetd restart
  5. Test the TFTP server with the following commands
    # tftp localhost
    tftp> get uImage
    Received 1456898 bytes in 0.4 seconds
    tftp> quit
  6. Set xinetd to start automatically on Fedora Core. Ubuntu users will skip this step.
    1. Click the System Menu
    2. Select Administration
    3. Select Services
    4. Select xinetd
    5. Click the Customize button
    6. Check Runlevel 2, 3, 4 and 5

Setting up NFS


  1. As root, extract rootfs.tar.gz to a directory and note the path. This path will be referred to as /full/path/to/rfs in this document.
    # mkdir /full/path/to/rfs
    # cd /full/path/to/rfs
    # sudo tar xvf rootfs.tar.gz
  2. Export this path by editing /etc/exports to include a line similar to the following:
    /full/path/to/rfs 10.0.0.10(rw,no_root_squash)
  3. Restart the NFS services
    • On Ubuntu issue the following commands in order:
      # service portmap stop
      # service nfs-kernel-server stop
      # service portmap start
      # service nfs-kernel-server start
      NOTE: Older versions of Ubuntu use nfs-common and nfs-user-server in place of nfs-kernel-server
    • On Fedora Core:
      # /etc/init.d/nfs restart
  4. Set nfsd to start automatically on Fedora Core. Ubuntu users will skip this step.
    1. Click the System Menu
    2. Select Administration
    3. Select Services
    4. Select NFS
    5. Click the Customize button
    6. Check Runlevel 2, 3, 4 and 5

Booting the Board


Set Environment Variables

You must set a few environment variables in order to boot the board over TFTP and DHCP. This is done with the setenv and saveenv commands in U-Boot. On the target, set the following environment variables:
Variable Value
bootargs console=ttyAM0,115200 ip=dhcp root=/dev/nfs rw
bootcmd bootp\;bootm
loadaddr 42000000
If you are not using bootp to load the kernel, you must also specify the following values:
Variable Value
serverip 10.0.0.1
ipaddr 10.0.0.10
bootfile uImage
bootargs console=ttyAM0,115200 ip=${ipaddr} root=/dev/nfs rw nfsroot=${serverip}:/full/path/to/rfs
bootcmd tftp\;bootm 42000000
Example

> setenv bootargs console=ttyAM0,115200 ip=dhcp root=/dev/nfs rw
> setenv bootcmd bootp\;bootm
> setenv loadaddr 42000000
> saveenv

Load The Kernel

You can use bootp or tftp to load the kernel. Note that the DHCP server needs to be set up for bootp, and tftp is necessary for both.
Example

> bootp
Speed: 100, full duplex
BOOTP broadcast 1
Using eTSEC1 device
TFTP from server 10.0.0.1; our IP address is 10.0.0.10
Filename 'uImage'.
Load address: 42000000
Loading: #################################################################
         #################################################################
         ##################################################
done
Bytes transferred = 2632869 (282ca5 hex)

Boot the Kernel

The bootm command is used to boot the kernel. It loads the file that was previously uploaded using the bootp, dhcp, or tftp commands.
Example

> bootm
## Booting kernel from Legacy Image at 042000000 ...
   Image Name:   Linux-2.6.31
   Image Type:   Linux Kernel Image (gzip compressed)
   Data Size:    2632805 Bytes =  2.5 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

Additional Information

Factory Documentation


How To Modify and Use Custom Device Tree with Factory

Summary

This HOWTO covers a two-step process for both modifying the Factory-provided device tree and then integrating the (now) custom device tree with Factory for use in future builds.
NOTE: This HOWTO assumes that the Desktop Factory has been configured and built prior to attempting to modify the device tree. If these Factory configuration and build steps have not been completed yet, please do so before continuing with this HOWTO.
Custom Device Tree use with Factory can be broken up into the following two distinct tasks, each of which is covered within this HOWTO:
  1. Modify Existing dts Files
    1. Modify existing device tree source (dts) files
    2. Rebuild binary device tree blob (dtb)
    3. Deploy new dtb for testing
  2. Integrate Custom dts with Factory
    1. Copy modified dts to local sources directory
    2. Modify workorder to utilize custom dts file
    3. Clean device tree sources
    4. Rebuild device tree and SDK Installer
Note: To use the modified dts with new Factory installations (or to use an existing custom dts), simply follow the steps 2.1 and 2.2 prior to kicking off the BSP/SDK build.

Device Tree Introduction

The Device Tree is a generic term encompassing the components that make up a new method of describing the hardware layout of an embedded system that isn't compiled into the kernel proper. The device tree mechanism aims at replacing conventional board .c files that hard-code the hardware layout for each particular processor and/or board. This was adopted into the mainline kernel due to the growing number of hardware variants for a given processor (e.g. Freescale's i.MX6 processor) and the resulting need for processor/board support in the kernel proper. Using device trees, a single kernel image can be booted along with the appropriate Device Tree Blob (dtb) to adequately describe the hardware layout.
The Device Tree Blob (*.dtb) file is the file that is actually deployed to the hardware at boot time. It is typically loaded by the bootloader along with the kernel image. The machine-readable dtb is generated via compilation of human-readable Device Tree Source (*.dts) and Device Tree Source Include (*.dtsi) files by the Device Tree Compiler (dtc).

Device Tree Sources and Factory

The dts and dtsi files are located within the kernel source tree at arch/[arch]/boot/dts/. This is where the dtc looks for particular .dtsi files included in the .dts, and also where the device tree compilation takes place.
Desktop Factory comes packaged with dts files for board/kernel combinations which support device trees. These can be found in target/configs/kernel/[board]/ - typically named dts-[kernel-version]. It is from here that the appropriate device tree source is copied into the src/dl/k/kernel/kernel-[version]/ directory and then ultimately to the kernel working directory (during the kernel_devicetree-fetch stage of BSP/SDK build) as kernel_devicetree.dts. This renaming of the *.dts file is intentional to eliminate any conflicts (overwriting of or confusion) with existing *.dts files included in the kernel source tree.

Modify Existing dts Files

Modify Existing Device Tree Source (dts) Files

Device tree modifications should start with the existing kernel_devicetree.dts file in the downloaded source files for the kernel (found in the src/dl/k/kernel/kernel-[version]/ directory). For example:
vim src/dl/k/kernel/kernel-3.10/kernel_devicetree.dts
Factory compiles the kernel_devicetree.dts with the dtc built in the Linux kernel source tree near the end of the kernel-build step of the BSP/SDK generation. This action needs to be initiated next.

Rebuild Binary Device Tree Blob (dtb)

Once modifications have been made to the dts, it needs to be recompiled. There are a number of kernel_devicetree-* make targets available within Factory to manipulate the device tree, though to rebuild the dtb, we can simply call the following from the top-level Factory directory:
make kernel_devicetree-host-install
This will propagate modifications to the kernel_devicetree.dts located in src/dl/ to the kernel working directory and recompile the dtb. The resulting kernel_devicetree.dtb is then copied to the Factory images directory for ease of access (build_[arch]-timesys-linux-[libc]/images/). This process renames the dtb as [board].dtb during the copy to the Factory images directory.
Note: The above command will not rebuild the SDK Installer to include the new dtb file. The make installer command run from the top-level Factory directory is necessary to rebuild the SDK Installer and include the newly compiled dtb.

Deploy New dtb for Testing

Once created, the new *.dtb file needs to be copied to the boot media and loaded by the bootloader along with the kernel image. Please refer to the appropriate Board-specific Getting Started Guide for information regarding this step.

Integrate Custom dts with Factory

Once the device tree has been satisfactorily modified and tested, the modifications can be integrated into the Factory BSP/SDK build process. This is accomplished by storing the custom dts in an accessible location, setting Factory to fetch the custom dts, resetting the device tree portion of the build to a pristine state, and rebuilding the SDK Installer. This example illustrates the steps necessary for storing the custom dts in the src/local/ directory for use in the BSP/SDK build; however, it can indeed be located elsewhere on the local development host, on a network-accessible device, or in a SCM system.

Copy Modified dts to Local Sources Directory

Now that the dts has been modified and resulting dtb tested, integration of the custom dts starts by preserving the modifications in a safe location within Factory (source modifications made in the build_*/linux-[VER] directory will be lost during certain cleaning operations). Factory is designed to utilize a specific user-created source directory (src/local/ for custom files to be used during the BSP/SDK build process. Storing your custom dts in this src/local/ directory not only keeps custom files centrally located in Factory (good practice from a maintenance standpoint), but src/local/ is not effected by cleaning operations run within Factory.
The src/local/ directory structure is intended to closely mimic that of the src/dl/ directory after the Factory fetch stage of a BSP/SDK build. Keeping with this convention, the src/local/ directory, along with the expected kernel-related subdirectory structure can be created with one simple command run from the top-level factory directory (replacing [kernel version] with the kernel version specific to your selections):
mkdir -p src/local/k/kernel/kernel-[kernel version]/
and then copying the dts to this new location is as simple as (again, substituting in your build's specific values in place of variables indicated by [square brackets]):
cp build_[arch]-timesys-linux-[libc]/linux-[kernel version]/arch/[arch]/boot/dts/kernel_devicetree.dts src/local/k/kernel/kernel-[kernel version]/
Note: if using an existing custom dts file (not modifying the Factory-supplied dts), be sure to replace the path to the dts file in this command to reflect the location of your custom dts file.

Modify Workorder to Utilize Custom dts File

The dts file Factory uses during the BSP/SDK build is defined within the workorder. It is here that you specify which dts file to fetch and use in generating the dtb for the build. The menuconfig interface can be used to make the change to your custom dts file as such.
  1. Run make menuconfig from the top-level Factory directory
  2. Navigate to Target Software > Kernel > Create Device Tree Blob > Device Tree filename for your board
  3. Modify value to reflect your custom dts file (in our case, this would be file://$(TSWO_SRC_LOCAL_LOCATION)/k/kernel/kernel-[kernel version]/kernel_devicetree.dts)
  4. Exit menuconfig and save the workorder
Note: the $(TSWO_SRC_LOCAL_LOCATION) entered into the workorder in step 3 above evaluates to src/local/ in the top-level Factory directory.
Now that the Factory knows where to find your custom dts file, all that is left to do is clean the device tree sources and rebuild.

Clean Device Tree Sources

In order rebuild the dtb, while having Factory pull your custom dts from the src/local/ directory, the device tree portion of the kernel build stage needs to be reset within Factory. This can be done with the following, issued from the top-level Factory directory:
make kernel_devicetree-distclean
This action will remove the existing dts file stored in the src/dl/k/kernel/kernel-[kerenl version]/ directory (thereby requiring your modified dts to be fetched during the rebuild) and also remove the compiled *.dtb file from your kernel working directory and the BSP output (build_*/images/) directory.

Rebuild Device Tree and SDK Installer

With the device tree reset and everything set to build the dtb with the custom dts file, all that's left to do is rebuild the dtb and SDK Installer.
  1. make kernel_devicetree-host-install
    • this only regenerates the device tree in the kernel working directory and copies it to the build_*/images/ directory (dts modifications will not be reflected in the dtb included in the SDK Installer)
    • this is automatically completed during the kernel build within Factory (make kernel-build from top-level Factory directory)
  2. make installers
    • this will run the above make kernel_devicetree-host-install and then rebuild the SDK Installer to include the custom dtb

How To Use a Custom Boot Logo / Splash Screen

If you would like to display a custom logo on the frame buffer of your board as the Linux kernel is booting, follow these instructions after you have a successful desktop factory build.

Prerequisites

  • Linux host with the GIMP Image Editor and netpbm utilities installed: On Ubuntu/Debian:
    sudo apt-get install gimp netpbm

    In Fedora/CentOS:
    sudo yum install gimp netpbm netpbm-progs

Limitations

  • The Boot Logo is limited to 224 colors. You can start with an image with more, but the ppmquant step will reduce the image to 224 colors.
  • The logo will be displayed relative to the upper left hand corner of the screen.

Method

  1. Run the GIMP image editor. It can be started on the command line with:
    gimp
  2. Open your logo image and scale it to fit your frame buffer. Some common frame buffer sizes are 320x240 and 480x272.
  3. Save the image as logo.ppm, when prompted for a format, choose raw.
  4. Run the following commands in the same directory you saved your logo.ppm:
    ppmquant 224 logo.ppm > logo_224.ppm
    pnmnoraw logo_224.ppm > logo_final.ppm
    
  5. Copy the resulting logo_final.ppm to your kernel source directory replacing the default boot logo. If you are using the default Timesys kernel, this will be located at </path/to/your/factory>/build*/linux-<version>/drivers/video/logo/logo_linux_clut224.ppm
  6. If you are using an external kernel, the path from drivers/ is the same.
  7. Rebuild your kernel image by running
    rm build_*/linux-<version>/.stamp_image_built && make

    from your factory directory and boot the resulting kernel image on your board.

Troubleshooting

My logo doesn't display

  • Make sure framebuffer support and boot logos are enabled in your kernel configuration. These options can be found in Device Drivers -> Graphics Support
  • Make sure that the framebuffer console (Device Drivers -> Graphics Support -> Console display driver support -> Framebuffer Console support) is enabled in your kernel configuration.
  • Check dmesg for the following message: boot-logo bigger than screen. Try using a smaller image. You are limited by the size of your framebuffer- the kernel will not resize for you. The default logo size is 80x80, so if you are having difficulty sizing, try that first.
  • If your image is divisible by 8, but not by 16, then you need to disable the high resolution VGA console fonts (CONFIG_FONT_8x16) in your kernel configuration, and rebuild. In general, your image should be divisible by the VGA console font size of your system for best results.
    1. Enable Device Drivers -> Graphics Support -> Console display driver support -> Select compiled-in fonts
    2. Disable every option beneath that, except for Device Drivers -> Graphics Support -> Console display driver support -> VGA 8x8 font

The wrong logo is displayed

  • If your kernel shows the Timesys boot logo rather than the default penguin, you will need to de-select the Timesys boot logo option in the kernel-menuconfig interface at:
    Device Drivers->Graphics Support->Bootup logo->224-color TimeSys Linux logo

What is Priority Inversion? Why You Care and What to Do About It

By far the most pervasive mechanism to control the sequencing of tasks in time-constrained systems is priority scheduling. Priority scheduling means that whenever a task or thread is ready to execute, its priority relative to other threads determines the actual order of execution. Ideally, priorities in a time-constrained system should always reflect the time constraints for the tasks. A useful priority assignment heuristic is to assign higher priorities to tasks that are part of tighter time constraints. Thus, a task that needs to respond in a shorter time will get higher priority than a task that needs to respond in a longer time. This intuitive heuristic, known as monotonic scheduling, works well when the system is not overloaded. The more commonly known rate-monotonic scheduling is a special case of this heuristic for periodic tasks. (For detailed explanations of these scheduling mechanisms, see the excellent textbook Real-Time Systems by Jane W. S. Liu (Prentice Hall; ISBN 0130996513; 1st edition).

What Is Priority Inversion?

The most desirable state of a priority scheduler is that the system should always be executing the task with the highest priority. However, it is impossible for an operating system to invariably achieve this state. When circumstances within the system force a higher-priority task to wait for a lower-priority task, priority inversion occurs. This can lead to unbounded priority inversion (discussed in the next section), where a high-priority thread misses its deadline.
Priority inversion is sometimes necessary, and is often outside the control of the operating system developer. At other times, priority inversion might be a deliberate consequence of design decisions made by system developers, as an attempt to balance priority enforcement with other desirable characteristics, such as maintaining integrity.
An example of priority inversion that is typically outside of an operating system developer’s control is the result of the interrupt architecture in most modern machines. When an interrupt occurs, most operating systems will handle the interrupt immediately, regardless of the time constraint of the tasks depending on it. At least some of the processing is done at high priority, to find out which task is waiting for the interrupt.
Another example is when a low-priority task has locked a resource (such as a device or data structure), and a higher-priority task attempts to lock it, the higher-priority task will remain blocked until the low-priority task releases the lock.

Because every operating system experiences incidents of priority inversion, the issue is not the presence of priority inversion, but rather the duration of each cause of priority inversion. The duration of priority inversion is very important in assessing whether an operating system will be suitable for a particular application or system.

What Is Unbounded Priority Inversion?

In most operating systems, an application might actually suffer unbounded priority inversion. An operating system that has large unbounded priority inversions makes a real-time application highly susceptible to timing failures.
Unbounded priority inversion is defined as any situation in which the duration of priority inversion is dependent not only on the time required to handle a shared resource, but on the (unpredictable) actions of other unrelated tasks as well. For example, in 1997, NASA landed a rover robot (Pathfinder) on Mars. The landing was very successful, using a highly innovative “bouncing ball” structure that opened up after landing to release the robot. A few days later, however, the lander software determined erroneously that its internal integrity had been lost and initiated a system reset sequence, aborting that day’s operations. The system reset repeated the next day. After a heroic effort by the Jet Propulsion Laboratory (JPL) team that built the Pathfinder, the problem was traced to priority inversion. Detailed examination of the priority inversion on MARS Pathfinder and the resolution using priority inheritance can be found at http://research.microsoft.com/~mbj/Mars_Pathfinder/Mars_Pathfinder.html.
The Pathfinder lander software contained a number of tasks. This document considers three of those tasks:
  1. periodically checks the health of the spacecraft systems and software.
  2. processes image data.
  3. performs an occasional test on equipment status.
In each period, resets a “Go/Nogo” hardware timer. It is assumed that the integrity of the lander software has been compromised if this timer ever times out. The processor is halted, all devices are reset, the software is completely reloaded, the spacecraft systems are tested, and the system starts over. This process does not complete until the next day.
Attempts to Lock

–Normal Execution
– Execution in Critical Section – Unbounded Priority Inversion
The problem is that and share a common data structure, locking a mutex to control access to it. Thus, if locks the mutex, and tries to lock it, must wait. Every now and then, when has the mutex locked, and is waiting for it, runs because it has a higher priority than . Thus, must wait for both and to complete and fails to reset the timer before it expires.
Even though nothing was actually wrong, the JPL team (which built the Pathfinder) traced the system failure to an unbounded priority inversion after extensive analysis. The team solved the problem by enabling priority inheritance for the mutex.
Unbounded priority inversion happens in many circumstances. It can happen in a preemptible operating system kernel unless it is explicitly avoided (most operating systems are not protected). Device drivers frequently lock critical structures or disable interrupts for long time periods, resulting in unbounded priority inversion. IP packets are placed in FIFO (first in, first out) queues without regard to priority, resulting in unbounded priority inversion. Applications lock data structures using semaphores or mutexes, resulting in unbounded priority inversion.

Avoiding Unbounded Priority Inversion

While there are many known academic solutions to the problem of avoiding unbounded priority inversion, in practice two techniques are commonly used: priority ceiling emulation and disabling interrupts.
The priority ceiling emulation technique raises the priority of any locking thread to its priority ceiling, defined as the highest priority of any thread that ever uses that lock. This requires the developer to supply a priority ceiling for each lock. In contrast, priority inheritance raises the priority of a thread only when holding a lock causes it to block a higher-priority thread. When a higher-priority thread is blocked, the low-priority thread inherits the priority of the higher-priority thread it is blocking.
Kernel threads can also disable interrupts or preemption. Disabling interrupts or preemption is an effective locking technique that avoids unbounded priority inversion, but is of limited general applicability since it stops all other processing. It’s like turning all traffic lights red in a city whenever any car wishes to cross any intersection. However, because of its low overhead, it can work well for locks that are never held for more than a few instructions.

Priority Inheritance vs. Priority Ceiling Emulation

Both the priority inheritance protocol and the priority ceiling emulation protocol* have strengths and weaknesses. When used correctly, each are useful components of a real-time designer’s toolbox.
Priority inheritance can be difficult in a complex environment; implementers not familiar with its nuances can easily make mistakes. However, there is ample evidence that it can also be used to great advantage. For example, in one jitter test (where jitter refers to the maximum deviation in the measured period of a periodic task), using an RTOS running on a heavily loaded 1.4GHz Pentium processor, the worst-case jitter dropped from 76,492 microseconds to 52 microseconds simply by enabling priority inheritance in the kernel. 1.4GHz is somewhat faster than is usually available in embedded systems, but even at this speed the worst-case jitter is still just over 70 milliseconds. Priority inheritance illustrates that faster hardware doesn’t make an application behave more predictably, but avoiding priority inversion does.
Priority ceiling emulation has certain desirable properties — it has good worst-case priority inversion control, it handles nested locks well, and can avoid deadlock in some cases. Priority inheritance can occasionally lead to poorer worst-case behavior when there are nested locks, and does not avoid deadlocks. However, most performance-critical applications and real-time operating systems minimize their use of nested locks, and there are other mechanisms to avoid deadlock when nesting cannot be avoided.
On the other hand, priority inheritance can be implemented so that there is no penalty when the locks are not contended, which covers the vast majority of time-constrained systems. This, in addition to the fact that many extra context switches are avoided, and medium-priority tasks are not preempted unnecessarily, leads to excellent average performance. In practical systems, including both hard and soft real-time systems, average performance is as important as worst-case response. In contrast, priority ceiling emulation will pay the cost of changing a thread’s priority twice regardless of whether there is contention for the lock or not, resulting in higher overhead and many unnecessary context switches and blocking in unrelated tasks.
Priority ceiling emulation is an attractive choice when the set of threads contending for the lock is known, when there may be nested locks, and when worst-case behavior is the only concern. On the other hand, priority inheritance is very effective when a lock is seldom part of a nested set, and when average performance is relevant in addition to worst-case performance.
Another important aspect to understand is that optimal use of priority ceiling emulation requires static analysis of the system to find the priority ceiling of each lock. While static analysis is highly desirable (and even necessary) for many applications with critical response requirements, it may be neither desirable nor cost-effective in many other applications in which only a few parts of the system are critical. Also, formal real-time analysis is primarily applicable to systems that are constructed according to a set of precise rules with limited dynamic operations. For systems that cannot be readily analyzed, priority inheritance is likely to be a more effective mechanism since it does not require static analysis.

Design and Analysis of Real-Time Systems

Priority inheritance and priority ceiling emulation are both effective and powerful techniques to prevent uncontrolled priority inversion when locks are used to protect critical sections in a real-time system. Real-time software designers must make intelligent decisions to use the appropriate technique, depending on their system requirements.
The solution to priority inversion problems starts with a sound architecture and design that must consider the decomposition of the system into tasks and shared resources, and how they impact the system’s ability to meet its timing constraints. Many performance problems can be solved by developing an architecture that avoids unnecessary coupling between tasks through shared resources.
Operating systems for performance-critical applications should provide a complete set of tools to manage priority inversion.

Protecting Resources

The foregoing discussion has concentrated on synchronization without creating unbounded priority inversion; it started with the supposition that locking is required to protect resources. Actually, it is important to remember that in a time-constrained application the best locking is no locking at all. There are four simple rules that can be applied for protecting resources:
  1. Use non-locking atomic operations (such as a flip-buffer) to avoid locking. This approach is clearly optimal with respect to priority inversion.
  2. Hold a lock (when it must be acquired) for as short a time as possible.
  3. Ensure that you understand the limitations of each of the various methods for managing priority inversion when locks are being used.
  4. Ensure that an operating system provides all the tools for managing priority inversion so you have the flexibility to meet a wide range of performance requirements.
Within the operating system, there are several things that can be done to handle priority inversion:
  1. Make the OS kernel fully preemptible. When it needs to lock resources, the kernel’s locking should use mutexes that use priority inheritance to bound blocking within the kernel.
  2. Prevent device drivers from disabling interrupts.
  3. Run interrupt handlers at appropriate priorities, instead of arbitrarily high priorities (as most operating systems do).
  4. Prioritize network packet processing according to the socket owner’s priority.
These features minimize or entirely prevent priority inversion, removing a major source of development delays for time-constrained software.

*Often incorrectly referred to as priority ceiling protocol. The original priority ceiling protocol, described in the paper “Priority Inheritance protocols: An approach to real-time synchronization,” by Sha, Lohoczky, and Rajkumar, is expensive to implement and not available in any commercial operating system. PCP emulation (also known as the highest locker protocol) is a simplified version of the original PCP protocol and is part of various standards such as POSIX (POSIX_PRIO_PROTECT), the Real-Time Specification for Java (RTSJ), OSEK, and the Ada 95 real-time specification. Priority inheritance is also part of standards such as POSIX (POSIX_PRIO_INHERIT) and the RTSJ.

This is copied from Timsys