【Linux】基于Exynos4412的U-Boot引导程序移植

【Linux】基于Exynos4412的U-Boot引导程序移植

零、准备

首先我们得去下载好U-Boot的源码,因为用的芯片是2012年出的Exynos4412,因此我们选择这个时间节点附近的U-Boot版本。本文选择U-Boot的v2013.01.01版本。

U-Boot官方网站:www.u-boot.org/ U-Boot下载页面:ftp.denx.de/pub/u-boot/ U-Boot-v2013.01.01版本下载页面:source.denx.de/u-boot/u-bo... U-Boot-v2013.01.01版本下载链接:ftp.denx.de/pub/u-boot/...

下载后得到如下文件:

另外我们需要一套ARM的交叉编译工具链,交叉编译工具本文不介绍。

壹、初次编译

1. 解压

使用如下命令解压:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot$ tar -vxf u-boot-2013.01.01.tar.bz2

得到如下文件夹:

进入到u-boot-2013.01.01文件夹中去,准备移植工作。

2. 设置编译器

我们在编译U-Uoot源码之前需要指定我们使用的处理器架构和编译器,指定的方式有两种,一种是通过make命令参数临时指定,一种是修改Makefile文件永久指定,两种方式各有优缺点。 因为我们是针对于ARM处理器的移植,所以选择修改Makefile更方便些。

使用如下命令修改Makefile文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi Makefile

将185行的变量CROSS_COMPILE直接指定为我们交叉编译工具的前缀arm-none-linux-gnueabi-,注意后面不要有空格。 修改好后保存退出。

3. 添加板子(Board)信息

每个人使用的开发板都有可能不同,U-Boot不可能支持所有的开发板,故有时候需要我们自己在U-Boot中添加对我们板子的支持。 U-Boot中支持了一部分板子,为了减少工作量提高效率,我们可以选择一款与我们板子相近的板子作为基础,我们可以在此基础之上进行针对我们板子的修改。 这边我选择的是Samsung公司的Origen。

3.1

使用如下命令把Origen的代码复制一份并且重命名:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ cp -rf board/samsung/origen/ board/samsung/ex4412                  # 复制源码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ mv board/samsung/ex4412/origen.c board/samsung/ex4412/ex4412.c     # 重命名     
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ cp include/configs/origen.h include/configs/ex4412.h               # 复制头文件
3.2

复制代码后,我们还需要对Makefile文件进行修改,让我们复制出来的代码能够正常编译。

使用如下命令修改Makefile文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi board/samsung/ex4412/Makefile

把31行的COBJS += origen.o改为COBJS += ex4412.o后保存退出。

3.3

我们还需要让我们的代码能够被编译进U-Boot中,即添加U-Boot对我们板子的支持,类似于在U-Boot中注册我们的板子。

使用如下命令编辑U-Boot的配置文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi boards.cfg

我们把我们参考的板子的配置信息复制一行,修改为我们的板子。修改完成后保存退出,这样我们就在U-Boot中注册了我们的板子了。

3.4

为了区别于其他板子的U-Boot程序,我们有时候需要在程序运行时输出一些提示信息,此类信息对程序运行影响不大,主要是供开发者调试,因此是可选的操作。

若要修改提示信息,可以使用如下命令编辑头文件中的宏定义:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi include/configs/ex4412.h

这里主要是104行的宏CONFIG_SYS_PROMPT和133行的宏CONFIG_IDENT_STRING,宏CONFIG_SYS_PROMPT定义的是U-Boot的系统提示,出现在命令行的前面,而宏CONFIG_IDENT_STRING定义的是U-Boot的识别字符串,出现在U-Boot输出内容的第一行。 修改完成后保存退出即可。

4. 第一次编译代码

4.1

首先我们要指定板子信息,使用如下命令指定:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ make ex4412_config

其中,ex4412为前面我们添加到boards.cfg文件中的Board name(第4)列的字符串。

4.2

