QEMU源码全解析 —— virtio(19)

接前一篇文章:

上回书继续讲解virtio_pci_driver的probe回调函数virtio_pci_probe(),在讲到第5段代码的时候,

    if (force_legacy) {
		rc = virtio_pci_legacy_probe(vp_dev);
		/* Also try modern mode if we can't map BAR0 (no IO space). */
		if (rc == -ENODEV || rc == -ENOMEM)
			rc = virtio_pci_modern_probe(vp_dev);
		if (rc)
			goto err_probe;
	} else {
		rc = virtio_pci_modern_probe(vp_dev);
		if (rc == -ENODEV)
			rc = virtio_pci_legacy_probe(vp_dev);
		if (rc)
			goto err_probe;
	}

引出来两个函数:virtio_pci_legacy_probe和virtio_pci_modern_probe。本回就来对它们进行解析。当然,由于legacy已成过去,因此重点围绕virtio_pci_modern_probe函数进行解析,捎带手地也讲一下virtio_pci_legacy_probe()。为了便于理解,再次贴出两个函数的源码:

  • virtio_pci_legacy_probe

virtio_pci_legacy_probe函数在Linux内核源码/drivers/virtio/virtio_pci_legacy.c中,代码如下:

/* the PCI probing function */
int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev)
{
	struct virtio_pci_legacy_device *ldev = &vp_dev->ldev;
	struct pci_dev *pci_dev = vp_dev->pci_dev;
	int rc;
 
	ldev->pci_dev = pci_dev;
 
	rc = vp_legacy_probe(ldev);
	if (rc)
		return rc;
 
	vp_dev->isr = ldev->isr;
	vp_dev->vdev.id = ldev->id;
 
	vp_dev->vdev.config = &virtio_pci_config_ops;
 
	vp_dev->config_vector = vp_config_vector;
	vp_dev->setup_vq = setup_vq;
	vp_dev->del_vq = del_vq;
 
	return 0;
}
  • virtio_pci_modern_probe

virtio_pci_modern_probe函数在Linux内核源码/drivers/virtio/virtio_pci_modern.c中,代码如下:

/* the PCI probing function */
int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
{
	struct virtio_pci_modern_device *mdev = &vp_dev->mdev;
	struct pci_dev *pci_dev = vp_dev->pci_dev;
	int err;
 
	mdev->pci_dev = pci_dev;
 
	err = vp_modern_probe(mdev);
	if (err)
		return err;
 
	if (mdev->device)
		vp_dev->vdev.config = &virtio_pci_config_ops;
	else
		vp_dev->vdev.config = &virtio_pci_config_nodev_ops;
 
	vp_dev->config_vector = vp_config_vector;
	vp_dev->setup_vq = setup_vq;
	vp_dev->del_vq = del_vq;
	vp_dev->isr = mdev->isr;
	vp_dev->vdev.id = mdev->id;
 
	return 0;
}

virtio_pci_modern_probe函数中最主要的是调用了vp_modern_probe函数,其在Linux内核源码/drivers/virtio/virtio_pci_modern_dev.c中,代码如下:

/*
 * vp_modern_probe: probe the modern virtio pci device, note that the
 * caller is required to enable PCI device before calling this function.
 * @mdev: the modern virtio-pci device
 *
 * Return 0 on succeed otherwise fail
 */
