أهلا بكم من جديد في هذه السلسلة من التدوينات بعنوان "محاكاة لينوس تورفالدس". اليوم سنرى GDT. أولا علينا أن نرى ما هو GDT. حسب ويكيبيديا:
• جدول الواصف العام or GDT هي بنية البيانات المستخدمة من قبل إنتل x86- معالجات عائلية تبدأ بـ 80286 من أجل تحديد خصائص مناطق الذاكرة المختلفة المستخدمة أثناء تنفيذ البرنامج ، بما في ذلك العنوان الأساسي والحجم وامتيازات الوصول مثل قابلية التنفيذ وقابلية الكتابة
ما يُترجم سيكون جدول واصف عالمي ، وهي بنية بيانات مستخدمة في معالجات Intel x86 منذ 80286 لتحديد خصائص مناطق الذاكرة المختلفة المستخدمة أثناء تنفيذ البرنامج.
باختصار ، إذا كنا نستخدم معالج Intel x86 ، فيجب علينا تحديد GDT للاستخدام الصحيح للذاكرة. لن نقوم بالكثير من التعقيد وسنقوم بتعريف 3 مداخل في الجدول:
- إدخال فارغ ، مطلوب لجميع الجداول.
- تذكرة للقسم البيانات، سوف نستخدم الحد الأقصى ، والذي يبلغ 32 جيجابايت في 4 بت.
- تذكرة للقسم الكود، سوف نستخدم الحد الأقصى ، والذي يبلغ 32 جيجابايت في 4 بت.
كما ترى ، ستستخدم البيانات والرمز نفس المساحة. حسنًا ، نحن الآن بصدد تنفيذه. لهذا سوف نستخدم هيكلين ، الأول سيكون مسؤولاً عن احتواء مؤشر للبيانات الحقيقية لـ GDT الخاص بنا. والثاني سيكون مصفوفة بإدخالات GDT. دعونا نحددها أولا
struct Entry{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));
ربما لاحظت __ سمة __ غريبة ((معبأة)) في نهاية الهياكل. هذا يخبر دول مجلس التعاون الخليجي بعدم تحسين الهياكل لأن ما نريده هو تمرير البيانات كما هي إلى المعالج. الآن سنقوم بعمل وظيفة لتثبيت GDT. قبل أن نعلن عن الهياكل ، سنبدأ الآن في تهيئتها.
struct ND::GDT::Entry gdt[3];
struct ND::GDT::Ptr gp;
void ND::GDT::Install()
{
gp.limit=(sizeof(struct ND::GDT::Entry)*3)-1;
gp.base=(uint32_t)&gdt;
}
لذلك علينا أن نبني المؤشر الذي ينتقل إلى جدول 3 مدخلات.
void ND::GDT::SetGate(int num, uint32_t base, uint32_t limit, uint8_t access,uint8_t gran)
{
gdt[num].base_low=(base & 0xFFFF);
gdt[num].base_middle=(base >> 16) & 0xFF;
gdt[num].base_high=(base >> 24) & 0xFF;
gdt[num].limit_low=(limit & 0xFFFF);
gdt[num].granularity=(limit >> 16) & 0x0F;
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access=access;
}
ونسميها 3 مرات من وظيفة التثبيت
ND::GDT::SetGate(0,0,0,0,0); /* NULL segmente entry */
ND::GDT::SetGate(1,0,0xFFFFFFFF,0x9A,0xCF); /* 4 GiB for Code Segment */
ND::GDT::SetGate(2,0,0xFFFFFFFF,0x92,0xCF); /* 4 GiB for Data segment */
أخيرًا ، يجب أن نخبر المعالج أن لدينا GDT ، حتى يتمكن من تحميله ، وفي حالتنا عند تحميل النواة باستخدام GRUB ، قم بالكتابة فوق GRUB GDT. لتحميل GDT هناك تعليمات في ASM تسمى lgdt (أو lgdtl اعتمادًا على البنية) ، سنستخدمها.
asm volatile("lgdtl (gp)");
asm volatile(
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movw %ax, %ss \n"
"ljmp $0x08, $next \n"
"next: \n"
);
حسنًا ، بمجرد الانتهاء من هذا ، سيحصل نظامنا بالفعل على GDT. في الفصل التالي سنرى IDT ، وهو جدول مشابه جدًا لـ GDT ولكن مع انقطاعات. لقد وضعت بعض رسائل الحالة والتأكيد مع GDT لذا يبدو NextDivel الآن كما يلي:
ربما تكون بنية 64 بت أكثر ملاءمة لهذه الأوقات ، فهي عبارة عن تراكم لمواصلة استخدام 8086.
لقد كنت أبحث عن معلومات حول GDT في x86_64 وأعتقد أنها تتبع النموذج القديم بعلم خاص. لا يزال يتم استخدام عنوان 32 بت. الآن لا أعرف بالضبط كيف أفعل ذلك بشكل صحيح. بعض الروابط:
http://wiki.osdev.org/Entering_Long_Mode_Directly
http://f.osdev.org/viewtopic.php?f=1&t=16275
كانت أولى مساهماتك جيدة جدًا ، لكن أعتقد أن العنوان يجب أن يكون
"محاكاة ريتشارد ستالمان" أو على الأقل أعتقد ذلك ،،،
تحياتي
أنشأ لينوس نواة لينكس ، وأنشأ ستالمان جنو وهي أدوات وأوامر يونكس.
العنوان مناسب لأنك تقوم بإنشاء نواة.
تحياتي!
شكرًا جزيلاً لك على الإجابة على جميع أسئلتي والتحلي بالصبر ، فأنا أعرف فقط الأساسيات كمُجمِّع ولا أعرف شيئًا تقريبًا عن C ، لكنني أحب ذلك حقًا ، والآن أنا مرتبك قليلاً مع GDT ، دعنا نرى ما إذا كنت تفهم.
ستحتوي GDT على "الواصفات" العالمية التي يمكن الوصول إليها دائمًا بواسطة أي برنامج ، وتشير هذه الواصفات إلى الأقسام التي سيتم تنفيذ (البرنامج) فيها؟ أو خلاف ذلك.