[PULL 07/83] hw/usb/hcd-ehci: Implement 64-bit qTD descriptor addressing

Cédric Le Goater <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
From: Jamin Lin <[email protected]>

EHCI supports 64-bit 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 64-bit control data structure addresses
(EHCI 1.0, section 2.3.5 and Appendix B).

qTD link pointers (current_qtd/next_qtd/altnext_qtd and qTD.next)
are stored as 32-bit values in the data structures and must be
expanded to full 64-bit descriptor addresses when 64-bit mode is
enabled. Update the qTD traversal paths to use ehci_get_desc_addr()
when following link pointers.

Appendix B also defines high dword fields for qTD buffer pointers.
Add bufptr_hi[5] to EHCIqtd and extend qTD fetch and QH overlay
handling to load and propagate the high buffer pointer fields.

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]>
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Link: https://lore.kernel.org/qemu-devel/[email protected]
Signed-off-by: Cédric Le Goater <[email protected]>
---
 hw/usb/hcd-ehci.h |  1 +
 hw/usb/hcd-ehci.c | 33 ++++++++++++++++++++++++---------
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index 8732264fdebb..7fed20690683 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -139,6 +139,7 @@ typedef struct EHCIqtd {
     uint32_t bufptr[5];               /* Standard buffer pointer */
 #define QTD_BUFPTR_MASK               0xfffff000
 #define QTD_BUFPTR_SH                 12
+    uint32_t bufptr_hi[5];
 } EHCIqtd;
 
 /* QH overlay: altnext_qtd, token, bufptr[5], bufptr_hi[5] */
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index fd5be7430ef6..947af86ac420 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -104,6 +104,7 @@ typedef enum {
  * must not read or write past that boundary.
  */
 #define EHCI_QH_DWORDS_32   (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t))
+#define EHCI_QTD_DWORDS_32  (offsetof(EHCIqtd, bufptr_hi) / sizeof(uint32_t))
 
 static const char *ehci_state_names[] = {
     [EST_INACTIVE]     = "INACTIVE",
@@ -178,6 +179,11 @@ static uint32_t ehci_qh_dwords(const EHCIState *s)
     return s->caps_64bit_addr ? (sizeof(EHCIqh) >> 2) : EHCI_QH_DWORDS_32;
 }
 
+static uint32_t ehci_qtd_dwords(const EHCIState *s)
+{
+    return s->caps_64bit_addr ? (sizeof(EHCIqtd) >> 2) : EHCI_QTD_DWORDS_32;
+}
+
 static void ehci_trace_usbsts(uint32_t mask, int state)
 {
     /* interrupts */
@@ -486,7 +492,8 @@ static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd)
             (p->qtd.next != qtd->next)) ||
         (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) ||
         p->qtd.token != qtd->token ||
-        p->qtd.bufptr[0] != qtd->bufptr[0]) {
+        p->qtd.bufptr[0] != qtd->bufptr[0] ||
+        p->qtd.bufptr_hi[0] != qtd->bufptr_hi[0]) {
         return false;
     } else {
         return true;
@@ -519,10 +526,11 @@ static void ehci_writeback_async_complete_packet(EHCIPacket *p)
 
     /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */
     memset(&qh, 0, sizeof(qh));
+    memset(&qtd, 0, sizeof(qtd));
     get_dwords(q->ehci, NLPTR_GET(q->qhaddr),
                (uint32_t *) &qh, ehci_qh_dwords(q->ehci));
     get_dwords(q->ehci, NLPTR_GET(q->qtdaddr),
-               (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
+               (uint32_t *) &qtd, ehci_qtd_dwords(q->ehci));
     if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) {
         p->async = EHCI_ASYNC_INITIALIZED;
         ehci_free_packet(p);
@@ -1215,6 +1223,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
 
     for (i = 0; i < 5; i++) {
         q->qh.bufptr[i] = p->qtd.bufptr[i];
+        q->qh.bufptr_hi[i] = p->qtd.bufptr_hi[i];
     }
 
     if (!(q->qh.epchar & QH_EPCHAR_DTC)) {
@@ -1248,7 +1257,8 @@ static int ehci_init_transfer(EHCIPacket *p)
             return -1;
         }
 
-        page  = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
+        page = ehci_get_buf_addr(p->queue->ehci, p->qtd.bufptr_hi[cpage],
+                                 p->qtd.bufptr[cpage], QTD_BUFPTR_MASK);
         page += offset;
         plen  = bytes;
         if (plen > 4096 - offset) {
@@ -1744,7 +1754,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
     } else if ((q->qh.token & QTD_TOKEN_ACTIVE) &&
                (NLPTR_TBIT(q->qh.current_qtd) == 0) &&
                (q->qh.current_qtd != 0)) {
-        q->qtdaddr = q->qh.current_qtd;
+        q->qtdaddr = ehci_get_desc_addr(ehci, q->qh.current_qtd);
         ehci_set_state(ehci, async, EST_FETCHQTD);
 
     } else {
@@ -1822,14 +1832,14 @@ static int ehci_state_advqueue(EHCIQueue *q)
      */
     if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
         (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
-        q->qtdaddr = q->qh.altnext_qtd;
+        q->qtdaddr = ehci_get_desc_addr(q->ehci, q->qh.altnext_qtd);
         ehci_set_state(q->ehci, q->async, EST_FETCHQTD);
 
     /*
      *  next qTD is valid
      */
     } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
-        q->qtdaddr = q->qh.next_qtd;
+        q->qtdaddr = ehci_get_desc_addr(q->ehci, q->qh.next_qtd);
         ehci_set_state(q->ehci, q->async, EST_FETCHQTD);
 
     /*
@@ -1855,10 +1865,14 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
         return 0;
     }
     barrier();
+    memset(qtd.bufptr_hi, 0, sizeof(qtd.bufptr_hi));
     if (get_dwords(q->ehci, addr +  0, &qtd.next,    1) < 0 ||
         get_dwords(q->ehci, addr +  4, &qtd.altnext, 1) < 0 ||
         get_dwords(q->ehci, addr + 12, qtd.bufptr,
-                   ARRAY_SIZE(qtd.bufptr)) < 0) {
+                   ARRAY_SIZE(qtd.bufptr)) < 0 ||
+        (q->ehci->caps_64bit_addr &&
+         get_dwords(q->ehci, addr + offsetof(EHCIqtd, bufptr_hi),
+                    qtd.bufptr_hi, ARRAY_SIZE(qtd.bufptr_hi)) < 0)) {
         return 0;
     }
     ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
@@ -1939,7 +1953,7 @@ static int ehci_fill_queue(EHCIPacket *p)
         if (NLPTR_TBIT(qtd.next) != 0) {
             break;
         }
-        qtdaddr = qtd.next;
+        qtdaddr = ehci_get_desc_addr(q->ehci, qtd.next);
         /*
          * Detect circular td lists, Windows creates these, counting on the
          * active bit going low after execution to make the queue stop.
@@ -1949,8 +1963,9 @@ static int ehci_fill_queue(EHCIPacket *p)
                 goto leave;
             }
         }
+        memset(qtd.bufptr_hi, 0, sizeof(qtd.bufptr_hi));
         if (get_dwords(q->ehci, NLPTR_GET(qtdaddr),
-                       (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) {
+                       (uint32_t *) &qtd, ehci_qtd_dwords(q->ehci)) < 0) {
             return -1;
         }
         ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.