All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dov Murik <dovmurik@linux.ibm.com>
To: "Philippe Mathieu-Daudé" <philmd@redhat.com>, qemu-devel@nongnu.org
Cc: Tom Lendacky <thomas.lendacky@amd.com>,
	Ashish Kalra <ashish.kalra@amd.com>,
	Brijesh Singh <brijesh.singh@amd.com>,
	Eduardo Habkost <ehabkost@redhat.com>,
	"Michael S. Tsirkin" <mst@redhat.com>,
	Connor Kuehl <ckuehl@redhat.com>,
	James Bottomley <jejb@linux.ibm.com>,
	Richard Henderson <richard.henderson@linaro.org>,
	"Dr. David Alan Gilbert" <dgilbert@redhat.com>,
	dovmurik@linux.ibm.com,
	Tobin Feldman-Fitzthum <tobin@linux.ibm.com>,
	Jim Cadden <jcadden@ibm.com>, Paolo Bonzini <pbonzini@redhat.com>,
	Laszlo Ersek <lersek@redhat.com>
Subject: Re: [PATCH v2 1/2] sev/i386: Introduce sev_add_kernel_loader_hashes for measured linux boot
Date: Tue, 22 Jun 2021 12:44:30 +0300	[thread overview]
Message-ID: <25b381ad-cdca-60dc-6fb1-1d97ea626843@linux.ibm.com> (raw)
In-Reply-To: <89258a7b-fe24-4930-5af7-278b68d1f07c@redhat.com>

Hi Philippe,

On 21/06/2021 23:32, Philippe Mathieu-Daudé wrote:
> Hi Dov,
> 
> Minor comments inlined.
> 
> On 6/21/21 9:05 PM, Dov Murik wrote:
>> Add the sev_add_kernel_loader_hashes function to calculate the hashes of
>> the kernel/initrd/cmdline and fill a designated OVMF encrypted hash
>> table area.  For this to work, OVMF must support an encrypted area to
>> place the data which is advertised via a special GUID in the OVMF reset
>> table.
>>
>> The hashes of each of the files is calculated (or the string in the case
>> of the cmdline with trailing '\0' included).  Each entry in the hashes
>> table is GUID identified and since they're passed through the
>> sev_encrypt_flash interface, the hashes will be accumulated by the PSP
>> measurement (SEV_LAUNCH_MEASURE).
>>
>> Co-developed-by: James Bottomley <jejb@linux.ibm.com>
>> Signed-off-by: James Bottomley <jejb@linux.ibm.com>
>> Signed-off-by: Dov Murik <dovmurik@linux.ibm.com>
>> ---
>>  target/i386/sev-stub.c |   5 ++
>>  target/i386/sev.c      | 121 +++++++++++++++++++++++++++++++++++++++++
>>  target/i386/sev_i386.h |  12 ++++
>>  3 files changed, 138 insertions(+)
>>
>> diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
>> index 0227cb5177..2b5e42d644 100644
>> --- a/target/i386/sev-stub.c
>> +++ b/target/i386/sev-stub.c
>> @@ -81,3 +81,8 @@ sev_get_attestation_report(const char *mnonce, Error **errp)
>>      error_setg(errp, "SEV is not available in this QEMU");
>>      return NULL;
>>  }
>> +
>> +bool sev_add_kernel_loader_hashes(KernelLoaderContext *ctx, Error **errp)
>> +{
>> +    return false;
> 
> Can't happen, so:
> 
>       g_assert_not_reached();
> 

OK, I'll use it.

I guess the comment is relevant to other functions in that file as well
(e.g., sev_encrypt_flash), but I'll leave that to your SEV-housekeeping
series.


>> +}
>> diff --git a/target/i386/sev.c b/target/i386/sev.c
>> index 83df8c09f6..8e3f601bb6 100644
>> --- a/target/i386/sev.c
>> +++ b/target/i386/sev.c
>> @@ -23,6 +23,7 @@
>>  #include "qemu/base64.h"
>>  #include "qemu/module.h"
>>  #include "qemu/uuid.h"
>> +#include "crypto/hash.h"
>>  #include "sysemu/kvm.h"
>>  #include "sev_i386.h"
>>  #include "sysemu/sysemu.h"
>> @@ -83,6 +84,29 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
>>      uint32_t reset_addr;
>>  } SevInfoBlock;
>>  
>> +#define SEV_HASH_TABLE_RV_GUID  "7255371f-3a3b-4b04-927b-1da6efa8d454"
>> +typedef struct __attribute__((__packed__))
> 
> The codebase used to use QEMU_PACKED (see "qemu/compiler.h" but
> apparently it isn't enforced.
> 

I can use it.


>  SevHashTableDescriptor {
>> +    /* SEV hash table area guest address */
>> +    uint32_t base;
>> +    /* SEV hash table area size (in bytes) */
>> +    uint32_t size;
>> +} SevHashTableDescriptor;
>> +
>> +/* hard code sha256 digest size */
>> +#define HASH_SIZE 32
>> +
>> +typedef struct __attribute__((__packed__)) SevHashTableEntry {
>> +    uint8_t guid[16];
> 
> What about using QemuUUID?
> 

I agree. I'll use it, coupled with your .data init below.


>> +    uint16_t len;
>> +    uint8_t hash[HASH_SIZE];
>> +} SevHashTableEntry;
>> +
>> +typedef struct __attribute__((__packed__)) SevHashTable {
>> +    uint8_t guid[16];
>> +    uint16_t len;
>> +    SevHashTableEntry entries[];
>> +} SevHashTable;
>> +
>>  static SevGuestState *sev_guest;
>>  static Error *sev_mig_blocker;
>>  
>> @@ -1077,6 +1101,103 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
>>      return 0;
>>  }
>>  
>> +static const uint8_t sev_hash_table_header_guid[] =
>> +        UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93,
>> +                0xd4, 0x11, 0xfd, 0x21);
> 
> Personally I'd have used:
> 
> static const QemuUUID sev_hash_table_header_guid = {
>     .data = UUID_LE(...);
> };