int vp_modern_probe(struct virtio_pci_modern_device *mdev)
{
	struct pci_dev *pci_dev = mdev->pci_dev;
	int err, common, isr, notify, device;
	u32 notify_length;
	u32 notify_offset;

	check_offsets();

	/* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */
	if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)
		return -ENODEV;

	if (pci_dev->device < 0x1040) {
		/* Transitional devices: use the PCI subsystem device id as
		 * virtio device id, same as legacy driver always did.
		 */
		mdev->id.device = pci_dev->subsystem_device;
	} else {
		/* Modern devices: simply use PCI device id, but start from 0x1040. */
		mdev->id.device = pci_dev->device - 0x1040;
	}
	mdev->id.vendor = pci_dev->subsystem_vendor;

	/* check for a common config: if not, use legacy mode (bar 0). */
	common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);
	if (!common) {
		dev_info(&pci_dev->dev,
			 "virtio_pci: leaving for legacy driver\n");
		return -ENODEV;
	}

	/* If common is there, these should be too... */
	isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG,
					 IORESOURCE_IO | IORESOURCE_MEM,
					 &mdev->modern_bars);
	notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);
	if (!isr || !notify) {
		dev_err(&pci_dev->dev,
			"virtio_pci: missing capabilities %i/%i/%i\n",
			common, isr, notify);
		return -EINVAL;
	}

	err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));
	if (err)
		err = dma_set_mask_and_coherent(&pci_dev->dev,
						DMA_BIT_MASK(32));
	if (err)
		dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA.  Trying to continue, but this might not work.\n");

	/* Device capability is only mandatory for devices that have
	 * device-specific configuration.
	 */
	device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);

	err = pci_request_selected_regions(pci_dev, mdev->modern_bars,
					   "virtio-pci-modern");
	if (err)
		return err;

	err = -EINVAL;
	mdev->common = vp_modern_map_capability(mdev, common,
				      sizeof(struct virtio_pci_common_cfg), 4,
				      0, sizeof(struct virtio_pci_common_cfg),
				      NULL, NULL);
	if (!mdev->common)
		goto err_map_common;
	mdev->isr = vp_modern_map_capability(mdev, isr, sizeof(u8), 1,
					     0, 1,
					     NULL, NULL);
	if (!mdev->isr)
		goto err_map_isr;

	/* Read notify_off_multiplier from config space. */
	pci_read_config_dword(pci_dev,
			      notify + offsetof(struct virtio_pci_notify_cap,
						notify_off_multiplier),
			      &mdev->notify_offset_multiplier);
	/* Read notify length and offset from config space. */
	pci_read_config_dword(pci_dev,
			      notify + offsetof(struct virtio_pci_notify_cap,
						cap.length),
			      &notify_length);

	pci_read_config_dword(pci_dev,
			      notify + offsetof(struct virtio_pci_notify_cap,
						cap.offset),
			      &notify_offset);

	/* We don't know how many VQs we'll map, ahead of the time.
	 * If notify length is small, map it all now.
	 * Otherwise, map each VQ individually later.
	 */
	if ((u64)notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) {
		mdev->notify_base = vp_modern_map_capability(mdev, notify,
							     2, 2,
							     0, notify_length,
							     &mdev->notify_len,
							     &mdev->notify_pa);
		if (!mdev->notify_base)
			goto err_map_notify;
	} else {
		mdev->notify_map_cap = notify;
	}

	/* Again, we don't know how much we should map, but PAGE_SIZE
	 * is more than enough for all existing devices.
	 */
	if (device) {
		mdev->device = vp_modern_map_capability(mdev, device, 0, 4,
							0, PAGE_SIZE,
							&mdev->device_len,
							NULL);
		if (!mdev->device)
			goto err_map_device;
	}

	return 0;

err_map_device:
	if (mdev->notify_base)
		pci_iounmap(pci_dev, mdev->notify_base);
err_map_notify:
	pci_iounmap(pci_dev, mdev->isr);
err_map_isr:
	pci_iounmap(pci_dev, mdev->common);
err_map_common:
	pci_release_selected_regions(pci_dev, mdev->modern_bars);
	return err;
}
EXPORT_SYMBOL_GPL(vp_modern_probe);

实际上在老版本KVM即Linux内核代码中,vp_modern_probe函数中的内容绝大多数是直接放在virtio_pci_modern_probe函数中的,后来才单独封了这样一个函数。

(1)vp_modern_probe首先设置了virtio设备的verdor ID和device ID。代码片段如下:

    /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */
	if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)
		return -ENODEV;

	if (pci_dev->device < 0x1040) {
		/* Transitional devices: use the PCI subsystem device id as
		 * virtio device id, same as legacy driver always did.
		 */
		mdev->id.device = pci_dev->subsystem_device;
	} else {
		/* Modern devices: simply use PCI device id, but start from 0x1040. */
		mdev->id.device = pci_dev->device - 0x1040;
	}
	mdev->id.vendor = pci_dev->subsystem_vendor;

值得注意的是,virtio PCI代理设备的device iD就是前文书(参见QEMU源码全解析 —— virtio(14))在讲virtio_pci_device_plugged函数(QEMU源码中)时设置的PCI_DEVICE_ID_VIRTIO_10_BASE+VIRTIO_ID_BALLOON,即0x1040+5。

所以,这里virtio设备的device ID(mdev->id.device)就是0x1040+5-0x1040=5,也就代表了VIRTIO_ID_BALLOON。

(2)接下来,调用多次virtio_pci_find_capability函数来发现virtio PCI代理设备的pci capability。代码片段如下:

/* check for a common config: if not, use legacy mode (bar 0). */
	common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);
	if (!common) {
		dev_info(&pci_dev->dev,
			 "virtio_pci: leaving for legacy driver\n");
		return -ENODEV;
	}

	/* If common is there, these should be too... */
	isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG,
					 IORESOURCE_IO | IORESOURCE_MEM,
					 &mdev->modern_bars);
	notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);
	if (!isr || !notify) {
		dev_err(&pci_dev->dev,
			"virtio_pci: missing capabilities %i/%i/%i\n",
			common, isr, notify);
		return -EINVAL;
	}

	err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64));
	if (err)
		err = dma_set_mask_and_coherent(&pci_dev->dev,
						DMA_BIT_MASK(32));
	if (err)
		dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA.  Trying to continue, but this might not work.\n");

	/* Device capability is only mandatory for devices that have
	 * device-specific configuration.
	 */
	device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);

