ใช้โมดูลผู้จำหน่าย pKVM

หน้านี้อธิบายวิธีการใช้โมดูลผู้จำหน่ายเครื่องเสมือนที่ใช้เคอร์เนล (pKVM) ที่ได้รับการป้องกัน เมื่อคุณทำตามขั้นตอนเหล่านี้เสร็จแล้ว คุณควรมีแผนผังไดเร็กทอรีที่คล้ายกับ:

Makefile
el1.c
hyp/
    Makefile
    el2.c
  1. เพิ่มโค้ดไฮเปอร์ไวเซอร์ EL2 ( el2.c ) อย่างน้อยที่สุด รหัสนี้จะต้องประกาศฟังก์ชัน init ที่ยอมรับการอ้างอิงถึงโครงสร้าง 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 โมดูลผู้จำหน่าย pKVM เป็นโครงสร้างที่ห่อหุ้มการเรียกกลับไปยังไฮเปอร์ไวเซอร์ pKVM โครงสร้างนี้เป็นไปตามกฎ ABI เดียวกันกับอินเทอร์เฟซ GKI

  2. สร้าง hyp/Makefile เพื่อสร้างโค้ดไฮเปอร์ไวเซอร์:

    hyp-obj-y := el2.o
    include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
    
  3. เพิ่มโค้ดเคอร์เนล EL1 ( el1.c ) ส่วนเริ่มต้นของโค้ดนี้ต้องมีการเรียก pkvm_load_el2 module เพื่อโหลดโค้ดไฮเปอร์ไวเซอร์ EL2 จากขั้นตอนที่ 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. สุดท้าย ให้สร้าง root makefile เพื่อผูกโค้ด EL1 และ 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
    

โหลดโมดูล pKVM

เช่นเดียวกับโมดูลผู้จำหน่าย GKI โมดูลผู้จำหน่าย pKVM สามารถโหลดได้โดยใช้ modprobe อย่างไรก็ตาม ด้วยเหตุผลด้านความปลอดภัย การโหลดจะต้องเกิดขึ้นก่อนที่จะถูกลิดรอนสิทธิ์ หากต้องการโหลดโมดูล pKVM คุณต้องแน่ใจว่าโมดูลของคุณรวมอยู่ในระบบไฟล์รูท ( initramfs ) และคุณต้องเพิ่มสิ่งต่อไปนี้ในบรรทัดคำสั่งเคอร์เนลของคุณ:

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

โมดูลผู้จำหน่าย pKVM ที่จัดเก็บไว้ใน initramfs สืบทอดลายเซ็นและการป้องกันของ initramfs

หากโมดูลผู้จำหน่าย pKVM ตัวใดตัวหนึ่งไม่สามารถโหลดได้ ระบบจะถือว่าไม่ปลอดภัย และจะไม่สามารถเริ่มเครื่องเสมือนที่ได้รับการป้องกันได้

เรียกใช้ฟังก์ชัน EL2 (ไฮเปอร์ไวเซอร์) จาก EL2 (โมดูลเคอร์เนล)

การเรียกไฮเปอร์ไวเซอร์ (HVC) คือคำสั่งที่ให้เคอร์เนลเรียกไฮเปอร์ไวเซอร์ ด้วยการเปิดตัวโมดูลผู้จำหน่าย pKVM HVC สามารถใช้เพื่อเรียกฟังก์ชันให้ทำงานที่ EL2 (ในโมดูลไฮเปอร์ไวเซอร์) จาก EL1 (โมดูลเคอร์เนล):

  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 ) ให้ลงทะเบียนตัวจัดการ EL2 ในโมดูลผู้ขาย 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. ในรหัส EL1 ของคุณ ( el1.c ) ให้โทรหา HVC:

    pkvm_el2_mod_call(hvc_number);