uboot启动代码详解.docx

上传人:牧羊曲112 文档编号:4925588 上传时间:2023-05-23 格式:DOCX 页数:51 大小:173.59KB
返回 下载 相关 举报
uboot启动代码详解.docx_第1页
第1页 / 共51页
uboot启动代码详解.docx_第2页
第2页 / 共51页
uboot启动代码详解.docx_第3页
第3页 / 共51页
uboot启动代码详解.docx_第4页
第4页 / 共51页
uboot启动代码详解.docx_第5页
第5页 / 共51页
亲,该文档总共51页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《uboot启动代码详解.docx》由会员分享,可在线阅读,更多相关《uboot启动代码详解.docx(51页珍藏版)》请在三一办公上搜索。

1、 1引言在专用的嵌入式板子运行GNU/Linux系统已经变得越来越流行。一个嵌入式Linux系 统从软件的角度看通常可以分为四个层次:1. 引导加载程序。固化在固件(firmware)中的boot代码,也就是Boot Loader,它的启 动通常分为两个阶段。2. Linux内核。特定于嵌入式板子的定制内核以及内核的启动参数。3. 文件系统。包括根文件系统和建立于Flash内存设备之上文件系统,root fs。4. 用户应用程序。特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包 括一个嵌入式图形用户界面。常用的嵌入式GUI有:MicroWindows和MiniGUI等。引导加载程

2、序是系统加电后运行的第一段软件代码。回忆一下PC的体系结构我们可以 知道,PC机中的引导加载程序由BIOS(其本质就是一段固件程序)和位于硬盘MBR中的OS Boot Loader (比如,LILO和GRUB等)一起组成。BIOS在完成硬件检测和资源分配后, 将硬盘MBR中的Boot Loader读到系统的RAM中,然后将控制权交给OS Boot Loader。 Boot Loader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入 口点去运行,也即开始启动操作系统。而在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会 内嵌一段短小的启动程序),因

3、此整个系统的加载启动任务就完全由Boot Loader来完成。 比如在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址 0x00000000处开始执行,而在这个地址处安排的通常就是系统的Boot Loader程序。 2 bootloader 简介应用程序文件系统操作系统内核| BootLoader |简单地说,Boot Loader (引导加载程序)就是在操作系统内核运行之前运行的一段小 程序,它的作用就是加载操作系统,它是系统加电后运行的第一段软件代码。通过这段代码 实现硬件的初始化,建立内存空间的映射图,为操作系统内核准备好硬件环境并引导内核的 启动。如上图

4、所示的那样在设备的启动过程中bootloader位于最底层,首先被运行来引导 操作系统运行,很容易可以看出bootloader是底层程序所以它的实现严重地依赖于硬件, 特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的BootLoader几乎是不可能的。 尽管如此,一些功能强大、支持硬件环境较多的BootLoader也被广大的使用者和爱好者所 支持,从而形成了一些被广泛认可的、较为通用的的bootloader实现。2.1 Boot Loader所支持的CPU和嵌入式板每种不同的CPU体系结构都有不同的Boot Loader。有些Boot Loader也支持多种体 系结构的CPU,比如U-B

5、oot就同时支持ARM体系结构和MIPS体系结构。除了依赖于CPU 的体系结构外,Boot Loader实际上也依赖于具体的嵌入式板级设备的配置。这也就是说, 对于两块不同的嵌入式板而言,即使它们是基于同一种CPU而构建的,要想让运行在一块 板子上的Boot Loader程序也能运行在另一块板子上,通常也都需要修改Boot Loader的 源程序。2.2 Boot Loader 的安装媒介(Installation Medium)系统加电或复位后,所有的CPU通常都从某个由CPU制造商预先安排的地址上取指 令。比如,基于ARM7TDMI core的CPU在复位时通常都从地址0x00000000

