1. 程式人生 > >zynqMP LINUX 啟動流程和移植

zynqMP LINUX 啟動流程和移植

最近花了幾天時間完成了zynqMP linux的移植工作,這裡記錄一下工作的流程。


zynqMP linux 啟動過程

下圖時xilinx手冊上摘錄的圖,描述了zynqMP 上的linux的整個boot過程
這裡寫圖片描述
  系統復位後,首先PMU(Platform Management Unit)會執行PMU ROM中固化程式碼,執行完後會啟動CSU處理核,CSU會負責從啟動儲存介質中載入FSBL

(First Stage Boot Loader)至on-chip ram中,FSBL可以由RPU負責執行也可由APU負責執行,須在製作FSBL時確定。繼而,CSU激勵RPU或APU執行FSBL。FSBL會載入PMU Frameware交付PMU執行。繼而FSBL執行完成後切換至ATF(Arm Trusted Frame),ATF於APU上執行。然後ATF啟動u-boot,u-boot為linux配置好執行環境將執行權交付給Linux核心。


U-BOOT製作

從xilinx wiki上下載u-boot-xlnx
檢視 board/xilinx/zynqmp/zynqmp.c 中board_late_init

int board_late_init(void)
{
    u32 ver, reg = 0;
    u8 bootmode;
    const char *mode;
    char *new_targets;
    int ret;

    if (!(gd->flags & GD_FLG_ENV_DEFAULT)) {
        debug("Saved variables - Skipping\n");
        return 0;
    }

    ver = zynqmp_get_silicon_version();

    switch
(ver) { case ZYNQMP_CSU_VERSION_VELOCE: setenv("setup", "setenv baudrate 4800 && setenv bootcmd run veloce"); case ZYNQMP_CSU_VERSION_EP108: case ZYNQMP_CSU_VERSION_SILICON: setenv("setup", "setenv partid auto"); break; case ZYNQMP_CSU_VERSION_QEMU: default: setenv("setup", "setenv partid 0"); } ret = zynqmp_mmio_read((ulong)&crlapb_base->boot_mode, &reg); if (ret) return -EINVAL; if (reg >> BOOT_MODE_ALT_SHIFT) reg >>= BOOT_MODE_ALT_SHIFT; bootmode = reg & BOOT_MODES_MASK; puts("Bootmode: "); switch (bootmode) { case USB_MODE: puts("USB_MODE\n"); mode = "usb"; setenv("modeboot", "usb_dfu_spl"); break; case JTAG_MODE: puts("JTAG_MODE\n"); mode = "pxe dhcp"; setenv("modeboot", "jtagboot"); break; case QSPI_MODE_24BIT: case QSPI_MODE_32BIT: mode = "qspi0"; puts("QSPI_MODE\n"); setenv("modeboot", "qspiboot"); break; case EMMC_MODE: puts("EMMC_MODE\n"); mode = "mmc0"; setenv("modeboot", "emmcboot"); break; case SD_MODE: puts("SD_MODE\n"); mode = "mmc0"; setenv("modeboot", "sdboot"); break; case SD1_LSHFT_MODE: puts("LVL_SHFT_"); /* fall through */ case SD_MODE1: puts("SD_MODE1\n"); #if defined(CONFIG_ZYNQ_SDHCI0) && defined(CONFIG_ZYNQ_SDHCI1) mode = "mmc1"; setenv("sdbootdev", "1"); #else mode = "mmc0"; #endif setenv("modeboot", "sdboot"); printf("modeboot:%s\n","sdboot"); break; case NAND_MODE: puts("NAND_MODE\n"); mode = "nand0"; setenv("modeboot", "nandboot"); break; default: mode = ""; printf("Invalid Boot Mode:0x%x\n", bootmode); break; } /* * One terminating char + one byte for space between mode * and default boot_targets */ new_targets = calloc(1, strlen(mode) + strlen(getenv("boot_targets")) + 2); sprintf(new_targets, "%s %s", mode, getenv("boot_targets")); setenv("boot_targets", new_targets); return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

這裡檢查了zynqMP的啟動方式,並設定了環境引數 modeboot ,此次測試使用sd卡boot,故modeboot 會被設定為sdboot,故u-boot會執行run sdboot命令,其中sdboot 環境引數設定在include/configs/xilinx_zynqmp.h中定義,如下所示

/* Initial environment variables */
#ifndef CONFIG_EXTRA_ENV_SETTINGS
#define CONFIG_EXTRA_ENV_SETTINGS \
    "kernel_addr=0x80000\0" \
    "initrd_addr=0xa00000\0" \
    "initrd_size=0x2000000\0" \
    "fdt_addr=4000000\0" \
    "fdt_high=0x10000000\0" \
    "loadbootenv_addr=0x100000\0" \
    "sdbootdev=0\0"\
    "kernel_offset=0x180000\0" \
    "fdt_offset=0x100000\0" \
    "kernel_size=0x1e00000\0" \
    "fdt_size=0x80000\0" \
    "bootenv=uEnv.txt\0" \
    "serverip=192.168.1.18\0" \
    "bootargs=earlycon clk_ignore_unused\0" \
    "loadbootenv=load mmc $sdbootdev:$partid ${loadbootenv_addr} ${bootenv}\0" \
    "importbootenv=echo Importing environment from SD ...; " \
        "env import -t ${loadbootenv_addr} $filesize\0" \
    "sd_uEnvtxt_existence_test=test -e mmc $sdbootdev:$partid /uEnv.txt\0" \
    "sata_root=if test $scsidevs -gt 0; then setenv bootargs $bootargs root=/dev/sda rw rootfstype=ext4; fi\0" \
    "sataboot=load scsi 0 80000 boot/Image && load scsi 0 $fdt_addr boot/system.dtb && booti 80000 - $fdt_addr\0" \
    "veloce=fdt addr f000000 && fdt resize" \
        "fdt set /amba/misc_clk clock-frequency <48000> && "\
        "fdt set /timer clock-frequency <240000> && " \
        "fdt set /amba/i2c_clk clock-frequency <240000> && " \
        "booti 80000 - f000000\0" \
    "netboot=tftpboot 10000000 image.ub && bootm\0" \
    "qspiboot=sf probe 0 0 0 && sf read $fdt_addr $fdt_offset $fdt_size && " \
          "sf read $kernel_addr $kernel_offset $kernel_size && " \
          "booti $kernel_addr - $fdt_addr\0" \
    "uenvboot=" \
        "if run sd_uEnvtxt_existence_test; then " \
            "run loadbootenv; " \
            "echo Loaded environment from ${bootenv}; " \
            "run importbootenv; " \
        "fi; " \
        "if test -n $uenvcmd; then " \
            "echo Running uenvcmd ...; " \
            "run uenvcmd; " \
        "fi\0" \
    "sdboot=mmc dev $sdbootdev && mmcinfo && run uenvboot;" \
        "setenv ipaddr   192.168.1.15 ;" \
        "setenv serverip 192.168.1.18 ;" \
        "setenv netmask 255.255.255.0 ;" \
        "ping $serverip ;" \
        "setenv bootargs console=ttyPS0,115200 root=/dev/nfs nfsroot=192.168.1.18:/home/rootfs,tcp ip=dhcp rw earlyprintk rootwait ;" \
        "tftpboot $kernel_addr Image &&" \
        "tftpboot $fdt_addr system.dtb &&" \
        "booti $kernel_addr - $fdt_addr\0" \
    "emmcboot=run sdboot\0" \
    "nandboot=nand info && nand read $fdt_addr $fdt_offset $fdt_size && " \
          "nand read $kernel_addr $kernel_offset $kernel_size && " \
          "booti $kernel_addr - $fdt_addr\0" \
    "xen_prepare_dt=fdt addr $fdt_addr && fdt resize 128 && " \
        "fdt set /chosen \\\\#address-cells <1> && " \
        "fdt set /chosen \\\\#size-cells <1> && " \
        "fdt mknod /chosen dom0 && " \
        "fdt set /chosen/dom0 compatible \"xen,linux-zimage\" \"xen,multiboot-module\" && " \
        "fdt set /chosen/dom0 reg <0x80000 0x$filesize> && " \
        "fdt set /chosen xen,xen-bootargs \"console=dtuart dtuart=serial0 dom0_mem=768M bootscrub=0 maxcpus=1 timer_slop=0\" && " \
        "fdt set /chosen xen,dom0-bootargs \"console=hvc0 earlycon=xen earlyprintk=xen maxcpus=1 clk_ignore_unused\"\0" \
    "xen_prepare_dt_qemu=run xen_prepare_dt && " \
        "fdt set /cpus/[email protected] device_type \"none\" && " \
        "fdt set /cpus/[email protected] device_type \"none\" && " \
        "fdt set /cpus/[email protected] device_type \"none\" && " \
        "fdt rm /cpus/[email protected] compatible && " \
        "fdt rm /cpus/[email protected] compatible && " \
        "fdt rm /cpus/[email protected] compatible\0" \
    "xen=tftpb $fdt_addr system.dtb &&  tftpb 0x80000 Image &&" \
        "run xen_prepare_dt && " \
        "tftpb 6000000 xen.ub && tftpb 0x1000000 image.ub && " \
        "bootm 6000000 0x1000000 $fdt_addr\0" \
    "xen_qemu=tftpb $fdt_addr system.dtb && tftpb 0x80000 Image && " \
        "run xen_prepare_dt_qemu && " \
        "tftpb 6000000 xen.ub && tftpb 0x1000000 image.ub && " \
        "bootm 6000000 0x1000000 $fdt_addr\0" \
    "jtagboot=tftpboot 80000 Image && tftpboot $fdt_addr system.dtb && " \
         "tftpboot 6000000 rootfs.cpio.ub && booti 80000 6000000 $fdt_addr\0" \
    "nosmp=setenv bootargs $bootargs maxcpus=1\0" \
    "nfsroot=setenv bootargs $bootargs root=/dev/nfs nfsroot=$serverip:/mnt/sata,tcp ip=$ipaddr:$serverip:$serverip:255.255.255.0:zynqmp:eth0:off rw\0" \
    "sdroot0=setenv bootargs $bootargs root=/dev/mmcblk0p2 rw rootwait\0" \
    "sdroot1=setenv bootargs $bootargs root=/dev/mmcblk1p2 rw rootwait\0" \
    "android=setenv bootargs $bootargs init=/init androidboot.selinux=disabled androidboot.hardware=$board\0" \
    "android_debug=run android && setenv bootargs $bootargs video=DP-1:[email protected] drm.debug=0xf\0" \
    "usb_dfu_spl=booti $kernel_addr - $fdt_addr\0" \
    "usbhostboot=usb start && load usb 0 $fdt_addr system.dtb && " \
             "load usb 0 $kernel_addr Image && " \
             "booti $kernel_addr - $fdt_addr\0" \
    PARTS_DEFAULT \
    DFU_ALT_INFO
#endif

#define CONFIG_PREBOOT      "run setup"
#define CONFIG_BOOTCOMMAND  "run $modeboot"
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

其中sdboot為

    "sdboot=mmc dev $sdbootdev && mmcinfo && run uenvboot;" \
        "setenv ipaddr   192.168.1.15 ;" \
        "setenv serverip 192.168.1.18 ;" \
        "setenv netmask 255.255.255.0 ;" \
        "ping $serverip ;" \
        "setenv bootargs console=ttyPS0,115200 root=/dev/nfs nfsroot=192.168.1.18:/home/rootfs,tcp ip=dhcp rw earlyprintk rootwait ;" \
        "tftpboot $kernel_addr Image &&" \
        "tftpboot $fdt_addr system.dtb &&" \
        "booti $kernel_addr - $fdt_addr\0" \
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

此處實現了TFTP形式的boot以及nfs檔案系統,這裡就不解釋TFTP Boot及nfs(xilinx預設配置是從sd卡第一分割槽(FAT格式)讀取devicetree以及Image實現boot,並以sd卡第二分割槽(ext4格式)作為系統的跟檔案系統)。

至此可以對u-boot編譯

cd $u-boot-xlnx-dir
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make distclean
make xilinx_zynqmp_zcu102_rev1_0_defconfig
make -j4  #-jn 為你要執行的核數
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在工程根目錄下面生成u-boot.elf



ATF編譯

下載Arm-Trusted-Firmware

cd $Arm-Trusted-Firmware
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make PLAT=zynqmp RESET_TO_BL31=1
  
  • 1
  • 2
  • 3
  • 4

由於一開始用的gcc為apt安裝,此版本有bug,
故去linaro官網下載,下載版本為7.2
生成的 ATF在/build/zynqmp/release/bl31



BOOT.BIN製作

使用Xilinx SDK中 Xilinx->Create Boot Image製作BOOT.BIN
bif檔案如下所示:

//arch = zynqmp; split = false; format = BIN
the_ROM_image:
{
    [fsbl_config]a53_x64
    [bootloader]PROJECT_DIR/project_1.sdk/fsbl/Debug/fsbl.elf
    [pmufw_image]PROJECT_DIR/project_1.sdk/pmu/Debug/pmu.elf
    [destination_device = pl]PROJECT_DIR/project_1.sdk/design_1_wrapper_hw_platform_0/design_1_wrapper.bit
    [destination_cpu = a53-0, exception_level = el-3]PROJECT_DIR/project_1.sdk/bl31.elf
    [destination_cpu = a53-0, exception_level = el-2]PROJECT_DIR/project_1.sdk/u-boot.elf
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其中PMU、FSBL由SDK中生成

在編譯PMU的時候碰到如下問題

mb-gcc未找到
  
  • 1

解決辦法:

sudo dpkg --add-architecture i386
sudo apt install libstdc++5:i386 
  
  • 1
  • 2


然後又出現如下問題:

mb-gcc: error while loading shared libraries: libstdc++.so.6: wrong ELF class: ELFCLASS64 
  
  • 1

解決方法:

sudo apt install gcc-multilib 
  
  • 1



LINUX編譯

下載xilinx linux

cd linux-xlnx
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make xilinx_zynqmp_defconfig
make menuconfig
make -j4  #-jn n為你要執行的核數