diff mbox

[Part2,v5.1,12.4/31] crypto: ccp: Implement SEV_PLATFORM_STATUS ioctl command

Message ID 20171011170205.qpu677qiqe4ludwm@pd.tnic (mailing list archive)
State New, archived
Headers show

Commit Message

Borislav Petkov Oct. 11, 2017, 5:02 p.m. UTC
On Fri, Oct 06, 2017 at 08:06:02PM -0500, Brijesh Singh wrote:
> The SEV_PLATFORM_STATUS command can be used by the platform owner to
> get the current status of the platform. The command is defined in
> SEV spec section 5.5.
> 
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: "Radim Krčmář" <rkrcmar@redhat.com>
> Cc: Borislav Petkov <bp@suse.de>
> Cc: Herbert Xu <herbert@gondor.apana.org.au>
> Cc: Gary Hook <gary.hook@amd.com>
> Cc: Tom Lendacky <thomas.lendacky@amd.com>
> Cc: linux-crypto@vger.kernel.org
> Cc: kvm@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  drivers/crypto/ccp/psp-dev.c | 34 ++++++++++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)

...

> @@ -198,6 +228,10 @@ static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
>  		ret = sev_handle_cmd(SEV_CMD_FACTORY_RESET, 0, &input.error);
>  		break;
>  	}
> +	case SEV_PLATFORM_STATUS: {
> +		ret = sev_ioctl_platform_status(&input);
> +		break;
> +	}

What's with the curly brackets around the case: statements?

Anyway, here are some more improvements:

* you can get rid of the struct copying into out and the bitfields by
doing something like this:

	ret = sev_do_cmd(SEV_CMD_PLATFORM_STATUS, data, &argp->error);
        if (ret)
                goto e_free;

        /* Clear out reserved fields: */
        data->owner  &= BIT(0);
        data->config &= BIT(0);

I'm not sure those are the ones you need to clear but you get
the idea - you simply poke holes in the reserved fields before
copying to userspace. If you need a more sophisticated mask, use
GENMASK/GENMASK_ULL.

And then you don't need struct sev_user_data_status and
simply remove the bitfields too.

* Also, a function should have a verb in the name, thus
sev_ioctl_do_platform_status().

---

Comments

Brijesh Singh Oct. 11, 2017, 7:49 p.m. UTC | #1
On 10/11/2017 12:02 PM, Borislav Petkov wrote:
...

> 
> What's with the curly brackets around the case: statements?
> 

I will remove the curly braces.

> Anyway, here are some more improvements:
> 
> * you can get rid of the struct copying into out and the bitfields by
> doing something like this:
> 
> 	ret = sev_do_cmd(SEV_CMD_PLATFORM_STATUS, data, &argp->error);
>          if (ret)
>                  goto e_free;
> 
>          /* Clear out reserved fields: */
>          data->owner  &= BIT(0);
>          data->config &= BIT(0);
> 
> I'm not sure those are the ones you need to clear but you get
> the idea - you simply poke holes in the reserved fields before
> copying to userspace. If you need a more sophisticated mask, use
> GENMASK/GENMASK_ULL.
> 

If we decide to go with this approach then we need use GENMASK (see below).

...

