Підготовка до 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

Speaker Notes

  • Це те саме, що було б для C: ініціалізація стану процесора, обнулення BSS і налаштування покажчика стека.
    • BSS (символ початку блоку, з історичних причин) — це частина об’єктного файлу, яка містить статично виділені змінні, які ініціалізуються нулем. Вони пропущені на зображенні, щоб не витрачати місце на зайві нулі. Компілятор припускає, що завантажувач подбає про їх обнулення.
  • BSS може бути вже обнулено, залежно від того, як ініціалізовано пам’ять і завантажено зображення, але ми обнуляємо його, щоб бути впевненими.
  • Нам потрібно ввімкнути MMU та кеш перед читанням або записом пам’яті. Якщо ми цього не зробимо:
    • Невирівняні доступи призведуть до помилки. Ми створюємо код Rust для цілі aarch64-unknown-none, яка встановлює +strict-align, щоб запобігти створенню компілятором невирівняних доступів, тому в цьому випадку це має бути гаразд, але це не обов’язково так загалом.
    • Якщо це було запущено у віртуальній машині, це може призвести до проблеми з узгодженістю кешу. Проблема полягає в тому, що віртуальна машина звертається до пам’яті безпосередньо з вимкненим кешем, в той час як хост має кешовані псевдоніми до тієї ж пам’яті. Навіть якщо хост не має явного доступу до пам’яті, спекулятивні доступи можуть призвести до заповнення кешу, а потім зміни з того чи іншого будуть втрачені, коли кеш буде очищено або віртуальна машина ввімкне кеш. (Кеш використовується за фізичною адресою, а не VA чи IPA.)
  • Для спрощення ми просто використовуємо жорстко закодовану таблицю сторінок (дивиться idmap.S), яка ідентифікує перший 1 ГіБ адресного простору для пристроїв, наступний 1 ГіБ для DRAM і ще 1 ГіБ вище для інших пристроїв. Це відповідає розміщенню пам’яті, яке використовує QEMU.
  • Ми також встановили вектор виключень (vbar_el1), про який ми розповімо більше пізніше.
  • Усі приклади цього дня припускають, що ми будемо працювати на рівні виключення 1 (EL1). Якщо вам потрібно запустити на іншому рівні виключення, вам потрібно буде відповідно змінити entry.S.