Abstract: The portability of Embedded Linux allows us to see it on various electronic products. Linux boot processes vary for processors of different architectures. This article takes the S3C2410 ARM processor as an example to analyze the execution process of bootloader after the system is powered on and the Startup Process of ARM Linux.
Keywords: ARM Linux bootloader Startup Process
Graph classification: tp316
1. Introduction
Linux was initially developed by Linus Torvalds, a student at the University of Helsinki in Sweden in 1991. Later, with the support of GNU, Linux was greatly developed. Although Linux is far less popular on desktop PCs than Microsoft's Windows operating system, the rapid development and increasing number of users of Linux cannot be ignored by Microsoft. In recent years, the rapid development of Linux in the field of embedded technology has injected new vitality into Linux.
An embedded Linux system can be divided into four parts: Boot Loader, Linux kernel, file system, and application.
Bootloader is the first code executed after the system is started or reset. It is mainly used to initialize the processor and peripherals and then call the Linux kernel. After completing system initialization, the Linux kernel needs to mount a file system as the root file system ). The root file system is the core component of the Linux system. It can be used as the storage area for files and data in the Linux system. Generally, it also includes the system configuration file and the library required to run the application software. An application can be said to be the "soul" of an embedded system. Its function is usually the goal of designing an embedded system. Without the support of applications, any well-designed embedded system on hardware has no practical significance.
From the above analysis, we can see the relationship and role of Bootloader and Linux kernel in the embedded system. Although bootloader can initialize the system and execute user-input commands during running, its most fundamental function is to start the Linux kernel. During the development of embedded systems, a large part of the energy is spent on the development or transplantation of Bootloader and Linux kernel. If you have a clear understanding of the bootloader Execution Process and the Linux Startup Process, it will help you to clarify the work required in the development process and thus accelerate the development process of the embedded system. This is exactly what we will study in this article.
2. bootloader
2.1 concepts and functions of bootloader
Bootloader is the boot loader of an embedded system. It is the first program that runs after the system is powered on. It acts like the BIOS on a PC. After the system initialization task is completed, it will copy the Linux kernel in non-volatile memory (usually flash or Doc) to ram, then, the system jumps to the First Command of the kernel to start the Linux kernel.
It can be seen that bootloader is closely related to the Linux kernel. To better understand the Startup Process of the Linux kernel, we must first understand the implementation process of bootloader, in this way, you can have a clear understanding of the entire Startup Process of the embedded system.
2.2 execution process of bootloader
The first command address executed after power-on or reset by different processors is different. For arm processors, the address is 0x00000000. For general embedded systems, non-volatile memory such as flash is usually mapped to this address, while bootloader is located at the front end of the memory, therefore, the first program executed after the system is powered on or reset is bootloader. Because the storage of bootloader is different, the execution process of bootloader is also different. The specific analysis is as follows.
In embedded systems, non-volatile memory is usually flash, and Flash is divided into nor flash and NAND Flash. The difference between them is that nor flash supports in-chip execution (xip, execute in place), so that code can be directly executed on flash without being copied to ram for execution. NAND Flash does not support xip,
Therefore, to execute code on NAND Flash, you must first copy the code to Ram and then jump to ram for execution.
In practical applications, bootloader can be designed to be complex based on different functions. In addition to completing basic initialization systems and calling basic tasks such as the Linux kernel, it can also execute commands entered by many users, for example, you can set Linux Startup parameters and partition the Flash files. You can also design it very easily to complete only the most basic functions. However, to enable the Linux kernel, all bootloaders must have the following functions [2]:
1) initialize Ram
Because linux kernels generally run in Ram, bootloader must set and initialize Ram before calling the Linux kernel to prepare for calling the Linux kernel. The task of initializing Ram includes setting the control register parameters of the CPU so that Ram can be used properly and the ram size can be checked.
2) initialize the serial port
The serial port plays an important role in the Linux Startup Process. It is one of the ways in which the Linux kernel interacts with users. Linux can output information through the serial port during the startup process, so that you can clearly understand the Linux Startup Process. Although it is not a task that must be completed by bootloader, output via serial port is a powerful tool for debugging Bootloader and Linux kernel, therefore, a general bootloader initializes a serial port during execution as a debugging port.
3) detect the processor type
Before calling the Linux kernel, bootloader must check the system's processor type and save it to a constant for the Linux kernel. During the startup process, the Linux kernel calls the corresponding initialization program based on the processor type.
4) set Linux Startup Parameters
During the execution of bootloader, you must set and initialize Linux kernel startup parameters. Currently, two methods are used to pass the startup parameters: struct param_struct and struct tag (taglist, tagged list. Struct param_struct is an old parameter transfer method.
Yes. After version 2.4, the Linux kernel basically uses the tag list method. However, to maintain compatibility with earlier versions, it still supports the struct param_struct parameter transfer mode, but during kernel startup it will be converted to the tag list mode.
The tag list method is a new parameter transfer method. It must start with atag_core and end with atag_none. You can add other lists as needed. During the Linux kernel startup process, Initialization is performed based on the startup parameters.
5) Call the Linux kernel Image
The last task completed by bootloader is to call the Linux kernel. If the Linux kernel is stored in flash and can be directly run on it (here flash refers to nor flash), you can directly jump to the kernel for execution. However, since there are various restrictions on code execution in flash, and the speed is far slower than that of RAM
The system copies the Linux kernel to Ram and jumps to ram for execution.
In either case, the CPU register must meet the following conditions before it is executed on the Linux kernel: R0 = 0, R1 = Processor type, R2 = mark the address of the list in Ram.
3. Linux kernel Startup Process
After bootloader copies the Linux kernel image to Ram, you can use the following code to start the Linux kernel: call_linux (0, machine_type, kernel_params_base ).
Among them, machine_tpye is the processor type detected by bootloader, and kernel_params_base is the address of the startup parameter in Ram. In this way, the parameters required for Linux Startup are passed from bootloader to the kernel.
Linux Kernel has two types of images: Non-compressed kernel, called image, and compressed version zimage. Linux Kernel startup varies with the kernel image in the initial stage. Zimage is formed by image compression, so its size is smaller than that of image. However, in order to use zimage, you must add the decompressed code at the beginning of it to decompress zimage before it can be executed. Therefore, its execution speed is slower than that of image. However, considering that the storage space of embedded systems is generally relatively small, zimage can occupy a small amount of storage space, it is worthwhile to sacrifice a little performance cost. Therefore, General embedded systems adopt the kernel compression method.
For ARM Series processors, the zimage Entry Program is arch/ARM/boot/compressed/head. S. It completes the following tasks in sequence: Enable MMU and cache, call decompress_kernel () to decompress the kernel, and call call_kernel () to start the non-compressed kernel image. Next, we will analyze the Linux kernel startup process.
3.1 Linux kernel entry
The Linux Non-compressed kernel entry is located in the stext segment in the file/ARCH/ARM/kernel/head-armv.S. The base address of this section is the jump address after the compressed kernel is decompressed. If the kernel loaded in the system is a non-compressed image, bootloader will copy the kernel from flash to Ram and directly jump to this address to start the Linux kernel. The entry files for Linux systems of different architectures are different. Because these files are related to specific architectures, they are generally written in assembly languages [3]. For ARM-based Linux systems, this file is a head-armv.S. This program calls the corresponding initialization function by searching for the processor kernel type and processor type, creates a page table, and finally jumps to the start_kernel () function to start kernel initialization. The detection processor kernel type is completed in the compilation sub-function _ lookup_processor_type. Use the following code to call it:
BL _ lookup_processor_type.
_ Lookup_processor_type when the call ends and the original program is returned, the returned result is saved to the Register. R8 stores the flag bit of the page table, R9 stores the processor ID, and R10 stores the struproc_info_list structure address related to the processor. The detection processor type is completed in the compilation subfunction _ lookup_ubunture_type. And
_ Lookup_processor_type is similar. It uses the code "BL _ lookup_processor_type" to call it. When this function returns, the return structure is stored in R5, R6, and R7 registers. R5 stores the starting base address of RAM, R6 stores the I/O base address, and R7 saves the page table offset address of I/O. After detecting the processor kernel and processor type, call the _ create_page_tables subfunction to create a page table, all it has to do is map the physical address of the 4 m space starting with the ram base address to the virtual address starting with 0xc0000000. For the author's S3C2410 Development Board, Ram is connected to the physical address 0x30000000, and 0x30000000 ~ when the call _ create_page_tables ends ~ 0x30400000 physical address will be mapped to 0xc0000000 ~ 0xc0400000 virtual address. After all the initialization is complete, use the following code to jump to the entry function start_kernel () of the C program, and initialize the kernel after the start: B symbol_name (start_kernel)
3.2 start_kernel Function
Start_kernel is the entry function after all Linux platforms enter the system kernel initialization. It mainly completes the remaining initialization work related to the hardware platform. After a series of kernel-related initialization, call the first user process-INIT process and wait for the execution of the user process to complete the Linux kernel startup. The specific work of this function is [4] [5]:
1) Call the setup_arch () function to perform the first initialization work related to the architecture;
This function has different definitions for different architectures. For the ARM platform, this function is defined in arch/ARM/kernel/setup. C. It first initializes the processor kernel through the detected Processor type, then initializes the memory structure based on the meminfo structure defined by the system through the bootmem_init () function, and finally calls paging_init () to enable MMU, create a kernel page table to map all physical memory and I/O space.
2) create an abnormal vector table and initialize the interrupt processing function;
3) initialize the system's core process scheduler and clock interrupt handling mechanism;
4) initialize the serial port console (Serial-console );
Arm-Linux usually initializes a serial port as the kernel console during initialization, so that the kernel can output information through the serial port during startup so that developers or users can understand the startup process of the system.
5) Create and initialize system cache to provide cache for various memory calling mechanisms, including dynamic memory allocation, virtualfile system and page cache.
6) initialize memory management to check the memory size and memory occupied by the kernel;
7) initialize the system's inter-process communication mechanism (IPC );
After all the above initialization work is completed, the start_kernel () function calls the rest_init () function for final initialization, this includes creating the System's first process-INIT process to end kernel startup. The INIT process first initializes a series of hardware and then mounts the root file system through the parameters passed through the command line. The INIT process will execute the "init =" parameter passed by the user to run the user-specified command, or execute one of the following processes:
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 ).
After all initialization is complete, the cpu_idle () function will be called to make the system idle and wait for the execution of the user program. So far, the entire Linux kernel has been started.
4. Conclusion
Linux Kernel is a very huge project. After more than a decade of development, it has grown from several hundred kb to several hundred megabytes. It is very difficult to clearly understand every process it executes. However, in the embedded development process, we do not need to be very clear about the internal working mechanism of Linux, as long as we modify the hardware-related parts of the Linux kernel as appropriate, linux can be transplanted to other target platforms. By analyzing the Linux Startup Process, we can see which functions are related to the hardware and which functions have been implemented in the Linux kernel. This will be targeted during the Linux porting process. The hierarchical design of Linux kernel makes Linux porting easier.
References
[1] Zhan Rongkai. Embedded System bootloader technology insider [Eb/Ol]. http://www.ibm.com/developerworks/cn/linux/l-btloader/index.html,2003.12.
[2] Russell king. Booting ARM Linux [Z]. Linux documentation. May 2002
[3] Liu Yu. Embedded System Interface Design and Linux driver development [M]. Beijing University of Aeronautics and Astronautics Press. 2006.6
[4] William gatliff. the Linux 2.4 kernel's startup procedure [dB/CD]. 2002 embedded system conference sanfrancisco, March .. 2002
[5] clardia Salzberg Rodriguez, Gordon Fischer, Steven Smolski. Linux kernel programming [M]. Chen Lijun, He Yan, Liu xialin. Mechanical Industry Press. 2006.7