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 plete general Unix class distributed operating system, it's structure pact, 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 process
1. 引 言
Linux 最初是由瑞典赫尔辛基大学的学生 Linus Torvalds在1991 年开发出来的,之后在 GNU的支持下,Linux 获得了巨大的发展。虽然 Linux 在桌面 PC 机上的普及程度远不及微软的 Windows 操作系统,但它的发展速度之快、用户数量的日益增多,也是微软所不能轻视的。从嵌入式系统到超级服务站,Linux 已获得广泛的应用。Linux 是一个完整通用的Unix 类分布式操作系统,它的结构紧凑、功能强、效率高、可移植性好且在Internet 上可自由取用。Linux 和Unix 操作系统一样,操作系统的主要功能集中在内核,内核中包含进程管理、文件管理、设备管理和网络管理等部分。近些年来 Linux 在嵌入式领域的迅猛发展,更是给 Linux 注入了新的活力。
2.1 内核结构及平台相关性
本文以分析的内核版本为2.6.9。当我们使用tar 命令将linux-2.6.9.tar.bz2 解开时,内核源代码被放到了linux-2.6.9/目录中。Linux 内核各功能文件分别存放在linux-2.6.9/ 目录下的相应子目录中。Linux 操作系统可以工作在多种不同硬件平台上,如80x86CPU 系列(80386 以上)、SUN sparc64 和arm26 等。为了让Linux 体现优良的可移植性,Linux 内核代码针对不同的硬件平台包含有对应的启动和初始化程序。这些程序处于arch/子目录中。用户完全可以根据自己的需要,从内核代码中各取所需,即时编译和更换系统内核,这也是Linux 操作系统获得世界各地网络爱好者普遍支持的主要原因。鉴于绝大部分Linux 应用于
Intel 80x86 系列平台,所以本文也仅限对Linux在80x86 系列平台的启动过程进行分析。本文所要探讨的启动程序位于arch/i386/boot/ 目录,系统的启动过程主要由bootsect.s、setup.s 和head.s 等3 个汇编程序完成。其中 bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用 Linux 内核。Linux 内核在完成系统的初始化之后需要挂载某个文件系统做为根文件系统(Root Filesystem)。根文件系统是 Linux 系统的核心组成部分,它可以做为Linux 系统中文件和数据的存储区域,通常它还包括系统配置文件和运行应用软件所需要的库。应用程序可以说是嵌入式系统的“灵魂”,它所实现的功能通常就是设计该嵌入式系统所要达到的目标。如果没有应用程序的支持,任何硬件上设计精良的嵌入式系统都没有实用意义。
从以上分析我们可以看出 bootloader 和 Linux 内核在嵌入式系统中的关系和作用。Bootloader在运行过程中虽然具有初始化系统和执行用户输入的命令等作用,但它最根本的功能就是为了启动 Linux 内核。在嵌入式系统开发的过程中,很大一部分精力都是花在bootloader 和 Linux 内核的开发或移植上。如果能清楚的了解 bootloader 执行流程和 Linux的启动过程,将有助于明确开发过程中所需的工作,从而加速嵌入式系统的开发过程。而这正是本文的所要研究的内容。
3. Bootloader
3.1 Bootloader的概念和作用
Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序,其作用类似于 PC 机上的 BIOS。在完成对系统的初始化任务之后,它会将非易失性存储器(通常是 Flash或 DOC 等)中的Linux 内核拷贝到 RAM 中去,然后跳转到内核的第一条指令处继续执行,从而启动 Linux 内核。由此可见,bootloader 和 Linux 内核有着密不可分的联系,要想清楚的了解 Linux内核的启动过程,我们必须先得认识 bootloader的执行过程,这样才能对嵌入式系统的整个启过程有清晰的掌握。
3.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中的地址。
4.启动过程分析
主要对bootsect.s、setup.s 和head.s 的工作机理做了较为详细的阐述。 4.1 bootsect 模块分析
bootsect.s 代码是磁盘引导块程序,驻留在引导盘的引导扇区(0 磁道,0 磁头,第1 扇区)。在PC加电ROM BIOS 自检后,bootsect.s 由BIOS 自动加载到内存0x7C00 处,然后将自己移到内存0x90000 处。图2 代码显示了bootsect.s 的移动过程,其中#BOOTSEG 为0x7C00,#INITSEG 为0x9000。接下来,程序利用BIOS中断,INT 0x13 将setup 模块从磁盘第2 个扇区开始读到0x90200 开始处,共读4 个扇区。如果读出错误,则CF标志置位,程序复位驱动器,并重试。加载setup 模块后,程序利用中断取磁盘驱动器参数,并将中断返回的每磁道扇区数保存在变量sectors 中。然后程序将system 模块加载到内存0x10000 处。加载system 模块期间,显示“Loading system?”信息。为了提高加载速度,只要可能,就每次加载整条磁道的数据。从磁盘读取一次数据后,程序就比较当前所读段是否就是系统数据末端所处的段(#ENDSEG),如果不是,就跳转至ok1_read 标号处继续读数据。最后,程序向软驱控制卡的驱动端口0x3f2 写0,关闭软驱电动机。程序运行jmpi 0,SETUPSEG,跳转到0x9020:0000 处,CPU开始执行setup 模块。
图2 bootsect 移动代码
4.2 setup 模块分析
setup.s首先利用ROMBIOS中断读取机器系统参数(光标位置、扩展内存数、硬盘参数表等),并将这些数据保存到内存0x90000 开始的位置(覆盖掉了bootsect 程序)。这些参数将被内核中相关程序使用,例如设备驱动程序集中的ttyio.c。随后系统进入保护模式运行。CPU 在实模式下运行,寻址一个内存地址主要是使用段基址和段内偏移值,段值被存放在段寄存器中;而在保护模式运行方式下,段寄存器中存放的是一个描述表中某项的索引值。索引值指定的描述符项中含有需要寻址的内存段的基地址、段的最大长度值和段的访问级别等信息。和实模式下的寻址相比,段寄存器值换成了段描述符项索引。接下来,程序关闭中断,将system模块整体向内存低端移动0x1000。每次移动0x8000 字,循环执行8 次。然后程序执行lidt idt_48 加载中断描述符表(idt)寄存器;执行lgdt gdt_48 加载全局描述符表(gdt)寄存器。此时中断描述符表中只有一个空项(值全为0)。全局描述符表中有3 个描述符项:第1 项无用,但必须存在;第2 项(索引值0x08)是系统代码段描述符,所定义的段基址为0,段中代码可被读和执行,段长为8 M;第3项(索引值0x10) 是系统数据段描述符,所定义的段的基址为0,段中数据可读和可写,段限长为8M。最后,程序重置协处理器,对8259 中断控制芯片编程,完成进入保护虚地址模式的所有准备工作。通过设置机器状态字MSW(第0 号控制寄存器CR0 的低16 位)中的PE 位使CPU进入保护模式,开始运行system模块中的head.s(指令jmpi 0,8)。注意,CPU 已在保护模式下运行,CS 置8 表示请求特权级0,使用全局描述符表中的第1 项(索引值0x08)。图3 显示了setup.s 结束后内存分布。
图3 setup 结束后内存分布
5.总结
Linux 内核是一个非常庞大的工程,经过十多年的发展,它已从从最初的几百 KB 大小发展到现在的几百兆。清晰的了解它执行的每一个过程是件非常困难的事。但是在嵌入式开发过程中,我们并不需要十分清楚 linux 的内部工作机制,只要适当修改 linux 内核中那些与硬件相关的部分,就可以将 linux 移植到其它目标平台上。通过对 linux 的启动过程的分 析,我们可以看出哪些是和硬件相关的,哪些是 linux 内核内部已实现的功能,这样在移植linux 的过程中便有所针对。而 linux内核的分层设计将使 linux 的移植变得更加容易。
[1] Andrew S T, Albent S W. Operating system(design and implementation)[M]. Prentice-Hall Internationl inc, 1996. [2]刘淼.嵌入式系统接口设计与Linux驱动程序开发[M].北京航空航天大学出版社.20XX.6 [3]Embedded System Conference San Francisco,March..20XX
[4]Linux内核编程[M].陈莉君,贺炎,刘霞林.机械工业出版社.20XX.7
VR虚拟现实-ARM Linux启动过程分析 精品



