Skip to content

📜 العقد المقدس (Qet4 API) ​

مكتبة qet4_api هي الواجهة الثابتة الوحيدة بين النواة والعالم الخارجي. كل برنامج يعمل في فضاء المستخدم (Userspace) سواء كان سائقاً (Driver) أو خدمة (Service) يعتمد فقط على هذا العقد.

القاعدة الذهبية: أرقام الاستدعاءات وتوقيعاتها لا تتغير أبداً.

1. جدول استدعاءات النظام الكامل (Syscalls) ​

يحتوي العقد حالياً على 12 استدعاءً ثابتاً:

الرقمالاسمالوظيفةالمعاملات (arg1 ➔ arg4)القيمة المرجعةالصلاحية / ملاحظات
0SYS_EXITإنهاء العملية الحالية--للجميع
1SYS_WRITEكتابة نص على الشاشةarg1: مؤشر النص, arg2: الطول-للجميع
2SYS_SENDإرسال رسالة IPCarg1: رقم المنفذ, arg2: مؤشر, arg3: طول-للجميع
3SYS_RECVاستقبال رسالة (يحظر)arg1: مؤشر, arg2: حجم المخزنطول الرسالة الفعليللجميع
4SYS_YIELDالتنازل عن المعالج--للجميع
5SYS_SPAWNإطلاق عملية جديدةarg1: مؤشر ELF, arg2: الحجم, arg4: مؤشر لمنفذ الردPID العمليةPID 1 (init) فقط
6SYS_GET_SENDERجلب PID المُرسل-PID المُرسلللجميع
7SYS_DRAWرسم حرف على VGAarg1: col, arg2: row, arg3: بيانات الحرف (حرف+لون)-للجميع
8SYS_READ_KEYقراءة كيبورد-Scancodeinput_server فقط
9SYS_GET_FB_INFOمعلومات الـ Framebufferarg1: مؤشر لـ FbInfo!= u64::MAX للنجاحللجميع
10SYS_DRAW_FBرسم Pixelsarg1: مؤشر الـ src, arg2: offset, arg3: len0 للنجاحللجميع
11SYS_DRAW_CHAR_FBرسم حرف على FBarg1: (حرف+احداثيات), arg2: fg, arg3: bg-للجميع
98SYS_ARG_ECHOاختبار تمرير المسجلاتarg1 ➔ arg4القيمة المدخلةاختبار فقط (abi-selftest)
99SYS_GET_SWITCH_COUNTعداد التبديلات-عدد التبديلاتاختبار فقط (abi-selftest)

ملاحظة: يتم تفعيل الاستدعاءات الاختبارية (98 و 99) فقط عند تجميع المكتبة بعلم abi-selftest والنواة بعلم abi-test-input.

2. اتفاقية الاستدعاء (Syscall ABI) ​

تستخدم النواة مسجلات محددة لتمرير القيم. من الضروري جداً الالتزام بالـ Clobbers (المسجلات التي تتلفها النواة)، وإلا ستفقد قيمك بشكل عشوائي!

  • rax = رقم الاستدعاء (وخروج القيمة المرجعة).
  • rdi = المعامل الأول (arg1).
  • rsi = المعامل الثاني (arg2).
  • rdx = المعامل الثالث (arg3).
  • r10 = المعامل الرابع (arg4). (ملاحظة: نستخدم r10 بدلاً من rcx لأن تعليمة syscall تكتب عنوان العودة في rcx).
  • المسجلات المتخربة (Clobbers): rcx, r11, r8, r9, r10. النواة تقوم بترجمة الاتفاقية وتكتب فوق هذه المسجلات.

إليك الكود الفعلي من المكتبة لدالة syscall3 والذي يوضح الاتفاقية بأكملها:

rust
#[inline(always)]
fn syscall3(num: u64, arg1: u64, arg2: u64, arg3: u64) -> u64 {
    let ret: u64;
    unsafe {
        core::arch::asm!(
            "syscall",
            inlateout("rax") num => ret,
            in("rdi") arg1,
            in("rsi") arg2,
            in("rdx") arg3,
            out("rcx") _,
            out("r11") _,
            out("r8") _,
            out("r9") _,
            out("r10") _,
            options(nostack),
        );
    }
    ret
}

3. جدول أكواد الأخطاء (Error Codes) ​

النواة تعيد القيم السالبة كرسائل أخطاء (نظراً لعدم استخدام Result في الأسمبلي المباشر). تعني القيمة نفس الشيء أو شيئاً مختلفاً حسب الاستدعاء:

الكود المُرجعSYS_SEND (إرسال)SYS_SPAWN (إطلاق عملية)SYS_READ_KEY (الكيبورد)
u64::MAXعنوان خارج الذاكرة / رسالة أكبر من 256خطأ عامفشل عام
u64::MAX - 1لا تملك صلاحية على المنفذEFAULT (مؤشر المنفذ فاسد)EACCES (ليس input_server)
u64::MAX - 2Mailbox Full (صندوق المستلم ممتلئ)EPERM (عملية غير PID 1)-

4. إطلاق العمليات (SYS_SPAWN) ​

المثال التالي يوضح كيفية استدعاء دالة spawn من المصدر الفعلي، مع ملاحظة تمرير المعامل الرابع كعنوان port_out ليكتب عليه الكرنل:

rust
/// إطلاق عملية مستخدم جديدة من بيانات ELF مدمجة
///
/// ⚠️  يتطلب صلاحية PID 1 (init) — EPERM لأي عملية أخرى
///
/// - arg4 = r10 = عنوان `port_out` → الـ kernel يكتب فيه رقم المنفذ
/// - النجاح: Ok((pid, port))
/// - الفشل:  Err(errno): MAX=عام، MAX-1=EFAULT، MAX-2=EPERM
pub fn spawn(elf_data: &[u8]) -> Result<(u64, usize), u64> {
    let mut port_out: u64 = u64::MAX;
    let mut ret = SYS_SPAWN;
    unsafe {
        core::arch::asm!(
            "syscall",
            inlateout("rax") ret,
            in("rdi")        elf_data.as_ptr() as u64,
            in("rsi")        elf_data.len() as u64,
            in("r10")        &mut port_out as *mut u64 as u64, // arg4
            out("rcx")       _,
            out("r11")       _,
            options(nostack),
        );
    }
    if ret >= u64::MAX - 2 {
        return Err(ret);
    }
    if port_out == u64::MAX {
        return Err(u64::MAX);
    }
    Ok((ret, port_out as usize))
}

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