實施 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 供應商模組 API 是一個封裝 pKVM 管理程式回呼的架構體。此結構遵循與 GKI 介面相同的 ABI 規則。

  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調用,以載入步驟 1 中的 EL2 虛擬機器管理程式碼。

    #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 , ...

儲存在initramfs中的 pKVM 供應商模組繼承了initramfs的簽章和保護。

如果 pKVM 供應商模組之一無法載入,系統將被視為不安全,並且無法啟動受保護的虛擬機器。

從 EL2(核心模組)呼叫 EL2(管理程式)函數

虛擬機器管理程式呼叫 (HVC)是一條讓核心呼叫虛擬機器管理程式的指令。隨著 pKVM 供應商模組的引入,HVC 可用於從 EL1(核心模組)呼叫在 EL2(在管理程式模組中)運行的函數:

  1. 在 EL2 代碼 ( el2.c ) 中,宣告 EL2 處理程序:

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. 在您的 EL1 代碼 ( el1.c ) 中,在 pKVM 供應商模組中註冊 EL2 處理程序:

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