arm linux kernel 从入口到start_kernel 的代码分析
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"
armlinuxkernel代码分 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)