View on GitHub

富乎 · 地问


avatar
辗转探寻为富乎?《天问》无解向地问!

<<< 返回主页

姗姗来迟的U-Boot移植

1、迟到原因

2、基础版移植

2.1 获取源码包

2.2 定制编译配置

2.3 修改板级头文件

3、进阶版移植

前面的基础版移植只是实现了最简单的引导逻辑。在实际的项目中,通常还要做更多的移植, 使调试和日常使用更加便利。

3.1 修改以太网络(方便以网络形式调试Linux系统和根文件系统)

3.2 支持可读性更好的储存分区管理(即mtdparts

3.3 其他特定项目所需的修改

例如:储存卡、显示屏、蓝牙、无线网络等,都是可选配置,视实际项目的需要而增减, 若有配备则需要移植,否则不用理会,在此不能一一列举讲述,有需要者可自行搜索其移植方法。

4、独树一帜的项目管理

4.1 一般做法带来的问题

4.2 改进做法

5、踩雷与排雷实录

5.1 不当地启用或禁用Driver Model选项引起的编译错误

Driver Model驱动模型,目的在于为驱动的定义和访问接口提供统一的方法, 以提高驱动间的兼容性以及访问的标准性,其与Linux内核的设备驱动模型类似, 但也有所区别,由于不是本文重点就不展开叙述了。以下是部分踩雷与排雷示例 (menuconfig一级配置项均是Device Drivers):

若开启了在启动期间显示CPU信息的功能,则同时需要开启以下选项:

[*] Enable CPU drivers using Driver Model

否则会报以下错误:

cmd/cpu.c:37:对‘cpu_get_info’未定义的引用

对于以太网口,则不能开启以下选项:

[ ] Enable Driver Model for Ethernet drivers

否则会报以下错误:

drivers/net/fec_mxc.c:538:47: 错误: dereferencing pointer to incomplete type ‘struct eth_device’

不过,这个问题只在较旧版本的uboot才出现。在v2020.07之后的版本, struct eth_device及相关接口已移除,全面拥抱Driver Model机制。

对于USB,则不能开启以下选项:

[*] USB support  --->
    [ ]   Enable driver model for USB

否则会报与设备树相关接口的错误:

drivers/usb/host/usb-uclass.c:696: undefined reference to `fdtdec_get_int'

若有其他类似报错,可先定位报错信息所在的文件和行数,再查找其相关的CONFIG_*宏, 并复制此宏名称到menuconfig界面去查找,最后再开启或禁用相关的配置项,一般就能解决报错了。

5.2 saveenv配置未改全而导致写过界事故

在前面基础版移植一章中提到,若只修改uboot分区大小而忘记同步修改环境变量储存区偏移量的话, 会导致saveenv写过界,例如:

i.MX6ULL > saveenv 
Saving Environment to NAND...
Erasing NAND...
Erasing at 0x3c00000 -- 100% complete.
Writing to NAND... OK

注意上面的0x3c00000对应60M的位置,在本项目中则落在根文件系统分区内, 就是说会破坏根文件系统,后续重启会卡住。除了要修正偏移量重新编译uboot, 根文件系统也要单独修复。由于前面已经移植好USB,所以可以将根文件系统镜像放到U盘里, 然后直接在uboot命令行进行烧写,过程样例如下(注意镜像后缀是.ubi而非.ubifs, 镜像生成方法可参考《Buildroot及BusyBox深度排雷》 一文“2.8 bad VID header offset”小节):

i.MX6ULL > usb start
starting USB...
USB0:   USB EHCI 1.00
scanning bus 0 for devices... 2 USB Device(s) found
USB1:   USB EHCI 1.00
scanning bus 1 for devices... 1 USB Device(s) found
       scanning usb for storage devices... 1 Storage Device(s) found
       scanning usb for ethernet devices... 0 Ethernet Device(s) found
i.MX6ULL > usb dev

USB device 0: Vendor: Mass     Rev: 1.00 Prod: Storage Device
            Type: Removable Hard Disk
            Capacity: 32000.0 MB = 31.2 GB (65536000 x 512)
i.MX6ULL > usb dev 0

USB device 0:
    Device 0: Vendor: Mass     Rev: 1.00 Prod: Storage Device
            Type: Removable Hard Disk
            Capacity: 32000.0 MB = 31.2 GB (65536000 x 512)
... is now current device
i.MX6ULL > fatls usb 0
 29966336   rootfs.ubifs
 30801920   rootfs.ubi

2 file(s), 0 dir(s)

i.MX6ULL > fatload usb 0 ${loadaddr} rootfs.ubi
reading rootfs.ubi
31195136 bytes read in 3081 ms (9.7 MiB/s)
i.MX6ULL > printenv filesize
filesize=1dc0000
i.MX6ULL > nand device

Device 0: nand0, sector size 128 KiB
  Page size       2048 b
  OOB size         128 b
  Erase size    131072 b
  subpagesize     2048 b
  options     0x40000200
  bbt options 0x    8000
i.MX6ULL > nand device 0
i.MX6ULL > nand erase 0x1900000 0x4900000

NAND erase: device 0 offset 0x1900000, size 0x4900000
Erasing at 0x61e0000 -- 100% complete.
OK
i.MX6ULL > nand write ${loadaddr} 0x1900000 ${filesize}

NAND write: device 0 offset 0x1900000, size 0x1dc0000
 31195136 bytes written: OK
i.MX6ULL > reset
resetting ...

5.3 用kobs-ng命令烧写uboot的注意事项

除了可用厂商提供的工具软件并借助储存卡来进行整个系统的烧录,还可以在操作系统里单独烧uboot, 不过需要在制作根文件系统时安装好kobs-ng命令,并且要遵循一定的流程,如下:

$ mount -t debugfs debugfs /sys/kernel/debug # 获取NAND的BCH布局
$
# 注:uboot在mtd的第几个分区与前面的mtdparts划分情况有关,一般分在最开头,即第0个分区。
$ flash_erase /dev/mtd0 0 0
$
$ kobs-ng init -x -v --chip_0_device_path=/dev/mtd0 /path/to/u-boot.imx # 烧写
$
$ sync # 确保数据落盘

最后要注意的是,重启时不能直接以reboot命令重启只能按硬件复位键进行硬复位! 否则,会卡住,并且无法重启,只能使用储存卡重烧整个系统。

5.4 开启tftpput命令时导致不能使用缩写命令tftp

uboot的命令支持缩写形式,例如tftpboot可缩写为tftp,但仅限于无歧义的情况下。 当勾选以下配置项时,将多了一个tftpput命令,其与tftpboot均与tftp开头, 将会导致tftp缩写不能使用:

Command line interface  --->
    Network commands  --->
        [*] tftp put

所以必须取消tftpput命令的勾选,反正一般情况下用不上,最好确保相邻的tftpsrv也取消掉。

5.5 以太网口能ping通但tftp下载失败

uboot命令行下通过tftp命令下载文件时曾出现以下现象:

Loading: #error frame: 0x9ee55180 0x00000804
T error frame: 0x9ee551c0 0x00000804
T error frame: 0x9ee551c0 0x00000804
T error frame: 0x9ee551c0 0x00000804
T error frame: 0x9ee55200 0x00000804
T T error frame: 0x9ee55200 0x00000804
T #error frame: 0x9ee55240 0x00000804
T error frame: 0x9ee55240 0x00000804
T error frame: 0x9ee55240 0x00000804
T #error frame: 0x9ee55280 0x00000804

需要注意下载进度显示中,用T表示等待超时或网络不通,用#表示正常接收数据, 而以上内容两种符号都出现过,况且又能ping通,说明网络是通的,只是数据接收极不稳定。 本来这种情况比较难排查,但由于之前做Linux内核移植时,曾遇过网络通但SSH操作很卡的情况, 最终在搜索各种方案以及对比多个项目的设备树配置时,发现了时钟引脚的电气配置有点跷蹊, 最终也证实了官方(即NXP)的配置有点问题,而某点原子的配置竟然可用,从而解决了问题。 所以,这次我也往这个方向排查,没想到又是同一个问题!

时钟引脚的电气配置详见3.1小节。这里需要提一下的是, 最终配置与官方配置的差异仅表现在引脚输出能力上,即电阻大小: 可用的值是PAD_CTL_DSE_240ohm,官方的则是PAD_CTL_DSE_40ohm。 由于这完全是硬件层面的问题,所以我也只能作一个不成熟的猜想,就是:官方配置的电阻值过小, 从而导致驱动电流过大,信号边沿升降时间很短,电磁干扰Electromagnetic InterferenceEMI)也强, 所以对数据包的收发造成干扰。若说错则请对这方面有了解的朋友指正。

5.6 内存不对齐引发的数据中断异常

报错现象类似下面这样:

Load address: 0x83000000
Loading: data abort
pc : [<9ff889e8>]          lr : [<9ff88a90>]
reloc pc : [<878429e8>]    lr : [<87842a90>]
sp : 9ee45b70  ip : 000000eb     fp : 00000045
r10: 00000b0f  r9 : 9ee45e80     r8 : 9ffef1f0
r7 : 00000f0b  r6 : 00004500     r5 : 0000002f  r4 : 9ffed38e
r3 : 14000045  r2 : 6f6fa8c0     r1 : 9ee45b78  r0 : 9ffed38e
Flags: nzCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

resetting ...

原因是启动初始化代码对CPU设置了内存对齐的检查,并且在运行过程中(网络收包时)检测到使用非对齐的内存地址的操作, 于是引发了异常而重置。本来应该修改相关的网络驱动代码,但若时间比较紧迫,或者要修改的范围太大, 则可以临时禁用这项检查,等以后有时间再慢慢改,或者等待修复这类缺陷(若是uboot自身缺陷的话)的新版本uboot发布。 禁用的方法是,在arch/arm/cpu/armv7/start.S将对应的设置语句改成清零,目标语句及相邻代码如下:

113	ENTRY(cpu_init_cp15)
114		/*
115		 * Invalidate L1 I/D
116		 */
117		mov	r0, #0			@ set up for MCR
118		mcr	p15, 0, r0, c8, c7, 0	@ invalidate TLBs
119		mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
120		mcr	p15, 0, r0, c7, c5, 6	@ invalidate BP array
121		mcr     p15, 0, r0, c7, c10, 4	@ DSB
122		mcr     p15, 0, r0, c7, c5, 4	@ ISB
123	
124		/*
125		 * disable MMU stuff and caches
126		 */
127		mrc	p15, 0, r0, c1, c0, 0
128		bic	r0, r0, #0x00002000	@ clear bits 13 (--V-)
129		bic	r0, r0, #0x00000007	@ clear bits 2:0 (-CAM)
130	#if 0
131		orr	r0, r0, #0x00000002	@ set bit 1 (--A-) Align    /* 原先是开启对齐检查 */
132	#else
133		bic	r0, r0, #0x00000002	@ clear bit 1 (--A-) Align  /* 现在要禁用 */
134	#endif

6、总结