6、取它的第一条 指令。而基于CPU构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或FLASH等)被映射到这个预先安排的地址上。因此在系统加电后,CPU将首先执行Boot Loader 程序。下图1就是一个同时装有Boot Loader、内核的启动参数、内核映像和根文件系统映像 的固态存储设备的典型空间分配结构图:图1固态存储设备的典型空间分配结构KernelHoM filespt ent2.3 Boot Loader 的启动过程:单阶段(Single Stage) /多阶段(Multi-Stage)通常多阶段的Boot Loader能提供更为复杂的功能,以及更好的可

7、移植性。从固态存 储设备上启动的Boot Loader大多都是2阶段的启动过程,也即启动过程可以分为stage 1和stage 2两部分。而至于在stage 1和stage 2具体完成哪些任务将在下面讨论。2.4 Boot Loader 的操作模式(Operation Mode)大多数Boot Loader都包含两种不同的操作模式:启动加载模式和下载模式,这 种区别仅对于开发人员才有意义。但从最终用户的角度看,Boot Loader的作用就是用来加 载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。启动加载(Boot loading)模式:这种模式也称为自主(Autonomous)

8、模式。也即Boot Loader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有 用户的介入。这种模式是Boot Loader的正常工作模式,因此在嵌入式产品发布的时侯, Boot Loader显然必须工作在这种模式下。下载(Downloading)模式:在这种模式下,目标机上的Boot Loader将通过串口连接或 网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。 从主机下载的文件通常首先被Boot Loader保存到目标机的RAM中,然后再被Boot Loader写到目标机上的FLASH类固态存储设备中。Boot Loader的

9、这种模式通常在第一次 安装内核与根文件系统时被使用;此外,以后的系统更新也会使用Boot Loader的这种工 作模式。工作于这种模式下的Boot Loader通常都会向它的终端用户提供一个简单的菜单 界面或命令行接口来接收要执行的操作。像Blob或U-Boot等这样功能强大的Boot Loader通常同时支持这两种工作模式, 而且允许用户在这两种工作模式之间进行切换。比如,Blob在启动时处于正常的启动加载 模式,但是它会延时10秒等待终端用户按下任意键而将blob切换到下载模式。如果在10 秒内没有用户按键,则blob继续启动Linux内核。2.5 常见的 Boot LoaderU-BOO

10、T: U-Boot 是 Das U-Boot 的简称,其含义是 Universal Boot Loader,是遵循 GPL 条 款的开放源码项目。uboot是一个庞大的公开源码的软件。它支持一些系列的arm体系,包 含常见的外设的驱动,是一个功能强大的板极支持包。vivi: vivi是韩国mizi公司开发的bootloader,适用于ARM9处理器。Vivi也有两种工 作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自 行启动linux内核,这是vivi的默认模式。如果修改或更新需要进入下载模式,在下载模 式下,vivi为用户提供一个命令行接口通过接口可以使用vi

11、vi提供的一些命令,来实现flash的烧写、管理、操作mtd分区信息、启动系统等功能。2.6 U-BOOT的目录结构目录说明board和开发板相关的文件,每一个开发板都以一个子目录出现在当前目录 中,比如:smdk2410。该子目录中存放于开发板相关的配置文件,如 makefile和U-Boot.lds。其中包含SDRAM初始化代码、Flash底层驱 动、板级初始化文件。config.mk定义了 TEXT_BASE是代码在内存的真 实地址common与体系结构无关的文件,实现各种命令的C文件。该文件主要实现uboot 命令行下支持的命令,每 条命令都对应 个文件。例如bootm命令对 应就是

12、cmd_bootm.c。cpu与特定CPU架构相关目录,每一款uboot下支持的CPU在该目录下对应 一个子目录,比如arm920t。每个CPU子目录中都包括cpu.c和 interrupt.c、start.Scpu.c 初始化 CPU、设置指令 Cache 和数据 Cache 等interrupt.c设置系统的各种中断和异常start.S是U-boot启动时 执行的第一个文件,它主要做早期系统初始化,代码重定向和设置系统 堆栈diskDisk分区处理代码,对磁盘的支持doc文档目录,uboot有非常完整的文档driversUboot支持的设备驱动程序都放在该目录,例如各种网卡、支持CFI的F

