From 4f989bf93a6b8c1a6ce0f38adf47469cd58493e0 Mon Sep 17 00:00:00 2001 From: Andrew Robbins Date: Tue, 25 Dec 2018 21:23:22 -0500 Subject: Refactor SeaGRUB floppy creation code Related issue #553 Previously, the GRUB boot image and core image were concatenated together and padding added to create a (pseudo) floppy image. However, testing revealed that GRUB has issues dynamically loading modules from $prefix if $prefix is not located on the same device from which GRUB booted. In order to get around this issue, a proper 2.88MiB floppy image is created using mkfs.fat with GRUB modules copied to directories on the filesystem (i.e, /boot/grub/i386-pc). The 2.88MiB filesystem size is necessary to be able to fit all modules onto the image. New functions added to grub-helper: * grub_floppy_image_mmd (create directories on image using mtools mmd) * grub_floppy_image_mcopy (copy files to image using mtools mcopy) --- projects/grub/grub-helper | 72 +++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 28 deletions(-) (limited to 'projects/grub/grub-helper') diff --git a/projects/grub/grub-helper b/projects/grub/grub-helper index 596f7d96..04814575 100755 --- a/projects/grub/grub-helper +++ b/projects/grub/grub-helper @@ -135,25 +135,31 @@ grub_build_bootable_image() { local grub_mkimage="$sources_path/grub-mkimage" local grub_module_dir="$sources_path/grub-core" - local grubimg="$build_path/grub.img" local grub_bootimg="$grub_module_dir/boot.img" - local grub_bootable_img="$build_path/grub2" + local grub_coreimg="$build_path/core.img" "$grub_mkimage" \ --config="$config_path" \ --directory="$grub_module_dir" \ - --output="$grubimg" \ + --output="$grub_coreimg" \ --format="$format" \ --prefix="$prefix" \ "${modmin[@]}" - cat "$grub_bootimg" "$grubimg" > "$grub_bootable_img" - rm -f "$grubimg" + cp -a "$grub_bootimg" "$build_path" } grub_build_floppy_image() { - local grubimg="$build_path/grub2" - local tempfile="$build_path/temp.file" + local floppyimg="$build_path/floppy.img" + local format="$(grub_format "$target" "$@")" + local grub_module_dir="$sources_path/grub-core" + local size="$(grub_size "$target" "$@")" + + local -a modules + + for module in "$grub_module_dir"/*.mod; do + modules+=($module) + done if ! grub_build_bootable_image "$@"; then printf '\n%s\n\n' "Error: Failed to build a GRUB image" 1>&2 @@ -161,33 +167,18 @@ grub_build_floppy_image() { return 1 fi - local size="$(grub_size "$target" "$@")" - - # Pre-allocate a floppy-sized image + # Pre-allocate a floppy-sized image with a FAT12 filesystem # SeaBIOS requires floppy images to have a "correct" size - if ! [[ -e "$tempfile" ]]; then - dd if=/dev/zero of="$tempfile" bs=1024 count="${size:-160}" + if ! [[ -e "$floppyimg" ]]; then + mkfs.fat -C -D 0x00 -F 12 -M 0xF9 -n SEAGRUB --invariant "$floppyimg" "$size" else - printf '\n%s\n\n' "Error: File $tempfile already exists!" 1>&2 + printf '\n%s\n\n' "Error: File $floppyimg already exists!" 1>&2 return 1 fi - local -i grubimg_size="$(stat -c %s "$grubimg")" - local -i floppy_size="$((${size:-160} * 1024))" - - # Graft the GRUB image onto the blank floppy image - if ((grubimg_size <= floppy_size)); then - dd if="$grubimg" of="$tempfile" bs=1 conv=notrunc - - rm -f "$grubimg" - mv "$tempfile" "$grubimg" - else - printf '\n%s' "Error: Image ${grubimg##*/} is too large; " 1>&2 - printf '%s\n\n' "it must be less than ${floppy_size}KiB in size" 1>&2 - - return 1 - fi + grub_floppy_image_mmd "$floppyimg" /boot /boot/grub "/boot/grub/$format" + grub_floppy_image_mcopy "$floppyimg" "/boot/grub/$format" "${modules[@]}" } grub_build_standalone_image() { @@ -216,3 +207,28 @@ grub_build_standalone_image() { --output="$grubimg" \ /boot/grub/grub.cfg="$config_path" } + +grub_floppy_image_mmd() { + local img="$1" + local -a dirs=("${@:2}") + + if [[ -n "$img" ]]; then + mmd -i "$img" "${dirs[@]}" + else + return 1 + fi +} + +grub_floppy_image_mcopy() { + local img="$1" + local target="$2" + local -a files=("${@:3}") + + if [[ -z "$img" ]]; then + return 1 + elif [[ -z "${files[@]}" ]]; then + mcopy -i "$img" -pv "::$target" + else + mcopy -i "$img" -pQv "${files[@]}" "::$target" + fi +} -- cgit v1.2.3-70-g09d2 From c9e5a6b91406fe0e1b8188c2818c5e2f7ad3e267 Mon Sep 17 00:00:00 2001 From: Andrew Robbins Date: Tue, 25 Dec 2018 21:41:27 -0500 Subject: Automate updating SeaGRUB floppy image blocklists Related issue #553 Determining the byte offset of core.img on the SeaGRUB floppy is accomplished using grub_bo(), though it is memory bound as it requires reading hex dumps for the pattern and comparand into memory. The blocklists that are stored in the boot record and core.img are sector numbers. Of particular note is the blocklist written to core.img is always the location of its *second* sector--not the first. Blocklists are used because floppy disks DO NOT have an MBR or MBR gap as the filesystem spans the entire disk. Consequently, the core.img cannot be stored in the usual ~1MiB gap. Unfortunately, using blocklists means they will have to be updated whenever core.img is moved. New functions added to grub-helper: * grub_bo * grub_bo_dump * grub_bo_search * grub_blocklist_format * grub_blocklist_generate * grub_floppy_image_make_bootable * grub_floppy_image_update_blocklists --- projects/grub/grub-helper | 96 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) (limited to 'projects/grub/grub-helper') diff --git a/projects/grub/grub-helper b/projects/grub/grub-helper index 04814575..ccc9cd73 100755 --- a/projects/grub/grub-helper +++ b/projects/grub/grub-helper @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (C) 2017 Andrew Robbins +# Copyright (C) 2017,2018 Andrew Robbins # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -62,6 +62,47 @@ grub_modmin_path() { project_file_path "$project" "$CONFIGS" "$MODMIN" "$@" } +grub_bo_search() { + local pattern="$1" + local comparand="$2" + + grep -Fbof <(grub_bo_dump "$pattern") <(grub_bo_dump "$comparand") | cut -d: -f1 +} + +grub_bo_dump() { + local file="$1" + + od -Anx -t x1 -w16 -v "$file" | paste -sd '' | tr -d ' ' +} + +grub_bo() { + local pattern="$1" + local comparand="$2" + + local nibble_offset="$(grub_bo_search "$pattern" "$comparand")" + + if [[ -n "$nibble_offset" ]]; then + printf '0x%X\n' $((nibble_offset / 2)) + else + return 1 + fi +} + +grub_blocklist_format() { + local blocklist="$1" + local byte + + while read -N 2 byte; do + printf '%s' "\\x$byte" + done <<< "$blocklist" +} + +grub_blocklist_generate() { + local -i byte_offset="$1" + + printf '%04x' $((byte_offset / 512)) | tac -rs .. +} + grub_copy_modules() { local grub_module_dir="$sources_path/grub-core" local keep_dir="$build_path/$(grub_format "$target" "$@")" @@ -179,6 +220,7 @@ grub_build_floppy_image() { grub_floppy_image_mmd "$floppyimg" /boot /boot/grub "/boot/grub/$format" grub_floppy_image_mcopy "$floppyimg" "/boot/grub/$format" "${modules[@]}" + grub_floppy_image_make_bootable "$floppyimg" } grub_build_standalone_image() { @@ -232,3 +274,55 @@ grub_floppy_image_mcopy() { mcopy -i "$img" -pQv "${files[@]}" "::$target" fi } + +grub_floppy_image_make_bootable() { + local floppyimg="$1" + local bootimg="$build_path/boot.img" + local coreimg="$build_path/core.img" + local oem_name='\x4C\x49\x42\x52\x45\x20\x20\x20' + + # write $floppyimg Bios Parameter Block to $bootimg first + dd if="$floppyimg" of="$bootimg" bs=1 skip=11 seek=11 count=52 conv=notrunc + dd if=<(printf "$oem_name") of="$bootimg" bs=1 seek=3 conv=notrunc + dd if=/dev/zero of="$floppyimg" count=1 conv=notrunc + dd if="$bootimg" of="$floppyimg" conv=notrunc + + grub_floppy_image_mcopy "$floppyimg" /boot/grub "$bootimg" + grub_floppy_image_mcopy "$floppyimg" /boot/grub "$coreimg" + + grub_floppy_image_update_blocklists "$coreimg" "$floppyimg" + rm -f "$bootimg" "$coreimg" +} + +grub_floppy_image_update_blocklists() { + local coreimg="$1" + local floppyimg="$2" + + local -i coreimg_offset="$(grub_bo "$coreimg" "$floppyimg")" + local -i coreimg_second_sector_offset=$((coreimg_offset + 0x200)) + + local -i boot_record_blocklist_offset=0x5C + local -i coreimg_blocklist_offset=$((coreimg_offset + 0x1F4)) + + # blocklists (little endian) describe the $coreimg_offset in sectors + local boot_record_blocklist="$(grub_blocklist_generate "$coreimg_offset")" + local coreimg_blocklist="$(grub_blocklist_generate "$coreimg_second_sector_offset")" + + if [[ $coreimg_offset -gt 0 ]]; then + dd if=<(printf "$(grub_blocklist_format "$boot_record_blocklist")") \ + of="$floppyimg" \ + bs=1 \ + seek="$boot_record_blocklist_offset" \ + conv=notrunc + + dd if=<(printf "$(grub_blocklist_format "$coreimg_blocklist")") \ + of="$floppyimg" \ + bs=1 \ + seek="$coreimg_blocklist_offset" \ + conv=notrunc + else + printf 1>&2 '%s\n' "Error: ${coreimg##*/} offset not found" + + return 1 + fi +} -- cgit v1.2.3-70-g09d2