summaryrefslogtreecommitdiff
path: root/hw/usb
diff options
context:
space:
mode:
authorWei Yang <weiyang@linux.vnet.ibm.com>2012-03-08 12:10:44 +1100
committerGerd Hoffmann <kraxel@redhat.com>2012-03-13 10:15:32 +0100
commit86e18cae209f8101ff9a6013a1ff6ae620c06944 (patch)
treed77d552348fe1b467b945de52c817252e6c28f15 /hw/usb
parentfcb70eca3f14349a0ef8e74dc808360f81592026 (diff)
downloadqemu-86e18cae209f8101ff9a6013a1ff6ae620c06944.tar.gz
qemu-86e18cae209f8101ff9a6013a1ff6ae620c06944.tar.bz2
qemu-86e18cae209f8101ff9a6013a1ff6ae620c06944.zip
usb-ohci: DMA writeback bug fixes
This patch fixes two bugs in the OHCI device where the device writes back data to system memory that should be exclusively under the control of the guest side driver. In OHCI specification Section 5.2.7, it mentioned "In all cases, Host Controller Driver is responsible for the insertion and removal of all Endpoint Descriptors in the various Host Controller Endpoint Descriptor lists". In the ohci_frame_boundary(), ohci_put_hcca() writes the entire hcca back including the interrupt ED lists which should be under driver control. This violates the specification and can race with a host driver updating that list at the same time. In the OHCI Spec Section 4.6, Transfer Descriptor Queue Processing, it mentioned "Since the TD pointed to by TailP is not accessed by the HC, the Host Controller Driver can initialize that TD and link at least one other to it without creating a coherency or synchronization problem". While the function ohci_put_ed() writes the entire endpoint descriptor back including the TailP which should under driver control. This violate the specification and can race with a host driver updating the TD list at the same time. In each case the solution is to make sure we don't write data which is under driver control. Cc: Gerd Hoffman <kraxel@redhat.com> Signed-off-by: Wei Yang <weiyang@linux.vnet.ibm.com> Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb')
-rw-r--r--hw/usb/hcd-ohci.c17
1 files changed, 15 insertions, 2 deletions
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 51fa111390..1a1cc88b1f 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -121,6 +121,11 @@ struct ohci_hcca {
uint16_t frame, pad;
uint32_t done;
};
+#define HCCA_WRITEBACK_OFFSET offsetof(struct ohci_hcca, frame)
+#define HCCA_WRITEBACK_SIZE 8 /* frame, pad, done */
+
+#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head)
+#define ED_WBACK_SIZE 4
static void ohci_bus_stop(OHCIState *ohci);
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
@@ -568,7 +573,13 @@ static inline int ohci_read_hcca(OHCIState *ohci,
static inline int ohci_put_ed(OHCIState *ohci,
uint32_t addr, struct ohci_ed *ed)
{
- return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+ /* ed->tail is under control of the HCD.
+ * Since just ed->head is changed by HC, just write back this
+ */
+
+ return put_dwords(ohci, addr + ED_WBACK_OFFSET,
+ (uint32_t *)((char *)ed + ED_WBACK_OFFSET),
+ ED_WBACK_SIZE >> 2);
}
static inline int ohci_put_td(OHCIState *ohci,
@@ -587,7 +598,9 @@ static inline int ohci_put_iso_td(OHCIState *ohci,
static inline int ohci_put_hcca(OHCIState *ohci,
uint32_t addr, struct ohci_hcca *hcca)
{
- cpu_physical_memory_write(addr + ohci->localmem_base, hcca, sizeof(*hcca));
+ cpu_physical_memory_write(addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET,
+ (char *)hcca + HCCA_WRITEBACK_OFFSET,
+ HCCA_WRITEBACK_SIZE);
return 1;
}