本頁介紹如何實現受保護的基於核心的虛擬機器 (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; }
pKVM 供應商模組 API 是一個封裝 pKVM 管理程式回呼的架構體。此結構遵循與 GKI 介面相同的 ABI 規則。
建立
hyp/Makefile
來建立虛擬機器管理程式碼:hyp-obj-y := el2.o include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
新增 EL1 核心程式碼 (
el1.c
)。此程式碼的 init 部分必須包含對pkvm_load_el2 module
調用,以載入步驟 1 中的 EL2 虛擬機器管理程式碼。#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);
最後,建立根 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 , ...
儲存在initramfs
中的 pKVM 供應商模組繼承了initramfs
的簽章和保護。
如果 pKVM 供應商模組之一無法載入,系統將被視為不安全,並且無法啟動受保護的虛擬機器。
從 EL2(核心模組)呼叫 EL2(管理程式)函數
虛擬機器管理程式呼叫 (HVC)是一條讓核心呼叫虛擬機器管理程式的指令。隨著 pKVM 供應商模組的引入,HVC 可用於從 EL1(核心模組)呼叫在 EL2(在管理程式模組中)運行的函數:
在 EL2 代碼 (
el2.c
) 中,宣告 EL2 處理程序:void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx) { /* Handle the call */ cpu_reg(ctx, 1) = 0; }
在您的 EL1 代碼 (
el1.c
) 中,在 pKVM 供應商模組中註冊 EL2 處理程序: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); 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);