یک ماژول فروشنده 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;
    }
    

    API ماژول فروشنده 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 ) را اضافه کنید. بخش init این کد باید شامل فراخوانی به 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. در نهایت، root 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 (hypervisor) از EL2 (ماژول هسته)

فراخوانی Hypervisor (HVC) دستورالعملی است که به هسته اجازه می‌دهد تا هایپروایزر را فراخوانی کند. با معرفی ماژول‌های فروشنده 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;
  }

اندروید-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);