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

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