VGA GPU passthrough qemu虚拟桌面pci穿透

转载请注明:http://blog.csdn.net/hubbybob1/article/details/77101913
上一篇文章已经介绍过显卡穿透的linux内核准备过程了,下面就要讲述穿透的额过程。上篇文章:http://blog.csdn.net/hubbybob1/article/details/73920296
首先,查看cpu是否支持虚拟化:

egrep -o '(vmx|svm)' /proc/cpuinfo 
//如果支持就会显示如下
vmx
或者
svm

其次就是要查看cpu是否支持 GPU 的PIC穿透,其实这个操作对cpu的要求挺高的,要进入BIOS去查看VT-d。确定好这些就可以操作了。

一、linux下的穿透(主要侧重Ubuntu)

1.修改linux的启动项

//通过命令,修改引导文件
vim /etc/default/grub 
//如下内容
#GRUB_HIDDEN_TIMEOUT=0
GRUB_CMDLINE_LINUX_DEFAULT="text"
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt default_hugepagesz=1G hugepagesz=1G hugepages=16 kvm.ignore_msrs=1 intremap=no_x2apic_optout video=efifb:off,vesafb:off"

//使修改的引导文件生效,这个Ubuntu和Centos7下的有所不一样
//ubuntu两个命令
update-grub
update-grub2
//centos7下,很多介绍是/boot/grub2/grub.conf,但是这个并不生效,真正生效的是下面的这个
grep2-mkconfig -o /boot/efi/EFI/centos/grub.conf

添加黑名单

编辑vim /etc/modprobe.d/blacklist.conf 

blacklist i915
blacklist efifb
blacklist radeon

执行完上面的操作时,重启,reboot
2.重启后查看启动项是否修改成功,

cat /proc/cmdline//查看修改的grub
dmesg | grep -e DMAR -e IOMMU//查看DMAR 的映射

两个命令截图如下:这样就是修改成功了
这里写图片描述
3.安装依赖库

//ubuntu下的安装
apt-get install libtool liblog4cpp5-dev libavcodec-dev libssl-dev xlibmesa-glu-dev libasound-dev libpng12-dev libfreetype6-dev libfontconfig1-dev libogg-dev libxrandr-dev kvm libgcrypt-dev libsdl-dev libnss3-dev libpixman-1-dev libxfixes-dev libjpeg8-dev libsasl2-dev python-pyparsing gtk+-3.0
apt-get install liblzo2-dev libsnappy-dev libgtk-3-dev librdmacm-dev libibverbs-dev xfslibs-dev libssh2-1-dev libaio-dev glusterfs-server libiscsi-dev libusb-1.0-0-dev libcap-dev libsdl2-dev
//centos7下的安装
yum install lzo-devel snappy-devel gtk2-devel gtk3-devel librdmacm-devel xfsprogs-devel libssh2-devel libaio-devel glusterfs-api-devel libiscsi-devel spice-server-devel libusb1-devel usbredir-devel libcap-devel libattr-devel SDL-devel

反正就是缺少什么就安装什么
4.安装相关的环境
a。celt-0.5.1.3.tar.gz
下载:http://www.celt-codec.org/downloads/ (最新的没用过,可以尝试一下)

tar zxvf celt-0.5.1.3.tar.gz
cd celt-0.5.1.3/
./configure
make
make install

b。安装libcacard-2.5.2.tar
下载:https://www.spice-space.org/page/Libcacard (这些下载都能通过spice官网下载)

tar xvf libcacard-2.5.2.tar 
cd libcacard-2.5.2/
./configure
make
make install

出现错误:
vscclient.o: undefined reference to symbol ‘pthread_create@@GLIBC_2.2.5’
error adding symbols: DSO missing from command line
修改Makefile 163行
LIBS= -lz -lrt -lm -lpthread
c。安装 spice-protocol-0.12.12.tar.bz2
下载:https://www.spice-space.org/download.html

tar jxvf spice-protocol-0.12.12.tar.bz2
cd spice-protocol-0.12.12/
./configure
make && make install

d。安装spice server spice-0.12.8.tar.bz2
下载:https://www.spice-space.org/download.html

tar jxvf spice-0.12.8.tar.bz2
cd spice-0.12.8/
./configure  --enable-smartcard --disable-xinerama --enable-opengl --enable-usbdir=yes --enable-usbredir=yes
make
make install

e。安装usb重定向sbredir-0.7.tar.bz2
下载:https://www.spice-space.org/download.html

tar jxvf usbredir-0.7.tar.bz2
cd usbredir-0.7/
./configure
make && make install

f。安装spice-gtk
下载:https://www.spice-space.org/download.html
可以手动安装也可以自动安装

//自动安装
apt-get install libspice-client-gtk-3.0-dev
//下载后解压安装,configure参数与spice server一样

g。最关键的安装就是QEMU的安装
下载:https://www.qemu.org/download/ (下载最新版本)