> --- a/include/linux/psp-sev.h
> +++ b/include/linux/psp-sev.h
> @@ -144,11 +144,9 @@ struct sev_data_status {


Since the structure is shared with firmware hence I was trying to match 
it with the spec.


>   	u8 api_major;				/* Out */
>   	u8 api_minor;				/* Out */
>   	u8 state;				/* Out */
> -	u8 owner : 1;				/* Out */
> -	u8 reserved1 : 7;
> -	u32 config : 1;				/* Out */
> -	u32 reserved2 : 23;
> -	u32 build : 8;				/* Out */
> +	u8 owner;				/* Out */


This is OK for now. But in future if FW steals another bit from 
reserved1 field to expose a new flag then 'owner' name will no longer be 
valid. If you don't to use bit field then we have to use some generic 
name instead of naming the field as 'owner'. Please note that its not 
just userspace, KVM driver also uses the same fields and it may also 
need to know which bit position to use.


> +	u32 config;				/* Out */
> +	u32 build;				/* Out */


This is a tricky one. The 32-bit are packed as:

BIT0 - config.es
BIT1-23: reserved
BIT24-31: build

Now, if we really don't want to use bit field then we have to declare 
them as:

u8 config[3];
u8 build;

I would prefer to keep the structure as is. I am OK with changing the 
userspace sev_user_data_status to match with FW's sev_data_status to 
avoid the coying from FW structure to userspace structure.



>   	u32 guest_count;			/* Out */
>   } __packed;
>   
>
Borislav Petkov Oct. 11, 2017, 8:04 p.m. UTC | #2
On Wed, Oct 11, 2017 at 02:49:55PM -0500, Brijesh Singh wrote:
> This is OK for now. But in future if FW steals another bit from reserved1
> field to expose a new flag then 'owner' name will no longer be valid. If you
> don't to use bit field then we have to use some generic name instead of
> naming the field as 'owner'. Please note that its not just userspace, KVM
> driver also uses the same fields and it may also need to know which bit
> position to use.

So what is this field called in the spec?

> This is a tricky one. The 32-bit are packed as:
> 
> BIT0 - config.es
> BIT1-23: reserved
> BIT24-31: build

Is that what the firmware gives?

Then it is easy:

	<firmware_field_name> &= GENMASK(23, 1);

and then userspace can pick apart bit 0 and bit24-31.

Just use one raw struct which the firmware gives you and the rest is
done by the sw. Like clearing reserved fields before copying to user.

You don't want to be updating that struct layout later: think of old
qemu with new kernel and all those different configurations.
Borislav Petkov Oct. 11, 2017, 8:10 p.m. UTC | #3
On Wed, Oct 11, 2017 at 10:04:44PM +0200, Borislav Petkov wrote:
> Then it is easy:
> 
> 	<firmware_field_name> &= GENMASK(23, 1);

[1:23] range cleared, of course:

	<firmware_field_name> &= ~GENMASK(23, 1);
Brijesh Singh Oct. 11, 2017, 8:10 p.m. UTC | #4
On 10/11/2017 03:04 PM, Borislav Petkov wrote:
> On Wed, Oct 11, 2017 at 02:49:55PM -0500, Brijesh Singh wrote:
>> This is OK for now. But in future if FW steals another bit from reserved1
>> field to expose a new flag then 'owner' name will no longer be valid. If you
>> don't to use bit field then we have to use some generic name instead of
>> naming the field as 'owner'. Please note that its not just userspace, KVM
>> driver also uses the same fields and it may also need to know which bit
>> position to use.
> 
> So what is this field called in the spec?
> 

See Section 5.5.2 [1]

