Add property for requesting AMD SEV measured kernel launch

- The 'sev-guest' object gains a boolean 'kernel-hashes' property
    which must be enabled to request a measured kernel launch.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmGWViUACgkQvobrtBUQ
 T9+nmw/+OIAj3gWwpPNrUdi2KeqmBdSBMG13CUCFXfAmQUDD8GkYZ78Cgzm7gkjm
 kH/bNiOWxA8QnteeEC6K1SF0z+wlyl0Q6SKxP+Zo+iRWw0fJkKivvtz/e7bE3E/K
 A8l7u5ZUUSGcP8fc/LbPzXKwS0C46B506MekeKbV4A03renXxfK0WLsNDcB29n7G
 /f4JEDbs96WyfRJdyzo9U4oumndKEvh6c8CazRk9g3PZQlULmLq5JpvbQzap/4HL
 Z/46ZalJkbrAMrby+0e9wNBE+5g+vkD/bVOJwHG9bv6ZEvp1vWMYwI0wF5T7Y0Wx
 2Uv4d0z+mjP5zPoxhKHmVrZNGoYKG80vnOIO45WqTWeJFsF7khLZ+oBEy6ito/Zy
 +DOo/FJCeCxdlh6JRB2xPM452iswx8moC/1fY6jeuVDF14s/Br5TpIYP+cqeBF1B
 YKzyzPSOUKZWoyVTHCVzEu4ddrlIyw9FHemG4RvBRuVd7ed12xzWHs3pa6i8smHf
 zmroL34X+//MorgFk5fzNbTmR65EzXTjR6gp/UbEmgaOZRpM2Gyh94DQttaOm3Kq
 FaLHP+jLFmuJPcfthJYRpzC7WvVoX2YT5mk3xQNXtEPzA6ESodzWPc1cTignth3t
 5/+sYkT+KCj5BhENqKWxUPmewpmgM3L+GxNWyucEqX62TNxRN8g=
 =1HDG
 -----END PGP SIGNATURE-----

Merge tag 'sev-hashes-pull-request' of https://gitlab.com/berrange/qemu into staging

Add property for requesting AMD SEV measured kernel launch

 - The 'sev-guest' object gains a boolean 'kernel-hashes' property
   which must be enabled to request a measured kernel launch.

# gpg: Signature made Thu 18 Nov 2021 02:33:25 PM CET
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [full]

* tag 'sev-hashes-pull-request' of https://gitlab.com/berrange/qemu:
  target/i386/sev: Replace qemu_map_ram_ptr with address_space_map
  target/i386/sev: Perform padding calculations at compile-time
  target/i386/sev: Fail when invalid hashes table area detected
  target/i386/sev: Rephrase error message when no hashes table in guest firmware
  target/i386/sev: Add kernel hashes only if sev-guest.kernel-hashes=on
  qapi/qom,target/i386: sev-guest: Introduce kernel-hashes=on|off option

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-11-18 15:06:05 +01:00
commit 44a3aa0608
3 changed files with 77 additions and 15 deletions

View File

@ -769,6 +769,10 @@
# @reduced-phys-bits: number of bits in physical addresses that become # @reduced-phys-bits: number of bits in physical addresses that become
# unavailable when SEV is enabled # unavailable when SEV is enabled
# #
# @kernel-hashes: if true, add hashes of kernel/initrd/cmdline to a
# designated guest firmware page for measured boot
# with -kernel (default: false) (since 6.2)
#
# Since: 2.12 # Since: 2.12
## ##
{ 'struct': 'SevGuestProperties', { 'struct': 'SevGuestProperties',
@ -778,7 +782,8 @@
'*policy': 'uint32', '*policy': 'uint32',
'*handle': 'uint32', '*handle': 'uint32',
'*cbitpos': 'uint32', '*cbitpos': 'uint32',
'reduced-phys-bits': 'uint32' } } 'reduced-phys-bits': 'uint32',
'*kernel-hashes': 'bool' } }
## ##
# @ObjectType: # @ObjectType:

