【转载】https://blog.csdn.net/kernel_yx/article/details/53045424

最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上。正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果。之前在学习uboot时,在网上看了很多文章,很多都是基于老版本的的uboot,并且很多都是直接从代码开始分析,并没有将uboot与ARM处理器体系结构结合起来。毕竟很多时候做一件事情,你知道怎么去做这件事和你知道这件事为什么要这么去做两个不同的概念。即英文中常说的:how do?和 why do?

将2016年7月发布的新版本uboot移植到单位的板子上并进行了调试,在此也将我的学习成果分享给大家。文中有很多地方加入了我自己的理解,可能不太准确,希望大家提出修改意见。

开始深入了解处理器启动流程前,请准备好两个工具:

1.uboot-2016-7源码包

(开源的随便下载  官方下载地址:ftp://ftp.denx.de/pub/u-boot/    下载u-boot-2016.07.tar.bz2)

2.代码阅读软件source insight

(推荐! 可以想象uboot源码包有10000多个文件,每个文件都有几百行甚至上千行代码。没有专业的代码阅读器如何查找函数原型。 当然不是全都需要看的!  根据自己的需要去查看)

同时下载好这几个技术文档

(笔者在学习uboot时阅读的几个文档  可以根据需求去查找阅读  完全没必要重头读到尾)

1.arm公司官方提供的ARMv7-A体系结构文档

《ARM® Architecture Reference Manual ARM®v7-A and ARM®v7-R 》

( 官方 品质保证! 如果觉得英文看着费劲 可以去看  杜春雷  《ARM体系结构与编程》 )
2.《Uboot中start.S源码的指令级的详尽解析_v1.6.pdf》(好吧  中文滴)

(对理解gnu arm汇编极其有帮助)

3.《ARM指令集快速查询手册.pdf》(中文滴)

(工具手册,快速查找各种arm指令用法)

4.《ARM指令详解[ARM标准].pdf》(还是中文滴)

(arm汇编的一些规范和常用形式,很多例子非常有用:子程序调用、散转、数据块复制等。对写裸机程序很有帮助,同样对理解uboot和内核启动代码有很大帮助)

5.《程序员的自我修养—链接、装载与库.pdf》(全是中文滴)

(里面会介绍ELF文件  程序链接的过程  最重要的部分就是帮助理解uboot的核心重定位!这个真的很重要!!)

好了,现在进入正题。

什么是uboot?

在嵌入式操作系统中,Boot Loader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由Boot Loader来完成。   uboot就是bootloader的一种,全称Universal Boot Loader。

目标:用uboot来启动单位设计的ARMv7的处理器。

问题:如何启动? 启动流程是什么?

首先,我先来描述一下需要移植uboot的这块板子上使用到的存储器(处理器架构可以暂时先不考虑,后面我会根据arm公司官方提供的ARMv7-A体系结构文档去分析uboot初始化处理器的汇编代码。无论是arm9 arm11 armv7 armv8都是相同的套路,首先需要根据存储器资源,去定制自己的启动流程)。在我手上的这块板子上使用存储器资源:Norflash(16MB)  SRAM(512KB) SDRAM(512MB)。

看到这里应该想到两个问题:

1. Norflash  SRAM SDRAM 作为存储器它们之间有何异同?

2. 如何通过它们去启动arm处理器?(重要的事情说三遍:先一定要理解启动流程  然后再去看uboot源码  要不然看了也无法理解  嵌入式的代码都是与硬件息息相关  了解硬件结构再去读代码 事半功倍)

下面我将对以上问题依次做出解析:

一. Norflash  SRAM  SDRAM 异同

norflash:norfalsh是非易失存储器(失去电源供电后norflash里存储的数据依然存在),NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地读取其内部的每一个字节(注意是读取!对于flash不是随意可以写入,一般写入NOR flash流程是:解保护->擦除->写入数据。由于flash特性只能从1翻转到0,无法从0翻转到1。擦除过程就是将flash中的某一个扇区全写0xFFFFFFF,再写入数据),代码指令可以直接在norflash上运行。(重要! 上电后可以读取norfalsh中的数据 写操作无法直接进行 前面我已经说得很清楚了)

SRAM:静态随机访问存储器(Static Random Access Memory)它是一种类型的半导体存储器。“静态”是指只要不掉电,存储在SRAM中的数据就不会丢失。这一点与动态RAM(DRAM)不同,DRAM需要进行周期性的刷新操作。然后,我们不应将SRAM与只读存储器(ROM)和Flash Memory相混淆,因为SRAM是一种易失性存储器,它只有在电源保持连续供应的情况下才能够保持数据。“随机访问”是指存储器的内容可以以任何顺序访问,而不管前一次访问的是哪一个位置。 (重要!上电后就可以读写SRAM中的数据,无需初始化操作)

SDRAM:同步动态随机存取存储器(Synchronous Dynamic Random Access Memory)需要不断的刷新,才能保存数据。而且是行列地址复用的,许多都有页模式。(重要! 需要对ddr控制器进行初始化<配置寄存器>,才能去读写SDRAM中的数据)

最后用表格来归纳总结下:

存储器 上电后存储器访问状态 掉电后存储器中数据状态
NORFLASH 可以读取数据,无法直接写人 数据存在
SRAM 可以直接读取写入数据 数据不存在
SDRAM 上电后没有初始化DDR控制器
         无法进行数据读写
数据不存在
 

前面介绍了Norflash  SRAM  SDRAM存储特性,下面就来聊聊他们各自的功能和完成的任务(重要!!Norflash  SRAM  SDRAM在arm处理器启动阶段完成的任务,都是根据他们各自的存储特性来的。如果对于我前面介绍的Norflash  SRAM  SDRAM存储特性还不太理解的同学可以自行百度再深入理解。)

Norflash特性:掉电非易失(失去电源供电后norflash里存储的数据依然存在),可以直接<读取>数据,无法直接<写入>数据。

Norflash作为arm处理器程序存储器。可以试想一下,如果程序存储器掉电以后里面的数据没有了。那么你的电脑如何自启动,难道每次开机前都要重新烧写一次代码。在此处可以思考一个问题,在上电后norflash可以看作一个可以随机读取的只读存储器。但是我们运行的程序,一般情况下.text段(代码段)是只读(ok),.rodata(只读数据段)是只读(也ok)。那么问题来了,对于.data段(数据段)和.bss段(未初始化的全局变量和静态变量)在程序运行的过程中变量的值是需要改变的(改变一个变量的值在底层硬件完成操作<在相应的地址(变量在物理地址上存储地址)上写入数据>),很可惜Norflash只能直接读取无法直接进行写操作。(重要!  怎么解决这个问题?  这时就需要SRAM  因为SRAM上电后就可以直接去读写,下面我就解释下SRAM的功能和作用)

SRAM特性:掉电易失(失去电源供电后SRAM里存储的数据不存在了),可以随意<读写>数据。(重要!容量小:512KB  程序运行速度快  价格贵!)。

在实际运行时,SRAM可以作为c语言运行时的堆栈空间。把arm处理器的sp(堆栈指针寄存器)设置在sram中,为c语言运行提供环境。关于全局变量的问题,我单独提一下,uboot在重定位前(将uboot镜像从flash搬运到ddr中继续运行前),无论是汇编还是c程序中没有定义全局变量。只是定义了一个结构体指针gd_t *gd_ptr用于存储uboot镜像的长度,重定位地址等信息,将gd_ptr的地址存储在r9中,r9中存储的地址值为sram顶端减去一个sizeof(gd_t )。(存储在sram里就可以随意读写了嘛    后面分析uboot代码时我会详细讲解)

SDRAM特性:掉电易失(失去电源供电后SDRAM里存储的数据不存在了),上电后没有初始化DDR控制器,无法进行数据读写。(重要!容量大:512MB  程序运行速度快  价格自己猜一下<必须价格便宜>)

既然需要使用大容量的SDRAM,必须配置ddr时钟和ddr控制器的寄存器。这一步在哪完成呢?(思考一下)

没错就是在norflash和SRAM搭建的程序运行环境中完成。完成什么呢?

1.完成对处理器时钟的初始化

2. DDR的初始化

3.给gd_t *gd_ptr赋值 (用于存储uboot镜像的长度,重定位地址,重定位偏移量等信息)

在uboot搬运到DDR中运行前进行最小系统的初始化,之后就将uboot搬运到ddr中进行运行。

(重要!此时Norfalsh和SRAM的任务就完成了(这俩就没用了),现在uboot就在ddr中运行了)

二. arm处理器启动流程分析

前面讲解了各个存储器在处理器启动阶段的作用,下面就归纳总结一下启动过程。

有时候描述说不清楚,直接上图。下图为手上这块ARM处理器的存储器地址空间(包括各存储器在arm处理器上的地址空间 以及容量大小)

那么启动流程可以总结为:

step1.uboot镜像(也就是bin文件 重要!不是elf文件  bin文件 和 elf文件异同 自行百度)存储在norflash中,上电后ARM默认从地址0x00000000处取出第一条指令。当运行C程时,形参和局部变量需要入栈,栈顶设置SRAM的顶端,为C程序提供运行环境。完成对gd_t *gd_ptr赋值(存储uboot镜像的长度,重定位地址等信息)。

step2.在第一阶段会完成arm处理器的时钟和DDR的配置以及对gd_t *gd_ptr赋值(用于存储uboot镜像的长度,重定位地址等信息)。此时SDRAM就可以正常进行数据读写。将uboot镜像复制到SDRAM中(重定位地址由gd_t *gd_ptr提供),根据重定位偏移量得到新的PC指针值,在DDR中继续运行uboot。

这里我想多提一下,uboot在ddr中运行后的地址跟其链接地址不一致(原来norflash中text_base = 0x0  在ddr中运行后text_base = 0x70453670 ),编译器会在链接时确定了其中变量以及函数的绝对地址,链接地址 加载地址 运行地址应该一致的(只是写裸机程序时大多数情况下)。

(1)如何对函数进行寻址调用(执行函数的过程就是跳转到某个地址上去执行一段代码)

(2)如何对全局变量进行寻址操作(全局变量的地址变了如何找到进行读写)

(3)对于全局指针变量中存储的其他变量或函数地址在重定位到DDR中之后如何操作?

问题先放在这  后面我会专门写一篇博客   将一个可执行程序静态链接过程  动态链接过程  与uboot重定位过程放在一起比较  感兴趣的可以直接去阅读这篇博客。

step3.在DDR中继续运行的uboot完成对ARM处理器各外围硬件的初始化,如SPI、I2C、网卡等。最终uboot进入main_loop,阻塞等待串口终端输入命令或者去启动内核。始终要清楚一点uboot的主要作用是通过 Flash和Sram初始化时钟和DDR(arm最小系统),最终uboot是需要在ddr中运行进行裸机调试或者去引导操作系统内核启动。

(如何编写uboot命令进行裸机调试 以及uboot如何引导操作系统 后面我会依次介绍)

最后上还是上图<启动流程图>:

上电后arm默认从地址0x00000000处取出第一条指令。SP指向SRAM的顶端,当运行C程序时,形参和局部变量需要入栈,SP依次减4入栈。在这部分运行的程序完成对时钟和ddr的配置(arm最小系统)以及对gd_t *gd_ptr赋值(用于存储uboot镜像的长度,重定位地址,重定位偏移量等信息)。此时ddr已经可以成功进行数据读写,将uboot镜像从flash复制到DDR,根据重定位偏移量获得新的PC位置 继续运行uboot。

step3

uboot已经在ddr中运行了,也就不存在任何硬件上限制了。step3完成对ARM处理器各外围硬件的初始化,如SPI、I2C、网卡等。最终uboot进入main_loop,阻塞等待串口终端输入命令或者去启动内核。

以上就是arm处理器的启动流程,是否弄明白了呢?

下一篇我就以三星出品的S3C2410以及S5PV210为例子,讲解一下他们从nandflash启动。至于现在手机和平板电脑上广泛运用的eMCC(内部就是nandflash  只不过是改变一下读写接口)也是一样的套路(请记住万事不离其宗 以不变应万变)。

uboot - the bootloader of linux的更多相关文章

  1. 【嵌入式】bootloader,linux,filesystem的烧写

    平台: 深圳市优龙科技有限公司的FS2410(基于ARM9) 准备: 1.用串口(UART1,J8)线与PC机相连,这个是用来传输数据和显示信息的 2.连接USB数据下载线(usb device),注 ...

  2. OK335xS U-boot 编译问题&无Linux shell 问题

    /************************************************************************** * OK335xS U-boot 编译问题&am ...

  3. BootLoader与Linux内核的参数传递【转】

    本文转载自:http://blog.sina.com.cn/s/blog_476d8cf30100rttx.html 在嵌入式系统中,BootLoader 是用来初始化硬件,加载内核,传递参数.因为嵌 ...

  4. linux常用的BootLoader U-boot的前世今生

    U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目.U-Boot的作用是系统引导. U-Boot从FADSROM.8xxROM.PPCBOOT逐步发展演化而 ...

  5. 【转】Bootloader之uBoot简介(转)

    原文网址:http://blog.csdn.net/sadamoo/article/details/8139946 来自http://blog.ednchina.com/hhuwxf/1915416/ ...

  6. 【转】uboot移植(一)BootLoader基本概念

    原文网址:http://blog.chinaunix.net/uid-25445243-id-3869348.html 一.BootLoader简介1.1.嵌入式Linux软件结构与分布 在一般情况下 ...

  7. 【嵌入式开发】 Bootloader 详解 ( 代码环境 | ARM 启动流程 | uboot 工作流程 | 架构设计)

    作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42462795 转载请著名出处 相关资源下载 :  -- u-boo ...

  8. Bootloader之uBoot简介

    本文转载自:http://blog.ednchina.com/hhuwxf/1915416/message.aspx 一.Bootloader的引入 从前面的硬件实验可以知道,系统上电之后,需要一段程 ...

  9. Bootloader之uBoot简介(转)

    来自http://blog.ednchina.com/hhuwxf/1915416/message.aspx,感谢作者 一.Bootloader的引入从前面的硬件实验可以知道,系统上电之后,需要一段程 ...

随机推荐

  1. 【原创】大数据基础之ElasticSearch(2)常用API整理

    Fortunately, Elasticsearch provides a very comprehensive and powerful REST API that you can use to i ...

  2. es2015 解构赋值

    解构赋值语法是一个 Javascript 表达式,这使得可以将值从数组或属性从对象提取到不同的变量中.

  3. python学习之Numpy.genfromtxt

    Python 并没有提供数组功能,虽然列表 (list) 可以完成基本的数组功能,但它并不是真正的数组,而且在数据量较大时,使用列表的速度就会慢的让人难受.Numpy 提供了真正的数组功能,以及对数据 ...

  4. Code First

    Add-Migration ****** Update-Database –Verbose

  5. [C#.Net]全局钩子实现USB扫码枪无焦点状态下扫入

    https://www.cnblogs.com/masonlu/p/10105135.html

  6. lxml.etree.HTML(text) 解析HTML文档

    0.参考 http://lxml.de/tutorial.html#the-xml-function There is also a corresponding function HTML() for ...

  7. C++类的成员

    1.成员变量 成员变量可以是任何类型,如基本数据类型.引用.另一个类的对象或指针.自身类的引用或指针,但不能是自身类的对象: 成员变量不能指定为auto.register.extern 存储类型. 1 ...

  8. SQL反模式学习笔记15 分组

    目标:查询得到每组的max(或者min等其他聚合函数)值,并且得到这个行的其他字段 反模式:引用非分组列 单值规则:跟在Select之后的选择列表中的每一列,对于每个分组来说都必须返回且仅返回一直值. ...

  9. 企业级Docker-Harbor

    [环境准备] # yum install -y yum-utils device-mapper-persistent-data lvm2 下载docker-ce版本的yum源 # yum-config ...

  10. 配置ubuntu的超管账号密码

    1 在搭建ubuntu 之后 只有自己的账号 2 root 用户是没有配置好的 需要重新配置 3 passwd root 4 输入新密码即可