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