The functions used to start the kernel are as follows:
Similar to the process of porting U-Boot, before porting Linux, you need to understand its boot process. The Linux process can be divided into two parts: the architecture/development board related boot process, and the subsequent general boot process. For uImage and zImage, they first self-decompress to get vmlinux, and then execute vmlinux to start the "normal" boot process.
The boot phase is usually written in assembly language. It first checks whether the kernel supports the processor of the current architecture, and then checks whether it supports the current development board. After passing the check, it prepares to call the start_kernel function of the next phase. This is mainly divided into the following two steps:
1) -- The virtual address used when connecting to the kernel, so you need to set up the page table and enable MMU;
2) Routine work before calling the C function start_kernel, including copying the data segment, clearing the BSS segment, and calling the start_kernel function.
The key code of the second stage is mainly written in C language. It performs all the work of kernel initialization, and finally calls the rest_init function to start the init process and create the first process of the system: the init process. In the second stage, there are still some architecture/development board related codes, such as resetting the page table, setting the system clock, initializing the serial port, etc.
Here is a detailed analysis:
1. Phase 1
As with Uboot, we check the function entry point in the connection file. After the kernel is compiled, the vmlinux.lds file will be generated under arch/arm/kernel/. Open it:
stext is defined in linux/arch/arm/kernel/head.S as the function entry point. linux/arch/arm/kernel/head.S is the first file executed after the linux kernel image is decompressed.
The code is only partial, but you can see what is done at this stage:
a -- Set to SVC mode, turn off IRQ and FIQ;
b -- Determine the CPU ID number and whether it is valid;
c -- Determine the machine ID number and check its validity;
d -- Check the validity of the parameter list atags passed by the bootloader
e -- Create the initial page table
The following is an analysis of the program segments encountered above:
a -- Make sure you are in SVC mode
There is nothing much to say about this, just set the CPSR mode bit and mask interrupts;
b -- Check if the CPU ID matches
Get the ID and put it in the r9 register, call the _lookup_processor_type function, which is mainly used to determine whether the kernel matches the current CPU. If not, the value of the r5 register should be 0. At this time, the _error_p function will be called to print the error message, that is, the kernel and the current CPU do not match, and the kernel cannot be started at this time; if the two match, it will return an address describing the processor structure (in the r5 register), and then call the following function.
Let's look at the _lookup_processor_type function, defined in arch/arm/kernel/head-common.S:
The above code is actually an address conversion process. Because the system's MMU function is not enabled when determining the CPU architecture, physical addresses are used. The kernel code is implemented with virtual addresses when connecting. Therefore, if you want to use the proc_info_list structure, you must first find the physical address of the proc_info_list structure, so you must use the above conversion code.
The proc_info_list structure is very important. There are many proc_info_list structures defined in the Linux kernel image. This structure represents the CPU architecture supported by the kernel. This part will be discussed below. Let's analyze the above code first:
Line 153: r3 stores the physical address of _lookup_processor_type_data;
Line 155: Get the offset between the virtual address and the physical address;
Lines 156-157: Use offset to convert the virtual addresses stored in r5 and r6 into physical addresses, mainly to obtain the physical addresses of _proc_info_begin and _proc_info_end, and put them in r5 and r6 respectively;
Line 159: r9 stores the previously read processor ID, and the unnecessary bits are masked here;
Line 160: Check whether the code matches the CPU hardware. If so, return. At this time, r5 stores the base address of the structure _proc_info_list corresponding to the CPU type. If not, check the next proc_info_list structure.
Line 163: If there is no match until _proc_info_end, it is considered an unknown CPU, r5 is assigned 0, and then returns;
Let's take a look at the proc_info_list structure, which is defined in arch/arm/include/asm/procinfo.h:
For Cortex-A9, the structure is initialized in the file arch/arm/mm/proc-v7.S:
.section ".proc.info.init" indicates where the structure is stored after compilation. In the link file arch/arm/kernel/vmlinux.lds:
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
The above two variables _proc_info_begin and _proc_info_end are used to calculate the physical address of the proc_info_list structure.
If the CPU ID matches, the proc-v7.S file will be compiled when the kernel file is compiled. This file can be seen in arch/arm/mm/Makefile
c -- Check if the machine ID matches
The _lookup_machine_type function is mainly used, and its implementation code is very similar to the _lookup_processor_type function, so it will not be explained here;
d -- Check the validity of the parameter list atags passed by the bootloader
The _vet_atags function is used to detect the validity of the parameter list atags
The format and description of the kernel parameter list can be found in the kernel source code directory tree. The parameter list must start with ATAG_CORE and end with ATAG_NONE. Here, ATAG_CORE and ATAG_NONE are the tags of each parameter, which is a 32-bit value. For example, ATAG_CORE=0x54410001. Other parameter tags include: ATAG_MEM32, ATAG_INITRD, ATAG_RAMDISK, ATAG_COMDLINE, etc. Each parameter tag represents a parameter structure, and each parameter structure constitutes a parameter list. The definition of the parameter structure is as follows:
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
struct tag_acorn acorn;
struct tag_memclk memclk;
} in;
};
The parameter structure consists of two parts, one is the tag_header structure and the other is the u union.
The tag_header structure is defined as follows:
struct tag_header {
u32 size;
u32 tag;
};
Where size: indicates the size of the entire tag structure (expressed in the number of words, not bytes), which is equal to the size of tag_header plus the size of u union. For example, the size of parameter structure ATAG_CORE=(sizeof(tag->tag_header)+sizeof(tag->u.core))>>2. The size of each parameter structure is generally obtained through the function tag_size(struct * tag_xxx). Where tag: indicates the tag of the entire tag structure, such as: ATAG_CORE, etc.
__vet_atags:
tst r2, #0x3 //r2 points to the starting position of the parameter list, here we determine whether it is word aligned
bne 1f
ldr r5, [r2, #0] //Get the size of the first tag structure
//#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) Determine whether the length of the tag is legal
subs r5, r5, #ATAG_CORE_SIZE
bne 1f
ldr r5, [r2, #4] //Get the tag of the first tag structure,
ldr r6, =ATAG_CORE
cmp r5, r6 //Determine whether the tag of the first tag structure is ATAG_CORE
bne 1f
mov pc, lr // exit normally
1: supply r2, #0
mov pc, lr // parameter table is incorrect
ENDPROC(__vet_atags)
e -- Create the initial page table
It is executed below:
Here is a detailed analysis:
/*
* Setup the initial page tables. We only setup the barest
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*
* Returns:
* r0, r3, r5-r7 corrupted
* r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)
*/
__create_page_tables:
pgtbl r4, r8 @ page table address
/*
* Clear the swapper page table
*/
mov r0, r4
mov r3, #0
add r6, r0, #PG_DIR_SIZE
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
bne 1b
#ifdef CONFIG_ARM_LPAE
/*
* Build the PGD table (first level) to point to the PMD table. A PGD
* entry is 64-bit wide.
*/
mov r0, r4
add r3, r4, #0x1000 @ first PMD table address
orr r3, r3, #3 @ PGD block type
mov r6, #4 @ PTRS_PER_PGD
mov r7, #1 << (55 - 32) @ L_PGD_SWAPPER
1:
#ifdef CONFIG_CPU_ENDIAN_BE8
str r7, [r0], #4 @ set top PGD entry bits
str r3, [r0], #4 @ set bottom PGD entry bits
#else
str r3, [r0], #4 @ set bottom PGD entry bits
str r7, [r0], #4 @ set top PGD entry bits
#endif
add r3, r3, #0x1000 @ next PMD table
subs r6, r6, #1
bne 1b
add r4, r4, #0x1000 @ point to the PMD tables
#ifdef CONFIG_CPU_ENDIAN_BE8
add r4, r4, #4 @ we only write the bottom word
#endif
#endif
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
/*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/
adr r0, __turn_mmu_on_loc
ldmia r0, {r3, r5, r6}
sub r0, r0, r3 @ virt->phys offset
add r5, r5, r0 @ phys __turn_mmu_on
add r6, r6, r0 @ phys __turn_mmu_on_end
mov r5, r5, lsr #SECTION_SHIFT
mov r6, r6, lsr #SECTION_SHIFT
1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base
str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping
cmp r5, r6
addlo r5, r5, #1 @ next section
blo 1b
/*
* Map our RAM from the start to the end of the kernel .bss section.
*/
add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
ldr r6, =(_end - 1)
orr r3, r8, r7
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
Previous article:Exynos4412 kernel porting (Part 2) - kernel compilation process analysis
Next article:Exynos4412 file system production (I) - Analysis of the file system startup process
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Huawei's Strategic Department Director Gai Gang: The cumulative installed base of open source Euler operating system exceeds 10 million sets
- Download from the Internet--ARM Getting Started Notes
- Learn ARM development(22)
- Learn ARM development(21)
- Learn ARM development(20)
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- Download and get a gift | Keysight Technologies [Cheats on how to choose and use oscilloscope probes]
- 【AT32F421 Review】+ Unboxing and Quick Start
- 【Smart Desktop Interactive Robot】Completed
- [Project source code] [Modelsim FAQ] Definition of port reg and wire in TestBench
- Automatic indoor environment control system
- Looking for projects that have been undertaken for vehicle-mounted ambient lighting
- Correct timing
- Countdown 2 weeks! Shenzhen Electronics Fair opens on September 9, full guide to visiting the exhibition~
- Video: UWB for Automotive Applications: An Engineering Primer
- Base station antenna, open it up for you to see!