Dual-boot Win 10 & EFI

Markus Köberl markus.koeberl at tugraz.at
Mon Aug 12 14:04:08 CEST 2019


On Wednesday, 5 June 2019 08:53:19 CEST John G Heim wrote:
> Anybody know how to save & restore the signature file for a Windows 10 
> install on a disk partitioned with GPT? Some time ago, I got a tip on 
> this list on doing a dual boot install. Copy some blocks from /dev/sda 
> to a temp file and then copy them back after partitioning the disk.
> 
> dd if=/dev/sda of=/tmp/fai/signature ibs=1 skip=440 count=4
> ...
> dd if=/tmp/fai/signature of=/dev/sda obs=1 seek=440 count=4
> 
> That works great on a machine with an msdos partition table. But it does 
> not seem to work on a disk partitioned via GPT.


I got it working, put it is not a very clean solution. You have to modify the files.
Probably you see less partitions used by Windows. The number changed after an Windows update on my systems and of course made grub stop working. The usual funny unexpected thing with Microsoft I guess :-(
Inline in this email are the files I have in the fai config space. Attached are my scripts for creating and restoring an image of the windows installation and a script to change the windows hostname.
I but an image of the windows installation on a network share which a can access during the installation.
The renaming is necessary as I have a fill lab with identical hosts which I am cloning this way.

You have to change some paths in the scripts like `/usr/local/SPSCscripts/fai` which is the place where I put all the scripts and how to access the images `mfsmount -H cfshm /clusterFS` mounts out MooseFS cluster file system. In some of the scrips I am using regular expressions to match a group of hosts. So have a closer look at the scripts and change what is necessary.
You will also have to install some packages into your fai nfsroot necessary for manipulating the partition information.
I had to change the scripts several times (moved form bios boot to uefi boot). So they got a little bit messy and I never found the time to clean it up. It is working with uefi boot and disabled secure boot and legacy mode on a M2 SSD using nvme protocol.
But probably will also work with different settings but is untested and I may have broken things while changing the scripts.
Be careful with uefi that you also run the fai installation via uefi mode. I remember that I had problems with that years ago.

Let me know if i forgot a script or something is unclear. I hope it is of use although it is not generic and will only work in my environment without changes...


disk_config/WINDOWS:

####################################
# gpt partiton table for uefi bios #
####################################

disk_config disk1 disklabel:gpt align-at:1M fstabkey:uuid bootable:2

# windows boot partition
primary  -                   450      -     -
# uefi boot partition
primary  /boot/efi           100      vfat  rw
# windows reserved partition
primary  -                    16      -     -
# windows C:
primary  -                142773      -     -
primary  -                   859      -     -
# linux:
primary  /                   40G      ext4  rw,noatime,errors=remount-ro tuneopts="-c 0 -i 0"
primary  /tmp                20G      ext4  rw,noatime createopts="-L tmp -m 5" tuneopts="-c 0 -i 0"
primary  /var/cache/openafs   5G      ext2  rw,noatime createopts="-L openafs -m 5" tuneopts="-c 0 -i 0"
primary  swap                 5G      swap  rw
primary  /home               10G-     ext4  rw,noatime createopts="-L srv -m 1" tuneopts="-c 0 -i 0"


hooks/mountdisks.WINDOWS:
#!/bin/bash

error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code

if [ -z "$BOOT_DEVICE" ]; then
    exit 189
fi

if ifclass GRUB_PC; then
    # force partition id on partition 1 and 2 to id 7 (NTFS) necessary for first installation
    # and mark windows boot partiotin as bootable (necessary for updates to work)
    sfdisk -d ${BOOT_DEVICE} | \
      sed -e 's#^\('${BOOT_DEVICE}'1 .*type=\).*$#\17, bootable#' | \
      sed -e 's#^\('${BOOT_DEVICE}'2 .*type=\).*$#\17#' | \
      sfdisk ${BOOT_DEVICE}
fi
if ifclass GRUB_UEFI; then
    # Disk GUID
    sgdisk --disk-guid=692A8440-E7A6-4F1E-8D8E-B6ABF895F247 ${BOOT_DEVICE}
    # Partiton GUID
    sgdisk --partition-guid=1:B8BAA86C-B3D4-4A02-8713-03FE4B7AC42D ${BOOT_DEVICE}
    sgdisk --partition-guid=2:BC9CC09F-D739-4FF3-9650-72FD84BD92AB ${BOOT_DEVICE}
    sgdisk --partition-guid=3:98D35356-2FB2-4A07-A1F3-1ACF486F8642 ${BOOT_DEVICE}
    sgdisk --partition-guid=4:6848A458-2A6C-4C73-AD6D-6B6CC9DAF8D6 ${BOOT_DEVICE}
    sgdisk --partition-guid=5:262EFA0A-1DE4-4DC1-B2F7-F203A8D2A712 ${BOOT_DEVICE}
    # Partiton type
    sgdisk --typecode=1:2700 ${BOOT_DEVICE}
    sgdisk --typecode=2:EF00 ${BOOT_DEVICE}
    sgdisk --typecode=3:0C01 ${BOOT_DEVICE}
    sgdisk --typecode=4:0700 ${BOOT_DEVICE}
    sgdisk --typecode=5:2700 ${BOOT_DEVICE}
    # Partition name
    sgdisk --change-name=1:"Basic data partition" ${BOOT_DEVICE}
    sgdisk --change-name=2:"EFI system partition" ${BOOT_DEVICE}
    sgdisk --change-name=3:"Microsoft reserved partition" ${BOOT_DEVICE}
    sgdisk --change-name=4:"Basic data partition" ${BOOT_DEVICE}
    # Partition attributes
    sgdisk --attributes=1:set:0  ${BOOT_DEVICE}
    sgdisk --attributes=1:set:63 ${BOOT_DEVICE}
    sgdisk --attributes=2:set:63 ${BOOT_DEVICE}
    sgdisk --attributes=3:set:63 ${BOOT_DEVICE}
    sgdisk --attributes=5:set:0  ${BOOT_DEVICE}
    sgdisk --attributes=5:set:63 ${BOOT_DEVICE}
fi

exit $error



fai-config/scripts/WINDOWS/60-restore:

#!/bin/bash

error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code


# skip the rest, if not an initial installation
if [ $FAI_ACTION = "install" ]; then

    # restore windows
    if ifclass WINDOWS; then

        # configure network for clusterFS usage
        ifconfig ${NIC1}:1 172.16.${IPADDR#129.27.*}
        # mount clusterFS
        mkdir /clusterFS
        mfsmount -H cfshm /clusterFS
        # restore latest windows
        cd ${target}/usr/local/SPSCscripts/fai

        if ifclass GRUB_PC; then

            # get dev
            windows_dev=$(echo ${disklist} | awk '{print $1}')
            ./restoreWindowsFromClusterFS.sh -e -d -q --backup newest --dev "/dev/${windows_dev}"
            # Windows C: should be partition 2
            ./renameWindows10.sh "/dev/${windows_dev}2"
            # add grub menue entry for Windows (partitin 1)
            # get UUID
            eval "$(blkid -o udev /dev/${windows_dev}1)"
            # create custom menue entry
            cat >> $target/etc/grub.d/40_custom <<-EOF

menuentry "Windows 10" --class windows --class os {
   insmod ntfs
   search --no-floppy --set=root --fs-uuid ${ID_FS_UUID}
   ntldr /bootmgr
}

EOF
            # set timeout
            perl -pi -pe "s/GRUB_TIMEOUT=\d+/GRUB_TIMEOUT=30/" $target/etc/default/grub
            $ROOTCMD update-grub

        fi
        if ifclass GRUB_UEFI; then

            # usually windows is installed on first disk
            WINDOWS_BOOT_DEVICE="/dev/$(echo ${disklist} | awk '{print $1}')"
            # restore filesystem images
            ./restoreEfiWindowsFromClusterFS.sh --backup newest -e --dev "${WINDOWS_BOOT_DEVIC}"
            # set windows hostname
            TYPE="EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"
            WINDOWS_PARTITION=`sfdisk -J "${WINDOWS_BOOT_DEVICE}" | jq '.partitiontable.partitions | .[] | select(.type=="'${TYPE}'") | .node' | sed 's/"//g'`
            ./renameWindows10.sh "${WINDOWS_PARTITION}"
            # set timeout
            perl -pi -pe "s/GRUB_TIMEOUT=\d+/GRUB_TIMEOUT=30/" $target/etc/default/grub
            $ROOTCMD update-grub

        fi

        cd /
        # umount clusterFS
        umount /clusterFS
        # take network interface for clusterFS down
        ifconfig ${NIC1}:1 down

    fi
fi

exit $error


fai-config/scripts/GRUB_UEFI/10-setup:

#!/bin/bash

error=0 ; trap "error=$((error|1))" ERR

set -a

[[ $FAI_ACTION != "softupdate" ]] || exit 0

if [ -z "$BOOT_DEVICE" ]; then
    exit 189
fi

modprobe efivars

# force target arch in case we did not boot using uefi pxe boot
if ifclass AMD64; then
    GRUB_TARGET_ARCH="--target=x86_64-efi"
fi

$ROOTCMD grub-install $GRUB_TARGET_ARCH $BOOT_DEVICE
$ROOTCMD update-grub

# update boot order
# get number for debian
EFI_DEBIAN_NUMBER=`$ROOTCMD efibootmgr | awk 'match($0,/Boot(.*)\* debian/,var) {print var[1]}'`
# get order into array
EFI_BOOT_ORDER=(`$ROOTCMD efibootmgr | awk 'match($0,/BootOrder: (.*)/,var) {print var[1]}' | tr -t ',' ' '`)

$ROOTCMD efibootmgr -o ${EFI_DEBIAN_NUMBER},`echo ${EFI_BOOT_ORDER[@]/$EFI_DEBIAN_NUMBER} | tr -t ' ' ','`

exit $error



regard
Markus Köberl
-- 
Markus Koeberl
Graz University of Technology
Signal Processing and Speech Communication Laboratory
E-mail: markus.koeberl at tugraz.at
-------------- next part --------------
A non-text attachment was scrubbed...
Name: backupEfiWindowsToClusterFS.sh
Type: application/x-shellscript
Size: 11270 bytes
Desc: not available
URL: <http://lists.uni-koeln.de/pipermail/linux-fai/attachments/20190812/41e19dad/attachment-0005.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: backupWindowsToClusterFS.sh
Type: application/x-shellscript
Size: 9502 bytes
Desc: not available
URL: <http://lists.uni-koeln.de/pipermail/linux-fai/attachments/20190812/41e19dad/attachment-0006.bin>
-------------- next part --------------
#!/usr/bin/expect -f
#
# This Expect script was generated by autoexpect on Wed Nov 28 12:25:16 2012
# Expect and autoexpect were both written by Don Libes, NIST.
#
# Note that autoexpect does not guarantee a working script.  It
# necessarily has to guess about certain things.  Two reasons a script
# might fail are:
#
# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
# etc.) and devices discard or ignore keystrokes that arrive "too
# quickly" after prompts.  If you find your new script hanging up at
# one spot, try adding a short sleep just before the previous send.
# Setting "force_conservative" to 1 (see below) makes Expect do this
# automatically - pausing briefly before sending each character.  This
# pacifies every program I know of.  The -c flag makes the script do
# this in the first place.  The -C flag allows you to define a
# character to toggle this mode off and on.

set force_conservative 0  ;# set to 1 to force conservative mode even if
			  ;# script wasn't run conservatively originally
if {$force_conservative} {
	set send_slow {1 .1}
	proc send {ignore arg} {
		sleep .1
		exp_send -s -- $arg
	}
}

#
# 2) differing output - Some programs produce different output each time
# they run.  The "date" command is an obvious example.  Another is
# ftp, if it produces throughput statistics at the end of a file
# transfer.  If this causes a problem, delete these patterns or replace
# them with wildcards.  An alternative is to use the -p flag (for
# "prompt") which makes Expect only look for the last line of output
# (i.e., the prompt).  The -P flag allows you to define a character to
# toggle this mode off and on.
#
# Read the man page for more info.
#
# -Don


set timeout -1

# get hostname
spawn "/bin/hostname"
expect
set myname $expect_out(buffer)
puts "hostname : $myname";

spawn chntpw -e /mnt/win/Windows/System32/config/SYSTEM
match_max 100000
expect -exact "> "
send -- "cd ControlSet001"
expect -exact "cd ControlSet001"
send -- "\r"
expect -exact "\\ControlSet001> "
send -- "cd Control"
expect -exact "cd Control"
send -- "\r"
expect -exact "\\ControlSet001\\Control> "
send -- "cd ComputerName"
expect -exact "cd ComputerName"
send -- "\r"
expect -exact "\\ControlSet001\\Control\\ComputerName> "
send -- "cd ComputerName"
expect -exact "cd ComputerName"
send -- "\r"
expect -exact "(...)\\Control\\ComputerName\\ComputerName> "
send -- "ed ComputerName"
expect -exact "ed ComputerName"
send -- "\r"
expect -exact "-> "
send -- "$myname"
expect -exact "(...)\\Control\\ComputerName\\ComputerName> "
send -- "q\r"
expect -exact "Write hive files? (y/n) \[n\] : "
send -- "y\r"
expect eof
-------------- next part --------------
#!/usr/bin/expect -f
#
# This Expect script was generated by autoexpect on Wed Nov 28 12:29:16 2012
# Expect and autoexpect were both written by Don Libes, NIST.
#
# Note that autoexpect does not guarantee a working script.  It
# necessarily has to guess about certain things.  Two reasons a script
# might fail are:
#
# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
# etc.) and devices discard or ignore keystrokes that arrive "too
# quickly" after prompts.  If you find your new script hanging up at
# one spot, try adding a short sleep just before the previous send.
# Setting "force_conservative" to 1 (see below) makes Expect do this
# automatically - pausing briefly before sending each character.  This
# pacifies every program I know of.  The -c flag makes the script do
# this in the first place.  The -C flag allows you to define a
# character to toggle this mode off and on.

set force_conservative 0  ;# set to 1 to force conservative mode even if
			  ;# script wasn't run conservatively originally
if {$force_conservative} {
	set send_slow {1 .1}
	proc send {ignore arg} {
		sleep .1
		exp_send -s -- $arg
	}
}

#
# 2) differing output - Some programs produce different output each time
# they run.  The "date" command is an obvious example.  Another is
# ftp, if it produces throughput statistics at the end of a file
# transfer.  If this causes a problem, delete these patterns or replace
# them with wildcards.  An alternative is to use the -p flag (for
# "prompt") which makes Expect only look for the last line of output
# (i.e., the prompt).  The -P flag allows you to define a character to
# toggle this mode off and on.
#
# Read the man page for more info.
#
# -Don


set timeout -1

# get hostname
spawn "/bin/hostname"
expect
set myname $expect_out(buffer)
puts "hostname : $myname";

spawn chntpw -e /mnt/win/Windows/System32/config/SYSTEM
match_max 100000
expect -exact "> "
send -- "cd ControlSet001"
expect -exact "cd ControlSet001"
send -- "\r"
expect -exact "\\ControlSet001> "
send -- "cd Services"
expect -exact "cd Services"
send -- "\r"
expect -exact "\\ControlSet001\\Services> "
send -- "cd Tcpip"
expect -exact "cd Tcpip"
send -- "\r"
expect -exact "\\ControlSet001\\Services\\Tcpip> "
send -- "cd Parameters"
expect -exact "cd Parameters"
send -- "\r"
expect -exact "\\ControlSet001\\Services\\Tcpip\\Parameters> "
send -- "ed HostName"
expect -exact "ed HostName"
send -- "\r"
expect -exact "-> "
send -- "$myname"
send -- "\r"
expect -exact "\\ControlSet001\\Services\\Tcpip\\Parameters> "
send -- "ed NV HostName"
expect -exact "ed NV HostName"
send -- "\r"
expect -exact "-> "
send -- "$myname"
send -- "\r"
expect -exact "\\ControlSet001\\Services\\Tcpip\\Parameters> "
send -- "q\r"
expect -exact "Write hive files? (y/n) \[n\] : "
send -- "y\r"
expect eof
-------------- next part --------------
A non-text attachment was scrubbed...
Name: restoreEfiWindowsFromClusterFS.sh
Type: application/x-shellscript
Size: 13174 bytes
Desc: not available
URL: <http://lists.uni-koeln.de/pipermail/linux-fai/attachments/20190812/41e19dad/attachment-0007.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: renameWindows10.sh
Type: application/x-shellscript
Size: 1138 bytes
Desc: not available
URL: <http://lists.uni-koeln.de/pipermail/linux-fai/attachments/20190812/41e19dad/attachment-0008.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: restoreWindowsFromClusterFS.sh
Type: application/x-shellscript
Size: 11390 bytes
Desc: not available
URL: <http://lists.uni-koeln.de/pipermail/linux-fai/attachments/20190812/41e19dad/attachment-0009.bin>


More information about the linux-fai mailing list