merge newlib tree to current
authorKornilios Kourtis <kkourt@inf.ethz.ch>
Fri, 3 Feb 2012 10:43:20 +0000 (11:43 +0100)
committerKornilios Kourtis <kkourt@inf.ethz.ch>
Fri, 3 Feb 2012 10:43:20 +0000 (11:43 +0100)
- merge conflicts on Hakefiles
- s/index/idx/g

compiles and boots qemu for x86_64

--HG--
rename : lib/c/src/sys-barrelfish/sys_morecore.c => lib/freec/src/sys-barrelfish/sys_morecore.c

34 files changed:
1  2 
hake/Config.hs.template
hake/RuleDefs.hs
include/Hakefile
include/barrelfish/caddr.h
include/barrelfish/core_state.h
include/barrelfish_kpi/init.h
include/barrelfish_kpi/legacy_idc_buffer.h
include/vfs/vfs.h
kernel/arch/arm/syscall.c
kernel/arch/x86/ipi_notify.c
kernel/arch/x86/misc.c
kernel/arch/x86_32/syscall.c
kernel/arch/x86_64/syscall.c
kernel/capabilities.c
kernel/dispatch.c
kernel/include/dispatch.h
lib/barrelfish/capabilities.c
lib/barrelfish/init.c
lib/barrelfish/vspace/utils.c
lib/contmng/contmng.c
lib/freec/src/sys-barrelfish/sys_morecore.c
lib/vfs/Hakefile
lib/vfs/vfs.c
lib/vfs/vfs_fat.c
usr/bench/ahci/Hakefile
usr/bench/ahci/main.c
usr/fish/fish.c
usr/monitor/monitor.h
usr/monitor/monitor_rpc_server.c
usr/netd/idc_barrelfish.c
usr/skb/Hakefile
usr/spawnd/bsp_bootup.c
usr/startd/Hakefile
usr/webserver/http_cache.c

Simple merge
@@@ -242,16 -228,14 +230,14 @@@ assembler opts src ob
      | optArch opts == "xscale" = XScale.assembler opts src obj
      | otherwise = [ ErrorMsg ("no assembler for " ++ (optArch opts)) ]
  
 -archive :: Options -> [String] -> String -> [ RuleToken ]
 -archive opts objs libname
 -    | optArch opts == "x86_64"  = X86_64.archive opts objs libname
 -    | optArch opts == "x86_32"  = X86_32.archive opts objs libname
 -    | optArch opts == "scc"     = SCC.archive opts objs libname
 -    | optArch opts == "arm"     = ARM.archive opts objs libname
 -    | optArch opts == "arm11mp" = ARM11MP.archive opts objs libname
 -    | optArch opts == "xscale" = XScale.archive opts objs libname
 +archive :: Options -> [String] -> [String] -> String -> String -> [ RuleToken ]
 +archive opts objs libs name libname
 +    | optArch opts == "x86_64"  = X86_64.archive opts objs libs name libname
 +    | optArch opts == "x86_32"  = X86_32.archive opts objs libs name libname
 +    | optArch opts == "scc"     = SCC.archive opts objs libs name libname
 +    | optArch opts == "arm"     = ARM.archive opts objs libs name libname
 +    | optArch opts == "arm11mp" = ARM11MP.archive opts objs libs name libname
-     | optArch opts == "beehive" = Beehive.archive opts objs libs name libname
 +    | optArch opts == "xscale" = XScale.archive opts objs libs name libname
      | otherwise = [ ErrorMsg ("Can't build a library for " ++ (optArch opts)) ]
  
  linker :: Options -> [String] -> [String] -> String -> [RuleToken]
  
  [ includeFile (options arch) f
        | f <- [ 
-    "bexec.h",
 -   "assert.h",
 -   "complex.h",
     "cpiobin.h",
 -   "ctype.h",
 -   "dirent.h",
     "dlfcn.h",
 -   "errno.h",
 -   "fpmath.h",
 -   "getopt.h",
 -   "ieeefp.h",
 -   "inttypes.h",
 -   "iso646.h",
 -   "limits.h",
 -   "locale.h",
     "lwipopts.h",
 -   "math.h",
     "multicast.h",
 -   "printf.h",
 -   "setjmp.h",
 -   "signal.h",
 -   "stdarg.h",
 -   "stdbool.h",
 -   "stddef.h",
 -   "stdint.h",
 -   "stdio_file.h",
 -   "stdio.h",
 -   "stdlib.h",
 -   "string.h",
 -   "termios.h",
 -   "time.h",
 -   "unistd.h",
     "utime.h",
     "values.h",
     "x86emu.h",
Simple merge
  #ifndef LIBBARRELFISH_CORESTATE_H
  #define LIBBARRELFISH_CORESTATE_H
  
 -#include <../lib/c/src/k_r_malloc.h>
 +#include <k_r_malloc.h>
  #include <barrelfish/waitset.h>
  
+ /* DELETEME! I can't find anything that uses this -AB 20120110 */
+ #if 0
  /*
   * XXX: This currently very small (4096 frames tracked, using 384KB
   * memory). Obviously making it much bigger results in a huge .bss
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -47,10 -47,9 +47,9 @@@ errval_t vspace_unmap(const void *buf
      return SYS_ERR_OK;
  }
  
- #ifndef NOTRANS
  /// Map with an alignment constraint
 -errval_t vspace_map_anon_aligned(void **retaddr, struct memobj **ret_memobj,
 -                                 struct vregion **ret_vregion, size_t size,
 +errval_t vspace_map_anon_aligned(void **retaddr, struct memobj *memobj,
 +                                 struct vregion *vregion, size_t size,
                                   size_t *retsize, vregion_flags_t flags,
                                   size_t alignment)
  {
Simple merge
index be343e2,0000000..20dd5b1
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
 +/**
 + * \file
 + * \brief morecore() is a sbrk() equivalent.
 + */
 +
 +/*
 + * Copyright (c) 2007, 2008, 2011, ETH Zurich.
 + * All rights reserved.
 + *
 + * This file is distributed under the terms in the attached LICENSE file.
 + * If you do not find this file, copies can be found by writing to:
 + * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
 + */
 +
 +#include <assert.h>
 +#include <k_r_malloc.h>
 +#include <stddef.h>
 +#include <barrelfish/barrelfish.h>
 +#include <barrelfish/core_state.h>
 +
 +Header *get_malloc_freep(void);
 +
 +typedef void *(*morecore_alloc_func_t)(size_t bytes, size_t *retbytes);
 +typedef void (*morecore_free_func_t)(void *base, size_t bytes);
 +
 +morecore_alloc_func_t sys_morecore_alloc;
 +morecore_free_func_t sys_morecore_free;
 +
 +/**
 + * \brief sbrk() equivalent.
 + *
 + * This function tries to allocate at least the amount given by 'nu'
 + * in sizeof(::Header) byte units. In some cases, it will allocate
 + * less, if no more memory was available. In any case, It returns a
 + * pointer to the freelist header of the memory region.  NULL is
 + * returned when out of memory.
 + *
 + * \param nu    Number of memory units (1 unit == sizeof(::Header) bytes)
 + *
 + * \return Pointer to freelist header of new memory region or NULL on out of
 + * memory.
 + */
 +Header *morecore(unsigned nu)
 +{
 +    Header *up;
 +    size_t nb = nu * sizeof(Header);
 +
 +    // Allocate requested number of pages and insert freelist header
 +    assert(sys_morecore_alloc);
 +    up = (Header *)sys_morecore_alloc(nb, &nb);
 +    if (up == NULL) {
 +        return NULL;
 +    }
 +    assert(nb % sizeof(Header) == 0);
 +    up->s.size = nb / sizeof(Header);
 +
 +    // Add header to freelist
 +    __free_locked((void *)(up + 1));
 +    return get_malloc_freep();
 +}
 +
 +/**
 + * \brief sbrk() garbage collector.
 + *
 + * Tries to free up pages at the end of the segment, so to shorten the
 + * segment and return memory to the operating system.
 + */
 +void lesscore(void)
 +{
- #if defined(NOTRANS) || defined(__arm__)
-     // Not implemented without translation hardware
++#if defined(__arm__)
++    // Not implemented
 +
 +#else
 +    struct morecore_state *state = get_morecore_state();
 +    genvaddr_t gvaddr =
 +        vregion_get_base_addr(&state->mmu_state.vregion)
 +        + state->mmu_state.offset;
 +    void *eaddr = (void*)vspace_genvaddr_to_lvaddr(gvaddr);
 +
 +    assert(sys_morecore_free);
 +
 +    // Deallocate from end of segment
 +    Header *prevp = state->header_freep, *p;
 +    for(p = prevp->s.ptr;; prevp = p, p = p->s.ptr) {
 +        if(p + p->s.size == eaddr) {
 +            prevp->s.ptr = p->s.ptr;
 +            state->header_freep = prevp;
 +
 +            // Give back the memory
 +            sys_morecore_free(p, p->s.size * sizeof(Header));
 +            break;
 +        }
 +
 +        if (p == state->header_freep) {       /* wrapped around free list */
 +            break;
 +        }
 +    }
 +#endif
 +}
    build library { target = "vfs_nonfs",
                  cFiles = [ "vfs.c", "vfs_path.c", "fopen.c", "vfs_ramfs.c", "cache.c" ],
                    addCFlags = [ "-DDISABLE_NFS" ],
-                   flounderBindings = [ "trivfs", "bcache" ],
-                   flounderExtraBindings = [ ("trivfs", ["rpcclient"]), ("bcache", ["rpcclient"]) ],
+                   mackerelDevices = [ "ata_identify", "fat_bpb", "fat16_ebpb", "fat32_ebpb", "fat_direntry" ],
+                   flounderBindings = [ "trivfs", "bcache", "ahci_mgmt" ],
+                   flounderExtraBindings = [ ("trivfs", ["rpcclient"]), ("bcache", ["rpcclient"]), ("ahci_mgmt", ["rpcclient"]), ("ata_rw28", ["ahci", "rpcclient"]) ],
                    flounderDefs = [ "monitor" ]
 +                },
 +  build library { target = "vfsfd",
 +        cFiles = [ "fdtab.c", "vfs_fd.c" ]
                  }
  ]
diff --cc lib/vfs/vfs.c
Simple merge
index 0000000,97fea51..adc74f8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1315 +1,1315 @@@
+ /*
+  * Copyright (c) 2009, 2011, ETH Zurich.
+  * All rights reserved.
+  *
+  * This file is distributed under the terms in the attached LICENSE file.
+  * If you do not find this file, copies can be found by writing to:
+  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+  */
+ #include <string.h>
+ #include <barrelfish/types.h>
+ #include <barrelfish/barrelfish.h>
+ #include <vfs/vfs_path.h>
+ #include <errors/errno.h>
+ #include <dev/fat_bpb_dev.h>
+ #include <dev/fat16_ebpb_dev.h>
+ #include <dev/fat32_ebpb_dev.h>
+ #include <dev/fat_direntry_dev.h>
+ #include <if/ata_rw28_defs.h>
+ #include <if/ata_rw28_ahci_defs.h>
+ #include <if/ata_rw28_rpcclient_defs.h>
+ #include <ahci/ahci.h>
+ #include "vfs_fat_conv.h"
+ #include "vfs_backends.h"
+ #include "vfs_ops.h"
+ #include "vfs_cache.h"
+ #if defined(FAT_DEBUG) || defined(VFS_DEBUG) || defined(GLOBAL_DEBUG)
+ #   define FAT_DEBUG_ENABLED
+ #   ifdef FAT_DEBUG
+ #       undef FAT_DEBUG
+ #   endif
+ #   define FAT_DEBUG_P() printf("fat:%s: ", __func__)
+ #   define FAT_DEBUG(s) \
+         printf("fat:%s: " s "\n", __func__)
+ #   define FAT_DEBUG_F(s, x...) \
+         printf("fat:%s: " s "\n", __func__, x)
+ #   define TRACE_ENTER \
+         printf("fat: entering %s\n", __func__)
+ #   define TRACE_ENTER_F(s, x...) \
+         printf("fat: entering %s: " s "\n", __func__, x)
+ #else
+ #   ifdef FAT_DEBUG_ENABLED
+ #       undef FAT_DEBUG_ENABLED
+ #   endif
+ #   define FAT_DEBUG_P() ((void)0)
+ #   define FAT_DEBUG(s) ((void)0)
+ #   define FAT_DEBUG_F(s, x...) ((void)0)
+ #   define TRACE_ENTER ((void)0)
+ #   define TRACE_ENTER_F(s, x...) ((void)0)
+ #endif
+ #define DUMP_DEV(dev_t, dev_p, buf_s) do { \
+     char dump_dev_buf__[(buf_s)]; \
+     dev_t ## _pr(dump_dev_buf__, (buf_s)-1, (dev_p)); \
+     dump_dev_buf__[(buf_s)-1] = 0; \
+     printf("%s\n", dump_dev_buf__); \
+ } while(0)
+ #ifndef CEIL_DIV
+ #define CEIL_DIV(x, d) (((x) + ((d)-1)) / (d))
+ #endif
+ #define fat_direntry_size 32
+ #define cluster_for_offset(offset, mount) \
+     ((offset) / ((mount)->block_size * (mount)->cluster_size))
+ #define offset_from_cluster(offset, mount) \
+     ((offset) % ((mount)->block_size * (mount)->cluster_size))
+ #define cluster_to_block(cluster, mount) \
+     ((mount)->clusters_start + (((cluster) - 2) * (mount)->cluster_size))
+ #define cluster_entry_size(mount) \
+     ((mount)->fat_type == FAT_TYPE_FAT16 ? sizeof(uint16_t) : sizeof(uint32_t))
+ #define fat_block_for_cluster(cluster, mount) \
+     ((cluster) * cluster_entry_size(mount) / (mount)->block_size)
+ #define fat_offset_for_cluster(cluster, mount) \
+     ((cluster) * cluster_entry_size(mount) % (mount)->block_size)
+ // NOTE: specification says max 255 chars, but format in principle seems to
+ // allow 260, so be on the safe side
+ #define LFN_CHAR_COUNT 260
+ #define LFN_MAX_LENGTH 255
+ #define DOSFN_MAX_LEN_UTF8 40
+ #define ASCII_MAX 127
+ struct fat_dirsearch {
+     size_t index;
+     fat_direntry_t *parent_direntry;
+     uint8_t *data;
+     size_t data_index;
+     size_t data_key;
+ };
+ struct fat_handle_common {
+     struct vfs_handle common;
+     uint8_t dirent_data[fat_direntry_size];
+     fat_direntry_t dirent;
+ };
+ struct fat_handle {
+     struct fat_handle_common h;
+     size_t offset;
+ };
+ struct fat_dirhandle {
+     struct fat_handle_common h;
+     struct fat_dirsearch search;
+ };
+ enum {
+     FAT_TYPE_FAT16 = 16,
+     FAT_TYPE_FAT32 = 32,
+ };
+ struct fat_mount {
+     struct ata_rw28_binding *ata_rw28_binding;
+     struct ata_rw28_rpc_client ata_rw28_rpc;
+     struct ahci_binding *ahci_binding;
+     errval_t bind_err;
+     int fat_type;
+     size_t startblock;
+     uint8_t bootsec_data[512];
+     fat_bpb_t bpb;
+     union {
+         fat16_ebpb_t f16;
+         fat32_ebpb_t f32;
+     } ebpb;
+     struct fs_cache *block_cache;
+     struct fs_cache *cluster_cache;
+     size_t block_count;
+     size_t block_size; // bytes
+     size_t cluster_size; // blocks
+     size_t fat_start; // blocks
+     size_t fat_size; // blocks
+     size_t rootdir_start; // blocks. 0 implies rootdir in clusters
+     size_t rootdir_cluster;
+     size_t clusters_start; // blocks
+     uint32_t last_cluster_start; // cluster
+ };
+ static errval_t
+ acquire_or_read(struct fat_mount *mount, struct fs_cache *cache,
 -        uint32_t index, uint8_t **data, size_t block, size_t size)
