13Linux启动流程.docx
《13Linux启动流程.docx》由会员分享,可在线阅读,更多相关《13Linux启动流程.docx(13页珍藏版)》请在冰豆网上搜索。
13Linux启动流程
Linux启动流程
1.Linux2.6.31启动流程分析
1.1.Linux/arch/arm/boot/compressed/head.s
u_boot跳到内核最先执行的代码是linux/arch/arm/boot/compressed/head.S。
这个程序完成的主要工作是解压内核,然后跳转到相关执行地址。
这部分代码在做驱动开发时不需要改动。
1.2.linux/arch/arm/kernel/head.S分析
linux/arch/arm/kernel/head.S用汇编代码完成,是内核最先执行的一个文件。
当解压缩代码执行完后,就开始执行linux/arch/arm/kernel/目录下真正的linux内核代码。
内核链接文件linux/arch/arm/kernel/vmlinux.lds指明:
真正的linux内核代码,开始所处的段空间为.text.head,也即内核代码段的头。
这一段汇编代码的主要作用,是检查cpuid,architecturenumber,初始化页表、cpu、bbs等操作,并跳到start_kernel函数。
这一段汇编代码是linux启动的汇编部分的关键代码段,大致流程为:
寻找CPU类型,查找机器信息,解析内核参数列表,创建内存分页机制,__lookup_processor_type,__lookup_machine_type,__vet_atags函数都在kernel/head-comm.S内,这个文件实际上是被包含在head.S内。
这一段汇编代码在执行前,处理器的状态应满足:
r0-shouldbe0
r1-uniquearchitecturenumber
MMU-off
I-cache-onoroff
D-cache-off
在代码的分析过程中略去一些条件编译的代码。
__INIT
.typestext,%function
ENTRY(stext)/*内核入口点*/
msrcpsr_c,#PSR_F_BIT|PSR_I_BIT|MODE_SVC@ensuresvcmode
@andirqsdisabled/*置当前程序状态寄存器,关中断,svc模式*/
mrcp15,0,r9,c0,c0@getprocessorid/*读processorid到r9*/
bl__lookup_processor_type@r5=procinfor9=cupid/*查找运行的cpu的id值,和此linux编译支持的id值是否有相等*/
movsr10,r5@invalidprocessor(r5=0)?
beq__error_p@yes,error'p'
bl__lookup_machine_type@r5=machinfo/*判断体系类型是否支持*/
movsr8,r5@invalidmachine(r5=0)?
beq__error_a@yes,error'a'
bl__vet_atags/*剖析__vet_atags内核参数列表,判断第一个参数类型是不是ATAG_CORE*/
bl__create_page_tables/*创建临时页表,供初始化使用*/
ldrr13,__switch_data@addresstojumptoafter
@mmuhasbeenenabled
adrlr,__enable_mmu@return(PIC)address
addpc,r10,#PROCINFO_INITFUNC
下面分被对于上述的四个函数进行分析:
在文件linux/arch/arm/kernel/head-common.S
一、__lookup_processor_type函数
/*
*ReadprocessorIDregister(CP#15,CR0),andlookupinthelinker-built
*supportedprocessorlist.Notethatwecan'tusetheabsoluteaddresses
*forthe__proc_infolistssincewearen'trunningwiththeMMUon
*(andtherefore,wearenotinthecorrectaddressspace).Wehaveto
*calculatetheoffset.
*
*r9=cpuid
*Returns:
*r3,r4,r6corrupted
*r5=proc_infopointerinphysicaladdressspace
*r9=cpuid(preserved)
*/
.type__lookup_processor_type,%function
__lookup_processor_type:
adrr3,3f/*取标号3的地址*/
ldmdar3,{r5-r7}/*r5:
__proc_info_begin;r6:
__proc_info_end;r7:
标号3的地址*/
subr3,r3,r7@getoffsetbetweenvirt&phys/*由于adrr3,3f取得标号3的物理地址,此时r7是标号3的虚拟地址,通过此指令获得virt&phys的差值,此时为负值*/
addr5,r5,r3@convertvirtaddressesto
addr6,r6,r3@physicaladdressspace
/*把标号__proc_info_begin,__proc_info_end从虚拟地址转化为物理地址,由于此时mmu没有开启,所以必须以物理地址来进行访问内存*/
1:
ldmiar5,{r3,r4}@value,mask/*从proc_info_list获得cpu_val(r3)和cpu_mask(r4)*/
andr4,r4,r9@maskwantedbits
teqr3,r4/*r9为从cp15读出来的cpuid,判断linux是否支持*/
beq2f/*相等则退出,r5为匹配proc_info的物理地址,
否则在下一个proc_info_list中查找*/
addr5,r5,#PROC_INFO_SZ@sizeof(proc_info_list)
cmpr5,r6
blo1b
movr5,#0@unknownprocessor/*没有找到匹配的,返回r5=0*/
2:
movpc,lr
/****************************************************************************/
/*
*Lookininclude/asm-arm/procinfo.handarch/arm/kernel/arch.[ch]for
*moreinformationaboutthe__proc_infoand__arch_infostructures.
*/
.long__proc_info_begin
.long__proc_info_end
3:
.long.
.long__arch_info_begin
.long__arch_info_end
标号__proc_info_begin__proc_info_end由链接脚本vmlinux.lds.S定义,在进行链接时会把其中内容填充。
__proc_info_begin=.;
*(.proc.info.init)
__proc_info_end=.;
__arch_info_begin=.;
*(.arch.info.init)
__arch_info_end=.;
二、__lookup_machine_type函数
/*
*Lookupmachinearchitectureinthelinker-buildlistofarchitectures.
*Notethatwecan'tusetheabsoluteaddressesforthe__arch_info
*listssincewearen'trunningwiththeMMUon(andtherefore,weare
*notinthecorrectaddressspace).Wehavetocalculatetheoffset.
*
*r1=machinearchitecturenumber
*Returns:
*r3,r4,r6corrupted
*r5=mach_infopointerinphysicaladdressspace
*/
.type__lookup_machine_type,%function
__lookup_machine_type:
/*此函数的实现同__lookup_processor_type,分析略*/
adrr3,3b
ldmiar3,{r4,r5,r6}
subr3,r3,r4@getoffsetbetweenvirt&phys
addr5,r5,r3@convertvirtaddressesto
addr6,r6,r3@physicaladdressspace
1:
ldrr3,[r5,#MACHINFO_TYPE]@getmachinetype
teqr3,r1@matchesloadernumber?
beq2f@found
addr5,r5,#SIZEOF_MACHINE_DESC@nextmachine_desc
cmpr5,r6
blo1b
movr5,#0@unknownmachine
2:
movpc,lr
3、__vet_atags函数
内核参数列表一般放在内核前面16K地址空间处。
列表的表项由structtag构成,每个structtag有常见的以下类型:
:
ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。
这些类型是宏定义,比如#defineATAG_CORE0x54410001
arch/arm/include/asm/setup.h
structtag_header{
__u32size;
__u32tag;
};
structtag{
structtag_headerhdr;
union{
structtag_corecore;//有效的内核
structtag_mem32mem;
structtag_videotextvideotext;
structtag_ramdiskramdisk;//文件系统
structtag_initrdinitrd;//临时根文件系统
structtag_serialnrserialnr;
structtag_revisionrevision;
structtag_videolfbvideolfb;
structtag_cmdlinecmdline;//命令行
}u;
};
/*Determinevalidityofther2atagspointer.Theheuristicrequires
*thatthepointerbealigned,inthefirst16kofphysicalRAMand
*thattheATAG_COREmarkerisfirstandpresent.Futurerevisions
*ofthisfunctionmaybemorelenientwiththephysicaladdressand
*mayalsobeabletomovetheATAGSblockifnecessary.
*
*r8=machinfo
*
*Returns:
*r2eithervalidatagspointer,orzero
*r5,r6corrupted
*/
__vet_atags:
tstr2,#0x3@aligned?
bne1f
ldrr5,[r2,#0]@isfirsttagATAG_CORE?
subsr5,r5,#ATAG_CORE_SIZE
bne1f
ldrr5,[r2,#4]
ldrr6,=ATAG_CORE
cmpr5,r6
bne1f
movpc,lr@atagpointerisok
四、__create_page_tables函数
本例中__create_page_tables函数在linux/arch/arm/kernel/head.S中。
/*
*Setuptheinitialpagetables.Weonlysetupthebarest
*amountwhicharerequiredtogetthekernelrunning,which
*generallymeansmappinginthekernelcode.
*
*r8=machinfo
*r9=cpuid
*r10=procinfo
*
*Returns:
*r0,r3,r6,r7corrupted
*r4=physicalpagetableaddress
*/
.type__create_page_tables,%function
__create_page_tables:
/*假设ram的物理起始地址为0x40000000*/
ldrr5,=PHYS_OFFSET
pgtblr4,r5@pagetableaddress/*r4=0x40004000页表在物理地址中基址*/
/*
*Clearthe16Klevel1swapperpagetable
*/
movr0,r4
movr3,#0
addr6,r0,#0x4000
1:
strr3,[r0],#4/*多条同样的指令在多级流水线体系结构中效率会很高*/
strr3,[r0],#4
strr3,[r0],#4
strr3,[r0],#4
teqr0,r6
bne1b/*把物理地址0x40004000至0x40008000清零*/
ldrr7,[r10,#PROCINFO_MMUFLAGS]@mmuflags
/*
*CreateidentitymappingforfirstMBofkernelto
*caterfortheMMUenable.Thisidentitymapping
*willberemovedbypaging_init().Weuseourcurrentprogram
*countertodeterminecorrespondingsectionbaseaddress.
*/
movr6,pc,lsr#20@startofkernelsection/*r6=0x300*/
orrr3,r7,r6,lsl#20@flags+kernelbase/*r3=0x40000000|muflag=0x40000c1e*/
strr3,[r4,r6,lsl#2]@identitymapping/*0x40004c00中的内容为0x40000c1e*/
/*这一部分把物理地址0x40000000,映射到0x40000000,也就是完全映射,保证了mmu开启,都可以正常取指运行*/
/*
*Nowsetupthepagetablesforourkerneldirect
*mappedregion.
*/
addr0,r4,#(KERNEL_START&0xff000000)>>18
@startofkernel/*r0=0x40007000*/
strr3,[r0,#(KERNEL_START&0x00f00000)>>18]!
/*0x40007000中的内容为0x40000c1e*/
ldrr6,=(KERNEL_END-1)
addr0,r0,#4
addr6,r4,r6,lsr#18
1:
cmpr0,r6
addr3,r3,#1<<20
strlsr3,[r0],#4
bls1b
/*
*Thenmapfirst1MBoframincaseitcontainsourbootparams.
*/
addr0,r4,#PAGE_OFFSET>>18
orrr6,r7,#PHYS_OFFSET
strr6,[r0]/*r0=0x40007000,r6=0x40000c1e*/
movpc,lr
下面以从虚拟地址0xc0005123到物理地址0x40005123的转换为例来说明mmu和页表共同的工作过程。
cp15的寄存器c2存放页表的基地址0x40004000,0x40004000&0xffffc000|((0xc0005123>>18)&0x3ffc)组成地址0x40007000,以此地址作为一级描述符地址,根据上述映射过程可以得到0x40007000中的内容是0x40000c1e,其中c1e说明是段地址,有读写权限(详见一级描述符格式说明),(0x40000c1e&0xfff00000)|(0xc0005123&0xfffff)的到地址0x4005123,此地址就是虚拟地址0xc0005123对应的物理地址。
/*****************************************************************************/
回到linux/arch/arm/kernel/head.S中
/*
*ThefollowingcallsCPUspecificcodeinapositionindependent
*manner.Seearch/arm/mm/proc-*.Sfordetails.r10=baseof
*xxx_proc_infostructureselectedby__lookup_machine_type
*above.Onreturn,theCPUwillbereadyfortheMMUtobe
*turnedon,andr0willholdtheCPUcontrolregistervalue.
*/
ldrr13,__switch_data@addresstojumptoafter
@mmuhasbeenenabled
adrlr,__enable_mmu@return(PIC)address
addpc,r10,#PROCINFO_INITFUNC/*此命令进行mmu寄存器的初始化可以参照cp15寄存器手册来分析实现的功能,执行完成后,执行函数__enable_mmu*/
/*
*SetupcommonbitsbeforefinallyenablingtheMMU.Essentially
*thisisjustloadingthepagetablepointeranddomainaccess
*registers.
*/
.type__enable_mmu,%function
__enable_mmu:
#ifdefCONFIG_ALIGNMENT_TRAP
orrr0,r0,#CR_A
#else
bicr0,r0,#CR_A
#endif
#ifdefCONFIG_CPU_DCACHE_DISABLE
bicr0,r0,#CR_C
#endif
#ifdefCONFIG_CPU_BPREDICT_DISABLE
bicr0,r0,#CR_Z
#endif
#ifdefCONFIG_CPU_ICACHE_DISABLE
bicr0,r0,#CR_I
#endif
movr5,#(domain_val(DOMAIN_USER,DOMAIN_MANAGER)|\
domain_val(DOMAIN_KERNEL,DOMAIN_MANAGER)|\
domain_val(DOMAIN_TABLE,DOMAIN_MANAGER)|\
domain_val(DOMAIN_IO,DOMAIN_CLIENT))
mcrp15,0,r5,c3,c0,0@loaddomainaccessregister
mcrp15,0,r4,c2,c0,0@loadpagetablepointer
b__turn_mmu_on
/*
*EnabletheMMU.Thiscompletelychangesthestructureofthevisible
*memoryspace.Youwillnotbeabletotraceexecutionthroughthis.
*Ifyouhaveanenquiryaboutthis,*please*checkthelinux-arm-kernel
*mailinglistar