1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0+
3#
4# Script to build an EFI thing suitable for booting with QEMU, possibly running
5# it also.
6
7# This just an example. It assumes that
8
9# - you build U-Boot in ${ubdir}/<name> where <name> is the U-Boot board config
10# - /mnt/x is a directory used for mounting
11# - you have access to the 'pure UEFI' builds for QEMU
12#
13# UEFI binaries for QEMU used for testing this script:
14#
15# OVMF-pure-efi.i386.fd at
16# https://drive.google.com/file/d/1jWzOAZfQqMmS2_dAK2G518GhIgj9r2RY/view?usp=sharing
17
18# OVMF-pure-efi.x64.fd at
19# https://drive.google.com/file/d/1c39YI9QtpByGQ4V0UNNQtGqttEzS-eFV/view?usp=sharing
20
21bzimage_fname=/tmp/kernel/arch/x86/boot/bzImage
22
23set -e
24
25usage() {
26	echo "Usage: $0 [-a | -p] [other opts]" 1>&2
27	echo 1>&2
28	echo "   -a   - Package up the app" 1>&2
29	echo "   -k   - Add a kernel" 1>&2
30	echo "   -o   - Use old EFI app build (before 32/64 split)" 1>&2
31	echo "   -p   - Package up the payload" 1>&2
32	echo "   -P   - Create a partition table" 1>&2
33	echo "   -r   - Run QEMU with the image" 1>&2
34	echo "   -s   - Run QEMU with serial only (no display)" 1>&2
35	echo "   -w   - Use word version (32-bit)" 1>&2
36	exit 1
37}
38
39# 32- or 64-bit EFI
40bitness=64
41
42# app or payload ?
43type=app
44
45# create a partition table and put the filesystem in that (otherwise put the
46# filesystem in the raw device)
47part=
48
49# run the image with QEMU
50run=
51
52# run QEMU without a display (U-Boot must be set to stdout=serial)
53serial=
54
55# before the 32/64 split of the app
56old=
57
58# package up a kernel as well
59kernel=
60
61# Set ubdir to the build directory where you build U-Boot out-of-tree
62# We avoid in-tree build because it gets confusing trying different builds
63ubdir=/tmp/b/
64
65while getopts "akopPrsw" opt; do
66	case "${opt}" in
67	a)
68		type=app
69		;;
70	p)
71		type=payload
72		;;
73	k)
74		kernel=1
75		;;
76	r)
77		run=1
78		;;
79	s)
80		serial=1
81		;;
82	w)
83		bitness=32
84		;;
85	o)
86		old=1
87		;;
88	P)
89		part=1
90		;;
91	*)
92		usage
93		;;
94	esac
95done
96
97run_qemu() {
98	extra=
99	if [[ "${bitness}" = "64" ]]; then
100		qemu=qemu-system-x86_64
101		bios=OVMF-pure-efi.x64.fd
102	else
103		qemu=qemu-system-i386
104		bios=OVMF-pure-efi.i386.fd
105	fi
106	if [[ -n "${serial}" ]]; then
107		extra="-display none -serial mon:stdio"
108	else
109		extra="-serial mon:stdio"
110	fi
111	echo "Running ${qemu}"
112	# Use 512MB since U-Boot EFI likes to have 256MB to play with
113	"${qemu}" -bios "${bios}" \
114		-m 512 \
115		-drive id=disk,file="${IMG}",if=none,format=raw \
116		-nic none -device ahci,id=ahci \
117		-device ide-hd,drive=disk,bus=ahci.0 ${extra}
118}
119
120setup_files() {
121	echo "Packaging ${BUILD}"
122	mkdir -p $TMP
123	cat >$TMP/startup.nsh <<EOF
124fs0:u-boot-${type}.efi
125EOF
126	sudo cp ${ubdir}/${BUILD}/u-boot-${type}.efi $TMP
127
128	# Can copy in other files here:
129	#sudo cp ${ubdir}/$BUILD/image.bin $TMP/chromeos.rom
130	#sudo cp /boot/vmlinuz-5.4.0-77-generic $TMP/vmlinuz
131}
132
133# Copy files into the filesystem
134copy_files() {
135	sudo cp $TMP/* $MNT
136	if [[ -n "${kernel}" ]]; then
137		sudo cp ${bzimage_fname} $MNT/vmlinuz
138	fi
139}
140
141# Create a filesystem on a raw device and copy in the files
142setup_raw() {
143	mkfs.vfat "${IMG}" >/dev/null
144	sudo mount -o loop "${IMG}" $MNT
145	copy_files
146	sudo umount $MNT
147}
148
149# Create a partition table and put the filesystem in the first partition
150# then copy in the files
151setup_part() {
152	# Create a gpt partition table with one partition
153	parted "${IMG}" mklabel gpt 2>/dev/null
154
155	# This doesn't work correctly. It creates:
156	# Number  Start   End     Size    File system  Name  Flags
157	#  1      1049kB  24.1MB  23.1MB               boot  msftdata
158	# Odd if the same is entered interactively it does set the FS type
159	parted -s -a optimal -- "${IMG}" mkpart boot fat32 1MiB 23MiB
160
161	# Map this partition to a loop device
162	kp="$(sudo kpartx -av ${IMG})"
163	read boot_dev<<<$(grep -o 'loop.*p.' <<< "${kp}")
164	test "${boot_dev}"
165	dev="/dev/mapper/${boot_dev}"
166
167	mkfs.vfat "${dev}" >/dev/null
168
169	sudo mount -o loop "${dev}" $MNT
170
171	copy_files
172
173	# Sync here since this makes kpartx more likely to work the first time
174	sync
175	sudo umount $MNT
176
177	# For some reason this needs a sleep or it sometimes fails, if it was
178	# run recently (in the last few seconds)
179	if ! sudo kpartx -d "${IMG}" > /dev/null; then
180		sleep .5
181		sudo kpartx -d "${IMG}" > /dev/null || \
182			echo "Failed to remove ${boot_dev}, use: sudo kpartx -d ${IMG}"
183	fi
184}
185
186TMP="/tmp/efi${bitness}${type}"
187MNT=/mnt/x
188BUILD="efi-x86_${type}${bitness}"
189IMG=try.img
190
191if [[ -n "${old}" && "${bitness}" = "32" ]]; then
192	BUILD="efi-x86_${type}"
193fi
194
195setup_files
196
197qemu-img create "${IMG}" 24M >/dev/null
198
199if [[ -n "${part}" ]]; then
200	setup_part
201else
202	setup_raw
203fi
204
205if [[ -n "${run}" ]]; then
206	run_qemu
207fi
208