این صفحه نحوه پیاده سازی یک ماژول فروشنده ماشین مجازی مبتنی بر هسته محافظت شده (pKVM) را توضیح می دهد. وقتی این مراحل را انجام دادید، باید یک درخت دایرکتوری شبیه به زیر داشته باشید:
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);
در نهایت، root 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 (hypervisor) از EL2 (ماژول هسته)
فراخوانی Hypervisor (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);