Page MenuHomeFreeBSD

D53472.diff
No OneTemporary

D53472.diff

diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -1,6 +1,7 @@
LOADER_NET_SUPPORT?= yes
LOADER_MSDOS_SUPPORT?= yes
LOADER_UFS_SUPPORT?= yes
+LOADER_USBDBC_SUPPORT?= no
LOADER_CD9660_SUPPORT?= no
LOADER_EXT2FS_SUPPORT?= no
@@ -30,6 +31,13 @@
gfx_fb.c \
8x16.c
+.if ${LOADER_USBDBC_SUPPORT} == "yes"
+.PATH: ${SRCTOP}/sys/dev/usb/controller
+SRCS+= xhci_dbc.c \
+ xhci_dbc_cons.c \
+ xhci_dbc_dma.c
+.endif
+
SRCS+= acpi_detect.c
.PATH: ${EFISRC}/acpica
CFLAGS+= -I${EFISRC}/acpica/include
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -64,6 +64,15 @@
#include "geliboot.h"
#endif
+#if defined(LOADER_USBDBC_SUPPORT)
+#include <dev/usb/controller/xhci_private.h>
+#include <dev/usb/controller/xhcireg.h>
+#include "xhci_dbc_pci.h" /* device_t */
+#include <dev/usb/controller/xhci_dbc.h>
+#include <dev/usb/controller/xhci_dbc_private.h>
+#include "xhci_dbc_cons.h" /* udb_sc */
+#endif /* usb dbc */
+
static int
bi_getboothowto(char *kargs)
{
@@ -449,6 +458,16 @@
file_addmetadata(kfp, MODINFOMD_EFI_ARCH, sizeof(MACHINE_ARCH),
MACHINE_ARCH);
#endif
+#if defined(LOADER_USBDBC_SUPPORT)
+ /*
+ * Pass the xhci debug struct through to the kernel if we have managed
+ * to configure it.
+ */
+ if (udb_sc0 != NULL) {
+ file_addmetadata(kfp, MODINFOMD_XHCI_DEBUG,
+ sizeof(struct xhci_debug_softc *), &udb_sc0);
+ }
+#endif
#endif
#ifdef LOADER_GELI_SUPPORT
geli_export_key_metadata(kfp);
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
--- a/stand/efi/loader/conf.c
+++ b/stand/efi/loader/conf.c
@@ -78,6 +78,9 @@
extern struct console efi_console;
extern struct console eficom;
+#if defined(LOADER_USBDBC_SUPPORT)
+extern struct console udb_console;
+#endif
#if defined(__aarch64__) && __FreeBSD_version < 1500000
/* Hack for backward compatibility -- but only for a while */
extern struct console comconsole;
@@ -91,6 +94,9 @@
struct console *consoles[] = {
&efi_console,
&eficom,
+#if defined(LOADER_USBDBC_SUPPORT)
+ &udb_console,
+#endif
#if defined(__aarch64__) && __FreeBSD_version < 1500000
&comconsole,
#endif
diff --git a/stand/efi/loader/xhci_dbc_cons.h b/stand/efi/loader/xhci_dbc_cons.h
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/xhci_dbc_cons.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+extern struct console udb_console;
+
+extern struct xhci_debug_softc *udb_sc0;
+extern struct xhci_debug_softc *udb_sc;
+
+extern const char *udb_hostname;
+extern const char *udb_serial;
diff --git a/stand/efi/loader/xhci_dbc_cons.c b/stand/efi/loader/xhci_dbc_cons.c
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/xhci_dbc_cons.c
@@ -0,0 +1,309 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/param.h>
+#include <dev/pci/pcireg.h>
+#include <dev/usb/controller/xhcireg.h>
+
+#include <bootstrap.h>
+#include <efi.h>
+#include <efilib.h>
+
+#include "xhci_dbc_cons.h"
+#include "xhci_dbc_pci.h"
+#include "xhci_dbc_dma.h"
+#include <dev/usb/controller/xhci_pci.h>
+#include <dev/usb/controller/xhci_private.h>
+#include <dev/usb/controller/xhci_dbc.h>
+#include <dev/usb/controller/xhci_dbc_private.h>
+
+static void udbc_probe(struct console *);
+static bool udbc_alloc(struct console *, EFI_PCI_IO_PROTOCOL *, EFI_HANDLE *);
+
+static int udbc_getc(void);
+static void udbc_putc(int);
+static int udbc_ischar(void);
+static int udbc_init(int);
+struct console udb_console = {
+ .c_name = "udbc",
+ .c_desc = "USB DbC serial port",
+ .c_flags = 0,
+ .c_probe = udbc_probe,
+ .c_init = udbc_init,
+ .c_out = udbc_putc,
+ .c_in = udbc_getc,
+ .c_ready = udbc_ischar
+};
+
+struct xhci_debug_softc *udb_sc0; /* linked-list */
+struct xhci_debug_softc *udb_sc; /* active instance */
+const char *udb_hostname;
+const char *udb_serial;
+
+static void
+udbc_probe(struct console *cons)
+{
+ EFI_STATUS status;
+ EFI_PCI_IO_PROTOCOL *pciio;
+ EFI_HANDLE *h0, *h;
+ UINTN hlen;
+ device_t dev;
+
+ h0 = h = NULL;
+ if (udb_sc0 != NULL)
+ return;
+
+ /* Get PCI I/O handle. */
+ hlen = 0;
+ status = BS->LocateHandleBuffer(
+ ByProtocol,
+ &pciio_guid,
+ NULL,
+ &hlen,
+ &h0);
+ if (EFI_ERROR(status))
+ goto error;
+
+ for (UINTN i = 0; i < hlen; i++) {
+ h = h0[i];
+ status = BS->HandleProtocol(
+ h,
+ &pciio_guid,
+ (VOID **)&pciio);
+ if (EFI_ERROR(status))
+ continue;
+ status = pciio->Pci.Read(
+ pciio,
+ EfiPciIoWidthUint32,
+ 0,
+ sizeof(dev) >> 2, /* 32-bit access */
+ &dev);
+ if (EFI_ERROR(status))
+ continue;
+ if (pci_get_headertype(dev) != PCIM_HDRTYPE_NORMAL &&
+ pci_get_headertype(dev) != PCIM_MFDEV)
+ continue;
+ if (xhci_pci_match(dev) != NULL)
+ (void) udbc_alloc(cons, pciio, h);
+ }
+
+ if (udb_sc0 != NULL) {
+ cons->c_flags = C_PRESENTIN | C_PRESENTOUT;
+ return;
+ }
+error:
+ DEBUG_PRINTF(1, ("%s: Compatible xHC not found.\n", __func__));
+ (void) BS->FreePool(h0);
+ return;
+}
+
+static bool
+udbc_alloc(struct console *cons, EFI_PCI_IO_PROTOCOL *pciio, EFI_HANDLE *h)
+{
+ struct xhci_debug_softc *sc;
+ UINTN SegmentNumber, BusNumber, DeviceNumber, FunctionNumber;
+ EFI_STATUS status;
+ int error;
+
+ if (pciio == NULL || h == NULL)
+ return (false);
+ status = pciio->GetLocation(
+ pciio,
+ &SegmentNumber,
+ &BusNumber,
+ &DeviceNumber,
+ &FunctionNumber);
+ if (EFI_ERROR(status))
+ return (false);
+ sc = udb_alloc_softc(sizeof(*sc), h, pciio);
+ if (sc == NULL)
+ return (false);
+ sc->sc_pci_rid = PCI_RID(BusNumber, DeviceNumber, FunctionNumber);
+ sc->sc_efi_pciio = pciio;
+ sc->sc_efi_hand = h;
+
+ sc->sc_dbc_off = xhci_debug_get_xecp(sc);
+ if (sc->sc_dbc_off == 0)
+ goto error;
+
+ xhci_debug_disable(sc);
+ xhci_debug_update_state(sc);
+
+ DEBUG_PRINTF(2, ("%s: before init\n", __func__));
+ if (sc->sc_init == false) {
+ error = xhci_debug_init_dma(sc);
+ if (error) {
+ DEBUG_PRINTF(1, ("USB DbC DMA configuration error\n"));
+ goto error;
+ }
+ error = xhci_debug_init_ring(sc);
+ if (error) {
+ DEBUG_PRINTF(1, ("USB DbC TRB ring configuration error\n"));
+ goto error;
+ }
+ sc->sc_init = true;
+ sc->sc_cookie = XHCI_DC_COOKIE;
+ }
+ xhci_debug_enable(sc);
+
+ /* Add a new entry */
+ if (udb_sc0 != NULL)
+ sc->sc_next = udb_sc0;
+ udb_sc0 = sc;
+
+ (void) udbc_init(0); /* XXX: Asks udbc to init now */
+
+ return (true);
+error:
+ /* XXX: free softc */
+ return (false);
+}
+
+static int
+udbc_init(int arg)
+{
+ struct xhci_debug_softc *sc;
+ uint32_t rid;
+ char *p, *endp;
+ bool gdb;
+
+ if (udb_sc != NULL) /* already initialized */
+ return (CMD_OK);
+
+ /* Initialize all instances */
+ for (sc = udb_sc0; sc != NULL; sc = sc->sc_next)
+ xhci_debug_enable(sc);
+
+ /* If called in c_probe, udb_sc will not be initialized. */
+ if (arg == 1)
+ return (CMD_OK);
+
+ p = getenv("hw.usb.xhci.dbc.enable");
+ if (p != NULL && p[0] == '0')
+ return (CMD_OK);
+
+ p = getenv("hw.usb.xhci.dbc.debug");
+ if (p != NULL) {
+ dbc_debug = strtol(p, &endp, 0);
+ if (*endp != '\0')
+ dbc_debug = 0;
+ }
+
+ rid = 0;
+ p = getenv("hw.usb.xhci.dbc.pci_rid");
+ if (p != NULL) {
+ rid = strtol(p, &endp, 0);
+ if (*endp != '\0')
+ rid = 0;
+ }
+
+ /* XXX: too late */
+ udb_hostname = getenv("hw.usb.xhci.dbc.hostname");
+ udb_serial = getenv("hw.usb.xhci.dbc.serial");
+ p = getenv("hw.usb.xhci.dbc.gdb");
+ gdb = (p != NULL && p[0] != '0');
+
+ /* Initialize flags */
+ for (sc = udb_sc0; sc != NULL; sc = sc->sc_next)
+ if (gdb)
+ sc->sc_flags |= XHCI_DEBUG_FLAGS_GDB;
+
+ for (sc = udb_sc0; sc != NULL; sc = sc->sc_next) {
+ /* If RID is not specified, use the first one. */
+ if ((rid == 0 || rid == sc->sc_pci_rid) &&
+ sc->sc_dbc_off != 0)
+ break;
+ }
+ if (sc == NULL) {
+ DEBUG_PRINTF(1, ("USB DbC not found\n"));
+ return (CMD_ERROR);
+ }
+
+ /* Define the console */
+ udb_sc = sc;
+ xhci_debug_event_dequeue(sc);
+
+ return (CMD_OK);
+}
+
+static void
+udbc_putc(int c0)
+{
+ struct xhci_debug_softc *sc = udb_sc;
+ u_char c = (0xff & c0);
+
+ if (sc == NULL)
+ return;
+
+ /* XXXTHJ: Try to avoid getting stuck */
+ while (DC_WORK_RING_FULL(&sc->udb_oring)) {
+ xhci_debug_bulk_transfer(sc);
+ xhci_debug_event_dequeue(sc);
+ delay(100);
+ }
+
+ if (work_enqueue(&sc->udb_oring, &c, sizeof(c)) != sizeof(c))
+ return;
+
+ xhci_debug_bulk_transfer(sc);
+ /*
+ * This dequeue just after the transfer is important.
+ * Do not remove this.
+ */
+ xhci_debug_event_dequeue(sc);
+}
+
+/* udbc_getc() is called periodically. */
+static int
+udbc_getc(void)
+{
+ struct xhci_debug_softc *sc = udb_sc;
+
+ if (sc == NULL)
+ return (-1);
+
+ /* Use udbc_ischar() to receive data */
+ return (udbc_ischar() ? work_dequeue(&sc->udb_iring) : -1);
+}
+
+static int
+udbc_ischar(void)
+{
+ struct xhci_debug_softc *sc = udb_sc;
+ struct xhci_debug_ring *iring;
+
+ if (sc == NULL)
+ return (0);
+ iring = &sc->udb_iring;
+ if (!DC_WORK_RING_EMPTY(iring))
+ return (1);
+
+ xhci_debug_bulk_transfer(sc);
+ xhci_debug_event_dequeue(sc);
+
+ return (!DC_WORK_RING_EMPTY(iring));
+}
diff --git a/stand/efi/loader/xhci_dbc_dma.h b/stand/efi/loader/xhci_dbc_dma.h
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/xhci_dbc_dma.h
@@ -0,0 +1,32 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+int xhci_debug_init_dma(struct xhci_debug_softc *);
+void xhci_debug_uninit_dma(struct xhci_debug_softc *);
+
+struct xhci_debug_softc *udb_alloc_softc(size_t, EFI_HANDLE *,
+ EFI_PCI_IO_PROTOCOL *);
diff --git a/stand/efi/loader/xhci_dbc_dma.c b/stand/efi/loader/xhci_dbc_dma.c
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/xhci_dbc_dma.c
@@ -0,0 +1,298 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/param.h>
+#include <dev/pci/pcireg.h>
+#include <dev/usb/controller/xhcireg.h>
+#include <machine/atomic.h>
+
+#include <assert.h>
+
+#include <bootstrap.h>
+#include <efi.h>
+#include <efilib.h>
+#include <dev/usb/controller/xhci_private.h>
+#include "xhci_dbc_pci.h"
+#include <dev/usb/controller/xhci_dbc.h>
+#include <dev/usb/controller/xhci_dbc_private.h>
+#include "xhci_dbc_cons.h"
+#include "xhci_dbc_dma.h"
+
+EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
+
+static void *udb_alloc_dma(struct xhci_debug_softc *, uint64_t);
+static void udb_free_dma(struct xhci_debug_softc *, VOID *);
+static uint64_t v2p(struct xhci_debug_softc *, void *);
+
+uint64_t
+v2p(struct xhci_debug_softc *sc, void *virt)
+{
+ UINTN i;
+ UINTN p;
+ UINTN offset;
+ UINTN needed;
+ UINTN mapped;
+ struct dma *dma;
+ EFI_STATUS status;
+ EFI_PHYSICAL_ADDRESS paddr;
+ VOID *mapping;
+
+ dma = NULL;
+ for (i = 0; i < nitems(sc->dma_desc); i++) {
+ dma = &sc->dma_desc[i];
+
+ DEBUG_PRINTF(1, ("%s: dma_desc[%lu].cpu_addr = %p\n", __func__,
+ i, dma->cpu_addr));
+ for (p = 0; p < dma->pages; p++) {
+ UINTN addr = (UINTN)dma->cpu_addr + (p * PAGE_SIZE);
+
+ DEBUG_PRINTF(1, ("%s: check addr: %lu = %lu + %lu * %u\n",
+ __func__, addr, (UINTN)dma->cpu_addr, p, PAGE_SIZE));
+ if ((UINTN)virt == addr) {
+ offset = addr - (UINTN)dma->cpu_addr;
+ goto found;
+ }
+ }
+ dma = NULL;
+ }
+ if (dma == NULL) {
+ DEBUG_PRINTF(1, ("CPU addr %p not found in DMA descriptor\n", virt));
+ return 0;
+ }
+
+found:
+#if 0
+ DEBUG_PRINTF(1, "%s: found, i = %lu, dma->paddr = %lu, offset = %lu\n",
+ __func__, i, dma->paddr, offset);
+#endif
+ if (dma->paddr && dma->mapping)
+ return (dma->paddr + offset);
+
+ needed = dma->pages << EFI_PAGE_SHIFT;
+ mapped = needed;
+ status = sc->sc_efi_pciio->Map(sc->sc_efi_pciio,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ (void *)virt, &mapped, &paddr, &mapping);
+ if (EFI_ERROR(status) || mapped != needed) {
+ DEBUG_PRINTF(1, ("pciio->Map failed: rc: 0x%lx, mapped: %lu, needed: %lu\n",
+ status, mapped, needed));
+ return (0);
+ }
+ dma->paddr = paddr;
+ dma->mapping = mapping;
+
+ if ((const void *)paddr != virt) {
+ DEBUG_PRINTF(1, ("Non-identity DMA mapping: dma: 0x%lx cpu: %p\n",
+ paddr, virt));
+ }
+
+ return (paddr);
+}
+
+struct xhci_debug_softc *
+udb_alloc_softc(size_t len, EFI_HANDLE *h, EFI_PCI_IO_PROTOCOL *pciio)
+{
+ struct xhci_debug_softc *sc;
+ EFI_STATUS status;
+ VOID *addr;
+ int pages;
+
+ DEBUG_PRINTF(2, ("%s: called\n", __func__));
+ sc = NULL;
+ pages = sizeof(*sc) / PAGE_SIZE + 1;
+ status = pciio->AllocateBuffer(pciio,
+ AllocateAnyPages,
+ EfiRuntimeServicesData,
+ sizeof(*sc) / PAGE_SIZE + 1,
+ &addr,
+ EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED);
+ if (EFI_ERROR(status)) {
+ DEBUG_PRINTF(1, ("%s: AllocateBuffer failed: 0x%lx\n",
+ __func__, status));
+ return (NULL);
+ }
+ memset(addr, 0, sizeof(*sc));
+ sc = (struct xhci_debug_softc *)addr;
+
+ sc->dma_desc[0].pages = pages;
+ sc->dma_desc[0].cpu_addr = addr;
+ sc->sc_efi_pciio = pciio;
+ sc->sc_efi_hand = h;
+ sc->sc_init_dma = false;
+ sc->sc_init = false;
+ if (udb_hostname != NULL)
+ strlcpy(sc->sc_hostname, udb_hostname, sizeof(sc->sc_hostname));
+ if (udb_serial != NULL)
+ strlcpy(sc->sc_serialno, udb_serial, sizeof(sc->sc_serialno));
+
+ return (sc);
+}
+
+int
+xhci_debug_init_dma(struct xhci_debug_softc *sc)
+{
+#define ALLOC_DMA(p, type, order) \
+ do { \
+ p = (type)udb_alloc_dma(sc, order); \
+ if (p == NULL) { \
+ xhci_debug_uninit_dma(sc); \
+ return (1); \
+ } \
+ } while (0)
+
+ if (sc->sc_init_dma)
+ return (0);
+
+ ALLOC_DMA(sc->udb_ctx, struct xhci_debug_ctx *, 0);
+ ALLOC_DMA(sc->udb_erst, struct xhci_event_ring_seg *, 0);
+ ALLOC_DMA(sc->udb_str, char *, 0);
+ ALLOC_DMA(sc->udb_ering.trb, struct xhci_trb *, DC_TRB_RING_ORDER);
+ ALLOC_DMA(sc->udb_iring.trb, struct xhci_trb *, DC_TRB_RING_ORDER);
+ ALLOC_DMA(sc->udb_oring.trb, struct xhci_trb *, DC_TRB_RING_ORDER);
+ ALLOC_DMA(sc->udb_ering.work.buf, uint8_t *, DC_WORK_RING_ORDER);
+ ALLOC_DMA(sc->udb_iring.work.buf, uint8_t *, DC_WORK_RING_ORDER);
+ ALLOC_DMA(sc->udb_oring.work.buf, uint8_t *, DC_WORK_RING_ORDER);
+
+ /* Export CTX, ERST, and STR */
+#define SETADDR(n, len) do { \
+ sc->udb_##n##_paddr = (uintptr_t)v2p(sc, sc->udb_##n); \
+ sc->udb_##n##_len = len; \
+ } while (0)
+ SETADDR(ctx, PAGE_SIZE);
+ SETADDR(erst, PAGE_SIZE);
+ SETADDR(str, PAGE_SIZE);
+
+ /* Export TRB rings. */
+#define SETADDR_RING(n, paddr, lenp, buf, len) do { \
+ sc->udb_##paddr = (uintptr_t)v2p(sc, sc->udb_##buf); \
+ sc->udb_##lenp = len; \
+ } while (0)
+
+ SETADDR_RING(ering, ering.paddr, ering.len, ering.trb,
+ PAGE_SIZE * (1UL << DC_TRB_RING_ORDER));
+ SETADDR_RING(iring, iring.paddr, iring.len, iring.trb,
+ PAGE_SIZE * (1UL << DC_TRB_RING_ORDER));
+ SETADDR_RING(oring, oring.paddr, oring.len, oring.trb,
+ PAGE_SIZE * (1UL << DC_TRB_RING_ORDER));
+ SETADDR_RING(ering.work, ering.work.paddr, ering.work.len,
+ ering.work.buf, PAGE_SIZE * (1UL << DC_WORK_RING_ORDER));
+ SETADDR_RING(iring.work, iring.work.paddr, iring.work.len,
+ iring.work.buf, PAGE_SIZE * (1UL << DC_WORK_RING_ORDER));
+ SETADDR_RING(oring.work, oring.work.paddr, oring.work.len,
+ oring.work.buf, PAGE_SIZE * (1UL << DC_WORK_RING_ORDER));
+
+ sc->sc_init_dma = true;
+
+ return (0);
+#undef ALLOC_DMA
+}
+
+static void *
+udb_alloc_dma(struct xhci_debug_softc *sc, uint64_t order)
+{
+ struct dma *dma;
+ EFI_STATUS status;
+ VOID *addr;
+ UINTN pages = 1UL << order;
+ UINTN i;
+
+ for (i = 0; i < nitems(sc->dma_desc); i++) {
+ dma = &sc->dma_desc[i];
+ if (dma->cpu_addr == NULL)
+ break;
+ }
+ if (i == nitems(sc->dma_desc)) {
+ DEBUG_PRINTF(1, ("%s: Out of DMA descriptor\n", __func__));
+ return (NULL);
+ }
+ status = sc->sc_efi_pciio->AllocateBuffer(sc->sc_efi_pciio,
+ AllocateAnyPages,
+ EfiRuntimeServicesData,
+ pages,
+ &addr,
+ EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED);
+ if (EFI_ERROR(status)) {
+ DEBUG_PRINTF(1, ("%s: AllocateBuffer failed: 0x%lx\n",
+ __func__, status));
+ return (NULL);
+ }
+
+ dma->pages = pages;
+ dma->cpu_addr = addr;
+ DEBUG_PRINTF(1, ("%s: i = %lu, cpu_addr = %p, pages = %lu\n", __func__,
+ i, dma->cpu_addr, pages));
+
+ return (addr);
+}
+
+static void
+udb_free_dma(struct xhci_debug_softc *sc, VOID *addr)
+{
+ struct dma *dma;
+ EFI_STATUS status;
+ UINTN i;
+
+ for (i = 0; i < nitems(sc->dma_desc); i++) {
+ dma = &sc->dma_desc[i];
+ if (dma->cpu_addr == addr)
+ break;
+ }
+ if (i == nitems(sc->dma_desc))
+ return;
+ if (dma->mapping) {
+ status = sc->sc_efi_pciio->Unmap(sc->sc_efi_pciio,
+ dma->mapping);
+ if (EFI_ERROR(status)) {
+ DEBUG_PRINTF(1, ("%s: Unmap failed: 0x%lx\n",
+ __func__, status));
+ return;
+ }
+ }
+ status = sc->sc_efi_pciio->FreeBuffer(sc->sc_efi_pciio, dma->pages,
+ addr);
+ if (EFI_ERROR(status)) {
+ DEBUG_PRINTF(1, ("%s: FreeBuffer failed: 0x%lx\n",
+ __func__, status));
+ return;
+ }
+ memset(&dma, 0, sizeof(*dma));
+}
+
+void
+xhci_debug_uninit_dma(struct xhci_debug_softc *sc)
+{
+ udb_free_dma(sc, sc->udb_ctx);
+ udb_free_dma(sc, sc->udb_erst);
+ udb_free_dma(sc, sc->udb_ering.trb);
+ udb_free_dma(sc, sc->udb_oring.trb);
+ udb_free_dma(sc, sc->udb_iring.trb);
+ udb_free_dma(sc, sc->udb_ering.work.buf);
+ udb_free_dma(sc, sc->udb_oring.work.buf);
+ udb_free_dma(sc, sc->udb_iring.work.buf);
+
+ sc->sc_init_dma = false;
+}
diff --git a/stand/efi/loader/xhci_dbc_pci.h b/stand/efi/loader/xhci_dbc_pci.h
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/xhci_dbc_pci.h
@@ -0,0 +1,94 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+extern EFI_GUID pciio_guid;
+
+/*
+ * Dummy functions to use xhci_pci_match(). Note that device_t is defined
+ * as PCI Type 00 header.
+ */
+typedef struct {
+ UINT16 VendorId;
+ UINT16 DeviceId;
+ UINT16 Command;
+ UINT16 Status;
+ UINT8 RevisionID;
+ UINT8 ClassCode[3];
+ UINT8 CacheLineSize;
+ UINT8 LatencyTimer;
+ UINT8 HeaderType;
+ UINT8 BIST;
+} PCI_DEVICE_INDEPENDENT_REGION;
+
+typedef struct {
+ UINT32 Bar[6];
+ UINT32 CISPtr;
+ UINT16 SubsystemVendorID;
+ UINT16 SubsystemID;
+ UINT32 ExpansionRomBar;
+ UINT8 CapabilityPtr;
+ UINT8 Reserved1[3];
+ UINT32 Reserved2;
+ UINT8 InterruptLine;
+ UINT8 InterruptPin;
+ UINT8 MinGnt;
+ UINT8 MaxLat;
+} PCI_DEVICE_HEADER_TYPE_REGION;
+
+typedef struct {
+ PCI_DEVICE_INDEPENDENT_REGION Hdr;
+ PCI_DEVICE_HEADER_TYPE_REGION Device;
+} PCI_TYPE00;
+
+typedef PCI_TYPE00 device_t;
+
+static inline uint32_t
+pci_get_devid(device_t dev)
+{
+ return ((dev.Hdr.DeviceId << 16) | dev.Hdr.VendorId);
+}
+static inline uint8_t
+pci_get_class(device_t dev)
+{
+ return (dev.Hdr.ClassCode[0]);
+}
+static inline uint8_t
+pci_get_subclass(device_t dev)
+{
+ return (dev.Hdr.ClassCode[1]);
+}
+static inline uint8_t
+pci_get_progif(device_t dev)
+{
+ return (dev.Hdr.ClassCode[2]);
+}
+static inline uint8_t
+pci_get_headertype(device_t dev)
+{
+ return (dev.Hdr.HeaderType);
+}
diff --git a/stand/efi/loader_ia32/Makefile b/stand/efi/loader_ia32/Makefile
--- a/stand/efi/loader_ia32/Makefile
+++ b/stand/efi/loader_ia32/Makefile
@@ -1,5 +1,6 @@
DO32=1
INSTALL_LOADER_HELP_FILE=no
+LOADER_USBDBC_SUPPORT=no
NEWVERSWHAT?= "EFI loader" amd64-ia32
diff --git a/stand/loader.mk b/stand/loader.mk
--- a/stand/loader.mk
+++ b/stand/loader.mk
@@ -53,6 +53,7 @@
# LOADER_NFS_SUPPORT Add NFS support
# LOADER_TFTP_SUPPORT Add TFTP support
# LOADER_UFS_SUPPORT Add support for UFS filesystems
+# LOADER_USBDBC_SUPPORT Add USB debug capability support
# LOADER_ZFS_SUPPORT Add support for ZFS filesystems
#
@@ -131,6 +132,11 @@
CFLAGS+= -DLOADER_BZIP2_SUPPORT
.endif
+# Debugging
+.if ${LOADER_USBDBC_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_USBDBC_SUPPORT
+.endif
+
# Network related things
.if ${LOADER_NET_SUPPORT:Uno} == "yes"
CFLAGS+= -DLOADER_NET_SUPPORT
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2508,6 +2508,8 @@
device uark
# USB support for Belkin F5U103 and compatible serial adapters
device ubsa
+# USB support for XHCI USB DbC.GP serial device
+device udbc
# USB support for serial adapters based on the FT8U100AX and FT8U232AM
device uftdi
# USB support for some Windows CE based serial communication.
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3396,6 +3396,7 @@
dev/usb/serial/ubser.c optional ubser
dev/usb/serial/uchcom.c optional uchcom
dev/usb/serial/ucycom.c optional ucycom
+dev/usb/serial/udbc.c optional udbc
dev/usb/serial/ufoma.c optional ufoma
dev/usb/serial/uftdi.c optional uftdi
dev/usb/serial/ugensa.c optional ugensa
@@ -3410,10 +3411,10 @@
dev/usb/serial/uvisor.c optional uvisor
dev/usb/serial/uvscom.c optional uvscom
dev/usb/serial/usb_serial.c optional ucom | u3g | uark | ubsa | ubser | \
- uchcom | ucycom | ufoma | uftdi | \
- ugensa | uipaq | umcs | umct | \
- umodem | umoscom | uplcom | usie | \
- uslcom | uvisor | uvscom
+ uchcom | ucycom | udbc | ufoma | \
+ uftdi | ugensa | uipaq | umcs | \
+ umct | umodem | umoscom | uplcom | \
+ usie | uslcom | uvisor | uvscom
#
# USB misc drivers
#
diff --git a/sys/dev/usb/controller/xhci_dbc.h b/sys/dev/usb/controller/xhci_dbc.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/controller/xhci_dbc.h
@@ -0,0 +1,231 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+extern int dbc_debug;
+
+#ifndef _KERNEL
+/* for struct mtx in xhci_debug_softc */
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+inline void mtx_lock_spin(struct mtx *m) { }
+inline int mtx_trylock_spin(struct mtx *m) { return (1); }
+inline void mtx_unlock_spin(struct mtx *m) { }
+#endif
+
+/* DbC idVendor and idProduct */
+#define DC_VENDOR 0x1d6b /* Linux Foundation */
+#define DC_PRODUCT 0x0011 /* Linux */
+#define DC_PROTOCOL 0x0000 /* GNU GDB = 1 */
+#define DC_REVISION 0x0010 /* 0.10 */
+
+#define DC_STRING_MANUFACTURER "The FreeBSD Foundation"
+#define DC_STRING_PRODUCT "FreeBSD USB DbC.GP Device"
+#define DC_STRING_SERIAL "12345678"
+
+/* DbC endpoint types */
+enum {
+ EP_BULK_OUT = 2,
+ EP_BULK_IN = 6
+};
+
+#define DC_TRB_PER_PAGE (XHCI_PAGE_SIZE / sizeof(struct xhci_trb))
+
+/* Defines the size in bytes of TRB rings as 2^DC_TRB_RING_ORDER * 4096 */
+#define DC_TRB_RING_ORDER 4
+#define DC_TRB_RING_LEN (DC_TRB_PER_PAGE * (1ULL << DC_TRB_RING_ORDER))
+#define DC_TRB_RING_OFFSET_MASK (DC_TRB_RING_LEN - 1U)
+#define DC_TRB_RING_LAST (DC_TRB_RING_LEN - 1U)
+#define DC_TRB_RING_BYTES (DC_TRB_RING_LEN * sizeof(struct xhci_trb))
+#define DC_TRB_RING_MASK (DC_TRB_RING_BYTES - 1U)
+
+/* Defines the size in bytes of work rings as 2^WORK_RING_ORDER * 4096 */
+#define DC_WORK_RING_ORDER 3
+#define DC_WORK_RING_LEN (XHCI_PAGE_SIZE * (1ULL << DC_WORK_RING_ORDER))
+#define DC_WORK_RING_OFFSET_MASK (DC_WORK_RING_LEN - 1U)
+#define DC_WORK_RING_LAST (DC_WORK_RING_LEN - 1U)
+#define DC_WORK_RING_BYTES DC_WORK_RING_LEN
+
+#define DC_WORK_RING_FULL(r) \
+ ((((r)->work.w_enq + 1) & DC_WORK_RING_OFFSET_MASK) == (r)->work.w_deq)
+#define DC_WORK_RING_EMPTY(r) \
+ ((r)->work.w_enq == (r)->work.w_deq)
+#define XHCI_DC_RING_FULL(r) \
+ ((((r)->enq + 1) & DC_TRB_RING_OFFSET_MASK) == (r)->deq)
+#define XHCI_DC_RING_EMPTY(r) \
+ ((r)->enq == (r)->deq)
+/* TRB ring has a link TRB at the tail of the queue. */
+#define XHCI_DC_RING_SLOTS(r) ( \
+ (((r)->deq <= (r)->enq) ? (r)->deq + DC_TRB_RING_LEN - 1 : (r)->deq) \
+ - (r)->enq)
+#define DC_WORK_RING_SLOTS(r) ( \
+ (((r)->w_deq <= (r)->w_enq) ? (r)->w_deq + DC_WORK_RING_LEN : (r)->w_deq) \
+ - (r)->w_enq)
+
+struct xhci_debug_ctx {
+ uint32_t info[16];
+ struct xhci_endp_ctx64 ep_out;
+ struct xhci_endp_ctx64 ep_in;
+};
+
+struct xhci_debug_work_ring {
+ uint8_t *buf;
+ uint32_t w_enq;
+ uint32_t w_deq;
+ uint64_t paddr;
+ uint64_t len;
+};
+
+struct xhci_debug_ring {
+ struct xhci_trb *trb; /* Array of TRBs */
+ uint64_t paddr; /* base address of TRBs */
+ uint64_t len; /* length of TRBs */
+ uint32_t enq; /* The offset of the enqueue ptr */
+ uint32_t deq; /* The offset of the dequeue ptr */
+ uint8_t cyc; /* Cycle state toggled on each wrap-around */
+ uint32_t doorbell; /* Doorbell target */
+#define XHCI_DC_RING_IN(ring) ((ring)->doorbell == XHCI_DCDB_IN)
+#define XHCI_DC_RING_OUT(ring) ((ring)->doorbell == XHCI_DCDB_OUT)
+#define XHCI_DC_RING_EV(ring) ((ring)->doorbell == XHCI_DCDB_INVALID)
+ struct xhci_debug_work_ring work;
+
+ struct mtx mtx;
+ uint32_t flags;
+#define XHCI_DC_RING_F_IN_PROGRESS 0x0001
+ uint32_t lasterror;
+ uint32_t lastenq;
+ uint32_t lastdeq;
+/*
+ * Bitmap for completion report.
+ *
+ * Set when enqueuing a normal TRB, and
+ * reset when receiving the corrensponding transfer event TRB.
+ */
+ uint8_t iocbitmap[DC_TRB_RING_LEN / 8 + 1];
+#define IOCBITMAP_GET(ring, n) \
+ (((ring)->iocbitmap[(n) / 8] >> ((uint8_t)(n) % 8)) & (uint8_t)1)
+#define IOCBITMAP_SET(ring, n) \
+ ((ring)->iocbitmap[(n) / 8] |= ((uint8_t)1 << (uint8_t)((n) % 8)))
+#define IOCBITMAP_CLEAR(ring, n) \
+ ((ring)->iocbitmap[(n) / 8] &= ~((uint8_t)1 << (uint8_t)((n) % 8)))
+
+ uint64_t ec[256]; /* error counter */
+#define XHCI_TRB_ERROR_EVLOCKED 0x30
+#define XHCI_TRB_ERROR_CALLED 0x31
+#define XHCI_TRB_ERROR_WORKQ_FULL 0x32
+#define XHCI_TRB_ERROR_UNKNOWN 0xff
+};
+
+#ifndef _KERNEL
+#define DMA_DESC_CAP 16
+struct dma {
+ UINTN pages;
+ EFI_PHYSICAL_ADDRESS paddr;
+ VOID *cpu_addr;
+ VOID *mapping;
+};
+#endif
+
+/*
+ * This softc is used in kernel and loader.
+ */
+struct xhci_softc;
+struct udbcons_priv;
+#define XHCI_DC_COOKIE 0x4d79b718
+struct xhci_debug_softc {
+ struct xhci_debug_softc *sc_next;
+ bool sc_next_fixup;
+ struct mtx sc_mtx;
+ uint32_t sc_cookie;
+
+ struct xhci_debug_ctx *udb_ctx;
+ uint64_t udb_ctx_paddr;
+ uint64_t udb_ctx_len;
+
+ struct xhci_event_ring_seg *udb_erst;
+ uint64_t udb_erst_paddr;
+ uint64_t udb_erst_len;
+
+ struct xhci_debug_ring udb_ering;
+ struct xhci_debug_ring udb_oring;
+ struct xhci_debug_ring udb_iring;
+
+ char *udb_str;
+ uint64_t udb_str_paddr;
+ uint64_t udb_str_len;
+
+ uint32_t sc_state;
+ uint32_t sc_flags;
+#define XHCI_DEBUG_FLAGS_GDB 0x0001
+ uint64_t sc_polling_count;
+#define XHCI_DC_POLLING_TIME 50
+ uint32_t sc_polling_time;
+ uint64_t sc_dequeue_count;
+ bool sc_init;
+ bool sc_init_dma;
+ bool sc_fixup_done;
+ bool sc_open;
+ char sc_hostname[256];
+ char sc_serialno[256];
+
+ /* In kernel, sc_*_off in struct xhci_softc is used. */
+ struct xhci_softc *sc_xhci; /* used in _X{READ,WRITE} */
+ uint32_t sc_dbc_off;
+ uint32_t sc_capa_off; /* zero */
+ uint32_t sc_pci_rid;
+
+#ifdef _KERNEL
+ device_t sc_dev; /* used in kernel only */
+ struct udbcons_priv *sc_cons; /* used in kernel only */
+ uint8_t dummy[1456]; /* XXX: pad to 10240 bytes */
+#else
+ struct console *sc_cons; /* used in loader only */
+ EFI_PCI_IO_PROTOCOL *sc_efi_pciio;
+ EFI_HANDLE sc_efi_hand;
+ struct dma dma_desc[DMA_DESC_CAP];
+ uint8_t dummy[936]; /* XXX: pad to 10240 bytes */
+#endif
+};
+
+
+void xhci_debug_reg_read(struct xhci_debug_softc *);
+void xhci_debug_reg_restore(struct xhci_debug_softc *);
+
+uint32_t xhci_debug_get_xecp(struct xhci_debug_softc *);
+bool xhci_debug_enable(struct xhci_debug_softc *);
+void xhci_debug_disable(struct xhci_debug_softc *);
+
+int xhci_debug_init_ring(struct xhci_debug_softc *);
+uint32_t xhci_debug_update_state(struct xhci_debug_softc *);
+
+uint64_t xhci_debug_bulk_transfer(struct xhci_debug_softc *);
+void xhci_debug_event_dequeue(struct xhci_debug_softc *);
+
+int work_dequeue(struct xhci_debug_ring *);
+int64_t work_enqueue(struct xhci_debug_ring *, const char *, int64_t);
+
+void udb_dump_info(struct xhci_debug_softc *);
diff --git a/sys/dev/usb/controller/xhci_dbc.c b/sys/dev/usb/controller/xhci_dbc.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/controller/xhci_dbc.c
@@ -0,0 +1,1126 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/param.h>
+#include <sys/condvar.h>
+#include <sys/systm.h> /* getenv_quad */
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <machine/atomic.h>
+
+#ifndef _KERNEL
+#include <bootstrap.h>
+#include <efi.h>
+#include <efilib.h>
+#include <xhci_dbc_pci.h> /* device_t */
+#include <dev/usb/controller/xhci_private.h>
+#include <dev/usb/controller/xhci_dbc.h>
+#include "xhci_dbc_cons.h"
+#include "xhci_dbc_dma.h"
+#endif
+
+static void flush_range(void *, uint32_t);
+
+static bool xhci_debug_update_regbit(struct xhci_debug_softc *, int, int, int,
+ bool);
+static bool xhci_debug_set_regbit(struct xhci_debug_softc *, int, int, int);
+static bool xhci_debug_reset_regbit(struct xhci_debug_softc *, int, int, int);
+
+static bool xhci_debug_wait_connection(struct xhci_debug_softc *, uint32_t);
+
+static struct xhci_debug_softc * xhci_debug_alloc_softc(device_t);
+static bool xhci_debug_softc_fixup(struct xhci_debug_softc *);
+static void xhci_debug_ring_init(struct xhci_debug_softc *,
+ struct xhci_debug_ring *, int, uint32_t);
+struct usb_strdesc;
+static void atou16cpy(struct usb_strdesc *, const char *);
+static void xhci_debug_init_strings(struct xhci_debug_softc *, uint32_t [16]);
+
+static void xhci_debug_ring_doorbell(struct xhci_debug_softc *, uint32_t);
+static uint64_t xhci_debug_bulk_transfer0(struct xhci_debug_softc *,
+ struct xhci_debug_ring *);
+
+static void xhci_debug_init_ep(struct xhci_endp_ctx64 *, uint64_t, uint32_t,
+ uint64_t);
+
+#if 0
+static bool xhci_debug_event_ready(struct xhci_debug_softc *);
+#endif
+static uint32_t xhci_debug_ring_boundary(struct xhci_debug_ring *, bool);
+
+static void xhci_debug_transfer_event_handler(struct xhci_debug_softc *,
+ uint64_t, uint32_t, uint32_t);
+static uint64_t trb_tx_enqueue_locked(struct xhci_debug_softc *sc,
+ struct xhci_debug_ring *);
+static uint64_t trb_rx_enqueue_locked(struct xhci_debug_softc *sc,
+ struct xhci_debug_ring *);
+static uint64_t trb_push_locked(struct xhci_debug_softc *sc,
+ struct xhci_debug_ring *, uint64_t, uint64_t);
+
+/* sc_state */
+#define STRSTATE(x) [XHCI_DCPORT_ST_##x] = #x
+static const char *strstate[] = {
+ STRSTATE(OFF),
+ STRSTATE(DISCONNECTED_RUNNING), /* not in spec */
+ STRSTATE(DISCONNECTED),
+ STRSTATE(DISABLED),
+ STRSTATE(RESETTING),
+ STRSTATE(ENABLED),
+ STRSTATE(CONFIGURED)
+};
+
+/* sysctls */
+
+int dbc_debug;
+int dbc_reset;
+int dbc_enable = 1;
+int dbc_baud = 115200;
+int dbc_gdb;
+uint32_t dbc_pci_rid;
+
+vm_paddr_t udbc_console;
+
+static struct xhci_debug_reg {
+ uint32_t erstsz;
+ uint64_t erstba;
+ uint64_t erdp;
+ uint64_t cp;
+ uint32_t ddi1;
+ uint32_t ddi2;
+} udbc_reg;
+
+void
+xhci_debug_reg_read(struct xhci_debug_softc *sc)
+{
+
+ if (sc == NULL)
+ return;
+
+ udbc_reg.erstsz = _XREAD4(sc, dbc, XHCI_DCERSTSZ);
+ udbc_reg.erstba = _XREAD44LH(sc, dbc, XHCI_DCERSTBA);
+ udbc_reg.erdp = _XREAD44LH(sc, dbc, XHCI_DCERDP);
+ udbc_reg.cp = _XREAD44LH(sc, dbc, XHCI_DCCP);
+ udbc_reg.ddi1 = _XREAD4(sc, dbc, XHCI_DCDDI1);
+ udbc_reg.ddi2 = _XREAD4(sc, dbc, XHCI_DCDDI2);
+}
+void
+xhci_debug_reg_restore(struct xhci_debug_softc *sc)
+{
+
+ if (sc == NULL)
+ return;
+
+ _XWRITE4(sc, dbc, XHCI_DCERSTSZ, udbc_reg.erstsz);
+ _XWRITE44LH(sc, dbc, XHCI_DCERSTBA, udbc_reg.erstba);
+ _XWRITE44LH(sc, dbc, XHCI_DCERDP, udbc_reg.erdp);
+ _XWRITE44LH(sc, dbc, XHCI_DCCP, udbc_reg.cp);
+ _XWRITE4(sc, dbc, XHCI_DCDDI1, udbc_reg.ddi1);
+ _XWRITE4(sc, dbc, XHCI_DCDDI2, udbc_reg.ddi2);
+}
+
+static void
+flush_range(void *ptr, uint32_t bytes)
+{
+#ifndef _KERNEL
+ uint32_t i;
+ const uint32_t clshft = 6;
+ const uint32_t clsize = (1UL << clshft);
+ const uint32_t clmask = clsize - 1;
+ uint32_t lines = (bytes >> clshft);
+ lines += (bytes & clmask) != 0;
+
+ if (bytes == 0)
+ return;
+
+# if defined(__amd64__)
+#define CLFLUSH(ptr) \
+ __asm volatile("clflush %0" : "+m"(*(volatile char *)ptr))
+
+ for (i = 0; i < lines; i++)
+ CLFLUSH((void *)((uint64_t)ptr + (i * clsize)));
+#undef CLFLUSH
+#endif
+#endif
+}
+
+uint32_t
+xhci_debug_update_state(struct xhci_debug_softc *sc)
+{
+ uint32_t ctrl;
+ uint32_t portsc;
+ uint32_t sc_state_old;
+
+ ctrl = _XREAD4(sc, dbc, XHCI_DCCTRL);
+ portsc = _XREAD4(sc, dbc, XHCI_DCPORTSC);
+ sc_state_old = sc->sc_state;
+ sc->sc_state = XHCI_DCSTATUS(ctrl, portsc);
+ if (sc->sc_state != sc_state_old)
+ device_printf(sc->sc_dev, "state: %s(0x%02x) -> %s(0x%02x)\n",
+ strstate[sc_state_old], sc_state_old,
+ strstate[sc->sc_state], sc->sc_state);
+ return (sc->sc_state);
+}
+
+static bool
+xhci_debug_update_regbit(struct xhci_debug_softc *sc, int reg, int bit,
+ int timeout, bool reset)
+{
+ uint32_t temp;
+ int expected;
+
+ timeout *= 10; /* ms */
+ temp = le32toh(_XREAD4(sc, dbc, reg));
+ if (reset)
+ temp &= ~bit;
+ else
+ temp |= bit;
+ _XWRITE4(sc, dbc, reg, htole32(temp));
+ expected = (reset) ? 0 : bit;
+ while ((le32toh(_XREAD4(sc, dbc, reg)) & bit) != expected &&
+ timeout != 0) {
+ delay(100);
+ timeout--;
+ }
+ return (timeout > 0);
+}
+static bool
+xhci_debug_set_regbit(struct xhci_debug_softc *sc, int reg, int bit,
+ int timeout)
+{
+
+ return (xhci_debug_update_regbit(sc, reg, bit, timeout, false));
+}
+static bool
+xhci_debug_reset_regbit(struct xhci_debug_softc *sc, int reg, int bit,
+ int timeout)
+{
+
+ return (xhci_debug_update_regbit(sc, reg, bit, timeout, true));
+}
+
+bool
+xhci_debug_enable(struct xhci_debug_softc *sc)
+{
+ uint32_t state;
+
+ if (sc == NULL)
+ return (false);
+
+ state = xhci_debug_update_state(sc);
+ if (state == XHCI_DCPORT_ST_OFF ||
+ state == XHCI_DCPORT_ST_DISCONNECTED) {
+ if (!xhci_debug_set_regbit(sc, XHCI_DCCTRL, XHCI_DCCTRL_DCE,
+ 1)) {
+ device_printf(sc->sc_dev, "initialization failed\n");
+ return (false);
+ }
+
+ }
+
+ /* If success, check the cable. */
+ return (xhci_debug_wait_connection(sc, 1));
+}
+
+static bool
+xhci_debug_wait_connection(struct xhci_debug_softc *sc, uint32_t timeout)
+{
+ bool ret;
+
+ if (xhci_debug_update_state(sc) == XHCI_DCPORT_ST_DISCONNECTED) {
+ ret = xhci_debug_set_regbit(sc, XHCI_DCPORTSC,
+ XHCI_DCPORTSC_PED, 1000); /* XXX: 1 was not enough */
+ if (ret == false) {
+ device_printf(sc->sc_dev, "No DbC cable detected\n");
+ return (ret);
+ }
+ }
+ timeout = 10000;
+ device_printf(sc->sc_dev, "waiting for a cable\n");
+ while (xhci_debug_update_state(sc) != XHCI_DCPORT_ST_CONFIGURED) {
+ delay(500);
+ if (--timeout == 0) {
+ device_printf(sc->sc_dev,
+ "DbC cable detection timed out\n");
+ return (false);
+ }
+ }
+
+ device_printf(sc->sc_dev, "DbC cable detected\n");
+
+ return (true);
+}
+
+void
+xhci_debug_disable(struct xhci_debug_softc *sc)
+{
+ bool ret;
+
+ xhci_debug_reset_regbit(sc, XHCI_DCPORTSC, XHCI_DCPORTSC_PED, 0);
+ ret = xhci_debug_reset_regbit(sc, XHCI_DCCTRL, XHCI_DCCTRL_DCE, 1000);
+ if (ret == false)
+ DEBUG_PRINTF(1, ("%s: clearing XHCI_DCCTRL_DCE failed\n",
+ __func__));
+ xhci_debug_update_state(sc);
+}
+
+int
+xhci_debug_init_ring(struct xhci_debug_softc *sc)
+{
+ uint64_t erdp, out, in;
+ uint32_t temp;
+
+ if (sc->sc_dbc_off == 0) {
+ DEBUG_PRINTF(1, ("%s: register is empty\n", __func__));
+ return (-1);
+ }
+ /* Create an event ring. */
+ xhci_debug_ring_init(sc, &sc->udb_ering, 0, XHCI_DCDB_INVAL);
+ erdp = sc->udb_ering.paddr;
+ memset(sc->udb_erst, 0, sizeof(*sc->udb_erst));
+ sc->udb_erst->qwEvrsTablePtr = htole64(erdp);
+ sc->udb_erst->dwEvrsTableSize = htole32(DC_TRB_RING_LEN);
+ DEBUG_PRINTF(2, ("%s: ERDP=0x%016llx\n", __func__,
+ (unsigned long long)erdp));
+
+ /* Create an output ring. */
+ xhci_debug_ring_init(sc, &sc->udb_oring, 1, XHCI_DCDB_OUT);
+ out = sc->udb_oring.paddr;
+
+ /* Create an input ring. */
+ xhci_debug_ring_init(sc, &sc->udb_iring, 1, XHCI_DCDB_IN);
+ in = sc->udb_iring.paddr;
+
+ /* Initialize DbC context. */
+ memset(sc->udb_ctx, 0, sizeof(*sc->udb_ctx));
+ xhci_debug_init_strings(sc, sc->udb_ctx->info);
+
+ /* Initialize endpoints. */
+ temp = _XREAD4(sc, dbc, XHCI_DCCTRL);
+ xhci_debug_init_ep(&sc->udb_ctx->ep_out,
+ (uint64_t)XHCI_DCCTRL_MBS_GET(le32toh(temp)), EP_BULK_OUT, out);
+ xhci_debug_init_ep(&sc->udb_ctx->ep_in,
+ (uint64_t)XHCI_DCCTRL_MBS_GET(le32toh(temp)), EP_BULK_IN, in);
+
+ /* Configure register values. */
+ _XWRITE4(sc, dbc, XHCI_DCERSTSZ, htole32(1));
+ _XWRITE44LH(sc, dbc, XHCI_DCERSTBA, htole64(sc->udb_erst_paddr));
+ _XWRITE44LH(sc, dbc, XHCI_DCERDP, htole64(erdp));
+ _XWRITE44LH(sc, dbc, XHCI_DCCP, htole64(sc->udb_ctx_paddr));
+ _XWRITE4(sc, dbc, XHCI_DCDDI1,
+ htole32((DC_VENDOR << 16) | DC_PROTOCOL));
+ _XWRITE4(sc, dbc, XHCI_DCDDI2,
+ htole32((DC_REVISION << 16) | DC_PRODUCT));
+ flush_range(sc->udb_ctx, sizeof(*sc->udb_ctx));
+ flush_range(sc->udb_erst, sizeof(*sc->udb_erst));
+ flush_range(sc->udb_ering.trb, DC_TRB_RING_BYTES);
+ flush_range(sc->udb_oring.trb, DC_TRB_RING_BYTES);
+ flush_range(sc->udb_iring.trb, DC_TRB_RING_BYTES);
+ flush_range(sc->udb_oring.work.buf, DC_WORK_RING_BYTES);
+ flush_range(sc->udb_iring.work.buf, DC_WORK_RING_BYTES);
+
+ return (0);
+}
+
+uint32_t
+xhci_debug_get_xecp(struct xhci_debug_softc *sc)
+{
+ uint32_t hccp1;
+ uint32_t eec;
+ uint32_t eecp;
+
+ if (sc == NULL)
+ return (0);
+
+ hccp1 = _XREAD4(sc, capa, XHCI_HCSPARAMS0);
+
+ if (XHCI_HCS0_XECP(hccp1) == 0)
+ goto notfound;
+
+ eec = -1; /* XXX */
+ for (eecp = XHCI_HCS0_XECP(hccp1) << 2;
+ eecp != 0 && XHCI_XECP_NEXT(eec) != 0;
+ eecp += XHCI_XECP_NEXT(eec) << 2) {
+ eec = _XREAD4(sc, capa, eecp);
+
+ DEBUG_PRINTF(1, ("%s: Looking for xECP: "
+ "expected=%02x, found=%02x, next=%04x\n", __func__,
+ XHCI_ID_USB_DEBUG, XHCI_XECP_ID(eec),
+ XHCI_XECP_NEXT(eec) << 2));
+
+ if (XHCI_XECP_ID(eec) == XHCI_ID_USB_DEBUG)
+ break;
+ }
+ if (eecp == 0 || XHCI_XECP_ID(eec) == 0)
+ goto notfound;
+
+ DEBUG_PRINTF(1, ("%s: DbC was found at %08x\n", __func__, eecp));
+
+ return (eecp);
+notfound:
+ DEBUG_PRINTF(1, ("%s: DbC was not found\n", __func__));
+
+ return (0);
+}
+
+static bool
+xhci_debug_softc_fixup(struct xhci_debug_softc *sc)
+{
+ return (true);
+}
+
+static void
+xhci_debug_ring_init(struct xhci_debug_softc *sc,
+ struct xhci_debug_ring *ring, int producer, uint32_t doorbell)
+{
+ memset(ring->trb, 0, DC_TRB_RING_BYTES);
+ memset(ring->work.buf, 0, DC_WORK_RING_BYTES);
+
+ ring->enq = 0;
+ ring->deq = 0;
+ ring->cyc = 1;
+ ring->doorbell = doorbell;
+ ring->work.w_enq = 0;
+ ring->work.w_deq = 0;
+
+ /* Place a link TRB at the tail if producer == 1. */
+ if (producer) {
+ /* Fields for link TRBs (section 6.4.4.1) */
+
+ ring->trb[DC_TRB_RING_LAST] = (struct xhci_trb){
+ .qwTrb0 = htole64(ring->paddr),
+ .dwTrb3 = htole32(
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
+ XHCI_TRB_3_TC_BIT
+ )
+ };
+ }
+}
+
+#define DC_STRDESC_STRING0 0
+#define DC_STRDESC_MANUFACTURER 1
+#define DC_STRDESC_PRODUCT 2
+#define DC_STRDESC_SERIAL 3
+#define USB_DESC_TYPE_STRING 3
+static struct usb_strdesc {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t chars[62]; /* UTF-16LE */
+} strdesc[] = {
+ [DC_STRDESC_STRING0] = {
+ .bLength = 2 + 4,
+ .bDescriptorType = USB_DESC_TYPE_STRING,
+ .chars = { 9, 0, 4, 0 }, /* 0x0409 = English */
+ },
+ [DC_STRDESC_MANUFACTURER] = {
+ .bLength = 2,
+ .bDescriptorType = USB_DESC_TYPE_STRING,
+ },
+ [DC_STRDESC_PRODUCT] = {
+ .bLength = 2,
+ .bDescriptorType = USB_DESC_TYPE_STRING,
+ },
+ [DC_STRDESC_SERIAL] = {
+ .bLength = 2,
+ .bDescriptorType = USB_DESC_TYPE_STRING,
+ }
+};
+static void
+atou16cpy(struct usb_strdesc *desc, const char *str)
+{
+ int i;
+
+ for (i = 0; i < strlen(str) && i * 2 < sizeof(desc->chars) - 1; i++) {
+ desc->chars[i * 2] = str[i];
+ desc->chars[i * 2 + 1] = '\0';
+ }
+ desc->bLength += i * 2;
+ desc->chars[sizeof(desc->chars) - 1] = '\0';
+}
+
+/* Initialize the DbC info with USB string descriptor addresses. */
+static void
+xhci_debug_init_strings(struct xhci_debug_softc *sc, uint32_t info[16])
+{
+ const char *serial;
+ char hostname[256];
+ char *p;
+
+ /*
+ * The product string will be "DC_STRING_PRODUCT (hostname)".
+ */
+ p = hostname;
+ strlcpy(hostname, DC_STRING_PRODUCT, sizeof(hostname));
+ if (sc->sc_hostname[0] != '\0' &&
+ strlen(sc->sc_hostname)
+ < sizeof(hostname) - sizeof(DC_STRING_PRODUCT) - 4) {
+ p = hostname + strlen(hostname);
+ *p++ = ' ';
+ *p++ = '(';
+ strlcpy(p, sc->sc_hostname, sizeof(hostname) - (p - hostname));
+ p = hostname + strlen(hostname);
+ *p++ = ')';
+ *p++ = '\0';
+ }
+ serial = (sc->sc_serialno[0] != '\0')
+ ? sc->sc_serialno : DC_STRING_SERIAL;
+ atou16cpy(&strdesc[DC_STRDESC_MANUFACTURER], DC_STRING_MANUFACTURER);
+ atou16cpy(&strdesc[DC_STRDESC_PRODUCT], hostname);
+ atou16cpy(&strdesc[DC_STRDESC_SERIAL], serial);
+
+ /* udb_str is 1 page buffer longer than sizeof(strdesc) */
+ memcpy(sc->udb_str, strdesc, sizeof(strdesc));
+
+#define STRADDR0(index) \
+ ((uint64_t)sc->udb_str_paddr + index * sizeof(struct usb_strdesc))
+#define STRADDR_LO(index) \
+ ((uint32_t)(STRADDR0(index) & 0xffffffff))
+#define STRADDR_HI(index) \
+ ((uint32_t)((STRADDR0(index) >> 32) & 0xffffffff))
+
+ info[XHCI_DCDBCIC_STR0DESC_LO] = STRADDR_LO(DC_STRDESC_STRING0);
+ info[XHCI_DCDBCIC_STR0DESC_HI] = STRADDR_HI(DC_STRDESC_STRING0);
+ info[XHCI_DCDBCIC_MANUDESC_LO] = STRADDR_LO(DC_STRDESC_MANUFACTURER);
+ info[XHCI_DCDBCIC_MANUDESC_HI] = STRADDR_HI(DC_STRDESC_MANUFACTURER);
+ info[XHCI_DCDBCIC_PRODDESC_LO] = STRADDR_LO(DC_STRDESC_PRODUCT);
+ info[XHCI_DCDBCIC_PRODDESC_HI] = STRADDR_HI(DC_STRDESC_PRODUCT);
+ info[XHCI_DCDBCIC_SERIALDESC_LO] = STRADDR_LO(DC_STRDESC_SERIAL);
+ info[XHCI_DCDBCIC_SERIALDESC_HI] = STRADDR_HI(DC_STRDESC_SERIAL);
+ info[XHCI_DCDBCIC_DESCLEN] =
+ XHCI_DCDBCIC_STR0DESC_LEN_SET(strdesc[DC_STRDESC_STRING0].bLength) |
+ XHCI_DCDBCIC_MANUDESC_LEN_SET(strdesc[DC_STRDESC_MANUFACTURER].bLength) |
+ XHCI_DCDBCIC_PRODDESC_LEN_SET(strdesc[DC_STRDESC_PRODUCT].bLength) |
+ XHCI_DCDBCIC_SERIALDESC_LEN_SET(strdesc[DC_STRDESC_SERIAL].bLength);
+
+#undef STRADDR0
+#undef STRADDR_LO
+#undef STRADDR_HI
+}
+
+static void
+xhci_debug_ring_doorbell(struct xhci_debug_softc *sc, uint32_t target)
+{
+ uint32_t doorbell;
+
+ /* The other bits in DCDB are RsvdP. */
+ /* XXXHRS: the spec has contradiction between Figure 7-9 and Table 7-18. */
+ doorbell = _XREAD4(sc, dbc, XHCI_DCDB) & ~XHCI_DCDB_MASK;
+ doorbell |= target;
+ _XWRITE4(sc, dbc, XHCI_DCDB, doorbell);
+
+ DEBUG_PRINTF(2, ("%s: doorbell = %04x\n", __func__, doorbell));
+}
+
+uint64_t
+xhci_debug_bulk_transfer(struct xhci_debug_softc *sc)
+{
+ return (
+ xhci_debug_bulk_transfer0(sc, &sc->udb_iring) +
+ xhci_debug_bulk_transfer0(sc, &sc->udb_oring));
+}
+
+static uint64_t
+xhci_debug_bulk_transfer0(struct xhci_debug_softc *sc,
+ struct xhci_debug_ring *ring)
+{
+ uint64_t len;
+ uint32_t temp;
+
+ xhci_debug_event_dequeue(sc);
+
+ len = 0;
+ if (mtx_trylock_spin(&sc->udb_ering.mtx) == 0)
+ goto end0;
+ if (mtx_trylock_spin(&ring->mtx) == 0)
+ goto end1;
+ if (XHCI_DC_RING_FULL(ring)) /* TRB queue is full */
+ goto end2;
+ /*
+ * DRC bit is asserted when it exits the CONFIGURED state.
+ * Clear the bit because DCDB is disabled until clearing the DRC bit.
+ * (sec 7.6.8.4)
+ * The timeout is ignored because this is a best-effort attempt.
+ */
+ temp = _XREAD4(sc, dbc, XHCI_DCCTRL);
+ if ((le32toh(temp) & XHCI_DCCTRL_DRC) != 0)
+ xhci_debug_reset_regbit(sc, XHCI_DCCTRL, XHCI_DCCTRL_DRC, 1);
+ /*
+ * Updating the state in bulk_transfer() is critical in loader.
+ * Do not remove this.
+ */
+ xhci_debug_update_state(sc);
+
+ len = (XHCI_DC_RING_IN(ring))
+ ? trb_rx_enqueue_locked(sc, ring)
+ : trb_tx_enqueue_locked(sc, ring);
+end2:
+ mtx_unlock_spin(&ring->mtx);
+end1:
+ mtx_unlock_spin(&sc->udb_ering.mtx);
+end0:
+ return (len);
+}
+
+/*
+ * Initializes the endpoint as specified in sections 7.6.3.2 and 7.6.9.2.
+ * Each endpoint is Bulk, so the MaxPStreams, LSA, HID, CErr, FE,
+ * Interval, Mult, and Max ESIT Payload fields are all 0.
+ *
+ * Max packet size: 1024
+ * Max burst size: mbs
+ * EP type: 2 for OUT bulk, 6 for IN bulk
+ * TR dequeue ptr: physical base address of transfer ring
+ * Avg TRB length: software defined (see 4.14.1.1 for suggested defaults, 3kB)
+ */
+static void
+xhci_debug_init_ep(struct xhci_endp_ctx64 *ep64, uint64_t mbs,
+ uint32_t type, uint64_t ring_paddr)
+{
+ struct xhci_endp_ctx *ep;
+
+ if (ep64 == NULL)
+ return;
+ ep = &ep64->ctx;
+ memset(ep64, 0, sizeof(*ep64));
+
+ /* Do we need XHCI_EPCTX_1_CERR_SET(3) in Sec 6.2.3 ? */
+ ep->dwEpCtx1 = htole32(
+ XHCI_EPCTX_1_MAXP_SIZE_SET(1024) |
+ XHCI_EPCTX_1_MAXB_SET((uint32_t)mbs) |
+ XHCI_EPCTX_1_EPTYPE_SET(type));
+ ep->qwEpCtx2 = htole64(
+ (ring_paddr & XHCI_EPCTX_2_TR_DQ_PTR_MASK) |
+ XHCI_EPCTX_2_DCS_SET(1));
+ ep->dwEpCtx4 = htole32(
+ XHCI_EPCTX_4_AVG_TRB_LEN_SET(1024 * 3));
+}
+
+#if 0
+static bool
+xhci_debug_event_ready(struct xhci_debug_softc *sc)
+{
+ uint32_t temp;
+
+ /* Seems inaccurate */
+ temp = _XREAD4(sc, dbc, XHCI_DCST);
+ return ((le32toh(temp) & XHCI_DCST_ER) == XHCI_DCST_ER);
+}
+#endif
+
+static uint32_t
+xhci_debug_ring_boundary(struct xhci_debug_ring *ring, bool update_enq)
+{
+ struct xhci_trb *trb = &ring->trb[ring->deq];
+ uint32_t dwTrb3;
+ uint32_t cyc;
+ uint32_t deq;
+
+ cyc = ring->cyc;
+ deq = ring->deq;
+ rmb();
+ dwTrb3 = le32toh(trb->dwTrb3);
+
+ while ((dwTrb3 & XHCI_TRB_3_CYCLE_BIT) == cyc) {
+ cyc = (deq == DC_TRB_RING_LAST)
+ ? cyc ^ 1
+ : cyc;
+ deq = (deq + 1) & DC_TRB_RING_OFFSET_MASK;
+ trb = &ring->trb[deq];
+ rmb();
+ dwTrb3 = le32toh(trb->dwTrb3);
+ }
+ if (update_enq)
+ ring->enq = deq;
+
+ return (deq);
+}
+
+static void
+xhci_debug_transfer_event_handler(struct xhci_debug_softc *sc,
+ uint64_t qwTrb0, uint32_t dwTrb2, uint32_t dwTrb3)
+{
+ struct xhci_trb *trb;
+ struct xhci_debug_ring *ring;
+ uint64_t qwEpCtx2;
+ uint32_t epid;
+ uint32_t error, len;
+ uint32_t deq_old;
+
+ /* EP ID is an index of context. */
+ epid = XHCI_TRB_3_EP_GET(dwTrb3);
+ error = XHCI_TRB_2_ERROR_GET(dwTrb2);
+
+ switch (epid) {
+ case XHCI_DC_EPID_OUT:
+ case XHCI_DC_EPID_OUT_INTEL:
+ ring = &sc->udb_oring;
+ break;
+ case XHCI_DC_EPID_IN:
+ case XHCI_DC_EPID_IN_INTEL:
+ ring = &sc->udb_iring;
+ break;
+ default:
+ ring = &sc->udb_ering;
+ }
+ ring->lasterror = error;
+
+ if (error != XHCI_TRB_ERROR_SUCCESS &&
+ error != XHCI_TRB_ERROR_SHORT_PKT) {
+ DEBUG_PRINTF(1, ("%s: transfer error (%u)", __func__, error));
+ ring->ec[error]++;
+
+ switch (error) {
+ case XHCI_TRB_ERROR_RING_UNDERRUN:
+ DEBUG_PRINTF(1, (": RING_UNDERRUN\n"));
+ break;
+ case XHCI_TRB_ERROR_RING_OVERRUN:
+ DEBUG_PRINTF(1, (": RING_OVERRUN\n"));
+ break;
+ case XHCI_TRB_ERROR_TRB:
+ DEBUG_PRINTF(1, (": TRB\n"));
+ break;
+ case XHCI_TRB_ERROR_STALL:
+ DEBUG_PRINTF(1, (": STALL\n"));
+ break;
+ default:
+ DEBUG_PRINTF(1, ("\n"));
+ }
+ }
+ if (ring != &sc->udb_ering)
+ mtx_lock_spin(&ring->mtx);
+ if (epid == XHCI_DC_EPID_OUT || epid == XHCI_DC_EPID_OUT_INTEL ||
+ epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) {
+ if ((dwTrb3 & XHCI_TRB_3_ISP_BIT) != 0) {
+ /* Event Data */
+ goto end;
+ }
+ deq_old = ring->deq;
+ rmb();
+
+ if (error == XHCI_TRB_ERROR_STALL) {
+ /* Halted state (sec 7.6.4.3) */
+ /* Update the deq pointer using ctx. */
+
+ rmb();
+ qwEpCtx2 = (ring == &sc->udb_oring)
+ ? le64toh(sc->udb_ctx->
+ ep_out.ctx.qwEpCtx2) &
+ XHCI_EPCTX_2_TR_DQ_PTR_MASK
+ : le64toh(sc->udb_ctx->
+ ep_in.ctx.qwEpCtx2) &
+ XHCI_EPCTX_2_TR_DQ_PTR_MASK;
+
+ ring->deq = qwEpCtx2 - (uint64_t)ring->paddr;
+ ring->deq /= sizeof(struct xhci_trb);
+ ring->deq &= DC_TRB_RING_OFFSET_MASK;
+ /*
+ * XXXHRS: Rollback the enqueued TRB.
+ *
+ * I am unsure of whether this is correct or not, but
+ * the enqueued old TRB will not be processed or
+ * generate another event TRB with
+ * XHCI_TRB_ERROR_SUCCESS after this event,
+ * while the internal dequeue pointer seems to remain.
+ * Enqueuing another TRB into the same address
+ * and ringing the doorbell seems to work.
+ */
+ ring->enq = ring->deq;
+ IOCBITMAP_CLEAR(ring, ring->enq);
+
+ trb = &ring->trb[ring->deq];
+ } else if (error == XHCI_TRB_ERROR_INVALID) {
+
+ /* do nothing */
+
+ trb = NULL;
+ } else if (error == XHCI_TRB_ERROR_SUCCESS &&
+ XHCI_TRB_2_BYTES_GET(dwTrb2) == 0 && qwTrb0 == 0) {
+ /*
+ * Use the current deq value
+ * because Transfer Event TRBs with
+ * zero length have no valid TRB
+ * pointer.
+ * XXXHRS: where is this in the spec?
+ */
+
+ /*
+ * Use the next of deq_old because the request TRB
+ * seems be ignored in this case.
+ */
+ ring->deq = deq_old + 1;
+ ring->deq &= DC_TRB_RING_OFFSET_MASK;
+ IOCBITMAP_CLEAR(ring, ring->deq);
+
+ ring->lastdeq = ring->deq;
+ trb = NULL;
+ } else {
+ /* Update the deq pointer using ER. */
+ /* XXX: should be optimized. */
+ /* (qwTrb0 & MASK) >> 4 (16 bytes) ? */
+
+ ring->deq = qwTrb0 - (uint64_t)ring->paddr;
+ ring->deq /= sizeof(struct xhci_trb);
+ ring->deq &= DC_TRB_RING_OFFSET_MASK;
+
+ IOCBITMAP_CLEAR(ring, ring->deq);
+ ring->lastdeq = ring->deq;
+ trb = &ring->trb[ring->deq];
+
+ /*
+ * XXX: Workaround for Forward jump. This is
+ * the biggest problem. Dequeue pointers reported
+ * in event TRBs are not sometimes monotonic.
+ * If a jump is detected, use the +1 address instead.
+ */
+ if (ring->deq < deq_old &&
+ 1 < (ring->deq + DC_TRB_RING_LEN) - deq_old) {
+ ring->deq = deq_old + 1;
+ ring->deq &= DC_TRB_RING_OFFSET_MASK;
+ } else if (deq_old < ring->deq &&
+ 1 < ring->deq - deq_old) {
+
+ ring->deq = deq_old + 1;
+ ring->deq &= DC_TRB_RING_OFFSET_MASK;
+ }
+ }
+ }
+ /* debug printf() */
+ if (epid == XHCI_DC_EPID_OUT || epid == XHCI_DC_EPID_OUT_INTEL ||
+ epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) {
+ if ((error == XHCI_TRB_ERROR_SUCCESS ||
+ error == XHCI_TRB_ERROR_SHORT_PKT) &&
+ trb != NULL) {
+ len = XHCI_TRB_2_BYTES_GET(le32toh(trb->dwTrb2))
+ - XHCI_TRB_2_BYTES_GET(dwTrb2);
+
+ DEBUG_PRINTF(2, ("%s: er->deq=%u, transfer length = "
+ "%u - %u = %u\n",
+ __func__, sc->udb_ering.deq,
+ XHCI_TRB_2_BYTES_GET(le32toh(trb->dwTrb2)),
+ XHCI_TRB_2_BYTES_GET(dwTrb2),
+ len));
+ }
+ }
+ if (epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) {
+ if (error == XHCI_TRB_ERROR_SUCCESS ||
+ error == XHCI_TRB_ERROR_SHORT_PKT) {
+ /*
+ * The new dequeue pointer is the next of
+ * the reported one.
+ */
+ if (!XHCI_DC_RING_EMPTY(ring))
+ ring->deq = (ring->deq + 1) &
+ DC_TRB_RING_OFFSET_MASK;
+
+ if (trb != NULL) {
+ len = XHCI_TRB_2_BYTES_GET(le32toh(trb->dwTrb2))
+ - XHCI_TRB_2_BYTES_GET(dwTrb2);
+
+ /* Advance the enq of work q */
+ flush_range(&ring->work.buf[ring->work.w_enq],
+ len);
+ ring->work.w_enq = (ring->work.w_enq + len)
+ & DC_WORK_RING_OFFSET_MASK;
+ }
+ }
+ }
+ if (epid == XHCI_DC_EPID_OUT || epid == XHCI_DC_EPID_OUT_INTEL ||
+ epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) {
+ /* printf() debug */
+ }
+ if (epid != XHCI_DC_EPID_OUT && epid != XHCI_DC_EPID_OUT_INTEL &&
+ epid != XHCI_DC_EPID_IN && epid != XHCI_DC_EPID_IN_INTEL) {
+ ring->ec[XHCI_TRB_ERROR_UNKNOWN]++;
+ }
+end:
+ if (ring != &sc->udb_ering)
+ mtx_unlock_spin(&ring->mtx);
+}
+
+void
+xhci_debug_event_dequeue(struct xhci_debug_softc *sc)
+{
+ struct xhci_debug_ring *er;
+ struct xhci_trb *ev;
+ uint64_t qwTrb0;
+ uint32_t dwTrb3, dwTrb2;
+ uint64_t erdp;
+ bool erdp_update;
+ uint32_t temp;
+
+ er = &sc->udb_ering;
+ er->ec[XHCI_TRB_ERROR_CALLED]++;
+ if (mtx_trylock_spin(&er->mtx) == 0) {
+ er->ec[XHCI_TRB_ERROR_EVLOCKED]++;
+ return;
+ }
+
+ ev = &er->trb[er->deq];
+ sc->sc_dequeue_count++;
+ erdp_update = false;
+
+ rmb();
+ qwTrb0 = le64toh(ev->qwTrb0);
+ dwTrb2 = le32toh(ev->dwTrb2);
+ dwTrb3 = le32toh(ev->dwTrb3);
+ while ((dwTrb3 & XHCI_TRB_3_CYCLE_BIT) == er->cyc) {
+ switch (XHCI_TRB_3_TYPE_GET(dwTrb3)) {
+ case XHCI_TRB_EVENT_TRANSFER:
+ xhci_debug_transfer_event_handler(sc, qwTrb0, dwTrb2,
+ dwTrb3);
+ break;
+ case XHCI_TRB_EVENT_PORT_STS_CHANGE:
+ temp = _XREAD4(sc, dbc, XHCI_DCPORTSC);
+ if (XHCI_DCPORTSC_CSC_GET(le32toh(temp))) {
+ device_printf(sc->sc_dev,
+ "DbC cable status changed%s\n",
+ (XHCI_DCPORTSC_CCS_GET(le32toh(temp)))
+ ? ""
+ : ": unplugged");
+ }
+ _XWRITE4(sc, dbc, XHCI_DCPORTSC,
+ temp & htole32(XHCI_DCPORTSC_ACK_MASK));
+ break;
+ default:
+ er->ec[XHCI_TRB_ERROR_UNKNOWN]++;
+
+ DEBUG_PRINTF(1,
+ ("%s: er->deq=%u"
+ ", er: qwTrb0=%lx"
+ ", dwTrb2=%x"
+ ", dwTrb3=%x"
+ ", type=%d (unknown)"
+ ", ec_unknown=%lu\n",
+ __func__,
+ er->deq,
+ qwTrb0,
+ dwTrb2,
+ dwTrb3,
+ XHCI_TRB_3_TYPE_GET(dwTrb3),
+ er->ec[XHCI_TRB_ERROR_UNKNOWN]));
+ break;
+ }
+ if (er->deq == DC_TRB_RING_LAST)
+ er->cyc ^= 1;
+ er->deq = (er->deq + 1) & DC_TRB_RING_OFFSET_MASK;
+ erdp_update = true;
+
+ ev = &er->trb[er->deq];
+ rmb();
+ qwTrb0 = le64toh(ev->qwTrb0);
+ dwTrb2 = le32toh(ev->dwTrb2);
+ dwTrb3 = le32toh(ev->dwTrb3);
+ }
+ if (erdp_update) {
+ erdp = _XREAD44LH(sc, dbc, XHCI_DCERDP);
+ erdp &= ~DC_TRB_RING_MASK;
+ erdp |= er->deq * sizeof(struct xhci_trb);
+ _XWRITE44LH(sc, dbc, XHCI_DCERDP, erdp);
+ }
+ mtx_unlock_spin(&er->mtx);
+}
+
+static uint64_t
+trb_rx_enqueue_locked(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring)
+{
+ struct xhci_debug_work_ring *work;
+ uint64_t addr;
+ uint64_t len;
+
+ if (ring == NULL)
+ return (0);
+ if ((work = &ring->work) == NULL)
+ return (0);
+ /* Skip if in-progress */
+ if (ring->flags != 0)
+ return (0);
+ /* Skip if there are pending TRB */
+ if (!XHCI_DC_RING_EMPTY(ring))
+ return (0);
+ /* Skip if there are received data */
+ if (!DC_WORK_RING_EMPTY(ring))
+ return (0);
+
+ addr = work->paddr + work->w_enq;
+ len = (work->w_enq < work->w_deq) ? work->w_deq : DC_WORK_RING_LEN;
+ len -= work->w_enq;
+
+ return (trb_push_locked(sc, ring, addr, len));
+}
+
+static uint64_t
+trb_tx_enqueue_locked(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring)
+{
+ struct xhci_debug_work_ring *work;
+ uint64_t addr;
+ uint64_t len0, len1;
+
+ if (ring == NULL)
+ return (0);
+ if ((work = &ring->work) == NULL)
+ return (0);
+ if (ring->flags != 0)
+ return (0);
+ if (DC_WORK_RING_EMPTY(ring))
+ return (0);
+
+ /* XXX: pending check? */
+
+ len0 = len1 = 0;
+ if (work->w_deq < work->w_enq) {
+ addr = work->paddr + work->w_deq;
+ len0 = trb_push_locked(sc, ring, addr, work->w_enq - work->w_deq);
+
+ work->w_deq += len0;
+ } else {
+ addr = work->paddr + work->w_deq;
+ len0 = trb_push_locked(sc, ring, addr,
+ DC_WORK_RING_LEN - work->w_deq);
+
+ work->w_deq += len0;
+ work->w_deq &= DC_WORK_RING_OFFSET_MASK;
+
+ if (work->w_deq == 0 &&
+ !DC_WORK_RING_EMPTY(ring) &&
+ !XHCI_DC_RING_FULL(ring)) {
+ addr = work->paddr;
+ len1 = trb_push_locked(sc, ring, addr, work->w_enq);
+
+ work->w_deq += len1;
+ }
+ }
+ return (len0 + len1);
+}
+
+static uint64_t
+trb_push_locked(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring,
+ uint64_t addr, uint64_t len)
+{
+ struct xhci_trb trb;
+
+ if (ring->enq == DC_TRB_RING_LAST) {
+ /*
+ * If it is the last entry, whose TRB type is LINK,
+ * cyc is set and ring->enq is advanced.
+ * The next enq should always be zero.
+ */
+ ring->trb[ring->enq].dwTrb3 &= ~htole32(XHCI_TRB_3_CYCLE_BIT);
+ ring->trb[ring->enq].dwTrb3 |= htole32(ring->cyc);
+ flush_range(&ring->trb[ring->enq], sizeof(ring->trb[0]));
+
+ /* Must be zero */
+ ring->enq = (ring->enq + 1) & DC_TRB_RING_OFFSET_MASK;
+ ring->cyc ^= 1;
+ }
+ trb = (struct xhci_trb){
+ .qwTrb0 = htole64(addr),
+ .dwTrb2 = htole32(XHCI_TRB_2_BYTES_SET((uint32_t)len)),
+ .dwTrb3 = htole32(
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL)
+ | ring->cyc | XHCI_TRB_3_IOC_BIT
+ )
+ };
+
+ /* XXX: overwrite check of iocbitmap? */
+
+ /* Note that the last entry never have a bit in the iocbitmap. */
+ IOCBITMAP_SET(ring, ring->enq);
+ ring->lastenq = ring->enq;
+
+ DEBUG_PRINTF(1, ("%s: enqueue: %s: enq=%d, len=%lu, deq=%d\n", __func__,
+ label, ring->enq, len, ring->deq));
+ memcpy(&ring->trb[ring->enq], &trb, sizeof(ring->trb[0]));
+ flush_range(&ring->trb[ring->enq], sizeof(ring->trb[0]));
+ ring->enq = (ring->enq + 1) & DC_TRB_RING_OFFSET_MASK;
+
+ xhci_debug_ring_doorbell(sc, ring->doorbell);
+
+ return (len);
+}
+
+int
+work_dequeue(struct xhci_debug_ring *ring)
+{
+ struct xhci_debug_work_ring *work;
+ int c;
+
+ if (ring == NULL)
+ return (-1);
+ if ((work = &ring->work) == NULL)
+ return (-1);
+
+ c = -1;
+ mtx_lock_spin(&ring->mtx);
+ if (!DC_WORK_RING_EMPTY(ring)) {
+ c = (int)work->buf[work->w_deq];
+ work->w_deq = (work->w_deq + 1) & DC_WORK_RING_OFFSET_MASK;
+ DEBUG_PRINTF(2, ("%s: [%c(%02x)]\n", __func__,
+ (char)c, (char)c));
+ }
+ mtx_unlock_spin(&ring->mtx);
+ return (c);
+}
+
+int64_t
+work_enqueue(struct xhci_debug_ring *ring, const char *buf, int64_t len)
+{
+ struct xhci_debug_work_ring *work;
+ uint32_t start, end;
+ int64_t i;
+
+ if (ring == NULL)
+ return (0);
+ if ((work = &ring->work) == NULL)
+ return (0);
+
+ i = 0;
+ end = 0;
+ mtx_lock_spin(&ring->mtx);
+ if (DC_WORK_RING_SLOTS(work) < len) {
+ ring->ec[XHCI_TRB_ERROR_WORKQ_FULL]++;
+ goto end;
+ }
+ start = work->w_enq;
+ DEBUG_PRINTF(1, ("%s: %s: len=%lu: ", __func__,
+ XHCI_DC_RING_IN(ring) ? "IR" : "OR", len));
+ while (!DC_WORK_RING_FULL(ring) && i < len) {
+ DEBUG_PRINTF(2, ("[%c(%02x)]", buf[i], buf[i]));
+ work->buf[work->w_enq++] = buf[i++];
+ work->w_enq &= DC_WORK_RING_OFFSET_MASK;
+ }
+ end = work->w_enq;
+ DEBUG_PRINTF(1, (": start=%u, end=%u\n", start, end));
+
+ if (start < end)
+ flush_range(&work->buf[start], end - start);
+ else if (0 < i) {
+ flush_range(&work->buf[start], DC_WORK_RING_LEN - start);
+ flush_range(&work->buf[0], end);
+ }
+end:
+ mtx_unlock_spin(&ring->mtx);
+ return (i);
+}
diff --git a/sys/dev/usb/controller/xhci_dbc_private.h b/sys/dev/usb/controller/xhci_dbc_private.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/controller/xhci_dbc_private.h
@@ -0,0 +1,101 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+extern int dbc_debug;
+
+#if _KERNEL
+
+#define DEBUG_PRINTF(level, args )do { \
+ if (dbc_debug >= level) printf args; \
+ } while (0)
+
+/* PCI register access via bus_dma in kernel */
+#define _XREAD4(sc, what, a) XREAD4((sc)->sc_xhci, what, a)
+#define _XWRITE4(sc, what, a, x) XWRITE4((sc)->sc_xhci, what, a, (x))
+#define _XREAD44LH(sc, what, a) XREAD44LH((sc)->sc_xhci, what, a)
+#define _XWRITE44LH(sc, what, a, x) XWRITE44LH((sc)->sc_xhci, what, a, (x))
+/* sys/systm.h */
+#define delay(x) DELAY(x)
+
+#else
+
+#define device_printf(dev, ...) \
+ printf(__VA_ARGS__)
+
+/* PCI register access via UEFI service in loader */
+static inline volatile uint32_t
+_XREAD4_0(struct xhci_debug_softc *sc, uint64_t offset)
+{
+ volatile uint32_t temp;
+
+ sc->sc_efi_pciio->Mem.Read(
+ sc->sc_efi_pciio,
+ EfiPciIoWidthUint32,
+ 0,
+ offset,
+ 1,
+ (VOID *)&temp);
+
+ return (temp);
+}
+static inline void
+_XWRITE4_0(struct xhci_debug_softc *sc, uint64_t offset, uint32_t temp)
+{
+ sc->sc_efi_pciio->Mem.Write(
+ sc->sc_efi_pciio,
+ EfiPciIoWidthUint32,
+ 0,
+ offset,
+ 1,
+ (VOID *)&temp);
+#if 0
+ /*
+ * wmb is defined in atomics.h, which we don't have. On amd64 it will
+ * issue an sfench. I am not entirely sure if this is needed or if it
+ * is the correct ordering primitive.
+ *
+ * The pci memory is mapped as cacheable for the dma structures and the
+ * softc (EFI_PCI_ATTRIBUTE_MEMORY_CACHED).
+ *
+ * On my test systems this functions as far as I can tell.
+ */
+ wmb();
+#endif
+}
+#define _XREAD4(sc, what, a) \
+ _XREAD4_0(sc, (a) + (sc)->sc_##what##_off)
+#define _XREAD44LH(sc, what, a) \
+ ((volatile uint64_t)_XREAD4(sc, what, a##_LO) | \
+ ((volatile uint64_t)_XREAD4(sc, what, a##_HI) << 32))
+#define _XWRITE4(sc, what, a, x) \
+ _XWRITE4_0(sc, (a) + (sc)->sc_##what##_off, (x))
+#define _XWRITE44LH(sc, what, a, x) \
+ do { \
+ _XWRITE4(sc, what, a##_LO, (uint32_t)((x) & 0xffffffff)); \
+ _XWRITE4(sc, what, a##_HI, (uint32_t)((x) >> 32)); \
+ } while (0)
+#endif
diff --git a/sys/dev/usb/controller/xhci_pci.h b/sys/dev/usb/controller/xhci_pci.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/controller/xhci_pci.h
@@ -0,0 +1,187 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010-2022 Hans Petter Selasky
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#define PCI_XHCI_VENDORID_AMD 0x1022
+#define PCI_XHCI_VENDORID_INTEL 0x8086
+#define PCI_XHCI_VENDORID_VMWARE 0x15ad
+#define PCI_XHCI_VENDORID_ZHAOXIN 0x1d17
+
+static inline const char *
+xhci_pci_match(device_t self)
+{
+ uint32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case 0x145c1022:
+ return ("AMD KERNCZ USB 3.0 controller");
+ case 0x148c1022:
+ return ("AMD Starship USB 3.0 controller");
+ case 0x149c1022:
+ return ("AMD Matisse USB 3.0 controller");
+ case 0x15e01022:
+ case 0x15e11022:
+ return ("AMD Raven USB 3.1 controller");
+ case 0x43ba1022:
+ return ("AMD X399 USB 3.0 controller");
+ case 0x43b91022: /* X370 */
+ case 0x43bb1022: /* B350 */
+ return ("AMD 300 Series USB 3.1 controller");
+ case 0x43d51022:
+ return ("AMD 400 Series USB 3.1 controller");
+ case 0x78121022:
+ case 0x78141022:
+ case 0x79141022:
+ return ("AMD FCH USB 3.0 controller");
+
+ case 0x077815ad:
+ case 0x077915ad:
+ return ("VMware USB 3.0 controller");
+
+ case 0x145f1d94:
+ return ("Hygon USB 3.0 controller");
+
+ case 0x01941033:
+ return ("NEC uPD720200 USB 3.0 controller");
+ case 0x00151912:
+ return ("NEC uPD720202 USB 3.0 controller");
+
+ case 0x10001b73:
+ return ("Fresco Logic FL1000G USB 3.0 controller");
+ case 0x10091b73:
+ return ("Fresco Logic FL1009 USB 3.0 controller");
+ case 0x11001b73:
+ return ("Fresco Logic FL1100 USB 3.0 controller");
+
+ case 0x10421b21:
+ return ("ASMedia ASM1042 USB 3.0 controller");
+ case 0x11421b21:
+ return ("ASMedia ASM1042A USB 3.0 controller");
+ case 0x13431b21:
+ return ("ASMedia ASM1143 USB 3.1 controller");
+ case 0x32421b21:
+ return ("ASMedia ASM3242 USB 3.2 controller");
+
+ case 0x02ed8086:
+ return ("Intel Comet Lake PCH-LP USB 3.1 controller");
+ case 0x0b278086:
+ return ("Intel Goshen Ridge Thunderbolt 4 USB controller");
+ case 0x0f358086:
+ return ("Intel BayTrail USB 3.0 controller");
+ case 0x11388086:
+ return ("Intel Maple Ridge Thunderbolt 4 USB controller");
+ case 0x15c18086:
+ case 0x15d48086:
+ case 0x15db8086:
+ return ("Intel Alpine Ridge Thunderbolt 3 USB controller");
+ case 0x15e98086:
+ case 0x15ec8086:
+ case 0x15f08086:
+ return ("Intel Titan Ridge Thunderbolt 3 USB controller");
+ case 0x19d08086:
+ return ("Intel Denverton USB 3.0 controller");
+ case 0x9c318086:
+ case 0x1e318086:
+ return ("Intel Panther Point USB 3.0 controller");
+ case 0x22b58086:
+ return ("Intel Braswell USB 3.0 controller");
+ case 0x31a88086:
+ return ("Intel Gemini Lake USB 3.0 controller");
+ case 0x34ed8086:
+ return ("Intel Ice Lake-LP USB 3.1 controller");
+ case 0x43ed8086:
+ return ("Intel Tiger Lake-H USB 3.2 controller");
+ case 0x461e8086:
+ return ("Intel Alder Lake-P Thunderbolt 4 USB controller");
+ case 0x51ed8086:
+ case 0x54ed8086:
+ case 0x5fed8086:
+ return ("Intel Alder Lake PCH USB 3.2 controller");
+ case 0x5aa88086:
+ return ("Intel Apollo Lake USB 3.0 controller");
+ case 0x7ae08086:
+ return ("Intel Alder Lake USB 3.2 controller");
+ case 0x8a138086:
+ return ("Intel Ice Lake Thunderbolt 3 USB controller");
+ case 0x8c318086:
+ return ("Intel Lynx Point USB 3.0 controller");
+ case 0x8cb18086:
+ return ("Intel Wildcat Point USB 3.0 controller");
+ case 0x8d318086:
+ return ("Intel Wellsburg USB 3.0 controller");
+ case 0x9a138086:
+ return ("Intel Tiger Lake-LP Thunderbolt 4 USB controller");
+ case 0x9a178086:
+ return ("Intel Tiger Lake-H Thunderbolt 4 USB controller");
+ case 0x9cb18086:
+ return ("Broadwell Integrated PCH-LP chipset USB 3.0 controller");
+ case 0x9d2f8086:
+ return ("Intel Sunrise Point-LP USB 3.0 controller");
+ case 0xa0ed8086:
+ return ("Intel Tiger Lake-LP USB 3.2 controller");
+ case 0xa12f8086:
+ return ("Intel Sunrise Point USB 3.0 controller");
+ case 0xa1af8086:
+ return ("Intel Lewisburg USB 3.0 controller");
+ case 0xa2af8086:
+ return ("Intel Union Point USB 3.0 controller");
+ case 0xa36d8086:
+ return ("Intel Cannon Lake USB 3.1 controller");
+ case 0xa71e8086:
+ return ("Intel Raptor Lake-P Thunderbolt 4 USB Controller");
+
+ case 0xa01b177d:
+ return ("Cavium ThunderX USB 3.0 controller");
+
+ case 0x1ada10de:
+ return ("NVIDIA TU106 USB 3.1 controller");
+
+ case 0x92021d17:
+ return ("Zhaoxin ZX-100 USB 3.0 controller");
+ case 0x92031d17:
+ return ("Zhaoxin ZX-200 USB 3.0 controller");
+ case 0x92041d17:
+ return ("Zhaoxin ZX-E USB 3.0 controller");
+
+ default:
+ break;
+ }
+
+ if ((pci_get_class(self) == PCIC_SERIALBUS)
+ && (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
+ && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) {
+ return ("XHCI (generic) USB 3.0 controller");
+ }
+ return (NULL); /* dunno */
+}
diff --git a/sys/dev/usb/controller/xhci_pci.c b/sys/dev/usb/controller/xhci_pci.c
--- a/sys/dev/usb/controller/xhci_pci.c
+++ b/sys/dev/usb/controller/xhci_pci.c
@@ -56,14 +56,10 @@
#include <dev/usb/usb_bus.h>
#include <dev/usb/usb_pci.h>
#include <dev/usb/controller/xhci.h>
+#include <dev/usb/controller/xhci_pci.h>
#include <dev/usb/controller/xhcireg.h>
#include "usb_if.h"
-#define PCI_XHCI_VENDORID_AMD 0x1022
-#define PCI_XHCI_VENDORID_INTEL 0x8086
-#define PCI_XHCI_VENDORID_VMWARE 0x15ad
-#define PCI_XHCI_VENDORID_ZHAOXIN 0x1d17
-
static device_probe_t xhci_pci_probe;
static device_detach_t xhci_pci_detach;
static usb_take_controller_t xhci_pci_take_controller;
@@ -87,157 +83,6 @@
DRIVER_MODULE(xhci, pci, xhci_pci_driver, NULL, NULL);
MODULE_DEPEND(xhci, usb, 1, 1, 1);
-static const char *
-xhci_pci_match(device_t self)
-{
- uint32_t device_id = pci_get_devid(self);
-
- switch (device_id) {
- case 0x145c1022:
- return ("AMD KERNCZ USB 3.0 controller");
- case 0x148c1022:
- return ("AMD Starship USB 3.0 controller");
- case 0x149c1022:
- return ("AMD Matisse USB 3.0 controller");
- case 0x15b61022:
- case 0x15b71022:
- return ("AMD Raphael/Granite Ridge USB 3.1 controller");
- case 0x15b81022:
- return ("AMD Raphael/Granite Ridge USB 2.0 controller");
- case 0x15e01022:
- case 0x15e11022:
- return ("AMD Raven USB 3.1 controller");
- case 0x43ba1022:
- return ("AMD X399 USB 3.0 controller");
- case 0x43b91022: /* X370 */
- case 0x43bb1022: /* B350 */
- return ("AMD 300 Series USB 3.1 controller");
- case 0x43d51022:
- return ("AMD 400 Series USB 3.1 controller");
- case 0x43f71022:
- return ("AMD 600 Series USB 3.2 controller");
- case 0x78121022:
- case 0x78141022:
- case 0x79141022:
- return ("AMD FCH USB 3.0 controller");
-
- case 0x077815ad:
- case 0x077915ad:
- return ("VMware USB 3.0 controller");
-
- case 0x145f1d94:
- return ("Hygon USB 3.0 controller");
-
- case 0x01941033:
- return ("NEC uPD720200 USB 3.0 controller");
- case 0x00151912:
- return ("NEC uPD720202 USB 3.0 controller");
-
- case 0x10001b73:
- return ("Fresco Logic FL1000G USB 3.0 controller");
- case 0x10091b73:
- return ("Fresco Logic FL1009 USB 3.0 controller");
- case 0x11001b73:
- return ("Fresco Logic FL1100 USB 3.0 controller");
-
- case 0x10421b21:
- return ("ASMedia ASM1042 USB 3.0 controller");
- case 0x11421b21:
- return ("ASMedia ASM1042A USB 3.0 controller");
- case 0x13431b21:
- return ("ASMedia ASM1143 USB 3.1 controller");
- case 0x32421b21:
- return ("ASMedia ASM3242 USB 3.2 controller");
-
- case 0x0b278086:
- return ("Intel Goshen Ridge Thunderbolt 4 USB controller");
- case 0x0f358086:
- return ("Intel BayTrail USB 3.0 controller");
- case 0x11388086:
- return ("Intel Maple Ridge Thunderbolt 4 USB controller");
- case 0x15c18086:
- case 0x15d48086:
- case 0x15db8086:
- return ("Intel Alpine Ridge Thunderbolt 3 USB controller");
- case 0x15e98086:
- case 0x15ec8086:
- case 0x15f08086:
- return ("Intel Titan Ridge Thunderbolt 3 USB controller");
- case 0x19d08086:
- return ("Intel Denverton USB 3.0 controller");
- case 0x9c318086:
- case 0x1e318086:
- return ("Intel Panther Point USB 3.0 controller");
- case 0x22b58086:
- return ("Intel Braswell USB 3.0 controller");
- case 0x31a88086:
- return ("Intel Gemini Lake USB 3.0 controller");
- case 0x34ed8086:
- return ("Intel Ice Lake-LP USB 3.1 controller");
- case 0x43ed8086:
- return ("Intel Tiger Lake-H USB 3.2 controller");
- case 0x461e8086:
- return ("Intel Alder Lake-P Thunderbolt 4 USB controller");
- case 0x4b7d8086:
- return ("Intel Elkhart Lake USB 3.1 controller");
- case 0x51ed8086:
- return ("Intel Alder Lake USB 3.2 controller");
- case 0x5aa88086:
- return ("Intel Apollo Lake USB 3.0 controller");
- case 0x7ae08086:
- return ("Intel Alder Lake USB 3.2 controller");
- case 0x8a138086:
- return ("Intel Ice Lake Thunderbolt 3 USB controller");
- case 0x8c318086:
- return ("Intel Lynx Point USB 3.0 controller");
- case 0x8cb18086:
- return ("Intel Wildcat Point USB 3.0 controller");
- case 0x8d318086:
- return ("Intel Wellsburg USB 3.0 controller");
- case 0x9a138086:
- return ("Intel Tiger Lake-LP Thunderbolt 4 USB controller");
- case 0x9a178086:
- return ("Intel Tiger Lake-H Thunderbolt 4 USB controller");
- case 0x9cb18086:
- return ("Broadwell Integrated PCH-LP chipset USB 3.0 controller");
- case 0x9d2f8086:
- return ("Intel Sunrise Point-LP USB 3.0 controller");
- case 0xa0ed8086:
- return ("Intel Tiger Lake-LP USB 3.2 controller");
- case 0xa12f8086:
- return ("Intel Sunrise Point USB 3.0 controller");
- case 0xa1af8086:
- return ("Intel Lewisburg USB 3.0 controller");
- case 0xa2af8086:
- return ("Intel Union Point USB 3.0 controller");
- case 0xa36d8086:
- return ("Intel Cannon Lake USB 3.1 controller");
-
- case 0xa01b177d:
- return ("Cavium ThunderX USB 3.0 controller");
-
- case 0x1ada10de:
- return ("NVIDIA TU106 USB 3.1 controller");
-
- case 0x92021d17:
- return ("Zhaoxin ZX-100 USB 3.0 controller");
- case 0x92031d17:
- return ("Zhaoxin ZX-200 USB 3.0 controller");
- case 0x92041d17:
- return ("Zhaoxin ZX-E USB 3.0 controller");
-
- default:
- break;
- }
-
- if ((pci_get_class(self) == PCIC_SERIALBUS)
- && (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
- && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) {
- return ("XHCI (generic) USB 3.0 controller");
- }
- return (NULL); /* dunno */
-}
-
static int
xhci_pci_probe(device_t self)
{
diff --git a/sys/dev/usb/controller/xhci_private.h b/sys/dev/usb/controller/xhci_private.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/controller/xhci_private.h
@@ -0,0 +1,228 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2010-2022 Hans Petter Selasky
+ * Copyright (c) 2020-2024 Hiroki Sato <hrs@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _USB_XHCI_PRIVATE_H_
+#define _USB_XHCI_PRIVATE_H_
+
+#define XHCI_DEV_CTX_ADDR_ALIGN 64 /* bytes */
+#define XHCI_DEV_CTX_ALIGN 64 /* bytes */
+#define XHCI_INPUT_CTX_ALIGN 64 /* bytes */
+#define XHCI_SLOT_CTX_ALIGN 32 /* bytes */
+#define XHCI_ENDP_CTX_ALIGN 32 /* bytes */
+#define XHCI_STREAM_CTX_ALIGN 16 /* bytes */
+#define XHCI_TRANS_RING_SEG_ALIGN 16 /* bytes */
+#define XHCI_CMD_RING_SEG_ALIGN 64 /* bytes */
+#define XHCI_EVENT_RING_SEG_ALIGN 64 /* bytes */
+#define XHCI_SCRATCH_BUF_ARRAY_ALIGN 64 /* bytes */
+#define XHCI_SCRATCH_BUFFER_ALIGN USB_PAGE_SIZE
+#define XHCI_TRB_ALIGN 16 /* bytes */
+#define XHCI_TD_ALIGN 64 /* bytes */
+#define XHCI_PAGE_SIZE 4096 /* bytes */
+
+struct xhci_endp_ctx {
+ volatile uint32_t dwEpCtx0;
+#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7)
+#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7)
+#define XHCI_EPCTX_0_EPSTATE_DISABLED 0
+#define XHCI_EPCTX_0_EPSTATE_RUNNING 1
+#define XHCI_EPCTX_0_EPSTATE_HALTED 2
+#define XHCI_EPCTX_0_EPSTATE_STOPPED 3
+#define XHCI_EPCTX_0_EPSTATE_ERROR 4
+#define XHCI_EPCTX_0_EPSTATE_RESERVED_5 5
+#define XHCI_EPCTX_0_EPSTATE_RESERVED_6 6
+#define XHCI_EPCTX_0_EPSTATE_RESERVED_7 7
+#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8)
+#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3)
+#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10)
+#define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F)
+#define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15)
+#define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1)
+#define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16)
+#define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF)
+ volatile uint32_t dwEpCtx1;
+#define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1)
+#define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3)
+#define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3)
+#define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7)
+#define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7)
+#define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1)
+#define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8)
+#define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF)
+#define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16)
+#define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF)
+ volatile uint64_t qwEpCtx2;
+#define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1)
+#define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1)
+#define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
+ volatile uint32_t dwEpCtx4;
+#define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF)
+#define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF)
+#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16)
+#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF)
+ volatile uint32_t dwEpCtx5;
+ volatile uint32_t dwEpCtx6;
+ volatile uint32_t dwEpCtx7;
+};
+
+struct xhci_endp_ctx64 {
+ struct xhci_endp_ctx ctx;
+ volatile uint8_t padding[32];
+};
+
+struct xhci_trb {
+ volatile uint64_t qwTrb0;
+#define XHCI_TRB_0_DIR_IN_MASK (0x80ULL << 0)
+#define XHCI_TRB_0_WLENGTH_MASK (0xFFFFULL << 48)
+ volatile uint32_t dwTrb2;
+#define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF)
+#define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24)
+#define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F)
+#define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17)
+#define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF)
+#define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF)
+#define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF)
+#define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF)
+#define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF)
+#define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22)
+#define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF)
+#define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16)
+
+ volatile uint32_t dwTrb3;
+#define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F)
+#define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10)
+#define XHCI_TRB_3_CYCLE_BIT (1U << 0)
+#define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */
+#define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */
+#define XHCI_TRB_3_ISP_BIT (1U << 2)
+#define XHCI_TRB_3_NSNOOP_BIT (1U << 3)
+#define XHCI_TRB_3_CHAIN_BIT (1U << 4)
+#define XHCI_TRB_3_IOC_BIT (1U << 5)
+#define XHCI_TRB_3_IDT_BIT (1U << 6)
+#define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3)
+#define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7)
+#define XHCI_TRB_3_BEI_BIT (1U << 9)
+#define XHCI_TRB_3_DCEP_BIT (1U << 9)
+#define XHCI_TRB_3_PRSV_BIT (1U << 9)
+#define XHCI_TRB_3_BSR_BIT (1U << 9)
+#define XHCI_TRB_3_TRT_MASK (3U << 16)
+#define XHCI_TRB_3_TRT_NONE (0U << 16)
+#define XHCI_TRB_3_TRT_OUT (2U << 16)
+#define XHCI_TRB_3_TRT_IN (3U << 16)
+#define XHCI_TRB_3_DIR_IN (1U << 16)
+#define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF)
+#define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16)
+#define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F)
+#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16)
+#define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF)
+#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20)
+#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31)
+#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23)
+#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF)
+#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24)
+
+/* Commands */
+#define XHCI_TRB_TYPE_RESERVED 0x00
+#define XHCI_TRB_TYPE_NORMAL 0x01
+#define XHCI_TRB_TYPE_SETUP_STAGE 0x02
+#define XHCI_TRB_TYPE_DATA_STAGE 0x03
+#define XHCI_TRB_TYPE_STATUS_STAGE 0x04
+#define XHCI_TRB_TYPE_ISOCH 0x05
+#define XHCI_TRB_TYPE_LINK 0x06
+#define XHCI_TRB_TYPE_EVENT_DATA 0x07
+#define XHCI_TRB_TYPE_NOOP 0x08
+#define XHCI_TRB_TYPE_ENABLE_SLOT 0x09
+#define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A
+#define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B
+#define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C
+#define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D
+#define XHCI_TRB_TYPE_RESET_EP 0x0E
+#define XHCI_TRB_TYPE_STOP_EP 0x0F
+#define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10
+#define XHCI_TRB_TYPE_RESET_DEVICE 0x11
+#define XHCI_TRB_TYPE_FORCE_EVENT 0x12
+#define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13
+#define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14
+#define XHCI_TRB_TYPE_GET_PORT_BW 0x15
+#define XHCI_TRB_TYPE_FORCE_HEADER 0x16
+#define XHCI_TRB_TYPE_NOOP_CMD 0x17
+
+/* Events */
+#define XHCI_TRB_EVENT_TRANSFER 0x20
+#define XHCI_TRB_EVENT_CMD_COMPLETE 0x21
+#define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22
+#define XHCI_TRB_EVENT_BW_REQUEST 0x23
+#define XHCI_TRB_EVENT_DOORBELL 0x24
+#define XHCI_TRB_EVENT_HOST_CTRL 0x25
+#define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26
+#define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27
+
+/* Error codes */
+#define XHCI_TRB_ERROR_INVALID 0x00
+#define XHCI_TRB_ERROR_SUCCESS 0x01
+#define XHCI_TRB_ERROR_DATA_BUF 0x02
+#define XHCI_TRB_ERROR_BABBLE 0x03
+#define XHCI_TRB_ERROR_XACT 0x04
+#define XHCI_TRB_ERROR_TRB 0x05
+#define XHCI_TRB_ERROR_STALL 0x06
+#define XHCI_TRB_ERROR_RESOURCE 0x07
+#define XHCI_TRB_ERROR_BANDWIDTH 0x08
+#define XHCI_TRB_ERROR_NO_SLOTS 0x09
+#define XHCI_TRB_ERROR_STREAM_TYPE 0x0A
+#define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B
+#define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C
+#define XHCI_TRB_ERROR_SHORT_PKT 0x0D
+#define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E
+#define XHCI_TRB_ERROR_RING_OVERRUN 0x0F
+#define XHCI_TRB_ERROR_VF_RING_FULL 0x10
+#define XHCI_TRB_ERROR_PARAMETER 0x11
+#define XHCI_TRB_ERROR_BW_OVERRUN 0x12
+#define XHCI_TRB_ERROR_CONTEXT_STATE 0x13
+#define XHCI_TRB_ERROR_NO_PING_RESP 0x14
+#define XHCI_TRB_ERROR_EV_RING_FULL 0x15
+#define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16
+#define XHCI_TRB_ERROR_MISSED_SERVICE 0x17
+#define XHCI_TRB_ERROR_CMD_RING_STOP 0x18
+#define XHCI_TRB_ERROR_CMD_ABORTED 0x19
+#define XHCI_TRB_ERROR_STOPPED 0x1A
+#define XHCI_TRB_ERROR_LENGTH 0x1B
+#define XHCI_TRB_ERROR_BAD_MELAT 0x1D
+#define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F
+#define XHCI_TRB_ERROR_EVENT_LOST 0x20
+#define XHCI_TRB_ERROR_UNDEFINED 0x21
+#define XHCI_TRB_ERROR_INVALID_SID 0x22
+#define XHCI_TRB_ERROR_SEC_BW 0x23
+#define XHCI_TRB_ERROR_SPLIT_XACT 0x24
+} __aligned(4);
+
+struct xhci_event_ring_seg {
+ volatile uint64_t qwEvrsTablePtr;
+ volatile uint32_t dwEvrsTableSize;
+ volatile uint32_t dwEvrsReserved;
+};
+
+#endif /* _USB_XHCI_PRIVATE_H_ */
diff --git a/sys/dev/usb/controller/xhcireg.h b/sys/dev/usb/controller/xhcireg.h
--- a/sys/dev/usb/controller/xhcireg.h
+++ b/sys/dev/usb/controller/xhcireg.h
@@ -211,6 +211,129 @@
#define XHCI_ID_EXT_MSI 0x0011
#define XHCI_ID_USB3_TUN 0x0012
+/*
+ * XHCI Debug Capability
+ * From Section 7.6 of xHCI April 2023 Revision 1.2b.
+ */
+#define XHCI_DCID 0x0000
+
+#define XHCI_DCDB 0x0004
+#define XHCI_DCDB_GET(x) (((x) >> 8) & 0xFF)
+#define XHCI_DCDB_MASK 0x0000FF00
+#define XHCI_DCDB_OUT 0x0000
+#define XHCI_DCDB_IN 0x0100
+#define XHCI_DCDB_INVAL 0xFF00
+
+#define XHCI_DCERSTSZ 0x0008 /* Event Ring Segment Table size */
+#define XHCI_DCERSTBA_LO 0x0010
+#define XHCI_DCERSTBA_HI 0x0014
+#define XHCI_DCERDP_LO 0x0018
+#define XHCI_DCERDP_HI 0x001C
+
+#define XHCI_DCCTRL 0x0020 /* Debug Control */
+#define XHCI_DCCTRL_DCR 0x00000001
+#define XHCI_DCCTRL_DCR_GET(x) (((x) ) & 0x01)
+#define XHCI_DCCTRL_LSE 0x00000002
+#define XHCI_DCCTRL_LSE_GET(x) (((x) >> 1) & 0x01)
+#define XHCI_DCCTRL_HOT 0x00000004
+#define XHCI_DCCTRL_HOT_GET(x) (((x) >> 2) & 0x01)
+#define XHCI_DCCTRL_HIT 0x00000008
+#define XHCI_DCCTRL_HIT_GET(x) (((x) >> 3) & 0x01)
+#define XHCI_DCCTRL_DRC 0x00000010
+#define XHCI_DCCTRL_DRC_GET(x) (((x) >> 4) & 0x01)
+#define XHCI_DCCTRL_MBS_GET(x) (((x) >> 16) & 0xFF)
+#define XHCI_DCCTRL_ADDR_GET(x) (((x) >> 24) & 0x07)
+#define XHCI_DCCTRL_DCE 0x80000000
+#define XHCI_DCCTRL_DCE_GET(x) (((x) >> 31) & 0x01)
+
+#define XHCI_DCST 0x0024 /* Status */
+#define XHCI_DCST_ER 0x00000001
+#define XHCI_DCST_ER_GET(x) (((x) ) & 0x01)
+#define XHCI_DCST_SBR 0x00000004
+#define XHCI_DCST_SBR_GET(x) (((x) >> 2) & 0x01)
+#define XHCI_DCST_PORT_GET(x) (((x) >> 24) & 0xFF)
+
+#define XHCI_DCPORTSC 0x0028 /* Port Control */
+#define XHCI_DCPORTSC_CCS 0x00000001
+#define XHCI_DCPORTSC_CCS_GET(x) (((x) ) & 0x01)
+#define XHCI_DCPORTSC_PED 0x00000002
+#define XHCI_DCPORTSC_PED_GET(x) (((x) >> 1) & 0x01)
+#define XHCI_DCPORTSC_PR 0x00000010
+#define XHCI_DCPORTSC_PR_GET(x) (((x) >> 4) & 0x01)
+#define XHCI_DCPORTSC_PLS_GET(x) (((x) >> 5) & 0x0F)
+#define XHCI_DCPORTSC_PLS_U0 0x00
+#define XHCI_DCPORTSC_PLS_U1 0x01
+#define XHCI_DCPORTSC_PLS_U2 0x02
+#define XHCI_DCPORTSC_PLS_U3 0x03
+#define XHCI_DCPORTSC_PLS_DISABLED 0x04
+#define XHCI_DCPORTSC_PLS_RXDETECTED 0x05
+#define XHCI_DCPORTSC_PLS_INACTIVE 0x06
+#define XHCI_DCPORTSC_PLS_POLLING 0x07
+#define XHCI_DCPORTSC_PLS_RECOVERY 0x08
+#define XHCI_DCPORTSC_PLS_HOTRESET 0x09
+#define XHCI_DCPORTSC_SPEED_GET(x) (((x) >> 10) & 0x0F)
+#define XHCI_DCPORTSC_CSC 0x00020000
+#define XHCI_DCPORTSC_CSC_GET(x) (((x) >> 17) & 0x01)
+#define XHCI_DCPORTSC_PRC 0x00200000
+#define XHCI_DCPORTSC_PRC_GET(x) (((x) >> 21) & 0x01)
+#define XHCI_DCPORTSC_PLC 0x00400000
+#define XHCI_DCPORTSC_PLC_GET(x) (((x) >> 22) & 0x01)
+#define XHCI_DCPORTSC_CEC 0x00800000
+#define XHCI_DCPORTSC_CEC_GET(x) (((x) >> 23) & 0x01)
+#define XHCI_DCCP_LO 0x0030 /* Context Pointer */
+#define XHCI_DCCP_HI 0x0034
+#define XHCI_DCDDI1 0x0038 /* Device Descriptor Info */
+#define XHCI_DCDDI2 0x003C /* Device Descriptor Info */
+
+/* DbC CIC offset in uint32 */
+#define XHCI_DCDBCIC_STR0DESC_LO 0x0000
+#define XHCI_DCDBCIC_STR0DESC_HI 0x0001
+#define XHCI_DCDBCIC_MANUDESC_LO 0x0002
+#define XHCI_DCDBCIC_MANUDESC_HI 0x0003
+#define XHCI_DCDBCIC_PRODDESC_LO 0x0004
+#define XHCI_DCDBCIC_PRODDESC_HI 0x0005
+#define XHCI_DCDBCIC_SERIALDESC_LO 0x0006
+#define XHCI_DCDBCIC_SERIALDESC_HI 0x0007
+#define XHCI_DCDBCIC_DESCLEN 0x0008
+#define XHCI_DCDBCIC_STR0DESC_LEN_GET(x) (((x) >> 0) & 0xff)
+#define XHCI_DCDBCIC_STR0DESC_LEN_SET(x) (((x) & 0xff) << 0)
+#define XHCI_DCDBCIC_MANUDESC_LEN_GET(x) (((x) >> 8) & 0xff)
+#define XHCI_DCDBCIC_MANUDESC_LEN_SET(x) (((x) & 0xff) << 8)
+#define XHCI_DCDBCIC_PRODDESC_LEN_GET(x) (((x) >> 16) & 0xff)
+#define XHCI_DCDBCIC_PRODDESC_LEN_SET(x) (((x) & 0xff) << 16)
+#define XHCI_DCDBCIC_SERIALDESC_LEN_GET(x) (((x) >> 24) & 0xff)
+#define XHCI_DCDBCIC_SERIALDESC_LEN_SET(x) (((x) & 0xff) << 24)
+
+#define XHCI_DCSTATUS(ctrl, portsc) \
+ (XHCI_DCCTRL_DCE_GET(ctrl) << 4 | \
+ XHCI_DCPORTSC_CCS_GET(portsc) << 3 | \
+ XHCI_DCPORTSC_PED_GET(portsc) << 2 | \
+ XHCI_DCPORTSC_PR_GET(portsc) << 1 | \
+ XHCI_DCCTRL_DCR_GET(ctrl))
+#define XHCI_DCPORTSC_ACK_MASK \
+ (XHCI_DCPORTSC_PED | \
+ XHCI_DCPORTSC_CSC | XHCI_DCPORTSC_PRC | \
+ XHCI_DCPORTSC_PLC | XHCI_DCPORTSC_CEC)
+
+#define XHCI_DCPORT_ST_OFF 0x00
+#define XHCI_DCPORT_ST_DISCONNECTED 0x10 /* DCE only */
+#define XHCI_DCPORT_ST_DISCONNECTED_RUNNING 0x11 /* XXX: DCE + DCR */
+#define XHCI_DCPORT_ST_DISABLED 0x18 /* DCE + CCS */
+#define XHCI_DCPORT_ST_RESETTING 0x1a /* DCE + CCS + PR */
+#define XHCI_DCPORT_ST_ENABLED 0x1c /* DCE + CCS + PED */
+#define XHCI_DCPORT_ST_CONFIGURED 0x1d /* DCE + CCS + PED + DCR */
+
+#define XHCI_DC_MAXPACKETLEN 1024
+/*
+ * While Sec 7.6.3.2 describes Endpoint IDs should be 0 or 1,
+ * Intel chips use Device Context Index (Sec 4.5.1) instead.
+ */
+#define XHCI_DC_EPID_OUT 0
+#define XHCI_DC_EPID_IN 1
+#define XHCI_DC_EPID_OUT_INTEL 2
+#define XHCI_DC_EPID_IN_INTEL 3
+#define XHCI_DC_SLOT 1
+
/* XHCI register R/W wrappers */
#define XREAD1(sc, what, a) \
bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
@@ -221,6 +344,9 @@
#define XREAD4(sc, what, a) \
bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off)
+#define XREAD44LH(sc, what, a) \
+ ((uint64_t)XREAD4((sc), what, a##_LO) | \
+ ((uint64_t)XREAD4((sc), what, a##_HI) << 32))
#define XWRITE1(sc, what, a, x) \
bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off, (x))
@@ -230,5 +356,10 @@
#define XWRITE4(sc, what, a, x) \
bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off, (x))
+#define XWRITE44LH(sc, what, a, x) \
+ do { \
+ XWRITE4((sc), what, a##_LO, (uint32_t)((x) & 0xffffffff)); \
+ XWRITE4((sc), what, a##_HI, (uint32_t)((x) >> 32)); \
+ } while (0)
#endif /* _XHCIREG_H_ */
diff --git a/sys/x86/include/metadata.h b/sys/x86/include/metadata.h
--- a/sys/x86/include/metadata.h
+++ b/sys/x86/include/metadata.h
@@ -35,6 +35,7 @@
#define MODINFOMD_MODULEP 0x1006
#define MODINFOMD_VBE_FB 0x1007
#define MODINFOMD_EFI_ARCH 0x1008
+#define MODINFOMD_XHCI_DEBUG 0x1009
/*
* This is not the same as the UEFI standard EFI_MEMORY_ATTRIBUTES_TABLE, though

File Metadata

Mime Type
text/plain
Expires
Tue, Jan 27, 9:25 AM (19 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28053661
Default Alt Text
D53472.diff (97 KB)

Event Timeline