Implementieren Sie ein pKVM-Anbietermodul

Auf dieser Seite wird erläutert, wie Sie ein Herstellermodul für geschützte Kernel-basierte virtuelle Maschinen (pKVM) implementieren. Wenn Sie mit diesen Schritten fertig sind, sollten Sie einen Verzeichnisbaum ähnlich dem folgenden haben:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. Fügen Sie den EL2-Hypervisor-Code ( el2.c ) hinzu. Dieser Code muss mindestens eine Init-Funktion deklarieren, die einen Verweis auf die pkvm_module_ops -Struktur akzeptiert:

    #include <asm/kvm_pkvm_module.h>
    
    int pkvm_driver_hyp_init(const struct pkvm_module_ops *ops)
    {
      /* Init the EL2 code */
    
      return 0;
    }
    

    Die pKVM-Anbietermodul-API ist eine Struktur, die Rückrufe an den pKVM-Hypervisor kapselt. Diese Struktur folgt denselben ABI-Regeln wie GKI-Schnittstellen.

  2. Erstellen Sie das hyp/Makefile , um den Hypervisor-Code zu erstellen:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. Fügen Sie den EL1-Kernelcode ( el1.c ) hinzu. Der Init-Abschnitt dieses Codes muss einen Aufruf des pkvm_load_el2 module enthalten, um den EL2-Hypervisor-Code aus Schritt 1 zu laden.

    #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. Erstellen Sie abschließend das Root-Makefile, um den EL1- und EL2-Code miteinander zu verknüpfen:

    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
    

Laden Sie ein pKVM-Modul

Wie bei GKI-Anbietermodulen können pKVM-Anbietermodule mit modprobe geladen werden. Aus Sicherheitsgründen muss jedoch das Laden vor dem Deprivilegieren erfolgen. Um ein pKVM-Modul zu laden, müssen Sie sicherstellen, dass Ihre Module im Root-Dateisystem ( initramfs ) enthalten sind, und Sie müssen Ihrer Kernel-Befehlszeile Folgendes hinzufügen:

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

Im initramfs gespeicherte pKVM-Anbietermodule erben die Signatur und den Schutz von initramfs .

Wenn eines der pKVM-Anbietermodule nicht geladen werden kann, gilt das System als unsicher und es ist nicht möglich, eine geschützte virtuelle Maschine zu starten.

Rufen Sie eine EL2-Funktion (Hypervisor) von EL2 (Kernelmodul) auf.

Ein Hypervisor-Aufruf (HVC) ist eine Anweisung, die es dem Kernel ermöglicht, den Hypervisor aufzurufen. Mit der Einführung von pKVM-Anbietermodulen kann ein HVC verwendet werden, um eine Funktion aufzurufen, die bei EL2 (im Hypervisor-Modul) von EL1 (dem Kernel-Modul) ausgeführt wird:

  1. Deklarieren Sie im EL2-Code ( el2.c ) den EL2-Handler:

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. Registrieren Sie in Ihrem EL1-Code ( el1.c ) den EL2-Handler in Ihrem pKVM-Anbietermodul:

    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. Rufen Sie in Ihrem EL1-Code ( el1.c ) den HVC auf:

    pkvm_el2_mod_call(hvc_number);