[edk2] [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code

Gabriel L. Somlo posted 6 patches 7 years, 8 months ago
[edk2] [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code
Posted by Gabriel L. Somlo 7 years, 8 months ago
All files are licensed under BSD. All files are copyright
Christoph Pfisterer (from refit 0.14), with the exception
of "fsw_efi_edk2_base.h", which is copyright Stefan Agner
(from the refind project).

This patch imports the completely unmodified original files.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
 OvmfPkg/FswHfsPlus/fsw_base.h          |  158 +++++
 OvmfPkg/FswHfsPlus/fsw_core.c          |  929 ++++++++++++++++++++++++++++
 OvmfPkg/FswHfsPlus/fsw_core.h          |  517 ++++++++++++++++
 OvmfPkg/FswHfsPlus/fsw_efi.c           | 1044 ++++++++++++++++++++++++++++++++
 OvmfPkg/FswHfsPlus/fsw_efi.h           |  102 ++++
 OvmfPkg/FswHfsPlus/fsw_efi_base.h      |   82 +++
 OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h |   76 +++
 OvmfPkg/FswHfsPlus/fsw_efi_lib.c       |  129 ++++
 OvmfPkg/FswHfsPlus/fsw_lib.c           |  294 +++++++++
 OvmfPkg/FswHfsPlus/fsw_strfunc.h       |  453 ++++++++++++++
 10 files changed, 3784 insertions(+)
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_base.h
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_core.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_core.h
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_efi.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_efi.h
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_efi_base.h
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_efi_lib.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_lib.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_strfunc.h

diff --git a/OvmfPkg/FswHfsPlus/fsw_base.h b/OvmfPkg/FswHfsPlus/fsw_base.h
new file mode 100644
index 0000000..ab18385
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_base.h
@@ -0,0 +1,158 @@
+/**
+ * \file fsw_base.h
+ * Base definitions switch.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef _FSW_BASE_H_
+#define _FSW_BASE_H_
+
+
+#ifndef FSW_DEBUG_LEVEL
+/**
+ * Global debugging level. Can be set locally for the scope of a single
+ * file by defining the macro before fsw_base.h is included.
+ */
+#define FSW_DEBUG_LEVEL 1
+#endif
+
+
+#ifdef HOST_EFI
+#include "fsw_efi_base.h"
+#endif
+
+#ifdef HOST_POSIX
+#include "fsw_posix_base.h"
+#endif
+
+// message printing
+
+#if FSW_DEBUG_LEVEL >= 1
+#define FSW_MSG_ASSERT(params) FSW_MSGFUNC params
+#else
+#define FSW_MSG_ASSERT(params)
+#endif
+
+#if FSW_DEBUG_LEVEL >= 2
+#define FSW_MSG_DEBUG(params) FSW_MSGFUNC params
+#else
+#define FSW_MSG_DEBUG(params)
+#endif
+
+#if FSW_DEBUG_LEVEL >= 3
+#define FSW_MSG_DEBUGV(params) FSW_MSGFUNC params
+#else
+#define FSW_MSG_DEBUGV(params)
+#endif
+
+
+// Documentation for system-dependent defines
+
+/**
+ * \typedef fsw_s8
+ * Signed 8-bit integer.
+ */
+
+/**
+ * \typedef fsw_u8
+ * Unsigned 8-bit integer.
+ */
+
+/**
+ * \typedef fsw_s16
+ * Signed 16-bit integer.
+ */
+
+/**
+ * \typedef fsw_u16
+ * Unsigned 16-bit integer.
+ */
+
+/**
+ * \typedef fsw_s32
+ * Signed 32-bit integer.
+ */
+
+/**
+ * \typedef fsw_u32
+ * Unsigned 32-bit integer.
+ */
+
+/**
+ * \typedef fsw_s64
+ * Signed 64-bit integer.
+ */
+
+/**
+ * \typedef fsw_u64
+ * Unsigned 64-bit integer.
+ */
+
+
+/**
+ * \def fsw_alloc(size,ptrptr)
+ * Allocate memory on the heap. This function or macro allocates \a size
+ * bytes of memory using host-specific methods. The address of the
+ * allocated memory block is stored into the pointer variable pointed
+ * to by \a ptrptr. A status code is returned; FSW_SUCCESS if the block
+ * was allocated or FSW_OUT_OF_MEMORY if there is not enough memory
+ * to allocated the requested block.
+ */
+
+/**
+ * \def fsw_free(ptr)
+ * Release allocated memory. This function or macro returns an allocated
+ * memory block to the heap for reuse. Does not return a status.
+ */
+
+/**
+ * \def fsw_memcpy(dest,src,size)
+ * Copies a block of memory from \a src to \a dest. The two memory blocks
+ * must not overlap, or the result of the operation will be undefined.
+ * Does not return a status.
+ */
+
+/**
+ * \def fsw_memeq(dest,src,size)
+ * Compares two blocks of memory for equality. Returns boolean true if the
+ * memory blocks are equal, boolean false if they are different.
+ */
+
+/**
+ * \def fsw_memzero(dest,size)
+ * Initializes a block of memory with zeros. Does not return a status.
+ */
+
+
+#endif
diff --git a/OvmfPkg/FswHfsPlus/fsw_core.c b/OvmfPkg/FswHfsPlus/fsw_core.c
new file mode 100644
index 0000000..db7016b
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_core.c
@@ -0,0 +1,929 @@
+/**
+ * \file fsw_core.c
+ * Core file system wrapper abstraction layer code.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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 "fsw_core.h"
+
+
+// functions
+
+static void fsw_blockcache_free(struct fsw_volume *vol);
+
+#define MAX_CACHE_LEVEL (5)
+
+
+/**
+ * Mount a volume with a given file system driver. This function is called by the
+ * host driver to make a volume accessible. The file system driver to use is specified
+ * by a pointer to its dispatch table. The file system driver will look at the
+ * data on the volume to determine if it can read the format. If the volume is found
+ * unsuitable, FSW_UNSUPPORTED is returned.
+ *
+ * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data
+ * structure. The caller must release it later by calling fsw_unmount.
+ *
+ * If this function returns an error status, the caller only needs to clean up its
+ * own buffers that may have been allocated through the read_block interface.
+ */
+
+fsw_status_t fsw_mount(void *host_data,
+                       struct fsw_host_table *host_table,
+                       struct fsw_fstype_table *fstype_table,
+                       struct fsw_volume **vol_out)
+{
+    fsw_status_t    status;
+    struct fsw_volume *vol;
+    
+    // allocate memory for the structure
+    status = fsw_alloc_zero(fstype_table->volume_struct_size, (void **)&vol);
+    if (status)
+        return status;
+    
+    // initialize fields
+    vol->phys_blocksize = 512;
+    vol->log_blocksize  = 512;
+    vol->label.type     = FSW_STRING_TYPE_EMPTY;
+    vol->host_data      = host_data;
+    vol->host_table     = host_table;
+    vol->fstype_table   = fstype_table;
+    vol->host_string_type = host_table->native_string_type;
+    
+    // let the fs driver mount the file system
+    status = vol->fstype_table->volume_mount(vol);
+    if (status)
+        goto errorexit;
+    
+    // TODO: anything else?
+    
+    *vol_out = vol;
+    return FSW_SUCCESS;
+    
+errorexit:
+    fsw_unmount(vol);
+    return status;
+}
+
+/**
+ * Unmount a volume by releasing all memory associated with it. This function is
+ * called by the host driver when a volume is no longer needed. It is also called
+ * by the core after a failed mount to clean up any allocated memory.
+ *
+ * Note that all dnodes must have been released before calling this function.
+ */
+
+void fsw_unmount(struct fsw_volume *vol)
+{
+    if (vol->root)
+        fsw_dnode_release(vol->root);
+    // TODO: check that no other dnodes are still around
+    
+    vol->fstype_table->volume_free(vol);
+    
+    fsw_blockcache_free(vol);
+    fsw_strfree(&vol->label);
+    fsw_free(vol);
+}
+
+/**
+ * Get in-depth information on the volume. This function can be called by the host
+ * driver to get additional information on the volume.
+ */
+
+fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb)
+{
+    return vol->fstype_table->volume_stat(vol, sb);
+}
+
+/**
+ * Set the physical and logical block sizes of the volume. This functions is called by
+ * the file system driver to announce the block sizes it wants to use for accessing
+ * the disk (physical) and for addressing file contents (logical).
+ * Usually both sizes will be the same but there may be file systems that need to access
+ * metadata at a smaller block size than the allocation unit for files.
+ *
+ * Calling this function causes the block cache to be dropped. All pointers returned
+ * from fsw_block_get become invalid. This function should only be called while
+ * mounting the file system, not as a part of file access operations.
+ *
+ * Both sizes are measured in bytes, must be powers of 2, and must not be smaller
+ * than 512 bytes. The logical block size cannot be smaller than the physical block size.
+ */
+
+void fsw_set_blocksize(struct fsw_volume *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize)
+{
+    // TODO: Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than
+    //  phys_blocksize.
+    
+    // drop core block cache if present
+    fsw_blockcache_free(vol);
+    
+    // signal host driver to drop caches etc.
+    vol->host_table->change_blocksize(vol,
+                                      vol->phys_blocksize, vol->log_blocksize,
+                                      phys_blocksize, log_blocksize);
+    
+    vol->phys_blocksize = phys_blocksize;
+    vol->log_blocksize = log_blocksize;
+}
+
+/**
+ * Get a block of data from the disk. This function is called by the file system driver
+ * or by core functions. It calls through to the host driver's device access routine.
+ * Given a physical block number, it reads the block into memory (or fetches it from the
+ * block cache) and returns the address of the memory buffer. The caller should provide
+ * an indication of how important the block is in the cache_level parameter. Blocks with
+ * a low level are purged first. Some suggestions for cache levels:
+ *
+ *  - 0: File data
+ *  - 1: Directory data, symlink data
+ *  - 2: File system metadata
+ *  - 3..5: File system metadata with a high rate of access
+ *
+ * If this function returns successfully, the returned data pointer is valid until the
+ * caller calls fsw_block_release.
+ */
+
+fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out)
+{
+    fsw_status_t    status;
+    fsw_u32         i, discard_level, new_bcache_size;
+    struct fsw_blockcache *new_bcache;
+    
+    // TODO: allow the host driver to do its own caching; just call through if
+    //  the appropriate function pointers are set
+    
+    if (cache_level > MAX_CACHE_LEVEL)
+        cache_level = MAX_CACHE_LEVEL;
+    
+    // check block cache
+    for (i = 0; i < vol->bcache_size; i++) {
+        if (vol->bcache[i].phys_bno == phys_bno) {
+            // cache hit!
+            if (vol->bcache[i].cache_level < cache_level)
+                vol->bcache[i].cache_level = cache_level;  // promote the entry
+            vol->bcache[i].refcount++;
+            *buffer_out = vol->bcache[i].data;
+            return FSW_SUCCESS;
+        }
+    }
+    
+    // find a free entry in the cache table
+    for (i = 0; i < vol->bcache_size; i++) {
+        if (vol->bcache[i].phys_bno == FSW_INVALID_BNO)
+            break;
+    }
+    if (i >= vol->bcache_size) {
+        for (discard_level = 0; discard_level <= MAX_CACHE_LEVEL; discard_level++) {
+            for (i = 0; i < vol->bcache_size; i++) {
+                if (vol->bcache[i].refcount == 0 && vol->bcache[i].cache_level <= discard_level)
+                    break;
+            }
+            if (i < vol->bcache_size)
+                break;
+        }
+    }
+    if (i >= vol->bcache_size) {
+        // enlarge / create the cache
+        if (vol->bcache_size < 16)
+            new_bcache_size = 16;
+        else
+            new_bcache_size = vol->bcache_size << 1;
+        status = fsw_alloc(new_bcache_size * sizeof(struct fsw_blockcache), &new_bcache);
+        if (status)
+            return status;
+        if (vol->bcache_size > 0)
+            fsw_memcpy(new_bcache, vol->bcache, vol->bcache_size * sizeof(struct fsw_blockcache));
+        for (i = vol->bcache_size; i < new_bcache_size; i++) {
+            new_bcache[i].refcount = 0;
+            new_bcache[i].cache_level = 0;
+            new_bcache[i].phys_bno = FSW_INVALID_BNO;
+            new_bcache[i].data = NULL;
+        }
+        i = vol->bcache_size;
+        
+        // switch caches
+        if (vol->bcache != NULL)
+            fsw_free(vol->bcache);
+        vol->bcache = new_bcache;
+        vol->bcache_size = new_bcache_size;
+    }
+    vol->bcache[i].phys_bno = FSW_INVALID_BNO;
+    
+    // read the data
+    if (vol->bcache[i].data == NULL) {
+        status = fsw_alloc(vol->phys_blocksize, &vol->bcache[i].data);
+        if (status)
+            return status;
+    }
+    status = vol->host_table->read_block(vol, phys_bno, vol->bcache[i].data);
+    if (status)
+        return status;
+    
+    vol->bcache[i].phys_bno = phys_bno;
+    vol->bcache[i].cache_level = cache_level;
+    vol->bcache[i].refcount = 1;
+    *buffer_out = vol->bcache[i].data;
+    return FSW_SUCCESS;
+}
+
+/**
+ * Releases a disk block. This function must be called to release disk blocks returned
+ * from fsw_block_get.
+ */
+
+void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer)
+{
+    fsw_u32 i;
+    
+    // TODO: allow the host driver to do its own caching; just call through if
+    //  the appropriate function pointers are set
+    
+    // update block cache
+    for (i = 0; i < vol->bcache_size; i++) {
+        if (vol->bcache[i].phys_bno == phys_bno && vol->bcache[i].refcount > 0)
+            vol->bcache[i].refcount--;
+    }
+}
+
+/**
+ * Release the block cache. Called internally when changing block sizes and when
+ * unmounting the volume. It frees all data occupied by the generic block cache.
+ */
+
+static void fsw_blockcache_free(struct fsw_volume *vol)
+{
+    fsw_u32 i;
+    
+    for (i = 0; i < vol->bcache_size; i++) {
+        if (vol->bcache[i].data != NULL)
+            fsw_free(vol->bcache[i].data);
+    }
+    if (vol->bcache != NULL) {
+        fsw_free(vol->bcache);
+        vol->bcache = NULL;
+    }
+    vol->bcache_size = 0;
+}
+
+/**
+ * Add a new dnode to the list of known dnodes. This internal function is used when a
+ * dnode is created to add it to the dnode list that is used to search for existing
+ * dnodes by id.
+ */
+
+static void fsw_dnode_register(struct fsw_volume *vol, struct fsw_dnode *dno)
+{
+    dno->next = vol->dnode_head;
+    if (vol->dnode_head != NULL)
+        vol->dnode_head->prev = dno;
+    dno->prev = NULL;
+    vol->dnode_head = dno;
+}
+
+/**
+ * Create a dnode representing the root directory. This function is called by the file system
+ * driver while mounting the file system. The root directory is special because it has no parent
+ * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions
+ * behaves in the same way as fsw_dnode_create.
+ */
+
+fsw_status_t fsw_dnode_create_root(struct fsw_volume *vol, fsw_u32 dnode_id, struct fsw_dnode **dno_out)
+{
+    fsw_status_t    status;
+    struct fsw_dnode *dno;
+    
+    // allocate memory for the structure
+    status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno);
+    if (status)
+        return status;
+    
+    // fill the structure
+    dno->vol = vol;
+    dno->parent = NULL;
+    dno->dnode_id = dnode_id;
+    dno->type = FSW_DNODE_TYPE_DIR;
+    dno->refcount = 1;
+    dno->name.type = FSW_STRING_TYPE_EMPTY;
+    // TODO: instead, call a function to create an empty string in the native string type
+    
+    fsw_dnode_register(vol, dno);
+    
+    *dno_out = dno;
+    return FSW_SUCCESS;
+}
+
+/**
+ * Create a new dnode representing a file system object. This function is called by
+ * the file system driver in response to directory lookup or read requests. Note that
+ * if there already is a dnode with the given dnode_id on record, then no new object
+ * is created. Instead, the existing dnode is returned and its reference count
+ * increased. All other parameters are ignored in this case.
+ *
+ * The type passed into this function may be FSW_DNODE_TYPE_UNKNOWN. It is sufficient
+ * to fill the type field during the dnode_fill call.
+ *
+ * The name parameter must describe a string with the object's name. A copy will be
+ * stored in the dnode structure for future reference. The name will not be used to
+ * shortcut directory lookups, but may be used to reconstruct paths.
+ *
+ * If the function returns successfully, *dno_out contains a pointer to the dnode
+ * that must be released by the caller with fsw_dnode_release.
+ */
+
+fsw_status_t fsw_dnode_create(struct fsw_dnode *parent_dno, fsw_u32 dnode_id, int type,
+                              struct fsw_string *name, struct fsw_dnode **dno_out)
+{
+    fsw_status_t    status;
+    struct fsw_volume *vol = parent_dno->vol;
+    struct fsw_dnode *dno;
+    
+    // check if we already have a dnode with the same id
+    for (dno = vol->dnode_head; dno; dno = dno->next) {
+        if (dno->dnode_id == dnode_id) {
+            fsw_dnode_retain(dno);
+            *dno_out = dno;
+            return FSW_SUCCESS;
+        }
+    }
+    
+    // allocate memory for the structure
+    status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno);
+    if (status)
+        return status;
+    
+    // fill the structure
+    dno->vol = vol;
+    dno->parent = parent_dno;
+    fsw_dnode_retain(dno->parent);
+    dno->dnode_id = dnode_id;
+    dno->type = type;
+    dno->refcount = 1;
+    status = fsw_strdup_coerce(&dno->name, vol->host_table->native_string_type, name);
+    if (status) {
+        fsw_free(dno);
+        return status;
+    }
+    
+    fsw_dnode_register(vol, dno);
+    
+    *dno_out = dno;
+    return FSW_SUCCESS;
+}
+
+/**
+ * Increases the reference count of a dnode. This must be balanced with
+ * fsw_dnode_release calls. Note that some dnode functions return a retained
+ * dnode pointer to their caller.
+ */
+
+void fsw_dnode_retain(struct fsw_dnode *dno)
+{
+    dno->refcount++;
+}
+
+/**
+ * Release a dnode pointer, deallocating it if this was the last reference.
+ * This function decrements the reference counter of the dnode. If the counter
+ * reaches zero, the dnode is freed. Since the parent dnode is released
+ * during that process, this function may cause it to be freed, too.
+ */
+
+void fsw_dnode_release(struct fsw_dnode *dno)
+{
+    struct fsw_volume *vol = dno->vol;
+    struct fsw_dnode *parent_dno;
+    
+    dno->refcount--;
+    
+    if (dno->refcount == 0) {
+        parent_dno = dno->parent;
+        
+        // de-register from volume's list
+        if (dno->next)
+            dno->next->prev = dno->prev;
+        if (dno->prev)
+            dno->prev->next = dno->next;
+        if (vol->dnode_head == dno)
+            vol->dnode_head = dno->next;
+        
+        // run fstype-specific cleanup
+        vol->fstype_table->dnode_free(vol, dno);
+        
+        fsw_strfree(&dno->name);
+        fsw_free(dno);
+        
+        // release our pointer to the parent, possibly deallocating it, too
+        if (parent_dno)
+            fsw_dnode_release(parent_dno);
+    }
+}
+
+/**
+ * Get full information about a dnode from disk. This function is called by the host
+ * driver as well as by the core functions. Some file systems defer reading full
+ * information on a dnode until it is actually needed (i.e. separation between
+ * directory and inode information). This function makes sure that all information
+ * is available in the dnode structure. The following fields may not have a correct
+ * value until fsw_dnode_fill has been called:
+ *
+ * type, size
+ */
+
+fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno)
+{
+    // TODO: check a flag right here, call fstype's dnode_fill only once per dnode
+    
+    return dno->vol->fstype_table->dnode_fill(dno->vol, dno);
+}
+
+/**
+ * Get extended information about a dnode. This function can be called by the host
+ * driver to get a full compliment of information about a dnode in addition to the
+ * fields of the fsw_dnode structure itself.
+ *
+ * Some data requires host-specific conversion to be useful (i.e. timestamps) and
+ * will be passed to callback functions instead of being written into the structure.
+ * These callbacks must be filled in by the caller.
+ */
+
+fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb)
+{
+    fsw_status_t    status;
+    
+    status = fsw_dnode_fill(dno);
+    if (status)
+        return status;
+    
+    sb->used_bytes = 0;
+    status = dno->vol->fstype_table->dnode_stat(dno->vol, dno, sb);
+    if (!status && !sb->used_bytes)
+        sb->used_bytes = DivU64x32(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize, NULL);
+    return status;
+}
+
+/**
+ * Lookup a directory entry by name. This function is called by the host driver.
+ * Given a directory dnode and a file name, it looks up the named entry in the
+ * directory.
+ *
+ * If the dnode is not a directory, the call will fail. The caller is responsible for
+ * resolving symbolic links before calling this function.
+ *
+ * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
+ * entry. The caller must call fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno,
+                              struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out)
+{
+    fsw_status_t    status;
+    
+    status = fsw_dnode_fill(dno);
+    if (status)
+        return status;
+    if (dno->type != FSW_DNODE_TYPE_DIR)
+        return FSW_UNSUPPORTED;
+    
+    return dno->vol->fstype_table->dir_lookup(dno->vol, dno, lookup_name, child_dno_out);
+}
+
+/**
+ * Find a file system object by path. This function is called by the host driver.
+ * Given a directory dnode and a relative or absolute path, it walks the directory
+ * tree until it finds the target dnode. If an intermediate node turns out to be
+ * a symlink, it is resolved automatically. If the target node is a symlink, it
+ * is not resolved.
+ *
+ * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
+ * entry. The caller must call fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno,
+                                   struct fsw_string *lookup_path, char separator,
+                                   struct fsw_dnode **child_dno_out)
+{
+    fsw_status_t    status;
+    struct fsw_volume *vol = dno->vol;
+    struct fsw_dnode *child_dno = NULL;
+    struct fsw_string lookup_name;
+    struct fsw_string remaining_path;
+    int             root_if_empty;
+    
+    remaining_path = *lookup_path;
+    fsw_dnode_retain(dno);
+    
+    // loop over the path
+    for (root_if_empty = 1; fsw_strlen(&remaining_path) > 0; root_if_empty = 0) {
+        // parse next path component
+        fsw_strsplit(&lookup_name, &remaining_path, separator);
+        
+        FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"),
+                       lookup_name.len, lookup_name.data,
+                       remaining_path.len, remaining_path.data));
+        
+        if (fsw_strlen(&lookup_name) == 0) {        // empty path component
+            if (root_if_empty)
+                child_dno = vol->root;
+            else
+                child_dno = dno;
+            fsw_dnode_retain(child_dno);
+            
+        } else {
+            // do an actual directory lookup
+            
+            // ensure we have full information
+            status = fsw_dnode_fill(dno);
+            if (status)
+                goto errorexit;
+            
+            // resolve symlink if necessary
+            if (dno->type == FSW_DNODE_TYPE_SYMLINK) {
+                status = fsw_dnode_resolve(dno, &child_dno);
+                if (status)
+                    goto errorexit;
+                
+                // symlink target becomes the new dno
+                fsw_dnode_release(dno);
+                dno = child_dno;   // is already retained
+                child_dno = NULL;
+                
+                // ensure we have full information
+                status = fsw_dnode_fill(dno);
+                if (status)
+                    goto errorexit;
+            }
+            
+            // make sure we operate on a directory
+            if (dno->type != FSW_DNODE_TYPE_DIR) {
+                return FSW_UNSUPPORTED;
+                goto errorexit;
+            }
+            
+            // check special paths
+            if (fsw_streq_cstr(&lookup_name, ".")) {    // self directory
+                child_dno = dno;
+                fsw_dnode_retain(child_dno);
+                
+            } else if (fsw_streq_cstr(&lookup_name, "..")) {   // parent directory
+                if (dno->parent == NULL) {
+                    // We cannot go up from the root directory. Caution: Certain apps like the EFI shell
+                    // rely on this behaviour!
+                    status = FSW_NOT_FOUND;
+                    goto errorexit;
+                }
+                child_dno = dno->parent;
+                fsw_dnode_retain(child_dno);
+                
+            } else {
+                // do an actual lookup
+                status = vol->fstype_table->dir_lookup(vol, dno, &lookup_name, &child_dno);
+                if (status)
+                    goto errorexit;
+            }
+        }
+        
+        // child_dno becomes the new dno
+        fsw_dnode_release(dno);
+        dno = child_dno;   // is already retained
+        child_dno = NULL;
+        
+        FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno->dnode_id));
+    }
+    
+    *child_dno_out = dno;
+    return FSW_SUCCESS;
+    
+errorexit:
+    FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status));
+    fsw_dnode_release(dno);
+    if (child_dno != NULL)
+        fsw_dnode_release(child_dno);
+    return status;
+}
+
+/**
+ * Get the next directory item in sequential order. This function is called by the
+ * host driver to read the complete contents of a directory in sequential (file system
+ * defined) order. Calling this function returns the next entry. Iteration state is
+ * kept by a shandle on the directory's dnode. The caller must set up the shandle
+ * when starting the iteration.
+ *
+ * When the end of the directory is reached, this function returns FSW_NOT_FOUND.
+ * If the function returns FSW_SUCCESS, *child_dno_out points to the next directory
+ * entry. The caller must call fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out)
+{
+    fsw_status_t    status;
+    struct fsw_dnode *dno = shand->dnode;
+    fsw_u64         saved_pos;
+    
+    if (dno->type != FSW_DNODE_TYPE_DIR)
+        return FSW_UNSUPPORTED;
+    
+    saved_pos = shand->pos;
+    status = dno->vol->fstype_table->dir_read(dno->vol, dno, shand, child_dno_out);
+    if (status)
+        shand->pos = saved_pos;
+    return status;
+}
+
+/**
+ * Read the target path of a symbolic link. This function is called by the host driver
+ * to read the "content" of a symbolic link, that is the relative or absolute path
+ * it points to.
+ *
+ * If the function returns FSW_SUCCESS, the string handle provided by the caller is
+ * filled with a string in the host's preferred encoding. The caller is responsible
+ * for calling fsw_strfree on the string.
+ */
+
+fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *target_name)
+{
+    fsw_status_t    status;
+    
+    status = fsw_dnode_fill(dno);
+    if (status)
+        return status;
+    if (dno->type != FSW_DNODE_TYPE_SYMLINK)
+        return FSW_UNSUPPORTED;
+    
+    return dno->vol->fstype_table->readlink(dno->vol, dno, target_name);
+}
+
+/**
+ * Read the target path of a symbolic link by accessing file data. This function can
+ * be called by the file system driver if the file system stores the target path
+ * as normal file data. This function will open an shandle, read the whole content
+ * of the file into a buffer, and build a string from that. Currently the encoding
+ * for the string is fixed as FSW_STRING_TYPE_ISO88591.
+ *
+ * If the function returns FSW_SUCCESS, the string handle provided by the caller is
+ * filled with a string in the host's preferred encoding. The caller is responsible
+ * for calling fsw_strfree on the string.
+ */
+
+fsw_status_t fsw_dnode_readlink_data(struct fsw_dnode *dno, struct fsw_string *link_target)
+{
+    fsw_status_t    status;
+    struct fsw_shandle shand;
+    fsw_u32         buffer_size;
+    char            buffer[FSW_PATH_MAX];
+    struct fsw_string s;
+    
+    if (dno->size > FSW_PATH_MAX)
+        return FSW_VOLUME_CORRUPTED;
+    
+    s.type = FSW_STRING_TYPE_ISO88591;
+    s.size = s.len = (int)dno->size;
+    s.data = buffer;
+    
+    // open shandle and read the data
+    status = fsw_shandle_open(dno, &shand);
+    if (status)
+        return status;
+    buffer_size = (fsw_u32)s.size;
+    status = fsw_shandle_read(&shand, &buffer_size, buffer);
+    fsw_shandle_close(&shand);
+    if (status)
+        return status;
+    if ((int)buffer_size < s.size)
+        return FSW_VOLUME_CORRUPTED;
+    
+    status = fsw_strdup_coerce(link_target, dno->vol->host_string_type, &s);
+    return status;
+}
+
+/**
+ * Resolve a symbolic link. This function can be called by the host driver to make
+ * sure the a dnode is fully resolved instead of pointing at a symlink. If the dnode
+ * passed in is not a symlink, it is returned unmodified.
+ *
+ * Note that absolute paths will be resolved relative to the root directory of the
+ * volume. If the host is an operating system with its own VFS layer, it should
+ * resolve symlinks on its own.
+ *
+ * If the function returns FSW_SUCCESS, *target_dno_out points at a dnode that is
+ * not a symlink. The caller is responsible for calling fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out)
+{
+    fsw_status_t    status;
+    struct fsw_string target_name;
+    struct fsw_dnode *target_dno;
+    
+    fsw_dnode_retain(dno);
+    
+    while (1) {
+        // get full information
+        status = fsw_dnode_fill(dno);
+        if (status)
+            goto errorexit;
+        if (dno->type != FSW_DNODE_TYPE_SYMLINK) {
+            // found a non-symlink target, return it
+            *target_dno_out = dno;
+            return FSW_SUCCESS;
+        }
+        if (dno->parent == NULL) {    // safety measure, cannot happen in theory
+            status = FSW_NOT_FOUND;
+            goto errorexit;
+        }
+        
+        // read the link's target
+        status = fsw_dnode_readlink(dno, &target_name);
+        if (status)
+            goto errorexit;
+        
+        // resolve it
+        status = fsw_dnode_lookup_path(dno->parent, &target_name, '/', &target_dno);
+        fsw_strfree(&target_name);
+        if (status)
+            goto errorexit;
+        
+        // target_dno becomes the new dno
+        fsw_dnode_release(dno);
+        dno = target_dno;   // is already retained
+    }
+    
+errorexit:
+    fsw_dnode_release(dno);
+    return status;
+}
+
+/**
+ * Set up a shandle (storage handle) to access a file's data. This function is called
+ * by the host driver and by the core when they need to access a file's data. It is also
+ * used in accessing the raw data of directories and symlinks if the file system uses
+ * the same mechanisms for storing the data of those items.
+ *
+ * The storage for the fsw_shandle structure is provided by the caller. The dnode and pos
+ * fields may be accessed, pos may also be written to to set the file pointer. The file's
+ * data size is available as shand->dnode->size.
+ *
+ * If this function returns FSW_SUCCESS, the caller must call fsw_shandle_close to release
+ * the dnode reference held by the shandle.
+ */
+
+fsw_status_t fsw_shandle_open(struct fsw_dnode *dno, struct fsw_shandle *shand)
+{
+    fsw_status_t    status;
+    struct fsw_volume *vol = dno->vol;
+    
+    // read full dnode information into memory
+    status = vol->fstype_table->dnode_fill(vol, dno);
+    if (status)
+        return status;
+    
+    // setup shandle
+    fsw_dnode_retain(dno);
+    
+    shand->dnode = dno;
+    shand->pos = 0;
+    shand->extent.type = FSW_EXTENT_TYPE_INVALID;
+    
+    return FSW_SUCCESS;
+}
+
+/**
+ * Close a shandle after accessing the dnode's data. This function is called by the host
+ * driver or core functions when they are finished with accessing a file's data. It
+ * releases the dnode reference and frees any buffers associated with the shandle itself.
+ * The dnode is only released if this was the last reference using it.
+ */
+
+void fsw_shandle_close(struct fsw_shandle *shand)
+{
+    if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER)
+        fsw_free(shand->extent.buffer);
+    fsw_dnode_release(shand->dnode);
+}
+
+/**
+ * Read data from a shandle (storage handle for a dnode). This function is called by the
+ * host driver or internally when data is read from a file. TODO: more
+ */
+
+fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer_in)
+{
+    fsw_status_t    status;
+    struct fsw_dnode *dno = shand->dnode;
+    struct fsw_volume *vol = dno->vol;
+    fsw_u8          *buffer, *block_buffer;
+    fsw_u32         buflen, copylen, pos;
+    fsw_u32         log_bno, pos_in_extent, phys_bno, pos_in_physblock;
+    fsw_u32         cache_level;
+    
+    if (shand->pos >= dno->size) {   // already at EOF
+        *buffer_size_inout = 0;
+        return FSW_SUCCESS;
+    }
+    
+    // initialize vars
+    buffer = buffer_in;
+    buflen = *buffer_size_inout;
+    pos = (fsw_u32)shand->pos;
+    cache_level = (dno->type != FSW_DNODE_TYPE_FILE) ? 1 : 0;
+    // restrict read to file size
+    if (buflen > dno->size - pos)
+        buflen = (fsw_u32)(dno->size - pos);
+    
+    while (buflen > 0) {
+        // get extent for the current logical block
+        log_bno = pos / vol->log_blocksize;
+        if (shand->extent.type == FSW_EXTENT_TYPE_INVALID ||
+            log_bno < shand->extent.log_start ||
+            log_bno >= shand->extent.log_start + shand->extent.log_count) {
+            
+            if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER)
+                fsw_free(shand->extent.buffer);
+            
+            // ask the file system for the proper extent
+            shand->extent.log_start = log_bno;
+            status = vol->fstype_table->get_extent(vol, dno, &shand->extent);
+            if (status) {
+                shand->extent.type = FSW_EXTENT_TYPE_INVALID;
+                return status;
+            }
+        }
+        
+        pos_in_extent = pos - shand->extent.log_start * vol->log_blocksize;
+        
+        // dispatch by extent type
+        if (shand->extent.type == FSW_EXTENT_TYPE_PHYSBLOCK) {
+            // convert to physical block number and offset
+            phys_bno = shand->extent.phys_start + pos_in_extent / vol->phys_blocksize;
+            pos_in_physblock = pos_in_extent & (vol->phys_blocksize - 1);
+            copylen = vol->phys_blocksize - pos_in_physblock;
+            if (copylen > buflen)
+                copylen = buflen;
+            
+            // get one physical block
+            status = fsw_block_get(vol, phys_bno, cache_level, (void **)&block_buffer);
+            if (status)
+                return status;
+            
+            // copy data from it
+            fsw_memcpy(buffer, block_buffer + pos_in_physblock, copylen);
+            fsw_block_release(vol, phys_bno, block_buffer);
+            
+        } else if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) {
+            copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent;
+            if (copylen > buflen)
+                copylen = buflen;
+            fsw_memcpy(buffer, (fsw_u8 *)shand->extent.buffer + pos_in_extent, copylen);
+            
+        } else {   // _SPARSE or _INVALID
+            copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent;
+            if (copylen > buflen)
+                copylen = buflen;
+            fsw_memzero(buffer, copylen);
+            
+        }
+        
+        buffer += copylen;
+        buflen -= copylen;
+        pos    += copylen;
+    }
+    
+    *buffer_size_inout = (fsw_u32)(pos - shand->pos);
+    shand->pos = pos;
+    
+    return FSW_SUCCESS;
+}
+
+// EOF
diff --git a/OvmfPkg/FswHfsPlus/fsw_core.h b/OvmfPkg/FswHfsPlus/fsw_core.h
new file mode 100644
index 0000000..0041ce1
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_core.h
@@ -0,0 +1,517 @@
+/**
+ * \file fsw_core.h
+ * Core file system wrapper abstraction layer header.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ * Portions Copyright (c) The Regents of the University of California.
+ * Portions Copyright (c) UNIX System Laboratories, Inc.
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef _FSW_CORE_H_
+#define _FSW_CORE_H_
+
+#include "fsw_base.h"
+
+
+/** Maximum size for a path, specifically symlink target paths. */
+#define FSW_PATH_MAX (4096)
+
+/** Helper macro for token concatenation. */
+#define FSW_CONCAT3(a,b,c) a##b##c
+/** Expands to the name of a fstype dispatch table (fsw_fstype_table) for a named file system type. */
+#define FSW_FSTYPE_TABLE_NAME(t) FSW_CONCAT3(fsw_,t,_table)
+
+/** Indicates that the block cache entry is empty. */
+#define FSW_INVALID_BNO (~0UL)
+
+
+//
+// Byte-swapping macros
+//
+
+
+/**
+ * \name Byte Order Macros
+ * Implements big endian vs. little endian awareness and conversion.
+ */
+/*@{*/
+
+typedef fsw_u16             fsw_u16_le;
+typedef fsw_u16             fsw_u16_be;
+typedef fsw_u32             fsw_u32_le;
+typedef fsw_u32             fsw_u32_be;
+typedef fsw_u64             fsw_u64_le;
+typedef fsw_u64             fsw_u64_be;
+
+#define FSW_SWAPVALUE_U16(v) ((((fsw_u16)(v) & 0xff00) >> 8) | \
+                              (((fsw_u16)(v) & 0x00ff) << 8))
+#define FSW_SWAPVALUE_U32(v) ((((fsw_u32)(v) & 0xff000000UL) >> 24) | \
+                              (((fsw_u32)(v) & 0x00ff0000UL) >> 8)  | \
+                              (((fsw_u32)(v) & 0x0000ff00UL) << 8)  | \
+                              (((fsw_u32)(v) & 0x000000ffUL) << 24))
+#define FSW_SWAPVALUE_U64(v) ((((fsw_u64)(v) & 0xff00000000000000ULL) >> 56) | \
+                              (((fsw_u64)(v) & 0x00ff000000000000ULL) >> 40) | \
+                              (((fsw_u64)(v) & 0x0000ff0000000000ULL) >> 24) | \
+                              (((fsw_u64)(v) & 0x000000ff00000000ULL) >> 8)  | \
+                              (((fsw_u64)(v) & 0x00000000ff000000ULL) << 8)  | \
+                              (((fsw_u64)(v) & 0x0000000000ff0000ULL) << 24) | \
+                              (((fsw_u64)(v) & 0x000000000000ff00ULL) << 40) | \
+                              (((fsw_u64)(v) & 0x00000000000000ffULL) << 56))
+
+#ifdef FSW_LITTLE_ENDIAN
+
+#define fsw_u16_le_swap(v) (v)
+#define fsw_u16_be_swap(v) FSW_SWAPVALUE_U16(v)
+#define fsw_u32_le_swap(v) (v)
+#define fsw_u32_be_swap(v) FSW_SWAPVALUE_U32(v)
+#define fsw_u64_le_swap(v) (v)
+#define fsw_u64_be_swap(v) FSW_SWAPVALUE_U64(v)
+
+#define fsw_u16_le_sip(var)
+#define fsw_u16_be_sip(var) (var = FSW_SWAPVALUE_U16(var))
+#define fsw_u32_le_sip(var)
+#define fsw_u32_be_sip(var) (var = FSW_SWAPVALUE_U32(var))
+#define fsw_u64_le_sip(var)
+#define fsw_u64_be_sip(var) (var = FSW_SWAPVALUE_U64(var))
+
+#else
+#ifdef FSW_BIG_ENDIAN
+
+#define fsw_u16_le_swap(v) FSW_SWAPVALUE_U16(v)
+#define fsw_u16_be_swap(v) (v)
+#define fsw_u32_le_swap(v) FSW_SWAPVALUE_U32(v)
+#define fsw_u32_be_swap(v) (v)
+#define fsw_u64_le_swap(v) FSW_SWAPVALUE_U64(v)
+#define fsw_u64_be_swap(v) (v)
+
+#define fsw_u16_le_sip(var) (var = FSW_SWAPVALUE_U16(var))
+#define fsw_u16_be_sip(var)
+#define fsw_u32_le_sip(var) (var = FSW_SWAPVALUE_U32(var))
+#define fsw_u32_be_sip(var)
+#define fsw_u64_le_sip(var) (var = FSW_SWAPVALUE_U64(var))
+#define fsw_u64_be_sip(var)
+
+#else
+#fail Neither FSW_BIG_ENDIAN nor FSW_LITTLE_ENDIAN are defined
+#endif
+#endif
+
+/*@}*/
+
+
+//
+// The following evil hack avoids a lot of casts between generic and fstype-specific
+// structures.
+//
+
+#ifndef VOLSTRUCTNAME
+#define VOLSTRUCTNAME fsw_volume
+#else
+struct VOLSTRUCTNAME;
+#endif
+#ifndef DNODESTRUCTNAME
+#define DNODESTRUCTNAME fsw_dnode
+#else
+struct DNODESTRUCTNAME;
+#endif
+
+
+/**
+ * Status code type, returned from all functions that can fail.
+ */
+typedef int fsw_status_t;
+
+/**
+ * Possible status codes.
+ */
+enum {
+    FSW_SUCCESS,
+    FSW_OUT_OF_MEMORY,
+    FSW_IO_ERROR,
+    FSW_UNSUPPORTED,
+    FSW_NOT_FOUND,
+    FSW_VOLUME_CORRUPTED,
+    FSW_UNKNOWN_ERROR
+};
+
+
+/**
+ * Core: A string with explicit length and encoding information.
+ */
+
+struct fsw_string {
+    int         type;               //!< Encoding of the string - empty, ISO-8859-1, UTF8, UTF16
+    int         len;                //!< Length in characters
+    int         size;               //!< Total data size in bytes
+    void        *data;              //!< Data pointer (may be NULL if type is EMPTY or len is zero)
+};
+
+/**
+ * Possible string types / encodings. In the case of FSW_STRING_TYPE_EMPTY,
+ * all other members of the fsw_string structure may be invalid.
+ */
+enum {
+    FSW_STRING_TYPE_EMPTY,
+    FSW_STRING_TYPE_ISO88591,
+    FSW_STRING_TYPE_UTF8,
+    FSW_STRING_TYPE_UTF16,
+    FSW_STRING_TYPE_UTF16_SWAPPED
+};
+
+#ifdef FSW_LITTLE_ENDIAN
+#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16
+#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16_SWAPPED
+#else
+#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16_SWAPPED
+#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16
+#endif
+
+/** Static initializer for an empty string. */
+#define FSW_STRING_INIT { FSW_STRING_TYPE_EMPTY, 0, 0, NULL }
+
+
+/* forward declarations */
+
+struct fsw_dnode;
+struct fsw_host_table;
+struct fsw_fstype_table;
+
+struct fsw_blockcache {
+    fsw_u32     refcount;           //!< Reference count
+    fsw_u32     cache_level;        //!< Level of importance of this block
+    fsw_u32     phys_bno;           //!< Physical block number
+    void        *data;              //!< Block data buffer
+};
+
+/**
+ * Core: Represents a mounted volume.
+ */
+
+struct fsw_volume {
+    fsw_u32     phys_blocksize;     //!< Block size for disk access / file system structures
+    fsw_u32     log_blocksize;      //!< Block size for logical file data
+    
+    struct DNODESTRUCTNAME *root;   //!< Root directory dnode
+    struct fsw_string label;        //!< Volume label
+    
+    struct fsw_dnode *dnode_head;   //!< List of all dnodes allocated for this volume
+    
+    struct fsw_blockcache *bcache;  //!< Array of block cache entries
+    fsw_u32     bcache_size;        //!< Number of entries in the block cache array
+    
+    void        *host_data;         //!< Hook for a host-specific data structure
+    struct fsw_host_table *host_table;      //!< Dispatch table for host-specific functions
+    struct fsw_fstype_table *fstype_table;  //!< Dispatch table for file system specific functions
+    int         host_string_type;   //!< String type used by the host environment
+};
+
+/**
+ * Core: Represents a "directory node" - a file, directory, symlink, whatever.
+ */
+
+struct fsw_dnode {
+    fsw_u32     refcount;           //!< Reference count
+    
+    struct VOLSTRUCTNAME *vol;      //!< The volume this dnode belongs to
+    struct DNODESTRUCTNAME *parent; //!< Parent directory dnode
+    struct fsw_string name;         //!< Name of this item in the parent directory
+    
+    fsw_u32     dnode_id;           //!< Unique id number (usually the inode number)
+    int         type;               //!< Type of the dnode - file, dir, symlink, special
+    fsw_u64     size;               //!< Data size in bytes
+    
+    struct fsw_dnode *next;         //!< Doubly-linked list of all dnodes: previous dnode
+    struct fsw_dnode *prev;         //!< Doubly-linked list of all dnodes: next dnode
+};
+
+/**
+ * Possible dnode types. FSW_DNODE_TYPE_UNKNOWN may only be used before
+ * fsw_dnode_fill has been called on the dnode.
+ */
+enum {
+    FSW_DNODE_TYPE_UNKNOWN,
+    FSW_DNODE_TYPE_FILE,
+    FSW_DNODE_TYPE_DIR,
+    FSW_DNODE_TYPE_SYMLINK,
+    FSW_DNODE_TYPE_SPECIAL
+};
+
+/**
+ * Core: Stores the mapping of a region of a file to the data on disk.
+ */
+
+struct fsw_extent {
+    int         type;               //!< Type of extent specification
+    fsw_u32     log_start;          //!< Starting logical block number
+    fsw_u32     log_count;          //!< Logical block count
+    fsw_u32     phys_start;         //!< Starting physical block number (for FSW_EXTENT_TYPE_PHYSBLOCK only)
+    void        *buffer;            //!< Allocated buffer pointer (for FSW_EXTENT_TYPE_BUFFER only)
+};
+
+/**
+ * Possible extent representation types. FSW_EXTENT_TYPE_INVALID is for shandle's
+ * internal use only, it must not be returned from a get_extent function.
+ */
+enum {
+    FSW_EXTENT_TYPE_INVALID,
+    FSW_EXTENT_TYPE_SPARSE,
+    FSW_EXTENT_TYPE_PHYSBLOCK,
+    FSW_EXTENT_TYPE_BUFFER
+};
+
+/**
+ * Core: An access structure to a dnode's raw data. There can be multiple
+ * shandles per dnode, each of them has its own position pointer.
+ */
+
+struct fsw_shandle {
+    struct fsw_dnode *dnode;        //!< The dnode this handle reads data from
+    
+    fsw_u64     pos;                //!< Current file pointer in bytes
+    struct fsw_extent extent;       //!< Current extent
+};
+
+/**
+ * Core: Used in gathering detailed information on a volume.
+ */
+
+struct fsw_volume_stat {
+    fsw_u64     total_bytes;        //!< Total size of data area size in bytes
+    fsw_u64     free_bytes;         //!< Bytes still available for storing file data
+};
+
+/**
+ * Core: Used in gathering detailed information on a dnode.
+ */
+
+struct fsw_dnode_stat {
+    fsw_u64     used_bytes;         //!< Bytes actually used by the file on disk
+    void        (*store_time_posix)(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time);   //!< Callback for storing a Posix-style timestamp
+    void        (*store_attr_posix)(struct fsw_dnode_stat *sb, fsw_u16 posix_mode);   //!< Callbock for storing a Posix-style file mode
+    void        *host_data;         //!< Hook for a host-specific data structure
+};
+
+/**
+ * Type of the timestamp passed into store_time_posix.
+ */
+enum {
+    FSW_DNODE_STAT_CTIME,
+    FSW_DNODE_STAT_MTIME,
+    FSW_DNODE_STAT_ATIME
+};
+
+/**
+ * Core: Function table for a host environment.
+ */
+
+struct fsw_host_table
+{
+    int         native_string_type; //!< String type used by the host environment
+    
+    void         (*change_blocksize)(struct fsw_volume *vol,
+                                     fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
+                                     fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize);
+    fsw_status_t (*read_block)(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
+};
+
+/**
+ * Core: Function table for a file system driver.
+ */
+
+struct fsw_fstype_table
+{
+    struct fsw_string name;         //!< String giving the name of the file system
+    fsw_u32     volume_struct_size; //!< Size for allocating the fsw_volume structure
+    fsw_u32     dnode_struct_size;  //!< Size for allocating the fsw_dnode structure
+    
+    fsw_status_t (*volume_mount)(struct VOLSTRUCTNAME *vol);
+    void         (*volume_free)(struct VOLSTRUCTNAME *vol);
+    fsw_status_t (*volume_stat)(struct VOLSTRUCTNAME *vol, struct fsw_volume_stat *sb);
+    
+    fsw_status_t (*dnode_fill)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno);
+    void         (*dnode_free)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno);
+    fsw_status_t (*dnode_stat)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+                               struct fsw_dnode_stat *sb);
+    fsw_status_t (*get_extent)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+                               struct fsw_extent *extent);
+    
+    fsw_status_t (*dir_lookup)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+                               struct fsw_string *lookup_name, struct DNODESTRUCTNAME **child_dno);
+    fsw_status_t (*dir_read)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+                             struct fsw_shandle *shand, struct DNODESTRUCTNAME **child_dno);
+    fsw_status_t (*readlink)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+                             struct fsw_string *link_target);
+};
+
+
+/**
+ * \name Volume Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_mount(void *host_data,
+                       struct fsw_host_table *host_table,
+                       struct fsw_fstype_table *fstype_table,
+                       struct fsw_volume **vol_out);
+void         fsw_unmount(struct fsw_volume *vol);
+fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb);
+
+void         fsw_set_blocksize(struct VOLSTRUCTNAME *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize);
+fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out);
+void         fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer);
+
+/*@}*/
+
+
+/**
+ * \name dnode Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_dnode_create_root(struct VOLSTRUCTNAME *vol, fsw_u32 dnode_id, struct DNODESTRUCTNAME **dno_out);
+fsw_status_t fsw_dnode_create(struct DNODESTRUCTNAME *parent_dno, fsw_u32 dnode_id, int type,
+                              struct fsw_string *name, struct DNODESTRUCTNAME **dno_out);
+void         fsw_dnode_retain(struct fsw_dnode *dno);
+void         fsw_dnode_release(struct fsw_dnode *dno);
+
+fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno);
+fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb);
+
+fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno,
+                              struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out);
+fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno,
+                                   struct fsw_string *lookup_path, char separator,
+                                   struct fsw_dnode **child_dno_out);
+fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out);
+fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *link_target);
+fsw_status_t fsw_dnode_readlink_data(struct DNODESTRUCTNAME *dno, struct fsw_string *link_target);
+fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out);
+
+/*@}*/
+
+
+/**
+ * \name shandle Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_shandle_open(struct DNODESTRUCTNAME *dno, struct fsw_shandle *shand);
+void         fsw_shandle_close(struct fsw_shandle *shand);
+fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer);
+
+/*@}*/
+
+
+/**
+ * \name Memory Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_alloc_zero(int len, void **ptr_out);
+fsw_status_t fsw_memdup(void **dest_out, void *src, int len);
+
+/*@}*/
+
+
+/**
+ * \name String Functions
+ */
+/*@{*/
+
+int          fsw_strlen(struct fsw_string *s);
+int          fsw_streq(struct fsw_string *s1, struct fsw_string *s2);
+int          fsw_streq_cstr(struct fsw_string *s1, const char *s2);
+fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src);
+void         fsw_strsplit(struct fsw_string *lookup_name, struct fsw_string *buffer, char separator);
+
+void         fsw_strfree(struct fsw_string *s);
+
+/*@}*/
+
+
+/**
+ * \name Posix Mode Macros
+ * These macros can be used globally to test fields and bits in
+ * Posix-style modes.
+ *
+ * Taken from FreeBSD sys/stat.h.
+ */
+/*@{*/
+#ifndef S_IRWXU
+
+#define	S_ISUID	0004000			/* set user id on execution */
+#define	S_ISGID	0002000			/* set group id on execution */
+#define	S_ISTXT	0001000			/* sticky bit */
+
+#define	S_IRWXU	0000700			/* RWX mask for owner */
+#define	S_IRUSR	0000400			/* R for owner */
+#define	S_IWUSR	0000200			/* W for owner */
+#define	S_IXUSR	0000100			/* X for owner */
+
+#define	S_IRWXG	0000070			/* RWX mask for group */
+#define	S_IRGRP	0000040			/* R for group */
+#define	S_IWGRP	0000020			/* W for group */
+#define	S_IXGRP	0000010			/* X for group */
+
+#define	S_IRWXO	0000007			/* RWX mask for other */
+#define	S_IROTH	0000004			/* R for other */
+#define	S_IWOTH	0000002			/* W for other */
+#define	S_IXOTH	0000001			/* X for other */
+
+#define	S_IFMT	 0170000		/* type of file mask */
+#define	S_IFIFO	 0010000		/* named pipe (fifo) */
+#define	S_IFCHR	 0020000		/* character special */
+#define	S_IFDIR	 0040000		/* directory */
+#define	S_IFBLK	 0060000		/* block special */
+#define	S_IFREG	 0100000		/* regular */
+#define	S_IFLNK	 0120000		/* symbolic link */
+#define	S_IFSOCK 0140000		/* socket */
+#define	S_ISVTX	 0001000		/* save swapped text even after use */
+#define	S_IFWHT  0160000		/* whiteout */
+
+#define	S_ISDIR(m)	(((m) & 0170000) == 0040000)	/* directory */
+#define	S_ISCHR(m)	(((m) & 0170000) == 0020000)	/* char special */
+#define	S_ISBLK(m)	(((m) & 0170000) == 0060000)	/* block special */
+#define	S_ISREG(m)	(((m) & 0170000) == 0100000)	/* regular file */
+#define	S_ISFIFO(m)	(((m) & 0170000) == 0010000)	/* fifo or socket */
+#define	S_ISLNK(m)	(((m) & 0170000) == 0120000)	/* symbolic link */
+#define	S_ISSOCK(m)	(((m) & 0170000) == 0140000)	/* socket */
+#define	S_ISWHT(m)	(((m) & 0170000) == 0160000)	/* whiteout */
+
+#define S_BLKSIZE	512		/* block size used in the stat struct */
+
+#endif
+/*@}*/
+
+
+#endif
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi.c b/OvmfPkg/FswHfsPlus/fsw_efi.c
new file mode 100644
index 0000000..c024162
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_efi.c
@@ -0,0 +1,1044 @@
+/**
+ * \file fsw_efi.c
+ * EFI host environment code.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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 "fsw_efi.h"
+
+#define DEBUG_LEVEL 0
+
+
+#ifndef FSTYPE
+/** The file system type name to use. */
+#define FSTYPE ext2
+#endif
+
+/** Helper macro for stringification. */
+#define FSW_EFI_STRINGIFY(x) L#x
+/** Expands to the EFI driver name given the file system type name. */
+#define FSW_EFI_DRIVER_NAME(t) L"Fsw " FSW_EFI_STRINGIFY(t) L" File System Driver"
+
+// function prototypes
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+                                                  IN EFI_HANDLE                   ControllerHandle,
+                                                  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath);
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+                                              IN EFI_HANDLE                   ControllerHandle,
+                                              IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath);
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+                                             IN  EFI_HANDLE                   ControllerHandle,
+                                             IN  UINTN                        NumberOfChildren,
+                                             IN  EFI_HANDLE                   *ChildHandleBuffer);
+
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+                                                      IN  CHAR8                        *Language,
+                                                      OUT CHAR16                       **DriverName);
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN  EFI_COMPONENT_NAME_PROTOCOL    *This,
+                                                          IN  EFI_HANDLE                     ControllerHandle,
+                                                          IN  EFI_HANDLE                     ChildHandle  OPTIONAL,
+                                                          IN  CHAR8                          *Language,
+                                                          OUT CHAR16                         **ControllerName);
+
+void fsw_efi_change_blocksize(struct fsw_volume *vol,
+                              fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
+                              fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize);
+fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
+
+EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume);
+
+EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
+                                                OUT EFI_FILE **Root);
+EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
+                                       OUT EFI_FILE **NewFileHandle);
+
+EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
+                             IN OUT UINTN *BufferSize,
+                             OUT VOID *Buffer);
+EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
+                               OUT UINT64 *Position);
+EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
+                               IN UINT64 Position);
+
+EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
+                            OUT EFI_FILE **NewHandle,
+                            IN CHAR16 *FileName,
+                            IN UINT64 OpenMode,
+                            IN UINT64 Attributes);
+EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
+                            IN OUT UINTN *BufferSize,
+                            OUT VOID *Buffer);
+EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
+                              IN UINT64 Position);
+
+EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
+                                 IN EFI_GUID *InformationType,
+                                 IN OUT UINTN *BufferSize,
+                                 OUT VOID *Buffer);
+EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
+                                       IN struct fsw_dnode *dno,
+                                       IN OUT UINTN *BufferSize,
+                                       OUT VOID *Buffer);
+
+/**
+ * Interface structure for the EFI Driver Binding protocol.
+ */
+
+EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = {
+    fsw_efi_DriverBinding_Supported,
+    fsw_efi_DriverBinding_Start,
+    fsw_efi_DriverBinding_Stop,
+    0x10,
+    NULL,
+    NULL
+};
+
+/**
+ * Interface structure for the EFI Component Name protocol.
+ */
+
+EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = {
+    fsw_efi_ComponentName_GetDriverName,
+    fsw_efi_ComponentName_GetControllerName,
+    "eng"
+};
+
+/**
+ * Dispatch table for our FSW host driver.
+ */
+
+struct fsw_host_table   fsw_efi_host_table = {
+    FSW_STRING_TYPE_UTF16,
+    
+    fsw_efi_change_blocksize,
+    fsw_efi_read_block
+};
+
+extern struct fsw_fstype_table   FSW_FSTYPE_TABLE_NAME(FSTYPE);
+
+
+EFI_DRIVER_ENTRY_POINT(fsw_efi_main)
+
+/**
+ * Image entry point. Installs the Driver Binding and Component Name protocols
+ * on the image's handle. Actually mounting a file system is initiated through
+ * the Driver Binding protocol at the firmware's request.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE         ImageHandle,
+                               IN EFI_SYSTEM_TABLE   *SystemTable)
+{
+    EFI_STATUS  Status;
+    
+    InitializeLib(ImageHandle, SystemTable);
+    
+    // complete Driver Binding protocol instance
+    fsw_efi_DriverBinding_table.ImageHandle          = ImageHandle;
+    fsw_efi_DriverBinding_table.DriverBindingHandle  = ImageHandle;
+    // install Driver Binding protocol
+    Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
+                                          &DriverBindingProtocol,
+                                          EFI_NATIVE_INTERFACE,
+                                          &fsw_efi_DriverBinding_table);
+    if (EFI_ERROR (Status)) {
+        return Status;
+    }
+    
+    // install Component Name protocol
+    Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
+                                          &ComponentNameProtocol,
+                                          EFI_NATIVE_INTERFACE,
+                                          &fsw_efi_ComponentName_table);
+    if (EFI_ERROR (Status)) {
+        return Status;
+    }
+    
+    return EFI_SUCCESS;
+}
+
+/**
+ * Driver Binding EFI protocol, Supported function. This function is called by EFI
+ * to test if this driver can handle a certain device. Our implementation only checks
+ * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols)
+ * and implicitly checks if the disk is already in use by another driver.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+                                                  IN EFI_HANDLE                   ControllerHandle,
+                                                  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath)
+{
+    EFI_STATUS          Status;
+    EFI_DISK_IO         *DiskIo;
+    
+    // we check for both DiskIO and BlockIO protocols
+    
+    // first, open DiskIO
+    Status = BS->OpenProtocol(ControllerHandle,
+                              &DiskIoProtocol,
+                              (VOID **) &DiskIo,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_BY_DRIVER);
+    if (EFI_ERROR(Status))
+        return Status;
+    
+    // we were just checking, close it again
+    BS->CloseProtocol(ControllerHandle,
+                      &DiskIoProtocol,
+                      This->DriverBindingHandle,
+                      ControllerHandle);
+    
+    // next, check BlockIO without actually opening it
+    Status = BS->OpenProtocol(ControllerHandle,
+                              &BlockIoProtocol,
+                              NULL,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+    return Status;
+}
+
+/**
+ * Driver Binding EFI protocol, Start function. This function is called by EFI
+ * to start driving the given device. It is still possible at this point to
+ * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver
+ * cannot find the superblock signature (or equivalent) that it expects.
+ *
+ * This function allocates memory for a per-volume structure, opens the
+ * required protocols (just Disk I/O in our case, Block I/O is only looked
+ * at to get the MediaId field), and lets the FSW core mount the file system.
+ * If successful, an EFI Simple File System protocol is exported on the
+ * device handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+                                              IN EFI_HANDLE                   ControllerHandle,
+                                              IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath)
+{
+    EFI_STATUS          Status;
+    EFI_BLOCK_IO        *BlockIo;
+    EFI_DISK_IO         *DiskIo;
+    FSW_VOLUME_DATA     *Volume;
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_DriverBinding_Start\n");
+#endif
+    
+    // open consumed protocols
+    Status = BS->OpenProtocol(ControllerHandle,
+                              &BlockIoProtocol,
+                              (VOID **) &BlockIo,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);   // NOTE: we only want to look at the MediaId
+    if (EFI_ERROR(Status)) {
+        Print(L"Fsw ERROR: OpenProtocol(BlockIo) returned %x\n", Status);
+        return Status;
+    }
+    
+    Status = BS->OpenProtocol(ControllerHandle,
+                              &DiskIoProtocol,
+                              (VOID **) &DiskIo,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_BY_DRIVER);
+    if (EFI_ERROR(Status)) {
+        Print(L"Fsw ERROR: OpenProtocol(DiskIo) returned %x\n", Status);
+        return Status;
+    }
+    
+    // allocate volume structure
+    Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA));
+    Volume->Signature       = FSW_VOLUME_DATA_SIGNATURE;
+    Volume->Handle          = ControllerHandle;
+    Volume->DiskIo          = DiskIo;
+    Volume->MediaId         = BlockIo->Media->MediaId;
+    Volume->LastIOStatus    = EFI_SUCCESS;
+    
+    // mount the filesystem
+    Status = fsw_efi_map_status(fsw_mount(Volume, &fsw_efi_host_table,
+                                          &FSW_FSTYPE_TABLE_NAME(FSTYPE), &Volume->vol),
+                                Volume);
+    
+    if (!EFI_ERROR(Status)) {
+        // register the SimpleFileSystem protocol
+        Volume->FileSystem.Revision     = EFI_FILE_IO_INTERFACE_REVISION;
+        Volume->FileSystem.OpenVolume   = fsw_efi_FileSystem_OpenVolume;
+        Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
+                                                       &FileSystemProtocol, &Volume->FileSystem,
+                                                       NULL);
+        if (EFI_ERROR(Status))
+            Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status);
+    }
+    
+    // on errors, close the opened protocols
+    if (EFI_ERROR(Status)) {
+        if (Volume->vol != NULL)
+            fsw_unmount(Volume->vol);
+        FreePool(Volume);
+        
+        BS->CloseProtocol(ControllerHandle,
+                          &DiskIoProtocol,
+                          This->DriverBindingHandle,
+                          ControllerHandle);
+    }
+    
+    return Status;
+}
+
+/**
+ * Driver Binding EFI protocol, Stop function. This function is called by EFI
+ * to stop the driver on the given device. This translates to an unmount
+ * call for the FSW core.
+ *
+ * We assume that all file handles on the volume have been closed before
+ * the driver is stopped. At least with the EFI shell, that is actually the
+ * case; it closes all file handles between commands.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+                                             IN  EFI_HANDLE                   ControllerHandle,
+                                             IN  UINTN                        NumberOfChildren,
+                                             IN  EFI_HANDLE                   *ChildHandleBuffer)
+{
+    EFI_STATUS          Status;
+    EFI_FILE_IO_INTERFACE *FileSystem;
+    FSW_VOLUME_DATA     *Volume;
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_DriverBinding_Stop\n");
+#endif
+    
+    // get the installed SimpleFileSystem interface
+    Status = BS->OpenProtocol(ControllerHandle,
+                              &FileSystemProtocol,
+                              (VOID **) &FileSystem,
+                              This->DriverBindingHandle,
+                              ControllerHandle,
+                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+    if (EFI_ERROR(Status))
+        return EFI_UNSUPPORTED;
+    
+    // get private data structure
+    Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem);
+    
+    // uninstall Simple File System protocol
+    Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
+                                                     &FileSystemProtocol, &Volume->FileSystem,
+                                                     NULL);
+    if (EFI_ERROR(Status)) {
+        Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status);
+        return Status;
+    }
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n");
+#endif
+    
+    // release private data structure
+    if (Volume->vol != NULL)
+        fsw_unmount(Volume->vol);
+    FreePool(Volume);
+    
+    // close the consumed protocols
+    Status = BS->CloseProtocol(ControllerHandle,
+                               &DiskIoProtocol,
+                               This->DriverBindingHandle,
+                               ControllerHandle);
+    
+    return Status;
+}
+
+/**
+ * Component Name EFI protocol, GetDriverName function. Used by the EFI
+ * environment to inquire the name of this driver. The name returned is
+ * based on the file system type actually used in compilation.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+                                                      IN  CHAR8                        *Language,
+                                                      OUT CHAR16                       **DriverName)
+{
+    if (Language == NULL || DriverName == NULL)
+        return EFI_INVALID_PARAMETER;
+    
+    if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) {
+        *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE);
+        return EFI_SUCCESS;
+    }
+    return EFI_UNSUPPORTED;
+}
+
+/**
+ * Component Name EFI protocol, GetControllerName function. Not implemented
+ * because this is not a "bus" driver in the sense of the EFI Driver Model.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN  EFI_COMPONENT_NAME_PROTOCOL    *This,
+                                                          IN  EFI_HANDLE                     ControllerHandle,
+                                                          IN  EFI_HANDLE                     ChildHandle  OPTIONAL,
+                                                          IN  CHAR8                          *Language,
+                                                          OUT CHAR16                         **ControllerName)
+{
+    return EFI_UNSUPPORTED;
+}
+
+/**
+ * FSW interface function for block size changes. This function is called by the FSW core
+ * when the file system driver changes the block sizes for the volume.
+ */
+
+void fsw_efi_change_blocksize(struct fsw_volume *vol,
+                              fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
+                              fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize)
+{
+    // nothing to do
+}
+
+/**
+ * FSW interface function to read data blocks. This function is called by the FSW core
+ * to read a block of data from the device. The buffer is allocated by the core code.
+ */
+
+fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer)
+{
+    EFI_STATUS          Status;
+    FSW_VOLUME_DATA     *Volume = (FSW_VOLUME_DATA *)vol->host_data;
+    
+    FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d  (%d)\n"), phys_bno, vol->phys_blocksize));
+    
+    // read from disk
+    Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId,
+                                      (UINT64)phys_bno * vol->phys_blocksize,
+                                      vol->phys_blocksize,
+                                      buffer);
+    Volume->LastIOStatus = Status;
+    if (EFI_ERROR(Status))
+        return FSW_IO_ERROR;
+    return FSW_SUCCESS;
+}
+
+/**
+ * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced
+ * by fsw_efi_read_block, so we map it back to the EFI status code remembered from
+ * the last I/O operation.
+ */
+
+EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume)
+{
+    switch (fsw_status) {
+        case FSW_SUCCESS:
+            return EFI_SUCCESS;
+        case FSW_OUT_OF_MEMORY:
+            return EFI_VOLUME_CORRUPTED;
+        case FSW_IO_ERROR:
+            return Volume->LastIOStatus;
+        case FSW_UNSUPPORTED:
+            return EFI_UNSUPPORTED;
+        case FSW_NOT_FOUND:
+            return EFI_NOT_FOUND;
+        case FSW_VOLUME_CORRUPTED:
+            return EFI_VOLUME_CORRUPTED;
+        default:
+            return EFI_DEVICE_ERROR;
+    }
+}
+
+/**
+ * File System EFI protocol, OpenVolume function. Creates a file handle for
+ * the root directory and returns it. Note that this function may be called
+ * multiple times and returns a new file handle each time. Each returned
+ * handle is closed by the client using it.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
+                                                OUT EFI_FILE **Root)
+{
+    EFI_STATUS          Status;
+    FSW_VOLUME_DATA     *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This);
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_FileSystem_OpenVolume\n");
+#endif
+    
+    Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root);
+    
+    return Status;
+}
+
+/**
+ * File Handle EFI protocol, Open function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This,
+                                          OUT EFI_FILE **NewHandle,
+                                          IN CHAR16 *FileName,
+                                          IN UINT64 OpenMode,
+                                          IN UINT64 Attributes)
+{
+    FSW_FILE_DATA      *File = FSW_FILE_FROM_FILE_HANDLE(This);
+    
+    if (File->Type == FSW_EFI_FILE_TYPE_DIR)
+        return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes);
+    // not supported for regular files
+    return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, Close function. Closes the FSW shandle
+ * and frees the memory used for the structure.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This)
+{
+    FSW_FILE_DATA      *File = FSW_FILE_FROM_FILE_HANDLE(This);
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_FileHandle_Close\n");
+#endif
+    
+    fsw_shandle_close(&File->shand);
+    FreePool(File);
+    
+    return EFI_SUCCESS;
+}
+
+/**
+ * File Handle EFI protocol, Delete function. Calls through to Close
+ * and returns a warning because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This)
+{
+    EFI_STATUS          Status;
+    
+    Status = This->Close(This);
+    if (Status == EFI_SUCCESS) {
+        // this driver is read-only
+        Status = EFI_WARN_DELETE_FAILURE;
+    }
+    
+    return Status;
+}
+
+/**
+ * File Handle EFI protocol, Read function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This,
+                                          IN OUT UINTN *BufferSize,
+                                          OUT VOID *Buffer)
+{
+    FSW_FILE_DATA      *File = FSW_FILE_FROM_FILE_HANDLE(This);
+    
+    if (File->Type == FSW_EFI_FILE_TYPE_FILE)
+        return fsw_efi_file_read(File, BufferSize, Buffer);
+    else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
+        return fsw_efi_dir_read(File, BufferSize, Buffer);
+    return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, Write function. Returns unsupported status
+ * because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This,
+                                           IN OUT UINTN *BufferSize,
+                                           IN VOID *Buffer)
+{
+    // this driver is read-only
+    return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * File Handle EFI protocol, GetPosition function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This,
+                                                 OUT UINT64 *Position)
+{
+    FSW_FILE_DATA      *File = FSW_FILE_FROM_FILE_HANDLE(This);
+    
+    if (File->Type == FSW_EFI_FILE_TYPE_FILE)
+        return fsw_efi_file_getpos(File, Position);
+    // not defined for directories
+    return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, SetPosition function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This,
+                                                 IN UINT64 Position)
+{
+    FSW_FILE_DATA      *File = FSW_FILE_FROM_FILE_HANDLE(This);
+    
+    if (File->Type == FSW_EFI_FILE_TYPE_FILE)
+        return fsw_efi_file_setpos(File, Position);
+    else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
+        return fsw_efi_dir_setpos(File, Position);
+    return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, GetInfo function. Dispatches to the common
+ * function implementing this.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This,
+                                             IN EFI_GUID *InformationType,
+                                             IN OUT UINTN *BufferSize,
+                                             OUT VOID *Buffer)
+{
+    FSW_FILE_DATA      *File = FSW_FILE_FROM_FILE_HANDLE(This);
+    
+    return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer);
+}
+
+/**
+ * File Handle EFI protocol, SetInfo function. Returns unsupported status
+ * because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This,
+                                             IN EFI_GUID *InformationType,
+                                             IN UINTN BufferSize,
+                                             IN VOID *Buffer)
+{
+    // this driver is read-only
+    return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * File Handle EFI protocol, Flush function. Returns unsupported status
+ * because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This)
+{
+    // this driver is read-only
+    return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * Set up a file handle for a dnode. This function allocates a data structure
+ * for a file handle, opens a FSW shandle and populates the EFI_FILE structure
+ * with the interface functions.
+ */
+
+EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
+                                       OUT EFI_FILE **NewFileHandle)
+{
+    EFI_STATUS          Status;
+    FSW_FILE_DATA       *File;
+    
+    // make sure the dnode has complete info
+    Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data);
+    if (EFI_ERROR(Status))
+        return Status;
+    
+    // check type
+    if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR)
+        return EFI_UNSUPPORTED;
+    
+    // allocate file structure
+    File = AllocateZeroPool(sizeof(FSW_FILE_DATA));
+    File->Signature = FSW_FILE_DATA_SIGNATURE;
+    if (dno->type == FSW_DNODE_TYPE_FILE)
+        File->Type = FSW_EFI_FILE_TYPE_FILE;
+    else if (dno->type == FSW_DNODE_TYPE_DIR)
+        File->Type = FSW_EFI_FILE_TYPE_DIR;
+    
+    // open shandle
+    Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand),
+                                (FSW_VOLUME_DATA *)dno->vol->host_data);
+    if (EFI_ERROR(Status)) {
+        FreePool(File);
+        return Status;
+    }
+    
+    // populate the file handle
+    File->FileHandle.Revision    = EFI_FILE_HANDLE_REVISION;
+    File->FileHandle.Open        = fsw_efi_FileHandle_Open;
+    File->FileHandle.Close       = fsw_efi_FileHandle_Close;
+    File->FileHandle.Delete      = fsw_efi_FileHandle_Delete;
+    File->FileHandle.Read        = fsw_efi_FileHandle_Read;
+    File->FileHandle.Write       = fsw_efi_FileHandle_Write;
+    File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition;
+    File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition;
+    File->FileHandle.GetInfo     = fsw_efi_FileHandle_GetInfo;
+    File->FileHandle.SetInfo     = fsw_efi_FileHandle_SetInfo;
+    File->FileHandle.Flush       = fsw_efi_FileHandle_Flush;
+    
+    *NewFileHandle = &File->FileHandle;
+    return EFI_SUCCESS;
+}
+
+/**
+ * Data read function for regular files. Calls through to fsw_shandle_read.
+ */
+
+EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
+                             IN OUT UINTN *BufferSize,
+                             OUT VOID *Buffer)
+{
+    EFI_STATUS          Status;
+    fsw_u32             buffer_size;
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_file_read %d bytes\n", *BufferSize);
+#endif
+    
+    buffer_size = *BufferSize;
+    Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer),
+                                (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data);
+    *BufferSize = buffer_size;
+    
+    return Status;
+}
+
+/**
+ * Get file position for regular files.
+ */
+
+EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
+                               OUT UINT64 *Position)
+{
+    *Position = File->shand.pos;
+    return EFI_SUCCESS;
+}
+
+/**
+ * Set file position for regular files. EFI specifies the all-ones value
+ * to be a special value for the end of the file.
+ */
+
+EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
+                               IN UINT64 Position)
+{
+    if (Position == 0xFFFFFFFFFFFFFFFFULL)
+        File->shand.pos = File->shand.dnode->size;
+    else
+        File->shand.pos = Position;
+    return EFI_SUCCESS;
+}
+
+/**
+ * Open function used to open new file handles relative to a directory.
+ * In EFI, the "open file" function is implemented by directory file handles
+ * and is passed a relative or volume-absolute path to the file or directory
+ * to open. We use fsw_dnode_lookup_path to find the node plus an additional
+ * call to fsw_dnode_resolve because EFI has no concept of symbolic links.
+ */
+
+EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
+                            OUT EFI_FILE **NewHandle,
+                            IN CHAR16 *FileName,
+                            IN UINT64 OpenMode,
+                            IN UINT64 Attributes)
+{
+    EFI_STATUS          Status;
+    FSW_VOLUME_DATA     *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
+    struct fsw_dnode    *dno;
+    struct fsw_dnode    *target_dno;
+    struct fsw_string   lookup_path;
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_dir_open: '%s'\n", FileName);
+#endif
+    
+    if (OpenMode != EFI_FILE_MODE_READ)
+        return EFI_WRITE_PROTECTED;
+    
+    lookup_path.type = FSW_STRING_TYPE_UTF16;
+    lookup_path.len  = StrLen(FileName);
+    lookup_path.size = lookup_path.len * sizeof(fsw_u16);
+    lookup_path.data = FileName;
+    
+    // resolve the path (symlinks along the way are automatically resolved)
+    Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno),
+                                Volume);
+    if (EFI_ERROR(Status))
+        return Status;
+    
+    // if the final node is a symlink, also resolve it
+    Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno),
+                                Volume);
+    fsw_dnode_release(dno);
+    if (EFI_ERROR(Status))
+        return Status;
+    dno = target_dno;
+    
+    // make a new EFI handle for the target dnode
+    Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle);
+    fsw_dnode_release(dno);
+    return Status;
+}
+
+/**
+ * Read function for directories. A file handle read on a directory retrieves
+ * the next directory entry.
+ */
+
+EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
+                            IN OUT UINTN *BufferSize,
+                            OUT VOID *Buffer)
+{
+    EFI_STATUS          Status;
+    FSW_VOLUME_DATA     *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
+    struct fsw_dnode    *dno;
+    
+#if DEBUG_LEVEL
+    Print(L"fsw_efi_dir_read...\n");
+#endif
+    
+    // read the next entry
+    Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno),
+                                Volume);
+    if (Status == EFI_NOT_FOUND) {
+        // end of directory
+        *BufferSize = 0;
+#if DEBUG_LEVEL
+        Print(L"...no more entries\n");
+#endif
+        return EFI_SUCCESS;
+    }
+    if (EFI_ERROR(Status))
+        return Status;
+    
+    // get info into buffer
+    Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer);
+    fsw_dnode_release(dno);
+    return Status;
+}
+
+/**
+ * Set file position for directories. The only allowed set position operation
+ * for directories is to rewind the directory completely by setting the
+ * position to zero.
+ */
+
+EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
+                              IN UINT64 Position)
+{
+    if (Position == 0) {
+        File->shand.pos = 0;
+        return EFI_SUCCESS;
+    } else {
+        // directories can only rewind to the start
+        return EFI_UNSUPPORTED;
+    }
+}
+
+/**
+ * Get file or volume information. This function implements the GetInfo call
+ * for all file handles. Control is dispatched according to the type of information
+ * requested by the caller.
+ */
+
+EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
+                                 IN EFI_GUID *InformationType,
+                                 IN OUT UINTN *BufferSize,
+                                 OUT VOID *Buffer)
+{
+    EFI_STATUS          Status;
+    FSW_VOLUME_DATA     *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
+    EFI_FILE_SYSTEM_INFO *FSInfo;
+    UINTN               RequiredSize;
+    struct fsw_volume_stat vsb;
+    
+    if (CompareGuid(InformationType, &GenericFileInfo) == 0) {
+#if DEBUG_LEVEL
+        Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n");
+#endif
+        
+        Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer);
+        
+    } else if (CompareGuid(InformationType, &FileSystemInfo) == 0) {
+#if DEBUG_LEVEL
+        Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n");
+#endif
+        
+        // check buffer size
+        RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label);
+        if (*BufferSize < RequiredSize) {
+            *BufferSize = RequiredSize;
+            return EFI_BUFFER_TOO_SMALL;
+        }
+        
+        // fill structure
+        FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
+        FSInfo->Size        = RequiredSize;
+        FSInfo->ReadOnly    = TRUE;
+        FSInfo->BlockSize   = Volume->vol->log_blocksize;
+        fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label);
+        
+        // get the missing info from the fs driver
+        ZeroMem(&vsb, sizeof(struct fsw_volume_stat));
+        Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume);
+        if (EFI_ERROR(Status))
+            return Status;
+        FSInfo->VolumeSize  = vsb.total_bytes;
+        FSInfo->FreeSpace   = vsb.free_bytes;
+        
+        // prepare for return
+        *BufferSize = RequiredSize;
+        Status = EFI_SUCCESS;
+        
+    } else if (CompareGuid(InformationType, &FileSystemVolumeLabelInfo) == 0) {
+#if DEBUG_LEVEL
+        Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n");
+#endif
+        
+        // check buffer size
+        RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label);
+        if (*BufferSize < RequiredSize) {
+            *BufferSize = RequiredSize;
+            return EFI_BUFFER_TOO_SMALL;
+        }
+        
+        // copy volume label
+        fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label);
+        
+        // prepare for return
+        *BufferSize = RequiredSize;
+        Status = EFI_SUCCESS;
+        
+    } else {
+        Status = EFI_UNSUPPORTED;
+    }
+    
+    return Status;
+}
+
+/**
+ * Time mapping callback for the fsw_dnode_stat call. This function converts
+ * a Posix style timestamp into an EFI_TIME structure and writes it to the
+ * appropriate member of the EFI_FILE_INFO structure that we're filling.
+ */
+
+static void fsw_efi_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time)
+{
+    EFI_FILE_INFO       *FileInfo = (EFI_FILE_INFO *)sb->host_data;
+    
+    if (which == FSW_DNODE_STAT_CTIME)
+        fsw_efi_decode_time(&FileInfo->CreateTime,       posix_time);
+    else if (which == FSW_DNODE_STAT_MTIME)
+        fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time);
+    else if (which == FSW_DNODE_STAT_ATIME)
+        fsw_efi_decode_time(&FileInfo->LastAccessTime,   posix_time);
+}
+
+/**
+ * Mode mapping callback for the fsw_dnode_stat call. This function looks at
+ * the Posix mode passed by the file system driver and makes appropriate
+ * adjustments to the EFI_FILE_INFO structure that we're filling.
+ */
+
+static void fsw_efi_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode)
+{
+    EFI_FILE_INFO       *FileInfo = (EFI_FILE_INFO *)sb->host_data;
+    
+    if ((posix_mode & S_IWUSR) == 0)
+        FileInfo->Attribute |= EFI_FILE_READ_ONLY;
+}
+
+/**
+ * Common function to fill an EFI_FILE_INFO with information about a dnode.
+ */
+
+EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
+                                       IN struct fsw_dnode *dno,
+                                       IN OUT UINTN *BufferSize,
+                                       OUT VOID *Buffer)
+{
+    EFI_STATUS          Status;
+    EFI_FILE_INFO       *FileInfo;
+    UINTN               RequiredSize;
+    struct fsw_dnode_stat sb;
+    
+    // make sure the dnode has complete info
+    Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
+    if (EFI_ERROR(Status))
+        return Status;
+    
+    // TODO: check/assert that the dno's name is in UTF16
+    
+    // check buffer size
+    RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name);
+    if (*BufferSize < RequiredSize) {
+        // TODO: wind back the directory in this case
+        
+#if DEBUG_LEVEL
+        Print(L"...BUFFER TOO SMALL\n");
+#endif
+        *BufferSize = RequiredSize;
+        return EFI_BUFFER_TOO_SMALL;
+    }
+    
+    // fill structure
+    ZeroMem(Buffer, RequiredSize);
+    FileInfo = (EFI_FILE_INFO *)Buffer;
+    FileInfo->Size = RequiredSize;
+    FileInfo->FileSize          = dno->size;
+    FileInfo->Attribute         = 0;
+    if (dno->type == FSW_DNODE_TYPE_DIR)
+        FileInfo->Attribute    |= EFI_FILE_DIRECTORY;
+    fsw_efi_strcpy(FileInfo->FileName, &dno->name);
+    
+    // get the missing info from the fs driver
+    ZeroMem(&sb, sizeof(struct fsw_dnode_stat));
+    sb.store_time_posix = fsw_efi_store_time_posix;
+    sb.store_attr_posix = fsw_efi_store_attr_posix;
+    sb.host_data = FileInfo;
+    Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume);
+    if (EFI_ERROR(Status))
+        return Status;
+    FileInfo->PhysicalSize      = sb.used_bytes;
+    
+    // prepare for return
+    *BufferSize = RequiredSize;
+#if DEBUG_LEVEL
+    Print(L"...returning '%s'\n", FileInfo->FileName);
+#endif
+    return EFI_SUCCESS;
+}
+
+// EOF
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi.h b/OvmfPkg/FswHfsPlus/fsw_efi.h
new file mode 100644
index 0000000..a6f58f3
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_efi.h
@@ -0,0 +1,102 @@
+/**
+ * \file fsw_efi.h
+ * EFI host environment header.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef _FSW_EFI_H_
+#define _FSW_EFI_H_
+
+#include "fsw_core.h"
+
+
+/**
+ * EFI Host: Private per-volume structure.
+ */
+
+typedef struct {
+    UINT64                      Signature;      //!< Used to identify this structure
+    
+    EFI_FILE_IO_INTERFACE       FileSystem;     //!< Published EFI protocol interface structure
+    
+    EFI_HANDLE                  Handle;         //!< The device handle the protocol is attached to
+    EFI_DISK_IO                 *DiskIo;        //!< The Disk I/O protocol we use for disk access
+    UINT32                      MediaId;        //!< The media ID from the Block I/O protocol
+    EFI_STATUS                  LastIOStatus;   //!< Last status from Disk I/O
+    
+    struct fsw_volume           *vol;           //!< FSW volume structure
+    
+} FSW_VOLUME_DATA;
+
+/** Signature for the volume structure. */
+#define FSW_VOLUME_DATA_SIGNATURE  EFI_SIGNATURE_32 ('f', 's', 'w', 'V')
+/** Access macro for the volume structure. */
+#define FSW_VOLUME_FROM_FILE_SYSTEM(a)  CR (a, FSW_VOLUME_DATA, FileSystem, FSW_VOLUME_DATA_SIGNATURE)
+
+/**
+ * EFI Host: Private structure for a EFI_FILE interface.
+ */
+
+typedef struct {
+    UINT64                      Signature;      //!< Used to identify this structure
+    
+    EFI_FILE                    FileHandle;     //!< Published EFI protocol interface structure
+    
+    UINTN                       Type;           //!< File type used for dispatchinng
+    struct fsw_shandle          shand;          //!< FSW handle for this file
+    
+} FSW_FILE_DATA;
+
+/** File type: regular file. */
+#define FSW_EFI_FILE_TYPE_FILE  (0)
+/** File type: directory. */
+#define FSW_EFI_FILE_TYPE_DIR   (1)
+
+/** Signature for the file handle structure. */
+#define FSW_FILE_DATA_SIGNATURE    EFI_SIGNATURE_32 ('f', 's', 'w', 'F')
+/** Access macro for the file handle structure. */
+#define FSW_FILE_FROM_FILE_HANDLE(a)  CR (a, FSW_FILE_DATA, FileHandle, FSW_FILE_DATA_SIGNATURE)
+
+
+//
+// Library functions
+//
+
+VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime);
+
+UINTN fsw_efi_strsize(struct fsw_string *s);
+VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src);
+
+
+#endif
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi_base.h b/OvmfPkg/FswHfsPlus/fsw_efi_base.h
new file mode 100644
index 0000000..3643b3b
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_efi_base.h
@@ -0,0 +1,82 @@
+/**
+ * \file fsw_efi_base.h
+ * Base definitions for the EFI host environment.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef _FSW_EFI_BASE_H_
+#define _FSW_EFI_BASE_H_
+
+
+#include <efi.h>
+#include <efilib.h>
+
+#define FSW_LITTLE_ENDIAN (1)
+
+
+// types, reuse EFI types
+
+typedef INT8    fsw_s8;
+typedef UINT8   fsw_u8;
+typedef INT16   fsw_s16;
+typedef UINT16  fsw_u16;
+typedef INT32   fsw_s32;
+typedef UINT32  fsw_u32;
+typedef INT64   fsw_s64;
+typedef UINT64  fsw_u64;
+
+
+// allocation functions
+
+#define fsw_alloc(size, ptrptr) (((*(ptrptr) = AllocatePool(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS)
+#define fsw_free(ptr) FreePool(ptr)
+
+// memory functions
+
+#define fsw_memzero(dest,size) ZeroMem(dest,size)
+#define fsw_memcpy(dest,src,size) CopyMem(dest,src,size)
+#define fsw_memeq(p1,p2,size) (CompareMem(p1,p2,size) == 0)
+
+// message printing
+
+#define FSW_MSGSTR(s) L##s
+#define FSW_MSGFUNC Print
+
+// 64-bit hooks
+
+#define FSW_U64_SHR(val,shiftbits) RShiftU64((val), (shiftbits))
+#define FSW_U64_DIV(val,divisor) DivU64x32((val), (divisor), NULL)
+
+
+#endif
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h b/OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h
new file mode 100644
index 0000000..ca14eb2
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h
@@ -0,0 +1,76 @@
+/**
+ * \file fsw_efi_edk2_base.h
+ * Base definitions for the EDK EFI Toolkit environment.
+ */
+/*
+ * Copyright (c) 2012 Stefan Agner
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef _FSW_EFI_EDK2_BASE_H_
+#define _FSW_EFI_EDK2_BASE_H_
+/*
+ * Here is common declarations for EDK<->EDK2 compatibility
+ */
+# include <Base.h>
+# include <Uefi.h>
+# include <Library/DebugLib.h>
+# include <Library/BaseLib.h>
+# include <Protocol/DriverBinding.h>
+# include <Library/BaseMemoryLib.h>
+# include <Library/UefiRuntimeServicesTableLib.h>
+# include <Library/UefiDriverEntryPoint.h>
+# include <Library/UefiBootServicesTableLib.h>
+# include <Library/MemoryAllocationLib.h>
+# include <Library/DevicePathLib.h>
+# include <Protocol/DevicePathFromText.h>
+# include <Protocol/DevicePathToText.h>
+# include <Protocol/DebugPort.h>
+# include <Protocol/DebugSupport.h>
+# include <Library/PrintLib.h>
+# include <Library/UefiLib.h>
+# include <Protocol/SimpleFileSystem.h>
+# include <Protocol/BlockIo.h>
+# include <Protocol/DiskIo.h>
+# include <Guid/FileSystemInfo.h>
+# include <Guid/FileInfo.h>
+# include <Guid/FileSystemVolumeLabelInfo.h>
+# include <Protocol/ComponentName.h>
+
+# define BS gBS
+
+# define EFI_FILE_HANDLE_REVISION EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION
+# define SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO  SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
+# define EFI_FILE_SYSTEM_VOLUME_LABEL_INFO EFI_FILE_SYSTEM_VOLUME_LABEL
+# define EFI_SIGNATURE_32(a, b, c, d) SIGNATURE_32(a, b, c, d)
+# define DivU64x32(x,y,z) DivU64x32((x),(y))
+
+
+#endif
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi_lib.c b/OvmfPkg/FswHfsPlus/fsw_efi_lib.c
new file mode 100644
index 0000000..df1817c
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_efi_lib.c
@@ -0,0 +1,129 @@
+/**
+ * \file fsw_efi_lib.c
+ * EFI host environment library functions.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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 "fsw_efi.h"
+
+
+//
+// time conversion
+//
+// Adopted from public domain code in FreeBSD libc.
+//
+
+#define SECSPERMIN      60
+#define MINSPERHOUR     60
+#define HOURSPERDAY     24
+#define DAYSPERWEEK     7
+#define DAYSPERNYEAR    365
+#define DAYSPERLYEAR    366
+#define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY      ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR     12
+
+#define EPOCH_YEAR      1970
+#define EPOCH_WDAY      TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+#define LEAPS_THRU_END_OF(y)    ((y) / 4 - (y) / 100 + (y) / 400)
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+    { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+static const int year_lengths[2] = {
+    DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime)
+{
+    long        days, rem;
+    int         y, newy, yleap;
+    const int   *ip;
+    
+    ZeroMem(EfiTime, sizeof(EFI_TIME));
+    
+    days = UnixTime / SECSPERDAY;
+    rem = UnixTime % SECSPERDAY;
+    
+    EfiTime->Hour = (int) (rem / SECSPERHOUR);
+    rem = rem % SECSPERHOUR;
+    EfiTime->Minute = (int) (rem / SECSPERMIN);
+    EfiTime->Second = (int) (rem % SECSPERMIN);
+    
+    y = EPOCH_YEAR;
+    while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
+        newy = y + days / DAYSPERNYEAR;
+        if (days < 0)
+            --newy;
+        days -= (newy - y) * DAYSPERNYEAR +
+            LEAPS_THRU_END_OF(newy - 1) -
+            LEAPS_THRU_END_OF(y - 1);
+        y = newy;
+    }
+    EfiTime->Year = y;
+    ip = mon_lengths[yleap];
+    for (EfiTime->Month = 0; days >= (long) ip[EfiTime->Month]; ++(EfiTime->Month))
+        days = days - (long) ip[EfiTime->Month];
+    EfiTime->Month++;  // adjust range to EFI conventions
+    EfiTime->Day = (int) (days + 1);
+}
+
+//
+// String functions, used for file and volume info
+//
+
+UINTN fsw_efi_strsize(struct fsw_string *s)
+{
+    if (s->type == FSW_STRING_TYPE_EMPTY)
+        return sizeof(CHAR16);
+    return (s->len + 1) * sizeof(CHAR16);
+}
+
+VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src)
+{
+    if (src->type == FSW_STRING_TYPE_EMPTY) {
+        Dest[0] = 0;
+    } else if (src->type == FSW_STRING_TYPE_UTF16) {
+        CopyMem(Dest, src->data, src->size);
+        Dest[src->len] = 0;
+    } else {
+        // TODO: coerce, recurse
+        Dest[0] = 0;
+    }
+}
+
+// EOF
diff --git a/OvmfPkg/FswHfsPlus/fsw_lib.c b/OvmfPkg/FswHfsPlus/fsw_lib.c
new file mode 100644
index 0000000..abb9062
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_lib.c
@@ -0,0 +1,294 @@
+/**
+ * \file fsw_lib.c
+ * Core file system wrapper library functions.
+ */
+
+/*-
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * 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 Christoph Pfisterer nor the names of the
+ *    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 COPYRIGHT
+ * OWNER 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 "fsw_core.h"
+
+/* Include generated string encoding specific functions */
+#include "fsw_strfunc.h"
+
+
+/**
+ * Allocate memory and clear it.
+ */
+
+fsw_status_t fsw_alloc_zero(int len, void **ptr_out)
+{
+    fsw_status_t status;
+    
+    status = fsw_alloc(len, ptr_out);
+    if (status)
+        return status;
+    fsw_memzero(*ptr_out, len);
+    return FSW_SUCCESS;
+}
+
+/**
+ * Duplicate a piece of data.
+ */
+
+fsw_status_t fsw_memdup(void **dest_out, void *src, int len)
+{
+    fsw_status_t status;
+    
+    status = fsw_alloc(len, dest_out);
+    if (status)
+        return status;
+    fsw_memcpy(*dest_out, src, len);
+    return FSW_SUCCESS;
+}
+
+/**
+ * Get the length of a string. Returns the number of characters in the string.
+ */
+
+int fsw_strlen(struct fsw_string *s)
+{
+    if (s->type == FSW_STRING_TYPE_EMPTY)
+        return 0;
+    return s->len;
+}
+
+/**
+ * Compare two strings for equality. The two strings are compared, taking their
+ * encoding into account. If they are considered equal, boolean true is returned.
+ * Otherwise, boolean false is returned.
+ */
+
+int fsw_streq(struct fsw_string *s1, struct fsw_string *s2)
+{
+    struct fsw_string temp_s;
+    
+    // handle empty strings
+    if (s1->type == FSW_STRING_TYPE_EMPTY) {
+        temp_s.type = FSW_STRING_TYPE_ISO88591;
+        temp_s.size = temp_s.len = 0;
+        temp_s.data = NULL;
+        return fsw_streq(&temp_s, s2);
+    }
+    if (s2->type == FSW_STRING_TYPE_EMPTY) {
+        temp_s.type = FSW_STRING_TYPE_ISO88591;
+        temp_s.size = temp_s.len = 0;
+        temp_s.data = NULL;
+        return fsw_streq(s1, &temp_s);
+    }
+    
+    // check length (count of chars)
+    if (s1->len != s2->len)
+        return 0;
+    if (s1->len == 0)   // both strings are empty
+        return 1;
+    
+    if (s1->type == s2->type) {
+        // same type, do a dumb memory compare
+        if (s1->size != s2->size)
+            return 0;
+        return fsw_memeq(s1->data, s2->data, s1->size);
+    }
+    
+    // dispatch to type-specific functions
+    #define STREQ_DISPATCH(type1, type2) \
+      if (s1->type == FSW_STRING_TYPE_##type1 && s2->type == FSW_STRING_TYPE_##type2) \
+        return fsw_streq_##type1##_##type2(s1->data, s2->data, s1->len); \
+      if (s2->type == FSW_STRING_TYPE_##type1 && s1->type == FSW_STRING_TYPE_##type2) \
+        return fsw_streq_##type1##_##type2(s2->data, s1->data, s1->len);
+    STREQ_DISPATCH(ISO88591, UTF8);
+    STREQ_DISPATCH(ISO88591, UTF16);
+    STREQ_DISPATCH(ISO88591, UTF16_SWAPPED);
+    STREQ_DISPATCH(UTF8, UTF16);
+    STREQ_DISPATCH(UTF8, UTF16_SWAPPED);
+    STREQ_DISPATCH(UTF16, UTF16_SWAPPED);
+    
+    // final fallback
+    return 0;
+}
+
+/**
+ * Compare a string with a C string constant. This sets up a string descriptor
+ * for the string constant (second argument) and runs fsw_streq on the two
+ * strings. Currently the C string is interpreted as ISO 8859-1.
+ * Returns boolean true if the strings are considered equal, boolean false otherwise.
+ */
+
+int fsw_streq_cstr(struct fsw_string *s1, const char *s2)
+{
+    struct fsw_string temp_s;
+    int i;
+    
+    for (i = 0; s2[i]; i++)
+        ;
+    
+    temp_s.type = FSW_STRING_TYPE_ISO88591;
+    temp_s.size = temp_s.len = i;
+    temp_s.data = (char *)s2;
+    
+    return fsw_streq(s1, &temp_s);
+}
+
+/**
+ * Creates a duplicate of a string, converting it to the given encoding during the copy.
+ * If the function returns FSW_SUCCESS, the caller must free the string later with
+ * fsw_strfree.
+ */
+
+fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src)
+{
+    fsw_status_t    status;
+    
+    if (src->type == FSW_STRING_TYPE_EMPTY || src->len == 0) {
+        dest->type = type;
+        dest->size = dest->len = 0;
+        dest->data = NULL;
+        return FSW_SUCCESS;
+    }
+    
+    if (src->type == type) {
+        dest->type = type;
+        dest->len  = src->len;
+        dest->size = src->size;
+        status = fsw_alloc(dest->size, &dest->data);
+        if (status)
+            return status;
+        
+        fsw_memcpy(dest->data, src->data, dest->size);
+        return FSW_SUCCESS;
+    }
+    
+    // dispatch to type-specific functions
+    #define STRCOERCE_DISPATCH(type1, type2) \
+      if (src->type == FSW_STRING_TYPE_##type1 && type == FSW_STRING_TYPE_##type2) \
+        return fsw_strcoerce_##type1##_##type2(src->data, src->len, dest);
+    STRCOERCE_DISPATCH(UTF8, ISO88591);
+    STRCOERCE_DISPATCH(UTF16, ISO88591);
+    STRCOERCE_DISPATCH(UTF16_SWAPPED, ISO88591);
+    STRCOERCE_DISPATCH(ISO88591, UTF8);
+    STRCOERCE_DISPATCH(UTF16, UTF8);
+    STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF8);
+    STRCOERCE_DISPATCH(ISO88591, UTF16);
+    STRCOERCE_DISPATCH(UTF8, UTF16);
+    STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF16);
+    
+    return FSW_UNSUPPORTED;
+}
+
+/**
+ * Splits a string at the first occurence of the separator character.
+ * The buffer string is searched for the separator character. If it is found, the
+ * element string descriptor is filled to point at the part of the buffer string
+ * before the separator. The buffer string itself is adjusted to point at the
+ * remaining part of the string (without the separator).
+ *
+ * If the separator is not found in the buffer string, then element is changed to
+ * point at the whole buffer string, and the buffer string itself is changed into
+ * an empty string.
+ *
+ * This function only manipulates the pointers and lengths in the two string descriptors,
+ * it does not change the actual string. If the buffer string is dynamically allocated,
+ * you must make a copy of it so that you can release it later.
+ */
+
+void fsw_strsplit(struct fsw_string *element, struct fsw_string *buffer, char separator)
+{
+    int i, maxlen;
+    
+    if (buffer->type == FSW_STRING_TYPE_EMPTY || buffer->len == 0) {
+        element->type = FSW_STRING_TYPE_EMPTY;
+        return;
+    }
+    
+    maxlen = buffer->len;
+    *element = *buffer;
+    
+    if (buffer->type == FSW_STRING_TYPE_ISO88591) {
+        fsw_u8 *p;
+        
+        p = (fsw_u8 *)element->data;
+        for (i = 0; i < maxlen; i++, p++) {
+            if (*p == separator) {
+                buffer->data = p + 1;
+                buffer->len -= i + 1;
+                break;
+            }
+        }
+        element->len = i;
+        if (i == maxlen) {
+            buffer->data = p;
+            buffer->len -= i;
+        }
+        
+        element->size = element->len;
+        buffer->size  = buffer->len;
+        
+    } else if (buffer->type == FSW_STRING_TYPE_UTF16) {
+        fsw_u16 *p;
+        
+        p = (fsw_u16 *)element->data;
+        for (i = 0; i < maxlen; i++, p++) {
+            if (*p == separator) {
+                buffer->data = p + 1;
+                buffer->len -= i + 1;
+                break;
+            }
+        }
+        element->len = i;
+        if (i == maxlen) {
+            buffer->data = p;
+            buffer->len -= i;
+        }
+        
+        element->size = element->len * sizeof(fsw_u16);
+        buffer->size  = buffer->len  * sizeof(fsw_u16);
+        
+    } else {
+        // fallback
+        buffer->type = FSW_STRING_TYPE_EMPTY;
+    }
+    
+    // TODO: support UTF8 and UTF16_SWAPPED
+}
+
+/**
+ * Frees the memory used by a string returned from fsw_strdup_coerce.
+ */
+
+void fsw_strfree(struct fsw_string *s)
+{
+    if (s->type != FSW_STRING_TYPE_EMPTY && s->data)
+        fsw_free(s->data);
+    s->type = FSW_STRING_TYPE_EMPTY;
+}
+
+// EOF
diff --git a/OvmfPkg/FswHfsPlus/fsw_strfunc.h b/OvmfPkg/FswHfsPlus/fsw_strfunc.h
new file mode 100644
index 0000000..bc37415
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_strfunc.h
@@ -0,0 +1,453 @@
+/* fsw_strfunc.h generated by mk_fsw_strfunc.py */
+
+static int fsw_streq_ISO88591_UTF8(void *s1data, void *s2data, int len)
+{
+    int i;
+    fsw_u8 *p1 = (fsw_u8 *)s1data;
+    fsw_u8 *p2 = (fsw_u8 *)s2data;
+    fsw_u32 c1, c2;
+    
+    for (i = 0; i < len; i++) {
+        c1 = *p1++;
+        c2 = *p2++;
+        if ((c2 & 0xe0) == 0xc0) {
+            c2 = ((c2 & 0x1f) << 6) | (*p2++ & 0x3f);
+        } else if ((c2 & 0xf0) == 0xe0) {
+            c2 = ((c2 & 0x0f) << 12) | ((*p2++ & 0x3f) << 6);
+            c2 |= (*p2++ & 0x3f);
+        } else if ((c2 & 0xf8) == 0xf0) {
+            c2 = ((c2 & 0x07) << 18) | ((*p2++ & 0x3f) << 12);
+            c2 |= ((*p2++ & 0x3f) << 6);
+            c2 |= (*p2++ & 0x3f);
+        }
+        if (c1 != c2)
+            return 0;
+    }
+    return 1;
+}
+
+static int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len)
+{
+    int i;
+    fsw_u8 *p1 = (fsw_u8 *)s1data;
+    fsw_u16 *p2 = (fsw_u16 *)s2data;
+    fsw_u32 c1, c2;
+    
+    for (i = 0; i < len; i++) {
+        c1 = *p1++;
+        c2 = *p2++;
+        if (c1 != c2)
+            return 0;
+    }
+    return 1;
+}
+
+static int fsw_streq_ISO88591_UTF16_SWAPPED(void *s1data, void *s2data, int len)
+{
+    int i;
+    fsw_u8 *p1 = (fsw_u8 *)s1data;
+    fsw_u16 *p2 = (fsw_u16 *)s2data;
+    fsw_u32 c1, c2;
+    
+    for (i = 0; i < len; i++) {
+        c1 = *p1++;
+        c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2);
+        if (c1 != c2)
+            return 0;
+    }
+    return 1;
+}
+
+static int fsw_streq_UTF8_UTF16(void *s1data, void *s2data, int len)
+{
+    int i;
+    fsw_u8 *p1 = (fsw_u8 *)s1data;
+    fsw_u16 *p2 = (fsw_u16 *)s2data;
+    fsw_u32 c1, c2;
+    
+    for (i = 0; i < len; i++) {
+        c1 = *p1++;
+        if ((c1 & 0xe0) == 0xc0) {
+            c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f);
+        } else if ((c1 & 0xf0) == 0xe0) {
+            c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6);
+            c1 |= (*p1++ & 0x3f);
+        } else if ((c1 & 0xf8) == 0xf0) {
+            c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12);
+            c1 |= ((*p1++ & 0x3f) << 6);
+            c1 |= (*p1++ & 0x3f);
+        }
+        c2 = *p2++;
+        if (c1 != c2)
+            return 0;
+    }
+    return 1;
+}
+
+static int fsw_streq_UTF8_UTF16_SWAPPED(void *s1data, void *s2data, int len)
+{
+    int i;
+    fsw_u8 *p1 = (fsw_u8 *)s1data;
+    fsw_u16 *p2 = (fsw_u16 *)s2data;
+    fsw_u32 c1, c2;
+    
+    for (i = 0; i < len; i++) {
+        c1 = *p1++;
+        if ((c1 & 0xe0) == 0xc0) {
+            c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f);
+        } else if ((c1 & 0xf0) == 0xe0) {
+            c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6);
+            c1 |= (*p1++ & 0x3f);
+        } else if ((c1 & 0xf8) == 0xf0) {
+            c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12);
+            c1 |= ((*p1++ & 0x3f) << 6);
+            c1 |= (*p1++ & 0x3f);
+        }
+        c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2);
+        if (c1 != c2)
+            return 0;
+    }
+    return 1;
+}
+
+static int fsw_streq_UTF16_UTF16_SWAPPED(void *s1data, void *s2data, int len)
+{
+    int i;
+    fsw_u16 *p1 = (fsw_u16 *)s1data;
+    fsw_u16 *p2 = (fsw_u16 *)s2data;
+    fsw_u32 c1, c2;
+    
+    for (i = 0; i < len; i++) {
+        c1 = *p1++;
+        c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2);
+        if (c1 != c2)
+            return 0;
+    }
+    return 1;
+}
+
+static fsw_status_t fsw_strcoerce_UTF8_ISO88591(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i;
+    fsw_u8       *sp;
+    fsw_u8       *dp;
+    fsw_u32         c;
+    
+    dest->type = FSW_STRING_TYPE_ISO88591;
+    dest->len  = srclen;
+    dest->size = srclen * sizeof(fsw_u8);
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u8 *)srcdata;
+    dp = (fsw_u8 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        if ((c & 0xe0) == 0xc0) {
+            c = ((c & 0x1f) << 6) | (*sp++ & 0x3f);
+        } else if ((c & 0xf0) == 0xe0) {
+            c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6);
+            c |= (*sp++ & 0x3f);
+        } else if ((c & 0xf8) == 0xf0) {
+            c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12);
+            c |= ((*sp++ & 0x3f) << 6);
+            c |= (*sp++ & 0x3f);
+        }
+        *dp++ = c;
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_ISO88591(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i;
+    fsw_u16       *sp;
+    fsw_u8       *dp;
+    fsw_u32         c;
+    
+    dest->type = FSW_STRING_TYPE_ISO88591;
+    dest->len  = srclen;
+    dest->size = srclen * sizeof(fsw_u8);
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u16 *)srcdata;
+    dp = (fsw_u8 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        *dp++ = c;
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_ISO88591(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i;
+    fsw_u16       *sp;
+    fsw_u8       *dp;
+    fsw_u32         c;
+    
+    dest->type = FSW_STRING_TYPE_ISO88591;
+    dest->len  = srclen;
+    dest->size = srclen * sizeof(fsw_u8);
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u16 *)srcdata;
+    dp = (fsw_u8 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++; c = FSW_SWAPVALUE_U16(c);
+        *dp++ = c;
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_ISO88591_UTF16(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i;
+    fsw_u8       *sp;
+    fsw_u16       *dp;
+    fsw_u32         c;
+    
+    dest->type = FSW_STRING_TYPE_UTF16;
+    dest->len  = srclen;
+    dest->size = srclen * sizeof(fsw_u16);
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u8 *)srcdata;
+    dp = (fsw_u16 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        *dp++ = c;
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF8_UTF16(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i;
+    fsw_u8       *sp;
+    fsw_u16       *dp;
+    fsw_u32         c;
+    
+    dest->type = FSW_STRING_TYPE_UTF16;
+    dest->len  = srclen;
+    dest->size = srclen * sizeof(fsw_u16);
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u8 *)srcdata;
+    dp = (fsw_u16 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        if ((c & 0xe0) == 0xc0) {
+            c = ((c & 0x1f) << 6) | (*sp++ & 0x3f);
+        } else if ((c & 0xf0) == 0xe0) {
+            c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6);
+            c |= (*sp++ & 0x3f);
+        } else if ((c & 0xf8) == 0xf0) {
+            c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12);
+            c |= ((*sp++ & 0x3f) << 6);
+            c |= (*sp++ & 0x3f);
+        }
+        *dp++ = c;
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF16(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i;
+    fsw_u16       *sp;
+    fsw_u16       *dp;
+    fsw_u32         c;
+    
+    dest->type = FSW_STRING_TYPE_UTF16;
+    dest->len  = srclen;
+    dest->size = srclen * sizeof(fsw_u16);
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u16 *)srcdata;
+    dp = (fsw_u16 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++; c = FSW_SWAPVALUE_U16(c);
+        *dp++ = c;
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_ISO88591_UTF8(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i, destsize;
+    fsw_u8       *sp;
+    fsw_u8       *dp;
+    fsw_u32         c;
+    
+    sp = (fsw_u8 *)srcdata;
+    destsize = 0;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        
+        if (c < 0x000080)
+            destsize++;
+        else if (c < 0x000800)
+            destsize += 2;
+        else if (c < 0x010000)
+            destsize += 3;
+        else
+            destsize += 4;
+    }
+    
+    dest->type = FSW_STRING_TYPE_UTF8;
+    dest->len  = srclen;
+    dest->size = destsize;
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u8 *)srcdata;
+    dp = (fsw_u8 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        
+        if (c < 0x000080) {
+            *dp++ = c;
+        } else if (c < 0x000800) {
+            *dp++ = 0xc0 | ((c >> 6) & 0x1f);
+            *dp++ = 0x80 | (c & 0x3f);
+        } else if (c < 0x010000) {
+            *dp++ = 0xe0 | ((c >> 12) & 0x0f);
+            *dp++ = 0x80 | ((c >> 6) & 0x3f);
+            *dp++ = 0x80 | (c & 0x3f);
+        } else {
+            *dp++ = 0xf0 | ((c >> 18) & 0x07);
+            *dp++ = 0x80 | ((c >> 12) & 0x3f);
+            *dp++ = 0x80 | ((c >> 6) & 0x3f);
+            *dp++ = 0x80 | (c & 0x3f);
+        }
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_UTF8(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i, destsize;
+    fsw_u16       *sp;
+    fsw_u8       *dp;
+    fsw_u32         c;
+    
+    sp = (fsw_u16 *)srcdata;
+    destsize = 0;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        
+        if (c < 0x000080)
+            destsize++;
+        else if (c < 0x000800)
+            destsize += 2;
+        else if (c < 0x010000)
+            destsize += 3;
+        else
+            destsize += 4;
+    }
+    
+    dest->type = FSW_STRING_TYPE_UTF8;
+    dest->len  = srclen;
+    dest->size = destsize;
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u16 *)srcdata;
+    dp = (fsw_u8 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++;
+        
+        if (c < 0x000080) {
+            *dp++ = c;
+        } else if (c < 0x000800) {
+            *dp++ = 0xc0 | ((c >> 6) & 0x1f);
+            *dp++ = 0x80 | (c & 0x3f);
+        } else if (c < 0x010000) {
+            *dp++ = 0xe0 | ((c >> 12) & 0x0f);
+            *dp++ = 0x80 | ((c >> 6) & 0x3f);
+            *dp++ = 0x80 | (c & 0x3f);
+        } else {
+            *dp++ = 0xf0 | ((c >> 18) & 0x07);
+            *dp++ = 0x80 | ((c >> 12) & 0x3f);
+            *dp++ = 0x80 | ((c >> 6) & 0x3f);
+            *dp++ = 0x80 | (c & 0x3f);
+        }
+    }
+    return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF8(void *srcdata, int srclen, struct fsw_string *dest)
+{
+    fsw_status_t    status;
+    int             i, destsize;
+    fsw_u16       *sp;
+    fsw_u8       *dp;
+    fsw_u32         c;
+    
+    sp = (fsw_u16 *)srcdata;
+    destsize = 0;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++; c = FSW_SWAPVALUE_U16(c);
+        
+        if (c < 0x000080)
+            destsize++;
+        else if (c < 0x000800)
+            destsize += 2;
+        else if (c < 0x010000)
+            destsize += 3;
+        else
+            destsize += 4;
+    }
+    
+    dest->type = FSW_STRING_TYPE_UTF8;
+    dest->len  = srclen;
+    dest->size = destsize;
+    status = fsw_alloc(dest->size, &dest->data);
+    if (status)
+        return status;
+    
+    sp = (fsw_u16 *)srcdata;
+    dp = (fsw_u8 *)dest->data;
+    for (i = 0; i < srclen; i++) {
+        c = *sp++; c = FSW_SWAPVALUE_U16(c);
+        
+        if (c < 0x000080) {
+            *dp++ = c;
+        } else if (c < 0x000800) {
+            *dp++ = 0xc0 | ((c >> 6) & 0x1f);
+            *dp++ = 0x80 | (c & 0x3f);
+        } else if (c < 0x010000) {
+            *dp++ = 0xe0 | ((c >> 12) & 0x0f);
+            *dp++ = 0x80 | ((c >> 6) & 0x3f);
+            *dp++ = 0x80 | (c & 0x3f);
+        } else {
+            *dp++ = 0xf0 | ((c >> 18) & 0x07);
+            *dp++ = 0x80 | ((c >> 12) & 0x3f);
+            *dp++ = 0x80 | ((c >> 6) & 0x3f);
+            *dp++ = 0x80 | (c & 0x3f);
+        }
+    }
+    return FSW_SUCCESS;
+}
-- 
2.7.4

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel