嵌入式Linux学习七步曲Sailor_forever(扬帆)自由传播版权所有翻版必究八一卦-我是whon目前就职于通信行业某外企研发中心n参与校园招聘和社会招聘的技术面试工作n5年嵌入式软件开发经验,擅长嵌入式Linux开发;n接触的软硬件平台包括ARM,DSP,PowerPC,uC/OS-II,Linux,VxWorks及OSE八一卦-我是whon嵌入式Linux七步曲学习群交流讨论资源共享n群号107900817n7steps2linux@gmail.comn嵌入式水平小调查n0—3个月n3—6个月n1年左右n2年以上n多少人参加过系列交流会?5嵌入式Linux学习七步曲111333444Linux主机开发环境嵌入式Linux交叉开发环境Linux系统bootloader移植Linux的内核移植555Linux的内核及驱动编程222666文件系统制作777Linux的高级应用编程6GrammyforLinux7STARVolunteernLinux系统bootloader移植nHobby,U-boot如何启动Linux内核nSailing,U-boot在ARM平台下的启动流程8STARVolunteer9VolunteerTaskn宗旨n鼓励大家实际的参与嵌入式Linux的开发n自己解决动手解决问题n总结记录、分享n形成知识库n采用统一的模板,争取成为系列交流会的特色项目n扩大BUPTBES的影响力,创造品牌n运作n下次交流会之前完成上次的总结文档nShare给大家,提建议意见n每次交流会颁奖鼓励n最终将评出STARVolunteer10VolunteerTasknLogo11KeyToSuccessnGoogle、Baidun理论+实践(开发板)n勤于思考,善于总结n多上相关技术论坛,他山之石可以攻玉n良好的文档撰写习惯nPassion!12CHAPTERLinux的内核移植13主要内容111333Linux内核的配置编译Linux启动流程Linux内核移植22214Linux内核的配置编译n配置工具nMakefilen链接脚本n编译链接过程15配置工具nmakexconfignmakeconfignmakemenuconfignCpu及板卡选择(优先配置,部分选项依赖于此)n串口n网口n文件系统支持n网络支持n调试支持16Makefilen层次化,级级调用n根目录下面的makefile设置基础配置n编译器类型nCROSS_COMPILEnCPU类型nARCHn编译链接参数等nMAKEFLAGS,目录打印nKBUILD_VERBOSE,makeV=1,编译选项及文件打印nCONFIG_DEBUG_INFO,gdb符号选项nCONFIG_FRAME_POINTER,防止栈形优化17压缩内核的Makefilen/arch/arm/boot/Makefile18压缩内核的Makefilen/arch/arm/boot/MakefilenuImagezImagecompressed/vmlinuxImagevmlinux19压缩内核的Makefilenarch/arm/boot/compressed/Makefile20压缩内核的Makefilenarch/arm/boot/compressed/Makefile21链接脚本n指定程序链接时各个段落的分布n指定程序链接的基地址n非压缩内核自身的链接脚本n压缩内核的链接脚本22非压缩内核链接脚本n/arch/arm/kernel/vmlinux.ldsn初始化入口为stext,对应的段为.text.headnPAGE_OFFSET+TEXT_OFFSET为内核链接的虚拟地址(0xC0000000+0x8000)23压缩内核链接脚本narch/arm/boot/compressed/vmlinux.ldsn功能n链接基地址为TEXT_START,由makefile指定,对于非XIP内核通常为0n将程序分成.text,.got,.data,.bss,.stack(stack不占据映像大小)n并设置了相关段的起始地址,便于进行代码重定位及内核解压缩n程序入口为arch/arm/boot/compressed/head.S24压缩内核链接脚本25压缩内核链接脚本26压缩内核链接脚本n映像布局.bss.data.got.text.stack(4096)_startSP__bss_start_end00000000_got_start_got_endpiggy.gz27压缩内核的编译链接过程n以0xc0008000为虚拟地址的非压缩内核自身的编译;n对非压缩内核进行gzip压缩形成piggy.gz文件;n将压缩过的内核印象piggy.gz转换成arch/arm/boot/compressed/piggy.o中的.piggydata数据段,并声明其数据域的起始地址;n解压缩代码的编译,链接地址是TEXT_START,以-fpic方式链接,地址无关2829主要内容111333Linux内核的配置编译Linux启动流程Linux内核移植22230Linux启动流程n压缩内核的启动n特点是位置无关,对内核启动地址要求低n内核压缩后比较小,便于存储传输n非压缩内核的启动n位置相关,必须在0xXXXX8000的启动地址(即RAM物理基地址0x8000偏移处)n压缩内核自解压时会自动解压缩到此位置n非压缩内核需要由bootloader保证此位置n两个主要阶段nMMU开启前nMMU开启后31压缩内核的启动n相关代码narch/arm/boot/compressed/n内核入口narch/arm/boot/compressed/head.S的startn主要阶段n判断是否需要重定位n符号表的重定位n内核解压缩n非压缩内核的加载32判断是否需要重定位n判断标准n当前程序的运行地址=?压缩内核的链接地址n如何实现n采用相对寻址指令获得当前程序的运行地址n程序的某个标号的链接地址保存在数据段中33判断是否需要重定位n汇编代码34判断是否需要重定位n反汇编代码35判断是否需要重定位n反汇编代码n8为流水线预取导致的PC值为当前运行指令+8naddr0,pc,#204即获得了当前程序运行地址的CC+8处的运行地址,存储在r0中nldmiar0,{r1,r2,r3,r4,r5,r6,ip,sp}n将r0地址处保存的相关数据顺序加载到r1,r2,r3,r4,r5,r6,ip,sp中nsubsr0,r0,r1nr1为LC0的链接地址,r0为LC0的运行地址,求二者差值,得加载域和链接域的差值36符号表的重定位n为什么要重定位n当压缩内核运行在非链接地址上时,需要对全局符号表进行修正,以便C函数中能够正确访问全局变量n内核解压缩时要获得内核印象所处的位置,相关地址需要修正n实现n将链接地址加上重定位的偏移量,获得各个标号的加载地址37符号表的重定位38内核解压缩n关键因素n覆盖问题n解压缩方案n内核解压缩的目的地址大于当前程序运行空间的最高地址,直接解压缩n内核解压缩的目的地址离当前程序运行的起始地址距离大于内核印象长度,直接解压缩n介于上述二者之间,需要间接解压缩39内核解压缩n内核映像代码段组成*.piggydataOthertextcodeRelocationcodeOthertextcode_startinput_datainput_data_end00000000reloc_startreloc_end40内核解压缩n直接解压缩到高端地址41内核解压缩n直接解压缩到低端地址.bss.data.got.text.stack(4096)SPLoadaddrforzImgeMalloc(64k)decompress*.piggydatainput_datainput_data_endNotusedRAMBaseNotused.bss.data.got.text.stack(4096)SPLoadaddrforzImgeMalloc(64k)*.piggydatainput_datainput_data_endRAMBaseNotusedRAMBase+0x8000image42内核解压缩n间接解压缩43内核解压缩n间接解压缩n确定内核解压缩的临时地址,即位于压缩内核代码之上;n解压缩内核到上述临时地址;n将专门重定位的代码(由reloc_start和reloc_end标识)拷贝到临时解压缩完毕的内核代码之上;n跳转到重定位代码处运行;n重定位代码将临时解压缩的内核再拷贝到最终的内核运行地址上;44非压缩内核的加载n加载条件n非压缩内核已经拷贝到RAM+0x8000地址n跳转到非压缩内核入口时的状态和从uboot中直接跳转到非压缩内核入口的状态需要一致n关闭cache及MMU,并传递machineid和targs的指针n实现n首先刷新cache;n关闭cache;n初始化传递给内核的三个参数;n将内核解压缩后的物理地址赋值给PC,实现绝对跳转45非压缩内核的加载n实现4647非压缩内核的启动n内核入口nvmLinux从arch/arm/kernel/head.SnMMU开启前nMMU关闭n运行在物理地址上,链接地址和运行地址不等n必须相对寻址nMMU开启后n链接地址和运行地址相等,内核最终的运行状态48MMU开启前n检查CPU类型和板卡类型n创建一级页表n保证内核本身运行所需要的页表n使能MMU49MMU开启前n使能MMU—汇编50MMU开启前n使能MMU—反汇编51MMU开启前n使能MMU—反汇编n使能MMU之前,内核运行在物理地址上,而内核编译链接的地址是虚拟地址,此时运行域和加载域不一致,只能采用相对寻址,ldr和adr都是相对于PC寻址的。nadrlr,__enable_mmu可实现将__enable_mmu函数的加载域地址存储在LR链接寄存器中naddpc,r10,#PROCINFO_INITFUNC直接对PC赋值n上述两句即可模拟一次函数调用,执行完毕处理器相关代码后返回到LR所指定的地址__enable_mmu52MMU开启后n拷贝数据段,准备跳转nbstart_kerneln/init/main.c中asmlinkagevoid__initstart_kernel(void)n设置体系结构相关指针nsetup_arch(&command_line)nArch/arm/kernel/setup.cn检查CPU类型n根据machineID获得板块相关信息n解析tags形式的命令行参数n初始化板块相关的函数指针,尤其是板块初始化函数init_machine53MMU开启后n设置体系结构相关指针54MMU开启后n设置体系结构相关指针n板卡初始化函数最终将在arch_initcall系列函数调用时被调用55MMU开启后n保存命令参数参数nsetup_command_linenprintk(KERN_NOTICEKernelcommandline:%s\n,boot_command_line);n中断定时器等初始化ntrap_init();ninit_IRQ();ninit_timers();nhrtimers_init();nsoftirq_init();ntimekeeping_init();ntime_init();56MMU开启后n控制台打印串口初始化nconsole_init()n从此之后打印输出便可以真正输出到串口终端上n第一个内核线程初始化nrest_initn启用第一个内核线程initn便开始了内核的调度n检查文件系统npopulate_rootfsn是否是ramdisk,若是,则解压缩57MMU开启后n驱动模块初始化ndo_basic_setupn内核模块初始化n调用mount_root,挂接文件系统58MMU开启后n打开用户空间串口nsys_open((constchar__user*)/dev/console,