#!/usr/bin/env bash # # Copyright 2023 RnD Center "ELVEES", JSC # # SPDX-License-Identifier: GPLv3 # # https://en.wikipedia.org/wiki/EFI_system_partition#Overview declare -r ESP_GUID="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" help() { echo 'tar2dev-uefi - A tool to create an UEFI bootable system on block device' echo echo 'It creates GUID Partition Table (GPT) on block device and two specific partitions.' echo 'The first is EFI System Partition (ESP), the second one for the rootfs.' echo echo 'Copies the specified EFI directory which may have the OS loader or the UEFI fallback' echo 'files (bootaa64.efi for ARM64) to the ESP, unpacks the rootfs tarball to the second' echo 'partition.' echo echo 'Specifically for GRUB2 this tool creates the EFI/BOOT/grub.cfg file so GRUB2 can find the' echo 'right partition from which it can load the kernel by searching for' echo '/boot/.findme file which will be created using this tool. This file' echo 'is created by tar2dev-uefi and needed to find the right root from GRUB2 perspective' echo 'e.g. (hd0,gpt2).' echo echo 'Usage: tar2dev-uefi [option] ' echo echo 'Options:' echo ' -h Print this help' echo ' -s The size of ESP in MiB (300 if not set)' echo echo 'Example 0:' echo ' tar2dev-uefi rootfs.tar.gz efi-part/EFI /dev/sdb' echo echo 'Example 1:' echo ' tar2dev-uefi -s 600 rootfs.tar.gz efi-part/EFI /dev/sdb' exit 1 } set -e source tar2dev while getopts "hs:" opt; do case ${opt} in h) help;; s) [[ $OPTARG =~ ^[0-9]+$ ]] || \ error "-s option requires a integer type argument" ARG_ESP_SIZE=$OPTARG ;; *) help;; esac done shift $((OPTIND-1)) [[ "$EUID" != "0" ]] && error "This script must be run as root" [[ -z "$1" || -z "$2" || -z "$3" ]] && help TAR=$1 EFI=$2 DEV=$3 ESP_SIZE=${ARG_ESP_SIZE:-300} iswholedisk "$DEV" || error "'$DEV' is not a disk" [[ -w "$DEV" ]] || error "'$DEV' is not writable" [[ -e "$DEV" ]] || error "file $DEV dosn't exists" [[ -d "$EFI" ]] || error "$EFI dosn't exists or isn't a directory" [[ -e "$TAR" || -r "$TAR" ]] || error "file '$TAR' dosn't exists or isn't readable" # Conditions are satisfied, ready to do the work. unmountall "$DEV" ESP_DIR=$(mktemp -d) ROOTFS_DIR=$(mktemp -d) trap 'echo "Clean up..."; unmountall "$DEV"; rm -rf "$ROOTFS_DIR" "$ESP_DIR"' EXIT # Create UEFI bootable system echo "Creating GPT ..." hideifok "echo 'label: gpt' | sfdisk $DEV" ESP_DEVICE="$DEV"1 ROOTFS_DEVICE="$DEV"2 echo "Creating ESP on $ESP_DEVICE and rootfs on $ROOTFS_DEVICE ..." hideifok "echo -e \",${ESP_SIZE}M,$ESP_GUID,*\n,,,\" | sfdisk -f $DEV" echo "Formating both partitions ..." hideifok "mkfs.vfat $ESP_DEVICE" hideifok "mkfs.ext4 -F -L root $ROOTFS_DEVICE" # Copy rootfs to the device and generate `findme` file to be searched by GRUB2 ROOTFS_PART_UUID=$(sfdisk --part-uuid "$DEV" 2) mount "$ROOTFS_DEVICE" "$ROOTFS_DIR" tar -C "$ROOTFS_DIR" -xf "$TAR" touch "$ROOTFS_DIR/boot/$ROOTFS_PART_UUID.findme" # Copy efi-part/EFI directory and generate grub.cfg DTB_PATH=$(find "$ROOTFS_DIR" -name 'mcom03*.dtb' | head --lines 1 | xargs dirname) mount "$ESP_DEVICE" "$ESP_DIR" cp -r "$EFI" "$ESP_DIR" cp -r "$DTB_PATH" "$ESP_DIR/dtb" # U-Boot expects DTB in this directory if [[ -f "$ROOTFS_DIR/boot/Image" ]]; then IMAGE_NAME=Image elif [[ -f "$ROOTFS_DIR/boot/vmlinuz" ]]; then IMAGE_NAME=vmlinuz else error "Couldn't find the proper kernel image name in /boot directory of" \ "the rootfs, expected /boot/Image or /boot/vmlinuz files" fi cat > "$ESP_DIR"/EFI/BOOT/grub.cfg << EOF set default="0" set timeout="2" menuentry 'Default' { #set debug=linux #set efi_linux_debug="efi=debug" search --file --set=root /boot/$ROOTFS_PART_UUID.findme if [ "\$?" = 0 ]; then echo "found rootfs on \$root" linux /boot/$IMAGE_NAME root=PARTUUID=${ROOTFS_PART_UUID,,*} console=ttyS0,115200n8 \ earlycon roottype=ext4 rw rootwait \$efi_linux_debug if [ -f /boot/initrd.img ]; then initrd /boot/initrd.img fi fi } EOF