View File

@ -5189,7 +5189,7 @@ SRST
-object secret,id=sec0,keyid=secmaster0,format=base64,\\ -object secret,id=sec0,keyid=secmaster0,format=base64,\\
data=$SECRET,iv=$(<iv.b64) data=$SECRET,iv=$(<iv.b64)
``-object sev-guest,id=id,cbitpos=cbitpos,reduced-phys-bits=val,[sev-device=string,policy=policy,handle=handle,dh-cert-file=file,session-file=file]`` ``-object sev-guest,id=id,cbitpos=cbitpos,reduced-phys-bits=val,[sev-device=string,policy=policy,handle=handle,dh-cert-file=file,session-file=file,kernel-hashes=on|off]``
Create a Secure Encrypted Virtualization (SEV) guest object, Create a Secure Encrypted Virtualization (SEV) guest object,
which can be used to provide the guest memory encryption support which can be used to provide the guest memory encryption support
on AMD processors. on AMD processors.
@ -5229,6 +5229,10 @@ SRST
session with the guest owner to negotiate keys used for session with the guest owner to negotiate keys used for
attestation. The file must be encoded in base64. attestation. The file must be encoded in base64.
The ``kernel-hashes`` adds the hashes of given kernel/initrd/
cmdline to a designated guest firmware page for measured Linux
boot with -kernel. The default is off. (Since 6.2)
e.g to launch a SEV guest e.g to launch a SEV guest
.. parsed-literal:: .. parsed-literal::

View File

