Rockchip RK3399 - 移植uboot 2023.04和内核FIT uImage制作
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4开发板
eMMC :16GB
LPDDR3:4GB
显示屏 :15.6 HDMI接口显示屏
u-boot :2023.04
linux :5.2.8
----------------------------------------------------------------------------------------------------------------------------
在前面我们已经介绍了编译Rockchip官方提供的uboot源码,并下载到开发板中进行测试运行。这一节我们尝试下载最新的uboot版本试试,当前最新版本为2023.04。
一、uboot
1.1 下载源码
u-boot软件包下载网站:https://ftp.denx.de/pub/u-boot/。
DENX相关的网站:http://www.denx.de/re/DPLG.html。
u-boot git仓库:https://gitlab.denx.de/u-boot/u-boot。
我们在ubuntu运行如下命令:
root@zhengyang:/work/sambashare/rk3399# wget https://ftp.denx.de/pub/u-boot/u-boot-2023.04.tar.bz2
解压:
root@zhengyang:/work/sambashare/rk3399# tar -jxf u-boot-2023.04.tar.bz2
进入到uboot文件夹里,这就是我们需要的uboot的源码了:
root@zhengyang:/work/sambashare/rk3399# cd u-boot-2023.04 root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ll 总用量 484 drwxrwxr-x 25 root root 4096 Apr 4 04:38 ./ drwxr-xr-x 14 root root 4096 May 21 15:22 ../ drwxrwxr-x 2 root root 4096 Apr 4 04:38 api/ drwxrwxr-x 14 root root 4096 Apr 4 04:38 arch/ -rw-rw-r-- 1 root root 21095 Apr 4 04:38 .azure-pipelines.yml drwxrwxr-x 177 root root 4096 Apr 4 04:38 board/ drwxrwxr-x 2 root root 4096 Apr 4 04:38 boot/ -rw-rw-r-- 1 root root 814 Apr 4 04:38 .checkpatch.conf drwxrwxr-x 10 root root 4096 Apr 4 04:38 cmd/ drwxrwxr-x 5 root root 4096 Apr 4 04:38 common/ -rw-rw-r-- 1 root root 2180 Apr 4 04:38 config.mk drwxrwxr-x 2 root root 57344 Apr 4 04:38 configs/ drwxrwxr-x 2 root root 4096 Apr 4 04:38 disk/ drwxrwxr-x 20 root root 4096 Apr 4 04:38 doc/ drwxrwxr-x 74 root root 4096 Apr 4 04:38 drivers/ drwxrwxr-x 2 root root 4096 Apr 4 04:38 dts/ drwxrwxr-x 2 root root 4096 Apr 4 04:38 env/ drwxrwxr-x 4 root root 4096 Apr 4 04:38 examples/ drwxrwxr-x 15 root root 4096 Apr 4 04:38 fs/ -rw-rw-r-- 1 root root 44 Apr 4 04:38 .get_maintainer.conf -rw-rw-r-- 1 root root 207 Apr 4 04:38 .gitattributes drwxrwxr-x 2 root root 4096 Apr 4 04:38 .github/ -rw-rw-r-- 1 root root 1115 Apr 4 04:38 .gitignore -rw-rw-r-- 1 root root 14133 Apr 4 04:38 .gitlab-ci.yml drwxrwxr-x 36 root root 20480 Apr 4 04:38 include/ -rw-rw-r-- 1 root root 783 Apr 4 04:38 Kbuild -rw-rw-r-- 1 root root 20750 Apr 4 04:38 Kconfig drwxrwxr-x 24 root root 4096 Apr 4 04:38 lib/ drwxrwxr-x 2 root root 4096 Apr 4 04:38 Licenses/ -rw-rw-r-- 1 root root 4022 Apr 4 04:38 .mailmap -rw-rw-r-- 1 root root 42030 Apr 4 04:38 MAINTAINERS -rw-rw-r-- 1 root root 81547 Apr 4 04:38 Makefile drwxrwxr-x 2 root root 4096 Apr 4 04:38 net/ drwxrwxr-x 5 root root 4096 Apr 4 04:38 post/ -rw-rw-r-- 1 root root 94985 Apr 4 04:38 README -rw-rw-r-- 1 root root 491 Apr 4 04:38 .readthedocs.yml drwxrwxr-x 6 root root 4096 Apr 4 04:38 scripts/ drwxrwxr-x 17 root root 4096 Apr 4 04:38 test/ drwxrwxr-x 15 root root 4096 Apr 4 04:38 tools/
1.2 配置uboot
uboot的编译分为两步:配置、编译。单板的默认配置在configs目录下,这里我们直接选择configs/evb-rk3399_defconfig,这是Rockchip评估板的配置:
CONFIG_ARM=y CONFIG_SKIP_LOWLEVEL_INIT=y CONFIG_COUNTER_FREQUENCY=24000000 CONFIG_ARCH_ROCKCHIP=y CONFIG_TEXT_BASE=0x00200000 CONFIG_NR_DRAM_BANKS=1 CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x300000 CONFIG_ENV_OFFSET=0x3F8000 CONFIG_DEFAULT_DEVICE_TREE="rk3399-evb" CONFIG_DM_RESET=y CONFIG_ROCKCHIP_RK3399=y CONFIG_TARGET_EVB_RK3399=y CONFIG_SPL_STACK=0x400000 CONFIG_DEBUG_UART_BASE=0xFF1A0000 CONFIG_DEBUG_UART_CLOCK=24000000 CONFIG_SYS_LOAD_ADDR=0x800800 CONFIG_DEBUG_UART=y CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-evb.dtb" CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_SPL_MAX_SIZE=0x2e000 CONFIG_SPL_PAD_TO=0x7f8000 CONFIG_SPL_HAS_BSS_LINKER_SECTION=y CONFIG_SPL_BSS_START_ADDR=0x400000 CONFIG_SPL_BSS_MAX_SIZE=0x2000 # CONFIG_SPL_RAW_IMAGE_SUPPORT is not set # CONFIG_SPL_SHARES_INIT_SP_ADDR is not set CONFIG_SPL_STACK_R=y CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x10000 CONFIG_TPL=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_GPT=y CONFIG_CMD_MMC=y CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_TIME=y CONFIG_SPL_OF_CONTROL=y CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" CONFIG_ENV_IS_IN_MMC=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_ROCKCHIP_GPIO=y CONFIG_SYS_I2C_ROCKCHIP=y CONFIG_MISC=y CONFIG_MMC_HS400_SUPPORT=y CONFIG_MMC_DW=y CONFIG_MMC_DW_ROCKCHIP=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_SDMA=y CONFIG_MMC_SDHCI_ROCKCHIP=y CONFIG_SF_DEFAULT_SPEED=20000000 CONFIG_ETH_DESIGNWARE=y CONFIG_GMAC_ROCKCHIP=y CONFIG_PHY_ROCKCHIP_INNO_USB2=y CONFIG_PHY_ROCKCHIP_TYPEC=y CONFIG_PMIC_RK8XX=y CONFIG_REGULATOR_PWM=y CONFIG_REGULATOR_RK8XX=y CONFIG_PWM_ROCKCHIP=y CONFIG_DM_RNG=y CONFIG_RNG_ROCKCHIP=y CONFIG_BAUDRATE=1500000 CONFIG_DEBUG_UART_SHIFT=2 CONFIG_SYS_NS16550_MEM32=y CONFIG_SYSRESET=y CONFIG_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_DWC3=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_GENERIC=y CONFIG_USB_DWC3=y CONFIG_USB_DWC3_GENERIC=y CONFIG_USB_HOST_ETHER=y CONFIG_USB_ETHER_ASIX=y CONFIG_USB_ETHER_ASIX88179=y CONFIG_USB_ETHER_MCS7830=y CONFIG_USB_ETHER_RTL8152=y CONFIG_USB_ETHER_SMSC95XX=y CONFIG_VIDEO=y # CONFIG_VIDEO_BPP8 is not set CONFIG_DISPLAY=y CONFIG_VIDEO_ROCKCHIP=y CONFIG_VIDEO_ROCKCHIP_MAX_YRES=1200 CONFIG_DISPLAY_ROCKCHIP_MIPI=y CONFIG_SPL_TINY_MEMSET=y CONFIG_ERRNO_STR=y
因此执行如下命令,生成.config文件:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# make evb-rk3399_defconfig V=1
接着我们需要执行make menuconfig做一些配置。
1.2.1 配置串口波特率
uboot中默认的调试串口波特率是1500000,有很多的调试终端不支持1.5M的波特率,我们可以把波特率重新配置下;
Device Drivers ---> Serial ---> (115200) Default baudrate
注意:波特率数值如果无法删除,按CTRL+回车键尝试。如果配置为1500000,后面测试时串口输出内容一部分正常,偶尔出现乱码,考虑是串口波特率太高不稳定造成的,降低波特率到115200尝试。
这里我更改为了115200,之前烧录Rockchip官方uboot时设置为1500000一点问题都没有,但是uboot官方代码设置为1500000竟然出现乱码,看样uboot官方对RK3399支持并不是很好。
1.2.2 配置eMMC
为什么要去配置eMMC呢?这个是因为我使用默认配置,编译后的uboot下载到开发板出现了无法对eMMC进行读写的问题,并且输出了如下错误:
sdhci_transfer_data: Error detected in status(0x208000)
然后我去比对了当前版本uboot和Rockchip官方(Rockchip RK3399 - TPL/SPL方式加载uboot)提供的uboot单板配置configs/evb-rk3399_defconfig的差异,发现当前版本默认开启了以下配置:
CONFIG_MMC_HS400_SUPPORT=y # 多出了这个 CONFIG_MMC_DW=y CONFIG_MMC_DW_ROCKCHIP=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_SDMA=y # 多出了这个
那CONFIG_MMC_HS400_SUPPOR、CONFIG_MMC_SDHCI_SDMA是什么呢?
CONFIG_MMC_HS400_SUPPORT 是一个配置选项,用于支持嵌入式设备中的高速 MMC(Multimedia Card)/SD(Secure Digital)卡,默认情况下未启用。
启用这个选项可以让MMC/SD卡在HS400模式下运行,从而提高读写速度,但是需要确保硬件上支持 HS400 模式,并且芯片厂商提供了对应的驱动程序。如果硬件不支持或驱动程序不可用,则启用此选项将会导致系统无法正常启动。
既然我们已经明白了该配置项的作用,那我们就要看一下我们的eMMC芯片是否支持HS400模式,这里用的开发板板载的eMMC型号为:KLMAG2WEMB-B031,找到芯片的datasheet有关HS400 mode的信息,具体定位到EXT_CSD第196个字节DEVICE_TYPE,其值为0x57;可以看到eMMC工作在HS400模式时,电压要求为1.8V。
配置项CONFIG_MMC_SDHCI_SDMA用于启用SD/SDIO/MMC 主机控制器使用 DMA 方式进行数据传输。
这里我们暂且将CONFIG_MMC_HS400_SUPPORT,CONFIG_MMC_SDHCI_SDMA配置关掉:
Device Drivers ---> MMC Host controller Support ---> [ ] MMC debugging [ ] enable HS400 support [ ] Support IO voltage configuration [ ] Support SDHCI SDMA
如果想查看eMMC读写命令信息,可以打开MMC debuging。
1.2.3 配置FIT
Boot options ---> Boot images ---> [*] Use a script to generate the .its script (arch/arm/mach-rockchip/make_fit_atf.py) .its file generator script for U-Boot FIT image
我们之前介绍过在生成u-boot.itb文件时,需要执行如下命令:
tools/mkimage -f u-boot.its u-boot.itb
mkimage将its文件以及对应的image data file,打包成一个itb文件,也就是uboot可以识别的image file(FIT-uImage)。
因为mkimage是根据its文件中的描述来打包镜像生成itb文件(FIT-uImage),所以首先需要制作一个its文件,在its文件中描述需要被打包的镜像,主要是bl31.bin,dtb文件,u-boot-nodtb.bin。
这里我们需要使用到arch/arm/mach-rockchip/make_fit_atf.py文件,这个是从Rockchip官网uboot下拷贝过来的,内容如下,这是一个python脚本,主要就是生成一个u-boot.its文件:#!/usr/bin/env python2 """ A script to generate FIT image source for rockchip boards with ARM Trusted Firmware and multiple device trees (given on the command line) usage: $0 <dt_name> [<dt_name> [<dt_name] ...] """ import os import sys import getopt # pip install pyelftools from elftools.elf.elffile import ELFFile from elftools.elf.sections import SymbolTableSection from elftools.elf.segments import Segment, InterpSegment, NoteSegment ELF_SEG_P_TYPE='p_type' ELF_SEG_P_PADDR='p_paddr' ELF_SEG_P_VADDR='p_vaddr' ELF_SEG_P_OFFSET='p_offset' ELF_SEG_P_FILESZ='p_filesz' ELF_SEG_P_MEMSZ='p_memsz' DT_HEADER="""/* * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd * * Minimal dts for a SPL FIT image payload. * * SPDX-License-Identifier: GPL-2.0+ X11 */ /dts-v1/; / { description = "Configuration to load ATF before U-Boot"; #address-cells = <1>; images { uboot { description = "U-Boot (64-bit)"; data = /incbin/("u-boot-nodtb.bin"); type = "standalone"; os = "U-Boot"; arch = "arm64"; compression = "none"; load = <0x%08x>; hash { algo = "sha256"; }; }; """ DT_IMAGES_NODE_END=""" }; """ DT_END=""" }; """ def append_atf_node(file, atf_index, phy_addr): """ Append ATF DT node to input FIT dts file. """ data = 'bl31_0x%08x.bin' % phy_addr print >> file, '\t\tatf@%d {' % atf_index print >> file, '\t\t\tdescription = \"ARM Trusted Firmware\";' print >> file, '\t\t\tdata = /incbin/("%s");' % data print >> file, '\t\t\ttype = "firmware";' print >> file, '\t\t\tarch = "arm64";' print >> file, '\t\t\tos = "arm-trusted-firmware";' print >> file, '\t\t\tcompression = "none";' print >> file, '\t\t\tload = <0x%08x>;' % phy_addr if atf_index == 1: print >> file, '\t\t\tentry = <0x%08x>;' % phy_addr print >> file, '\t\t\thash {' print >> file, '\t\t\t\talgo = "sha256";' print >> file, '\t\t\t};' print >> file, '\t\t};' print >> file, '' def append_fdt_node(file, dtbs): """ Append FDT nodes. """ cnt = 1 for dtb in dtbs: dtname = os.path.basename(dtb) print >> file, '\t\tfdt {' print >> file, '\t\t\tdescription = "U-Boot device tree blob";' print >> file, '\t\t\tdata = /incbin/("u-boot.dtb");' print >> file, '\t\t\ttype = "flat_dt";' print >> file, '\t\t\tarch = "arm64";' print >> file, '\t\t\tcompression = "none";' print >> file, '\t\t\thash {' print >> file, '\t\t\t\talgo = "sha256";' print >> file, '\t\t\t};' print >> file, '\t\t};' print >> file, '' cnt = cnt + 1 def append_conf_section(file, cnt, dtname, atf_cnt): print >> file, '\t\tconfig {' print >> file, '\t\t\tdescription = "Rockchip armv8 with ATF";' print >> file, '\t\t\trollback-index = <0x0>;' print >> file, '\t\t\tfirmware = "atf@1";' print >> file, '\t\t\tloadables = "uboot",', for i in range(1, atf_cnt): print >> file, '"atf@%d"' % (i+1), if i != (atf_cnt - 1): print >> file, ',', else: print >> file, ';' print >> file, '\t\t\tfdt = "fdt";' print >> file, '\t\t\tsignature {' print >> file, '\t\t\t\talgo = "sha256,rsa2048";' print >> file, '\t\t\t\tpadding = "pss";' print >> file, '\t\t\t\tkey-name-hint = "dev";' print >> file, '\t\t\t\tsign-images = "fdt", "firmware", "loadables";' print >> file, '\t\t\t};' print >> file, '\t\t};' print >> file, '' def append_conf_node(file, dtbs, atf_cnt): """ Append configeration nodes. """ cnt = 1 print >> file, '\tconfigurations {' print >> file, '\t\tdefault = "config";' for dtb in dtbs: dtname = os.path.basename(dtb) append_conf_section(file, cnt, dtname, atf_cnt) cnt = cnt + 1 print >> file, '\t};' print >> file, '' def generate_atf_fit_dts(fit_file_name, bl31_file_name, uboot_file_name, dtbs_file_name): """ Generate FIT script for ATF image. """ if fit_file_name != sys.stdout: fit_file = open(fit_file_name, "wb") else: fit_file = sys.stdout num_load_seg = 0 p_paddr = 0xFFFFFFFF with open(uboot_file_name) as uboot_file: uboot = ELFFile(uboot_file) for i in range(uboot.num_segments()): seg = uboot.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): p_paddr = seg.__getitem__(ELF_SEG_P_PADDR) num_load_seg = num_load_seg + 1 assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1) print >> fit_file, DT_HEADER % p_paddr with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) for i in range(bl31.num_segments()): seg = bl31.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): paddr = seg.__getitem__(ELF_SEG_P_PADDR) p= seg.__getitem__(ELF_SEG_P_PADDR) append_atf_node(fit_file, i+1, paddr) atf_cnt = i+1 append_fdt_node(fit_file, dtbs_file_name) print >> fit_file, '%s' % DT_IMAGES_NODE_END append_conf_node(fit_file, dtbs_file_name, atf_cnt) print >> fit_file, '%s' % DT_END if fit_file_name != sys.stdout: fit_file.close() def generate_atf_binary(bl31_file_name): with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) num = bl31.num_segments() for i in range(num): seg = bl31.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): paddr = seg.__getitem__(ELF_SEG_P_PADDR) file_name = 'bl31_0x%08x.bin' % paddr with open(file_name, "wb") as atf: atf.write(seg.data()); def get_bl31_segments_info(bl31_file_name): """ Get load offset, physical offset, file size from bl31 elf file program headers. """ with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) num = bl31.num_segments() print 'Number of Segments : %d' % bl31.num_segments() for i in range(num): print 'Segment %d' % i seg = bl31.get_segment(i) ptype = seg[ELF_SEG_P_TYPE] poffset = seg[ELF_SEG_P_OFFSET] pmemsz = seg[ELF_SEG_P_MEMSZ] pfilesz = seg[ELF_SEG_P_FILESZ] print 'type: %s\nfilesz: %08x\nmemsz: %08x\noffset: %08x' % (ptype, pfilesz, pmemsz, poffset) paddr = seg[ELF_SEG_P_PADDR] print 'paddr: %08x' % paddr def main(): uboot_elf="./u-boot" bl31_elf="./bl31.elf" FIT_ITS=sys.stdout opts, args = getopt.getopt(sys.argv[1:], "o:u:b:h") for opt, val in opts: if opt == "-o": FIT_ITS=val elif opt == "-u": uboot_elf=val elif opt == "-b": bl31_elf=val elif opt == "-h": print __doc__ sys.exit(2) dtbs = args #get_bl31_segments_info("u-boot") #get_bl31_segments_info("bl31.elf") generate_atf_fit_dts(FIT_ITS, bl31_elf, uboot_elf, dtbs) generate_atf_binary(bl31_elf); if __name__ == "__main__": main()
拷贝命令过来:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# cp ../u-boot/arch/arm/mach-rockchip/make_fit_atf.py ./arch/arm/mach-rockchip/
1.2.4 配置uboot启动倒计时
如果在uboot启动倒计时结束之前,没有按下任何键,将会执行那么将执行也就是bootcmd中配置中的命令,bootcmd中保存着默认的启动命令。
(5) delay in seconds before automatically booting
保存文件,输入文件名为evb-rk3399_defconfig ,在当前路径下生成evb-rk3399_defconfig ,存档:
root@zhengyang:/work/sambashare/rk3399/u-boot# mv evb-rk3399_defconfig ./configs/
注意:如果需要配置生效,需要使用make distclean清除之前的配置,重新执行配置命令。
1.3 编译uboot
执行make命令,生成u-boot文件:
root@zhengyang:/work/sambashare/rk3399/u-boot# make ARCH=arm CROSS_COMPILE=arm-linux-
然后出现了如下错误:
binman: Filename 'atf-bl31' not found in input path (.,.,./board/rockchip/evb_rk3399,arch/arm/dts) (cwd='/work/sambashare/rk3399/u-boot-2023.04')
大致意思就是缺少了bl31.elf文件,我们将Rockchip RK3399 - TPL/SPL方式加载uboot章节制作的bl31.elf拷贝到uboot根目录下:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# cp /work/sambashare/rk3399/arm-trusted-firmware/build/rk3399/release/bl31/bl31.elf ./ root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# cp bl31.elf atf-bl31
重新编译,可以看到在 ./spl、./tpl目录下生成镜像文件:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ls tpl/u-boot* tpl/u-boot.cfg tpl/u-boot-tpl tpl/u-boot-tpl.dtb tpl/u-boot-tpl.map tpl/u-boot-tpl.sym tpl/u-boot-spl.lds tpl/u-boot-tpl.bin tpl/u-boot-tpl-dtb.bin tpl/u-boot-tpl-nodtb.bin root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ls spl/u-boot* spl/u-boot.cfg spl/u-boot-spl.bin spl/u-boot-spl-dtb.bin spl/u-boot-spl.map spl/u-boot-spl.sym spl/u-boot-spl spl/u-boot-spl.dtb spl/u-boot-spl.lds spl/u-boot-spl-nodtb.bin
二、idbloader.img
我们基于uboot源码编译出TPL/SPL,其中TPL负责实现DDR初始化,TPL初始化结束之后会回跳到BootROM程序,BootROM程序继续加载SPL,SPL加载u-boot.itb文件,然后跳转到uboor执行。
idbloader.img是由tpl/u-boot-tpl.bin和spl/u-boot-spl.bin文件生成,这里我们需要使用到tools目录下的mkimage工具。
2.1 tpl/u-boot-tpl.bin
在u-boot目录下执行:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# tools/mkimage -n rk3399 -T rksd -d tpl/u-boot-tpl.bin idbloader.img Image Type: Rockchip RK33 (SD/MMC) boot image Init Data Size: 53248 bytes
其中:
- -n rk3399将镜像文件的名称设置为"rk3399";
- -T rksd将映像类型指定为Rockchip SD卡启动映像;
- -d tpl/u-boot-tpl.bin将生成的TPL镜像文件"tpl/u-boot-tpl.bin"指定为输入文件,而idbloader.img则指定为输出文件。
生成idbloader.img文件:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ll idbloader.img -rw-r--r-- 1 root root 55296 May 21 17:12 idbloader.img
2.2 spl/u-boot-spl.bin
将spl/u-boot-spl.bin合并到idbloader.img:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# cat spl/u-boot-spl.bin >> idbloader.img root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ll idbloader.img -rw-r--r-- 1 root root 143866 May 21 17:13 idbloader.img
三、u-boot.idb
3.1 u-boot.itd生成规则
这里我们需要生成FIT-uImage,对应的镜像文件名称为u-boot.itd,在顶层Makefile定义有:
ifdef U_BOOT_ITS u-boot.itb: u-boot-nodtb.bin \ $(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_SANDBOX),dts/dt.dtb) \ $(if $(CONFIG_MULTI_DTB_FIT),$(FINAL_DTB_CONTAINER)) \ $(U_BOOT_ITS) FORCE $(call if_changed,mkfitimage) $(BOARD_SIZE_CHECK) endif ifneq ($(CONFIG_SPL_FIT_SOURCE),"") // 方式1. 如果自己已经有u-boot.its,配置CONFIG_SPL_FIT_SOURCE=u-boot.its U_BOOT_ITS := u-boot.its $(U_BOOT_ITS): $(subst ",,$(CONFIG_SPL_FIT_SOURCE)) $(call if_changed,copy) else ifneq ($(CONFIG_USE_SPL_FIT_GENERATOR),) // 方式2. 使用脚本生成 配置CONFIG_USE_SPL_FIT_GENERATOR=y U_BOOT_ITS := u-boot.its $(U_BOOT_ITS): $(U_BOOT_ITS_DEPS) FORCE $(srctree)/$(CONFIG_SPL_FIT_GENERATOR) \ $(patsubst %,arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) > $@ // 在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o endif endif
这里我们采用的配置CONFIG_SPL_FIT_GENERATOR的方式来生成u-boot.its,也就是我们在执行make menuconfig时配置的:
CONFIG_SPL_FIT_SOURCE="" CONFIG_USE_SPL_FIT_GENERATOR=y CONFIG_SPL_FIT_GENERATOR="arch/arm/mach-rockchip/make_fit_atf.py"
3.2 编译
执行编译命令:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# make u-boot.itb ARCH=arm CROSS_COMPILE=arm-linux- UPD include/generated/timestamp_autogenerated.h ENVC include/generated/env.txt ENVP include/generated/env.in ENVT include/generated/environment.h CC cmd/version.o AR cmd/built-in.o CC env/common.o AR env/built-in.o CC lib/efi_loader/dtbdump.o LD lib/efi_loader/dtbdump_efi.so OBJCOPY lib/efi_loader/dtbdump.efi CC lib/efi_loader/initrddump.o LD lib/efi_loader/initrddump_efi.so OBJCOPY lib/efi_loader/initrddump.efi LD u-boot OBJCOPY u-boot-nodtb.bin RELOC u-boot-nodtb.bin ./"arch/arm/mach-rockchip/make_fit_atf.py" \ arch/arm/dts/rk3399-evb.dtb > u-boot.its MKIMAGE u-boot.itb
编译完成在当前路径下生成u-boot.its、u-boot.itb文件。
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ll u-boot.it* -rw-r--r-- 1 root root 974224 May 21 17:14 u-boot.itb -rw-r--r-- 1 root root 2423 May 21 17:14 u-boot.its
其中u-boot.its文件内容如下:
/* * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd * * Minimal dts for a SPL FIT image payload. * * SPDX-License-Identifier: GPL-2.0+ X11 */ /dts-v1/; / { description = "Configuration to load ATF before U-Boot"; #address-cells = <1>; images { uboot { description = "U-Boot (64-bit)"; data = /incbin/("u-boot-nodtb.bin"); type = "standalone"; os = "U-Boot"; arch = "arm64"; compression = "none"; load = <0x00200000>; hash { algo = "sha256"; }; }; atf@1 { description = "ARM Trusted Firmware"; data = /incbin/("bl31_0x00040000.bin"); type = "firmware"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0x00040000>; entry = <0x00040000>; hash { algo = "sha256"; }; }; atf@2 { description = "ARM Trusted Firmware"; data = /incbin/("bl31_0xff3b0000.bin"); type = "firmware"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0xff3b0000>; hash { algo = "sha256"; }; }; atf@3 { description = "ARM Trusted Firmware"; data = /incbin/("bl31_0xff8c0000.bin"); type = "firmware"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0xff8c0000>; hash { algo = "sha256"; }; }; atf@4 { description = "ARM Trusted Firmware"; data = /incbin/("bl31_0xff8c1000.bin"); type = "firmware"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0xff8c1000>; hash { algo = "sha256"; }; }; atf@5 { description = "ARM Trusted Firmware"; data = /incbin/("bl31_0xff8c2000.bin"); type = "firmware"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0xff8c2000>; hash { algo = "sha256"; }; }; fdt { description = "U-Boot device tree blob"; data = /incbin/("u-boot.dtb"); type = "flat_dt"; arch = "arm64"; compression = "none"; hash { algo = "sha256"; }; }; }; configurations { default = "config"; config { description = "Rockchip armv8 with ATF"; rollback-index = <0x0>; firmware = "atf@1"; loadables = "uboot", "atf@2" , "atf@3" , "atf@4" , "atf@5" , "atf@6" ; fdt = "fdt"; signature { algo = "sha256,rsa2048"; padding = "pss"; key-name-hint = "dev"; sign-images = "fdt", "firmware", "loadables"; }; }; }; };
四、FIT uImage
我们在Mini2440之uboot移植流程之linux内核启动分析(六)中介绍过内核镜像包括多种格式,比如Legacy uImage、FIT uImage,这一节我们将学习制作FIT uImage,并将其烧录到开发板的eMMC第0x8000个扇区上。
4.1 编译内核
在Rockchip RK3399 - linux内核移植我们介绍了linux内核5.2.8版本的编译,编译完成后会在arch/arm64/boot/文件夹下生成内核镜像文件,以及设备树dst/rockchip/xxx.dtb文件;
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll arch/arm64/boot 总用量 44032 drwxr-xr-x 3 root root 4096 May 16 11:28 ./ drwxr-xr-x 14 root root 4096 May 16 11:15 ../ drwxr-xr-x 33 root root 4096 May 16 10:27 dts/ -rw-r--r-- 1 root root 55 May 16 10:27 .gitignore -rw-r--r-- 1 root root 33616384 May 16 11:27 Image -rw-r--r-- 1 root root 134 May 16 11:27 .Image.cmd -rw-r--r-- 1 root root 11617499 May 16 11:28 Image.gz -rw-r--r-- 1 root root 101 May 16 11:28 .Image.gz.cmd -rw-r--r-- 1 root root 1562 May 16 10:27 install.sh -rw-r--r-- 1 root root 960 May 16 10:27 Makefile root@zhengyang:/work/sambashare/rk3399/linux-5.2.8/arch/arm64# cd arch/arm64/boot root@zhengyang:/work/sambashare/rk3399/linux-5.2.8/arch/arm64/boot# ls dts/rockchip/*rk3399*.dtb dts/rockchip/rk3399-evb.dtb dts/rockchip/rk3399-gru-scarlet-kd.dtb dts/rockchip/rk3399-rock960.dtb dts/rockchip/rk3399-ficus.dtb dts/rockchip/rk3399-nanopc-t4.dtb dts/rockchip/rk3399-rock-pi-4.dtb dts/rockchip/rk3399-firefly.dtb dts/rockchip/rk3399-nanopi-m4.dtb dts/rockchip/rk3399-rockpro64.dtb dts/rockchip/rk3399-gru-bob.dtb dts/rockchip/rk3399-nanopi-neo4.dtb dts/rockchip/rk3399-roc-pc.dtb dts/rockchip/rk3399-gru-kevin.dtb dts/rockchip/rk3399-orangepi.dtb dts/rockchip/rk3399-sapphire.dtb dts/rockchip/rk3399-gru-scarlet-inx.dtb dts/rockchip/rk3399-puma-haikou.dtb dts/rockchip/rk3399-sapphire-excavator.dtb
其中Image是内核镜像原生二进制文件,可以直接在芯片上运行的,由于Image文件较大,我们将Image压缩转换为Image.gz(内核编译默认也会生成这个文件):
cat arch/arm64/boot/Image | gzip -n -f -9 > arch/arm64/boot/Image.gz
4.2 创建its文件
因为mkimage是根据its文件中的描述来打包镜像生成itb文件(FIT uImage),所以首先需要制作一个its文件,在its文件中描述需要被打包的镜像,主要是kernel镜像,dtb文件,ramdisk镜像。
我们创建一个kernel.its文件,内容如下:
/* * Simple U-Boot uImage source file containing a single kernel and FDT blob */ /dts-v1/; / { description = "Simple image with single Linux kernel and FDT blob"; #address-cells = <1>; images { kernel { description = "Vanilla Linux kernel"; data = /incbin/("arch/arm64/boot/Image.gz"); type = "kernel"; arch = "arm64"; os = "linux"; compression = "gzip"; load = <0x280000>; entry = <0x280000>; hash-1 { algo = "crc32"; }; hash-2 { algo = "sha1"; }; }; fdt { description = "Flattened Device Tree blob"; data = /incbin/("arch/arm64/boot/dts/rockchip/rk3399-evb.dtb"); type = "flat_dt"; arch = "arm64"; compression = "none"; load = <0x8300000>; entry = <0x8300000>; hash-1 { algo = "crc32"; }; hash-2 { algo = "sha1"; }; }; ramdisk { description = "Ramdisk for project-x"; data = /incbin/("ramdisk.gz"); type = "ramdisk"; arch = "arm64"; os = "linux"; load = <00000000>; entry = <00000000>; compression = "gzip"; hash-1 { algo = "crc32"; }; }; }; configurations { default = "conf-1"; conf-1 { description = "Boot Linux kernel with FDT blob"; kernel = "kernel"; fdt = "fdt"; ramdisk = "ramdisk"; }; }; };
这里我们内核镜像使用的Image.gz文件,该文件是通过gzip对Image进行压缩得到的,因此需要在kernel属性中指定compression,这样uboot才知道如何解压。
4.2.1 内核加载地址0x280000
这里有一点一定要注意:kernel的load、entry地址一定要指定为0x280000;我最初将load、entry都指定为0x20000000、0x20080000,后来又尝试了0x200000,uboot启动内核时都会卡在Starting kernel ...,就这个小小的问题我排查了好多天,说多了这都是泪啊;那我后来是怎么发现load、entry加载地址有问题的呢?
我们在上一篇博客中介绍了了Distro Boot启动内核的方式(Rockchip官方推荐的),当时是可以启动内核的。所以我去参考了Rockchip官方提供的uboot代码。
我修改了Rockchip官方提供的uboot代码,在arch/arm/lib/bootm.c文件中将 images->ep(Image镜像的加载地址)、images->fd_addr(fdt的加载地址)信息打印出来:
/* Subcommand: GO */ static void boot_jump_linux(bootm_headers_t *images, int flag) { #ifdef CONFIG_ARM64 void (*kernel_entry)(void *fdt_addr, void *res0, void *res1, void *res2); int fake = (flag & BOOTM_STATE_OS_FAKE_GO); int es_flag = 0; #if defined(CONFIG_AMP) es_flag = arm64_switch_amp_pe(images); #elif defined(CONFIG_ARM64_SWITCH_TO_AARCH32) es_flag = arm64_switch_aarch32(images); #endif kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1, void *res2))images->ep; printf("## Transferring control to Linux (at address %lx)...\n", (ulong) kernel_entry); printf("%s 0x%x=0x%x \n",__func__,images->ep,*((unsigned int *)images->ep)); printf("%s 0x%x=0x%x \n",__func__,images->ep + 4,*((unsigned int *)(images->ep + 4) )); printf("%s 0x%x=0x%x \n",__func__,images->ep + 20,*((unsigned int *)(images->ep + 20) )); printf("%s 0x%x=0x%x \n",__func__,images->ft_addr,*((unsigned int *)images->ft_addr)); printf("%s 0x%x=0x%x \n",__func__,((unsigned int *)images->ft_addr)+1,*((unsigned int *)images->ft_addr+1)); printf("%s 0x%x=0x%x \n",__func__,((unsigned int *)images->ft_addr)+5,*((unsigned int *)images->ft_addr+5)); bootstage_mark(BOOTSTAGE_ID_RUN_OS); announce_and_cleanup(images, fake); // 这里面会输出Starting kernel if (!fake) { #ifdef CONFIG_ARMV8_PSCI // 不会走 armv8_setup_psci();#endif do_nonsec_virt_switch(); update_os_arch_secondary_cores(images->os.arch); #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 // 不会走 armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, (u64)switch_to_el1, ES_TO_AARCH64); #else if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && (images->os.arch == IH_ARCH_ARM)){ armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, (u64)images->ft_addr, es_flag, (u64)images->ep, ES_TO_AARCH32); }else { // 走这里 armv8_switch_to_el2((u64)images->ft_addr, 0, 0, es_flag, images->ep, ES_TO_AARCH64); } #endif } ...... // 后面是非CONFIG_ARM64成立时,才会走的代码 }
这是当时打印出来的内容:
## Transferring control to Linux (at address 280000)... boot_jump_linux 0x280000=0x91005a4d boot_jump_linux 0x280004=0x14433fff boot_jump_linux 0x280014=0x0 boot_jump_linux 0x8300000=0xedfe0dd0 boot_jump_linux 0x8300004=0xc00000 boot_jump_linux 0x8300014=0x11000000 Total: 5818.548 ms Starting kernel ...
然后我又修改了当前最新版本uboot的代码,将images->ep、images->fd_addr也打印出来,下面时我配置为load、entry为0x20000000时输出的内容:
## Transferring control to Linux (at address 20000000)... boot_jump_linux 0x20000000=0x91005a4d boot_jump_linux 0x20000004=0x14433fff boot_jump_linux 0x20000014=0x0 boot_jump_linux 0xf4606000=0xedfe0dd0 boot_jump_linux 0xf4606004=0xc00000 boot_jump_linux 0xf4606014=0x11000000
同时比对这两个uboot 中ep、fd_addr内容差异:我发现kernel、fdt都成功加载到内存了,除了加载的地址不一样,但是内存的数据时完全一样的。
最开始我也没有往加载地址这方面去想,然后就开始百度,各种尝试。然而网上相关资料少之又少;后来我尝试了很多办法:
(1) 最初我想的是会不会是内核启动时卡住了,所以我就去定位内核日志缓冲区log_buf在内存的地址,然后去查看该内存的内容,但是该缓冲区并没有任何有用的数据;我就开始怀疑人生了,难道是我排查的方法有问题,在文章最后会记录这种方法;实际上现在回想起来,内核代码应该都没有执行到,直接卡在了armv8_switch_to_el2函数;该函数等后面我们有时间再研究;
(2) 然后又在想是不是启动参数bootargs配置的有问题,导致内核日志没有通过串口打印出来,专文为此我还去研究了一下earlycon控制台驱动和uart8250串口驱动;后面也会介绍如何配置earlycon和console;
(3) 最后我又尝试使用Rockchip官方提供的uboot代码去加载kernel.itb镜像文件,由于Rockchip官方提供的uboot在启动方式上做了大量魔改,所以我又去阅读了cmd/bootfit.c以及arch/arm/mach-rockchip/fit.c相关的代码,发现几处比较有意思的地方。
当我们把FIT uImage下载到内存某个地址,比如0x10000000,运行boot_fit 0x10000000启动内核,该命令会执行do_boot_fit函数,这个函数内部会调用fit_image_pre_process对FIT uImage进行预处理:包括检查其格式是否合法、修正镜像元素的入口地址和加载地址等信息,并且在必要时分配系统内存。因此要求FIT uImage必须满足以下条件:
必须要有ramdisk、kernel、fdt节点;(这里就很烦人,ramdisk你限制个屁,为此我还去做了ramdisk镜像);
校验kernel、ramdisk、ftd的data-offset、data-size属性,这个就导致在使用tools/mkimage工具生成FIT uImage镜像的时候必须要指定-E参数,不然生成的FIT uImage是不会有这两个属性的;
后来我发现代码中会从环境变量中获取一个加载地址,虽然这个地址并没有被使用到,我就想着要不按照这个改一下,后来尝试发现内核竟然真的可以启动了,撒花啊......,至于为啥一定要配置kernel的load地址为0x280000,目前我还不知道原因,等后面有时间研究一下。
fdt_addr_r=0x08300000 kernel_addr_r=0x00280000
4.2.2 radmisk
关于radmisk根文件系统的制作上一节我们已经介绍过了,这里不重复说了,具体可以参考amdist根文件系统镜像。
此外,如果不需要ramdisk的话,将kernel.its文件中ramdisk相关配置全部删除即可。
4.3 生成kernel.itb
FIT image 文件的编译过程很简单,这里我们需要将uboot路径下的mkimage工具拷贝过来,然后在命令行使用mkimage工具编译即可:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cp ../u-boot-2023.04/tools/mkimage ./ root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ./mkimage -f kernel.its kernel.itb
需要注意的是这里一定不能指定-E参数,不然uboot在进行kernel镜像hash校验的时候就会失败。
五、烧录测试
由于我们每次修改程序后,重新编译步骤比较麻烦,这里我们可以将这些步骤编写成一个shell脚本,这样每次执行就比较容易了。
5.1 自动构建脚本
在uboot根目录下创建一个build.sh文件:
#!/bin/bash # 接收第一个参数 支持 ''|'config'|'clean' step=$1 # 接收 V=1 支持编译输出详细信息 V=$2 cmd=${V%=*} if [[ ${cmd} = 'V' ]]; then V=${V#*=} fi if [[ ${step} == "config" ]];then echo '----------------config evb-rk3399_defconfig------- -------------' make evb-rk3399_defconfig V=${V} fi if [[ -z ${step} ]];then echo "---------------1. compile uboot-------------------------------- " make ARCH=arm CROSS_COMPILE=arm-linux- V=${V} echo "---------------2. mkimahe idbloader----------------------------" tools/mkimage -n rk3399 -T rksd -d tpl/u-boot-tpl.bin idbloader.img cat spl/u-boot-spl.bin >> idbloader.img echo "---------------3. make itb-------------------------------------" make u-boot.itb ARCH=arm CROSS_COMPILE=arm-linux- cp ./u-boot.itb ../rkdeveloptool cp ./idbloader.img ../rkdeveloptool fi if [[ ${step} == "clean" ]];then echo "-----------------clean-----------------------------------------" make clean
make distclean fi
然后给文件赋予可执行权限:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# chmod +x build.sh
5.1.1 clean
执行如下命令进行清理:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ./build.sh clean
5.1.2 配置
执行如下命令进行uboot配置:
root@zhengyang:/work/sambashare/rk3399/u-boot# ./build.sh config ----------------config evb-rk3399_defconfig------- ------------- HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf # # configuration written to .config #
5.1.3 构建
执行如下命令进行uboot编译、生成idbloader.img、 u-boot.itb文件,并拷贝到rkdeveloptool目录下:
root@zhengyang:/work/sambashare/rk3399/u-boot-2023.04# ./build.sh
如果需要输出编译详情信息,追加V=1参数即可。
5.2 烧录uboot
烧录方法有两种:
- 一种是基于Rockchip的官方烧录工具RKDevTool;官方RKDevTool是基于recovery模式实现的,如果板子带有recovery按键,可以使用这种方式;
- 另外一种是在开发板上使用rkdeveloptool工具直接烧写eMMC;这里我们采用rkdeveloptool烧录的方式;
5.2.1 准备镜像
我们需按照之前的流程得到了idbloader.img、u-boot.itb文件,由于我们这里不进行内核和根文件系统的烧录,所以暂时不需要准备这俩。
按照Rockchip官方要求将idbloader.img烧录到eMMC的0x40扇区,u-boot.itb烧录到0x4000扇区。
我们需要将idbloader.img、u-boot.itb、kernel.itb拷贝到rkdeveloptool路径下:
root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot-2023.04/idbloader.img ./ root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# cp ../u-boot-2023.04/u-boot.itb ./ root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# cp ../linux-5.2.8/kernel.itb ./
5.2.2 进入MASKROM升级模式
NanoPC-T4开发板如需进入MASKROM升级模式,需要进入如下操作:
- 将开发板连接上电源,并且连接Type-C数据线到PC;
- 按住BOOT键再长按Power键开机(保持按下BOOT键5秒以上),将强制进入MASKROM模式。
一般电脑识别到USB连接,都会发出声音。或者观察虚拟机右下角是否突然多个USB设备:右键点击链接;
5.2.3 下载uboot
使用下载引导命令去使目标机器初始化DDR与运行usbplug(初始化DDR的原因是由于升级需要很大的内存,所以需要使用到DDR);
root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool db rk3399_loader_v1.27.126.bin Downloading bootloader succeeded.
由于BootROM启动会将rk3399_loader_v1.27.126.bin将在到内部SRAM中,然后跳转到ddr.bin代码进行DDR的初始化,ddr.bin执行之后会回跳到BootROM程序,BootROM程序继续加载usbplug.bin,由usbplug.bin完成程序的下载以及烧录到eMMC。
如果接上串口的话,执行完这一步可以看到如下输出信息:
DDR Version 1.27 20211018 In Channel 0: LPDDR3, 800MHz CS = 0 MR0=0x58 MR1=0x58 MR2=0x58 MR3=0x58 MR4=0x2 MR5=0x1 MR6=0x5 MR7=0x0 MR8=0x1F MR9=0x1F MR10=0x1F MR11=0x1F MR12=0x1F MR13=0x1F MR14=0x1F MR15=0x1F MR16=0x1F CS = 1 MR0=0x58 MR1=0x58 MR2=0x58 MR3=0x58 MR4=0x2 MR5=0x1 MR6=0x5 MR7=0x0 MR8=0x1F MR9=0x1F MR10=0x1F MR11=0x1F MR12=0x1F MR13=0x1F MR14=0x1F MR15=0x1F MR16=0x1F Bus Width=32 Col=10 Bank=8 Row=15/15 CS=2 Die Bus-Width=32 Size=2048MB Channel 1: LPDDR3, 800MHz CS = 0 MR0=0x58 MR1=0x58 MR2=0x58 MR3=0x58 MR4=0x2 MR5=0x1 MR6=0x5 MR7=0x0 MR8=0x1F MR9=0x1F MR10=0x1F MR11=0x1F MR12=0x1F MR13=0x1F MR14=0x1F MR15=0x1F MR16=0x1F CS = 1 MR0=0x58 MR1=0x58 MR2=0x58 MR3=0x58 MR4=0x2 MR5=0x1 MR6=0x5 MR7=0x0 MR8=0x1F MR9=0x1F MR10=0x1F MR11=0x1F MR12=0x1F MR13=0x1F MR14=0x1F MR15=0x1F MR16=0x1F Bus Width=32 Col=10 Bank=8 Row=15/15 CS=2 Die Bus-Width=32 Size=2048MB 256B stride ch 0 ddrconfig = 0x101, ddrsize = 0x2020 ch 1 ddrconfig = 0x101, ddrsize = 0x2020 pmugrf_os_reg[2] = 0x3AA0DAA0, stride = 0xD OUT Boot1 Release Time: Jun 2 2020 15:02:17, version: 1.26 CPUId = 0x0 SdmmcInit=2 0 BootCapSize=100000 UserCapSize=14910MB FwPartOffset=2000 , 100000 UsbBoot ...73858 powerOn 86071
使用wl命令烧写镜像到目标机器的eMMC,需要注意的是访问DDR所需的所有其他命令都应在使用db命令之后才能使用;
root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x40 idbloader.img Write LBA from file (100%) root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x4000 u-boot.itb Write LBA from file (100%)
如果有制作好ext4类型的根文件系统,可以将根文件系统烧录到eMMC 0x40000扇区处,烧录命令:
root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool wl 0x40000 rk3399_ext4.img
在烧写镜像完成后使用rd命令重启目标机器:
root@zhengyang:/work/sambashare/rk3399/rkdeveloptool# rkdeveloptool rd Reset Device OK.
需要注意的是:如果这个时候你也烧录了内核程序,执行完rd命令后是无法进入uboot命令的的,这里会直接启动内核。
5.3 测试u-boot
5.3.1 串口连接
使用准备好的USB转串口适配器和连接线(需另购),连接开发板:
引脚 | 开发板接口 | USB转串口 |
1 | GNC | - |
2 | VCC 5V | - |
3 | UART2DBG_TX | RX |
4 | UART2DBG_RX | TX |
5.3.2 MobaXterm
这里我使用的串口调试工具是MobaXterm,选择串口端口,设置波特率为115200,8位数据位,1位停止位。
5.3.3 上电
给开发板上电,通过串口打印输出:
U-Boot TPL 2023.04 (May 21 2023 - 17:50:22) Channel 0: LPDDR3, 800MHz BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB Channel 1: LPDDR3, 800MHz BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB 256B stride Trying to boot from BOOTROM Returning to boot ROM... U-Boot SPL 2023.04 (May 21 2023 - 17:50:22 +0800) Trying to boot from MMC2 spl_load_fit_image: Skip load 'atf@5': image size is 0! cannot find image node 'atf@6': -1 NOTICE: BL31: v2.8(release):c194aa0 NOTICE: BL31: Built : 19:26:54, May 11 2023 U-Boot 2023.04 (May 21 2023 - 17:50:25 +0800) SoC: Rockchip rk3399 Reset cause: RST Model: Rockchip RK3399 Evaluation Board DRAM: 4 GiB (effective 3.9 GiB) Core: 254 devices, 27 uclasses, devicetree: separate MMC: mmc@fe320000: 1, mmc@fe330000: 0 Loading Environment from MMC... OK In: serial Out: serial Err: serial Model: Rockchip RK3399 Evaluation Board Net: eth0: ethernet@fe300000 Hit any key to stop autoboot: 0
在倒计时执行完之前,按CTRL+C即可进入uboot命令行。
5.3.4 设置ip以及bootargs
这里我们这是本机和服务器的ip地址:
=> setenv ipaddr 192.168.0.105 => setenv serverip 192.168.0.200 => saveenv Saving Environment to MMC... Writing to MMC(0)... OK
ARM64,在kernel未建立console之前,使用earlycon,实现日志信息的打印,因此需要配置bootargs,要加入如下选项:
earlycon=uart8250,mmio32,0xff1a0000,115200n8
其中uart8250表示针对uart8250这个串口设备,mio32表示内存I/O资源32位,0xff1a0000是RK3399串口2寄存器基地址,115200表示串口波特率。
uart8250串口驱动定义在内核根目录drivers/tty/serial/8250/8250_early.c文件中:
EARLYCON_DECLARE(uart8250, early_serial8250_setup);
有关uart8250串口驱动是如何注册的,这里就不过多介绍了,具体可以参考ARM64是怎样使用earlycon实现打印的。
我们在uboot命令行设置:
=> setenv bootargs earlycon=uart8250,mmio32,0xff1a0000 console=ttyS2,115200n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init => saveenv Saving Environment to MMC... Writing to MMC(0)... OK => print bootargs bootargs=earlycon=uart8250,mmio32,0xff1a0000 console=ttyS2,115200n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init
这里我通过root属性指定了根文件系统所在位置,这里我是用的ext4类型的根文件系统。
5.3.5 mmc
查看mmc信息:
=> mmc list mmc@fe320000: 1 mmc@fe330000: 0 (eMMC) => mmc info Device: mmc@fe330000 Manufacturer ID: 15 OEM: 0 Name: AJNB4R Bus Speed: 200000000 Mode: HS200 (200MHz) Rd Block Len: 512 MMC version 5.1 High Capacity: Yes Capacity: 14.6 GiB Bus Width: 8-bit Erase Group Size: 512 KiB HC WP Group Size: 8 MiB User Capacity: 14.6 GiB WRREL Boot Capacity: 4 MiB ENH RPMB Capacity: 4 MiB ENH Boot area 0 is not write protected Boot area 1 is not write protected
从上图中可以看到MMC设备版本为5.1, 14.6GiB(eMMC为16GB),速度为20000000Hz=200MHz, 8 位宽的总线。
5.4 烧录内核
5.4.1 下载kernel
我们将内核拷贝到tftp文件目录:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cp kernel.itb /work/tftpboot/
接着通过uboot命令行将kernel.itb下载待内存地址0x10000000处:
=> tftp 0x10000000 kernel.itb Speed: 100, full duplex Using ethernet@fe300000 device TFTP from server 192.168.0.200; our IP address is 192.168.0.105 Filename 'kernel.itb'. Load address: 0x10000000 Loading: ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################# 1.2 MiB/s done Bytes transferred = 8832472 (86c5d8 hex)
通过mmc write命令将内核镜像烧录到eMMC第0x8000个扇区处:
=> mmc erase 0x8000 0xA000 MMC erase: dev # 0, block # 32768, count 40960 ... 40960 blocks erased: OK => mmc write 0x10000000 0x8000 0xA000 MMC write: dev # 0, block # 32768, count 40960 ... 40960 blocks written: OK
这里cnt传入的是0xA000,即写入大小为20MB,已经大于kernel.itb文件大小了。
5.4.2 bootm启动内核
在uboot命令行运行bootm 0x10000000命令启动内核:
=> bootm 0x10000000 ## Loading kernel from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'kernel' kernel subimage Description: Vanilla Linux kernel Type: Kernel Image Compression: gzip compressed Data Start: 0x100000e8 Data Size: 8648300 Bytes = 8.2 MiB Architecture: AArch64 OS: Linux Load Address: 0x00280000 Entry Point: 0x00280000 Hash algo: crc32 Hash value: 6470c524 Hash algo: sha1 Hash value: b0f2e3608a5689b77fc110e7c399cf6a0398c691 Verifying Hash Integrity ... crc32+ sha1+ OK bootm_find_os images.os.load=0x280000 # kernel.its文件中kernel节点配置的"load"属性 bootm_find_os images.ep=0x280000 # kernel.its文件中kernel节点配置的"entry"属性 bootm_find_os images.os.image_start=0x100000e8 ## Loading ramdisk from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'ramdisk' ramdisk subimage Description: Ramdisk for project-x Type: RAMDisk Image Compression: gzip compressed Data Start: 0x1084b298 Data Size: 134661 Bytes = 131.5 KiB Architecture: AArch64 OS: Linux Load Address: 0x00000000 Entry Point: 0x00000000 Hash algo: crc32 Hash value: 649c78d3 Verifying Hash Integrity ... crc32+ OK WARNING: 'compression' nodes for ramdisks are deprecated, please fix your .its file! ## Loading fdt from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'fdt' fdt subimage Description: Flattened Device Tree blob Type: Flat Device Tree Compression: uncompressed Data Start: 0x1083f87c Data Size: 47365 Bytes = 46.3 KiB Architecture: AArch64 Load Address: 0x08300000 Hash algo: crc32 Hash value: 2489ab59 Hash algo: sha1 Hash value: 002b18a40044056ce9097742d3cf5f4480338521 Verifying Hash Integrity ... crc32+ sha1+ OK Loading fdt from 0x1083f87c to 0x08300000 Booting using the fdt blob at 0x8300000 Working FDT set to 8300000 Uncompressing Kernel Image kernel loaded at 0x00280000, end = 0x01679a00 Loading Ramdisk to f45f1000, end f4611e05 ... OK Loading Device Tree to 00000000f45e2000, end 00000000f45f0904 ... OK Working FDT set to f45e2000 ## Transferring control to Linux (at address 280000)... boot_jump_linux 0x280000=0x91005a4d # 解压缩之后得到Image镜像,并加载到0x280000地址处 boot_jump_linux 0x280004=0x14433fff boot_jump_linux 0x280014=0x0 boot_jump_linux 0xf45e2000=0xedfe0dd0 #fdt镜像加载到0xf45e2000地址处 boot_jump_linux 0xf45e2004=0x80c00000 boot_jump_linux 0xf45e2014=0x11000000 Starting kernel ...
上面的日志信息里,我打印了内存地址0x280000、0x280004、0x280014处的数据,可以看到和Image镜像文件的内容是一样的。
同时也打印了内存地址0xf45e2000、0xf45e2004、0xf45e2014处的数据,这里地址0xf45e2004和rk3399-evb.dtb镜像文件内容不太一样,这个字段是表示itb文件大小的(可能uboot修改了这个字段的值),其它两个地址数据一样的。
这里说明uboot已经成功解压了zImage,并将解压后的Image加载到了0x280000地址处,这个地址就是我们在kernel.its文件kernel节点load属性指定的地址。
但是设备树的地址我们在kernel.its文件fdt节点load属性指定的地址为0x8300000,这里却被uboot重定位到了0xf45e2000;
Uncompressing Kernel Image kernel loaded at 0x00280000, end = 0x01679a00 Loading Ramdisk to f45f1000, end f4611e05 ... OK Loading Device Tree to 00000000f45e2000, end 00000000f45f0904 ... OK Working FDT set to f45e2000
不过这并不重要,因为它并不影响内核的启动。我们把内核启动的全部内容显示出来:
=> bootm 0x10000000 ## Loading kernel from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'kernel' kernel subimage Description: Vanilla Linux kernel Type: Kernel Image Compression: gzip compressed Data Start: 0x100000e8 Data Size: 8648300 Bytes = 8.2 MiB Architecture: AArch64 OS: Linux Load Address: 0x00280000 Entry Point: 0x00280000 Hash algo: crc32 Hash value: 6470c524 Hash algo: sha1 Hash value: b0f2e3608a5689b77fc110e7c399cf6a0398c691 Verifying Hash Integrity ... crc32+ sha1+ OK bootm_find_os images.os.load=0x280000 bootm_find_os images.ep=0x280000 bootm_find_os images.os.image_start=0x100000e8 ## Loading ramdisk from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'ramdisk' ramdisk subimage Description: Ramdisk for project-x Type: RAMDisk Image Compression: gzip compressed Data Start: 0x1084b298 Data Size: 134661 Bytes = 131.5 KiB Architecture: AArch64 OS: Linux Load Address: 0x00000000 Entry Point: 0x00000000 Hash algo: crc32 Hash value: 649c78d3 Verifying Hash Integrity ... crc32+ OK WARNING: 'compression' nodes for ramdisks are deprecated, please fix your .its file! ## Loading fdt from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'fdt' fdt subimage Description: Flattened Device Tree blob Type: Flat Device Tree Compression: uncompressed Data Start: 0x1083f87c Data Size: 47365 Bytes = 46.3 KiB Architecture: AArch64 Load Address: 0x08300000 Hash algo: crc32 Hash value: 2489ab59 Hash algo: sha1 Hash value: 002b18a40044056ce9097742d3cf5f4480338521 Verifying Hash Integrity ... crc32+ sha1+ OK Loading fdt from 0x1083f87c to 0x08300000 Booting using the fdt blob at 0x8300000 Working FDT set to 8300000 Uncompressing Kernel Image kernel loaded at 0x00280000, end = 0x01679a00 bootm_load_os 0x20000000=0x5aa5f00f bootm_load_os 0x20000010=0xffffffff Loading Ramdisk to f45f4000, end f4614e05 ... OK Loading Device Tree to 00000000f45e5000, end 00000000f45f3904 ... OK Working FDT set to f45e5000 ## Transferring control to Linux (at address 280000)... boot_jump_linux 0x280000=0x91005a4d boot_jump_linux 0x280004=0x14433fff boot_jump_linux 0x280014=0x0 boot_jump_linux 0xf45e5000=0xedfe0dd0 boot_jump_linux 0xf45e5004=0x80c00000 boot_jump_linux 0xf45e5014=0x11000000 Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034] [ 0.000000] Linux version 5.2.8 (root@zhengyang) (gcc version 12.2.1 20221205 (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24))) #2 SMP PREEMPT Tue May 23 20:59:23 CST 2023 [ 0.000000] Machine model: Rockchip RK3399 Evaluation Board [ 0.000000] earlycon: uart8250 at MMIO32 0x00000000ff1a0000 (options '') [ 0.000000] printk: bootconsole [uart8250] enabled [ 0.000000] efi: Getting EFI parameters from FDT: [ 0.000000] efi: UEFI not found. [ 0.000000] cma: Reserved 32 MiB at 0x00000000f6000000 [ 0.000000] kmemleak: Kernel memory leak detector disabled [ 0.000000] NUMA: No NUMA configuration found [ 0.000000] NUMA: Faking a node at [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] NUMA: NODE_DATA [mem 0xf57fb840-0xf57fcfff] [ 0.000000] Zone ranges: [ 0.000000] DMA32 [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] Normal empty [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: PSCIv1.1 detected in firmware. [ 0.000000] psci: Using standard PSCI v0.2 function IDs [ 0.000000] psci: MIGRATE_INFO_TYPE not supported. [ 0.000000] psci: SMC Calling Convention v1.0 [ 0.000000] percpu: Embedded 23 pages/cpu s56728 r8192 d29288 u94208 [ 0.000000] Detected VIPT I-cache on CPU0 [ 0.000000] CPU features: detected: ARM erratum 845719 [ 0.000000] CPU features: detected: GIC system register CPU interface [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 999432 [ 0.000000] Policy zone: DMA32 [ 0.000000] Kernel command line: earlycon=uart8250,mmio32,0xff1a0000 console=ttyS2,115200n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init [ 0.000000] Memory: 3932900K/4061184K available (11324K kernel code, 1766K rwdata, 5836K rodata, 1472K init, 444K bss, 95516K reserved, 32768K cma-reserved) [ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=6, Nodes=1 [ 0.000000] rcu: Preemptible hierarchical RCU implementation. [ 0.000000] rcu: RCU restricting CPUs from NR_CPUS=256 to nr_cpu_ids=6. [ 0.000000] Tasks RCU enabled. [ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies. [ 0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=6 [ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0 [ 0.000000] GICv3: GIC: Using split EOI/Deactivate mode [ 0.000000] GICv3: Distributor has no Range Selector support [ 0.000000] GICv3: no VLPI support, no direct LPI support [ 0.000000] GICv3: CPU0: found redistributor 0 region 0:0x00000000fef00000 [ 0.000000] ITS [mem 0xfee20000-0xfee3ffff] [ 0.000000] ITS@0x00000000fee20000: allocated 65536 Devices @f5080000 (flat, esz 8, psz 64K, shr 0) [ 0.000000] ITS: using cache flushing for cmd queue [ 0.000000] GICv3: using LPI property table @0x00000000f5040000 [ 0.000000] GIC: using cache flushing for LPI property table [ 0.000000] GICv3: CPU0: using allocated LPI pending table @0x00000000f5050000 [ 0.000000] GICv3: GIC: PPI partition interrupt-partition-0[0] { /cpus/cpu@0[0] /cpus/cpu@1[1] /cpus/cpu@2[2] /cpus/cpu@3[3] } [ 0.000000] GICv3: GIC: PPI partition interrupt-partition-1[1] { /cpus/cpu@100[4] /cpus/cpu@101[5] } [ 0.000000] random: get_random_bytes called from start_kernel+0x2a8/0x440 with crng_init=0 [ 0.000000] rockchip_mmc_get_phase: invalid clk rate [ 0.000000] rockchip_mmc_get_phase: invalid clk rate [ 0.000000] rockchip_mmc_get_phase: invalid clk rate [ 0.000000] rockchip_mmc_get_phase: invalid clk rate [ 0.000000] arch_timer: cp15 timer(s) running at 24.00MHz (phys). [ 0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns [ 0.000006] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns [ 0.010567] Console: colour dummy device 80x25 [ 0.015503] kmemleak: Early log buffer exceeded (4684), please increase DEBUG_KMEMLEAK_EARLY_LOG_SIZE [ 0.025718] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=96000) [ 0.037007] pid_max: default: 32768 minimum: 301 [ 0.042192] LSM: Security Framework initializing [ 0.049468] Dentry cache hash table entries: 524288 (order: 10, 4194304 bytes) [ 0.058479] Inode-cache hash table entries: 262144 (order: 9, 2097152 bytes) [ 0.066294] Mount-cache hash table entries: 8192 (order: 4, 65536 bytes) [ 0.073715] Mountpoint-cache hash table entries: 8192 (order: 4, 65536 bytes) [ 0.105683] ASID allocator initialised with 32768 entries [ 0.119660] rcu: Hierarchical SRCU implementation. [ 0.133070] Platform MSI: interrupt-controller@fee20000 domain created [ 0.140557] PCI/MSI: /interrupt-controller@fee00000/interrupt-controller@fee20000 domain created [ 0.153868] EFI services will not be available. [ 0.166887] smp: Bringing up secondary CPUs ... [ 0.204098] Detected VIPT I-cache on CPU1 [ 0.204136] GICv3: CPU1: found redistributor 1 region 0:0x00000000fef20000 [ 0.204152] GICv3: CPU1: using allocated LPI pending table @0x00000000f5060000 [ 0.204195] CPU1: Booted secondary processor 0x0000000001 [0x410fd034] [ 0.236175] Detected VIPT I-cache on CPU2 [ 0.236204] GICv3: CPU2: found redistributor 2 region 0:0x00000000fef40000 [ 0.236217] GICv3: CPU2: using allocated LPI pending table @0x00000000f5070000 [ 0.236249] CPU2: Booted secondary processor 0x0000000002 [0x410fd034] [ 0.268270] Detected VIPT I-cache on CPU3 [ 0.268297] GICv3: CPU3: found redistributor 3 region 0:0x00000000fef60000 [ 0.268310] GICv3: CPU3: using allocated LPI pending table @0x00000000f5100000 [ 0.268340] CPU3: Booted secondary processor 0x0000000003 [0x410fd034] [ 0.300382] CPU features: detected: EL2 vector hardening [ 0.300389] ARM_SMCCC_ARCH_WORKAROUND_1 missing from firmware [ 0.300397] Detected PIPT I-cache on CPU4 [ 0.300428] GICv3: CPU4: found redistributor 100 region 0:0x00000000fef80000 [ 0.300441] GICv3: CPU4: using allocated LPI pending table @0x00000000f5110000 [ 0.300475] CPU4: Booted secondary processor 0x0000000100 [0x410fd082] [ 0.332481] Detected PIPT I-cache on CPU5 [ 0.332506] GICv3: CPU5: found redistributor 101 region 0:0x00000000fefa0000 [ 0.332519] GICv3: CPU5: using allocated LPI pending table @0x00000000f5120000 [ 0.332546] CPU5: Booted secondary processor 0x0000000101 [0x410fd082] [ 0.332658] smp: Brought up 1 node, 6 CPUs [ 0.485015] SMP: Total of 6 processors activated. [ 0.490191] CPU features: detected: 32-bit EL0 Support [ 0.495866] CPU features: detected: CRC32 instructions [ 0.502518] CPU: All CPU(s) started at EL2 [ 0.507081] alternatives: patching kernel code [ 0.515264] devtmpfs: initialized [ 0.527144] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns [ 0.537922] futex hash table entries: 2048 (order: 5, 131072 bytes) [ 0.545737] pinctrl core: initialized pinctrl subsystem [ 0.553111] DMI not present or invalid. [ 0.557801] NET: Registered protocol family 16 [ 0.563230] audit: initializing netlink subsys (disabled) [ 0.569309] audit: type=2000 audit(0.440:1): state=initialized audit_enabled=0 res=1 [ 0.577866] cpuidle: using governor menu [ 0.582412] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers. [ 0.591647] DMA: preallocated 256 KiB pool for atomic allocations [ 0.599905] Serial: AMBA PL011 UART driver [ 0.641668] HugeTLB registered 1.00 GiB page size, pre-allocated 0 pages [ 0.649071] HugeTLB registered 32.0 MiB page size, pre-allocated 0 pages [ 0.656444] HugeTLB registered 2.00 MiB page size, pre-allocated 0 pages [ 0.663814] HugeTLB registered 64.0 KiB page size, pre-allocated 0 pages [ 0.674045] cryptd: max_cpu_qlen set to 1000 [ 0.684207] ACPI: Interpreter disabled. [ 0.690524] vcc5v0_host: supplied by vcc5v0_sys [ 0.697856] vgaarb: loaded [ 0.701176] SCSI subsystem initialized [ 0.705720] usbcore: registered new interface driver usbfs [ 0.711813] usbcore: registered new interface driver hub [ 0.717737] usbcore: registered new device driver usb [ 0.724279] pps_core: LinuxPPS API ver. 1 registered [ 0.729754] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it> [ 0.739819] PTP clock support registered [ 0.744302] EDAC MC: Ver: 3.0.0 [ 0.749287] FPGA manager framework [ 0.753135] Advanced Linux Sound Architecture Driver Initialized. [ 0.760529] clocksource: Switched to clocksource arch_sys_counter [ 0.767412] VFS: Disk quotas dquot_6.6.0 [ 0.771788] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes) [ 0.779554] pnp: PnP ACPI: disabled [ 0.792876] NET: Registered protocol family 2 [ 0.798067] tcp_listen_portaddr_hash hash table entries: 2048 (order: 3, 32768 bytes) [ 0.806898] TCP established hash table entries: 32768 (order: 6, 262144 bytes) [ 0.815308] TCP bind hash table entries: 32768 (order: 7, 524288 bytes) [ 0.823454] TCP: Hash tables configured (established 32768 bind 32768) [ 0.830868] UDP hash table entries: 2048 (order: 4, 65536 bytes) [ 0.837627] UDP-Lite hash table entries: 2048 (order: 4, 65536 bytes) [ 0.845054] NET: Registered protocol family 1 [ 0.854289] RPC: Registered named UNIX socket transport module. [ 0.860819] RPC: Registered udp transport module. [ 0.865992] RPC: Registered tcp transport module. [ 0.871168] RPC: Registered tcp NFSv4.1 backchannel transport module. [ 0.878253] PCI: CLS 0 bytes, default 64 [ 0.882710] Unpacking initramfs... [ 0.887059] Initramfs unpacking failed: no cpio magic [ 0.892694] Freeing initrd memory: 128K [ 0.897699] hw perfevents: enabled with armv8_cortex_a53 PMU driver, 7 counters available [ 0.907033] hw perfevents: enabled with armv8_cortex_a72 PMU driver, 7 counters available [ 0.916313] kvm [1]: IPA Size Limit: 40bits [ 0.921599] kvm [1]: vgic-v2@fff20000 [ 0.925656] kvm [1]: GIC system register CPU interface enabled [ 0.932248] kvm [1]: vgic interrupt IRQ10 [ 0.936894] kvm [1]: Hyp mode initialized successfully [ 0.952634] Initialise system trusted keyrings [ 0.957685] workingset: timestamp_bits=44 max_order=20 bucket_order=0 [ 0.972551] squashfs: version 4.0 (2009/01/31) Phillip Lougher [ 0.979786] NFS: Registering the id_resolver key type [ 0.985379] Key type id_resolver registered [ 0.989977] Key type id_legacy registered [ 0.994572] 9p: Installing v9fs 9p2000 file system support [ 1.002090] Key type asymmetric registered [ 1.006609] Asymmetric key parser 'x509' registered [ 1.012015] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 245) [ 1.020152] io scheduler mq-deadline registered [ 1.025129] io scheduler kyber registered [ 1.046992] EINJ: ACPI disabled. [ 1.058723] dma-pl330 ff6d0000.dma-controller: Loaded driver for PL330 DMAC-241330 [ 1.067069] dma-pl330 ff6d0000.dma-controller: DBUFF-32x8bytes Num_Chans-6 Num_Peri-12 Num_Events-12 [ 1.078680] dma-pl330 ff6e0000.dma-controller: Loaded driver for PL330 DMAC-241330 [ 1.087034] dma-pl330 ff6e0000.dma-controller: DBUFF-128x8bytes Num_Chans-8 Num_Peri-20 Num_Events-16 [ 1.102268] pwm-regulator: supplied by regulator-dummy [ 1.113487] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled [ 1.122756] printk: console [ttyS2] disabled [ 1.127500] ff1a0000.serial: ttyS2 at MMIO 0xff1a0000 (irq = 30, base_baud = 1500000) is a 16550A [ 1.137445] printk: console [ttyS2] enabled [ 1.137445] printk: console [ttyS2] enabled [ 1.146826] printk: bootconsole [uart8250] disabled [ 1.146826] printk: bootconsole [uart8250] disabled [ 1.159234] SuperH (H)SCI(F) driver initialized [ 1.165065] msm_serial: driver initialized [ 1.171069] cacheinfo: Unable to detect cache hierarchy for CPU 0 [ 1.186024] loop: module loaded [ 1.195578] libphy: Fixed MDIO Bus: probed [ 1.200531] tun: Universal TUN/TAP device driver, 1.6 [ 1.207232] thunder_xcv, ver 1.0 [ 1.210915] thunder_bgx, ver 1.0 [ 1.214576] nicpf, ver 1.0 [ 1.218450] hclge is initializing [ 1.222164] hns3: Hisilicon Ethernet Network Driver for Hip08 Family - version [ 1.230244] hns3: Copyright (c) 2017 Huawei Corporation. [ 1.236272] e1000e: Intel(R) PRO/1000 Network Driver - 3.2.6-k [ 1.242801] e1000e: Copyright(c) 1999 - 2015 Intel Corporation. [ 1.249467] igb: Intel(R) Gigabit Ethernet Network Driver - version 5.6.0-k [ 1.257258] igb: Copyright (c) 2007-2014 Intel Corporation. [ 1.263541] igbvf: Intel(R) Gigabit Virtual Function Network Driver - version 2.4.0-k [ 1.272303] igbvf: Copyright (c) 2009 - 2012 Intel Corporation. [ 1.279411] sky2: driver version 1.30 [ 1.284512] VFIO - User Level meta-driver version: 0.3 [ 1.292410] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver [ 1.299731] ehci-pci: EHCI PCI platform driver [ 1.304767] ehci-platform: EHCI generic platform driver [ 1.312551] ehci-platform fe380000.usb: EHCI Host Controller [ 1.318912] ehci-platform fe380000.usb: new USB bus registered, assigned bus number 1 [ 1.328582] ehci-platform fe380000.usb: irq 26, io mem 0xfe380000 [ 1.348555] ehci-platform fe380000.usb: USB 2.0 started, EHCI 1.00 [ 1.356122] hub 1-0:1.0: USB hub found [ 1.360361] hub 1-0:1.0: 1 port detected [ 1.367287] ehci-platform fe3c0000.usb: EHCI Host Controller [ 1.373647] ehci-platform fe3c0000.usb: new USB bus registered, assigned bus number 2 [ 1.382729] ehci-platform fe3c0000.usb: irq 28, io mem 0xfe3c0000 [ 1.404555] ehci-platform fe3c0000.usb: USB 2.0 started, EHCI 1.00 [ 1.412032] hub 2-0:1.0: USB hub found [ 1.416271] hub 2-0:1.0: 1 port detected [ 1.421172] ehci-orion: EHCI orion driver [ 1.425865] ehci-exynos: EHCI EXYNOS driver [ 1.430680] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver [ 1.437613] ohci-pci: OHCI PCI platform driver [ 1.442658] ohci-platform: OHCI generic platform driver [ 1.448712] ohci-platform fe3a0000.usb: Generic Platform OHCI controller [ 1.456233] ohci-platform fe3a0000.usb: new USB bus registered, assigned bus number 3 [ 1.465273] ohci-platform fe3a0000.usb: irq 27, io mem 0xfe3a0000 [ 1.533221] hub 3-0:1.0: USB hub found [ 1.537457] hub 3-0:1.0: 1 port detected [ 1.542330] ohci-platform fe3e0000.usb: Generic Platform OHCI controller [ 1.549851] ohci-platform fe3e0000.usb: new USB bus registered, assigned bus number 4 [ 1.558835] ohci-platform fe3e0000.usb: irq 29, io mem 0xfe3e0000 [ 1.629112] hub 4-0:1.0: USB hub found [ 1.633347] hub 4-0:1.0: 1 port detected [ 1.638209] ohci-exynos: OHCI EXYNOS driver [ 1.643694] usbcore: registered new interface driver usb-storage [ 1.654584] i2c /dev entries driver [ 1.667216] sdhci: Secure Digital Host Controller Interface driver [ 1.674145] sdhci: Copyright(c) Pierre Ossman [ 1.679662] Synopsys Designware Multimedia Card Interface Driver [ 1.687679] sdhci-pltfm: SDHCI platform and OF driver helper [ 1.695906] mmc0: CQHCI version 5.10 [ 1.724845] mmc0: SDHCI controller on fe330000.sdhci [fe330000.sdhci] using ADMA [ 1.735366] ledtrig-cpu: registered to indicate activity on CPUs [ 1.744128] usbcore: registered new interface driver usbhid [ 1.750398] usbhid: USB HID core driver [ 1.759722] NET: Registered protocol family 17 [ 1.764911] 9pnet: Installing 9P2000 support [ 1.769773] Key type dns_resolver registered [ 1.775247] registered taskstats version 1 [ 1.779845] Loading compiled-in X.509 certificates [ 1.811457] hctosys: unable to open rtc device (rtc0) [ 1.818093] ALSA device list: [ 1.821448] No soundcards found. [ 1.825488] ttyS2 - failed to request DMA [ 1.830108] Waiting for root device PARTUUID=B921B045-1D... [ 1.840558] mmc0: Command Queue Engine enabled [ 1.845551] mmc0: new HS400 Enhanced strobe MMC card at address 0001 [ 1.853481] mmcblk0: mmc0:0001 AJNB4R 14.6 GiB [ 1.858830] mmcblk0boot0: mmc0:0001 AJNB4R partition 1 4.00 MiB [ 1.865718] mmcblk0boot1: mmc0:0001 AJNB4R partition 2 4.00 MiB [ 1.872610] mmcblk0rpmb: mmc0:0001 AJNB4R partition 3 4.00 MiB, chardev (237:0) [ 1.884260] mmcblk0: p1 p2 p3 p4 p5 [ 1.911423] EXT4-fs (mmcblk0p5): recovery complete [ 1.917504] EXT4-fs (mmcblk0p5): mounted filesystem with ordered data mode. Opts: (null) [ 1.926604] VFS: Mounted root (ext4 filesystem) on device 179:5. [ 1.934088] devtmpfs: mounted [ 1.938355] Freeing unused kernel memory: 1472K [ 1.961024] Run /sbin/init as init process [ 2.044679] usb 4-1: new low-speed USB device number 2 using ohci-platform Please press Enter to activate this console. [ 2.520720] usb 4-1: device descriptor read/64, error -62 [ 2.873915] input: SIGMACHIP Usb Mouse as /devices/platform/fe3e0000.usb/usb4/4-1/4-1:1.0/0003:1C4F:0065.0001/input/input0 [ 2.886675] hid-generic 0003:1C4F:0065.0001: input: USB HID v1.10 Mouse [SIGMACHIP Usb Mouse] on usb-fe3e0000.usb-1/input0 [root@zy:/]# [
5.4.3 启动卡在start kernel排查方法
如果你在内核启动时,卡在了Starting kernel ...。如果想解决问题,就必须知道问题出现在了哪里,因此一般通过内核启动的时候打印的信息来定位问题产生的原因。我们在linux内核调试-printk说过printk函数输出的日志都会被保存到内核日志缓冲区__log_buf中,因此我们可以直接读取该缓冲区, __log_buf其地址在System.map被标记出来,我们可以找到具体的位置。具体步骤如下:
(1) 首先去内核编译目录找到System.map文件。
(2) 找到System.map中_text(代码段起始地址)的虚拟地址,执行:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# nm vmlinux | grep _text ffff000010080000 T _text
(3) 找到System.map中__log_buf对应的虚拟地址,执行:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# nm vmlinux | grep __log_buf ffff000011493358 __log_buf
(4) 直接从eMMC第0x8000个扇区加载内核,启动内核,内核此时卡在Starting kernel …位置;
=> mmc read 0x10000000 0x8000 0xA000 MMC read: dev # 0, block # 32768, count 40960 ... 40960 blocks read: OK => bootm 0x10000000
(5) 重新启动系统(注意:不要断电启动,按下复位键即可),进入uboot命令行界面;
(6) 计算__log_buf在内存的物理地址:
PA__log_buf = __log_buf - _text + PA
其中PA为内核在内存的起始地址,这里是0x280000;这里通过计算得到0x1693358
在命令行输入(数字是刚才得到的数字):
md 0x1693358
(7) 不停的敲击回车键,直到为全为0。
这个内容在内存中是使用struct prink_log结构存储的,每个truct prink_log后面跟着需要输出的内容,内容长度由text_len字段确定。
struct printk_log { u64 ts_nsec; // 时间ns u16 len; // 该条消息在内存占用的长度,包括当前结构体 u16 text_len; // 输出的内容长度 u16 dict_len; u8 facility; u8 flags:5; u8 level:3; #iddef CONFIG_PRINTK_CALLER u32 caller_id #endif }
如果想解析内核日志缓冲区log_buf的内容,可以参考DUMP kernel log_buf以及解析log_buf字符的办法这篇博客,这篇博客提供了python脚本解析内核日志缓存器的内容。
参考文章
[1] Rockchip参考官方移植
[2] Rockchip RK3399 - TPL/SPL方式加载uboot
[3] 嵌入式Linux之uboot源码make配置编译正向分析
[4] 嵌入式ARM64 Linux内核FIT uimage方式启动
[5] DUMP kernel log_buf以及解析log_buf字符的办法