arm-linux kernel boot learning notes (2)

Keywords: Makefile Attribute C

Source: http://blog.csdn.net/lidan113lidan/article/details/46225209


Part3[call_kernel,start_kernel)

The kernel jumps to call_kernel, which actually jumps to the ENTRY(stext) tag in. / kernel/arch/arm/kernel/head.S.
Before looking at step, I first summarize some address-related macros in the kernel:

  • PHYS_OFFSET
    PHYS_OFFSET is the initial physical address of RAM and the address of RAM on the CPU address bus (besides RAM, the CPU address bus maps many IO ports, such as the port of GPT clock, the output port of UART, etc.). The so-called mapping means that on the CPU address bus, through a physical address, you can access a register of peripherals such as GPT/UART. These registers can become IO ports. This way of physically soldering/connecting the physical address of the CPU can correspond to a register of a peripheral is called mapping. Shoot).
    PHYS_OFFSET represents how RAM is mapped, and its value is mach-related, not necessarily 0. Generally recorded in the platform-related memory.h file, goldfish and mt6582 are defined as follows:

    //.kernel/arch\arm\mach-goldfish\include\mach\memory.h
    
    #define PHYS_OFFSET UL(0x00000000)
    
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4
    //.\platform\mt6582\kernel\core\include\mach\memory.h
    
    #define PHYS_OFFSET 0x80000000  
    
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4
  • PAGE_OFFSET
    The meaning of PAGE_OFFSET should be the initial virtual address of the kernel, that is, the boundary between the kernel state and the user state.
    Looking at the information on the Internet, it is said that PAGE_OFFSET is the difference between the physical address and the virtual address of the linear mapping part of the kernel. Personally, I think this understanding is incorrect. Take mt6582 as an example, its RAM starting address is 0x80000000, and the virtual address mapped to is 0xC0000000000. But PAGE_OFFSET=0xC0000000000 is obviously incorrect. Maybe this word is wrong. The original meaning is the difference between physical address and virtual address, but it is not used in many platforms. So I think PAGE_OFFSET is better understood as the boundary between kernel and user state.
    PAGE_OFFSET is configured by CONFIG_PAGE_OFFSET.

  • PV_OFFSET
    There is no such word in the kernel, but there is often an adr in the kernel that subtracts the compiled address of the code from the current pc address to get an offset. This value means the difference between a physical address and its virtual address in the linear mapping segment of the kernel. For convenience, I call it PV_OFFSET.
    Personal definition of PV_OFFSET (pure yy):
    A part of the kernel needs linear mapping (_stext - end_rodata), that is to say, a fixed difference between the physical address and the virtual address of this part. In the kernel, for this part of the address, the physical address and virtual address can be converted directly through the _pa/_va macro, without the need for page tables. _ Pa/_va is actually achieved by adding or subtracting this difference, which is defined as PV_OFFSET.
    Generally speaking, the starting location of RAM (physical address PHYS_OFFSET) is linearly mapped to the starting virtual address of the kernel (virtual address PAGE_FFSET), so in general, PV_OFFSET = PHYS_OFFSET-PAGE_OFFSET,

  • TEXT_OFFSET
    The offset of the starting address of the kernel code in RAM relative to the starting address of RAM/the offset of the virtual starting address of the kernel code relative to the virtual starting address of the kernel (the two definitions are actually the same), on mt6582:
    The starting address of the kernel code in RAM is 0x80008000, and the starting address of RAM is 0x80000000.
    The virtual starting address of the kernel code is 0xC0008000, and the virtual starting address of the kernel is 0xC0000000.
    TEXT_OFFSET is 0x00008000, mainly as mentioned earlier, the starting position of RAM will be linearly mapped to the starting virtual address of the kernel.
    The previous calculation of TEXT_OFFSET is not a definition, but the value is defined in the. / arch/arm/Makefile file file.

  • KERNEL_RAM_VADDR
    This value is defined in arch/arm/kernel/head.S

    #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
    

    This value represents the starting virtual address of the kernel code.

  • KERNEL_RAM_PADDR

    #define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
    

    This value is the address of the kernel code in RAM. This value can only be related to mach platform, because PHYS_OFFSET of each platform is different. This value can be seen in the code of mt6582. Maybe the existence of this definition itself is unscientific, which is not understood at present.

  • swapper_pg_dir

    //./arch/arm/kernel/head.S
    .globl  swapper_pg_dir
    .equ    swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

