QEMU源码全解析 —— PCI设备模拟(7)

接前一篇文章:

上一回讲解了pci_edu_realize函数中的pci_register_bar函数,本回开始对于edu设备的MMIO读写函数进行解析。

操作系统与PCI设备交互的主要方式是PIO和MMIO。MMIO虽然是一段内存,但是其没有EPT映射,在虚拟机访问设备的MMIO时,会产生VM Exit;KVM识别此MMIO访问并且将该访问分派到应用层QEMU中;QEMU根据内存虚拟化的步骤进行分派,找到设备注册的MMIO读写回调函数;设备的MMIO读写回调函数根据设备的功能进行模拟,完成模拟之后可能会发送中断到虚拟机中,从而完成一些MMIO访问。

前文书(QEMU源码全解析 —— PCI设备模拟(5))已经讲过,pci_edu_realize函数中调用memory_region_init_io函数,指定其读写函数是edu_mmio_ops。

edu_mmio_ops在hw/misc/edu中初始化,代码如下:

static const MemoryRegionOps edu_mmio_ops = {.read = edu_mmio_read,.write = edu_mmio_write,.endianness = DEVICE_NATIVE_ENDIAN,.valid = {.min_access_size = 4,.max_access_size = 8,},.impl = {.min_access_size = 4,.max_access_size = 8,},};

edu_mmio_ops的类型为MemoryRegionOps,此结构在include/exec/memory.h中定义,代码如下:

typedef struct MemoryRegionOps MemoryRegionOps;

而struct MemoryRegionOps的定义也在include/exec/memory.h中,如下:

/** Memory region callbacks*/
struct MemoryRegionOps {/* Read from the memory region. @addr is relative to @mr; @size is* in bytes. */uint64_t (*read)(void *opaque,hwaddr addr,unsigned size);/* Write to the memory region. @addr is relative to @mr; @size is* in bytes. */void (*write)(void *opaque,hwaddr addr,uint64_t data,unsigned size);MemTxResult (*read_with_attrs)(void *opaque,hwaddr addr,uint64_t *data,unsigned size,MemTxAttrs attrs);MemTxResult (*write_with_attrs)(void *opaque,hwaddr addr,uint64_t data,unsigned size,MemTxAttrs attrs);enum device_endian endianness;/* Guest-visible constraints: */struct {/* If nonzero, specify bounds on access sizes beyond which a machine* check is thrown.*/unsigned min_access_size;unsigned max_access_size;/* If true, unaligned accesses are supported.  Otherwise unaligned* accesses throw machine checks.*/bool unaligned;/** If present, and returns #false, the transaction is not accepted* by the device (and results in machine dependent behaviour such* as a machine check exception).*/bool (*accepts)(void *opaque, hwaddr addr,unsigned size, bool is_write,MemTxAttrs attrs);} valid;/* Internal implementation constraints: */struct {/* If nonzero, specifies the minimum size implemented.  Smaller sizes* will be rounded upwards and a partial result will be returned.*/unsigned min_access_size;/* If nonzero, specifies the maximum size implemented.  Larger sizes* will be done as a series of accesses with smaller sizes.*/unsigned max_access_size;/* If true, unaligned accesses are supported.  Otherwise all accesses* are converted to (possibly multiple) naturally aligned accesses.*/bool unaligned;} impl;
};

其中的read和Write函数分别表示该MMIO的读写回调;endianness表示字节的大小端模式。

以write回调函数为例,

    /* Write to the memory region. @addr is relative to @mr; @size is* in bytes. */void (*write)(void *opaque,hwaddr addr,uint64_t data,unsigned size);
static void edu_mmio_write(void *opaque, hwaddr addr, uint64_t val,unsigned size)

其原型中的opaque表示的是设备的对象;addr表示虚拟机读的地址在该MMIO中的偏移地址;data(val)表示要写入的值;size表示写入值的大小,通常由单字节、双字节、四字节以及八字节。

edu_mmio_write函数同样在hw/misc/edu.c中,代码如下:

