BeagleV OpenSBI 簡介與說明

文章目錄

本文引用的參考資料: 

BeagleV的OpenSBI github專案程式碼:

https://github.com/starfive-tech/opensbi

WD的SBI說明投影片(OpenSBI最主要由Western Digital Corporation貢獻):

https://riscv.org/wp-content/uploads/2019/12/Summit_bootflow.pdf

RISC-V官方OpenSBI 程式碼

https://github.com/riscv/opensbi

一、前言

OpenSBI是一個介於M-Mode與S-Mode之間的應用界面,引述官方github說明:

The RISC-V Supervisor Binary Interface (SBI) is the recommended interface between:

A platform-specific firmware running in M-mode and a bootloader, a hypervisor or a general-purpose OS executing in S-mode or HS-mode.
A hypervisor running in HS-mode and a bootloader or a general-purpose OS executing in VS-mode.

RISC-V二進制介面(SBI)主要針對以下情況:

1. 在M-mode下運行的特定於平台的韌體,以及在S-mode 或HS-mode 下執行的引導程式,管理程式或通用OS。
2. 在HS-mode下運行的管理程式以及在VS-mode下執行的引導加載程式或通用OS。

其中,M-mode是指Machine Mode,Hypervisor是一個搭載不同作業系統的小程式,HS-mode 或 S-mode是指作業系統Operation System。

但上述說明似乎還是不淺顯易懂,依據筆者的理解,OpsnSBI是SBI規範下的一個實作軟體,他負責調度使用者或作業系統的命令給底層硬體。除此之外,還將各家廠商的平台進行整合,讓OpenSBI不會碎片化。

在更簡單的表示就是作業系統與硬體系統的媒介。

貳、簡介

傳統x86作業系統之系統保護環示意圖

左圖是傳統x86架構中的Privilege Ring。

在傳統的x86架構系統中,CPU為了更高的安全性以及避免系統硬體被惡意程式控制,創造一種中文名為”分級保護域” Privilege Level的架構,依據維基百科節錄:

電腦作業系統提供不同的資源訪問級別。在計算機體系結構中,Rings是由兩個或更多的特權態組成。在一些硬體或者微代碼級別上提供不同特權態模式的CPU架構上,保護環通常都是硬體強制的。Rings是從最高特權級(通常被叫作0級)到最低特權級(通常對應最大的數字) 依序排列。在大多數作業系統中,Ring 0擁有最高特權,並且可以和最多的硬體直接互動(比如CPU,快取記憶體)。

這種架構被證實是可行且有效的,因此如ARM、RISC-V也都採用這種方式來保護電腦的CPU與其他硬體設備。

RISC-V架構系統指令層級與ARM架構之相對關係

在RISC-V架構中,由最低優先權到最高優先權分別是

User mode (U-mode)

Supervisor mode(S-mode)

Hypervisor mode(H-mode/HS-mode)

Firmware(M-mode)

RISC-V的系統優先層級對應到ARM架構如下圖所示:

 

除此之外,在電腦的作業系統架構中,Hypervisor又稱為虛擬機器監視器,是用來建立與執行虛擬機器的軟體、韌體或硬體。

 

 

Hypervisor兩種型態功能示意圖

左圖是Hypervisor Type1與Tpye2 在系統中的腳色。

在我們這個層級,由於還沒有OS在執行,因此我們的Hypervisor是Type1的型態,介於上層作業系統與下層硬體的中介。

RISC-V是一個將過去指令集去蕪存菁的新架構,其泛用性被各家廠商欽賴。然而這將無法避免整個RISC-V架構碎片化的問題(Fragmentation)。為了避免最終RISC-V變成各家廠商坐擁獨立標準的混亂指令集架構,由RISC-V Foundation統一出一個Supervisor規範名為SBI,即”Supervisor Binary Interface”。

參、RISC-V 系統啟動流程

BeagleV開機模式選擇電路

在系統啟動之前,有些開發板會提供啟動模式的選項,可以透過指撥開關或電阻等等方式來實現0與1的硬體訊號。

在BeagleV中,電路圖就有提供BootMode的選擇

 

 

 

傳統系統開機流程圖

