Questa pagina spiega come implementare un modulo del fornitore di una macchina virtuale protetta basata su kernel (pKVM). Al termine di questi passaggi, dovresti visualizzare una struttura di directory simile alla seguente:
Makefile
el1.c
hyp/
Makefile
el2.c
Aggiungi il codice hypervisor EL2 (
el2.c
). Come minimo, questo codice deve dichiarare una funzione init che accetta un riferimento allo 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 è uno struct che incapsula i callback hypervisor pKVM. Questo struct 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 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 del 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 il makefile principale per collegare il codice EL1 ed 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
Carica 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 del deprivilegio.
Per caricare un modulo pKVM, devi assicurarti che i moduli siano inclusi in
il file system radice (initramfs
) e devi aggiungere quanto segue al tuo
riga di comando 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.
Richiama una funzione EL2 (hypervisor) da EL2 (modulo kernel)
Una chiamata hypervisor (HVC) è un'istruzione che consente al kernel di chiamare l'hypervisor. Con l'introduzione dei moduli del fornitore pKVM, un HVC può essere utilizzato per richiamare una funzione da eseguire su EL2 (nel modulo hypervisor) da EL1 (il modulo del 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 il gestore EL2 nel tuo fornitore pKVM modulo: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);