0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

頁表創(chuàng)建源碼分析RISC-V Linux

麥辣雞腿堡 ? 來源:嵌入式Linux充電站 ? 作者:Vincent ? 2023-10-08 11:22 ? 次閱讀

頁表創(chuàng)建源碼分析

RISC-V Linux啟動,經(jīng)歷了兩次頁表創(chuàng)建過程,第一次使用C函數(shù)setup_vm()創(chuàng)建臨時頁表,第二次使用C函數(shù)setup_vm_final()創(chuàng)建最終頁表。

具體細節(jié)參考代碼中的注釋,下面的代碼省略了一些不重要的部分。

setup_vm()

asmlinkage void __init setup_vm(uintptr_t dtb_pa)
{
 uintptr_t va, pa, end_va;
 uintptr_t load_pa = (uintptr_t)(&_start);
 uintptr_t load_sz = (uintptr_t)(&_end) - load_pa;
 uintptr_t map_size;
 //load_pa就是kernel加載的其實物理地址
    //load_sz就是kernel的實際大小

    //page_offset就是kernel的起始物理地址對應的虛擬地址,va_pa_offset是他們的偏移量
 va_pa_offset = PAGE_OFFSET - load_pa;
    
    //計算得到kernel起始物理地址的物理頁,PFN_DOWN是將物理地址右移12位,因為sv39的物理地址的低12位是pa_offset,所以右移12位,得到pfn
 pfn_base = PFN_DOWN(load_pa);

 map_size = PMD_SIZE;//PMD_SIZE為2M,在當前,map_size只能為PGDIR_SIZE或PMD_SIZE。這時kernel默認不允許建立PTE。

 //檢查PAGE_OFFSET是否1G對齊,以及kernel入口地址是否2M對齊
 BUG_ON((PAGE_OFFSET % PGDIR_SIZE) != 0);
 BUG_ON((load_pa % map_size) != 0);

    //allc_pte_early里面是BUG(),對于臨時頁表,kernel不允許我們建立PTE
 pt_ops.alloc_pte = alloc_pte_early;
 pt_ops.get_pte_virt = get_pte_virt_early;
#ifndef __PAGETABLE_PMD_FOLDED
 pt_ops.alloc_pmd = alloc_pmd_early;
 pt_ops.get_pmd_virt = get_pmd_virt_early;
#endif
 /* 設置 early PGD for fixmap */
 create_pgd_mapping(early_pg_dir, FIXADDR_START,
      (uintptr_t)fixmap_pgd_next, PGDIR_SIZE, PAGE_TABLE);


 /* 設置 fixmap PMD */
 create_pmd_mapping(fixmap_pmd, FIXADDR_START,
      (uintptr_t)fixmap_pte, PMD_SIZE, PAGE_TABLE);
 /* 設置 trampoline PGD and PMD */
 create_pgd_mapping(trampoline_pg_dir, PAGE_OFFSET,
      (uintptr_t)trampoline_pmd, PGDIR_SIZE, PAGE_TABLE);
 create_pmd_mapping(trampoline_pmd, PAGE_OFFSET,
      load_pa, PMD_SIZE, PAGE_KERNEL_EXEC);

 /*
  * 設置覆蓋整個內核的早期PGD,這將使我們能夠達到paging_init()。
  * 稍后在下面的 setup_vm_final() 中映射所有內存。
  */
 end_va = PAGE_OFFSET + load_sz;
 for (va = PAGE_OFFSET; va < end_va; va += map_size)
  create_pgd_mapping(early_pg_dir, va,
       load_pa + (va - PAGE_OFFSET),
       map_size, PAGE_KERNEL_EXEC);

 /* 為dtb創(chuàng)建早期的PMD */
 create_pgd_mapping(early_pg_dir, DTB_EARLY_BASE_VA,
      (uintptr_t)early_dtb_pmd, PGDIR_SIZE, PAGE_TABLE);
 /* 為 FDT 早期掃描創(chuàng)建兩個連續(xù)的 PMD 映射 */
 pa = dtb_pa & ~(PMD_SIZE - 1);
 create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA,
      pa, PMD_SIZE, PAGE_KERNEL);
 create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA + PMD_SIZE,
      pa + PMD_SIZE, PMD_SIZE, PAGE_KERNEL);
 dtb_early_va = (void *)DTB_EARLY_BASE_VA + (dtb_pa & (PMD_SIZE - 1));
 ......

}

setup_vm()在最開始就進行了kernel入口地址的對齊檢查,要求入口地址2M對齊。假設內存起始地址為0x80000000,那么kernel只能放在0x80000000、0x80200000等2M對齊處。為什么會有這種對齊要求呢?

我猜測單純是為給opensbi預留了2M空間,因為kernel之前還有opensbi,而opensbi運行完之后,默認跳轉地址就是偏移2M,kernel只是為了跟opensbi對應,所以設置了2M對齊。

