This patch contains as following

 - remove lots of conditionals
 - optimize the memory if you use linear cramfs only (as before)

This patch is based on linux-2.6.12-rc5

Kyungmin Park

Index: fs/Kconfig
===================================================================
--- d402e9ad3075e839b5a287a79470eedd33d5471e/fs/Kconfig  (mode:100644)
+++ uncommitted/fs/Kconfig  (mode:100644)
@@ -1171,6 +1171,57 @@
 
 	  If unsure, say N.
 
+config CRAMFS_LINEAR
+	bool "Use linear addressing for cramfs"
+	depends on CRAMFS
+	help
+	  This option tells the CramFs driver to load data directly from
+	  a linear adressed memory range (usually non volatile memory
+	  like flash) instead of going through the block device layer.
+	  This saves some memory since no intermediate buffering is
+	  necessary.
+
+	  This is also a prerequisite for XIP of binaries stored on the
+	  filesystem.
+
+	  The location of the CramFs image in memory is board
+	  dependent. Therefore, if you say Y, you must know the proper
+	  physical address where to store the CramFs image and specify
+	  it using the physaddr=0x******** mount option (for example:
+	  "mount -t cramfs -o physaddr=0x100000 none /mnt").
+
+	  If unsure, say N.
+
+config CRAMFS_LINEAR_ONLY
+	bool "Use only linear cramfs"
+	depends on CRAMFS_LINEAR
+	help
+	  Say Y if you want to use only linear cramfs.
+
+config CRAMFS_LINEAR_XIP
+	bool "Support XIP on linear cramfs"
+	depends on CRAMFS_LINEAR
+	help
+	  You must say Y to this option if you want to be able to run
+	  applications directly from non-volatile memory.  XIP
+	  applications are marked by setting the sticky bit (ie, "chmod
+	  +t <app name>").  A CramFs file system then needs to be
+	  created using mkcramfs (with XIP CramFs support in
+	  it). Applications marked for XIP execution will not be
+	  compressed since they have to run directly from flash.
+
+config ROOT_CRAMFS_LINEAR
+	bool "Root file system on linear cramfs"
+	depends on CRAMFS_LINEAR
+	help
+	  Say Y if you have enabled linear cramfs, and you want to be
+	  able to use the linear CramFs image as a root file system.  To
+	  actually have the kernel mount this CramFs image as a root
+	  file system, you must also pass the command line parameter
+	  "root=/dev/null rootflags=physaddr=0x********" to the kernel
+	  (replace 0x******** with the physical address location of the
+	  linear CramFs image to boot with).
+
 config VXFS_FS
 	tristate "FreeVxFS file system support (VERITAS VxFS(TM)
compatible)"
 	help
Index: fs/cramfs/inode.c
===================================================================
--- d402e9ad3075e839b5a287a79470eedd33d5471e/fs/cramfs/inode.c
(mode:100644)
+++ uncommitted/fs/cramfs/inode.c  (mode:100644)
@@ -6,6 +6,52 @@
  * This file is released under the GPL.
  */
 
