Section 5.1. Composite Kernel Image: Piggy and Friends


5.1. Composite Kernel Image: Piggy and Friends

At power-on, the bootloader in an embedded system is first to get processor control. After the bootloader has performed some low-level hardware initialization, control is passed to the Linux kernel. This can be a manual sequence of events to facilitate the development process (for example, the user types interactive load/boot commands at the bootloader prompt), or an automated startup sequence typical of a production environment. We have dedicated Chapter 7, "Bootloaders," to this subject, so we defer any detailed bootloader discussion to that chapter.

In Chapter 4, "The Linux Kernel: A Different Perspective," we examined the components that make up the Linux kernel image. Recall that one of the common files built for every architecture is the ELF binary named vmlinux. This binary file is the monolithic kernel itself, or what we have been calling the kernel proper. In fact, when we looked at its construction in the link stage of vmlinux, we pointed out where we might look to see where the first line of code might be found. In most architectures, it is found in an assembly language source file called head.S or similar. In the PowerPC (ppc) branch of the kernel, several versions of head.S are present, depending on the processor. For example, the AMCC 440 series processors are initialized from a file called head_44x.S.

Some architectures and bootloaders are capable of directly booting the vmlinux kernel image. For example, platforms based on PowerPC architecture and the U-Boot bootloader can usually boot the vmlinux image directly[1](after conversion from ELF to binary, as you will shortly see). In other combinations of architecture and bootloader, additional functionality might be needed to set up the proper context and provide the necessary utilities for loading and booting the kernel.

[1] The kernel image is nearly always stored in compressed format, unless boot time is a critical issue. In this case, the image might be called uImage, a compressed vmlinux file with a U-Boot header. See Chapter 7,"Bootloaders."

Listing 5-1 details the final sequence of steps in the kernel build process for a hardware platform based on the ADI Engineering Coyote Reference Platform, which contains an Intel IXP425 network processor. This listing uses the quiet form of output from the kernel build system, which is the default. As pointed out in Chapter 4, it is a useful shorthand notation, allowing more focus on errors and warnings during the build.

Listing 5-1. Final Kernel Build Sequence: ARM/IXP425 (Coyote)

$ make ARCH=arm CROSS_COMPILE=xscale_be- zImage ...   < many build steps omitted for clarity>   LD          vmlinux   SYSMAP      System.map   OBJCOPY     arch/arm/boot/Image   Kernel:     arch/arm/boot/Image is ready   AS          arch/arm/boot/compressed/head.o   GZIP        arch/arm/boot/compressed/piggy.gz   AS          arch/arm/boot/compressed/piggy.o   CC          arch/arm/boot/compressed/misc.o   AS          arch/arm/boot/compressed/head-xscale.o   AS          arch/arm/boot/compressed/big-endian.o   LD          arch/arm/boot/compressed/vmlinux   OBJCOPY     arch/arm/boot/zImage   Kernel:     arch/arm/boot/zImage is ready   Building modules, stage 2. ...

In the third line of Listing 5-1, the vmlinux image (the kernel proper) is linked. Following that, a number of additional object modules are processed. These include head.o, piggy.o,[2] and the architecture-specific head-xscale.o, among others. (The tags identify what is happening on each line. For example, AS indicates that the assembler is invoked, GZIP indicates compression, and so on.) In general, these object modules are specific to a given architecture (ARM/XScale, in this example) and contain low-level utility routines needed to boot the kernel on this particular architecture. Table 5-1 details the components from Listing 5-1.

[2] The term piggy was originally used to describe a "piggy-back" concept. In this case, the binary kernel image is piggy-backed onto the bootstrap loader to produce the composite kernel image.

Table 5-1. ARM/XScale Low-Level Architecture Objects

Component

Function/Description

vmlinux

Kernel proper, in ELF format, including symbols, comments, debug info (if compiled with -g) and architecture-generic components.

System.map

Text-based kernel symbol table for vmlinux module.

Image

Binary kernel module, stripped of symbols, notes, and comments.

head.o

ARM-specific startup code generic to ARM processors. It is this object that is passed control by the bootloader.

piggy.gz

