Moved tasks/* into containers/posix

This commit is contained in:
Bahadir Balban
2009-08-25 14:34:21 +03:00
parent 117e3482b6
commit 87e2aeaf58
516 changed files with 27 additions and 27 deletions

213
containers/.config Normal file
View File

@@ -0,0 +1,213 @@
#
# Automatically generated make config: don't edit
# Fri Jan 11 15:12:13 2008
#
# TARGET_alpha is not set
TARGET_arm=y
# TARGET_avr32 is not set
# TARGET_bfin is not set
# TARGET_cris is not set
# TARGET_e1 is not set
# TARGET_frv is not set
# TARGET_h8300 is not set
# TARGET_hppa is not set
# TARGET_i386 is not set
# TARGET_i960 is not set
# TARGET_ia64 is not set
# TARGET_m68k is not set
# TARGET_microblaze is not set
# TARGET_mips is not set
# TARGET_nios is not set
# TARGET_nios2 is not set
# TARGET_powerpc is not set
# TARGET_sh is not set
# TARGET_sh64 is not set
# TARGET_sparc is not set
# TARGET_v850 is not set
# TARGET_vax is not set
# TARGET_x86_64 is not set
#
# Target Architecture Features and Options
#
TARGET_ARCH="arm"
FORCE_OPTIONS_FOR_ARCH=y
# CONFIG_ARM_OABI is not set
CONFIG_ARM_EABI=y
CONFIG_GENERIC_ARM=y
# CONFIG_ARM610 is not set
# CONFIG_ARM710 is not set
# CONFIG_ARM7TDMI is not set
# CONFIG_ARM720T is not set
# CONFIG_ARM920T is not set
# CONFIG_ARM922T is not set
# CONFIG_ARM926T is not set
# CONFIG_ARM10T is not set
# CONFIG_ARM1136JF_S is not set
# CONFIG_ARM1176JZ_S is not set
# CONFIG_ARM1176JZF_S is not set
# CONFIG_ARM_SA110 is not set
# CONFIG_ARM_SA1100 is not set
# CONFIG_ARM_XSCALE is not set
# CONFIG_ARM_IWMMXT is not set
TARGET_SUBARCH=""
#
# Using ELF file format
#
ARCH_ANY_ENDIAN=y
ARCH_LITTLE_ENDIAN=y
# ARCH_WANTS_BIG_ENDIAN is not set
ARCH_WANTS_LITTLE_ENDIAN=y
ARCH_HAS_MMU=y
ARCH_USE_MMU=y
UCLIBC_HAS_FLOATS=y
UCLIBC_HAS_FPU=y
DO_C99_MATH=y
KERNEL_HEADERS="/opt/l4-project/tasks/libc/linux-headers/include"
HAVE_DOT_CONFIG=y
#
# General Library Settings
#
# HAVE_NO_PIC is not set
DOPIC=y
# HAVE_NO_SHARED is not set
# ARCH_HAS_NO_LDSO is not set
HAVE_SHARED=y
# FORCE_SHAREABLE_TEXT_SEGMENTS is not set
LDSO_LDD_SUPPORT=y
LDSO_CACHE_SUPPORT=y
# LDSO_PRELOAD_FILE_SUPPORT is not set
LDSO_BASE_FILENAME="ld.so"
# UCLIBC_STATIC_LDCONFIG is not set
# LDSO_RUNPATH is not set
UCLIBC_CTOR_DTOR=y
# HAS_NO_THREADS is not set
UCLIBC_HAS_THREADS=y
# PTHREADS_DEBUG_SUPPORT is not set
LINUXTHREADS_OLD=y
UCLIBC_HAS_LFS=y
# MALLOC is not set
# MALLOC_SIMPLE is not set
MALLOC_STANDARD=y
MALLOC_GLIBC_COMPAT=y
UCLIBC_DYNAMIC_ATEXIT=y
# COMPAT_ATEXIT is not set
# UCLIBC_SUSV3_LEGACY is not set
# UCLIBC_SUSV3_LEGACY_MACROS is not set
UCLIBC_HAS_SHADOW=y
# UCLIBC_HAS_PROGRAM_INVOCATION_NAME is not set
UCLIBC_HAS___PROGNAME=y
# UNIX98PTY_ONLY is not set
ASSUME_DEVPTS=y
UCLIBC_HAS_TM_EXTENSIONS=y
UCLIBC_HAS_TZ_CACHING=y
UCLIBC_HAS_TZ_FILE=y
UCLIBC_HAS_TZ_FILE_READ_MANY=y
UCLIBC_TZ_FILE_PATH="/etc/TZ"
#
# Advanced Library Settings
#
UCLIBC_PWD_BUFFER_SIZE=256
UCLIBC_GRP_BUFFER_SIZE=256
#
# Networking Support
#
# UCLIBC_HAS_IPV6 is not set
UCLIBC_HAS_RPC=y
UCLIBC_HAS_FULL_RPC=y
UCLIBC_HAS_REENTRANT_RPC=y
# UCLIBC_USE_NETLINK is not set
# UCLIBC_HAS_BSD_RES_CLOSE is not set
#
# String and Stdio Support
#
UCLIBC_HAS_STRING_GENERIC_OPT=y
UCLIBC_HAS_STRING_ARCH_OPT=y
UCLIBC_HAS_CTYPE_TABLES=y
UCLIBC_HAS_CTYPE_SIGNED=y
# UCLIBC_HAS_CTYPE_UNSAFE is not set
UCLIBC_HAS_CTYPE_CHECKED=y
# UCLIBC_HAS_CTYPE_ENFORCED is not set
# UCLIBC_HAS_WCHAR is not set
# UCLIBC_HAS_LOCALE is not set
UCLIBC_HAS_HEXADECIMAL_FLOATS=y
UCLIBC_HAS_GLIBC_CUSTOM_PRINTF=y
# USE_OLD_VFPRINTF is not set
UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS=9
UCLIBC_HAS_SCANF_GLIBC_A_FLAG=y
# UCLIBC_HAS_STDIO_BUFSIZ_NONE is not set
# UCLIBC_HAS_STDIO_BUFSIZ_256 is not set
# UCLIBC_HAS_STDIO_BUFSIZ_512 is not set
# UCLIBC_HAS_STDIO_BUFSIZ_1024 is not set
# UCLIBC_HAS_STDIO_BUFSIZ_2048 is not set
UCLIBC_HAS_STDIO_BUFSIZ_4096=y
# UCLIBC_HAS_STDIO_BUFSIZ_8192 is not set
UCLIBC_HAS_STDIO_BUILTIN_BUFFER_NONE=y
# UCLIBC_HAS_STDIO_BUILTIN_BUFFER_4 is not set
# UCLIBC_HAS_STDIO_BUILTIN_BUFFER_8 is not set
# UCLIBC_HAS_STDIO_SHUTDOWN_ON_ABORT is not set
UCLIBC_HAS_STDIO_GETC_MACRO=y
UCLIBC_HAS_STDIO_PUTC_MACRO=y
UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION=y
# UCLIBC_HAS_FOPEN_LARGEFILE_MODE is not set
UCLIBC_HAS_FOPEN_EXCLUSIVE_MODE=y
UCLIBC_HAS_GLIBC_CUSTOM_STREAMS=y
UCLIBC_HAS_PRINTF_M_SPEC=y
UCLIBC_HAS_ERRNO_MESSAGES=y
# UCLIBC_HAS_SYS_ERRLIST is not set
UCLIBC_HAS_SIGNUM_MESSAGES=y
# UCLIBC_HAS_SYS_SIGLIST is not set
UCLIBC_HAS_GNU_GETOPT=y
UCLIBC_HAS_GNU_GETSUBOPT=y
#
# Big and Tall
#
UCLIBC_HAS_REGEX=y
UCLIBC_HAS_REGEX_OLD=y
UCLIBC_HAS_FNMATCH=y
UCLIBC_HAS_FNMATCH_OLD=y
# UCLIBC_HAS_WORDEXP is not set
UCLIBC_HAS_FTW=y
UCLIBC_HAS_GLOB=y
UCLIBC_HAS_GNU_GLOB=y
#
# Library Installation Options
#
SHARED_LIB_LOADER_PREFIX="/lib"
RUNTIME_PREFIX="/"
DEVEL_PREFIX="/usr/"
#
# Security options
#
# UCLIBC_BUILD_PIE is not set
# UCLIBC_HAS_ARC4RANDOM is not set
# HAVE_NO_SSP is not set
# UCLIBC_HAS_SSP is not set
UCLIBC_BUILD_RELRO=y
UCLIBC_BUILD_NOW=y
# UCLIBC_BUILD_NOEXECSTACK is not set
#
# uClibc development/debugging options
#
CROSS_COMPILER_PREFIX="/opt/scratch3/buildroot/build_arm/staging_dir/usr/bin/arm-linux-uclibcgnueabi-"
UCLIBC_EXTRA_CFLAGS=""
# DODEBUG is not set
# DODEBUG_PT is not set
DOSTRIP=y
# DOASSERTS is not set
# SUPPORT_LD_DEBUG is not set
# SUPPORT_LD_DEBUG_EARLY is not set
# UCLIBC_MALLOC_DEBUGGING is not set
WARNINGS="-Wall"
# EXTRA_WARNINGS is not set
# DOMULTI is not set
# UCLIBC_MJN3_ONLY is not set

View File

@@ -0,0 +1,93 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Russel Winder
import os.path
import subprocess
import shutil
Import('environment', 'images')
bootdescTemplate = \
'''
/* This file is autogenerated, do not edit by hand. */
/* 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) * %s,
.total_images = %s,
.images = {
%s
},
};
'''
imageTemplate = \
''' [%s] = {
.name = "%s",
.phys_start = %s,
.phys_end = %s,
},
'''
def generateLocationData(image):
process = subprocess.Popen('tools/pyelf/readelf.py --lma-start-end ' + image.path, shell=True, stdout=subprocess.PIPE)
assert process.wait() == 0
return (process.stdout.readline().strip(), process.stdout.readline().strip().split()[1], process.stdout.readline().strip().split()[1])
def generateBootdesc(target, source, env):
'''
Extracts name, start, end information from the kernel and each svc task.
Uses this information to produce bootdesc.c
'''
with open(target[0].path, 'w' ) as f:
imagesString = ''
numberOfImages = len(source) - 1
for index in range(1, len(source)):
imagesString += imageTemplate % ((index - 1,) + generateLocationData(source[index]))
f.write(bootdescTemplate % (numberOfImages, numberOfImages, imagesString))
def relocateBootdesc(target, source, env):
name, start, end = generateLocationData(source[1])
print "arm-none-linux-gnueabi-objcopy" + " --adjust-section-vma .data=" + end + " " + source[0].path
# process = subprocess.Popen(executable='arm-none-linux-gnueabi-objcopy', args=(
# '--adjust-section-vma .data=' + end,
# source[0].path))
# assert process.wait() == 0
os.system("arm-none-linux-gnueabi-objcopy --adjust-section-vma .data=" + end + " " + source[0].path)
shutil.copyfile(source[0].path, target[0].path)
bootdescSource = environment.Command('bootdesc.c', images, generateBootdesc)
objects = environment.Object(bootdescSource)
Depends(objects, environment['configFiles'])
bootdesc = environment.Command('bootdesc.axf', environment.Program('bootdesc_intermediate', objects) + [images[1]] , relocateBootdesc)
Return('bootdesc')

View File

@@ -0,0 +1,8 @@
ENTRY(_start)
SECTIONS
{
_start = .;
.data : { *(.data) }
_end = .;
}

View File

@@ -0,0 +1,24 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Russel Winder
Import('environment', 'previousImage')
program = environment['buildTask']('fs0', Glob('*.c') + [Glob(directory + '/*.c') for directory in [ 'src', 'src/lib', 'src/lib/elf', 'src/memfs']], environment, previousImage)
Return('program')

View File

@@ -0,0 +1,23 @@
/*
* Container entry point for this task
*
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#include <posix/posix_init.h>
#include <l4lib/init.h>
#include <l4lib/utcb.h>
void main(void);
void __container_init(void)
{
/* Generic L4 thread initialisation */
__l4_init();
/* FS0 posix-service initialisation */
posix_service_init();
/* Entry to main */
main();
}

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__ */

View File

@@ -0,0 +1,6 @@
#ifndef __FS0_MM_H__
#define __FS0_MM_H__
#endif /* __FS0_MM_H__ */

View File

@@ -0,0 +1,171 @@
/*
* VFS definitions.
*
* Copyright (C) 2007, 2008 Bahadir Balban.
*/
#ifndef __FS_H__
#define __FS_H__
#include <l4/lib/list.h>
#include <l4/macros.h>
#include <l4lib/types.h>
#include <stat.h>
#include <path.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 {
int (*compare)(struct dentry *d, const 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 close;
file_op_t mmap;
file_op_t lseek;
file_op_t flush;
file_op_t fsync;
};
/* Operations that work on vnode fields and associations between vnodes */
struct vnode_ops {
vnode_op_t create;
struct vnode *(*lookup)(struct vnode *root, struct pathdata *pdata,
const char *component);
int (*readdir)(struct vnode *v);
int (*filldir)(void *buf, struct vnode *v, int count);
vnode_op_t link;
vnode_op_t unlink;
int (*mkdir)(struct vnode *parent, const char *name);
struct vnode *(*mknod)(struct vnode *parent, const char *name,
unsigned int mode);
vnode_op_t rmdir;
vnode_op_t rename;
vnode_op_t getattr;
vnode_op_t setattr;
};
struct superblock_ops {
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);
};
#define VFS_DNAME_MAX 256
struct dentry {
int refcnt;
char name[VFS_DNAME_MAX];
struct dentry *parent; /* Parent dentry */
struct link child; /* List of dentries with same parent */
struct link children; /* List of children dentries */
struct link vref; /* For vnode's dirent reference list */
struct link cache_list; /* Dentry cache reference */
struct vnode *vnode; /* The vnode associated with dentry */
struct dentry_ops ops;
};
/*
* Buffer that maintains directory content for a directory vnode. This is the
* only vnode content that fs0 maintains. All other file data is in mm0 page
* cache.
*/
struct dirbuf {
unsigned long npages;
int dirty;
u8 *buffer;
};
/* Posix-style dirent format used by userspace. Returned by sys_readdir() */
#define DIRENT_NAME_MAX 32
struct dirent {
u32 inum; /* Inode number */
u32 offset; /* Dentry offset in its buffer */
u16 rlength; /* Record length */
u8 name[DIRENT_NAME_MAX]; /* Name string */
};
struct vnode {
unsigned long vnum; /* Filesystem-wide unique vnode id */
int refcnt; /* Reference counter */
int links; /* Number of hard links */
struct superblock *sb; /* Reference to superblock */
struct vnode_ops ops; /* Operations on this vnode */
struct file_ops fops; /* File-related operations on this vnode */
struct link dentries; /* Dirents that refer to this vnode */
struct link cache_list; /* For adding the vnode to vnode cache */
struct dirbuf dirbuf; /* Only directory buffers are kept */
u32 mode; /* Permissions and vnode type */
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 */
};
/* FS0 vfs specific macros */
/* Check if directory */
#define vfs_isdir(v) S_ISDIR((v)->mode)
/* Set vnode type */
#define vfs_set_type(v, type) {v->mode &= ~S_IFMT; v->mode |= S_IFMT & 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 link list; /* Member of list of all fs types */
struct link sblist; /* List of superblocks with this type */
};
struct superblock *get_superblock(void *buf);
struct superblock {
u64 fssize;
unsigned int blocksize;
struct link 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__ */

View File

@@ -0,0 +1,13 @@
#ifndef __GLOBALS_H__
#define __GLOBALS_H__
struct global_list {
int total;
struct link list;
};
extern struct global_list global_vm_files;
extern struct global_list global_vm_objects;
extern struct global_list global_tasks;
#endif /* __GLOBALS_H__ */

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,44 @@
#ifndef __LIB_BIT_H__
#define __LIB_BIT_H__
#include <l4lib/types.h>
unsigned int __clz(unsigned int bitvector);
int find_and_set_first_free_bit(u32 *word, unsigned int lastbit);
int find_and_set_first_free_contig_bits(u32 *word, unsigned int limit,
int nbits);
int check_and_clear_bit(u32 *word, int bit);
int check_and_clear_contig_bits(u32 *word, int first, int nbits);
int check_and_set_bit(u32 *word, int bit);
/* Set */
static inline void setbit(unsigned int *w, unsigned int flags)
{
*w |= flags;
}
/* Clear */
static inline void clrbit(unsigned int *w, unsigned int flags)
{
*w &= ~flags;
}
/* Test */
static inline int tstbit(unsigned int *w, unsigned int flags)
{
return *w & flags;
}
/* Test and clear */
static inline int tstclr(unsigned int *w, unsigned int flags)
{
int res = tstbit(w, flags);
clrbit(w, flags);
return res;
}
#endif /* __LIB_BIT_H__ */

View File

@@ -0,0 +1,29 @@
#ifndef __MM0_IDPOOL_H__
#define __MM0_IDPOOL_H__
#include <lib/bit.h>
#include <l4/macros.h>
#include INC_GLUE(memory.h)
#include <string.h>
struct id_pool {
int nwords;
u32 bitmap[];
};
/* Copy one id pool to another by calculating its size */
static inline void id_pool_copy(struct id_pool *to, struct id_pool *from, int totalbits)
{
int nwords = BITWISE_GETWORD(totalbits);
memcpy(to, from, nwords * SZ_WORD + sizeof(struct id_pool));
}
struct id_pool *id_pool_new_init(int mapsize);
int id_new(struct id_pool *pool);
int id_del(struct id_pool *pool, int id);
int id_get(struct id_pool *pool, int id);
int ids_new_contiguous(struct id_pool *pool, int numids);
int ids_del_contiguous(struct id_pool *pool, int first, int numids);
#endif /* __MM0_IDPOOL_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

@@ -0,0 +1,9 @@
#ifndef __LIB_PATHSTR_H__
#define __LIB_PATHSTR_H__
char *strreverse(char *str);
char *splitpath_end(char **path, char sep);
char *splitpath(char **str, char sep);
#endif /* __LIB_PATHSTR_H__ */

View File

@@ -0,0 +1,15 @@
/*
* Fake spinlock for future multi-threaded mm0
*/
#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(struct spinlock *s) { }
static inline void spin_unlock(struct spinlock *s) { }
#endif /* __MM0_SPINLOCK_H__ */

View File

@@ -0,0 +1,17 @@
/*
* Virtual address allocation pool (for shm)
*
* Copyright (C) 2007 Bahadir Balban
*/
#ifndef __VADDR_H__
#define __VADDR_H__
#include <lib/idpool.h>
void vaddr_pool_init(struct id_pool *pool, unsigned long start,
unsigned long end);
void *vaddr_new(struct id_pool *pool, int npages);
int vaddr_del(struct id_pool *, void *vaddr, int npages);
#endif /* __VADDR_H__ */

View File

@@ -0,0 +1,48 @@
/*
* Simple linker script for userspace or svc tasks.
*
* Copyright (C) 2007 Bahadir Balban
*/
/*
* The only catch with this linker script is that everything
* is linked starting at virtual_base, and loaded starting
* at physical_base. virtual_base is the predefined region
* of virtual memory for userland applications. physical_base
* is determined at build-time, it is one of the subsequent pages
* that come after the kernel image's load area.
*/
/* USER_AREA_START, see memlayout.h */
virtual_base = 0x10000000;
__stack = (0x20000000 - 0x1000 - 8); /* First page before env/args page */
INCLUDE "include/physical_base.lds"
/* physical_base = 0x228000; */
offset = virtual_base - physical_base;
ENTRY(_start)
SECTIONS
{
. = virtual_base;
_start_text = .;
.text : AT (ADDR(.text) - offset) { crt0.o(.text) *(.text) }
/* 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)
{
. = ALIGN(4K); /* Align UTCB to page boundary */
_start_utcb = .;
*(.utcb)
_end_utcb = .;
. = ALIGN(4K);
_start_bdev = .;
*(.data.memfs)
_end_bdev = .;
*(.data)
}
.bss : AT (ADDR(.bss) - offset) { *(.bss) }
_end = .;
}

View File

@@ -0,0 +1,7 @@
#ifndef __MEMFS_FILE_H__
#define __MEMFS_FILE_H__
extern struct file_ops memfs_file_operations;
extern struct dentry_ops memfs_dentry_operations;
#endif /* __MEMFS_FILE_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_4MB
#define MEMFS_TOTAL_INODES 128
#define MEMFS_TOTAL_BLOCKS 2000
#define MEMFS_FMAX_BLOCKS 40
#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[MEMFS_FMAX_BLOCKS]; /* 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 */
unsigned long root_vnum; /* The root vnum of this superblock */
struct link inode_cache_list; /* Chain of alloc caches */
struct link 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 offset; /* Dentry offset in its buffer */
u16 rlength; /* Record 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;
int memfs_format_filesystem(void *buffer);
struct memfs_inode *memfs_create_inode(struct memfs_superblock *sb);
void memfs_register_fstype(struct link *);
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

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2008 Bahadir Balban
*
* Path lookup related information
*/
#ifndef __PATH_H__
#define __PATH_H__
#include <l4/lib/list.h>
#include <task.h>
/*
* FIXME:
* These ought to be strings and split/comparison functions should
* always use strings because formats like UTF-8 wouldn't work.
*/
#define VFS_CHAR_SEP '/'
#define VFS_STR_ROOTDIR "/"
#define VFS_STR_PARDIR ".."
#define VFS_STR_CURDIR "."
#define VFS_STR_XATDIR "...."
struct pathdata {
struct link list;
struct vnode *vstart;
};
struct pathcomp {
struct link list;
const char *str;
};
struct pathdata *pathdata_parse(const char *pathname, char *pathbuf,
struct tcb *task);
void pathdata_destroy(struct pathdata *p);
/* Destructive, i.e. unlinks those components from list */
const char *pathdata_next_component(struct pathdata *pdata);
const char *pathdata_last_component(struct pathdata *pdata);
#endif /* __PATH_H__ */

View File

@@ -0,0 +1,82 @@
#ifndef __FS0_STAT_H__
#define __FS0_STAT_H__
/* Posix definitions for file mode flags (covers type and access permissions) */
#define S_IFMT 00170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
#define O_ACCMODE 00000003
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
#define O_CREAT 00000100
#define O_EXCL 00000200
#define O_NOCTTY 00000400
#define O_TRUNC 00001000
#define O_APPEND 00002000
#define O_NONBLOCK 00004000
#define O_SYNC 00010000
#define FASYNC 00020000
#define O_DIRECT 00040000
#define O_LARGEFILE 00100000
#define O_DIRECTORY 00200000
#define O_NOFOLLOW 00400000
#define O_NOATIME 01000000
#define O_NDELAY O_NONBLOCK
/*
* Internal codezero-specific stat structure.
* This is converted to posix stat in userspace
*/
struct kstat {
u64 vnum;
u32 mode;
int links;
u16 uid;
u16 gid;
u64 size;
int blksize;
u64 atime;
u64 mtime;
u64 ctime;
};
#endif /* __FS0_STAT_H__ */

View File

