PUBLIC DEVELOPMENT LOG

Gerçek bir
aarch64 Microkernel
inşa ediyoruz.

ASELSAN Milli Telefon vizyonu için sıfırdan, Android'siz, capability-based Rust microkernel geliştirme süreci.

M3
M1 + M2 TAMAMLANDI MMU aktif · heap çalışıyor · alloc::Box kullanılabilir
SON TAMAMLANAN — M2 KAPANDI
M2 — MMU + Identity Paging + Heap
MAIR + TCR + 4K identity mapping + MMU enable + BumpAllocator + LockedHeap. Kernel artık alloc::Box::new()'ı çalıştırıyor; timer IRQ'ları MMU translation altında kesintisiz devam ediyor.
QEMU'da Doğrulandı
Günlüğü Gör
YOL HARİTASI
7 Milestone
M0
İlk Boot
TAMAMLANDI
QEMU virt üzerinde aarch64 kernel boot + PL011 UART ile log yazma
19 Mayıs 2026
M1
Exception & Timer
TAMAMLANDI
VBAR_EL1 + Exception handler + ARM Generic Timer + irq_lock!
✓ M1.1
✓ M1.2
✓ M1.3
✓ M1.4
M2
Memory Management
TAMAMLANDI
Physical allocator + MMU + identity mapping + kernel heap
✓ M2.1
✓ M2.2
✓ M2.3
✓ M2.4
✓ M2.5
M3
Scheduler
SIRADAKİ
Task struct + context switch + preemptive round-robin (timer-driven)
M4 — M7
User Mode + Capability + IPC
M4: EL0 + Syscall
M5: Capability System
M6: Gerçek IPC
M7: Orijinal Demo
GELİŞTİRME GÜNLÜĞÜ
Adım adım ne yaptık
M0
İlk Bare-Metal Kernel
19 Mayıs 2026 • 1 gün
TAMAMLANDI
Yapılanlar
  • Proje yapısı temizlendi (simulation + kernel)
  • aarch64 bare-metal iskeleti oluşturuldu
  • linker.ld + boot.S yazıldı
  • QEMU virt + PL011 UART ile ilk boot
Karşılaşılan Zorluklar
Node sürümü nedeniyle Astro kullanılamadı → Basit ve güçlü tek dosya Tailwind çözümü tercih edildi.
Linker script yolu workspace vs kernel klasörü sorunu çözüldü (build.rs ile).
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.Sexceptions.rsmod.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.rsmod.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.rstimer.rsexceptions.rsmod.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üreTVAL (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ğilimlineer ↑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.rsexceptions.rsmod.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.1
Adres tipleri + FrameAllocator trait
19 Mayıs 2026 • mm/ modülünün temel iskeleti
TAMAMLANDI
Yapılanlar
  • PhysAddr(u64) — fiziksel adres newtype, as_u64(), as_mut_ptr<T>()
  • VirtAddr(u64) — sanal adres newtype
  • PhysFrame — 4 KiB hizalı fiziksel sayfa, from_start_address()
  • FrameAllocator trait — fn allocate_frame() -> Option<PhysFrame>
  • Tüm tipler Copy + Eq + Debug — sıfır-cost soyutlama
Neden bu önce?
Sayfa tablosu, allocator ve MMU kodu yazmadan önce tip ayrımı şart: bir u64'ün fiziksel mi sanal mı adres olduğunu derleyici zorlamalı. Aksi halde MMU açıldığında "yanlış yerden okuduk mu?" sorusu manuel hâle gelir. Bu küçük yatırım, M2.3+ tüm kodun okunabilirliğini ve güvenliğini taşıyor.
kernel/src/mm/address.rs · frame_allocator.rs (trait) · mod.rs
M2.2
BumpAllocator — Physical Frame Allocator
19 Mayıs 2026 • en basit, en güvenilir başlangıç
TAMAMLANDI
Yapılanlar
  • BumpAllocator { next, end } — monotonik artan pointer
  • Aralık: 0x4020_00000x4800_0000 (kernel'den sonraki RAM)
  • 32 256 frame × 4 KiB = 126 MiB kullanılabilir
  • remaining_frames() — debug/log için
  • FrameAllocator trait'ini implement ediyor
Bump neden, bitmap niye değil?
Boot zamanı sayfa tabloları + kernel heap için sadece ardışık frame'ler gerek; geri verme (free) yok. Bump 5 satırla yazılır, hatasızdır, sıfır-state-bug ihtimali. Asıl genel amaçlı bitmap/buddy allocator M2.6+'ya kalsın — o zaman MMU çalışıyor olacak ve free-list'i heap'te tutabileceğiz.
kernel/src/mm/frame_allocator.rs
M2.3
PageTable + 4-seviye walker (map_4k_page)
19 Mayıs 2026 • aarch64 4K granule, 48-bit VA
TAMAMLANDI
Yapılanlar
  • PageTable — 512 × 8-byte entry, sayfa hizalı
  • PageTableEntryset_address / set_flags / is_table
  • PageTableFlags — bitflags: VALID, TABLE, AP, AF, SH, AttrIdx, UXN, PXN, hazır NORMAL ve DEVICE
  • map_4k_page(virt, phys, flags) — L0→L1→L2→L3 yürüyüş, eksik tabloları EARLY_PAGE_TABLES havuzundan alır
  • get_detailed_mapping(VA) — bir adres için 4 seviyenin descriptor değerlerini döner (doğrulama için)
aarch64 4K descriptor encoding (kritik)
Seviyebits[1:0]Anlam
L011Table
L1/L201Block (1 GiB / 2 MiB)
L1/L211Table (next level)
L311Page (4K) — TEK GEÇERLİ DEĞER
L301RESERVED / Invalid (yasak)
Bu tablo neden önemli: M2.4 testinde L3 entry'lerini yanlışlıkla 0x...601 yazdık (bit 1 yoktu → Reserved). CPU sessizce dondu. Açıklaması M2.4 entry'sinde.
kernel/src/mm/paging.rs · kernel/src/arch/aarch64/mmu.rs
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_pageflags | 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.tomlbuild-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.2context_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