@ -37,6 +37,7 @@
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "exec/confidential-guest-support.h" #include "exec/confidential-guest-support.h"
#include "hw/i386/pc.h" #include "hw/i386/pc.h"
#include "exec/address-spaces.h"
#define TYPE_SEV_GUEST "sev-guest" #define TYPE_SEV_GUEST "sev-guest"
OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
@ -62,6 +63,7 @@ struct SevGuestState {
char *session_file; char *session_file;
uint32_t cbitpos; uint32_t cbitpos;
uint32_t reduced_phys_bits; uint32_t reduced_phys_bits;
bool kernel_hashes;
/* runtime state */ /* runtime state */
uint32_t handle; uint32_t handle;
@ -109,9 +111,19 @@ typedef struct QEMU_PACKED SevHashTable {
SevHashTableEntry cmdline; SevHashTableEntry cmdline;
SevHashTableEntry initrd; SevHashTableEntry initrd;
SevHashTableEntry kernel; SevHashTableEntry kernel;
uint8_t padding[];
} SevHashTable; } SevHashTable;
/*
* Data encrypted by sev_encrypt_flash() must be padded to a multiple of
* 16 bytes.
*/
typedef struct QEMU_PACKED PaddedSevHashTable {
SevHashTable ht;
uint8_t padding[ROUND_UP(sizeof(SevHashTable), 16) - sizeof(SevHashTable)];
} PaddedSevHashTable;
QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0);
static SevGuestState *sev_guest; static SevGuestState *sev_guest;
static Error *sev_mig_blocker; static Error *sev_mig_blocker;
@ -327,6 +339,20 @@ sev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
sev->sev_device = g_strdup(value); sev->sev_device = g_strdup(value);
} }
static bool sev_guest_get_kernel_hashes(Object *obj, Error **errp)
{
SevGuestState *sev = SEV_GUEST(obj);
return sev->kernel_hashes;
}
static void sev_guest_set_kernel_hashes(Object *obj, bool value, Error **errp)
{
SevGuestState *sev = SEV_GUEST(obj);
sev->kernel_hashes = value;
}
static void static void
sev_guest_class_init(ObjectClass *oc, void *data) sev_guest_class_init(ObjectClass *oc, void *data)
{ {
@ -345,6 +371,11 @@ sev_guest_class_init(ObjectClass *oc, void *data)
sev_guest_set_session_file); sev_guest_set_session_file);
object_class_property_set_description(oc, "session-file", object_class_property_set_description(oc, "session-file",
"guest owners session parameters (encoded with base64)"); "guest owners session parameters (encoded with base64)");
object_class_property_add_bool(oc, "kernel-hashes",
sev_guest_get_kernel_hashes,
sev_guest_set_kernel_hashes);
object_class_property_set_description(oc, "kernel-hashes",
"add kernel hashes to guest firmware for measured Linux boot");
} }
static void static void
@ -1196,18 +1227,35 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp)
uint8_t *data; uint8_t *data;
SevHashTableDescriptor *area; SevHashTableDescriptor *area;
SevHashTable *ht; SevHashTable *ht;
PaddedSevHashTable *padded_ht;
uint8_t cmdline_hash[HASH_SIZE]; uint8_t cmdline_hash[HASH_SIZE];
uint8_t initrd_hash[HASH_SIZE]; uint8_t initrd_hash[HASH_SIZE];
uint8_t kernel_hash[HASH_SIZE]; uint8_t kernel_hash[HASH_SIZE];
uint8_t *hashp; uint8_t *hashp;
size_t hash_len = HASH_SIZE; size_t hash_len = HASH_SIZE;
int aligned_len; hwaddr mapped_len = sizeof(*padded_ht);
MemTxAttrs attrs = { 0 };
bool ret = true;
/*
* Only add the kernel hashes if the sev-guest configuration explicitly
* stated kernel-hashes=on.
*/
if (!sev_guest->kernel_hashes) {
return false;
}
if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) {
error_setg(errp, "SEV: kernel specified but OVMF has no hash table guid"); error_setg(errp, "SEV: kernel specified but guest firmware "
"has no hashes table GUID");
return false; return false;
} }
area = (SevHashTableDescriptor *)data; area = (SevHashTableDescriptor *)data;
if (!area->base || area->size < sizeof(PaddedSevHashTable)) {
error_setg(errp, "SEV: guest firmware hashes table area is invalid "
"(base=0x%x size=0x%x)", area->base, area->size);
return false;
}
/* /*
* Calculate hash of kernel command-line with the terminating null byte. If * Calculate hash of kernel command-line with the terminating null byte. If
@ -1248,7 +1296,13 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp)
* Populate the hashes table in the guest's memory at the OVMF-designated * Populate the hashes table in the guest's memory at the OVMF-designated
* area for the SEV hashes table * area for the SEV hashes table
*/ */
ht = qemu_map_ram_ptr(NULL, area->base); padded_ht = address_space_map(&address_space_memory, area->base,
&mapped_len, true, attrs);
if (!padded_ht || mapped_len != sizeof(*padded_ht)) {
error_setg(errp, "SEV: cannot map hashes table guest memory area");
return false;
}
ht = &padded_ht->ht;
ht->guid = sev_hash_table_header_guid; ht->guid = sev_hash_table_header_guid;
ht->len = sizeof(*ht); ht->len = sizeof(*ht);
@ -1265,18 +1319,17 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp)
ht->kernel.len = sizeof(ht->kernel); ht->kernel.len = sizeof(ht->kernel);
memcpy(ht->kernel.hash, kernel_hash, sizeof(ht->kernel.hash)); memcpy(ht->kernel.hash, kernel_hash, sizeof(ht->kernel.hash));
/* When calling sev_encrypt_flash, the length has to be 16 byte aligned */ /* zero the excess data so the measurement can be reliably calculated */
aligned_len = ROUND_UP(ht->len, 16); memset(padded_ht->padding, 0, sizeof(padded_ht->padding));
if (aligned_len != ht->len) {
/* zero the excess data so the measurement can be reliably calculated */ if (sev_encrypt_flash((uint8_t *)padded_ht, sizeof(*padded_ht), errp) < 0) {
memset(ht->padding, 0, aligned_len - ht->len); ret = false;
} }
if (sev_encrypt_flash((uint8_t *)ht, aligned_len, errp) < 0) { address_space_unmap(&address_space_memory, padded_ht,
return false; mapped_len, true, mapped_len);
}
return true; return ret;
} }
static void static void