| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0+ |
| # |
| # Script to build an EFI thing suitable for booting with QEMU, possibly running |
| # it also. |
| |
| # This just an example. It assumes that |
| |
| # - you build U-Boot in ${ubdir}/<name> where <name> is the U-Boot board config |
| # - /mnt/x is a directory used for mounting |
| # - you have access to the 'pure UEFI' builds for QEMU |
| # |
| # UEFI binaries for QEMU used for testing this script: |
| # |
| # OVMF-pure-efi.i386.fd at |
| # https://drive.google.com/file/d/1jWzOAZfQqMmS2_dAK2G518GhIgj9r2RY/view?usp=sharing |
| |
| # OVMF-pure-efi.x64.fd at |
| # https://drive.google.com/file/d/1c39YI9QtpByGQ4V0UNNQtGqttEzS-eFV/view?usp=sharing |
| |
| bzimage_fname=/tmp/kernel/arch/x86/boot/bzImage |
| |
| set -e |
| |
| usage() { |
| echo "Usage: $0 [-a | -p] [other opts]" 1>&2 |
| echo 1>&2 |
| echo " -a - Package up the app" 1>&2 |
| echo " -k - Add a kernel" 1>&2 |
| echo " -o - Use old EFI app build (before 32/64 split)" 1>&2 |
| echo " -p - Package up the payload" 1>&2 |
| echo " -P - Create a partition table" 1>&2 |
| echo " -r - Run QEMU with the image" 1>&2 |
| echo " -s - Run QEMU with serial only (no display)" 1>&2 |
| echo " -w - Use word version (32-bit)" 1>&2 |
| exit 1 |
| } |
| |
| # 32- or 64-bit EFI |
| bitness=64 |
| |
| # app or payload ? |
| type=app |
| |
| # create a partition table and put the filesystem in that (otherwise put the |
| # filesystem in the raw device) |
| part= |
| |
| # run the image with QEMU |
| run= |
| |
| # run QEMU without a display (U-Boot must be set to stdout=serial) |
| serial= |
| |
| # before the 32/64 split of the app |
| old= |
| |
| # package up a kernel as well |
| kernel= |
| |
| # Set ubdir to the build directory where you build U-Boot out-of-tree |
| # We avoid in-tree build because it gets confusing trying different builds |
| ubdir=/tmp/b/ |
| |
| while getopts "akopPrsw" opt; do |
| case "${opt}" in |
| a) |
| type=app |
| ;; |
| p) |
| type=payload |
| ;; |
| k) |
| kernel=1 |
| ;; |
| r) |
| run=1 |
| ;; |
| s) |
| serial=1 |
| ;; |
| w) |
| bitness=32 |
| ;; |
| o) |
| old=1 |
| ;; |
| P) |
| part=1 |
| ;; |
| *) |
| usage |
| ;; |
| esac |
| done |
| |
| run_qemu() { |
| extra= |
| if [[ "${bitness}" = "64" ]]; then |
| qemu=qemu-system-x86_64 |
| bios=OVMF-pure-efi.x64.fd |
| else |
| qemu=qemu-system-i386 |
| bios=OVMF-pure-efi.i386.fd |
| fi |
| if [[ -n "${serial}" ]]; then |
| extra="-display none -serial mon:stdio" |
| else |
| extra="-serial mon:stdio" |
| fi |
| echo "Running ${qemu}" |
| # Use 512MB since U-Boot EFI likes to have 256MB to play with |
| "${qemu}" -bios "${bios}" \ |
| -m 512 \ |
| -drive id=disk,file="${IMG}",if=none,format=raw \ |
| -nic none -device ahci,id=ahci \ |
| -device ide-hd,drive=disk,bus=ahci.0 ${extra} |
| } |
| |
| setup_files() { |
| echo "Packaging ${BUILD}" |
| mkdir -p $TMP |
| cat >$TMP/startup.nsh <<EOF |
| fs0:u-boot-${type}.efi |
| EOF |
| sudo cp ${ubdir}/${BUILD}/u-boot-${type}.efi $TMP |
| |
| # Can copy in other files here: |
| #sudo cp ${ubdir}/$BUILD/image.bin $TMP/chromeos.rom |
| #sudo cp /boot/vmlinuz-5.4.0-77-generic $TMP/vmlinuz |
| } |
| |
| # Copy files into the filesystem |
| copy_files() { |
| sudo cp $TMP/* $MNT |
| if [[ -n "${kernel}" ]]; then |
| sudo cp ${bzimage_fname} $MNT/vmlinuz |
| fi |
| } |
| |
| # Create a filesystem on a raw device and copy in the files |
| setup_raw() { |
| mkfs.vfat "${IMG}" >/dev/null |
| sudo mount -o loop "${IMG}" $MNT |
| copy_files |
| sudo umount $MNT |
| } |
| |
| # Create a partition table and put the filesystem in the first partition |
| # then copy in the files |
| setup_part() { |
| # Create a gpt partition table with one partition |
| parted "${IMG}" mklabel gpt 2>/dev/null |
| |
| # This doesn't work correctly. It creates: |
| # Number Start End Size File system Name Flags |
| # 1 1049kB 24.1MB 23.1MB boot msftdata |
| # Odd if the same is entered interactively it does set the FS type |
| parted -s -a optimal -- "${IMG}" mkpart boot fat32 1MiB 23MiB |
| |
| # Map this partition to a loop device |
| kp="$(sudo kpartx -av ${IMG})" |
| read boot_dev<<<$(grep -o 'loop.*p.' <<< "${kp}") |
| test "${boot_dev}" |
| dev="/dev/mapper/${boot_dev}" |
| |
| mkfs.vfat "${dev}" >/dev/null |
| |
| sudo mount -o loop "${dev}" $MNT |
| |
| copy_files |
| |
| # Sync here since this makes kpartx more likely to work the first time |
| sync |
| sudo umount $MNT |
| |
| # For some reason this needs a sleep or it sometimes fails, if it was |
| # run recently (in the last few seconds) |
| if ! sudo kpartx -d "${IMG}" > /dev/null; then |
| sleep .5 |
| sudo kpartx -d "${IMG}" > /dev/null || \ |
| echo "Failed to remove ${boot_dev}, use: sudo kpartx -d ${IMG}" |
| fi |
| } |
| |
| TMP="/tmp/efi${bitness}${type}" |
| MNT=/mnt/x |
| BUILD="efi-x86_${type}${bitness}" |
| IMG=try.img |
| |
| if [[ -n "${old}" && "${bitness}" = "32" ]]; then |
| BUILD="efi-x86_${type}" |
| fi |
| |
| setup_files |
| |
| qemu-img create "${IMG}" 24M >/dev/null |
| |
| if [[ -n "${part}" ]]; then |
| setup_part |
| else |
| setup_raw |
| fi |
| |
| if [[ -n "${run}" ]]; then |
| run_qemu |
| fi |