当前位置:文档之家› linux内核启动 Android系统启动过程详解

linux内核启动 Android系统启动过程详解

linux内核启动+Android系统启动过程详解第一部分:汇编部分Linux启动之linux-rk3288-tchip/kernel/arch/arm/boot/compressed/ head.S分析这段代码是linux boot后执行的第一个程序,完成的主要工作是解压内核,然后跳转到相关执行地址。

这部分代码在做驱动开发时不需要改动,但分析其执行流程对是理解android 的第一步开头有一段宏定义这是gnu arm汇编的宏定义。

关于GUN 的汇编和其他编译器,在指令语法上有很大差别,具体可查询相关GUN汇编语法了解另外此段代码必须不能包括重定位部分。

因为这时一开始必须要立即运行的。

所谓重定位,比如当编译时某个文件用到外部符号是用动态链接库的方式,那么该文件生成的目标文件将包含重定位信息,在加载时需要重定位该符号,否则执行时将因找不到地址而出错#ifdef DEBUG//开始是调试用,主要是一些打印输出函数,不用关心#if defined(CONFIG_DEBUG_ICEDCC)……具体代码略#endif宏定义结束之后定义了一个段,.section ".start", #alloc, #execinstr这个段的段名是 .start,#alloc表示Section contains allocated data, #execinstr表示Sectioncontains executable instructions.生成最终映像时,这段代码会放在最开头.alignstart:.type start,#function /*.type指定start这个符号是函数类型*/.rept 8mov r0, r0 //将此命令重复8次,相当于nop,这里是为中断向量保存空间.endrb 1f.word 0x016f2818 @ Magic numbers to help the loader.word start @ absolute load/run zImage//此处保存了内核加载和运行的地址,实质上也是本函数的运行地址address.word _edata @ 内核结束地址//注意这些地址在顶层vmlixu.lds(具体在/kernel文件夹里)里进行了定义,是链接的地址,加载内核后可能会进行重定位1: mov r7, r1 @ 保存architecture ID,这里是从bootload传递进来的mov r8, r2 @ 保存参数列表atags指针r1和r2中分别存放着由bootloader传递过来的architectureID和指向标记列表的指针。

这里将这两个参数先保存。

#ifndef __ARM_ARCH_2__/** Booting from Angel - need to enter SVC mode and disable* FIQs/IRQs (numeric definitions from angel arm.h source).* We only do this if we were in user mode on entry.*/读取cpsr并判断是否处理器处于supervisor模式——从bootload进入kernel,系统已经处于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。

之后是再次确认中断关闭,并完成cpsr写入Angel 是ARM的调试协议,一般用的是MULTI-ICE。

ANGLE 需要在板子上有驻留程序,然后通过串口就可以调试了。

用过的AXD或trace调试环境的话,对此应该比较熟悉。

not_angel: //若不是通过angel调试进入内核mrs r2, cpsr @ turn off interrupts toorr r2, r2, #0xc0 @ prevent angel from runningmsr cpsr_c, r2 //这里将cpsr中I、F位分别置“1”,关闭IRQ和FIQ#elseteqp pc, #0x0c000003 @ turn offinterrupts常用TEQP PC,#(新模式编号)来改变模式#endif另外链接器会把一些处理器相关的代码链接到这个位置,也就是arch/arm/boot/compressed/head-xxx.S文件中的代码。

在高通平台下,这个文件是head-msm.S连接脚是compress/vmlinux.lds,其中部分内容大致如下,在连接时,连接器根据每个文件中的段名将相同的段合在一起,比如将head.S和head-msm.S的.start段合在一起SECTIONS{. = TEXT_START;_text = .;.text : {_start = .;*(.start)*(.text)*(.text.*)*(.fixup)*(.gnu.warning)*(.rodata)*(.rodata.*)*(.glue_7)*(.glue_7t)*(.piggydata). = ALIGN(4);}_etext = .;}下面即进入.text段.textadr r0, LC0 //当前运行时LC0符号所在地址位置,注意,这里用的是adr指令,这个指令会根据目前PC的值,计算符号相对于PC的位置,是个相对地址。

