微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM linux的启动部分源代码简略分析

ARM linux的启动部分源代码简略分析

时间:11-09 来源:互联网 点击:
以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用。

当内核映像被加载到RAM之后,Bootloader的控制权被释放。内核映像并不是可直接运行的目标代码,而是一个压缩过的zImage(小内核)。但是,也并非是zImage映像中的一切均被压缩了,映像中包含未被压缩的部分,这部分中包含解压缩程序,解压缩程序会解压缩映像中被压缩的部分。zImage使用gzip压缩的,它不仅仅是一个压缩文件,而且在这个文件的开头部分内嵌有gzip解压缩代码。当zImage被调用时它从arch/arm/boot/compressed/head.S的start汇编例程开始执行。这个例程进行一些基本的硬件设置,并调用arch/arm/boot/compressed/misc.c中的decompress_kernel()解压缩内核。

arch/arm/kernel/head.S文件是内核真正的启动入口点,一般是由解压缩内核的程序来调用的。首先先看下对于运行这个文件的要求:

MMU = off; D-cache = off; I-cache = 无所谓,开也可以,关也可以; r0 = 0;r1 = 机器号;r2 = atags 指针。

这段代码是位置无关的,所以,如果以地址0xC0008000来链接内核,那么就可以直接用__pa(0xc0008000)地址来调用这里的代码。

其实,在这个(Linux内核中总共有多达几十个的以head.S命名的文件)head.S文件中的一项重要工作就是设置内核的临时页表,不然mmu开起来也玩不转,但是内核怎么知道如何映射内存呢?linux的内核将映射到虚地址0xCxxxxxxx处,但他怎么知道在4GB的地址空间中有哪一片ram是可用的,从而可以映射过去呢?

因为不同的系统有不通的内存映像,所以,LINUX约定,要调用内核代码,一定要满足上面的调用要求,以为最初的内核代码提供一些最重要的关于机器的信息。内核代码开始的时候,R1存放的是系统目标平台的代号,对于一些常见的,标准的平台,内核已经提供了支持,只要在编译的时候选中就行了,例如对X86平台,内核是从物理地址1M开始映射的。

好了好了,看下面的代码。

ENTRY(stext)是这个文件的入口点。最初的几行是这样的:

setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9

@ ensure svc mode

@ and irqs disabled

// 设置为SVC模式,关闭中断和快速中断
// 此处设定系统的工作状态为SVC,arm有7种状态每种状态

// 都有自己的堆栈,SVC为管理模式,具有完全的权限,可以执行任意指令

// 访问任意地址的内存

//setmode是一个宏,其定义为:

//.macro setmode, mode, reg

//msr cpsr_c, #\mode

//.endm

mrc p15, 0, r9, c0, c0 @ get processor id

bl __lookup_processor_type @ r5=procinfo r9=cpuid

movs r10, r5 @ invalid processor (r5=0)?

beq __error_p @ yes, error p

这几行是查询处理器的类型的,我们知道arm系列有很多型号,arm7、arm9、arm11、Cortex核等等类型,这么多型号要如何区分呢?其实,在arm的15号协处理器(其实ARM暂时也就这么一个协处理器)中有一个只读寄存器,存放与处理器相关信息。

__lookup_processor_type是arch/arm/kernel/head-common.S文件中定义的一个例程,这个head-common.S用include命令被包含在head.S文件中。其定义为:

__lookup_processor_type:

adr r3, 3f

ldmia r3, {r5 - r7}

add r3, r3, #8

sub r3, r3, r7 @ get offset between virt&phys

add r5, r5, r3 @ convert virt addresses to

add r6, r6, r3 @ physical address space

1: ldmia r5, {r3, r4} @ value, mask

and r4, r4, r9 @ mask wanted bits

teq r3, r4

beq 2f

add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)

cmp r5, r6

blo 1b

mov r5, #0 @ unknown processor

2: mov pc, lr

ENDPROC(__lookup_processor_type)

这个例程接受处理器ID(保存在寄存器r9中)为参数,查找链接器建立的支持的处理器表。此时此刻还不能使用__proc_info表的绝对地址,因为这时候MMU还没有开启,所以此时运行的程序没有在正确的地址空间中。所以不得不计算偏移量。若没有找到processor ID对应的处理器,则在r5寄存器中返回返回0,否则返回一个proc_info_list结构体的指针(在物理地址空间)。proc_info_list结构体在文件中定义:

struct proc_info_list {

unsigned int cpu_val;

unsigned int cpu_mask;

unsigned long __cpu_mm_mmu_flags; /* used by head.S */

unsigned long __cpu_io_mmu_flags; /* used by head.S */

unsigned long __cpu_flush; /* used by head.S */

const char *arch_name;

const char *elf_name;

unsigned int elf_hwcap;

const char *cpu_name;

struct p

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

网站地图

Top