├── 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 |
--------------------------------------------------------------------------------