@samitouri / QOSamiQemu / commits / d027e3c63c

hw/usb/hcd-ehci: Implement 64-bit iTD descriptor addressing

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 <jamin_lin@aspeedtech.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Jul 13, 2026 at 03:27 UTC d027e3c63c60b0c6daf93ccfbd127bb60526ab0d
2 files changed +16 -5
hw/usb/hcd-ehci.c
+15 -5
@@ -105,6 +105,7 @@ typedef enum {
105 */
106 #define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t))
107 #define EHCI_QTD_DWORDS_32 (offsetof(EHCIqtd, bufptr_hi) / sizeof(uint32_t))
108 +#define EHCI_ITD_DWORDS_32 (offsetof(EHCIitd, bufptr_hi) / sizeof(uint32_t))
109
110 static const char *ehci_state_names[] = {
111 [EST_INACTIVE] = "INACTIVE",
@@ -184,6 +185,11 @@ static uint32_t ehci_qtd_dwords(const EHCIState *s)
185 return s->caps_64bit_addr ? (sizeof(EHCIqtd) >> 2) : EHCI_QTD_DWORDS_32;
186 }
187
188 +static uint32_t ehci_itd_dwords(const EHCIState *s)
189 +{
190 + return s->caps_64bit_addr ? (sizeof(EHCIitd) >> 2) : EHCI_ITD_DWORDS_32;
191 +}
192 +
193 static void ehci_trace_usbsts(uint32_t mask, int state)
194 {
195 /* interrupts */
@@ -1486,7 +1492,8 @@ static int ehci_process_itd(EHCIState *ehci,
1492 return -1;
1493 }
1494
1489 - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
1495 + ptr1 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg],
1496 + itd->bufptr[pg], ITD_BUFPTR_MASK);
1497 qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as);
1498 if (off + len > 4096) {
1499 /* transfer crosses page border */
@@ -1494,7 +1501,9 @@ static int ehci_process_itd(EHCIState *ehci,
1501 qemu_sglist_destroy(&ehci->isgl);
1502 return -1; /* avoid page pg + 1 */
1503 }
1497 - ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK);
1504 + ptr2 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg + 1],
1505 + itd->bufptr[pg + 1],
1506 + ITD_BUFPTR_MASK);
1507 uint32_t len2 = off + len - 4096;
1508 uint32_t len1 = len - len2;
1509 qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
@@ -1774,8 +1783,9 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
1783 assert(!async);
1784 entry = ehci_get_fetch_addr(ehci, async);
1785
1786 + memset(&itd, 0, sizeof(itd));
1787 if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
1778 - sizeof(EHCIitd) >> 2) < 0) {
1788 + ehci_itd_dwords(ehci)) < 0) {
1789 return -1;
1790 }
1791 ehci_trace_itd(ehci, entry, &itd);
@@ -1785,8 +1795,8 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
1795 }
1796
1797 put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd,
1788 - sizeof(EHCIitd) >> 2);
1789 - ehci_set_fetch_addr(ehci, async, itd.next);
1798 + ehci_itd_dwords(ehci));
1799 + ehci_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, itd.next));
1800 ehci_set_state(ehci, async, EST_FETCHENTRY);
1801
1802 return 1;
hw/usb/hcd-ehci.h
+1
@@ -63,6 +63,7 @@ typedef struct EHCIitd {
63 #define ITD_BUFPTR_MAXPKT_SH 0
64 #define ITD_BUFPTR_MULT_MASK 0x00000003
65 #define ITD_BUFPTR_MULT_SH 0
66 + uint32_t bufptr_hi[7];
67 } EHCIitd;
68
69 /*