[1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf

The bit0 is named as 'owner' and bits 7:1 is left blank (no name is given).


>> This is a tricky one. The 32-bit are packed as:
>>
>> BIT0 - config.es
>> BIT1-23: reserved
>> BIT24-31: build
> 
> Is that what the firmware gives?
> 

Yes

> Then it is easy:
> 
> 	<firmware_field_name> &= GENMASK(23, 1);
> 
> and then userspace can pick apart bit 0 and bit24-31.
> 

The bit0 is named as config.es and bit1-23 is left blank and bit31-24 is 
named as build.

The current 'struct sev_data_status' matches with the firmware names and 
the bit fields. Only thing I did was the fields with no name is called 
as "reservedX"


> Just use one raw struct which the firmware gives you and the rest is
> done by the sw. Like clearing reserved fields before copying to user.
> 
> You don't want to be updating that struct layout later: think of old
> qemu with new kernel and all those different configurations.
>
Borislav Petkov Oct. 11, 2017, 8:28 p.m. UTC | #5
On Wed, Oct 11, 2017 at 03:10:49PM -0500, Brijesh Singh wrote:
> The current 'struct sev_data_status' matches with the firmware names and the
> bit fields. Only thing I did was the fields with no name is called as
> "reservedX"

Ok, I see it. So what you actually wanna do is:

struct sev_data_status {
        u8 api_major;                           /* Out */
        u8 api_minor;                           /* Out */
        u8 state;                               /* Out */
        u8 flags;                               /* Out */
        u32 config;                             /* Out */
        u32 guest_count;                        /* Out */
} __packed;

as this is exactly what the firwmare gives you. Theoretically, you
could've also done:

struct sev_data_status {
	u64 first_qword;
	u32 second_dword;
};

but you have the fields mostly defined already and that would be too
confusing.

What I mean is, once you've gotten the command buffer, then you can pick
fields apart in software.

	owner	  = status.flags & 1;
	config_es = status.config & 1;
	build	  = (status.config >> 24) & 0xff;

This way, if new fields get added, you don't have to change the struct
definitions - *especially* if they're visible to userspace - and users
of that struct can be extended to understand the new fields.

And before you copy the struct to userspace, you can simply clear out
the reserved fields as nothing should rely on them having a particular
value, because, well, they're reserved, doh.

Makes sense?
Brijesh Singh Oct. 11, 2017, 8:45 p.m. UTC | #6
On 10/11/2017 03:28 PM, Borislav Petkov wrote:
> On Wed, Oct 11, 2017 at 03:10:49PM -0500, Brijesh Singh wrote:
>> The current 'struct sev_data_status' matches with the firmware names and the
>> bit fields. Only thing I did was the fields with no name is called as
>> "reservedX"
> 
> Ok, I see it. So what you actually wanna do is:
> 
> struct sev_data_status {
>          u8 api_major;                           /* Out */
>          u8 api_minor;                           /* Out */
>          u8 state;                               /* Out */
>          u8 flags;                               /* Out */
>          u32 config;                             /* Out */
>          u32 guest_count;                        /* Out */
> } __packed;
> 

OK, if userspace is going to pick bits apart then how about this:

  struct sev_data_status {
          u8 api_major;                           /* Out */
          u8 api_minor;                           /* Out */
          u8 state;                               /* Out */
          u32 flags;                              /* Out */
          u8 build;                               /* Out */
          u32 guest_count;                        /* Out */
  } __packed;

> 
> Makes sense?
> 

Please let me know if you are okay with my above structure.

-Brijesh
Brijesh Singh Oct. 11, 2017, 8:53 p.m. UTC | #7
On 10/11/2017 03:45 PM, Brijesh Singh wrote:
> 
> 
> On 10/11/2017 03:28 PM, Borislav Petkov wrote:
>> On Wed, Oct 11, 2017 at 03:10:49PM -0500, Brijesh Singh wrote:
>>> The current 'struct sev_data_status' matches with the firmware names 
>>> and the
>>> bit fields. Only thing I did was the fields with no name is called as
>>> "reservedX"
>>
>> Ok, I see it. So what you actually wanna do is:
>>
>> struct sev_data_status {
>>          u8 api_major;                           /* Out */
>>          u8 api_minor;                           /* Out */
>>          u8 state;                               /* Out */
>>          u8 flags;                               /* Out */
>>          u32 config;                             /* Out */
>>          u32 guest_count;                        /* Out */
>> } __packed;
>>
> 
> OK, if userspace is going to pick bits apart then how about this:
> 
>   struct sev_data_status {
>           u8 api_major;                           /* Out */
>           u8 api_minor;                           /* Out */
>           u8 state;                               /* Out */
>           u32 flags;                              /* Out */
>           u8 build;                               /* Out */
>           u32 guest_count;                        /* Out */
>   } __packed;
> 


BTW, I kept the build name because KVM driver prints some debug 
information like this:

pr_info_once("SEV: api_major %d api_minor %d build %d\n",
               status->api_major, status->api_minor, status->build);

Of course, I can modify it to access bit field if we decide to go with 
your recommended structure - thanks
Borislav Petkov Oct. 11, 2017, 8:54 p.m. UTC | #8
On Wed, Oct 11, 2017 at 03:45:00PM -0500, Brijesh Singh wrote:
> OK, if userspace is going to pick bits apart then how about this:
> 
>  struct sev_data_status {
>          u8 api_major;                           /* Out */
>          u8 api_minor;                           /* Out */
>          u8 state;                               /* Out */
>          u32 flags;                              /* Out */
>          u8 build;                               /* Out */

I guess. Except the spec marks those as 31:24 and belonging to the dword
starting at offset 4 and you've made "build" a separate u8 and the dword
starts at offset 3. I mean, not a big deal, I think you can change the
spec for a change :-)

Because it looks like the 4 bytes starting at offset 3 really are meant
for miscellaneous bits. And then CONFIG.ES could've been bit 1 of the
byte at offset 3. Oh well...

> Please let me know if you are okay with my above structure.

But yeah, in the end of the day it probably doesn't matter a whole lot.
The spec just counts those in 32-bit quantities.
diff mbox

Patch

diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
index d668045956cb..1479db533da0 100644
--- a/drivers/crypto/ccp/psp-dev.c
+++ b/drivers/crypto/ccp/psp-dev.c
@@ -176,9 +176,8 @@  static int sev_do_cmd(int cmd, void *data, int *psp_ret)
 	return ret;
 }
 