然后,我们就可以开始编译U-Boot了! 使用如下命令开始编译U-Boot:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ make

好,我们遇到了一个错误。

4.3

打开错误中提到的文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi spl/u-boot-spl.lds

看样子是我们刚刚修改的代码出问题了,lds文件不支持//注释,因此我们重新修改刚刚的文件,把使用//的注释行改用/* */注释。

编辑我们的头文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi include/configs/ex4412.h

保存退出。

4.4

再重新编译:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ make

编译成功!

可以看到在U-Boot源码目录下生成了u-boot.bin文件:

5. 总结

本小节初步完成了对U-Boot的编译操作,认识到了lds文件不支持//注释,后面尽可能用/* */注释。但是仅是编译的话,U-Boot可能还不能在我们板子上正常运行,我们还得针对我们的板子做更多的优化,此小节算是我们的板子仅在U-Boot中注册了吧。

贰、移植

本小节不一定适用于所有人,移植操作需要针对目标板子做特别的操作,因此对大多数人来说,本小节能参考的只是移植的步骤。 每完成一个移植可以尝试编译运行检查错误,以免将来找错误困难,此步骤本文省略。

1. 集成板子加密引导代码

有时为保障芯片启动的安全性,其初始引导需要经过特定的加密处理后,才能引导运行U-Boot。因此,我们需在U-Boot源码中添加芯片公司提供的特定的加密处理代码。

本芯片的加密处理代码如下:

1.1

使用如下命令复制到U-Boot目录下:

bash 复制代码
yu@Yubuntu:~/ex4412/secure$ cp -rf CodeSign4SecureBoot ~/ex4412/uboot/u-boot-2013.01.01
yu@Yubuntu:~/ex4412/secure$ cp -rf sdfuse_q ~/ex4412/uboot/u-boot-2013.01.01
1.2

加密代码也需要被编译进U-Boot,故我们修改Makefile文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi Makefile

这段代码主要是对编译后的U-Boot可执行文件进行处理,先将其分割成固定大小的文件,然后编译sdfuse_q下的代码,运行的chksumadd_padding脚本。

1.3

在使用make命令编译时,默认情况下只会链接U-Boot源码里的相关代码到u-boot.bin中,而我们添加的初始引导加密代码不会被链接到u-boot.bin中。 为了解决这个问题,我们需要自行编写一个编译脚本把我们添加的初始引导加密代码链接到u-boot.bin中。

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ touch build.sh
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ chmod +x build.sh
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi build.sh

我们首先对U-Boot源码进行配置和编译,之后把初始引导加密代码链接到u-boot.bin上。最终,脚本会生成一个完整的U-Boot镜像:u-boot-ex4412.bin

1.4

运行结果: 成功生成u-boot-ex4412.bin~

2. 添加相关调试代码

为了方便我们识别问题,我们可以在U-Boot源码中针对我们的板子添加一段点亮LED的代码,这样U-Boot有没有在运行我们通过LED的状态就可以得知。

我们编辑U-Boot的启动代码,把点亮LED的代码加入其中:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi arch/arm/cpu/armv7/start.S

编辑完成后保存退出。

3. 实现串口输出

为了方便调试,我们需要针对我们的开发板调整UART的代码,让U-Boot支持我们开发板的串口功能。

编辑U-Boot处理器低级初始化代码文件lowlevel_init.S

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi board/samsung/ex4412/lowlevel_init.S

初始化临时栈 关掉看门狗 初始化串口 跳过初始化TrustZone保护控制器

4. 网卡移植

我们有时候要从电脑上通过网口下载一些程序到开发板上运行,因此需要让U-Boot支持我们开发板的网卡才行。

4.1

编辑我们的板级代码:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi board/samsung/ex4412/ex4412.c

初始化DM9000相关代码 调用DM9000初始化代码 网络初始化代码

保存退出~

4.2

编辑我们的板级代码头文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi include/configs/ex4412.h

配置支持ping命令和网络 配置网络相关的宏

保存退出~

