Sorry for the pretty long delay between posts, its been crazy over here.. The linux kernel stuff is really done during my spare time when I usually get a breather between company stuff, but please note that i will continue to post stuff that I’m learning about..
Anyway, we continue where we left off with the Linux Kernel last time, this time we’re going to take a quick look to how any embedded system boots up.
First off, what does an embedded system consist of?
An embedded system, from our perspective is defined by its core architectures. We have the x86 and amd64 based systems that represents most of commercial personal computers available today in the market. Phones today are also coming out with powerful ARM Cortex based processors that are powerful enough to require a complete operating system to run them. The linux kernel has to be compiled for the target architecture as required. During the compilation, during the kernelconfig, it is possible to select whatever external hardware peripherals are connected to the processor. This allows the kernel to include all the drivers required by the kernel. If the required driver is missing, then the hardware can not be used by the processor. RAM, Flash, Serial Ports, Timers all these things are handled by the kernel.
The Bootloader : Kicking the system into consciousness!
When a processor first receives those transient fluctuations that precede a fully active VCC line powering up all its hardware circuits, it really cant do much. It can only execute code that is present from the RAM, but the RAM also just got powered up.. its contains no data of value, and the only way data can be put there is by an instruction from the processor. A neat little catch-22 with a silver bow on top. The solution is very simple, we’ll need to put some code into a Read-Only Memory block which can be executed by the processor just after start-up.. This bit of code is what we call “the Bootloader”.
The Bootloader code is always machine specific, Architecture specific and is completely contained of Hardware instructions to start all sorts of low level peripherals (send initialization signals) to devices like the memory controller to prepare the RAM to store data, the Hard Driver Controller to let the hard disk know that its supposed to find the operating system files to put into the RAM and various other things like Bus controllers, Serial port devices and a lot of more jing-bang that you can find out later.
Now the Bootloader (since it needs to be found without any identifying address in the system) must have certain requirements. IBM PC (the first ones.. even until today’s i7 systems) require the Bootloader to be present in the 446 Bytes of the Hard Drive or Floppy Partition called the Master Boot Record. This means that the compiled machine executable binary file that the processor first runs needs to be around that size! Now, systems can have around 512Bytes to 8KBytes of bootloader data storable.
These are called first stage Bootloaders, their primary function is to start the bare minimum of systems required to start it up! Their last task is to load (or find..) the second stage bootloader from wherever it is and transfer control to it. These 2nd stage Bootloaders are the likes of GRUB, LILO, NTLDR (for the windows).
These Bootloaders are a lot bigger (~20k) and do a lot more than your average IPL code snippet. A little diagnostic of the hardware (POST) is done which verifies that all the connected hardware / memory / buses are up and functional.. This bootloader also needs to open a secondary memory storage locations with its driver (HardDrive / Flash Storage / Floppy Disks) and load the linux kernel from there and place it into RAM. If the Linux Kernel is in a compressed format (uImage), an extraction stage occurs before the kernel starts executing itself and grabbing control of the startup process!
The Linux Kernel : Starting the Engines
The first thing that the linux kernel does with in its new regime is continue with the initialization process of the devices and drivers of the systems that were not required by the bootloader (and thus not initialized by it..). The only thing about this entire thing is since everything is handler as pointer links and function calls, the linux kernel start systems do not have an explicit main() function as would be familiar to other developers.
If you are curious about what is the first code within the linux kernel that executes when its given control,
- The first line that executes is the
startassembly routine found in./arch/arm/boot/head.S - This routine does some basic hardware setup and invokes the
startup_32assembly routine found in./arch/arm/boot/compressed/head.S. - This routine sets up a basic environment (stack, etc.)
- The kernel is then decompressed through a call to a C function called
decompress_kernel(located in./arch/arm/boot/compressed/misc.c). - When the kernel is decompressed into memory, the control is given to the
startup_32function in./arch/arm/kernel/head.S.
Platform Specific Initialization Sequences
Before anything, the first thing that has to be done by the Kernel is set up the environment responsible for allowing C program to execute within the Kernel. This stage is basically setting up some of the standard libraries that C uses (conio, stdio, stdlib etc..). Once these libraries have been loaded, then only function calls to these libraries would start working.
The main code that starts this sequence can be found is the function start_kernel() found in init/main.c. The call to this function means all the architecture specific code has finished and the platform now has full access to the systems resources.
- setup_arch() found in kernel/setup.c is the first thing the start_kernel() function calls, to compete some CPU and memory based configurations
- the next thing is the trap_init() function that sets up the kernel’s exception handling subsystem that allows for debugging and recovering itself from errors.
- init_IRQ() is the function that sets up the interrupt controller so that interrupts can be handled by the kernel.
- time_init() starts the ticker of the kernel’s clock with regard to an onboard RTC. From now on – jiffies are available for kernel code to start using.
- console_init() starts the system console on a serial port if it is available. Output and error messages can be observed from this point on from this serial line..
- calculate_delay() is the last thing to run here and it calculates some delay constants depending on the clocks present in the system to allow for consistent delays over different architectures and platforms.
After this the kernel starts initializing subsystems like the VFS (Virtual File System), scheduler for handling multiprocessing CPU and various other operating system stuff. The kernel has to remain in the RAM and help in the running of the system. Basically how it does that is by spawning threads/processes.
The kernel also needs to connect to a complete filesystem (or memory storage device containing the rest of the linux operating system files. This is all managed by start_kernel().
Kernel Threads and Processes
A process is basically an instance of a running program. Which is just compiled c source code for a particular task. Process can have memory in RAM that they can call their own, they can request all computer resources like the filesystem, RAM and a whole load of other things. The kernel keeps a structure for all the processes that are currently being given CPU time by its scheduler, it keeps track of the RAM and memory usage of the processes and provides a message scheme for passing messages to the processes in a unified manner (IPC).
Once the linux kernel has finished with all its internal setting up, the transistion to userspace begins by connecting and loading up a filesystem.
Loading a filesystem
The only transfer of data that has happened till now is the loading of the linux Kernel image onto the RAM. The kernel by itself cant really do anything.. it is just a collection of instructions on how to work with the system’s hardware. It needs to load other programs that make use of these functionalities and provide an operating system framework for the user to take advantage of the system’s hardware in a portable, hassle free way. These other programs need to be stored in a physical permanent memory location (can be HDD or Flash Memory) and this collection of files in a heirarchial way for organizing them is none other than a Filesystem.
The filesystem is mounted as ‘/’ and it contains some standard directories where the linux operating system information is stored.
If you want to follow the path we last were at the end of the start_kernel() function. The last task of that function is to find the file /sbin/init and execute it as a process called init with PID 0. This file signifies the end of the kernel’s responsibility in the startup procedure. The init process takes care it from now on. In case this file is not found by the kernel (eg: No Filesystem..), it gives a kernel panic error. This is a common error when working with development boards, it is important to keep cool and recheck the settings that deal with the connection to the linux filesystem.
The Father of All Processes : init
the init process has a PID 0 (or 1 sometimes..) and is a very special process. There is a signal called SIGKILL which is sometimes used to terminate a process that behaves unruly and could have gotten stuck through pointer errors. This init process is the only process that doesnt get affected by SIGKILL. The init process is configured by entries in /etc/inittab (a text file) to control its settings and behavior.
The init process also has some responsibilities, it is the fallback process of making sure the system is always functional at all times. It hadles computer shutdown routines and also adopts certain child processes who’se parents have died without killing them. (that sounds so wrong – i’m talking about child and parent processes..)
Once init is done, the login propt can be shown to the user and the linux system is up and running.
I hope this discourse about the linux kernel is helpful, from the next post will be practical notes on how to write linux drivers for the mini2440!!





















