2001-09-21 Bradley D. LaRonde This patch allows cramfs optional direct access to a cramfs image in memory (ram, rom, flash). It eliminates the unnecessary step of passing data through an intermediate buffer, as compared to accessing the same image through a memory block device like mtdblock. Secondly, it allows optional cramfs linear root support. It eliminates the requirement of having to provide a block device to use a linear cramfs image as the root filesystem. Thridly, it provides optional XIP. It patches mkcramfs to store files marked +t uncompressed and page-aligned. Linux can then mmap those files and execute them in-place without copying them entirely to ram first. diff -urNbB linux-2.4.9/fs/Config.in linux-2.4.9-bdl/fs/Config.in --- linux-2.4.9/fs/Config.in Mon Jul 2 16:03:04 2001 +++ linux-2.4.9-bdl/fs/Config.in Wed Aug 29 12:48:44 2001 @@ -31,6 +31,12 @@ int 'JFFS debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_JFFS_FS_VERBOSE 0 fi tristate 'Compressed ROM file system support' CONFIG_CRAMFS +dep_mbool ' Use linear addressing for cramfs' CONFIG_CRAMFS_LINEAR $CONFIG_CRAMFS +if [ "$CONFIG_CRAMFS_LINEAR" != "n" ] ; then + hex ' Start address of linear cramfs image' CONFIG_CRAMFS_LINEAR_ADDR 0xbf200000 +fi +dep_bool ' Support XIP on linear cramfs' CONFIG_CRAMFS_LINEAR_XIP $CONFIG_CRAMFS_LINEAR +dep_bool ' Root file system on linear cramfs' CONFIG_ROOT_CRAMFS_LINEAR $CONFIG_CRAMFS_LINEAR bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS tristate 'Simple RAM-based file system support' CONFIG_RAMFS diff -urNbB linux-2.4.9/fs/cramfs/inode.c linux-2.4.9-bdl/fs/cramfs/inode.c --- linux-2.4.9/fs/cramfs/inode.c Thu Jul 19 18:14:53 2001 +++ linux-2.4.9-bdl/fs/cramfs/inode.c Wed Aug 29 12:48:44 2001 @@ -11,6 +11,39 @@ * The actual compression is based on zlib, see the other files. */ +/* 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 + */ + #include #include #include @@ -38,6 +71,71 @@ #define CRAMINO(x) ((x)->offset?(x)->offset<<2:1) #define OFFSET(x) ((x)->i_ino) +#ifdef CONFIG_CRAMFS_LINEAR_XIP +#define CRAMFS_LINEAR_OFFSET (CONFIG_CRAMFS_LINEAR_ADDR & 0xfff) + +static int cramfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long address, length; + struct inode *inode = file->f_dentry->d_inode; + + /* 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(CONFIG_CRAMFS_LINEAR_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); + +#if 0 + /* Doing the following makes it slower and more broken. bdl */ + /* + * Accessing memory above the top the kernel knows about or + * through a file pointer that was marked O_SYNC will be + * done non-cached. + */ + vma->vm_page_prot = + __pgprot((pgprot_val(vma->vm_page_prot) & ~_CACHE_MASK) + | _CACHE_UNCACHED); +#endif + + /* + * Don't dump addresses that are not real memory to a core file. + */ + vma->vm_flags |= VM_IO; + + if (remap_page_range(vma->vm_start, KSEG0ADDR(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, KSEG0ADDR(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, +}; + +#define CRAMFS_INODE_IS_XIP(x) ((x)->i_mode & S_ISVTX) +#endif + static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode) { struct inode * inode = new_inode(sb); @@ -56,7 +154,11 @@ without -noleaf option. */ insert_inode_hash(inode); if (S_ISREG(inode->i_mode)) { +#ifdef CONFIG_CRAMFS_LINEAR_XIP + inode->i_fop = CRAMFS_INODE_IS_XIP(inode) ? &cramfs_linear_xip_fops : &generic_ro_fops; +#else inode->i_fop = &generic_ro_fops; +#endif inode->i_data.a_ops = &cramfs_aops; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &cramfs_dir_inode_operations; @@ -72,6 +174,17 @@ return inode; } +#ifdef CONFIG_CRAMFS_LINEAR +/* + * Return a pointer to the block in the linearly addressed cramfs image. + */ +static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) +{ + if (!len) + return NULL; + return (void*)(CONFIG_CRAMFS_LINEAR_ADDR + offset); +} +#else /* Not linear addressing - aka regular block mode. */ /* * We have our own block cache: don't fill up the buffer cache * with the rom-image, because the way the filesystem is set @@ -182,11 +295,13 @@ } return read_buffers[buffer] + offset; } - +#endif /* !CONFIG_CRAMFS_LINEAR */ static struct super_block * cramfs_read_super(struct super_block *sb, void *data, int silent) { +#ifndef CONFIG_CRAMFS_LINEAR int i; +#endif struct cramfs_super super; unsigned long root_offset; struct super_block * retval = NULL; @@ -195,9 +310,11 @@ sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; +#ifndef CONFIG_CRAMFS_LINEAR /* Invalidate the read buffers on mount: think disk change.. */ 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)); @@ -379,9 +496,20 @@ bytes_filled = 0; if (page->index < maxblock) { struct super_block *sb = inode->i_sb; - u32 blkptr_offset = OFFSET(inode) + page->index*4; + u32 blkptr_offset; u32 start_offset, compr_len; +#ifdef CONFIG_CRAMFS_LINEAR_XIP + if(CRAMFS_INODE_IS_XIP(inode)) { + blkptr_offset = PAGE_ALIGN(OFFSET(inode)) - CRAMFS_LINEAR_OFFSET + + page->index * PAGE_CACHE_SIZE; + memcpy(page_address(page), + cramfs_read(sb, blkptr_offset, PAGE_CACHE_SIZE), + PAGE_CACHE_SIZE); + bytes_filled = PAGE_CACHE_SIZE; + } else { +#endif + blkptr_offset = OFFSET(inode) + page->index*4; start_offset = OFFSET(inode) + maxblock*4; if (page->index) start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4); @@ -394,6 +522,9 @@ PAGE_CACHE_SIZE, cramfs_read(sb, start_offset, compr_len), compr_len); +#ifdef CONFIG_CRAMFS_LINEAR_XIP + } +#endif } memset(page_address(page) + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled); flush_dcache_page(page); diff -urNbB linux-2.4.9/fs/super.c linux-2.4.9-bdl/fs/super.c --- linux-2.4.9/fs/super.c Sun Aug 12 20:58:52 2001 +++ linux-2.4.9-bdl/fs/super.c Wed Aug 29 12:52:03 2001 @@ -18,6 +18,7 @@ * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996. * Added devfs support: Richard Gooch , 13-JAN-1998 * Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000 + * Added cramfs linear root support: Bradley D. LaRonde , 11-Feb-2001 */ #include @@ -1530,6 +1531,29 @@ void *handle; char path[64]; int path_start = -1; +#ifdef CONFIG_ROOT_NFS + void *data; +#endif + +#ifdef CONFIG_ROOT_CRAMFS_LINEAR + if (MAJOR(ROOT_DEV) != UNNAMED_MAJOR) + goto root_cramfs_linear_end; + fs_type = get_fs_type("cramfs"); + if (!fs_type) { + printk(KERN_ERR "VFS: Error getting cramfs filesystem type for cramfs linear root.\n"); + goto root_cramfs_linear_end; + } + sb = get_sb_nodev(fs_type, root_mountflags, 0); + if (IS_ERR(sb)) { + printk(KERN_ERR "VFS: Error getting nodev super block for cramfs linear root: %d\n", + PTR_ERR(sb)); + goto root_cramfs_linear_end; + } + ROOT_DEV = sb->s_dev; + up(&mount_sem); + goto mount_it; +root_cramfs_linear_end: +#endif #ifdef CONFIG_ROOT_NFS void *data; diff -urNbB linux-2.4.9/scripts/cramfs/mkcramfs.c linux-2.4.9-bdl/scripts/cramfs/mkcramfs.c --- linux-2.4.9/scripts/cramfs/mkcramfs.c Thu Jul 19 18:14:53 2001 +++ linux-2.4.9-bdl/scripts/cramfs/mkcramfs.c Wed Aug 29 12:48:44 2001 @@ -59,6 +59,10 @@ exit(status); } +#define PAGE_SIZE (4096) +#define PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) +#define ROM_OFFSET 0 +#define ROM_ALIGN(x) (PAGE_ALIGN((x) + ROM_OFFSET) - ROM_OFFSET) #define PAGE_CACHE_SIZE (4096) /* The kernel assumes PAGE_CACHE_SIZE as block size. */ static unsigned int blksize = PAGE_CACHE_SIZE; @@ -464,6 +468,30 @@ return 0; } +static unsigned int do_xip(char *base, unsigned int offset, + char const *name, char *uncompressed, + unsigned int size) +{ + unsigned int start, end; + + /* align to page boundary */ + + start = ROM_ALIGN(offset); + memset(base + offset, 0, start - offset); + + memcpy(base + start, uncompressed, size); + + /* pad to page boundary */ + + end = ROM_ALIGN(start + size); + memset(base + start + size, 0, end - (start + size)); + + printf("XIP (%u+%u bytes)\toffset %u\t%s\n", + size, (end - offset) - size, offset, name); + + return end; +} + /* * One 4-byte pointer per block and then the actual blocked * output. The first block does not need an offset pointer, @@ -513,8 +541,8 @@ st_blocks * 512. But if you say that then perhaps administrative data should also be included in both. */ change = new_size - original_size; - printf("%6.2f%% (%+d bytes)\t%s\n", - (change * 100) / (double) original_size, change, name); + printf("%6.2f%% (%+d bytes)\toffset %lu\t%s\n", + (change * 100) / (double) original_size, change, original_offset, name); return curr; } @@ -535,6 +563,9 @@ } else { set_data_offset(entry, base, offset); entry->offset=offset; + if (entry->mode & S_ISVTX) + offset = do_xip(base, offset, entry->name, entry->uncompressed, entry->size); + else offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size); } }