Mengimplementasikan modul vendor pKVM

Halaman ini menjelaskan cara mengimplementasikan modul vendor virtual machine (pKVM) berbasis kernel yang dilindungi. Setelah selesai dengan langkah-langkah ini, Anda akan memiliki pohon direktori yang mirip dengan:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. Tambahkan kode hypervisor EL2 (el2.c). Minimal, kode ini harus mendeklarasikan fungsi init yang menerima referensi ke struct 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;
    }
    

    API modul vendor pKVM adalah callback enkapsulasi struct ke hypervisor pKVM. Struct ini mengikuti aturan ABI yang sama dengan antarmuka GKI.

  2. Buat hyp/Makefile untuk mem-build kode hypervisor:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. Tambahkan kode kernel EL1 (el1.c). Bagian init kode ini harus berisi panggilan ke pkvm_load_el2 module untuk memuat kode hypervisor EL2 dari langkah 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. Terakhir, buat makefile root untuk menggabungkan kode EL1 dan 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
    

Memuat modul pKVM

Seperti halnya modul vendor GKI, modul vendor pKVM dapat dimuat menggunakan modprobe. Namun, untuk alasan keamanan, pemuatan harus dilakukan sebelum penghapusan. Untuk memuat modul pKVM, Anda harus memastikan bahwa modul Anda disertakan dalam sistem file root (initramfs) dan Anda harus menambahkan berikut ini ke command line kernel:

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

Modul vendor pKVM yang disimpan di initramfs mewarisi tanda tangan dan perlindungan initramfs.

Jika salah satu modul vendor pKVM gagal dimuat, sistem dianggap tidak aman dan tidak akan dapat memulai virtual machine yang dilindungi.

Memanggil fungsi EL2 (hypervisor) dari EL2 (modul kernel)

Panggilan hypervisor (HVC) adalah instruksi yang memungkinkan kernel memanggil hypervisor. Dengan diperkenalkannya modul vendor pKVM, HVC dapat digunakan untuk memanggil fungsi yang akan dijalankan pada EL2 (dalam modul hypervisor) dari EL1 (modul kernel):

  1. Dalam kode EL2 (el2.c), deklarasikan pengendali 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. Dalam kode EL1 (el1.c), daftarkan pengendali EL2 di vendor pKVM Anda :

    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. Dalam kode EL1 Anda (el1.c), panggil HVC:

    pkvm_el2_mod_call(hvc_number);