傳統的系統開機流程如下:

首先是圖中ROM方塊,又稱作Zero Stage Bootloader

CPU內部的ROM中會存放引導啟動程式,當開發版上電後,系統時脈開始震盪,CPU收到時賣訊號後,便會從這個ROM的位址(如0x08000000)開始讀取啟動程式碼。

這部分的啟動程式包含硬體中斷向量、SRAM的設定參數等等。

接著是LOADER部分,又稱作First Stage Bootloader

當SRAM被設定完成,CPU會把要執行的程式碼由外部的EEPROM或其他儲存媒介載入到SRAM中執行。由於CPU內部的ROM非常珍貴,因此比較主要的開機程序會在SRAM中執行。

這裡會再把板子上的DDRAM做初始化設定,並且載入下一階段的啟動程式。

下一步是RUNTIME程式,其全名是Runtime envoiroment,執行時系統

可以理解成所有應用程式會呼叫到共同程式碼,也就是標準Library。

在Linux系統中可以是glibc等等。

他是一種把半編譯的執行碼在目標機器上執行的環境,其包含SOC的安全設定以及Bootloader的設定等等。在RISC-V架構中,Runtime系統是由OpenSBI實作的。

最後在進入系統之前,要先載入Bootloader。

常見的Bootloader有U-Boot, GRUB, LinuxBoot等等。這層Bootloader做的事情是建立核心的檔案系統、網路設定、以及開機的選項設定等,有些也會加入顯示器設定等周邊裝置的參數。

最後,我們就能夠進入Operation System(OS)來做任何我們想要的操作。

RISC-V與傳統開機流程有些許不同,其中最大的差異是第二階段啟動

 

 

RISC-V系統開機流程圖

RISC-V的開機流程如下:

將傳統系統動流程與RISC-V啟動流程互相比對後,會發現兩者其實大同小異,主要是中間OpenSBI是RISC-V特有的。

雖然ARM架構的系統在啟動時也有專屬的ATF(ARM Trusted Firmware) 做為RUNTIME的腳色,但在這邊OpenSBI還包含整合家廠商不同的韌體,並且規範統一的指令名稱。

肆、OpenSBI特色

OpenSBI特色

OpenSBI 最主要的特色有以下幾點:

1. 分層結構設計使整體通用於各種結構

– 包含描述平台(platform)抽象概念(abstraction)的通用SBI函式庫,大部分被外部韌體或開機引導程序使用,如EDK2等。

– 特定平台函式庫 ,與核心函式庫相當但多了特定平台所需之驅動程式。

– 特定平台韌體參考,提供三種不同型態的RUNTIME韌體。

2.超廣泛的硬體支援

-支援32位元(RV32)與64位元(RV64)

-支援Hypervisor模式

-支援未對齊的加載/儲存處理(Misaligned load/store handling )

(因為未對齊的記憶體或資料位址會造成整體性能下降)

-支援丟失的CSR模擬

-支援使用PMP保護韌體(PMP: Physical Memory Protection)

 

 

 

OpenSBI 層架構

那為什麼RISC-V架構的OpenSBI要這麼講求開發板 (Platform) 的概念呢?

因為RISC-V是一個開源的硬體架構,大家可以不用支付權利金使用指令集以及核心,但這一定會造成各家廠商擁有一定的能力開發後,導致碎片化發生,就是大家擁有各自的客製化指令集,或客製化的IC Layout,最後成為一盤散沙。Android作業系統在開發初期就經歷過類似的事件,一直到後期才逐漸統一。所以要避免碎片化,他們想出一套方法,讓每一個產品自己需要的平台專屬部分獨立出來,共用的部分則保留在主要SPI函式庫中。OpenSBI的資料庫架構如下圖所示:

圖中,最基本的SBI函式庫是libsbi.a,這是每個RISC-V平台都會用到的通用函式庫。

接著,廠商自己的平台會有自己的libplatsbi.a函式庫,最後,再將整個硬體客製化(platform specific)的韌體打包好使用。

 

伍、OpenSBI韌體型態

OpenSBI最後包好的韌體可以有三種型態,這三種型態的韌體都是基於開發板的,其分別是:

1. FW_PAYLOAD

