首页 逐行分析u-boot

逐行分析u-boot

举报
开通vip

逐行分析u-boot逐行分析u-boot 15.1 Bootloader简介 [编辑] 15.1.1 Bootloader的概念 一。 Bootloader的引入 从前面的硬件实验可以知道,系统上电之后,需要一段程序来进行初始化:关闭WATCHDOG、改变系统时钟、初始化存储控制器、将更多的代码复制到内存中等等。如果它能将操作系统内核(无论从本地,比如Flash;还是从远端,比如通过网络)复制到内存中运行,就称这段程序为Bootloader。 简单地说,Bootloader就是这么一小段程序,它在系统上电时开始执行,初始化硬件设备、准...

逐行分析u-boot
逐行分析u-boot 15.1 Bootloader简介 [编辑] 15.1.1 Bootloader的概念 一。 Bootloader的引入 从前面的硬件实验可以知道,系统上电之后,需要一段程序来进行初始化:关闭WATCHDOG、改变系统时钟、初始化存储控制器、将更多的代码复制到内存中等等。如果它能将操作系统内核(无论从本地,比如Flash;还是从远端,比如通过网络)复制到内存中运行,就称这段程序为Bootloader。 简单地说,Bootloader就是这么一小段程序,它在系统上电时开始执行,初始化硬件设备、准备好软件环境,最后调用操作系统内核。 可以增强Bootloader的功能,比如增加网络功能、从PC上通过串口或网络下载文件、烧写文件、将Flash上压缩的文件解压后再运行等──这就是一个功能更为强大的Bootloader,也称为Monitor。实际上,在最终产品中用户并不需要这些功能,它们只是为了方便开发。 Bootloader的实现严重依赖于具体硬件,在嵌入式系统中硬件配置千差万别,即使是相同的CPU,它的外设(比如Flash)也可能不同,所以不可能有一个Bootloader支持所有的CPU、所有的电路板。即使是支持CPU架构比较多的U-Boot,也不是一拿来就可以使用的(除非里面的配置刚好与你的板子相同),需要进行一些移植。 二。 Bootloader的启动方式 CPU上电后,会从某个地址开始执行。比如MIPS结构的CPU会从0xBFC00000取第一条指令,而ARM结构的CPU则从地址0x0000000开始。嵌入式单板中,需要把存储器件ROM或Flash等映射到这个地址,Bootloader就存放在这个地址开始处,这样一上电就可以执行。 在开发时,通常需要使用各种命令操作Bootloader,一般通过串口来连接PC和开发板,可以在串口上输入各种命令、观察运行结果等。这也只是对开发人员才有意义,用户使用产品时是不用接串口来控制Bootloader的。从这个观点来看,Bootloader可以分为两种操作模式(Operation Mode): (1)启动加载(Boot loading)模式。 上电后,Bootloader从板子上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。产品发布时,Bootloader工作在这种模式下。 (2)下载(Downloading)模式。 在这种模式下,开发人员可以使用各种命令,通过串口连接或网络连接等通信手段从主机(Host)下载文件(比如内核映像、文件系统映像),将它们直接放在内存运行或是烧入Flash类固态存储设备中。 板子与主机间传输文件时,可以使用串口的xmodem/ymodem/zmodem 协议 离婚协议模板下载合伙人协议 下载渠道分销协议免费下载敬业协议下载授课协议下载 ,它们使用简单,只是速度比较慢;还可以使用网络通过tftp、nfs协议来传输,这时,主机上要开启tftp、nfs服务;还有其他 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 ,比如USB等。 像Blob或U-Boot等这样功能强大的Bootloader通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,U-Boot在启动时处于正常的启动加载模式,但是它会延时若干秒(这可以设置)等待终端用户按下任意键而将U-Boot切换到下载模式。如果在指定时间内没有用户按键,则U-Boot继续启动Linux内核。 [编辑] 15.1.2 Bootloader的结构和启动过程 ​ 1. 概述 在移植之前先了解Bootloader的一些通用概念,对理解它的代码会有所帮助。 在一个嵌入式Linux系统中,从软件的角度通常可以分为4个层次: (1)引导加载程序,包括固化在固件(firmware)中的 boot 代码(可选)和Bootloader两大部分。 有些CPU在运行Bootloader之前先运行一段固化的程序(固件,firmware),比如x86结构的CPU就是先运行BIOS中的固件,然后才运行硬盘第一个分区(MBR)中的Bootloader。 在大多嵌入式系统中并没有固件,Bootloader是上电后执行的第一个程序。 (2)Linux内核。 特定于嵌入式板子的定制内核以及内核的启动参数。内核的启动参数可以是内核默认的,或是由Bootloader传递给它的。 (3)文件系统。 包括根文件系统和建立于Flash内存设备之上的文件系统。里面包含了Linux系统能够运行所必需的应用程序、库等,比如可以给用户提供操作Linux的控制界面的shell程序,动态连接的程序运行时需要的glibc或uClibc库,等等。 (4)用户应用程序。 特定于用户的应用程序,它们也存储在文件系统中。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:Qtopia 和 MiniGUI 等。 显然,在嵌入系统的固态存储设备上有相应的分区来存储它们,图15.1是一个典型的分区结构。 [[Image:]] 图15.1 嵌入式Linux系统中的典型分区结构 “Boot parameters”分区中存放一些可设置的参数,比如IP地址、串口波特率、要传递给内核的命令行参数等。正常启动过程中,Bootloader首先运行,然后它将内核复制到内存中(也有些内核可以在固态存储设备上直接运行),并且在内存某个固定的地址设置好要传递给内核的参数,最后运行内核。内核启动之后,它会挂接(mount)根文件系统(“Root filesystem”),启动文件系统中的应用程序。 ​ 2. Bootloader的两个阶段 Bootloader的启动过程启动过程可以分为单阶段(Single Stage)、多阶段(Multi-Stage)两种。通常多阶段的Bootloader能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的Bootloader大多都是 2 阶段的启动过程。这从前面的硬件实验可以很好地理解这点:第一阶段使用汇编来实现,它完成一些依赖于 CPU 体系结构的初始化,并调用第二阶段的代码。第二阶段则通常使用C语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。 一般而言,这两个阶段完成的功能可以如下分类,但这不是绝对的: (1)Bootloader第一阶段的功能。 ​  o​ 硬件设备初始化。 o​ 为加载Bootloader的第二阶段代码准备RAM空间。 o​ 拷贝Bootloader的第二阶段代码到 RAM 空间中。 o​ 设置好栈。 o​ 跳转到第二阶段代码的C入口点。 在第一阶段进行的硬件初始化一般包括:关闭WATCHDOG、关中断、设置CPU的速度和时钟频率、RAM初始化等。这些并不都是必需的,比如S3C2410/S3C2440的开发板所使用的U-Boot中,就将CPU的速度和时钟频率的设置放在第二阶段。 甚至,将第二阶段的代码复制到RAM空间中也不是必需的,对于NOR Flash等存储设备,完全可以在上面直接执行代码,只不过这相比在RAM中执行效率大为降低。 (2)Bootloader第二阶段的功能。 ​  o​ 初始化本阶段要使用到的硬件设备。 o​ 检测系统内存映射(memory map)。 o​ 将内核映像和根文件系统映像从Flash上读到RAM空间中。 o​ 为内核设置启动参数。 o​ 调用内核。 为了方便开发,至少要初始化一个串口以便程序员与Bootloader进行交互。 所谓检测内存映射,就是确定板上使用了多少内存,它们的地址空间是什么。由于嵌入式开发中,Bootloader多是针对某类板子进行编写,所以可以根据板子的情况直接设置,不需要考虑可以适用于各类情况的复杂算法。 Flash上的内核映像有可能是经过压缩的,在读到RAM之后,还需要进行解压。当然,对于有自解压功能的内核,不需要Bootloader来解压。 将根文件系统映像复制到RAM中,这不是必需的。这取决于是什么类型的根文件系统,以及内核访问它的方法。 为内核设置启动参数将在下一小节介绍。 将内核存放在适当的位置后,直接跳到到它的入口点即可调用内核。调用内核之前,下列条件要满足: (1)CPU 寄存器的设置。 ​  o​ R0=0 o​ R1=机器类型ID;对于ARM结构的CPU,其机器类型ID可以参见 linux/arch/arm/tools/mach-types。 o​ R2=启动参数标记列表在 RAM 中起始基地址 (2)CPU工作模式。 ​  o​ 必须禁止中断(IRQs和FIQs) o​ CPU 必须 SVC 模式 (3)Cache 和 MMU 的设置。 ​  o​ MMU 必须关闭 o​ 指令 Cache 可以打开也可以关闭 o​ 数据 Cache 必须关闭 如果用C语言,可以像下列示例代码一样来调用内核: void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE; …… theKernel(0, ARCH_NUMBER, (u32) kernel_params_start); ​ 3. Bootloader与内核的交互 Bootloader与内核的交互是单向的,Bootloader将各类参数传给内核。由于它们不能同时运行,传递办法只有一个:Bootloader将参数放在某个约定的地方之后,再启动内核,内核启动后从这个地方获得参数。 除了约定好参数存放的地址外,还要规定参数的结构。Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。标记,就是一种数据结构;标记列表,就是挨着存放的多个标记。标记列表以标记ATAG_CORE 开始,以标记ATAG_NONE 结束。标记的数据结构为tag,它由一个tag_header结构和一个联合(union)组成。tag_header结构表示标记的类型及长度,比如是表示内存还是表示命令行参数等。对于不同类型的标记使用不同的联合(union),比如表示内存时使用tag_mem32,表示命令行时使用tag_cmdline。数据结构tag和tag_header定义在Linux内核源码的include/asm/setup.h头文件中: struct tag_header { u32 size; u32 tag; };
struct tag { struct tag_header hdr; union { struct tag_corecore; struct tag_mem32mem; struct tag_videotextvideotext; struct tag_ramdiskramdisk; struct tag_initrdinitrd; struct tag_serialnrserialnr; struct tag_revisionrevision; struct tag_videolfbvideolfb; struct tag_cmdlinecmdline;
/* * Acorn specific */ struct tag_acornacorn;
/* * DC21285 specific */ struct tag_memclkmemclk; } u; }; 下面以设置内存标记、命令行标记为例说明参数的传递: (1)设置标记 ATAG_CORE。 标记列表以标记 ATAG_CORE开始,假设Bootloader与内核约定的参数存放地址为0x30000100,则可以以如下代码设置标记 ATAG_CORE: params = (struct tag *) 0x30000100;
params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0;
params = tag_next (params); 其中,tag_next定义如下,它指向当前标记的末尾: #define tag_next(t)((struct tag *)((u32 *)(t) + (t)->hdr.size)) (2)设置内存标记。 假设开发板使用的内存起始地址为0x30000000,大小为0x4000000,则内存标记可以如下设置: params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); params->u.mem.start = 0x30000000; params->u.mem.size = 0x4000000; params = tag_next (params); (3)设置命令行标记。 命令行就是一个字符串,它被用来控制内核的一些行为。比如"root=/dev/mtdblock2 init=/linuxrc console=ttySAC0"表示根文件系统在MTD2分区上,系统启动后执行的第一个程序为/linuxrc,控制台为ttySAC0(即第一个串口)。 命令行可以在Bootloader中通过命令设置好,然后如下构造标记传给内核: char *p = "root=/dev/mtdblock2 init=/linuxrc console=ttySAC0"; params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); (4)设置标记ATAG_NONE。 标记列表以标记ATAG_NONE结束,如下设置: params->hdr.tag = ATAG_NONE; params->hdr.size = 0; [编辑] 15.1.3 常用Bootloader介绍 现在Bootloader种类繁多,比如x86上有LILO、GRUB等。对于ARM架构的CPU,有U-Boot、Vivi等。它们各有特点,下面列出Linux的开放源代码的Bootloader及其支持的体系架构,如表15.1所示。 表15.1 开放源码的Linux引导程序 Bootloader Monitor 描述 X86 ARM PowerPC LILO 否 Linux磁盘引导程序 是 否 否 GRUB 否 GNU的LILO替代程序 是 否 否 Loadlin 否 从DOS引导Linux 是 否 否 ROLO 否 从ROM引导Linux而不需要BIOS 是 否 否 Etherboot 否 通过以太网卡启动Linux系统的固件 是 否 否 LinuxBIOS 否 完全替代BUIS的Linux引导程序 是 否 否 BLOB 是 LART等硬件平台的引导程序 否 是 否 U-Boot 是 通用引导程序 是 是 是 RedBoot 是 基于eCos的引导程序 是 是 是 Vivi 是 Mizi公司针对SAMSUNG的ARM CPU 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 的引导程序 否 是 否 对于本书使用的S3C2410/S3C2440开发板,U-Boot和Vivi是两个好选择。Vivi是Mizi公司针对SAMSUNG的ARM架构CPU专门设计的,基本上可以直接使用,命令简单方便。不过其初始版本只支持串口下载,速度较慢。在网上出现了各种改进版本:支持网络功能、USB功能、烧写YAFFS文件系统映像等。U-Boot则支持大多CPU,可以烧写EXT2、JFFS2文件系统映像,支持串口下载、网络下载,并提供了大量的命令。相对于Vivi,它的使用更复杂,但是可以用来更方便地调试程序。 [编辑] 15.2 U-Boot分析与移植 [编辑] 15.2.1 U-Boot 工程 路基工程安全技术交底工程项目施工成本控制工程量增项单年度零星工程技术标正投影法基本原理 简介 U-Boot,全称为Universal Boot Loader,即通用Bootloader,是遵循GPL条款的开放源代码项目。其前身是由德国DENX软件工程中心的Wolfgang Denk基于8xxROM的源码创建的PPCBOOT工程。后来整理代码结构使得非常容易增加其他类型的开发板、其他架构的CPU(原来只支持PowerPC);增加更多的功能,比如启动Linux、下载S-Record格式的文件、通过网络启动、通过PCMCIA/CompactFLash/ATA disk/SCSI等方式启动。增加ARM架构CPU及其他更多CPU的支持后,改名为U-Boot。 它的名字“通用”有两层含义:可以引导多种操作系统、支持多种架构的CPU。它支持如下操作系统:Linux、NetBSD、 VxWorks、QNX、RTEMS、ARTOS、LynxOS等,支持如下架构的CPU:PowerPC、MIPS、x86、ARM、NIOS、XScale等。 U-Boot有如下特性: ​ 开放源码; ​ 支持多种嵌入式操作系统内核,如Linux、NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS; ​ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale; ​ 较高的可靠性和稳定性; ​ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等; ​ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等; ​ 较为丰富的开发调试文档与强大的网络技术支持; ​ 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统 ​ 支持NFS挂载、从FLASH中引导压缩或非压缩系统内核; ​ 可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲; ​ 支持目标板环境变量多种存储方式,如FLASH、NVRAM、EEPROM; ​ CRC32校验,可校验FLASH中内核、RAMDISK镜像文件是否完好; ​ 上电自检功能:SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号; ​ 特殊功能:XIP内核引导; 可以从http://sourceforge.net/projects/u-boot获得U-Boot的最新版本,如果使用过程中碰到问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 或是发现Bug,可以通过邮件列表网站http://lists.sourceforge.net/lists/listinfo/u-boot-users/获得帮助。 最新的更新代码地址http://www.denx.de/wiki/U-Boot/WebHome [编辑] 15.2.2 U-Boot源码结构 本书在u-boot-1.1.6的基础上进行分析和移植,从sourceforge网站下载u-boot-1.1.6.tar.bz2后解压即得到全部源码。U-Boot源码目录结构比较简单、独立,目录结构也比较浅,很容易全部掌握。 u-boot-1.1.6根目录下共有26个子目录,可以分为4类: (1)平台相关的或开发板相关的。 (2)通用的函数。 (3)通用的设备驱动程序。 (4)U-Boot工具、示例程序、文档。 先将这26个目录的功能与作用如表15.2所示。 表15.2 U-Boot顶层目录说明 目录 特性 解释说明 board 开发板相关 对应不同配置的电路板(即使CPU相同),比如smdk2410、sbc2410x cpu 平台相关 对应不同的CPU,比如arm920t、arm925t、i386等;在它们的子目录下仍可以进一步细分,比如arm920t下就有at91rm9200、s3c24x0 lib_i386类似 某一架构下通用的文件 include 通用的函数 头文件和开发板配置文件,开发板的配置文件都放在include/configs目录下,U-Boot没有make menuconfig类似的莱单来进行可视化配置,需要手动地修改配置文件中的宏定义 lib_generic 通用的库函数,比如printf等 common 通用的函数,多是对下一层驱动程序的进一步封装 disk 通用的设备驱动程序 硬盘接口程序 drivers 各类具体设备的驱动程序,基本上可以通用,它们通过宏从外面引入平台/开发板相关的函数 dtt 数字温度测量器或者传感器的驱动 fs 文件系统 nand_spl U-Boot一般从ROM、NOR Flash等设备启动,现在开始支持从NAND Flash启动,但是支持的CPU种类还不多 net 各种网络协议 post 上电自检程序 rtc 实时时钟的驱动 doc 文档 开发、使用文档 examples 示例程序 一些测试程序,可以使用U-Boot下载后运行 tools 工具 制作S-Record、U-Boot格式映像的工具,比如mkimage U-Boot中各目录间也是有层次结构的,虽然这种分法不是绝对的,但是在移植过程中可以提供一些指导意义,如图15.2所示。 [[Image:]] 图15.2 U-Boot顶层目录的层次结构 比如common/cmd_nand.c文件提供了操作NAND Flash的各种命令,这些命令通过调用drivers/nand/nand_base.c中的擦除、读写函数来实现。这些函数针对NAND Flash的共性作了一些封装,将平台/开发板相关的代码用宏或外部函数来代替。而这些宏与外部函数,如果与平台相关,就要在下一层次的cpu/xxx(xxx表示某型号的CPU)中实现;如果与开发板相关,就要在下一层次的board/xxx目录(xxx表示某款开发板)中实现。本书移植的U-Boot,就是在cpu/arm920t/s3c24x0目录下增加了一个nand_flash.c文件来实现这些函数。 以增加烧写yaffs文件系统映像的功能为例──就是在common目录下的cmd_nand.c中增加命令,比如nand write.yaffs:这个命令要调用drivers/nand/nand_util.c中的相应函数,针对yaffs文件系统的特点依次调用擦除、烧写函数。而这些函数依赖于drivers/nand/nand_base.c、cpu/arm920t/s3c24x0/nand_flash.c文件中的相关函数。 目前u-boot-1.1.6支持10种架构──根目录下有10个类似lib_i386的目录、31个型号(类型)的CPU──cpu目录下有31个子目录,214种开发板──board目录下有214个子目录,很容易从中找到与自己的板子相似的配置,在上面稍作修改即可使用。 [编辑] 15.2.3 U-Boot的配置、编译、连接过程 [编辑] 1. U-Boot初体验 u-boot-1.1.6中有几千个文件,要想了解对于某款开发板,使用哪些文件、哪个文件首先执行、可执行文件占用内存的情况,最好的方法就是阅读它的Makefile。 根据顶层Readme文件的说明,可以知道如果要使用开发板board/,就先执行“make _config”命令进行配置,然后执行“make all”,就可以生成如下3个文件: ​ u-boot.bin:二进制可执行文件,它就是可以直接烧入ROM、NOR Flash的文件。 ​ u-boot:ELF格式的可执行文件 ​ u-boot.srec:Motorola S-Record格式的可执行文件 对于S3C2410的开发板,执行“make smdk2410_config”、“make all”后生成的u-boot.bin可以烧入NOR Flash中运行。启动后可以看到串口输出一些信息后进入控制界面,等待用户的输入。 对于S3C2440的开发板,烧入上面生成的u-boot.bin,串口无输出,需要修改代码。 在修改代码之前,先看看上面两个命令“make smdk2410_config”、“make all”做了什么事情,以了解程序的流程,知道要修改哪些文件。 另外,编译U-Boot成功后,还会在它的tools子目录下生成一些工具,比如mkimage等。将它们复制到/usr/local/bin目录下,以后就可以直接使用它们了,比如编译内核时,会使用mkimage来生成U-Boot格式的内核映像文件uImage。 [编辑] 2. U-Boot的配置过程 在顶层Makefile中可以看到如下代码: SRCTREE:= $(CURDIR) …… MKCONFIG:= $(SRCTREE)/mkconfig …… smdk2410_config:unconfig @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0 假定我们在u-boot-1.1.6的根目录下编译,则其中的MKCONFIG就是根目录下的mkconfig文件。$(@:_config=)的结果就是将“smdk2410_config”中的“_config”去掉,结果为“smdk2410”。所以“make smdk2410_config”实际上就是执行如下命令: ./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0 再来看看mkconfig的作用,在mkconfig文件开头第6行给出了它的用法: 06 # Parameters: Target Architecture CPU Board [VENDOR] [SOC] 这里解释一下概念,对于S3C2410、S3C2440,它们被称为SoC(System on Chip),上面除CPU外,还集成了包括UART、USB控制器、NAND Flash控制器等等设备(称为片内外设)。S3C2410/S3C2440中的CPU为arm920t。 以下,分步骤分析mkconfig的作用: (1)确定开发板名称BOARD_NAME。 11 APPEND=no# Default: Create new config file 12 BOARD_NAME=""# Name to print in make output 13 14 while [ $# -gt 0 ] ; do 15 case "$1" in 16 --) shift ; break ;; 17 -a) shift ; APPEND=yes ;; 18 -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;; 19 *) break ;; 20 esac 21 done 22 23 [ "${BOARD_NAME}" ] || BOARD_NAME="$1" 对于“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令,其中没有“--”、“-a”、“-n”等符号,所以第14~22行没做任何事情。第11、12行两个变量仍维持原来的值。 执行完第23行后,BOARD_NAME的值等于第1个参数,即“smdk2410”。 (2)创建到平台/开发板相关的头文件的链接。 略过mkconfig文件中的一些没有起作用的行: 30 # 31 # Create link to architecture specific headers 32 # 33 if [ "$SRCTREE" != "$OBJTREE" ] ; then …… 45 else 46 cd ./include 47 rm -f asm 48 ln -s asm-$2 asm 49 fi 50 第33行判断源代码目录和目标文件目录是否一样,可以选择在其他目录下编译U-Boot,这可以令源代码目录保持干净,可以同时使用不同的配置进行编译。不过本书直接在源代码目录下编译的,第33行的条件不满足,将执行else分支的代码。 第46~48行进入include目录,删除asm文件(这是上一次配置时建立的链接文件),然后再次建立asm文件,并令它链接向asm-$2目录,即asm-arm。 继续往下看代码: 51 rm -f asm-$2/arch 52 53 if [ -z "$6" -o "$6" = "NULL" ] ; then 54 ln -s ${LNPREFIX}arch-$3 asm-$2/arch 55 else 56 ln -s ${LNPREFIX}arch-$6 asm-$2/arch 57 fi 58 59 if [ "$2" = "arm" ] ; then 60 rm -f asm-$2/proc 61 ln -s ${LNPREFIX}proc-armv asm-$2/proc 62 fi 63 第51行删除asm-$2/arch目录,即asm-arm/arch。 对于“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令,$6为“s3c24x0”,不为空,也不是“NULL”,所以第53行的条件不满足,将执行else分支。 第56行中,LNPREFIX为空,所以这个命令实际上就是:ln -s arch-$6 asm-$2/arch,即:ln -s arch-s3c24x0 asm-arm/arch。 第60、61行重新建立asm-arm/proc文件,并让它链接向proc-armv目录。 (3)创建顶层Makefile包含的文件include/config.mk。 64 # 65 # Create include file for Make 66 # 67 echo "ARCH = $2" > config.mk 68 echo "CPU = $3" >> config.mk 69 echo "BOARD = $4" >> config.mk 70 71 [ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk 72 73 [ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk 74 对于“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令,上面几行代码创建的config.mk文件内容如下: ARCH = arm CPU = arm920t BOARD = smdk2410 SOC = s3c24x0 (4)创建开发板相关的头文件include/config.h。 75 # 76 # Create board specific header file 77 # 78 if [ "$APPEND" = "yes" ]# Append to existing config file 79 then 80 echo >> config.h 81 else 82 > config.h# Create new config file 83 fi 84 echo "/* Automatically generated - do not edit */" >>config.h 85 echo "#include " >>config.h 86 前面说过,APPEND维持原值“no”,所以config.h被重新建立,它的内容如下: /* Automatically generated - do not edit */ #include " 现在总结一下,配置命令“make smdk2410_config”,实际的作用就是执行“./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0”命令。假设执行“./mkconfig $1 $2 $3 $4 $5 $6”命令,则将产生如下结果: (1)开发板名称BOARD_NAME等于$1; (2)创建到平台/开发板相关的头文件的链接: ln -s asm-$2 asm ln -s arch-$6 asm-$2/arch ln -s proc-armv asm-$2/proc# 如果$2不是arm的话,此行没有 (3) 创建顶层Makefile包含的文件include/config.mk。 ARCH = $2 CPU = $3 BOARD = $4 VENDOR = $5# $5为空,或者是NULL的话,此行没有 SOC = $6# $6为空,或者是NULL的话,此行没有 (4)创建开发板相关的头文件include/config.h。 /* Automatically generated - do not edit */ #include " 从这4个结果可以知道,如果要在board目录下新建一个开发板的目录,则在include/config目录下也要建立一个文件.h,里面存放的就是开发板的配置信息。 U-Boot还没有类似Linux一样的可视化配置界面(比如使用make menuconfig来配置),要手动修改配置文件include/config/.h来裁减、设置U-Boot。 配置文件中有两类宏: (1)一类是选项(Options),前缀为“CONFIG_”,它们用于选择CPU、SOC、开发板类型,设置系统时钟、选择设备驱动等。比如: #define CONFIG_ARM920T1/* This is an ARM920T Core*/ #defineCONFIG_S3C24101/* in a SAMSUNG S3C2410 SoC */ #define CONFIG_SMDK24101/* on a SAMSUNG SMDK2410 Board */ #define CONFIG_SYS_CLK_FREQ12000000/* the SMDK2410 has 12MHz input clock */ #define CONFIG_DRIVER_CS89001/* we have a CS8900 on-board */ (2)另一类是参数(Setting),前缀为“CFG_”,它们用于设置malloc缓冲池的大小、U-Boot的提示符、U-Boot下载文件时的默认加载地址、Flash的起始地址等。比如: #define CFG_MALLOC_LEN(CFG_ENV_SIZE + 128*1024) #defineCFG_PROMPT"100ASK> "/* Monitor Command Prompt*/ #defineCFG_LOAD_ADDR0x33000000/* default load address*/ #define PHYS_FLASH_10x00000000 /* Flash Bank #1 */ 从下面的编译、连接过程可知,U-Boot中几乎每个文件都被编译和连接,但是这些文件是否包含有效的代码,则由宏开关来设置。比如对于网卡驱动drivers/cs8900.c,它的格式为: #include /* 将包含配置文件include/config/.h */ …… #ifdef CONFIG_DRIVER_CS8900 /* 实际的代码 */ …… #endif/* CONFIG_DRIVER_CS8900 */ 如果定义了宏CONFIG_DRIVER_CS8900,则文件中包含有效的代码;否则,文件被注释为空。 可以这样粗糙地认为,“CONFIG_”除了设置一些参数外,主要用来设置U-Boot的功能、选择使用文件中的哪一部分;而“CFG_”用来设置更细节的参数。 [编辑] 3. U-Boot的编译、连接过程 配置完后,执行“make all”即可编译,从Makefile中可以了解U-Boot使用了哪些文件、哪个文件首先执行、可执行文件占用内存的情况。 先确定用到哪些文件,下面只摘取Makefile中与arm相关的部分: 117 include $(OBJTREE)/include/config.mk 118 exportARCH CPU BOARD VENDOR SOC 119 …… 127 ifeq ($(ARCH),arm) 128 CROSS_COMPILE = arm-linux- 129 endif …… 163 # load other configuration 164 include $(TOPDIR)/config.mk 165 第117、164行用于包含其他的config.mk文件,第117行所要包含文件的就是在上面的配置过程中制作出来的include/config.mk文件,其中定义了ARCH、CPU、BOARD、SOC等4个变量的值为arm、arm920t、smdk2410、s3c24x0。 第164行包含顶层目录的config.mk文件,它根据上面4个变量的值确定了编译器、编译选项等。其中对我们理解编译过程有帮助的是BOARDDIR、LDFLAGS的值,config.mk中: 88 BOARDDIR = $(BOARD) …… 91 sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk# include board specific rules …… 143 LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds …… 189 LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS) 在board/smdk2410/config.mk中,定义了“TEXT_BASE = 0x33F80000”。所以,最终结果如下:BOARDDIR为smdk2410;LDFLAGS中有“-T board/smdk2410/u-boot.lds -Ttext 0x33F80000”字样。 继续往下看Makefile: 166 ######################################################################### 167 # U-Boot objects....order is important (i.e. start must be first) 168 169 OBJS = cpu/$(CPU)/start.o …… 193 LIBS = lib_generic/libgeneric.a 194 LIBS += board/$(BOARDDIR)/lib$(BOARD).a 195 LIBS += cpu/$(CPU)/lib$(CPU).a …… 199 LIBS += lib_$(ARCH)/lib$(ARCH).a 200 LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \ 201 fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a 202 LIBS += net/libnet.a …… 212 LIBS += $(BOARDLIBS) 213 …… 从第169行得知,OBJS的第一个值为“cpu/$(CPU)/start.o”,即“cpu/arm920t/start.o”。 第193~213行指定了LIBS变量就是平台/开发板相关的各个目录、通用目录下相应的库,比如:lib_generic/libgeneric.a、board/smdk2410/libsmdk2410.a、cpu/arm920t/libarm920t.a、lib_arm/libarm.a、fs/cramfs/libcramfs.a fs/fat/libfat.a等。 OBJS、LIBS所代表的.o、.a文件就是U-Boot的构成,它们通过如下命令由相应的源文件(或相应子目录下的文件)编译得到。 268 $(OBJS): 269 $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@)) 270 271 $(LIBS): 272 $(MAKE) -C $(dir $(subst $(obj),,$@)) 273 274 $(SUBDIRS): 275 $(MAKE) -C $@ all 276 第268、269两行的规则表示,对于OBJS中的每个成员,都将进入cpu/$(CPU)目录(即cpu/arm920t)编译它们。现在OBJS为cpu/arm920t/start.o,它将由cpu/arm920t/start.S编译得到。 第271、272两行的规则表示,对于LIBS中的每个成员,都将进入相应的子目录执行“make”命令。这些子目录中的Makefile,结构相似,它们将Makefle中指定的文件编译、连接成一个库文件。 当所有的OBJS、LIBS所表示的.o和.a文件都生成后,就剩最后的连接了,这对应Makefile中如下几行: 246 $(obj)u-boot.srec:$(obj)u-boot 247 $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@ 248 249 $(obj)u-boot.bin:$(obj)u-boot 250 $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ 251 …… 262 $(obj)u-boot:depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT) 263 UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\ 264 cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \ 265 --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \ 266 -Map u-boot.map -o u-boot 267 先使用第262~266的规则连接得到ELF格式的u-boot,最后转换为二进制格式u-boot.bin、S-Record格式u-boot.srec。LDFLAGS确定了连接方式,其中的“-T board/smdk2410/u-boot.lds -Ttext 0x33F80000”字样指定了程序的布局、地址。board/smdk2410/u-boot.lds文件如下: 28 SECTIONS 29 { 30 . = 0x00000000; 31 32 . = ALIGN(4); 33 .text : 34 { 35 cpu/arm920t/start.o(.text) 36 *(.text) 37 } 38 39 . = ALIGN(4); 40 .rodata : { *(.rodata) } 41 42 . = ALIGN(4); 43 .data : { *(.data) } 44 45 . = ALIGN(4); 46 .got : { *(.got) } 47 48 . = .; 49 __u_boot_cmd_start = .; 50 .u_boot_cmd : { *(.u_boot_cmd) } 51 __u_boot_cmd_end = .; 52 53 . = ALIGN(4); 54 __bss_start = .; 55 .bss : { *(.bss) } 56 _end = .; 57 } 从第35行可知,cpu/arm920t/start.o被放在程序的最前面,所以U-Boot的入口点在cpu/arm920t/start.S中。 现在来总结一下U-Boot的编译流程: (1)首先编译cpu/$(CPU)/start.S,对于不同的CPU,还可能编译cpu/$(CPU)下的其他文件。 (2)然后,对于平台/开发板相关的每个目录、每个通用目录,都使用它们各自的Makefile生成相应的库。 (3)将1、2步骤生成的.o、.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码段起始地址、board/$(BOARDDIR)/u-boot.lds连接脚本进行连接。 (4)第3步得到的是ELF格式的U-Boot,后面Makefile还会将它转换为二进制格式、S-Record格式。 [编辑] 15.2.4 U-Boot的启动过程源码分析 首先强调,本书使用的U-Boot从NOR Flash启动,下面以开发板smdk2410的U-Boot为例。 U-Boot属于两阶段的Bootloader,第一阶段的文件为cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S,前者是平台相关,后者是开发板相关。 [编辑] U-Boot第一阶段代码分析 它与15.1.2节中描述的Bootloader第一阶段所完成的功能可以一一对应: (1)硬件设备初始化。 依次完成如下设置:将CPU的工作模式设为管理模式(svc),关闭WATCHDOG,设置FCLK、HCLK、PCLK的比例(即设置CLKDIVN寄存器),关闭MMU、CACHE。 代码都在cpu/arm920t/start.S中,注释也比较完善,读者有不明白的地方可以参考前面硬件实验的相关章节。 (2)为加载Bootloader的第二阶段代码准备RAM空间。 所谓准备RAM空间,就是初始化内存芯片,使它可用。对于S3C2410/S3C2440,通过在start.S中调用lowlevel_init函数来设置存储控制器,使得外接的SDRAM可用。代码在board/smdk2410/lowlevel_init.S中。 注意:lowlevel_init.S文件是开发板相关的,这表示如果外接的设备不一样,可以修改lowlevel_init.S文件中的相关宏。 lowlevel_init函数并不复杂,只是要注意这时的代码、数据都只保存在NOR Flash上,内存中还没有,所以读取数据时要变换地址。代码如下: 129 _TEXT_BASE: 130 .wordTEXT_BASE 131 132 .globl lowlevel_init 133 lowlevel_init: 134 /* memory control configuration */ 135 /* make r0 relative the current location so that it */ 136 /* reads SMRDATA out of FLASH rather than memory ! */ 137 ldr r0, =SMRDATA 138 ldrr1, _TEXT_BASE 139 subr0, r0, r1 140 ldrr1, =BWSCON/* Bus Width Status Controller */ 141 add r2, r0, #13*4 142 0: 143 ldr r3, [r0], #4 144 str r3, [r1], #4 145 cmp r2, r0 146 bne 0b 147 148 /* every
本文档为【逐行分析u-boot】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_545359
暂无简介~
格式:doc
大小:356KB
软件:Word
页数:76
分类:互联网
上传时间:2011-09-17
浏览量:182