qemu/hw/remote/proxy-memory-listener.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2018, 2021 Oracle and/or its affiliates.
   3 *
   4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   5 * See the COPYING file in the top-level directory.
   6 *
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "qemu-common.h"
  11
  12#include "qemu/compiler.h"
  13#include "qemu/int128.h"
  14#include "qemu/range.h"
  15#include "exec/memory.h"
  16#include "exec/cpu-common.h"
  17#include "cpu.h"
  18#include "exec/ram_addr.h"
  19#include "exec/address-spaces.h"
  20#include "qapi/error.h"
  21#include "hw/remote/mpqemu-link.h"
  22#include "hw/remote/proxy-memory-listener.h"
  23
  24/*
  25 * TODO: get_fd_from_hostaddr(), proxy_mrs_can_merge() and
  26 * proxy_memory_listener_commit() defined below perform tasks similar to the
  27 * functions defined in vhost-user.c. These functions are good candidates
  28 * for refactoring.
  29 *
  30 */
  31
  32static void proxy_memory_listener_reset(MemoryListener *listener)
  33{
  34    ProxyMemoryListener *proxy_listener = container_of(listener,
  35                                                       ProxyMemoryListener,
  36                                                       listener);
  37    int mrs;
  38
  39    for (mrs = 0; mrs < proxy_listener->n_mr_sections; mrs++) {
  40        memory_region_unref(proxy_listener->mr_sections[mrs].mr);
  41    }
  42
  43    g_free(proxy_listener->mr_sections);
  44    proxy_listener->mr_sections = NULL;
  45    proxy_listener->n_mr_sections = 0;
  46}
  47
  48static int get_fd_from_hostaddr(uint64_t host, ram_addr_t *offset)
  49{
  50    MemoryRegion *mr;
  51    ram_addr_t off;
  52
  53    /**
  54     * Assumes that the host address is a valid address as it's
  55     * coming from the MemoryListener system. In the case host
  56     * address is not valid, the following call would return
  57     * the default subregion of "system_memory" region, and
  58     * not NULL. So it's not possible to check for NULL here.
  59     */
  60    mr = memory_region_from_host((void *)(uintptr_t)host, &off);
  61
  62    if (offset) {
  63        *offset = off;
  64    }
  65
  66    return memory_region_get_fd(mr);
  67}
  68
  69static bool proxy_mrs_can_merge(uint64_t host, uint64_t prev_host, size_t size)
  70{
  71    if (((prev_host + size) != host)) {
  72        return false;
  73    }
  74
  75    if (get_fd_from_hostaddr(host, NULL) !=
  76            get_fd_from_hostaddr(prev_host, NULL)) {
  77        return false;
  78    }
  79
  80    return true;
  81}
  82
  83static bool try_merge(ProxyMemoryListener *proxy_listener,
  84                      MemoryRegionSection *section)
  85{
  86    uint64_t mrs_size, mrs_gpa, mrs_page;
  87    MemoryRegionSection *prev_sec;
  88    bool merged = false;
  89    uintptr_t mrs_host;
  90    RAMBlock *mrs_rb;
  91
  92    if (!proxy_listener->n_mr_sections) {
  93        return false;
  94    }
  95
  96    mrs_rb = section->mr->ram_block;
  97    mrs_page = (uint64_t)qemu_ram_pagesize(mrs_rb);
  98    mrs_size = int128_get64(section->size);
  99    mrs_gpa = section->offset_within_address_space;
 100    mrs_host = (uintptr_t)memory_region_get_ram_ptr(section->mr) +
 101               section->offset_within_region;
 102
 103    if (get_fd_from_hostaddr(mrs_host, NULL) < 0) {
 104        return true;
 105    }
 106
 107    mrs_host = mrs_host & ~(mrs_page - 1);
 108    mrs_gpa = mrs_gpa & ~(mrs_page - 1);
 109    mrs_size = ROUND_UP(mrs_size, mrs_page);
 110
 111    prev_sec = proxy_listener->mr_sections +
 112               (proxy_listener->n_mr_sections - 1);
 113    uint64_t prev_gpa_start = prev_sec->offset_within_address_space;
 114    uint64_t prev_size = int128_get64(prev_sec->size);
 115    uint64_t prev_gpa_end   = range_get_last(prev_gpa_start, prev_size);
 116    uint64_t prev_host_start =
 117        (uintptr_t)memory_region_get_ram_ptr(prev_sec->mr) +
 118        prev_sec->offset_within_region;
 119    uint64_t prev_host_end = range_get_last(prev_host_start, prev_size);
 120
 121    if (mrs_gpa <= (prev_gpa_end + 1)) {
 122        g_assert(mrs_gpa > prev_gpa_start);
 123
 124        if ((section->mr == prev_sec->mr) &&
 125            proxy_mrs_can_merge(mrs_host, prev_host_start,
 126                                (mrs_gpa - prev_gpa_start))) {
 127            uint64_t max_end = MAX(prev_host_end, mrs_host + mrs_size);
 128            merged = true;
 129            prev_sec->offset_within_address_space =
 130                MIN(prev_gpa_start, mrs_gpa);
 131            prev_sec->offset_within_region =
 132                MIN(prev_host_start, mrs_host) -
 133                (uintptr_t)memory_region_get_ram_ptr(prev_sec->mr);
 134            prev_sec->size = int128_make64(max_end - MIN(prev_host_start,
 135                                                         mrs_host));
 136        }
 137    }
 138
 139    return merged;
 140}
 141
 142static void proxy_memory_listener_region_addnop(MemoryListener *listener,
 143                                                MemoryRegionSection *section)
 144{
 145    ProxyMemoryListener *proxy_listener = container_of(listener,
 146                                                       ProxyMemoryListener,
 147                                                       listener);
 148
 149    if (!memory_region_is_ram(section->mr) ||
 150            memory_region_is_rom(section->mr)) {
 151        return;
 152    }
 153
 154    if (try_merge(proxy_listener, section)) {
 155        return;
 156    }
 157
 158    ++proxy_listener->n_mr_sections;
 159    proxy_listener->mr_sections = g_renew(MemoryRegionSection,
 160                                          proxy_listener->mr_sections,
 161                                          proxy_listener->n_mr_sections);
 162    proxy_listener->mr_sections[proxy_listener->n_mr_sections - 1] = *section;
 163    proxy_listener->mr_sections[proxy_listener->n_mr_sections - 1].fv = NULL;
 164    memory_region_ref(section->mr);
 165}
 166
 167static void proxy_memory_listener_commit(MemoryListener *listener)
 168{
 169    ProxyMemoryListener *proxy_listener = container_of(listener,
 170                                                       ProxyMemoryListener,
 171                                                       listener);
 172    MPQemuMsg msg;
 173    MemoryRegionSection *section;
 174    ram_addr_t offset;
 175    uintptr_t host_addr;
 176    int region;
 177    Error *local_err = NULL;
 178
 179    memset(&msg, 0, sizeof(MPQemuMsg));
 180
 181    msg.cmd = MPQEMU_CMD_SYNC_SYSMEM;
 182    msg.num_fds = proxy_listener->n_mr_sections;
 183    msg.size = sizeof(SyncSysmemMsg);
 184    if (msg.num_fds > REMOTE_MAX_FDS) {
 185        error_report("Number of fds is more than %d", REMOTE_MAX_FDS);
 186        return;
 187    }
 188
 189    for (region = 0; region < proxy_listener->n_mr_sections; region++) {
 190        section = &proxy_listener->mr_sections[region];
 191        msg.data.sync_sysmem.gpas[region] =
 192            section->offset_within_address_space;
 193        msg.data.sync_sysmem.sizes[region] = int128_get64(section->size);
 194        host_addr = (uintptr_t)memory_region_get_ram_ptr(section->mr) +
 195                    section->offset_within_region;
 196        msg.fds[region] = get_fd_from_hostaddr(host_addr, &offset);
 197        msg.data.sync_sysmem.offsets[region] = offset;
 198    }
 199    if (!mpqemu_msg_send(&msg, proxy_listener->ioc, &local_err)) {
 200        error_report_err(local_err);
 201    }
 202}
 203
 204void proxy_memory_listener_deconfigure(ProxyMemoryListener *proxy_listener)
 205{
 206    memory_listener_unregister(&proxy_listener->listener);
 207
 208    proxy_memory_listener_reset(&proxy_listener->listener);
 209}
 210
 211void proxy_memory_listener_configure(ProxyMemoryListener *proxy_listener,
 212                                     QIOChannel *ioc)
 213{
 214    proxy_listener->n_mr_sections = 0;
 215    proxy_listener->mr_sections = NULL;
 216
 217    proxy_listener->ioc = ioc;
 218
 219    proxy_listener->listener.begin = proxy_memory_listener_reset;
 220    proxy_listener->listener.commit = proxy_memory_listener_commit;
 221    proxy_listener->listener.region_add = proxy_memory_listener_region_addnop;
 222    proxy_listener->listener.region_nop = proxy_memory_listener_region_addnop;
 223    proxy_listener->listener.priority = 10;
 224
 225    memory_listener_register(&proxy_listener->listener,
 226                             &address_space_memory);
 227}
 228