【问题标题】:Making a virtual IOPCIDevice with IOKit使用 IOKit 制作虚拟 IOPCIDevice
【发布时间】:2018-06-04 19:16:12
【问题描述】:

我设法创建了一个附加到 IOResources 的虚拟 IOPCIDevice,基本上什么都不做。我可以让现有的驱动程序注册并与之匹配。

但是,在 IO 处理方面,我遇到了一些麻烦。 IOPCIDevice 类中描述的函数(例如 configRead、ioRead、configWrite、ioWrite)的 IO 访问可以由我自己的代码处理。但是使用内存映射和 IODMACommand 的驱动程序是问题所在。

我似乎需要管理两件事:IODeviceMemory(在 IOPCIDevice 中描述)和 DMA 传输。

我如何创建一个最终指向内存/RAM 的 IODeviceMemory,以便当驱动程序尝试与 PCI 设备通信时,它最终什么也不做或只是将数据移动到 RAM,因此我的用户空间客户端可以处理这些数据并采取行动作为模拟 PCI 设备?

然后可以将 DMA 命令也定向到我的用户空间客户端,而不会干扰使用 IODMACommand 的现有驱动程序的源代码。

谢谢!

【问题讨论】:

    标签: macos emulation iokit pci pci-e


    【解决方案1】:

    捕获内存访问

    所以理论上,要实现您想要的,您需要分配一个内存区域,将其保护位设置为只读(或者如果您正在模拟的设备中的读取有副作用,则可能既不读取也不写入),然后将任何写入捕获到您自己的处理函数中,然后您将在其中模拟设备寄存器写入。

    据我所知,您可以在 macOS 用户空间中使用 Mach 异常处理来执行此类操作。您需要设置将页面保护错误异常从您控制的进程发送到您控制的 Mach 端口。在该端口的消息处理程序中,您将:

    • 检查访问的去向
    • 如果是设备内存,你会挂起进程的所有线程
    • 将写入的线程切换为单步,暂时允许写入内存区域
    • 恢复作者线程
    • 捕获单步消息。您的“设备内存”现在包含写入的值。
    • 执行您的“设备”副作用。
    • 在编写器线程中关闭单步。
    • 恢复所有线程。

    正如我所说,我相信这可以在用户空间进程中完成。这并不容易,您可以从网络上各种晦涩的示例中拼凑出您需要使用的 Mach 调用。我曾经做过类似的工作,但似乎再也找不到那个代码了,抱歉。

    …在内核中

    现在,另一个问题是您尝试在内核中执行此操作。我不知道有任何公共 KPI 可以让您执行我上面描述的任何事情。您可以从以下地方开始寻找黑客攻击:

    • 您可以很容易地使IOMemoryDescriptors 由系统内存支持。不要担心IODeviceMemory 术语:这些只是IOMemoryDescriptor 对象; the IODeviceMemory class is a lie。捕获访问完全是另一回事。原则上,您可以使用 createMappingInTask() 函数的“引用”标志找出特定 MD 的虚拟内存映射,然后使用 NULL 后备内存在返回的 IOMemoryMap 上调用 redirect() 方法争论。不幸的是,这只会暂停任何试图访问映射的线程。发生这种情况时,您不会收到回调。
    • 您可以深入了解 Mach VM 内存子系统的内部结构,该子系统主要位于 xnu 源代码的 osfmk/vm/ 目录中。也许有一种方法可以为那里的 VM 区域设置自定义故障处理程序。不过,您可能不得不对私有内核 API 感到厌烦。

    为什么?

    最后,你为什么要这样做?退后一步:你最终想用这个做什么?似乎以这种方式模拟 PCI 设备本身并不是目的,所以这真的是实现您最终想要实现的更大目标的唯一方法吗? See: XY problem

    【讨论】:

    • 嘿pmdj,谢谢你的建议。我还想知道当驱动程序对“设备”内存执行写入操作时如何获得通知。但是,macOS 可以通过 IOMemoryDe​​scriptor 直接指向 PCIe 设备的内存空间吗?通过阅读 Apple 的 PCIe 驱动程序的源代码,他们似乎广泛使用 IODMACommand。此外,通过检查一些 PCIe IC 数据表,它通常需要触发消息传递,例如configWrite 告诉 IC 一条新消息已写入。但最重要的是,您认为可以通过 IODMACommand 调用发出通知吗?
    • @kpeteL IODMACommand 的目的主要是确保一些系统内存缓冲区的物理内存页面在设备访问它们的过程中被连接起来(这样它们就不会被内核的 VM 移动系统),并获取相应的物理(或 IOMMU 空间)地址范围。 (AKA “scatter-gather list”)这些 scatter-gather 列表如何传递给设备是特定于设备的,但它几乎必须写入 BAR(内存映射或 I/O),写入 PCI 配置空间, 或另一个 DMA 缓冲区,其位置和大小先前已写入 BAR 或配置。
    • @kpeteL 换句话说,真正启动 DMA 传输的是 BAR 或 PCI 配置写入。当然,仅根据您获得的 IOMMU 空间地址访问 DMA 缓冲区完全是另一个问题。您可能需要查看您的虚拟 IOPCIDevice 将如何与 VT-d 交互(参见 IOPCIDevice 源包中的 vtd.c)。除非您要模拟的设备非常简单,否则这将是一项艰巨的任务。您确定没有更好的方法来解决您的问题吗?
    • 我正在使用这种方法来解决一些 Apple 未公开的源代码。或者,使用另一种方法,有墙可以正确模拟 hostusbdevice。内存陷阱对我来说不再是问题,因为我可以过滤我的驱动程序调用,并锻炼何时检查内存缓冲区。
    • 嘿@pmdj 我现在成功地制作了一个虚拟 PCI 设备,它将内存映射到用户空间,但是我在制作虚拟中断时遇到了麻烦。或者实际上连接到我的设备的驱动程序甚至不能添加 IOFilterInterruptEventSources。调用 IOService::getInterruptType 时失败。为了解决这个问题,我假设我可以将自己的虚拟 IOInterruptController 类附加到设备父级的分支上。那么你知道任何伪造中断的方法吗?我可以只使用一些虚拟中断向量吗? 0x80?或者你怎么看?
    猜你喜欢
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 2011-07-08
    • 1970-01-01
    • 2014-12-16
    相关资源
    最近更新 更多