Uuntu 19.10でGPUパススルーの構築（X570、シングルGPU）

##Ubuntu19.10のKVMでシングルGPU構成でGPUパススルーが出来る手順について掲載します。

##環境
　　マザー：Biostar X570GT8
　　CPU：Ryzen3600
　　OS：ubuntu 19.10
　　GPU：RADEON RX580

###1. grubの設定

#### # vi /etc/defalut/grub
```
GRUB_CMDLINE_LINUX="amd_iommu=on iommu=pt pcie_aspm=off acpi_enforce_resources=lax thermal.off=1 video=1920x1080"
amd_iommu : iommuの有効化
iommu     : PCIパススルーのパフォーマンス改善
pcie_aspm : PCI-Expressの省電力機能の無効化

GRUB_GFXMODE="1920x1080-24"
# update-grub2
```

###2. initramfs設定
#### # vi /etc/modules-load.d/modules.conf
```
vfio
vfio_iommu_type1
vfio_virqfd
options kvm_amd avic=1
```

###3. カーネルモジュールの設定
####3.1 KVMモジュールの設定
#### # vi /etc/modprobe.d/kvm.conf
```
options kvm ignore_msrs=1
options kvm allow_unsafe_assigned_interrupts=1
options kvm report_ignored_msrs=0
```
####3.2 QEMUモジュールの設定
#### # vi /etc/modprobe.d/qemu-system-x86.conf
```
options kvm_intel nested=1
options kvm_amd avic=1
```
####3.3 VFIOモジュールの設定
#### # vi /etc/modprobe.d/vfio-pci.conf
```
options vfio-pci disable_vga=1
```
#### # vi /etc/modprobe.d/vfio_iommu_type1.conf
```
options vfio_iommu_type1 allow_unsafe_interrupts=1
```
####3.4 AMD GPUドライバー設定
#### # vi /etc/modprobe.d/amd.conf
```
blacklist radeon
options amdgpu pcie_gen_cap=0x2
```
オプションはシングルGPU構成の時に、ドライバー再ロード後にVMが起動出来なくなるのを防ぐ設定。
ただし、ドライバーのアンロード、ロードをパススルーを挟まずに行うとOSがクラッシュする。
###4. initramfsの再作成
#### # update-initramfs -k all -u
###5. libvirtdの設定
####5.1 QEMU HOOKスクリプトの作成(シングルGPU用、ホストとゲストでGPUを分けるなら不要）
#### # vi /etc/libvirt/hooks/qemu
```
#!/bin/bash

SHELL_PATH=/etc/libvirt/hooks

NAME=${1}
OPERATION=${2}
SUB_OPERATION=${3}
SCRIPT=${SHELL_PATH}/qemu_${NAME}.sh
LOG_FILE=/tmp/qemu.log

echo `date "+%Y-%m-%d %H:%M:%S.%N"` $* >> ${LOG_FILE}

if [ -x ${SCRIPT} ] ;then
        echo `date "+%Y-%m-%d %H:%M:%S.%N"` $SCRIPT ${OPERATION} ${SUB_OPERATION} >> ${LOG_FILE}
        $SCRIPT ${OPERATION} ${SUB_OPERATION}
fi
```
#### # vi /etc/libvirt/hooks/qemu_VM名.sh
```
#!/bin/bash
MODULE_FILE=/var/tmp/gpu_module

OPERATION=${1}
SUB_OPERATION=${2}
SCRIPT=$0
MAX_WAIT_COUNT=5
LOG_FILE=/tmp/qemu.log

case ${OPERATION} in
    "prepare")
        case ${SUB_OPERATION} in
            "begin")
                # display-manager.service Stop
                #systemctl isolate multi-user.target
                systemctl stop display-manager.service
                # kill lxqt-panel(In lightdm, lxqt environment, process may remain)
                killall lxqt-panel
                # vtconsole Unbind
                echo 0 > /sys/class/vtconsole/vtcon1/bind
                # VGA Device List Up
                DEVICE_ID=`lspci -nn | grep VGA | awk '{print $1}'`
                # Boot VGA Check
                for device in `echo ${DEVICE_ID}`; do
                    # Boot VGA Flag Check
                    if [ "`cat /sys/bus/pci/devices/0000:${device}/boot_vga`" = "1" ]; then
                        # PCI Vender ID Get
                        VENDER_ID=`cat /sys/bus/pci/devices/0000:${device}/vendor`
                        # Use Kernel Driver Name Get
                        DRIVER_NAME=`lspci -k -s 0000:${device} | grep "Kernel driver in use" | awk '{print $5}'`
                    fi
                done
                # Kernel Module List Up
                lsmod | grep ^${DRIVER_NAME} | awk '{print $1}' > ${MODULE_FILE}
                # Kernel GPU Module Check
                if [ -s ${MODULE_FILE} ]; then
                    # AMD GPU Driver Clashes
                    # Runs only with NVIDIA Driver
                    if [ "${DRIVER_NAME}" = "nvidia" ]; then
                        # Unbind EFI-Framebuffer
                        echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind
                    fi
                    # Failure to sleep results in an error
                    sleep 2
                    # Unload All GPU drivers
                    for module in `cat ${MODULE_FILE}`; do
                        # Unload Kernel Module
                        modprobe -s -r ${module}
                        # Return Code Setting
                        rc=$?
                        # Running Log Output
                        echo "${SCRIPT} modprobe -r rc=${rc}" >> $LOG_FILE
                    done
                fi
                exit $rc
            ;;
        esac
    ;;
    "release")
        case ${SUB_OPERATION} in
            "end")
                # GPU Kernel Module Check
                if [ -s ${MODULE_FILE} ]; then
                    # Load All GPU drivers
                    for module in `cat ${MODULE_FILE}`; do
                        # Load Kernel Module
                        modprobe ${module}
                    done
                    # GPU Kernel Module List File Remove
                    # rm -f ${MODULE_FILE}
                    # Sleep for the time being
                    sleep 1
                    # efi-framebuffer.0 File not exist check
                    if [ ! -e /sys/bus/platform/drivers/efi-framebuffer/efi-framebuffer.0 ]; then
                        # Bind EFI-Framebuffer
                        echo "efi-framebuffer.0" > /sys/bus/platform/drivers/efi-framebuffer/bind
                    fi
                fi
                # display-manager.service Start
                #systemctl isolate graphical.target
                systemctl start display-manager.service
            ;;
        esac
    ;;
esac
```
####5.2 VMの作成
####5.2.1 virt-managerで新規VMを作成
 ・GPUを追加
 ・USB(キーボード、マウス）のPCIデバイスを追加
 ・ディスプレイ Spiceを削除
 ・チャンネル qemu-gaを削除
 ・チャンネル spiceを削除
 ・ビデオQXLを削除

####5.2.2 VMのxmlファイル編集
#### # vi /etc/libvirt/qemu/VM名.xml
####5.2.2.1 schema拡張
```
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
```
####5.2.2.2 VIDEO BIOSファイル設定
※VIDEO BIOSファイルはTechPowerUpサイトからGPUに合ったファイルをダウンロードするかGPU-Zで吸い出す。
　NVIDIAのBIOSは先頭から0x600バイト削除する。("55 AA 7B"の前を削除）

    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
        <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
      </source>
      <rom bar='on' file='/etc/libvirt/qemu/vbios/vbios.rom'/>
      <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
    </hostdev>

####5.2.2.3 QEMU VGAの無効化
```
  <qemu:commandline>
    <qemu:arg value='-vga'/>
    <qemu:arg value='none'/>
    <qemu:arg value='-nographic'/>
  </qemu:commandline>
```
####5.2.2.4 NVIDIAのCode43対応
```
  <features>
    <hyperv>
      <vendor_id state='on' value='123456789ab'/>
    </hyperv>
    <kvm>
      <hidden state='on'/>
    </kvm>
  </features>
```
####5.2.2.5 QEMU4.0のNVIDIAのバグを回避
```
  <features>
    <ioapic driver='kvm'/>
  </features>
```
####5.2.2.6 RYZENのCPU機能の有効化
```
  <cpu mode='host-passthrough' check='none'>
    <feature policy='require' name='topoext'/>
    <feature policy='require' name='invtsc'/>
  </cpu>
```
###5.2.2.7 CPU,iothreadを固定することで映像と音声のずれを防ぐ設定。
```
  <iothreads>1</iothreads>
  <cputune>
    <vcpupin vcpu='0' cpuset='2'/>
    <vcpupin vcpu='1' cpuset='8'/>
    <vcpupin vcpu='2' cpuset='3'/>
    <vcpupin vcpu='3' cpuset='9'/>
    <vcpupin vcpu='4' cpuset='4'/>
    <vcpupin vcpu='5' cpuset='10'/>
    <emulatorpin cpuset='5'/>
    <iothreadpin iothread='1' cpuset='11'/>
  </cputune>
```
####5.2.3 libvirtdの再起動
#### # systemctl restart libvirtd