pKVM 공급업체 모듈 구현

이 페이지에서는 보호된 커널 기반 가상 머신(pKVM) 공급업체 모듈을 구현하는 방법을 설명합니다. 이 단계를 완료하면 다음과 유사한 디렉터리 트리가 생성됩니다.

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. EL2 하이퍼바이저 코드( el2.c )를 추가합니다. 최소한 이 코드는 pkvm_module_ops 구조체에 대한 참조를 허용하는 init 함수를 선언해야 합니다.

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

    pKVM 공급업체 모듈 API는 pKVM 하이퍼바이저에 대한 콜백을 캡슐화하는 구조체입니다. 이 구조체는 GKI 인터페이스와 동일한 ABI 규칙을 따릅니다.

  2. 하이퍼바이저 코드를 빌드하기 위한 hyp/Makefile 만듭니다.

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. EL1 커널 코드( el1.c )를 추가합니다. 이 코드의 init 섹션에는 1단계의 EL2 하이퍼바이저 코드를 로드하기 위한 pkvm_load_el2 module 에 대한 호출이 포함되어야 합니다.

    #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. 마지막으로 EL1 및 EL2 코드를 함께 묶는 루트 makefile을 만듭니다.

    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
    

pKVM 모듈 로드

GKI 공급업체 모듈과 마찬가지로 pKVM 공급업체 모듈은 modprobe를 사용하여 로드할 수 있습니다. 그러나 보안상의 이유로 로드는 권한 해제 전에 이루어져야 합니다. pKVM 모듈을 로드하려면 모듈이 루트 파일 시스템( initramfs )에 포함되어 있는지 확인하고 커널 명령줄에 다음을 추가해야 합니다.

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

initramfs 에 저장된 pKVM 공급업체 모듈은 initramfs 의 서명과 보호를 상속합니다.

pKVM 공급업체 모듈 중 하나가 로드되지 않으면 시스템은 안전하지 않은 것으로 간주되어 보호되는 가상 머신을 시작할 수 없습니다.

EL2(커널 모듈)에서 EL2(하이퍼바이저) 함수 호출

HVC(하이퍼바이저 호출)는 커널이 하이퍼바이저를 호출할 수 있도록 하는 명령입니다. pKVM 공급업체 모듈이 도입되면서 HVC를 사용하여 EL1(커널 모듈)의 EL2(하이퍼바이저 모듈)에서 실행할 함수를 호출할 수 있습니다.

  1. EL2 코드( el2.c )에서 EL2 핸들러를 선언합니다.

    void pkvm_driver_hyp_hvc(struct kvm_cpu_context *ctx)
    {
      /* Handle the call */
    
      cpu_reg(ctx, 1) = 0;
    }
    
  2. EL1 코드( el1.c )에서 pKVM 공급업체 모듈에 EL2 핸들러를 등록합니다.

    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. EL1 코드( el1.c )에서 HVC를 호출합니다.

    pkvm_el2_mod_call(hvc_number);