Yes, I'll use this.

> 
> and added qemu_uuid_copy() to complete the API, but that's fine.

I think simple C assignment works for structs (and hence QemuUUID),
something like:

    SevHashTable *ht = ...;
    ht->guid = sev_hash_table_header_guid;

(where both ht->guid and sev_hash_table_header_guid are QemuUUID.)


> 
>> +
>> +static const uint8_t sev_kernel_entry_guid[] =
>> +        UUID_LE(0x4de79437, 0xabd2, 0x427f, 0xb8, 0x35, 0xd5, 0xb1,
>> +                0x72, 0xd2, 0x04, 0x5b);
>> +static const uint8_t sev_initrd_entry_guid[] =
>> +        UUID_LE(0x44baf731, 0x3a2f, 0x4bd7, 0x9a, 0xf1, 0x41, 0xe2,
>> +                0x91, 0x69, 0x78, 0x1d);
>> +static const uint8_t sev_cmdline_entry_guid[] =
>> +        UUID_LE(0x97d02dd8, 0xbd20, 0x4c94, 0xaa, 0x78, 0xe7, 0x71,
>> +                0x4d, 0x36, 0xab, 0x2a);
>> +
>> +static void fill_sev_hash_table_entry(SevHashTableEntry *e, const uint8_t *guid,
>> +                                      const uint8_t *hash, size_t hash_len)
>> +{
>> +    memcpy(e->guid, guid, sizeof(e->guid));
>> +    e->len = sizeof(*e);
>> +    memcpy(e->hash, hash, hash_len);
>> +}
>> +
>> +/*
>> + * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page
>> + * which is included in SEV's initial memory measurement.
>> + */
>> +bool sev_add_kernel_loader_hashes(KernelLoaderContext *ctx, Error **errp)
>> +{
>> +    uint8_t *data;
>> +    SevHashTableDescriptor *area;
>> +    SevHashTable *ht;
>> +    SevHashTableEntry *e;
>> +    uint8_t hash_buf[HASH_SIZE];
>> +    uint8_t *hash = hash_buf;
>> +    size_t hash_len = sizeof(hash_buf);
>> +    int ht_index = 0;
>> +    int aligned_len;
>> +
>> +    if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) {
> 
> If we never use the data_len argument, can we simplify the prototype?

The current uses for the OVMF reset vector GUIDed table is for simple
structs with known length (secret injection page address, SEV-ES reset
address, SEV table of hashes address).  But keeping the length there
allows adding variable-sized entries such as strings/blobs.



> 
>> +        error_setg(errp, "SEV: kernel specified but OVMF has no hash table guid");
>> +        return false;
>> +    }
>> +    area = (SevHashTableDescriptor *)data;
>> +
>> +    ht = qemu_map_ram_ptr(NULL, area->base);
>> +
>> +    /* Populate the hashes table header */
>> +    memcpy(ht->guid, sev_hash_table_header_guid, sizeof(ht->guid));
>> +    ht->len = sizeof(*ht);
>> +
>> +    /* Calculate hash of kernel command-line */
>> +    if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->cmdline_data,
>> +                           ctx->cmdline_size,
>> +                           &hash, &hash_len, errp) < 0) {
>> +        return false;
>> +    }
> 
> Maybe move the qcrypto_hash_bytes() call before filling ht?