@@ -0,0 +1,35 @@
/*
* System call function signatures.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#ifndef __FS0_SYSCALLS_H__
#define __FS0_SYSCALLS_H__
#include <task.h>
/* Posix calls */
int sys_open(struct tcb *sender, const char *pathname, int flags, u32 mode);
int sys_readdir(struct tcb *sender, int fd, void *buf, int count);
int sys_mkdir(struct tcb *sender, const char *pathname, unsigned int mode);
int sys_chdir(struct tcb *sender, const char *pathname);
/* Calls from pager that completes a posix call */
int pager_open_bypath(struct tcb *pager, char *pathname);
int pager_sys_open(struct tcb *sender, l4id_t opener, int fd);
int pager_sys_read(struct tcb *sender, unsigned long vnum, unsigned long f_offset,
unsigned long npages, void *pagebuf);
int pager_sys_write(struct tcb *sender, unsigned long vnum, unsigned long f_offset,
unsigned long npages, void *pagebuf);
int pager_sys_close(struct tcb *sender, l4id_t closer, int fd);
int pager_update_stats(struct tcb *sender, unsigned long vnum,
unsigned long newsize);
int pager_notify_fork(struct tcb *sender, l4id_t parentid,
l4id_t childid, unsigned long utcb_address,
unsigned int flags);
int pager_notify_exit(struct tcb *sender, l4id_t tid);
#endif /* __FS0_SYSCALLS_H__ */

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2008 Bahadir Balban
*/
#ifndef __FS0_TASK_H__
#define __FS0_TASK_H__
#include <lib/idpool.h>
#include <l4/lib/list.h>
#include <l4/api/kip.h>
#define __TASKNAME__ __VFSNAME__
#define TCB_NO_SHARING 0
#define TCB_SHARED_VM (1 << 0)
#define TCB_SHARED_FILES (1 << 1)
#define TCB_SHARED_FS (1 << 2)
#define TASK_FILES_MAX 32
struct task_fd_head {
int fd[TASK_FILES_MAX];
struct id_pool *fdpool;
int tcb_refs;
};
struct task_fs_data {
struct vnode *curdir;
struct vnode *rootdir;
int tcb_refs;
};
/* Thread control block, fs0 portion */
struct tcb {
l4id_t tid;
struct link list;
unsigned long shpage_address;
struct task_fd_head *files;
struct task_fs_data *fs_data;
};
/* Structures used when receiving new task info from pager */
struct task_data {
unsigned long tid;
unsigned long shpage_address;
};
struct task_data_head {
unsigned long total;
struct task_data tdata[];
};
struct tcb *find_task(int tid);
int init_task_data(void);
#endif /* __FS0_TASK_H__ */

View File

@@ -0,0 +1,90 @@
#ifndef __VFS_H__
#define __VFS_H__
#include <fs.h>
#include <lib/malloc.h>
#include <l4/lib/list.h>
#include <memfs/memfs.h>
#include <l4/macros.h>
#include <stdio.h>
#include <task.h>
#include <path.h>
extern struct link vnode_cache;
extern struct link dentry_cache;
/*
* This is a temporary origacement for page cache support provided by mm0.
* Normally mm0 tracks all vnode pages, but this is used to track pages in
* directory vnodes, which are normally never mapped by tasks.
*/
static inline void *vfs_alloc_dirpage(struct vnode *v)
{
/*
* Urgh, we allocate from the block cache of memfs to store generic vfs directory
* pages. This is currently the quickest we can allocate page-aligned memory.
*/
return memfs_alloc_block(v->sb->fs_super);
}
static inline void vfs_free_dirpage(struct vnode *v, void *block)
{
memfs_free_block(v->sb->fs_super, block);
}
static inline struct dentry *vfs_alloc_dentry(void)
{
struct dentry *d = kzalloc(sizeof(struct dentry));
link_init(&d->child);
link_init(&d->children);
link_init(&d->vref);
link_init(&d->cache_list);
return d;
}
static inline void vfs_free_dentry(struct dentry *d)
{
return kfree(d);
}
static inline struct vnode *vfs_alloc_vnode(void)
{
struct vnode *v = kzalloc(sizeof(struct vnode));
link_init(&v->dentries);
link_init(&v->cache_list);
return v;
}
static inline void vfs_free_vnode(struct vnode *v)
{
BUG(); /* Are the dentries freed ??? */
list_remove(&v->cache_list);
kfree(v);
}
static inline struct superblock *vfs_alloc_superblock(void)
{
struct superblock *sb = kmalloc(sizeof(struct superblock));
link_init(&sb->list);
return sb;
}
struct vfs_mountpoint {
struct superblock *sb; /* The superblock of mounted filesystem */
struct vnode *pivot; /* The dentry upon which we mount */
};
extern struct vfs_mountpoint vfs_root;
int vfs_mount_root(struct superblock *sb);
struct vnode *generic_vnode_lookup(struct vnode *thisnode, struct pathdata *p,
const char *component);
struct vnode *vfs_lookup_bypath(struct pathdata *p);
struct vnode *vfs_lookup_byvnum(struct superblock *sb, unsigned long vnum);
#endif /* __VFS_H__ */

148
containers/posix/fs0/main.c Normal file
View File

@@ -0,0 +1,148 @@
/*
* FS0. Filesystem implementation
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <stdio.h>
#include <string.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/kip.h>
#include <l4lib/utcb.h>
#include <l4lib/ipcdefs.h>
#include <fs.h>
#include <init.h>
#include <syscalls.h>
#include <task.h>
#include <posix/sys/time.h>
#include <l4/api/errno.h>
/*
* TODO:
* - Have a dentry cache searchable by name
* - Have a vnode cache searchable by vnum (and name?)
* - fs-specific readdir would read contents by page range, and add vnodes/dentries
* to their caches, while populating the directory vnode being read.
* - Have 2 vfs_lookup() flavors, one that searches a path, one that searches a vnum.
* - dirbuf is either allocated by low-level readdir, or else by a higher level, i.e.
* either high-level vfs code, or the mm0 page cache.
* - readdir provides a posix-compliant dirent structure list in dirbuf.
* - memfs dentries should be identical to posix struct dirents.
*
* ALL DONE!!! But untested.
*
* - Add mkdir
* - Add create
* - Add read/write -> This will need page cache and mm0 involvement.
*
* Done those, too. but untested.
*/
/* Synchronise with pager via a `wait' tagged ipc with destination as pager */
void wait_pager(l4id_t partner)
{
l4_send(partner, L4_IPC_TAG_SYNC);
// printf("%s: Pager synced with us.\n", __TASKNAME__);
}
void handle_fs_requests(void)
{
u32 mr[MR_UNUSED_TOTAL];
l4id_t senderid;
struct tcb *sender;
int ret;
u32 tag;
if ((ret = l4_receive(L4_ANYTHREAD)) < 0) {
printf("%s: %s: IPC Error: %d. Quitting...\n", __TASKNAME__,
__FUNCTION__, ret);
BUG();
}
/* Read conventional ipc data */
tag = l4_get_tag();
senderid = l4_get_sender();
if (!(sender = find_task(senderid))) {
l4_ipc_return(-ESRCH);
return;
}
/* Read mrs not used by syslib */
for (int i = 0; i < MR_UNUSED_TOTAL; i++)
mr[i] = read_mr(MR_UNUSED_START + i);
/* FIXME: Fix all these syscalls to read any buffer data from the caller task's utcb.
* Make sure to return -EINVAL if data is not valid. */
switch(tag) {
case L4_IPC_TAG_SYNC:
printf("%s: Synced with waiting thread.\n", __TASKNAME__);
return; /* No origy for this tag */
case L4_IPC_TAG_OPEN:
ret = sys_open(sender, (void *)mr[0], (int)mr[1], (unsigned int)mr[2]);
break;
case L4_IPC_TAG_MKDIR:
ret = sys_mkdir(sender, (const char *)mr[0], (unsigned int)mr[1]);
break;
case L4_IPC_TAG_CHDIR:
ret = sys_chdir(sender, (const char *)mr[0]);
break;
case L4_IPC_TAG_READDIR:
ret = sys_readdir(sender, (int)mr[0], (void *)mr[1], (int)mr[2]);
break;
case L4_IPC_TAG_PAGER_READ:
ret = pager_sys_read(sender, (unsigned long)mr[0], (unsigned long)mr[1],
(unsigned long)mr[2], (void *)mr[3]);
break;
case L4_IPC_TAG_PAGER_OPEN:
ret = pager_sys_open(sender, (l4id_t)mr[0], (int)mr[1]);
break;
case L4_IPC_TAG_PAGER_OPEN_BYPATH:
ret = pager_open_bypath(sender, (char *)mr[0]);
break;
case L4_IPC_TAG_PAGER_WRITE:
ret = pager_sys_write(sender, (unsigned long)mr[0], (unsigned long)mr[1],
(unsigned long)mr[2], (void *)mr[3]);
break;
case L4_IPC_TAG_PAGER_CLOSE:
ret = pager_sys_close(sender, (l4id_t)mr[0], (int)mr[1]);
break;
case L4_IPC_TAG_PAGER_UPDATE_STATS:
ret = pager_update_stats(sender, (unsigned long)mr[0],
(unsigned long)mr[1]);
break;
case L4_IPC_TAG_NOTIFY_FORK:
ret = pager_notify_fork(sender, (l4id_t)mr[0], (l4id_t)mr[1],
(unsigned long)mr[2], (unsigned int)mr[3]);
break;
case L4_IPC_TAG_NOTIFY_EXIT:
ret = pager_notify_exit(sender, (l4id_t)mr[0]);
break;
default:
printf("%s: Unrecognised ipc tag (%d) "
"received from tid: %d. Ignoring.\n", __TASKNAME__,
mr[MR_TAG], sender);
}
/* Reply */
if ((ret = l4_ipc_return(ret)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, ret);
BUG();
}
}
void main(void)
{
printf("\n%s: Started with thread id %d\n", __TASKNAME__, self_tid());
initialise();
wait_pager(PAGER_TID);
printf("%s: VFS service initialized. Listening requests.\n", __TASKNAME__);
while (1) {
handle_fs_requests();
}
}

View File

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

View File

@@ -0,0 +1,147 @@
/*
* A pseudo-filesystem for reading the in-memory
* server tasks loaded from the initial elf executable.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <fs.h>
#include <l4/lib/list.h>
#include <malloc.h>
struct dentry *bootfs_dentry_lookup(struct dentry *d, char *dname)
{
struct dentry *this;
list_foreach_struct(this, child, &d->children) {
if (this->compare(this, dname))
return this;
}
return 0;
}
struct dentry *path_lookup(struct superblock *sb, char *pathstr)
{
char *dname;
char *splitpath;
struct dentry *this, *next;
/* First dentry is root */
this = sb->root;
/* Get next path component from path string */
dname = path_next_dentry_name(pathstr);
if (!this->compare(dname))
return;
while(!(dname = path_next_dentry_name(pathstr))) {
if ((d = this->lookup(this, dname)))
return 0;
}
}
/*
* This creates a pseudo-filesystem tree from the loaded
* server elf images whose information is available from
* initdata.
*/
void bootfs_populate(struct initdata *initdata, struct superblock *sb)
{
struct bootdesc *bd = initdata->bootdesc;
struct svc_image *img;
struct dentry *d;
struct vnode *v;
struct file *f;
for (int i = 0; i < bd->total_images; i++) {
img = &bd->images[i];
d = malloc(sizeof(struct dentry));
v = malloc(sizeof(struct vnode));
f = malloc(sizeof(struct file));
/* Initialise dentry for image */
d->refcnt = 0;
d->vnode = v;
d->parent = sb->root;
strncpy(d->name, img->name, VFS_DENTRY_NAME_MAX);
link_init(&d->child);
link_init(&d->children);
list_insert(&d->child, &sb->root->children);
/* Initialise vnode for image */
v->refcnt = 0;
v->id = img->phys_start;
v->size = img->phys_end - img->phys_start;
link_init(&v->dirents);
list_insert(&d->v_ref, &v->dirents);
/* Initialise file struct for image */
f->refcnt = 0;
f->dentry = d;
img_d++;
img_vn++;
img_f++;
}
}
void bootfs_init_root(struct dentry *r)
{
struct vnode *v = r->vnode;
/* Initialise dentry for rootdir */
r->refcnt = 0;
strcpy(r->name, "");
link_init(&r->child);
link_init(&r->children);
link_init(&r->vref);
r->parent = r;
/* Initialise vnode for rootdir */
v->id = 0;
v->refcnt = 0;
link_init(&v->dirents);
link_init(&v->state_list);
list_insert(&r->vref, &v->dirents);
v->size = 0;
}
struct superblock *bootfs_init_sb(struct superblock *sb)
{
sb->root = malloc(sizeof(struct dentry));
sb->root->vnode = malloc(sizeof(struct vnode));
bootfs_init_root(&sb->root);
return sb;
}
struct superblock bootfs_sb = {
.fs = bootfs_type,
.ops = {
.read_sb = bootfs_read_sb,
.write_sb = bootfs_write_sb,
.read_vnode = bootfs_read_vnode,
.write_vnode = bootfs_write_vnode,
},
};
struct superblock *bootfs_get_sb(void)
{
bootfs_init_sb(&bootfs_sb);
return &bootfs_sb;
}
struct file_system_type bootfs_type = {
.name = "bootfs",
.magic = 0,
.get_sb = bootfs_get_sb,
};
void init_bootfs()
{
bootfs_init_sb(&bootfs_sb);
bootfs_populate(&bootfs_sb);
}

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 link *fslist)
{
list_insert(&sfs_type.list, fslist);
}

View File

@@ -0,0 +1,113 @@
/*
* The disk layout of our simple unix-like filesystem.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#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)
/*
*
* Filesystem layout:
*
* |---------------|
* | Group 0 |
* |---------------|
* | Group 1 |
* |---------------|
* | ... |
* |---------------|
* | Group n |
* |---------------|
*
*
* Group layout:
*
* |---------------|
* | Superblock |
* |---------------|
* | Inode table |
* |---------------|
* | Data blocks |
* |---------------|
*
* or
*
* |---------------|
* | Data blocks |
* |---------------|
*
*/
#define BLOCK_SIZE PAGE_SIZE
#define BLOCK_BITS PAGE_BITS
#define GROUP_SIZE SZ_8MB
#define INODE_TABLE_SIZE ((GROUP_SIZE / BLOCK_SIZE) / 2)
#define INODE_BITMAP_SIZE (INODE_TABLE_SIZE >> 5)
struct sfs_superblock {
u32 magic; /* Filesystem magic number */
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 total;
u32 free;
u32 inodemap[INODE_BITMAP_SIZE];
struct sfs_inode inode[INODE_TABLE_SIZE];
};
/*
* The purpose of an inode:
*
* 1) Uniquely identify a file or a directory.
* 2) Keep file/directory metadata.
* 3) Provide access means to file blocks/directory contents.
*/
#define INODE_DIRECT_BLOCKS 5
struct sfs_inode_blocks {
int szidx; /* Direct array index size */
unsigned long indirect;
unsigned long indirect2;
unsigned long indirect3;
unsigned long direct[];
};
struct sfs_inode {
u32 unum; /* Unit number this inode is in */
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 */
struct sfs_inode_blocks blocks;
} __attribute__ ((__packed__));
struct sfs_dentry {
u32 inum; /* Inode number */
u32 nlength; /* Name length */
u8 name[]; /* Name string */
} __attribute__ ((__packed__));
void sfs_register_type(struct link *);
#endif /* __C0FS_LAYOUT_H__ */

View File

