في هذه المقالة ، أود أن أعتبر الجوانب العملية لإنشاء برنامج hypervisor
بسيط يعتمد على تقنية المحاكاة الافتراضية للأجهزة Intel VMX.
المحاكاة الافتراضية للأجهزة هي مجال متخصص إلى حد ما في برمجة النظام وليس لديها مجتمع كبير ، في روسيا بالتأكيد. آمل أن تساعد المواد الواردة في المقالة أولئك الذين يرغبون في اكتشاف المحاكاة الافتراضية للأجهزة والإمكانيات التي توفرها. كما قيل في البداية ، أريد أن أفكر فقط في الجانب العملي دون الغوص في النظرية ، لذلك من المفترض أن القارئ على دراية بهندسة x86-64 ولديه على الأقل فكرة عامة عن آليات VMX.
مصادر المقال .
لنبدأ بوضع أهداف لبرنامج Hypervisor:
- يعمل قبل تحميل نظام التشغيل الضيف
- دعم معالج منطقي واحد و 4 غيغابايت من الذاكرة الفعلية للضيف
- التأكد من أن نظام تشغيل الضيف يعمل بشكل صحيح مع الأجهزة المعروضة في منطقة الذاكرة الفعلية
- معالجة VMexits
- يجب تشغيل نظام التشغيل الضيف من الأوامر الأولى في بيئة افتراضية.
- إخراج معلومات تصحيح الأخطاء عبر منفذ COM (طريقة عامة وسهلة التنفيذ)
كنظام تشغيل ضيف ، اخترت Windows 7 x32 ، حيث تم تعيين القيود التالية:
- تشارك وحدة معالجة مركزية واحدة فقط
- تم تعطيل خيار PAE الذي يسمح لنظام تشغيل 32 بت باستخدام مقدار الذاكرة الفعلية الذي يزيد عن 4 جيجابايت
- BIOS في الوضع القديم ، تم تعطيل UEFI
وصف أداة تحميل التشغيل
لكي يبدأ برنامج مراقبة الأجهزة الافتراضية عند بدء تشغيل جهاز الكمبيوتر ، اخترت الطريقة الأسهل ، أي أنني قمت بتدوين أداة تحميل التشغيل الخاصة بي في قطاع MBR للقرص الذي تم تثبيت نظام التشغيل الضيف عليه. كان من الضروري أيضًا وضع رمز برنامج Hypervisor في مكان ما على القرص. في حالتي ، يقرأ MBR الأصلي أداة تحميل التشغيل بدءًا من قطاع 2048 ، مما يوفر مساحة خالية مشروطة للكتابة إلى (2047 * 512) كيلوبايت. هذا أكثر من كافٍ لاستيعاب جميع مكونات برنامج مراقبة الأجهزة الافتراضية.
فيما يلي تخطيط برنامج Hypervisor على القرص ، يتم تعيين كافة القيم في القطاعات.

عملية التنزيل كما يلي:

