Good day, Geoff.
Below is a "tutorial." In it, I deal with QEmu geometry and
cylinder-aligned partitions, but that is very "old school" and not
particularly relevant to your inquiry.
Use a work-directory:
$ mkdir geoff/; cd geoff;
Create a 50 MiB HDD image:
$ dd if=/dev/zero of=geoff.hdd count=0 seek=$((50 * 1024 * 1024 / 512))
0+0 records in
0+0 records out
0 bytes copied, 0.00014406 s, 0.0 kB/s
$ dpkg -L syslinux-common | grep geodsp1s
/usr/lib/syslinux/mbr/diag/geodsp/geodsp1s.img.xz
Write geometry-detection code to it:
$ cat /usr/lib/syslinux/mbr/diag/geodsp/geodsp1s.img.xz | xz
--decompress | dd of=geoff.hdd conv=notrunc
16129+0 records in
16129+0 records out
8258048 bytes (8.3 MB, 7.9 MiB) copied, 0.0195868 s, 422 MB/s
Boot with QEmu:
$ qemu-system-i386 -nographic -hda geoff.hdd -boot c
[...]
SeaBIOS (version 1.14.0-2)
iPXE (http://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+07F8F4C0+07ECF4C0
CA00
Booting from Hard Disk...
80CHS 0063,0F,3F
@CHS 0000,01,01:0000003F
@CHS 0001,00,01:000003F0
@EDD 0000003F:0000003F
@EDD 00003EC1:00003EC1
D=EDD
end
Control-A, C, quit
This QEmu VM's SeaBIOS is using 16 heads (0x0F + 1) and 63 sectors
(0x3F) for this small image.
$ node -e 'function chs_to_lba(n_heads, n_sectors, c, h, s) { return
(((c * n_heads) + h) * n_sectors) + (s - 1); }
console.log(chs_to_lba(16, 63, 1, 0, 1));'
1008
Round up the image to a cyclinder boundary, add yet another cylinder
(mostly wasted) for GPT secondary header:
$ node -e 'console.log(50 * 1024 * 1024 / 512 / 1008);'
101.58730158730158
$ dd if=/dev/zero of=geoff.hdd count=0 seek=$((103 * 1008))
0+0 records in
0+0 records out
0 bytes copied, 0.000141558 s, 0.0 kB/s
Cross fingers that QEmu won't use a different geometry for this
slightly-larger image.
Sector-count:
$ blockdev --getsz geoff.hdd
103824
The last sector for a nice partition (leaving the last cylinder alone)
will be:
$ echo $((102 * 1008 - 1))
102815
Wipe the geometry detection program from the MBR:
$ dd if=/dev/zero of=geoff.hdd count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 0.000169125 s, 3.0 MB/s
Use gdisk to prepare the HDD, including a hybrid MBR:
$ gdisk geoff.hdd
GPT fdisk (gdisk) version 1.0.6
Warning: Partition table header claims that the size of partition table
entries is 0 bytes, but this program supports only 128-byte entries.
Adjusting accordingly, but partition table may be garbage.
Warning: Partition table header claims that the size of partition table
entries is 0 bytes, but this program supports only 128-byte entries.
Adjusting accordingly, but partition table may be garbage.
Partition table scan:
MBR: not present
BSD: not present
APM: not present
GPT: not present
Creating new GPT entries in memory.
Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y
Command (? for help): x
Expert command (? for help): l
Enter the sector alignment value (1-65536, default = 2048): 1008
Expert command (? for help): m
Command (? for help): n
Partition number (1-128, default 1): 1
First sector (34-103790, default = 1008) or {+-}size{KMGTP}: 1008
Last sector (1008-103790, default = 103790) or {+-}size{KMGTP}: 102815
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8300
Changed type of partition to 'Linux filesystem'
Command (? for help): r
Recovery/transformation command (? for help): h
WARNING! Hybrid MBRs are flaky and dangerous! If you decide not to use
one,
just hit the Enter key at the below prompt and your MBR partition table
will
be untouched.
Type from one to three GPT partition numbers, separated by spaces, to be
added to the hybrid MBR, in sequence: 1
Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? (Y/N): y
Creating entry for GPT partition #1 (MBR partition #2)
Enter an MBR hex code (default 83): 83
Set the bootable flag? (Y/N): y
Unused partition space(s) found. Use one to protect more partitions?
(Y/N): y
Note: Default is 0xEE, but this may confuse Mac OS X.
Enter an MBR hex code (default EE): ee
Recovery/transformation command (? for help): w
Final checks complete. About to write GPT data. THIS WILL OVERWRITE
EXISTING
PARTITIONS!!
Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to geoff.hdd.
Caution: More than one 0xEE MBR partition found. This can cause problems
in some OSes.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.
Save the first 34 blocks:
$ dd if=geoff.hdd of=orig_mbr_plus_gpt.dd count=34
34+0 records in
34+0 records out
17408 bytes (17 kB, 17 KiB) copied, 0.000277563 s, 62.7 MB/s
Use fdisk to adjust the hybrid MBR to use the QEmu geometry and move up
a partition:
$ fdisk -t mbr geoff.hdd
Welcome to fdisk (util-linux 2.36.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): c
DOS Compatibility flag is set (DEPRECATED!)
Command (m for help): x
Expert command (m for help): h
Number of heads (1-255, default 255): 16
Expert command (m for help): s
Number of sectors (1-63, default 63): 63
Expert command (m for help): r
Command (m for help): d
Partition number (1,2,4, default 4): 2
Partition 2 has been deleted.
Command (m for help): n
Partition type
p primary (2 primary, 0 extended, 2 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (2,3, default 2): 2
First sector (1008-102815, default 1008): 1008
Last sector, +/-sectors or +/-size{K,M,G,T,P} (1008-102815, default
102815): 102815
Created a new partition 2 of type 'Linux' and of size 49.7 MiB.
Command (m for help): a
Partition number (1,2,4, default 4): 2
The bootable flag on partition 2 is enabled now.
Command (m for help): d
Partition number (1,2,4, default 4): 4
Partition 4 has been deleted.
Command (m for help): n
Partition type
p primary (2 primary, 0 extended, 2 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (3,4, default 3): 3
First sector (102816-103823, default 102816): 102816
Last sector, +/-sectors or +/-size{K,M,G,T,P} (102816-103823, default
103823): 103823
Created a new partition 3 of type 'Linux' and of size 504 KiB.
Command (m for help): t
Partition number (1-3, default 3): 3
Hex code or alias (type L to list all): ee
Changed type of partition 'Linux' to 'GPT'.
Command (m for help): w
The partition table has been altered.
Syncing disks.
Hybrid MBR looks like:
$ fdisk -l -t mbr geoff.hdd
Disk geoff.hdd: 50.7 MiB, 53157888 bytes, 103824 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000
Device Boot Start End Sectors Size Id Type
geoff.hdd1 1 1007 1007 503.5K ee GPT
geoff.hdd2 * 1008 102815 101808 49.7M 83 Linux
geoff.hdd3 102816 103823 1008 504K ee GPT
Attach as a loop device:
$ losetup -f --show -P geoff.hdd
/dev/loop0
Create a FAT12 FS on the partition:
$ mkfs.fat -F 12 -g 16/63 -n SYSLINUX /dev/loop0p1
mkfs.fat 4.2 (2021-01-31)
Mount the partition:
$ mkdir fat12/
$ mount -t vfat /dev/loop0p1 fat12/
Create a useful directory structure:
$ mkdir fat12/BIOS/
$ mkdir -p fat12/EFI/BOOT/
Create a syslinux.cfg file:
$ cat << 'EOF' > syslinux.cfg
PROMPT 1
TIMEOUT 100
TOTALTIMEOUT 3000
DEFAULT default
LABEL default
KERNEL /vmlinuz-5.10.0-18-686-pae rdinit=/bin/sh console=ttyS0
INITRD /initrd.img-5.10.0-18-686-pae
EOF
Copy your syslinux.cfg file to both of these directories:
$ cp syslinux.cfg fat12/BIOS/
$ cp syslinux.cfg fat12/EFI/BOOT/
Copy in a 32-bit Debian Linux kernel and initramfs archive:
[...]
$ file fat12/vmlinuz-5.10.0-18-686-pae
fat12/vmlinuz-5.10.0-18-686-pae: Linux kernel x86 boot executable
bzImage, version 5.10.0-18-686-pae (debian-kernel at lists.debian.org) #1
SMP Debian 5.10.140-1 (2022-09-02), RO-rootFS, swap_dev 0x4, Normal VGA
$ file fat12/initrd.img-5.10.0-18-686-pae
fat12/initrd.img-5.10.0-18-686-pae: gzip compressed data, was
"mkinitramfs-MAIN_WZ0vP2", last modified: Thu Nov 21 16:56:36 2024,
from
Unix, original size modulo 2^32 80993792
Copy in the Syslinux (U)EFI programs (you only seemed to want the x86_64
one, so the i386 one isn't needed, but I've included it):
$ dpkg -L syslinux-efi | grep /efi | grep efi$
/usr/lib/SYSLINUX.EFI/efi32/syslinux.efi
/usr/lib/SYSLINUX.EFI/efi64/syslinux.efi
$ cp /usr/lib/SYSLINUX.EFI/efi32/syslinux.efi
fat12/EFI/BOOT/BOOTIA32.EFI
$ cp /usr/lib/SYSLINUX.EFI/efi64/syslinux.efi fat12/EFI/BOOT/BOOTX64.EFI
Copy in Syslinux binaries for the 3 combinations (again, you only seemed
to want one):
$ dpkg -L syslinux-common | grep ldlinux
/usr/lib/syslinux/modules/bios/ldlinux.c32
/usr/lib/syslinux/modules/efi32/ldlinux.e32
/usr/lib/syslinux/modules/efi64/ldlinux.e64
$ cp /usr/lib/syslinux/modules/bios/ldlinux.c32 fat12/BIOS/
$ cp /usr/lib/syslinux/modules/efi32/ldlinux.e32
/usr/lib/syslinux/modules/efi64/ldlinux.e64 fat12/EFI/BOOT/
Unmount the partition:
$ umount fat12/
Install the BIOS version of Syslinux:
$ syslinux -d /BIOS /dev/loop0p1
Install the Syslinux MBR code:
$ dpkg -L syslinux-common | grep mbr.bin
/usr/lib/syslinux/mbr/altmbr.bin
/usr/lib/syslinux/mbr/gptmbr.bin
/usr/lib/syslinux/mbr/mbr.bin
$ cat /usr/lib/syslinux/mbr/mbr.bin > /dev/loop0
Remove the loop device:
$ losetup -d /dev/loop0
Make a copy of (U)EFI default variables:
$ dpkg -L ovmf-ia32 | grep fd$
/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd
/usr/share/OVMF/OVMF32_VARS_4M.fd
$ dpkg -L ovmf | grep fd$
/usr/share/OVMF/OVMF_CODE.fd
/usr/share/OVMF/OVMF_CODE.secboot.fd
/usr/share/OVMF/OVMF_CODE_4M.fd
/usr/share/OVMF/OVMF_CODE_4M.secboot.fd
/usr/share/OVMF/OVMF_VARS.fd
/usr/share/OVMF/OVMF_VARS.ms.fd
/usr/share/OVMF/OVMF_VARS_4M.fd
/usr/share/OVMF/OVMF_VARS_4M.ms.fd
/usr/share/OVMF/OVMF_VARS_4M.snakeoil.fd
/usr/share/ovmf/OVMF.fd
/usr/share/OVMF/OVMF_CODE.ms.fd
/usr/share/OVMF/OVMF_CODE_4M.ms.fd
/usr/share/qemu/OVMF.fd
$ cp /usr/share/OVMF/OVMF32_VARS_4M.fd .
$ cp /usr/share/OVMF/OVMF_VARS.fd .
Test with 32-bit QEmu in BIOS mode:
$ qemu-system-i386 -m 256M -nographic -net none -hda geoff.hdd -boot c
[...]
BusyBox v1.30.1 (Debian 1:1.30.1-6+b3) built-in shell (ash)
Enter 'help' for a list of built-in commands.
(initramfs) poweroff -f
[ 16.519617] sd 0:0:0:0: [sda] Synchronizing SCSI cache
[ 16.520190] sd 0:0:0:0: [sda] Stopping disk
[ 16.521348] ACPI: Preparing to enter system sleep state S5
[ 16.521983] reboot: Power down
Test with 32-bit QEmu in (U)EFI mode:
$ qemu-system-i386 -m 256M -machine q35 -nographic -net none -drive
file=/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd,if=pflash,format=raw,unit=0,readonly=on
-drive if=pflash,format=raw,file=OVMF32_VARS_4M.fd -hda geoff.hdd
[...]
BusyBox v1.30.1 (Debian 1:1.30.1-6+b3) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/bin/sh: can't access tty; job control turned off
/ # mkdir /sys/
/ # mount -t sysfs sysfs /sys/
/ # ls /sys/firmware/efi/
config_table fw_platform_size runtime systab
efivars fw_vendor runtime-map
/ # poweroff -f
[ 63.044284] ACPI: Preparing to enter system sleep state S5
[ 63.044933] reboot: Power down
Test with 64-bit QEmu in (U)EFI mode:
$ qemu-system-x86_64 -m 256M -machine q35 -nographic -net none -drive
file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on
-drive if=pflash,format=raw,file=OVMF_VARS.fd -hda geoff.hdd
[...]
BusyBox v1.30.1 (Debian 1:1.30.1-6+b3) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/bin/sh: can't access tty; job control turned off
/ # mkdir /sys/
/ # mount -t sysfs sysfs /sys/
/ # ls /sys/firmware/efi/
config_table fw_platform_size runtime
efivars fw_vendor systab
/ # poweroff -f
[ 29.641487] ACPI: Preparing to enter system sleep state S5
[ 29.642205] reboot: Power down
All of these QEmu-based tests passed, for me. What's different, for
you, Geoff?
- Shao Miller