本页面介绍了如何实现受保护的基于内核的虚拟机 (pKVM) 供应商模块。完成这些步骤后,您应该会得到一个类似于以下的目录树:
Makefile
el1.c
hyp/
Makefile
el2.c
添加 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 规则。
创建
hyp/Makefile
以构建 hypervisor 代码hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
添加 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);
最后,创建根 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 模块中)运行的函数
- 在 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;
}
在您的 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);
在您的 EL1 代码 (
el1.c
) 中,调用 HVCpkvm_el2_mod_call(hvc_number);