Starting Linux
Booting and System Management
Pressing the start button, turns on the power supply. The power supply converts the 120 volt electricity into forms of energy the computer can use to process information.
After the power supply has passed its internal self tests and the output has stabilized, it sends the 5V power good signal to the processor timer chip. Once the mother board is energized and the processor timer chip receives the power good signal, it turns on the Central Processing Unit (CPU).
It normally takes between .1 and .5 seconds for the power good signal to reach the timer chip, otherwise an error may be thrown. The processor timer chip controls the reset line to the processor. The CPU resets all of its registers to predefined values. Then the processor starts working in real mode.
Turning your computer on loads the BIOS/UEFI from NVRAM. NVRAM is Non-Volatile Random Access Memory. It retains information when power is turned off. BIOS is the Basic Input/Output System. It is gradually being replaced with the more modern UEFI or Unified Extensible Firmware Interface.
UEFI
When you turn your computer on, the firmware (very low level software, embedded in your hardware by the manufacturer) consults the GUID Partition Table (GPT) to identify the EFI System Partition (ESP). It reads the target application configured in the ESP and executes it.
GPT is part of the UEFI specification. GUID is the Globally Unique IDentifier. GUIDs are not the same as UUIDs (Universally Unique IDs), although they do the same thing, giving block devices unique names.
Linux UUIDs are a function of filesystems. They are created when the filesystem is created. You can display your UUIDs with the command
$ sudo blkid
Use the command
$ sudo gdisk /dev/sda
to display your partition tables and your GUIDs.
UEFI is software that is stored in an EEPROM, a chip soldered into the mother board. Among other things, it searches for and loads a boot loader from the ESP or the MBR on the hard drive into the RAM on the CPU. Turning your computer on, loads UEFI. The firmware scans the GPT and executes the target application, which is Grub2 for now.
Eventually, the Linux kernel will probably be the target application. For as long as Linux supports backwards compatibility with computers that use BIOS to start, which could be a long time, Grub2 starts the Linux kernel. The Linux kernel starts systemd and systemd starts your desktop.
The protected MBR enables booting from a legacy BIOS, and it protects GPT from GPT-unaware operating systems and utilities, such as fdisk.
UEFI can also read and manage File Allocation Table (FAT) filesystems. The EFI System Partition (ESP) is the UEFI system set up on your computer’s hard drive. It contains the Boot loader, among other things.
The ESP is a generic FAT filesystem, which can be mounted, read, written and maintained by any operating system. UEFI can load a UNIX or Linux kernel without a boot loader. However, to maintain compatibility with legacy BIOS systems, Linux uses the GRUB2 boot loader.
UEFI saves the pathname to load from the ESP as a configuration parameter. With no configuration it looks for a standard path. For example, /boot/efi/EFI/ubuntu/grubx64.efi.
UEFI defines a standard Application Programming Interface (API) for accessing the system’s hardware. It is capable of UEFI-level add-on device drivers, written in a processor-independent language and stored in the ESP. Operating systems can use the UEFI interface or they can take direct control of the hardware.
UEFI has an API so that you can examine and modify UEFI variables, including boot menu entries, on a running system. The efibootmgr command lets you change the boot order or create or destroy boot entries.
GRUB : The GRand Unified Boot loader
A boot loader is typically distinct from both the BIOS/UEFI code and the operating system kernel. It is also distinct from the initial boot block of BIOS. A boot loader’s primary purpose is to identify and load an operating system kernel.
The boot process goes something like this:
Your machine’s BIOS/UEFI loads and runs GRUB. GRUB finds the kernel image on the disk, loads it into memory and then starts it.
Typically, your Linux kernel starts with a CPU inspection, then a memory inspection, device bus discovery, device discovery, it initializes the devices and their drivers, executes auxiliary kernel subsystem setup (networking, etc), it mounts the root filesystem and then starts systemd.
systemd then starts your user space, your desktop and all of your applications.
Most boot loaders can display a boot-time user interface, which enables you to select one of several kernels or operating systems. Boot loaders can also set configuration settings for the kernel.
GRUB, developed by the GNU Project, is the default boot loader on most Linux distributions. The current version of GRUB, GRUB 2, is very different from the original version. Grub 2 is so ubiquitous these days, most people just refer to it as Grub.
Grub allows you to set parameters such as which kernel or operating system to boot or the operating system mode to boot into. Grub understands most common file systems and can read its configuration from a regular text file.
The configuration file is grub.cfg and can usually be found in /boot/grub, along with a few other resources and code modules that Grub may need. You can change your boot configuration by editing the grub.cfg file.
You can create a grub.cfg file yourself, but it is more common to generate it with the grub-mkconfig utility, which is wrapped as update-grub in Debian and Ubuntu. update-grub is a command that executes the mkconfig command.
Most distributions assume that grub-cfg can be regenerated and does so automatically after updates, so you will need to take steps to prevent it from clobbering your custom grub-cfg file. Typically, you create a grub-cfg.d file to override the settings in grub-cfg. Or you may have to setup your grub-cfg so that it is not updated during updates.
Distributions configure grub-mkconfig in a variety of configurations. Usually, the configuration is set in /etc/default/grub. If you edit the configuration in etc/default/grub, run update-grub to translate it into a proper grub.cfg file. You should also run update-grub after making any kernel changes, because update-grub inventories your system’s bootable kernels.
You can edit the /etc/grub.d/40_custom file to change the order that all your kernels are listed in the boot menu, set a boot password or change the names of boot menu items. Run update-grub after making any changes.
GRUB has a command line interface for editing config files during boot time. Type c at the GRUB boot screen to enter command-line mode.
This is all just educational information, to help you understand how Linux works. You’ve got to investigate further. This is just a start. Keep learning. Practice makes perfect. Keep making yourself a more and more perfect, very high performance computer technician.
Understand the hardware and the software. Build your own desktop computer. Get the best parts and build a very high performance desktop computer with multiple hard drives and multiple monitors. Build your own very high performance, holistic home office.
Sources: Unix and Linux System Administration Handbook;
Nemeth, Snyder, Hein, Whaley, Mackin, 2018 How Linux Works; Ward, 2015
efibootmgr -help
efibootmgr -help
efibootmgr version 17
usage: efibootmgr [options]
-a | –active –sets bootnum active
-A | –inactive –sets bootnum inactive
-b | –bootnum XXXX –modify BootXXXX (hex)
-B | –delete-bootnum –delete bootnum
-c | –create –create new variable bootnum and add to bootorder
-C | –create-only –create new variable bootnum and do not add to bootorder
-D | –remove-dups –remove duplicate values from BootOrder
-d | –disk –disk (defaults to /dev/sda) containing loader
-r | –driver –Operate on Driver variables, not Boot Variables.
-e | –edd [1|3|-1] –force EDD 1.0 or 3.0 creation variables, or guess
-E | –device num EDD 1.0 –device number (defaults to 0x80)
-g | –gpt –force disk with invalid PMBR to be treated as GPT
-i | –iface name –create a netboot entry for the named interface
-l | –loader name –(defaults to “\EFI\ubuntu\grub.efi”)
-L | –label –label Boot manager display label (defaults to “Linux”)
-m | –mirror-below-4G t|f –mirror memory below 4GB
-M | –mirror-above-4G X –percentage memory to mirror above 4GB
-n | –bootnext XXXX –set BootNext to XXXX (hex)
-N | –delete-bootnext –delete BootNext
-o | –bootorder XXXX,YYYY,ZZZZ,… –explicitly set BootOrder (hex)
-O | –delete-bootorder –delete BootOrder
-p | –part –part partition containing loader (defaults to 1 on partitioned devices)
-q | –quiet –be quiet
-t | –timeout seconds –set boot manager timeout waiting for user input.
-T | –delete-timeout –delete Timeout.
-u | –unicode | –UCS-2 –handle extra args as UCS-2 (default is ASCII)
-v | –verbose –print additional information
-V | –version –return version and exit
-w | –write-signature –write unique sig to MBR if needed
-y | –sysprep –Operate on SysPrep variables, not Boot Variables.
-@ | –append-binary-args file –append extra args from file (use “-” for stdin)
-h | –help –show help/usage
Source: Kubuntu 20.04
UEFI/Grub
linux – refers to the Linux kernel to spawn. It should be a path relative to $BOOT. Every distribution should create a machine id and version specific sub-directory below $BOOT and place its kernels and initial RAM disk images there.
By the time I get all this figured out, GRUB2 will be deprecated and UEFI will start the kernel all by itself. The kernel will start Systemd. Systemd will start Kwin, Wayland, ZSH, KDE and all your desktop applications.
UEFI calls Grub, which is stored on the hard disk, and loads it into RAM on the CPU. The boot loader calls the Kernel from the hard disk and decompresses it by unzipping the .bz file. Then it further expands it by switching to long mode, switching from 32-bit mode to 64-bit mode. Then, it finds a big enough section of RAM, which does not interfere with any other programs, and loads the kernel there. Once the Kernel is loaded, it takes control of the computer.
UEFI is usually stored on a separate chip that starts the computer. It checks the boot order list and selects the appropriate device to load from. That could be the hard drive of the computer, a DVD ROM, CD ROM or a USB thumb drive.
UEFI hands control over to the boot loader. boot.img starts. boot.img is a very simple pointer to Grub2’s core image. diskboot.img is the beginning of the core image and is usually stored in the unused space immediately after the first sector, before the first partition.
The diskboot.img loads the rest of the bootloader core image, which contains Grub2’s kernel and file system management drivers into memory and executes the grub_main function.
grub_main turns the console on, it gets the base address for modules, sets the root device, loads and parses the Grub configuration file, loads modules, etc, and then moves grub to normal mode.
The grub_normal_execute function completes the final preparations and prints a list of options for selecting an operating system on your screen. After selecting one of the options, the grub_menu_execute_entry function executes the boot command, booting the selected operating system.
This list appears on your screen if you have a DVDROM, CDROM or USB plugged in and you make the selection. Otherwise, the selection happens automatically and your login page eventually appears.
Pressing <esc> during boot will also print a list of options on your screen, which will enable you to enter your BIOS/UEFI configuration and adjust it the way you want it.
The boot command reads the kernel header at arch/x86/boot/header.S, then it loads the selected kernel into memory and transfers control to the kernel. Execution starts at _start in arch/x86/boot/header.S. The kernel setup entry point is:
// header.S line 262
.globl _start
_start:
The first function called is copy_boot_params(void). It copies the kernel setup header into boot_params.
After jumping to start_of_setup, the kernel makes sure that all segment register values are equal, sets up a correct stack if necessary, sets up bss and then jumps to the C code in arch/x86/boot/main.c.
GCC is the main C library that Linux is made with and uses to function.
Once everything is set up right, the kernel setup process decompresses the kernel and jumps to it.
- Protected Mode
- Copy Boot parameters into boot_params
- Initialize Console
- Initialize Heap
- Validate CPU
- Memory Detection
- Initialize Keyboard
- Query Intel SpeedStep
- Query Advanced Power Management
- Query Enhanced Disk Drive
Sources: https://github.com/0xAX/linux-insides
Decompression
The Linux kernel is compressed in a .bz file when your computer is turned off. When you start your computer, after the transition to 64-bit mode, the computer decompresses the kernel.
The computer has to find the correct place to store the activated kernel in memory and reserve that space for the kernel. It has the ways and means to avoid using memory reserved for other tasks.
Linux is organized into pages and the pages are loaded into tables. These tables need to be properly initialized. The physical and the virtual addresses are randomized at first and then, the Linux kernel is eventually decompressed in the lowest available, large enough space in memory.
The Linux kernel is stored in your boot directory. Your boot loader, Grub2, loads the kernel into RAM and starts it. It transitions into 64-bit mode and then decompresses the file. At which point the kernel is running and takes control of the computer.
Parted
Parted is the KDE partition manager. You can make changes using the command line or by editing the grub.cfg file. You can start parted with the command: sudo parted. Press tab to see a list of commands. Type print to see a printout of your disk. Type q to exit parted.
While the older sysVinit system was modular, with each module doing one thing very well, systemd aggregates all the modules into one program, which starts and manages all of the user space on your computer.
You can find out whether your computer is using BIOS or UEFI by using the ls /sys/firmware/efi command. If your computer is using UEFI, the ls /sys/firmware/efi command will include a list of files and folders, otherwise, it will not be present at all. Use the efibootmgr command to manage your boot loader. efibootmgr –help can help you figure out what you can do with it.
Protected Mode
To enter protected mode, the Global Descriptor Table (GPT), must be activated with segment descriptors suitable for code, data and stack. The lowest bit of the CR0 or the MSW register indicates whether the CPU is in real or protected mode. Protected mode enhances an operating system’s control over application software in order to improve stability and security.
There are 4 privilege levels, or rings, in protected mode, numbered 0 to 3. Ring 0 is the most privileged, ring 3 is the least privileged. The rings enable the system to restrict tasks from accessing data, call gates or executing privileged instructions. Usually, the operating system kernel and some drivers run in ring 0, your applications run in ring 3.
In real mode, the logical address points directly toward a physical memory location, every logical address consists of two 16 bit sections. The segment_part of the logical address contains the base address of a segment, with a granularity of 16 bytes. The offset is the position within the 16 bytes.
In protected mode, the segment_part is replaced by a 16-bit selector. The 13 upper bits, bit 3 through 15, contain the index of an entry inside a descriptor table. The next bit, bit 2, specifies whether the operation is used with GDT or LDT. The lowest two bits, 0 and 1, are combined to determine the privilege of the request. 0 is the highest privilege and 3 is the lowest.
Sources: https://en.wikipedia.org/wiki/Protected_mode https://wiki.osdev.org/Protected_Mode
set_video
The boot loader fills the vid_mode field. The Linux kernel boot protocol describes it as:
vga=<mode>. <mode> here is either an integer (in C notation, either decimal, octal or hexadecimal) or one of the strings “normal” (meaning 0xFFFF), “ext” (meaning 0xFFFE) or “ask” (meaning 0xFFFD). This value should be entered into the vid_mode field, as it is used by the kernel before the command line is parsed.
0xFFFF is a hexadecimal (base 16) number, which is based on 16, rather than 10. 0-9 and A-F are the numerals. 0x indicates that the number is hexidecimal.
The set_video function gets vid_mode from boot_params.hdr. After that, you can see the call to reset_heap. reset_heap is a macro defined in the arch/x86/boot/boot.h header file.
Heap API
The heap is a constantly changing array of files that evolves as the computer operates. It is a tree shaped hierarchy, with rules for the relationships between it’s nodes and leaves. Nodes have children, leaves do not. One exception to that rule is, the root node contains data and it has leaves.
The hierarchy is formed by parents, children and siblings. This structure enables files and functions to find and call each other.
Initialize heap with the init_heap function. Reset the heap by resetting the heap variable to _end, which is extern char _end[]. Next is the get_heap macro, for heap allocation. It calls the internal function _get_heap with three parameters:
- the size of the datatype allocated for
- how variables should be aligned
- how many items to allocate
Your program contains a heap and a stack. They are constantly changing as the computer operates. Each one accomplishes different tasks.
A heap is stored in your computer in the form of an array. There is a mathematical formula that determines how all the nodes are related to each other. You could say that the heap is one particular kind of array.
The stack and the heap are two different regions of memory. A heap is a hierarchical, top down, data structure that contains global variables in a tree shaped array. A heap is managed by the programmer and scattered around in memory.
Stacks are all one fixed region of memory, automatically used by your operating system in a bottom up, linear, Last In First Out (LIFO) data structure. Stacks are faster, efficiently managed by the CPU and limited in size. A heap is slower, unlimited in size and globally accessible.
Video Mode
After the reset_heap is called in the set_video function, the store_mode_params is called. store_mode_params stores video mode parameters in the include/uapi/linux/screen_info.h header file.
The store_mode_params function starts with a call to the store_cursor_position function, which gets information about the cursor and stores it in the orig_x and orig_y fields of the boot_params,screen_info structure.
After store_cursor_position is executed, store_video_mode is called and gets the current video mode and stores it in boot_params.screen_info.orig_video_mode . Then store_mode_params checks the current video mode and sets the video_segment .
After setting up the address of the video segment, the font size needs to be stored in boot_params.screen_info.orig_video_points.
Finally, we get the number of columns and rows and store them in boot_params.screen_info.orig_video_col and boot_params.screen_info.orig_video_lines.
store_mode_params is finished.
Then we get the save_screen function, which saves the screen to the heap.
Call probe_cards at the arch/x86/boot/video-mode.c source code file. video_cards is just a memory address where all card_info structures are placed in this segment.
Setup Video Mode and transition to long mode
- 32-bit entry point
- Reload segments (If needed)
- Stack setup
- CPU Verification
- Calculate the relocation address
- Reload segments (If needed)
- Prepare to enter long mode
- Long mode
- Early page table initialization
- Transition to 64-bit mode
The computer sets up your computer hardware so that the software can be properly displayed on your screen. It prepares for the transition into protected mode. It sets up the Interrupt Descriptor Table, which describes interrupt handlers etc. The IDT is not actually installed yet. It is just set up with a pointer to the address.
The Global Descriptor Table is set up and then the actual transition into protected mode happens. The computer starts at the 32-bit entry point. You may have to reload the segments.
Set up the stack and verify the CPU. Reload the segments. Calculate the relocation address. Prepare for entering long mode. Long mode is the native format for x86-64 processors. The computer now has 8 additional registers to work with and can use 64-bit words, in addition to all the 32-bit functions. And then the transition to 64-bit mode is finally completed.
All this information is a very superficial look at how a computer starts, runs and processes information. I’ve posted this here for my own education as much as for yours. I like knowing how things work. I will continue to investigate this subject and edit and polish and update the story. My main source of information about computer hardware and firmware is Modern Computer Architecture and Organization, by Jim Ledin, published by Packt. Of course, I also get a lot of information from the Internet.