實作 pKVM 供應商模組

本頁面說明如何實作受保護的 Kernel-based Virtual Machine (pKVM) 供應商模組。完成這些步驟後,您應該擁有類似如下的目錄樹狀結構:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. 新增 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;
    }
    

    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)。此程式碼的初始化部分必須包含對 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 供應商模組,您可以使用 modprobe 載入 pKVM 供應商模組。但基於安全考量,載入作業必須在卸除權限前發生。如要載入 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 處理常式:

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) 中,在 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); // 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);