/* 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插入