qemu内存模型(4) 内存渲染过程(MemoryRegion到FlatView)

/* Render a memory region into the global view.  Ranges in @view obscure
 * ranges in @mr.
 */
 622 static void render_memory_region(FlatView *view,
 623                                  MemoryRegion *mr,
 624                                  Int128 base,
 625                                  AddrRange clip,
 626                                  bool readonly)
 627 {
 628     MemoryRegion *subregion;
 629     unsigned i;
 630     hwaddr offset_in_region;
 631     Int128 remain;
 632     Int128 now;
 633     FlatRange fr;
 634     AddrRange tmp;
 635
 636     if (!mr->enabled) {
 637         return;
 638     }
 639
 640     int128_addto(&base, int128_make64(mr->addr));
 641     readonly |= mr->readonly;
 642
 643     tmp = addrrange_make(base, mr->size);
 644
 645     if (!addrrange_intersects(tmp, clip)) {
 646         return;
 647     }
 648
 649     clip = addrrange_intersection(tmp, clip);
 650
 651     if (mr->alias) {
 652         int128_subfrom(&base, int128_make64(mr->alias->addr));
 653         int128_subfrom(&base, int128_make64(mr->alias_offset));
 654         render_memory_region(view, mr->alias, base, clip, readonly);
 655         return;
 656     }
 657
 658     /* Render subregions in priority order. */
 659     QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
 660         render_memory_region(view, subregion, base, clip, readonly);
 661     }
 662
 663     if (!mr->terminates) {
 664         return;
 665     }
 666
 667     offset_in_region = int128_get64(int128_sub(clip.start, base));
 668     base = clip.start;
 669     remain = clip.size;
 670
 671     fr.mr = mr;
 672     fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
 673     fr.romd_mode = mr->romd_mode;
 674     fr.readonly = readonly;
 675
 676     /* Render the region itself into any gaps left by the current view. */
 677     for (i = 0; i < view->nr && int128_nz(remain); ++i) {
 678         if (int128_ge(base, addrrange_end(view->ranges[i].addr))) {
 679             continue;
 680         }
 681         if (int128_lt(base, view->ranges[i].addr.start)) {
 682             now = int128_min(remain,
 683                              int128_sub(view->ranges[i].addr.start, base));
 684             fr.offset_in_region = offset_in_region;
 685             fr.addr = addrrange_make(base, now);
 686             flatview_insert(view, i, &fr);
 687             ++i;
 688             int128_addto(&base, now);
 689             offset_in_region += int128_get64(now);
 690             int128_subfrom(&remain, now);
 691         }
 692         now = int128_sub(int128_min(int128_add(base, remain),
 693                                     addrrange_end(view->ranges[i].addr)),
 694                          base);
 695         int128_addto(&base, now);
 696         offset_in_region += int128_get64(now);
 697         int128_subfrom(&remain, now);
 698     }
 699     if (int128_nz(remain)) {
 700         fr.offset_in_region = offset_in_region;
 701         fr.addr = addrrange_make(base, remain);
 702         flatview_insert(view, i, &fr);
 703     }
 704 }

这段代码理解起来比较困难,主要做的事情是把树状的MemoryRegion渲染成 一段段FlatRang插入的Flatview中.

要理解这个操作首先要说下理解qemu的内存建模(参考 qemu 内存模型(1)—文档)

首先MemoryRegion有两种,一种为纯容器(逻辑上的概念),一种为物理内存(物理内存又有几种 mmio内存. ram, rom, ioport),

qemu要把MemoryRegion映射到cpu看到的线性地址上(AddressSpace), 但是有些情况两个设备的内存映射到相同的线性地址, 比如mmio. 举个例子,对于一个内存条是4g, 它的线性地址为0-4g, 但是vga设备的内存地址通过mmio映射到线性地址的768k–(768+256k), 这种情况应该使vga的线性地址覆盖掉ram的768k–(768+256k)地址,这样写vga地址才能成功.

FlatRang则表示一个MemoryRegion映射到线性空间的一段地址.

qemu内存建模是通过优先级来实现内存地址的覆盖, 优先级高的MemoryRegion 先被转换为 FlatRang, 后面渲染的时候低优先级的FlatRang不覆盖高优先级的FlatRang, 低优先级的FlatRang如果被高优先级

的FlatRang截断则只保存不被高优先级遮盖的部分到flatview, 这是render_memory_region 的677行到704行的主要工作

MemoryRegion 的terminates 变量如果为false表示它是一个纯容器, 纯容器自身没有线性地址(只规定一个偏移和大小来限制子区域线性地址的取值), 通过子region来生成线性地址, 所以纯容器所规定的线性地址空间

可能留下空洞,会被其他低优先级的MemoryRegion所填充

MemoryRegion的addr 变量表示相对于父容器的偏移, 和大小,

FlatRange的offset_in_region 变量表示该FlatRange相对所在MemoryRegion的位置(因为一个MemoryRegion可能被高优先级的MemoryRegion截断成多段,所以offset_in_region不一定为0)

fr.addr 标示该FlatRange在线性地址空间的偏移

好吧来看下代码

static void render_memory_region(FlatView *view,
623 MemoryRegion *mr,
624 Int128 base,
625 AddrRange clip,
626 bool readonly)
函数的参数为FlatView *view,代表线性空间试图,维护MemoryRegion和 线性地址的关系(用于gpa→hva的转换)

MemoryRegion *mr则为要渲染的MemoryRegion
base 标示该MemoryRegion对应最根层MemoryRegion的偏移(最根层的偏移一般为0 ,现在已知的就system_memory和 system_io两个地址空间, 分别代表内存空间和io空间)
clip 表示父空间的地址范围, 子空间的地址范围是不允许落在父空间的范围外的
readonly 则表示该区域是否为只读的,用于赋值FlatRange

643-649行找出当前MemoryRegion落在父MemoryRegion地址空间内的有效空间

651-656行主要 处理别名的情况, 把别名指向的空间相对于当前地址空间的偏移进行渲染

659-661行则渲染子ragion

663-665 行对于纯容器不生成FlatRange. 所以渲染完子空间就可以了

667-669行 计算mr在线性地址空间的偏移和大小

671-674 设置FlatRange的属性

667-703 则和前边高优先级的MemoryRegion进行比较, 生成FlatRange

我们来详细分析667-703行, 首先要知道FlatRange在FlatView中是按照地址升序来排列的,另外不存在地址重合的FlatRange

首先678-680 行把结束地址小于MemoryRegion开始地址的FlatRange都排除掉,因为他们的地址和我们要生成的地址没有冲突

剩下的地址分为如下四种情况

在这里插入图片描述
base 表示要生成的地址空间的开始 remin表示要生成地址空间的结尾

start表示下一个要比较的地址空间的开始, end表示下一个要比较地址空间的结尾

681-691行处理的情况为 1, 2 , 4 ,

684-687 先把base到 min(remin, start) 这段地址生成一个FlatRange(不和其他地址重合)

将剩余部分地址

688 - 690 行将剩余地址裁剪出来,用于, 对于情况3 其实还有一部分FlatRange可能要生成新的FlatRange(end到remin)

692-697 行主要把要生成的地址和现有的地址重合部分跳过

695 - 698 行 重合的部分跳过,重合的部分生成FlatRange, 最后剩下的部分为现有FlatRange都不包含的部分, 则在 699-703 生成新的FlatRange插入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章