那opensbi需要占用2M這么大?實際上只需要幾百KB,因此opensbi和kernel中間有一段內存是空閑的,沒有人使用。這個問題我們下篇再講。

setup_vm_final()

在該函數(shù)中開始為整個物理內存做內存映射,通過swapper頁表來管理,并且清除掉匯編階段的頁表。

static void __init setup_vm_final(void)
{
 uintptr_t va, map_size;
 phys_addr_t pa, start, end;
 u64 i;

 /**
  * 此時MMU已經(jīng)開啟,但是頁表還沒完全建立。
  */
 pt_ops.alloc_pte = alloc_pte_fixmap;
 pt_ops.get_pte_virt = get_pte_virt_fixmap;
#ifndef __PAGETABLE_PMD_FOLDED
 pt_ops.alloc_pmd = alloc_pmd_fixmap;
 pt_ops.get_pmd_virt = get_pmd_virt_fixmap;
#endif
 /* Setup swapper PGD for fixmap */
 create_pgd_mapping(swapper_pg_dir, FIXADDR_START,
      __pa_symbol(fixmap_pgd_next),
      PGDIR_SIZE, PAGE_TABLE);

 /* 為整個物理內存創(chuàng)建頁表 */
 for_each_mem_range(i, &start, &end) {
  if (start >= end)
   break;
  if (start <= __pa(PAGE_OFFSET) &&
      __pa(PAGE_OFFSET) < end)
   start = __pa(PAGE_OFFSET);

        //best_map_size是選擇合適的映射大小,kernel入口地址2M對齊或者kernel大小能被2M整除時,map_size就是2M,否則就是4K。
  map_size = best_map_size(start, end - start);
  for (pa = start; pa < end; pa += map_size) {
   va = (uintptr_t)__va(pa);
   create_pgd_mapping(swapper_pg_dir, va, pa,
        map_size, PAGE_KERNEL_EXEC);
  }
 }

 /* 清除fixmap的PMD和PTE */
 clear_fixmap(FIX_PTE);
 clear_fixmap(FIX_PMD);

 /* 切換到swapper頁表,這個是最終的頁表,匯編階段relocate開啟MMU的操作,跟下面這句是一樣的。 */
 csr_write(CSR_SATP, PFN_DOWN(__pa_symbol(swapper_pg_dir)) | SATP_MODE);
 local_flush_tlb_all();//刷新TLB

 ......
}

說明:

在setup_vm_final()函數(shù)中,通過swapper_pg_dir頁表來管理整個物理內存的訪問。并且清除匯編階段的頁表fixmap_pte和early_pg_dir。(本質上就是把該頁表項的內容清0,即賦值為0)

最終把swapper_pg_dir頁表的物理地址賦值給SATP寄存器。這樣CPU就可以通過該頁表訪問整個物理內存。

切換頁表通過如下實現(xiàn):

csr_write(CSR_SATP,PFN_DOWN(_pa(swapper_pg_dir))|SATP_MODE);

在swapper_pg_dir管理的kernel space中,其虛擬地址與物理地址空間的偏移是固定的,為va_pa_offset(定義在arch/riscv/mm/init.c中的一個全局變量)

注意:swapper_pg_dir管理的是kernel space的頁表,即它把物理內存映射到的虛擬地址空間是只能kernel訪問的。user space不能訪問,用戶空間如果訪問,必須自行建立頁表,把物理地址映射到user space的虛擬地址空間。kernel線程共享這個swapper_pg_dir頁表。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11314

    瀏覽量

    209807
  • 代碼
    +關注

    關注

    30

    文章

    4798

    瀏覽量

    68725
  • RISC
    +關注

    關注

    6

    文章

    463

    瀏覽量

    83767
