From: David Mosberger <davidm@napali.hpl.hp.com>




 drivers/char/mem.c           |  183 ++++++++++++++++++++++++++-----------------
 include/asm-i386/pgtable.h   |    7 +
 include/asm-m68k/pgtable.h   |   17 +++
 include/asm-ppc/pgtable.h    |    5 +
 include/asm-ppc64/pgtable.h  |    5 +
 include/asm-x86_64/pgtable.h |    5 +
 6 files changed, 150 insertions(+), 72 deletions(-)

diff -puN drivers/char/mem.c~mem_driver_cleanup drivers/char/mem.c
--- 25/drivers/char/mem.c~mem_driver_cleanup	2003-06-05 00:05:25.000000000 -0700
+++ 25-akpm/drivers/char/mem.c	2003-06-05 00:06:59.000000000 -0700
@@ -29,15 +29,59 @@
 #include <asm/io.h>
 #include <asm/pgalloc.h>
 
+#ifdef CONFIG_IA64
+#include <linux/efi.h>
+#endif
+
 #ifdef CONFIG_FB
 extern void fbmem_init(void);
 #endif
 #if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
 extern void tapechar_init(void);
 #endif
-     
+
+/*
+ * Architectures vary in how they handle caching for addresses
+ * outside of main memory.
+ *
+ */
+static inline int uncached_access(struct file *file, unsigned long addr)
+{
+#if defined(__i386__)
+	/*
+	 * On the PPro and successors, the MTRRs are used to set
+	 * memory types for physical addresses outside main memory,
+	 * so blindly setting PCD or PWT on those pages is wrong.
+	 * For Pentiums and earlier, the surround logic should disable
+	 * caching for the high addresses through the KEN pin, but
+	 * we maintain the tradition of paranoia in this code.
+	 */
+	if (file->f_flags & O_SYNC)
+		return 1;
+ 	return !( test_bit(X86_FEATURE_MTRR, boot_cpu_data.x86_capability) ||
+		  test_bit(X86_FEATURE_K6_MTRR, boot_cpu_data.x86_capability) ||
+		  test_bit(X86_FEATURE_CYRIX_ARR, boot_cpu_data.x86_capability) ||
+		  test_bit(X86_FEATURE_CENTAUR_MCR, boot_cpu_data.x86_capability) )
+	  && addr >= __pa(high_memory);
+#elif defined(CONFIG_IA64)
+	/*
+	 * On ia64, we ignore O_SYNC because we cannot tolerate memory
+	 * attribute aliases.
+	 */
+	return !(efi_mem_attributes(addr) & EFI_MEMORY_WB);
+#else
+	/*
+	 * Accessing memory above the top the kernel knows about or through a
+	 * file pointer that was marked O_SYNC will be done non-cached.
+	 */
+	if (file->f_flags & O_SYNC)
+		return 1;
+	return addr >= __pa(high_memory);
+#endif
+}
+
 static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
-			    const char * buf, size_t count, loff_t *ppos)
+				const char * buf, size_t count, loff_t *ppos)
 {
 	ssize_t written;
 
@@ -62,6 +106,40 @@ static ssize_t do_write_mem(struct file 
 }
 
 
+static ssize_t do_write_io(struct file *file, void *p, unsigned long realp,
+				const char *buf, size_t count, loff_t *ppos)
+{
+	ssize_t written;
+	char byte;
+	size_t i;
+
+	/* this is slow but nobody cares... */
+
+	written = 0;
+#if defined(__sparc__) || (defined(__mc68000__) && defined(CONFIG_MMU))
+	/* we don't have page 0 mapped on sparc and m68k.. */
+	if (realp < PAGE_SIZE) {
+		unsigned long sz = PAGE_SIZE-realp;
+
+		if (sz > count)
+			sz = count;
+		/* Hmm. Do something? */
+		buf += sz;
+		p += sz;
+		count -= sz;
+		written += sz;
+	}
+#endif
+	for (i = 0; i < count; ++i) {
+		if (get_user(byte, buf + i))
+			return -EFAULT;
+		writeb(byte, p + i);
+	}
+	written += count;
+	*ppos += written;;
+	return written;
+}
+
 /*
  * This funcion reads the *physical* memory. The f_pos points directly to the 
  * memory location. 
@@ -72,7 +150,8 @@ static ssize_t read_mem(struct file * fi
 	unsigned long p = *ppos;
 	unsigned long end_mem;
 	ssize_t read;
-	
+	void *addr;
+
 	end_mem = __pa(high_memory);
 	if (p >= end_mem)
 		return 0;
@@ -95,8 +174,22 @@ static ssize_t read_mem(struct file * fi
 		}
 	}
 #endif
-	if (copy_to_user(buf, __va(p), count))
-		return -EFAULT;
+	if (uncached_access(file, (unsigned long) p)) {
+		char byte;
+		size_t i;
+
+		if (!(addr = ioremap(p, count)))
+			return -EFAULT;
+		/* this is slow but nobody cares... */
+		for (i = 0; i < count; ++i) {
+			byte = readb(addr + i);
+			if (put_user(byte, buf + i))
+				return -EFAULT;
+		}
+		iounmap(addr);
+	} else
+		if (copy_to_user(buf, __va(p), count))
+			return -EFAULT;
 	read += count;
 	*ppos += read;
 	return read;
