微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 嵌入式 arm平台kernel启动第二阶段分析

嵌入式 arm平台kernel启动第二阶段分析

时间:11-09 来源:互联网 点击:
接着上面的分析,第一阶段的代码跳转后,会进入第二阶段的代码。

第二阶段的代码是从\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所在的内存地址(

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top