На этой странице объясняется, как реализовать защищенный модуль поставщика виртуальной машины на основе ядра (pKVM). Когда вы закончите эти шаги, у вас должно получиться дерево каталогов, подобное:
Makefile
el1.c
hyp/
Makefile
el2.c
Добавьте код гипервизора EL2 (
el2.c
). Как минимум, этот код должен объявить функцию инициализации, принимающую ссылку на структуруpkvm_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; }
API модуля поставщика pKVM — это структура, инкапсулирующая обратные вызовы к гипервизору pKVM. Эта структура следует тем же правилам ABI, что и интерфейсы GKI.
Создайте
hyp/Makefile
для сборки кода гипервизора:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
Добавьте код ядра EL1 (
el1.c
). Раздел инициализации этого кода должен содержать вызовpkvm_load_el2 module
для загрузки кода гипервизора EL2 с шага 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);
Наконец, создайте корневой make-файл, чтобы связать код 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, модули поставщиков pKVM можно загрузить с помощью modprobe. Однако по соображениям безопасности перед лишением привилегий должна произойти загрузка. Чтобы загрузить модуль pKVM, вы должны убедиться, что ваши модули включены в корневую файловую систему ( initramfs
), и вы должны добавить следующее в командную строку вашего ядра:
kvm-arm.protected_modules= mod1 , mod2 , mod3 , ...
Модули поставщика pKVM, хранящиеся в initramfs
наследуют подпись и защиту initramfs
.
Если один из модулей производителя pKVM не загружается, система считается небезопасной и запустить защищенную виртуальную машину будет невозможно.
Вызов функции EL2 (гипервизора) из EL2 (модуля ядра)
Вызов гипервизора (HVC) — это инструкция, которая позволяет ядру вызывать гипервизор. С появлением модулей поставщика pKVM HVC можно использовать для вызова функции для запуска на EL2 (в модуле гипервизора) из EL1 (модуля ядра):
- В коде EL2 (
el2.c
) объявите обработчик EL2:
Андроид-14
void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
{
/* Handle the call */
cpu_reg(ctx, 1) = 0;
}
Андроид-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
) зарегистрируйте обработчик EL2 в вашем модуле поставщика 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);
В вашем коде EL1 (
el1.c
) вызовите HVC:pkvm_el2_mod_call(hvc_number);