توضّح هذه الصفحة كيفية تنفيذ وحدة مورّد لجهاز افتراضي مستند إلى نواة محمية (pKVM).
بالنسبة إلى الإصدار 6.12 من نظام التشغيل Android 16 والإصدارات الأحدث، عند الانتهاء من تنفيذ هذه الخطوات، من المفترض أن تظهر لك شجرة دليل مشابهة لما يلي:
BUILD.bazel
el1.c
hyp/
BUILD.bazel
el2.c
للاطّلاع على مثال كامل، يُرجى الاطّلاع على مقالة إنشاء وحدة pKVM باستخدام DDK .
بالنسبة إلى الإصدارات 6.6 من Android 15 والإصدارات الأقدم:
Makefile
el1.c
hyp/
Makefile
el2.c
أضِف رمز برنامج إدارة الأجهزة الظاهرية EL2 (
el2.c
). على الأقل، يجب أن يعلن هذا الرمز عن دالة init تقبل إشارة إلى بنيةpkvm_module_ops
:#include <asm/kvm_pkvm_module.h> int pkvm_driver_hyp_init(const struct pkvm_module_ops *ops) { /* Init the EL2 code */ return 0; }
واجهة برمجة التطبيقات الخاصة بوحدة المورّد في pKVM هي بنية تُغلِّف طلبات إعادة الاتصال بخدمة افتراضية لجهاز تحكم في مثيل افتراضي (VM) في pKVM. تتّبع هذه البنية قواعد ABI نفسها التي تتّبعها واجهات GKI.
أنشئ
hyp/Makefile
لإنشاء رمز برنامج تشغيل الأجهزة الافتراضية:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
أضِف رمز نواة EL1 (
el1.c
). يجب أن يحتوي قسم بدء تشغيل هذا الرمز على طلب إلىpkvm_load_el2 module
لتحميل رمز مشرف EL2 من الخطوة 1.#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <asm/kvm_pkvm_module.h> int __kvm_nvhe_pkvm_driver_hyp_init(const struct pkvm_module_ops *ops); static int __init pkvm_driver_init(void) { unsigned long token; return pkvm_load_el2_module(__kvm_nvhe_pkvm_driver_hyp_init, &token); } module_init(pkvm_driver_init);
أخيرًا، أنشئ قواعد التصميم.
بالنسبة إلى الإصدار android16-6.12 والإصدارات الأحدث، يُرجى الرجوع إلى مقالة إنشاء وحدة pKVM باستخدام DDK لإنشاء
ddk_library()
لمستوى EL2 وddk_module()
لمستوى EL1.بالنسبة إلى الإصدار android15-6.6 والإصدارات الأقدم، أنشئ ملف makefile الجذر لربط رمز EL1 و EL2 معًا:
ifneq ($(KERNELRELEASE),) clean-files := hyp/hyp.lds hyp/hyp-reloc.S obj-m := pkvm_module.o pkvm_module-y := el1.o hyp/kvm_nvhe.o $(PWD)/hyp/kvm_nvhe.o: FORCE $(Q)$(MAKE) $(build)=$(obj)/hyp $(obj)/hyp/kvm_nvhe.o else all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean endif
تحميل وحدة pKVM
كما هو الحال مع وحدات مورّدي GKI، يمكن تحميل وحدات مورّدي pKVM باستخدام modprobe.
ومع ذلك، يجب أن يتم التحميل قبل إزالة الأذونات لأسباب تتعلق بالأمان.
لتحميل وحدة pKVM، عليك التأكّد من تضمين الوحدات في
نظام الملفات الجذر (initramfs
) ويجب إضافة ما يلي إلى
سطر أوامر kernel:
kvm-arm.protected_modules=mod1,mod2,mod3,...
ترث وحدات مورّدي pKVM المخزّنة في initramfs
توقيع initramfs
وحمايته.
إذا تعذّر تحميل إحدى وحدات مورّدي pKVM، يُعتبر النظام غير آمن ولن يكون من الممكن تشغيل جهاز افتراضي محمي.
استدعاء دالة EL2 (نظام التشغيل الظاهري) من EL1 (وحدة النواة)
طلب تشغيل نظام التشغيل الظاهري (HVC) هو تعليمات تتيح للنواة طلب تشغيل نظام التشغيل الظاهري. مع إدخال وحدات مورّدي pKVM، يمكن استخدام HVC لطلب تنفيذ دالة في EL2 (في وحدة المشرف الظاهري) من EL1 (وحدة النواة):
- في رمز EL2 (
el2.c
)، أدخِل معالِج EL2:
Android-14
void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
{
/* Handle the call */
cpu_reg(ctx, 1) = 0;
}
Android-15
void pkvm_driver_hyp_hvc(struct user_pt_regs *regs)
{
/* Handle the call */
regs->regs[0] = SMCCC_RET_SUCCESS;
regs->regs[1] = 0;
}
في رمز EL1 (
el1.c
)، سجِّل معالِج EL2 في ملف pKVM الخاص بموفّر الوحدة:int __kvm_nvhe_pkvm_driver_hyp_init(const struct pkvm_module_ops *ops); void __kvm_nvhe_pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx); // Android14 void __kvm_nvhe_pkvm_driver_hyp_hvc(struct user_pt_regs *regs); // Android15 static int hvc_number; static int __init pkvm_driver_init(void) { long token; int ret; ret = pkvm_load_el2_module(__kvm_nvhe_pkvm_driver_hyp_init,token); if (ret) return ret; ret = pkvm_register_el2_mod_call(__kvm_nvhe_pkvm_driver_hyp_hvc, token) if (ret < 0) return ret; hvc_number = ret; return 0; } module_init(pkvm_driver_init);
في رمز EL1 (
el1.c
)، اتصل بفريق دعم العملاء من المستوى العالي:pkvm_el2_mod_call(hvc_number);