代码拉取完成,页面将自动刷新
同步操作将从 src-anolis-os/qemu-kvm 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
From 133fe2e6a250623ccc9305fdacc1e8d990878fb4 Mon Sep 17 00:00:00 2001
From: hanliyang <hanliyang@hygon.cn>
Date: Tue, 7 Jun 2022 15:19:32 +0800
Subject: [PATCH 26/29] anolis: target/i386/csv: Add support for migrate VMSA
for CSV2 guest
CSV2 can protect guest's cpu state through memory encryption. Each
vcpu has its corresponding memory, which is also called VMSA, and
is encrypted by guest's specific encrytion key.
When CSV2 guest exit to host, the vcpu's state will be encrypted
and saved to VMSA, and the VMSA will be decrypted and loaded to cpu
when the guest's vcpu running at next time.
If user wants to migrate one CSV2 guest to target machine, the VMSA
of the vcpus also should be migrated to target. CSV firmware provides
SEND_UPDATE_VMSA/RECEIVE_UPDATE_VMSA API through which VMSA can be
converted into secure data and transmitted to the remote end (for
example, network transmission).
The migration of cpu state is identified by CPUState.cpu_index which
may not equals to vcpu id from KVM's perspective.
When migrate the VMSA, the source QEMU will invoke SEND_UPDATE_VMSA to
generate data correspond to VMSA, after target QEMU received the data,
it will calc target vcpu id in the KVM by CPUState.cpu_index, and then
invoke RECEIVE_UPDATE_VMSA to restore VMSA correspond to vcpu.
Signed-off-by: hanliyang <hanliyang@hygon.cn>
---
include/exec/confidential-guest-support.h | 6 +
linux-headers/linux/kvm.h | 16 ++
migration/ram.c | 30 ++++
target/i386/sev.c | 196 ++++++++++++++++++++++
target/i386/sev.h | 3 +
target/i386/trace-events | 2 +
6 files changed, 253 insertions(+)
diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h
index f6a30fab3..a069bad9d 100644
--- a/include/exec/confidential-guest-support.h
+++ b/include/exec/confidential-guest-support.h
@@ -90,6 +90,12 @@ struct ConfidentialGuestMemoryEncryptionOps {
/* Load the incoming encrypted pages queued in list into guest memory */
int (*load_queued_incoming_pages)(QEMUFile *f);
+
+ /* Write the encrypted cpu state */
+ int (*save_outgoing_cpu_state)(QEMUFile *f);
+
+ /* Load the encrypted cpu state */
+ int (*load_incoming_cpu_state)(QEMUFile *f);
};
typedef struct ConfidentialGuestSupportClass {
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 951afc8b2..e5d282c75 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -1904,6 +1904,14 @@ struct kvm_sev_send_update_data {
__u32 trans_len;
};
+struct kvm_sev_send_update_vmsa {
+ __u32 vcpu_id;
+ __u64 hdr_uaddr;
+ __u32 hdr_len;
+ __u64 trans_uaddr;
+ __u32 trans_len;
+};
+
struct kvm_sev_receive_start {
__u32 handle;
__u32 policy;
@@ -1922,6 +1930,14 @@ struct kvm_sev_receive_update_data {
__u32 trans_len;
};
+struct kvm_sev_receive_update_vmsa {
+ __u32 vcpu_id;
+ __u64 hdr_uaddr;
+ __u32 hdr_len;
+ __u64 trans_uaddr;
+ __u32 trans_len;
+};
+
/* CSV command */
enum csv_cmd_id {
KVM_CSV_NR_MIN = 0xc0,
diff --git a/migration/ram.c b/migration/ram.c
index 90f1bda83..8c61eecb9 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1371,6 +1371,23 @@ static int ram_save_shared_region_list(RAMState *rs, QEMUFile *f)
return ops->save_outgoing_shared_regions_list(rs->f);
}
+/**
+ * ram_save_encrypted_cpu_state: send the encrypted cpu state
+ */
+static int ram_save_encrypted_cpu_state(RAMState *rs, QEMUFile *f)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ ConfidentialGuestSupportClass *cgs_class =
+ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs));
+ struct ConfidentialGuestMemoryEncryptionOps *ops =
+ cgs_class->memory_encryption_ops;
+
+ save_page_header(rs, rs->f, rs->last_seen_block,
+ RAM_SAVE_FLAG_ENCRYPTED_DATA);
+ qemu_put_be32(rs->f, RAM_SAVE_ENCRYPTED_CPU_STATE);
+ return ops->save_outgoing_cpu_state(rs->f);
+}
+
static int load_encrypted_data(QEMUFile *f, uint8_t *ptr)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -1395,6 +1412,8 @@ static int load_encrypted_data(QEMUFile *f, uint8_t *ptr)
return -EINVAL;
}
return ops->load_queued_incoming_pages(f);
+ } else if (flag == RAM_SAVE_ENCRYPTED_CPU_STATE) {
+ return ops->load_incoming_cpu_state(f);
} else {
error_report("unknown encrypted flag %x", flag);
return 1;
@@ -3507,6 +3526,17 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
if (memcrypt_enabled()) {
ret = ram_save_shared_region_list(rs, f);
}
+
+ /*
+ * send the encrypted cpu state, for example, CSV2 guest's
+ * vmsa for each vcpu.
+ */
+ if (!ret && memcrypt_enabled() && is_hygon_cpu()) {
+ ret = ram_save_encrypted_cpu_state(rs, f);
+ if (ret) {
+ error_report("Failed to save encrypted cpu state");
+ }
+ }
}
if (ret < 0) {
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 2f7d98be6..9cdf3e6bf 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -100,6 +100,10 @@ struct SevGuestState {
gchar *send_packet_hdr;
size_t send_packet_hdr_len;
+ /* needed by live migration of HYGON CSV2 guest */
+ gchar *send_vmsa_packet_hdr;
+ size_t send_vmsa_packet_hdr_len;
+
uint32_t reset_cs;
uint32_t reset_ip;
bool reset_data_valid;
@@ -193,6 +197,9 @@ static const char *const sev_fw_errlist[] = {
#define SHARED_REGION_LIST_CONT 0x1
#define SHARED_REGION_LIST_END 0x2
+#define ENCRYPTED_CPU_STATE_CONT 0x1
+#define ENCRYPTED_CPU_STATE_END 0x2
+
static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = {
.save_setup = sev_save_setup,
.save_outgoing_page = sev_save_outgoing_page,
@@ -204,6 +211,8 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = {
.save_queued_outgoing_pages = csv_save_queued_outgoing_pages,
.queue_incoming_page = csv_queue_incoming_page,
.load_queued_incoming_pages = csv_load_queued_incoming_pages,
+ .save_outgoing_cpu_state = csv_save_outgoing_cpu_state,
+ .load_incoming_cpu_state = csv_load_incoming_cpu_state,
};
static int
@@ -1020,6 +1029,9 @@ sev_send_finish(void)
}
g_free(sev_guest->send_packet_hdr);
+ if (sev_es_enabled() && is_hygon_cpu()) {
+ g_free(sev_guest->send_vmsa_packet_hdr);
+ }
sev_set_guest_state(sev_guest, SEV_STATE_RUNNING);
}
@@ -2214,6 +2226,190 @@ int csv_load_queued_incoming_pages(QEMUFile *f)
return csv_receive_update_data_batch(s);
}
+static int
+sev_send_vmsa_get_packet_len(int *fw_err)
+{
+ int ret;
+ struct kvm_sev_send_update_vmsa update = { 0, };
+
+ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_UPDATE_VMSA,
+ &update, fw_err);
+ if (*fw_err != SEV_RET_INVALID_LEN) {
+ ret = -1;
+ error_report("%s: failed to get session length ret=%d fw_error=%d '%s'",
+ __func__, ret, *fw_err, fw_error_to_str(*fw_err));
+ goto err;
+ }
+
+ ret = update.hdr_len;
+
+err:
+ return ret;
+}
+
+static int
+sev_send_update_vmsa(SevGuestState *s, QEMUFile *f, uint32_t cpu_id,
+ uint32_t cpu_index, uint32_t size)
+{
+ int ret, fw_error;
+ guchar *trans = NULL;
+ struct kvm_sev_send_update_vmsa update = {};
+
+ /*
+ * If this is first call then query the packet header bytes and allocate
+ * the packet buffer.
+ */
+ if (!s->send_vmsa_packet_hdr) {
+ s->send_vmsa_packet_hdr_len = sev_send_vmsa_get_packet_len(&fw_error);
+ if (s->send_vmsa_packet_hdr_len < 1) {
+ error_report("%s: SEND_UPDATE_VMSA fw_error=%d '%s'",
+ __func__, fw_error, fw_error_to_str(fw_error));
+ return 1;
+ }
+
+ s->send_vmsa_packet_hdr = g_new(gchar, s->send_vmsa_packet_hdr_len);
+ }
+
+ /* allocate transport buffer */
+ trans = g_new(guchar, size);
+
+ update.vcpu_id = cpu_id;
+ update.hdr_uaddr = (uintptr_t)s->send_vmsa_packet_hdr;
+ update.hdr_len = s->send_vmsa_packet_hdr_len;
+ update.trans_uaddr = (uintptr_t)trans;
+ update.trans_len = size;
+
+ trace_kvm_sev_send_update_vmsa(cpu_id, cpu_index, trans, size);
+
+ ret = sev_ioctl(s->sev_fd, KVM_SEV_SEND_UPDATE_VMSA, &update, &fw_error);
+ if (ret) {
+ error_report("%s: SEND_UPDATE_VMSA ret=%d fw_error=%d '%s'",
+ __func__, ret, fw_error, fw_error_to_str(fw_error));
+ goto err;
+ }
+
+ /*
+ * Migration of vCPU's VMState according to the instance_id
+ * (i.e. CPUState.cpu_index)
+ */
+ qemu_put_be32(f, sizeof(uint32_t));
+ qemu_put_buffer(f, (uint8_t *)&cpu_index, sizeof(uint32_t));
+
+ qemu_put_be32(f, update.hdr_len);
+ qemu_put_buffer(f, (uint8_t *)update.hdr_uaddr, update.hdr_len);
+
+ qemu_put_be32(f, update.trans_len);
+ qemu_put_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len);
+
+err:
+ g_free(trans);
+ return ret;
+}
+
+int csv_save_outgoing_cpu_state(QEMUFile *f)
+{
+ SevGuestState *s = sev_guest;
+ CPUState *cpu;
+ int ret = 0;
+
+ /* Only support migrate VMSAs for HYGON CSV2 guest */
+ if (!sev_es_enabled() || !is_hygon_cpu()) {
+ return 0;
+ }
+
+ CPU_FOREACH(cpu) {
+ qemu_put_be32(f, ENCRYPTED_CPU_STATE_CONT);
+ ret = sev_send_update_vmsa(s, f, kvm_arch_vcpu_id(cpu),
+ cpu->cpu_index, TARGET_PAGE_SIZE);
+ if (ret) {
+ goto err;
+ }
+ }
+
+ qemu_put_be32(f, ENCRYPTED_CPU_STATE_END);
+
+err:
+ return ret;
+}
+
+static int sev_receive_update_vmsa(QEMUFile *f)
+{
+ int ret = 1, fw_error = 0;
+ CPUState *cpu;
+ uint32_t cpu_index, cpu_id = 0;
+ gchar *hdr = NULL, *trans = NULL;
+ struct kvm_sev_receive_update_vmsa update = {};
+
+ /* get cpu index buffer */
+ assert(qemu_get_be32(f) == sizeof(uint32_t));
+ qemu_get_buffer(f, (uint8_t *)&cpu_index, sizeof(uint32_t));
+
+ CPU_FOREACH(cpu) {
+ if (cpu->cpu_index == cpu_index) {
+ cpu_id = kvm_arch_vcpu_id(cpu);
+ break;
+ }
+ }
+ update.vcpu_id = cpu_id;
+
+ /* get packet header */
+ update.hdr_len = qemu_get_be32(f);
+ if (!check_blob_length(update.hdr_len)) {
+ return 1;
+ }
+
+ hdr = g_new(gchar, update.hdr_len);
+ qemu_get_buffer(f, (uint8_t *)hdr, update.hdr_len);
+ update.hdr_uaddr = (uintptr_t)hdr;
+
+ /* get transport buffer */
+ update.trans_len = qemu_get_be32(f);
+ if (!check_blob_length(update.trans_len)) {
+ goto err;
+ }
+
+ trans = g_new(gchar, update.trans_len);
+ update.trans_uaddr = (uintptr_t)trans;
+ qemu_get_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len);
+
+ trace_kvm_sev_receive_update_vmsa(cpu_id, cpu_index,
+ trans, update.trans_len, hdr, update.hdr_len);
+
+ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_RECEIVE_UPDATE_VMSA,
+ &update, &fw_error);
+ if (ret) {
+ error_report("Error RECEIVE_UPDATE_VMSA ret=%d fw_error=%d '%s'",
+ ret, fw_error, fw_error_to_str(fw_error));
+ }
+
+err:
+ g_free(trans);
+ g_free(hdr);
+ return ret;
+}
+
+int csv_load_incoming_cpu_state(QEMUFile *f)
+{
+ int status, ret = 0;
+
+ /* Only support migrate VMSAs for HYGON CSV2 guest */
+ if (!sev_es_enabled() || !is_hygon_cpu()) {
+ return 0;
+ }
+
+ status = qemu_get_be32(f);
+ while (status == ENCRYPTED_CPU_STATE_CONT) {
+ ret = sev_receive_update_vmsa(f);
+ if (ret) {
+ break;
+ }
+
+ status = qemu_get_be32(f);
+ }
+
+ return ret;
+}
+
static const QemuUUID sev_hash_table_header_guid = {
.data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93,
0xd4, 0x11, 0xfd, 0x21)
diff --git a/target/i386/sev.h b/target/i386/sev.h
index 345dffac5..8b38567c3 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -44,6 +44,7 @@ typedef struct SevKernelLoaderContext {
#define RAM_SAVE_ENCRYPTED_PAGE_BATCH 0x4
#define RAM_SAVE_ENCRYPTED_PAGE_BATCH_END 0x5
#define CSV_OUTGOING_PAGE_WINDOW_SIZE (4094 * TARGET_PAGE_SIZE)
+#define RAM_SAVE_ENCRYPTED_CPU_STATE 0x6
#ifdef CONFIG_SEV
bool sev_enabled(void);
@@ -82,6 +83,8 @@ int csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr);
int csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent);
int csv_queue_incoming_page(QEMUFile *f, uint8_t *ptr);
int csv_load_queued_incoming_pages(QEMUFile *f);
+int csv_save_outgoing_cpu_state(QEMUFile *f);
+int csv_load_incoming_cpu_state(QEMUFile *f);
struct sev_ops {
int (*sev_ioctl)(int fd, int cmd, void *data, int *error);
diff --git a/target/i386/trace-events b/target/i386/trace-events
index e32b0319b..60a4609c0 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -17,6 +17,8 @@ kvm_sev_send_finish(void) ""
kvm_sev_receive_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p"
kvm_sev_receive_update_data(void *src, void *dst, int len, void *hdr, int hdr_len) "guest %p trans %p len %d hdr %p hdr_len %d"
kvm_sev_receive_finish(void) ""
+kvm_sev_send_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *dst, int len) "cpu_id %d cpu_index %d trans %p len %d"
+kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int len, void *hdr, int hdr_len) "cpu_id %d cpu_index %d trans %p len %d hdr %p hdr_len %d"
# csv.c
kvm_csv_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIu64
--
2.31.1
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。