@@ -23,9 +23,21 @@
#define SEV_POLICY_DOMAIN 0x10
#define SEV_POLICY_SEV 0x20
+typedef struct SevKernelLoaderContext {
+ char *setup_data;
+ size_t setup_size;
+ char *kernel_data;
+ size_t kernel_size;
+ char *initrd_data;
+ size_t initrd_size;
+ char *cmdline_data;
+ size_t cmdline_size;
+} SevKernelLoaderContext;
+
int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp);
int sev_inject_launch_secret(const char *hdr, const char *secret,
uint64_t gpa, Error **errp);
+bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp);
int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size);
void sev_es_set_reset_vector(CPUState *cpu);
@@ -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"
@@ -86,6 +87,32 @@ typedef struct __attribute__((__packed__)) SevInfoBlock {
uint32_t reset_addr;
} SevInfoBlock;
+#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454"
+typedef struct QEMU_PACKED 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 QEMU_PACKED SevHashTableEntry {
+ QemuUUID guid;
+ uint16_t len;
+ uint8_t hash[HASH_SIZE];
+} SevHashTableEntry;
+
+typedef struct QEMU_PACKED SevHashTable {
+ QemuUUID guid;
+ uint16_t len;
+ SevHashTableEntry cmdline;
+ SevHashTableEntry initrd;
+ SevHashTableEntry kernel;
+ uint8_t padding[];
+} SevHashTable;
+
static SevGuestState *sev_guest;
static Error *sev_mig_blocker;
@@ -1151,6 +1178,117 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size)
return 0;
}
+static const QemuUUID sev_hash_table_header_guid = {
+ .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93,
+ 0xd4, 0x11, 0xfd, 0x21)
+};
+
+static const QemuUUID sev_kernel_entry_guid = {
+ .data = UUID_LE(0x4de79437, 0xabd2, 0x427f, 0xb8, 0x35, 0xd5, 0xb1,
+ 0x72, 0xd2, 0x04, 0x5b)
+};
+static const QemuUUID sev_initrd_entry_guid = {
+ .data = UUID_LE(0x44baf731, 0x3a2f, 0x4bd7, 0x9a, 0xf1, 0x41, 0xe2,
+ 0x91, 0x69, 0x78, 0x1d)
+};
+static const QemuUUID sev_cmdline_entry_guid = {
+ .data = UUID_LE(0x97d02dd8, 0xbd20, 0x4c94, 0xaa, 0x78, 0xe7, 0x71,
+ 0x4d, 0x36, 0xab, 0x2a)
+};
+
+/*
+ * 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(SevKernelLoaderContext *ctx, Error **errp)
+{
+ uint8_t *data;
+ SevHashTableDescriptor *area;
+ SevHashTable *ht;
+ uint8_t cmdline_hash[HASH_SIZE];
+ uint8_t initrd_hash[HASH_SIZE];
+ uint8_t kernel_hash[HASH_SIZE];
+ uint8_t *hashp;
+ size_t hash_len = HASH_SIZE;
+ int aligned_len;
+
+ 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");
+ return false;
+ }
+ area = (SevHashTableDescriptor *)data;
+
+ /*
+ * Calculate hash of kernel command-line with the terminating null byte. If
+ * the user doesn't supply a command-line via -append, the 1-byte "\0" will
+ * be used.
+ */
+ hashp = cmdline_hash;
+ if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->cmdline_data,
+ ctx->cmdline_size, &hashp, &hash_len, errp) < 0) {
+ return false;
+ }
+ assert(hash_len == HASH_SIZE);
+
+ /*
+ * Calculate hash of initrd. If the user doesn't supply an initrd via
+ * -initrd, an empty buffer will be used (ctx->initrd_size == 0).
+ */
+ hashp = initrd_hash;
+ if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->initrd_data,
+ ctx->initrd_size, &hashp, &hash_len, errp) < 0) {
+ return false;
+ }
+ assert(hash_len == HASH_SIZE);
+
+ /* Calculate hash of the kernel */
+ hashp = kernel_hash;
+ 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, ARRAY_SIZE(iov),
+ &hashp, &hash_len, errp) < 0) {
+ return false;
+ }
+ assert(hash_len == HASH_SIZE);
+
+ /*
+ * Populate the hashes table in the guest's memory at the OVMF-designated
+ * area for the SEV hashes table
+ */
+ ht = qemu_map_ram_ptr(NULL, area->base);
+
+ ht->guid = sev_hash_table_header_guid;
+ ht->len = sizeof(*ht);
+
+ ht->cmdline.guid = sev_cmdline_entry_guid;
+ ht->cmdline.len = sizeof(ht->cmdline);
+ memcpy(ht->cmdline.hash, cmdline_hash, sizeof(ht->cmdline.hash));
+
+ ht->initrd.guid = sev_initrd_entry_guid;
+ ht->initrd.len = sizeof(ht->initrd);
+ memcpy(ht->initrd.hash, initrd_hash, sizeof(ht->initrd.hash));
+
+ ht->kernel.guid = sev_kernel_entry_guid;
+ ht->kernel.len = sizeof(ht->kernel);
+ memcpy(ht->kernel.hash, kernel_hash, sizeof(ht->kernel.hash));
+
+ /* When calling sev_encrypt_flash, the length 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->padding, 0, aligned_len - ht->len);
+ }
+
+ if (sev_encrypt_flash((uint8_t *)ht, aligned_len, errp) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
static void
sev_register_types(void)
{