Exynos4412 kernel transplantation (Part 3) - kernel boot process analysis

Publisher:chuyifeiLatest update time:2021-12-14 Source: eefocusKeywords:Exynos4412 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

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)

[1] [2]
Keywords:Exynos4412 Reference address:Exynos4412 kernel transplantation (Part 3) - kernel boot process analysis

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

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号