13、lash、串 口、USB 等fs支持的文件系统,目前支持cramfs、fat、fdos、jffs2和registerfsinclude头文件,还有对各种硬件平台支持的汇编文件,系统配置文件和对文件 系统支持的文件等。该目录下的configs目录有与开发板相关的配置文 件,如smdk2410.h。该目录下的asm目录有与CPU体系结构相关的头 文件,比如arm对应的就是asm-arm。net与网络协议栈相关的代码,BOOTP协议、TFTP协议、RARP和NFS文件 系统的实现等lib_xxx与ARM体系结构相关的库文件。如与arm相关的库放在lib_arm中tools生成 uboot 的工具,如

14、 mkimage, crc 等等 3 Boot Loader的主要任务与典型结构框架从操作系统的角度看,Boot Loader的总目标就是正确地调用内核来执行。另外,由于 Boot Loader的实现依赖于CPU的体系结构,因此大多数Boot Loader都分为stage1和 stage2两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在 stage1中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2则通常用 C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。以u-boot为例,它启动过程的两个阶段(stage)如下:第一阶段(

15、stage 1) cpu/arm920t/start.S依赖于CPU体系结构的代码(如设备初始化代码等),一般用汇编语言来实现。主要进 行以下方面的设置:设置ARM进入SVC模式、禁止IRQ和FIQ、关闭看门狗、屏蔽所有中断。 设置时钟(FCLK,HCLK,PCLK)、清空I/D cache、清空TLB、禁止MMU和cache、配置内存控 制器、为搬运代码做准备、搬移uboot映像到RAM中(使用copy_loop实现)、分配堆栈、 清空bss段(使用clbss_l实现)。最后通过ldr pc, _start_armboot跳转到第二阶段。第二阶段(stage 2) lib_arm/board

16、.c该阶段主要都是用C语言来实现。start_armboot()进行一系列初始化(cpu,板卡,中 断,串口,控制台等),开启I/Dcache。初始化FLASH,根据系统配置执行其他初始化操作。 打印LOG,使能中断,获取环境变量,初始化网卡。最后进入main_loop()函数。综上所述,可简单的归纳两个阶段的功能如下:第一阶段的功能:硬件设备初始化加载U-Boot第二阶段代码到RAM空间设置好栈跳转到第二阶段代码入口第二阶段的功能:初始化本阶段使用的硬件设备检测系统内存映射将内核从Flash读取到RAM中为内核设置启动参数调用内核U-Boot启动第一阶段流程如下:硬件设备初始化复制第二阶段代

17、码到RAM空间设置堆栈指针清空BSS段跳转到第二阶段入口3.1 u-boot的stage1详细分析uboot的第一阶段设计的非常巧妙,几乎都是用汇编语言实现的。首先我们来看一下它的链接脚本(u-boot-1.1.6boardsmdk2410u-boot.lds),通过它 我们可以知道它整个程序的各个段是怎么存放的。它定义了整个程序编译之后的连接过程, 决定了一个可执行程序的各个段的存储位置。/*指定输出可执行文件是elf格式,32位ARM指令,小端*/OUTPUT_FORMAT(elf32-littlearm, elf32-littlearm, elf32-littlearm)/*指定输出可执

18、行文件的平台架构为ARM架构*/OUTPUT_ARCH(arm)/*指定输出可执行文件的起始代码段为_start */ENTRY(_start)SECTIONS.=0x00000000;/ 入口地址.=ALIGN(4);/四字节对齐.text :/代码段,上面3行标识是不占任何空间的cpu/arm920t/start.o(.text)/ 这里将start.o放在第一位就表示把start.s编译时放在最开始,也就是uboot启动是最先执行start.S*(.text)/所有的其他程序的代码段以四字节对齐放在它后面.=ALIGN(4); /前面的“.”表示当前值.rodata : *(.rodat

19、a) /只读数据段.=ALIGN(4);.data : *(.data) /指定读/写数据段.=ALIGN(4);.got : *(.got) /指定got段,got段式是uboot自定义的一个段,非标准段.=.;_u_boot_cmd_start = .;/把_u_boot_cmd_start 赋值为当前位置,即起始位置.u_boot_cmd : *(.u_boot_cmd) /指定 u_boot_cmd 段,uboot 把所有的 uboot 命令放在该段_u_boot_cmd_end = .;/把_u_boot_cmd_end赋值为当前位置,即结束位置.=ALIGN(4);_bss_sta

