diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
index b3395bdfdf53..41c4a7c05903 100644
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -2,6 +2,7 @@
 LOADER_NET_SUPPORT?=	yes
 LOADER_MSDOS_SUPPORT?=	yes
 LOADER_UFS_SUPPORT?=	yes
+LOADER_USBDBC_SUPPORT?=	yes
 LOADER_CD9660_SUPPORT?=	no
 LOADER_EXT2FS_SUPPORT?=	no
 
@@ -23,7 +24,12 @@ SRCS=	autoload.c \
 	self_reloc.c \
 	vers.c \
 	gfx_fb.c \
-	8x16.c
+	8x16.c 
+
+.if ${LOADER_USBDBC_SUPPORT} != "no"
+CFLAGS+=        -I${SYSDIR}/dev/usb/controller
+SRCS+=		usb_dbc.c
+.endif
 
 CFLAGS+=	-I${.CURDIR}/../loader
 .if ${MK_LOADER_ZFS} != "no"
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
index 3bc74ea6354c..25d02f499763 100644
--- a/stand/efi/loader/conf.c
+++ b/stand/efi/loader/conf.c
@@ -78,6 +78,7 @@ struct netif_driver *netif_drivers[] = {
 
 extern struct console efi_console;
 extern struct console eficom;
+extern struct console udb_console;
 #if defined(__aarch64__) && __FreeBSD_version < 1500000
 /* Hack for backward compatibility -- but only for a while */
 extern struct console comconsole;
@@ -91,6 +92,7 @@ extern struct console spinconsole;
 struct console *consoles[] = {
 	&efi_console,
 	&eficom,
+	&udb_console,
 #if defined(__aarch64__) && __FreeBSD_version < 1500000
 	&comconsole,
 #endif
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
index e4d62f2e940c..cedd0b6469f2 100644
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -59,6 +59,8 @@
 #include "efizfs.h"
 #include "framebuffer.h"
 
+#include "usb_dbc.h"
+
 #include "platform/acfreebsd.h"
 #include "acconfig.h"
 #define ACPI_SYSTEM_XFACE
@@ -1750,3 +1752,66 @@ command_netserver(int argc, char *argv[])
 
 COMMAND_SET(netserver, "netserver", "change or display netserver URI",
     command_netserver);
+
+static int
+command_udb_info(int argc, char *argv[])
+{
+
+	udb_info();
+
+	return (CMD_OK);
+}
+COMMAND_SET(udb_info, "udb_info", "USB DbC configuration info",
+    command_udb_info);
+
+static int
+command_udb_send(int argc, char *argv[])
+{
+	size_t len;
+	size_t i;
+
+	if (argc != 2) {
+		printf("argument required\n");
+		return (CMD_ERROR);
+	}
+	printf("Sending '%s\\r\\n'...\n", argv[1]);
+	len = strlen(argv[1]);
+	for (i = 0; i < len; i++) {
+		udb_console.c_out((u_int)argv[1][i]); 
+	}
+	udb_console.c_out('\r'); 
+	udb_console.c_out('\n'); 
+	return (CMD_OK);
+}
+COMMAND_SET(udb_send, "udb_send", "USB DbC send string test",
+    command_udb_send);
+
+static int
+command_udb_probe(int argc, char *argv[])
+{
+	udb_console.c_probe(&udb_console);
+
+	return (CMD_OK);
+}
+COMMAND_SET(udb_probe, "udb_probe", "USB DbC probe",
+    command_udb_probe);
+
+static int
+command_udb_recon(int argc, char *argv[])
+{
+	udb_console.c_init(1);
+
+	return (CMD_OK);
+}
+COMMAND_SET(udb_recon, "udb_recon", "USB DbC reconnect",
+    command_udb_recon);
+
+static int
+command_udb_init(int argc, char *argv[])
+{
+
+	udb_console.c_init(0);
+	return (CMD_OK);
+}
+COMMAND_SET(udb_init, "udb_init", "USB DbC init",
+    command_udb_init);
diff --git a/stand/efi/loader/usb_dbc.c b/stand/efi/loader/usb_dbc.c
new file mode 100644
index 000000000000..8977126defc4
--- /dev/null
+++ b/stand/efi/loader/usb_dbc.c
@@ -0,0 +1,1073 @@
+#include <sys/param.h>
+#include <bootstrap.h>
+#include <efi.h>
+#include <efilib.h>
+#include <xhcireg.h>
+
+#include "usb_xhci.h"
+#include "usb_dbc.h"
+
+#define	SFENCE(x)	__asm volatile("sfence" ::: "memory")
+#define	PAUSE(x)	__asm volatile("pause" ::: "memory")
+
+#define DMA_DESC_CAP 7
+struct dma {
+	UINTN pages;
+	EFI_PHYSICAL_ADDRESS paddr;
+	VOID *cpu_addr;
+	VOID *mapping;
+};
+
+struct udb_ctx {
+	uint32_t info[16];
+	uint32_t ep_out[16];
+	uint32_t ep_in[16];
+};
+
+struct udb_efi {
+	EFI_HANDLE img_hand;
+	EFI_HANDLE pci_hand;
+	EFI_PCI_IO_PROTOCOL *pciio;
+};
+static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
+
+/* Must be aligned with xhci_debug_ring_ctx in xhci.h */
+struct xhci_debug_softc {
+	int open;
+	uint64_t xhci_xecp_offset;
+
+	struct xhci_event_ring_seg *udb_erst;
+	struct xhci_debug_ring udb_ering;
+	struct xhci_debug_ring udb_oring;
+	struct xhci_debug_ring udb_iring;
+	struct xhci_debug_work_ring udb_owork;
+
+/* private */
+	struct xhci_debug_reg *udb_reg;
+	struct udb_ctx *udb_ctx;
+
+	uint32_t xhc_cf8;
+	uint64_t xhc_mmio_phys;
+	uint64_t xhc_mmio_size;
+	void *xhc_mmio;
+
+	struct udb_efi efi;
+	struct dma dma_desc[DMA_DESC_CAP];
+
+	int dma_allocated;
+	int init;
+
+	char *udb_str;
+};
+static struct xhci_debug_softc *udb_sc;
+
+static uint32_t ind(uint32_t);
+static void outd(uint32_t, uint32_t);
+static uint32_t pci_read_config(uint32_t, uint32_t);
+static void pci_write_config(uint32_t, uint32_t, uint32_t);
+static void udb_enable(struct xhci_debug_softc *);
+static void udb_disable(struct xhci_debug_softc *);
+static int udb_init_ring(struct xhci_debug_softc *);
+
+static void udb_probe(struct console *);
+static int udb_init(int);
+static int udb_find(struct xhci_debug_softc *);
+static int udb_setup(struct xhci_debug_softc *);
+static void *udb_alloc_dma(struct xhci_debug_softc *, uint64_t);
+static void udb_free_dma(struct xhci_debug_softc *, void *);
+static void udb_uninit_dma(struct xhci_debug_softc *);
+static int udb_init_dma(struct xhci_debug_softc *);
+static void udb_xhci_debug_ring_init(struct xhci_debug_softc *, struct xhci_debug_ring *,
+    int, int);
+static void udb_init_ep(uint32_t *, size_t, uint64_t, uint32_t, uint64_t);
+
+static struct xhci_debug_softc *udb_open(void);
+static void udb_close(struct xhci_debug_softc *);
+static void udb_pop_events(struct xhci_debug_softc *);
+static void udb_init_strings(struct xhci_debug_softc *, uint32_t *);
+static uint64_t v2p(struct xhci_debug_softc *, void *);
+static void udb_flush(struct xhci_debug_softc *);
+
+static int udb_getc(void);
+static void udb_putc(int);
+static int udb_ischar(void);
+
+static void push_trb(struct xhci_debug_ring *, uint64_t, uint64_t);
+static int xhci_debug_work_ring_full(const struct xhci_debug_work_ring *);
+static uint64_t xhci_debug_work_ring_size(const struct xhci_debug_work_ring *);
+static int64_t push_work(struct xhci_debug_work_ring *, const char *, int64_t);
+
+static int xhci_debug_ring_full(const struct xhci_debug_ring *);
+static void flush_range(void *, uint32_t);
+
+struct console udb_console = {
+	.c_name = "udb",
+	.c_desc = "USB DbC serial port",
+	.c_flags = 0,
+	.c_probe = udb_probe,
+	.c_init = udb_init,
+	.c_out = udb_putc,
+	.c_in = udb_getc,
+	.c_ready = udb_ischar
+};
+
+static struct {
+	uint16_t vendor;
+	uint16_t device;
+} ids[] = {
+	{ XHC_VEN_INTEL, XHC_DEV_COMET_LAKE_LP },
+	{ XHC_VEN_INTEL, XHC_DEV_COMET_LAKE },
+	{ XHC_VEN_INTEL, XHC_DEV_N2000 },
+	{ XHC_VEN_INTEL, XHC_DEV_C3000 },
+	{ XHC_VEN_INTEL, XHC_DEV_C210 },
+	{ XHC_VEN_INTEL, XHC_DEV_E8000 },
+	{ XHC_VEN_INTEL, XHC_DEV_PUMA },
+	{ XHC_VEN_INTEL, XHC_DEV_CELERON },
+	{ XHC_VEN_INTEL, XHC_DEV_ICE_LAKE },
+	{ XHC_VEN_INTEL, XHC_DEV_TIGER_LAKE_H },
+	{ XHC_VEN_INTEL, XHC_DEV_N3350 },
+	{ XHC_VEN_INTEL, XHC_DEV_C220 },
+	{ XHC_VEN_INTEL, XHC_DEV_NINE },
+	{ XHC_VEN_INTEL, XHC_DEV_C610 },
+	{ XHC_VEN_INTEL, XHC_DEV_EIGHT },
+	{ XHC_VEN_INTEL, XHC_DEV_WILDCAT_POINT },
+	{ XHC_VEN_INTEL, XHC_DEV_SUNRISE_POINT },
+	{ XHC_VEN_INTEL, XHC_DEV_CANNON_POINT },
+	{ XHC_VEN_INTEL, XHC_DEV_SILVER },
+	{ XHC_VEN_INTEL, XHC_DEV_TIGER_LAKE },
+	{ XHC_VEN_INTEL, XHC_DEV_C230 },
+	{ XHC_VEN_INTEL, XHC_DEV_C620 },
+	{ XHC_VEN_INTEL, XHC_DEV_Z370 },
+	{ XHC_VEN_INTEL, XHC_DEV_CANNON_LAKE },
+
+	{ XHC_VEN_AMD, XHC_DEV_KERNCZ },
+	{ XHC_VEN_AMD, XHC_DEV_STARSHIP },
+	{ XHC_VEN_AMD, XHC_DEV_MATISSE },
+	{ XHC_VEN_AMD, XHC_DEV_X399 },
+	{ XHC_VEN_AMD, XHC_DEV_X370 },
+	{ XHC_VEN_AMD, XHC_DEV_B350 },
+	{ XHC_VEN_AMD, XHC_DEV_400 },
+	{ XHC_VEN_AMD, XHC_DEV_FCH_1 },
+	{ XHC_VEN_AMD, XHC_DEV_FCH_2 },
+	{ XHC_VEN_AMD, XHC_DEV_FCH_3 },
+
+	{ XHC_VEN_VMWARE, XHC_DEV_VMWARE_1 },
+	{ XHC_VEN_VMWARE, XHC_DEV_VMWARE_2 },
+
+	{ XHC_VEN_ASMEDIA, XHC_DEV_ASM1042 },
+	{ XHC_VEN_ASMEDIA, XHC_DEV_ASM1042A },
+	{ XHC_VEN_ASMEDIA, XHC_DEV_ASM1143 },
+	{ XHC_VEN_ASMEDIA, XHC_DEV_ASM3242 },
+	{ 0, 0 }
+};
+
+void
+udb_probe(struct console *cons)
+{
+	struct xhci_debug_softc *sc;
+	EFI_STATUS status;
+	EFI_HANDLE *h0, *h;
+	EFI_PCI_IO_PROTOCOL *pciio;
+	UINTN hlen;
+	uint64_t devfn;
+	char buf[32];
+	int i;
+
+	h0 = h = NULL;
+	if (udb_sc != NULL)
+		return;
+	sc = calloc(1, sizeof(*sc));
+	if (sc == NULL)
+		return;
+
+	/* Get PCI I/O handle. */
+	hlen = 0;
+	status = BS->LocateHandle(ByProtocol, &pciio_guid, NULL, &hlen, NULL);
+	printf("%s: LocateHandle1 = %lx\n", __func__, status);
+	if (status != EFI_BUFFER_TOO_SMALL)
+		goto error;
+
+	h0 = malloc(hlen);
+	status = BS->LocateHandle(ByProtocol, &pciio_guid, NULL, &hlen, h0);
+	printf("%s: LocateHandle2 = %lu\n", __func__, status);
+	if (status != EFI_SUCCESS)
+		goto error;
+	hlen /= sizeof(EFI_HANDLE);
+
+	/* Look up vendor IDs. */
+	for (h = h0; h < h0 + hlen; h++) {
+		UINT32 dev_ven;
+
+		status = OpenProtocolByHandle(*h, &pciio_guid,
+		    (void **)&pciio);
+		if (status != EFI_SUCCESS)
+			continue;
+		status = pciio->Pci.Read(pciio, EfiPciIoWidthUint32,
+		    PCIR_DEVVENDOR, 1, &dev_ven);
+		if (status != EFI_SUCCESS) {
+			BS->CloseProtocol(h, &pciio_guid, IH, NULL);
+			continue;
+		}
+		printf("%s: dev_ven = %08x\n", __func__, dev_ven);
+		for (i = 0; i < sizeof(ids)/sizeof(ids[0]); i++) {
+			if (dev_ven != ((ids[i].device << 16) | ids[i].vendor))
+				continue;
+			sc->efi.pci_hand = h;
+			sc->efi.pciio = pciio;
+			udb_sc = sc;
+			goto done;
+		}
+	}
+done:
+	snprintf(buf, sizeof(buf), "0x%016llx", (unsigned long long)udb_sc);
+	setenv("usb_dbc_sc", buf, 1);
+	free(h0);
+
+	for (devfn = 0; devfn < 256; devfn++) {
+		uint32_t dev = (devfn & 0xf8) >> 3;
+		uint32_t fun = devfn & 0x07;
+		uint32_t cf8 = (1UL << 31) | (dev << 11) | (fun << 8);
+		uint32_t hdr = (pci_read_config(cf8, 3) & 0xff0000U) >> 16;
+
+		if (hdr == 0 || hdr == 0x80) {
+			if ((pci_read_config(cf8, 2) >> 8) == XHC_CLASSC) {
+				sc->xhc_cf8 = cf8;
+				break;
+			}
+		}
+	}
+	if (sc->xhc_cf8 == 0) {
+		printf("%s: Compatible xHC not found.\n", __func__);
+		goto error;
+	}
+	if (udb_setup(sc) != 0) {
+		cons->c_flags = C_PRESENTIN | C_PRESENTOUT;
+
+		udb_disable(sc);
+		if (udb_init_dma(sc) != 0) {
+			printf("USB DbC DMA configuration error\n");
+			return;
+		}
+		udb_init_ring(sc);
+		udb_enable(sc);
+	}
+	return;
+error:
+	free(h0);
+	free(sc);
+	sc = NULL;
+	return;
+}
+
+static uint32_t
+ind(uint32_t port)
+{
+	uint32_t val;
+
+	__asm volatile(
+	    "xorq %%rax, %%rax\n\t"
+	    "movq %1, %%rdx\n\t"
+	    "inl %%dx, %%eax\n\t"
+	    : "=a"(val)
+	    : "g"((uint64_t)port));
+	return (val);
+}
+
+static void
+outd(uint32_t port, uint32_t val)
+{
+
+	__asm volatile(
+	    "movq %0, %%rdx\n\t"
+	    "movq %1, %%rax\n\t"
+	    "outl %%eax, %%dx\n\t"
+	    :
+	    : "g"((uint64_t)port), "g"((uint64_t)val));
+}
+
+static uint32_t
+pci_read_config(uint32_t cf8, uint32_t reg)
+{
+	uint32_t addr = (cf8 & 0xffffff03UL) | (reg << 2);
+
+	outd(0xcf8, addr);
+	return (ind(0xcfc));
+}
+
+static void
+pci_write_config(uint32_t cf8, uint32_t reg, uint32_t val)
+{
+	uint32_t addr = (cf8 & 0xffffff03UL) | (reg << 2);
+
+	outd(0xcf8, addr);
+	outd(0xcfc, val);
+}
+
+static void
+udb_enable(struct xhci_debug_softc *sc)
+{
+
+	sc->udb_reg->ctrl |= XHCI_DCCTRL_DCE;
+	SFENCE();
+	while ((sc->udb_reg->ctrl & XHCI_DCCTRL_DCE) == 0)
+		PAUSE();
+
+	sc->udb_reg->portsc |= XHCI_PS_PED;
+}
+
+static int
+udb_init(int arg)
+{
+	struct xhci_debug_softc *sc = udb_sc;
+
+	if (sc == NULL) {		/* allocated in c_probe */
+		printf("USB DbC not found\n");
+		return (CMD_ERROR);
+	}
+
+	if (sc->udb_reg == NULL) {	/* set in c_probe */
+		printf("USB DbC register not found\n");
+		return (CMD_ERROR);
+	}
+
+	while ((sc->udb_reg->ctrl & XHCI_DCCTRL_DCR) == 0) {
+		printf("Insert a debug cable: PR=%d, PLS=%d, DCR=0\n",
+		    ((sc->udb_reg->portsc & XHCI_PS_PR) != 0),
+		    XHCI_PS_PLS_GET(sc->udb_reg->portsc));
+		PAUSE();
+	}
+	printf("Debug Cable Inserted: PR=%d, PLS=%d, DCR=1\n",
+		    ((sc->udb_reg->portsc & XHCI_PS_PR) != 0),
+		    XHCI_PS_PLS_GET(sc->udb_reg->portsc));
+
+	sc->open = 1;
+	sc->init++;
+
+	return (CMD_OK);
+}
+
+static void
+udb_disable(struct xhci_debug_softc *sc)
+{
+	sc->udb_reg->portsc &= ~XHCI_PS_PED;
+	SFENCE();
+	sc->udb_reg->ctrl &= ~XHCI_DCCTRL_DCE;
+
+	while (sc->udb_reg->ctrl & XHCI_DCCTRL_DCE)
+		PAUSE();
+}
+
+static int
+udb_init_ring(struct xhci_debug_softc *sc)
+{
+	uint64_t erdp, out, in;
+
+	if (sc->udb_reg == NULL) {
+		printf("%s: register is empty\n", __func__);
+		return (0);
+	}
+	/* Create an event ring. */
+	udb_xhci_debug_ring_init(sc, &sc->udb_ering, 0, XHCI_DB_INVAL);
+	if ((erdp = v2p(sc, sc->udb_ering.trb)) == 0)
+		return (0);
+	memset(sc->udb_erst, 0, sizeof(*sc->udb_erst));
+	/* htole64? */
+	sc->udb_erst->qwEvrsTablePtr = htole64(erdp);
+	sc->udb_erst->dwEvrsTableSize = htole32(UDB_TRB_RING_CAP);
+	printf("%s: ERDP=0x%016llx\n", __func__, (unsigned long long)erdp);
+
+	/* Create an output ring. */
+	udb_xhci_debug_ring_init(sc, &sc->udb_oring, 1, XHCI_DB_OUT);
+	if ((out = v2p(sc, sc->udb_oring.trb)) == 0)
+		return (0);
+
+	/* Create an input ring. */
+	udb_xhci_debug_ring_init(sc, &sc->udb_iring, 1, XHCI_DB_IN);
+	if ((in = v2p(sc, sc->udb_iring.trb)) == 0)
+		return (0);
+
+	/* Initialize DbC context. */
+	memset(sc->udb_ctx, 0, sizeof(*sc->udb_ctx));
+	udb_init_strings(sc, sc->udb_ctx->info);
+
+	/* Initialize endppints. */ 
+	udb_init_ep(sc->udb_ctx->ep_out, sizeof(*sc->udb_ctx->ep_out),
+	    (uint64_t)XHCI_DCCTRL_MBS_GET(sc->udb_reg->ctrl), EP_BULK_OUT, out);
+	udb_init_ep(sc->udb_ctx->ep_in, sizeof(*sc->udb_ctx->ep_in),
+	    (uint64_t)XHCI_DCCTRL_MBS_GET(sc->udb_reg->ctrl), EP_BULK_IN, in);
+
+	/* Configure register values. */
+	sc->udb_reg->erstsz = 1;
+	sc->udb_reg->erstba = v2p(sc, sc->udb_erst);
+	sc->udb_reg->erdp = erdp;
+	sc->udb_reg->cp = v2p(sc, sc->udb_ctx);
+	sc->udb_reg->ddi1 = (DC_VENDOR   << 16) | DC_PROTOCOL;
+	sc->udb_reg->ddi2 = (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, UDB_TRB_RING_BYTES);
+	flush_range(sc->udb_oring.trb, UDB_TRB_RING_BYTES);
+	flush_range(sc->udb_iring.trb, UDB_TRB_RING_BYTES);
+	flush_range(sc->udb_owork.buf, UDB_WORK_RING_BYTES);
+
+	sc->udb_owork.enq = 0;
+	sc->udb_owork.deq = 0;
+	sc->udb_owork.paddr = v2p(sc, sc->udb_owork.buf);
+
+	return (1);
+}
+
+static int
+udb_find(struct xhci_debug_softc *sc)
+{
+	uint32_t *xcap;
+	uint8_t *mmio;
+	uint32_t *hccp1;
+
+	mmio = (uint8_t *)sc->xhc_mmio;
+	hccp1 = (uint32_t *)(void *)(mmio + 0x10);
+
+	/* Extended Capability Pointer in HCCP1 must be non-zero */
+	if ((*hccp1 &0xffff0000) == 0)
+		return (0);
+
+	/*
+	 * Look up ID=0x0a in the capability list
+	 * located at mmio + (HCCPARAMS1[31:16] << 2).
+	 */
+	xcap = (uint32_t *)(void *)
+	    (mmio + (((*hccp1 & 0xffff0000) >> 16) << 2));
+
+#if 0
+	while (XHCI_XECP_ID(*xcap) != XHCI_ID_USB_DEBUG && XHCI_XECP_NEXT(*xcap)) {
+		printf("%s: looking for %02x: %02x\n",
+		    __func__, XHCI_ID_USB_DEBUG, XHCI_XECP_ID(*xcap));
+		xcap += XHCI_XECP_NEXT(*xcap);
+	}
+	if (XHCI_XECP_ID(*xcap) != XHCI_ID_USB_DEBUG) {
+		printf("%s: XHCI_ID_USB_DEBUG is not found\n", __func__);
+		return (0);
+	}
+#else
+#define        ID(x) (*(x) & 0xff)
+#define        NEXT(x) ((*(x) & 0xff00) >> 8)
+	while (ID(xcap) != XHCI_ID_USB_DEBUG && NEXT(xcap)) {
+		printf("%s: looking for DBC_ID(0x0a): %02x\n",
+		    __func__, ID(xcap));
+		xcap += NEXT(xcap);
+	}
+	if (ID(xcap) != XHCI_ID_USB_DEBUG) {
+		printf("%s: DBC_ID is not found\n", __func__);
+		return (0);
+	}
+#endif
+	sc->xhci_xecp_offset = (uint64_t)xcap - (uint64_t)mmio;
+	sc->udb_reg = (struct xhci_debug_reg *)xcap;
+	return (1);
+}
+
+static int
+udb_setup(struct xhci_debug_softc *sc)
+{
+	uint32_t bar0;
+	uint64_t bar1;
+
+	/* Parse the BAR and map the registers */
+	bar0 = pci_read_config(sc->xhc_cf8, 4);
+	bar1 = pci_read_config(sc->xhc_cf8, 5);
+
+	if ((bar0 & 0x1) != 0 || ((bar0 & 0x6) >> 1) != 2) {
+		printf("%s: BAR must be 64-bit.\n", __func__);
+		return (0);
+	}
+
+	pci_write_config(sc->xhc_cf8, 4, 0xffffffff);
+	sc->xhc_mmio_size = ~(pci_read_config(sc->xhc_cf8, 4) & 0xfffffff0) + 1;
+	pci_write_config(sc->xhc_cf8, 4, bar0);
+
+	sc->xhc_mmio_phys = (bar0 & 0xfffffff0) | (bar1 << 32);
+	sc->xhc_mmio = (void *)sc->xhc_mmio_phys;
+
+	if (sc->xhc_mmio == NULL) {
+		printf("%s: mmio_phys is NULL.\n", __func__);
+		return (0);
+	}
+
+	return (udb_find(sc));
+}
+
+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 < DMA_DESC_CAP; i++) {
+		dma = &sc->dma_desc[i];
+		if (dma->cpu_addr == NULL)
+			break;
+	}
+	if (i == DMA_DESC_CAP) {
+		printf("%s: Out of DMA descriptor\n", __func__);
+		return (NULL);
+	}
+	status = sc->efi.pciio->AllocateBuffer(sc->efi.pciio,
+	    AllocateAnyPages,
+	    EfiRuntimeServicesData,
+	    pages,
+	    &addr,
+	    EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED);
+	if (EFI_ERROR(status)) {
+		printf("%s: AllocateBuffer failed: 0x%lx\n",
+		    __func__, status);
+		return (NULL);
+	}
+
+	dma->pages = pages;
+	dma->cpu_addr = addr;
+	printf("%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 < DMA_DESC_CAP; i++) {
+		dma = &sc->dma_desc[i];
+		if (dma->cpu_addr == addr)
+			break;
+	}
+	if (i == DMA_DESC_CAP)
+		return;
+	if (dma->mapping) {
+		status = sc->efi.pciio->Unmap(sc->efi.pciio, dma->mapping);
+		if (EFI_ERROR(status)) {
+			printf("%s: Unmap failed: 0x%lx\n",
+			    __func__, status);
+			return;
+		}
+	}
+	status = sc->efi.pciio->FreeBuffer(sc->efi.pciio, dma->pages, addr);
+	if (EFI_ERROR(status)) {
+		printf("%s: FreeBuffer failed: 0x%lx\n",
+		    __func__, status);
+		return;
+	}
+	memset(&dma, 0, sizeof(*dma));
+}
+
+static void
+udb_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_owork.buf);
+
+	sc->dma_allocated = 0;
+}
+
+static int
+udb_init_dma(struct xhci_debug_softc *sc)
+{
+#define	ALLOC_DMA(p, type, order)				\
+	do { 							\
+		p = (type)udb_alloc_dma(sc, order); 	\
+		if (p == NULL) { 				\
+			udb_uninit_dma(sc);			\
+			return (1);				\
+		}						\
+	} while (0)
+
+	if (sc->dma_allocated)
+		return (0);
+	ALLOC_DMA(sc->udb_ctx, struct udb_ctx *, 0);
+	ALLOC_DMA(sc->udb_erst, struct xhci_event_ring_seg *, 0);
+	ALLOC_DMA(sc->udb_ering.trb, struct xhci_trb *, UDB_TRB_RING_ORDER);
+	ALLOC_DMA(sc->udb_iring.trb, struct xhci_trb *, UDB_TRB_RING_ORDER);
+	ALLOC_DMA(sc->udb_oring.trb, struct xhci_trb *, UDB_TRB_RING_ORDER);
+	ALLOC_DMA(sc->udb_owork.buf, uint8_t *, UDB_WORK_RING_ORDER);
+	ALLOC_DMA(sc->udb_str, char *, 0);
+	sc->dma_allocated = 1;
+
+	return (0);
+#undef	ALLOC_DMA
+}
+
+uint64_t
+v2p(struct xhci_debug_softc *sc, void *virt)
+{
+	UINTN i = 0;
+	UINTN offset = 0;
+	UINTN needed = 0;
+	UINTN mapped = 0;
+	struct dma *dma = NULL;
+	EFI_PHYSICAL_ADDRESS paddr = 0;
+	EFI_PCI_IO_PROTOCOL *pci = sc->efi.pciio;
+	EFI_STATUS status;
+	VOID *mapping = NULL;
+
+	for (; i < DMA_DESC_CAP; i++) {
+		dma = &sc->dma_desc[i];
+		UINTN p = 0;
+
+#if 0
+		printf("%s: dma_desc[%lu].cpu_addr = %p\n", __func__,
+		    i, dma->cpu_addr);
+#endif
+		for (; p < dma->pages; p++) {
+			UINTN addr = (UINTN)dma->cpu_addr + (p * PAGE_SIZE);
+#if 0
+			printf("%s: check addr: %lu = %lu + %lu * %llu\n",
+			    __func__, addr, (UINTN)dma->cpu_addr, p, PAGE_SIZE);
+#endif
+			if ((UINTN)virt == addr) {
+				offset = addr - (UINTN)dma->cpu_addr;
+				goto found;
+			}
+		}
+		dma = NULL;
+	}
+
+	if (!dma) {
+		printf("CPU addr %p not found in DMA descriptor\n", virt);
+		return 0;
+	}
+
+found:
+	printf("%s: found, i = %lu, dma->paddr = %lu, offset = %lu\n",
+	    __func__, i, dma->paddr, offset);
+	if (dma->paddr && dma->mapping)
+		return dma->paddr + offset;
+
+	needed = dma->pages << EFI_PAGE_SHIFT;
+	mapped = needed;
+	status = pci->Map(pci, EfiPciIoOperationBusMasterCommonBuffer,
+	    (void *)virt, &mapped, &paddr, &mapping);
+	if (EFI_ERROR(status) || mapped != needed) {
+		printf("pci->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) {
+		printf("Non-identity DMA mapping: dma: 0x%lx cpu: %p\n",
+		    paddr, virt);
+	}
+
+	return (paddr);
+}
+
+static void
+udb_xhci_debug_ring_init(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring,
+    int producer, int doorbell)
+{
+	memset(ring->trb, 0, UDB_TRB_RING_CAP * sizeof(ring->trb[0]));
+
+	ring->enq = 0;
+	ring->deq = 0;
+	ring->cyc = 1;
+	ring->doorbell = (uint8_t)doorbell;
+
+	/* Place a link TRB at the tail if producer == 1. */
+	if (producer) {
+		struct xhci_trb *trb = &ring->trb[UDB_TRB_RING_CAP - 1];
+
+		trb->dwTrb3 |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK);
+		trb->dwTrb3 |= XHCI_TRB_3_TC_BIT;
+		/* Fields for link TRBs (section 6.4.4.1) */
+		trb->qwTrb0 = v2p(sc, ring->trb);
+	}
+}
+
+struct usb_strdesc {
+	uint8_t bLength;
+	uint8_t bDescriptorType;
+	uint8_t chars[62];	/* UTF-16LE */
+};
+#define USB_DESC_TYPE_STRING	3
+
+/* Initialize the DbC info with USB string descriptor addresses. */
+void
+udb_init_strings(struct xhci_debug_softc *sc, uint32_t *info)
+{
+	struct usb_strdesc udb_strdesc[] = {
+		{
+		  .bLength = 2 + 4,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = { 9, 0, 4, 0 },	/* 0x0409 = English */
+		},
+		{
+		  .bLength = 2 + 36,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = {
+		    'F', 0, 'r', 0, 'e', 0, 'e', 0, 'B', 0, 'S', 0, 'D', 0,
+		    ' ', 0,
+		    'F', 0, 'o', 0, 'u', 0, 'n', 0, 'd', 0, 'a', 0, 't', 0,
+		    'i', 0, 'o', 0, 'n', 0,
+		  }
+		},
+		{
+		  .bLength = 2 + 50,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = {
+		    'F', 0, 'r', 0, 'e', 0, 'e', 0, 'B', 0, 'S', 0, 'D', 0,
+		    ' ', 0,
+		    'U', 0, 'S', 0, 'B', 0, ' ', 0, 'D', 0, 'b', 0, 'C', 0,
+		    '.', 0, 'G', 0, 'P', 0, ' ', 0, 'D', 0, 'e', 0, 'v', 0,
+		    'i', 0, 'c', 0, 'e', 0,
+		  }
+		},
+		{
+		  .bLength = 2 + 2,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = { '0', 0 }
+		}
+	};
+	uint64_t *sda;
+
+	/* udb_str is 1 page buffer longer than sizeof(udb_strdesc) */
+	memcpy(sc->udb_str, udb_strdesc, sizeof(udb_strdesc));
+
+	sda = (uint64_t *)(void *)&info[0];
+	sda[0] = v2p(sc, sc->udb_str);
+	sda[1] = sda[0] + 64;
+	sda[2] = sda[0] + 64 + 64;
+	sda[3] = sda[0] + 64 + 64 + 64;
+	info[8] = (4 << 24) | (52 << 16) | (38 << 8) | 6;
+}
+
+void
+udb_info(void)
+{
+	struct xhci_debug_softc *sc = udb_sc;
+	struct xhci_debug_reg *r;
+	if (sc == NULL)
+		return;
+	printf("USB DbC INFO (softc = %p):\n", sc);
+
+	r = sc->udb_reg;
+	if (r == NULL)
+		return;
+
+	printf("USB DbC INFO (reg = %p):\n", r);
+	printf("=============\n");
+	printf("sc->dma_allocated = %d\n", sc->dma_allocated);
+	printf("sc->open = %d\n", sc->open);
+	printf("sc->init = %d\n", sc->init);
+	printf("=============\n");
+	printf("    ctrl: 0x%x stat: 0x%x psc: 0x%x\n", r->ctrl, r->st,
+	    r->portsc);
+	printf("      DbC Enable = %s\n",
+	    (r->ctrl & XHCI_DCCTRL_DCE) ? "YES" : "NO");
+	printf("      DbC Running = %s\n",
+	    (r->ctrl & XHCI_DCCTRL_DCR) ? "YES" : "NO");
+	printf("      Link State Event Enable = %s\n",
+	    (r->ctrl & XHCI_DCCTRL_LSE) ? "YES" : "NO");
+	printf("      Halt OUT TR = %s\n",
+	    (r->ctrl & XHCI_DCCTRL_HOT) ? "YES" : "NO");
+	printf("      Halt IN TR = %s\n",
+	    (r->ctrl & XHCI_DCCTRL_HIT) ? "YES" : "NO");
+	printf("      DbC Run Change = %s\n",
+	    (r->ctrl & XHCI_DCCTRL_DRC) ? "YES" : "NO");
+	printf("      DbC Port = %u\n", (uint32_t)XHCI_DCST_PORT_GET(r->st));
+	printf("    id: 0x%x, doorbell: 0x%x\n", r->id, r->doorbell);
+	printf("    erstsz: %u, erstba: 0x%lx\n", r->erstsz, r->erstba);
+	printf("    erdp: 0x%lx, cp: 0x%lx\n", r->erdp, r->cp);
+	printf("    ddi1: 0x%x, ddi2: 0x%x\n", r->ddi1, r->ddi2);
+	printf("    erstba == v2p(sc, erst): %d\n",
+	    r->erstba == v2p(sc, sc->udb_erst));
+	printf("    erdp == erst[0].qwEvrsTablePtr: %d\n",
+	    r->erdp == sc->udb_erst[0].qwEvrsTablePtr);
+	printf("    cp == v2p(ctx): %d\n",
+	    r->cp == v2p(sc, sc->udb_ctx));
+	printf("=============\n");
+	printf("DbC Commands:\n");
+	printf(" udb_send xxx: Send 'xxx'\n");
+	printf(" udb_probe: Probe and initialize DbC\n");
+	printf(" udb_init: Enable DbC\n");
+	printf(" udb_recon: Reconnect DbC Cable\n");
+	printf("=============\n");
+}
+
+/*
+ * Commit the pending transfer TRBs to the DbC.  This notifies
+ * the DbC of any previously-queued data on the work ring and
+ * rings the doorbell.
+ */
+void
+udb_flush(struct xhci_debug_softc *sc)
+{
+	struct xhci_debug_reg *reg = sc->udb_reg;
+	struct xhci_debug_ring *oring = &sc->udb_oring;
+	struct xhci_debug_work_ring *work = &sc->udb_owork;
+	uint32_t doorbell = (reg->doorbell & 0xffff00ff) | (oring->doorbell << 8);
+
+	/* Check if DbC is running */
+	if (sc->open && (reg->ctrl & XHCI_DCCTRL_DCE) == 0) {
+		/* Try to reinitialize */
+		if (udb_init(0) != CMD_OK)
+			return;
+	}
+
+	udb_pop_events(sc);
+
+	if ((reg->ctrl & XHCI_DCCTRL_DCR) == 0) {
+		printf("%s: DbC not configured\n", __func__);
+		return;
+	}
+	if (reg->ctrl & XHCI_DCCTRL_DRC) {
+		reg->ctrl |= XHCI_DCCTRL_DRC;
+		reg->portsc |= XHCI_PS_PED;
+		SFENCE();
+	}
+	if (xhci_debug_ring_full(oring))	/* TRB queue is full */
+		return;
+	if (work->enq == work->deq)	/* Work queue is empty */
+		return;
+
+	if (work->enq > work->deq) {
+		/* Push TRBs on work queue to TRB queue */
+		push_trb(oring, work->paddr + work->deq, work->enq - work->deq);
+		work->deq = work->enq;
+	} else {
+		push_trb(oring, work->paddr + work->deq, UDB_WORK_RING_CAP - work->deq);
+		work->deq = 0;
+		if (work->enq > 0 && !xhci_debug_ring_full(oring)) {
+			push_trb(oring, work->paddr, work->enq);
+			work->deq = work->enq;
+		}
+	}
+	/* Ring the doorbell */
+	SFENCE();
+	reg->doorbell = doorbell;
+}
+
+static void
+udb_putc(int c0)
+{
+	struct xhci_debug_softc *sc = udb_sc;
+	u_char c = (0xff & c0);
+
+	if (sc == NULL)
+		return;
+	if (!push_work(&sc->udb_owork, &c, 1))
+		return;
+	if (c == '\n')
+		udb_flush(sc);
+}
+
+static int
+udb_getc(void)
+{
+
+	return (-1);
+}
+
+static int
+udb_ischar(void)
+{
+
+	return (0);	/* XXX: never arrive a new char */
+}
+
+static struct xhci_debug_softc *
+udb_open(void)
+{
+	return (NULL);
+}
+
+static void
+udb_free(struct xhci_debug_softc *sc)
+{
+	struct xhci_debug_softc *sc0;
+
+	sc0 = sc;
+}
+
+static void
+udb_close(struct xhci_debug_softc *sc)
+{
+	udb_disable(sc);
+	udb_free(sc);
+	sc->open = 0;
+}
+
+/*
+ * 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: debug mbs (from db_reg->ctrl register)
+ * 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)
+ */
+static void
+udb_init_ep(uint32_t *ep, size_t len, uint64_t mbs, uint32_t type,
+    uint64_t ring_paddr)
+{
+	memset(ep, 0, len);
+
+	ep[1] = (1024 << 16) | ((uint32_t)mbs << 8) | (type << 3);
+	ep[2] = (ring_paddr & 0xFFFFFFFF) | 1;
+	ep[3] = ring_paddr >> 32;
+	ep[4] = 3 * 1024;
+}
+
+/* XXX: OUT transfer only */
+static void
+udb_pop_events(struct xhci_debug_softc *sc)
+{
+	const int trb_shift = 4;
+	struct xhci_debug_reg *reg = sc->udb_reg;
+	struct xhci_debug_ring *er = &sc->udb_ering;
+	struct xhci_debug_ring *tr = &sc->udb_oring;
+	struct xhci_trb *event = &er->trb[er->deq];
+	uint64_t erdp = reg->erdp;
+
+#define LFENCE(x)	__asm volatile("lfence" ::: "memory")
+	LFENCE();
+#undef	LFENCE
+	while ((event->dwTrb3 & XHCI_TRB_3_CYCLE_BIT) == er->cyc) {
+		switch (XHCI_TRB_3_TYPE_GET(event->dwTrb3)) {
+		case XHCI_TRB_EVENT_TRANSFER:
+			if (XHCI_TRB_2_ERROR_GET(event->dwTrb2) != XHCI_TRB_ERROR_SUCCESS) {
+				printf("transfer error: %u\n",
+				    XHCI_TRB_2_ERROR_GET(event->dwTrb2));
+            			break;
+        		}
+			tr->deq =
+			    (event->qwTrb0 & UDB_TRB_RING_MASK) >> trb_shift;
+			break;
+		case XHCI_TRB_EVENT_PORT_STS_CHANGE:
+			reg->portsc |= (XHCI_DCPORTSC_ACK_MASK & reg->portsc);
+			break;
+		default:
+			break;
+		}
+		er->cyc = (er->deq == UDB_TRB_RING_CAP - 1) ? er->cyc ^ 1 : er->cyc;
+		er->deq = (er->deq + 1) & (UDB_TRB_RING_CAP - 1);
+		event = &er->trb[er->deq];
+	}
+
+	erdp &= ~UDB_TRB_RING_MASK;
+	erdp |= (er->deq << trb_shift);
+	SFENCE();
+	reg->erdp = erdp;
+}
+
+static void
+push_trb(struct xhci_debug_ring *ring, uint64_t addr, uint64_t len)
+{
+	struct xhci_trb trb;
+
+	if (ring->enq == UDB_TRB_RING_CAP - 1) {
+		/*
+		 * Make sure the xHC processes the link TRB in order
+		 * for wrap-around to work properly by setting the TRB's
+		 * cycle bit, just like with normal TRBs.
+		 */
+		struct xhci_trb *link = &ring->trb[ring->enq];
+
+		link->dwTrb3 &= ~XHCI_TRB_3_CYCLE_BIT;
+		link->dwTrb3 |= ring->cyc;
+		ring->enq = 0;
+		ring->cyc ^= 1;
+	}
+	trb.qwTrb0 = 0;
+	trb.dwTrb2 = 0;
+	trb.dwTrb3 = 0;
+
+	trb.dwTrb3 |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL);
+	trb.dwTrb3 &= ~XHCI_TRB_3_CYCLE_BIT;
+	trb.dwTrb3 |= ring->cyc;
+
+	trb.qwTrb0 = addr;
+	trb.dwTrb2 = XHCI_TRB_2_BYTES_SET(trb.dwTrb2 | (uint32_t)len);
+	trb.dwTrb3 |= XHCI_TRB_3_IOC_BIT;
+
+	ring->trb[ring->enq++] = trb;
+	flush_range(&ring->trb[ring->enq - 1], sizeof(trb));
+}
+
+static int
+xhci_debug_work_ring_full(const struct xhci_debug_work_ring *ring)
+{
+
+	return ((ring->enq + 1) & (UDB_WORK_RING_CAP - 1)) == ring->deq;
+}
+
+static uint64_t
+xhci_debug_work_ring_size(const struct xhci_debug_work_ring *ring)
+{
+
+	return (ring->enq >= ring->deq)
+	    ? (ring->enq - ring->deq)
+	    : (UDB_WORK_RING_CAP - ring->deq + ring->enq);
+}
+
+static int64_t
+push_work(struct xhci_debug_work_ring *ring, const char *buf, int64_t len)
+{
+	int64_t i = 0;
+	uint32_t start = ring->enq;
+	uint32_t end = 0;
+
+	while (!xhci_debug_work_ring_full(ring) && i < len) {
+		ring->buf[ring->enq] = buf[i++];
+		ring->enq = (ring->enq + 1) & (UDB_WORK_RING_CAP - 1);
+	}
+
+	end = ring->enq;
+
+	if (end > start)
+		flush_range(&ring->buf[start], end - start);
+	else if (i > 0) {
+		flush_range(&ring->buf[start], UDB_WORK_RING_CAP - start);
+		flush_range(&ring->buf[0], end);
+	}
+	return (i);
+}
+
+static int
+xhci_debug_ring_full(const struct xhci_debug_ring *ring)
+{
+
+	return ((ring->enq + 1) & (UDB_TRB_RING_CAP - 1)) == ring->deq;
+}
+
+static void
+flush_range(void *ptr, uint32_t bytes)
+{
+	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;
+
+#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
+}
diff --git a/stand/efi/loader/usb_dbc.h b/stand/efi/loader/usb_dbc.h
new file mode 100644
index 000000000000..4f1f9844f08a
--- /dev/null
+++ b/stand/efi/loader/usb_dbc.h
@@ -0,0 +1,117 @@
+#ifndef	_USB_DBC_H_
+#define	_USB_DBC_H_
+
+#define	PCIR_DEVVENDOR	0
+
+/* Supported xHC PCI configurations */
+#define XHC_CLASSC		0x0c0330ULL
+#define XHC_VEN_INTEL		0x8086ULL
+
+#define	XHC_DEV_COMET_LAKE_LP	0x02edULL
+#define	XHC_DEV_COMET_LAKE	0x06edULL
+#define	XHC_DEV_N2000		0x0f35ULL
+#define	XHC_DEV_C3000		0x19d0ULL
+#define	XHC_DEV_C210		0x1e31ULL
+#define	XHC_DEV_E8000		0x22b5ULL
+#define	XHC_DEV_PUMA		0x2bb5ULL
+#define	XHC_DEV_CELERON		0x31a8ULL
+#define	XHC_DEV_ICE_LAKE	0x34edULL
+#define	XHC_DEV_TIGER_LAKE_H	0x43edULL
+#define	XHC_DEV_N3350		0x5aa8ULL
+#define	XHC_DEV_C220		0x8c31ULL
+#define	XHC_DEV_NINE		0x8cb1ULL
+#define	XHC_DEV_C610		0x8d31ULL
+#define	XHC_DEV_EIGHT		0x9c31ULL
+#define	XHC_DEV_WILDCAT_POINT	0x9cb1ULL
+#define	XHC_DEV_SUNRISE_POINT	0x9d2fULL
+#define	XHC_DEV_CANNON_POINT	0x9dedULL
+#define	XHC_DEV_SILVER		0x31a8ULL
+#define	XHC_DEV_TIGER_LAKE	0xa0edULL
+#define	XHC_DEV_C230		0xa12fULL
+#define	XHC_DEV_C620		0xa1afULL
+#define	XHC_DEV_Z370		0xa2afULL
+#define	XHC_DEV_CANNON_LAKE	0xa36dULL
+
+#define XHC_VEN_AMD		0x1022ULL
+
+#define	XHC_DEV_KERNCZ		0x145cULL
+#define	XHC_DEV_STARSHIP	0x148cULL
+#define	XHC_DEV_MATISSE		0x149cULL
+#define	XHC_DEV_X399		0x43baULL
+#define	XHC_DEV_X370		0x43b9ULL
+#define	XHC_DEV_B350		0x43bbULL
+#define	XHC_DEV_400		0x43d5ULL
+#define	XHC_DEV_FCH_1		0x7812ULL
+#define	XHC_DEV_FCH_2		0x7814ULL
+#define	XHC_DEV_FCH_3		0x7914ULL
+
+#define XHC_VEN_VMWARE		0x15adULL
+#define	XHC_DEV_VMWARE_1	0x0778ULL
+#define	XHC_DEV_VMWARE_2	0x0779ULL
+
+#define XHC_VEN_ASMEDIA		0x1b21ULL
+#define XHC_DEV_ASM1042		0x1042ULL
+#define XHC_DEV_ASM1042A	0x1142ULL
+#define XHC_DEV_ASM1143		0x1343ULL
+#define XHC_DEV_ASM3242		0x3242ULL
+
+/* 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	/* 1.0 */
+
+#define	DC_STRING_MANUFACTURER	"The FreeBSD Foundation"
+#define	DC_STRING_PRODUCT	"FreeBSD USB DbC Serial"
+#define	DC_STRING_SERIAL	"12345678"
+
+#define XHCI_DCPORTSC_ACK_MASK	\
+	(XHCI_DCPORTSC_CSC | XHCI_DCPORTSC_PRC | \
+	 XHCI_DCPORTSC_PLC | XHCI_DCPORTSC_CEC)
+
+/* DbC endpoint types */
+enum {
+	EP_BULK_OUT = 2,
+	EP_BULK_IN = 6
+};
+
+#define UDB_TRB_MAX_TRANSFER (XHCI_PAGE_SIZE << 4)
+#define UDB_TRB_PER_PAGE (XHCI_PAGE_SIZE / sizeof(struct xhci_trb))
+
+/* Defines the size in bytes of TRB rings as 2^UDB_TRB_RING_ORDER * 4096 */
+#define UDB_TRB_RING_ORDER 4
+#define UDB_TRB_RING_CAP (UDB_TRB_PER_PAGE * (1ULL << UDB_TRB_RING_ORDER))
+#define UDB_TRB_RING_BYTES (UDB_TRB_RING_CAP * sizeof(struct xhci_trb))
+#define UDB_TRB_RING_MASK (UDB_TRB_RING_BYTES - 1U)
+
+/* Defines the size in bytes of work rings as 2^WORK_RING_ORDER * 4096 */
+#define	UDB_WORK_RING_ORDER 3
+#define UDB_WORK_RING_CAP (XHCI_PAGE_SIZE * (1ULL << UDB_WORK_RING_ORDER))
+#define	UDB_WORK_RING_BYTES UDB_WORK_RING_CAP
+
+#if UDB_WORK_RING_CAP > UDB_TRB_MAX_TRANSFER
+#error "UDB_WORK_RING_ORDER must be at most 4"
+#endif
+
+struct xhci_debug_ring {
+	struct xhci_trb *trb;	/* Array 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 */
+#define XHCI_DB_OUT	0x0
+#define XHCI_DB_IN	0x1
+#define XHCI_DB_INVAL	0xFF
+	uint8_t doorbell;	/* Doorbell target */
+};
+
+struct xhci_debug_work_ring {
+	uint8_t *buf;
+	uint32_t enq;
+	uint32_t deq;
+	uint64_t paddr;
+};
+
+extern struct console udb_console;
+void udb_info(void);
+
+#endif
diff --git a/stand/efi/loader/usb_xhci.h b/stand/efi/loader/usb_xhci.h
new file mode 100644
index 000000000000..cd1feecd6874
--- /dev/null
+++ b/stand/efi/loader/usb_xhci.h
@@ -0,0 +1,209 @@
+
+/*-
+ * 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.
+ */
+
+#ifndef _XHCI_DBC_H_
+#define	_XHCI_DBC_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_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;
+};
+
+enum {
+	XHCI_ST_DISABLED,
+	XHCI_ST_ENABLED,
+	XHCI_ST_DEFAULT,
+	XHCI_ST_ADDRESSED,
+	XHCI_ST_CONFIGURED,
+	XHCI_ST_MAX
+};
+
+struct xhci_debug_reg {
+	uint32_t id;
+	uint32_t doorbell;
+	uint32_t erstsz;
+	uint32_t rsvdz;
+	uint64_t erstba;
+	uint64_t erdp;
+	uint32_t ctrl;
+	uint32_t st;
+	uint32_t portsc;
+	uint32_t rsvdp;
+	uint64_t cp;
+	uint32_t ddi1;
+	uint32_t ddi2;
+};
+
+struct xhci_debug_ctx {
+	uint32_t info[64];
+	uint32_t ep_out[64];
+	uint32_t ep_in[64];
+};
+
+#endif					/* _XHCI_DBC_H_ */
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index ab1c3856051b..38212dec294f 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -380,3 +380,6 @@ device		uinput			# install /dev/uinput cdev
 options 	HID_DEBUG		# enable debug msgs
 device		hid			# Generic HID support
 options 	IICHID_SAMPLING		# Workaround missing GPIO INTR support