The file Image compressed with gzip.

piggy.o

The file piggy.gz in assembly language format so it can be linked with a subsequent object, misc.o (see the text).

misc.o

Routines used for decompressing the kernel image (piggy.gz), and the source of the familiar boot message: "Uncompressing Linux … Done" on some architectures.

head-xscale.o

Processor initialization specific to the XScale processor family.

big-endian.o

Tiny assembly language routine to switch the XScale processor into big-endian mode.

vmlinux

Composite kernel image. Note this is an unfortunate choice of names, because it duplicates the name for the kernel proper; the two are not the same. This binary image is the result when the kernel proper is linked with the objects in this table. See the text for an explanation.

zImage

Final composite kernel image loaded by bootloader. See the following text.


An illustration will help you understand this structure and the following discussion. Figure 5-1 shows the image components and their metamorphosis during the build process leading up to a bootable kernel image. The following sections describe the components and process in detail.

Figure 5-1. Composite kernel image construction


5.1.1. The Image Object

After the vmlinux kernel ELF file has been built, the kernel build system continues to process the targets described in Table 5-1. The Image object is created from the vmlinux object. Image is basically the vmlinux ELF file stripped of redundant sections (notes and comments) and also stripped of any debugging symbols that might have been present. The following command is used for this:

xscale_be-objcopy -O binary -R .note -R .comment -S  \ vmlinux arch/arm/boot/Image


In the previous objcopy command, the -O option tells objcopy to generate a binary file, the -R option removes the ELF sections named .note and .comment, and the -S option is the flag to strip debugging symbols. Notice that objcopy takes the vmlinux ELF image as input and generates the target binary file called Image. In summary, Image is nothing more than the kernel proper in binary form stripped of debug symbols and the .note and .comment ELF sections.

5.1.2. Architecture Objects

Following the build sequence further, a number of small modules are compiled. These include several assembly language files (head.o, head-xscale.o, and so on) that perform low-level architecture and processor-specific tasks. Each of these objects is summarized in Table 5-1. Of particular note is the sequence creating the object called piggy.o. First, the Image file (binary kernel image) is compressed using this gzip command:

gzip -f -9 < Image > piggy.gz


This creates a new file called piggy.gz, which is simply a compressed version of the binary kernel Image. You can see this graphically in Figure 5-1. What follows next is rather interesting. An assembly language file called piggy.S is assembled, which contains a reference to the compressed piggy.gz. In essence, the binary kernel image is being piggybacked into a low-level assembly language bootstrap loader.[3] This bootstrap loader initializes the processor and required memory regions, decompresses the binary kernel image, and loads it into the proper place in system memory before passing control to it. Listing 5-2 reproduces .../arch/arm/boot/compressed/piggy.S in its entirety.

[3] Not to be confused with the bootloader, a bootstrap loader can be considered a second-stage loader, where the bootloader itself can be thought of as a first-stage loader.

Listing 5-2. Assembly File Piggy.S

  .section .piggydata,#alloc   .globl    input_data input_data:   .incbin  "arch/arm/boot/compressed/piggy.gz"   .globl   input_data_end input_data_end:

This small assembly language file is simple yet produces a complexity that is not immediately obvious. The purpose of this file is to cause the compressed, binary kernel image to be emitted by the assembler as an ELF section called .piggydata. It is triggered by the .incbin assembler preprocessor directive, which can be viewed as the assembler's version of a #include file. In summary, the net result of this assembly language file is to contain the compressed binary kernel image as a payload within another imagethe bootstrap loader. Notice the labels input_data and input_data_end. The bootstrap loader uses these to identify the boundaries of the binary payload, the kernel image.

5.1.3. Bootstrap Loader

Not to be confused with a bootloader, many architectures use a bootstrap loader (or second-stage loader) to load the Linux kernel image into memory. Some bootstrap loaders perform checksum verification of the kernel image, and most perform decompression and relocation of the kernel image. The difference between a bootloader and a bootstrap loader in this context is simple: The bootloader controls the board upon power-up and does not rely on the Linux kernel in any way. In contrast, the bootstrap loader's primary purpose in life is to act as the glue between a board-level bootloader and the Linux kernel. It is the bootstrap loader's responsibility to provide a proper context for the kernel to run in, as well as perform the necessary steps to decompress and relocate the kernel binary image. It is similar to the concept of a primary and secondary loader found in the PC architecture.

