2. Analysis of Linux kernel startup process

The last blog explained the code structure and compilation process of the Linux kernel . This blog mainly explains the kernel startup process, which is helpful for porting the kernel. To understand the startup process of the kernel, you need to know the purpose of the kernel. The main purpose of some complicated and cumbersome operations before the kernel is started is to let the kernel run. What is the purpose of the kernel operation? It must be for running applications, so the main purpose of the kernel is to mount the root file system and then run your own programs . Since the Linux kernel supports a variety of CPU frameworks and a variety of single boards like u-boot , there are initialization functions related to architecture and development boards . The kernel startup process can also be divided into two parts: the boot process related to the architecture / development board and the subsequent general startup process. The following figure is the startup process of the Linux kernel vmlinux on the ARM architecture processor. The vmlinux kernel is used here because the kernel of other formats (Image, uImage, etc.) will get vmlinux after some special operations, and then start vmlinux.

The first stage usually uses assembly language programming. Since the kernel supports multiple CPU architectures and various development boards, it is necessary to check whether the kernel supports such CPU architectures and development boards . At the end of BootLoader booting, machine code will be passed to the kernel. After the test is passed, because the virtual address is used when linking the kernel, you need to set the page table and enable the MMU- > prepare to call the C (start_kernel) environment of the next stage, so you need to copy the data segment, clear the BBS segment, set the stack , Call the start_kernel function (that is, the virtual address used by the kernel) .

The code flow of the first stage is analyzed below (arch / arm / kernel / head.S)

The above is the content in arch / $ (ARCH) /head.S, which is mainly used to detect whether the kernel supports the CPU architecture and the single board (the single board is the first parameter passed in by BootLoader when the kernel starts (save in R1 )), Create a first-level page table, enable MMU. The following is a detailed explanation.

①Check whether the kernel supports the CPU of this development board. The ID of the CPU can be obtained from C0 in the coprocessor CP15:

Manufacturer number (8bit) ARM uses 0x41 Product sub-number (4bit) ARM system version number (4bit) Product master number (8bit) Processor version number (4bit)

The above function is defined in head_common.S. For the first line in the __lookup_processor_type function is to obtain the physical address of label 3: at R3 (the MMU function is not enabled at this time, so the CPU uses the physical address) Note: The address obtained by the ADR instruction is based on the PC register Calculated . The second line of code is to save the values ​​of __proc_info_begin __proc_info_end and. (These are the addresses used in the link script, which are virtual addresses) in r5-r7. This is to calculate the deviation between the physical address and the virtual address, and the latter two are to calculate the physical addresses of __proc_info_begin and __proc_info_end. Label 1: What is done inside is to read the proc_info_list information from __proc_info_begin to __proc_info_end (the prototype of the proc_info_list structure is defined in include / asm-arm / procinfo.h, which means the CPU supported by the kernel. For the ARM architecture CPU these structures The body is defined in the arch / arm / mm directory , such as proc_arm920.S, which represents the definition file of the structure and other information of the proc_info_list of the arm920 architecture CPU. Different proc_info_list structures are used to support CPUs in different institutions, they are all defined In the .proc_info_init section. These structures are organized together (__proc_info_begin is the start address ), and then compared with the CPUID (r9) value read from the processor to see if it belongs to the same CPU. If it matches, it is transferred to the label 2: Return at the location, if it does not match, continue to read, and then match. No match was successful until the end, and r5 was assigned to 0 to end.note: The CPU of different development boards may be different. The CPU of the ARM architecture is defined in the arch / arm / mm / directory, which defines the relevant information of the CPU, such as __arm920_proc_info (proc_info_list structure) defined in the proc_arm920.S file. So when using this CPU, you need to include this file. When configuring the kernel, you need to configure it (the configuration menu is system Type-> Support ARM920T processor). It is the same reason to use other chips .

②Check whether the kernel supports the machine ID of this development board. The machine ID is passed in when BootLoader starts the kernel (stored in R1). The __look_machine_type function is also similar to __look_processor_type and is also defined in head_common.S .

For each supported development board in the kernel, a machine_desc structure is defined, which defines some properties and functions related to the development board, such as machine type ID, starting I / O physical address, and interrupt initialization function . For the ARM architecture board-definition file stored in the arch / arm / mach-xxx / xxxx.c in , which contains some information related to the development board, machine_desc prototype is defined in the body include / asm-arm / mach / In arch.h , all machine_desc structures are in the .arch.info.init section. The start address of this section is __arch_info_begin, and the end address is __arch_info_end. To use the relevant development boards, you also need to configure the kernel. You also need to include the relevant files into the kernel ( such as arch / arm / math-s3c2410 / mach-smdk2410.c ). The configuration menu is in system type-> XXX machine-> xx in.

        The __lookup_machine_type function is similar to the above __lookup_processor_type function. The virtual start address and end address of the .arch_info_init section are obtained and converted into physical addresses. Then read the machine ID in machine_desc from the start address of the segment and compare it with R1. If it matches, jump to label 2 and return. If they do not match, continue to read the information inside. If they do not match, the assignment R5 = 0 ends.

③ Create a page table to enable the MMU, initialize the stack, clear the BSS segment, and copy the data segment, etc., and call start_kernel (in head-common.S) to enter the second stage of the kernel.

After calling __enable_mmu to enable the MMU function, the function finally executes the stuff in R13 (that is, the __switch_data saved above). __Switch_data is defined in head_common.S, which defines many functions.

At the end of the above function, call start_kernel in main.c to enter the second stage.

The second stage of kernel startup:

There are two types of parameters that u-boot passes to the kernel: one is the tag list of an address that exists in advance, and the other is the machine ID specified in the R1 register when the kernel is called. The machine ID will be used during the boot phase (when detecting the machine ID in the above), the tag list will be initially processed in the setup_arch function.

The function of set_arch function: make some settings related to the processor-> make some settings related to the development board-> process the tag list-> process the command line parameters-> re-initialize the page table paging_init (& meminfo, mdesc).

In the initialization page table paging_init, the second parameter is the machine_desc structure returned by the previous lookup_machine_type function. This structure is defined in the arch / arm / mach-xxx / mach-smdkxxx.c function. The definition of s3c2440 development board is as follows. This structure is often important, board-level initialization is defined here

The map_io in paging_init-> devicemap_init-> mdesc-> map_io () is the member of the .map_io function pointer in this structure . Need to modify the next clock, modify 16934400 to 12MHz.

Console initialization function:

Call each function defined between __con_initcall_start and __con_initcall_end. These functions are specified using the macro console_initcall (). This macro is used to define a function with the attribute of .con_initcall_init.

After a series of initializations, start_kernel will call rest_init (); in the rest_init () function, the kernel thread kernel_init will be created. Mainly to mount the file system and run the application, mount the root file system in prepare_namespace () .

After mounting the file system, run init_post (), which mainly opens the / dev / console device file, and then calls run_init_process () to execute the init program.

The kernel has run to start the first process init, init will fork out various applications. Then you can start running content in user space.

Published 35 original articles · Like1 · Visits 1870

Guess you like

Origin blog.csdn.net/lzj_linux188/article/details/102695522