如何启动内核vivi与Linux kernel的参数传递情景分析.docx
《如何启动内核vivi与Linux kernel的参数传递情景分析.docx》由会员分享,可在线阅读,更多相关《如何启动内核vivi与Linux kernel的参数传递情景分析.docx(20页珍藏版)》请在冰豆网上搜索。
![如何启动内核vivi与Linux kernel的参数传递情景分析.docx](https://file1.bdocx.com/fileroot1/2023-1/3/51a0d6fb-0b4b-44c7-9c60-80d92b40037f/51a0d6fb-0b4b-44c7-9c60-80d92b40037f1.gif)
如何启动内核vivi与Linuxkernel的参数传递情景分析
vivi开发笔记(十七):
vivi与Linuxkernel的参数传递情景分析(上)
在上一部分提到过了,vivi作为bootloader,向内核传递启动参数是其本职工作之一。
要把这个情景分析清楚,不仅仅需要分析vivi的参数机制,而且要分析Linuxkernel的接收机制。
因为这是一个简单的通信过程,比起本科所学习的TCP/IP来简单的多,但是因为简单,所以在协议上并不规范,理解上反而不如TCP/IP协议。
下面就分为两个方面对此情景分析。
一、综述内核参数传递机制
现在内核参数传递机制有两种:
一种是基于structparam_struct,这种已经比较老了。
缺点是该结构每个成员的位置是固定的,受限比较大。
另外一种就是新的structtagway。
说新是相对的,Linuxkernel2.4.x都希望采用这种tag的方式。
关于这方面的资料,可以有如下参考(所给出的目录是基于linux-2.4.18的内核,以顶层Makefile所在目录为当前目录。
这里基于ARM架构的S3C2410,其他的SoC可以类比很容易得到):
1、关于bootloader的理解--【Documentation/arm/booting】
此文档详细的讲述了bootloader的作用,具体内容如下:
[armlinux@lqmarm]$catBooting
BootingARMLinux
=================
Author:
RussellKing
Date:
18May2002
Thefollowingdocumentationisrelevantto2.4.18-rmk6andbeyond.
InordertobootARMLinux,yourequireabootloader,whichisasmall
programthatrunsbeforethemainkernel.Thebootloaderisexpected
toinitialisevariousdevices,andeventuallycalltheLinuxkernel,
passinginformationtothekernel.
Essentially,thebootloadershouldprovide(asaminimum)the
following:
1.SetupandinitialisetheRAM.
2.Initialiseoneserialport.
3.Detectthemachinetype.
4.Setupthekerneltaggedlist.
5.Callthekernelimage.
1.SetupandinitialiseRAM
---------------------------
Existingbootloaders:
MANDATORY
Newbootloaders:
MANDATORY
ThebootloaderisexpectedtofindandinitialiseallRAMthatthe
kernelwilluseforvolatiledatastorageinthesystem.Itperforms
thisinamachinedependentmanner.(Itmayuseinternalalgorithms
toautomaticallylocateandsizeallRAM,oritmayuseknowledgeof
theRAMinthemachine,oranyothermethodthebootloaderdesigner
seesfit.)
2.Initialiseoneserialport
-----------------------------
Existingbootloaders:
OPTIONAL,RECOMMENDED
Newbootloaders:
OPTIONAL,RECOMMENDED
Thebootloadershouldinitialiseandenableoneserialportonthe
target.Thisallowsthekernelserialdrivertoautomaticallydetect
whichserialportitshoulduseforthekernelconsole(generally
usedfordebuggingpurposes,orcommunicationwiththetarget.)
Asanalternative,thebootloadercanpasstherelevant'console='
optiontothekernelviathetaggedlistsspecifingtheport,and
serialformatoptionsasdescribedin
linux/Documentation/kernel-parameters.txt.
3.Detectthemachinetype
--------------------------
Existingbootloaders:
OPTIONAL
Newbootloaders:
MANDATORY
Thebootloadershoulddetectthemachinetypeitsrunningonbysome
method.Whetherthisisahardcodedvalueorsomealgorithmthat
looksattheconnectedhardwareisbeyondthescopeofthisdocument.
ThebootloadermustultimatelybeabletoprovideaMACH_TYPE_xxx
valuetothekernel.(seelinux/arch/arm/tools/mach-types).
4.Setupthekerneltaggedlist
-------------------------------
Existingbootloaders:
OPTIONAL,HIGHLYRECOMMENDED
Newbootloaders:
MANDATORY
Thebootloadermustcreateandinitialisethekerneltaggedlist.
AvalidtaggedliststartswithATAG_COREandendswithATAG_NONE.
TheATAG_COREtagmayormaynotbeempty.AnemptyATAG_COREtag
hasthesizefieldsetto'2'(0x00000002).TheATAG_NONEmustset
thesizefieldtozero.
Anynumberoftagscanbeplacedinthelist.Itisundefined
whetherarepeatedtagappendstotheinformationcarriedbythe
previoustag,orwhetheritreplacestheinformationinits
entirety;sometagsbehaveastheformer,othersthelatter.
Thebootloadermustpassataminimumthesizeandlocationof
thesystemmemory,androotfilesystemlocation.Therefore,the
minimumtaggedlistshouldlook:
+-----------+
base->|ATAG_CORE||
+-----------+|
|ATAG_MEM||increasingaddress
+-----------+|
|ATAG_NONE||
+-----------+v
ThetaggedlistshouldbestoredinsystemRAM.
Thetaggedlistmustbeplacedinaregionofmemorywhereneither
thekerneldecompressornorinitrd'bootp'programwilloverwrite
it.Therecommendedplacementisinthefirst16KiBofRAM.
5.Callingthekernelimage
---------------------------
Existingbootloaders:
MANDATORY
Newbootloaders:
MANDATORY
TherearetwooptionsforcallingthekernelzImage.IfthezImage
isstoredinflash,andislinkedcorrectlytoberunfromflash,
thenitislegalforthebootloadertocallthezImageinflash
directly.
ThezImagemayalsobeplacedinsystemRAM(atanylocation)and
calledthere.Notethatthekerneluses16KofRAMbelowtheimage
tostorepagetables.Therecommendedplacementis32KiBintoRAM.
Ineithercase,thefollowingconditionsmustbemet:
-CPUregistersettings
r0=0,
r1=machinetypenumberdiscoveredin(3)above.
r2=physicaladdressoftaggedlistinsystemRAM.
-CPUmode
Allformsofinterruptsmustbedisabled(IRQsandFIQs)
TheCPUmustbeinSVCmode.(AspecialexceptionexistsforAngel)
-Caches,MMUs
TheMMUmustbeoff.
Instructioncachemaybeonoroff.
Datacachemustbeoff.
-Thebootloaderisexpectedtocallthekernelimagebyjumping
directlytothefirstinstructionofthekernelimage.
可以看出bootloader最少具备5项功能,上面比较清晰。
可以看出,现在2.4的内核都是希望采用taggedlist的方式来进行传递的,这里没有提到比较老的方式。
这里要特别注意的是,r2=physicaladdressoftaggedlistinsystemRAM.,这里的“必须”是针对于taggedlist而言的,如果采用param_struct,则并没有这个限制。
这在后面将会详细分析,而这正是可能导致疑惑的地方。
2、参数传递数据结构的定义位置【include/asm/setup.h】,在这里就可以看到两种参数传递方式了。
可以说,现在bootloader和Linuxkernel约定的参数传递机制就是这两种,必须严格按照这两种机制进行传输,否则的话,kernel可能因为无法识别bootloader传递过来的参数而导致无法启动。
关于这两种方式,在这里还有说明:
/*
*linux/include/asm/setup.h
*
*Copyright(C)1997-1999RussellKing
*
*Thisprogramisfreesoftware;youcanredistributeitand/ormodify
*itunderthetermsoftheGNUGeneralPublicLicenseversion2as
*publishedbytheFreeSoftwareFoundation.
*
*Structurepassedtokerneltotellitaboutthe
*hardwareit'srunningon.Seelinux/Documentation/arm/Setup
*formoreinfo.
*
*NOTE:
*Thisfilecontainstwowaystopassinformationfromtheboot
*loadertothekernel.Theoldstructparam_structisdeprecated,
*butitwillbekeptinthekernelfor5yearsfromnow
*(2001).Thiswillallowbootloaderstoconverttothenewstruct
*tagway.
*/
这说明,现在参数传递必须要采用tag方式,因为现在新的kernel已经不支持param_struct方式了。
不幸的是,vivi还是采用的param_struct方式。
这里暂时以param_struct为主分析,考虑以后更新为tag方式。
在这里你也可以参考【Documentation/arm/setup】,里面有关于选项具体含义的详细说明。
(在这里多说几句。
Linux的Documentation是一个很好的学习库,几乎所有的问题在这里都能有初步的解答。
如果要想继续深入,那么就要读源代码了。
学习上,先看README,然后翻阅Documentation,无疑是一条捷径。
而且,是否有完备的文档,也是判断这个软件是否优秀的重要标准。
)
二、vivi设置Linux参数分析
上面对bootloader与Linuxkernel之间参数传递的两种方式已经有了一个总体的理解。
下面就来先看vivi部分如何设置Linux参数。
【init/main.c】boot_or_vivi()-->run_autoboot()-->exec_string("boot")
到此,也就是要执行boot命令。
与命令相关部分都在【lib/command.c】中,找到boot_cmd,然后跟踪至【lib/boot_kernel.c】,boot的执行行为函数为command_boot(),继续分析:
【lib/boot_kernel.c】command_boot()-->
主要就是三步工作。
·获取media_type。
media_type=get_param_value("media_type",&ret);
media_type是重要的,因为对于不同的存储介质,底层的驱动函数是不同的。
通过media_type这个顶层抽象,实现了与底层驱动的联系。
[armlinux@lqminclude]$catboot_kernel.h
#ifndef_VIVI_BOOT_KERNEL_H_
#define_VIVI_BOOT_KERNEL_H_
/*
*MediaType:
Atypeofstoragedevicethatcontainsthelinuxkernel
*
*+----------------+-----------------------------------------+
*|Value(Integer)|Type|
*+----------------+-----------------------------------------+
*|0|UNKNOWN|
*|1|RAM|
*|2|NORFlashMemory|
*|3|SMC(NANDFlashMemory)ontheS3C2410|
*+----------------+-----------------------------------------+
*/
enum{
MT_UNKNOWN=0,
MT_RAM,
MT_NOR_FLASH,
MT_SMC_S3C2410
};
#endif/*_VIVI_BOOT_KERNEL_H_*/
上面就是vivi支持的media_type,现在此开发板是MT_SMC_S3C2410,也就是nandflashmemory的选择部分。
·获取nandflash的kernel分区信息,为下载做好准备
kernel_part=get_mtd_partition("kernel");
if(kernel_part==NULL){
printk("Can'tfinddefault'kernel'partition\n");
return;
}
from=kernel_part->offset;
size=kernel_part->size;
这里获得了kernel所在nandflash的起始地址和大小。
这里应该注意,虽然kernel_part->offset是偏移量,但是这个偏移是相对于0x00000000而言,所以这时的offset就是对应的起始地址。
当然,对nandflash来说,这里的地址并非是内存映射,需要做一系列的变化,具体是在nand_read_ll函数中,前面的基本实验已经做过了。
·启动内核
boot_kernel(from,size,media_type);
利用前面得到的media_type,from,size就可以来启动内核了,当然还有多步工作要去做。
具体包括如下内容:
(1)获取内存基地址
boot_mem_base=get_param_value("boot_mem_base",&ret);
在vivi中,sdram是从0x30000000开始的,所以这里的boot_mem_base就是0x30000000.
(2)把kernel映象从nandflash复制到sdram的固定位置
to=boot_mem_base+LINUX_KERNEL_OFFSET;
printk("Copylinuxkernelfrom0x%08lxto0x%08lx,size=0x%08lx...",
from,to,size);
ret=copy_kernel_img(to,(char*)from,size,media_type);
这里LINUX_KERNEL_OFFSET是0x8000,关于为什么是0x8000,这是历史原因造成的,是Linux内核的一个约定,具体可以查看Linux内核的源代码中的arch/arm/kernel/head_armv.S,如下:
/*
*Weplacethepagetables16KbelowTEXTADDR.Therefore,wemustmakesure
*thatTEXTADDRiscorrectlyset.Currently,weexpecttheleastsignificant
*"short"tobe0x8000,butwecouldprobablyrelaxthisrestrictionto
*TEXTADDR>PAGE_OFFSET+0x4000
*
*Notethatswapper_pg_diristhevirtualaddressofthepagetables,and
*pgtblgivesusaposition-independentreferencetothesetables.Wecan
*dothisbecausestext==TEXTADDR
*
*swapper_pg_dir,pgtblandkrnladrareallcloselyrelated.
*/
可以看出,TEXTADDR就是stext的地址,本开发板上为0x30008000,在0x30008000往下,会放置16K的页表,预计是0x8000.不过此处可能会放松这个限制。
另外,我们的一些参数也会放到内存起始区域。
这在后面就可以看到。
总之,这个地方的位置boot_mem_base也就是kernel的第一条指令所在地,最后的程序跳转要跳到这个位置。
(