Should we clear the scratch page to prevent that the dc(4) hardware
may see sensitive information?
bluhm
On Sat, Apr 02, 2016 at 05:53:03PM +0200, Mark Kettenis wrote:
> It is pretty clear that the DMA engine on the Davicom dc(4) hardware
> is broken and will read beyond the end of the buffer that we pass it.
> This is bad news for hardware that uses an IOMMU, as it will detect
> the DMA overrun and (at least on sparc64) signal an unrecoverable
> error.
>
> It is somewhat difficult to fix this in the driver. We could copy
> mbufs that we want to transmit into a buffer that has enough trailing
> space, but then we have to make sure that the buffer is physically
> contiguous and that the unused trailing space is mapped into the
> IOMMU.
>
> The diff below takes a different approach. It introduces a new
> BUS_DMA_OVERRUN flag. On sparc64 this flag will make sure that we map
> an additional scratch page into the IOMMU. This fixes the DMA errors
> that people reported ever since dlg@ changed the pools.
>
> If people agree with this approach, I will add a
>
> #define BUS_DMA_OVERRUN 0
>
> to all the other bus_dma implementations in the tree.
>
> Thoughts?
>
>
> Index: dev/ic/dc.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/ic/dc.c,v
> retrieving revision 1.149
> diff -u -p -r1.149 dc.c
> --- dev/ic/dc.c 28 Nov 2015 22:57:43 -0000 1.149
> +++ dev/ic/dc.c 2 Apr 2016 15:42:15 -0000
> @@ -2584,13 +2584,13 @@ dc_start(struct ifnet *ifp)
>
> map = sc->sc_tx_sparemap;
> switch (bus_dmamap_load_mbuf(sc->sc_dmat, map, m,
> - BUS_DMA_NOWAIT)) {
> + BUS_DMA_NOWAIT | BUS_DMA_OVERRUN)) {
> case 0:
> break;
> case EFBIG:
> if (m_defrag(m, M_DONTWAIT) == 0 &&
> bus_dmamap_load_mbuf(sc->sc_dmat, map, m,
> - BUS_DMA_NOWAIT) == 0)
> + BUS_DMA_NOWAIT | BUS_DMA_OVERRUN) == 0)
> break;
>
> /* FALLTHROUGH */
> Index: arch/sparc64/dev/iommu.c
> ===================================================================
> RCS file: /cvs/src/sys/arch/sparc64/dev/iommu.c,v
> retrieving revision 1.72
> diff -u -p -r1.72 iommu.c
> --- arch/sparc64/dev/iommu.c 9 Jan 2015 14:23:25 -0000 1.72
> +++ arch/sparc64/dev/iommu.c 2 Apr 2016 15:42:17 -0000
> @@ -195,6 +195,13 @@ iommu_init(char *name, struct iommu_stat
> pmap_update(pmap_kernel());
> memset(is->is_tsb, 0, size);
>
> + TAILQ_INIT(&mlist);
> + if (uvm_pglistalloc(PAGE_SIZE, (paddr_t)0, (paddr_t)-1,
> + (paddr_t)PAGE_SIZE, (paddr_t)0, &mlist, 1, UVM_PLA_NOWAIT) != 0)
> + panic("iommu_init: no memory");
> + m = TAILQ_FIRST(&mlist);
> + is->is_scratch = VM_PAGE_TO_PHYS(m);
> +
> #ifdef DEBUG
> if (iommudebug & IDB_INFO) {
> /* Probe the iommu */
> @@ -734,6 +741,13 @@ iommu_dvmamap_load(bus_dma_tag_t t, bus_
> }
> }
> }
> + if (flags & BUS_DMA_OVERRUN) {
> + err = iommu_iomap_insert_page(ims, is->is_scratch);
> + if (err) {
> + iommu_iomap_clear_pages(ims);
> + return (EFBIG);
> + }
> + }
> sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
>
> mtx_enter(&is->is_mtx);
> @@ -939,6 +953,13 @@ iommu_dvmamap_load_raw(bus_dma_tag_t t,
> }
>
> left -= seg_len;
> + }
> + }
> + if (flags & BUS_DMA_OVERRUN) {
> + err = iommu_iomap_insert_page(ims, is->is_scratch);
> + if (err) {
> + iommu_iomap_clear_pages(ims);
> + return (EFBIG);
> }
> }
> sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
> Index: arch/sparc64/dev/iommuvar.h
> ===================================================================
> RCS file: /cvs/src/sys/arch/sparc64/dev/iommuvar.h,v
> retrieving revision 1.16
> diff -u -p -r1.16 iommuvar.h
> --- arch/sparc64/dev/iommuvar.h 22 Jan 2014 10:52:35 -0000 1.16
> +++ arch/sparc64/dev/iommuvar.h 2 Apr 2016 15:42:17 -0000
> @@ -117,6 +117,8 @@ struct iommu_state {
>
> struct strbuf_ctl *is_sb[2]; /* Streaming buffers if any */
>
> + paddr_t is_scratch; /* Scratch page */
> +
> /* copies of our parents state, to allow us to be self contained */
> bus_space_tag_t is_bustag; /* our bus tag */
> bus_space_handle_t is_iommu; /* IOMMU registers */
> Index: arch/sparc64/dev/viommu.c
> ===================================================================
> RCS file: /cvs/src/sys/arch/sparc64/dev/viommu.c,v
> retrieving revision 1.16
> diff -u -p -r1.16 viommu.c
> --- arch/sparc64/dev/viommu.c 9 Jan 2015 14:23:25 -0000 1.16
> +++ arch/sparc64/dev/viommu.c 2 Apr 2016 15:42:17 -0000
> @@ -106,6 +106,9 @@ void
> viommu_init(char *name, struct iommu_state *is, int tsbsize,
> u_int32_t iovabase)
> {
> + struct vm_page *m;
> + struct pglist mlist;
> +
> /*
> * Setup the iommu.
> *
> @@ -121,6 +124,13 @@ viommu_init(char *name, struct iommu_sta
> is->is_dvmaend = iovabase + IOTSB_VSIZE(tsbsize) - 1;
> }
>
> + TAILQ_INIT(&mlist);
> + if (uvm_pglistalloc(PAGE_SIZE, (paddr_t)0, (paddr_t)-1,
> + (paddr_t)PAGE_SIZE, (paddr_t)0, &mlist, 1, UVM_PLA_NOWAIT) != 0)
> + panic("%s: no memory", __func__);
> + m = TAILQ_FIRST(&mlist);
> + is->is_scratch = VM_PAGE_TO_PHYS(m);
> +
> /*
> * Allocate a dvma map.
> */
> @@ -341,6 +351,13 @@ viommu_dvmamap_load(bus_dma_tag_t t, bus
> }
> }
> }
> + if (flags & BUS_DMA_OVERRUN) {
> + err = iommu_iomap_insert_page(ims, is->is_scratch);
> + if (err) {
> + iommu_iomap_clear_pages(ims);
> + return (EFBIG);
> + }
> + }
> sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
>
> mtx_enter(&is->is_mtx);
> @@ -522,6 +539,13 @@ viommu_dvmamap_load_raw(bus_dma_tag_t t,
> }
>
> left -= seg_len;
> + }
> + }
> + if (flags & BUS_DMA_OVERRUN) {
> + err = iommu_iomap_insert_page(ims, is->is_scratch);
> + if (err) {
> + iommu_iomap_clear_pages(ims);
> + return (EFBIG);
> }
> }
> sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
> Index: arch/sparc64/include/bus.h
> ===================================================================
> RCS file: /cvs/src/sys/arch/sparc64/include/bus.h,v
> retrieving revision 1.29
> diff -u -p -r1.29 bus.h
> --- arch/sparc64/include/bus.h 13 May 2013 17:46:42 -0000 1.29
> +++ arch/sparc64/include/bus.h 2 Apr 2016 15:42:17 -0000
> @@ -356,19 +356,20 @@ bus_space_barrier(t, h, o, s, f)
> /*
> * Flags used in various bus DMA methods.
> */
> -#define BUS_DMA_WAITOK 0x000 /* safe to sleep (pseudo-flag)
> */
> -#define BUS_DMA_NOWAIT 0x001 /* not safe to sleep */
> -#define BUS_DMA_ALLOCNOW 0x002 /* perform resource allocation
> now */
> -#define BUS_DMA_COHERENT 0x004 /* hint: map memory DMA
> coherent */
> -#define BUS_DMA_NOWRITE 0x008 /* I suppose the following two
> should default on */
> -#define BUS_DMA_BUS1 0x010 /* placeholders for bus
> functions... */
> -#define BUS_DMA_BUS2 0x020
> -#define BUS_DMA_BUS3 0x040
> -#define BUS_DMA_BUS4 0x080
> -#define BUS_DMA_STREAMING 0x100 /* hint: sequential,
> unidirectional */
> -#define BUS_DMA_READ 0x200 /* mapping is device -> memory
> only */
> -#define BUS_DMA_WRITE 0x400 /* mapping is memory -> device
> only */
> -#define BUS_DMA_ZERO 0x800 /* zero memory in dmamem_alloc
> */
> +#define BUS_DMA_WAITOK 0x0000 /* safe to sleep (pseudo-flag)
> */
> +#define BUS_DMA_NOWAIT 0x0001 /* not safe to sleep */
> +#define BUS_DMA_ALLOCNOW 0x0002 /* perform resource allocation
> now */
> +#define BUS_DMA_COHERENT 0x0004 /* hint: map memory DMA
> coherent */
> +#define BUS_DMA_NOWRITE 0x0008 /* I suppose the following two
> should default on */
> +#define BUS_DMA_BUS1 0x0010 /* placeholders for bus
> functions... */
> +#define BUS_DMA_BUS2 0x0020
> +#define BUS_DMA_BUS3 0x0040
> +#define BUS_DMA_BUS4 0x0080
> +#define BUS_DMA_STREAMING 0x0100 /* hint: sequential,
> unidirectional */
> +#define BUS_DMA_READ 0x0200 /* mapping is device -> memory
> only */
> +#define BUS_DMA_WRITE 0x0400 /* mapping is memory -> device
> only */
> +#define BUS_DMA_ZERO 0x0800 /* zero memory in dmamem_alloc
> */
> +#define BUS_DMA_OVERRUN 0x1000 /* tolerate DMA overruns */
>
> #define BUS_DMA_NOCACHE BUS_DMA_BUS1
> #define BUS_DMA_DVMA BUS_DMA_BUS2 /* Don't bother with
> alignment */