Questa pagina spiega come implementare un modulo fornitore di macchine virtuali basate sul kernel protetto (pKVM).
Per android16-6.12 e versioni successive, al termine di questi passaggi, dovresti avere una struttura di directory simile a:
BUILD.bazel
el1.c
hyp/
BUILD.bazel
el2.c
Per un esempio completo, vedi Compilare un modulo pKVM con DDK .
Per android15-6.6 e versioni precedenti:
Makefile
el1.c
hyp/
Makefile
el2.c
Aggiungi il codice dell'hypervisor EL2 (
el2.c
). Come minimo, questo codice deve dichiarare una funzione di inizializzazione che accetti un riferimento alla structpkvm_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; }
L'API del modulo fornitore pKVM è una struttura che incapsula i callback all'hypervisor pKVM. Questa struttura segue le stesse regole ABI delle interfacce GKI.
Crea
hyp/Makefile
per creare il codice dell'hypervisor:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
Aggiungi il codice del kernel EL1 (
el1.c
). La sezione init di questo codice deve contenere una chiamata apkvm_load_el2 module
per caricare il codice dell'hypervisor EL2 dal passaggio 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);
Infine, crea le regole di compilazione.
Per android16-6.12 e versioni successive, consulta Crea un modulo pKVM con DDK per creare
ddk_library()
per EL2 eddk_module()
per EL1.Per android15-6.6 e versioni precedenti, crea il makefile principale per collegare il codice 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
Caricare un modulo pKVM
Come per i moduli del fornitore GKI, i moduli del fornitore pKVM possono essere caricati utilizzando modprobe.
Tuttavia, per motivi di sicurezza, il caricamento deve avvenire prima della rimozione dei privilegi.
Per caricare un modulo pKVM, devi assicurarti che i moduli siano inclusi nel
file system root (initramfs
) e devi aggiungere quanto segue alla
riga di comando del kernel:
kvm-arm.protected_modules=mod1,mod2,mod3,...
I moduli del fornitore pKVM archiviati in initramfs
ereditano la firma e la protezione di initramfs
.
Se uno dei moduli del fornitore pKVM non viene caricato, il sistema viene considerato non sicuro e non sarà possibile avviare una macchina virtuale protetta.
Chiamare una funzione EL2 (hypervisor) da EL1 (modulo del kernel)
Una chiamata hypervisor (HVC) è un'istruzione che consente al kernel di chiamare l'hypervisor. Con l'introduzione dei moduli del fornitore pKVM, è possibile utilizzare un HVC per chiamare una funzione da eseguire a livello EL2 (nel modulo hypervisor) da EL1 (il modulo kernel):
- Nel codice EL2 (
el2.c
), dichiara il gestore 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;
}
Nel codice EL1 (
el1.c
), registra l'handler EL2 nel modulo fornitore 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);
Nel codice EL1 (
el1.c
), chiama l'HVC:pkvm_el2_mod_call(hvc_number);