Module Name:    src
Committed By:   thorpej
Date:           Mon Nov 20 15:16:46 UTC 2023

Modified Files:
        src/sys/dev/pci: pciide_common.c

Log Message:
pciide_dma_dmamap_setup(): If we end up with a DMA segment with an odd
length, unload the map and return EIO.  Some controllers get really upset
if a DMA segment has an odd length.  This can happen if a physio user
performs a virtually-contiguous I/O that starts at an odd address and spans
a page boundary where the resulting physical pages are discontiguous.

Ultimately, it's up to the physio user to paint inside the lines, but this
will prevent the disk controller from wandering off into the weeds, at least.

PR port-alpha/56434


To generate a diff of this commit:
cvs rdiff -u -r1.67 -r1.68 src/sys/dev/pci/pciide_common.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/pciide_common.c
diff -u src/sys/dev/pci/pciide_common.c:1.67 src/sys/dev/pci/pciide_common.c:1.68
--- src/sys/dev/pci/pciide_common.c:1.67	Mon Aug 24 05:37:41 2020
+++ src/sys/dev/pci/pciide_common.c	Mon Nov 20 15:16:46 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: pciide_common.c,v 1.67 2020/08/24 05:37:41 msaitoh Exp $	*/
+/*	$NetBSD: pciide_common.c,v 1.68 2023/11/20 15:16:46 thorpej Exp $	*/
 
 
 /*
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pciide_common.c,v 1.67 2020/08/24 05:37:41 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pciide_common.c,v 1.68 2023/11/20 15:16:46 thorpej Exp $");
 
 #include <sys/param.h>
 
@@ -735,6 +735,31 @@ pciide_dma_dmamap_setup(struct pciide_so
 		}
 		}
 #endif
+		/*
+		 * Some controllers get really upset if the length
+		 * of any DMA segment is odd.  This isn't something
+		 * that's going to happen in normal steady-state
+		 * operation (reading VM pages, etc.), but physio users
+		 * don't have as many guard rails.
+		 *
+		 * Consider an 8K read request that starts at an odd
+		 * offset within a page.  At first blush, all of the
+		 * checks pass because it's a sector-rounded size, but
+		 * unless the buffer spans 2 physically contiguous pages,
+		 * it's going to result in 2 odd-length DMA segments.
+		 */
+		if (dma_maps->dmamap_xfer->dm_segs[seg].ds_len & 1) {
+			unsigned long long phys =
+			    dma_maps->dmamap_xfer->dm_segs[seg].ds_addr;
+			unsigned long long len =
+			    dma_maps->dmamap_xfer->dm_segs[seg].ds_len;
+			aprint_verbose_dev(sc->sc_wdcdev.sc_atac.atac_dev,
+			    "ODD segment length: "
+			    "seg %d addr 0x%llx len 0x%llx\n",
+			    seg, phys, len);
+			bus_dmamap_unload(sc->sc_dmat, dma_maps->dmamap_xfer);
+			return EIO;
+		}
 		dma_maps->dma_table[seg].base_addr =
 		    htole32(dma_maps->dmamap_xfer->dm_segs[seg].ds_addr);
 		dma_maps->dma_table[seg].byte_count =

Reply via email to