* [PATCH v3 1/8] hw/usb/xhci: Move HCD constants to a header and add register constants
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 2/8] hw/usb/xhci: Rename and move HCD register region constants to header Nicholas Piggin
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
Prepare to use some of these constants in xhci qtest code.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/usb/hcd-xhci.h | 214 ++++++++++++++++++++++
hw/usb/hcd-xhci.c | 450 +++++++++++++++-------------------------------
2 files changed, 360 insertions(+), 304 deletions(-)
diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h
index 9c3974f1489..ee364efd0ab 100644
--- a/hw/usb/hcd-xhci.h
+++ b/hw/usb/hcd-xhci.h
@@ -115,6 +115,220 @@ typedef enum TRBCCode {
CC_SPLIT_TRANSACTION_ERROR
} TRBCCode;
+/* Register definitions */
+#define XHCI_HCCAP_REG_CAPLENGTH 0x00
+#define XHCI_HCCAP_REG_HCIVERSION 0x02
+#define XHCI_HCCAP_REG_HCSPARAMS1 0x04
+#define XHCI_HCCAP_REG_HCSPARAMS2 0x08
+#define XHCI_HCCAP_REG_HCSPARAMS3 0x0C
+#define XHCI_HCCAP_REG_HCCPARAMS1 0x10
+#define XHCI_HCCPARAMS1_AC64 0x00000001
+#define XHCI_HCCPARAMS1_XECP_SHIFT 16
+#define XHCI_HCCPARAMS1_MAXPSASIZE_SHIFT 12
+#define XHCI_HCCAP_REG_DBOFF 0x14
+#define XHCI_HCCAP_REG_RTSOFF 0x18
+#define XHCI_HCCAP_REG_HCCPARAMS2 0x1C
+#define XHCI_HCCAP_EXTCAP_START 0x20 /* SW-defined */
+
+#define XHCI_PORT_PR_SZ 0x10
+#define XHCI_PORT_REG_PORTSC 0x00
+#define XHCI_PORTSC_CCS (1 << 0)
+#define XHCI_PORTSC_PED (1 << 1)
+#define XHCI_PORTSC_OCA (1 << 3)
+#define XHCI_PORTSC_PR (1 << 4)
+#define XHCI_PORTSC_PLS_SHIFT 5
+#define XHCI_PORTSC_PLS_MASK 0xf
+#define XHCI_PORTSC_PP (1 << 9)
+#define XHCI_PORTSC_SPEED_SHIFT 10
+#define XHCI_PORTSC_SPEED_MASK 0xf
+#define XHCI_PORTSC_SPEED_FULL (1 << 10)
+#define XHCI_PORTSC_SPEED_LOW (2 << 10)
+#define XHCI_PORTSC_SPEED_HIGH (3 << 10)
+#define XHCI_PORTSC_SPEED_SUPER (4 << 10)
+#define XHCI_PORTSC_PIC_SHIFT 14
+#define XHCI_PORTSC_PIC_MASK 0x3
+#define XHCI_PORTSC_LWS (1 << 16)
+#define XHCI_PORTSC_CSC (1 << 17)
+#define XHCI_PORTSC_PEC (1 << 18)
+#define XHCI_PORTSC_WRC (1 << 19)
+#define XHCI_PORTSC_OCC (1 << 20)
+#define XHCI_PORTSC_PRC (1 << 21)
+#define XHCI_PORTSC_PLC (1 << 22)
+#define XHCI_PORTSC_CEC (1 << 23)
+#define XHCI_PORTSC_CAS (1 << 24)
+#define XHCI_PORTSC_WCE (1 << 25)
+#define XHCI_PORTSC_WDE (1 << 26)
+#define XHCI_PORTSC_WOE (1 << 27)
+#define XHCI_PORTSC_DR (1 << 30)
+#define XHCI_PORTSC_WPR (1 << 31)
+/* read/write bits */
+#define XHCI_PORTSC_RW_MASK (XHCI_PORTSC_PP | \
+ XHCI_PORTSC_WCE | \
+ XHCI_PORTSC_WDE | \
+ XHCI_PORTSC_WOE)
+/* write-1-to-clear bits*/
+#define XHCI_PORTSC_W1C_MASK (XHCI_PORTSC_CSC | \
+ XHCI_PORTSC_PEC | \
+ XHCI_PORTSC_WRC | \
+ XHCI_PORTSC_OCC | \
+ XHCI_PORTSC_PRC | \
+ XHCI_PORTSC_PLC | \
+ XHCI_PORTSC_CEC)
+#define XHCI_PORT_REG_PORTPMSC 0x04
+#define XHCI_PORT_REG_PORTLI 0x08
+#define XHCI_PORT_REG_PORTHLPMC 0x0C
+
+#define XHCI_OPER_REG_USBCMD 0x00
+#define XHCI_USBCMD_RS (1 << 0)
+#define XHCI_USBCMD_HCRST (1 << 1)
+#define XHCI_USBCMD_INTE (1 << 2)
+#define XHCI_USBCMD_HSEE (1 << 3)
+#define XHCI_USBCMD_LHCRST (1 << 7)
+#define XHCI_USBCMD_CSS (1 << 8)
+#define XHCI_USBCMD_CRS (1 << 9)
+#define XHCI_USBCMD_EWE (1 << 10)
+#define XHCI_USBCMD_EU3S (1 << 11)
+#define XHCI_OPER_REG_USBSTS 0x04
+#define XHCI_USBSTS_HCH (1 << 0)
+#define XHCI_USBSTS_HSE (1 << 2)
+#define XHCI_USBSTS_EINT (1 << 3)
+#define XHCI_USBSTS_PCD (1 << 4)
+#define XHCI_USBSTS_SSS (1 << 8)
+#define XHCI_USBSTS_RSS (1 << 9)
+#define XHCI_USBSTS_SRE (1 << 10)
+#define XHCI_USBSTS_CNR (1 << 11)
+#define XHCI_USBSTS_HCE (1 << 12)
+/* these bits are write-1-to-clear */
+#define XHCI_USBSTS_W1C_MASK (XHCI_USBSTS_HSE | \
+ XHCI_USBSTS_EINT | \
+ XHCI_USBSTS_PCD | \
+ XHCI_USBSTS_SRE)
+#define XHCI_OPER_REG_PAGESIZE 0x08
+#define XHCI_OPER_REG_DNCTRL 0x14
+#define XHCI_OPER_REG_CRCR_LO 0x18
+#define XHCI_CRCR_RCS (1 << 0)
+#define XHCI_CRCR_CS (1 << 1)
+#define XHCI_CRCR_CA (1 << 2)
+#define XHCI_CRCR_CRR (1 << 3)
+#define XHCI_OPER_REG_CRCR_HI 0x1C
+#define XHCI_OPER_REG_DCBAAP_LO 0x30
+#define XHCI_OPER_REG_DCBAAP_HI 0x34
+#define XHCI_OPER_REG_CONFIG 0x38
+
+#define XHCI_DBELL_DB_SZ 0x4
+
+#define XHCI_INTR_REG_MFINDEX 0x00
+#define XHCI_INTR_REG_IR0 0x20
+#define XHCI_INTR_IR_SZ 0x20
+
+#define XHCI_INTR_REG_IMAN 0x00
+#define XHCI_IMAN_IP (1 << 0)
+#define XHCI_IMAN_IE (1 << 1)
+#define XHCI_INTR_REG_IMOD 0x04
+#define XHCI_INTR_REG_ERSTSZ 0x08
+#define XHCI_INTR_REG_ERSTBA_LO 0x10
+#define XHCI_INTR_REG_ERSTBA_HI 0x14
+#define XHCI_INTR_REG_ERDP_LO 0x18
+#define XHCI_ERDP_EHB (1 << 3)
+#define XHCI_INTR_REG_ERDP_HI 0x1C
+
+#define TRB_SIZE 16
+typedef struct XHCITRB {
+ uint64_t parameter;
+ uint32_t status;
+ uint32_t control;
+ dma_addr_t addr;
+ bool ccs;
+} XHCITRB;
+
+enum {
+ PLS_U0 = 0,
+ PLS_U1 = 1,
+ PLS_U2 = 2,
+ PLS_U3 = 3,
+ PLS_DISABLED = 4,
+ PLS_RX_DETECT = 5,
+ PLS_INACTIVE = 6,
+ PLS_POLLING = 7,
+ PLS_RECOVERY = 8,
+ PLS_HOT_RESET = 9,
+ PLS_COMPILANCE_MODE = 10,
+ PLS_TEST_MODE = 11,
+ PLS_RESUME = 15,
+};
+
+#define CR_LINK TR_LINK
+
+#define TRB_C (1 << 0)
+#define TRB_TYPE_SHIFT 10
+#define TRB_TYPE_MASK 0x3f
+#define TRB_TYPE(t) (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
+
+#define TRB_EV_ED (1 << 2)
+
+#define TRB_TR_ENT (1 << 1)
+#define TRB_TR_ISP (1 << 2)
+#define TRB_TR_NS (1 << 3)
+#define TRB_TR_CH (1 << 4)
+#define TRB_TR_IOC (1 << 5)
+#define TRB_TR_IDT (1 << 6)
+#define TRB_TR_TBC_SHIFT 7
+#define TRB_TR_TBC_MASK 0x3
+#define TRB_TR_BEI (1 << 9)
+#define TRB_TR_TLBPC_SHIFT 16
+#define TRB_TR_TLBPC_MASK 0xf
+#define TRB_TR_FRAMEID_SHIFT 20
+#define TRB_TR_FRAMEID_MASK 0x7ff
+#define TRB_TR_SIA (1 << 31)
+
+#define TRB_TR_DIR (1 << 16)
+
+#define TRB_CR_SLOTID_SHIFT 24
+#define TRB_CR_SLOTID_MASK 0xff
+#define TRB_CR_EPID_SHIFT 16
+#define TRB_CR_EPID_MASK 0x1f
+
+#define TRB_CR_BSR (1 << 9)
+#define TRB_CR_DC (1 << 9)
+
+#define TRB_LK_TC (1 << 1)
+
+#define TRB_INTR_SHIFT 22
+#define TRB_INTR_MASK 0x3ff
+#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
+
+#define EP_TYPE_MASK 0x7
+#define EP_TYPE_SHIFT 3
+
+#define EP_STATE_MASK 0x7
+#define EP_DISABLED (0 << 0)
+#define EP_RUNNING (1 << 0)
+#define EP_HALTED (2 << 0)
+#define EP_STOPPED (3 << 0)
+#define EP_ERROR (4 << 0)
+
+#define SLOT_STATE_MASK 0x1f
+#define SLOT_STATE_SHIFT 27
+#define SLOT_STATE(s) (((s) >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK)
+#define SLOT_ENABLED 0
+#define SLOT_DEFAULT 1
+#define SLOT_ADDRESSED 2
+#define SLOT_CONFIGURED 3
+
+#define SLOT_CONTEXT_ENTRIES_MASK 0x1f
+#define SLOT_CONTEXT_ENTRIES_SHIFT 27
+
+typedef enum EPType {
+ ET_INVALID = 0,
+ ET_ISO_OUT,
+ ET_BULK_OUT,
+ ET_INTR_OUT,
+ ET_CONTROL,
+ ET_ISO_IN,
+ ET_BULK_IN,
+ ET_INTR_IN,
+} EPType;
+
typedef struct XHCIRing {
dma_addr_t dequeue;
bool ccs;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 64c3a23b9b7..b57db309b8d 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -47,8 +47,8 @@
#define TRANSFER_LIMIT 256
#define LEN_CAP 0x40
-#define LEN_OPER (0x400 + 0x10 * XHCI_MAXPORTS)
-#define LEN_RUNTIME ((XHCI_MAXINTRS + 1) * 0x20)
+#define LEN_OPER (0x400 + XHCI_PORT_PR_SZ * XHCI_MAXPORTS)
+#define LEN_RUNTIME ((XHCI_MAXINTRS + 1) * XHCI_INTR_IR_SZ)
#define LEN_DOORBELL ((XHCI_MAXSLOTS + 1) * 0x20)
#define OFF_OPER LEN_CAP
@@ -65,154 +65,6 @@
# error Increase XHCI_LEN_REGS
#endif
-/* bit definitions */
-#define USBCMD_RS (1<<0)
-#define USBCMD_HCRST (1<<1)
-#define USBCMD_INTE (1<<2)
-#define USBCMD_HSEE (1<<3)
-#define USBCMD_LHCRST (1<<7)
-#define USBCMD_CSS (1<<8)
-#define USBCMD_CRS (1<<9)
-#define USBCMD_EWE (1<<10)
-#define USBCMD_EU3S (1<<11)
-
-#define USBSTS_HCH (1<<0)
-#define USBSTS_HSE (1<<2)
-#define USBSTS_EINT (1<<3)
-#define USBSTS_PCD (1<<4)
-#define USBSTS_SSS (1<<8)
-#define USBSTS_RSS (1<<9)
-#define USBSTS_SRE (1<<10)
-#define USBSTS_CNR (1<<11)
-#define USBSTS_HCE (1<<12)
-
-
-#define PORTSC_CCS (1<<0)
-#define PORTSC_PED (1<<1)
-#define PORTSC_OCA (1<<3)
-#define PORTSC_PR (1<<4)
-#define PORTSC_PLS_SHIFT 5
-#define PORTSC_PLS_MASK 0xf
-#define PORTSC_PP (1<<9)
-#define PORTSC_SPEED_SHIFT 10
-#define PORTSC_SPEED_MASK 0xf
-#define PORTSC_SPEED_FULL (1<<10)
-#define PORTSC_SPEED_LOW (2<<10)
-#define PORTSC_SPEED_HIGH (3<<10)
-#define PORTSC_SPEED_SUPER (4<<10)
-#define PORTSC_PIC_SHIFT 14
-#define PORTSC_PIC_MASK 0x3
-#define PORTSC_LWS (1<<16)
-#define PORTSC_CSC (1<<17)
-#define PORTSC_PEC (1<<18)
-#define PORTSC_WRC (1<<19)
-#define PORTSC_OCC (1<<20)
-#define PORTSC_PRC (1<<21)
-#define PORTSC_PLC (1<<22)
-#define PORTSC_CEC (1<<23)
-#define PORTSC_CAS (1<<24)
-#define PORTSC_WCE (1<<25)
-#define PORTSC_WDE (1<<26)
-#define PORTSC_WOE (1<<27)
-#define PORTSC_DR (1<<30)
-#define PORTSC_WPR (1<<31)
-
-#define CRCR_RCS (1<<0)
-#define CRCR_CS (1<<1)
-#define CRCR_CA (1<<2)
-#define CRCR_CRR (1<<3)
-
-#define IMAN_IP (1<<0)
-#define IMAN_IE (1<<1)
-
-#define ERDP_EHB (1<<3)
-
-#define TRB_SIZE 16
-typedef struct XHCITRB {
- uint64_t parameter;
- uint32_t status;
- uint32_t control;
- dma_addr_t addr;
- bool ccs;
-} XHCITRB;
-
-enum {
- PLS_U0 = 0,
- PLS_U1 = 1,
- PLS_U2 = 2,
- PLS_U3 = 3,
- PLS_DISABLED = 4,
- PLS_RX_DETECT = 5,
- PLS_INACTIVE = 6,
- PLS_POLLING = 7,
- PLS_RECOVERY = 8,
- PLS_HOT_RESET = 9,
- PLS_COMPILANCE_MODE = 10,
- PLS_TEST_MODE = 11,
- PLS_RESUME = 15,
-};
-
-#define CR_LINK TR_LINK
-
-#define TRB_C (1<<0)
-#define TRB_TYPE_SHIFT 10
-#define TRB_TYPE_MASK 0x3f
-#define TRB_TYPE(t) (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
-
-#define TRB_EV_ED (1<<2)
-
-#define TRB_TR_ENT (1<<1)
-#define TRB_TR_ISP (1<<2)
-#define TRB_TR_NS (1<<3)
-#define TRB_TR_CH (1<<4)
-#define TRB_TR_IOC (1<<5)
-#define TRB_TR_IDT (1<<6)
-#define TRB_TR_TBC_SHIFT 7
-#define TRB_TR_TBC_MASK 0x3
-#define TRB_TR_BEI (1<<9)
-#define TRB_TR_TLBPC_SHIFT 16
-#define TRB_TR_TLBPC_MASK 0xf
-#define TRB_TR_FRAMEID_SHIFT 20
-#define TRB_TR_FRAMEID_MASK 0x7ff
-#define TRB_TR_SIA (1<<31)
-
-#define TRB_TR_DIR (1<<16)
-
-#define TRB_CR_SLOTID_SHIFT 24
-#define TRB_CR_SLOTID_MASK 0xff
-#define TRB_CR_EPID_SHIFT 16
-#define TRB_CR_EPID_MASK 0x1f
-
-#define TRB_CR_BSR (1<<9)
-#define TRB_CR_DC (1<<9)
-
-#define TRB_LK_TC (1<<1)
-
-#define TRB_INTR_SHIFT 22
-#define TRB_INTR_MASK 0x3ff
-#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
-
-#define EP_TYPE_MASK 0x7
-#define EP_TYPE_SHIFT 3
-
-#define EP_STATE_MASK 0x7
-#define EP_DISABLED (0<<0)
-#define EP_RUNNING (1<<0)
-#define EP_HALTED (2<<0)
-#define EP_STOPPED (3<<0)
-#define EP_ERROR (4<<0)
-
-#define SLOT_STATE_MASK 0x1f
-#define SLOT_STATE_SHIFT 27
-#define SLOT_STATE(s) (((s)>>SLOT_STATE_SHIFT)&SLOT_STATE_MASK)
-#define SLOT_ENABLED 0
-#define SLOT_DEFAULT 1
-#define SLOT_ADDRESSED 2
-#define SLOT_CONFIGURED 3
-
-#define SLOT_CONTEXT_ENTRIES_MASK 0x1f
-#define SLOT_CONTEXT_ENTRIES_SHIFT 27
-
#define get_field(data, field) \
(((data) >> field##_SHIFT) & field##_MASK)
@@ -223,17 +75,6 @@ enum {
*data = val_; \
} while (0)
-typedef enum EPType {
- ET_INVALID = 0,
- ET_ISO_OUT,
- ET_BULK_OUT,
- ET_INTR_OUT,
- ET_CONTROL,
- ET_ISO_IN,
- ET_BULK_IN,
- ET_INTR_IN,
-} EPType;
-
typedef struct XHCITransfer {
XHCIEPContext *epctx;
USBPacket packet;
@@ -440,7 +281,7 @@ static uint64_t xhci_mfindex_get(XHCIState *xhci)
static void xhci_mfwrap_update(XHCIState *xhci)
{
- const uint32_t bits = USBCMD_RS | USBCMD_EWE;
+ const uint32_t bits = XHCI_USBCMD_RS | XHCI_USBCMD_EWE;
uint32_t mfindex, left;
int64_t now;
@@ -465,7 +306,7 @@ static void xhci_mfwrap_timer(void *opaque)
static void xhci_die(XHCIState *xhci)
{
- xhci->usbsts |= USBSTS_HCE;
+ xhci->usbsts |= XHCI_USBSTS_HCE;
DPRINTF("xhci: asserted controller error\n");
}
@@ -557,51 +398,51 @@ static void xhci_intr_update(XHCIState *xhci, int v)
int level = 0;
if (v == 0) {
- if (xhci->intr[0].iman & IMAN_IP &&
- xhci->intr[0].iman & IMAN_IE &&
- xhci->usbcmd & USBCMD_INTE) {
+ if (xhci->intr[0].iman & XHCI_IMAN_IP &&
+ xhci->intr[0].iman & XHCI_IMAN_IE &&
+ xhci->usbcmd & XHCI_USBCMD_INTE) {
level = 1;
}
if (xhci->intr_raise) {
if (xhci->intr_raise(xhci, 0, level)) {
- xhci->intr[0].iman &= ~IMAN_IP;
+ xhci->intr[0].iman &= ~XHCI_IMAN_IP;
}
}
}
if (xhci->intr_update) {
xhci->intr_update(xhci, v,
- xhci->intr[v].iman & IMAN_IE);
+ xhci->intr[v].iman & XHCI_IMAN_IE);
}
}
static void xhci_intr_raise(XHCIState *xhci, int v)
{
- bool pending = (xhci->intr[v].erdp_low & ERDP_EHB);
+ bool pending = (xhci->intr[v].erdp_low & XHCI_ERDP_EHB);
- xhci->intr[v].erdp_low |= ERDP_EHB;
- xhci->intr[v].iman |= IMAN_IP;
- xhci->usbsts |= USBSTS_EINT;
+ xhci->intr[v].erdp_low |= XHCI_ERDP_EHB;
+ xhci->intr[v].iman |= XHCI_IMAN_IP;
+ xhci->usbsts |= XHCI_USBSTS_EINT;
if (pending) {
return;
}
- if (!(xhci->intr[v].iman & IMAN_IE)) {
+ if (!(xhci->intr[v].iman & XHCI_IMAN_IE)) {
return;
}
- if (!(xhci->usbcmd & USBCMD_INTE)) {
+ if (!(xhci->usbcmd & XHCI_USBCMD_INTE)) {
return;
}
if (xhci->intr_raise) {
if (xhci->intr_raise(xhci, v, true)) {
- xhci->intr[v].iman &= ~IMAN_IP;
+ xhci->intr[v].iman &= ~XHCI_IMAN_IP;
}
}
}
static inline int xhci_running(XHCIState *xhci)
{
- return !(xhci->usbsts & USBSTS_HCH);
+ return !(xhci->usbsts & XHCI_USBSTS_HCH);
}
static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
@@ -846,15 +687,15 @@ static void xhci_er_reset(XHCIState *xhci, int v)
static void xhci_run(XHCIState *xhci)
{
trace_usb_xhci_run();
- xhci->usbsts &= ~USBSTS_HCH;
+ xhci->usbsts &= ~XHCI_USBSTS_HCH;
xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
static void xhci_stop(XHCIState *xhci)
{
trace_usb_xhci_stop();
- xhci->usbsts |= USBSTS_HCH;
- xhci->crcr_low &= ~CRCR_CRR;
+ xhci->usbsts |= XHCI_USBSTS_HCH;
+ xhci->crcr_low &= ~XHCI_CRCR_CRR;
}
static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count,
@@ -2480,7 +2321,7 @@ static void xhci_process_commands(XHCIState *xhci)
return;
}
- xhci->crcr_low |= CRCR_CRR;
+ xhci->crcr_low |= XHCI_CRCR_CRR;
while ((type = xhci_ring_fetch(xhci, &xhci->cmd_ring, &trb, &addr))) {
event.ptr = addr;
@@ -2632,32 +2473,32 @@ static void xhci_port_update(XHCIPort *port, int is_detach)
uint32_t pls = PLS_RX_DETECT;
assert(port);
- port->portsc = PORTSC_PP;
+ port->portsc = XHCI_PORTSC_PP;
if (!is_detach && xhci_port_have_device(port)) {
- port->portsc |= PORTSC_CCS;
+ port->portsc |= XHCI_PORTSC_CCS;
switch (port->uport->dev->speed) {
case USB_SPEED_LOW:
- port->portsc |= PORTSC_SPEED_LOW;
+ port->portsc |= XHCI_PORTSC_SPEED_LOW;
pls = PLS_POLLING;
break;
case USB_SPEED_FULL:
- port->portsc |= PORTSC_SPEED_FULL;
+ port->portsc |= XHCI_PORTSC_SPEED_FULL;
pls = PLS_POLLING;
break;
case USB_SPEED_HIGH:
- port->portsc |= PORTSC_SPEED_HIGH;
+ port->portsc |= XHCI_PORTSC_SPEED_HIGH;
pls = PLS_POLLING;
break;
case USB_SPEED_SUPER:
- port->portsc |= PORTSC_SPEED_SUPER;
- port->portsc |= PORTSC_PED;
+ port->portsc |= XHCI_PORTSC_SPEED_SUPER;
+ port->portsc |= XHCI_PORTSC_PED;
pls = PLS_U0;
break;
}
}
- set_field(&port->portsc, pls, PORTSC_PLS);
+ set_field(&port->portsc, pls, XHCI_PORTSC_PLS);
trace_usb_xhci_port_link(port->portnr, pls);
- xhci_port_notify(port, PORTSC_CSC);
+ xhci_port_notify(port, XHCI_PORTSC_CSC);
}
static void xhci_port_reset(XHCIPort *port, bool warm_reset)
@@ -2673,20 +2514,20 @@ static void xhci_port_reset(XHCIPort *port, bool warm_reset)
switch (port->uport->dev->speed) {
case USB_SPEED_SUPER:
if (warm_reset) {
- port->portsc |= PORTSC_WRC;
+ port->portsc |= XHCI_PORTSC_WRC;
}
/* fall through */
case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
- set_field(&port->portsc, PLS_U0, PORTSC_PLS);
+ set_field(&port->portsc, PLS_U0, XHCI_PORTSC_PLS);
trace_usb_xhci_port_link(port->portnr, PLS_U0);
- port->portsc |= PORTSC_PED;
+ port->portsc |= XHCI_PORTSC_PED;
break;
}
- port->portsc &= ~PORTSC_PR;
- xhci_port_notify(port, PORTSC_PRC);
+ port->portsc &= ~XHCI_PORTSC_PR;
+ xhci_port_notify(port, XHCI_PORTSC_PRC);
}
static void xhci_reset(DeviceState *dev)
@@ -2695,12 +2536,12 @@ static void xhci_reset(DeviceState *dev)
int i;
trace_usb_xhci_reset();
- if (!(xhci->usbsts & USBSTS_HCH)) {
+ if (!(xhci->usbsts & XHCI_USBSTS_HCH)) {
DPRINTF("xhci: reset while running!\n");
}
xhci->usbcmd = 0;
- xhci->usbsts = USBSTS_HCH;
+ xhci->usbsts = XHCI_USBSTS_HCH;
xhci->dnctrl = 0;
xhci->crcr_low = 0;
xhci->crcr_high = 0;
@@ -2741,56 +2582,56 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
uint32_t ret;
switch (reg) {
- case 0x00: /* HCIVERSION, CAPLENGTH */
+ case XHCI_HCCAP_REG_CAPLENGTH: /* Covers HCIVERSION and CAPLENGTH */
ret = 0x01000000 | LEN_CAP;
break;
- case 0x04: /* HCSPARAMS 1 */
+ case XHCI_HCCAP_REG_HCSPARAMS1:
ret = ((xhci->numports_2+xhci->numports_3)<<24)
| (xhci->numintrs<<8) | xhci->numslots;
break;
- case 0x08: /* HCSPARAMS 2 */
+ case XHCI_HCCAP_REG_HCSPARAMS2:
ret = 0x0000000f;
break;
- case 0x0c: /* HCSPARAMS 3 */
+ case XHCI_HCCAP_REG_HCSPARAMS3:
ret = 0x00000000;
break;
- case 0x10: /* HCCPARAMS */
- if (sizeof(dma_addr_t) == 4) {
- ret = 0x00080000 | (xhci->max_pstreams_mask << 12);
- } else {
- ret = 0x00080001 | (xhci->max_pstreams_mask << 12);
+ case XHCI_HCCAP_REG_HCCPARAMS1:
+ ret = (XHCI_HCCAP_EXTCAP_START / 4) << XHCI_HCCPARAMS1_XECP_SHIFT;
+ ret |= xhci->max_pstreams_mask << XHCI_HCCPARAMS1_MAXPSASIZE_SHIFT;
+ if (sizeof(dma_addr_t) == 8) {
+ ret |= XHCI_HCCPARAMS1_AC64;
}
break;
- case 0x14: /* DBOFF */
+ case XHCI_HCCAP_REG_DBOFF:
ret = OFF_DOORBELL;
break;
- case 0x18: /* RTSOFF */
+ case XHCI_HCCAP_REG_RTSOFF:
ret = OFF_RUNTIME;
break;
/* extended capabilities */
- case 0x20: /* Supported Protocol:00 */
+ case XHCI_HCCAP_EXTCAP_START + 0x00: /* Supported Protocol:00 */
ret = 0x02000402; /* USB 2.0 */
break;
- case 0x24: /* Supported Protocol:04 */
+ case XHCI_HCCAP_EXTCAP_START + 0x04: /* Supported Protocol:04 */
ret = 0x20425355; /* "USB " */
break;
- case 0x28: /* Supported Protocol:08 */
+ case XHCI_HCCAP_EXTCAP_START + 0x08: /* Supported Protocol:08 */
ret = (xhci->numports_2 << 8) | (xhci->numports_3 + 1);
break;
- case 0x2c: /* Supported Protocol:0c */
+ case XHCI_HCCAP_EXTCAP_START + 0x0c: /* Supported Protocol:0c */
ret = 0x00000000; /* reserved */
break;
- case 0x30: /* Supported Protocol:00 */
+ case XHCI_HCCAP_EXTCAP_START + 0x10: /* Supported Protocol:00 */
ret = 0x03000002; /* USB 3.0 */
break;
- case 0x34: /* Supported Protocol:04 */
+ case XHCI_HCCAP_EXTCAP_START + 0x14: /* Supported Protocol:04 */
ret = 0x20425355; /* "USB " */
break;
- case 0x38: /* Supported Protocol:08 */
+ case XHCI_HCCAP_EXTCAP_START + 0x18: /* Supported Protocol:08 */
ret = (xhci->numports_3 << 8) | 1;
break;
- case 0x3c: /* Supported Protocol:0c */
+ case XHCI_HCCAP_EXTCAP_START + 0x1c: /* Supported Protocol:0c */
ret = 0x00000000; /* reserved */
break;
default:
@@ -2808,14 +2649,18 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size)
uint32_t ret;
switch (reg) {
- case 0x00: /* PORTSC */
+ case XHCI_PORT_REG_PORTSC:
ret = port->portsc;
break;
- case 0x04: /* PORTPMSC */
- case 0x08: /* PORTLI */
+ case XHCI_PORT_REG_PORTLI:
ret = 0;
break;
- case 0x0c: /* PORTHLPMC */
+ case XHCI_PORT_REG_PORTPMSC:
+ ret = 0;
+ qemu_log_mask(LOG_UNIMP, "%s: read from port register PORTHPMSC",
+ __func__);
+ break;
+ case XHCI_PORT_REG_PORTHLPMC:
ret = 0;
qemu_log_mask(LOG_UNIMP, "%s: read from port register PORTHLPMC",
__func__);
@@ -2840,37 +2685,35 @@ static void xhci_port_write(void *ptr, hwaddr reg,
trace_usb_xhci_port_write(port->portnr, reg, val);
switch (reg) {
- case 0x00: /* PORTSC */
+ case XHCI_PORT_REG_PORTSC:
/* write-1-to-start bits */
- if (val & PORTSC_WPR) {
+ if (val & XHCI_PORTSC_WPR) {
xhci_port_reset(port, true);
break;
}
- if (val & PORTSC_PR) {
+ if (val & XHCI_PORTSC_PR) {
xhci_port_reset(port, false);
break;
}
portsc = port->portsc;
notify = 0;
- /* write-1-to-clear bits*/
- portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
- PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
- if (val & PORTSC_LWS) {
+ portsc &= ~(val & XHCI_PORTSC_W1C_MASK);
+ if (val & XHCI_PORTSC_LWS) {
/* overwrite PLS only when LWS=1 */
- uint32_t old_pls = get_field(port->portsc, PORTSC_PLS);
- uint32_t new_pls = get_field(val, PORTSC_PLS);
+ uint32_t old_pls = get_field(port->portsc, XHCI_PORTSC_PLS);
+ uint32_t new_pls = get_field(val, XHCI_PORTSC_PLS);
switch (new_pls) {
case PLS_U0:
if (old_pls != PLS_U0) {
- set_field(&portsc, new_pls, PORTSC_PLS);
+ set_field(&portsc, new_pls, XHCI_PORTSC_PLS);
trace_usb_xhci_port_link(port->portnr, new_pls);
- notify = PORTSC_PLC;
+ notify = XHCI_PORTSC_PLC;
}
break;
case PLS_U3:
if (old_pls < PLS_U3) {
- set_field(&portsc, new_pls, PORTSC_PLS);
+ set_field(&portsc, new_pls, XHCI_PORTSC_PLS);
trace_usb_xhci_port_link(port->portnr, new_pls);
}
break;
@@ -2883,22 +2726,21 @@ static void xhci_port_write(void *ptr, hwaddr reg,
break;
}
}
- /* read/write bits */
- portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
- portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
+ portsc &= ~XHCI_PORTSC_RW_MASK;
+ portsc |= val & XHCI_PORTSC_RW_MASK;
port->portsc = portsc;
if (notify) {
xhci_port_notify(port, notify);
}
break;
- case 0x04: /* PORTPMSC */
- case 0x0c: /* PORTHLPMC */
+ case XHCI_PORT_REG_PORTPMSC:
+ case XHCI_PORT_REG_PORTHLPMC:
qemu_log_mask(LOG_UNIMP,
"%s: write 0x%" PRIx64
" (%u bytes) to port register at offset 0x%" HWADDR_PRIx,
__func__, val, size, reg);
break;
- case 0x08: /* PORTLI */
+ case XHCI_PORT_REG_PORTLI:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Write to read-only PORTLI register",
__func__);
break;
@@ -2917,31 +2759,31 @@ static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size)
uint32_t ret;
switch (reg) {
- case 0x00: /* USBCMD */
+ case XHCI_OPER_REG_USBCMD:
ret = xhci->usbcmd;
break;
- case 0x04: /* USBSTS */
+ case XHCI_OPER_REG_USBSTS:
ret = xhci->usbsts;
break;
- case 0x08: /* PAGESIZE */
+ case XHCI_OPER_REG_PAGESIZE:
ret = 1; /* 4KiB */
break;
- case 0x14: /* DNCTRL */
+ case XHCI_OPER_REG_DNCTRL:
ret = xhci->dnctrl;
break;
- case 0x18: /* CRCR low */
+ case XHCI_OPER_REG_CRCR_LO:
ret = xhci->crcr_low & ~0xe;
break;
- case 0x1c: /* CRCR high */
+ case XHCI_OPER_REG_CRCR_HI:
ret = xhci->crcr_high;
break;
- case 0x30: /* DCBAAP low */
+ case XHCI_OPER_REG_DCBAAP_LO:
ret = xhci->dcbaap_low;
break;
- case 0x34: /* DCBAAP high */
+ case XHCI_OPER_REG_DCBAAP_HI:
ret = xhci->dcbaap_high;
break;
- case 0x38: /* CONFIG */
+ case XHCI_OPER_REG_CONFIG:
ret = xhci->config;
break;
default:
@@ -2961,60 +2803,60 @@ static void xhci_oper_write(void *ptr, hwaddr reg,
trace_usb_xhci_oper_write(reg, val);
switch (reg) {
- case 0x00: /* USBCMD */
- if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) {
+ case XHCI_OPER_REG_USBCMD:
+ if ((val & XHCI_USBCMD_RS) && !(xhci->usbcmd & XHCI_USBCMD_RS)) {
xhci_run(xhci);
- } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) {
+ } else if (!(val & XHCI_USBCMD_RS) && (xhci->usbcmd & XHCI_USBCMD_RS)) {
xhci_stop(xhci);
}
- if (val & USBCMD_CSS) {
+ if (val & XHCI_USBCMD_CSS) {
/* save state */
- xhci->usbsts &= ~USBSTS_SRE;
+ xhci->usbsts &= ~XHCI_USBSTS_SRE;
}
- if (val & USBCMD_CRS) {
+ if (val & XHCI_USBCMD_CRS) {
/* restore state */
- xhci->usbsts |= USBSTS_SRE;
+ xhci->usbsts |= XHCI_USBSTS_SRE;
}
xhci->usbcmd = val & 0xc0f;
xhci_mfwrap_update(xhci);
- if (val & USBCMD_HCRST) {
+ if (val & XHCI_USBCMD_HCRST) {
xhci_reset(DEVICE(xhci));
}
xhci_intr_update(xhci, 0);
break;
- case 0x04: /* USBSTS */
- /* these bits are write-1-to-clear */
- xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
+ case XHCI_OPER_REG_USBSTS:
+ xhci->usbsts &= ~(val & XHCI_USBSTS_W1C_MASK);
xhci_intr_update(xhci, 0);
break;
- case 0x14: /* DNCTRL */
+ case XHCI_OPER_REG_DNCTRL:
xhci->dnctrl = val & 0xffff;
break;
- case 0x18: /* CRCR low */
- xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & CRCR_CRR);
+ case XHCI_OPER_REG_CRCR_LO:
+ xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & XHCI_CRCR_CRR);
break;
- case 0x1c: /* CRCR high */
+ case XHCI_OPER_REG_CRCR_HI:
xhci->crcr_high = val;
- if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
+ if (xhci->crcr_low & (XHCI_CRCR_CA | XHCI_CRCR_CS) &&
+ (xhci->crcr_low & XHCI_CRCR_CRR)) {
XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
- xhci->crcr_low &= ~CRCR_CRR;
+ xhci->crcr_low &= ~XHCI_CRCR_CRR;
xhci_event(xhci, &event, 0);
DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
} else {
dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
xhci_ring_init(xhci, &xhci->cmd_ring, base);
}
- xhci->crcr_low &= ~(CRCR_CA | CRCR_CS);
+ xhci->crcr_low &= ~(XHCI_CRCR_CA | XHCI_CRCR_CS);
break;
- case 0x30: /* DCBAAP low */
+ case XHCI_OPER_REG_DCBAAP_LO:
xhci->dcbaap_low = val & 0xffffffc0;
break;
- case 0x34: /* DCBAAP high */
+ case XHCI_OPER_REG_DCBAAP_HI:
xhci->dcbaap_high = val;
break;
- case 0x38: /* CONFIG */
+ case XHCI_OPER_REG_CONFIG:
xhci->config = val & 0xff;
break;
default:
@@ -3028,9 +2870,9 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg,
XHCIState *xhci = ptr;
uint32_t ret = 0;
- if (reg < 0x20) {
+ if (reg < XHCI_INTR_REG_IR0) {
switch (reg) {
- case 0x00: /* MFINDEX */
+ case XHCI_INTR_REG_MFINDEX:
ret = xhci_mfindex_get(xhci) & 0x3fff;
break;
default:
@@ -3038,28 +2880,28 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg,
break;
}
} else {
- int v = (reg - 0x20) / 0x20;
+ int v = (reg - XHCI_INTR_REG_IR0) / XHCI_INTR_IR_SZ;
XHCIInterrupter *intr = &xhci->intr[v];
- switch (reg & 0x1f) {
- case 0x00: /* IMAN */
+ switch (reg & (XHCI_INTR_IR_SZ - 1)) {
+ case XHCI_INTR_REG_IMAN:
ret = intr->iman;
break;
- case 0x04: /* IMOD */
+ case XHCI_INTR_REG_IMOD:
ret = intr->imod;
break;
- case 0x08: /* ERSTSZ */
+ case XHCI_INTR_REG_ERSTSZ:
ret = intr->erstsz;
break;
- case 0x10: /* ERSTBA low */
+ case XHCI_INTR_REG_ERSTBA_LO:
ret = intr->erstba_low;
break;
- case 0x14: /* ERSTBA high */
+ case XHCI_INTR_REG_ERSTBA_HI:
ret = intr->erstba_high;
break;
- case 0x18: /* ERDP low */
+ case XHCI_INTR_REG_ERDP_LO:
ret = intr->erdp_low;
break;
- case 0x1c: /* ERDP high */
+ case XHCI_INTR_REG_ERDP_HI:
ret = intr->erdp_high;
break;
}
@@ -3078,29 +2920,29 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
trace_usb_xhci_runtime_write(reg, val);
- if (reg < 0x20) {
+ if (reg < XHCI_INTR_REG_IR0) {
trace_usb_xhci_unimplemented("runtime write", reg);
return;
}
- v = (reg - 0x20) / 0x20;
+ v = (reg - XHCI_INTR_REG_IR0) / XHCI_INTR_IR_SZ;
intr = &xhci->intr[v];
- switch (reg & 0x1f) {
- case 0x00: /* IMAN */
- if (val & IMAN_IP) {
- intr->iman &= ~IMAN_IP;
+ switch (reg & (XHCI_INTR_IR_SZ - 1)) {
+ case XHCI_INTR_REG_IMAN:
+ if (val & XHCI_IMAN_IP) {
+ intr->iman &= ~XHCI_IMAN_IP;
}
- intr->iman &= ~IMAN_IE;
- intr->iman |= val & IMAN_IE;
+ intr->iman &= ~XHCI_IMAN_IE;
+ intr->iman |= val & XHCI_IMAN_IE;
xhci_intr_update(xhci, v);
break;
- case 0x04: /* IMOD */
+ case XHCI_INTR_REG_IMOD:
intr->imod = val;
break;
- case 0x08: /* ERSTSZ */
+ case XHCI_INTR_REG_ERSTSZ:
intr->erstsz = val & 0xffff;
break;
- case 0x10: /* ERSTBA low */
+ case XHCI_INTR_REG_ERSTBA_LO:
if (xhci->nec_quirks) {
/* NEC driver bug: it doesn't align this to 64 bytes */
intr->erstba_low = val & 0xfffffff0;
@@ -3108,16 +2950,17 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
intr->erstba_low = val & 0xffffffc0;
}
break;
- case 0x14: /* ERSTBA high */
+ case XHCI_INTR_REG_ERSTBA_HI:
intr->erstba_high = val;
xhci_er_reset(xhci, v);
break;
- case 0x18: /* ERDP low */
- if (val & ERDP_EHB) {
- intr->erdp_low &= ~ERDP_EHB;
+ case XHCI_INTR_REG_ERDP_LO:
+ if (val & XHCI_ERDP_EHB) {
+ intr->erdp_low &= ~XHCI_ERDP_EHB;
}
- intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB);
- if (val & ERDP_EHB) {
+ intr->erdp_low = (val & ~XHCI_ERDP_EHB) |
+ (intr->erdp_low & XHCI_ERDP_EHB);
+ if (val & XHCI_ERDP_EHB) {
dma_addr_t erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
unsigned int dp_idx = (erdp - intr->er_start) / TRB_SIZE;
if (erdp >= intr->er_start &&
@@ -3127,7 +2970,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
}
}
break;
- case 0x1c: /* ERDP high */
+ case XHCI_INTR_REG_ERDP_HI:
intr->erdp_high = val;
break;
default:
@@ -3156,8 +2999,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
return;
}
- reg >>= 2;
-
+ reg /= XHCI_DBELL_DB_SZ;
if (reg == 0) {
if (val == 0) {
xhci_process_commands(xhci);
@@ -3250,11 +3092,11 @@ static void xhci_wakeup(USBPort *usbport)
XHCIPort *port = xhci_lookup_port(xhci, usbport);
assert(port);
- if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) {
+ if (get_field(port->portsc, XHCI_PORTSC_PLS) != PLS_U3) {
return;
}
- set_field(&port->portsc, PLS_RESUME, PORTSC_PLS);
- xhci_port_notify(port, PORTSC_PLC);
+ set_field(&port->portsc, PLS_RESUME, XHCI_PORTSC_PLS);
+ xhci_port_notify(port, XHCI_PORTSC_PLC);
}
static void xhci_complete(USBPort *port, USBPacket *packet)
@@ -3341,7 +3183,7 @@ static void usb_xhci_init(XHCIState *xhci)
XHCIPort *port;
unsigned int i, usbports, speedmask;
- xhci->usbsts = USBSTS_HCH;
+ xhci->usbsts = XHCI_USBSTS_HCH;
if (xhci->numports_2 > XHCI_MAXPORTS_2) {
xhci->numports_2 = XHCI_MAXPORTS_2;
@@ -3429,10 +3271,10 @@ static void usb_xhci_realize(DeviceState *dev, Error **errp)
for (i = 0; i < xhci->numports; i++) {
XHCIPort *port = &xhci->ports[i];
- uint32_t offset = OFF_OPER + 0x400 + 0x10 * i;
+ uint32_t offset = OFF_OPER + 0x400 + XHCI_PORT_PR_SZ * i;
port->xhci = xhci;
memory_region_init_io(&port->mem, OBJECT(dev), &xhci_port_ops, port,
- port->name, 0x10);
+ port->name, XHCI_PORT_PR_SZ);
memory_region_add_subregion(&xhci->mem, offset, &port->mem);
}
}
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 2/8] hw/usb/xhci: Rename and move HCD register region constants to header
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 1/8] hw/usb/xhci: Move HCD constants to a header and add register constants Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 3/8] tests/qtest/xhci: Add controller and device setup and ring tests Nicholas Piggin
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
This also adds some missing constants rather than open-coding
offsets and sizes.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/usb/hcd-xhci.h | 16 ++++++++++++++++
hw/usb/hcd-xhci.c | 48 ++++++++++++++++++++++-------------------------
2 files changed, 38 insertions(+), 26 deletions(-)
diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h
index ee364efd0ab..20059fcf66c 100644
--- a/hw/usb/hcd-xhci.h
+++ b/hw/usb/hcd-xhci.h
@@ -115,6 +115,22 @@ typedef enum TRBCCode {
CC_SPLIT_TRANSACTION_ERROR
} TRBCCode;
+/* Register regions */
+#define XHCI_REGS_LENGTH_CAP 0x40
+#define XHCI_REGS_LENGTH_OPER 0x400
+#define XHCI_REGS_LENGTH_PORT (XHCI_PORT_PR_SZ * XHCI_MAXPORTS)
+#define XHCI_REGS_LENGTH_RUNTIME ((XHCI_MAXINTRS + 1) * XHCI_INTR_IR_SZ)
+/* XXX: Should doorbell length be *4 rather than *32? */
+#define XHCI_REGS_LENGTH_DOORBELL ((XHCI_MAXSLOTS + 1) * 0x20)
+
+#define XHCI_REGS_OFFSET_CAP 0
+#define XHCI_REGS_OFFSET_OPER (XHCI_REGS_OFFSET_CAP + \
+ XHCI_REGS_LENGTH_CAP)
+#define XHCI_REGS_OFFSET_PORT (XHCI_REGS_OFFSET_OPER + \
+ XHCI_REGS_LENGTH_OPER)
+#define XHCI_REGS_OFFSET_RUNTIME 0x1000
+#define XHCI_REGS_OFFSET_DOORBELL 0x2000
+
/* Register definitions */
#define XHCI_HCCAP_REG_CAPLENGTH 0x00
#define XHCI_HCCAP_REG_HCIVERSION 0x02
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index b57db309b8d..7470db38561 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -46,22 +46,14 @@
#define COMMAND_LIMIT 256
#define TRANSFER_LIMIT 256
-#define LEN_CAP 0x40
-#define LEN_OPER (0x400 + XHCI_PORT_PR_SZ * XHCI_MAXPORTS)
-#define LEN_RUNTIME ((XHCI_MAXINTRS + 1) * XHCI_INTR_IR_SZ)
-#define LEN_DOORBELL ((XHCI_MAXSLOTS + 1) * 0x20)
-
-#define OFF_OPER LEN_CAP
-#define OFF_RUNTIME 0x1000
-#define OFF_DOORBELL 0x2000
-
-#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME
-#error Increase OFF_RUNTIME
+#if (XHCI_REGS_OFFSET_PORT + XHCI_REGS_LENGTH_PORT) > XHCI_REGS_OFFSET_RUNTIME
+#error Increase XHCI_REGS_OFFSET_RUNTIME
#endif
-#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL
-#error Increase OFF_DOORBELL
+#if (XHCI_REGS_OFFSET_RUNTIME + XHCI_REGS_LENGTH_RUNTIME) > \
+ XHCI_REGS_OFFSET_DOORBELL
+#error Increase XHCI_REGS_OFFSET_DOORBELL
#endif
-#if (OFF_DOORBELL + LEN_DOORBELL) > XHCI_LEN_REGS
+#if (XHCI_REGS_OFFSET_DOORBELL + XHCI_REGS_LENGTH_DOORBELL) > XHCI_LEN_REGS
# error Increase XHCI_LEN_REGS
#endif
@@ -2583,7 +2575,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
switch (reg) {
case XHCI_HCCAP_REG_CAPLENGTH: /* Covers HCIVERSION and CAPLENGTH */
- ret = 0x01000000 | LEN_CAP;
+ ret = 0x01000000 | XHCI_REGS_LENGTH_CAP;
break;
case XHCI_HCCAP_REG_HCSPARAMS1:
ret = ((xhci->numports_2+xhci->numports_3)<<24)
@@ -2603,10 +2595,10 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
}
break;
case XHCI_HCCAP_REG_DBOFF:
- ret = OFF_DOORBELL;
+ ret = XHCI_REGS_OFFSET_DOORBELL;
break;
case XHCI_HCCAP_REG_RTSOFF:
- ret = OFF_RUNTIME;
+ ret = XHCI_REGS_OFFSET_RUNTIME;
break;
/* extended capabilities */
@@ -3256,22 +3248,26 @@ static void usb_xhci_realize(DeviceState *dev, Error **errp)
memory_region_init(&xhci->mem, OBJECT(dev), "xhci", XHCI_LEN_REGS);
memory_region_init_io(&xhci->mem_cap, OBJECT(dev), &xhci_cap_ops, xhci,
- "capabilities", LEN_CAP);
+ "capabilities", XHCI_REGS_LENGTH_CAP);
memory_region_init_io(&xhci->mem_oper, OBJECT(dev), &xhci_oper_ops, xhci,
- "operational", 0x400);
+ "operational", XHCI_REGS_LENGTH_OPER);
memory_region_init_io(&xhci->mem_runtime, OBJECT(dev), &xhci_runtime_ops,
- xhci, "runtime", LEN_RUNTIME);
+ xhci, "runtime", XHCI_REGS_LENGTH_RUNTIME);
memory_region_init_io(&xhci->mem_doorbell, OBJECT(dev), &xhci_doorbell_ops,
- xhci, "doorbell", LEN_DOORBELL);
+ xhci, "doorbell", XHCI_REGS_LENGTH_DOORBELL);
- memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap);
- memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper);
- memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime);
- memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell);
+ memory_region_add_subregion(&xhci->mem, XHCI_REGS_OFFSET_CAP,
+ &xhci->mem_cap);
+ memory_region_add_subregion(&xhci->mem, XHCI_REGS_OFFSET_OPER,
+ &xhci->mem_oper);
+ memory_region_add_subregion(&xhci->mem, XHCI_REGS_OFFSET_RUNTIME,
+ &xhci->mem_runtime);
+ memory_region_add_subregion(&xhci->mem, XHCI_REGS_OFFSET_DOORBELL,
+ &xhci->mem_doorbell);
for (i = 0; i < xhci->numports; i++) {
XHCIPort *port = &xhci->ports[i];
- uint32_t offset = OFF_OPER + 0x400 + XHCI_PORT_PR_SZ * i;
+ uint32_t offset = XHCI_REGS_OFFSET_PORT + XHCI_PORT_PR_SZ * i;
port->xhci = xhci;
memory_region_init_io(&port->mem, OBJECT(dev), &xhci_port_ops, port,
port->name, XHCI_PORT_PR_SZ);
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 3/8] tests/qtest/xhci: Add controller and device setup and ring tests
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 1/8] hw/usb/xhci: Move HCD constants to a header and add register constants Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 2/8] hw/usb/xhci: Rename and move HCD register region constants to header Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 4/8] hw/usb/xhci: Support TR NOOP commands Nicholas Piggin
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
Add tests which init the host controller registers to the point where
command and event rings, irqs are operational. Enumerate ports and set
up an attached device context that enables device transfer ring to be
set up and tested.
This test does a bunch of things at once and is not yet well librified,
but it allows testing basic mechanisms and gives a starting point for
further work.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/usb/hcd-xhci.h | 7 +
hw/usb/hcd-xhci.c | 7 -
tests/qtest/usb-hcd-xhci-test.c | 516 +++++++++++++++++++++++++++++++-
3 files changed, 517 insertions(+), 13 deletions(-)
diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h
index 20059fcf66c..02a005ce78d 100644
--- a/hw/usb/hcd-xhci.h
+++ b/hw/usb/hcd-xhci.h
@@ -350,6 +350,13 @@ typedef struct XHCIRing {
bool ccs;
} XHCIRing;
+typedef struct XHCIEvRingSeg {
+ uint32_t addr_low;
+ uint32_t addr_high;
+ uint32_t size;
+ uint32_t rsvd;
+} XHCIEvRingSeg;
+
typedef struct XHCIPort {
XHCIState *xhci;
uint32_t portsc;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 7470db38561..88973c485d1 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -128,13 +128,6 @@ struct XHCIEPContext {
QEMUTimer *kick_timer;
};
-typedef struct XHCIEvRingSeg {
- uint32_t addr_low;
- uint32_t addr_high;
- uint32_t size;
- uint32_t rsvd;
-} XHCIEvRingSeg;
-
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid, unsigned int streamid);
static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid);
diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c
index 0cccfd85a64..b9fb2356d26 100644
--- a/tests/qtest/usb-hcd-xhci-test.c
+++ b/tests/qtest/usb-hcd-xhci-test.c
@@ -8,17 +8,174 @@
*/
#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "libqtest.h"
+#include "libqos/libqos-pc.h"
#include "libqtest-single.h"
#include "libqos/usb.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/usb/hcd-xhci.h"
+
+/*** Test Setup & Teardown ***/
+typedef struct XHCIQSlotState {
+ /* In-memory arrays */
+ uint64_t device_context;
+ uint64_t transfer_ring;
+
+ uint32_t tr_trb_entries;
+ uint32_t tr_trb_idx;
+ uint32_t tr_trb_c;
+} XHCIQSlotState;
+
+typedef struct XHCIQState {
+ /* QEMU PCI variables */
+ QOSState *parent;
+ QPCIDevice *dev;
+ QPCIBar bar;
+ uint64_t barsize;
+ uint32_t fingerprint;
+
+ /* In-memory arrays */
+ uint64_t dc_base_array;
+ uint64_t command_ring;
+ uint64_t event_ring_seg;
+ uint64_t event_ring;
+
+ uint32_t cr_trb_entries;
+ uint32_t cr_trb_idx;
+ uint32_t cr_trb_c;
+ uint32_t er_trb_entries;
+ uint32_t er_trb_idx;
+ uint32_t er_trb_c;
+
+ /* Host controller properties */
+ uint32_t rtoff, dboff;
+ uint32_t maxports, maxslots, maxintrs;
+
+ XHCIQSlotState slots[32];
+} XHCIQState;
+
+#define XHCI_NEC_ID (PCI_DEVICE_ID_NEC_UPD720200 << 16 | \
+ PCI_VENDOR_ID_NEC)
+
+/**
+ * Locate, verify, and return a handle to the XHCI device.
+ */
+static QPCIDevice *get_xhci_device(QTestState *qts, uint32_t *fingerprint)
+{
+ QPCIDevice *xhci;
+ uint32_t xhci_fingerprint;
+ QPCIBus *pcibus;
+
+ pcibus = qpci_new_pc(qts, NULL);
+
+ /* Find the XHCI PCI device and verify it's the right one. */
+ xhci = qpci_device_find(pcibus, QPCI_DEVFN(0x1D, 0x0));
+ g_assert(xhci != NULL);
+
+ xhci_fingerprint = qpci_config_readl(xhci, PCI_VENDOR_ID);
+ switch (xhci_fingerprint) {
+ case XHCI_NEC_ID:
+ break;
+ default:
+ /* Unknown device. */
+ g_assert_not_reached();
+ }
+
+ if (fingerprint) {
+ *fingerprint = xhci_fingerprint;
+ }
+ return xhci;
+}
+
+static void free_xhci_device(QPCIDevice *dev)
+{
+ QPCIBus *pcibus = dev ? dev->bus : NULL;
+
+ /* libqos doesn't have a function for this, so free it manually */
+ g_free(dev);
+ qpci_free_pc(pcibus);
+}
+
+/**
+ * Start a Q35 machine and bookmark a handle to the XHCI device.
+ */
+G_GNUC_PRINTF(1, 0)
+static XHCIQState *xhci_vboot(const char *cli, va_list ap)
+{
+ XHCIQState *s;
+
+ s = g_new0(XHCIQState, 1);
+ s->parent = qtest_pc_vboot(cli, ap);
+ alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT);
+
+ /* Verify that we have an XHCI device present. */
+ s->dev = get_xhci_device(s->parent->qts, &s->fingerprint);
+ s->bar = qpci_iomap(s->dev, 0, &s->barsize);
+ /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */
+ qpci_device_enable(s->dev);
+
+ return s;
+}
+
+/**
+ * Start a Q35 machine and bookmark a handle to the XHCI device.
+ */
+G_GNUC_PRINTF(1, 2)
+static XHCIQState *xhci_boot(const char *cli, ...)
+{
+ XHCIQState *s;
+ va_list ap;
+
+ if (cli) {
+ va_start(ap, cli);
+ s = xhci_vboot(cli, ap);
+ va_end(ap);
+ } else {
+ s = xhci_boot("-M q35 "
+ "-device nec-usb-xhci,id=xhci,bus=pcie.0,addr=1d.0 "
+ "-drive id=drive0,if=none,file=null-co://,"
+ "file.read-zeroes=on,format=raw");
+ }
+
+ return s;
+}
+
+/**
+ * Clean up the PCI device, then terminate the QEMU instance.
+ */
+static void xhci_shutdown(XHCIQState *xhci)
+{
+ QOSState *qs = xhci->parent;
+
+ free_xhci_device(xhci->dev);
+ g_free(xhci);
+ qtest_shutdown(qs);
+}
+
+/*** tests ***/
static void test_xhci_hotplug(void)
{
- usb_test_hotplug(global_qtest, "xhci", "1", NULL);
+ XHCIQState *s;
+ QTestState *qts;
+
+ s = xhci_boot(NULL);
+ qts = s->parent->qts;
+
+ usb_test_hotplug(qts, "xhci", "1", NULL);
+
+ xhci_shutdown(s);
}
static void test_usb_uas_hotplug(void)
{
- QTestState *qts = global_qtest;
+ XHCIQState *s;
+ QTestState *qts;
+
+ s = xhci_boot(NULL);
+ qts = s->parent->qts;
qtest_qmp_device_add(qts, "usb-uas", "uas", "{}");
qtest_qmp_device_add(qts, "scsi-hd", "scsihd", "{'drive': 'drive0'}");
@@ -30,25 +187,371 @@ static void test_usb_uas_hotplug(void)
qtest_qmp_device_del(qts, "scsihd");
qtest_qmp_device_del(qts, "uas");
+
+ xhci_shutdown(s);
}
static void test_usb_ccid_hotplug(void)
{
- QTestState *qts = global_qtest;
+ XHCIQState *s;
+ QTestState *qts;
+
+ s = xhci_boot(NULL);
+ qts = s->parent->qts;
qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}");
qtest_qmp_device_del(qts, "ccid");
/* check the device can be added again */
qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}");
qtest_qmp_device_del(qts, "ccid");
+
+ xhci_shutdown(s);
+}
+
+static uint64_t xhci_guest_zalloc(XHCIQState *s, uint64_t size)
+{
+ char mem[0x1000];
+ uint64_t ret;
+
+ g_assert(size <= 0x1000);
+
+ memset(mem, 0, size);
+
+ ret = guest_alloc(&s->parent->alloc, size);
+ qtest_memwrite(s->parent->qts, ret, mem, size);
+
+ return ret;
+}
+
+static uint32_t xhci_cap_readl(XHCIQState *s, uint64_t addr)
+{
+ return qpci_io_readl(s->dev, s->bar, XHCI_REGS_OFFSET_CAP + addr);
}
+static uint32_t xhci_op_readl(XHCIQState *s, uint64_t addr)
+{
+ return qpci_io_readl(s->dev, s->bar, XHCI_REGS_OFFSET_OPER + addr);
+}
+
+static void xhci_op_writel(XHCIQState *s, uint64_t addr, uint32_t value)
+{
+ qpci_io_writel(s->dev, s->bar, XHCI_REGS_OFFSET_OPER + addr, value);
+}
+
+static uint32_t xhci_port_readl(XHCIQState *s, uint32_t port, uint64_t addr)
+{
+ return qpci_io_readl(s->dev, s->bar,
+ XHCI_REGS_OFFSET_PORT + port * XHCI_PORT_PR_SZ + addr);
+}
+
+static uint32_t xhci_rt_readl(XHCIQState *s, uint64_t addr)
+{
+ return qpci_io_readl(s->dev, s->bar, s->rtoff + addr);
+}
+
+static void xhci_rt_writel(XHCIQState *s, uint64_t addr, uint32_t value)
+{
+ qpci_io_writel(s->dev, s->bar, s->rtoff + addr, value);
+}
+
+static uint32_t xhci_intr_readl(XHCIQState *s, uint32_t intr, uint64_t addr)
+{
+ return xhci_rt_readl(s, XHCI_INTR_REG_IR0 +
+ intr * XHCI_INTR_IR_SZ + addr);
+}
+
+
+static void xhci_intr_writel(XHCIQState *s, uint32_t intr, uint64_t addr,
+ uint32_t value)
+{
+ xhci_rt_writel(s, XHCI_INTR_REG_IR0 +
+ intr * XHCI_INTR_IR_SZ + addr, value);
+}
+
+static void xhci_db_writel(XHCIQState *s, uint32_t db, uint32_t value)
+{
+ qpci_io_writel(s->dev, s->bar, s->dboff + db * XHCI_DBELL_DB_SZ, value);
+}
+
+static void wait_event_trb(XHCIQState *s, XHCITRB *trb)
+{
+ XHCITRB t;
+ uint64_t er_addr = s->event_ring + s->er_trb_idx * TRB_SIZE;
+ uint32_t value;
+ guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+
+ /* Wait for event interrupt */
+
+ do {
+ if (g_get_monotonic_time() >= end_time) {
+ g_error("Timeout expired");
+ }
+ qtest_clock_step(s->parent->qts, 10000);
+
+ value = xhci_op_readl(s, XHCI_OPER_REG_USBSTS);
+ } while (!(value & XHCI_USBSTS_EINT));
+
+ value = xhci_intr_readl(s, 0, XHCI_INTR_REG_IMAN);
+
+ /* With MSI-X enabled, IMAN IP is cleared after raising the interrupt */
+ g_assert(!(value & XHCI_IMAN_IP));
+
+ /* Ensure MSI-X interrupt is pending. */
+ /* XXX: this is never cleared so it doesn't verify multiple interrupts.
+ * should enable the msix vector like e1000e */
+ assert(qpci_msix_pending(s->dev, 0));
+
+ xhci_op_writel(s, XHCI_OPER_REG_USBSTS, XHCI_USBSTS_EINT); /* clear EINT */
+
+ qtest_memread(s->parent->qts, er_addr, &t, TRB_SIZE);
+
+ trb->parameter = le64_to_cpu(t.parameter);
+ trb->status = le32_to_cpu(t.status);
+ trb->control = le32_to_cpu(t.control);
+
+ g_assert((trb->status >> 24) == CC_SUCCESS);
+ g_assert((trb->control & TRB_C) == s->er_trb_c); /* C bit has been set */
+
+ s->er_trb_idx++;
+ if (s->er_trb_idx == s->er_trb_entries) {
+ s->er_trb_idx = 0;
+ s->er_trb_c ^= 1;
+ }
+ /* Update ERDP to processed TRB addr and EHB bit, which clears EHB */
+ er_addr = s->event_ring + s->er_trb_idx * TRB_SIZE;
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_ERDP_LO,
+ (er_addr & 0xffffffff) | XHCI_ERDP_EHB);
+}
+
+static void set_link_trb(XHCIQState *s, uint64_t ring, uint32_t c,
+ uint32_t entries)
+{
+ XHCITRB trb;
+
+ g_assert(entries > 1);
+
+ memset(&trb, 0, TRB_SIZE);
+ trb.parameter = cpu_to_le64(ring);
+ trb.control = cpu_to_le32(c | /* C */
+ (TR_LINK << TRB_TYPE_SHIFT) |
+ TRB_LK_TC);
+ qtest_memwrite(s->parent->qts, ring + TRB_SIZE * (entries - 1),
+ &trb, TRB_SIZE);
+}
+
+static void submit_cr_trb(XHCIQState *s, XHCITRB *trb)
+{
+ uint64_t cr_addr = s->command_ring + s->cr_trb_idx * TRB_SIZE;
+ XHCITRB t;
+
+ trb->control |= s->cr_trb_c; /* C */
+
+ t.parameter = cpu_to_le64(trb->parameter);
+ t.status = cpu_to_le32(trb->status);
+ t.control = cpu_to_le32(trb->control);
+
+ qtest_memwrite(s->parent->qts, cr_addr, &t, TRB_SIZE);
+ s->cr_trb_idx++;
+ /* Last entry contains the link, so wrap back */
+ if (s->cr_trb_idx == s->cr_trb_entries - 1) {
+ set_link_trb(s, s->command_ring, s->cr_trb_c, s->cr_trb_entries);
+ s->cr_trb_idx = 0;
+ s->cr_trb_c ^= 1;
+ }
+ xhci_db_writel(s, 0, 0); /* doorbell 0 */
+}
+
+/*
+ * This test brings up an endpoint and runs some noops through its command
+ * ring and gets responses back on the event ring.
+ *
+ * This could be librified in future (like AHCI0 to have a way to bring up
+ * an endpoint to test device protocols.
+ */
+static void pci_xhci_stress_rings(void)
+{
+ XHCIQState *s;
+ uint32_t value;
+ uint64_t input_context;
+ XHCIEvRingSeg ev_seg;
+ XHCITRB trb;
+ uint32_t hcsparams1;
+ uint32_t slotid;
+ g_autofree void *mem = g_malloc0(0x1000); /* buffer for writing to guest */
+ int i;
+
+ s = xhci_boot("-M q35 "
+ "-device nec-usb-xhci,id=xhci,bus=pcie.0,addr=1d.0 "
+ "-device usb-storage,bus=xhci.0,drive=drive0 "
+ "-drive id=drive0,if=none,file=null-co://,"
+ "file.read-zeroes=on,format=raw "
+ );
+
+ hcsparams1 = xhci_cap_readl(s, XHCI_HCCAP_REG_HCSPARAMS1);
+ s->maxports = (hcsparams1 >> 24) & 0xff;
+ s->maxintrs = (hcsparams1 >> 8) & 0x3ff;
+ s->maxslots = hcsparams1 & 0xff;
+
+ s->dboff = xhci_cap_readl(s, XHCI_HCCAP_REG_DBOFF);
+ s->rtoff = xhci_cap_readl(s, XHCI_HCCAP_REG_RTSOFF);
+
+ s->dc_base_array = xhci_guest_zalloc(s, 0x800);
+ s->command_ring = xhci_guest_zalloc(s, 0x1000);
+ s->event_ring = xhci_guest_zalloc(s, 0x1000);
+ s->event_ring_seg = xhci_guest_zalloc(s, 0x100);
+
+ /* Arbitrary small sizes so we can make them wrap */
+ s->cr_trb_entries = 0x20;
+ s->cr_trb_c = 1;
+ s->er_trb_entries = 0x10;
+ s->er_trb_c = 1;
+
+ ev_seg.addr_low = cpu_to_le32(s->event_ring & 0xffffffff);
+ ev_seg.addr_high = cpu_to_le32(s->event_ring >> 32);
+ ev_seg.size = cpu_to_le32(0x10);
+ ev_seg.rsvd = 0;
+ qtest_memwrite(s->parent->qts, s->event_ring_seg, &ev_seg, sizeof(ev_seg));
+
+ xhci_op_writel(s, XHCI_OPER_REG_USBCMD, XHCI_USBCMD_HCRST);
+ do {
+ value = xhci_op_readl(s, XHCI_OPER_REG_USBSTS);
+ } while (value & XHCI_USBSTS_CNR);
+
+ xhci_op_writel(s, XHCI_OPER_REG_CONFIG, s->maxslots);
+
+ xhci_op_writel(s, XHCI_OPER_REG_DCBAAP_LO, s->dc_base_array & 0xffffffff);
+ xhci_op_writel(s, XHCI_OPER_REG_DCBAAP_HI, s->dc_base_array >> 32);
+
+ xhci_op_writel(s, XHCI_OPER_REG_CRCR_LO,
+ (s->command_ring & 0xffffffff) | s->cr_trb_c);
+ xhci_op_writel(s, XHCI_OPER_REG_CRCR_HI, s->command_ring >> 32);
+
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_ERSTSZ, 1);
+
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_ERSTBA_LO,
+ s->event_ring_seg & 0xffffffff);
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_ERSTBA_HI,
+ s->event_ring_seg >> 32);
+
+ /* ERDP */
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_ERDP_LO, s->event_ring & 0xffffffff);
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_ERDP_HI, s->event_ring >> 32);
+
+ qpci_msix_enable(s->dev);
+ xhci_op_writel(s, XHCI_OPER_REG_USBCMD, XHCI_USBCMD_RS | XHCI_USBCMD_INTE);
+
+ /* Enable interrupts on ER IMAN */
+ xhci_intr_writel(s, 0, XHCI_INTR_REG_IMAN, XHCI_IMAN_IE);
+
+ assert(!qpci_msix_pending(s->dev, 0));
+
+ /* Wrap the command and event rings with no-ops a few times */
+ for (i = 0; i < 100; i++) {
+ /* Issue a command ring no-op */
+ memset(&trb, 0, TRB_SIZE);
+ trb.control |= CR_NOOP << TRB_TYPE_SHIFT;
+ trb.control |= TRB_TR_IOC;
+ submit_cr_trb(s, &trb);
+ wait_event_trb(s, &trb);
+ }
+
+ /* Query ports */
+ for (i = 0; i < s->maxports; i++) {
+ value = xhci_port_readl(s, i, 0); /* PORTSC */
+
+ /* Only first port should be attached and enabled */
+ if (i == 0) {
+ g_assert(value & XHCI_PORTSC_CCS);
+ g_assert(value & XHCI_PORTSC_PED);
+ /* Port Speed must be identified (non-zero) */
+ g_assert(((value >> XHCI_PORTSC_SPEED_SHIFT) &
+ XHCI_PORTSC_SPEED_MASK) != 0);
+ } else {
+ g_assert(!(value & XHCI_PORTSC_CCS));
+ g_assert(!(value & XHCI_PORTSC_PED));
+ g_assert(((value >> XHCI_PORTSC_PLS_SHIFT) &
+ XHCI_PORTSC_PLS_MASK) == 5);
+ }
+ }
+
+ /* Issue a command ring enable slot */
+ memset(&trb, 0, TRB_SIZE);
+ trb.control |= CR_ENABLE_SLOT << TRB_TYPE_SHIFT;
+ trb.control |= TRB_TR_IOC;
+ submit_cr_trb(s, &trb);
+ wait_event_trb(s, &trb);
+ slotid = (trb.control >> TRB_CR_SLOTID_SHIFT) & 0xff;
+
+ s->slots[slotid].transfer_ring = xhci_guest_zalloc(s, 0x1000);
+ s->slots[slotid].tr_trb_entries = 0x10;
+ s->slots[slotid].tr_trb_c = 1;
+
+ /* 32-byte input context size, should check HCCPARAMS1 for 64-byte size */
+ input_context = xhci_guest_zalloc(s, 0x420);
+
+ /* Set input control context */
+ ((uint32_t *)mem)[1] = cpu_to_le32(0x3); /* Add device contexts 0 and 1 */
+ ((uint32_t *)mem)[8] = cpu_to_le32(1 << 27); /* 1 context entry */
+ ((uint32_t *)mem)[9] = cpu_to_le32(1 << 16); /* 1 port number */
+
+ /* Set endpoint 0 context */
+ ((uint32_t *)mem)[16] = 0;
+ ((uint32_t *)mem)[17] = cpu_to_le32((ET_CONTROL << EP_TYPE_SHIFT) |
+ (0x200 << 16)); /* max packet sz XXX? */
+ ((uint32_t *)mem)[18] = cpu_to_le32((s->slots[slotid].transfer_ring &
+ 0xffffffff) | 1); /* DCS=1 */
+ ((uint32_t *)mem)[19] = cpu_to_le32(s->slots[slotid].transfer_ring >> 32);
+ ((uint32_t *)mem)[20] = cpu_to_le32(0x200); /* Average TRB length */
+ qtest_memwrite(s->parent->qts, input_context, mem, 0x420);
+
+ s->slots[slotid].device_context = xhci_guest_zalloc(s, 0x400);
+
+ ((uint64_t *)mem)[0] = cpu_to_le64(s->slots[slotid].device_context);
+ qtest_memwrite(s->parent->qts, s->dc_base_array + 8 * slotid, mem, 8);
+
+ /* Issue a command ring address device */
+ memset(&trb, 0, TRB_SIZE);
+ trb.parameter = input_context;
+ trb.control |= CR_ADDRESS_DEVICE << TRB_TYPE_SHIFT;
+ trb.control |= slotid << TRB_CR_SLOTID_SHIFT;
+ submit_cr_trb(s, &trb);
+ wait_event_trb(s, &trb);
+
+ /* XXX: Could check EP state is running */
+
+ /* Shut it down */
+ qpci_msix_disable(s->dev);
+
+ guest_free(&s->parent->alloc, s->slots[slotid].device_context);
+ guest_free(&s->parent->alloc, s->slots[slotid].transfer_ring);
+ guest_free(&s->parent->alloc, input_context);
+ guest_free(&s->parent->alloc, s->event_ring);
+ guest_free(&s->parent->alloc, s->event_ring_seg);
+ guest_free(&s->parent->alloc, s->command_ring);
+ guest_free(&s->parent->alloc, s->dc_base_array);
+
+ xhci_shutdown(s);
+}
+
+/* tests */
int main(int argc, char **argv)
{
int ret;
+ const char *arch;
g_test_init(&argc, &argv, NULL);
+ /* Check architecture */
+ arch = qtest_get_arch();
+ if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
+ g_test_message("Skipping test for non-x86");
+ return 0;
+ }
+
+ if (!qtest_has_device("nec-usb-xhci")) {
+ return 0;
+ }
+
qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug);
if (qtest_has_device("usb-uas")) {
qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
@@ -56,11 +559,12 @@ int main(int argc, char **argv)
if (qtest_has_device("usb-ccid")) {
qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug);
}
+ if (qtest_has_device("usb-storage")) {
+ qtest_add_func("/xhci/pci/xhci-stress-rings", pci_xhci_stress_rings);
+ }
- qtest_start("-device nec-usb-xhci,id=xhci"
- " -drive id=drive0,if=none,file=null-co://,"
- "file.read-zeroes=on,format=raw");
ret = g_test_run();
+
qtest_end();
return ret;
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 4/8] hw/usb/xhci: Support TR NOOP commands
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
` (2 preceding siblings ...)
2025-04-11 7:58 ` [PATCH v3 3/8] tests/qtest/xhci: Add controller and device setup and ring tests Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 5/8] tests/qtest/xhci: add a test for " Nicholas Piggin
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
Implement XHCI TR NOOP commands by setting up then immediately
completing the packet.
The IBM AIX XHCI HCD driver uses NOOP commands to check driver and
hardware health, which works after this change.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/usb/hcd-xhci.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 88973c485d1..b6f65628db7 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1663,6 +1663,20 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
return xhci_submit(xhci, xfer, epctx);
}
+static int xhci_noop_transfer(XHCIState *xhci, XHCITransfer *xfer)
+{
+ /*
+ * TR NOOP conceptually probably better not call into USB subsystem
+ * (usb_packet_setup() via xhci_setup_packet()). In practice it
+ * works and avoids code duplication.
+ */
+ if (xhci_setup_packet(xfer) < 0) {
+ return -1;
+ }
+ xhci_try_complete_packet(xfer);
+ return 0;
+}
+
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid, unsigned int streamid)
{
@@ -1785,6 +1799,8 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
epctx->kick_active++;
while (1) {
+ bool noop = false;
+
length = xhci_ring_chain_length(xhci, ring);
if (length <= 0) {
if (epctx->type == ET_ISO_OUT || epctx->type == ET_ISO_IN) {
@@ -1813,10 +1829,20 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
epctx->kick_active--;
return;
}
+ if (type == TR_NOOP) {
+ noop = true;
+ }
}
xfer->streamid = streamid;
- if (epctx->epid == 1) {
+ if (noop) {
+ if (length != 1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: NOOP TR TRB within TRB chain!\n", __func__);
+ /* Undefined behavior, we no-op the entire chain */
+ }
+ xhci_noop_transfer(xhci, xfer);
+ } else if (epctx->epid == 1) {
xhci_fire_ctl_transfer(xhci, xfer);
} else {
xhci_fire_transfer(xhci, xfer, epctx);
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 5/8] tests/qtest/xhci: add a test for TR NOOP commands
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
` (3 preceding siblings ...)
2025-04-11 7:58 ` [PATCH v3 4/8] hw/usb/xhci: Support TR NOOP commands Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 6/8] tests/qtest/xhci: test the qemu-xhci device Nicholas Piggin
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
Run some TR NOOP commands through the transfer ring.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
tests/qtest/usb-hcd-xhci-test.c | 36 ++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c
index b9fb2356d26..63359fb70b9 100644
--- a/tests/qtest/usb-hcd-xhci-test.c
+++ b/tests/qtest/usb-hcd-xhci-test.c
@@ -361,9 +361,33 @@ static void submit_cr_trb(XHCIQState *s, XHCITRB *trb)
xhci_db_writel(s, 0, 0); /* doorbell 0 */
}
+static void submit_tr_trb(XHCIQState *s, int slot, XHCITRB *trb)
+{
+ XHCIQSlotState *sl = &s->slots[slot];
+ uint64_t tr_addr = sl->transfer_ring + sl->tr_trb_idx * TRB_SIZE;
+ XHCITRB t;
+
+ trb->control |= sl->tr_trb_c; /* C */
+
+ t.parameter = cpu_to_le64(trb->parameter);
+ t.status = cpu_to_le32(trb->status);
+ t.control = cpu_to_le32(trb->control);
+
+ qtest_memwrite(s->parent->qts, tr_addr, &t, TRB_SIZE);
+ sl->tr_trb_idx++;
+ /* Last entry contains the link, so wrap back */
+ if (sl->tr_trb_idx == sl->tr_trb_entries - 1) {
+ set_link_trb(s, sl->transfer_ring, sl->tr_trb_c, sl->tr_trb_entries);
+ sl->tr_trb_idx = 0;
+ sl->tr_trb_c ^= 1;
+ }
+ xhci_db_writel(s, slot, 1); /* doorbell slot, EP0 target */
+}
+
/*
* This test brings up an endpoint and runs some noops through its command
- * ring and gets responses back on the event ring.
+ * ring and gets responses back on the event ring, then brings up a device
+ * context and runs some noops through its transfer ring.
*
* This could be librified in future (like AHCI0 to have a way to bring up
* an endpoint to test device protocols.
@@ -519,6 +543,16 @@ static void pci_xhci_stress_rings(void)
/* XXX: Could check EP state is running */
+ /* Wrap the transfer ring a few times */
+ for (i = 0; i < 100; i++) {
+ /* Issue a transfer ring slot 0 noop */
+ memset(&trb, 0, TRB_SIZE);
+ trb.control |= TR_NOOP << TRB_TYPE_SHIFT;
+ trb.control |= TRB_TR_IOC;
+ submit_tr_trb(s, slotid, &trb);
+ wait_event_trb(s, &trb);
+ }
+
/* Shut it down */
qpci_msix_disable(s->dev);
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 6/8] tests/qtest/xhci: test the qemu-xhci device
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
` (4 preceding siblings ...)
2025-04-11 7:58 ` [PATCH v3 5/8] tests/qtest/xhci: add a test for " Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 7/8] hw/usb/hcd-xhci-pci: Make PCI device more configurable Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 8/8] hw/usb/hcd-xhci-pci: Add TI TUSB73X0 XHCI controller model Nicholas Piggin
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
Add support in the test code for running multiple drivers, and add
tests for the qemu-xhci device.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
tests/qtest/usb-hcd-xhci-test.c | 96 +++++++++++++++++++++------------
1 file changed, 63 insertions(+), 33 deletions(-)
diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c
index 63359fb70b9..4efe7b69d4f 100644
--- a/tests/qtest/usb-hcd-xhci-test.c
+++ b/tests/qtest/usb-hcd-xhci-test.c
@@ -13,10 +13,15 @@
#include "libqos/libqos-pc.h"
#include "libqtest-single.h"
#include "libqos/usb.h"
+#include "hw/pci/pci.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
#include "hw/usb/hcd-xhci.h"
+typedef struct TestData {
+ const char *device;
+} TestData;
+
/*** Test Setup & Teardown ***/
typedef struct XHCIQSlotState {
/* In-memory arrays */
@@ -56,6 +61,8 @@ typedef struct XHCIQState {
XHCIQSlotState slots[32];
} XHCIQState;
+#define XHCI_QEMU_ID (PCI_DEVICE_ID_REDHAT_XHCI << 16 | \
+ PCI_VENDOR_ID_REDHAT)
#define XHCI_NEC_ID (PCI_DEVICE_ID_NEC_UPD720200 << 16 | \
PCI_VENDOR_ID_NEC)
@@ -76,6 +83,7 @@ static QPCIDevice *get_xhci_device(QTestState *qts, uint32_t *fingerprint)
xhci_fingerprint = qpci_config_readl(xhci, PCI_VENDOR_ID);
switch (xhci_fingerprint) {
+ case XHCI_QEMU_ID:
case XHCI_NEC_ID:
break;
default:
@@ -128,20 +136,21 @@ static XHCIQState *xhci_boot(const char *cli, ...)
XHCIQState *s;
va_list ap;
- if (cli) {
- va_start(ap, cli);
- s = xhci_vboot(cli, ap);
- va_end(ap);
- } else {
- s = xhci_boot("-M q35 "
- "-device nec-usb-xhci,id=xhci,bus=pcie.0,addr=1d.0 "
- "-drive id=drive0,if=none,file=null-co://,"
- "file.read-zeroes=on,format=raw");
- }
+ va_start(ap, cli);
+ s = xhci_vboot(cli, ap);
+ va_end(ap);
return s;
}
+static XHCIQState *xhci_boot_dev(const char *device)
+{
+ return xhci_boot("-M q35 "
+ "-device %s,id=xhci,bus=pcie.0,addr=1d.0 "
+ "-drive id=drive0,if=none,file=null-co://,"
+ "file.read-zeroes=on,format=raw", device);
+}
+
/**
* Clean up the PCI device, then terminate the QEMU instance.
*/
@@ -156,12 +165,13 @@ static void xhci_shutdown(XHCIQState *xhci)
/*** tests ***/
-static void test_xhci_hotplug(void)
+static void test_xhci_hotplug(const void *arg)
{
+ const TestData *td = arg;
XHCIQState *s;
QTestState *qts;
- s = xhci_boot(NULL);
+ s = xhci_boot_dev(td->device);
qts = s->parent->qts;
usb_test_hotplug(qts, "xhci", "1", NULL);
@@ -169,12 +179,13 @@ static void test_xhci_hotplug(void)
xhci_shutdown(s);
}
-static void test_usb_uas_hotplug(void)
+static void test_usb_uas_hotplug(const void *arg)
{
+ const TestData *td = arg;
XHCIQState *s;
QTestState *qts;
- s = xhci_boot(NULL);
+ s = xhci_boot_dev(td->device);
qts = s->parent->qts;
qtest_qmp_device_add(qts, "usb-uas", "uas", "{}");
@@ -191,12 +202,13 @@ static void test_usb_uas_hotplug(void)
xhci_shutdown(s);
}
-static void test_usb_ccid_hotplug(void)
+static void test_usb_ccid_hotplug(const void *arg)
{
+ const TestData *td = arg;
XHCIQState *s;
QTestState *qts;
- s = xhci_boot(NULL);
+ s = xhci_boot_dev(td->device);
qts = s->parent->qts;
qtest_qmp_device_add(qts, "usb-ccid", "ccid", "{}");
@@ -392,8 +404,9 @@ static void submit_tr_trb(XHCIQState *s, int slot, XHCITRB *trb)
* This could be librified in future (like AHCI0 to have a way to bring up
* an endpoint to test device protocols.
*/
-static void pci_xhci_stress_rings(void)
+static void test_xhci_stress_rings(const void *arg)
{
+ const TestData *td = arg;
XHCIQState *s;
uint32_t value;
uint64_t input_context;
@@ -405,11 +418,11 @@ static void pci_xhci_stress_rings(void)
int i;
s = xhci_boot("-M q35 "
- "-device nec-usb-xhci,id=xhci,bus=pcie.0,addr=1d.0 "
+ "-device %s,id=xhci,bus=pcie.0,addr=1d.0 "
"-device usb-storage,bus=xhci.0,drive=drive0 "
"-drive id=drive0,if=none,file=null-co://,"
- "file.read-zeroes=on,format=raw "
- );
+ "file.read-zeroes=on,format=raw ",
+ td->device);
hcsparams1 = xhci_cap_readl(s, XHCI_HCCAP_REG_HCSPARAMS1);
s->maxports = (hcsparams1 >> 24) & 0xff;
@@ -567,11 +580,37 @@ static void pci_xhci_stress_rings(void)
xhci_shutdown(s);
}
+static void add_test(const char *name, TestData *td, void (*fn)(const void *))
+{
+ g_autofree char *full_name = g_strdup_printf(
+ "/xhci/pci/%s/%s", td->device, name);
+ qtest_add_data_func(full_name, td, fn);
+}
+
+static void add_tests(TestData *td)
+{
+ add_test("hotplug", td, test_xhci_hotplug);
+ if (qtest_has_device("usb-uas")) {
+ add_test("usb-uas", td, test_usb_uas_hotplug);
+ }
+ if (qtest_has_device("usb-ccid")) {
+ add_test("usb-ccid", td, test_usb_ccid_hotplug);
+ }
+ if (qtest_has_device("usb-storage")) {
+ add_test("xhci-stress-rings", td, test_xhci_stress_rings);
+ }
+}
+
/* tests */
int main(int argc, char **argv)
{
int ret;
const char *arch;
+ int i;
+ TestData td[] = {
+ { .device = "qemu-xhci", },
+ { .device = "nec-usb-xhci", },
+ };
g_test_init(&argc, &argv, NULL);
@@ -582,19 +621,10 @@ int main(int argc, char **argv)
return 0;
}
- if (!qtest_has_device("nec-usb-xhci")) {
- return 0;
- }
-
- qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug);
- if (qtest_has_device("usb-uas")) {
- qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
- }
- if (qtest_has_device("usb-ccid")) {
- qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug);
- }
- if (qtest_has_device("usb-storage")) {
- qtest_add_func("/xhci/pci/xhci-stress-rings", pci_xhci_stress_rings);
+ for (i = 0; i < ARRAY_SIZE(td); i++) {
+ if (qtest_has_device(td[i].device)) {
+ add_tests(&td[i]);
+ }
}
ret = g_test_run();
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 7/8] hw/usb/hcd-xhci-pci: Make PCI device more configurable
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
` (5 preceding siblings ...)
2025-04-11 7:58 ` [PATCH v3 6/8] tests/qtest/xhci: test the qemu-xhci device Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
2025-04-11 7:58 ` [PATCH v3 8/8] hw/usb/hcd-xhci-pci: Add TI TUSB73X0 XHCI controller model Nicholas Piggin
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
To prepare to support another USB PCI Host Controller, make some PCI
configuration dynamic.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/usb/hcd-xhci-pci.h | 9 ++++
hw/usb/hcd-xhci-pci.c | 118 +++++++++++++++++++++++++++++++++---------
2 files changed, 103 insertions(+), 24 deletions(-)
diff --git a/hw/usb/hcd-xhci-pci.h b/hw/usb/hcd-xhci-pci.h
index 5b61ae84555..09aabae6e01 100644
--- a/hw/usb/hcd-xhci-pci.h
+++ b/hw/usb/hcd-xhci-pci.h
@@ -41,6 +41,15 @@ typedef struct XHCIPciState {
OnOffAuto msi;
OnOffAuto msix;
bool conditional_intr_mapping;
+ uint8_t cache_line_size;
+ uint8_t pm_cap_off;
+ uint8_t pcie_cap_off;
+ uint8_t msi_cap_off;
+ uint8_t msix_cap_off;
+ int msix_bar_nr;
+ uint64_t msix_bar_size;
+ uint32_t msix_table_off;
+ uint32_t msix_pba_off;
} XHCIPciState;
#endif
diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c
index d908eb787d3..eb918ce3d6e 100644
--- a/hw/usb/hcd-xhci-pci.c
+++ b/hw/usb/hcd-xhci-pci.c
@@ -32,9 +32,6 @@
#include "trace.h"
#include "qapi/error.h"
-#define OFF_MSIX_TABLE 0x3000
-#define OFF_MSIX_PBA 0x3800
-
static void xhci_pci_intr_update(XHCIState *xhci, int n, bool enable)
{
XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
@@ -120,6 +117,31 @@ static int xhci_pci_vmstate_post_load(void *opaque, int version_id)
return 0;
}
+static int xhci_pci_add_pm_capability(PCIDevice *pci_dev, uint8_t offset,
+ Error **errp)
+{
+ int err;
+
+ err = pci_add_capability(pci_dev, PCI_CAP_ID_PM, offset,
+ PCI_PM_SIZEOF, errp);
+ if (err < 0) {
+ return err;
+ }
+
+ pci_set_word(pci_dev->config + offset + PCI_PM_PMC,
+ PCI_PM_CAP_VER_1_2 |
+ PCI_PM_CAP_D1 | PCI_PM_CAP_D2 |
+ PCI_PM_CAP_PME_D0 | PCI_PM_CAP_PME_D1 |
+ PCI_PM_CAP_PME_D2 | PCI_PM_CAP_PME_D3hot);
+ pci_set_word(pci_dev->wmask + offset + PCI_PM_PMC, 0);
+ pci_set_word(pci_dev->config + offset + PCI_PM_CTRL,
+ PCI_PM_CTRL_NO_SOFT_RESET);
+ pci_set_word(pci_dev->wmask + offset + PCI_PM_CTRL,
+ PCI_PM_CTRL_STATE_MASK);
+
+ return 0;
+}
+
static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp)
{
int ret;
@@ -128,7 +150,7 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp)
dev->config[PCI_CLASS_PROG] = 0x30; /* xHCI */
dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
- dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
+ dev->config[PCI_CACHE_LINE_SIZE] = s->cache_line_size;
dev->config[0x60] = 0x30; /* release number */
object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL);
@@ -144,40 +166,78 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp)
s->xhci.nec_quirks = true;
}
- if (s->msi != ON_OFF_AUTO_OFF) {
- ret = msi_init(dev, 0x70, s->xhci.numintrs, true, false, &err);
- /*
- * Any error other than -ENOTSUP(board's MSI support is broken)
- * is a programming error
- */
- assert(!ret || ret == -ENOTSUP);
- if (ret && s->msi == ON_OFF_AUTO_ON) {
- /* Can't satisfy user's explicit msi=on request, fail */
- error_append_hint(&err, "You have to use msi=auto (default) or "
- "msi=off with this machine type.\n");
+ if (s->pm_cap_off) {
+ if (xhci_pci_add_pm_capability(dev, s->pm_cap_off, &err)) {
error_propagate(errp, err);
return;
}
- assert(!err || s->msi == ON_OFF_AUTO_AUTO);
- /* With msi=auto, we fall back to MSI off silently */
- error_free(err);
}
+
+ if (s->msi != ON_OFF_AUTO_OFF) {
+ ret = msi_init(dev, s->msi_cap_off, s->xhci.numintrs,
+ true, false, &err);
+ if (ret) {
+ if (ret != -ENOTSUP) {
+ /* Programming error */
+ error_propagate(errp, err);
+ return;
+ }
+ if (s->msi == ON_OFF_AUTO_ON) {
+ /* Can't satisfy user's explicit msi=on request, fail */
+ error_append_hint(&err, "You have to use msi=auto (default) "
+ "or msi=off with this machine type.\n");
+ error_propagate(errp, err);
+ return;
+ }
+ error_free(err);
+ err = NULL; /* With msi=auto, we fall back to MSI off silently */
+ }
+ }
+
pci_register_bar(dev, 0,
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64,
&s->xhci.mem);
if (pci_bus_is_express(pci_get_bus(dev))) {
- ret = pcie_endpoint_cap_init(dev, 0xa0);
+ ret = pcie_endpoint_cap_init(dev, s->pcie_cap_off);
assert(ret > 0);
}
if (s->msix != ON_OFF_AUTO_OFF) {
- /* TODO check for errors, and should fail when msix=on */
- msix_init(dev, s->xhci.numintrs,
- &s->xhci.mem, 0, OFF_MSIX_TABLE,
- &s->xhci.mem, 0, OFF_MSIX_PBA,
- 0x90, NULL);
+ MemoryRegion *msix_bar = &s->xhci.mem;
+
+ if (s->msix_bar_nr != 0) {
+ memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev),
+ "xhci-msix", s->msix_bar_size);
+ msix_bar = &dev->msix_exclusive_bar;
+ pci_register_bar(dev, s->msix_bar_nr,
+ PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_64,
+ msix_bar);
+ }
+
+ ret = msix_init(dev, s->xhci.numintrs,
+ msix_bar, s->msix_bar_nr, s->msix_table_off,
+ msix_bar, s->msix_bar_nr, s->msix_pba_off,
+ s->msix_cap_off, &err);
+ if (ret) {
+ if (ret != -ENOTSUP) {
+ /* Programming error */
+ error_propagate(errp, err);
+ return;
+ }
+ if (s->msix == ON_OFF_AUTO_ON) {
+ /* Can't satisfy user's explicit msix=on request, fail */
+ error_append_hint(&err, "You have to use msix=auto (default) "
+ "or msix=off with this machine type.\n");
+ error_propagate(errp, err);
+ return;
+ }
+ error_free(err);
+ err = NULL; /* With msix=auto, we fall back to MSI-X off silently */
+ /* Should we unregister BAR and memory region here? */
+ }
}
s->xhci.as = pci_get_address_space(dev);
}
@@ -214,6 +274,16 @@ static void xhci_instance_init(Object *obj)
PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
object_initialize_child(obj, "xhci-core", &s->xhci, TYPE_XHCI);
qdev_alias_all_properties(DEVICE(&s->xhci), obj);
+
+ s->cache_line_size = 0x10;
+ s->pm_cap_off = 0;
+ s->pcie_cap_off = 0xa0;
+ s->msi_cap_off = 0x70;
+ s->msix_cap_off = 0x90;
+ s->msix_bar_nr = 0;
+ s->msix_bar_size = 0;
+ s->msix_table_off = 0x3000;
+ s->msix_pba_off = 0x3800;
}
static const Property xhci_pci_properties[] = {
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 8/8] hw/usb/hcd-xhci-pci: Add TI TUSB73X0 XHCI controller model
2025-04-11 7:58 [PATCH v3 0/8] usb/xhci: TR NOOP, TI HCD device, more qtests Nicholas Piggin
` (6 preceding siblings ...)
2025-04-11 7:58 ` [PATCH v3 7/8] hw/usb/hcd-xhci-pci: Make PCI device more configurable Nicholas Piggin
@ 2025-04-11 7:58 ` Nicholas Piggin
7 siblings, 0 replies; 9+ messages in thread
From: Nicholas Piggin @ 2025-04-11 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Nicholas Piggin, Paolo Bonzini, Michael S. Tsirkin,
Marcel Apfelbaum, Fabiano Rosas, Laurent Vivier,
Phil Dennis-Jordan, Bernhard Beschow
The TI TUSB73X0 controller has some interesting differences from NEC,
notably a separate BAR for MSIX, and PM capabilities. The spec is freely
available without sign-up.
This controller is accepted by IBM Power proprietary firmware and
software (when the subsystem IDs are set to Power servers, which is not
done here). IBM code is picky about device support, so the NEC device
can not be used.
xhci qtests are added for this device.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
include/hw/pci/pci_ids.h | 1 +
include/hw/usb/xhci.h | 1 +
hw/usb/hcd-xhci-ti.c | 88 +++++++++++++++++++++++++++++++++
tests/qtest/usb-hcd-xhci-test.c | 4 ++
hw/usb/Kconfig | 5 ++
hw/usb/meson.build | 1 +
6 files changed, 100 insertions(+)
create mode 100644 hw/usb/hcd-xhci-ti.c
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 33e2898be95..99fe751703f 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -182,6 +182,7 @@
#define PCI_VENDOR_ID_HP 0x103c
#define PCI_VENDOR_ID_TI 0x104c
+#define PCI_DEVICE_ID_TI_TUSB73X0 0x8241
#define PCI_VENDOR_ID_MOTOROLA 0x1057
#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002
diff --git a/include/hw/usb/xhci.h b/include/hw/usb/xhci.h
index 5c90e1373e5..203ec1fca32 100644
--- a/include/hw/usb/xhci.h
+++ b/include/hw/usb/xhci.h
@@ -3,6 +3,7 @@
#define TYPE_XHCI "base-xhci"
#define TYPE_NEC_XHCI "nec-usb-xhci"
+#define TYPE_TI_XHCI "ti-usb-xhci"
#define TYPE_QEMU_XHCI "qemu-xhci"
#define TYPE_XHCI_SYSBUS "sysbus-xhci"
diff --git a/hw/usb/hcd-xhci-ti.c b/hw/usb/hcd-xhci-ti.c
new file mode 100644
index 00000000000..9ad9d6edf7a
--- /dev/null
+++ b/hw/usb/hcd-xhci-ti.c
@@ -0,0 +1,88 @@
+/*
+ * USB xHCI controller emulation
+ * Datasheet https://www.ti.com/product/TUSB7340
+ *
+ * Copyright (c) 2011 Securiforest
+ * Date: 2011-05-11 ; Author: Hector Martin <hector@marcansoft.com>
+ * Based on usb-xhci-nec.c, emulates TI TUSB73X0
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/usb.h"
+#include "qemu/module.h"
+#include "hw/pci/pci.h"
+#include "hw/qdev-properties.h"
+
+#include "hcd-xhci-pci.h"
+
+OBJECT_DECLARE_SIMPLE_TYPE(XHCITiState, TI_XHCI)
+
+struct XHCITiState {
+ XHCIPciState parent_obj;
+
+ uint32_t intrs;
+ uint32_t slots;
+};
+
+static const Property ti_xhci_properties[] = {
+ DEFINE_PROP_UINT32("intrs", XHCITiState, intrs, 8),
+ DEFINE_PROP_UINT32("slots", XHCITiState, slots, XHCI_MAXSLOTS),
+};
+
+static void ti_xhci_instance_init(Object *obj)
+{
+ XHCIPciState *pci = XHCI_PCI(obj);
+ XHCITiState *ti = TI_XHCI(obj);
+
+ pci->xhci.numintrs = ti->intrs;
+ pci->xhci.numslots = ti->slots;
+
+ pci->cache_line_size = 0x0;
+ pci->pm_cap_off = 0x40;
+ pci->pcie_cap_off = 0x70;
+ pci->msi_cap_off = 0x48;
+ pci->msix_cap_off = 0xc0;
+ pci->msix_bar_nr = 0x2;
+ pci->msix_bar_size = 0x800000;
+ pci->msix_table_off = 0x0;
+ pci->msix_pba_off = 0x1000;
+}
+
+static void ti_xhci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ device_class_set_props(dc, ti_xhci_properties);
+ k->vendor_id = PCI_VENDOR_ID_TI;
+ k->device_id = PCI_DEVICE_ID_TI_TUSB73X0;
+ k->revision = 0x02;
+}
+
+static const TypeInfo ti_xhci_info = {
+ .name = TYPE_TI_XHCI,
+ .parent = TYPE_XHCI_PCI,
+ .instance_size = sizeof(XHCITiState),
+ .instance_init = ti_xhci_instance_init,
+ .class_init = ti_xhci_class_init,
+};
+
+static void ti_xhci_register_types(void)
+{
+ type_register_static(&ti_xhci_info);
+}
+
+type_init(ti_xhci_register_types)
diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c
index 4efe7b69d4f..dc438cf35c7 100644
--- a/tests/qtest/usb-hcd-xhci-test.c
+++ b/tests/qtest/usb-hcd-xhci-test.c
@@ -65,6 +65,8 @@ typedef struct XHCIQState {
PCI_VENDOR_ID_REDHAT)
#define XHCI_NEC_ID (PCI_DEVICE_ID_NEC_UPD720200 << 16 | \
PCI_VENDOR_ID_NEC)
+#define XHCI_TI_ID (PCI_DEVICE_ID_TI_TUSB73X0 << 16 | \
+ PCI_VENDOR_ID_TI)
/**
* Locate, verify, and return a handle to the XHCI device.
@@ -85,6 +87,7 @@ static QPCIDevice *get_xhci_device(QTestState *qts, uint32_t *fingerprint)
switch (xhci_fingerprint) {
case XHCI_QEMU_ID:
case XHCI_NEC_ID:
+ case XHCI_TI_ID:
break;
default:
/* Unknown device. */
@@ -610,6 +613,7 @@ int main(int argc, char **argv)
TestData td[] = {
{ .device = "qemu-xhci", },
{ .device = "nec-usb-xhci", },
+ { .device = "ti-usb-xhci", },
};
g_test_init(&argc, &argv, NULL);
diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
index 69c663be52f..00d82a97211 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -49,6 +49,11 @@ config USB_XHCI_NEC
default y if PCI_DEVICES
select USB_XHCI_PCI
+config USB_XHCI_TI
+ bool
+ default y if PCI_DEVICES
+ select USB_XHCI_PCI
+
config USB_XHCI_SYSBUS
bool
select USB_XHCI
diff --git a/hw/usb/meson.build b/hw/usb/meson.build
index 17360a5b5a4..375fa420be6 100644
--- a/hw/usb/meson.build
+++ b/hw/usb/meson.build
@@ -23,6 +23,7 @@ system_ss.add(when: 'CONFIG_USB_XHCI', if_true: files('hcd-xhci.c'))
system_ss.add(when: 'CONFIG_USB_XHCI_PCI', if_true: files('hcd-xhci-pci.c'))
system_ss.add(when: 'CONFIG_USB_XHCI_SYSBUS', if_true: files('hcd-xhci-sysbus.c'))
system_ss.add(when: 'CONFIG_USB_XHCI_NEC', if_true: files('hcd-xhci-nec.c'))
+system_ss.add(when: 'CONFIG_USB_XHCI_TI', if_true: files('hcd-xhci-ti.c'))
system_ss.add(when: 'CONFIG_USB_DWC2', if_true: files('hcd-dwc2.c'))
system_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c'))
system_ss.add(when: 'CONFIG_USB_CHIPIDEA', if_true: files('chipidea.c'))
--
2.47.1
^ permalink raw reply related [flat|nested] 9+ messages in thread