+
+options		GDB
+options		DDB
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index c6434d1ea85d..1acd044c7025 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2446,6 +2446,8 @@ device		u3g
 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
index 7d3c44893e09..9572ac548fb1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3313,6 +3313,7 @@ dev/usb/serial/ubsa.c		optional ubsa
 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
@@ -3327,10 +3328,10 @@ dev/usb/serial/uslcom.c		optional uslcom
 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.c b/sys/dev/usb/controller/xhci.c
index 17800dc11d0c..60aadb4afd7d 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -63,6 +63,11 @@
 #include <sys/malloc.h>
 #include <sys/priv.h>
 
+#include <sys/cons.h>
+#include <sys/kdb.h>
+#include <sys/tty.h>
+#include <sys/reboot.h>
+
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 
@@ -154,6 +159,51 @@ struct xhci_std_temp {
 	uint8_t			do_isoc_sync;
 };
 
+/* for USB DbC */
+#define	UDBCONS_NAME	"udbcons"
+
+#define	UDB_TRB_MAX_TRANSFER (USB_PAGE_SIZE << 4)
+#define	UDB_TRB_PER_PAGE (USB_PAGE_SIZE / sizeof(struct xhci_trb))
+#define	UDB_TRB_RING_ORDER 4
+#define	UDB_TRB_RING_CAP (UDB_TRB_PER_PAGE * (1ULL << UDB_TRB_RING_ORDER))
+#define	UDB_TRB_RING_BYTES (UDB_TRB_RING_CAP * sizeof(struct xhci_trb))
+#define	UDB_TRB_RING_MASK (UDB_TRB_RING_BYTES - 1U)
+#define	UDB_WORK_RING_ORDER 3
+#define	UDB_WORK_RING_CAP (USB_PAGE_SIZE * (1ULL << UDB_WORK_RING_ORDER))
+#define	UDB_WORK_RING_BYTES UDB_WORK_RING_CAP
+
+#if UDB_WORK_RING_CAP > UDB_TRB_MAX_TRANSFER
+#error	"UDB_WORK_RING_ORDER must be at most 4"
+#endif
+
+static int xhci_debug_init(struct xhci_softc *);
+
+/* DbC console ops */
+
+static int udbcons_init(struct xhci_softc *, device_t);
+
+struct udbcons_priv;
+typedef void udbcons_putc_t(struct udbcons_priv *, int);
+typedef int udbcons_getc_t(struct udbcons_priv *);
+struct udbcons_ops {
+	udbcons_putc_t	*putc;
+	udbcons_getc_t	*getc;
+};
+struct udbcons_priv {
+	device_t			dev;	/* XHCI */
+	struct xhci_softc		*xhci;
+	const struct udbcons_ops	*ops;
+	struct callout			callout;
+	struct tty			*tp;
+	bool				opened;
+	int				polltime;
+	int				alt_break_state;
+};
+static struct udbcons_priv udbcons0;
+static void xhci_debug_reg_read(struct xhci_softc *);
+static void xhci_debug_reg_restore(struct xhci_softc *);
+static void xhci_debug_enable(struct xhci_softc *);
+
 static void	xhci_do_poll(struct usb_bus *);
 static void	xhci_device_done(struct usb_xfer *, usb_error_t);
 static void	xhci_root_intr(struct xhci_softc *);