(below)

> 
>> +    e = &ht->entries[ht_index++];
>> +    fill_sev_hash_table_entry(e, sev_cmdline_entry_guid, hash, hash_len);
>> +
>> +    /* Calculate hash of initrd */
>> +    if (ctx->initrd_data) {
>> +        if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->initrd_data,
>> +                               ctx->initrd_size, &hash, &hash_len, errp) < 0) {
>> +            return false;
>> +        }
> 
> Ah, now I see the pattern. Hmm OK then.
> 

But this might change if initrd_hash is no longer optional (see separate
self-reply to this patch).  In such a case I'll probably first calculate
all the three hashes, and then fill in the SevHashTable struct.


-Dov


>> +        e = &ht->entries[ht_index++];
>> +        fill_sev_hash_table_entry(e, sev_initrd_entry_guid, hash, hash_len);
>> +    }
>> +
>> +    /* Calculate hash of the kernel */
>> +    struct iovec iov[2] = {
>> +        { .iov_base = ctx->setup_data, .iov_len = ctx->setup_size },
>> +        { .iov_base = ctx->kernel_data, .iov_len = ctx->kernel_size }
>> +    };
>> +    if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, iov, 2,
>> +                            &hash, &hash_len, errp) < 0) {
>> +        return false;
>> +    }
>> +    e = &ht->entries[ht_index++];
>> +    fill_sev_hash_table_entry(e, sev_kernel_entry_guid, hash, hash_len);
>> +
>> +    /* now we have all the possible entries, finalize the hashes table */
>> +    ht->len += ht_index * sizeof(*e);
>> +    /* SEV len has to be 16 byte aligned */
>> +    aligned_len = ROUND_UP(ht->len, 16);
>> +    if (aligned_len != ht->len) {
>> +        /* zero the excess data so the measurement can be reliably calculated */
>> +        memset(&ht->entries[ht_index], 0, aligned_len - ht->len);
>> +    }
>> +
>> +    if (sev_encrypt_flash((uint8_t *)ht, aligned_len, errp) < 0) {
>> +        return false;
>> +    }
>> +
>> +    return true;
>> +}
> 


  reply	other threads:[~2021-06-22  9:47 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-21 19:05 [PATCH v2 0/2] x86/sev: Measured Linux SEV guest with kernel/initrd/cmdline Dov Murik
2021-06-21 19:05 ` [PATCH v2 1/2] sev/i386: Introduce sev_add_kernel_loader_hashes for measured linux boot Dov Murik
2021-06-21 20:32   ` Philippe Mathieu-Daudé
2021-06-22  9:44     ` Dov Murik [this message]
2021-06-22  9:49       ` Philippe Mathieu-Daudé
2021-06-22 10:26         ` Dov Murik
2021-06-22 11:10           ` Philippe Mathieu-Daudé
2021-06-22  8:28   ` Dov Murik
2021-06-22 21:15   ` Connor Kuehl
2021-06-23  8:41     ` Dov Murik
2021-06-23  8:49       ` Daniel P. Berrangé
2021-06-23  9:28         ` Dov Murik
2021-06-21 19:05 ` [PATCH v2 2/2] x86/sev: generate SEV kernel loader hashes in x86_load_linux Dov Murik
2021-06-22 20:55   ` Connor Kuehl
2021-06-23  6:54     ` Dov Murik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=25b381ad-cdca-60dc-6fb1-1d97ea626843@linux.ibm.com \
    --to=dovmurik@linux.ibm.com \
    --cc=ashish.kalra@amd.com \
    --cc=brijesh.singh@amd.com \
    --cc=ckuehl@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=jcadden@ibm.com \
    --cc=jejb@linux.ibm.com \
    --cc=lersek@redhat.com \
    --cc=mst@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=philmd@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=richard.henderson@linaro.org \
    --cc=thomas.lendacky@amd.com \
    --cc=tobin@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.