--- a/sys/dev/disk/nata/ata-dma.c 2026-09-26 23:19:21.437252143 +0000 +++ b/sys/dev/disk/nata/ata-dma.c 2026-09-26 23:19:44.228957985 +0000 @@ -106,11 +106,40 @@ 0, &ch->dma->sg_tag)) goto error; - if (bus_dma_tag_create(ch->dma->dmatag,ch->dma->alignment,ch->dma->boundary, + /* + * DF-5083: DragonFly bus_dmamap_load() strips BUS_DMA_NOWAIT when a + * real map is supplied (busdma_machdep.c, "follow old semantics") and + * defers the transfer when the tag's bounce-page pool is short, + * returning EINPROGRESS with map->callback / map->callback_arg + * (ata_dmaload()'s STACK-allocated cba) / map->buf (the request + * buffer) kept armed. When bounce pages later free up, busdma_swi() + * re-runs the load and invokes the callback against ata_dmaload()'s + * dead stack frame and a buffer whose request has already completed: + * ata_dmasetprd() then reads args->dmatab from stale stack memory and + * writes PRD entries wherever that word points (kernel memory + * corruption), and re-walks the freed/reused buffer KVA. + * + * nata cannot handle deferral, so make it impossible by construction: + * - BUS_DMA_PRIVBZONE: a private bounce zone that no other driver + * can drain (a channel has exactly one data map and requests are + * serialized by the channel state machine, so the single map can + * never compete with itself); and + * - one extra page of tag maxsize, so the lazily allocated pool + * (atop(round_page(maxsize)) pages at bus_dmamap_create) covers a + * worst-case page-straddling transfer of max_iosize bytes + * (atop(max_iosize) + 1 pages). + * + * NOTE: chipset drivers that raise ch->dma->max_iosize after + * ata_dmainit() (ata-ahci.c, ata-marvell.c, ata-promise.c, + * ata-siliconimage.c) must size their own pool accordingly; that is + * addressed separately in those files. + */ + if (bus_dma_tag_create(ch->dma->dmatag, ch->dma->alignment, + ch->dma->boundary, ch->dma->max_address, BUS_SPACE_MAXADDR, - ch->dma->max_iosize, + ch->dma->max_iosize + PAGE_SIZE, ATA_DMA_ENTRIES, ch->dma->segsize, - 0, &ch->dma->data_tag)) + BUS_DMA_PRIVBZONE, &ch->dma->data_tag)) goto error; if (bus_dmamem_alloc(ch->dma->sg_tag, (void **)&ch->dma->sg, 0, @@ -251,7 +280,18 @@ if ((error = bus_dmamap_load(ch->dma->data_tag, ch->dma->data_map, data, count, ch->dma->setprd, &cba, BUS_DMA_NOWAIT)) || (error = cba.error)) + { + /* + * EINPROGRESS must be impossible with the private-zone data tag + * (see ata_dmaalloc()). It would leave the deferred callback + * armed against this function's dead stack frame -- fail hard + * rather than corrupt kernel memory later. + */ + if (error == EINPROGRESS) + panic("ata_dmaload: deferred DMA load (DF-5083) " + "-- private bounce pool too small"); return error; + } *entries = cba.nsegs;