Wdrażanie modułu dostawcy pKVM

Na tej stronie dowiesz się, jak wdrożyć moduł dostawcy chronionej maszyny wirtualnej opartej na jądrze (pKVM).

W przypadku wersji android16-6.12 i nowszych po wykonaniu tych czynności powinna być widoczna struktura katalogów podobna do tej:

BUILD.bazel
el1.c
hyp/
    BUILD.bazel
    el2.c

Pełny przykład znajdziesz w artykule Tworzenie modułu pKVM za pomocą DDK.

W przypadku Androida 15–6.6 i starszych:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. Dodaj kod hiperwizora EL2 (el2.c). Musi on zawierać co najmniej deklarację funkcji 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 hiperwizora pKVM. Ta struktura jest zgodna z tymi samymi regułami interfejsu ABI co interfejsy GKI.

  2. Utwórz hyp/Makefile, aby skompilować kod hiperwizora:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. Dodaj kod jądra EL1 (el1.c). Sekcja inicjowania tego kodu musi zawierać wywołanie pkvm_load_el2 module, aby wczytać kod hiperwizora 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 reguły kompilacji.

    W przypadku Androida 16–6.12 i nowszych zapoznaj się z artykułem Tworzenie modułu pKVM za pomocą DDK, aby utworzyć ddk_library() dla EL2 i ddk_module() dla EL1.

    W przypadku Androida 15-6.6 i starszych wersji utwórz główny plik makefile, aby połączyć kod 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
    

Wczytywanie modułu pKVM

Podobnie jak w przypadku modułów dostawcy GKI, moduły dostawcy pKVM można wczytywać za pomocą polecenia modprobe. Ze względów bezpieczeństwa wczytywanie musi jednak nastąpić przed ograniczeniem uprawnień. Aby załadować moduł pKVM, musisz się upewnić, że moduły są uwzględnione w głównym systemie plików (initramfs), i dodać do wiersza poleceń jądra te elementy:

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

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

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

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

Wywołanie hiperwizora (HVC) to instrukcja, która umożliwia jądru wywołanie hiperwizora. Wraz z wprowadzeniem modułów dostawcy pKVM wywołanie HVC może służyć do wywoływania funkcji do uruchomienia na poziomie EL2 (w module hipernadzorcy) z poziomu EL1 (w module jądra):

  1. W kodzie EL2 (el2.c) zadeklaruj moduł obsługi 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;
  }
  1. W kodzie EL1 (el1.c) zarejestruj moduł obsługi EL2 w module dostawcy 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);
    
  2. W kodzie EL1 (el1.c) wywołaj HVC:

    pkvm_el2_mod_call(hvc_number);