这也是在(QEMU源码)virtio_pci_device_plugged函数中写入到virtio PCI代理设备的配置空间中的,参见QEMU源码全解析 —— virtio(14)和QEMU源码全解析 —— virtio(15)。

(3)virtio_pci_find_capability函数找到所属的PCI BAR,然后写入到virt_pci_device的modern_bars成员中。代码片段如下:

    /* Device capability is only mandatory for devices that have
	 * device-specific configuration.
	 */
	device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG,
					    IORESOURCE_IO | IORESOURCE_MEM,
					    &mdev->modern_bars);

从(QEMU源码)virtio_pci_realize函数中可以知道这个modern_bars是1<<4,如下图所示:

(4)接着,pci_request_selected_regions函数就将virtio PCI代理设备的BAR地址空间保留出来了。代码片段如下:

    err = pci_request_selected_regions(pci_dev, mdev->modern_bars,
					   "virtio-pci-modern");
	if (err)
		return err;

(5)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/258856.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java如何开发PC客户端(Windows,Mac,Linux)

项目编译工具&#xff1a;Gradle开发工具&#xff1a; Idea开发语言&#xff1a; 建议java17以上ui组件&#xff1a;openjfx (org.openjfx.javafxplugin)打包工具: jpackage (org.beryx.jlink) 一、如何解决打包问题 java 14以后&#xff0c;有了jpackage工具&#xff0c;能够…

【Axure高保真原型】中继器表格——移入显示详情卡片

今天和大家分享中继器表格——移入显示详情卡片的原型模板&#xff0c;鼠标移入员工姓名&#xff0c;会显示对应员工的详细卡片&#xff0c;那这个原型是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;在中继器表格里维护对应的信息即可。预览时即可生成交互效果&…

Ansible自动化运维以及模块使用

ansible的作用 远程操作主机功能 自动化运维(playbook剧本基于yaml格式书写) ansible是基于python开发的配置管理和应用部署工具。在自动化运维中&#xff0c;现在是异军突起 ansible能够批量配置、部署、管理上千台主机。类似于Xshell的一键输入工具。不需要每次都切换主机…

ITIL® 4 Foundation​,12月23日即将开课~想了解点击查看

ITIL 4 Foundation即将开课~ 想报名的必须提前预约啦 &#x1f447;&#x1f447;&#x1f447; 培训地点&#xff1a; 远程直播&#xff1a;线上平台学习 开课时间&#xff1a; 周末班&#xff1a;12月23日、24日&#xff1b; 什么是ITIL&#xff1f; 信息技术基础架构…

【史上最易懂】变分推断:从【求分布】的推断问题,变成【缩小距离】的优化问题,用简单的分布 q 去近似复杂的分布 p

变分推断&#xff1a;从求分布的推断问题&#xff0c;变成缩小距离的优化问题 频率学派与贝叶斯学派变分推断完整推导 频率学派与贝叶斯学派 学过概率论&#xff0c;应该了解过&#xff0c;概率分为 2 个学派&#xff1a; 频率学派&#xff1a;数据是客观的&#xff08;看到啥…

【ArcGIS微课1000例】0081:ArcGIS指北针乱码解决方案

问题描述&#xff1a; ArcGIS软件在作图模式下插入指北针&#xff0c;出现指北针乱码&#xff0c;如下图所示&#xff1a; 问题解决 下载并安装字体&#xff08;配套实验数据包0081.rar中获取&#xff09;即可解决该问题。 正常的指北针选择器&#xff1a; 专栏介绍&#xff…

专攻代码型闪存芯片赛道,芯天下授权世强硬创代理全线产品

近年来受下游应用需求增长的驱动&#xff0c;代码型闪存芯片市场空间持续扩张&#xff0c;在后疫情之下NOR Flash及SLC NAND Flash市场规模整体仍保持逐步增长的趋势。 为了迎合市场需求&#xff0c;世强先进&#xff08;深圳&#xff09;科技股份有限公司&#xff08;下称“世…

青少年CTF-qsnctf-Web-PingMe02

题目环境&#xff1a; 题目难度&#xff1a;★ 题目描述&#xff1a;诶&#xff1f;又是一道Ping题目诶&#xff01; 给了一个ip参数 传参&#xff1a; ?ip1.1.1.1 有回显结果 使用英文分号";"进行连接后续命令 列出此路径下的目录和文件 ?ip1.1.1.1;ls 列出根目录…

