aboutsummaryrefslogtreecommitdiff
path: root/hw/xen/xen_pt_config_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xen/xen_pt_config_init.c')
-rw-r--r--hw/xen/xen_pt_config_init.c59
1 files changed, 58 insertions, 1 deletions
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index a75baea870..b1ad743754 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -1893,6 +1893,10 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
reg_entry->reg = reg;
if (reg->init) {
+ uint32_t host_mask, size_mask;
+ unsigned int offset;
+ uint32_t val;
+
/* initialize emulate register */
rc = reg->init(s, reg_entry->reg,
reg_grp->base_offset + reg->offset, &data);
@@ -1905,8 +1909,61 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
g_free(reg_entry);
return 0;
}
+ /* Sync up the data to dev.config */
+ offset = reg_grp->base_offset + reg->offset;
+ size_mask = 0xFFFFFFFF >> ((4 - reg->size) << 3);
+
+ switch (reg->size) {
+ case 1: rc = xen_host_pci_get_byte(&s->real_device, offset, (uint8_t *)&val);
+ break;
+ case 2: rc = xen_host_pci_get_word(&s->real_device, offset, (uint16_t *)&val);
+ break;
+ case 4: rc = xen_host_pci_get_long(&s->real_device, offset, &val);
+ break;
+ default: assert(1);
+ }
+ if (rc) {
+ /* Serious issues when we cannot read the host values! */
+ g_free(reg_entry);
+ return rc;
+ }
+ /* Set bits in emu_mask are the ones we emulate. The dev.config shall
+ * contain the emulated view of the guest - therefore we flip the mask
+ * to mask out the host values (which dev.config initially has) . */
+ host_mask = size_mask & ~reg->emu_mask;
+
+ if ((data & host_mask) != (val & host_mask)) {
+ uint32_t new_val;
+
+ /* Mask out host (including past size). */
+ new_val = val & host_mask;
+ /* Merge emulated ones (excluding the non-emulated ones). */
+ new_val |= data & host_mask;
+ /* Leave intact host and emulated values past the size - even though
+ * we do not care as we write per reg->size granularity, but for the
+ * logging below lets have the proper value. */
+ new_val |= ((val | data)) & ~size_mask;
+ XEN_PT_LOG(&s->dev,"Offset 0x%04x mismatch! Emulated=0x%04x, host=0x%04x, syncing to 0x%04x.\n",
+ offset, data, val, new_val);
+ val = new_val;
+ } else
+ val = data;
+
+ /* This could be just pci_set_long as we don't modify the bits
+ * past reg->size, but in case this routine is run in parallel
+ * we do not want to over-write other registers. */
+ switch (reg->size) {
+ case 1: pci_set_byte(s->dev.config + offset, (uint8_t)val);
+ break;
+ case 2: pci_set_word(s->dev.config + offset, (uint16_t)val);
+ break;
+ case 4: pci_set_long(s->dev.config + offset, val);
+ break;
+ default: assert(1);
+ }
/* set register value */
- reg_entry->data = data;
+ reg_entry->data = val;
+
}
/* list add register entry */
QLIST_INSERT_HEAD(&reg_grp->reg_tbl_list, reg_entry, entries);