找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
广告投放联系QQ68610888
查看: 155580|回复: 240

【U-Boot】U-Boot 基本编译教程

 火... [复制链接]
发表于 2014-5-1 01:15 | 显示全部楼层 |阅读模式
本帖最后由 hackpascal 于 2014-11-15 12:09 编辑

此教程为入门教程只讲解如何编译 U-Boot,不讲解代码修改及排错等。
此教程适用于 AR 及 QCA 系列的 ar71xx 平台。
若有疑问,或看不懂,请直接回帖,以便楼主修改教程。。

准备工作:

Linux 系统:
        32 位 / 64 位均可。楼主使用的是 Ubuntu 12.10 amd64。
        64 位环境下需要安装 32 位库。如 Ubuntu 下是运行 sudo apt-get install libc6:i386 libgcc1:i38 libstdc++5:i386 libstdc++6:i386

基础编译环境:
        一般来说每个发行版的 Linux 系统都有包管理器。
        例如在 Ubuntu 的 Shell 里运行 sudo apt-get install build-essential 就可安装好编译环境。
        其他的系统请自行百度。

基本的 Linux Shell 基础:
        这个只能自己想办法解决了。


入门级:

        入门级使用现成的工具链和编译脚本,使用最少的命令完成代码编译。
        使用 TL-MR3420 v1 的代码进行讲解。

        1. 下载源代码
                打开 http://www.tp-link.com/en/support/gpl/,在左侧选择 3G/4G Routers,然后在列表中选择 TL-MR3420 V1 的代码进行下载。




        2. 解压代码
                这个就随便了,一般解压在主目录。
                将下载的 mr3420_3220v1.tar.gz 移动到主目录。
                注意:这里压缩包扩展名有误,实际上是 bzip2 压缩的。
                打开终端,此时终端应该默认在主目录下,若不在主目录下,就运行 cd ~ 命令进入主目录。
                运行命令 tar -jxvf mr3420_3220v1.tar.gz 以解压代码。




        3. 编译代码

                运行命令 cd mr3420_3220v1/build 进入代码编译目录。
                运行命令 make BOARD_TYPE=ap99 fs_prep 以创建编译过程所需的目录。
                运行命令 make BOARD_TYPE=ap99 uboot 以开始编译 U-Boot。

        4. 刷机测试
                如果没有意外,那么编译结束后的输出应该是这样的:




                生成的 u-boot.bin 位于 mr3420_3220v1/images/ap99/ 目录下。
                此文件未被填充到 128K,需要手动填充。
                填充后的文件就可以进行刷机测试了。
                此文件是未压缩版,因此本身体积很大。此代码也不能生成压缩版的 U-Boot。

        5. 说明
                操作说明请参阅代码根目录下的 readme.txt。
                入门级的操作就这么简单{:soso_e120:}。


进阶级:

        进阶级依然使用 TL-MR3420 v1 的代码。
        进阶级将分离 U-Boot 代码及现成的工具链,搭建基本的交叉编译环境。

        1. 分离 U-Boot 代码
                U-Boot 代码位于 mr3420_3220v1/ap99/boot/u-boot 。
                将整个 u-boot 目录复制到一个地方,如用户主目录。
                此 u-boot 目录里的代码就是全部所需的代码了。之后的所有操作都在这里面完成。



        2. 复制工具链
                此工具链即为编译器,但是是针对 mips 架构的交叉编译器。
                工具链位于 mr3420_3220v1/build/gcc-3.4.4-2.16.1/build_mips。
                将整个 build_mips 复制到一个地方,如主目录。

        3. 搭建编译环境
                现在,mr3420_3220v1 目录已经没有用,可以删掉,因为所有需要的文件都已复制出来了。
                设置环境变量以让 Shell 能够访问到工具链。运行命令 export PATH=$PATH:~/build_mips/bin 即可。
                        此设置只在当前的 Shell 里有效。关于如何设置永久的环境变量,请自行百度。

        4. 清理代码
                此操作用于清理上次编译产生的临时文件及配置文件,为下次配置及编译做准备。
                进入 u-boot 目录:运行命令 cd ~/u-boot
                运行命令 make distclean 以执行完全清理。

        5. 编译代码
                这里是一个完整的编译步骤。

                a. 指定开发板类型
                        此操作表示我们要编译哪个开发板。AR7241 的开发板(这里其实叫参考板,reference board)名称为 ap99,因此运行 make ap99_config 以进行配置。
                        所有的开发板定义都写在 Makefile 里。其中以 ***_config: 开头的行,*** 就是开发板名称。
                        AR 及 QCA 系列的参考板名称会在最后列出。
                        有些开发板还有额外的参数可以定义,这里暂不讲解。




                b. 编译
                        运行命令 make CROSS_COMPILE=mips-linux- 进行编译。
                        CROSS_COMPILE=mips-linux- 指定了交叉编译器的文件名前缀为 mips-linux-。例如,要使用工具链中的 gcc 程序,那么就运行 mips-linux-gcc。这个可以看一下 build_mips/bin 目录下的文件名。
                        由于之前运行了 export PATH=$PATH:~/build_mips/bin 命令,所以 Shell 能够直接找到相关程序的位置。否则,CROSS_COMPILE= 需要指定为 ~/build_mips/bin/mips-linux-。

                c. 测试
                        编译成功后,u-boot 目录下会生成 u-boot.bin,这就是未填充到 128K 的文件。

        6. 常用编译命令。
                make ***_config    配置生成为指定的开发板(*** 为开发板名称)。
                make CROSS_COMPILE=***    编译代码,并指定交叉编译器文件名前缀为 ***。
                make clean    清理上次编译生成的临时文件。此命令不会删除开发板配置,下次编译可以直接运行 make 命令。
                make distclean    清理编译生成的临时文件及开发板配置。运行此命令后,需要再次指定开发板后才能进行编译。