@@ -230,6 +280,9 @@ xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
 	cb(bus, &sc->sc_hw.ctx_pc, &sc->sc_hw.ctx_pg,
 	   sizeof(struct xhci_dev_ctx_addr), XHCI_PAGE_SIZE);
 
+	cb(bus, &sc->sc_hw.dbc_pc, &sc->sc_hw.dbc_pg,
+	   sizeof(struct xhci_debug_softc), XHCI_PAGE_SIZE);
+
 	for (i = 0; i != sc->sc_noscratch; i++) {
 		cb(bus, &sc->sc_hw.scratch_pc[i], &sc->sc_hw.scratch_pg[i],
 		    XHCI_PAGE_SIZE, XHCI_PAGE_SIZE);
@@ -311,6 +364,7 @@ xhci_start_controller(struct xhci_softc *sc)
 	uint16_t i;
 
 	DPRINTF("\n");
+	printf("%s called\n", __func__);
 
 	sc->sc_event_ccs = 1;
 	sc->sc_event_idx = 0;
@@ -321,6 +375,9 @@ xhci_start_controller(struct xhci_softc *sc)
 	if (err)
 		return (err);
 
+	xhci_debug_reg_restore(sc);
+	xhci_debug_enable(sc);
+
 	/* set up number of device slots */
 	DPRINTF("CONFIG=0x%08x -> 0x%08x\n",
 	    XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot);
@@ -569,6 +626,68 @@ xhci_init(struct xhci_softc *sc, device_t self, uint8_t dma32)
 	/* enable 64Kbyte control endpoint quirk */
 	sc->sc_bus.control_ep_quirk = (xhcictlquirk ? 1 : 0);
 
+#if 0
+	if (sc->sc_udbc == NULL) {
+		struct usb_page_search buf_res;
+ 
+		usbd_get_page(&sc->sc_hw.dbc_pc, 0, &buf_res);
+		sc->sc_udbc = (struct xhci_debug_softc *)buf_res.buffer;
+		memset(sc->sc_udbc, 0, sizeof(*sc->sc_udbc));
+		sc->sc_udbc->base = buf_res.physaddr;
+	}
+#endif
+	/* Check if DbC is available. */
+	{
+		uint32_t eec = -1;	/* XXX */
+		uint32_t eecp;
+
+		for (eecp = XHCI_HCS0_XECP(temp) << 2;
+		     eecp != 0 && XHCI_XECP_NEXT(eec);
+		     eecp += XHCI_XECP_NEXT(eec) << 2) {
+			eec = XREAD4(sc, capa, eecp);
+
+			if (XHCI_XECP_ID(eec) != XHCI_ID_USB_DEBUG) {
+				device_printf(self,
+				    "looking for DBC(0x0a): %02x\n",
+				    XHCI_XECP_ID(eec));
+				continue;
+			}
+		}
+		if (XHCI_XECP_ID(eec) == XHCI_ID_USB_DEBUG) {
+			struct xhci_debug_reg *r;
+
+			udbcons_init(sc, self);
+#if 1
+			sc->sc_udbc.xhci_xecp_offset = eecp;
+#endif
+			xhci_debug_reg_read(sc);
+
+			r = &sc->sc_udbc.reg;
+
+			device_printf(self, "DbC found at 0x%04x:\n"
+			    "  ERSTSZ=%04x\n"
+			    "  ERSTBA=0x%08llx\n"
+			    "  ERDP=0x%08llx\n"
+			    "  CP=0x%08llx\n"
+			    "  DDI1=0x%04x\n"
+			    "  DDI2=0x%04x\n",
+				eecp,
+				r->erstsz,
+				(unsigned long long)le64toh(r->erstba),
+				(unsigned long long)le64toh(r->erdp),
+				(unsigned long long)le64toh(r->cp),
+				r->ddi1,
+				r->ddi2);
+
+			eec = XREAD4(sc, capa, eecp + XHCI_DCCTRL);
+			device_printf(self, "DbC is %s\n",
+			    (eec & XHCI_DCCTRL_DCE)
+				? "running"
+				: "not running");
+		} else
+			device_printf(self, "DbC not available\n");
+	}
+
 	temp = XREAD4(sc, capa, XHCI_HCSPARAMS1);
 
 	/* get number of device slots */
@@ -4406,3 +4525,762 @@ static const struct usb_bus_methods xhci_bus_methods = {
 	.set_hw_power_sleep = xhci_set_hw_power_sleep,
 	.set_endpoint_mode = xhci_set_endpoint_mode,
 };
+
+/* XHCI DbC console */
+
+static void
+xhci_debug_reg_read(struct xhci_softc *sc)
+{
+	struct xhci_debug_reg *r;
+	uint32_t offset;
+	
+#if 0
+	if (sc->sc_udbc == NULL)
+		return;
+#endif
+	offset = sc->sc_udbc.xhci_xecp_offset;
+	if (offset == 0)
+		return;
+
+	r = &sc->sc_udbc.reg;
+
+	r->erstsz = XREAD4(sc, capa, offset + XHCI_DCERSTSZ);
+
+	/* LE */
+	r->erstba = XREAD4(sc, capa, offset + XHCI_DCERSTBA);
+	r->erstba |= (uint64_t)XREAD4(sc, capa, offset + XHCI_DCERSTBA + 4) << 32;
+	/* LE */
+	r->erdp = XREAD4(sc, capa, offset + XHCI_DCERDP);
+	r->erdp |= (uint64_t)XREAD4(sc, capa, offset + XHCI_DCERDP + 4) << 32;
+	/* LE */
+	r->cp = XREAD4(sc, capa, offset + XHCI_DCCP);
+	r->cp |= (uint64_t)XREAD4(sc, capa, offset + XHCI_DCCP + 4) << 32;
+
+	r->ddi1 = XREAD4(sc, capa, offset + XHCI_DCDDI1);
+	r->ddi2 = XREAD4(sc, capa, offset + XHCI_DCDDI2);
+}
+static void
+xhci_debug_reg_restore(struct xhci_softc *sc)
+{
+	struct xhci_debug_reg *r;
+	uint32_t offset;
+	
+	printf("%s: enter\n", __func__);
+#if 0
+	if (sc->sc_udbc == NULL)
+		return;
+#endif
+	offset = sc->sc_udbc.xhci_xecp_offset;
+	if (offset == 0)
+		return;
+
+	r = &sc->sc_udbc.reg;
+
+	XWRITE4(sc, capa, offset + XHCI_DCERSTSZ, r->erstsz);
+
+	/* LE */
+	XWRITE4(sc, capa, offset + XHCI_DCERSTBA, r->erstba & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCERSTBA + 4, (r->erstba >> 32) & 0xFFFFFFFF);
+	/* LE */
+	XWRITE4(sc, capa, offset + XHCI_DCERDP, r->erdp & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCERDP + 4, (r->erdp >> 32) & 0xFFFFFFFF);
+	/* LE */
+	XWRITE4(sc, capa, offset + XHCI_DCCP, r->cp & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCCP + 4, (r->cp >> 32) & 0xFFFFFFFF);
+
+	XWRITE4(sc, capa, offset + XHCI_DCDDI1, r->ddi1);
+	XWRITE4(sc, capa, offset + XHCI_DCDDI2, r->ddi2);
+	printf("%s: leave\n", __func__);
+}
+
+struct udb_ctx {
+	uint32_t info[16];
+	uint32_t ep_out[16];
+	uint32_t ep_in[16];
+};
+static uint64_t udb_xhci_debug_ring_init(uint64_t, struct xhci_debug_ring *,
+    int, int);
+static void udb_init_ep(uint32_t *, size_t, uint64_t, uint32_t, uint64_t);
+static void udb_init_strings(struct xhci_debug_softc *, uint64_t, uint32_t *);
+
+/* 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	/* 1.0 */
+
+static int
+xhci_debug_init(struct xhci_softc *sc)
+{
+	struct xhci_debug_softc *dsc;
+	struct xhci_debug_reg *r;
+	uint64_t erdp, out, in;
+	uint64_t pbase;
+	uint32_t temp;
+	uint64_t offset;
+
+	if (sc == NULL)
+		return (-1);
+	dsc = &sc->sc_udbc;
+	if (dsc == NULL)
+		return (-1);
+	offset = dsc->xhci_xecp_offset;
+	if (offset == 0)
+		return (-1);
+	if (dsc->init != 0)
+		return (0);
+	pbase = dsc->base;
+#define	ADDR(x)	(pbase + __offsetof(struct xhci_debug_softc, x))
+
+	/* init ring */
+
+	/* Create an event ring. */
+	erdp = udb_xhci_debug_ring_init(ADDR(udb_ering), &dsc->udb_ering,
+	    0, XHCI_DB_INVAL);
+	if (erdp == 0)
+		return (-1);
+	memset(dsc->udb_erst, 0, sizeof(*dsc->udb_erst));
+	/* htole64? */
+	dsc->udb_erst->qwEvrsTablePtr = htole64(erdp);
+	dsc->udb_erst->dwEvrsTableSize = htole32(UDB_TRB_RING_CAP);
+	printf("%s: ERDP=0x%016llx\n", __func__, (unsigned long long)erdp);
+
+	/* Create an output ring. */
+	out = udb_xhci_debug_ring_init(ADDR(udb_oring), &dsc->udb_oring,
+	    1, XHCI_DB_OUT);
+	if (out == 0)
+		return (-1);
+
+	/* Create an input ring. */
+	in = udb_xhci_debug_ring_init(ADDR(udb_iring), &dsc->udb_iring,
+	    1, XHCI_DB_IN);
+	if (in == 0)
+		return (-1);
+
+	/* Initialize DbC context. */
+	memset(&dsc->ctx, 0, sizeof(dsc->ctx));
+	udb_init_strings(dsc, pbase, dsc->ctx.info);
+
+	temp = XREAD4(sc, capa, offset + XHCI_DCCTRL);
+
+#define	EP_BULK_OUT	2
+#define	EP_BULK_IN	6
+	/* Initialize endppints. */ 
+	udb_init_ep(dsc->ctx.ep_out, sizeof(*dsc->ctx.ep_out),
+	    (uint64_t)XHCI_DCCTRL_MBS_GET(temp), EP_BULK_OUT, out);
+	udb_init_ep(dsc->ctx.ep_in, sizeof(*dsc->ctx.ep_in),
+	    (uint64_t)XHCI_DCCTRL_MBS_GET(temp), EP_BULK_IN, in);
+
+	/* Configure register values. */
+	XWRITE4(sc, capa, offset + XHCI_DCERSTSZ, 1);
+	XWRITE4(sc, capa, offset + XHCI_DCERSTBA, ADDR(udb_erst) & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCERSTBA + 4, (ADDR(udb_erst) >> 32) & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCERDP, erdp & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCERDP + 4, (erdp >> 32) & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCCP, ADDR(ctx) & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCCP + 4, (ADDR(ctx) >> 32) & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCDDI1,
+	    (DC_VENDOR   << 16) | DC_PROTOCOL);
+	XWRITE4(sc, capa, offset + XHCI_DCDDI2,
+	    (DC_REVISION << 16) | DC_PRODUCT);
+
+	usb_pc_cpu_flush(&sc->sc_hw.dbc_pc);
+
+	dsc->udb_owork.enq = 0;
+	dsc->udb_owork.deq = 0;
+	dsc->udb_owork.paddr = ADDR(udb_owork);
+	dsc->udb_owork.paddr += __offsetof(struct xhci_debug_work_ring, buf);
+
+	dsc->init = 1;
+	xhci_debug_reg_read(sc);
+
+	r = &sc->sc_udbc.reg;
+
+	printf("XHCI DbC Initialized:\n"
+	    "  ERSTSZ=%04x\n"
+	    "  ERSTBA=0x%08llx\n"
+	    "  ERDP=0x%08llx\n"
+	    "  CP=0x%08llx\n"
+	    "  DDI1=0x%04x\n"
+	    "  DDI2=0x%04x\n",
+		r->erstsz,
+		(unsigned long long)le64toh(r->erstba),
+		(unsigned long long)le64toh(r->erdp),
+		(unsigned long long)le64toh(r->cp),
+		r->ddi1,
+		r->ddi2);
+
+	return (0);
+}
+
+static uint64_t
+udb_xhci_debug_ring_init(uint64_t pbase,
+    struct xhci_debug_ring *ring, int producer, int doorbell)
+{
+	uint64_t addr = 0;
+
+	memset(ring->trb, 0, UDB_TRB_RING_CAP * sizeof(ring->trb[0]));
+
+	ring->enq = 0;
+	ring->deq = 0;
+	ring->cyc = 1;
+	ring->doorbell = (uint8_t)doorbell;
+
+	/* Place a link TRB at the tail if producer == 1. */
+	if (producer) {
+		struct xhci_trb *trb = &ring->trb[UDB_TRB_RING_CAP - 1];
+
+		trb->dwTrb3 |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK);
+		trb->dwTrb3 |= XHCI_TRB_3_TC_BIT;
+		/* Fields for link TRBs (section 6.4.4.1) */
+		trb->qwTrb0 = pbase;
+		trb->qwTrb0 += __offsetof(struct xhci_debug_ring, trb);
+		addr = trb->qwTrb0;
+	}
+	return (addr);
+}
+
+/*
+ * 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: debug mbs (from db_reg->ctrl register)
+ * 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)
+ */
+static void
+udb_init_ep(uint32_t *ep, size_t len, uint64_t mbs, uint32_t type,
+    uint64_t ring_paddr)
+{
+	memset(ep, 0, len);
+
+	ep[1] = (1024 << 16) | ((uint32_t)mbs << 8) | (type << 3);
+	ep[2] = (ring_paddr & 0xFFFFFFFF) | 1;
+	ep[3] = ring_paddr >> 32;
+	ep[4] = 3 * 1024;
+}
+
+struct usb_strdesc {
+	uint8_t bLength;
+	uint8_t bDescriptorType;
+	uint8_t chars[62];	/* UTF-16LE */
+};
+#define USB_DESC_TYPE_STRING	3
+
+/* Initialize the DbC info with USB string descriptor addresses. */
+void
+udb_init_strings(struct xhci_debug_softc *dsc, uint64_t pbase, uint32_t *info)
+{
+	struct usb_strdesc udb_strdesc[] = {
+		{
+		  .bLength = 2 + 4,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = { 9, 0, 4, 0 },	/* 0x0409 = English */
+		},
+		{
+		  .bLength = 2 + 36,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = {
+		    'F', 0, 'r', 0, 'e', 0, 'e', 0, 'B', 0, 'S', 0, 'D', 0,
+		    ' ', 0,
+		    'F', 0, 'o', 0, 'u', 0, 'n', 0, 'd', 0, 'a', 0, 't', 0,
+		    'i', 0, 'o', 0, 'n', 0,
+		  }
+		},
+		{
+		  .bLength = 2 + 50,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = {
+		    'F', 0, 'r', 0, 'e', 0, 'e', 0, 'B', 0, 'S', 0, 'D', 0,
+		    ' ', 0,
+		    'U', 0, 'S', 0, 'B', 0, ' ', 0, 'D', 0, 'b', 0, 'C', 0,
+		    '.', 0, 'G', 0, 'P', 0, ' ', 0, 'D', 0, 'e', 0, 'v', 0,
+		    'i', 0, 'c', 0, 'e', 0,
+		  }
+		},
+		{
+		  .bLength = 2 + 2,
+		  .bDescriptorType = USB_DESC_TYPE_STRING,
+		  .chars = { '0', 0 }
+		}
+	};
+	uint64_t *sda;
+	memcpy(dsc->udb_str, udb_strdesc, sizeof(udb_strdesc));
+
+	sda = (uint64_t *)(void *)&info[0];
+	sda[0] = ADDR(udb_str);
+	sda[1] = sda[0] + 64;
+	sda[2] = sda[0] + 64 + 64;
+	sda[3] = sda[0] + 64 + 64 + 64;
+	info[8] = (4 << 24) | (52 << 16) | (38 << 8) | 6;
+}
+
+static void
+xhci_debug_enable(struct xhci_softc *sc)
+{
+	uint32_t offset;
+	uint32_t temp;
+#if 0
+	int error;
+#endif
+	
+	printf("%s: enter\n", __func__);
+#if 0
+	if (sc->sc_udbc == NULL)
+		return;
+#endif
+	offset = sc->sc_udbc.xhci_xecp_offset;
+	if (offset == 0)
+		return;
+
+#if 0
+	error = xhci_debug_init(sc);
+	if (error)
+		return;
+	usb_pc_cpu_flush(&sc->sc_hw.dbc_pc);
+#endif
+
+	temp = XREAD4(sc, capa, offset + XHCI_DCCTRL);
+	XWRITE4(sc, capa, offset + XHCI_DCCTRL, temp | (1 << 31));
+	temp = XREAD4(sc, capa, offset + XHCI_DCPORTSC);
+	XWRITE4(sc, capa, offset + XHCI_DCPORTSC, temp | (1 << 2));
+	printf("%s: leave\n", __func__);
+}
+
+/* udbcons */
+
+static int64_t udb_push_work(struct xhci_debug_work_ring *, const char *, int64_t);
+static void udb_pop_events(struct xhci_softc *);
+static void udb_flush(struct xhci_softc *);
+
+static int
+udbcons_getc(struct udbcons_priv *cons) {
+	return (-1);
+}
+static void
+udbcons_putc(struct udbcons_priv *cons, int ch) {
+	struct xhci_softc *sc;
+	uint64_t i;
+
+	return;
+
+	sc = cons->xhci;
+#if 0
+	if (sc->sc_udbc == NULL)
+		return;
+#endif
+
+	u_char c = (0xff & ch);
+
+	i = udb_push_work(&sc->sc_udbc.udb_owork, &c, 1);
+	usb_pc_cpu_flush(&sc->sc_hw.dbc_pc);
+	if (i == 0)
+		return;
+	if (c == '\n')
+		udb_flush(sc);
+}
+static struct udbcons_ops udbcons0_ops = {
+	.putc = udbcons_putc,
+	.getc = udbcons_getc,
+};
+
+static tsw_outwakeup_t xhci_debug_tty_outwakeup;
+
+static struct ttydevsw xhci_debug_ttydevsw = {
+	.tsw_flags	= TF_NOPREFIX,
+	.tsw_outwakeup	= xhci_debug_tty_outwakeup,
+};
+
+static void xhci_debug_timeout(void *);
+
+static int
+udbcons_init(struct xhci_softc *sc, device_t dev)
+{
+	struct udbcons_priv *cons;
+#if 0
+	device_printf(dev, "%s: enter\n", __func__);
+#endif
+
+	cons = &udbcons0;
+	cons->dev = dev;
+	cons->xhci = sc;
+	cons->ops = &udbcons0_ops;
+
+	device_printf(dev, "call tty_alloc\n");
+	cons->tp = tty_alloc(&xhci_debug_ttydevsw, cons);
+	cons->polltime = 1;
+
+	device_printf(dev, "call tty_makedev\n");
+	tty_makedev(cons->tp, NULL, "%s", UDBCONS_NAME);
+	tty_init_console(cons->tp, 0);
+
+	device_printf(dev, "call callout_init\n");
+	callout_init(&cons->callout, 1);
+	callout_reset(&cons->callout, cons->polltime,
+	    xhci_debug_timeout, cons->tp);
+
+#if 0
+	device_printf(dev, "%s: leave\n", __func__);
+#endif
+	return (0);
+}
+
+static cn_probe_t xhci_debug_cnprobe;
+static cn_init_t xhci_debug_cninit;
+static cn_term_t xhci_debug_cnterm;
+static cn_getc_t xhci_debug_cngetc;
+static cn_putc_t xhci_debug_cnputc;
+static cn_grab_t xhci_debug_cngrab;
+static cn_ungrab_t xhci_debug_cnungrab;
+
+const struct consdev_ops xhci_debug_cnops = {
+	.cn_probe	= xhci_debug_cnprobe,
+	.cn_init	= xhci_debug_cninit,
+	.cn_term	= xhci_debug_cnterm,
+	.cn_getc	= xhci_debug_cngetc,
+	.cn_putc	= xhci_debug_cnputc,
+	.cn_grab	= xhci_debug_cngrab,
+	.cn_ungrab	= xhci_debug_cnungrab,
+};
+
+CONSOLE_DRIVER(xhci_debug);
+
+#define MAX_BURST_LEN	1
+static void
+xhci_debug_tty_outwakeup(struct tty *tp)
+{
+	struct udbcons_priv *cons;
+	u_char buf[MAX_BURST_LEN];
+	int len;
+	int i;
+
+	cons = tty_softc(tp);
+	for (;;) {
+		len = ttydisc_getc(tp, buf, sizeof(buf));
+		if (len == 0)
+			break;
+		KASSERT(len == 1, ("tty error"));
+
+		for (i = 0; i < len; i++)
+			udbcons_putc(cons, buf[i]);
+	}
+}
+
+static void
+xhci_debug_timeout(void *v)
+{
+	struct udbcons_priv *cons;
+	struct tty *tp;
+	int ch;
+
+	tp = v;
+	cons = tty_softc(tp);
+
+	tty_lock(tp);
+	while ((ch = xhci_debug_cngetc(NULL)) != -1)
+		ttydisc_rint(tp, ch, 0);
+	ttydisc_rint_done(tp);
+	tty_unlock(tp);
+
+#if 0
+	if (cons->xhci != NULL)
+		device_printf(cons->dev, "%s called\n", __func__);
+#endif
+
+	callout_reset(&cons->callout, cons->polltime, xhci_debug_timeout, tp);
+}
+
+static void
+xhci_debug_cnprobe(struct consdev *cp)
+{
+
+	cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
+	sprintf(cp->cn_name, "%s", UDBCONS_NAME);
+}
+
+static void
+xhci_debug_cninit(struct consdev *cp)
+{
+
+}
+
+static void
+xhci_debug_cnterm(struct consdev *cp)
+{
+}
+
+/* empty */
+static void
+xhci_debug_cngrab(struct consdev *cp)
+{
+}
+
+/* empty */
+static void
+xhci_debug_cnungrab(struct consdev *cp)
+{
+}
+
+static int
+xhci_debug_cngetc(struct consdev *cp)
+{
+	int ch;
+
+	ch = udbcons_getc(&udbcons0);
+	if (ch > 0 && ch < 0xff) {
+#if defined(KDB)
+		kdb_alt_break(ch, &udbcons0.alt_break_state);
+#endif
+		return (ch);
+	}
+	return (-1);
+}
+
+static void
+xhci_debug_cnputc(struct consdev *cp, int ch)
+{
+	udbcons_putc(&udbcons0, ch);
+}
+
+/* DbC TRB handling */
+
+static void push_trb(struct xhci_debug_ring *, uint64_t, uint64_t);
+static int xhci_debug_work_ring_full(const struct xhci_debug_work_ring *);
+static uint64_t xhci_debug_work_ring_size(const struct xhci_debug_work_ring *);
+static int xhci_debug_ring_full(const struct xhci_debug_ring *);
+static void flush_range(void *, uint32_t);
+
+/*
+ * Commit the pending transfer TRBs to the DbC.  This notifies
+ * the DbC of any previously-queued data on the work ring and
+ * rings the doorbell.
+ */
+void
+udb_flush(struct xhci_softc *sc)
+{
+	struct xhci_debug_ring *oring;
+	struct xhci_debug_work_ring *work;
+	uint32_t temp;
+	uint32_t doorbell;
+	uint32_t offset;
+
+#if 0
+	if (sc->sc_udbc == NULL)
+		return;
+#endif
+	offset = sc->sc_udbc.xhci_xecp_offset;
+	if (offset == 0)
+		return;
+ 	oring = &sc->sc_udbc.udb_oring;
+ 	work = &sc->sc_udbc.udb_owork;
+
+	udb_pop_events(sc);
+
+	temp = XREAD4(sc, capa, offset + XHCI_DCCTRL);
+
+	if (XHCI_DCCTRL_DCR_GET(temp) == 0) {
+		printf("%s: DbC not configured\n", __func__);
+		return;
+	}
+	if (XHCI_DCCTRL_DRC_GET(temp)) {
+		XWRITE4(sc, capa, offset + XHCI_DCCTRL,
+		    temp | XHCI_DCCTRL_DRC);
+
+		/* Enable port */
+		temp = XREAD4(sc, capa, offset + XHCI_DCPORTSC);
+		XWRITE4(sc, capa, offset + XHCI_DCPORTSC, temp | XHCI_PS_PED);
+	}
+	if (xhci_debug_ring_full(oring))	/* TRB queue is full */
+		return;
+	if (work->enq == work->deq)	/* Work queue is empty */
+		return;
+
+	if (work->enq > work->deq) {
+		/* Push TRBs on work queue to TRB queue */
+		push_trb(oring, work->paddr + work->deq, work->enq - work->deq);
+		usb_pc_cpu_flush(&sc->sc_hw.dbc_pc);
+		work->deq = work->enq;
+	} else {
+		push_trb(oring, work->paddr + work->deq, UDB_WORK_RING_CAP - work->deq);
+		usb_pc_cpu_flush(&sc->sc_hw.dbc_pc);
+		work->deq = 0;
+		if (work->enq > 0 && !xhci_debug_ring_full(oring)) {
+			push_trb(oring, work->paddr, work->enq);
+			usb_pc_cpu_flush(&sc->sc_hw.dbc_pc);
+			work->deq = work->enq;
+		}
+	}
+
+	/* Ring the doorbell */
+	temp = XREAD4(sc, capa, offset + XHCI_DCDB);
+	doorbell = (temp & 0xffff00ff) | (oring->doorbell << 8);
+	XWRITE4(sc, capa, offset + XHCI_DCDB, doorbell);
+}
+
+/* XXX: OUT transfer only */
+#define	XHCI_DCPORTSC_ACK_MASK  \
+	(XHCI_DCPORTSC_CSC | XHCI_DCPORTSC_PRC | \
+	 XHCI_DCPORTSC_PLC | XHCI_DCPORTSC_CEC)
+
+static void
+udb_pop_events(struct xhci_softc *sc)
+{
+	const int trb_shift = 4;
+	struct xhci_debug_ring *er;
+	struct xhci_debug_ring *tr;
+	struct xhci_trb *event;
+	uint32_t temp;
+	uint64_t erdp;
+	uint32_t offset;
+
+#if 0
+	if (sc->sc_udbc == NULL)
+		return;
+#endif
+	offset = sc->sc_udbc.xhci_xecp_offset;
+	if (offset == 0)
+		return;
+
+ 	er = &sc->sc_udbc.udb_ering;
+ 	tr = &sc->sc_udbc.udb_oring;
+ 	event = &er->trb[er->deq];
+
+	erdp = XREAD4(sc, capa, offset + XHCI_DCERDP);
+	erdp |= (uint64_t)XREAD4(sc, capa, offset + XHCI_DCERDP + 4) << 32;
+
+	while ((event->dwTrb3 & XHCI_TRB_3_CYCLE_BIT) == er->cyc) {
+		switch (XHCI_TRB_3_TYPE_GET(event->dwTrb3)) {
+		case XHCI_TRB_EVENT_TRANSFER:
+			if (XHCI_TRB_2_ERROR_GET(event->dwTrb2) != XHCI_TRB_ERROR_SUCCESS) {
+				printf("transfer error: %u\n",
+				    XHCI_TRB_2_ERROR_GET(event->dwTrb2));
+            			break;
+        		}
+			tr->deq =
+			    (event->qwTrb0 & UDB_TRB_RING_MASK) >> trb_shift;
+			break;
+		case XHCI_TRB_EVENT_PORT_STS_CHANGE:
+			temp = XREAD4(sc, capa, offset + XHCI_DCPORTSC);
+			XWRITE4(sc, capa, offset + XHCI_DCPORTSC,
+			    temp & XHCI_DCPORTSC_ACK_MASK);
+			break;
+		default:
+			break;
+		}
+		er->cyc = (er->deq == UDB_TRB_RING_CAP - 1) ? er->cyc ^ 1 : er->cyc;
+		er->deq = (er->deq + 1) & (UDB_TRB_RING_CAP - 1);
+		event = &er->trb[er->deq];
+	}
+
+	erdp &= ~UDB_TRB_RING_MASK;
+	erdp |= (er->deq << trb_shift);
+	XWRITE4(sc, capa, offset + XHCI_DCERDP, erdp & 0xFFFFFFFF);
+	XWRITE4(sc, capa, offset + XHCI_DCERDP + 4, (erdp >> 32) & 0xFFFFFFFF);
+}
+
+static void
+push_trb(struct xhci_debug_ring *ring, uint64_t addr, uint64_t len)
+{
+	struct xhci_trb trb;
+
+	if (ring->enq == UDB_TRB_RING_CAP - 1) {
+		/*
+		 * Make sure the xHC processes the link TRB in order
+		 * for wrap-around to work properly by setting the TRB's
+		 * cycle bit, just like with normal TRBs.
+		 */
+		struct xhci_trb *link = &ring->trb[ring->enq];
+
+		link->dwTrb3 &= ~XHCI_TRB_3_CYCLE_BIT;
+		link->dwTrb3 |= ring->cyc;
+		ring->enq = 0;
+		ring->cyc ^= 1;
+	}
+	trb.qwTrb0 = 0;
+	trb.dwTrb2 = 0;
+	trb.dwTrb3 = 0;
+
+	trb.dwTrb3 |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL);
+	trb.dwTrb3 &= ~XHCI_TRB_3_CYCLE_BIT;
+	trb.dwTrb3 |= ring->cyc;
+
+	trb.qwTrb0 = addr;
+	trb.dwTrb2 = XHCI_TRB_2_BYTES_SET(trb.dwTrb2 | (uint32_t)len);
+	trb.dwTrb3 |= XHCI_TRB_3_IOC_BIT;
+
+	ring->trb[ring->enq++] = trb;
+
+	flush_range(&ring->trb[ring->enq - 1], sizeof(trb));
+}
+
+static int
+xhci_debug_work_ring_full(const struct xhci_debug_work_ring *ring)
+{
+
+	return ((ring->enq + 1) & (UDB_WORK_RING_CAP - 1)) == ring->deq;
+}
+
+static uint64_t
+xhci_debug_work_ring_size(const struct xhci_debug_work_ring *ring)
+{
+
+	return (ring->enq >= ring->deq)
+	    ? (ring->enq - ring->deq)
+	    : (UDB_WORK_RING_CAP - ring->deq + ring->enq);
+}
+
+static int64_t
+udb_push_work(struct xhci_debug_work_ring *ring, const char *buf, int64_t len)
+{
+	int64_t i = 0;
+	uint32_t start = ring->enq;
+	uint32_t end = 0;
+
+	while (!xhci_debug_work_ring_full(ring) && i < len) {
+		ring->buf[ring->enq] = buf[i++];
+		ring->enq = (ring->enq + 1) & (UDB_WORK_RING_CAP - 1);
+	}
+
+	end = ring->enq;
+
+	if (end > start)
+		flush_range(&ring->buf[start], end - start);
+	else if (i > 0) {
+		flush_range(&ring->buf[start], UDB_WORK_RING_CAP - start);
+		flush_range(&ring->buf[0], end);
+	}
+	return (i);
+}
+
+static int
+xhci_debug_ring_full(const struct xhci_debug_ring *ring)
+{
+
+	return ((ring->enq + 1) & (UDB_TRB_RING_CAP - 1)) == ring->deq;
+}
+
+static void
+flush_range(void *ptr, uint32_t bytes)
+{
+	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;
+
+	/* XXX */
+	return;
+
+#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
+}
diff --git a/sys/dev/usb/controller/xhci.h b/sys/dev/usb/controller/xhci.h
index 3758815238ad..a2aede959edb 100644
--- a/sys/dev/usb/controller/xhci.h
+++ b/sys/dev/usb/controller/xhci.h
@@ -455,10 +455,12 @@ struct xhci_hw_softc {
 	struct usb_page_cache	root_pc;
 	struct usb_page_cache	ctx_pc;
 	struct usb_page_cache	scratch_pc[XHCI_MAX_SCRATCHPADS];
+	struct usb_page_cache	dbc_pc;
 
 	struct usb_page		root_pg;
 	struct usb_page		ctx_pg;
 	struct usb_page		scratch_pg[XHCI_MAX_SCRATCHPADS];
+	struct usb_page		dbc_pg;
 
 	struct xhci_hw_dev	devs[XHCI_MAX_DEVICES + 1];
 };
