视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Linux内核源码分析--内核启动之(2)Image内核启动(汇编部分)
2025-09-29 16:39:01 责编:小OO
文档
Linux内核源码分析--内核启动之(2)Imag

(2)Image e 内核启动(汇编部分)(Linux-3.0ARMv7)

在完成了zImage自解压之后,就跳转到了解压后的内核(也就是vmlinux的bin版本Image),具体的入口可以在arch/arm/kernel/vmlinux.lds.S(最终的链接脚本是通过这个文件产生的)中获得:

1......

2SECTIONS

3{

4#ifdef CONFIG_XIP_KERNEL

5.=XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);

6#else

7.=PAGE_OFFSET+TEXT_OFFSET;

8#endif

9.init:{/*Init code and data*/

10_stext=.;

11_sinittext=.;

12......

arch/arm/kernel/head.S

13/*

14*linux/arch/arm/kernel/head.S

15*

16*Copyright(C)1994-2002Russell King

17*Copyright(c)2003ARM Limited

18*All Rights Reserved

19*

20*This program is free software;you can redistribute it and/or modify 21*it under the terms of the GNU General Public License version2as 22*published by the Free Software Foundation.

23*

24*所有32-bit CPU的内核启动代码

25*/

26#include

27#include

28

29#include

30#include

31#include

32#include

33#include

34#include

35#include

36

37#ifdef CONFIG_DEBUG_LL

38#include39#endif

40

41/*

42*swapper_pg_dir是初始页表的虚拟地址.

43*我们将页表放在KERNEL_RAM_VADDR以下16K的空间中.因此我们必须保证44*KERNEL_RAM_VADDR已经被正常设置.当前,我们期望的是

45*这个地址的最后16bits为0x8000,但我们或许可以放宽这项到

46*KERNEL_RAM_VADDR>=PAGE_OFFSET+0x4000.

47*/

48#define KERNEL_RAM_VADDR(PAGE_OFFSET+TEXT_OFFSET)

49#if(KERNEL_RAM_VADDR&0xffff)!=0x8000

50#error KERNEL_RAM_VADDR must start at0xXXXX8000

51#endif

52

53.globl swapper_pg_dir

54.equ swapper_pg_dir,KERNEL_RAM_VADDR-0x4000

55

56/*

57*TEXT_OFFSET是内核代码(解压后)相对于RAM起始的偏移.

58*而#TEXT_OFFSET-0x4000就是页表相对于RAM起始的偏移.

59*这个宏的作用是将phys(RAM的启示地址)加上页表的偏移,

60*而得到页表的起始物理地址

61*/

62.macro pgtbl,rd,phys

63add\\rd,\\phys,#TEXT_OFFSET-0x4000

.endm

65

66#ifdef CONFIG_XIP_KERNEL

67#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)

68#define KERNEL_END_edata_loc

69#else

70#define KERNEL_START KERNEL_RAM_VADDR

71#define KERNEL_END_end

72#endif

7374/*

75*内核启动入口点.

76*---------------------------

77*

78*这个入口正常情况下是在解压完成后被调用的.

79*调用条件:MMU=off,D-cache=off,I-cache=dont care,r0=0,

80*r1=machine nr,r2=atags or dtb pointer.

81*这些条件在解压完成后会被逐一满足,然后才跳转过来。

82*

83*这些代码大多数是位置无关的,如果你的内核入口地址在连接时确定为

84*0xc0008000,你调用此函数的物理地址就是__pa(0xc0008000).

85*

86*完整的machineID列表,请参见linux/arch/arm/tools/mach-types

87*

88*我们尽量让代码简洁;不在此处添加任何设备特定的代码

*-这些特定的初始化代码是boot loader的工作(或在极端情况下,

90*有充分理由的情况下,可以由zImage完成)。

91*/

92__HEAD

93ENTRY(stext)

94setmode PSR_F_BIT|PSR_I_BIT|SVC_MODE,r9@CPU模式设置宏

95@(进入svc模式并且关闭中断)96mrc p15,0,r9,c0,c0@获取处理器id-->r9

97bl__lookup_processor_type@返回r5=procinfo r9=cpuid 98movs r10,r5@r10=r5,并可以检测r5=0?注意当前r10的值

99THUMB(it eq)@force fixup-able long branch encoding

100beq__error_p@yes,error'p'如果r5=0,则内核处理器不匹配,出错~死循环

101

102/*

103*获取RAM的起始物理地址,并保存于r8=phys_offset

104*XIP内核与普通在RAM中运行的内核不同

105*(1)CONFIG_XIP_KERNEL

106*通过运行时计算????107*(2)正常RAM中运行的内核

108*通过编译时确定(PLAT_PHYS_OFFSET一般在

arch/arm/mach-xxx/include/mach/memory.h定义)

109*

110*/

111#ifndef CONFIG_XIP_KERNEL

112adr r3,2f

113ldmia r3,{r4,r8}

114sub r4,r3,r4@(PHYS_OFFSET-PAGE_OFFSET)

115add r8,r8,r4@PHYS_OFFSET

116#else

117ldr r8,=PLAT_PHYS_OFFSET

118#endif

119

120/*

121*r1=machine no,r2=atags or dtb,

122*r8=phys_offset,r9=cpuid,r10=procinfo

123*/

124bl__vet_atags@判断r2(内核启动参数)指针的有效性

125#ifdef CONFIG_SMP_ON_UP

126bl__fixup_smp@?如果运行SMP内核在单处理器系统中启动,做适当调整

127#endif

128#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

129bl__fixup_pv_table@根据内核在内存中的位置修正物理地址与虚拟地址的转换机制

130#endif

131bl__create_page_tables@初始化页表!

132

133/*

134*以下使用位置无关的方法调用的是CPU特定代码。

135*详情请见arch/arm/mm/proc-*.S

136*r10=xxx_proc_info结构体的基地址(在上面__lookup_processor_type函数中选中的)

137*返回时,CPU已经为MMU的启动做好了准备,138*且r0保存着CPU控制寄存器的值.

139*/

140ldr r13,=__mmap_switched@在MMU启动之后跳入的第一个虚拟地址

141adr lr,BSYM(1f)@设置返回的地址(PIC)

142mov r8,r4@将swapper_pg_dir的物理地址放入r8,

143@以备__enable_mmu中将其放入TTBR1 144ARM(add pc,r10,#PROCINFO_INITFUNC)@跳入构架相关的初始化处理器函数(例如A8的是__v7_setup)

145THUMB(add r12,r10,#PROCINFO_INITFUNC)@主要目的只配置CP15(包括缓存配置)

146THUMB(mov pc,r12)

1471:b__enable_mmu@启动MMU

148ENDPROC(stext)

149.ltorg

150#ifndef CONFIG_XIP_KERNEL

1512:.long.

152.long PAGE_OFFSET

153#endif

154

155/*

156*创建初始化页表.我们只创建最基本的页表,

157*以满足内核运行的需要,

158*这通常意味着仅映射内核代码本身.

159*

160*r8=phys_offset,r9=cpuid,r10=procinfo

161*

162*返回:

163*r0,r3,r5-r7被篡改

1*r4=页表物理地址

165*/

166__create_page_tables:

167pgtbl r4,r8@现在r4=页表的起始物理地址

168

169/*

170*清零16K的一级初始页表区

171*这些页表在内核自解压时被设置过

172*(此时MMU已关闭)

173*/

174mov r0,r4

175mov r3,#0

176add r6,r0,#0x4000

1771:str r3,[r0],#4

178str r3,[r0],#4

179str r3,[r0],#4

180str r3,[r0],#4

181teq r0,r6

182bne1b

183

184/*

185*获取节描述符的默认配置(除节基址外的其他配置)

186*这个数据依构架而不同,数据是用汇编文件配置的:

187*arch/arm/mm/proc-xxx.S

188*(此时MMU已关闭)

1*/

190ldr r7,[r10,#PROCINFO_MM_MMUFLAGS]@获取mm_mmuflags(节描述符默认配置),保存于r7

191

192/*

193*创建特定映射,以满足__enable_mmu的需求。

194*此特定映射将被paging_init()删除。

195*

196*其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表

197*以保证在启用mmu时代码的正确执行--1:1映射(物理地址=虚拟地址)

198*/

199adr r0,__enable_mmu_loc

200ldmia r0,{r3,r5,r6}

201sub r0,r0,r3@获取编译时确定的虚拟地址到当前物理地址的偏移

202add r5,r5,r0@__enable_mmu的当前物理地址203add r6,r6,r0@__enable_mmu_end的当前物理地址

204mov r5,r5,lsr#20@__enable_mmu的节基址

205mov r6,r6,lsr#20@__enable_mmu_end的节基址

206

2071:orr r3,r7,r5,lsl#20@生成节描述符:flags+节基址

208str r3,[r4,r5,lsl#2]@设置节描述符,1:1映射(物理地址=虚拟地址)

209teq r5,r6@完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)

210addne r5,r5,#1@r5=下一节的基址

211bne1b

212

213/*

214*现在创建内核的逻辑映射区页表(节映射)

215*创建范围:KERNEL_START---KERNEL_END

216*KERNEL_START:内核最终运行的虚拟地址

217*KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)

218*/

219mov r3,pc@获取当前物理地址

220mov r3,r3,lsr#20@r3=当前物理地址的节基址

221orr r3,r7,r3,lsl#20@r3为当前物理地址的节描述符

222/*

223*下面是为了确定页表项的入口地址

224*其实页表入口项的偏移就反应了对应的虚拟地址的高位

225*

226*由于ARM指令集的8bit位图问题,只能分两次得到

227*KERNEL_START:内核最终运行的虚拟地址

228*

229*/

230add r0,r4,#(KERNEL_START&0xff000000)>>18

231str r3,[r0,#(KERNEL_START&0x00f00000)>>18]!

232ldr r6,=(KERNEL_END-1)

233add r0,r0,#4

234add r6,r4,r6,lsr#18@r6=内核逻辑映射结束的节基址

2351:cmp r0,r6

236add r3,r3,#1<<20@生成节描述符(只需做基址递增)237strls r3,[r0],#4@设置节描述符

238bls1b

239

240#ifdef CONFIG_XIP_KERNEL

241/*

242*如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分

243*这里我们再映射一些RAM来作为.data and.bss空间.

244*/

245add r3,r8,#TEXT_OFFSET

246orr r3,r3,r7@生成节描述符:flags+节基址

247add r0,r4,#(KERNEL_RAM_VADDR&0xff000000)>>18

248str r3,[r0,#(KERNEL_RAM_VADDR&0x00f00000)>>18]!

249ldr r6,=(_end-1)

250add r0,r0,#4

251add r6,r4,r6,lsr#18

2521:cmp r0,r6

253add r3,r3,#1<<20

254strls r3,[r0],#4

255bls1b

256#endif

257

258/*

259*然后映射启动参数区(现在r2中的atags物理地址)

260*或者

261*如果启动参数区的虚拟地址没有确定(或者无效),则会映射RAM的头1MB.

262*/

263mov r0,r2,lsr#20

2movs r0,r0,lsl#20

265moveq r0,r8@如果atags指针无效,则r0=r8(映射RAM的头1MB)

266sub r3,r0,r8

267add r3,r3,#PAGE_OFFSET@转换为虚拟地址

268add r3,r4,r3,lsr#18@确定页表项(节描述符)入口地址

269orr r6,r7,r0@生成节描述符

270str r6,[r3]@设置节描述符271

272/*

273*下面是调试信息的输出函数区

274*这里做了IO内存空间的节映射

275*/

276#ifdef CONFIG_DEBUG_LL

277#ifndef CONFIG_DEBUG_ICEDCC

278/*

279*为串口调试映射IO内存空间(将串口IO内存之上的所有地址都映射了)

280*这允许调试信息(在paging_init之前)从串口控制台输出

281*

282*/

283addruart r7,r3@宏代码,位于

arch/arm/mach-xxx/include/mach/debug-macro.S

284@作用是将串口控制寄存器的基址放入r7(物理地址)和r3(虚拟地址) 285mov r3,r3,lsr#20

286mov r3,r3,lsl#2

287

288add r0,r4,r3@r0为串口IO内存映射页表项的入口地址

2rsb r3,r3,#0x4000@16K(PTRS_PER_PGD*sizeof(long))-r3

290cmp r3,#0x0800@limit to512MB,入口地址有效性检查(只能在最后#0x0800内)

291movhi r3,#0x0800@也就是说虚拟地址被在3.5G以上

292add r6,r0,r3@r6为页表结束地址

293mov r3,r7,lsr#20

294ldr r7,[r10,#PROCINFO_IO_MMUFLAGS]@io_mmuflags

295orr r3,r7,r3,lsl#20@生成节描述符

2961:str r3,[r0],#4

297add r3,r3,#1<<20

298teq r0,r6

299bne1b

300

301#else/*CONFIG_DEBUG_ICEDCC*/

302/*我们无需任何串口调试映射for ICEDCC*/

303ldr r7,[r10,#PROCINFO_IO_MMUFLAGS]@io_mmuflags304#endif/*!CONFIG_DEBUG_ICEDCC*/

305

306#if defined(CONFIG_ARCH_NETWINDER)||defined(CONFIG_ARCH_CATS) 307/*

308*如果我们在使用NetWinder或CATS,我们也需要为调试信息映射309*16550-type串口

310*/

311add r0,r4,#0xff000000>>18

312orr r3,r7,#0x7c000000

313str r3,[r0]

314#endif

315#ifdef CONFIG_ARCH_RPC

316/*

317*Map in screen at0x02000000&SCREEN2_BASE

318*Similar reasons here-for debug.This is

319*only for Acorn RiscPC architectures.

320*/

321add r0,r4,#0x02000000>>18

322orr r3,r7,#0x02000000

323str r3,[r0]

324add r0,r4,#0xd8000000>>18

325str r3,[r0]

326#endif

327#endif

328mov pc,lr@页表创建结束,返回

329ENDPROC(__create_page_tables)

330.ltorg

331.align

332__enable_mmu_loc:

333.long.

334.long__enable_mmu

335.long__enable_mmu_end

336

337#if defined(CONFIG_SMP)

338__CPUINIT339ENTRY(secondary_startup)

340/*

341*Common entry point for secondary CPUs.

342*

343*Ensure that we're in SVC mode,and IRQs are disabled.Lookup

344*the processor type-there is no need to check the machine type

345*as it has already been validated by the primary processor.

346*/

347setmode PSR_F_BIT|PSR_I_BIT|SVC_MODE,r9

348mrc p15,0,r9,c0,c0@get processor id

349bl__lookup_processor_type

350movs r10,r5@invalid processor?

351moveq r0,#'p'@yes,error'p'

352THUMB(it eq)@force fixup-able long branch encoding

353beq__error_p

354

355/*

356*Use the page tables supplied from__cpu_up.

357*/

358adr r4,__secondary_data

359ldmia r4,{r5,r7,r12}@address to jump to after

360sub lr,r4,r5@mmu has been enabled

361ldr r4,[r7,lr]@get secondary_data.pgdir

362add r7,r7,#4

363ldr r8,[r7,lr]@get secondary_data.swapper_pg_dir

3adr lr,BSYM(__enable_mmu)@return address

365mov r13,r12@__secondary_switched address

366ARM(add pc,r10,#PROCINFO_INITFUNC)@initialise processor 367@(return control reg)

368THUMB(add r12,r10,#PROCINFO_INITFUNC)

369THUMB(mov pc,r12)

370ENDPROC(secondary_startup)

371

372/*

373*r6=&secondary_data

375ENTRY(__secondary_switched)

376ldr sp,[r7,#4]@get secondary_data.stack 377mov fp,#0

378b secondary_start_kernel

379ENDPROC(__secondary_switched)

380

381.align

382

383.type__secondary_data,%object

384__secondary_data:

385.long.

386.long secondary_data

387.long__secondary_switched

388#endif/*defined(CONFIG_SMP)*/

3

390

391

392/*

393*在最后启动MMU前,设置一些常用位Essentially

394*其实,这里只是加载了页表指针和域访问控制数据寄存器

395*

396*

397*r0=cp#15control register

398*r1=machine ID

399*r2=atags or dtb pointer

400*r4=page table pointer

401*r9=processor ID

402*r13=最后要跳入的虚拟地址

403*/

404__enable_mmu:

405#ifdef CONFIG_ALIGNMENT_TRAP

406orr r0,r0,#CR_A

407#else

408bic r0,r0,#CR_A410#ifdef CONFIG_CPU_DCACHE_DISABLE

411bic r0,r0,#CR_C

412#endif

413#ifdef CONFIG_CPU_BPREDICT_DISABLE

414bic r0,r0,#CR_Z

415#endif

416#ifdef CONFIG_CPU_ICACHE_DISABLE

417bic r0,r0,#CR_I

418#endif

419mov r5,#(domain_val(DOMAIN_USER,DOMAIN_MANAGER)|\

420domain_val(DOMAIN_KERNEL,DOMAIN_MANAGER)|\

421domain_val(DOMAIN_TABLE,DOMAIN_MANAGER)|\

422domain_val(DOMAIN_IO,DOMAIN_CLIENT))@设置域访问控制数据423mcr p15,0,r5,c3,c0,0@载入域访问控制数据到DACR

424mcr p15,0,r4,c2,c0,0@载入页表基址到TTBR0

425b__turn_mmu_on@开启MMU

426ENDPROC(__enable_mmu)

427

428/*

429*使能MMU.这完全改变了可见的内存地址空间结构。

430*您将无法通过这里跟踪执行。

431*如果你已对此进行探究,*请*在向邮件列表发送另一个新帖之前,

432*检查linux-arm-kernel的邮件列表归档

433*

434*r0=cp#15control register

435*r1=machine ID

436*r2=atags or dtb pointer

437*r9=processor ID

438*r13=最后要跳入的*虚拟*地址

439*

440*其他寄存器依赖上面的调用函数

441*/

442.align5

443__turn_mmu_on:444mov r0,r0

445mcr p15,0,r0,c1,c0,0@设置cp#15控制寄存器(启用MMU)446mrc p15,0,r3,c0,c0,0@read id reg

447mov r3,r3

448mov r3,r13@r3中装入最后要跳入的*虚拟*地址449mov pc,r3@跳转到__mmap_switched

450__enable_mmu_end:

451ENDPROC(__turn_mmu_on)

452

453

454#ifdef CONFIG_SMP_ON_UP

455__INIT

456__fixup_smp:

457and r3,r9,#0x000f0000@architecture version

458teq r3,#0x000f0000@CPU ID supported?

459bne__fixup_smp_on_up@no,assume UP

460

461bic r3,r9,#0x00ff0000

462bic r3,r3,#0x0000000f@mask0xff00fff0

463mov r4,#0x41000000

4orr r4,r4,#0x0000b000

465orr r4,r4,#0x00000020@val0x4100b020

466teq r3,r4@ARM11MPCore?

467moveq pc,lr@yes,assume SMP

468

469mrc p15,0,r0,c0,c0,5@read MPIDR

470and r0,r0,#0xc0000000@multiprocessing extensions and

471teq r0,#0x80000000@not part of a uniprocessor system? 472moveq pc,lr@yes,assume SMP

473

474__fixup_smp_on_up:

475adr r0,1f

476ldmia r0,{r3-r5}

477sub r3,r0,r3

478add r4,r4,r3479add r5,r5,r3

480b__do_fixup_smp_on_up

481ENDPROC(__fixup_smp)

482

483.align

4841:.word.

485.word__smpalt_begin

486.word__smpalt_end

487

488.pushsection.data

4.globl smp_on_up

490smp_on_up:

491ALT_SMP(.long1)

492ALT_UP(.long0)

493.popsection

494#endif

495

496.text

497__do_fixup_smp_on_up:

498cmp r4,r5

499movhs pc,lr

500ldmia{r0,r6}

501ARM(str r6,[r0,r3])

502THUMB(add r0,r0,r3)

503#ifdef__ARMEB__

504THUMB(mov r6,r6,ror#16)@Convert word order for big-endian. 505#endif

506THUMB(strh r6,[r0],#2)@For Thumb-2,store as two halfwords 507THUMB(mov r6,r6,lsr#16)@to be robust against misaligned r3. 508THUMB(strh r6,[r0])

509b__do_fixup_smp_on_up

510ENDPROC(__do_fixup_smp_on_up)

511

512ENTRY(fixup_smp)

513stmfd{r4-r6,lr}514mov r4,r0

515add r5,r0,r1

516mov r3,#0

517bl__do_fixup_smp_on_up

518ldmfd{r4-r6,pc}

519ENDPROC(fixup_smp)

520

521#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

522

523/*__fixup_pv_table-patch the stub instructions with the delta between

524*PHYS_OFFSET and PAGE_OFFSET,which is assumed to be16MiB aligned and 525*can be expressed by an immediate shifter operand.The stub instruction 526*has a form of'(add|sub)rd,rn,#imm'.

527*/

528__HEAD

529__fixup_pv_table:

530adr r0,1f

531ldmia r0,{r3-r5,r7}

532sub r3,r0,r3@PHYS_OFFSET-PAGE_OFFSET

533add r4,r4,r3@adjust table start address

534add r5,r5,r3@adjust table end address

535add r7,r7,r3@adjust__pv_phys_offset address

536str r8,[r7]@save computed PHYS_OFFSET to__pv_phys_offset

537#ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

538mov r6,r3,lsr#24@constant for add/sub instructions

539teq r3,r6,lsl#24@must be16MiB aligned

540#else

541mov r6,r3,lsr#16@constant for add/sub instructions

542teq r3,r6,lsl#16@must bekiB aligned

543#endif

544THUMB(it ne@cross section branch)

545bne__error

546str r6,[r7,#4]@save to__pv_offset

547b__fixup_a_pv_table

548ENDPROC(__fixup_pv_table)549

550.align

5511:.long.

552.long__pv_table_begin

553.long__pv_table_end

5542:.long__pv_phys_offset

555

556.text

557__fixup_a_pv_table:

558#ifdef CONFIG_THUMB2_KERNEL

559#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT 560lsls r0,r6,#24

561lsr r6,#8

562beq1f

563clz r7,r0

5lsr r0,#24

565lsl r0,r7

566bic r0,0x0080

567lsrs r7,#1

568orrcs r0,#0x0080

569orr r0,r0,r7,lsl#12

570#endif

5711:lsls r6,#24

572beq4f

573clz r7,r6

574lsr r6,#24

575lsl r6,r7

576bic r6,#0x0080

577lsrs r7,#1

578orrcs r6,#0x0080

579orr r6,r6,r7,lsl#12

580orr r6,#0x4000

581b4f

5822:@at this point the C flag is always clear 583add r7,r3

584#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

585ldrh ip,[r7]

586tst ip,0x0400@the i bit tells us LS or MS byte

587beq3f

588cmp r0,#0@set C flag,and...

5biceq ip,0x0400@immediate zero value has a special encoding 590streqh ip,[r7]@that requires the i bit cleared

591#endif

5923:ldrh ip,[r7,#2]

593and ip,0x8f00

594orrcc ip,r6@mask in offset bits31-24

595orrcs ip,r0@mask in offset bits23-16

596strh ip,[r7,#2]

5974:cmp r4,r5

598ldrcc r7,[r4],#4@use branch for delay slot

599bcc2b

600bx lr

601#else

602#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

603and r0,r6,#255@offset bits23-16

604mov r6,r6,lsr#8@offset bits31-24

605#else

606mov r0,#0@just in case...

607#endif

608b3f

6092:ldr ip,[r7,r3]

610bic ip,ip,#0x000000ff

611tst ip,#0x400@rotate shift tells us LS or MS byte

612orrne ip,ip,r6@mask in offset bits31-24

613orreq ip,ip,r0@mask in offset bits23-16

614str ip,[r7,r3]

6153:cmp r4,r5

616ldrcc r7,[r4],#4@use branch for delay slot

617bcc2b

618mov pc,lr619#endif

620ENDPROC(__fixup_a_pv_table)

621

622ENTRY(fixup_pv_table)

623stmfd{r4-r7,lr}

624ldr r2,2f@get address of__pv_phys_offset 625mov r3,#0@no offset

626mov r4,r0@r0=table start

627add r5,r0,r1@r1=table size

628ldr r6,[r2,#4]@get__pv_offset

629bl__fixup_a_pv_table

630ldmfd{r4-r7,pc}

631ENDPROC(fixup_pv_table)

632

633.align

6342:.long__pv_phys_offset

635

636.data

637.globl__pv_phys_offset

638.type__pv_phys_offset,%object

639__pv_phys_offset:

0.long0

1.size__pv_phys_offset,.-__pv_phys_offset

2__pv_offset:

3.long0

4#endif

5

6#include"head-common.S"

arch/arm/kernel/head-common.S

7/*

8*linux/arch/arm/kernel/head-common.S

9*

650*Copyright(C)1994-2002Russell King651*Copyright(c)2003ARM Limited

652*All Rights Reserved

653*

654*This program is free software;you can redistribute it and/or modify

655*it under the terms of the GNU General Public License version2as

656*published by the Free Software Foundation.

657*

658*/

659

660#define ATAG_CORE0x54410001

661#define ATAG_CORE_SIZE((2*4+3*4)>>2)

662#define ATAG_CORE_SIZE_EMPTY((2*4)>>2)

663

6#ifdef CONFIG_CPU_BIG_ENDIAN

665#define OF_DT_MAGIC0xd00dfeed

666#else

667#define OF_DT_MAGIC0xedfe0dd0/*0xd00dfeed in big-endian*/

668#endif

669

670/*

671*异常处理.一些我们无法处理的错误.

672*我们应当告诉用户(这些错误信息),但因为我们甚至无法保证是在正确的架构上运行,673*所以我们什么都不做(死循环)。

674*

675*如果CONFIG_DEBUG_LL被设置,我们试图打印出错误信息,

676*并希望这可以对我们有帮助(例如这对bootloader没有提供适当的处理器ID

677*是有帮助的).

678*/

679__HEAD

680

681/*确定r2(内核启动参数)指针的有效性。The heuristic要求

682*是4Byte对齐的、在物理内存的头16K中,且以ATAG_CORE标记开头。

683*如果选择了CONFIG_OF_FLATTREE,dtb指针也是可以接受的.

684*

685*在这个函数的未来版本中可能会对物理地址的要求更为宽松,686*且如果有必要的话,可能可以移动ATAGS数据块.

687*

688*返回:

6*r2可能是有效的atags指针,有效的dtb指针,或者0

690*r5,r6被篡改

691*/

692__vet_atags:

693tst r2,#0x3@是否4Byte对齐?

694bne1f@不是则认为指针无效,返回

695

696ldr r5,[r2,#0]@获取r2指向的前4Byte,用于下面测试

697#ifdef CONFIG_OF_FLATTREE

698ldr r6,=OF_DT_MAGIC@is it a DTB?

699cmp r5,r6

700beq2f

701#endif

702

703/*内核启动参数块的规范是:

704*(wait for updata)

705*/

706cmp r5,#ATAG_CORE_SIZE@第一个tag是ATAG_CORE吗?测试的是tag_header中的size

707@如果为ATAG_CORE,那么必为ATAG_CORE_SIZE 708cmpne r5,#ATAG_CORE_SIZE_EMPTY@如果第一个tag的tag_header中的size为ATAG_CORE_SIZE_EMPTY

709@说明此处也有atags

710bne1f

711ldr r5,[r2,#4]@第一个tag_header的tag(魔数)

712ldr r6,=ATAG_CORE@获取ATAG_CORE的魔数

713cmp r5,r6@判断第一个tag是否为ATAG_CORE

714bne1f@不是则认为指针无效,返回

715

7162:mov pc,lr@atag/dtb指针有效

717

7181:mov r2,#0719mov pc,lr

720ENDPROC(__vet_atags)

721

722/*

723*以下的代码段是在MMU开启的状态下执行的,

724*而且使用的是绝对地址;这不是位置无关代码.

725*

726*r0=cp#15控制寄存器值

727*r1=machine ID

728*r2=atags/dtb pointer

729*r9=processor ID

730*/

731__INIT

732__mmap_switched:

733adr r3,__mmap_switched_data

734

735ldmia{r4,r5,r6,r7}

736cmp r4,r5@如果有必要,拷贝数据段。

737@对比__data_loc和_sdata

738@__data_loc是数据段在内核代码映像中的存储位置739@_sdata是数据段的链接位置(在内存中的位置)740@如果是XIP技术的内核,这两个数据肯定不同7411:cmpne r5,r6@检测数据是否拷贝完成

742ldrne fp,[r4],#4

743strne fp,[r5],#4

744bne1b

745

746mov fp,#0@清零BSS段(and zero fp)

7471:cmp r6,r7@检测是否完成

748strcc fp,[r6],#4

749bcc1b

750

751/*这里将需要的数据从寄存器中转移到全局变量中,

752*因为最后会跳入C代码,寄存器会被使用。

753*/754ARM(ldmia r3,{r4,r5,r6,r7,sp})

755THUMB(ldmia r3,{r4,r5,r6,r7})

756THUMB(ldr sp,[r3,#16])

757str r9,[r4]@保存processor ID到全局变量processor_id

758str r1,[r5]@保存machine type到全局变量__machine_arch_type 759str r2,[r6]@保存atags指针到全局变量__atags_pointer

760bic r4,r0,#CR_A@清除cp15控制寄存器值的'A'bit(禁用对齐错误检查)

761stmia r7,{r0,r4}@保存控制寄存器值到全局变量cr_alignment(在arch/arm/kernel/entry-armv.S)

762b start_kernel@跳入C代码(init/main.c)

763ENDPROC(__mmap_switched)

7

765.align2

766.type__mmap_switched_data,%object

767__mmap_switched_data:

768.long__data_loc@r4

769.long_sdata@r5

770.long__bss_start@r6

771.long_end@r7

772.long processor_id@r4

773.long__machine_arch_type@r5

774.long__atags_pointer@r6

775.long cr_alignment@r7

776.long init_thread_union+THREAD_START_SP@sp

777.size__mmap_switched_data,.-__mmap_switched_data

778

779/*

780*这里提供一个C-API版本的__lookup_processor_type

781*/

782ENTRY(lookup_processor_type)

783stmfd{r4-r6,r9,lr}

784mov r9,r0

785bl__lookup_processor_type

786mov r0,r5

787ldmfd{r4-r6,r9,pc}

788ENDPROC(lookup_processor_type)

7

790/*

791*读取处理器ID寄存器(CP#15,CR0),并且查找编译时确定的处理器

792*支持列表.注意:我们不能对__proc_info使用绝对地址,

793*因为我们还没有重新初始化页表(MMU已关闭,之前是解压时使用的1:1映射)。

794*(我们不在正确的地址空间:内核是按虚拟地址(0xc00008000)编译的,

795*而现在我们运行在MMU关闭的情况下)。

796*我们必须计算偏移量。

797*

798*r9=cpuid

799*Returns:

800*r3,r4,r6被篡改

801*r5=proc_info指针(物理地址空间)

802*r9=cpuid(保留)

803*/

804__CPUINIT

805__lookup_processor_type:

806adr r3,__lookup_processor_type_data@获取运行时的地址数据

807ldmia r3,{r4-r6}@获取编译时确定的地址数据(虚拟地址)

808sub r3,r3,r4@获取地址偏移virt&phys(r3)

809add r5,r5,r3@将虚拟地址空间转换为物理地址空间

810add r6,r6,r3@r5=__proc_info_begin r6=__proc_info_end

8111:ldmia r5,{r3,r4}@获取proc_info_list结构体中的value,mask

812and r4,r4,r9@利用掩码处理从CP15获取的处理器ID

813teq r3,r4@对比编译时确定的处理器ID

814beq2f@若处理器ID匹配,返回

815add r5,r5,#PROC_INFO_SZ@利用sizeof(proc_info_list)跳入下一个处理器ID的匹配

816cmp r5,r6@是否已经处理完proc_info_list数据

817blo1b@如果还有proc_info_list数据,再次检查匹配

818mov r5,#0@否则,编译的内核与此处理器不匹配,r5=#0 8192:mov pc,lr

820ENDPROC(__lookup_processor_type)821

822/*

823*参见中关于__proc_info结构体的信息.

824*/

825.align2

826.type__lookup_processor_type_data,%object

827__lookup_processor_type_data:

828.long.

829.long__proc_info_begin

830.long__proc_info_end

831.size__lookup_processor_type_data,.-__lookup_processor_type_data 832

833/*

834*处理器ID不匹配时的入口

835*如果启用了调试信息,会从consol打印提示信息

836*之后会进入__error的死循环

837*/

838__error_p:

839#ifdef CONFIG_DEBUG_LL

840adr r0,str_p1

841bl printascii

842mov r0,r9

843bl printhex8

844adr r0,str_p2

845bl printascii

846b__error

847str_p1:.asciz"\\nError:unrecognized/unsupported processor variant(0x" 848str_p2:.asciz").\\n"

849.align

850#endif

851ENDPROC(__error_p)

852

853/*

854*出错时的死循环入口

855*/856__error:

857#ifdef CONFIG_ARCH_RPC

858/*

859*出错时屏幕变红-RiscPC only. 860*/

861mov r0,#0x02000000 862mov r3,#0x11

863orr r3,r3,r3,lsl#8

8orr r3,r3,r3,lsl#16

865str r3,[r0],#4

866str r3,[r0],#4

867str r3,[r0],#4

868str r3,[r0],#4

869#endif

8701:mov r0,r0

871b1b

872ENDPROC(__error)下载本文

显示全文
专题