在riscv上電時,會進行CPU自檢,然后跳轉到bootloader處執行。bootloader設置好kernel的運行環境后,從硬盤加載kernel到內存,最后再跳轉到kernel入口地址。
我們采用的bootloader為OpenSBI,被加載到0x80000000地址,OpenSBI探測好外設并初始化內核的環境變量后,加載內核到0x80200000地址,最后再跳轉到0x80200000地址。從上文我們知道,我們的入口點_start地址正好為0x80200000,也就是OpenSBI會調用我們kernel的_start函數。
在riscv64中,有如下幾種特權級:

User模式:該特權模式為權限最小的模式,在linux系統中用戶態就運行在該特權級;
Supervisor模式:該特權級時linux操作系統運行的模式,特權級別比User模式高;
Machine模式:CPU上電啟動后運行在該特權模式,該特權比Supervisor更高。
從 U 到 S 再到 M,權限不斷提高,這意味著你可以使用更多的特權指令,訪需求權限更高的寄存器等等。我們可以使用一些指令來修改 CPU 的當前特權級。
riscv64 的 M ModeM-mode(機器模式,縮寫為 M 模式)是 RISC-V 中 hart(hardware thread,硬件線程)可以執行的最高權
限模式。在 M 模式下運行的 hart 對內存,I/O 和一些對于啟動和配置系統來說必要的底層功能有著完全的
使用權。riscv64 的 S ModeS-mode(監管者模式,縮寫為 S 模式)是支持現代類 Unix 操作系統的權限模式,支持現代類 Unix 操作系
統所需要的基于頁面的虛擬內存機制是其核心。
OpenSBI運行在Machine模式,當跳轉到kernel地址0x80200000執行時,會切換到Supervisor模式執行。
在我們之前的 _start() 代碼中只執行了loop,也就是無限循環,沒有做任何實質的操作。現在我們需要添加不同的功能,因此就需要設置一下kernel內核的運行環境。這個運行環境我們首先需要設置的是內核堆棧,在rust函數調用時會使用到這個堆棧,如果我們不設置sp的地址,那么就可能使用sp指向的任何地址,這將給程序帶來意想不到的后果。
由于riscv64的sp地址不能通過rust語言設置,因此這部分的環境變量就需要在匯編程序下設置:
# src/boot/entry_riscv64.asm.section .text.entry.global _start
_start:la sp, bootstacktopcall rust_main.section .bss.stack.align 12.global bootstack
bootstack:.space 4096 * 4.global bootstacktop
bootstacktop:
_start 程序放在 .text.entry 這個段中,我們鏈接腳本中將 .text.entry 放在了.text 段的第一個位置,也就是將_start函數放在了.text的第一個位置。
在_start開始處,將堆棧的頂部地址加載到sp寄存器中,并且堆棧的大小為16k,然后調用rust_main函數。
在main.rs中,我們將_start函數刪除,并且添加rust_main函數。
#![no_main]
#![no_std]use core::panic::PanicInfo;#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {loop{}
}#[no_mangle]
extern "C" fn rust_main() -> ! {loop{}
}
為了支持匯編代碼,我們需要開啟global_asm特性,因此我們在main.rs中開啟該特性,并包含src/boot/entry_riscv64.asm匯編代碼:
#![no_main]
#![no_std]use core::panic::PanicInfo;#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {loop{}
}#![feature(global_asm)]
global_asm!(include_str!("boot/entry_riscv64.asm"));#[no_mangle]
extern "C" fn rust_main() -> ! {loop{}
}
注意,上面中我們將匯編特性代碼添加在panic_handler下面,此時會報錯:
error: an inner attribute is not permitted in this context--> src/main.rs:11:1|
11 | #![feature(global_asm)]| ^^^^^^^^^^^^^^^^^^^^^^^|= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.error[E0658]: use of unstable library feature 'global_asm': `global_asm!` is not stable enough for use and is subject to change--> src/main.rs:13:1|
13 | global_asm!(include_str!("boot/entry_riscv64.asm"));| ^^^^^^^^^^|= note: see issue #35119 <https://github.com/rust-lang/rust/issues/35119> for more information= help: add `#![feature(global_asm)]` to the crate attributes to enableerror: aborting due to 2 previous errorsFor more information about this error, try `rustc --explain E0658`.
error: could not compile `rust_os`.
這里顯示錯誤的原因為global_asm是不穩定的功能,需要使用nightly來編譯程序。但我們此時已經是使用nightly了。正真的原因是#![feature(global_asm)]特性代碼放在了panic_handler代碼的后面,解決的辦法是#![feature(global_asm)]特性必須放在文件開頭的位置,修改后的代碼如下:
#![no_main]
#![no_std]
#![feature(global_asm)]use core::panic::PanicInfo;#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {loop{}
}global_asm!(include_str!("boot/entry_riscv64.asm"));#[no_mangle]
extern "C" fn rust_main() -> ! {loop{}
}
此時可以編譯通過了。