- يقرأ loader.mbr محمل رمز المحمل من القرص وينقل التحكم إليه.
- يبدل load.main إلى الوضع الطويل ، ثم يقرأ جدول عناصر Load.table المحملة ، والذي يتم على أساسه إجراء تحميل إضافي لمكونات برنامج Hypervisor في الذاكرة.
- بعد انتهاء محمل الإقلاع من العمل في الذاكرة الفعلية على العنوان 0x100000000 ، يوجد رمز برنامج مراقبة الأجهزة الافتراضية ، تم اختيار هذا العنوان بحيث يمكن استخدام النطاق من 0 إلى 0xFFFFFFFF للتخطيط المباشر للذاكرة الفعلية للضيف.
- يقوم Windows mbr بالتمهيد على العنوان الفعلي 0x7C00.
أود أن ألفت الانتباه إلى حقيقة أن محمل الإقلاع بعد التبديل إلى الوضع الطويل لم يعد بإمكانه استخدام خدمات BIOS للعمل مع الأقراص الفعلية ، لذلك استخدمت "واجهة وحدة تحكم المضيف المتقدمة" لقراءة القرص.
مزيد من التفاصيل حول التي يمكن العثور عليها
هنا .
الوصف الوظيفي Hypervisor
بعد أن يتلقى برنامج مراقبة الأجهزة الافتراضية التحكم ، فإن مهمته الأولى هي تهيئة البيئة التي يجب أن يعمل فيها ، للقيام بذلك ، تسمى الوظائف بالتسلسل:
- InitLongModeGdt () - إنشاء وتحميل جدول من 4 واصفات: NULL ، CS64 ، DS64 ، TSS64
- InitLongModeIdt (isr_vector) - تهيئة أول ناقلات 32 مقاطعة بواسطة معالج مشترك ، أو بالأحرى ، كعبها
- InitLongModeTSS () - تهيئة جزء حالة المهمة
- InitLongModePages () - تهيئة الترحيل:
[0x00000000 - 0xFFFFFFFF] - حجم الصفحة 2 ميجابايت ، تعطيل ذاكرة التخزين المؤقت ؛
[0x100000000 - 0x13FFFFFFF] - حجم الصفحة 2 ميجابايت ، إعادة كتابة ذاكرة التخزين المؤقت ، الصفحات العامة ؛
[0x140000000 - n] - غير موجود ؛ - InitControlAndSegmenRegs () - تسجيلات مقطع إعادة التحميل
بعد ذلك ، تحتاج إلى التأكد من أن المعالج يدعم VMX ، ويتم إجراء التحقق بواسطة وظيفة
CheckVMXConditions () :
- CPUID.1: يجب تعيين ECX.VMX [بت 5] على 1
- في سجل MSR ، يجب تعيين IA32_FEATURE_CONTROL bit 2 - تمكن VMXON من تشغيل SMX و bit 0 - Lock (مناسب عند تصحيح الأخطاء في Bochs)
إذا كان كل شيء على ما يرام ويعمل برنامج Hypervisor على معالج يدعم ظاهرية الأجهزة ، فانتقل إلى التهيئة الأولية لـ VMX ، راجع وظيفة
InitVMX () :
- إنشاء مناطق ذاكرة VMXON و VMCS (هياكل بيانات التحكم في الجهاز الظاهري) بحجم 4096 بايت. يتم تسجيل معرف مراجعة VMCS المأخوذ من MSR IA32_VMX_BASIC في أول 31 بت من كل منطقة.
- يتم التحقق من أنه في النظام يسجل CR0 و CR4 يتم تعيين كافة البتات وفقًا لمتطلبات VMX.
- يتم وضع المعالج المنطقي في وضع الجذر vmx بواسطة الأمر VMXON (العنوان الفعلي لمنطقة VMXON كوسيطة).
- يقوم الأمر VMCLEAR (VMCS) بتعيين حالة التشغيل لـ VMCS إلى Clear ، ويقوم الأمر بتعيين القيم الخاصة بالتطبيق إلى VMCS.
- يقوم الأمر VMPTRLD (VMCS) بتحميل عنوان VMCS الحالي الذي تم تمريره كوسيطة في مؤشر VMCS الحالي.
سيبدأ تنفيذ نظام التشغيل الضيف في الوضع الحقيقي من العنوان 0x7C00 ، حيث نتذكر ، يضع اللودر.main win7.mbr. لإعادة إنشاء بيئة افتراضية مماثلة لتلك التي يتم فيها تنفيذ mbr عادة ، يتم
استدعاء الدالة
InitGuestRegisterState () ، والتي تقوم بتعيين تسجيلات vmx غير الجذر كما يلي:
CR0 = 0x10 CR3 = 0 CR4 = 0 DR7 = 0 RSP = 0xFFD6 RIP = 0x7C00 RFLAGS = 0x82 ES.base = 0 CS.base = 0 SS.base = 0 DS.base = 0 FS.base = 0 GS.base = 0 LDTR.base = 0 TR.base = 0 ES.limit = 0xFFFFFFFF CS.limit = 0xFFFF SS.limit = 0xFFFF DS.limit = 0xFFFFFFFF FS.limit = 0xFFFF GS.limit = 0xFFFF LDTR.limit = 0xFFFF TR.limit = 0xFFFF ES.access rights = 0xF093 CS.access rights = 0x93 SS.access rights = 0x93 DS.access rights = 0xF093 FS.access rights = 0x93 GS.access rights = 0x93 LDTR.access rights = 0x82 TR.access rights = 0x8B ES.selector = 0 CS.selector = 0 SS.selector = 0 DS.selector = 0 FS.selector = 0 GS.selector = 0 LDTR.selector = 0 TR.selector = 0 GDTR.base = 0 IDTR.base = 0 GDTR.limit = 0 IDTR.limit = 0x3FF
وتجدر الإشارة إلى أن مجال الحد لذاكرة التخزين المؤقت للوصف للمقطع يسجل DS و ES هو 0xFFFFFFFF. هذا مثال على استخدام الوضع غير الواقعي - ميزة معالج x86 تسمح لك بتجاوز حد المقاطع في الوضع الحقيقي. يمكنك قراءة المزيد عن هذا
هنا .
أثناء وجوده في وضع vmx not-root ، قد يواجه نظام التشغيل الضيف موقفًا حيث يكون من الضروري إعادة التحكم إلى المضيف في وضع الجذر vmx. في هذه الحالة ، يحدث مخرج VM يتم خلاله حفظ الحالة الحالية لـ vmx non-root وتحميل vmx-root. تتم تهيئة جذر vmx بواسطة
دالة InitHostStateArea () ، التي تحدد القيمة التالية
للتسجيلات :
CR0 = 0x80000039 CR3 = PML4_addr CR4 = 0x420A1 RSP = STACK64 RIP = VMEXIT_handler ES.selector = 0x10 CS.selector = 0x08 SS.selector = 0x10 DS.selector = 0x10 FS.selector = 0x10 GS.selector = 0x10 TR.selector = 0x18 TR.base = TSS GDTR.base = GDT64 IDTR.base = IDTR
بعد ذلك ، يتم إنشاء مساحة العنوان الفعلي
للضيف ( دالة
InitEPT () ). هذه واحدة من أهم اللحظات عند إنشاء برنامج مراقبة الأجهزة الافتراضية ، لأن الحجم أو النوع المضبوط بشكل غير صحيح في أي من مواقع الذاكرة يمكن أن يؤدي إلى أخطاء قد لا تظهر على الفور ، ولكن مع احتمال كبير سيؤدي إلى فرامل غير متوقعة أو توقف نظام الضيف. بشكل عام ، هناك القليل من المرح هنا ومن الأفضل إيلاء اهتمام كاف لضبط الذاكرة.
توضح الصورة التالية نموذج مساحة العنوان الفعلي للضيف:

