您的位置 首页 电路

嵌入式 arm渠道kernel发动第二阶段剖析

接着上面的分析,第一阶段的代码跳转后,会进入第二阶段的代码。第二阶段的代码是从\arch\arm\kernel\head.S开始的。内核启动第二阶段主要…

接着上面的剖析,榜首阶段的代码跳转后,会进入第二阶段的代码。

第二阶段的代码是从\arch\arm\kernel\head.S开端的。

内核发动第二阶段首要完结的作业有,cpuID检查,machineID(也便是开发板ID)检查,创立初始化页表,设置C代码运转环境,跳转到内核榜首个真实的C函数startkernel开端履行。

这一阶段涉及到两个重要的结构体:

(1)一个是structproc_info_list首要描绘CPU相关的信息,界说在文件arch\arm\include\asm\procinfo.h中,与其相关的函数及变量在文件arch/arm/mm/proc_arm920.S中被界说和赋值。

(2)另一个结构体是描绘开发板或者说机器信息的结构体structmachine_desc,界说在\arch\arm\include\asm\mach\arch.h文件中,其函数的界说和变量的赋值在板极相关文件arch/arm/mach-s3c2410/mach-smdk2410.c中完成,这也是内核移植非常重要的一个文件。

该阶段一般由前面的解紧缩代码调用,进入该阶段要求:

MMU=off,D-cache=off,I-cache=dontcare,r0=0,r1=machineid.

一切的机器ID列表保存在arch/arm/tools/mach-types文件中,在编译时会将这些机器ID依照共同的格局链接到根本内核映像文件vmlinux的__arch_info_begin和__arch_info_end之间的段中。存储格局界说在include/asm-arm/mach/arch.h文件中的结构体structmachine_desc{}。这两个结构体的内容终究会被衔接到根本内核映像vmlinux中的两个段内,别离是*(.proc.info.init)和*(.arch.info.init),能够参阅下面的衔接脚本。

链接脚本:arch/arm/kernel/vmlinux.lds

*链接脚本

SECTIONS