By definition, swapper_pg_dir means a page table placed in front of the virtual address at which the kernel code starts. Note its location, it should be placed in front of the virtual address where the kernel code starts!

ENTRY(stext):

/*
stext It is the entry function of vmlinux (large), that is, the instruction at the 0 address of Image (in the process of vmlinux passing through objcopy - > Image, it actually extracts the entry function step to the 0 address of Image.
System state before stext:
##1. MMU        = off   
##2. D-Cache    = off
##3. r0         = 0
##4. r1         = Structural ID
##5. r2         = atags
 Be careful,
1. Previously, the system should have been in the shutdown and interruption state, because the interruption vector table has not been set.
2. In zImage, the cache_on function is called at the beginning, and the MMU is usually opened inside it. The identity mapping is done in order to use the cache to accelerate the decompression of the kernel. After decompression of the kernel, the call kernel will call cache_off to shut down the mmu, and then the system is in the state of MMU shutdown.
*/
ENTRY(stext)
    //PSR_F_BIT|PSR_I_BIT are two kinds of interrupts of arm. This is to prohibit IRQ and FIQ interrupts and enter svc mode at the same time. The default should be svc mode. Here is to make sure.
    setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
    //Getting processor id from cp15
    mrc p15, 0, r9, c0, c0      @ get processor id
    //Find processor information that is consistent with the processor itself. When compiled, the kernel compiles all the cpu information currently supported by the kernel to the mirror [_proc_info_begin, _proc_info_end). The start/end address is recorded in the _lookup_processor_type_data variable, and the _lookup_processor_type function is the root. According to the current processor id, the address of the struct proc_info_list structure of the corresponding processor information is found. If found, save the pointer to r5 and return, otherwise r5 returns empty.
    bl  __lookup_processor_type 
    //This sentence sets the cpsr register, and if r5 is NULL, it directly _error_p.
    movs r10, r5        @ invalid processor (r5=0)?
    beq __error_p       @ yes, error 'p'

//r8 = PHYS_OFFSET
#ifndef CONFIG_XIP_KERNEL
    //In mt6582, XIP is not set, so here it is.
    //The current address of r3 2f
    adr r3, 2f
    //r4 = 2f compiler address, r8 is PAGE_OFFSET
    ldmia   r3, {r4, r8}
    //The current address of r4 = 2f at this time is offset from the compiled address (PV_OFFSET).
    sub r4, r3, r4  @ (PHYS_OFFSET - PAGE_OFFSET)
    //r8 = PHYS_OFFSET at this time.
    add r8, r8, r4          @ PHYS_OFFSET
#else
    //PHYS_OFFSET is generally defined in mach-xxx/include/mach/memory.h file.
    ldr r8, =PHYS_OFFSET @ always constant in this case
