ARMLinux启动过程分析(1).

合集下载

ARM+Linux的启动分析(zImage)

ARM+Linux的启动分析(zImage)

ARM+Linux的启动分析(zImage)基于ARM的Linux的启动分析报告摘要:本文主要分析基于ARM的Linux-2.2.26内核启动过程。

将首先从/arch/arm/Makefile着手,介绍三种不同的启动方案,再剖析典型的压缩内核zImage启动方案的代码结构,最后将详细分析这种方案的启动过程,直到调用start_kernel()为止。

1、Linux内核的启动方案:由/arch/arm/Makefile的代码可以看出,主要有三种启动方案,分别是: echo '* zImage - Compressed kernel image (arch/$ (ARCH)/boot/zImage)'echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'echo ' bootpImage - Combined zImage and initial RAM disk' echo ' (supply initrd image via make variable INITRD=)'Linux内核有两种映像:一种是非压缩内核,叫Image,另一种是它的压缩版本,叫zImage。

根据内核映像的不同,Linux内核的启动在开始阶段也有所不同。

zImage是Image经过压缩形成的,所以它的大小比Image小。

但为了能使用zImage,必须在它的开头加上解压缩的代码,将zImage解压缩之后才能执行,因此它的执行速度比Image要慢。

但考虑到嵌入式系统的存储空容量一般比较小,采用zImage可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。

所以一般的嵌入式系统均采用压缩内核的方式(另外bootpImage是编译包含zImage和initrd的映像,可以通过make 变量INITRD=提供initrd映像)。

Arm_linux_启动分析

Arm_linux_启动分析

Arm linux 启动分析(1)王利明 walimi@ 宋振宇zhenyusong@ 2003-3-201.概述:在内核运行之前需要系统引导程序(Bootloader)完成加载内核和一些辅助性的工作,然后跳转到内核代码的起始地址并执行。

本文先分析了Bootloader 的初始化工作,接着从内核镜像的起始地址进行分析。

整个arm linux 内核的启动可分为三个阶段:第一阶段主要是进行cpu 和体系结构的检查、cpu 本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。

第一阶段的初始化是从内核入口(ENTRY(stext))开始到start_kernel 前结束。

这一阶段的代码在/arch/arm/head_armv.S 中。

2.Bootloader 2.1简介 本处介绍主要来自内核源代码下的Documentation/arm/Booting 文件,适合于arm linux 2.4.18-rmk6及以上版本。

Bootloader 主要作用是初始化一些必要的设备,然后调用内核,同时传递参数给内核。

主要完成如下工作:1. 建立和初始化RAM 。

2. 初始化一个串口。

3.4.建立内核的5. 调用内核镜像。

2.2功能详细介绍 1.建立和初始化RAM 。

要求:必须功能:探测所有的RAM 位置和大小,并对RAM 进行初始化。

2.初始化一个串口。

要求:可选,建议功能:Bootloader 应该初始化并启动一个串口。

这可以让内核的串口驱动自动探测哪个串口作为内核的控制台。

另外也可以通过给内核传递“console=”参数完成此工作。

3.检测机器的系统结构。

要求:必须功能:Bootloader 应该通过某种方法探测机器类型,最后传递给内核一个MACH_TYPE_xxx 值,这些值参看4要求:必须功能:Bootloader 必须创建和初始化内核的tagged list 。

Linuxarm底层启动流程简介

Linuxarm底层启动流程简介

Linux开发驱动底层环境启动流程Uboot编译:rm -rf 删除命令tar xjf 解压uboot文件patch -p1 < ../u-boot-1.1.6.jz2440.patch 打补丁 -p1 指示忽略第一个斜杠前因为已经在当前目录。

打好补丁之后配置文件make 100ask24x0_config之后make配置文件是厂家提供好的。

Make后生成bin文件,在Windows下进入oflash目录烧写这个文件。

选择Open jtag S2C2440 nand flash 0块0 2 0 0 0这几个数字烧写之后进入启动界面之后uboot命令按help查看支持哪些命令。

?Md 查看 md命令Print 命令查看环境变量设置环境变量 set xxxx xx savereset重启Uboot 的终极目的就是要启动内核。

Make config 就相当于执行命令100ask24x0_config : unconfig@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0NULL s3c24x0MKCONFIG := $(SRCTREE)/mkconfig$(@:_config=) $(@ 表示目标文件100ask24x0 _config=) 表示替换掉他用后面的arm arm920t 100ask24x0 NULL s3c24x0 100ask24x0最后就执行这个命令Mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0找到脚本文件mkconfig #!/bin/sh表示他是用bin目录下sh解释执行,后面是传入的参数。

这个配置过程它做了一些连接工作,把arm board arch 链接到现在的文件。

编译时的make 做的工作:用到了上面配置的config.mk(.a文件表示所有编译好的文件打包成成的一个库)OBJS = cpu/arm920/xxxxLib +=xxxx.aALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)依赖$(obj)u-boot.bin文件$(obj)u-boot.bin他又依赖于$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \-Map u-boot.map -o u-boot得到的bin文件就是这个.a文件如何组织连接在lds连接文件有说明链接地址在0x33f80000就是说应该在这个地址运行比如从0x00000000 到0x08000000是128M空间2^27次方=2^7*2^10*2^10=128M搜索命令 grep “” - nR内存一共64M0x33f80000空出512KUBOOT做一些工作:进入管理模式、关看门狗中断、初始化存储控制器、设置栈IRQFIQ、重定位(NORflash不能像内存一样写)、从flash拷到sdram、到他该运行的地址0x33f80000、清BSS段、调用C函数从上往下依次是512K UBOOT 然后是用来实现malloc接下来是全局参数,在接下来就是各种模式的栈。

arm版本linux系统的启动流程

arm版本linux系统的启动流程

arm版本linux系统的启动流程ARM架构是一种常见的处理器架构,被广泛应用于嵌入式设备和移动设备中。

在ARM版本的Linux系统中,启动流程是非常重要的,它决定了系统如何从开机到正常运行。

本文将详细介绍ARM版本Linux系统的启动流程。

一、引导加载程序(Bootloader)引导加载程序是系统启动的第一阶段,它位于系统的固化存储器中,比如ROM或Flash。

在ARM版本的Linux系统中,常用的引导加载程序有U-Boot和GRUB等。

引导加载程序的主要功能是加载内核镜像到内存中,并将控制权转交给内核。

二、内核初始化引导加载程序将内核镜像加载到内存后,控制权被转交给内核。

内核初始化是系统启动的第二阶段,它主要完成以下几个步骤:1. 设置异常向量表:ARM架构中,异常是指硬件产生的中断或故障,比如系统调用、中断请求等。

内核需要设置异常向量表,以便正确处理异常。

2. 初始化处理器:内核对处理器进行初始化,包括设置页表、启用缓存、初始化中断控制器等。

3. 启动第一个进程:内核创建第一个用户进程(一般是init进程),并将控制权转交给它。

init进程是系统中所有其他进程的父进程,负责系统的初始化工作。

三、设备树(Device Tree)设备树是ARM版本Linux系统中的一种机制,用于描述硬件设备的相关信息。

在内核初始化过程中,内核会解析设备树,并建立设备树对象,以便后续的设备驱动程序使用。

设备树描述了硬件设备的类型、地址、中断等信息,以及设备之间的连接关系。

它使得内核能够在运行时自动识别和配置硬件设备,大大提高了系统的可移植性和灵活性。

四、启动初始化(Init)启动初始化是系统启动的第三阶段,它是用户空间的第一个进程(init进程)接管系统控制权后的操作。

启动初始化主要完成以下几个任务:1. 挂载根文件系统:启动初始化会挂载根文件系统,使得用户可以访问文件系统中的文件和目录。

