On Wed, Sep 27, 2017 at 07:32:20PM -0400, Nicolas Pitre wrote: > To distinguish between both access types, the cramfs_physmem filesystem > type must be specified when using a memory accessible cramfs image, and > the physaddr argument must provide the actual filesystem image's physical > memory location. Sorry, but this still is a complete no-go. A physical address is not a proper interface. You still need to have some interface for your NOR nand or DRAM. - usually that would be a mtd driver, but if you have a good reason why that's not suitable for you (and please explain it well) we'll need a little OF or similar layer to bind a thin driver. > > Signed-off-by: Nicolas Pitre <nico@xxxxxxxxxx> > Tested-by: Chris Brandt <chris.brandt@xxxxxxxxxxx> > --- > fs/cramfs/Kconfig | 29 +++++- > fs/cramfs/inode.c | 264 +++++++++++++++++++++++++++++++++++++++++++----------- > 2 files changed, 241 insertions(+), 52 deletions(-) > > diff --git a/fs/cramfs/Kconfig b/fs/cramfs/Kconfig > index 11b29d491b..5b4e0b7e13 100644 > --- a/fs/cramfs/Kconfig > +++ b/fs/cramfs/Kconfig > @@ -1,6 +1,5 @@ > config CRAMFS > tristate "Compressed ROM file system support (cramfs) (OBSOLETE)" > - depends on BLOCK > select ZLIB_INFLATE > help > Saying Y here includes support for CramFs (Compressed ROM File > @@ -20,3 +19,31 @@ config CRAMFS > in terms of performance and features. > > If unsure, say N. > + > +config CRAMFS_BLOCKDEV > + bool "Support CramFs image over a regular block device" if EXPERT > + depends on CRAMFS && BLOCK > + default y > + help > + This option allows the CramFs driver to load data from a regular > + block device such a disk partition or a ramdisk. > + > +config CRAMFS_PHYSMEM > + bool "Support CramFs image directly mapped in physical memory" > + depends on CRAMFS > + default y if !CRAMFS_BLOCKDEV > + help > + This option allows 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. > + > + The filesystem type for this feature is "cramfs_physmem". > + 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_physmem -o physaddr=0x100000 none /mnt"). > + > + If unsure, say N. > diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c > index 7919967488..19f464a214 100644 > --- a/fs/cramfs/inode.c > +++ b/fs/cramfs/inode.c > @@ -24,6 +24,7 @@ > #include <linux/mutex.h> > #include <uapi/linux/cramfs_fs.h> > #include <linux/uaccess.h> > +#include <linux/io.h> > > #include "internal.h" > > @@ -36,6 +37,8 @@ struct cramfs_sb_info { > unsigned long blocks; > unsigned long files; > unsigned long flags; > + void *linear_virt_addr; > + phys_addr_t linear_phys_addr; > }; > > static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) > @@ -140,6 +143,9 @@ static struct inode *get_cramfs_inode(struct super_block *sb, > * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to > * worry about end-of-buffer issues even when decompressing a full > * page cache. > + * > + * Note: This is all optimized away at compile time when > + * CONFIG_CRAMFS_BLOCKDEV=n. > */ > #define READ_BUFFERS (2) > /* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ > @@ -160,10 +166,10 @@ static struct super_block *buffer_dev[READ_BUFFERS]; > static int next_buffer; > > /* > - * Returns a pointer to a buffer containing at least LEN bytes of > - * filesystem starting at byte offset OFFSET into the filesystem. > + * Populate our block cache and return a pointer from it. > */ > -static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) > +static void *cramfs_blkdev_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]; > @@ -239,7 +245,39 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i > return read_buffers[buffer] + offset; > } > > -static void cramfs_kill_sb(struct super_block *sb) > +/* > + * Return a pointer to the linearly addressed cramfs image in memory. > + */ > +static void *cramfs_direct_read(struct super_block *sb, unsigned int offset, > + unsigned int len) > +{ > + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); > + > + if (!len) > + return NULL; > + if (len > sbi->size || offset > sbi->size - len) > + return page_address(ZERO_PAGE(0)); > + return sbi->linear_virt_addr + offset; > +} > + > +/* > + * 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) > +{ > + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); > + > + if (IS_ENABLED(CONFIG_CRAMFS_PHYSMEM) && sbi->linear_virt_addr) > + return cramfs_direct_read(sb, offset, len); > + else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) > + return cramfs_blkdev_read(sb, offset, len); > + else > + return NULL; > +} > + > +static void cramfs_blkdev_kill_sb(struct super_block *sb) > { > struct cramfs_sb_info *sbi = CRAMFS_SB(sb); > > @@ -247,6 +285,16 @@ static void cramfs_kill_sb(struct super_block *sb) > kfree(sbi); > } > > +static void cramfs_physmem_kill_sb(struct super_block *sb) > +{ > + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); > + > + if (sbi->linear_virt_addr) > + memunmap(sbi->linear_virt_addr); > + kill_anon_super(sb); > + kfree(sbi); > +} > + > static int cramfs_remount(struct super_block *sb, int *flags, char *data) > { > sync_filesystem(sb); > @@ -254,34 +302,24 @@ static int cramfs_remount(struct super_block *sb, int *flags, char *data) > return 0; > } > > -static int cramfs_fill_super(struct super_block *sb, void *data, int silent) > +static int cramfs_read_super(struct super_block *sb, > + struct cramfs_super *super, int silent) > { > - int i; > - struct cramfs_super super; > + struct cramfs_sb_info *sbi = CRAMFS_SB(sb); > unsigned long root_offset; > - struct cramfs_sb_info *sbi; > - struct inode *root; > - > - sb->s_flags |= MS_RDONLY; > - > - sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); > - if (!sbi) > - return -ENOMEM; > - sb->s_fs_info = sbi; > > - /* Invalidate the read buffers on mount: think disk change.. */ > - mutex_lock(&read_mutex); > - for (i = 0; i < READ_BUFFERS; i++) > - buffer_blocknr[i] = -1; > + /* We don't know the real size yet */ > + sbi->size = PAGE_SIZE; > > /* Read the first block and get the superblock from it */ > - memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super)); > + mutex_lock(&read_mutex); > + memcpy(super, cramfs_read(sb, 0, sizeof(*super)), sizeof(*super)); > mutex_unlock(&read_mutex); > > /* Do sanity checks on the superblock */ > - if (super.magic != CRAMFS_MAGIC) { > + if (super->magic != CRAMFS_MAGIC) { > /* check for wrong endianness */ > - if (super.magic == CRAMFS_MAGIC_WEND) { > + if (super->magic == CRAMFS_MAGIC_WEND) { > if (!silent) > pr_err("wrong endianness\n"); > return -EINVAL; > @@ -289,10 +327,10 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) > > /* check at 512 byte offset */ > mutex_lock(&read_mutex); > - memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super)); > + memcpy(super, cramfs_read(sb, 512, sizeof(*super)), sizeof(*super)); > mutex_unlock(&read_mutex); > - if (super.magic != CRAMFS_MAGIC) { > - if (super.magic == CRAMFS_MAGIC_WEND && !silent) > + if (super->magic != CRAMFS_MAGIC) { > + if (super->magic == CRAMFS_MAGIC_WEND && !silent) > pr_err("wrong endianness\n"); > else if (!silent) > pr_err("wrong magic\n"); > @@ -301,34 +339,34 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) > } > > /* get feature flags first */ > - if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { > + if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) { > pr_err("unsupported filesystem features\n"); > return -EINVAL; > } > > /* Check that the root inode is in a sane state */ > - if (!S_ISDIR(super.root.mode)) { > + if (!S_ISDIR(super->root.mode)) { > pr_err("root is not a directory\n"); > return -EINVAL; > } > /* correct strange, hard-coded permissions of mkcramfs */ > - super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); > + super->root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); > > - root_offset = super.root.offset << 2; > - if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { > - sbi->size = super.size; > - sbi->blocks = super.fsid.blocks; > - sbi->files = super.fsid.files; > + root_offset = super->root.offset << 2; > + if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { > + sbi->size = super->size; > + sbi->blocks = super->fsid.blocks; > + sbi->files = super->fsid.files; > } else { > sbi->size = 1<<28; > sbi->blocks = 0; > sbi->files = 0; > } > - sbi->magic = super.magic; > - sbi->flags = super.flags; > + sbi->magic = super->magic; > + sbi->flags = super->flags; > if (root_offset == 0) > pr_info("empty filesystem"); > - else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && > + else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && > ((root_offset != sizeof(struct cramfs_super)) && > (root_offset != 512 + sizeof(struct cramfs_super)))) > { > @@ -336,9 +374,18 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) > return -EINVAL; > } > > + return 0; > +} > + > +static int cramfs_finalize_super(struct super_block *sb, > + struct cramfs_inode *cramfs_root) > +{ > + struct inode *root; > + > /* Set it all up.. */ > + sb->s_flags |= MS_RDONLY; > sb->s_op = &cramfs_ops; > - root = get_cramfs_inode(sb, &super.root, 0); > + root = get_cramfs_inode(sb, cramfs_root, 0); > if (IS_ERR(root)) > return PTR_ERR(root); > sb->s_root = d_make_root(root); > @@ -347,6 +394,92 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent) > return 0; > } > > +static int cramfs_blkdev_fill_super(struct super_block *sb, void *data, int silent) > +{ > + struct cramfs_sb_info *sbi; > + struct cramfs_super super; > + int i, err; > + > + sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); > + if (!sbi) > + return -ENOMEM; > + sb->s_fs_info = sbi; > + > + /* Invalidate the read buffers on mount: think disk change.. */ > + for (i = 0; i < READ_BUFFERS; i++) > + buffer_blocknr[i] = -1; > + > + err = cramfs_read_super(sb, &super, silent); > + if (err) > + return err; > + return cramfs_finalize_super(sb, &super.root); > +} > + > +static int cramfs_physmem_fill_super(struct super_block *sb, void *data, int silent) > +{ > + struct cramfs_sb_info *sbi; > + struct cramfs_super super; > + char *p; > + int err; > + > + sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); > + if (!sbi) > + return -ENOMEM; > + sb->s_fs_info = sbi; > + > + /* > + * 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. > + */ > + if (!data || !(p = strstr((char *)data, "physaddr="))) { > + pr_err("unknown physical address for linear cramfs image\n"); > + return -EINVAL; > + } > + sbi->linear_phys_addr = memparse(p + 9, NULL); > + if (!sbi->linear_phys_addr) { > + pr_err("bad value for cramfs image physical address\n"); > + return -EINVAL; > + } > + if (sbi->linear_phys_addr & (PAGE_SIZE-1)) { > + pr_err("physical address %pap for linear cramfs isn't aligned to a page boundary\n", > + &sbi->linear_phys_addr); > + return -EINVAL; > + } > + > + /* > + * Map only one page for now. Will remap it when fs size is known. > + * Although we'll only read from it, we want the CPU cache to > + * kick in for the higher throughput it provides, hence MEMREMAP_WB. > + */ > + pr_info("checking physical address %pap for linear cramfs image\n", &sbi->linear_phys_addr); > + sbi->linear_virt_addr = memremap(sbi->linear_phys_addr, PAGE_SIZE, > + MEMREMAP_WB); > + if (!sbi->linear_virt_addr) { > + pr_err("ioremap of the linear cramfs image failed\n"); > + return -ENOMEM; > + } > + > + err = cramfs_read_super(sb, &super, silent); > + if (err) > + return err; > + > + /* Remap the whole filesystem now */ > + pr_info("linear cramfs image appears to be %lu KB in size\n", > + sbi->size/1024); > + memunmap(sbi->linear_virt_addr); > + sbi->linear_virt_addr = memremap(sbi->linear_phys_addr, sbi->size, > + MEMREMAP_WB); > + if (!sbi->linear_virt_addr) { > + pr_err("ioremap of the linear cramfs image failed\n"); > + return -ENOMEM; > + } > + > + return cramfs_finalize_super(sb, &super.root); > +} > + > static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) > { > struct super_block *sb = dentry->d_sb; > @@ -573,38 +706,67 @@ static const struct super_operations cramfs_ops = { > .statfs = cramfs_statfs, > }; > > -static struct dentry *cramfs_mount(struct file_system_type *fs_type, > - int flags, const char *dev_name, void *data) > +static struct dentry *cramfs_blkdev_mount(struct file_system_type *fs_type, > + int flags, const char *dev_name, void *data) > +{ > + return mount_bdev(fs_type, flags, dev_name, data, cramfs_blkdev_fill_super); > +} > + > +static struct dentry *cramfs_physmem_mount(struct file_system_type *fs_type, > + int flags, const char *dev_name, void *data) > { > - return mount_bdev(fs_type, flags, dev_name, data, cramfs_fill_super); > + return mount_nodev(fs_type, flags, data, cramfs_physmem_fill_super); > } > > static struct file_system_type cramfs_fs_type = { > .owner = THIS_MODULE, > .name = "cramfs", > - .mount = cramfs_mount, > - .kill_sb = cramfs_kill_sb, > + .mount = cramfs_blkdev_mount, > + .kill_sb = cramfs_blkdev_kill_sb, > .fs_flags = FS_REQUIRES_DEV, > }; > + > +static struct file_system_type cramfs_physmem_fs_type = { > + .owner = THIS_MODULE, > + .name = "cramfs_physmem", > + .mount = cramfs_physmem_mount, > + .kill_sb = cramfs_physmem_kill_sb, > +}; > + > +#ifdef CONFIG_CRAMFS_BLOCKDEV > MODULE_ALIAS_FS("cramfs"); > +#endif > +#ifdef CONFIG_CRAMFS_PHYSMEM > +MODULE_ALIAS_FS("cramfs_physmem"); > +#endif > > static int __init init_cramfs_fs(void) > { > int rv; > > - rv = cramfs_uncompress_init(); > - if (rv < 0) > - return rv; > - rv = register_filesystem(&cramfs_fs_type); > - if (rv < 0) > - cramfs_uncompress_exit(); > - return rv; > + if ((rv = cramfs_uncompress_init()) < 0) > + goto err0; > + if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV) && > + (rv = register_filesystem(&cramfs_fs_type)) < 0) > + goto err1; > + if (IS_ENABLED(CONFIG_CRAMFS_PHYSMEM) && > + (rv = register_filesystem(&cramfs_physmem_fs_type)) < 0) > + goto err2; > + return 0; > + > +err2: if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) > + unregister_filesystem(&cramfs_fs_type); > +err1: cramfs_uncompress_exit(); > +err0: return rv; > } > > static void __exit exit_cramfs_fs(void) > { > cramfs_uncompress_exit(); > - unregister_filesystem(&cramfs_fs_type); > + if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) > + unregister_filesystem(&cramfs_fs_type); > + if (IS_ENABLED(CONFIG_CRAMFS_PHYSMEM)) > + unregister_filesystem(&cramfs_physmem_fs_type); > } > > module_init(init_cramfs_fs) > -- > 2.9.5 > ---end quoted text--- -- To unsubscribe from this list: send the line "unsubscribe linux-embedded" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html