summaryrefslogtreecommitdiff
path: root/hw/hyperv
diff options
context:
space:
mode:
authorRoman Kagan <rkagan@virtuozzo.com>2018-09-21 11:22:11 +0300
committerPaolo Bonzini <pbonzini@redhat.com>2018-10-19 13:44:14 +0200
commit267e071bd6d675c15e7ffbf8aaf44d488ebd5c83 (patch)
treecee516311465fd6a9167a13dd2769e547e8911ea /hw/hyperv
parent9b4cf107b09d18ac30f46fd1c4de8585ccba030c (diff)
hyperv: make overlay pages for SynIC
Per Hyper-V spec, SynIC message and event flag pages are to be implemented as so called overlay pages. That is, they are owned by the hypervisor and, when mapped into the guest physical address space, overlay the guest physical pages such that 1) the overlaid guest page becomes invisible to the guest CPUs until the overlay page is turned off 2) the contents of the overlay page is preserved when it's turned off and back on, even at a different address; it's only zeroed at vcpu reset This particular nature of SynIC message and event flag pages is ignored in the current code, and guest physical pages are used directly instead. This happens to (mostly) work because the actual guests seem not to depend on the features listed above. This patch implements those pages as the spec mandates. Since the extra RAM regions, which introduce migration incompatibility, are only added at SynIC object creation which only happens when hyperv_synic_kvm_only == false, no extra compat logic is necessary. Signed-off-by: Roman Kagan <rkagan@virtuozzo.com> Message-Id: <20180921082217.29481-5-rkagan@virtuozzo.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/hyperv')
-rw-r--r--hw/hyperv/hyperv.c51
1 files changed, 48 insertions, 3 deletions
diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c
index 3d6f044282..70cf129d04 100644
--- a/hw/hyperv/hyperv.c
+++ b/hw/hyperv/hyperv.c
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qapi/error.h"
+#include "exec/address-spaces.h"
#include "sysemu/kvm.h"
#include "hw/hyperv/hyperv.h"
@@ -21,6 +22,10 @@ typedef struct SynICState {
bool enabled;
hwaddr msg_page_addr;
hwaddr event_page_addr;
+ MemoryRegion msg_page_mr;
+ MemoryRegion event_page_mr;
+ struct hyperv_message_page *msg_page;
+ struct hyperv_event_flags_page *event_page;
} SynICState;
#define TYPE_SYNIC "hyperv-synic"
@@ -36,8 +41,28 @@ static void synic_update(SynICState *synic, bool enable,
{
synic->enabled = enable;
- synic->msg_page_addr = msg_page_addr;
- synic->event_page_addr = event_page_addr;
+ if (synic->msg_page_addr != msg_page_addr) {
+ if (synic->msg_page_addr) {
+ memory_region_del_subregion(get_system_memory(),
+ &synic->msg_page_mr);
+ }
+ if (msg_page_addr) {
+ memory_region_add_subregion(get_system_memory(), msg_page_addr,
+ &synic->msg_page_mr);
+ }
+ synic->msg_page_addr = msg_page_addr;
+ }
+ if (synic->event_page_addr != event_page_addr) {
+ if (synic->event_page_addr) {
+ memory_region_del_subregion(get_system_memory(),
+ &synic->event_page_mr);
+ }
+ if (event_page_addr) {
+ memory_region_add_subregion(get_system_memory(), event_page_addr,
+ &synic->event_page_mr);
+ }
+ synic->event_page_addr = event_page_addr;
+ }
}
void hyperv_synic_update(CPUState *cs, bool enable,
@@ -54,11 +79,31 @@ void hyperv_synic_update(CPUState *cs, bool enable,
static void synic_realize(DeviceState *dev, Error **errp)
{
+ Object *obj = OBJECT(dev);
+ SynICState *synic = SYNIC(dev);
+ char *msgp_name, *eventp_name;
+ uint32_t vp_index;
+
+ /* memory region names have to be globally unique */
+ vp_index = hyperv_vp_index(synic->cs);
+ msgp_name = g_strdup_printf("synic-%u-msg-page", vp_index);
+ eventp_name = g_strdup_printf("synic-%u-event-page", vp_index);
+
+ memory_region_init_ram(&synic->msg_page_mr, obj, msgp_name,
+ sizeof(*synic->msg_page), &error_abort);
+ memory_region_init_ram(&synic->event_page_mr, obj, eventp_name,
+ sizeof(*synic->event_page), &error_abort);
+ synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr);
+ synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr);
+
+ g_free(msgp_name);
+ g_free(eventp_name);
}
-
static void synic_reset(DeviceState *dev)
{
SynICState *synic = SYNIC(dev);
+ memset(synic->msg_page, 0, sizeof(*synic->msg_page));
+ memset(synic->event_page, 0, sizeof(*synic->event_page));
synic_update(synic, false, 0, 0);
}