其它说明:

        这里使用的 MR3420 v1 代码不能生成压缩代码。其他的,如 AR9331 AR934x 等代码需要在编译时加入 COMPRESSED_UBOOT=1 来进行编译,如 make CROSS_COMPILE=mips-linux- COMPRESSED_UBOOT=1

        编译 AR934x QCA95xx 的代码需要指定使用的以太网交换机 PHY 类型,即在编译时加入 ETH_CONFIG=_s27 或 ETH_CONFIG=_s17 。其中 _s27 为内置百兆 PHY,_s17 为外部千兆 PHY。

        具体路由型号的 GPL 代码里都有说明文件,可以根据说明文件来进行编译,并了解参考板名称。


常见参考板名称:

        AR7240   -   ap91
        AR7241   -   ap99
        AR9331   -   ap121
        AR9341   -   ap123
        AR9342   -   mi124
        AR9344   -   db12x
        QCA953x   -   ap143
        QCA9558   -   ap135

        其它的请在 https://wikidevi.com/wiki/Atheroshttps://wikidevi.com/wiki/Qualcomm_Atheros 上进行查找。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

点评

你说的很对。: 5.0 我赞同你的说法。: 5.0
你说的很对。: 5 我赞同你的说法。: 5
爱上你了  发表于 2018-4-21 00:52
不顶你我都过意不去,激动的都打错字了  发表于 2014-9-7 08:12
我顶你我都觉得过意不去!  发表于 2014-9-7 08:12
论坛有您更光彩!  发表于 2014-5-23 23:47
楼主你太强大了,授人鱼不如授人渔,不过你那个不死uboot是不是做好了就不会更新了比如ar9344千兆那个uboot还会更新吗?  发表于 2014-5-4 12:00
好样的。你做得很对!更多的朋友想看你分享源代码的基本修改方法  发表于 2014-5-2 13:31
膜拜中.....  发表于 2014-5-1 11:07
楼主文武双全,文能写代码,武能修主板!佩服!!佩服!!!  发表于 2014-5-1 10:56

评分

参与人数 44恩山币 +63 收起 理由
MarkHui + 1 强大的恩山!(以下重复1万次)
lsh37 + 1 我来恩山就是为了撩你!
regedit110 + 1 亲,你太牛逼了,走,我请你吃鳇鱼头去~~~
a7077447 + 1 我来恩山就是为了看你!
缘下有我 + 1 亲,你这么厉害,我想做你的经纪人!
随风飘逝ゞ + 1 一看就是觉得高端、大气、上档次!
touchmii + 1 我来恩山就是为了看你!
HugoYuan + 1 土豪,我们做朋友吧!
jianqiumy + 1 强大的恩山!(以下重复1万次)
test0x01 + 3 一看就是觉得高端、大气、上档次!
hhh1934 + 1 一看就是觉得高端、大气、上档次!
guzhiqiang + 1 泥马,真给力,我要顶!
lizhendong + 1 强大的恩山!(以下重复1万次)
libc0607 + 1 城会玩
hzxbusiness + 1 虽然暂时还不能完全看懂,但仍觉得楼主实在是太牛13了,真是不明觉厉!
sl3317 + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!
pro_drive + 1 土豪,我们做朋友吧!
师兄 + 1 楼主威武—我就想问问挖掘机技术哪家强?
wuzhimeng + 1 土豪,我们做朋友吧!
foting + 1 楼主威武—我就想问问挖掘机技术哪家强?
proxypaul + 3 一看就是觉得高端、大气、上档次!
sen921 + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!
selfishworm + 1 楼主威武—我就想问问挖掘机技术哪家强?
hotar + 1 楼主已经被我列为中国之光~暖男
wsdy001 + 3 拿去买糖吃吧!
right_temp + 1 一看就是觉得高端、大气、上档次!
qzhangdlut + 1 一看就是觉得高端、大气、上档次!
为侬消得人憔悴 + 1 楼主好棒,我们做朋友吧
hj2150663 + 1 楼主威武—我就想问问挖掘机技术哪家强?
榕小榕 + 1 楼主威武—我就想问问挖掘机技术哪家强?
wonder_01_ + 1 做人要低调 很强大~~~~~~~~~~~
slly + 1 强大的恩山!(以下重复1万次)
mike22437120 + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!
pppeng-101 + 1 土豪,我们做朋友吧!
泽铭他爹 + 1 一看就是觉得高端、大气、上档次!
oywl43210 + 3 什么时候更新下,期待啊。。。。。。。。
haxc + 5 土豪,我们做朋友吧!
chenxin92 + 3 一看就是觉得高端、大气、上档次!
zhoutao0712 + 2 奸商追捧的新星升起!
pass3389 + 3 强大的恩山!(以下重复1万次)
ZHIZAI100 + 3 一看就是觉得高端、大气、上档次!
mury + 1 楼主什么时候出进阶篇,想知道怎么修改出32m uboot啊
cgnq + 1 泥马,真给力,我要顶!
imwf + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!

