From: Roman.Hodek@informatik.uni-erlangen.de (Roman Hodek)
Date: Sun, 5 Oct 1997 18:09:19 +0200 (CEST)
To: linux-m68k@lists.linux-m68k.org
Subject: L68K: kernel_unmap()
Sender: owner-linux-m68k@phil.uni-sb.de


Here is the long-promised implementation of a kernel_unmap() function
(hi Geert&Bernd... :-).

Since all the stuff got bigger and bigger, I've moved kernel_map(),
kernel_unmap(), kernel_set_cachemode() and their related functions
into a new file kmap.c. A simple list is used to record allocated
regions in the virtual address space. And I also extended the address
area available to kernel_map() from 16 to 32 MB, the new address range
is 0xd0000000..0xefffffff. (This could also easily be extended in the
future if needed.)

I now also cared for races in and between kernel_{,un}map(). The
'kmap_regions' list must be protected from concurrent access anyway, I
use a semaphore for this. This sema also is down the whole time while
kernel_unmap() is running, because this was the simplest solution to
make it race-free :-) (performance isn't a big issue here...)
Otherwise it'd be somewhat complicated to check all potential
interactions between kernel_map() and kernel_unmap()...

Roman

------------------------------------------------------------------------------
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.57.orig/arch/m68k/mm/Makefile linux-2.1.57/arch/m68k/mm/Makefile
--- linux-2.1.57.orig/arch/m68k/mm/Makefile	Sat Nov  9 18:57:05 1996
+++ linux-2.1.57/arch/m68k/mm/Makefile	Wed Oct  1 22:53:23 1997
@@ -8,6 +8,6 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-O_OBJS	 := init.o fault.o memory.o extable.o
+O_OBJS	 := init.o fault.o memory.o kmap.o extable.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.57.orig/arch/m68k/mm/kmap.c linux-2.1.57/arch/m68k/mm/kmap.c
--- linux-2.1.57.orig/arch/m68k/mm/kmap.c	Thu Jan  1 01:00:00 1970
+++ linux-2.1.57/arch/m68k/mm/kmap.c	Sun Oct  5 17:42:27 1997
@@ -0,0 +1,564 @@
+/*
+ *  linux/arch/m68k/mm/kmap.c
+ *
+ *  Copyright (C) 1997 Roman Hodek
+ */
+
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+
+
+extern pte_t *kernel_page_table (unsigned long *memavailp);
+
+/* Virtual address region for use by kernel_map() */
+#define	KMAP_START	0xd0000000
+#define	KMAP_END	0xf0000000
+
+/* Granularity of kernel_map() allocations */
+#define KMAP_STEP	(256*1024)
+
+/* Size of pool of KMAP structures; that is needed, because kernel_map() can
+ * be called at times where kmalloc() isn't initialized yet. */
+#define	KMAP_POOL_SIZE	16
+
+/* structure for maintainance of kmap regions */
+typedef struct kmap {
+	struct kmap *next, *prev;	/* linking of list */
+	unsigned long addr;			/* start address of region */
+	unsigned long mapaddr;		/* address returned to user */
+	unsigned long size;			/* size of region */
+	unsigned free : 1;			/* flag whether free or allocated */
+	unsigned kmalloced : 1;		/* flag whether got this from kmalloc() */
+	unsigned pool_alloc : 1;	/* flag whether got this is alloced in pool */
+} KMAP;
+
+KMAP kmap_pool[KMAP_POOL_SIZE] = {
+	{ NULL, NULL, KMAP_START, KMAP_START, KMAP_END-KMAP_START, 1, 0, 1 },
+	{ NULL, NULL, 0, 0, 0, 0, 0, 0 },
+};
+
+/*
+ * anchor of kmap region list
+ *
+ * The list is always ordered by addresses, and regions are always adjacent,
+ * i.e. there must be no holes between them!
+ */
+KMAP *kmap_regions = &kmap_pool[0];
+
+/* for protecting the kmap_regions list against races */
+static struct semaphore kmap_sem = MUTEX;
+
+
+
+/*
+ * Low-level allocation and freeing of KMAP structures
+ */
+static KMAP *alloc_kmap( int use_kmalloc )
+{
+	KMAP *p;
+	int i;
+
+	/* first try to get from the pool if possible */
+	for( i = 0; i < KMAP_POOL_SIZE; ++i ) {
+		if (!kmap_pool[i].pool_alloc) {
+			kmap_pool[i].kmalloced = 0;
+			kmap_pool[i].pool_alloc = 1;
+			return( &kmap_pool[i] );
+		}
+	}
+	
+	if (use_kmalloc && (p = (KMAP *)kmalloc( sizeof(KMAP), GFP_KERNEL ))) {
+		p->kmalloced = 1;
+		return( p );
+	}
+	
+	return( NULL );
+}
+
+static void free_kmap( KMAP *p )
+{
+	if (p->kmalloced)
+		kfree( p );
+	else
+		p->pool_alloc = 0;
+}
+
+
+/*
+ * Get a free region from the kmap address range
+ */
+static KMAP *kmap_get_region( unsigned long size, int use_kmalloc )
+{
+	KMAP *p, *q;
+
+	/* look for a suitable free region */
+	for( p = kmap_regions; p; p = p->next )
+		if (p->free && p->size >= size)
+			break;
+	if (!p) {
+		printk( KERN_ERR "kernel_map: address space for "
+				"allocations exhausted\n" );
+		return( NULL );
+	}
+	
+	if (p->size > size) {
+		/* if free region is bigger than we need, split off the rear free part
+		 * into a new region */
+		if (!(q = alloc_kmap( use_kmalloc ))) {
+			printk( KERN_ERR "kernel_map: out of memory\n" );
+			return( NULL );
+		}
+		q->addr = p->addr + size;
+		q->size = p->size - size;
+		p->size = size;
+		q->free = 1;
+
+		q->prev = p;
+		q->next = p->next;
+		p->next = q;
+		if (q->next) q->next->prev = q;
+	}
+	
+	p->free = 0;
+	return( p );
+}
+
+
+/*
+ * Free a kernel_map region again
+ */
+static void kmap_put_region( KMAP *p )
+{
+	KMAP *q;
+
+	p->free = 1;
+
+	/* merge with previous region if possible */
+	q = p->prev;
+	if (q && q->free) {
+		if (q->addr + q->size != p->addr) {
+			printk( KERN_ERR "kernel_malloc: allocation list destroyed\n" );
+			return;
+		}
+		q->size += p->size;
+		q->next = p->next;
+		if (p->next) p->next->prev = q;
+		free_kmap( p );
+		p = q;
+	}
+
+	/* merge with following region if possible */
+	q = p->next;
+	if (q && q->free) {
+		if (p->addr + p->size != q->addr) {
+			printk( KERN_ERR "kernel_malloc: allocation list destroyed\n" );
+			return;
+		}
+		p->size += q->size;
+		p->next = q->next;
+		if (q->next) q->next->prev = p;
+		free_kmap( q );
+	}
+}
+
+
+/*
+ * kernel_map() helpers
+ */
+static inline pte_t *
+pte_alloc_kernel_map(pmd_t *pmd, unsigned long address,
+		     unsigned long *memavailp)
+{
+	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
+	if (pmd_none(*pmd)) {
+		pte_t *page = kernel_page_table(memavailp);
+		if (pmd_none(*pmd)) {
+			if (page) {
+				pmd_set(pmd, page);
+				memset( page, 0, PAGE_SIZE );
+				return page + address;
+			}
+			pmd_set(pmd, BAD_PAGETABLE);
+			return NULL;
+		}
+		if (memavailp)
+			panic("kernel_map: slept during init?!?");
+		cache_page((unsigned long) page);
+		free_page((unsigned long) page);
+	}
+	if (pmd_bad(*pmd)) {
+		printk( KERN_ERR "Bad pmd in pte_alloc_kernel_map: %08lx\n",
+		       pmd_val(*pmd));
+		pmd_set(pmd, BAD_PAGETABLE);
+		return NULL;
+	}
+	return (pte_t *) pmd_page(*pmd) + address;
+}
+
+static inline void
+kernel_map_pte(pte_t *pte, unsigned long address, unsigned long size,
+	       unsigned long phys_addr, pgprot_t prot)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_val(*pte) = phys_addr + pgprot_val(prot);
+		address += PAGE_SIZE;
+		phys_addr += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+}
+
+static inline int
+kernel_map_pmd (pmd_t *pmd, unsigned long address, unsigned long size,
+		unsigned long phys_addr, pgprot_t prot,
+		unsigned long *memavailp)
+{
+	unsigned long end;
+
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	phys_addr -= address;
+
+	if (CPU_IS_040_OR_060) {
+		do {
+			pte_t *pte = pte_alloc_kernel_map(pmd, address, memavailp);
+			if (!pte)
+				return -ENOMEM;
+			kernel_map_pte(pte, address, end - address,
+				       address + phys_addr, prot);
+			address = (address + PMD_SIZE) & PMD_MASK;
+			pmd++;
+		} while (address < end);
+	} else {
+		/* On the 68030 we use early termination page descriptors.
+		   Each one points to 64 pages (256K). */
+		int i = (address >> (PMD_SHIFT-4)) & 15;
+		do {
+			(&pmd_val(*pmd))[i++] = (address + phys_addr) | pgprot_val(prot);
+			address += PMD_SIZE / 16;
+		} while (address < end);
+	}
+	return 0;
+}
+
+
+/*
+ * Map some physical address range into the kernel address space. The
+ * code is copied and adapted from map_chunk().
+ */
+/* Rewritten by Andreas Schwab to remove all races. */
+
+unsigned long kernel_map(unsigned long phys_addr, unsigned long size,
+			 int cacheflag, unsigned long *memavailp)
+{
+	unsigned long retaddr, from, end;
+	pgd_t *dir;
+	pgprot_t prot;
+	KMAP *kmap;
+
+	/* Round down 'phys_addr' to 256 KB and adjust size */
+	retaddr = phys_addr & (KMAP_STEP-1);
+	size += retaddr;
+	phys_addr &= ~(KMAP_STEP-1);
+	/* Round up the size to 256 KB. It doesn't hurt if too much is
+	   mapped... */
+	size = (size + KMAP_STEP - 1) & ~(KMAP_STEP-1);
+	
+	down( &kmap_sem );
+	if (!(kmap = kmap_get_region( size, memavailp == NULL )))
+		return( 0 );
+	from = kmap->addr;
+	retaddr += from;
+	kmap->mapaddr = retaddr;
+	end = from + size;
+	up( &kmap_sem );
+
+	if (CPU_IS_040_OR_060) {
+		pgprot_val(prot) = (_PAGE_PRESENT | _PAGE_GLOBAL040 |
+				    _PAGE_ACCESSED | _PAGE_DIRTY);
+		switch (cacheflag) {
+		case KERNELMAP_FULL_CACHING:
+			pgprot_val(prot) |= _PAGE_CACHE040;
+			break;
+		case KERNELMAP_NOCACHE_SER:
+		default:
+			pgprot_val(prot) |= _PAGE_NOCACHE_S;
+			break;
+		case KERNELMAP_NOCACHE_NONSER:
+			pgprot_val(prot) |= _PAGE_NOCACHE;
+			break;
+		case KERNELMAP_NO_COPYBACK:
+			pgprot_val(prot) |= _PAGE_CACHE040W;
+			break;
+		}
+	} else
+		pgprot_val(prot) = (_PAGE_PRESENT | _PAGE_ACCESSED |
+				    _PAGE_DIRTY |
+				    ((cacheflag == KERNELMAP_FULL_CACHING ||
+				      cacheflag == KERNELMAP_NO_COPYBACK)
+				     ? 0 : _PAGE_NOCACHE030));
+
+	phys_addr -= from;
+	dir = pgd_offset_k(from);
+	while (from < end) {
+		pmd_t *pmd = pmd_alloc_kernel(dir, from);
+
+		if (kernel_map_pmd(pmd, from, end - from, phys_addr + from,
+				   prot, memavailp)) {
+			printk( KERN_ERR "kernel_map: out of memory\n" );
+			return 0UL;
+		}
+		from = (from + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+
+	return retaddr;
+}
+
+
+/*
+ * kernel_unmap() helpers
+ */
+static inline void pte_free_kernel_unmap( pmd_t *pmd )
+{
+	unsigned long page = pmd_page(*pmd);
+	mem_map_t *pagemap = &mem_map[MAP_NR(page)];
+	
+	pmd_clear(pmd);
+	cache_page(page);
+
+	if (PageReserved( pagemap )) {
+		/* need to unreserve pages that were allocated with memavailp != NULL;
+		 * this works only if 'page' is page-aligned */
+		if (page & ~PAGE_MASK)
+			return;
+		clear_bit( PG_reserved, &pagemap->flags );
+		atomic_set( &pagemap->count, 1 );
+	}
+	free_page( page );
+}
+
+/*
+ * This not only unmaps the requested region, but also loops over the whole
+ * pmd to determine whether the other pte's are clear (so that the page can be
+ * freed.) If so, it returns 1, 0 otherwise.
+ */
+static inline int
+kernel_unmap_pte_range(pmd_t * pmd, unsigned long address, unsigned long size)
+{
+	pte_t *pte;
+	unsigned long addr2, end, end2;
+	int all_clear = 1;
+
+	if (pmd_none(*pmd))
+		return( 0 );
+	if (pmd_bad(*pmd)) {
+		printk( KERN_ERR "kernel_unmap_pte_range: bad pmd (%08lx)\n",
+				pmd_val(*pmd) );
+		pmd_clear(pmd);
+		return( 0 );
+	}
+	address &= ~PMD_MASK;
+	addr2 = 0;
+	pte = pte_offset(pmd, addr2);
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	end2 = addr2 + PMD_SIZE;
+	while( addr2 < end2 ) {
+		if (!pte_none(*pte)) {
+			if (address <= addr2 && addr2 < end)
+				pte_clear(pte);
+			else
+				all_clear = 0;
+		}
+		++pte;
+		addr2 += PAGE_SIZE;
+	}
+	return( all_clear );
+}
+
+static inline void
+kernel_unmap_pmd_range(pgd_t * dir, unsigned long address, unsigned long size)
+{
+	pmd_t * pmd;
+	unsigned long end;
+
+	if (pgd_none(*dir))
+		return;
+	if (pgd_bad(*dir)) {
+		printk( KERN_ERR "kernel_unmap_pmd_range: bad pgd (%08lx)\n",
+				pgd_val(*dir) );
+		pgd_clear(dir);
+		return;
+	}
+	pmd = pmd_offset(dir, address);
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	
+	if (CPU_IS_040_OR_060) {
+		do {
+			if (kernel_unmap_pte_range(pmd, address, end - address))
+				pte_free_kernel_unmap( pmd );
+			address = (address + PMD_SIZE) & PMD_MASK;
+			pmd++;
+		} while (address < end);
+	} else {
+		/* On the 68030 clear the early termination descriptors */
+		int i = (address >> (PMD_SHIFT-4)) & 15;
+		do {
+			(&pmd_val(*pmd))[i++] = 0;
+			address += PMD_SIZE / 16;
+		} while (address < end);
+	}
+}
+
+/*
+ * Unmap a kernel_map()ed region again
+ */
+void kernel_unmap( unsigned long addr )
+{
+	unsigned long end;
+	pgd_t *dir;
+	KMAP *p;
+
+	down( &kmap_sem );
+	
+	/* find region for 'addr' in list; must search for mapaddr! */
+	for( p = kmap_regions; p; p = p->next )
+		if (!p->free && p->mapaddr == addr)
+			break;
+	if (!p) {
+		printk( KERN_ERR "kernel_unmap: trying to free invalid region\n" );
+		return;
+	}
+	addr = p->addr;
+	end = addr + p->size;
+	kmap_put_region( p );
+
+	dir = pgd_offset_k( addr );
+	while( addr < end ) {
+		kernel_unmap_pmd_range( dir, addr, end - addr );
+		addr = (addr + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	
+	up( &kmap_sem );
+	/* flushing for a range would do, but there's no such function for kernel
+	 * address space... */
+	flush_tlb_all();
+}
+
+
+/*
+ * kernel_set_cachemode() helpers
+ */
+static inline void set_cmode_pte( pmd_t *pmd, unsigned long address,
+				  unsigned long size, unsigned cmode )
+{	pte_t *pte;
+	unsigned long end;
+
+	if (pmd_none(*pmd))
+		return;
+
+	pte = pte_offset( pmd, address );
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end >= PMD_SIZE)
+		end = PMD_SIZE;
+
+	for( ; address < end; pte++ ) {
+		pte_val(*pte) = (pte_val(*pte) & ~_PAGE_NOCACHE) | cmode;
+		address += PAGE_SIZE;
+	}
+}
+
+
+static inline void set_cmode_pmd( pgd_t *dir, unsigned long address,
+				  unsigned long size, unsigned cmode )
+{
+	pmd_t *pmd;
+	unsigned long end;
+
+	if (pgd_none(*dir))
+		return;
+
+	pmd = pmd_offset( dir, address );
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+
+	if ((pmd_val(*pmd) & _DESCTYPE_MASK) == _PAGE_PRESENT) {
+		/* 68030 early termination descriptor */
+		pmd_val(*pmd) = (pmd_val(*pmd) & ~_PAGE_NOCACHE) | cmode;
+		return;
+	}
+	else {
+		/* "normal" tables */
+		for( ; address < end; pmd++ ) {
+			set_cmode_pte( pmd, address, end - address, cmode );
+			address = (address + PMD_SIZE) & PMD_MASK;
+		}
+	}
+}
+
+
+/*
+ * Set new cache mode for some kernel address space.
+ * The caller must push data for that range itself, if such data may already
+ * be in the cache.
+ */
+void kernel_set_cachemode( unsigned long address, unsigned long size,
+						   unsigned cmode )
+{
+	pgd_t *dir = pgd_offset_k( address );
+	unsigned long end = address + size;
+	
+	if (CPU_IS_040_OR_060) {
+		switch( cmode ) {
+		  case KERNELMAP_FULL_CACHING:
+			cmode = _PAGE_CACHE040;
+			break;
+		  case KERNELMAP_NOCACHE_SER:
+		  default:
+			cmode = _PAGE_NOCACHE_S;
+			break;
+		  case KERNELMAP_NOCACHE_NONSER:
+			cmode = _PAGE_NOCACHE;
+			break;
+		  case KERNELMAP_NO_COPYBACK:
+			cmode = _PAGE_CACHE040W;
+			break;
+		}
+	} else
+		cmode = ((cmode == KERNELMAP_FULL_CACHING ||
+				  cmode == KERNELMAP_NO_COPYBACK)    ?
+			 0 : _PAGE_NOCACHE030);
+
+	for( ; address < end; dir++ ) {
+		set_cmode_pmd( dir, address, end - address, cmode );
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+	}
+	/* flushing for a range would do, but there's no such function for kernel
+	 * address space... */
+	flush_tlb_all();
+}
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.57.orig/arch/m68k/mm/memory.c linux-2.1.57/arch/m68k/mm/memory.c
--- linux-2.1.57.orig/arch/m68k/mm/memory.c	Fri Sep  5 18:23:01 1997
+++ linux-2.1.57/arch/m68k/mm/memory.c	Sat Oct  4 00:19:04 1997
@@ -18,8 +18,6 @@
 #include <asm/traps.h>
 #include <asm/amigahw.h>
 
-extern pte_t *kernel_page_table (unsigned long *memavailp);
-
 /* Strings for `extern inline' functions in <asm/pgtable.h>.  If put
    directly into these functions, they are output for every file that
    includes pgtable.h */
@@ -621,245 +619,3 @@
 	return 0;
 }
 
-/* Map some physical address range into the kernel address space. The
- * code is copied and adapted from map_chunk().
- */
-/* Rewritten by Andreas Schwab to remove all races. */
-
-static inline pte_t *
-pte_alloc_kernel_map(pmd_t *pmd, unsigned long address,
-		     unsigned long *memavailp)
-{
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t *page = kernel_page_table(memavailp);
-		if (pmd_none(*pmd)) {
-			if (page) {
-				pmd_set(pmd, page);
-				return page + address;
-			}
-			pmd_set(pmd, BAD_PAGETABLE);
-			return NULL;
-		}
-		if (memavailp)
-			panic("kernel_map: slept during init?!?");
-		cache_page((unsigned long) page);
-		free_page((unsigned long) page);
-	}
-	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc_kernel_map: %08lx\n",
-		       pmd_val(*pmd));
-		pmd_set(pmd, BAD_PAGETABLE);
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
-}
-
-static inline void
-kernel_map_pte(pte_t *pte, unsigned long address, unsigned long size,
-	       unsigned long phys_addr, pgprot_t prot)
-{
-	unsigned long end;
-
-	address &= ~PMD_MASK;
-	end = address + size;
-	if (end > PMD_SIZE)
-		end = PMD_SIZE;
-	do {
-		pte_val(*pte) = phys_addr + pgprot_val(prot);
-		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
-		pte++;
-	} while (address < end);
-}
-
-static inline int
-kernel_map_pmd (pmd_t *pmd, unsigned long address, unsigned long size,
-		unsigned long phys_addr, pgprot_t prot,
-		unsigned long *memavailp)
-{
-	unsigned long end;
-
-	address &= ~PGDIR_MASK;
-	end = address + size;
-	if (end > PGDIR_SIZE)
-		end = PGDIR_SIZE;
-	phys_addr -= address;
-
-	if (CPU_IS_040_OR_060) {
-		do {
-			pte_t *pte = pte_alloc_kernel_map(pmd, address, memavailp);
-			if (!pte)
-				return -ENOMEM;
-			kernel_map_pte(pte, address, end - address,
-				       address + phys_addr, prot);
-			address = (address + PMD_SIZE) & PMD_MASK;
-			pmd++;
-		} while (address < end);
-	} else {
-		/* On the 68030 we use early termination page descriptors.
-		   Each one points to 64 pages (256K). */
-		int i = (address >> (PMD_SHIFT-4)) & 15;
-		do {
-			(&pmd_val(*pmd))[i++] = (address + phys_addr) | pgprot_val(prot);
-			address += PMD_SIZE / 16;
-		} while (address < end);
-	}
-	return 0;
-}
-
-unsigned long kernel_map(unsigned long phys_addr, unsigned long size,
-			 int cacheflag, unsigned long *memavailp)
-{
-#define STEP_SIZE	(256*1024)
-
-	static unsigned long vaddr = 0xe0000000; /* safe place */
-	unsigned long retaddr, from, end;
-	pgd_t *dir;
-	pgprot_t prot;
-
-	/* Round down 'phys_addr' to 256 KB and adjust size */
-	size += phys_addr & (STEP_SIZE-1);
-	retaddr = vaddr + (phys_addr & (STEP_SIZE-1));
-	phys_addr &= ~(STEP_SIZE-1);
-	/* Round up the size to 256 KB. It doesn't hurt if too much is
-	   mapped... */
-	size = (size + STEP_SIZE - 1) & ~(STEP_SIZE-1);
-	from = vaddr;
-	/* Claim the address space immediately, we may be sleeping. */
-	vaddr += size;
-	end = vaddr;
-
-	if (CPU_IS_040_OR_060) {
-		pgprot_val(prot) = (_PAGE_PRESENT | _PAGE_GLOBAL040 |
-				    _PAGE_ACCESSED | _PAGE_DIRTY);
-		switch (cacheflag) {
-		case KERNELMAP_FULL_CACHING:
-			pgprot_val(prot) |= _PAGE_CACHE040;
-			break;
-		case KERNELMAP_NOCACHE_SER:
-		default:
-			pgprot_val(prot) |= _PAGE_NOCACHE_S;
-			break;
-		case KERNELMAP_NOCACHE_NONSER:
-			pgprot_val(prot) |= _PAGE_NOCACHE;
-			break;
-		case KERNELMAP_NO_COPYBACK:
-			pgprot_val(prot) |= _PAGE_CACHE040W;
-			break;
-		}
-	} else
-		pgprot_val(prot) = (_PAGE_PRESENT | _PAGE_ACCESSED |
-				    _PAGE_DIRTY |
-				    ((cacheflag == KERNELMAP_FULL_CACHING ||
-				      cacheflag == KERNELMAP_NO_COPYBACK)
-				     ? 0 : _PAGE_NOCACHE030));
-
-	phys_addr -= from;
-	dir = pgd_offset_k(from);
-	while (from < end) {
-		pmd_t *pmd = pmd_alloc_kernel(dir, from);
-
-		if (kernel_map_pmd(pmd, from, end - from, phys_addr + from,
-				   prot, memavailp)) {
-			printk(KERN_ERR "kernel_map: out of memory\n");
-			return 0UL;
-		}
-		from = (from + PGDIR_SIZE) & PGDIR_MASK;
-		dir++;
-	}
-
-	return retaddr;
-}
-
-
-static inline void set_cmode_pte( pmd_t *pmd, unsigned long address,
-				  unsigned long size, unsigned cmode )
-{	pte_t *pte;
-	unsigned long end;
-
-	if (pmd_none(*pmd))
-		return;
-
-	pte = pte_offset( pmd, address );
-	address &= ~PMD_MASK;
-	end = address + size;
-	if (end >= PMD_SIZE)
-		end = PMD_SIZE;
-
-	for( ; address < end; pte++ ) {
-		pte_val(*pte) = (pte_val(*pte) & ~_PAGE_NOCACHE) | cmode;
-		address += PAGE_SIZE;
-	}
-}
-
-
-static inline void set_cmode_pmd( pgd_t *dir, unsigned long address,
-				  unsigned long size, unsigned cmode )
-{
-	pmd_t *pmd;
-	unsigned long end;
-
-	if (pgd_none(*dir))
-		return;
-
-	pmd = pmd_offset( dir, address );
-	address &= ~PGDIR_MASK;
-	end = address + size;
-	if (end > PGDIR_SIZE)
-		end = PGDIR_SIZE;
-
-	if ((pmd_val(*pmd) & _DESCTYPE_MASK) == _PAGE_PRESENT) {
-		/* 68030 early termination descriptor */
-		pmd_val(*pmd) = (pmd_val(*pmd) & ~_PAGE_NOCACHE) | cmode;
-		return;
-	}
-	else {
-		/* "normal" tables */
-		for( ; address < end; pmd++ ) {
-			set_cmode_pte( pmd, address, end - address, cmode );
-			address = (address + PMD_SIZE) & PMD_MASK;
-		}
-	}
-}
-
-
-/*
- * Set new cache mode for some kernel address space.
- * The caller must push data for that range itself, if such data may already
- * be in the cache.
- */
-
-void kernel_set_cachemode( unsigned long address, unsigned long size,
-						   unsigned cmode )
-{
-	pgd_t *dir = pgd_offset_k( address );
-	unsigned long end = address + size;
-	
-	if (CPU_IS_040_OR_060) {
-		switch( cmode ) {
-		  case KERNELMAP_FULL_CACHING:
-			cmode = _PAGE_CACHE040;
-			break;
-		  case KERNELMAP_NOCACHE_SER:
-		  default:
-			cmode = _PAGE_NOCACHE_S;
-			break;
-		  case KERNELMAP_NOCACHE_NONSER:
-			cmode = _PAGE_NOCACHE;
-			break;
-		  case KERNELMAP_NO_COPYBACK:
-			cmode = _PAGE_CACHE040W;
-			break;
-		}
-	} else
-		cmode = ((cmode == KERNELMAP_FULL_CACHING ||
-				  cmode == KERNELMAP_NO_COPYBACK)    ?
-			 0 : _PAGE_NOCACHE030);
-
-	for( ; address < end; dir++ ) {
-		set_cmode_pmd( dir, address, end - address, cmode );
-		address = (address + PGDIR_SIZE) & PGDIR_MASK;
-	}
-	flush_tlb_all();
-}
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.57.orig/include/asm-m68k/pgtable.h linux-2.1.57/include/asm-m68k/pgtable.h
--- linux-2.1.57.orig/include/asm-m68k/pgtable.h	Mon Sep 29 15:45:56 1997
+++ linux-2.1.57/include/asm-m68k/pgtable.h	Wed Oct  1 23:09:58 1997
@@ -730,11 +730,14 @@
 int mm_end_of_chunk (unsigned long addr, int len);
 
 /*
- * Map some physical address range into the kernel address space. The
- * code is copied and adapted from map_chunk().
+ * Map some physical address range into the kernel address space.
  */
 extern unsigned long kernel_map(unsigned long paddr, unsigned long size,
 				int nocacheflag, unsigned long *memavailp );
+/*
+ * Unmap a region alloced by kernel_map().
+ */
+extern void kernel_unmap( unsigned long addr );
 /*
  * Change the cache mode of some kernel address range.
  */
