efi: Add a script to build an image for testing on UEFI

It is quite complicated to run U-Boot on QEMU since we have four
different builds and they must use different versions of qemu and the
UEFI binaries.

Add a script to help. It requires U-Boot itself to be built. Once that
is done you can use this script to build an image for use with qemu and
optionally run it.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 9d8cba9..fa7fe90 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -710,6 +710,7 @@
 S:	Maintained
 W:	https://u-boot.readthedocs.io/en/latest/develop/uefi/u-boot_on_efi.html
 F:	lib/efi/efi_app.c
+F:	scripts/build-efi.sh
 
 EFI PAYLOAD
 M:	Heinrich Schuchardt <xypron.glpk@gmx.de>
diff --git a/doc/develop/uefi/u-boot_on_efi.rst b/doc/develop/uefi/u-boot_on_efi.rst
index 43afb11..8856af3 100644
--- a/doc/develop/uefi/u-boot_on_efi.rst
+++ b/doc/develop/uefi/u-boot_on_efi.rst
@@ -98,6 +98,11 @@
 EFI (or vice versa). Also it will often fail to print an error message if
 you get this wrong.
 
+You may find the script `scripts/build-efi.sh` helpful for building and testing
+U-Boot on UEFI on QEMU. It also includes links to UEFI binaries dating from
+2021.
+
+See `Example run`_ for an example run.
 
 Inner workings
 --------------
@@ -193,6 +198,63 @@
 Everything else is built as a normal U-Boot, so is always 32-bit on x86 at
 present.
 
+Example run
+-----------
+
+This shows running with serial enabled (see `include/configs/efi-x86_app.h`)::
+
+   $ scripts/build-efi.sh -wsPr
+   Packaging efi-x86_app32
+   Running qemu-system-i386
+
+   BdsDxe: failed to load Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x3,0x0)/Sata(0x0,0xFFFF,0x0): Not Found
+   BdsDxe: loading Boot0002 "EFI Internal Shell" from Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(7C04A583-9E3E-4F1C-AD65-E05268D0B4D1)
+   BdsDxe: starting Boot0002 "EFI Internal Shell" from Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(7C04A583-9E3E-4F1C-AD65-E05268D0B4D1)
+
+   UEFI Interactive Shell v2.2
+   EDK II
+   UEFI v2.70 (EDK II, 0x00010000)
+   Mapping table
+         FS0: Alias(s):HD0a65535a1:;BLK1:
+             PciRoot(0x0)/Pci(0x3,0x0)/Sata(0x0,0xFFFF,0x0)/HD(1,GPT,0FFD5E61-3B0C-4326-8049-BDCDC910AF72,0x800,0xB000)
+        BLK0: Alias(s):
+             PciRoot(0x0)/Pci(0x3,0x0)/Sata(0x0,0xFFFF,0x0)
+
+   Press ESC in 5 seconds to skip startup.nsh or any other key to continue.
+   Shell> fs0:u-boot-app.efi
+   U-Boot EFI App (using allocated RAM address 47d4000) key=8d4, image=06a6f610
+   starting
+
+
+   U-Boot 2022.01-rc4 (Sep 19 2021 - 14:03:20 -0600)
+
+   CPU: x86, vendor Intel, device 663h
+   DRAM:  32 MiB
+    0: efi_media_0  PciRoot(0x0)/Pci(0x3,0x0)/Sata(0x0,0xFFFF,0x0)
+    1: <partition>  PciRoot(0x0)/Pci(0x3,0x0)/Sata(0x0,0xFFFF,0x0)/HD(1,GPT,0FFD5E61-3B0C-4326-8049-BDCDC910AF72,0x800,0xB000)
+   Loading Environment from nowhere... OK
+   Model: EFI x86 Application
+   Hit any key to stop autoboot:  0
+
+   Partition Map for EFI device 0  --   Partition Type: EFI
+
+   Part    Start LBA       End LBA            Name
+           Attributes
+           Type GUID
+           Partition GUID
+     1     0x00000800      0x0000b7ff      "boot"
+           attrs:  0x0000000000000000
+           type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
+           guid:   0ffd5e61-3b0c-4326-8049-bdcdc910af72
+          19   startup.nsh
+      528384   u-boot-app.efi
+       10181   NvVars
+
+   3 file(s), 0 dir(s)
+
+   => QEMU: Terminated
+
+
 Future work
 -----------
 This work could be extended in a number of ways:
diff --git a/scripts/build-efi.sh b/scripts/build-efi.sh
new file mode 100755
index 0000000..bc9aeeb
--- /dev/null
+++ b/scripts/build-efi.sh
@@ -0,0 +1,193 @@
+#!/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
+
+set -e
+
+usage() {
+	echo "Usage: $0 [-a | -p] [other opts]" 1>&2
+	echo 1>&2
+	echo "   -a   - Package up the app" 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=
+
+# 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 "aopPrsw" opt; do
+	case "${opt}" in
+	a)
+		type=app
+		;;
+	p)
+		type=payload
+		;;
+	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"
+	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
+}
+
+# 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