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