+/* Linear Addressing code
+ *
+ * Copyright (C) 2000 Shane Nay.
+ *
+ * Allows you to have a linearly addressed cramfs filesystem.
+ * Saves the need for buffer, and the munging of the buffer.
+ * Savings a bit over 32k with default PAGE_SIZE, BUFFER_SIZE
+ * etc.  Usefull on embedded platform with ROM :-).
+ *
+ * Downsides- Currently linear addressed cramfs partitions
+ * don't co-exist with block cramfs partitions.
+ *
+ */
+
+/*
+ * 28-Dec-2000: XIP mode for linear cramfs
+ * Copyright (C) 2000 Robert Leslie <rob@mars.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
USA
+ */
+
+/*
+ * Support both cramfs and linear cramfs simultaneously
+ * Copyright (C) 2005 Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Now you can use both cramfs and linear cramfs simultaneously
+ * To support a platform using both NOR and NAND
+ *
+ * Remove Shane Nay's downsides :)
+ * Remove linear cramfs for XIP dependency.
+ *   XIP files are running whether XIP mode is set or not
+ *   even though XIP files are mounted with cramfs
+ */
+
 /*
  * These are the VFS interfaces to the compressed rom filesystem.
  * The actual compression is based on zlib, see the other files.
@@ -25,6 +71,7 @@
 #include <asm/semaphore.h>
 
 #include <asm/uaccess.h>
+#include <asm/tlbflush.h>
 
 static struct super_operations cramfs_ops;
 static struct inode_operations cramfs_dir_inode_operations;
@@ -39,9 +86,66 @@
 #define CRAMINO(x)	((x)->offset?(x)->offset<<2:1)
 #define OFFSET(x)	((x)->i_ino)
 
+#define LINEAR(x)	((x)->linear_phys_addr)
+#define CRAMFS_INODE_IS_XIP(x)	((x)->i_mode & S_ISVTX)
+
+#ifdef CONFIG_CRAMFS_LINEAR_XIP
+static int cramfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long address, length;
+	struct inode *inode = file->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+
+	/* this is only used in the case of read-only maps for XIP */
+
+	if (vma->vm_flags & VM_WRITE)
+		return generic_file_mmap(file, vma);
+
+	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+		return -EINVAL;
+
+	address  = PAGE_ALIGN(sbi->linear_phys_addr + OFFSET(inode));
+	address += vma->vm_pgoff << PAGE_SHIFT;
+
+	length = vma->vm_end - vma->vm_start;
+
+	if (length > inode->i_size)
+		length = inode->i_size;
+
+	length = PAGE_ALIGN(length);
+
+	/*
+	 * Don't dump addresses that are not real memory to a core file.
+	 */
+	vma->vm_flags |= VM_IO;
+	flush_tlb_page(vma, address);
+	if (io_remap_page_range(vma, vma->vm_start, address, length,
+				vma->vm_page_prot))
+		return -EAGAIN;
+
+#ifdef DEBUG_CRAMFS_XIP
+	printk("cramfs_mmap: mapped %s at 0x%08lx, length %lu to vma
0x%08lx"
+		", page_prot 0x%08lx\n",
+		file->f_dentry->d_name.name, address, length,
+		vma->vm_start, pgprot_val(vma->vm_page_prot));
+#endif
+
+	return 0;
+}
+
+static struct file_operations cramfs_linear_xip_fops = {
+	.read		= generic_file_read,
+	.mmap		= cramfs_mmap,
+};
+#endif
+
 static struct inode *get_cramfs_inode(struct super_block *sb, struct
cramfs_inode * cramfs_inode)
 {
 	struct inode * inode = new_inode(sb);
+#ifdef CONFIG_CRAMFS_LINEAR_XIP
+	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+#endif
 	static struct timespec zerotime;
 
 	if (inode) {
@@ -61,6 +165,10 @@
 		insert_inode_hash(inode);
 		if (S_ISREG(inode->i_mode)) {
 			inode->i_fop = &generic_ro_fops;
+#ifdef CONFIG_CRAMFS_LINEAR_XIP
+			if (LINEAR(sbi) && CRAMFS_INODE_IS_XIP(inode))
+				inode->i_fop = &cramfs_linear_xip_fops;
+#endif
 			inode->i_data.a_ops = &cramfs_aops;
 		} else if (S_ISDIR(inode->i_mode)) {
 			inode->i_op = &cramfs_dir_inode_operations;
@@ -78,6 +186,7 @@
 	return inode;
 }
 
+#ifndef CONFIG_CRAMFS_LINEAR_ONLY
 /*
  * We have our own block cache: don't fill up the buffer cache
  * with the rom-image, because the way the filesystem is set
@@ -111,7 +220,7 @@
  * Returns a pointer to a buffer containing at least LEN bytes of
  * filesystem starting at byte offset OFFSET into the filesystem.
  */
-static void *cramfs_read(struct super_block *sb, unsigned int offset,
unsigned int len)
+static void *cramfs_block_read(struct super_block *sb, unsigned int
offset, unsigned int len)
 {
 	struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
 	struct page *pages[BLKS_PER_BUF];
@@ -187,6 +296,35 @@
 	}
 	return read_buffers[buffer] + offset;
 }
+#endif	/* CONFIG_CRAMFS_LINEAR_ONLY */
+
+#ifdef CONFIG_CRAMFS_LINEAR
+static void *cramfs_linear_read(struct super_block *sb, unsigned int
offset, unsigned int len)
+{
+	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+
+	if (!len)
+		return NULL;
+
+	return (void *)(sbi->linear_virt_addr + offset);
+}
+#endif
+
+#ifdef CONFIG_CRAMFS_LINEAR_ONLY
+#define cramfs_read(sb, offset, len)	cramfs_linear_read(sb, offset, len)
+#elif CONFIG_CRAMFS_LINEAR
+static void *cramfs_read(struct super_block *sb, unsigned int offset,
unsigned int len)
+{
+	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+
+	if (LINEAR(sbi))
+		return cramfs_linear_read(sb, offset, len);
+
+	return cramfs_block_read(sb, offset, len);
+}
+#else
+#define cramfs_read(sb, offset, len)	cramfs_block_read(sb, offset, len)
+#endif
 
 static void cramfs_put_super(struct super_block *sb)
 {
@@ -202,11 +340,16 @@
 
 static int cramfs_fill_super(struct super_block *sb, void *data, int
silent)
 {
+#ifndef CONFIG_CRAMFS_LINEAR_ONLY
 	int i;
+#endif
 	struct cramfs_super super;
 	unsigned long root_offset;
 	struct cramfs_sb_info *sbi;
 	struct inode *root;
+#ifdef CONFIG_CRAMFS_LINEAR
+	char *p;
+#endif
 
 	sb->s_flags |= MS_RDONLY;
 
@@ -216,10 +359,53 @@
 	sb->s_fs_info = sbi;
 	memset(sbi, 0, sizeof(struct cramfs_sb_info));
 
+#ifdef CONFIG_CRAMFS_LINEAR
+	/*
+	 * The physical location of the cramfs image is specified as
+	 * a mount parameter.  This parameter is mandatory for obvious
+	 * reasons.  Some validation is made on the phys address but this
+	 * is not exhaustive and we count on the fact that someone using
+	 * this feature is supposed to know what he/she's doing.
+	 */
+	sbi->linear_phys_addr = 0;
+	if (!data || !(p = strstr((char *)data, "physaddr=")))
+		goto read;
+
+	sbi->linear_phys_addr = simple_strtoul(p + 9, NULL, 0);
+	if (sbi->linear_phys_addr & (PAGE_SIZE-1)) {
+		printk(KERN_ERR "cramfs: physical address 0x%lx for"
+			" linear cramfs isn't aligned to a page boundary\n",
+			sbi->linear_phys_addr);
+		goto out;
+	}
+	if (sbi->linear_phys_addr == 0) {
+		printk(KERN_ERR "cramfs: physical address for"
+			" linear cramfs image can't be 0\n");
+		goto out;
+	}
+	printk(KERN_INFO "cramfs: checking physical address 0x%lx for"
+		" linear cramfs image\n",
+		sbi->linear_phys_addr);
+
+	/* Map only one page for now.  Will remap it when fs size is known.
*/
+	sbi->linear_virt_addr =
+		ioremap(sbi->linear_phys_addr, PAGE_SIZE);
+	if (!sbi->linear_virt_addr) {
+		printk(KERN_ERR "cramfs: ioremap of the linear cramfs image
failed\n");
+		goto out;
+	}
+read:
+#endif
+
 	/* Invalidate the read buffers on mount: think disk change.. */
 	down(&read_mutex);
+#ifndef CONFIG_CRAMFS_LINEAR_ONLY
+#ifdef CONFIG_CRAMFS_LINEAR
+	if (!LINEAR(sbi))
+#endif
 	for (i = 0; i < READ_BUFFERS; i++)
 		buffer_blocknr[i] = -1;
+#endif
 
 	/* Read the first block and get the superblock from it */
 	memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
@@ -281,8 +467,32 @@
 		iput(root);
 		goto out;
 	}
+#ifdef CONFIG_CRAMFS_LINEAR
+	if (LINEAR(sbi)) {
+		/* Remap the whole filesystem now */
+		iounmap(sbi->linear_virt_addr);
+		printk(KERN_INFO "cramfs: linear cramfs image appears"
+			" to be %lu KB in size\n", sbi->size >> 10);
+#ifdef CONFIG_ARM
+		sbi->linear_virt_addr =
+			ioremap_cached(sbi->linear_phys_addr, sbi->size);
+#else
+		sbi->linear_virt_addr =
+			ioremap(sbi->linear_phys_addr, sbi->size);
+#endif
+		if (!sbi->linear_virt_addr) {
+			printk(KERN_ERR "cramfs: ioremap of the linear
cramfs"
+				" image failed\n");
+			goto out;
+		}
+	}
+#endif
 	return 0;
 out:
+#ifdef CONFIG_CRAMFS_LINEAR
+	if (LINEAR(sbi) && sbi->linear_virt_addr)
+		iounmap(sbi->linear_virt_addr);
+#endif
 	kfree(sbi);
 	sb->s_fs_info = NULL;
 	return -EINVAL;
@@ -430,13 +640,26 @@
 static int cramfs_readpage(struct file *file, struct page * page)
 {
 	struct inode *inode = page->mapping->host;
+	struct super_block *sb = inode->i_sb;
 	u32 maxblock, bytes_filled;
 	void *pgdata;
 
 	maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >>
PAGE_CACHE_SHIFT;
 	bytes_filled = 0;
-	if (page->index < maxblock) {
-		struct super_block *sb = inode->i_sb;
+
+	/* Handle uncompressed files */
+	if (CRAMFS_INODE_IS_XIP(inode) && page->index < maxblock) {
+		u32 blkptr_offset = PAGE_ALIGN(OFFSET(inode)) +
+				page->index * PAGE_CACHE_SIZE;
+
+		down(&read_mutex);
+		memcpy(page_address(page),
+			cramfs_read(sb, blkptr_offset, PAGE_CACHE_SIZE),
+			PAGE_CACHE_SIZE);
+		up(&read_mutex);
+		bytes_filled = PAGE_CACHE_SIZE;
+		pgdata = kmap(page);
+	} else if (page->index < maxblock) {
 		u32 blkptr_offset = OFFSET(inode) + page->index*4;
 		u32 start_offset, compr_len;
 
@@ -494,6 +717,7 @@
 	.statfs		= cramfs_statfs,
 };
 
+#ifndef CONFIG_CRAMFS_LINEAR_ONLY
 static struct super_block *cramfs_get_sb(struct file_system_type *fs_type,
 	int flags, const char *dev_name, void *data)
 {
@@ -507,17 +731,55 @@
 	.kill_sb	= kill_block_super,
 	.fs_flags	= FS_REQUIRES_DEV,
 };
+#endif
+
+#ifdef CONFIG_CRAMFS_LINEAR
+static struct super_block * cramfs_linear_get_sb(struct file_system_type
*fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_nodev(fs_type, flags, data, cramfs_fill_super);
+}
+
+static void cramfs_linear_kill_sb(struct super_block *sb)
+{
+	struct cramfs_sb_info *sbi = CRAMFS_SB(sb);
+
+	if (sbi->linear_virt_addr)
+		iounmap(sbi->linear_virt_addr);
+
+	kill_litter_super(sb);
+}
+
+static struct file_system_type cramfs_linear_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "cramfs_linear",
+	.get_sb		= cramfs_linear_get_sb,
+	.kill_sb	= cramfs_linear_kill_sb,
+};
+#endif
 
 static int __init init_cramfs_fs(void)
 {
 	cramfs_uncompress_init();
+#ifdef CONFIG_CRAMFS_LINEAR_ONLY
+	return register_filesystem(&cramfs_linear_fs_type);
+#else
+#ifdef CONFIG_CRAMFS_LINEAR
+	register_filesystem(&cramfs_linear_fs_type);
+#endif
 	return register_filesystem(&cramfs_fs_type);
+#endif
 }
 
 static void __exit exit_cramfs_fs(void)
 {
 	cramfs_uncompress_exit();
+#ifdef CONFIG_CRAMFS_LINEAR
+	unregister_filesystem(&cramfs_linear_fs_type);
+#endif
+#ifndef CONFIG_CRAMFS_LINEAR_ONLY
 	unregister_filesystem(&cramfs_fs_type);
+#endif
 }
 
 module_init(init_cramfs_fs)
Index: include/linux/cramfs_fs_sb.h
===================================================================
--- d402e9ad3075e839b5a287a79470eedd33d5471e/include/linux/cramfs_fs_sb.h
(mode:100644)
+++ uncommitted/include/linux/cramfs_fs_sb.h  (mode:100644)
@@ -10,6 +10,10 @@
 			unsigned long blocks;
 			unsigned long files;
 			unsigned long flags;
+#ifdef CONFIG_CRAMFS_LINEAR
+			unsigned long linear_phys_addr;
+			void __iomem *linear_virt_addr;
+#endif
 };
 
 static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb)
Index: init/do_mounts.c
===================================================================
--- d402e9ad3075e839b5a287a79470eedd33d5471e/init/do_mounts.c  (mode:100644)
+++ uncommitted/init/do_mounts.c  (mode:100644)
@@ -339,6 +339,17 @@
 }
 #endif
 
+#ifdef CONFIG_ROOT_CRAMFS_LINEAR
+static int __init mount_cramfs_linear_root(void)
+{
+	create_dev("/dev/root", ROOT_DEV, NULL);
+	if (do_mount_root("/dev/root", "cramfs_linear",
+	    root_mountflags, root_mount_data) == 0)
+		return 1;
+	return 0;
+}
+#endif
+
 #if defined(CONFIG_BLK_DEV_RAM) || defined(CONFIG_BLK_DEV_FD)
 void __init change_floppy(char *fmt, ...)
 {
@@ -380,6 +391,15 @@
 		ROOT_DEV = Root_FD0;
 	}
 #endif
+#ifdef CONFIG_ROOT_CRAMFS_LINEAR
+	if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
+		if (mount_cramfs_linear_root())
+			return;
+
+		printk(KERN_ERR "VFS: Unable to mount linear cramfs
root.\n");
+		ROOT_DEV = Root_FD0;
+	}
+#endif
 #ifdef CONFIG_BLK_DEV_FD
 	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
 		/* rd_doload is 2 for a dual initrd/ramload setup */
Index: mm/memory.c
===================================================================
--- d402e9ad3075e839b5a287a79470eedd33d5471e/mm/memory.c  (mode:100644)
+++ uncommitted/mm/memory.c  (mode:100644)
@@ -1251,6 +1251,45 @@
 	pte_t entry;
 
 	if (unlikely(!pfn_valid(pfn))) {
+#ifdef CONFIG_CRAMFS_LINEAR_XIP
+		if (pte_present(pte) && pte_read(pte)) {
+			/*
+			 * Handle COW of XIP memory.
+			 * Note that the source memory actually isn't a ram
+			 * page so no struct page is associated to the
source
+			 * pte.
+			 */
+			char *dst;
+			int ret;
+
+			spin_unlock(&mm->page_table_lock);
+			new_page = alloc_page(GFP_HIGHUSER);
+			if (!new_page)
+				return VM_FAULT_OOM;
+
+			/* copy XIP data to memory */
+			dst = kmap_atomic(new_page, KM_USER0);
+			ret = copy_from_user(dst, (void*)address,
PAGE_SIZE);
+			kunmap_atomic(dst, KM_USER0);
+
+			/* make sure pte didn't change while we dropped the
+			   lock */
+			spin_lock(&mm->page_table_lock);
+			if (!ret && pte_same(*page_table, pte)) {
+				inc_mm_counter(mm, rss);
+				break_cow(vma, new_page, address,
page_table);
+				lru_cache_add(new_page);
+				page_add_file_rmap(new_page);
+				spin_unlock(&mm->page_table_lock);
+				return VM_FAULT_MINOR;  /* Minor fault */
+			}
+
+			/* pte changed: back off */
+			spin_unlock(&mm->page_table_lock);
+			page_cache_release(new_page);
+			return ret ? VM_FAULT_OOM : VM_FAULT_MINOR;
+		}
+#endif
 		/*
 		 * This should really halt the system so it can be debugged
or
 		 * at least the kernel stops what it's doing before it
corrupts