tar Jxvf qemu-2.10.1.tar.xz
cd qemu-2.10.1/

./configure  --target-list=x86_64-softmmu --enable-kvm --enable-gtk --with-gtkabi=3.0 --enable-virtfs --enable-tcg-interpreter --enable-xfsctl --enable-libiscsi --enable-glusterfs --enable-snappy --enable-lzo --enable-rdma --enable-linux-aio --enable-vhost-net --enable-spice --enable-smartcard  --enable-libusb --enable-usb-redir --enable-guest-agent  --enable-libssh2 --enable-sdl --audio-drv-list='oss alsa sdl' 

make && make install

到此相关的依赖库和环境就搭建完毕了。
5。编写穿透脚本
编写脚本vfio-bind文件如下

#!/bin/bash

modprobe vfio-pci

for dev in "$@"; do
        vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
        device=$(cat /sys/bus/pci/devices/$dev/device)
        if [ -e /sys/bus/pci/devices/$dev/driver ]; then
                echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
        fi
        echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done

将脚本命名为vfio-bind 并cp到/usr/bin/

cp vfio-bind /usr/bin/

然后你可以终端输入vfio然后tab键看是否能将此命令补全为vfio-bind
6。在虚拟机启动脚本内即命令qemu-system-x86_64命令下添加如下参数

//先加载模块
modprobe vfio
modprob vfio_pci
//解绑GPU PCI的ID
vfio-bind 0000:00:02.0 //这个命令相当于下面的命令
echo 0000:00:02.0 > /sys/bus/pci/devices/0000:00:02.0/driver/ubind
echo "vfio-pci" > /sys/bus/pci/devices/0000:00:02.0/driver_override
echo 8086 1912 > /sys/bus/pci/drivers/vfio-pci/new_id
//再解绑回来,就是解绑的反过程xhci_hcd,可以通过lspci命令查看,下面会讲到
echo 0000:00:14.0 > /sys/bus/pci/drivers/vfio-pci/unbind
echo0000:00:14.0 > /sys/bus/pci/drivers/xhci_hcd/bind
//qemu参数
-device vfio-pci,host=00:02.0,bus=pci.0,addr=ox2

如何查看VGA的ID呢( 0000:00:02.0)可以查看/sys/bus/pci/devices/下的

ls /sys/bus/pci/devices/ #查看有多少驱动
lspci          #查看每个ID对应的驱动

完成上面的命令就可以查看得知VGA对应的ID为0000:00:02.0,usb控制器对应的是0000:00:14.0,在确定穿透之前,先确定每个设备所在的分组,同一分组的设备必须同时穿透,不然穿透不成功的,如下方法测试,编写脚本group.sh如下:

#!/bin/bash
shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do
	n=${d#*/iommu_groups/*}; n=${n%%/*}
	printf 'IOMMU Group:%s  ' "$n"
	lspci -nns "${d##*/}"
done;

查看后的结果如下:
这里写图片描述
可以查看到VGA在group1内,而且在组内只有一个,但是可以看到在,group 3内有两个设备 usb controller和signal processing controller两个,如果要穿透USB的话必须要同时加上signal processing controller (0000:00:14.2),同样可以看到group 7也是一样的。
因此VGA和USB同时穿透如下:

vfio-bind 0000:00:02.0 0000:00:14.2 0000:00:14.0 #解绑
//qemu-system-x86_64命令参数
-device vfio-pci,host=00:02.0,bus=pci.0,addr=0x2 \
-device vfio-pci,host=00:14.2 \
-device vfio-pci,host=00:14.0 \

解绑前后的对比,使用命令

lspci -nnk 
lspci -vvk #这个命令更详细一点

解绑前
这里写图片描述
解绑后,就不能使用本地的终端查看了,因为本地的usb已经不存在,所以只用通过远程ssh连接来查看。
这里写图片描述
通过对比可以看到,本地解绑后的驱动都变成vfio-pci.到此基本上VGA和USB的穿透也就基本结束了,看着过程很复杂其实很简单。
需要注意的是:
1,在穿透VAG时 的qemu启动命令的执行环境,不能是桌面环境,应为桌面环境VGA正在被占用,是没办法穿透的,必须在字符终端界面或者远程ssh连接终端的情况下才会穿透成功。
2.在穿透USB的情况是,一旦USB解绑了,在本地是无法使用的,而在虚拟机启动的过程中也是无法使用的,因为虚拟机windows下并没有USB的驱动,需要安装,那这就有问题了,一旦解绑了,在ubuntu下无法使用,在windows下也无法使用,这该怎么办,其实可以这样解决,下载安装带有USB3.0驱动的ISO镜像,不用在自己安装或者先穿透USB,不穿透VGA,通过spice远程连接,安装驱动精灵,安装usb驱动,安装好后,再重新启动VGA穿透,两种方法都能成功。
3.在上面的图中我们可以看到声卡的穿透也是可以的,但是要同时穿透4个设备,有的cpu的声卡和网卡是在一个组内,这样就不好穿透声卡了,影响网卡的使用,网卡尽量桥接,本地ubuntu能使用,虚拟机也能使用,如果两个设备在一起,就只能模拟声卡来解决了。(如何模拟声卡我会在另一篇文章中描述)
4.在使用显卡穿透时,一定要注意几个参数的使用,什么意思自己去查qemu文档

-full-screen \
-vga none \
-nodefaults \
-nographic

参考:
https://bbs.archlinux.org/viewtopic.php?id=162768
https://wiki.debian.org/VGAPassthrough

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
QEMU是一款开源的虚拟机软件,它支持模拟多种不同的硬件设备,包括虚拟显示器。在QEMU中,虚拟显示器驱动程序主要由以下两个部分组成: 1. 显示器后端(Display Backends):负责将客户机的图形数据渲染成显存中的像素数据,并将其发送给前端。 2. 显示器前端(Display Frontends):负责将后端发送的像素数据显示出来。 下面是一个简单的QEMU虚拟显示器驱动程序实现: ```c #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/module.h" #include "ui/console.h" #include "ui/console-input.h" #include "ui/pixel_ops.h" #include "hw/display/virtio-gpu.h" #define TYPE_QEMU_DISPLAY "qemu-display" #define QEMU_DISPLAY(obj) \ OBJECT_CHECK(QemuDisplayState, (obj), TYPE_QEMU_DISPLAY) typedef struct QemuDisplayState { VirtIOGPU *virtio_gpu; DisplaySurface *surface; Console *console; uint8_t *fb; int fb_stride; } QemuDisplayState; static void qemu_display_refresh(DisplayChangeListener *dcl, const QRegion *region) { QemuDisplayState *s = QEMU_DISPLAY(dcl); DisplaySurface *surface = s->surface; int pos_x, pos_y; uint8_t *fb = s->fb; int fb_stride = s->fb_stride; int width = surface->width; int height = surface->height; for (pos_y = 0; pos_y < height; pos_y++) { for (pos_x = 0; pos_x < width; pos_x++) { uint8_t *pixel = fb + pos_y * fb_stride + pos_x * 4; uint32_t color = pixel_get(surface, pixel); console_write_graphic(s->console, pos_x, pos_y, color); } } } static void qemu_display_destroy(DisplayChangeListener *dcl) { QemuDisplayState *s = QEMU_DISPLAY(dcl); qemu_free(s->fb); s->fb = NULL; display_surface_unref(s->surface); s->surface = NULL; virtio_gpu_cleanup(s->virtio_gpu); s->virtio_gpu = NULL; } static void qemu_display_init(DisplayChangeListener *dcl, DisplaySurface *surface, int x, int y, int w, int h, int stride) { QemuDisplayState *s = QEMU_DISPLAY(dcl); s->surface = surface; s->console = graphic_console_init(surface->width, surface->height, qemu_get_display_type()); s->fb_stride = surface->width * 4; s->fb = qemu_mallocz(s->fb_stride * surface->height); s->virtio_gpu = virtio_gpu_init(s->fb, s->fb_stride, surface->width, surface->height); } static DisplayChangeListener *qemu_display_create(VirtIOGPU *virtio_gpu) { QemuDisplayState *s = qemu_mallocz(sizeof(QemuDisplayState)); DisplayChangeListener *dcl = display_state_create(s, qemu_display_refresh, qemu_display_destroy, qemu_display_init); s->virtio_gpu = virtio_gpu; return dcl; } static void qemu_display_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); VirtIOGPU *virtio_gpu = VIRTIO_GPU(dev); DisplayChangeListener *dcl = qemu_display_create(virtio_gpu); display_state_set_surface(dcl, virtio_gpu_get_primary(virtio_gpu)); virtio_gpu_set_displaychange_listener(virtio_gpu, dcl); } static void qemu_display_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); VirtIOGPUClass *vgc = VIRTIO_GPU_CLASS(oc); dc->reset = virtio_device_reset; dc->vmsd = &vmstate_virtio_pci_device; vgc->init_display = qemu_display_initfn; } static const TypeInfo qemu_display_info = { .name = TYPE_QEMU_DISPLAY, .parent = TYPE_VIRTIO_GPU, .instance_size = sizeof(QemuDisplayState), .class_init = qemu_display_class_init, }; static void qemu_display_register_types(void) { type_register_static(&qemu_display_info); } type_init(qemu_display_register_types) ``` 这个驱动程序实现了一个简单的QEMU虚拟显示器,它使用VirtIO-GPU协议与客户机进行通信。在初始化函数qemu_display_initfn中,驱动程序会创建一个DisplayChangeListener对象,并将其注册到virtio_gpu中。当客户机发送图形数据时,virtio_gpu会将数据传递给驱动程序的后端,并在显示内容更新时通知前端刷新显示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值