Esta página explica como implementar um módulo de fornecedor de máquina virtual baseada em kernel (pKVM) protegido. Ao concluir essas etapas, você deverá ter uma árvore de diretórios semelhante a:
Makefile
el1.c
hyp/
Makefile
el2.c
Adicione o código do hipervisor EL2 (
el2.c
). No mínimo, este código deve declarar uma função init aceitando 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 do fornecedor pKVM é uma estrutura que encapsula retornos de chamada para o hipervisor pKVM. Essa estrutura segue as mesmas regras ABI das interfaces GKI.
Crie o
hyp/Makefile
para construir o código do hipervisor:hyp-obj-y := el2.o
include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.moduleAdicione o código do kernel EL1 (
el1.c
). A seção init deste código deve conter uma chamada aopkvm_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 o código 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
Tal como acontece com os módulos do fornecedor GKI, os módulos do fornecedor pKVM podem ser carregados usando modprobe. Contudo, por razões de segurança, o carregamento deve ocorrer antes da desprivilegiação. Para carregar um módulo pKVM, você deve garantir que seus módulos estejam incluídos no sistema de arquivos raiz ( initramfs
) e adicionar o seguinte à linha de comando do kernel:
kvm-arm.protected_modules= mod1 , mod2 , mod3 , ...
Os módulos do fornecedor pKVM armazenados no initramfs
herdam a assinatura e a proteção do initramfs
.
Se um dos módulos do fornecedor pKVM falhar ao carregar, o sistema será considerado inseguro e não será possível iniciar uma máquina virtual protegida.
Chame uma função EL2 (hipervisor) do EL2 (módulo do kernel)
Uma chamada de hipervisor (HVC) é uma instrução que permite ao kernel chamar o hipervisor. Com a introdução dos módulos do fornecedor pKVM, um HVC pode ser usado para chamar uma função a ser executada no EL2 (no módulo do hipervisor) a partir do EL1 (o módulo do kernel):
No código EL2 (
el2.c
), declare o manipulador EL2:void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
{
/* Handle the call */
cpu_reg(ctx, 1) = 0;
}No seu código EL1 (
el1.c
), registre o manipulador 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);
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 seu código EL1 (
el1.c
), chame o HVC:pkvm_el2_mod_call(hvc_number);