هذا ليس نظام تشغيل حقيقي . إنه مجرد نظام تشغيل بسيط تم إنشاؤه في أغراض تعليمية.
الهدف الرئيسي الذي أتابعه هو معرفة كيفية عمل نظام التشغيل من الألف إلى الياء. بدءًا من قطاع التمهيد الخاص ، ومقاطعات الأجهزة والبرامج ، وسائقي خاصين.
يتم احتساب المستودع مع مجلس التعاون الخليجي لأنني أخطط لكتابة نظام تشغيل بسيط آخر مع الصدأ. لذلك ، آمل أن يكون هناك Ghaiklor-OS-GCC و Ghaiklor-Os-Rustc .
| مرحبا بالعالم |
|---|
يتكون Ghaiklor-OS-GCC من ملفين بتنسيق ثنائي خام: boot.bin و kernel.bin . وهي موجودة في boot/boot.bin و kernel/kernel.bin وفقًا لذلك بعد الترجمة.
يتم تجميع boot.bin عبر NASM . Makefile يأخذ boot/boot.asm ومكالمات nasm boot/boot.asm -f bin -o boot/boot.bin . تتضمن مقابض NASM من المراسلين الفرعيين نفسها ، لذلك سيتم تجميع جميع ملفات التجميع إلى ثنائي. لا شيء آخر ، بسيط.
يتم تجميع kernel.bin عبر GCC و LD عبر المشتركة التي يجب تثبيتها. ألق نظرة على قسم بيئة التنمية. بعد تثبيت Cross-Compiler ، يمكننا أخذ مصادر من وحدة المعالجة المركزية والسائقين ، وتشمل مجلدات kernel و libc بشكل متكرر. يتم تجميع جميع ملفات .C عبر gcc . يتم استخدام ملفات الكائنات المترجمة لتجميع kernel.bin ثم ، عبر LD ld -o kernel/kernel.bin -Ttext 0x1000 <OBJ_FILES> --oformat binary .
OS-IMAGE.BIN هو تم تجميعها تسلسل boot.bin و kernel.bin . تحققت بسهولة مع cat boot/boot.bin kernel/kernel.bin > os-image.bin .
لقد كتبت نص bootstrap.sh ، يمكنك تشغيله. سيقوم بتثبيت جميع التبعيات المطلوبة لجهاز المضيف الخاص بك.
bash bootstrap.shعند تشغيل الكمبيوتر أو إعادة تعيينه ، يمر عبر سلسلة من التشخيصات تسمى اختبار Post-Power-On-Self. يتوج هذا التسلسل في تحديد موقع جهاز قابل للتمهيد ، مثل مرنة أو قرص مرن أو قرص صلب.
يكون الجهاز قابل للتمهيد إذا كان يحمل قطاع التمهيد مع تسلسل البايت 0x55 و 0xAA في Bytes 511 و 512 على التوالي. عندما تجد BIOS قطاع التمهيد ، يتم تحميله في الذاكرة على 0x0000:0x7C00 .
تطبيق بسيط لجهاز القابل للتمهيد:
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55 $ - $$ النتائج في CURRENT_POINTER - START_POINTER . بهذه الطريقة نحن نحسب المدة التي يستغرقها سجل التمهيد لدينا. بعد ذلك ، نحن فروع 510 منه ونملأ الأصفار ، وحصل على سجل التمهيد 512 بايت مع توقيع قطاع التمهيد.
على سبيل المثال ، لدينا $ - $$ تساوي 100. لذا ، لدينا 510 - 100 = 410 بايت مجاني. نحن نملأ هذه البايت 410 مع الأصفار. والآخر بايتان 511 و 512 هما توقيع قابل للتمهيد والذي نملأه بـ dw 0xAA55 .
منتهي! لدينا جهازنا القابل للتمهيد ويمكننا استبدال jmp $ بأي رمز تريده.
تنفيذ قطاع التمهيد
في البداية ، يعمل الكود الخاص بنا في الوضع الحقيقي.
الوضع الحقيقي هو وضع مبسط 16 بت موجود على جميع معالجات X86. كان الوضع الحقيقي أول تصميم في وضع X86 واستخدمه العديد من أنظمة التشغيل المبكرة. لأغراض التوافق ، تبدأ جميع معالجات X86 في التنفيذ في الوضع الحقيقي.
ما هو السيئ والجيد في الوضع الحقيقي؟
سلبيات
إيجابيات
نظرًا للعديد من القيود والمشاكل التي يواجهها الوضع الحقيقي ، نحتاج إلى التبديل إلى الوضع المحمي.
الوضع المحمي هو الوضع الرئيسي للتشغيل لمعالجات Intel الحديثة منذ 80286. يسمح بالعمل مع العديد من مساحات العناوين الظاهرية ، لكل منها بحد أقصى 4 جيجابايت من الذاكرة القابلة للمعالجة.
نظرًا لأن وحدة المعالجة المركزية التي تم تهيئتها بواسطة BIOS تبدأ في الوضع الحقيقي ، فإن التبديل إلى الوضع المحمي يمنعك من استخدام معظم مقاطعات BIOS. قبل التبديل إلى الوضع المحمي ، يجب عليك تعطيل المقاطعات ، بما في ذلك NMI ، وتمكين خط A20 وتحميل جدول الواصف العالمي.
خوارزمية للتبديل إلى الوضع المحمي:
cli
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pmتنفيذ التحول إلى PM
جدول الواصف العالمي
لكن يمكننا الذهاب إلى أبعد من ذلك ...
ما هو الوضع الطويل ولماذا إعداده؟
نظرًا لأن إدخال معالجات x86-64 تم تقديم وضع جديد أيضًا ، والذي يسمى الوضع الطويل. يتكون الوضع الطويل بشكل أساسي من وضعين فرعيين هما الوضع الفعلي 64 بت ووضع التوافق (32 بت).
ما يهمنا هو ببساطة وضع 64 بت لأن هذا الوضع يوفر الكثير من الميزات الجديدة مثل:
قبل التحول إلى وضع طويل ، يجب علينا التحقق مما إذا كانت وحدة المعالجة المركزية تدعم هذا الوضع. في حال ، إذا لم تدعم وحدة المعالجة المركزية الوضع الطويل ، فنحن بحاجة إلى التراجع عن الوضع المحمي.
اكتشف ما إذا كان الوضع الطويل يدعم
إذا كان الأمر كذلك ، فقم بالتبديل إلى الوضع الطويل
كل هذه الأوضاع رائعة ، لكن لا يمكننا كتابة نظام تشغيل في 512 بايت. لذلك ، يجب أن يعرف قطاع التمهيد كيفية تحميل نواةنا المترجمة من القرص الصلب.
عندما نكون في الوضع الحقيقي ، يمكننا استخدام المقاطعات BIOS للقراءة من القرص. في حالتنا ، INT 13,2 - Read Disk Sectors .
كيف تستخدمه؟
;; al = number of sectors to read (1 - 128)
;; ch = track/cylinder number
;; cl = sector number
;; dh = head number
;; dl = drive number
;; bx = pointer to buffer
mov ah , 0x02
mov al , 15
mov ch , 0x00
mov cl , 0x02
mov dh , 0x00
mov dl , 0
mov bx , KERNEL_OFFSET_IN_MEMORY
int 0x13 ينتج هذا الرمز إلى القراءة من القرص الثابت إلى العنوان KERNEL_OFFSET_IN_MEMORY . يقرأ 15 قطاعًا يبدأ من الثاني ويخزنه عن طريق العنوان KERNEL_OFFSET_IN_MEMORY .
نظرًا لأن صورة OS المترجمة الخاصة بنا هي سلسلة من قطاع التمهيد والنواة ، ونحن نعلم أن قطاع التمهيد لدينا هو 512 بايت ، يمكننا أن نكون متأكدين ، من أن kernel تبدأ في القطاع الثاني.
عند الانتهاء من القراءة بنجاح ، يمكننا الاتصال بالتعليمات في KERNEL_OFFSET_IN_MEMORY وإعطاء التنفيذ إلى kernel.
call KERNEL_OFFSET_IN_MEMORY
jmp $تنفيذ قراءة القرص
يمكننا رسم خط هنا حول قطاع التمهيد لدينا. التدفق بسيط:
call البسيطة ؛في هذه الخطوة ، أنهى قطاع التمهيد عمله ويبدأ العمل مع النواة.
يمكنك التنقل من خلال مصادر التمهيد ومحاولة الحصول على كيفية عملها.
عندما نقوم بالاتصال بالتعليم حسب العنوان ، يمكننا الحصول على بعض المشاكل. لا يمكننا التأكد من أن هذه التعليمات حسب العنوان هي kernel_main() . الحل بسيط.
يمكننا كتابة روتين فرعي متصل ببداية رمز النواة. هذه الدالة الخارجي للترويج الفرعي من kernel - kernel_main() . عندما يتم ربط ملفات الكائن معًا ، سيتم ترجمة هذه المكالمة إلى Call of kernel_main() .
global _start
[bits 32]
[extern kernel_main]
_start:
call kernel_main
jmp $تنفيذ دخول النواة
في هذه الخطوة ، لدينا نقطة دخول إلى طريقة kernel_main() . وهذه هي نقطة الدخول الخاصة بنا لنواة كاملة.
أعتقد ، ممل لشرح كيف يعمل #include وما يحدث في kernel_main() . يمكنك بسهولة اتباع الطرق التي أتصل بها منها.
دخول النواة في ج
هذا هو أبسط جزء.
نحن بحاجة إلى إنشاء صورة boot/boot.bin بتنسيق RAW الثنائي. للقيام بذلك ، نسمي nasm Assembler مع أعلام خاصة.
nasm boot/boot.asm -f bin -o boot/boot.binينتج عنه تنسيق ثنائي خام يمكنك تشغيله عبر QEMU.
في هذه الخطوة ، لدينا قطاع التمهيد يعمل.
نحتاج إلى بناء جميع المصادر من جميع المجلدات بشكل متكرر ، باستثناء مجلد boot .
يتم تجميع جميع ملفات C إلى ملفات الكائنات عبر ملفات gcc وملفات التجميع عبر nasm :
gcc -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -c < SOURCE > -o < OBJ_FILE >
nasm < SOURCE > -f elf -o < OBJ_FILE > ينتج عنه جميع ملفات الكائنات المطلوبة لربطها بالتنسيق الثنائي الخام. كل ما تبقى للقيام به هو ربطهم معًا عبر ld :
ld -o kernel/kernel.bin -Ttext 0x1000 kernel/kernel_entry.o < OBJ_FILES > --oformat binary لاحظ أن kernel/kernel_entry.o في المقام الأول لأن لدينا مشكلة في الاتصال kernel_main() . وبهذه الطريقة ، نضمن أن يتم استدعاء التعليمات الأولى من boot/kernel_entry.asm .
بعد كل شيء ، قمنا بتجميع صورة kernel بتنسيق ثنائي الخام.
نظرًا لأن قطاع التمهيد والنواة لدينا عبارة عن تنسيقات ثنائية خام ، يمكننا فقط تسلسلها.
cat boot/boot.bin kernel/kernel.bin > os-image.bin الآن ، يمكننا تشغيل os-image.bin عبر qemu-system-i386 . BIOS تحاول تحديد موقع القطاع القابل للتمهيد ، واكتشاف boot/boot.bin الخاص بنا ويرى التوقيع. يبدأ تنفيذ رمز التجميع الخاص بنا على boot/boot.bin الذي يقوم بتحميل kernel/kernel.bin عبر int 13،2 في الذاكرة وينفذها.
هكذا يعمل كل شيء معًا. لا تتردد في التنقل من خلال المشروع ، شكرًا؟
ترخيص معهد ماساتشوستس للتكنولوجيا (MIT)
حقوق الطبع والنشر (ج) 2016 يوجين أوبريزكوف
يتم منح الإذن بموجب هذا ، مجانًا ، لأي شخص يحصل على نسخة من هذا البرنامج وملفات الوثائق المرتبطة به ("البرنامج") ، للتعامل في البرنامج دون تقييد ، بما في ذلك على سبيل المثال لا الحصر حقوق استخدام الأشخاص ونسخها ودمجها ودمجها وتوزيعها وتوزيعها على ما يلي:
يجب إدراج إشعار حقوق الطبع والنشر أعلاه وإشعار الإذن هذا في جميع النسخ أو الأجزاء الكبيرة من البرنامج.
يتم توفير البرنامج "كما هو" ، دون أي ضمان من أي نوع ، صريح أو ضمني ، بما في ذلك على سبيل المثال لا الحصر ضمانات القابلية للتسويق واللياقة لغرض معين وعدم الانفجار. لا يجوز بأي حال من الأحوال أن يكون المؤلفون أو حاملي حقوق الطبع والنشر مسؤولاً عن أي مطالبة أو أضرار أو مسؤولية أخرى ، سواء في إجراء عقد أو ضرر أو غير ذلك ، ناشئة عن أو خارج البرنامج أو الاستخدام أو غيرها من المعاملات في البرنامج.