drf知识--01

前后端开发模式 在开发Web应用中&#xff0c;有两种应用模式&#xff1a; 前后端混合开发: bbs 项目--renderajax 1、全栈开发--前端html后端都是一个人写 2、前端人员&#xff1a;写空页面&#xff0c;没有模板语法&#xff0c;只要html&#xff0c;c…

在区块链中看CHAT的独特见解

问CHAT&#xff1a;谈谈对区块链以及区块链金融的理解 CHAT回复&#xff1a;区块链是一种去中心化的分布式数据库技术&#xff0c;这种技术通过加密算法&#xff0c;使数据在网络中传输和存储的过程变得更加安全可靠。区块链的出现引领了存储、交易等形式的革命&#xff0c;改变…

航空港务数据大屏为航空港的可持续发展提供有力支撑!

随着经济的发展&#xff0c;不断加建与扩建民用机场&#xff0c;空港行业规模不断扩大。在不断引进和消化发达国家先进技术的同时&#xff0c;中国深入开展了对新技术和新材料的研究&#xff0c;极大地丰富和发展了中国的机场建设技术。且各项机场建设计划均已落实推进&#xf…

hadoop格式化报错

在var/bigdata/hadoop/ha/dfs/jn/下没有mycluster目录增加后就格式化成功了

Flask ImportError: DLL load failed: 找不到指定的模块。

一、anaconda环境 将anaconda3安装路径下DDL目录中的 libcrypto-1_1-x64.dll 和 libssl-1_1-x64.dll 拷贝到 虚拟环境目录下的DLL中 完美解决 成功了给个赞吧&#xff01;

【IDEA】Intellij IDEA相关配置

IDEA 全称 IntelliJ IDEA&#xff0c;是java编程语言的集成开发环境。IntelliJ在业界被公认为最好的Java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超…

真VS假(数字化前提)

元宇宙最近消停了很多&#xff0c;起始这个词刚出来的时候&#xff0c;很多人搞不清楚&#xff0c;元宇宙就是看待真假一个典范。如果对真假有个清晰的认知&#xff0c;那么对于看待很多事情&#xff0c;会给您带来不一样的视角&#xff0c;不仅仅是对企业数字化&#xff0c;可…

Spring MVC框架支持RESTful,设计URL时可以使用{自定义名称}的占位符@Get(“/{id:[0-9]+}/delete“)

背景&#xff1a;在开发实践中&#xff0c;如果没有明确的规定URL&#xff0c;可以参考&#xff1a; 传统接口 获取数据列表,固定接口路径&#xff1a;/数据类型的复数 例如&#xff1a;/albums/select RESTful接口 - 根据ID获取某条数据&#xff1a;/数据类型的复数/{id} - 例…

3D小球跑酷

目录 一、前言 二、开发环境 三、场景搭建 1. 创建项目 2. 创建场景内物体 2.1 创建跑道 2.2 创建玩家 2.3 创建障碍物 2.4 改变跑道和障碍物的颜色 2.4.1 创建材质 2.4.2 给跑道和障碍物更换材质 四、功能脚本实现 1. 创建玩家脚本 2. 相机跟随 3. 胜负的判定 3…

DeepLabV3+实现sar影像海面溢油区识别

今天我们分享DeepLabV3的sai影像水体提取。 数据集 本次使用的数据集是Deep-SAR Oil Spill (SOS) dataset。该数据集由中国地质大学的朱祺琪团队制作并共享。该数据集包含墨西哥湾溢油区域和波斯湾溢油区域&#xff0c;分别获取于ALOS 和Sentinel-1A卫星。由ECHO研究组搜集制作…

phpmyadmin4.8.1远程文件包含漏洞 [GWCTF 2019]我有一个数据库1

打开题目 我们用dirsearch扫描一下后台看看 扫描结果如下 我们访问一下robots.txt看看&#xff0c;提示有phpinfo.php 那我们访问一下phpinfo.php 发现没有任何信息后我们转去看看phpmyadmin看看 成功访问到页面 在这里我们看到phpmyadmin的版本号是4.8.1 我们百度搜索一下看…

ABAP与HANA集成 2:ABAP调用HANA存储过程或SQL语句

作者 idan lian 如需转载备注出处 需求 虽然是做BW模块&#xff0c;但是最近项目上种种&#xff0c;都需要给ABAP人员或者前台用户提供能供他们使用的表&#xff0c;就稍微研究了下ABAP和HANA的集成问题&#xff0c;因为我们BW更擅长的还是HANA&#xff0c;而且HANA的运行效…