準備使用 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

Speaker Notes

  • 這與使用 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