最近尝试在把工作环境切换到Linux,但其中遇到了不少麻烦,尤其是我的开发工作和Windows强相关,需要用到很多闭源的Win-only软件(比如IAR,S32DS,各种嵌入式开发工具)
我直觉性的以为像安装黑群晖那样解决了直通直通问题,就没有后顾之忧了,然后现实是只直通Windows的安装分区是无法直接启动的
Why: 现代x86平台操作系统的引导通常有两种方式,MBR和UEFI
使用UEFI的引导,首先会去查找ESP分区中已经被注册的efi文件,并加载入内存执行,同时Windows的boot.efi也校验磁盘的UUID是否和注册的一致来确保系统安全启动。
而MBR则会检查磁盘首部引导区中被写入的配置,同时去读取被“激活”的主分区引导信息

而只直通一个分区为磁盘的话,是不会有磁盘的UUID信息的,甚至连MBR都无法做到(因为首位也没有空白的区域)

所以单磁盘场景下,我们需要创建一个类Raid0的虚拟阵列,为Windows安装分区添加首部分区信息和ESP引导分区,这样就可以正常的引导

首先我们需要使能这两个内核模块

sudo modprobe loop
sudo modprobe linear

生成一个1M的和100M的空白文件

dd if=/dev/zero of=efi1 bs=1M count=100
dd if=/dev/zero of=efi2 bs=1M count=1

创建两个Loop文件,分别用于存放分区表和ESP分区,我们称其为esp1 esp2

sudo losetup -f efi1
sudo losetup -f efi2

我们要确保这两个loop文件已经正确的挂载到了/dev/loop0和/dev/loop1,执行以下命令检查

losetup -a

现在,我们要将这我们刚刚创建的loop分区和我们的Windows安装分区组成一个RAID设备,请自行将sda2修改为windows安装分区

sudo mdadm --build --verbose /dev/md0 --chunk=512 --level=linear --raid-devices=3 /dev/loop0 /dev/sda2 /dev/loop1

接下来是创建分区的时候了,你可以跟着下面parted的参考操作,也可以使用更简单的方法,比如引导一个WIN PE并执行分区软件

sudo parted /dev/md0
(parted) unit s
(parted) mktable gpt
(parted) mkpart primary fat32 2048 204799    # depends on size of efi1 file
(parted) mkpart primary ntfs 204800 -2049    # depends on size of efi1 and efi2 files
(parted) set 1 boot on
(parted) set 1 esp on
(parted) set 2 msftdata on
(parted) name 1 EFI
(parted) name 2 Windows
(parted) quit
#格式化你的分区为EFI
sudo mkfs.msdos -F 32 -n EFI /dev/md0p1

现在你已经有了一个完整的虚拟磁盘,把它映射到Virt-manager中或者手动用QEMU来引导

使用
<disk type="block" device="disk">
  <driver name="qemu" type="raw" cache="none" io="native"/>
  <source dev="/dev/nvme0n1p1" index="1"/>
  <backingStore/>
  <target dev="sdg" bus="sata"/>
  <alias name="sata1-0-0"/>
  <address type="drive" controller="1" bus="0" target="0" unit="0"/>
</disk>


chown $USER:$USER /dev/md0  # 修改权限使得当前用户可以读写分区
手动使用QEMU,需要准备ovmf固件
qemu-system-x86_64 \
    -bios /usr/share/ovmf/ovmf_x64.bin \
    -drive file=/dev/md0,media=disk,format=raw \
    -cpu host -enable-kvm -m 2G \
    -cdrom /path/to/windows.iso

在Windows PE或者Windows 安装磁盘中启动 CMD,其中,安装磁盘可以使用Shift+f10

diskpart
DISKPART> list disk
DISKPART> select disk 0    # Select the disk
DISKPART> list volume      # Find EFI volume (partition) number
DISKPART> select volume 2  # Select EFI volume
DISKPART> assign letter=B  # Assign B: to EFI volume
DISKPART> exit

接下来创建引导项

bcdboot C:\Windows /s B: /f ALL

现在,你可以通过QEMU命令行或者Virt-manager图形化工具来引导Windows

qemu-system-x86_64 \
    -bios /usr/share/ovmf/ovmf_x64.bin \
    -drive file=/dev/md0,media=disk,format=raw \
    -cpu host -enable-kvm -m 2G

最后的一点小问题是,你需要每次开机的时候都手动创建/dev/md0来完成你的Windows启动,这里给出一个脚本参考如何自动化的挂载

#!/bin/bash
sudo losetup -f efi1
sudo losetup -f efi2
losetup -a
 sudo mdadm --build --verbose /dev/md0 --chunk=512 --level=linear --raid-devices=3 /dev/loop0 /dev/nvme0n1p2 /dev/loop1

如果很巧,你也是一位Nixos用户,那么这里有一份现成的作业答案

#mount-win10-disk.nix
{ lib, pkgs, config, ...}:
with lib;
{

  systemd.services.mount-win10-disks = {
    path = with pkgs; [mdadm util-linux];

    script = ''
      ${pkgs.util-linux}/bin/losetup -f /path/to/you/efi1
      ${pkgs.util-linux}/bin/losetup -f  /path/to/you/efi2
      ${pkgs.util-linux}/bin/losetup -a
       mdadm --build --verbose /dev/md0 --chunk=512 --level=linear --raid-devices=3 /dev/loop0 /dev/nvme0n1p2 /dev/loop1

    '';
    wantedBy = [ "multi-user.target" ];
  };
}


参考自: https://lejenome.tik.tn/post/boot-physical-windows-inside-qemu-guest-machine

标签: none

发表新评论