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;
}

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

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

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

相关文章

【开源】基于JAVA的数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

有道云笔记编辑 Markdown 文件 - GitHub README.md

有道云笔记编辑 Markdown 文件 - GitHub README.md 1. 新建 -> Markdown2. GitHub README.mdReferences 1. 新建 -> Markdown ​ 2. GitHub README.md ​​​ References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

关于群晖ARPL界面能出现ip但是使用Synology Assistant搜索不到ip问题 及解决方法

文章引用ing304 频道文章&#xff1a;https://qun.qq.com/qqweb/qunpro/share?_wv3&_wwv128&appChannelshare&inviteCode20jx8dPsU2z&contentID1m4NKs&businessType2&from181174&shareSource5&bizka 前言 当进入该界面后 提示IP无法访问&a…

面试题:SELECT COUNT(*) 会造成全表扫描吗 ?

文章目录 前言SQL 选用索引的执行成本如何计算实例说明总结 前言 SELECT COUNT(*)会不会导致全表扫描引起慢查询呢&#xff1f; SELECT COUNT(*) FROM SomeTable 网上有一种说法&#xff0c;针对无 where_clause 的 COUNT(*)&#xff0c;MySQL 是有优化的&#xff0c;优化器…

.NET core 中的Kestrel 服务器

什么是Kestrel&#xff1f; Kestrel 是一个跨平台的Web服务器&#xff0c;会默认在ASP.NET Core 项目模板中对其进行配置。未使用 IIS 托管时&#xff0c;ASP.NET Core 项目模板默认使用 Kestrel。 Kestrel 的功能包括&#xff1a; 跨平台&#xff1a;Kestrel 是可在 Window…

资源三角形

美国哈佛大学的研究小组提出了著名的资源三角形&#xff1a;没有物质&#xff0c;什么也不存在&#xff1b;没有能量&#xff0c;什么也不会发生&#xff1b;没有信息&#xff0c;任何事物都没有意义。物质、能量和信息是相互有区别的&#xff0c;是人类社会赖以生存、发展的三…

代码随想录-刷题第五十五天

72. 编辑距离 题目链接&#xff1a;72. 编辑距离 思路&#xff1a;本题是用动规来解决的经典题目&#xff0c;这道题目看上去好像很复杂&#xff0c;但用动规可以很巧妙地算出最少编辑距离。动态规划五步曲分析&#xff1a; dp[i][j]表示以下标i-1为结尾的字符串word1&#x…

基础篇_开发web程序(C/S架构,SpringBoot,贷款计算器-WEB版)

文章目录 一. C/S 架构1. C/S 架构2. URL 格式 二. Spring Boot1. 向导生成2. 准备工作1) 修改版本2) 修改maven 设置 3. 导入模块4. hello world5. 处理输入页面接收参数练习 - 加法 三. 贷款计算器 - WEB 版1. 数组定义改写贷款计算器越界遍历默认值 2. 二维数组3. 贷款计算器…

PaddleSeg学习4——paddle模型使用TensorRT推理(c++)

paddle模型使用TensorRT推理 1 模型末端添加softmax和argmax算子2 paddle模型转onnx模型3 onnx模型转TensorRT模型3.1 安装TensorRT-8.5.3.13.2 使用 trtexec 将onnx模型编译优化导出为engine模型 4 TensorRT模型推理测试5 完整代码6 测试结果 1 模型末端添加softmax和argmax算…

SpringBoot 源码解析4:refresh 方法解析

SpringBoot 源码解析4&#xff1a;refresh 方法解析 1. refresh 方法解析2. 准备刷新 AbstractApplicationContext#prepareRefresh3. 获取bean工厂 AbstractApplicationContext#obtainFreshBeanFactory4. 准备bean工厂 AbstractApplicationContext#prepareBeanFactory5. Servle…

Dell 机架式服务器 - 高级定制

Dell 机架式服务器 - 高级定制 1. Dell Technologies2.1. Servers & Storage (服务器及存储) -> Servers2.2. Rack Servers (机架式服务器)2.3. Shop2.4. PowerEdge Rack Servers (PowerEdge 机架式服务器)2.5. PowerEdge R760 Rack Server (PowerEdge R760 机架式服务器…

边缘计算:连接实时数据的力量与未来发展之路

边缘计算是一种分布式计算范式&#xff0c;它旨在将数据处理、存储和应用服务带到数据源的近端&#xff0c;即网络的“边缘”。在边缘计算模型中&#xff0c;算力和存储资源距离末端用户或数据源更近&#xff0c;这减少了数据在网络中传输的距离&#xff0c;从而降低延迟&#…

BikeDNA(四)初始化参考数据

BikeDNA&#xff08;四&#xff09;初始化参考数据 这本笔记本&#xff1a; 加载定义研究区域的多边形&#xff0c;然后为研究区域创建网格叠加。加载参考数据。处理参考数据以创建分析所需的网络结构和属性。 先决条件和条件 输入/输出 config.yml 必须提前设置。 此笔记本…

uniapp 查找不到uview-ui文件怎么办?

用官方的方式总是报&#xff1a;文件查找失败&#xff1a;uview-ui at main.js 解决方案&#xff1a; 1.先安装uview-ui npm install uview-ui 下载成功是这样的&#xff1a; 而不是这样的&#xff1a; 这样的原因是你的项目里没有package.json包&#xff0c;先执行 npm …

Ps:操控变形

Ps菜单&#xff1a;编辑/操控变形 Edit/Puppet Warp 操控变形 Puppet Warp命令能够借助网格随意扭曲特定图像区域&#xff0c;同时可保持其他区域不变。 其应用范围小至精细的图像修饰&#xff08;如发型设计&#xff09;&#xff0c;大至总体的变换&#xff08;如重新定位手臂…

Ftrans飞驰云联荣获“CSA 2023安全创新奖”

2023年12月21日&#xff0c;第七届云安全联盟大中华区大会在深圳成功举办。会上&#xff0c;CSA大中华区发布了多个研究成果并进行 CSA 2023年度颁奖仪式&#xff0c;Ftrans飞驰云联以其突出的技术创新能力和广泛的市场应用前景&#xff0c;荣获备受瞩目的“CSA 2023安全创新奖…

watchdog,一个无敌的 Python 库

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个无敌的 Python 库 - watchdog。 Github地址&#xff1a;https://github.com/gorakhargosh/watchdog 在软件开发和系统管理领域&#xff0c;经常需要监控文件和目录的变化&#xff0c;以便在文…

JDBC PrepareStatement 的使用(附各种场景 demo)

在 Java 中&#xff0c;与关系型数据库进行交互是非常常见的任务之一。JDBC&#xff08;Java Database Connectivity&#xff09;是 Java 平台的一个标准 API&#xff0c;用于连接和操作各种关系型数据库。其中&#xff0c;PreparedStatement 是 JDBC 中的一个重要接口&#xf…

abaqus重新打开之后自定义的工具栏状态恢复默认的解决办法

在自定义工具栏之后&#xff0c;点击&#xff1a; File——Save Display Options——勾选Current&#xff0c;点击OK。 中文版&#xff1a;文件-保存显示选项-目录选择当前目录&#xff0c;点击确定。 重新打开abaqus之后发现工具栏是自己定义的。 另&#xff1a; 1. 视口注…

brpc: a little source code

之前在https://www.yuque.com/treblez/qksu6c/nqe8ip59cwegl6rk?singleDoc# 《olap/clickhouse-编译器优化与向量化》中我谈过brpc的汇编控制bthread。本文就来看一下brpc作为一个高性能的rpc实现&#xff0c;除了自定义线程栈之外&#xff0c;代码还有什么优秀之处。 因为时间…