-連結下一啟動階段的韌體

2.FW_JUMP

-可以跳躍到下一啟動階段的靜態位址的韌體

3.FW_DYNAMIC

-可以跳躍到下一啟動階段的動態位址的韌體

接下來會對三種型態的韌體作介紹。

 

FW_PAYLOAD開機流程圖

1.FW_PAYLOAD

FW_PAYLOAD如同其名,是一個韌體載體。

他把RUNTIME(運行時系統,也就是library)與下一階段BootLoader包在一起。這麼做的好處可以直接覆蓋裝置樹(Device tree blob)設定,讓開機速度更快。

但缺點是當OpenSBI或BootLoader有更動,我們就要重新建構一個PAYLOAD,且沒有溝通機制讓LOADER與PAYLOAd做通訊。

而FW_PAYLOAD的啟動流程如上圖所示,會照順序從ROM>LOADER>FW_PAYLOAD>OS 進行啟動。

 

 

FW_JUMP開機流程圖

2.FW_JUMP

FW_JUMP型態的韌體與FW_PAYLOAD最大的差異除了沒有把OpenSBI與BootLoader包在一起之外,JUMP型態的韌體在啟動過程進行到LOADER階段,會同時載入RUNTIME以及BootLoader,這在使用QEMU進行模擬的情況非常好用。

但這種方法缺點是LOADER階段載入BootLoader的位址是寫死的,且與FW_PAYLOAD相同,這種型態的韌體也沒有與前一個開機階段互相溝通的機制。

 

 

 

FW_DYNAMIC開機流程圖

3.FW_DYNAMIC

FW_DYNAMIC改善上述FW_JUMP的缺點,讓OpenSBI可以動態載入下一啟動階段。透過使用fw_dynamic_info結構的參數,包含版本、下一啟動階段位址、模式等等參數設定下一階段的流程。可以注意到struct fw_dynamic_info中第一項”magic”參數,它的功能是讓使用這個struct的程式可以利用magic與version參數來檢查引用的資料型態是否正確。

由於OpenSBI是在kernel之前的韌體,還沒有載入glibc或標準C/C++語言函式庫,因此像是class等宣告無法作用,只能用struct來撰寫相同功能。

但衍生出一個缺點,就是前一階段的啟動器需要注意fw_dynamic_info的資料內容與型態。

前言中有提到,RISC-V把整個電腦系統分成M-mode、S-mode以及U-mode,每個層級不能直接存取更底層的資源,需要透過ABI或library進行呼叫。但在OpenSBI中,沒有ABI也沒有library,只能夠過LOADER在M-mode存放資料存在暫存器,RUNTIME(OpenSBI)在S-mode中讀取暫存器的資料這種方法來傳遞資料。

FW_DYNAMIC利用”a2″暫存器把struct fw_dynamic_info 放進去,讓OpenSBI讀取。但雖然都是資料,卻又衍生出”Big ending/ Little ending” 也就是”位元組順序“的問題。所以在這底層階段,位元組的順序也需要考慮進去,可以說是非常仔細且嚴謹的。

 

陸、OpenSBI作為函式庫使用

OpenSBI作為還是庫使用於開機流程中之情況

OpenSBI 除了可以包裝成各種不同功能的韌體之外,也可以當作UEFI (Unified Extensible Firmware Interface) 的一環。EDK2(EDKII)是EFI的Development Kit,也就是EFI的開發環境,此開發環境把OpenSBI中的函式庫與LOADER連結在一起,讓擴充韌體可以完整使用OpenSBI中的所有函式庫,如libsbi.a、libsbiutils.a、libplatsbi.a等等。(OpenSBI Librery usage)

目前RISC-V實作的範例是由HPE(Hewlett Packard Enterprise)主導的開源EDK2,並且整合OpenSBI,其專案在Github上開源。

柒、結論

OpenSBI 是SBI的實作,但只提供執行時程式的韌體與函式庫。

得力於OpenSBI以開發平台為主的特性,可以容易的移植到新的SoC上。

此外,OpenSBI所引用的韌體是非強制性的,任何晶片提供廠可以實作自己專屬的,同時也不會強制執行任何開機引導程序。