@@ -0,0 +1,25 @@
/*
* 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>
/*
* 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

@@ -0,0 +1,91 @@
/*
* FS0 Initialisation.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <bdev.h>
#include <task.h>
#include <stdio.h>
#include <string.h>
#include <l4/lib/list.h>
#include <l4/api/errno.h>
#include <memfs/memfs.h>
struct link fs_type_list;
struct superblock *vfs_probe_filesystems(void *block)
{
struct file_system_type *fstype;
struct superblock *sb;
list_foreach_struct(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_insert(&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 */
link_init(&fs_type_list);
/* Call per-fs registration functions */
memfs_register_fstype(&fs_type_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);
/* 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);
printf("%s: Mounted memfs root filesystem.\n", __TASKNAME__);
/* 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

@@ -0,0 +1,111 @@
/*
* Bit manipulation functions.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/bit.h>
#include <l4/macros.h>
#include <l4/config.h>
#include <stdio.h>
#include INC_GLUE(memory.h)
/* Emulation of ARM's CLZ (count leading zeroes) instruction */
unsigned int __clz(unsigned int bitvector)
{
unsigned int x = 0;
while((!(bitvector & ((unsigned)1 << 31))) && (x < 32)) {
bitvector <<= 1;
x++;
}
return x;
}
int find_and_set_first_free_bit(u32 *word, unsigned int limit)
{
int success = 0;
int i;
for(i = 0; i < limit; i++) {
/* Find first unset bit */
if (!(word[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i))) {
/* Set it */
word[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
success = 1;
break;
}
}
/* Return bit just set */
if (success)
return i;
else
return -1;
}
int find_and_set_first_free_contig_bits(u32 *word, unsigned int limit,
int nbits)
{
int i = 0, first = 0, last = 0, found = 0;
/* Can't allocate more than the limit */
if (nbits > limit)
return -1;
/* This is a state machine that checks n contiguous free bits. */
while (i + nbits < limit) {
first = i;
last = i;
while (!(word[BITWISE_GETWORD(last)] & BITWISE_GETBIT(last))) {
last++;
i++;
if (last == first + nbits) {
found = 1;
break;
}
}
if (found)
break;
i++;
}
/* If found, set the bits */
if (found) {
for (int x = first; x < first + nbits; x++)
word[BITWISE_GETWORD(x)] |= BITWISE_GETBIT(x);
return first;
} else
return -1;
}
int check_and_clear_bit(u32 *word, int bit)
{
/* Check that bit was set */
if (word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit)) {
word[BITWISE_GETWORD(bit)] &= ~BITWISE_GETBIT(bit);
return 0;
} else {
printf("Trying to clear already clear bit\n");
return -1;
}
}
int check_and_clear_contig_bits(u32 *word, int first, int nbits)
{
for (int i = first; i < first + nbits; i++)
if (check_and_clear_bit(word, i) < 0)
return -1;
return 0;
}
int check_and_set_bit(u32 *word, int bit)
{
/* Check that bit was clear */
if (!(word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit))) {
word[BITWISE_GETWORD(bit)] |= BITWISE_GETBIT(bit);
return 0;
} else {
//printf("Trying to set already set bit\n");
return -1;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Used for thread and space ids.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/idpool.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>
#include <l4/api/errno.h>
struct id_pool *id_pool_new_init(int totalbits)
{
int nwords = BITWISE_GETWORD(totalbits);
struct id_pool *new = kzalloc((nwords * SZ_WORD)
+ sizeof(struct id_pool));
if (!new)
return PTR_ERR(-ENOMEM);
new->nwords = nwords;
return new;
}
int id_new(struct id_pool *pool)
{
int id = find_and_set_first_free_bit(pool->bitmap,
pool->nwords * WORD_BITS);
if (id < 0)
printf("%s: Warning! New id alloc failed\n", __FUNCTION__);
return id;
}
/* This finds n contiguous free ids, allocates and returns the first one */
int ids_new_contiguous(struct id_pool *pool, int numids)
{
int id = find_and_set_first_free_contig_bits(pool->bitmap,
pool->nwords *WORD_BITS,
numids);
if (id < 0)
printf("%s: Warning! New id alloc failed\n", __FUNCTION__);
return id;
}
/* This deletes a list of contiguous ids given the first one and number of ids */
int ids_del_contiguous(struct id_pool *pool, int first, int numids)
{
int ret;
if (pool->nwords * WORD_BITS < first + numids)
return -1;
if ((ret = check_and_clear_contig_bits(pool->bitmap, first, numids)))
printf("%s: Error: Invalid argument range.\n", __FUNCTION__);
return ret;
}
int id_del(struct id_pool *pool, int id)
{
int ret;
if (pool->nwords * WORD_BITS < id)
return -1;
if ((ret = check_and_clear_bit(pool->bitmap, id) < 0))
printf("%s: Error: Could not delete id.\n", __FUNCTION__);
return ret;
}
/* Return a specific id, if available */
int id_get(struct id_pool *pool, int id)
{
int ret;
ret = check_and_set_bit(pool->bitmap, id);
if (ret < 0)
return ret;
else
return id;
}

View File

@@ -0,0 +1,417 @@
/*****************************************************************************
Simple malloc
Chris Giese <geezer@execpc.com> http://www.execpc.com/~geezer
Release date: Oct 30, 2002
This code is public domain (no copyright).
You can do whatever you want with it.
Features:
- First-fit
- free() coalesces adjacent free blocks
- Uses variable-sized heap, enlarged with kbrk()/sbrk() function
- Does not use mmap()
- Can be easily modified to use fixed-size heap
- Works with 16- or 32-bit compilers
Build this program with either of the two main() functions, then run it.
Messages that indicate a software error will contain three asterisks (***).
*****************************************************************************/
#include <string.h> /* memcpy(), memset() */
#include <stdio.h> /* printf() */
#define _32BIT 1
/* use small (32K) heap for 16-bit compilers,
large (500K) heap for 32-bit compilers */
#if defined(_32BIT)
#define HEAP_SIZE 500000uL
#else
#define HEAP_SIZE 32768u
#endif
#define MALLOC_MAGIC 0x6D92 /* must be < 0x8000 */
typedef struct _malloc /* Turbo C DJGPP */
{
size_t size; /* 2 bytes 4 bytes */
struct _malloc *next; /* 2 bytes 4 bytes */
unsigned magic : 15; /* 2 bytes total 4 bytes total */
unsigned used : 1;
} malloc_t; /* total 6 bytes 12 bytes */
static char *g_heap_bot, *g_kbrk, *g_heap_top;
/*****************************************************************************
*****************************************************************************/
void dump_heap(void)
{
unsigned blks_used = 0, blks_free = 0;
size_t bytes_used = 0, bytes_free = 0;
malloc_t *m;
int total;
printf("===============================================\n");
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
{
printf("blk %5p: %6u bytes %s\n", m,
m->size, m->used ? "used" : "free");
if(m->used)
{
blks_used++;
bytes_used += m->size;
}
else
{
blks_free++;
bytes_free += m->size;
}
}
printf("blks: %6u used, %6u free, %6u total\n", blks_used,
blks_free, blks_used + blks_free);
printf("bytes: %6u used, %6u free, %6u total\n", bytes_used,
bytes_free, bytes_used + bytes_free);
printf("g_heap_bot=0x%p, g_kbrk=0x%p, g_heap_top=0x%p\n",
g_heap_bot, g_kbrk, g_heap_top);
total = (bytes_used + bytes_free) +
(blks_used + blks_free) * sizeof(malloc_t);
if(total != g_kbrk - g_heap_bot)
printf("*** some heap memory is not accounted for\n");
printf("===============================================\n");
}
/*****************************************************************************
POSIX sbrk() looks like this
void *sbrk(int incr);
Mine is a bit different so I can signal the calling function
if more memory than desired was allocated (e.g. in a system with paging)
If your kbrk()/sbrk() always allocates the amount of memory you ask for,
this code can be easily changed.
int brk( void *sbrk( void *kbrk(
function void *adr); int delta); int *delta);
---------------------- ------------ ------------ -------------
POSIX? yes yes NO
return value if error -1 -1 NULL
get break value . sbrk(0) int x=0; kbrk(&x);
set break value to X brk(X) sbrk(X - sbrk(0)) int x=X, y=0; kbrk(&x) - kbrk(&y);
enlarge heap by N bytes . sbrk(+N) int x=N; kbrk(&x);
shrink heap by N bytes . sbrk(-N) int x=-N; kbrk(&x);
can you tell if you're
given more memory
than you wanted? no no yes
*****************************************************************************/
static void *kbrk(int *delta)
{
static char heap[HEAP_SIZE];
/**/
char *new_brk, *old_brk;
/* heap doesn't exist yet */
if(g_heap_bot == NULL)
{
g_heap_bot = g_kbrk = heap;
g_heap_top = g_heap_bot + HEAP_SIZE;
}
new_brk = g_kbrk + (*delta);
/* too low: return NULL */
if(new_brk < g_heap_bot)
return NULL;
/* too high: return NULL */
if(new_brk >= g_heap_top)
return NULL;
/* success: adjust brk value... */
old_brk = g_kbrk;
g_kbrk = new_brk;
/* ...return actual delta... (for this sbrk(), they are the same)
(*delta) = (*delta); */
/* ...return old brk value */
return old_brk;
}
/*****************************************************************************
kmalloc() and kfree() use g_heap_bot, but not g_kbrk nor g_heap_top
*****************************************************************************/
void *kmalloc(size_t size)
{
unsigned total_size;
malloc_t *m, *n;
int delta;
if(size == 0)
return NULL;
total_size = size + sizeof(malloc_t);
/* search heap for free block (FIRST FIT) */
m = (malloc_t *)g_heap_bot;
/* g_heap_bot == 0 == NULL if heap does not yet exist */
if(m != NULL)
{
if(m->magic != MALLOC_MAGIC)
// panic("kernel heap is corrupt in kmalloc()");
{
printf("*** kernel heap is corrupt in kmalloc()\n");
return NULL;
}
for(; m->next != NULL; m = m->next)
{
if(m->used)
continue;
/* size == m->size is a perfect fit */
if(size == m->size)
m->used = 1;
else
{
/* otherwise, we need an extra sizeof(malloc_t) bytes for the header
of a second, free block */
if(total_size > m->size)
continue;
/* create a new, smaller free block after this one */
n = (malloc_t *)((char *)m + total_size);
n->size = m->size - total_size;
n->next = m->next;
n->magic = MALLOC_MAGIC;
n->used = 0;
/* reduce the size of this block and mark it used */
m->size = size;
m->next = n;
m->used = 1;
}
return (char *)m + sizeof(malloc_t);
}
}
/* use kbrk() to enlarge (or create!) heap */
delta = total_size;
n = kbrk(&delta);
/* uh-oh */
if(n == NULL)
return NULL;
if(m != NULL)
m->next = n;
n->size = size;
n->magic = MALLOC_MAGIC;
n->used = 1;
/* did kbrk() return the exact amount of memory we wanted?
cast to make "gcc -Wall -W ..." shut the hell up */
if((int)total_size == delta)
n->next = NULL;
else
{
/* it returned more than we wanted (it will never return less):
create a new, free block */
m = (malloc_t *)((char *)n + total_size);
m->size = delta - total_size - sizeof(malloc_t);
m->next = NULL;
m->magic = MALLOC_MAGIC;
m->used = 0;
n->next = m;
}
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)
{
malloc_t *m, *n;
/* get address of header */
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
if(m->magic != MALLOC_MAGIC)
// panic("attempt to kfree() block at 0x%p "
// "with bad magic value", blk);
{
printf("*** attempt to kfree() block at 0x%p "
"with bad magic value\n", blk);
return;
}
/* find this block in the heap */
n = (malloc_t *)g_heap_bot;
if(n->magic != MALLOC_MAGIC)
// panic("kernel heap is corrupt in kfree()");
{
printf("*** kernel heap is corrupt in kfree()\n");
return;
}
for(; n != NULL; n = n->next)
{
if(n == m)
break;
}
/* not found? bad pointer or no heap or something else? */
if(n == NULL)
// panic("attempt to kfree() block at 0x%p "
// "that is not in the heap", blk);
{
printf("*** attempt to kfree() block at 0x%p "
"that is not in the heap\n", blk);
return;
}
/* free the block */
m->used = 0;
/* coalesce adjacent free blocks
Hard to spell, hard to do */
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
{
while(!m->used && m->next != NULL && !m->next->used)
{
/* resize this block */
m->size += sizeof(malloc_t) + m->next->size;
/* merge with next block */
m->next = m->next->next;
}
}
}
/*****************************************************************************
*****************************************************************************/
void *krealloc(void *blk, size_t size)
{
void *new_blk;
malloc_t *m;
/* size == 0: free block */
if(size == 0)
{
if(blk != NULL)
kfree(blk);
new_blk = NULL;
}
else
{
/* allocate new block */
new_blk = kmalloc(size);
/* if allocation OK, and if old block exists, copy old block to new */
if(new_blk != NULL && blk != NULL)
{
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
if(m->magic != MALLOC_MAGIC)
// panic("attempt to krealloc() block at "
// "0x%p with bad magic value", blk);
{
printf("*** attempt to krealloc() block at "
"0x%p with bad magic value\n", blk);
return NULL;
}
/* copy minimum of old and new block sizes */
if(size > m->size)
size = m->size;
memcpy(new_blk, blk, size);
/* free the old block */
kfree(blk);
}
}
return new_blk;
}
/*****************************************************************************
*****************************************************************************/
#if 0
#include <stdlib.h> /* rand() */
#define SLOTS 17
int main(void)
{
unsigned lifetime[SLOTS];
void *blk[SLOTS];
int i, j, k;
dump_heap();
memset(lifetime, 0, sizeof(lifetime));
memset(blk, 0, sizeof(blk));
for(i = 0; i < 1000; i++)
{
printf("Pass %6u\n", i);
for(j = 0; j < SLOTS; j++)
{
/* age the block */
if(lifetime[j] != 0)
{
(lifetime[j])--;
continue;
}
/* too old; free it */
if(blk[j] != NULL)
{
kfree(blk[j]);
blk[j] = NULL;
}
/* alloc new block of random size
Note that size_t==unsigned, but kmalloc() uses integer math,
so block size must be positive integer */
#if defined(_32BIT)
k = rand() % 40960 + 1;
#else
k = rand() % 4096 + 1;
#endif
blk[j] = kmalloc(k);
if(blk[j] == NULL)
printf("failed to alloc %u bytes\n", k);
else
/* give it a random lifetime 0-20 */
lifetime[j] = rand() % 21;
}
}
/* let's see what we've wrought */
printf("\n\n");
dump_heap();
/* free everything */
for(j = 0; j < SLOTS; j++)
{
if(blk[j] != NULL)
{
kfree(blk[j]);
blk[j] = NULL;
}
(lifetime[j]) = 0;
}
/* after all that, we should have a single, unused block */
dump_heap();
return 0;
}
/*****************************************************************************
*****************************************************************************/
int main(void)
{
void *b1, *b2, *b3;
dump_heap();
b1 = kmalloc(42);
dump_heap();
b2 = kmalloc(23);
dump_heap();
b3 = kmalloc(7);
dump_heap();
b2 = krealloc(b2, 24);
dump_heap();
kfree(b1);
dump_heap();
b1 = kmalloc(5);
dump_heap();
kfree(b2);
dump_heap();
kfree(b3);
dump_heap();
kfree(b1);
dump_heap();
return 0;
}
#endif

View File

@@ -0,0 +1,92 @@
/*
* Functions to manipulate path strings.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <string.h>
#include <alloca.h>
/* Reverses a string by allocating on stack. Not super-efficient but easy. */
char *strreverse(char *str)
{
int length = strlen(str);
char *tmp = alloca(length);
strcpy(tmp, str);
for (int i = 0; i < length; i++)
str[i] = tmp[length - 1 - i];
return str;
}
/*
* 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;
}
/* Same as split path, but splits components from the end. Slow. */
char *splitpath_end(char **path, char sep)
{
char *component;
/* Reverse the string */
strreverse(*path);
/* Pick one from the start */
component = splitpath(path, sep);
/* Reverse the rest back to original. */
strreverse(*path);
/* Reverse component back to original */
strreverse(component);
return component;
}
/* Splitpath test program. Tests all 3 functions.
int main()
{
char str1[256] = "/a/b/c/d/////e/f";
char *str2 = malloc(strlen(str1) + 1);
char *comp;
strcpy(str2, str1);
comp = splitpath_end(&str2, '/');
while (*comp) {
printf("%s and %s\n", comp, str2);
comp = splitpath_end(&str2, '/');
}
}
*/

View File

@@ -0,0 +1,39 @@
/*
* This module allocates an unused virtual address range for shm segments.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/bit.h>
#include <l4/macros.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
#include <lib/vaddr.h>
#include <stdio.h>
void vaddr_pool_init(struct id_pool *pool, unsigned long start, unsigned long end)
{
pool = id_pool_new_init(__pfn(end - start));
}
void *vaddr_new(struct id_pool *pool, int npages)
{
unsigned int shm_vpfn;
if ((int)(shm_vpfn = ids_new_contiguous(pool, npages)) < 0)
return 0;
return (void *)__pfn_to_addr(shm_vpfn + SHM_AREA_START);
}
int vaddr_del(struct id_pool *pool, void *vaddr, int npages)
{
unsigned long idpfn = __pfn(page_align(vaddr) - SHM_AREA_START);
if (ids_del_contiguous(pool, idpfn, npages) < 0) {
printf("%s: Invalid address range returned to "
"virtual address pool.\n", __FUNCTION__);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,90 @@
/*
* Inode lookup.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <stat.h>
#include <l4/api/errno.h>
#include <lib/pathstr.h>
#include <path.h>
/*
* 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,
struct pathdata *pdata)
{
struct dentry *childdir;
struct vnode *v;
const char *component = pathdata_next_component(pdata);
list_foreach_struct(childdir, &parentdir->children, child)
if (IS_ERR(v = childdir->vnode->ops.lookup(childdir->vnode,
pdata, component)))
/* Means not found, continue search */
if ((int)v == -ENOENT)
continue;
else /* A real error */
return v;
else /* No error, so found it */
return v;
/* Out of all children dentries, neither was a match */
return PTR_ERR(-ENOENT);
}
/* Lookup, recursive, assuming single-mountpoint */
struct vnode *generic_vnode_lookup(struct vnode *thisnode,
struct pathdata *pdata,
const char *component)
{
struct dentry *d;
struct vnode *found;
int err;
/* Does this path component match with any of this vnode's dentries? */
list_foreach_struct(d, &thisnode->dentries, vref) {
if (d->ops.compare(d, component)) {
/* Is this a directory? */
if (vfs_isdir(thisnode)) {
/* Are there more path components? */
if (!list_empty(&pdata->list)) {
/* Read directory contents */
if ((err = d->vnode->ops.readdir(d->vnode)) < 0)
return PTR_ERR(err);
/* Search all children one level below. */
if ((found = lookup_dentry_children(d, pdata)))
/* Either found, or non-zero error */
return found;
} else
return thisnode;
} else { /* Its a file */
if (!list_empty(&pdata->list)) /* 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 PTR_ERR(-ENOENT);
}
int generic_dentry_compare(struct dentry *d, const char *name)
{
if (!strcmp(d->name, name) || !strcmp(name, VFS_STR_CURDIR))
return 1;
else
return 0;
}
struct dentry_ops generic_dentry_operations = {
.compare = generic_dentry_compare,
};

View File

@@ -0,0 +1,142 @@
/*
* 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.
*
* TODO: Think about whether to use inode or the vnode's fields (e.g. size)
* and when updated, which one is to be updated first. Normally if you use and
* update inode, then you sync vnode via read_vnode. but this is not really meant for
* this use, its meant for retrieving an unknown inode under the vnode with valid vnum.
* here we already have the inode.
*/
int memfs_file_read_write(struct vnode *v, unsigned int pfn,
unsigned int npages, void *buf, int wr)
{
struct memfs_inode *i;
struct memfs_superblock *memfs_sb;
unsigned int start, end, count;
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) {
/* Find read boundaries from expected range and file's current range */
start = pfn < __pfn(v->size) ? pfn : __pfn(v->size);
end = pfn + npages < __pfn(page_align_up(v->size))
? pfn + npages : __pfn(page_align_up(v->size));
count = end - start;
/* Copy the data from inode blocks into page buffer */
for (int x = start, bufpage = 0; x < end; x++, bufpage++)
memcpy(((void *)buf) + (bufpage * blocksize),
i->block[x], blocksize);
return (int)(count * blocksize);
} else { /* Write-specific operations */
/* Is the write beyond current file size? */
if (v->size < ((pfn + npages) * (blocksize))) {
unsigned long pagediff = pfn + npages - __pfn(v->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(v->size))
holes = pfn - __pfn(v->size);
else
holes = 0;
/* Allocate new blocks */
for (int x = 0; x < pagediff; x++)
if (!(i->block[__pfn(v->size) + x] =
memfs_alloc_block(v->sb->fs_super)))
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(v->size) + x], 0, blocksize);
}
/* 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 (int)(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,
};

View File

@@ -0,0 +1,215 @@
/*
* A simple read/writeable memory-only filesystem.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <init.h>
#include <fs.h>
#include <vfs.h>
#include <task.h>
#include <stdio.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_insert(&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_insert(&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 */
link_init(&sb->block_cache_list);
link_init(&sb->inode_cache_list);
memfs_init_caches(sb);
return 0;
}
/* Allocates a block of unused buffer */
void *memfs_alloc_block(struct memfs_superblock *sb)
{
struct mem_cache *cache;
list_foreach_struct(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_foreach_removable_struct(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,
},
};
/*
* Initialise root inode as a directory, as in the mknod() call
* but differently since root is parentless and is the parent of itself.
*/
int memfs_init_rootdir(struct superblock *sb)
{
struct memfs_superblock *msb = sb->fs_super;
struct dentry *d;
struct vnode *v;
/*
* Create the root vnode. Since this is memfs, root vnode is
* not read-in but dynamically created here. We expect this
* first vnode to have vnum = 0.
*/
v = sb->root = sb->ops->alloc_vnode(sb);
msb->root_vnum = sb->root->vnum;
BUG_ON(msb->root_vnum != 0);
/* Initialise fields */
vfs_set_type(v, S_IFDIR);
/* Allocate a new vfs dentry */
if (!(d = vfs_alloc_dentry()))
return -ENOMEM;
/*
* Initialise root dentry.
*
* NOTE: Root's parent is itself.
* Here's how it looks like in structures:
* root's parent is root. But root's child is not root.
*
* NOTE: Root has no name. This helps since splitpath
* cuts out the '/' and "" is left for root name search.
*/
strncpy(d->name, VFS_STR_ROOTDIR, VFS_DNAME_MAX);
d->ops = generic_dentry_operations;
d->parent = d;
d->vnode = v;
/* Associate dentry with its vnode */
list_insert(&d->vref, &d->vnode->dentries);
/* Add both vnode and dentry to their flat caches */
list_insert(&d->cache_list, &dentry_cache);
list_insert(&v->cache_list, &vnode_cache);
return 0;
}
/* 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;
/* We initialise the root vnode as the root directory */
memfs_init_rootdir(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;
// printf("%s: %s: Reading superblock.\n", __TASKNAME__, __FUNCTION__);
/* We don't do sanity checks here, just confirm id. */
if (strcmp(sb->name, "memfs")) {
printf("%s: Name does not match: %s\n", __FUNCTION__, sb->name);
return 0;
}
if (sb->magic != MEMFS_MAGIC) {
printf("%s: Magic number not match: %s\n", __FUNCTION__, sb->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 link *fslist)
{
/* Initialise superblock list for this fstype */
link_init(&memfs_fstype.sblist);
/* Add this fstype to list of available fstypes. */
list_insert(&memfs_fstype.list, fslist);
}

View File

@@ -0,0 +1,427 @@
/*
* 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_foreach_struct(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_insert(&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_foreach_removable_struct(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_remove(&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;
}
/* Allocates both an inode and a vnode and associates the two together */
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;
v->sb = sb;
/* Return the vnode */
return v;
}
/* Frees the inode and the corresponding vnode */
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;
}
/* Writes a valid vnode's fields back to its fs-specific inode */
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;
}
/*
* Creates ordinary files and directories at the moment. In the future,
* other file types will be added. Returns the created node.
*/
struct vnode *memfs_vnode_mknod(struct vnode *v, const char *dirname,
unsigned int mode)
{
struct dentry *d, *parent = link_to_struct(v->dentries.next,
struct dentry, vref);
struct memfs_dentry *memfsd;
struct dentry *newd;
struct vnode *newv;
int err;
/*
* Precautions to prove that parent is the *only* dentry,
* since directories can't have multiple dentries associated
* with them.
*/
BUG_ON(list_empty(&v->dentries));
BUG_ON(parent->vref.next != &v->dentries);
BUG_ON(!vfs_isdir(v));
/* Populate the children */
if ((err = v->ops.readdir(v)) < 0)
return PTR_ERR(err);
/* Check there's no existing child with same name */
list_foreach_struct(d, &parent->children, child) {
/* Does the name exist as a child? */
if(d->ops.compare(d, dirname))
return PTR_ERR(-EEXIST);
}
/* Allocate a new vnode for the new directory */
if (IS_ERR(newv = v->sb->ops->alloc_vnode(v->sb)))
return newv;
/* Initialise the vnode */
vfs_set_type(newv, mode);
/* Get the next directory entry available on the parent vnode */
if (v->dirbuf.npages * PAGE_SIZE <= v->size)
return PTR_ERR(-ENOSPC);
/* Fill in the new entry to parent directory entry */
memfsd = (struct memfs_dentry *)&v->dirbuf.buffer[v->size];
memfsd->offset = v->size;
memfsd->rlength = sizeof(*memfsd);
memfsd->inum = ((struct memfs_inode *)newv->inode)->inum;
strncpy((char *)memfsd->name, dirname, MEMFS_DNAME_MAX);
memfsd->name[MEMFS_DNAME_MAX - 1] = '\0';
/* Write the updated directory buffer back to disk block */
if ((err = v->fops.write(v, 0, 1, v->dirbuf.buffer)) < 0)
return PTR_ERR(err); /* FIXME: free all you allocated so far */
/* Update parent vnode size */
v->size += sizeof(*memfsd);
v->sb->ops->write_vnode(v->sb, v);
/* Allocate a new vfs dentry */
if (!(newd = vfs_alloc_dentry()))
return PTR_ERR(-ENOMEM);
/* Initialise it */
newd->ops = generic_dentry_operations;
newd->parent = parent;
newd->vnode = newv;
strncpy(newd->name, dirname, VFS_DNAME_MAX);
/* Associate dentry with its vnode */
list_insert(&newd->vref, &newd->vnode->dentries);
/* Associate dentry with its parent */
list_insert(&newd->child, &parent->children);
/* Add both vnode and dentry to their flat caches */
list_insert(&newd->cache_list, &dentry_cache);
list_insert(&newv->cache_list, &vnode_cache);
return newv;
}
/*
* Reads the vnode directory contents into vnode's buffer in a posix-compliant
* struct dirent format.
*
* Reading the buffer, 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.
*/
int memfs_vnode_readdir(struct vnode *v)
{
int err;
struct memfs_dentry *memfsd;
struct dentry *parent = link_to_struct(v->dentries.next,
struct dentry, vref);
/*
* Precautions to prove that parent is the *only* dentry,
* since directories can't have multiple dentries associated
* with them.
*/
BUG_ON(parent->vref.next != &v->dentries);
BUG_ON(!vfs_isdir(v));
/* If a buffer is there, it means the directory is already read */
if (v->dirbuf.buffer)
return 0;
/* This is as big as a page */
if (IS_ERR(v->dirbuf.buffer = vfs_alloc_dirpage(v))) {
printf("%s: Could not allocate dirbuf.\n", __FUNCTION__);
return (int)v->dirbuf.buffer;
}
v->dirbuf.npages = 1;
/*
* Fail if vnode size is bigger than a page. Since this allocation
* method is to be origaced, we can live with this limitation for now.
*/
BUG_ON(v->size > PAGE_SIZE);
/* Read memfsd contents into the buffer */
if ((err = v->fops.read(v, 0, 1, v->dirbuf.buffer)))
return err;
memfsd = (struct memfs_dentry *)v->dirbuf.buffer;
/* Read fs-specific directory entry into vnode and dentry caches. */
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 -ENOMEM;
/* Initialise it */
newd->ops = generic_dentry_operations;
newd->parent = parent;
list_insert(&newd->child, &parent->children);
/*
* Lookup the vnode for dentry by its vnode number. We call
* vnode_lookup_byvnum instead of directly reading it because
* this dentry might just be a link to a vnode that's already
* in the vnode cache. If it's not there, the lookup function
* allocates and reads it for us as well.
*/
newv = newd->vnode = vfs_lookup_byvnum(v->sb, memfsd[i].inum);
if (!newv) {
printf("Filesystem seems to be broken. Directory has"
"inode number: %d, but no such inode found.\n",
memfsd[i].inum);
BUG();
}
/* Assing this dentry as a name of its vnode */
list_insert(&newd->vref, &newd->vnode->dentries);
/* Increase link count */
newv->links++;
/* Copy fields into generic dentry */
memcpy(newd->name, memfsd[i].name, MEMFS_DNAME_MAX);
/* Add both vnode and dentry to their caches */
list_insert(&newd->cache_list, &dentry_cache);
list_insert(&newv->cache_list, &vnode_cache);
}
return 0;
}
/*
* Copies fs-specific dirent data into user buffer in
* generic struct dirent format.
*/
int memfs_vnode_filldir(void *userbuf, struct vnode *v, int count)
{
int nbytes;
int err;
/* Bytes to read, minimum of vnode size and count requested */
nbytes = (v->size <= count) ? v->size : count;
/* Read the dir content from fs, if haven't done so yet */
if ((err = v->ops.readdir(v)) < 0)
return err;
/* Do we have those bytes at hand? */
if (v->dirbuf.buffer && (v->dirbuf.npages * PAGE_SIZE) >= nbytes) {
/*
* Memfs does a direct copy since memfs dirent format
* is the same as generic dirent format.
*/
memcpy(userbuf, v->dirbuf.buffer, nbytes);
return nbytes;
}
return 0;
}
struct vnode_ops memfs_vnode_operations = {
.readdir = memfs_vnode_readdir,
.filldir = memfs_vnode_filldir,
.mknod = memfs_vnode_mknod,
.lookup = generic_vnode_lookup,
};
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

@@ -0,0 +1,145 @@
/*
* Path manipulation functions.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/lib/list.h>
#include <l4/api/errno.h>
#include <lib/pathstr.h>
#include <lib/malloc.h>
#include <path.h>
#include <stdio.h>
#include <fs.h>
#include <task.h>
#include <vfs.h>
const char *pathdata_next_component(struct pathdata *pdata)
{
struct pathcomp *p, *n;
const char *pathstr;
list_foreach_removable_struct(p, n, &pdata->list, list) {
list_remove(&p->list);
pathstr = p->str;
kfree(p);
return pathstr;
}
return "";
}
/* Check there's at least one element, unlink and return the last element */
const char *pathdata_last_component(struct pathdata *pdata)
{
struct pathcomp *p;
const char *pathstr;
if (!list_empty(&pdata->list)) {
p = link_to_struct(pdata->list.prev, struct pathcomp, list);
list_remove(&p->list);
pathstr = p->str;
kfree(p);
return pathstr;
}
return "";
}
/* Unlink and free all path components in pathdata, and then free pathdata */
void pathdata_destroy(struct pathdata *p)
{
struct pathcomp *c, *n;
list_foreach_removable_struct(c, n, &p->list, list) {
list_remove(&c->list);
kfree(c);
}
kfree(p);
}
void pathdata_print(struct pathdata *p)
{
struct pathcomp *comp;
printf("Extracted path is:\n");
list_foreach_struct(comp, &p->list, list)
printf("%s\n", comp->str);
}
/* Extracts all path components from pathname into more presentable form */
struct pathdata *pathdata_parse(const char *pathname,
char *pathbuf, struct tcb *task)
{
struct pathdata *pdata = kzalloc(sizeof(*pdata));
struct pathcomp *comp;
char *str;
if (!pdata)
return PTR_ERR(-ENOMEM);
/* Initialise pathdata */
link_init(&pdata->list);
strcpy(pathbuf, pathname);
/* First component is root if there's a root */
if (pathname[0] == VFS_CHAR_SEP) {
if (!(comp = kzalloc(sizeof(*comp)))) {
kfree(pdata);
return PTR_ERR(-ENOMEM);
}
link_init(&comp->list);
comp->str = VFS_STR_ROOTDIR;
list_insert_tail(&comp->list, &pdata->list);
if (task)
/* Lookup start vnode is root vnode */
pdata->vstart = task->fs_data->rootdir;
else /* If no task, we use the root mountpoint pivot vnode */
pdata->vstart = vfs_root.pivot;
/* Otherwise start from current directory */
} else {
struct dentry *curdir;
if (!(comp = kzalloc(sizeof(*comp)))) {
kfree(pdata);
return PTR_ERR(-ENOMEM);
}
link_init(&comp->list);
/* Get current dentry for this task */
curdir = link_to_struct(task->fs_data->curdir->dentries.next,
struct dentry, vref);
/* Use its name in path component */
comp->str = curdir->name;
list_insert_tail(&comp->list, &pdata->list);
/* Lookup start vnode is current dir vnode */
pdata->vstart = task->fs_data->curdir;
}
/* Add every other path component */
str = splitpath(&pathbuf, VFS_CHAR_SEP);
while(*str) {
/* Any curdir components in path are ignored. */
if (!strcmp(str, VFS_STR_CURDIR)) {
;
} else {
if (!(comp = kzalloc(sizeof(*comp)))) {
pathdata_destroy(pdata);
return PTR_ERR(-ENOMEM);
}
link_init(&comp->list);
comp->str = str;
list_insert_tail(&comp->list, &pdata->list);
}
/* Next component */
str = splitpath(&pathbuf, VFS_CHAR_SEP);
}
// pathdata_print(pdata);
return pdata;
}

View File

@@ -0,0 +1,86 @@
#include <stdio.h>
#include <block.h>
#include <l4/lib/math.h>
#include <l4/macros.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
/*
* Romfs superblock descriptor:
*
* All words are Big-Endian.
*
* Word 0: | - | r | o | m |
* Word 1: | 1 | f | s | - |
* Word 2: | Size | The number of bytes in this fs.
* Word 3: | Checksum | The checksum of first 512 bytes.
* Word 4: | Volume Name | The name of volume, padded to 16-byte boundary.
* Rest: | File Headers | The rest of the data.
*/
struct romfs_superblock {
u32 word0;
u32 word1;
u32 size;
u32 checksum;
char name[0];
};
struct romfs_inode {
unsigned long mdata_size; /* Size of metadata */
unsigned long data_offset; /* Offset of data from start of fs */
};
static u32
romfs_checksum(void *data)
{
u32 sum = 0;
u32 *ptr = data;
size >>= 2;
while (size > 0) {
sum += be32_to_cpu(*ptr++);
size--;
}
return sum;
}
int romfs_fill_super(struct superblock *sb)
{
char buf[PAGE_SIZE];
struct romfs_superblock *romfs_sb = (struct romfs_superblock *)buf;
unsigned long vroot_offset;
struct vnode *vroot;
/* Read first page from block device */
bdev_readpage(0, buf);
/* Check superblock sanity */
if (strcmp(be32_to_cpu(romfs_sb->word0), ROMFS_SB_WORD0)) {
printf("Bad magic word 0\n");
}
if (strcmp(be32_to_cpu(romfs_sb->word1), ROMFS_SB_WORD1)) {
printf("Bad magic word 1\n");
}
if (romfs_checksum(romfs_sb, min(romfs_sb->size, PAGE_SIZE))) {
printf("Bad checksum.\n");
}
/* Copy some params to generic superblock */
sb->size = be32_to_cpu(romfs_sb->size);
sb->magic = ROMFS_MAGIC;
sb->ops = romfs_ops;
/* Offset of first vnode, which is the root vnode */
vroot_offset = align_up(strnlen(romfs_sb->name, ROMFS_MAXNAME) + 1, 16);
if (!(vroot = romfs_read_vnode(s, vroot_offset))) {
printf("Error, could not get root inode.\n");
}
/* Get the dirent for this vnode */
if (!(sb->root = new_dentry(vroot))) {
printf("Error: Could not get new dentry for root vnode.\n");
}
}

View File

@@ -0,0 +1,43 @@
#ifndef __ROMFS_H__
#define __ROMFS_H__
#define ROMFS_MAGIC 0x7275
#define ROMFS_FTYPE_MSK 0xF /* File mask */
#define ROMFS_FTYPE_HRD 0 /* Hard link */
#define ROMFS_FTYPE_DIR 1 /* Directory */
#define ROMFS_FTYPE_REG 2 /* Regular file */
#define ROMFS_FTYPE_SYM 3 /* Symbolic link */
#define ROMFS_FTYPE_BLK 4 /* Block device */
#define ROMFS_FTYPE_CHR 5 /* Char device */
#define ROMFS_FTYPE_SCK 6 /* Socket */
#define ROMFS_FTYPE_FIF 7 /* FIFO */
#define ROMFS_FTYPE_EXE 8 /* Executable */
#define ROMFS_NAME_ALIGN 16 /* Alignment size of names */
#define ROMFS_SB_WORD0 "-rom"
#define ROMFS_SB_WORD1 "1fs-"
/*
* Romfs superblock descriptor:
*
* All words are Big-Endian.
*
* Word 0: | - | r | o | m |
* Word 1: | 1 | f | s | - |
* Word 2: | Size | The number of bytes in this fs.
* Word 3: | Checksum | The checksum of first 512 bytes.
* Word 4: | Volume Name | The name of volume, padded to 16-byte boundary.
* Rest: | File Headers | The rest of the data.
*/
struct romfs_superblock {
u32 word0;
u32 word1;
u32 size;
u32 checksum;
char name[0];
};
#endif /* __ROMFS_H__ */

View File

@@ -0,0 +1,61 @@
#ifndef __LINUX_ROMFS_FS_H
#define __LINUX_ROMFS_FS_H
/* The basic structures of the romfs filesystem */
#define ROMBSIZE BLOCK_SIZE
#define ROMBSBITS BLOCK_SIZE_BITS
#define ROMBMASK (ROMBSIZE-1)
#define ROMFS_MAGIC 0x7275
#define ROMFS_MAXFN 128
#define __mkw(h,l) (((h)&0x00ff)<< 8|((l)&0x00ff))
#define __mkl(h,l) (((h)&0xffff)<<16|((l)&0xffff))
#define __mk4(a,b,c,d) cpu_to_be32(__mkl(__mkw(a,b),__mkw(c,d)))
#define ROMSB_WORD0 __mk4('-','r','o','m')
#define ROMSB_WORD1 __mk4('1','f','s','-')
/* On-disk "super block" */
struct romfs_super_block {
__be32 word0;
__be32 word1;
__be32 size;
__be32 checksum;
char name[0]; /* volume name */
};
/* On disk inode */
struct romfs_inode {
__be32 next; /* low 4 bits see ROMFH_ */
__be32 spec;
__be32 size;
__be32 checksum;
char name[0];
};
#define ROMFH_TYPE 7
#define ROMFH_HRD 0
#define ROMFH_DIR 1
#define ROMFH_REG 2
#define ROMFH_SYM 3
#define ROMFH_BLK 4
#define ROMFH_CHR 5
#define ROMFH_SCK 6
#define ROMFH_FIF 7
#define ROMFH_EXEC 8
/* Alignment */
#define ROMFH_SIZE 16
#define ROMFH_PAD (ROMFH_SIZE-1)
#define ROMFH_MASK (~ROMFH_PAD)
#ifdef __KERNEL__
/* Not much now */
#endif /* __KERNEL__ */
#endif

View File

@@ -0,0 +1,528 @@
/*
* 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 <lib/pathstr.h>
#include <lib/malloc.h>
#include <string.h>
#include <stdio.h>
#include <task.h>
#include <stat.h>
#include <vfs.h>
#include <alloca.h>
#include <path.h>
#include <syscalls.h>
#define NILFD -1
/*
* This informs mm0 about an opened file descriptors.
*
* 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. MM0 is not notified about opened files
* but is rather informed when it asks to be. This avoids deadlocks by
* keeping the request flow in one way.
*/
int pager_sys_open(struct tcb *pager, l4id_t opener, int fd)
{
struct tcb *task;
struct vnode *v;
//printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
if (pager->tid != PAGER_TID)
return -EINVAL;
/* Check if such task exists */
if (!(task = find_task(opener)))
return -ESRCH;
/* Check if that fd has been opened */
if (task->files->fd[fd] == NILFD)
return -EBADF;
/* Search the vnode by that vnum */
if (IS_ERR(v = vfs_lookup_byvnum(vfs_root.pivot->sb,
task->files->fd[fd])))
return (int)v;
/*
* Write file information, they will
* be sent via the return origy.
*/
write_mr(L4SYS_ARG0, v->vnum);
write_mr(L4SYS_ARG1, v->size);
return 0;
}
/* This is called when the pager needs to open a vfs file via its path */
int pager_open_bypath(struct tcb *pager, char *pathname)
{
struct pathdata *pdata;
struct tcb *task;
struct vnode *v;
int retval;
//printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
if (pager->tid != PAGER_TID)
return -EINVAL;
/* Parse path data */
if (IS_ERR(pdata = pathdata_parse(pathname,
alloca(strlen(pathname) + 1),
task)))
return (int)pdata;
/* Search the vnode by that path */
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
retval = (int)v;
goto out;
}
/*
* Write file information, they will
* be sent via the return origy.
*/
write_mr(L4SYS_ARG0, v->vnum);
write_mr(L4SYS_ARG1, v->size);
return 0;
out:
pathdata_destroy(pdata);
return retval;
}
/* Directories only for now */
void print_vnode(struct vnode *v)
{
struct dentry *d, *c;
printf("Vnode names:\n");
list_foreach_struct(d, &v->dentries, vref) {
printf("%s\n", d->name);
printf("Children dentries:\n");
list_foreach_struct(c, &d->children, child)
printf("%s\n", c->name);
}
}
/* Creates a node under a directory, e.g. a file, directory. */
struct vnode *vfs_create(struct tcb *task, struct pathdata *pdata,
unsigned int mode)
{
struct vnode *vparent, *newnode;
const char *nodename;
/* The last component is to be created */
nodename = pathdata_last_component(pdata);
/* Check that the parent directory exists. */
if (IS_ERR(vparent = vfs_lookup_bypath(pdata)))
return vparent;
/* The parent vnode must be a directory. */
if (!vfs_isdir(vparent))
return PTR_ERR(-ENOENT);
/* Create new directory under the parent */
if (IS_ERR(newnode = vparent->ops.mknod(vparent, nodename, mode)))
return newnode;
// print_vnode(vparent);
return newnode;
}
/*
* Pager notifies vfs about a closed file descriptor.
*
* FIXME: fsync + close could be done under a single "close" ipc
* from pager. Currently there are 2 ipcs: 1 fsync + 1 fd close.
*/
int pager_sys_close(struct tcb *sender, l4id_t closer, int fd)
{
struct tcb *task;
int err;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
BUG_ON(!(task = find_task(closer)));
if ((err = id_del(task->files->fdpool, fd)) < 0) {
printf("%s: Error releasing fd identifier.\n",
__FUNCTION__);
return err;
}
task->files->fd[fd] = NILFD;
return 0;
}
/* FIXME:
* - Is it already open?
* - Allocate a copy of path string since lookup destroys it
* - Check flags and mode.
*/
int sys_open(struct tcb *task, const char *pathname, int flags, unsigned int mode)
{
struct pathdata *pdata;
struct vnode *v;
int fd;
int retval;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
/* Parse path data */
if (IS_ERR(pdata = pathdata_parse(pathname,
alloca(strlen(pathname) + 1),
task)))
return (int)pdata;
/* Creating new file */
if (flags & O_CREAT) {
/* Make sure mode identifies a file */
mode |= S_IFREG;
if (IS_ERR(v = vfs_create(task, pdata, mode))) {
retval = (int)v;
goto out;
}
} else {
/* Not creating, just opening, get the vnode */
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
retval = (int)v;
goto out;
}
}
/* Get a new fd */
BUG_ON((fd = id_new(task->files->fdpool)) < 0);
retval = fd;
/* Assign the new fd with the vnode's number */
task->files->fd[fd] = v->vnum;
out:
pathdata_destroy(pdata);
return retval;
}
int sys_mkdir(struct tcb *task, const char *pathname, unsigned int mode)
{
struct pathdata *pdata;
struct vnode *v;
int ret = 0;
/* Parse path data */
if (IS_ERR(pdata = pathdata_parse(pathname,
alloca(strlen(pathname) + 1),
task)))
return (int)pdata;
/* Make sure we create a directory */
mode |= S_IFDIR;
/* Create the directory or fail */
if (IS_ERR(v = vfs_create(task, pdata, mode)))
ret = (int)v;
/* Destroy extracted path data */
pathdata_destroy(pdata);
return ret;
}
int sys_chdir(struct tcb *task, const char *pathname)
{
struct vnode *v;
struct pathdata *pdata;
int ret = 0;
/* Parse path data */
if (IS_ERR(pdata = pathdata_parse(pathname,
alloca(strlen(pathname) + 1),
task)))
return (int)pdata;
/* Get the vnode */
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
ret = (int)v;
goto out;
}
/* Ensure it's a directory */
if (!vfs_isdir(v)) {
ret = -ENOTDIR;
goto out;
}
/* Assign the current directory pointer */
task->fs_data->curdir = v;
out:
/* Destroy extracted path data */
pathdata_destroy(pdata);
return ret;
}
void fill_kstat(struct vnode *v, struct kstat *ks)
{
ks->vnum = (u64)v->vnum;
ks->mode = v->mode;
ks->links = v->links;
ks->uid = v->owner & 0xFFFF;
ks->gid = (v->owner >> 16) & 0xFFFF;
ks->size = v->size;
ks->blksize = v->sb->blocksize;
ks->atime = v->atime;
ks->mtime = v->mtime;
ks->ctime = v->ctime;
}
int sys_fstat(struct tcb *task, int fd, void *statbuf)
{
struct vnode *v;
unsigned long vnum;
/* Get the vnum */
if (fd < 0 || fd > TASK_FILES_MAX || task->files->fd[fd] == NILFD)
return -EBADF;
vnum = task->files->fd[fd];
/* Lookup vnode */
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
return -EINVAL;
/* Fill in the c0-style stat structure */
fill_kstat(v, statbuf);
return 0;
}
/*
* Returns codezero-style stat structure which in turn is
* converted to posix style stat structure via the libposix
* library in userspace.
*/
int sys_stat(struct tcb *task, const char *pathname, void *statbuf)
{
struct vnode *v;
struct pathdata *pdata;
int ret = 0;
/* Parse path data */
if (IS_ERR(pdata = pathdata_parse(pathname,
alloca(strlen(pathname) + 1),
task)))
return (int)pdata;
/* Get the vnode */
if (IS_ERR(v = vfs_lookup_bypath(pdata))) {
ret = (int)v;
goto out;
}
/* Fill in the c0-style stat structure */
fill_kstat(v, statbuf);
out:
/* Destroy extracted path data */
pathdata_destroy(pdata);
return ret;
}
/*
* Note this can be solely called by the pager and is not the posix read call.
* That call is in the pager. This merely supplies the pages the pager needs
* if they're not in the page cache.
*/
int pager_sys_read(struct tcb *pager, unsigned long vnum, unsigned long f_offset,
unsigned long npages, void *pagebuf)
{
struct vnode *v;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
if (pager->tid != PAGER_TID)
return -EINVAL;
/* Lookup vnode */
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
return -EINVAL;
/* Ensure vnode is not a directory */
if (vfs_isdir(v))
return -EISDIR;
return v->fops.read(v, f_offset, npages, pagebuf);
}
int pager_update_stats(struct tcb *pager, unsigned long vnum,
unsigned long newsize)
{
struct vnode *v;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
if (pager->tid != PAGER_TID)
return -EINVAL;
/* Lookup vnode */
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
return -EINVAL;
v->size = newsize;
v->sb->ops->write_vnode(v->sb, v);
return 0;
}
/*
* This can be solely called by the pager and is not the posix write call.
* That call is in the pager. This writes the dirty pages of a file
* back to disk via vfs.
*
* The buffer must be contiguous by page, if npages > 1.
*/
int pager_sys_write(struct tcb *pager, unsigned long vnum, unsigned long f_offset,
unsigned long npages, void *pagebuf)
{
struct vnode *v;
int ret;
int fwrite_end;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
if (pager->tid != PAGER_TID)
return -EINVAL;
/* Lookup vnode */
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
return -EINVAL;
/* Ensure vnode is not a directory */
if (vfs_isdir(v))
return -EISDIR;
//printf("%s/%s: Writing to vnode %lu, at pgoff 0x%x, %d pages, buf at 0x%x\n",
// __TASKNAME__, __FUNCTION__, vnum, f_offset, npages, pagebuf);
if ((ret = v->fops.write(v, f_offset, npages, pagebuf)) < 0)
return ret;
/*
* If the file is extended, write silently extends it.
* We update the extended size here. Otherwise subsequent write's
* may fail by relying on wrong file size.
*/
fwrite_end = __pfn_to_addr(f_offset) + ret;
if (v->size < fwrite_end) {
v->size = fwrite_end;
v->sb->ops->write_vnode(v->sb, v);
}
return ret;
}
/*
* FIXME: Here's how this should have been:
* v->ops.readdir() -> Reads fs-specific directory contents. i.e. reads
* the directory buffer, doesn't care however contained vnode details are
* stored.
*
* After reading, it converts the fs-spceific contents into generic vfs
* dentries and populates the dentries of those vnodes.
*
* If vfs_readdir() is issued, those generic dentries are converted into
* the posix-defined directory record structure. During this on-the-fly
* generation, pseudo-entries such as . and .. are also added.
*
* If this layering is not done, i.e. the low-level dentry buffer already
* keeps this record structure and we try to return that, then we wont
* have a chance to add the pseudo-entries . and .. These record entries
* are essentially created from parent vnode and current vnode but using
* the names . and ..
*/
int fill_dirent(void *buf, unsigned long vnum, int offset, char *name)
{
struct dirent *d = buf;
d->inum = (unsigned int)vnum;
d->offset = offset;
d->rlength = sizeof(struct dirent);
strncpy((char *)d->name, name, DIRENT_NAME_MAX);
return d->rlength;
}
/*
* Reads @count bytes of posix struct dirents into @buf. This implements
* the raw dirent read syscall upon which readdir() etc. posix calls
* can be built in userspace.
*
* FIXME: Ensure buf is in shared utcb, and count does not exceed it.
*/
int sys_readdir(struct tcb *t, int fd, void *buf, int count)
{
int dirent_size = sizeof(struct dirent);
int total = 0, nbytes = 0;
unsigned long vnum;
struct vnode *v;
struct dentry *d;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
/* Check address is in task's utcb */
if ((unsigned long)buf < t->shpage_address ||
(unsigned long)buf > t->shpage_address + PAGE_SIZE)
return -EINVAL;
if (fd < 0 || fd > TASK_FILES_MAX || t->files->fd[fd] == NILFD)
return -EBADF;
vnum = t->files->fd[fd];
/* Lookup vnode */
if (!(v = vfs_lookup_byvnum(vfs_root.pivot->sb, vnum)))
return -EINVAL;
d = link_to_struct(v->dentries.next, struct dentry, vref);
/* Ensure vnode is a directory */
if (!vfs_isdir(v))
return -ENOTDIR;
/* Write pseudo-entries . and .. to user buffer */
if (count < dirent_size)
return 0;
fill_dirent(buf, v->vnum, nbytes, VFS_STR_CURDIR);
nbytes += dirent_size;
buf += dirent_size;
count -= dirent_size;
if (count < dirent_size)
return 0;
fill_dirent(buf, d->parent->vnode->vnum, nbytes, VFS_STR_PARDIR);
nbytes += dirent_size;
buf += dirent_size;
count -= dirent_size;
/* Copy fs-specific dir to buf in struct dirent format */
if ((total = v->ops.filldir(buf, v, count)) < 0)
return total;
return nbytes + total;
}

