Booting your kernel

Booting is highly device-dependent, but here is an example using U-Boot on a BeagleBone Black and QEMU:.

BeagleBone Black

The following U-Boot commands show how to boot Linux on a BeagleBone Black:

U-Boot# fatload mmc 0:1 0x80200000 zImage
reading zImage
4606360 bytes read in 254 ms (17.3 MiB/s)
U-Boot# fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb
reading am335x-boneblack.dtb
29478 bytes read in 9 ms (3.1 MiB/s)
U-Boot# setenv bootargs console=ttyO0,115200
U-Boot# bootz 0x80200000 - 0x80f00000
Kernel image @ 0x80200000 [ 0x000000 - 0x464998 ]
## Flattened Device Tree blob at 80f00000
   Booting using the fdt blob at 0x80f00000
   Loading Device Tree to 8fff5000, end 8ffff325 ... OK
Starting kernel ...
[   0.000000] Booting Linux on physical CPU 0x0
...

Note that we set the kernel command line to console=ttyO0,115200. That tells Linux which device to use for console output which, in this case, is the first UART on the board, device ttyO0, at a speed of 115,200 bits per second. Without this, we would not see any messages after Starting the kernel ... and therefore would not know if it was working or not.

QEMU

Assuming that you have already installed qemu-system-arm, you can launch it with the multi_v7 kernel and the .dtb file for the ARM Versatile Express, as follows:

$ QEMU_AUDIO_DRV=none 
qemu-system-arm -m 256M -nographic -M vexpress-a9 -kernel zImage -dtb vexpress-v2p-ca9.dtb -append "console=ttyAMA0"

Note that setting QEMU_AUDIO_DRV to none is just to suppress error messages from QEMU about missing configurations for the audio drivers, which we do not use.

To exit from QEMU, type Ctrl-A then x (two separate keystrokes).

Kernel panic

While things started off well, they ended badly:

[    1.886379] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[    1.895105] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0, 0)

This is a good example of a kernel panic. A panic occurs when the kernel encounters an unrecoverable error. By default, it will print out a message to the console and then halt. You can set the panic command line parameter to allow a few seconds before it reboots following a panic.

In this case, the unrecoverable error is because there is no root filesystem, illustrating that a kernel is useless without a user space to control it. You can supply a user space by providing a root filesystem either as a ramdisk or on a mountable mass storage device. We will talk about how to create a root filesystem in the next chapter but, to get things up and running, assume that we have a ramdisk in the file uRamdisk and you can then boot to a shell prompt by entering these commands into U-Boot:

fatload mmc 0:1 0x80200000 zImage
fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb
fatload mmc 0:1 0x81000000 uRamdisk
setenv bootargs console=ttyO0,115200 rdinit=/bin/sh
bootz 0x80200000 0x81000000 0x80f00000

Here, I have added rdinit=/bin/sh to the command line so that the kernel will run a shell and give us a shell prompt. Now, the output on the console looks like this:

...
[    1.930923] sr_init: No PMIC hook to init smartreflex
[    1.936424] sr_init: platform driver register failed for SR
[    1.964858] Freeing unused kernel memory: 408K (c0824000 - c088a000)
/ # uname -a
Linux (none) 3.18.3 #1 SMP Wed Jan 21 08:34:58 GMT 2015 armv7l GNU/Linux
/ #

At last, we have a prompt and can interact with our device.

Early user space

In order to transition from kernel initialization to user space, the kernel has to mount a root filesystem and execute a program in that root filesystem. This can be via a ramdisk, as shown in the previous section, or by mounting a real filesystem on a block device. The code for all of this is in init/main.c, starting with the function rest_init() which creates the first thread with PID 1 and runs the code in kernel_init(). If there is a ramdisk, it will try to execute the program /init, which will take on the task of setting up the user space.

If it fails to find and run /init, it tries to mount a filesystem by calling the function prepare_namespace() in init/do_mounts.c. This requires a root= command line to give the name of the block device to use for mounting, usually in the form:

  • root=/dev/<disk name><partition number>
  • root=/dev/<disk name>p<partition number>

For example, for the first partition on an SD card, that would be root=/dev/mmcblk0p1. If the mount succeeds, it will try to execute /sbin/init, followed by /etc/init, /bin/init, and then /bin/sh, stopping at the first one that works.

The init program can be overridden on the command line. For a ramdisk, use rdinit=, (I used rdinit=/bin/sh earlier to execute a shell) and, for a filesystem, use init=.

Kernel messages

Kernel developers are fond of printing out useful information through liberal use of printk() and similar functions. The messages are categorized according to importance, 0 being the highest:

Level

Value

Meaning

KERN_EMERG

0

The system is unusable

KERN_ALERT

1

Action must be taken immediately

KERN_CRIT

2

Critical conditions

KERN_ERR

3

Error conditions

KERN_WARNING

4

Warning conditions

KERN_NOTICE

5

Normal but significant conditions

KERN_INFO

6

Informational

KERN_DEBUG

7

Debug-level messages

They are first written to a buffer, __log_buf, the size of which is two to the power of CONFIG_LOG_BUF_SHIFT. For example, if it is 16, then __log_buf is 64 KiB. You can dump the entire buffer using the command dmesg.

If the level of a message is less than the console log level, it is displayed on the console as well as being placed in __log_buf. The default console log level is 7, meaning that messages of level 6 and lower are displayed, filtering out KERN_DEBUG which is level 7. You can change the console log level in several ways, including by using the kernel parameter loglevel=<level> or the command dmesg -n <level>.

Kernel command line

The kernel command line is a string that is passed to the kernel by the bootloader, via the bootargs variable in the case of U-Boot; it can also be defined in the device tree, or set as part of the kernel configuration in CONFIG_CMDLINE.

We have seen some examples of the kernel command line already but there are many more. There is a complete list in Documentation/kernel-parameters.txt. Here is a smaller list of the most useful ones:

Name

Description

debug

Sets the console log level to the highest level, eight, to ensure that you see all the kernel messages on the console.

init=

The init program to run from a mounted root filesystem, which defaults to /sbin/init.

lpj=

Sets the loops_per_jiffy to a given constant, see the following paragraph.

panic=

Behavior when the kernel panics: if it is greater than zero, it gives the number of seconds before rebooting; if it is zero, it waits forever (this is the default); or if it is less than zero, it reboots without any delay.

quiet

Sets the console log level to one, suppressing all but emergency messages. Since most devices have a serial console, it takes time to output all those strings. Consequently, reducing the number of messages using this option reduces boot time.

rdinit=

The init program to run from a ramdisk, it defaults to /init.

ro

Mounts the root device as read-only. Has no effect on a ramdisk which is always read/write.

root=

Device to mount the root filesystem.

rootdelay=

The number of seconds to wait before trying to mount the root device, defaults to zero. Useful if the device takes time to probe the hardware, but also see rootwait.

rootfstype=

The filesystem type for the root device. In many cases, it is auto-detected during mount, but it is required for jffs2 filesystems.

rootwait

Waits indefinitely for the root device to be detected. Usually necessary with mmc devices.

rw

Mounts the root device as read-write (default).

The lpj parameter is often mentioned in connection with reducing the kernel boot time. During initialization, the kernel loops for approximately 250 ms to calibrate a delay loop. The value is stored in the variable loops_per_jiffy, and reported like this:

Calibrating delay loop... 996.14 BogoMIPS (lpj=4980736)

If the kernel always runs on the same hardware it will always calculate the same value. You can shave 250 ms off the boot time by adding lpj=4980736 to the command line.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.107.193