EHCI supports 64-bit control data structure addressing when the 64-bit Addressing Capability bit in HCCPARAMS is set. In that mode, the CTRLDSSEGMENT register provides the upper 32 bits that are concatenated with 32-bit link pointer values to form full 64-bit descriptor addresses (EHCI 1.0, section 2.3.5 and Appendix B).
iTD link pointers are stored as 32-bit values and must be expanded to full 64-bit descriptor addresses when 64-bit mode is enabled. Update the iTD traversal path to use ehci_get_desc_addr() when following link pointers. Appendix B also defines high dword fields for iTD buffer pointers. Add bufptr_hi[7] to EHCIitd and use ehci_get_buf_addr() to construct full 64-bit buffer addresses from bufptr[] and bufptr_hi[] fields when processing isochronous transfers. This allows buffers above 4GB to be handled correctly. When 64-bit capability is disabled, descriptor and buffer addresses remain 32-bit and existing behaviour is unchanged. Signed-off-by: Jamin Lin <[email protected]> Reviewed-by: Philippe Mathieu-Daudé <[email protected]> --- hw/usb/hcd-ehci.h | 1 + hw/usb/hcd-ehci.c | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 7fed206906..49176f3fd2 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -63,6 +63,7 @@ typedef struct EHCIitd { #define ITD_BUFPTR_MAXPKT_SH 0 #define ITD_BUFPTR_MULT_MASK 0x00000003 #define ITD_BUFPTR_MULT_SH 0 + uint32_t bufptr_hi[7]; } EHCIitd; /* diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 947af86ac4..1a286c32e6 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -105,6 +105,7 @@ typedef enum { */ #define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t)) #define EHCI_QTD_DWORDS_32 (offsetof(EHCIqtd, bufptr_hi) / sizeof(uint32_t)) +#define EHCI_ITD_DWORDS_32 (offsetof(EHCIitd, bufptr_hi) / sizeof(uint32_t)) static const char *ehci_state_names[] = { [EST_INACTIVE] = "INACTIVE", @@ -184,6 +185,11 @@ static uint32_t ehci_qtd_dwords(const EHCIState *s) return s->caps_64bit_addr ? (sizeof(EHCIqtd) >> 2) : EHCI_QTD_DWORDS_32; } +static uint32_t ehci_itd_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIitd) >> 2) : EHCI_ITD_DWORDS_32; +} + static void ehci_trace_usbsts(uint32_t mask, int state) { /* interrupts */ @@ -1486,7 +1492,8 @@ static int ehci_process_itd(EHCIState *ehci, return -1; } - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); + ptr1 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg], + itd->bufptr[pg], ITD_BUFPTR_MASK); qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); if (off + len > 4096) { /* transfer crosses page border */ @@ -1494,7 +1501,9 @@ static int ehci_process_itd(EHCIState *ehci, qemu_sglist_destroy(&ehci->isgl); return -1; /* avoid page pg + 1 */ } - ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK); + ptr2 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg + 1], + itd->bufptr[pg + 1], + ITD_BUFPTR_MASK); uint32_t len2 = off + len - 4096; uint32_t len1 = len - len2; qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); @@ -1774,8 +1783,9 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) assert(!async); entry = ehci_get_fetch_addr(ehci, async); + memset(&itd, 0, sizeof(itd)); if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2) < 0) { + ehci_itd_dwords(ehci)) < 0) { return -1; } ehci_trace_itd(ehci, entry, &itd); @@ -1785,8 +1795,8 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) } put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); - ehci_set_fetch_addr(ehci, async, itd.next); + ehci_itd_dwords(ehci)); + ehci_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, itd.next)); ehci_set_state(ehci, async, EST_FETCHENTRY); return 1; -- 2.43.0
