qemu/target-i386-csv-Add-support-to-migrate-the-outgoing-.patch

453 lines
14 KiB
Diff
Raw Permalink Normal View History

QEMU update to version 8.2.0-26: - vdpa-dev: Fix initialisation order to restore VDUSE compatibility - tcg: Allow top bit of SIMD_DATA_BITS to be set in simd_desc() - migration: fix-possible-int-overflow - target/m68k: Map FPU exceptions to FPSR register - qemu-options: Fix CXL Fixed Memory Window interleave-granularity typo - hvf: arm: Fix encodings for ID_AA64PFR1_EL1 and debug System registers - hw/intc/arm_gic: Fix handling of NS view of GICC_APR<n> - qio: Inherit follow_coroutine_ctx across TLS - target/riscv: Fix the element agnostic function problem - accel/tcg: Fix typo causing tb->page_addr[1] to not be recorded - tcg/loongarch64: Fix tcg_out_movi vs some pcrel pointers - migration: Fix file migration with fdset - ui/vnc: don't return an empty SASL mechlist to the client - target/arm: Fix FJCVTZS vs flush-to-zero - hw/ppc/e500: Prefer QOM cast - sphinx/qapidoc: Fix to generate doc for explicit, unboxed arguments - hw/ppc/e500: Remove unused "irqs" parameter - hw/ppc/e500: Add missing device tree properties to i2c controller node - hw/i386/amd_iommu: Don't leak memory in amdvi_update_iotlb() - hw/arm/mps2-tz.c: fix RX/TX interrupts order - target/i386: csv: Add support to migrate the incoming context for CSV3 guest - target/i386: csv: Add support to migrate the outgoing context for CSV3 guest - target/i386: csv: Add support to migrate the incoming page for CSV3 guest - target/i386: csv: Add support to migrate the outgoing page for CSV3 guest - linux-headers: update kernel headers to include CSV3 migration cmds - vfio: Only map shared region for CSV3 virtual machine - vga: Force full update for CSV3 guest - target/i386: csv: Load initial image to private memory for CSV3 guest - target/i386: csv: Do not register/unregister guest secure memory for CSV3 guest - target/i386: cpu: Populate CPUID 0x8000_001F when CSV3 is active - target/i386: csv: Add command to load vmcb to CSV3 guest memory - target/i386: csv: Add command to load data to CSV3 guest memory - target/i386: csv: Add command to initialize CSV3 context - target/i386: csv: Add CSV3 context - next-kbd: convert to use qemu_input_handler_register() - qemu/bswap: Undefine CPU_CONVERT() once done - exec/memop: Remove unused memop_big_endian() helper - hw/nvme: fix handling of over-committed queues - 9pfs: fix crash on 'Treaddir' request - hw/misc/psp: Pin the hugepage memory specified by mem2 during use for psp - hw/misc: support tkm use mem2 memory - hw/i386: add mem2 option for qemu - kvm: add support for guest physical bits - target/i386: add guest-phys-bits cpu property Signed-off-by: Jiabo Feng <fengjiabo1@huawei.com> (cherry picked from commit f45f35e88509a4ffa9f62332ee9601e9fe1f8d09)
2024-12-12 17:01:35 +08:00
From 13bd2629b78f528b0b4684a643f59d30b7274aa8 Mon Sep 17 00:00:00 2001
From: jiangxin <jiangxin@hygon.cn>
Date: Fri, 17 Jun 2022 09:37:56 +0800
Subject: [PATCH] target/i386: csv: Add support to migrate the outgoing page
for CSV3 guest
The csv3_send_encrypt_data() provides the method to encrypt the
guest's private pages during migration. The routine is similar to
CSV2's. Usually, it starts with a SEND_START command to create the
migration context. Then SEND_ENCRYPT_DATA command is performed to
encrypt guest pages. After migration is completed, a SEND_FINISH
command is performed to the firmware.
Signed-off-by: Jiang Xin <jiangxin@hygon.cn>
Signed-off-by: hanliyang <hanliyang@hygon.cn>
---
migration/ram.c | 87 +++++++++++++++++++
target/i386/csv.c | 182 +++++++++++++++++++++++++++++++++++++++
target/i386/csv.h | 22 +++++
target/i386/sev.c | 14 ++-
target/i386/sev.h | 1 +
target/i386/trace-events | 1 +
6 files changed, 306 insertions(+), 1 deletion(-)
diff --git a/migration/ram.c b/migration/ram.c
index 1377b9eb37..1f9348fd06 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -2480,6 +2480,90 @@ ram_save_encrypted_pages_in_batch(RAMState *rs, PageSearchStatus *pss)
}
#endif
+/**
+ * ram_save_csv3_pages - send the given csv3 VM pages to the stream
+ */
+static int ram_save_csv3_pages(RAMState *rs, PageSearchStatus *pss)
+{
+ bool page_dirty;
+ int ret;
+ int tmppages, pages = 0;
+ uint8_t *p;
+ uint32_t host_len = 0;
+ uint64_t bytes_xmit = 0;
+ RAMBlock *block = pss->block;
+ ram_addr_t offset = 0;
+ hwaddr paddr = RAM_ADDR_INVALID;
+ MachineState *ms = MACHINE(qdev_get_machine());
+ ConfidentialGuestSupportClass *cgs_class =
+ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs));
+ struct ConfidentialGuestMemoryEncryptionOps *ops =
+ cgs_class->memory_encryption_ops;
+
+ if (!kvm_csv3_enabled())
+ return 0;
+
+ do {
+ page_dirty = migration_bitmap_clear_dirty(rs, block, pss->page);
+
+ /* Check the pages is dirty and if it is send it */
+ if (page_dirty) {
+ ret = kvm_physical_memory_addr_from_host(kvm_state,
+ block->host + (pss->page << TARGET_PAGE_BITS), &paddr);
+ /* Process ROM or MMIO */
+ if (paddr == RAM_ADDR_INVALID ||
+ memory_region_is_rom(block->mr)) {
+ tmppages = migration_ops->ram_save_target_page(rs, pss);
+ } else {
+ /* Caculate the offset and host virtual address of the page */
+ offset = pss->page << TARGET_PAGE_BITS;
+ p = block->host + offset;
+
+ if (ops->queue_outgoing_page(p, TARGET_PAGE_SIZE, offset))
+ return -1;
+
+ tmppages = 1;
+ host_len += TARGET_PAGE_SIZE;
+
+ stat64_add(&mig_stats.normal_pages, 1);
+ }
+ } else {
+ tmppages = 0;
+ }
+
+ if (tmppages >= 0) {
+ pages += tmppages;
+ } else {
+ return tmppages;
+ }
+
+ pss_find_next_dirty(pss);
+ } while (offset_in_ramblock(block,
+ ((ram_addr_t)pss->page) << TARGET_PAGE_BITS) &&
+ host_len < CSV3_OUTGOING_PAGE_WINDOW_SIZE);
+
+ /* Check if there are any queued pages */
+ if (host_len != 0) {
+ /* Always set offset as 0 for csv3. */
+ ram_transferred_add(save_page_header(pss, pss->pss_channel,
+ block, 0 | RAM_SAVE_FLAG_ENCRYPTED_DATA));
+
+ qemu_put_be32(pss->pss_channel, RAM_SAVE_ENCRYPTED_PAGE);
+ ram_transferred_add(4);
+ /* Process the queued pages in batch */
+ ret = ops->save_queued_outgoing_pages(pss->pss_channel, &bytes_xmit);
+ if (ret) {
+ return -1;
+ }
+ ram_transferred_add(bytes_xmit);
+ }
+
+ /* The offset we leave with is the last one we looked at */
+ pss->page--;
+
+ return pages;
+}
+
/**
* ram_save_host_page: save a whole host page
*
@@ -2515,6 +2599,9 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss)
return 0;
}
+ if (kvm_csv3_enabled())
+ return ram_save_csv3_pages(rs, pss);
+
#ifdef CONFIG_HYGON_CSV_MIG_ACCEL
/*
* If command_batch function is enabled and memory encryption is enabled
diff --git a/target/i386/csv.c b/target/i386/csv.c
index e4706efa27..22e709a95c 100644
--- a/target/i386/csv.c
+++ b/target/i386/csv.c
@@ -16,8 +16,13 @@
#include "qapi/error.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
+#include "migration/blocker.h"
+#include "migration/qemu-file.h"
+#include "migration/misc.h"
+#include "monitor/monitor.h"
#include <linux/kvm.h>
+#include <linux/psp-sev.h>
#ifdef CONFIG_NUMA
#include <numaif.h>
@@ -30,6 +35,19 @@
bool csv_kvm_cpu_reset_inhibit;
+struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops = {
+ .save_setup = sev_save_setup,
+ .save_outgoing_page = NULL,
+ .is_gfn_in_unshared_region = NULL,
+ .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list,
+ .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list,
+ .queue_outgoing_page = csv3_queue_outgoing_page,
+ .save_queued_outgoing_pages = csv3_save_queued_outgoing_pages,
+};
+
+#define CSV3_OUTGOING_PAGE_NUM \
+ (CSV3_OUTGOING_PAGE_WINDOW_SIZE / TARGET_PAGE_SIZE)
+
Csv3GuestState csv3_guest = { 0 };
int
@@ -70,6 +88,7 @@ csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops)
csv3_guest.fw_error_to_str = ops->fw_error_to_str;
QTAILQ_INIT(&csv3_guest.dma_map_regions_list);
qemu_mutex_init(&csv3_guest.dma_map_regions_list_mutex);
+ csv3_guest.sev_send_start = ops->sev_send_start;
}
return 0;
}
@@ -301,3 +320,166 @@ end:
qemu_mutex_unlock(&s->dma_map_regions_list_mutex);
return;
}
+
+static inline hwaddr csv3_hva_to_gfn(uint8_t *ptr)
+{
+ ram_addr_t offset = RAM_ADDR_INVALID;
+
+ kvm_physical_memory_addr_from_host(kvm_state, ptr, &offset);
+
+ return offset >> TARGET_PAGE_BITS;
+}
+
+static int
+csv3_send_start(QEMUFile *f, uint64_t *bytes_sent)
+{
+ if (csv3_guest.sev_send_start)
+ return csv3_guest.sev_send_start(f, bytes_sent);
+ else
+ return -1;
+}
+
+static int
+csv3_send_get_packet_len(int *fw_err)
+{
+ int ret;
+ struct kvm_csv3_send_encrypt_data update = {0};
+
+ update.hdr_len = 0;
+ update.trans_len = 0;
+ ret = csv3_ioctl(KVM_CSV3_SEND_ENCRYPT_DATA, &update, fw_err);
+ if (*fw_err != SEV_RET_INVALID_LEN) {
+ error_report("%s: failed to get session length ret=%d fw_error=%d '%s'",
+ __func__, ret, *fw_err, fw_error_to_str(*fw_err));
+ ret = 0;
+ goto err;
+ }
+
+ if (update.hdr_len <= INT_MAX)
+ ret = update.hdr_len;
+ else
+ ret = 0;
+
+err:
+ return ret;
+}
+
+static int
+csv3_send_encrypt_data(Csv3GuestState *s, QEMUFile *f,
+ uint8_t *ptr, uint32_t size, uint64_t *bytes_sent)
+{
+ int ret, fw_error = 0;
+ guchar *trans;
+ uint32_t guest_addr_entry_num;
+ uint32_t i;
+ struct kvm_csv3_send_encrypt_data update = { };
+
+ /*
+ * If this is first call then query the packet header bytes and allocate
+ * the packet buffer.
+ */
+ if (!s->send_packet_hdr) {
+ s->send_packet_hdr_len = csv3_send_get_packet_len(&fw_error);
+ if (s->send_packet_hdr_len < 1) {
+ error_report("%s: SEND_UPDATE fw_error=%d '%s'",
+ __func__, fw_error, fw_error_to_str(fw_error));
+ return 1;
+ }
+
+ s->send_packet_hdr = g_new(gchar, s->send_packet_hdr_len);
+ }
+
+ if (!s->guest_addr_len || !s->guest_addr_data) {
+ error_report("%s: invalid host address or size", __func__);
+ return 1;
+ } else {
+ guest_addr_entry_num = s->guest_addr_len / sizeof(struct guest_addr_entry);
+ }
+
+ /* allocate transport buffer */
+ trans = g_new(guchar, guest_addr_entry_num * TARGET_PAGE_SIZE);
+
+ update.hdr_uaddr = (uintptr_t)s->send_packet_hdr;
+ update.hdr_len = s->send_packet_hdr_len;
+ update.guest_addr_data = (uintptr_t)s->guest_addr_data;
+ update.guest_addr_len = s->guest_addr_len;
+ update.trans_uaddr = (uintptr_t)trans;
+ update.trans_len = guest_addr_entry_num * TARGET_PAGE_SIZE;
+
+ trace_kvm_csv3_send_encrypt_data(trans, update.trans_len);
+
+ ret = csv3_ioctl(KVM_CSV3_SEND_ENCRYPT_DATA, &update, &fw_error);
+ if (ret) {
+ error_report("%s: SEND_ENCRYPT_DATA ret=%d fw_error=%d '%s'",
+ __func__, ret, fw_error, fw_error_to_str(fw_error));
+ goto err;
+ }
+
+ for (i = 0; i < guest_addr_entry_num; i++) {
+ if (s->guest_addr_data[i].share)
+ memcpy(trans + i * TARGET_PAGE_SIZE, (guchar *)s->guest_hva_data[i].hva,
+ TARGET_PAGE_SIZE);
+ }
+
+ qemu_put_be32(f, update.hdr_len);
+ qemu_put_buffer(f, (uint8_t *)update.hdr_uaddr, update.hdr_len);
+ *bytes_sent += 4 + update.hdr_len;
+
+ qemu_put_be32(f, update.guest_addr_len);
+ qemu_put_buffer(f, (uint8_t *)update.guest_addr_data, update.guest_addr_len);
+ *bytes_sent += 4 + update.guest_addr_len;
+
+ qemu_put_be32(f, update.trans_len);
+ qemu_put_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len);
+ *bytes_sent += (4 + update.trans_len);
+
+err:
+ s->guest_addr_len = 0;
+ g_free(trans);
+ return ret;
+}
+
+int
+csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr)
+{
+ Csv3GuestState *s = &csv3_guest;
+ uint32_t i = 0;
+
+ if (!s->guest_addr_data) {
+ s->guest_hva_data = g_new0(struct guest_hva_entry, CSV3_OUTGOING_PAGE_NUM);
+ s->guest_addr_data = g_new0(struct guest_addr_entry, CSV3_OUTGOING_PAGE_NUM);
+ s->guest_addr_len = 0;
+ }
+
+ if (s->guest_addr_len >= sizeof(struct guest_addr_entry) * CSV3_OUTGOING_PAGE_NUM) {
+ error_report("Failed to queue outgoing page");
+ return 1;
+ }
+
+ i = s->guest_addr_len / sizeof(struct guest_addr_entry);
+ s->guest_hva_data[i].hva = (uintptr_t)ptr;
+ s->guest_addr_data[i].share = 0;
+ s->guest_addr_data[i].reserved = 0;
+ s->guest_addr_data[i].gfn = csv3_hva_to_gfn(ptr);
+ s->guest_addr_len += sizeof(struct guest_addr_entry);
+
+ return 0;
+}
+
+int
+csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent)
+{
+ Csv3GuestState *s = &csv3_guest;
+
+ /*
+ * If this is a first buffer then create outgoing encryption context
+ * and write our PDH, policy and session data.
+ */
+ if (!csv3_check_state(SEV_STATE_SEND_UPDATE) &&
+ csv3_send_start(f, bytes_sent)) {
+ error_report("Failed to create outgoing context");
+ return 1;
+ }
+
+ return csv3_send_encrypt_data(s, f, NULL, 0, bytes_sent);
+}
diff --git a/target/i386/csv.h b/target/i386/csv.h
index 12733341b3..12c1b22659 100644
--- a/target/i386/csv.h
+++ b/target/i386/csv.h
@@ -81,6 +81,18 @@ struct dma_map_region {
QTAILQ_ENTRY(dma_map_region) list;
};
+#define CSV3_OUTGOING_PAGE_WINDOW_SIZE (512 * TARGET_PAGE_SIZE)
+
+struct guest_addr_entry {
+ uint64_t share: 1;
+ uint64_t reserved: 11;
+ uint64_t gfn: 52;
+};
+
+struct guest_hva_entry {
+ uint64_t hva;
+};
+
struct Csv3GuestState {
uint32_t policy;
int sev_fd;
@@ -89,11 +101,19 @@ struct Csv3GuestState {
const char *(*fw_error_to_str)(int code);
QTAILQ_HEAD(, dma_map_region) dma_map_regions_list;
QemuMutex dma_map_regions_list_mutex;
+ gchar *send_packet_hdr;
+ size_t send_packet_hdr_len;
+ struct guest_hva_entry *guest_hva_data;
+ struct guest_addr_entry *guest_addr_data;
+ size_t guest_addr_len;
+
+ int (*sev_send_start)(QEMUFile *f, uint64_t *bytes_sent);
};
typedef struct Csv3GuestState Csv3GuestState;
extern struct Csv3GuestState csv3_guest;
+extern struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops;
extern int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops);
extern int csv3_launch_encrypt_vmcb(void);
@@ -101,5 +121,7 @@ int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp);
int csv3_shared_region_dma_map(uint64_t start, uint64_t end);
void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end);
+int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr);
+int csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent);
#endif
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 0012a5efb0..5a96b0b452 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -1270,7 +1270,11 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
qemu_add_vm_change_state_handler(sev_vm_state_change, sev);
migration_add_notifier(&sev_migration_state, sev_migration_state_notifier);
- cgs_class->memory_encryption_ops = &sev_memory_encryption_ops;
+ if (csv3_enabled()) {
+ cgs_class->memory_encryption_ops = &csv3_memory_encryption_ops;
+ } else {
+ cgs_class->memory_encryption_ops = &sev_memory_encryption_ops;
+ }
QTAILQ_INIT(&sev->shared_regions_list);
/* Determine whether support MSR_AMD64_SEV_ES_GHCB */
@@ -2654,9 +2658,17 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp)
return ret;
}
+static int _sev_send_start(QEMUFile *f, uint64_t *bytes_sent)
+{
+ SevGuestState *s = sev_guest;
+
+ return sev_send_start(s, f, bytes_sent);
+}
+
struct sev_ops sev_ops = {
.sev_ioctl = sev_ioctl,
.fw_error_to_str = fw_error_to_str,
+ .sev_send_start = _sev_send_start,
};
static void
diff --git a/target/i386/sev.h b/target/i386/sev.h
index e91431e0f7..8ccef22a95 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -83,6 +83,7 @@ extern bool sev_kvm_has_msr_ghcb;
struct sev_ops {
int (*sev_ioctl)(int fd, int cmd, void *data, int *error);
const char *(*fw_error_to_str)(int code);
+ int (*sev_send_start)(QEMUFile *f, uint64_t *bytes_sent);
};
extern struct sev_ops sev_ops;
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 34c205ffda..a4a58b12a1 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -22,3 +22,4 @@ kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int
# csv.c
kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIx64
+kvm_csv3_send_encrypt_data(void *dst, int len) "trans %p len %d"
--
2.41.0.windows.1