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 "). 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 + * + * 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 + * + * 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 #include +#include 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