From: Tom Lendacky <[email protected]>

Implement the GET_APIC_IDS NAE event to gather and return the list of
APIC IDs for all vCPUs in the guest.

Since it is now possible to launch vCPUs without going through the
LAUNCH_UPDATE process, be sure to mark the guest state protected and to
enable LBR virtualization.

Since it is now possible to launch vCPUs by APIC ID before the first
INIT-SIPI request, be sure to check for the AP create event in the
kvm_arch_vcpu_ioctl_run() loop when the AP is in the uninitialized state.

Signed-off-by: Tom Lendacky <[email protected]>
Co-developed-by: Joerg Roedel <[email protected]>
Co-developed-by: Carlos López <[email protected]>
Signed-off-by: Joerg Roedel <[email protected]>
---
 arch/x86/include/asm/sev-common.h |  1 +
 arch/x86/include/uapi/asm/svm.h   |  1 +
 arch/x86/kvm/svm/sev.c            | 87 +++++++++++++++++++++++++++++--
 3 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/sev-common.h 
b/arch/x86/include/asm/sev-common.h
index ee17a3541b55..cedb7ea91da5 100644
--- a/arch/x86/include/asm/sev-common.h
+++ b/arch/x86/include/asm/sev-common.h
@@ -137,6 +137,7 @@ enum psc_op {
 #define GHCB_HV_FT_SNP                 BIT_ULL(0)
 #define GHCB_HV_FT_SNP_AP_CREATION     BIT_ULL(1)
 #define GHCB_HV_FT_SNP_RINJ            (BIT_ULL(2) | 
GHCB_HV_FT_SNP_AP_CREATION)
+#define GHCB_HV_FT_APIC_ID_LIST                BIT_ULL(4)
 #define GHCB_HV_FT_SNP_MULTI_VMPL      BIT_ULL(5)
 
 /*
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index d281dd21c540..91395b82eadd 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -123,6 +123,7 @@
 #define SVM_VMGEXIT_HVDB_QUERY                 2
 #define SVM_VMGEXIT_HVDB_CLEAR                 3
 #define SVM_VMGEXIT_HV_IPI                      0x80000015ull
+#define SVM_VMGEXIT_GET_APIC_IDS               0x80000017ull
 #define SVM_VMGEXIT_SNP_RUN_VMPL               0x80000018ull
 #define SVM_VMGEXIT_SAVIC                      0x8000001aull
 #define SVM_VMGEXIT_SAVIC_REGISTER_GPA         0
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 12b039823c1c..c0b2879f8e9f 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -40,9 +40,10 @@
 #define GHCB_VERSION_MAX       2ULL
 #define GHCB_VERSION_MIN       1ULL
 
-#define GHCB_HV_FT_SUPPORTED   (GHCB_HV_FT_SNP |               \
-                                GHCB_HV_FT_SNP_AP_CREATION |   \
-                                GHCB_HV_FT_SNP_RINJ)
+#define GHCB_HV_FT_SUPPORTED   (GHCB_HV_FT_SNP                 | \
+                                GHCB_HV_FT_SNP_AP_CREATION     | \
+                                GHCB_HV_FT_SNP_RINJ            | \
+                                GHCB_HV_FT_APIC_ID_LIST)
 
 /*
  * The GHCB spec essentially states that all non-zero error codes other than
@@ -3518,6 +3519,10 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
                        if (!kvm_ghcb_rax_is_valid(svm))
                                goto vmgexit_err;
                break;
+       case SVM_VMGEXIT_GET_APIC_IDS:
+               if (!kvm_ghcb_rax_is_valid(svm))
+                       goto vmgexit_err;
+               break;
        case SVM_VMGEXIT_NMI_COMPLETE:
        case SVM_VMGEXIT_AP_HLT_LOOP:
        case SVM_VMGEXIT_AP_JUMP_TABLE:
@@ -4439,6 +4444,78 @@ static int sev_snp_hv_ipi(struct vcpu_svm *svm)
        return 0;
 }
 
+struct sev_apic_id_desc {
+       u32     num_entries;
+       u32     apic_ids[];
+};
+
+static void sev_get_apic_ids(struct vcpu_svm *svm)
+{
+       struct ghcb *ghcb = svm->sev_es.ghcb;
+       struct kvm_vcpu *vcpu = &svm->vcpu, *loop_vcpu;
+       struct kvm *kvm = vcpu->kvm;
+       unsigned int id_desc_size;
+       struct sev_apic_id_desc *desc;
+       struct page *page;
+       gpa_t gpa;
+       u64 pages;
+       unsigned long i;
+       int n;
+
+       pages = vcpu->arch.regs[VCPU_REGS_RAX];
+
+       /* Each APIC ID is 32-bits in size, so make sure there is room */
+       n = atomic_read(&kvm->online_vcpus);
+       /*TODO: is this possible? */
+       if (n < 0)
+               return;
+
+       id_desc_size = sizeof(*desc);
+       id_desc_size += n * sizeof(desc->apic_ids[0]);
+       if (id_desc_size > (pages * PAGE_SIZE)) {
+               vcpu->arch.regs[VCPU_REGS_RAX] = PFN_UP(id_desc_size);
+               return;
+       }
+
+       gpa = svm->vmcb->control.exit_info_1;
+
+       ghcb_set_sw_exit_info_1(ghcb, 2);
+       ghcb_set_sw_exit_info_2(ghcb, 5);
+
+       if (!page_address_valid(vcpu, gpa))
+               return;
+
+       page = gfn_to_page(kvm, gpa_to_gfn(gpa));
+       kvm_release_page_unused(page);
+       if (!page)
+               return;
+
+       if (!pages)
+               return;
+
+       /* Allocate a buffer to hold the APIC IDs */
+       desc = kvzalloc(id_desc_size, GFP_KERNEL_ACCOUNT);
+       if (!desc)
+               return;
+
+       desc->num_entries = n;
+       kvm_for_each_vcpu(i, loop_vcpu, kvm) {
+               /*TODO: is this possible? */
+               if (i >= n)
+                       break;
+
+               desc->apic_ids[i] = loop_vcpu->vcpu_id;
+       }
+
+       if (!kvm_write_guest(kvm, gpa, desc, id_desc_size)) {
+               /* IDs were successfully written */
+               ghcb_set_sw_exit_info_1(ghcb, 0);
+               ghcb_set_sw_exit_info_2(ghcb, 0);
+       }
+
+       kvfree(desc);
+}
+
 static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
 {
        struct vmcb_control_area *control = &svm->vmcb->control;
@@ -4730,6 +4807,10 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
                }
                ret = 1;
                break;
+       case SVM_VMGEXIT_GET_APIC_IDS:
+               sev_get_apic_ids(svm);
+               ret = 1;
+               break;
        case SVM_VMGEXIT_UNSUPPORTED_EVENT:
                vcpu_unimpl(vcpu,
                            "vmgexit: unsupported event - exit_info_1=%#llx, 
exit_info_2=%#llx\n",
-- 
2.53.0


Reply via email to