View File

@@ -0,0 +1,315 @@
/*
* 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/arch/utcb.h>
#include <l4lib/ipcdefs.h>
#include <lib/malloc.h>
#include <lib/idpool.h>
#include <task.h>
#include <vfs.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <errno.h>
#include <syscalls.h>
#include <globals.h>
extern void *shared_page;
struct global_list global_tasks = {
.list = { &global_tasks.list, &global_tasks.list },
.total = 0,
};
void global_add_task(struct tcb *task)
{
BUG_ON(!list_empty(&task->list));
list_insert_tail(&task->list, &global_tasks.list);
global_tasks.total++;
}
void global_remove_task(struct tcb *task)
{
BUG_ON(list_empty(&task->list));
list_remove_init(&task->list);
BUG_ON(--global_tasks.total < 0);
}
struct tcb *find_task(int tid)
{
struct tcb *t;
list_foreach_struct(t, &global_tasks.list, list)
if (t->tid == tid)
return t;
return 0;
}
/* Allocate a vfs task structure according to given flags */
struct tcb *tcb_alloc_init(unsigned int flags)
{
struct tcb *task;
if (!(task = kzalloc(sizeof(struct tcb))))
return PTR_ERR(-ENOMEM);
/* Allocate new fs data struct if its not shared */
if (!(flags & TCB_SHARED_FS)) {
if (!(task->fs_data =
kzalloc(sizeof(*task->fs_data)))) {
kfree(task);
return PTR_ERR(-ENOMEM);
}
task->fs_data->tcb_refs = 1;
}
/* Allocate file structures if not shared */
if (!(flags & TCB_SHARED_FILES)) {
if (!(task->files =
kzalloc(sizeof(*task->files)))) {
kfree(task->fs_data);
kfree(task);
return PTR_ERR(-ENOMEM);
}
if (IS_ERR(task->files->fdpool =
id_pool_new_init(TASK_FILES_MAX))) {
void *err = task->files->fdpool;
kfree(task->files);
kfree(task->fs_data);
kfree(task);
return err;
}
task->files->tcb_refs = 1;
}
/* Ids will be set up later */
task->tid = TASK_ID_INVALID;
/* Initialise list structure */
link_init(&task->list);
return task;
}
void copy_tcb(struct tcb *to, struct tcb *from, unsigned int share_flags)
{
if (share_flags & TCB_SHARED_FILES) {
to->files = from->files;
to->files->tcb_refs++;
} else {
/* Copy all file descriptors */
memcpy(to->files->fd, from->files->fd,
TASK_FILES_MAX * sizeof(to->files->fd[0]));
/* Copy the idpool */
id_pool_copy(to->files->fdpool, from->files->fdpool, TASK_FILES_MAX);
}
if (share_flags & TCB_SHARED_FS) {
to->fs_data = from->fs_data;
to->fs_data->tcb_refs++;
} else
memcpy(to->fs_data, from->fs_data, sizeof(*to->fs_data));
}
/* Allocate a task struct and initialise it */
struct tcb *tcb_create(struct tcb *orig, l4id_t tid, unsigned long shpage,
unsigned int share_flags)
{
struct tcb *task;
/* Can't have some share flags with no original task */
BUG_ON(!orig && share_flags);
/* Create a task and use given space and thread ids. */
if (IS_ERR(task = tcb_alloc_init(share_flags)))
return task;
task->tid = tid;
task->shpage_address = shpage;
/*
* If there's an original task that means this will be a full
* or partial copy of it. We copy depending on share_flags.
*/
if (orig)
copy_tcb(task, orig, share_flags);
return task;
}
/* Free shared structures if no references left */
void tcb_free_resources(struct tcb *task)
{
if (--task->fs_data->tcb_refs == 0)
kfree(task->fs_data);
if (--task->files->tcb_refs == 0) {
kfree(task->files->fdpool);
kfree(task->files);
}
}
void tcb_destroy(struct tcb *task)
{
global_remove_task(task);
tcb_free_resources(task);
kfree(task);
}
/*
* Attaches to task's utcb. TODO: Add SHM_RDONLY and test it.
* FIXME: This calls the pager and is a potential for deadlock
* it only doesn't lock because it is called during initialisation.
*/
int task_utcb_attach(struct tcb *t)
{
int shmid;
void *shmaddr;
/* Use it as a key to create a shared memory region */
if ((shmid = shmget((key_t)t->shpage_address, PAGE_SIZE, 0)) == -1)
goto out_err;
/* Attach to the region */
if ((int)(shmaddr = shmat(shmid, (void *)t->shpage_address, 0)) == -1)
goto out_err;
/* Ensure address is right */
if ((unsigned long)shmaddr != t->shpage_address)
return -EINVAL;
// printf("%s: Mapped shared page of task %d @ 0x%x\n",
// __TASKNAME__, t->tid, shmaddr);
return 0;
out_err:
printf("%s: Mapping utcb of task %d failed with err: %d.\n",
__TASKNAME__, t->tid, errno);
return -EINVAL;
}
/*
* Receives ipc from pager about a new fork event and
* the information on the resulting child task.
*/
int pager_notify_fork(struct tcb *sender, l4id_t parentid,
l4id_t childid, unsigned long shpage_address,
unsigned int flags)
{
struct tcb *child, *parent;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
BUG_ON(!(parent = find_task(parentid)));
/* Create a child vfs tcb using given parent and copy flags */
if (IS_ERR(child = tcb_create(parent, childid, shpage_address, flags)))
return (int)child;
global_add_task(child);
// printf("%s/%s: Exiting...\n", __TASKNAME__, __FUNCTION__);
return 0;
}
/*
* Pager tells us that a task is exiting by this call.
*/
int pager_notify_exit(struct tcb *sender, l4id_t tid)
{
struct tcb *task;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
BUG_ON(!(task = find_task(tid)));
tcb_destroy(task);
// printf("%s/%s: Exiting...\n", __TASKNAME__, __FUNCTION__);
return 0;
}
/* Read task information into the utcb page, since it won't fit into mrs. */
struct task_data_head *receive_pager_taskdata(void)
{
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 PTR_ERR(err);
}
/* Check if call itself was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Error: %d.\n", __FUNCTION__, err);
return PTR_ERR(err);
}
/* Data is expected in the utcb page */
// printf("%s: %d Total tasks.\n", __FUNCTION__,
// ((struct task_data_head *)shared_page)->total);
return (struct task_data_head *)shared_page;
}
int init_task_structs(struct task_data_head *tdata_head)
{
struct tcb *t;
for (int i = 0; i < tdata_head->total; i++) {
/* New tcb with fields sent by pager */
if (IS_ERR(t = tcb_create(0, tdata_head->tdata[i].tid,
tdata_head->tdata[i].shpage_address,
0)))
return (int)t;
/* Initialise vfs specific fields. */
t->fs_data->rootdir = vfs_root.pivot;
t->fs_data->curdir = vfs_root.pivot;
/* Print task information */
//printf("%s: Task info received from mm0:\n", __TASKNAME__);
//printf("%s: task id: %d, utcb address: 0x%x\n",
// __TASKNAME__, t->tid, t->utcb_address);
/* shm attach to the utcbs for all these tasks except own */
if (t->tid != self_tid())
task_utcb_attach(t);
global_add_task(t);
}
return 0;
}
int init_task_data(void)
{
struct task_data_head *tdata_head;
/* Read how many tasks and tids of each */
BUG_ON((tdata_head = receive_pager_taskdata()) < 0);
/* Initialise local task structs using this info */
BUG_ON(init_task_structs(tdata_head) < 0);
return 0;
}

View File

@@ -0,0 +1,84 @@
/*
* High-level vfs implementation.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <fs.h>
#include <vfs.h>
#include <task.h>
#include <path.h>
LINK_DECLARE(vnode_cache);
LINK_DECLARE(dentry_cache);
/*
* /
* /boot
* /boot/images/mm0.axf
* /boot/images/fs0.axf
* /boot/images/test0.axf
* /file.txt
*/
struct vfs_mountpoint vfs_root;
/*
* Vnodes in the vnode cache have 2 keys. One is their dentry names, the other
* is their vnum. This one checks the vnode cache by the given vnum first.
* If nothing is found, it reads the vnode from disk into cache. This is called
* by system calls since tasks keep an fd-to-vnum table.
*/
struct vnode *vfs_lookup_byvnum(struct superblock *sb, unsigned long vnum)
{
struct vnode *v;
int err;
/* Check the vnode flat list by vnum */
list_foreach_struct(v, &vnode_cache, cache_list)
if (v->vnum == vnum)
return v;
/* Check the actual filesystem for the vnode */
v = vfs_alloc_vnode();
v->vnum = vnum;
/* Note this only checks given superblock */
if ((err = sb->ops->read_vnode(sb, v)) < 0) {
vfs_free_vnode(v);
return PTR_ERR(err);
}
/* Add the vnode back to vnode flat list */
list_insert(&v->cache_list, &vnode_cache);
return v;
}
/*
* Vnodes in the vnode cache have 2 keys. One is the set of dentry names they
* have, the other is their vnum. This one checks the vnode cache by the path
* first. If nothing is found, it reads the vnode from disk into the cache.
*/
struct vnode *vfs_lookup_bypath(struct pathdata *pdata)
{
const char *firstcomp;
/*
* This does vfs cache + fs lookup.
*/
BUG_ON(list_empty(&pdata->list));
firstcomp = pathdata_next_component(pdata);
return pdata->vstart->ops.lookup(pdata->vstart, pdata, firstcomp);
}
int vfs_mount_root(struct superblock *sb)
{
/*
* Lookup the root vnode of this superblock.
* The root superblock has vnode number 0.
*/
vfs_root.pivot = vfs_lookup_byvnum(sb, 0);
vfs_root.sb = sb;
return 0;
}

View File

@@ -0,0 +1,27 @@
#!/usr/bin/python
import os
import sys
compiler_prefix = "arm-linux-"
objdump = "objdump"
command = "-t"
image_name = "roottask.axf"
linkoutput_file_suffix = "-linkinfo.txt"
linkoutput_file = image_name + linkoutput_file_suffix
def generate_bootdesc():
command = compiler_prefix + objdump + " -t " + image_name + " > " + linkoutput_file
print command
os.system(command)
f = open(linkoutput_file, "r")
while True:
line = f.readline()
if len(line) is 0:
break
if "_start" in line or "_end" in line:
print line
f.close()
if __name__ == "__main__":
generate_bootdesc()

View File

@@ -0,0 +1,33 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Russel Winder
Import('environment')
e = environment.Clone()
e.Append(CPPPATH = ['include'])
# TODO: There are errors in this code that -Werror gives problems with.
e['CCFLAGS'] = ['-g', '-nostdlib', '-Wall', '-ffreestanding', '-std=gnu99']
objects = e.StaticObject(Glob('src/*.c') + Glob('src/' + e['ARCH'] + '/*.[cS]'))
Depends(objects, e['configFiles'])
library = e.StaticLibrary('l4', objects)
Return('library')

View File

@@ -0,0 +1 @@
arch-arm

View File

@@ -0,0 +1,15 @@
#ifndef __ARM_ASM_H__
#define __ARM_ASM_H__
#define BEGIN_PROC(name) \
.global name; \
.type name,function; \
.align; \
name:
#define END_PROC(name) \
.fend_##name: \
.size name,.fend_##name - name;
#endif /* __ARM_ASM_H__ */

View File

@@ -0,0 +1,92 @@
/*
* System call prototypes.
*
* Copyright (C) 2007 Bahadir Balban
*/
#ifndef __ARM_SYSCALLS_H__
#define __ARM_SYSCALLS_H__
#include <l4lib/arch/types.h>
#include <l4lib/arch/utcb.h>
#include <l4/generic/space.h>
#include <l4/api/space.h>
#include <l4/api/kip.h>
#include <l4/api/ipc.h>
#include <l4/api/thread.h>
struct task_ids {
int tid;
int spid;
int tgid;
};
static inline void *
l4_kernel_interface(unsigned int *api_version, unsigned int *api_flags,
unsigned int *kernel_id)
{
return (void *)L4_KIP_ADDRESS;
}
typedef unsigned int (*__l4_thread_switch_t)(u32);
extern __l4_thread_switch_t __l4_thread_switch;
unsigned int l4_thread_switch (u32 dest);
typedef int (*__l4_getid_t)(struct task_ids *ids);
extern __l4_getid_t __l4_getid;
int l4_getid(struct task_ids *ids);
typedef int (*__l4_ipc_t)(l4id_t to, l4id_t from, u32 flags);
extern __l4_ipc_t __l4_ipc;
int l4_ipc(l4id_t to, l4id_t from, u32 flags);
typedef int (*__l4_capability_control_t)(unsigned int req, unsigned int flags, void *buf);
extern __l4_capability_control_t __l4_capability_control;
int l4_capability_control(unsigned int req, unsigned int flags, void *buf);
typedef int (*__l4_map_t)(void *phys, void *virt,
u32 npages, u32 flags, l4id_t tid);
extern __l4_map_t __l4_map;
int l4_map(void *p, void *v, u32 npages, u32 flags, l4id_t tid);
typedef int (*__l4_unmap_t)(void *virt, unsigned long npages, l4id_t tid);
extern __l4_unmap_t __l4_unmap;
int l4_unmap(void *virtual, unsigned long numpages, l4id_t tid);
typedef int (*__l4_thread_control_t)(unsigned int action, struct task_ids *ids);
extern __l4_thread_control_t __l4_thread_control;
int l4_thread_control(unsigned int action, struct task_ids *ids);
typedef int (*__l4_space_control_t)(unsigned int action, void *kdata);
extern __l4_space_control_t __l4_space_control;
int l4_space_control(unsigned int action, void *kdata);
typedef int (*__l4_ipc_control_t)(unsigned int action, l4id_t blocked_sender,
u32 blocked_tag);
extern __l4_ipc_control_t __l4_ipc_control;
int l4_ipc_control(unsigned int, l4id_t blocked_sender, u32 blocked_tag);
typedef int (*__l4_exchange_registers_t)(void *exregs_struct, l4id_t tid);
extern __l4_exchange_registers_t __l4_exchange_registers;
int l4_exchange_registers(void *exregs_struct, l4id_t tid);
typedef int (*__l4_container_control_t)(unsigned int req, unsigned int flags, void *buf);
extern __l4_container_control_t __l4_container_control;
int l4_container_control(unsigned int req, unsigned int flags, void *buf);
typedef int (*__l4_time_t)(void *timeval, int set);
extern __l4_time_t __l4_time;
int l4_time(void *timeval, int set);
typedef int (*__l4_mutex_control_t)(void *mutex_word, int op);
extern __l4_mutex_control_t __l4_mutex_control;
int l4_mutex_control(void *mutex_word, int op);
/* To be supplied by server tasks. */
void *virt_to_phys(void *);
void *phys_to_virt(void *);
#endif /* __ARM_SYSCALLS_H__ */

View File

@@ -0,0 +1,292 @@
/*
* Helper functions that wrap raw l4 syscalls.
*
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#ifndef __L4LIB_SYSLIB_H__
#define __L4LIB_SYSLIB_H__
#include <stdio.h>
#include <l4lib/arch/syscalls.h>
#include <l4/macros.h>
/*
* NOTE:
* Its best to use these wrappers because they generalise the way
* common ipc data like sender id, error, ipc tag are passed
* between ipc parties.
*
* The arguments to l4_ipc() are used by the microkernel to initiate
* the ipc. Any data passed in message registers may or may not be
* a duplicate of this data, but the distinction is that anything
* that is passed via the mrs are meant to be used by the other party
* participating in the ipc.
*/
/* For system call arguments */
#define L4SYS_ARG0 (MR_UNUSED_START)
#define L4SYS_ARG1 (MR_UNUSED_START + 1)
#define L4SYS_ARG2 (MR_UNUSED_START + 2)
#define L4SYS_ARG3 (MR_UNUSED_START + 3)
#define L4_IPC_TAG_MASK 0x00000FFF
/*
* Servers get sender.
*/
static inline l4id_t l4_get_sender(void)
{
return (l4id_t)read_mr(MR_SENDER);
}
/*
* When doing an ipc the sender never has to be explicitly set in
* the utcb via this function since this information is found out
* by the microkernel by checking the system caller's id. This is
* only used for restoring the sender on the utcb in order to
* complete an earlier ipc.
*/
static inline void l4_set_sender(l4id_t sender)
{
write_mr(MR_SENDER, sender);
}
static inline unsigned int l4_set_ipc_size(unsigned int word, unsigned int size)
{
word &= ~L4_IPC_FLAGS_SIZE_MASK;
word |= ((size << L4_IPC_FLAGS_SIZE_SHIFT) & L4_IPC_FLAGS_SIZE_MASK);
return word;
}
static inline unsigned int l4_get_ipc_size(unsigned int word)
{
return (word & L4_IPC_FLAGS_SIZE_MASK) >> L4_IPC_FLAGS_SIZE_SHIFT;
}
static inline unsigned int l4_set_ipc_msg_index(unsigned int word, unsigned int index)
{
/* FIXME: Define MR_PRIMARY_TOTAL, MR_TOTAL etc. and use MR_TOTAL HERE! */
BUG_ON(index > UTCB_SIZE);
word &= ~L4_IPC_FLAGS_MSG_INDEX_MASK;
word |= (index << L4_IPC_FLAGS_MSG_INDEX_SHIFT) &
L4_IPC_FLAGS_MSG_INDEX_MASK;
return word;
}
static inline unsigned int l4_get_ipc_msg_index(unsigned int word)
{
return (word & L4_IPC_FLAGS_MSG_INDEX_MASK)
>> L4_IPC_FLAGS_MSG_INDEX_SHIFT;
}
static inline unsigned int l4_set_ipc_flags(unsigned int word, unsigned int flags)
{
word &= ~L4_IPC_FLAGS_TYPE_MASK;
word |= flags & L4_IPC_FLAGS_TYPE_MASK;
return word;
}
static inline unsigned int l4_get_ipc_flags(unsigned int word)
{
return word & L4_IPC_FLAGS_TYPE_MASK;
}
static inline unsigned int l4_get_tag(void)
{
return read_mr(MR_TAG) & L4_IPC_TAG_MASK;
}
static inline void l4_set_tag(unsigned int tag)
{
unsigned int tag_flags = read_mr(MR_TAG);
tag_flags &= ~L4_IPC_TAG_MASK;
tag_flags |= tag & L4_IPC_TAG_MASK;
write_mr(MR_TAG, tag_flags);
}
/*
* If we're about to do another ipc, this saves the last ipc's
* parameters such as the sender and tag information.
* Any previously saved data in save slots are destroyed.
*/
static inline void l4_save_ipcregs(void)
{
l4_get_utcb()->saved_sender = l4_get_sender();
l4_get_utcb()->saved_tag = l4_get_tag();
}
static inline void l4_restore_ipcregs(void)
{
l4_set_tag(l4_get_utcb()->saved_tag);
l4_set_sender(l4_get_utcb()->saved_sender);
}
static inline l4id_t self_tid(void)
{
struct task_ids ids;
l4_getid(&ids);
return ids.tid;
}
static inline int l4_send_full(l4id_t to, unsigned int tag)
{
l4_set_tag(tag);
return l4_ipc(to, L4_NILTHREAD, L4_IPC_FLAGS_FULL);
}
static inline int l4_receive_full(l4id_t from)
{
return l4_ipc(L4_NILTHREAD, from, L4_IPC_FLAGS_FULL);
}
static inline int l4_sendrecv_full(l4id_t to, l4id_t from, unsigned int tag)
{
int err;
BUG_ON(to == L4_NILTHREAD || from == L4_NILTHREAD);
l4_set_tag(tag);
err = l4_ipc(to, from, L4_IPC_FLAGS_FULL);
return err;
}
static inline int l4_send_extended(l4id_t to, unsigned int tag,
unsigned int size, void *buf)
{
unsigned int flags = 0;
l4_set_tag(tag);
/* Set up flags word for extended ipc */
flags = l4_set_ipc_flags(flags, L4_IPC_FLAGS_EXTENDED);
flags = l4_set_ipc_size(flags, size);
flags = l4_set_ipc_msg_index(flags, L4SYS_ARG0);
/* Write buffer pointer to MR index that we specified */
write_mr(L4SYS_ARG0, (unsigned long)buf);
return l4_ipc(to, L4_NILTHREAD, flags);
}
static inline int l4_receive_extended(l4id_t from, unsigned int size, void *buf)
{
unsigned int flags = 0;
/* Indicate extended receive */
flags = l4_set_ipc_flags(flags, L4_IPC_FLAGS_EXTENDED);
/* How much data is accepted */
flags = l4_set_ipc_size(flags, size);
/* Indicate which MR index buffer pointer is stored */
flags = l4_set_ipc_msg_index(flags, L4SYS_ARG0);
/* Set MR with buffer to receive data */
write_mr(L4SYS_ARG0, (unsigned long)buf);
return l4_ipc(L4_NILTHREAD, from, flags);
}
static inline int l4_sendrecv_extended(l4id_t to, l4id_t from,
unsigned int tag, void *buf)
{
/* Need to imitate sendrecv but with extended send/recv flags */
return 0;
}
static inline int l4_send(l4id_t to, unsigned int tag)
{
l4_set_tag(tag);
return l4_ipc(to, L4_NILTHREAD, 0);
}
static inline int l4_sendrecv(l4id_t to, l4id_t from, unsigned int tag)
{
int err;
BUG_ON(to == L4_NILTHREAD || from == L4_NILTHREAD);
l4_set_tag(tag);
err = l4_ipc(to, from, 0);
return err;
}
static inline int l4_receive(l4id_t from)
{
return l4_ipc(L4_NILTHREAD, from, 0);
}
/* Servers:
* Sets the message register for returning errors back to client task.
* These are usually posix error codes.
*/
static inline void l4_set_retval(int retval)
{
write_mr(MR_RETURN, retval);
}
/* Clients:
* Learn result of request.
*/
static inline int l4_get_retval(void)
{
return read_mr(MR_RETURN);
}
static inline void l4_print_mrs()
{
printf("Message registers: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
read_mr(0), read_mr(1), read_mr(2), read_mr(3),
read_mr(4), read_mr(5));
}
/* Servers:
* Return the ipc result back to requesting task.
*/
static inline int l4_ipc_return(int retval)
{
l4id_t sender = l4_get_sender();
l4_set_retval(retval);
/* Setting the tag may overwrite retval so we l4_send without tagging */
return l4_ipc(sender, L4_NILTHREAD, 0);
}
void *l4_new_virtual(int npages);
void *l4_del_virtual(void *virt, int npages);
/* A helper that translates and maps a physical address to virtual */
static inline void *l4_map_helper(void *phys, int npages)
{
struct task_ids ids;
void *virt = l4_new_virtual(npages);
l4_getid(&ids);
l4_map(phys, virt, npages, MAP_USR_RW_FLAGS, ids.tid);
return virt;
}
/* A helper that translates and maps a physical address to virtual */
static inline void *l4_unmap_helper(void *virt, int npages)
{
struct task_ids ids;
l4_getid(&ids);
l4_unmap(virt, npages, ids.tid);
l4_del_virtual(virt, npages);
return 0;
}
#endif /* __L4LIB_SYSLIB_H__ */

View File

@@ -0,0 +1,8 @@
#ifndef __L4LIB_ARM_TYPES_H___
#define __L4LIB_ARM_TYPES_H__
#define TASK_ID_INVALID -1
#include <l4/arch/arm/types.h>
#endif /* __L4LIB_ARM_TYPES_H__ */

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2009 Bahadir Bilgehan Balban
*/
#ifndef __ARM_UTCB_H__
#define __ARM_UTCB_H__
#define USER_UTCB_REF 0xFF000050
#define L4_KIP_ADDRESS 0xFF000000
#define UTCB_KIP_OFFSET 0x50
#ifndef __ASSEMBLY__
#include <l4lib/types.h>
#include <l4/macros.h>
#include INC_GLUE(message.h)
#include INC_GLUE(memory.h)
#include <string.h>
#include <stdio.h>
/* UTCB implementation */
/*
* NOTE: In syslib.h the first few mrs are used by data frequently
* needed for all ipcs. Those mrs are defined the kernel message.h
*/
struct utcb {
u32 mr[MR_TOTAL]; /* MRs that are mapped to real registers */
u32 saved_tag; /* Saved tag field for stacked ipcs */
u32 saved_sender; /* Saved sender field for stacked ipcs */
u32 mr_rest[MR_REST]; /* Complete the utcb for up to 64 words */
};
extern struct kip *kip;
/*
* Pointer to Kernel Interface Page's UTCB pointer offset.
*/
extern struct utcb **kip_utcb_ref;
static inline struct utcb *l4_get_utcb()
{
/*
* By double dereferencing, we get the private TLS (aka UTCB). First
* reference is to the KIP's utcb offset, second is to the utcb itself,
* to which the KIP's utcb reference had been updated during context
* switch.
*/
return *kip_utcb_ref;
}
/* Functions to read/write utcb registers */
static inline unsigned int read_mr(int offset)
{
if (offset < MR_TOTAL)
return l4_get_utcb()->mr[offset];
else
return l4_get_utcb()->mr_rest[offset - MR_TOTAL];
}
static inline void write_mr(unsigned int offset, unsigned int val)
{
if (offset < MR_TOTAL)
l4_get_utcb()->mr[offset] = val;
else
l4_get_utcb()->mr_rest[offset - MR_TOTAL] = val;
}
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_UTCB_H__ */

View File

@@ -0,0 +1,24 @@
#ifndef __MM0_EXREGS_H__
#define __MM0_EXREGS_H__
#include <l4/api/exregs.h>
void exregs_set_stack(struct exregs_data *s, unsigned long sp);
void exregs_set_mr(struct exregs_data *s, int offset, unsigned long val);
void exregs_set_pc(struct exregs_data *s, unsigned long pc);
void exregs_set_pager(struct exregs_data *s, l4id_t pagerid);
void exregs_set_utcb(struct exregs_data *s, unsigned long virt);
/*
exregs_set_stack(unsigned long sp)
exregs_set_pc(unsigned long pc)
exregs_set_return(unsigned long retreg)
exregs_set_arg0(unsigned long arg0)
exregs_set_mr0(unsigned long mr0)
exregs_set_mr_sender(unsigned long sender)
exregs_set_mr_return(unsigned long retreg)
exregs_set_all(unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3,
unsigned long sp, unsigned long pc, u32 valid_vector, l4id_t pager);
*/
#endif /* __MM0_EXREGS_H__ */

View File