20、rt = .;/_bss_start赋值为当前位置,即bss段得开始位置.bss : *(.bss) _end = .;/把_end赋值为当前位置,即bss段得结束地址从上面这段代码我们可以看出uboot运行的第一个程序是cpu/arm920t/start.S里面的 第一个段_start。我们查看start.S的源码。3.1.1硬件设备初始化(1) 设置异常向量cpu/arm920t/start.S开头有如下的代码:/global用于声明一个符号可被其他文档引用,相当于声明了一个全局变量,.globl和.global相同。 /该部分为处理器的异常处理向量表。地址范围为0x00000000 0x

21、00000020,刚好8条指令。.globl_start/* u-boot 启动入口 */_start:b reset/* 复位 */ldrpc, _undefined_instruction/*未定义指令向量*/ldrpc, _software_interrupt/* 软件中断向量*/ldrpc, _prefetch_abort/*预取指令异常向量*/ldrpc, _data_abort/*数据操作异常向量*/ldrpc, _not_used/*未使用 */ldrpc, _irq/* irq中断向量*/ldrpc, fiq/* fiq中断向量*/* 中断向量表入口地址*/.word伪操作用于

22、分配一段字内存单元(分配的单元都是字对齐的),并用伪操作中的 expr初始化。.long和.int作用与之相同。_undefined_instruction:_software_interrupt:.word undefined_instruction.word software_interrupt_prefetch_abort:.word prefetch_abort_data_abort:.word data_abort_not_used:_irq:.word not_used.word irq_fiq:.word fiq.balignl 16,0xdeadbeef以上代码设置了 ARM异

23、常向量表,各个异常向量介绍如下:地址异常进入模式描述0x00000000复位管理模式复位电平有效时,产生复位异常,程序跳转 到复位处理程序处执行0x00000004未定义指令未定义模式遇到不能处理的指令时产生未定义指令异常0x00000008软件中断管理模式执行SWI指令产生,用于用户模式下的程序 调用特权操作指令0x0000000c预存指令中止模式处理器预取指令的地址不存在,或该地址不 允许当前指令访问,产生指令预取中止异常0x00000010数据操作中止模式处理器数据访问指令的地址不存在或该地址 不允许当前指令访问时,产生数据中止异常0x00000014未使用未使用未使用0x0000001

24、8IRQIRQ外部中断请求有效,且CPSR中的I位为。时, 产生IRQ异常0x0000001cFIQFIQ快速中断请求引脚有效,且CPSR中的F位为 0时,产生FIQ异常在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。当一个异常产生时,CPU 根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就 跳转到对应的异常处理程序执行。其中复位异常向量的指令“b reset”决定了 U-Boot启动后将自动跳转到标号reset 处执行。许多人都认为_start的值是0x00000000,为什么是这个地址呢?因为连接脚本上指定 了。真的是这样吗?我们

25、来看看我们编译好之后,在u-boot目录下有个System.map,这里 面有各个变量的值,其中会告诉你,_start的值为:0x33f80000。注意,这里有两个地址: 编译地址和运行地址。什么是编译地址?什么是运行地址?32位的处理器,它的每一条指令是4个字节,以4个字节存储顺序,进行顺序执行, CPU是顺序执行的,只要没发生什么跳转,它会顺序进行执行,编译器会对每一条指令分配 一个编译地址,这是编译器分配的,在编译过程中分配的地址,我们称之为编译地址。运行地址是指,程序指令真正运行的地址,是由用户指定的,用户将运行地址烧录到哪 里,哪里就是运行的地址。编译地址和运行地址如何来算呢?假如有

