Finished adding untested bare functionality vfs

Finished adding untested shm syscalls.
Finished adding untested l4 send/recv helpers

Everything compiles. Now going to fix lots of bugs ;-)
This commit is contained in:
Bahadir Balban
2008-02-03 17:42:38 +00:00
parent 05e9028e90
commit cab2e8bdd3
51 changed files with 1661 additions and 227 deletions

View File

@@ -14,7 +14,7 @@ task_name = "fs0"
# The root directory of the repository where this file resides:
project_root = "../.."
tools_root = join(project_root, "tools")
prev_image = join(project_root, "tasks/blkdev0/blkdev0.axf")
prev_image = join(project_root, "tasks/mm0/mm0.axf")
libs_path = join(project_root, "libs")
ld_script = "include/linker.lds"
physical_base_ld_script = "include/physical_base.lds"
@@ -26,6 +26,10 @@ libc_incpath = join(libc_libpath, "include")
libc_crt0 = join(libs_path, "c/build/crt/sys-userspace/arch-arm/crt0.o")
libc_name = "c-%s" % libc_variant
#libmem paths:
libmem_path = "../libmem"
libmem_incpath = "../libmem"
# libl4 paths:
libl4_path = "../libl4"
libl4_incpath1 = join(libl4_path, "include")
@@ -47,16 +51,16 @@ env = Environment(CC = 'arm-none-linux-gnueabi-gcc',
# We don't use -nostdinc because sometimes we need standard headers,
# such as stdarg.h e.g. for variable args, as in printk().
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib', '-T' + ld_script, "-L" + libc_libpath, "-L" + libl4_path],
LINKFLAGS = ['-nostdlib', '-T' + ld_script, "-L" + libc_libpath, "-L" + libl4_path,\
"-L" + libmem_path],
ASFLAGS = ['-D__ASSEMBLY__'],
PROGSUFFIX = '.axf', # The suffix to use for final executable
ENV = {'PATH' : os.environ['PATH']}, # Inherit shell path
LIBS = [libc_name, 'gcc', libc_name, \
'libl4'], # libgcc.a - This is required for division routines.
LIBS = [libc_name, 'gcc', 'libmc', 'libl4', 'gcc', libc_name],
CPPFLAGS = "-D__USERSPACE__",
CPPPATH = ['#include', libl4_incpath1, kernel_incpath, libc_incpath])
CPPPATH = ['#include', libl4_incpath1, kernel_incpath, libc_incpath, libmem_incpath])
src = [glob("src/*.c"), glob("*.c"), glob("src/arch/arm/*.c")]
src = [glob("src/*.c"), glob("*.c"), glob("src/arch/arm/*.c"), glob("src/memfs/*.c"), glob("src/lib/*.c")]
objs = env.Object(src)
physical_base = env.Command(physical_base_ld_script, prev_image, get_physical_base)
crt0_copied = env.Command("crt0.o", libc_crt0, copy_crt0)

8
tasks/fs0/include/bdev.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef __BDEV_H__
#define __BDEV_H__
/*
* This is a mock-up compiled in blockdev buffer, to be used temporarily.
*/
void *vfs_rootdev_open(void);
#endif/* __BDEV_H__ */

18
tasks/fs0/include/file.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef __FS0_MM_H__
#define __FS0_MM_H__
/*
* Describes the in-memory representation of a file. This is used to track
* file content, i.e. file pages by mm0, this is a temporary mock up until
* fs0 and mm0 are wired together.
*/
struct vm_file {
unsigned long vnum;
unsigned long length;
/* This is the cache of physical pages that this file has in memory. */
struct list_head page_cache_list;
struct vm_pager *pager;
};
#endif /* __FS0_MM_H__ */

View File

