Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 28 Current »

This article describes how to install CentOS or Fedora on a disk image from an ISO installer image using Qemu. This disk image can be copied to an SD card to boot the ZCU102.

Table of Contents

Introduction

Linux distros generally distribute ISO files for installation. This works well for PC and server platforms, but generally not for embedded platforms. In this article we will use QEMU with a generic ARM64 model to install CentOS and Fedora from an ISO onto a raw disk image. We will install CentOS 8 and Fedora 29 as both of these ship with 4.18 kernel which has enough upstreamed driver support for Zynq Ultrascale+.

Installing a CentOS or Fedora system for Zynq UltraScale+ is currently a proof-of-concept (POC). You should not assume this builds a Xilinx supported, production-ready system. Please do your due diligence before deploying CentOS or Fedora and regression test against your system requirements.

While this flow targets CentOS and Fedora because they are close relatives, this flow can be extended to support Ubuntu, Debian and openSUSE.

Requirements

  • Linux VM or bare-metal machine

    • Sudo privileges

    • Terminal emulator

    • Internet access

  • ZCU102 evaluation board

    • 8 GB (minimum)/16 GB (recommended) SD card

Linux Host Machine

Install Host Package Dependencies

CentOS/Fedora/RHEL

$ sudo dnf group install "Virtualization Host"
$ sudo dnf install wget kpartx epel-release

Ubuntu/Debian

$ sudo apt-get install qemu wget kpartx

Install QEMU for Aarch64

CentOS/Fedora/RHEL

$ sudo dnf install qemu-system-aarch64

Ubuntu/Debian

$ sudo apt-get install qemu-system-arm

If the install fails you may need to build QEMU from source. If the install succeeds, you may skip to “Prepare Workspace.”

Build QEMU for Aarch64

CentOS/Fedora/RHEL

$ sudo dnf group install "Development Tools"
$ sudo dnf install glib2-devel pixman-devel

Ubuntu/Debian

$ sudo apt-get install pkg-config libglib2.0-dev libpixman-1-dev git-core build-essential

Build QEMU

$ git clone https://git.qemu.org/git/qemu.git
$ cd qemu
$ git checkout -b v4.1.1.xlnx v4.1.1 # or later stable tag
$ git submodule update --init --recursive
$ ./configure --target-list=aarch64-softmmu,microblazeel-softmmu --disable-kvm --disable-xen
$ make

Recommend adding the path for the newly built aarch64 Qemu to your PATH variable in “.bashrc”, which should be $HOME/qemu/aarch64-softmmu.

Prepare Workspace

Create a directory structure for your workspace.

$ export COS_BUILD=$HOME/centos8-dev
$ mkdir -p $COS_BUILD/rpmbuild $COS_BUILD/images/{efi,boot} $COS_BUILD/deploy/{efi/dtb/xilinx,boot}
$ export IMG_ROOT= $COS_BUILD/images
$ cd $IMG_ROOT

Download the UEFI boot image for the QEMU generic ARM model.

$ wget https://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.img.gz
$ gunzip QEMU_EFI.img.gz

Create the QEMU script to run the ISO installer.

These scripts use the QEMU generic ARM model vs. the ZCU102 model. This allows us to use a full-blown UEFI bootloader from Linaro and does not rely on a properly configured kernel for Zynq UltraScale+.

#!/bin/bash
# Install distro from an ISO onto a virtual disk image
# . runqemu-iso <distro>.iso

iso=$1
os=${iso%.iso}
size=7G

disk_img=$os.qcow2
efi_img=$os.efi

qemu-img create -f qcow2 $disk_img ${size}
qemu-img create -f qcow2 $efi_img 64M

qemu-system-aarch64 \
    -cpu cortex-a53 -M virt -m 4096 -nographic -smp $(nproc) -boot d \
    -drive if=pflash,format=raw,file=QEMU_EFI.img \
    -drive if=pflash,format=qcow2,file=${efi_img} \
    -device virtio-scsi-device \
    -device scsi-cd,drive=cdrom \
    -device virtio-blk-device,drive=hd0 \
    -drive file=${iso},id=cdrom,media=cdrom,if=none \
    -drive file=${disk_img},id=hd0,if=none

The actual storage space on your SD card is less than the size on the label. This script creates a 7 GB image targeting an 8 GB SD card.

Create the QEMU script to boot from a QCOW2 or raw image.

#!/bin/bash
# Boot distro from a QCOW2 or raw image
# . runqemu-img <distro>.qcow2

disk_img=$1
efi_img=${1%.*}.efi

