linux 3.6 启动源码分析(一)

作为需要和硬件打交道的工程师来说,比较关注的是驱动和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 

15. #include 

16. 

17. #include 

18. #include 

19. #include 

20. #include 

21. #include 

22. #include 

23. #include 

24. 

25. #ifdef CONFIG_DEBUG_LL

26. #include 

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"


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.  * 参见  中关于 __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)


 

你可能感兴趣的:(linux源码学习)