-static int sev_ioctl_platform_status(struct sev_issue_cmd *argp)
+static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp)
 {
-	struct sev_user_data_status out;
 	struct sev_data_status *data;
 	int ret;
 
@@ -186,19 +185,15 @@  static int sev_ioctl_platform_status(struct sev_issue_cmd *argp)
 	if (!data)
 		return -ENOMEM;
 
-	ret = sev_handle_cmd(SEV_CMD_PLATFORM_STATUS, data, &argp->error);
+	ret = sev_do_cmd(SEV_CMD_PLATFORM_STATUS, data, &argp->error);
 	if (ret)
 		goto e_free;
 
-	out.api_major = data->api_major;
-	out.api_minor = data->api_minor;
-	out.state = data->state;
-	out.owner = data->owner;
-	out.config = data->config;
-	out.build = data->build;
-	out.guest_count = data->guest_count;
-	if (copy_to_user((void __user *)(uintptr_t) argp->data,
-			 &out, sizeof(struct sev_user_data_status)))
+	/* Clear out reserved fields: */
+	data->owner  &= BIT(0);
+	data->config &= BIT(0);
+
+	if (copy_to_user((void __user *)argp->data, data, sizeof(*data)))
 		ret = -EFAULT;
 
 e_free:
@@ -226,10 +221,10 @@  static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
 		ret = sev_do_cmd(SEV_CMD_FACTORY_RESET, 0, &input.error);
 		break;
 
-	case SEV_PLATFORM_STATUS: {
-		ret = sev_ioctl_platform_status(&input);
+	case SEV_PLATFORM_STATUS:
+		ret = sev_ioctl_do_platform_status(&input);
 		break;
-	}
+
 	default:
 		ret = -EINVAL;
 		goto out;
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
index 10b843cce75f..223942ba3e7e 100644
--- a/include/linux/psp-sev.h
+++ b/include/linux/psp-sev.h
@@ -144,11 +144,9 @@  struct sev_data_status {
 	u8 api_major;				/* Out */
 	u8 api_minor;				/* Out */
 	u8 state;				/* Out */
-	u8 owner : 1;				/* Out */
-	u8 reserved1 : 7;
-	u32 config : 1;				/* Out */
-	u32 reserved2 : 23;
-	u32 build : 8;				/* Out */
+	u8 owner;				/* Out */
+	u32 config;				/* Out */
+	u32 build;				/* Out */
 	u32 guest_count;			/* Out */
 } __packed;