QEMU中的内存API(2)

4 区域的生命周期

        一个区域由memory_region_init*()函数中一个创建并与一个object关联,该object被当前它自己或parent。QEMU保证只要该区域对guest可见或只要该区域被vCPU或其他设备使用,owner object仍保持有效。比如owner object在address_space_map()操作和address_space_unmap()操作之间并不会消亡。

        在创建后,一个区域通过函数memory_region_add_subregion()被添加到一个地址空间或一个container中,并使用函数memory_region_del_subregion()移除。

        各种区域属性(只读,脏标记,合并MMIO,ioeventfd)在区域生命周期可以被修改。只要区域可见,它们就可以生效。这可以立即产生,也可以稍后产生,或永远不会产生。

        当owner object消亡时内存区域自动消亡。

        但是如果内存区域为动态分配的数据结构的一部分,你应该调用object_unparent()在数据结构被释放之前毁销内存区域。比如看hw/vfio/pci.c中VFIOMSIXInfo和VFIOQuirk。

        只要一个内存区域被设备或CPU使用时你不能销毁内存区域。为了使用它,因为在设备的生命周期内通常规则不能动态的创建或销毁内存区域,仅在内存区域owner的instance_finalize回调中调用object_unparent()。动态分配的数据结构包含内存区域,内存区域应该在instance_finalize回调中被销毁。

        若你破坏上述规则,下面的情况可能产生:

  1. 内存区域的owner通过memory_region_ref获得引用计数(比如通过address_space_map);
  2. 区域被unparent,不再有owner;
  3. 当address_space_unmap被调用时,对内存区域owner引用计数被释放;

        对上述规则有一个例外:在任何时间对alias或container区域调用object_unparent是可以的。因此在设备的生命周期动态的创建或销毁alias或container区域也是可以的。

        例外使用也是有效的因为alias或container仅帮助QEMU建立guest的内存映射;它们不能被直接访问。Memory_region_ref和memory_region_unref不需要对alias或container调用,上述情况不会发生。利用这个例外不是很必要,因此不鼓励,但也可以用于有些地方。

        对于没有owner的区域(NULL在创建时被传递),机器object被用作owner。因为instance_finalize不会被machine object调用,你不能对没有owner的区域调用object_unparent,如果它们不是alias或container。

5 区域的重叠和优化级

        通常区域之间不会相互重叠;一个内存地址被定位到一个目标上。在一些场景下允许区域重叠也是有用的,有时可以来控制哪个重叠区域可以被guest看见。这可以通过memory_region_add_subregion_overlap(),它允许在相同的container中一个区域与其他区域重叠,并指定优先级允许core来决定相同的地址的哪个区域被看到(最高优先级被看到)。

   优先级值被赋予,默认值为0。这意味着你可以使用memory_region_add_subregion_overlap()在”above”区域和”below”区域。

        如果重叠的最高优先级为一个container或alias,更低优先级区域将出现在更高优先级留下的hole区域(通过没有映射子区域)。(这可以递归的应用。如果子区域自己的container或alias,留下hole,更低优先级区域将出现在Hole)。

        比如,假定我们有一个大小为0x8000在container A,它有两个子区域B和C。B为一个映射在0x2000,大小为0x4000的container,优先级为2;C为一个MMIO映射区,它映射到0x0,大小为0x6000,优先级为1。B当前有两个自己的子区域:D偏移为0,大小为0x1000,E偏移为0x2000,大小为0x1000。如下图所示:

        可以看到的区域地址范围如下所示:

         因为B有比C更高在优先级,它的子区域出现在flat映射,它与C重叠。当B没有映射C的区域。

        如果B提供它自己的MMIO操作(如它不是单纯的container),这将用于范围内的任何地址,该范围没有被D和E处理,结果将成为:

         优先级值对container是本地的,因为两个区域的优先级仅当它们都是相同container在子区域时才能比较。这意味着负责container的设备可以使用它们来管理它子区域的交互,而没有任何边际效应。在上述例子中,D和E的优先级不重要因为他们相互不重叠。正是B和C的相对优先级导致D和E出现在C的顶部;D和E的优先级不再与C的优先级比较。

6 可见性

        当guest访问一个地址时,内存core使用如下规则选择内存区域:

(1)根区域的所有直接子区域都与地址匹配,以降序优先级

- 如果地址在区域offset/size之外,子区域被放弃;

- 如果子区域为叶子结点(RAM或MMIO),查询停止,返回叶子区域;

- 如果子区域为container,在子区域使用相同的算法(在地址被子区域偏移被调整);

- 如果子区域为alias,在alias的目的上继续搜索(在地址被子区域偏移和alias偏移);

- 如果在一个container或alias子区域的递归搜索没有找到匹配(因为它的地址范围的container覆盖中的hole),如果container有自己的MMIO或RAM,返回container。否则我们在优先级顺序中继续下一个子区域

(2)如果没有子区域匹配地址,搜索终止,找不到匹配

7 内存映射的例子

         这是一个简化的PC内存映射。4GB RAM块通过两个alias映射到系统地址空间:”lomen”为3.5GB的1:1映射;”himem”在地址4GB至少映射0.5GB。这为PCI hole留下0.5GB,这允许在4GB内存系统中留下32位PCI总线。

        内存控制器将地址范围640K-748K转化到PCI地址空间。它使用vga-window alias,映射到一个更高优先级,因此它将RAM抽象到时相同的地址。Vga通过编程内存控制被移除;可通过移除alias和导出RAM建立。

        PCI地址空间并不是系统地址空间的直接孩子,因为我们仅希望部分被看到(我们通过使用alias完成上述)。它有两个子区域:vga-area为传统vga窗口并由两个32K内存块指向两个framebuffer的section。

        另外vram被映射到地址BAR e1000000,另外BAR包含MMIO寄存器被映射。

        注意如果guest映射一个BAR在PCI hole之外,它将不会被看到,因为PCI-HOLE将其放在0.5GB范围。

8 MMIO操作

        MMIO区域由->read()和->write()回调提供,这对大多数设备是足够。一些设备基于内存使用的属性而变化行为,或对可能造成总线错误回复而不是成功的完成;这些设备可以使用->read_with_attrs()和->write_with_attrs()回调。

        另外各种限制可以被应用来控制这些回调如何被调用:

- .valid.min_access_size,.valid.max_access_size定义了设备接受的访问大小;在这个范围之外进行访问将有设备和总线特定的范围(忽略,或检查);

- .valid.unaligned指定了设备支持的非对齐访问;若失败,非对齐的访问将涉及合适的总线或CPU特定的行为;

- .impl.min_access_size,.impl.max_access_size定义了实现支持的访问大小;其他的访问大小使用有效的被模拟。比如4byte写将使用1byte写来模拟,如果.impl.max_access_size=1;

- .impl.unaligned 指定实现支持不对齐的访问;如果失败,不对齐的访问将由两个访问模拟

内容翻译来自:

memory.rst - docs/devel/memory.rst - Qemu source code (v6.2.0) - Bootlin

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值