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,
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).
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
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:
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
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
__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
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)
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:
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)
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
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
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
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)
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)
ReplyDeleteNICE for giving a chance to share ideas for your comuty i really thanks for that great post.
thanks for article
best post thanks for shared this blog.
ReplyDeleteFull Stack Training in Chennai | Certification | Online Training Course | Full Stack Training in Bangalore | Certification | Online Training Course | Full Stack Training in Hyderabad | Certification | Online Training Course | Full Stack Training in Pune | Certification | Online Training Course | Full Stack Training | Certification | Full Stack Online Training Course
Also, based on the reference: arm linux kernel from the entrance to the start_kernel code analysis
ReplyDeleteIt is important to note that the difference between the link address and the runtime address good things
DevOps Online Training in Chennai
DevOps Training in Bangalore
DevOps Training in Hyderabad
DevOps Training in Coimbatore
DevOps Online Training
DevOps Training
Really very happy to say, your post is very interesting to read embedded system. I never stop myself to say something about it. You’re doing a great job. Keep it up.
ReplyDeleteDevOps Training in Chennai
DevOps Online Training in Chennai
DevOps Training in Bangalore
DevOps Training in Hyderabad
DevOps Training in Coimbatore
DevOps Training
DevOps Online Training