Understanding the Boot Process

Typically, a computer boots in three stages:

  1. The hardware is initialized by software closely tied to the hardware.

  2. The kernel is loaded into RAM by the kernel loader.

  3. The kernel itself runs, further initializing the hardware, and then runs the application software.

BIOS

When power is applied to an embedded Linux device, the hardware goes through a series of events that ends with the processor in some sort of reset state. When the hardware releases the processor from the reset state, it begins executing instructions at a known location. These instructions are normally stored in ROM or flash memory. On a PC, these instructions are called the BIOS. The BIOS code is responsible for getting the hardware on the PC ready for the operating system. As part of this setup process, the early BIOS code does a number of really low-level things such as enabling L1 and L2 caches, detecting how much memory is installed, and setting up the RAM refresh. If you’re writing this code, you’ll have your nose deeply buried in the user manuals for your processor, your processor’s supporting chipset, and the devices you’re setting up. After the initial chipset and hardware initialization, the BIOS then loads the kernel (or the kernel loader) into memory and starts it running. On a PC, once Linux is running it generally ignores the BIOS, so in this book we’ll also ignore all the BIOS functionality used by other operating systems such as Microsoft Windows but ignored by Linux. Linux also runs on many architectures, such as ARM, MIPS, PowerPC, and so on, which don’t use a PC BIOS. Generally these other architectures have some architecture-specific sort of low-level startup code (such as PPCBug, OpenFirmware, RedBoot, and so on) that provides functionality similar to what’s provided by the PC BIOS. For the rest of this book, we’ll call this startup code the “BIOS,” with the understanding that we’re really talking about whatever low-level firmware code is appropriate for the architecture.

Kernel Loader

The kernel loader is a small program that has a better understanding of the rules of the operating system and mass storage layout than the BIOS. It can therefore load the OS and start it. On an x86 PC running a standard Linux desktop distribution such as Red Hat, SuSe, or Debian, a kernel loader such as LILO or GRUB normally loads the kernel. For x86-based embedded applications, syslinux also works very well.

For non-x86-based architectures, other boot loaders such as MILO may be needed. If you’re writing a BIOS from scratch, or using a system that was specifically designed with Linux in mind, the kernel loader may very well be integrated directly into the BIOS. Regardless of how it happens on your specific platform, after the kernel loader completes its job, the kernel is in memory and the loader jumps into it.

Kernel

Immediately after the kernel loader jumps into the Linux kernel, the kernel starts initializing itself and all of its compiled-in drivers. If a monitor is attached to the computer (or a serial console is enabled), the first thing the kernel says looks something like this:

Linux version 2.2.18-20 ([email protected]) 
(gcc version egcs-2.91.66+19990314/Linux (egcs-1.1.2 release)) #1 
Mon Nov 23 10:25:54 EDT 2000 

This list gives you the Linux version, the user and machine for which the kernel was compiled, the compiler version, and the kernel compile date. (So much detail that we had to break it into three lines above.) A lot of information about the kernel boot process comes next. Each of the compiled-in drivers typically prints out some information about itself. After the kernel is completely initialized, the computer is finally ready to run the embedded application.

In Linux, as in any version of UNIX, the software that runs can be either kernel code or a user process. If the processor has a Memory Management Unit (MMU), user code runs at a lower privilege level than kernel code, which prevents it from doing things that could crash the computer. Linux can also run on computers without an MMU (using the uClinux kernel patches). In these cases, there’s no concept of differing privilege levels—all code essentially runs as if it were kernel code and has the potential to crash the machine.While this can make debugging more difficult than usual, it shouldn’t deter developers from considering the less expensive MMU-less processors. Since users typically don’t put arbitrary software on their embedded device (as they do on their PCs), an embedded device is much less crash-prone.The software was put there by the embedded system designer and presumably was well tested.

An embedded application will usually consist of one or more user-level applications, the first of which is called init. As shown in Listing 4.1, kernel code from the /usr/src/linux/init/main.c file, the program doesn’t have to be called init; you can pass an init=/some_program command-line argument into the kernel from the boot loader and the program you specified will run at boot time.

Listing 4.1. Kernel Code from /usr/src/linux/init/main.c
static int init(void * unused) 
{
    lock_kernel(); 
    do_basic_setup(); 

    /* 
     * Ok, we have completed the initial bootup, and 
     * we're essentially up and running. Get rid of the 
     * initmem segments and start the user-mode stuff. 
     */ 
    free_initmem(); 
    unlock_kernel(); 
   if (open("/dev/console", O_RDWR, 0) < 0) 
       printk("Warning: unable to open an initial console.
"); 

   (void) dup(0); 
   (void) dup(0); 

   /* 
    * We try each of these until one succeeds. 
    * 
    * The Bourne shell can be used instead of init if we are 
    * trying to recover a really broken machine. 
    */ 

   if (execute_command) 
       execve(execute_command,argv_init,envp_init); 
       execve("/sbin/init",argv_init,envp_init); 
       execve("/etc/init",argv_init,envp_init); 
       execve("/bin/init",argv_init,envp_init); 
       execve("/bin/sh",argv_init,envp_init); 
   panic("No init found. Try passing init= option to kernel."); 
} 

In a typical Linux desktop or server computer, the init program reads a file called /etc /inittab. This file enumerates which scripts and commands init needs to run to achieve the required runlevel, effectively booting the machine. However, you don’t have to use the standard init program that reads inittab and boots the machine; the init program can do anything you want in your embedded system. In fact, if you statically link it, it can be the only file on the filesystem. Your entire application can be housed in this single program. For a deeply embedded system with little need to talk to the outside world, this may be the way to go. Using this technique, you’ll get the smallest possible disk image.

More typically, however, you’ll want to use a small shell, such as ash, and run some scripts that use external commands on your filesystem to do things such as bring up the network and run any daemons your application requires. This is the technique used in the Embedded Linux Workshop (see Chapter 7).

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

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