הטמעת מודול של ספק pKVM

בדף הזה מוסבר איך להטמיע מודול ספק של מכונה וירטואלית (pKVM) מוגנת מבוססת ליבה (kernel). בסיום השלבים האלה, אמור להיות לכם עץ ספריות שדומה לזה:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. מוסיפים את קוד ה-hypervisor של EL2 (el2.c). לכל הפחות, הקוד הזה צריך להצהיר על פונקציה התחלתית שמקבלת הפניה למבנה 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 הוא מבנה שמסתיר קריאות חוזרות (callback) אל pKVM hypervisor. המבנה הזה פועל לפי אותם כללי ABI כמו בממשקי GKI.

  2. כדי ליצור את הקוד של Hypervisor, יוצרים את hyp/Makefile:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. מוסיפים את קוד הליבה של EL1 (el1.c). הקטע ההתחלתי של הקוד הזה חייב להכיל קריאה ל-pkvm_load_el2 module כדי לטעון את קוד ה-hypervisor של 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) וצריך להוסיף את הפרטים הבאים ל שורת הפקודה בליבה (kernel):

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

מודולים של ספקי pKVM שמאוחסנים ב-initramfs יורשים את החתימה ואת ההגנה של initramfs.

אם אחד מהמודולים של ספק ה-pKVM נכשל, המערכת נחשבת לא מאובטחת ולא ניתן יהיה להפעיל מכונה וירטואלית מוגנת.

קריאה לפונקציית EL2 (hypervisor) מ-EL2 (מודול ליבה)

קריאת hypervisor (HVC) היא הוראה שמאפשרת לליבה (kernel) לקרוא ל-hypervisor. הצגת המודולים של ספקי pKVM מאפשרת להשתמש ב-HVC כדי להפעיל פונקציה להרצה ב-EL2 (במודול hypervisor) מ-EL1 (מודול הליבה):

  1. בקוד EL2 (el2.c), מצהירים על ה-handler של EL2:

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. בקוד ה-EL1 (el1.c), יש לרשום את ה-handler של 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);
    
    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);
    
  3. בקוד ה-EL1 (el1.c), קוראים ל-HVC:

    pkvm_el2_mod_call(hvc_number);