المرحلة 4a: البنية التحتية للنواة الدقيقة
هذه المرحلة تُعدّ الأساس الذي سيُبنى عليه الانتقال من Monolithic إلى Microkernel حقيقي. كل مكون هنا ضروري قبل إطلاق أول عملية في وضع المستخدم (Ring 3).
فلسفة النواة الدقيقة
أقل كود ممكن في Ring 0 — كل شيء آخر في User Space. النواة تتحمل مسؤولية: الجدولة، إدارة الذاكرة، IPC، وتوجيه المقاطعات فقط. السائقات، نظام الملفات، الشبكة — كلها عمليات User Space تتواصل عبر IPC.
ما تم إنجازه
1. GDT + TSS (kernel/arch/x86_64/gdt.rs)
جدول الواصفات العام (GDT) أُعيد بناؤه بالكامل في Rust ليشمل:
| Index | Selector | الوصف | DPL |
|---|
| 0 | 0x00 | Null | — | | 1 | 0x08 | Kernel Code (64-bit) | 0 | | 2 | 0x10 | Kernel Data | 0 | | 3 | 0x18 | User Data | 3 | | 4 | 0x20 | User Code (64-bit) | 3 | | 5-6 | 0x28 | TSS (128-bit) | 0 |
ترتيب User Data قبل User Code مقصود — تعليمة sysret تحسب:
SS = STAR[63:48] + 8 = 0x10 + 8 = 0x18(User Data) ✅CS = STAR[63:48] + 16 = 0x10 + 16 = 0x20(User Code) ✅
TSS يحتوي:
rsp0: مكدس النواة (يُحدّث عند كل context switch)ist[1]: مكدس Double Fault منفصل (4KB)
2. إعادة برمجة PIC (kernel/arch/x86_64/pic.rs)
المشكلة الكلاسيكية: الـ PIC افتراضياً يوجّه IRQ0-7 إلى vectors 8-15 — نفس نطاق أخطاء المعالج (Double Fault=8، GPF=13). أي تفعيل للتايمر يسبب تصادماً مع معالجات الأخطاء.
الحل: إعادة برمجة Master PIC لـ vectors 0x20-0x27 و Slave PIC لـ 0x28-0x2F عبر تسلسل ICW1-ICW4.
EOI (End-Of-Interrupt): كل معالج IRQ ينتهي بإرسال 0x20 لـ port الـ PIC command — بدونه الـ PIC يتجمد بعد أول مقاطعة.
3. PIT Timer (kernel/arch/x86_64/pit.rs)
تكوين Programmable Interval Timer بتردد ~100Hz (مقاطعة كل ~10ms). يُستخدم لتشغيل الجدولة (preemptive multitasking).
4. نظام الاستدعاءات Syscall/Sysret (kernel/arch/x86_64/syscall.rs)
تكوين 4 MSRs:
- EFER.SCE (bit 0): تفعيل تعليمة syscall — بدونه =
#UD(Invalid Opcode) - STAR: مقاطع GDT لـ syscall (0x08) و sysret (0x10)
- LSTAR: عنوان نقطة الدخول (
syscall_entryفي Assembly) - SFMASK: إخفاء IF (bit 9) لتعطيل المقاطعات عند الدخول — حماية من reentrancy
5. بيانات Per-CPU (kernel/arch/x86_64/percpu.rs)
هيكل PerCpu يحتوي مكدس النواة ومكان مؤقت لحفظ RSP المستخدم أثناء syscall. عنوانه يُحمل في IA32_KERNEL_GS_BASE (0xC0000102) — ليس GS_BASE! لأن swapgs يبادل GS.base مع KERNEL_GS_BASE.
6. نقطة دخول Syscall (boot/x86_64/syscall_entry.asm)
تسلسل حرج:
swapgs— الوصول لـ Per-CPU- حفظ
user_rspفي Per-CPU - تحميل مكدس النواة الآمن
- حفظ كل المسجلات على مكدس النواة
- تحضير المعاملات بترتيب عكسي (لمنع فقدان القيم)
- استدعاء
syscall_dispatchفي Rust - استعادة المسجلات —
pop r10بدلpop rsp(لمنع تبديل مبكر للمكدس) mov rsp, r10— آخر شيء قبل الخروجswapgsعكسي +sysretq
7. الجدولة (kernel/sched/mod.rs) + IPC (kernel/ipc/mod.rs)
- هيكل
TaskمعCpuContextوTaskState - جدولة Round-Robin (هيكلياً جاهزة)
syscall_dispatchيعالج:SYS_EXIT،SYS_WRITE،SYS_SEND،SYS_RECV
الأخطاء الحرجة المُكتشفة والمُصححة أثناء التصميم
تم اكتشاف وتصحيح 10 أخطاء حرجة عبر 4 جولات مراجعة قبل التنفيذ:
- PIC Remapping مفقود — تصادم IRQ0 مع Double Fault
- EOI مفقود — PIC يتجمد بعد أول IRQ
- TSS.rsp0 لا يُحدّث عند context switch — stack corruption
- syscall لا يبدّل المكدس تلقائياً — تنفيذ على user stack خطير
- STAR[63:48] = 0x18 بدل 0x10 — GPF فوري عند sysret
mov rdi,raxقبل حفظ arg1 — فقدان المعامل الأول- PerCpu في GS_BASE بدل KERNEL_GS_BASE — swapgs يبدّل لقمامة
- EFER.SCE غير مفعّل — syscall = #UD
pop rspيبدّل المكدس مبكراً — r11/rcx تُقرأ من user stack- arg2 يُفقد بسبب ترتيب النسخ الخاطئ
النتيجة
النواة تعمل بكل مكونات البنية التحتية:
- GDT: OK | TSS: OK | PIC: OK | PIT: OK | Syscall MSRs: OK
- Timer يرسل ticks بنجاح (تأكيد أن PIC remap + EOI يعملان)
- 123 ميغابايت ذاكرة حرة متاحة
المرحلة التالية (4b): إطلاق أول عملية User Space في Ring 3 مع syscall فعلي.