5. eMMC移植

有时候我们需要设置U-Boot的环境变量来完成自动启动内核的操作,而U-Boot的环境变量要想下次启动时还有效就得保存在eMMC(或其他非易失性存储器)上,因此我们有必要让U-Boot支持我们的eMMC。

准备如下相关文件,这些文件改写于一些开源项目:

5.1

把这些文件复制到项目中去:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/eMMC$ cp movi.c ../u-boot-2013.01.01/arch/arm/cpu/armv7/exynos/
yu@Yubuntu:~/ex4412/uboot/eMMC$ cp cmd_movi.c cmd_mmc.c cmd_mmc_fdisk.c ../u-boot-2013.01.01/common/
yu@Yubuntu:~/ex4412/uboot/eMMC$ cp mmc.c s5p_mshc.c ../u-boot-2013.01.01/drivers/mmc/
yu@Yubuntu:~/ex4412/uboot/eMMC$ cp mmc.h movi.h s5p_mshc.h ../u-boot-2013.01.01/include/
5.2

然后修改对应的Makefile,让这些代码能被正确编译进U-Boot中:

5.2.1
bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi arch/arm/cpu/armv7/exynos/Makefile

movi.c编译进U-Boot,movi.c中实现的是从 eMMC复制U-Boot到内存的功能。

5.2.2
bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi common/Makefile

cmd_mmc_fdisk主要实现了对eMMC进行分区管理的功能,cmd_movi主要实现了对eMMC上的不同镜像进行读写操作的功能。

5.2.3
bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi drivers/mmc/Makefile

s5p_mshc是针对Exynos4412的MMC主机控制器驱动,提供的功能有初始化、时钟管理、数据传输准备、传输模式设置、命令发送和FIFO管理等,使得U-Boot能够与eMMC进行有效的通信和数据交互。

5.3

然后我们要修改板级文件,让开发板支持eMMC相关的操作:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi board/samsung/ex4412/ex4412.c
5.3.1

导入头文件。

5.3.2

这段代码的主要作用是对eMMC控制器的时钟和GPIO进行配置,并调用一些初始化函数,让eMMC设备能够正常工作。

5.3.3

调用刚刚写的eMMC初始化代码。

5.3.4

这段代码的主要是在系统启动后期检测启动设备(eMMC或SD卡),并输出相应的启动模式信息。

保存退出~

5.4

最后我们需要修改配置文件:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi include/configs/ex4412.h

一些eMMC相关的宏配置。

保存退出~

6. 电源管理移植

U-Boot源码里有关于电源管理芯片的代码与我们的板子是不匹配的,后续可能会造成内核启动时卡死。所以我们需要调整一下电源管理芯片相关的代码。

准备好芯片公司为我们提供的代码:

6.1

pmic_s5m8767.c复制到U-Boot源码中去:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/pm$ cp pmic_s5m8767.c ../u-boot-2013.01.01/drivers/power/pmic/
6.2

修改Makefile文件,将pmic_s5m8767.c添加到编译列表中去:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi drivers/power/pmic/Makefile

保存退出~

6.3

把代码内相关的函数在头文件中声明:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi include/power/pmic.h

保存退出~

6.4

修改配置文件,启用相关代码:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi include/configs/ex4412.h

保存退出~

6.5

修改板级文件,在文件中添加电源相关的初始化代码:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi board/samsung/ex4412/ex4412.c

保存退出~

6.6

禁用原有的电源管理:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi drivers/power/Makefile

保存退出~

6.7

在架构代码中添加对我们的电源管理代码的支持:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ vi arch/arm/cpu/armv7/s5p-common/cpu_info.c

保存退出~

叁、编译运行

完成上述移植相关代码的修改后,我们尝试编译到开发板上运行。

1. 编译

使用如下命令进行编译:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ ./build.sh

编译成功~

2. 制作启动镜像

原本u-boot-ex4412.bin应该是可以直接运行的,但是SD卡有对应的格式,我们需要在其前面加上512字节的空白内容。 使用如下命令制作SD卡格式的U-Boot程序镜像:

