上篇分析了RISC-V Linux啟動的頁表創建,提到RISC-V Linux入口地址必須2M對齊,今天講講如何解決2M對齊的問題,或者說如何優化部分內存。
注意:本文基于linux5.10.111內核
內存占用分析
每顆芯片在出廠時,其bootrom就已經固化在芯片內部,假設bootrom的地址是0x0,即上電后,會從0x0地址處開始運行程序。
在啟動RISC-V Linux之前,需要先運行opensbi,因此應該把opensbi放到地址0x0
處,這樣芯片上電后,就會從0x0
地址處執行opensbi。在opensbi運行完后,會跳轉到opensbi運行地址偏移2M的位置去執行下一級boot(這里下一級boot是kernel),即跳轉到0x200000
地址處運行kernel,因此應該把kernel放到內存的0x200000
處。
內存分布示意圖如下:
對于kernel來說,在啟動時會從自己的kernel加載地址處(即0x200000
)開始建立頁表映射,只有對物理內存建立了頁表映射,后面才能訪問這些內存。而kernel加載地址前面的2M內存(即0x0 - 0x200000
)將被kernel忽略,不會對這2M內存建立頁表,即kernel無法訪問這2M內存。
在QEMU上RISC-V Linux的啟動信息:
但opensbi實際不需要使用2M這么大的范圍,默認是512KB
,opensbi的pmp會保護這512KB
內存,不讓其他程序訪問。
因此在Kernel和opensbi之間會存在1.5M
的內存空隙,并且這部分內存空隙沒有程序使用,這就會造成內存浪費,那如何讓kernel將前面的一部分內存也利用起來呢?
優化方案
對這2M內存的優化,有兩種方案:
方案一:將opensbi放到內存的最后面,kernel入口地址仍然保持2M對齊。
方案二:opensbi仍然放到內存的起始位置,通過修改內核源碼,解除2M對齊限制,即可將kernel地址往前挪。
方案一
我們將opensbi放到內存的最后面,kernel入口地址仍然保持2M對齊。
即kernel放到內存的最前面,opensbi放到后面:
例如kernel放到內存的0x0
地址處,opensbi放到內存的0x10000000
地址處。這樣kernel前面就不會有預留內存,只不過這樣需要修改bootrom的地址,將地址從0x0
修改為0x0x10000000
。這種方案只適合芯片還沒出廠前,因為用戶無法修改bootrom的地址,芯片出廠后,bootrom地址是固定的,假設bootrom地址為0x0
,那么芯片上電后,就會從0x0
開始運行程序,所以opensbi必須放到0x0
地址處,這樣必然kernel只能往后偏移2M。
方案二
我們也可以修改RISC-V Linux的內核源碼,解除2M對齊的限制。我們只需要在setup_vm()
函數中,將原來的二級頁表改為三級頁表,這樣kernel入口地址只需要4K對齊,因此就能將kernel往前挪,從而利用前面的內存。
修改代碼
路徑:arch/riscv/mm/init.c
注釋原來的2M對齊檢查:
對kernel的前2M頁表映射由二級頁表改為三級頁表:
//新增一個PTE pte_t trampoline_pte[PTRS_PER_PTE] __page_aligned_bss; create_pgd_mapping(trampoline_pg_dir,PAGE_OFFSET, (uintptr_t)trampoline_pmd,PGDIR_SIZE,PAGE_TABLE); create_pmd_mapping(trampoline_pmd,PAGE_OFFSET, (uintptr_t)trampoline_pte,PMD_SIZE,PAGE_TABLE); end_va = PAGE_OFFSET + PMD_SIZE; for (va = PAGE_OFFSET; va < end_va; va += PAGE_SIZE) { create_pte_mapping(trampoline_pte,PAGE_OFFSET, load_pa + (va - PAGE_OFFSET), PAGE_SIZE,PAGE_KERNEL_EXEC); }
登錄后復制
對整個kernel的頁表映射由二級頁表改為三級頁表:
假設kernel大小為4M+
//定義三個PTE pte_t load_sz_pte[PTRS_PER_PTE] __page_aligned_bss; pte_t load_sz_pte1[PTRS_PER_PTE] __page_aligned_bss; pte_t load_sz_pte2[PTRS_PER_PTE] __page_aligned_bss; //=======0-2M====== create_pgd_mapping(early_pg_dir,PAGE_OFFSET, (uintptr_t)early_pmd,PGDIR_SIZE,PAGE_TABLE); create_pmd_mapping(early_pmd,PAGE_OFFSET, (uintptr_t)load_sz_pte,PMD_SIZE,PAGE_TABLE); end_va = PAGE_OFFSET + PMD_SIZE; for (va = PAGE_OFFSET; va < end_va; va += PAGE_SIZE) { create_pte_mapping(load_sz_pte,PAGE_OFFSET, load_pa + (va - PAGE_OFFSET), PAGE_SIZE,PAGE_KERNEL_EXEC); } //=======2-4M========== create_pgd_mapping(early_pg_dir,PAGE_OFFSET + PMD_SIZE, (uintptr_t)early_pmd,PGDIR_SIZE,PAGE_TABLE); create_pmd_mapping(early_pmd,PAGE_OFFSET, (uintptr_t)load_sz_pte1,PMD_SIZE,PAGE_TABLE); end_va = PAGE_OFFSET + (PMD_SIZE * 2); for (va = PAGE_OFFSET + PMD_SIZE; va < end_va; va += PAGE_SIZE) { create_pte_mapping(load_sz_pte1,va, load_pa + (va - PAGE_OFFSET), PAGE_SIZE,PAGE_KERNEL_EXEC); } //=======4-6M========== create_pgd_mapping(early_pg_dir,PAGE_OFFSET + (PMD_SIZE*2), (uintptr_t)early_pmd,PGDIR_SIZE,PAGE_TABLE); create_pmd_mapping(early_pmd,PAGE_OFFSET, (uintptr_t)load_sz_pte2,PMD_SIZE,PAGE_TABLE); end_va = PAGE_OFFSET + (PMD_SIZE * 3); for (va = PAGE_OFFSET + (PMD_SIZE*2); va < end_va; va += PAGE_SIZE) { create_pte_mapping(load_sz_pte2,va, load_pa + (va - PAGE_OFFSET), PAGE_SIZE,PAGE_KERNEL_EXEC); }
登錄后復制
通過以上的代碼修改,就能將Kernel入口地址往前挪1.5M,只給opensbi預留512KB,這樣RISC-V Linux啟動之后,可用物理內存就會增加。
總結
RISC-V Linux入口地址2M對齊的操作目前還沒看到有人解釋,不過應該就是為了給opensbi預留2M,于是kernel只建立了二級頁表,使得入口地址必須2M對齊。對這部分內存的優化解決方案,目前也還沒人給出,希望本文的優化方案能夠幫助到有些人,也希望能夠給大家一些啟發。
以上就是實戰 | RISC-V Linux入口地址2M預留內存優化的詳細內容,更多請關注www.92cms.cn其它相關文章!