static void edu_mmio_write(void *opaque, hwaddr addr, uint64_t val,unsigned size)
{EduState *edu = opaque;if (addr < 0x80 && size != 4) {return;}if (addr >= 0x80 && size != 4 && size != 8) {return;}switch (addr) {case 0x04:edu->addr4 = ~val;break;case 0x08:if (qatomic_read(&edu->status) & EDU_STATUS_COMPUTING) {break;}/* EDU_STATUS_COMPUTING cannot go 0->1 concurrently, because it is only* set in this function and it is under the iothread mutex.*/qemu_mutex_lock(&edu->thr_mutex);edu->fact = val;qatomic_or(&edu->status, EDU_STATUS_COMPUTING);qemu_cond_signal(&edu->thr_cond);qemu_mutex_unlock(&edu->thr_mutex);break;case 0x20:if (val & EDU_STATUS_IRQFACT) {qatomic_or(&edu->status, EDU_STATUS_IRQFACT);/* Order check of the COMPUTING flag after setting IRQFACT.  */smp_mb__after_rmw();} else {qatomic_and(&edu->status, ~EDU_STATUS_IRQFACT);}break;case 0x60:edu_raise_irq(edu, val);break;case 0x64:edu_lower_irq(edu, val);break;case 0x80:dma_rw(edu, true, &val, &edu->dma.src, false);break;case 0x88:dma_rw(edu, true, &val, &edu->dma.dst, false);break;case 0x90:dma_rw(edu, true, &val, &edu->dma.cnt, false);break;case 0x98:if (!(val & EDU_DMA_RUN)) {break;}dma_rw(edu, true, &val, &edu->dma.cmd, true);break;}
}

edu_mmio_write函数展示了一个虚拟机在写设备MMIO地址时QEMU中设备模拟的典型行为。

(1)首先,需要检查读写地址以及大小是否在范围之内。代码片段如下:

    if (addr < 0x80 && size != 4) {return;}if (addr >= 0x80 && size != 4 && size != 8) {return;}

(2)然后,根据具体的地址来进行适当的行为。

这些行为可以是简单地设置一个值,如这里的写0x04地址,代码片段如下:

    case 0x04:edu->addr4 = ~val;break;

也可以是将中断设置为高电平(写0x60地址)或者设置为低电平(写0x64地址),代码片段如下:

    case 0x60:edu_raise_irq(edu, val);break;case 0x64:edu_lower_irq(edu, val);break;

还可以是通过dma读写设备虚拟机的物理地址(写0x80地址),代码片段如下:

    case 0x80:dma_rw(edu, true, &val, &edu->dma.src, false);break;

对于read回调函数,也是类似的机制。这里仅给出edu_mmio_read函数源码,在hw/misc/edu.c中,代码如下:

static uint64_t edu_mmio_read(void *opaque, hwaddr addr, unsigned size)
{EduState *edu = opaque;uint64_t val = ~0ULL;if (addr < 0x80 && size != 4) {return val;}if (addr >= 0x80 && size != 4 && size != 8) {return val;}switch (addr) {case 0x00:val = 0x010000edu;break;case 0x04:val = edu->addr4;break;case 0x08:qemu_mutex_lock(&edu->thr_mutex);val = edu->fact;qemu_mutex_unlock(&edu->thr_mutex);break;case 0x20:val = qatomic_read(&edu->status);break;case 0x24:val = edu->irq_status;break;case 0x80:dma_rw(edu, false, &val, &edu->dma.src, false);break;case 0x88:dma_rw(edu, false, &val, &edu->dma.dst, false);break;case 0x90:dma_rw(edu, false, &val, &edu->dma.cnt, false);break;case 0x98:dma_rw(edu, false, &val, &edu->dma.cmd, false);break;}return val;
}

欲知后事如何,且看下回分解。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Camunda Variable Scope(Global、Local )