++        uint32_t idx, uint8_t **data, size_t block, size_t size)
+ {
 -    TRACE_ENTER_F("index=%"PRIu32", block=%zu, size=%zu", index, block, size);
++    TRACE_ENTER_F("idx=%"PRIu32", block=%zu, size=%zu", idx, block, size);
+     uint8_t *data_;
+     errval_t err;
 -    err = fs_cache_acquire(cache, index, (void**)&data_);
++    err = fs_cache_acquire(cache, idx, (void**)&data_);
+     if (err_is_fail(err) && err != FS_CACHE_NOTPRESENT) {
+         return err;
+     }
+     else if (err == FS_CACHE_NOTPRESENT) {
+         size_t read_size;
+         err = mount->ata_rw28_rpc.vtbl.read_dma(&mount->ata_rw28_rpc,
+                 size, block, &data_, &read_size);
+         if (err_is_fail(err)) {
+             return err;
+         }
+         assert(size == read_size);
 -        err = fs_cache_put(cache, index, data_);
++        err = fs_cache_put(cache, idx, data_);
+         if (err_is_fail(err)) {
+             return err;
+         }
+     }
+     *data = data_;
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ acquire_block(struct fat_mount *mount, size_t block, uint8_t **data)
+ {
+     return acquire_or_read(mount, mount->block_cache,
+             block, data, block, mount->block_size);
+ }
+ static errval_t
+ release_block(struct fat_mount *mount, size_t block)
+ {
+     return fs_cache_release(mount->block_cache, block);
+ }
+ static errval_t
+ acquire_cluster(struct fat_mount *mount, uint32_t cluster, uint8_t **data)
+ {
+     return acquire_or_read(mount, mount->cluster_cache, cluster, data,
+             cluster_to_block(cluster, mount),
+             mount->block_size * mount->cluster_size);
+ }
+ static errval_t
+ release_cluster(struct fat_mount *mount, uint32_t cluster)
+ {
+     return fs_cache_release(mount->cluster_cache, cluster);
+ }
+ static void
+ dirsearch_initialize(struct fat_dirsearch *search, fat_direntry_t *parent)
+ {
+     memset(search, 0, sizeof(*search));
+     search->parent_direntry = parent;
+ }
+ static void
+ dirsearch_wipe(struct fat_dirsearch *search, struct fat_mount *mount)
+ {
+     errval_t err;
+     if (search->data) {
+         if (!search->parent_direntry && mount->fat_type == FAT_TYPE_FAT16) {
+             // the special rootdir area of FAT16 is handled with the block cache
+             err = release_block(mount, search->data_key);
+         }
+         else {
+             err = release_cluster(mount, search->data_key);
+         }
+         if (err_is_fail(err)) {
+             // this should not happen
+             USER_PANIC_ERR(err, "could not release search data cache");
+         }
+     }
+     memset(search, 0, sizeof(*search));
+ }
+ static uint32_t
+ next_cluster(struct fat_mount *mount, uint32_t cluster, uint32_t *rescluster)
+ {
+     TRACE_ENTER;
+     errval_t err;
+     // calculate block and offset
+     size_t cluster_fat_block = fat_block_for_cluster(cluster, mount);
+     size_t cluster_fat_offset = fat_offset_for_cluster(cluster, mount);
+     FAT_DEBUG_F("cluster %"PRIu32" is at fat block %zu + %zu",
+             cluster, cluster_fat_block, cluster_fat_offset);
+     // fetch block data
+     uint8_t *data;
+     err = acquire_block(mount, cluster_fat_block, &data);
+     if (err_is_fail(err)) {
+         return err;
+     }
+     // lookup cluster in found block
+     uint32_t result;
+     if (mount->fat_type == FAT_TYPE_FAT16) {
+         result = *(uint16_t*)(data+cluster_fat_offset);
+     }
+     else {
+         result = *(uint32_t*)(data+cluster_fat_offset);
+     }
+     // release cache ref
+     err = release_block(mount, cluster_fat_block);
+     if (err_is_fail(err)) {
+         return err;
+     }
+     FAT_DEBUG_F("next cluster is %"PRIu32, result);
+     *rescluster = result;
+     return SYS_ERR_OK;
+ }
+ static void
+ update_lfn(const uint8_t *entry_data, fat_direntry_t *entry,
+         uint16_t lfn_data[LFN_CHAR_COUNT])
+ {
+     TRACE_ENTER;
+     uint8_t seq_nr = entry_data[0];
+     FAT_DEBUG_F("updating lfn data from entry with seq_nr 0x%x", (int)seq_nr);
+     if (seq_nr & 0x40) {
+         // first entry, reset lfn_data
+         FAT_DEBUG("first entry, resetting lfn_data");
+         memset(lfn_data, 0, sizeof(lfn_data));
+     }
+     /*
+     if (seq_nr & 0x80) {
+         // entry is deleted
+         printf("entry is deleted");
+         return;
+     }
+     */
+     // remove flag bits from sequence number, make 0-based
+     seq_nr = (seq_nr & 0x1f) - 1;
+     static const size_t chars_per_lfn_entry = 13;
+     // chars 0-4 are 16-bit words from offset 0x1
+     for (size_t i = 0; i < 5; i++) {
+         uint16_t c = *(uint16_t*)(entry_data + 0x1 + i*2);
+         lfn_data[seq_nr*chars_per_lfn_entry+0+i] = c;
+     }
+     // chars 5-10 are 16-bit words from offset 0xe
+     for (size_t i = 0; i < 6; i++) {
+         uint16_t c = *(uint16_t*)(entry_data + 0xe + i*2);
+         lfn_data[seq_nr*chars_per_lfn_entry+5+i] = c;
+     }
+     // chars 11-12 are 16-bit words from offset 0x1c
+     for (size_t i = 0; i < 2; i++) {
+         uint16_t c = *(uint16_t*)(entry_data + 0x1c + i*2);
+         lfn_data[seq_nr*chars_per_lfn_entry+11+i] = c;
+     }
+ }
+ static errval_t
+ next_f16_rootdir_block(struct fat_mount *mount, struct fat_dirsearch *search,
+         uint8_t **data)
+ {
+     TRACE_ENTER;
+     errval_t err = SYS_ERR_OK;
+     assert(!search->parent_direntry);
+     assert(mount->fat_type == FAT_TYPE_FAT16);
+     // check that index is within number of valid root entries
+     size_t search_end = fat_bpb_rtc_rd(&mount->bpb);
+     if (search->index >= search_end) {
+         return FS_ERR_INDEX_BOUNDS;
+     }
+     // calculate block index
+     size_t bytes_offset = search->index * fat_direntry_size;
+     size_t block_index = bytes_offset / mount->block_size;
+     size_t block_offset = bytes_offset % mount->block_size;
+     FAT_DEBUG_F("search index %zu at block %zu + %zu",
+             search->index, block_index, block_offset);
+     if (!search->data || search->data_index != block_index) {
+         FAT_DEBUG("need new block data");
+         // determine block
+         size_t block = mount->rootdir_start + block_index;
+         FAT_DEBUG_F("file block %zu is block %zu", block_index, block);
+         // fetch new block data
+         uint8_t *new_data;
+         err = acquire_block(mount, block, &new_data);
+         if (err_is_fail(err)) {
+             return err;
+         }
+         // free old block data
+         if (search->data) {
+             err = release_block(mount, search->data_key);
+             if (err_is_fail(err)) {
+                 release_block(mount, block);
+                 return err;
+             }
+         }
+         search->data = new_data;
+         search->data_index = block_index;
+         search->data_key = block;
+     }
+     *data = search->data + block_offset;
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ next_subdir_block(struct fat_mount *mount, struct fat_dirsearch *search,
+         uint8_t **data)
+ {
+     TRACE_ENTER;
+     errval_t err = SYS_ERR_OK;
+     assert(search->parent_direntry || mount->fat_type == FAT_TYPE_FAT32);
+     // calculate block index
+     size_t bytes_offset = search->index * fat_direntry_size;
+     size_t block_index = bytes_offset / mount->block_size;
+     size_t cluster_index = block_index / mount->cluster_size;
+     size_t cluster_offset = bytes_offset %
+         (mount->block_size * mount->cluster_size);
+     FAT_DEBUG_F("search index %zu at cluster %zu + %zu",
+             search->index, cluster_index, cluster_offset);
+     if (!search->data || search->data_index != cluster_index) {
+         FAT_DEBUG("need new block data");
+         // get start cluster of direntry data
+         uint32_t cluster = 0;
+         if (search->parent_direntry) {
+             if (mount->fat_type == FAT_TYPE_FAT32) {
+                 cluster = (uint32_t)fat_direntry_starth_rd(search->parent_direntry) << 16;
+             }
+             cluster += fat_direntry_start_rd(search->parent_direntry);
+         }
+         else {
+             cluster = mount->rootdir_cluster;
+         }
+         // determine cluster corresponding to cluster_index
+         for (size_t clsidx = cluster_index;
+             cluster < mount->last_cluster_start && clsidx > 0;
+             clsidx--)
+         {
+             err = next_cluster(mount, cluster, &cluster);
+             if (err_is_fail(err)) {
+                 return err;
+             }
+         }
+         FAT_DEBUG_F("dir cluster %zu is cluster %"PRIu32, cluster_index, cluster);
+         if (cluster >= mount->last_cluster_start) {
+             return FS_ERR_INDEX_BOUNDS;
+         }
+         // read new cluster data
+         uint8_t *new_data;
+         err = acquire_cluster(mount, cluster, &new_data);
+         if (err_is_fail(err)) {
+             return err;
+         }
+         // free old data
+         if (search->data) {
+             err = release_cluster(mount, search->data_key);
+             if (err_is_fail(err)) {
+                 USER_PANIC_ERR(err, "could not release dir data cache");
+             }
+         }
+         search->data = new_data;
+         search->data_index = cluster_index;
+         search->data_key = cluster;
+     }
+     *data = search->data + cluster_offset;
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ read_next_direntry(struct fat_mount *mount, struct fat_dirsearch *search,
+         char dosfn[12], uint16_t lfn_data[LFN_CHAR_COUNT], bool *has_lfn,
+         uint8_t direntry_data[fat_direntry_size])
+ {
+     TRACE_ENTER;
+     errval_t err = SYS_ERR_OK;
+     bool has_lfn_ = false;
+     for ( ; ; search->index++) {
+         FAT_DEBUG_F("checking search index %zu", search->index);
+         uint8_t *entry_data = NULL;
+         bool special_rootdir = !search->parent_direntry &&
+             mount->fat_type == FAT_TYPE_FAT16;
+         if (special_rootdir) {
+             err = next_f16_rootdir_block(mount, search, &entry_data);
+         }
+         else {
+             err = next_subdir_block(mount, search, &entry_data);
+         }
+         if (err_is_fail(err)) {
+             return err;
+         }
+         fat_direntry_t entry;
+         fat_direntry_initialize(&entry, (char*)entry_data);
+         // check for various kinds of non-regular entries
+         if (fat_direntry_fn_rd(&entry, 0) == 0xe5) {
+             // 0xe5 implies deleted, skip
+             FAT_DEBUG("found 0xe5 direntry");
+             has_lfn_ = false;
+             continue;
+         }
+         else if (fat_direntry_attr_rd(&entry) == 0xf) {
+             // (attr == ro | hidden | system | volume) implies long filename
+             FAT_DEBUG("found lfn direntry");
+             if (entry_data[0] & 0x40 && !(entry_data[0] & 0x80)) {
+                 // first lfn entry and not deleted implies next real entry has a lfn
+                 has_lfn_ = true;
+             }
+             update_lfn(entry_data, &entry, lfn_data);
+             continue;
+         }
+         else if (!entry_data[0]) {
+             // entries with names starting at 0 have never been allocated, so
+             // later entries are unused too
+             FAT_DEBUG("found clean direntry");
+             return FS_ERR_INDEX_BOUNDS;
+         }
+         else if (fat_direntry_start_rd(&entry) == 0 &&
+                 !fat_direntry_attr_dir_rdf(&entry)) {
+             // entries with starting cluster 0 should only occur in directory
+             // and lfn entries.  lfn entries are already handled, so something
+             // weird is going on if we get here.
+             FAT_DEBUG("found non-lfn/dir direntry with start cluster 0");
+             continue;
+         }
+ #ifdef FAT_DEBUG_ENABLED
+         FAT_DEBUG_P();
+         // dump found names
+         printf("8.3 entry: \"");
+         for (uint8_t *p = entry_data; p < entry_data+11; p++) {
+             putchar(*p);
+         }
+         printf("\", lfn entry: ");
+         if (has_lfn_) {
+             putchar('"');
+             for (uint16_t *p = lfn_data; *p && p < lfn_data+LFN_CHAR_COUNT; p++) {
+                 putchar(*p > ASCII_MAX ? '?' : (char)*p);
+             }
+             putchar('"');
+         }
+         else {
+             printf("(none)");
+         }
+         putchar('\n');
+ #endif
+         // at this point, we've found a real entry
+         memcpy(dosfn, entry_data, 11);
+         dosfn[11] = 0;
+         *has_lfn = has_lfn_;
+         memcpy(direntry_data, entry_data, fat_direntry_size);
+         search->index++;
+         return SYS_ERR_OK;
+     }
+     return FS_ERR_INDEX_BOUNDS;
+ }
+ static errval_t
+ find_path(struct fat_mount *mount, const char *path,
+         uint8_t direntry[fat_direntry_size])
+ {
+     TRACE_ENTER_F("\"%s\"", path);
+     errval_t err = SYS_ERR_OK;
+     const char *part_begin = path, *part_end;
+     fat_direntry_t parent_entry;
+     fat_direntry_t *parent = NULL;
+     do {
+         if (*part_begin == '/') {
+             part_begin++;
+         }
+         for (part_end = part_begin+1; *part_end && *part_end != '/'; part_end++) ;
+         size_t part_len = part_end - part_begin;
+ #ifdef FAT_DEBUG_ENABLED
+         {
+             char part[part_len+1];
+             strncpy(part, part_begin, part_len);
+             part[part_len]=0;
+             FAT_DEBUG_F("part \"%s\"", part);
+         }
+ #endif
+         struct fat_dirsearch search;
+         dirsearch_initialize(&search, parent);
+         char dosfn[12];
+         uint16_t lfn_data[LFN_CHAR_COUNT];
+         bool has_lfn;
+         char buf[LFN_CHAR_COUNT];
+         do {
+             err = read_next_direntry(mount, &search, dosfn, lfn_data,
+                     &has_lfn, direntry);
+             if (err_is_fail(err)) {
+                 dirsearch_wipe(&search, mount);
+                 if (err == FS_ERR_INDEX_BOUNDS) {
+                     err = FS_ERR_NOTFOUND;
+                 }
+                 return err;
+             }
+             if (has_lfn) {
+                 size_t len;
+                 for (len = 0; len < LFN_CHAR_COUNT; len++) {
+                     if (!lfn_data[len] || lfn_data[len] > ASCII_MAX) {
+                         break;
+                     }
+                 }
+                 if (lfn_data[len] > ASCII_MAX) {
+                     continue;
+                 }
+                 for (size_t i = 0; i < len; ++i) {
+                     buf[i] = (char)lfn_data[i];
+                 }
+                 buf[len] = 0;
+             }
+             else {
+                 if (dos2unixfn((const unsigned char*)dosfn, (unsigned char*)buf, LFN_CHAR_COUNT)) {
+                     // TODO: handle error
+                 }
+             }
+             FAT_DEBUG_F("comparing part to %s", buf);
+             bool match = strncmp(part_begin, buf, part_len) == 0 && buf[part_len] == 0;
+             if (match) {
+                 break;
+             }
+         } while (1);
+         parent = &parent_entry;
+         fat_direntry_initialize(parent, (char*)direntry);
+         dirsearch_wipe(&search, mount);
+         part_begin = part_end;
+     } while(*part_begin);
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ openhandle(struct fat_mount *mount, const char *path, struct fat_handle_common *handle)
+ {
+     TRACE_ENTER;
+     errval_t err = SYS_ERR_OK;
+     err = find_path(mount, path, handle->dirent_data);
+     if (err_is_fail(err)) {
+         return err;
+     }
+     fat_direntry_initialize(&handle->dirent, (char*)handle->dirent_data);
+ #ifdef FAT_DEBUG_ENABLED
+     /*DUMP_DEV(fat_direntry, &handle->dirent, 4096);*/
+ #endif
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ open(void *st, const char *path, vfs_handle_t *fhandle)
+ {
+     TRACE_ENTER_F("\"%s\"", path);
+     errval_t err = SYS_ERR_OK;
+     struct fat_handle *handle = NULL;
+     struct fat_mount *mount = st;
+     handle = calloc(1, sizeof(*handle));
+     if (!handle) {
+         err = LIB_ERR_MALLOC_FAIL;
+         goto error;
+     }
+     err = openhandle(mount, path, &handle->h);
+     if (err_is_fail(err)) {
+         goto error;
+     }
+     if (fat_direntry_attr_dir_rdf(&handle->h.dirent)) {
+         err = FS_ERR_NOTFILE;
+         goto error;
+     }
+     *fhandle = handle;
+     goto end;
+ error:
+     free(handle);
+ end:
+     return err;
+ }
+ static errval_t
+ create(void *st, const char *path, vfs_handle_t *handle)
+ {
+     TRACE_ENTER;
+     return LIB_ERR_NOT_IMPLEMENTED;
+ }
+ static errval_t
+ fat_remove(void *st, const char *path)
+ {
+     TRACE_ENTER;
+     return LIB_ERR_NOT_IMPLEMENTED;
+ }
+ static errval_t
+ read(void *st, vfs_handle_t fhandle, void *buffer, size_t bytes, size_t *bytes_read)
+ {
+     TRACE_ENTER;
+     errval_t err;
+     struct fat_handle *handle = fhandle;
+     struct fat_mount *mount = st;
+     size_t file_size = fat_direntry_size_rd(&handle->h.dirent);
+     size_t offset = handle->offset;
+     assert(bytes_read);
+     *bytes_read = 0;
+     // limited requested bytes to remaining file size
+     size_t file_remainder = file_size - handle->offset;
+     if (bytes > file_remainder) {
+         bytes = file_remainder;
+     }
+     if (bytes == 0) {
+         return SYS_ERR_OK;
+     }
+     FAT_DEBUG_F("reading %zu bytes", bytes);
+     fat_direntry_t *dirent = &handle->h.dirent;
+     int isdir = fat_direntry_attr_dir_rdf(dirent);
+     if (isdir) {
+         return FS_ERR_NOTFILE;
+     }
+     size_t remaining = bytes;
+     do {
+         // split read offset into cluster index and offset within cluster
+         size_t cluster_index = cluster_for_offset(offset, mount);
+         size_t cluster_offset = offset_from_cluster(offset, mount);
+         FAT_DEBUG_F("reading from file cluster %zu + %zu",
+                 cluster_index, cluster_offset);
+         // determin read size, only read single cluster and not beyond end of file
+         size_t read_size = remaining;
+         size_t cluster_remainder = mount->cluster_size * mount->block_size - cluster_offset;
+         if (cluster_remainder < read_size) {
+             read_size = cluster_remainder;
+         }
+         file_remainder = fat_direntry_size_rd(dirent) - offset;
+         if (file_remainder < read_size) {
+             read_size = file_remainder;
+         }
+         FAT_DEBUG_F("reading %zu from cluster (clus_rem=%zu, f_rem=%zu)",
+                 read_size, cluster_remainder, file_remainder);
+         // determine cluster corresponding to cluster_index
+         uint32_t cluster = fat_direntry_start_rd(dirent);
+         if (mount->fat_type == FAT_TYPE_FAT32) {
+             cluster += (uint32_t)fat_direntry_starth_rd(dirent) << 16;
+         }
+         for (size_t clsidx = cluster_index; clsidx > 0; clsidx--) {
+             err = next_cluster(mount, cluster, &cluster);
+             if (err_is_fail(err)) {
+                 return err;
+             }
+         }
+         FAT_DEBUG_F("file cluster %zu is cluster %"PRIu32, cluster_index, cluster);
+         assert(cluster < mount->last_cluster_start);
+         // fetch data and copy into buffer
+         uint8_t *data;
+         err = acquire_cluster(mount, cluster, &data);
+         if (err_is_fail(err)) {
+             return err;
+         }
+         memcpy(buffer, data+cluster_offset, read_size);
+         err = release_cluster(mount, cluster);
+         if (err_is_fail(err)) {
+             // should not happen
+             USER_PANIC_ERR(err, "could not release file cluster cache");
+         }
+         // update variables for successful read
+         buffer = (char *)buffer + read_size;
+         remaining -= read_size;
+         *bytes_read += read_size;
+         offset += read_size;
+         FAT_DEBUG_F("read of cluster %"PRIu32" completed", cluster);
+     } while (remaining);
+     // read completed, update handle's offset
+     FAT_DEBUG_F("read of %zu bytes completed", bytes);
+     handle->offset = offset;
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ write(void *st, vfs_handle_t handle, const void *buffer, size_t bytes,
+         size_t *bytes_written)
+ {
+     TRACE_ENTER;
+     return LIB_ERR_NOT_IMPLEMENTED;
+ }
+ static errval_t
+ truncate(void *st, vfs_handle_t handle, size_t bytes)
+ {
+     TRACE_ENTER;
+     return LIB_ERR_NOT_IMPLEMENTED;
+ }
+ static errval_t
+ seek(void *st, vfs_handle_t fhandle, enum vfs_seekpos whence, off_t offset)
+ {
+     TRACE_ENTER;
+     struct fat_handle *handle = fhandle;
+     size_t size = fat_direntry_size_rd(&handle->h.dirent);
+     size_t base = 0;
+     switch (whence) {
+     case VFS_SEEK_SET: base = 0; break;
+     case VFS_SEEK_CUR: base = handle->offset; break;
+     case VFS_SEEK_END: base = size; break;
+     default: USER_PANIC("invalid whence argument to fat seek"); break;
+     }
+     if (offset < 0 && (-offset) > base) {
+         handle->offset = 0;
+     }
+     else if (base + offset > size) {
+         handle->offset = size;
+     }
+     else {
+         handle->offset = base + offset;
+     }
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ tell(void *st, vfs_handle_t fhandle, size_t *pos)
+ {
+     TRACE_ENTER;
+     struct fat_handle *handle = fhandle;
+     *pos = handle->offset;
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ stat(void *st, vfs_handle_t handle, struct vfs_fileinfo *info)
+ {
+     TRACE_ENTER;
+     struct fat_handle_common *fhc = handle;
+     if (fat_direntry_attr_dir_rdf(&fhc->dirent)) {
+         info->type = VFS_DIRECTORY;
+         info->size = 0; // TODO: something more useful?
+     }
+     else {
+         info->type = VFS_FILE;
+         info->size = fat_direntry_size_rd(&fhc->dirent);
+     }
+     return SYS_ERR_OK;;
+ }
+ static errval_t
+ close(void *st, vfs_handle_t fhandle)
+ {
+     TRACE_ENTER;
+     struct fat_handle *handle = fhandle;
+     free(handle);
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ mkdir(void *st, const char *path)
+ {
+     TRACE_ENTER_F("\"%s\"", path);
+     // fail if already present
+     return LIB_ERR_NOT_IMPLEMENTED;
+ }
+ static errval_t
+ rmdir(void *st, const char *path)
+ {
+     TRACE_ENTER_F("\"%s\"", path);
+     // fail if not empty
+     return LIB_ERR_NOT_IMPLEMENTED;
+ }
+ static errval_t
+ opendir(void *st, const char *path, vfs_handle_t *dhandle)
+ {
+     TRACE_ENTER_F("\"%s\"", path);
+     errval_t err = SYS_ERR_OK;
+     struct fat_dirhandle *new_handle = NULL;
+     struct fat_mount *mount = st;
+     new_handle = calloc(1, sizeof(*new_handle));
+     if (!new_handle) {
+         err = LIB_ERR_MALLOC_FAIL;
+         goto error;
+     }
+     if (!path[0] || (path[0] == '/' && path[1] == '\0')) {
+         FAT_DEBUG("got opendir for root");
+         *dhandle = new_handle;
+         goto end;
+     }
+     err = openhandle(mount, path, &new_handle->h);
+     if (err_is_fail(err)) {
+         goto error;
+     }
+     if (!fat_direntry_attr_dir_rdf(&new_handle->h.dirent)) {
+         err = FS_ERR_NOTDIR;
+         goto error;
+     }
+     FAT_DEBUG_F("directory %s found", path);
+     dirsearch_initialize(&new_handle->search, &new_handle->h.dirent);
+     *dhandle = new_handle;
+     goto end;
+ error:
+     free(new_handle);
+ end:
+     return err;
+ }
+ static errval_t
+ dir_read_next(void *st, vfs_handle_t dhandle, char **name, struct vfs_fileinfo *info)
+ {
+     TRACE_ENTER;
+     errval_t err = SYS_ERR_OK;
+     struct fat_dirhandle *handle = dhandle;
+     struct fat_mount *mount = st;
+     FAT_DEBUG_F("handle->search.index = %zu", handle->search.index);
+     char dosfn[12];
+     uint16_t lfn_data[LFN_CHAR_COUNT];
+     bool has_lfn;
+     uint8_t dirent_data[fat_direntry_size];
+     fat_direntry_t dirent;
+     // read next direntry
+     err = read_next_direntry(mount, &handle->search, dosfn, lfn_data,
+             &has_lfn, dirent_data);
+     if (err_is_fail(err)) {
+         return err;
+     }
+     fat_direntry_initialize(&dirent, (char*)dirent_data);
+     if (has_lfn) {
+         size_t len;
+         for (len = 0; len < LFN_CHAR_COUNT; len++) {
+             if (!lfn_data[len]) {
+                 break;
+             }
+         }
+         char *buf = malloc(len+1);
+         if (!buf) {
+             return LIB_ERR_MALLOC_FAIL;
+         }
+         for (size_t i = 0; i < len; ++i) {
+             if (lfn_data[i] > ASCII_MAX) {
+                 buf[i] = '?';
+             }
+             else {
+                 buf[i] = (char)lfn_data[i];
+             }
+         }
+         buf[len] = 0;
+         *name = buf;
+     }
+     else {
+         char *buf = malloc(DOSFN_MAX_LEN_UTF8);
+         dos2unixfn((const unsigned char*)dosfn, (unsigned char*)buf, DOSFN_MAX_LEN_UTF8);
+         *name = buf;
+     }
+ #ifdef FAT_DEBUG_ENABLED
+     /*DUMP_DEV(fat_direntry, &dirent, 4096);*/
+ #endif
+     bool isdir = fat_direntry_attr_dir_rdf(&dirent);
+     info->type = isdir ? VFS_DIRECTORY : VFS_FILE;
+     info->size = fat_direntry_size_rd(&dirent);
+     return SYS_ERR_OK;
+ }
+ static errval_t
+ closedir(void *st, vfs_handle_t dhandle)
+ {
+     TRACE_ENTER;
+     errval_t err = SYS_ERR_OK;
+     struct fat_mount *mount = st;
+     struct fat_dirhandle *handle = dhandle;
+     dirsearch_wipe(&handle->search, mount);
+     free(handle);
+     return err;
+ }
+ struct vfs_ops fat_ops = {
+     .open = open,
+     .create = create,
+     .remove = fat_remove,
+     .read = read,
+     .write = write,
+     .truncate = truncate,
+     .seek = seek,
+     .tell = tell,
+     .stat = stat,
+     .close = close,
+     .opendir = opendir,
+     .dir_read_next = dir_read_next,
+     .closedir = closedir,
+     .mkdir = mkdir,
+     .rmdir = rmdir,
+ };
+ static void
+ ahci_init_cb(void *st, errval_t err, struct ahci_binding *b)
+ {
+     TRACE_ENTER;
+     struct fat_mount *mount = st;
+     
+     if (err_is_fail(err)) {
+         mount->bind_err = err;
+         return;
+     }
+     mount->ahci_binding = b;
+ }
+ static void
+ ahci_close_cb(void *arg)
+ {
+     *(bool *)arg = true;
+ }
+ errval_t
+ vfs_fat_mount(const char *uri, void **retst, struct vfs_ops **retops)
+ {
+     TRACE_ENTER;
+     errval_t err;
+     // format: scheme://port[+offset]
+     int type;
+     if (strncmp(uri, "fat16://", 8) == 0) {
+         type = FAT_TYPE_FAT16;
+     }
+     else if (strncmp(uri, "fat32://", 8) == 0) {
+         type = FAT_TYPE_FAT32;
+     }
+     else {
+         return VFS_ERR_BAD_URI;
+     }
+     // skip scheme
+     const char *puri = uri + 8;
+     // parse port
+     if (*puri < '0' || *puri > '9') {
+         return VFS_ERR_BAD_URI;
+     }
+     size_t port = 0, startblock = 0;
+     while (*puri >= '0' && *puri <= '9') {
+         port = port*10 + (*puri++ - '0');
+     }
+     if (*puri == '+') {
+         // parse offset
+         puri++;
+         if (*puri < '0' || *puri > '9') {
+             return VFS_ERR_BAD_URI;
+         }
+         while (*puri >= '0' && *puri <= '9') {
+             startblock = startblock*10 + (*puri++ - '0');
+         }
+     }
+     if (*puri != 0) {
+         return VFS_ERR_BAD_URI;
+     }
+     FAT_DEBUG_F("got mount for port %zu, offset %zu", port, startblock);
+     struct fat_mount *mount = calloc(1, sizeof(struct fat_mount));
+     if (!mount) {
+         return LIB_ERR_MALLOC_FAIL;
+     }
+     mount->fat_type = type;
+     mount->startblock = startblock;
+     err = ahci_init(port, ahci_init_cb, mount, get_default_waitset());
+     if (err_is_fail(err)) {
+         goto ahci_init_failed;
+     }
+     while (!mount->ahci_binding && err_is_ok(mount->bind_err)) {
+         messages_wait_and_handle_next();
+     }
+     if (err_is_fail(mount->bind_err)) {
+         err = mount->bind_err;
+         goto ahci_init_failed;
+     }
+     FAT_DEBUG("ahci_init completed");
+     struct ahci_ata_rw28_binding *ahci_ata_rw28_binding;
+     ahci_ata_rw28_binding = calloc(1, sizeof(struct ahci_ata_rw28_binding));
+     err = ahci_ata_rw28_init(ahci_ata_rw28_binding,
+             get_default_waitset(), mount->ahci_binding);
+     if (err_is_fail(err)) {
+         goto ata_rw28_init_failed;
+     }
+     FAT_DEBUG("ahci_ata_rw28_init completed");
+     mount->ata_rw28_binding = (struct ata_rw28_binding*)ahci_ata_rw28_binding;
+     err = ata_rw28_rpc_client_init(&mount->ata_rw28_rpc, mount->ata_rw28_binding);
+     if (err_is_fail(err)) {
+         goto ata_rw28_init_failed;
+     }
+     FAT_DEBUG("ata_rw28_rpc_client_init completed");
+     // read data from fat boot sector
+     uint8_t *data;
+     size_t size;
+     err = mount->ata_rw28_rpc.vtbl.read_dma_block(&mount->ata_rw28_rpc,
+             mount->startblock, &data, &size);
+     if (err_is_fail(err)) {
+         goto bootsec_read_failed;
+     }
+     assert(size == 512);
+ #ifdef FAT_DEBUG_ENABLED
+     FAT_DEBUG("dumping sector 0 raw");
+     for (size_t i = 0; i < size; i+=16) {
+         printf("%02x %02x %02x %02x  %02x %02x %02x %02x    "
+                 "%02x %02x %02x %02x  %02x %02x %02x %02x\n",
+                 data[i+0], data[i+1], data[i+2], data[i+3],
+                 data[i+4], data[i+5], data[i+6], data[i+7],
+                 data[i+8], data[i+9], data[i+10], data[i+11],
+                 data[i+12], data[i+13], data[i+14], data[i+15]
+                 );
+     }
+     FAT_DEBUG("end sector 0 dump");
+ #endif
+     memcpy(mount->bootsec_data, data, size);
+     free(data);
+     data = NULL;
+     if (memcmp(mount->bootsec_data+0x1FE, "\x55\xAA", 2) != 0) {
+         FAT_DEBUG_F("boot sector check bytes do not match, expected 0x55 0xAA,"
+                 " got 0x%02x 0x%02x", mount->bootsec_data[0x1FE],
+                 mount->bootsec_data[0x1FF]);
+         goto fs_check_failed;
+     }
+     fat_bpb_initialize(&mount->bpb, (char*)&mount->bootsec_data);
+     mount->block_size = fat_bpb_bps_rd(&mount->bpb);
+     mount->cluster_size = fat_bpb_spc_rd(&mount->bpb);
+     mount->fat_start = mount->startblock + fat_bpb_rsvs_rd(&mount->bpb);
+     size_t ssc = fat_bpb_ssc_rd(&mount->bpb), lsc = fat_bpb_lsc_rd(&mount->bpb);
+     if (ssc && lsc) {
+         FAT_DEBUG_F("both small and large sector count are nonzero:"
+                 " ssc=%zu, lsc=%zu", ssc, lsc);
+         goto fs_check_failed;
+     }
+     else if (ssc) {
+         mount->block_count = ssc;
+     }
+     else if (lsc) {
+         mount->block_count = lsc;
+     }
+     else {
+         FAT_DEBUG("both small and large sector count are zero");
+         goto fs_check_failed;
+     }
+ #ifdef FAT_DEBUG_ENABLED
+     DUMP_DEV(fat_bpb, &mount->bpb, 4096);
+ #endif
+     if (mount->fat_type == FAT_TYPE_FAT16) {
+         fat16_ebpb_initialize(&mount->ebpb.f16, (char*)&mount->bootsec_data);
+ #ifdef FAT_DEBUG_ENABLED
+         DUMP_DEV(fat16_ebpb, &mount->ebpb.f16, 4096);
+ #endif
+         uint8_t signature = fat16_ebpb_ebs_rd(&mount->ebpb.f16);
+         if (signature == 0x28) {
+             FAT_DEBUG("FAT16 EBPB signature 0x28 indicates unsupported FAT type");
+             goto fs_check_failed;
+         }
+         if (signature != 0x29) {
+             FAT_DEBUG_F("FAT16 EBPB signature does not match, expected 0x29,"
+                     " got 0x%02x", signature);
+             goto fs_check_failed;
+         }
+         mount->fat_size = fat_bpb_spf_rd(&mount->bpb);
+         mount->rootdir_start = mount->fat_start +
+             fat_bpb_fatc_rd(&mount->bpb) * mount->fat_size;
+         mount->clusters_start = mount->rootdir_start +
+             CEIL_DIV(fat_bpb_rtc_rd(&mount->bpb) * fat_direntry_size,
+                     fat_bpb_bps_rd(&mount->bpb));
+         mount->last_cluster_start = 0xfff8;
+     }
+     else {
+         fat32_ebpb_initialize(&mount->ebpb.f32, (char*)&mount->bootsec_data);
+ #ifdef FAT_DEBUG_ENABLED
+         DUMP_DEV(fat32_ebpb, &mount->ebpb.f32, 4096);
+ #endif
+         uint8_t signature = fat32_ebpb_ebs_rd(&mount->ebpb.f32);
+         if (signature != 0x29) {
+             FAT_DEBUG_F("FAT32 EBPB signature does not match, expected 0x29,"
+                     " got 0x%02x", signature);
+             goto fs_check_failed;
+         }
+         mount->fat_size = fat32_ebpb_spf_rd(&mount->ebpb.f32);
+         mount->rootdir_cluster = fat32_ebpb_rtst_rd(&mount->ebpb.f32);
+         mount->clusters_start = mount->fat_start +
+             fat_bpb_fatc_rd(&mount->bpb) * mount->fat_size;
+         mount->last_cluster_start = 0xfffffff8;
+         size_t fs_info_sector = fat32_ebpb_fsis_rd(&mount->ebpb.f32);
+         if (fs_info_sector <= 0 || fs_info_sector >= mount->block_count) {
+             FAT_DEBUG_F("File System Information Sector out of range,"
+                     " block=%zu, block_count=%zu", fs_info_sector,
+                     mount->block_count);
+             goto fs_check_failed;
+         }
+         mount->ata_rw28_rpc.vtbl.read_dma_block(&mount->ata_rw28_rpc,
+                 mount->startblock + fs_info_sector, &data, &size);
+         if (memcmp(data+0, "RRaA", 4) != 0 ||
+             memcmp(data+0x1e4, "rrAa", 4) != 0)
+         {
+             FAT_DEBUG_F("File System Information Sector signatures do not match,"
+                     " %"PRIx32", %"PRIx32, *(uint32_t*)(data+0),
+                     *(uint32_t*)(data+0x1e4));
+             goto fs_check_failed;
+         }
+         if (memcmp(data+0x1fe, "\x55\xAA", 2) != 0) {
+             FAT_DEBUG("File System Information Sector check bytes do not match");
+             goto fs_check_failed;
+         }
+ #ifdef FAT_DEBUG_ENABLED
+         FAT_DEBUG("dumping FSIS");
+         printf("nr of free clusters: %"PRIu32"\n", *(uint32_t*)(data+0x1e8));
+         printf("most recently allocated cluster: %"PRIu32"\n",
+                 *(uint32_t*)(data+0x1ec));
+         printf("----------------\n");
+ #endif
+         free(data);
+         data = NULL;
+     }
+ #ifdef FAT_DEBUG_ENABLED
+     FAT_DEBUG("dumping mount variables");
+ #define D(v) printf("%-18s %zu\n", #v ":", mount-> v )
+     D(block_size);
+     D(cluster_size);
+     D(fat_start);
+     D(fat_size);
+     D(rootdir_start);
+     D(rootdir_cluster);
+     D(clusters_start);
+ #undef D
+     printf("----------------\n");
+ #endif
+     err = fs_cache_init(1<<7, 1<<8, &mount->block_cache);
+     if (err_is_fail(err)) {
+         goto cache_init_failed;
+     }
+     err = fs_cache_init(1<<7, 1<<8, &mount->cluster_cache);
+     if (err_is_fail(err)) {
+         goto cache_init_failed;
+     }
+     *retops = &fat_ops;
+     *retst = mount;
+     return SYS_ERR_OK;
+ cache_init_failed:
+     if (mount->block_cache) {
+         fs_cache_free(mount->block_cache);
+     }
+     if (mount->cluster_cache) {
+         fs_cache_free(mount->cluster_cache);
+     }
+     goto bootsec_read_failed;
+ fs_check_failed:
+     err = FAT_ERR_BAD_FS;
+ bootsec_read_failed:
+     if (data) {
+         free(data);
+     }
+     data = NULL;
+ ata_rw28_init_failed:
+     free(ahci_ata_rw28_binding);
+     bool closed = false;
+     errval_t err2 = ahci_close(mount->ahci_binding, MKCLOSURE(ahci_close_cb, &closed));
+     if (err_is_ok(err2)) {
+         while (!closed) {
+             event_dispatch(mount->ahci_binding->waitset);
+         }
+     }
+     // TODO: bindings leak? how to close flounder connection?
+ ahci_init_failed:
+     free(mount);
+     return err;
+ }
@@@ -11,9 -11,9 +11,8 @@@
  --------------------------------------------------------------------------
  
  [
- build application { target = "bmp_bench",
-                   cFiles = [ "bmpbench.c", "bb_test.c", "bb_pingpong.c" ],
-                   flounderBindings = [ "test", "ping_pong" ],
-                   addLibraries = [ "trace" ]
+ build application { target = "ahci_bench",
+                   cFiles = [ "main.c" ],
 -                  addLibraries = [ "vfs", "nfs", "ahci", "lwip",  "contmng", "procon",
 -                                       "pci", "trace", "skb" ]
++                  addLibraries = libDeps [ "pci", "trace", "skb", "vfs"]
                   }
  ]
index 0000000,73e0ec4..5ca5672
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,926 +1,927 @@@
+ /**
+  * \file
+  * \brief ahci benchmarks
+  */
+ /*
+  * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich.
+  * All rights reserved.
+  *
+  * This file is distributed under the terms in the attached LICENSE file.
+  * If you do not find this file, copies can be found by writing to:
+  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+  */
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <inttypes.h>
+ #include <barrelfish/barrelfish.h>
++#include <limits.h>
+ #include <trace/trace.h>
+ #include <vfs/vfs.h>
+ #include <vfs/vfs_path.h>
+ #define ENTRIES(array)  (sizeof(array) / sizeof(array[0]))
+ static const char *cwd = "/";
+ static int mount(const char *path, const char *uri)
+ {
+     char *apath = vfs_path_mkabsolute(cwd, path);
+     errval_t err = vfs_mount(apath, uri);
+     free(apath);
+     if (err_is_fail(err)) {
+         DEBUG_ERR(err, "in vfs_mount %s %s", path, uri);
+         return 1;
+     }
+     return 0;
+ }
+ static double
+ dd_bench(char *source, char *target, size_t blocksize, size_t count)
+ {
+     vfs_handle_t source_vh = NULL;
+     vfs_handle_t target_vh = NULL;
+     size_t blocks_written = 0;
+     size_t total_bytes_read = 0;
+     size_t total_bytes_written = 0;
+     errval_t err;
+     double ret = 0;
+     double kbps = 0.0;
+     err = vfs_open(source, &source_vh);
+     if (err_is_fail(err)) {
+         printf("%s: %s (%ld)\n", source, err_getstring(err), err);
+         return -1;
+     }
+     err = vfs_create(target, &target_vh);
+     if (err_is_fail(err)) {
+         // close source handle
+         if (source_vh != NULL)
+             vfs_close(source_vh);
+         printf("%s: %s (%ld)\n", target, err_getstring(err), err);
+         return -1;
+     }
+     uint8_t *buffer = malloc(blocksize);
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t tscperms;
+     err = sys_debug_get_tsc_per_ms(&tscperms);
+     assert(err_is_ok(err));
+     uint64_t start = rdtsc();
+ #endif
+     if (buffer == NULL) {
+         ret = -2;
+         printf("failed to allocate buffer of size %zd\n", blocksize);
+         goto out;
+     }
+     size_t rsize, wsize;
+     do {
+         err = vfs_read(source_vh, buffer, blocksize, &rsize);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "error reading file");
+             ret = -1;
+             goto out;
+         }
+         total_bytes_read += rsize;
+         size_t wpos = 0;
+         while (wpos < rsize) {
+             if (wpos > 0)
+                 printf("was unable to write the whole chunk of size %zd. Now at pos: %zd of buffer\n", rsize, wpos);
+             err = vfs_write(target_vh, &buffer[wpos], rsize - wpos, &wsize);
+             if (err_is_fail(err) || wsize == 0) {
+                 DEBUG_ERR(err, "error writing file");
+                 ret = -1;
+                 goto out;
+             }
+             wpos += wsize;
+             total_bytes_written += wsize;
+         }
+         blocks_written++;
+     } while (rsize > 0 && !(count > 0 && blocks_written >= count));
+ out:
+     if (buffer != NULL)
+         free(buffer);
+     if (source_vh != NULL) {
+         err = vfs_close(source_vh);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+         }
+     }
+     if (target_vh != NULL) {
+         err = vfs_close(target_vh);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+         }
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t stop = rdtsc();
+     uint64_t elapsed_msecs = ((stop - start) / tscperms);
+     double elapsed_secs = (double)elapsed_msecs/1000.0;
+     kbps = ((double)total_bytes_written / 1024.0) / elapsed_secs;
+     if (ret == 0)
+         return kbps;
+     else
+         return ret;
+ #else
+     return ret;
+ #endif
+ }
+ struct bench_res {
+     double read;
+     double write;
+ };
+ static errval_t
+ fill_bench(char *target, uint8_t *buffer, size_t blocksize, size_t count, struct bench_res *result)
+ {
+     vfs_handle_t target_vh = NULL;
+     size_t blocks_written = 0;
+     size_t blocks_read = 0;
+     size_t total_bytes_read = 0;
+     size_t total_bytes_written = 0;
+     uint64_t start = 0, stop = 0;
+     errval_t err;
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t tscperms;
+     err = sys_debug_get_tsc_per_ms(&tscperms);
+     assert(err_is_ok(err));
+ #endif
+     errval_t ret = 0;
+     err = vfs_open(target, &target_vh);
+     if (err_is_fail(err)) {
+         printf("%s: %s (%ld)\n", target, err_getstring(err), err);
+         return err;
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     start = rdtsc();
+ #endif
+     if (buffer == NULL) {
+         ret = -2;
+         printf("failed to allocate buffer of size %zd\n", blocksize);
+         goto out;
+     }
+     size_t wsize;
+     do {
+         // initialize buffer
+         for (size_t i = 0; i < blocksize; i += sizeof(size_t))
+             *((size_t *)(buffer + i)) = blocks_written;
+         // write to file
+         size_t wpos = 0;
+         while (wpos < blocksize) {
+             if (wpos > 0)
+                 printf("was unable to write the whole chunk of size %zd. Now at pos: %zd of buffer\n", blocksize, wpos);
+             err = vfs_write(target_vh, &buffer[wpos], blocksize - wpos, &wsize);
+             if (err_is_fail(err) || wsize == 0) {
+                 DEBUG_ERR(err, "error writing file");
+                 ret = err;
+                 goto out;
+             }
+             wpos += wsize;
+             total_bytes_written += wsize;
+         }
+         blocks_written++;
+     } while (blocksize > 0 && !(count > 0 && blocks_written >= count));
+     err = vfs_close(target_vh);
+     if (err_is_fail(err)) {
+         DEBUG_ERR(err, "in vfs_close");
+         goto out;
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     stop = rdtsc();
+     {
+         uint64_t elapsed_msecs = ((stop - start) / tscperms);
+         double elapsed_secs = (double)elapsed_msecs/1000.0;
+         result->write = ((double)total_bytes_written / 1024.0) / elapsed_secs;
+       printf("%lf\n", result->write);
+     }
+ #endif
+     err = vfs_open(target, &target_vh);
+     if (err_is_fail(err)) {
+         printf("%s: %s (%ld)\n", target, err_getstring(err), err);
+         goto out;
+     }
+     err = vfs_seek(target_vh, VFS_SEEK_SET, 0);
+     if (err_is_fail(err)) {
+         DEBUG_ERR(err, "seeking failed");
+         ret = err;
+         goto out;
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     start = rdtsc();
+ #endif
+     size_t rsize;
+     do {
+         // read
+         size_t rpos = 0;
+         while (rpos < blocksize) {
+             if (rpos > 0)
+                 printf("was unable to read whole chunk of size %zd. Now at pos: %zd of buffer\n", blocksize, rpos);
+             err = vfs_read(target_vh, &buffer[rpos], blocksize - rpos, &rsize);
+             if (err_is_fail(err) || wsize == 0) {
+                 DEBUG_ERR(err, "error reading file");
+                 ret = err;
+                 goto out;
+             }
+             rpos += rsize;
+             total_bytes_read += rsize;
+         }
+         // verify data
+         for (size_t i = 0; i < blocksize; i += sizeof(size_t)) {
+             if (*((size_t *)(buffer + i)) != blocks_read) {
+                 printf("Verification failed! Block %zd, value %zd\n", blocks_read, *((size_t *)(buffer + i)) );
+                 ret = err;
+                 goto out;
+             }
+         }
+         blocks_read++;
+     } while (blocksize > 0 && !(count > 0 && blocks_read >= count));
+ out:
+     if (target_vh != NULL) {
+         err = vfs_close(target_vh);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+             ret = err;
+         }
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     stop = rdtsc();
+     {
+         uint64_t elapsed_msecs = ((stop - start) / tscperms);
+         double elapsed_secs = (double)elapsed_msecs/1000.0;
+         result->read = ((double)total_bytes_read / 1024.0) / elapsed_secs;
+       printf("%lf\n", result->read);
+     }
+ #endif
+     return ret;
+ }
+ static int
+ ahci_benchmark(int argc, char *argv[])
+ {
+     if (argc != 3) {
+         printf("usage: %s <source> <target>\nsource and target must be absolute paths!\n", argv[0]);
+         return 1;
+     }
+     char *source = argv[1];
+     char *target = argv[2];
+     printf("Running dd disk test\n");
+     size_t blocksize, count = 16;
+     dd_bench(source, target, 131072, 16);
+     for (blocksize = 131072; blocksize >= 512; blocksize /= 2, count *= 2) {
+         double r1, r2, r3;
+         r1 = dd_bench(source, target, blocksize, count);
+         r2 = dd_bench(source, target, blocksize, count);
+         r3 = dd_bench(source, target, blocksize, count);
+         double kbps = (r1 + r2 + r3) / 3;
+         printf("%zd\t%.3lf\n", blocksize, kbps);
+     }
+     printf("dd disk test completed\n");
+     return 0;
+ }
+ static int 
+ ahci_fillbench(int argc, char *argv[])
+ {
+     if (argc != 2) {
+         printf("usage: %s <target>\ntarget must be an absolute path!\n", argv[0]);
+         return 1;
+     }
+     char *target = argv[1];
+     printf("Running fill disk test\n");
+     size_t blocksize, count = 256;
+     struct bench_res dummy;
+     uint8_t *buffer = malloc(131072);
+     fill_bench(target, buffer, 131072, count, &dummy);
+     printf("bs\tread\tvar\twrite\tvar\n");
+     for (blocksize = 131072; blocksize >= 512; blocksize /= 2, count *= 2) {
+         struct bench_res r1, r2, r3;
+         fill_bench(target, buffer, blocksize, count, &r1);
+         fill_bench(target, buffer, blocksize, count, &r2);
+         fill_bench(target, buffer, blocksize, count, &r3);
+         double write_kbps = (r1.write + r2.write + r3.write) / 3;
+         double read_kbps = (r1.read + r2.read + r3.read) / 3;
+         double write_var = (write_kbps - r1.write) * (write_kbps - r1.write);
+         write_var += (write_kbps - r2.write) * (write_kbps - r2.write);
+         write_var += (write_kbps - r3.write) * (write_kbps - r3.write);
+         write_var /= 3;
+         double read_var = (read_kbps - r1.read) * (read_kbps - r1.read);
+         read_var += (read_kbps - r2.read) * (read_kbps - r2.read);
+         read_var += (read_kbps - r3.read) * (read_kbps - r3.read);
+         read_var /= 3;
+         printf("%zd\t%.3lf\t%.3lf\t%.3lf\t%.3lf\n", blocksize, read_kbps, read_var, write_kbps, write_var);
+     }
+     free(buffer);
+     printf("fill disk test completed\n");
+     return 0;
+ }
+ static int
+ shuffle_file(int argc, char *argv[])
+ {
+     if (argc != 6) {
+         printf("Usage: %s <file> <filesize> <blocksize> <count> <seed>\n", argv[0]);
+         return 0;
+     }
+     size_t filesize = atoi(argv[2]);
+     size_t blocksize = atoi(argv[3]);
+     size_t count = atoi(argv[4]);
+     size_t randval = atoi(argv[5]);
+     size_t randbit;
+     char * filename = argv[1];
+     size_t rsize = 0;
+     size_t wsize = 0;
+     int ret = 0;
+ #define RAND_NEXT do {\
+     randbit = ((randval >> 0) ^ (randval >> 3)) & 1;\
+     randval = (randval >> 1) | (randbit << (sizeof(size_t) * CHAR_BIT - 1));\
+ } while (0)
+ #define RAND_BLOCK ((randval % (filesize / blocksize)) * blocksize)
+     if (filesize % blocksize != 0) {
+         printf("Please spcifiy the filesize as a multiple of blocksize\n");
+         return 0;
+     }
+     uint8_t * buffer = malloc(blocksize);
+     
+     if (buffer == NULL) {
+         printf("failed to allocate buffer of size %zd\n", blocksize);
+         return 1;
+     }
+     errval_t err;
+     vfs_handle_t f = NULL;
+     char *path = vfs_path_mkabsolute(cwd, filename);
+     err = vfs_open(path, &f);
+     if (err_is_fail(err)) {
+         printf("%s: %s\n", path, err_getstring(err));
+         return 1;
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t tscperms;
+     err = sys_debug_get_tsc_per_ms(&tscperms);
+     assert(err_is_ok(err));
+     //printf("ticks per millisec: %" PRIu64 "\n", tscperms);
+     uint64_t start = rdtsc();
+ #endif
+     size_t count2 = count;
+     while (count2--) {
+         RAND_NEXT;
+         vfs_seek(f, VFS_SEEK_SET, RAND_BLOCK);
+         
+         err = vfs_read(f, buffer, blocksize, &rsize);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "error reading file");
+             ret = 1;
+             goto out;
+         }
+         assert(rsize == blocksize);
+         RAND_NEXT;
+         vfs_seek(f, VFS_SEEK_SET, RAND_BLOCK);
+         err = vfs_write(f, buffer, blocksize, &wsize);
+         if (err_is_fail(err) || wsize == 0) {
+             DEBUG_ERR(err, "error writing file");
+             ret = 1;
+             goto out;
+         }
+         assert(wsize == blocksize);
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t stop = rdtsc();
+     uint64_t elapsed_msecs = ((stop - start) / tscperms);
+     double elapsed_secs = (double)elapsed_msecs/1000.0;
+     printf("start: %" PRIu64 " stop: %" PRIu64 "\n", start, stop);
+     double kbps = ((double)(count * blocksize) / 1024.0) / elapsed_secs;
+     printf("%zu bytes read. %zu bytes written. %f s, %f kB/s\n", count * blocksize, count * blocksize, elapsed_secs, kbps); 
+ #endif
+ out:
+     if (buffer != NULL)
+         free(buffer);
+     
+     if (f != NULL) {
+         err = vfs_close(f);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+         }
+     }
+     return ret;
+ }
+ static int
+ rand_bench(char *target, uint8_t *buffer, size_t filesize, size_t blocksize, size_t count, size_t randval, struct bench_res *result)
+ {
+     size_t randbit;
+     size_t rsize = 0;
+     size_t wsize = 0;
+     int ret = 0;
+ #define RAND_NEXT do {\
+     randbit = ((randval >> 0) ^ (randval >> 3)) & 1;\
+     randval = (randval >> 1) | (randbit << (sizeof(size_t) * CHAR_BIT - 1));\
+ } while (0)
+ #define RAND_BLOCK ((randval % (filesize / blocksize)) * blocksize)
+     if (filesize % blocksize != 0) {
+         printf("Please spcifiy the filesize as a multiple of blocksize\n");
+         return 0;
+     }
+     errval_t err;
+     vfs_handle_t f = NULL;
+     char *path = vfs_path_mkabsolute(cwd, target);
+     err = vfs_open(path, &f);
+     if (err_is_fail(err)) {
+         printf("%s: %s\n", path, err_getstring(err));
+         return 1;
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t tscperms;
+     err = sys_debug_get_tsc_per_ms(&tscperms);
+     assert(err_is_ok(err));
+     //printf("ticks per millisec: %" PRIu64 "\n", tscperms);
+     uint64_t start = rdtsc();
+ #endif
+     size_t count2 = count;
+     while (count2--) {
+         RAND_NEXT;
+         vfs_seek(f, VFS_SEEK_SET, RAND_BLOCK);
+         err = vfs_read(f, buffer, blocksize, &rsize);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "error reading file");
+             ret = 1;
+             goto out;
+         }
+         assert(rsize == blocksize);
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t stop = rdtsc();
+     uint64_t elapsed_msecs = ((stop - start) / tscperms);
+     double elapsed_secs = (double)elapsed_msecs/1000.0;
+     result->read = ((double)(count * blocksize) / 1024.0) / elapsed_secs;
+     start = rdtsc();
+ #endif
+     count2 = count;
+     while(count2--) {
+         RAND_NEXT;
+         vfs_seek(f, VFS_SEEK_SET, RAND_BLOCK);
+         err = vfs_write(f, buffer, blocksize, &wsize);
+         if (err_is_fail(err) || wsize == 0) {
+             DEBUG_ERR(err, "error writing file");
+             ret = 1;
+             goto out;
+         }
+         assert(wsize == blocksize);
+         vfs_flush(f);
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     stop = rdtsc();
+     elapsed_msecs = ((stop - start) / tscperms);
+     elapsed_secs = (double)elapsed_msecs/1000.0;
+     result->write = ((double)(count * blocksize) / 1024.0) / elapsed_secs;
+ #endif
+ out:
+     if (f != NULL) {
+         err = vfs_close(f);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+         }
+     }
+     return ret;
+ }
+ #define FILESIZE (1024 * 1024 * 1024)
+ static int
+ ahci_randbench(int argc, char *argv[])
+ {
+     if (argc != 2) {
+         printf("usage: %s <target>\ntarget must be an absolute path!\n", argv[0]);
+         return 1;
+     }
+     char *target = argv[1];
+     printf("Running random disk test\n");
+     size_t blocksize, count = 256;
+     uint8_t *buffer = malloc(131072);
+     printf("bs\tread\tvar\twrite\tvar\n");
+     for (blocksize = 131072; blocksize >= 512; blocksize /= 2) {
+         struct bench_res r1, r2, r3;
+         rand_bench(target, buffer, FILESIZE, blocksize, count, 9147, &r1);
+         rand_bench(target, buffer, FILESIZE, blocksize, count, 26447, &r2);
+         rand_bench(target, buffer, FILESIZE, blocksize, count, 5109, &r3);
+         double read_kbps = (r1.read + r2.read + r3.read) / 3;
+         double read_var = (read_kbps - r1.read) * (read_kbps - r1.read);
+         read_var += (read_kbps - r2.read) * (read_kbps - r2.read);
+         read_var += (read_kbps - r3.read) * (read_kbps - r3.read);
+         read_var /= 3;
+         double write_kbps = (r1.write + r2.write + r3.write) / 3;
+         double write_var = (write_kbps - r1.write) * (write_kbps - r1.write);
+         write_var += (write_kbps - r2.write) * (write_kbps - r2.write);
+         write_var += (write_kbps - r3.write) * (write_kbps - r3.write);
+         write_var /= 3;
+         printf("%zd\t%.3lf\t%.3lf\t%.3lf\t%.3lf\n", blocksize, read_kbps, read_var, write_kbps, write_var);
+     }
+     free(buffer);
+     printf("random disk test completed\n");
+     return 0;
+ }
+ static int
+ rand_bench_time(char *target, uint8_t *buffer, size_t filesize, size_t blocksize, size_t count, size_t randval, struct bench_res *result)
+ {
+     size_t randbit;
+     size_t rsize = 0;
+     size_t wsize = 0;
+     int ret = 0;
+ #define RAND_NEXT do {\
+     randbit = ((randval >> 0) ^ (randval >> 3)) & 1;\
+     randval = (randval >> 1) | (randbit << (sizeof(size_t) * CHAR_BIT - 1));\
+ } while (0)
+ #define RAND_BLOCK ((randval % (filesize / blocksize)) * blocksize)
+     if (filesize % blocksize != 0) {
+         printf("Please spcifiy the filesize as a multiple of blocksize\n");
+         return 0;
+     }
+     errval_t err;
+     vfs_handle_t f = NULL;
+     char *path = vfs_path_mkabsolute(cwd, target);
+     err = vfs_open(path, &f);
+     if (err_is_fail(err)) {
+         printf("%s: %s\n", path, err_getstring(err));
+         return 1;
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t tscperms;
+     err = sys_debug_get_tsc_per_ms(&tscperms);
+     assert(err_is_ok(err));
+     //printf("ticks per millisec: %" PRIu64 "\n", tscperms);
+     uint64_t start = rdtsc();
+ #endif
+     size_t count2 = count;
+     while (count2--) {
+         RAND_NEXT;
+         vfs_seek(f, VFS_SEEK_SET, RAND_BLOCK);
+         err = vfs_read(f, buffer, blocksize, &rsize);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "error reading file");
+             ret = 1;
+             goto out;
+         }
+         assert(rsize == blocksize);
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     uint64_t stop = rdtsc();
+     uint64_t elapsed_msecs = ((stop - start) / tscperms);
+     double elapsed_secs = (double)elapsed_msecs/1000.0;
+     result->read = elapsed_secs;
+     start = rdtsc();
+ #endif
+     count2 = count;
+     while(count2--) {
+         RAND_NEXT;
+         vfs_seek(f, VFS_SEEK_SET, RAND_BLOCK);
+         err = vfs_write(f, buffer, blocksize, &wsize);
+         if (err_is_fail(err) || wsize == 0) {
+             DEBUG_ERR(err, "error writing file");
+             ret = 1;
+             goto out;
+         }
+         assert(wsize == blocksize);
+         vfs_flush(f);
+     }
+ #if defined(__x86_64__) || defined(__i386__)
+     stop = rdtsc();
+     elapsed_msecs = ((stop - start) / tscperms);
+     elapsed_secs = (double)elapsed_msecs/1000.0;
+     result->write = elapsed_secs;
+ #endif
+ out:
+     if (f != NULL) {
+         err = vfs_close(f);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+         }
+     }
+     return ret;
+ }
+ #define FILESIZE (1024 * 1024 * 1024)
+ static int
+ ahci_randbench_time(int argc, char *argv[])
+ {
+     if (argc != 2) {
+         printf("usage: %s <target>\ntarget must be an absolute path!\n", argv[0]);
+         return 1;
+     }
+     char *target = argv[1];
+     printf("Running random disk test\n");
+     size_t blocksize, count = 256;
+     uint8_t *buffer = malloc(131072);
+     printf("bs\tread\tvar\n");
+     for (blocksize = 131072; blocksize >= 512; blocksize /= 2) {
+         struct bench_res r1, r2, r3;
+         rand_bench_time(target, buffer, FILESIZE, blocksize, count, 9147, &r1);
+         rand_bench_time(target, buffer, FILESIZE, blocksize, count, 26447, &r2);
+         rand_bench_time(target, buffer, FILESIZE, blocksize, count, 5109, &r3);
+         r1.read /= count;
+         r2.read /= count;
+         r3.read /= count;
+         r1.write /= count;
+         r2.write /= count;
+         r3.write /= count;
+         double read_sec = (r1.read + r2.read + r3.read) / 3;
+         double read_var = (read_sec - r1.read) * (read_sec - r1.read);
+         read_var += (read_sec - r2.read) * (read_sec - r2.read);
+         read_var += (read_sec - r3.read) * (read_sec - r3.read);
+         read_var /= 3;
+         double write_sec = (r1.write + r2.write + r3.write) / 3;
+         double write_var = (write_sec - r1.write) * (write_sec - r1.write);
+         write_var += (write_sec - r2.write) * (write_sec - r2.write);
+         write_var += (write_sec - r3.write) * (write_sec - r3.write);
+         write_var /= 3;
+         printf("%zd\t%.3lf\t%.3lf\t%.3lf\t%.3lf\n", blocksize, read_sec, read_var, write_sec, write_var);
+     }
+     free(buffer);
+     printf("random disk test completed\n");
+     return 0;
+ }
+ static int
+ ahci_read_write(int argc, char *argv[])
+ {
+     if (argc != 4) {
+         printf("usage: %s <block device> <blocksize> <count>\n", argv[0]);
+         return 1;
+     }
+     char *dev = argv[1];
+     size_t blocksize = atoi(argv[2]);
+     size_t count = atoi(argv[3]);
+     printf("malloc buf\n");
+     uint8_t *buf = malloc(blocksize);
+     int ret = 0;
+     errval_t err;
+     vfs_handle_t f = NULL;
+     char *path = vfs_path_mkabsolute(cwd, dev);
+     printf("open\n");
+     err = vfs_open(path, &f);
+     if (err_is_fail(err)) {
+         printf("%s: %s\n", path, err_getstring(err));
+         ret = 1;
+         goto out;
+     }
+     for (int i = 0; i < count; i++) {
+         size_t read = 0;
+         size_t rsize;
+         while(read < blocksize) {
+             printf("read\n");
+             err = vfs_read(f, &buf[read], blocksize, &rsize);
+             if (err_is_fail(err)) {
+                 DEBUG_ERR(err, "error reading file");
+                 ret = 1;
+                 goto out;
+             }
+             read += rsize;
+         }
+         size_t written = 0;
+         size_t wsize;
+         while(written < blocksize) {
+             printf("write\n");
+             err = vfs_write(f, &buf[written], blocksize, &wsize);
+             if (err_is_fail(err)) {
+                 DEBUG_ERR(err, "error writing file");
+                 ret = 1;
+                 goto out;
+             }
+             written += wsize;
+         }
+     }
+ out:
+     if (buf)
+         printf("free\n");
+         free(buf);
+     if (f) {
+         printf("close\n");
+         err = vfs_close(f);
+         if (err_is_fail(err)) {
+             DEBUG_ERR(err, "in vfs_close");
+         }
+     }
+     return ret;
+ }
+ typedef int (*Command)(int argc, char *argv[]);
+ struct cmd {
+     const char  *name;
+     Command     cmd;
+     const char  *usage;
+ };
+ static struct cmd commands[] = {
+     { "benchmark", ahci_benchmark, "benchmark ahci" },
+     { "fillbench", ahci_fillbench, "benchmark ahci sequential" },
+     { "randbench", ahci_randbench, "benchmark ahci random (throughput)" },
+     { "randbench_time", ahci_randbench_time, "benchmark ahci random (response time)" },
+     { "read_write", ahci_read_write, "read from block device and write back" },
+     { "shuffle_file", shuffle_file, "Shuffle a file around" },
+ };
+ #define NUM_COMMANDS (sizeof(commands)/sizeof(commands[0]))
+ static struct cmd *find_command(const char *name)
+ {
+     for(int i = 0; i < ENTRIES(commands); i++) {
+         struct cmd *cmd = &commands[i];
+         if(strcmp(name, cmd->name) == 0) {
+             return cmd;
+         }
+     }
+     return NULL;
+ }
+ static void usage(char *progname)
+ {
+     printf("%s <mountpoint> <mount uri> <command> <command_args...>\n", progname);
+     for (int i = 0; i < NUM_COMMANDS; i++) {
+         printf("\t%s: %s\n", commands[i].name, commands[i].usage);
+     }
+     printf("To get detailed usage informations for the different benchmarks\n"
+            "run the command without additional arguments.\n\n");
+ }
+ int main(int argc, char *argv[])
+ {
+     // mount
+     if (argc < 4) {
+         usage(argv[0]);
+         return 1;
+     }
+     // argv[1] mountpoint, argv[2] mount uri
+     if(mount(argv[1], argv[2]))
+         return 1;
+     // find command
+     struct cmd *cmd;
+     if ((cmd = find_command(argv[3])) == NULL)
+         return 1;
+     return cmd->cmd(argc-3, argv+3);
+ }
diff --cc usr/fish/fish.c
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -25,10 -25,11 +25,10 @@@ let ramfs_files = find inDir "eclipse_k
                          addCFlags = [ "-O2" ],
                          flounderBindings = [ "skb" ],
                          addIncludes = [ "eclipse_kernel/src"],
 -                        addLibraries = [ "eclipse", "shm", "dummies",
 -                                         "posixcompat", "msun",
 -                                         "icsolver", "vfs", "nfs", "ahci", "lwip",
 -                                         "contmng", "procon", "hashtable"],
 -                        architectures = [ arch ]
 +                        addLibraries = libDeps [ "eclipse", "shm", "dummies",
 +                                                 "icsolver", "vfs",
 +                                                 "posixcompat" ],
-                         architectures = [ arch ]
++                       architectures = [ arch ]
                  }
  in
    [ Rules [build (args arch) | arch <- [ "x86_64", "x86_32" ]],
@@@ -193,10 -184,19 +184,19 @@@ static void state_machine(void
  
  /* -------------------------- MAIN ------------------------------- */
  
- static void set_skb_present(char *str)
+ static void mappings_from_skb(uintptr_t my_arch_id, char *str)
  {
+     // insert mapping for self
+     coreid_mappings[my_core_id].arch_id = my_arch_id;
+     coreid_mappings[my_core_id].present = true;
+     coreid_t n = 0;
+     if (n == my_core_id) {
+         n++;
+     }
      while (*str != '\0') {
 -        if (!isdigit(*str)) {
 +        if (!isdigit((int)*str)) {
              str++;
              continue;
          }
@@@ -12,7 -12,8 +12,7 @@@
  
  [ build application { target = "startd",
                      cFiles = [ "main.c", "spawn.c" ],
 -                    addLibraries = [ "spawndomain", "elf", "trace",
 -                                     "dist", "vfs", "nfs", "ahci", "lwip",
 -                                       "contmng", "procon", "hashtable" ]
 -                    }
 +                    addLibraries = libDeps [ "spawndomain", "elf", "trace",
 +                                       "dist", "vfs"]
-                     }
++                   }
  ]
Simple merge