Hurdled around to watch one of the games in Toyko 2020 Olympics, we put on the TV, a smart TV, and then we waited an agonizing 10 seconds watching the device boot. Being impatient to watch the game, we were understandably irritated with the time it took the TV to load. Most of us have been there - where we just want to start doing stuff as soon as we switch on our device.
It starts from the code we executed when we press the power button. Yes! You execute a code when you press a power button. This code is a machine specific code (called Bootloader) that initializes the device to start.
Bootloaders are usually agile. Because they do a sprint, to ensure that our device is available for us to use, on time. And to achieve that, our agile friend would need two items: the operating system image (henceforth referred to as kernel) and the device tree binary, dtb (this is simply a database, containing hardware components that are in our device). Some of the main features of the bootloader are:
- Small: It should not occupy significant space, usually less than 128 KB.
- Fast: The end user is not interested in using the boot-loader. They probably don't even care if it exists, they just want to use their device as soon as possible (I just want to watch Olympic games 😀).
- Configurable: Necessary to turn on different hardware features e.g. boot over ethernet
- Debuggable: To know what is causing our OS not to boot properly.
The bootloader takes both the kernel image and dtb from a storage location (NAND, SD/eMMC, TFTP server etc.), and loads them in the random access memory (RAM). Because the kernel would need to be aware of the hardware components of the board it is working with, the bootloader takes of the address of the dtb in the RAM and passes it to the kernel. The bootloader which has been operating from a specific partition in the boot medium (e.g. NAND, SD/eMMC) then jumps to the kernel in the RAM.
Next, is the starting of the low-level kernel initialization, such as enabling the memory management unit (MMU) to abstract the physical memory addresses to virtual address spaces for the device's processes (think of an App, such as netflix, as a process) to use, and the running of architecture specific initialization code. This is done in the kernel’s head file i.e. head.s. Here also, the kernel is decompressed.
Note: ARM kernel’s are usually compressed because of MONEY! That is, space is saved on the memory holding the kernel, and speed is also gained. Both speed and memory is money.
When all architecture specific initialization has been done, the kernel entry point then begins with start_kernel() i.e. the non-architecture specific kernel startup, that:
- Initializes memory, scheduling, interrupts etc.
- Initializes statically compiled drivers e.g. WiFi module driver
- Mounts the root filesystem i.e. the / directory
- Executes the first user process, init – i.e. the services that run when your device completely boots e.g. a date/time service that ensures the time on your device is set correctly.
Hurray, your device is now ready to use!!
References:
- Alberto Liberal - Linux Driver Development for Embedded Processors
- Dongliang Mu - Linux Insides
- Lars Zimmerman - Memory Management Units
- IBM - Device Management