实现 pKVM 供应商模块

本页面介绍了如何实现受保护的基于内核的虚拟机 (pKVM) 供应商模块。完成这些步骤后,您应该会得到一个类似于以下的目录树:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. 添加 EL2 hypervisor 代码 (el2.c)。至少,此代码必须声明一个接受对 pkvm_module_ops 结构引用的 init 函数

    #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 hypervisor 的回调。此结构遵循与 GKI 接口相同的 ABI 规则。

  2. 创建 hyp/Makefile 以构建 hypervisor 代码

    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 hypervisor 代码。

    #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 供应商模块加载失败,则系统被认为是不安全的,并且无法启动受保护的虚拟机。

从 EL1(内核模块)调用 EL2(hypervisor)函数

hypervisor 调用 (HVC) 是一条指令,允许内核调用 hypervisor。随着 pKVM 供应商模块的引入,HVC 可用于从 EL1(内核模块)调用在 EL2(在 hypervisor 模块中)运行的函数

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