From: Connor Davis <connojdavis@gmail.com>
To: xen-devel@lists.xenproject.org
Cc: "Connor Davis" <connojdavis@gmail.com>,
"Andrew Cooper" <andrew.cooper3@citrix.com>,
"George Dunlap" <george.dunlap@citrix.com>,
"Ian Jackson" <iwj@xenproject.org>,
"Jan Beulich" <jbeulich@suse.com>,
"Julien Grall" <julien@xen.org>,
"Stefano Stabellini" <sstabellini@kernel.org>,
"Wei Liu" <wl@xen.org>, "Roger Pau Monné" <roger.pau@citrix.com>
Subject: [PATCH] drivers/char: Add USB3 debug capability driver
Date: Tue, 11 May 2021 18:12:40 -0600 [thread overview]
Message-ID: <9a6a15ebc538105c83be88883ab3a7125ed52d37.1620776791.git.connojdavis@gmail.com> (raw)
Add support for the xHCI debug capability (DbC). The DbC provides
a SuperSpeed serial link between a debug target running Xen and a
debug host. To use it you will need a USB3 A/A debug cable plugged into
a root port on the target machine. Recent kernels report the existence
of the DbC capability at
/sys/kernel/debug/usb/xhci/<seg>:<bus>:<slot>.<func>/reg-ext-dbc:00
The host machine should have the usb_debug.ko module on the system
(the cable can be plugged into any port on the host side). After the
host usb_debug enumerates the DbC, it will create a /dev/ttyUSB<n> file
that can be used with things like minicom.
To use the DbC as a console, pass `console=dbgp dbgp=xhci`
on the xen command line. This will select the first host controller
found that implements the DbC. Other variations to 'dbgp=' are accepted,
please see xen-command-line.pandoc for more. Remote GDB is also supported
with `gdb=dbgp dbgp=xhci`. Note that to see output and/or provide input
after dom0 starts, DMA remapping of the host controller must be
disabled.
Signed-off-by: Connor Davis <connojdavis@gmail.com>
---
MAINTAINERS | 6 +
docs/misc/xen-command-line.pandoc | 19 +-
xen/arch/x86/Kconfig | 1 -
xen/arch/x86/setup.c | 1 +
xen/drivers/char/Kconfig | 15 +
xen/drivers/char/Makefile | 1 +
xen/drivers/char/xhci-dbc.c | 1122 +++++++++++++++++++++++++++++
xen/drivers/char/xhci-dbc.h | 621 ++++++++++++++++
xen/include/asm-x86/fixmap.h | 4 +
xen/include/xen/serial.h | 15 +
10 files changed, 1803 insertions(+), 2 deletions(-)
create mode 100644 xen/drivers/char/xhci-dbc.c
create mode 100644 xen/drivers/char/xhci-dbc.h
diff --git a/MAINTAINERS b/MAINTAINERS
index d46b08a0d2..3f38c022a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -620,6 +620,12 @@ F: tools/xentrace/
F: xen/common/trace.c
F: xen/include/xen/trace.h
+XHCI DBC DRIVER
+M: Connor Davis <connojdavis@gmail.com>
+S: Maintained
+F: xen/drivers/char/xhci-dbc.c
+F: xen/drivers/char/xhci-dbc.h
+
XSM/FLASK
M: Daniel De Graaf <dgdegra@tycho.nsa.gov>
S: Supported
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
index c32a397a12..1b63432806 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -714,9 +714,26 @@ Available alternatives, with their meaning, are:
### dbgp
> `= ehci[ <integer> | @pci<bus>:<slot>.<func> ]`
-Specify the USB controller to use, either by instance number (when going
+Specify the EHCI USB controller to use, either by instance number (when going
over the PCI busses sequentially) or by PCI device (must be on segment 0).
+If you have a system with an xHCI USB controller that supports the Debug
+Capability (DbC), you can use
+
+> `= xhci[ <integer> | @pci<bus>:<slot>.<func> ]`
+
+To use this, you need a USB3 A/A debugging cable plugged into a SuperSpeed
+root port on the target machine. Recent kernels expose the existence of the
+DbC at /sys/kernel/debug/usb/xhci/<seg>:<bus>:<slot>.<func>/reg-ext-dbc:00.
+Note that to see output and process input after dom0 is started, you need to
+ensure that the host controller's DMA is not remapped (e.g. with
+dom0-iommu=passthrough).
+
+Finally, be sure that usb_debug.ko is present on the debug host, as the DbC
+emulates a USB debug device that is bound to usb_debug, which in turn
+creates a /dev/ttyUSB<n> file that can be used with things like minicom
+and gdb.
+
### debug_stack_lines
> `= <integer>`
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index e55e029b79..469227f66b 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -11,7 +11,6 @@ config X86
select HAS_ALTERNATIVE
select HAS_COMPAT
select HAS_CPUFREQ
- select HAS_EHCI
select HAS_EX_TABLE
select HAS_FAST_MULTIPLY
select HAS_IOPORTS
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 8105dc36bb..4612f3e8b8 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -924,6 +924,7 @@ void __init noreturn __start_xen(unsigned long mbi_p)
ns16550.irq = 3;
ns16550_init(1, &ns16550);
ehci_dbgp_init();
+ xhci_dbc_init();
console_init_preirq();
if ( pvh_boot )
diff --git a/xen/drivers/char/Kconfig b/xen/drivers/char/Kconfig
index b572305657..a48ca69a87 100644
--- a/xen/drivers/char/Kconfig
+++ b/xen/drivers/char/Kconfig
@@ -63,6 +63,21 @@ config HAS_SCIF
config HAS_EHCI
bool
depends on X86
+ default y if !HAS_XHCI_DBC
help
This selects the USB based EHCI debug port to be used as a UART. If
you have an x86 based system with USB, say Y.
+
+config HAS_XHCI_DBC
+ bool "xHCI Debug Capability driver"
+ depends on X86 && HAS_PCI
+ help
+ This selects the xHCI Debug Capabilty to be used as a UART.
+
+config XHCI_FIXMAP_PAGES
+ int "Number of fixmap entries to allocate for the xHC"
+ depends on HAS_XHCI_DBC
+ default 16
+ help
+ This should equal the size (in 4K pages) of the first 64-bit
+ BAR of the host controller in which the DbC is being used.
diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
index 7c646d771c..c2cb8c09b7 100644
--- a/xen/drivers/char/Makefile
+++ b/xen/drivers/char/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_HAS_MVEBU) += mvebu-uart.o
obj-$(CONFIG_HAS_OMAP) += omap-uart.o
obj-$(CONFIG_HAS_SCIF) += scif-uart.o
obj-$(CONFIG_HAS_EHCI) += ehci-dbgp.o
+obj-$(CONFIG_HAS_XHCI_DBC) += xhci-dbc.o
obj-$(CONFIG_ARM) += arm-uart.o
obj-y += serial.o
obj-$(CONFIG_XEN_GUEST) += xen_pv_console.o
diff --git a/xen/drivers/char/xhci-dbc.c b/xen/drivers/char/xhci-dbc.c
new file mode 100644
index 0000000000..5e22f4a601
--- /dev/null
+++ b/xen/drivers/char/xhci-dbc.c
@@ -0,0 +1,1122 @@
+/*
+ * xen/drivers/char/xhci-dbc.c
+ *
+ * Driver for USB xHCI Debug Capability
+ * ported from https://github.com/connojd/xue.git
+ *
+ * Copyright (C) 2019 Assured Information Security, Inc.
+ * Copyright (C) 2021 Connor Davis <connojdavis@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "xhci-dbc.h"
+
+#include <xen/param.h>
+#include <xen/pci.h>
+#include <xen/serial.h>
+#include <xen/timer.h>
+
+#include <asm/fixmap.h>
+
+/*
+ * To use the DbC as a serial console, pass in the following string
+ * param (in addition to console=dbgp):
+ *
+ * dbgp=xhci | xhci[0-9] | xhci@pci<bus>:<dev>.<func>
+ *
+ * The first variant uses the first compatible xhci controller found.
+ * The second looks for the n'th xhci with a DbC. Both use breadth-first
+ * search of the PCI bus. The last uses the pci string to specify the
+ * BDF of the USB3 host controller you want to use.
+ * PCI segments > 0 are not supported.
+ */
+static char __initdata opt_dbgp[30];
+string_param("dbgp", opt_dbgp);
+
+static struct xhci_dbc xhci_dbc;
+
+/* Buffers for raw data referenced by struct data_ring */
+static char data_out[DATA_RING_SIZE] __aligned(PAGE_SIZE);
+static char data_in[DATA_RING_SIZE] __aligned(PAGE_SIZE);
+
+/* Ring segments must not straddle a 64K boundary */
+static struct trb trb_evt[TRB_RING_SIZE] __aligned(TRB_RING_ALIGN);
+static struct trb trb_out[TRB_RING_SIZE] __aligned(TRB_RING_ALIGN);
+static struct trb trb_in[TRB_RING_SIZE] __aligned(TRB_RING_ALIGN);
+
+/* Port variable for timer handling */
+static DEFINE_PER_CPU(struct serial_port *, poll_port);
+
+static void init_strings(struct xhci_dbc *dbc)
+{
+ struct dbc_info_ctx *info = &dbc->ctx.info;
+ struct dbc_str_desc *dbc_str = &dbc->strings;
+ struct usb_str_desc *usb_str = (struct usb_str_desc *)&dbc_str->string0;
+
+ const char manufacturer[] = "Xen Project";
+ const char product[] = "Xen DbC Device";
+ const char serial[] = "0001";
+
+ uint32_t length = 0;
+
+ /*
+ * Each string is converted to UTF16, plus one byte for bLength and
+ * one byte for bDescriptorType.
+ */
+ BUILD_BUG_ON(sizeof(manufacturer) * 2 + 2 >= DBC_MAX_STR_SIZE);
+ BUILD_BUG_ON(sizeof(product) * 2 + 2 >= DBC_MAX_STR_SIZE);
+ BUILD_BUG_ON(sizeof(serial) * 2 + 2 >= DBC_MAX_STR_SIZE);
+
+ /* string0 specifies the LANGIDs supported... */
+ usb_str->bLength = 4;
+ usb_str->bDescriptorType = USB_DT_STRING;
+ usb_str->wData[0] = cpu_to_le16(USB_LANGID_ENGLISH);
+ length |= usb_str->bLength;
+
+ /* ...the others are UTF16LE-encoded strings */
+ usb_str = (struct usb_str_desc *)&dbc_str->manufacturer;
+ usb_str->bLength = 2 + (2 * strlen(manufacturer));
+ usb_str->bDescriptorType = USB_DT_STRING;
+ length |= ((uint32_t)usb_str->bLength << 8);
+ copy_utf16le(usb_str->wData, manufacturer);
+
+ usb_str = (struct usb_str_desc *)&dbc_str->product;
+ usb_str->bLength = 2 + (2 * strlen(product));
+ usb_str->bDescriptorType = USB_DT_STRING;
+ length |= ((uint32_t)usb_str->bLength << 16);
+ copy_utf16le(usb_str->wData, product);
+
+ usb_str = (struct usb_str_desc *)&dbc_str->serial;
+ usb_str->bLength = 2 + (2 * strlen(serial));
+ usb_str->bDescriptorType = USB_DT_STRING;
+ length |= ((uint32_t)usb_str->bLength << 24);
+ copy_utf16le(usb_str->wData, serial);
+
+ memset(info, 0, sizeof(*info));
+
+ info->string0 = cpu_to_le64(virt_to_maddr(dbc_str->string0));
+ info->manufacturer = cpu_to_le64(virt_to_maddr(dbc_str->manufacturer));
+ info->product = cpu_to_le64(virt_to_maddr(dbc_str->product));
+ info->serial = cpu_to_le64(virt_to_maddr(dbc_str->serial));
+ info->length = cpu_to_le32(length);
+}
+
+/*
+ * Initialize the endpoint as specified in sections 7.6.3.2
+ * and 7.6.9.2. Each DbC endpoint has Bulk type, so MaxPStreams,
+ * LSA, HID, CErr, FE, Interval, Mult, and Max ESIT Payload
+ * fields are all 0.
+ */
+static inline void init_endpoint(struct xhci_dbc *dbc,
+ uint32_t type,
+ uint64_t trdp)
+{
+ struct dbc_ep_ctx *ep;
+ const uint32_t max_pkt = EP_MAX_PKT_SIZE << 16;
+ const uint32_t max_burst = dbc_max_burst(dbc) << 8;
+
+ ep = (type == EP_TYPE_BULK_OUT) ? &dbc->ctx.ep_out
+ : &dbc->ctx.ep_in;
+ memset(ep, 0, sizeof(*ep));
+
+ ep->data1 = cpu_to_le32(max_pkt | max_burst | (type << 3));
+ ep->trdp = cpu_to_le64(trdp | EP_INITIAL_DCS);
+ ep->data2 = cpu_to_le32(EP_AVG_TRB_LENGTH);
+}
+
+static inline void init_xfer_ring(struct trb_ring *ring,
+ struct trb *trb,
+ int size)
+{
+ struct trb *last_trb;
+
+ memset(trb, 0, size * sizeof(*trb));
+
+ ring->trb = trb;
+ ring->enq = 0;
+ ring->deq = 0;
+ ring->cycle = 1;
+
+ /*
+ * Transfer rings must have a link TRB that points to the next segment.
+ * Currently only one-segment rings are supported, so setup a link TRB
+ * pointing back to the beginning.
+ */
+
+ last_trb = &trb[size - 1];
+
+ trb_set_type(last_trb, TRB_TYPE_LINK);
+ trb_link_set_toggle_cycle(last_trb);
+ trb_link_set_rsp(last_trb, virt_to_maddr(trb));
+}
+
+static inline void init_event_ring(struct trb_ring *ring,
+ struct trb *trb,
+ int size)
+{
+ memset(trb, 0, size * sizeof(*trb));
+
+ ring->trb = trb;
+ ring->enq = 0;
+ ring->deq = 0;
+ ring->cycle = 1;
+}
+
+static inline void init_data_ring(struct data_ring *ring,
+ char *data,
+ int size)
+{
+ memset(data, 0, size);
+
+ ring->buf = data;
+ ring->enq = 0;
+ ring->deq = 0;
+ ring->dma_addr = virt_to_maddr(data);
+}
+
+static inline void init_erst(struct xhci_dbc *dbc, uint64_t erdp, int size)
+{
+ /* Only one-segment rings are supported */
+ BUILD_BUG_ON(DBC_ERSTSZ != 1);
+
+ memset(dbc->erst, 0, sizeof(dbc->erst));
+
+ dbc->erst[0].base = cpu_to_le64(erdp);
+ dbc->erst[0].size = cpu_to_le16(size);
+}
+
+/*
+ * Map in the registers. Section 5.2.1 of the xHCI spec
+ * mandates that the first two BARs must be implemented as a
+ * 64-bit MMIO region that includes all of the xHC's registers.
+ */
+static int xhci_map_regs(struct xhci_dbc *dbc)
+{
+ uint64_t phys, size, pages;
+ int fix = FIX_XHCI_END;
+
+ BUILD_BUG_ON(CONFIG_XHCI_FIXMAP_PAGES <= 0);
+
+ if ( pci_size_mem_bar(dbc->sbdf, PCI_BASE_ADDRESS_0, &phys, &size, 0)
+ != 2 )
+ return -EINVAL;
+
+ if ( size < PAGE_SIZE )
+ return -EINVAL;
+
+ if ( (phys & ~PAGE_MASK) || (size & ~PAGE_MASK) )
+ return -EINVAL;
+
+ pages = size / PAGE_SIZE;
+
+ if ( pages > CONFIG_XHCI_FIXMAP_PAGES )
+ return -EINVAL;
+
+ for ( uint64_t p = 0; p < pages; p++ )
+ {
+ set_fixmap_nocache(fix--, phys);
+ phys += PAGE_SIZE;
+ }
+
+ dbc->mmio_paddr = phys;
+ dbc->mmio_pages = pages;
+ dbc->xhc_regs = (uint8_t __iomem *)fix_to_virt(FIX_XHCI_END);
+
+ return 0;
+}
+
+static void xhci_unmap_regs(struct xhci_dbc *dbc)
+{
+ int fix = FIX_XHCI_END;
+
+ for ( uint64_t p = 0; p < dbc->mmio_pages; p++ )
+ clear_fixmap(fix--);
+
+ dbc->xhc_regs = NULL;
+}
+
+static inline void xhci_enable_mmio_and_dma(const struct xhci_dbc *dbc)
+{
+ uint8_t cmd = pci_conf_read8(dbc->sbdf, PCI_COMMAND);
+
+ cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ pci_conf_write8(dbc->sbdf, PCI_COMMAND, cmd);
+}
+
+/*
+ * Search for an xHCI extended capability. If start is 0, then begin the search
+ * at the beginning of the list. Otherwise, start is the offset from the
+ * beginning of the xHC's registers. Note this function will return the _next_
+ * capability with id == @capid, so if start points to a cap with @capid X on
+ * entry, this will return the offset of the next cap with @capid X (or 0
+ * if there is none).
+ */
+static uint32_t xhci_find_next_cap(const struct xhci_dbc *dbc,
+ uint32_t start,
+ uint32_t capid)
+{
+ const uint8_t __iomem *cap;
+ uint32_t val, id, next, offset;
+
+ offset = start;
+ if ( !offset )
+ {
+ const uint32_t xecp = readl(&dbc->xhc_regs[XHCI_HCCPARAMS1]) >> 16;
+ if ( !xecp )
+ return 0;
+
+ offset = xecp << 2;
+ }
+
+ do {
+ cap = dbc->xhc_regs + offset;
+ val = readl(cap);
+ id = val & 0xFF;
+
+ if ( offset != start && (id == capid) )
+ return offset;
+
+ next = (val & 0xFF00) >> 8;
+ offset += next << 2;
+ } while ( next );
+
+ return 0;
+}
+
+static int dbc_find_regs(struct xhci_dbc *dbc)
+{
+ uint32_t offset = xhci_find_next_cap(dbc, 0, XHCI_CAPID_DBC);
+
+ if ( !offset )
+ return -ENODEV;
+
+ dbc->dbc_regs = (struct dbc_regs __iomem *)(dbc->xhc_regs + offset);
+ return 0;
+}
+
+static void dbc_ring_doorbell(struct xhci_dbc *dbc, int doorbell)
+{
+ uint32_t db;
+ struct dbc_regs __iomem *regs = dbc->dbc_regs;
+ uint32_t ctrl = readl(®s->ctrl);
+
+ if ( ctrl & CTRL_DRC )
+ {
+ /* Re-arm the doorbell */
+ writel(ctrl | CTRL_DRC, ®s->ctrl);
+ }
+
+ if ( !(ctrl & CTRL_DCR) )
+ return;
+
+ if ( doorbell == DOORBELL_OUT && (ctrl & CTRL_HOT) )
+ return;
+
+ if ( doorbell == DOORBELL_IN && (ctrl & CTRL_HIT) )
+ return;
+
+ db = (readl(®s->db) & 0xFFFF00FF) | (doorbell << 8);
+ writel(db, ®s->db);
+}
+
+static inline void dbc_stop(const struct xhci_dbc *dbc)
+{
+ uint32_t ctrl = readl(&dbc->dbc_regs->ctrl);
+
+ writel(ctrl & ~CTRL_DCE, &dbc->dbc_regs->ctrl);
+ readl_poll(&dbc->dbc_regs->ctrl, dbc_off, POLL_LIMIT);
+}
+
+static void dbc_init(struct xhci_dbc *dbc, uint64_t erdp)
+{
+ uint64_t ordp = virt_to_maddr(trb_out);
+ uint64_t irdp = virt_to_maddr(trb_in);
+
+ init_xfer_ring(&dbc->trb_out_ring, trb_out, TRB_RING_SIZE);
+ init_xfer_ring(&dbc->trb_in_ring, trb_in, TRB_RING_SIZE);
+
+ init_data_ring(&dbc->data_out_ring, data_out, DATA_RING_SIZE);
+ init_data_ring(&dbc->data_in_ring, data_in, DATA_RING_SIZE);
+
+ init_event_ring(&dbc->trb_evt_ring, trb_evt, TRB_RING_SIZE);
+ init_erst(dbc, erdp, TRB_RING_SIZE);
+ init_strings(dbc);
+
+ init_endpoint(dbc, EP_TYPE_BULK_OUT, ordp);
+ init_endpoint(dbc, EP_TYPE_BULK_IN, irdp);
+}
+
+static void dbc_reset(struct xhci_dbc *dbc)
+{
+ struct dbc_regs __iomem *regs;
+ uint64_t erdp, cp, erstba;
+ uint32_t erstsz, ddi;
+
+ BUILD_BUG_ON(DATA_RING_SIZE > TRB_MAX_XFER);
+
+ /* Make sure we start from the DbC-Off state */
+ dbc_stop(dbc);
+
+ erdp = virt_to_maddr(trb_evt);
+ dbc_init(dbc, erdp);
+
+ regs = dbc->dbc_regs;
+
+ erstsz = (readl(®s->erstsz) & 0xFFFF0000) | DBC_ERSTSZ;
+ writel(erstsz, ®s->erstsz);
+
+ erstba = (lo_hi_readq(®s->erstba) & 0xF) | virt_to_maddr(&dbc->erst);
+ lo_hi_writeq(erstba, ®s->erstba);
+
+ erdp |= lo_hi_readq(®s->erdp) & 0x8;
+ lo_hi_writeq(erdp, ®s->erdp);
+
+ cp = (lo_hi_readq(®s->cp) & 0xF) | virt_to_maddr(&dbc->ctx);
+ lo_hi_writeq(cp, ®s->cp);
+
+ ddi = (DBC_VENDOR << 16) | DBC_PROTOCOL;
+ writel(ddi, ®s->ddi1);
+
+ ddi = (DBC_REVISION << 16) | DBC_PRODUCT;
+ writel(ddi, ®s->ddi2);
+}
+
+static int dbc_start(const struct xhci_dbc *dbc)
+{
+ uint32_t ctrl, portsc;
+ struct dbc_regs __iomem *regs = dbc->dbc_regs;
+ const int max_try = 2;
+
+ for ( int try = 0; try < max_try; try++ )
+ {
+ ctrl = readl(®s->ctrl);
+ writel(ctrl | CTRL_DCE | CTRL_LSE, ®s->ctrl);
+
+ ctrl = readl_poll(®s->ctrl, dbc_on, POLL_LIMIT);
+ if ( !dbc_on(ctrl) )
+ return -ENODEV;
+
+ /* Wait for transition to DbC-Enabled state */
+ portsc = readl_poll(®s->portsc, dbc_enabled, POLL_LIMIT);
+ if ( !dbc_enabled(portsc) )
+ return -ENODEV;
+
+ /*
+ * Now we're DbC-Enabled, wait for the host to enumerate the
+ * DbC capability and bring us to the DbC-Configured state.
+ */
+ ctrl = readl_poll(®s->ctrl, dbc_configured, POLL_LIMIT);
+ if ( !dbc_configured(ctrl) )
+ {
+ /*
+ * Clear port status change bits. If a tPortConfigurationTimeout
+ * is the reason we failed to configure, then we must clear
+ * PORTSC.CEC manually. If we failed for another reason
+ * (e.g. an LTSSM substate polling timeout or warm reset), then
+ * the relevant status bits in PORTSC will be cleared upon
+ * clearing CTRL_DCE.
+ */
+ portsc = readl(®s->portsc);
+ writel(portsc | PORTSC_CEC, ®s->portsc);
+ dbc_stop(dbc);
+
+ continue;
+ }
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static inline int dbc_state(const struct xhci_dbc *dbc)
+{
+ uint32_t ctrl = readl(&dbc->dbc_regs->ctrl);
+ uint32_t portsc = readl(&dbc->dbc_regs->portsc);
+ uint32_t pls = (portsc & PORTSC_PLS) >> 5;
+
+ if (!(ctrl & CTRL_DCE))
+ return DBC_OFF;
+
+ if (!(portsc & PORTSC_CCS))
+ return DBC_DISCONNECTED;
+
+ if (ctrl & CTRL_DCR)
+ return DBC_CONFIGURED;
+
+ if (portsc & PORTSC_PR)
+ return DBC_RESETTING;
+
+ if (pls == DBC_PLS_INACTIVE)
+ return DBC_ERROR;
+
+ if (pls == DBC_PLS_DISABLED)
+ return DBC_DISABLED;
+
+ return DBC_ENABLED;
+}
+
+static inline void dbc_port_disable(const struct xhci_dbc *dbc)
+{
+ uint32_t portsc = readl(&dbc->dbc_regs->portsc);
+
+ writel(portsc & ~PORTSC_PED, &dbc->dbc_regs->portsc);
+ readl_poll(&dbc->dbc_regs->portsc, port_disabled, POLL_LIMIT);
+}
+
+static inline void dbc_port_enable(const struct xhci_dbc *dbc)
+{
+ uint32_t portsc = readl(&dbc->dbc_regs->portsc);
+
+ writel(portsc | PORTSC_PED, &dbc->dbc_regs->portsc);
+ readl_poll(&dbc->dbc_regs->portsc, port_enabled, POLL_LIMIT);
+}
+
+static inline void dbc_handle_psc_event(const struct xhci_dbc *dbc)
+{
+ uint32_t portsc = readl(&dbc->dbc_regs->portsc);
+
+ portsc |= PORTSC_PSC_MASK;
+ writel(portsc, &dbc->dbc_regs->portsc);
+}
+
+static void dbc_handle_out_xfer_event(struct xhci_dbc *dbc,
+ const struct trb *event)
+{
+ struct trb_ring *trb_ring = &dbc->trb_out_ring;
+
+ if ( trb_xfer_event_cc(event) == TRB_CC_TRANSACTION_ERROR)
+ {
+ uint32_t ctrl = readl(&dbc->dbc_regs->ctrl);
+
+ if ( (ctrl & CTRL_DRC) &&
+ ep_state(&dbc->ctx.ep_out) == EP_STATE_DISABLED )
+ {
+ uint32_t bytes_left = trb_xfer_event_len(event);
+
+ if ( bytes_left )
+ {
+ /*
+ * In this case we left the DBC_CONFIGURED state after
+ * partially transfering a TRB. Recover by re-queueing
+ * the left over bytes. See section 7.6.4.4 for more
+ * details on this behavior.
+ */
+ uint64_t idx = (trb_xfer_event_ptr(event) & TRB_RING_MASK) /
+ sizeof(struct trb);
+
+ struct trb *orig_trb = &trb_ring->trb[idx];
+
+ uint64_t dma = trb_norm_buf(orig_trb) +
+ trb_norm_len(orig_trb) - bytes_left;
+
+ trb_norm_set_buf(orig_trb, dma);
+ trb_norm_set_len(orig_trb, bytes_left);
+
+ /*
+ * Make sure the xHC schedules this TRB when the doorbell
+ * is rung later.
+ */
+ dbc->ctx.ep_out.trdp = cpu_to_le64(virt_to_maddr(orig_trb));
+ }
+ }
+ }
+
+ trb_ring->deq = (trb_xfer_event_ptr(event) & TRB_RING_MASK) /
+ sizeof(struct trb);
+}
+
+static void dbc_handle_in_xfer_event(struct xhci_dbc *dbc,
+ const struct trb *event)
+{
+ uint32_t idx = (trb_xfer_event_ptr(event) & TRB_RING_MASK) /
+ sizeof(struct trb);
+ struct trb *orig_trb = &dbc->trb_in_ring.trb[idx];
+ uint32_t bytes_rcvd = trb_norm_len(orig_trb) - trb_xfer_event_len(event);
+ struct data_ring *in_ring = &dbc->data_in_ring;
+ struct trb_ring *trb_ring = &dbc->trb_in_ring;
+
+ if ( in_ring->enq + bytes_rcvd >= DATA_RING_SIZE )
+ in_ring->enq = bytes_rcvd - (DATA_RING_SIZE - in_ring->enq);
+ else
+ in_ring->enq += bytes_rcvd;
+
+ trb_ring->deq = ((trb_xfer_event_ptr(event) & TRB_RING_MASK) /
+ sizeof(struct trb)) + 1;
+ trb_ring->deq &= TRB_RING_SIZE - 1;
+}
+
+static void dbc_handle_xfer_event(struct xhci_dbc *dbc,
+ const struct trb *event)
+{
+ int epid = trb_xfer_event_epid(event);
+
+ if ( epid == XDBC_EPID_OUT || epid == XDBC_EPID_OUT_INTEL )
+ dbc_handle_out_xfer_event(dbc, event);
+ else if ( epid == XDBC_EPID_IN || epid == XDBC_EPID_IN_INTEL )
+ dbc_handle_in_xfer_event(dbc, event);
+}
+
+static void dbc_process_events(struct xhci_dbc *dbc)
+{
+ struct trb_ring *evt_ring = &dbc->trb_evt_ring;
+ struct trb *evt = &evt_ring->trb[evt_ring->deq];
+ bool update_erdp = false;
+
+ while ( trb_cycle(evt) == evt_ring->cycle )
+ {
+ /*
+ * The xHC performs a release with the cycle bit to publish
+ * the TRB's contents. Add the corresponding acquire here.
+ */
+ rmb();
+
+ switch ( trb_type(evt) )
+ {
+ case TRB_TYPE_PSC_EVENT:
+ dbc_handle_psc_event(dbc);
+ break;
+ case TRB_TYPE_XFER_EVENT:
+ dbc_handle_xfer_event(dbc, evt);
+ break;
+ default:
+ break;
+ }
+
+ if ( evt_ring->deq == TRB_RING_SIZE - 1 )
+ evt_ring->cycle ^= 1;
+
+ evt_ring->deq = (evt_ring->deq + 1) & (TRB_RING_SIZE - 1);
+ evt = &evt_ring->trb[evt_ring->deq];
+ update_erdp = true;
+ }
+
+ if ( update_erdp )
+ {
+ uint64_t erdp = lo_hi_readq(&dbc->dbc_regs->erdp);
+
+ erdp &= ~TRB_RING_MASK;
+ erdp |= evt_ring->deq * sizeof(struct trb);
+
+ lo_hi_writeq(erdp, &dbc->dbc_regs->erdp);
+ }
+}
+
+static int dbc_handle_state(struct xhci_dbc *dbc)
+{
+ switch ( dbc_state(dbc) )
+ {
+ case DBC_OFF:
+ dbc_reset(dbc);
+ return dbc_start(dbc);
+ case DBC_DISABLED:
+ dbc_port_enable(dbc);
+ break;
+ case DBC_ERROR:
+ /*
+ * Try to toggle the port (without releasing it) to re-enumerate,
+ * lest we get stuck if the host never issues a hot or warm reset.
+ */
+ dbc_port_disable(dbc);
+ dbc_port_enable(dbc);
+ break;
+ default:
+ /*
+ * There are no other states in which we can actually do
+ * anything to cause a transition to DBC_CONFIGURED; we just
+ * have to wait on the hardware to transition.
+ */
+ break;
+ }
+
+ return 0;
+}
+
+static void dbc_queue_xfer(struct trb_ring *ring,
+ uint64_t dma,
+ uint32_t len)
+{
+ struct trb *trb;
+
+ if (ring->enq == TRB_RING_SIZE - 1) {
+ /*
+ * We have to make sure the xHC processes the link TRB in order
+ * for wrap-around to work properly. We do this by marking the
+ * xHC as owner of the link TRB by setting the TRB's cycle bit
+ * (just like with normal TRBs).
+ */
+ struct trb *link = &ring->trb[ring->enq];
+
+ /* Release TRB fields */
+ wmb();
+ trb_set_cycle(link, ring->cycle);
+
+ ring->enq = 0;
+ ring->cycle ^= 1;
+ }
+
+ trb = &ring->trb[ring->enq];
+ trb_set_type(trb, TRB_TYPE_NORMAL);
+
+ trb_norm_set_buf(trb, dma);
+ trb_norm_set_len(trb, len);
+ trb_norm_set_ioc(trb);
+
+ /* Release TRB fields */
+ wmb();
+ trb_set_cycle(trb, ring->cycle);
+
+ ring->enq++;
+}
+
+static void dbc_queue_tx_data(struct xhci_dbc *dbc)
+{
+ paddr_t dma;
+ uint64_t len;
+
+ struct data_ring *out_ring = &dbc->data_out_ring;
+ struct trb_ring *trb_ring = &dbc->trb_out_ring;
+
+ if ( out_ring->enq == out_ring->deq )
+ return;
+
+ dma = out_ring->dma_addr + out_ring->deq;
+
+ if ( out_ring->enq > out_ring->deq )
+ {
+ len = out_ring->enq - out_ring->deq;
+ dbc_queue_xfer(trb_ring, dma, len);
+ }
+ else
+ {
+ len = DATA_RING_SIZE - out_ring->deq;
+ dbc_queue_xfer(trb_ring, dma, len);
+
+ if ( out_ring->enq )
+ dbc_queue_xfer(trb_ring, out_ring->dma_addr, out_ring->enq);
+ }
+
+ out_ring->deq = out_ring->enq;
+}
+
+static void dbc_queue_rx_data(struct xhci_dbc *dbc)
+{
+ struct data_ring *in_ring = &dbc->data_in_ring;
+ struct trb_ring *trb_ring = &dbc->trb_in_ring;
+
+ if ( in_ring->enq + MAX_RX_SIZE <= DATA_RING_SIZE )
+ dbc_queue_xfer(trb_ring, in_ring->dma_addr + in_ring->enq, MAX_RX_SIZE);
+ else
+ {
+ uint64_t first_len = DATA_RING_SIZE - in_ring->enq;
+
+ dbc_queue_xfer(trb_ring, in_ring->dma_addr + in_ring->enq, first_len);
+ dbc_queue_xfer(trb_ring, in_ring->dma_addr, MAX_RX_SIZE - first_len);
+ }
+}
+
+static void dbc_flush_locked(struct xhci_dbc *dbc)
+{
+ if ( dbc->unsafe )
+ return;
+
+ xhci_enable_mmio_and_dma(dbc);
+
+ if ( dbc_handle_state(dbc) )
+ return;
+
+ dbc_process_events(dbc);
+ dbc_queue_tx_data(dbc);
+ dbc_ring_doorbell(dbc, DOORBELL_OUT);
+}
+
+static void dbc_flush(struct serial_port *port)
+{
+ unsigned long flags;
+ struct xhci_dbc *dbc = (struct xhci_dbc *)port->uart;
+
+ spin_lock_irqsave(&dbc->lock, flags);
+ dbc_flush_locked(dbc);
+ spin_unlock_irqrestore(&dbc->lock, flags);
+}
+
+static int dbc_reset_prepare(struct xhci_dbc *dbc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbc->lock, flags);
+
+ if ( dbc->unsafe )
+ goto out;
+
+ /* Drain pending transfers and events */
+ dbc_flush_locked(dbc);
+ dbc_stop(dbc);
+
+ /*
+ * We aren't allowed to touch any MMIO regs until the host
+ * controller is done resetting as indicated by dbc_reset_done.
+ */
+ dbc->unsafe = true;
+
+out:
+ spin_unlock_irqrestore(&dbc->lock, flags);
+ return 0;
+}
+
+static int dbc_reset_done(struct xhci_dbc *dbc)
+{
+ int rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbc->lock, flags);
+
+ if ( !dbc->unsafe )
+ goto out;
+
+ dbc->unsafe = false;
+ dbc_reset(dbc);
+
+ rc = dbc_start(dbc);
+
+out:
+ spin_unlock_irqrestore(&dbc->lock, flags);
+ return 0;
+}
+
+int dbgp_op(const struct physdev_dbgp_op *op)
+{
+ struct xhci_dbc *dbc = &xhci_dbc;
+
+ if ( !dbc->dbc_regs )
+ return 0;
+
+ switch ( op->bus )
+ {
+ case PHYSDEVOP_DBGP_BUS_UNKNOWN:
+ /* Only PCI is supported ATM */
+ return -EINVAL;
+ case PHYSDEVOP_DBGP_BUS_PCI:
+ if ( PCI_SEG(dbc->sbdf.sbdf) != op->u.pci.seg ||
+ PCI_BUS(dbc->sbdf.sbdf) != op->u.pci.bus ||
+ PCI_DEVFN2(dbc->sbdf.sbdf) != op->u.pci.devfn )
+ return -ENODEV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch ( op->op )
+ {
+ case PHYSDEVOP_DBGP_RESET_PREPARE:
+ return dbc_reset_prepare(dbc);
+ case PHYSDEVOP_DBGP_RESET_DONE:
+ return dbc_reset_done(dbc);
+ default:
+ return -EINVAL;
+ }
+}
+
+static void dbc_putc(struct serial_port *port, char c)
+{
+ struct xhci_dbc *dbc = port->uart;
+ struct data_ring *ring = &dbc->data_out_ring;
+
+ ring->buf[ring->enq] = c;
+ ring->enq = (ring->enq + 1) & (DATA_RING_SIZE - 1);
+}
+
+static int dbc_getc(struct serial_port *port, char *c)
+{
+ int rc;
+ unsigned long flags;
+
+ struct xhci_dbc *dbc = port->uart;
+ struct data_ring *ring = &dbc->data_in_ring;
+
+ rc = 0;
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ spin_lock_irqsave(&dbc->lock, flags);
+
+ /*
+ * If the port is synchronous we need to ring the doorbell
+ * and process events manually here instead of relying on
+ * the timer callback. Hence taking tx_lock prior to checking
+ * port->sync.
+ */
+ if ( port->sync )
+ {
+ if ( dbc->unsafe )
+ goto out;
+
+ if ( dbc->trb_in_ring.enq == dbc->trb_in_ring.deq )
+ {
+ dbc_queue_rx_data(dbc);
+ dbc_ring_doorbell(dbc, DOORBELL_IN);
+ }
+
+ dbc_process_events(dbc);
+ }
+
+ if ( ring->enq != ring->deq )
+ {
+ *c = ring->buf[ring->deq];
+ ring->deq = (ring->deq + 1) & (DATA_RING_SIZE - 1);
+ rc = 1;
+ }
+
+out:
+ spin_unlock_irqrestore(&dbc->lock, flags);
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+
+ return rc;
+}
+
+static void dbc_rx_poll(struct cpu_user_regs *regs)
+{
+ unsigned long flags;
+
+ struct serial_port *port = this_cpu(poll_port);
+ struct xhci_dbc *dbc = port->uart;
+
+ spin_lock_irqsave(&dbc->lock, flags);
+
+ if ( dbc->unsafe )
+ goto out;
+
+ dbc_process_events(dbc);
+
+ if ( dbc->trb_in_ring.enq == dbc->trb_in_ring.deq )
+ {
+ dbc_queue_rx_data(dbc);
+ dbc_ring_doorbell(dbc, DOORBELL_IN);
+ }
+
+out:
+ spin_unlock_irqrestore(&dbc->lock, flags);
+
+ while ( dbc->data_in_ring.enq != dbc->data_in_ring.deq )
+ serial_rx_interrupt(port, regs);
+
+ set_timer(&dbc->timer, NOW() + MILLISECS(5));
+}
+
+static void dbc_handle_timer(void *data)
+{
+ this_cpu(poll_port) = data;
+
+#ifdef run_in_exception_handler
+ run_in_exception_handler(dbc_rx_poll);
+#else
+ dbc_rx_poll(guest_cpu_user_regs());
+#endif
+}
+
+static void __init dbc_init_postirq(struct serial_port *port)
+{
+ struct xhci_dbc *dbc = port->uart;
+
+ if ( !dbc->dbc_regs )
+ return;
+
+ init_timer(&dbc->timer, dbc_handle_timer, port, 0);
+ set_timer(&dbc->timer, NOW() + MILLISECS(5));
+}
+
+static struct uart_driver __read_mostly dbc_driver = {
+ .init_postirq = dbc_init_postirq,
+ .putc = dbc_putc,
+ .flush = dbc_flush,
+ .getc = dbc_getc
+};
+
+static bool __init xhci_detect(uint32_t bus,
+ uint32_t dev,
+ uint32_t func)
+{
+ uint32_t hdr, class;
+ pci_sbdf_t sbdf = PCI_SBDF(0, bus, dev, func);
+
+ hdr = pci_conf_read8(sbdf, PCI_HEADER_TYPE);
+ if ( (hdr != PCI_HEADER_TYPE_NORMAL) &&
+ (hdr != (PCI_HEADER_TYPE_NORMAL | 0x80)) )
+ return false;
+
+ class = pci_conf_read32(sbdf, PCI_CLASS_REVISION) >> 8;
+ if ( class != XHCI_PCI_CLASS )
+ return false;
+
+ return true;
+}
+
+/*
+ * This implements the handoff algorithm described in section 4.22.1.
+ * It is adapted to xen from linux:
+ * drivers/usb/early/xhci-dbc.c:xdbc_bios_handoff
+ * Copyright (C) 2016 Intel Corporation
+ * and
+ * drivers/usb/host/pci-quirks.c:quirk_usb_handoff_xhci
+ * Copyright (C) 1999 Martin Mares <mj@ucw.cz>
+ */
+static void __init dbc_bios_handoff(const struct xhci_dbc *dbc)
+{
+ uint32_t __iomem *legacy;
+ uint32_t val;
+ uint32_t offset = xhci_find_next_cap(dbc, 0, XHCI_CAPID_LEGACY);
+
+ if ( !offset )
+ return;
+
+ legacy = (uint32_t __iomem *)(dbc->xhc_regs + offset);
+ val = readl(legacy);
+
+ writel(val | XHCI_OS_OWNED, legacy);
+
+ /*
+ * We are supposed to wait at most one second for the BIOS to
+ * clear its ownership bit. Provide an approximation with
+ * cpu_relax since early_time_init hasn't been called (and
+ * therefore udelay isn't available) yet.
+ */
+
+ val = readl_poll(legacy, xhci_os_owned, POLL_LIMIT);
+ if ( !xhci_os_owned(val) )
+ /* BIOS lost their chance, all your usb are belong to us */
+ writel(val & ~XHCI_BIOS_OWNED, legacy);
+
+ /* Now disable SMIs in the USBLEGCTLSTS register */
+ val = readl(legacy + 4);
+ val &= XHCI_LEGACY_DISABLE_SMI;
+ val |= XHCI_LEGACY_SMI_EVENTS;
+
+ writel(val, legacy + 4);
+}
+
+int __init xhci_find(unsigned long num, uint32_t *b, uint32_t *d, uint32_t *f)
+{
+ uint32_t bus, dev, func;
+ unsigned long count = 0;
+
+ for ( bus = 0; bus < 256; bus++ )
+ {
+ for ( dev = 0; dev < 32; dev++ )
+ {
+ for ( func = 0; func < 8; func++ )
+ {
+ if ( !pci_device_detect(0, bus, dev, func) )
+ {
+ if ( !func )
+ break;
+ continue;
+ }
+
+ if ( !xhci_detect(bus, dev, func) || count++ != num )
+ {
+ pci_sbdf_t sbdf = PCI_SBDF(0, bus, dev, func);
+ uint8_t hdr = pci_conf_read8(sbdf, PCI_HEADER_TYPE);
+
+ /* Go to next dev if this one isn't multi-function */
+ if ( !func && !(hdr & 0x80) )
+ break;
+ continue;
+ }
+ else
+ {
+ *b = bus;
+ *d = dev;
+ *f = func;
+
+ return 0;
+ }
+ }
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int __init xhci_parse_param(uint32_t *bus, uint32_t *dev, uint32_t *func)
+{
+ if ( strncmp(opt_dbgp, "xhci", 4) )
+ return -ENODEV;
+
+ if ( isdigit(opt_dbgp[4]) || !opt_dbgp[4] )
+ {
+ unsigned long num = 0;
+
+ if ( opt_dbgp[4] )
+ num = simple_strtoul(opt_dbgp + 4, NULL, 10);
+
+ if ( xhci_find(num, bus, dev, func) )
+ return -ENODEV;
+ }
+ else if ( !strncmp(opt_dbgp, "xhci@pci", 8) )
+ {
+ if ( !parse_pci(opt_dbgp + 8, NULL, bus, dev, func) )
+ return -ENODEV;
+
+ if ( !pci_device_detect(0, *bus, *dev, *func) )
+ return -ENODEV;
+
+ if ( !xhci_detect(*bus, *dev, *func) )
+ return -ENODEV;
+ }
+ else
+ return -ENODEV;
+
+ return 0;
+}
+
+void __init xhci_dbc_init(void)
+{
+ uint32_t bus, dev, func;
+ struct xhci_dbc *dbc = &xhci_dbc;
+
+ if ( xhci_parse_param(&bus, &dev, &func) )
+ return;
+
+ dbc->sbdf = PCI_SBDF(0, bus, dev, func);
+ xhci_enable_mmio_and_dma(dbc);
+
+ if ( xhci_map_regs(dbc) )
+ return;
+
+ if ( dbc_find_regs(dbc) )
+ goto unmap;
+
+ spin_lock_init(&dbc->lock);
+
+ dbc_bios_handoff(dbc);
+ dbc_reset(dbc);
+
+ if ( dbc_start(dbc) )
+ goto stop;
+
+ serial_register_uart(SERHND_DBGP, &dbc_driver, dbc);
+ return;
+
+stop:
+ dbc_stop(dbc);
+
+unmap:
+ xhci_unmap_regs(dbc);
+}
diff --git a/xen/drivers/char/xhci-dbc.h b/xen/drivers/char/xhci-dbc.h
new file mode 100644
index 0000000000..34f51933a9
--- /dev/null
+++ b/xen/drivers/char/xhci-dbc.h
@@ -0,0 +1,621 @@
+/*
+ * xen/drivers/char/xhci-dbc.h
+ *
+ * Driver for USB xHCI Debug Capability
+ * ported from https://github.com/connojd/xue.git
+ *
+ * Copyright (C) 2019 Assured Information Security, Inc.
+ * Copyright (C) 2021 Connor Davis <connojdavis@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <xen/lib.h>
+#include <xen/page-size.h>
+#include <xen/pci.h>
+#include <xen/spinlock.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+/* USB string descriptor values */
+#define USB_DT_STRING 3
+#define USB_LANGID_ENGLISH 0x0409
+#define DBC_MAX_STR_SIZE 64
+
+/* Number of DbC event ring segments */
+#define DBC_ERSTSZ 1
+
+/* DbC control register */
+#define CTRL_DCR (1U << 0)
+#define CTRL_LSE (1U << 1)
+#define CTRL_HOT (1U << 2)
+#define CTRL_HIT (1U << 3)
+#define CTRL_DRC (1U << 4)
+#define CTRL_DCE (1U << 31)
+
+/* DbC port status change register */
+#define PORTSC_CCS (1U << 0)
+#define PORTSC_PED (1U << 1)
+#define PORTSC_PR (1U << 4)
+#define PORTSC_PLS (0xFU << 5)
+#define PORTSC_CSC (1U << 17)
+#define PORTSC_PRC (1U << 21)
+#define PORTSC_PLC (1U << 22)
+#define PORTSC_CEC (1U << 23)
+
+/* Port status change bits must be cleared for further event generation */
+#define PORTSC_PSC_MASK (PORTSC_CSC|PORTSC_PRC|PORTSC_PLC|PORTSC_CEC)
+
+/* TRB types */
+#define TRB_TYPE_NORMAL 1
+#define TRB_TYPE_LINK 6
+#define TRB_TYPE_XFER_EVENT 32
+#define TRB_TYPE_PSC_EVENT 34
+
+/* TRB completion codes */
+#define TRB_CC_TRANSACTION_ERROR 4
+
+#define TRB_MAX_XFER (PAGE_SIZE * 16)
+#define TRB_PER_PAGE (PAGE_SIZE / sizeof(struct trb))
+#define TRB_RING_ORDER 2
+#define TRB_RING_PAGES (1U << TRB_RING_ORDER)
+#define TRB_RING_SIZE (TRB_PER_PAGE * TRB_RING_PAGES)
+#define TRB_RING_MASK ((TRB_RING_SIZE * sizeof(struct trb)) - 1)
+#define TRB_RING_ALIGN (PAGE_SIZE * 16)
+
+/* DbC endpoint values */
+#define EP_STATE_DISABLED 0
+#define EP_STATE_RUNNING 1
+#define EP_STATE_HALTED 2
+#define EP_STATE_STOPPED 3
+#define EP_STATE_ERROR 4
+#define EP_TYPE_BULK_OUT 2
+#define EP_TYPE_BULK_IN 6
+#define EP_MAX_PKT_SIZE 1024
+#define EP_AVG_TRB_LENGTH (3 * 1024)
+#define EP_INITIAL_DCS 1
+
+#define TRB_TYPE_MASK 0xFC00
+#define TRB_TYPE_SHIFT 10
+#define TRB_CYCLE_MASK 0x1
+#define TRB_EVENT_EPID_MASK 0x1F0000
+#define TRB_EVENT_EPID_SHIFT 16
+#define TRB_EVENT_CC_SHIFT 24
+#define TRB_EVENT_LEN_MASK 0x1FFFF
+#define TRB_NORM_LEN_MASK 0x1FFFF
+#define TRB_NORM_IOC_MASK 0x20
+#define TRB_LINK_TOGGLE_CYCLE_MASK 0x2
+#define EP_STATE_MASK 0x7
+#define MAX_BURST_MASK 0xFF0000
+#define MAX_BURST_SHIFT 16
+
+/*
+ * The following comment and XDBC_EPID_* values are taken from
+ * linux/drivers/usb/early/xhci-dbc.h:
+ *
+ * These are the "Endpoint ID" (also known as "Context Index") values for the
+ * OUT Transfer Ring and the IN Transfer Ring of a Debug Capability Context data
+ * structure.
+ * According to the "eXtensible Host Controller Interface for Universal Serial
+ * Bus (xHCI)" specification, section "7.6.3.2 Endpoint Contexts and Transfer
+ * Rings", these should be 0 and 1, and those are the values AMD machines give
+ * you; but Intel machines seem to use the formula from section "4.5.1 Device
+ * Context Index", which is supposed to be used for the Device Context only.
+ * Luckily the values from Intel don't overlap with those from AMD, so we can
+ * just test for both.
+ */
+#define XDBC_EPID_OUT 0
+#define XDBC_EPID_IN 1
+#define XDBC_EPID_OUT_INTEL 2
+#define XDBC_EPID_IN_INTEL 3
+
+/*
+ * The doorbell target is written to the doorbell register to
+ * notify hardware that the respective transfer ring's enqueue
+ * pointer has been updated (i.e., data is ready to be transfered).
+ */
+#define DOORBELL_OUT 0
+#define DOORBELL_IN 1
+
+#define DATA_RING_ORDER 2
+#define DATA_RING_PAGES (1UL << DATA_RING_ORDER)
+#define DATA_RING_SIZE (PAGE_SIZE * DATA_RING_PAGES)
+#define DATA_RING_MASK (DATA_RING_SIZE - 1)
+
+#define MAX_RX_SIZE EP_MAX_PKT_SIZE
+
+#define DBC_PROTOCOL 0x0001 /* Remote GDB */
+#define DBC_VENDOR 0x1D6B /* Linux Foundation */
+#define DBC_PRODUCT 0x0010
+#define DBC_REVISION 0x0000
+
+/*
+ * xHCI PCI class code
+ *
+ * Bits 7:0 - 0x30: USB3 host controller compliant with xHCI
+ * Bits 15:8 - 0x03: USB host controller
+ * Bits 23:16 - 0x0C: Serial bus controller
+ */
+#define XHCI_PCI_CLASS 0x0C0330
+
+/*
+ * Byte offset from the base of the xHC's MMIO
+ * region to the HCCPARAMS1 capability register
+ */
+#define XHCI_HCCPARAMS1 0x10
+
+/* Extended xHCI capabilities we care about */
+#define XHCI_CAPID_LEGACY 1
+#define XHCI_CAPID_DBC 10
+
+/* Legacy ownership bits */
+#define XHCI_BIOS_OWNED (1 << 16)
+#define XHCI_OS_OWNED (1 << 24)
+
+/* The set bits here are RsvdP, the RW SMI bits are clear */
+#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 17) | (0xff << 5) | (0x7 << 1))
+
+/* SMI events, RW1C */
+#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29)
+
+/* DbC states */
+enum {
+ DBC_OFF,
+ DBC_DISCONNECTED,
+ DBC_DISABLED,
+ DBC_ENABLED,
+ DBC_ERROR,
+ DBC_RESETTING,
+ DBC_CONFIGURED
+};
+
+/* Port link states */
+enum {
+ DBC_PLS_U0,
+ DBC_PLS_U1,
+ DBC_PLS_U2,
+ DBC_PLS_U3,
+ DBC_PLS_DISABLED,
+ DBC_PLS_RXDETECT,
+ DBC_PLS_INACTIVE,
+ DBC_PLS_POLLING,
+ DBC_PLS_RECOVERY,
+ DBC_PLS_HOT_RESET
+};
+
+/*
+ * struct trb - xHC transfer request block
+ *
+ * TRBs come in several flavors, but the DbC mainly deals with
+ * event and normal TRB types. The fields' meanings vary depending
+ * on the type (which is always defined in bits @ctrl[15:10]).
+ *
+ * Normal TRBs are placed onto transfer rings and point to data
+ * that the xHC should read or write. In turn, the xHC produces
+ * event TRBs on the the event ring, which are processed by the driver.
+ */
+struct trb {
+ __le64 params;
+ __le32 status;
+ __le32 ctrl;
+};
+
+/*
+ * struct erst_segment - Event ring segment
+ *
+ * Specifies a physically contiguous region of event TRBs. The region must
+ * not straddle a 64K boundary.
+ *
+ * @base the 64-byte aligned physical address of the segment
+ * @size the number of TRBs in the segment. Must be in [16, 4096].
+ */
+struct erst_segment {
+ __le64 base;
+ __le16 size;
+ __le16 __rsvdz[3];
+};
+
+/*
+ * struct dbc_info_ctx - Physical addresses for the DbC's USB
+ * String Descriptors.
+ *
+ * @string0 - address for languages supported
+ * @manufacturer - address for manufacturer
+ * @product - address for product
+ * @serial - address for serial number
+ * @length - each byte contains the length of the corresponding string
+ * in bytes, e.g., byte 0 of @length is the number of
+ * bytes of string0.
+ */
+struct dbc_info_ctx {
+ __le64 string0;
+ __le64 manufacturer;
+ __le64 product;
+ __le64 serial;
+ __le32 length;
+ __le32 __rsvdz[7];
+};
+
+/*
+ * struct dbc_ep_ctx - DbC endpoint context
+ *
+ * Describes a single bulk transfer ring. The DbC has two bulk endpoints:
+ * one for IN transfers, one for OUT. The field values required for the DbC
+ * are specified in sections 7.6.3.2 and 7.6.9.2.
+ */
+struct dbc_ep_ctx {
+ __le32 data0;
+ __le32 data1;
+ __le64 trdp;
+ __le32 data2;
+ __le32 __rsvdo[11];
+};
+
+/*
+ * struct dbc_ctx - DbC context
+ *
+ * Container for the USB string and endpoint contexts.
+ */
+struct dbc_ctx {
+ struct dbc_info_ctx info;
+ struct dbc_ep_ctx ep_out;
+ struct dbc_ep_ctx ep_in;
+};
+
+/*
+ * struct dbc_regs
+ *
+ * The registers are a subset of the xHC's MMIO registers
+ * and are found by traversing the xHC's capability list.
+ *
+ * @id - extended capability ID
+ * @db - doorbell register, written by software whenever new TRBs
+ * are ready for consumption.
+ * @erstsz - number of entries in the event ring segment stable (ERST).
+ * @erstba - phys address of the ERST, value must be 64-byte aligned.
+ * @erdp - event ring dequeue pointer, updated as software consumes events.
+ * @ctrl - control register
+ * @st - status register
+ * @portsc - port status and control register
+ * @cp - phys address of the DbC context, value must be 16-byte aligned.
+ * @ddi1 - DbC protocol and vendor ID
+ * @ddi2 - device revision and product ID
+ */
+struct dbc_regs {
+ __le32 id;
+ __le32 db;
+ __le32 erstsz;
+ __le32 __rsvdz;
+ __le64 erstba;
+ __le64 erdp;
+ __le32 ctrl;
+ __le32 st;
+ __le32 portsc;
+ __le32 __rsvdp;
+ __le64 cp;
+ __le32 ddi1;
+ __le32 ddi2;
+};
+
+/*
+ * struct trb_ring
+ *
+ * The DbC uses three TRB rings: one event ring and two bulk transfer rings
+ * (one for each direction). The hardware produces Event TRBs onto the event
+ * ring and the driver code produces Normal TRBs onto the transfer rings.
+ *
+ * @trb array of trbs
+ * @enq offset into @trb at which the next trb is produced
+ * @deq offset of the most recently consumed trb
+ * @cycle the bit that determines the ownership of any given
+ * trb in the ring. Both the driver and hardware have internal
+ * copies that toggle as the ring is traversed.
+ */
+struct trb_ring {
+ struct trb *trb;
+ unsigned int enq;
+ unsigned int deq;
+ unsigned int cycle;
+};
+
+/*
+ * struct data_ring
+ *
+ * The raw bytes sent from Xen and received from the debug host are buffered
+ * into a data_ring. When the uart is flush()'ed, the bytes are queued into
+ * a TRB on the appropriate transfer ring.
+ *
+ * @buf the buffer for the data
+ * @enq end of buffered data
+ * @deq beginning of buffered data
+ * @dma_addr physical address of @buf
+ */
+struct data_ring {
+ char *buf;
+ unsigned int enq;
+ unsigned int deq;
+ paddr_t dma_addr;
+};
+
+/* USB string descriptor */
+struct usb_str_desc {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __le16 wData[1];
+};
+
+/*
+ * DbC device strings. string0 is special in that it encodes
+ * the supported LANGIDs of the DbC device. The others are
+ * UTF16LE-encoded character strings
+ */
+struct dbc_str_desc {
+ char string0[DBC_MAX_STR_SIZE];
+ char manufacturer[DBC_MAX_STR_SIZE];
+ char product[DBC_MAX_STR_SIZE];
+ char serial[DBC_MAX_STR_SIZE];
+};
+
+/*
+ * struct xhci_dbc
+ *
+ * @sbdf - location of the HC on the PCI bus
+ * @lock - used for synchronizing safety checks
+ * @timer - used to inject RX interrupts
+ * @unsafe - when true, MMIO accesses are not permitted. This applies
+ * for a small window of time after dom0 resets the HC until
+ * the HC clears the Controller Not Ready (CNR) bit
+ * sandwiched between the PHYSDEVOP_DBGP_RESET_PREPARE and
+ * PHYSDEVOP_DBGP_RESET_DONE hypercalls.
+ * @mmio_paddr - base address of the HC's MMIO region
+ * @mmio_pages - number of 4K pages of the HC's MMIO region
+ * @xhc_regs - fixmapped value of mmio_paddr
+ * @dbc_regs - base address of the DbC registers
+ * @trb_evt_ring - event ring. The driver consumes TRBs off of
+ * this ring.
+ * @trb_out_ring - OUT transfer ring. Data written by Xen is referenced
+ * by TRBs placed onto this ring.
+ * @trb_in_ring - IN transfer ring. When the port is !sync, a timer
+ * interrupt places TRBs onto this ring, otherwise the TRB
+ * is queued directly by dbc_getc.
+ * @data_out_ring - circular buffer for data written by Xen
+ * @data_in_ring - circular buffer for data received from debug host
+ * @strings - USB string data
+ * @ctx - string and endpoint context
+ * @erst - event ring segment table. All event and transfer rings use
+ * only one segment.
+ */
+struct xhci_dbc {
+ pci_sbdf_t sbdf;
+ spinlock_t lock;
+ struct timer timer;
+ bool unsafe;
+ uint64_t mmio_paddr;
+ uint64_t mmio_pages;
+ uint8_t __iomem *xhc_regs;
+ struct dbc_regs __iomem *dbc_regs;
+ struct trb_ring trb_evt_ring;
+ struct trb_ring trb_out_ring;
+ struct trb_ring trb_in_ring;
+ struct data_ring data_out_ring;
+ struct data_ring data_in_ring;
+ struct dbc_str_desc strings __aligned(64);
+ struct dbc_ctx ctx __aligned(64);
+ struct erst_segment erst[DBC_ERSTSZ] __aligned(64);
+};
+
+/*
+ * Type used for polling predicates over 32-bit MMIO register values.
+ * Return true when polling should stop, false otherwise.
+ */
+typedef bool (*poll_pred_t)(uint32_t val);
+
+#define POLL_LIMIT 10000000
+
+static uint32_t readl_poll(const uint32_t __iomem *ptr,
+ poll_pred_t done,
+ unsigned int limit)
+{
+ unsigned int i = 0;
+ uint32_t val = readl(ptr);
+
+ while ( !done(val) && i++ < limit )
+ {
+ cpu_relax();
+ val = readl(ptr);
+ }
+
+ return val;
+}
+
+static inline bool xhci_os_owned(uint32_t legacy)
+{
+ return (legacy & (XHCI_BIOS_OWNED | XHCI_OS_OWNED)) == XHCI_OS_OWNED;
+}
+
+static inline bool dbc_enabled(uint32_t portsc)
+{
+ return (portsc & (PORTSC_CCS | PORTSC_PED)) == (PORTSC_CCS | PORTSC_PED);
+}
+
+static inline bool dbc_configured(uint32_t ctrl)
+{
+ return (ctrl & CTRL_DCR) != 0;
+}
+
+static inline bool dbc_on(uint32_t ctrl)
+{
+ return (ctrl & CTRL_DCE) != 0;
+}
+
+static inline bool dbc_off(uint32_t ctrl)
+{
+ return (ctrl & CTRL_DCE) == 0;
+}
+
+/* Imported from include/linux/io-64-nonatomic-lo-hi.h */
+static inline __u64 lo_hi_readq(const volatile void __iomem *addr)
+{
+ const volatile u32 __iomem *p = addr;
+ u32 low, high;
+
+ low = readl(p);
+ high = readl(p + 1);
+
+ return low + ((u64)high << 32);
+}
+
+/* Imported from include/linux/io-64-nonatomic-lo-hi.h */
+static inline void lo_hi_writeq(__u64 val, volatile void __iomem *addr)
+{
+ const volatile u8 __iomem *p = addr;
+
+ writel(val, p);
+ writel(val >> 32, p + 4);
+}
+
+static inline void copy_utf16le(u16 *wstr, const char *s)
+{
+ int len = strlen(s);
+
+ for ( int i = 0; i < len; i++ )
+ wstr[i] = cpu_to_le16(s[i]);
+}
+
+
+static inline void trb_set_type(struct trb *trb, uint32_t type)
+{
+ uint32_t ctrl = le32_to_cpu(trb->ctrl);
+
+ ctrl &= ~TRB_TYPE_MASK;
+ ctrl |= type << TRB_TYPE_SHIFT;
+
+ trb->ctrl = cpu_to_le32(ctrl);
+}
+
+static inline int trb_type(const struct trb *trb)
+{
+ return (le32_to_cpu(trb->ctrl) & TRB_TYPE_MASK) >> TRB_TYPE_SHIFT;
+}
+
+
+static inline void trb_set_cycle(struct trb *trb, uint32_t cyc)
+{
+ uint32_t ctrl = le32_to_cpu(trb->ctrl);
+
+ ctrl &= ~TRB_CYCLE_MASK;
+ ctrl |= cyc;
+
+ trb->ctrl = cpu_to_le32(ctrl);
+}
+
+static inline int trb_cycle(const struct trb *trb)
+{
+ return le32_to_cpu(trb->ctrl) & TRB_CYCLE_MASK;
+}
+
+static inline int trb_xfer_event_epid(const struct trb *trb)
+{
+ return (le32_to_cpu(trb->ctrl) & TRB_EVENT_EPID_MASK)
+ >> TRB_EVENT_EPID_SHIFT;
+}
+
+static inline int trb_xfer_event_cc(const struct trb *trb)
+{
+ return le32_to_cpu(trb->status) >> TRB_EVENT_CC_SHIFT;
+}
+
+static inline uint64_t trb_xfer_event_ptr(const struct trb *trb)
+{
+ return le64_to_cpu(trb->params);
+}
+
+static inline uint32_t trb_xfer_event_len(const struct trb *trb)
+{
+ return le32_to_cpu(trb->status) & TRB_EVENT_LEN_MASK;
+}
+
+static inline void trb_norm_set_buf(struct trb *trb, uint64_t dma)
+{
+ trb->params = cpu_to_le64(dma);
+}
+
+static inline void trb_norm_set_len(struct trb *trb, uint32_t len)
+{
+ uint32_t status = le32_to_cpu(trb->status);
+
+ status &= ~TRB_EVENT_LEN_MASK;
+ status |= len;
+
+ trb->status = cpu_to_le32(status);
+}
+
+static inline uint64_t trb_norm_buf(const struct trb *trb)
+{
+ return le64_to_cpu(trb->params);
+}
+
+static inline uint32_t trb_norm_len(const struct trb *trb)
+{
+ return le32_to_cpu(trb->status) & TRB_NORM_LEN_MASK;
+}
+
+static inline void trb_norm_set_ioc(struct trb *trb)
+{
+ uint32_t ctrl = le32_to_cpu(trb->ctrl);
+
+ ctrl |= TRB_NORM_IOC_MASK;
+ trb->ctrl = cpu_to_le32(ctrl);
+}
+
+static inline void trb_link_set_toggle_cycle(struct trb *trb)
+{
+ uint32_t ctrl = le32_to_cpu(trb->ctrl);
+
+ ctrl |= TRB_LINK_TOGGLE_CYCLE_MASK;
+ trb->ctrl = cpu_to_le32(ctrl);
+}
+
+static inline void trb_link_set_rsp(struct trb *trb, uint64_t rsp)
+{
+ trb->params = cpu_to_le64(rsp);
+}
+
+static inline bool port_disabled(uint32_t portsc)
+{
+ return (portsc & PORTSC_PED) == 0;
+}
+
+static inline bool port_enabled(uint32_t portsc)
+{
+ return (portsc & PORTSC_PED) != 0;
+}
+
+static inline uint32_t ep_state(const struct dbc_ep_ctx *ctx)
+{
+ return le32_to_cpu(ctx->data0) & EP_STATE_MASK;
+}
+
+static inline uint32_t dbc_max_burst(const struct xhci_dbc *dbc)
+{
+ return (readl(&dbc->dbc_regs->ctrl) & MAX_BURST_MASK)
+ >> MAX_BURST_SHIFT;
+}
diff --git a/xen/include/asm-x86/fixmap.h b/xen/include/asm-x86/fixmap.h
index 0db314baeb..35f9b38d97 100644
--- a/xen/include/asm-x86/fixmap.h
+++ b/xen/include/asm-x86/fixmap.h
@@ -43,6 +43,10 @@ enum fixed_addresses {
FIX_COM_BEGIN,
FIX_COM_END,
FIX_EHCI_DBGP,
+#ifdef CONFIG_HAS_XHCI_DBC
+ FIX_XHCI_BASE,
+ FIX_XHCI_END = FIX_XHCI_BASE + CONFIG_XHCI_FIXMAP_PAGES - 1,
+#endif
#ifdef CONFIG_XEN_GUEST
FIX_PV_CONSOLE,
FIX_XEN_SHARED_INFO,
diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
index 6548f0b0a9..85098de590 100644
--- a/xen/include/xen/serial.h
+++ b/xen/include/xen/serial.h
@@ -170,7 +170,22 @@ struct ns16550_defaults {
unsigned long io_base; /* default io_base address */
};
void ns16550_init(int index, struct ns16550_defaults *defaults);
+
+#ifdef CONFIG_HAS_EHCI
void ehci_dbgp_init(void);
+#else
+static inline void ehci_dbgp_init(void)
+{
+}
+#endif
+
+#ifdef CONFIG_HAS_XHCI_DBC
+void xhci_dbc_init(void);
+#else
+static inline void xhci_dbc_init(void)
+{
+}
+#endif
void arm_uart_init(void);
base-commit: d4fb5f166c2bfbaf9ba0de69da0d411288f437a9
--
2.31.1
next reply other threads:[~2021-05-12 0:13 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-05-12 0:12 Connor Davis [this message]
2021-05-17 9:27 ` [PATCH] drivers/char: Add USB3 debug capability driver Jan Beulich
2021-05-17 13:40 ` Connor Davis
2021-05-17 13:47 ` Jan Beulich
2021-05-17 9:36 ` Julien Grall
2021-05-17 13:41 ` Connor Davis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=9a6a15ebc538105c83be88883ab3a7125ed52d37.1620776791.git.connojdavis@gmail.com \
--to=connojdavis@gmail.com \
--cc=andrew.cooper3@citrix.com \
--cc=george.dunlap@citrix.com \
--cc=iwj@xenproject.org \
--cc=jbeulich@suse.com \
--cc=julien@xen.org \
--cc=roger.pau@citrix.com \
--cc=sstabellini@kernel.org \
--cc=wl@xen.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).