diff mbox

[RFC,Part2,v3,15/26] KVM: SVM: Add support for SEV LAUNCH_START command

Message ID 20170724200303.12197-16-brijesh.singh@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Brijesh Singh July 24, 2017, 8:02 p.m. UTC
The command is used to bootstrap SEV guest from unencrypted boot images.
The command creates a new VM encryption key (VEK) using the guest owner's
policy, public DH certificates, and session information.

Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 arch/x86/kvm/svm.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)

Comments

Borislav Petkov Sept. 13, 2017, 5:25 p.m. UTC | #1
On Mon, Jul 24, 2017 at 03:02:52PM -0500, Brijesh Singh wrote:
> The command is used to bootstrap SEV guest from unencrypted boot images.
> The command creates a new VM encryption key (VEK) using the guest owner's

s/The command/It/

> policy, public DH certificates, and session information.
> 
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  arch/x86/kvm/svm.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 165 insertions(+)
> 
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 72f7c27..3e325578 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -329,6 +329,8 @@ static unsigned int max_sev_asid;
>  static unsigned long *sev_asid_bitmap;
>  static int sev_asid_new(void);
>  static void sev_asid_free(int asid);
> +static void sev_deactivate_handle(struct kvm *kvm, int *error);
> +static void sev_decommission_handle(struct kvm *kvm, int *error);

Please move code in a way that you don't need those forward
declarations. Also, I'm wondering if having all the SEV-related code
could live in sev.c or so - svm.c is humongous.

>  static bool svm_sev_enabled(void)
>  {
> @@ -1565,6 +1567,12 @@ static void sev_vm_destroy(struct kvm *kvm)
>  	if (!sev_guest(kvm))
>  		return;
>  
> +	/* release the firmware resources for this guest */
> +	if (sev_get_handle(kvm)) {
> +		sev_deactivate_handle(kvm, &error);
> +		sev_decommission_handle(kvm, &error);
> +	}
> +
>  	sev_asid_free(sev_get_asid(kvm));
>  	sev_firmware_uninit();
>  }
> @@ -5635,6 +5643,159 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
>  	return ret;
>  }
>  
> +static int sev_issue_cmd(struct kvm *kvm, int id, void *data, int *error)
> +{
> +	int fd = sev_get_fd(kvm);
> +	struct fd f;
> +	int ret;
> +
> +	f = fdget(fd);
> +	if (!f.file)
> +		return -EBADF;
> +
> +	ret = sev_issue_cmd_external_user(f.file, id, data, error);
> +	fdput(f);
> +
> +	return ret;
> +}
> +
> +static void sev_decommission_handle(struct kvm *kvm, int *error)
> +{
> +	struct sev_data_decommission *data;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);

Also, better on stack. Please do that for the other functions below too.

> +	if (!data)
> +		return;
> +
> +	data->handle = sev_get_handle(kvm);
> +	sev_guest_decommission(data, error);
> +	kfree(data);
> +}
> +
> +static void sev_deactivate_handle(struct kvm *kvm, int *error)
> +{
> +	struct sev_data_deactivate *data;
> +	int ret;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return;
> +
> +	data->handle = sev_get_handle(kvm);
> +	ret = sev_guest_deactivate(data, error);
> +	if (ret)
> +		goto e_free;
> +
> +	wbinvd_on_all_cpus();
> +
> +	sev_guest_df_flush(error);
> +e_free:
> +	kfree(data);
> +}
> +
> +static int sev_activate_asid(struct kvm *kvm, unsigned int handle, int *error)
> +{
> +	struct sev_data_activate *data;
> +	int asid = sev_get_asid(kvm);
> +	int ret;
> +
> +	wbinvd_on_all_cpus();
> +
> +	ret = sev_guest_df_flush(error);
> +	if (ret)
> +		return ret;
> +
> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->handle = handle;
> +	data->asid   = asid;
> +	ret = sev_guest_activate(data, error);
> +	if (ret)
> +		goto e_err;
> +
> +	sev_set_handle(kvm, handle);
> +e_err:
> +	kfree(data);
> +	return ret;
> +}
> +
> +static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> +	struct sev_data_launch_start *start = NULL;
> +	struct kvm_sev_launch_start params;
> +	void *dh_cert_addr = NULL;
> +	void *session_addr = NULL;
> +	int *error = &argp->error;
> +	int ret;
> +
> +	if (!sev_guest(kvm))
> +		return -ENOTTY;
> +
> +	ret = -EFAULT;
> +	if (copy_from_user(&params, (void *)argp->data,
> +				sizeof(struct kvm_sev_launch_start)))

Sanity-check params. This is especially important if later we start
using reserved fields.

> +		goto e_free;
> +
> +	ret = -ENOMEM;
> +	start = kzalloc(sizeof(*start), GFP_KERNEL);
> +	if (!start)
> +		goto e_free;
> +
> +	/* Bit 15:6 reserved, must be 0 */
> +	start->policy = params.policy & ~0xffc0;
> +
> +	if (params.dh_cert_length && params.dh_cert_address) {

Yeah, we talked about this already: sanity-checking needed. But you get
the idea.

> +		ret = -ENOMEM;
> +		dh_cert_addr = kmalloc(params.dh_cert_length, GFP_KERNEL);
> +		if (!dh_cert_addr)
> +			goto e_free;
> +
> +		ret = -EFAULT;
> +		if (copy_from_user(dh_cert_addr, (void *)params.dh_cert_address,
> +				params.dh_cert_length))
> +			goto e_free;
> +
> +		start->dh_cert_address = __sme_set(__pa(dh_cert_addr));
> +		start->dh_cert_length = params.dh_cert_length;
> +	}
> +
> +	if (params.session_length && params.session_address) {
> +		ret = -ENOMEM;
> +		session_addr = kmalloc(params.session_length, GFP_KERNEL);
> +		if (!session_addr)
> +			goto e_free;
> +
> +		ret = -EFAULT;
> +		if (copy_from_user(session_addr, (void *)params.session_address,
> +				params.session_length))

                if (copy_from_user(session_addr,
				   (void *)params.session_address,
				   params.session_length))

reads better to me. Better yet if you shorten those member names into
s_addr and s_len and so on...
Brijesh Singh Sept. 13, 2017, 6:23 p.m. UTC | #2
On 09/13/2017 12:25 PM, Borislav Petkov wrote:
...

>> +static void sev_deactivate_handle(struct kvm *kvm, int *error);
>> +static void sev_decommission_handle(struct kvm *kvm, int *error);
> 
> Please move code in a way that you don't need those forward
> declarations. Also, I'm wondering if having all the SEV-related code
> could live in sev.c or so - svm.c is humongous.
> 


Yes, svm.c is humongous.


...

>> +
>> +static void sev_decommission_handle(struct kvm *kvm, int *error)
>> +{
>> +	struct sev_data_decommission *data;
>> +
>> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
> 
> Also, better on stack. Please do that for the other functions below too.


Yes, some structures are small and I don't expect them to grow in newer API
spec. We should be able to move them on the stack. I will audit the code and
make the necessary changes.


....

>> +	ret = -EFAULT;
>> +	if (copy_from_user(&params, (void *)argp->data,
>> +				sizeof(struct kvm_sev_launch_start)))
> 
> Sanity-check params. This is especially important if later we start
> using reserved fields.
> 

Yes, I will add some upper bound check on the length field and add the
sanity-check just after copying the parameters from userspace


...

>> +		goto e_free;
>> +
>> +	ret = -ENOMEM;
>> +	start = kzalloc(sizeof(*start), GFP_KERNEL);
>> +	if (!start)
>> +		goto e_free;
>> +
>> +	/* Bit 15:6 reserved, must be 0 */
>> +	start->policy = params.policy & ~0xffc0;
>> +
>> +	if (params.dh_cert_length && params.dh_cert_address) {
> 
> Yeah, we talked about this already: sanity-checking needed. But you get
> the idea.
> 

Will do

...

> 
>                  if (copy_from_user(session_addr,
> 				   (void *)params.session_address,
> 				   params.session_length))
> 
> reads better to me. Better yet if you shorten those member names into
> s_addr and s_len and so on...
> 
> 

Will use your recommendation.

thanks
Borislav Petkov Sept. 13, 2017, 6:37 p.m. UTC | #3
On Wed, Sep 13, 2017 at 01:23:08PM -0500, Brijesh Singh wrote:
> Yes, I will add some upper bound check on the length field and add the
> sanity-check just after copying the parameters from userspace

Also, you could either fail the command if some of the reserved fields
are set - picky - or zero them out - less picky :)
Brijesh Singh Sept. 13, 2017, 6:58 p.m. UTC | #4
On 09/13/2017 01:37 PM, Borislav Petkov wrote:
> On Wed, Sep 13, 2017 at 01:23:08PM -0500, Brijesh Singh wrote:
>> Yes, I will add some upper bound check on the length field and add the
>> sanity-check just after copying the parameters from userspace
> 
> Also, you could either fail the command if some of the reserved fields
> are set - picky - or zero them out - less picky :)
> 


Actually reversed fields are not exposed in userspace structure.

e.g a LAUNCH_UPDATE_DATE userspace structure looks like this:

struct kvm_sev_launch_update_data {
	__u64 address;   /* userspace address of memory region to encrypt */
	__u32 length;	 /* length of memory region to encrypt */
};

But SEV firmware command structure is a slightly different (mainly it contains
the reserved field and firmware handle etc).

/**
   * struct sev_data_launch_update_data - LAUNCH_UPDATE_DATA command parameter
   *
   * @handle: firmware handle to use
   * @length: length of memory to be encrypted
   * @address: physical address of memory region to encrypt
   */
  struct sev_data_launch_update_data {
          u32 handle;                             /* In */
          u32 reserved;
          u64 address;                            /* In */
          u32 length;                             /* In */
  };


Please note that some commands require us passing the VM ASID etc --
userspace does not have VM ASID information.

The current approach is -- while handling the command we copy the value
from userspace structure into FW compatible structure and also populate
missing fields which are not known to userspace (e.g firmware handle,
VM ASID, use system physical addresses etc).
Borislav Petkov Sept. 13, 2017, 9:02 p.m. UTC | #5
On Wed, Sep 13, 2017 at 01:58:31PM -0500, Brijesh Singh wrote:
> Actually reversed fields are not exposed in userspace structure.

Ok.

> The current approach is -- while handling the command we copy the value
> from userspace structure into FW compatible structure and also populate
> missing fields which are not known to userspace (e.g firmware handle,
> VM ASID, use system physical addresses etc).

Yap, that makes sense. All I'm saying is, check anything that userspace
can influence and make sure it is sensible.

Thx.
diff mbox

Patch

diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 72f7c27..3e325578 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -329,6 +329,8 @@  static unsigned int max_sev_asid;
 static unsigned long *sev_asid_bitmap;
 static int sev_asid_new(void);
 static void sev_asid_free(int asid);
+static void sev_deactivate_handle(struct kvm *kvm, int *error);
+static void sev_decommission_handle(struct kvm *kvm, int *error);
 
 static bool svm_sev_enabled(void)
 {
@@ -1565,6 +1567,12 @@  static void sev_vm_destroy(struct kvm *kvm)
 	if (!sev_guest(kvm))
 		return;
 
+	/* release the firmware resources for this guest */
+	if (sev_get_handle(kvm)) {
+		sev_deactivate_handle(kvm, &error);
+		sev_decommission_handle(kvm, &error);
+	}
+
 	sev_asid_free(sev_get_asid(kvm));
 	sev_firmware_uninit();
 }
@@ -5635,6 +5643,159 @@  static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
 	return ret;
 }
 
