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 =