{

.=TEXTADDR;

.init:{/*初始化代码段*/

_stext=.;

_sinittext=.;

*(.init.text)

_einittext=.;

__proc_info_begin=.;

*(.proc.info.init)

__proc_info_end=.;

__arch_info_begin=.;

*(.arch.info.init)

__arch_info_end=.;

__tagtable_begin=.;

*(.taglist.init)

__tagtable_end=.;

.=ALIGN(16);

__setup_start=.;

*(.init.setup)

__setup_end=.;

__early_begin=.;

*(.early_param.init)

__early_end=.;

__initcall_start=.;

*(.initcall1.init)

*(.initcall2.init)

*(.initcall3.init)

*(.initcall4.init)

*(.initcall5.init)

*(.initcall6.init)

*(.initcall7.init)

__initcall_end=.;

__con_initcall_start=.;

*(.con_initcall.init)

__con_initcall_end=.;

__security_initcall_start=.;

*(.security_initcall.init)

__security_initcall_end=.;

.=ALIGN(32);

__initramfs_start=.;

usr/built-in.o(.init.ramfs)

__initramfs_end=.;

.=ALIGN(64);

__per_cpu_start=.;

*(.data.percpu)

__per_cpu_end=.;

#ifndefCONFIG_XIP_KERNEL

__init_begin=_stext;

*(.init.data)

.=ALIGN(4096);

__init_end=.;

#endif

}

*链接脚本

下面开端代码\arch\arm\kernel\head.S的注释:

开端剖析前先看下一点基础常识:

1.kernel运转的史前时期和内存布局

arm渠道下,zImage.bin紧缩镜像是由bootloader加载到物理内存,然后跳到zImage.bin里一段程序,它专门于将被紧缩的kernel解紧缩到KERNEL_RAM_PADDR开端的一段内存中,接着跳进真实的kernel去履行。该kernel的履行起点是stext函数,界说于arch/arm/kernel/head.S。此刻内存的布局如下图所示

在开发板3c2410中,SDRAM衔接到内存操控器的Bank6中,它的开端内存地址是0x30000000,巨细为64M,即0x20000000。ARMLinuxkernel将SDRAM的开端地址界说为PHYS_OFFSET。经bootloader加载kernel并由自解压部分代码运转后,终究kernel被放置到KERNEL_RAM_PADDR(=PHYS_OFFSET+TEXT_OFFSET,即0x30008000)地址上的一段内存,经此放置后,kernel代码今后均不会被移动。

在进入kernel代码前,即bootloader和自解紧缩阶段,ARM未敞开MMU功用。因而kernel发动代码一个重要功用是设置好相应的页表,并敞开MMU功用。为了支撑MMU功用,kernel镜像中的一切符号,包含代码段和数据段的符号,在链接时都生成了它在敞开MMU时,地点物理内存地址映射到的虚拟内存地址。

以armkernel榜首个符号(函数)stext为例,在编译链接,它生成的虚拟地址是0xc0008000,而放置它的物理地址为0x30008000(还记得这是PHYS_OFFSET+TEXT_OFFSET吗?)。实际上这个改换能够运用简略的公式进行标明:va=pa–PHYS_OFFSET+PAGE_OFFSET。Armlinux终究的kernel空间的页表,便是依照这个联系来树立。

之所以较早提及armlinux的内存映射,原因是在进入kernel代码,里边一切符号地址值为清一色的0xCXXXXXXX地址,而此刻ARM未敞开MMU功用,故在履行stext函数榜首条履行时,它的PC值便是stext地点的内存地址(即物理地址,0x30008000)。因而,下面有些代码,需求运用地址无关技能。

__HEAD/*该宏界说了下面的代码坐落”.head.text”段内*/

.typestext,%function/*声明stext为函数*/

ENTRY(stext)/*第二阶段的进口地址*/

setmodePSR_F_BIT|PSR_I_BIT|SVC_MODE,r9@ensuresvcmodeandirqsdisabled进入超级权限形式,关中止

/*从协处理器CP15,C0读取CPUID,然后在__proc_info_begin开端的段中进行查找,假如找到,则回来对应处理器相关结构体在物理地址空间的首地址到r5,终究保存在r10中*/

mrcp15,0,r9,c0,c0@getprocessorid取出cpuid

bl__lookup_processor_type@r5=procinfor9=cpuid

//

__lookup_processor_type函数的详细解析开端(\arch\arm\kernel\head-common.S)

//

在解说该程序段之前先来看一些相关常识,内核所支撑的每一种CPU类型都由结构体proc_info_list来描绘。

该结构体在文件arch/arm/include/asm/procinfo.h中界说:

structproc_info_list{

unsignedintcpu_val;

unsignedintcpu_mask;

unsignedlong__cpu_mm_mmu_flags;/*usedbyhead.S*/

unsignedlong__cpu_io_mmu_flags;/*usedbyhead.S*/

unsignedlong__cpu_flush;/*usedbyhead.S*/

constchar*arch_name;

constchar*elf_name;

unsignedintelf_hwcap;

constchar*cpu_name;

structprocessor*proc;

structcpu_tlb_fns*tlb;

structcpu_user_fns*user;

structcpu_cache_fns*cache;

};

关于arm920来说,其对应结构体在文件linux/arch/arm/mm/proc-arm920.S中初始化。

.section”.proc.info.init”,#alloc,#execinstr/*界说了一个段,下面的结构体寄存在该段中*/

.type__arm920_proc_info,#object/*声明一个结构体目标*/

__arm920_proc_info:/*为该结构体赋值*/

.long0x41009200

.long0xff00fff0

.longPMD_TYPE_SECT|\

PMD_SECT_BUFFERABLE|\

PMD_SECT_CACHEABLE|\

PMD_BIT4|\

PMD_SECT_AP_WRITE|\

PMD_SECT_AP_READ

.longPMD_TYPE_SECT|\

PMD_BIT4|\

PMD_SECT_AP_WRITE|\

PMD_SECT_AP_READ

b__arm920_setup

…………………………………

.section”.proc.info.init”标明晰该结构在编译后寄存的方位。在链接文件arch/arm/kernel/vmlinux.lds中:

SECTIONS

{

#ifdefCONFIG_XIP_KERNEL

.=XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);

#else

.=PAGE_OFFSET+TEXT_OFFSET;

#endif

.text.head:{

_stext=.;

_sinittext=.;

*(.text.head)

}

.init:{/*Initcodeanddata*/

INIT_TEXT

_einittext=.;

__proc_info_begin=.;

*(.proc.info.init)

__proc_info_end=.;

__arch_info_begin=.;

*(.arch.info.init)

__arch_info_end=.;

__tagtable_begin=.;

*(.taglist.init)

__tagtable_end=.;

………………………………

一切CPU类型对应的被初始化的proc_info_list结构体都放在__proc_info_begin和__proc_info_end之间。

/*

*r9=cpuid

*Returns:

*r5=proc_infopointerinphysicaladdressspace

*r9=cpuid(preserved)

*/

__lookup_processor_type:

adrr3,3f@r3存储的是标号3的物理地址(因为没有启用mmu,所以当时肯定是物理地址)

ldmiar3,{r5-r7}@R5=__proc_info_begin,r6=__proc_info_end,r7=标号4处的虚拟地址,即4:.long.处的地址

addr3,r3,#8@得到4处的物理地址,刚好是越过两条指令

subr3,r3,r7@getoffsetbetweenvirt&phys得到虚拟地址和物理地址之间的offset

/*运用offset,将r5和r6中保存的虚拟地址转变为物理地址*/

addr5,r5,r3@convertvirtaddressesto

addr6,r6,r3@physicaladdressspace

1:ldmiar5,{r3,r4}@value,maskr3=cpu_val,r4=cpu_mask

andr4,r4,r9@maskwantedbits;r9中寄存的是从前读出的processorID,此处屏蔽不需求的位

teqr3,r4@检查代码和CPU硬件是否匹配(比方想在arm920t上运转为cortex-a8编译的内核?不让)

beq2f@假如持平则跳转到标号2处,履行回来指令

addr5,r5,#PROC_INFO_SZ@sizeof(proc_info_list结构的长度,在这等于48)假如没找到,跳到下一个proc_info_list处

cmpr5,r6@判别是不是到了该段的结束

blo1b@假如没有,持续跳到标号1处,查找下一个

movr5,#0@unknownprocessor,假如到了结束,没找到匹配的,就把0赋值给r5,然后回来

2:movpc,lr@找到后回来,r5指向找到的结构体

ENDPROC(__lookup_processor_type)

.align2

3:.long__proc_info_begin

.long__proc_info_end

4:.long.@“.”标明当时这行代码编译衔接后的虚拟地址

.long__arch_info_begin

.long__arch_info_end

//

__lookup_processor_type函数的详细解析结束(\arch\arm\kernel\head-common.S)

//

movsr10,r5@invalidprocessor(r5=0)?

beq__error_p@yes,errorp

/*机器ID是由u-boot引导内核是经过thekernel第二个参数传递进来的,现在保存在r1中,在__arch_info_begin开端的段中进行查找,假如找到,则回来machine对应相关结构体在物理地址空间的首地址到r5,终究保存在r8中。

bl__lookup_machine_type@r5=machinfo

//

__lookup_machine_type函数的详细解析开端(\arch\arm\kernel\head-common.S)

//

每一个CPU渠道都可能有其不一样的结构体,描绘这个渠道的结构体是machine_desc。

这个结构体在文件arch/arm/include/asm/mach/arch.h中界说:

structmachine_desc{

unsignedintnr;/*architecturenumber*/

unsignedintphys_io;/*startofphysicalio*/

………………………………

};

关于渠道smdk2410来说其对应machine_desc结构在文件linux/arch/arm/mach-s3c2410/mach-smdk2410.c中初始化:

MACHINE_START(SMDK2410,”SMDK2410″)

.phys_io=S3C2410_PA_UART,

.io_pg_offst=(((u32)S3C24XX_VA_UART)>>18)&0xfffc,

.boot_params=S3C2410_SDRAM_PA+0x100,

.map_io=smdk2410_map_io,

.init_irq=s3c24xx_init_irq,

.init_machine=smdk2410_init,

.timer=&s3c24xx_timer,

MACHINE_END

关于宏MACHINE_START在文件arch/arm/include/asm/mach/arch.h中界说:

#defineMACHINE_START(_type,_name)/

staticconststructmachine_desc__mach_desc_##_type/

__used/

__attribute__((__section__(“.arch.info.init”)))={/

.nr=MACH_TYPE_##_type,/

.name=_name,

#defineMACHINE_END/

};

__attribute__((__section__(“.arch.info.init”)))标明该结构体在并今后寄存的方位。

在链接文件链接脚本文件arch/arm/kernel/vmlinux.lds中

SECTIONS

{

#ifdefCONFIG_XIP_KERNEL

.=XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);

#else

.=PAGE_OFFSET+TEXT_OFFSET;

#endif

.text.head:{

_stext=.;

_sinittext=.;

*(.text.head)

}

.init:{/*Initcodeanddata*/

INIT_TEXT

_einittext=.;

__proc_info_begin=.;

*(.proc.info.init)

__proc_info_end=.;

__arch_info_begin=.;

*(.arch.info.init)

__arch_info_end=.;

………………………………

在__arch_info_begin和__arch_info_end之间寄存了linux内核所支撑的一切渠道对应的machine_desc结构体。

/*

*r1=machinearchitecturenumber

*Returns:

*r5=mach_infopointerinphysicaladdressspace

*/

__lookup_machine_type:

adrr3,4b@把标号4处的地址放到r3寄存器里边

ldmiar3,{r4,r5,r6}@R4=标号4处的虚拟地址,r5=__arch_info_begin,r6=__arch_info_end

subr3,r3,r4@getoffsetbetweenvirt&phys核算出虚拟地址与物理地址的偏移

/*运用offset,将r5和r6中保存的虚拟地址转变为物理地址*/

addr5,r5,r3@convertvirtaddressesto

addr6,r6,r3@physicaladdressspace

/*读取machine_desc结构的nr参数,关于smdk2410来说该值是MACH_TYPE_SMDK2410,这个值在文件linux/arch/arm/tools/mach-types中:

smdk2410ARCH_SMDK2410SMDK2410193*/

1:ldrr3,[r5,#MACHINFO_TYPE]@getmachinetype

teqr3,r1@matchesloadernumber?把取到的machineid和从uboot中传过来的machineid(寄存r1中)相比较

beq2f@found假如持平,则跳到标号2处,回来

addr5,r5,#SIZEOF_MACHINE_DESC@nextmachine_desc没有找到,则持续找下一个,加上该结构体的长度

cmpr5,r6@判别是否现已到该段的结尾

blo1b@假如没有,则跳转到标号1处,持续查找

movr5,#0@unknownmachine假如现已到结尾,而且没找到,则回来值r5寄存器赋值为0

2:movpc,lr@回来原函数,且r5作为回来值

ENDPROC(__lookup_machine_type)

.align2

3:.long__proc_info_begin

.long__proc_info_end

4:.long.@“.”标明当时这行代码编译衔接后的虚拟地址

.long__arch_info_begin

.long__arch_info_end

//

__lookup_machine_type函数的详细解析结束(\arch\arm\kernel\head-common.S)

//

movsr8,r5@invalidmachine(r5=0)?

beq__error_a@yes,errora

/*检查bootloader传入的参数列表atags的合法性*/

bl__vet_atags

//

__vet_atags函数的详细解析开端(\arch\arm\kernel\head-common.S)

//

关于参数链表:

内核参数链表的格局和阐明能够从内核源代码目录树中的\arch\arm\include\asm\setup.h中找到,参数链表有必要以ATAG_CORE开端,以ATAG_NONE结束。这儿的ATAG_CORE,ATAG_NONE是各个参数的符号,自身是一个32位值,例如:ATAG_CORE=0x54410001。其它的参数符号还包含:ATAG_MEM32,ATAG_INITRD,ATAG_RAMDISK,ATAG_COMDLINE等。每个参数符号就代表一个参数结构体,由各个参数结构体构成了参数链表。参数结构体的界说如下:

structtag{
structtag_headerhdr;
union{
structtag_corecore;
structtag_mem32mem;
structtag_videotextvideotext;
structtag_ramdiskramdisk;
structtag_initrdinitrd;
structtag_serialnrserialnr;
structtag_revisionrevision;
structtag_videolfbvideolfb;
structtag_cmdlinecmdline;
structtag_acornacorn;
structtag_memclkmemclk;
}u;
};

参数结构体包含两个部分,一个是tag_header结构体,一个是u联合体。

tag_header结构体的界说如下:

structtag_header{

u32size;

u32tag;

};

其间size:标明整个tag结构体的巨细(用字的个数来标明,而不是字节的个数),等于tag_header的巨细加上u联合体的巨细,例如,参数结构体ATAG_CORE的size=(sizeof(tag->tag_header)+sizeof(tag->u.core))>>2,一般经过函数tag_size(struct*tag_xxx)来取得每个参数结构体的size。其间tag:标明整个tag结构体的符号,如:ATAG_CORE等。

/*r8=machinfo

*Returns:

*r2eithervalidatagspointer,orzero

*/

__vet_atags:

tstr2,#0x3@aligned?r2指向该参数链表的开端方位,此处判别它是否字对齐

bne1f@假如没有对齐,跳到标号1处直接回来,而且把r2的值赋值为0,作为回来值

ldrr5,[r2,#0]@isfirsttagATAG_CORE?获取榜首个tag结构的size

cmpr5,#ATAG_CORE_SIZE@判别该tag的长度是否合法

cmpner5,#ATAG_CORE_SIZE_EMPTY

bne1f@假如不合法,反常回来

ldrr5,[r2,#4]@获取榜首个tag结构体的符号

ldrr6,=ATAG_CORE@取出符号ATAG_CORE的内容

cmpr5,r6@判别该符号是否等于ATAG_CORE

bne1f@假如不等,反常回来

movpc,lr@atagpointerisok,假如都持平,则正常回来

1:movr2,#0@反常回来值

movpc,lr@反常回来

ENDPROC(__vet_atags)

//

__vet_atags函数的详细解析结束(\arch\arm\kernel\head-common.S)

//

/*创立内核初始化页表*/

bl__create_page_tables

//

__create_page_tables函数的详细解析开端(\arch\arm\kernel\head.S)

//

/*

*r8=machinfo

*r9=cpuid

*r10=procinfo

*Returns:

*r4=physicalpagetableaddress

*/

/*在该文件的最初有如下宏界说*/

#defineKERNEL_RAM_PADDR(PHYS_OFFSET+TEXT_OFFSET)

.macropgtbl,rd

ldr\rd,=(KERNEL_RAM_PADDR-0x4000)

.endm

其间:PHYS_OFFSET在arch/arm/mach-s3c2410/include/mach/memory.h界说,为UL(0x30000000),而TEXT_OFFSET在arch/arm/Makefile中界说,为内核镜像在内存中到内存开端方位的偏移(字节),为$(textofs-y)textofs-y也在文件arch/arm/Makefile中界说,为textofs-y:=0x00008000,r4=30004000为暂时页表的开端地址,首要便是初始化16K的页表,高12位虚拟地址为页表索引,每个页表索引占4个字节,所认为4K*4=16K,大页表,每一个页表项,映射1MB虚拟地址.

__create_page_tables:

/*为内核代码存储区域创立页表,首要将内核开端地址-0x4000到内核开端地址之间的16K存储器清0,将创立的页表存于此处*/

pgtblr4@r4中寄存的为页表的基地址,终究该地址会写入cp15的寄存器c2,这个值有必要是16K对齐的

movr0,r4@把页表的基地址寄存到r0中

movr3,#0@把r3清0

addr6,r0,#0x4000@r6指向16K的结尾

1:strr3,[r0],#4@把16K的页表空间清0

strr3,[r0],#4

strr3,[r0],#4

strr3,[r0],#4

teqr0,r6

bne1b

/*从proc_info_list结构中获取字段__cpu_mm_mmu_flags,该字段包含了存储空间拜访权限等,此处指令履行之后r7=0x00000c1e*/

ldrr7,[r10,#PROCINFO_MM_MMUFLAGS]@mm_mmuflags

/*为内核的榜首MB创立共同的映射,认为翻开MMU做准备,这个映射将会被paging_init()移除,这儿运用程序计数器来取得相应的段的基地址*/

movr6,pc

movr6,r6,lsr#20@startofkernelsection

orrr3,r7,r6,lsl#20@flags+kernelbase

strr3,[r4,r6,lsl#2]@identitymapping

/*MMU是经过C2中基地址(高18位)与虚拟地址的高12位组合成物理地址,在转化表中查找地址条目。R4中寄存的便是这个基地址0x30004000*/

addr0,r4,#(KERNEL_START&0xff000000)>>18@r0=0x30007000r0寄存的是转化表的开端方位

strr3,[r0,#(KERNEL_START&0x00f00000)>>18]!@r3寄存的是内核镜像代码段的开端地址

ldrr6,=(KERNEL_END-1)@获取内核的尾部虚拟地址存于r6中

addr0,r0,#4@榜首个地址条目寄存在0x30007004处,今后顺次递加

addr6,r4,r6,lsr#18@核算终究一个地址条目寄存的方位

1:cmpr0,r6@填充这之间的地址条目

/*每一个地址条目代表了1MB空间的地址映射。物理地址将从0x30100000开端映射。0X30000000开端的1MB空间将在下面映射*/

addr3,r3,#1<<20

strlsr3,[r0],#4

bls1b

…………………………………

…………………………………………

/*为了运用发动参数,将物理内存的榜首MB映射到内核虚拟地址空间的榜首个MB,r4寄存的是页表的地址。映射0X30000000开端的1MB空间PAGE_OFFSET=0XC0000000,PHYS_OFFSET=0X30000000,r0=0x30007000,上面是从0x30007004开端寄存地址条目的*/

addr0,r4,#PAGE_OFFSET>>18

orrr6,r7,#(PHYS_OFFSET&0xff000000)@r6=0x30000c1e

.if(PHYS_OFFSET&0x00f00000)

orrr6,r6,#(PHYS_OFFSET&0x00f00000)

.endif

strr6,[r0]@将0x30000c1e存于0x30007000处。

………………………

………………………………

movpc,lr@子程序回来

ENDPROC(__create_page_tables)

//

__create_page_tables函数的详细解析结束(\arch\arm\kernel\head.S)

//

/*把__switch_data标号处的地址放入r13寄存器,当履行完__enable_mmu函数时会把r13寄存器的值赋值给pc,跳转到__switch_data处履行*/

ldrr13,__switch_data@addresstojumptoaftermmuhasbeenenabled

/*把__enable_mmu函数的地址值,赋值给lr寄存器,当履行完__arm920_setup时,回来后履行__enable_mmu*/

adrlr,BSYM(__enable_mmu)@return(PIC)address

//

__enable_mmu函数的详细解析开端(\arch\arm\kernel\head.S)

//

__enable_mmu:

#ifdefCONFIG_ALIGNMENT_TRAP

orrr0,r0,#CR_A//使能地址对齐过错检测

#else

bicr0,r0,#CR_A

#endif

#ifdefCONFIG_CPU_DCACHE_DISABLE

bicr0,r0,#CR_C//制止数据cache

#endif

#ifdefCONFIG_CPU_BPREDICT_DISABLE

bicr0,r0,#CR_Z

#endif

#ifdefCONFIG_CPU_%&&&&&%ACHE_DISABLE

bicr0,r0,#CR_I//制止指令cache

#endif//装备相应的拜访权限并存入r5中

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//将拜访权限写入协处理器

mcrp15,0,r4,c2,c0,0//将页表基地址写入基址寄存器C2,0X30004000

b__turn_mmu_on//跳转到程序段去翻开MMU

ENDPROC(__enable_mmu)

文件linux/arch/arm/kernel/head.S中

__turn_mmu_on:

movr0,r0

mcrp15,0,r0,c1,c0,0//翻开MMU一起翻开cache等。

mrcp15,0,r3,c0,c0,0@readidreg读取id寄存器

movr3,r3

movr3,r3//两个空操作,等候前面所取的指令得以履行。

movpc,r13//程序跳转

ENDPROC(__turn_mmu_on)

//

__enable_mmu函数的详细解析结束(\arch\arm\kernel\head.S)

//

/*履行__arm920_setup函数(\arch\arm\mm\proc-arm920.S),该函数完结对数据cache,指令cache,writebuffer等初始化操作*/

ARM(addpc,r10,#PROCINFO_INITFUNC)

//

__arm920_setup函数的详细解析开端(\arch\arm\mm\proc-arm920.S)

//

在上面程序段.section”.text.head”,”ax”的终究有这样几行:

addpc,r10,#PROCINFO_INITFUNC

R10中寄存的是在函数__lookup_processor_type中成功匹配的结构体proc_info_list。关于arm920来说在文件linux/arch/arm/mm/proc-arm920.S中有:

.section”.proc.info.init”,#alloc,#execinstr

.type__arm920_proc_info,#object

__arm920_proc_info:

.long0x41009200

.long0xff00fff0

.longPMD_TYPE_SECT|/

PMD_SECT_BUFFERABLE|/

PMD_SECT_CACHEABLE|/

PMD_BIT4|/

PMD_SECT_AP_WRITE|/

PMD_SECT_AP_READ

.longPMD_TYPE_SECT|/

PMD_BIT4|/

PMD_SECT_AP_WRITE|/

PMD_SECT_AP_READ

b__arm920_setup

………………………………

addpc,r10,#PROCINFO_INITFUNC的意思跳到函数__arm920_setup去履行。

.type__arm920_setup,#function//标明这是一个函数

__arm920_setup:

movr0,#0//设置r0为0。

mcrp15,0,r0,c7,c7//使数据cahche,指令cache无效。

mcrp15,0,r0,c7,c10,4//使writebuffer无效。

#ifdefCONFIG_MMU

mcrp15,0,r0,c8,c7//使数据TLB,指令TLB无效。

#endif

adrr5,arm920_crval//获取arm920_crval的地址,并存入r5。

ldmiar5,{r5,r6}//获取arm920_crval地址处的接连8字节别离存入r5,r6。

mrcp15,0,r0,c1,c0//获取CP15下操控寄存器的值,并存入r0。

bicr0,r0,r5//经过检查arm920_crval的值可知该行是铲除r0中相关位,为今后对这些位的赋值做准备

orrr0,r0,r6//设置r0中的相关位,即为mmu做相应设置。

movpc,lr//上面有操作adrlr,__enable_mmu,此处将跳到程序段__enable_mmu处。

.size__arm920_setup,.-__arm920_setup

.typearm920_crval,#object

arm920_crval:

crvalclear=0x00003f3f,mmuset=0x00003135,ucset=0x00001130

//

__arm920_setup函数的详细解析结束(\arch\arm\mm\proc-arm920.S)

//

ENDPROC(stext)

接着往下剖析linux/arch/arm/kernel/head-common.S中:

.type__switch_data,%object@界说__switch_data为一个目标

__switch_data:

.long__mmap_switched

.long__data_loc@r4

.long_data@r5

.long__bss_start@r6

.long_end@r7

.longprocessor_id@r4

.long__machine_arch_type@r5

.long__atags_pointer@r6

.longcr_alignment@r7

.longinit_thread_union+THREAD_START_SP@sp

/*

*ThefollowingfragmentofcodeisexecutedwiththeMMUoninMMUmode,

*andusesabsoluteaddresses;thisisnotpositionindependent.

*r0=cp#15controlregister

*r1=machineID

*r2=atagspointer

*r9=processorID

*/

/*其间上面的几个段的界说是在文件arch/arm/kernel/vmlinux.lds中指定*/

vmlinux.lds开端*

SECTIONS

{

……………………

#ifdefCONFIG_XIP_KERNEL

__data_loc=ALIGN(4);/*locationinbinary*/

.=PAGE_OFFSET+TEXT_OFFSET;

#else

.=ALIGN(THREAD_SIZE);

__data_loc=.;

#endif

.data:AT(__data_loc){//此处数据存储在上面__data_loc处。

_data=.;/*addressinmemory*/

*(.data.init_task)

…………………………

.bss:{

__bss_start=.;/*BSS*/

*(.bss)

*(COMMON)

_end=.;

}

………………………………

init_thread_union是init进程的基地址.在arch/arm/kernel/init_task.c中:

unionthread_unioninit_thread_union__attribute__((__section__(“.init.task”)))={INIT_THREAD_INFO(init_task)};

对照vmlnux.lds.S中,咱们能够知道inittask是寄存在.data段的开端8k,而且是THREAD_SIZE(8k)对齐的*/

vmlinux.lds结束*

__mmap_switched:

adrr3,__switch_data+4

ldmiar3!,{r4,r5,r6,r7}

……………………

………………………………

movfp,#0@铲除bss段

1:cmpr6,r7

strccfp,[r6],#4

bcc1b

ARM(ldmiar3,{r4,r5,r6,r7,sp})/*把__machine_arch_type变量值放入r5中,把__atags_pointer变量的值放入r6中*/

strr9,[r4]@SaveprocessorID保存处理器id到processor_id地点的地址中

strr1,[r5]@Savemachinetype保存machineid到__machine_arch_type中

strr2,[r6]@Saveatagspointer保存参数列表首地址到__atags_pointer中

bicr4,r0,#CR_A@ClearAbit

stmiar7,{r0,r4}@Savecontrolregistervalues

bstart_kernel@程序跳转到函数start_kernel进入C言语部分。

ENDPROC(__mmap_switched)

处处咱们的发动的第二阶段剖析结束。

后面会接着剖析第三阶段。第三阶段完全是C言语代码,从start_kernel函数开端。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/dianlu/276698.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部