crypto-stm32-fix-loop-iterating-through-scatterlist-for-dma.patch
cpufreq-brcmstb-avs-cpufreq-fix-warray-bounds-bug.patch
of-property-fw_devlink-add-a-devlink-for-panel-followers.patch
+usb-typec-tcpm-set-initial-svdm-version-based-on-pd-revision.patch
+usb-typec-bus-verify-partner-exists-in-typec_altmode_attention.patch
+usb-core-unite-old-scheme-and-new-scheme-descriptor-reads.patch
+usb-core-change-usb_get_device_descriptor-api.patch
+usb-core-fix-race-by-not-overwriting-udev-descriptor-in-hub_port_init.patch
--- /dev/null
+From de28e469da75359a2bb8cd8778b78aa64b1be1f4 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Aug 2023 15:12:21 -0400
+Subject: USB: core: Change usb_get_device_descriptor() API
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit de28e469da75359a2bb8cd8778b78aa64b1be1f4 upstream.
+
+The usb_get_device_descriptor() routine reads the device descriptor
+from the udev device and stores it directly in udev->descriptor. This
+interface is error prone, because the USB subsystem expects in-memory
+copies of a device's descriptors to be immutable once the device has
+been initialized.
+
+The interface is changed so that the device descriptor is left in a
+kmalloc-ed buffer, not copied into the usb_device structure. A
+pointer to the buffer is returned to the caller, who is then
+responsible for kfree-ing it. The corresponding changes needed in the
+various callers are fairly small.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://lore.kernel.org/r/d0111bb6-56c1-4f90-adf2-6cfe152f6561@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hcd.c | 10 +++++++---
+ drivers/usb/core/hub.c | 44 +++++++++++++++++++++++---------------------
+ drivers/usb/core/message.c | 29 ++++++++++++-----------------
+ drivers/usb/core/usb.h | 4 ++--
+ 4 files changed, 44 insertions(+), 43 deletions(-)
+
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -983,6 +983,7 @@ static int register_root_hub(struct usb_
+ {
+ struct device *parent_dev = hcd->self.controller;
+ struct usb_device *usb_dev = hcd->self.root_hub;
++ struct usb_device_descriptor *descr;
+ const int devnum = 1;
+ int retval;
+
+@@ -994,13 +995,16 @@ static int register_root_hub(struct usb_
+ mutex_lock(&usb_bus_idr_lock);
+
+ usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+- retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
+- if (retval != sizeof usb_dev->descriptor) {
++ descr = usb_get_device_descriptor(usb_dev);
++ if (IS_ERR(descr)) {
++ retval = PTR_ERR(descr);
+ mutex_unlock(&usb_bus_idr_lock);
+ dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
+ dev_name(&usb_dev->dev), retval);
+- return (retval < 0) ? retval : -EMSGSIZE;
++ return retval;
+ }
++ usb_dev->descriptor = *descr;
++ kfree(descr);
+
+ if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
+ retval = usb_get_bos_descriptor(usb_dev);
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2656,12 +2656,17 @@ int usb_authorize_device(struct usb_devi
+ }
+
+ if (usb_dev->wusb) {
+- result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
+- if (result < 0) {
++ struct usb_device_descriptor *descr;
++
++ descr = usb_get_device_descriptor(usb_dev);
++ if (IS_ERR(descr)) {
++ result = PTR_ERR(descr);
+ dev_err(&usb_dev->dev, "can't re-read device descriptor for "
+ "authorization: %d\n", result);
+ goto error_device_descriptor;
+ }
++ usb_dev->descriptor = *descr;
++ kfree(descr);
+ }
+
+ usb_dev->authorized = 1;
+@@ -4747,7 +4752,7 @@ hub_port_init(struct usb_hub *hub, struc
+ const char *driver_name;
+ bool do_new_scheme;
+ int maxp0;
+- struct usb_device_descriptor *buf;
++ struct usb_device_descriptor *buf, *descr;
+
+ buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+ if (!buf)
+@@ -4984,15 +4989,16 @@ hub_port_init(struct usb_hub *hub, struc
+ usb_ep0_reinit(udev);
+ }
+
+- retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
+- if (retval < (signed)sizeof(udev->descriptor)) {
++ descr = usb_get_device_descriptor(udev);
++ if (IS_ERR(descr)) {
++ retval = PTR_ERR(descr);
+ if (retval != -ENODEV)
+ dev_err(&udev->dev, "device descriptor read/all, error %d\n",
+ retval);
+- if (retval >= 0)
+- retval = -ENOMSG;
+ goto fail;
+ }
++ udev->descriptor = *descr;
++ kfree(descr);
+
+ /*
+ * Some superspeed devices have finished the link training process
+@@ -5111,7 +5117,7 @@ hub_power_remaining(struct usb_hub *hub)
+
+
+ static int descriptors_changed(struct usb_device *udev,
+- struct usb_device_descriptor *old_device_descriptor,
++ struct usb_device_descriptor *new_device_descriptor,
+ struct usb_host_bos *old_bos)
+ {
+ int changed = 0;
+@@ -5122,8 +5128,8 @@ static int descriptors_changed(struct us
+ int length;
+ char *buf;
+
+- if (memcmp(&udev->descriptor, old_device_descriptor,
+- sizeof(*old_device_descriptor)) != 0)
++ if (memcmp(&udev->descriptor, new_device_descriptor,
++ sizeof(*new_device_descriptor)) != 0)
+ return 1;
+
+ if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
+@@ -5443,9 +5449,8 @@ static void hub_port_connect_change(stru
+ {
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
+- struct usb_device_descriptor descriptor;
++ struct usb_device_descriptor *descr;
+ int status = -ENODEV;
+- int retval;
+
+ dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
+ portchange, portspeed(hub, portstatus));
+@@ -5472,23 +5477,20 @@ static void hub_port_connect_change(stru
+ * changed device descriptors before resuscitating the
+ * device.
+ */
+- descriptor = udev->descriptor;
+- retval = usb_get_device_descriptor(udev,
+- sizeof(udev->descriptor));
+- if (retval < 0) {
++ descr = usb_get_device_descriptor(udev);
++ if (IS_ERR(descr)) {
+ dev_dbg(&udev->dev,
+- "can't read device descriptor %d\n",
+- retval);
++ "can't read device descriptor %ld\n",
++ PTR_ERR(descr));
+ } else {
+- if (descriptors_changed(udev, &descriptor,
++ if (descriptors_changed(udev, descr,
+ udev->bos)) {
+ dev_dbg(&udev->dev,
+ "device descriptor has changed\n");
+- /* for disconnect() calls */
+- udev->descriptor = descriptor;
+ } else {
+ status = 0; /* Nothing to do */
+ }
++ kfree(descr);
+ }
+ #ifdef CONFIG_PM
+ } else if (udev->state == USB_STATE_SUSPENDED &&
+--- a/drivers/usb/core/message.c
++++ b/drivers/usb/core/message.c
+@@ -1039,40 +1039,35 @@ char *usb_cache_string(struct usb_device
+ }
+
+ /*
+- * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
+- * @dev: the device whose device descriptor is being updated
+- * @size: how much of the descriptor to read
++ * usb_get_device_descriptor - read the device descriptor
++ * @udev: the device whose device descriptor should be read
+ *
+ * Context: task context, might sleep.
+ *
+- * Updates the copy of the device descriptor stored in the device structure,
+- * which dedicates space for this purpose.
+- *
+ * Not exported, only for use by the core. If drivers really want to read
+ * the device descriptor directly, they can call usb_get_descriptor() with
+ * type = USB_DT_DEVICE and index = 0.
+ *
+- * This call is synchronous, and may not be used in an interrupt context.
+- *
+- * Return: The number of bytes received on success, or else the status code
+- * returned by the underlying usb_control_msg() call.
++ * Returns: a pointer to a dynamically allocated usb_device_descriptor
++ * structure (which the caller must deallocate), or an ERR_PTR value.
+ */
+-int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
++struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
+ {
+ struct usb_device_descriptor *desc;
+ int ret;
+
+- if (size > sizeof(*desc))
+- return -EINVAL;
+ desc = kmalloc(sizeof(*desc), GFP_NOIO);
+ if (!desc)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
++
++ ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
++ if (ret == sizeof(*desc))
++ return desc;
+
+- ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
+ if (ret >= 0)
+- memcpy(&dev->descriptor, desc, size);
++ ret = -EMSGSIZE;
+ kfree(desc);
+- return ret;
++ return ERR_PTR(ret);
+ }
+
+ /*
+--- a/drivers/usb/core/usb.h
++++ b/drivers/usb/core/usb.h
+@@ -42,8 +42,8 @@ extern bool usb_endpoint_is_ignored(stru
+ struct usb_endpoint_descriptor *epd);
+ extern int usb_remove_device(struct usb_device *udev);
+
+-extern int usb_get_device_descriptor(struct usb_device *dev,
+- unsigned int size);
++extern struct usb_device_descriptor *usb_get_device_descriptor(
++ struct usb_device *udev);
+ extern int usb_set_isoch_delay(struct usb_device *dev);
+ extern int usb_get_bos_descriptor(struct usb_device *dev);
+ extern void usb_release_bos_descriptor(struct usb_device *dev);
--- /dev/null
+From ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Aug 2023 15:14:14 -0400
+Subject: USB: core: Fix race by not overwriting udev->descriptor in hub_port_init()
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b upstream.
+
+Syzbot reported an out-of-bounds read in sysfs.c:read_descriptors():
+
+BUG: KASAN: slab-out-of-bounds in read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883
+Read of size 8 at addr ffff88801e78b8c8 by task udevd/5011
+
+CPU: 0 PID: 5011 Comm: udevd Not tainted 6.4.0-rc6-syzkaller-00195-g40f71e7cd3c6 #0
+Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/27/2023
+Call Trace:
+ <TASK>
+ __dump_stack lib/dump_stack.c:88 [inline]
+ dump_stack_lvl+0xd9/0x150 lib/dump_stack.c:106
+ print_address_description.constprop.0+0x2c/0x3c0 mm/kasan/report.c:351
+ print_report mm/kasan/report.c:462 [inline]
+ kasan_report+0x11c/0x130 mm/kasan/report.c:572
+ read_descriptors+0x263/0x280 drivers/usb/core/sysfs.c:883
+...
+Allocated by task 758:
+...
+ __do_kmalloc_node mm/slab_common.c:966 [inline]
+ __kmalloc+0x5e/0x190 mm/slab_common.c:979
+ kmalloc include/linux/slab.h:563 [inline]
+ kzalloc include/linux/slab.h:680 [inline]
+ usb_get_configuration+0x1f7/0x5170 drivers/usb/core/config.c:887
+ usb_enumerate_device drivers/usb/core/hub.c:2407 [inline]
+ usb_new_device+0x12b0/0x19d0 drivers/usb/core/hub.c:2545
+
+As analyzed by Khazhy Kumykov, the cause of this bug is a race between
+read_descriptors() and hub_port_init(): The first routine uses a field
+in udev->descriptor, not expecting it to change, while the second
+overwrites it.
+
+Prior to commit 45bf39f8df7f ("USB: core: Don't hold device lock while
+reading the "descriptors" sysfs file") this race couldn't occur,
+because the routines were mutually exclusive thanks to the device
+locking. Removing that locking from read_descriptors() exposed it to
+the race.
+
+The best way to fix the bug is to keep hub_port_init() from changing
+udev->descriptor once udev has been initialized and registered.
+Drivers expect the descriptors stored in the kernel to be immutable;
+we should not undermine this expectation. In fact, this change should
+have been made long ago.
+
+So now hub_port_init() will take an additional argument, specifying a
+buffer in which to store the device descriptor it reads. (If udev has
+not yet been initialized, the buffer pointer will be NULL and then
+hub_port_init() will store the device descriptor in udev as before.)
+This eliminates the data race responsible for the out-of-bounds read.
+
+The changes to hub_port_init() appear more extensive than they really
+are, because of indentation changes resulting from an attempt to avoid
+writing to other parts of the usb_device structure after it has been
+initialized. Similar changes should be made to the code that reads
+the BOS descriptor, but that can be handled in a separate patch later
+on. This patch is sufficient to fix the bug found by syzbot.
+
+Reported-and-tested-by: syzbot+18996170f8096c6174d0@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/linux-usb/000000000000c0ffe505fe86c9ca@google.com/#r
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: Khazhy Kumykov <khazhy@google.com>
+Fixes: 45bf39f8df7f ("USB: core: Don't hold device lock while reading the "descriptors" sysfs file")
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/r/b958b47a-9a46-4c22-a9f9-e42e42c31251@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c | 114 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 70 insertions(+), 44 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -4736,10 +4736,17 @@ static int get_bMaxPacketSize0(struct us
+ * the port lock. For a newly detected device that is not accessible
+ * through any global pointers, it's not necessary to lock the device,
+ * but it is still necessary to lock the port.
++ *
++ * For a newly detected device, @dev_descr must be NULL. The device
++ * descriptor retrieved from the device will then be stored in
++ * @udev->descriptor. For an already existing device, @dev_descr
++ * must be non-NULL. The device descriptor will be stored there,
++ * not in @udev->descriptor, because descriptors for registered
++ * devices are meant to be immutable.
+ */
+ static int
+ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
+- int retry_counter)
++ int retry_counter, struct usb_device_descriptor *dev_descr)
+ {
+ struct usb_device *hdev = hub->hdev;
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+@@ -4751,6 +4758,7 @@ hub_port_init(struct usb_hub *hub, struc
+ int devnum = udev->devnum;
+ const char *driver_name;
+ bool do_new_scheme;
++ const bool initial = !dev_descr;
+ int maxp0;
+ struct usb_device_descriptor *buf, *descr;
+
+@@ -4789,32 +4797,34 @@ hub_port_init(struct usb_hub *hub, struc
+ }
+ oldspeed = udev->speed;
+
+- /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
+- * it's fixed size except for full speed devices.
+- * For Wireless USB devices, ep0 max packet is always 512 (tho
+- * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
+- */
+- switch (udev->speed) {
+- case USB_SPEED_SUPER_PLUS:
+- case USB_SPEED_SUPER:
+- case USB_SPEED_WIRELESS: /* fixed at 512 */
+- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
+- break;
+- case USB_SPEED_HIGH: /* fixed at 64 */
+- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+- break;
+- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
+- /* to determine the ep0 maxpacket size, try to read
+- * the device descriptor to get bMaxPacketSize0 and
+- * then correct our initial guess.
++ if (initial) {
++ /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
++ * it's fixed size except for full speed devices.
++ * For Wireless USB devices, ep0 max packet is always 512 (tho
++ * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
+ */
+- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+- break;
+- case USB_SPEED_LOW: /* fixed at 8 */
+- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
+- break;
+- default:
+- goto fail;
++ switch (udev->speed) {
++ case USB_SPEED_SUPER_PLUS:
++ case USB_SPEED_SUPER:
++ case USB_SPEED_WIRELESS: /* fixed at 512 */
++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
++ break;
++ case USB_SPEED_HIGH: /* fixed at 64 */
++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
++ break;
++ case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
++ /* to determine the ep0 maxpacket size, try to read
++ * the device descriptor to get bMaxPacketSize0 and
++ * then correct our initial guess.
++ */
++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
++ break;
++ case USB_SPEED_LOW: /* fixed at 8 */
++ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
++ break;
++ default:
++ goto fail;
++ }
+ }
+
+ if (udev->speed == USB_SPEED_WIRELESS)
+@@ -4837,22 +4847,24 @@ hub_port_init(struct usb_hub *hub, struc
+ if (udev->speed < USB_SPEED_SUPER)
+ dev_info(&udev->dev,
+ "%s %s USB device number %d using %s\n",
+- (udev->config) ? "reset" : "new", speed,
++ (initial ? "new" : "reset"), speed,
+ devnum, driver_name);
+
+- /* Set up TT records, if needed */
+- if (hdev->tt) {
+- udev->tt = hdev->tt;
+- udev->ttport = hdev->ttport;
+- } else if (udev->speed != USB_SPEED_HIGH
+- && hdev->speed == USB_SPEED_HIGH) {
+- if (!hub->tt.hub) {
+- dev_err(&udev->dev, "parent hub has no TT\n");
+- retval = -EINVAL;
+- goto fail;
++ if (initial) {
++ /* Set up TT records, if needed */
++ if (hdev->tt) {
++ udev->tt = hdev->tt;
++ udev->ttport = hdev->ttport;
++ } else if (udev->speed != USB_SPEED_HIGH
++ && hdev->speed == USB_SPEED_HIGH) {
++ if (!hub->tt.hub) {
++ dev_err(&udev->dev, "parent hub has no TT\n");
++ retval = -EINVAL;
++ goto fail;
++ }
++ udev->tt = &hub->tt;
++ udev->ttport = port1;
+ }
+- udev->tt = &hub->tt;
+- udev->ttport = port1;
+ }
+
+ /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
+@@ -4881,6 +4893,12 @@ hub_port_init(struct usb_hub *hub, struc
+
+ maxp0 = get_bMaxPacketSize0(udev, buf,
+ GET_DESCRIPTOR_BUFSIZE, retries == 0);
++ if (maxp0 > 0 && !initial &&
++ maxp0 != udev->descriptor.bMaxPacketSize0) {
++ dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
++ retval = -ENODEV;
++ goto fail;
++ }
+
+ retval = hub_port_reset(hub, port1, udev, delay, false);
+ if (retval < 0) /* error or disconnect */
+@@ -4954,6 +4972,12 @@ hub_port_init(struct usb_hub *hub, struc
+ } else {
+ u32 delay;
+
++ if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) {
++ dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
++ retval = -ENODEV;
++ goto fail;
++ }
++
+ delay = udev->parent->hub_delay;
+ udev->hub_delay = min_t(u32, delay,
+ USB_TP_TRANSMISSION_DELAY_MAX);
+@@ -4997,7 +5021,10 @@ hub_port_init(struct usb_hub *hub, struc
+ retval);
+ goto fail;
+ }
+- udev->descriptor = *descr;
++ if (initial)
++ udev->descriptor = *descr;
++ else
++ *dev_descr = *descr;
+ kfree(descr);
+
+ /*
+@@ -5302,7 +5329,7 @@ static void hub_port_connect(struct usb_
+ }
+
+ /* reset (non-USB 3.0 devices) and get descriptor */
+- status = hub_port_init(hub, udev, port1, i);
++ status = hub_port_init(hub, udev, port1, i, NULL);
+ if (status < 0)
+ goto loop;
+
+@@ -5928,7 +5955,7 @@ static int usb_reset_and_verify_device(s
+ struct usb_device *parent_hdev = udev->parent;
+ struct usb_hub *parent_hub;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+- struct usb_device_descriptor descriptor = udev->descriptor;
++ struct usb_device_descriptor descriptor;
+ struct usb_host_bos *bos;
+ int i, j, ret = 0;
+ int port1 = udev->portnum;
+@@ -5960,7 +5987,7 @@ static int usb_reset_and_verify_device(s
+ /* ep0 maxpacket size may change; let the HCD know about it.
+ * Other endpoints will be handled by re-enumeration. */
+ usb_ep0_reinit(udev);
+- ret = hub_port_init(parent_hub, udev, port1, i);
++ ret = hub_port_init(parent_hub, udev, port1, i, &descriptor);
+ if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
+ break;
+ }
+@@ -5972,7 +5999,6 @@ static int usb_reset_and_verify_device(s
+ /* Device might have changed firmware (DFU or similar) */
+ if (descriptors_changed(udev, &descriptor, bos)) {
+ dev_info(&udev->dev, "device firmware changed\n");
+- udev->descriptor = descriptor; /* for disconnect() calls */
+ goto re_enumerate;
+ }
+
--- /dev/null
+From 85d07c55621676d47d873d2749b88f783cd4d5a1 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 4 Aug 2023 15:10:59 -0400
+Subject: USB: core: Unite old scheme and new scheme descriptor reads
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 85d07c55621676d47d873d2749b88f783cd4d5a1 upstream.
+
+In preparation for reworking the usb_get_device_descriptor() routine,
+it is desirable to unite the two different code paths responsible for
+initially determining endpoint 0's maximum packet size in a newly
+discovered USB device. Making this determination presents a
+chicken-and-egg sort of problem, in that the only way to learn the
+maxpacket value is to get it from the device descriptor retrieved from
+the device, but communicating with the device to retrieve a descriptor
+requires us to know beforehand the ep0 maxpacket size.
+
+In practice this problem is solved in two different ways, referred to
+in hub.c as the "old scheme" and the "new scheme". The old scheme
+(which is the approach recommended by the USB-2 spec) involves asking
+the device to send just the first eight bytes of its device
+descriptor. Such a transfer uses packets containing no more than
+eight bytes each, and every USB device must have an ep0 maxpacket size
+>= 8, so this should succeed. Since the bMaxPacketSize0 field of the
+device descriptor lies within the first eight bytes, this is all we
+need.
+
+The new scheme is an imitation of the technique used in an early
+Windows USB implementation, giving it the happy advantage of working
+with a wide variety of devices (some of them at the time would not
+work with the old scheme, although that's probably less true now). It
+involves making an initial guess of the ep0 maxpacket size, asking the
+device to send up to 64 bytes worth of its device descriptor (which is
+only 18 bytes long), and then resetting the device to clear any error
+condition that might have resulted from the guess being wrong. The
+initial guess is determined by the connection speed; it should be
+correct in all cases other than full speed, for which the allowed
+values are 8, 16, 32, and 64 (in this case the initial guess is 64).
+
+The reason for this patch is that the old- and new-scheme parts of
+hub_port_init() use different code paths, one involving
+usb_get_device_descriptor() and one not, for their initial reads of
+the device descriptor. Since these reads have essentially the same
+purpose and are made under essentially the same circumstances, this is
+illogical. It makes more sense to have both of them use a common
+subroutine.
+
+This subroutine does basically what the new scheme's code did, because
+that approach is more general than the one used by the old scheme. It
+only needs to know how many bytes to transfer and whether or not it is
+being called for the first iteration of a retry loop (in case of
+certain time-out errors). There are two main differences from the
+former code:
+
+ We initialize the bDescriptorType field of the transfer buffer
+ to 0 before performing the transfer, to avoid possibly
+ accessing an uninitialized value afterward.
+
+ We read the device descriptor into a temporary buffer rather
+ than storing it directly into udev->descriptor, which the old
+ scheme implementation used to do.
+
+Since the whole point of this first read of the device descriptor is
+to determine the bMaxPacketSize0 value, that is what the new routine
+returns (or an error code). The value is stored in a local variable
+rather than in udev->descriptor. As a side effect, this necessitates
+moving a section of code that checks the bcdUSB field for SuperSpeed
+devices until after the full device descriptor has been retrieved.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: Oliver Neukum <oneukum@suse.com>
+Link: https://lore.kernel.org/r/495cb5d4-f956-4f4a-a875-1e67e9489510@rowland.harvard.edu
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/hub.c | 173 ++++++++++++++++++++++++++-----------------------
+ 1 file changed, 94 insertions(+), 79 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -4661,6 +4661,67 @@ static int hub_enable_device(struct usb_
+ return hcd->driver->enable_device(hcd, udev);
+ }
+
++/*
++ * Get the bMaxPacketSize0 value during initialization by reading the
++ * device's device descriptor. Since we don't already know this value,
++ * the transfer is unsafe and it ignores I/O errors, only testing for
++ * reasonable received values.
++ *
++ * For "old scheme" initialization, size will be 8 so we read just the
++ * start of the device descriptor, which should work okay regardless of
++ * the actual bMaxPacketSize0 value. For "new scheme" initialization,
++ * size will be 64 (and buf will point to a sufficiently large buffer),
++ * which might not be kosher according to the USB spec but it's what
++ * Windows does and what many devices expect.
++ *
++ * Returns: bMaxPacketSize0 or a negative error code.
++ */
++static int get_bMaxPacketSize0(struct usb_device *udev,
++ struct usb_device_descriptor *buf, int size, bool first_time)
++{
++ int i, rc;
++
++ /*
++ * Retry on all errors; some devices are flakey.
++ * 255 is for WUSB devices, we actually need to use
++ * 512 (WUSB1.0[4.8.1]).
++ */
++ for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) {
++ /* Start with invalid values in case the transfer fails */
++ buf->bDescriptorType = buf->bMaxPacketSize0 = 0;
++ rc = usb_control_msg(udev, usb_rcvaddr0pipe(),
++ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
++ USB_DT_DEVICE << 8, 0,
++ buf, size,
++ initial_descriptor_timeout);
++ switch (buf->bMaxPacketSize0) {
++ case 8: case 16: case 32: case 64: case 255:
++ if (buf->bDescriptorType == USB_DT_DEVICE) {
++ rc = buf->bMaxPacketSize0;
++ break;
++ }
++ fallthrough;
++ default:
++ if (rc >= 0)
++ rc = -EPROTO;
++ break;
++ }
++
++ /*
++ * Some devices time out if they are powered on
++ * when already connected. They need a second
++ * reset, so return early. But only on the first
++ * attempt, lest we get into a time-out/reset loop.
++ */
++ if (rc > 0 || (rc == -ETIMEDOUT && first_time &&
++ udev->speed > USB_SPEED_FULL))
++ break;
++ }
++ return rc;
++}
++
++#define GET_DESCRIPTOR_BUFSIZE 64
++
+ /* Reset device, (re)assign address, get device descriptor.
+ * Device connection must be stable, no more debouncing needed.
+ * Returns device in USB_STATE_ADDRESS, except on error.
+@@ -4685,6 +4746,12 @@ hub_port_init(struct usb_hub *hub, struc
+ int devnum = udev->devnum;
+ const char *driver_name;
+ bool do_new_scheme;
++ int maxp0;
++ struct usb_device_descriptor *buf;
++
++ buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
++ if (!buf)
++ return -ENOMEM;
+
+ /* root hub ports have a slightly longer reset period
+ * (from USB 2.0 spec, section 7.1.7.5)
+@@ -4799,9 +4866,6 @@ hub_port_init(struct usb_hub *hub, struc
+
+ for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
+ if (do_new_scheme) {
+- struct usb_device_descriptor *buf;
+- int r = 0;
+-
+ retval = hub_enable_device(udev);
+ if (retval < 0) {
+ dev_err(&udev->dev,
+@@ -4810,52 +4874,8 @@ hub_port_init(struct usb_hub *hub, struc
+ goto fail;
+ }
+
+-#define GET_DESCRIPTOR_BUFSIZE 64
+- buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+- if (!buf) {
+- retval = -ENOMEM;
+- continue;
+- }
+-
+- /* Retry on all errors; some devices are flakey.
+- * 255 is for WUSB devices, we actually need to use
+- * 512 (WUSB1.0[4.8.1]).
+- */
+- for (operations = 0; operations < GET_MAXPACKET0_TRIES;
+- ++operations) {
+- buf->bMaxPacketSize0 = 0;
+- r = usb_control_msg(udev, usb_rcvaddr0pipe(),
+- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+- USB_DT_DEVICE << 8, 0,
+- buf, GET_DESCRIPTOR_BUFSIZE,
+- initial_descriptor_timeout);
+- switch (buf->bMaxPacketSize0) {
+- case 8: case 16: case 32: case 64: case 255:
+- if (buf->bDescriptorType ==
+- USB_DT_DEVICE) {
+- r = 0;
+- break;
+- }
+- fallthrough;
+- default:
+- if (r == 0)
+- r = -EPROTO;
+- break;
+- }
+- /*
+- * Some devices time out if they are powered on
+- * when already connected. They need a second
+- * reset. But only on the first attempt,
+- * lest we get into a time out/reset loop
+- */
+- if (r == 0 || (r == -ETIMEDOUT &&
+- retries == 0 &&
+- udev->speed > USB_SPEED_FULL))
+- break;
+- }
+- udev->descriptor.bMaxPacketSize0 =
+- buf->bMaxPacketSize0;
+- kfree(buf);
++ maxp0 = get_bMaxPacketSize0(udev, buf,
++ GET_DESCRIPTOR_BUFSIZE, retries == 0);
+
+ retval = hub_port_reset(hub, port1, udev, delay, false);
+ if (retval < 0) /* error or disconnect */
+@@ -4866,14 +4886,13 @@ hub_port_init(struct usb_hub *hub, struc
+ retval = -ENODEV;
+ goto fail;
+ }
+- if (r) {
+- if (r != -ENODEV)
++ if (maxp0 < 0) {
++ if (maxp0 != -ENODEV)
+ dev_err(&udev->dev, "device descriptor read/64, error %d\n",
+- r);
+- retval = -EMSGSIZE;
++ maxp0);
++ retval = maxp0;
+ continue;
+ }
+-#undef GET_DESCRIPTOR_BUFSIZE
+ }
+
+ /*
+@@ -4919,19 +4938,17 @@ hub_port_init(struct usb_hub *hub, struc
+ break;
+ }
+
+- retval = usb_get_device_descriptor(udev, 8);
+- if (retval < 8) {
++ /* !do_new_scheme || wusb */
++ maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0);
++ if (maxp0 < 0) {
++ retval = maxp0;
+ if (retval != -ENODEV)
+ dev_err(&udev->dev,
+ "device descriptor read/8, error %d\n",
+ retval);
+- if (retval >= 0)
+- retval = -EMSGSIZE;
+ } else {
+ u32 delay;
+
+- retval = 0;
+-
+ delay = udev->parent->hub_delay;
+ udev->hub_delay = min_t(u32, delay,
+ USB_TP_TRANSMISSION_DELAY_MAX);
+@@ -4948,27 +4965,10 @@ hub_port_init(struct usb_hub *hub, struc
+ if (retval)
+ goto fail;
+
+- /*
+- * Some superspeed devices have finished the link training process
+- * and attached to a superspeed hub port, but the device descriptor
+- * got from those devices show they aren't superspeed devices. Warm
+- * reset the port attached by the devices can fix them.
+- */
+- if ((udev->speed >= USB_SPEED_SUPER) &&
+- (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+- dev_err(&udev->dev, "got a wrong device descriptor, "
+- "warm reset device\n");
+- hub_port_reset(hub, port1, udev,
+- HUB_BH_RESET_TIME, true);
+- retval = -EINVAL;
+- goto fail;
+- }
+-
+- if (udev->descriptor.bMaxPacketSize0 == 0xff ||
+- udev->speed >= USB_SPEED_SUPER)
++ if (maxp0 == 0xff || udev->speed >= USB_SPEED_SUPER)
+ i = 512;
+ else
+- i = udev->descriptor.bMaxPacketSize0;
++ i = maxp0;
+ if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
+ if (udev->speed == USB_SPEED_LOW ||
+ !(i == 8 || i == 16 || i == 32 || i == 64)) {
+@@ -4994,6 +4994,20 @@ hub_port_init(struct usb_hub *hub, struc
+ goto fail;
+ }
+
++ /*
++ * Some superspeed devices have finished the link training process
++ * and attached to a superspeed hub port, but the device descriptor
++ * got from those devices show they aren't superspeed devices. Warm
++ * reset the port attached by the devices can fix them.
++ */
++ if ((udev->speed >= USB_SPEED_SUPER) &&
++ (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
++ dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n");
++ hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
++ retval = -EINVAL;
++ goto fail;
++ }
++
+ usb_detect_quirks(udev);
+
+ if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+@@ -5016,6 +5030,7 @@ fail:
+ hub_port_disable(hub, port1, 0);
+ update_devnum(udev, devnum); /* for disconnect processing */
+ }
++ kfree(buf);
+ return retval;
+ }
+
--- /dev/null
+From f23643306430f86e2f413ee2b986e0773e79da31 Mon Sep 17 00:00:00 2001
+From: RD Babiera <rdbabiera@google.com>
+Date: Mon, 14 Aug 2023 18:05:59 +0000
+Subject: usb: typec: bus: verify partner exists in typec_altmode_attention
+
+From: RD Babiera <rdbabiera@google.com>
+
+commit f23643306430f86e2f413ee2b986e0773e79da31 upstream.
+
+Some usb hubs will negotiate DisplayPort Alt mode with the device
+but will then negotiate a data role swap after entering the alt
+mode. The data role swap causes the device to unregister all alt
+modes, however the usb hub will still send Attention messages
+even after failing to reregister the Alt Mode. type_altmode_attention
+currently does not verify whether or not a device's altmode partner
+exists, which results in a NULL pointer error when dereferencing
+the typec_altmode and typec_altmode_ops belonging to the altmode
+partner.
+
+Verify the presence of a device's altmode partner before sending
+the Attention message to the Alt Mode driver.
+
+Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes")
+Cc: stable@vger.kernel.org
+Signed-off-by: RD Babiera <rdbabiera@google.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/r/20230814180559.923475-1-rdbabiera@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/bus.c | 12 ++++++++++--
+ drivers/usb/typec/tcpm/tcpm.c | 3 ++-
+ include/linux/usb/typec_altmode.h | 2 +-
+ 3 files changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/typec/bus.c
++++ b/drivers/usb/typec/bus.c
+@@ -154,12 +154,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
+ *
+ * Notifies the partner of @adev about Attention command.
+ */
+-void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
++int typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
+ {
+- struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
++ struct altmode *partner = to_altmode(adev)->partner;
++ struct typec_altmode *pdev;
++
++ if (!partner)
++ return -ENODEV;
++
++ pdev = &partner->adev;
+
+ if (pdev->ops && pdev->ops->attention)
+ pdev->ops->attention(pdev, vdo);
++
++ return 0;
+ }
+ EXPORT_SYMBOL_GPL(typec_altmode_attention);
+
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -1871,7 +1871,8 @@ static void tcpm_handle_vdm_request(stru
+ }
+ break;
+ case ADEV_ATTENTION:
+- typec_altmode_attention(adev, p[1]);
++ if (typec_altmode_attention(adev, p[1]))
++ tcpm_log(port, "typec_altmode_attention no port partner altmode");
+ break;
+ }
+ }
+--- a/include/linux/usb/typec_altmode.h
++++ b/include/linux/usb/typec_altmode.h
+@@ -67,7 +67,7 @@ struct typec_altmode_ops {
+
+ int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo);
+ int typec_altmode_exit(struct typec_altmode *altmode);
+-void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
++int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
+ int typec_altmode_vdm(struct typec_altmode *altmode,
+ const u32 header, const u32 *vdo, int count);
+ int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf,
--- /dev/null
+From c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 Mon Sep 17 00:00:00 2001
+From: RD Babiera <rdbabiera@google.com>
+Date: Mon, 31 Jul 2023 16:59:23 +0000
+Subject: usb: typec: tcpm: set initial svdm version based on pd revision
+
+From: RD Babiera <rdbabiera@google.com>
+
+commit c97cd0b4b54eb42aed7f6c3c295a2d137f6d2416 upstream.
+
+When sending Discover Identity messages to a Port Partner that uses Power
+Delivery v2 and SVDM v1, we currently send PD v2 messages with SVDM v2.0,
+expecting the port partner to respond with its highest supported SVDM
+version as stated in Section 6.4.4.2.3 in the Power Delivery v3
+specification. However, sending SVDM v2 to some Power Delivery v2 port
+partners results in a NAK whereas sending SVDM v1 does not.
+
+NAK messages can be handled by the initiator (PD v3 section 6.4.4.2.5.1),
+and one solution could be to resend Discover Identity on a lower SVDM
+version if possible. But, Section 6.4.4.3 of PD v2 states that "A NAK
+response Should be taken as an indication not to retry that particular
+Command."
+
+Instead, we can set the SVDM version to the maximum one supported by the
+negotiated PD revision. When operating in PD v2, this obeys Section
+6.4.4.2.3, which states the SVDM field "Shall be set to zero to indicate
+Version 1.0." In PD v3, the SVDM field "Shall be set to 01b to indicate
+Version 2.0."
+
+Fixes: c34e85fa69b9 ("usb: typec: tcpm: Send DISCOVER_IDENTITY from dedicated work")
+Cc: stable@vger.kernel.org
+Signed-off-by: RD Babiera <rdbabiera@google.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://lore.kernel.org/r/20230731165926.1815338-1-rdbabiera@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c | 35 +++++++++++++++++++++++++++++++----
+ 1 file changed, 31 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -3929,6 +3929,29 @@ static enum typec_cc_status tcpm_pwr_opm
+ }
+ }
+
++static void tcpm_set_initial_svdm_version(struct tcpm_port *port)
++{
++ switch (port->negotiated_rev) {
++ case PD_REV30:
++ break;
++ /*
++ * 6.4.4.2.3 Structured VDM Version
++ * 2.0 states "At this time, there is only one version (1.0) defined.
++ * This field Shall be set to zero to indicate Version 1.0."
++ * 3.0 states "This field Shall be set to 01b to indicate Version 2.0."
++ * To ensure that we follow the Power Delivery revision we are currently
++ * operating on, downgrade the SVDM version to the highest one supported
++ * by the Power Delivery revision.
++ */
++ case PD_REV20:
++ typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
++ break;
++ default:
++ typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
++ break;
++ }
++}
++
+ static void run_state_machine(struct tcpm_port *port)
+ {
+ int ret;
+@@ -4153,10 +4176,12 @@ static void run_state_machine(struct tcp
+ * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
+ * port->explicit_contract to decide whether to send the command.
+ */
+- if (port->explicit_contract)
++ if (port->explicit_contract) {
++ tcpm_set_initial_svdm_version(port);
+ mod_send_discover_delayed_work(port, 0);
+- else
++ } else {
+ port->send_discover = false;
++ }
+
+ /*
+ * 6.3.5
+@@ -4439,10 +4464,12 @@ static void run_state_machine(struct tcp
+ * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
+ * port->explicit_contract.
+ */
+- if (port->explicit_contract)
++ if (port->explicit_contract) {
++ tcpm_set_initial_svdm_version(port);
+ mod_send_discover_delayed_work(port, 0);
+- else
++ } else {
+ port->send_discover = false;
++ }
+
+ power_supply_changed(port->psy);
+ break;