查看全部评分

我的恩山、我的无线 The best wifi forum is right here.
 楼主| 发表于 2014-5-1 01:17 | 显示全部楼层
本帖最后由 hackpascal 于 2014-10-28 22:21 编辑

交叉编译工具链制作

有时候,编译 U-Boot 却没有合适的工具链,因此需要自己制作。
自己制作工具链并不复杂,而且可以根据自己的需要进行设置。

下载代码:
这里要用到的是 Buildroot,这是一个用于生成嵌入式 Linux 系统的编译环境。
说道这里,你可能会觉得,这跟 OpenWrt 的编译环境很像。事实上,OpenWrt 的编译环境正是基于 Buildroot 的。

但是为什么不使用 OpenWrt 而要使用 Buildroot 呢?
这是因为 OpenWrt 生成的工具链依赖度太高,而且缺少部分特性,容易导致编译失败。
另外,在没有任何现成工具链的时候,用 OpenWrt 制作工具链花费的时间比 Buildroot 多。

下载地址:
http://buildroot.uclibc.org/download.html
在 Download 下面选择一个下载,就行,如 buildroot-2014.08.tar.gz

配置:
解压代码,如解压到 ~/buildroot-2014.08
打开终端,进入 buildroot 目录,运行 make menuconfig 命令:



以 Atheros AR71XX 为例
AR71XX CPU 架构为 MIPS 32位,大端字节序 (Big-endian),指令集 MIPS32 Release 2 (mips32r2),不含浮点单元
Ralink 的 CPU 跟 AR71XX 基本相同,只是字节序为小端 (Little-endian)





Target options  ---> Target Architecture
选择 MIPS (big endian)

Target options  ---> Target Architecture Variant
选择 mips 32r2

Target options  ---> Use soft-float
选中

编译:
运行 make 即可开始编译。编译过程跟 OpenWrt 基本相同,即先下载代码吧,然后进行编译。
若要加快速度,可以加上多线程参数 -jN,其中 N 为 CPU 核心数
若要显示详细过程,可以加上 V=99。



获取工具链:
编译完成后,工具链位于 buildroot 目录下的 output/host/usr 下
将 usr 文件夹复制出来即可

重要提醒:
如果使用 buildroot 工具链来编译 U-Boot,需修改 U-Boot 中的一个文件,否则 U-Boot 可能会在代码重定位时卡死

cpu/mips/config.mk

在最后加上两行行
  1. PLATFORM_CPPFLAGS += -G 0 -mabicalls -fpic -msoft-float -fno-schedule-insns -fno-schedule-insns2
  2. PLATFORM_LDFLAGS       += -G 0 -static -n -nostdlib
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

点评

