Wednesday 7 June 2017

ARM Linux boot process analysis step 1


1.Compressed and uncompressed kernel images

 Uncompressed kernel image is the real Linux kernel code. The compressed kernel image is a non-compressed kernel image as data
Compress the package and add the decompression code. That is, it is a self-extracting executable image. Compress the kernel image
When executing, unzip the internally contained data blocks (ie, the uncompressed kernel image), and then perform the uncompressed kernel image.

The uncompressed kernel image is generated by the make icon command. The generation process is:

(1) the core of the various modules through the compiler, link, in the kernel source code to generate the top-level directory vmlinux file, which is
An image of an ELF format

(2) with arm-linux-objcopy order vmlinux converted to binary format image arch/arm/boot/Image


The compressed kernel image is generated by the make zImage command, which is generated as follows:
(1) with gzip non-compressed kernel binary mapping arch/arm/boot/ Image compression, generate
 Arch/arm/boot/compressed/piggy.gz file

(2) arch / arm / boot / compressed / directory has three files: piggy.s, defined a. / Piggy.gz file
Data segment; head.S contains the gzip compressed kernel to extract the code; vmlinux-lds is the link script.
These files are compiled and compiled, and the vmlinux file is generated in the arch / arm / boot / compressed / directory.
An image of an ELF format
(3) with arm-linux-objcopy command to arch / arm / boot / compressed / vmlinux converted to binary format image:
Arch / arm / boot / compressed / zImage

The generation of the two kernel maps is shown below:


2 kernel entry
Linux kernel compiler connection generated after the ELF image file is vmlinux, from the kernel source code under the top directory
Makefile (that is, top-level Makefile) can be found in the vmlinux generation rules:
Vmlinux: $ (vmlinux-lds) $ (vmlinux-init) $ (vmlinux-main) $ (kallsyms.o) FORCE
Which $ (vmlinux-lds) is to compile the connection script, for the ARM platform, is arch / arm / kernel / vmlinux-lds file.
Vmlinux-init is also defined in the top-level Makefile:
Vmlinux-init: = $ (head-y) $ (init-y)
Head-y defined in arch / arm / Makefile:
Head-y: = arch / arm / kernel / head $ (MMUEXT) .o arch / arm / kernel / init_task.o
...
Ifeq ($ (CONFIG_MMU),)
MMUEXT: = -nommu
Endif
For MMU processor, MMUEXT is a blank string, so arch / arm / kernel / head.O is the first
Then the file is compiled by arch / arm / kernel / head.S.

Based on the above analysis, it can be concluded that the non-compressed ARM Linux kernel entry point in the arch / arm / kernel / head.s
in



According to arm linux, from the beginning of the first instruction of kernel analysis, has been analyzed to enter start_kernel () function
We present the linux-4.10 kernel version as an example to analyze, all the code in this paper, the front will add the line number to facilitate and source code control,

In the file init/main.c: 

482 asmlinkage __visible void __init start_kernel(void)
 
In the process of analysis of the code, we use indentation to indicate the code level
Since the start of some code is platform specific, while the implementation of most of the functions of the platform are similar, but in order to better description of code, the platform dependent code,
In addition, this paper started with the uncompressed kernel. The kernel extract part of the code, in arch/arm/boot/compressed, this paper does not discuss

A start condition

        Usually from the power system to execute Linux kernel to this part of the task is performed by the boot loader to complete
        About boot loader, this paper will not do too much introduction
        Here only discuss into some restricted conditions when the Linux kernel, this is boot loader in the last jump to finish before kernel:
        1 CPU must be in the SVC (supervisor) model, and IRQ and FIQ interrupt are prohibited;
        2 MMU (memory management unit) must be closed, the virtual address to physical address;
        The 3 data cache (Data cache) must be closed
        The 4 directive cache (Instruction cache) may be opened, can also be closed, this is not mandatory;
        5 CPU 0 general-purpose registers (R0) must be 0;
        6 CPU 1 general-purpose registers (R1) must be ARM Linux machine type (machine on type, we will have to explain)
        7. CPU General register2 (r2) Must kernel parameter list Physical address (parameter list Byboot loaderTransfer tokernel,The list is used to describe a device information attribute,The details can refer to"Booting ARM Linux"File).
Two starting kernel
First of all, we first to several important macro description (aiming at MMU):

User/kernel memory split is set to 3G/1G, CONFIG_PAGE_OFFSET is 0xC0000000
User/kernel memory split to 2G/2G, CONFIG_PAGE_OFFSET is   0x80000000

