diff --git a/README b/README index 5a6242a..6f0872c 100644 --- a/README +++ b/README @@ -12,7 +12,7 @@ tasks that implement memory management, a virtual filesystem layer, and these servers currently support a small but essential subset of the POSIX API. Codezero project is an effort to implement a modern, open source operating -system based on the latest technology in microkernel design. It targets +system based on the latest technology in microkernel and OS design. It targets realtime, high-end embedded systems and has an emphasis on the ARM architecture. It is quite common to see open source projects developed in a closed-doors @@ -23,13 +23,32 @@ the Linux Kernel. Why the name Codezero? -The project focuses on simplicity, elegance, cleanliness, which are assets -usually hard to achieve in software engineering. The philosophy is to implement -a structurally complete system with most modern and complex operating system -features while retaining simplicity in the implementation as much as possible. -Avoidance of code bloat is a short way to put it. This objective also fits well -with embedded platforms, which usually have rigorous memory and performance -requirements. +The project focuses on simplicity, elegance, cleanliness, which are important +assets in software engineering. The philosophy is to implement a structurally +complete system with most modern OS features while retaining simplicity in the +implementation as much as possible. This objective also fits well with embedded +platforms, which usually have rigorous memory and performance requirements. Also +the project is written from scratch, so the name emphasises that. + + +Design & Features: + +Codezero microkernel: Based on L4 microkernel principles, there are only a few +system calls and the microkernel provides purely mechanism; threads and address +spaces, and the methods of inter-process communication between them. Anything +beyond these are policy and they are implemented in the userspace. Due to this +rigorously simple design the microkernel can be used to design completely +different operating systems. In terms of other features, the microkernel is +preemptive, and smp-ready. Currently only synchronous communication is +implemented, but this will change in the near future. + +MM0: Implements memory management. It contains memory and page allocators. It +implements demand paging by managing page faults, physical pages and their +file/task associations. + +FS0: Implements a simple, modern virtual filesystem layer. Since it abstracts +the low-level filesystem details, it is a relatively easy job to port a new +filesystem to be used under FS0. License: @@ -51,28 +70,32 @@ Why yet another Posix (micro) kernel? There are many open source Posix operating systems with advanced features such as *BSD and Linux. However these were originally not designed for embedded systems. Unix itself and all the tools built upon weren't meant for using on -small devices. Besides, these operating systems naturally contain a lot of -historical code. Linux is well established, and targets a broad range of -platforms and uses, but consequently embedded platforms don't always get enough -emphasis. Also such well established, mature systems tend to oppose major design -overhauls, which limits innovation to a certain extent. In addition, their code -base is so big, that it gets more and more difficult to understand how the -system works. Usually %95 of the code is irrelevant to the problem, in case of -embedded systems. Codezero is written from scratch to solely target embedded -systems and as such the source code is %100 relevant. It is small and free from -legacy code. Finally monolithic kernels may have issues with dependability due -to much of the code sharing the same address space. Being a microkernel design, -Codezero aims to defeat this problem and increase dependability. +small devices. Accordingly, these operating systems contain a lot of historical +code. Linux is well established, and targets a broad range of platforms and +uses, but consequently embedded platforms don't always get enough emphasis. Also +such well established, mature systems tend to oppose major design overhauls, +which limits innovation to a certain extent. In addition, their code base is so +big, that it gets more and more difficult to understand how the system works. +Usually much of the code is irrelevant to the problem, in case of embedded +systems. Codezero is written from scratch to solely target embedded systems and +as such the source code is %100 relevant. It is small and free from legacy code. +Finally monolithic kernels may have issues with dependability due to much of the +code sharing the same address space. Being a microkernel design, Codezero aims +to defeat this problem and increase dependability. Other than these modern kernels, there is systems software targeting embedded devices. Most of them are proprietary, with their own users. Some of the open source ones are structurally too simplistic, and lack modern features such as -paging. There are certainly existing well-designed embedded operating systems, -but Codezero provides an alternative that will follow the open source -development principles more closely. Finally, there are new ideas in OS -literature that would improve Unix but aren't implemented either because they -have no existing users or may break compatibility somewhat. (E.g. some are -presented in Plan 9, or ReiserFS 4). As well as practising realistic development -methodologies, Codezero project aims to keep up with the latest OS literature -and provide the opportunity to incorporate the latest ideas in OS technology. +paging. There are existing well-designed embedded OS'es, but Codezero provides +an alternative that will follow the open source development principles more +closely. This will prove useful because many embedded systems still use older +development methods and the right open source methodology would prove favorable +in the fast-paced nature of development (Linux has already proven that). + +Finally, there are new ideas in OS literature that would improve Unix but aren't +implemented either because they have no existing users or may break compatibility +somewhat (e.g. some are presented in Plan 9). As well as practising realistic +development methodologies, Codezero project aims to keep up with the latest OS +literature and provide the opportunity to incorporate the latest ideas in OS +technology. diff --git a/include/l4/macros.h b/include/l4/macros.h index e578ef9..4cdf050 100644 --- a/include/l4/macros.h +++ b/include/l4/macros.h @@ -88,6 +88,10 @@ #define printk printf #endif +/* Functions who may either return a pointer or an error code can use these: */ +#define PTR_ERR(x) ((void *)(x)) +#define IS_ERR(x) (((int)(x)) < 0) + /* TEST: Is this type of printk well tested? */ #define BUG() {do { \ printk("BUG in file: %s function: %s line: %d\n", \ diff --git a/loader/start.axf.S b/loader/start.axf.S index eb1089d..4837f11 100644 --- a/loader/start.axf.S +++ b/loader/start.axf.S @@ -12,5 +12,5 @@ .align 4 .global bkpt_phys_to_virt; .type bkpt_phys_to_virt, function; -.equ bkpt_phys_to_virt, 0x107118 +.equ bkpt_phys_to_virt, 0x106f88 diff --git a/src/glue/arm/init.c b/src/glue/arm/init.c index ebeb26a..222ec86 100644 --- a/src/glue/arm/init.c +++ b/src/glue/arm/init.c @@ -252,7 +252,7 @@ void switch_to_user(struct ktcb *task) void init_inittask(char *name, struct task_ids *ids) { - struct svc_image *taskimg; + struct svc_image *taskimg = 0; struct ktcb *task; int task_pages; @@ -273,10 +273,12 @@ void init_inittask(char *name, struct task_ids *ids) */ for (int i = 0; i < bootdesc->total_images; i++) { if (!strcmp(name, bootdesc->images[i].name)) { - BUG_ON(!(taskimg = &bootdesc->images[i])); + taskimg = &bootdesc->images[i]; break; } } + BUG_ON(!taskimg); + printk("\nInitialising %s.\n", name); if (taskimg->phys_start & PAGE_MASK) printk("Warning, image start address not page aligned.\n"); diff --git a/tasks/blkdev0/include/blkdev.h b/tasks/blkdev0/include/blkdev.h index 61c0506..26d6bc6 100644 --- a/tasks/blkdev0/include/blkdev.h +++ b/tasks/blkdev0/include/blkdev.h @@ -1,19 +1,27 @@ #ifndef __BLKDEV_H__ #define __BLKDEV_H__ +#include + struct block_device; struct block_device_ops { - void (*open)(struct block_device *bdev); - void (*read)(unsigned long offset, int size, void *buf); - void (*write)(unsigned long offset, int size, void *buf); - void (*read_page)(unsigned long pfn, void *buf); - void (*write_page)(unsigned long pfn, void *buf); + int (*init)(struct block_device *bdev); + int (*open)(struct block_device *bdev); + int (*read)(struct block_device *bdev, unsigned long offset, + int size, void *buf); + int (*write)(struct block_device *bdev, unsigned long offset, + int size, void *buf); + int (*read_page)(struct block_device *bdev, + unsigned long pfn, void *buf); + int (*write_page)(struct block_device *bdev, + unsigned long pfn, void *buf); }; struct block_device { char *name; - unsigned long size; + void *private; /* Low-level device specific data */ + u64 size; struct block_device_ops ops; }; diff --git a/tasks/blkdev0/include/ramdisk.h b/tasks/blkdev0/include/ramdisk.h index b41421e..e792be3 100644 --- a/tasks/blkdev0/include/ramdisk.h +++ b/tasks/blkdev0/include/ramdisk.h @@ -1,6 +1,7 @@ #ifndef __RAMDISK_H__ #define __RAMDISK_H__ +extern struct block_device ramdisk[]; extern struct block_device ramdisk[]; #endif diff --git a/tasks/blkdev0/main.c b/tasks/blkdev0/main.c index 5e6308e..6d60f5c 100644 --- a/tasks/blkdev0/main.c +++ b/tasks/blkdev0/main.c @@ -1,10 +1,59 @@ - +/* + * High-level block device handling. + * + * Copyright (C) 2007, 2008 Bahadir Balban + */ #include #include +/* + * Handles block device requests from fs0 using a combination of + * server-specific and posix shm semantics + */ +void handle_block_device_request() +{ + u32 mr[MR_UNUSED_TOTAL]; + l4id_t sender; + int err; + u32 tag; + + printf("%s: Listening requests.\n", __TASKNAME__); + + if ((err = l4_receive(L4_ANYTHREAD)) < 0) { + printf("%s: %s: IPC Error: %d. Quitting...\n", __TASKNAME__, + __FUNCTION__, err); + BUG(); + } + + /* Read conventional ipc data */ + tag = l4_get_tag(); + sender = l4_get_sender(); + + /* Read mrs not used by syslib */ + for (int i = 0; i < MR_UNUSED_TOTAL; i++) + mr[i] = read_mr(i); + + switch(tag) { + case L4_IPC_TAG_WAIT: + printf("%s: Synced with waiting thread.\n", __TASKNAME__); + break; + case L4_IPC_TAG_BLOCK_OPEN: + sys_open(sender, (void *)mr[0], (int)mr[1], (u32)mr[2]); + break; + default: + printf("%s: Unrecognised ipc tag (%d)" + "received. Ignoring.\n", __TASKNAME__, mr[MR_TAG]); + } +} + int main(void) { + /* Initialise the block devices */ init_blkdev(); + + while (1) { + handle_block_device_request(); + } return 0; } diff --git a/tasks/blkdev0/src/init.c b/tasks/blkdev0/src/init.c index d5c7d88..f353d6a 100644 --- a/tasks/blkdev0/src/init.c +++ b/tasks/blkdev0/src/init.c @@ -6,5 +6,6 @@ void init_blkdev(void) { - ramdisk.ops.open(&ramdisk); + ramdisk[0].ops.init(&ramdisk[0]); + ramdisk[1].ops.init(&ramdisk[1]); } diff --git a/tasks/blkdev0/src/ramdisk.c b/tasks/blkdev0/src/ramdisk.c index aee8aaf..8d7a11a 100644 --- a/tasks/blkdev0/src/ramdisk.c +++ b/tasks/blkdev0/src/ramdisk.c @@ -1,7 +1,7 @@ /* * A basic ramdisk implementation. * - * Copyright (C) 2007 Bahadir Balban + * Copyright (C) 2007, 2008 Bahadir Balban * * The ramdisk binary is embedded in the data section of the ramdisk device * executable. Read/writes simply occur to this area. The disk area could @@ -22,45 +22,77 @@ extern char _end_ramdisk0[]; extern char _start_ramdisk1[]; extern char _end_ramdisk1[]; +struct ramdisk_data { + u64 base; + u64 end; +}; + +struct ramdisk_data rddata[2]; + __attribute__((section(".data.sfs"))) char sfsdisk[SZ_16MB]; -static unsigned long ramdisk_base[2]; - -void ramdisk_open(struct block_device *ramdisk) +int ramdisk_init(struct block_device *ramdisk) { - ramdisk_base = (unsigned long)_start_ramdisk; - ramdisk->size = (unsigned long)_end_ramdisk - - (unsigned long)_start_ramdisk; + struct ramdisk_data *rddata = ramdisk->private; + + if (!strcmp("ramdisk0", ramdisk->name)) { + rddata->base = (u64)(unsigned long)_start_ramdisk0; + rddata->end = (u64)(unsigned long)_end_ramdisk0; + ramdisk->size = (u64)((unsigned long)_end_ramdisk0 - + (unsigned long)_start_ramdisk0); + } else if (!strcmp("ramdisk1", ramdisk->name)) { + rddata->base = (u64)(unsigned long)_start_ramdisk1; + rddata->end = (u64)(unsigned long)_end_ramdisk1; + ramdisk->size = (u64)((unsigned long)_end_ramdisk1 - + (unsigned long)_start_ramdisk1); + } else + return -1; + + return 0; } -void ramdisk_read(unsigned long offset, int size, void *buf) +int ramdisk_open(struct block_device *ramdisk) { - void *src = (void *)(ramdisk_base + offset); + return 0; +} + +int ramdisk_read(struct block_device *b, unsigned long offset, int size, void *buf) +{ + struct ramdisk_data *data = b->private; + void *src = (void *)(unsigned long)(data->base + offset); memcpy(buf, src, size); + return 0; } -void ramdisk_write(unsigned long offset, int size, void *buf) +int ramdisk_write(struct block_device *b, unsigned long offset, + int size, void *buf) { - void *dst = (void *)(ramdisk_base + offset); + struct ramdisk_data *data = b->private; + void *dst = (void *)(unsigned long)(data->base + offset); memcpy(dst, buf, size); + return 0; } -void ramdisk_readpage(unsigned long pfn, void *buf) +int ramdisk_readpage(struct block_device *b, unsigned long pfn, void *buf) { - ramdisk_read(__pfn_to_addr(pfn), PAGE_SIZE, buf); + ramdisk_read(b, __pfn_to_addr(pfn), PAGE_SIZE, buf); + return 0; } -void ramdisk_writepage(unsigned long pfn, void *buf) +int ramdisk_writepage(struct block_device *b, unsigned long pfn, void *buf) { - ramdisk_write(__pfn_to_addr(pfn), PAGE_SIZE, buf); + ramdisk_write(b, __pfn_to_addr(pfn), PAGE_SIZE, buf); + return 0; } struct block_device ramdisk[2] = { [0] = { .name = "ramdisk0", + .private = &rddata[0], .ops = { + .init = ramdisk_init, .open = ramdisk_open, .read = ramdisk_read, .write = ramdisk_write, @@ -70,7 +102,9 @@ struct block_device ramdisk[2] = { }, [1] = { .name = "ramdisk1", + .private = &rddata[1], .ops = { + .init = ramdisk_init, .open = ramdisk_open, .read = ramdisk_read, .write = ramdisk_write, diff --git a/tasks/bootdesc/SConstruct b/tasks/bootdesc/SConstruct index 591f206..7eb14cf 100644 --- a/tasks/bootdesc/SConstruct +++ b/tasks/bootdesc/SConstruct @@ -14,12 +14,12 @@ tools_root = "../../tools" kernel = join(project_root, "build/start.axf") mm0 = join(project_root, "tasks/mm0/mm0.axf") -# fs0 = join(project_root, "tasks/fs0/fs0.axf") +fs0 = join(project_root, "tasks/fs0/fs0.axf") test0 = join(project_root, "tasks/test0/test0.axf") -test1 = join(project_root, "tasks/test1/test1.axf") +#test1 = join(project_root, "tasks/test1/test1.axf") #blkdev0 = join(project_root, "tasks/fsbin/blkdev0.axf") -images = [kernel, mm0, test0, test1] +images = [kernel, mm0, fs0, test0] autogen_templ = "bootdesc.c.append" def get_image_name_start_end(image, target): diff --git a/tasks/bootdesc/bootdesc.c.orig b/tasks/bootdesc/bootdesc.c.orig deleted file mode 100644 index 6819983..0000000 --- a/tasks/bootdesc/bootdesc.c.orig +++ /dev/null @@ -1,39 +0,0 @@ - - -/* Supervisor task at load time. */ -struct svc_image { - char name[16]; - unsigned int phys_start; - unsigned int phys_end; -} __attribute__((__packed__)); - -/* Supervisor task descriptor at load time */ -struct bootdesc { - int desc_size; - int total_images; - struct svc_image images[]; -} __attribute__((__packed__)); - - -struct bootdesc bootdesc = { - .desc_size = sizeof(struct bootdesc) + sizeof(struct svc_image) * 3, - .total_images = 3, - .images = { - [0] = { - .name = "inittask", - .phys_start = 0x208000, - .phys_end = 0x213260, - }, - [1] = { - .name = "roottask", - .phys_start = 0x218000, - .phys_end = 0x21b344, - }, - [2] = { - .name = "testtask", - .phys_start = 0x220000, - .phys_end = 0x223344, - }, - - }, -}; diff --git a/tasks/fs0/MAKE_SURE_TO_SET_VFS_TID b/tasks/fs0/MAKE_SURE_TO_SET_VFS_TID deleted file mode 100644 index e69de29..0000000 diff --git a/tasks/fs0/SConstruct b/tasks/fs0/SConstruct index 79eec45..bae4e53 100644 --- a/tasks/fs0/SConstruct +++ b/tasks/fs0/SConstruct @@ -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) diff --git a/tasks/fs0/include/bdev.h b/tasks/fs0/include/bdev.h new file mode 100644 index 0000000..c85a0cd --- /dev/null +++ b/tasks/fs0/include/bdev.h @@ -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__ */ diff --git a/tasks/fs0/include/file.h b/tasks/fs0/include/file.h new file mode 100644 index 0000000..46a9b25 --- /dev/null +++ b/tasks/fs0/include/file.h @@ -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__ */ diff --git a/tasks/fs0/include/fs.h b/tasks/fs0/include/fs.h index c601597..112d3e7 100644 --- a/tasks/fs0/include/fs.h +++ b/tasks/fs0/include/fs.h @@ -1,25 +1,41 @@ /* * VFS definitions. * - * Copyright (C) 2007 Bahadir Balban. + * Copyright (C) 2007, 2008 Bahadir Balban. */ #ifndef __FS_H__ #define __FS_H__ -#include -typedef void (*dentry_op_t)(void); -typedef void (*superblock_op_t)(void); +#include +#include +#include +#include + + 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__ */ diff --git a/tasks/fs0/include/init.h b/tasks/fs0/include/init.h new file mode 100644 index 0000000..30b4104 --- /dev/null +++ b/tasks/fs0/include/init.h @@ -0,0 +1,7 @@ +#ifndef __INIT_H__ +#define __INIT_H__ + +/* FS0 initialisation */ +int initialise(void); + +#endif /* __INIT_H__ */ diff --git a/tasks/fs0/include/lib/malloc.h b/tasks/fs0/include/lib/malloc.h new file mode 100644 index 0000000..37479ff --- /dev/null +++ b/tasks/fs0/include/lib/malloc.h @@ -0,0 +1,19 @@ +#ifndef __PRIVATE_MALLOC_H__ +#define __PRIVATE_MALLOC_H__ + +#include +#include + +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__ */ diff --git a/tasks/fs0/include/lib/spinlock.h b/tasks/fs0/include/lib/spinlock.h index 7a61930..b3cc39c 100644 --- a/tasks/fs0/include/lib/spinlock.h +++ b/tasks/fs0/include/lib/spinlock.h @@ -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) { } diff --git a/tasks/fs0/include/linker.lds b/tasks/fs0/include/linker.lds index 54a31ae..0471090 100644 --- a/tasks/fs0/include/linker.lds +++ b/tasks/fs0/include/linker.lds @@ -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 = .; diff --git a/tasks/fs0/include/malloc.h b/tasks/fs0/include/malloc.h deleted file mode 100644 index 2d5b215..0000000 --- a/tasks/fs0/include/malloc.h +++ /dev/null @@ -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__ */ diff --git a/tasks/fs0/include/memfs/memfs.h b/tasks/fs0/include/memfs/memfs.h new file mode 100644 index 0000000..7d87e8d --- /dev/null +++ b/tasks/fs0/include/memfs/memfs.h @@ -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 +#include +#include +#include +#include INC_GLUE(memory.h) +#include +#include + +/* + * + * 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__ */ diff --git a/tasks/fs0/include/memfs/vnode.h b/tasks/fs0/include/memfs/vnode.h new file mode 100644 index 0000000..38e3080 --- /dev/null +++ b/tasks/fs0/include/memfs/vnode.h @@ -0,0 +1,7 @@ +#ifndef __MEMFS_VNODE_H__ +#define __MEMFS_VNODE_H__ + +#include + + +#endif /* __MEMFS_VNODE_H__ */ diff --git a/tasks/fs0/include/task.h b/tasks/fs0/include/task.h index d03c73b..f4185ae 100644 --- a/tasks/fs0/include/task.h +++ b/tasks/fs0/include/task.h @@ -1,7 +1,25 @@ +/* + * Copyright (C) 2008 Bahadir Balban + */ #ifndef __FS0_TASK_H__ #define __FS0_TASK_H__ +#include +#include + #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__ */ diff --git a/tasks/fs0/src/bdev.c b/tasks/fs0/src/bdev.c new file mode 100644 index 0000000..526c021 --- /dev/null +++ b/tasks/fs0/src/bdev.c @@ -0,0 +1,14 @@ +/* + * This is just to allocate some memory as a block device. + */ +#include + +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; +} diff --git a/tasks/fs0/src/c0fs/c0fs.c b/tasks/fs0/src/c0fs/c0fs.c new file mode 100644 index 0000000..0cdc769 --- /dev/null +++ b/tasks/fs0/src/c0fs/c0fs.c @@ -0,0 +1,29 @@ +/* + * A basic unix-like read/writeable filesystem for Codezero. + * + * Copyright (C) 2007, 2008 Bahadir Balban + */ +#include +#include + +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); +} diff --git a/tasks/fs0/src/simplefs/sfs.h b/tasks/fs0/src/c0fs/c0fs.h similarity index 78% rename from tasks/fs0/src/simplefs/sfs.h rename to tasks/fs0/src/c0fs/c0fs.h index f21a8a5..13d36fe 100644 --- a/tasks/fs0/src/simplefs/sfs.h +++ b/tasks/fs0/src/c0fs/c0fs.h @@ -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 +#include #include #include #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__ */ diff --git a/tasks/fs0/src/file.c b/tasks/fs0/src/file.c new file mode 100644 index 0000000..d9f3173 --- /dev/null +++ b/tasks/fs0/src/file.c @@ -0,0 +1,28 @@ +/* + * File content tracking. + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include +#include +#include +#include INC_GLUE(memory.h) +#include + +/* 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); +} diff --git a/tasks/fs0/src/init.c b/tasks/fs0/src/init.c index 62d0624..07a35aa 100644 --- a/tasks/fs0/src/init.c +++ b/tasks/fs0/src/init.c @@ -3,19 +3,92 @@ * * Copyright (C) 2007 Bahadir Balban */ -#include #include -#include +#include +#include +#include +#include #include +#include #include -#include -#include +#include +#include -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; } diff --git a/tasks/fs0/src/lib/idpool.c b/tasks/fs0/src/lib/idpool.c index 136ad56..76e5970 100644 --- a/tasks/fs0/src/lib/idpool.c +++ b/tasks/fs0/src/lib/idpool.c @@ -4,7 +4,9 @@ * Copyright (C) 2007 Bahadir Balban */ #include -#include +// #include --> This requires page allocation to grow/shrink. +#include // --> This is a local library that statically allocates its heap. +#include #include INC_GLUE(memory.h) #include diff --git a/tasks/fs0/src/malloc.c b/tasks/fs0/src/lib/malloc.c similarity index 98% rename from tasks/fs0/src/malloc.c rename to tasks/fs0/src/lib/malloc.c index c628470..27c3829 100644 --- a/tasks/fs0/src/malloc.c +++ b/tasks/fs0/src/lib/malloc.c @@ -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) diff --git a/tasks/fs0/src/lookup.c b/tasks/fs0/src/lookup.c new file mode 100644 index 0000000..ae01faa --- /dev/null +++ b/tasks/fs0/src/lookup.c @@ -0,0 +1,111 @@ +/* + * Inode lookup. + * + * Copyright (C) 2007, 2008 Bahadir Balban + */ +#include +#include +#include +#include + +/* + * 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, +}; + diff --git a/tasks/fs0/src/memfs/file.c b/tasks/fs0/src/memfs/file.c new file mode 100644 index 0000000..558c9d4 --- /dev/null +++ b/tasks/fs0/src/memfs/file.c @@ -0,0 +1,136 @@ +/* + * Memfs file operations + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include +#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, +}; + diff --git a/tasks/fs0/src/memfs/memfs.c b/tasks/fs0/src/memfs/memfs.c new file mode 100644 index 0000000..3d38f21 --- /dev/null +++ b/tasks/fs0/src/memfs/memfs.c @@ -0,0 +1,159 @@ +/* + * A simple read/writeable memory-only filesystem. + * + * Copyright (C) 2007, 2008 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} + diff --git a/tasks/fs0/src/memfs/vnode.c b/tasks/fs0/src/memfs/vnode.c new file mode 100644 index 0000000..6532af4 --- /dev/null +++ b/tasks/fs0/src/memfs/vnode.c @@ -0,0 +1,278 @@ +/* + * Inode and vnode implementation. + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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, +}; + diff --git a/tasks/fs0/src/sb.c b/tasks/fs0/src/sb.c deleted file mode 100644 index e69de29..0000000 diff --git a/tasks/fs0/src/simplefs/sfs.c b/tasks/fs0/src/simplefs/sfs.c deleted file mode 100644 index 704f7a4..0000000 --- a/tasks/fs0/src/simplefs/sfs.c +++ /dev/null @@ -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 *) -{ - -} - - diff --git a/tasks/fs0/src/syscalls.c b/tasks/fs0/src/syscalls.c new file mode 100644 index 0000000..3b3ff19 --- /dev/null +++ b/tasks/fs0/src/syscalls.c @@ -0,0 +1,95 @@ +/* + * Some syscall stubs + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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; +} + diff --git a/tasks/fs0/src/task.c b/tasks/fs0/src/task.c new file mode 100644 index 0000000..0479a18 --- /dev/null +++ b/tasks/fs0/src/task.c @@ -0,0 +1,107 @@ +/* + * FS0 task data initialisation. + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} + diff --git a/tasks/fs0/src/vfs.c b/tasks/fs0/src/vfs.c new file mode 100644 index 0000000..997fee1 --- /dev/null +++ b/tasks/fs0/src/vfs.c @@ -0,0 +1,36 @@ +/* + * High-level vfs implementation. + * + * Copyright (C) 2008 Bahadir Balban + */ +#include +#include + +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; +} + diff --git a/tasks/libl4/include/l4lib/arch-arm/syslib.h b/tasks/libl4/include/l4lib/arch-arm/syslib.h index 2d694c4..7825156 100644 --- a/tasks/libl4/include/l4lib/arch-arm/syslib.h +++ b/tasks/libl4/include/l4lib/arch-arm/syslib.h @@ -91,14 +91,14 @@ static inline void l4_set_retval(int retval) /* Clients: * Learn result of request. - */ + */ static inline int l4_get_retval(void) { return read_mr(MR_RETURN); } /* Servers: - * Return the ipc result back to requesting task. + * Return the ipc result back to requesting task. */ static inline int l4_ipc_return(int retval) { @@ -175,5 +175,4 @@ static inline int l4_reclaim_pages(l4id_t tid) return 0; } - #endif /* __L4LIB_SYSLIB_H__ */ diff --git a/tasks/libl4/include/l4lib/arch-arm/utcb.h b/tasks/libl4/include/l4lib/arch-arm/utcb.h index eb8acb5..124f2a1 100644 --- a/tasks/libl4/include/l4lib/arch-arm/utcb.h +++ b/tasks/libl4/include/l4lib/arch-arm/utcb.h @@ -22,6 +22,7 @@ #define MR_UNUSED_START 2 /* The first mr that's not used by syslib.h */ #define MR_TOTAL 6 #define MR_UNUSED_TOTAL (MR_TOTAL - MR_UNUSED_START) +#define MR_USABLE_TOTAL MR_UNUSED_TOTAL /* Compact utcb for now! :-) */ struct utcb { diff --git a/tasks/libl4/include/l4lib/ipcdefs.h b/tasks/libl4/include/l4lib/ipcdefs.h index cd503a3..356d003 100644 --- a/tasks/libl4/include/l4lib/ipcdefs.h +++ b/tasks/libl4/include/l4lib/ipcdefs.h @@ -40,4 +40,8 @@ #define L4_IPC_TAG_CLOSE 16 #define L4_IPC_TAG_BRK 17 +/* Tags for ipc between fs0 and mm0 */ +#define L4_IPC_TAG_OPENDATA 25 +#define L4_IPC_TAG_TASKDATA 26 + #endif /* __IPCDEFS_H__ */ diff --git a/tasks/libposix/shm.c b/tasks/libposix/shm.c index 39a1061..69ded18 100644 --- a/tasks/libposix/shm.c +++ b/tasks/libposix/shm.c @@ -2,7 +2,7 @@ * This is the glue logic between posix shared memory functions * and their L4 implementation. * - * Copyright (C) 2007 Bahadir Balban + * Copyright (C) 2007, 2008 Bahadir Balban */ #include #include @@ -12,80 +12,110 @@ #include #include +#define PTR_ERR(x) ((void *)(x)) +#define IS_ERR(x) (((int)(x)) < 0) -static inline int l4_shmget(l4id_t key, int size, int shmflg) +int l4_shmget(l4id_t key, int size, int shmflg) { + int err; + write_mr(L4SYS_ARG0, key); write_mr(L4SYS_ARG1, size); write_mr(L4SYS_ARG2, shmflg); /* Call pager with shmget() request. Check ipc error. */ - if ((errno = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_SHMGET)) < 0) { - printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, errno); - return -1; + if ((err = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_SHMGET)) < 0) { + printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err); + return err; } /* Check if syscall itself was successful */ - if ((errno = l4_get_retval()) < 0) { - printf("%s: SHMGET Error: %d.\n", __FUNCTION__, errno); - return -1; + if ((err = l4_get_retval()) < 0) { + printf("%s: SHMGET Error: %d.\n", __FUNCTION__, err); + return err; } /* Obtain shmid. */ return read_mr(L4SYS_ARG0); } -static inline void *l4_shmat(l4id_t shmid, const void *shmaddr, int shmflg) +void *l4_shmat(l4id_t shmid, const void *shmaddr, int shmflg) { + int err; + write_mr(L4SYS_ARG0, shmid); write_mr(L4SYS_ARG1, (unsigned long)shmaddr); write_mr(L4SYS_ARG2, shmflg); /* Call pager with shmget() request. Check ipc error. */ - if ((errno = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_SHMAT)) < 0) { - printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, errno); - return (void *)-1; + if ((err = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_SHMAT)) < 0) { + printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err); + return PTR_ERR(err); } /* Check if syscall itself was successful */ - if ((errno = l4_get_retval()) < 0) { - printf("%s: SHMAT Error: %d.\n", __FUNCTION__, errno); - return (void *)-1; + if ((err = l4_get_retval()) < 0) { + printf("%s: SHMAT Error: %d.\n", __FUNCTION__, err); + return PTR_ERR(err); } /* Obtain shm base. */ return (void *)read_mr(L4SYS_ARG0); } -static inline int l4_shmdt(const void *shmaddr) +int l4_shmdt(const void *shmaddr) { + int err; + write_mr(L4SYS_ARG0, (unsigned long)shmaddr); /* Call pager with shmget() request. Check ipc error. */ - if ((errno = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_SHMDT)) < 0) { - printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, errno); + if ((err = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_SHMDT)) < 0) { + printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err); return -1; } /* Check if syscall itself was successful */ - if ((errno = l4_get_retval()) < 0) { - printf("%s: SHMDT Error: %d.\n", __FUNCTION__, errno); + if ((err = l4_get_retval()) < 0) { + printf("%s: SHMDT Error: %d.\n", __FUNCTION__, err); return -1; - } return 0; } int shmget(key_t key, size_t size, int shmflg) { - return l4_shmget(key, size, shmflg); + int ret = l4_shmget(key, size, shmflg); + + /* If error, return positive error code */ + if (ret < 0) { + errno = -ret; + return -1; + } + /* else return value */ + return ret; } void *shmat(int shmid, const void *shmaddr, int shmflg) { - return l4_shmat(shmid, shmaddr, shmflg); + void *ret = l4_shmat(shmid, shmaddr, shmflg); + + /* If error, return positive error code */ + if (IS_ERR(ret)) { + errno = -((int)ret); + return PTR_ERR(-1); + } + /* else return value */ + return ret; } int shmdt(const void *shmaddr) { - return l4_shmdt(shmaddr); + int ret = l4_shmdt(shmaddr); + + /* If error, return positive error code */ + if (ret < 0) { + errno = -ret; + return -1; + } + /* else return value */ + return ret; } - diff --git a/tasks/mm0/SConstruct b/tasks/mm0/SConstruct index 4560116..106edf2 100644 --- a/tasks/mm0/SConstruct +++ b/tasks/mm0/SConstruct @@ -59,13 +59,13 @@ def get_physical_base(source, target, env): 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'], + CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror' ], 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, 'libl4', 'libmm', 'libmc', 'libkm', \ - 'gcc', libc_name], # libgcc.a - This is required for division routines. + 'gcc', libc_name], # libgcc.a - This is required for division routines. CPPFLAGS = "-D__USERSPACE__", CPPPATH = ['#include', libl4_incpath, libc_incpath, kernel_incpath, \ libmem_incpath, libposix_incpath]) diff --git a/tasks/mm0/include/task.h b/tasks/mm0/include/task.h index 4b675c5..83c3fd5 100644 --- a/tasks/mm0/include/task.h +++ b/tasks/mm0/include/task.h @@ -64,6 +64,8 @@ void init_pm(struct initdata *initdata); int start_init_tasks(struct initdata *initdata); void dump_tasks(void); +void send_task_data(l4id_t requester); + /* Used by servers that have a reference to tcbs (e.g. a pager) */ #define current ((struct ktcb *)__L4_ARM_Utcb()->usr_handle) diff --git a/tasks/mm0/main.c b/tasks/mm0/main.c index 6122790..091654d 100644 --- a/tasks/mm0/main.c +++ b/tasks/mm0/main.c @@ -63,6 +63,10 @@ void handle_requests(void) page_fault_handler(sender, (fault_kdata_t *)&mr[0]); break; + case L4_IPC_TAG_TASKDATA: + send_task_data(sender); + break; + case L4_IPC_TAG_SHMGET: { struct sys_shmget_args *args = (struct sys_shmget_args *)&mr[0]; sys_shmget(args->key, args->size, args->shmflg); diff --git a/tasks/mm0/src/mmap.c b/tasks/mm0/src/mmap.c index 5076cb4..45c2cfc 100644 --- a/tasks/mm0/src/mmap.c +++ b/tasks/mm0/src/mmap.c @@ -494,4 +494,3 @@ int sys_brk(l4id_t sender, void *ds_end) return 0; } - diff --git a/tasks/mm0/src/task.c b/tasks/mm0/src/task.c index 63351af..6ef938f 100644 --- a/tasks/mm0/src/task.c +++ b/tasks/mm0/src/task.c @@ -11,6 +11,7 @@ #include INC_GLUE(memory.h) #include #include +#include #include #include #include @@ -147,3 +148,31 @@ void init_pm(struct initdata *initdata) start_init_tasks(initdata); } +void send_task_data(l4id_t requester) +{ + struct tcb *t; + int li, err; + + if (requester != VFS_TID) { + printf("Task data is not requested by FS0, ignoring.\n"); + return; + } + + /* First word is total number of tcbs */ + write_mr(L4SYS_ARG0, tcb_head.total); + + /* Write each tcb's tid */ + li = 0; + list_for_each_entry(t, &tcb_head.list, list) { + BUG_ON(li >= MR_USABLE_TOTAL); + write_mr(L4SYS_ARG1 + li, t->tid); + li++; + } + + /* Reply */ + if ((err = l4_ipc_return(0)) < 0) { + printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err); + BUG(); + } +} + diff --git a/tasks/test0/SConstruct b/tasks/test0/SConstruct index b169b0e..358eb82 100644 --- a/tasks/test0/SConstruct +++ b/tasks/test0/SConstruct @@ -14,7 +14,7 @@ task_name = "test0" # The root directory of the repository where this file resides: project_root = "../.." tools_root = join(project_root, "tools") -prev_image = join(project_root, "tasks/mm0/mm0.axf") +prev_image = join(project_root, "tasks/fs0/fs0.axf") libs_path = join(project_root, "libs") ld_script = "include/linker.lds" physical_base_ld_script = "include/physical_base.lds" diff --git a/tools/l4-qemu b/tools/l4-qemu index 2994537..45a940d 100755 --- a/tools/l4-qemu +++ b/tools/l4-qemu @@ -1,6 +1,6 @@ cd build #arm-none-eabi-insight & -qemu-system-arm -M versatilepb -kernel final.axf -nographic -s & +qemu-system-arm -S -kernel final.axf -nographic -s -M versatilepb & sleep 0.5 arm-none-eabi-insight ; pkill qemu-system-arm #arm-none-eabi-gdb ; pkill qemu-system-arm