Subject: Managing DVMA
To: None <dej@eecg.toronto.edu>
From: Gordon W. Ross <gwr@mc.com>
List: port-sun3
Date: 02/10/1995 12:33:40
> From: David Jones <dej@eecg.toronto.edu>
> Date: 	Fri, 10 Feb 1995 00:04:33 -0500
> X-Mailer: ELM [version 2.3 PL11]
> Sender: owner-port-sun3@netbsd.org
> Precedence: list
> X-Loop: port-sun3@NetBSD.ORG
> 
> I'm writing the Xylogics 450 driver, and I think I've got most
> everything figured out except for one thing:
> 
> Given that I have called dvma_vm_alloc() to allocate space in
> the DVMA area for the controller to DMA into, and given a
> page in kernel space pointed to by a bp, what's necessary
> for me to map the data page into the DVMA space?

It is a little involved.  First, for if the B_PHYS flag is set
in the buf you are passed, then physio will have already mapped
the pages into DVMA space for you.  Otherwise, the driver should
call some (yet-to-be-created) dvma function to remap the buffer
into DVMA space.  I've attached a first-cut implementation.

> pagemove() looks interesting, but it claims to work only if
> both addresses are in the kernel map.

That's just for use by the VM code, and not appropriate here.
The buffer addresses passed to strategy are always in kernel.

Note that once the pages are mapped into DVMA space, the
transfer (to or from DVMA space) will have a contiguous
address range, even if the user pages were scattered!

Gordon

[ example code attached ]


We (I?) should create a dvma module:
(this is a "first cut" - may be buggy)

/* sun3/dvma.c */

long dvma_kvtovme(long dvma_addr)
{
	if (dvma_addr < DVMA_SPACE_START)
		panic("dvma_kvtovme");
	return (dvma_addr - DVMA_SPACE_START);
}

dvma_remap(char *kva, int len)
{
	vm_offset_t phys, dvma_addr;

	/* XXX - multiple pages possible? */
	if (len > NBPG)
		return (0);

	phys = pmap_extract(kernel_map, (vm_offset_t)kva);
	if (phys == 0)
		panic("dvma_setup: phys=0");

	/* Get some DVMA space. */
	dvma = dvma_vm_alloc(1);
	if (dvma == NULL)
		panic("dvma_setup: dvma=0");

	/* Duplicate the mapping there, non-cached. */	
	pmap_enter(kernel_pmap, dvma_addr,
			phys_addr | PMAP_NC,
			phys_addr + NBPG,
			VM_PROT_ALL, FALSE);

	return(dvma_addr);
}

void dvma_unmap(long dvma_addr, int len)
{
	pmap_remove(kernel_pmap, dvma_addr,
			dvma_addr + len);
	dvma_vm_free(dvma_addr, len); /* ? */
}


Then, the xy code might look something like this:

/* dev/xy.c */

xy_dma_setup(sc, bp, ...)
	struct xy_softc *sc;
	struct buf *bp;
{
	vm_offset_t dvma_addr, vme_addr;

	if (bp->bp_flags & B_PHYS) {
		/* Could be multiple pages here. */
		dvma_addr = bp->b_data;
	} else {
		dvma_addr = dvma_remap(bp->b_data, bp->b_bcount);
	}

	if (dvma_addr == NULL)
		panic("xy_dma_setup");

	vme_addr = dvma_kvtovme(dvma_addr);

	/* Tell the xy to use the vme_addr ... */
}