2. 加载系统服务:启动初始化会加载并启动系统服务,比如网络服务、日志服务、时间同步服务等。

优选(VR虚拟现实)ARMLinux启动过程分析

优选(VR虚拟现实)ARMLinux启动过程分析

优选(VR虚拟现实)ARMLinux启动过程分析ARM Linux启动过程分析赵楠本章学习目标:●了解Linux结构及平台属性●了解bootloader的相关知识●熟悉并掌握启动过程摘要:从嵌入式系统到超级服务站,嵌入式Linux 的可移植性使得我们可以在各种电子产品上看到它的身影。

Linux 是一个完整通用的Unix 类分布式操作系统,它的结构紧凑、功能强、效率高、可移植性好且在Internet 上可自由取用。

对于不同体系结构的处理器来说Linux的启动过程也有所不同。

本文以S3C2410 ARM处理器为例,详细分析了系统上电后bootloader的执行流程及ARM Linux的启动过程。

关键词:ARM Linux bootloader 启动过程Abstract: from the embedded system to super service station, embedded Linux portability allows us to various electronic products in the form of seeing it. Linux is a complete general Unix class distributed operating system, it's structure compact, the function is strong, high efficiency, good portability and in the Internet can be free to take. For different system structure of the processor is the start of the Linux process is also different. Based on the ARM processor S3C2410 as an example, the paper analyses system after the execution flow of electric bootloader and ARM Linux start-up process.Keywords: ARM Linux bootloader start-up process1. 引言Linux 最初是由瑞典赫尔辛基大学的学生Linus Torvalds在1991 年开发出来的,之后在GNU的支持下,Linux 获得了巨大的发展。

基于 Arm 的 linux 的启动分析

基于 Arm 的 linux 的启动分析

基于Arm 的linux 的启动分析(转)目录:一Makefile 的分析 (2)1.1 启动方案 (2)1.2 zImage 代码结构 (2)1.2.1 顶层vmlinux 的生成过程 (2)1.2.2 zImage 的生成 (6)二zImage 的启动过程 (12)2.1 compressed/vmlinux.lds 文件的分析 (12)2.2 compressed/head.s 文件的分析 (12)2.3inux/arch/arm/kernel/head.S 文件的分析 (18)2.4 arch/arm/kernel/common.s 文件的分析 (19)(看这个文章把握住两点:本文第一个过程是make zImage的过程,是在配置生成时候的过程,这个时候内核还没有往板子上下载呢,第二个过程是拷贝到板子上以后,从flash再拷贝到内存中,解压并且执行内核的第一阶段的启动)注:目录均设有超级链接,可以按住CTRL 并单击鼠标进行跟踪链接。

一Makefile 的分析1.1 启动方案在/arch/arm 文件夹下的Makefile 文件第215 行得到语句(有的在顶层目录中就有)define archhelpecho '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)'echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'echo ' bootpImage - Combined zImage and initial RAM disk'由此可以知道基于ARM 的启动方案有Image 、zImage 、xipImage 和bootpImage 四种。

Linux arm 启动 c语言部分详解第一讲(from Start kernel)

Linux arm 启动 c语言部分详解第一讲(from Start kernel)

[原创]Linux arm 启动c语言部分详解第一讲(from Start kernel)written by leeming作为我们实验室的一个学术交流,我顺着fp的linux arm启动汇编部分继续下去。

我们可以看到其实linux汇编部分的启动大量的工作是对zimage的解压,重定位等操作,如果是image(也就是zimage解压重定位结束后)来说,其实主要就做了以下这么几件事情:1.建立启动时的一级页表,2.打开mmu,3.保存机器号等参数。

因此对于整个处理器系统来说还需要做大量的工作,对于移植内核来说,只有真正了解了这部分你才会明白在arch/arm/mach-sep4020这个目录中的文件为什么需要这样写。