bash 复制代码
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ sudo dd if=/dev/zero of=blank.bin count=1
yu@Yubuntu:~/ex4412/uboot/u-boot-2013.01.01$ cat blank.bin u-boot-ex4412.bin > u-boot-ex4412-sd.bin

3. 在开发板上运行

将上一步得到的u-boot-ex4412-sd.bin,从SD卡的0扇区开始连续写入,完成后把SD卡插到开发板上,设置开发板从SD卡启动。 能够正常运行,但是有问题,help命令都提示找不到。

4. 解决问题

可看我的另外一篇博客:《【U-Boot】解决U-Boot的"Unknown command 'help' - try 'help'"问题》

4.1 解释与分析

经研究与common/command.cinclude/u-boot.lstu-boot.map相关的文件代码发现,是helper.mk中的排序出了问题。 其中$(1)$(2)传入的分别是include/u-boot.lst和编译要用到的一些.o文件。

1.$(1): $(2)表示$(1)依赖于$(2)$(2)中的内容变了之后$(1)要重新生成。

2.$(OBJDUMP) -h $(2)表示用objdump工具把传入的所有.o文件的段信息输出。

3. 接下来是使用sed(Stream Editor:流编辑器)命令对刚刚输出的段信息进行编辑,所有的-n表示启用安静模式,即仅打印显式指定的行。

(1). -e '/.*\.u_boot_list[^ ]\+/ ! {d;n}'表示对不符合正则表达.*.u_boot_list[^ ]+的行删除掉,即只要包含有u_boot_list的行。

示例: 4 .u_boot_list.cmd.bdinfo 00000018 00000000 00000000 00016484 2**2

(2). -e 's/.*\(\.u_boot_list[^ ]\+\).*$$$$/\1/'表示只保留中间的.u_boot_list[^ ]+部分。

  • s/a/b表示把a替换成b
  • $$$$实际在正则表达式中为$makefile替换后变成$$sed替换后变成$,表示行尾;
  • \1表示正则表达式捕获到的expr子模式,即括号里的内容。

示例:.u_boot_list.cmd.bdinfo

(3). -e 's/\.[^\.]\+$$$$//'表示把符合正则表达式.[^.]+$的部分替换为空字符串,即删除掉。

示例:.u_boot_list.cmd

(4). -e ':s /^.\+$$$$/ { p;s/^\(.*\)\.[^\.]*$$$$/\1/;b s }'表示提取所有"上级域名",有点复杂。

  • :s表示标签,后面b s表示跳转回s的位置,这样可以达到循环处理的目的。
  • ^.\+$$$$表示正则表达式^.+$,匹配非空行。
  • {...}是命令块,表示在非空行上进行这样的处理。
  • 命令块中p表示打印当前行。
  • ^\(.*\)\.[^\.]*$$$$表示正则表达式^(.*).[^.]*$,配合替换命令即表示删除最后一个点及这个点之后的数据。

总结就是把.a.b这样的转换成.a.a.b两行。

示例:.u_boot_list.cmd.u_boot_list

(5). -e 'h;s/$$$$/\a/p;g;s/$$$$/@/p;g;s/$$$$/~/p;'表示在行后面分别加上\a@~

  • h表示将当前行的内容复制到保持空间方便后面使用。
  • $$$$表示正则表达式$,匹配行尾,则s/$$$$/\a/表示在行尾加上\a
  • p表示打印替换后的行,即打印当前行 + \a
  • g表示从保持空间中恢复,即恢复到在行尾加\a之前的状态。

后面就是在原始行行尾后加@~,然后打印。

示例:.u_boot_list.cmd\a.u_boot_list.cmd@.u_boot_list.cmd~.u_boot_list\a.u_boot_list@.u_boot_list~

