تنفيذ وحدة مورّد وحدة التحكّم عن بُعد في شاشة الفيديو (pKVM)

توضّح هذه الصفحة كيفية تنفيذ وحدة بائع تستند إلى آلة افتراضية محمية بنواة (pKVM).

في الإصدارات android16-6.12 والإصدارات الأحدث، بعد الانتهاء من هذه الخطوات، من المفترض أن تظهر شجرة دليل مشابهة لما يلي:

BUILD.bazel
el1.c
hyp/
    BUILD.bazel
    el2.c

للاطّلاع على مثال كامل، راجِع إنشاء وحدة pKVM باستخدام DDK.

في الإصدارات 15-6.6 من Android والإصدارات الأقدم، اتّبِع الخطوات التالية:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. أضِف رمز برنامج 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 هي بنية تتضمّن عمليات رد الاتصال إلى برنامج Hypervisor الخاص بـ pKVM. تتّبع هذه البنية قواعد واجهة التطبيق الثنائية نفسها التي تتّبعها واجهات GKI.

  2. أنشئ hyp/Makefile لإنشاء رمز برنامج المشرف:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. أضِف رمز نواة 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);
    
  4. أخيرًا، أنشئ قواعد الإنشاء.

    بالنسبة إلى الإصدار 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)، ويجب إضافة ما يلي إلى سطر أوامر النواة:

kvm-arm.protected_modules=mod1,mod2,mod3,...

تتضمّن وحدات pKVM الخاصة بالمورّد والمخزّنة في initramfs التوقيع والحماية الخاصَّين بـ initramfs.

في حال تعذُّر تحميل إحدى وحدات مورّد pKVM، سيُعتبر النظام غير آمن ولن يكون من الممكن بدء تشغيل جهاز افتراضي محمي.

استدعاء دالة EL2 (برنامج مراقبة) من EL1 (وحدة النواة)

استدعاء برنامج Hypervisor (HVC) هو تعليمات تتيح للنواة استدعاء برنامج Hypervisor. مع طرح وحدات pKVM الخاصة بالمورِّدين، يمكن استخدام HVC لطلب تنفيذ دالة في EL2 (في وحدة برنامج المشرف) من EL1 (وحدة النواة):

  1. في رمز 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;
  }
  1. في رمز 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);
    
  2. في رمز EL1 (el1.c)، استدعِ HVC:

    pkvm_el2_mod_call(hvc_number);