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