準備使用 Rust
我們需要先完成一些初始化作業,才能開始執行 Rust 程式碼。
.section .init.entry, "ax"
.global entry
entry:
/*
* Load and apply the memory management configuration, ready to enable MMU and
* caches.
*/
adrp x30, idmap
msr ttbr0_el1, x30
mov_i x30, .Lmairval
msr mair_el1, x30
mov_i x30, .Ltcrval
/* Copy the supported PA range into TCR_EL1.IPS. */
mrs x29, id_aa64mmfr0_el1
bfi x30, x29, #32, #4
msr tcr_el1, x30
mov_i x30, .Lsctlrval
/*
* Ensure everything before this point has completed, then invalidate any
* potentially stale local TLB entries before they start being used.
*/
isb
tlbi vmalle1
ic iallu
dsb nsh
isb
/*
* Configure sctlr_el1 to enable MMU and cache and don't proceed until this
* has completed.
*/
msr sctlr_el1, x30
isb
/* Disable trapping floating point access in EL1. */
mrs x30, cpacr_el1
orr x30, x30, #(0x3 << 20)
msr cpacr_el1, x30
isb
/* Zero out the bss section. */
adr_l x29, bss_begin
adr_l x30, bss_end
0: cmp x29, x30
b.hs 1f
stp xzr, xzr, [x29], #16
b 0b
1: /* Prepare the stack. */
adr_l x30, boot_stack_end
mov sp, x30
/* Set up exception vector. */
adr x30, vector_table_el1
msr vbar_el1, x30
/* Call into Rust code. */
bl main
/* Loop forever waiting for interrupts. */
2: wfi
b 2b
- 這與使用 C 時相同:將處理器狀態初始化、將 BSS 設為零,以及設定堆疊指標。
- BSS (區塊起始符號,因歷史因素而存在) 是物件檔案的一部分,含有初始化為零的靜態分配變數。映像檔會省略這些變數,以免浪費空間儲存零。編譯器會假設由載入器負責將變數初始化為零。
- 視記憶體初始化及映像檔載入方式而定,BSS 可能已為零,但為了確定,我們會將它設為零。
- 我們需要啟用 MMU 和快取,才能讀取或寫入任何記憶體。如果不這樣做:
- 未對齊的存取會發生錯誤。我們是為
aarch64-unknown-none
目標建構 Rust 程式碼,這會設定+strict-align
,防止編譯器產生未對齊的存取,因此在本例中應該不會出錯,但這不一定是一般情況。 - 如果是在 VM 中執行,可能會導致快取一致性問題。問題在於 VM 會在快取已停用時直接存取記憶體,而主機具有相同記憶體的可快取別名。即使主機未明確存取記憶體,推測存取行為仍可能導致快取填補,而當快取遭到清理或 VM 啟用快取時,存取之間的變更就會遺失 (快取是以實體位址做為索引鍵,並非使用 VA 或 IPA)。
- 未對齊的存取會發生錯誤。我們是為
- 為求簡單,我們只使用寫死的分頁表 (見
idmap.S
),其中前 1 GiB 的位址空間是對應至裝置、接下來 1 GiB 是對應至 DRAM,另外 1 GiB 以上則適用更多裝置。這符合 QEMU 使用的記憶體布局。 - 我們也會設定例外狀況向量 (
vbar_el1
),稍後將進一步說明。 - 今天下午的所有例子都假設我們會在例外狀況層級 1 (EL1) 執行。如要在不同的例外狀況層級執行,就需據以修改
entry.S
。