好东东。。慢慢学习一下。。。大大 能不能做个WDR5600用的 U-BOOT 呢。。ART文件专用的有吗?  详情 回复 发表于 2016-2-1 17:49
前辈,如您2楼所讲,编译过程为 make -j2 可是无奈下载工具的速度太慢,经常会卡死 若我自己下载的binutils-2.22文件,我应该怎么操作呢? make过程中的Saving to: `/home/chansane/buildroot-2014.08/output/bu  详情 回复 发表于 2014-11-19 19:38
我要来好好学习  详情 回复 发表于 2014-11-15 17:07
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

 楼主| 发表于 2014-5-1 01:18 | 显示全部楼层
本帖最后由 hackpascal 于 2014-10-28 22:10 编辑

U-Boot 启动过程及简单代码分析

这里以 AR7241 的 U-Boot 为例

MIPS 架构简介
MIPS 地址空间:
这里说的是地址空间,不是内存空间。内存只是映射在一部分地址空间上而已。
内存分为4段 (Kuseg、Kseg0、Kseg1、Kseg2),其中 Kseg0 (0x80000000 ~ 0x9fffffff) 为缓存段,直接映射在物理地址段上。
Kseg1 (0xa0000000 ~ 0xbfffffff) 为非缓存段,直接映射在物理地址段上。
Kuseg 和 Kseg2 需要经过地址转换才能访问。
在 CPU 复位时,缓存未被初始化,只有 Kseg1 能够被直接访问。

AR71XX 物理段:
内存、Flash、IO寄存器等都被映射在物理段上
范围为 0~0x1fffffff,访问时需要通过宏 KSEG0ADDR(_addr) 来通过 Kseg0 访问或 KSEG1ADDR(_addr) 来通过 Kseg1 访问。
物理地址一般不能直接访问,都需要通过 Kseg0 或 Kseg1 来访问。

主要的映射范围:
DDR:0~0x0fffffff (这一段直接映射物理内存,一般通过带缓存的 Kseg0 来访问,以便加快速度)
I/O空间:0x10000000 ~ 0x1dffffff (这一段直接控制硬件,必须通过 Kseg1 来访问)
SPI Flash 空间:0x1f000000 ~ 0x1fffffff (这一段映射闪存的前 16MB 数据)

AR71XX 启动过程

CPU 上电时,物理段中,SPI Flash 的前4MB 被循环映射在 0x1f000000 上,因此会被重复 4 次。
执行地址为 KSEG1ADDR(0x1fc00000),即 0xbfc00000。

*注意:U-Boot 在编译时,实际设置的基址,即启动地址为 KSEG0ADDR(0x1f000000),即 0x9f000000。下面会讲到为什么要这么设置。

U-Boot 中的起始代码在 cpu/mips/start.S 中:

  1. _start:
  2. #ifndef COMPRESSED_UBOOT
  3.         RVECENT(reset,0)        /* U-boot entry point */
  4.         RVECENT(reset,1)        /* software reboot */
  5.         RVECENT(romReserved,2)
  6. ----------------------------------------------------省略----------------------------------------------------
  7.         RVECENT(romReserved,125)
  8.         RVECENT(romReserved,126)
  9.         RVECENT(romReserved,127)

  10.         /* We hope there are no more reserved vectors!
  11.          * 128 * 8 == 1024 == 0x400
  12.          * so this is address R_VEC+0x400 == 0xbfc00400
  13.          */
复制代码

以上代码设置异常向量,不需要理解
第一句 RVECENT(reset,0) 为一个跳转,直接转到下面的代码进行运行

  1.         .align 4
  2. reset:
  3.         /* set GPIO_OE  -- added by lsz to fix the LED issue 09.03.25 */
  4.         li                a1, AR7100_GPIO_OE
  5.         lw                v1, 0(a1)
  6.         li                v0, 0x3fdff        
  7.         or                v1, v1, v0
  8.         sw                v1, 0(a1)
  9.         lw                v0, 0(a1)
  10.         lw                v0, 0(a1)

  11. ----------------------------------------------------省略----------------------------------------------------

  12.         /* CONFIG0 register */
  13.         li        t0, CONF_CM_UNCACHED
  14.         mtc0        t0, CP0_CONFIG
复制代码

以上代码前半部分通过设置 GPIO 来点亮路由的 LED。后半部分设置 CPU 相关参数,不需要理解
以上代码没有包含使用相对地址的指令,也就是说,无论这段代码在 CPU 地址空间的何种位置,都能够正常执行
因此虽然 CPU 的执行地址为 0xbfc00000,以上代码依然能执行

  1.     /* Initialize GOT pointer.*/
  2.         bal     1f
  3.         nop
  4.         .word   _GLOBAL_OFFSET_TABLE_
  5.         1:
  6.         move    gp, ra
  7.         lw      t1, 0(ra)
  8.         move        gp, t1
复制代码

以上代码加载了用于 U-Boot 重定位的 GOT 指针,它是 U-Boot 在内存中进行自身移动的关键

  1.         la      t9, lowlevel_init
  2.         jalr    t9
  3.         nop
复制代码

以上代码进行 CPU 频率设置,超频就是在这里实现的

  1.         la        t0, rel_start
  2.         j        t0
  3.         nop

  4. rel_start:
复制代码

以上代码很关键,它实现了 U-Boot 的执行地址的转移
假设当前 CPU 的执行地址在 0xbfc00000 + x
那么执行此段代码后,CPU 执行地址将跳转到 0x9f000000 + x 处进行执行
前面提到了 CPU 在复位时,物理段 0x1f000000 处循环映射 4 次 Flash 的前 4MB,而 U-Boot 大小只有100KB多,
因此 0x1f000000 跟 0x1fc00000 处的数据是一样的,这就实现了运行地址的转移。
至于为什么不跳转到 0xbf000000 + x 处运行,是因为 AR71XX CPU 在复位时,缓存可用,因此通过 Kseg0 段执行,可以加快速度。

  1.         /* REMAP_DISABLE */
  2.         li        a0, AR7100_SPI_CLOCK
  3.         li        t0, 0x43
  4.         sw        t0, 0(a0)
复制代码

以上代码的作用是禁用物理段 0x1f000000 的 4MB Flash 数据循环映射,以便让 0x1f000000 能够映射 16MB 的 Flash 数据


  1.         /* Initialize caches...
  2.          */
  3.         /* 初始化缓存 */
  4.         la      t9, simple_mips_cache_reset
  5.         jalr    t9
  6.         nop

  7.         /* ... and enable them.
  8.          */
  9.         /* 启用缓存 */
  10.         li        t0, CONF_CM_CACHABLE_NONCOHERENT
  11.         mtc0        t0, CP0_CONFIG

  12. ----------------------------------------------------省略----------------------------------------------------

  13. #if defined(CONFIG_AR7100) || defined(CONFIG_AR7240)
  14.     /* 将缓存锁定在 Kseg0 低端处 */
  15.     la      t9, mips_cache_lock_24k
  16.     jalr    t9   
  17.     nop
  18. #endif
  19. #endif /* #ifndef COMPRESSED_UBOOT */
  20.         /* 将栈指针指向缓存 */
  21.         li        t0, CFG_SDRAM_BASE + CFG_INIT_SP_OFFSET
  22.         la        sp, 0(t0)

  23.         /* 进入 C 环境 */
  24.         la        t9, board_init_f
  25.         j        t9
  26.         nop
复制代码

这段代码的作用是初始化缓存,并将缓存锁定在 Kseg0 上,用作临时的栈空间
此时内存并未初始化, Kseg0 对应的 0x80000000 ~ 0x8fffffff 还不能被访问,因此需要用缓存代替。
然后跳转到 board_init_f 进行执行

下面开始在 C 环境中运行
文件:lib_mips/board.c

  1. void board_init_f(ulong bootflag)
  2. {
  3.         gd_t gd_data, *id;
  4.         bd_t *bd;
  5.         init_fnc_t **init_fnc_ptr;
  6.         ulong addr, addr_sp, len = (ulong)&uboot_end - CFG_MONITOR_BASE;
  7.         ulong *s;
  8. #ifdef COMPRESSED_UBOOT
  9.         char board_string[50];
  10. #endif
  11. #ifdef CONFIG_PURPLE
  12.         void copy_code (ulong);
  13. #endif


  14.         /* Pointer is writable since we allocated a register for it.
  15.          */
  16.         gd = &gd_data;
  17.         /* compiler optimization barrier needed for GCC >= 3.4 */
  18.         __asm__ __volatile__("": : :"memory");

  19.         memset ((void *)gd, 0, sizeof (gd_t));

  20.         /* 依次运行初始化函数 */
  21.         for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  22.                 if ((*init_fnc_ptr)() != 0) {
  23.                         hang ();
  24.                 }
  25.         }

  26. #ifdef COMPRESSED_UBOOT
  27.         checkboard(board_string);
  28.         printf("%s\n\n",board_string);
  29.         gd->ram_size = bootflag;
  30.         puts ("DRAM:  ");
  31.         print_size (gd->ram_size, "\n");
  32. #endif

  33.         /*
  34.          * Now that we have DRAM mapped and working, we can
  35.          * relocate the code and continue running from DRAM.
  36.          */
  37.         addr = CFG_SDRAM_BASE + gd->ram_size;

  38.         /* We can reserve some RAM "on top" here.
  39.          */

  40.         /* round down to next 4 kB limit.
  41.          */
  42.         addr &= ~(4096 - 1);
  43.         debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);

  44. ----------------------------------------------------省略----------------------------------------------------

  45.         /*
  46.          * Save local variables to board info struct
  47.          */
  48.         /* 记录内存基址及大小、串口波特率 */
  49.         bd->bi_memstart        = CFG_SDRAM_BASE;        /* start of  DRAM memory */
  50.         bd->bi_memsize        = gd->ram_size;                /* size  of  DRAM memory in bytes */
  51.         bd->bi_baudrate        = gd->baudrate;                /* Console Baudrate */

  52.         memcpy (id, (void *)gd, sizeof (gd_t));

  53.         /* On the purple board we copy the code in a special way
  54.          * in order to solve flash problems
  55.          */
  56. #ifdef CONFIG_PURPLE
  57.         copy_code(addr);
  58. #endif

  59.         /* 执行代码转移 */
  60.         relocate_code (addr_sp, id, addr);

  61.         /* NOTREACHED - relocate_code() does not return */
  62. }
复制代码

board_init_f 的作用为进行内存、时钟、串口等设备的初始化,并为 U-Boot 的运行划分内存。

初始化函数位于 board_init_f 函数上方

  1. init_fnc_t *init_sequence[] = {
  2. #ifndef COMPRESSED_UBOOT
  3.         timer_init,    /* 初始化时钟 */
  4. #endif
  5.         env_init,                /* initialize environment */ /* 初始化环境变量 */
  6. #ifdef CONFIG_INCA_IP
  7.         incaip_set_cpuclk,        /* set cpu clock according to environment variable */
  8. #endif
  9.         init_baudrate,                /* initialze baudrate settings */
  10. #ifndef COMPRESSED_UBOOT
  11.         serial_init,                /* serial communications setup */ /* 初始化串口,这样 TTL 才有输出 */
  12. #endif
  13.         console_init_f,   /* 初始化控制台环境 */
  14.         display_banner,                /* say that we are here */ /* 显示 U-Boot 1.1.4 ..... */
  15. #ifndef COMPRESSED_UBOOT
  16.         checkboard,   /* 显示 AP99 (ar7241 - Virian) U-boot */
  17.         init_func_ram,    /* 初始化内存 */
  18. #endif
  19.         NULL,
  20. };
复制代码

以上为初始化函数列表,依次运行

在初始化完成后,board_init_f 调用 relocate_code 将 U-Boot 代码从 Flash 中复制到内存中,再从内存中执行

回到 cpu/mips/start.S

  1.         .globl        relocate_code
  2.         .ent        relocate_code
  3. relocate_code:
  4.         move        sp, a0                /* Set new stack pointer                */

  5.         li        t0, CFG_MONITOR_BASE
  6.         la        t3, in_ram
  7.         lw        t2, -12(t3)        /* t2 <-- uboot_end_data        */
  8.         move        t1, a2

  9.         /*
  10.          * Fix GOT pointer:
  11.          *
  12.          * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address
  13.          */
  14.         /* 根据传递的参数计算移动距离 */
  15.         move        t6, gp
  16.         sub        gp, CFG_MONITOR_BASE
  17.         add        gp, a2                        /* gp now adjusted                */
  18.         sub        t6, gp, t6                /* t6 <-- relocation offset        */

  19.         /*
  20.          * t0 = source address
  21.          * t1 = target address
  22.          * t2 = source end address
  23.          */
  24.         /* 复制代码到内存 */
  25. 1:
  26.         lw        t3, 0(t0)
  27.         sw        t3, 0(t1)
  28.         addu        t0, 4
  29.         ble        t0, t2, 1b
  30.         addu        t1, 4                        /* delay slot                        */

  31.         /* If caches were enabled, we would have to flush them here.
  32.          */

  33.         /* Jump to where we've relocated ourselves.
  34.          */
  35.         /* 跳转到移动后的代码继续执行 */
  36.         addi        t0, a2, in_ram - _start
  37.         j        t0
  38.         nop

  39.         .word        uboot_end_data
  40.         .word        uboot_end
  41.         .word        num_got_entries

  42. in_ram:
  43.         /* 此时已经在内存中了 */
  44.         /* Now we want to update GOT.
  45.          */
  46.         lw        t3, -4(t0)        /* t3 <-- num_got_entries        */
  47.         addi        t4, gp, 8        /* Skipping first two entries.        */
  48.         li        t2, 2
  49.         /* GOT 表重定位 */
  50. 1:
  51.         lw        t1, 0(t4)
  52.         beqz        t1, 2f
  53.         add        t1, t6
  54.         sw        t1, 0(t4)
  55. 2:
  56.         addi        t2, 1
  57.         blt        t2, t3, 1b
  58.         addi        t4, 4                /* delay slot                        */

  59.         /* Clear BSS.
  60.          */
  61.         lw        t1, -12(t0)        /* t1 <-- uboot_end_data        */
  62.         lw        t2, -8(t0)        /* t2 <-- uboot_end                */
  63.         add        t1, t6                /* adjust pointers                */
  64.         add        t2, t6

  65.         /* 清除 BSS 段,即全局变量清零 */
  66.         sub        t1, 4
  67. 1:        addi        t1, 4
  68.         bltl        t1, t2, 1b
  69.         sw        zero, 0(t1)        /* delay slot                        */

  70.         /* 移交控制权到 board_init_r */
  71.         move        a0, a1
  72.         la        t9, board_init_r
  73.         j        t9
  74.         move        a1, a2                /* delay slot                        */

  75.         .end        relocate_code
复制代码

relocate_code 的作用是将 U-Boot 代码移动到内存的最高端,并清空 BSS 段,设置栈指针,创造一个完整的 C 环境。

然后 C 环境就绪,进入 board_init_r 继续运行,此时 SPI Flash 不再被使用,U-Boot 完全在内存中运行了

回到 lib_mips/board.c:

  1. void board_init_r (gd_t *id, ulong dest_addr)
  2. {
  3.         cmd_tbl_t *cmdtp;
  4.         ulong size;
  5.         extern void malloc_bin_reloc (void);
  6. #ifndef CFG_ENV_IS_NOWHERE
  7.         extern char * env_name_spec;
  8. #endif
  9.         char *s, *e;
  10.         bd_t *bd;
  11.         int i;

  12.         gd = id;
  13.         gd->flags |= GD_FLG_RELOC;        /* tell others: relocation done */

  14.         debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

  15.         gd->reloc_off = dest_addr - CFG_MONITOR_BASE;  /* 记录 U-Boot 重定位时移动的偏移量 */

  16.         monitor_flash_len = (ulong)&uboot_end_data - dest_addr;

  17.         /*
  18.          * We have to relocate the command table manually
  19.          */
  20.         /* U-Boot 命令表,重定位所有字符串地址和函数地址 */
  21.          for (cmdtp = &__u_boot_cmd_start; cmdtp !=  &__u_boot_cmd_end; cmdtp++) {
  22.                 ulong addr;

  23.                 addr = (ulong) (cmdtp->cmd) + gd->reloc_off;
  24. #if 0
  25.                 printf ("Command "%s": 0x%08lx => 0x%08lx\n",
  26.                                 cmdtp->name, (ulong) (cmdtp->cmd), addr);
  27. #endif
  28.                 cmdtp->cmd =
  29.                         (int (*)(struct cmd_tbl_s *, int, int, char *[]))addr;

  30.                 addr = (ulong)(cmdtp->name) + gd->reloc_off;
  31.                 cmdtp->name = (char *)addr;

  32.                 if (cmdtp->usage) {
  33.                         addr = (ulong)(cmdtp->usage) + gd->reloc_off;
  34.                         cmdtp->usage = (char *)addr;
  35.                 }
  36. #ifdef        CFG_LONGHELP
  37.                 if (cmdtp->help) {
  38.                         addr = (ulong)(cmdtp->help) + gd->reloc_off;
  39.                         cmdtp->help = (char *)addr;
  40.                 }
  41. #endif
  42.         }
  43.         /* there are some other pointer constants we must deal with */
  44. #ifndef CFG_ENV_IS_NOWHERE
  45.         env_name_spec += gd->reloc_off;
  46. #endif

  47.         /* 熄灭路由的 LED */
  48.         /* turn off switch LED added by tiger 20091225 */
  49.         ar7240_gpio_sw_led();

  50. ----------------------------------------------------省略----------------------------------------------------

  51.         /* configure available FLASH banks */
  52.         /* 初始化 Flash,显示 Flash:XXXX (X MB) */
  53.         size = flash_init();
  54.         display_flash_config (size);

  55.         bd = gd->bd;
  56.         bd->bi_flashstart = CFG_FLASH_BASE;
  57.         bd->bi_flashsize = size;
  58. #if CFG_MONITOR_BASE == CFG_FLASH_BASE
  59.         bd->bi_flashoffset = monitor_flash_len;        /* reserved area for U-Boot */
  60. #else
  61.         bd->bi_flashoffset = 0;
  62. #endif

  63.         /* initialize malloc() area */
  64.         /* 初始化内存管理器,以便能够使用 malloc 等函数 */
  65.         mem_malloc_init();
  66.         malloc_bin_reloc();

  67.         /* relocate environment function pointers etc. */
  68.         /* 重定位环境变量相关函数和数据 */
  69.         env_relocate();

  70.         /* board MAC address */
  71.         s = getenv ("ethaddr");
  72.         for (i = 0; i < 6; ++i) {
  73.                 bd->bi_enetaddr[i] = s ? simple_strtoul (s, &e, 16) : 0;
  74.                 if (s)
  75.                         s = (*e) ? e + 1 : e;
  76.         }

  77.         /* IP Address */
  78.         bd->bi_ip_addr = getenv_IPaddr("ipaddr");

  79. #if defined(CONFIG_PCI)
  80.         /*
  81.          * Do pci configuration
  82.          */
  83.         /* PCI 设备初始化 */
  84.         pci_init();
  85. #endif

  86. /** leave this here (after malloc(), environment and PCI are working) **/
  87.         /* Initialize devices */
  88.         devices_init ();

  89.         jumptable_init ();

  90.         /* Initialize the console (after the relocation and devices init) */
  91.         console_init_r ();
  92. /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/

  93.         /* Initialize from environment */
  94.         if ((s = getenv ("loadaddr")) != NULL) {
  95.                 load_addr = simple_strtoul (s, NULL, 16);
  96.         }
  97. #if (CONFIG_COMMANDS & CFG_CMD_NET)
  98.         if ((s = getenv ("bootfile")) != NULL) {
  99.                 copy_filename (BootFile, s, sizeof (BootFile));
  100.         }
  101. #endif        /* CFG_CMD_NET */

  102. #if defined(CONFIG_MISC_INIT_R)
  103.         /* miscellaneous platform dependent initialisations */
  104.         misc_init_r ();
  105. #endif

  106. #if (CONFIG_COMMANDS & CFG_CMD_NET)
  107.         /* 初始化网络 */
  108. #if defined(CONFIG_NET_MULTI)
  109.         puts ("Net:   ");
  110. #endif
  111.         eth_initialize(gd->bd);
  112. #endif

  113.         /* main_loop() can return to retry autoboot, if so just run it again. */
  114.         /* 进入命令主循环,永不返回 */
  115.         for (;;) {
  116.                 main_loop ();
  117.         }

  118.         /* NOTREACHED - no way out of command loop except booting */
  119. }
复制代码

到此为止 U-Boot 就开始正常运行了
在处理交互命令前,U-Boot 会检查有没有启动命令

文件:common/main.c

  1. void main_loop (void)
  2. {
  3. ----------------------------------------------------省略----------------------------------------------------

  4. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  5.         char *s;
  6.         int bootdelay;
  7. #endif
  8. ----------------------------------------------------省略----------------------------------------------------

  9. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  10.         /* 获取启动延时 */
  11.         s = getenv ("bootdelay");
  12.         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

  13. //        debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

  14. # ifdef CONFIG_BOOT_RETRY_TIME
  15.         init_cmd_timeout ();
  16. # endif        /* CONFIG_BOOT_RETRY_TIME */

  17. ----------------------------------------------------省略----------------------------------------------------

  18.                 /* 获取启动命令 */
  19.                 s = getenv ("bootcmd");
  20.        if (!s) {
  21.             /* 如果没有启动命令,就设置默认的命令 */
  22. #ifdef CONFIG_ROOTFS_FLASH
  23.            /* XXX if rootfs is in flash, expect uImage to be in flash */
  24. #ifdef CONFIG_AR7100
  25.            /* 这里设置的即为 AR71XX 的默认固件启动命令 */
  26.            setenv ("bootcmd", "bootm 0xbf200000");
  27. #else
  28.            setenv ("bootcmd", "bootm 0xbf450000");
  29. #endif /* CONFIG_AR7100 */
  30. #else
  31.            setenv ("bootcmd", "tftpboot 0x8022c090 uImage; bootm 0x8022c090");
  32. #endif
  33.        }
  34.                 /* 重新获取启动命令 */
  35.                 s = getenv ("bootcmd");

  36. //        debug ("### main_loop: bootcmd="%s"\n", s ? s : "<UNDEFINED>");

  37.         if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
  38. # ifdef CONFIG_AUTOBOOT_KEYED
  39.                 int prev = disable_ctrlc(1);        /* disable Control C checking */
  40. # endif

  41. # ifndef CFG_HUSH_PARSER
  42.                 /* 执行启动命令 */
  43.                 run_command (s, 0);
  44. # else
  45.                 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
  46.                                     FLAG_EXIT_FROM_LOOP);
  47. # endif

  48. # ifdef CONFIG_AUTOBOOT_KEYED
  49.                 disable_ctrlc(prev);        /* restore Control C checking */
  50. # endif
  51.         }

  52. ----------------------------------------------------省略----------------------------------------------------
  53. #endif        /* CONFIG_BOOTDELAY */

  54. #ifdef CONFIG_AMIGAONEG3SE
  55.         {
  56.             extern void video_banner(void);
  57.             video_banner();
  58.         }
  59. #endif

  60.         /* 启动命令执行完毕,则开始处理交互命令 */

  61.         /*
  62.          * Main Loop for Monitor Command Processing
  63.          */
  64. #ifdef CFG_HUSH_PARSER
  65.         parse_file_outer();
  66.         /* This point is never reached */
  67.         for (;;);
  68. #else
  69.         for (;;) {
  70. #ifdef CONFIG_BOOT_RETRY_TIME
  71.                 if (rc >= 0) {
  72.                         /* Saw enough of a valid command to
  73.                          * restart the timeout.
  74.                          */
  75.                         reset_cmd_timeout();
  76.                 }
  77. #endif
  78.                 len = readline (CFG_PROMPT);

  79.                 flag = 0;        /* assume no special flags for now */
  80.                 if (len > 0)
  81.                         strcpy (lastcommand, console_buffer);
  82.                 else if (len == 0)
  83.                         flag |= CMD_FLAG_REPEAT;
  84. #ifdef CONFIG_BOOT_RETRY_TIME
  85.                 else if (len == -2) {
  86.                         /* -2 means timed out, retry autoboot
  87.                          */
  88.                         puts ("\nTimed out waiting for command\n");
  89. # ifdef CONFIG_RESET_TO_RETRY
  90.                         /* Reinit board to run initialization code again */
  91.                         do_reset (NULL, 0, 0, NULL);
  92. # else
  93.                         return;                /* retry autoboot */
  94. # endif
  95.                 }
  96. #endif

  97.                 if (len == -1)
  98.                         puts ("<INTERRUPT>\n");
  99.                 else
  100.                         rc = run_command (lastcommand, flag);

  101.                 if (rc <= 0) {
  102.                         /* invalid command or not repeatable, forget it */
  103.                         lastcommand[0] = 0;
  104.                 }
  105.         }
  106. #endif /*CFG_HUSH_PARSER*/
  107. }
复制代码

至此,U-Boot 的启动流程就分析完毕了


相关代码位置:

AR7241 架构代码:

board/ar7240/ap99:
ap99.c 包含 USB 初始化等

board/ar7240/common:
ar7240_flash.c 包含 Flash 初始化代码
ar7240_pci.c 包含 PCI-E 设备初始化代码
ar7240_s26_phy.c:AR724X/AR9331 内置交换机初始化代码
lowlevel_init.S:lowlevel_init 函数所在的代码,用于初始化 CPU 频率

cpu/mips/ar7240:
ag7240.c:网络驱动
ar7240_serial.c:串口初始化及操作代码
meminit.c:内存初始化代码

U-Boot 通用代码:
common:
cmd_*.c: U-Boot 命令代码
env_*.c:环境变量底层模块
command.c:命令解释模块
console.c:控制台模块
dlmalloc.c:内存管理模块
environment.c:环境变量公用模块

net:
eth.c:网络初始化模块
net.c:网络功能实现及调度模块
tftp.c:tftp 功能模块

点评

您好! init_fnc_t *init_sequence[] = { #ifndef COMPRESSED_UBOOT timer_init, #endif env_init, /* initialize environment */ #ifdef CONFIG_INCA_IP incaip_set_cpuclk, /* set cpu clock according  详情 回复 发表于 2015-5-11 23:56
这个真不是一般人能学会的,有点复杂。看了头疼。  发表于 2015-1-22 23:14
这太厉害了 代码也解析了一遍  详情 回复 发表于 2014-11-18 16:48

评分

参与人数 2恩山币 +6 收起 理由
laoma348 + 5 ganxiegongxiang
moontide + 1 霸道

查看全部评分

我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

 楼主| 发表于 2014-5-1 01:20 | 显示全部楼层
再占一楼
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 02:10 | 显示全部楼层
LZ好霸道,一下占那么多楼
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 02:12 | 显示全部楼层
楼主,伟大!非常非常感谢!
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 08:56 | 显示全部楼层
LZ厉害
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 09:00 来自手机 | 显示全部楼层
牛逼。。。。
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 09:13 | 显示全部楼层
教程终于出来了支持支持
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 09:57 来自手机 | 显示全部楼层
多谢楼主,好贴!
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 10:13 来自手机 | 显示全部楼层
支持楼主
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 10:21 | 显示全部楼层
坐等更新
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 10:26 来自手机 | 显示全部楼层
我按教程搭好了环境(ubuntu 13.04,操作系统64位),准备再跟。
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 10:47 来自手机 | 显示全部楼层
感谢楼主分享
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

发表于 2014-5-1 10:50 | 显示全部楼层
感谢楼主做出杰出贡献
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

有疑问请添加管理员QQ86788181|手机版|小黑屋|Archiver|恩山无线论坛(常州市恩山计算机开发有限公司版权所有) ( 苏ICP备05084872号 )

GMT+8, 2024-4-24 06:30

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

| 江苏省互联网有害信息举报中心 举报信箱:js12377 | @jischina.com.cn 举报电话:025-88802724 本站不良内容举报信箱:68610888@qq.com 举报电话:0519-86695797

快速回复 返回顶部 返回列表