+static int sev_issue_cmd(struct kvm *kvm, int id, void *data, int *error)
+{
+	int fd = sev_get_fd(kvm);
+	struct fd f;
+	int ret;
+
+	f = fdget(fd);
+	if (!f.file)
+		return -EBADF;
+
+	ret = sev_issue_cmd_external_user(f.file, id, data, error);
+	fdput(f);
+
+	return ret;
+}
+
+static void sev_decommission_handle(struct kvm *kvm, int *error)
+{
+	struct sev_data_decommission *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return;
+
+	data->handle = sev_get_handle(kvm);
+	sev_guest_decommission(data, error);
+	kfree(data);
+}
+
+static void sev_deactivate_handle(struct kvm *kvm, int *error)
+{
+	struct sev_data_deactivate *data;
+	int ret;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return;
+
+	data->handle = sev_get_handle(kvm);
+	ret = sev_guest_deactivate(data, error);
+	if (ret)
+		goto e_free;
+
+	wbinvd_on_all_cpus();
+
+	sev_guest_df_flush(error);
+e_free:
+	kfree(data);
+}
+
+static int sev_activate_asid(struct kvm *kvm, unsigned int handle, int *error)
+{
+	struct sev_data_activate *data;
+	int asid = sev_get_asid(kvm);
+	int ret;
+
+	wbinvd_on_all_cpus();
+
+	ret = sev_guest_df_flush(error);
+	if (ret)
+		return ret;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->handle = handle;
+	data->asid   = asid;
+	ret = sev_guest_activate(data, error);
+	if (ret)
+		goto e_err;
+
+	sev_set_handle(kvm, handle);
+e_err:
+	kfree(data);
+	return ret;
+}
+
+static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+	struct sev_data_launch_start *start = NULL;
+	struct kvm_sev_launch_start params;
+	void *dh_cert_addr = NULL;
+	void *session_addr = NULL;
+	int *error = &argp->error;
+	int ret;
+
+	if (!sev_guest(kvm))
+		return -ENOTTY;
+
+	ret = -EFAULT;
+	if (copy_from_user(&params, (void *)argp->data,
+				sizeof(struct kvm_sev_launch_start)))
+		goto e_free;
+
+	ret = -ENOMEM;
+	start = kzalloc(sizeof(*start), GFP_KERNEL);
+	if (!start)
+		goto e_free;
+
+	/* Bit 15:6 reserved, must be 0 */
+	start->policy = params.policy & ~0xffc0;
+
+	if (params.dh_cert_length && params.dh_cert_address) {
+		ret = -ENOMEM;
+		dh_cert_addr = kmalloc(params.dh_cert_length, GFP_KERNEL);
+		if (!dh_cert_addr)
+			goto e_free;
+
+		ret = -EFAULT;
+		if (copy_from_user(dh_cert_addr, (void *)params.dh_cert_address,
+				params.dh_cert_length))
+			goto e_free;
+
+		start->dh_cert_address = __sme_set(__pa(dh_cert_addr));
+		start->dh_cert_length = params.dh_cert_length;
+	}
+
+	if (params.session_length && params.session_address) {
+		ret = -ENOMEM;
+		session_addr = kmalloc(params.session_length, GFP_KERNEL);
+		if (!session_addr)
+			goto e_free;
+
+		ret = -EFAULT;
+		if (copy_from_user(session_addr, (void *)params.session_address,
+				params.session_length))
+			goto e_free;
+
+		start->session_address = __sme_set(__pa(session_addr));
+		start->session_length = params.session_length;
+	}
+
+	start->handle = params.handle;
+	ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_START, start, error);
+	if (ret)
+		goto e_free;
+
+	ret = sev_activate_asid(kvm, start->handle, error);
+	if (ret)
+		goto e_free;
+
+	params.handle = start->handle;
+	if (copy_to_user((void *) argp->data, &params,
+				sizeof(struct kvm_sev_launch_start)))
+		ret = -EFAULT;
+e_free:
+	kfree(dh_cert_addr);
+	kfree(session_addr);
+	kfree(start);
+	return ret;
+}
+
 static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
 {
 	struct kvm_sev_cmd sev_cmd;
@@ -5650,6 +5811,10 @@  static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
 		r = sev_guest_init(kvm, &sev_cmd);
 		break;
 	}
+	case KVM_SEV_LAUNCH_START: {
+		r = sev_launch_start(kvm, &sev_cmd);
+		break;
+	}
 	default:
 		break;
 	}