微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > arm linux kernel 从入口到start_kernel 的代码分析

arm linux kernel 从入口到start_kernel 的代码分析

时间:11-09 来源:互联网 点击:



231行: 通过pc值的高12位(右移20位),得到kernel的section,并存储到r6中.因为当前是通过运行时地址得到的kernel的section,因而是物理地址.
232行: r3 = r7 (r6 < 20); flags + kernel base,得到页表中需要设置的值.
233行: 设置页表: mem[r4 + r6 * 4] = r3
这里,因为页表的每一项是32 bits(4 bytes),所以要乘以4(<2).
上面这三行,设置了kernel的第一个section(物理地址所在的page entry)的页表项
239, 240行: TEXTADDR是内核的起始虚拟地址(0xc8), 这两行是设置kernel起始虚拟地址的页表项(注意,这里设置的页表项和上面的231 - 233行设置的页表项是不同的 )
执行完后,r0指向kernel的第2个section的虚拟地址所在的页表项.
/* TODO: 这两行的code很奇怪,为什么要先取TEXTADDR的高8位(Bit[31:24])0xff,然后再取后面的8位 (Bit[23:20])0x00f00*/
242行: 这一行计算kernel镜像的大小(bytes).
_end 是在vmlinux.lds.S中162行定义的,标记kernel的结束位置(虚拟地址):
00158 .bss : {
00159 __bss_start = .; /* BSS */
00160 *(.bss)
00161 *(COMMON)
00162 _end = .;
00163 }
kernel的size = _end - PAGE_OFFSET -1, 这里 减1的原因是因为 _end 是 location counter,它的地址是kernel镜像后面的一个byte的地址.
243行: 地址右移20位,计算出kernel有多少sections(也就是有多少兆,因为段描述符每个可以映射1MiB的虚拟地址),并将结果存到r6中
245 - 248行: 这几行用来填充kernel所有section虚拟地址对应的页表项.
253行: 将r0设置为RAM第一兆虚拟地址的页表项地址(page entry)
254行: r7中存储的是mmu flags, 逻辑或上RAM的起始物理地址,得到RAM第一个MB页表项的值.
255行: 设置RAM的第一个MB虚拟地址的页表.
上面这三行是用来设置RAM中第一兆虚拟地址的页表. 之所以要设置这个页表项的原因是RAM的第一兆内存中可能存储着boot params.
这样,kernel所需要的基本的页表我们都设置完了, 如下图所示:

下面是linux-2.6.30.4中的arch/arm/kernel/head.S,代码有一些不同,但是效果一样:

1: /*
2:  *  linux/arch/arm/kernel/head.S
3:  *
4:  *  Copyright (C) 1994-2002 Russell King
5:  *  Copyright (c) 2003 ARM Limited
6:  *  All Rights Reserved
7:  *
8:  * This program is free software; you can redistribute it and/or modify
9:  * it under the terms of the GNU General Public License version 2 as
10:  * published by the Free Software Foundation.
11:  *
12:  *  Kernel startup code for all 32-bit CPUs
13:  */
14: #include 
15: #include 
16: 
17: #include 
18: #include 
19: #include 
20: #include 
21: #include 
22: #include 
23: #include 
24: 
25: #if (PHYS_OFFSET & 0x001fffff)
26: #error "PHYS_OFFSET must be at an even 2MiB boundary!"
27: #endif
28: 
29: #define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)
30: #define KERNEL_RAM_PADDR    (PHYS_OFFSET + TEXT_OFFSET)
31: 
32: 
33: /*
34:  * swapper_pg_dir is the virtual address of the initial page table.
35:  * We place the page tables 16K below KERNEL_RAM_VADDR.  Therefore, we must
36:  * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect
37:  * the least significant 16 bits to be 0x8, but we could probably
38:  * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4.
39:  */
40: #if (KERNEL_RAM_VADDR & 0xffff) != 0x8
41: #error KERNEL_RAM_VADDR must start at 0xXXXX8
42: #endif
43: 
44:     .globl    swapper_pg_dir
45:     .equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4
46: 
47:     .macro    pgtbl, rd
48:     ldr    \rd, =(KERNEL_RAM_PADDR - 0x4)
49:     .endm
50: 
51: #ifdef CONFIG_XIP_KERNEL
52: #define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
53: #define KERNEL_END    _edata_loc
54: #else
55: #define KERNEL_START    KERNEL_RAM_VADDR
56: #define KERNEL_END    _end
57: #endif
58: 
59: /*
60:  * Kernel startup entry point.
61:  * 
62:  *
63:  * This is normally called from the decompressor code.  The requirements
64:  * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
65:  * r1 = machine nr, r2 = atags pointer.
66:  *
67:  * This code is mostly position independent, so if you link the kernel at
68:  * 0xc8, you call this at __pa(0xc8).
69:  *
70:  * See linux/arch/arm/tools/mach-types for the complete list of machine
71:  * numbers for r1.
72:  *
73:  * Were trying to keep crap to a minimum; DO NOT add any machine specific
74:  * crap here - thats what the boot loader (or in extreme, well justified
75:  * circumstances, zImage) is for.
76:  */
77:     .section ".text.head", "ax"
78: ENTRY(stext)
79:     msr    cpsr_c, #PSR_F_BIT  PSR_I_BIT  SVC_MODE @ ensure svc mode
80:                         @ and irqs disabled
81:     mrc    p15, 0, r9, c0, c0        @ get processor id
82:     bl    __lookup_processor_type        @ r5=procinfo r9=cpuid
83:     movs    r10, r5                @ invalid processor (r5=0)?
84:     beq    __error_p            @ yes, error p
85:     bl    __lookup_machine_type        @ r5=machinfo
86:     movs    r8, r5                @ invalid machine (r5=0)?
87:     beq    __error_a            @ yes, error a
88:     bl    __vet_atags
89:     bl    __create_page_tables
90: 
91:     /*
92:      * The following calls CPU specific code in a position independent
93:      * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
94:      * xxx_proc_info structure selected by __lookup_machine_type
95:      * above.  On return, the CPU will be ready for the MMU to be
96:      * turned on, and r0 will hold the CPU control register value.
97:      */
98:     ldr    r13, __switch_data        @ address to jump to after
99:                         @ mmu has been enabled
100:     adr    lr, __enable_mmu        @ return (PIC) address
101:     add    pc, r10, #PROCINFO_INITFUNC
102: ENDPROC(stext)
103: 
104: #if defined(CONFIG_SMP)
105: ENTRY(secondary_startup)
106:     /*
107:      * Common entry point for secondary CPUs.
108:      *
109:      * Ensure that were in SVC mode, and IRQs are disabled.  Lookup
110:      * the processor type - there is no need to check the machine type
:      * as it has already been validated by the primary processor.
112:      */
113:     msr    cpsr_c, #PSR_F_BIT  PSR_I_BIT  SVC_MODE
114:     mrc    p15, 0, r9, c0, c0        @ get processor id
115:     bl    __lookup_processor_type
116:     movs    r10, r5                @ invalid processor?
117:     moveq    r0, #p            @ yes, error p
118:     beq    __error
119: 
120:     /*
121:      * Use the page tables supplied from  __cpu_up.
122:      */
123:     adr    r4, __secondary_data
124:     ldmia    r4, {r5, r7, r13}        @ address to jump to after
125:     sub    r4, r4, r5            @ mmu has been enabled
126:     ldr    r4, [r7, r4]            @ get secondary_data.pgdir
127:     adr    lr, __enable_mmu        @ return address
128:     add    pc, r10, #PROCINFO_INITFUNC    @ initialise processor
129:                         @ (return control reg)
130: ENDPROC(secondary_startup)
131: 
132:     /*
133:      * r6  = &secondary_data
134:      */
135: ENTRY(__secondary_switched)
136:     ldr    sp, [r7, #4]            @ get secondary_data.stack
137:     mov    fp, #0
138:     b    secondary_start_kernel
139: ENDPROC(__secondary_switched)
140: 
141:     .type    __secondary_data, %object
142: __secondary_data:
143:     .long    .
144:     .long    secondary_data
145:     .long    __secondary_switched
146: #endif /* defined(CONFIG_SMP) */
147: 
148: 
149: 
150: /*
151:  * Setup common bits before finally enabling the MMU.  Essentially
152:  * this is just loading the page table pointer and domain access
153:  * registers.
154:  */
155: __enable_mmu:
156: #ifdef CONFIG_ALIGNMENT_TRAP
157:     orr    r0, r0, #CR_A
158: #else
159:     bic    r0, r0, #CR_A
160: #endif
161: #ifdef CONFIG_CPU_DCACHE_DISABLE
162:     bic    r0, r0, #CR_C
163: #endif
164: #ifdef CONFIG_CPU_BPREDICT_DISABLE
165:     bic    r0, r0, #CR_Z
166: #endif
167: #ifdef CONFIG_CPU_ICACHE_DISABLE
168:     bic    r0, r0, #CR_I
169: #endif
170:     mov    r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER)  \
171:               domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER)  \
172:               domain_val(DOMAIN_TABLE, DOMAIN_MANAGER)  \
173:               domain_val(DOMAIN_IO, DOMAIN_CLIENT))
174:     mcr    p15, 0, r5, c3, c0, 0        @ load domain access register
175:     mcr    p15, 0, r4, c2, c0, 0        @ load page table pointer
176:     b    __turn_mmu_on
177: ENDPROC(__enable_mmu)
178: 
179: /*
180:  * Enable the MMU.  This completely changes the structure of the visible
181:  * memory space.  You will not be able to trace execution through this.
182:  * If you have an enquiry about this, *please* check the linux-arm-kernel
183:  * mailing list archives BEFORE sending another post to the list.
184:  *
185:  *  r0  = cp#15 control register
186:  *  r13 = *virtual* address to jump to upon completion
187:  *
188:  * other registers depend on the function called upon completion
189:  */
190:     .align    5
191: __turn_mmu_on:
192:     mov    r0, r0
193:     mcr    p15, 0, r0, c1, c0, 0        @ write control reg
194:     mrc    p15, 0, r3, c0, c0, 0        @ read id reg
195:     mov    r3, r3
196:     mov    r3, r3
197:     mov    pc, r13
198: ENDPROC(__turn_mmu_on)
199: 
200: 
201: /*
202:  * Setup the initial page tables.  We only setup the barest
203:  * amount which are required to get the kernel running, which
204:  * generally means mapping in the kernel code.
205:  *
206:  * r8  = machinfo
207:  * r9  = cpuid
208:  * r10 = procinfo
209:  *
210:  * Returns:
211:  *  r0, r3, r6, r7 corrupted
212:  *  r4 = physical page table address
213:  */
214: __create_page_tables:
215:     pgtbl    r4                @ page table address
216: 
217:     /*
218:      * Clear the 16K level 1 swapper page table
219:      */
220:     mov    r0, r4
221:     mov    r3, #0
:     add    r6, r0, #0x4
223: 1:    str    r3, [r0], #4
224:     str    r3, [r0], #4
225:     str    r3, [r0], #4
226:     str    r3, [r0], #4
227:     teq    r0, r6
228:     bne    1b
229: 
230:     ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
231: 
232:     /*
233:      * Create identity mapping for first MB of kernel to
234:      * cater for the MMU enable.  This identity mapping
235:      * will be removed by paging_init().  We use our current program
236:      * counter to determine corresponding section base address.
237:      */
238:     mov    r6, pc, lsr #20            @ start of kernel section
239:     orr    r3, r7, r6, lsl #20        @ flags + kernel base
240:     str    r3, [r4, r6, lsl #2]        @ identity mapping
241: 
242:     /*
243:      * Now setup the pagetables for our kernel direct
244:      * mapped region.
245:      */
246:     add    r0, r4,  #(KERNEL_START & 0xff) >> 18
247:     str    r3, [r0, #(KERNEL_START & 0x00f00) >> 18]!
248:     ldr    r6, =(KERNEL_END - 1)
249:     add    r0, r0, #4
250:     add    r6, r4, r6, lsr #18
251: 1:    cmp    r0, r6
252:     add    r3, r3, #1 < 20
253:     strls    r3, [r0], #4
254:     bls    1b
255: 
256: #ifdef CONFIG_XIP_KERNEL
257:     /*
258:      * Map some ram to cover our .data and .bss areas.
259:      */
260:     orr    r3, r7, #(KERNEL_RAM_PADDR & 0xff)
261:     .if    (KERNEL_RAM_PADDR & 0x00f00)
262:     orr    r3, r3, #(KERNEL_RAM_PADDR & 0x00f00)
263:     .endif
264:     add    r0, r4,  #(KERNEL_RAM_VADDR & 0xff) >> 18
265:     str    r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00) >> 18]!
266:     ldr    r6, =(_end - 1)
267:     add    r0, r0, #4
268:     add    r6, r4, r6, lsr #18
269: 1:    cmp    r0, r6
270:     add    r3, r3, #1 < 20
271:     strls    r3, [r0], #4
272:     bls    1b
273: #endif
274: 
275:     /*
276:      * Then map first 1MB of ram in case it contains our boot params.
277:      */
278:     add    r0, r4, #PAGE_OFFSET >> 18
279:     orr    r6, r7, #(PHYS_OFFSET & 0xff)
280:     .if    (PHYS_OFFSET & 0x00f00)
281:     orr    r6, r6, #(PHYS_OFFSET & 0x00f00)
282:     .endif
283:     str    r6, [r0]
284: 
285: #ifdef CONFIG_DEBUG_LL
286:     ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
287:     /*
288:      * Map in IO space for serial debugging.
289:      * This allows debug messages to be output
290:      * via a serial console before paging_init.
291:      */
292:     ldr    r3, [r8, #MACHINFO_PGOFFIO]
293:     add    r0, r4, r3
294:     rsb    r3, r3, #0x4            @ PTRS_PER_PGD*sizeof(long)
295:     cmp    r3, #0x0800            @ limit to 512MB
296:     movhi    r3, #0x0800
297:     add    r6, r0, r3
298:     ldr    r3, [r8, #MACHINFO_PHYSIO]
299:     orr    r3, r3, r7
300: 1:    str    r3, [r0], #4
301:     add    r3, r3, #1 < 20
302:     teq    r0, r6
303:     bne    1b
304: #if defined(CONFIG_ARCH_NETWINDER)  defined(CONFIG_ARCH_CATS)
305:     /*
306:      * If were using the NetWinder or CATS, we also need to map
307:      * in the 16550-type serial port for the debug messages
308:      */
309:     add    r0, r4, #0xff >> 18
310:     orr    r3, r7, #0x7c
311:     str    r3, [r0]
312: #endif
313: #ifdef CONFIG_ARCH_RPC
314:     /*
315:      * Map in screen at 0x02 & SCREEN2_BASE
316:      * Similar reasons here - for debug.  This is
317:      * only for Acorn RiscPC architectures.
318:      */
319:     add    r0, r4, #0x02 >> 18
320:     orr    r3, r7, #0x02
321:     str    r3, [r0]
322:     add    r0, r4, #0xd8 >> 18
323:     str    r3, [r0]
324: #endif
325: #endif
326:     mov    pc, lr
327: ENDPROC(__create_page_tables)
328:     .ltorg
329: 
330: #include "head-common.S"

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top