@@ -107,86 +200,32 @@ static ssize_t write_mem(struct file * f
 {
 	unsigned long p = *ppos;
 	unsigned long end_mem;
+	ssize_t ret;
+	void *addr;
 
 	end_mem = __pa(high_memory);
 	if (p >= end_mem)
 		return 0;
 	if (count > end_mem - p)
 		count = end_mem - p;
-	return do_write_mem(file, __va(p), p, buf, count, ppos);
-}
-
-#ifndef pgprot_noncached
-
-/*
- * This should probably be per-architecture in <asm/pgtable.h>
- */
-static inline pgprot_t pgprot_noncached(pgprot_t _prot)
-{
-	unsigned long prot = pgprot_val(_prot);
-
-#if defined(__i386__) || defined(__x86_64__)
-	/* On PPro and successors, PCD alone doesn't always mean 
-	    uncached because of interactions with the MTRRs. PCD | PWT
-	    means definitely uncached. */ 
-	if (boot_cpu_data.x86 > 3)
-		prot |= _PAGE_PCD | _PAGE_PWT;
-#elif defined(__powerpc__)
-	prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
-#elif defined(__mc68000__) && defined(CONFIG_MMU)
-#ifdef SUN3_PAGE_NOCACHE
-	if (MMU_IS_SUN3)
-		prot |= SUN3_PAGE_NOCACHE;
-	else
-#endif
-	if (MMU_IS_851 || MMU_IS_030)
-		prot |= _PAGE_NOCACHE030;
-	/* Use no-cache mode, serialized */
-	else if (MMU_IS_040 || MMU_IS_060)
-		prot = (prot & _CACHEMASK040) | _PAGE_NOCACHE_S;
-#endif
-
-	return __pgprot(prot);
-}
-
-#endif /* !pgprot_noncached */
-
-/*
- * Architectures vary in how they handle caching for addresses 
- * outside of main memory.
- */
-static inline int noncached_address(unsigned long addr)
-{
-#if defined(__i386__)
-	/* 
-	 * On the PPro and successors, the MTRRs are used to set
-	 * memory types for physical addresses outside main memory, 
-	 * so blindly setting PCD or PWT on those pages is wrong.
-	 * For Pentiums and earlier, the surround logic should disable 
-	 * caching for the high addresses through the KEN pin, but
-	 * we maintain the tradition of paranoia in this code.
-	 */
- 	return !( test_bit(X86_FEATURE_MTRR, boot_cpu_data.x86_capability) ||
-		  test_bit(X86_FEATURE_K6_MTRR, boot_cpu_data.x86_capability) ||
-		  test_bit(X86_FEATURE_CYRIX_ARR, boot_cpu_data.x86_capability) ||
-		  test_bit(X86_FEATURE_CENTAUR_MCR, boot_cpu_data.x86_capability) )
-	  && addr >= __pa(high_memory);
-#else
-	return addr >= __pa(high_memory);
-#endif
+	if (uncached_access(file, (unsigned long) p)) {
+		if (!(addr = ioremap(p, count)))
+			return -EFAULT;
+		ret = do_write_io(file, addr, p, buf, count, ppos);
+		iounmap(addr);
+	} else
+		ret = do_write_mem(file, __va(p), p, buf, count, ppos);
+	return ret;
 }
 
 static int mmap_mem(struct file * file, struct vm_area_struct * vma)
 {
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 
-	/*
-	 * Accessing memory above the top the kernel knows about or
-	 * through a file pointer that was marked O_SYNC will be
-	 * done non-cached.
-	 */
-	if (noncached_address(offset) || (file->f_flags & O_SYNC))
+#ifdef pgprot_noncached
+	if (uncached_access(file, offset))
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
 
 	/* Don't try to swap out physical pages.. */
 	vma->vm_flags |= VM_RESERVED;
diff -puN include/asm-i386/pgtable.h~mem_driver_cleanup include/asm-i386/pgtable.h
--- 25/include/asm-i386/pgtable.h~mem_driver_cleanup	2003-06-05 00:05:25.000000000 -0700
+++ 25-akpm/include/asm-i386/pgtable.h	2003-06-05 00:05:25.000000000 -0700
@@ -221,6 +221,13 @@ static inline void ptep_set_wrprotect(pt
 static inline void ptep_mkdirty(pte_t *ptep)			{ set_bit(_PAGE_BIT_DIRTY, &ptep->pte_low); }
 
 /*
+ * Macro to mark a page protection value as "uncacheable".  On processors which do not support
+ * it, this is a no-op.
+ */
+#define pgprot_noncached(prot)	((boot_cpu_data.x86 > 3)					  \
+				 ? (__pgprot(pgprot_val(prot) | _PAGE_PCD | _PAGE_PWT)) : (prot))
+
+/*
  * Conversion functions: convert a page and protection to a page entry,
  * and a page entry and page directory to the page they refer to.
  */
diff -puN include/asm-m68k/pgtable.h~mem_driver_cleanup include/asm-m68k/pgtable.h
--- 25/include/asm-m68k/pgtable.h~mem_driver_cleanup	2003-06-05 00:05:25.000000000 -0700
+++ 25-akpm/include/asm-m68k/pgtable.h	2003-06-05 00:05:25.000000000 -0700
@@ -153,6 +153,23 @@ extern inline void update_mmu_cache(stru
 #ifndef __ASSEMBLY__
 #include <asm-generic/pgtable.h>
 
+/*
+ * Macro to mark a page protection value as "uncacheable".
+ */
+#ifdef SUN3_PAGE_NOCACHE
+# define __SUN3_PAGE_NOCACHE	SUN3_PAGE_NOCACHE
+#else
+# define __SUN3_PAGE_NOCACHE	0
+#endif
+#define pgprot_noncached(prot)							\
+	(MMU_IS_SUN3								\
+	 ? (__pgprot(pgprot_val(prot) | __SUN3_PAGE_NOCACHE))			\
+	 : ((MMU_IS_851 || MMU_IS_030)						\
+	    ? (__pgprot(pgprot_val(prot) | _PAGE_NOCACHE030))			\
+	    : (MMU_IS_040 || MMU_IS_060)					\
+	    ? (__pgprot((pgprot_val(prot) & _CACHEMASK040) | _PAGE_NOCACHE_S))	\
+	    : (prot)))
+
 typedef pte_t *pte_addr_t;
 
 #endif /* !__ASSEMBLY__ */
diff -puN include/asm-ppc64/pgtable.h~mem_driver_cleanup include/asm-ppc64/pgtable.h
--- 25/include/asm-ppc64/pgtable.h~mem_driver_cleanup	2003-06-05 00:05:25.000000000 -0700
+++ 25-akpm/include/asm-ppc64/pgtable.h	2003-06-05 00:05:25.000000000 -0700
@@ -303,6 +303,11 @@ static inline void ptep_mkdirty(pte_t *p
 	pte_update(ptep, 0, _PAGE_DIRTY);
 }
 
+/*
+ * Macro to mark a page protection value as "uncacheable".
+ */
+#define pgprot_noncached(prot)	(__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED))
+
 #define pte_same(A,B)	(((pte_val(A) ^ pte_val(B)) & ~_PAGE_HPTEFLAGS) == 0)
 
 /*
diff -puN include/asm-ppc/pgtable.h~mem_driver_cleanup include/asm-ppc/pgtable.h
--- 25/include/asm-ppc/pgtable.h~mem_driver_cleanup	2003-06-05 00:05:25.000000000 -0700
+++ 25-akpm/include/asm-ppc/pgtable.h	2003-06-05 00:05:25.000000000 -0700
@@ -478,6 +478,11 @@ static inline void ptep_mkdirty(pte_t *p
 	pte_update(ptep, 0, _PAGE_DIRTY);
 }
 
+/*
+ * Macro to mark a page protection value as "uncacheable".
+ */
+#define pgprot_noncached(prot)	(__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED))
+
 #define pte_same(A,B)	(((pte_val(A) ^ pte_val(B)) & ~_PAGE_HASHPTE) == 0)
 
 #define pmd_page_kernel(pmd)	\
diff -puN include/asm-x86_64/pgtable.h~mem_driver_cleanup include/asm-x86_64/pgtable.h
--- 25/include/asm-x86_64/pgtable.h~mem_driver_cleanup	2003-06-05 00:05:25.000000000 -0700
+++ 25-akpm/include/asm-x86_64/pgtable.h	2003-06-05 00:05:25.000000000 -0700
@@ -265,6 +265,11 @@ static inline  int ptep_test_and_clear_y
 static inline void ptep_set_wrprotect(pte_t *ptep)		{ clear_bit(_PAGE_BIT_RW, ptep); }
 static inline void ptep_mkdirty(pte_t *ptep)			{ set_bit(_PAGE_BIT_DIRTY, ptep); }
 
+/*
+ * Macro to mark a page protection value as "uncacheable".
+ */
+#define pgprot_noncached(prot)	(__pgprot(pgprot_val(prot) | _PAGE_PCD | _PAGE_PWT))
+
 #define __LARGE_PTE (_PAGE_PSE|_PAGE_PRESENT) 
 static inline int pmd_large(pmd_t pte) { 
 	return (pmd_val(pte) & __LARGE_PTE) == __LARGE_PTE; 

_