qemu 内存模型(1)---文档

首先阅读qemu的memory.txt文档, 大概有如下信息可知

1 首先AddressSpace是cpu可以看到的地址空间,一般就是cpu地址总线宽度的可寻址范围

主要包括两个地址空间

 1 ram 空间 , 通过mov等指令直接访问

 2 I/O空间, 通过out in等io指令访问

2 MemoryRegion 用于记录连续内存/io空间和host 虚拟地址的关系, 和这段连续空间的设备属性

1 ram 比较简单的内存空间,读写对应直接修改 hva, 初始memory_region_init_ram, memory_region_init_resizeable_ram, memory_region_init_ram_from_file, memory_region_init_ram_ptr 函数用于初始化

2 mmio 这些空间对应于设备的地址空间映射到内存地址空间的空间(也就是mmio), 读写这些地址空间都会触发对应模拟设备的回调操作 memory_region_init_io 初始化

3 ROM 只读空间, 读直接访问hva, 写则拒绝,使用memory_region_init_rom() 初始化

4 rom device 这类空间读直接访问hva, 写则回调设备注册的函数(设备可能进行出错处理)

5 IOMMU 用于iommu建模型,也就是支持iommu的设备模拟 memory_region_init_iommu()

6 container 逻辑单元, 逻辑单元内的地址可以通过overlay覆盖, memory_region_init() 初始化容器

7 别名: 比较难理解, 比如我的pci是一个MemoryRegion是一个Region, 对应一片连续的hva, 但是我要把它映射到ram的地址空间, 就需要别名机制,把pci的一段地址分裂出一个别名,把这个别名放入Ram MemoryRegion的子region中,这样这段地址在, 也就是同一个MemoryRegion有相同

的设备属性(MMIO, RAM , ROM , ROM DEVICES), 但是可能在不连续的AddressSpace, 就是这样.

addresspace上是在ram所在的区域,但是实际的hva却指向pci的region, memory_region_init_alias() 初始化

8 保留区域: 主要用于调试,使用 memory_region_init_io() 初始化

3 几个约束和建议

1 别名MemoryRegion没有子Region

2 建议使用一个纯容器作为父MemoryRegion, 一个整个空间的MemoryRegion作为背景MemoryRegion, 其他MemoryRegion 覆盖到这个MemoryRegion上面, 当读写MemoryRegion对应的AddressSpace的时候, 只回调相应MemoryRegion的地址

4 迁移

Where the memory region is backed by host memory (RAM, ROM and
ROM device memory region types), this host memory needs to be
copied to the destination on migration. These APIs which allocate
the host memory for you will also register the memory so it is
migrated:

  • memory_region_init_ram()
  • memory_region_init_rom()
  • memory_region_init_rom_device()

For most devices and boards this is the correct thing. If you
have a special case where you need to manage the migration of
the backing memory yourself, you can call the functions:

  • memory_region_init_ram_nomigrate()
  • memory_region_init_rom_nomigrate()
  • memory_region_init_rom_device_nomigrate()
    which only initialize the MemoryRegion and leave handling
    migration to the caller.

The functions:

  • memory_region_init_resizeable_ram()
  • memory_region_init_ram_from_file()
  • memory_region_init_ram_from_fd()
  • memory_region_init_ram_ptr()
  • memory_region_init_ram_device_ptr()
    are for special cases only, and so they do not automatically
    register the backing memory for migration; the caller must
    manage migration if necessary.
    也就是说对于两种类型的MemoryRegion, 一种对应hva, 这种系统会直接帮忙迁移(也就是自动保存,复制)
    另外一种不对应hva, 需要设备实现者自己去在相应的回调中实现保存复制

5 MemoryRegion的名称

名字大部分用于调试,也有部分用于迁移时的复制,所以不要随便修改名字

6 优先级和覆盖策略

memory_region_add_subregion_overlap() 函数允许一个MemoryRegion覆盖容器中的其他区域, 覆盖是根据优先级来设置的,高优先级覆盖低优先级地址
如果高优先级的MemoryRegion是别名或者容器, 地址是不连续的,则会留下一些空洞,低优先级的MemoryRegion会在这些空洞的地方漏出来

