@@ -116,6 +116,7 @@
#define SVM_VMGEXIT_AP_CREATE 1
#define SVM_VMGEXIT_AP_DESTROY 2
#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018
+#define SVM_VMGEXIT_SEV_TIO_GUEST_REQUEST 0x80000020
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
@@ -237,6 +238,7 @@
{ SVM_VMGEXIT_GUEST_REQUEST, "vmgexit_guest_request" }, \
{ SVM_VMGEXIT_EXT_GUEST_REQUEST, "vmgexit_ext_guest_request" }, \
{ SVM_VMGEXIT_AP_CREATION, "vmgexit_ap_creation" }, \
+ { SVM_VMGEXIT_SEV_TIO_GUEST_REQUEST, "vmgexit_sev_tio_guest_request" }, \
{ SVM_VMGEXIT_HV_FEATURES, "vmgexit_hypervisor_feature" }, \
{ SVM_EXIT_ERR, "invalid_guest_state" }
@@ -135,6 +135,28 @@ struct kvm_xen_exit {
} u;
};
+struct kvm_user_vmgexit {
+#define KVM_USER_VMGEXIT_TIO_REQ 4
+ __u32 type; /* KVM_USER_VMGEXIT_* type */
+ union {
+ struct {
+ __u32 guest_rid; /* in */
+ __u16 ret; /* out */
+#define KVM_USER_VMGEXIT_TIO_REQ_FLAG_STATUS BIT(0)
+#define KVM_USER_VMGEXIT_TIO_REQ_FLAG_MMIO_VALIDATE BIT(1)
+#define KVM_USER_VMGEXIT_TIO_REQ_FLAG_MMIO_CONFIG BIT(2)
+ __u8 flags; /* in */
+ __u8 tdi_status; /* out */
+ __u64 data_gpa; /* in */
+ __u64 data_npages; /* in/out */
+ __u64 req_spa; /* in */
+ __u64 rsp_spa; /* in */
+ __u64 mmio_gpa; /* in */
+ __s32 fw_err; /* out */
+ } tio_req;
+ };
+} __packed;
+
#define KVM_S390_GET_SKEYS_NONE 1
#define KVM_S390_SKEYS_MAX 1048576
@@ -178,6 +200,7 @@ struct kvm_xen_exit {
#define KVM_EXIT_NOTIFY 37
#define KVM_EXIT_LOONGARCH_IOCSR 38
#define KVM_EXIT_MEMORY_FAULT 39
+#define KVM_EXIT_VMGEXIT 40
/* For KVM_EXIT_INTERNAL_ERROR */
/* Emulate instruction failed. */
@@ -446,6 +469,7 @@ struct kvm_run {
__u64 gpa;
__u64 size;
} memory_fault;
+ struct kvm_user_vmgexit vmgexit;
/* Fix the size of the union. */
char padding[256];
};
@@ -3390,6 +3390,8 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
control->exit_info_1 == control->exit_info_2)
goto vmgexit_err;
break;
+ case SVM_VMGEXIT_SEV_TIO_GUEST_REQUEST:
+ break;
default:
reason = GHCB_ERR_INVALID_EVENT;
goto vmgexit_err;
@@ -4250,6 +4252,71 @@ static int snp_mmio_rmp_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int snp_complete_sev_tio_guest_request(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ struct vmcb_control_area *control = &svm->vmcb->control;
+ gpa_t req_gpa = control->exit_info_1;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_sev_info *sev;
+ u8 msg_type = 0;
+
+ if (!sev_snp_guest(kvm))
+ return -EINVAL;
+
+ sev = &to_kvm_svm(kvm)->sev_info;
+
+ if (kvm_read_guest(kvm, req_gpa + offsetof(struct snp_guest_msg_hdr, msg_type),
+ &msg_type, 1))
+ return -EIO;
+
+ if (msg_type == TIO_MSG_TDI_INFO_REQ)
+ vcpu->arch.regs[VCPU_REGS_RDX] = vcpu->run->vmgexit.tio_req.tdi_status;
+
+ ghcb_set_sw_exit_info_2(svm->sev_es.ghcb,
+ SNP_GUEST_ERR(0, vcpu->run->vmgexit.tio_req.fw_err));
+
+ return 1; /* Resume guest */
+}
+
+static int snp_sev_tio_guest_request(struct kvm_vcpu *vcpu, gpa_t req_gpa, gpa_t resp_gpa)
+{
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_sev_info *sev;
+ u8 msg_type;
+
+ if (!sev_snp_guest(kvm))
+ return SEV_RET_INVALID_GUEST;
+
+ sev = &to_kvm_svm(kvm)->sev_info;
+
+ if (kvm_read_guest(kvm, req_gpa + offsetof(struct snp_guest_msg_hdr, msg_type),
+ &msg_type, 1))
+ return -EIO;
+
+ vcpu->run->exit_reason = KVM_EXIT_VMGEXIT;
+ vcpu->run->vmgexit.type = KVM_USER_VMGEXIT_TIO_REQ;
+ vcpu->run->vmgexit.tio_req.guest_rid = vcpu->arch.regs[VCPU_REGS_RCX];
+ vcpu->run->vmgexit.tio_req.flags = 0;
+ if (msg_type == TIO_MSG_TDI_INFO_REQ)
+ vcpu->run->vmgexit.tio_req.flags |= KVM_USER_VMGEXIT_TIO_REQ_FLAG_STATUS;
+ if (msg_type == TIO_MSG_MMIO_VALIDATE_REQ) {
+ vcpu->run->vmgexit.tio_req.flags |= KVM_USER_VMGEXIT_TIO_REQ_FLAG_MMIO_VALIDATE;
+ vcpu->run->vmgexit.tio_req.mmio_gpa = vcpu->arch.regs[VCPU_REGS_RDX];
+ }
+ if (msg_type == TIO_MSG_MMIO_CONFIG_REQ) {
+ vcpu->run->vmgexit.tio_req.flags |= KVM_USER_VMGEXIT_TIO_REQ_FLAG_MMIO_CONFIG;
+ vcpu->run->vmgexit.tio_req.mmio_gpa = vcpu->arch.regs[VCPU_REGS_RDX];
+ }
+ vcpu->run->vmgexit.tio_req.data_gpa = vcpu->arch.regs[VCPU_REGS_RAX];
+ vcpu->run->vmgexit.tio_req.data_npages = vcpu->arch.regs[VCPU_REGS_RBX];
+ vcpu->run->vmgexit.tio_req.req_spa = req_gpa;
+ vcpu->run->vmgexit.tio_req.rsp_spa = resp_gpa;
+ vcpu->arch.complete_userspace_io = snp_complete_sev_tio_guest_request;
+
+ return 0; /* Exit KVM */
+}
+
static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
{
struct vmcb_control_area *control = &svm->vmcb->control;
@@ -4530,6 +4597,9 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
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_SEV_TIO_GUEST_REQUEST:
+ ret = snp_sev_tio_guest_request(vcpu, 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",
The SEV TIO spec defines a new TIO_GUEST_MESSAGE message to provide a secure communication channel between a SNP VM and the PSP, very similar to the GHCB GUEST MESSAGE. However the new call requires additional information about the host and guest PCI BDFn and the VM id (which is GCTX==guest context page). Since KVM does not know PCI, it exits to QEMU which has all the pieces to make the call to the PSP. This relies on necessary additional ioctl() are implemented separately, such as: - IOMMUFD "TDI bind" to bind a secure VF to a CoCo VM; - IOMMUFD "Guest Request" to manage secure DMA and MMIO; - SEV KVM ioctl() to call RMPUPDATE on MMIO ranges. Define new VMGEXIT code - SEV_TIO_GUEST_REQUEST. Define its parameters in kvm_run::kvm_user_vmgexit. These include: - guest BDFn, - GHCB request/response buffers (encrypted guest pages), - space for certificate/measurements/interface repors (non encrypted guest pages). Some numeric values are out of order because numbers in between have been used at different stages of KVM SEV-SNP upstreaming process. Signed-off-by: Alexey Kardashevskiy <aik@amd.com> --- arch/x86/include/uapi/asm/svm.h | 2 + include/uapi/linux/kvm.h | 24 +++++++ arch/x86/kvm/svm/sev.c | 70 ++++++++++++++++++++ 3 files changed, 96 insertions(+)