Handle region_add events by invoking the MSHV memory registration
ioctl to map guest memory into the hypervisor partition. This allows
the guest to access memory through MSHV-managed mappings.
Note that this assumes the hypervisor will accept regions that overlap
in userspace_addr. Currently that's not the case, it will be addressed
in a later commit in the series.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
---
accel/mshv/mem.c | 127 +++++++++++++++++++++++++++++++++++++++-
accel/mshv/trace-events | 16 +++++
include/system/mshv.h | 11 ++++
3 files changed, 151 insertions(+), 3 deletions(-)
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c
index eddd83ae83..f51e9fee8e 100644
--- a/accel/mshv/mem.c
+++ b/accel/mshv/mem.c
@@ -13,13 +13,134 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "linux/mshv.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
+#include "exec/memattrs.h"
+#include <sys/ioctl.h>
+#include "trace.h"
+
+static int set_guest_memory(int vm_fd, const mshv_user_mem_region *region)
+{
+ int ret;
+
+ ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region);
+ if (ret < 0) {
+ error_report("failed to set guest memory");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map)
+{
+ struct mshv_user_mem_region region = {0};
+
+ region.guest_pfn = mr->guest_phys_addr >> MSHV_PAGE_SHIFT;
+ region.size = mr->memory_size;
+ region.userspace_addr = mr->userspace_addr;
+
+ if (!map) {
+ region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP);
+ trace_mshv_unmap_memory(mr->userspace_addr, mr->guest_phys_addr,
+ mr->memory_size);
+ return set_guest_memory(vm_fd, ®ion);
+ }
+
+ region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE);
+ if (!mr->readonly) {
+ region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE);
+ }
+
+ trace_mshv_map_memory(mr->userspace_addr, mr->guest_phys_addr,
+ mr->memory_size);
+ return set_guest_memory(vm_fd, ®ion);
+}
+
+static int set_memory(const MshvMemoryRegion *mshv_mr, bool add)
+{
+ int ret = 0;
+
+ if (!mshv_mr) {
+ error_report("Invalid mshv_mr");
+ return -1;
+ }
+
+ trace_mshv_set_memory(add, mshv_mr->guest_phys_addr,
+ mshv_mr->memory_size,
+ mshv_mr->userspace_addr, mshv_mr->readonly,
+ ret);
+ return map_or_unmap(mshv_state->vm, mshv_mr, add);
+}
+
+/*
+ * Calculate and align the start address and the size of the section.
+ * Return the size. If the size is 0, the aligned section is empty.
+ */
+static hwaddr align_section(MemoryRegionSection *section, hwaddr *start)
+{
+ hwaddr size = int128_get64(section->size);
+ hwaddr delta, aligned;
+
+ /*
+ * works in page size chunks, but the function may be called
+ * with sub-page size and unaligned start address. Pad the start
+ * address to next and truncate size to previous page boundary.
+ */
+ aligned = ROUND_UP(section->offset_within_address_space,
+ qemu_real_host_page_size());
+ delta = aligned - section->offset_within_address_space;
+ *start = aligned;
+ if (delta > size) {
+ return 0;
+ }
+
+ return (size - delta) & qemu_real_host_page_mask();
+}
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add)
{
- error_report("unimplemented");
- abort();
-}
+ int ret = 0;
+ MemoryRegion *area = section->mr;
+ bool writable = !area->readonly && !area->rom_device;
+ hwaddr start_addr, mr_offset, size;
+ void *ram;
+ MshvMemoryRegion mshv_mr = {0};
+
+ trace_mshv_set_phys_mem(add, section->mr->name);
+
+ /* If the memory device is a writable non-ram area, we do not
+ * want to map it into the guest memory. If it is not a ROM device,
+ * we want to remove mshv memory mapping, so accesses will trap.
+ */
+ if (!memory_region_is_ram(area)) {
+ if (writable) {
+ return;
+ } else if (!area->romd_mode) {
+ add = false;
+ }
+ }
+
+ size = align_section(section, &start_addr);
+ if (!size) {
+ return;
+ }
+ mr_offset = section->offset_within_region + start_addr -
+ section->offset_within_address_space;
+
+ ram = memory_region_get_ram_ptr(area) + mr_offset;
+
+ mshv_mr.guest_phys_addr = start_addr;
+ mshv_mr.memory_size = size;
+ mshv_mr.readonly = !writable;
+ mshv_mr.userspace_addr = (uint64_t)ram;
+
+ ret = set_memory(&mshv_mr, add);
+ if (ret < 0) {
+ error_report("Failed to set memory region");
+ abort();
+ }
+}
diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events
index f99e8c5a41..9a3af6b8be 100644
--- a/accel/mshv/trace-events
+++ b/accel/mshv/trace-events
@@ -1,3 +1,19 @@
# See docs/devel/tracing.rst for syntax documentation.
+mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "[add = %d] gpa = %lx size = %lx user = %lx readonly = %d result = %d"
mshv_hvcall_args(const char* hvcall, uint16_t code, uint16_t in_sz) "built args for '%s' code: %d in_sz: %d"
+
+mshv_set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data) "gsi=%d addr=%lx data=%x"
+mshv_remove_msi_routing(uint32_t gsi) "gsi=%d"
+mshv_add_msi_routing(uint64_t addr, uint32_t data) "addr=%lx data=%x"
+mshv_commit_msi_routing_table(int vm_fd, int len) "vm_fd=%d table_size=%d"
+mshv_register_irqfd(int vm_fd, int event_fd, uint32_t gsi) "vm_fd=%d event_fd=%d gsi=%d"
+mshv_irqchip_update_irqfd_notifier_gsi(int event_fd, int resample_fd, int virq, bool add) "event_fd=%d resample_fd=%d virq=%d add=%d"
+
+mshv_insn_fetch(uint64_t addr, size_t size) "gpa=%lx size=%lu"
+mshv_mem_write(uint64_t addr, size_t size) "\tgpa=%lx size=%lu"
+mshv_mem_read(uint64_t addr, size_t size) "\tgpa=%lx size=%lu"
+mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=%lx gpa=%010lx size=%08lx"
+mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=%lx gpa=%010lx size=%08lx"
+mshv_set_phys_mem(bool add, const char *name) "\tadd=%d name=%s"
+mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=%lx gpa=%010lx size=%lx access_type=%d"
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 2ac594d0aa..3624d9477f 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -30,6 +30,8 @@
#define CONFIG_MSHV_IS_POSSIBLE
#endif
+#define MSHV_PAGE_SHIFT 12
+
#ifdef CONFIG_MSHV_IS_POSSIBLE
extern bool mshv_allowed;
#define mshv_enabled() (mshv_allowed)
@@ -77,6 +79,15 @@ int mshv_arch_post_init_vm(int vm_fd);
int mshv_hvcall(int mshv_fd, const struct mshv_root_hvcall *args);
/* memory */
+typedef struct MshvMemoryRegion {
+ uint64_t guest_phys_addr;
+ uint64_t memory_size;
+ uint64_t userspace_addr;
+ bool readonly;
+} MshvMemoryRegion;
+
+int mshv_add_mem(int vm_fd, const MshvMemoryRegion *mr);
+int mshv_remove_mem(int vm_fd, const MshvMemoryRegion *mr);
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add);
--
2.34.1