linux 3.6 启动源码分析(一)
来源:互联网 发布:js文字超出显示省略号 编辑:程序博客网 时间:2024/06/01 07:24
转载地址:http://blog.csdn.net/qing_ping/article/details/17350733
作为需要和硬件打交道的工程师来说,比较关注的是驱动和CPU初始化这一块。所以我沿着启动的路线,重点学习一下和硬件相关的代码。就从linux解压的入口说起。学习阶段,基本是参考大神文章http://blog.chinaunix.net/uid/20543672/cid-6411-list-7.html所写。
linux自解压完成后就跳转到了解压后的内核(也就是vmlinux的bin版本Image),具体的入口可以在arch/arm/kernel/vmlinux.lds.S(最终的链接脚本是通过这个文件产生的)中获得:
这个入口在arch/arm/kernel/head.S中,这个文件就是Linux内核真正启动的地方,是初始化部分的开始,用汇编写成。他必须为后面的C代码做好准备
- 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. * 所有32-bit CPU的内核启动代码
- 13. */
- 14. #include <linux/linkage.h>
- 15. #include <linux/init.h>
- 16.
- 17. #include <asm/assembler.h>
- 18. #include <asm/domain.h>
- 19. #include <asm/ptrace.h>
- 20. #include <asm/asm-offsets.h>
- 21. #include <asm/memory.h>
- 22. #include <asm/thread_info.h>
- 23. #include <asm/system.h>
- 24.
- 25. #ifdef CONFIG_DEBUG_LL
- 26. #include <mach/debug-macro.S>
- 27. #endif
- 28.
- 29. /*
- 30. * swapper_pg_dir 是初始页表的虚拟地址.
- 31. * 我们将页表放在KERNEL_RAM_VADDR以下16K的空间中. 因此我们必须保证
- 32. * KERNEL_RAM_VADDR已经被正常设置.当前, 我们期望的是
- 33. * 这个地址的最后16 bits为0x8000, 但我们或许可以放宽这项限制到
- 34. * KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
- 35. */
- 36. #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
- 37. #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
- 38. #error KERNEL_RAM_VADDR must start at 0xXXXX8000
- 39. #endif
- 40.
- 41. .globl swapper_pg_dir
- 42. .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
- 43.
- 44. /*
- 45. * TEXT_OFFSET 是内核代码(解压后)相对于RAM起始的偏移.
- 46. * 而#TEXT_OFFSET - 0x4000就是页表相对于RAM起始的偏移.
- 47. * 这个宏的作用是将phys(RAM的启示地址)加上页表的偏移,
- 48. * 而得到页表的起始物理地址
- 49. */
- 50. .macro pgtbl, rd, phys
- 51. add \rd, \phys, #TEXT_OFFSET - 0x4000
- 52. .endm
- 53.
- 54. #ifdef CONFIG_XIP_KERNEL
- 55. #define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
- 56. #define KERNEL_END _edata_loc
- 57. #else
- 58. #define KERNEL_START KERNEL_RAM_VADDR
- 59. #define KERNEL_END _end
- 60. #endif
- 61.
- 62. /*
- 63. * 内核启动入口点.
- 64. * —————————
- 65. *
- 66. * 这个入口正常情况下是在解压完成后被调用的.
- 67. * 调用条件: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
- 68. * r1 = machine nr, r2 = atags or dtb pointer.
- 69. * 这些条件在解压完成后会被逐一满足,然后才跳转过来。
- 70. *
- 71. * 这些代码大多数是位置无关的, 如果你的内核入口地址在连接时确定为
- 72. * 0xc0008000, 你调用此函数的物理地址就是 __pa(0xc0008000).
- 73. *
- 74. * 完整的machineID列表,请参见 linux/arch/arm/tools/mach-types
- 75. *
- 76. * 我们尽量让代码简洁; 不在此处添加任何设备特定的代码
- 77. * - 这些特定的初始化代码是boot loader的工作(或在极端情况下,
- 78. * 有充分理由的情况下, 可以由zImage完成)。
- 79. */
- 80. __HEAD
- 81. ENTRY(stext)
- 82. setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ CPU模式设置宏
- 83. @ (进入svc模式并且关闭中断)
- 84. mrc p15, 0, r9, c0, c0 @ 获取处理器id–>r9
- 85. bl __lookup_processor_type @ 返回r5=procinfo r9=cpuid
- 86. movs r10, r5 @ r10=r5,并可以检测r5=0?注意当前r10的值
- 87. THUMB( it eq ) @ force fixup-able long branch encoding
- 88. beq __error_p @ yes, error ’p’如果r5=0,则内核处理器不匹配,出错~死循环
- 89.
- 90. /*
- 91. * 获取RAM的起始物理地址,并保存于 r8 = phys_offset
- 92. * XIP内核与普通在RAM中运行的内核不同
- 93. * (1)CONFIG_XIP_KERNEL
- 94. * 通过运行时计算????
- 95. * (2)正常RAM中运行的内核
- 96. * 通过编译时确定(PLAT_PHYS_OFFSET 一般在arch/arm/mach-xxx/include/mach/memory.h定义)
- 97. *
- 98. */
- 99. #ifndef CONFIG_XIP_KERNEL
- 100. adr r3, 2f
- 101. ldmia r3, {r4, r8}
- 102. sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
- 103. add r8, r8, r4 @ PHYS_OFFSET
- 104. #else
- 105. ldr r8, =PLAT_PHYS_OFFSET
- 106. #endif
- 107.
- 108. /*
- 109. * r1 = machine no, r2 = atags or dtb,
- 110. * r8 = phys_offset, r9 = cpuid, r10 = procinfo
- 111. */
- 112. bl __vet_atags @ 判断r2(内核启动参数)指针的有效性
- 113. #ifdef CONFIG_SMP_ON_UP
- 114. bl __fixup_smp @ ???如果运行SMP内核在单处理器系统中启动,做适当调整
- 115. #endif
- 116. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
- 117. bl __fixup_pv_table @ ????根据内核在内存中的位置修正物理地址与虚拟地址的转换机制
- 118. #endif
- 119. bl __create_page_tables @ 初始化页表!
- 120.
- 121. /*
- 122. * 以下使用位置无关的方法调用的是CPU特定代码。
- 123. * 详情请见arch/arm/mm/proc-*.S
- 124. * r10 = xxx_proc_info 结构体的基地址(在上面__lookup_processor_type函数中选中的)
- 125. * 返回时, CPU 已经为 MMU 的启动做好了准备,
- 126. * 且 r0 保存着CPU控制寄存器的值.
- 127. */
- 128. ldr r13, =__mmap_switched @ 在MMU启动之后跳入的第一个虚拟地址
- 129. adr lr, BSYM(1f) @ 设置返回的地址(PIC)
- 130. mov r8, r4 @ 将swapper_pg_dir的物理地址放入r8,
- 131. @ 以备__enable_mmu中将其放入TTBR1
- 132. ARM( add pc, r10, #PROCINFO_INITFUNC ) @ 跳入构架相关的初始化处理器函数(例如A8的是__v7_setup)
- 133. THUMB( add r12, r10, #PROCINFO_INITFUNC ) @主要目的只配置CP15(包括缓存配置)
- 134. THUMB( mov pc, r12 )
- 135. 1: b __enable_mmu @ 启动MMU
- 136. ENDPROC(stext)
- 137. .ltorg
- 138. #ifndef CONFIG_XIP_KERNEL
- 139. 2: .long .
- 140. .long PAGE_OFFSET
- 141. #endif
- 142.
- 143. /*
- 144. * 创建初始化页表.我们只创建最基本的页表,
- 145. * 以满足内核运行的需要,
- 146. * 这通常意味着仅映射内核代码本身.
- 147. *
- 148. * r8 = phys_offset, r9 = cpuid, r10 = procinfo
- 149. *
- 150. * 返回:
- 151. *r0, r3, r5-r7 被篡改
- 152. *r4 = 页表物理地址
- 153. */
- 154. __create_page_tables:
- 155. pgtbl r4, r8 @ 现在r4 = 页表的起始物理地址
- 156.
- 157. /*
- 158. * 清零16K的一级初始页表区
- 159. * 这些页表在内核自解压时被设置过
- 160. * (此时MMU已关闭)
- 161. */
- 162. mov r0, r4
- 163. mov r3, #0
- 164. add r6, r0, #0x4000
- 165. 1: str r3, [r0], #4
- 166. str r3, [r0], #4
- 167. str r3, [r0], #4
- 168. str r3, [r0], #4
- 169. teq r0, r6
- 170. bne 1b
- 171.
- 172. /*
- 173. * 获取节描述符的默认配置(除节基址外的其他配置)
- 174. * 这个数据依构架而不同,数据是用汇编文件配置的:
- 175. * arch/arm/mm/proc-xxx.S
- 176. * (此时MMU已关闭)
- 177. */
- 178. ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ 获取mm_mmuflags(节描述符默认配置),保存于r7
- 179.
- 180. /*
- 181. * 创建特定映射,以满足__enable_mmu的需求。
- 182. * 此特定映射将被paging_init()删除。
- 183. *
- 184. * 其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表
- 185. * 以保证在启用mmu时代码的正确执行–1:1映射(物理地址=虚拟地址)
- 186. */
- 187. adr r0, __enable_mmu_loc
- 188. ldmia r0, {r3, r5, r6}
- 189. sub r0, r0, r3 @ 获取编译时确定的虚拟地址到当前物理地址的偏移
- 190. add r5, r5, r0 @ __enable_mmu的当前物理地址
- 191. add r6, r6, r0 @ __enable_mmu_end的当前物理地址
- 192. mov r5, r5, lsr #20 @ __enable_mmu的节基址
- 193. mov r6, r6, lsr #20 @ __enable_mmu_end的节基址
- 194.
- 195. 1: orr r3, r7, r5, lsl #20 @ 生成节描述符:flags + 节基址
- 196. str r3, [r4, r5, lsl #2] @ 设置节描述符,1:1映射(物理地址=虚拟地址)
- 197. teq r5, r6 @ 完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)
- 198. addne r5, r5, #1 @ r5 = 下一节的基址
- 199. bne 1b
- 200.
- 201. /*
- 202. * 现在创建内核的逻辑映射区页表(节映射)
- 203. * 创建范围:KERNEL_START—KERNEL_END
- 204. * KERNEL_START:内核最终运行的虚拟地址
- 205. * KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)
- 206. */
- 207. mov r3, pc @ 获取当前物理地址
- 208. mov r3, r3, lsr #20 @ r3 = 当前物理地址的节基址
- 209. orr r3, r7, r3, lsl #20 @ r3 为当前物理地址的节描述符
- 210. /*
- 211. * 下面是为了确定页表项的入口地址
- 212. * 其实页表入口项的偏移就反应了对应的虚拟地址的高位
- 213. *
- 214. * 由于ARM指令集的8bit位图问题,只能分两次得到
- 215. * KERNEL_START:内核最终运行的虚拟地址
- 216. *
- 217. */
- 218. add r0, r4, #(KERNEL_START & 0xff000000) >> 18
- 219. str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
- 220. ldr r6, =(KERNEL_END - 1)
- 221. add r0, r0, #4
- 222. add r6, r4, r6, lsr #18 @ r6 = 内核逻辑映射结束的节基址
- 223. 1: cmp r0, r6
- 224. add r3, r3, #1 << 20 @ 生成节描述符(只需做基址递增)
- 225. strls r3, [r0], #4 @ 设置节描述符
- 226. bls 1b
- 227.
- 228. #ifdef CONFIG_XIP_KERNEL
- 229. /*
- 230. * 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分
- 231. * 这里我们再映射一些RAM来作为 .data and .bss 空间.
- 232. */
- 233. add r3, r8, #TEXT_OFFSET
- 234. orr r3, r3, r7 @ 生成节描述符:flags + 节基址
- 235. add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
- 236. str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
- 237. ldr r6, =(_end - 1)
- 238. add r0, r0, #4
- 239. add r6, r4, r6, lsr #18
- 240. 1: cmp r0, r6
- 241. add r3, r3, #1 << 20
- 242. strls r3, [r0], #4
- 243. bls 1b
- 244. #endif
- 245.
- 246. /*
- 247. * 然后映射启动参数区(现在r2中的atags物理地址)
- 248. * 或者
- 249. * 如果启动参数区的虚拟地址没有确定(或者无效),则会映射RAM的头1MB.
- 250. */
- 251. mov r0, r2, lsr #20
- 252. movs r0, r0, lsl #20
- 253. moveq r0, r8 @ 如果atags指针无效,则r0 = r8(映射RAM的头1MB)
- 254. sub r3, r0, r8
- 255. add r3, r3, #PAGE_OFFSET @ 转换为虚拟地址
- 256. add r3, r4, r3, lsr #18 @ 确定页表项(节描述符)入口地址
- 257. orr r6, r7, r0 @ 生成节描述符
- 258. str r6, [r3] @ 设置节描述符
- 259.
- 260. /*
- 261. * 下面是调试信息的输出函数区
- 262. * 这里做了IO内存空间的节映射
- 263. */
- 264. #ifdef CONFIG_DEBUG_LL
- 265. #ifndef CONFIG_DEBUG_ICEDCC
- 266. /*
- 267. * 为串口调试映射IO内存空间(将串口IO内存之上的所有地址都映射了)
- 268. * 这允许调试信息(在paging_init之前)从串口控制台输出
- 269. *
- 270. */
- 271. addruart r7, r3 @ 宏代码,位于arch/arm/mach-xxx/include/mach/debug-macro.S
- 272. @ 作用是将串口控制寄存器的基址放入r7(物理地址)和r3(虚拟地址)
- 273. mov r3, r3, lsr #20
- 274. mov r3, r3, lsl #2
- 275.
- 276. add r0, r4, r3 @ r0为串口IO内存映射页表项的入口地址
- 277. rsb r3, r3, #0x4000 @ 16K(PTRS_PER_PGD*sizeof(long))-r3
- 278. cmp r3, #0x0800 @ limit to 512MB,入口地址有效性检查(只能在最后#0x0800内)
- 279. movhi r3, #0x0800 @ 也就是说虚拟地址被限制在3.5G以上
- 280. add r6, r0, r3 @ r6为页表结束地址
- 281. mov r3, r7, lsr #20
- 282. ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
- 283. orr r3, r7, r3, lsl #20 @ 生成节描述符
- 284. 1: str r3, [r0], #4
- 285. add r3, r3, #1 << 20
- 286. teq r0, r6
- 287. bne 1b
- 288.
- 289. #else /* CONFIG_DEBUG_ICEDCC */
- 290. /* 我们无需任何串口调试映射 for ICEDCC */
- 291. ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
- 292. #endif /* !CONFIG_DEBUG_ICEDCC */
- 293.
- 294. #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
- 295. /*
- 296. * 如果我们在使用 NetWinder 或 CATS,我们也需要为调试信息映射
- 297. * 16550-type 串口
- 298. */
- 299. add r0, r4, #0xff000000 >> 18
- 300. orr r3, r7, #0x7c000000
- 301. str r3, [r0]
- 302. #endif
- 303. #ifdef CONFIG_ARCH_RPC
- 304. /*
- 305. * Map in screen at 0x02000000 & SCREEN2_BASE
- 306. * Similar reasons here - for debug. This is
- 307. * only for Acorn RiscPC architectures.
- 308. */
- 309. add r0, r4, #0x02000000 >> 18
- 310. orr r3, r7, #0x02000000
- 311. str r3, [r0]
- 312. add r0, r4, #0xd8000000 >> 18
- 313. str r3, [r0]
- 314. #endif
- 315. #endif
- 316. mov pc, lr @页表创建结束,返回
- 317. ENDPROC(__create_page_tables)
- 318. .ltorg
- 319. .align
- 320. __enable_mmu_loc:
- 321. .long .
- 322. .long __enable_mmu
- 323. .long __enable_mmu_end
- 324.
- 325. #if defined(CONFIG_SMP)
- 326. __CPUINIT
- 327. ENTRY(secondary_startup)
- 328. /*
- 329. * Common entry point for secondary CPUs.
- 330. *
- 331. * Ensure that we’re in SVC mode, and IRQs are disabled. Lookup
- 332. * the processor type - there is no need to check the machine type
- 333. * as it has already been validated by the primary processor.
- 334. */
- 335. setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
- 336. mrc p15, 0, r9, c0, c0 @ get processor id
- 337. bl __lookup_processor_type
- 338. movs r10, r5 @ invalid processor?
- 339. moveq r0, #’p’ @ yes, error ‘p’
- 340. THUMB( it eq ) @ force fixup-able long branch encoding
- 341. beq __error_p
- 342.
- 343. /*
- 344. * Use the page tables supplied from __cpu_up.
- 345. */
- 346. adr r4, __secondary_data
- 347. ldmia r4, {r5, r7, r12} @ address to jump to after
- 348. sub lr, r4, r5 @ mmu has been enabled
- 349. ldr r4, [r7, lr] @ get secondary_data.pgdir
- 350. add r7, r7, #4
- 351. ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir
- 352. adr lr, BSYM(__enable_mmu) @ return address
- 353. mov r13, r12 @ __secondary_switched address
- 354. ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor
- 355. @ (return control reg)
- 356. THUMB( add r12, r10, #PROCINFO_INITFUNC )
- 357. THUMB( mov pc, r12 )
- 358. ENDPROC(secondary_startup)
- 359.
- 360. /*
- 361. * r6 = &secondary_data
- 362. */
- 363. ENTRY(__secondary_switched)
- 364. ldr sp, [r7, #4] @ get secondary_data.stack
- 365. mov fp, #0
- 366. b secondary_start_kernel
- 367. ENDPROC(__secondary_switched)
- 368.
- 369. .align
- 370.
- 371. .type __secondary_data, %object
- 372. __secondary_data:
- 373. .long .
- 374. .long secondary_data
- 375. .long __secondary_switched
- 376. #endif /* defined(CONFIG_SMP) */
- 377.
- 378.
- 379.
- 380. /*
- 381. * 在最后启动MMU前,设置一些常用位 Essentially
- 382. * 其实,这里只是加载了页表指针和域访问控制数据寄存器
- 383. *
- 384. *
- 385. *r0 = cp#15 control register
- 386. * r1 = machine ID
- 387. * r2 = atags or dtb pointer
- 388. * r4 = page table pointer
- 389. * r9 = processor ID
- 390. * r13 = 最后要跳入的虚拟地址
- 391. */
- 392. __enable_mmu:
- 393. #ifdef CONFIG_ALIGNMENT_TRAP
- 394. orr r0, r0, #CR_A
- 395. #else
- 396. bic r0, r0, #CR_A
- 397. #endif
- 398. #ifdef CONFIG_CPU_DCACHE_DISABLE
- 399. bic r0, r0, #CR_C
- 400. #endif
- 401. #ifdef CONFIG_CPU_BPREDICT_DISABLE
- 402. bic r0, r0, #CR_Z
- 403. #endif
- 404. #ifdef CONFIG_CPU_ICACHE_DISABLE
- 405. bic r0, r0, #CR_I
- 406. #endif
- 407. mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
- 408. domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
- 409. domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
- 410. domain_val(DOMAIN_IO, DOMAIN_CLIENT)) @设置域访问控制数据
- 411. mcr p15, 0, r5, c3, c0, 0 @ 载入域访问控制数据到DACR
- 412. mcr p15, 0, r4, c2, c0, 0 @ 载入页表基址到TTBR0
- 413. b __turn_mmu_on @ 开启MMU
- 414. ENDPROC(__enable_mmu)
- 415.
- 416. /*
- 417. * 使能 MMU.这完全改变了可见的内存地址空间结构。
- 418. * 您将无法通过这里跟踪执行。
- 419. * 如果你已对此进行探究, *请*在向邮件列表发送另一个新帖之前,
- 420. * 检查linux-arm-kernel的邮件列表归档
- 421. *
- 422. *r0 = cp#15 control register
- 423. * r1 = machine ID
- 424. * r2 = atags or dtb pointer
- 425. * r9 = processor ID
- 426. * r13 = 最后要跳入的*虚拟*地址
- 427. *
- 428. * 其他寄存器依赖上面的调用函数
- 429. */
- 430. .align 5
- 431. __turn_mmu_on:
- 432. mov r0, r0
- 433. mcr p15, 0, r0, c1, c0, 0 @ 设置cp#15控制寄存器(启用MMU)
- 434. mrc p15, 0, r3, c0, c0, 0 @ read id reg
- 435. mov r3, r3
- 436. mov r3, r13 @ r3中装入最后要跳入的*虚拟*地址
- 437. mov pc, r3 @ 跳转到__mmap_switched
- 438. __enable_mmu_end:
- 439. ENDPROC(__turn_mmu_on)
- 440.
- 441.
- 442. #ifdef CONFIG_SMP_ON_UP
- 443. __INIT
- 444. __fixup_smp:
- 445. and r3, r9, #0x000f0000 @ architecture version
- 446. teq r3, #0x000f0000 @ CPU ID supported?
- 447. bne __fixup_smp_on_up @ no, assume UP
- 448.
- 449. bic r3, r9, #0x00ff0000
- 450. bic r3, r3, #0x0000000f @ mask 0xff00fff0
- 451. mov r4, #0x41000000
- 452. orr r4, r4, #0x0000b000
- 453. orr r4, r4, #0x00000020 @ val 0x4100b020
- 454. teq r3, r4 @ ARM 11MPCore?
- 455. moveq pc, lr @ yes, assume SMP
- 456.
- 457. mrc p15, 0, r0, c0, c0, 5 @ read MPIDR
- 458. and r0, r0, #0xc0000000 @ multiprocessing extensions and
- 459. teq r0, #0x80000000 @ not part of a uniprocessor system?
- 460. moveq pc, lr @ yes, assume SMP
- 461.
- 462. __fixup_smp_on_up:
- 463. adr r0, 1f
- 464. ldmia r0, {r3 - r5}
- 465. sub r3, r0, r3
- 466. add r4, r4, r3
- 467. add r5, r5, r3
- 468. b __do_fixup_smp_on_up
- 469. ENDPROC(__fixup_smp)
- 470.
- 471. .align
- 472. 1: .word .
- 473. .word __smpalt_begin
- 474. .word __smpalt_end
- 475.
- 476. .pushsection .data
- 477. .globl smp_on_up
- 478. smp_on_up:
- 479. ALT_SMP(.long 1)
- 480. ALT_UP(.long 0)
- 481. .popsection
- 482. #endif
- 483.
- 484. .text
- 485. __do_fixup_smp_on_up:
- 486. cmp r4, r5
- 487. movhs pc, lr
- 488. ldmia {r0, r6}
- 489. ARM( str r6, [r0, r3] )
- 490. THUMB( add r0, r0, r3 )
- 491. #ifdef __ARMEB__
- 492. THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.
- 493. #endif
- 494. THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords
- 495. THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.
- 496. THUMB( strh r6, [r0] )
- 497. b __do_fixup_smp_on_up
- 498. ENDPROC(__do_fixup_smp_on_up)
- 499.
- 500. ENTRY(fixup_smp)
- 501. stmfd {r4 - r6, lr}
- 502. mov r4, r0
- 503. add r5, r0, r1
- 504. mov r3, #0
- 505. bl __do_fixup_smp_on_up
- 506. ldmfd {r4 - r6, pc}
- 507. ENDPROC(fixup_smp)
- 508.
- 509. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
- 510.
- 511. /* __fixup_pv_table - patch the stub instructions with the delta between
- 512. * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
- 513. * can be expressed by an immediate shifter operand. The stub instruction
- 514. * has a form of ’(add|sub) rd, rn, #imm’.
- 515. */
- 516. __HEAD
- 517. __fixup_pv_table:
- 518. adr r0, 1f
- 519. ldmia r0, {r3-r5, r7}
- 520. sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
- 521. add r4, r4, r3 @ adjust table start address
- 522. add r5, r5, r3 @ adjust table end address
- 523. add r7, r7, r3 @ adjust __pv_phys_offset address
- 524. str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset
- 525. #ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
- 526. mov r6, r3, lsr #24 @ constant for add/sub instructions
- 527. teq r3, r6, lsl #24 @ must be 16MiB aligned
- 528. #else
- 529. mov r6, r3, lsr #16 @ constant for add/sub instructions
- 530. teq r3, r6, lsl #16 @ must be 64kiB aligned
- 531. #endif
- 532. THUMB( it ne @ cross section branch )
- 533. bne __error
- 534. str r6, [r7, #4] @ save to __pv_offset
- 535. b __fixup_a_pv_table
- 536. ENDPROC(__fixup_pv_table)
- 537.
- 538. .align
- 539. 1: .long .
- 540. .long __pv_table_begin
- 541. .long __pv_table_end
- 542. 2: .long __pv_phys_offset
- 543.
- 544. .text
- 545. __fixup_a_pv_table:
- 546. #ifdef CONFIG_THUMB2_KERNEL
- 547. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
- 548. lsls r0, r6, #24
- 549. lsr r6, #8
- 550. beq 1f
- 551. clz r7, r0
- 552. lsr r0, #24
- 553. lsl r0, r7
- 554. bic r0, 0x0080
- 555. lsrs r7, #1
- 556. orrcs r0, #0x0080
- 557. orr r0, r0, r7, lsl #12
- 558. #endif
- 559. 1: lsls r6, #24
- 560. beq 4f
- 561. clz r7, r6
- 562. lsr r6, #24
- 563. lsl r6, r7
- 564. bic r6, #0x0080
- 565. lsrs r7, #1
- 566. orrcs r6, #0x0080
- 567. orr r6, r6, r7, lsl #12
- 568. orr r6, #0x4000
- 569. b 4f
- 570. 2: @ at this point the C flag is always clear
- 571. add r7, r3
- 572. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
- 573. ldrh ip, [r7]
- 574. tst ip, 0x0400 @ the i bit tells us LS or MS byte
- 575. beq 3f
- 576. cmp r0, #0 @ set C flag, and …
- 577. biceq ip, 0x0400 @ immediate zero value has a special encoding
- 578. streqh ip, [r7] @ that requires the i bit cleared
- 579. #endif
- 580. 3: ldrh ip, [r7, #2]
- 581. and ip, 0x8f00
- 582. orrcc ip, r6 @ mask in offset bits 31-24
- 583. orrcs ip, r0 @ mask in offset bits 23-16
- 584. strh ip, [r7, #2]
- 585. 4: cmp r4, r5
- 586. ldrcc r7, [r4], #4 @ use branch for delay slot
- 587. bcc 2b
- 588. bx lr
- 589. #else
- 590. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
- 591. and r0, r6, #255 @ offset bits 23-16
- 592. mov r6, r6, lsr #8 @ offset bits 31-24
- 593. #else
- 594. mov r0, #0 @ just in case…
- 595. #endif
- 596. b 3f
- 597. 2: ldr ip, [r7, r3]
- 598. bic ip, ip, #0x000000ff
- 599. tst ip, #0x400 @ rotate shift tells us LS or MS byte
- 600. orrne ip, ip, r6 @ mask in offset bits 31-24
- 601. orreq ip, ip, r0 @ mask in offset bits 23-16
- 602. str ip, [r7, r3]
- 603. 3: cmp r4, r5
- 604. ldrcc r7, [r4], #4 @ use branch for delay slot
- 605. bcc 2b
- 606. mov pc, lr
- 607. #endif
- 608. ENDPROC(__fixup_a_pv_table)
- 609.
- 610. ENTRY(fixup_pv_table)
- 611. stmfd {r4 - r7, lr}
- 612. ldr r2, 2f @ get address of __pv_phys_offset
- 613. mov r3, #0 @ no offset
- 614. mov r4, r0 @ r0 = table start
- 615. add r5, r0, r1 @ r1 = table size
- 616. ldr r6, [r2, #4] @ get __pv_offset
- 617. bl __fixup_a_pv_table
- 618. ldmfd {r4 - r7, pc}
- 619. ENDPROC(fixup_pv_table)
- 620.
- 621. .align
- 622. 2: .long __pv_phys_offset
- 623.
- 624. .data
- 625. .globl __pv_phys_offset
- 626. .type __pv_phys_offset, %object
- 627. __pv_phys_offset:
- 628. .long 0
- 629. .size __pv_phys_offset, . - __pv_phys_offset
- 630. __pv_offset:
- 631. .long 0
- 632. #endif
- 633.
- 634. #include ”head-common.S”
1./*2. * linux/arch/arm/kernel/head.S3. *4. * Copyright (C) 1994-2002 Russell King5. * Copyright (c) 2003 ARM Limited6. * All Rights Reserved7. *8. * This program is free software; you can redistribute it and/or modify9. * it under the terms of the GNU General Public License version 2 as10. * published by the Free Software Foundation.11. *12. * 所有32-bit CPU的内核启动代码13. */14. #include <linux/linkage.h>15. #include <linux/init.h>16. 17. #include <asm/assembler.h>18. #include <asm/domain.h>19. #include <asm/ptrace.h>20. #include <asm/asm-offsets.h>21. #include <asm/memory.h>22. #include <asm/thread_info.h>23. #include <asm/system.h>24. 25. #ifdef CONFIG_DEBUG_LL26. #include <mach/debug-macro.S>27. #endif28. 29. /*30. * swapper_pg_dir 是初始页表的虚拟地址.31. * 我们将页表放在KERNEL_RAM_VADDR以下16K的空间中. 因此我们必须保证32. * KERNEL_RAM_VADDR已经被正常设置.当前, 我们期望的是33. * 这个地址的最后16 bits为0x8000, 但我们或许可以放宽这项限制到34. * KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.35. */36. #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)37. #if (KERNEL_RAM_VADDR & 0xffff) != 0x800038. #error KERNEL_RAM_VADDR must start at 0xXXXX800039. #endif40. 41. .globl swapper_pg_dir42. .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x400043. 44. /*45. * TEXT_OFFSET 是内核代码(解压后)相对于RAM起始的偏移.46. * 而#TEXT_OFFSET - 0x4000就是页表相对于RAM起始的偏移.47. * 这个宏的作用是将phys(RAM的启示地址)加上页表的偏移,48. * 而得到页表的起始物理地址49. */50. .macro pgtbl, rd, phys51. add \rd, \phys, #TEXT_OFFSET - 0x400052. .endm53. 54. #ifdef CONFIG_XIP_KERNEL55. #define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)56. #define KERNEL_END _edata_loc57. #else58. #define KERNEL_START KERNEL_RAM_VADDR59. #define KERNEL_END _end60. #endif61. 62. /*63. * 内核启动入口点.64. * ---------------------------65. *66. * 这个入口正常情况下是在解压完成后被调用的.67. * 调用条件: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,68. * r1 = machine nr, r2 = atags or dtb pointer.69. * 这些条件在解压完成后会被逐一满足,然后才跳转过来。70. *71. * 这些代码大多数是位置无关的, 如果你的内核入口地址在连接时确定为72. * 0xc0008000, 你调用此函数的物理地址就是 __pa(0xc0008000).73. *74. * 完整的machineID列表,请参见 linux/arch/arm/tools/mach-types75. *76. * 我们尽量让代码简洁; 不在此处添加任何设备特定的代码77. * - 这些特定的初始化代码是boot loader的工作(或在极端情况下,78. * 有充分理由的情况下, 可以由zImage完成)。79. */80. __HEAD81. ENTRY(stext)82. setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ CPU模式设置宏83. @ (进入svc模式并且关闭中断)84. mrc p15, 0, r9, c0, c0 @ 获取处理器id-->r985. bl __lookup_processor_type @ 返回r5=procinfo r9=cpuid86. movs r10, r5 @ r10=r5,并可以检测r5=0?注意当前r10的值87. THUMB( it eq ) @ force fixup-able long branch encoding88. beq __error_p @ yes, error 'p'如果r5=0,则内核处理器不匹配,出错~死循环89. 90. /*91. * 获取RAM的起始物理地址,并保存于 r8 = phys_offset92. * XIP内核与普通在RAM中运行的内核不同93. * (1)CONFIG_XIP_KERNEL94. * 通过运行时计算????95. * (2)正常RAM中运行的内核96. * 通过编译时确定(PLAT_PHYS_OFFSET 一般在arch/arm/mach-xxx/include/mach/memory.h定义)97. * 98. */99. #ifndef CONFIG_XIP_KERNEL100. adr r3, 2f101. ldmia r3, {r4, r8}102. sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)103. add r8, r8, r4 @ PHYS_OFFSET104. #else105. ldr r8, =PLAT_PHYS_OFFSET106. #endif107. 108. /*109. * r1 = machine no, r2 = atags or dtb,110. * r8 = phys_offset, r9 = cpuid, r10 = procinfo111. */112. bl __vet_atags @ 判断r2(内核启动参数)指针的有效性113. #ifdef CONFIG_SMP_ON_UP114. bl __fixup_smp @ ???如果运行SMP内核在单处理器系统中启动,做适当调整115. #endif116. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT117. bl __fixup_pv_table @ ????根据内核在内存中的位置修正物理地址与虚拟地址的转换机制118. #endif119. bl __create_page_tables @ 初始化页表!120. 121. /*122. * 以下使用位置无关的方法调用的是CPU特定代码。123. * 详情请见arch/arm/mm/proc-*.S124. * r10 = xxx_proc_info 结构体的基地址(在上面__lookup_processor_type函数中选中的)125. * 返回时, CPU 已经为 MMU 的启动做好了准备,126. * 且 r0 保存着CPU控制寄存器的值.127. */128. ldr r13, =__mmap_switched @ 在MMU启动之后跳入的第一个虚拟地址129. adr lr, BSYM(1f) @ 设置返回的地址(PIC)130. mov r8, r4 @ 将swapper_pg_dir的物理地址放入r8,131. @ 以备__enable_mmu中将其放入TTBR1132. ARM( add pc, r10, #PROCINFO_INITFUNC ) @ 跳入构架相关的初始化处理器函数(例如A8的是__v7_setup)133. THUMB( add r12, r10, #PROCINFO_INITFUNC ) @主要目的只配置CP15(包括缓存配置)134. THUMB( mov pc, r12 )135. 1: b __enable_mmu @ 启动MMU136. ENDPROC(stext)137. .ltorg138. #ifndef CONFIG_XIP_KERNEL139. 2: .long .140. .long PAGE_OFFSET141. #endif142. 143. /*144. * 创建初始化页表.我们只创建最基本的页表,145. * 以满足内核运行的需要,146. * 这通常意味着仅映射内核代码本身.147. *148. * r8 = phys_offset, r9 = cpuid, r10 = procinfo149. *150. * 返回:151. *r0, r3, r5-r7 被篡改152. *r4 = 页表物理地址153. */154. __create_page_tables:155. pgtbl r4, r8 @ 现在r4 = 页表的起始物理地址156. 157. /*158. * 清零16K的一级初始页表区159. * 这些页表在内核自解压时被设置过160. * (此时MMU已关闭)161. */162. mov r0, r4163. mov r3, #0164. add r6, r0, #0x4000165. 1: str r3, [r0], #4166. str r3, [r0], #4167. str r3, [r0], #4168. str r3, [r0], #4169. teq r0, r6170. bne 1b171. 172. /*173. * 获取节描述符的默认配置(除节基址外的其他配置)174. * 这个数据依构架而不同,数据是用汇编文件配置的:175. * arch/arm/mm/proc-xxx.S176. * (此时MMU已关闭)177. */178. ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ 获取mm_mmuflags(节描述符默认配置),保存于r7179. 180. /*181. * 创建特定映射,以满足__enable_mmu的需求。182. * 此特定映射将被paging_init()删除。183. * 184. * 其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表185. * 以保证在启用mmu时代码的正确执行--1:1映射(物理地址=虚拟地址)186. */187. adr r0, __enable_mmu_loc188. ldmia r0, {r3, r5, r6}189. sub r0, r0, r3 @ 获取编译时确定的虚拟地址到当前物理地址的偏移190. add r5, r5, r0 @ __enable_mmu的当前物理地址191. add r6, r6, r0 @ __enable_mmu_end的当前物理地址192. mov r5, r5, lsr #20 @ __enable_mmu的节基址193. mov r6, r6, lsr #20 @ __enable_mmu_end的节基址194. 195. 1: orr r3, r7, r5, lsl #20 @ 生成节描述符:flags + 节基址196. str r3, [r4, r5, lsl #2] @ 设置节描述符,1:1映射(物理地址=虚拟地址)197. teq r5, r6 @ 完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)198. addne r5, r5, #1 @ r5 = 下一节的基址199. bne 1b200. 201. /*202. * 现在创建内核的逻辑映射区页表(节映射)203. * 创建范围:KERNEL_START---KERNEL_END204. * KERNEL_START:内核最终运行的虚拟地址205. * KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)206. */207. mov r3, pc @ 获取当前物理地址208. mov r3, r3, lsr #20 @ r3 = 当前物理地址的节基址209. orr r3, r7, r3, lsl #20 @ r3 为当前物理地址的节描述符210. /*211. * 下面是为了确定页表项的入口地址212. * 其实页表入口项的偏移就反应了对应的虚拟地址的高位213. *214. * 由于ARM指令集的8bit位图问题,只能分两次得到215. * KERNEL_START:内核最终运行的虚拟地址216. *217. */218. add r0, r4, #(KERNEL_START & 0xff000000) >> 18219. str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!220. ldr r6, =(KERNEL_END - 1)221. add r0, r0, #4222. add r6, r4, r6, lsr #18 @ r6 = 内核逻辑映射结束的节基址223. 1: cmp r0, r6224. add r3, r3, #1 << 20 @ 生成节描述符(只需做基址递增)225. strls r3, [r0], #4 @ 设置节描述符226. bls 1b227. 228. #ifdef CONFIG_XIP_KERNEL229. /*230. * 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分231. * 这里我们再映射一些RAM来作为 .data and .bss 空间.232. */233. add r3, r8, #TEXT_OFFSET234. orr r3, r3, r7 @ 生成节描述符:flags + 节基址235. add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18236. str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!237. ldr r6, =(_end - 1)238. add r0, r0, #4239. add r6, r4, r6, lsr #18240. 1: cmp r0, r6241. add r3, r3, #1 << 20242. strls r3, [r0], #4243. bls 1b244. #endif245. 246. /*247. * 然后映射启动参数区(现在r2中的atags物理地址) 248. * 或者249. * 如果启动参数区的虚拟地址没有确定(或者无效),则会映射RAM的头1MB.250. */251. mov r0, r2, lsr #20252. movs r0, r0, lsl #20253. moveq r0, r8 @ 如果atags指针无效,则r0 = r8(映射RAM的头1MB)254. sub r3, r0, r8255. add r3, r3, #PAGE_OFFSET @ 转换为虚拟地址256. add r3, r4, r3, lsr #18 @ 确定页表项(节描述符)入口地址257. orr r6, r7, r0 @ 生成节描述符258. str r6, [r3] @ 设置节描述符259. 260. /*261. * 下面是调试信息的输出函数区262. * 这里做了IO内存空间的节映射263. */264. #ifdef CONFIG_DEBUG_LL265. #ifndef CONFIG_DEBUG_ICEDCC266. /*267. * 为串口调试映射IO内存空间(将串口IO内存之上的所有地址都映射了)268. * 这允许调试信息(在paging_init之前)从串口控制台输出269. * 270. */271. addruart r7, r3 @ 宏代码,位于arch/arm/mach-xxx/include/mach/debug-macro.S272. @ 作用是将串口控制寄存器的基址放入r7(物理地址)和r3(虚拟地址)273. mov r3, r3, lsr #20274. mov r3, r3, lsl #2275. 276. add r0, r4, r3 @ r0为串口IO内存映射页表项的入口地址277. rsb r3, r3, #0x4000 @ 16K(PTRS_PER_PGD*sizeof(long))-r3278. cmp r3, #0x0800 @ limit to 512MB,入口地址有效性检查(只能在最后#0x0800内)279. movhi r3, #0x0800 @ 也就是说虚拟地址被限制在3.5G以上280. add r6, r0, r3 @ r6为页表结束地址281. mov r3, r7, lsr #20282. ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags283. orr r3, r7, r3, lsl #20 @ 生成节描述符284. 1: str r3, [r0], #4285. add r3, r3, #1 << 20286. teq r0, r6287. bne 1b288. 289. #else /* CONFIG_DEBUG_ICEDCC */290. /* 我们无需任何串口调试映射 for ICEDCC */291. ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags292. #endif /* !CONFIG_DEBUG_ICEDCC */293. 294. #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)295. /*296. * 如果我们在使用 NetWinder 或 CATS,我们也需要为调试信息映射297. * 16550-type 串口298. */299. add r0, r4, #0xff000000 >> 18300. orr r3, r7, #0x7c000000301. str r3, [r0]302. #endif303. #ifdef CONFIG_ARCH_RPC304. /*305. * Map in screen at 0x02000000 & SCREEN2_BASE306. * Similar reasons here - for debug. This is307. * only for Acorn RiscPC architectures.308. */309. add r0, r4, #0x02000000 >> 18310. orr r3, r7, #0x02000000311. str r3, [r0]312. add r0, r4, #0xd8000000 >> 18313. str r3, [r0]314. #endif315. #endif316. mov pc, lr @页表创建结束,返回317. ENDPROC(__create_page_tables)318. .ltorg319. .align320. __enable_mmu_loc:321. .long .322. .long __enable_mmu323. .long __enable_mmu_end324. 325. #if defined(CONFIG_SMP)326. __CPUINIT327. ENTRY(secondary_startup)328. /*329. * Common entry point for secondary CPUs.330. *331. * Ensure that we're in SVC mode, and IRQs are disabled. Lookup332. * the processor type - there is no need to check the machine type333. * as it has already been validated by the primary processor.334. */335. setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9336. mrc p15, 0, r9, c0, c0 @ get processor id337. bl __lookup_processor_type338. movs r10, r5 @ invalid processor?339. moveq r0, #'p' @ yes, error 'p'340. THUMB( it eq ) @ force fixup-able long branch encoding341. beq __error_p342. 343. /*344. * Use the page tables supplied from __cpu_up.345. */346. adr r4, __secondary_data347. ldmia r4, {r5, r7, r12} @ address to jump to after348. sub lr, r4, r5 @ mmu has been enabled349. ldr r4, [r7, lr] @ get secondary_data.pgdir350. add r7, r7, #4351. ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir352. adr lr, BSYM(__enable_mmu) @ return address353. mov r13, r12 @ __secondary_switched address354. ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor355. @ (return control reg)356. THUMB( add r12, r10, #PROCINFO_INITFUNC )357. THUMB( mov pc, r12 )358. ENDPROC(secondary_startup)359. 360. /*361. * r6 = &secondary_data362. */363. ENTRY(__secondary_switched)364. ldr sp, [r7, #4] @ get secondary_data.stack365. mov fp, #0366. b secondary_start_kernel367. ENDPROC(__secondary_switched)368. 369. .align370. 371. .type __secondary_data, %object372. __secondary_data:373. .long .374. .long secondary_data375. .long __secondary_switched376. #endif /* defined(CONFIG_SMP) */377. 378. 379. 380. /*381. * 在最后启动MMU前,设置一些常用位 Essentially382. * 其实,这里只是加载了页表指针和域访问控制数据寄存器383. * 384. *385. *r0 = cp#15 control register386. * r1 = machine ID387. * r2 = atags or dtb pointer388. * r4 = page table pointer389. * r9 = processor ID390. * r13 = 最后要跳入的虚拟地址391. */392. __enable_mmu:393. #ifdef CONFIG_ALIGNMENT_TRAP394. orr r0, r0, #CR_A395. #else396. bic r0, r0, #CR_A397. #endif398. #ifdef CONFIG_CPU_DCACHE_DISABLE399. bic r0, r0, #CR_C400. #endif401. #ifdef CONFIG_CPU_BPREDICT_DISABLE402. bic r0, r0, #CR_Z403. #endif404. #ifdef CONFIG_CPU_ICACHE_DISABLE405. bic r0, r0, #CR_I406. #endif407. mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \408. domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \409. domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \410. domain_val(DOMAIN_IO, DOMAIN_CLIENT)) @设置域访问控制数据411. mcr p15, 0, r5, c3, c0, 0 @ 载入域访问控制数据到DACR412. mcr p15, 0, r4, c2, c0, 0 @ 载入页表基址到TTBR0413. b __turn_mmu_on @ 开启MMU414. ENDPROC(__enable_mmu)415. 416. /*417. * 使能 MMU.这完全改变了可见的内存地址空间结构。418. * 您将无法通过这里跟踪执行。419. * 如果你已对此进行探究, *请*在向邮件列表发送另一个新帖之前,420. * 检查linux-arm-kernel的邮件列表归档421. *422. *r0 = cp#15 control register423. * r1 = machine ID424. * r2 = atags or dtb pointer425. * r9 = processor ID426. * r13 = 最后要跳入的*虚拟*地址427. *428. * 其他寄存器依赖上面的调用函数429. */430. .align 5431. __turn_mmu_on:432. mov r0, r0433. mcr p15, 0, r0, c1, c0, 0 @ 设置cp#15控制寄存器(启用MMU)434. mrc p15, 0, r3, c0, c0, 0 @ read id reg435. mov r3, r3436. mov r3, r13 @ r3中装入最后要跳入的*虚拟*地址437. mov pc, r3 @ 跳转到__mmap_switched438. __enable_mmu_end:439. ENDPROC(__turn_mmu_on)440. 441. 442. #ifdef CONFIG_SMP_ON_UP443. __INIT444. __fixup_smp:445. and r3, r9, #0x000f0000 @ architecture version446. teq r3, #0x000f0000 @ CPU ID supported?447. bne __fixup_smp_on_up @ no, assume UP448. 449. bic r3, r9, #0x00ff0000450. bic r3, r3, #0x0000000f @ mask 0xff00fff0451. mov r4, #0x41000000452. orr r4, r4, #0x0000b000453. orr r4, r4, #0x00000020 @ val 0x4100b020454. teq r3, r4 @ ARM 11MPCore?455. moveq pc, lr @ yes, assume SMP456. 457. mrc p15, 0, r0, c0, c0, 5 @ read MPIDR458. and r0, r0, #0xc0000000 @ multiprocessing extensions and459. teq r0, #0x80000000 @ not part of a uniprocessor system?460. moveq pc, lr @ yes, assume SMP461. 462. __fixup_smp_on_up:463. adr r0, 1f464. ldmia r0, {r3 - r5}465. sub r3, r0, r3466. add r4, r4, r3467. add r5, r5, r3468. b __do_fixup_smp_on_up469. ENDPROC(__fixup_smp)470. 471. .align472. 1: .word .473. .word __smpalt_begin474. .word __smpalt_end475. 476. .pushsection .data477. .globl smp_on_up478. smp_on_up:479. ALT_SMP(.long 1)480. ALT_UP(.long 0)481. .popsection482. #endif483. 484. .text485. __do_fixup_smp_on_up:486. cmp r4, r5487. movhs pc, lr488. ldmia {r0, r6}489. ARM( str r6, [r0, r3] )490. THUMB( add r0, r0, r3 )491. #ifdef __ARMEB__492. THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.493. #endif494. THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords495. THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.496. THUMB( strh r6, [r0] )497. b __do_fixup_smp_on_up498. ENDPROC(__do_fixup_smp_on_up)499. 500. ENTRY(fixup_smp)501. stmfd {r4 - r6, lr}502. mov r4, r0503. add r5, r0, r1504. mov r3, #0505. bl __do_fixup_smp_on_up506. ldmfd {r4 - r6, pc}507. ENDPROC(fixup_smp)508. 509. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT510. 511. /* __fixup_pv_table - patch the stub instructions with the delta between512. * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and513. * can be expressed by an immediate shifter operand. The stub instruction514. * has a form of '(add|sub) rd, rn, #imm'.515. */516. __HEAD517. __fixup_pv_table:518. adr r0, 1f519. ldmia r0, {r3-r5, r7}520. sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET521. add r4, r4, r3 @ adjust table start address522. add r5, r5, r3 @ adjust table end address523. add r7, r7, r3 @ adjust __pv_phys_offset address524. str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset525. #ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT526. mov r6, r3, lsr #24 @ constant for add/sub instructions527. teq r3, r6, lsl #24 @ must be 16MiB aligned528. #else529. mov r6, r3, lsr #16 @ constant for add/sub instructions530. teq r3, r6, lsl #16 @ must be 64kiB aligned531. #endif532. THUMB( it ne @ cross section branch )533. bne __error534. str r6, [r7, #4] @ save to __pv_offset535. b __fixup_a_pv_table536. ENDPROC(__fixup_pv_table)537. 538. .align539. 1: .long .540. .long __pv_table_begin541. .long __pv_table_end542. 2: .long __pv_phys_offset543. 544. .text545. __fixup_a_pv_table:546. #ifdef CONFIG_THUMB2_KERNEL547. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT548. lsls r0, r6, #24549. lsr r6, #8550. beq 1f551. clz r7, r0552. lsr r0, #24553. lsl r0, r7554. bic r0, 0x0080555. lsrs r7, #1556. orrcs r0, #0x0080557. orr r0, r0, r7, lsl #12558. #endif559. 1: lsls r6, #24560. beq 4f561. clz r7, r6562. lsr r6, #24563. lsl r6, r7564. bic r6, #0x0080565. lsrs r7, #1566. orrcs r6, #0x0080567. orr r6, r6, r7, lsl #12568. orr r6, #0x4000569. b 4f570. 2: @ at this point the C flag is always clear571. add r7, r3572. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT573. ldrh ip, [r7]574. tst ip, 0x0400 @ the i bit tells us LS or MS byte575. beq 3f576. cmp r0, #0 @ set C flag, and ...577. biceq ip, 0x0400 @ immediate zero value has a special encoding578. streqh ip, [r7] @ that requires the i bit cleared579. #endif580. 3: ldrh ip, [r7, #2]581. and ip, 0x8f00582. orrcc ip, r6 @ mask in offset bits 31-24583. orrcs ip, r0 @ mask in offset bits 23-16584. strh ip, [r7, #2]585. 4: cmp r4, r5586. ldrcc r7, [r4], #4 @ use branch for delay slot587. bcc 2b588. bx lr589. #else590. #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT591. and r0, r6, #255 @ offset bits 23-16592. mov r6, r6, lsr #8 @ offset bits 31-24593. #else594. mov r0, #0 @ just in case...595. #endif596. b 3f597. 2: ldr ip, [r7, r3]598. bic ip, ip, #0x000000ff599. tst ip, #0x400 @ rotate shift tells us LS or MS byte600. orrne ip, ip, r6 @ mask in offset bits 31-24601. orreq ip, ip, r0 @ mask in offset bits 23-16602. str ip, [r7, r3]603. 3: cmp r4, r5604. ldrcc r7, [r4], #4 @ use branch for delay slot605. bcc 2b606. mov pc, lr607. #endif608. ENDPROC(__fixup_a_pv_table)609. 610. ENTRY(fixup_pv_table)611. stmfd {r4 - r7, lr}612. ldr r2, 2f @ get address of __pv_phys_offset613. mov r3, #0 @ no offset614. mov r4, r0 @ r0 = table start615. add r5, r0, r1 @ r1 = table size616. ldr r6, [r2, #4] @ get __pv_offset617. bl __fixup_a_pv_table618. ldmfd {r4 - r7, pc}619. ENDPROC(fixup_pv_table)620. 621. .align622. 2: .long __pv_phys_offset623. 624. .data625. .globl __pv_phys_offset626. .type __pv_phys_offset, %object627. __pv_phys_offset:628. .long 0629. .size __pv_phys_offset, . - __pv_phys_offset630. __pv_offset:631. .long 0632. #endif633. 634. #include "head-common.S"
arch/arm/kernel/head-common.S
- 1./*
- 2. * linux/arch/arm/kernel/head-common.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. */
- 13.
- 14. #define ATAG_CORE 0x54410001
- 15. #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
- 16. #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
- 17.
- 18. #ifdef CONFIG_CPU_BIG_ENDIAN
- 19. #define OF_DT_MAGIC 0xd00dfeed
- 20. #else
- 21. #define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */
- 22. #endif
- 23.
- 24. /*
- 25. * 异常处理.一些我们无法处理的错误.
- 26. * 我们应当告诉用户(这些错误信息),但因为我们甚至无法保证是在正确的架构上运行,
- 27. * 所以我们什么都不做(死循环)。
- 28. *
- 29. * 如果 CONFIG_DEBUG_LL 被设置,我们试图打印出错误信息,
- 30. * 并希望这可以对我们有帮助 (例如这对bootloader没有提供适当的处理器ID
- 31. * 是有帮助的).
- 32. */
- 33. __HEAD
- 34.
- 35. /* 确定r2(内核启动参数)指针的有效性。 The heuristic 要求
- 36. * 是4Byte对齐的、在物理内存的头16K中,且以ATAG_CORE标记开头。
- 37. * 如果选择了CONFIG_OF_FLATTREE,dtb指针也是可以接受的.
- 38. *
- 39. * 在这个函数的未来版本中 可能会对物理地址的要求更为宽松,
- 40. * 且如果有必要的话,可能可以移动ATAGS数据块.
- 41. *
- 42. * 返回:
- 43. *r2 可能是有效的 atags 指针, 有效的 dtb 指针,或者0
- 44. * r5, r6 被篡改
- 45. */
- 46. __vet_atags:
- 47. tst r2, #0x3 @ 是否4Byte对齐?
- 48. bne 1f @ 不是则认为指针无效,返回
- 49.
- 50. ldr r5, [r2, #0] @获取r2指向的前4Byte,用于下面测试
- 51. #ifdef CONFIG_OF_FLATTREE
- 52. ldr r6, =OF_DT_MAGIC @ is it a DTB?
- 53. cmp r5, r6
- 54. beq 2f
- 55. #endif
- 56.
- 57. /* 内核启动参数块的规范是:
- 58. * (wait for updata)
- 59. */
- 60. cmp r5, #ATAG_CORE_SIZE @ 第一个tag是ATAG_CORE吗?测试的是tag_header中的size
- 61. @ 如果为ATAG_CORE,那么必为ATAG_CORE_SIZE
- 62. cmpne r5, #ATAG_CORE_SIZE_EMPTY @ 如果第一个tag的tag_header中的size为ATAG_CORE_SIZE_EMPTY
- 63. @ 说明此处也有atags
- 64. bne 1f
- 65. ldr r5, [r2, #4] @ 第一个tag_header的tag(魔数)
- 66. ldr r6, =ATAG_CORE @ 获取ATAG_CORE的魔数
- 67. cmp r5, r6 @ 判断第一个tag是否为ATAG_CORE
- 68. bne 1f @ 不是则认为指针无效,返回
- 69.
- 70. 2: mov pc, lr @ atag/dtb 指针有效
- 71.
- 72. 1: mov r2, #0
- 73. mov pc, lr
- 74. ENDPROC(__vet_atags)
- 75.
- 76. /*
- 77. * 以下的代码段是在MMU开启的状态下执行的,
- 78. * 而且使用的是绝对地址; 这不是位置无关代码.
- 79. *
- 80. *r0 = cp#15 控制寄存器值
- 81. *r1 = machine ID
- 82. * r2 = atags/dtb pointer
- 83. * r9 = processor ID
- 84. */
- 85. __INIT
- 86. __mmap_switched:
- 87. adr r3, __mmap_switched_data
- 88.
- 89. ldmia {r4, r5, r6, r7}
- 90. cmp r4, r5 @ 如果有必要,拷贝数据段。
- 91. @ 对比__data_loc和_sdata
- 92. @ __data_loc是数据段在内核代码映像中的存储位置
- 93. @ _sdata是数据段的链接位置(在内存中的位置)
- 94. @ 如果是XIP技术的内核,这两个数据肯定不同
- 95. 1: cmpne r5, r6 @ 检测数据是否拷贝完成
- 96. ldrne fp, [r4], #4
- 97. strne fp, [r5], #4
- 98. bne 1b
- 99.
- 100. mov fp, #0 @ 清零 BSS 段(and zero fp)
- 101. 1: cmp r6, r7 @ 检测是否完成
- 102. strcc fp, [r6],#4
- 103. bcc 1b
- 104.
- 105. /* 这里将需要的数据从寄存器中转移到全局变量中,
- 106. * 因为最后会跳入C代码,寄存器会被使用。
- 107. */
- 108. ARM( ldmia r3, {r4, r5, r6, r7, sp})
- 109. THUMB( ldmia r3, {r4, r5, r6, r7} )
- 110. THUMB( ldr sp, [r3, #16] )
- 111. str r9, [r4] @ 保存 processor ID到全局变量processor_id
- 112. str r1, [r5] @ 保存 machine type到全局变量__machine_arch_type
- 113. str r2, [r6] @ 保存 atags指针到全局变量__atags_pointer
- 114. bic r4, r0, #CR_A @ 清除cp15 控制寄存器值的 ’A’ bit(禁用对齐错误检查)
- 115. stmia r7, {r0, r4} @ 保存控制寄存器值到全局变量cr_alignment(在arch/arm/kernel/entry-armv.S)
- 116. b start_kernel @ 跳入C代码(init/main.c)
- 117. ENDPROC(__mmap_switched)
- 118.
- 119. .align 2
- 120. .type __mmap_switched_data, %object
- 121. __mmap_switched_data:
- 122. .long __data_loc @ r4
- 123. .long _sdata @ r5
- 124. .long __bss_start @ r6
- 125. .long _end @ r7
- 126. .long processor_id @ r4
- 127. .long __machine_arch_type @ r5
- 128. .long __atags_pointer @ r6
- 129. .long cr_alignment @ r7
- 130. .long init_thread_union + THREAD_START_SP @ sp
- 131. .size __mmap_switched_data, . - __mmap_switched_data
- 132.
- 133. /*
- 134. * 这里提供一个 C-API 版本的 __lookup_processor_type
- 135. */
- 136. ENTRY(lookup_processor_type)
- 137. stmfd {r4 - r6, r9, lr}
- 138. mov r9, r0
- 139. bl __lookup_processor_type
- 140. mov r0, r5
- 141. ldmfd {r4 - r6, r9, pc}
- 142. ENDPROC(lookup_processor_type)
- 143.
- 144. /*
- 145. * 读取处理器ID寄存器 (CP#15, CR0), 并且查找编译时确定的处理器
- 146. * 支持列表.注意:我们不能对__proc_info使用绝对地址,
- 147. * 因为我们还没有重新初始化页表(MMU已关闭,之前是解压时使用的1:1映射)。
- 148. * (我们不在正确的地址空间:内核是按虚拟地址(0xc00008000)编译的,
- 149. * 而现在我们运行在MMU关闭的情况下)。
- 150. * 我们必须计算偏移量。
- 151. *
- 152. * r9 = cpuid
- 153. * Returns:
- 154. * r3, r4, r6 被篡改
- 155. * r5 = proc_info 指针(物理地址空间)
- 156. * r9 = cpuid (保留)
- 157. */
- 158. __CPUINIT
- 159. __lookup_processor_type:
- 160. adr r3, __lookup_processor_type_data @获取运行时的地址数据
- 161. ldmia r3, {r4 - r6} @获取编译时确定的地址数据(虚拟地址)
- 162. sub r3, r3, r4 @ 获取地址偏移 virt&phys(r3)
- 163. add r5, r5, r3 @ 将虚拟地址空间转换为物理地址空间
- 164. add r6, r6, r3 @ r5=__proc_info_begin r6=__proc_info_end
- 165. 1: ldmia r5, {r3, r4} @ 获取proc_info_list结构体中的value, mask
- 166. and r4, r4, r9 @ 利用掩码处理从CP15获取的处理器ID
- 167. teq r3, r4 @ 对比编译时确定的处理器ID
- 168. beq 2f @ 若处理器ID匹配,返回
- 169. add r5, r5, #PROC_INFO_SZ @ 利用sizeof(proc_info_list)跳入下一个处理器ID的匹配
- 170. cmp r5, r6 @ 是否已经处理完proc_info_list数据
- 171. blo 1b @ 如果还有proc_info_list数据,再次检查匹配
- 172. mov r5, #0 @ 否则,编译的内核与此处理器不匹配,r5 = #0
- 173. 2: mov pc, lr
- 174. ENDPROC(__lookup_processor_type)
- 175.
- 176. /*
- 177. * 参见 <asm/procinfo.h> 中关于 __proc_info 结构体的信息.
- 178. */
- 179. .align 2
- 180. .type __lookup_processor_type_data, %object
- 181. __lookup_processor_type_data:
- 182. .long .
- 183. .long __proc_info_begin
- 184. .long __proc_info_end
- 185. .size __lookup_processor_type_data, . - __lookup_processor_type_data
- 186.
- 187. /*
- 188. * 处理器ID不匹配时的入口
- 189. * 如果启用了调试信息,会从consol打印提示信息
- 190. * 之后会进入__error的死循环
- 191. */
- 192. __error_p:
- 193. #ifdef CONFIG_DEBUG_LL
- 194. adr r0, str_p1
- 195. bl printascii
- 196. mov r0, r9
- 197. bl printhex8
- 198. adr r0, str_p2
- 199. bl printascii
- 200. b __error
- 201. str_p1: .asciz ”\nError: unrecognized/unsupported processor variant (0x”
- 202. str_p2: .asciz ”).\n”
- 203. .align
- 204. #endif
- 205. ENDPROC(__error_p)
- 206.
- 207. /*
- 208. * 出错时的死循环入口
- 209. */
- 210. __error:
- 211. #ifdef CONFIG_ARCH_RPC
- 212. /*
- 213. * 出错时屏幕变红 - RiscPC only.
- 214. */
- 215. mov r0, #0x02000000
- 216. mov r3, #0x11
- 217. orr r3, r3, r3, lsl #8
- 218. orr r3, r3, r3, lsl #16
- 219. str r3, [r0], #4
- 220. str r3, [r0], #4
- 221. str r3, [r0], #4
- 222. str r3, [r0], #4
- 223. #endif
- 224. 1: mov r0, r0
- 225. b 1b
- 226. ENDPROC(__error)
1./*2. * linux/arch/arm/kernel/head-common.S3. *4. * Copyright (C) 1994-2002 Russell King5. * Copyright (c) 2003 ARM Limited6. * All Rights Reserved7. *8. * This program is free software; you can redistribute it and/or modify9. * it under the terms of the GNU General Public License version 2 as10. * published by the Free Software Foundation.11. *12. */13. 14. #define ATAG_CORE 0x5441000115. #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)16. #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)17. 18. #ifdef CONFIG_CPU_BIG_ENDIAN19. #define OF_DT_MAGIC 0xd00dfeed20. #else21. #define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */22. #endif23. 24. /*25. * 异常处理.一些我们无法处理的错误.26. * 我们应当告诉用户(这些错误信息),但因为我们甚至无法保证是在正确的架构上运行,27. * 所以我们什么都不做(死循环)。28. *29. * 如果 CONFIG_DEBUG_LL 被设置,我们试图打印出错误信息,30. * 并希望这可以对我们有帮助 (例如这对bootloader没有提供适当的处理器ID31. * 是有帮助的).32. */33. __HEAD34. 35. /* 确定r2(内核启动参数)指针的有效性。 The heuristic 要求36. * 是4Byte对齐的、在物理内存的头16K中,且以ATAG_CORE标记开头。37. * 如果选择了CONFIG_OF_FLATTREE,dtb指针也是可以接受的.38. * 39. * 在这个函数的未来版本中 可能会对物理地址的要求更为宽松,40. * 且如果有必要的话,可能可以移动ATAGS数据块.41. *42. * 返回:43. *r2 可能是有效的 atags 指针, 有效的 dtb 指针,或者044. * r5, r6 被篡改45. */46. __vet_atags:47. tst r2, #0x3 @ 是否4Byte对齐?48. bne 1f @ 不是则认为指针无效,返回49. 50. ldr r5, [r2, #0] @获取r2指向的前4Byte,用于下面测试51. #ifdef CONFIG_OF_FLATTREE52. ldr r6, =OF_DT_MAGIC @ is it a DTB?53. cmp r5, r654. beq 2f55. #endif56. 57. /* 内核启动参数块的规范是:58. * (wait for updata)59. */60. cmp r5, #ATAG_CORE_SIZE @ 第一个tag是ATAG_CORE吗?测试的是tag_header中的size61. @ 如果为ATAG_CORE,那么必为ATAG_CORE_SIZE62. cmpne r5, #ATAG_CORE_SIZE_EMPTY @ 如果第一个tag的tag_header中的size为ATAG_CORE_SIZE_EMPTY63. @ 说明此处也有atags64. bne 1f65. ldr r5, [r2, #4] @ 第一个tag_header的tag(魔数)66. ldr r6, =ATAG_CORE @ 获取ATAG_CORE的魔数67. cmp r5, r6 @ 判断第一个tag是否为ATAG_CORE68. bne 1f @ 不是则认为指针无效,返回69. 70. 2: mov pc, lr @ atag/dtb 指针有效71. 72. 1: mov r2, #073. mov pc, lr74. ENDPROC(__vet_atags)75. 76. /*77. * 以下的代码段是在MMU开启的状态下执行的,78. * 而且使用的是绝对地址; 这不是位置无关代码.79. *80. *r0 = cp#15 控制寄存器值81. *r1 = machine ID82. * r2 = atags/dtb pointer83. * r9 = processor ID84. */85. __INIT86. __mmap_switched:87. adr r3, __mmap_switched_data88. 89. ldmia {r4, r5, r6, r7}90. cmp r4, r5 @ 如果有必要,拷贝数据段。91. @ 对比__data_loc和_sdata92. @ __data_loc是数据段在内核代码映像中的存储位置93. @ _sdata是数据段的链接位置(在内存中的位置)94. @ 如果是XIP技术的内核,这两个数据肯定不同95. 1: cmpne r5, r6 @ 检测数据是否拷贝完成96. ldrne fp, [r4], #497. strne fp, [r5], #498. bne 1b99. 100. mov fp, #0 @ 清零 BSS 段(and zero fp)101. 1: cmp r6, r7 @ 检测是否完成102. strcc fp, [r6],#4103. bcc 1b104. 105. /* 这里将需要的数据从寄存器中转移到全局变量中,106. * 因为最后会跳入C代码,寄存器会被使用。107. */108. ARM( ldmia r3, {r4, r5, r6, r7, sp})109. THUMB( ldmia r3, {r4, r5, r6, r7} )110. THUMB( ldr sp, [r3, #16] )111. str r9, [r4] @ 保存 processor ID到全局变量processor_id112. str r1, [r5] @ 保存 machine type到全局变量__machine_arch_type113. str r2, [r6] @ 保存 atags指针到全局变量__atags_pointer114. bic r4, r0, #CR_A @ 清除cp15 控制寄存器值的 'A' bit(禁用对齐错误检查)115. stmia r7, {r0, r4} @ 保存控制寄存器值到全局变量cr_alignment(在arch/arm/kernel/entry-armv.S)116. b start_kernel @ 跳入C代码(init/main.c)117. ENDPROC(__mmap_switched)118. 119. .align 2120. .type __mmap_switched_data, %object121. __mmap_switched_data:122. .long __data_loc @ r4123. .long _sdata @ r5124. .long __bss_start @ r6125. .long _end @ r7126. .long processor_id @ r4127. .long __machine_arch_type @ r5128. .long __atags_pointer @ r6129. .long cr_alignment @ r7130. .long init_thread_union + THREAD_START_SP @ sp131. .size __mmap_switched_data, . - __mmap_switched_data132. 133. /*134. * 这里提供一个 C-API 版本的 __lookup_processor_type135. */136. ENTRY(lookup_processor_type)137. stmfd {r4 - r6, r9, lr}138. mov r9, r0139. bl __lookup_processor_type140. mov r0, r5141. ldmfd {r4 - r6, r9, pc}142. ENDPROC(lookup_processor_type)143. 144. /*145. * 读取处理器ID寄存器 (CP#15, CR0), 并且查找编译时确定的处理器146. * 支持列表.注意:我们不能对__proc_info使用绝对地址,147. * 因为我们还没有重新初始化页表(MMU已关闭,之前是解压时使用的1:1映射)。148. * (我们不在正确的地址空间:内核是按虚拟地址(0xc00008000)编译的,149. * 而现在我们运行在MMU关闭的情况下)。150. * 我们必须计算偏移量。151. *152. * r9 = cpuid153. * Returns:154. * r3, r4, r6 被篡改155. * r5 = proc_info 指针(物理地址空间)156. * r9 = cpuid (保留)157. */158. __CPUINIT159. __lookup_processor_type:160. adr r3, __lookup_processor_type_data @获取运行时的地址数据161. ldmia r3, {r4 - r6} @获取编译时确定的地址数据(虚拟地址)162. sub r3, r3, r4 @ 获取地址偏移 virt&phys(r3)163. add r5, r5, r3 @ 将虚拟地址空间转换为物理地址空间164. add r6, r6, r3 @ r5=__proc_info_begin r6=__proc_info_end165. 1: ldmia r5, {r3, r4} @ 获取proc_info_list结构体中的value, mask166. and r4, r4, r9 @ 利用掩码处理从CP15获取的处理器ID167. teq r3, r4 @ 对比编译时确定的处理器ID168. beq 2f @ 若处理器ID匹配,返回169. add r5, r5, #PROC_INFO_SZ @ 利用sizeof(proc_info_list)跳入下一个处理器ID的匹配170. cmp r5, r6 @ 是否已经处理完proc_info_list数据171. blo 1b @ 如果还有proc_info_list数据,再次检查匹配172. mov r5, #0 @ 否则,编译的内核与此处理器不匹配,r5 = #0173. 2: mov pc, lr174. ENDPROC(__lookup_processor_type)175. 176. /*177. * 参见 <asm/procinfo.h> 中关于 __proc_info 结构体的信息.178. */179. .align 2180. .type __lookup_processor_type_data, %object181. __lookup_processor_type_data:182. .long .183. .long __proc_info_begin184. .long __proc_info_end185. .size __lookup_processor_type_data, . - __lookup_processor_type_data186. 187. /*188. * 处理器ID不匹配时的入口189. * 如果启用了调试信息,会从consol打印提示信息190. * 之后会进入__error的死循环191. */192. __error_p:193. #ifdef CONFIG_DEBUG_LL194. adr r0, str_p1195. bl printascii196. mov r0, r9197. bl printhex8198. adr r0, str_p2199. bl printascii200. b __error201. str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x"202. str_p2: .asciz ").\n"203. .align204. #endif205. ENDPROC(__error_p)206. 207. /*208. * 出错时的死循环入口209. */210. __error:211. #ifdef CONFIG_ARCH_RPC212. /*213. * 出错时屏幕变红 - RiscPC only.214. */215. mov r0, #0x02000000216. mov r3, #0x11217. orr r3, r3, r3, lsl #8218. orr r3, r3, r3, lsl #16219. str r3, [r0], #4220. str r3, [r0], #4221. str r3, [r0], #4222. str r3, [r0], #4223. #endif224. 1: mov r0, r0225. b 1b226. ENDPROC(__error)
阅读全文
0 0
- linux 3.6 启动源码分析(一)
- linux 3.6 启动源码分析(一)
- linux 3.6 启动源码分析(二) start_kernel
- linux 3.6 启动源码分析(三) setup_arch
- linux 3.6 启动源码分析(四) rest_init
- linux 3.6 启动源码分析(六) do_basic_setup
- linux 3.6 启动源码分析(七) do_initcalls
- linux 3.6 启动源码分析(二) start_kernel
- linux 3.6 启动源码分析(三) setup_arch
- linux 3.6 启动源码分析(四) rest_init
- linux 3.6 启动源码分析(六) do_basic_setup
- linux 3.6 启动源码分析(七) do_initcalls
- netty源码分析(一)-启动
- Amoeba源码分析一:启动过程分析
- Kubelet源码分析(一) 启动流程分析
- Linux内核启动分析一
- Linux tmpfs 源码分析(一)
- Linux tmpfs 源码分析(一)
- Mac下利用终端编译C++(两种方式)
- 欢迎使用CSDN-markdown编辑器
- td高度调整无效
- 198. House Robber
- SSH框架的整合
- linux 3.6 启动源码分析(一)
- java实现定时任务的三种方法
- Pythonic Data Structures and Algorithms(Array 3)
- Apache Beam 快速入门(Python 版)
- web开发之MVC设计模式
- HTML中引用CSS的几种方法
- 前端缓存
- leetcode 146. LRU Cache 需要深入学习Java的Map的内部实现
- 信息收集