Macro                                               Position                                 The default value             Explain
KERNEL_RAM_VADDR      arch/arm/kernel/head.S +37              0xc0008000      Kernel RAM in the virtual address
PAGE_OFFSET                include/asm-arm/memeory.h +34     0xc0000000      The starting virtual address kernel space
TEXT_OFFSET                 arch/arm/Makefile +247                    0x00008000      The kernel with respect to offset storage space
TEXTADDR                     arch/arm/kernel/head.S +68              0xc0008000      The starting virtual address kernel
PHYS_OFFSET              include/asm-arm/arch-xxx/memory.h   Platform specific    at physical address RAM

The kernel of entrance is stext, which is defined in the arch/arm/kernel/vmlinux.lds.S:

The kernel of entrance is stext, which is defined in the arch/arm/kernel/vmlinux.lds.S:
         ENTRY(stext)
        For vmlinux.lds.S, this is LD script file, this file format and assembly and C programs are different, this paper is not LD script too much introduction, only the kernel used in the content on, details about the LD can be found in the ld.info
        The ENTRY (stext) said the program is in the symbol stext. entrance
        The symbol stext is defined in the arch/arm/kernel/head.S:
        Below we will mainly code arm linux boot set out to make a general introduction, then, we will turn out in detail
        In arch/arm/kernel/head.S 80-162, is the main code arm linux boot:

 80  ENTRY(stext)
 81  ARM_BE8(setend be )                    @ ensure we are in BE8 mode
 82
 83  THUMB( badr    r9, 1f          )       @ Kernel is always entered in ARM.
 84  THUMB( bx      r9              )       @ If this is a Thumb-2 kernel,
 85  THUMB( .thumb                  )       @ switch to Thumb now.
 86  THUMB(1:                       )
 87
 88 #ifdef CONFIG_ARM_VIRT_EXT
 89         bl      __hyp_stub_install
 90 #endif
 91         @ ensure svc mode and all interrupts masked
 92         safe_svcmode_maskall r9
 93
 94         mrc     p15, 0, r9, c0, c0              @ get processor id
 95         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid
 96         movs    r10, r5                         @ invalid processor (r5=0)?
 97  THUMB( it      eq )            @ force fixup-able long branch encoding
 98         beq     __error_p                       @ yes, error 'p'
 99