26、两个编译地址a=0x10, b=0x7,b的运行地址是0x300,那么a的运行地址就是b的运行地址加上两者编译地址的差值,a-b=0x10-0x7=0x3, a 的运行地址就是 0x300+0x3=0x303。(2) CPU进入SVC模式start_code:/* set the cpu to SVC32 mode*/mrs r0, cpsrbic r0, r0, #0x1f/*工作模式位清零*/orr r0, r0, #0xd3/*工作模式位设置为“10011” (管理模式),并将中断禁止位和快中断禁止位置1 */msr cpsr, r0以上代码将CPU的工作模式位设置为管理模式,并将中断禁

27、止位和快中断禁止位置一, 从而屏蔽了 IRQ和FIQ中断。(3)设置控制寄存器地址#if defined(CONFIG_S3C2400)#define pWTCON 0x15300000#define INTMSK 0x14400008#define CLKDIVN 0x14800014#else /* s3c2410与s3c2440下面4个寄存器地址相同*/#define pWTCON 0x53000000#define INTMSK 0x4A000008#define INTSUBMSK 0x4A00001C#define CLKDIVN 0x4C000014# endif/* WATCH

28、DOG控制寄存器地址 */* INTMSK寄存器地址 */* INTSUBMSK寄存器地址*/* CLKDIVN寄存器地址*/对于 s3c2440 开发板,以上代码完成了 WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN 四个 寄存器的地址的设置。(4)关闭看门狗ldr r0, =pWTCONmov r1, #0x0str r1, r0/*看门狗控制器的最低位为0时,看门狗不输出复位信号*/以上代码向看门狗控制寄存器写入0,关闭看门狗。为什么需要关闭看门狗呢?这里有 个喂狗的过程,所谓的喂狗是每隔一段时间给某个寄存器置位而已,在实际中会专门启动一 个线程或进程会专门喂狗,当上

29、层软件出现故障时就会停止喂狗,停止喂狗之后,cpu会自 动复位,一般都在外部专门有一个看门狗,做一个外部的电路,不在cpu内部使用看门狗, 否则在U-Boot启动过程中,CPU将不断重启。(5) 屏蔽中断/* mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffff /*某位被置1则对应的中断被屏蔽*/ldr r0, =INTMSKstr r1, r0INTMSK是主中断屏蔽寄存器,每一位对应SRCPND (中断源引脚寄存器)中的一位,表 明SRCPND相应位代表的中断请求是否被CPU所处理。IN

30、TMSK寄存器是一个32位的寄存器, 每位对应一个中断,向其中写入0xffffffff就将INTMSK寄存器全部位置1,从而屏蔽对应 的中断。为什么要关闭中断呢?中断处理(ldrpc.)是将代码的编译地址放在了指针上, 而这段时间内还没有搬移代码,所以不能进行跳转。#if defined(CONFIG_S3C2440)ldr r1, =0x7fffldr r0, =INTSUBMSKstr r1, r0#endifINTSUBMSK每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是 否被CPU所处理。INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低1

31、5位。向其中 写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置1,从而屏蔽对应的中断。(6)设置 MPLLCON,UPLLCON, CLKDIVN#if defined(CONFIG_S3C2440)#define MPLLCON0x4C000004#define UPLLCON0x4C000008ldr r0, =CLKDIVNmov r1, #5str r1, r0ldr r0, =MPLLCONldr r1, =0x7F021str r1, r0ldr r0, =UPLLCONldr r1, =0x38022str r1, r0#else/* FCLK:HCLK:P

32、CLK = 1:2:4 */* default FCLK is 120 MHz ! */ldr r0, =CLKDIVNmov r1, #3str r1, r0#endifCPU上电几毫秒后,晶振输出稳定,FCLK=Fin (晶振频率),CPU开始执行指令。但实际 上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。这就需要设置CLKDIVN, MPLLCON,UPLLCON 这 3 个寄存器。CLKDIVN寄存器用于设置FCLK,HCLK,PCLK三者间的比例如下:CLKDIVN位说明初始值HDIVN2:100 : HCLK = FCLK/1.01 : HCLK = FCL

