На этой странице объясняется, как реализовать модуль поставщика защищенной виртуальной машины на базе ядра (pKVM).
Для Android 16-6.12 и более поздних версий после выполнения этих шагов у вас должно быть дерево каталогов, похожее на следующее:
BUILD.bazel
el1.c
hyp/
BUILD.bazel
el2.c
Полный пример см. в разделе Создание модуля pKVM с помощью DDK .
Для Android 15-6.6 и более ранних версий:
Makefile
el1.c
hyp/
Makefile
el2.c
Добавьте код гипервизора EL2 (
el2.c
). Как минимум, этот код должен объявлять функцию init, принимающую ссылку на структуру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
). Раздел init этого кода должен содержать вызов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);
Наконец, создайте правила сборки.
Для Android 16-6.12 и более поздних версий обратитесь к разделу Сборка модуля pKVM с помощью DDK, чтобы создать
ddk_library()
для EL2 иddk_module()
для EL1.Для android15-6.6 и более ранних версий создайте корневой makefile для объединения кода 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 (гипервизора) из EL1 (модуля ядра)
Вызов гипервизора (HVC) — это инструкция, позволяющая ядру обращаться к гипервизору. С появлением модулей поставщика pKVM HVC можно использовать для вызова функции, которая должна выполняться на уровне EL2 (в модуле гипервизора) из уровня EL1 (модуля ядра):
- В коде EL2 (
el2.c
) объявите обработчик EL2:
Android-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);