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]> --- hw/usb/hcd-ehci.h | 1 + hw/usb/hcd-ehci.c | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index df16426f76..f0cb50ba45 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -73,6 +73,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 5964ede05b..a4a45c7601 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1471,7 +1471,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 */ @@ -1479,7 +1480,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); @@ -1768,7 +1771,7 @@ 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_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, itd.next)); ehci_set_state(ehci, async, EST_FETCHENTRY); return 1; -- 2.43.0