@@ -0,0 +1,6 @@
#ifndef __L4LIB_INIT__
#define __L4LIB_INIT__
void __l4_init(void);
#endif

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2007, 2008 Bahadir Balban
*
* This file contains ipc definitions that are needed for server tasks
* to communicate with each other. For example common shared memory ids
* between two servers, or common ipc tags used between two servers are
* defined here.
*/
#ifndef __IPCDEFS_H__
#define __IPCDEFS_H__
#include <l4/api/ipc.h>
/* SHMID used betweeen FS0 and BLKDEV0 servers */
#define FS_BLKDEV_SHMID 0
/*** IPC Tags used between server tasks ***/
/* For ping ponging */
#define L4_IPC_TAG_SYNC_EXTENDED 1
#define L4_IPC_TAG_SYNC_FULL 2
#define L4_IPC_TAG_SYNC 3
/* To obtain default shared page address */
#define L4_IPC_TAG_SHPAGE 4
/* XXX: unused */
#define L4_IPC_TAG_GRANT 5
/* Posix system call tags */
#define L4_IPC_TAG_SHMGET 6
#define L4_IPC_TAG_SHMAT 7
#define L4_IPC_TAG_SHMDT 8
#define L4_IPC_TAG_MMAP 9
#define L4_IPC_TAG_MUNMAP 10
#define L4_IPC_TAG_MSYNC 11
#define L4_IPC_TAG_OPEN 12
#define L4_IPC_TAG_READ 13
#define L4_IPC_TAG_WRITE 14
#define L4_IPC_TAG_LSEEK 15
#define L4_IPC_TAG_CLOSE 16
#define L4_IPC_TAG_BRK 17
#define L4_IPC_TAG_READDIR 18
#define L4_IPC_TAG_MKDIR 19
#define L4_IPC_TAG_EXECVE 20
#define L4_IPC_TAG_CHDIR 21
#define L4_IPC_TAG_FORK 22
#define L4_IPC_TAG_STAT 23
#define L4_IPC_TAG_FSTAT 24
#define L4_IPC_TAG_FSYNC 25
#define L4_IPC_TAG_CLONE 26
#define L4_IPC_TAG_EXIT 27
#define L4_IPC_TAG_WAIT 28
/* Tags for ipc between fs0 and mm0 */
#define L4_IPC_TAG_TASKDATA 40
#define L4_IPC_TAG_PAGER_OPEN 41 /* vfs sends the pager open file data. */
#define L4_IPC_TAG_PAGER_READ 42 /* Pager reads file contents from vfs */
#define L4_IPC_TAG_PAGER_WRITE 43 /* Pager writes file contents to vfs */
#define L4_IPC_TAG_PAGER_CLOSE 44 /* Pager notifies vfs of file close */
#define L4_IPC_TAG_PAGER_UPDATE_STATS 45 /* Pager updates file stats in vfs */
#define L4_IPC_TAG_NOTIFY_FORK 46 /* Pager notifies vfs of process fork */
#define L4_IPC_TAG_NOTIFY_EXIT 47 /* Pager notifies vfs of process exit */
#define L4_IPC_TAG_PAGER_OPEN_BYPATH 48 /* Pager opens a vfs file by pathname */
#endif /* __IPCDEFS_H__ */

View File

@@ -0,0 +1,15 @@
/*
* Kernel Interface Page
*
* Copyright (C) 2007 Bahadir Balban
*
*/
#ifndef __L4LIB_KIP_H__
#define __L4LIB_KIP_H__
/* Use the kernel header */
#include <l4lib/types.h>
#include <l4lib/arch/syscalls.h>
#include <l4/api/kip.h>
#endif /* __KIP_H__ */

View File

@@ -0,0 +1,38 @@
/*
* User space locking
*
* Copyright (C) 2009 Bahadir Bilgehan Balban
*/
#ifndef __L4_MUTEX_H__
#define __L4_MUTEX_H__
#if !defined(__ASSEMBLY__)
#include <l4/api/mutex.h>
struct l4_mutex {
unsigned int lock;
} __attribute__((aligned(sizeof(int))));
void l4_mutex_init(struct l4_mutex *m);
int l4_mutex_lock(struct l4_mutex *m);
int l4_mutex_unlock(struct l4_mutex *m);
#endif
/* Mutex return value - don't mix up with mutes state */
#define L4_MUTEX_CONTENDED -1
#define L4_MUTEX_SUCCESS 0
/* Mutex states - Any valid tid value is a locked state */
#define L4_MUTEX_UNLOCKED -1
#define L4_MUTEX(m) \
struct l4_mutex m = { L4_MUTEX_UNLOCKED }
#endif /* __L4_MUTEX_H__ */

View File

@@ -0,0 +1,23 @@
#ifndef __OS_KSTAT_H__
#define __OS_KSTAT_H__
#include <l4lib/types.h>
/*
* Internal codezero-specific stat structure.
* This is converted to posix stat in userspace
*/
struct kstat {
u64 vnum;
u32 mode;
int links;
u16 uid;
u16 gid;
u64 size;
int blksize;
u64 atime;
u64 mtime;
u64 ctime;
};
#endif

View File

@@ -0,0 +1,7 @@
#ifndef __OS_READDIR_H__
#define __OS_READDIR_H__
/* Any os syscall related data that is not in posix */
ssize_t os_readdir(int fd, void *buf, size_t count);
#endif

View File

@@ -0,0 +1,14 @@
#ifndef __L4_THREAD_H__
#define __L4_THREAD_H__
#include <libl4/arch/utcb.h>
#include <libl4/arch/types.h>
struct l4_thread_struct {
l4id_t tlid; /* Thread local id */
struct task_ids ids; /* Thread L4-defined ids */
struct utcb *utcb; /* Thread utcb */
unsigned long stack_start; /* Thread start of stack */
};
#endif /* __L4_THREAD_H__ */

View File

@@ -0,0 +1,6 @@
#ifndef __TYPES_H__
#define __TYPES_H__
#include <l4lib/arch/types.h>
#endif /* __TYPES_H__ */

View File

@@ -0,0 +1,12 @@
/*
* Copyright (C) 2007 Bahadir Balban
*/
#ifndef __UTCB_H__
#define __UTCB_H__
#include <l4lib/types.h>
#include <l4lib/arch/utcb.h>
int utcb_init(void);
#endif /* __UTCB_H__ */

View File

@@ -0,0 +1,51 @@
/*
* Generic to arch-specific interface for
* exchange_registers()
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4lib/exregs.h>
#include INC_GLUE(message.h)
void exregs_set_mr(struct exregs_data *s, int offset, unsigned long val)
{
/* Get MR0 */
u32 *mr = &s->context.MR0_REGISTER;
/* Sanity check */
BUG_ON(offset > MR_TOTAL || offset < 0);
/* Set MR */
mr[offset] = val;
/* Set valid bit for mr register */
s->valid_vect |= FIELD_TO_BIT(exregs_context_t, MR0_REGISTER) << offset;
}
void exregs_set_pager(struct exregs_data *s, l4id_t pagerid)
{
s->pagerid = pagerid;
s->flags |= EXREGS_SET_PAGER;
}
void exregs_set_utcb(struct exregs_data *s, unsigned long virt)
{
s->utcb_address = virt;
s->flags |= EXREGS_SET_UTCB;
}
void exregs_set_stack(struct exregs_data *s, unsigned long sp)
{
s->context.sp = sp;
s->valid_vect |= FIELD_TO_BIT(exregs_context_t, sp);
}
void exregs_set_pc(struct exregs_data *s, unsigned long pc)
{
s->context.pc = pc;
s->valid_vect |= FIELD_TO_BIT(exregs_context_t, pc);
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2009 Bahadir Balban
*/
#include <l4lib/arch/asm.h>
#include <l4lib/mutex.h>
/*
* NOTES:
*
* Recap on swp:
*
* swp rx, ry, [rz]
*
* In one instruction:
*
* 1) Stores the value in ry into location pointed by rz.
* 2) Loads the value in the location of rz into rx.
* By doing so, in one instruction one can attempt to lock
* a word, and discover whether it was already locked.
*
* Why use tid of thread to lock mutex instead of
* a single lock value?
*
* Because in one atomic instruction, not only the locking attempt
* should be able to indicate whether it is locked, but also
* the contentions. A unified lock value would not be sufficient.
* The only way to indicate a contended lock is to store the
* unique TID of the locker.
*/
/*
* Any non-negative value that is a potential TID
* (including 0) means mutex is locked.
*/
/*
* @r0 = address of mutex word
* @r1 = unique tid of current thread
*/
BEGIN_PROC(__l4_mutex_lock)
swp r2, r1, [r0]
cmp r2, #L4_MUTEX_UNLOCKED @ Was the lock available?
movne r0, #L4_MUTEX_CONTENDED @ Indicate failure
moveq r0, #L4_MUTEX_SUCCESS @ Indicate success
mov pc, lr
END_PROC(__l4_mutex_lock)
/*
* @r0 = address of mutex word
* @r1 = unique tid of current thread
*/
BEGIN_PROC(__l4_mutex_unlock)
mov r3, #L4_MUTEX_UNLOCKED
swp r2, r3, [r0]
cmp r2, r1 @ Check lock had original tid value
movne r0, #L4_MUTEX_CONTENDED @ Indicate contention
moveq r0, #L4_MUTEX_SUCCESS @ Indicate no contention
cmp r2, #L4_MUTEX_UNLOCKED @ Or - was it already unlocked?
1:
beq 1b @ If so busy-spin to indicate bug.
mov pc, lr
END_PROC(__l4_mutex_unlock)

View File

@@ -0,0 +1,222 @@
/*
* Userspace system call interface.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4lib/arch/asm.h>
#include <l4lib/arch/utcb.h>
#include <l4/generic/space.h>
#include <l4/macros.h>
#include INC_GLUE(message.h)
/* Old macro */
#if 0
.macro utcb_address rx
ldr \rx, =utcb
.endm
#endif
/* New macro does double dereference */
.macro utcb_address rx
ldr \rx, =kip_utcb_ref @ First get pointer to utcb pointer in KIP
ldr \rx, [\rx] @ Get pointer to UTCB address from UTCB pointer in KIP
ldr \rx, [\rx] @ Get the utcb address
.endm
BEGIN_PROC(l4_thread_switch)
ldr r12, =__l4_thread_switch
ldr pc, [r12] @ Jump into the SWI. Kernel returns to LR_USR, which is the caller.
END_PROC(l4_thread_switch)
/*
* The syscall returns process ids. This function saves the returned values in the
* arguments passed by reference. @r0 = struct task_ids *
*/
BEGIN_PROC(l4_getid)
ldr r12, =__l4_getid @ See l4_kdata_read for why its so simple.
ldr pc, [r12] @ Return.
END_PROC(l4_getid)
/*
* Reads/manipulates capabilities of a thread, particularly a pager.
* @r0 = request type, @r1 = request flags, @r2 = io buffer ptr
*/
BEGIN_PROC(l4_capability_control)
ldr r12, =__l4_capability_control
ldr pc, [r12] @ Jump into the SWI
/*
* The LR_USR points at the return address of this function. The system
* call return path directly jumps to LR_USR so we don't even need a
* return instruction here.
*/
END_PROC(l4_capability_control)
/*
* For clone() we need special assembler handling
* Same signature as ipc(): @r0 = to, @r1 = from @r2 = flags
*
* NOTE: Note that this breaks l4 system call interface,
* this should be moved elsewhere and modified using existing l4 mechanisms.
*/
BEGIN_PROC(arch_clone)
stmfd sp!, {r4-r8,lr} @ Save context.
utcb_address r12 @ Get utcb address.
ldmia r12!, {r3-r8} @ Load 6 Message registers from utcb. MR0-MR5
ldr r12, =__l4_ipc
mov lr, pc
ldr pc, [r12] @ Perform the ipc()
/*
* At this moment:
* - MR_RETURN tells us whether we are parent or child (or have failed).
* - Child has new SP set, with |func_ptr|arg1|{End of stack}SP<-| on stack.
* - Child needs exit logic when its function is finished.
*/
cmp r0, #0 @ Check ipc success
blt ipc_failed
cmp MR_RETURN_REGISTER, #0 @ Check ipc return register MR_RETURN.
blt clone_failed @ Ipc was ok but clone() failed.
bgt parent_return @ It has child pid, goto parent return.
child:
ldr r0, [sp, #-4]! @ Load child's first argument.
mov lr, pc @ Save return address
ldr pc, [sp, #-4]! @ Load function pointer from stack
child_exit:
b child_exit @ We infinitely loop for now.
@ Return with normal ipc return sequence
parent_return:
clone_failed:
ipc_failed:
utcb_address r12 @ Get utcb
stmia r12, {r3-r8} @ Store mrs.
ldmfd sp!, {r4-r8,pc} @ Return restoring pc and context.
END_PROC(arch_clone)
/*
* Inter-process communication. Loads message registers as arguments before the call,
* and stores them as results after the call. @r0 = to, @r1 = from.
*/
BEGIN_PROC(l4_ipc)
stmfd sp!, {r4-r8,lr} @ Save context.
utcb_address r12 @ Get utcb address.
ldmia r12!, {r3-r8} @ Load 6 Message registers from utcb. MR0-MR5
ldr r12, =__l4_ipc
mov lr, pc
ldr pc, [r12]
utcb_address r12 @ Get utcb address.
stmia r12, {r3-r8} @ Store 6 Message registers to utcb. MR0-MR5
ldmfd sp!, {r4-r8,pc} @ Return restoring pc, and context.
END_PROC(l4_ipc)
/*
* System call that maps an area of memory into the given address space.
* @r0 = physical address, @r1 = virtual address, @r2 = map size in pages,
* @r3 = map flags, @r4 = The tgid of the address space to map.
*/
BEGIN_PROC(l4_map)
stmfd sp!, {r4, lr}
ldr r4, [sp, #8] @ FIXME: Is this right?
ldr r12, =__l4_map
mov lr, pc @ We must return here to restore r4.
ldr pc, [r12]
ldmfd sp!, {r4, pc}
END_PROC(l4_map)
/*
* System call that unmaps an area of memory into the given address space.
* @r0 = virtual, @r1 = pages, @r2 = tid of address space to unmap
*/
BEGIN_PROC(l4_unmap)
stmfd sp!, {lr}
ldr r12, =__l4_unmap
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_unmap)
/*
* System call that controls containers and their parameters.
* @r0 = request type, @r1 = request flags, @r2 = io buffer ptr
*/
BEGIN_PROC(l4_container_control)
stmfd sp!, {lr}
ldr r12, =__l4_container_control
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_container_control)
/*
* System call that gets or sets the time info structure.
* @r0 = ptr to time structure @r1 = set or get. set = 1, get = 0.
*/
BEGIN_PROC(l4_time)
stmfd sp!, {lr}
ldr r12, =__l4_time
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_time)
/*
* System call that controls thread creation, destruction and modification.
* @r0 = thread action, @r1 = &ids, @r2 = utcb address
*/
BEGIN_PROC(l4_thread_control)
stmfd sp!, {lr}
ldr r12, =__l4_thread_control
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_thread_control)
/*
* System call that modifies ipc blocked sender lists of receivers.
* @r0 = Action (e.g. block/unblock), @r1 = sender id, @r2 = sender tag
*/
BEGIN_PROC(l4_ipc_control)
stmfd sp!, {lr}
ldr r12, =__l4_ipc_control
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_ipc_control)
/*
* Manipulates address spaces, e.g. sets up shared memory areas between threads
* @r0 = operation code, @r1 = struct shm_kdata *kdata
*/
BEGIN_PROC(l4_space_control)
stmfd sp!, {lr}
ldr r12, =__l4_space_control
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_space_control)
/*
* Locks/unlocks a userspace mutex.
* @r0 = mutex virtual address, @r1 = mutex operation code
*/
BEGIN_PROC(l4_mutex_control)
stmfd sp!, {lr}
ldr r12, =__l4_mutex_control
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_mutex_control)
/*
* Sets registers of a thread and its pager.
* @r0 = ptr to exregs_data structure, @r1 = tid of thread.
*/
BEGIN_PROC(l4_exchange_registers)
stmfd sp!, {lr}
ldr r12, =__l4_exchange_registers
mov lr, pc
ldr pc, [r12]
ldmfd sp!, {pc} @ Restore original lr and return.
END_PROC(l4_exchange_registers)

View File

@@ -0,0 +1,62 @@
/*
* Initialise system call offsets and utcb reference.
*
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#include <l4lib/kip.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/arch/utcb.h>
#include <l4lib/ipcdefs.h>
#include <l4/macros.h>
#include INC_GLUE(memlayout.h)
#include <stdio.h>
__l4_ipc_t __l4_ipc = 0;
__l4_map_t __l4_map = 0;
__l4_unmap_t __l4_unmap = 0;
__l4_getid_t __l4_getid = 0;
__l4_thread_switch_t __l4_thread_switch = 0;
__l4_thread_control_t __l4_thread_control = 0;
__l4_ipc_control_t __l4_ipc_control = 0;
__l4_space_control_t __l4_space_control = 0;
__l4_exchange_registers_t __l4_exchange_registers = 0;
__l4_container_control_t __l4_container_control = 0;
__l4_capability_control_t __l4_capability_control = 0;
__l4_time_t __l4_time = 0;
__l4_mutex_control_t __l4_mutex_control = 0;
struct kip *kip;
/*
* Reference to private UTCB of this thread.
* Used only for pushing/reading ipc message registers.
*/
struct utcb **kip_utcb_ref;
void __l4_init(void)
{
/* Kernel interface page */
kip = l4_kernel_interface(0, 0, 0);
/* Reference to utcb field of KIP */
kip_utcb_ref = (struct utcb **)&kip->utcb;
__l4_ipc = (__l4_ipc_t)kip->ipc;
__l4_map = (__l4_map_t)kip->map;
__l4_unmap = (__l4_unmap_t)kip->unmap;
__l4_getid = (__l4_getid_t)kip->getid;
__l4_thread_switch = (__l4_thread_switch_t)kip->thread_switch;
__l4_thread_control= (__l4_thread_control_t)kip->thread_control;
__l4_ipc_control= (__l4_ipc_control_t)kip->ipc_control;
__l4_space_control= (__l4_space_control_t)kip->space_control;
__l4_exchange_registers =
(__l4_exchange_registers_t)kip->exchange_registers;
__l4_capability_control =
(__l4_capability_control_t)kip->capability_control;
__l4_container_control =
(__l4_container_control_t)kip->container_control;
__l4_time = (__l4_time_t)kip->time;
__l4_mutex_control = (__l4_mutex_control_t)kip->mutex_control;
}

View File

@@ -0,0 +1,89 @@
/*
* Userspace mutex implementation
*
* Copyright (C) 2009 Bahadir Bilgehan Balban
*/
#include <l4lib/mutex.h>
#include <l4lib/types.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
/*
* NOTES:
*
* The design is kept as simple as possible.
*
* l4_mutex_lock() locks an initialized, mutex.
* If it contends, it calls the mutex syscall.
*
* l4_mutex_unlock() releases an acquired mutex.
* If there was contention, mutex syscall is called
* to resolve by the kernel.
*
* Internals:
*
* (1) The kernel creates a waitqueue for every unique
* mutex in the system, i.e. every unique physical
* address that is contended as a mutex. In that respect
* virtual mutex addresses are translated to physical
* and checked for match.
*
* (2) If a mutex is contended, kernel is called by both the
* locker and the unlocker (i.e. the lock holder). The syscall
* results in a rendezvous and both tasks quit the syscall
* synchronised. A rendezvous is necessary because it is not possible
* to check lock status and send a WAIT or WAKEUP request to the
* kernel atomically from userspace. In other words, a WAKEUP call
* would be lost if it arrived before the unsuccessful lock attempt
* resulted in a WAIT.
*
* (3) The unlocker releases the lock after it returns from the syscall.
* (4) The locker continuously tries to acquire the lock
*
* Issues:
* - The kernel action is to merely wake up sleepers. If
* a new thread acquires the lock meanwhile, all those woken
* up threads would have to sleep again.
* - All sleepers are woken up (aka thundering herd). This
* must be done because if a single task is woken up, there
* is no guarantee that that would in turn wake up others.
* It might even quit attempting to take the lock.
* - Whether this is the best design - time will tell.
*/
extern int __l4_mutex_lock(void *word, l4id_t tid);
extern int __l4_mutex_unlock(void *word, l4id_t tid);
void l4_mutex_init(struct l4_mutex *m)
{
m->lock = L4_MUTEX_UNLOCKED;
}
int l4_mutex_lock(struct l4_mutex *m)
{
l4id_t tid = self_tid();
int err;
while(__l4_mutex_lock(m, tid) == L4_MUTEX_CONTENDED) {
if ((err = l4_mutex_control(&m->lock, L4_MUTEX_LOCK)) < 0) {
printf("%s: Error: %d\n", __FUNCTION__, err);
return err;
}
}
return 0;
}
int l4_mutex_unlock(struct l4_mutex *m)
{
l4id_t tid = self_tid();
int err;
if (__l4_mutex_unlock(m, tid) == L4_MUTEX_CONTENDED) {
if ((err = l4_mutex_control(&m->lock, L4_MUTEX_UNLOCK)) < 0) {
printf("%s: Error: %d\n", __FUNCTION__, err);
return err;
}
}
return 0;
}

View File

@@ -0,0 +1,39 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Russel Winder
Import('environment', 'posixServicesDirectory')
e = environment.Clone()
e.Append(CPPPATH = ['#' + posixServicesDirectory + 'libl4/include' , '.' ])
mmObjects = e.StaticObject(Glob('mm/*.c'))
Depends(mmObjects, e['configFiles'])
mmLibrary = e.StaticLibrary('mm', mmObjects)
kmObjects = e.StaticObject(Glob('kmalloc/*.c'))
Depends(kmObjects, e['configFiles'])
kmLibrary = e.StaticLibrary('km', kmObjects)
mcObjects = e.StaticObject(Glob('memcache/*.c'))
Depends(mcObjects, e['configFiles'])
mcLibrary = e.StaticLibrary('mc', mcObjects)
libraries = (mmLibrary, kmLibrary, mcLibrary)
Return('libraries')

View File

