mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
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>
This commit is contained in:
committed by
Dom Cobley
parent
245f894baf
commit
1adfa3fec3
@@ -2448,7 +2448,7 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb)
|
||||
|
||||
/* Restore urb->transfer_buffer from the end of the allocated area */
|
||||
memcpy(&stored_xfer_buffer,
|
||||
PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length,
|
||||
PTR_ALIGN(urb->transfer_buffer + urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN,
|
||||
dma_get_cache_alignment()),
|
||||
sizeof(urb->transfer_buffer));
|
||||
|
||||
@@ -2471,17 +2471,36 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
|
||||
void *kmalloc_ptr;
|
||||
size_t kmalloc_size;
|
||||
|
||||
if (urb->num_sgs || urb->sg ||
|
||||
urb->transfer_buffer_length == 0 ||
|
||||
if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Hardware bug: the core will only do DMA writes of full words
|
||||
* in length, and DMA buffers must start at a word boundary.
|
||||
* TODO: is this limited to BCM2835 and friends, or other core variants?
|
||||
*/
|
||||
if (!(usb_urb_dir_in(urb) && (urb->transfer_buffer_length & (DWC2_USB_DMA_ALIGN - 1))) &&
|
||||
!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the URB already has a DMA mapping, this alignment mechanism won't
|
||||
* work - the replacement buffer won't be used by the core, as the HCD layer
|
||||
* skips mapping. Mappings have the granularity of a page, so it's unlikely that the
|
||||
* DMA length bug will cause data trampling. In any case, warn if there's a driver
|
||||
* submitting unaligned mapped buffers.
|
||||
*/
|
||||
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
|
||||
if (urb->transfer_dma & (DWC2_USB_DMA_ALIGN - 1))
|
||||
WARN_ONCE(1, "Unaligned DMA-mapped buffer");
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Allocate a buffer with enough padding for original transfer_buffer
|
||||
* pointer. This allocation is guaranteed to be aligned properly for
|
||||
* DMA
|
||||
*/
|
||||
kmalloc_size = urb->transfer_buffer_length +
|
||||
kmalloc_size = urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN +
|
||||
(dma_get_cache_alignment() - 1) +
|
||||
sizeof(urb->transfer_buffer);
|
||||
|
||||
@@ -2493,7 +2512,7 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
|
||||
* Position value of original urb->transfer_buffer pointer to the end
|
||||
* of allocation for later referencing
|
||||
*/
|
||||
memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length,
|
||||
memcpy(PTR_ALIGN(kmalloc_ptr + urb->transfer_buffer_length + DWC2_USB_DMA_ALIGN,
|
||||
dma_get_cache_alignment()),
|
||||
&urb->transfer_buffer, sizeof(urb->transfer_buffer));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user