blob: 55f72b0f5422c5e2a1e40d6655a372a0fcf38afb [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/bin/sh
2#
3# Copyright (c) 2012, Intel Corporation.
4# All rights reserved.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
14# the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19#
20
21LANG=C
22
23# Set to 1 to enable additional output
24DEBUG=0
25OUT="/dev/null"
26
27#
28# Defaults
29#
30# 20 Mb for the boot partition
31BOOT_SIZE=20
32# 5% for swap
33SWAP_RATIO=5
34
35# Cleanup after die()
36cleanup() {
37 debug "Syncing and unmounting devices"
38 # Unmount anything we mounted
39 unmount $ROOTFS_MNT || error "Failed to unmount $ROOTFS_MNT"
40 unmount $BOOTFS_MNT || error "Failed to unmount $BOOTFS_MNT"
41 unmount $HDDIMG_ROOTFS_MNT || error "Failed to unmount $HDDIMG_ROOTFS_MNT"
42 unmount $HDDIMG_MNT || error "Failed to unmount $HDDIMG_MNT"
43
44 # Remove the TMPDIR
45 debug "Removing temporary files"
46 if [ -d "$TMPDIR" ]; then
47 rm -rf $TMPDIR || error "Failed to remove $TMPDIR"
48 fi
49}
50
51trap 'die "Signal Received, Aborting..."' HUP INT TERM
52
53# Logging routines
54WARNINGS=0
55ERRORS=0
56CLEAR="$(tput sgr0)"
57INFO="$(tput bold)"
58RED="$(tput setaf 1)$(tput bold)"
59GREEN="$(tput setaf 2)$(tput bold)"
60YELLOW="$(tput setaf 3)$(tput bold)"
61info() {
62 echo "${INFO}$1${CLEAR}"
63}
64error() {
65 ERRORS=$((ERRORS+1))
66 echo "${RED}$1${CLEAR}"
67}
68warn() {
69 WARNINGS=$((WARNINGS+1))
70 echo "${YELLOW}$1${CLEAR}"
71}
72success() {
73 echo "${GREEN}$1${CLEAR}"
74}
75die() {
76 error "$1"
77 cleanup
78 exit 1
79}
80debug() {
81 if [ $DEBUG -eq 1 ]; then
82 echo "$1"
83 fi
84}
85
86usage() {
87 echo "Usage: $(basename $0) [-v] DEVICE HDDIMG TARGET_DEVICE"
88 echo " -v: Verbose debug"
89 echo " DEVICE: The device to write the image to, e.g. /dev/sdh"
90 echo " HDDIMG: The hddimg file to generate the efi disk from"
91 echo " TARGET_DEVICE: The device the target will boot from, e.g. /dev/mmcblk0"
92}
93
94image_details() {
95 IMG=$1
96 info "Image details"
97 echo " image: $(stat --printf '%N\n' $IMG)"
98 echo " size: $(stat -L --printf '%s bytes\n' $IMG)"
99 echo " modified: $(stat -L --printf '%y\n' $IMG)"
100 echo " type: $(file -L -b $IMG)"
101 echo ""
102}
103
104device_details() {
105 DEV=$1
106 BLOCK_SIZE=512
107
108 info "Device details"
109 echo " device: $DEVICE"
110 if [ -f "/sys/class/block/$DEV/device/vendor" ]; then
111 echo " vendor: $(cat /sys/class/block/$DEV/device/vendor)"
112 else
113 echo " vendor: UNKOWN"
114 fi
115 if [ -f "/sys/class/block/$DEV/device/model" ]; then
116 echo " model: $(cat /sys/class/block/$DEV/device/model)"
117 else
118 echo " model: UNKNOWN"
119 fi
120 if [ -f "/sys/class/block/$DEV/size" ]; then
121 echo " size: $(($(cat /sys/class/block/$DEV/size) * $BLOCK_SIZE)) bytes"
122 else
123 echo " size: UNKNOWN"
124 fi
125 echo ""
126}
127
128unmount_device() {
129 grep -q $DEVICE /proc/mounts
130 if [ $? -eq 0 ]; then
131 warn "$DEVICE listed in /proc/mounts, attempting to unmount"
132 umount $DEVICE* 2>/dev/null
133 return $?
134 fi
135 return 0
136}
137
138unmount() {
139 if [ "$1" = "" ] ; then
140 return 0
141 fi
142 grep -q $1 /proc/mounts
143 if [ $? -eq 0 ]; then
144 debug "Unmounting $1"
145 umount $1
146 return $?
147 fi
148 return 0
149}
150
151#
152# Parse and validate arguments
153#
154if [ $# -lt 3 ] || [ $# -gt 4 ]; then
155 usage
156 exit 1
157fi
158
159if [ "$1" = "-v" ]; then
160 DEBUG=1
161 OUT="1"
162 shift
163fi
164
165DEVICE=$1
166HDDIMG=$2
167TARGET_DEVICE=$3
168
169LINK=$(readlink $DEVICE)
170if [ $? -eq 0 ]; then
171 DEVICE="$LINK"
172fi
173
174if [ ! -w "$DEVICE" ]; then
175 usage
176 if [ ! -e "${DEVICE}" ] ; then
177 die "Device $DEVICE cannot be found"
178 else
179 die "Device $DEVICE is not writable (need to run under sudo?)"
180 fi
181fi
182
183if [ ! -e "$HDDIMG" ]; then
184 usage
185 die "HDDIMG $HDDIMG does not exist"
186fi
187
188#
189# Ensure the hddimg is not mounted
190#
191unmount "$HDDIMG" || die "Failed to unmount $HDDIMG"
192
193#
194# Check if any $DEVICE partitions are mounted
195#
196unmount_device || die "Failed to unmount $DEVICE"
197
198#
199# Confirm device with user
200#
201image_details $HDDIMG
202device_details $(basename $DEVICE)
203echo -n "${INFO}Prepare EFI image on $DEVICE [y/N]?${CLEAR} "
204read RESPONSE
205if [ "$RESPONSE" != "y" ]; then
206 echo "Image creation aborted"
207 exit 0
208fi
209
210
211#
212# Prepare the temporary working space
213#
214TMPDIR=$(mktemp -d mkefidisk-XXX) || die "Failed to create temporary mounting directory."
215HDDIMG_MNT=$TMPDIR/hddimg
216HDDIMG_ROOTFS_MNT=$TMPDIR/hddimg_rootfs
217ROOTFS_MNT=$TMPDIR/rootfs
218BOOTFS_MNT=$TMPDIR/bootfs
219mkdir $HDDIMG_MNT || die "Failed to create $HDDIMG_MNT"
220mkdir $HDDIMG_ROOTFS_MNT || die "Failed to create $HDDIMG_ROOTFS_MNT"
221mkdir $ROOTFS_MNT || die "Failed to create $ROOTFS_MNT"
222mkdir $BOOTFS_MNT || die "Failed to create $BOOTFS_MNT"
223
224
225#
226# Partition $DEVICE
227#
228DEVICE_SIZE=$(parted -s $DEVICE unit mb print | grep ^Disk | cut -d" " -f 3 | sed -e "s/MB//")
229# If the device size is not reported there may not be a valid label
230if [ "$DEVICE_SIZE" = "" ] ; then
231 parted -s $DEVICE mklabel msdos || die "Failed to create MSDOS partition table"
232 DEVICE_SIZE=$(parted -s $DEVICE unit mb print | grep ^Disk | cut -d" " -f 3 | sed -e "s/MB//")
233fi
234SWAP_SIZE=$((DEVICE_SIZE*SWAP_RATIO/100))
235ROOTFS_SIZE=$((DEVICE_SIZE-BOOT_SIZE-SWAP_SIZE))
236ROOTFS_START=$((BOOT_SIZE))
237ROOTFS_END=$((ROOTFS_START+ROOTFS_SIZE))
238SWAP_START=$((ROOTFS_END))
239
240# MMC devices use a partition prefix character 'p'
241PART_PREFIX=""
242if [ ! "${DEVICE#/dev/mmcblk}" = "${DEVICE}" ] || [ ! "${DEVICE#/dev/loop}" = "${DEVICE}" ]; then
243 PART_PREFIX="p"
244fi
245BOOTFS=$DEVICE${PART_PREFIX}1
246ROOTFS=$DEVICE${PART_PREFIX}2
247SWAP=$DEVICE${PART_PREFIX}3
248
249TARGET_PART_PREFIX=""
250if [ ! "${TARGET_DEVICE#/dev/mmcblk}" = "${TARGET_DEVICE}" ]; then
251 TARGET_PART_PREFIX="p"
252fi
253TARGET_ROOTFS=$TARGET_DEVICE${TARGET_PART_PREFIX}2
254TARGET_SWAP=$TARGET_DEVICE${TARGET_PART_PREFIX}3
255
256echo ""
257info "Boot partition size: $BOOT_SIZE MB ($BOOTFS)"
258info "ROOTFS partition size: $ROOTFS_SIZE MB ($ROOTFS)"
259info "Swap partition size: $SWAP_SIZE MB ($SWAP)"
260echo ""
261
262# Use MSDOS by default as GPT cannot be reliably distributed in disk image form
263# as it requires the backup table to be on the last block of the device, which
264# of course varies from device to device.
265
266info "Partitioning installation media ($DEVICE)"
267
268debug "Deleting partition table on $DEVICE"
269dd if=/dev/zero of=$DEVICE bs=512 count=2 >$OUT 2>&1 || die "Failed to zero beginning of $DEVICE"
270
271debug "Creating new partition table (MSDOS) on $DEVICE"
272parted -s $DEVICE mklabel msdos >$OUT 2>&1 || die "Failed to create MSDOS partition table"
273
274debug "Creating boot partition on $BOOTFS"
275parted -s $DEVICE mkpart primary 0% $BOOT_SIZE >$OUT 2>&1 || die "Failed to create BOOT partition"
276
277debug "Enabling boot flag on $BOOTFS"
278parted -s $DEVICE set 1 boot on >$OUT 2>&1 || die "Failed to enable boot flag"
279
280debug "Creating ROOTFS partition on $ROOTFS"
281parted -s $DEVICE mkpart primary $ROOTFS_START $ROOTFS_END >$OUT 2>&1 || die "Failed to create ROOTFS partition"
282
283debug "Creating swap partition on $SWAP"
284parted -s $DEVICE mkpart primary $SWAP_START 100% >$OUT 2>&1 || die "Failed to create SWAP partition"
285
286if [ $DEBUG -eq 1 ]; then
287 parted -s $DEVICE print
288fi
289
290
291#
292# Check if any $DEVICE partitions are mounted after partitioning
293#
294unmount_device || die "Failed to unmount $DEVICE partitions"
295
296
297#
298# Format $DEVICE partitions
299#
300info "Formatting partitions"
301debug "Formatting $BOOTFS as vfat"
302if [ ! "${DEVICE#/dev/loop}" = "${DEVICE}" ]; then
303 mkfs.vfat -I $BOOTFS -n "EFI" >$OUT 2>&1 || die "Failed to format $BOOTFS"
304else
305 mkfs.vfat $BOOTFS -n "EFI" >$OUT 2>&1 || die "Failed to format $BOOTFS"
306fi
307
308debug "Formatting $ROOTFS as ext3"
309mkfs.ext3 -F $ROOTFS -L "ROOT" >$OUT 2>&1 || die "Failed to format $ROOTFS"
310
311debug "Formatting swap partition ($SWAP)"
312mkswap $SWAP >$OUT 2>&1 || die "Failed to prepare swap"
313
314
315#
316# Installing to $DEVICE
317#
318debug "Mounting images and device in preparation for installation"
319mount -o loop $HDDIMG $HDDIMG_MNT >$OUT 2>&1 || error "Failed to mount $HDDIMG"
320mount -o loop $HDDIMG_MNT/rootfs.img $HDDIMG_ROOTFS_MNT >$OUT 2>&1 || error "Failed to mount rootfs.img"
321mount $ROOTFS $ROOTFS_MNT >$OUT 2>&1 || error "Failed to mount $ROOTFS on $ROOTFS_MNT"
322mount $BOOTFS $BOOTFS_MNT >$OUT 2>&1 || error "Failed to mount $BOOTFS on $BOOTFS_MNT"
323
324info "Preparing boot partition"
325EFIDIR="$BOOTFS_MNT/EFI/BOOT"
326cp $HDDIMG_MNT/vmlinuz $BOOTFS_MNT >$OUT 2>&1 || error "Failed to copy vmlinuz"
327# Copy the efi loader and configs (booti*.efi and grub.cfg if it exists)
328cp -r $HDDIMG_MNT/EFI $BOOTFS_MNT >$OUT 2>&1 || error "Failed to copy EFI dir"
329# Silently ignore a missing gummiboot loader dir (we might just be a GRUB image)
330cp -r $HDDIMG_MNT/loader $BOOTFS_MNT >$OUT 2>&1
331
332# Update the boot loaders configurations for an installed image
333# Remove any existing root= kernel parameters and:
334# o Add a root= parameter with the target rootfs
335# o Specify ro so fsck can be run during boot
336# o Specify rootwait in case the target media is an asyncronous block device
337# such as MMC or USB disks
338# o Specify "quiet" to minimize boot time when using slow serial consoles
339
340# Look for a GRUB installation
341GRUB_CFG="$EFIDIR/grub.cfg"
342if [ -e "$GRUB_CFG" ]; then
343 info "Configuring GRUB"
344 # Delete the install entry
345 sed -i "/menuentry 'install'/,/^}/d" $GRUB_CFG
346 # Delete the initrd lines
347 sed -i "/initrd /d" $GRUB_CFG
348 # Delete any LABEL= strings
349 sed -i "s/ LABEL=[^ ]*/ /" $GRUB_CFG
350
351 sed -i "s@ root=[^ ]*@ @" $GRUB_CFG
352 sed -i "s@vmlinuz @vmlinuz root=$TARGET_ROOTFS ro rootwait quiet @" $GRUB_CFG
353fi
354
355# Look for a gummiboot installation
356GUMMI_ENTRIES="$BOOTFS_MNT/loader/entries"
357GUMMI_CFG="$GUMMI_ENTRIES/boot.conf"
358if [ -d "$GUMMI_ENTRIES" ]; then
359 info "Configuring Gummiboot"
360 # remove the install target if it exists
361 rm $GUMMI_ENTRIES/install.conf >$OUT 2>&1
362
363 if [ ! -e "$GUMMI_CFG" ]; then
364 echo "ERROR: $GUMMI_CFG not found"
365 fi
366
367 sed -i "/initrd /d" $GUMMI_CFG
368 sed -i "s@ root=[^ ]*@ @" $GUMMI_CFG
369 sed -i "s@options *LABEL=boot @options LABEL=Boot root=$TARGET_ROOTFS ro rootwait quiet @" $GUMMI_CFG
370fi
371
372# Ensure we have at least one EFI bootloader configured
373if [ ! -e $GRUB_CFG ] && [ ! -e $GUMMI_CFG ]; then
374 die "No EFI bootloader configuration found"
375fi
376
377
378info "Copying ROOTFS files (this may take a while)"
379cp -a $HDDIMG_ROOTFS_MNT/* $ROOTFS_MNT >$OUT 2>&1 || die "Root FS copy failed"
380
381echo "$TARGET_SWAP swap swap defaults 0 0" >> $ROOTFS_MNT/etc/fstab
382
383# We dont want udev to mount our root device while we're booting...
384if [ -d $ROOTFS_MNT/etc/udev/ ] ; then
385 echo "$TARGET_DEVICE" >> $ROOTFS_MNT/etc/udev/mount.blacklist
386fi
387
388
389# Call cleanup to unmount devices and images and remove the TMPDIR
390cleanup
391
392echo ""
393if [ $WARNINGS -ne 0 ] && [ $ERRORS -eq 0 ]; then
394 echo "${YELLOW}Installation completed with warnings${CLEAR}"
395 echo "${YELLOW}Warnings: $WARNINGS${CLEAR}"
396elif [ $ERRORS -ne 0 ]; then
397 echo "${RED}Installation encountered errors${CLEAR}"
398 echo "${RED}Errors: $ERRORS${CLEAR}"
399 echo "${YELLOW}Warnings: $WARNINGS${CLEAR}"
400else
401 success "Installation completed successfully"
402fi
403echo ""