100 #ifdef CONFIG_ARM_LPAE
101         mrc     p15, 0, r3, c0, c1, 4           @ read ID_MMFR0
102         and     r3, r3, #0xf                    @ extract VMSA support
103         cmp     r3, #5                          @ long-descriptor translation table format?
104  THUMB( it      lo )                            @ force fixup-able long branch encoding
105         blo     __error_lpae                    @ only classic page table format
106 #endif
107
108 #ifndef CONFIG_XIP_KERNEL
109         adr     r3, 2f
110         ldmia   r3, {r4, r8}
111         sub     r4, r3, r4                      @ (PHYS_OFFSET - PAGE_OFFSET)
112         add     r8, r8, r4                      @ PHYS_OFFSET
113 #else
114         ldr     r8, =PLAT_PHYS_OFFSET           @ always constant in this case
115 #endif
116
117         /*
118          * r1 = machine no, r2 = atags or dtb,
119          * r8 = phys_offset, r9 = cpuid, r10 = procinfo
120          */
121         bl      __vet_atags
122 #ifdef CONFIG_SMP_ON_UP
123         bl      __fixup_smp
124 #endif
125 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
126         bl      __fixup_pv_table
127 #endif
128         bl      __create_page_tables
129
130         /*
131          * The following calls CPU specific code in a position independent
132          * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
133          * xxx_proc_info structure selected by __lookup_processor_type
134          * above.
135          *
136          * The processor init function will be called with:
137          *  r1 - machine type
138          *  r2 - boot data (atags/dt) pointer
139          *  r4 - translation table base (low word)
140          *  r5 - translation table base (high word, if LPAE)
141          *  r8 - translation table base 1 (pfn if LPAE)
142          *  r9 - cpuid
143          *  r13 - virtual address for __enable_mmu -> __turn_mmu_on
144          *
145          * On return, the CPU will be ready for the MMU to be turned on,
146          * r0 will hold the CPU control register value, r1, r2, r4, and
147          * r9 will be preserved.  r5 will also be preserved if LPAE.
148          */
149         ldr     r13, =__mmap_switched           @ address to jump to after
150                                                 @ mmu has been enabled
151         badr    lr, 1f                          @ return (PIC) address
152 #ifdef CONFIG_ARM_LPAE
153         mov     r5, #0                          @ high TTBR0
154         mov     r8, r4, lsr #12                 @ TTBR1 is swapper_pg_dir pfn
155 #else
156         mov     r8, r4                          @ set TTBR1 to swapper_pg_dir
157 #endif
158         ldr     r12, [r10, #PROCINFO_INITFUNC]
159         add     r12, r12, r10
160         ret     r12
161 1:      b       __enable_mmu
162  ENDPROC(stext)

Among them, 80 line is to ensure that the kernel is running in SVC mode, and IRQ and FIRQ interrupt has been closed, it is very cautious
The main arm Linux boot can be summarized into the following steps:
    1. Determine processor type       
    2. Create a page table  fixup_pv_table                        
    3. Create a page table            (128That's ok)                            
    4. Call the platform specific__cpu_flushFunction        (In thestruct proc_info_listIn the)                                 
    5. Open mmu                               
    6. Switching data                                
    The final jump tostart_kernel                (In the__switch_dataThe time of the end of the,Call b        start_kernel)
Here, we follow this line, analysis of Code. Gradually



Determination of processor type 1
kernel\arch\arm\kernel\head.S
94         mrc     p15, 0, r9, c0, c0              @ get processor id
 95         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid
 96         movs    r10, r5                         @ invalid processor (r5=0)?
 97  THUMB( it      eq )            @ force fixup-able long branch encoding
 98         beq     __error_p                       @ yes, error 'p'
Line 94: through the CP15 coprocessor registers of the C0 to obtain the processor ID command. Details about the CP15 can refer to the arm manual
Line 95: jump to __lookup_processor_type. in __lookup_processor_type, the processor type stored in the R5
96,Line 98: judgment in R5 processor type is 0, if it is 0, that is not a valid processor type, jump to __error_p (error)

__The lookup_processor_type function is mainly matched according to the processor ID and the system obtained from CPU in proc_info, will match to the proc_info_list base address stored in the R5, 0 corresponding processor type. not found

Below we analysis of __lookup_processor_type function
  arch/arm/kernel/Head-common.S:
126  * This provides a C-API version of __lookup_processor_type
127  */
128 ENTRY(lookup_processor_type)
129         stmfd   sp!, {r4 - r6, r9, lr}
130         mov     r9, r0
131         bl      __lookup_processor_type
132         mov     r0, r5
133         ldmfd   sp!, {r4 - r6, r9, pc}
134 ENDPROC(lookup_processor_type)
139 /*
140  * Read processor ID register (CP#15, CR0), and look up in the linker-built
141  * supported processor list.  Note that we can't use the absolute addresses
142  * for the __proc_info lists since we aren't running with the MMU on
143  * (and therefore, we are not in the correct address space).  We have to
144  * calculate the offset.
145  *
146  *      r9 = cpuid
147  * Returns:
148  *      r3, r4, r6 corrupted
149  *      r5 = proc_info pointer in physical address space
150  *      r9 = cpuid (preserved)
151  */
152 __lookup_processor_type:
153         adr     r3, __lookup_processor_type_data
154         ldmia   r3, {r4 - r6}
155         sub     r3, r3, r4                      @ get offset between virt&phys
156         add     r5, r5, r3                      @ convert virt addresses to
157         add     r6, r6, r3                      @ physical address space
158 1:      ldmia   r5, {r3, r4}                    @ value, mask
159         and     r4, r4, r9                      @ mask wanted bits
160         teq     r3, r4
161         beq     2f
162         add     r5, r5, #PROC_INFO_SZ           @ sizeof(proc_info_list)
163         cmp     r5, r6
164         blo     1b
165         mov     r5, #0                          @ unknown processor
166 2:      ret     lr
167 ENDPROC(__lookup_processor_type)
168
169 /*
170  * Look in <asm/procinfo.h> for information about the __proc_info structure.
171  */
172         .align  2
173         .type   __lookup_processor_type_data, %object
174 __lookup_processor_type_data:
175         .long   .
176         .long   __proc_info_begin
177         .long   __proc_info_end
178         .size   __lookup_processor_type_data, . - __lookup_processor_type_data


152, The 152 line is the function definition
153 row: the address of instruction, The __lookup_processor_type_data function, namely 175th, the address is stored in the R3
        It should be noted that, ADR instruction address, the address is a based on PC, to pay special attention to, this address is the __lookup_processor_type_data address of " " operation; at this time, because MMU is not open, can also be understood as a physical address (physical address). (for details refer to arm instruction manual)
153 row: because the R3 address is 175 lines of position of the address, so after the implementation of: (ldmia stack pointer is decremented, namely R3 decline, memory address numbers larger corresponding register number larger)
        R4 is the __lookup_processor_type_data address
        R5 is the 176 line symbol __proc_info_begin address;
        R6 is the 177 line symbol __proc_info_end address;

        Here need to pay attention to the differences between the address of the link address and runtime. R3
Note :
Adr for the ARM pseudo-instructions, according to reference: understand adr, ldr instructions
Adr is a small range of address read directives, adr instruction will be based on pc relative offset address value read to the scratchpad
So the value of r3 = (the distance between the address of the tag __lookup_processor_type_data and this instruction) + (the address of this instruction)
= The address of the tag __lookup_processor_type_data


#Update: This address is the actual runtime address because it refers to the PC value
Ex. If our Kernel is linked to the address of 0xC0008000
And __lookup_processor_type_data in the Kernel offset 0x514
(That is, the link address of __lookup_processor_type_data is 0xC0008000 + 0x514 = 0xC0008514)
But if the actual operation of our Kernel is moved to 0x30008000 (physical memory address) to perform
Then the value of r3 is 0x30008000 + 0x514 = 0x30008514, rather than the original link address 0xC0008514
And ldmia r3, {r4 - r6} The final result of this instruction is:
R4 = the address of the tag __lookup_processor_type_data
R5 = address of __proc_info_begin
R6 = address of __proc_info_end
(Ldmia will load the value of the bottom address into a relatively small scratchpad, and r4 compared to r5, r6 is "relatively small" scratchpad, and so on )
The last r3 value is the address of the next instruction of .long__proc_info_end

Also, based on the reference: arm linux kernel from the entrance to the start_kernel code analysis
It is important to note that the difference between the link address and the runtime address
In this r4 store the link address (virtual address) (= 0xC0008514)
And r3 is stored at run-time address (physical address )( 0x30008514)
__Proc_info_begin and __proc_info_end are in arch/arm/kernel/vmlinux.lds.S:

 17         VMLINUX_SYMBOL(__proc_info_begin) = .;                          \
 18         *(.proc.info.init)                                              \
 19         VMLINUX_SYMBOL(__proc_info_end) = .;

Here is the statement of two variables: __proc_info_begin and __proc_info_end, wherein the equal sign "." location counter (details please refer to the ld.info)
        This three line means: __proc_info_begin position, put all the files in the ".Proc.info.init" the contents of the section, and then followed by the location of the __proc_info_end
        Kernel uses struct proc_info_list to describe processor type

In include \arch\arm\include\asm\procinfo.h:

29 struct proc_info_list {
 30         unsigned int            cpu_val;
 31         unsigned int            cpu_mask;
 32         unsigned long           __cpu_mm_mmu_flags;     /* used by head.S */
 33         unsigned long           __cpu_io_mmu_flags;     /* used by head.S */
 34         unsigned long           __cpu_flush;            /* used by head.S */
 35         const char              *arch_name;
 36         const char              *elf_name;
 37         unsigned int            elf_hwcap;
 38         const char              *cpu_name;
 39         struct processor        *proc;
 40         struct cpu_tlb_fns      *tlb;
 41         struct cpu_user_fns     *user;
 42         struct cpu_cache_fns    *cache;
 43 };
  We present a case of arm7v, the processor is v7l

In arch/arm/mm/proc-v7.S

459         .section ".rodata"
460
461         string  cpu_arch_name, "armv7"
462         string  cpu_elf_name, "v7"
463         .align
464
465         .section ".proc.info.init", #alloc, #execinstr
466
467         /*
468          * Standard v7 proc info content
469          */
470 .macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions
471         ALT_SMP(.long   PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
472                         PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags)
473         ALT_UP(.long    PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
474                         PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)
475         .long   PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \
476                 PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags
477         W(b)    \initfunc
478         .long   cpu_arch_name
479         .long   cpu_elf_name
480         .long   HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \
481                 HWCAP_EDSP | HWCAP_TLS | \hwcaps
482         .long   cpu_v7_name
483         .long   \proc_fns
484         .long   v7wbi_tlb_fns
485         .long   v6_user_fns
486         .long   v7_cache_fns
487 .endm

534         /*
535          * ARM Ltd. Cortex A7 processor.
536          */
537         .type   __v7_ca7mp_proc_info, #object
538 __v7_ca7mp_proc_info:
539         .long   0x410fc070
540         .long   0xff0ffff0
541         __v7_proc __v7_ca7mp_setup
542         .size   __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info

From the 465 row, we can see the __v7_proc_info in the ".Proc.info.init" section
        The control of struct proc_info_list, we can see that __cpu_flush is defined in the 480 elements, namely __v7_ca7mp_setup. (we will be in the " __cpu_flush function " 4 calling platform specific section in the detailed analysis of this part of the content.)
From the above we can see the content: R5 __proc_info_begin is the starting address of proc_info_list, R6 __proc_info_end is in the end address of proc_info_list
Line 153: from the above analysis we can know that the R3 is stored in the physical address __lookup_processor_type_data, and R4 storage is the virtual address __lookup_processor_type_data, this line is the calculated difference physical address the current program running and virtual address, save it to a R3
176 row: virtual address will be stored in R5 (__proc_info_begin) into a physical address
177 row: virtual address will be stored in R6 (__proc_info_end) into a physical address
176 line: struct control proc_info_list, can know, this is the current proc_info cpu_val and cpu_mask respectively, R3, R4
Line 153: store in R9 processor ID (75 arch/arm/kernel/head.S), by using the Boolean and operator and R4 cpu_mask, we get the required value
Line 154: 153 in a row the values obtained were compared with R3 in cpu_val
176 line: if they are equal, we find that the corresponding processor type, jumped to 160 lines, return
177 row: (if not equal), the R5 pointing to the next proc_info,
177 row: compared with R6, check whether the __proc_info_end
177 line: if not to __proc_info_end, show that the proc_info configuration, returns 176 rows to find
177 row: get here, that all proc_info are matched, but no match is found, R5 is set to 0(unknown processor)
166 row: Return

235  *      __v7_setup
236  *
237  *      Initialise TLB, Caches, and MMU state ready to switch the MMU
238  *      on.  Return in r0 the new CP15 C1 control register setting.
239  *
240  *      This should be able to cover all ARMv7 cores.
241  *
242  *      It is assumed that:
243  *      - cache type register is implemented
244  */
245 __v7_ca5mp_setup:
246 __v7_ca9mp_setup:
247 __v7_cr7mp_setup:
248         mov     r10, #(1 << 0)                  @ Cache/TLB ops broadcasting
249         b       1f
250 __v7_ca7mp_setup:
251 __v7_ca12mp_setup:
252 __v7_ca15mp_setup:
253 __v7_b15mp_setup:
254 __v7_ca17mp_setup:
255         mov     r10, #0
256 1:
257 #ifdef CONFIG_SMP
258         ALT_SMP(mrc     p15, 0, r0, c1, c0, 1)
259         ALT_UP(mov      r0, #(1 << 6))          @ fake it for UP
260         tst     r0, #(1 << 6)                   @ SMP/nAMP mode enabled?
261         orreq   r0, r0, #(1 << 6)               @ Enable SMP/nAMP mode
262         orreq   r0, r0, r10                     @ Enable CPU-specific SMP bits
263         mcreq   p15, 0, r0, c1, c0, 1
264 #endif
265         b       __v7_setup

321 __v7_setup:
322         adr     r12, __v7_setup_stack           @ the local stack
323         stmia   r12, {r0-r5, r7, r9, r11, lr}
324         bl      v7_flush_dcache_louis
325         ldmia   r12, {r0-r5, r7, r9, r11, lr}
326
327         mrc     p15, 0, r0, c0, c0, 0           @ read main ID register
328         and     r10, r0, #0xff000000            @ ARM?
329         teq     r10, #0x41000000
330         bne     3f
331         and     r5, r0, #0x00f00000             @ variant
332         and     r6, r0, #0x0000000f             @ revision
333         orr     r6, r6, r5, lsr #20-4           @ combine variant and revision
334         ubfx    r0, r0, #4, #12                 @ primary part number
335
336         /* Cortex-A8 Errata */
337         ldr     r10, =0x00000c08                @ Cortex-A8 primary part number
338         teq     r0, r10
339         bne     2f
340 #if defined(CONFIG_ARM_ERRATA_430973) && !defined(CONFIG_ARCH_MULTIPLATFORM)
341
342         teq     r5, #0x00100000                 @ only present in r1p*
343         mrceq   p15, 0, r10, c1, c0, 1          @ read aux control register
344         orreq   r10, r10, #(1 << 6)             @ set IBE to 1
345         mcreq   p15, 0, r10, c1, c0, 1          @ write aux control register
346 #endif
347 #ifdef CONFIG_ARM_ERRATA_458693
348         teq     r6, #0x20                       @ only present in r2p0
349         mrceq   p15, 0, r10, c1, c0, 1          @ read aux control register
350         orreq   r10, r10, #(1 << 5)             @ set L1NEON to 1
351         orreq   r10, r10, #(1 << 9)             @ set PLDNOP to 1
352         mcreq   p15, 0, r10, c1, c0, 1          @ write aux control register
353 #endif
354 #ifdef CONFIG_ARM_ERRATA_460075
355         teq     r6, #0x20                       @ only present in r2p0
356         mrceq   p15, 1, r10, c9, c0, 2          @ read L2 cache aux ctrl register
357         tsteq   r10, #1 << 22
358         orreq   r10, r10, #(1 << 22)            @ set the Write Allocate disable bit
359         mcreq   p15, 1, r10, c9, c0, 2          @ write the L2 cache aux ctrl register
360 #endif
361         b       3f
362
363         /* Cortex-A9 Errata */
364 2:      ldr     r10, =0x00000c09                @ Cortex-A9 primary part number
365         teq     r0, r10
366         bne     3f
367 #ifdef CONFIG_ARM_ERRATA_742230
368         cmp     r6, #0x22                       @ only present up to r2p2
369         mrcle   p15, 0, r10, c15, c0, 1         @ read diagnostic register
370         orrle   r10, r10, #1 << 4               @ set bit #4
371         mcrle   p15, 0, r10, c15, c0, 1         @ write diagnostic register
372 #endif
373 #ifdef CONFIG_ARM_ERRATA_742231
374         teq     r6, #0x20                       @ present in r2p0
375         teqne   r6, #0x21                       @ present in r2p1
376         teqne   r6, #0x22                       @ present in r2p2
377         mrceq   p15, 0, r10, c15, c0, 1         @ read diagnostic register
378         orreq   r10, r10, #1 << 12              @ set bit #12
379         orreq   r10, r10, #1 << 22              @ set bit #22
380         mcreq   p15, 0, r10, c15, c0, 1         @ write diagnostic register
381 #endif
382 #ifdef CONFIG_ARM_ERRATA_743622
383         teq     r5, #0x00200000                 @ only present in r2p*
384         mrceq   p15, 0, r10, c15, c0, 1         @ read diagnostic register
385         orreq   r10, r10, #1 << 6               @ set bit #6
386         mcreq   p15, 0, r10, c15, c0, 1         @ write diagnostic register
387 #endif
388 #if defined(CONFIG_ARM_ERRATA_751472) && defined(CONFIG_SMP)
389         ALT_SMP(cmp r6, #0x30)                  @ present prior to r3p0
390         ALT_UP_B(1f)
391         mrclt   p15, 0, r10, c15, c0, 1         @ read diagnostic register
392         orrlt   r10, r10, #1 << 11              @ set bit #11
393         mcrlt   p15, 0, r10, c15, c0, 1         @ write diagnostic register
394 1:
395 #endif
396
397         /* Cortex-A15 Errata */
398 3:      ldr     r10, =0x00000c0f                @ Cortex-A15 primary part number
399         teq     r0, r10
400         bne     4f
401
402 #ifdef CONFIG_ARM_ERRATA_773022
403         cmp     r6, #0x4                        @ only present up to r0p4
404         mrcle   p15, 0, r10, c1, c0, 1          @ read aux control register
405         orrle   r10, r10, #1 << 1               @ disable loop buffer
406         mcrle   p15, 0, r10, c1, c0, 1          @ write aux control register
407 #endif
408
409 4:      mov     r10, #0
410         mcr     p15, 0, r10, c7, c5, 0          @ I+BTB cache invalidate
411 #ifdef CONFIG_MMU
412         mcr     p15, 0, r10, c8, c7, 0          @ invalidate I + D TLBs
413         v7_ttb_setup r10, r4, r8, r5            @ TTBCR, TTBRx setup
414         ldr     r5, =PRRR                       @ PRRR
415         ldr     r6, =NMRR                       @ NMRR
416         mcr     p15, 0, r5, c10, c2, 0          @ write PRRR
417         mcr     p15, 0, r6, c10, c2, 1          @ write NMRR
418 #endif
419         dsb                                     @ Complete invalidations
420 #ifndef CONFIG_ARM_THUMBEE
421         mrc     p15, 0, r0, c0, c1, 0           @ read ID_PFR0 for ThumbEE
422         and     r0, r0, #(0xf << 12)            @ ThumbEE enabled field
423         teq     r0, #(1 << 12)                  @ check if ThumbEE is present
424         bne     1f
425         mov     r5, #0
426         mcr     p14, 6, r5, c1, c0, 0           @ Initialize TEEHBR to 0
427         mrc     p14, 6, r0, c0, c0, 0           @ load TEECR
428         orr     r0, r0, #1                      @ set the 1st bit in order to
429         mcr     p14, 6, r0, c0, c0, 0           @ stop userspace TEEHBR access
430 1:
431 #endif
432         adr     r5, v7_crval
433         ldmia   r5, {r5, r6}
434  ARM_BE8(orr    r6, r6, #1 << 25)               @ big-endian page tables
435 #ifdef CONFIG_SWP_EMULATE
436         orr     r5, r5, #(1 << 10)              @ set SW bit in "clear"
437         bic     r6, r6, #(1 << 10)              @ clear it in "mmuset"
438 #endif
439         mrc     p15, 0, r0, c1, c0, 0           @ read control register
440         bic     r0, r0, r5                      @ clear bits them
441         orr     r0, r0, r6                      @ set them
442  THUMB( orr     r0, r0, #1 << 30        )       @ Thumb exceptions
443         ret     lr                              @ return to head.S:__ret
444 ENDPROC(__v7_setup)
445
446         .align  2
447 __v7_setup_stack:
448         .space  4 * 11                          @ 11 registers

2. __fixup_pv_table

564 #ifdef __ARMEB__
565 #define LOW_OFFSET      0x4
566 #define HIGH_OFFSET     0x0
567 #else
568 #define LOW_OFFSET      0x0
569 #define HIGH_OFFSET     0x4
570 #endif
571
572 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
573
574 /* __fixup_pv_table - patch the stub instructions with the delta between
575  * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
576  * can be expressed by an immediate shifter operand. The stub instruction
577  * has a form of '(add|sub) rd, rn, #imm'.
578  */
579         __HEAD
580 __fixup_pv_table:
581         adr     r0, 1f
582         ldmia   r0, {r3-r7}
583         mvn     ip, #0
584         subs    r3, r0, r3      @ PHYS_OFFSET - PAGE_OFFSET
585         add     r4, r4, r3      @ adjust table start address
586         add     r5, r5, r3      @ adjust table end address
587         add     r6, r6, r3      @ adjust __pv_phys_pfn_offset address
588         add     r7, r7, r3      @ adjust __pv_offset address
589         mov     r0, r8, lsr #12 @ convert to PFN
590         str     r0, [r6]        @ save computed PHYS_OFFSET to __pv_phys_pfn_offset
591         strcc   ip, [r7, #HIGH_OFFSET]  @ save to __pv_offset high bits
592         mov     r6, r3, lsr #24 @ constant for add/sub instructions
593         teq     r3, r6, lsl #24 @ must be 16MiB aligned
594 THUMB(  it      ne              @ cross section branch )
595         bne     __error
596         str     r3, [r7, #LOW_OFFSET]   @ save to __pv_offset low bits
597         b       __fixup_a_pv_table
598 ENDPROC(__fixup_pv_table)
599
600         .align
601 1:      .long   .
602         .long   __pv_table_begin
603         .long   __pv_table_end
604 2:      .long   __pv_phys_pfn_offset
605         .long   __pv_offset
606
607         .text
608 __fixup_a_pv_table:
609         adr     r0, 3f
610         ldr     r6, [r0]
611         add     r6, r6, r3
612         ldr     r0, [r6, #HIGH_OFFSET]  @ pv_offset high word
613         ldr     r6, [r6, #LOW_OFFSET]   @ pv_offset low word
614         mov     r6, r6, lsr #24
615         cmn     r0, #1
616 #ifdef CONFIG_THUMB2_KERNEL
617         moveq   r0, #0x200000   @ set bit 21, mov to mvn instruction
618         lsls    r6, #24
619         beq     2f
620         clz     r7, r6
621         lsr     r6, #24
622         lsl     r6, r7
623         bic     r6, #0x0080
624         lsrs    r7, #1
625         orrcs   r6, #0x0080
626         orr     r6, r6, r7, lsl #12
627         orr     r6, #0x4000
628         b       2f
629 1:      add     r7, r3
630         ldrh    ip, [r7, #2]
631 ARM_BE8(rev16   ip, ip)
632         tst     ip, #0x4000
633         and     ip, #0x8f00
634         orrne   ip, r6  @ mask in offset bits 31-24
635         orreq   ip, r0  @ mask in offset bits 7-0
636 ARM_BE8(rev16   ip, ip)
637         strh    ip, [r7, #2]
638         bne     2f
639         ldrh    ip, [r7]
640 ARM_BE8(rev16   ip, ip)
641         bic     ip, #0x20
642         orr     ip, ip, r0, lsr #16
643 ARM_BE8(rev16   ip, ip)
644         strh    ip, [r7]
645 2:      cmp     r4, r5
646         ldrcc   r7, [r4], #4    @ use branch for delay slot
647         bcc     1b
648         bx      lr
649 #else
650 #ifdef CONFIG_CPU_ENDIAN_BE8
651         moveq   r0, #0x00004000 @ set bit 22, mov to mvn instruction
652 #else
653         moveq   r0, #0x400000   @ set bit 22, mov to mvn instruction
654 #endif
655         b       2f
656 1:      ldr     ip, [r7, r3]
657 #ifdef CONFIG_CPU_ENDIAN_BE8
658         @ in BE8, we load data in BE, but instructions still in LE
659         bic     ip, ip, #0xff000000
660         tst     ip, #0x000f0000 @ check the rotation field
661         orrne   ip, ip, r6, lsl #24 @ mask in offset bits 31-24
662         biceq   ip, ip, #0x00004000 @ clear bit 22
663         orreq   ip, ip, r0      @ mask in offset bits 7-0
664 #else
665         bic     ip, ip, #0x000000ff
666         tst     ip, #0xf00      @ check the rotation field
667         orrne   ip, ip, r6      @ mask in offset bits 31-24
668         biceq   ip, ip, #0x400000       @ clear bit 22
669         orreq   ip, ip, r0      @ mask in offset bits 7-0
670 #endif
671         str     ip, [r7, r3]
672 2:      cmp     r4, r5
673         ldrcc   r7, [r4], #4    @ use branch for delay slot
674         bcc     1b
675         ret     lr
676 #endif
677 ENDPROC(__fixup_a_pv_table)
678
679         .align
680 3:      .long __pv_offset



580, The 580 line is the function definition
581 row: the address of instruction, The 1f function, namely 581th, the address is stored in the R0
        It should be noted that, ADR instruction address, the address is a based on PC, to pay special attention to, this address is the 1F address of " " operation; at this time, because MMU is not open, can also be understood as a physical address (physical address). (for details refer to arm instruction manual)
581 row: because the R0 address is 601 lines of position of the address, so after the implementation of: (ldmia stack pointer is decremented, namely R0 decline, memory address numbers larger corresponding register number larger)
        R3 is the 1F address
        R4 is the 602 line symbol __pv_table_begin address(0xc0e56f7c);
        R5 is the 603 line symbol __pv_table_end address(0xc0e57750);
        R6 is the 604 line symbol __pv_phys_pfn_offset address(0xc0ed0684);
        R7 is the 605 line symbol __pv_offset address(0xc0ed0688);

        Here need to pay attention to the differences between the address of the link address and runtime. R0
Note :
Adr for the ARM pseudo-instructions, according to reference: understand adr, ldr instructions
Adr is a small range of address read directives, adr instruction will be based on pc relative offset address value read to the scratchpad
So the value of r0 = (the distance between the address of the tag 1F and this instruction) + (the address of this instruction)
= The address of the tag 1F


#Update: This address is the actual runtime address because it refers to the PC value
Ex. If our Kernel is linked to the address of 0xC0008000
And 1F in the Kernel offset 0xE4EF7C
(That is, the link address of 1F is 0xC0008000 + E4EF7C = 0xC0E56F7C)
But if the actual operation of our Kernel is moved to 0x30008000 (physical memory address) to perform
Then the value of r3 is 0x30008000 + 0xE4EF7C = 0x30E56F7C, rather than the original link address 0xC0E56F7C
And ldmia   r0, {r3-r7} The final result of this instruction is:

 R3 = the address of this tag 1F
 R4 = address of __pv_table_begin address(0xc0e56f7c);
 R5 = address of __pv_table_end address(0xc0e57750);
 R6 = address of __pv_phys_pfn_offset address(0xc0ed0684);
 R7 = address of __pv_offset (0xc0ed0688);
       
Also, based on the reference: arm linux kernel from the entrance to the start_kernel code analysis
It is important to note that the difference between the link address and the runtime address
In this r3 store the link address (virtual address) (= 0xC0E56F7C)
And r0 is stored at run-time address (physical address )(0x30E56F7C)
 

4 comments: