From e0f188d739f13aa16ec06fa897d92624f6b19fb6 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 10 Jul 2015 07:30:45 +0200 Subject: [PATCH] add virtual filesystem and support of an initrd - initrd is embedded in the kernel --- hermit/Makefile | 11 +- hermit/fs/Makefile | 4 + hermit/fs/fs.c | 246 ++++++++++++++++ hermit/fs/initrd.c | 578 +++++++++++++++++++++++++++++++++++++ hermit/include/hermit/fs.h | 293 +++++++++++++++++++ hermit/kernel/main.c | 9 + hermit/link.ld | 4 + hermit/tools/Makefile | 39 ++- hermit/tools/initrd.S | 2 + hermit/tools/make_initrd.c | 134 +++++++++ 10 files changed, 1311 insertions(+), 9 deletions(-) create mode 100644 hermit/fs/Makefile create mode 100644 hermit/fs/fs.c create mode 100644 hermit/fs/initrd.c create mode 100644 hermit/include/hermit/fs.h create mode 100644 hermit/tools/initrd.S create mode 100644 hermit/tools/make_initrd.c diff --git a/hermit/Makefile b/hermit/Makefile index 4dd09144b..7880a4fdd 100644 --- a/hermit/Makefile +++ b/hermit/Makefile @@ -3,7 +3,7 @@ TOPDIR := $(shell pwd) ARCH = x86 NAME = hermit LWIPDIRS = lwip/src/arch lwip/src/api lwip/src/core lwip/src/core/ipv4 lwip/src/netif -KERNDIRS = kernel mm libkern arch/$(ARCH)/kernel arch/$(ARCH)/mm $(LWIPDIRS) +KERNDIRS = kernel mm libkern fs arch/$(ARCH)/kernel arch/$(ARCH)/mm $(LWIPDIRS) SUBDIRS = $(KERNDIRS) GIT_VERSION := $(shell git describe --abbrev=6 --dirty --always --tags) TODAY := $(shell date +'%Y%m%d') @@ -70,7 +70,7 @@ endif default: all -all: newlib $(NAME).elf +all: newlib tools $(NAME).elf newlib: $Q$(MAKE) ARCH=$(ARCH) \ @@ -94,9 +94,10 @@ tools: $Q$(MAKE) ARCH=$(ARCH) CFLAGS="$(CFLAGS_FOR_TOOLS)" LDFLAGS="$(LDFLAGS_FOR_TOOLS)" -C tools $(NAME).elf: - $Q$(LD_FOR_TARGET) $(LDFLAGS) -o $(NAME).elf $^ + @echo [LD] $(NAME).elf + $Q$(LD_FOR_TARGET) $(LDFLAGS) -o $(NAME).elf $^ tools/initrd.o @echo [OBJCOPY] $(NAME).bin - $Q$(OBJCOPY_FOR_TARGET) -j .mboot -j .kmsg -j .text -j .text.startup -j .data -j .rodata -j .bss -O binary $(NAME).elf $(NAME).bin + $Q$(OBJCOPY_FOR_TARGET) -j .mboot -j .kmsg -j .initrd -j .text -j .text.startup -j .data -j .rodata -j .bss -O binary $(NAME).elf $(NAME).bin $(NAME).elf32: $(NAME).elf @echo [OBJCOPY] $(NAME).sym @@ -149,6 +150,6 @@ include/hermit/config.inc: include/hermit/config.h @echo [GCC-ASM] $@ $Q$(CC_FOR_TARGET) $(CFLAGS) -c -o $@ $< -.PHONY: default all clean emu gdb newlib +.PHONY: default all clean emu gdb newlib tools include $(addsuffix /Makefile,$(SUBDIRS)) diff --git a/hermit/fs/Makefile b/hermit/fs/Makefile new file mode 100644 index 000000000..a3f281a56 --- /dev/null +++ b/hermit/fs/Makefile @@ -0,0 +1,4 @@ +C_source := fs.c initrd.c +MODULE := fs + +include $(TOPDIR)/Makefile.inc diff --git a/hermit/fs/fs.c b/hermit/fs/fs.c new file mode 100644 index 000000000..e74281d9f --- /dev/null +++ b/hermit/fs/fs.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +vfs_node_t* fs_root = NULL; // The root of the filesystem. + +ssize_t read_fs(fildes_t* file, uint8_t* buffer, size_t size) +{ + vfs_node_t* node = file->node; + ssize_t ret = -EINVAL; + + if (BUILTIN_EXPECT(!node || !buffer, 0)) + return ret; + + spinlock_lock(&node->lock); + // Has the node got a read callback? + if (node->read != 0) + ret = node->read(file, buffer, size); + spinlock_unlock(&node->lock); + + return ret; +} + +ssize_t write_fs(fildes_t* file, uint8_t* buffer, size_t size) +{ + vfs_node_t* node = file->node; + ssize_t ret = -EINVAL; + + if (BUILTIN_EXPECT(!node || !buffer, 0)) + return ret; + + spinlock_lock(&node->lock); + // Has the node got a write callback? + if (node->write != 0) + ret = node->write(file, buffer, size); + spinlock_unlock(&node->lock); + + return ret; +} + +int open_fs(fildes_t* file, const char* name) +{ + uint32_t ret = 0, i, j = 1; + vfs_node_t* file_node = NULL; /* file node */ + vfs_node_t* dir_node = NULL; + char fname[MAX_FNAME]; + + if (BUILTIN_EXPECT(!name, 0)) + return ret; + + if (name[0] == '/') + file_node = fs_root; + + while((name[j] != '\0') || ((file_node != NULL) && (file_node->type == FS_DIRECTORY))) { + i = 0; + while((name[j] != '/') && (name[j] != '\0')) { + fname[i] = name[j]; + i++; j++; + } + fname[i] = '\0'; + dir_node = file_node; /* file must be a directory */ + file_node = finddir_fs(dir_node, fname); + if (name[j] == '/') + j++; + } + + //kprintf("dir_node = %p, file_node = %p, name = %s \n", dir_node, file_node, fname); + + /* file exists */ + if(file_node) { + spinlock_lock(&file_node->lock); + file->node = file_node; + // Has the file_node got an open callback? + if (file_node->open != 0) + ret = file->node->open(file, NULL); + spinlock_unlock(&file_node->lock); + } else if (dir_node) { /* file doesn't exist or opendir was called */ + spinlock_lock(&dir_node->lock); + file->node = dir_node; + // Has the dir_node got an open callback? + if (dir_node->open != 0) + ret = dir_node->open(file, fname); + spinlock_unlock(&dir_node->lock); + } else { + ret = -ENOENT; + } + + return ret; +} + +int close_fs(fildes_t* file) +{ + int ret = -EINVAL; + + if (BUILTIN_EXPECT(!(file->node), 0)) + return ret; + + spinlock_lock(&file->node->lock); + // Has the node got a close callback? + if (file->node->close != 0) + ret = file->node->close(file); + spinlock_unlock(&file->node->lock); + + return ret; +} + +struct dirent* readdir_fs(vfs_node_t * node, uint32_t index) +{ + struct dirent* ret = NULL; + + if (BUILTIN_EXPECT(!node, 0)) + return ret; + + spinlock_lock(&node->lock); + // Is the node a directory, and does it have a callback? + if ((node->type == FS_DIRECTORY) && node->readdir != 0) + ret = node->readdir(node, index); + spinlock_unlock(&node->lock); + + return ret; +} + +vfs_node_t* finddir_fs(vfs_node_t* node, const char *name) +{ + vfs_node_t* ret = NULL; + + if (BUILTIN_EXPECT(!node, 0)) + return ret; + + spinlock_lock(&node->lock); + // Is the node a directory, and does it have a callback? + if ((node->type == FS_DIRECTORY) && node->finddir != 0) + ret = node->finddir(node, name); + spinlock_unlock(&node->lock); + + return ret; +} + +vfs_node_t* mkdir_fs(vfs_node_t* node, const char *name) +{ + vfs_node_t* ret = NULL; + + if (BUILTIN_EXPECT(!node, 0)) + return ret; + + spinlock_lock(&node->lock); + if (node->mkdir != 0) + ret = node->mkdir(node, name); + spinlock_unlock(&node->lock); + + return ret; +} + +vfs_node_t* findnode_fs(const char* name) +{ + uint32_t i, j = 1; + vfs_node_t* ret = NULL; + char fname[MAX_FNAME]; + + if (BUILTIN_EXPECT(!name, 0)) + return ret; + + if (name[0] == '/') + ret = fs_root; + + while((name[j] != '\0') && ret) { + i = 0; + while((name[j] != '/') && (name[j] != '\0')) { + fname[i] = name[j]; + i++; j++; + } + fname[i] = '\0'; + ret = finddir_fs(ret, fname); + if (name[j] == '/') + j++; + } + + return ret; +} + +void list_fs(vfs_node_t* node, uint32_t depth) +{ + int j, i = 0; + dirent_t* dirent = NULL; + fildes_t* file = kmalloc(sizeof(fildes_t)); + file->offset = 0; + file->flags = 0; + + + while ((dirent = readdir_fs(node, i)) != 0) { + for(j=0; jname); + + if (strcmp(dirent->name, ".") && strcmp(dirent->name, "..")) { + vfs_node_t *new_node = finddir_fs(node, dirent->name); + if (new_node) { + if (new_node->type == FS_FILE) { + char buff[16] = {[0 ... 15] = 0x00}; + + file->node = new_node; + file->offset = 0; + file->flags = 0; + + read_fs(file, (uint8_t*)buff, 8); + for(j=0; j +#include +#include +#include +#include +#include +#include +#include + +static vfs_node_t initrd_root; + +/* + * Note that linker symbols are not variables, they have no memory allocated for + * maintaining a value, rather their address is their value. + */ +extern const void initrd_start, initrd_end; + +#define INITRD_MAGIC_NUMBER 0x4711 + +/* + * Grub maps the initrd as module into the address space of the kernel. + * The module begins with the header "initrd_header_t", which describes + * the number of mounted files and the mount point. The header follows + * by the struct "initrd_file_desc_t" for each file, which will be mounted. + * This struct describes the file properties and the position in the + * module. + */ +typedef struct { + uint32_t magic; + uint32_t nfiles; + char mount_point[MAX_FNAME]; +} initrd_header_t; + +typedef struct { + uint32_t length; + uint32_t offset; + char fname[MAX_FNAME]; +} initrd_file_desc_t; + +static ssize_t initrd_read(fildes_t* file, uint8_t* buffer, size_t size) +{ + vfs_node_t* node = file->node; + + uint32_t i, pos = 0, found = 0; + off_t offset = 0; + char* data = NULL; + block_list_t* blist = &node->block_list; + + if (file->flags & O_WRONLY) + return -EACCES; + + /* init the tmp offset */ + offset = file->offset; + + /* searching for the valid data block */ + if (offset) { + pos = offset / node->block_size; + offset = offset % node->block_size; + } + do { + for(i=0; idata[i]) { + found++; + if (found > pos) + data = (char*) blist->data[i]; + } + } + + blist = blist->next; + } while(blist && !data); + + if (BUILTIN_EXPECT(!data, 0)) + return 0; + + /* + * If the data block is not large engough, + * we copy only the rest of the current block. + * The user has to restart the read operation + * for the next block. + */ + if ((offset + size) >= node->block_size) + size = node->block_size - offset; + + memcpy(buffer, data + offset, size); + + file->offset += size; + return size; +} + +static ssize_t initrd_emu_readdir(fildes_t* file, uint8_t* buffer, size_t size) +{ + vfs_node_t* node = file->node; + + uint32_t i, j, k, count; + uint32_t index = file->offset; + dirent_t* dirent; + dir_block_t* dirblock; + block_list_t* blist = &node->block_list; + + do { + for(i=0,count=0; idata[i]; + for(j=0; dirblock && jentries[j]; + if (dirent->vfs_node) { + count++; + if (count > index) { + k=0; + do { + buffer[k] = dirent->name[k]; + k++; + } while(dirent->name[k] != '\0'); + file->offset++; + return k; + } + } + } + } + + blist = blist->next; + } while(blist); + + return -EINVAL; +} + +static ssize_t initrd_write(fildes_t* file, uint8_t* buffer, size_t size) +{ + uint32_t i, pos = 0, found = 0; + off_t offset = 0; + char* data = NULL; + vfs_node_t* node = file->node; + block_list_t* blist = &node->block_list; + + if (file->flags & O_RDONLY) + return -EACCES; + + if (file->flags & O_APPEND) + file->offset = node->block_size; + + /* init the tmp offset */ + offset = file->offset; + + /* searching for the valid data block */ + if (offset) { + pos = offset / MAX_DATAENTRIES; + offset = offset % MAX_DATAENTRIES; + } + + do { + for (i = 0; i < MAX_DATABLOCKS && !data; i++) { + if ((size + offset) >= MAX_DATAENTRIES) + size = MAX_DATAENTRIES - offset; + if(!blist->data[i]) { + blist->data[i] = (data_block_t*) + kmalloc(sizeof(data_block_t)); + if (blist->data[i]) + memset(blist->data[i], 0x00, + sizeof(data_block_t)); + } + found++; + if (found > pos) { + data = (char*) blist->data[i]; + } + } + + if (!blist->next) { + blist->next = (block_list_t*) + kmalloc(sizeof(block_list_t)); + if (blist->next) + memset(blist->next, 0x00, + sizeof(block_list_t)); + } + blist = blist->next; + } while(blist && !data); + + /* you may have to increase nodesize */ + if (node->block_size < (file->offset + size)) + node->block_size = file->offset + size; + /* + * If the data block is not large engough, + * we copy only the rest of the current block. + * The user has to restart the write operation + * for the next block. + */ + memcpy(data + offset, buffer, size); + file->offset += size; + return size; +} + +static int initrd_open(fildes_t* file, const char* name) +{ + if (file->node->type == FS_FILE) { + if ((file->flags & O_CREAT) && (file->flags & O_EXCL)) + return -EEXIST; + + /* in the case of O_TRUNC kfree all the nodes */ + if (file->flags & O_TRUNC) { + uint32_t i; + char* data = NULL; + block_list_t* blist = &file->node->block_list; + block_list_t* lastblist = NULL; + + /* the first blist pointer have do remain valid. */ + for(i=0; idata[i]) { + kfree(blist->data[i]); + } + } + if (blist->next) { + lastblist = blist; + blist = blist->next; + lastblist->next = NULL; + + /* kfree all other blist pointers */ + do { + for(i=0; idata[i]) { + kfree(blist->data[i]); + } + } + lastblist = blist; + blist = blist->next; + kfree(lastblist); + } while(blist); + } + + /* reset the block_size */ + file->node->block_size = 0; + } + } + + if (file->node->type == FS_DIRECTORY) { + + /* opendir was called: */ + if (name[0] == '\0') + return 0; + + /* open file was called: */ + if (!(file->flags & O_CREAT)) + return -ENOENT; + + uint32_t i, j; + block_list_t* blist = NULL; + /* CREATE FILE */ + vfs_node_t* new_node = kmalloc(sizeof(vfs_node_t)); + if (BUILTIN_EXPECT(!new_node, 0)) + return -EINVAL; + + blist = &file->node->block_list; + dir_block_t* dir_block; + dirent_t* dirent; + + memset(new_node, 0x00, sizeof(vfs_node_t)); + new_node->type = FS_FILE; + new_node->read = initrd_read; + new_node->write = initrd_write; + new_node->open = initrd_open; + spinlock_init(&new_node->lock); + + /* create a entry for the new node in the directory block of current node */ + do { + for(i=0; idata[i]) { + dir_block = (dir_block_t*) blist->data[i]; + for(j=0; jentries[j]; + if (!dirent->vfs_node) { + dirent->vfs_node = new_node; + strncpy(dirent->name, (char*) name, MAX_FNAME); + goto exit_create_file; // there might be a better Solution *************** + } + } + } + } + /* if all blocks are reserved, we have to allocate a new one */ + if (!blist->next) { + blist->next = (block_list_t*) kmalloc(sizeof(block_list_t)); + if (blist->next) + memset(blist->next, 0x00, sizeof(block_list_t)); + } + + blist = blist->next; + } while(blist); + +exit_create_file: + file->node = new_node; + file->node->block_size = 0; + + } + return 0; +} + + +static dirent_t* initrd_readdir(vfs_node_t* node, uint32_t index) +{ + uint32_t i, j, count; + dirent_t* dirent; + dir_block_t* dirblock; + block_list_t* blist = &node->block_list; + + do { + for(i=0,count=0; idata[i]; + for(j=0; dirblock && jentries[j]; + if (dirent->vfs_node) { + count++; + if (count > index) + return dirent; + } + } + } + + blist = blist->next; + } while(blist); + + return NULL; +} + +static vfs_node_t* initrd_finddir(vfs_node_t* node, const char *name) +{ + uint32_t i, j; + dir_block_t* dirblock; + dirent_t* dirent; + block_list_t* blist = &node->block_list; + + do { + for(i=0; idata[i]; + for(j=0; dirblock && jentries[j]; + if (!strncmp(dirent->name, name, MAX_FNAME)) + return dirent->vfs_node; + } + } + + blist = blist->next; + } while(blist); + + return NULL; +} + +static vfs_node_t* initrd_mkdir(vfs_node_t* node, const char* name) +{ + uint32_t i, j; + dir_block_t* dir_block; + dir_block_t* tmp; + dirent_t* dirent; + vfs_node_t* new_node; + block_list_t* blist = &node->block_list; + + if (BUILTIN_EXPECT(node->type != FS_DIRECTORY, 0)) + return NULL; + + /* exists already a entry with same name? */ + if (initrd_finddir(node, name)) + return NULL; + + new_node = kmalloc(sizeof(vfs_node_t)); + if (BUILTIN_EXPECT(!new_node, 0)) + return NULL; + + memset(new_node, 0x00, sizeof(vfs_node_t)); + new_node->type = FS_DIRECTORY; + new_node->read = &initrd_emu_readdir; + new_node->readdir = &initrd_readdir; + new_node->finddir = &initrd_finddir; + new_node->mkdir = &initrd_mkdir; + new_node->open = &initrd_open; + spinlock_init(&new_node->lock); + + /* create default directory entry */ + dir_block = (dir_block_t*) kmalloc(sizeof(dir_block_t)); + if (BUILTIN_EXPECT(!dir_block, 0)) + goto out; + memset(dir_block, 0x00, sizeof(dir_block_t)); + new_node->block_list.data[0] = dir_block; + strncpy(dir_block->entries[0].name, ".", MAX_FNAME); + dir_block->entries[0].vfs_node = new_node; + strncpy(dir_block->entries[1].name, "..", MAX_FNAME); + dir_block->entries[1].vfs_node = node; + + do { + /* searching for a free directory block */ + for(i=0; idata[i]) { + tmp = (dir_block_t*) blist->data[i]; + for(j=0; jentries[j]; + if (!dirent->vfs_node) { + dirent->vfs_node = new_node; + strncpy(dirent->name, name, MAX_FNAME); + return new_node; + } + } + } + } + + /* if all blocks are reserved, we have to allocate a new one */ + if (!blist->next) { + blist->next = (block_list_t*) kmalloc(sizeof(block_list_t)); + if (blist->next) + memset(blist->next, 0x00, sizeof(block_list_t)); + } + + blist = blist->next; + } while(blist); + + kfree(dir_block); +out: + kfree(new_node); + + return NULL; +} + +int initrd_init(void) +{ + dir_block_t* dir_block; + vfs_node_t* tmp; + uint32_t i, j, k, l; +#if 0 + uint32_t mods_count = 0; + multiboot_module_t* mmodule = NULL; + + if (mb_info && (mb_info->flags & MULTIBOOT_INFO_MODS)) { + mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); + mods_count = mb_info->mods_count; + } +#endif + + /* Initialize the root directory. */ + fs_root = &initrd_root; + memset(&initrd_root, 0x00, sizeof(vfs_node_t)); + initrd_root.type = FS_DIRECTORY; + initrd_root.read = &initrd_emu_readdir; + initrd_root.readdir = &initrd_readdir; + initrd_root.finddir = &initrd_finddir; + initrd_root.mkdir = &initrd_mkdir; + initrd_root.open = &initrd_open; + spinlock_init(&initrd_root.lock); + + /* create default directory block */ + dir_block = (dir_block_t*) kmalloc(sizeof(dir_block_t)); + if (BUILTIN_EXPECT(!dir_block, 0)) + return -ENOMEM; + memset(dir_block, 0x00, sizeof(dir_block_t)); + initrd_root.block_list.data[0] = dir_block; + strncpy(dir_block->entries[0].name, ".", MAX_FNAME); + dir_block->entries[0].vfs_node = fs_root; + strncpy(dir_block->entries[1].name, "..", MAX_FNAME); + dir_block->entries[1].vfs_node = fs_root; + + /* create the directory bin and dev */ + mkdir_fs(fs_root, "bin"); + //mkdir_fs(fs_root, "sbin"); + mkdir_fs(fs_root, "dev"); + //mkdir_fs(fs_root, "tmp"); + + /* + * Currently we attached the initrd at the end of the kernel + * => we don't longer use grub to load initrd as module + */ +#if 0 + /* For every module.. */ + for(i=0; imagic != INITRD_MAGIC_NUMBER, 0)) { + kprintf("Invalid magic number for a init ram disk: 0x%x\n", header->magic); + continue; + } + + tmp = findnode_fs(header->mount_point); + if (BUILTIN_EXPECT(!tmp, 0)) { + kprintf("Did not found mount point %s.\n", header->mount_point); + continue; + } + + if (BUILTIN_EXPECT(tmp->type != FS_DIRECTORY, 0)) { + kprintf("%s is not a valid mount point.\n", header->mount_point); + continue; + } + + file_desc = (initrd_file_desc_t*) (header + 1); + for(j=0; jnfiles; j++) { + block_list_t* blist; + dir_block_t* dir_block; + dirent_t* dirent; + + if (finddir_fs(tmp, file_desc->fname)) { + kprintf("Error: %s alreay exits\n", file_desc->fname); + goto next_file; + } + + /* create a new node and map the module as data block */ + new_node = kmalloc(sizeof(vfs_node_t)); + if (BUILTIN_EXPECT(!new_node, 0)) { + kprintf("Not enough memory to create new initrd node\n"); + goto next_file; + } + memset(new_node, 0x00, sizeof(vfs_node_t)); + new_node->type = FS_FILE; + new_node->read = initrd_read; + new_node->write = initrd_write; + new_node->open = initrd_open; + new_node->block_size = file_desc->length; + new_node->block_list.data[0] = ((char*) header) + file_desc->offset; + spinlock_init(&new_node->lock); + + /* create a entry for the new node in the directory block of current node */ + blist = &tmp->block_list; + do { + for(k=0; kdata[k]) { + dir_block = (dir_block_t*) blist->data[k]; + for(l=0; lentries[l]; + if (!dirent->vfs_node) { + dirent->vfs_node = new_node; + strncpy(dirent->name, file_desc->fname, MAX_FNAME); + goto next_file; + } + } + } + } + + /* if all blocks are reserved, we have to allocate a new one */ + if (!blist->next) { + blist->next = (block_list_t*) kmalloc(sizeof(block_list_t)); + if (blist->next) + memset(blist->next, 0x00, sizeof(block_list_t)); + } + + blist = blist->next; + } while(blist); +next_file: + file_desc++; + } + } + + return 0; +} diff --git a/hermit/include/hermit/fs.h b/hermit/include/hermit/fs.h new file mode 100644 index 000000000..4a3207ab8 --- /dev/null +++ b/hermit/include/hermit/fs.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/eduos/fs.h + * @brief Filesystem related functions and structures + */ + +#ifndef __FS_H__ +#define __FS_H__ + +#include +#include + +#define FS_FILE 0x01 +#define FS_DIRECTORY 0x02 +#define FS_CHARDEVICE 0x03 +//#define FS_BLOCKDEVICE 0x04 +//#define FS_PIPE 0x05 +//#define FS_SYMLINK 0x06 +//#define FS_MOUNTPOINT 0x08 // Is the file an active mountpoint? + + +/*file descriptor init*/ +#define NR_OPEN 100 + +#define _FOPEN (-1) /* from sys/file.h, kernel use only */ +#define _FREAD 0x0001 /* read enabled */ +#define _FWRITE 0x0002 /* write enabled */ +#define _FAPPEND 0x0008 /* append (writes guaranteed at the end) */ +#define _FMARK 0x0010 /* internal; mark during gc() */ +#define _FDEFER 0x0020 /* internal; defer for next gc pass */ +#define _FASYNC 0x0040 /* signal pgrp when data ready */ +#define _FSHLOCK 0x0080 /* BSD flock() shared lock present */ +#define _FEXLOCK 0x0100 /* BSD flock() exclusive lock present */ +#define _FCREAT 0x0200 /* open with file create */ +#define _FTRUNC 0x0400 /* open with truncation */ +#define _FEXCL 0x0800 /* error on open if file exists */ +#define _FNBIO 0x1000 /* non blocking I/O (sys5 style) */ +#define _FSYNC 0x2000 /* do all writes synchronously */ +#define _FNONBLOCK 0x4000 /* non blocking I/O (POSIX style) */ +#define _FNDELAY _FNONBLOCK /* non blocking I/O (4.2 style) */ +#define _FNOCTTY 0x8000 /* don't assign a ctty on this open */ + +/*open flags*/ +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define O_APPEND _FAPPEND +#define O_CREAT _FCREAT +#define O_TRUNC _FTRUNC +#define O_EXCL _FEXCL +#define O_SYNC _FSYNC +#define O_NONBLOCK _FNONBLOCK +#define O_NOCTTY _FNOCTTY + +/*lseek defines*/ +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + +struct vfs_node; +struct fildes; +/** @defgroup fsprototypes FS access function prototypes + * + * These typedefs define the type of callbacks - called when read/write/open/close are called.\n + * They aren't as well documented as the read_fs and so on functions. Just look there for further information. + * + * @{ + */ + +/** @brief Read function pointer */ +typedef ssize_t (*read_type_t) (struct fildes *, uint8_t*, size_t); +/** @brief Write function pointer */ +typedef ssize_t (*write_type_t) (struct fildes *, uint8_t*, size_t); +/** @brief Open function pointer */ +typedef int (*open_type_t) (struct fildes *, const char *name); +/** @brief Close function pointer */ +typedef int (*close_type_t) (struct fildes *); +/** @brief Read directory function pointer */ +typedef struct dirent *(*readdir_type_t) (struct vfs_node *, uint32_t); +/** @brief Find directory function pointer */ +typedef struct vfs_node *(*finddir_type_t) (struct vfs_node *, const char *name); +/** @brief Make directory function pointer */ +typedef struct vfs_node *(*mkdir_type_t) (struct vfs_node *, const char *name); + +/** @} */ + +#define MAX_DATABLOCKS 12 +#define MAX_DIRENTRIES 32 +#define MAX_DATAENTRIES 4096 + + +/** @brief Block list structure. VFS nodes keep those in a list */ +typedef struct block_list { + /// Array with pointers to data blocks + void* data[MAX_DATABLOCKS]; + /// Pointer to the next block_list in the list + struct block_list* next; +} block_list_t; + +typedef struct vfs_node { + /// The permissions mask. + uint32_t mask; + /// The owning user. + uint32_t uid; + /// The owning group. + uint32_t gid; + /// Includes the node type. See the defines above. + uint32_t type; + /// Open handler function pointer + open_type_t open; + /// Close handler function pointer + close_type_t close; + /// Read handler function pointer + read_type_t read; + /// Write handler function pointer + write_type_t write; + /// Read dir handler function pointer + readdir_type_t readdir; + /// Find dir handler function pointer + finddir_type_t finddir; + /// Make dir handler function pointer + mkdir_type_t mkdir; + /// Lock variable to thread-protect this structure + spinlock_t lock; + /// Block size + size_t block_size; + /// List of blocks + block_list_t block_list; +} vfs_node_t; + +/** @brief file descriptor structure */ +typedef struct fildes { + vfs_node_t* node; /* */ + off_t offset; /* */ + int flags; /* */ + int mode; /* */ + int count; /* number of tasks using this fd */ +} fildes_t, *filp_t; + +/** @brief Directory entry structure */ +typedef struct dirent { + /// Directory name + char name[MAX_FNAME]; + /// Corresponding VFS node pointer + vfs_node_t* vfs_node; +} dirent_t; + +/** @brief Dir block structure which will keep directory entries */ +typedef struct { + /// Array of directory entries + dirent_t entries[MAX_DIRENTRIES]; +} dir_block_t; + +typedef struct { + ///Array of data entries + char entries[MAX_DATAENTRIES]; +} data_block_t; + + +/* Time Value Specification Structures, P1003.1b-1993, p. 261 */ +struct timespec { + long tv_sec; /* Seconds */ + long tv_nsec; /* Nanoseconds */ +}; + +struct stat +{ + uint16_t st_dev; /* ID of device containing file */ + uint16_t st_ino; /* inode number */ + uint32_t st_mode; /* protection */ + unsigned short st_nlink; /* number of hard links */ + uint16_t st_uid; /* user ID of owner */ + uint16_t st_gid; /* group ID of owner */ + uint16_t st_rdev; /* device ID (if special file) */ + off_t st_size; /* total size, in bytes */ + struct timespec st_atim; /* time of last access */ + struct timespec st_mtim; /* time of last modification */ + struct timespec st_ctim; /* time of last status change */ + uint32_t st_blksize; /* blocksize for filesystem I/O */ + uint32_t st_blocks; /* number of blocks allocated */ +} stat_t; + +extern vfs_node_t* fs_root; // The root of the filesystem. + +/** @defgroup fsfunc FS related functions + * + * Standard read/write/open/close/mkdir functions. Note that these are all suffixed with + * _fs to distinguish them from the read/write/open/close which deal with file descriptors, + * not file nodes. + * + * @{ + */ + +/** @brief Read from file system into the buffer + * @param file Pointer to the file descriptor to read from + * @param buffer Pointer to buffer to write into + * @param size Number of bytes to read + * @return + * - number of bytes copied (size) + * - 0 on error + */ +ssize_t read_fs(fildes_t* file, uint8_t* buffer, size_t size); + +/** @brief Write into the file system from the buffer + * @param file Pointer to the file descriptor to write to + * @param buffer Pointer to buffer to read from + * @param size Number of bytes to read + * @return + * - number of bytes copied (size) + * - 0 on error + */ +ssize_t write_fs(fildes_t* file, uint8_t* buffer, size_t size); + +/** @brief Yet to be documented */ +int open_fs(fildes_t* file, const char* fname); + +/** @brief Yet to be documented */ +int close_fs(fildes_t * file); + +/** @brief Get dir entry at index + * @param node VFS node to get dir entry from + * @param index Index position of desired dir entry + * @return + * - The desired dir entry + * - NULL on failure + */ +struct dirent *readdir_fs(vfs_node_t * node, uint32_t index); + +/** @brief Find a directory by looking for the dir name + * @param node The node where to start the search from + * @param name The dir name string + * @return + * - a VFS node pointer + * - NULL on failure + */ +vfs_node_t* finddir_fs(vfs_node_t * node, const char *name); + +/** @brief Make a new directory in a VFS node + * @param node Pointer to the node where the dir is to create in + * @param name Name of the new directory + * @return + * - new VFS node pointer + * - NULL on failure + */ +vfs_node_t* mkdir_fs(vfs_node_t* node, const char* name); + +/** @brief Find a node within root file system + * @param name The node name + * @return + * - VFS node pointer + * - NULL on failure + */ +vfs_node_t* findnode_fs(const char* name); + +/** @brief List a filesystem hirachically */ +void list_fs(vfs_node_t* node, uint32_t depth); + +int initrd_init(void); + +#endif diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index 4f8a5d468..da95be85c 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ static int hermit_init(void) timer_init(); multitasking_init(); memory_init(); + initrd_init(); return 0; } @@ -147,10 +149,17 @@ int main(void) kprintf("Current allocated memory: %lu KiB\n", atomic_int32_read(&total_allocated_pages) * PAGE_SIZE / 1024); kprintf("Current available memory: %lu KiB\n", atomic_int32_read(&total_available_pages) * PAGE_SIZE / 1024); +#if 1 + kputs("Filesystem:\n"); + list_fs(fs_root, 1); +#endif + create_kernel_task(NULL, foo, "foo1", NORMAL_PRIO); create_kernel_task(NULL, foo, "foo2", NORMAL_PRIO); +#if 0 init_netifs(); +#endif while(1) { check_workqueues(); diff --git a/hermit/link.ld b/hermit/link.ld index 57d36d78e..4f3307165 100644 --- a/hermit/link.ld +++ b/hermit/link.ld @@ -18,6 +18,10 @@ SECTIONS .rodata ALIGN(4096) : AT(ADDR(.rodata)) { *(.rodata) *(.rodata.*) + . = ALIGN((1 << 12)); + initrd_start = .; + *(.initrd) + initrd_end = .; } .data ALIGN(4096) : AT(ADDR(.data)) { *(.data) diff --git a/hermit/tools/Makefile b/hermit/tools/Makefile index a4aad5f52..5d4af68ce 100644 --- a/hermit/tools/Makefile +++ b/hermit/tools/Makefile @@ -1,19 +1,50 @@ MAKE = make NASM = nasm NASMFLAGS = -fbin +CC = gcc +CFLAGS = -O2 -Wall +HEXDUMP = hexdump +LDFLGAS = +EXECFILES = $(shell find ../newlib/examples -perm -u+r+x -type f) + +# Prettify output +V = 0 +ifeq ($V,0) + Q = @ + P = > /dev/null +endif + +# other implicit rules +%.o : %.c + @echo [CC] $@ + $Q$(CC) -c $(CFLAGS) -o $@ $< default: all -all: init.hex +all: init.hex make_initrd initrd.o init.bin: init.asm - $(NASM) $(NASMFLAGS) -o $@ $< + @echo [NASM] $@ + $Q$(NASM) $(NASMFLAGS) -o $@ $< init.hex: init.bin - hexdump -v -e '"0x" 1/1 "%02X" ", "' $< > $@ + @echo [HEXDUMP] $@ + $Q$(HEXDUMP) -v -e '"0x" 1/1 "%02X" ", "' $< > $@ + +initrd.o: initrd.S initrd.img + @echo [CC] Build initrd as object file + $Q$(CC) -c -o $@ initrd.S + +initrd.img: $(EXECFILES) make_initrd + @echo [MAKE_INITRD] initrd.img + $Q./make_initrd /bin $(foreach FILE, $(EXECFILES), $(FILE) $(shell basename $(FILE))) + +make_initrd: make_initrd.o + $Q$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) clean: - $(RM) -rf *.o *~ *.bin *.obj *.hex + @echo Cleaning tools + $Q$(RM) -rf *.o *~ *.bin *.obj *.hex veryclean: clean diff --git a/hermit/tools/initrd.S b/hermit/tools/initrd.S new file mode 100644 index 000000000..157996498 --- /dev/null +++ b/hermit/tools/initrd.S @@ -0,0 +1,2 @@ +.section .initrd +.incbin "initrd.img" diff --git a/hermit/tools/make_initrd.c b/hermit/tools/make_initrd.c new file mode 100644 index 000000000..494b68e6c --- /dev/null +++ b/hermit/tools/make_initrd.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#define INITRD_MAGIC_NUMBER 0x4711 +#define MAX_FNAME 128 +#define PAGE_SIZE 4096 + +typedef struct { + uint32_t magic; + uint32_t nfiles; + char mount_point[MAX_FNAME]; +} initrd_header_t; + +typedef struct { + uint32_t length; + uint32_t offset; + char fname[MAX_FNAME]; +} initrd_file_desc_t; + +static void print_options(void) +{ + printf(" make_initrd mount_point path name [path name]\n"); + printf("\n"); + printf(" mount_point - mount point of init ram disk, where all file will be mounted.\n"); + printf(" path - path to the file, which will be mounted\n"); + printf(" name - file name, which will be used be the initrd\n"); +} + +int main(int argc, char **argv) +{ + int i, nfiles = (argc - 2) / 2; + initrd_header_t header; + initrd_file_desc_t* file_desc; + off_t offset; + FILE* istream; + FILE* ostream; + + if ((argc < 4) || (strcmp(argv[1], "-h") == 0)) { + print_options(); + return 0; + } + + memset(&header, 0x00, sizeof(initrd_header_t)); + header.magic = INITRD_MAGIC_NUMBER; + header.nfiles = nfiles; + strncpy(header.mount_point, argv[1], MAX_FNAME); + + file_desc = (initrd_file_desc_t*) malloc(sizeof(initrd_file_desc_t)*nfiles); + if (!file_desc) { + fprintf(stderr, "No enough memory\n"); + return -1; + } + memset(file_desc, 0x00, sizeof(initrd_file_desc_t)*nfiles); + offset = sizeof(initrd_header_t) + nfiles * sizeof(initrd_file_desc_t); + + for(i=0; i 0); + + fclose(istream); + free(buf); + } + + fclose(ostream); + + return 0; +}