Skip to content

Commit 75abda3

Browse files
P33Mpopcornmix
authored andcommitted
drivers: dwc2: better handle hardware length & alignment issues
The version of the dwc-otg core used in BCM2835 through BCM2712 only does whole-word writes, as well as needing the documented requirement for DMA buffers to start on a word boundary. Also, the alignment method used in the dwc2 driver doesn't handle the case where the URB has the NO_TRANSFER_DMA_MAP flag set, so reject buffers that have unaligned DMA start addresses. At least one whole page should be mapped, so the BCM283x whole-word-write bug should be benign in this case. Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
1 parent 3c3bf0a commit 75abda3

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

drivers/usb/dwc2/hcd.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,7 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb)
24472447

24482448
/* Restore urb->transfer_buffer from the end of the allocated area */
24492449
memcpy(&stored_xfer_buffer,
2450-
PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length,
2450+
PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN,
24512451
dma_get_cache_alignment()),
24522452
sizeof(urb->transfer_buffer));
24532453

@@ -2470,17 +2470,36 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
24702470
void *kmalloc_ptr;
24712471
size_t kmalloc_size;
24722472

2473-
if (urb->num_sgs || urb->sg ||
2474-
urb->transfer_buffer_length == 0 ||
2473+
if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
2474+
return 0;
2475+
2476+
/*
2477+
* Hardware bug: the core will only do DMA writes of full words
2478+
* in length, and DMA buffers must start at a word boundary.
2479+
* TODO: is this limited to BCM2835 and friends, or other core variants?
2480+
*/
2481+
if (!(usb_urb_dir_in(urb) && (urb->transfer_buffer_length & (DWC2_USB_DMA_ALIGN - 1))) &&
24752482
!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
24762483
return 0;
24772484

2485+
/*
2486+
* If the URB already has a DMA mapping, this alignment mechanism won't
2487+
* work - the replacement buffer won't be used by the core, as the HCD layer
2488+
* skips mapping. Mappings have the granularity of a page, so it's unlikely that the
2489+
* DMA length bug will cause data trampling. In any case, warn if there's a driver
2490+
* submitting unaligned mapped buffers.
2491+
*/
2492+
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
2493+
if (urb->transfer_dma & (DWC2_USB_DMA_ALIGN - 1))
2494+
WARN_ONCE(1, "Unaligned DMA-mapped buffer");
2495+
return 0;
2496+
}
24782497
/*
24792498
* Allocate a buffer with enough padding for original transfer_buffer
24802499
* pointer. This allocation is guaranteed to be aligned properly for
24812500
* DMA
24822501
*/
2483-
kmalloc_size = urb->transfer_buffer_length +
2502+
kmalloc_size = urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN +
24842503
(dma_get_cache_alignment() - 1) +
24852504
sizeof(urb->transfer_buffer);
24862505

@@ -2492,7 +2511,7 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
24922511
* Position value of original urb->transfer_buffer pointer to the end
24932512
* of allocation for later referencing
24942513
*/
2495-
memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length,
2514+
memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN,
24962515
dma_get_cache_alignment()),
24972516
&urb->transfer_buffer, sizeof(urb->transfer_buffer));
24982517

0 commit comments

Comments
 (0)