@@ -94,6 +94,26 @@ static const struct hvm_io_handler null_handler = {
.ops = &null_ops
};
+static int ioreq_server_read(const struct hvm_io_handler *io_handler,
+ uint64_t addr,
+ uint32_t size,
+ uint64_t *data)
+{
+ if ( hvm_copy_from_guest_phys(data, addr, size) != HVMCOPY_okay )
+ return X86EMUL_UNHANDLEABLE;
+
+ return X86EMUL_OKAY;
+}
+
+static const struct hvm_io_ops ioreq_server_ops = {
+ .read = ioreq_server_read,
+ .write = null_write
+};
+
+static const struct hvm_io_handler ioreq_server_handler = {
+ .ops = &ioreq_server_ops
+};
+
static int hvmemul_do_io(
bool_t is_mmio, paddr_t addr, unsigned long *reps, unsigned int size,
uint8_t dir, bool_t df, bool_t data_is_addr, uintptr_t data)
@@ -193,6 +213,9 @@ static int hvmemul_do_io(
* - If the domain ioreq_server is NULL, assume there is a
* race between the unbinding of ioreq server and guest fault
* so re-try the instruction.
+ *
+ * - If the accesss is a read, this could be part of a
+ * read-modify-write instruction, emulate the read first.
*/
struct hvm_ioreq_server *s = NULL;
p2m_type_t p2mt = p2m_invalid;
@@ -226,6 +249,17 @@ static int hvmemul_do_io(
vio->io_req.state = STATE_IOREQ_NONE;
break;
}
+
+ /*
+ * This is part of a read-modify-write instruction.
+ * Emulate the read part so we have the value cached.
+ */
+ if ( dir == IOREQ_READ )
+ {
+ rc = hvm_process_io_intercept(&ioreq_server_handler, &p);
+ vio->io_req.state = STATE_IOREQ_NONE;
+ break;
+ }
}
}