آماده شدن برای 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 و تنظیم stack pointer.
- این BSS (نماد شروع بلوک، به دلایل تاریخی) بخشی از object file است که حاوی متغیرهای تخصیص یافته استاتیکی است که مقدار اولیه آنها صفر است. برای جلوگیری از اتلاف فضا روی صفر، آنها از تصویر حذف شدهاند. کامپایلر فرض میکند که لودر از صفر کردن آنها مراقبت میکند.
- ممکن است BSS قبلاً صفر شده باشد، بسته به اینکه چگونه حافظه مقداردهی اولیه شده و تصویر بارگذاری شده است، اما برای اطمینان آن را صفر میکنیم.
- قبل از خواندن یا نوشتن هر حافظه باید MMU و cache را فعال کنیم. اگر این کار را نکنیم:
- دسترسیهای بدون تراز خطا خواهند داشت. ما کد Rust را برای هدف
aarch64-unknown-none
میسازیم که+strict-align
را تنظیم میکند تا از ایجاد دسترسیهای بدون تراز توسط کامپایلر جلوگیری کند، بنابراین در این مورد باید خوب باشد، اما لزوماً اینطور نیست. - اگر در VM اجرا میشد، این کار میتواند منجر به مشکلات انسجام cache شود. مشکل این است که VM مستقیماً با حافظه cache غیرفعال شده به حافظه دسترسی پیدا میکند، در حالی که host دارای نام مستعار قابل cache برای همان حافظه است. حتی اگر cache به طور صریح به حافظه دسترسی نداشته باشد، دسترسیهای گمانهزنی میتواند منجر به پر شدن حافظه cache شود، و پس از پاک شدن حافظه cache یا فعال کردن حافظه توسط VM، تغییرات از یک یا دیگری از بین میرود. (حافظه cache با آدرس فیزیکی کلید میخورد، نه VA یا IPA.)
- دسترسیهای بدون تراز خطا خواهند داشت. ما کد Rust را برای هدف
- برای سادگی، ما فقط از یک pagetable کدگذاری شده استفاده می کنیم (به
idmap.S
مراجعه کنید) که ۱ گیگابایت اول فضای آدرس را برای دستگاهها، ۱ گیگابایت بعدی را برای DRAM و ۱ گیگابایت دیگر را برای دستگاههای بیشتر نگاشت میکند. این با چیدمان حافظهای که QEMU استفاده میکند مطابقت دارد. - ما همچنین exception vector (
vbar_el1
) را تنظیم کردیم که در ادامه بیشتر در مورد آن خواهیم دید. - همه مثالها امروز بعد از ظهر فرض میکنند که ما در سطح استثنا 1 (EL1) اجرا خواهیم کرد. اگر نیاز به اجرا در سطح استثنایی متفاوت دارید، باید
entry.S
را بر این اساس تغییر دهید.