之所以这样做,是因为下面指令用到了绝对地址加载ldmia指令,必须要调整确定目前LC0的真实位置,这个位置也就是用adr 来计算ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}subs r0, r0, r1 @ //这里获得当前LCD0实际地址与链接地址差值//r1即是LC0的连接地址,也即由vmlinux.lds定位的地址//差值存入r0中。

beq not_relocated //如果相等不需要重定位,因为已经在正确的//地址运行了。

重定位的原因是,MMU单元未使能,不能进行地址映射,必须要手工重定位。

下面举个简单例子说明:如果连接地址是0xc0000000,那么LC0的连接地址假如连接为0xc0000010,那么LC0相对于连接起始地址的差为0x10,当此段代码是从0xc0000000运行的话,那么执行adrr0,LC0的值实际上按下面公式计算:R0=PC+0x10,由于PC=连接处的值,可知,此时是在ram 中运行,同理如果是在不是在连接处运行,则假设是在0x00000000处运行,则R0=0x00000000+0x10,可知,此时不是在ram的连接处运行。

上面这几行代码用于判断代码是否已经重定位到内存中,LC0这个符号在head.S中定义如下,实质上相当于c语言的全局数据结构,结构的每个域存储的是一个指针。

指针本身的值代表不同的代码段,已经在顶层连接脚本vmlinux.lds里进行了赋值,比如_start是内核开始的地址.type LC0, #objectLC0: .word LC0 @ r1//这个要加载到r1中的LC0是链接时LC0的地址.word __bss_start @ r2.word _end @ r3.word zreladdr @ r4.word _start @ r5.word _got_start @ r6.word _got_end @ ip.word user_stack+4096@ sp通过当前运行时LC0的地址与链接器所链接的地址进行比较判断。

若相等则是运行在链接的地址上。

如果不是运行在链接的地址上,则下面的代码必须修改相关地址,进行重新运行/** r5 - zImage base address* r6 - GOT start* ip - GOT end*///修正实际运行的位置,否则跳转指令就找不到相关代码add r5, r5, r0 //修改内核映像基地址add r6, r6, r0add ip, ip, r0 //修改got表的起始和结束位置#ifndef CONFIG_ZBOOT_ROM/*若没有定义CONFIG_ZBOOT_ROM,此时运行的是完全位置无关代码位置无关代码,也就是不能有绝对地址寻址。

所以为了保持相对地址正确,需要将bss段以及堆栈的地址都进行调整* r2 - BSS start* r3 - BSS end* sp - stack pointer*/add r2, r2, r0add r3, r3, r0add sp, sp, r0//全局符号表的地址也需要更改,否则,对全局变量引用将会出错1: ldr r1, [r6, #0] @ relocate entries in the GOTadd r1, r1, r0 @ table. This fixes up thestr r1, [r6], #4 @ C references.cmp r6, ipblo 1b#else //若定义了CONFIG_ZBOOT_ROM,只对got表中在bss段以外的符号进行重定位1: ldr r1, [r6, #0] @ relocate entries in the GOTcmp r1, r2 @ entry < bss_start ||cmphs r3, r1 @ _end < entryaddlo r1, r1, r0 @ table. This fixes up thestr r1, [r6], #4 @ C references.cmp r6, ipblo 1b#endif如果运行当前运行地址和链接地址相等,则不需进行重定位。

直接清除bss段not_relocated: mov r0, #01: str r0, [r2], #4 @ clear bssstr r0, [r2], #4str r0, [r2], #4str r0, [r2], #4cmp r2, r3blo 1b之后跳转到cache_on处bl cache_oncache_on定义.align 5cache_on: mov r3, #8 @ cache_on functionb call_cache_fn把r3的值设为8。

这是一个偏移量,也就是索引proc_types中的操作函数。

然后跳转到call_cache_fn。