A是一个纯容器,里面有B和C两个子MemoryRegion, C的优先级是1 , 相对A偏移为0, 大小为6000

B是一个纯容器,里面有两个MemoryRegion.分别是D和E, D相对B偏移为0,大小为1000, E相对B偏移为2000,大小为1000,如下图

    0      1000   2000   3000   4000   5000   6000   7000   8000
    |------|------|------|------|------|------|------|------|

A: [ ]
C: [CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC]
B: [ ]
D: [DDDDD]
E: [EEEEE]

最终映射处的地址如下
[CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC]

如果B不是一个纯容器,是一个MMIO区域则如下
[CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB]
另外优先级比较只发生在同一个容器的不同MemoryRegion中, 所以容器只能管理子区域不影响其他区域

7 可见性

当guest os 操作内存地址(gpa)的时候,使用如下规则选择MemoryRegion

递归向下

根区域的子区域按照优先级进行匹配
    - 如果一个区域的地址不匹配,则子区域也不匹配
    - 如果一个区域是叶子区域, 地址匹配则匹配
    - 如果是一个容器,按照相同算法递归向下匹配
    - 如果区域是一个别名, 则与别名目标区域进行匹配
    - 如果区域是一个纯容器区域,则该区域的子区域不包含该地址,寻找下一个区域(因为这个纯容器可能存在一个洞), 否则直接返回未找到
如果上述没有找到,则该区域不存在

7 示例

system_memory: container@0-2^48-1
|
±— lomem: alias@0-0xdfffffff —> #ram (0-0xdfffffff)
|
±— himem: alias@0x100000000-0x11fffffff —> #ram (0xe0000000-0xffffffff)
|
±— vga-window: alias@0xa0000-0xbffff —> #pci (0xa0000-0xbffff)
| (prio 1)
|
±— pci-hole: alias@0xe0000000-0xffffffff —> #pci (0xe0000000-0xffffffff)

pci (0-2^32-1)
|
±-- vga-area: container@0xa0000-0xbffff
| |
| ±-- alias@0x00000-0x7fff —> #vram (0x010000-0x017fff)
| |
| ±-- alias@0x08000-0xffff —> #vram (0x020000-0x027fff)
|
±— vram: ram@0xe1000000-0xe1ffffff
|
±— vga-mmio: mmio@0xe2000000-0xe200ffff

ram: ram@0x00000000-0xffffffff

system_memory为一个纯容器,这里对应cpu看到的AddressSpace , 0-2^48 地址空间. ram的大小是 4g, lomem 1:1映射到 system_memory的AddressSpace的 0-3.5G,

himem映射到4-4.5G AddressSpace. 留下3.5G-4G的空洞, 使用pci-hole别名进行映射(这部分其实是pci MemoryRegion的0xe0000000-0xffffffff区域的一个别名)

vag-window别名映射在memory AddressSpace的 640K-768K, 优先级比较高会覆盖 lomem, 内存控制器可以移除这个别名暴露下面的ram地址空间

pci MemoryRegion没有对应的地址空间, 只用于映射到 memory AddressSpace, 包含两个子区域 .

vga-area区域模拟vga窗口, 由两个32k的区域组成,模拟vga双缓冲

varm 和 vga-mmio 作为pci的子区域, vram在地址e1000000处被映射为BAR,并且在其之后映射包含MMIO寄存器的附加BAR

8 MMIO 操作

mmio区域提供读写回调, 还可以添加额外的限制

1 指定设备访问的最大和最小地址大小

.valid.min_access_size,
.valid.max_access_size

2 对齐要求
.valid.unaligned 表示该设备支持未对齐的访问

3 实现的访问大小

.impl.min_access_size, .impl.max_access_size

4 .impl.unaligned 实现的对齐要求

5 .old_mmio 老式的内存访问

发布了113 篇原创文章 · 获赞 22 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/woai110120130/article/details/101606578