| #!/bin/bash | |
| set -o errexit | |
| set -o pipefail | |
| set -o nounset | |
| # Script to generate kernelefi.img and shimefi.img. | |
| # | |
| # kernelefi.img uses a locally built kernel with the builtin_cmdline symbol | |
| # present and shimefi.img uses a shim from cos-125-19216-104-133. | |
| # | |
| # e.g.: | |
| # bash create_efi_gpt.sh kernelefi.img bzImage 2 /efi/boot/bootx64.efi 1500000 | |
| # bash create_efi_gpt.sh shimefi.img shimx64.efi 2 /efi/boot/bootx64.efi | |
| # bash create_efi_gpt.sh kernelefi_arm64.img vmlinuz.bin 2 /efi/boot/bootaa64.efi 1500000 | |
| if [[ $# -ne 5 ]]; then | |
| echo "Usage: $0 <output_image_path> <bootx64_efi_source> <priority_partition> <efi_internal_path> <extra_padding_in_bytes>" | |
| echo "Example efi_internal_path: /efi/boot/bootx64.efi" | |
| exit 1 | |
| fi | |
| readonly OUTPUT_IMAGE="$1" | |
| readonly BOOT_SOURCE="$2" | |
| readonly PRIORITY_PART="$3" | |
| readonly EFI_INTERNAL_PATH="$4" | |
| readonly EFI_PADDING="${5:-0}" | |
| readonly EFI_PART_IMAGE="efi_part.img" | |
| # 1. Calculate required size for the partition | |
| SOURCE_SIZE=$(stat -c%s "$BOOT_SOURCE") | |
| # Add overhead for FAT metadata (at least 512KB for small files, total min 1MB) | |
| EFI_SIZE_BYTES=$((SOURCE_SIZE + 512 * 1024 + EFI_PADDING)) | |
| if [[ $EFI_SIZE_BYTES -lt $((1024 * 1024)) ]]; then | |
| EFI_SIZE_BYTES=$((1024 * 1024)) | |
| fi | |
| # Align partition size to 512-byte sectors | |
| PART_SIZE_SECTORS=$(( (EFI_SIZE_BYTES + 511) / 512 )) | |
| # 2. Create the FAT image | |
| truncate -s "$((PART_SIZE_SECTORS * 512))" "$EFI_PART_IMAGE" | |
| mkfs.vfat "$EFI_PART_IMAGE" | |
| # 3. Create directory structure and copy the EFI file | |
| # Ensure leading ::/ for mtools | |
| INTERNAL_PATH_MTOOLS="::${EFI_INTERNAL_PATH#/}" | |
| DIR_PATH=$(dirname "${EFI_INTERNAL_PATH}") | |
| # Recursively create directories | |
| IFS='/' read -ra ADDR <<< "$DIR_PATH" | |
| CURRENT_DIR="::" | |
| for i in "${ADDR[@]}"; do | |
| if [[ -n "$i" ]]; then | |
| CURRENT_DIR="${CURRENT_DIR}/$i" | |
| # mmd might fail if directory exists, so we ignore error. | |
| mmd -i "$EFI_PART_IMAGE" "$CURRENT_DIR" || true | |
| fi | |
| done | |
| mcopy -i "$EFI_PART_IMAGE" "$BOOT_SOURCE" "$INTERNAL_PATH_MTOOLS" | |
| # 4. Define GPT layout | |
| # Start at sector 2048 (1MiB) for alignment compatibility and to avoid | |
| # "sector already used" errors with some versions of sfdisk. | |
| readonly START_SECTOR=2048 | |
| # Backup GPT is 33 sectors at the end. | |
| # sfdisk by default aligns partitions to 1MiB (2048 sectors). | |
| # We add enough sectors to account for alignment of partitions 2, 3, and 4. | |
| TOTAL_SECTORS=$(( START_SECTOR + PART_SIZE_SECTORS + 3 * 2048 + 33 )) | |
| # 5. Create the final container | |
| truncate -s "$((TOTAL_SECTORS * 512))" "$OUTPUT_IMAGE" | |
| # 6. Initialize GPT and create the partitions | |
| # Partition 1: EFI System Partition GUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B | |
| # Partition 2: ChromeOS Kernel GUID: FE3A2A5D-4F32-41A7-B725-ACCC3285A309 | |
| # Partition 3: ChromeOS Root GUID: 3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC | |
| # Partition 4: ChromeOS Kernel GUID: FE3A2A5D-4F32-41A7-B725-ACCC3285A309 | |
| echo "label: gpt | |
| start=${START_SECTOR}, size=${PART_SIZE_SECTORS}, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, name=\"EFI-SYSTEM\" | |
| size=1, type=FE3A2A5D-4F32-41A7-B725-ACCC3285A309, name=\"KERN-A\" | |
| size=1, type=3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC, name=\"ROOT-A\" | |
| size=1, type=FE3A2A5D-4F32-41A7-B725-ACCC3285A309, name=\"KERN-B\"" | sfdisk --no-reread "$OUTPUT_IMAGE" | |
| cgpt prioritize -i "$PRIORITY_PART" -P 2 "$OUTPUT_IMAGE" | |
| # 7. DD the EFI partition into the GPT image | |
| dd if="$EFI_PART_IMAGE" of="$OUTPUT_IMAGE" bs=512 seek="${START_SECTOR}" conv=notrunc status=none | |
| # Cleanup | |
| rm "$EFI_PART_IMAGE" | |
| echo "Successfully created minimal GPT image: $OUTPUT_IMAGE ($((TOTAL_SECTORS * 512 / 1024)) KiB)" |