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 <magnusku...@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