تنفيذ وحدة مورِّد pKVM

تشرح هذه الصفحة كيفية تطبيق وحدة مورِّد جهاز افتراضي محمية تستند إلى النواة (pKVM). عند الانتهاء من هذه الخطوات، من المفترض أن يكون لديك شجرة دليل مشابهة لـ:

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 هي بنية تحوي طلبات استدعاء لخدمة افتراضية لنظام التشغيل pKVM. تتّبع هذه البنية قواعد ABI نفسها التي تتّبعها واجهات 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. أخيرًا، أنشئ ملف 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 (نظام التشغيل الظاهري) من EL2 (وحدة النواة)

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

  1. في رمز EL2 (el2.c)، أدخِل معالِج EL2:

الإصدار 14 من نظام التشغيل Android

  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);