repositoryService.createDeployment().name("全局局部变量流程").addClasspathResource("bpmn/global_local_variable.bpmn").deploy(); identityService.setAuthenticatedUserId("huihui");// UserTask1 VariableMap startVariables Variable…

C#上位机与欧姆龙PLC的通信12----【再爆肝】上位机应用开发(WPF版)

1、先上图 继上节完成winform版的应用后&#xff0c;今天再爆肝wpf版的&#xff0c;看看看。 可以看到&#xff0c;wpf的确实还是漂亮很多&#xff0c;现在人都喜欢漂亮的&#xff0c;颜值高的&#xff0c;现在是看脸时代&#xff0c;作为软件来说&#xff0c;是交给用户使用的…

LeetCode - 1371 每个元音包含偶数次的最长子字符串(Java JS Python C)

题目来源 1371. 每个元音包含偶数次的最长子字符串 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串 s &#xff0c;请你返回满足以下条件的最长子字符串的长度&#xff1a;每个元音字母&#xff0c;即 a&#xff0c;e&#xff0c;i&#xff0c;o&#xff0…

Vue+ElementUI+Axios实现携带参数的文件上传(数据校验+进度条)

VueElementUIAxios实现携带参数的文件上传&#xff08;数据校验进度条&#xff09; 可以实现对上传文件的类型&#xff0c;大小进行数据校验&#xff0c;以及对上传文件所要携带的数据也进行的校验&#xff0c;也有文件上传进度的进度条。 一、Vue 结构部分 弹窗显示&#xff0…

Camunda Sub Process

一&#xff1a;内嵌子流程 repositoryService.createDeployment().name("内嵌子流程").addClasspathResource("bpmn/embed_sub_process.bpmn").deploy(); identityService.setAuthenticatedUserId("huihui"); ProcessInstance processInstance …

使用numpy处理图片——缩放图片

缩放图片是让图片丢失部分像素&#xff0c;从而导致图片失真。一种比较简单的方法就是抽取法。比如如果我们要将照片在宽度上缩小50%&#xff0c;则可以在第二维度上每隔2个像素取一个像素来保存&#xff1b;类似的&#xff0c;如果我们希望在高度上缩小50%&#xff0c;则可以在…

instanceof、对象类型转化、static关键字

instanceof 与 对象类型转换 instanceof是判断一个对象是否与一个类有关系的关键字 先看引用类型&#xff0c;再看实际类型 *例子&#xff1a;obj instanceof A 先看obj的类型是否与A有关联&#xff0c;无关联则报错&#xff0c;有关联则判断obj的实际类型 因为obj的实际类…

集简云动作管理平台上线:创建强大且可分享的AI助手(GPTs)

OpenAI的GPT Store于昨天上线&#xff0c;用户可以找到好用的GPTs&#xff0c;也可以将自己的GPTs分享到GPT Store中。未来&#xff08;预计今年1季度&#xff09;甚至可以从GPTs Store中获取利润分成。 要创建强大的GPTs离不开调用外部的软件工具&#xff0c;比如查询CRM/ERP软…

Spring Boot - Application Events 的发布顺序_ApplicationStartedEvent

文章目录 Pre概述Code源码分析 Pre Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent 概述 Spring Boot 的广播机制是基于观察者模式实现的&#xff0c;它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦&#…

视频监控平台的管理员账号在所有客户端都无法登录的问题解决

目 录 一、问题描述 二、问题排查 1、看问题提示 2、看日志信息 3、问题定位 三、问题解决 1. 添加权限角色 2、添加操作用户 3、验证 一、问题描述 AS-V1000视频监控平台安装部署完成后&#xff0c;发现管理员admin不能到web客户端&#xff0c;觉…

Maven的安装和配置

国内Maven仓库之阿里云Aliyun仓库地址及设置 用过Maven的都知道Maven的方便便捷&#xff0c;但由于某些网络原因&#xff0c;访问国外的Maven仓库不便捷&#xff0c;好在阿里云搭建了国内的maven仓库。 需要使用的话&#xff0c;要在maven的settings.xml 文件里配置mirrors的子…