Figure 5-2 makes this concept clear. The bootstrap loader is concatenated to the kernel image for loading.

Figure 5-2. Composite kernel image for ARM XScale


In the example we have been studying, the bootstrap loader consists of the binary images shown in Figure 5-2. The functions performed by this bootstrap loader include the following:

  • Low-level assembly processor initialization, which includes support for enabling the processor's internal instruction and data caches, disabling interrupts, and setting up a C runtime environment. These include head.o and head-xscale.o.

  • Decompression and relocation code, embodied in misc.o.

  • Other processor-specific initialization, such as big-endian.o, which enables the big endian mode for this particular processor.

It is worth noting that the details we have been examining in the preceding sections are specific to the ARM/XScale kernel implementation. Each architecture has different details, although the concepts are similar. Using a similar analysis to that presented here, you can learn the requirements of your own architecture.

5.1.4. Boot Messages

Perhaps you've seen a PC workstation booting a desktop Linux distribution such as Red Hat or SUSE Linux. After the PC's own BIOS messages, you see a flurry of console messages being displayed by Linux as it initializes the various kernel subsystems. Significant portions of the output are common across disparate architectures and machines. Two of the more interesting early boot messages are the kernel version string and the kernel command line, which is detailed shortly. Listing 5-3 reproduces the kernel boot messages for the ADI Engineering Coyote Reference Platform booting Linux on the Intel XScale IXP425 processor. The listing has been formatted with line numbers for easy reference.

Listing 5-3. Linux Boot Messages on IPX425

[View full width]

