@samitouri / QOSamiQemu / commits / 0cb43fdc0b

hw/usb/hcd-ehci: Implement 64-bit QH 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 supplies the upper 32 bits which are concatenated with 32-bit link pointer fields to form full 64-bit descriptor addresses (EHCI 1.0, section 2.3.5 and Appendix B). The current implementation assumes 32-bit QH descriptor addresses and directly uses link pointer values without applying the CTRLDSSEGMENT upper dword. Introduce a helper, ehci_get_desc_addr(), to construct full 64-bit descriptor addresses when 64-bit capability is enabled. Update QH traversal paths (async list walk, horizontal QH link, and periodic schedule entry handling) to use the translated 64-bit addresses. EHCI 64-bit buffer pointer fields are defined in Appendix B as split 32-bit low/high parts located at separate offsets, rather than a single contiguous 64-bit field. Therefore, the buffer pointers cannot be represented as uint64_t bufptr[5] without violating the descriptor layout defined by the specification. Introduce ehci_get_buf_addr() to construct full 64-bit buffer addresses from bufptr[] and bufptr_hi[] fields. Use this helper when calculating transfer buffer addresses so that data buffers above 4GB are correctly handled. Also add bufptr_hi[5] to EHCIqh to support 64-bit buffer pointer fields as defined in Appendix B. When 64-bit capability is disabled, descriptor addresses remain 32-bit and existing behaviour is unchanged. Note: Similar split 64-bit buffer pointer handling is required for qTD, iTD and siTD descriptors, which will be addressed in follow-up changes. 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-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Jul 13, 2026 at 03:27 UTC 0cb43fdc0b2f2fb7f1c914d734ac32d9b91a5bbe
3 files changed +62 -16
hw/usb/hcd-ehci.c
+57 -15
@@ -96,6 +96,15 @@ typedef enum {
96 *data = val; \
97 } while (0)
98
99 +/*
100 + * EHCIqh / EHCIqtd / EHCIitd are sized to always include the extended
101 + * high buffer pointer fields from EHCI 1.0 Appendix B. When 64-bit
102 + * addressing capability is not advertised to the guest, the descriptors
103 + * in guest memory only have the classic 32-bit layout, so DMA transfers
104 + * must not read or write past that boundary.
105 + */
106 +#define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t))
107 +
108 static const char *ehci_state_names[] = {
109 [EST_INACTIVE] = "INACTIVE",
110 [EST_ACTIVE] = "ACTIVE",
@@ -147,6 +156,28 @@ static const char *addr2str(hwaddr addr)
156 return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
157 }
158
159 +static uint64_t ehci_get_buf_addr(const EHCIState *s, uint32_t hi,
160 + uint32_t lo, uint32_t lo_mask)
161 +{
162 + uint64_t addr = lo & lo_mask;
163 +
164 + if (s->caps_64bit_addr) {
165 + addr = deposit64(addr, 32, 32, hi);
166 + }
167 +
168 + return addr;
169 +}
170 +
171 +static uint64_t ehci_get_desc_addr(const EHCIState *s, uint32_t lo)
172 +{
173 + return ehci_get_buf_addr(s, s->ctrldssegment, lo, UINT32_MAX);
174 +}
175 +
176 +static uint32_t ehci_qh_dwords(const EHCIState *s)
177 +{
178 + return s->caps_64bit_addr ? (sizeof(EHCIqh) >> 2) : EHCI_QH_DWORDS_32;
179 +}
180 +
181 static void ehci_trace_usbsts(uint32_t mask, int state)
182 {
183 /* interrupts */
@@ -440,7 +471,7 @@ static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh)
471 (qh->current_qtd != q->qh.current_qtd) ||
472 (q->async && qh->next_qtd != q->qh.next_qtd) ||
473 (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd,
443 - 7 * sizeof(uint32_t)) != 0) ||
474 + EHCI_QH_OVERLAY_COUNT * sizeof(uint32_t)) != 0) ||
475 (q->dev != NULL && q->dev->addr != devaddr)) {
476 return false;
477 } else {
@@ -487,8 +518,9 @@ static void ehci_writeback_async_complete_packet(EHCIPacket *p)
518 int state;
519
520 /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */
521 + memset(&qh, 0, sizeof(qh));
522 get_dwords(q->ehci, NLPTR_GET(q->qhaddr),
491 - (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
523 + (uint32_t *) &qh, ehci_qh_dwords(q->ehci));
524 get_dwords(q->ehci, NLPTR_GET(q->qtdaddr),
525 (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
526 if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) {
@@ -1143,7 +1175,7 @@ static void ehci_opreg_write(void *ptr, hwaddr addr,
1175 static void ehci_flush_qh(EHCIQueue *q)
1176 {
1177 uint32_t *qh = (uint32_t *) &q->qh;
1146 - uint32_t dwords = sizeof(EHCIqh) >> 2;
1178 + uint32_t dwords = ehci_qh_dwords(q->ehci);
1179 uint64_t addr = NLPTR_GET(q->qhaddr);
1180
1181 put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
@@ -1538,7 +1570,9 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async)
1570 EHCIqh qh;
1571 int i = 0;
1572 int again = 0;
1541 - uint64_t entry = ehci->asynclistaddr;
1573 + uint64_t entry = 0;
1574 +
1575 + entry = ehci_get_desc_addr(ehci, ehci->asynclistaddr);
1576
1577 /* set reclamation flag at start event (4.8.6) */
1578 if (async) {
@@ -1548,9 +1582,10 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async)
1582 ehci_queues_rip_unused(ehci, async);
1583
1584 /* Find the head of the list (4.9.1.1) */
1585 + memset(&qh, 0, sizeof(qh));
1586 for (i = 0; i < MAX_QH; i++) {
1587 if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh,
1553 - sizeof(EHCIqh) >> 2) < 0) {
1588 + ehci_qh_dwords(ehci)) < 0) {
1589 return 0;
1590 }
1591 ehci_trace_qh(NULL, NLPTR_GET(entry), &qh);
@@ -1566,8 +1601,8 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async)
1601 goto out;
1602 }
1603
1569 - entry = qh.next;
1570 - if (entry == ehci->asynclistaddr) {
1604 + entry = ehci_get_desc_addr(ehci, qh.next);
1605 + if (entry == ehci_get_desc_addr(ehci, ehci->asynclistaddr)) {
1606 break;
1607 }
1608 }
@@ -1651,8 +1686,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
1686 goto out;
1687 }
1688
1689 + memset(&qh, 0, sizeof(qh));
1690 if (get_dwords(ehci, NLPTR_GET(q->qhaddr),
1655 - (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) {
1691 + (uint32_t *) &qh, ehci_qh_dwords(ehci)) < 0) {
1692 q = NULL;
1693 goto out;
1694 }
@@ -1693,7 +1729,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
1729 }
1730
1731 if (trace_event_get_state_backends(TRACE_USB_EHCI_FETCHQH_DBG)) {
1696 - if (q->qhaddr != q->qh.next) {
1732 + if (q->qhaddr != ehci_get_desc_addr(ehci, q->qh.next)) {
1733 trace_usb_ehci_fetchqh_dbg(q->qhaddr,
1734 q->qh.epchar & QH_EPCHAR_H,
1735 q->qh.token & QTD_TOKEN_HALT,
@@ -1876,10 +1912,12 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
1912
1913 static int ehci_state_horizqh(EHCIQueue *q)
1914 {
1915 + uint64_t addr;
1916 int again = 0;
1917
1881 - if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) {
1882 - ehci_set_fetch_addr(q->ehci, q->async, q->qh.next);
1918 + addr = ehci_get_desc_addr(q->ehci, q->qh.next);
1919 + if (ehci_get_fetch_addr(q->ehci, q->async) != addr) {
1920 + ehci_set_fetch_addr(q->ehci, q->async, addr);
1921 ehci_set_state(q->ehci, q->async, EST_FETCHENTRY);
1922 again = 1;
1923 } else {
@@ -2205,6 +2243,8 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
2243 uint32_t entry;
2244 uint32_t list;
2245 const int async = 0;
2246 + uint64_t entry64;
2247 + uint64_t list64;
2248
2249 /* 4.6 */
2250
@@ -2229,12 +2269,14 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
2269 break;
2270 }
2271 list |= ((ehci->frindex & 0x1ff8) >> 1);
2232 -
2233 - if (get_dwords(ehci, list, &entry, 1) < 0) {
2272 + list64 = ehci_get_desc_addr(ehci, list);
2273 + if (get_dwords(ehci, list64, &entry, 1) < 0) {
2274 break;
2275 }
2236 - trace_usb_ehci_periodic_state_advance(ehci->frindex / 8, list, entry);
2237 - ehci_set_fetch_addr(ehci, async, entry);
2276 + entry64 = ehci_get_desc_addr(ehci, entry);
2277 + trace_usb_ehci_periodic_state_advance(ehci->frindex / 8,
2278 + list64, entry64);
2279 + ehci_set_fetch_addr(ehci, async, entry64);
2280 ehci_set_state(ehci, async, EST_FETCHENTRY);
2281 ehci_advance_state(ehci, async);
2282 ehci_queues_rip_unused(ehci, async);
hw/usb/hcd-ehci.h
+4
@@ -141,6 +141,9 @@ typedef struct EHCIqtd {
141 #define QTD_BUFPTR_SH 12
142 } EHCIqtd;
143
144 +/* QH overlay: altnext_qtd, token, bufptr[5], bufptr_hi[5] */
145 +#define EHCI_QH_OVERLAY_COUNT 12
146 +
147 /*
148 * EHCI spec version 1.0 Section 3.6
149 */
@@ -194,6 +197,7 @@ typedef struct EHCIqh {
197 #define BUFPTR_FRAMETAG_MASK 0x0000001f
198 #define BUFPTR_SBYTES_MASK 0x00000fe0
199 #define BUFPTR_SBYTES_SH 5
200 + uint32_t bufptr_hi[5];
201 } EHCIqh;
202
203 enum async_state {
hw/usb/trace-events
+1 -1
@@ -113,7 +113,7 @@ usb_ehci_dma_error(void) ""
113 usb_ehci_execute_complete(uint64_t qhaddr, uint32_t next, uint64_t qtdaddr, int status, int actual_length) "qhaddr=0x%" PRIx64 ", next=0x%x, qtdaddr=0x%" PRIx64 ", status=%d, actual_length=%d"
114 usb_ehci_fetchqh_reclaim_done(uint64_t qhaddr) "QH 0x%" PRIx64 " H-bit set, reclamation status reset - done processing"
115 usb_ehci_fetchqh_dbg(uint64_t qhaddr, uint32_t h, uint32_t halt, uint32_t active, uint32_t next) "QH 0x%" PRIx64 " (h 0x%x halt 0x%x active 0x%x) next 0x%08x"
116 -usb_ehci_periodic_state_advance(uint32_t frame, uint32_t list, uint32_t entry) "frame=%d, list=0x%x, entry=0x%x"
116 +usb_ehci_periodic_state_advance(uint32_t frame, uint64_t list, uint64_t entry) "frame=%d, list=0x%" PRIx64 ", entry=0x%" PRIx64
117 usb_ehci_skipped_uframes(uint64_t skipped_uframes) "skipped %" PRIu64 " uframes"
118 usb_ehci_log(const char *msg) "%s"
119