@@ -1,25 +1,41 @@
/*
* VFS definitions.
*
* Copyright (C) 2007 Bahadir Balban.
* Copyright (C) 2007, 2008 Bahadir Balban.
*/
#ifndef __FS_H__
#define __FS_H__
#include <l4/lib/list.h>
typedef void (*dentry_op_t)(void);
typedef void (*superblock_op_t)(void);
#include <l4/lib/list.h>
#include <l4/macros.h>
#include <l4lib/types.h>
#include <stat.h>
typedef void (*vnode_op_t)(void);
typedef void (*file_op_t)(void);
struct dentry;
struct file;
struct file_system_type;
struct superblock;
struct vnode;
struct dentry_ops {
dentry_op_t compare;
int (*compare)(struct dentry *d, char *n);
};
/* Operations that work on file content */
struct file_ops {
/* Read a vnode's contents by page range */
int (*read)(struct vnode *v, unsigned long pfn,
unsigned long npages, void *buf);
/* Write a vnode's contents by page range */
int (*write)(struct vnode *v, unsigned long pfn,
unsigned long npages, void *buf);
file_op_t open;
file_op_t read;
file_op_t write;
file_op_t close;
file_op_t mmap;
file_op_t lseek;
@@ -27,9 +43,11 @@ struct file_ops {
file_op_t fsync;
};
/* Operations that work on vnode fields and associations between vnodes */
struct vnode_ops {
vnode_op_t create;
vnode_op_t lookup;
struct vnode *(*lookup)(struct vnode *root, char *path);
void * (*readdir)(struct vnode *v, void *dirbuf);
vnode_op_t link;
vnode_op_t unlink;
vnode_op_t mkdir;
@@ -40,22 +58,28 @@ struct vnode_ops {
};
struct superblock_ops {
superblock_op_t read_sb;
superblock_op_t write_sb;
superblock_op_t read_vnode;
superblock_op_t write_vnode;
int (*write_sb)(struct superblock *sb);
/*
* Given a vnum, reads the disk-inode and copies its data
* into the vnode's generic fields
*/
int (*read_vnode)(struct superblock *sb, struct vnode *v);
/* Writes vnode's generic fields into the disk-inode structure */
int (*write_vnode)(struct superblock *sb, struct vnode *v);
/* Allocates a disk-inode along with a vnode, and associates the two */
struct vnode *(*alloc_vnode)(struct superblock *sb);
/* Frees the vnode and the corresponding on-disk inode */
int (*free_vnode)(struct superblock *sb, struct vnode *v);
};
struct dentry;
struct file;
struct file_system_type;
struct superblock;
struct vnode;
#define VFS_DENTRY_NAME_MAX 512
#define VFS_DNAME_MAX 256
struct dentry {
int refcnt;
char name[VFS_DENTRY_NAME_MAX];
char name[VFS_DNAME_MAX];
struct dentry *parent; /* Parent dentry */
struct list_head child; /* List of dentries with same parent */
struct list_head children; /* List of children dentries */
@@ -64,35 +88,54 @@ struct dentry {
struct dentry_ops ops;
};
struct file {
int refcnt;
struct dentry *dentry;
struct file_ops ops;
};
struct vnode {
unsigned long id; /* Filesystem-wide unique vnode id */
unsigned long vnum; /* Filesystem-wide unique vnode id */
int refcnt; /* Reference counter */
int hardlinks; /* Number of hard links */
struct superblock *sb; /* Reference to superblock */
struct vnode_ops ops; /* Operations on this vnode */
struct list_head dirents; /* Dirents that refer to this vnode */
struct file_ops fops; /* File-related operations on this vnode */
struct list_head dentries; /* Dirents that refer to this vnode */
struct list_head state_list; /* List for vnode's dirty/clean state */
unsigned long size; /* Total size of vnode in bytes */
u32 type; /* Vnode type, dev? socket? dir? ... */
u32 mode; /* Permissions */
u32 owner; /* Owner */
u64 atime; /* Last access time */
u64 mtime; /* Last content modification */
u64 ctime; /* Last vnode modification */
u64 size; /* Size of contents */
void *inode; /* Ptr to fs-specific inode */
};
struct file_system_type {
char name[256];
unsigned long magic;
unsigned int flags;
struct superblock *(*get_sb)(void);
struct list_head sb_list;
/* FS0 vfs specific macros */
#define vfs_isdir(v) S_ISDIR((v)->type)
struct fstype_ops {
struct superblock *(*get_superblock)(void *buf);
};
#define VFS_FSNAME_MAX 256
struct file_system_type {
char name[VFS_FSNAME_MAX];
unsigned long magic;
struct fstype_ops ops;
struct list_head list; /* Member of list of all fs types */
struct list_head sblist; /* List of superblocks with this type */
};
struct superblock *get_superblock(void *buf);
struct superblock {
struct file_system_type fs;
struct superblock_ops ops;
struct dentry *root_dirent;
u64 fssize;
unsigned int blocksize;
struct list_head list;
struct file_system_type *fs;
struct superblock_ops *ops;
struct vnode *root;
void *fs_super;
};
void vfs_mount_fs(struct superblock *sb);
extern struct dentry_ops generic_dentry_operations;
#endif /* __FS_H__ */

7
tasks/fs0/include/init.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef __INIT_H__
#define __INIT_H__
/* FS0 initialisation */
int initialise(void);
#endif /* __INIT_H__ */

View File

@@ -0,0 +1,19 @@
#ifndef __PRIVATE_MALLOC_H__
#define __PRIVATE_MALLOC_H__
#include <stddef.h>
#include <string.h>
void *kmalloc(size_t size);
void kfree(void *blk);
static inline void *kzalloc(size_t size)
{
void *buf = kmalloc(size);
memset(buf, 0, size);
return buf;
}
#endif /*__PRIVATE_MALLOC_H__ */

View File

@@ -4,13 +4,11 @@
#ifndef __MM0_SPINLOCK_H__
#define __MM0_SPINLOCK_H__
struct spinlock {
int lock;
};
static inline void spin_lock_init(struct spinlock *s) { }
static inline void spin_lock_init(struct spinlock *s) { }
static inline void spin_lock(struct spinlock *s) { }
static inline void spin_unlock(struct spinlock *s) { }

View File

@@ -30,13 +30,13 @@ SECTIONS
/* rodata is needed else your strings will link at physical! */
.rodata : AT (ADDR(.rodata) - offset) { *(.rodata) }
.rodata1 : AT (ADDR(.rodata1) - offset) { *(.rodata1) }
.data : AT (ADDR(.data) - offset)
.data : AT (ADDR(.data) - offset)
{
. = ALIGN(4K);
_start_ramdisk = .;
*(.data.diskspace)
_end_ramdisk = .;
*(.data)
_start_bdev = .;
*(.data.memfs)
_end_bdev = .;
*(.data)
}
.bss : AT (ADDR(.bss) - offset) { *(.bss) }
_end = .;

View File

@@ -1,7 +0,0 @@
#ifndef __PRIVATE_MALLOC_H__
#define __PRIVATE_MALLOC_H__
void *kmalloc(size_t size);
void kfree(void *blk);
#endif /*__PRIVATE_MALLOC_H__ */

View File

@@ -0,0 +1,97 @@
/*
* The disk layout of our simple unix-like filesystem.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#ifndef __MEMFS_LAYOUT_H__
#define __MEMFS_LAYOUT_H__
#include <l4lib/types.h>
#include <l4/lib/list.h>
#include <l4/macros.h>
#include <l4/config.h>
#include INC_GLUE(memory.h)
#include <memcache/memcache.h>
#include <lib/idpool.h>
/*
*
* Filesystem layout:
*
* |---------------|
* | Superblock |
* |---------------|
*
* Superblock layout:
*
* |---------------|
* | inode cache |
* |---------------|
* | dentry cache |
* |---------------|
* | block cache |
* |---------------|
*
*/
/*
* These fixed filesystem limits make it much easier to implement
* filesystem space allocation.
*/
#define MEMFS_TOTAL_SIZE SZ_8MB
#define MEMFS_TOTAL_INODES 128
#define MEMFS_TOTAL_BLOCKS 2000
#define MEMFS_FMAX_BLOCKS 5
#define MEMFS_BLOCK_SIZE PAGE_SIZE
#define MEMFS_MAGIC 0xB
#define MEMFS_NAME "memfs"
#define MEMFS_NAME_SIZE 8
struct memfs_inode {
u32 inum; /* Inode number */
u32 mode; /* File permissions */
u32 owner; /* File owner */
u64 atime; /* Last access time */
u64 mtime; /* Last content modification */
u64 ctime; /* Last inode modification */
u64 size; /* Size of contents */
void *block[5]; /* Number of blocks */
};
struct memfs_superblock {
u32 magic; /* Filesystem magic number */
char name[8];
u32 blocksize; /* Filesystem block size */
u64 fmaxblocks; /* Maximum number of blocks per file */
u64 fssize; /* Total size of filesystem */
struct memfs_inode *root; /* The root of this superblock */
struct list_head inode_cache_list; /* Chain of alloc caches */
struct list_head block_cache_list; /* Chain of alloc caches */
struct id_pool *ipool; /* Index pool for inodes */
struct id_pool *bpool; /* Index pool for blocks */
struct memfs_inode *inode[MEMFS_TOTAL_INODES]; /* Table of inodes */
void *block[MEMFS_TOTAL_BLOCKS]; /* Table of fs blocks */
} __attribute__ ((__packed__));
#define MEMFS_DNAME_MAX 32
struct memfs_dentry {
u32 inum; /* Inode number */
u32 nlength; /* Name length */
u8 name[MEMFS_DNAME_MAX]; /* Name string */
};
extern struct vnode_ops memfs_vnode_operations;
extern struct superblock_ops memfs_superblock_operations;
extern struct file_ops memfs_file_operations;
extern struct memfs_superblock *memfs_superblock;
int memfs_format_filesystem(void *buffer);
struct memfs_inode *memfs_create_inode(struct memfs_superblock *sb);
void memfs_register_fstype(struct list_head *);
struct superblock *memfs_get_superblock(void *block);
int memfs_generate_superblock(void *block);
void *memfs_alloc_block(struct memfs_superblock *sb);
int memfs_free_block(struct memfs_superblock *sb, void *block);
#endif /* __MEMFS_LAYOUT_H__ */

View File

@@ -0,0 +1,7 @@
#ifndef __MEMFS_VNODE_H__
#define __MEMFS_VNODE_H__
#include <fs.h>
#endif /* __MEMFS_VNODE_H__ */

View File

@@ -1,7 +1,25 @@
/*
* Copyright (C) 2008 Bahadir Balban
*/
#ifndef __FS0_TASK_H__
#define __FS0_TASK_H__
#include <lib/idpool.h>
#include <l4/lib/list.h>
#define __TASKNAME__ "FS0"
#endif /* __FS0_TASK_H__ */
#define TASK_OFILES_MAX 32
/* Thread control block, fs0 portion */
struct tcb {
l4id_t tid;
struct list_head list;
int fd[TASK_OFILES_MAX];
struct id_pool *fdpool;
};
struct tcb *find_task(int tid);
void init_task_data(void);
#endif /* __FS0_TASK_H__ */

14
tasks/fs0/src/bdev.c Normal file
View File

@@ -0,0 +1,14 @@
/*
* This is just to allocate some memory as a block device.
*/
#include <l4/macros.h>
extern char _start_bdev[];
extern char _end_bdev[];
__attribute__((section(".data.memfs"))) char blockdevice[SZ_16MB];
void *vfs_rootdev_open(void)
{
return (void *)_start_bdev;
}

29
tasks/fs0/src/c0fs/c0fs.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* A basic unix-like read/writeable filesystem for Codezero.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <init.h>
#include <fs.h>
void sfs_read_sb(struct superblock *sb)
{
}
static void simplefs_alloc_vnode(struct vnode *)
{
}
struct file_system_type sfs_type = {
.name = "c0fs",
.magic = 1,
.flags = 0,
};
/* Registers sfs as an available filesystem type */
void sfs_register_fstype(struct list_head *fslist)
{
list_add(&sfs_type.list, fslist);
}

View File

@@ -4,10 +4,11 @@
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#ifndef __SFS_LAYOUT_H__
#define __SFS_LAYOUT_H__
#ifndef __C0FS_LAYOUT_H__
#define __C0FS_LAYOUT_H__
#include <l4lib/types.h>
#include <l4/lib/list.h>
#include <l4/macros.h>
#include <l4/config.h>
#include INC_GLUE(memory.h)
@@ -54,13 +55,20 @@
struct sfs_superblock {
u32 magic; /* Filesystem magic number */
u32 fssize; /* Total size of filesystem */
u32 szidx; /* Bitmap index size */
u64 fssize; /* Total size of filesystem */
u32 total; /* To */
u32 groupmap[]; /* Bitmap of all fs groups */
};
struct sfs_group_table {
u32 total;
u32 free;
u32 groupmap[];
};
struct sfs_inode_table {
u32 szidx;
u32 total;
u32 free;
u32 inodemap[INODE_BITMAP_SIZE];
struct sfs_inode inode[INODE_TABLE_SIZE];
};
@@ -74,7 +82,7 @@ struct sfs_inode_table {
*/
#define INODE_DIRECT_BLOCKS 5
struct sfs_inode_blocks {
u32 szidx; /* Direct array index size */
int szidx; /* Direct array index size */
unsigned long indirect;
unsigned long indirect2;
unsigned long indirect3;
@@ -86,10 +94,10 @@ struct sfs_inode {
u32 inum; /* Inode number */
u32 mode; /* File permissions */
u32 owner; /* File owner */
u32 atime; /* Last access time */
u32 mtime; /* Last content modification */
u32 ctime; /* Last inode modification */
u32 size; /* Size of contents */
u64 atime; /* Last access time */
u64 mtime; /* Last content modification */
u64 ctime; /* Last inode modification */
u64 size; /* Size of contents */
struct sfs_inode_blocks blocks;
} __attribute__ ((__packed__));
@@ -99,4 +107,7 @@ struct sfs_dentry {
u8 name[]; /* Name string */
} __attribute__ ((__packed__));
#endif /* __SFS_LAYOUT_H__ */
void sfs_register_type(struct list_head *);
#endif /* __C0FS_LAYOUT_H__ */

28
tasks/fs0/src/file.c Normal file
View File

@@ -0,0 +1,28 @@
/*
* File content tracking.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <file.h>
#include <l4/lib/list.h>
#include <l4/macros.h>
#include INC_GLUE(memory.h)
#include <stdio.h>
/* List of all in-memory files. */
struct list_head vm_file_list;
/*
* This reads contents of a file in pages, calling the fs-specific file read function to read-in
* those pages' contents.
*
* Normally this is ought to be called by mm0 when a file's pages cannot be found in the page
* cache.
*/
int generic_file_read(struct vnode *v, unsigned long pfn, unsigned long npages, void *page_buf)
{
BUG_ON(!is_page_aligned(page_buf));
return v->fops.read(v, pfn, npages, page_buf);
}

View File

@@ -3,19 +3,92 @@
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <kdata.h>
#include <fs.h>
#include <string.h>
#include <vfs.h>
#include <bdev.h>
#include <task.h>
#include <kdata.h>
#include <stdio.h>
#include <string.h>
#include <l4/lib/list.h>
#include <init.h>
#include <blkdev/blkdev.h>
#include <l4/api/errno.h>
#include <memfs/memfs.h>
void initialise(void)
struct list_head fs_type_list;
struct superblock *vfs_probe_filesystems(void *block)
{
struct block_device *bdev;
struct file_system_type *fstype;
struct superblock *sb;
request_initdata(&initdata);
vfs_probe_fs(bdev);
list_for_each_entry(fstype, &fs_type_list, list) {
/* Does the superblock match for this fs type? */
if ((sb = fstype->ops.get_superblock(block))) {
/*
* Add this to the list of superblocks this
* fs already has.
*/
list_add(&sb->list, &fstype->sblist);
return sb;
}
}
return PTR_ERR(-ENODEV);
}
/*
* Registers each available filesystem so that these can be
* used when probing superblocks on block devices.
*/
void vfs_register_filesystems(void)
{
/* Initialise fstype list */
INIT_LIST_HEAD(&fs_type_list);
/* Call per-fs registration functions */
memfs_register_fstype(&fs_type_list);
}
extern struct list_head vm_file_list;
/*
* Filesystem initialisation.
*/
int initialise(void)
{
void *rootdev_blocks;
struct superblock *root_sb;
/* Get standard init data from microkernel */
request_initdata(&initdata);
/* Register compiled-in filesystems with vfs core. */
vfs_register_filesystems();
/* Get a pointer to first block of root block device */
rootdev_blocks = vfs_rootdev_open();
/*
* Since the *only* filesystem we have is a temporary memory
* filesystem, we create it on the root device first.
*/
memfs_format_filesystem(rootdev_blocks);
INIT_LIST_HEAD(&vm_file_list);
/* Search for a filesystem on the root device */
BUG_ON(IS_ERR(root_sb = vfs_probe_filesystems(rootdev_blocks)));
/* Mount the filesystem on the root device */
vfs_mount_root(root_sb);
/* Learn about what tasks are running */
init_task_data();
/*
* Initialisation is done. From here on, we can start
* serving filesystem requests.
*/
return 0;
}

View File

@@ -4,7 +4,9 @@
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/idpool.h>
#include <kmalloc/kmalloc.h>
// #include <kmalloc/kmalloc.h> --> This requires page allocation to grow/shrink.
#include <lib/malloc.h> // --> This is a local library that statically allocates its heap.
#include <l4/macros.h>
#include INC_GLUE(memory.h)
#include <stdio.h>

View File

@@ -42,7 +42,7 @@ typedef struct _malloc /* Turbo C DJGPP */
static char *g_heap_bot, *g_kbrk, *g_heap_top;
/*****************************************************************************
*****************************************************************************/
static void dump_heap(void)
void dump_heap(void)
{
unsigned blks_used = 0, blks_free = 0;
size_t bytes_used = 0, bytes_free = 0;
@@ -204,6 +204,15 @@ create a new, free block */
}
return (char *)n + sizeof(malloc_t);
}
static inline void *kzalloc(size_t size)
{
void *buf = kmalloc(size);
memset(buf, 0, size);
return buf;
}
/*****************************************************************************
*****************************************************************************/
void kfree(void *blk)

111
tasks/fs0/src/lookup.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* Inode lookup.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <stat.h>
#include <l4/api/errno.h>
/*
* Splits the string str according to the given seperator, returns the
* first component, and modifies the str so that it points at the next
* available component (or a leading separator which can be filtered
* out on a subsequent call to this function).
*/
char *splitpath(char **str, char sep)
{
char *cursor = *str;
char *end;
/* Move forward until no seperator */
while (*cursor == sep) {
*cursor = '\0';
cursor++; /* Move to first char of component */
}
end = cursor;
while (*end != sep && *end != '\0')
end++; /* Move until end of component */
if (*end == sep) { /* if ended with separator */
*end = '\0'; /* finish component by null */
if (*(end + 1) != '\0') /* if more components after, */
*str = end + 1; /* assign beginning to str */
else
*str = end; /* else str is also depleted, give null */
} else /* if end was null, that means the end for str, too. */
*str = end;
return cursor;
}
/*
* Given a dentry that has been populated by readdir with children dentries
* and their vnodes, this itself checks all those children on that level for
* a match, but it also calls lookup, which recursively checks lower levels.
*/
struct vnode *lookup_dentry_children(struct dentry *parentdir, char *path)
{
struct dentry *childdir;
struct vnode *v;
list_for_each_entry(childdir, &parentdir->children, child)
/* Non-zero result means either found it or error occured */
if ((v = childdir->vnode->ops.lookup(childdir->vnode, path)))
return v;
/* Out of all children dentries, neither was a match, so we return 0 */
return 0;
}
/* Lookup, recursive, assuming single-mountpoint */
struct vnode *generic_vnode_lookup(struct vnode *thisnode, char *path)
{
char *component = splitpath(&path, VFS_STR_SEP);
struct dentry *d;
struct vnode *found;
int err;
/* Does this path component match with any of parent vnode's names? */
list_for_each_entry(d, &thisnode->dentries, vref) {
if (d->ops.compare(d, component)) {
/* Is this a directory? */
if (vfs_isdir(thisnode)) {
/* Are there any more path components? */
if (path)
/* Read directory contents */
if (IS_ERR(err = (int)d->vnode->ops.readdir(d->vnode, 0)))
return PTR_ERR(err);
/* Search all children one level below. */
if ((found = lookup_dentry_children(d, path)))
/* Either found, or non-zero error */
return found;
else
return thisnode;
} else { /* Its a file */
if (path) /* There's still path, but not directory */
return PTR_ERR(-ENOTDIR);
else /* No path left, found it, so return file */
return thisnode;
}
}
}
/* Not found, return nothing */
return 0;
}
int generic_dentry_compare(struct dentry *d, char *name)
{
if (!strcmp(d->name, name))
return 1;
else
return 0;
}
struct dentry_ops generic_dentry_operations = {
.compare = generic_dentry_compare,
};

136
tasks/fs0/src/memfs/file.c Normal file
View File

@@ -0,0 +1,136 @@
/*
* Memfs file operations
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <file.h>
#include <memfs/memfs.h>
#include <stdio.h>
#include <string.h>
#include <l4/macros.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
#if 0
/*
* FIXME: read_write() could be more layered using these functions.
*/
void *memfs_read_block(struct vnode *v, int blknum)
{
void *buf = vfs_alloc_block();
if (!buf)
return PTR_ERR(-ENOMEM);
if(!v->block[blknum])
return PTR_ERR(-EEXIST);
memcpy(buf, &v->block[blknum], v->sb->blocksize);
return buf;
}
int memfs_write_block(struct vnode *v, int blknum, void *buf)
{
if(!v->block[blknum])
return -EEXIST;
memcpy(&v->block[blknum], buf, v->sb->blocksize);
return 0;
}
#endif
/*
* Handles both read and writes since most details are common.
*/
int memfs_file_read_write(struct vnode *v, unsigned long pfn,
unsigned long npages, void *buf, int wr)
{
struct memfs_inode *i;
struct memfs_superblock *memfs_sb;
u32 blocksize;
/* Don't support different block and page sizes for now */
BUG_ON(v->sb->blocksize != PAGE_SIZE);
/* Buffer must be page aligned */
BUG_ON(!is_page_aligned(buf));
/* Low-level fs refs must be valid */
BUG_ON(!(i = v->inode));
BUG_ON(!(memfs_sb = v->sb->fs_super));
blocksize = v->sb->blocksize;
/* Check filesystem per-file size limit */
if ((pfn + npages) > memfs_sb->fmaxblocks) {
printf("%s: fslimit: Trying to %s outside maximum file range: %x-%x\n",
__FUNCTION__, (wr) ? "write" : "read", pfn, pfn + npages);
return -EINVAL; /* Same error that posix llseek returns */
}
/* Read-specific operations */
if (!wr) {
/* Check if read is beyond EOF */
if ((pfn + npages) > __pfn(v->size)) {
printf("%s: Trying to read beyond end of file: %x-%x\n",
__FUNCTION__, pfn, pfn + npages);
return -EINVAL; /* Same error that posix llseek returns */
}
/* Copy the data from inode blocks into page buffer */
for (int x = pfn, bufpage = 0; x < pfn + npages; x++, bufpage++)
memcpy(((void *)buf) + (bufpage * blocksize),
&i->block[x], blocksize);
} else { /* Write-specific operations */
/* Is the write beyond current file size? */
if (i->size < ((pfn + npages) * (blocksize))) {
unsigned long diff = pfn + npages - __pfn(i->size);
unsigned long holes;
/*
* If write is not consecutively after the currently
* last file block, the gap must be filled in by holes.
*/
if (pfn > __pfn(i->size))
holes = pfn - __pfn(i->size);
/* Allocate new blocks */
for (int x = 0; x < diff; x++)
if (!(i->block[__pfn(i->size) + x] = memfs_alloc_block(memfs_sb)))
return -ENOSPC;
/* Zero out the holes. FIXME: How do we zero out non-page-aligned bytes?` */
for (int x = 0; x < holes; x++)
memset(i->block[__pfn(i->size) + x], 0, blocksize);
/* Update size and the vnode. FIXME: How do we handle non page-aligned size */
i->size = (pfn + npages) * blocksize;
v->sb->ops->read_vnode(v->sb, v);
}
/* Copy the data from page buffer into inode blocks */
for (int x = pfn, bufpage = 0; x < pfn + npages; x++, bufpage++)
memcpy(i->block[x], ((void *)buf) + (bufpage * blocksize), blocksize);
}
return npages * blocksize;
}
int memfs_file_write(struct vnode *v, unsigned long pfn, unsigned long npages, void *buf)
{
return memfs_file_read_write(v, pfn, npages, buf, 1);
}
int memfs_file_read(struct vnode *v, unsigned long pfn, unsigned long npages, void *buf)
{
return memfs_file_read_write(v, pfn, npages, buf, 0);
}
struct file_ops memfs_file_operations = {
.read = memfs_file_read,
.write = memfs_file_write,
};

159
tasks/fs0/src/memfs/memfs.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* A simple read/writeable memory-only filesystem.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <init.h>
#include <fs.h>
#include <vfs.h>
#include <memfs/memfs.h>
#include <memfs/vnode.h>
#include <lib/idpool.h>
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
struct memfs_superblock *memfs_superblock;
/* Initialise allocation caches as part of superblock initialisation */
int memfs_init_caches(struct memfs_superblock *sb)
{
void *free_block;
struct mem_cache *block_cache;
struct mem_cache *inode_cache;
/* Use the whole filesystem space to initialise block cache */
free_block = (void *)sb + sizeof(*sb);
block_cache = mem_cache_init(free_block, sb->fssize - sizeof(*sb),
sb->blocksize, 1);
list_add(&block_cache->list, &sb->block_cache_list);
/* Allocate a block and initialise it as first inode cache */
free_block = mem_cache_alloc(block_cache);
inode_cache = mem_cache_init(free_block, sb->blocksize,
sizeof(struct memfs_inode), 0);
list_add(&inode_cache->list, &sb->inode_cache_list);
return 0;
}
/*
* Given an empty block buffer, initialises a filesystem there.
*/
int memfs_format_filesystem(void *buffer)
{
struct memfs_superblock *sb = buffer; /* Buffer is the first block */
/* Zero initialise the superblock area */
memset(sb, 0, sizeof(*sb));
/* Initialise filesystem parameters */
sb->magic = MEMFS_MAGIC;
memcpy(sb->name, MEMFS_NAME, MEMFS_NAME_SIZE);
sb->blocksize = MEMFS_BLOCK_SIZE;
sb->fmaxblocks = MEMFS_FMAX_BLOCKS;
sb->fssize = MEMFS_TOTAL_SIZE;
/* Initialise block and inode index pools */
sb->ipool = id_pool_new_init(MEMFS_TOTAL_INODES);
sb->bpool = id_pool_new_init(MEMFS_TOTAL_BLOCKS);
/* Initialise bitmap allocation lists for blocks and inodes */
INIT_LIST_HEAD(&sb->block_cache_list);
INIT_LIST_HEAD(&sb->inode_cache_list);
memfs_init_caches(sb);
/* We allocate and fix a root inode so the sb is ready for mount */
sb->root = memfs_create_inode(sb);
return 0;
}
/* Allocates a block of unused buffer */
void *memfs_alloc_block(struct memfs_superblock *sb)
{
struct mem_cache *cache;
list_for_each_entry(cache, &sb->block_cache_list, list) {
if (cache->free)
return mem_cache_zalloc(cache);
else
continue;
}
return PTR_ERR(-ENOSPC);
}
/*
* Even though on a list, block allocation is currently from a single cache.
* This frees a block back to the free buffer cache.
*/
int memfs_free_block(struct memfs_superblock *sb, void *block)
{
struct mem_cache *c, *tmp;
list_for_each_entry_safe(c, tmp, &sb->block_cache_list, list)
if (!mem_cache_free(c, block))
return 0;
else
return -EINVAL;
return -EINVAL;
}
struct superblock *memfs_get_superblock(void *block);
struct file_system_type memfs_fstype = {
.name = "memfs",
.magic = MEMFS_MAGIC,
.ops = {
.get_superblock = memfs_get_superblock,
},
};
/* Copies fs-specific superblock into generic vfs superblock */
struct superblock *memfs_fill_superblock(struct memfs_superblock *sb,
struct superblock *vfs_sb)
{
vfs_sb->fs = &memfs_fstype;
vfs_sb->ops = &memfs_superblock_operations;
vfs_sb->fs_super = sb;
vfs_sb->fssize = sb->fssize;
vfs_sb->blocksize = sb->blocksize;
/*
* Create the first vnode. Since this is memfs, root vnode is
* not read-in but dynamically created here.
*/
vfs_sb->ops->alloc_vnode(vfs_sb);
return vfs_sb;
}
/*
* Probes block buffer for a valid memfs superblock, if found,
* allocates and copies data to a vfs superblock, and returns it.
*/
struct superblock *memfs_get_superblock(void *block)
{
struct memfs_superblock *sb = block;
struct superblock *vfs_sb;
/* We don't do sanity checks here, just confirm id. */
if (!strcmp(sb->name, "memfs"))
return 0;
if (sb->magic != MEMFS_MAGIC)
return 0;
/* Allocate a vfs superblock. */
vfs_sb = vfs_alloc_superblock();
/* Fill generic sb from fs-specific sb */
return memfs_fill_superblock(sb, vfs_sb);
}
/* Registers sfs as an available filesystem type */
void memfs_register_fstype(struct list_head *fslist)
{
list_add(&memfs_fstype.list, fslist);
}

278
tasks/fs0/src/memfs/vnode.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* Inode and vnode implementation.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <memfs/memfs.h>
#include <memfs/file.h>
#include <l4/lib/list.h>
#include <l4/api/errno.h>
#include <l4/macros.h>
#include <lib/malloc.h>
#include <stdio.h>
struct memfs_inode *memfs_alloc_inode(struct memfs_superblock *sb)
{
struct mem_cache *cache;
struct memfs_inode *i;
void *free_block;
/* Ask existing inode caches for a new inode */
list_for_each_entry(cache, &sb->inode_cache_list, list) {
if (cache->free)
if (!(i = mem_cache_zalloc(cache)))
return PTR_ERR(-ENOSPC);
else
return i;
else
continue;
}
/* Ask existing block caches for a new block */
if (IS_ERR(free_block = memfs_alloc_block(sb)))
return PTR_ERR(free_block);
/* Initialise it as an inode cache */
cache = mem_cache_init(free_block, sb->blocksize,
sizeof(struct memfs_inode), 0);
list_add(&cache->list, &sb->inode_cache_list);
if (!(i = mem_cache_zalloc(cache)))
return PTR_ERR(-ENOSPC);
else
return i;
}
/*
* O(n^2) complexity but its simple, yet it would only reveal on high numbers.
*/
int memfs_free_inode(struct memfs_superblock *sb, struct memfs_inode *i)
{
struct mem_cache *c, *tmp;
list_for_each_entry_safe(c, tmp, &sb->inode_cache_list, list) {
/* Free it, if success */
if (!mem_cache_free(c, i)) {
/* If cache completely emtpy */
if (mem_cache_is_empty(c)) {
/* Free the block, too. */
list_del(&c->list);
memfs_free_block(sb, c);
}
return 0;
}
}
return -EINVAL;
}
/* Allocates *and* initialises the inode */
struct memfs_inode *memfs_create_inode(struct memfs_superblock *sb)
{
struct memfs_inode *i;
/* Allocate the inode */
if (PTR_ERR(i = memfs_alloc_inode(sb)) < 0)
return i;
/* Allocate a new inode number */
if ((i->inum = id_new(sb->ipool)) < 0)
return i;
/* Put a reference to this inode in the inode table at this index */
sb->inode[i->inum] = i;
return i;
}
/* Deallocate the inode and any other closely relevant structure */
int memfs_destroy_inode(struct memfs_superblock *sb, struct memfs_inode *i)
{
int inum = i->inum;
/* Deallocate the inode */
if (memfs_free_inode(sb, i) < 0)
return -EINVAL;
/* Deallocate the inode number */
if (id_del(sb->ipool, inum) < 0)
return -EINVAL;
/* Clear the ref in inode table */
sb->inode[inum] = 0;
return 0;
}
struct vnode *memfs_alloc_vnode(struct superblock *sb)
{
struct memfs_inode *i;
struct vnode *v;
/* Get a (pseudo-disk) memfs inode */
if (IS_ERR(i = memfs_create_inode(sb->fs_super)))
return PTR_ERR(i);
/* Get a vnode */
if (!(v = vfs_alloc_vnode()))
return PTR_ERR(-ENOMEM);
/* Associate the two together */
v->inode = i;
v->vnum = i->inum;
/* Associate memfs-specific fields with vnode */
v->ops = memfs_vnode_operations;
v->fops = memfs_file_operations;
/* Return the vnode */
return v;
}
int memfs_free_vnode(struct superblock *sb, struct vnode *v)
{
struct memfs_inode *i = v->inode;
BUG_ON(!i); /* Vnodes that come here must have valid inodes */
/* Destroy on-disk inode */
memfs_destroy_inode(sb->fs_super, i);
/* Free vnode */
vfs_free_vnode(v);
return 0;
}
/*
* Given a vnode with a valid vnum, this retrieves the corresponding
* inode from the filesystem.
*/
struct memfs_inode *memfs_read_inode(struct superblock *sb, struct vnode *v)
{
struct memfs_superblock *fssb = sb->fs_super;
BUG_ON(!fssb->inode[v->vnum]);
return fssb->inode[v->vnum];
}
/*
* Given a preallocated vnode with a valid vnum, this reads the corresponding
* inode from the filesystem and fills in the vnode's fields.
*/
int memfs_read_vnode(struct superblock *sb, struct vnode *v)
{
struct memfs_inode *i = memfs_read_inode(sb, v);
if (!i)
return -EEXIST;
/* Simply copy common fields */
v->vnum = i->inum;
v->size = i->size;
v->mode = i->mode;
v->owner = i->owner;
v->atime = i->atime;
v->mtime = i->mtime;
v->ctime = i->ctime;
return 0;
}
int memfs_write_vnode(struct superblock *sb, struct vnode *v)
{
struct memfs_inode *i = v->inode;
/* Vnodes that come here must have valid inodes */
BUG_ON(!i);
/* Simply copy common fields */
i->inum = v->vnum;
i->size = v->size;
i->mode = v->mode;
i->owner = v->owner;
i->atime = v->atime;
i->mtime = v->mtime;
i->ctime = v->ctime;
return 0;
}
/*
* Allocates and populates all dentries and their corresponding vnodes that are
* the direct children of vnode v. This means that by each call to readdir, the vfs
* layer increases its cache of filesystem tree by one level beneath that directory.
*/
void *memfs_vnode_readdir(struct vnode *v, void *dirbuf)
{
int err;
struct memfs_dentry *memfsd = dirbuf;
struct dentry *parent = list_entry(v->dentries.next, struct dentry,
vref);
/* Check directory type */
if (!vfs_isdir(v))
return PTR_ERR(-ENOTDIR);
/* Allocate dirbuf if one is not provided by the upper layer */
if (!dirbuf) {
/* This is as big as a page */
memfsd = dirbuf = vfs_alloc_dirpage();
/*
* Fail if vnode size is bigger than a page. Since this allocation
* method is to be replaced, we can live with this limitation for now.
*/
BUG_ON(v->size > PAGE_SIZE);
}
/* Read contents into the buffer */
if ((err = v->fops.read(v, 0, 1, dirbuf)))
return PTR_ERR(err);
/* For each fs-specific directory entry */
for (int i = 0; i < (v->size / sizeof(struct memfs_dentry)); i++) {
struct dentry *newd;
struct vnode *newv;
/* Allocate a vfs dentry */
if (!(newd = vfs_alloc_dentry()))
return PTR_ERR(-ENOMEM);
/* Initialise it */
newd->ops = generic_dentry_operations;
newd->parent = parent;
list_add(&newd->child, &parent->children);
/*
* Read the vnode for dentry by its vnode number.
*/
newv = newd->vnode = vfs_alloc_vnode();
newv->vnum = memfsd[i].inum;
BUG_ON(newv->sb->ops->read_vnode(newv->sb, newv) < 0);
/* Assing this dentry as a name of its vnode */
list_add(&newd->vref, &newd->vnode->dentries);
/* Copy fields into generic dentry */
memcpy(newd->name, memfsd[i].name, MEMFS_DNAME_MAX);
}
return dirbuf;
}
struct vnode_ops memfs_vnode_operations = {
.lookup = generic_vnode_lookup,
.readdir = memfs_vnode_readdir,
};
struct superblock_ops memfs_superblock_operations = {
.read_vnode = memfs_read_vnode,
.write_vnode = memfs_write_vnode,
.alloc_vnode = memfs_alloc_vnode,
.free_vnode = memfs_free_vnode,
};

View File

View File

@@ -1,17 +0,0 @@
/*
* A basic unix-like read/writeable filesystem.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
void sfs_read_sb(struct superblock *sb)
{
}
static void simplefs_alloc_vnode(struct vnode *)
{
}

95
tasks/fs0/src/syscalls.c Normal file
View File

@@ -0,0 +1,95 @@
/*
* Some syscall stubs
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4/api/errno.h>
#include <l4lib/types.h>
#include <l4lib/ipcdefs.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <vfs.h>
#include <lib/malloc.h>
#include <string.h>
#include <stdio.h>
#include <task.h>
/*
* This notifies mm0 that this is the fd that refers to this vnode number
* from now on.
*
* MM0 *also* keeps track of fd's because mm0 is a better candidate
* for handling syscalls that access file content (i.e. read/write) since
* it maintains the page cache.
*/
int send_pager_opendata(l4id_t sender, int fd, unsigned long vnum)
{
int err;
write_mr(L4SYS_ARG0, sender);
write_mr(L4SYS_ARG1, fd);
write_mr(L4SYS_ARG2, vnum);
if ((err = l4_send(PAGER_TID, L4_IPC_TAG_OPENDATA)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
return err;
}
return 0;
}
int do_open(l4id_t sender, const char *pathname, int flags, unsigned int mode)
{
struct vnode *v;
struct tcb *t;
int fd;
/* FIXME: Use strnlen */
char *pathcopy = kmalloc(strlen(pathname));
/* FIXME: Use strncpy */
memcpy(pathcopy, pathname, strlen(pathname));
/* Get the vnode */
if (IS_ERR(v = vfs_lookup(vfs_root.pivot->sb, pathcopy)))
return (int)v;
/* Get the task */
BUG_ON(!(t = find_task(sender)));
/* Get a new fd */
BUG_ON(!(fd = id_new(t->fdpool)));
/* Assign the new fd with the vnode's number */
t->fd[fd] = v->vnum;
/* Tell mm0 about opened vnode information */
BUG_ON(send_pager_opendata(sender, fd, v->vnum) < 0);
return 0;
}
/* FIXME:
* - Is it already open?
* - Allocate a copy of path string since lookup destroys it
* - Check flags and mode.
*/
int sys_open(l4id_t sender, const char *pathname, int flags, unsigned int mode)
{
return do_open(sender, pathname, flags, mode);
}
int sys_read(l4id_t sender, int fd, void *buf, int count)
{
return 0;
}
int sys_write(l4id_t sender, int fd, const void *buf, int count)
{
return 0;
}
int sys_lseek(l4id_t sender, int fd, int offset, int whence)
{
return 0;
}

107
tasks/fs0/src/task.c Normal file
View File

@@ -0,0 +1,107 @@
/*
* FS0 task data initialisation.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/lib/list.h>
#include <l4/api/errno.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/ipcdefs.h>
#include <lib/malloc.h>
#include <task.h>
struct tcb_head {
struct list_head list;
int total; /* Total threads */
} tcb_head;
struct tcb *find_task(int tid)
{
struct tcb *t;
list_for_each_entry(t, &tcb_head.list, list)
if (t->tid == tid)
return t;
return 0;
}
/*
* Asks pager to send information about currently running tasks. Since this is
* called during initialisation, there can't be that many, so we assume message
* registers are sufficient. First argument tells how many there are, the rest
* tells the tids.
*/
int receive_pager_taskdata(l4id_t *tdata)
{
int err;
/* Make the actual ipc call */
if ((err = l4_sendrecv(PAGER_TID, PAGER_TID,
L4_IPC_TAG_TASKDATA)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
return err;
}
/* Check if call itself was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Error: %d.\n", __FUNCTION__, err);
return err;
}
/* Read total number of tasks. Note already used one mr. */
if ((tdata[0] = (l4id_t)read_mr(L4SYS_ARG0)) >= MR_UNUSED_TOTAL) {
printf("%s: Error: Too many tasks to read. Won't fit in mrs.\n",
__FUNCTION__);
BUG();
}
/* Now read task ids. */
for (int i = 0; i < (int)tdata[0]; i++)
tdata[1 + i] = (l4id_t)read_mr(L4SYS_ARG1 + i);
return 0;
}
/* Allocate a task struct and initialise it */
struct tcb *create_tcb(l4id_t tid)
{
struct tcb *t;
if (!(t = kmalloc(sizeof(*t))))
return PTR_ERR(-ENOMEM);
t->tid = tid;
INIT_LIST_HEAD(&t->list);
list_add_tail(&t->list, &tcb_head.list);
tcb_head.total++;
return t;
}
int init_task_structs(l4id_t *tdata)
{
struct tcb *t;
int total = tdata[0];
for (int i = 0; i < total; i++)
if (IS_ERR(t = create_tcb(tdata[1 + i])))
return (int)t;
return 0;
}
void init_task_data(void)
{
l4id_t tdata[MR_UNUSED_TOTAL];
INIT_LIST_HEAD(&tcb_head.list);
/* Read how many tasks and tids of each */
BUG_ON(receive_pager_taskdata(tdata) < 0);
/* Initialise local task structs using this info */
init_task_structs(tdata);
}

36
tasks/fs0/src/vfs.c Normal file
View File

@@ -0,0 +1,36 @@
/*
* High-level vfs implementation.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
struct vnode *vfs_lookup(struct superblock *sb, char *path)
{
/* If it's just / we already got it. */
if (!strcmp(path, "/"))
return sb->root;
return sb->root->ops.lookup(sb->root, path);
}
/*
* /
* /boot
* /boot/images/mm0.axf
* /boot/images/fs0.axf
* /boot/images/test0.axf
* /file.txt
*/
struct vfs_mountpoint vfs_root;
int vfs_mount_root(struct superblock *sb)
{
/* Lookup the root vnode of this superblock */
vfs_root.pivot = vfs_lookup(sb, "/");
vfs_root.sb = sb;
return 0;
}