1 Uncompressing Linux... done, booting the kernel. 2 Linux version 2.6.14-clh (chris@pluto) (gcc version 3.4.3 (MontaVista 3.4.3-25.0.30 .0501131 2005-07-23)) #11 Sat Mar 25 11:16:33 EST 2006 3 CPU: XScale-IXP42x Family [690541c1] revision 1 (ARMv5TE) 4 Machine: ADI Engineering Coyote 5 Memory policy: ECC disabled, Data cache writeback 6 CPU0: D VIVT undefined 5 cache 7 CPU0: I cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets 8 CPU0: D cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets 9 Built 1 zonelists 10 Kernel command line: console=ttyS0,115200 ip=bootp root=/dev/nfs 11 PID hash table entries: 512 (order: 9, 8192 bytes) 12 Console: colour dummy device 80x30 13 Dentry cache hash table entries: 16384 (order: 4, 65536 bytes) 14 Inode-cache hash table entries: 8192 (order: 3, 32768 bytes) 15 Memory: 64MB = 64MB total 16 Memory: 62592KB available (1727K code, 339K data, 112K init) 17 Mount-cache hash table entries: 512 18 CPU: Testing write buffer coherency: ok 19 softlockup thread 0 started up. 20 NET: Registered protocol family 16 21 PCI: IXP4xx is host 22 PCI: IXP4xx Using direct access for memory space 23 PCI: bus0: Fast back to back transfers enabled 24 dmabounce: registered device 0000:00:0f.0 on pci bus 25 NetWinder Floating Point Emulator V0.97 (double precision) 26 JFFS2 version 2.2. (NAND) (C) 2001-2003 Red Hat, Inc. 27 Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled 28 ttyS0 at MMIO 0xc8001000 (irq = 13) is a XScale 29 io scheduler noop registered 30 io scheduler anticipatory registered 31 io scheduler deadline registered 32 io scheduler cfq registered 33 RAMDISK driver initialized: 16 RAM disks of 8192K size 1024 blocksize 34 loop: loaded (max 8 devices) 35 eepro100.c:v1.09j-t 9/29/99 Donald Becker http://www.scyld.com/network/eepro100.html 36 eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin <saw@saw.sw .com.sg> and others 37 eth0: 0000:00:0f.0, 00:0E:0C:00:82:F8, IRQ 28. 38   Board assembly 741462-016, Physical connectors present: RJ45 39   Primary interface chip i82555 PHY #1. 40   General self-test: passed. 41   Serial sub-system self-test: passed. 42   Internal registers self-test: passed. 43   ROM checksum self-test: passed (0x8b51f404). 44 IXP4XX-Flash.0: Found 1 x16 devices at 0x0 in 16-bit bank 45 Intel/Sharp Extended Query Table at 0x0031 46 Using buffer write method 47 cfi_cmdset_0001: Erase suspend on write enabled 48 Searching for RedBoot partition table in IXP4XX-Flash.0 at offset 0xfe0000 49 5 RedBoot partitions found on MTD device IXP4XX-Flash.0 50 Creating 5 MTD partitions on "IXP4XX-Flash.0": 51 0x00000000-0x00060000 : "RedBoot" 52 0x00100000-0x00260000 : "MyKernel" 53 0x00300000-0x00900000 : "RootFS" 54 0x00fc0000-0x00fc1000 : "RedBoot config" 55 mtd: partition "RedBoot config" doesn't end on an erase block -- force  read-only0x00fe0000-0x01000000 : "FIS directory" 56 NET: Registered protocol family 2 57 IP route cache hash table entries: 1024 (order: 0, 4096 bytes) 58 TCP established hash table entries: 4096 (order: 2, 16384 bytes) 59 TCP bind hash table entries: 4096 (order: 2, 16384 bytes) 60 TCP: Hash tables configured (established 4096 bind 4096) 61 TCP reno registered 62 TCP bic registered 63 NET: Registered protocol family 1 64 Sending BOOTP requests . OK 65 IP-Config: Got BOOTP answer from 192.168.1.10, my address is 192.168.1.141 66 IP-Config: Complete: 67        device=eth0, addr=192.168.1.141, mask=255.255.255.0, gw=255.255.25 5.255, 68        host=192.168.1.141, domain=, nis-domain=(none), 69        bootserver=192.168.1.10, rootserver=192.168.1.10,  rootpath=/home/chris/sandbox/coyote-target 70 Looking up port of RPC 100003/2 on 192.168.1.10 71 Looking up port of RPC 100005/1 on 192.168.1.10 72 VFS: Mounted root (nfs filesystem). 73 Freeing init memory: 112K 74 Mounting proc 75 Starting system loggers 76 Configuring lo 77 Starting inetd 78 / #

The kernel produces much useful information during startup, as shown in Listing 5-3. We study this output in some detail in the next few sections. Line 1 is produced by the bootstrap loader we presented earlier in this chapter. This message was produced by the decompression loader found in …/arch/arm/boot/compressed/misc.c.

Line 2 of Listing 5-3 is the kernel version string. It is the first line of output from the kernel itself. One of the first lines of C code executed by the kernel (in .../init/main.c) upon entering start_kernel() is as follows:

printk(linux_banner);


This line produces the output just describedthe kernel version string, Line 2 of Listing 5-3. This version string contains a number of pertinent data points related to the kernel image:

  • Kernel version: Linux version 2.6.10-clh

  • Username/machine name where kernel was compiled

  • Toolchain info: gcc version 3.4.3, supplied by MontaVista Software

  • Build number

  • Date and time compiled

This is useful information both during development and later in production. All but one of the entries are self-explanatory. The build number is simply a tool that the developers added to the version string to indicate that something more substantial than the date and time changed from one build to the next. It is a way for developers to keep track of the build in a generic and automatic fashion. You will notice in this example that this was the eleventh build in this series, as indicated by the #11 on line 2 of Listing 5-3. The version string is stored in a hidden file in the top-level Linux directory and is called .version. It is automatically incremented by a build script found in .../scripts/mkversion and by the top-level makefile. In short, it is a version string that is automatically incremented whenever anything substantial in the kernel is rebuilt.



Embedded Linux Primer(c) A Practical Real-World Approach
Embedded Linux Primer: A Practical Real-World Approach
ISBN: 0131679848
EAN: 2147483647
Year: 2007
Pages: 167

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net