qemu-system-aarch64 \
    -cpu cortex-a53 -M virt -m 4096 -nographic -smp $(nproc) \
    -drive if=pflash,format=raw,file=QEMU_EFI.img \
    -drive if=pflash,format=qcow2,file=${efi_img} \
    -device virtio-blk-device,drive=hd0 \
    -drive file=${disk_img},id=hd0,if=none 

CentOS

Install CentOS on Virtual Disk from ISO

Download an ISO from one of mirrors for aarch64 CentOS 8.

$ cd $IMG_ROOT
$ wget http://mirrors.usc.edu/pub/linux/distributions/centos/8.0.1905/isos/aarch64/CentOS-8-aarch64-1905-dvd1.iso
$ wget http://mirrors.usc.edu/pub/linux/distributions/centos/8.0.1905/isos/aarch64/CHECKSUM
$ sha256sum -c CHECKSUM
CentOS-8-aarch64-1905-boot.iso: OK

Depending on the number of ISOs included in the CHECKSUM file, you may get failures for files you have not downloaded. This is OK.

$ . runqemu-iso CentOS-8-aarch64-1905-dvd1.iso

At the Grub menu, select install Linux and follow the installation menus. Configure as you see fit and let the VM reboot.

Prepare the Virtual Disk Image for ZCU102

Login as root or switch to superuser (su). Since the initramfs is built with “hostonly” support, we will force dracut to add the MMC and SDHCI drivers.

# echo "add_drivers+=\"mmc_block sdhci-of-arasan\"" > /etc/dracut.conf.d/zcu102.conf
# dracut --force

If your ZCU102 fails to boot, you can try adding all the kernel modules to the initramfs with dracut “no-hostonly” option.

# dracut --force --no-hostonly

Power down the VM.

# systemctl poweroff

Convert the QCOW2 image to a raw disk image.

$ qemu-img convert CentOS-8-aarch64-1905-dvd1.qcow2 CentOS-8-aarch64-1905-dvd1.raw

I recommend archiving the QCOW2 image if you ever need to refer back to a clean installation.

Create loop devices for the raw disk image. Mount the 2nd partition onto the “boot” directory.

$ sudo kpartx -va CentOS-8-aarch64-1905-dvd1.raw
add map loop0p1 (253:3): 0 1228800 linear 7:1 2048
add map loop0p2 (253:4): 0 2097152 linear 7:1 1230848
add map loop0p3 (253:5): 0 11350016 linear 7:1 3328000
$ sudo mount /dev/mapper/loop0p2 boot

Next grep the kernel configuration looking for a couple key options to see if we at least have a console and SDHCI support.

$ grep 'ARCH_ZYNQMP\|SERIAL_XILINX\|SDHCI_OF_ARASAN' boot/config-4.18.0-80.el8.aarch64 
# CONFIG_ARCH_ZYNQMP is not set
# CONFIG_SERIAL_XILINX_PS_UART is not set
# CONFIG_MMC_SDHCI_OF_ARASAN is not set

The default CentOS 8 kernel may not boot on the ZCU102, so you may need to build and install a new kernel. Please see Build a CentOS 8 System for Zynq UltraScale+ to generate the kernel RPMS if the CONFIG_* requirements above are not met.

$ sudo cp ../rpmbuild/kernel/RPMS/aarch64/kernel-4.18.0-80.el8.xlnx.aarch64.rpm \
../rpmbuild/kernel/RPMS/aarch64/kernel-core-4.18.0-80.el8.xlnx.aarch64.rpm \
../rpmbuild/kernel/RPMS/aarch64/kernel-modules-4.18.0-80.el8.xlnx.aarch64.rpm \
../rpmbuild/kernel/RPMS/aarch64/kernel-headers-4.18.0-80.el8.xlnx.aarch64.rpm \
boot/

Unmount the boot directory and cleanup the mappings.

$ sudo umount boot
$ sudo kpartx -d CentOS-8-aarch64-1905-dvd1.raw
loop deleted : /dev/loop0

Boot the system with the raw disk image and we will install the kernel RPM packages in the QEMU VM.

$ . runqemu-img CentOS-8-aarch64-1905-dvd1.raw

Login as root and install the kernel.

zcu102 login: root
Password:
[root@localhost ~]# rpm -ivh kernel-*.rpm

Once the kernel installation is complete, power-off the VM.

[root@localhost ~]# systemctl poweroff

Next lets check the partition scheme on the raw disk image. The installer should have used a GPT scheme.

fdisk -l CentOS-8-aarch64-1905-dvd1.raw
Disk CentOS-8-aarch64-1905-dvd1.raw: 7 GiB, 7516192768 bytes, 14680064 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: A01B0908-71D5-4058-A016-AE386F645BD9

