goto vmgexit_err;
                break;
        case SVM_VMGEXIT_GUEST_REQUEST:
+       case SVM_VMGEXIT_EXT_GUEST_REQUEST:
                if (!sev_snp_guest(vcpu->kvm) ||
                    !PAGE_ALIGNED(control->exit_info_1) ||
                    !PAGE_ALIGNED(control->exit_info_2) ||
        return ret;
 }
 
+static int snp_handle_ext_guest_req(struct vcpu_svm *svm, gpa_t req_gpa, gpa_t resp_gpa)
+{
+       struct kvm *kvm = svm->vcpu.kvm;
+       u8 msg_type;
+
+       if (!sev_snp_guest(kvm))
+               return -EINVAL;
+
+       if (kvm_read_guest(kvm, req_gpa + offsetof(struct snp_guest_msg_hdr, msg_type),
+                          &msg_type, 1))
+               return -EIO;
+
+       /*
+        * As per GHCB spec, requests of type MSG_REPORT_REQ also allow for
+        * additional certificate data to be provided alongside the attestation
+        * report via the guest-provided data pages indicated by RAX/RBX. The
+        * certificate data is optional and requires additional KVM enablement
+        * to provide an interface for userspace to provide it, but KVM still
+        * needs to be able to handle extended guest requests either way. So
+        * provide a stub implementation that will always return an empty
+        * certificate table in the guest-provided data pages.
+        */
+       if (msg_type == SNP_MSG_REPORT_REQ) {
+               struct kvm_vcpu *vcpu = &svm->vcpu;
+               u64 data_npages;
+               gpa_t data_gpa;
+
+               if (!kvm_ghcb_rax_is_valid(svm) || !kvm_ghcb_rbx_is_valid(svm))
+                       goto request_invalid;
+
+               data_gpa = vcpu->arch.regs[VCPU_REGS_RAX];
+               data_npages = vcpu->arch.regs[VCPU_REGS_RBX];
+
+               if (!PAGE_ALIGNED(data_gpa))
+                       goto request_invalid;
+
+               /*
+                * As per GHCB spec (see "SNP Extended Guest Request"), the
+                * certificate table is terminated by 24-bytes of zeroes.
+                */
+               if (data_npages && kvm_clear_guest(kvm, data_gpa, 24))
+                       return -EIO;
+       }
+
+       return snp_handle_guest_req(svm, req_gpa, resp_gpa);
+
+request_invalid:
+       ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2);
+       ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_INPUT);
+       return 1; /* resume guest */
+}
+
 static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
 {
        struct vmcb_control_area *control = &svm->vmcb->control;
        case SVM_VMGEXIT_GUEST_REQUEST:
                ret = snp_handle_guest_req(svm, control->exit_info_1, control->exit_info_2);
                break;
+       case SVM_VMGEXIT_EXT_GUEST_REQUEST:
+               ret = snp_handle_ext_guest_req(svm, control->exit_info_1, control->exit_info_2);
+               break;
        case SVM_VMGEXIT_UNSUPPORTED_EVENT:
                vcpu_unimpl(vcpu,
                            "vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",