版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Armlinux启动分析
wal zhenyus 在内核运行之前需要系统引导程序(Bootladr)完成加载内核和一些辅助性的工作,然后跳转到内核代码的起始地址并执行。本文先分析了Bootloder的初始化工作,接着从内核镜像的起始地址进行分析。整个armlinux内核的启动可分为三个阶段:第一阶段主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更次的初始化,如根设备和外部设备的初始化。第一阶段的初始化是从内核(ENTRYstxt))开始到star_krnel前结束。这一阶段的代码在/arch/am/ea_amv.S中。简介本处介绍主要来自内核源代码下的ation/arm/Booting文件,适合于armlinux2.4.18-rmk6及以上版本。Bootloader主要作用是初始化一些必要的设备,然后调用内核,同时传递参数给内核。主要完建立和初始化RAM初始化一个串口检测机建立内核的taggedlist调用内建立和初始化RAM要求:必功能:探测所有的RAM位置和大小RAM进行初始化初始化一个串口要求:可选,建功能:Bootloader应该初始化并启动一个串口。这可以让内核的串口驱动自动探测哪个串口作为内核的控制台。另外也可以通过给内核传递“console=”参数完成此工作。功能:Bootloader应该通过某种方法探测机器类型,最后传递给内核一个MACH_TYPE_xxx值,这些值参看linux/arch/arm/tools/mach-types。建立内核的taggedlist。功能:Bootloader必须创建和初始化内核的taggedlist。一个合法的taggedlist开始于ATAG_CORE并结束ATAG_NONE。ATAG_COREtag可以为空。一个空的ATAG_COREtag的size字段设为“2”(0x00000002)。ATAG_NONE的size字段必须设为“0”。taggedlist可以有任意多的tag。Bootloader必须至少传递系统内存的大小和位置,以及根文件系统的位置,一个最小化的taggedlist应该像如下: base->|ATAG_CORE| +|ATAG_MEM||increasing +|ATAG_NONE| +taggedlistinitrd”bootp”程序都不会覆盖的内存区域。建议放RAM16K小的地方。功能:可以从flash调用内核,也可以从系统RAM中调用内核。对于后者需要注意,内核使用内核镜像以下的16K内存作为页表,建议把内核起始放在RAM32K处。无论是哪种方法,CPUregistersettingsr0=0,r1=machinetypenumberdiscoveredin(3)above.r2=physicaladdressoftaggedlistinsystemCPUAllformsofinterruptsmustbedisabled(IRQsandTheCPUmustbeinSVCmode.(AspecialexceptionexistsforCaches,TheMMUmustbeInstructioncachemaybeonoroff. achemustbeoff.Thebootloaderisexpectedtocallthekernelimagebyjumdirectlytotheinstructionofthekernelimage.2.3Skyeye相应说明因为Skyeye暂时没有Bootloader,所以以上一些设置必须由Skyeye在初始化的时候自己来完成,如r1的设置等。Head_armv.S说明这个文件是arch/arm/kernel/head-armv.S,用汇编代码完成,是内核最先执行的一个文件。这一段汇编代码的主要作用,是检查cpuid,architecturenumber,初始化页表、cpu、bbs等操作,并跳到start_kernel函数。它在执行前,处理器的状态应满足: -shouldbe -uniquearchitecture -I-cache-onorD-cache–流程代码详细注释(略去一些条件编译的代码*lacethepagetables16Kbelow Therefore,wemustmakethatTEXTADDRiscorrectly Currently,weexpecttheleast"short"tobe0x8000,butwecouldprobablyrelaxthisrestrictionTEXTADDR>PAGE_OFFSET+Notethatswapper_pg_diristhevirtualaddressofthepagetables,pgtblgivesusaposition-independentreferencetothese Wedothisbecausestext==swapper_pg_dir,pgtblandkrnladrareallclosely#if(TEXTADDR&0xffff)!=#errorTEXTADDRmuststartat0xXXXX8000 .equSYMBOL_NAME(swapper_pg_dir),TEXTADDR- pgtbl,reg,rambaseadr\reg,stextsub\reg,\reg,Sincethepagetableiscloselyrelatedtothekernelstartaddress,canconvertthepagetablebaseaddresstothebaseaddressofthecontaining krnladr,rd,pgtable,rambasebic\rd,\pgtable,#0x000ff000KernelstartupentryTherules -shouldbe -uniquearchitecture -I-cache-onorD-cache-Seelinux/arch/arm/tools/mach-typesforthe istoffor.section".text.init",#alloc,#exec.typestextfunctionENTRY(stext)//内核movr12,r0movr0,#F_BIT|I_BIT|MODE_SVC@makesuresvcmode//程序状态,FIQ、IRQ,设Supervisor模式。msrcpsr_c, @andallirqsdisabled// lookup_processor_type//跳转到判断cpu类型,查找运行的cpu的id值,和//此linux编译支持的id值,是否有相等teqr10,#@invalidprocessor?// r0,@yes,error lookup_architecture_type//跳转到判断体系类型,看r1//architecturenumber值是否支持。teqr7, @invalidarchitecture?// r0,#'a' @yes,error'a' adr @returnaddress//lr=0xcaddpc,r10, @initialiseprocessor//r10:pointertoprocessor//( arm720_setup:movr0,mcrp15,0,r0,c7,c7,0 @invalidatecachesmcrp15,0,r0,c8,c7,0 @flushTLB(v4)mcrp15,0,r4,c2, @loadpagetable//cp15寄存器movr0, @s0,1=mcrp15,0,r0,c3, @loadaccessmrcp15,0,r0,c1, @getcontrol bicr0,r0, @..V...RSBLDPWCAM orrr0,r0, @111 orrr0,r0, @..1...01..111101(new)其中 M位1(详见cp15寄存器1说明movpc, ret(head-』@(returncontrol switch_data,%objectswitch_data: .long.long.long .long.long ret, mcrp15,0,r0,c1,c0//cp15movr0,r0movr0,r0movr0,r0movpc,lr//switch_Thiscodefollowsonafterthetableswitchandjump =processorcontrol =machine =processor mmap_switched://把sp指针指向 //一个进程的task_struct和系统堆栈的地址;清空BSS段;保存processor//和machinetype,到全局变量processor_id machine_arch_type,这些//以后要用到;r0为"A"置位的controlregister值,r2为"A"清空的//controlregister值,即对齐检查(Alignmentfaultchecking)//存到cr_alignment,和cr_no_alignment(在文件entry-armv.S中//adr switch_data+ r3,{r4,r5,r6,r7,r8,sp}@r2=@sp=stack movfp,#0 @ClearBSS(andzerofp) cmpr4,r5strccfp,bcc r9, @Saveprocessor r1,[r7] @Savemachinetype#ifdefCONFIG_ALIGNMENT_TRAPorrr0,r0,
bicr2,r0, @Clear'A' r8,{r0,r2} @Savecontrolregistervalues SYMBOL_NAME(start_kernel)//跳转到start_kernelSetuptheinitialpage Weonlysetuptheamountwhicharerequiredtogetthekernelrunning,generallymeansmapinthekernelWeonlymapin4MBofRAM,whichshouldbesufficientallr5=physicaladdressofstartofr6=physicalIOr7=byteoffsetintopagetablesforr8=pagetablepgtblr4, @pagetableaddress//调用宏pgtbl,r4=0xc0024000基址Clearthe16Klevel1swapperpagemovr0,r4//r0=0xc0024000movr3,#0addr2,r0,
r3,[r0],#r3,[r0],#strr3,[r0],#strr3,[r0],#4teqr0,r2bne //将地址0xc0024000~0xc0028000CreateidentitymapforMBofkernelcaterfortheMMU Thisidentitywillberemovedbykrnladrr2,r4,r5 @startofkernel//r2=0xc0000000;r4=0xc0024000addr3,r8,r2 @flags+kernelbase//flags,r8=0xc1e;r3=0xc0000c1estrr3,[r4,r2,lsr#18]@identitymap//addr:0xc0027000;value:0xc0000c1eNowsetupthepagetablesforourkernelmapped WeroundTEXTADDRdowntonearestmegabyteaddr0,r4,#(TEXTADDR&0xff000000)>>18@startofkernel//r0=0xc0027000 r2,r3,#0x00f00000//r2=0xc0000c1e r2,[r0] @PAGE_OFFSET+0MBaddr0,r0,#(TEXTADDR&0x00f00000)>>18 r3,[r0],# @KERNEL+addr3,r3,#1<< r3,[r0],# @KERNEL+addr3,r3,#1<< r3,[r0],# @KERNEL+addr3,r3,#1<< r3,[r0],# @KERNEL+ EnsurethatthesectionofRAMisweassume1.theRAMisalignedtoa32MB2.thekernelisexecutinginthesame32MBasthestartofbicr0,r0,#0x01f00000>>18 @rounddown//r0=0xc0027000andr2,r5,#0xfe000000 @rounddown//r2=0xc0000000addr3,r8,r2 @flags+rambase//r3=0xc0000c1e r3,[r0]bicr8,r8,#0x0c @turnoffcacheable//r8=0xc12@andbufferablebitsmovpc,ReadprocessorIDregister(CP#15,CR0),andlookupinthelinker-supportedprocessor Notethatwecan'tusetheabsolutefor proc_infolistssincewearen'trunningwiththeMMU(andtherefore,wearenotinthecorrectaddress Wehavecalculatether5,r6,r7 =pagetable =processorr10=pointertoprocessorlookup_processor_type://cpu类型adrr5, //2 r5,{r7,r9,r10} subr5,r5, @convert addr7,r7,r5 @toouraddressspaceaddr10,r9,r5 //r10:proc_info_beginmrcp15,0,r9,c0,c @getprocessor //cp15寄存器0中cpuid至r9。此版本是 r10,{r5,r6,r8}@value,mask,mmuflags//armlinux中cpu信////0xffffff00;r8,mmuflags即一级描//andr6,r6, @maskwanted //cpuid的低8teqr5, //0cpuidarmlinuxcpuid比较 pc, //addr10,r10, @sizeof(proc_info_list)//否则寻找下一块cmpr10,r7 movr10, @unknownprocessor//movpc,Lookininclude/asm-arm/procinfohandarch/arm/kernel/arch.[ch]moreinformationabout proc_info arch_info .long Lookupmachinearchitectureinthelinker-buildlistofNotethatwecan'tusetheabsoluteaddressesfor listssincewearen'trunningwiththeMMUon(andtherefore,wenotinthecorrectaddress Wehavetocalculatether1=machinearchitecturer2,r3,r4r5=physicalstartaddressofr6=physicaladdressofr7=byteoffsetintopagetablesforlookup_architecture_type://判断体系类型adrr4,2b//取上面标号2的地址 r4,{r2,r3,r5,r6, @throwawayr2, r7:subr5,r4, @convertaddressesaddr4,r6, @toouraddress addr7,r7, ldrr5,[r4] @getmachinetypeteqr5,r1//r1machinetype号,此为91beq2faddr4,r4,#SIZEOF_MACHINE_DESC//不匹配,查找下一个cmpr4,r7 movr7,#0 @unknownarchitecturemovpc,lr r4,{r5,r6, @found,getresults//r5,ram物理起始地址:0xc0000000;r6,io地址:0x8000000;r7,io在页表的偏移:0x3fc0movpc //返首先,portinglinux的时候要规划内存影像,如小弟的系统有64m地址从0x080000000x0bffffff,32mflash,地址从0x0c0000000x0dff规划如下:bootloader,linuxkernel,rootdisk放在flash里。具体从0x0c000000开始的第一个1Mbootloader,0x0c100000开始2mlinuxkernel,从0x0c300000开始都给rootdisk。首先,启动后arm920T将地址0x0c000000到0(可通过跳线设置),实际上从0x0c000000启动,进入的bootloader,但由于flash速度慢,所以bootloader前面有一小段程序把bootloader拷贝到SDRAM中的0x0AFE0100,再从0x08000000运行bootloader,叫这段小程序为flashloader,flashloader必须要首先初始化SDRAM,不然往那放那些东东.equSOURCE,0x0C000100bootloader的存放地.equ,0x0AFE0100目标地.equSDCTL0,0x221000SDRAM控制器寄存器//sizeisstoredinlocation.global_start:////;*Init////;*//;***************LDRr1,=SDCTL0////;SetPrechargeCommandLDRr3,=0x92120200//ldrr3,=0x92120251STRr3,[r1]//;IssuePrechargeAllCommadLDRr3,=0x8200000LDRr2,//;SetAutoRefreshCommandLDRr3,=0xA2120200STRr3,//;IssueAutoRefreshCommandLDRr3,=0x8000000LDRr2,LDRr2,[r3]LDRr2,[r3]LDRr2,[r3]LDRr2,[r3]LDRr2,[r3]LDRr2,[r3]LDRr2,[r3]//;SetModeRegisterLDRr3,=0xB2120200STRr3,[r1]//;IssueModeRegisterLDRr3,=0x08111800//;ModeRegistervalueLDRr2,[r3]//;SetNormalModeLDRr3,=0x82124200STRr3,[r1]//;*EndofSDRAMandSyncFlashInit//copycodefromFLASHtoldrr0,=SOURCEldrr1,=subr3,r0,#4ldrr2,[r3]ldrr3,[r0]strr3,[r1]addr0,r0,#4addr1,r1,#4subr2,r2,#4teqr2,#0beq_EndCopyb_CopyLoopldrr0,=movpc,r0上回书说到flashloaderbootloaderload0x0AFE0100,然回跳了过去,其实0x0AFE0100就是烧在flash0x0C000100中的真正的bootloader:bootloader有几个文件组成,先是START.s,也是唯一的一个汇编程序,其余的都是C写成的,START.s主要初始化堆栈:ldrr1,=StackInitldrsp,[r1]b//此处跳到了C代码的main函数,当C代码执行完后,还要调//下面的JumpToKernel0x跳到LINXUkernel运.equ end_data+0x1000// end_data在连结中指.long.globalJumpToKernel//jumptothecopycode(gettheargumentsright)movpc,r0.global//r0=jump//r1-r4=argumentstouse(thesegetshifted)//jumptothecopycode(gettheargumentsright)movr8,r0movr0,r1movr1,r2movr2,r3movr3,r4movpc,.section.section".bss.boot下面让看看bootloader的c代码干了些什么。main函数比较长,让分段慢慢看intma{U32*pSource,*pDestin,count;U8countDown,bootOption;U32delayCount;U32fileSize,i;charc;char*pCmdLine;char*pMem;init();//初始化FLASH控制器和CPU时钟EUARTinit();//串口初始EUARTputString("\n\nDBMX1LinuxBootloaderver0.2.0\n");EUARTputString("Copyright(C)2002Motorola.\n\n");EUARTputString((U8*)cmdLine);EUARTputString("Pressanykeyforalternateboot-upoptions...小弟的bootloader主要干这么几件事:init(); ProgrambootloaderProgramkernelProgramroot-diskDownloadkernelandbootfromDownloadkernelandbootwithver0.1.xbootloaderBootaver0.1.xBootwithadifferentcommand也就是说,可以在bootloader里选择重新kernel,rootdisk并写入的方法是用usb连接,10m的rootdisk也就刷的一下。关于usb的请参看先前的贴子“为arm开发平台增加usb接口“。如果不选,直接回车,就开始把整个linux的内核拷贝到SDRAM中运行列位flashloader中不是sdram控制器了吗?怎么中还要初始化呢,各位有所不知,小弟用的是可以直接使用sdram控制器的接口,切记:在flash中运行的代码是不能初始化连接flashsdram控制器的,不然绝对死掉了。所以,当程序在flash中运行的时候,去初始化而现在在sdram中运行,可放心大胆地初始化fsh了,主要是设定字宽,行列延时,因为缺省都是最大的。另外,如果列位看官的cpu有足够的片内ram,完全可以先把bootloader放在片内ram,干完一切后再跳到LINUX,小弟着也是不得已而为之啊。如果直接输入回车,进入kernel拷贝工作EUARTputString("CopyingkernelfromFlashtoRAM...\n");count=0x200000;//2MbytespSource=(U32*)0x0C100000;pDestin=(U32*)0x08008000;{*(pDestin++)=*(pSource++);count-=4;}while(count>}EUARTputString("Bootingkernel这一段没有什么可说的,运行完后kernel就在0x08008000了,至于空出0x8000的一段,主要是放kelnel的一些全局数据结构,如内核页表,arm的 16k知道,linux内核启动的时候可以传入参数,如在PC上,如果使用当出现LILO:,可以输入root=/dev/hda1.或mem=128M等指定文件系统的设备或内存大小,在系统上,参数的传入是要靠bootloader完成的,pMemchar*)0x083FF000;//参数字符串的目标存放地址pCmdLine=(char*)&cmdLine;//定义的静态字符串while((*(pMem++)=*(pCmdLine++))!=0);//拷贝JumpToKernel((void*)0x80080000x083FF000跳转到内核return(0);JumpToKernel文中的start.S定义过//jumptothecopycode(gettheargumentsright)movpc,r0.global//r0=jump//r1=argumentstouse(theseget由于armGCC的c参数调R0开始,所以R0KERNKEL的地r1是参数字符串的地址到此为止,为linux引导做的准备工作就结束了,下一回就正式进入linux的代码好,从本节开始,走过了bootloader的漫长征途,开始进入linux的内核说实话,linux宝典的确高深莫测,洋人花了十几年,各种内功心法层处不穷。有些地方反复linux的是一段汇编代码,用于基本的硬件设置和建立临时页表,对ARMLINUX是linux/arch/arm/kernle/headarmv.S,走#ifdefined(CONFIG_MX1)movr1,#MACH_TYPE_MX1来的MACH_TYPE_MX1?其实,在headarmv.S中的一项重要工作就是设置内核的临时页表,不然mmu开起来也玩不转,但是内核怎么知道如何内存呢?linux的内核将到虚地址0xCxxxxxxx处,但他怎么知道把哪一片ram因为不通的系统有不通的内存影像,所以LINUX约定,内核代码开始的时候,R1放的是系统目标平台的代号,对于一些常见的,标准的平台,内核已经提供了支持,只要在编译的时候选中就行了,例如对X86平台,内核是从物理地址1M开始的。如果老兄是自己攒小弟拿人钱财,与人消灾,用的是摩托的MX1,只好自己写了,定义了#MACH_TYPE_MX1,当MACHINE_START(MX1ADS,"MotorolaMX1ADS")MAINTAINER("SPSMotorola")BOOT_MEM(0x08000000,0x00200000,0xf看起来怪怪的,但现在大家只要知道他定义了基本的内存映象RAM从0x08000000开始i/o空间从0x00200000开始,i/o空间到虚拟地址空间0xf0200000开始处。摩托的i/o和内存是编址的。其他的项,在下面的初始化过程中会逐个介绍到。好了好了,再看下面的指令movr0,#F_BIT|I_BIT|MODE_SVC@makesuresvcmode//设置为SVC模式,允//此处设定系统的工作状态,arm有7//每种状态有自己的堆msrcpsr_c,r0@andallirqsdiabled //定义处理器相关信息,如value,mask,//放在段 lookup_processor_type取得这些信息,在下 lookup_architecture_type中这一段是查询处理器的种类,大家知道arm有arm7,arm9等类型,如何区分呢在arm协处理器中有一个只读寄存器,存放处理器相关信息。 lookup_processor_type将.long0x41009200//CPU.long0xff00fff0//cpu.long0x00000c1e@mmuflags .long.long.longHWCAP_SWP|HWCAP_HALF|.long.long第一项是CPUid,将与协处理器中读出的id作比较,其余的都是与处理器相关的信息,到下面初始化的过程中自然会用到。。查询到了处理器类型和系统的内存映像后就要进入初始化过程中比较关键的一步了,开始设mmu,但首先要设置一个临时的内核页表,4m的内存,这在初始化过程中是足够了//r5=08000000ram起始地址r6=00200000io地址,r7=f0200000ioteqr7,#0@invalidarchitecture?moveqr0,#'a'@yes,error'a' 其 create_page_tables为pgtblr4//r4=08004000临时页表的起始地//r5=08000000,ram的起始地//r6=00200000,i/o寄存器空间的起始地//r7=00003c//r8=00000c//thepagetablein08004000isjusttempbasepage,wheninit_task'ssweaper_page_dirready,//thetemppagewillbe//thehigh12bitofvirtualaddressisbasetableindex,soweneed4kx4=16ktempbasepage,movr0,r4movr3,addr2,r0,#0x4000@16kofpagetable1:strr3,[r0],#4@Clearpagetablestrr3,[r0],strr3,[r0],strr3,[r0],#4teqr0,r2bneCreateidentitymapforMBofThisismarkedcacheableandTheidentitymapwillberemoved//由于linux编译的地址是0xC0008000,load的地址是0x08008000,需要将虚地0xC0008000到0800800一//同时,由于部分代码也要直接0x08008000,所以0x08008000对应的表项也要填//页表中的表象为section,AP=11表示任何模式下可, 为0。addr3,r8,r5@mmuflags+startofRAM//r3=08000caddr0,r4,r5,lsr//r0=0800strr3,[r0]@identityma//*08004200=08000c1e0x200表象对应的是08000000Nowsetupthepagetablesforourkernelmappedregion.WeroundTEXTADDRdowntonearestmegabyte//下面是addr0,r4,#(TEXTADDR&0xfff00000)>>18@startof//r0=r4+0x3000=08004000+3000=08007000strr3,[r0],#4@PAGE_OFFSET+0MB//*08007004=08000caddr3,r3,#1<<//r3=08100cstrr3,[r0],#4@PAGE_OFFSET+//*08007008=08100caddr3,r3,#1<<20strr3,[r0],#4//*0800700c=08200c1e@PAGE_OFFSET+addr3,r3,#1<<strr3,[r0],#4@PAGE_OFFSET+//*08007010=08300cbicr8,r8,#0x0c@turnoff//r8=00000c12@andbufferablebitsmovpc,lr//子程序返回。下一回就要开始打开mmu的操作上回书讲到已经设置好了内核的页表,然后要跳转到 这个函数在arch/arm/mm/proc-arm929.smovr0,#0mcrp15,0,r0,c7,c7@invalidateI,Dcachesonv4mcrp15,0,r0,c7,c10,4@drainwritebufferonv4mcrp15,0,r0,c8,c7@invalidateI,DTLBsonv4mcrp15,0,r4,c2,c0@loadpagetablepointermovr0,#0x1f@s0,1=clientmcrp15,0,r0,c3,c0@loadaccessmrcp15,0,r0,c1,c0@getcontrolregisterClearout'unwanted'bits(thenputtheminifweneed@VIZFRSBLDPbicr0,r0,#0x0e00bicr0,r0,#0x0002bicr0,r0,#0x000cbicr0,r0,#0x1000@...0 Turnonwhatweorrr0,r0,orrr0,r0,#0x2100@..1....1 #ifdefCONFIG_CPU_ARM920_D_CACHE_ONorrr0,r0,#0x0004@.1..#ifdefCONFIG_CPU_ARM920_I_CACHE_ONorrr0,r0,#0x1000@...1movpc,lr这一段首先关闭i,dcache,清除writebuffer,然后设置页 的保护,在上节中,注意到页 项的都是0,寄存器中的0对应的是0b11,表示模式为manager,不受限制。接下来设置控制寄存器,打开d,icache注意armdcachemmu一起打开,而icache可以单独打其实,cache和mmu的关系实在是紧密,每一个页表项都有标cacheable的,可以说本来就是设计一起使用最后,自函数返回后,有一mcrp15,0,r0,c1,c使设置生效上回讲到arm靠初始化完成了,打开了到此为止,汇编部分的初始化代码就差不多了,最后还有几件事情做1。初始化BSS段,全部清零,BSS2。保存与.long.long .long.long.long .long.long不用讲,大家一看就明白意3。重新设置堆栈指针,指向init_task的堆栈。init_task是系统的第一个任务,init_task的堆栈在taskstructure的后8K,后面会看到。4。最后就要跳到C代码的start_kernel。bSYMBOL_NAME(start_kernel)现在让来回忆一下目前的系统状态临时页表已经建立,在0X08004000处,了4M,虚地址0XC000000被CACHE,MMU都已经打开堆栈用的是任务init_task的堆栈如果以为到了c代码可以松一口气的话,就大错特措了,linux的c也不比汇编好懂多少,相反到掩盖了汇编的一些和机器相关的部分,有时候更难懂。其实作为编写操作系统的c代码,只不过是汇编的另一种写法,和机器代码的联系是很紧密的。start_kernel在/linux/init/main.casmlinkage init{char*command_line;unsignedlongmempages;externchar setup_arch(&command_line);//arm/kernel/setup.cprintk("Kernelcommandline:%s\n", trap_init();//arm/kernle/traps.cstart_kernel中的函数个个都是重量级的,首先用printk(linux_banner);打出系统版本号,这里面就大有文章,系统才刚开张,你让他打印到哪里去呢?先给大家交个底,以后到console的部分自然清楚,printk和printf不同,他首先输出到系统的一个缓冲区内,大约4k,如果登记了console,则调用console->wirte函数输出,否则就一直在buffer里呆着。所以,用printk输出的信息,如果超出了4k,会冲掉前面的。在系统引导起来后,用dmesg看的也就是这个buffer中的东东。下面就是一个重量级的函数setup_arch(&command_line);完成内存映像的初始化,其中command_line是从bootloader中传下来的。 initsetup_arch(char{structparam_struct*params= *mdesc;//archstructure,foryourads,defined include/arm-asm/mach/arch.hverylongstructmeminfochar*from= memset(&meminfo,0,sizeof(meminfo));也就是可支持不连续的物理内存区域。这一点在系统中很有用,例如对于SDRAM和meminfo结构定义如下#defineNR_BANKS4//definethesystenmemregion,notconsistentstructmeminfo{intnr_banks;unsignedlongend;struct{unsignedlongstart;unsignedlongsize;intnode;}下面是:ROOT_DEV=MKDEV(0,ROOT_DEV是宏,指明启动的设备,系统中通常是flash这里面有一个有趣的悖论:ix的设备都是在e下,这些设备文件需要设备驱动程序支持,而设备文件才能取得设备号,才能加载驱动程序,那么第一个设备驱动程序是怎么加载?就是TD,不需备文件,直指定备号。下面准备初始化真正的内核页表,而不再是临时的了。mdesc=setup_architecture(mac//findthemachinetypeinmach-//theadsname,memmap,io返回如下结构:MACHINE_START(INTEGRATOR,"MotorolaMAINTAINER("ARM/DeepBlueSolutions")BOOT_MEM(0x08000000,0x00200000,0xf0200000)面介绍过这个结构,不过这次用它玩真的了书接上回下面是init_mm的初始化,init_mm定义在/arch/arm/kernel/init_task.cstructmm_structinit_mm=从本回开始的相当一部分内容是和内存管理相关的,凭心而论,操作系统的内存管理是很复杂的,牵扯到处理器的硬件细节和算法,限于篇幅所限制,请大家先仔细读一读armmmu的部分,中文参考资料:linux内核源代码情景,linux2.4.18原代码分析。init_mm.start_code=(unsignedlong)内核代码段开始init_mm.end_code=(unsignedlong)内核代码段结束init_mm.end_data=(unsignedlong)内核数据段开始init_mm.brk=(unsignedlong)内核数据段结束每一个任务都有一个mm_struct结构管理任务内存空间,init_mm是内核的mm_struct,其中设置成员变量*mmap指向自己,意味着内核只有一个内存管理结构,设置*pgd=swapper_pg_dir,swapper_pg_dir是内核的页,在arm体系结构有16k,所以init_mm定义了整个kernel的内存空间,下面会碰到内核线程,所有的内核线程都使用内核空间,拥有和内核同样的权限。memc mand_line,from,//clearcommand =//setparse_cmdline(&meminfo,cmdline_p,//bootloader的参数拷贝到c定义在这个函数在内核结尾分一页出来作位图,根据具体系统的内存大小整个rampaging_init(&meminfomdesc);定义在arm/mm/init.c创建内核页表,所有物理内存和io空间,对于不同的处理器,这个函数差别很大, initpaging_init(structmeminfo*mi,structmachine_desc*mdesc{void*zero_page,*bad_page,*bad_table;intnode;//staticstructmeminfomeminfo initdata={0,};memcpy(&meminfo,mi,sizeof(meminfo));allocatewhatweneedforthebadnotethatwecountonthisgoingzero_page=alloc_bootbad_page=alloc_bootmem_low_pages(PAGE_SIZE);bad_table=alloc_bootmem_low_pages(TABLE_SIZE);分配三个页出来,用于处理异常过armlinux中,得到如下badpage=0xc0001000上回说到在paging_init中分配了三个页badpage=0xc0001000但是奇怪的很,在更新的linux代码中只分配了一zero_page,而且在源代码中找不到用在什么地方了,大家吧paging_init的主要 initmemtable_init(structmeminfo*中完成的,为系统内存创建页表meminfo结构如下structme{intnr_banks;unsignedlongend;struct{unsignedlongstart;unsignedlongsize;intnode;}是用来系统中的内存区段的,因为在系统中并不是所有的内存都能,例如只有64m,flash32m,而且不见得是连续的,所以用meminfo这些区段 initmemtable_init(structmeminfo*{structmap_desc*init_maps,*p,*q;unsignedlongaddress=0;intinit_maps=p=alloc_boot其中map_desc定义为structma{unsignedlongvirtual;unsignedlongphysical;unsignedlonglength;int:4,//页表的prot_read:1,//保护标志prot_write:1,//写保护标志cacheable:1,//是否cachebufferable:1是否用writebufferlast:1;//空map_desc是区段及其属性的定义,属性位的意义请参考ARMMMU的介绍。下面对meminfo的区段进行遍历,同时填写init_ma中的各项内容:for(i=0;i<mi->nr_banks;{if(mi->bank[i].size==0)p->physical=mi-p->virtual= phys_to_virt(p->physical);p->length=mi->bank[i].size;p->=p->prot_read=p->prot_write=p->cacheable=1;//可以p->bufferable=1使用writebufferp++;//下一个区段}如果系统有flash,#ifdefFLUSH_BASEp->physical=FLUSH_BASE_PHYS;p->virtual=FLUSH_BASE;p->length=p->=p->prot_read=p->prot_write=0;p->cacheable=1;p->bufferable=1;p其中的prot_read和prot_write是用来设置页表的的,q=init_maps;do{if(address<q->virtual||q==p)address+=PGDIR_SIZE;}elseaddress=q->virtual+q-address=(address+PGDIR_SIZE-1)&q}}while(address!=上次说到memtable_init中初始化页表的循环,这个过程比较重要,看仔细些q=init_maps;do{if(address<q->virtual||q==p)//由于内核空间是从c0000000开始,所以c000//以前的页表项全部清address+=PGDIR_SIZE;//每个表项增加1m,这里感到了section的好}其中clear_map()是个宏,根据处理器的不同,在920下被展开为cpu_arm920_set_pmd(((pmd_t*)(((&init_mm)->pgd+((virt)>>20)))),((pmd_t){(0)}));其中init_mm为内核的mm_struct,pgdswapper_pg_dir,在 中定#ifdefeorr2,r1,#0x0atstr2,#0x0bbiceqr1,r1,#4strr1,把pmd_t填写到页表项中,由于pmd_t0,实际等于清除了这一项,由于dcache打开,这一条指令实际并没有写回内存,而是写到cache中mcrp15,0,r0,c7c10,1把cache中地址r0对应的内容写回内存中,这一条语句实际是写到了writebuffer中,还没有真正写回内存。mcrp15,0,r0,c7,c10,4等待把writebuffer中的内容写回内存。在这之前core等待movpc,lr在这里看到,由于页表的内容十分关键,为了确保写回内存,采用了直接操作cache的方法。由armcore中,打开dcache必定要用writebuffer.所以还wb的回写问题。由于考虑到效率,使用了cache和buffer,所以在某些地方要用指令保证数据被及时写回。下面c0000000后面的页表elseaddress=q->virtual+q-address=(address+PGDIR_SIZE-1)&q}}while(address!=create_map也在mm-armv.c中定义static i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 难治性高血压的诊断与管理总结2026
- 跨境游升温目的地选择攻略
- 2026届海南省高三最后一卷历史试卷含解析
- 2026届滨州市高三第六次模拟考试历史试卷含解析
- 初中数学课堂生成式AI评价对学生学习策略调整的实践研究教学研究课题报告
- 循证康复实践中的康复-患者赋能
- 影像组学联合临床数据构建疗效预测综合模型
- 影像组学在肿瘤个体化治疗中的伦理考量
- 2026年智能包装检测技术报告
- 康复医学研究生科研转化平台建设
- 智能建造施工技术 课件全套 王春林 项目1-11 智能建造施工概论- 外墙保温与建筑施工碳排放计算
- DB1303-T365-2023 长城主题旅游景区服务规范
- 办公耗材维修合同范本
- 高压清洗机安全操作规范及制度培训考试试题
- 电子书 -人生翻盘指南
- 四川省成品住宅装修工程技术标准(修订)
- 副斜井提升绞车选型设计
- 脑炎的相关知识课件
- JBT 14437-2023 二氧化碳致裂管 (正式版)
- 坚守教育底线筑起师德防线
- pep六年级英语下册Unit4单元总复习课件
评论
0/150
提交评论