已阅读5页,还剩7页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
linux 内核启动代码分析(我们使用的cpu是采用xscale技术的pxa250,具体的开发板是cerfboard。这里的代码解释都是根据pxa250进行的。不过,正如我们下面会看到,大部分的代码都是和具体的pxa型号无关的。另外,我们列出代码的行号仅仅为了解释方便,并不是代码在源文件中真正的行号。1. bootloader及内核解压bootloader将内核加载到内存中,设定一些寄存器,然后就将控制权交由内核了,这时并不需要打开mmu功能。一般,我们所说的内核都是指被压缩了的内核piggy.gz,其与head.o、misc.o和head-xscale.o一起构成最终的内核代码。启动内核的过程首先包括解压,最先执行的代码是:arch/arm/boot/compressed/head.sarch/arm/boot/compressed/head-xscale.sarch/arm/boot/compressed/misc.c上面代码主要任务是解压真正的内核代码,真正的内核代码是从head-armv.s开始,以后提到的内核都是指真正的内核:arch/arm/kernel/head-armv.s2. 内核启动第一阶段内核启动第一阶段是从entry(stext)到c语言函数start_kernel,这一部分涉及的代码有arch/arm/kernel/head-armv.sarch/arm/mm/proc-xscale.s在bootloader调用内核运行之前,要在指定的寄存器中存放参数,如下:r0 = 0, r1 = machine type number,对于mach_type_pxa_cerf是139r2 = physical address of tagged list in system ram.2.1 stextentry(stext)是整个内核的入口,其代码位于编译后生成的目标文件kernel.o中的.text.init区(section),最后被链接到内核的.init区。1.section .text.init,#alloc,#execinstr定义所属区2.typestext, #function定义符号stext为函数符号3entry(stext)4movr12, r05movr0, #f_bit | i_bit | mode_svc make sure svc mode6msrcpsr_c, r0 and all irqs disabled7bl_lookup_processor_type8teqr10, #0 invalid processor?9moveqr0, #p yes, error p10beq_error11bl_lookup_architecture_type12teqr7, #0 invalid architecture?13moveqr0, #a yes, error a14beq_error15bl_create_page_tables16adrlr, _ret return address17addpc, r10, #12 initialise processor18 (return control reg)第5行,准备进入svc工作模式,同时关闭中断(i_bit)和快速中断(f_bit)第7行,查看处理器类型,主要是为了得到处理器的id以及页表的flags,详细说明见下。第11行,查看一些体系结构的信息,详细说明见下。第15行,建立页表。第17行,跳转到处理器的初始化函数,其函数地址是从_lookup_processor_type中得到的,需要注意的是第16行,当处理器初始化完成后,会直接跳转到_ret去执行,这是由于初始化函数最后的语句是mov pc, lr。2.2 _lookup_processor_type我们先来看看_lookup_processor_type函数,我们知道arm系列的处理器有arm7、arm9、arm10以及xscale等,可是如何区分呢?在内核中有个结构struct proc_info_list,用来记录处理器相关的信息:19struct proc_info_list 20unsigned intcpu_val;21unsigned intcpu_mask;22unsigned long_cpu_mmu_flags;/* used by head-armv.s */23unsigned long_cpu_flush;/* used by head-armv.s */24const char*arch_name;25const char*elf_name;26unsigned intelf_hwcap;27struct proc_info_item*info;28struct processor*proc;29;对于采用xscale技术pxa250,具体的处理器相关信息存放在arch/arm/mm/proc-xscale.s中,这里抽出了与pxa250有关的信息,和struct proc_info_list对照就明白了:30.section ., #alloc, #execinstr 31_pxa250_proc_info:32.long0x6905210033.long0xfffff7f034#if cache_write_through35.long0x00000c0a36#else37.long0x00000c0e38#endif39b_xscale_setup40.longcpu_arch_name41.longcpu_elf_name42.longhwcap_swp|hwcap_half|hwcap_thumb|hwcap_fast_mult|hwcap_edsp43.longcpu_pxa250_info44.longxscale_processor_functions45.size_pxa250_proc_info, . - _pxa250_proc_info第39行是处理器初始化函数_xscale_setup的地址这里的一些结构随着我们对于代码的深入就会渐渐清晰了,现在我们不用过分纠缠与此。由于第30行.section指示符,上面定义的_pxa250_proc_info信息在编译的时候,被链接到.区中,而arch/arm/vmlinux-armv.lds中指定一个符号_proc_info_begin指向此.区的起始地址,而_proc_info_end指向此.区结尾的下一地址。这是gnu的ld的一些功能,各位若不清楚,可以查看ld的手册。_proc_info_begin = .;*(.)_proc_info_end = .;这样,我们通过循环就可以访问所有的struct proc_info_list类型的对象了。好了,做了这么多铺垫,我们来看看_lookup_processor_type函数吧:46_lookup_processor_type:47adrr5, 2f get runtime address 48ldmiar5, r7, r9, r1049subr5, r5, r10 convert addresses, offset between 50 runtime address and link address51addr7, r7, r5 to our address space52addr10, r9, r553mrcp15, 0, r9, c0, c0 get processor id541:ldmiar10, r5, r6, r8 value, mask, mmuflags55andr6, r6, r9 mask wanted bits56teqr5, r657moveqpc, lr58addr10, r10, #36 sizeof(proc_info_list)59cmpr10, r760blt1b61movr10, #0 unknown processor62movpc, lr6364/*65 * look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.ch for66 * more information about the _proc_info and _arch_info structures.67 */682:.long_proc_info_end69.long_proc_info_begin70.long2b71.long_arch_info_begin72.long_arch_info_end第6869行就是我们刚才说的指向.区起始和结尾地址的符号,在第47行,我们将第68行代码的地址放到r5中,那么作为指针,r5指向的(非是r5的内容)是.区结尾的下一地址_proc_info_end。第48行结果是r7=_proc_info_end,r9= _proc_info_begin,r10=第70行的地址这个函数执行完毕后,寄存器情况如下:r8 = page table flagsr9 = processor idr10 = pointer to processor structure(成功时),r10 = 0 (失败时)2.3 _lookup_architecture_type类似于struct proc_info_list,每个机器(某种开发板)都有自己特定的体系结构,如物理内存地址,物理i/o地址,显存起始地址等等,这个结构为struct machine_desc:struct machine_desc /* note! the first four elements are used* by assembler code in head-armv.s*/unsigned intnr;/* architecture number*/unsigned intphys_ram;/* start of physical ram */unsigned intphys_io;/* start of physical io*/unsigned intio_pg_offst;/* byte offset for io page tabe entry*/const char*name;/* architecture name*/unsigned intparam_offset;/* parameter page*/unsigned intvideo_start;/* start of video ram*/unsigned intvideo_end;/* end of video ram*/unsigned intreserve_lp0 :1;/* never has lp0*/unsigned intreserve_lp1 :1;/* never has lp1*/unsigned intreserve_lp2 :1;/* never has lp2*/unsigned intsoft_reboot :1;/* soft reboot*/void(*fixup)(struct machine_desc *,struct param_struct *, char *,struct meminfo *);void(*map_io)(void);/* io mapping function*/void(*init_irq)(void);我所使用的开发板其特定的体系配置文件在arch/arm/mach-pxa/cerf.c中,是通过一些特定的宏来实现的,如下:1machine_start(pxa_cerf, cerfboard pxa reference board)2maintainer(intrinsyc software inc.)3boot_mem(0xa0000000, 0x40000000, 0xf8000000)4boot_params(0xa0000100)5fixup(fixup_cerf)6mapio(cerf_map_io)7initirq(cerf_init_irq)8machine_end第1行的宏设定nr为machine_start_pxa_cerf,name为cerfboard pxa reference board。第3行的宏设定物理内存起始地址为0xa0000000,物理i/o起始地址为0x40000000第4行的宏设定内核参数的物理地址为0xa00001002.4 _create_page_tables我们的内核起始物理地址是0xa000,8000,虚拟地址是0xc000,8000。下面的代码,是建立内核起始处4mb空间的映射,采用了一级映射方式,即段式(section)映射(非x386的段)方式,每段映射范围为1mb空间。于是我们需要建立4个表项,实现:虚拟地址0xc000,00000xc030,0000映射到物理地址0xa000,00000xa030,0000另外在调用_create_page_tables之前,一些寄存器已存储一些必要的值,如下所示:r5 = physical address of start of ramr6 = physical io addressr7 = byte offset into page tables for ior8 = page table flags函数调用完成后,r4存储的是页表的地址1_create_page_tables:2pgtbl r4, r5 page table address r4= 0xa000400034/*5 * clear the 16k level 1 swapper page table, 6 * addr 0xa0004000: 0xa0008000 is page table, total 16k7 */8movr0, r4 r0=0xa00040009movr3, #010addr2, r0, #0x4000 r2=0xa0008000111:strr3, r0, #4 r3-r0, r0=r0+#412strr3, r0, #413strr3, r0, #414strr3, r0, #415teqr0, r216bne1b1718/*19 * create identity mapping for first mb of kernel to20 * cater for the mmu enable. this identity mapping21 * will be removed by paging_init()22 *23 *.macrokrnladr, rd, pgtable, rambase24 *bicrd, pgtable, #0x000ff00025 *.endm26 */27krnladrr2, r4, r5 start of kernel, r2=0xa000000028addr3, r8, r2 flags + kernel base, r3=0xa0000c0e29strr3, r4, r2, lsr #18 identity mapping 30 value r3=0xa0000c0e store to addr 0xa00068003132/*33 * textaddr= 0xc000800034 */35addr0, r4, #(textaddr & 0xff000000) 18 start of kernel, r0=0xa000700036bicr2, r3, #0x00f00000 r2=0xa0000c0e37strr2, r0 page_offset + 0mb, 0xa0000c0e-0xa0007000038addr0, r0, #(textaddr & 0x00f00000) 18 r0=0xa0007000, no change39strr3, r0, #4 kernel + 0mb40addr3, r3, #1 0xa0007000441strr3, r0, #4 kernel + 1mb42addr3, r3, #1 0xa0007000843strr3, r0, #4 kernel + 2mb44addr3, r3, #1 0xa0007000c45strr3, r0, #4 kernel + 3mb4647/* r0= 0xa00070010*/48/*49 * ensure that the first section of ram is present.50 * we assume that:51 * 1. the ram is aligned to a 32mb boundary52 * 2. the kernel is executing in the same 32mb chunk53 * as the start of ram.54 */55bicr0, r0, #0x01f00000 18 round down, r0=0xa000700056andr2, r5, #0xfe000000 round down57addr3, r8, r2 flags + rambase58strr3, r05960bicr8, r8, #0x0c turn off cacheable and bufferable bits61 movpc, lr我已经把每一步涉及的地址详细列出了,读者可以自行对照阅读。第1116行,清空页表项从0xa000,4000到0xa000,8000,共16kb第28行,取得_cpu_mmu_flags。第3545行,填写页表项,共4项。读者可以对照xscale的地址映射手册,因为采用的是段式映射方式,所以每1mb虚拟空间映射到相同的页表表项,根据手册说明,段式映射只有一级表索引,是虚拟地址的前12位;而页式映射的页目录表是前12位,页表是接着的8位,最后12位才是页内偏移,读者一定不要和386的10位页目录表,10位页表的机制相混淆。我们举个例子说明,对于虚拟地址0xc00x,xxxxx,其前12位为c00,页表基址为0xa000,4000,所以表项地址为0xa000,4000+0xc00setup_arch中使用,来得到当前处理器的struct proc_info_list结构和当前系统的machine_desc结构的数据。最后,跳转到init/main.c中的start_kernel进入内核启动的第二阶段,从此以后,内核代码就以c语言为主了。3. 内核启动第二阶段内核启动第二阶段从init/main.c的start_kernel()函数开始到函数结束,这一阶段对整个系统内存、cache、信号、设备等进行初始化,最后产生新的内核线程init后,调用cpu_idle()完成内核第二阶段。有很多书籍介绍这一部分的内容,我们这里仅仅讲述与xscale结构相关的部分。3.1 setup_arch在start_kernel()中我们碰到的第一个重要的函数就是setup_arch()了,这个函数主要任务是初始化bootmem的内存分配管理,初始化页表等:1void _init setup_arch(char *cmdline_p)23struct tag *tags = (struct tag *)&init_tags;4struct machine_desc *mdesc;5char *from = default_command_line;67root_dev = mkdev(0, 255);89setup_processor();10mdesc = setup_machine(machine_arch_type);11machine_name = mdesc-name;1213if (mdesc-soft_reboot)14reboot_setup(s);1516if (mdesc-param_offset)17tags = phys_to_virt(mdesc-param_offset);1819if (mdesc-fixup)20mdesc-fixup(mdesc, (struct param_struct *)tags,21 &from, &meminfo);第9行,设置当前的processor,主要根据_proc_info_begin和_proc_info_end这2个指针来寻找当前processor的struct proc_info_list结构数据,这个我们前面已经提到过。第10行,则是根据_arch_info_begin和_arch_info_end这2个指针寻找当前系统的struct machine_desc结构数据。我们假定的系统是采用pxa250的cerfboard,所以是在arch/arm/mach-pxa/cerf.c中通过宏machine_start来定义的。第20行,调用特定机器的fixup()函数,此函数的主要作用是设定内存的bank,保存在全局变量meminfo中,例如,我们的系统的fixup()函数如下:static void _init fixup_cerf(struct machine_desc *desc, struct param_struct *params,char *cmdline, struct meminfo *mi)#ifdef config_pxa_cerf_ram_128mbset_bank (0, cerf_ram_base, 64*1024*1024);set_bank (1, cerf_ram_base+64*1024*1024, 64*1024*1024);mi-nr_banks = 2;#elseset_bank (0, cerf_ram_base, cerf_ram_size);mi-nr_banks = 1;#endif我们接着往下看:22if (tags-hdr.tag != atag_core)23convert_to_tag_list(tags);2425if (tags-hdr.tag = atag_core) 26if (meminfo.nr_banks != 0)27squash_mem_tags(tags);28parse_tags(tags);293031if (meminfo.nr_banks = 0) 32me
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 煤厂入股合同范例
- 售卖羊肉分割合同范例
- 宁夏清洗空调合同范例
- 定制家具签单合同范例
- 物流车辆托管合同范例
- 玩具代理代销合同范例
- 2025年三明如何考货运从业资格证
- 催乳师合同范例
- 2025年本溪货运资格证试题及答案
- 冷餐冰箱租赁合同范例
- 资产评估服务评估实施方案
- 普通话语音与发声-1智慧树知到期末考试答案2024年
- 企业合同签订流程培训
- 2024年浙江省义乌市绣湖中学八年级数学第二学期期末学业质量监测试题含解析
- 北京市海淀区2023-2024学年四年级上学期期末英语试题
- 乡镇街道合法性培训审查
- LNG液化天然气项目事故后果模拟分析评价
- 脂肪肝健康教育-课件
- 体育与健康(水平二)《花样跳绳一级动作(18课时)》大单元教学计划
- 地产集团品牌管理办法
- 腹腔穿刺术评分表
评论
0/150
提交评论