浅谈QEMU Memory Region 与 Address Space

浅谈QEMU Memory Region 与 Address Space

1. Memory Region

1.1 Memory Region & Address Space & Device

参考:http://blog.vmsplice.net/2016/01/qemu-internals-how-guest-physical-ram.html

  • 三者的关系可以用下图简单表示:

    image-20230421140537578

  • 从下往上看,首先每一个Memory Region对象都包含了RAMBlock,RAMBlock是QEMU真正向Host alloc申请空间的数据结构,它的大小可以指定;而Memory Region是从RAMBlock进行了抽象,有助于Device操作内存空间。

    • QEMU维护了RAMList,每申请一个RAMBlock(Memory Region)添加到List中
  • Address Space包含了一系列Memory Region,在当前上下文空间中,通过地址空间对当前一系列存储对象进行一个统一抽象,很抽象举个例子

    • 比如当前是CPU正在运行的状态,就有了一个Address Space名为CPU-system,此时的寻址空间,高位是操作系统内核空间,中间有0x40000000~0x7FFFFFFF共4G的地址是用于RAM内存空间,还有一些地址用于I/O
    • 后来需要I/O,进入I/O状态后,又有了一个Address Space名为I/O,此时的所有地址都是I/O地址
  • 对于memory-backend,可以不用理解,只需要知道大多数device都需要和存储交互,操作的对象是Memory Region即可

    image-20230421143554938

  • Memory Region、RamBlock的对应关系可以参考上图

1.1.1 PCI Address Space
  • PCI是如今最常用的总线协议,QEMU虚拟机默认的总线协议也是PCI。需要特别指出的是

    • PCI地址空间是可以开机配置的,也就是说此时我还在用0x45F23F32访问内存地址,经过配置后,再次启动,这个地址可能是属于某个PCI Device的IO地址
    • 由于64位地址空间十分宽阔,所以PCI地址空间一般可以认为只有一个,将所有的设备都涵盖在64位地址空间内

    image-20230421144701937

  • 如上图的例子,灰色部分是一个PCI的地址空间,有两个Memory Region分别是两个pci-hole,大小为128个字节,中间有个1G的RAM内存

  • 我们可以对PCI地址空间进行配置

    image-20230421144836575

    扩大RAM内存的容量

1.2 Memory Region 类型

  • RAM类型:
    • 内存Memory和Cache就是RAM类型,创建接口为:
      • memory_region_init_ram()
      • 特殊的:memory_region_init_resizeable_ram(), memory_region_init_ram_from_file(), or memory_region_init_ram_ptr()
  • MMIO类型:
    • 用于host callbacks创建的guest memory,如每次read or write操作会导致host的一个callback,可以通过memory_region_init_io()创建
  • ROM类型
    • read-only-memory,主要用于闪存,比如存OS的加载器,创建接口:
      • memory_region_init_rom()
  • ROM Device类型:
    • 像RAM一样read,同时write会调用host callback的device,创建接口:
      • memory_region_init_rom_device()
  • IOMMU类型:
    • 有IOMMU职责的region,即能将地址直接翻译到目标的memory region,只能用于IOMMU device,创建接口:
      • memory_region_init_iommu()
  • container类型:
    • 顾名思义,包含了其他的mr,记录每个mr的offset。container可以管理多个mr为一个单位,十分有用。比如,一个PCI BAR可能包含了一个RAM region和一个MMIO region
    • 创建container mr使用纯净的init方式:
      • memory_region_init()
  • alias类型:
    • 一个region的子集。允许将一个region分解成不连续的子region,创建接口:
      • memory_region_add_subregion(),用于将一个已存在的region加入到某个container中
      • memory_region_init_alias(),用于创建一个新的region
  • reservation region类型:
    • 用于debug,在enable-kvm之后使用
      • memory_region_init_io()
  • qemu mr支持向所有的非container中add一个subregion,比如MMIO,RAM,ROM region,此时这些Region也表现为container,除非规定了这个region的所有地址都由该region自己处理
  • 但qemu不支持向alias类型的region中加入subregion

1.3 Memory Region 生命周期

  • 首先一个mr的诞生从memory_region_init*()函数开始,并且mr属于一个已存在的对象,比如vcpu,说明这个mr是vcpu的一部分,称这个vcpu对象是此mr的owner或者parent
  • QEMU规定只要mr还对guest可见,owner就不会死亡
  • 被创建后的mr,可以被加入到一个address space或container,或者被移除
    • 接口分别为:memory_region_add_subregion()和memory_region_del_subregion()
  • mr被创建后,职责不是一成不变的,可以修改,发生在mr被置为visible的时候赋予
  • 当owner死亡后,mr会被自动析构清除
  • mr也可以手动清除
    • 接口为object_unparent()

注意:尽量不要在一个device生命周期中动态的创建使用mr,即使这样做了,也要注意显式free掉

1.4 Memory Region Overlapping and Priority

  • 通常,一个regions会被解析成一个唯一确定的目标地址,并不会重叠(overlapping),但一些场景下mr会重叠。即,一个mr对应了多个目标地址,在一次调用中使用哪个目标地址,取决于这些目标地址的优先级(priority),即优先级高的被返回。

    • 对应的接口:memory_region_add_subregion_overlap(),允许一个region和同一个容器中的其他region重叠,并且声明优先级
  • 看下面的例子

    image-20230414164841725

  • container A从0开始,size为0x8000,A有2个subregions:B和C,B是一个从0x2000开始,大小为0x4000的容器,优先级为2;C是一个从0开始,大小为0x6000的MMIO region,优先级为1。B容器中有2个subregions:D和E

  • 那么C范围的地址可以看成:

    • [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC]
      
    • 因为B的优先级高于C,所以用的是B容器的subregions,但是B并不是连续存在的,有空洞(holes),此时QEMU默认用上一级填满,所以用CCCCC填满

      注意:priority对于container是local的,即两个容器内的两个region比较priority是没意义的

  • 如果B的空洞被赋予值了,比如B提供自己的MMIO操作,那么结果就是

    • [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB]
      

1.5 Visibility

  • 当你获得一个address时,怎么确定这个地址的值
    • 首先得根据地址空间确定当前的地址映射
    • 再查找address上的值

1.6 FlatView

  • 通常一个系统的地址空间有多个,而且mr还是重叠的,那么如何快速根据一个地址就得到它映射的位置
    • 正常的做法是,先确定当前地址空间的visibility,然后在计算该地址的映射
    • QEMU设计了FlatView Buffer,用于保存一个完整的地址空间映射,这样下次再用到这个地址空间的时候就可以直接获得
      • 接口:generate_memory_topology()

思考

  • 为什么memory region可以重叠,为什么需要alias的概念

    参考:https://martins3.github.io/qemu/memory.html#memoryregion

    • 很简单,这样可以让QEMU代码变得简单
      • 如果不能重叠,那么一个模块要拆改那就Memory Region的时候,就要考虑其他模块的Memory Region的大小和边界
      • 使用alias可以将一个Memory Region的一部分放到另外一个mr上,因为对于一个地址空间来说,并不是按照连续的方式给每一个类型的memory划分区域的,而是离散的。比如一个pc.ram,创建的时候就有ram-below-4g和ram-above-4g,分别位于地址空间的尾部和头部,如果没有alias,就需要创建两个pc.ram,就需要mmap两次,之后的操作也需要两次
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值