#endif

    //According to procinfo in r5, verify that the size and magic number of atag data structure are correct, and incorrectly return r2 to zero.
    bl  __vet_atags

    //SMP related, did not see????????????????????????
    bl  __fixup_smp

    /*
        1. Identical mapping of the 2MB segment of the code in which _turn_mmu_on resides in order to avoid addressing errors when the MMU is turned on or off.
        2. Linear mapping is made for the whole image area of the kernel. The size of the linear mapping area is expressed by the compiler address (i.e. virtual address).
            The virtual address range of linear mapping is [KERNEL_RAM_VADDR, KERNEL_END], which is determined at compile time.
            The physical address is the page where the PC is located corresponding to the page where KERNEL_RAM_VADDR is located, then mapping in turn, and finally mapping results.
            It can be simply expressed as follows:
                 Physical address mapping virtual address
(pc The base address of the page - - - - - - <===============> - ---------------- - (the base address of KERNEL_RAM_VADDR)
                    |                            |      <----------- KERNEL_RAM_VADDR 
        PC -------> |                            |      
                    |                            |
               -----------  <==========> --------------- (Next page) 
                    |                            |
                    |                            |
                    |                            |      <----------- KERNEL_END
               -----------  <==========> --------------- (Last page, where KERNEL_END is located
             A default condition for piecewise linear mapping is to assume that the location of the pc is on the first page of the kernel if the pc
             If it is not on the first page of the kernel, all linear mappings will be dislocated when the kernel starts, crash directly.
        3. The page where the kernel startup parameter atags is located is linearly mapped, and the physical address of atags + PV_OFFSET
            It is considered a virtual address of atags.
        __create_page_tables Page tables are populated according to mapping relationships, and mmu is not started.
    */
    bl  __create_page_tables

    //R13 (not for sp here) saves the address of _mmap_switched
    ldr r13, =__mmap_switched   

    //In arm, BSYM is an empty macro with LR <-_enable_mmu physical address
    adr lr, BSYM(1f)            @ return (PIC) address

    mov r8, r4                  @ set TTBR1 to swapper_pg_dir
    //r10 stores the address of proc_info, and PROCINFO_INITFUNC is the offset of the function _cpu_flush.
    //Here we jump to the processor-related _cpu_flush function. For the v7 processor, the final call here is
    //It should be the _v7_setup function. In this function, flags are set up and then jumped to lr
    //That is the _enable_mmu function, where the process is: _v7_setup-> _enable_mmu->. 
    //__turn_mmu_on -> __mmap_switched -> start_kernel
    ARM(add pc, r10, #PROCINFO_INITFUNC )
    //Open mmu here and the page table takes effect.
1:  b   __enable_mmu
ENDPROC(stext)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

__lookup_processor_type

/* 
If the kernel wants to run normally, it must find the adapted processor information and machine information. This function determines whether the current cpu is supported by the kernel. If supported, r5 returns an address to describe the processor architecture, otherwise r5 = 0.
The input parameter is r9 = cpuid, from cp15. CP15 only saves one processor id, and other relevant information of the processor is to be written to the core, which is dynamically matched to the specific processor at runtime.
*/
__lookup_processor_type:
    //r3 stores the base address of the _lookup_processor_type_data data data structure. Since the MMU is closed at this time, the obtained r3 is the physical address of this variable.
    adr r3, __lookup_processor_type_data

    /*
    __lookup_processor_type_data:
    .long   .
    .long   __proc_info_begin
    .long   __proc_info_end
    ##.size name, expression 
    .size   __lookup_processor_type_data, . - __lookup_processor_type_data

    Three data in r3 are loaded in:
    r4 = .
    r5 = __proc_info_begin
    r6 = __proc_info_end
    */
    ldmia   r3, {r4 - r6}

    /*
    vmlinux(Big) is compiled into an executable file, which has a default load base address (KERNEL_RAM_VADDR, usually 0xC0008000),'. 'is parsed at compile time, so it must represent the virtual address at compile time of current instructions.
    Here, it is also the virtual address of the label _lookup_processor_type_data.
    The virtual address zreladdr that the kernel really decompresses is defined in Makefile.boot (0x00008000 in goldfish). When decompress_kernel was decompressed before, the state of MMU was physical address = Virtual address, so the physical address that the kernel really decompressed was zreladdr.
    The physical address of _lookup_processor_type_data is obtained by adr instruction, which exists in r3.
    sub r3, r3, r4; Translated into c language is r3 = r3 - r4. The result of calculation is the offset between physical address and virtual address, equal to - PAGE_OFFSET, which is equivalent to dynamic calculation of PAGE_OFFSET once here.
    */
    sub r3, r3, r4  

    //Based on the offset, the virtual addresses of _proc_info_begin and _proc_info_end are modified to physical addresses.
    add r5, r5, r3
    add r6, r6, r3

    /*
    Searching for. proc.info.init in engineering code, you can see many structures defined as. section". proc.info.init". These are the specific information of each cpu. This structure is also defined in c as struct proc_info_list (./kernel/arch/arm/include/asm/procinfo.h), which is based on the current process Id. Comparing all the supported proc_info_list structures in the kernel, see which structure matches the process Id. If a match is found, the address of the proc_info_list is returned. Some functions/variables related to the processor are stored in the proc_info_list. Its definition is as follows:
    struct proc_info_list {
        unsigned int        cpu_val;
        unsigned int        cpu_mask;
        unsigned long       __cpu_mm_mmu_flags; 
        unsigned long       __cpu_io_mmu_flags; 
        unsigned long       __cpu_flush;        
        const char      *arch_name;
        const char      *elf_name;
        unsigned int        elf_hwcap;
        const char      *cpu_name;
        struct processor    *proc;
        struct cpu_tlb_fns  *tlb;
        struct cpu_user_fns *user;
        struct cpu_cache_fns    *cache;
    };
    */

    //Match value/mask
1:  ldmia   r5, {r3, r4}    @ value, mask
    and r4, r4, r9          @ mask wanted bits
    teq r3, r4
    //Equality means that a matching proc_info_list structure has been found and adjusted to 2f
    beq 2f
    //If not, continue to compare the next one.
    add r5, r5, #PROC_INFO_SZ
    cmp r5, r6
    blo 1b
    //If the match is unsuccessful, r5 = 0
    mov r5, #0              @ unknown processor
    //If it comes from 1, r5 is already the address of proc_info_list, press return directly
2:  mov pc, lr 
ENDPROC(__lookup_processor_type)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

__create_page_tables

/*
Before entering:
r8: Save the initial physical address of the platform-related RAM
r9: cpuid
r10: procinfo
 Return:
r4 Returns the starting physical address of the page table
*/
__create_page_tables:
/*
    Macro input:
     phys Start address for RAM
     TEXT_OFFSET The offset of the initial physical address (in RAM) for the kernel code from the RAM initial address
     PG_DIR_SIZE Size for a complete page table (16KB)
    Macro output:
     rd For output, the physical address of the page table.
    .macro  pgtbl, rd, phys
    add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE
    .endm
*/
    /*
      In arm, it is believed that: 
          1. The physical address of the page table = the starting address of RAM + TEXT_OFFSET - PG_DIR_SIZE
          2. Virtual address of page table = PAGE_OFFSET + TEXT_OFFSET - PG_DIR_SIZE
          (Reference definitions. equ swapper_pg_dir, KERNEL_RAM_VADDR-PG_DIR_SIZE)
      So this sentence calculates the starting physical address of the page table according to the starting address of RAM.
    */
    //r4 is the starting physical address of the page table
    pgtbl   r4, r8              @ page table address

    //Clear the 16KB 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

    //Get _cpu_mm_mmu_flags from the struct proc_info_list corresponding to the current processor
    ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

    /* 
       Map the page on which the _turn_mmu_on_loc function (a very short function) is located to the page table, otherwise the switch on and off of the MMU will be problematic.
    */
    //Get the physical address of _turn_mmu_on_loc
    adr r0, __turn_mmu_on_loc
    //R3 = compiled virtual address of _turn_mmu_on_loc, R5 = compiled virtual address of _turn_mmu_on, 
    //R6 = turn_mmu_on_end compiles the virtual address, where _turn_mmu_on to
    //_ turn_mmu_on_end is the code that opens mmu.
    ldmia   r0, {r3, r5, r6}
    //Convert the previous compiled virtual address to a physical address
    sub r0, r0, r3          @ virt->phys offset
    add r5, r5, r0          @ phys __turn_mmu_on
    add r6, r6, r0          @ phys __turn_mmu_on_end
    //Right-shifted r5,r6 represents a subscript in the page table
    mov r5, r5, lsr #SECTION_SHIFT
    mov r6, r6, lsr #SECTION_SHIFT
    //Get the base address of the page on which the _turn_mmu_on function resides (physical address, 20 bits left by r5).
    //Or the upper mmu mask mmuflags, which exists in r3 (besides address, there are masks in the page table entry).
    //This r3 is what is ultimately written into the page table entries.
1:  orr r3, r7, r5, lsl #SECTION_SHIFT  
    //Store r3 in the corresponding page table entries in the page table (the address written is r5*4)
    str r3, [r4, r5, lsl #PMD_ORDER]
    //If the _turn_mmu_on function spans pages, then loop processing
    cmp r5, r6
    addlo   r5, r5, #1          @ next section
    blo 1b

    /*
        Mapping the page where the physical address of the pc is located to the page where the virtual address KERNEL_RAM_VADDR is located,
        Then keep the linear mapping until the virtual address is KERNEL_END.
        Note that there is a default premise that the current pc page is always the first page of the kernel code, if this
        If the requirement is not satisfied, the whole linear address will be dislocated after mapping.
     */ 
    //Get the physical address of the current instruction
    mov r3, pc
    //Calculate the page subscript of the current instruction page in pgd
    mov r3, r3, lsr #SECTION_SHIFT
    //Generate the pgd page table content to be filled, r7 is the page table attribute.
    orr r3, r7, r3, lsl #SECTION_SHIFT
    //Write the page table entry content (r3) of the pc page into the page table entry corresponding to the virtual address KERNEL_START
    //SECTION_SHIFT is the size of a page and PMD_ORDER is the number of bytes per page table item.
    //In fact, it is finally divided into two steps. add + str implements the operation of writing to the page table of pgd.
    //The second step r0 adds the address of the KERNEL_START page entry.
    add r0, r4,  #(KERNEL_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)
    str r3, [r0, #((KERNEL_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!
    //KERNEL_END is the end of the kernel and is dynamically generated in vmlinux.lds.s.
    //Ultimately in disassembly, the immediate number will change with relocation.
    ldr r6, =(KERNEL_END - 1)
    //Get the address of the next page table item
    add r0, r0, #1 << PMD_ORDER
    //r6 is the address of the page table entry corresponding to KERNEL_END.
    add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
    //Loop through and initialize all page table entries
1:  cmp r0, r6
    add r3, r3, #1 << SECTION_SHIFT
    strls   r3, [r0], #1 << PMD_ORDER
    bls 1b

    /*
        r2 Stored is the physical address of atags, which is boot params, where the page of ATAGS is located.
        Linearly mapped to the page table, the virtual address is the physical address + PV_OFFSET.
    */
    //r0 stores r2, the physical address aligned base address
    mov r0, r2, lsr #SECTION_SHIFT
    movs r0, r0, lsl #SECTION_SHIFT
    //If r0 is zero, then r8 is assigned to the past. r8 is the initial physical address of ram
    moveq   r0, r8 
    //Calculate the offset between the ram start physical address
    sub r3, r0, r8
    ##This offset + page_offset is the virtual address that the page should map to.
    add r3, r3, #PAGE_OFFSET
    //Find the page table to fill according to the virtual address
    add r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)
    orr r6, r7, r0
    //Store content in page table entries
    str r6, [r3]
    //Return
    mov pc, lr
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122

__mmap_switched

_ enable_mmu is the code that opens mmu. After opening mmu, it jumps to _mmap_switched function through stack. The last step of _mmap_switched function is start_kernel s. Since starting_kernels, it mainly enters c code.

/*
__mmap_switched_data:
    .long   __data_loc          @ r4
    .long   _sdata              @ r5
    .long   __bss_start         @ r6
    .long   _end                @ r7
    .long   processor_id            @ r4
    .long   __machine_arch_type     @ r5
    .long   __atags_pointer         @ r6
    .long   cr_alignment            @ r7
    .long   init_thread_union + THREAD_START_SP @ sp
    .size   __mmap_switched_data, . - __mmap_switched_data
*/
__mmap_switched:
    //_ The mmap_switched_data variable contains the values that should be initialized in each register before the c code is executed.
    //These values are specified in vmlinux.lds.S.
    adr r3, __mmap_switched_data

    //r4=__data_loc, r5=_sdata, r6=__bss_start, r7=_end
    ldmia   r3!, {r4, r5, r6, r7}

    //If _sdata!= _data_loc, the replicated data is reinitialized.
    //_ data_loc points to the starting address of the initialization data area in the binary file.
    //_ Data points to the starting address of the initialization data area in memory, if the kernel is executed on a ROM chip.
    //The initialization area in ROM should not be modified. The initialization section is copied to RAM and initialized in RAM.
    cmp r4, r5          @ Copy data segment if needed
1:  cmpne   r5, r6
    ldrne   fp, [r4], #4
    strne   fp, [r5], #4
    bne 1b
    //bss section clearing
    mov fp, #0              @ Clear BSS (and zero fp)
1:  cmp r6, r7
    strcc   fp, [r6],#4
    bcc 1b

 ARM(   ldmia   r3, {r4, r5, r6, r7, sp})
    //Save the processor ID, machine information, etc. calculated before to the memory used for c variable.
    str r9, [r4]            @ Save processor ID
    str r1, [r5]            @ Save machine type
    str r2, [r6]            @ Save atags pointer
    bic r4, r0, #CR_A       @ Clear 'A' bit
    stmia   r7, {r0, r4}    @ Save control register values
    //From then on, it's C that executes, jumping to start_kernel(kernel/init/main.c)
    b   start_kernel
ENDPROC(__mmap_switched)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

Reference material

[1] http://blog.chinaunix.net/uid-20692981-id-3033903.html

Posted by vchris on Sun, 14 Jul 2019 17:25:00 -0700