Esta página explica como implementar um módulo de fornecedor de máquina virtual protegida baseada em kernel (pKVM). Ao concluir essas etapas, você terá uma árvore de diretórios semelhante a esta:
Makefile
el1.c
hyp/
Makefile
el2.c
Adicione o código do hipervisor EL2 (
el2.c
). No mínimo, esse código precisa declarar uma função init que aceita uma referência à estruturapkvm_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; }
A API do módulo de fornecedor pKVM é uma estrutura que encapsula callbacks para o hipervisor pKVM. Esse struct segue as mesmas regras de ABI das interfaces GKI.
Crie o
hyp/Makefile
para criar o código do hipervisor:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
Adicione o código do kernel EL1 (
el1.c
). A seção de inicialização desse código precisa conter uma chamada parapkvm_load_el2 module
para carregar o código do hipervisor EL2 da etapa 1.#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);
Por fim, crie o makefile raiz para unir os códigos EL1 e 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
Carregar um módulo pKVM
Assim como os módulos de fornecedores do GKI, os módulos de fornecedores do pKVM podem ser carregados usando o modprobe.
No entanto, por motivos de segurança, o carregamento precisa ocorrer antes da privação de privilégios.
Para carregar um módulo pKVM, verifique se os módulos estão incluídos no
sistema de arquivos raiz (initramfs
) e adicione o seguinte à
linha de comando do kernel:
kvm-arm.protected_modules=mod1,mod2,mod3,...
Os módulos de fornecedor pKVM armazenados no initramfs
herdam a assinatura e a proteção de initramfs
.
Se um dos módulos do fornecedor pKVM não for carregado, o sistema será considerado não seguro, e não será possível iniciar uma máquina virtual protegida.
Chamar uma função EL2 (hipervisor) do EL2 (módulo do kernel)
Uma chamada de hipervisor (HVC, na sigla em inglês) é uma instrução que permite que o kernel chame o hipervisor. Com a introdução dos módulos do fornecedor pKVM, um HVC pode ser usado para chamar uma função para ser executada no EL2 (no módulo do hipervisor) do EL1 (o módulo do kernel):
- No código EL2 (
el2.c
), declare o gerenciador 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;
}
No código EL1 (
el1.c
), registre o gerenciador do EL2 no módulo do fornecedor pKVM: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);
No código EL1 (
el1.c
), chame o HVC:pkvm_el2_mod_call(hvc_number);