coreboot-kgpe-d16/payloads/libpayload/drivers/usb/xhci_devconf.c

406 lines
11 KiB
C
Raw Normal View History

/*
* This file is part of the libpayload project.
*
* Copyright (C) 2013 secunet Security Networks AG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
//#define XHCI_SPEW_DEBUG
#include <arch/virtual.h>
#include <usb/usb.h>
#include "xhci_private.h"
static u32
xhci_gen_route(xhci_t *const xhci, const int hubport, const int hubaddr)
{
if (!hubaddr)
return 0;
2013-09-04 02:15:31 +02:00
u32 route_string = SC_GET(ROUTE, xhci->dev[hubaddr].ctx.slot);
int i;
for (i = 0; i < 20; i += 4) {
if (!(route_string & (0xf << i))) {
route_string |= (hubport & 0xf) << i;
break;
}
}
return route_string;
}
static int
xhci_get_rh_port(xhci_t *const xhci, const int hubport, const int hubaddr)
{
if (!hubaddr)
return hubport;
2013-09-04 02:15:31 +02:00
return SC_GET(RHPORT, xhci->dev[hubaddr].ctx.slot);
}
static int
xhci_get_tt(xhci_t *const xhci, const usb_speed speed,
const int hubport, const int hubaddr,
int *const tt, int *const tt_port)
{
if (!hubaddr)
return 0;
2013-09-04 02:15:31 +02:00
const slotctx_t *const slot = xhci->dev[hubaddr].ctx.slot;
if ((*tt = SC_GET(TTID, slot))) {
*tt_port = SC_GET(TTPORT, slot);
} else if (speed < HIGH_SPEED &&
SC_GET(SPEED1, slot) - 1 == HIGH_SPEED) {
*tt = hubaddr;
*tt_port = hubport;
}
return *tt != 0;
}
2013-09-04 02:15:31 +02:00
static inputctx_t *
xhci_make_inputctx(const size_t ctxsize)
{
int i;
const size_t size = (1 + NUM_EPS) * ctxsize;
inputctx_t *const ic = malloc(sizeof(*ic));
void *dma_buffer = dma_memalign(64, size);
if (!ic || !dma_buffer) {
free(ic);
free(dma_buffer);
return NULL;
}
memset(dma_buffer, 0, size);
ic->drop = dma_buffer + 0;
ic->add = dma_buffer + 4;
dma_buffer += ctxsize;
for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
ic->dev.ep[i] = dma_buffer;
return ic;
}
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
usbdev_t *
xhci_set_address (hci_t *controller, usb_speed speed, int hubport, int hubaddr)
{
xhci_t *const xhci = XHCI_INST(controller);
2013-09-04 02:15:31 +02:00
const size_t ctxsize = CTXSIZE(xhci);
devinfo_t *di = NULL;
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
usbdev_t *dev = NULL;
int i;
2013-09-04 02:15:31 +02:00
inputctx_t *const ic = xhci_make_inputctx(ctxsize);
transfer_ring_t *const tr = malloc(sizeof(*tr));
if (tr)
tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t));
2013-09-04 02:15:31 +02:00
if (!ic || !tr || !tr->ring) {
xhci_debug("Out of memory\n");
goto _free_return;
}
int slot_id;
int cc = xhci_cmd_enable_slot(xhci, &slot_id);
if (cc != CC_SUCCESS) {
xhci_debug("Enable slot failed: %d\n", cc);
goto _free_return;
} else {
xhci_debug("Enabled slot %d\n", slot_id);
}
2013-09-04 02:15:31 +02:00
di = &xhci->dev[slot_id];
void *dma_buffer = dma_memalign(64, NUM_EPS * ctxsize);
if (!dma_buffer)
goto _disable_return;
2013-09-04 02:15:31 +02:00
memset(dma_buffer, 0, NUM_EPS * ctxsize);
for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
di->ctx.ep[i] = dma_buffer;
*ic->add = (1 << 0) /* Slot Context */ | (1 << 1) /* EP0 Context */ ;
SC_SET(ROUTE, ic->dev.slot, xhci_gen_route(xhci, hubport, hubaddr));
SC_SET(SPEED1, ic->dev.slot, speed + 1);
SC_SET(CTXENT, ic->dev.slot, 1); /* the endpoint 0 context */
SC_SET(RHPORT, ic->dev.slot, xhci_get_rh_port(xhci, hubport, hubaddr));
int tt, tt_port;
if (xhci_get_tt(xhci, speed, hubport, hubaddr, &tt, &tt_port)) {
xhci_debug("TT for %d: %d[%d]\n", slot_id, tt, tt_port);
2013-09-04 02:15:31 +02:00
SC_SET(MTT, ic->dev.slot, SC_GET(MTT, xhci->dev[tt].ctx.slot));
SC_SET(TTID, ic->dev.slot, tt);
SC_SET(TTPORT, ic->dev.slot, tt_port);
}
di->transfer_rings[1] = tr;
xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE);
2013-09-04 02:15:31 +02:00
ic->dev.ep0->tr_dq_low = virt_to_phys(tr->ring);
ic->dev.ep0->tr_dq_high = 0;
EC_SET(TYPE, ic->dev.ep0, EP_CONTROL);
EC_SET(AVRTRB, ic->dev.ep0, 8);
EC_SET(MPS, ic->dev.ep0, 8);
EC_SET(CERR, ic->dev.ep0, 3);
EC_SET(DCS, ic->dev.ep0, 1);
2013-09-04 02:15:31 +02:00
xhci->dcbaa[slot_id] = virt_to_phys(di->ctx.raw);
cc = xhci_cmd_address_device(xhci, slot_id, ic);
if (cc != CC_SUCCESS) {
xhci_debug("Address device failed: %d\n", cc);
goto _disable_return;
} else {
xhci_debug("Addressed device %d (USB: %d)\n",
2013-09-04 02:15:31 +02:00
slot_id, SC_GET(UADDR, di->ctx.slot));
}
mdelay(SET_ADDRESS_MDELAY);
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
dev = init_device_entry(controller, slot_id);
if (!dev)
goto _disable_return;
dev->address = slot_id;
dev->hub = hubaddr;
dev->port = hubport;
dev->speed = speed;
dev->endpoints[0].dev = dev;
dev->endpoints[0].endpoint = 0;
dev->endpoints[0].toggle = 0;
dev->endpoints[0].direction = SETUP;
dev->endpoints[0].type = CONTROL;
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
u8 buf[8];
if (get_descriptor(dev, gen_bmRequestType(device_to_host, standard_type,
dev_recp), DT_DEV, 0, buf, sizeof(buf)) != sizeof(buf)) {
usb_debug("first get_descriptor(DT_DEV) failed\n");
goto _disable_return;
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
}
dev->endpoints[0].maxpacketsize = usb_decode_mps0(speed, buf[7]);
if (dev->endpoints[0].maxpacketsize != 8) {
2013-09-04 02:15:31 +02:00
memset((void *)ic->dev.ep0, 0x00, ctxsize);
*ic->add = (1 << 1); /* EP0 Context */
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
EC_SET(MPS, ic->dev.ep0, dev->endpoints[0].maxpacketsize);
cc = xhci_cmd_evaluate_context(xhci, slot_id, ic);
if (cc != CC_SUCCESS) {
xhci_debug("Context evaluation failed: %d\n", cc);
goto _disable_return;
}
}
goto _free_ic_return;
_disable_return:
xhci_cmd_disable_slot(xhci, slot_id);
xhci->dcbaa[slot_id] = 0;
usb_detach_device(controller, slot_id);
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
dev = NULL;
_free_return:
if (tr)
free((void *)tr->ring);
free(tr);
2013-09-04 02:15:31 +02:00
if (di)
free(di->ctx.raw);
free((void *)di);
_free_ic_return:
2013-09-04 02:15:31 +02:00
if (ic)
free(ic->raw);
free(ic);
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
return dev;
}
static int
xhci_finish_hub_config(usbdev_t *const dev, inputctx_t *const ic)
{
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
hub_descriptor_t desc;
if (get_descriptor(dev, gen_bmRequestType(device_to_host, class_type,
dev_recp), 0x29, 0, &desc, sizeof(desc)) != sizeof(desc)) {
xhci_debug("Failed to fetch hub descriptor\n");
return COMMUNICATION_ERROR;
}
SC_SET(HUB, ic->dev.slot, 1);
SC_SET(MTT, ic->dev.slot, 0); /* No support for Multi-TT */
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
SC_SET(NPORTS, ic->dev.slot, desc.bNbrPorts);
if (dev->speed == HIGH_SPEED)
SC_SET(TTT, ic->dev.slot,
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
(desc.wHubCharacteristics >> 5) & 0x0003);
return 0;
}
static size_t
xhci_bound_interval(const endpoint_t *const ep)
{
if ( (ep->dev->speed == LOW_SPEED &&
(ep->type == ISOCHRONOUS ||
ep->type == INTERRUPT)) ||
(ep->dev->speed == FULL_SPEED &&
ep->type == INTERRUPT))
{
if (ep->interval < 3)
return 3;
else if (ep->interval > 11)
return 11;
else
return ep->interval;
} else {
if (ep->interval < 0)
return 0;
else if (ep->interval > 15)
return 15;
else
return ep->interval;
}
}
static int
xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic)
{
xhci_t *const xhci = XHCI_INST(ep->dev->controller);
const int ep_id = xhci_ep_id(ep);
xhci_debug("ep_id: %d\n", ep_id);
if (ep_id <= 1 || 32 <= ep_id)
return DRIVER_ERROR;
transfer_ring_t *const tr = malloc(sizeof(*tr));
if (tr)
tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t));
if (!tr || !tr->ring) {
free(tr);
xhci_debug("Out of memory\n");
return OUT_OF_MEMORY;
}
2013-09-04 02:15:31 +02:00
xhci->dev[ep->dev->address].transfer_rings[ep_id] = tr;
xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE);
2013-09-04 02:15:31 +02:00
*ic->add |= (1 << ep_id);
if (SC_GET(CTXENT, ic->dev.slot) < ep_id)
SC_SET(CTXENT, ic->dev.slot, ep_id);
2013-09-04 02:15:31 +02:00
epctx_t *const epctx = ic->dev.ep[ep_id];
xhci_debug("Filling epctx (@%p)\n", epctx);
epctx->tr_dq_low = virt_to_phys(tr->ring);
epctx->tr_dq_high = 0;
2013-09-04 02:15:31 +02:00
EC_SET(INTVAL, epctx, xhci_bound_interval(ep));
EC_SET(CERR, epctx, 3);
EC_SET(TYPE, epctx, ep->type | ((ep->direction != OUT) << 2));
EC_SET(MPS, epctx, ep->maxpacketsize);
EC_SET(DCS, epctx, 1);
size_t avrtrb;
switch (ep->type) {
case BULK: case ISOCHRONOUS: avrtrb = 3 * 1024; break;
case INTERRUPT: avrtrb = 1024; break;
default: avrtrb = 8; break;
}
2013-09-04 02:15:31 +02:00
EC_SET(AVRTRB, epctx, avrtrb);
EC_SET(MXESIT, epctx, EC_GET(MPS, epctx) * EC_GET(MBS, epctx));
return 0;
}
int
xhci_finish_device_config(usbdev_t *const dev)
{
xhci_t *const xhci = XHCI_INST(dev->controller);
2013-09-04 02:15:31 +02:00
devinfo_t *const di = &xhci->dev[dev->address];
int i, ret = 0;
2013-09-04 02:15:31 +02:00
inputctx_t *const ic = xhci_make_inputctx(CTXSIZE(xhci));
if (!ic) {
xhci_debug("Out of memory\n");
return OUT_OF_MEMORY;
}
2013-09-04 02:15:31 +02:00
*ic->add = (1 << 0); /* Slot Context */
2013-09-04 02:15:31 +02:00
xhci_dump_slotctx(di->ctx.slot);
ic->dev.slot->f1 = di->ctx.slot->f1;
ic->dev.slot->f2 = di->ctx.slot->f2;
ic->dev.slot->f3 = di->ctx.slot->f3;
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
if (dev->descriptor->bDeviceClass == 0x09 && dev->speed < SUPER_SPEED) {
ret = xhci_finish_hub_config(dev, ic);
if (ret)
goto _free_return;
}
for (i = 1; i < dev->num_endp; ++i) {
ret = xhci_finish_ep_config(&dev->endpoints[i], ic);
if (ret)
goto _free_ep_ctx_return;
}
xhci_dump_inputctx(ic);
libpayload: usb: Refactor USB enumeration to fix SuperSpeed devices This patch represents a major overhaul of the USB enumeration code in order to make it cleaner and much more robust to weird or malicious devices. The main improvement is that it correctly parses the USB descriptors even if there are unknown descriptors interspersed within, which is perfectly legal and in particular present on all SuperSpeed devices (due to the SuperSpeed Endpoint Companion Descriptor). In addition, it gets rid of the really whacky and special cased get_descriptor() function, which would read every descriptor twice whether it made sense or not. The new code makes the callers allocate descriptor memory and only read stuff twice when it's really necessary (i.e. the device and configuration descriptors). Finally, it also moves some more responsibilities into the controller-specific set_address() function in order to make sure things are initialized at the same stage for all controllers. In the new model it initializes the device entry (which zeroes the endpoint array), sets up endpoint 0 (including MPS), sets the device address and finally returns the whole usbdev_t structure with that address correctly set. Note that this should make SuperSpeed devices work, but SuperSpeed hubs are a wholly different story and would require a custom hub driver (since the hub descriptor and port status formats are different for USB 3.0 ports, and the whole issue about the same hub showing up as two different devices on two different ports might present additional challenges). The stack currently just issues a warning and refuses to initialize this part of the hub, which means that 3.0 devices connected through a 3.0 hub may not work correctly. Change-Id: Ie0b82dca23b7a750658ccc1a85f9daae5fbc20e1 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/170666 Reviewed-by: Kees Cook <keescook@chromium.org> (cherry picked from commit ecec80e062f7efe32a9a17479dcf8cb678a4a98b) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6780 Tested-by: build bot (Jenkins)
2013-09-18 07:16:04 +02:00
const int config_id = dev->configuration->bConfigurationValue;
xhci_debug("config_id: %d\n", config_id);
const int cc =
xhci_cmd_configure_endpoint(xhci, dev->address, config_id, ic);
if (cc != CC_SUCCESS) {
xhci_debug("Configure endpoint failed: %d\n", cc);
ret = CONTROLLER_ERROR;
goto _free_ep_ctx_return;
} else {
xhci_debug("Endpoints configured\n");
}
goto _free_return;
_free_ep_ctx_return:
for (i = 2; i < 31; ++i) {
if (di->transfer_rings[i])
free((void *)di->transfer_rings[i]->ring);
free(di->transfer_rings[i]);
di->transfer_rings[i] = NULL;
}
_free_return:
2013-09-04 02:15:31 +02:00
free(ic->raw);
free(ic);
return ret;
}
void
xhci_destroy_dev(hci_t *const controller, const int slot_id)
{
xhci_t *const xhci = XHCI_INST(controller);
if (slot_id <= 0 || slot_id > xhci->max_slots_en)
return;
int i;
const int cc = xhci_cmd_disable_slot(xhci, slot_id);
if (cc != CC_SUCCESS)
xhci_debug("Failed to disable slot %d: %d\n", slot_id, cc);
2013-09-04 02:15:31 +02:00
devinfo_t *const di = &xhci->dev[slot_id];
for (i = 1; i < 31; ++i) {
if (di->transfer_rings[i])
free((void *)di->transfer_rings[i]->ring);
free(di->transfer_rings[i]);
free(di->interrupt_queues[i]);
}
xhci->dcbaa[slot_id] = 0;
}