From 9eddf65670b967729e1984d0fe5ab661e1058ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=9B=BD?= Date: Thu, 26 Mar 2020 14:05:33 +0800 Subject: [PATCH] x86: generate EFI platform bootable images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add EFI platform bootable images for x86 platforms. These images can also boot from legacy BIOS platform. EFI System Partition need to be fat12/fat16/fat32 (not need to load filesystem drivers), so the first partition of EFI images are not ext4 filesystem any more. GPT partition table has an alternate partition table, we did not generate it. This may cause problems when use these images as qemu disk (kernel can not find rootfs), we pad enough sectors will be ok. Signed-off-by: 李国 [part_magic_* refactoring, removed genisoimage checks] Signed-off-by: Petr Štetiar (cherry picked from commit a6b7c3e672764858fd294998406ae791f5964b4a) --- config/Config-images.in | 53 +++++----------- include/image.mk | 1 + .../base-files/files/lib/upgrade/common.sh | 62 ++++++++++++++++--- scripts/gen_image_generic.sh | 10 ++- target/linux/x86/64/target.mk | 2 +- .../x86/base-files/lib/preinit/79_move_config | 9 +-- .../lib/preinit/81_upgrade_bootloader | 18 ++++++ .../x86/base-files/lib/upgrade/platform.sh | 60 ++++++++++++++---- target/linux/x86/generic/config-4.14 | 1 + target/linux/x86/generic/config-4.19 | 3 +- target/linux/x86/generic/config-4.9 | 1 + target/linux/x86/generic/target.mk | 2 +- target/linux/x86/legacy/target.mk | 2 +- 13 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 target/linux/x86/base-files/lib/preinit/81_upgrade_bootloader diff --git a/config/Config-images.in b/config/Config-images.in index 339ddeb096..a50547c29b 100644 --- a/config/Config-images.in +++ b/config/Config-images.in @@ -188,28 +188,28 @@ menu "Target Images" select PACKAGE_grub2 default y - config EFI_IMAGES - bool "Build EFI GRUB images (Linux x86 or x86_64 host only)" + config GRUB_EFI_IMAGES + bool "Build GRUB EFI images (Linux x86 or x86_64 host only)" depends on TARGET_x86 - depends on TARGET_ROOTFS_EXT4FS || TARGET_ROOTFS_ISO || TARGET_ROOTFS_JFFS2 || TARGET_ROOTFS_SQUASHFS + depends on TARGET_ROOTFS_EXT4FS || TARGET_ROOTFS_JFFS2 || TARGET_ROOTFS_SQUASHFS select PACKAGE_grub2 select PACKAGE_grub2-efi + select PACKAGE_kmod-fs-vfat default y config GRUB_CONSOLE bool "Use Console Terminal (in addition to Serial)" - depends on GRUB_IMAGES || EFI_IMAGES + depends on GRUB_IMAGES || GRUB_EFI_IMAGES default y config GRUB_SERIAL string "Serial port device" - depends on GRUB_IMAGES || EFI_IMAGES - default "hvc0" if TARGET_x86_xen_domu - default "ttyS0" if ! TARGET_x86_xen_domu + depends on GRUB_IMAGES || GRUB_EFI_IMAGES + default "ttyS0" config GRUB_BAUDRATE int "Serial port baud rate" - depends on GRUB_IMAGES || EFI_IMAGES + depends on GRUB_IMAGES || GRUB_EFI_IMAGES default 38400 if TARGET_x86_generic default 115200 @@ -220,21 +220,20 @@ menu "Target Images" config GRUB_BOOTOPTS string "Extra kernel boot options" - depends on GRUB_IMAGES || EFI_IMAGES - default "xencons=hvc" if TARGET_x86_xen_domu + depends on GRUB_IMAGES || GRUB_EFI_IMAGES help If you don't know, just leave it blank. config GRUB_TIMEOUT string "Seconds to wait before booting the default entry" - depends on GRUB_IMAGES || EFI_IMAGES + depends on GRUB_IMAGES || GRUB_EFI_IMAGES default "0" help If you don't know, 5 seconds is a reasonable default. config GRUB_TITLE string "Title for the menu entry in GRUB" - depends on GRUB_IMAGES || EFI_IMAGES + depends on GRUB_IMAGES || GRUB_EFI_IMAGES default "OpenWrt" help This is the title of the GRUB menu entry. @@ -243,39 +242,21 @@ menu "Target Images" config ISO_IMAGES bool "Build LiveCD image (ISO)" depends on TARGET_x86 - select GRUB_IMAGES + depends on GRUB_IMAGES || GRUB_EFI_IMAGES config VDI_IMAGES bool "Build VirtualBox image files (VDI)" - depends on TARGET_x86 || TARGET_x86_64 - select GRUB_IMAGES - select TARGET_IMAGES_PAD + depends on TARGET_x86 + depends on GRUB_IMAGES || GRUB_EFI_IMAGES select PACKAGE_kmod-e1000 config VMDK_IMAGES bool "Build VMware image files (VMDK)" - depends on TARGET_x86 || TARGET_x86_64 - select GRUB_IMAGES - select TARGET_IMAGES_PAD + depends on TARGET_x86 + depends on GRUB_IMAGES || GRUB_EFI_IMAGES select PACKAGE_kmod-e1000 default y - config VHD_IMAGES - bool "Build Hyper-V image files (VHD)" - depends on TARGET_x86 || TARGET_x86_64 - depends on GRUB_IMAGES || EFI_IMAGES - select TARGET_IMAGES_PAD - select PACKAGE_kmod-tulip - default n - - config QCOW2_IMAGES - bool "Build PVE/KVM image files (QCOW2)" - depends on TARGET_x86 || TARGET_x86_64 - depends on GRUB_IMAGES || EFI_IMAGES - select TARGET_IMAGES_PAD - select PACKAGE_kmod-e1000 - default n - config TARGET_IMAGES_PAD bool "Pad images to filesystem size (for JFFS2)" depends on GRUB_IMAGES || EFI_IMAGES @@ -307,7 +288,7 @@ menu "Target Images" config TARGET_ROOTFS_PARTNAME string "Root partition on target device" - depends on GRUB_IMAGES || EFI_IMAGES + depends on GRUB_IMAGES || GRUB_EFI_IMAGES help Override the root partition on the final device. If left empty, it will be mounted by PARTUUID which makes the kernel find the diff --git a/include/image.mk b/include/image.mk index cdbc129ded..00235daeb3 100644 --- a/include/image.mk +++ b/include/image.mk @@ -42,6 +42,7 @@ IMG_PREFIX:=$(VERSION_DIST_SANITIZED)-$(IMG_PREFIX_VERNUM)$(IMG_PREFIX_VERCODE)$ IMG_ROOTFS:=$(IMG_PREFIX)-rootfs IMG_COMBINED:=$(IMG_PREFIX)-combined IMG_PART_SIGNATURE:=$(shell echo $(SOURCE_DATE_EPOCH)$(LINUX_VERMAGIC) | $(MKHASH) md5 | cut -b1-8) +IMG_PART_DISKGUID:=$(shell echo $(SOURCE_DATE_EPOCH)$(LINUX_VERMAGIC) | $(MKHASH) md5 | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.{10})../\1-\2-\3-\4-\500/') MKFS_DEVTABLE_OPT := -D $(INCLUDE_DIR)/device_table.txt diff --git a/package/base-files/files/lib/upgrade/common.sh b/package/base-files/files/lib/upgrade/common.sh index 1da87b975d..e2b3ff9664 100755 --- a/package/base-files/files/lib/upgrade/common.sh +++ b/package/base-files/files/lib/upgrade/common.sh @@ -98,6 +98,24 @@ get_magic_long() { (get_image "$@" | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2>/dev/null } +get_magic_gpt() { + (get_image "$@" | dd bs=8 count=1 skip=64) 2>/dev/null +} + +get_magic_vfat() { + (get_image "$@" | dd bs=1 count=3 skip=54) 2>/dev/null +} + +part_magic_efi() { + local magic=$(get_magic_gpt "$@") + [ "$magic" = "EFI PART" ] +} + +part_magic_fat() { + local magic=$(get_magic_vfat "$@") + [ "$magic" = "FAT" ] +} + export_bootdevice() { local cmdline uuid disk uevent line local MAJOR MINOR DEVNAME DEVTYPE @@ -144,6 +162,17 @@ export_bootdevice() { fi done ;; + PARTUUID=????????-????-????-????-??????????02) + uuid="${rootpart#PARTUUID=}" + uuid="${uuid%02}00" + for disk in $(find /dev -type b); do + set -- $(dd if=$disk bs=1 skip=568 count=16 2>/dev/null | hexdump -v -e '8/1 "%02x "" "2/1 "%02x""-"6/1 "%02x"') + if [ "$4$3$2$1-$6$5-$8$7-$9" = "$uuid" ]; then + uevent="/sys/class/block/${disk##*/}/uevent" + break + fi + done + ;; /dev/*) uevent="/sys/class/block/${disk##*/}/uevent" ;; @@ -203,17 +232,34 @@ get_partitions() { # rm -f "/tmp/partmap.$filename" local part - for part in 1 2 3 4; do - set -- $(hexdump -v -n 12 -s "$((0x1B2 + $part * 16))" -e '3/4 "0x%08X "' "$disk") + part_magic_efi "$disk" && { + #export_partdevice will fail when partition number is greater than 15, as + #the partition major device number is not equal to the disk major device number + for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do + set -- $(hexdump -v -n 48 -s "$((0x380 + $part * 0x80))" -e '4/4 "%08x"" "4/4 "%08x"" "4/4 "0x%08X "' "$disk") - local type="$(( $(hex_le32_to_cpu $1) % 256))" - local lba="$(( $(hex_le32_to_cpu $2) ))" - local num="$(( $(hex_le32_to_cpu $3) ))" + local type="$1" + local lba="$(( $(hex_le32_to_cpu $4) * 0x100000000 + $(hex_le32_to_cpu $3) ))" + local end="$(( $(hex_le32_to_cpu $6) * 0x100000000 + $(hex_le32_to_cpu $5) ))" + local num="$(( $end - $lba ))" - [ $type -gt 0 ] || continue + [ "$type" = "00000000000000000000000000000000" ] && continue - printf "%2d %5d %7d\n" $part $lba $num >> "/tmp/partmap.$filename" - done + printf "%2d %5d %7d\n" $part $lba $num >> "/tmp/partmap.$filename" + done + } || { + for part in 1 2 3 4; do + set -- $(hexdump -v -n 12 -s "$((0x1B2 + $part * 16))" -e '3/4 "0x%08X "' "$disk") + + local type="$(( $(hex_le32_to_cpu $1) % 256))" + local lba="$(( $(hex_le32_to_cpu $2) ))" + local num="$(( $(hex_le32_to_cpu $3) ))" + + [ $type -gt 0 ] || continue + + printf "%2d %5d %7d\n" $part $lba $num >> "/tmp/partmap.$filename" + done + } fi } diff --git a/scripts/gen_image_generic.sh b/scripts/gen_image_generic.sh index 5162be11dd..9d05b6254e 100755 --- a/scripts/gen_image_generic.sh +++ b/scripts/gen_image_generic.sh @@ -20,7 +20,7 @@ sect=63 cyl=$(( (KERNELSIZE + ROOTFSSIZE) * 1024 * 1024 / (head * sect * 512))) # create partition table -set $(ptgen -o "$OUTPUT" -h $head -s $sect -p ${KERNELSIZE}m -p ${ROOTFSSIZE}m ${ALIGN:+-l $ALIGN} ${SIGNATURE:+-S 0x$SIGNATURE}) +set $(ptgen -o "$OUTPUT" -h $head -s $sect ${GUID:+-g} -p ${KERNELSIZE}m -p ${ROOTFSSIZE}m ${ALIGN:+-l $ALIGN} ${SIGNATURE:+-S 0x$SIGNATURE} ${GUID:+-G $GUID}) KERNELOFFSET="$(($1 / 512))" KERNELSIZE="$2" @@ -30,6 +30,12 @@ ROOTFSSIZE="$(($4 / 512))" [ -n "$PADDING" ] && dd if=/dev/zero of="$OUTPUT" bs=512 seek="$ROOTFSOFFSET" conv=notrunc count="$ROOTFSSIZE" dd if="$ROOTFSIMAGE" of="$OUTPUT" bs=512 seek="$ROOTFSOFFSET" conv=notrunc -make_ext4fs -J -L kernel -l "$KERNELSIZE" "$OUTPUT.kernel" "$KERNELDIR" +if [ -n "$GUID" ]; then + [ -n "$PADDING" ] && dd if=/dev/zero of="$OUTPUT" bs=512 seek="$(($ROOTFSOFFSET + $ROOTFSSIZE))" conv=notrunc count="$sect" + mkfs.fat -n kernel -C "$OUTPUT.kernel" -S 512 "$(($KERNELSIZE / 1024))" + mcopy -s -i "$OUTPUT.kernel" "$KERNELDIR"/* ::/ +else + make_ext4fs -J -L kernel -l "$KERNELSIZE" "$OUTPUT.kernel" "$KERNELDIR" +fi dd if="$OUTPUT.kernel" of="$OUTPUT" bs=512 seek="$KERNELOFFSET" conv=notrunc rm -f "$OUTPUT.kernel" diff --git a/target/linux/x86/64/target.mk b/target/linux/x86/64/target.mk index 95e32b3360..637bef1a09 100644 --- a/target/linux/x86/64/target.mk +++ b/target/linux/x86/64/target.mk @@ -1,5 +1,5 @@ ARCH:=x86_64 -BOARDNAME:=x64 64bit +BOARDNAME:=x64_64 DEFAULT_PACKAGES += kmod-button-hotplug kmod-e1000e kmod-e1000 kmod-igb kmod-bnx2 define Target/Description diff --git a/target/linux/x86/base-files/lib/preinit/79_move_config b/target/linux/x86/base-files/lib/preinit/79_move_config index aff720a52c..8dc44e772b 100644 --- a/target/linux/x86/base-files/lib/preinit/79_move_config +++ b/target/linux/x86/base-files/lib/preinit/79_move_config @@ -2,15 +2,16 @@ # Copyright (C) 2012-2015 OpenWrt.org move_config() { - local partdev + local partdev parttype=ext4 . /lib/upgrade/common.sh if export_bootdevice && export_partdevice partdev 1; then mkdir -p /boot - mount -t ext4 -o rw,noatime "/dev/$partdev" /boot - if [ -f /boot/sysupgrade.tgz ]; then - mv -f /boot/sysupgrade.tgz / + part_magic_fat "/dev/$partdev" && parttype=vfat + mount -t $parttype -o rw,noatime "/dev/$partdev" /boot + if [ -f "/boot/sysupgrade.tgz" ]; then + mv -f "/boot/sysupgrade.tgz" / fi mount --bind /boot/boot /boot fi diff --git a/target/linux/x86/base-files/lib/preinit/81_upgrade_bootloader b/target/linux/x86/base-files/lib/preinit/81_upgrade_bootloader new file mode 100644 index 0000000000..42f04d76a7 --- /dev/null +++ b/target/linux/x86/base-files/lib/preinit/81_upgrade_bootloader @@ -0,0 +1,18 @@ +upgrade_bootloader() { + local diskdev + + . /lib/upgrade/common.sh + + if [ ! -f /boot/grub/upgraded ] && export_bootdevice && export_partdevice diskdev 0; then + part_magic_efi "/dev/$diskdev" && return 0 + echo "(hd0) /dev/$diskdev" > /tmp/device.map + /usr/sbin/grub-bios-setup \ + -m "/tmp/device.map" \ + -d "/boot/grub" \ + -r "hd0,msdos1" \ + "/dev/$diskdev" \ + && touch /boot/grub/upgraded + fi +} + +[ "$INITRAMFS" = "1" ] || boot_hook_add preinit_main upgrade_bootloader diff --git a/target/linux/x86/base-files/lib/upgrade/platform.sh b/target/linux/x86/base-files/lib/upgrade/platform.sh index 439ba8f512..9147ddcf2a 100644 --- a/target/linux/x86/base-files/lib/upgrade/platform.sh +++ b/target/linux/x86/base-files/lib/upgrade/platform.sh @@ -1,3 +1,5 @@ +RAMFS_COPY_BIN='grub-bios-setup' + platform_check_image() { local diskdev partdev diff [ "$#" -gt 1 ] && return 1 @@ -17,8 +19,8 @@ platform_check_image() { get_partitions "/dev/$diskdev" bootdisk - #extract the boot sector from the image - get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b 2>/dev/null + echo "Extract boot sector from the image" + get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b get_partitions /tmp/image.bs image @@ -35,15 +37,38 @@ platform_check_image() { } platform_copy_config() { - local partdev + local partdev parttype=ext4 if export_partdevice partdev 1; then - mount -t ext4 -o rw,noatime "/dev/$partdev" /mnt - cp -af "$CONF_TAR" /mnt/ + part_magic_fat "/dev/$partdev" && parttype=vfat + mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt + cp -af "$CONF_TAR" "/mnt/" umount /mnt fi } +platform_do_bootloader_upgrade() { + local bootpart parttable=msdos + local diskdev="$1" + + if export_partdevice bootpart 1; then + mkdir -p /tmp/boot + mount -o rw,noatime "/dev/$bootpart" /tmp/boot + echo "(hd0) /dev/$diskdev" > /tmp/device.map + part_magic_efi "/dev/$diskdev" && parttable=gpt + + echo "Upgrading bootloader on /dev/$diskdev..." + grub-bios-setup \ + -m "/tmp/device.map" \ + -d "/tmp/boot/boot/grub" \ + -r "hd0,${parttable}1" \ + "/dev/$diskdev" \ + && touch /tmp/boot/boot/grub/upgraded + + umount /tmp/boot + fi +} + platform_do_upgrade() { local diskdev partdev diff @@ -54,11 +79,11 @@ platform_do_upgrade() { sync - if [ "$SAVE_PARTITIONS" = "1" ]; then + if [ "$UPGRADE_OPT_SAVE_PARTITIONS" = "1" ]; then get_partitions "/dev/$diskdev" bootdisk - #extract the boot sector from the image - get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b + echo "Extract boot sector from the image" + get_image_dd "$1" of=/tmp/image.bs count=63 bs=512b get_partitions /tmp/image.bs image @@ -69,7 +94,7 @@ platform_do_upgrade() { fi if [ -n "$diff" ]; then - get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync + get_image_dd "$1" of="/dev/$diskdev" bs=4096 conv=fsync # Separate removal and addtion is necessary; otherwise, partition 1 # will be missing if it overlaps with the old partition 2 @@ -83,13 +108,24 @@ platform_do_upgrade() { while read part start size; do if export_partdevice partdev $part; then echo "Writing image to /dev/$partdev..." - get_image "$@" | dd of="/dev/$partdev" ibs="512" obs=1M skip="$start" count="$size" conv=fsync + get_image_dd "$1" of="/dev/$partdev" ibs=512 obs=1M skip="$start" count="$size" conv=fsync else echo "Unable to find partition $part device, skipped." fi done < /tmp/partmap.image - #copy partition uuid echo "Writing new UUID to /dev/$diskdev..." - get_image "$@" | dd of="/dev/$diskdev" bs=1 skip=440 count=4 seek=440 conv=fsync + get_image_dd "$1" of="/dev/$diskdev" bs=1 skip=440 count=4 seek=440 conv=fsync + + platform_do_bootloader_upgrade "$diskdev" + local parttype=ext4 + part_magic_efi "/dev/$diskdev" || return 0 + + if export_partdevice partdev 1; then + part_magic_fat "/dev/$partdev" && parttype=vfat + mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt + set -- $(dd if="/dev/$diskdev" bs=1 skip=1168 count=16 2>/dev/null | hexdump -v -e '8/1 "%02x "" "2/1 "%02x""-"6/1 "%02x"') + sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1$4$3$2$1-$6$5-$8$7-$9/ig" /mnt/boot/grub/grub.cfg + umount /mnt + fi } diff --git a/target/linux/x86/generic/config-4.14 b/target/linux/x86/generic/config-4.14 index b752f2e55b..9a37b735da 100644 --- a/target/linux/x86/generic/config-4.14 +++ b/target/linux/x86/generic/config-4.14 @@ -126,6 +126,7 @@ CONFIG_FB_EFI=y CONFIG_FB_HYPERV=y # CONFIG_FB_I810 is not set # CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA is not set +CONFIG_FB_SIMPLE=y CONFIG_FB_SYS_COPYAREA=y CONFIG_FB_SYS_FILLRECT=y CONFIG_FB_SYS_FOPS=y diff --git a/target/linux/x86/generic/config-4.19 b/target/linux/x86/generic/config-4.19 index afd3c55c0a..7d59d2531c 100644 --- a/target/linux/x86/generic/config-4.19 +++ b/target/linux/x86/generic/config-4.19 @@ -138,6 +138,7 @@ CONFIG_FB_DEFERRED_IO=y CONFIG_FB_EFI=y CONFIG_FB_HYPERV=y # CONFIG_FB_I810 is not set +CONFIG_FB_SIMPLE=y CONFIG_FB_SYS_COPYAREA=y CONFIG_FB_SYS_FILLRECT=y CONFIG_FB_SYS_FOPS=y @@ -458,4 +459,4 @@ CONFIG_XEN_XENBUS_FRONTEND=y CONFIG_XPS=y CONFIG_ZLIB_DEFLATE=y # CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set -# CONFIG_INTEL_ATOMISP2_PM is not set \ No newline at end of file +# CONFIG_INTEL_ATOMISP2_PM is not set diff --git a/target/linux/x86/generic/config-4.9 b/target/linux/x86/generic/config-4.9 index 2eb30069c1..84b3d1a23f 100644 --- a/target/linux/x86/generic/config-4.9 +++ b/target/linux/x86/generic/config-4.9 @@ -127,6 +127,7 @@ CONFIG_FB_EFI=y CONFIG_FB_HYPERV=y # CONFIG_FB_I810 is not set # CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA is not set +CONFIG_FB_SIMPLE=y CONFIG_FB_SYS_COPYAREA=y CONFIG_FB_SYS_FILLRECT=y CONFIG_FB_SYS_FOPS=y diff --git a/target/linux/x86/generic/target.mk b/target/linux/x86/generic/target.mk index 70527d001d..90586f56d6 100644 --- a/target/linux/x86/generic/target.mk +++ b/target/linux/x86/generic/target.mk @@ -1,4 +1,4 @@ -BOARDNAME:=x86 32bit +BOARDNAME:=Generic CPU_TYPE :=pentium4 FEATURES += audio pci pcie usb DEFAULT_PACKAGES += kmod-button-hotplug diff --git a/target/linux/x86/legacy/target.mk b/target/linux/x86/legacy/target.mk index a82e49ea8f..bd0a87d67f 100644 --- a/target/linux/x86/legacy/target.mk +++ b/target/linux/x86/legacy/target.mk @@ -1,4 +1,4 @@ -BOARDNAME:=Legacy x86 (i586) +BOARDNAME:=Legacy define Target/Description Build firmware images for legacy x86 based boards