diff mbox

[RFC,v1,06/22] sev: add initial SEV support

Message ID 147377806784.11859.11149856529336910514.stgit@brijesh-build-machine
State New
Headers show

Commit Message

Brijesh Singh Sept. 13, 2016, 2:47 p.m. UTC
This patch adds the initial support required to integrate Secure
Encrypted Virtualization feature, the patch include the following
changes:

- adds sev.c and sev.h files: the file will contain SEV APIs implemention.
- add kvm_sev_enabled(): similar to kvm_enabled() this function can be
  used to check if sev is enabled on this guest.
- implement functions to parse SEV specific configuration file.

A typical SEV config file looks like this:

[sev-launch]
	flags = "00000000"
	policy	= "000000"
	dh_pub_qx = "0123456789abcdef0123456789abcdef"
	dh_pub_qy = "0123456789abcdef0123456789abcdef"
	nonce = "0123456789abcdef"
	vcpu_count = "1"
	vcpu_length = "30"
	vcpu_mask = "00ab"


Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 Makefile.target      |    2 
 include/sysemu/kvm.h |   10 ++
 include/sysemu/sev.h |   27 +++++
 kvm-all.c            |    6 +
 sev.c                |  282 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 326 insertions(+), 1 deletion(-)
 create mode 100644 include/sysemu/sev.h
 create mode 100644 sev.c

Comments

Eduardo Habkost Sept. 13, 2016, 3:58 p.m. UTC | #1
On Tue, Sep 13, 2016 at 10:47:47AM -0400, Brijesh Singh wrote:
> This patch adds the initial support required to integrate Secure
> Encrypted Virtualization feature, the patch include the following
> changes:
> 
> - adds sev.c and sev.h files: the file will contain SEV APIs implemention.
> - add kvm_sev_enabled(): similar to kvm_enabled() this function can be
>   used to check if sev is enabled on this guest.
> - implement functions to parse SEV specific configuration file.
> 
> A typical SEV config file looks like this:
> 

Are those config options documented somewhere?

> [sev-launch]
> 	flags = "00000000"
> 	policy	= "000000"
> 	dh_pub_qx = "0123456789abcdef0123456789abcdef"
> 	dh_pub_qy = "0123456789abcdef0123456789abcdef"
> 	nonce = "0123456789abcdef"
> 	vcpu_count = "1"
> 	vcpu_length = "30"
> 	vcpu_mask = "00ab"

Why a separate config file loading mechanism? If the user really
needs to load the SEV configuration data from a separate file,
you can just use regular config sections and use -readconfig.

Now, about the format of the new config sections ("sev" and
"sev-launch"): I am not sure adding new command-line options and
config sections is necessary. Is it possible to implement it as a
combination of:
* new options to existing command-line options and/or
* new options to existing objects and/or
* new options to existing devices and/or
* new types for -object? (see how crypto secrets and net filters
  are configured, for an example)


[...]
>  extern bool kvm_allowed;
> +extern bool kvm_sev_allowed;

Can we place this inside struct KVMState?

>  extern bool kvm_kernel_irqchip;
>  extern bool kvm_split_irqchip;
>  extern bool kvm_async_interrupts_allowed;
[...]
> @@ -1745,6 +1747,10 @@ static int kvm_init(MachineState *ms)
>  
>      kvm_state = s;
>  
> +    if (!sev_init(kvm_state)) {
> +        kvm_sev_allowed = true;
> +    }

sev_init() errors are being ignored here. sev_init() could report
errors properly using Error** (and kvm_init() should not ignore
them).

> +
>      if (kvm_eventfds_allowed) {
>          s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
>          s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
[...]
> +
> +struct SEVInfo {
> +    uint8_t state;  /* guest current state */
> +    uint8_t type;   /* guest type (encrypted, unencrypted) */
> +    struct kvm_sev_launch_start *launch_start;
> +    struct kvm_sev_launch_update *launch_update;
> +    struct kvm_sev_launch_finish *launch_finish;
> +};
> +
> +typedef struct SEVInfo SEVInfo;
> +static SEVInfo *sev_info;

Can we place this pointer inside struct KVMState?

[...]
> +static unsigned int read_config_u32(QemuOpts *opts, const char *key)
> +{
> +    unsigned int val;
> +
> +    val = qemu_opt_get_number_del(opts, key, -1);
> +    DPRINTF(" %s = %08x\n", key, val);
> +
> +    return val;
> +}
> +
> +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)

Function name confused me (it seemed to read only one u8 value).

> +{
> +    int i;
> +    const char *v;
> +
> +    v = qemu_opt_get(opts, key);
> +    if (!v) {
> +        return 0;
> +    }
> +
> +    DPRINTF(" %s = ", key);
> +    i = 0;
> +    while (*v) {
> +        sscanf(v, "%2hhx", &val[i]);

Function doesn't check for array length.

> +        DPRINTF("%02hhx", val[i]);
> +        v += 2;
> +        i++;
> +    }
> +    DPRINTF("\n");
> +
> +    return i;

Do we really need to write our own parser? I wonder if we can
reuse crypto/secret.c for loading the keys.

> +}
> +
[...]
Brijesh Singh Sept. 13, 2016, 7:54 p.m. UTC | #2
Hi Eduardo,

On 09/13/2016 10:58 AM, Eduardo Habkost wrote:
>>
>> A typical SEV config file looks like this:
>>
>
> Are those config options documented somewhere?
>

Various commands and parameters are documented [1]

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

>> [sev-launch]
>> 	flags = "00000000"
>> 	policy	= "000000"
>> 	dh_pub_qx = "0123456789abcdef0123456789abcdef"
>> 	dh_pub_qy = "0123456789abcdef0123456789abcdef"
>> 	nonce = "0123456789abcdef"
>> 	vcpu_count = "1"
>> 	vcpu_length = "30"
>> 	vcpu_mask = "00ab"
>
> Why a separate config file loading mechanism? If the user really
> needs to load the SEV configuration data from a separate file,
> you can just use regular config sections and use -readconfig.
>
I will look into -readconfig and see if we can use that.

> Now, about the format of the new config sections ("sev" and
> "sev-launch"): I am not sure adding new command-line options and
> config sections is necessary. Is it possible to implement it as a
> combination of:
> * new options to existing command-line options and/or
> * new options to existing objects and/or
> * new options to existing devices and/or
> * new types for -object? (see how crypto secrets and net filters
>   are configured, for an example)
>
>

All these config parameters should be provided by the guest owner before 
launching or migrating a guest. I believe we need to make changes in 
libvirt, virsh and other upper layer software stack to communicate with 
guest owner to get these input parameters. For development purposes I 
choose a simple config file to get these parameters. I am not sure if we 
will able to "add new option to a existing objects/device" but we can 
look into creating a "new type for -object" or we can simply accept a fd 
from upper layer and read the fd to get these parameters.

> [...]
>>  extern bool kvm_allowed;
>> +extern bool kvm_sev_allowed;
>
> Can we place this inside struct KVMState?
>
Yes, i will add this in v2.
>>  extern bool kvm_kernel_irqchip;
>>  extern bool kvm_split_irqchip;
>>  extern bool kvm_async_interrupts_allowed;
> [...]
>> @@ -1745,6 +1747,10 @@ static int kvm_init(MachineState *ms)
>>
>>      kvm_state = s;
>>
>> +    if (!sev_init(kvm_state)) {
>> +        kvm_sev_allowed = true;
>> +    }
>
> sev_init() errors are being ignored here. sev_init() could report
> errors properly using Error** (and kvm_init() should not ignore
> them).
Thanks, will fix in v2.
>
>> +
>>      if (kvm_eventfds_allowed) {
>>          s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
>>          s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
> [...]
>> +
>> +struct SEVInfo {
>> +    uint8_t state;  /* guest current state */
>> +    uint8_t type;   /* guest type (encrypted, unencrypted) */
>> +    struct kvm_sev_launch_start *launch_start;
>> +    struct kvm_sev_launch_update *launch_update;
>> +    struct kvm_sev_launch_finish *launch_finish;
>> +};
>> +
>> +typedef struct SEVInfo SEVInfo;
>> +static SEVInfo *sev_info;
>
> Can we place this pointer inside struct KVMState?
>
Will try to move into KVMState.
> [...]
>> +static unsigned int read_config_u32(QemuOpts *opts, const char *key)
>> +{
>> +    unsigned int val;
>> +
>> +    val = qemu_opt_get_number_del(opts, key, -1);
>> +    DPRINTF(" %s = %08x\n", key, val);
>> +
>> +    return val;
>> +}
>> +
>> +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)
>
> Function name confused me (it seemed to read only one u8 value).
>
I will fix the name, the function converts string into a hex bytes 
array. e.g "12aabbccdd" is converted to val[] = {0x12,0xaa,0xbb,0xcc, 0xdd}.

