หน้านี้จะอธิบายวิธีใช้โมดูลผู้ให้บริการเครื่องเสมือน (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 คือโครงสร้างที่รวมการเรียกกลับไปยัง Hypervisor ของ 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);
สุดท้าย ให้สร้างไฟล์ 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 (hypervisor) จาก EL2 (โมดูลเคอร์เนล)
การเรียก Hypervisor (HVC) คือคำสั่งที่ช่วยให้เคอร์เนลเรียก Hypervisor ได้ เมื่อมีการเปิดตัวโมดูลผู้ให้บริการ 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;
}
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;
}
ในรหัส 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);