山东大学RISC-V公共开放平台开发记录5
山東大學RISC-V公共開放平臺開發記錄
4移植Linux操作系統
4.1移植前相應知識
4.1.1特權等級與操作系統的關系
現代操作系統(如Linux、Windows等)為了權限的區分都區分為用戶態和內核態。一般來說用戶程序都運行在用戶態,當它們需要切換到內核態以獲得更高權限時,需要向操作系統申請;而操作系統內核和設備驅動程序則默認就運行在內核態。每個用戶線程也擁有兩個棧,一個是用戶態棧,一個是內核態棧,分別當處于用戶態和內核態時使用。
操作系統的用戶態和內核態對應到處理器的硬件層面上,即為不同的特權等級。在RISC-V中,用戶態一般對應User Mode,而內核態一般對應Supervisor Mode。
4.1.2用戶態切換到內核態的方式
一般來說用戶程序一直運行在用戶態中,只有當發生了一下三種事件之一時才會轉到內核態中:
\1. 系統調用,即應用程序使用操作系統提供的接口調用內核功能;
\2. 異常,當應用程序運行時出現異常時(比如最常見的缺頁異常)也會切換到內核態進行處理;
\3. 外部中斷,最常見的情況為當外設(如磁盤、網絡適配器)完成用戶請求時會向處理器發出中斷,此時操作系統會暫停當前的程序運行從而轉移到內核態處理這些事件。
4.1.3RISC-V特權指令與CSR
在RISC-V ISA Specification中的用戶指令中,這些用戶指令主要是完成數值計算、內存存取和分支跳轉等“具體任務”。與此同時,在RISC-V Specification中的第二部分,即Previleged Architectures中,還規定了一些特權指令,這些指令有的在用戶指令中也出現過(比如ECALL和EBREAK),這些指令在不同特權等級下執行會有不同的效果;另一些指令僅在指定環境中執行(比如WFI只能在Machine模式下執行)。同時還有一些CSR只能在對應的(或者更高等級的)特權等級中訪問。
接下來將簡介幾個特權指令以及特權CSR。
4.1.4特權指令舉例
ECALL指令用于向執行環境發出請求,在不同的特權等級中執行ECALL指令有不同的效果:在User Mode中會引發environment-call-from-U-mode異常,在Supervisor Mode中會引發environment-call-from-S-mode異常, 而在Machine Mode中會引發environment-call-from-M-mode異常。
這條指令在Linux中用于系統調用,如下為arch/riscv/kernel/sbi.c中的部分代碼,使用ECALL指令時,將異常類型寫在a7寄存器, 參數寫在a0-a5寄存器,后面會根據異常類型的不同調用不同的異常處理函數。
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,unsigned long arg1, unsigned long arg2,unsigned long arg3, unsigned long arg4,unsigned long arg5){struct sbiret ret;register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);asm volatile ("ecall": "+r" (a0), "+r" (a1): "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7): "memory");ret.error = a0;ret.value = a1;return ret;}比如實現一個putchar函數用于打印一個字符到系統控制臺上,就如下通過ECALL來實現
void sbi_console_putchar(int ch){sbi_ecall(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, ch, 0, 0, 0, 0, 0);}EBREAK指令與調試模式有關,通過該指令將執行流返回到調試器中,執行該指令會引發breakpoint exception。
WFI指令全稱Wait For Interrupt,它會將該Hart陷入等待狀態,直到有中斷觸發為止。在下文介紹系統啟動過程時,會用到該指令陷入自旋,等待fesvr將程序代碼準備好并觸發中斷開始執行。
4.1.5特權CSR舉例
上文中的mstatus就是特權CSR中非常重要的一個,接下來再舉例幾個典型的特權CSR。
mtvec與(Machine模式的)trap處理函數向量有關。它的底兩位為MODE,其余位為字對其的Base地址。
當Mode為0時為直接模式,所有的Trap均將PC指向Base地址;當Mode為1時為向量模式,發生Trap時PC設置為Base+4*cause。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9BPayFZl-1654330970635)(file:///C:/Users/Jeremiah/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]
mtvec的字段
與此類似的還有stvec,指的是Supervisor模式下的Trap處理向量。
上文的cause需要我們的mcause寄存器出場解釋。當Trap發生時,mcause中會寫入Trap原因的編號。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NDqqffs1-1654330970636)(file:///C:/Users/Jeremiah/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg)]mcause的字段
mcause的最高位為Interrupt,當該Trap是由中斷觸發時,Interrupt置為1。
當然還有對應的scauseCSR,代表著Supervisor模式下的Trap原因。
4.2在RISC-V上啟動Linux內核
作為一個“成熟”(主要指生態建設方面)指令集的標志是在Linux內核代碼倉庫的arch目錄中擁有一席之地,riscv-linux(即Linux內核的RISC-V移植)到目前位置已經合并到Linux代碼倉庫中。
接下來介紹在RISC-V平臺上啟動Linux內核的過程。
4.2.1Linux內核如何感知與操作硬件
由于RISC-V的硬件與之前的其它指令集的硬件均不同,最關鍵的問題就是如何讓Linux內核感知硬件并能夠操控具體的硬件?有兩個方法可以滿足這個需求:
通過設備樹的方式。這里簡要介紹一下設備樹,它是一種描述操作系統使用的硬件的一種格式,包括內存地址映射,CPU核的配置以及外設等。設備樹文件目前寫在Linux內核代碼的arch/riscv/boot/dts(以RISC-V為例)中,包括一個.dts文件和多個.dtsi文件,它們經過編譯形成設備樹。.dtsi文件大概是SoC的一些公共特性(比如各種型號的CPU核,包括他們的指令集、Cache大小等以及中斷控制器、PWM等外設),而.dts文件則包括的是具體某個板級系統的組成(即將.dtsi中定義的各項模塊組合起來)。
通過SBI(Supervisor Binary Interface)的方式。RISC-V的SBI是具體平臺的固件與正在運行的操作系統之間的接口,用于Machine模式與Supervisor模式進行交互(讀者應該比較熟悉ABI,即Application Binary Interface,它規定了數據類型的大小、布局、調用約定與系統調用、目標文件的二進制格式等。滿足一個完整ABI的應用程序應該無需重新編譯即可在其它支持此ABI的操作系統上運行,可以類比的是,滿足一個完整SBI的操作系統應該可以無需修改直接運行在任何支持該SBI的硬件平臺上),操作系統可以通過SBI進行硬件方面的調用。 RISC-V的SBI文檔在riscv-sbi-doc中,目前還在持續更新。其中大概包括了操作系統對于定時器、中斷、緩存相關fence指令的執行、CPU核心狀態的控制等接口。其參考實現為opensbi,開發人員可以參考其實現方式根據需求實現自己版本的SBI。
4.2.2內核啟動之前
本節介紹基于RISC-V的SoC中從上電到Linux內核啟動之前發生的事情。
以Chipyard SoC為例,Chipyard中包含了一個名為BootROM的部分,它存儲了系統上電后首先執行的指令以及設備樹的信息。BootROM的匯編代碼編寫在bootrom.S中。 閱讀該代碼后可以發現,BootROM所做的事情就是啟動一個hart(硬件線程),休眠其它hart(即使其自旋),在啟動的hart中設置中斷開關,并陷入wfi,以等待程序被加載好后觸發的中斷。
如果是裸機程序,那么這里由fesvr將待執行的程序加載到內存后,fesvr會觸發中斷,Chipyard就會開始執行用戶程序。
如果想啟動Linux內核,那么這時就需要第二級的BootLoader,目前使用最廣泛的是Berkeley實現的BBL(Berkeley Boot Loader),在內核啟動之前,BBL會做如下事項:
選擇一個hart作為主線程,其它hart均休眠,等待Linux內核準備好后再喚醒他們
把BootROM中的設備樹信息經過過濾(省去一些和Linux無關的內容,比如平臺相關的功耗控制設備等)后傳遞給Linux內核
喚醒其它線程,設置它們的PMP(允許Supervisor模式訪問所有內存區域),trap處理向量并進入Supervisor模式
執行mret指令進入Supervisor Mode
開始執行Linux內核
4.2.3內核剛開始啟動
內核剛開始啟動時,要求系統做好如下準備: 1. a0寄存器存儲hartID,即CSR mhartid中存儲的值,這個hartID將被映射到Linux的CPU ID 2. a1存儲的是指向設備樹的地址 3. 內存地址是直接映射的,沒有開啟分頁 4. 內核的ELF執行映像已經加載完成
然后在start_kernel(即內核入口函數)被調用之前,完成下面的過程: 1. 利用PAGE_OFFSET對整個內存地址進行劃分(劃分為內核空間和用戶空間) 2. 啟用分頁 3. 設立C運行時環境,建立棧和全局指針 4. 設置處理啟動早期異常的trap向量 5. 調用start_kernel,啟動內核
4.2.4setup_arch
進入start_kernel以后,處理器運行的代碼就從“匯編”變成了“C”,但這并不意味著從此以后全是指令集無關的部分了。 在start_nernel中,還有一個名為setup_arch的函數這個大塊頭,它是和指令集密切相關的。
在RISC-V的系統中,setup_arch進行了如下初始化工作: 1. 如果SBI實現了console驅動,則啟動EARLY_PRINTKconsole。在RISC-V中一般都會啟動EARLY_PRINTK這樣一來用戶在啟動早期就能看到日志輸出 2. 處理內核命令行參數中的體系結構相關的部分,在RISC-V中通常只有內存大小相關參數 3. 解析設備樹中的內存,釋放出來給內核使用 4. 初始化內存管理子系統,包括初始化Zero Page和不同的內存區(包括Zone_DMA, Zone_Normal和Zone_Highmem)。RISC-V這里只支持了Zone_Normal,比較簡單 5. 喚醒其它hart 6. 從設備樹中讀取處理器的ISA,并寫入ELF的hwcap字段中,以告知應用程序它們正在運行在怎樣的處理器上。
4.2.5其他啟動方式
和SiFive使用BBL不同,平頭哥使用U-boot作為啟動程序,但只是和上文內容略有區別,平頭哥的處理器系統啟動Linux內核的過程如下圖所示。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-g51lSb5L-1654330970637)(file:///C:/Users/Jeremiah/AppData/Local/Temp/msohtmlclip1/01/clip_image006.gif)]
和上文Chipyard中的BootROM類似,平頭哥也在SoC中嵌入了ROM,用于存儲啟動代碼。 在上電時,ROM中的程序從外部介質(eMMC或Flash)將U-boot SPL讀取到片上SRAM中運行,U-boot SPL全稱為(U-boot Second Program Loader),它運行在系統啟動的第一階段,用于初始化DDR以及各種外設。
之后U-boot可以從網絡或者Flash中加載Linux內核映像到DDR中,并開始啟動內核。
到此為止,Linux內核啟動的過程中與RISC-V體系結構相關的部分就介紹完畢。
4.3RISC-V Linux 內核使用的虛擬內存布局。
RISC-V S-mode 虛擬地址翻譯分幾種方式Sv32, Sv39 和Sv48。
其中Sv39 和 Sv48 為RV64 設計。為了更直觀的理解地址翻譯過程,我們給出 SV39 地址轉換的全過程圖示
C-V體系結構相關的部分就介紹完畢。
4.3RISC-V Linux 內核使用的虛擬內存布局。
RISC-V S-mode 虛擬地址翻譯分幾種方式Sv32, Sv39 和Sv48。
其中Sv39 和 Sv48 為RV64 設計。為了更直觀的理解地址翻譯過程,我們給出 SV39 地址轉換的全過程圖示
總結
以上是生活随笔為你收集整理的山东大学RISC-V公共开放平台开发记录5的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 命格算法,具有“一夜暴富”天赋的
- 下一篇: 前端关于添加一个带有数字圈,类似手机端有