Підготовка до Rust
Перш ніж ми зможемо запускати код Rust, нам потрібно виконати деяку ініціалізацію.
.section .init.entry, "ax"
.global entry
entry:
/*
* Завантаження та застосування конфігурації керування пам'яттю, готової
* до ввімкнення MMU та кешів.
*/
adrp x30, idmap
msr ttbr0_el1, x30
mov_i x30, .Lmairval
msr mair_el1, x30
mov_i x30, .Ltcrval
/* Скопіювати підтримуваний діапазон PA у TCR_EL1.IPS. */
mrs x29, id_aa64mmfr0_el1
bfi x30, x29, #32, #4
msr tcr_el1, x30
mov_i x30, .Lsctlrval
/*
* Перевірити все до завершення цього пункту, а потім зробити недійсними всі
* потенційно застарілі локальні записи TLB до того, як вони почнуть використовуватися.
*/
isb
tlbi vmalle1
ic iallu
dsb nsh
isb
/*
* Налаштувати sctlr_el1 на ввімкнення MMU та кешу і не продовжувати, доки це
* не буде зроблено.
*/
msr sctlr_el1, x30
isb
/* Вимкнути перехоплення доступу з плаваючою комою в EL1. */
mrs x30, cpacr_el1
orr x30, x30, #(0x3 << 20)
msr cpacr_el1, x30
isb
/* Обнуліть секцію bss. */
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: /* Підготувати стек. */
adr_l x30, boot_stack_end
mov sp, x30
/* Налаштування вектора виключень. */
adr x30, vector_table_el1
msr vbar_el1, x30
/* Виклик коду Rust. */
bl main
/* Постійно циклічно чекаємо на переривання. */
2: wfi
b 2b
- Це те саме, що було б для C: ініціалізація стану процесора, обнулення BSS і налаштування покажчика стека.
- BSS (символ початку блоку, з історичних причин) — це частина об’єктного файлу, яка містить статично виділені змінні, які ініціалізуються нулем. Вони пропущені на зображенні, щоб не витрачати місце на зайві нулі. Компілятор припускає, що завантажувач подбає про їх обнулення.
- BSS може бути вже обнулено, залежно від того, як ініціалізовано пам’ять і завантажено зображення, але ми обнуляємо його, щоб бути впевненими.
- Нам потрібно ввімкнути MMU та кеш перед читанням або записом пам’яті. Якщо ми цього не зробимо:
- Невирівняні доступи призведуть до помилки. Ми створюємо код Rust для цілі
aarch64-unknown-none
, яка встановлює+strict-align
, щоб запобігти створенню компілятором невирівняних доступів, тому в цьому випадку це має бути гаразд, але це не обов’язково так загалом. - Якщо це було запущено у віртуальній машині, це може призвести до проблеми з узгодженістю кешу. Проблема полягає в тому, що віртуальна машина звертається до пам'яті безпосередньо з вимкненим кешем, в той час як хост має кешовані псевдоніми до тієї ж пам'яті. Навіть якщо хост не має явного доступу до пам’яті, спекулятивні доступи можуть призвести до заповнення кешу, а потім зміни з того чи іншого будуть втрачені, коли кеш буде очищено або віртуальна машина ввімкне кеш. (Кеш використовується за фізичною адресою, а не VA чи IPA.)
- Невирівняні доступи призведуть до помилки. Ми створюємо код Rust для цілі
- Для спрощення ми просто використовуємо жорстко закодовану таблицю сторінок (дивиться
idmap.S
), яка ідентифікує перший 1 ГіБ адресного простору для пристроїв, наступний 1 ГіБ для DRAM і ще 1 ГіБ вище для інших пристроїв. Це відповідає розміщенню пам'яті, яке використовує QEMU. - Ми також встановили вектор виключень (
vbar_el1
), про який ми розповімо більше пізніше. - Усі приклади цього дня припускають, що ми будемо працювати на рівні виключення 1 (EL1). Якщо вам потрібно запустити на іншому рівні виключення, вам потрібно буде відповідно змінити
entry.S
.