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

Reply via email to