Implementar un módulo de proveedor pKVM

Esta página explica cómo implementar un módulo de proveedor de máquina virtual basada en kernel protegida (pKVM). Cuando haya terminado con estos pasos, debería tener un árbol de directorios similar a:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. Agregue el código del hipervisor EL2 ( el2.c ). Como mínimo, este código debe declarar una función init que acepte una referencia a la estructura 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;
    }
    

    La API del módulo de proveedor pKVM es una estructura que encapsula devoluciones de llamada al hipervisor pKVM. Esta estructura sigue las mismas reglas ABI que las interfaces GKI.

  2. Cree hyp/Makefile para compilar el código del hipervisor:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. Agregue el código del kernel EL1 ( el1.c ). La sección de inicio de este código debe contener una llamada al pkvm_load_el2 module para cargar el código del hipervisor EL2 desde el paso 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. Finalmente, cree el archivo MAKE raíz para unir el código EL1 y 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
    

Cargar un módulo pKVM

Al igual que con los módulos del proveedor GKI, los módulos del proveedor pKVM se pueden cargar usando modprobe. Sin embargo, por razones de seguridad, la carga debe realizarse antes de la privación de privilegios. Para cargar un módulo pKVM, debe asegurarse de que sus módulos estén incluidos en el sistema de archivos raíz ( initramfs ) y debe agregar lo siguiente a la línea de comandos de su kernel:

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

Los módulos de proveedor pKVM almacenados en initramfs heredan la firma y protección de initramfs .

Si uno de los módulos del proveedor pKVM no se carga, el sistema se considera inseguro y no será posible iniciar una máquina virtual protegida.

Llame a una función EL2 (hipervisor) desde EL2 (módulo kernel)

Una llamada de hipervisor (HVC) es una instrucción que permite al kernel llamar al hipervisor. Con la introducción de los módulos del proveedor pKVM, se puede utilizar un HVC para solicitar que una función se ejecute en EL2 (en el módulo del hipervisor) desde EL1 (el módulo del kernel):

  1. En el código EL2 ( el2.c ), declare el controlador EL2:

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. En su código EL1 ( el1.c ), registre el controlador EL2 en su módulo de proveedor 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);
    
    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. En su código EL1 ( el1.c ), llame al HVC:

    pkvm_el2_mod_call(hvc_number);