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