进入正题:1.进入start_kernel,详解setup_arch(处理器的移植,页表建立都在这里实现的)asmlinkage void __init start_kernel(void){&nbsp; &nbsp;&nbsp; &nbsp; char * command_line;&nbsp; &nbsp;&nbsp; &nbsp; extern struct kernel_param __start___param[], __stop___param[];/** Interrupts are still disabled. Do necessary setups, then* enable them*/&nbsp; &nbsp;&nbsp; &nbsp; lock_kernel();&nbsp; &nbsp;&nbsp; &nbsp; //这里是和高端内存相关的操作,arm中不涉及&nbsp; &nbsp;&nbsp; &nbsp; page_address_init();&nbsp; &nbsp;&nbsp; &nbsp; //这个只是printk的等级,但是为什么在控制台不显示,待看&nbsp; &nbsp;&nbsp; &nbsp; //这时候控制台还没有初始化,因此所有的信息都是在log_buf里&nbsp; &nbsp;&nbsp; &nbsp; printk(KERN_NOTICE);&nbsp; &nbsp;&nbsp; &nbsp; printk(linux_banner);&nbsp; &nbsp;&nbsp; &nbsp; setup_arch(&amp;command_line);&nbsp; &nbsp;&nbsp; &nbsp; ……&nbsp; &nbsp;&nbsp; &nbsp; ……到这里就碰到了我们详解start kernel的第一道坎,setup_arch(&amp;command_line);别看就一句话,其实这个函数本身是非常庞大的,下面我们来具体看完整的setup_arch函数。

linux启动流程分析---内核解压缩过程

linux启动流程分析---内核解压缩过程

linux启动流程分析---内核解压缩过程内核压缩和解压缩代码都在目录kernel/arch/arm/boot/compressed,编译完成后将产生vmlinux、head.o、misc.o、head-xscale.o、piggy.o这几个文件,head.o是内核的头部文件,负责初始设置;misc.o将主要负责内核的解压工作,它在head.o之后;head-xscale.o文件主要针对Xscale的初始化,将在链接时与head.o合并;piggy.o是一个中间文件,其实是一个压缩的内核(kernel/vmlinux),只不过没有和初始化文件及解压文件链接而已;vmlinux是(没有--lw:zImage是压缩过的内核)压缩过的内核,就是由piggy.o、head.o、misc.o、head-xscale.o组成的。

在BootLoader完成系统的引导以后并将Linux内核调入内存之后,调用bootLinux(),这个函数将跳转到kernel的起始位置。

如果kernel没有压缩,就可以启动了。

如果kernel压缩过,则要进行解压,在压缩过的kernel头部有解压程序。

压缩过得kernel入口第一个文件源码位置在arch/arm/boot/compressed/head.S。

它将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,然后使用在打印出信息“Uncompressing Linux...”后,调用gunzip()。

将内核放于指定的位置。

以下分析head.S文件:(1)对于各种Arm CPU的DEBUG输出设定,通过定义宏来统一操作。

(2)设置kernel开始和结束地址,保存architecture ID。

ARMlinux内核启动分析.

ARMlinux内核启动分析.

ARM linux 内核启动分析[原创2007-06-11 10:35:46 ]发表者:jimmy_leehead-armv.S主支分析head-armv.S是解压后(或未压缩的内核最先执行的一个文件,这个文件位于arch/arm/ kernel/head-armv.S,在与这个文件同目录下还有一个文件head-armo.S 与head-armv.S 很相似,但从arch/arm/下的Makefile中可以看到区别在哪里:ifeq ($(CONFIG_CPU_26,yPROCESSOR := armoifeq ($(CONFIG_ROM_KERNEL,yTEXTADDR = 0x03800000LDSCRIPT = arch/arm/vmli nu x-armo-ro m.l ds.inelseTEXTADDR = 0x02080000LDSCRIPT = arch/arm/vmli nu x-armo.lds. inen dif开篇说到,head-armv.S是进入内核最先执行的文件,为什么呢?内核可执行文件由许多链接在一起的对象文件组成。

对象文件有许多节,如文本、数据、init数据、bass等等。

这些对象文件都是由一个称为link script的文件链接并装入的。

这个link script的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。

vmlinux-armv.lds就是链接内核用到的link script,它位于arch/arm/目录下,你可能注意至U了同目录下还有一vmlinux-armv.lds.in 文件,这两文件可是有关系的,答案就在arch/arm/Makefile里。

ifeq ($(CONFIG_CPU_32,y /* 对于pxa 270 来说这里是True */PROCESSOR = armvTEXTADDR = 0xC0008000LDSCRIPT = arch/arm/vmli nux-armv.lds.inen dif$(wildcard in clude/c on fig/arch/*.h@echo ' Ge nerat ing $@'@sed 's/TEXTADDR/$(TEXTADDR/;s/DATAADDR/$(DATAADDR/' $(LDSCRIPT >$@从这个Makefile中我们可以看到,实际上arch/arm/vmlinux-armv.lds.in 就是arch/arm/vmlinux-armv」ds 是一个蓝本,在make 的时候vmlinux-armv」ds 是由sed命令来替换vmlinux-armv.lds.in文件中的TEXTADDR, DATAADDR 为特定的值而生成的。

arm-linux 启动代码分析——stage1 (1)

arm-linux 启动代码分析——stage1 (1)

arm-linux 启动代码分析——stage1 (1)本文针对arm linux, 从kernel的第一条指令开始分析,一直分析到进入start_kernel()函数.我们当前以linux-2.6.19内核版本作为范例来分析,本文中所有的代码,前面都会加上行号以便于和源码进行对照.例:在文件init/main.c中:00478: asmlinkage void __init start_kernel(void)前面的&quot;00478:&quot; 表示478行,冒号后面的内容就是源码了.在分析代码的过程中,我们使用缩进来表示各个代码的调用层次.由于启动部分有一些代码是平台特定的,虽然大部分的平台所实现的功能都比较类似,但是为了更好的对code进行说明,对于平台相关的代码,我们选择at91(ARM926EJS)平台进行分析.另外,本文是以uncompressed kernel开始讲解的.对于内核解压缩部分的code,在arch/arm/boot/compressed中,本文不做讨论.一. 启动条件通常从系统上电到执行到linux kenel这部分的任务是由bootloader来完成.关于boot loader的内容,本文就不做过多介绍.这里只讨论进入到linux kernel的时候的一些限制条件,这一般是bootloader在最后跳转到kernel之前要完成的:1.CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址对物理地址;3. 数据cache(Data cache)必须是关闭的4. 指令cache(Instructioncache)可以是打开的,也可以是关闭的,这个没有强制要求;5. CPU 通用寄存器0 (r0)必须是0;6. CPU 通用寄存器1 (r1)必须是ARM Linux machinetype (关于machine type, 我们后面会有讲解)7. CPU 通用寄存器2 (r2) 必须是kernel parameterlist 的物理地址(parameter list 是由bootloader传递给kernel,用来描述设备信息属性的列表,详细内容可参考&quot;Booting ARM Linux&quot;文档).二. starting kernel首先,我们先对几个重要的宏进行说明(我们针对有MMU的情况):&nbsp; &nbsp;&nbsp;&nbsp;宏&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;位置&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;默认值&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;说明KERNEL_RAM_ADDR&nbsp;&nbsp;arch/arm/kernel/head.S+26&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;0xc0008000&nbsp; &nbsp;&nbsp;&nbsp;kernel在RAM中的的虚拟地址PAGE_OFFSET&nbsp; &nbsp;&nbsp; &nbsp;include/asm-arm/memeory.h+50&nbsp; &nbsp;&nbsp; &nbsp;0xc0000000&nbsp; &nbsp;&nbsp;&nbsp;内核空间的起始虚拟地址TEXT_OFFSET&nbsp; &nbsp;&nbsp; &nbsp;arch/arm/Makefile+137&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x00008000&nbsp; &nbsp;&nbsp;&nbsp;内核相对于存储空间的偏移TEXTADDR&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;arch/arm/kernel/head.S+49&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; 0xc0008000&nbsp; &nbsp;&nbsp;&nbsp;kernel的起始虚拟地址PHYS_OFFSET&nbsp; &nbsp;&nbsp; &nbsp;include/asm-arm/arch-xxx/memory.h&nbsp; &nbsp;平台相关&nbsp; &nbsp;&nbsp; &nbsp;RAM的起始物理地址内核的入口是stext,这是在arch/arm/kernel/vmlinux.lds.S中定义的:00011: ENTRY(stext)对于vmlinux.lds.S,这是ldscript文件,此文件的格式和汇编及C程序都不同,本文不对ldscript作过多的介绍,只对内核中用到的内容进行讲解,关于ld的详细内容可以参考这里的ENTRY(stext) 表示程序的入口是在符号stext.而符号stext是在arch/arm/kernel/head.S中定义的:下面我们将arm linuxboot的主要代码列出来进行一个概括的介绍,然后,我们会逐个的进行详细的讲解.在arch/arm/kernel/head.S中72 - 94 行,是armlinux boot的主代码:00072:ENTRY(stext)&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00073:&nbsp;&nbsp;msr cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode00074:&nbsp; &nbsp;&nbsp; &nbsp;@and irqsdisabled&nbsp; &nbsp;&nbsp; &nbsp;00075:&nbsp;&nbsp;mrc p15, 0,r9, c0, c0&nbsp;&nbsp;@ getprocessorid&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;00076:bl __lookup_processor_type&nbsp;&nbsp;@r5=procinfor9=cpuid&nbsp; &nbsp;00077:&nbsp;&nbsp;movs r10,r5&nbsp; &nbsp; @invalid processor (r5=0)?00078:beq __error_p&nbsp; &nbsp;@yes, error‘p’&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00079:bl __lookup_machine_type&nbsp;&nbsp;@r5=machinfo&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00080:&nbsp;&nbsp;movs r8,r5&nbsp; &nbsp; @invalid machine (r5=0)?00081:beq __error_a&nbsp; &nbsp;@yes, error‘a’&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00082:bl __create_page_tables&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;00083:&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;00084:&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00091:&nbsp;&nbsp;ldr r13,__switch_data&nbsp;&nbsp;@address to jump to after00092:&nbsp; &nbsp;&nbsp; &nbsp;@mmu has beenenabled&nbsp; &nbsp;00093:&nbsp;&nbsp;adr lr,__enable_mmu&nbsp;&nbsp;@return (PIC)address&nbsp; &nbsp;00094:&nbsp;&nbsp;add pc,r10,#PROCINFO_INITFUNC&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;其中,73行是确保kernel运行在SVC模式下,并且IRQ和FIRQ中断已经关闭,这样做是很谨慎的.arm linux boot的主线可以概括为以下几个步骤:1. 确定processortype&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(75 - 78行)2. 确定machinetype&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(79 - 81行)3.创建页表&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(82行)&nbsp; &nbsp;4.调用平台特定的__cpu_flush函数&nbsp; &nbsp;&nbsp; &nbsp;(在struct proc_info_list中) (94行)&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;5.开启mmu&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(93行)6. 切换数据&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(91行)最终跳转到start_kernel&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(在__switch_data的结束的时候,调用了b start_kernel)下面,我们按照这个主线,逐步的分析Code.1. 确定processor type&nbsp; &nbsp;arch/arm/kernel/head.S中:00075:&nbsp;&nbsp;mrc p15, 0,r9, c0, c0&nbsp;&nbsp;@ getprocessorid&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;00076:bl __lookup_processor_type&nbsp;&nbsp;@r5=procinfor9=cpuid&nbsp; &nbsp;00077:&nbsp;&nbsp;movs r10,r5&nbsp; &nbsp; @invalid processor (r5=0)?00078:beq __error_p&nbsp; &nbsp;@yes, error‘p’&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;75行: 通过cp15协处理器的c0寄存器来获得processor id的指令.关于cp15的详细内容可参考相关的arm手册76行:跳转到__lookup_processor_type.在__lookup_processor_type中,会把processortype 存储在r5中77,78行: 判断r5中的processor type是否是0,如果是0,说明是无效的processortype,跳转到__error_p(出错)__lookup_processor_type 函数主要是根据从cpu中获得的processorid和系统中的proc_info进行匹配,将匹配到的proc_info_list的基地址存到r5中,0表示没有找到对应的processor type.下面我们分析__lookup_processor_type函数arch/arm/kernel/head-common.S中:00145:.type __lookup_processor_type,%function00146: __lookup_processor_type:00147:&nbsp;&nbsp;adr r3,3f00148:&nbsp;&nbsp;ldmda r3,{r5 - r7}00149:&nbsp;&nbsp;sub r3, r3,r7&nbsp; &nbsp;@get offset between virt&amp;phys00150:&nbsp;&nbsp;add r5, r5,r3&nbsp; &nbsp;@convert virt addresses to00151:&nbsp;&nbsp;add r6, r6,r3&nbsp; &nbsp;@physical address space00152: 1: ldmia r5,{r3,r4}&nbsp; &nbsp;@value, mask00153:&nbsp;&nbsp;and r4, r4,r9&nbsp; &nbsp;@mask wanted bits00154:&nbsp;&nbsp;teq r3,r400155:beq 2f00156:&nbsp;&nbsp;add r5, r5,#PROC_INFO_SZ&nbsp;&nbsp;@sizeof(proc_info_list)00157:&nbsp;&nbsp;cmp r5,r600158:blo 1b00159:&nbsp;&nbsp;mov r5,#0&nbsp; &nbsp; @unknown processor00160: 2: mov pc,lr00161:00162:00165: ENTRY(lookup_processor_type)00166:&nbsp;&nbsp;stmfd sp!,{r4 - r7, r9, lr}00167:&nbsp;&nbsp;mov r9,r000168:bl __lookup_processor_type00169:&nbsp;&nbsp;mov r0,r500170:&nbsp;&nbsp;ldmfd sp!,{r4 - r7, r9, pc}00171:00172:00176:.long __proc_info_begin00177:.long __proc_info_end00178:3: .long .00179:.long __arch_info_begin00180:.long __arch_info_end&nbsp; &nbsp;145, 146行是函数定义147行: 取地址指令,这里的3f是向前symbol名称是3的位置,即第178行,将该地址存入r3.&nbsp; &nbsp;&nbsp; &nbsp;这里需要注意的是,adr指令取址,获得的是基于pc的一个地址,要格外注意,这个地址是3f 处的&quot;运行时地址&quot;,由于此时MMU还没有打开,也可以理解成物理地址(实地址).(详细内容可参考arm指令手册)&nbsp; &nbsp;&nbsp; &nbsp;148行: 因为r3中的地址是178行的位置的地址,因而执行完后:&nbsp; &nbsp;&nbsp; &nbsp;r5存的是176行符号__proc_info_begin的地址;&nbsp; &nbsp;&nbsp; &nbsp;r6存的是177行符号__proc_info_end的地址;&nbsp; &nbsp;&nbsp; &nbsp;r7存的是3f处的地址.&nbsp; &nbsp;&nbsp; &nbsp;这里需要注意链接地址和运行时地址的区别. r3存储的是运行时地址(物理地址),而r7中存储的是链接地址(虚拟地址).&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;__proc_info_begin和__proc_info_end是在arch/arm/kernel/vmlinux.lds.S中:&nbsp; &nbsp;&nbsp; &nbsp;00031:&nbsp;&nbsp;__proc_info_begin= .;&nbsp; &nbsp;&nbsp; &nbsp;00032:&nbsp; &nbsp;*(.init)&nbsp; &nbsp;&nbsp; &nbsp;00033:&nbsp;&nbsp;__proc_info_end= .;&nbsp; &nbsp;&nbsp; &nbsp;这里是声明了两个变量:__proc_info_begin 和__proc_info_end,其中等号后面的&quot;.&quot;是locationcounter(详细内容请参考)&nbsp; &nbsp;&nbsp; &nbsp;这三行的意思是: __proc_info_begin 的位置上,放置所有文件中的&quot;.init&quot;段的内容,然后紧接着是__proc_info_end 的位置.&nbsp; &nbsp;&nbsp; &nbsp;kernel 使用struct proc_info_list来描述processor type.&nbsp; &nbsp;&nbsp; &nbsp;在include/asm-arm/procinfo.h 中:&nbsp; &nbsp;&nbsp; &nbsp;00029: struct proc_info_list {&nbsp; &nbsp;&nbsp; &nbsp;00030:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;cpu_val;&nbsp; &nbsp;&nbsp; &nbsp;00031:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;cpu_mask;&nbsp; &nbsp;&nbsp; &nbsp;00032:&nbsp;&nbsp;unsignedlong&nbsp;&nbsp;__cpu_mm_mmu_flags;&nbsp; &nbsp;&nbsp; &nbsp;00033:&nbsp;&nbsp;unsignedlong&nbsp;&nbsp;__cpu_io_mmu_flags;&nbsp; &nbsp;&nbsp; &nbsp;00034:&nbsp;&nbsp;unsignedlong&nbsp;&nbsp;__cpu_flush;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00035:&nbsp;&nbsp;constchar&nbsp;&nbsp;*arch_name;&nbsp; &nbsp;&nbsp; &nbsp;00036:&nbsp;&nbsp;constchar&nbsp;&nbsp;*elf_name;&nbsp; &nbsp;&nbsp; &nbsp;00037:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;elf_hwcap;&nbsp; &nbsp;&nbsp; &nbsp;00038:&nbsp;&nbsp;constchar&nbsp;&nbsp;*cpu_name;&nbsp; &nbsp;&nbsp; &nbsp;00039:&nbsp;&nbsp;structprocessor *proc;&nbsp; &nbsp;&nbsp; &nbsp;00040:&nbsp;&nbsp;structcpu_tlb_fns *tlb;&nbsp; &nbsp;&nbsp; &nbsp;00041:&nbsp;&nbsp;structcpu_user_fns *user;&nbsp; &nbsp;&nbsp; &nbsp;00042:&nbsp;&nbsp;structcpu_cache_fns *cache;&nbsp; &nbsp;&nbsp; &nbsp;00043: };&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;我们当前以at91为例,其processor是926的.&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;在arch/arm/mm/proc-arm926.S 中:&nbsp; &nbsp;&nbsp; &nbsp;00464:&nbsp;&nbsp;.section &quot;.init&quot;, #alloc,#execinstr&nbsp; &nbsp;&nbsp; &nbsp;00465:&nbsp; &nbsp;&nbsp; &nbsp;00466:.type __arm926_proc_info,#object&nbsp; &nbsp;&nbsp; &nbsp;00467: __arm926_proc_info:&nbsp; &nbsp;&nbsp; &nbsp;00468:.long 0x41069260&nbsp; &nbsp;@ARM926EJ-S (v5TEJ)&nbsp; &nbsp;&nbsp; &nbsp;00469:.long 0xff0ffff0&nbsp; &nbsp;&nbsp; &nbsp;00470:.long&nbsp;&nbsp;PMD_TYPE_SECT | \&nbsp; &nbsp;&nbsp; &nbsp;00471:&nbsp;&nbsp;PMD_SECT_BUFFERABLE| \&nbsp; &nbsp;&nbsp; &nbsp;00472:&nbsp;&nbsp;PMD_SECT_CACHEABLE| \&nbsp; &nbsp;&nbsp; &nbsp;00473:&nbsp; &nbsp;PMD_BIT4 |\&nbsp; &nbsp;&nbsp; &nbsp;00474:&nbsp;&nbsp;PMD_SECT_AP_WRITE| \&nbsp; &nbsp;&nbsp; &nbsp;00475:&nbsp;&nbsp;PMD_SECT_AP_READ&nbsp; &nbsp;&nbsp; &nbsp;00476:.long&nbsp;&nbsp;PMD_TYPE_SECT | \&nbsp; &nbsp;&nbsp; &nbsp;00477:&nbsp; &nbsp;PMD_BIT4 |\&nbsp; &nbsp;&nbsp; &nbsp;00478:&nbsp;&nbsp;PMD_SECT_AP_WRITE| \&nbsp; &nbsp;&nbsp; &nbsp;00479:&nbsp;&nbsp;PMD_SECT_AP_READ&nbsp; &nbsp;&nbsp; &nbsp;00480:b __arm926_setup&nbsp; &nbsp;&nbsp; &nbsp;00481:.long cpu_arch_name&nbsp; &nbsp;&nbsp; &nbsp;00482:.long cpu_elf_name&nbsp; &nbsp;&nbsp; &nbsp;00483:.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HW CAP_EDSP|HWCAP_JA V A&nbsp; &nbsp;&nbsp; &nbsp;00484:.long cpu_arm926_name&nbsp; &nbsp;&nbsp; &nbsp;00485:.long arm926_processor_functions&nbsp; &nbsp;&nbsp; &nbsp;00486:.long v4wbi_tlb_fns&nbsp; &nbsp;&nbsp; &nbsp;00487:.long v4wb_user_fns&nbsp; &nbsp;&nbsp; &nbsp;00488:.long arm926_cache_fns&nbsp; &nbsp;&nbsp; &nbsp;00489:.size __arm926_proc_info,. - __arm926_proc_info&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;从464行,我们可以看到__arm926_proc_info 被放到了&quot;.init&quot;段中.&nbsp; &nbsp;&nbsp; &nbsp;对照struct proc_info_list,我们可以看到__cpu_flush的定义是在480行,即__arm926_setup.(我们将在&quot;4.调用平台特定的__cpu_flush函数&quot;一节中详细分析这部分的内容.)&nbsp; &nbsp;&nbsp; &nbsp;从以上的内容我们可以看出: r5中的__proc_info_begin是proc_info_list的起始地址,r6中的__proc_info_end是proc_info_list的结束地址.149行:从上面的分析我们可以知道r3中存储的是3f处的物理地址,而r7存储的是3f处的虚拟地址,这一行是计算当前程序运行的物理地址和虚拟地址的差值,将其保存到r3中.150行: 将r5存储的虚拟地址(__proc_info_begin)转换成物理地址151行: 将r6存储的虚拟地址(__proc_info_end)转换成物理地址152行: 对照structproc_info_list,可以得知,这句是将当前proc_info的cpu_val和cpu_mask分别存r3,r4中153行: r9中存储了processorid(arch/arm/kernel/head.S中的75行),与r4的cpu_mask进行逻辑与操作,得到我们需要的值154行: 将153行中得到的值与r3中的cpu_val进行比较155行: 如果相等,说明我们找到了对应的processor type,跳到160行,返回156行: (如果不相等) , 将r5指向下一个proc_info,157行: 和r6比较,检查是否到了__proc_info_end.158行: 如果没有到__proc_info_end,表明还有proc_info配置,返回152行继续查找159行: 执行到这里,说明所有的proc_info都匹配过了,但是没有找到匹配的,将r5设置成0(unknownprocessor)160行: 返回2. 确定machine type&nbsp; &nbsp;arch/arm/kernel/head.S中:00079:bl __lookup_machine_type&nbsp;&nbsp;@r5=machinfo&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00080:&nbsp;&nbsp;movs r8,r5&nbsp; &nbsp; @invalid machine (r5=0)?00081:beq __error_a&nbsp; &nbsp;@yes, error ‘a’79行: 跳转到__lookup_machine_type函数,在__lookup_machine_type中,会把structmachine_desc的基地址(machine type)存储在r5中80,81行: 将r5中的machine_desc的基地址存储到r8中,并判断r5是否是0,如果是0,说明是无效的machinetype,跳转到__error_a(出错)__lookup_machine_type 函数下面我们分析__lookup_machine_type 函数:&nbsp; &nbsp;&nbsp; &nbsp;arch/arm/kernel/head-common.S中:&nbsp; &nbsp;&nbsp; &nbsp;00176:.long __proc_info_begin00177:.long __proc_info_end00178:3: .long .00179:.long __arch_info_begin00180:.long __arch_info_end00181:00182:&nbsp;&nbsp;00193:.type __lookup_machine_type,%function00194: __lookup_machine_type:00195:&nbsp;&nbsp;adr r3,3b00196:&nbsp;&nbsp;ldmia r3,{r4, r5, r6}00197:&nbsp;&nbsp;sub r3, r3,r4&nbsp; &nbsp;@get offset between virt&amp;phys00198:&nbsp;&nbsp;add r5, r5,r3&nbsp; &nbsp;@convert virt addresses to00199:&nbsp;&nbsp;add r6, r6,r3&nbsp; &nbsp;@physical address space00200: 1: ldr r3,[r5, #MACHINFO_TYPE] @ get machinetype00201:&nbsp;&nbsp;teq r3,r1&nbsp; &nbsp; @matches loader number?00202:beq 2f&nbsp; &nbsp; @found00203:&nbsp;&nbsp;add r5, r5,#SIZEOF_MACHINE_DESC @ nextmachine_desc00204:&nbsp;&nbsp;cmp r5,r600205:blo 1b00206:&nbsp;&nbsp;mov r5,#0&nbsp; &nbsp; @unknown machine00207: 2: mov pc,lr193, 194行: 函数声明195行: 取地址指令,这里的3b是向后symbol名称是3的位置,即第178行,将该地址存入r3.&nbsp; &nbsp;&nbsp; &nbsp;和上面我们对__lookup_processor_type 函数的分析相同,r3中存放的是3b处物理地址.196行: r3是3b处的地址,因而执行完后:&nbsp; &nbsp;&nbsp; &nbsp;r4存的是3b处的地址&nbsp; &nbsp;&nbsp; &nbsp;r5存的是__arch_info_begin 的地址&nbsp; &nbsp;&nbsp; &nbsp;r6存的是__arch_info_end 的地址&nbsp; &nbsp;&nbsp; &nbsp;__arch_info_begin 和__arch_info_end是在arch/arm/kernel/vmlinux.lds.S中:&nbsp; &nbsp;&nbsp; &nbsp;00034:&nbsp;&nbsp;__arch_info_begin= .;&nbsp; &nbsp;&nbsp; &nbsp;00035:&nbsp; &nbsp;*(.init)&nbsp; &nbsp;&nbsp; &nbsp;00036:&nbsp;&nbsp;__arch_info_end= .;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;这里是声明了两个变量:__arch_info_begin 和__arch_info_end,其中等号后面的&quot;.&quot;是locationcounter(详细内容请参考)&nbsp; &nbsp;&nbsp; &nbsp;这三行的意思是: __arch_info_begin 的位置上,放置所有文件中的&quot;.init&quot;段的内容,然后紧接着是__arch_info_end 的位置.&nbsp; &nbsp;&nbsp; &nbsp;kernel 使用struct machine_desc 来描述machine type.&nbsp; &nbsp;&nbsp; &nbsp;在include/asm-arm/mach/arch.h 中:&nbsp; &nbsp;&nbsp; &nbsp;00017: struct machine_desc {&nbsp; &nbsp;&nbsp; &nbsp;00018:&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00022:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;nr;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00023:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;phys_io;&nbsp; &nbsp;&nbsp; &nbsp;00024:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;io_pg_offst;&nbsp; &nbsp;&nbsp; &nbsp;00026:&nbsp; &nbsp;&nbsp; &nbsp;00027:&nbsp;&nbsp;constchar&nbsp;&nbsp;*name;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00028:&nbsp;&nbsp;unsignedlong&nbsp;&nbsp;boot_params;&nbsp; &nbsp;&nbsp; &nbsp;00029:&nbsp; &nbsp;&nbsp; &nbsp;00030:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;video_start;&nbsp; &nbsp;&nbsp; &nbsp;00031:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;video_end;&nbsp; &nbsp;&nbsp; &nbsp;00032:&nbsp; &nbsp;&nbsp; &nbsp;00033:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;reserve_lp0:1;&nbsp; &nbsp;&nbsp; &nbsp;00034:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;reserve_lp1:1;&nbsp; &nbsp;&nbsp; &nbsp;00035:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;reserve_lp2:1;&nbsp; &nbsp;&nbsp; &nbsp;00036:&nbsp;&nbsp;unsignedint&nbsp;&nbsp;soft_reboot:1;&nbsp; &nbsp;&nbsp; &nbsp;00037:void&nbsp; &nbsp;(*fixup)(structmachine_desc *,&nbsp; &nbsp;&nbsp; &nbsp;00038:&nbsp; &nbsp;&nbsp;&nbsp;struct tag *, char **,&nbsp; &nbsp;&nbsp; &nbsp;00039:&nbsp; &nbsp;&nbsp;&nbsp;struct meminfo *);&nbsp; &nbsp;&nbsp; &nbsp;00040:void&nbsp; &nbsp;(*map_io)(void);&nbsp; &nbsp;&nbsp; &nbsp;00041:void&nbsp; &nbsp;(*init_irq)(void);&nbsp; &nbsp;&nbsp; &nbsp;00042:&nbsp;&nbsp;structsys_timer *timer;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00043:void&nbsp; &nbsp;(*init_machine)(void);&nbsp; &nbsp;&nbsp; &nbsp;00044: };&nbsp; &nbsp;&nbsp; &nbsp;00045:&nbsp; &nbsp;&nbsp; &nbsp;00046:&nbsp; &nbsp;&nbsp; &nbsp;00050: #defineMACHINE_START(_type,_name)&nbsp; &nbsp;\&nbsp; &nbsp;&nbsp; &nbsp;00051: static const struct machine_desc__mach_desc_##_type \&nbsp; &nbsp;&nbsp; &nbsp;00052:__attribute_used__&nbsp; &nbsp;&nbsp;&nbsp;\&nbsp; &nbsp;&nbsp; &nbsp;00053:__attribute__((__section__(&quot;.init&quot;))) ={ \&nbsp; &nbsp;&nbsp; &nbsp;00054:.nr&nbsp;&nbsp;=MACH_TYPE_##_type,&nbsp;&nbsp;\&nbsp; &nbsp;&nbsp; &nbsp;00055:.name&nbsp;&nbsp;=_name,&nbsp; &nbsp;&nbsp; &nbsp;00056:&nbsp; &nbsp;&nbsp; &nbsp;00057: #defineMACHINE_END&nbsp; &nbsp; \&nbsp; &nbsp;&nbsp; &nbsp;00058:};&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;内核中,一般使用宏MACHINE_START来定义machine type.&nbsp; &nbsp;&nbsp; &nbsp;对于at91, 在arch/arm/mach-at91rm9200/board-ek.c 中:&nbsp; &nbsp;&nbsp; &nbsp;00137: MACHINE_START(AT91RM9200EK, &quot;Atmel AT91RM9200-EK&quot;)&nbsp; &nbsp;&nbsp; &nbsp;00138:&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;00139:&nbsp;&nbsp;.phys_io =AT91_BASE_SYS,&nbsp; &nbsp;&nbsp; &nbsp;00140:.io_pg_offst =(AT91_VA_BASE_SYS &gt;&gt; 18)&amp; 0xfffc,&nbsp; &nbsp;&nbsp; &nbsp;00141:.boot_params =AT91_SDRAM_BASE + 0x100,&nbsp; &nbsp;&nbsp; &nbsp;00142:.timer&nbsp;&nbsp;=&amp;at91rm9200_timer,&nbsp; &nbsp;&nbsp; &nbsp;00143:.map_io&nbsp;&nbsp;=ek_map_io,&nbsp; &nbsp;&nbsp; &nbsp;00144:&nbsp;&nbsp;.init_irq =ek_init_irq,&nbsp; &nbsp;&nbsp; &nbsp;00145:.init_machine =ek_board_init,&nbsp; &nbsp;&nbsp; &nbsp;00146: MACHINE_END197行:r3中存储的是3b处的物理地址,而r4中存储的是3b处的虚拟地址,这里计算处物理地址和虚拟地址的差值,保存到r3中198行: 将r5存储的虚拟地址(__arch_info_begin)转换成物理地址&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;199行:将r6存储的虚拟地址(__arch_info_end)转换成物理地址&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;200行: MACHINFO_TYPE 在arch/arm/kernel/asm-offset.c 101行定义, 这里是取struct machine_desc中的nr(architecture number) 到r3中201行: 将r3中取到的machine type 和r1中的machine type(见前面的&quot;启动条件&quot;)进行比较202行: 如果相同,说明找到了对应的machine type,跳转到207行的2f处,此时r5中存储了对应的structmachine_desc的基地址203行: (不相同), 取下一个machine_desc的地址204行: 和r6进行比较,检查是否到了__arch_info_end.205行: 如果不相同,说明还有machine_desc,返回200行继续查找.206行: 执行到这里,说明所有的machind_desc都查找完了,并且没有找到匹配的, 将r5设置成0(unknownmachine).207行: 返回3. 创建页表通过前面的两步,我们已经确定了processor type 和machine type.此时,一些特定寄存器的值如下所示:r8 = machineinfo&nbsp; &nbsp;&nbsp; &nbsp;(struct machine_desc的基地址)r9 = cpuid&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(通过cp15协处理器获得的cpu id)r10 =procinfo&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(struct proc_info_list的基地址)创建页表是通过函数__create_page_tables 来实现的.这里,我们使用的是arm的L1主页表,L1主页表也称为段页表(section page table)L1 主页表将4 GB 的地址空间分成若干个1 MB的段(section),因此L1页表包含4096个页表项(sectionentry). 每个页表项是32 bits(4 bytes)因而L1主页表占用4096 *4 = 16k的内存空间.&nbsp; &nbsp;&nbsp; &nbsp;对于ARM926,其L1 section entry的格式为:(可参考arm926EJS TRM):&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;。

ARMlinux内核启动分析.

ARMlinux内核启动分析.

ARM linux内核启动分析[原创 2007-06-11 10:35:46 ] 发表者: jimmy_leehead-armv.S主支分析head-armv.S是解压后(或未压缩的内核最先执行的一个文件,这个文件位于arch/arm/kernel/head-armv.S,在与这个文件同目录下还有一个文件head-armo.S与head-armv.S 很相似,但从arch/arm/下的Makefile中可以看到区别在哪里:ifeq ($(CONFIG_CPU_26,yPROCESSOR := armoifeq ($(CONFIG_ROM_KERNEL,yDATAADDR = 0x02080000TEXTADDR = 0x03800000LDSCRIPT = arch/arm/vmlinux-armo-rom.lds.inelseTEXTADDR = 0x02080000LDSCRIPT = arch/arm/vmlinux-armo.lds.inendifendififeq ($(CONFIG_CPU_32,yPROCESSOR = armvTEXTADDR = 0xC0008000LDSCRIPT = arch/arm/vmlinux-armv.lds.inendif……HEAD :=arch/arm/kernel/head-$(PROCESSOR.o \arch/arm/kernel/init_task.o闲话少说,在进入分析head-armv.S之前,交待一下我所分析的内核版本号以及硬件平台,内核是2.4.19-rmk7-pxa2,对应的硬件平台为pxa 270。

开篇说到,head-armv.S是进入内核最先执行的文件,为什么呢?内核可执行文件由许多链接在一起的对象文件组成。

对象文件有许多节,如文本、数据、init 数据、bass 等等。

这些对象文件都是由一个称为link script的文件链接并装入的。

ARM+Linux的启动分析(zImage)

ARM+Linux的启动分析(zImage)

基于ARM的Linux的启动分析报告摘要:本文主要分析基于ARM的Linux-2.2.26内核启动过程。

将首先从/arch/arm/Makefile着手,介绍三种不同的启动方案,再剖析典型的压缩内核zImage启动方案的代码结构,最后将详细分析这种方案的启动过程,直到调用start_kernel()为止。

1、Linux内核的启动方案:由/arch/arm/Makefile的代码可以看出,主要有三种启动方案,分别是: echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)'echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'echo ' bootpImage - Combined zImage and initial RAM disk'echo ' (supply initrd image via make variable INITRD=<path>)'Linux内核有两种映像:一种是非压缩内核,叫 Image,另一种是它的压缩版本,叫zImage。

根据内核映像的不同,Linux内核的启动在开始阶段也有所不同。

zImage是Image经过压缩形成的,所以它的大小比 Image小。

但为了能使用 zImage,必须在它的开头加上解压缩的代码,将 zImage解压缩之后才能执行,因此它的执行速度比Image要慢。

但考虑到嵌入式系统的存储空容量一般比较小,采用zImage可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。

所以一般的嵌入式系统均采用压缩内核的方式(另外bootpImage是编译包含zImage和initrd的映像,可以通过make变量INITRD=<path>提供initrd映像)。

ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客

ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客

ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客linux启动分析(1)---bootloader启动内核过程我分析的是2.4.19的内核版本,是xscale的平台,参考了网上很多有价值的帖子,也加入了自己的一些看法,陆续总结成文字,今天是第一篇:内核一般是由bootloader来引导的,通过bootloader启动内核一般要传递三个参数,第一个参数放在寄存器0中,一般都为0,r0 = 0;第二个参数放在寄存器1中,是机器类型id,r1 = Machine Type Number;第三个参数放在寄存器2中,是启动参数标记列表在ram中的起始基地址;bootloader首先要将ramdisk(如果有)和内核拷贝到ram当中,然后可以通过c语言的模式启动内核:void (*startkernel)(int zero, int arch, unsigned int params_addr) = (void(*)(int, int, unsigned int))KERNEL_RAM_BASE;startkernel(0, ARCH_NUMBER, (unsigned int)kernel_params_start);其中KERNEL_RAM_BASE为内核在ram中启动的地址,ARCH_NUMBER是Machine Type Number,kernel_params_start 是参数在ram的偏移地址。

这时候就将全力交给了内核。

linux启动分析(2)---内核启动地址的确定内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,但是该文件是由vmlinux-armv.lds.in生成的,根据编译选项的不同源文件还可以是vmlinux-armo.lds.in,vmlinux-armv-xip.lds.in。

arm-linux启动流程分析

arm-linux启动流程分析

arm-linux启动流程分析arm-linux启动流程分析一.概述 (1)二.init进程 (1)三.inittab文件简要说明 (1)四.arm-linux系统的启动分析 (3)一.概述本文将讲述arm-linux内核启动后到进入用户shell模式这段时间的启动流程,从上电到进入kernel的过程请参见sourcesafe下“personal/yangxiyuan/资料”目录下的“ARM-linux启动流程.htm”文档。

二.init进程init进程是内核启动后的第一个用户级进程,是系统中所有进程的父进程,init进程的主要任务是按照inittab文件所提供的信息创建进程,init进程繁衍出完成通常操作所需的子进程,这些操作包括:设置机器名、检查和安装磁盘及文件系统、启动系统日志、配置网络接口并启动网络和邮件服务,启动打印服务等。

三.inittab文件简要说明inittab文件中每一记录都从新的一行开始,每个记录项最多可有512个字符,每一项的格式通常如下:id: run level:action:process,解释如下:id字段:是最多4个字符的字符串,用来唯一标志表项。

run level字段:定义该记录项被调用时的运行级别,run level可以由一个或多个运行级别构成,也可以是空,空则代表运行级别0~6。

# 0 - halt (Do NOT set initdefault to this)# - 关闭计算机,安全关闭电源# 1 - Single user mode# - 进入单用户维护模式,卸下除root以外的所有文件系统并杀死所有用户进程# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)# - 多用户模式,无网络连接# 3 - Full multiuser mode# - 多用户模式,并以文本方式作为登陆方式# 4 - unused# 5 - X11# - 多用户模式,并以图形方式作为登陆方式# 6 - reboot (Do NOT set initdefault to this)# - 停止linux系统,并按照/etc/inittab默认的登记项重新引导系统。

ARM启动过程分析

ARM启动过程分析

ARM 启动过程的理解:第一篇参考文章1.在板子上电的一开始,首先自动判断是否是autoboot模式(这是由硬件设计阶段,由硬件工程师对mcu的引脚连线决定的),我所使用的s3c2410是带有nandflash的,并切被设置成autoboot,从nandflash开始启动.2.在判断是autoboot模式后,mcu内置的nandflash控制器自动将nandflash的最前面的4k区域(这4k区域存放着bootloader的最前面4k代码)拷贝到samsung所谓的"steppingstone"里面(实际上是一块4k大小的SRAM).这一过程完全由硬件自动实现,不需软件控制.3.在拷贝完前4k代码后,nandflash控制器自动将"steppingstone"映射到arm地址空间0x00000000开始的前4k区域.4.在映射过程完成后.nandflash控制器将pc指针直接指向arm地址空间的0x00000000位置,准备开始执行"steppingstone"上的代码.5.而"steppingstone"上从nandflash拷贝过来的4k代码,是程序员写的bootloader的前4k代码.这个bootloader在之前写好,并已经被烧写到nandflash的0x00000000开始的最前面区域..而这"steppingstone"上的4k代码就是bootloader的前4k代码.6.在pc指向arm地址空间的0x00000000后,系统就开始执行指令代码.这4k代码的任务是:初始化硬件,设置中断向量表,设置堆栈,然后一个很重要的任务是,将nandflash的最前面区域的bootloader(包含4k启动代码)拷贝到SDRAM中去,bootloader 代码的大小是写好bootloader就确定的.然后只需要确定bootloader想映射到SDRAM的起始位置就ok.7.在完成对nandflash上的bootloader搬移后,找到4k代码的搬移代码最后一个指令的下一个指令在SDRAM的bootloader 的地址,然后跳转到该位置,继续执行bootloader的剩余代码(引导系统).现在有这么几个问题:在启动启动完成后,steppingstone会被映射到其他地方,可以作为一般存储使用;为加快终端响应,需要将sdram开始的代码重新映射到0x00000000开始的一段区域,这样两个虚拟地址空间映射到一个物理内存区域;本文来自CSDN博客,转载请标明出处:/martree/archive/2008/11/17/3321639.aspx第二篇系统初始化流程如下:禁止看门狗——》在中断控制器中屏蔽所有中断——》系统时钟设置——》初始化端口——》DMA设置——》cashe和总线设置——》存储器设置,初始化SDRAM——》初始化堆栈——》设置IRQ和FIQ的入口——》地址重映射通常系统初始化有两个阶段组成,分别为汇编和C写成。

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

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

ARM linux的启动部分源代码简略分析ARM linux的启动部分源代码简略分析以友善之臂的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的内核将映射到虚地址0xCxxx xxxx处,但他怎么知道在4GB的地址空间中有哪一片ram是可用的,从而可以映射过去呢?因为不同的系统有不通的内存映像,所以,LINUX约定,要调用内核代码,一定要满足上面的调用要求,以为最初的内核代码提供一些最重要的关于机器的信息。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

ARM Linux启动过程分析(1)
摘要:嵌入式 Linux 的可移植性使得我们可以在各种电子产品上看到它的身影。

对于不同体系结构的处理器来说Linux的启动过程也有所不同。

本文以S3C2410 ARM处理器为例,详细分析了系统上电后 bootloader的执行
流程及 ARM Linux的启动过程。

关键词:ARM Linux bootloader 启动过程Abstract:We can see embedded Linux in kinds of electronic products
b ecause of its portability. Linux’s start-up procedure for different processors is also different. This paper provides the analysis ofbootloader execution process and Linux kernel start-up procedure - taking the S3C2410 ARM processor as example. Keywords: ARM Linux bootloader start-up procedure 1. 引言 Linux 最初是由瑞典赫尔辛基大学的学生 Linus Torvalds在1991 年开发出来的,之后在 GNU的支持下,Linux 获得了巨大的发展。

虽然 Linux 在桌面 PC 机上的普及程度远不及微软的Windows 操作系统,但它的发展速度之快、用户数量的日益增多,也是微软所不能轻视的。

而近些年来 Linux 在嵌入式领域的迅猛发展,更是给 Linux 注入了新的活力。

一个嵌入式 Linux 系统从软件角度看可以分为四个部分:引导加载程序(bootloader), Linux 内核,文件系统,应用程序。

其中bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用 Linux 内核。

Linux 内核在完成系统的初始化之后需要挂载某个文件系统做为根文件系统(Root Filesystem)。

根文件系统是 Linux 系统的核心组成部分,它可以做为Linux 系统中文件和数据的存储区域,通常它还包括系统配置文件和运行应用软件所需要的库。

应用程序可以说是嵌入式系统的“灵魂”,它所实现的功能通常就是设计该嵌入式系统所要达到的目标。

如果没有应用程序的支持,任何硬件上设计精良的嵌入式系统都没有实用意义。

从以上分析我们可以看出 bootloader 和 Linux 内核在嵌入式系统中的关系和作用。

Bootloader在运行过程中虽然具有初始化系统和执行用户输入的命令等作用,但它最根本的功能就是为了启动 Linux 内核。

在嵌入式系统开发的过程中,很大一部分精力都是花在bootloader 和 Linux 内核的开发或移植上。

如果能清楚的了解 bootloader 执行流程和 Linux的启动过程,将有助于明确开发过程中所需的工作,从而加速嵌入式系统的开发过程。

而这正是本文的所要研究的内容。

2. Bootloader 2.1 Bootloader的概念和作用Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序,其作用类似于 PC 机上的 BIOS。

在完成对系统的初始化任务之后,它会将非易失性存储器(通常是 Flash或 DOC 等)中的Linux 内核拷贝到 RAM 中去,然后跳转到内核的第一条指令处继续执行,从而启动 Linux 内核。

由此可见,bootloader 和 Linux 内核有着密不可分的联系,要想清楚的了解 Linux 内核的启动过程,我们必须先得认识 bootloader的执行过程,这样才能对嵌
入式系统的整个启过程有清晰的掌握。

2.2 Bootloader的执行过程不同的处
理器上电或复位后执行的第一条指令地址并不相同,对于 ARM 处理器来说,该地址为 0x00000000。

对于一般的嵌入式系统,通常把 Flash 等非易失性存储器映射到这个地址处,而 bootloader就位于该存储器的最前端,所以系统上
电或复位后执行的第一段程序便是 bootloader。

而因为存储 bootloader的存储器不同,bootloader的执行过程也并不相同,下面将具体分析。

嵌入式系
统中广泛采用的非易失性存储器通常是 Flash,而 Flash 又分为 Nor Flash 和Nand Flash 两种。

它们之间的不同在于: Nor Flash 支持芯片内执行(XIP, eXecute In Place),这样代码可以在Flash上直接执行而不必拷贝到RAM中去执行。

而Nand Flash并不支持XIP,所以要想执行 Nand Flash 上的代码,必须先将其拷贝到 RAM中去,然后跳到 RAM 中去执行。

实际应用中的 bootloader根据所需功能的不同可以设计得很复杂,除完成基本的初始化系统和调用 Linux 内核等基本任务外,还可以执行很多用户输入的命令,比如设置 Linux 启动参数,给 Flash 分区等;也可以设计得很简单,只完成最基本的功能。

但为了能达到启动Linux 内核的目的,所有的 bootloader都必须具备以下功能:1) 初始化 RAM 因为 Linux 内核一般都会在 RAM 中运行,所以在调用 Linux 内核之前 bootloader 必须设置和初始化 RAM,为调用Linux内核做好准备。

初始化 RAM 的任务包括设置 CPU 的控制寄存器参数,以便能正常使用 RAM 以及检测RAM 大小等。

2) 初始化串口串口在 Linux 的启动过程中有着非常重要的作用,它是 Linux内核和用户交互的方式之一。

Linux 在启动过程中可以将信息通过串口输出,这样便可清楚的了解 Linux 的启动过程。

虽然它并不是 bootloader 必须要完成的工作,但是通过串口输出信息是调试 bootloader 和Linux 内核的强有力的工具,所以一般的bootloader 都会在执行过程中初始化一个串口做为调试端口。

3) 检测处理器类型 Bootloader在调用 Linux内核前必须检测系统的处理器类型,并将其保存到某个常量中提供给 Linux 内核。

Linux 内核在启动过程中会根据该处理器类型调用相应的初始化程序。

4) 设置 Linux启动参数 Bootloader在执行过程中必须设置和初始化 Linux 的内核启动参数。

目前传递启动参数主要采用两种方式:即通过 struct param_struct 和struct tag(标记列表,tagged list)两种结构传递。

struct param_struct 是一种比较老的参数传递方式,在 2.4 版本以前的内核中使用较多。

从 2.4 版本以后 Linux 内核基本上采用标记列表的方式。

但为了保持和以前版本的兼容性,它仍支持 struct param_struct 参数传递方式,只不过在内核启动过程中它将被转换成标记列表方式。

标记列表方式是种比较新的参数传递方式,它必须以 ATAG_CORE 开始,并以ATAG_NONE 结尾。

中间可以根据需要加入其他列表。

Linux内核在启动过程中会根据该启动参数进行相应的初始化工作。

5) 调用 Linux内核映像Bootloader完成的最后一项工作便是调用 Linux内核。

如果 Linux 内核存放在 Flash
中,并且可直接在上面运行(这里的 Flash 指 Nor Flash),那么可直接跳转到内核中去执行。

但由于在 Flash 中执行代码会有种种限制,而且速度也远不及 RAM 快,所以一般的嵌入式系统都是将 Linux 内核拷贝到 RAM 中,然后跳转到 RAM 中去执行。

不论哪种情况,在跳到 Linux 内核执行之前 CUP的寄存器必须满足以下条件:r0=0,r1=处理器类型,r2=标记列表在 RAM中的地址。

1。

相关文档
最新文档