Device                              Start       End  Sectors  Size Type
CentOS-8-aarch64-1905-dvd1.raw1   2048      1230847  1228800  600M EFI System
CentOS-8-aarch64-1905-dvd1.raw2   1230848   3327999  2097152    1G Linux filesystem
CentOS-8-aarch64-1905-dvd1.raw3   3328000   14678015 11350016  5.4G Linux LVM

However, the Zynq UltraScale+ ROM requires an MBR scheme. So let’s convert the GPT to MBR.

$ sgdisk -m 1:2:3 CentOS-8-aarch64-1905-dvd1.raw
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
GPT data structures destroyed! You may now partition the disk using fdisk or
other utilities.

Mark the 1st partition with the boot flag.

$ sfdisk -A CentOS-8-aarch64-1905-dvd1.raw 1
The bootable flag on partition 1 is enabled now.

The partition table has been altered.
Syncing disks.

Lets check to make sure the partition scheme converted correctly. You should see “Disklabel type: dos” and the Boot flag marked.

$ fdisk -l CentOS-8-aarch64-1905-dvd1.raw
Disk CentOS-8-aarch64-1905-dvd1.raw: 7 GiB, 7516192768 bytes, 14680064 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device                          Boot   Start      End  Sectors  Size Id Type
CentOS-8-aarch64-1905-dvd1.raw1 *       2048  1230847  1228800  600M ef EFI (FAT
CentOS-8-aarch64-1905-dvd1.raw2      1230848  3327999  2097152    1G 83 Linux
CentOS-8-aarch64-1905-dvd1.raw3      3328000 14678015 11350016  5.4G 8e Linux LV

Now lets create mappings for the raw image and mount the 1st partition on “efi.”

$ sudo kpartx -va CentOS-8-aarch64-1905-dvd1.raw
add map loop0p1 (253:5): 0 1228800 linear 7:2 2048
add map loop0p2 (253:6): 0 2097152 linear 7:2 1230848
add map loop0p3 (253:7): 0 11350016 linear 7:2 3328000
$ sudo mount /dev/mapper/loop0p1 efi

Install UEFI (boot.bin), DTB and replace shim with GRUB since we are not using PC secure boot.

$ sudo cp boot.bin efi/
$ mkdir -p efi/dtb/xilinx/
$ sudo cp zynqmp-zcu102-rev1.0.dtb efi/dtb/xilinx/
$ sudo cp efi/EFI/centos/grubaa64.efi efi/EFI/BOOT/BOOTAA64.EFI

Unmount the “efi” partition and delete the mappings.

$ sudo umount efi
$ sudo kpartx -d CentOS-8-aarch64-1905-dvd1.raw
loop deleted : /dev/loop0

Finally copy the raw disk image to the SD card and boot the ZCU102.

$ sudo dd if=CentOS-8-aarch64-1905-dvd1.raw of=/dev/sdX bs=4M iflag=fullblock oflag=direct status=progress; sync

Boot CentOS on ZCU102

Fedora

Install Fedora on Virtual Disk from ISO

$ cd $IMG_ROOT
$ wget https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/29/Server/aarch64/iso/Fedora-Server-dvd-aarch64-29-1.2.iso
$ wget https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/29/Server/aarch64/iso/Fedora-Server-29-1.2-aarch64-CHECKSUM
$ sha256sum -c Fedora-Server-29-1.2-aarch64-CHECKSUM 
Fedora-Server-dvd-aarch64-29-1.2.iso: OK
$ . install-iso Fedora-Server-dvd-aarch64-29-1.2.iso

At the Grub menu, select Install Linux and follow the installation menus. Configure as you see fit and let the VM reboot.

Prepare the Virtual Disk Image for ZCU102

Login as root or switch to superuser (su). Since the initramfs is built with “hostonly” support, we will force dracut to add the MMC and SDHCI drivers. You can verify the presence of these modules in the initramfs using “lsinitrd” before and after running dracut.

# echo "add_drivers+=\"mmc_block sdhci-of-arasan\"" > /etc/dracut.conf.d/zcu102.conf
# dracut --force

If your ZCU102 fails to boot, you can try adding all the kernel modules to the initramfs with dracut “no-hostonly” option.

# dracut --force --no-hostonly

Power down the VM.

# systemctl poweroff

First you will need to first convert the QCOW2 image to a raw disk image.

$ qemu-img convert Fedora-Server-dvd-aarch64-29-1.2.qcow2 Fedora-Server-dvd-aarch64-29-1.2.raw

I recommend archiving the QCOW2 image if you ever need to refer back to a virgin installation.

Create loop devices for the raw disk image. Mount the 2nd partition onto the “boot” directory.

$ sudo kpartx -va Fedora-Server-dvd-aarch64-29-1.2.raw 
add map loop0p1 (253:3): 0 409600 linear 7:0 2048
add map loop0p2 (253:4): 0 2097152 linear 7:0 411648
add map loop0p3 (253:5): 0 12169216 linear 7:0 2508800
$ sudo mount /dev/mapper/loop0p2 boot

Next check the kernel to see if we believe it will boot the ZCU102. Let’s grep the configuration looking for a couple key options.

$ grep 'ARCH_ZYNQMP\|SERIAL_XILINX\|SDHCI_OF_ARASAN' boot/config-4.18.16-300.fc29.aarch64 
CONFIG_ARCH_ZYNQMP=y
CONFIG_SERIAL_XILINX_PS_UART=y
CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
CONFIG_MMC_SDHCI_OF_ARASAN=m

It appears there are enough configuration options to at least get a console and mount the SD card.

Unmount the boot directory and cleanup the mappings.

$ sudo umount boot
$ sudo kpartx -d Fedora-Server-dvd-aarch64-29-1.2.raw
loop deleted : /dev/loop0

Next lets check the partition scheme on the raw disk image. The installer should have used a GPT scheme.

$ fdisk -l Fedora-Server-dvd-aarch64-29-1.2.raw
Disk Fedora-Server-dvd-aarch64-29-1.2.raw: 7 GiB, 7516192768 bytes, 14680064 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: BA9EE671-D41D-4199-B3FF-4F4A54B09E79

Device                                  Start      End  Sectors  Size Type
Fedora-Server-dvd-aarch64-29-1.2.raw1    2048   411647   409600  200M EFI System
Fedora-Server-dvd-aarch64-29-1.2.raw2  411648  2508799  2097152    1G Linux file
Fedora-Server-dvd-aarch64-29-1.2.raw3 2508800 14678015 12169216  5.8G Linux LVM

However, the Zynq UltraScale+ ROM requires an MBR scheme. So let’s convert the GPT to MBR.

$ sgdisk -m 1:2:3 Fedora-Server-dvd-aarch64-29-1.2.raw 
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
GPT data structures destroyed! You may now partition the disk using fdisk or
other utilities.

Also, mark the 1st partition with the boot flag.

$ sfdisk -A Fedora-Server-dvd-aarch64-29-1.2.raw 1
The bootable flag on partition 1 is enabled now.

The partition table has been altered.
Syncing disks.

Now lets check to make sure the partition scheme converted correctly. You should see “Disklabel type: dos” and the Boot flag marked.

$ fdisk -l Fedora-Server-dvd-aarch64-29-1.2.raw
Disk Fedora-Server-dvd-aarch64-29-1.2.raw: 7 GiB, 7516192768 bytes, 14680064 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device                                Boot   Start      End  Sectors  Size Id Type
Fedora-Server-dvd-aarch64-29-1.2.raw1 *       2048   411647   409600  200M ef EFI 
Fedora-Server-dvd-aarch64-29-1.2.raw2       411648  2508799  2097152    1G 83 Linu
Fedora-Server-dvd-aarch64-29-1.2.raw3      2508800 14678015 12169216  5.8G 8e Linu

Now lets create mappings for the raw image and mount the 1st partition on “efi” and the 2nd partition on “boot”.

$ sudo kpartx -va Fedora-Server-dvd-aarch64-29-1.2.raw  
add map loop0p1 (253:3): 0 409600 linear 7:0 2048
add map loop0p2 (253:4): 0 2097152 linear 7:0 411648
add map loop0p3 (253:5): 0 12169216 linear 7:0 2508800
$ sudo mount /dev/mapper/loop0p1 efi
$ sudo mount /dev/mapper/loop0p2 boot

Install UEFI (boot.bin), DTB and replace shim with GRUB since we are not using PC secure boot.

$ sudo cp boot.bin efi/
$ mkdir -p efi/dtb/xilinx/
$ sudo cp boot/dtb-4.18.16-300.fc29.aarch64/xilinx/zynqmp-zcu102-rev1.0.dtb efi/dtb/xilinx/
$ sudo cp efi/EFI/fedora/grubaa64.efi efi/EFI/BOOT/BOOTAA64.EFI

Unmount the “efi” and “boot” partitions. Then delete the mappings.

$ sudo umount efi
$ sudo umount boot
$ sudo kpartx -d Fedora-Server-dvd-aarch64-29-1.2.raw
loop deleted : /dev/loop0

Finally copy the raw disk image to the SD card and boot the ZCU102.

$ sudo dd if=Fedora-Server-dvd-aarch64-29-1.2.raw of=/dev/sdX bs=4M iflag=fullblock oflag=direct status=progress; sync

Boot Fedora on ZCU102

Fedora (Quick Install)

This quick install leverages a preinstalled RPI ARMv8 disk image.

Install Fedora from a Raw Disk Image

Fedora also provides raw disk images. You may download the raw disk image for a quicker install.

$ wget https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/29/Server/aarch64/images/Fedora-Server-29-1.2.aarch64.raw.xz
$ wget https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/29/Server/aarch64/images/Fedora-Server-29-1.2-aarch64-CHECKSUM
$ sha256sum -c Fedora-Server-29-1.2-aarch64-CHECKSUM
Fedora-Server-29-1.2.aarch64.raw.xz: OK
$ xz -d Fedora-Server-29-1.2.aarch64.raw.xz

Prepare the Virtual Disk Image for ZCU102

The raw image is already formatted as MBR, so we don’t need to modify the partitioning scheme.

$ fdisk -l Fedora-Server-29-1.2.aarch64.raw
Disk Fedora-Server-29-1.2.aarch64.raw: 7 GiB, 7516192768 bytes, 14680064 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x180b287d

Device                            Boot   Start      End  Sectors  Size Id Type
Fedora-Server-29-1.2.aarch64.raw1 *       2048   411647   409600  200M  6 FAT16
Fedora-Server-29-1.2.aarch64.raw2       411648  2508799  2097152    1G 83 Linux
Fedora-Server-29-1.2.aarch64.raw3      2508800 14680063 12171264  5.8G 8e Linux 

Now lets create mappings for the raw image and mount the 1st partition on “efi” and the 2nd partition on “boot”.

$ sudo kpartx -va Fedora-Server-29-1.2.aarch64.raw 
[sudo] password for emil: 
add map loop0p1 (253:3): 0 409600 linear 7:0 2048
add map loop0p2 (253:4): 0 2097152 linear 7:0 411648
add map loop0p3 (253:5): 0 12171264 linear 7:0 2508800
$ sudo mount /dev/mapper/loop0p1 efi
$ sudo mount /dev/mapper/loop0p2 boot

Install UEFI (boot.bin), DTB and replace shim with GRUB since we are not using PC secure boot.

$ sudo cp boot.bin efi/
$ mkdir -p efi/dtb/xilinx/
$ sudo cp boot/dtb/xilinx/zynqmp-zcu102-rev1.0.dtb efi/dtb/xilinx/
$ sudo cp efi/EFI/fedora/grubaa64.efi efi/EFI/BOOT/BOOTAA64.EFI

The “efi” directory also contains RPI support images and files. You may delete them or leave them as they are benign.

Next check the kernel to see if we believe it will boot the ZCU102. Let’s grep the configuration looking for a couple key options.

$ grep 'ARCH_ZYNQMP\|SERIAL_XILINX\|SDHCI_OF_ARASAN' boot/config-4.18.16-300.fc29.aarch64
CONFIG_ARCH_ZYNQMP=y
CONFIG_SERIAL_XILINX_PS_UART=y
CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
CONFIG_MMC_SDHCI_OF_ARASAN=m

It appears there are enough configuration options to at least get a console and mount the SD card.

Check the initramfs for mmc_block and sdhci-of-arasan kernel modules.

$ sudo lsinitrd boot/initramfs-4.18.16-300.fc29.aarch64.img | grep 'mmc_block\|sdhci-of-arasan'
-rw-r--r--   1 root     root        18740 Jul 26  2018 usr/lib/modules/4.18.16-300.fc29.aarch64/kernel/drivers/mmc/core/mmc_block.ko.xz
-rw-r--r--   1 root     root         5800 Jul 26  2018 usr/lib/modules/4.18.16-300.fc29.aarch64/kernel/drivers/mmc/host/sdhci-of-arasan.ko.xz

These modules are installed in the initramfs, so we should be able to mount a block device on the SD card.

Unmount the “efi” and “boot” partitions and delete the mappings.

$ sudo umount efi
$ sudo umount boot
$ sudo kpartx -d Fedora-Server-29-1.2.aarch64.raw
loop deleted : /dev/loop0

Finally copy the raw disk image to the SD card and boot the ZCU102.

$ sudo dd if=Fedora-Server-29-1.2.aarch64.raw of=/dev/sdX bs=4M iflag=fullblock oflag=direct status=progress; sync

References

  • No labels