33、K/2.0010 : HCLK = FCLK/4 (当 CAMDIVN9 = 0 时)HCLK= FCLK/8(当 CAMDIVN9 = 1 时)11 : HCLK = FCLK/3 (当 CAMDIVN8 = 0 时)HCLK = FCLK/6(当 CAMDIVN8 = 1 时)PDIVN00: PCLK = HCLK/11: PCLK = HCLK/20设置CLKDIVN为5,就将HDIVN设置为二进制的10,由于CAMDIVN9没有被改变过,取默认值0,因此HCLK = FCLK/4。PDIVN被设置为1,因此PCLK= HCLK/2。因此分频比 FCLK:HCLK:PCLK = 1:4

34、:8 。MPLLCON寄存器用于设置FCLK与Fin的倍数。MPLLCON的位19:12称为MDIV,位9:4 称为PDIV,位1:0称为SDIV。对于S3C2440,FCLK与Fin的关系如下面公式:MPLL Control RegisterMpll = (2 * m * Fin)/ (p * 2s)m = (MDIV + 8). p = (PDIV + 2), s = SDIVMPLLCON与UPLLCON通常设置如下:输入频率输出频率MDIVPDIVSDIV12.0000MHz48.00 MHz56(0x38)2212.0000MHz405.00 MHz127(0x7f)21当s3c24

35、40系统主频设置为405MHZ,USB时钟频率设置为48MHZ时,系统可以稳定运行,因此设置MPLLCON与UPLLCON为:MPLLCON=(0x7f12) | (0x024) | (0x01) = 0x7f021UPLLCON=(0x3812) | (0x024) | (0x02) = 0x38022(7) 关闭 MMU,cache接着往下看:#ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_crit#endifcpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动 则应该注释掉这段代码。下面分析一下cp

36、u_init_crit到底做了什么:#ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_init_crit:/*使数据cache与指令cache无效*/*/mov r0, #0mcr p15, 0, r0, c7, c7, 0/* 向 c7 写入 0 将使 ICache 与 DCache 无效*/mcr p15, 0, r0, c8, c7, 0/* 向 c8 写入 0 将使 TLB 失效 */* disable MMU stuff and caches*/mrc p15, 0,r0, c1, c0,0/* 读出控制寄存器到r0中 */bic r0, r0,#0x0000

37、2300clear bits13, 9:8 (-VRS)bic r0, r0, #0x00000087 clear bits 7, 2:0 (BCAM)orr r0, r0,#0x00000002set bit 2(A) Alignorr r0, r0,#0x00001000set bit 12(I) I-Cachemcr p15, 0,r0, c1, c0,0/* 保存r0 到控制寄存器 */* before relocating, we have to setup RAM timing* because memory timing is board-dependend, you will*

38、 find a lowlevel_init.S in your board directory.*/mov ip, lrbl lowlevel_initmov lr, ipmov pc, lr#endif /* CONFIG_SKIP_LOWLEVEL_INIT */代码中的c0, c1, c7, c8都是ARM920T的协处理器CP15的寄存器。其中c7是cache 控制寄存器,c8是TLB控制寄存器。将0写入c7、c8,使Cache,TLB内容无效。通过修改CP15的c1寄存器来关闭了 MMU。为什么要关闭catch和MMU呢? catch和MMU是做什么用的?catch是cpu内部的一个

39、2级缓存,她的作用是将常用的数据和指令放在cpu内部,MMU 是用来做虚实地址转换用的,我们的目的是设置控制的寄存器,寄存器都是实地址,如果既 要开启MMU又要做虚实地址转换的话,中间还多一步,先要把实地址转换成虚地址,然后再 做设置,但对uboot而言就是起到一个简单的初始化的作用和引导操作系统,如果开启MMU 的话,很麻烦,也没必要,所以关闭MMU.说到catch就必须提到一个关键字Volatile,以后在设置寄存器时会经常遇到,他的本 质是告诉编译器不要对我的代码进行优化,优化的过程是将常用的代码取出来放到catch 中,它没有从实际的物理地址去取,它直接从cpu的缓存中去取,但常用的代