ما نراه هنا:
- [0 - 0xFFFFFFFF] النطاق الكامل لمساحة عنوان الضيف. النوع الافتراضي: إعادة الكتابة
- [0xA0000 - 0xBFFFFF] - ذاكرة فيديو. النوع: غير قابل للوصول
- [0xBA647000 - 0xFFFFFFFF] - ذاكرة وصول عشوائي للأجهزة. النوع: غير قابل للوصول
- [0x0000000 - 0xCFFFFFFF] - ذاكرة الفيديو. النوع: دمج الكتابة
- [0xD0000000 - 0xD1FFFFFF] - ذاكرة فيديو. النوع: دمج الكتابة
- [0xFA000000 - 0xFAFFFFFF] - ذاكرة فيديو. النوع: دمج الكتابة
أخذت المعلومات لإنشاء مثل هذه المناطق من الأداة المساعدة RAMMap (علامة التبويب النطاقات المادية). كما استخدمت البيانات من Windows Device Manager. بالطبع ، على جهاز كمبيوتر آخر ، من المحتمل أن تختلف نطاقات العناوين. أما بالنسبة لنوع ذاكرة الضيف ، في التطبيق ، يتم تحديد النوع فقط من خلال القيمة المحددة في جداول EPT. إنه بسيط ، ولكنه ليس صحيحًا تمامًا ، وبشكل عام ، يجب مراعاة نوع الذاكرة التي يريد نظام تشغيل الضيف تثبيتها في عنونة صفحته.
بعد اكتمال إنشاء مساحة عنوان الضيف ، يمكنك المتابعة إلى
إعدادات حقل التحكم في تنفيذ VM
( وظيفة
InitExecutionControlFields () ). هذه مجموعة كبيرة إلى حد ما من الخيارات التي تسمح لك بتعيين ظروف التشغيل لنظام تشغيل الضيف في وضع vmx not-root. يمكنك ، على سبيل المثال ، تعقب المكالمات إلى منافذ الإدخال / الإخراج أو مراقبة التغييرات في تسجيلات MSR. ولكن في حالتنا ، أستخدم فقط القدرة على التحكم في إعداد بتات معينة في سجل CR0. والحقيقة هي أن 30 بت (CD) و 29 (NW) شائعة في كلا الوضعين vmx non-root و vmx root ، وإذا قام نظام الضيف الضيف بتعيين هذه البتات إلى 1 ، فسيؤثر ذلك سلبًا على الأداء.
تم تقريبًا تقريبًا الانتهاء من عملية تكوين برنامج مراقبة الأجهزة الافتراضية ، يبقى فقط لفرض التحكم في الانتقال إلى وضع الضيف vmx غير الجذر والعودة إلى جذر vmx لوضع المضيف. يتم تعيين الإعدادات في الوظائف:
إعدادات
InitVMEntryControl () للانتقال إلى vmx non-root:
- تحميل الضيف IA32_EFER
- تحميل الضيف IA32_PAT
- تحميل MSRs الضيف (IA32_MTRR_PHYSBASE0 ، IA32_MTRR_PHYSMASK0 ، IA32_MTRR_DEF_TYPE)
إعدادات
InitVMExitControl () للتبديل إلى جذر vmx:
- تحميل المضيف IA32_EFER ؛
- حفظ الضيف IA32_EFER ؛
- تحميل المضيف IA32_PAT ؛
- حفظ الضيف IA32_PAT ؛
- Host.CS.L = 1 ، Host.IA32_EFER.LME = 1 ، Host.IA32_EFER.LMA = 1 ؛
- حفظ MSRs الضيف (IA32_MTRR_PHYSBASE0 ، IA32_MTRR_PHYSMASK0 ، IA32_MTRR_DEF_TYPE) ؛
- تحميل MSRs المضيف (IA32_MTRR_PHYSBASE0 ، IA32_MTRR_PHYSMASK0 ، IA32_MTRR_DEF_TYPE) ؛
الآن بعد اكتمال جميع الإعدادات ،
تضع وظيفة
VMLaunch () المعالج في وضع vmx غير الجذر ويبدأ نظام الضيف (Guest) في العمل. كما ذكرت سابقًا ، يمكن تعيين الشروط في إعدادات التحكم في تنفيذ vm ، وفي هذه الحالة سيعيد برنامج hypervisor التحكم إلى نفسه في وضع الجذر vmx. في مثالي البسيط ، أعطي نظام التشغيل الضيف الحرية الكاملة للعمل ، ومع ذلك ، في بعض الحالات ، سيظل برنامج مراقبة الأجهزة الافتراضية بحاجة إلى التدخل وتعديل نظام التشغيل.
- إذا حاول نظام تشغيل الضيف تغيير بتات CD و NW في سجل CR0 ، فإن معالج الخروج من VM
يصحح البيانات المسجلة في CR0. يتم أيضًا تعديل حقل ظل قراءة CR0 بحيث يتلقى نظام تشغيل الضيف القيمة المسجلة عند قراءة CR0. - تنفيذ الأمر xsetbv. يستدعي هذا الأمر دائمًا VM Exit ، بغض النظر عن الإعدادات ، لذلك أضفت للتو تنفيذه في وضع الجذر vmx.
- تنفيذ أمر كيوبيد. يستدعي هذا الأمر أيضًا خروج VM غير مشروط. لكني قمت بتغيير طفيف في معالجها. إذا كانت القيم الموجودة في وسيطة eax هي 0x80000002 - 0x80000004 ، فلن تُرجع وحدة المعالجة المركزية اسم العلامة التجارية للمعالج ، ولكن السطر: VMX Study Core :) يمكن رؤية النتيجة في لقطة الشاشة:

الملخص
إن برنامج Hypervisor المكتوب كمثال للمقال قادر تمامًا على دعم التشغيل المستقر لنظام التشغيل الضيف ، على الرغم من أنه بالطبع ليس حلاً كاملاً. لا يتم استخدام Intel VT-d ، يتم تنفيذ دعم معالج منطقي واحد فقط ، ولا يوجد تحكم في الانقطاعات وتشغيل الأجهزة الطرفية. بشكل عام ، لم أستخدم أي شيء تقريبًا من مجموعة غنية من الأدوات التي توفرها Intel للمحاكاة الافتراضية للأجهزة. ومع ذلك ، إذا كان المجتمع مهتمًا ، فسأستمر في الكتابة عن Intel VMX ، خاصة وأن هناك شيء للكتابة عنه.
نعم ، لقد نسيت تقريباً ، من المناسب تصحيح برنامج مراقبة الأجهزة الافتراضية ومكوناته باستخدام Bochs. في البداية هي أداة لا غنى عنها. لسوء الحظ ، يختلف تنزيل برنامج Hypervisor في Bochs عن التنزيل على جهاز كمبيوتر فعلي. في وقت ما ، قمت بعمل جمعية خاصة لتبسيط هذه العملية ، وسأحاول ترتيب المصادر ووضعها مع المشروع في المستقبل القريب.
هذا كل شيء. شكرا لكم على اهتمامكم.