@@ -537,6 +537,50 @@ Returns: 0 on success, -negative on error
See SEV-SNP specification for further details on launch finish input parameters.
+22. KVM_SEV_SNP_GET_CERTS
+-------------------------
+
+After the SNP guest launch flow has started, the KVM_SEV_SNP_GET_CERTS command
+can be issued to request the data that has been installed with the
+KVM_SEV_SNP_SET_CERTS command.
+
+Parameters (in/out): struct kvm_sev_snp_get_certs
+
+Returns: 0 on success, -negative on error
+
+::
+
+ struct kvm_sev_snp_get_certs {
+ __u64 certs_uaddr;
+ __u64 certs_len
+ };
+
+If no certs have been installed, then the return value is -ENOENT.
+If the buffer specified in the struct is too small, the certs_len field will be
+overwritten with the required bytes to receive all the certificate bytes and the
+return value will be -EINVAL.
+
+23. KVM_SEV_SNP_SET_CERTS
+-------------------------
+
+After the SNP guest launch flow has started, the KVM_SEV_SNP_SET_CERTS command
+can be issued to override the /dev/sev certs data that is returned when a
+guest issues an extended guest request. This is useful for instance-specific
+extensions to the host certificates.
+
+Parameters (in/out): struct kvm_sev_snp_set_certs
+
+Returns: 0 on success, -negative on error
+
+::
+
+ struct kvm_sev_snp_set_certs {
+ __u64 certs_uaddr;
+ __u64 certs_len
+ };
+
+The certs_len field may not exceed SEV_FW_BLOB_MAX_SIZE.
+
References
==========
@@ -2277,6 +2277,113 @@ static int snp_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int snp_get_instance_certs(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+ struct kvm_sev_snp_get_certs params;
+ struct sev_snp_certs *snp_certs;
+ int rc = 0;
+
+ if (!sev_snp_guest(kvm))
+ return -ENOTTY;
+
+ if (!sev->snp_context)
+ return -EINVAL;
+
+ if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data,
+ sizeof(params)))
+ return -EFAULT;
+
+ snp_certs = sev_snp_certs_get(sev->snp_certs);
+ /* No instance certs set. */
+ if (!snp_certs)
+ return -ENOENT;
+
+ if (params.certs_len < sev->snp_certs->len) {
+ /* Output buffer too small. Return the required size. */
+ params.certs_len = sev->snp_certs->len;
+
+ if (copy_to_user((void __user *)(uintptr_t)argp->data, ¶ms,
+ sizeof(params)))
+ rc = -EFAULT;
+ else
+ rc = -EINVAL; /* May be ENOSPC? */
+ } else {
+ if (copy_to_user((void __user *)(uintptr_t)params.certs_uaddr,
+ snp_certs->data, snp_certs->len))
+ rc = -EFAULT;
+ }
+
+ sev_snp_certs_put(snp_certs);
+
+ return rc;
+}
+
+static void snp_replace_certs(struct kvm_sev_info *sev, struct sev_snp_certs *snp_certs)
+{
+ sev_snp_certs_put(sev->snp_certs);
+ sev->snp_certs = snp_certs;
+}
+
+static int snp_set_instance_certs(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+ unsigned long length = SEV_FW_BLOB_MAX_SIZE;
+ struct kvm_sev_snp_set_certs params;
+ struct sev_snp_certs *snp_certs;
+ void *to_certs;
+ int ret;
+
+ if (!sev_snp_guest(kvm))
+ return -ENOTTY;
+
+ if (!sev->snp_context)
+ return -EINVAL;
+
+ if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data,
+ sizeof(params)))
+ return -EFAULT;
+
+ if (params.certs_len > SEV_FW_BLOB_MAX_SIZE)
+ return -EINVAL;
+
+ /*
+ * Setting a length of 0 is the same as "uninstalling" instance-
+ * specific certificates.
+ */
+ if (params.certs_len == 0) {
+ snp_replace_certs(sev, NULL);
+ return 0;
+ }
+
+ /* Page-align the length */
+ length = ALIGN(params.certs_len, PAGE_SIZE);
+
+ to_certs = kmalloc(length, GFP_KERNEL | __GFP_ZERO);
+ if (!to_certs)
+ return -ENOMEM;
+
+ if (copy_from_user(to_certs,
+ (void __user *)(uintptr_t)params.certs_uaddr,
+ params.certs_len)) {
+ ret = -EFAULT;
+ goto error_exit;
+ }
+
+ snp_certs = sev_snp_certs_new(to_certs, length);
+ if (!snp_certs) {
+ ret = -ENOMEM;
+ goto error_exit;
+ }
+
+ snp_replace_certs(sev, snp_certs);
+
+ return 0;
+error_exit:
+ kfree(to_certs);
+ return ret;
+}
+
int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -2376,6 +2483,12 @@ int sev_mem_enc_ioctl(struct kvm *kvm, void __user *argp)
case KVM_SEV_SNP_LAUNCH_FINISH:
r = snp_launch_finish(kvm, &sev_cmd);
break;
+ case KVM_SEV_SNP_GET_CERTS:
+ r = snp_get_instance_certs(kvm, &sev_cmd);
+ break;
+ case KVM_SEV_SNP_SET_CERTS:
+ r = snp_set_instance_certs(kvm, &sev_cmd);
+ break;
default:
r = -EINVAL;
goto out;
@@ -2591,6 +2704,8 @@ static int snp_decommission_context(struct kvm *kvm)
snp_free_firmware_page(sev->snp_context);
sev->snp_context = NULL;
+ sev_snp_certs_put(sev->snp_certs);
+
return 0;
}
@@ -95,6 +95,7 @@ struct kvm_sev_info {
u64 snp_init_flags;
void *snp_context; /* SNP guest context page */
u64 sev_features; /* Features set at VMSA creation */
+ struct sev_snp_certs *snp_certs;
};
struct kvm_svm {
@@ -22,7 +22,7 @@
#define __psp_pa(x) __pa(x)
#endif
-#define SEV_FW_BLOB_MAX_SIZE 0x4000 /* 16KB */
+#define SEV_FW_BLOB_MAX_SIZE 0x5000 /* 20KB */
struct sev_snp_certs {
void *data;
@@ -1937,6 +1937,8 @@ enum sev_cmd_id {
KVM_SEV_SNP_LAUNCH_START,
KVM_SEV_SNP_LAUNCH_UPDATE,
KVM_SEV_SNP_LAUNCH_FINISH,
+ KVM_SEV_SNP_GET_CERTS,
+ KVM_SEV_SNP_SET_CERTS,
KVM_SEV_NR_MAX,
};
@@ -2084,6 +2086,16 @@ struct kvm_sev_snp_launch_finish {
__u8 pad[6];
};
+struct kvm_sev_snp_get_certs {
+ __u64 certs_uaddr;
+ __u64 certs_len;
+};
+
+struct kvm_sev_snp_set_certs {
+ __u64 certs_uaddr;
+ __u64 certs_len;
+};
+
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)