Wdróż moduł dostawcy pKVM

Na tej stronie dowiesz się, jak wdrożyć moduł dostawcy chronionej maszyny wirtualnej opartej na jądrze (pKVM). Po wykonaniu tych czynności powinno powstać drzewo katalogów podobne do:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. Dodaj kod hipernadzorcy EL2 (el2.c). Ten kod musi zawierać przynajmniej zadeklarowaną funkcję init, która akceptuje odwołanie do struktury 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;
    }
    

    Interfejs API modułu dostawcy pKVM to struktura zawierająca wywołania zwrotne do funkcji hipernadzorca pKVM. Ta struktura jest zgodna z tymi samymi regułami ABI co interfejsy GKI.

  2. Utwórz hyp/Makefile, aby utworzyć kod hipernadzorcy:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. Dodaj kod jądra EL1 (el1.c). Sekcja init tego kodu musi zawierać wywołanie do pkvm_load_el2 module umożliwiające wczytanie kodu hipernadzorcy EL2 z kroku 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. Na koniec utwórz główny plik Makefile, aby powiązać ze sobą kody EL1 i 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
    

Wczytaj moduł pKVM

Tak jak w przypadku modułów dostawcy GKI, moduły dostawcy pKVM można ładować za pomocą modprobe. Ze względów bezpieczeństwa ładowanie musi nastąpić przed jego wyłączeniem. Aby wczytać moduł pKVM, musisz upewnić się, że Twoje moduły są uwzględnione w główny system plików (initramfs). Musisz dodać do pliku wiersz poleceń jądra:

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

Moduły dostawcy pKVM przechowywane w regionie initramfs dziedziczą podpis i ochronę initramfs.

Jeśli nie uda się wczytać jednego z modułów dostawcy pKVM, system zostanie uznany za niezabezpieczone i nie będzie można uruchomić chronionej maszyny wirtualnej.

Wywoływanie funkcji EL2 (hipernadzorcy) z EL2 (modułu jądra)

Wywołanie hipernadzorcy (HVC) to instrukcja, która pozwala jąderowi wywołać hipernadzorcę. Dzięki wprowadzeniu modułów dostawcy pKVM można używać HVC do wywoływania funkcji EL2 (w module hipernadzorcy) z EL1 (modułu jądra):

  1. W kodzie EL2 (el2.c) zadeklaruj moduł obsługi EL2:

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. W kodzie EL1 (el1.c) zarejestruj moduł obsługi EL2 u dostawcy pKVM część:

    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. Za pomocą kodu EL1 (el1.c) wywołaj funkcję HVC:

    pkvm_el2_mod_call(hvc_number);