├── README.md ├── hooks-igpupt.pl ├── vfio-startup.sh ├── vfio-teardown.sh ├── vm-start.sh └── vm-stop.sh /README.md: -------------------------------------------------------------------------------- 1 | # pvevm-hooks 2 | 3 | #### 介绍 4 | 5 | PVE下KVM虚拟机直通钩子脚本
6 | 本项目可以让PVE虚拟机直通的核显、声卡、USB控制器,在虚拟机关闭后返回PVE宿主机
7 | 实现效果和详细操作说明请查看:
8 | B站视频:https://www.bilibili.com/video/BV1oT41137CU
9 | 博客文章:https://zhing.fun/pve_igpupt/
10 | 11 | 12 | #### 使用说明 13 | 14 | 克隆本仓库至/root目录
15 | ``` 16 | git clone https://github.com/HelloZhing/pvevm-hooks.git 17 | ``` 18 | 添加可执行权限
19 | ``` 20 | cd pvevm-hooks 21 | chmod a+x *.sh *.pl 22 | ``` 23 | 脚本中默认没有启用USB直通返回,如需启用,请取消vm-stop.sh中“echo $usb_addr...”两行注释。
24 | 复制perl脚本至snippets目录
25 | ``` 26 | mkdir /var/lib/vz/snippets 27 | cp hooks-igpupt.pl /var/lib/vz/snippets/hooks-igpupt.pl 28 | ``` 29 | 将钩子脚本应用至虚拟机
30 | ``` 31 | qm set --hookscript local:snippets/hooks-igpupt.pl 32 | ``` 33 |
34 | 如果PVE安装了图形界面
35 | 请取消vm-start.sh中$(dirname $0)/vfio-startup.sh该行注释
36 | 取消vm-stop.sh中$(dirname $0)/vfio-teardown.sh该行注释 37 | 38 | 39 | #### 感谢 40 | @ledisthebest
41 | 提供的脚本vfio-startup.sh和vfio-teardown.sh
42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /hooks-igpupt.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Exmple hook script for PVE guests (hookscript config option) 4 | # You can set this via pct/qm with 5 | # pct set -hookscript 6 | # qm set -hookscript 7 | # where has to be an executable file in the snippets folder 8 | # of any storage with directories e.g.: 9 | # qm set 100 -hookscript local:snippets/hookscript.pl 10 | 11 | use strict; 12 | use warnings; 13 | use POSIX; 14 | 15 | print "GUEST HOOK: " . join(' ', @ARGV). "\n"; 16 | 17 | # First argument is the vmid 18 | 19 | my $vmid = shift; 20 | 21 | # Second argument is the phase 22 | 23 | my $phase = shift; 24 | 25 | if ($phase eq 'pre-start') { 26 | 27 | # First phase 'pre-start' will be executed before the guest 28 | # is started. Exiting with a code != 0 will abort the start 29 | 30 | system ("/root/pvevm-hooks/vm-start.sh $vmid 2>&1"); 31 | print "$vmid is starting, doing preparations.\n"; 32 | 33 | my $pid = fork(); 34 | if ($pid != 0) { 35 | POSIX::_exit 0; 36 | } else { 37 | close STDOUT; 38 | close STDERR; 39 | close STDIN; 40 | POSIX::setsid(); 41 | exec ("/root/pvevm-hooks/vm-stop.sh $vmid"); 42 | } 43 | 44 | # print "preparations failed, aborting." 45 | # exit(1); 46 | 47 | } elsif ($phase eq 'post-start') { 48 | 49 | # Second phase 'post-start' will be executed after the guest 50 | # successfully started. 51 | 52 | print "$vmid started successfully.\n"; 53 | 54 | } elsif ($phase eq 'pre-stop') { 55 | 56 | # Third phase 'pre-stop' will be executed before stopping the guest 57 | # via the API. Will not be executed if the guest is stopped from 58 | # within e.g., with a 'poweroff' 59 | 60 | print "$vmid will be stopped.\n"; 61 | 62 | } elsif ($phase eq 'post-stop') { 63 | 64 | # Last phase 'post-stop' will be executed after the guest stopped. 65 | # This should even be executed in case the guest crashes or stopped 66 | # unexpectedly. 67 | 68 | print "$vmid stopped. Doing cleanup.\n"; 69 | 70 | } else { 71 | die "got unknown phase '$phase'\n"; 72 | } 73 | 74 | system ("echo $phase >> /root/pvevm-hooks/$vmid-hooks.log"); 75 | 76 | exit(0); 77 | -------------------------------------------------------------------------------- /vfio-startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Helpful to read output when debugging 3 | # set -x 4 | 5 | long_delay=10 6 | medium_delay=5 7 | short_delay=1 8 | echo "Beginning of startup!" 9 | 10 | function stop_display_manager_if_running { 11 | # Stop dm using systemd 12 | if command -v systemctl >/dev/null 2; then 13 | if systemctl is-active --quiet "$1.service" ; then 14 | echo $1 >> /tmp/vfio-store-display-manager 15 | systemctl stop "$1.service" 16 | fi 17 | 18 | while systemctl is-active --quiet "$1.service" ; do 19 | sleep "${medium_delay}" 20 | done 21 | 22 | return 23 | fi 24 | 25 | # Stop dm using runit 26 | if command -v sv; then 27 | if sv status $1 ; then 28 | echo $1 >> /tmp/vfio-store-display-manager 29 | sv stop $1 30 | fi 31 | fi 32 | } 33 | 34 | 35 | # Stop currently running display manager 36 | if test -e "/tmp/vfio-store-display-manager" ; then 37 | rm -f /tmp/vfio-store-display-manager 38 | fi 39 | 40 | stop_display_manager_if_running sddm 41 | stop_display_manager_if_running gdm 42 | stop_display_manager_if_running lightdm 43 | stop_display_manager_if_running lxdm 44 | stop_display_manager_if_running xdm 45 | stop_display_manager_if_running mdm 46 | stop_display_manager_if_running display-manager 47 | 48 | sleep "${medium_delay}" 49 | 50 | # Unbind VTconsoles if currently bound (adapted from https://www.kernel.org/doc/Documentation/fb/fbcon.txt) 51 | if test -e "/tmp/vfio-bound-consoles" ; then 52 | rm -f /tmp/vfio-bound-consoles 53 | fi 54 | for (( i = 0; i < 16; i++)) 55 | do 56 | if test -x /sys/class/vtconsole/vtcon${i}; then 57 | if [ `cat /sys/class/vtconsole/vtcon${i}/name | grep -c "frame buffer"` \ 58 | = 1 ]; then 59 | echo 0 > /sys/class/vtconsole/vtcon${i}/bind 60 | echo "Unbinding console ${i}" 61 | echo $i >> /tmp/vfio-bound-consoles 62 | fi 63 | fi 64 | done 65 | 66 | # Unbind EFI-Framebuffer 67 | if test -e "/tmp/vfio-is-nvidia" ; then 68 | rm -f /tmp/vfio-is-nvidia 69 | fi 70 | 71 | if lsmod | grep "nvidia" &> /dev/null ; then 72 | echo "true" >> /tmp/vfio-is-nvidia 73 | echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind 74 | fi 75 | 76 | echo "End of startup!" -------------------------------------------------------------------------------- /vfio-teardown.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -x 3 | 4 | echo "Beginning of teardown!" 5 | 6 | sleep 10 7 | 8 | # Restart Display Manager 9 | input="/tmp/vfio-store-display-manager" 10 | while read displayManager; do 11 | if command -v systemctl; then 12 | systemctl start "$displayManager.service" 13 | else 14 | if command -v sv; then 15 | sv start $displayManager 16 | fi 17 | fi 18 | done < "$input" 19 | 20 | # Rebind VT consoles (adapted from https://www.kernel.org/doc/Documentation/fb/fbcon.txt) 21 | input="/tmp/vfio-bound-consoles" 22 | while read consoleNumber; do 23 | if test -x /sys/class/vtconsole/vtcon${consoleNumber}; then 24 | if [ `cat /sys/class/vtconsole/vtcon${consoleNumber}/name | grep -c "frame buffer"` \ 25 | = 1 ]; then 26 | echo "Rebinding console ${consoleNumber}" 27 | echo 1 > /sys/class/vtconsole/vtcon${consoleNumber}/bind 28 | fi 29 | fi 30 | done < "$input" 31 | 32 | # Rebind framebuffer for nvidia 33 | if test -e "/tmp/vfio-is-nvidia" ; then 34 | echo "efi-framebuffer.0" > /sys/bus/platform/drivers/efi-framebuffer/bind 35 | fi 36 | 37 | echo "End of teardown!" -------------------------------------------------------------------------------- /vm-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VMID="$1" 4 | igd_id="8086 $(lspci -n|grep '0:02.0'|cut -d ':' -f4|cut -c 1-4)" 5 | 6 | echo "VM $VMID is starting" > $(dirname $0)/$VMID-hooks.log 7 | 8 | #$(dirname $0)/vfio-startup.sh 9 | 10 | sleep 1 11 | 12 | echo 0000:00:02.0 > /sys/bus/pci/drivers/i915/unbind 13 | if ! lsmod | grep "vfio_pci" &> /dev/null ; then 14 | modprobe vfio-pci 15 | fi 16 | echo $igd_id > /sys/bus/pci/drivers/vfio-pci/new_id 17 | -------------------------------------------------------------------------------- /vm-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VMID="$1" 4 | ia_addr="0000:$(lspci|grep 'Audio'|grep 'Intel'|cut -c 1-7)" 5 | usb_addr="0000:$(lspci|grep 'USB'|grep 'Intel'|cut -c 1-7)" 6 | igd_id="8086 $(lspci -n|grep '0:02.0'|cut -d ':' -f4|cut -c 1-4)" 7 | 8 | echo "waitting" >> $(dirname $0)/$VMID-hooks.log 9 | 10 | sleep 10 11 | 12 | TimeSec=0 13 | until ! test -e "/var/run/qemu-server/$VMID.pid" 14 | do 15 | if [ $[$TimeSec%3600] -eq 0 ]; then 16 | echo "VM $VMID is running "$(date "+%Y-%m-%d %H:%M:%S") >> $(dirname $0)/$VMID-hooks.log 17 | fi 18 | sleep 3 19 | let TimeSec+=3 20 | done 21 | 22 | echo 0000:00:02.0 > /sys/bus/pci/drivers/vfio-pci/unbind 23 | echo $ia_addr > /sys/bus/pci/drivers/vfio-pci/unbind 24 | #echo $usb_addr > /sys/bus/pci/drivers/vfio-pci/unbind 25 | echo $igd_id > /sys/bus/pci/drivers/vfio-pci/remove_id 26 | echo 0000:00:02.0 > /sys/bus/pci/drivers/i915/bind 27 | echo $ia_addr > /sys/bus/pci/drivers/snd_hda_intel/bind 28 | #echo $usb_addr >/sys/bus/pci/drivers/xhci_hcd/bind 29 | 30 | sleep 1 31 | 32 | #$(dirname $0)/vfio-teardown.sh 33 | 34 | echo "VM $VMID stopped "$(date "+%Y-%m-%d %H:%M:%S") >> $(dirname $0)/$VMID-hooks.log 35 | --------------------------------------------------------------------------------