Booter une partition windows avec KVM
=====================================

Pour booter sur une partition windows depuis KVM, nous allons passer
par trois étapes : 

#. Créer un disque à l'aide de ``mdadm`` enagrégeant des images au format ``raw``
   et la partition, afin de recréer la table des partitions et la partition de boot.
#. Booter sur le disque créé et réinstaller la partition de boot à l'aide d'une
   image d'installation Windows
#. Installer et paramétrer les drivers kvm pour Windows


Pour ce tutoriel, je me suis grandement inspiré de `ce tutoriel <https://simgunz.org/posts/2021-12-12-boot-windows-partition-from-linux-kvm/>`_.

Installation des prérequis
--------------------------

On installe d'abord les paquets nécessaires à la virtualisation et ceux à la création des disques :

.. code-block:: bash

  pacman -S qemu-full virt-manager dnsmasq mdadm dosfstools

Création du disque mdadm :

On se place dans le répertoire des images libvirt :

.. code-block:: bash

  cd /var/lib/libvirt/images/


On créer ensuite les fichiers ``raw`` qui vont être agrégés à notre partition :

.. code-block:: bash

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

On identifie ensuite notre partition windows, par exemple ``/dev/nvme0n1p3``.

On vérifie que les modes nécessaires sont bien chargés, sinon on les démarre à la main :

.. code-block:: bash

  lsmod | grep loop
  lsmod | grep linear
  # On charge les modules si nécessaire :
  modprobe loop
  modprobe linear

On regarde le device loop disponible et on monte ensuite les images en loopback :

.. code-block:: bash

  losetup -f
  losetup /dev/loop0 /var/lib/libvirt/images/efi1
  losetup -f
  losetup /dev/loop1 /var/lib/libvirt/images/efi2

Pour finir on créer le disque md0 qui agrège les deux loop et notre partition.

.. code-block:: bash

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

Pour supprimer le disque, on utilise les commandes suivantes :

.. code-block:: bash

  mdadm --stop /dev/md0
  losetup -d /dev/loop0 /dev/loop1

Création de la table des partitions
-----------------------------------

Pour définir ou commence et ou finissent les partitions, il va falloir utiliser gparted avec les unités en secteur
et calculer la taille de nos fichiers raw en secteur.

On récupère la taille en bytes  avec la commande ls -la, et on la divise par 512, la taille des secteurs que l'on a
choisie quand on à créé le disque ``mdadm`` avec l'option ``--chunk=512``.

.. image:: ./calculatesizeinsector.png
  :width: 600px

On créer ensuite la table des partitions et la partiticon de boot :

.. code-block:: bash

  parted /dev/md0
  (parted) unit s                               # use sector size
  (parted) mktable gpt
  (parted) mkpart EFI fat32 2048 409599         # depends on size of efi1 file
  (parted) mkpart Windows ntfs 409600 -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) quit

Pour être sur que notre table des partitions est bien alignée sur la partition windows, on peut la monter :

.. code-block:: bash
  
  mount /dev/md0p2 /mnt
  ls -lha /mnt
  umount /mnt

Pour finir, on formate notre partition efi en fat 32 :

.. code-block:: bash

  mkfs.fat -F 32 /dev/md0p1

Premier boot et installation de la partition de démarrage Windows
-----------------------------------------------------------------

On peut télécharger l'iso d'installation de Windows `ici <https://www.microsoft.com/en-us/software-download/windows10ISO>`_

Lancement de la machine virtuelle et boot sur l'iso d'installation :

.. code-block:: bash

  qemu-system-x86_64 \
    -bios /usr/share/ovmf/x64/OVMF_CODE.fd \
    -drive file=/dev/md0,media=disk,format=raw \
    -cpu host -enable-kvm -m 4G \
    -cdrom /root/Downloads/Win10_22H2_English_x64v1.iso

On peut ensuite lancer une invite de commande pour installer la partition de boot Windows :

.. code-block:: msdos

  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
  exit

On peut éteindre l'ordinateur et redémarrer sans l'iso d'installation pour être sur que tout
fonctionne bien :

.. code-block:: bash

  qemu-system-x86_64 \
    -bios /usr/share/ovmf/x64/OVMF_CODE.fd \
    -drive file=/dev/md0,media=disk,format=raw \
    -cpu host -enable-kvm -m 4G

Création de la machine virtuelle dans virt-manager :
----------------------------------------------------

.. note:: Prendre des screenshots depuis la machine d'installation

File -> New Virtual Machine
Import existing disk image
Storage path -> Browse local -> /dev/md0
OS -> Windows 10
Memory 6144
CPU 4
Customize before install

Overview ->  Firmware  OVMF_CODE.fd
CPU -> Topology  1,2,2
Remove Tablet device
Video QLX

Vérifier le xml :

.. code-block:: xml

  <driver name="qemu" type="raw" cache="none" io="native"/>

  <features>
    <hyperv>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='8191'/>
    </hyperv>
  <features/>

  <clock>
    <timer name='hypervclock' present='yes'/>
  </clock>


Installation des drivers windows et optimisation de la config
-------------------------------------------------------------

Installation des drivers et config virtio
AutoResize

Automatiser la création de /dev/md0 au lancement de la machine virtuelle
------------------------------------------------------------------------


Création d'un script pour créer et supprimer ``/dev/md0``
---------------------------------------------------------

Pour faciliter la création et la suppression du volume mdadm, on peut utiliser un script tel que celui ci :

.. code-block:: bash

  #!/bin/bash

  _WINPART="/dev/nvme0n1p3"
  cd $(dirname "$0") || exit 1
  _EFIDIR=$(pwd)
  _LOOP1=""
  _LOOP2=""

  _start() {
    set -e
    #Do nothing if md0 exist
    [[ -e /dev/md0 ]] && printf "/dev/md0 already exists" && exit 1
    # Enable modules if not loaded
    lsmod | grep loop || modprobe loop
    lsmod | grep linear || modprobe linear
    _LOOP1=$(losetup -f)
    losetup "$_LOOP1" "$_EFIDIR/efi1"
    _LOOP2=$(losetup -f)
    losetup "$_LOOP2" "$_EFIDIR/efi2"
    mdadm --build --verbose /dev/md0 --chunk=512 --level=linear \
      --raid-devices=3 "$_LOOP1" "$_WINPART" "$_LOOP2"
    echo "$_LOOP1 $_LOOP2" > .loop-devices
  }

  _stop() {
    mdadm --stop /dev/md0
    xargs losetup -d < .loop-devices
  }

  _usage() {
    cat << EOF
  usage :
    md0.sh start    Create md0 device
    md0.sh stop     Destroy md0 device
  EOF
  }

  [[ "$1" == "start" ]] && _start
  [[ "$1" == "stop" ]] && _stop
  [[ "$1" == "usage" ]] && _usage






