1简介
使用xilinx的官方开发工具Petalinux为zynqMP系列芯片移植linux操作系统时,烦躁于Petalinux编译一次需要很长时间,以及占用了大量内存空间,因此就打算脱离Petalinux做u-boot、kernel以及rootfs移植,编译速度得到大幅提升内容也是高度定制化,想要嵌入个自制小工具也很轻松,成就感也得到极大满足。ps:网上找到一个类似教程,但是T*D要钱才能看,我不愿意掏那几块钱,所以就自己摸索了一个流程,过程可能有误,勿喷。
2流程分析
Petalinux移植操作系统时生成了两个文件boot.bin和image.ub,因此我使用非Petalinux方式生成的烧写文件也是锚定于这两个文件。
2.1 文件构成
2.1.1 boot.bin
Petalinux打包文件为boot.bin时使用到了一个打包工具boogen和一个打包文件.bif。bootgen是xilinx官方提供的一个打包工具,具体描述可以去查看官方文档ug1283。下载链接:GitHub - Xilinx/bootgen: bootgen source code
.bif文件是bootgen执行打包时的依据,就是需要把哪些东西打包成一个boot.bin,Petalinux参照的.bif文件内容如下:
the_ROM_image:
{
[bootloader, destination_cpu=a53-0] image/bootgen/zynqmp_fsbl.elf
[pmufw_image] image/bootgen/pmufw.elf
[destination_device=pl] image/bootgen/fpga.bit
[destination_cpu=a53-0, exception_level=el-3, trustzone] image/bootgen/bl31.elf
[destination_cpu=a53-0, exception_level=el-2] image/bootgen/u-boot.elf
}
可以看出打包成一个boot.bin文件需要:fsbl.elf、pmufw.elf、fpga.bit、bl31.elf、u-boot.elf 这5个文件。其中相较于zynq系列芯片移植,多出了pmu、bl31两份文件,pmu是电源管理模块、bl31是arm trust firmware固件,具体这两文件是做什么的可以自行查询这里不做过多介绍,这里只需要知道boot.bin打包时必须要有这几个文件就行。
2.1.2 image.ub
Peatlinux打包文件为image.ub时使用到了一个打包工具mkimage和打包文件.its。mkimage是一个u-boot工具,用于打包制作镜像文件,.its文件是mkimage时的依据,就是告诉mkimage需要将哪些文件打包成一个image.ub,Petalinux参照的.its文件内容如下:
/dts-v1/;
/ {
description = "U-Boot fitImage for PetaLinux/4.14-xilinx-v2018.3+gitAUTOINC+eeab73d120/plnx-zynqmp";
#address-cells = <1>;
images {
kernel@1 {
description = "Linux kernel";
data = /incbin/("image/imagegen/Image");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x80000>;
entry = <0x80000>;
hash@1 {
algo = "sha1";
};
};
fdt@system.dtb {
description = "Flattened Device Tree blob";
data = /incbin/("image/imagegen/system.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
hash@1 {
algo = "sha1";
};
};
ramdisk@1 {
description = "petalinux-user-image";
data = /incbin/("image/imagegen/rootfs.cpio.gz");
type = "ramdisk";
arch = "arm64";
os = "linux";
compression = "gzip";
hash@1 {
algo = "sha1";
};
};
};
configurations {
default = "conf@system.dtb";
conf@system.dtb {
description = "1 Linux kernel, FDT blob, ramdisk";
kernel = "kernel@1";
fdt = "fdt@system.dtb";
ramdisk = "ramdisk@1";
hash@1 {
algo = "sha1";
};
};
};
};
可以看出打包一个image.ub需要:Image、system.dtb、rootfs.cpio.gz 这3个文件,其中Image是linux kernel编译后的镜像文件、system.dtb是设备树文件、rootfs.cpio.gz是文件系统。
2.1.3 小结
通过上述分析,我们知道要移植一个完整的linux操作系统到zynqmp上需要用到以上8个文件,包括fsbl.elf、.bit、pmu.elf、bl31.elf、u-boot用于boot.bin,Image、.dtb、rootfs.cpio.gz用于image.ub以及两个打包工具bootgen、mkimage。接下来就着重分析以及生成上述文件。
2.2 文件生成
2.2.1 fsbl
首先找fpga工程师要一份.hdf文件(vivado版本)或者.xsa文件(vitis版本),我这里使用的vivado2018.3,所以使用的是sdk2018.3。
打开sdk导入.hdf文件,
首先 File->New->Application Project
导入新的.hdf文件,生成bsp文件,Hardware PLatform->New Hardware Project
选择Zynq MP FSBL,然后Finish执行编译,得到fsbl.elf
2.2.2 pmu
基于刚刚的.hdf文件,新建一个pmu。
2.2.3 bl31
首先下载arm-trusted-firmware源码卜哩啾啾哩卜哩朵/arm-trusted-firmware - Gitee.com,解压放到Ubuntu16.04下,执行如下指令,编译生成bl31.elf
2.2.4 fpga.bit
直接找fpga工程师要,略。
2.2.5 u-boot
boot.bin生成最复杂也是操作最多的部分,首先下载u-boot源码卜哩啾啾哩卜哩朵/u-boot-xlnx - Gitee.com,解压到Ubuntu16.04目录下。
2.2.5.1 defconfig
xilinx针对不同类型的板子都有对应的默认配置文件,配置文件位于configs文件夹下,选择适合自己板子的配置文件。
如果不知道自己板子该用哪一种,可以使用Petalinux查看,在u-boot Configuration下,可以看到我的板子默认配置文件为xilinx_zynqmp_zcu102_rev1_0_defconfig
回到config文件夹下,复制一份 xilinx_zynqmp_zcu102_rev1_0_defconfig,更名为xilinx_zynqmp_3eg_defconfig,然后返回上一级目录执行make,可以看到配置文件保存到.config文件中了。.config文件是一个隐藏文件,可以使用Ctrl+H查看隐藏文件。
默认配置文件中可能包含一些我们用不到的配置,编译出来会使u-boot文件稍大一些,可以使用make menuconfig手动再做一些配置。
有几个点需要注意一下:
①ARM architecture->Board configuration name,该项是指定配置头文件,该文件保存在include/configs下,文件名称叫做platform-top.h(从petalinux下拷贝出来的,可以自定义,但必须确保该目录下有该文件,否则编译报文件缺失),该文件中包含一些配置信息,内容如下:
platform-top.h
#include <configs/platform-auto.h>
#define CONFIG_SYS_BOOTM_LEN 0xF000000
/*如果板子不支持USB DFU可以直接删掉*/
#define DFU_ALT_INFO_RAM \
"dfu_ram_info=" \
"setenv dfu_alt_info " \
"image.ub ram $netstart 0x1e00000\0" \
"dfu_ram=run dfu_ram_info && dfu 0 ram 0\0" \
"thor_ram=run dfu_ram_info && thordown 0 ram 0\0"
#define DFU_ALT_INFO_MMC \
"dfu_mmc_info=" \
"set dfu_alt_info " \
"${kernel_image} fat 0 1\\\\;" \
"dfu_mmc=run dfu_mmc_info && dfu 0 mmc 0\0" \
"thor_mmc=run dfu_mmc_info && thordown 0 mmc 0\0"
/*Required for uartless designs */
#ifndef CONFIG_BAUDRATE
#define CONFIG_BAUDRATE 115200
#ifdef CONFIG_DEBUG_UART
#undef CONFIG_DEBUG_UART
#endif
#endif
/*Define CONFIG_ZYNQMP_EEPROM here and its necessaries in u-boot menuconfig if you had EEPROM memory. */
/*eeprom 相关配置 若板子没有eeprom 也可以直接删掉*/
#ifdef CONFIG_ZYNQMP_EEPROM
#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1
#define CONFIG_CMD_EEPROM
#define CONFIG_ZYNQ_EEPROM_BUS 5
#define CONFIG_ZYNQ_GEM_EEPROM_ADDR 0x54
#define CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET 0x20
#endif
platform-auto.h
/*
* This file is auto-generated by PetaLinux SDK
* DO NOT MODIFY this file, the modification will not persist
*/
#ifndef __PLNX_CONFIG_H
#define __PLNX_CONFIG_H
/* The following table includes the supported baudrates */
#define CONFIG_SYS_BAUDRATE_TABLE { 4800, 9600, 19200, 38400, 57600, 115200 }
/* processor - psu_cortexa53_0 */
#define CONFIG_CPU_ARMV8
#define CONFIG_CLOCKS
#define CONFIG_REMAKE_ELF
#define CONFIG_BOARD_EARLY_INIT_R
#define CONFIG_BOARD_EARLY_INIT_F
#define CONFIG_ARM_DCC
#define CONFIG_MP
/* main_memory - psu_ddr_0 */
/* uart - psu_uart_0 */
#define PSSERIAL0 "psserial0=setenv stdout ttyPS0;setenv stdin ttyPS0\0"
#define SERIAL_MULTI "serial=setenv stdout serial;setenv stdin serial\0"
#define CONSOLE_ARG "console=console=ttyPS0,115200\0"
#define SERIAL_MULTI "serial=setenv stdout serial;setenv stdin serial\0"
#define CONFIG_BAUDRATE 115200
/* ethernet - psu_ethernet_3 */
#define CONFIG_SYS_FAULT_ECHO_LINK_DOWN
#define PHY_ANEG_TIMEOUT 20000
#define CONFIG_MII
#define CONFIG_NET_MULTI
#define CONFIG_NETCONSOLE 1
/**u-boot初始IP地址以及TFTP使用的服务器端IP地址**/
#define CONFIG_SERVERIP 192.168.3.154
#define CONFIG_IPADDR 192.168.3.101
/* spi_flash - psu_qspi_0 */
#define XILINX_PS8_QSPI_CLK_FREQ_HZ 125000000
#define CONFIG_SF_DEFAULT_SPEED (XILINX_PS8_QSPI_CLK_FREQ_HZ / 4)
#define CONFIG_MTD_UBI_WL_THRESHOLD 4096
#define CONFIG_MTD_UBI_BEB_LIMIT 0
#define CONFIG_CMD_UBI
#define CONFIG_RBTREE
#define CONFIG_CMD_UBIFS
#define CONFIG_LZO
#define CONFIG_CMD_MTDPARTS
#define CONFIG_MTD_DEVICE
#define CONFIG_MTD_PARTITIONS
#define CONFIG_SPI_FLASH_MTD
/* rtc - psu_rtc */
/* zynq_ultra_ps_e_0 */
#define COUNTER_FREQUENCY 100000000
/* intc - psu_acpu_gic */
#define ACPU_GIC_BASEADDR 0xF9010000
#define CONFIG_GICV2 1
#define GICD_BASE (ACPU_GIC_BASEADDR)
#define GICC_BASE (ACPU_GIC_BASEADDR + 0x10000)
/* FPGA */
/* Memory testing handling */
#define CONFIG_SYS_MEMTEST_START 0x0
#define CONFIG_SYS_MEMTEST_END (0x0 + 0x1000)
#define CONFIG_SYS_LOAD_ADDR (0x0 + 0x100000) /* default load address */
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_LOAD_ADDR - GENERATED_GBL_DATA_SIZE)
#define CONFIG_NR_DRAM_BANKS 2
/* Size of malloc() pool */
#define SIZE 0x2000000
#define CONFIG_SYS_MALLOC_LEN SIZE
/* BOOTP options */
#define CONFIG_BOOTP_SERVERIP
#define CONFIG_BOOTP_BOOTFILESIZE
#define CONFIG_BOOTP_BOOTPATH
#define CONFIG_BOOTP_GATEWAY
#define CONFIG_BOOTP_HOSTNAME
#define CONFIG_BOOTP_MAY_FAIL
#define CONFIG_BOOTP_DNS
#define CONFIG_BOOTP_SUBNETMASK
#define CONFIG_BOOTP_PXE
/*Command line configuration.*/
#define CONFIG_CMDLINE_EDITING
#define CONFIG_AUTO_COMPLETE
#define CONFIG_IMAGE_FORMAT_LEGACY
#define CONFIG_SUPPORT_RAW_INITRD
/* Miscellaneous configurable options */
#define CONFIG_SYS_CBSIZE 2048/* Console I/O Buffer Size */
#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16)
#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
/* Use the HUSH parser */
#define CONFIG_SYS_PROMPT_HUSH_PS2 "> "
#define CONFIG_ENV_VARS_UBOOT_CONFIG
#define CONFIG_ENV_OVERWRITE /* Allow to overwrite the u-boot environment variables */
#define CONFIG_LMB
/* FDT support */
#define CONFIG_DISPLAY_BOARDINFO_LATE
/* Boot Argument Buffer Size */
#define CONFIG_SYS_MAXARGS 64 /* max number of command args */
#define CONFIG_SYS_LONGHELP
/* Initial memory map for Linux */
#define CONFIG_SYS_BOOTMAPSZ 0x8000000
/* Environment settings*/
#define CONFIG_ENV_SPI_MAX_HZ 30000000
#define CONFIG_ENV_OFFSET 0x100000
#define CONFIG_ENV_SIZE 0x40000
#define CONFIG_ENV_SECT_SIZE 0x20000
/* PREBOOT */
#define CONFIG_PREBOOT "echo U-BOOT for zynqmp3eg;setenv preboot; echo; dhcp"
/**主要修改部分**/
/**修改内核传参参数**/
/* Extra U-Boot Env settings */
#define CONFIG_EXTRA_ENV_SETTINGS \
SERIAL_MULTI \
CONSOLE_ARG \
PSSERIAL0 \
"nc=setenv stdout nc;setenv stdin nc;\0" \
"ethaddr=00:0a:35:00:22:01\0" \
"autoload=no\0" \
"sdbootdev=0\0" \
"clobstart=0x10000000\0" \
"netstart=0x10000000\0" \
"dtbnetstart=0x23fff000\0" \
"loadaddr=0x10000000\0" \
"bootsize=0x100000\0" \
"bootstart=0x0\0" \
"boot_img=BOOT.BIN\0" \
"load_boot=tftpboot ${clobstart} ${boot_img}\0" \
"update_boot=setenv img boot; setenv psize ${bootsize}; setenv installcmd \"install_boot\"; run load_boot test_img; setenv img; setenv psize; setenv installcmd\0" \
"install_boot=sf probe 0 && sf erase ${bootstart} ${bootsize} && " \
"sf write ${clobstart} ${bootstart} ${filesize}\0" \
"bootenvsize=0x40000\0" \
"bootenvstart=0x100000\0" \
"eraseenv=sf probe 0 && sf erase ${bootenvstart} ${bootenvsize}\0" \
"jffs2_img=rootfs.jffs2\0" \
"load_jffs2=tftpboot ${clobstart} ${jffs2_img}\0" \
"update_jffs2=setenv img jffs2; setenv psize ${jffs2size}; setenv installcmd \"install_jffs2\"; run load_jffs2 test_img; setenv img; setenv psize; setenv installcmd\0" \
"install_jffs2=sf probe 0 && sf erase ${jffs2start} ${jffs2size} && " \
"sf write ${clobstart} ${jffs2start} ${filesize}\0" \
"kernelsize=0x1600000\0" \ /*flash存放内核空间长度*/
"kernelstart=0x940000\0" \ /*flash中内核存放的起始地址*/
"kernel_img=image.ub\0" \
"load_kernel=tftpboot ${clobstart} ${kernel_img}\0" \
"update_kernel=setenv img kernel; setenv psize ${kernelsize}; setenv installcmd \"install_kernel\"; run load_kernel test_crc; setenv img; setenv psize; setenv installcmd\0" \
"install_kernel=sf probe 0 && sf erase ${kernelstart} ${kernelsize} && " \
"sf write ${clobstart} ${kernelstart} ${filesize}\0" \
"cp_kernel2ram=sf probe 0 && sf read ${netstart} ${kernelstart} ${kernelsize}\0" \
"dtb_img=system.dtb\0" \
"load_dtb=tftpboot ${clobstart} ${dtb_img}\0" \
"update_dtb=setenv img dtb; setenv psize ${dtbsize}; setenv installcmd \"install_dtb\"; run load_dtb test_img; setenv img; setenv psize; setenv installcmd\0" \
"fault=echo ${img} image size is greater than allocated place - partition ${img} is NOT UPDATED\0" \
"test_crc=if imi ${clobstart}; then run test_img; else echo ${img} Bad CRC - ${img} is NOT UPDATED; fi\0" \
"test_img=setenv var \"if test ${filesize} -gt ${psize}\\; then run fault\\; else run ${installcmd}\\; fi\"; run var; setenv var\0" \
"netboot=tftpboot ${netstart} ${kernel_img} && bootm\0" \
"default_bootcmd=run cp_kernel2ram && bootm ${netstart}\0" \
""
/* BOOTCOMMAND */
#define CONFIG_BOOTCOMMAND "run default_bootcmd"
#endif /* __PLNX_CONFIG_H */
②删除掉不必要的选项。我的板子上没有eeprom、USB也没有使用I2C,所以在u-boot配置里去掉了这几项勾选。
修改u-boot环境变量存放位置为Spi Flash
2.2.5.2 u-boot设备树
设备树在目录arch/arm/dts下,选择适合自己板子的设备树。如果要使用自定义设备树,需要在该目录下的Makefile中添加自己的设备树编译。
设备树文件需要修改到与自己硬件对应,最常见的就是引脚、时钟、基地址等,比如默认配置文件的外部参考时钟是33MHz,而我自己的板子是50MHz,所以需要修改。此外还有打印串口、默认网口、DDR等配置也需要一并修改,zynqmp系列u-boot的串口打印等都挪到设备树中生效了,再去修改头文件中的宏定义已经不可行了。
还有一些特殊的配置,例如文件在flash中的分配,在linux下使用dd命令更新时也能知道该往哪个分区下载。
配置完成后执行make,进行编译。如果在编译过程中遇到有编译错误,可以在make menuconfig时把对应的配置取消掉,如果warn信息过多,可以在u-boot的顶层Makefile中添加一条取消告警的参数。
KBUILD_CFLAGS += -w
编译后得到u-boot。至此BOOT.bin的生成条件已经完全满足了,接下来就是打包了。
2.2.6 bootgen
将上述文件放到同一目录下,填写.bif文件(参照2.1.1),然后执行如下指令
bootgen -arch zynqmp -image bootgen.bif -o BOOT.BIN -w on
生成BOOT.bin后就可以下载到开发板上了, 注意询问下fpga工程师用的flash选用的哪种模式,我的是x4-sigle。
下载完成后,插上232调试串口用串口助手就能看到打印了。
2.2.7 kernel Image
linux内核部分生成比较简单,基本步骤和生成u-boot差不多。区别在于linux文件夹下目录分配不一样。下载地址卜哩啾啾哩卜哩朵/linux-xlnx
第一步依旧是make defconfig
然后make menuconfig 做一些自定义的删减和添加,我这里直接用用默认的配置,所以直接make -j8编译了,整个编译过程大概几分钟。生成的Image位置如下所示,在arch/arm64/boot目录下
2.2.8 kernel dtb
可以自定义设备树也直接使用Petalinux生成的system.dtb。自定义设备树需要加到kernel中编译,具体做法和u-boot一样,在设备树目录下的Makefile中添加上要编译的设备树,设备树就会编译进内核中。
2.2.9 rootfs.cpio.gz
文件系统制作较为简单,将Petalinux生成的rootfs.gpio.gz复制出来,解压到一个文件夹下,如下图:
【重点】,之前在这卡了很久,开发板内核加载完成即将加载文件系统时,显示需要使用root权限,纠结好久后尝试在制作文件系统时用root权限解压cpio文件才解决!解压后的文件夹下长这样
查看文件夹所有者及权限,是root
如果使用非sudo,显示如下,zynqdevelop是我的主机,开发板没有该用户自然就无权限加载了。
解压完成后就可以随意修改文件系统了~,比如修改用户名就去修改etc/hostname
sudo gedit etc/hostname
又比如添加自定义小工具,直接将编译好的可执行程序放到usr/bin目录下
sudo cp tools/memtool rootfs/usr/bin
又或者添加自启动脚本或者关机脚本,可以修改etc/init.d下的rcS和rcK两个文件,还有很多可以自定义的部分就不一一赘述了,可自行添加和修改。修改完成后,就可以重新打包成rootfs.cpio.gz了。
sh -c 'cd rootfs/ && find ./* | sudo cpio -H newc -o > rootfs.cpio && gzip rootfs.cpio'
至此,打包成image.ub的原材料也准备好了,接下来就可以打包了。
2.2.10 mkimage
mkimage -f imagegen.its image.ub
3 注意
生成的文件,在使用时其名称需要修改的和.bif和.its两个文件中的一致,image.ub烧写的时候可以使用u-boot的tftp命令,整个过程最需要细心的部分就是设备树配置,不经意错过一个小细节可能就致使整个系统工作不正常,一定要对照fpga工程连线或者原理图来搞。
其实上述过程还可以编写为一个Makefile去控制,以简化操作步骤
CROSS_COMPILE :=aarch64-linux-gnu-
ARCH :=arm64
BUILD :=build
KERNEL :=linux-xlnx-xilinx-v2018.3
KERNEL_DEFCONFIG :=xilinx_zynqmp_defconfig
KERNEL_CFG := $(BUILD)/$(KERNEL)/.config
UBOOT :=u-boot-xlnx-xilinx-v2018.3
UBOOT_DEFCONFIG :=xilinx_zynqmp_zcu102_rev1_0_defconfig
UBOOT_CFG := $(BUILD)/$(UBOOT)/.config
DTS :=zynqmp-3eg.dts
DTB :=zynqmp-3eg.dtb
BIF :=bootgen.bif
ITS :=imagegen.its
export ARCH CROSS_COMPILE
all:kernel uboot dtb rootfs bootgen imagegen
#
$(KERNEL_CFG):
make -C $(KERNEL) O=../$(BUILD)/$(KERNEL) ARCH=$(ARCH) $(KERNEL_DEFCONFIG)
kernel:$(KERNEL_CFG)
make -C $(KERNEL) O=../$(BUILD)/$(KERNEL) CROSS_COMPILE=$(CROSS_COMPILE) -j8
cp $(BUILD)/$(KERNEL)/arch/arm64/boot/Image ./image
kernel_menuconfig:$(KERNEL_CFG)
make -C $(KERNEL) O=../$(BUILD)/$(KERNEL) menuconfig
kernel_mrproper:
make -C $(BUILD)/$(KERNEL) mrproper
kernel_clean:
rm -rf $(BUILD)/$(KERNEL)
#
$(UBOOT_CFG):
make -C $(UBOOT) O=../$(BUILD)/$(UBOOT) ARCH=aarch64 CROSS_COMPILE=$(CROSS_COMPILE) $(UBOOT_DEFCONFIG)
uboot:$(UBOOT_CFG)
make -C $(UBOOT) O=../$(BUILD)/$(UBOOT) CROSS_COMPILE=$(CROSS_COMPILE) -j8
cp $(BUILD)/$(UBOOT)/u-boot.elf ./image/bootgen
#cp $(BUILD)/$(UBOOT)/arch/arm/dts/$(DTB) ./image
uboot_menuconfig:$(UBOOT_CFG)
make -C $(UBOOT) O=../$(BUILD)/$(UBOOT) menuconfig
uboot_mrproper:
make -C $(BUILD)/$(UBOOT) mrproper
uboot_clean:
rm -rf $(BUILD)/$(UBOOT)
bootgen:
# cp zynqsrc/* image
bootgen -arch zynqmp -image $(BIF) -o image/bootgen/BOOT.BIN -w on
imagegen:
mkimage -f $(ITS) image/imagegen/image.ub
# DTB
dtb:
dtc -I dts -O dtb zynqMPDTS/$(DTS) -o zynqMPDTS/$(DTB)
cp zynqMPDTS/$(DTB) ./image
dts:
dtc -I dtb -O dts zynqMPDTS/$(DTB) -o zynqMPDTS/_$(DTS)
# rootfs
release_fs:
mkdir rootfs
gunzip -c rootfs.cpio.gz | sh -c 'cd rootfs && sudo cpio -i'
fs:
sh -c 'cd rootfs/ && find ./* | sudo cpio -H newc -o > rootfs.cpio && gzip rootfs.cpio'
mv rootfs/rootfs.cpio.gz image/imagegen
clean:uboot_clean kernel_clean