xref: /openbmc/linux/arch/x86/boot/genimage.sh (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1 #!/bin/bash
2 #
3 # This file is subject to the terms and conditions of the GNU General Public
4 # License.  See the file "COPYING" in the main directory of this archive
5 # for more details.
6 #
7 # Copyright (C) 2017 by Changbin Du <changbin.du@intel.com>
8 #
9 # Adapted from code in arch/x86/boot/Makefile by H. Peter Anvin and others
10 #
11 # "make fdimage/fdimage144/fdimage288/hdimage/isoimage"
12 # script for x86 architecture
13 #
14 # Arguments:
15 #   $1  - fdimage format
16 #   $2  - target image file
17 #   $3  - kernel bzImage file
18 #   $4  - mtools configuration file
19 #   $5  - kernel cmdline
20 #   $6+ - initrd image file(s)
21 #
22 # This script requires:
23 #   bash
24 #   syslinux
25 #   mtools (for fdimage* and hdimage)
26 #   edk2/OVMF (for hdimage)
27 #
28 # Otherwise try to stick to POSIX shell commands...
29 #
30 
31 # Use "make V=1" to debug this script
32 case "${KBUILD_VERBOSE}" in
33 *1*)
34         set -x
35         ;;
36 esac
37 
38 # Exit the top-level shell with an error
39 topshell=$$
40 trap 'exit 1' USR1
41 die() {
42 	echo ""        1>&2
43 	echo " *** $*" 1>&2
44 	echo ""        1>&2
45 	kill -USR1 $topshell
46 }
47 
48 # Verify the existence and readability of a file
49 verify() {
50 	if [ ! -f "$1" -o ! -r "$1" ]; then
51 		die "Missing file: $1"
52 	fi
53 }
54 
55 diskfmt="$1"
56 FIMAGE="$2"
57 FBZIMAGE="$3"
58 MTOOLSRC="$4"
59 KCMDLINE="$5"
60 shift 5				# Remaining arguments = initrd files
61 
62 export MTOOLSRC
63 
64 # common options for dd
65 dd='dd iflag=fullblock'
66 
67 # Make sure the files actually exist
68 verify "$FBZIMAGE"
69 
70 declare -a FDINITRDS
71 irdpfx=' initrd='
72 initrdopts_syslinux=''
73 initrdopts_efi=''
74 for f in "$@"; do
75 	if [ -f "$f" -a -r "$f" ]; then
76 	    FDINITRDS=("${FDINITRDS[@]}" "$f")
77 	    fname="$(basename "$f")"
78 	    initrdopts_syslinux="${initrdopts_syslinux}${irdpfx}${fname}"
79 	    irdpfx=,
80 	    initrdopts_efi="${initrdopts_efi} initrd=${fname}"
81 	fi
82 done
83 
84 # Read a $3-byte littleendian unsigned value at offset $2 from file $1
85 le() {
86 	local n=0
87 	local m=1
88 	for b in $(od -A n -v -j $2 -N $3 -t u1 "$1"); do
89 		n=$((n + b*m))
90 		m=$((m * 256))
91 	done
92 	echo $n
93 }
94 
95 # Get the EFI architecture name such that boot{name}.efi is the default
96 # boot file name. Returns false with no output if the file is not an
97 # EFI image or otherwise unknown.
98 efiarch() {
99 	[ -f "$1" ] || return
100 	[ $(le "$1" 0 2) -eq 23117 ] || return		# MZ magic
101 	peoffs=$(le "$1" 60 4)				# PE header offset
102 	[ $peoffs -ge 64 ] || return
103 	[ $(le "$1" $peoffs 4) -eq 17744 ] || return	# PE magic
104 	case $(le "$1" $((peoffs+4+20)) 2) in		# PE type
105 		267)	;;				# PE32
106 		523)	;;				# PE32+
107 		*) return 1 ;;				# Invalid
108 	esac
109 	[ $(le "$1" $((peoffs+4+20+68)) 2) -eq 10 ] || return # EFI app
110 	case $(le "$1" $((peoffs+4)) 2) in		# Machine type
111 		 332)	echo i386	;;
112 		 450)	echo arm	;;
113 		 512)	echo ia64	;;
114 		20530)	echo riscv32	;;
115 		20580)	echo riscv64	;;
116 		20776)	echo riscv128	;;
117 		34404)	echo x64	;;
118 		43620)	echo aa64	;;
119 	esac
120 }
121 
122 # Get the combined sizes in bytes of the files given, counting sparse
123 # files as full length, and padding each file to cluster size
124 cluster=16384
125 filesizes() {
126 	local t=0
127 	local s
128 	for s in $(ls -lnL "$@" 2>/dev/null | awk '/^-/{ print $5; }'); do
129 		t=$((t + ((s+cluster-1)/cluster)*cluster))
130 	done
131 	echo $t
132 }
133 
134 # Expand directory names which should be in /usr/share into a list
135 # of possible alternatives
136 sharedirs() {
137 	local dir file
138 	for dir in /usr/share /usr/lib64 /usr/lib; do
139 		for file; do
140 			echo "$dir/$file"
141 			echo "$dir/${file^^}"
142 		done
143 	done
144 }
145 efidirs() {
146 	local dir file
147 	for dir in /usr/share /boot /usr/lib64 /usr/lib; do
148 		for file; do
149 			echo "$dir/$file"
150 			echo "$dir/${file^^}"
151 		done
152 	done
153 }
154 
155 findsyslinux() {
156 	local f="$(find -L $(sharedirs syslinux isolinux) \
157 		    -name "$1" -readable -type f -print -quit 2>/dev/null)"
158 	if [ ! -f "$f" ]; then
159 		die "Need a $1 file, please install syslinux/isolinux."
160 	fi
161 	echo "$f"
162 	return 0
163 }
164 
165 findovmf() {
166 	local arch="$1"
167 	shift
168 	local -a names=(-false)
169 	local name f
170 	for name; do
171 		names=("${names[@]}" -or -iname "$name")
172 	done
173 	for f in $(find -L $(efidirs edk2 ovmf) \
174 			\( "${names[@]}" \) -readable -type f \
175 			-print 2>/dev/null); do
176 		if [ "$(efiarch "$f")" = "$arch" ]; then
177 			echo "$f"
178 			return 0
179 		fi
180 	done
181 	die "Need a $1 file for $arch, please install EDK2/OVMF."
182 }
183 
184 do_mcopy() {
185 	if [ ${#FDINITRDS[@]} -gt 0 ]; then
186 		mcopy "${FDINITRDS[@]}" "$1"
187 	fi
188 	if [ -n "$efishell" ]; then
189 		mmd "$1"EFI "$1"EFI/Boot
190 		mcopy "$efishell" "$1"EFI/Boot/boot${kefiarch}.efi
191 	fi
192 	if [ -n "$kefiarch" ]; then
193 		echo linux "$KCMDLINE$initrdopts_efi" | \
194 			mcopy - "$1"startup.nsh
195 	fi
196 	echo default linux "$KCMDLINE$initrdopts_syslinux" | \
197 		mcopy - "$1"syslinux.cfg
198 	mcopy "$FBZIMAGE" "$1"linux
199 }
200 
201 genbzdisk() {
202 	verify "$MTOOLSRC"
203 	mformat -v 'LINUX_BOOT' a:
204 	syslinux "$FIMAGE"
205 	do_mcopy a:
206 }
207 
208 genfdimage144() {
209 	verify "$MTOOLSRC"
210 	$dd if=/dev/zero of="$FIMAGE" bs=1024 count=1440 2>/dev/null
211 	mformat -v 'LINUX_BOOT' v:
212 	syslinux "$FIMAGE"
213 	do_mcopy v:
214 }
215 
216 genfdimage288() {
217 	verify "$MTOOLSRC"
218 	$dd if=/dev/zero of="$FIMAGE" bs=1024 count=2880 2>/dev/null
219 	mformat -v 'LINUX_BOOT' w:
220 	syslinux "$FIMAGE"
221 	do_mcopy w:
222 }
223 
224 genhdimage() {
225 	verify "$MTOOLSRC"
226 	mbr="$(findsyslinux mbr.bin)"
227 	kefiarch="$(efiarch "$FBZIMAGE")"
228 	if [ -n "$kefiarch" ]; then
229 		# The efishell provides command line handling
230 		efishell="$(findovmf $kefiarch shell.efi shell${kefiarch}.efi)"
231 		ptype='-T 0xef'	# EFI system partition, no GPT
232 	fi
233 	sizes=$(filesizes "$FBZIMAGE" "${FDINITRDS[@]}" "$efishell")
234 	# Allow 1% + 2 MiB for filesystem and partition table overhead,
235 	# syslinux, and config files; this is probably excessive...
236 	megs=$(((sizes + sizes/100 + 2*1024*1024 - 1)/(1024*1024)))
237 	$dd if=/dev/zero of="$FIMAGE" bs=$((1024*1024)) count=$megs 2>/dev/null
238 	mpartition -I -c -s 32 -h 64 $ptype -b 64 -a p:
239 	$dd if="$mbr" of="$FIMAGE" bs=440 count=1 conv=notrunc 2>/dev/null
240 	mformat -v 'LINUX_BOOT' -s 32 -h 64 -c $((cluster/512)) -t $megs h:
241 	syslinux --offset $((64*512)) "$FIMAGE"
242 	do_mcopy h:
243 }
244 
245 geniso() {
246 	tmp_dir="$(dirname "$FIMAGE")/isoimage"
247 	rm -rf "$tmp_dir"
248 	mkdir "$tmp_dir"
249 	isolinux=$(findsyslinux isolinux.bin)
250 	ldlinux=$(findsyslinux  ldlinux.c32)
251 	cp "$isolinux" "$ldlinux" "$tmp_dir"
252 	cp "$FBZIMAGE" "$tmp_dir"/linux
253 	echo default linux "$KCMDLINE" > "$tmp_dir"/isolinux.cfg
254 	cp "${FDINITRDS[@]}" "$tmp_dir"/
255 	genisoimage -J -r -appid 'LINUX_BOOT' -input-charset=utf-8 \
256 		    -quiet -o "$FIMAGE" -b isolinux.bin \
257 		    -c boot.cat -no-emul-boot -boot-load-size 4 \
258 		    -boot-info-table "$tmp_dir"
259 	isohybrid "$FIMAGE" 2>/dev/null || true
260 	rm -rf "$tmp_dir"
261 }
262 
263 rm -f "$FIMAGE"
264 
265 case "$diskfmt" in
266 	bzdisk)     genbzdisk;;
267 	fdimage144) genfdimage144;;
268 	fdimage288) genfdimage288;;
269 	hdimage)    genhdimage;;
270 	isoimage)   geniso;;
271 	*)          die "Unknown image format: $diskfmt";;
272 esac
273