Skip to content

المرحلة 4a: البنية التحتية للنواة الدقيقة ​

هذه المرحلة تُعدّ الأساس الذي سيُبنى عليه الانتقال من Monolithic إلى Microkernel حقيقي. كل مكون هنا ضروري قبل إطلاق أول عملية في وضع المستخدم (Ring 3).

فلسفة النواة الدقيقة ​

أقل كود ممكن في Ring 0 — كل شيء آخر في User Space. النواة تتحمل مسؤولية: الجدولة، إدارة الذاكرة، IPC، وتوجيه المقاطعات فقط. السائقات، نظام الملفات، الشبكة — كلها عمليات User Space تتواصل عبر IPC.

ما تم إنجازه ​

1. GDT + TSS (kernel/arch/x86_64/gdt.rs) ​

جدول الواصفات العام (GDT) أُعيد بناؤه بالكامل في Rust ليشمل:

IndexSelectorالوصف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) ​

تسلسل حرج:

  1. swapgs — الوصول لـ Per-CPU
  2. حفظ user_rsp في Per-CPU
  3. تحميل مكدس النواة الآمن
  4. حفظ كل المسجلات على مكدس النواة
  5. تحضير المعاملات بترتيب عكسي (لمنع فقدان القيم)
  6. استدعاء syscall_dispatch في Rust
  7. استعادة المسجلات — pop r10 بدل pop rsp (لمنع تبديل مبكر للمكدس)
  8. mov rsp, r10 — آخر شيء قبل الخروج
  9. 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 جولات مراجعة قبل التنفيذ:

  1. PIC Remapping مفقود — تصادم IRQ0 مع Double Fault
  2. EOI مفقود — PIC يتجمد بعد أول IRQ
  3. TSS.rsp0 لا يُحدّث عند context switch — stack corruption
  4. syscall لا يبدّل المكدس تلقائياً — تنفيذ على user stack خطير
  5. STAR[63:48] = 0x18 بدل 0x10 — GPF فوري عند sysret
  6. mov rdi,rax قبل حفظ arg1 — فقدان المعامل الأول
  7. PerCpu في GS_BASE بدل KERNEL_GS_BASE — swapgs يبدّل لقمامة
  8. EFER.SCE غير مفعّل — syscall = #UD
  9. pop rsp يبدّل المكدس مبكراً — r11/rcx تُقرأ من user stack
  10. arg2 يُفقد بسبب ترتيب النسخ الخاطئ

النتيجة ​

النواة تعمل بكل مكونات البنية التحتية:

  • GDT: OK | TSS: OK | PIC: OK | PIT: OK | Syscall MSRs: OK
  • Timer يرسل ticks بنجاح (تأكيد أن PIC remap + EOI يعملان)
  • 123 ميغابايت ذاكرة حرة متاحة

المرحلة التالية (4b): إطلاق أول عملية User Space في Ring 3 مع syscall فعلي.

تم تطويره بحب بواسطة مجتمع Qtoom.