@@ -0,0 +1,202 @@
/*
* Bitmap-based linked-listable fixed-size memory cache.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <memcache/memcache.h>
#include <string.h>
#include <stdio.h>
/* Some definitions from glue/memory.h */
#define align_up(addr, size) ((((unsigned long)addr) + (size - 1)) & (~(size - 1)))
#define SZ_WORD sizeof(unsigned long)
#define WORD_BITS 32
#define BITWISE_GETWORD(x) (x >> 5) /* Divide by 32 */
#define BITWISE_GETBIT(x) (1 << (x % WORD_BITS))
static int find_and_set_first_free_bit(u32 *word, unsigned int limit)
{
int success = 0;
int i;
for(i = 0; i < limit; i++) {
/* Find first unset bit */
if (!(word[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i))) {
/* Set it */
word[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
success = 1;
break;
}
}
/* Return bit just set */
if (success)
return i;
else
return -1;
}
static int check_and_clear_bit(u32 *word, int bit)
{
/* Check that bit was set */
if (word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit)) {
word[BITWISE_GETWORD(bit)] &= ~BITWISE_GETBIT(bit);
return 0;
} else {
//printf("Trying to clear already clear bit\n");
return -1;
}
}
/* Allocate, clear and return element */
void *mem_cache_zalloc(struct mem_cache *cache)
{
void *elem = mem_cache_alloc(cache);
memset(elem, 0, cache->struct_size);
return elem;
}
/* Allocate another element from given @cache. Returns 0 when full. */
void *mem_cache_alloc(struct mem_cache *cache)
{
int bit;
if (cache->free > 0) {
/* NOTE: If needed, must lock here */
cache->free--;
if ((bit = find_and_set_first_free_bit(cache->bitmap,
cache->total)) < 0) {
printk("Error: Anomaly in cache occupied state.\n"
"Bitmap full although cache->free > 0\n");
BUG();
}
/* NOTE: If needed, must unlock here */
return (void *)(cache->start + (cache->struct_size * bit));
} else {
/* Cache full */
return 0;
}
}
/* Free element at @addr in @cache. Return negative on error. */
int mem_cache_free(struct mem_cache *cache, void *addr)
{
unsigned int struct_addr = (unsigned int)addr;
unsigned int bit;
int err = 0;
/* Check boundary */
if (struct_addr < cache->start || struct_addr > cache->end) {
printk("Error: This address doesn't belong to this cache.\n");
return -1;
}
bit = ((struct_addr - cache->start) / cache->struct_size);
/* Check alignment:
* Find out if there was a lost remainder in last division.
* There shouldn't have been, because addresses are allocated at
* struct_size offsets from cache->start. */
if (((bit * cache->struct_size) + cache->start) != struct_addr) {
printk("Error: This address is not aligned on a predefined "
"structure address in this cache.\n");
err = -1;
return err;
}
/* NOTE: If needed, must lock here */
/* Check free/occupied state */
if (check_and_clear_bit(cache->bitmap, bit) < 0) {
printk("Error: Anomaly in cache occupied state:\n"
"Trying to free already free structure.\n");
err = -1;
goto out;
}
cache->free++;
if (cache->free > cache->total) {
printk("Error: Anomaly in cache occupied state:\n"
"More free elements than total.\n");
err = -1;
goto out;
}
out:
/* NOTE: If locked, must unlock here */
return err;
}
struct mem_cache *mem_cache_init(void *start,
int cache_size,
int struct_size,
unsigned int aligned)
{
struct mem_cache *cache = start;
unsigned int area_start;
unsigned int *bitmap;
int bwords_in_structs;
int bwords;
int total;
int bsize;
if ((struct_size < 0) || (cache_size < 0) ||
((unsigned long)start == ~(0))) {
printk("Invalid parameters.\n");
return 0;
}
/* The cache definition itself is at the beginning.
* Skipping it to get to start of free memory. i.e. the cache. */
area_start = (unsigned long)start + sizeof(struct mem_cache);
cache_size -= sizeof(struct mem_cache);
if (cache_size < struct_size) {
printk("Cache too small for given struct_size\n");
return 0;
}
/* Get how much bitmap words occupy */
total = cache_size / struct_size;
bwords = total >> 5; /* Divide by 32 */
if (total & 0x1F) { /* Remainder? */
bwords++; /* Add one more word for remainder */
}
bsize = bwords * 4;
/* This many structures will be chucked from cache for bitmap space */
bwords_in_structs = ((bsize) / struct_size) + 1;
/* Total structs left after deducing bitmaps */
total = total - bwords_in_structs;
cache_size -= bsize;
/* This should always catch too small caches */
if (total <= 0) {
printk("Cache too small for given struct_size\n");
return 0;
}
if (cache_size <= 0) {
printk("Cache too small for given struct_size\n");
return 0;
}
bitmap = (unsigned int *)area_start;
area_start = (unsigned int)(bitmap + bwords);
if (aligned) {
unsigned int addr = area_start;
unsigned int addr_aligned = align_up(area_start, struct_size);
unsigned int diff = addr_aligned - addr;
BUG_ON(diff >= struct_size);
if (diff)
total--;
cache_size -= diff;
area_start = addr_aligned;
}
link_init(&cache->list);
cache->start = area_start;
cache->end = area_start + cache_size;
cache->total = total;
cache->free = cache->total;
cache->struct_size = struct_size;
cache->bitmap = bitmap;
/* NOTE: If needed, must initialise lock here */
memset(cache->bitmap, 0, bwords*SZ_WORD);
return cache;
}

View File

@@ -0,0 +1,50 @@
/*
* Bitmap-based link-listable fixed-size memory cache.
*
* Copyright (C) 2007 Bahadir Balban
*/
#ifndef __MEMCACHE_H__
#define __MEMCACHE_H__
#include <l4/config.h>
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/lib/list.h>
/* Very basic cache structure. All it does is, keep an internal bitmap of
* items of struct_size. (Note bitmap is fairly efficient and simple for a
* fixed-size memory cache) Keeps track of free/occupied items within its
* start/end boundaries. Does not grow/shrink but you can link-list it. */
struct mem_cache {
struct link list;
int total;
int free;
unsigned int start;
unsigned int end;
unsigned int struct_size;
unsigned int *bitmap;
};
void *mem_cache_zalloc(struct mem_cache *cache);
void *mem_cache_alloc(struct mem_cache *cache);
int mem_cache_free(struct mem_cache *cache, void *addr);
struct mem_cache *mem_cache_init(void *start, int cache_size,
int struct_size, unsigned int alignment);
static inline int mem_cache_is_full(struct mem_cache *cache)
{
return cache->free == 0;
}
static inline int mem_cache_is_empty(struct mem_cache *cache)
{
return cache->free == cache->total;
}
static inline int mem_cache_is_last_free(struct mem_cache *cache)
{
return cache->free == 1;
}
static inline int mem_cache_total_empty(struct mem_cache *cache)
{
return cache->free;
}
#endif /* __MEMCACHE_H__ */

View File

@@ -0,0 +1,249 @@
/*
* A proof-of-concept linked-list based page allocator.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <stdio.h>
#include <string.h>
#include <l4/config.h>
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include "alloc_page.h"
#include INC_GLUE(memory.h)
#include INC_SUBARCH(mm.h)
#include INC_GLUE(memlayout.h)
struct page_allocator allocator;
/*
* Allocate a new page area from the page area cache
*/
static struct page_area *new_page_area(struct page_allocator *p)
{
struct mem_cache *cache;
struct page_area *new_area;
list_foreach_struct(cache, &p->pga_cache_list, list) {
if ((new_area = mem_cache_alloc(cache)) != 0) {
new_area->cache = cache;
p->pga_free--;
return new_area;
}
}
return 0;
}
/* Given the page @quantity, finds a free region, divides and returns new area. */
static struct page_area *
get_free_page_area(int quantity, struct page_allocator *p)
{
struct page_area *new, *area;
if (quantity <= 0)
return 0;
list_foreach_struct(area, &p->page_area_list, list) {
/* Check for exact size match */
if (area->numpages == quantity && !area->used) {
area->used = 1;
return area;
}
/* Divide a bigger area */
if (area->numpages > quantity && !area->used) {
new = new_page_area(p);
area->numpages -= quantity;
new->pfn = area->pfn + area->numpages;
new->numpages = quantity;
new->used = 1;
link_init(&new->list);
list_insert(&new->list, &area->list);
return new;
}
}
/* No more pages */
return 0;
}
/*
* All physical memory is tracked by a simple linked list implementation. A
* single list contains both used and free page_area descriptors. Each page_area
* describes a continuous region of physical pages, indicating its location by
* it's pfn.
*
* alloc_page() keeps track of all page-granuled memory, except the bits that
* were in use before the allocator initialised. This covers anything that is
* outside the @start @end range. This includes the page tables, first caches
* allocated by this function, compile-time allocated kernel data and text.
* Also other memory regions like IO are not tracked by alloc_page() but by
* other means.
*/
void init_page_allocator(unsigned long start, unsigned long end)
{
/* Initialise a page area cache in the first page */
struct page_area *freemem, *area;
struct mem_cache *cache;
link_init(&allocator.page_area_list);
link_init(&allocator.pga_cache_list);
/* Initialise the first page area cache */
cache = mem_cache_init(l4_map_helper((void *)start, 1), PAGE_SIZE,
sizeof(struct page_area), 0);
list_insert(&cache->list, &allocator.pga_cache_list);
/* Initialise the first area that describes the page just allocated */
area = mem_cache_alloc(cache);
link_init(&area->list);
area->pfn = __pfn(start);
area->used = 1;
area->numpages = 1;
area->cache = cache;
list_insert(&area->list, &allocator.page_area_list);
/* Update freemem start address */
start += PAGE_SIZE;
/* Initialise first area that describes all of free physical memory */
freemem = mem_cache_alloc(cache);
link_init(&freemem->list);
freemem->pfn = __pfn(start);
freemem->numpages = __pfn(end) - freemem->pfn;
freemem->cache = cache;
freemem->used = 0;
/* Add it as the first unused page area */
list_insert(&freemem->list, &allocator.page_area_list);
/* Initialise free page area counter */
allocator.pga_free = mem_cache_total_empty(cache);
}
/*
* Check if we're about to run out of free page area structures.
* If so, allocate a new cache of page areas.
*/
int check_page_areas(struct page_allocator *p)
{
struct page_area *new;
struct mem_cache *newcache;
void *newpage;
/* If only one free area left */
if (p->pga_free == 1) {
/* Use that area to allocate a new page */
if (!(new = get_free_page_area(1, p)))
return -1; /* Out of memory */
/* Free page areas must now be reduced to 0 */
BUG_ON(p->pga_free != 0);
/* Map the new page into virtual memory */
newpage = l4_map_helper((void *)__pfn_to_addr(new->pfn), 1);
/* Initialise it as a new source of page area structures */
newcache = mem_cache_init(newpage, PAGE_SIZE,
sizeof(struct page_area), 0);
/*
* Update the free page area counter
* NOTE: need to lock the allocator here
*/
p->pga_free += mem_cache_total_empty(newcache);
/*
* Add the new cache to available
* list of free page area caches
*/
list_insert(&newcache->list, &p->pga_cache_list);
/* Unlock here */
}
return 0;
}
void *alloc_page(int quantity)
{
struct page_area *new;
/*
* First make sure we have enough page
* area structures in the cache
*/
if (check_page_areas(&allocator) < 0)
return 0; /* Out of memory */
/*
* Now allocate the actual pages, using the available
* page area structures to describe the allocation
*/
new = get_free_page_area(quantity, &allocator);
/* Return physical address */
return (void *)__pfn_to_addr(new->pfn);
}
/* Merges two page areas, frees area cache if empty, returns the merged area. */
struct page_area *merge_free_areas(struct page_area *before,
struct page_area *after)
{
struct mem_cache *c;
BUG_ON(before->pfn + before->numpages != after->pfn);
BUG_ON(before->used || after->used)
BUG_ON(before == after);
before->numpages += after->numpages;
list_remove(&after->list);
c = after->cache;
mem_cache_free(c, after);
/* Recursively free the cache page */
if (mem_cache_is_empty(c)) {
list_remove(&c->list);
BUG_ON(free_page(l4_unmap_helper(c, 1)) < 0)
}
return before;
}
static int find_and_free_page_area(void *addr, struct page_allocator *p)
{
struct page_area *area, *prev, *next;
/* First find the page area to be freed. */
list_foreach_struct(area, &p->page_area_list, list)
if (__pfn_to_addr(area->pfn) == (unsigned long)addr &&
area->used) { /* Found it */
area->used = 0;
goto found;
}
return -1; /* Finished the loop, but area not found. */
found:
/* Now merge with adjacent areas, if possible */
if (area->list.prev != &p->page_area_list) {
prev = link_to_struct(area->list.prev, struct page_area, list);
if (!prev->used)
area = merge_free_areas(prev, area);
}
if (area->list.next != &p->page_area_list) {
next = link_to_struct(area->list.next, struct page_area, list);
if (!next->used)
area = merge_free_areas(area, next);
}
return 0;
}
int free_page(void *paddr)
{
return find_and_free_page_area(paddr, &allocator);
}

View File

@@ -0,0 +1,30 @@
#ifndef __ALLOC_PAGE_H__
#define __ALLOC_PAGE_H__
#include <memcache/memcache.h>
/* List member to keep track of free and unused physical pages.
* Has PAGE_SIZE granularity */
struct page_area {
struct link list;
unsigned int used; /* Used or free */
unsigned int pfn; /* Base pfn */
unsigned int numpages; /* Number of pages this region covers */
struct mem_cache *cache;/* The cache used when freeing the page area for
* quickly finding where the area is stored. */
};
struct page_allocator {
struct link page_area_list;
struct link pga_cache_list;
int pga_free;
};
/* Initialises the page allocator */
void init_page_allocator(unsigned long start, unsigned long end);
/* Page allocation functions */
void *alloc_page(int quantity);
int free_page(void *paddr);
#endif /* __ALLOC_PAGE_H__ */

View File

@@ -0,0 +1,110 @@
#!/usr/bin/python
import os
import shutil
from os.path import join
import sys
project_root = join(os.getcwd(), "../..")
source_root = os.path.join(project_root, 'src')
headers_root = os.path.join(project_root, 'include')
tests_run_root = os.path.join(os.getcwd(), 'tmp')
tools_root = os.getcwd()
init_state = "page_init.out"
exit_state = "page_exit.out"
SZ_10MB = 1024 * 1024 * 10
def power(x, y):
res = 1
for i in range(y):
res = res * x;
return res
def test_mm():
'''
Tries to set up meaningful input parameters for page_size, maximum allocation
size, total number of allocations, and total pages, and tests the memory allocator
in every one of these combinations. The parameter guessing is not great, but at least
some test cases are reasonable.
'''
page_sizes = [128, 256, 512, 1024, 2048, 4096, 8192]
max_alloc_sizes = [1, 10, 40, 50, 100, 200]
for page_size in page_sizes:
numpages = SZ_10MB / page_size
for i in range(1, 3):
res = numpages / power(10, i) # Divide numpages to 10, 100, 1000
if res > 0:
max_alloc_sizes.append(numpages/10)
max_alloc_sizes.append(numpages/100)
max_alloc_sizes.append(numpages/1000)
for max_alloc_size in max_alloc_sizes:
if max_alloc_size >= numpages: # If a single allocation exceeds total, adjust.
max_alloc_size = numpages / 2
num_allocs = numpages / (max_alloc_size) * 2 * 2 / 3
cmd = "./test -a=p -n=%d -s=%d -fi=%s -fx=%s -ps=%d -pn=%d" % \
(num_allocs, max_alloc_size, join(tests_run_root, init_state),\
join(tests_run_root, exit_state), page_size, numpages)
print "num_allocs = %d, max_alloc_size = %d, page_size = %d, numpages = %d" % \
(num_allocs, max_alloc_size, page_size, numpages)
os.system(cmd)
#os.system("cat %s" % join(tests_run_root, init_state))
diffcmd = "diff " + join(tests_run_root, init_state) + " " + join(tests_run_root, exit_state)
if os.system(diffcmd) != 0:
print "Error: %s has failed.\n" % cmd
sys.exit(1)
def test_km():
'''
Tries to set up meaningful input parameters for payload size, maximum allocation
size, total number of allocations, and total pages, and tests kmalloc
in every one of these combinations. The parameter guessing is not great, but at least
some test cases are reasonable.
'''
page_sizes = [4096, 8192]
max_alloc_sizes = [1, 10, 40, 50, 100, 200, 1024, 2048, 4096, 10000, 50000, 100000]
numpages = 1024
for page_size in page_sizes:
for max_alloc_size in max_alloc_sizes:
num_allocs = (numpages * page_size * 3) / (max_alloc_size * 2)
cmd = "./test -a=k -n=%d -s=%d -fi=%s -fx=%s -ps=%d -pn=%d" % \
(num_allocs, max_alloc_size, join(tests_run_root, init_state),\
join(tests_run_root, exit_state), page_size, numpages)
print "num_allocs = %d, max_alloc_size = %d, page_size = %d, numpages = %d" %\
(num_allocs, max_alloc_size, page_size, numpages)
diffcmd = "diff " + join(tests_run_root, init_state) + " " +\
join(tests_run_root, exit_state)
if os.system(diffcmd) != 0:
print "Error: %s has failed.\n" % cmd
sys.exit(1)
def test_mm_params(num_allocs, max_alloc_size, page_size, numpages, iterations):
for i in range(iterations):
cmd = "./test -a=p -n=%d -s=%d -fi=%s -fx=%s -ps=%d -pn=%d" % \
(num_allocs, max_alloc_size, join(tests_run_root, init_state),\
join(tests_run_root, exit_state), page_size, numpages)
print "num_allocs = %d, max_alloc_size = %d, page_size = %d, numpages = %d" % \
(num_allocs, max_alloc_size, page_size, numpages)
os.system(cmd)
#os.system("cat %s" % join(tests_run_root, init_state))
diffcmd = "diff " + join(tests_run_root, init_state) + " " + join(tests_run_root, exit_state)
if os.system(diffcmd) != 0:
print "Error: %s has failed.\n" % cmd
sys.exit(1)
def run_tests():
if os.path.exists(tests_run_root):
shutil.rmtree(tests_run_root)
os.mkdir(tests_run_root)
# for i in range (100):
#test_km()
test_mm()
#test_mm_params(10922, 10, 128, 81920, 50)
#test_km()
#test_mc()
if __name__ == '__main__':
run_tests()

View File

@@ -0,0 +1,16 @@
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/config.h>
/* Emulation of CLZ (count leading zeroes) instruction */
unsigned int __clz(unsigned int bitvector)
{
unsigned int x = 0;
while((!(bitvector & ((unsigned)1 << 31))) && (x < 32)) {
bitvector <<= 1;
x++;
}
return x;
}

View File

@@ -0,0 +1,7 @@
#ifndef __CLZ_H__
#define __CLZ_H__
unsigned int __clz(unsigned int bitvector);
#endif /* __CLZ_H__ */

View File

@@ -0,0 +1,33 @@
#include "debug.h"
#include <stdio.h>
void print_page_area_list(struct page_allocator *p)
{
struct page_area *area;
list_foreach_struct (area, &p->page_area_list, list) {
printf("%-20s\n%-20s\n", "Page area:","-------------------------");
printf("%-20s %u\n", "Pfn:", area->pfn);
printf("%-20s %d\n", "Used:", area->used);
printf("%-20s %d\n\n", "Number of pages:", area->numpages);
}
}
void print_km_area(struct km_area *s)
{
printf("%-20s\n%-20s\n", "Subpage area:","-------------------------");
printf("%-20s 0x%lu\n", "Addr:", s->vaddr);
printf("%-20s 0x%lu\n", "Size:", s->size);
printf("%-20s %d\n", "Used:", s->used);
printf("%-20s %d\n\n", "Head_of_pages:", s->pg_alloc_pages);
}
void print_km_area_list(struct link *km_areas)
{
struct km_area *area;
list_foreach_struct (area, km_areas, list)
print_km_area(area);
}

View File

@@ -0,0 +1,17 @@
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <kmalloc/kmalloc.h>
#include <mm/alloc_page.h>
#include <l4/lib/list.h>
#if defined(DEBUG)
#define dprintf printf
#else
#define dprintf(...)
#endif
void print_page_area_list(struct page_allocator *p);
void print_km_area_list(struct link *s);
void print_km_area(struct km_area *s);
#endif /* DEBUG_H */

View File

@@ -0,0 +1,28 @@
#include "libl4.h"
unsigned long virt_to_phys(unsigned long addr)
{
return addr;
}
unsigned long phys_to_virt(unsigned long addr)
{
return addr;
}
u32 l4_getpid(unsigned int *a, unsigned int *b, unsigned int *c)
{
return 0;
}
u32 l4_unmap(unsigned long a, unsigned long b, u32 npages)
{
return 0;
}
u32 l4_map(unsigned long a, unsigned long b, u32 size, u32 flags, unsigned int tid)
{
return 0;
}

View File

@@ -0,0 +1,17 @@
/*
* Mock-up l4 library definitions for host testing.
*
*/
#ifndef __TESTS_LIBL4_H__
#define __TESTS_LIBL4_H__
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
u32 l4_map(unsigned long phys, unsigned long virt, u32 size, u32 flags, u32 tid);
u32 l4_unmap(unsigned long a, unsigned long b, u32 npages);
u32 l4_getpid(unsigned int *a, unsigned int *b, unsigned int *c);
#endif

View File

View File

@@ -0,0 +1,250 @@
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <l4/macros.h>
#include <l4/config.h>
#include <kmalloc/kmalloc.h>
#include <mm/alloc_page.h>
#include INC_SUBARCH(mm.h)
#include INC_ARCH(linker.h)
#include INC_PLAT(printascii.h)
#include INC_PLAT(offsets.h)
#include INC_GLUE(memlayout.h)
#include "tests.h"
#include "test_kmalloc.h"
#include "test_allocpage.h"
#include "test_memcache.h"
#include "clz.h"
#include "memory.h"
#include "libl4.h"
#include "debug.h"
unsigned int TEST_PHYSMEM_TOTAL_PAGES = 250;
unsigned int TEST_PHYSMEM_TOTAL_SIZE;
unsigned int PHYS_MEM_START;
unsigned int PHYS_MEM_END;
void *malloced_test_memory;
void memory_initialise(void)
{
init_page_allocator(PHYS_MEM_START, PHYS_MEM_END);
kmalloc_init();
}
/* Allocating memory from the host C library, and
* it is used as if it is the physical memory available
* on the system.
*/
void alloc_test_memory()
{
TEST_PHYSMEM_TOTAL_SIZE = (PAGE_SIZE * TEST_PHYSMEM_TOTAL_PAGES);
if (!(malloced_test_memory = malloc(TEST_PHYSMEM_TOTAL_SIZE)))
printf("Host system out of memory.\n");
PHYS_MEM_START = (unsigned int)malloced_test_memory;
PHYS_MEM_END = PHYS_MEM_START + TEST_PHYSMEM_TOTAL_SIZE;
PHYS_MEM_START = page_align_up(PHYS_MEM_START);
PHYS_MEM_END = page_align(PHYS_MEM_END);
/* Normally _end is to know where the loaded kernel image
* ends in physical memory, so the system can start allocating
* physical memory from there. Because in our mock-up there's no
* used space in the malloc()'ed memory, _end is the same as the
* beginning of malloc()'ed memory.
*/
_end = PHYS_MEM_START;
dprintf("Initialising physical memory\n");
dprintf("Initialising allocators:\n");
memory_initialise();
}
struct cmdline_opts {
char run_allocator;
int allocations;
int alloc_size_max;
int physmem_pages;
int page_size;
int no_of_pages;
char *finit_path;
char *fexit_path;
} options;
int check_options_validity(struct cmdline_opts *opts)
{
if (opts->allocations <= 0) {
printf("Invalid number of allocations: %d\n", opts->allocations);
return -1;
}
if (opts->no_of_pages <= 0) {
printf("Invalid number of pages: %d\n", opts->no_of_pages);
return -1;
}
if (opts->alloc_size_max <= 0) {
printf("Invalid alloc_size_max: %d\n", opts->alloc_size_max);
return -1;
}
if (opts->page_size <= 0) {
printf("Invalid page_size: %d\n", opts->page_size);
return -1;
}
return 0;
}
void print_options(struct cmdline_opts *opts)
{
dprintf("Running: %s\n",
((opts->run_allocator == 'p') ? "page allocator" :
((opts->run_allocator == 'k') ? "kmalloc/kfree" :
"memcache allocator")));
dprintf("Total allocations: %d\n", opts->allocations);
dprintf("Maximum allocation size: %d, 0x%x(hex)\n\n",
opts->alloc_size_max, opts->alloc_size_max);
dprintf("Initial state file: %s\n", opts->finit_path);
dprintf("Exit state file: %s\n", opts->fexit_path);
}
void display_help(void)
{
printf("Main:\n");
printf("\tUsage:\n");
printf("\tmain\t-a=<p>|<k>|<m> [-n=<number of allocations>] [-s=<maximum size for any allocation>]\n"
"\t\t[-fi=<file to dump init state>] [-fx=<file to dump exit state>]\n"
"\t\t[-ps=<page size>] [-pn=<total number of pages>]\n");
printf("\n");
}
int get_cmdline_opts(int argc, char *argv[], struct cmdline_opts *opts)
{
int parsed = 0;
memset(opts, 0, sizeof (struct cmdline_opts));
if (argc <= 1)
return -1;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][2] == '=') {
if (argv[i][1] == 'a') {
if (argv[i][3] == 'k' ||
argv[i][3] == 'm' ||
argv[i][3] == 'p') {
opts->run_allocator = argv[i][3];
parsed = 1;
}
}
if (argv[i][1] == 'n') {
opts->allocations = atoi(&argv[i][3]);
parsed = 1;
}
if (argv[i][1] == 's') {
opts->alloc_size_max = atoi(&argv[i][3]);
parsed = 1;
}
}
if (argv[i][0] == '-' && argv[i][1] == 'f'
&& argv[i][3] == '=') {
if (argv[i][2] == 'i') {
opts->finit_path = &argv[i][4];
parsed = 1;
}
if (argv[i][2] == 'x') {
opts->fexit_path = &argv[i][4];
parsed = 1;
}
}
if (argv[i][0] == '-' && argv[i][1] == 'p'
&& argv[i][3] == '=') {
if (argv[i][2] == 's') {
opts->page_size = atoi(&argv[i][4]);
parsed = 1;
}
if (argv[i][2] == 'n') {
opts->no_of_pages = atoi(&argv[i][4]);
parsed = 1;
}
}
}
if (!parsed)
return -1;
return 0;
}
void get_output_files(FILE **out1, FILE **out2,
char *alloc_func_name, char *rootpath)
{
char pathbuf[150];
char *root = "/tmp/";
char *initstate_prefix = "test_initstate_";
char *endstate_prefix = "test_endstate_";
char *extension = ".out";
if (!rootpath)
rootpath = root;
/* File path manipulations */
sprintf(pathbuf, "%s%s%s%s", rootpath, initstate_prefix, alloc_func_name, extension);
*out1 = fopen(pathbuf,"w+");
sprintf(pathbuf, "%s%s%s%s", rootpath, endstate_prefix, alloc_func_name, extension);
*out2 = fopen(pathbuf, "w+");
return;
}
int main(int argc, char *argv[])
{
FILE *finit, *fexit;
int output_files = 0;
if (get_cmdline_opts(argc, argv, &options) < 0) {
display_help();
return 1;
}
print_options(&options);
if (check_options_validity(&options) < 0)
exit(1);
if (options.finit_path && options.fexit_path) {
finit = fopen(options.finit_path, "w+");
fexit = fopen(options.fexit_path, "w+");
output_files = 1;
}
if (options.page_size) {
PAGE_SIZE = options.page_size;
PAGE_MASK = PAGE_SIZE - 1;
PAGE_BITS = 32 - __clz(PAGE_MASK);
dprintf("Using: Page Size: %d\n", PAGE_SIZE);
dprintf("Using: Page Mask: 0x%x\n", PAGE_MASK);
dprintf("Using: Page Bits: %d\n", PAGE_BITS);
}
if (options.no_of_pages) {
dprintf("Using: Total pages: %d\n", options.no_of_pages);
TEST_PHYSMEM_TOTAL_PAGES = options.no_of_pages;
}
alloc_test_memory();
if (options.run_allocator == 'p') {
if (!output_files)
get_output_files(&finit, &fexit, "alloc_page", 0);
test_allocpage(options.allocations, options.alloc_size_max,
finit, fexit);
} else if (options.run_allocator == 'k') {
if (!output_files)
get_output_files(&finit, &fexit, "kmalloc", 0);
test_kmalloc(options.allocations, options.alloc_size_max,
finit, fexit);
} else if (options.run_allocator == 'm') {
if (!output_files)
get_output_files(&finit, &fexit, "memcache", 0);
test_memcache(options.allocations, options.alloc_size_max,
finit, fexit, 1);
} else {
printf("Invalid allocator option.\n");
}
free((void *)malloced_test_memory);
fclose(finit);
fclose(fexit);
return 0;
}