收藏 人收藏

    評論

    相關推薦

    自制RISC-V源碼與設計流程案例分析

    Overlay 簡介 RISC-V-On-PYNQ Overlay實現(xiàn)了在PYNQ-Z2板上的RISC-V處理器及工具鏈集成,并提供了完整的RISC-V源碼與設計流程,得益于PYNQ軟
    的頭像 發(fā)表于 11-08 10:05 ?6616次閱讀
    自制<b class='flag-5'>RISC-V</b><b class='flag-5'>源碼</b>與設計流程案例<b class='flag-5'>分析</b>

    如何使用RISC-V創(chuàng)建自定義處理器

    采用RISC-V來替代其自定義體系結構。該體系結構吸引人的一個關鍵特征是,CPU開發(fā)人員可以在不犧牲為基本標準創(chuàng)建的工具和庫的適用性的情況下,使RISC-V功能適應其需求。適應的關鍵在于了解R
    的頭像 發(fā)表于 04-14 15:55 ?4168次閱讀
    如何使用<b class='flag-5'>RISC-V</b><b class='flag-5'>創(chuàng)建</b>自定義處理器

    RISC-V能否復制Linux 的成功?》

    的產(chǎn)品,Linux成為開源軟件發(fā)展的基石。 這種成功是否可以復制到開源硬件上呢?RISC-V這樣的指令集架構(ISA)是否也可以像Linux內核作為開源軟件的基礎一樣,成為開源硬件發(fā)展的基石呢? 這個
    發(fā)表于 11-26 20:20

    基于risc-v架構的芯片與linux系統(tǒng)兼容性討論

    一直對基于RISC-V架構的芯片與Linux系統(tǒng)的兼容性比較感興趣,查了各種資料,眾說紛紜,在此整理一下學習內容,以備后用。這個復雜而重要的話題,涉及多個方面的考量。下面談談我的學習總結
    發(fā)表于 11-30 17:20

    科普RISC-V生態(tài)架構(認識RISC-V)

    RISC-V,逐步完成全線產(chǎn)品遷移到RISC-V定制架構;MicroSemi提供基于Risc-V+Linux+CNN加速器的AI解決方案;印度***則大力資助基于RISC-V的處理器項
    發(fā)表于 08-02 11:50

    分析RISC-V架構的不同之處

    0 RISC-V和其他開放架構有何不同如果僅從“免費”或“開放”這兩點來評判,RISC-V架構并不是第一個做到免費或開放的處理器架構。在開始之前,我們先通過論述幾個具有代表性的開放架構,來分析
    發(fā)表于 07-26 06:58

    RISC-V-Reader-Chinese-v2p1 RISC-V手冊(中文) RISC-V開源指令集的指南

    RISC-V 手冊 一本開源指令集的指南 本書是由 RISC-V 設計者 DAVID PATTERSON等親自寫的書。書寫的非常精彩,和Risc-V一樣非常簡潔明了,沒有廢話,書本身也不厚,114
    發(fā)表于 04-22 18:04

    【轉載】RISC-V 能打 50 年!risc-v 現(xiàn)在和未來的發(fā)展

    人性化的設定,大大降低了芯片的研究門檻,使 RISC-V 被譽為“半導體行業(yè)的 Linux”,廣受開發(fā)者歡迎。據(jù)分析機構 Semico Research 的《RISC-V 市場
    發(fā)表于 02-27 20:02

    我了解的RISC-V

    幾十條指令。 RISC-V的規(guī)范文檔僅有145,而“特權架構文檔”的篇幅也僅為91。 易于移植*nix 現(xiàn)代操作系統(tǒng)都做了特權級指令和用戶級指令的分離,特權指令只能操作系統(tǒng)調用,而用戶級指令才能
    發(fā)表于 03-19 10:52

    RISC-V的商業(yè)模式,國內外RISC-V的產(chǎn)業(yè)生態(tài)分析

    RISC-V將是智慧物聯(lián)網(wǎng)創(chuàng)新發(fā)展的新機遇,本文首先介紹了什么是指令集架構,其次介紹了RISC-V的歷史機遇、RISC-V的商業(yè)模式及中國CPU的未來展望,最后對國內外RISC-V的發(fā)
    的頭像 發(fā)表于 06-22 16:14 ?3557次閱讀
    <b class='flag-5'>RISC-V</b>的商業(yè)模式,國內外<b class='flag-5'>RISC-V</b>的產(chǎn)業(yè)生態(tài)<b class='flag-5'>分析</b>

    RISC-V的挑戰(zhàn)分析

    雖然RISC-V相關的開發(fā)和應用方面已經(jīng)取得了長足進步,但前面還有一些障礙。普林斯頓大學的研究人員發(fā)現(xiàn)了RISC-V開源處理器內核的一些缺陷,他們認為這些缺陷關系重大。他們發(fā)現(xiàn),在RISC-V處理器
    的頭像 發(fā)表于 02-25 09:41 ?2805次閱讀

    RISC-VRISC-V AI的未來(特邀講座)

    主題演講:RISC-VRISC-V AI的未來(特邀講座)ppt分享
    發(fā)表于 07-14 17:15 ?16次下載

    RISC-V設計支持工具,支持RISC-V技術的基礎

    RISC-V設計支持工具,支持RISC-V技術的基礎 ppt分享
    發(fā)表于 07-14 17:15 ?12次下載

    Arch Linux RISC-V 端口及相關作品簡介

    Arch Linux RISC-V 端口及相關作品簡介 演講ppt分享
    發(fā)表于 07-17 16:34 ?6次下載

    RISC-V Linux創(chuàng)建

    臨時分析 MMU開啟前,需要建立好kernel、dtb、trampoline等。以便MMU開啟后,并且在內存管理模塊運行之前,ker
    的頭像 發(fā)表于 10-08 11:14 ?601次閱讀