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. 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. Signed-off-by: Jamin Lin <[email protected]> --- hw/usb/hcd-ehci.h | 1 + hw/usb/hcd-ehci.c | 45 ++++++++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 7dc6d151cc..f18150c352 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -204,6 +204,7 @@ typedef struct EHCIqh { #define BUFPTR_FRAMETAG_MASK 0x0000001f #define BUFPTR_SBYTES_MASK 0x00000fe0 #define BUFPTR_SBYTES_SH 5 + uint32_t bufptr_hi[5]; } EHCIqh; /* diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d7a0917caf..8fd3fd60c4 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -146,6 +146,18 @@ static const char *addr2str(hwaddr addr) return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); } +static uint64_t ehci_get_desc_addr(EHCIState *s, uint32_t low) +{ + uint64_t addr; + + addr = (uint64_t)low; + if (s->caps_64bit_addr) { + addr |= (uint64_t)s->ctrldssegment << 32; + } + + return addr; +} + static void ehci_trace_usbsts(uint32_t mask, int state) { /* interrupts */ @@ -438,8 +450,9 @@ static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh) (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || (qh->current_qtd != q->qh.current_qtd) || (q->async && qh->next_qtd != q->qh.next_qtd) || + /* 1 altnext_qtd + 1 token + 5 bufptr + 5 bufptr_hi */ (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd, - 7 * sizeof(uint32_t)) != 0) || + 12 * sizeof(uint32_t)) != 0) || (q->dev != NULL && q->dev->addr != devaddr)) { return false; } else { @@ -1534,7 +1547,9 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) EHCIqh qh; int i = 0; int again = 0; - uint64_t entry = ehci->asynclistaddr; + uint64_t entry = 0; + + entry = ehci_get_desc_addr(ehci, ehci->asynclistaddr); /* set reclamation flag at start event (4.8.6) */ if (async) { @@ -1562,8 +1577,8 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) goto out; } - entry = qh.next; - if (entry == ehci->asynclistaddr) { + entry = ehci_get_desc_addr(ehci, qh.next); + if (entry == ehci_get_desc_addr(ehci, ehci->asynclistaddr)) { break; } } @@ -1688,7 +1703,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } #if EHCI_DEBUG - if (q->qhaddr != q->qh.next) { + if (q->qhaddr != ehci_get_desc_addr(ehci, q->qh.next)) { DPRINTF("FETCHQH: QH 0x%" PRIx64 " (h %x halt %x active %x) next 0x%08x\n", q->qhaddr, @@ -1879,10 +1894,12 @@ static int ehci_state_fetchqtd(EHCIQueue *q) static int ehci_state_horizqh(EHCIQueue *q) { + uint64_t addr; int again = 0; - if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) { - ehci_set_fetch_addr(q->ehci, q->async, q->qh.next); + addr = ehci_get_desc_addr(q->ehci, q->qh.next); + if (ehci_get_fetch_addr(q->ehci, q->async) != addr) { + ehci_set_fetch_addr(q->ehci, q->async, addr); ehci_set_state(q->ehci, q->async, EST_FETCHENTRY); again = 1; } else { @@ -2210,6 +2227,8 @@ static void ehci_advance_periodic_state(EHCIState *ehci) uint32_t entry; uint32_t list; const int async = 0; + uint64_t entry64; + uint64_t list64; /* 4.6 */ @@ -2234,14 +2253,14 @@ static void ehci_advance_periodic_state(EHCIState *ehci) break; } list |= ((ehci->frindex & 0x1ff8) >> 1); - - if (get_dwords(ehci, list, &entry, 1) < 0) { + list64 = ehci_get_desc_addr(ehci, list); + if (get_dwords(ehci, list64, &entry, 1) < 0) { break; } - - DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", - ehci->frindex / 8, list, entry); - ehci_set_fetch_addr(ehci, async, entry); + entry64 = ehci_get_desc_addr(ehci, entry); + DPRINTF("PERIODIC state adv fr=%d. %" PRIx64 " -> %" PRIx64 "\n", + ehci->frindex / 8, list64, entry64); + ehci_set_fetch_addr(ehci, async, entry64); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); ehci_queues_rip_unused(ehci, async); -- 2.43.0