View File

@@ -0,0 +1,9 @@
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
unsigned int PAGE_SIZE = TEST_PAGE_SIZE;
unsigned int PAGE_MASK = TEST_PAGE_MASK;
unsigned int PAGE_BITS = TEST_PAGE_BITS;

View File

@@ -0,0 +1,216 @@
/*
* Generic random allocation/deallocation test
*
* Copyright 2007 (C) Bahadir Balban
*
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
#include <l4/lib/list.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "test_alloc_generic.h"
#include "debug.h"
void print_test_state(unsigned int title,
print_alloc_state_t print_allocator_state)
{
switch (title) {
case TEST_STATE_BEGIN:
printf("=================\n"
"===== BEGIN =====\n"
"=================\n\n");
break;
case TEST_STATE_MIDDLE:
printf("==================\n"
"===== MIDDLE =====\n"
"==================\n\n");
break;
case TEST_STATE_END:
printf("===========\n"
"=== END ===\n"
"===========\n\n");
break;
case TEST_STATE_ERROR:
printf("=================\n"
"===== ERROR =====\n"
"=================\n\n");
break;
default:
printf("Title error.\n");
}
print_allocator_state();
}
void get_output_filepaths(FILE **out1, FILE **out2,
char *alloc_func_name)
{
char pathbuf[150];
char *rootpath = "/tmp/";
char *initstate_prefix = "test_initstate_";
char *endstate_prefix = "test_endstate_";
char *extention = ".out";
/* File path manipulations */
sprintf(pathbuf, "%s%s%s%s", rootpath, initstate_prefix, alloc_func_name, extention);
*out1 = fopen(pathbuf,"w+");
sprintf(pathbuf, "%s%s%s%s", rootpath, endstate_prefix, alloc_func_name, extention);
*out2 = fopen(pathbuf, "w+");
return;
}
/* This function is at the heart of generic random allocation testing.
* It is made as simple as possible, and can be used for testing all
* allocators. It randomly allocates/deallocates data and prints out
* the outcome of the action. Here are a few things it does and doesn't
* do:
* - It does not test false input on the allocators, e.g. attempting
* to free an address that hasn't been allocated, or attempting to
* free address 0.
* - It does capture and compare initial and final states of the
* allocators' internal structures after all allocations are freed.
* This is done by comparing two files filled with allocator state
* by functions supplied by the allocators themselves.
* - It expects the allocator NOT to run out of memory.
*/
int
test_alloc_free_random_order(const int MAX_ALLOCATIONS,
const int ALLOC_SIZE_MAX,
alloc_func_t alloc,
free_func_t free,
print_alloc_state_t print_allocator_state,
FILE *state_init_file, FILE *state_end_file)
{
/* The last element in full_state that tells about any full index.
* This is the limit the random deallocation would use to find a full
* index */
int random_size;
int random_action;
int random_index;
int alloc_so_far = 0;
int full_state_last = -1;
int halfway_through = 0;
FILE * const default_stdout = stdout;
/* Memory pointers */
void *mem[MAX_ALLOCATIONS];
/* Each element keeps track of one currently full index number */
int full_state[MAX_ALLOCATIONS];
/* Check arguments first */
if (!MAX_ALLOCATIONS || !ALLOC_SIZE_MAX || !alloc || !free
|| !print_allocator_state || !state_init_file || !state_end_file) {
printf("Invalid arguments to %s()\n", __FUNCTION__);
return 1;
}
memset(mem, 0, MAX_ALLOCATIONS * sizeof(void *));
memset(full_state, 0, MAX_ALLOCATIONS * sizeof(int));
//print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = state_init_file;
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = default_stdout;
/* Randomly either allocate/deallocate at a random
* index, of random size */
srand(time(0));
while (1) {
if (alloc_so_far < (MAX_ALLOCATIONS / 2)) {
/* Give more chance to allocations at the beginning */
if ((rand() % 4) == 0) /* 1/4 chance */
random_action = FREE;
else /* 3/4 chance */
random_action = ALLOCATE;
} else {
if (!halfway_through) {
#if defined (DEBUG)
print_test_state(TEST_STATE_MIDDLE,
print_allocator_state);
#endif
halfway_through = 1;
}
/* Give more chane to freeing after halfway-through */
if ((rand() % 3) == 0) /* 1/3 chance */
random_action = ALLOCATE;
else /* 2/3 chance */
random_action = FREE;
}
random_size = (rand() % (ALLOC_SIZE_MAX-1)) + 1;
if (random_action == ALLOCATE) {
if (alloc_so_far < MAX_ALLOCATIONS) {
alloc_so_far++;
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
if (mem[i] == 0) { // Find the first empty slot.
int allocation_error =
((mem[i] = alloc(random_size)) <= 0);
dprintf("%-12s%-8s%-12p%-8s%-10d\n",
"alloc:", "addr:", mem[i],
"size:", random_size);
if (allocation_error) {
print_test_state(TEST_STATE_ERROR,
print_allocator_state);
if (mem[i] < 0) {
printf("Error: alloc() returned negative value\n");
BUG();
} else if (mem[i] == 0) {
printf("Error: Allocator is out of memory.\n");
return 1;
}
}
full_state_last++;
full_state[full_state_last] = i;
break;
}
}
} else
random_action = FREE;
}
if (random_action == FREE) {
/* all are free, can't free anymore */
if (full_state_last < 0)
continue;
else if (full_state_last > 0)
random_index = rand() % full_state_last;
else
random_index = 0; /* Last item */
if(mem[full_state[random_index]] == 0)
BUG();
if (free(mem[full_state[random_index]]) < 0)
BUG();
dprintf("%-12s%-8s%-12p\n","free:",
"addr:", mem[full_state[random_index]]);
mem[full_state[random_index]] = 0;
/* Fill in the empty gap with last element */
full_state[random_index] = full_state[full_state_last];
/* Last element now in the gap
* (somewhere inbetween first and last) */
full_state[full_state_last] = 0;
/* One less in the number of full items */
full_state_last--;
}
/* Check that all allocations and deallocations took place */
if (alloc_so_far == MAX_ALLOCATIONS && full_state_last < 0) {
for (int i = 0; i < MAX_ALLOCATIONS; i++)
BUG_ON(full_state[i] != 0); // A final sanity check.
break;
}
}
//print_test_state(TEST_STATE_END, print_allocator_state);
stdout = state_end_file;
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = default_stdout;
return 0;
}

View File

@@ -0,0 +1,29 @@
#ifndef __TEST_ALLOC_GENERIC_H__
#define __TEST_ALLOC_GENERIC_H__
enum test_state_title {
TEST_STATE_BEGIN = 0,
TEST_STATE_MIDDLE,
TEST_STATE_END,
TEST_STATE_ERROR
};
typedef void (*print_alloc_state_t)(void);
typedef void *(*alloc_func_t)(int size);
typedef int (*free_func_t)(void *addr);
enum alloc_action {
FREE = 0,
ALLOCATE = 1,
};
void get_output_filepaths(FILE **out1, FILE **out2,
char *alloc_func_name);
int test_alloc_free_random_order(const int MAX_ALLOCATIONS,
const int ALLOC_SIZE_MAX,
alloc_func_t alloc, free_func_t free,
print_alloc_state_t print_allocator_state,
FILE *init_state, FILE *exit_state);
#endif /* __TEST_ALLOC_GENERIC_H__ */

View File

@@ -0,0 +1,85 @@
/*
* Testing code for the page allocator.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include INC_GLUE(memory.h)
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "test_allocpage.h"
#include "test_alloc_generic.h"
#include "debug.h"
unsigned int PAGE_ALLOCATIONS = 30;
unsigned int PAGE_ALLOC_SIZE_MAX = 8;
extern struct page_allocator allocator;
void print_page_area(struct page_area *a, int areano)
{
printf("Area starts @: 0x%lu, %s, numpages: %d\n",
__pfn_to_addr(a->pfn),
(a->used) ? "used" : "unused", a->numpages);
return;
}
void print_areas(struct link *area_head)
{
struct page_area *cur;
int areano = 1;
printf("Page areas:\n-------------\n");
list_foreach_struct(cur, area_head, list)
print_page_area(cur, areano++);
}
void print_cache(struct mem_cache *c, int cacheno)
{
printf("Cache %d state:\n-------------\n", cacheno);
printf("Total: %d\n", c->total);
printf("Free: %d\n", c->free);
printf("Start: 0x%x\n", c->start);
}
void print_caches(struct link *cache_head)
{
int caches = 1;
struct mem_cache *cur;
list_foreach_struct(cur, cache_head, list)
print_cache(cur, caches++);
}
void print_page_allocator_state(void)
{
print_areas(&allocator.page_area_list);
printf("Data Cache:\n--------\n");
print_caches(&allocator.dcache_list);
printf("Cache Cache:\n----------\n");
print_caches(&allocator.ccache_list);
}
/* FIXME: with current default parameters (allocations = 30, sizemax = 8),
* for some odd reason, we got the bug at line 280 in alloc_page.c.
* Very weird. Find out why.
*/
void test_allocpage(int page_allocations, int page_alloc_size_max,
FILE *init_state, FILE *exit_state)
{
//if (!page_allocations)
// page_allocations = PAGE_ALLOCATIONS;
//if (!page_alloc_size_max)
// page_alloc_size_max = PAGE_ALLOC_SIZE_MAX;
dprintf("\nPAGE ALLOCATOR TEST:====================================\n\n");
test_alloc_free_random_order(page_allocations, page_alloc_size_max,
alloc_page, free_page,
print_page_allocator_state,
init_state, exit_state);
}

View File

@@ -0,0 +1,13 @@
#ifndef __TEST_ALLOCPAGE_H__
#define __TEST_ALLOCPAGE_H__
#include <mm/alloc_page.h>
#include "tests.h"
void test_allocpage(int num_allocs, int alloc_max, FILE *init, FILE *exit);
void print_page_area(struct page_area *a, int no);
void print_caches(struct link *cache_head);
void print_cache(struct mem_cache *c, int cacheno);
void print_areas(struct link *area_head);
void print_page_area(struct page_area *ar, int areano);
#endif

View File

@@ -0,0 +1,42 @@
/*
* Testing code for the kmalloc allocator.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include INC_GLUE(memory.h)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "test_alloc_generic.h"
#include "test_allocpage.h"
#include "debug.h"
#include "tests.h"
extern struct link km_area_start;
void print_kmalloc_state(void)
{
print_km_area_list(&km_area_start);
}
void test_kmalloc(int kmalloc_allocations, int kmalloc_alloc_size_max,
FILE *init_state, FILE *exit_state)
{
unsigned int KMALLOC_ALLOCATIONS = 20;
unsigned int KMALLOC_ALLOC_SIZE_MAX = (PAGE_SIZE * 3);
if (!kmalloc_allocations)
kmalloc_allocations = KMALLOC_ALLOCATIONS;
if (!kmalloc_alloc_size_max)
kmalloc_alloc_size_max = KMALLOC_ALLOC_SIZE_MAX;
test_alloc_free_random_order(kmalloc_allocations, kmalloc_alloc_size_max,
kmalloc, kfree, print_kmalloc_state,
init_state, exit_state);
}

View File

@@ -0,0 +1,8 @@
#ifndef __TEST_KMALLOC_H__
#define __TEST_KMALLOC_H__
#include <kmalloc/kmalloc.h>
void test_kmalloc(int num_allocs, int allocs_max, FILE *initstate, FILE *exitstate);
#endif

View File

@@ -0,0 +1,115 @@
/*
* Testing code for the memcache structure.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memcache/memcache.h>
#include "test_memcache.h"
#include "test_alloc_generic.h"
#include "debug.h"
#include "tests.h"
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include INC_GLUE(memory.h)
unsigned int MEM_CACHE_SIZE;
struct mem_cache *this;
void *buffer;
void *mem_cache_alloc_wrapped(int size)
{
return mem_cache_alloc(this);
}
int mem_cache_free_wrapped(void *addr)
{
return mem_cache_free(this, addr);
}
void print_memcache_state(void)
{
printf("%-15s%d\n","Total:", this->total);
printf("%-15s%d\n","Free:", this->free);
printf("Bitmap has %d words:\n", BITWISE_GETWORD(this->total) + 1);
for (int i = 0; i <= BITWISE_GETWORD(this->total); i++)
printf("0x%x\n", this->bitmap[i]);
}
int test_memcache_init_aligned(int *items_max, int item_size)
{
if (item_size * 10 > MEM_CACHE_SIZE)
MEM_CACHE_SIZE = item_size * 10;
if (!(buffer = calloc(1, MEM_CACHE_SIZE))) {
printf("System out of memory.\n");
BUG();
}
if ((this = mem_cache_init(buffer, MEM_CACHE_SIZE,
item_size, 1)) == 0) {
printf("Unable to initialise cache.\n");
return -1;
}
*items_max = mem_cache_total_empty(this);
printf("\nMEMCACHE TEST: ALIGNED ELEMENTS\n==========================\n");
printf("%-30s%d\n", "Item size:", item_size);
printf("%-30s0x%x\n", "Cache occupied space:", MEM_CACHE_SIZE);
printf("%-30s%d\n","Total items in cache:", *items_max);
printf("%-30s0x%x\n","Total items space:", (*items_max * item_size));
return 0;
}
int test_memcache_init(int *items_max, int item_size)
{
if (item_size * 10 > MEM_CACHE_SIZE)
MEM_CACHE_SIZE = item_size * 10;
printf("%s: Allocating cache memory.\n",__FUNCTION__);
if (!(buffer = calloc(1, MEM_CACHE_SIZE))) {
printf("System out of memory.\n");
BUG();
}
if ((this = mem_cache_init(buffer, MEM_CACHE_SIZE,
item_size, 0)) == 0) {
printf("Unable to initialise cache.\n");
return -1;
}
*items_max = mem_cache_total_empty(this);
printf("\nMEMCACHE TEST:\n========================\n");
printf("%-30s%d\n", "Item size:", item_size);
printf("%-30s0x%x\n", "Cache occupied space:", MEM_CACHE_SIZE);
printf("%-30s%d\n","Total items in cache:", *items_max);
printf("%-30s0x%x\n","Total items space:", (*items_max * item_size));
return 0;
}
int test_memcache(int items_max, int item_size, FILE *init_state, FILE *exit_state, int aligned)
{
const unsigned int TEST_CACHE_ITEM_SIZE = 5;
MEM_CACHE_SIZE = PAGE_SIZE * 5;
if (!item_size)
item_size = TEST_CACHE_ITEM_SIZE;
/* items_max value is ignored and overwritten because caches have fixed size. */
test_memcache_init(&items_max, item_size);
test_alloc_free_random_order(items_max, /* unused */ 2, mem_cache_alloc_wrapped,
mem_cache_free_wrapped, print_memcache_state,
init_state, exit_state);
free(buffer);
if (aligned) {
test_memcache_init_aligned(&items_max, item_size);
test_alloc_free_random_order(items_max, /* unused */ 2, mem_cache_alloc_wrapped,
mem_cache_free_wrapped, print_memcache_state,
init_state, exit_state);
}
free(buffer);
return 0;
}

View File

@@ -0,0 +1,10 @@
#ifndef __TEST_MEMCACHE_H__
#define __TEST_MEMCACHE_H__
#include <memcache/memcache.h>
int test_memcache(int num_alloc, int alloc_size_max, FILE *initstate, FILE *exitstate, int aligned);
#endif /* __TEST_MEMCACHE_H__ */

View File

@@ -0,0 +1,21 @@
#ifndef __TESTS_H__
#define __TESTS_H__
/* Mock-up physical memory */
extern unsigned int TEST_PHYSMEM_TOTAL_PAGES;
extern unsigned int TEST_PHYSMEM_TOTAL_SIZE;
/* Allocator test */
extern unsigned int PAGE_ALLOCATIONS;
extern unsigned int PAGE_ALLOC_SIZE_MAX;
/* Memcache test */
extern unsigned int MEMCACHE_ALLOCS_MAX;
extern unsigned int TEST_CACHE_ITEM_SIZE;
/* Kmalloc */
extern unsigned int KMALLOC_ALLOCATIONS;
extern unsigned int KMALLOC_ALLOC_SIZE_MAX;
#endif /* __TESTS_H__ */

View File

@@ -0,0 +1,80 @@
libposix
Copyright (C) 2007 Bahadir Balban
Despite the name, this is a library that supports only a small portion of posix functions.
Highest priority POSIX functions are:
shmat
shmget
shmdt
mmap
munmap
sbrk
read
readdir
write
lseek
open
close
creat
mkdir
mknod
link
unlink
fork
clone
execve
getpid
wait
kill
getenv
setenv
Currently supported functions are:
shmat
shmget
shmdt
mmap
munmap
read
readdir
write
lseek
open
close
creat
mkdir
mknod
fork
clone
execve
exit
getpid
Functions to be supported in the near future are:
link
unlink
wait
kill
sbrk
getenv
setenv
Other calls:
pipe
mount
unmount
swapon
New ones will be added as needed.

View File

@@ -0,0 +1,33 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
# Author: Russel Winder
Import('environment', 'posixServicesDirectory')
e = environment.Clone()
e.Append(CPPPATH = ['include', 'include/posix', '#' + posixServicesDirectory + 'libl4/include'])
# TODO: There are errors in this code that -Werror gives problems with.
e['CCFLAGS'] = ['-g', '-nostdlib', '-Wall', '-ffreestanding', '-std=gnu99']
objects = e.StaticObject(Glob('*.c'))
Depends(objects, e['configFiles'])
library = e.StaticLibrary('posix', objects)
Return('library')

View File

@@ -0,0 +1,54 @@
/*
* l4/posix glue for mkdir()
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <shpage.h>
#include <libposix.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/ipcdefs.h>
#include <l4lib/utcb.h>
#include <l4/macros.h>
#include INC_GLUE(memory.h)
static inline int l4_chdir(const char *pathname)
{
int fd;
// write_mr(L4SYS_ARG0, (unsigned long)pathname);
copy_to_shpage((void *)pathname, 0, strlen(pathname) + 1);
write_mr(L4SYS_ARG0, (unsigned long)shared_page);
/* Call pager with shmget() request. Check ipc error. */
if ((fd = l4_sendrecv(VFS_TID, VFS_TID, L4_IPC_TAG_CHDIR)) < 0) {
print_err("%s: L4 IPC Error: %d.\n", __FUNCTION__, fd);
return fd;
}
/* Check if syscall itself was successful */
if ((fd = l4_get_retval()) < 0) {
print_err("%s: MKDIR Error: %d.\n", __FUNCTION__, fd);
return fd;
}
return fd;
}
int chdir(const char *pathname)
{
int ret;
/* If error, return positive error code */
if ((ret = l4_chdir(pathname)) < 0) {
errno = -ret;
return -1;
}
/* else return value */
return ret;
}

View File

@@ -0,0 +1,78 @@
/*
* l4/posix glue for close() and fsync()
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/ipcdefs.h>
#include <l4lib/utcb.h>
#include <l4/macros.h>
#include INC_GLUE(memory.h)
#include <libposix.h>
static inline int l4_close(int fd)
{
write_mr(L4SYS_ARG0, fd);
/* Call pager with close() request. Check ipc error. */
if ((fd = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_CLOSE)) < 0) {
print_err("%s: L4 IPC Error: %d.\n", __FUNCTION__, fd);
return fd;
}
/* Check if syscall itself was successful */
if ((fd = l4_get_retval()) < 0) {
print_err("%s: CLOSE Error: %d.\n", __FUNCTION__, fd);
return fd;
}
return fd;
}
int close(int fd)
{
int ret = l4_close(fd);
/* If error, return positive error code */
if (ret < 0) {
errno = -ret;
return -1;
}
/* else return value */
return ret;
}
static inline int l4_fsync(int fd)
{
write_mr(L4SYS_ARG0, fd);
/* Call pager with close() request. Check ipc error. */
if ((fd = l4_sendrecv(PAGER_TID, PAGER_TID, L4_IPC_TAG_FSYNC)) < 0) {
print_err("%s: L4 IPC Error: %d.\n", __FUNCTION__, fd);
return fd;
}
/* Check if syscall itself was successful */
if ((fd = l4_get_retval()) < 0) {
print_err("%s: CLOSE Error: %d.\n", __FUNCTION__, fd);
return fd;
}
return fd;
}
int fsync(int fd)
{
int ret = l4_fsync(fd);
/* If error, return positive error code */
if (ret < 0) {
errno = -ret;
return -1;
}
/* else return value */
return ret;
}

View File

@@ -0,0 +1,35 @@
/*
* Environment accessor functions
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <string.h>
#include <stdlib.h>
#include <libposix.h>
char **__environ;
/*
* Search for given name in name=value string pairs located
* in the environment segment, and return the pointer to value
* string.
*/
char *getenv(const char *name)
{
char **envp = __environ;
int length;
if (!envp)
return 0;
length = strlen(name);
while(*envp) {
if (memcmp(name, *envp, length) == 0 &&
(*envp)[length] == '=')
return *envp + length + 1;
envp++;
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More