40、码就是为了感 知一些常用变量的变化,所以在这种情况下要用Volatile关键字告诉编译器不要做优化, 每次从实际的物理地址中去取指令。(8) 初始化RAM控制寄存器其中的bl lowlevel_init用于初始化各个bank,完成了内存初始化的工作,由于内存 初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board下面相应的目录中。 对于 s3c2440,lowlevel_init 在 board/smdk2440/lowlevel_init.S 中定义如下: #define BWSCON 0x48000000/* 13个存储控制器的开始地址*/_TEXT_BASE:.w

41、ord TEXT_BASE.globl lowlevel_initlowlevel_init:/* memory control configuration */* make r0 relative the current location so that it */* reads SMRDATA out of FLASH rather than memory ! */ldrr0, =SMRDATA _TEXT_BASE r0, r1/*=BWSCON /* Bus r2, r0, #13*4SMRDATA减_TEXT_BASE就是13个寄存器的偏移地址*/Width Status Contr

42、oller */ldr r1, sub r0, ldr r1, add0:ldrr3, r0, #4/*将13个寄存器的值逐一赋值给对应的寄存器*/strr3,r1,#4cmpr2,r0bne0b/* everything is fine now */mov pc, lr.ltorg/* the literal pools origin */SMRDATA:.word.word/*下面是13个寄存器的值*/lowlevel_init的作用就是将SMRDATA开始的13个值复制给开始地址BWSCON的13个 寄存器,从而完成了存储控制器的设置。(9) 复制U-Boot第二阶段代码到RAMrelo

43、cate:adr r0, _start/* r0 - current position of code */ldr r1, _TEXT_BASE/* test if we run from flash or RAM */*判断U-Boot是否是下载到RAM中运行,若是,则不用再复制到RAM中了,这种情况通常 在调试U-Boot时才发生*/cmp r0, r1 /*_start等于_TEXT_BASE说明是下载到RAM中运行*/beq stack_setupldr r2,_armboot_startldr r3,_bss_startsub r2,r3, r2/*r2-sizeofarmboot*

44、/add r2,r0, r2/*r2-sourceendaddress */*搬运U-Boot自身到RAM中*/copy_loop:ldmia r0!,r3-r10/*从地址为r0的NOR Flash中读入8个字的数据*/stmia r1!,r3-r10/*将r3至r10寄存器的数据复制给地址为r1的内存*/cmp r0, r2/* until source end addreee r2 */ble copy_loop终于到重点部分了 :代码重定向(拷贝stage2到RAM中),拷贝时要确定两点:(1) stage2的可执行映象在固态存储设备的存放起始地址和终止地址;(2) RAM空间的起始地

45、址。下面我们来看看它到底是怎么重定向的:adr r0,_startadr伪指令,汇编器会将执行到_start时PC的值放到r0中。所以此时r0中保存的不 是编译地址,而是运行地址。假如U-boot是从RAM开始运行,则从adr,r0,_start得到 的地址信息为 r0=_start=_TEXT_BASE=TEXT_BASE=0x33f80000;假如 U-boot 从 Flash 开始 运行,即从处理器对应的地址运行,则r0=0x00000000。而这里r0=0。ldr r1,_TEXT_BASE这条汇编指令的意思是把_TEXT_BASE的值作为地址,把这个地址的内容赋给r1,从下面 可以知

46、道:_TEXT_BASE:. word TEXTBASE|_TEXT_BASE 里面存储的内容是 TEXT_BASE,我们通过查看 board/smdk2410/config.mk 发现TEXT_BASE的值为0x33f80000,所以r1的值就是0x33f80000cmp r0,r1将r0和r1做比较,此时r0 = 0x00000000,r1 = 0x33f80000,显然不相等,那么执行 的就是下面的汇编指令:ldr r2, _armboot_start.globl a nnbootstart armbootstart:.word start由此可以知道r2的值是_start,通过ldr将标号的编译地址放到r2中,也就是 0x33f80000,即代码段的起始地址。ldr r3, _bss_start/* These are defined. in the b

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号