M1.1
Exception Vector Table
19 Mayıs 2026 • aynı gün
QEMU'da DOĞRULANDI
Yapılanlar
- 16 entry'lik aarch64 vektör tablosu (2 KB hizalı)
-
SAVE_CONTEXT / RESTORE_CONTEXT trampoline'ları (x0–x30 + ELR + SPSR)
- Rust handler'lar (sync / IRQ / FIQ / SError / invalid)
- VBAR_EL1 kurulumu + ESR.EC decode'u
-
brk #0xdead ile sync exception → handler → eret başarılı
QEMU UART çıktısı
[BOOT] Şu anki Exception Level: EL1
[M1.1] VBAR_EL1 = 0x0000000040000800
[M1.1] Exception handler'lar aktif.
[TEST] Sync exception tetikleniyor (brk #0xdead)...
[EXC] ----- Senkron exception (EL1, SP_ELx) -----
ESR_EL1 = 0xf200dead (EC=0x3c → BRK)
FAR_EL1 = 0x0000000000000000
ELR_EL1 = 0x00000000400025b8
[EXC] BRK #0xdead yakalandı. Sonrakine atlanıyor.
[TEST] Exception'dan başarıyla geri döndük.
[OK] M1.1 doğrulandı.
kernel/src/arch/aarch64/exceptions.S •
exceptions.rs •
mod.rs
Karşılaşılan zorluklar:
Cargo workspace içinde target/ dizini kernel altında değil
workspace kökündeydi (Makefile düzeltildi). .cargo/config.toml
içinde [target.aarch64-unknown-none] iki kez tanımlanmıştı —
birleştirildi. Vektör tablosunun 0x800 hizalı olması için linker script'e açık
ALIGN(0x800) + ayrı .text.exceptions
bölümü eklendi.
M1.2
Çalışan Exception Handler — EC-bazlı policy
19 Mayıs 2026 • aynı gün
3/3 PATH DOĞRULANDI
Yapılanlar
-
trigger_brk(), trigger_svc(), trigger_undefined() test API'si
- Sync handler'a
match ec tabanlı policy
- BRK (EC=0x3c) → recover, ELR += 4
- SVC (EC=0x15) → recover (HW otomatik), imm16 logla
- UDF (EC=0x00) → panic, dump_context
- Data Abort (EC=0x25) ve Instruction Abort (EC=0x21) için panic path'leri hazır (FAR_EL1 raporlu)
QEMU UART çıktısı
[TEST] (1/3) BRK — brk #0xdead
[EXC] BRK #0xdead (EC=0x3c) — recover (ELR += 4)
[TEST] (1/3) BRK'tan geri döndük. OK.
[TEST] (2/3) SVC — svc #0x42
[EXC] SVC #0x0042 (EC=0x15) — supervisor call
[TEST] (2/3) SVC'den geri döndük. OK.
[TEST] (3/3) UDF — udf #0xbeef
[EXC] !!! UNDEFINED / UNKNOWN INSTRUCTION !!!
ESR_EL1 = 0x02000000 ELR = 0x400027a4
!!! KERNEL PANIC !!!
Dosya: kernel/src/arch/aarch64/exceptions.rs:120
Mesaj: undefined instruction
kernel/src/arch/aarch64/exceptions.rs •
mod.rs •
kernel/src/main.rs
Karşılaşılan zorluk:
İlk denemede core::ptr::write_volatile(null) ile data abort
tetiklemeye çalıştım, ama MMU henüz aktif olmadığı için (M2'de gelecek) QEMU bunu silent
geçti — kernel devam etti. Çözüm: panic path'ini gerçek-data-abort yerine udf #0xbeef
ile test ettim. Gerçek MMU-bazlı data abort'u M2'de ekleyeceğiz; handler kodu zaten hazır.
M1.3
ARM Generic Timer + GICv2 — Periyodik IRQ
19 Mayıs 2026 • aynı gün • ilk denemede çalıştı
TICK ZİNCİRİ TAM
Yapılanlar
-
gic.rs — GICv2 (GICD @ 0x0800_0000, GICC @ 0x0801_0000) init + enable_irq + ack/eoi
-
timer.rs — CNTV: CNTFRQ_EL0 oku, CNTV_TVAL_EL0 arm, CNTV_CTL_EL0=0b01
-
rust_irq_handler — IAR oku → timer (PPI 27) ise ack+rearm+tick++ → EOIR
-
enable_irqs() / disable_irqs() — DAIF.I helper'ları
- Atomic
TICKS sayacı (M3 scheduler için temel)
QEMU UART çıktısı
[M1.3] GICv2 init: GICD@0x08000000, GICC@0x08010000
[M1.3] CNTFRQ_EL0 = 62500000 Hz → 2 Hz tick
(her tick 31250000 count)
[M1.3] GICD ISENABLER: PPI 27 aktif
[M1.3] DAIF.I = 0 → IRQ'lar açıldı.
[OK] Kernel idle döngüsü (wfi). Timer bekleniyor...
[TICK 1] virtual timer (PPI 27)
[TICK 2] virtual timer (PPI 27)
[TICK 3] virtual timer (PPI 27)
[TICK 4] virtual timer (PPI 27)
...
[TICK 11] virtual timer (PPI 27)
kernel/src/arch/aarch64/gic.rs •
timer.rs •
exceptions.rs •
mod.rs
Niçin önemli:
Artık kernel'ın bir "kalbi" var. Periyodik IRQ olmadan preemptive scheduler kurulamaz —
M3'ün ön şartı bu. Şu anda wfi ile uyuyor, sadece timer
IRQ ile uyanıp tick basıyor, sonra tekrar uyuyor. Tam zincir:
timer → GIC → vector → SAVE → Rust → EOI → RESTORE → eret → wfi.
M1.4
100 Hz drift-free tick + irq_lock!
19 Mayıs 2026 • M1 KAPANDI
~600× DRIFT İYİLEŞMESİ
Yapılanlar
- TICK_HZ 2 → 100 Hz
- UART log her tick yerine her 1 saniyede "[SEC n]" özeti
- Jitter ölçümü:
CNTVCT_EL0 ile expected vs actual drift (µs)
- TVAL → CVAL geçişi: mutlak compare register, handler latency'si birikmiyor
-
IrqGuard RAII + irq_lock!() makrosu — scope-bazlı kritik bölge (M3 scheduler kuyrukları için)
TVAL → CVAL etkisi
| Süre | TVAL (eski) | CVAL (yeni) |
| 1 sn | +19 661 µs | +127 µs |
| 3 sn | +50 820 µs | +166 µs |
| 6 sn | +100 533 µs | +118 µs |
| Eğilim | lineer ↑ | sabit |
QEMU UART çıktısı (CVAL ile)
[M1.3] CNTFRQ_EL0 = 62500000 Hz
→ 100 Hz tick (her tick 625000 count)
[M1.3] GICD ISENABLER: PPI 27 aktif
[M1.3] DAIF.I = 0 → IRQ'lar açıldı.
[M1.4] Saniyelik özet aktif.
[OK] Kernel idle döngüsü (wfi). Timer bekleniyor...
[SEC 1] ticks=100 drift=+127 µs (+7957 count)
[SEC 2] ticks=200 drift=+121 µs (+7579 count)
[SEC 3] ticks=300 drift=+166 µs (+10410 count)
[SEC 4] ticks=400 drift=+173 µs (+10868 count)
[SEC 5] ticks=500 drift=+97 µs (+6108 count)
[SEC 6] ticks=600 drift=+118 µs (+7405 count)
kernel/src/arch/aarch64/timer.rs •
exceptions.rs •
mod.rs (IrqGuard)
Niçin TVAL biriktiriyordu:
Handler her tetiklendiğinde "şu andan {period} sayım sonra tekrar tetikle" diyordu —
handler içinde geçen her µs bir sonraki tick'i o kadar geciktirip kalıcı borç oluşturuyordu.
CVAL ile "mutlak hedef = bir önceki hedef + period" diyoruz; latency artık tek-tick'lik
gecikme olur, bir sonraki tick'i etkilemez. Bu, scheduler için tek-tick jitter'ı toplam ms'lik
kayma yapmadan tolere etmemizi sağlıyor.
M2.4
MAIR + TCR + Identity Mapping + MMU enable
19 Mayıs 2026 • M2'nin en sinir bozucu hatasını içerir
2 HATA → DÜZELTİLDİ
Yapılanlar
- MAIR_EL1 =
0x00…04ff — Attr0=Normal WB-WA, Attr1=Device-nGnRnE
- TCR_EL1 =
0x00…800710 — T0SZ=16 (48-bit), 4K granule, Inner/Outer WB-WA, IPS=40-bit
- Identity map: kernel RAM (128 MiB @ 0x4000_0000, NORMAL), UART (0x0900_0000, DEVICE), GIC (0x0800_0000, DEVICE)
- 3 adres için derin doğrulama: L0/L1/L2/L3 descriptor'larını ekrana basıp gözle teyit
- MMU enable sırası:
dc civac (tüm tablolar) → MSR TTBR0 → TLBI VMALLE1IS → DSB ISH → ISB → MSR SCTLR_EL1 (M=1, C=1, I=1) → ISB
QEMU çıktısı
[M2.4.1] MAIR: 0x00000000000004ff
[M2.4.1] TCR : 0x0000000000800710
[M2.4.2] 4K Identity Mapping + derin doğrulama:
VA=0x40000000 L0=0x..d003 L1=0x..e003
L2=0x..f003 L3=0x40000603 ← Page ✓
VA=0x09000000 L3=0x09000407 ✓ (UART, DEVICE)
VA=0x08000000 L3=0x08000407 ✓ (GIC, DEVICE)
[M2.4.3] 1. dc civac + dsb sy
[M2.4.3] 2. TTBR0_EL1 yazılıyor
[M2.4.3] 3. TLBI VMALLE1IS
[M2.4.3] 4. DSB ISH + ISB
[M2.4.3] 5. MMU + Cache enable (SCTLR_EL1)
[M2.4.3] 6. Final ISB
[M2.4.3] MMU enable sequence tamamlandı.
kernel/src/arch/aarch64/mmu.rs
İlk denemede karşılaştıklarımız
Hata 1 — MMU enable'da SESSİZ kilitlenme (log yok, panic yok, CPU dondu)
L3 descriptor için sadece VALID set edip TABLE bit'ini kapattığımız için entry 0x..601 oldu. ARM 4K granule kuralı (M2.3 tablosunda): L3'te bits[1:0]=01 = Reserved. SCTLR.M=1 sonrası ilk instruction fetch Reserved descriptor'a düştü → translation fault → exception vector için de translation gerek → recursive fault → sessiz freeze.
Fix: map_4k_page → flags | VALID | TABLE. L3'te TABLE bit'i aslında "Page" anlamına geliyor. Entry'ler 0x..603 / 0x..407 oldu, MMU enable geçti.
M2.5
LockedHeap + global_allocator + alloc::Box
19 Mayıs 2026 • kernel'in ilk gerçek dinamik allocation'ı
HEAP ÇALIŞIYOR
Yapılanlar
-
linked_list_allocator = "0.10" + use_spin feature
-
#[global_allocator] static HEAP: LockedHeap
-
init_kernel_heap(&mut bump) — BumpAllocator'dan 2048 ardışık frame al, ilkinin adresi heap base
- Heap = 8 MiB, identity-mapped RAM içinde (M2.4 mapping'i yeterli, ek
map_4k_page çağrısı YOK)
- Smoke test:
alloc::Box::new(0xA5A5_1234) → değer geri okunabiliyor
QEMU çıktısı
[M2] BumpAllocator aralığı:
PhysAddr(0x40200000) → PhysAddr(0x48000000)
[M2.5] Kernel heap kuruluyor:
0x0000000040200000 + 8 MiB (identity-mapped)
[M2.5] 2048 sayfa rezerve edildi, LockedHeap aktif.
[M2.5] Kalan frame: 30208
[M2.5] Box::new test: 0xa5a51234 (heap çalışıyor!)
[SEC 1] ticks=100 drift=+381 µs
[SEC 5] ticks=500 drift=+84 µs
↑ MMU aktif + heap aktif iken bile timer sabit
kernel/Cargo.toml · kernel/src/main.rs (HEAP + init_kernel_heap)
Bu adımda karşılaştıklarımız
Hata 2 — Build: E0152 duplicate lang item in core
linked_list_allocator alloc'a dolaylı bağımlı. Rustup'ın precompiled aarch64-unknown-none target'ı alloc'u kendi core'uyla birlikte sundu; bizimkiler build-std core kullanıyordu → linker iki farklı core gördü, reddetti.
Fix: .cargo/config.toml → build-std = ["core", "compiler_builtins", "alloc"]. Artık alloc da bizimle birlikte derleniyor.
Hata 3 — İlk allocation'da Translation fault L0
İlk tasarımda heap base = 0xFFFF_8000_0000_0000 (high-half / TTBR1). Ama TTBR1_EL1 hiç kurulmamıştı. FAR_EL1 = 0xFFFF_8000_0000_0000 tam o adresi gösterdi — DFSC=0x04 (Translation fault, level 0).
Fix: heap'i identity-mapped RAM'e taşı (BumpAllocator zaten o aralıktan veriyor, ek mapping gereksiz). TTBR1 + high-half kernel mapping M2.6+'ya kaldı.
Bonus:
M1.2'de "MMU yokken bu test çalıştırılamaz" diyerek handler'a koyduğumuz data abort
panic path'i, Hata 3'ün ilk patlamasında gerçek MMU translation fault'unu yakaladı.
Hipotez 3 adım önce kuruldu, bu adımda doğrulandı.
M3
Preemptive Scheduler + Context Switch
M2 kapandı, sıradaki büyük adım
PLAN
Önerilen alt adımlar (M1 ve M2 tüm ön şartları sağladı):
- M3.1 — Task struct: id, name, stack, saved registers, state (Ready/Running/Blocked)
- M3.2 —
context_switch.S: x19–x28 + LR + SP save/restore (callee-saved)
- M3.3 — Round-robin scheduler queue (heap'te
VecDeque<Task>)
- M3.4 — Timer IRQ'da
schedule() çağrısı → preemption
- M3.5 — İki kernel-mode task'i paralel çalıştır (her biri farklı mesaj basıyor) ile demo