xref: /linux/arch/x86/boot/genimage.sh (revision 6beeaf48db6c548fcfc2ad32739d33af2fef3a5b)
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
32case "${KBUILD_VERBOSE}" in
33*1*)
34        set -x
35        ;;
36esac
37
38# Exit the top-level shell with an error
39topshell=$$
40trap 'exit 1' USR1
41die() {
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
49verify() {
50	if [ ! -f "$1" -o ! -r "$1" ]; then
51		die "Missing file: $1"
52	fi
53}
54
55diskfmt="$1"
56FIMAGE="$2"
57FBZIMAGE="$3"
58MTOOLSRC="$4"
59KCMDLINE="$5"
60shift 5				# Remaining arguments = initrd files
61
62export MTOOLSRC
63
64# common options for dd
65dd='dd iflag=fullblock'
66
67# Make sure the files actually exist
68verify "$FBZIMAGE"
69
70declare -a FDINITRDS
71irdpfx=' initrd='
72initrdopts_syslinux=''
73initrdopts_efi=''
74for 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
82done
83
84# Read a $3-byte littleendian unsigned value at offset $2 from file $1
85le() {
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.
98efiarch() {
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 a 4K block size
124filesizes() {
125	local t=0
126	local s
127	for s in $(ls -lnL "$@" 2>/dev/null | awk '/^-/{ print $5; }'); do
128		t=$((t + ((s+4095)/4096)*4096))
129	done
130	echo $t
131}
132
133# Expand directory names which should be in /usr/share into a list
134# of possible alternatives
135sharedirs() {
136	local dir file
137	for dir in /usr/share /usr/lib64 /usr/lib; do
138		for file; do
139			echo "$dir/$file"
140			echo "$dir/${file^^}"
141		done
142	done
143}
144efidirs() {
145	local dir file
146	for dir in /usr/share /boot /usr/lib64 /usr/lib; do
147		for file; do
148			echo "$dir/$file"
149			echo "$dir/${file^^}"
150		done
151	done
152}
153
154findsyslinux() {
155	local f="$(find -L $(sharedirs syslinux isolinux) \
156		    -name "$1" -readable -type f -print -quit 2>/dev/null)"
157	if [ ! -f "$f" ]; then
158		die "Need a $1 file, please install syslinux/isolinux."
159	fi
160	echo "$f"
161	return 0
162}
163
164findovmf() {
165	local arch="$1"
166	shift
167	local -a names=(-false)
168	local name f
169	for name; do
170		names=("${names[@]}" -or -iname "$name")
171	done
172	for f in $(find -L $(efidirs edk2 ovmf) \
173			\( "${names[@]}" \) -readable -type f \
174			-print 2>/dev/null); do
175		if [ "$(efiarch "$f")" = "$arch" ]; then
176			echo "$f"
177			return 0
178		fi
179	done
180	die "Need a $1 file for $arch, please install EDK2/OVMF."
181}
182
183do_mcopy() {
184	if [ ${#FDINITRDS[@]} -gt 0 ]; then
185		mcopy "${FDINITRDS[@]}" "$1"
186	fi
187	if [ -n "$efishell" ]; then
188		mmd "$1"EFI "$1"EFI/Boot
189		mcopy "$efishell" "$1"EFI/Boot/boot${kefiarch}.efi
190	fi
191	if [ -n "$kefiarch" ]; then
192		echo linux "$KCMDLINE$initrdopts_efi" | \
193			mcopy - "$1"startup.nsh
194	fi
195	echo default linux "$KCMDLINE$initrdopts_syslinux" | \
196		mcopy - "$1"syslinux.cfg
197	mcopy "$FBZIMAGE" "$1"linux
198}
199
200genbzdisk() {
201	verify "$MTOOLSRC"
202	mformat -v 'LINUX_BOOT' a:
203	syslinux "$FIMAGE"
204	do_mcopy a:
205}
206
207genfdimage144() {
208	verify "$MTOOLSRC"
209	$dd if=/dev/zero of="$FIMAGE" bs=1024 count=1440 2>/dev/null
210	mformat -v 'LINUX_BOOT' v:
211	syslinux "$FIMAGE"
212	do_mcopy v:
213}
214
215genfdimage288() {
216	verify "$MTOOLSRC"
217	$dd if=/dev/zero of="$FIMAGE" bs=1024 count=2880 2>/dev/null
218	mformat -v 'LINUX_BOOT' w:
219	syslinux "$FIMAGE"
220	do_mcopy w:
221}
222
223genhdimage() {
224	verify "$MTOOLSRC"
225	mbr="$(findsyslinux mbr.bin)"
226	kefiarch="$(efiarch "$FBZIMAGE")"
227	if [ -n "$kefiarch" ]; then
228		# The efishell provides command line handling
229		efishell="$(findovmf $kefiarch shell.efi shell${kefiarch}.efi)"
230		ptype='-T 0xef'	# EFI system partition, no GPT
231	fi
232	sizes=$(filesizes "$FBZIMAGE" "${FDINITRDS[@]}" "$efishell")
233	# Allow 1% + 1 MiB for filesystem and partition table overhead,
234	# syslinux, and config files
235	megs=$(((sizes + sizes/100 + 2*1024*1024 - 1)/(1024*1024)))
236	$dd if=/dev/zero of="$FIMAGE" bs=$((1024*1024)) count=$megs 2>/dev/null
237	mpartition -I -c -s 32 -h 64 -t $megs $ptype -b 512 -a h:
238	$dd if="$mbr" of="$FIMAGE" bs=440 count=1 conv=notrunc 2>/dev/null
239	mformat -v 'LINUX_BOOT' -s 32 -h 64 -t $megs h:
240	syslinux --offset $((512*512)) "$FIMAGE"
241	do_mcopy h:
242}
243
244geniso() {
245	tmp_dir="$(dirname "$FIMAGE")/isoimage"
246	rm -rf "$tmp_dir"
247	mkdir "$tmp_dir"
248	isolinux=$(findsyslinux isolinux.bin)
249	ldlinux=$(findsyslinux  ldlinux.c32)
250	cp "$isolinux" "$ldlinux" "$tmp_dir"
251	cp "$FBZIMAGE" "$tmp_dir"/linux
252	echo default linux "$KCMDLINE" > "$tmp_dir"/isolinux.cfg
253	cp "${FDINITRDS[@]}" "$tmp_dir"/
254	genisoimage -J -r -appid 'LINUX_BOOT' -input-charset=utf-8 \
255		    -quiet -o "$FIMAGE" -b isolinux.bin \
256		    -c boot.cat -no-emul-boot -boot-load-size 4 \
257		    -boot-info-table "$tmp_dir"
258	isohybrid "$FIMAGE" 2>/dev/null || true
259	rm -rf "$tmp_dir"
260}
261
262rm -f "$FIMAGE"
263
264case "$diskfmt" in
265	bzdisk)     genbzdisk;;
266	fdimage144) genfdimage144;;
267	fdimage288) genfdimage288;;
268	hdimage)    genhdimage;;
269	isoimage)   geniso;;
270	*)          die "Unknown image format: $diskfmt";;
271esac
272