ioreamp() and modules

Takashi Oe toe at unlinfo.unl.edu
Tue Mar 2 18:13:57 EST 1999


Hi,

Recently, I've been trying to modify pm2fb.c so that it works for my
Permedia 2v card, but I'm not getting much success with ioremap().

I have a dummy driver which basically does ioremap() and some register
accesses and exits.  As long as the driver is compiled in, everything goes
as expected and things are nice.  However, when the driver is loaded via
insmod, it usually ends up causing a kernel exception.  The size of i/o
mapping seems to matter.  For example, if I try to ioremap the size of
0x10000 or so, it will cause the exception almost every time with 2.2.2
from cvs.samba.org as of yesterday, but, if the size is about 0x1000 or
so, I have to load the driver many times before causing the exception.
When the exception occurs, it usually goes like
	ioremap
	__ioremap
	map_page
	MMU_get_page
and, since MMU_get_page() is __initfunc(), there is no wonder I see the
exception, but I don't know why MMU_get_page() gets to be called in the
first place.  I don't remember exactly how it was with 2.2.1, but it
wasn't much better as I recall.

I have made a workaround and it's attached.  It just avoids using
map_page() and probably not good for performance reasons or something.
Anyhow, I am yet to see the exception with this patch.

Has anybody seen something like this?

My machine is Apple PowerMac 7600 with 604e/225MHz and 128MB RAM.


Takashi Oe
-------------- next part --------------
--- linux/arch/ppc/mm/init.c.ORIG	Sun Feb 28 17:59:59 1999
+++ linux/arch/ppc/mm/init.c	Tue Mar  2 00:23:20 1999
@@ -338,35 +338,45 @@
 	 */
 	p = addr & PAGE_MASK;
 	size = PAGE_ALIGN(addr + size) - p;
-	if (size == 0)
+	if (size == 0 || size >= size + p)
 		return NULL;
 
+	if ((flags & _PAGE_PRESENT) == 0)
+		flags |= pgprot_val(PAGE_KERNEL);
+	if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU))
+		flags |= _PAGE_GUARDED;
+
 	if (mem_init_done) {
 		struct vm_struct *area;
 		area = get_vm_area(size);
 		if (area == 0)
 			return NULL;
 		v = VMALLOC_VMADDR(area->addr);
+		for (i = 0; i < size; i += PAGE_SIZE) {
+			if(remap_page_range(v+i, p+i, PAGE_SIZE, __pgprot(flags))) {
+				vfree(area->addr);
+				return NULL;
+			}
+		}
 	} else {
 		if (p >= ioremap_base)
 			v = p;
 		else
 			v = (ioremap_bot -= size);
-	}
-
-	if ((flags & _PAGE_PRESENT) == 0)
-		flags |= pgprot_val(PAGE_KERNEL);
-	if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU))
-		flags |= _PAGE_GUARDED;
-	for (i = 0; i < size; i += PAGE_SIZE)
-		map_page(&init_task, v+i, p+i, flags);
 
+		for (i = 0; i < size; i += PAGE_SIZE)
+			map_page(&init_task, v+i, p+i, flags);
+	}
 	return (void *) (v + (addr & ~PAGE_MASK));
 }
 
 void iounmap(void *addr)
 {
-	/* XXX todo */
+	unsigned long v = (unsigned long)addr & PAGE_MASK;
+
+	if(v == iopa(v) || v < ioremap_base)
+		return;
+	vfree((void *)v);
 }
 
 unsigned long iopa(unsigned long addr)


More information about the Linuxppc-dev mailing list