4. 接下来的LC_COLLATE=C sort -u是对之前的数据进行去重和排序,问题就出现在这里,我们来分析一下:

  • LC_COLLATE=C表示设置字符排序规则为C locale,即基于ASCII二进制值的排序。
  • sort -u表示排序和去重。

接着上面"3"中的示例,得到的结果如下:

lst 复制代码
.u_boot_list 
.u_boot_list@
.u_boot_list~
.u_boot_list.cmd 
.u_boot_list.cmd@
.u_boot_list.cmd~

这个顺序就有问题,见我另外一篇博客的结论: 因此,我们很容易想到一个解决方案,我们希望把前三行和后三行,或者说三行为一个整体进行倒序以解决问题。

代码要整体分析,我们先看最后一个sed语句。

5. 最后一个sed语句是对之前行尾的\a@~进行替换,以完成一个完整的lst文件。

  • -e '/\a$$$$/ { s/\./_/g;s/\a$$$$/__start = .;/p; }'表示在以\a结尾的行中,把.替换成_g表示全部替换),把行尾的\a替换成__start = .;并打印。
  • -e '/~$$$$/ { s/\./_/g;s/~$$$$/__end = .;/p; }''表示在以~结尾的行中,把.替换成_g表示全部替换),把行尾的~替换成__end = .;并打印。
  • -e '/@$$$$/ { s/\(.*\)@$$$$/*(SORT(\1.*));/p }'表示在以@结尾的行中,把除了@的部分放入*(SORT(\1.*));中(替换\1)并打印。

接着上面"4"中的示例,得到的结果如下:

lst 复制代码
_u_boot_list__start = .;
*(SORT(.u_boot_list.*));
_u_boot_list__end = .;
_u_boot_list_cmd__start = .;
*(SORT(.u_boot_list.cmd.*));_u_boot_list_cmd__end = .;

可以发现,是有顺序的,必须是"start"、"SORT"和"end",因此我们需要对三行整体考虑。

4.2 解决问题

我们只需要达到三行一个单位倒序的目的即可。

编辑helper.mk文件: 保存退出~

然后我们还要改Makefile文件,把调用的函数改改: 改成调用我们刚刚修改好的make_u_boot_list_reverse,保存退出~

5. 再次运行

重新编译并制作SD卡镜像:

写入到SD卡上再在开发板上运行: 成功~

再看看能正常从TFTP服务器上下载并启动Linux不: 一切顺利~


至此,U-Boot成功移植~

肆、参考资料:

  1. www.jyshare.com/front-end/8...
  2. www.cnblogs.com/minuhy/p/18...
  3. lxblog.com/qianwen/sha...
  4. blog.csdn.net/darnell888/...
  5. github.com/u-boot/u-bo...
  6. www.runoob.com/linux/linux...
  7. www.runoob.com/linux/linux...
  8. www.doubao.com/thread/w40d...
  9. www.doubao.com/thread/w7d9...
  10. www.doubao.com/thread/wd0b...
  11. www.doubao.com/thread/wc35...
  12. www.doubao.com/thread/wdda...
相关推荐
王闯写bug1 小时前
linux一次启动多个jar包
linux·jar
脑子慢且灵1 小时前
计算机操作系统——存储器管理
linux·服务器·windows·操作系统·存储器·存储器多级结构
小码过河.2 小时前
CentOS 安装 Docker
linux·docker·centos
HappyGame022 小时前
Linux命令-vim编辑
linux·vim
笑远2 小时前
Vim/Vi 常用命令速查手册
linux·编辑器·vim
撒旦骑路西法,大战吕布2 小时前
如果你在使用 Ubuntu/Debian:使用 apt 安装 OpenSSH
linux·ubuntu·debian
_李筱夜3 小时前
ubuntu桌面版使用root账号进行登录
linux·ubuntu
laimaxgg4 小时前
Dockerfile
linux·运维·服务器·ubuntu·docker
GalaxyPokemon4 小时前
LINUX基础 [四] - Linux工具
linux·运维
与passion共存5 小时前
Linux系统下Docker安装
linux·docker