Triển khai mô-đun nhà cung cấp pKVM

Trang này giải thích cách triển khai mô-đun nhà cung cấp máy ảo dựa trên hạt nhân (pKVM) được bảo vệ. Khi bạn thực hiện xong các bước này, bạn sẽ có một cây thư mục tương tự như:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. Thêm mã ảo hóa EL2 ( el2.c ). Tối thiểu, mã này phải khai báo hàm init chấp nhận tham chiếu đến cấu trú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 mô-đun nhà cung cấp pKVM là một lệnh gọi lại đóng gói cấu trúc cho trình ảo hóa pKVM. Cấu trúc này tuân theo các quy tắc ABI giống như giao diện GKI.

  2. Tạo hyp/Makefile để xây dựng mã bộ ảo hóa:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. Thêm mã hạt nhân EL1 ( el1.c ). Phần init của mã này phải chứa lệnh gọi đến pkvm_load_el2 module để tải mã bộ ảo hóa EL2 từ bước 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);
    
  4. Cuối cùng, tạo tệp makefile gốc để liên kết mã EL1 và EL2 với nhau:

    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
    

Tải mô-đun pKVM

Giống như các mô-đun nhà cung cấp GKI, các mô-đun nhà cung cấp pKVM có thể được tải bằng modprobe. Tuy nhiên, vì lý do bảo mật, việc tải phải diễn ra trước khi tước quyền. Để tải mô-đun pKVM, bạn phải đảm bảo các mô-đun của bạn được bao gồm trong hệ thống tệp gốc ( initramfs ) và bạn phải thêm phần sau vào dòng lệnh kernel của mình:

kvm-arm.protected_modules= mod1 , mod2 , mod3 , ...

Các mô-đun nhà cung cấp pKVM được lưu trữ trong initramfs kế thừa chữ ký và khả năng bảo vệ của initramfs .

Nếu một trong các mô-đun của nhà cung cấp pKVM không tải được, hệ thống được coi là không an toàn và sẽ không thể khởi động máy ảo được bảo vệ.

Gọi hàm EL2 (hypervisor) từ EL2 (mô-đun hạt nhân)

Lệnh gọi bộ ảo hóa (HVC) là một lệnh cho phép kernel gọi bộ ảo hóa. Với việc giới thiệu các mô-đun nhà cung cấp pKVM, HVC có thể được sử dụng để gọi một hàm chạy ở EL2 (trong mô-đun ảo hóa) từ EL1 (mô-đun hạt nhân):

  1. Trong mã EL2 ( el2.c ), khai báo trình xử lý EL2:

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. Trong mã EL1 của bạn ( el1.c ), hãy đăng ký trình xử lý EL2 trong mô-đun nhà cung cấp pKVM của bạn:

    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);
    
  3. Trong mã EL1 của bạn ( el1.c ), hãy gọi HVC:

    pkvm_el2_mod_call(hvc_number);