@@ -491,6 +493,65 @@ enum xhci_quirks {
 	XHCI_QUIRK_DMA_32B				= 0x00000002,
 };
 
+struct xhci_debug_reg {
+	uint32_t id;
+	uint32_t doorbell;
+	uint32_t erstsz;
+	uint32_t rsvdz;
+	uint64_t erstba;
+	uint64_t erdp;
+	uint32_t ctrl;
+	uint32_t st;
+	uint32_t portsc;
+	uint32_t rsvdp;
+	uint64_t cp;
+	uint32_t ddi1;
+	uint32_t ddi2;
+};
+
+struct xhci_debug_ring {
+	struct xhci_trb *trb;	/* Array 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 */
+#define	XHCI_DB_OUT	0x0
+#define	XHCI_DB_IN	0x1
+#define	XHCI_DB_INVAL	0xFF
+	uint8_t doorbell;	/* Doorbell target */
+};
+
+/* XXX */
+struct xhci_debug_work_ring {
+	uint8_t *buf;
+	uint32_t enq;
+	uint32_t deq;
+	uint64_t paddr;
+};
+
+struct xhci_debug_ctx {
+	uint32_t info[16];
+	uint32_t ep_out[16];
+	uint32_t ep_in[16];
+};
+
+struct xhci_debug_softc {
+	struct xhci_debug_ctx	ctx;
+	struct xhci_debug_reg	reg;
+	struct xhci_event_ring_seg *udb_erst;
+	struct xhci_debug_ring	udb_ering;
+	struct xhci_debug_ring	udb_oring;
+	struct xhci_debug_ring	udb_iring;
+	struct xhci_debug_work_ring	udb_owork;
+	char		udb_str[256];
+	int		open;
+	int		init;
+	struct {
+		volatile uint64_t dummy;
+	} __aligned(64) padding;
+	uint64_t	xhci_xecp_offset;
+	uint64_t	base;
+};
+
 struct xhci_softc {
 	struct xhci_hw_softc	sc_hw;
 	/* base device */
@@ -570,6 +631,9 @@ struct xhci_softc {
 
 	/* XHCI quirks. */
 	uint32_t		sc_quirks;
+
+	/* XHCI DbC */
+	struct xhci_debug_softc	sc_udbc;
 };
 
 #define	XHCI_CMD_LOCK(sc)	sx_xlock(&(sc)->sc_cmd_sx)
diff --git a/sys/dev/usb/controller/xhcireg.h b/sys/dev/usb/controller/xhcireg.h
index 9d0b6e2f4b4b..001a8dd866ae 100644
--- a/sys/dev/usb/controller/xhcireg.h
+++ b/sys/dev/usb/controller/xhcireg.h
@@ -205,6 +205,65 @@
 #define	XHCI_ID_VIRTUALIZATION	0x0004
 #define	XHCI_ID_MSG_IRQ		0x0005
 #define	XHCI_ID_USB_LOCAL_MEM	0x0006
+#define	XHCI_ID_USB_DEBUG	0x000a
+
+/* XHCI Debug Capability */
+#define	XHCI_DCID		0x0001
+#define	XHCI_DCDB		0x0004
+#define	XHCI_DCERSTSZ		0x0008
+#define	XHCI_DCERSTBA		0x0010
+#define	XHCI_DCERDP		0x0018
+#define	XHCI_DCCTRL		0x0020
+#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
+#define	XHCI_DCST_ER		0x00000000
+#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
+#define	XHCI_DCPORTSC_CCS	0x00000001
+#define	XHCI_DCPORTSC_CCS_GET(x)	(((x)      ) & 0x01)
+#define	XHCI_DCPORTSC_PED	0x00000004
+#define	XHCI_DCPORTSC_PED_GET(x)	(((x) >>  2) & 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		0x0030
+#define	XHCI_DCDDI1		0x0038
+#define	XHCI_DCDDI2		0x003C
 
 /* XHCI register R/W wrappers */
 #define	XREAD1(sc, what, a) \
diff --git a/sys/dev/usb/serial/udbc.c b/sys/dev/usb/serial/udbc.c
new file mode 100644
index 000000000000..098086ec97b9
--- /dev/null
+++ b/sys/dev/usb/serial/udbc.c
@@ -0,0 +1,426 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * Copyright (c) 2016-24 Hiroki Sato <hrs@FreeBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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/cdefs.h>
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_ioctl.h>
+#include "usbdevs.h"
+
+#define	USB_DEBUG_VAR udbc_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, udbc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+    "USB DbC Client");
+
+#ifdef USB_DEBUG
+static int udbc_debug = 0;
+SYSCTL_INT(_hw_usb_udbc, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &udbc_debug, 0, "Debug level");
+#endif
+
+#define	UDBC_CONFIG_INDEX	0
+
+#define	UDBC_IBUFSIZE	1024
+#define	UDBC_OBUFSIZE	1024
+
+enum {
+	UDBC_BULK_DT_WR,
+	UDBC_BULK_DT_RD,
+	UDBC_N_TRANSFER,	/* n of EP */
+};
+
+struct udbc_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_device *sc_udev;
+	struct usb_xfer *sc_xfer[UDBC_N_TRANSFER];
+	device_t sc_dev;
+	struct mtx sc_mtx;
+
+	uint32_t sc_unit;
+};
+
+/* prototypes */
+
+static device_probe_t udbc_probe;
+static device_attach_t udbc_attach;
+static device_detach_t udbc_detach;
+static void udbc_free_softc(struct udbc_softc *);
+
+static usb_callback_t udbc_write_callback;
+static usb_callback_t udbc_read_callback;
+
+static void	udbc_free(struct ucom_softc *);
+static void	udbc_cfg_open(struct ucom_softc *);
+static void	udbc_cfg_close(struct ucom_softc *);
+static int	udbc_pre_param(struct ucom_softc *, struct termios *);
+static int	udbc_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
+		    struct thread *);
+static void	udbc_start_read(struct ucom_softc *);
+static void	udbc_stop_read(struct ucom_softc *);
+static void	udbc_start_write(struct ucom_softc *);
+static void	udbc_stop_write(struct ucom_softc *);
+static void	udbc_poll(struct ucom_softc *ucom);
+
+static const struct usb_config udbc_config[UDBC_N_TRANSFER] = {
+	[UDBC_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UDBC_OBUFSIZE,
+		.flags = {.pipe_bof = 1,},
+		.callback = &udbc_write_callback,
+	},
+
+	[UDBC_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UDBC_IBUFSIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &udbc_read_callback,
+	},
+};
+
+static const struct ucom_callback udbc_callback = {
+#if 0
+	.ucom_cfg_get_status = &udbc_cfg_get_status,
+	.ucom_cfg_set_dtr = &udbc_cfg_set_dtr,
+	.ucom_cfg_set_rts = &udbc_cfg_set_rts,
+	.ucom_cfg_set_break = &udbc_cfg_set_break,
+	.ucom_cfg_param = &udbc_cfg_param,
+#endif
+	.ucom_cfg_open = &udbc_cfg_open,
+	.ucom_cfg_close = &udbc_cfg_close,
+	.ucom_pre_param = &udbc_pre_param,
+	.ucom_ioctl = &udbc_ioctl,
+	.ucom_start_read = &udbc_start_read,
+	.ucom_stop_read = &udbc_stop_read,
+	.ucom_start_write = &udbc_start_write,
+	.ucom_stop_write = &udbc_stop_write,
+	.ucom_poll = &udbc_poll,
+	.ucom_free = &udbc_free,
+};
+
+static device_method_t udbc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, udbc_probe),
+	DEVMETHOD(device_attach, udbc_attach),
+	DEVMETHOD(device_detach, udbc_detach),
+	DEVMETHOD_END
+};
+
+static driver_t udbc_driver = {
+	.name = "udbc",
+	.methods = udbc_methods,
+	.size = sizeof(struct udbc_softc),
+};
+
+DRIVER_MODULE(udbc, uhub, udbc_driver, NULL, NULL);
+MODULE_DEPEND(udbc, ucom, 1, 1, 1);
+MODULE_DEPEND(udbc, usb, 1, 1, 1);
+MODULE_VERSION(udbc, 1);
+
+static int
+udbc_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return (ENXIO);
+	if (uaa->info.bConfigIndex != UDBC_CONFIG_INDEX)
+		return (ENXIO);
+	if (uaa->info.bInterfaceClass != UDCLASS_DIAGNOSTIC)
+		return (ENXIO);
+	if (uaa->info.bDeviceProtocol != 0x00)	/* GNU GDB == 1 */
+		return (ENXIO);
+
+	return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+udbc_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct udbc_softc *sc = device_get_softc(dev);
+	int error;
+
+	DPRINTF("\n");
+
+	sc->sc_udev = uaa->device;
+	sc->sc_dev = dev;
+	sc->sc_unit = device_get_unit(dev);
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "udbc", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_ucom.sc_portno = 0;
+
+	error = usbd_transfer_setup(uaa->device,
+	    &uaa->info.bIfaceIndex, sc->sc_xfer, udbc_config,
+	    UDBC_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		device_printf(dev, "allocating USB "
+		    "transfers failed\n");
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UDBC_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UDBC_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &udbc_callback, &sc->sc_mtx);
+	if (error)
+		goto detach;
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);			/* success */
+
+detach:
+	udbc_detach(dev);
+	return (ENXIO);
+}
+
+static int
+udbc_detach(device_t dev)
+{
+	struct udbc_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UDBC_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	udbc_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(udbc);
+
+static void
+udbc_free_softc(struct udbc_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+udbc_free(struct ucom_softc *ucom)
+{
+	udbc_free_softc(ucom->sc_parent);
+}
+
+static void
+udbc_cfg_open(struct ucom_softc *ucom)
+{
+
+	/*
+	 * This do-nothing open routine exists for the sole purpose of this
+	 * DPRINTF() so that you can see the point at which open gets called
+	 * when debugging is enabled.
+	 */
+	DPRINTF("\n");
+}
+
+static void
+udbc_cfg_close(struct ucom_softc *ucom)
+{
+
+	/*
+	 * This do-nothing close routine exists for the sole purpose of this
+	 * DPRINTF() so that you can see the point at which close gets called
+	 * when debugging is enabled.
+	 */
+	DPRINTF("\n");
+}
+
+static void
+udbc_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct udbc_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t buflen;
+
+	DPRINTFN(3, "\n");
+
+	switch (USB_GET_STATE(xfer)) {
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+		}
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0, UDBC_OBUFSIZE, 
+		    &buflen) == 0)
+			break;
+		if (buflen != 0) {
+			usbd_xfer_set_frame_len(xfer, 0, buflen);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+	}
+}
+
+static void
+udbc_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct udbc_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int buflen;
+
+	DPRINTFN(3, "\n");
+
+	usbd_xfer_status(xfer, &buflen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, buflen);
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static int
+udbc_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+
+	DPRINTF("\n");
+
+	return (0);
+}
+
+static int
+udbc_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
+    int flag, struct thread *td)
+{
+#if 0
+	struct udbc_softc *sc = ucom->sc_parent;
+#endif
+	int err;
+
+	switch (cmd) {
+	default:
+		return (ENOIOCTL);
+	}
+	if (err != USB_ERR_NORMAL_COMPLETION)
+		return (EIO);
+	return (0);
+}
+
+static void
+udbc_start_read(struct ucom_softc *ucom)
+{
+	struct udbc_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UDBC_BULK_DT_RD]);
+}
+
+static void
+udbc_stop_read(struct ucom_softc *ucom)
+{
+	struct udbc_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UDBC_BULK_DT_RD]);
+}
+
+static void
+udbc_start_write(struct ucom_softc *ucom)
+{
+	struct udbc_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UDBC_BULK_DT_WR]);
+}
+
+static void
+udbc_stop_write(struct ucom_softc *ucom)
+{
+	struct udbc_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UDBC_BULK_DT_WR]);
+}
+
+static void
+udbc_poll(struct ucom_softc *ucom)
+{
+	struct udbc_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_poll(sc->sc_xfer, UDBC_N_TRANSFER);
+}
diff --git a/sys/modules/usb/Makefile b/sys/modules/usb/Makefile
index 2c593495b9a4..e82bdd615f68 100644
--- a/sys/modules/usb/Makefile
+++ b/sys/modules/usb/Makefile
@@ -48,8 +48,8 @@ SUBDIR += ${_dwc_otg} ehci ${_musb} ohci uhci xhci ${_uss820dci} \
 SUBDIR += ${_rum} ${_run} ${_runfw} ${_uath} upgt usie ural ${_zyd} ${_urtw} 
 SUBDIR += atp cfumass uhid uhid_snes ukbd ums udbp uep wmt wsp ugold uled \
 	  usbhid
-SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \
-	  umct umcs umodem umoscom uplcom uslcom uvisor uvscom
+SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom udbc ufoma uftdi ugensa \
+	  uipaq ulpt umct umcs umodem umoscom uplcom uslcom uvisor uvscom
 SUBDIR += cp2112
 SUBDIR += udl
 SUBDIR += uether aue axe axge cdce cdceem cue ${_kue} mos rue smsc udav uhso \
diff --git a/sys/modules/usb/udbc/Makefile b/sys/modules/usb/udbc/Makefile
new file mode 100644
index 000000000000..9996b2e391fb
--- /dev/null
+++ b/sys/modules/usb/udbc/Makefile
@@ -0,0 +1,9 @@
+S=     ${SRCTOP}/sys
+
+.PATH: $S/dev/usb/serial
+
+KMOD=	udbc
+SRCS=	opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h usbdevs.h \
+	udbc.c
+
+.include <bsd.kmod.mk>
