qemu/kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch

316 lines
10 KiB
Diff
Raw Normal View History

QEMU update to version 8.2.0-18: - hw/loongarch/virt: Fix FDT memory node address width - hw/loongarch: Fix fdt memory node wrong 'reg' - load_elf: fix iterator's type for elf file processing - migration/colo: Fix bdrv_graph_rdlock_main_loop: Assertion `!qemu_in_… - target/i386: no single-step exception after MOV or POP SS - char-stdio: Restore blocking mode of stdout on exit - backends/cryptodev-builtin: Fix local_error leaks - target/loongarch: fix a wrong print in cpu dump - virtio-pci: fix use of a released vector - target/arm: Disable SVE extensions when SVE is disabled - hw/misc/bcm2835_property: Fix handling of FRAMEBUFFER_SET_PALETTE - target/i386: Introduce SapphireRapids-v3 to add missing features - virtio-net: Ensure queue index fits with RSS (CVE-2024-6505) - nbd/server: CVE-2024-7409: Avoid use-after-free when closing server - update io/trace-events. Parameters should remain consistent. - update docs/tools/virtfs-proxy-helper.rst. This place is spelled wrong. - kvm: Add support for CSV2 reboot - target/i386/kvm: Fix the resettable info when emulate Hygon CSV2 guest - target/i386: get/set/migrate GHCB state - target/i386: csv: Add support for migrate VMSA for CSV2 guest - migration/ram: Accelerate the loading of CSV guest's encrypted pages - migration/ram: Accelerate the transmission of CSV guest's encrypted pages - target/i386: csv: add support to load incoming encrypted pages queued in the CMD list - target/i386: csv: add support to queue the incoming page into a list - target/i386: csv: add support to encrypt the outgoing pages in the list queued before. - target/i386: csv: add support to queue the outgoing page into a list - target/i386: csv: Read cert chain from file when prepared for CSV live migration - target/i386: Introduce header file csv.h - migration/ram: Fix calculation of gfn correpond to a page in ramblock - target/i386: sev: Clear shared_regions_list when reboot CSV Guest - migration/ram: Force encrypted status for VGA vram - target/i386: sev: Return 0 if sev_send_get_packet_len() fails - kvm: Add support for userspace MSR filtering and handling of MSR_KVM_MIGRATION_CONTROL. - migration/ram: Force encrypted status for flash0 & flash1 devices. - migration/ram: add support to send encrypted pages - migration: add support to migrate shared regions list - kvm: Add support for SEV shared regions list and KVM_EXIT_HYPERCALL. - target/i386: sev: add support to load incoming encrypted page - target/i386: sev: add support to encrypt the outgoing page - target/i386: sev: do not create launch context for an incoming guest - target/i386: sev: provide callback to setup outgoing context - confidential guest support: introduce ConfidentialGuestMemoryEncryptionOps for encrypted VMs - migration.json: add AMD SEV specific migration parameters - doc: update AMD SEV to include Live migration flow - crypto/tlscredspsk: Free username on finalize - hw/nvme: fix leak of uninitialized memory in io_mgmt_recv - hw/display/vhost-user-gpu.c: fix vhost_user_gpu_chr_read() - cvm : Implement command blacklist for cvm security enhancement - crypto: Introduce SM3 hash hmac pbkdf algorithm - virtio-net: Use virtual time for RSC timers - vvfat: Fix bug in writing to middle of file - hw/core/ptimer: fix timer zero period condition for freq > 1GHz - hw/misc: support vpsp Signed-off-by: Jiabo Feng <fengjiabo1@huawei.com>
2024-09-18 15:20:53 +08:00
From 02e6bfc88ce5e944ce36b8ccb7d2af103a969980 Mon Sep 17 00:00:00 2001
From: Ashish Kalra <ashish.kalra@amd.com>
Date: Tue, 27 Jul 2021 15:05:49 +0000
Subject: [PATCH] kvm: Add support for SEV shared regions list and
KVM_EXIT_HYPERCALL.
cherry-picked from https://github.com/AMDESE/qemu/commit/fcbbd9b19ac.
KVM_HC_MAP_GPA_RANGE hypercall is used by the SEV guest to notify a
change in the page encryption status to the hypervisor. The hypercall
should be invoked only when the encryption attribute is changed from
encrypted -> decrypted and vice versa. By default all guest pages are
considered encrypted.
The hypercall exits to userspace with KVM_EXIT_HYPERCALL exit code,
currently this is used only by SEV guests for guest page encryptiion
status tracking. Add support to handle this exit and invoke SEV
shared regions list handlers.
Add support for SEV guest shared regions and implementation of the
SEV shared regions list.
Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
[ Fix conflicts. ]
Signed-off-by: hanliyang <hanliyang@hygon.cn>
---
linux-headers/linux/kvm.h | 3 ++
target/i386/kvm/kvm.c | 48 +++++++++++++++++
target/i386/kvm/sev-stub.c | 11 ++++
target/i386/sev.c | 106 +++++++++++++++++++++++++++++++++++++
target/i386/sev.h | 3 ++
5 files changed, 171 insertions(+)
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 8d12435e41..9489a20835 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -348,6 +348,7 @@ struct kvm_run {
} iocsr_io;
/* KVM_EXIT_HYPERCALL */
struct {
+#define KVM_HC_MAP_GPA_RANGE 12
__u64 nr;
__u64 args[6];
__u64 ret;
@@ -1204,6 +1205,8 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_ARM_VIRT_MSI_BYPASS 799
+#define KVM_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE)
+
#ifdef KVM_CAP_IRQ_ROUTING
struct kvm_irq_routing_irqchip {
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index a0bc9ea7b1..82f6d3b048 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -148,6 +148,7 @@ static int has_xcrs;
static int has_sregs2;
static int has_exception_payload;
static int has_triple_fault_event;
+static int has_map_gpa_range;
static bool has_msr_mcg_ext_ctl;
@@ -2191,6 +2192,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10);
}
+ if (sev_enabled()) {
+ c = cpuid_find_entry(&cpuid_data.cpuid,
+ KVM_CPUID_FEATURES | kvm_base, 0);
+ if (c) {
+ c->eax |= (1 << KVM_FEATURE_MIGRATION_CONTROL);
+ if (has_map_gpa_range) {
+ c->eax |= (1 << KVM_FEATURE_HC_MAP_GPA_RANGE);
+ }
+ }
+ }
+
cpuid_data.cpuid.nent = cpuid_i;
cpuid_data.cpuid.padding = 0;
@@ -2584,6 +2596,17 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
#endif
}
+ has_map_gpa_range = kvm_check_extension(s, KVM_CAP_EXIT_HYPERCALL);
+ if (has_map_gpa_range) {
+ ret = kvm_vm_enable_cap(s, KVM_CAP_EXIT_HYPERCALL, 0,
+ KVM_EXIT_HYPERCALL_VALID_MASK);
+ if (ret < 0) {
+ error_report("kvm: Failed to enable MAP_GPA_RANGE cap: %s",
+ strerror(-ret));
+ return ret;
+ }
+ }
+
ret = kvm_get_supported_msrs(s);
if (ret < 0) {
return ret;
@@ -4936,6 +4959,28 @@ static int kvm_handle_tpr_access(X86CPU *cpu)
return 1;
}
+static int kvm_handle_exit_hypercall(X86CPU *cpu, struct kvm_run *run)
+{
+ /*
+ * Currently this exit is only used by SEV guests for
+ * guest page encryption status tracking.
+ */
+ if (run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) {
+ unsigned long enc = run->hypercall.args[2];
+ unsigned long gpa = run->hypercall.args[0];
+ unsigned long npages = run->hypercall.args[1];
+ unsigned long gfn_start = gpa >> TARGET_PAGE_BITS;
+ unsigned long gfn_end = gfn_start + npages;
+
+ if (enc) {
+ sev_remove_shared_regions_list(gfn_start, gfn_end);
+ } else {
+ sev_add_shared_regions_list(gfn_start, gfn_end);
+ }
+ }
+ return 0;
+}
+
int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
{
static const uint8_t int3 = 0xcc;
@@ -5359,6 +5404,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
ret = kvm_xen_handle_exit(cpu, &run->xen);
break;
#endif
+ case KVM_EXIT_HYPERCALL:
+ ret = kvm_handle_exit_hypercall(cpu, run);
+ break;
default:
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
ret = -1;
diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c
index 1be5341e8a..1282d242a7 100644
--- a/target/i386/kvm/sev-stub.c
+++ b/target/i386/kvm/sev-stub.c
@@ -19,3 +19,14 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
/* If we get here, cgs must be some non-SEV thing */
return 0;
}
+
+int sev_remove_shared_regions_list(unsigned long gfn_start,
+ unsigned long gfn_end)
+{
+ return 0;
+}
+
+int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end)
+{
+ return 0;
+}
diff --git a/target/i386/sev.c b/target/i386/sev.c
index de1a4b271e..8525a7351f 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -44,6 +44,11 @@
#define TYPE_SEV_GUEST "sev-guest"
OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
+struct shared_region {
+ unsigned long gfn_start, gfn_end;
+ QTAILQ_ENTRY(shared_region) list;
+};
+
/**
* SevGuestState:
@@ -87,6 +92,8 @@ struct SevGuestState {
uint32_t reset_cs;
uint32_t reset_ip;
bool reset_data_valid;
+
+ QTAILQ_HEAD(, shared_region) shared_regions_list;
};
#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
@@ -1136,6 +1143,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
migration_add_notifier(&sev_migration_state, sev_migration_state_notifier);
cgs_class->memory_encryption_ops = &sev_memory_encryption_ops;
+ QTAILQ_INIT(&sev->shared_regions_list);
cgs->ready = true;
@@ -1671,6 +1679,104 @@ int sev_load_incoming_page(QEMUFile *f, uint8_t *ptr)
return sev_receive_update_data(f, ptr);
}
+int sev_remove_shared_regions_list(unsigned long start, unsigned long end)
+{
+ SevGuestState *s = sev_guest;
+ struct shared_region *pos;
+
+ QTAILQ_FOREACH(pos, &s->shared_regions_list, list) {
+ unsigned long l, r;
+ unsigned long curr_gfn_end = pos->gfn_end;
+
+ /*
+ * Find if any intersection exists ?
+ * left bound for intersecting segment
+ */
+ l = MAX(start, pos->gfn_start);
+ /* right bound for intersecting segment */
+ r = MIN(end, pos->gfn_end);
+ if (l <= r) {
+ if (pos->gfn_start == l && pos->gfn_end == r) {
+ QTAILQ_REMOVE(&s->shared_regions_list, pos, list);
+ } else if (l == pos->gfn_start) {
+ pos->gfn_start = r;
+ } else if (r == pos->gfn_end) {
+ pos->gfn_end = l;
+ } else {
+ /* Do a de-merge -- split linked list nodes */
+ struct shared_region *shrd_region;
+
+ pos->gfn_end = l;
+ shrd_region = g_malloc0(sizeof(*shrd_region));
+ if (!shrd_region) {
+ return 0;
+ }
+ shrd_region->gfn_start = r;
+ shrd_region->gfn_end = curr_gfn_end;
+ QTAILQ_INSERT_AFTER(&s->shared_regions_list, pos,
+ shrd_region, list);
+ }
+ }
+ if (end <= curr_gfn_end) {
+ break;
+ }
+ }
+ return 0;
+}
+
+int sev_add_shared_regions_list(unsigned long start, unsigned long end)
+{
+ struct shared_region *shrd_region;
+ struct shared_region *pos;
+ SevGuestState *s = sev_guest;
+
+ if (QTAILQ_EMPTY(&s->shared_regions_list)) {
+ shrd_region = g_malloc0(sizeof(*shrd_region));
+ if (!shrd_region) {
+ return -1;
+ }
+ shrd_region->gfn_start = start;
+ shrd_region->gfn_end = end;
+ QTAILQ_INSERT_TAIL(&s->shared_regions_list, shrd_region, list);
+ return 0;
+ }
+
+ /*
+ * shared regions list is a sorted list in ascending order
+ * of guest PA's and also merges consecutive range of guest PA's
+ */
+ QTAILQ_FOREACH(pos, &s->shared_regions_list, list) {
+ /* handle duplicate overlapping regions */
+ if (start >= pos->gfn_start && end <= pos->gfn_end) {
+ return 0;
+ }
+ if (pos->gfn_end < start) {
+ continue;
+ }
+ /* merge consecutive guest PA(s) -- forward merge */
+ if (pos->gfn_start <= start && pos->gfn_end >= start) {
+ pos->gfn_end = end;
+ return 0;
+ }
+ break;
+ }
+ /*
+ * Add a new node
+ */
+ shrd_region = g_malloc0(sizeof(*shrd_region));
+ if (!shrd_region) {
+ return -1;
+ }
+ shrd_region->gfn_start = start;
+ shrd_region->gfn_end = end;
+ if (pos) {
+ QTAILQ_INSERT_BEFORE(pos, shrd_region, list);
+ } else {
+ QTAILQ_INSERT_TAIL(&s->shared_regions_list, shrd_region, list);
+ }
+ return 1;
+}
+
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 d94da2956b..acf69d4e6f 100644
--- a/target/i386/sev.h
+++ b/target/i386/sev.h
@@ -61,6 +61,9 @@ int sev_inject_launch_secret(const char *hdr, const char *secret,
int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size);
void sev_es_set_reset_vector(CPUState *cpu);
+int sev_remove_shared_regions_list(unsigned long gfn_start,
+ unsigned long gfn_end);
+int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end);
int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp);
--
2.41.0.windows.1