这个函数的定义如下:call_cache_fn:adr r12, proc_types //把proc_types的相对地址加载到r12中#ifdef CONFIG_CPU_CP15mrc p15, 0, r6, c0, c0 @ get processor ID #elseldr r6, =CONFIG_PROCESSOR_ID#endif1: ldr r1, [r12, #0] @ get valueldr r2, [r12, #4] @ get maskeor r1, r1, r6 @ (real ^ match)tst r1, r2 @是否和CPU ID 匹配?addeq pc, r12, r3 @用刚才的偏移量,查找//到cache操作函数,找到后就执行相关操作,比如执行b __armv7_mmu_cache_on//add r12, r12, #4*5 //如果不相等,则偏移到下个proc_types结构处b 1baddeq pc, r12, r3 @ call cache functionproc_type的定义如下,实质上还是一张数据结构表.type proc_types,#objectproc_types:.word 0x41560600 @ ARM6/610.word 0xffffffe0b __arm6_mmu_cache_off @ works, but slowb __arm6_mmu_cache_offmov pc, lr@ b __arm6_mmu_cache_on@ untested@ b __arm6_mmu_cache_off@ b __armv3_mmu_cache_flush.word 0x00000000 @ old ARM ID.word 0x0000f000mov pc, lrmov pc, lrmov pc, lr.word 0x41007000 @ ARM7/710.word 0xfff8fe00b __arm7_mmu_cache_offb __arm7_mmu_cache_offmov pc, lr.word 0x41807200 @ ARM720T (writethrough).word 0xffffff00b __armv4_mmu_cache_onb __armv4_mmu_cache_offmov pc, lr.word 0x41007400 @ ARM74x.word 0xff00ff00b __armv3_mpu_cache_onb __armv3_mpu_cache_offb __armv3_mpu_cache_flush.word 0x41009400 @ ARM94x.word 0xff00ff00b __armv4_mpu_cache_onb __armv4_mpu_cache_offb __armv4_mpu_cache_flush.word 0x00007000 @ ARM7 IDs.word 0x0000f000mov pc, lrmov pc, lrmov pc, lr@ Everything from here on will be the new ID system..word 0x4401a100 @ sa110 / sa1100.word 0xffffffe0b __armv4_mmu_cache_onb __armv4_mmu_cache_offb __armv4_mmu_cache_flush.word 0x6901b110 @ sa1110.word 0xfffffff0b __armv4_mmu_cache_onb __armv4_mmu_cache_offb __armv4_mmu_cache_flush@ These match on the architecture ID.word 0x00020000 @.word 0x000f0000 //b __armv4_mmu_cache_onb __armv4_mmu_cache_on //指令的地址b __armv4_mmu_cache_offb __armv4_mmu_cache_flush.word 0x00050000 @ ARMv5TE.word 0x000f0000b __armv4_mmu_cache_onb __armv4_mmu_cache_offb __armv4_mmu_cache_flush.word 0x00060000 @ ARMv5TEJ.word 0x000f0000b __armv4_mmu_cache_onb __armv4_mmu_cache_offb __armv4_mmu_cache_flush.word 0x0007b000 @ ARMv6.word 0x0007f000b __armv4_mmu_cache_onb __armv4_mmu_cache_offb __armv6_mmu_cache_flush.word 0 @ unrecognised type.word 0mov pc, lrmov pc, lrmov pc, lr.size proc_types, . - proc_types找到执行的cache函数后,就用上面的addeq pc, r12, r3直接跳转,例如执行下面这个处理器结构的cache函数__armv7_mmu_cache_on:mov r12, lr //注意,这里需要手工保存返回地址!!这样做的原因是下面的bl指令会覆盖掉原来的lr,为保证程序正确返回,需要保存原来lr的值bl __setup_mmumov r0, #0mcr p15, 0, r0, c7, c10, 4 @ drain write buffermcr p15, 0, r0, c8, c7, 0 @ flush I,D TLBsmrc p15, 0, r0, c1, c0, 0 @ read control regorr r0, r0, #0x5000 @ I-cache enable, RR cache replacementorr r0, r0, #0x0030bl __common_mmu_cache_onmov r0, #0mcr p15, 0, r0, c8, c7, 0 @ flush I,D TLBsmov pc, r12 //返回到cache_on这个函数首先执行__setup_mmu,然后清空write buffer、I/Dcache、TLB.接着打开i-cache,设置为Round-robin replacement。

相关主题