All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/7] Add xHCI USB support
@ 2020-12-07  7:41 Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors Patrick Rudolph
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

Add basic support for xHCI USB controllers and non root xHCI hubs.
The motivation is to use this code on platforms that do not provide
user input by runtime services (like BIOS or UEFI platform) do.
This is the case when GRUB is used as coreboot payload for example.

The code is based on seabios implementation, but has been heavily
modified to match grubs internals.

Changes done in version 2:
 * Code cleanup
 * Code style fixes
 * Don't leak memory buffers
 * Compile without warnings
 * Add more defines
 * Add more helper functions
 * Don't assume a 1:1 virtual to physical mapping
 * Flush cachelines after writing buffers
 * Don't use hardcoded page size
 * Proper scratchpad register setup
 * xHCI bios ownership handoff

Changes done in version 3:
 * Several bug fixes for real hardware
 * Fixed a race condition detecting events, which doesn't appear on
   qemu based xHCI controllers
 * Don't accidently disable USB3.0 devices after first command
 * Support arbitrary protocol speed IDs
 * Coding style cleanup
 * Support for non root SuperSpeed hubs

Changes Tested:
 * Qemu system x86_64
   * virtual USB HID keyboard (usb-kbd)
   * virtual USB HID mass storage (usb-storage)
 * Intel C246 integrated xHCI (Supermicro X11SSH-F)
   * iKVM HID keyboard
   * USB3 HID mass storage (controller root port)
   * External USB HID keyboard

TODO:
 * Test on more hardware
 * Test on USB3 hubs
 * Support for USB 3.1 and USB 3.2 controllers

Patrick Rudolph (7):
  grub-core/bus/usb: Parse SuperSpeed companion descriptors
  usb: Add enum for xHCI
  usbtrans: Set default maximum packet size
  grub-core/bus/usb: Add function pointer for attach/detach events
  grub-core/bus/usb/usbhub: Add new private fields for xHCI controller
  grub-core/bus/usb: Add xhci support
  grub-core/bus/usb/usbhub: Add xHCI non root hub support

 Makefile.am                       |    2 +-
 grub-core/Makefile.core.def       |    7 +
 grub-core/bus/usb/ehci.c          |    2 +
 grub-core/bus/usb/ohci.c          |    2 +
 grub-core/bus/usb/serial/common.c |    2 +-
 grub-core/bus/usb/uhci.c          |    2 +
 grub-core/bus/usb/usb.c           |   44 +-
 grub-core/bus/usb/usbhub.c        |   95 +-
 grub-core/bus/usb/usbtrans.c      |    6 +-
 grub-core/bus/usb/xhci-pci.c      |  195 +++
 grub-core/bus/usb/xhci.c          | 2496 +++++++++++++++++++++++++++++
 grub-core/commands/usbtest.c      |    2 +-
 grub-core/disk/usbms.c            |    2 +-
 grub-core/term/usb_keyboard.c     |    2 +-
 include/grub/usb.h                |   18 +-
 include/grub/usbdesc.h            |   12 +-
 include/grub/usbtrans.h           |    4 +
 17 files changed, 2852 insertions(+), 41 deletions(-)
 create mode 100644 grub-core/bus/usb/xhci-pci.c
 create mode 100644 grub-core/bus/usb/xhci.c

-- 
2.26.2



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2021-03-19  1:06   ` Glenn Washburn
  2020-12-07  7:41 ` [PATCH v2 2/7] usb: Add enum for xHCI Patrick Rudolph
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

Parse the SS_ENDPOINT_COMPANION descriptor, which is only present on USB 3.0
capable devices and xHCI controllers. Make the descendp an array of pointers
to the endpoint descriptor as it's no longer an continous array.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 grub-core/bus/usb/serial/common.c |  2 +-
 grub-core/bus/usb/usb.c           | 44 +++++++++++++++++++------------
 grub-core/bus/usb/usbhub.c        | 22 ++++++++++++----
 grub-core/commands/usbtest.c      |  2 +-
 grub-core/disk/usbms.c            |  2 +-
 grub-core/term/usb_keyboard.c     |  2 +-
 include/grub/usb.h                |  2 +-
 include/grub/usbdesc.h            | 11 +++++++-
 8 files changed, 59 insertions(+), 28 deletions(-)

diff --git a/grub-core/bus/usb/serial/common.c b/grub-core/bus/usb/serial/common.c
index 8e94c7dc0..63e64cc0a 100644
--- a/grub-core/bus/usb/serial/common.c
+++ b/grub-core/bus/usb/serial/common.c
@@ -72,7 +72,7 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno,
   for (j = 0; j < interf->endpointcnt; j++)
     {
       struct grub_usb_desc_endp *endp;
-      endp = &usbdev->config[0].interf[interfno].descendp[j];
+      endp = usbdev->config[0].interf[interfno].descendp[j];
 
       if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2
 	  && (in_endp == GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING
diff --git a/grub-core/bus/usb/usb.c b/grub-core/bus/usb/usb.c
index 8da5e4c74..6b32bbd93 100644
--- a/grub-core/bus/usb/usb.c
+++ b/grub-core/bus/usb/usb.c
@@ -115,7 +115,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
   struct grub_usb_desc_device *descdev;
   struct grub_usb_desc_config config;
   grub_usb_err_t err;
-  int i;
+  int i, j;
 
   /* First we have to read first 8 bytes only and determine
    * max. size of packet */
@@ -149,6 +149,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
       int currif;
       char *data;
       struct grub_usb_desc *desc;
+      struct grub_usb_desc_endp *endp;
 
       /* First just read the first 4 bytes of the configuration
 	 descriptor, after that it is known how many bytes really have
@@ -192,24 +193,27 @@ grub_usb_device_initialize (grub_usb_device_t dev)
 	    = (struct grub_usb_desc_if *) &data[pos];
 	  pos += dev->config[i].interf[currif].descif->length;
 
+    dev->config[i].interf[currif].descendp = grub_malloc (
+            dev->config[i].interf[currif].descif->endpointcnt *
+            sizeof(struct grub_usb_desc_endp));
+
+    j = 0;
 	  while (pos < config.totallen)
             {
               desc = (struct grub_usb_desc *)&data[pos];
-              if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT)
-                break;
-              if (!desc->length)
-                {
-                  err = GRUB_USB_ERR_BADDEVICE;
-                  goto fail;
-                }
-              pos += desc->length;
-            }
-
-	  /* Point to the first endpoint.  */
-	  dev->config[i].interf[currif].descendp
-	    = (struct grub_usb_desc_endp *) &data[pos];
-	  pos += (sizeof (struct grub_usb_desc_endp)
-		  * dev->config[i].interf[currif].descif->endpointcnt);
+              if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT) {
+                endp = (struct grub_usb_desc_endp *) &data[pos];
+                dev->config[i].interf[currif].descendp[j++] = endp;
+                pos += desc->length;
+              } else {
+                if (!desc->length)
+                  {
+                    err = GRUB_USB_ERR_BADDEVICE;
+                    goto fail;
+                  }
+                pos += desc->length;
+             }
+	  }
 	}
     }
 
@@ -217,8 +221,14 @@ grub_usb_device_initialize (grub_usb_device_t dev)
 
  fail:
 
-  for (i = 0; i < 8; i++)
+  for (i = 0; i < 8; i++) {
+    int currif;
+
+    for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
+      grub_free (dev->config[i].interf[currif].descendp);
+
     grub_free (dev->config[i].descconf);
+  }
 
   return err;
 }
diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
index a06cce302..136bf6ee4 100644
--- a/grub-core/bus/usb/usbhub.c
+++ b/grub-core/bus/usb/usbhub.c
@@ -82,8 +82,14 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
   if (i == GRUB_USBHUB_MAX_DEVICES)
     {
       grub_error (GRUB_ERR_IO, "can't assign address to USB device");
-      for (i = 0; i < 8; i++)
-        grub_free (dev->config[i].descconf);
+      for (i = 0; i < 8; i++) {
+	int currif;
+
+	for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
+	  grub_free (dev->config[i].interf[currif].descendp);
+
+	grub_free (dev->config[i].descconf);
+      }
       grub_free (dev);
       return NULL;
     }
@@ -96,8 +102,14 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
 			      i, 0, 0, NULL);
   if (err)
     {
-      for (i = 0; i < 8; i++)
-        grub_free (dev->config[i].descconf);
+      for (i = 0; i < 8; i++) {
+	int currif;
+
+	for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
+	  grub_free (dev->config[i].interf[currif].descendp);
+
+	grub_free (dev->config[i].descconf);
+      }
       grub_free (dev);
       return NULL;
     }
@@ -176,7 +188,7 @@ grub_usb_add_hub (grub_usb_device_t dev)
        i++)
     {
       struct grub_usb_desc_endp *endp = NULL;
-      endp = &dev->config[0].interf[0].descendp[i];
+      endp = dev->config[0].interf[0].descendp[i];
 
       if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
 	  == GRUB_USB_EP_INTERRUPT)
diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c
index 2c6d93fe6..55a657635 100644
--- a/grub-core/commands/usbtest.c
+++ b/grub-core/commands/usbtest.c
@@ -185,7 +185,7 @@ usb_iterate (grub_usb_device_t dev, void *data __attribute__ ((unused)))
       for (j = 0; j < interf->endpointcnt; j++)
 	{
 	  struct grub_usb_desc_endp *endp;
-	  endp = &dev->config[0].interf[i].descendp[j];
+	  endp = dev->config[0].interf[i].descendp[j];
 
 	  grub_printf ("Endpoint #%d: %s, max packed size: %d, transfer type: %s, latency: %d\n",
 		       endp->endp_addr & 15,
diff --git a/grub-core/disk/usbms.c b/grub-core/disk/usbms.c
index 380ca4c4c..64e1be8ff 100644
--- a/grub-core/disk/usbms.c
+++ b/grub-core/disk/usbms.c
@@ -184,7 +184,7 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
   for (j = 0; j < interf->endpointcnt; j++)
     {
       struct grub_usb_desc_endp *endp;
-      endp = &usbdev->config[0].interf[interfno].descendp[j];
+      endp = usbdev->config[0].interf[interfno].descendp[j];
 
       if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 	/* Bulk IN endpoint.  */
diff --git a/grub-core/term/usb_keyboard.c b/grub-core/term/usb_keyboard.c
index e67b8f785..d65e3474d 100644
--- a/grub-core/term/usb_keyboard.c
+++ b/grub-core/term/usb_keyboard.c
@@ -175,7 +175,7 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
   for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
        j++)
     {
-      endp = &usbdev->config[configno].interf[interfno].descendp[j];
+      endp = usbdev->config[configno].interf[interfno].descendp[j];
 
       if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
 	  == GRUB_USB_EP_INTERRUPT)
diff --git a/include/grub/usb.h b/include/grub/usb.h
index 512ae1dd0..0527a3e2b 100644
--- a/include/grub/usb.h
+++ b/include/grub/usb.h
@@ -149,7 +149,7 @@ struct grub_usb_interface
 {
   struct grub_usb_desc_if *descif;
 
-  struct grub_usb_desc_endp *descendp;
+  struct grub_usb_desc_endp **descendp;
 
   /* A driver is handling this interface. Do we need to support multiple drivers
      for single interface?
diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h
index aac5ab05a..bb2ab2e27 100644
--- a/include/grub/usbdesc.h
+++ b/include/grub/usbdesc.h
@@ -29,7 +29,8 @@ typedef enum {
   GRUB_USB_DESCRIPTOR_INTERFACE,
   GRUB_USB_DESCRIPTOR_ENDPOINT,
   GRUB_USB_DESCRIPTOR_DEBUG = 10,
-  GRUB_USB_DESCRIPTOR_HUB = 0x29
+  GRUB_USB_DESCRIPTOR_HUB = 0x29,
+  GRUB_USB_DESCRIPTOR_SS_ENDPOINT_COMPANION = 0x30
 } grub_usb_descriptor_t;
 
 struct grub_usb_desc
@@ -105,6 +106,14 @@ struct grub_usb_desc_endp
   grub_uint8_t interval;
 } GRUB_PACKED;
 
+struct grub_usb_desc_ssep {
+  grub_uint8_t  length;
+  grub_uint8_t  type;
+  grub_uint8_t  maxburst;
+  grub_uint8_t  attrib;
+  grub_uint16_t interval;
+} GRUB_PACKED;
+
 struct grub_usb_desc_str
 {
   grub_uint8_t length;
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 2/7] usb: Add enum for xHCI
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 3/7] usbtrans: Set default maximum packet size Patrick Rudolph
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

Will be used in future patches.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 include/grub/usb.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/grub/usb.h b/include/grub/usb.h
index 0527a3e2b..f65ac4f5d 100644
--- a/include/grub/usb.h
+++ b/include/grub/usb.h
@@ -47,7 +47,8 @@ typedef enum
     GRUB_USB_SPEED_NONE,
     GRUB_USB_SPEED_LOW,
     GRUB_USB_SPEED_FULL,
-    GRUB_USB_SPEED_HIGH
+    GRUB_USB_SPEED_HIGH,
+    GRUB_USB_SPEED_SUPER
   } grub_usb_speed_t;
 
 typedef int (*grub_usb_iterate_hook_t) (grub_usb_device_t dev, void *data);
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 3/7] usbtrans: Set default maximum packet size
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 2/7] usb: Add enum for xHCI Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 4/7] grub-core/bus/usb: Add function pointer for attach/detach events Patrick Rudolph
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

Set the maximum packet size to 512 for SuperSpeed devices.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 grub-core/bus/usb/usbtrans.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/bus/usb/usbtrans.c b/grub-core/bus/usb/usbtrans.c
index 85f081fff..72eb9b598 100644
--- a/grub-core/bus/usb/usbtrans.c
+++ b/grub-core/bus/usb/usbtrans.c
@@ -128,8 +128,12 @@ grub_usb_control_msg (grub_usb_device_t dev,
   setupdata_addr = grub_dma_get_phys (setupdata_chunk);
 
   /* Determine the maximum packet size.  */
-  if (dev->descdev.maxsize0)
+  if (dev->descdev.maxsize0 && dev->speed != GRUB_USB_SPEED_SUPER)
     max = dev->descdev.maxsize0;
+  else if (dev->descdev.maxsize0 && dev->speed == GRUB_USB_SPEED_SUPER)
+    max = 1UL << dev->descdev.maxsize0;
+  else if (dev->speed == GRUB_USB_SPEED_SUPER)
+    max = 512;
   else
     max = 64;
 
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 4/7] grub-core/bus/usb: Add function pointer for attach/detach events
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
                   ` (2 preceding siblings ...)
  2020-12-07  7:41 ` [PATCH v2 3/7] usbtrans: Set default maximum packet size Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 5/7] grub-core/bus/usb/usbhub: Add new private fields for xHCI controller Patrick Rudolph
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

The xHCI code needs to be called for attaching or detaching a device.
Introduce two functions pointers and call it from the USB hub code.

Will be used in future commits, currently this doesn't change any functionality.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 grub-core/bus/usb/ehci.c   |  2 ++
 grub-core/bus/usb/ohci.c   |  2 ++
 grub-core/bus/usb/uhci.c   |  2 ++
 grub-core/bus/usb/usbhub.c | 19 +++++++++++++++++++
 include/grub/usb.h         |  4 ++++
 5 files changed, 29 insertions(+)

diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c
index d966fc210..6b3f2f87a 100644
--- a/grub-core/bus/usb/ehci.c
+++ b/grub-core/bus/usb/ehci.c
@@ -1812,6 +1812,8 @@ static struct grub_usb_controller_dev usb_controller = {
   .hubports = grub_ehci_hubports,
   .portstatus = grub_ehci_portstatus,
   .detect_dev = grub_ehci_detect_dev,
+  .attach_dev = NULL,
+  .detach_dev = NULL,
   /* estimated max. count of TDs for one bulk transfer */
   .max_bulk_tds = GRUB_EHCI_N_TD * 3 / 4 
 };
diff --git a/grub-core/bus/usb/ohci.c b/grub-core/bus/usb/ohci.c
index f0be533d4..44c7e94a9 100644
--- a/grub-core/bus/usb/ohci.c
+++ b/grub-core/bus/usb/ohci.c
@@ -1440,6 +1440,8 @@ static struct grub_usb_controller_dev usb_controller =
   .hubports = grub_ohci_hubports,
   .portstatus = grub_ohci_portstatus,
   .detect_dev = grub_ohci_detect_dev,
+  .attach_dev = NULL,
+  .detach_dev = NULL,
   /* estimated max. count of TDs for one bulk transfer */
   .max_bulk_tds = GRUB_OHCI_TDS * 3 / 4
 };
diff --git a/grub-core/bus/usb/uhci.c b/grub-core/bus/usb/uhci.c
index 7c5811fd6..4143d24fe 100644
--- a/grub-core/bus/usb/uhci.c
+++ b/grub-core/bus/usb/uhci.c
@@ -845,6 +845,8 @@ static struct grub_usb_controller_dev usb_controller =
   .hubports = grub_uhci_hubports,
   .portstatus = grub_uhci_portstatus,
   .detect_dev = grub_uhci_detect_dev,
+  .attach_dev = NULL,
+  .detach_dev = NULL,
   /* estimated max. count of TDs for one bulk transfer */
   .max_bulk_tds = N_TD * 3 / 4
 };
diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
index 136bf6ee4..df1913aee 100644
--- a/grub-core/bus/usb/usbhub.c
+++ b/grub-core/bus/usb/usbhub.c
@@ -66,6 +66,15 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
   dev->split_hubport = split_hubport;
   dev->split_hubaddr = split_hubaddr;
 
+  if (controller->dev->attach_dev) {
+    err = controller->dev->attach_dev (controller, dev);
+    if (err)
+      {
+	grub_free (dev);
+	return NULL;
+      }
+  }
+
   err = grub_usb_device_initialize (dev);
   if (err)
     {
@@ -405,6 +414,8 @@ static void
 detach_device (grub_usb_device_t dev)
 {
   unsigned i;
+  grub_usb_err_t err;
+
   int k;
   if (!dev)
     return;
@@ -425,6 +436,14 @@ detach_device (grub_usb_device_t dev)
 	  if (inter && inter->detach_hook)
 	    inter->detach_hook (dev, i, k);
 	}
+  if (dev->controller.dev->detach_dev) {
+    err = dev->controller.dev->detach_dev (&dev->controller, dev);
+    if (err)
+      {
+	// XXX
+      }
+  }
+
   grub_usb_devs[dev->addr] = 0;
 }
 
diff --git a/include/grub/usb.h b/include/grub/usb.h
index f65ac4f5d..5c5fb5ec4 100644
--- a/include/grub/usb.h
+++ b/include/grub/usb.h
@@ -122,6 +122,10 @@ struct grub_usb_controller_dev
 
   grub_usb_speed_t (*detect_dev) (grub_usb_controller_t dev, int port, int *changed);
 
+  grub_usb_err_t (*attach_dev) (grub_usb_controller_t ctrl, grub_usb_device_t dev);
+
+  grub_usb_err_t (*detach_dev) (grub_usb_controller_t ctrl, grub_usb_device_t dev);
+
   /* Per controller flag - port reset pending, don't do another reset */
   grub_uint64_t pending_reset;
 
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 5/7] grub-core/bus/usb/usbhub: Add new private fields for xHCI controller
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
                   ` (3 preceding siblings ...)
  2020-12-07  7:41 ` [PATCH v2 4/7] grub-core/bus/usb: Add function pointer for attach/detach events Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 6/7] grub-core/bus/usb: Add xhci support Patrick Rudolph
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

Store the root port number, the route, consisting out of the port ID
in each nibble, and a pointer to driver private data.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 grub-core/bus/usb/usbhub.c | 14 ++++++++++----
 include/grub/usb.h         |  5 +++++
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
index df1913aee..63bee1d1a 100644
--- a/grub-core/bus/usb/usbhub.c
+++ b/grub-core/bus/usb/usbhub.c
@@ -49,7 +49,9 @@ static grub_usb_controller_dev_t grub_usb_list;
 static grub_usb_device_t
 grub_usb_hub_add_dev (grub_usb_controller_t controller,
                       grub_usb_speed_t speed,
-                      int split_hubport, int split_hubaddr)
+                      int split_hubport, int split_hubaddr,
+                      int root_portno,
+                      grub_uint32_t route)
 {
   grub_usb_device_t dev;
   int i;
@@ -65,6 +67,8 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
   dev->speed = speed;
   dev->split_hubport = split_hubport;
   dev->split_hubaddr = split_hubaddr;
+  dev->root_port = root_portno;
+  dev->route = route;
 
   if (controller->dev->attach_dev) {
     err = controller->dev->attach_dev (controller, dev);
@@ -245,7 +249,7 @@ attach_root_port (struct grub_usb_hub *hub, int portno,
      and full/low speed device connected to OHCI/UHCI needs not
      transaction translation - e.g. hubport and hubaddr should be
      always none (zero) for any device connected to any root hub. */
-  dev = grub_usb_hub_add_dev (hub->controller, speed, 0, 0);
+  dev = grub_usb_hub_add_dev (hub->controller, speed, 0, 0, portno, 0);
   hub->controller->dev->pending_reset = 0;
   npending--;
   if (! dev)
@@ -673,10 +677,12 @@ poll_nonroot_hub (grub_usb_device_t dev)
 		    split_hubport = dev->split_hubport;
 		    split_hubaddr = dev->split_hubaddr;
 		  }
-		
+
+
 	      /* Add the device and assign a device address to it.  */
 	      next_dev = grub_usb_hub_add_dev (&dev->controller, speed,
-					       split_hubport, split_hubaddr);
+					       split_hubport, split_hubaddr, dev->root_port,
+					       dev->route << 4 | (i & 0xf));
 	      if (dev->controller.dev->pending_reset)
 		{
 		  dev->controller.dev->pending_reset = 0;
diff --git a/include/grub/usb.h b/include/grub/usb.h
index 5c5fb5ec4..5a3325b7d 100644
--- a/include/grub/usb.h
+++ b/include/grub/usb.h
@@ -233,6 +233,11 @@ struct grub_usb_device
   int split_hubport;
 
   int split_hubaddr;
+
+  /* xHCI specific information */
+  int root_port;
+  grub_uint32_t route;
+  void *xhci_priv;
 };
 
 \f
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 6/7] grub-core/bus/usb: Add xhci support
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
                   ` (4 preceding siblings ...)
  2020-12-07  7:41 ` [PATCH v2 5/7] grub-core/bus/usb/usbhub: Add new private fields for xHCI controller Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2020-12-07  7:41 ` [PATCH v2 7/7] grub-core/bus/usb/usbhub: Add xHCI non root hub support Patrick Rudolph
  2021-02-19 18:11 ` [PATCH v2 0/7] Add xHCI USB support Glenn Washburn
  7 siblings, 0 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph, sylv

Add support for xHCI USB controllers.
The code is based on seabios implementation, but has been heavily
modified to match grubs internals.

Changes done in version 2:
* Code cleanup
* Code style fixes
* Don't leak memory buffers
* Compile without warnings
* Add more defines
* Add more helper functions
* Don't assume a 1:1 virtual to physical mapping
* Flush cachelines after writing buffers
* Don't use hardcoded page size
* Proper scratchpad register setup
* xHCI bios ownership handoff

Changes done in version 3:
* Fixed a race condition detecting events, which doesn't appear on
  qemu based xHCI controllers
* Don't accidently disable USB3.0 devices after first command
* Support arbitrary protocol speed IDs
* Coding style cleanup

Tested:
* Qemu system x86_64
  * virtual USB HID keyboard (usb-kbd)
  * virtual USB HID mass storage (usb-storage)
* init Supermicro X11SSH-F
  * iKVM HID keyboard
  * USB3 HID mass storage (controller root port)
  * USB HID keyboard

TODO:
  * Test on more hardware
  * Test on USB3 hubs
  * Support for USB 3.1 and USB 3.2 controllers

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Signed-off-by: sylv <sylv@sylv.io>
---
 Makefile.am                  |    2 +-
 grub-core/Makefile.core.def  |    7 +
 grub-core/bus/usb/xhci-pci.c |  195 +++
 grub-core/bus/usb/xhci.c     | 2496 ++++++++++++++++++++++++++++++++++
 include/grub/usb.h           |    4 +
 5 files changed, 2703 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/bus/usb/xhci-pci.c
 create mode 100644 grub-core/bus/usb/xhci.c

diff --git a/Makefile.am b/Makefile.am
index bf9c1ba64..81b9d1092 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -425,7 +425,7 @@ if COND_i386_coreboot
 FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst)
 default_payload.elf: grub-mkstandalone grub-mkimage FORCE
 	test -f $@ && rm $@ || true
-	pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg
+	pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata xhci ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg
 endif
 
 endif
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index b5f47fc41..d5f550419 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -644,6 +644,13 @@ module = {
   enable = arm_coreboot;
 };
 
+module = {
+  name = xhci;
+  common = bus/usb/xhci.c;
+  pci = bus/usb/xhci-pci.c;
+  enable = pci;
+};
+
 module = {
   name = pci;
   common = bus/pci.c;
diff --git a/grub-core/bus/usb/xhci-pci.c b/grub-core/bus/usb/xhci-pci.c
new file mode 100644
index 000000000..a5bd3c97d
--- /dev/null
+++ b/grub-core/bus/usb/xhci-pci.c
@@ -0,0 +1,195 @@
+/* xhci.c - XHCI Support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 9elements Cyber Security
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/pci.h>
+#include <grub/cpu/pci.h>
+#include <grub/cs5536.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/usb.h>
+
+#define GRUB_XHCI_PCI_SBRN_REG  0x60
+#define GRUB_XHCI_ADDR_MEM_MASK	(~0xff)
+
+/* USBLEGSUP bits and related OS OWNED byte offset */
+enum
+{
+  GRUB_XHCI_BIOS_OWNED = (1 << 16),
+  GRUB_XHCI_OS_OWNED = (1 << 24)
+};
+
+/* PCI iteration function... */
+static int
+grub_xhci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
+		    void *data __attribute__ ((unused)))
+{
+  volatile grub_uint32_t *regs;
+  grub_uint32_t base, base_h;
+  grub_uint32_t eecp_offset;
+  grub_uint32_t usblegsup = 0;
+  grub_uint64_t maxtime;
+  grub_uint32_t interf;
+  grub_uint32_t subclass;
+  grub_uint32_t class;
+  grub_uint8_t release;
+  grub_uint32_t class_code;
+
+  grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: begin\n");
+
+  if (pciid == GRUB_CS5536_PCIID)
+    {
+	grub_dprintf ("xhci", "CS5536 not supported\n");
+	return 0;
+    }
+  else
+    {
+      grub_pci_address_t addr;
+      addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
+      class_code = grub_pci_read (addr) >> 8;
+      interf = class_code & 0xFF;
+      subclass = (class_code >> 8) & 0xFF;
+      class = class_code >> 16;
+
+      /* If this is not an XHCI controller, just return.  */
+      if (class != 0x0c || subclass != 0x03 || interf != 0x30)
+	return 0;
+
+      grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: class OK\n");
+
+      /* Check Serial Bus Release Number */
+      addr = grub_pci_make_address (dev, GRUB_XHCI_PCI_SBRN_REG);
+      release = grub_pci_read_byte (addr);
+      if (release != 0x30)
+	{
+	  grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: Wrong SBRN: %0x\n",
+			release);
+	  return 0;
+	}
+      grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: bus rev. num. OK\n");
+
+      /* Determine XHCI XHCC registers base address.  */
+      addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+      base = grub_pci_read (addr);
+      addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1);
+      base_h = grub_pci_read (addr);
+      /* Stop if registers are mapped above 4G - GRUB does not currently
+       * work with registers mapped above 4G */
+      if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32)
+	  && (base_h != 0))
+	{
+	  grub_dprintf ("xhci",
+			"XHCI grub_xhci_pci_iter: registers above 4G are not supported\n");
+	  return 0;
+	}
+      base &= GRUB_PCI_ADDR_MEM_MASK;
+      if (!base)
+	{
+	  grub_dprintf ("xhci",
+			"XHCI: XHCI is not mapped\n");
+	  return 0;
+	}
+
+      /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */
+      addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
+      grub_pci_write_word(addr,
+			  GRUB_PCI_COMMAND_MEM_ENABLED
+			  | GRUB_PCI_COMMAND_BUS_MASTER
+			  | grub_pci_read_word(addr));
+
+      grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: 32-bit XHCI OK\n");
+    }
+
+  grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: iobase of XHCC: %08x\n",
+		(base & GRUB_XHCI_ADDR_MEM_MASK));
+
+  regs = grub_pci_device_map_range (dev,
+				    (base & GRUB_XHCI_ADDR_MEM_MASK),
+				    0x100);
+
+  /* Is there EECP ? */
+  eecp_offset = (grub_le_to_cpu32 (regs[2]) >> 8) & 0xff;
+
+    /* Determine and change ownership. */
+  /* EECP offset valid in HCCPARAMS */
+  /* Ownership can be changed via EECP only */
+  if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40)
+    {
+      grub_pci_address_t pciaddr_eecp;
+      pciaddr_eecp = grub_pci_make_address (dev, eecp_offset);
+
+      usblegsup = grub_pci_read (pciaddr_eecp);
+      if (usblegsup & GRUB_XHCI_BIOS_OWNED)
+	{
+	  grub_boot_time ("Taking ownership of XHCI controller");
+	  grub_dprintf ("xhci",
+			"XHCI grub_xhci_pci_iter: XHCI owned by: BIOS\n");
+	  /* Ownership change - set OS_OWNED bit */
+	  grub_pci_write (pciaddr_eecp, usblegsup | GRUB_XHCI_OS_OWNED);
+	  /* Ensure PCI register is written */
+	  grub_pci_read (pciaddr_eecp);
+
+	  /* Wait for finish of ownership change, XHCI specification
+	   * doesn't say how long it can take... */
+	  maxtime = grub_get_time_ms () + 1000;
+	  while ((grub_pci_read (pciaddr_eecp) & GRUB_XHCI_BIOS_OWNED)
+		 && (grub_get_time_ms () < maxtime));
+	  if (grub_pci_read (pciaddr_eecp) & GRUB_XHCI_BIOS_OWNED)
+	    {
+	      grub_dprintf ("xhci",
+			    "XHCI grub_xhci_pci_iter: XHCI change ownership timeout");
+	      /* Change ownership in "hard way" - reset BIOS ownership */
+	      grub_pci_write (pciaddr_eecp, GRUB_XHCI_OS_OWNED);
+	      /* Ensure PCI register is written */
+	      grub_pci_read (pciaddr_eecp);
+	    }
+	}
+      else if (usblegsup & GRUB_XHCI_OS_OWNED)
+	/* XXX: What to do in this case - nothing ? Can it happen ? */
+	grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: XHCI owned by: OS\n");
+      else
+	{
+	  grub_dprintf ("xhci",
+			"XHCI grub_Xhci_pci_iter: XHCI owned by: NONE\n");
+	  /* XXX: What to do in this case ? Can it happen ?
+	   * Is code below correct ? */
+	  /* Ownership change - set OS_OWNED bit */
+	  grub_pci_write (pciaddr_eecp, GRUB_XHCI_OS_OWNED);
+	  /* Ensure PCI register is written */
+	  grub_pci_read (pciaddr_eecp);
+	}
+
+      /* Disable SMI, just to be sure.  */
+      pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4);
+      grub_pci_write (pciaddr_eecp, 0);
+      /* Ensure PCI register is written */
+      grub_pci_read (pciaddr_eecp);
+    }
+
+  grub_dprintf ("xhci", "inithw: XHCI grub_xhci_pci_iter: ownership OK\n");
+
+  grub_xhci_init_device (regs);
+  return 0;
+}
+
+void
+grub_xhci_pci_scan (void)
+{
+  grub_pci_iterate (grub_xhci_pci_iter, NULL);
+}
diff --git a/grub-core/bus/usb/xhci.c b/grub-core/bus/usb/xhci.c
new file mode 100644
index 000000000..f4591ffb5
--- /dev/null
+++ b/grub-core/bus/usb/xhci.c
@@ -0,0 +1,2496 @@
+/* xhci.c - XHCI Support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 9elements Cyber Security
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Big parts of this software are inspired by seabios XHCI implementation
+ * Released under LGPLv3. Credits to:
+ *
+ * Copyright (C) 2013  Gerd Hoffmann <kraxel@redhat.com>
+ * Copyright (C) 2014  Kevin O'Connor <kevin@koconnor.net>
+ */
+
+#include <grub/dl.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/usb.h>
+#include <grub/usbtrans.h>
+#include <grub/misc.h>
+#include <grub/time.h>
+#include <grub/loader.h>
+#include <grub/disk.h>
+#include <grub/dma.h>
+#include <grub/cache.h>
+#include <grub/i386/cpuid.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* This simple GRUB implementation of XHCI driver */
+/* Based on the specification
+ * "eXtensible Host Controller Interface for Universal Serial Bus" Revision 1.2
+ */
+
+
+#define xhci_get_field(data, field)	     \
+    (((data) >> field##_SHIFT) & field##_MASK)
+#define XHCI_PORTSC_PLS_MASK     0xf
+#define XHCI_PORTSC_PLS_SHIFT    5
+#define XHCI_PORTSC_SPEED_MASK   0xf
+#define XHCI_PORTSC_SPEED_SHIFT  10
+
+enum
+{
+  XHCI_USB_FULLSPEED = 1,
+  XHCI_USB_LOWSPEED,
+  XHCI_USB_HIGHSPEED,
+  XHCI_USB_SUPERSPEED
+};
+
+/* Chapter 5.3 Host Controller Capability Registers */
+struct grub_xhci_caps {
+    grub_uint8_t  caplength;
+    grub_uint8_t  reserved_01;
+    grub_uint16_t hciversion;
+    grub_uint32_t hcsparams1;
+    grub_uint32_t hcsparams2;
+    grub_uint32_t hcsparams3;
+    grub_uint32_t hccparams;
+    grub_uint32_t dboff;
+    grub_uint32_t rtsoff;
+    grub_uint32_t hccparams2;
+} GRUB_PACKED;
+
+/* extended capabilities */
+struct grub_xhci_xcap {
+    grub_uint32_t cap;
+    grub_uint32_t data[];
+} GRUB_PACKED;
+
+#define XHCI_CAP_LEGACY_SUPPORT 1
+#define XHCI_CAP_SUPPORTED_PROTOCOL 2
+
+struct xhci_portmap {
+    grub_uint8_t start;
+    grub_uint8_t count;
+} GRUB_PACKED;
+
+struct grub_xhci_op {
+    grub_uint32_t usbcmd;
+    grub_uint32_t usbsts;
+    grub_uint32_t pagesize;
+    grub_uint32_t reserved_01[2];
+    grub_uint32_t dnctl;
+    grub_uint32_t crcr_low;
+    grub_uint32_t crcr_high;
+    grub_uint32_t reserved_02[4];
+    grub_uint32_t dcbaap_low;
+    grub_uint32_t dcbaap_high;
+    grub_uint32_t config;
+} GRUB_PACKED;
+
+enum
+{
+  GRUB_XHCI_CMD_RS = (1<<0),
+  GRUB_XHCI_CMD_HCRST = (1<<1),
+  GRUB_XHCI_CMD_INTE = (1<<2),
+  GRUB_XHCI_CMD_HSEE = (1<<3),
+  GRUB_XHCI_CMD_LHCRST = (1<<7),
+  GRUB_XHCI_CMD_CSS = (1<<8),
+  GRUB_XHCI_CMD_CRS = (1<<9),
+  GRUB_XHCI_CMD_EWE = (1<<10),
+  GRUB_XHCI_CMD_EU3S = (1<<11)
+};
+
+enum
+{
+  GRUB_XHCI_STS_HCH = (1<<0),
+  GRUB_XHCI_STS_HSE = (1<<2),
+  GRUB_XHCI_STS_EINT = (1<<3),
+  GRUB_XHCI_STS_PCD = (1<<4),
+  GRUB_XHCI_STS_SSS = (1<<8),
+  GRUB_XHCI_STS_RSS = (1<<9),
+  GRUB_XHCI_STS_SRE = (1<<10),
+  GRUB_XHCI_STS_CNR = (1<<11),
+  GRUB_XHCI_STS_HCE = (1<<12)
+};
+
+
+/* Port Registers Offset */
+#define GRUB_XHCI_PR_OFFSET 0x400
+/* Interrupter Registers Offset */
+#define GRUB_XHCI_IR_OFFSET 0x20
+
+/* Port Status and Control registers offsets */
+
+/* Chapter 6 Data Structures */
+#define ALIGN_SPBA 64
+#define ALIGN_DCBAA 64
+#define ALIGN_CMD_RING_SEG 64
+#define ALIGN_EVT_RING_SEG 64
+#define ALIGN_EVT_RING_TABLE 64
+#define ALIGN_TRB 16
+#define ALIGN_INCTX 64
+#define ALIGN_SLOTCTX 32
+
+#define BOUNDARY_RING 0x10000
+
+enum
+{
+  GRUB_XHCI_PORTSC_CCS = (1<<0),
+  GRUB_XHCI_PORTSC_PED = (1<<1),
+  GRUB_XHCI_PORTSC_OCA = (1<<3),
+  GRUB_XHCI_PORTSC_PR = (1<<4),
+  GRUB_XHCI_PORTSC_PP = (1<<9),
+  GRUB_XHCI_PORTSC_SPEED_FULL = (1<<10),
+  GRUB_XHCI_PORTSC_SPEED_LOW = (2<<10),
+  GRUB_XHCI_PORTSC_SPEED_HIGH = (3<<10),
+  GRUB_XHCI_PORTSC_SPEED_SUPER = (4<<10),
+  GRUB_XHCI_PORTSC_LWS = (1<<16),
+  GRUB_XHCI_PORTSC_CSC = (1<<17),
+  GRUB_XHCI_PORTSC_PEC = (1<<18),
+  GRUB_XHCI_PORTSC_WRC = (1<<19),
+  GRUB_XHCI_PORTSC_OCC = (1<<20),
+  GRUB_XHCI_PORTSC_PRC = (1<<21),
+  GRUB_XHCI_PORTSC_PLC = (1<<22),
+  GRUB_XHCI_PORTSC_CEC = (1<<23),
+  GRUB_XHCI_PORTSC_CAS = (1<<24),
+  GRUB_XHCI_PORTSC_WCE = (1<<25),
+  GRUB_XHCI_PORTSC_WDE = (1<<26),
+  GRUB_XHCI_PORTSC_WOE = (1<<27),
+  GRUB_XHCI_PORTSC_DR = (1<<30),
+  GRUB_XHCI_PORTSC_WPR = (1<<31)
+};
+
+/* XHCI memory data structs */
+#define GRUB_XHCI_MAX_ENDPOINTS 32
+
+#define GRUB_XHCI_RING_ITEMS 128
+#define GRUB_XHCI_RING_SIZE (GRUB_XHCI_RING_ITEMS*sizeof(struct grub_xhci_trb))
+/*
+ *  xhci_ring structs are allocated with XHCI_RING_SIZE alignment,
+ *  then we can get it from a trb pointer (provided by evt ring).
+ */
+#define XHCI_RING(_trb)	  \
+    ((struct grub_xhci_ring*)((grub_uint32_t)(_trb) & ~(GRUB_XHCI_RING_SIZE-1)))
+
+/* slot context */
+struct grub_xhci_slotctx {
+  grub_uint32_t ctx[4];
+  grub_uint32_t reserved_01[4];
+} GRUB_PACKED;
+
+/* endpoint context */
+struct grub_xhci_epctx {
+  grub_uint32_t ctx[2];
+  grub_uint32_t deq_low;
+  grub_uint32_t deq_high;
+  grub_uint32_t length;
+  grub_uint32_t reserved_01[3];
+} GRUB_PACKED;
+
+/* device context array element */
+struct grub_xhci_devlist {
+  grub_uint32_t ptr_low;
+  grub_uint32_t ptr_high;
+} GRUB_PACKED;
+
+/* input context */
+struct grub_xhci_inctx {
+  grub_uint32_t del;
+  grub_uint32_t add;
+  grub_uint32_t reserved_01[6];
+} GRUB_PACKED;
+
+/* transfer block (ring element) */
+struct grub_xhci_trb {
+  grub_uint32_t ptr_low;
+  grub_uint32_t ptr_high;
+  grub_uint32_t status;
+  grub_uint32_t control;
+} GRUB_PACKED;
+
+#define TRB_C	       (1<<0)
+#define TRB_TYPE_SHIFT	  10
+#define TRB_TYPE_MASK       0x3f
+#define TRB_TYPE(t)	 (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
+
+#define TRB_EV_ED	   (1<<2)
+
+#define TRB_TR_ENT	  (1<<1)
+#define TRB_TR_ISP	  (1<<2)
+#define TRB_TR_NS	   (1<<3)
+#define TRB_TR_CH	   (1<<4)
+#define TRB_TR_IOC	  (1<<5)
+#define TRB_TR_IDT	  (1<<6)
+#define TRB_TR_TBC_SHIFT	7
+#define TRB_TR_TBC_MASK     0x3
+#define TRB_TR_BEI	  (1<<9)
+#define TRB_TR_TLBPC_SHIFT      16
+#define TRB_TR_TLBPC_MASK   0xf
+#define TRB_TR_FRAMEID_SHIFT    20
+#define TRB_TR_FRAMEID_MASK 0x7ff
+#define TRB_TR_SIA	  (1<<31)
+
+#define TRB_TR_DIR	  (1<<16)
+
+#define TRB_CR_SLOTID_SHIFT     24
+#define TRB_CR_SLOTID_MASK  0xff
+#define TRB_CR_EPID_SHIFT       16
+#define TRB_CR_EPID_MASK    0x1f
+
+#define TRB_CR_BSR	  (1<<9)
+#define TRB_CR_DC	   (1<<9)
+
+#define TRB_LK_TC	   (1<<1)
+
+#define TRB_INTR_SHIFT	  22
+#define TRB_INTR_MASK       0x3ff
+#define TRB_INTR(t)	 (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
+
+typedef enum TRBType {
+    TRB_RESERVED = 0,
+    TR_NORMAL,
+    TR_SETUP,
+    TR_DATA,
+    TR_STATUS,
+    TR_ISOCH,
+    TR_LINK,
+    TR_EVDATA,
+    TR_NOOP,
+    CR_ENABLE_SLOT,
+    CR_DISABLE_SLOT,
+    CR_ADDRESS_DEVICE,
+    CR_CONFIGURE_ENDPOINT,
+    CR_EVALUATE_CONTEXT,
+    CR_RESET_ENDPOINT,
+    CR_STOP_ENDPOINT,
+    CR_SET_TR_DEQUEUE,
+    CR_RESET_DEVICE,
+    CR_FORCE_EVENT,
+    CR_NEGOTIATE_BW,
+    CR_SET_LATENCY_TOLERANCE,
+    CR_GET_PORT_BANDWIDTH,
+    CR_FORCE_HEADER,
+    CR_NOOP,
+    ER_TRANSFER = 32,
+    ER_COMMAND_COMPLETE,
+    ER_PORT_STATUS_CHANGE,
+    ER_BANDWIDTH_REQUEST,
+    ER_DOORBELL,
+    ER_HOST_CONTROLLER,
+    ER_DEVICE_NOTIFICATION,
+    ER_MFINDEX_WRAP,
+} TRBType;
+
+typedef enum TRBCCode {
+    CC_INVALID = 0,
+    CC_SUCCESS,
+    CC_DATA_BUFFER_ERROR,
+    CC_BABBLE_DETECTED,
+    CC_USB_TRANSACTION_ERROR,
+    CC_TRB_ERROR,
+    CC_STALL_ERROR,
+    CC_RESOURCE_ERROR,
+    CC_BANDWIDTH_ERROR,
+    CC_NO_SLOTS_ERROR,
+    CC_INVALID_STREAM_TYPE_ERROR,
+    CC_SLOT_NOT_ENABLED_ERROR,
+    CC_EP_NOT_ENABLED_ERROR,
+    CC_SHORT_PACKET,
+    CC_RING_UNDERRUN,
+    CC_RING_OVERRUN,
+    CC_VF_ER_FULL,
+    CC_PARAMETER_ERROR,
+    CC_BANDWIDTH_OVERRUN,
+    CC_CONTEXT_STATE_ERROR,
+    CC_NO_PING_RESPONSE_ERROR,
+    CC_EVENT_RING_FULL_ERROR,
+    CC_INCOMPATIBLE_DEVICE_ERROR,
+    CC_MISSED_SERVICE_ERROR,
+    CC_COMMAND_RING_STOPPED,
+    CC_COMMAND_ABORTED,
+    CC_STOPPED,
+    CC_STOPPED_LENGTH_INVALID,
+    CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
+    CC_ISOCH_BUFFER_OVERRUN = 31,
+    CC_EVENT_LOST_ERROR,
+    CC_UNDEFINED_ERROR,
+    CC_INVALID_STREAM_ID_ERROR,
+    CC_SECONDARY_BANDWIDTH_ERROR,
+    CC_SPLIT_TRANSACTION_ERROR
+} TRBCCode;
+
+enum {
+    PLS_U0	      =  0,
+    PLS_U1	      =  1,
+    PLS_U2	      =  2,
+    PLS_U3	      =  3,
+    PLS_DISABLED	=  4,
+    PLS_RX_DETECT       =  5,
+    PLS_INACTIVE	=  6,
+    PLS_POLLING	 =  7,
+    PLS_RECOVERY	=  8,
+    PLS_HOT_RESET       =  9,
+    PLS_COMPILANCE_MODE = 10,
+    PLS_TEST_MODE       = 11,
+    PLS_RESUME	  = 15,
+};
+
+/* event ring segment */
+struct grub_xhci_er_seg {
+  grub_uint32_t ptr_low;
+  grub_uint32_t ptr_high;
+  grub_uint32_t size;
+  grub_uint32_t reserved_01;
+} GRUB_PACKED;
+
+struct grub_xhci_ring {
+    struct grub_xhci_trb      ring[GRUB_XHCI_RING_ITEMS];
+    struct grub_xhci_trb      evt;
+    grub_uint32_t	     eidx;
+    grub_uint32_t	     nidx;
+    grub_uint32_t	     cs;
+};
+
+/* port registers */
+struct grub_xhci_pr {
+    grub_uint32_t portsc;
+    grub_uint32_t portpmsc;
+    grub_uint32_t portli;
+    grub_uint32_t reserved_01;
+} GRUB_PACKED;
+
+/* doorbell registers */
+struct grub_xhci_db {
+    grub_uint32_t doorbell;
+} GRUB_PACKED;
+
+/* runtime registers */
+struct grub_xhci_rts {
+    grub_uint32_t mfindex;
+} GRUB_PACKED;
+
+/* interrupter registers */
+struct grub_xhci_ir {
+    grub_uint32_t iman;
+    grub_uint32_t imod;
+    grub_uint32_t erstsz;
+    grub_uint32_t reserved_01;
+    grub_uint32_t erstba_low;
+    grub_uint32_t erstba_high;
+    grub_uint32_t erdp_low;
+    grub_uint32_t erdp_high;
+} GRUB_PACKED;
+
+struct grub_xhci_psid {
+	grub_uint8_t id;
+	grub_uint8_t psie;
+	grub_uint16_t psim;
+	grub_uint64_t bitrate;
+	grub_usb_speed_t grub_usb_speed;
+};
+
+struct grub_xhci_psids {
+	grub_uint8_t major;
+	grub_uint8_t minor;
+	struct grub_xhci_psid psids[16];
+};
+
+struct grub_xhci
+{
+  grub_uint8_t shutdown; /* 1 if preparing shutdown of controller */
+  /* xhci registers */
+  volatile struct grub_xhci_caps *caps;	/* Capability registers */
+  volatile struct grub_xhci_op *op;	/* Operational registers */
+  volatile struct grub_xhci_pr *pr;	/* Port Registers */
+  volatile struct grub_xhci_db *db;	/* doorbell */
+  volatile struct grub_xhci_ir *ir;	/* Interrupt Registers */
+  /* devinfo */
+  grub_uint32_t xcap;
+  grub_uint32_t ports;
+  grub_uint32_t slots;
+  grub_uint8_t flag64;
+  grub_uint16_t spb;
+  grub_uint32_t pagesize;
+  struct xhci_portmap usb2;
+  struct xhci_portmap usb3;
+  struct grub_xhci_psids *psids;
+  /* xhci data structures */
+  struct grub_pci_dma_chunk *devs_dma;
+  volatile struct grub_xhci_devlist *devs;
+  struct grub_pci_dma_chunk *cmds_dma;
+  volatile struct grub_xhci_ring *cmds;
+  struct grub_pci_dma_chunk *evts_dma;
+  volatile struct grub_xhci_ring *evts;
+  struct grub_pci_dma_chunk *eseg_dma;
+  volatile struct grub_xhci_er_seg *eseg;
+  struct grub_pci_dma_chunk *spba_dma;
+  struct grub_pci_dma_chunk *spad_dma;
+
+  struct grub_xhci *next;
+};
+
+struct grub_xhci_priv {
+  grub_uint8_t                    slotid;
+  grub_uint32_t                   max_packet;
+  struct grub_pci_dma_chunk       *enpoint_trbs_dma[32];
+  volatile struct grub_xhci_ring  *enpoint_trbs[32];
+  struct grub_pci_dma_chunk       *slotctx_dma;
+};
+
+struct grub_xhci_port {
+  grub_uint32_t portsc;
+  grub_uint32_t portpmsc;
+  grub_uint32_t portli;
+  grub_uint32_t reserved_01;
+};
+
+struct grub_xhci_transfer_controller_data {
+  grub_uint32_t transfer_size;
+};
+
+static struct grub_xhci *xhci;
+
+/****************************************************************
+ * general access functions
+ ****************************************************************/
+
+static inline void
+grub_xhci_write32(volatile void *addr, grub_uint32_t val) {
+    *(volatile grub_uint32_t *)addr = val;
+}
+static inline void
+grub_xhci_write16(volatile void *addr, grub_uint16_t val) {
+    *(volatile grub_uint16_t *)addr = val;
+}
+static inline void
+grub_xhci_write8(void *addr, grub_uint8_t val) {
+    *(volatile grub_uint8_t *)addr = val;
+}
+
+static inline grub_uint32_t
+grub_xhci_read32(volatile void *addr) {
+  return grub_le_to_cpu32 (*((volatile grub_uint32_t *)addr));
+}
+
+static inline grub_uint16_t
+grub_xhci_read16(volatile void *addr) {
+  return grub_le_to_cpu16 (*((volatile grub_uint32_t *)addr));
+}
+static inline grub_uint8_t
+grub_xhci_read8(volatile void *addr) {
+  return (*((volatile grub_uint32_t *)addr));
+}
+
+static inline grub_uint32_t
+grub_xhci_port_read (struct grub_xhci *x, grub_uint32_t port)
+{
+  return grub_xhci_read32(&x->pr[port].portsc);
+}
+
+static inline void
+grub_xhci_port_write (struct grub_xhci *x, grub_uint32_t port,
+		      grub_uint32_t and_mask, grub_uint32_t or_mask)
+{
+  grub_uint32_t reg = grub_xhci_port_read(x, port);
+  reg &= and_mask;
+  reg |= or_mask;
+
+  grub_xhci_write32(&x->pr[port].portsc, reg);
+}
+
+/****************************************************************
+ * xhci status and support functions
+ ****************************************************************/
+
+static grub_uint32_t xhci_get_pagesize(struct grub_xhci *x)
+{
+  /* Chapter 5.4.3 Page Size Register (PAGESIZE) */
+  for (grub_uint8_t i = 0; i < 16; i++)
+    {
+      if (grub_xhci_read32(&x->op->pagesize) & (1 << i))
+	return 1 << (12 + i);
+    }
+  return 0;
+}
+
+static grub_uint8_t xhci_is_halted(struct grub_xhci *x)
+{
+  return !!(grub_xhci_read32(&x->op->usbsts) & 1);
+}
+
+/* Just for debugging */
+static void xhci_check_status(struct grub_xhci *x)
+{
+  grub_uint32_t reg;
+
+  reg = grub_xhci_read32(&x->op->usbsts);
+  if (reg & 1)
+    grub_dprintf("xhci", "%s: xHCI halted\n", __func__);
+  if (reg & 2)
+    grub_dprintf("xhci", "%s: Host system error detected\n", __func__);
+  if (reg & (1 << 12))
+    grub_dprintf("xhci", "%s: Internal error detected\n", __func__);
+  reg = grub_xhci_read32(&x->op->crcr_low);
+  if (reg & (1 << 3))
+    grub_dprintf("xhci", "%s: Command ring running\n", __func__);
+}
+
+/* xhci_memalign_dma32 allocates DMA memory satisfying alignment and boundary
+ * requirements without wasting to much memory */
+static struct grub_pci_dma_chunk *
+xhci_memalign_dma32(grub_size_t align,
+		    grub_size_t size,
+		    grub_size_t boundary)
+{
+	struct grub_pci_dma_chunk *tmp;
+	const grub_uint32_t mask = boundary - 1;
+	grub_uint32_t start, end;
+
+	/* Allocate some memory and check if it doesn't cross boundary */
+	tmp = grub_memalign_dma32(align, size);
+	start = grub_dma_get_phys(tmp);
+	end = start + size - 1;
+	if ((start & mask) == (end & mask))
+		return tmp;
+	/* Buffer isn't usable, allocate bigger one */
+	grub_dma_free(tmp);
+
+	return grub_memalign_dma32(boundary, size);
+}
+
+/****************************************************************
+ * helper functions for in context DMA buffer
+ ****************************************************************/
+
+static int
+grub_xhci_inctx_size(struct grub_xhci *x)
+{
+  const grub_uint8_t cnt = GRUB_XHCI_MAX_ENDPOINTS + 1;
+  return (sizeof(struct grub_xhci_inctx) * cnt) << x->flag64;
+}
+
+static void
+grub_xhci_inctx_sync_dma_caches(struct grub_xhci *x, struct grub_pci_dma_chunk *inctx)
+{
+  grub_arch_sync_dma_caches(inctx, grub_xhci_inctx_size(x));
+}
+
+static struct grub_pci_dma_chunk *
+grub_xhci_alloc_inctx(struct grub_xhci *x, int maxepid,
+		      struct grub_usb_device *dev)
+{
+  int size = grub_xhci_inctx_size(x);
+  struct grub_pci_dma_chunk *dma = xhci_memalign_dma32(ALIGN_INCTX, size,
+						       x->pagesize);
+  if (!dma)
+    return NULL;
+
+  volatile struct grub_xhci_inctx *in = grub_dma_get_virt(dma);
+  grub_memset((void *)in, 0, size);
+
+  struct grub_xhci_slotctx *slot = (void*)&in[1 << x->flag64];
+  slot->ctx[0]    |= maxepid << 27; /* context entries */
+  grub_dprintf("xhci", "%s: speed=%d root_port=%d\n", __func__, dev->speed, dev->root_port);
+  switch (dev->speed) {
+    case GRUB_USB_SPEED_FULL:
+      slot->ctx[0]    |= XHCI_USB_FULLSPEED << 20;
+      break;
+    case GRUB_USB_SPEED_HIGH:
+     slot->ctx[0]     |= XHCI_USB_HIGHSPEED << 20;
+      break;
+    case GRUB_USB_SPEED_LOW:
+      slot->ctx[0]    |= XHCI_USB_LOWSPEED << 20;
+      break;
+    case GRUB_USB_SPEED_SUPER:
+      slot->ctx[0]    |= XHCI_USB_SUPERSPEED << 20;
+      break;
+    case GRUB_USB_SPEED_NONE:
+      slot->ctx[0]    |= 0 << 20;
+      break;
+  }
+
+  /* Route is greater zero on devices that are connected to a non root hub */
+  if (dev->route)
+    {
+      /* FIXME: Implement this code for non SuperSpeed hub devices */
+    }
+  slot->ctx[0]    |= dev->route;
+  slot->ctx[1]    |= (dev->root_port+1) << 16;
+
+  grub_arch_sync_dma_caches(in, size);
+
+  return dma;
+}
+
+/****************************************************************
+ * xHCI event processing
+ ****************************************************************/
+
+/* Dequeue events on the XHCI event ring generated by the hardware */
+static void xhci_process_events(struct grub_xhci *x)
+{
+    volatile struct grub_xhci_ring *evts = x->evts;
+    /* XXX invalidate caches */
+
+    for (;;) {
+	/* check for event */
+	grub_uint32_t nidx = grub_xhci_read32(&evts->nidx);
+	grub_uint32_t cs = grub_xhci_read32(&evts->cs);
+	volatile struct grub_xhci_trb *etrb = evts->ring + nidx;
+	grub_uint32_t control = grub_xhci_read32(&etrb->control);
+	if ((control & TRB_C) != (cs ? 1 : 0))
+	    return;
+
+	/* process event */
+	grub_uint32_t evt_type = TRB_TYPE(control);
+	grub_uint32_t evt_cc = (grub_xhci_read32(&etrb->status) >> 24) & 0xff;
+
+	switch (evt_type)
+	  {
+	    case ER_TRANSFER:
+	    case ER_COMMAND_COMPLETE:
+	      {
+		struct grub_xhci_trb  *rtrb = (void*)grub_xhci_read32(&etrb->ptr_low);
+		struct grub_xhci_ring *ring = XHCI_RING(rtrb);
+		volatile struct grub_xhci_trb  *evt = &ring->evt;
+		grub_uint32_t eidx = rtrb - ring->ring + 1;
+		grub_dprintf("xhci", "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n",
+			      __func__, ring, rtrb, evt, evt_type, eidx, evt_cc);
+		*evt = *etrb;
+		grub_xhci_write32(&ring->eidx, eidx);
+		break;
+	      }
+	    case ER_PORT_STATUS_CHANGE:
+	      {
+		/* Nothing to do here. grub_xhci_detect_dev will handle it */
+		break;
+	      }
+	    default:
+	      {
+		grub_dprintf("xhci", "%s: unknown event, type %d, cc %d\n",
+			      __func__, evt_type, evt_cc);
+		break;
+	      }
+	}
+
+	/* move ring index, notify xhci */
+	nidx++;
+	if (nidx == GRUB_XHCI_RING_ITEMS)
+	  {
+	    nidx = 0;
+	    cs = cs ? 0 : 1;
+	    grub_xhci_write32(&evts->cs, cs);
+	  }
+	grub_xhci_write32(&evts->nidx, nidx);
+	volatile struct grub_xhci_ir *ir = x->ir;
+	grub_uint32_t erdp = (grub_uint32_t)(evts->ring + nidx);
+	grub_xhci_write32(&ir->erdp_low, erdp);
+	grub_xhci_write32(&ir->erdp_high, 0);
+    }
+}
+
+/****************************************************************
+ * TRB handling
+ ****************************************************************/
+
+/* Signal the hardware to process events on a TRB ring */
+static void xhci_doorbell(struct grub_xhci *x, grub_uint32_t slotid, grub_uint32_t value)
+{
+  xhci_check_status(x);
+  grub_dprintf("xhci", "%s: slotid %d, epid %d\n", __func__, slotid, value);
+  grub_xhci_write32(&x->db[slotid].doorbell, value);
+}
+
+/* Check if a ring has any pending TRBs */
+static int xhci_ring_busy(volatile struct grub_xhci_ring *ring)
+{
+  grub_uint32_t eidx = grub_xhci_read32(&ring->eidx);
+  grub_uint32_t nidx = grub_xhci_read32(&ring->nidx);
+
+  return (eidx != nidx);
+}
+
+/* Returns free space in ring */
+static int xhci_ring_free_space(volatile struct grub_xhci_ring *ring)
+{
+  grub_uint32_t eidx = grub_xhci_read32(&ring->eidx);
+  grub_uint32_t nidx = grub_xhci_read32(&ring->nidx);
+
+  /* nidx is never 0, so reduce ring buffer size by one */
+  return (eidx > nidx) ? eidx-nidx
+		: (ARRAY_SIZE(ring->ring) - 1) - nidx + eidx;
+}
+
+/* Check if a ring is full */
+static int xhci_ring_full(volatile struct grub_xhci_ring *ring)
+{
+  /* Might need to insert one link TRB */
+  return xhci_ring_free_space(ring) <= 1;
+}
+
+/* Check if a ring is almost full */
+static int xhci_ring_almost_full(volatile struct grub_xhci_ring *ring)
+{
+  /* Might need to insert one link TRB */
+  return xhci_ring_free_space(ring) <= 2;
+}
+
+/* Wait for a ring to empty (all TRBs processed by hardware) */
+static int xhci_event_wait(struct grub_xhci *x,
+			   volatile struct grub_xhci_ring *ring,
+			   grub_uint32_t timeout)
+{
+    grub_uint32_t end = grub_get_time_ms () + timeout;
+
+    for (;;)
+      {
+	xhci_check_status(x);
+	xhci_process_events(x);
+	if (!xhci_ring_busy(ring))
+	  {
+	    grub_uint32_t status = ring->evt.status;
+	    return (status >> 24) & 0xff;
+	  }
+	if (grub_get_time_ms () > end)
+	  {
+	    xhci_check_status(x);
+	    grub_dprintf("xhci", "%s: Timeout waiting for event\n", __func__);
+	    return -1;
+	  }
+      }
+}
+
+/* Add a TRB to the given ring, either regular or inline */
+static void xhci_trb_fill(volatile struct grub_xhci_ring *ring,
+			  grub_uint64_t ptr, grub_uint32_t xferlen,
+			  grub_uint32_t flags)
+{
+  volatile struct grub_xhci_trb *dst = &ring->ring[ring->nidx];
+  dst->ptr_low = ptr & 0xffffffff;
+  dst->ptr_high = ptr >> 32;
+  dst->status = xferlen;
+  dst->control = flags | (ring->cs ? TRB_C : 0);
+
+  grub_arch_sync_dma_caches(dst, sizeof(ring->ring[0]));
+}
+
+/*
+ * Queue a TRB onto a ring.
+ *
+ * The caller must pass a pointer to the data in physical address-space or the
+ * data itself (but no more than 8 bytes) in data_or_addr. Inline data must have
+ * the flag TRB_TR_IDT set.
+ */
+static void xhci_trb_queue(volatile struct grub_xhci_ring *ring,
+			   grub_uint64_t data_or_addr,
+			   grub_uint32_t xferlen, grub_uint32_t flags)
+{
+  grub_dprintf("xhci", "%s: ring %p data %llx len %d flags 0x%x remain 0x%x\n", __func__,
+      ring, data_or_addr, xferlen & 0x1ffff, flags, xferlen >> 17);
+
+  if (xhci_ring_full(ring))
+    {
+      grub_dprintf("xhci", "%s: ERROR: ring %p is full, discarding TRB\n",
+	__func__, ring);
+      return;
+    }
+
+  if (ring->nidx >= ARRAY_SIZE(ring->ring) - 1)
+    {
+      /* Reset to command buffer pointer to the first element */
+      xhci_trb_fill(ring, (grub_addr_t)ring->ring, 0, (TR_LINK << 10) | TRB_LK_TC);
+      ring->nidx = 0;
+      ring->cs ^= 1;
+      grub_dprintf("xhci", "%s: ring %p [linked]\n", __func__, ring);
+    }
+
+  xhci_trb_fill(ring, data_or_addr, xferlen, flags);
+  ring->nidx++;
+  grub_dprintf("xhci", "%s: ring %p [nidx %d, len %d]\n",
+	       __func__, ring, ring->nidx, xferlen);
+}
+
+/*
+ * Queue a TRB onto a ring and flush it if necessary.
+ *
+ * The caller must pass a pointer to the data in physical address-space or the
+ * data itself (but no more than 8 bytes) in data_or_addr. Inline data must have
+ * the flag TRB_TR_IDT set.
+ */
+static int xhci_trb_queue_and_flush(struct grub_xhci *x,
+				    grub_uint32_t slotid,
+				    grub_uint32_t epid,
+				    volatile struct grub_xhci_ring *ring,
+				    grub_uint64_t data_or_addr,
+				    grub_uint32_t xferlen, grub_uint32_t flags)
+{
+  grub_uint8_t submit = 0;
+  if (xhci_ring_almost_full(ring))
+    {
+      grub_dprintf("xhci", "%s: almost full e %d n %d\n", __func__, ring->eidx, ring->nidx);
+      flags |= TRB_TR_IOC;
+      submit = 1;
+    }
+  /* Note: xhci_trb_queue might queue on or two elements, if the end of the TRB
+   * has been reached. The caller must account for that when filling the TRB. */
+  xhci_trb_queue(ring, data_or_addr, xferlen, flags);
+  /* Submit if less no free slot is remaining, we might need an additional
+   * one on the next call to this function. */
+  if (submit)
+    {
+      xhci_doorbell(x, slotid, epid);
+      int rc = xhci_event_wait(x, ring, 1000);
+      grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
+      return rc;
+    }
+  return 0;
+}
+
+/****************************************************************
+ * xHCI command functions
+ ****************************************************************/
+
+/* Submit a command to the xHCI command TRB */
+static int xhci_cmd_submit(struct grub_xhci *x,
+			   struct grub_pci_dma_chunk *inctx_dma,
+			   grub_uint32_t flags)
+{
+  volatile struct grub_xhci_inctx *inctx;
+  /* Don't submit if halted, it will fail */
+  if (xhci_is_halted(x))
+    return -1;
+
+  if (inctx_dma)
+    {
+      grub_xhci_inctx_sync_dma_caches(x, inctx_dma);
+
+      inctx = grub_dma_get_virt(inctx_dma);
+
+      struct grub_xhci_slotctx *slot = (void*)&inctx[1 << x->flag64];
+      grub_uint32_t port = ((slot->ctx[1] >> 16) & 0xff) - 1;
+      grub_uint32_t portsc = grub_xhci_port_read(x, port);
+      if (!(portsc & GRUB_XHCI_PORTSC_CCS))
+	{
+	  grub_dprintf("xhci", "%s: root port %d no longer connected\n",
+	    __func__, port);
+	  return -1;
+	}
+      xhci_trb_queue(x->cmds, grub_dma_get_phys(inctx_dma), 0, flags);
+    }
+    else
+    {
+      xhci_trb_queue(x->cmds, 0, 0, flags);
+    }
+
+    xhci_doorbell(x, 0, 0);
+    int rc = xhci_event_wait(x, x->cmds, 1000);
+    grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
+
+    return rc;
+}
+
+static int xhci_cmd_enable_slot(struct grub_xhci *x)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_ENABLE_SLOT << 10);
+
+  grub_dprintf("xhci", "%s:\n", __func__);
+  int cc = xhci_cmd_submit(x, NULL, flags);
+  if (cc != CC_SUCCESS)
+      return -1;
+  grub_dprintf("xhci", "%s: %p\n", __func__, &x->cmds->evt.control);
+  grub_dprintf("xhci", "%s: %x\n", __func__, grub_xhci_read32(&x->cmds->evt.control));
+
+  return (grub_xhci_read32(&x->cmds->evt.control) >> 24) & 0xff;
+}
+
+static int xhci_cmd_disable_slot(struct grub_xhci *x, grub_uint32_t slotid)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_DISABLE_SLOT << 10);
+  flags |= (slotid << 24);
+
+  grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
+  return xhci_cmd_submit(x, NULL, flags);
+}
+
+static int xhci_cmd_stop_endpoint(struct grub_xhci *x, grub_uint32_t slotid
+				       , grub_uint32_t epid
+				       , grub_uint32_t suspend)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_STOP_ENDPOINT << 10);
+  flags |= (epid << 16);
+  flags |= (suspend << 23) ;
+  flags |= (slotid << 24);
+
+  return xhci_cmd_submit(x, NULL, flags);
+}
+
+static int xhci_cmd_reset_endpoint(struct grub_xhci *x, grub_uint32_t slotid
+				       , grub_uint32_t epid
+				       , grub_uint32_t preserve)
+{
+  grub_uint32_t flags = 0;
+  flags |= (preserve << 9);
+  flags |= (CR_RESET_ENDPOINT << 10);
+  flags |= (epid << 16);
+  flags |= (slotid << 24);
+
+  return xhci_cmd_submit(x, NULL, flags);
+}
+
+static int xhci_cmd_set_dequeue_pointer(struct grub_xhci *x, grub_uint32_t slotid
+				       , grub_uint32_t epid
+				       , grub_addr_t tr_deque_pointer)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_SET_TR_DEQUEUE << 10);
+  flags |= (epid << 16);
+  flags |= (slotid << 24);
+
+  xhci_trb_queue(x->cmds, tr_deque_pointer, 0, flags);
+
+  xhci_doorbell(x, 0, 0);
+  int rc = xhci_event_wait(x, x->cmds, 1000);
+  grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
+
+  return rc;
+}
+
+static int xhci_cmd_address_device(struct grub_xhci *x, grub_uint32_t slotid,
+				   struct grub_pci_dma_chunk *inctx_dma)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_ADDRESS_DEVICE << 10);
+  flags |= (slotid << 24);
+
+  grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
+  return xhci_cmd_submit(x, inctx_dma, flags);
+}
+
+static int xhci_cmd_configure_endpoint(struct grub_xhci *x, grub_uint32_t slotid,
+				       struct grub_pci_dma_chunk *inctx_dma)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_CONFIGURE_ENDPOINT << 10);
+  flags |= (slotid << 24);
+
+  grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
+  return xhci_cmd_submit(x, inctx_dma, flags);
+}
+
+static int xhci_cmd_evaluate_context(struct grub_xhci *x, grub_uint32_t slotid,
+				     struct grub_pci_dma_chunk *inctx_dma)
+{
+  grub_uint32_t flags = 0;
+  flags |= (CR_EVALUATE_CONTEXT << 10);
+  flags |= (slotid << 24);
+
+  grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
+  return xhci_cmd_submit(x, inctx_dma, flags);
+}
+
+/****************************************************************
+ * xHCI host controller initialization
+ ****************************************************************/
+
+static grub_usb_err_t
+grub_xhci_reset (struct grub_xhci *x)
+{
+  grub_uint32_t reg;
+  grub_uint32_t end;
+
+  reg = grub_xhci_read32(&x->op->usbcmd);
+  if (reg & GRUB_XHCI_CMD_RS)
+    {
+      reg &= ~GRUB_XHCI_CMD_RS;
+      grub_xhci_write32(&x->op->usbcmd, reg);
+
+      end = grub_get_time_ms () + 32;
+      while (grub_xhci_read32(&x->op->usbcmd) & GRUB_XHCI_STS_HCH)
+	{
+	  if (grub_get_time_ms () > end)
+	      return GRUB_USB_ERR_TIMEOUT;
+
+	  grub_millisleep(1);
+	}
+    }
+
+  grub_dprintf("xhci", "grub_xhci_reset: resetting HC\n");
+  grub_xhci_write32(&x->op->usbcmd, GRUB_XHCI_CMD_HCRST);
+
+  /* Wait for device to complete reset and be enabled */
+  end = grub_get_time_ms () + 100;
+  while (grub_xhci_read32(&x->op->usbcmd) & GRUB_XHCI_CMD_HCRST)
+    {
+      if (grub_get_time_ms () > end)
+	  return GRUB_USB_ERR_TIMEOUT;
+
+      grub_millisleep(1);
+    }
+
+  /* Wait for device to complete reset and be enabled */
+  end = grub_get_time_ms () + 100;
+  while (grub_xhci_read32(&x->op->usbsts) & GRUB_XHCI_STS_CNR)
+    {
+      if (grub_get_time_ms () > end)
+	  return GRUB_USB_ERR_TIMEOUT;
+
+      grub_millisleep(1);
+    }
+
+  grub_xhci_write32(&x->op->config, x->slots);
+  grub_xhci_write32(&x->op->dcbaap_low, grub_dma_get_phys(x->devs_dma));
+  grub_xhci_write32(&x->op->dcbaap_high, 0);
+  grub_xhci_write32(&x->op->crcr_low, grub_dma_get_phys(x->cmds_dma)| 1);
+  grub_xhci_write32(&x->op->crcr_high, 0);
+  x->cmds->cs = 1;
+
+  grub_arch_sync_dma_caches(x->cmds, sizeof(*x->cmds));
+
+  x->eseg->ptr_low = grub_dma_get_phys(x->evts_dma);
+  x->eseg->ptr_high = 0;
+  x->eseg->size = GRUB_XHCI_RING_ITEMS;
+
+  grub_arch_sync_dma_caches(x->eseg, sizeof(*x->eseg));
+
+  grub_xhci_write32(&x->ir->erstsz, 1);
+  grub_xhci_write32(&x->ir->erdp_low, grub_dma_get_phys(x->evts_dma));
+  grub_xhci_write32(&x->ir->erdp_high, 0);
+  grub_xhci_write32(&x->ir->erstba_low, grub_dma_get_phys(x->eseg_dma));
+  grub_xhci_write32(&x->ir->erstba_high, 0);
+  x->evts->cs = 1;
+
+  grub_arch_sync_dma_caches(x->evts, sizeof(*x->eseg));
+
+  xhci_check_status(x);
+
+  grub_dprintf ("xhci", "XHCI OP COMMAND: %08x\n",
+		grub_xhci_read32 (&x->op->usbcmd));
+  grub_dprintf ("xhci", "XHCI OP STATUS: %08x\n",
+		grub_xhci_read32 (&x->op->usbsts));
+  grub_dprintf ("xhci", "XHCI OP PAGESIZE: %08x\n",
+		grub_xhci_read32 (&x->op->pagesize));
+  grub_dprintf ("xhci", "XHCI OP DNCTRL: %08x\n",
+		grub_xhci_read32 (&x->op->dnctl));
+  grub_dprintf ("xhci", "XHCI OP CRCR_LOW: %08x\n",
+		grub_xhci_read32 (&x->op->crcr_low));
+  grub_dprintf ("xhci", "XHCI OP CRCR_HIGH: %08x\n",
+		grub_xhci_read32 (&x->op->crcr_high));
+  grub_dprintf ("xhci", "XHCI OP DCBAAP_LOW: %08x\n",
+		grub_xhci_read32 (&x->op->dcbaap_low));
+  grub_dprintf ("xhci", "XHCI OP DCBAAP_HIGH: %08x\n",
+		grub_xhci_read32 (&x->op->dcbaap_high));
+  grub_dprintf ("xhci", "XHCI OP CONFIG: %08x\n",
+		grub_xhci_read32 (&x->op->config));
+  grub_dprintf ("xhci", "XHCI IR ERSTSZ: %08x\n",
+		grub_xhci_read32 (&x->ir->erstsz));
+  grub_dprintf ("xhci", "XHCI IR ERDP: %08x\n",
+		grub_xhci_read32 (&x->ir->erdp_low));
+  grub_dprintf ("xhci", "XHCI IR ERSTBA: %08x\n",
+		grub_xhci_read32 (&x->ir->erstba_low));
+
+  xhci_check_status(x);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+static grub_usb_err_t
+grub_xhci_request_legacy_handoff(volatile struct grub_xhci_xcap *xcap)
+{
+  grub_uint32_t end;
+
+  end = grub_get_time_ms () + 10;
+  for (;;)
+    {
+      grub_uint32_t cap = grub_xhci_read32(&xcap->cap);
+      if (cap & (1 << 16))
+	grub_xhci_write32(&xcap->cap, cap | (1 << 24));
+      else
+        break;
+
+      if (grub_get_time_ms () > end)
+	{
+	  grub_dprintf ("xhci","ERROR: %s TIMEOUT\n", __func__);
+	  return GRUB_USB_ERR_TIMEOUT;
+	}
+      grub_millisleep(1);
+    }
+  return GRUB_USB_ERR_NONE;
+}
+
+static void
+grub_xhci_fill_default_speed_mapping(struct grub_xhci_psids *ids)
+{
+	/* Chapter 7.2.2.1.1 "Default USB Speed ID Mapping" */
+	ids->psids[0].id = 1;
+	ids->psids[0].psie = 2;
+	ids->psids[0].psim = 12;
+	ids->psids[1].id = 2;
+	ids->psids[1].psie = 1;
+	ids->psids[1].psim = 1500;
+	ids->psids[2].id = 3;
+	ids->psids[2].psie = 2;
+	ids->psids[2].psim = 480;
+	ids->psids[3].id = 4;
+	ids->psids[3].psie = 3;
+	ids->psids[3].psim = 5;
+	ids->psids[4].id = 5;
+	ids->psids[4].psie = 3;
+	ids->psids[4].psim = 10;
+	ids->psids[5].id = 6;
+	ids->psids[5].psie = 3;
+	ids->psids[5].psim = 10;
+	ids->psids[6].id = 7;
+	ids->psids[6].psie = 3;
+	ids->psids[6].psim = 20;
+}
+
+static void
+grub_xhci_calc_speed_mapping(struct grub_xhci_psids *ids)
+{
+  const grub_uint64_t mult[4] = {1ULL, 1000ULL, 1000000ULL, 1000000000ULL};
+
+  for (grub_uint8_t i = 0; i < 16; i++)
+    {
+      if (ids->psids[i].id == 0)
+	continue;
+      ids->psids[i].bitrate = mult[ids->psids[i].psie & 3] * (grub_uint64_t)ids->psids[i].psim;
+      if (ids->psids[i].bitrate < 12000000ULL)
+	ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_LOW;
+      else if (ids->psids[i].bitrate < 480000000ULL)
+	ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_FULL;
+      else if (ids->psids[i].bitrate > 1200000000ULL)
+	ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_SUPER;
+      else
+	ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_HIGH;
+    }
+}
+
+
+/* PCI iteration function... */
+void
+grub_xhci_init_device (volatile void *regs)
+{
+  struct grub_xhci *x;
+  grub_uint32_t hcs1, hcc, reg;
+
+  /* Allocate memory for the controller and fill basic values. */
+  x = grub_zalloc (sizeof (*x));
+  if (!x)
+    {
+      grub_dprintf("xhci", "Failed to allocate memory\n");
+      return;
+    }
+  x->caps = (volatile struct grub_xhci_caps *) regs;
+  x->op = (volatile struct grub_xhci_op *) (((grub_uint8_t *)regs) +
+      grub_xhci_read8(&x->caps->caplength));
+  x->pr = (volatile struct grub_xhci_pr *) (((grub_uint8_t *)x->op) +
+      GRUB_XHCI_PR_OFFSET);
+  x->db = (volatile struct grub_xhci_db *) (((grub_uint8_t *)regs) +
+      grub_xhci_read32(&x->caps->dboff));
+  x->ir = (volatile struct grub_xhci_ir *) (((grub_uint8_t *)regs) +
+		  grub_xhci_read32(&x->caps->rtsoff) + GRUB_XHCI_IR_OFFSET);
+
+  grub_dprintf ("xhci", "XHCI init: CAPLENGTH: 0x%02x\n",
+		grub_xhci_read8 (&x->caps->caplength));
+  grub_dprintf ("xhci", "XHCI init: HCIVERSION: 0x%04x\n",
+		grub_xhci_read16 (&x->caps->hciversion));
+  grub_dprintf ("xhci", "XHCI init: HCSPARAMS1: 0x%08x\n",
+		grub_xhci_read32 (&x->caps->hcsparams1));
+  grub_dprintf ("xhci", "XHCI init: HCSPARAMS2: 0x%08x\n",
+		grub_xhci_read32 (&x->caps->hcsparams2));
+  grub_dprintf ("xhci", "XHCI init: HCSPARAMS3: 0x%08x\n",
+		grub_xhci_read32 (&x->caps->hcsparams3));
+  grub_dprintf ("xhci", "XHCI init: HCCPARAMS: 0x%08x\n",
+		grub_xhci_read32 (&x->caps->hcsparams3));
+  grub_dprintf ("xhci", "XHCI init: DBOFF: 0x%08x\n",
+		grub_xhci_read32 (&x->caps->dboff));
+  grub_dprintf ("xhci", "XHCI init: RTOFF: 0x%08x\n",
+		grub_xhci_read32 (&x->caps->rtsoff));
+
+  hcs1 = grub_xhci_read32(&x->caps->hcsparams1);
+  hcc = grub_xhci_read32(&x->caps->hccparams);
+  x->ports = (grub_uint32_t) ((hcs1 >> 24) & 0xff);
+  x->slots = (grub_uint32_t) (hcs1	 & 0xff);
+  x->xcap  = (grub_uint32_t) ((hcc >> 16) & 0xffff) * sizeof(grub_uint32_t);
+  x->flag64 = (grub_uint8_t) ((hcc & 0x04) ? 1 : 0);
+  grub_dprintf("xhci", "XHCI init: %d ports, %d slots, %d byte contexts\n"
+	       , x->ports, x->slots, x->flag64 ? 64 : 32);
+
+  x->psids = grub_zalloc (sizeof (struct grub_xhci_psids) * x->ports);
+  if (x->xcap)
+    {
+      grub_uint32_t off;
+      volatile grub_uint8_t *addr = (grub_uint8_t *) x->caps + x->xcap;
+      do
+	{
+	  volatile struct grub_xhci_xcap *xcap = (void *)addr;
+	  grub_uint32_t ports, name, cap = grub_xhci_read32(&xcap->cap);
+	  switch (cap & 0xff) {
+	    case XHCI_CAP_LEGACY_SUPPORT:
+	      {
+		if (grub_xhci_request_legacy_handoff(xcap) != GRUB_USB_ERR_NONE)
+		  {
+		    grub_dprintf("xhci", "XHCI init: Failed to get xHCI ownership\n");
+		    goto fail;
+		  }
+	        break;
+	      }
+	    case XHCI_CAP_SUPPORTED_PROTOCOL:
+	      {
+		name  = grub_xhci_read32(&xcap->data[0]);
+		ports = grub_xhci_read32(&xcap->data[1]);
+		const grub_uint8_t major = (cap >> 24) & 0xff;
+		const grub_uint8_t minor = (cap >> 16) & 0xff;
+		const grub_uint8_t psic = (ports >> 28) & 0xf;
+		const grub_uint8_t count = (ports >> 8) & 0xff;
+		const grub_uint8_t start = (ports >> 0) & 0xff;
+		grub_dprintf("xhci", "XHCI init: protocol %c%c%c%c %x.%02x"
+				", %d ports (offset %d), def %x, psic %d\n"
+				, (name >>  0) & 0xff
+				, (name >>  8) & 0xff
+				, (name >> 16) & 0xff
+				, (name >> 24) & 0xff
+				, major, minor
+				, count, start
+				, ports >> 16
+				, psic);
+		if (name == 0x20425355 /* "USB " */)
+		  {
+		    if (major == 2)
+		      {
+			x->usb2.start = start;
+			x->usb2.count = count;
+		      }
+		    else if (major == 3)
+		      {
+			x->usb3.start = start;
+			x->usb3.count = count;
+		      }
+
+		    for (grub_uint32_t p = start - 1; p < start + count - 1UL; p++)
+		      {
+			x->psids[p].major = major;
+			x->psids[p].minor = minor;
+			grub_xhci_fill_default_speed_mapping(&x->psids[p]);
+			for (grub_uint8_t i = 0; i < psic; i++)
+			  {
+			    grub_uint32_t psid = grub_xhci_read32(&xcap->data[3 + i]);
+			    x->psids[p].psids[i].id = (psid >> 0) & 0xf;
+			    x->psids[p].psids[i].psie = (psid >> 4) & 0x3;
+			    x->psids[p].psids[i].psim = (psid >> 16) & 0xfffff;
+			  }
+			grub_xhci_calc_speed_mapping(&x->psids[p]);
+		      }
+		  }
+
+		break;
+	      }
+	    default:
+	      {
+	        grub_dprintf("xhci", "XHCI    extcap 0x%x @ %p\n", cap & 0xff, addr);
+	        break;
+	      }
+	  }
+	off = (cap >> 8) & 0xff;
+	addr += off << 2;
+	}
+      while (off > 0);
+    }
+
+  x->pagesize = xhci_get_pagesize(x);
+  grub_dprintf("xhci", "XHCI init: Minimum supported page size 0x%x\n",
+	       x->pagesize);
+
+  /* Chapter 6.1 Device Context Base Address Array */
+  x->devs_dma = xhci_memalign_dma32(ALIGN_DCBAA,
+				    sizeof(*x->devs) * (x->slots + 1),
+				    x->pagesize);
+  if (!x->devs_dma)
+      goto fail;
+  x->devs = grub_dma_get_virt(x->devs_dma);
+  grub_memset((void *)x->devs, 0, sizeof(*x->devs) * (x->slots + 1));
+  grub_arch_sync_dma_caches(x->devs, sizeof(*x->devs) * (x->slots + 1));
+  grub_dprintf ("xhci", "XHCI init: device memory %p (%x)\n",
+		grub_dma_get_virt(x->devs_dma),
+		grub_dma_get_phys(x->devs_dma));
+
+  /* Chapter 6.5 Event Ring Segment Table */
+  x->eseg_dma = xhci_memalign_dma32(ALIGN_EVT_RING_TABLE, sizeof(*x->eseg), 0);
+  if (!x->eseg_dma)
+      goto fail;
+  x->eseg = grub_dma_get_virt(x->eseg_dma);
+  grub_memset((void *)x->eseg, 0, sizeof(*x->eseg));
+  grub_arch_sync_dma_caches(x->eseg, sizeof(*x->eseg));
+  grub_dprintf ("xhci", "XHCI init: event ring table memory %p (%x)\n",
+		grub_dma_get_virt(x->eseg_dma),
+		grub_dma_get_phys(x->eseg_dma));
+
+  x->cmds_dma = xhci_memalign_dma32(ALIGN_CMD_RING_SEG, sizeof(*x->cmds),
+				    BOUNDARY_RING);
+  if (!x->cmds_dma)
+      goto fail;
+  x->cmds = grub_dma_get_virt(x->cmds_dma);
+  grub_memset((void *)x->cmds, 0, sizeof(*x->cmds));
+  grub_arch_sync_dma_caches(x->cmds, sizeof(*x->cmds));
+  grub_dprintf ("xhci", "XHCI init: command ring memory %p (%x)\n",
+		grub_dma_get_virt(x->cmds_dma),
+		grub_dma_get_phys(x->cmds_dma));
+
+  x->evts_dma = xhci_memalign_dma32(ALIGN_EVT_RING_SEG, sizeof(*x->evts),
+				    BOUNDARY_RING);
+  if (!x->evts_dma)
+      goto fail;
+  x->evts = grub_dma_get_virt(x->evts_dma);
+  grub_memset((void *)x->evts, 0, sizeof(*x->evts));
+  grub_arch_sync_dma_caches(x->evts, sizeof(*x->evts));
+  grub_dprintf ("xhci", "XHCI init: event ring memory %p (%x)\n",
+		grub_dma_get_virt(x->evts_dma),
+		grub_dma_get_phys(x->evts_dma));
+
+  /* Chapter 4.20 Scratchpad Buffers */
+  reg = grub_xhci_read32(&x->caps->hcsparams2);
+  x->spb = (reg >> 21 & 0x1f) << 5 | reg >> 27;
+  if (x->spb)
+    {
+      volatile grub_uint64_t *spba;
+      grub_dprintf("xhci", "XHCI init: set up %d scratch pad buffers\n",
+		   x->spb);
+      x->spba_dma = xhci_memalign_dma32(ALIGN_SPBA, sizeof(*spba) * x->spb,
+					x->pagesize);
+      if (!x->spba_dma)
+	goto fail;
+
+      x->spad_dma = xhci_memalign_dma32(x->pagesize, x->pagesize * x->spb,
+					x->pagesize);
+      if (!x->spad_dma)
+	{
+	  grub_dma_free(x->spba_dma);
+	  goto fail;
+	}
+
+      spba = grub_dma_get_virt(x->spba_dma);
+      for (grub_uint32_t i = 0; i < x->spb; i++)
+	spba[i] = (grub_addr_t)grub_dma_get_phys(x->spad_dma) + (i * x->pagesize);
+      grub_arch_sync_dma_caches(x->spba_dma, sizeof(*spba) * x->spb);
+
+      x->devs[0].ptr_low = grub_dma_get_phys(x->spba_dma);
+      x->devs[0].ptr_high = 0;
+      grub_arch_sync_dma_caches(x->devs_dma, sizeof(x->devs[0]));
+      grub_dprintf ("xhci", "XHCI init: Allocated %d scratch buffers of size 0x%x\n",
+		    x->spb, x->pagesize);
+    }
+
+  grub_xhci_reset(x);
+
+  /* Set the running bit */
+  reg = grub_xhci_read32 (&x->op->usbcmd);
+  reg |= GRUB_XHCI_CMD_RS;
+  grub_xhci_write32 (&x->op->usbcmd, reg);
+
+
+  /* Link to xhci now that initialisation is successful.  */
+  x->next = xhci;
+  xhci = x;
+
+  return;
+
+fail:
+  grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: FAILED!\n");
+  if (x)
+    {
+      if (x->devs_dma)
+	grub_dma_free (x->devs_dma);
+      if (x->eseg_dma)
+	grub_dma_free (x->eseg_dma);
+      if (x->cmds_dma)
+	grub_dma_free (x->cmds_dma);
+      if (x->evts_dma)
+	grub_dma_free (x->evts_dma);
+      if (x->spad_dma)
+	grub_dma_free (x->spad_dma);
+      if (x->spba_dma)
+	grub_dma_free (x->spba_dma);
+    }
+  grub_free (x);
+
+  return;
+}
+
+static int
+grub_xhci_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data)
+{
+  struct grub_xhci *x;
+  struct grub_usb_controller dev;
+
+  for (x = xhci; x; x = x->next)
+    {
+      dev.data = x;
+      if (hook (&dev, hook_data))
+	return 1;
+    }
+
+  return 0;
+}
+
+/****************************************************************
+ * xHCI maintainance functions 
+ ****************************************************************/
+
+static grub_usb_err_t
+grub_xhci_update_hub_portcount (struct grub_xhci *x,
+				grub_usb_transfer_t transfer,
+				grub_uint32_t slotid)
+{
+  struct grub_pci_dma_chunk *in_dma;
+  volatile struct grub_xhci_slotctx *hdslot;
+  grub_uint32_t epid = 0;
+
+  if (!transfer || !transfer->dev || !transfer->dev->nports)
+    return GRUB_USB_ERR_NONE;
+
+  hdslot = grub_dma_phys2virt(x->devs[slotid].ptr_low, x->devs_dma);
+  if ((hdslot->ctx[3] >> 27) == 3)
+    /* Already configured */
+    return 0;
+
+  grub_dprintf("xhci", "%s: updating hub config to %d ports\n", __func__,
+	       transfer->dev->nports);
+
+  xhci_check_status(x);
+
+  /* Allocate input context and initialize endpoint info. */
+  in_dma = grub_xhci_alloc_inctx(x, epid, transfer->dev);
+  if (!in_dma)
+    return GRUB_USB_ERR_INTERNAL;
+  volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
+
+  in->add = (1 << epid);
+
+  struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
+  ep->ctx[0]   |= 1 << 26;
+  ep->ctx[1]   |= transfer->dev->nports << 24;
+
+  int cc = xhci_cmd_configure_endpoint(x, slotid, in_dma);
+  grub_dma_free(in_dma);
+
+  if (cc != CC_SUCCESS)
+    {
+      grub_dprintf("xhci", "%s: reconf ctl endpoint: failed (cc %d)\n",
+		   __func__, cc);
+      return GRUB_USB_ERR_BADDEVICE;
+    }
+
+  return GRUB_USB_ERR_NONE;
+}
+
+static grub_usb_err_t
+grub_xhci_update_max_paket_size (struct grub_xhci *x,
+				 grub_usb_transfer_t transfer,
+				 grub_uint32_t slotid,
+				 grub_uint32_t max_packet)
+{
+  struct grub_pci_dma_chunk *in_dma;
+  grub_uint32_t epid = 1;
+
+  if (!transfer || !transfer->dev || !max_packet)
+    return GRUB_USB_ERR_NONE;
+
+  grub_dprintf("xhci", "%s: updating max packet size to 0x%x\n", __func__,
+	       max_packet);
+
+  xhci_check_status(x);
+
+  /* Allocate input context and initialize endpoint info. */
+  in_dma = grub_xhci_alloc_inctx(x, epid, transfer->dev);
+  if (!in_dma)
+    return GRUB_USB_ERR_INTERNAL;
+  volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
+  in->add = (1 << epid);
+
+  struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
+  ep->ctx[1]   |= max_packet << 16;
+
+  int cc = xhci_cmd_evaluate_context(x, slotid, in_dma);
+  grub_dma_free(in_dma);
+
+  if (cc != CC_SUCCESS)
+    {
+      grub_dprintf("xhci", "%s: reconf ctl endpoint: failed (cc %d)\n",
+		   __func__, cc);
+      return GRUB_USB_ERR_BADDEVICE;
+    }
+
+  return GRUB_USB_ERR_NONE;
+}
+
+/****************************************************************
+ * xHCI endpoint enablement functions
+ ****************************************************************/
+
+static grub_usb_err_t
+grub_xhci_prepare_endpoint (struct grub_xhci *x,
+			    struct grub_usb_device *dev,
+			    grub_uint8_t endpoint,
+			    grub_transfer_type_t dir,
+			    grub_transaction_type_t type,
+			    grub_uint32_t maxpaket,
+			    struct grub_xhci_priv *priv)
+{
+  grub_uint32_t epid;
+  struct grub_pci_dma_chunk *reqs_dma;
+  struct grub_pci_dma_chunk *in_dma;
+  volatile struct grub_xhci_ring *reqs;
+  volatile struct grub_xhci_slotctx *slotctx;
+
+  if (!x || !priv)
+    return GRUB_USB_ERR_INTERNAL;
+
+  xhci_check_status(x);
+
+  if (endpoint == 0)
+    {
+      epid = 1;
+    }
+  else
+    {
+      epid = (endpoint & 0x0f) * 2;
+      epid += (dir == GRUB_USB_TRANSFER_TYPE_IN) ? 1 : 0;
+    }
+  grub_dprintf("xhci", "%s: epid %d\n", __func__, epid);
+
+  /* Test if already prepared */
+  if (priv->slotid > 0 && priv->enpoint_trbs[epid] != NULL)
+    return GRUB_USB_ERR_NONE;
+
+  /* Allocate DMA buffer as endpoint cmd TRB */
+  reqs_dma = xhci_memalign_dma32(ALIGN_TRB, sizeof(*reqs),
+				 BOUNDARY_RING);
+  if (!reqs_dma)
+    return GRUB_USB_ERR_INTERNAL;
+  reqs = grub_dma_get_virt(reqs_dma);
+  grub_memset((void *)reqs, 0, sizeof(*reqs));
+  reqs->cs = 1;
+
+  grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
+
+  /* Allocate input context and initialize endpoint info. */
+  in_dma = grub_xhci_alloc_inctx(x, epid, dev);
+  if (!in_dma)
+    {
+      grub_dma_free(reqs_dma);
+      return GRUB_USB_ERR_INTERNAL;
+    }
+  volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
+  in->add = 0x01 | (1 << epid);
+
+  struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
+  switch (type)
+    {
+      case GRUB_USB_TRANSACTION_TYPE_CONTROL:
+        ep->ctx[1]   |= 0 << 3;
+        break;
+      case GRUB_USB_TRANSACTION_TYPE_BULK:
+        ep->ctx[1]   |= 2 << 3;
+        break;
+    }
+  if (dir == GRUB_USB_TRANSFER_TYPE_IN
+      || type== GRUB_USB_TRANSACTION_TYPE_CONTROL)
+      ep->ctx[1] |= 1 << 5;
+  ep->ctx[1]   |= maxpaket << 16;
+  ep->deq_low  = grub_dma_get_phys(reqs_dma);
+  ep->deq_low  |= 1;	 /* dcs */
+  ep->length   = maxpaket;
+
+  grub_dprintf("xhci", "%s: ring %p, epid %d, max %d\n", __func__,
+	       reqs, epid, maxpaket);
+  if (epid == 1 || priv->slotid == 0) {
+    /* Enable slot. */
+    int slotid = xhci_cmd_enable_slot(x);
+    if (slotid < 0)
+      {
+	grub_dprintf("xhci", "%s: enable slot: failed\n", __func__);
+	grub_dma_free(reqs_dma);
+	grub_dma_free(in_dma);
+	return GRUB_USB_ERR_BADDEVICE;
+      }
+    grub_dprintf("xhci", "%s: get slot %d assigned\n", __func__, slotid);
+
+    grub_uint32_t size = (sizeof(struct grub_xhci_slotctx) * GRUB_XHCI_MAX_ENDPOINTS) << x->flag64;
+
+    /* Allocate memory for the device specific slot context */
+    priv->slotctx_dma = xhci_memalign_dma32(ALIGN_SLOTCTX, size,
+					    x->pagesize);
+    if (!priv->slotctx_dma)
+      {
+	grub_dprintf("xhci", "%s: grub_memalign_dma32 failed\n", __func__);
+	grub_dma_free(reqs_dma);
+	grub_dma_free(in_dma);
+	return GRUB_USB_ERR_INTERNAL;
+      }
+    slotctx = grub_dma_get_virt(priv->slotctx_dma);
+
+    grub_dprintf("xhci", "%s: enable slot: got slotid %d\n", __func__, slotid);
+    grub_memset((void *)slotctx, 0, size);
+    grub_arch_sync_dma_caches(slotctx, size);
+
+    x->devs[slotid].ptr_low = grub_dma_get_phys(priv->slotctx_dma);
+    x->devs[slotid].ptr_high = 0;
+    grub_arch_sync_dma_caches(&x->devs[slotid], sizeof(x->devs[0]));
+
+    /* Send set_address command. */
+    int cc = xhci_cmd_address_device(x, slotid, in_dma);
+    if (cc != CC_SUCCESS)
+      {
+	grub_dprintf("xhci","%s: address device: failed (cc %d)\n", __func__, cc);
+	cc = xhci_cmd_disable_slot(x, slotid);
+	if (cc != CC_SUCCESS) {
+	    grub_dprintf("xhci", "%s: disable failed (cc %d)\n", __func__, cc);
+	} else {
+	  x->devs[slotid].ptr_low = 0;
+	  x->devs[slotid].ptr_high = 0;
+	  grub_arch_sync_dma_caches(&x->devs[slotid], sizeof(x->devs[0]));
+	}
+	grub_dma_free(priv->slotctx_dma);
+	grub_dma_free(reqs_dma);
+	grub_dma_free(in_dma);
+	return GRUB_USB_ERR_BADDEVICE;
+      }
+    priv->enpoint_trbs[epid] = reqs;
+    priv->enpoint_trbs_dma[epid] = reqs_dma;
+    priv->slotid = slotid;
+    priv->max_packet = 0;
+  }
+  if (epid != 1)
+    {
+	/* Send configure command. */
+	int cc = xhci_cmd_configure_endpoint(x, priv->slotid, in_dma);
+	if (cc != CC_SUCCESS)
+	  {
+	    grub_dprintf("xhci", "%s: configure endpoint: failed (cc %d)\n",
+			 __func__, cc);
+	    grub_dma_free(reqs_dma);
+	    grub_dma_free(in_dma);
+	    return GRUB_USB_ERR_BADDEVICE;
+	  }
+      priv->enpoint_trbs[epid] = reqs;
+      priv->enpoint_trbs_dma[epid] = reqs_dma;
+    }
+
+  grub_dprintf("xhci", "%s: done\n", __func__);
+  grub_dma_free(in_dma);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+
+/****************************************************************
+ * xHCI transfer helper functions
+ ****************************************************************/
+
+static grub_usb_err_t
+grub_xhci_usb_to_grub_err (unsigned char status)
+{
+  if (status != CC_SUCCESS)
+    grub_dprintf("xhci", "%s: xfer failed (cc %d)\n", __func__, status);
+  else
+    grub_dprintf("xhci", "%s: xfer done   (cc %d)\n", __func__, status);
+
+  if (status == CC_BABBLE_DETECTED)
+    return GRUB_USB_ERR_BABBLE;
+  else if (status == CC_DATA_BUFFER_ERROR)
+    return GRUB_USB_ERR_DATA;
+  else if (status == CC_STALL_ERROR)
+    return GRUB_USB_ERR_STALL;
+  else if (status != CC_SUCCESS)
+    return GRUB_USB_ERR_NAK;
+
+  return GRUB_USB_ERR_NONE;
+}
+
+static int
+grub_xhci_transfer_is_zlp(grub_usb_transfer_t transfer, int idx)
+{
+  if (idx >= transfer->transcnt)
+    return 0;
+
+  grub_usb_transaction_t tr = &transfer->transactions[idx];
+
+  return (tr->size == 0) &&
+    ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
+    (tr->pid == GRUB_USB_TRANSFER_TYPE_IN));
+}
+
+static int
+grub_xhci_transfer_is_last(grub_usb_transfer_t transfer, int idx)
+{
+    return (idx + 1) == transfer->transcnt;
+}
+
+static int
+grub_xhci_transfer_is_data(grub_usb_transfer_t transfer, int idx)
+{
+  grub_usb_transaction_t tr;
+
+  if (idx >= transfer->transcnt)
+    return 0;
+
+  tr = &transfer->transactions[idx];
+  if (tr->size == 0 ||
+      (tr->pid == GRUB_USB_TRANSFER_TYPE_SETUP))
+    return 0;
+
+  /* If there's are no DATA pakets before it's a DATA paket */
+  for (int i = idx - 1; i >= 0; i--)
+    {
+      tr = &transfer->transactions[i];
+      if (tr->size > 0 &&
+	  ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
+	  (tr->pid == GRUB_USB_TRANSFER_TYPE_IN)))
+	    return 0;
+    }
+  return 1;
+}
+
+static int
+grub_xhci_transfer_is_in(grub_usb_transfer_t transfer, int idx)
+{
+  grub_usb_transaction_t tr;
+
+  if (idx >= transfer->transcnt)
+    return 0;
+
+  tr = &transfer->transactions[idx];
+
+  return tr->pid == GRUB_USB_TRANSFER_TYPE_IN;
+}
+
+static int
+grub_xhci_transfer_is_normal(grub_usb_transfer_t transfer, int idx)
+{
+  grub_usb_transaction_t tr;
+
+  if (idx >= transfer->transcnt)
+    return 0;
+
+  tr = &transfer->transactions[idx];
+  if (tr->size == 0 ||
+      (tr->pid == GRUB_USB_TRANSFER_TYPE_SETUP))
+    return 0;
+
+  /* If there's at least one DATA paket before it's a normal */
+  for (int i = idx - 1; i >= 0; i--)
+    {
+      tr = &transfer->transactions[i];
+      if (tr->size > 0 &&
+	  ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
+	  (tr->pid == GRUB_USB_TRANSFER_TYPE_IN)))
+	    return 1;
+
+    }
+  return 0;
+}
+
+static int
+grub_xhci_transfer_next_is_normal(grub_usb_transfer_t transfer, int idx)
+{
+  return grub_xhci_transfer_is_normal(transfer, idx + 1);
+}
+
+static int
+grub_xhci_transfer_next_is_in(grub_usb_transfer_t transfer, int idx)
+{
+  return grub_xhci_transfer_is_in(transfer, idx + 1);
+}
+
+static grub_uint8_t grub_xhci_epid_from_transfer(grub_usb_transfer_t transfer)
+{
+  grub_uint8_t epid;
+
+  if (transfer->endpoint == 0) {
+      epid = 1;
+  } else {
+    epid = (transfer->endpoint & 0x0f) * 2;
+    epid += (transfer->dir == GRUB_USB_TRANSFER_TYPE_IN) ? 1 : 0;
+  }
+  return epid;
+}
+
+/****************************************************************
+ * xHCI transfer functions
+ ****************************************************************/
+
+static grub_usb_err_t
+grub_xhci_setup_transfer (grub_usb_controller_t dev,
+			  grub_usb_transfer_t transfer)
+{
+  struct grub_xhci_transfer_controller_data *cdata;
+  struct grub_xhci *x = (struct grub_xhci *) dev->data;
+  grub_uint8_t epid;
+  grub_usb_err_t err;
+  volatile struct grub_xhci_ring *reqs;
+  int rc;
+  struct grub_xhci_priv *priv;
+
+   xhci_check_status(x);
+
+  if (!dev || !transfer || !transfer->dev || !transfer->dev->xhci_priv)
+    return GRUB_USB_ERR_INTERNAL;
+
+  priv = transfer->dev->xhci_priv;
+  err = grub_xhci_prepare_endpoint(x, transfer->dev,
+				   transfer->endpoint,
+				   transfer->dir,
+				   transfer->type,
+				   transfer->max,
+				   priv);
+
+  if (err != GRUB_USB_ERR_NONE)
+    return err;
+
+  epid = grub_xhci_epid_from_transfer(transfer);
+
+  /* Update the max packet size once descdev.maxsize0 is valid */
+  if (epid == 1 &&
+      (priv->max_packet == 0) &&
+      (transfer->dev->descdev.maxsize0 > 0))
+    {
+      if (transfer->dev->speed == GRUB_USB_SPEED_SUPER)
+        priv->max_packet = 1UL << transfer->dev->descdev.maxsize0;
+      else
+        priv->max_packet = transfer->dev->descdev.maxsize0;
+      err = grub_xhci_update_max_paket_size(x, transfer, priv->slotid, priv->max_packet);
+      if (err != GRUB_USB_ERR_NONE)
+        {
+          grub_dprintf("xhci", "%s: Updating max paket size failed\n", __func__);
+          return err;
+        }
+    }
+  if (epid == 1 &&
+      transfer->dev->descdev.class == 9 &&
+      transfer->dev->nports > 0)
+    {
+      err = grub_xhci_update_hub_portcount(x, transfer, priv->slotid);
+      if (err != GRUB_USB_ERR_NONE)
+        {
+          grub_dprintf("xhci", "%s: Updating max paket size failed\n", __func__);
+          return err;
+        }
+    }
+
+  /* Allocate private data for the transfer */
+  cdata = grub_zalloc(sizeof(*cdata));
+  if (!cdata)
+    return GRUB_USB_ERR_INTERNAL;
+
+  reqs = priv->enpoint_trbs[epid];
+
+  transfer->controller_data = cdata;
+
+  /* Now queue the transfer onto the TRB */
+  if (transfer->type == GRUB_USB_TRANSACTION_TYPE_CONTROL)
+  {
+    volatile struct grub_usb_packet_setup *setupdata;
+    setupdata = (void *)transfer->transactions[0].data;
+    grub_dprintf("xhci", "%s: CONTROLL TRANS req %d\n", __func__, setupdata->request);
+    grub_dprintf("xhci", "%s: CONTROLL TRANS length %d\n", __func__, setupdata->length);
+
+    if (setupdata && setupdata->request == GRUB_USB_REQ_SET_ADDRESS)
+      return GRUB_USB_ERR_NONE;
+
+    if (transfer->transcnt < 2)
+      return GRUB_USB_ERR_INTERNAL;
+
+    for (int i = 0; i < transfer->transcnt; i++)
+      {
+	grub_uint32_t flags = 0;
+	grub_uint64_t inline_data;
+	grub_usb_transaction_t tr = &transfer->transactions[i];
+
+	switch (tr->pid)
+	  {
+	    case GRUB_USB_TRANSFER_TYPE_SETUP:
+	      {
+		grub_dprintf("xhci", "%s: SETUP PKG\n", __func__);
+		grub_dprintf("xhci", "%s: transfer->size %d\n", __func__, transfer->size);
+		grub_dprintf("xhci", "%s: tr->size %d SETUP PKG\n", __func__, tr->size);
+
+		flags |= (TR_SETUP << 10);
+		flags |= TRB_TR_IDT;
+
+		if (transfer->size > 0)
+		  {
+		    if (grub_xhci_transfer_next_is_in(transfer, i))
+		      flags |= (3 << 16); /* TRT IN */
+		    else
+		      flags |= (2 << 16); /* TRT OUT */
+		  }
+		break;
+	      }
+	    case GRUB_USB_TRANSFER_TYPE_OUT:
+	      {
+		grub_dprintf("xhci", "%s: OUT PKG\n", __func__);
+		cdata->transfer_size += tr->size;
+		break;
+	      }
+	    case GRUB_USB_TRANSFER_TYPE_IN:
+	      {
+		grub_dprintf("xhci", "%s: IN PKG\n", __func__);
+		cdata->transfer_size += tr->size;
+		flags |= TRB_TR_DIR;
+		break;
+	      }
+	  }
+
+	if (grub_xhci_transfer_is_normal(transfer, i))
+	  flags |= (TR_NORMAL << 10);
+	else if (grub_xhci_transfer_is_data(transfer, i))
+	  flags |= (TR_DATA << 10);
+	else if (grub_xhci_transfer_is_zlp(transfer, i))
+	  flags |= (TR_STATUS << 10);
+
+	if (grub_xhci_transfer_next_is_normal(transfer, i))
+	  flags |= TRB_TR_CH;
+
+	if (grub_xhci_transfer_is_last(transfer, i))
+	  flags |= TRB_TR_IOC;
+
+	/* Assume the ring has enough free space for all TRBs */
+	if (flags & TRB_TR_IDT && tr->size <= (int)sizeof(inline_data))
+	  {
+	    grub_memcpy(&inline_data, (void *)tr->data, tr->size);
+	    xhci_trb_queue(reqs, inline_data, tr->size, flags);
+	  }
+	else
+	  {
+	    xhci_trb_queue(reqs, tr->data, tr->size, flags);
+	  }
+      }
+  }
+  else if (transfer->type == GRUB_USB_TRANSACTION_TYPE_BULK)
+    {
+      for (int i = 0; i < transfer->transcnt; i++)
+	{
+	  grub_uint32_t flags = (TR_NORMAL << 10);
+	  grub_usb_transaction_t tr = &transfer->transactions[i];
+	  switch (tr->pid)
+	    {
+	      case GRUB_USB_TRANSFER_TYPE_OUT:
+		{
+		  grub_dprintf("xhci", "%s: OUT PKG\n", __func__);
+		  cdata->transfer_size += tr->size;
+		  break;
+		}
+	      case GRUB_USB_TRANSFER_TYPE_IN:
+		{
+		  grub_dprintf("xhci", "%s: IN PKG\n", __func__);
+		  cdata->transfer_size += tr->size;
+		  flags |= TRB_TR_DIR;
+		  break;
+		}
+		case GRUB_USB_TRANSFER_TYPE_SETUP:
+		  break;
+	    }
+	  if (grub_xhci_transfer_is_last(transfer, i))
+	    flags |= TRB_TR_IOC;
+
+	  /* The ring might be to small, submit while adding new entries */
+	  rc = xhci_trb_queue_and_flush(x, priv->slotid, epid,
+				  reqs, tr->data, tr->size, flags);
+	  if (rc < 0)
+	    return GRUB_USB_ERR_TIMEOUT;
+	  else if (rc > 1)
+	    return grub_xhci_usb_to_grub_err(rc);
+
+	}
+    }
+  xhci_doorbell(x, priv->slotid, epid);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+static grub_usb_err_t
+grub_xhci_check_transfer (grub_usb_controller_t dev,
+			  grub_usb_transfer_t transfer, grub_size_t * actual)
+{
+  grub_uint32_t status;
+  grub_uint32_t remaining;
+  grub_uint8_t epid;
+  volatile struct grub_xhci_ring *reqs;
+  grub_usb_err_t err;
+  int rc;
+
+  if (!dev->data || !transfer->controller_data || !transfer->dev ||
+      !transfer->dev->xhci_priv)
+    return GRUB_USB_ERR_INTERNAL;
+
+
+  struct grub_xhci_priv *priv = transfer->dev->xhci_priv;
+  struct grub_xhci *x = (struct grub_xhci *) dev->data;
+  struct grub_xhci_transfer_controller_data *cdata =
+    transfer->controller_data;
+
+  xhci_check_status(x);
+  xhci_process_events(x);
+
+  epid = grub_xhci_epid_from_transfer(transfer);
+
+  reqs = priv->enpoint_trbs[epid];
+
+  /* XXX: invalidate caches */
+
+  /* Get current status from event ring buffer */
+  status = (reqs->evt.status>> 24) & 0xff;
+  remaining = reqs->evt.status & 0xffffff;
+
+  if (status != CC_STOPPED_LENGTH_INVALID)
+      *actual = cdata->transfer_size - remaining;
+  else
+    *actual = 0;
+
+  if (xhci_ring_busy(reqs))
+      return GRUB_USB_ERR_WAIT;
+
+  grub_free(cdata);
+
+  grub_dprintf("xhci", "%s: xfer done\n", __func__);
+
+  err = grub_xhci_usb_to_grub_err(status);
+  if (err != GRUB_USB_ERR_NONE)
+    {
+      if (status == CC_STALL_ERROR)
+	{
+	  /* Clear the stall by resetting the endpoint */
+	  rc = xhci_cmd_reset_endpoint(x, priv->slotid, epid, 1);
+
+	  if (rc < 0)
+	    return GRUB_USB_ERR_TIMEOUT;
+
+	  return GRUB_USB_ERR_STALL;
+	}
+      else if (remaining > 0)
+	{
+	  return GRUB_USB_ERR_DATA;
+	}
+    }
+
+  return err;
+}
+
+static grub_usb_err_t
+grub_xhci_cancel_transfer (grub_usb_controller_t dev,
+			grub_usb_transfer_t transfer)
+{
+  grub_uint8_t epid;
+  volatile struct grub_xhci_ring *reqs;
+  struct grub_pci_dma_chunk *enpoint_trbs_dma;
+  grub_addr_t deque_pointer;
+  int rc;
+
+  if (!dev->data || !transfer->controller_data || !transfer->dev ||
+      !transfer->dev->xhci_priv)
+      return GRUB_USB_ERR_INTERNAL;
+
+  struct grub_xhci *x = (struct grub_xhci *) dev->data;
+  struct grub_xhci_transfer_controller_data *cdata =
+    transfer->controller_data;
+  struct grub_xhci_priv *priv = transfer->dev->xhci_priv;
+
+  epid = grub_xhci_epid_from_transfer(transfer);
+
+  enpoint_trbs_dma = priv->enpoint_trbs_dma[epid];
+  reqs = priv->enpoint_trbs[epid];
+
+  /* Abort current command */
+  rc = xhci_cmd_stop_endpoint(x, priv->slotid, epid, 0);
+  if (rc < 0)
+    return GRUB_USB_ERR_TIMEOUT;
+
+  /* Reset state */
+  reqs->nidx = 0;
+  reqs->eidx = 0;
+  reqs->cs = 1;
+
+  grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
+
+  /* Reset the dequeue pointer to the begging of the TRB */
+  deque_pointer = grub_dma_get_phys(enpoint_trbs_dma);
+  rc = xhci_cmd_set_dequeue_pointer(x, priv->slotid, epid, deque_pointer| 1 );
+  if (rc < 0)
+    return GRUB_USB_ERR_TIMEOUT;
+
+  reqs->evt.ptr_low = 0;
+  reqs->evt.ptr_high = 0;
+  reqs->evt.control = 0;
+  reqs->evt.status = 0;
+
+  grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
+
+  /* Restart ring buffer processing */
+  xhci_doorbell(x, priv->slotid, epid);
+
+  grub_free (cdata);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+/****************************************************************
+ * xHCI port status functions
+ ****************************************************************/
+
+static int
+grub_xhci_hubports (grub_usb_controller_t dev)
+{
+  struct grub_xhci *x = (struct grub_xhci *) dev->data;
+  grub_uint32_t portinfo;
+
+  portinfo = x->ports;
+  grub_dprintf ("xhci", "root hub ports=%d\n", portinfo);
+  return portinfo;
+}
+
+static grub_usb_err_t
+grub_xhci_portstatus (grub_usb_controller_t dev,
+			  unsigned int port, unsigned int enable)
+{
+  struct grub_xhci *x = (struct grub_xhci *) dev->data;
+  grub_uint32_t portsc, pls;
+  grub_uint32_t end;
+
+  portsc = grub_xhci_port_read(x, port);
+  pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
+
+  grub_dprintf("xhci", "grub_xhci_portstatus port #%d: 0x%08x,%s%s pls %d enable %d\n",
+	       port, portsc,
+	       (portsc & GRUB_XHCI_PORTSC_PP)  ? " powered," : "",
+	       (portsc & GRUB_XHCI_PORTSC_PED) ? " enabled," : "",
+	       pls, enable);
+  xhci_check_status(x);
+
+  if ((enable && (portsc & GRUB_XHCI_PORTSC_PED)) ||
+      (!enable && !(portsc & GRUB_XHCI_PORTSC_PED)))
+    return GRUB_USB_ERR_NONE;
+
+  if (!enable)
+    {
+      /* Disable port */
+      grub_xhci_port_write(x, port, ~0, GRUB_XHCI_PORTSC_PED);
+      return GRUB_USB_ERR_NONE;
+    }
+
+  grub_dprintf ("xhci", "portstatus: XHCI STATUS: %08x\n",
+		grub_xhci_read32(&x->op->usbsts));
+  grub_dprintf ("xhci",
+		"portstatus: begin, iobase=%p, port=%d, status=0x%08x\n",
+		x->caps, port, portsc);
+
+  switch (pls)
+    {
+      case PLS_U0:
+	/* A USB3 port - controller automatically performs reset */
+	break;
+      case PLS_POLLING:
+	/* A USB2 port - perform device reset */
+	grub_xhci_port_write(x, port, ~GRUB_XHCI_PORTSC_PED, GRUB_XHCI_PORTSC_PR);
+	break;
+      default:
+        return GRUB_USB_ERR_NONE;
+    }
+
+  /* Wait for device to complete reset and be enabled */
+  end = grub_get_time_ms () + 100;
+  for (;;)
+    {
+      portsc = grub_xhci_port_read(x, port);
+      if (!(portsc & GRUB_XHCI_PORTSC_CCS))
+	{
+	  /* Device disconnected during reset */
+	  grub_dprintf ("xhci","ERROR: %s device disconnected\n", __func__);
+	  return GRUB_USB_ERR_BADDEVICE;
+	}
+      if (portsc & GRUB_XHCI_PORTSC_PED)
+	  /* Reset complete */
+	  break;
+      if (grub_get_time_ms () > end)
+	{
+	  grub_dprintf ("xhci","ERROR: %s TIMEOUT\n", __func__);
+	  return GRUB_USB_ERR_TIMEOUT;
+	}
+    }
+  xhci_check_status(x);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+/****************************************************************
+ * xHCI detect device functions
+ ****************************************************************/
+
+static grub_usb_speed_t
+grub_xhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
+{
+  struct grub_xhci *x = (struct grub_xhci *) dev->data;
+  grub_uint32_t portsc, speed;
+
+  *changed = 0;
+  grub_dprintf("xhci", "%s: dev=%p USB%d_%d port %d\n", __func__, dev,
+	       x->psids[port-1].major, x->psids[port-1].minor, port);
+
+  /* On shutdown advertise all ports as disconnected. This will trigger
+   * a gracefull detatch. */
+  if (x->shutdown)
+    {
+      *changed = 1;
+      return GRUB_USB_SPEED_NONE;
+    }
+
+  /* Don't advertise new devices, connecting will fail if halted */
+  if (xhci_is_halted(x))
+    return GRUB_USB_SPEED_NONE;
+
+  portsc = grub_xhci_port_read(x, port);
+  speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED);
+  grub_uint8_t pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
+
+  grub_dprintf("xhci", "grub_xhci_portstatus port #%d: 0x%08x,%s%s pls %d\n",
+	       port, portsc,
+	       (portsc & GRUB_XHCI_PORTSC_PP)  ? " powered," : "",
+	       (portsc & GRUB_XHCI_PORTSC_PED) ? " enabled," : "",
+	       pls);
+
+  /* Connect Status Change bit - it detects change of connection */
+  if (portsc & GRUB_XHCI_PORTSC_CSC)
+    {
+      *changed = 1;
+
+      grub_xhci_port_write(x, port, ~GRUB_XHCI_PORTSC_PED, GRUB_XHCI_PORTSC_CSC);
+    }
+
+  if (!(portsc & GRUB_XHCI_PORTSC_CCS))
+    return GRUB_USB_SPEED_NONE;
+
+  for (grub_uint8_t i = 0; i < 16 && x->psids[port-1].psids[i].id > 0; i++)
+    {
+      if (x->psids[port-1].psids[i].id == speed)
+        {
+	  grub_dprintf("xhci", "%s: grub_usb_speed = %d\n", __func__,
+		       x->psids[port-1].psids[i].grub_usb_speed );
+	  return x->psids[port-1].psids[i].grub_usb_speed;
+	}
+    }
+
+  return GRUB_USB_SPEED_NONE;
+}
+
+/****************************************************************
+ * xHCI attach/detach functions
+ ****************************************************************/
+
+static grub_usb_err_t
+grub_xhci_attach_dev (grub_usb_controller_t ctrl, grub_usb_device_t dev)
+{
+  struct grub_xhci *x = (struct grub_xhci *) ctrl->data;
+  grub_usb_err_t err;
+  grub_uint32_t max;
+
+  grub_dprintf("xhci", "%s: dev=%p\n", __func__, dev);
+
+  if (!dev || !x)
+    return GRUB_USB_ERR_INTERNAL;
+
+  dev->xhci_priv = grub_zalloc (sizeof (struct grub_xhci_priv));
+  if (!dev->xhci_priv)
+    return GRUB_USB_ERR_INTERNAL;
+
+
+  switch (dev->speed)
+    {
+      case GRUB_USB_SPEED_LOW:
+	{
+	  max = 8;
+	  break;
+	}
+      case GRUB_USB_SPEED_FULL:
+      case GRUB_USB_SPEED_HIGH:
+	{
+	  max = 64;
+	  break;
+	}
+      case GRUB_USB_SPEED_SUPER:
+	{
+	  max = 512;
+	  break;
+	}
+      default:
+      case GRUB_USB_SPEED_NONE:
+	{
+	  max = 0;
+	}
+    }
+
+  /* Assign a slot, assign an address and configure endpoint 0 */
+  err = grub_xhci_prepare_endpoint(x, dev,
+				  0,
+				  0,
+				  GRUB_USB_TRANSACTION_TYPE_CONTROL,
+				  max,
+				  dev->xhci_priv);
+
+  return err;
+}
+
+static grub_usb_err_t
+grub_xhci_detach_dev (grub_usb_controller_t ctrl, grub_usb_device_t dev)
+{
+  struct grub_xhci *x = (struct grub_xhci *) ctrl->data;
+  struct grub_xhci_priv *priv;
+  int cc = CC_SUCCESS;
+
+  grub_dprintf("xhci", "%s: dev=%p\n", __func__, dev);
+
+  if (!dev)
+    return GRUB_USB_ERR_INTERNAL;
+
+  if (dev->xhci_priv)
+    {
+      priv = dev->xhci_priv;
+      /* Stop endpoints and free ring buffer */
+      for (int i = 0; i < GRUB_XHCI_MAX_ENDPOINTS; i++)
+	{
+	  if (priv->enpoint_trbs[i] != NULL)
+	    {
+	      cc = xhci_cmd_stop_endpoint(x, priv->slotid, i, 1);
+	      if (cc != CC_SUCCESS)
+		grub_dprintf("xhci", "Failed to disable EP%d on slot %d\n", i,
+			     priv->slotid);
+
+	      grub_dprintf("xhci", "grub_dma_free[%d]\n", i);
+
+	      grub_dma_free(priv->enpoint_trbs_dma[i]);
+	      priv->enpoint_trbs[i] = NULL;
+	      priv->enpoint_trbs_dma[i] = NULL;
+	    }
+	}
+
+      cc = xhci_cmd_disable_slot(x, priv->slotid);
+      if (cc == CC_SUCCESS)
+	{
+	  if (priv->slotctx_dma)
+	    grub_dma_free(priv->slotctx_dma);
+	  x->devs[priv->slotid].ptr_low = 0;
+	  x->devs[priv->slotid].ptr_high = 0;
+	  grub_arch_sync_dma_caches(&x->devs[priv->slotid], sizeof(x->devs[0]));
+	}
+	else
+	  grub_dprintf("xhci", "Failed to disable slot %d\n", priv->slotid);
+
+      grub_free(dev->xhci_priv);
+    }
+
+  dev->xhci_priv = NULL;
+
+  if (cc != CC_SUCCESS)
+    return GRUB_USB_ERR_BADDEVICE;
+  return GRUB_USB_ERR_NONE;
+}
+
+/****************************************************************
+ * xHCI terminate functions
+ ****************************************************************/
+
+static void
+grub_xhci_halt(struct grub_xhci *x)
+{
+  grub_uint32_t reg;
+
+  /* Halt the command ring */
+  reg = grub_xhci_read32(&x->op->crcr_low);
+  grub_xhci_write32(&x->op->crcr_low, reg | 4);
+
+  int rc = xhci_event_wait(x, x->cmds, 100);
+  grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
+  if (rc < 0)
+    return;
+
+  /* Stop the controller */
+  reg = grub_xhci_read32(&x->op->usbcmd);
+  if (reg & GRUB_XHCI_CMD_RS)
+    {
+      reg &= ~GRUB_XHCI_CMD_RS;
+      grub_xhci_write32(&x->op->usbcmd, reg);
+    }
+
+  return;
+}
+
+static grub_err_t
+grub_xhci_fini_hw (int noreturn __attribute__ ((unused)))
+{
+  struct grub_xhci *x;
+
+  /* We should disable all XHCI HW to prevent any DMA access etc. */
+  for (x = xhci; x; x = x->next)
+    {
+      x->shutdown = 1;
+
+      /* Gracefully detach active devices */
+      grub_usb_poll_devices(0);
+
+      /* Check if xHCI is halted and halt it if not */
+      grub_xhci_halt (x);
+
+      /* Reset xHCI */
+      if (grub_xhci_reset (x) != GRUB_USB_ERR_NONE)
+	return GRUB_ERR_BAD_DEVICE;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static struct grub_usb_controller_dev usb_controller = {
+  .name = "xhci",
+  .iterate = grub_xhci_iterate,
+  .setup_transfer = grub_xhci_setup_transfer,
+  .check_transfer = grub_xhci_check_transfer,
+  .cancel_transfer = grub_xhci_cancel_transfer,
+  .hubports = grub_xhci_hubports,
+  .portstatus = grub_xhci_portstatus,
+  .detect_dev = grub_xhci_detect_dev,
+  .attach_dev = grub_xhci_attach_dev,
+  .detach_dev = grub_xhci_detach_dev,
+  /* estimated max. count of TDs for one bulk transfer */
+  .max_bulk_tds = GRUB_XHCI_RING_ITEMS - 3
+};
+
+GRUB_MOD_INIT (xhci)
+{
+  grub_stop_disk_firmware ();
+
+  grub_boot_time ("Initing XHCI hardware");
+  grub_xhci_pci_scan ();
+  grub_boot_time ("Registering XHCI driver");
+  grub_usb_controller_dev_register (&usb_controller);
+  grub_boot_time ("XHCI driver registered");
+}
+
+GRUB_MOD_FINI (xhci)
+{
+  grub_xhci_fini_hw (0);
+  grub_usb_controller_dev_unregister (&usb_controller);
+}
diff --git a/include/grub/usb.h b/include/grub/usb.h
index 5a3325b7d..ac0f8c141 100644
--- a/include/grub/usb.h
+++ b/include/grub/usb.h
@@ -334,6 +334,10 @@ grub_usb_cancel_transfer (grub_usb_transfer_t trans);
 void
 grub_ehci_init_device (volatile void *regs);
 void
+grub_xhci_init_device (volatile void *regs);
+void
 grub_ehci_pci_scan (void);
+void
+grub_xhci_pci_scan (void);
 
 #endif /* GRUB_USB_H */
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v2 7/7] grub-core/bus/usb/usbhub: Add xHCI non root hub support
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
                   ` (5 preceding siblings ...)
  2020-12-07  7:41 ` [PATCH v2 6/7] grub-core/bus/usb: Add xhci support Patrick Rudolph
@ 2020-12-07  7:41 ` Patrick Rudolph
  2021-02-19 18:11 ` [PATCH v2 0/7] Add xHCI USB support Glenn Washburn
  7 siblings, 0 replies; 12+ messages in thread
From: Patrick Rudolph @ 2020-12-07  7:41 UTC (permalink / raw)
  To: grub-devel; +Cc: Patrick Rudolph

Tested on Intel PCH C246, the USB3 hub can be configured by grub.

Issues:
* USB3 devices connected behind that hub are sometimes not detected.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 grub-core/bus/usb/usbhub.c | 40 ++++++++++++++++++++++++++++++++------
 include/grub/usbdesc.h     |  1 +
 include/grub/usbtrans.h    |  4 ++++
 3 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
index 63bee1d1a..841796f2f 100644
--- a/grub-core/bus/usb/usbhub.c
+++ b/grub-core/bus/usb/usbhub.c
@@ -148,19 +148,32 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
   return dev;
 }
 
-\f
+static grub_usb_err_t
+grub_usb_set_hub_depth(grub_usb_device_t dev, grub_uint8_t depth)
+{
+  return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
+			            | GRUB_USB_REQTYPE_CLASS
+			            | GRUB_USB_REQTYPE_TARGET_DEV),
+			      GRUB_USB_HUB_REQ_SET_HUB_DEPTH, depth,
+			      0, 0, NULL);
+}
+
 static grub_usb_err_t
 grub_usb_add_hub (grub_usb_device_t dev)
 {
   struct grub_usb_usb_hubdesc hubdesc;
   grub_usb_err_t err;
+  grub_uint16_t req;
   int i;
-  
+
+  req = (dev->speed == GRUB_USB_SPEED_SUPER) ? GRUB_USB_DESCRIPTOR_SS_HUB :
+    GRUB_USB_DESCRIPTOR_HUB;
+
   err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 	  		            | GRUB_USB_REQTYPE_CLASS
 			            | GRUB_USB_REQTYPE_TARGET_DEV),
-                              GRUB_USB_REQ_GET_DESCRIPTOR,
-			      (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
+			      GRUB_USB_REQ_GET_DESCRIPTOR,
+			      (req << 8) | 0,
 			      0, sizeof (hubdesc), (char *) &hubdesc);
   if (err)
     return err;
@@ -183,6 +196,19 @@ grub_usb_add_hub (grub_usb_device_t dev)
       return GRUB_USB_ERR_INTERNAL;
     }
 
+  if (dev->speed == GRUB_USB_SPEED_SUPER)
+    {
+      grub_uint8_t depth;
+      grub_uint32_t route;
+      /* Depth maximum value is 5, but root hubs doesn't count */
+      for (depth = 0, route = dev->route; (route & 0xf) > 0; route >>= 4)
+	depth++;
+
+      err = grub_usb_set_hub_depth(dev, depth);
+      if (err)
+        return err;
+    }
+
   /* Power on all Hub ports.  */
   for (i = 1; i <= hubdesc.portcnt; i++)
     {
@@ -637,7 +663,9 @@ poll_nonroot_hub (grub_usb_device_t dev)
 	      int split_hubaddr = 0;
 
 	      /* Determine the device speed.  */
-	      if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
+	      if (dev->speed == GRUB_USB_SPEED_SUPER)
+	        speed = GRUB_USB_SPEED_SUPER;
+	      else if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
 		speed = GRUB_USB_SPEED_LOW;
 	      else
 		{
@@ -651,7 +679,7 @@ poll_nonroot_hub (grub_usb_device_t dev)
 	      grub_millisleep (10);
 
               /* Find correct values for SPLIT hubport and hubaddr */
-	      if (speed == GRUB_USB_SPEED_HIGH)
+	      if (speed == GRUB_USB_SPEED_HIGH || speed == GRUB_USB_SPEED_SUPER)
 	        {
 		  /* HIGH speed device needs not transaction translation */
 		  split_hubport = 0;
diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h
index bb2ab2e27..1697aa465 100644
--- a/include/grub/usbdesc.h
+++ b/include/grub/usbdesc.h
@@ -30,6 +30,7 @@ typedef enum {
   GRUB_USB_DESCRIPTOR_ENDPOINT,
   GRUB_USB_DESCRIPTOR_DEBUG = 10,
   GRUB_USB_DESCRIPTOR_HUB = 0x29,
+  GRUB_USB_DESCRIPTOR_SS_HUB = 0x2a,
   GRUB_USB_DESCRIPTOR_SS_ENDPOINT_COMPANION = 0x30
 } grub_usb_descriptor_t;
 
diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h
index aef482cb9..a5891e663 100644
--- a/include/grub/usbtrans.h
+++ b/include/grub/usbtrans.h
@@ -110,6 +110,10 @@ enum
     GRUB_USB_REQ_SET_INTERFACE = 0x0B,
     GRUB_USB_REQ_SYNC_FRAME = 0x0C
   };
+enum
+  {
+    GRUB_USB_HUB_REQ_SET_HUB_DEPTH = 0x0C,
+  };
 
 #define GRUB_USB_FEATURE_ENDP_HALT	0x00
 #define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 0/7] Add xHCI USB support
  2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
                   ` (6 preceding siblings ...)
  2020-12-07  7:41 ` [PATCH v2 7/7] grub-core/bus/usb/usbhub: Add xHCI non root hub support Patrick Rudolph
@ 2021-02-19 18:11 ` Glenn Washburn
  2021-02-24  6:46   ` Patrick Rudolph
  7 siblings, 1 reply; 12+ messages in thread
From: Glenn Washburn @ 2021-02-19 18:11 UTC (permalink / raw)
  To: Patrick Rudolph; +Cc: The development of GNU GRUB

Hi Patrick,

Thanks for the contribution. I think this would be a great addition to
GRUB. However, there are a few issues I see at the moment.

On Mon,  7 Dec 2020 08:41:20 +0100
Patrick Rudolph <patrick.rudolph@9elements.com> wrote:

> Add basic support for xHCI USB controllers and non root xHCI hubs.
> The motivation is to use this code on platforms that do not provide
> user input by runtime services (like BIOS or UEFI platform) do.
> This is the case when GRUB is used as coreboot payload for example.
> 
> The code is based on seabios implementation, but has been heavily
> modified to match grubs internals.

Are these the same changes as in https://github.com/Nitrokey/grub.git
referenced in a previous email? I'm asking because there were some
comments that were unaddressed. So, if it is, could you please address
Thomas's email (I haven't checked to see if they are still relevant if
so)? (see: https://marc.info/?l=grub-devel&m=159769164332293)

Searching in the mailing list, I found this attempt in early 2017.
Having not looked at the code at all, could this help in addressing
some of Thomas's concerns?
https://github.com/bjornfor/grub/tree/add-coreboot-xhci-driver-2nd-attempt-v2

I've run your code through the GitLab CI I've configured, and there was
a build failure on x86_64-efi. Looks like some implicit type casting
issues. I believe it failed for both gcc 8.1 and 10.1. Here is the
error log when using gcc 10.1:
https://gitlab.com/gwashburn/grub/-/jobs/1025317154#L594

Also, I think that any xhci support should be accompanied by passing
make check tests.  See uhci_test for an example of how this might be
done. Since you've already tested on qemu, I think you're 90% of the
way there in making some tests. Both of the ones listed before would be
nice. If you'd like some guidance or help, let me know.

Glenn

> Changes done in version 2:
>  * Code cleanup
>  * Code style fixes
>  * Don't leak memory buffers
>  * Compile without warnings
>  * Add more defines
>  * Add more helper functions
>  * Don't assume a 1:1 virtual to physical mapping
>  * Flush cachelines after writing buffers
>  * Don't use hardcoded page size
>  * Proper scratchpad register setup
>  * xHCI bios ownership handoff
> 
> Changes done in version 3:
>  * Several bug fixes for real hardware
>  * Fixed a race condition detecting events, which doesn't appear on
>    qemu based xHCI controllers
>  * Don't accidently disable USB3.0 devices after first command
>  * Support arbitrary protocol speed IDs
>  * Coding style cleanup
>  * Support for non root SuperSpeed hubs
> 
> Changes Tested:
>  * Qemu system x86_64
>    * virtual USB HID keyboard (usb-kbd)
>    * virtual USB HID mass storage (usb-storage)
>  * Intel C246 integrated xHCI (Supermicro X11SSH-F)
>    * iKVM HID keyboard
>    * USB3 HID mass storage (controller root port)
>    * External USB HID keyboard
> 
> TODO:
>  * Test on more hardware
>  * Test on USB3 hubs
>  * Support for USB 3.1 and USB 3.2 controllers
> 
> Patrick Rudolph (7):
>   grub-core/bus/usb: Parse SuperSpeed companion descriptors
>   usb: Add enum for xHCI
>   usbtrans: Set default maximum packet size
>   grub-core/bus/usb: Add function pointer for attach/detach events
>   grub-core/bus/usb/usbhub: Add new private fields for xHCI controller
>   grub-core/bus/usb: Add xhci support
>   grub-core/bus/usb/usbhub: Add xHCI non root hub support
> 
>  Makefile.am                       |    2 +-
>  grub-core/Makefile.core.def       |    7 +
>  grub-core/bus/usb/ehci.c          |    2 +
>  grub-core/bus/usb/ohci.c          |    2 +
>  grub-core/bus/usb/serial/common.c |    2 +-
>  grub-core/bus/usb/uhci.c          |    2 +
>  grub-core/bus/usb/usb.c           |   44 +-
>  grub-core/bus/usb/usbhub.c        |   95 +-
>  grub-core/bus/usb/usbtrans.c      |    6 +-
>  grub-core/bus/usb/xhci-pci.c      |  195 +++
>  grub-core/bus/usb/xhci.c          | 2496
> +++++++++++++++++++++++++++++ grub-core/commands/usbtest.c      |
> 2 +- grub-core/disk/usbms.c            |    2 +-
>  grub-core/term/usb_keyboard.c     |    2 +-
>  include/grub/usb.h                |   18 +-
>  include/grub/usbdesc.h            |   12 +-
>  include/grub/usbtrans.h           |    4 +
>  17 files changed, 2852 insertions(+), 41 deletions(-)
>  create mode 100644 grub-core/bus/usb/xhci-pci.c
>  create mode 100644 grub-core/bus/usb/xhci.c
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 0/7] Add xHCI USB support
  2021-02-19 18:11 ` [PATCH v2 0/7] Add xHCI USB support Glenn Washburn
@ 2021-02-24  6:46   ` Patrick Rudolph
  2021-02-26 16:53     ` Glenn Washburn
  0 siblings, 1 reply; 12+ messages in thread
From: Patrick Rudolph @ 2021-02-24  6:46 UTC (permalink / raw)
  To: development; +Cc: The development of GNU GRUB

Hi Glenn,
yes it's the same patch series, but has been cleaned and improved a lot.
I've addressed most of the comments received earlier.

As stated in the commit message, USB 3.1 and USB 3.2 are not
tested, as I lack hardware to test this. I'm not going to look into this
any further into this as it works fine with USB 3.0 compliant xhci controllers.

I'l fix the remaining issues and publish a new patch series.

Kind regards,
Patrick Rudolph

On Fri, Feb 19, 2021 at 7:11 PM Glenn Washburn
<development@efficientek.com> wrote:
>
> Hi Patrick,
>
> Thanks for the contribution. I think this would be a great addition to
> GRUB. However, there are a few issues I see at the moment.
>
> On Mon,  7 Dec 2020 08:41:20 +0100
> Patrick Rudolph <patrick.rudolph@9elements.com> wrote:
>
> > Add basic support for xHCI USB controllers and non root xHCI hubs.
> > The motivation is to use this code on platforms that do not provide
> > user input by runtime services (like BIOS or UEFI platform) do.
> > This is the case when GRUB is used as coreboot payload for example.
> >
> > The code is based on seabios implementation, but has been heavily
> > modified to match grubs internals.
>
> Are these the same changes as in https://github.com/Nitrokey/grub.git
> referenced in a previous email? I'm asking because there were some
> comments that were unaddressed. So, if it is, could you please address
> Thomas's email (I haven't checked to see if they are still relevant if
> so)? (see: https://marc.info/?l=grub-devel&m=159769164332293)
>
> Searching in the mailing list, I found this attempt in early 2017.
> Having not looked at the code at all, could this help in addressing
> some of Thomas's concerns?
> https://github.com/bjornfor/grub/tree/add-coreboot-xhci-driver-2nd-attempt-v2
>
> I've run your code through the GitLab CI I've configured, and there was
> a build failure on x86_64-efi. Looks like some implicit type casting
> issues. I believe it failed for both gcc 8.1 and 10.1. Here is the
> error log when using gcc 10.1:
> https://gitlab.com/gwashburn/grub/-/jobs/1025317154#L594
>
> Also, I think that any xhci support should be accompanied by passing
> make check tests.  See uhci_test for an example of how this might be
> done. Since you've already tested on qemu, I think you're 90% of the
> way there in making some tests. Both of the ones listed before would be
> nice. If you'd like some guidance or help, let me know.
>
> Glenn
>
> > Changes done in version 2:
> >  * Code cleanup
> >  * Code style fixes
> >  * Don't leak memory buffers
> >  * Compile without warnings
> >  * Add more defines
> >  * Add more helper functions
> >  * Don't assume a 1:1 virtual to physical mapping
> >  * Flush cachelines after writing buffers
> >  * Don't use hardcoded page size
> >  * Proper scratchpad register setup
> >  * xHCI bios ownership handoff
> >
> > Changes done in version 3:
> >  * Several bug fixes for real hardware
> >  * Fixed a race condition detecting events, which doesn't appear on
> >    qemu based xHCI controllers
> >  * Don't accidently disable USB3.0 devices after first command
> >  * Support arbitrary protocol speed IDs
> >  * Coding style cleanup
> >  * Support for non root SuperSpeed hubs
> >
> > Changes Tested:
> >  * Qemu system x86_64
> >    * virtual USB HID keyboard (usb-kbd)
> >    * virtual USB HID mass storage (usb-storage)
> >  * Intel C246 integrated xHCI (Supermicro X11SSH-F)
> >    * iKVM HID keyboard
> >    * USB3 HID mass storage (controller root port)
> >    * External USB HID keyboard
> >
> > TODO:
> >  * Test on more hardware
> >  * Test on USB3 hubs
> >  * Support for USB 3.1 and USB 3.2 controllers
> >
> > Patrick Rudolph (7):
> >   grub-core/bus/usb: Parse SuperSpeed companion descriptors
> >   usb: Add enum for xHCI
> >   usbtrans: Set default maximum packet size
> >   grub-core/bus/usb: Add function pointer for attach/detach events
> >   grub-core/bus/usb/usbhub: Add new private fields for xHCI controller
> >   grub-core/bus/usb: Add xhci support
> >   grub-core/bus/usb/usbhub: Add xHCI non root hub support
> >
> >  Makefile.am                       |    2 +-
> >  grub-core/Makefile.core.def       |    7 +
> >  grub-core/bus/usb/ehci.c          |    2 +
> >  grub-core/bus/usb/ohci.c          |    2 +
> >  grub-core/bus/usb/serial/common.c |    2 +-
> >  grub-core/bus/usb/uhci.c          |    2 +
> >  grub-core/bus/usb/usb.c           |   44 +-
> >  grub-core/bus/usb/usbhub.c        |   95 +-
> >  grub-core/bus/usb/usbtrans.c      |    6 +-
> >  grub-core/bus/usb/xhci-pci.c      |  195 +++
> >  grub-core/bus/usb/xhci.c          | 2496
> > +++++++++++++++++++++++++++++ grub-core/commands/usbtest.c      |
> > 2 +- grub-core/disk/usbms.c            |    2 +-
> >  grub-core/term/usb_keyboard.c     |    2 +-
> >  include/grub/usb.h                |   18 +-
> >  include/grub/usbdesc.h            |   12 +-
> >  include/grub/usbtrans.h           |    4 +
> >  17 files changed, 2852 insertions(+), 41 deletions(-)
> >  create mode 100644 grub-core/bus/usb/xhci-pci.c
> >  create mode 100644 grub-core/bus/usb/xhci.c
> >


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 0/7] Add xHCI USB support
  2021-02-24  6:46   ` Patrick Rudolph
@ 2021-02-26 16:53     ` Glenn Washburn
  0 siblings, 0 replies; 12+ messages in thread
From: Glenn Washburn @ 2021-02-26 16:53 UTC (permalink / raw)
  To: Patrick Rudolph; +Cc: The development of GNU GRUB

Good day Patrick,

On Wed, 24 Feb 2021 07:46:48 +0100
Patrick Rudolph <patrick.rudolph@9elements.com> wrote:

> Hi Glenn,
> yes it's the same patch series, but has been cleaned and improved a
> lot. I've addressed most of the comments received earlier.

I see that I implied that you hadn't and I didn't mean to. I only meant
to suggest that there were possible unaddressed issues. Glad to hear
those were already taken care of.

> As stated in the commit message, USB 3.1 and USB 3.2 are not
> tested, as I lack hardware to test this. I'm not going to look into
> this any further into this as it works fine with USB 3.0 compliant
> xhci controllers.
> 
> I'l fix the remaining issues and publish a new patch series.

Great. Are you thinking of adding in some tests? If not, I would
appreciate knowing the qemu opts used to create the virtual machine (I'm
unsure how to turn on xHCI in qemu).

Glenn

> 
> On Fri, Feb 19, 2021 at 7:11 PM Glenn Washburn
> <development@efficientek.com> wrote:
> >
> > Hi Patrick,
> >
> > Thanks for the contribution. I think this would be a great addition
> > to GRUB. However, there are a few issues I see at the moment.
> >
> > On Mon,  7 Dec 2020 08:41:20 +0100
> > Patrick Rudolph <patrick.rudolph@9elements.com> wrote:
> >
> > > Add basic support for xHCI USB controllers and non root xHCI hubs.
> > > The motivation is to use this code on platforms that do not
> > > provide user input by runtime services (like BIOS or UEFI
> > > platform) do. This is the case when GRUB is used as coreboot
> > > payload for example.
> > >
> > > The code is based on seabios implementation, but has been heavily
> > > modified to match grubs internals.
> >
> > Are these the same changes as in
> > https://github.com/Nitrokey/grub.git referenced in a previous
> > email? I'm asking because there were some comments that were
> > unaddressed. So, if it is, could you please address Thomas's email
> > (I haven't checked to see if they are still relevant if so)? (see:
> > https://marc.info/?l=grub-devel&m=159769164332293)
> >
> > Searching in the mailing list, I found this attempt in early 2017.
> > Having not looked at the code at all, could this help in addressing
> > some of Thomas's concerns?
> > https://github.com/bjornfor/grub/tree/add-coreboot-xhci-driver-2nd-attempt-v2
> >
> > I've run your code through the GitLab CI I've configured, and there
> > was a build failure on x86_64-efi. Looks like some implicit type
> > casting issues. I believe it failed for both gcc 8.1 and 10.1. Here
> > is the error log when using gcc 10.1:
> > https://gitlab.com/gwashburn/grub/-/jobs/1025317154#L594
> >
> > Also, I think that any xhci support should be accompanied by passing
> > make check tests.  See uhci_test for an example of how this might be
> > done. Since you've already tested on qemu, I think you're 90% of the
> > way there in making some tests. Both of the ones listed before
> > would be nice. If you'd like some guidance or help, let me know.
> >
> > Glenn
> >
> > > Changes done in version 2:
> > >  * Code cleanup
> > >  * Code style fixes
> > >  * Don't leak memory buffers
> > >  * Compile without warnings
> > >  * Add more defines
> > >  * Add more helper functions
> > >  * Don't assume a 1:1 virtual to physical mapping
> > >  * Flush cachelines after writing buffers
> > >  * Don't use hardcoded page size
> > >  * Proper scratchpad register setup
> > >  * xHCI bios ownership handoff
> > >
> > > Changes done in version 3:
> > >  * Several bug fixes for real hardware
> > >  * Fixed a race condition detecting events, which doesn't appear
> > > on qemu based xHCI controllers
> > >  * Don't accidently disable USB3.0 devices after first command
> > >  * Support arbitrary protocol speed IDs
> > >  * Coding style cleanup
> > >  * Support for non root SuperSpeed hubs
> > >
> > > Changes Tested:
> > >  * Qemu system x86_64
> > >    * virtual USB HID keyboard (usb-kbd)
> > >    * virtual USB HID mass storage (usb-storage)
> > >  * Intel C246 integrated xHCI (Supermicro X11SSH-F)
> > >    * iKVM HID keyboard
> > >    * USB3 HID mass storage (controller root port)
> > >    * External USB HID keyboard
> > >
> > > TODO:
> > >  * Test on more hardware
> > >  * Test on USB3 hubs
> > >  * Support for USB 3.1 and USB 3.2 controllers
> > >
> > > Patrick Rudolph (7):
> > >   grub-core/bus/usb: Parse SuperSpeed companion descriptors
> > >   usb: Add enum for xHCI
> > >   usbtrans: Set default maximum packet size
> > >   grub-core/bus/usb: Add function pointer for attach/detach events
> > >   grub-core/bus/usb/usbhub: Add new private fields for xHCI
> > > controller grub-core/bus/usb: Add xhci support
> > >   grub-core/bus/usb/usbhub: Add xHCI non root hub support
> > >
> > >  Makefile.am                       |    2 +-
> > >  grub-core/Makefile.core.def       |    7 +
> > >  grub-core/bus/usb/ehci.c          |    2 +
> > >  grub-core/bus/usb/ohci.c          |    2 +
> > >  grub-core/bus/usb/serial/common.c |    2 +-
> > >  grub-core/bus/usb/uhci.c          |    2 +
> > >  grub-core/bus/usb/usb.c           |   44 +-
> > >  grub-core/bus/usb/usbhub.c        |   95 +-
> > >  grub-core/bus/usb/usbtrans.c      |    6 +-
> > >  grub-core/bus/usb/xhci-pci.c      |  195 +++
> > >  grub-core/bus/usb/xhci.c          | 2496
> > > +++++++++++++++++++++++++++++ grub-core/commands/usbtest.c      |
> > > 2 +- grub-core/disk/usbms.c            |    2 +-
> > >  grub-core/term/usb_keyboard.c     |    2 +-
> > >  include/grub/usb.h                |   18 +-
> > >  include/grub/usbdesc.h            |   12 +-
> > >  include/grub/usbtrans.h           |    4 +
> > >  17 files changed, 2852 insertions(+), 41 deletions(-)
> > >  create mode 100644 grub-core/bus/usb/xhci-pci.c
> > >  create mode 100644 grub-core/bus/usb/xhci.c
> > >


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors
  2020-12-07  7:41 ` [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors Patrick Rudolph
@ 2021-03-19  1:06   ` Glenn Washburn
  0 siblings, 0 replies; 12+ messages in thread
From: Glenn Washburn @ 2021-03-19  1:06 UTC (permalink / raw)
  To: Patrick Rudolph; +Cc: The development of GNU GRUB

Hi Patrick,

I noticed some style issues while rebasing this code. Also this patch
series now looks like it'll need a touch up when rebasing on to the
release candidate (you might want to wait until the actual release
comes out to send the next version).

On Mon,  7 Dec 2020 08:41:21 +0100
Patrick Rudolph <patrick.rudolph@9elements.com> wrote:

> Parse the SS_ENDPOINT_COMPANION descriptor, which is only present on
> USB 3.0 capable devices and xHCI controllers. Make the descendp an
> array of pointers to the endpoint descriptor as it's no longer an
> continous array.
> 
> Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
> ---
>  grub-core/bus/usb/serial/common.c |  2 +-
>  grub-core/bus/usb/usb.c           | 44
> +++++++++++++++++++------------ grub-core/bus/usb/usbhub.c        |
> 22 ++++++++++++---- grub-core/commands/usbtest.c      |  2 +-
>  grub-core/disk/usbms.c            |  2 +-
>  grub-core/term/usb_keyboard.c     |  2 +-
>  include/grub/usb.h                |  2 +-
>  include/grub/usbdesc.h            | 11 +++++++-
>  8 files changed, 59 insertions(+), 28 deletions(-)
> 
> diff --git a/grub-core/bus/usb/serial/common.c
> b/grub-core/bus/usb/serial/common.c index 8e94c7dc0..63e64cc0a 100644
> --- a/grub-core/bus/usb/serial/common.c
> +++ b/grub-core/bus/usb/serial/common.c
> @@ -72,7 +72,7 @@ grub_usbserial_attach (grub_usb_device_t usbdev,
> int configno, int interfno, for (j = 0; j < interf->endpointcnt; j++)
>      {
>        struct grub_usb_desc_endp *endp;
> -      endp = &usbdev->config[0].interf[interfno].descendp[j];
> +      endp = usbdev->config[0].interf[interfno].descendp[j];
>  
>        if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2
>  	  && (in_endp == GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING
> diff --git a/grub-core/bus/usb/usb.c b/grub-core/bus/usb/usb.c
> index 8da5e4c74..6b32bbd93 100644
> --- a/grub-core/bus/usb/usb.c
> +++ b/grub-core/bus/usb/usb.c
> @@ -115,7 +115,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
>    struct grub_usb_desc_device *descdev;
>    struct grub_usb_desc_config config;
>    grub_usb_err_t err;
> -  int i;
> +  int i, j;
>  
>    /* First we have to read first 8 bytes only and determine
>     * max. size of packet */
> @@ -149,6 +149,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
>        int currif;
>        char *data;
>        struct grub_usb_desc *desc;
> +      struct grub_usb_desc_endp *endp;
>  
>        /* First just read the first 4 bytes of the configuration
>  	 descriptor, after that it is known how many bytes really
> have @@ -192,24 +193,27 @@ grub_usb_device_initialize
> (grub_usb_device_t dev) = (struct grub_usb_desc_if *) &data[pos];
>  	  pos += dev->config[i].interf[currif].descif->length;
>  
> +    dev->config[i].interf[currif].descendp = grub_malloc (
> +            dev->config[i].interf[currif].descif->endpointcnt *
> +            sizeof(struct grub_usb_desc_endp));
> +
> +    j = 0;
>  	  while (pos < config.totallen)
>              {
>                desc = (struct grub_usb_desc *)&data[pos];
> -              if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT)
> -                break;
> -              if (!desc->length)
> -                {
> -                  err = GRUB_USB_ERR_BADDEVICE;
> -                  goto fail;
> -                }
> -              pos += desc->length;
> -            }
> -
> -	  /* Point to the first endpoint.  */
> -	  dev->config[i].interf[currif].descendp
> -	    = (struct grub_usb_desc_endp *) &data[pos];
> -	  pos += (sizeof (struct grub_usb_desc_endp)
> -		  *
> dev->config[i].interf[currif].descif->endpointcnt);
> +              if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT) {
> +                endp = (struct grub_usb_desc_endp *) &data[pos];
> +                dev->config[i].interf[currif].descendp[j++] = endp;
> +                pos += desc->length;
> +              } else {

The above brackets don't follow GRUB convention. I haven't reviewed the
other patches, but there may be similar issues.

> +                if (!desc->length)
> +                  {
> +                    err = GRUB_USB_ERR_BADDEVICE;
> +                    goto fail;
> +                  }

They should be formatted like these.

> +                pos += desc->length;
> +             }
> +	  }
>  	}
>      }
>  
> @@ -217,8 +221,14 @@ grub_usb_device_initialize (grub_usb_device_t
> dev) 
>   fail:
>  
> -  for (i = 0; i < 8; i++)
> +  for (i = 0; i < 8; i++) {
> +    int currif;
> +
> +    for (currif = 0; currif < dev->config[i].descconf->numif;
> currif++)
> +      grub_free (dev->config[i].interf[currif].descendp);
> +
>      grub_free (dev->config[i].descconf);
> +  }

Ditto on the brackets. And many more, I think you get the idea.

>  
>    return err;
>  }
> diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
> index a06cce302..136bf6ee4 100644
> --- a/grub-core/bus/usb/usbhub.c
> +++ b/grub-core/bus/usb/usbhub.c
> @@ -82,8 +82,14 @@ grub_usb_hub_add_dev (grub_usb_controller_t
> controller, if (i == GRUB_USBHUB_MAX_DEVICES)
>      {
>        grub_error (GRUB_ERR_IO, "can't assign address to USB device");
> -      for (i = 0; i < 8; i++)
> -        grub_free (dev->config[i].descconf);
> +      for (i = 0; i < 8; i++) {
> +	int currif;
> +
> +	for (currif = 0; currif < dev->config[i].descconf->numif;
> currif++)
> +	  grub_free (dev->config[i].interf[currif].descendp);
> +
> +	grub_free (dev->config[i].descconf);
> +      }
>        grub_free (dev);
>        return NULL;
>      }
> @@ -96,8 +102,14 @@ grub_usb_hub_add_dev (grub_usb_controller_t
> controller, i, 0, 0, NULL);
>    if (err)
>      {
> -      for (i = 0; i < 8; i++)
> -        grub_free (dev->config[i].descconf);
> +      for (i = 0; i < 8; i++) {
> +	int currif;
> +
> +	for (currif = 0; currif < dev->config[i].descconf->numif;
> currif++)
> +	  grub_free (dev->config[i].interf[currif].descendp);
> +
> +	grub_free (dev->config[i].descconf);
> +      }
>        grub_free (dev);
>        return NULL;
>      }
> @@ -176,7 +188,7 @@ grub_usb_add_hub (grub_usb_device_t dev)
>         i++)
>      {
>        struct grub_usb_desc_endp *endp = NULL;
> -      endp = &dev->config[0].interf[0].descendp[i];
> +      endp = dev->config[0].interf[0].descendp[i];
>  
>        if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
>  	  == GRUB_USB_EP_INTERRUPT)
> diff --git a/grub-core/commands/usbtest.c
> b/grub-core/commands/usbtest.c index 2c6d93fe6..55a657635 100644
> --- a/grub-core/commands/usbtest.c
> +++ b/grub-core/commands/usbtest.c
> @@ -185,7 +185,7 @@ usb_iterate (grub_usb_device_t dev, void *data
> __attribute__ ((unused))) for (j = 0; j < interf->endpointcnt; j++)
>  	{
>  	  struct grub_usb_desc_endp *endp;
> -	  endp = &dev->config[0].interf[i].descendp[j];
> +	  endp = dev->config[0].interf[i].descendp[j];
>  
>  	  grub_printf ("Endpoint #%d: %s, max packed size: %d,
> transfer type: %s, latency: %d\n", endp->endp_addr & 15,
> diff --git a/grub-core/disk/usbms.c b/grub-core/disk/usbms.c
> index 380ca4c4c..64e1be8ff 100644
> --- a/grub-core/disk/usbms.c
> +++ b/grub-core/disk/usbms.c
> @@ -184,7 +184,7 @@ grub_usbms_attach (grub_usb_device_t usbdev, int
> configno, int interfno) for (j = 0; j < interf->endpointcnt; j++)
>      {
>        struct grub_usb_desc_endp *endp;
> -      endp = &usbdev->config[0].interf[interfno].descendp[j];
> +      endp = usbdev->config[0].interf[interfno].descendp[j];
>  
>        if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2)
>  	/* Bulk IN endpoint.  */
> diff --git a/grub-core/term/usb_keyboard.c
> b/grub-core/term/usb_keyboard.c index e67b8f785..d65e3474d 100644
> --- a/grub-core/term/usb_keyboard.c
> +++ b/grub-core/term/usb_keyboard.c
> @@ -175,7 +175,7 @@ grub_usb_keyboard_attach (grub_usb_device_t
> usbdev, int configno, int interfno) for (j = 0; j <
> usbdev->config[configno].interf[interfno].descif->endpointcnt; j++)
>      {
> -      endp = &usbdev->config[configno].interf[interfno].descendp[j];
> +      endp = usbdev->config[configno].interf[interfno].descendp[j];
>  
>        if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
>  	  == GRUB_USB_EP_INTERRUPT)
> diff --git a/include/grub/usb.h b/include/grub/usb.h
> index 512ae1dd0..0527a3e2b 100644
> --- a/include/grub/usb.h
> +++ b/include/grub/usb.h
> @@ -149,7 +149,7 @@ struct grub_usb_interface
>  {
>    struct grub_usb_desc_if *descif;
>  
> -  struct grub_usb_desc_endp *descendp;
> +  struct grub_usb_desc_endp **descendp;
>  
>    /* A driver is handling this interface. Do we need to support
> multiple drivers for single interface?
> diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h
> index aac5ab05a..bb2ab2e27 100644
> --- a/include/grub/usbdesc.h
> +++ b/include/grub/usbdesc.h
> @@ -29,7 +29,8 @@ typedef enum {
>    GRUB_USB_DESCRIPTOR_INTERFACE,
>    GRUB_USB_DESCRIPTOR_ENDPOINT,
>    GRUB_USB_DESCRIPTOR_DEBUG = 10,
> -  GRUB_USB_DESCRIPTOR_HUB = 0x29
> +  GRUB_USB_DESCRIPTOR_HUB = 0x29,
> +  GRUB_USB_DESCRIPTOR_SS_ENDPOINT_COMPANION = 0x30
>  } grub_usb_descriptor_t;
>  
>  struct grub_usb_desc
> @@ -105,6 +106,14 @@ struct grub_usb_desc_endp
>    grub_uint8_t interval;
>  } GRUB_PACKED;
>  
> +struct grub_usb_desc_ssep {
> +  grub_uint8_t  length;
> +  grub_uint8_t  type;
> +  grub_uint8_t  maxburst;
> +  grub_uint8_t  attrib;
> +  grub_uint16_t interval;
> +} GRUB_PACKED;
> +
>  struct grub_usb_desc_str
>  {
>    grub_uint8_t length;

Cheers,
Glenn


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2021-03-19  1:07 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-07  7:41 [PATCH v2 0/7] Add xHCI USB support Patrick Rudolph
2020-12-07  7:41 ` [PATCH v2 1/7] grub-core/bus/usb: Parse SuperSpeed companion descriptors Patrick Rudolph
2021-03-19  1:06   ` Glenn Washburn
2020-12-07  7:41 ` [PATCH v2 2/7] usb: Add enum for xHCI Patrick Rudolph
2020-12-07  7:41 ` [PATCH v2 3/7] usbtrans: Set default maximum packet size Patrick Rudolph
2020-12-07  7:41 ` [PATCH v2 4/7] grub-core/bus/usb: Add function pointer for attach/detach events Patrick Rudolph
2020-12-07  7:41 ` [PATCH v2 5/7] grub-core/bus/usb/usbhub: Add new private fields for xHCI controller Patrick Rudolph
2020-12-07  7:41 ` [PATCH v2 6/7] grub-core/bus/usb: Add xhci support Patrick Rudolph
2020-12-07  7:41 ` [PATCH v2 7/7] grub-core/bus/usb/usbhub: Add xHCI non root hub support Patrick Rudolph
2021-02-19 18:11 ` [PATCH v2 0/7] Add xHCI USB support Glenn Washburn
2021-02-24  6:46   ` Patrick Rudolph
2021-02-26 16:53     ` Glenn Washburn

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.