Commit da4458bd authored by David Howells's avatar David Howells Committed by David Woodhouse

NOMMU: Make it possible for RomFS to use MTD devices directly

Change RomFS so that it can use MTD devices directly - without the intercession
of the block layer - as well as using block devices.

This permits RomFS:

 (1) to use the MTD direct mapping facility available under NOMMU conditions if
     the underlying device is directly accessible by the CPU (including XIP);

 (2) and thus to be used when the block layer is disabled.

RomFS can be configured with support just for MTD devices, just for Block
devices or for both.  If RomFS is configured for both, then it will treat
mtdblock device files as MTD backing stores, not block layer backing stores.

I tested this using a CONFIG_MMU=n CONFIG_BLOCK=n kernel running on my FRV
board with a RomFS image installed on the mtdram test device.  I see my test
program being run XIP:

	# cat /proc/maps
	...
	c0c000b0-c0c01f8c r-xs 00000000 1f:00 144        /mnt/doshm
	...

GDB on the kernel can be used to show that these addresses are within the
set-aside RAM space.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Tested-by: default avatarBernd Schmidt <bernd.schmidt@analog.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 6e232cfc
......@@ -14,3 +14,27 @@ config ROMFS_FS
If you don't know whether you need it, then you don't need it:
answer N.
config ROMFS_ON_BLOCK
bool "Block device-backed ROM file system support" if (ROMFS_ON_MTD && EMBEDDED)
depends on ROMFS_FS && BLOCK
help
This permits ROMFS to use block devices buffered through the page
cache as the medium from which to retrieve data. It does not allow
direct mapping of the medium.
If unsure, answer Y.
config ROMFS_ON_MTD
bool "MTD-backed ROM file system support"
depends on ROMFS_FS
depends on MTD=y || (ROMFS_FS=m && MTD)
help
This permits ROMFS to use MTD based devices directly, without the
intercession of the block layer (which may have been disabled). It
also allows direct mapping of MTD devices through romfs files under
NOMMU conditions if the underlying device is directly addressable by
the CPU.
If unsure, answer Y.
#
# Makefile for the linux romfs filesystem routines.
# Makefile for the linux RomFS filesystem routines.
#
obj-$(CONFIG_ROMFS_FS) += romfs.o
romfs-objs := inode.o
romfs-y := storage.o super.o
ifneq ($(CONFIG_MMU),y)
romfs-$(CONFIG_ROMFS_ON_MTD) += mmap-nommu.o
endif
/* RomFS internal definitions
*
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* 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.
*/
#include <linux/romfs_fs.h>
struct romfs_inode_info {
struct inode vfs_inode;
unsigned long i_metasize; /* size of non-data area */
unsigned long i_dataoffset; /* from the start of fs */
};
static inline size_t romfs_maxsize(struct super_block *sb)
{
return (size_t) (unsigned long) sb->s_fs_info;
}
static inline struct romfs_inode_info *ROMFS_I(struct inode *inode)
{
return container_of(inode, struct romfs_inode_info, vfs_inode);
}
/*
* mmap-nommu.c
*/
#if !defined(CONFIG_MMU) && defined(CONFIG_ROMFS_ON_MTD)
extern const struct file_operations romfs_ro_fops;
#else
#define romfs_ro_fops generic_ro_fops
#endif
/*
* storage.c
*/
extern int romfs_dev_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen);
extern ssize_t romfs_dev_strnlen(struct super_block *sb,
unsigned long pos, size_t maxlen);
extern int romfs_dev_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size);
/* NOMMU mmap support for RomFS on MTD devices
*
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* 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.
*/
#include <linux/mm.h>
#include <linux/mtd/super.h>
#include "internal.h"
/*
* try to determine where a shared mapping can be made
* - only supported for NOMMU at the moment (MMU can't doesn't copy private
* mappings)
* - attempts to map through to the underlying MTD device
*/
static unsigned long romfs_get_unmapped_area(struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
unsigned long flags)
{
struct inode *inode = file->f_mapping->host;
struct mtd_info *mtd = inode->i_sb->s_mtd;
unsigned long isize, offset;
if (!mtd)
goto cant_map_directly;
isize = i_size_read(inode);
offset = pgoff << PAGE_SHIFT;
if (offset > isize || len > isize || offset > isize - len)
return (unsigned long) -EINVAL;
/* we need to call down to the MTD layer to do the actual mapping */
if (mtd->get_unmapped_area) {
if (addr != 0)
return (unsigned long) -EINVAL;
if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
return (unsigned long) -EINVAL;
offset += ROMFS_I(inode)->i_dataoffset;
if (offset > mtd->size - len)
return (unsigned long) -EINVAL;
return mtd->get_unmapped_area(mtd, len, offset, flags);
}
cant_map_directly:
return (unsigned long) -ENOSYS;
}
/*
* permit a R/O mapping to be made directly through onto an MTD device if
* possible
*/
static int romfs_mmap(struct file *file, struct vm_area_struct *vma)
{
return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS;
}
const struct file_operations romfs_ro_fops = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
.splice_read = generic_file_splice_read,
.mmap = romfs_mmap,
.get_unmapped_area = romfs_get_unmapped_area,
};
/* RomFS storage access routines
*
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* 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.
*/
#include <linux/fs.h>
#include <linux/mtd/super.h>
#include <linux/buffer_head.h>
#include "internal.h"
#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)
#error no ROMFS backing store interface configured
#endif
#ifdef CONFIG_ROMFS_ON_MTD
#define ROMFS_MTD_READ(sb, ...) ((sb)->s_mtd->read((sb)->s_mtd, ##__VA_ARGS__))
/*
* read data from an romfs image on an MTD device
*/
static int romfs_mtd_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen)
{
size_t rlen;
int ret;
ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf);
return (ret < 0 || rlen != buflen) ? -EIO : 0;
}
/*
* determine the length of a string in a romfs image on an MTD device
*/
static ssize_t romfs_mtd_strnlen(struct super_block *sb,
unsigned long pos, size_t maxlen)
{
ssize_t n = 0;
size_t segment;
u_char buf[16], *p;
size_t len;
int ret;
/* scan the string up to 16 bytes at a time */
while (maxlen > 0) {
segment = min_t(size_t, maxlen, 16);
ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
if (ret < 0)
return ret;
p = memchr(buf, 0, len);
if (p)
return n + (p - buf);
maxlen -= len;
pos += len;
n += len;
}
return n;
}
/*
* compare a string to one in a romfs image on MTD
* - return 1 if matched, 0 if differ, -ve if error
*/
static int romfs_mtd_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
{
u_char buf[16];
size_t len, segment;
int ret;
/* scan the string up to 16 bytes at a time */
while (size > 0) {
segment = min_t(size_t, size, 16);
ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
if (ret < 0)
return ret;
if (memcmp(buf, str, len) != 0)
return 0;
size -= len;
pos += len;
str += len;
}
return 1;
}
#endif /* CONFIG_ROMFS_ON_MTD */
#ifdef CONFIG_ROMFS_ON_BLOCK
/*
* read data from an romfs image on a block device
*/
static int romfs_blk_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen)
{
struct buffer_head *bh;
unsigned long offset;
size_t segment;
/* copy the string up to blocksize bytes at a time */
while (buflen > 0) {
offset = pos & (ROMBSIZE - 1);
segment = min_t(size_t, buflen, ROMBSIZE - offset);
bh = sb_bread(sb, pos >> ROMBSBITS);
if (!bh)
return -EIO;
memcpy(buf, bh->b_data + offset, segment);
brelse(bh);
buflen -= segment;
pos += segment;
}
return 0;
}
/*
* determine the length of a string in romfs on a block device
*/
static ssize_t romfs_blk_strnlen(struct super_block *sb,
unsigned long pos, size_t limit)
{
struct buffer_head *bh;
unsigned long offset;
ssize_t n = 0;
size_t segment;
u_char *buf, *p;
/* scan the string up to blocksize bytes at a time */
while (limit > 0) {
offset = pos & (ROMBSIZE - 1);
segment = min_t(size_t, limit, ROMBSIZE - offset);
bh = sb_bread(sb, pos >> ROMBSBITS);
if (!bh)
return -EIO;
buf = bh->b_data + offset;
p = memchr(buf, 0, segment);
brelse(bh);
if (p)
return n + (p - buf);
limit -= segment;
pos += segment;
n += segment;
}
return n;
}
/*
* compare a string to one in a romfs image on a block device
* - return 1 if matched, 0 if differ, -ve if error
*/
static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
{
struct buffer_head *bh;
unsigned long offset;
size_t segment;
bool x;
/* scan the string up to 16 bytes at a time */
while (size > 0) {
offset = pos & (ROMBSIZE - 1);
segment = min_t(size_t, size, ROMBSIZE - offset);
bh = sb_bread(sb, pos >> ROMBSBITS);
if (!bh)
return -EIO;
x = (memcmp(bh->b_data + offset, str, segment) != 0);
brelse(bh);
if (x)
return 0;
size -= segment;
pos += segment;
str += segment;
}
return 1;
}
#endif /* CONFIG_ROMFS_ON_BLOCK */
/*
* read data from the romfs image
*/
int romfs_dev_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen)
{
size_t limit;
limit = romfs_maxsize(sb);
if (pos >= limit)
return -EIO;
if (buflen > limit - pos)
buflen = limit - pos;
#ifdef CONFIG_ROMFS_ON_MTD
if (sb->s_mtd)
return romfs_mtd_read(sb, pos, buf, buflen);
#endif
#ifdef CONFIG_ROMFS_ON_BLOCK
if (sb->s_bdev)
return romfs_blk_read(sb, pos, buf, buflen);
#endif
return -EIO;
}
/*
* determine the length of a string in romfs
*/
ssize_t romfs_dev_strnlen(struct super_block *sb,
unsigned long pos, size_t maxlen)
{
size_t limit;
limit = romfs_maxsize(sb);
if (pos >= limit)
return -EIO;
if (maxlen > limit - pos)
maxlen = limit - pos;
#ifdef CONFIG_ROMFS_ON_MTD
if (sb->s_mtd)
return romfs_mtd_strnlen(sb, pos, limit);
#endif
#ifdef CONFIG_ROMFS_ON_BLOCK
if (sb->s_bdev)
return romfs_blk_strnlen(sb, pos, limit);
#endif
return -EIO;
}
/*
* compare a string to one in romfs
* - return 1 if matched, 0 if differ, -ve if error
*/
int romfs_dev_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
{
size_t limit;
limit = romfs_maxsize(sb);
if (pos >= limit)
return -EIO;
if (size > ROMFS_MAXFN)
return -ENAMETOOLONG;
if (size > limit - pos)
return -EIO;
#ifdef CONFIG_ROMFS_ON_MTD
if (sb->s_mtd)
return romfs_mtd_strncmp(sb, pos, str, size);
#endif
#ifdef CONFIG_ROMFS_ON_BLOCK
if (sb->s_bdev)
return romfs_blk_strncmp(sb, pos, str, size);
#endif
return -EIO;
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment