首頁 >運維 >linux運維 >實戰 | RISC-V Linux入口位址2M預留記憶體優化

實戰 | RISC-V Linux入口位址2M預留記憶體優化

嵌入式Linux充电站
嵌入式Linux充电站轉載
2023-08-01 15:37:591212瀏覽

上篇分析了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處。

記憶體分佈示意圖如下:

實戰 | RISC-V Linux入口位址2M預留記憶體優化

對於kernel來說,在啟動時會從自己的kernel載入位址處(即0x200000)開始建立頁表映射,只有對物理記憶體建立了頁表映射,後面才能存取這些記憶體。而kernel載入位址前面的2M記憶體(即0x0 - 0x200000)將被kernel忽略,不會對這2M記憶體建立頁表,即kernel無法存取這2M記憶體。

在QEMU上RISC-V Linux的啟動資訊:

實戰 | RISC-V Linux入口位址2M預留記憶體優化

#但opensbi實際上不需要使用2M這麼大的範圍,預設是512KB ,opensbi的pmp會保護這512KB內存,不讓其他程式存取。

實戰 | RISC-V Linux入口位址2M預留記憶體優化

因此在Kernel和opensbi之間會存在1.5M的記憶體空隙,而這部分記憶體空隙沒有程式使用,這就會造成記憶體浪費,那要如何讓kernel將前面的一部分記憶體也利用起來呢?

優化方案

#對這2M記憶體的最佳化,有兩個方案:

#方案一:將opensbi放到記憶體的最後面,kernel入口位址仍保持2M對齊。

方案二:opensbi仍放到記憶體的起始位置,透過修改核心原始碼,解除2M對齊限制,即可將kernel位址往前移。

方案一

我們將opensbi放到記憶體的最後面,kernel入口位址仍然保持2M對齊。

即kernel放到記憶體的最前面,opensbi放到後面:

實戰 | RISC-V Linux入口位址2M預留記憶體優化

#例如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對齊檢查:

實戰 | RISC-V Linux入口位址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);
}
實戰 | RISC-V Linux入口位址2M預留記憶體優化

整個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);
}
實戰 | RISC-V Linux入口位址2M預留記憶體優化
實戰 | RISC-V Linux入口位址2M預留記憶體優化

透過以上的程式碼修改,就能將Kernel入口位址往前挪1.5M,只給opensbi預留512KB,這樣RISC-V Linux啟動之後,可用實體記憶體就會增加。

實戰 | RISC-V Linux入口位址2M預留記憶體優化

總結

#RISC-V Linux入口位址2M對齊的操作目前還沒看到有人解釋,不過應該就是為了預留給opensbi2M,於是kernel只建立了二級頁表,使得入口位址必須2M對齊。對這部分記憶體的最佳化解決方案,目前也沒人給出,希望本文的優化方案能夠幫助到有些人,也希望能夠給大家一些啟發。

#

以上是實戰 | RISC-V Linux入口位址2M預留記憶體優化的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:嵌入式Linux充电站。如有侵權,請聯絡admin@php.cn刪除