>> +{
>> +    int i;
>> +    const char *v;
>> +
>> +    v = qemu_opt_get(opts, key);
>> +    if (!v) {
>> +        return 0;
>> +    }
>> +
>> +    DPRINTF(" %s = ", key);
>> +    i = 0;
>> +    while (*v) {
>> +        sscanf(v, "%2hhx", &val[i]);
>
> Function doesn't check for array length.
Thanks, i will fix this.
>
>> +        DPRINTF("%02hhx", val[i]);
>> +        v += 2;
>> +        i++;
>> +    }
>> +    DPRINTF("\n");
>> +
>> +    return i;
>
> Do we really need to write our own parser? I wonder if we can
> reuse crypto/secret.c for loading the keys.
>
I just looked at crypto/secret.c for loading the keys but not sure if 
will able to reuse the secret_load routines, this is mainly because the 
SEV inputs parameters are different compare to what we have in 
crypto/secrets.c. I will still look more closely and see if we can find 
some common code.
>> +}
>> +
> [...]
>
Michael S. Tsirkin Sept. 13, 2016, 8:10 p.m. UTC | #3
On Tue, Sep 13, 2016 at 02:54:40PM -0500, Brijesh Singh wrote:
> Hi Eduardo,
> 
> On 09/13/2016 10:58 AM, Eduardo Habkost wrote:
> > > 
> > > A typical SEV config file looks like this:
> > > 
> > 
> > Are those config options documented somewhere?
> > 
> 
> Various commands and parameters are documented [1]
> 
> [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf

Could you write a TL;DR summary?

IMHO it's not reasonable to ask that people read spec
documents in order to understand QEMU command line
parameters or config flags.

Some text should also go into docs/

> > > [sev-launch]
> > > 	flags = "00000000"
> > > 	policy	= "000000"
> > > 	dh_pub_qx = "0123456789abcdef0123456789abcdef"
> > > 	dh_pub_qy = "0123456789abcdef0123456789abcdef"
> > > 	nonce = "0123456789abcdef"
> > > 	vcpu_count = "1"
> > > 	vcpu_length = "30"
> > > 	vcpu_mask = "00ab"
> > 
> > Why a separate config file loading mechanism? If the user really
> > needs to load the SEV configuration data from a separate file,
> > you can just use regular config sections and use -readconfig.
> > 
> I will look into -readconfig and see if we can use that.
> 
> > Now, about the format of the new config sections ("sev" and
> > "sev-launch"): I am not sure adding new command-line options and
> > config sections is necessary. Is it possible to implement it as a
> > combination of:
> > * new options to existing command-line options and/or
> > * new options to existing objects and/or
> > * new options to existing devices and/or
> > * new types for -object? (see how crypto secrets and net filters
> >   are configured, for an example)
> > 
> > 
> 
> All these config parameters should be provided by the guest owner before
> launching or migrating a guest. I believe we need to make changes in
> libvirt, virsh and other upper layer software stack to communicate with
> guest owner to get these input parameters. For development purposes I choose
> a simple config file to get these parameters. I am not sure if we will able
> to "add new option to a existing objects/device" but we can look into
> creating a "new type for -object" or we can simply accept a fd from upper
> layer and read the fd to get these parameters.
> 
> > [...]
> > >  extern bool kvm_allowed;
> > > +extern bool kvm_sev_allowed;
> > 
> > Can we place this inside struct KVMState?
> > 
> Yes, i will add this in v2.
> > >  extern bool kvm_kernel_irqchip;
> > >  extern bool kvm_split_irqchip;
> > >  extern bool kvm_async_interrupts_allowed;
> > [...]
> > > @@ -1745,6 +1747,10 @@ static int kvm_init(MachineState *ms)
> > > 
> > >      kvm_state = s;
> > > 
> > > +    if (!sev_init(kvm_state)) {
> > > +        kvm_sev_allowed = true;
> > > +    }
> > 
> > sev_init() errors are being ignored here. sev_init() could report
> > errors properly using Error** (and kvm_init() should not ignore
> > them).
> Thanks, will fix in v2.
> > 
> > > +
> > >      if (kvm_eventfds_allowed) {
> > >          s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
> > >          s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
> > [...]
> > > +
> > > +struct SEVInfo {
> > > +    uint8_t state;  /* guest current state */
> > > +    uint8_t type;   /* guest type (encrypted, unencrypted) */
> > > +    struct kvm_sev_launch_start *launch_start;
> > > +    struct kvm_sev_launch_update *launch_update;
> > > +    struct kvm_sev_launch_finish *launch_finish;
> > > +};
> > > +
> > > +typedef struct SEVInfo SEVInfo;
> > > +static SEVInfo *sev_info;
> > 
> > Can we place this pointer inside struct KVMState?
> > 
> Will try to move into KVMState.
> > [...]
> > > +static unsigned int read_config_u32(QemuOpts *opts, const char *key)
> > > +{
> > > +    unsigned int val;
> > > +
> > > +    val = qemu_opt_get_number_del(opts, key, -1);
> > > +    DPRINTF(" %s = %08x\n", key, val);
> > > +
> > > +    return val;
> > > +}
> > > +
> > > +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)
> > 
> > Function name confused me (it seemed to read only one u8 value).
> > 
> I will fix the name, the function converts string into a hex bytes array.
> e.g "12aabbccdd" is converted to val[] = {0x12,0xaa,0xbb,0xcc, 0xdd}.
> 
> > > +{
> > > +    int i;
> > > +    const char *v;
> > > +
> > > +    v = qemu_opt_get(opts, key);
> > > +    if (!v) {
> > > +        return 0;
> > > +    }
> > > +
> > > +    DPRINTF(" %s = ", key);
> > > +    i = 0;
> > > +    while (*v) {
> > > +        sscanf(v, "%2hhx", &val[i]);
> > 
> > Function doesn't check for array length.
> Thanks, i will fix this.
> > 
> > > +        DPRINTF("%02hhx", val[i]);
> > > +        v += 2;
> > > +        i++;
> > > +    }
> > > +    DPRINTF("\n");
> > > +
> > > +    return i;
> > 
> > Do we really need to write our own parser? I wonder if we can
> > reuse crypto/secret.c for loading the keys.
> > 
> I just looked at crypto/secret.c for loading the keys but not sure if will
> able to reuse the secret_load routines, this is mainly because the SEV
> inputs parameters are different compare to what we have in crypto/secrets.c.
> I will still look more closely and see if we can find some common code.
> > > +}
> > > +
> > [...]
> >
Eduardo Habkost Sept. 13, 2016, 10 p.m. UTC | #4
(CCing Daniel Berrange in case he has feedback on the
nonce/dh_pub_qx/dh_pub_qy loading/parsing at the end of this
message)

On Tue, Sep 13, 2016 at 02:54:40PM -0500, Brijesh Singh wrote:
> Hi Eduardo,
> 
> On 09/13/2016 10:58 AM, Eduardo Habkost wrote:
> > > 
> > > A typical SEV config file looks like this:
> > > 
> > 
> > Are those config options documented somewhere?
> > 
> 
> Various commands and parameters are documented [1]
> 
> [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf

If I understand correctly, the docs describe the firmware
interface. The interface provided by QEMU is not the same thing,
and needs to be documented as well (even if it contains pointers
to sections or tables in the firmware interface docs).

Some of the questions I have about the fields are:
* Do we really need the user to provide all the options below?
  * Can't QEMU or KVM calculate vcpu_count/vcpu_length/vcpu_mask,
    for example?
* Is bit 0 (KS) the only bit that can be set on flags? If so, why
  not a boolean "ks" option?
* Is "policy" the guest policy structure described at page 23? If
  so, why exposing the raw value instead of separate fields for
  each bit/field in the structure? (and only for the ones that
  are supposed to be set by the user)
* If vcpu_mask is a bitmap for each VCPU, should we represent it
  as a list of VCPU indexes?

A good way to model this data and document it more properly is
through a QAPI schema. grep for "opts_visitor_new()" in the code
for examples where QEMU options are parsed according to a QAPI
schema. The downside is that using a QAPI visitor is (AFAIK) not
possible if using -object like I suggest below.

> 
> > > [sev-launch]
> > > 	flags = "00000000"
> > > 	policy	= "000000"
> > > 	dh_pub_qx = "0123456789abcdef0123456789abcdef"
> > > 	dh_pub_qy = "0123456789abcdef0123456789abcdef"
> > > 	nonce = "0123456789abcdef"
> > > 	vcpu_count = "1"
> > > 	vcpu_length = "30"
> > > 	vcpu_mask = "00ab"
> > 
> > Why a separate config file loading mechanism? If the user really
> > needs to load the SEV configuration data from a separate file,
> > you can just use regular config sections and use -readconfig.
> > 
> I will look into -readconfig and see if we can use that.
> 
> > Now, about the format of the new config sections ("sev" and
> > "sev-launch"): I am not sure adding new command-line options and
> > config sections is necessary. Is it possible to implement it as a
> > combination of:
> > * new options to existing command-line options and/or
> > * new options to existing objects and/or
> > * new options to existing devices and/or
> > * new types for -object? (see how crypto secrets and net filters
> >   are configured, for an example)
> > 
> > 
> 
> All these config parameters should be provided by the guest owner before
> launching or migrating a guest. I believe we need to make changes in
> libvirt, virsh and other upper layer software stack to communicate with
> guest owner to get these input parameters. For development purposes I choose
> a simple config file to get these parameters. I am not sure if we will able
> to "add new option to a existing objects/device" but we can look into
> creating a "new type for -object" or we can simply accept a fd from upper
> layer and read the fd to get these parameters.

I was thinking of something like:

  -object sev-launch-rule,flags=0,policy=0,dh_pub_qx=XXXXX,dh_pub_qy=YYYYY,nonce=ZZZZ,vcpu_count=1,vcpu_length=30,vcpu_mask=00ab \
  -machine pc,accel=kvm,sev=on  # see note below[1]

With this, you won't need separate code for command-line, config
files, and QMP commands. They will all be able to use the same
mechanisms.

...but this conflicts with the idea of using QAPI. So I am not
sure which way to go. (But either way we go, we need a clearer
and better documented set of parameters).

[1] I would go even further and separate the accel object options
    from the machine options, but this would require reworking
    the accelerator configuration inside QEMU. e.g.:

  -object kvm-accel,sev=on,id=kvm0
  -machine pc,accel=kvm0

[...]
> > > +static unsigned int read_config_u32(QemuOpts *opts, const char *key)
> > > +{
> > > +    unsigned int val;
> > > +
> > > +    val = qemu_opt_get_number_del(opts, key, -1);
> > > +    DPRINTF(" %s = %08x\n", key, val);
> > > +
> > > +    return val;
> > > +}
> > > +
> > > +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)
> > 
> > Function name confused me (it seemed to read only one u8 value).
> > 
> I will fix the name, the function converts string into a hex bytes array.
> e.g "12aabbccdd" is converted to val[] = {0x12,0xaa,0xbb,0xcc, 0xdd}.
> 
> > > +{
> > > +    int i;
> > > +    const char *v;
> > > +
> > > +    v = qemu_opt_get(opts, key);
> > > +    if (!v) {
> > > +        return 0;
> > > +    }
> > > +
> > > +    DPRINTF(" %s = ", key);
> > > +    i = 0;
> > > +    while (*v) {
> > > +        sscanf(v, "%2hhx", &val[i]);
> > 
> > Function doesn't check for array length.
> Thanks, i will fix this.
> > 
> > > +        DPRINTF("%02hhx", val[i]);
> > > +        v += 2;
> > > +        i++;
> > > +    }
> > > +    DPRINTF("\n");
> > > +
> > > +    return i;
> > 
> > Do we really need to write our own parser? I wonder if we can
> > reuse crypto/secret.c for loading the keys.
> > 
> I just looked at crypto/secret.c for loading the keys but not sure if will
> able to reuse the secret_load routines, this is mainly because the SEV
> inputs parameters are different compare to what we have in crypto/secrets.c.
> I will still look more closely and see if we can find some common code.

There are other parameters, sure, but maybe it would be
appropriate to just load nonce/dh_pub_qx/dh_pub_qy as
TTYPE_QCRYPTO_SECRET object(s) (-object secret,...)? I am not
sure because I don't understand the crypto part fully.

Daniel, what do you think?
Daniel P. Berrangé Sept. 14, 2016, 8:30 a.m. UTC | #5
On Tue, Sep 13, 2016 at 07:00:44PM -0300, Eduardo Habkost wrote:
> (CCing Daniel Berrange in case he has feedback on the
> nonce/dh_pub_qx/dh_pub_qy loading/parsing at the end of this
> message)
> 
> On Tue, Sep 13, 2016 at 02:54:40PM -0500, Brijesh Singh wrote:
> > Hi Eduardo,
> > 
> > On 09/13/2016 10:58 AM, Eduardo Habkost wrote:
> > > > 
> > > > A typical SEV config file looks like this:
> > > > 
> > > 
> > > Are those config options documented somewhere?
> > > 
> > 
> > Various commands and parameters are documented [1]
> > 
> > [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf
> 
> If I understand correctly, the docs describe the firmware
> interface. The interface provided by QEMU is not the same thing,
> and needs to be documented as well (even if it contains pointers
> to sections or tables in the firmware interface docs).
> 
> Some of the questions I have about the fields are:
> * Do we really need the user to provide all the options below?
>   * Can't QEMU or KVM calculate vcpu_count/vcpu_length/vcpu_mask,
>     for example?
> * Is bit 0 (KS) the only bit that can be set on flags? If so, why
>   not a boolean "ks" option?
> * Is "policy" the guest policy structure described at page 23? If
>   so, why exposing the raw value instead of separate fields for
>   each bit/field in the structure? (and only for the ones that
>   are supposed to be set by the user)
> * If vcpu_mask is a bitmap for each VCPU, should we represent it
>   as a list of VCPU indexes?
> 
> A good way to model this data and document it more properly is
> through a QAPI schema. grep for "opts_visitor_new()" in the code
> for examples where QEMU options are parsed according to a QAPI
> schema. The downside is that using a QAPI visitor is (AFAIK) not
> possible if using -object like I suggest below.

It needs to use QOM really, not QAPI, since it has to be user
creatable on the CLI and we don't want to invent new command
line arguments.

> > > > +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)
> > > 
> > > Function name confused me (it seemed to read only one u8 value).
> > > 
> > I will fix the name, the function converts string into a hex bytes array.
> > e.g "12aabbccdd" is converted to val[] = {0x12,0xaa,0xbb,0xcc, 0xdd}.
> > 
> > > > +{
> > > > +    int i;
> > > > +    const char *v;
> > > > +
> > > > +    v = qemu_opt_get(opts, key);
> > > > +    if (!v) {
> > > > +        return 0;
> > > > +    }
> > > > +
> > > > +    DPRINTF(" %s = ", key);
> > > > +    i = 0;
> > > > +    while (*v) {
> > > > +        sscanf(v, "%2hhx", &val[i]);
> > > 
> > > Function doesn't check for array length.
> > Thanks, i will fix this.
> > > 
> > > > +        DPRINTF("%02hhx", val[i]);
> > > > +        v += 2;
> > > > +        i++;
> > > > +    }
> > > > +    DPRINTF("\n");
> > > > +
> > > > +    return i;
> > > 
> > > Do we really need to write our own parser? I wonder if we can
> > > reuse crypto/secret.c for loading the keys.
> > > 
> > I just looked at crypto/secret.c for loading the keys but not sure if will
> > able to reuse the secret_load routines, this is mainly because the SEV
> > inputs parameters are different compare to what we have in crypto/secrets.c.
> > I will still look more closely and see if we can find some common code.
> 
> There are other parameters, sure, but maybe it would be
> appropriate to just load nonce/dh_pub_qx/dh_pub_qy as
> TTYPE_QCRYPTO_SECRET object(s) (-object secret,...)? I am not
> sure because I don't understand the crypto part fully.

The secrets object is used for information that has to be kept
private from eavesdroppers. Based on the param names here
'dh_pub_qx' it sounds like this is non-sensitive "public"
data, so would not need to use the secrets object, but it
is hard to say for sure without close look at the technical
details.

Regards,
Daniel
Daniel P. Berrangé Sept. 14, 2016, 8:37 a.m. UTC | #6
On Tue, Sep 13, 2016 at 10:47:47AM -0400, Brijesh Singh wrote:
> This patch adds the initial support required to integrate Secure
> Encrypted Virtualization feature, the patch include the following
> changes:
> 
> - adds sev.c and sev.h files: the file will contain SEV APIs implemention.
> - add kvm_sev_enabled(): similar to kvm_enabled() this function can be
>   used to check if sev is enabled on this guest.
> - implement functions to parse SEV specific configuration file.
> 
> A typical SEV config file looks like this:
> 
> [sev-launch]
> 	flags = "00000000"
> 	policy	= "000000"
> 	dh_pub_qx = "0123456789abcdef0123456789abcdef"
> 	dh_pub_qy = "0123456789abcdef0123456789abcdef"
> 	nonce = "0123456789abcdef"
> 	vcpu_count = "1"
> 	vcpu_length = "30"
> 	vcpu_mask = "00ab"

We do not want to be inventing new config options for this - we should
be using QOM via -object to create this.



> diff --git a/sev.c b/sev.c
> new file mode 100644
> index 0000000..2d71ca6
> --- /dev/null
> +++ b/sev.c
> @@ -0,0 +1,282 @@
> +/*
> + * QEMU SEV support
> + *
> + * Copyright Advanced Micro Devices 2016
> + *
> + * Author:
> + *      Brijesh Singh <brijesh.singh@amd.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +
> +#include <linux/kvm.h>
> +
> +#include "qemu-common.h"
> +#include "qemu/atomic.h"
> +#include "qemu/option.h"
> +#include "qemu/config-file.h"
> +#include "qemu/error-report.h"
> +#include "hw/hw.h"
> +#include "hw/pci/msi.h"
> +#include "hw/s390x/adapter.h"
> +#include "exec/gdbstub.h"
> +#include "sysemu/kvm_int.h"
> +#include "sysemu/sev.h"
> +#include "qemu/bswap.h"
> +#include "exec/memory.h"
> +#include "exec/ram_addr.h"
> +#include "exec/address-spaces.h"
> +#include "qemu/event_notifier.h"
> +#include "trace.h"
> +#include "hw/irq.h"
> +
> +//#define DEBUG_SEV
> +
> +#ifdef DEBUG_SEV
> +#define DPRINTF(fmt, ...) \
> +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +struct SEVInfo {
> +    uint8_t state;  /* guest current state */
> +    uint8_t type;   /* guest type (encrypted, unencrypted) */
> +    struct kvm_sev_launch_start *launch_start;
> +    struct kvm_sev_launch_update *launch_update;
> +    struct kvm_sev_launch_finish *launch_finish;
> +};
> +
> +typedef struct SEVInfo SEVInfo;
> +static SEVInfo *sev_info;
> +static const char *cfg_file;
> +
> +enum {
> +    LAUNCH_OPTS = 0,
> +};
> +
> +enum {
> +    PRE_ENCRYPTED_GUEST = 0,
> +    UNENCRYPTED_GUEST,
> +};
> +
> +static QemuOptsList launch_opts = {
> +    .name = "sev-launch",
> +    .head = QTAILQ_HEAD_INITIALIZER(launch_opts.head),
> +    .desc = {
> +        {
> +            .name = "flags",
> +            .type = QEMU_OPT_NUMBER,
> +        },
> +        {
> +            .name = "policy",
> +            .type = QEMU_OPT_NUMBER,
> +        },
> +        {
> +            .name = "dh_pub_qx",
> +            .type = QEMU_OPT_STRING,
> +        },
> +        {
> +            .name = "dh_pub_qy",
> +            .type = QEMU_OPT_STRING,
> +        },
> +        {
> +            .name = "nonce",
> +            .type = QEMU_OPT_STRING,
> +        },
> +        {
> +            .name = "vcpu_length",
> +            .type = QEMU_OPT_NUMBER,
> +        },
> +        {
> +            .name = "vcpu_count",
> +            .type = QEMU_OPT_NUMBER,
> +        },
> +        {
> +            .name = "vcpu_mask",
> +            .type = QEMU_OPT_STRING,
> +        },
> +        { /* end of list */ }
> +    },
> +};



> +
> +static QemuOptsList *config_groups[] = {
> +    &launch_opts,
> +    NULL
> +};
> +
> +struct add_rule_data {
> +    SEVInfo *s;
> +    int action;
> +};
> +
> +static unsigned int read_config_u32(QemuOpts *opts, const char *key)
> +{
> +    unsigned int val;
> +
> +    val = qemu_opt_get_number_del(opts, key, -1);
> +    DPRINTF(" %s = %08x\n", key, val);
> +
> +    return val;
> +}
> +
> +static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)
> +{
> +    int i;
> +    const char *v;
> +
> +    v = qemu_opt_get(opts, key);
> +    if (!v) {
> +        return 0;
> +    }
> +
> +    DPRINTF(" %s = ", key);
> +    i = 0;
> +    while (*v) {
> +        sscanf(v, "%2hhx", &val[i]);
> +        DPRINTF("%02hhx", val[i]);
> +        v += 2;
> +        i++;
> +    }
> +    DPRINTF("\n");
> +
> +    return i;
> +}
> +
> +static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
> +{
> +    struct add_rule_data *d = opaque;
> +
> +    switch (d->action) {
> +        case LAUNCH_OPTS: {
> +            struct kvm_sev_launch_start *start;
> +            struct kvm_sev_launch_update *update;
> +            struct kvm_sev_launch_finish *finish;
> +
> +            /* LAUNCH_START parameters */
> +            start = g_malloc0(sizeof(*start));
> +
> +            DPRINTF("Parsing 'sev-launch' parameters\n");
> +            start->flags = read_config_u32(opts, "flags");
> +            start->policy = read_config_u32(opts, "policy");
> +            read_config_u8(opts, "nonce", start->nonce);
> +            read_config_u8(opts, "dh_pub_qx", start->dh_pub_qx);
> +            read_config_u8(opts, "dh_pub_qy", start->dh_pub_qy);
> +            sev_info->launch_start = start;
> +
> +            /* LAUNCH_UPDATE */
> +            update = g_malloc0(sizeof(*update));
> +            sev_info->launch_update = update;
> +
> +            /* LAUNCH_FINISH parameters */
> +            finish = g_malloc0(sizeof(*finish));
> +
> +            finish->vcpu_count = read_config_u32(opts, "vcpu_count");
> +            finish->vcpu_length = read_config_u32(opts, "vcpu_length");
> +            if (qemu_opt_get(opts, "vcpu_mask")) {
> +                finish->vcpu_mask_length =
> +                                    strlen(qemu_opt_get(opts, "vcpu_mask")) / 2;
> +                finish->vcpu_mask_addr = (unsigned long)
> +                                        g_malloc0(finish->vcpu_length);
> +                read_config_u8(opts, "vcpu_mask",
> +                        (uint8_t *)finish->vcpu_mask_addr);
> +            }
> +
> +            sev_info->launch_finish = finish;
> +
> +            break;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int parse_add_rules(QemuOptsList *list, struct add_rule_data *d)
> +{
> +    Error *local_err = NULL;
> +
> +    qemu_opts_foreach(list, add_rule, d, &local_err);
> +    if (local_err) {
> +        return 1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int parse_sev_cfg(SEVInfo *s, int type, const char *filename)
> +{
> +    FILE *f;
> +    int ret = 0;
> +    struct add_rule_data d;
> +
> +    if (filename) {
> +        f = fopen(filename, "r");
> +        if (f == NULL) {
> +            return 1;
> +        }
> +
> +        ret = qemu_config_parse(f, config_groups, filename);
> +        if (ret < 0) {
> +            fprintf(stderr, "SEV: could not parse config file\n");
> +            exit(EXIT_FAILURE);
> +        }
> +    }
> +
> +    switch (type) {
> +    case LAUNCH_OPTS:
> +        d.s = s;
> +        d.action = type;
> +        ret = parse_add_rules(&launch_opts, &d);
> +        break;
> +    }
> +
> +    return ret;
> +
> +}
> +
> +int sev_init(KVMState *kvm_state)
> +{
> +    QemuOpts *opts;
> +    const char *type;
> +
> +    opts = qemu_find_opts_singleton("sev");
> +    cfg_file = qemu_opt_get(opts, "config");
> +    if (!cfg_file) {
> +        return 1;
> +    }
> +
> +    type = qemu_opt_get(opts, "type");
> +    if (!type) {
> +        return 1;
> +    }
> +
> +    sev_info = calloc(1, sizeof(*sev_info));
> +    if (!sev_info) {
> +        return 1;
> +    }

Use 'g_new0' for allocation and dont check the return
value since g_new0 aborts on oom.

> +
> +    if (!strcmp(type, "unencrypted")) {
> +        sev_info->type = UNENCRYPTED_GUEST;
> +    } else if (!strcmp(type, "encrypted")) {
> +        sev_info->type = PRE_ENCRYPTED_GUEST;

You should define an enum in qapi-schema.json for these
values, and use an enum property for 'type' in the QOM
object you then create. This automatically then handles
the string <-> int conversion

> +    } else {
> +        fprintf(stderr, "SEV: unsupported type '%s'\n", type);
> +        goto err;
> +    }
> +
> +    /* call SEV launch start APIs based on guest type */
> +
> +    return 0;
> +err:
> +    free(sev_info);
> +    sev_info = NULL;
> +    return 1;
> +}

All the command line argument handling in this file should be removed.
Instead you should define a user creatable object type to represent
the properties you need to have configured. If you want a simple
example of how to define a QOM object type, take a look at the code
in crypto/secret.c


Regards,
Daniel
Eduardo Habkost Sept. 14, 2016, 11:54 a.m. UTC | #7
On Wed, Sep 14, 2016 at 09:30:51AM +0100, Daniel P. Berrange wrote:
> On Tue, Sep 13, 2016 at 07:00:44PM -0300, Eduardo Habkost wrote:
> > (CCing Daniel Berrange in case he has feedback on the
> > nonce/dh_pub_qx/dh_pub_qy loading/parsing at the end of this
> > message)
> > 
> > On Tue, Sep 13, 2016 at 02:54:40PM -0500, Brijesh Singh wrote:
> > > Hi Eduardo,
> > > 
> > > On 09/13/2016 10:58 AM, Eduardo Habkost wrote:
> > > > > 
> > > > > A typical SEV config file looks like this:
> > > > > 
> > > > 
> > > > Are those config options documented somewhere?
> > > > 
> > > 
> > > Various commands and parameters are documented [1]
> > > 
> > > [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf
> > 
> > If I understand correctly, the docs describe the firmware
> > interface. The interface provided by QEMU is not the same thing,
> > and needs to be documented as well (even if it contains pointers
> > to sections or tables in the firmware interface docs).
> > 
> > Some of the questions I have about the fields are:
> > * Do we really need the user to provide all the options below?
> >   * Can't QEMU or KVM calculate vcpu_count/vcpu_length/vcpu_mask,
> >     for example?
> > * Is bit 0 (KS) the only bit that can be set on flags? If so, why
> >   not a boolean "ks" option?
> > * Is "policy" the guest policy structure described at page 23? If
> >   so, why exposing the raw value instead of separate fields for
> >   each bit/field in the structure? (and only for the ones that
> >   are supposed to be set by the user)
> > * If vcpu_mask is a bitmap for each VCPU, should we represent it
> >   as a list of VCPU indexes?
> > 
> > A good way to model this data and document it more properly is
> > through a QAPI schema. grep for "opts_visitor_new()" in the code
> > for examples where QEMU options are parsed according to a QAPI
> > schema. The downside is that using a QAPI visitor is (AFAIK) not
> > possible if using -object like I suggest below.
> 
> It needs to use QOM really, not QAPI, since it has to be user
> creatable on the CLI and we don't want to invent new command
> line arguments.

As much as I don't like not being able to use the QAPI schema to
document -object, this is true.

[...]
> > > > 
> > > > Do we really need to write our own parser? I wonder if we can
> > > > reuse crypto/secret.c for loading the keys.
> > > > 
> > > I just looked at crypto/secret.c for loading the keys but not sure if will
> > > able to reuse the secret_load routines, this is mainly because the SEV
> > > inputs parameters are different compare to what we have in crypto/secrets.c.
> > > I will still look more closely and see if we can find some common code.
> > 
> > There are other parameters, sure, but maybe it would be
> > appropriate to just load nonce/dh_pub_qx/dh_pub_qy as
> > TTYPE_QCRYPTO_SECRET object(s) (-object secret,...)? I am not
> > sure because I don't understand the crypto part fully.
> 
> The secrets object is used for information that has to be kept
> private from eavesdroppers. Based on the param names here
> 'dh_pub_qx' it sounds like this is non-sensitive "public"
> data, so would not need to use the secrets object, but it
> is hard to say for sure without close look at the technical
> details.

They are a public key and nonce for ECDH key agreement, so they
are public (AFAICS).

So let's forget about "-object secret". I just want to ensure we
either reuse existing parsing code, or put the new parser in
common code that can be reused.
Daniel P. Berrangé Sept. 14, 2016, 11:58 a.m. UTC | #8
On Wed, Sep 14, 2016 at 08:54:12AM -0300, Eduardo Habkost wrote:
> On Wed, Sep 14, 2016 at 09:30:51AM +0100, Daniel P. Berrange wrote:
> > On Tue, Sep 13, 2016 at 07:00:44PM -0300, Eduardo Habkost wrote:
> > > (CCing Daniel Berrange in case he has feedback on the
> > > nonce/dh_pub_qx/dh_pub_qy loading/parsing at the end of this
> > > message)
> > > 
> > > On Tue, Sep 13, 2016 at 02:54:40PM -0500, Brijesh Singh wrote:
> > > > Hi Eduardo,
> > > > 
> > > > On 09/13/2016 10:58 AM, Eduardo Habkost wrote:
> > > > > > 
> > > > > > A typical SEV config file looks like this:
> > > > > > 
> > > > > 
> > > > > Are those config options documented somewhere?
> > > > > 
> > > > 
> > > > Various commands and parameters are documented [1]
> > > > 
> > > > [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf
> > > 
> > > If I understand correctly, the docs describe the firmware
> > > interface. The interface provided by QEMU is not the same thing,
> > > and needs to be documented as well (even if it contains pointers
> > > to sections or tables in the firmware interface docs).
> > > 
> > > Some of the questions I have about the fields are:
> > > * Do we really need the user to provide all the options below?
> > >   * Can't QEMU or KVM calculate vcpu_count/vcpu_length/vcpu_mask,
> > >     for example?
> > > * Is bit 0 (KS) the only bit that can be set on flags? If so, why
> > >   not a boolean "ks" option?
> > > * Is "policy" the guest policy structure described at page 23? If
> > >   so, why exposing the raw value instead of separate fields for
> > >   each bit/field in the structure? (and only for the ones that
> > >   are supposed to be set by the user)
> > > * If vcpu_mask is a bitmap for each VCPU, should we represent it
> > >   as a list of VCPU indexes?
> > > 
> > > A good way to model this data and document it more properly is
> > > through a QAPI schema. grep for "opts_visitor_new()" in the code
> > > for examples where QEMU options are parsed according to a QAPI
> > > schema. The downside is that using a QAPI visitor is (AFAIK) not
> > > possible if using -object like I suggest below.
> > 
> > It needs to use QOM really, not QAPI, since it has to be user
> > creatable on the CLI and we don't want to invent new command
> > line arguments.
> 
> As much as I don't like not being able to use the QAPI schema to
> document -object, this is true.

FWIW, in the medium-long term there is clear scope for
adding a 'object' type to the QAPI schema, that could be
used to generate the boilerplate code for QOM, so we can
reconcile these eventually.

Regards,
Daniel
Brijesh Singh Sept. 14, 2016, 4:10 p.m. UTC | #9
>> Various commands and parameters are documented [1]
>>
>> [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf
>
> If I understand correctly, the docs describe the firmware
> interface. The interface provided by QEMU is not the same thing,
> and needs to be documented as well (even if it contains pointers
> to sections or tables in the firmware interface docs).
>
> Some of the questions I have about the fields are:
> * Do we really need the user to provide all the options below?
>   * Can't QEMU or KVM calculate vcpu_count/vcpu_length/vcpu_mask,
>     for example?

Good question, I don't think we need to get this information from guest 
owner and it can be calculated from KVM. I will check with security 
folks on how this information is used in measurement generation and make 
the changes accordingly.

> * Is bit 0 (KS) the only bit that can be set on flags? If so, why
>   not a boolean "ks" option?
Agreed. I will fix in v2.

> * Is "policy" the guest policy structure described at page 23? If
>   so, why exposing the raw value instead of separate fields for
>   each bit/field in the structure? (and only for the ones that
>   are supposed to be set by the user)

Yes policy is described in chapter 3, page 23. I am open to separate the 
fields.
Let me know if something like this works

sev-launch-rule,flags.ks=0,policy.dbg=0,policy.ks=0,policy.nosend=0,...


> * If vcpu_mask is a bitmap for each VCPU, should we represent it
>   as a list of VCPU indexes?
>
I will check on this one.

> A good way to model this data and document it more properly is
> through a QAPI schema. grep for "opts_visitor_new()" in the code
> for examples where QEMU options are parsed according to a QAPI
> schema. The downside is that using a QAPI visitor is (AFAIK) not
> possible if using -object like I suggest below.
>
>>
>>>> [sev-launch]
>>>> 	flags = "00000000"
>>>> 	policy	= "000000"
>>>> 	dh_pub_qx = "0123456789abcdef0123456789abcdef"
>>>> 	dh_pub_qy = "0123456789abcdef0123456789abcdef"
>>>> 	nonce = "0123456789abcdef"
>>>> 	vcpu_count = "1"
>>>> 	vcpu_length = "30"
>>>> 	vcpu_mask = "00ab"
>>>
>>
>> All these config parameters should be provided by the guest owner before
>> launching or migrating a guest. I believe we need to make changes in
>> libvirt, virsh and other upper layer software stack to communicate with
>> guest owner to get these input parameters. For development purposes I choose
>> a simple config file to get these parameters. I am not sure if we will able
>> to "add new option to a existing objects/device" but we can look into
>> creating a "new type for -object" or we can simply accept a fd from upper
>> layer and read the fd to get these parameters.
>
> I was thinking of something like:
>
>   -object sev-launch-rule,flags=0,policy=0,dh_pub_qx=XXXXX,dh_pub_qy=YYYYY,nonce=ZZZZ,vcpu_count=1,vcpu_length=30,vcpu_mask=00ab \
>   -machine pc,accel=kvm,sev=on  # see note below[1]
>
> With this, you won't need separate code for command-line, config
> files, and QMP commands. They will all be able to use the same
> mechanisms.
>
> ...but this conflicts with the idea of using QAPI. So I am not
> sure which way to go. (But either way we go, we need a clearer
> and better documented set of parameters).
>

I am open to idea and need direction on which way to go. I will  work on 
documenting the parameters and usages. Should I consider implementing 
your below approach in v2 ?

-object 
sev-launch-rule,flags=0,policy=0,dh_pub_qx=XXXXX,dh_pub_qy=YYYYY,nonce=ZZZZ,vcpu_count=1,vcpu_length=30,vcpu_mask=00ab 
\ -machine pc,accel=kvm,sev=on

Any tips on which qemu file i can use as reference during 
implementation. Thanks.

> [1] I would go even further and separate the accel object options
>     from the machine options, but this would require reworking
>     the accelerator configuration inside QEMU. e.g.:
>
>   -object kvm-accel,sev=on,id=kvm0
>   -machine pc,accel=kvm0
>
> [...]
>>> Do we really need to write our own parser? I wonder if we can
>>> reuse crypto/secret.c for loading the keys.
>>>
>> I just looked at crypto/secret.c for loading the keys but not sure if will
>> able to reuse the secret_load routines, this is mainly because the SEV
>> inputs parameters are different compare to what we have in crypto/secrets.c.
>> I will still look more closely and see if we can find some common code.
>
> There are other parameters, sure, but maybe it would be
> appropriate to just load nonce/dh_pub_qx/dh_pub_qy as
> TTYPE_QCRYPTO_SECRET object(s) (-object secret,...)? I am not
> sure because I don't understand the crypto part fully.
>
> Daniel, what do you think?
>
Daniel P. Berrangé Sept. 14, 2016, 4:13 p.m. UTC | #10
On Wed, Sep 14, 2016 at 11:10:54AM -0500, Brijesh Singh wrote:
> 
> I am open to idea and need direction on which way to go. I will  work on
> documenting the parameters and usages. Should I consider implementing your
> below approach in v2 ?
> 
> -object sev-launch-rule,flags=0,policy=0,dh_pub_qx=XXXXX,dh_pub_qy=YYYYY,nonce=ZZZZ,vcpu_count=1,vcpu_length=30,vcpu_mask=00ab
> \ -machine pc,accel=kvm,sev=on
> 
> Any tips on which qemu file i can use as reference during implementation.

Take a look at crypto/secret.c for an example of how to create a QOM
object that can be loaded with '-object'. There are more examples
in backends/{hostmem,rng}*.c too.

Regards,
Daniel
Michael S. Tsirkin Sept. 14, 2016, 4:20 p.m. UTC | #11
On Wed, Sep 14, 2016 at 11:10:54AM -0500, Brijesh Singh wrote:
> 
> > > Various commands and parameters are documented [1]
> > > 
> > > [1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf
> > 
> > If I understand correctly, the docs describe the firmware
> > interface. The interface provided by QEMU is not the same thing,
> > and needs to be documented as well (even if it contains pointers
> > to sections or tables in the firmware interface docs).
> > 
> > Some of the questions I have about the fields are:
> > * Do we really need the user to provide all the options below?
> >   * Can't QEMU or KVM calculate vcpu_count/vcpu_length/vcpu_mask,
> >     for example?
> 
> Good question, I don't think we need to get this information from guest
> owner and it can be calculated from KVM. I will check with security folks on
> how this information is used in measurement generation and make the changes
> accordingly.
> 
> > * Is bit 0 (KS) the only bit that can be set on flags? If so, why
> >   not a boolean "ks" option?
> Agreed. I will fix in v2.
> 
> > * Is "policy" the guest policy structure described at page 23? If
> >   so, why exposing the raw value instead of separate fields for
> >   each bit/field in the structure? (and only for the ones that
> >   are supposed to be set by the user)
> 
> Yes policy is described in chapter 3, page 23. I am open to separate the
> fields.
> Let me know if something like this works
> 
> sev-launch-rule,flags.ks=0,policy.dbg=0,policy.ks=0,policy.nosend=0,...

My question is, does all of it have to be sev specific?
For example, add a generic flag to block debug commands from monitor.
When blocked, and if sev happens to be enabled, you can run guest with
debug disabled.
Brijesh Singh Sept. 14, 2016, 6:46 p.m. UTC | #12
Hi Michael,

>>
>> Yes policy is described in chapter 3, page 23. I am open to separate the
>> fields.
>> Let me know if something like this works
>>
>> sev-launch-rule,flags.ks=0,policy.dbg=0,policy.ks=0,policy.nosend=0,...
>
> My question is, does all of it have to be sev specific?
> For example, add a generic flag to block debug commands from monitor.
> When blocked, and if sev happens to be enabled, you can run guest with
> debug disabled.
>
>
All the sev option should be specified during guest creation.

A typical SEV flow look like this:

1) Guest owner requests cloud provider to launch a SEV quest.

2) Cloud provides requests the guest owner to provide the SEV launch 
parameter. These parameters includes: flags, policy, nonce, public 
diffie-hellman keys.

3) cloud provider launches qemu with '-sev' option.
    The code path looks like this:

    kvm_sev_guest_start()   [qemu]
       kvm_vm_ioctl(SEV_ISSUE_CMD)                 [kvm]
         asid = allocates a new SEV aid.           [kvm]
         psp_issue_cmd(LAUNCH_START)               [kvm]
          // the will call the firmware to create
          // new cryptographic context
         psp_issue_cmd(ACTIVATE, asid)             [kvm]
         // the command will bind asid to this crypto
         // context and loads the encryption key into
         // memory controller

4) As part of guest creation qemu loader copies data/code into guest 
memory (e.g BIOS). We need to encrypt the data/code copied into guest 
memory using LAUNCH_UPDATE command.
       sev_launch_update()
         kvm_vm_ioctl(SEV_ISSUE_CMD)
           psp_issue_cmd(LAUNCH_UPDATE, buffer)
            // the command will encrypt in-place

5) After qemu is done copying data into guest memory we call 
LAUNCH_FINISH, this command generate a measurement (basically hash of 
the data encrypted through LAUNCH_UPDATE).

6) Cloud provider sends the measurement to guest owner.

7) Guest owner validates the measurement. If measurement matches then we 
are good to launch the guest. This should ensure that bootcode was not 
compromised by hypervisor.

Once the guest is running then memory content of VM is transparently 
encrypted with key loaded into memory controller. SEV guest have the 
concept of private and shared memory. Private memory is encrypted with 
the guest-specific keys, while share memory may be encrypted with 
hypervisor key. Certain type of memory (namely instruction pages and 
guest page tables) are always considered as private. The guest OS can
mark individual pages of memory as encrypted using the standard x86 page 
table. I call it C-bit in PTE, if C-bit (bit 47) is set then page is 
private and if C-bit is 0 then page is shared. A page that is marked 
private will be automatically decrypted when read from the DRAM and 
encrypted when written to DRAM. Note that since C-bit is only 
controllable by the guest OS when it in operating in 64-bit or 32-bit 
PAE mode, in all other modes the SEV hardware forces the C-bit to a 1.

One of field in launch_start struct is 'policy', if policy allows the 
debugging then hypervisor can use SEV commands 
(kvm_sev_debug_decrypt/kvm_sev_debug_encrypt) to read and write into 
guest memory. If policy does not allow the debugging then any read of 
guest memory from hypervisor will get encrypted data and similarly 
writes to guest memory from hypervisor will result guest reading the 
garbage.
Michael S. Tsirkin Sept. 14, 2016, 8:23 p.m. UTC | #13
On Wed, Sep 14, 2016 at 01:46:09PM -0500, Brijesh Singh wrote:
> 7) Guest owner validates the measurement. If measurement matches then we are
> good to launch the guest. This should ensure that bootcode was not
> compromised by hypervisor.

As hypervisor can e.g. execute said code in any order (without touching
protected memory) this seems rather like adding asserts in code at
random points. Frankly if one is so worried about the boot sequence,
just send an already booted guest to the cloud provider.


But anyway, that's beside the point. My point is that all this
measurement dance is orthogonal to memory encryption.
It happens to be part of the same AMD CPU, but it
might not be on other CPUs, and I don't see why
should command line/QOM APIs tie us to what AMD did.
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index a440bcb..74ad204 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -136,7 +136,7 @@  ifdef CONFIG_SOFTMMU
 obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
 obj-y += qtest.o bootdevice.o
 obj-y += hw/
-obj-$(CONFIG_KVM) += kvm-all.o
+obj-$(CONFIG_KVM) += kvm-all.o sev.o
 obj-y += memory.o cputlb.o
 obj-y += memory_mapping.o
 obj-y += dump.o
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index c9c2436..7f83de0 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -40,6 +40,7 @@ 
 #endif
 
 extern bool kvm_allowed;
+extern bool kvm_sev_allowed;
 extern bool kvm_kernel_irqchip;
 extern bool kvm_split_irqchip;
 extern bool kvm_async_interrupts_allowed;
@@ -56,6 +57,14 @@  extern bool kvm_ioeventfd_any_length_allowed;
 
 #if defined CONFIG_KVM || !defined NEED_CPU_H
 #define kvm_enabled()           (kvm_allowed)
+
+/**
+ * kvm_sev_enabled:
+ *
+ * Returns: true if guest is running into SEV-enabled mode.
+ */
+#define kvm_sev_enabled()       (kvm_sev_allowed)
+
 /**
  * kvm_irqchip_in_kernel:
  *
@@ -171,6 +180,7 @@  extern bool kvm_ioeventfd_any_length_allowed;
 
 #else
 #define kvm_enabled()           (0)
+#define kvm_sev_enabled()       (false)
 #define kvm_irqchip_in_kernel() (false)
 #define kvm_irqchip_is_split() (false)
 #define kvm_async_interrupts_enabled() (false)
diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h
new file mode 100644
index 0000000..0ee8aff
--- /dev/null
+++ b/include/sysemu/sev.h
@@ -0,0 +1,27 @@ 
+/*
+ * QEMU SEV support
+ *
+ * Copyright: Advanced Micro Devices, 2016
+ *
+ * Authors:
+ *  Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_SEV_H
+#define QEMU_SEV_H
+
+#include "sysemu/kvm.h"
+
+/**
+ * sev_init - initialize Secure Encrypted Virtualization on this guest
+ * @kvm_state - KVM handle
+ * Returns: 1 on error, 0 on success
+ */
+int sev_init(KVMState *kvm_state);
+
+#endif
+
diff --git a/kvm-all.c b/kvm-all.c
index ebf35b0..e194849 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -36,6 +36,7 @@ 
 #include "qemu/event_notifier.h"
 #include "trace.h"
 #include "hw/irq.h"
+#include "sysemu/sev.h"
 
 #include "hw/boards.h"
 
@@ -119,6 +120,7 @@  bool kvm_readonly_mem_allowed;
 bool kvm_vm_attributes_allowed;
 bool kvm_direct_msi_allowed;
 bool kvm_ioeventfd_any_length_allowed;
+bool kvm_sev_allowed;
 
 static const KVMCapabilityInfo kvm_required_capabilites[] = {
     KVM_CAP_INFO(USER_MEMORY),
@@ -1745,6 +1747,10 @@  static int kvm_init(MachineState *ms)
 
     kvm_state = s;
 
+    if (!sev_init(kvm_state)) {
+        kvm_sev_allowed = true;
+    }
+
     if (kvm_eventfds_allowed) {
         s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
         s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
diff --git a/sev.c b/sev.c
new file mode 100644
index 0000000..2d71ca6
--- /dev/null
+++ b/sev.c
@@ -0,0 +1,282 @@ 
+/*
+ * QEMU SEV support
+ *
+ * Copyright Advanced Micro Devices 2016
+ *
+ * Author:
+ *      Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/atomic.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "hw/hw.h"
+#include "hw/pci/msi.h"
+#include "hw/s390x/adapter.h"
+#include "exec/gdbstub.h"
+#include "sysemu/kvm_int.h"
+#include "sysemu/sev.h"
+#include "qemu/bswap.h"
+#include "exec/memory.h"
+#include "exec/ram_addr.h"
+#include "exec/address-spaces.h"
+#include "qemu/event_notifier.h"
+#include "trace.h"
+#include "hw/irq.h"
+
+//#define DEBUG_SEV
+
+#ifdef DEBUG_SEV
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+struct SEVInfo {
+    uint8_t state;  /* guest current state */
+    uint8_t type;   /* guest type (encrypted, unencrypted) */
+    struct kvm_sev_launch_start *launch_start;
+    struct kvm_sev_launch_update *launch_update;
+    struct kvm_sev_launch_finish *launch_finish;
+};
+
+typedef struct SEVInfo SEVInfo;
+static SEVInfo *sev_info;
+static const char *cfg_file;
+
+enum {
+    LAUNCH_OPTS = 0,
+};
+
+enum {
+    PRE_ENCRYPTED_GUEST = 0,
+    UNENCRYPTED_GUEST,
+};
+
+static QemuOptsList launch_opts = {
+    .name = "sev-launch",
+    .head = QTAILQ_HEAD_INITIALIZER(launch_opts.head),
+    .desc = {
+        {
+            .name = "flags",
+            .type = QEMU_OPT_NUMBER,
+        },
+        {
+            .name = "policy",
+            .type = QEMU_OPT_NUMBER,
+        },
+        {
+            .name = "dh_pub_qx",
+            .type = QEMU_OPT_STRING,
+        },
+        {
+            .name = "dh_pub_qy",
+            .type = QEMU_OPT_STRING,
+        },
+        {
+            .name = "nonce",
+            .type = QEMU_OPT_STRING,
+        },
+        {
+            .name = "vcpu_length",
+            .type = QEMU_OPT_NUMBER,
+        },
+        {
+            .name = "vcpu_count",
+            .type = QEMU_OPT_NUMBER,
+        },
+        {
+            .name = "vcpu_mask",
+            .type = QEMU_OPT_STRING,
+        },
+        { /* end of list */ }
+    },
+};
+
+static QemuOptsList *config_groups[] = {
+    &launch_opts,
+    NULL
+};
+
+struct add_rule_data {
+    SEVInfo *s;
+    int action;
+};
+
+static unsigned int read_config_u32(QemuOpts *opts, const char *key)
+{
+    unsigned int val;
+
+    val = qemu_opt_get_number_del(opts, key, -1);
+    DPRINTF(" %s = %08x\n", key, val);
+
+    return val;
+}
+
+static int read_config_u8(QemuOpts *opts, const char *key, uint8_t *val)
+{
+    int i;
+    const char *v;
+
+    v = qemu_opt_get(opts, key);
+    if (!v) {
+        return 0;
+    }
+
+    DPRINTF(" %s = ", key);
+    i = 0;
+    while (*v) {
+        sscanf(v, "%2hhx", &val[i]);
+        DPRINTF("%02hhx", val[i]);
+        v += 2;
+        i++;
+    }
+    DPRINTF("\n");
+
+    return i;
+}
+
+static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
+{
+    struct add_rule_data *d = opaque;
+
+    switch (d->action) {
+        case LAUNCH_OPTS: {
+            struct kvm_sev_launch_start *start;
+            struct kvm_sev_launch_update *update;
+            struct kvm_sev_launch_finish *finish;
+
+            /* LAUNCH_START parameters */
+            start = g_malloc0(sizeof(*start));
+
+            DPRINTF("Parsing 'sev-launch' parameters\n");
+            start->flags = read_config_u32(opts, "flags");
+            start->policy = read_config_u32(opts, "policy");
+            read_config_u8(opts, "nonce", start->nonce);
+            read_config_u8(opts, "dh_pub_qx", start->dh_pub_qx);
+            read_config_u8(opts, "dh_pub_qy", start->dh_pub_qy);
+            sev_info->launch_start = start;
+
+            /* LAUNCH_UPDATE */
+            update = g_malloc0(sizeof(*update));
+            sev_info->launch_update = update;
+
+            /* LAUNCH_FINISH parameters */
+            finish = g_malloc0(sizeof(*finish));
+
+            finish->vcpu_count = read_config_u32(opts, "vcpu_count");
+            finish->vcpu_length = read_config_u32(opts, "vcpu_length");
+            if (qemu_opt_get(opts, "vcpu_mask")) {
+                finish->vcpu_mask_length =
+                                    strlen(qemu_opt_get(opts, "vcpu_mask")) / 2;
+                finish->vcpu_mask_addr = (unsigned long)
+                                        g_malloc0(finish->vcpu_length);
+                read_config_u8(opts, "vcpu_mask",
+                        (uint8_t *)finish->vcpu_mask_addr);
+            }
+
+            sev_info->launch_finish = finish;
+
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int parse_add_rules(QemuOptsList *list, struct add_rule_data *d)
+{
+    Error *local_err = NULL;
+
+    qemu_opts_foreach(list, add_rule, d, &local_err);
+    if (local_err) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static int parse_sev_cfg(SEVInfo *s, int type, const char *filename)
+{
+    FILE *f;
+    int ret = 0;
+    struct add_rule_data d;
+
+    if (filename) {
+        f = fopen(filename, "r");
+        if (f == NULL) {
+            return 1;
+        }
+
+        ret = qemu_config_parse(f, config_groups, filename);
+        if (ret < 0) {
+            fprintf(stderr, "SEV: could not parse config file\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    switch (type) {
+    case LAUNCH_OPTS:
+        d.s = s;
+        d.action = type;
+        ret = parse_add_rules(&launch_opts, &d);
+        break;
+    }
+
+    return ret;
+
+}
+
+int sev_init(KVMState *kvm_state)
+{
+    QemuOpts *opts;
+    const char *type;
+
+    opts = qemu_find_opts_singleton("sev");
+    cfg_file = qemu_opt_get(opts, "config");
+    if (!cfg_file) {
+        return 1;
+    }
+
+    type = qemu_opt_get(opts, "type");
+    if (!type) {
+        return 1;
+    }
+
+    sev_info = calloc(1, sizeof(*sev_info));
+    if (!sev_info) {
+        return 1;
+    }
+
+    if (!strcmp(type, "unencrypted")) {
+        sev_info->type = UNENCRYPTED_GUEST;
+    } else if (!strcmp(type, "encrypted")) {
+        sev_info->type = PRE_ENCRYPTED_GUEST;
+    } else {
+        fprintf(stderr, "SEV: unsupported type '%s'\n", type);
+        goto err;
+    }
+
+    /* call SEV launch start APIs based on guest type */
+
+    return 0;
+err:
+    free(sev_info);
+    sev_info = NULL;
+    return 1;
+}
+