public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot)
@ 2017-03-07  3:14 Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code Gabriel L. Somlo
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

This series is intended to restart the conversation re. adding
boot support for Mac OS X to QEMU via OVMF.

The *last* three patches represent what's left of Reza's excellent
work on getting OVMF to support booting OS X. They are rebased to
the point where they still apply cleanly and work, but I haven't
made any significant changes to them beyond that.

The *first* three patches (1-3 of 6) provide the BSD-licensed HFS+
filesystem support required by Reza's work:

    - patch 1/6: After a bit of archaeology, I managed to isolate
                 the original EFI "File System Wrapper" (FSW) files
                 released under the BSD by the "rEFIt" project; One
                 additional file, also BSD-licensed, was imported
                 from the "rEFInd" project.
                 The FSW is an abstraction layer that facilitates
                 the creation of a file system driver by providing
                 a set methods for mount/unmount/readdir/etc.

    - patch 2/6: fix a few compiler errors and other discrepancies to
                 facilitate integrating FSW into EDK2/OVMF.

    - patch 3/6: A BSD-licensed implementation of an FSW HFS+ driver.
                 Based on Apple's HFS+ specification (TN1150), this is
                 a minimalistic, bare-bones set of FSW methods capable
                 of locating and loading files from an HFS+ volume.
                 Lots of functionality (e.g. stat, readdir, hard/sym
                 links, or accessing files with more than 8 fragments)
                 is unimplemented at this time. I only implemented the
                 methods necessary to support loading Apple's boot.efi
                 and kernel.

                 For any extra features (such as stat, readdir, etc.)
                 I'd need to figure out how to navigate around on a
                 mounted volume (e.g. from the UEFI shell ?) to cause
                 the rest of the methods to be called, so I could test
                 whether they work or not. Particularly support for
                 fragmented files -- I'd have to create one somehow
                 (against OS X and HFS's best efforts to prevent it),
                 then cause it to be accessed from the UEFI shell to
                 test that whatever I'm implementing actually works.
                 Any "EDK2 filesystem developer's primer" on how to
                 do those things would be immensely helpful at this point.

Also, I would like some feedback and advice on where to go from here.

At this time, neither the FSW wrapper nor the HFS+ driver I wrote comply
with EDK2's coding standards. Before I start working on that, it would
be helpful to find out whether the FSW layer is of any interest to EDK2
as a way to facilitate adding support for arbitrary new filesystems.

If not, it might be worth factoring out the common bits and roll the
the whole thing up into a standalone HFS+ driver, which would be a
significantly different direction to go.

The series is also available on GitHub, in my "macboot" branch:

	https://github.com/gsomlo/edk2/tree/macboot

A set of instructions on how to get things working is available here:

	http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/

Thanks in advance for any feedback, advice, etc.
  --Gabriel

Gabriel L. Somlo (6):
  FswHfsPlus: add File System Wrapper (FSW) interface code
  FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies
  FswHfsPlus: implement FSW driver for the HFS+ file system
  EdkCompatibilityPkg: allow ConsoleControl protocol to be used
  OvmfPkg: add Apple boot support
  OvmfPkg: enable AppleSupport library for Ovmf firmware

 EdkCompatibilityPkg/EdkCompatibilityPkg.dec        |    2 +
 OvmfPkg/FswHfsPlus/FswHfsPlus.inf                  |   57 ++
 OvmfPkg/FswHfsPlus/fsw_base.h                      |  158 +++
 OvmfPkg/FswHfsPlus/fsw_core.c                      |  929 +++++++++++++++++
 OvmfPkg/FswHfsPlus/fsw_core.h                      |  517 ++++++++++
 OvmfPkg/FswHfsPlus/fsw_efi.c                       | 1040 ++++++++++++++++++++
 OvmfPkg/FswHfsPlus/fsw_efi.h                       |  102 ++
 OvmfPkg/FswHfsPlus/fsw_efi_base.h                  |   81 ++
 OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h             |   76 ++
 OvmfPkg/FswHfsPlus/fsw_efi_lib.c                   |  129 +++
 OvmfPkg/FswHfsPlus/fsw_hfsplus.c                   |  535 ++++++++++
 OvmfPkg/FswHfsPlus/fsw_hfsplus.h                   |  238 +++++
 OvmfPkg/FswHfsPlus/fsw_lib.c                       |  294 ++++++
 OvmfPkg/FswHfsPlus/fsw_strfunc.h                   |  453 +++++++++
 OvmfPkg/Include/Library/AppleSupportLib.h          |   28 +
 OvmfPkg/Library/AppleSupportLib/AppleSupport.c     |  107 ++
 .../Library/AppleSupportLib/AppleSupportLib.inf    |   50 +
 OvmfPkg/Library/AppleSupportLib/Bds.c              |  151 +++
 OvmfPkg/Library/AppleSupportLib/Bds.h              |   21 +
 OvmfPkg/Library/AppleSupportLib/Common.h           |   24 +
 OvmfPkg/Library/AppleSupportLib/Console.c          |   86 ++
 OvmfPkg/Library/AppleSupportLib/Console.h          |   28 +
 OvmfPkg/Library/AppleSupportLib/Datahub.c          |  104 ++
 OvmfPkg/Library/AppleSupportLib/Datahub.h          |   32 +
 .../Library/PlatformBootManagerLib/BdsPlatform.c   |   10 +
 .../Library/PlatformBootManagerLib/BdsPlatform.h   |    1 +
 .../PlatformBootManagerLib.inf                     |    1 +
 OvmfPkg/OvmfPkgIa32.dsc                            |    8 +
 OvmfPkg/OvmfPkgIa32.fdf                            |    3 +
 OvmfPkg/OvmfPkgIa32X64.dsc                         |    8 +
 OvmfPkg/OvmfPkgIa32X64.fdf                         |    3 +
 OvmfPkg/OvmfPkgX64.dsc                             |    8 +
 OvmfPkg/OvmfPkgX64.fdf                             |    3 +
 33 files changed, 5287 insertions(+)
 create mode 100644 OvmfPkg/FswHfsPlus/FswHfsPlus.inf
 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_hfsplus.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_hfsplus.h
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_lib.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_strfunc.h
 create mode 100644 OvmfPkg/Include/Library/AppleSupportLib.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupport.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Common.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.h

-- 
2.7.4



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
@ 2017-03-07  3:14 ` Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 2/6] FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies Gabriel L. Somlo
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

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



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 2/6] FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code Gabriel L. Somlo
@ 2017-03-07  3:14 ` Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 3/6] FswHfsPlus: implement FSW driver for the HFS+ file system Gabriel L. Somlo
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

Connect FSW to EDK2:

	- in fsw_efi_base.h, s/efi[lib].h/fsw_efi_edk2_base.h/

Fix compile discrepancies:

	- Fix FSW_INVALID_BNO, FSW_EFI_STRINGIFY macros
	- Remove EFI_DRIVER_ENTRY_POINT, InitializeLib() call
	- use correct protocol name GUIDs (gEfi<ProtoName>Guid)
	- edk2's CompareGuid returns TRUE on equal, not 0 like gnu-efi

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
 OvmfPkg/FswHfsPlus/fsw_core.h     |  2 +-
 OvmfPkg/FswHfsPlus/fsw_efi.c      | 36 ++++++++++++++++--------------------
 OvmfPkg/FswHfsPlus/fsw_efi_base.h |  3 +--
 3 files changed, 18 insertions(+), 23 deletions(-)

diff --git a/OvmfPkg/FswHfsPlus/fsw_core.h b/OvmfPkg/FswHfsPlus/fsw_core.h
index 0041ce1..a611b19 100644
--- a/OvmfPkg/FswHfsPlus/fsw_core.h
+++ b/OvmfPkg/FswHfsPlus/fsw_core.h
@@ -52,7 +52,7 @@
 #define FSW_FSTYPE_TABLE_NAME(t) FSW_CONCAT3(fsw_,t,_table)
 
 /** Indicates that the block cache entry is empty. */
-#define FSW_INVALID_BNO (~0UL)
+#define FSW_INVALID_BNO (~0U)
 
 
 //
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi.c b/OvmfPkg/FswHfsPlus/fsw_efi.c
index c024162..d2b53a7 100644
--- a/OvmfPkg/FswHfsPlus/fsw_efi.c
+++ b/OvmfPkg/FswHfsPlus/fsw_efi.c
@@ -46,7 +46,7 @@
 #endif
 
 /** Helper macro for stringification. */
-#define FSW_EFI_STRINGIFY(x) L#x
+#define FSW_EFI_STRINGIFY(x) #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"
 
@@ -149,8 +149,6 @@ struct fsw_host_table   fsw_efi_host_table = {
 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
@@ -162,14 +160,12 @@ EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE         ImageHandle,
 {
     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,
+                                          &gEfiDriverBindingProtocolGuid,
                                           EFI_NATIVE_INTERFACE,
                                           &fsw_efi_DriverBinding_table);
     if (EFI_ERROR (Status)) {
@@ -178,7 +174,7 @@ EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE         ImageHandle,
     
     // install Component Name protocol
     Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
-                                          &ComponentNameProtocol,
+                                          &gEfiComponentNameProtocolGuid,
                                           EFI_NATIVE_INTERFACE,
                                           &fsw_efi_ComponentName_table);
     if (EFI_ERROR (Status)) {
@@ -206,7 +202,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL
     
     // first, open DiskIO
     Status = BS->OpenProtocol(ControllerHandle,
-                              &DiskIoProtocol,
+                              &gEfiDiskIoProtocolGuid,
                               (VOID **) &DiskIo,
                               This->DriverBindingHandle,
                               ControllerHandle,
@@ -216,13 +212,13 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL
     
     // we were just checking, close it again
     BS->CloseProtocol(ControllerHandle,
-                      &DiskIoProtocol,
+                      &gEfiDiskIoProtocolGuid,
                       This->DriverBindingHandle,
                       ControllerHandle);
     
     // next, check BlockIO without actually opening it
     Status = BS->OpenProtocol(ControllerHandle,
-                              &BlockIoProtocol,
+                              &gEfiBlockIoProtocolGuid,
                               NULL,
                               This->DriverBindingHandle,
                               ControllerHandle,
@@ -258,7 +254,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL  *T
     
     // open consumed protocols
     Status = BS->OpenProtocol(ControllerHandle,
-                              &BlockIoProtocol,
+                              &gEfiBlockIoProtocolGuid,
                               (VOID **) &BlockIo,
                               This->DriverBindingHandle,
                               ControllerHandle,
@@ -269,7 +265,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL  *T
     }
     
     Status = BS->OpenProtocol(ControllerHandle,
-                              &DiskIoProtocol,
+                              &gEfiDiskIoProtocolGuid,
                               (VOID **) &DiskIo,
                               This->DriverBindingHandle,
                               ControllerHandle,
@@ -297,7 +293,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL  *T
         Volume->FileSystem.Revision     = EFI_FILE_IO_INTERFACE_REVISION;
         Volume->FileSystem.OpenVolume   = fsw_efi_FileSystem_OpenVolume;
         Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
-                                                       &FileSystemProtocol, &Volume->FileSystem,
+                                                       &gEfiSimpleFileSystemProtocolGuid, &Volume->FileSystem,
                                                        NULL);
         if (EFI_ERROR(Status))
             Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status);
@@ -310,7 +306,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL  *T
         FreePool(Volume);
         
         BS->CloseProtocol(ControllerHandle,
-                          &DiskIoProtocol,
+                          &gEfiDiskIoProtocolGuid,
                           This->DriverBindingHandle,
                           ControllerHandle);
     }
@@ -343,7 +339,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN  EFI_DRIVER_BINDING_PROTOCOL  *T
     
     // get the installed SimpleFileSystem interface
     Status = BS->OpenProtocol(ControllerHandle,
-                              &FileSystemProtocol,
+                              &gEfiSimpleFileSystemProtocolGuid,
                               (VOID **) &FileSystem,
                               This->DriverBindingHandle,
                               ControllerHandle,
@@ -356,7 +352,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN  EFI_DRIVER_BINDING_PROTOCOL  *T
     
     // uninstall Simple File System protocol
     Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
-                                                     &FileSystemProtocol, &Volume->FileSystem,
+                                                     &gEfiSimpleFileSystemProtocolGuid, &Volume->FileSystem,
                                                      NULL);
     if (EFI_ERROR(Status)) {
         Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status);
@@ -373,7 +369,7 @@ EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN  EFI_DRIVER_BINDING_PROTOCOL  *T
     
     // close the consumed protocols
     Status = BS->CloseProtocol(ControllerHandle,
-                               &DiskIoProtocol,
+                               &gEfiDiskIoProtocolGuid,
                                This->DriverBindingHandle,
                                ControllerHandle);
     
@@ -884,14 +880,14 @@ EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
     UINTN               RequiredSize;
     struct fsw_volume_stat vsb;
     
-    if (CompareGuid(InformationType, &GenericFileInfo) == 0) {
+    if (CompareGuid(InformationType, &gEfiFileInfoGuid)) {
 #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) {
+    } else if (CompareGuid(InformationType, &gEfiFileSystemInfoGuid)) {
 #if DEBUG_LEVEL
         Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n");
 #endif
@@ -922,7 +918,7 @@ EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
         *BufferSize = RequiredSize;
         Status = EFI_SUCCESS;
         
-    } else if (CompareGuid(InformationType, &FileSystemVolumeLabelInfo) == 0) {
+    } else if (CompareGuid(InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
 #if DEBUG_LEVEL
         Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n");
 #endif
diff --git a/OvmfPkg/FswHfsPlus/fsw_efi_base.h b/OvmfPkg/FswHfsPlus/fsw_efi_base.h
index 3643b3b..5884b92 100644
--- a/OvmfPkg/FswHfsPlus/fsw_efi_base.h
+++ b/OvmfPkg/FswHfsPlus/fsw_efi_base.h
@@ -39,8 +39,7 @@
 #define _FSW_EFI_BASE_H_
 
 
-#include <efi.h>
-#include <efilib.h>
+#include "fsw_efi_edk2_base.h"
 
 #define FSW_LITTLE_ENDIAN (1)
 
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 3/6] FswHfsPlus: implement FSW driver for the HFS+ file system
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 2/6] FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies Gabriel L. Somlo
@ 2017-03-07  3:14 ` Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 4/6] EdkCompatibilityPkg: allow ConsoleControl protocol to be used Gabriel L. Somlo
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

Implement read-only HFS+ access methods within the FSW framework.
Based on the HFS+ Volume Format spec (TN1150), available online at
https://developer.apple.com/legacy/library/technotes/tn/tn1150.html

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
 OvmfPkg/FswHfsPlus/FswHfsPlus.inf |  57 ++++
 OvmfPkg/FswHfsPlus/fsw_hfsplus.c  | 535 ++++++++++++++++++++++++++++++++++++++
 OvmfPkg/FswHfsPlus/fsw_hfsplus.h  | 238 +++++++++++++++++
 3 files changed, 830 insertions(+)
 create mode 100644 OvmfPkg/FswHfsPlus/FswHfsPlus.inf
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_hfsplus.c
 create mode 100644 OvmfPkg/FswHfsPlus/fsw_hfsplus.h

diff --git a/OvmfPkg/FswHfsPlus/FswHfsPlus.inf b/OvmfPkg/FswHfsPlus/FswHfsPlus.inf
new file mode 100644
index 0000000..a3581a9
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/FswHfsPlus.inf
@@ -0,0 +1,57 @@
+## @file
+#  File System Wrapper (FSW) based HFS+ driver.
+#
+#  Copyright (C) 2017, Gabriel L. Somlo <gsomlo@gmail.com>
+#
+#  This program and the accompanying materials are licensed and made
+#  available under the terms and conditions of the BSD License which
+#  accompanies this distribution.   The full text of the license may
+#  be found at http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+#  BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+#  EXPRESS OR IMPLIED.
+##
+
+[Defines]
+  INF_VERSION    = 0x00010005
+  BASE_NAME      = FswHfsPlus
+  FILE_GUID      = F474802F-26BB-4E4F-A923-2FC568E94125
+  MODULE_TYPE    = UEFI_DRIVER
+  VERSION_STRING = 1.0
+  ENTRY_POINT    = fsw_efi_main
+
+[Sources]
+  fsw_base.h
+  fsw_efi_base.h
+  fsw_efi_edk2_base.h
+  fsw_strfunc.h
+  fsw_core.h
+  fsw_core.c
+  fsw_efi.h
+  fsw_efi.c
+  fsw_lib.c
+  fsw_efi_lib.c
+  fsw_hfsplus.h
+  fsw_hfsplus.c
+
+[BuildOptions]
+  GCC:*_*_*_CC_FLAGS = -DHOST_EFI -DFSTYPE=hfsplus
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  UefiLib
+  UefiDriverEntryPoint
+
+[Guids]
+  gEfiFileSystemInfoGuid
+  gEfiFileSystemVolumeLabelInfoIdGuid
+  gEfiFileInfoGuid
+
+[Protocols]
+  gEfiBlockIoProtocolGuid
+  gEfiDiskIoProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
diff --git a/OvmfPkg/FswHfsPlus/fsw_hfsplus.c b/OvmfPkg/FswHfsPlus/fsw_hfsplus.c
new file mode 100644
index 0000000..ef17cc9
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_hfsplus.c
@@ -0,0 +1,535 @@
+/** @file
+  HFS+ file system driver.
+
+  Copyright (C) 2017, Gabriel L. Somlo <gsomlo@gmail.com>
+
+  This program and the accompanying materials are licensed and made
+  available under the terms and conditions of the BSD License which
+  accompanies this distribution.   The full text of the license may
+  be found at http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+  BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+  EXPRESS OR IMPLIED.
+**/
+
+#include "fsw_hfsplus.h"
+
+
+/* Given dnode 'd' and offset 'pos' into the data file it represents,
+ * retrieve 'len' bytes of data into buffer 'buf';
+ * Return FSW_SUCCESS or error code.
+ */
+static fsw_status_t
+fsw_hfsplus_read(struct fsw_hfsplus_dnode *d, fsw_u64 pos,
+                 fsw_u32 len, void *buf)
+{
+    struct fsw_shandle sh;
+    fsw_u32            buflen;
+    fsw_status_t       status;
+
+    status = fsw_shandle_open(d, &sh);
+    if (status)
+        return status;
+
+    sh.pos = pos;
+    buflen = len;
+    status = fsw_shandle_read(&sh, &buflen, buf);
+    if (!status && buflen != len)
+        status = FSW_IO_ERROR;
+
+    fsw_shandle_close(&sh);
+    return status;
+}
+
+/* Given the volume being mounted ('v'), and the ID & fork data of the B-Tree
+ * file being set up ('dn_id' and 'f', respectively), populate a cached,
+ * in-memory record of the B-Tree file at the location pointed to by 'btp';
+ * Return FSW_SUCCESS or error code.
+ */
+static fsw_status_t
+fsw_hfsplus_btf_setup(struct fsw_hfsplus_volume *v,
+                      fsw_u32 dn_id, HFSPlusForkData *f,
+                      struct fsw_hfsplus_dnode **btp)
+{
+    BTHeaderRec  hdr_rec;
+    fsw_status_t status;
+
+    status = fsw_dnode_create_root(v, dn_id, btp);
+    if (status)
+        return status;
+
+    (*btp)->g.size = fsw_u64_be_swap(f->logicalSize);
+    fsw_memcpy((*btp)->extents, f->extents, sizeof(HFSPlusExtentRecord));
+
+    // read header record (from node 0, immediately past the node descriptor)
+    status = fsw_hfsplus_read(*btp, sizeof(BTNodeDescriptor),
+                              sizeof(BTHeaderRec), &hdr_rec);
+    if (status)
+        return status;
+
+    // grab root node index and node size from header record
+    (*btp)->bt_root = fsw_u32_be_swap(hdr_rec.rootNode);
+    (*btp)->bt_ndsz = fsw_u16_be_swap(hdr_rec.nodeSize);
+
+    return FSW_SUCCESS;
+}
+
+/* Mount an HFS+ volume. Read volume header (equivalent of superblock),
+ * and set up dnodes for the root folder and B-Tree file(s).
+ * Return FSW_SUCCESS or error code.
+ */
+static fsw_status_t
+fsw_hfsplus_vol_mount(struct fsw_hfsplus_volume *v)
+{
+    void         *buf;
+    fsw_u32      bs;
+    fsw_status_t status;
+
+    // allocate memory for vol. header
+    status = fsw_alloc(sizeof(HFSPlusVolumeHeader), &v->vh);
+    if (status)
+        return status;
+
+    // read vol. header into buffer
+    fsw_set_blocksize(v, kHFSBlockSize, kHFSBlockSize);
+    status = fsw_block_get(v, kMasterDirectoryBlock, 0, &buf);
+    if (status)
+        return status;
+    fsw_memcpy(v->vh, buf, sizeof(HFSPlusVolumeHeader));
+    fsw_block_release(v, kMasterDirectoryBlock, buf);
+
+    // check vol. header
+    if (fsw_u16_be_swap(v->vh->signature) != kHFSPlusSigWord)
+        return FSW_UNSUPPORTED;
+
+    // use block size specified by vol. header
+    bs = fsw_u32_be_swap(v->vh->blockSize);
+    fsw_set_blocksize(v, bs, bs);
+
+    // set up catalog B-Tree file:
+    status = fsw_hfsplus_btf_setup(v, kHFSCatalogFileID, &v->vh->catalogFile,
+                                   &v->catf);
+    if (status)
+        return status;
+
+    // set up root folder:
+    status = fsw_dnode_create_root(v, kHFSRootFolderID, &v->g.root);
+    if (status)
+        return status;
+
+    return FSW_SUCCESS;
+}
+
+/* Free the volume data structure. Called by the core after an unmount or
+ * unsuccessful mount, to release the memory used by the file system type
+ * specific part of the volume structure.
+ */
+static void
+fsw_hfsplus_vol_free(struct fsw_hfsplus_volume *v)
+{
+    if (v->vh)
+        fsw_free(v->vh);
+    if (v->catf)
+        fsw_dnode_release((struct fsw_dnode *)v->catf);
+}
+
+/* Get in-depth information on a volume.
+ */
+static fsw_status_t
+fsw_hfsplus_vol_stat(struct fsw_hfsplus_volume *v, struct fsw_volume_stat *s)
+{
+    // FIXME: not yet supported!
+    return FSW_UNSUPPORTED;
+}
+
+/* Get full information on a dnode from disk. This function is called by
+ * the core whenever it needs to access fields in the dnode structure that
+ * may not be filled immediately upon creation of the dnode.
+ */
+static fsw_status_t
+fsw_hfsplus_dno_fill(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d)
+{
+    // NOTE: not applicable to HFS+ dnodes!
+    return FSW_SUCCESS;
+}
+
+/* Free the dnode data structure. Called by the core when deallocating a dnode
+ * structure to release the memory used by the file system type specific part
+ * of the dnode structure.
+ */
+static void
+fsw_hfsplus_dno_free(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d)
+{
+    // NOTE: not applicable to HFS+ dnodes!
+    return;
+}
+
+/* Get in-depth dnode information. The core ensures fsw_hfsplus_dno_fill()
+ * has been called on the dnode before this function is called. Note that some
+ * data is not directly stored into the structure, but passed to a host-specific
+ * callback that converts it to the host-specific format.
+ */
+static fsw_status_t
+fsw_hfsplus_dno_stat(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode  *d,
+                     struct fsw_dnode_stat *s)
+{
+    // FIXME: not yet supported!
+    return FSW_SUCCESS;
+}
+
+/* Given a B-Tree node pointed to by 'btnode', with node size 'size',
+ * locate the record given by its record number 'rnum';
+ * Return a pointer to the B-Tree key at the beginning of the record.
+ */
+static HFSPlusBTKey *
+fsw_hfsplus_bt_get_rec(BTNodeDescriptor* btnode, fsw_u16 size, fsw_u16 rnum)
+{
+    fsw_u16 *off = (fsw_u16 *)((void *)btnode + size) - 1 - rnum;
+    return (HFSPlusBTKey *)((void *)btnode + fsw_u16_be_swap(*off));
+}
+
+/* Given a B-Tree record pointer 'k', return a pointer to the data
+ * immediately following the key record; IOW, skip the key record which
+ * prefixes the record data payload.
+ */
+static void *
+fsw_hfsplus_bt_rec_skip_key(HFSPlusBTKey *k)
+{
+    return (void *)k + sizeof(k->keyLength) + fsw_u16_be_swap(k->keyLength);
+}
+
+/* Return the child node number immediately following the key record 'k' of
+ * an index node
+ */
+static fsw_u32
+fsw_hfsplus_bt_idx_get_child(HFSPlusBTKey *k)
+{
+    fsw_u32 *child;
+    child = (fsw_u32 *)fsw_hfsplus_bt_rec_skip_key(k);
+    return fsw_u32_be_swap(*child);
+}
+
+/* key comparison procedure type */
+typedef int (*k_cmp_t)(HFSPlusBTKey*, HFSPlusBTKey*);
+
+/* Search an HFS+ special file's B-Tree (given by 'bt'), for a search key
+ * matching 'sk', using comparison procedure 'k_cmp' to determine when a key
+ * match occurs; Fill a caller-provided B-Tree node buffer ('btnode'), and
+ * return a pointer to the matching record data inside 'btnode' via 'data_ptr';
+ * On error, set fsw_status_t return code acoordingly.
+ *
+ * NOTE: A HFS+ volume has a few "special" files, linked directly from the
+ *       volume header. For the purpose of this driver, we mainly care about
+ *       two of them: the "catalog" and "extents" files. All of these files
+ *       are organized as B-Tree structures. This means that, overlaid on
+ *       top of the linear span of each file there is an array of nodes of
+ *       a given size (node_size), internally cross-linked with "pointers"
+ *       to parent/child/sibling nodes, which are essentially the "index"
+ *       (or 'node-number') of the target node in this overlaid array.
+ *       Ultimately, (node-number * node-size) is a byte offset into the
+ *       file, to the location where the referenced node's data begins.
+ *       Each B-Tree file's "dnode" information is available in the HFS+
+ *       volume header. The node at the very beginning of each file (at
+ *       index or node-number == 0) contains a "header node", which provides
+ *       the 'node-number' of the B-Tree's "root" node, as well as the
+ *       'node-size' of all nodes in that B-Tree file.
+ */
+static fsw_status_t
+fsw_hfsplus_bt_search(struct fsw_hfsplus_dnode *bt,
+                      HFSPlusBTKey *sk, k_cmp_t k_cmp,
+                      BTNodeDescriptor *btnode, void **data_ptr)
+{
+    fsw_u32      node;
+    fsw_u16      rec, lo, hi;
+    HFSPlusBTKey *tk;    // trial key
+    int          cmp;
+    fsw_status_t status;
+
+    // start searching from the B-Tree root node:
+    node = bt->bt_root;
+
+    for (;;) {
+        // load data for current node into caller-provided buffer 'btnode'
+        status = fsw_hfsplus_read(bt, (fsw_u64)node * bt->bt_ndsz,
+                                  bt->bt_ndsz, btnode);
+        if (status)
+            return status;
+
+        // sanity check: record 0 located immediately after node descriptor
+        if ((void *)btnode + sizeof(BTNodeDescriptor) !=
+            (void *)fsw_hfsplus_bt_get_rec(btnode, bt->bt_ndsz, 0))
+            return FSW_VOLUME_CORRUPTED;
+
+        // search records within current node
+        lo = 0;
+        hi = fsw_u16_be_swap(btnode->numRecords) - 1;
+        while (lo <= hi) {
+             // access record data, then compare to search key 'sk'
+             rec = (lo + hi) >> 1;
+             tk = fsw_hfsplus_bt_get_rec(btnode, bt->bt_ndsz, rec);
+             cmp = k_cmp(tk, sk);
+
+             if (cmp < 0)      // (tk < sk)
+                 lo = rec + 1;
+             else if (cmp > 0) // (tk > sk)
+                 hi = rec - 1;
+             else {            // (tk == sk)
+                 if (btnode->kind != kBTLeafNode) {
+                     hi = rec;
+                     break;
+                 }
+                 // success: return pointer to data immediately past trial key
+                 *data_ptr = fsw_hfsplus_bt_rec_skip_key(tk);
+                 return FSW_SUCCESS;
+             }
+        }
+
+        // NOTE: following the binary search, 'hi' now points at the
+        //       record with the largest 'tk' for which (tk <= sk)
+
+        if (btnode->kind != kBTIndexNode)
+            break;
+
+        // on an index node, so descend to child
+        tk = fsw_hfsplus_bt_get_rec(btnode, bt->bt_ndsz, hi);
+        node = fsw_hfsplus_bt_idx_get_child(tk);
+    }
+
+    // search key 'sk' not found
+    return FSW_NOT_FOUND;
+}
+
+
+/* Compare unsigned integers 'a' and 'b';
+ * Return -1/0/1 if 'a' is less/equal/greater than 'b'.
+ */
+static int
+fsw_hfsplus_int_cmp(fsw_u32 a, fsw_u32 b)
+{
+    return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+/* Basic latin unicode lowercase
+ */
+static fsw_u16
+fsw_hfsplus_ucblatin_tolower(fsw_u16 c)
+{
+    if (c == 0)
+        return 0xFFFF;
+    if (c == 0x00C6 || c == 0x00D0 || c == 0x00D8 || c == 0x00DE ||
+        (c >= 0x0041 && c <= 0x005A))
+        return c + 0x0020;
+    return c;
+    // FIXME: does edk2 have its own built-in function we could use here?
+}
+
+/* Compare an on-disk catalog B-Tree trial key ('tk') with an in-memory
+ * search key ('sk'). Precedence is parentID, nodeName (keyLength does not
+ * factor into the comparison).
+ * Return -1/0/1 if 'tk'is smaller/equal/larger than 'sk', respectively.
+ * NOTE: all 'tk' fields are stored as big-endian values and must be
+ *       converted to CPU endianness before any comparison to corresponding
+ *       fields in 'sk'.
+ */
+static int
+fsw_hfsplus_cat_cmp(HFSPlusBTKey *tk, HFSPlusBTKey *sk)
+{
+    fsw_u16 *t_str, *s_str;
+    fsw_u16 t_len, s_len;
+    fsw_u16 t_char, s_char;
+    int ret;
+
+    // compare parent IDs: if unequal, we're done!
+    ret = fsw_hfsplus_int_cmp(fsw_u32_be_swap(tk->catKey.parentID),
+                              sk->catKey.parentID);
+    if (ret)
+        return ret;
+
+    // unicode string pointers and lengths:
+    t_len = fsw_u16_be_swap(tk->catKey.nodeName.length);
+    t_str = tk->catKey.nodeName.unicode;
+    s_len = sk->catKey.nodeName.length;
+    s_str = sk->catKey.nodeName.unicode;
+
+    for (;;) {
+        // start by assuming strings are empty:
+        t_char = s_char = 0;
+
+        // find next valid char from on-disk key string:
+        while (t_char == 0 && t_len > 0) {
+            t_char = fsw_hfsplus_ucblatin_tolower(fsw_u16_be_swap(*t_str));
+            t_len--;
+            t_str++;
+        }
+
+        // find next valid char from memory key string:
+        while (s_char == 0 && s_len > 0) {
+            s_char = fsw_hfsplus_ucblatin_tolower(*s_str);
+            s_len--;
+            s_str++;
+        }
+
+        // stop if difference or both strings exhausted:
+        ret = fsw_hfsplus_int_cmp(t_char, s_char);
+        if (ret || s_char == 0)
+            break;
+    }
+
+    return ret;
+}
+
+/* Retrieve file data mapping information. This function is called by
+ * the core when fsw_shandle_read needs to know where on the disk the
+ * required piece of the file's data can be found. The core makes sure
+ * that fsw_hfsplus_dno_fill has been called on the dnode before.
+ * Our task here is to get the physical disk block number for the
+ * requested logical block number.
+ * NOTE: logical and physical block sizes are the same (see mount method).
+ */
+static fsw_status_t
+fsw_hfsplus_get_ext(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+                    struct fsw_extent *e)
+{
+    fsw_u32             off, bc;
+    HFSPlusExtentRecord *er;
+    int                 i;
+
+    // set initial offset to provided starting logical block number:
+    off = e->log_start;
+
+    // start with dnode's initial extent record:
+    er = &d->extents;
+
+    // search extent record:
+    for (i = 0; i < kHFSPlusExtentDensity; i++) {
+        // get block count for current extent descriptor:
+        bc = fsw_u32_be_swap((*er)[i].blockCount);
+
+        // have we exhausted all available extents?
+        if (bc == 0)
+            return FSW_NOT_FOUND;
+
+        // offset is relative to current extent's physical startBlock:
+        if (off < bc) {
+            e->type = FSW_EXTENT_TYPE_PHYSBLOCK;
+            e->phys_start = fsw_u32_be_swap((*er)[i].startBlock) + off;
+            e->log_count = bc - off;
+            return FSW_SUCCESS;
+        }
+
+        // update offset to NEXT extent descriptor:
+        off -= bc;
+    }
+
+    // FIXME: more than 8 fragments not yet supported!
+    return FSW_UNSUPPORTED;
+}
+
+
+/* Lookup a directory's child dnode by name. This function is called on a
+ * directory to retrieve the directory entry with the given name. A dnode
+ * is constructed for this entry and returned. The core makes sure that
+ * fsw_hfsplus_dno_fill has been called and the dnode is actually a directory.
+ */
+static fsw_status_t
+fsw_hfsplus_dir_get(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+                    struct fsw_string *name, struct fsw_hfsplus_dnode **d_out)
+{
+    BTNodeDescriptor     *btnode;
+    fsw_s16              child_rec_type;
+    fsw_u32              child_dno_id, child_dno_type;
+    HFSPlusCatalogKey    k;
+    HFSPlusCatalogRecord *rec;
+    fsw_status_t         status;
+
+    // we only support FSW_STRING_TYPE_UTF16 names:
+    if (name->type != FSW_STRING_TYPE_UTF16 ||
+        name->size > sizeof(k.nodeName.unicode))
+        return FSW_UNSUPPORTED;
+
+    // pre-allocate bt-node buffer for use by search function:
+    status = fsw_alloc(v->catf->bt_ndsz, &btnode);
+    if (status)
+        return status;
+
+    // search catalog file for child named by 'name':
+    k.parentID = d->g.dnode_id;
+    k.nodeName.length = name->len;
+    fsw_memcpy(k.nodeName.unicode, name->data, name->size);
+    // NOTE: keyLength not used in search, setting only for completeness:
+    k.keyLength = sizeof(k.parentID) + sizeof(k.nodeName.length) + name->size;
+    status = fsw_hfsplus_bt_search(v->catf,
+                                   (HFSPlusBTKey *)&k, fsw_hfsplus_cat_cmp,
+                                   btnode, (void **)&rec);
+    if (status)
+        goto done;
+
+    // child record immediately follows the record key data:
+    child_rec_type = fsw_u16_be_swap(rec->recordType);
+    if (child_rec_type == kHFSPlusFolderRecord) {
+        child_dno_id = fsw_u32_be_swap(rec->folderRecord.folderID);
+        child_dno_type = FSW_DNODE_TYPE_DIR;
+    } else if (child_rec_type == kHFSPlusFileRecord) {
+        child_dno_id = fsw_u32_be_swap(rec->fileRecord.fileID);
+        child_dno_type = FSW_DNODE_TYPE_FILE;
+    } else {
+        child_dno_id = 0;
+        child_dno_type = FSW_DNODE_TYPE_UNKNOWN;
+    }
+    status = fsw_dnode_create(d, child_dno_id, child_dno_type, name, d_out);
+    if (status)
+        goto done;
+
+    // if child node is a file, set size and initial extents:
+    if (child_rec_type == kHFSPlusFileRecord) {
+        (*d_out)->g.size =
+            fsw_u64_be_swap(rec->fileRecord.dataFork.logicalSize);
+        fsw_memcpy((*d_out)->extents, &rec->fileRecord.dataFork.extents,
+                   sizeof(HFSPlusExtentRecord));
+    }
+
+done:
+    fsw_free(btnode);
+    return status;
+}
+
+/* Get the next directory entry when reading a directory. This function is
+ * called during directory iteration to retrieve the next directory entry.
+ * A dnode is constructed for the entry and returned. The core makes sure
+ * that fsw_hfsplus_dno_fill has been called and the dnode is actually a
+ * directory. The shandle provided by the caller is used to record the
+ * position in the directory between calls.
+ */
+static fsw_status_t
+fsw_hfsplus_dir_read(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+                     struct fsw_shandle *sh, struct fsw_hfsplus_dnode **d_out)
+{
+    // FIXME: not yet supported!
+    return FSW_UNSUPPORTED;
+}
+
+/* Get the target path of a symbolic link. This function is called when a
+ * symbolic link needs to be resolved. The core makes sure that the
+ * fsw_hfsplus_dno_fill has been called on the dnode and that it really is a
+ * symlink.
+ */
+static fsw_status_t
+fsw_hfsplus_readlink(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d,
+                     struct fsw_string *lnk_tgt)
+{
+    // FIXME: not yet supported!
+    return FSW_UNSUPPORTED;
+}
+
+
+/* HFS+ FSW Method Dispatch Table
+ */
+struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfsplus) = {
+    { FSW_STRING_TYPE_ISO88591, 4, 4, "hfsplus" },
+    sizeof(struct fsw_hfsplus_volume), sizeof(struct fsw_hfsplus_dnode),
+    fsw_hfsplus_vol_mount, fsw_hfsplus_vol_free, fsw_hfsplus_vol_stat,
+    fsw_hfsplus_dno_fill, fsw_hfsplus_dno_free, fsw_hfsplus_dno_stat,
+    fsw_hfsplus_get_ext, fsw_hfsplus_dir_get, fsw_hfsplus_dir_read,
+    fsw_hfsplus_readlink,
+};
diff --git a/OvmfPkg/FswHfsPlus/fsw_hfsplus.h b/OvmfPkg/FswHfsPlus/fsw_hfsplus.h
new file mode 100644
index 0000000..38f4631
--- /dev/null
+++ b/OvmfPkg/FswHfsPlus/fsw_hfsplus.h
@@ -0,0 +1,238 @@
+/** @file
+  HFS+ file system driver header.
+
+  Copyright (C) 2017, Gabriel L. Somlo <gsomlo@gmail.com>
+
+  This program and the accompanying materials are licensed and made
+  available under the terms and conditions of the BSD License which
+  accompanies this distribution.   The full text of the license may
+  be found at http://opensource.org/licenses/bsd-license.php
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"
+  BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER
+  EXPRESS OR IMPLIED.
+**/
+
+#ifndef _FSW_HFSPLUS_H_
+#define _FSW_HFSPLUS_H_
+
+#define VOLSTRUCTNAME fsw_hfsplus_volume
+#define DNODESTRUCTNAME fsw_hfsplus_dnode
+#include "fsw_core.h"
+
+/*============= HFS+ constants and data types from Apple TN1150 =============*/
+#pragma pack(1)
+
+#define kHFSBlockSize            512 // Minimum block size to transfer Vol.Hdr.
+#define kMasterDirectoryBlock      2 // Vol.Hdr. disk offset (x kHFSBlockSize)
+
+#define kHFSPlusSigWord       0x482B // HFS+ volume signature (ASCII for 'H+')
+
+#define kHFSPlusMaxFileNameChars 255 // Max. length of HFS+ folder or filename
+
+#define kHFSPlusExtentDensity      8 // Number of extent descriptors per record
+
+#define kHFSPlusDataFork        0x00 // data fork type
+#define kHFSPlusResourceFork    0xFF // resource fork type
+
+#define kHFSRootFolderID           2 // ID of the root folder
+#define kHFSExtentsFileID          3 // ID of the extent overflow file
+#define kHFSCatalogFileID          4 // ID of the catalog file
+
+#define kHFSPlusFolderRecord       1 // catalog folder record type
+#define kHFSPlusFileRecord         2 // catalog file record type
+
+#define kBTLeafNode               -1 // B-Tree leaf node type
+#define kBTIndexNode               0 // B-Tree index node type
+#define kBTHeaderNode              1 // B-Tree header node type
+
+// file/folder name unicode string type
+typedef struct {
+    fsw_u16 length;                            // character count
+    fsw_u16 unicode[kHFSPlusMaxFileNameChars]; // character string
+} HFSUniStr255;
+
+// extent descriptor type
+typedef struct {
+    fsw_u32 startBlock; // first allocation block in the extent
+    fsw_u32 blockCount; // extent length, in allocation blocks
+} HFSPlusExtentDescriptor;
+
+// extent record type
+typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[kHFSPlusExtentDensity];
+
+// fork-data information type
+typedef struct {
+    fsw_u64             logicalSize; // size of valid fork data, in bytes
+    fsw_u32             clumpSize;   // fork-specific clump size
+    fsw_u32             totalBlocks; // total blocks across all extents in fork
+    HFSPlusExtentRecord extents;     // initial extents for the fork data
+} HFSPlusForkData;
+
+// volume header type
+typedef struct {
+    fsw_u16 signature;          // should be kHFSPlusSigWord
+    fsw_u16 version;            // should be kHFSPlusVersion
+    fsw_u32 attributes;         // volume attributes
+    fsw_u32 lastMountedVersion; // unique ID of software that last wrote volume
+    fsw_u32 journalInfoBlock;   // block no. of journal info
+    fsw_u32 createDate;         // volume creation timestamp (date and time)
+    fsw_u32 modifyDate;         // volume last modification timestamp
+    fsw_u32 backupDate;         // timestamp of last backup
+    fsw_u32 checkedDate;        // timestamp of last consistency check
+    fsw_u32 fileCount;          // total number of files on volume
+    fsw_u32 folderCount;        // total number of folders on volume
+    fsw_u32 blockSize;          // allocation block size, in bytes
+    fsw_u32 totalBlocks;        // total number of allocation blocks
+    fsw_u32 freeBlocks;         // total number of unused allocation blocks
+    fsw_u32 nextAllocation;     // block number to start next allocation search
+    fsw_u32 rsrcClumpSize;      // dflt. resource fork clump size (in bytes)
+    fsw_u32 dataClumpSize;      // dflt. data fork clump size (in bytes)
+    fsw_u32 nextCatalogID;      // next unused catalog ID
+    fsw_u32 writeCount;         // incr. each time volume is write-mounted
+    fsw_u64 encodingsBitmap;    // keep track of text encodings used in names
+    fsw_u8  finderInfo[32];     // information used by the OS X Finder
+    HFSPlusForkData allocationFile; // info re. size & location of alloc. file
+    HFSPlusForkData extentsFile;    // info re. size & location of extents file
+    HFSPlusForkData catalogFile;    // info re. size & location of catalog file
+    HFSPlusForkData attributesFile; // info re. size & location of attr. file
+    HFSPlusForkData startupFile;    // info re. size & location of startup file
+} HFSPlusVolumeHeader;
+
+// file permissions type
+typedef struct {
+    fsw_u32 ownerID;    // owner UID (or hard-link previous-link)
+    fsw_u32 groupID;    // owner GID (or hard-link next-link)
+    fsw_u8  adminFlags; // flags changeable by root only
+    fsw_u8  ownerFlags; // flags changeable by owner
+    fsw_u16 fileMode;   // BSD file type and mode bits
+    union {
+        fsw_u32 iNodeNum;  // link reference number, if hard-link
+        fsw_u32 linkCount; // ref-count of hard-links, if indirect node file
+        fsw_u32 rawDevice; // device number, if block/char special dev. file
+    } special;          // reserved for directories and most files
+} HFSPlusBSDInfo;
+
+// finder info types
+typedef struct {
+    fsw_u8 opaque[16];
+} FndrFileInfo, FndrDirInfo, FndrOpaqueInfo;
+
+// catalog folder record type
+typedef struct {
+    fsw_s16 recordType;         // should be kHFSPlusFolderRecord
+    fsw_u16 flags;              // bit flags about the folder (reserved)
+    fsw_u32 valence;            // items directly contained by this folder
+    fsw_u32 folderID;           // CNID of this folder
+    fsw_u32 createDate;         // folder creation timestamp (date and time)
+    fsw_u32 contentModDate;     // folder content last modification timestamp
+    fsw_u32 attributeModDate;   // ctime (last change to a catalog record field)
+    fsw_u32 accessDate;         // atime (last access timestamp)
+    fsw_u32 backupDate;         // timestamp of last backup
+    HFSPlusBSDInfo permissions; // folder permissions
+    FndrDirInfo userInfo;       // information used by the OS X Finder
+    FndrOpaqueInfo finderInfo;  // additional information for the OS X Finder
+    fsw_u32 textEncoding;       // hint re. folder name text encoding
+    fsw_u32 reserved;           // reserved field
+} HFSPlusCatalogFolder;
+
+// catalog file record type
+typedef struct {
+    fsw_s16 recordType;         // should be kHFSPlusFileRecord
+    fsw_u16 flags;              // bit flags about the file (reserved)
+    fsw_u32 reserved1;          // reserved field
+    fsw_u32 fileID;             // CNID of this file
+    fsw_u32 createDate;         // file creation timestamp (date and time)
+    fsw_u32 contentModDate;     // file content last modification timestamp
+    fsw_u32 attributeModDate;   // ctime (last change to a catalog record field)
+    fsw_u32 accessDate;         // atime (last access timestamp)
+    fsw_u32 backupDate;         // timestamp of last backup
+    HFSPlusBSDInfo permissions; // file permissions
+    FndrFileInfo userInfo;      // information used by the OS X Finder
+    FndrOpaqueInfo finderInfo;  // additional information for the OS X Finder
+    fsw_u32 textEncoding;       // hint re. file name text encoding
+    fsw_u32 reserved2;          // reserved field
+    HFSPlusForkData dataFork;     // info re. size & location of data fork
+    HFSPlusForkData resourceFork; // info re. size & location of resource fork
+} HFSPlusCatalogFile;
+
+// generic (union) catalog record type
+typedef union {
+    fsw_s16              recordType;   // set to kHFSPlus[Folder|File]Record
+    HFSPlusCatalogFolder folderRecord; // catalog folder record fields
+    HFSPlusCatalogFile   fileRecord;   // catalog file record fields
+} HFSPlusCatalogRecord;
+
+// B-Tree node descriptor found at the start of each node
+typedef struct {
+    fsw_u32 fLink;      // number of next node of this type (0 if we're last)
+    fsw_u32 bLink;      // number of prev. node of this type (0 if we're first)
+    fsw_s8  kind;       // node type (leaf, index, header, map)
+    fsw_u8  height;     // node depth in B-Tree hierarchy (0 for header)
+    fsw_u16 numRecords; // number of records contained in this node
+    fsw_u16 reserved;   // reserved field
+} BTNodeDescriptor;
+
+// B-Tree header record type (first record of a B-Tree header node)
+typedef struct {
+    fsw_u16 treeDepth;      // current B-Tree depth (always == rootNode.height)
+    fsw_u32 rootNode;       // node number of B-Tree root node
+    fsw_u32 leafRecords;    // total number of records across all leaf nodes
+    fsw_u32 firstLeafNode;  // node number of first leaf node
+    fsw_u32 lastLeafNode;   // node number of last leaf node
+    fsw_u16 nodeSize;       // node size (in bytes)
+    fsw_u16 maxKeyLength;   // max. length of a key in index/leaf node
+    fsw_u32 totalNodes;     // total number of nodes in the B-Tree
+    fsw_u32 freeNodes;      // number of unused nodes in the B-Tree
+    fsw_u16 reserved1;      // reserved field
+    fsw_u32 clumpSize;      // reserved field (deprecated)
+    fsw_u8  btreeType;      // reserved (0 for catalog, extents, attrib. file)
+    fsw_u8  keyCompareType; // case-sensitive string comparison (HFSX only)
+    fsw_u32 attributes;     // B-Tree attributes
+    fsw_u32 reserved3[16];  // reserved field
+} BTHeaderRec;
+
+// extent overflow file key type
+typedef struct {
+    fsw_u16 keyLength;  // key length (excluding this field)
+    fsw_u8  forkType;   // data or resource fork
+    fsw_u8  pad;        // ensure 32-bit alignment for subsequent fields
+    fsw_u32 fileID;     // CNID of file to which this extent record applies
+    fsw_u32 startBlock; // start block of first extent described by this record
+} HFSPlusExtentKey;
+
+// catalog file key type
+typedef struct {
+    fsw_u16      keyLength; // key length (excluding this field)
+    fsw_u32      parentID;  // ID of parent folder (or CNID if thread record)
+    HFSUniStr255 nodeName;  // basename of file or folder
+} HFSPlusCatalogKey;
+
+// generic (union) B-Tree record key type
+typedef union {
+    fsw_u16           keyLength; // key length (excluding this field)
+    HFSPlusExtentKey  extKey;    // extent key fields
+    HFSPlusCatalogKey catKey;    // catalog key fields
+} HFSPlusBTKey;
+
+#pragma pack()
+/*========= end HFS+ constants and data types from Apple TN1150 =============*/
+
+
+// FSW: HFS+ specific dnode
+struct fsw_hfsplus_dnode {
+    struct fsw_dnode g;             // Generic (parent) dnode structure
+    HFSPlusExtentRecord extents;    // HFS+ initial extent record
+    fsw_u32 bt_root;                // root node index (if B-Tree file)
+    fsw_u16 bt_ndsz;                // node size (if B-Tree file)
+};
+
+
+// FSW: HFS+ specific volume
+struct fsw_hfsplus_volume {
+    struct fsw_volume g;            // Generic (parent) volume structure
+    HFSPlusVolumeHeader *vh;        // Raw HFS+ Volume Header
+    struct fsw_hfsplus_dnode *catf; // Catalog file dnode
+};
+
+#endif // _FSW_HFSPLUS_H_
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 4/6] EdkCompatibilityPkg: allow ConsoleControl protocol to be used
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
                   ` (2 preceding siblings ...)
  2017-03-07  3:14 ` [RFC PATCH 3/6] FswHfsPlus: implement FSW driver for the HFS+ file system Gabriel L. Somlo
@ 2017-03-07  3:14 ` Gabriel L. Somlo
  2017-03-07  3:14 ` [RFC PATCH 5/6] OvmfPkg: add Apple boot support Gabriel L. Somlo
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

Apple's bootloader requires the ConsoleControl protocol to be
implemented. By adding the Foundation path to the package description,
the ConsoleProtocol can be used in Ovmf without the need of copying it
to Include/Protocol from MdePkg.

The gEfiConsoleControlProtocolGuid is added to the .dec file, to be
consumed by Ovmf.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
 EdkCompatibilityPkg/EdkCompatibilityPkg.dec | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/EdkCompatibilityPkg/EdkCompatibilityPkg.dec b/EdkCompatibilityPkg/EdkCompatibilityPkg.dec
index 1fd1251..0a9225d 100644
--- a/EdkCompatibilityPkg/EdkCompatibilityPkg.dec
+++ b/EdkCompatibilityPkg/EdkCompatibilityPkg.dec
@@ -25,6 +25,7 @@
   
 [Includes]
   Compatibility/Include
+  Foundation
 
 [LibraryClasses]
   ##  @libraryclass  Provides functions for language conversion between
@@ -44,6 +45,7 @@
 [Protocols]
   gEfiPrintProtocolGuid                   = { 0xdf2d868e, 0x32fc, 0x4cf0, {0x8e, 0x6b, 0xff, 0xd9, 0x5d, 0x13, 0x43, 0xd0} }
   gEfiSmmBaseHelperReadyProtocolGuid      = { 0x910dca07, 0x1f94, 0x4ee7, { 0xaf, 0x2f, 0xff, 0x72, 0xf3, 0x15, 0x43, 0x53 } }
+  gEfiConsoleControlProtocolGuid          = { 0xf42f7782, 0x012e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x04, 0xf7, 0x21 }}
 
 [PcdsDynamic, PcdsDynamicEx]
   gEfiEdkCompatibilityPkgTokenSpaceGuid.BootScriptThunkDataPtr|0x0|UINT64|0x30000001
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 5/6] OvmfPkg: add Apple boot support
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
                   ` (3 preceding siblings ...)
  2017-03-07  3:14 ` [RFC PATCH 4/6] EdkCompatibilityPkg: allow ConsoleControl protocol to be used Gabriel L. Somlo
@ 2017-03-07  3:14 ` Gabriel L. Somlo
  2017-03-07 16:14   ` Laszlo Ersek
  2017-03-07  3:14 ` [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware Gabriel L. Somlo
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

Apple's boot.efi checks if the ConsoleControl protocol returns
EFI_SUCCESS in both GetMode and SetMode.

Apple uses a kernel extension to make parts of the DataHub protocol
available to the xnu kernel. The xnu kernel then checks for FSB
frequency and if it's not found fails with:

"tsc_init: EFI not supported!"
https://github.com/opensource-apple/xnu/blob/10.9/osfmk/i386/tsc.c

Also, some firmware settings are added to AppleSupport and written
to nvram.

Lastly, use (already present) HFS+ filesystem driver to locate, load,
and launch the Apple-provided OS X bootloader.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
 OvmfPkg/Include/Library/AppleSupportLib.h          |  28 ++++
 OvmfPkg/Library/AppleSupportLib/AppleSupport.c     | 107 +++++++++++++++
 .../Library/AppleSupportLib/AppleSupportLib.inf    |  50 +++++++
 OvmfPkg/Library/AppleSupportLib/Bds.c              | 151 +++++++++++++++++++++
 OvmfPkg/Library/AppleSupportLib/Bds.h              |  21 +++
 OvmfPkg/Library/AppleSupportLib/Common.h           |  24 ++++
 OvmfPkg/Library/AppleSupportLib/Console.c          |  86 ++++++++++++
 OvmfPkg/Library/AppleSupportLib/Console.h          |  28 ++++
 OvmfPkg/Library/AppleSupportLib/Datahub.c          | 104 ++++++++++++++
 OvmfPkg/Library/AppleSupportLib/Datahub.h          |  32 +++++
 10 files changed, 631 insertions(+)
 create mode 100644 OvmfPkg/Include/Library/AppleSupportLib.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupport.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Common.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.h
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.c
 create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.h

diff --git a/OvmfPkg/Include/Library/AppleSupportLib.h b/OvmfPkg/Include/Library/AppleSupportLib.h
new file mode 100644
index 0000000..583d06b
--- /dev/null
+++ b/OvmfPkg/Include/Library/AppleSupportLib.h
@@ -0,0 +1,28 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef _APPLESUPPORT_LIB_INCLUDED_
+#define _APPLESUPPORT_LIB_INCLUDED_
+
+EFI_STATUS
+EFIAPI
+InitializeAppleSupport (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  );
+
+EFI_STATUS
+BdsBootApple ();
+
+#endif
diff --git a/OvmfPkg/Library/AppleSupportLib/AppleSupport.c b/OvmfPkg/Library/AppleSupportLib/AppleSupport.c
new file mode 100644
index 0000000..92f7d79
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/AppleSupport.c
@@ -0,0 +1,107 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include "Console.h"
+#include "Datahub.h"
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <FrameworkDxe.h>
+#include <Guid/DataHubRecords.h>
+#include <Protocol/DataHub.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+
+EFI_GUID gAppleFirmwareVariableGuid = {
+    0x4D1EDE05, 0x38C7, 0x4A6A, {0x9C, 0xC6, 0x4B, 0xCC, 0xA8, 0xB3, 0x8C, 0x14 }
+};
+
+/**
+  Register Handler for the specified interrupt source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+  @param Handler  Callback for interrupt. NULL to unregister
+
+  @retval  EFI_SUCCESS            The firmware has successfully stored the variable and its data as
+  defined by the Attributes.
+  @retval  EFI_INVALID_PARAMETER  An invalid combination of attribute bits was supplied, or the
+  DataSize exceeds the maximum allowed.
+  @retval  EFI_INVALID_PARAMETER  VariableName is an empty Unicode string.
+  @retval  EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
+  @retval  EFI_DEVICE_ERROR       The variable could not be saved due to a hardware failure.
+  @retval  EFI_WRITE_PROTECTED    The variable in question is read-only.
+  @retval  EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
+  @retval  EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
+  set but the AuthInfo does NOT pass the validation check carried
+  out by the firmware.
+
+**/
+EFI_STATUS
+InitializeFirmware ()
+{
+  EFI_STATUS          Status;
+
+  UINT32              BackgroundClear = 0x00000000;
+  UINT32              FwFeatures      = 0x80000015;
+  UINT32              FwFeaturesMask  = 0x800003ff;
+
+  Status = gRT->SetVariable(L"BackgroundClear",
+                            &gAppleFirmwareVariableGuid,
+                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                            sizeof(BackgroundClear), &BackgroundClear);
+
+  Status = gRT->SetVariable(L"FirmwareFeatures",
+                            &gAppleFirmwareVariableGuid,
+                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                            sizeof(FwFeatures), &FwFeatures);
+
+  Status = gRT->SetVariable(L"FirmwareFeaturesMask",
+                            &gAppleFirmwareVariableGuid,
+                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                            sizeof(FwFeaturesMask), &FwFeaturesMask);
+
+  return Status;
+}
+
+/**
+  Register driver.
+
+  @param  ImageHandle   of the loaded driver
+  @param  SystemTable   Pointer to the System Table
+
+  @retval EFI_SUCCESS   Driver registered
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeAppleSupport (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS                         Status;
+
+  Status = InitializeConsoleControl(ImageHandle);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = InitializeDatahub(ImageHandle);
+  ASSERT_EFI_ERROR(Status);
+
+  Status = InitializeFirmware();
+  ASSERT_EFI_ERROR(Status);
+
+  return Status;
+}
diff --git a/OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf b/OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
new file mode 100644
index 0000000..6caf29d
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
@@ -0,0 +1,50 @@
+#/* @file
+#  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+#
+#  This program and the accompanying materials #  are licensed and made
+#  available under the terms and conditions of the BSD License which
+#  accompanies this distribution. The full text of the license may be
+#  found at http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#*/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = AppleSupportLib
+  FILE_GUID                      = CEC880AF-68DF-4CDF-BBA5-FF9B202382AB
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = AppleSupportLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  AppleSupport.c
+  Console.c
+  Datahub.c
+  Bds.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  IntelFrameworkPkg/IntelFrameworkPkg.dec
+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
+  EdkCompatibilityPkg/EdkCompatibilityPkg.dec
+
+[LibraryClasses]
+  UefiBootServicesTableLib
+  BaseLib
+  DebugLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiConsoleControlProtocolGuid
+  gEfiDataHubProtocolGuid
diff --git a/OvmfPkg/Library/AppleSupportLib/Bds.c b/OvmfPkg/Library/AppleSupportLib/Bds.c
new file mode 100644
index 0000000..88f7437
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Bds.c
@@ -0,0 +1,151 @@
+#include "Common.h"
+#include "Bds.h"
+
+#include <PiDxe.h>
+
+#include <Library/DevicePathLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/GenericBdsLib.h>
+#include <Protocol/SimpleFileSystem.h>
+
+#include <Guid/FileInfo.h>
+
+EFI_STATUS
+BdsFileSystemLoadImage (
+  IN     EFI_HANDLE Handle,
+  IN     EFI_ALLOCATE_TYPE     Type,
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,
+  OUT    UINTN                 *ImageSize
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
+  EFI_FILE_PROTOCOL               *Fs;
+  EFI_FILE_INFO                   *FileInfo;
+  EFI_FILE_PROTOCOL               *File;
+  UINTN                           Size;
+
+  /* FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath; */
+
+  Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Try to Open the volume and get root directory
+  Status = FsProtocol->OpenVolume (FsProtocol, &Fs);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  File = NULL;
+  Status = Fs->Open (Fs, &File, EFI_CORESERVICES, EFI_FILE_MODE_READ, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Size = 0;
+  File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);
+  FileInfo = AllocatePool (Size);
+  Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Get the file size
+  Size = FileInfo->FileSize;
+  if (ImageSize) {
+    *ImageSize = Size;
+  }
+  FreePool (FileInfo);
+
+  Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
+  // Try to allocate in any pages if failed to allocate memory at the defined location
+  if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
+    Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
+  }
+  if (!EFI_ERROR (Status)) {
+    Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));
+  }
+
+  return Status;
+}
+
+
+/**
+  Start an EFI Application from a Device Path
+
+  @param  ParentImageHandle     Handle of the calling image
+  @param  DevicePath            Location of the EFI Application
+
+  @retval EFI_SUCCESS           All drivers have been connected
+  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
+  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store the matching results.
+
+**/
+EFI_STATUS
+BdsStartEfiApplication (
+  IN EFI_HANDLE                  Handle,
+  IN EFI_HANDLE                  ParentImageHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_HANDLE                   ImageHandle;
+  EFI_PHYSICAL_ADDRESS         BinaryBuffer;
+  UINTN                        BinarySize;
+
+  // Find the nearest supported file loader
+  Status = BdsFileSystemLoadImage (Handle, AllocateAnyPages, &BinaryBuffer, &BinarySize);
+  if (EFI_ERROR (Status)) {
+    DEBUG((EFI_D_INFO, "== Bds could not load System image =="));
+    return Status;
+  }
+
+  // Load the image from the Buffer with Boot Services function
+  Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Before calling the image, enable the Watchdog Timer for  the 5 Minute period
+  gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
+  // Start the image
+  Status = gBS->StartImage (ImageHandle, NULL, NULL);
+  // Clear the Watchdog Timer after the image returns
+  gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
+
+  return Status;
+}
+
+
+EFI_STATUS
+BdsBootApple ()
+{
+  UINTN                           Index;
+  EFI_DEVICE_PATH_PROTOCOL        *TempDevicePath;
+  EFI_HANDLE                      *SimpleFileSystemHandles;
+  UINTN                           NumberSimpleFileSystemHandles;
+
+  gBS->LocateHandleBuffer (
+      ByProtocol,
+      &gEfiSimpleFileSystemProtocolGuid,
+      NULL,
+      &NumberSimpleFileSystemHandles,
+      &SimpleFileSystemHandles
+      );
+  DEBUG((EFI_D_INFO, "Number Device File System: %d\n", NumberSimpleFileSystemHandles));
+  for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
+    //
+    // Get the device path size of SimpleFileSystem handle
+    //
+    TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
+    BdsStartEfiApplication (
+      SimpleFileSystemHandles[Index],
+      gImageHandle,
+      TempDevicePath
+      );
+
+  }
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Library/AppleSupportLib/Bds.h b/OvmfPkg/Library/AppleSupportLib/Bds.h
new file mode 100644
index 0000000..725d03b
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Bds.h
@@ -0,0 +1,21 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+
+#ifndef _APPLESUPPORT_BDS_H_INCLUDED_
+#define _APPLESUPPORT_BDS_H_INCLUDED_
+
+#define EFI_CORESERVICES L"System\\Library\\CoreServices\\boot.efi"
+
+#endif
diff --git a/OvmfPkg/Library/AppleSupportLib/Common.h b/OvmfPkg/Library/AppleSupportLib/Common.h
new file mode 100644
index 0000000..725f2af
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Common.h
@@ -0,0 +1,24 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+
+#ifndef _APPLESUPPORT_COMMON_H_INCLUDED_
+#define _APPLESUPPORT_COMMON_H_INCLUDED_
+
+#include <Uefi.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#endif
diff --git a/OvmfPkg/Library/AppleSupportLib/Console.c b/OvmfPkg/Library/AppleSupportLib/Console.c
new file mode 100644
index 0000000..5f23300
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Console.c
@@ -0,0 +1,86 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include "Console.h"
+
+EFI_STATUS EFIAPI
+GetModeImpl(
+    IN  EFI_CONSOLE_CONTROL_PROTOCOL      *This,
+    OUT EFI_CONSOLE_CONTROL_SCREEN_MODE   *Mode,
+    OUT BOOLEAN                           *GopUgaExists,  OPTIONAL
+    OUT BOOLEAN                           *StdInLocked    OPTIONAL
+    )
+{
+  *Mode = EfiConsoleControlScreenGraphics;
+
+  if (GopUgaExists)
+    *GopUgaExists = TRUE;
+  if (StdInLocked)
+    *StdInLocked = FALSE;
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI
+SetModeImpl(
+    IN  EFI_CONSOLE_CONTROL_PROTOCOL      *This,
+    IN  EFI_CONSOLE_CONTROL_SCREEN_MODE   Mode
+    )
+{
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI
+LockStdInImpl(
+    IN  EFI_CONSOLE_CONTROL_PROTOCOL      *This,
+    IN CHAR16                             *Password
+    )
+{
+  return EFI_SUCCESS;
+}
+
+
+
+EFI_CONSOLE_CONTROL_PROTOCOL gConsoleController =
+{
+  GetModeImpl,
+  SetModeImpl,
+};
+
+/**
+  Install ConsoleControl protocol, which is needed for Apple's
+  boot.efi
+
+  @param  ImageHandle   of the loaded driver
+
+  @retval EFI_SUCCESS       Successfully installed protocol handler
+  @retval EFI_DEVICE_ERROR  ConsoleProtocol could not be installed
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeConsoleControl (
+  IN EFI_HANDLE         ImageHandle
+  )
+{
+  EFI_STATUS                         Status;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+      &ImageHandle,
+      &gEfiConsoleControlProtocolGuid,
+      &gConsoleController,
+      NULL
+      );
+
+  return Status;
+}
diff --git a/OvmfPkg/Library/AppleSupportLib/Console.h b/OvmfPkg/Library/AppleSupportLib/Console.h
new file mode 100644
index 0000000..dabf902
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Console.h
@@ -0,0 +1,28 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef _APPLESUPPORT_CONSOLE_H_INCLUDED_
+#define _APPLESUPPORT_CONSOLE_H_INCLUDED_
+
+#include "Common.h"
+
+#include <Protocol/ConsoleControl/ConsoleControl.h>
+
+EFI_STATUS
+EFIAPI
+InitializeConsoleControl (
+  IN EFI_HANDLE         ImageHandle
+  );
+
+#endif
diff --git a/OvmfPkg/Library/AppleSupportLib/Datahub.c b/OvmfPkg/Library/AppleSupportLib/Datahub.c
new file mode 100644
index 0000000..3f1ec56
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Datahub.c
@@ -0,0 +1,104 @@
+/** @file
+*
+*  Copyright (c) 2011, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include "Datahub.h"
+
+EFI_GUID gAppleSystemInfoProducerNameGuid = {0x64517CC8, 0x6561, 0x4051, {0xB0, 0x3C, 0x59, 0x64, 0xB6, 0x0F, 0x4C, 0x7A}};
+EFI_GUID gAppleFsbFrequencyPropertyGuid = {0xD1A04D55, 0x75B9, 0x41A3, {0x90, 0x36, 0x8F, 0x4A, 0x26, 0x1C, 0xBB, 0xA2}};
+EFI_GUID gAppleDevicePathsSupportedGuid = {0x5BB91CF7, 0xD816, 0x404B, {0x86, 0x72, 0x68, 0xF2, 0x7F, 0x78, 0x31, 0xDC}};
+
+typedef struct {
+  UINT32              DataNameSize;
+  UINT32              DataSize;
+} EFI_PROPERTY_SUBCLASS_RECORD;
+
+typedef struct {
+  EFI_SUBCLASS_TYPE1_HEADER   Header;
+  EFI_PROPERTY_SUBCLASS_RECORD  Record;
+} EFI_PROPERTY_SUBCLASS_DATA;
+
+
+EFI_STATUS
+SetEfiPlatformProperty (
+    IN EFI_DATA_HUB_PROTOCOL *DataHub,
+    IN CONST EFI_STRING Name,
+    EFI_GUID EfiPropertyGuid,
+    VOID *Data,
+    UINT32 DataSize
+    )
+{
+  EFI_STATUS Status;
+  UINT32 DataNameSize;
+  EFI_PROPERTY_SUBCLASS_DATA *DataRecord;
+
+  DataNameSize = (UINT32)StrSize(Name);
+
+  DataRecord = AllocateZeroPool (sizeof (EFI_PROPERTY_SUBCLASS_DATA) + DataNameSize + DataSize);
+  ASSERT (DataRecord != NULL);
+
+  DataRecord->Header.Version = EFI_DATA_RECORD_HEADER_VERSION;
+  DataRecord->Header.HeaderSize = sizeof(EFI_SUBCLASS_TYPE1_HEADER);
+  DataRecord->Header.Instance = 0xFFFF;
+  DataRecord->Header.SubInstance = 0xFFFF;
+  DataRecord->Header.RecordType = 0xFFFFFFFF;
+  DataRecord->Record.DataNameSize = DataNameSize;
+  DataRecord->Record.DataSize = DataSize;
+
+
+  CopyMem((UINT8 *)DataRecord + sizeof(EFI_PROPERTY_SUBCLASS_DATA), Name, DataNameSize);
+  CopyMem((UINT8 *)DataRecord + sizeof(EFI_PROPERTY_SUBCLASS_DATA) + DataNameSize, Data, DataSize);
+
+  Status = DataHub->LogData(DataHub, &EfiPropertyGuid, &gAppleSystemInfoProducerNameGuid,
+                             EFI_DATA_RECORD_CLASS_DATA,
+                             DataRecord,
+                             sizeof(EFI_PROPERTY_SUBCLASS_DATA) + DataNameSize + DataSize);
+
+  if (DataRecord) {
+    gBS->FreePool(DataRecord);
+  }
+
+  return Status;
+}
+
+/**
+  Initialize the DataHub protocols data for the xnu kernel to
+  detect an EFI boot.
+
+  @param  ImageHandle   of the loaded driver
+
+  @retval EFI_SUCCESS Source was updated to support Handler.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeDatahub (
+  IN EFI_HANDLE         ImageHandle
+  )
+{
+  EFI_STATUS                         Status;
+  EFI_DATA_HUB_PROTOCOL              *DataHub;
+
+  Status = gBS->LocateProtocol(&gEfiDataHubProtocolGuid, NULL, (VOID **)&DataHub);
+  ASSERT_EFI_ERROR(Status);
+
+  UINT64 FsbFrequency = 667000000;
+  UINT32 DevicePathsSupported = 1;
+
+  SetEfiPlatformProperty(DataHub, L"FSBFrequency", gAppleFsbFrequencyPropertyGuid, &FsbFrequency, sizeof(UINT64));
+  SetEfiPlatformProperty(DataHub, L"DevicePathsSupported", gAppleDevicePathsSupportedGuid, &DevicePathsSupported, sizeof(UINT32));
+  ASSERT_EFI_ERROR(Status);
+
+  return Status;
+}
diff --git a/OvmfPkg/Library/AppleSupportLib/Datahub.h b/OvmfPkg/Library/AppleSupportLib/Datahub.h
new file mode 100644
index 0000000..d7f8806
--- /dev/null
+++ b/OvmfPkg/Library/AppleSupportLib/Datahub.h
@@ -0,0 +1,32 @@
+/** @file
+*
+*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef _APPLESUPPORT_DATAHUB_H_INCLUDED_
+#define _APPLESUPPORT_DATAHUB_H_INCLUDED_
+
+#include "Common.h"
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <FrameworkDxe.h>
+#include <Guid/DataHubRecords.h>
+#include <Protocol/DataHub.h>
+
+EFI_STATUS
+EFIAPI
+InitializeDatahub (
+  IN EFI_HANDLE         ImageHandle
+  );
+
+#endif
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
                   ` (4 preceding siblings ...)
  2017-03-07  3:14 ` [RFC PATCH 5/6] OvmfPkg: add Apple boot support Gabriel L. Somlo
@ 2017-03-07  3:14 ` Gabriel L. Somlo
  2017-03-07 16:21   ` Laszlo Ersek
  2017-03-07 15:41 ` [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Laszlo Ersek
  2017-03-29 23:22 ` Phil Dennis-Jordan
  7 siblings, 1 reply; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-07  3:14 UTC (permalink / raw)
  To: edk2-devel; +Cc: lersek, jordan.l.justen, reza.jelveh, agraf, kraxel

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
---
 OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c           | 10 ++++++++++
 OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h           |  1 +
 .../Library/PlatformBootManagerLib/PlatformBootManagerLib.inf  |  1 +
 OvmfPkg/OvmfPkgIa32.dsc                                        |  8 ++++++++
 OvmfPkg/OvmfPkgIa32.fdf                                        |  3 +++
 OvmfPkg/OvmfPkgIa32X64.dsc                                     |  8 ++++++++
 OvmfPkg/OvmfPkgIa32X64.fdf                                     |  3 +++
 OvmfPkg/OvmfPkgX64.dsc                                         |  8 ++++++++
 OvmfPkg/OvmfPkgX64.fdf                                         |  3 +++
 9 files changed, 45 insertions(+)

diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
index cc35630..9f6be90 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
+++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
@@ -381,6 +381,11 @@ Returns:
   }
 
   //
+  // Initialize AppleSupport library
+  //
+  InitializeAppleSupport (gImageHandle, gST);
+
+  //
   // Prevent further changes to LockBoxes or SMRAM.
   //
   Handle = NULL;
@@ -1474,6 +1479,11 @@ Routine Description:
 
   RemoveStaleFvFileOptions ();
   SetBootOrderFromQemu ();
+
+  //
+  // Locate and launch Apple's OS X bootloader
+  //
+  BdsBootApple ();
 }
 
 /**
diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h
index ec58efa..3fa7712 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h
+++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h
@@ -49,6 +49,7 @@ Abstract:
 #include <Library/NvVarsFileLib.h>
 #include <Library/QemuFwCfgLib.h>
 #include <Library/QemuBootOrderLib.h>
+#include <Library/AppleSupportLib.h>
 
 #include <Protocol/Decompress.h>
 #include <Protocol/PciIo.h>
diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
index f9e35c9..66d31a5 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -40,6 +40,7 @@
   OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
+  AppleSupportLib
   BaseLib
   MemoryAllocationLib
   UefiBootServicesTableLib
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 0bce56b..da83cba 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -176,6 +176,8 @@
   OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
   XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf
 
+  AppleSupportLib|OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
+
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
 
@@ -696,6 +698,12 @@
   MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
   #
+  # Apple Support
+  #
+  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
+  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
+
+  #
   # Network Support
   #
   MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index 09c1658..0e00bd9 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -280,6 +280,9 @@ INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
+INF  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
+INF  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
+
 INF  FatPkg/EnhancedFatDxe/Fat.inf
 
 !ifndef $(USE_OLD_SHELL)
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 56f7ff9..a38dc9b 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -181,6 +181,8 @@
   OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
   XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf
 
+  AppleSupportLib|OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
+
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
 
@@ -705,6 +707,12 @@
   MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
   #
+  # Apple Support
+  #
+  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
+  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
+
+  #
   # Network Support
   #
   MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 5233314..2bd2d77 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -280,6 +280,9 @@ INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
+INF  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
+INF  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
+
 INF  FatPkg/EnhancedFatDxe/Fat.inf
 
 !ifndef $(USE_OLD_SHELL)
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index d0b0b0e..2bff68d 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -181,6 +181,8 @@
   OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
   XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf
 
+  AppleSupportLib|OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
+
 [LibraryClasses.common]
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
 
@@ -703,6 +705,12 @@
   MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
   #
+  # Apple Support
+  #
+  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
+  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
+
+  #
   # Network Support
   #
   MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 3615010..0165a63 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -280,6 +280,9 @@ INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
 INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
 
+INF  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
+INF  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
+
 INF  FatPkg/EnhancedFatDxe/Fat.inf
 
 !ifndef $(USE_OLD_SHELL)
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot)
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
                   ` (5 preceding siblings ...)
  2017-03-07  3:14 ` [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware Gabriel L. Somlo
@ 2017-03-07 15:41 ` Laszlo Ersek
  2017-03-29 23:22 ` Phil Dennis-Jordan
  7 siblings, 0 replies; 12+ messages in thread
From: Laszlo Ersek @ 2017-03-07 15:41 UTC (permalink / raw)
  To: Gabriel L. Somlo, edk2-devel; +Cc: jordan.l.justen, reza.jelveh, agraf, kraxel

On 03/07/17 04:14, Gabriel L. Somlo wrote:
> This series is intended to restart the conversation re. adding
> boot support for Mac OS X to QEMU via OVMF.

I have no way to test this, nor any interest to use it, so on the
community level, my only role here (for OvmfPkg) is to recommend a way
in which this (quite specific) feature can be accommodated without a
high risk of regressions and/or complexity increase. From reading the
commit messages, it looks like the OSX boot flow doesn't conform to UEFI
2.x, so any damage this feature does to OVMF (even in human
understanding) should be contained.

(1) Please introduce a new build flag, such as -D APPLE_BOOT_ENABLE or
-D OSX_BOOT_ENABLE, with a FALSE default value.

This build option should control:

(1a) whether FswHfsPlus and DataHubDxe are included in the DSC/FDF,

(1b) the value of a new FeaturePCD (also to be introduced). For the
name, I suggest gUefiOvmfPkgTokenSpaceGuid.PcdAppleBootEnable, or
...PcdOsxBootEnable.

In turn, the feature PCD should control code that's being added to
modules that we unconditionally build into OVMF. The prime examples for
this are the InitializeAppleSupport() and BdsBootApple() function calls
in PlatformBootManagerLib.

(1c) how the AppleSupportLib class is resolved. If the PCD / build flag
are false (the default), the lib class should be resolved to a Null
instance.

The point is, introducing a dependency on EdkCompatibilityPkg is
*incredibly* ugly, and if OVMF is built with the build flag disabled
(the default), I don't want that dependency to exist even at build time.
Hence the Null library instance, with no functionality and no dependencies.

(2) Please feed all the patches to:

  python BaseTools/Scripts/PatchCheck.py

because a number of coding style problems can be spotted. I'll comment
on those individually, as I notice them, but PatchCheck.py should help
in general.

Also please double-check if the new files are all CRLF terminated (I
didn't check this, just making sure).

(3) With the recent feature divergence in OvmfPkg (see also Xen PVH!),
it is high time we introduced maintainers / reviewers on the module (or
even file) level.

This would be a first for edk2, but in the mid-term, I'd strongly like
to assign Xen patch review and OSX boot patch review to dedicated
reviewers. No review from those reviewers --> no commit. The new
reviewers would not (immediately?) get push access, but their review
on-list would be of primary importance for pushing any patches.

Jordan, can you please help with figuring out the necessary format for
Maintainers.txt? If OSX boot has to be the feature that pioneers this,
so be it. In fact, such a detailed directory of maintainers would have
been useful for MdePkg and MdeModulePkg for years.

> The *last* three patches represent what's left of Reza's excellent
> work on getting OVMF to support booting OS X. They are rebased to
> the point where they still apply cleanly and work, but I haven't
> made any significant changes to them beyond that.
> 
> The *first* three patches (1-3 of 6) provide the BSD-licensed HFS+
> filesystem support required by Reza's work:

This is a filesystem driver which is not closely related to OVMF. It
should not go under OvmfPkg, but under a new top level package. I think
someone (maybe Jordan?) recommended FileSystemPkg/ earlier. Of course,
this would need a new entry in Maintainers.txt as well, and then, for
uniformity, FatPkg should be moved there as well.

> 
>     - patch 1/6: After a bit of archaeology, I managed to isolate
>                  the original EFI "File System Wrapper" (FSW) files
>                  released under the BSD by the "rEFIt" project; One
>                  additional file, also BSD-licensed, was imported
>                  from the "rEFInd" project.
>                  The FSW is an abstraction layer that facilitates
>                  the creation of a file system driver by providing
>                  a set methods for mount/unmount/readdir/etc.

So, technically speaking, since I've just suggested moving the driver
from under OvmfPkg to FileSystemPkg/, I no longer have any jurisdiction
over the driver. I will comment nonetheless, like any other contributor:

I disagree with the inclusion of any such external wrapper. In my mind
this is similar to how the Linux project rejects vendor-specific middle
layers. It might make sense for the rEFIt project to abstract the UEFI
interfaces even further, but having those one-off abstractions within
the edk2 tree does not make sense, in my opinion. Please code the driver
directly against UEFI interfaces. The reasoning is the same as the Linux
argument: anyone else looking at this code will expect to have to deal
with UEFI-level abstractions, and the filesystem spec, not another
(external) project's abstractions.

And, similarly to the AMD middle layer thing for Linux, the middle layer
being introduced here (in patch #1) is huge: the diffstat lists 3784
lines (the diffstat for the full series is 5287 lines). Please let's not
do this.

Again, this is just my opinion.

> 
>     - patch 2/6: fix a few compiler errors and other discrepancies to
>                  facilitate integrating FSW into EDK2/OVMF.
> 
>     - patch 3/6: A BSD-licensed implementation of an FSW HFS+ driver.
>                  Based on Apple's HFS+ specification (TN1150), this is
>                  a minimalistic, bare-bones set of FSW methods capable
>                  of locating and loading files from an HFS+ volume.
>                  Lots of functionality (e.g. stat, readdir, hard/sym
>                  links, or accessing files with more than 8 fragments)
>                  is unimplemented at this time.

This ties in nicely with my above argument. "stat", "readdir",
"hardlinks" and "symlinks" are concepts outside of the UEFI
specification, that is, outside of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and
EFI_FILE_PROTOCOL.

These primitives might make sense for rEFIt, but they make no sense for
a filesystem driver residing within the edk2 tree.

> I only implemented the
>                  methods necessary to support loading Apple's boot.efi
>                  and kernel.
> 
>                  For any extra features (such as stat, readdir, etc.)
>                  I'd need to figure out how to navigate around on a
>                  mounted volume (e.g. from the UEFI shell ?) to cause
>                  the rest of the methods to be called, so I could test
>                  whether they work or not. Particularly support for
>                  fragmented files -- I'd have to create one somehow
>                  (against OS X and HFS's best efforts to prevent it),
>                  then cause it to be accessed from the UEFI shell to
>                  test that whatever I'm implementing actually works.
>                  Any "EDK2 filesystem developer's primer" on how to
>                  do those things would be immensely helpful at this point.

If you implement a pure EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and
EFI_FILE_PROTOCOL (with Revision==EFI_FILE_PROTOCOL_REVISION, that is,
not revision 2), you will see, from the UEFI spec, the member functions:
- Open(),
- Close(),
- Delete(),
- Read(),
- Write(),
- SetPosition(),
- GetPosition(),
- GetInfo(),
- SetInfo(),
- Flush().

Since this is (I assume?) a read-only driver, you can return
EFI_ACCESS_DENIED or EFI_WRITE_PROTECTED from Write(), SetInfo(), and
Flush(), as appropriate. Delete() should return EFI_WARN_DELETE_FAILURE.
Open() and Read() can be trivially tested in the UEFI shell (copy a file
to another, writeable (fat32) filesystem). SetPosition() and
GetPosition() can likely be implemented trivially. GetInfo() can be
tested with directory listing commands in the shell (dir, ls, AFAIR).

You can see a synthetic, read-only filesystem implementation in
"ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c".

> 
> Also, I would like some feedback and advice on where to go from here.
> 
> At this time, neither the FSW wrapper nor the HFS+ driver I wrote comply
> with EDK2's coding standards. Before I start working on that, it would
> be helpful to find out whether the FSW layer is of any interest to EDK2
> as a way to facilitate adding support for arbitrary new filesystems.

Ah, it's great that you ask this question explicitly! Please see my
answer above.

Regardless, there are some coding standard issues with the OvmfPkg
patches too (see my point (2) above); I shall comment on those elsewhere.

Thanks!
Laszlo

> 
> If not, it might be worth factoring out the common bits and roll the
> the whole thing up into a standalone HFS+ driver, which would be a
> significantly different direction to go.
> 
> The series is also available on GitHub, in my "macboot" branch:
> 
> 	https://github.com/gsomlo/edk2/tree/macboot
> 
> A set of instructions on how to get things working is available here:
> 
> 	http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/
> 
> Thanks in advance for any feedback, advice, etc.
>   --Gabriel
> 
> Gabriel L. Somlo (6):
>   FswHfsPlus: add File System Wrapper (FSW) interface code
>   FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies
>   FswHfsPlus: implement FSW driver for the HFS+ file system
>   EdkCompatibilityPkg: allow ConsoleControl protocol to be used
>   OvmfPkg: add Apple boot support
>   OvmfPkg: enable AppleSupport library for Ovmf firmware
> 
>  EdkCompatibilityPkg/EdkCompatibilityPkg.dec        |    2 +
>  OvmfPkg/FswHfsPlus/FswHfsPlus.inf                  |   57 ++
>  OvmfPkg/FswHfsPlus/fsw_base.h                      |  158 +++
>  OvmfPkg/FswHfsPlus/fsw_core.c                      |  929 +++++++++++++++++
>  OvmfPkg/FswHfsPlus/fsw_core.h                      |  517 ++++++++++
>  OvmfPkg/FswHfsPlus/fsw_efi.c                       | 1040 ++++++++++++++++++++
>  OvmfPkg/FswHfsPlus/fsw_efi.h                       |  102 ++
>  OvmfPkg/FswHfsPlus/fsw_efi_base.h                  |   81 ++
>  OvmfPkg/FswHfsPlus/fsw_efi_edk2_base.h             |   76 ++
>  OvmfPkg/FswHfsPlus/fsw_efi_lib.c                   |  129 +++
>  OvmfPkg/FswHfsPlus/fsw_hfsplus.c                   |  535 ++++++++++
>  OvmfPkg/FswHfsPlus/fsw_hfsplus.h                   |  238 +++++
>  OvmfPkg/FswHfsPlus/fsw_lib.c                       |  294 ++++++
>  OvmfPkg/FswHfsPlus/fsw_strfunc.h                   |  453 +++++++++
>  OvmfPkg/Include/Library/AppleSupportLib.h          |   28 +
>  OvmfPkg/Library/AppleSupportLib/AppleSupport.c     |  107 ++
>  .../Library/AppleSupportLib/AppleSupportLib.inf    |   50 +
>  OvmfPkg/Library/AppleSupportLib/Bds.c              |  151 +++
>  OvmfPkg/Library/AppleSupportLib/Bds.h              |   21 +
>  OvmfPkg/Library/AppleSupportLib/Common.h           |   24 +
>  OvmfPkg/Library/AppleSupportLib/Console.c          |   86 ++
>  OvmfPkg/Library/AppleSupportLib/Console.h          |   28 +
>  OvmfPkg/Library/AppleSupportLib/Datahub.c          |  104 ++
>  OvmfPkg/Library/AppleSupportLib/Datahub.h          |   32 +
>  .../Library/PlatformBootManagerLib/BdsPlatform.c   |   10 +
>  .../Library/PlatformBootManagerLib/BdsPlatform.h   |    1 +
>  .../PlatformBootManagerLib.inf                     |    1 +
>  OvmfPkg/OvmfPkgIa32.dsc                            |    8 +
>  OvmfPkg/OvmfPkgIa32.fdf                            |    3 +
>  OvmfPkg/OvmfPkgIa32X64.dsc                         |    8 +
>  OvmfPkg/OvmfPkgIa32X64.fdf                         |    3 +
>  OvmfPkg/OvmfPkgX64.dsc                             |    8 +
>  OvmfPkg/OvmfPkgX64.fdf                             |    3 +
>  33 files changed, 5287 insertions(+)
>  create mode 100644 OvmfPkg/FswHfsPlus/FswHfsPlus.inf
>  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_hfsplus.c
>  create mode 100644 OvmfPkg/FswHfsPlus/fsw_hfsplus.h
>  create mode 100644 OvmfPkg/FswHfsPlus/fsw_lib.c
>  create mode 100644 OvmfPkg/FswHfsPlus/fsw_strfunc.h
>  create mode 100644 OvmfPkg/Include/Library/AppleSupportLib.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupport.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Common.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.h
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 5/6] OvmfPkg: add Apple boot support
  2017-03-07  3:14 ` [RFC PATCH 5/6] OvmfPkg: add Apple boot support Gabriel L. Somlo
@ 2017-03-07 16:14   ` Laszlo Ersek
  0 siblings, 0 replies; 12+ messages in thread
From: Laszlo Ersek @ 2017-03-07 16:14 UTC (permalink / raw)
  To: Gabriel L. Somlo, edk2-devel; +Cc: jordan.l.justen, reza.jelveh, agraf, kraxel

On 03/07/17 04:14, Gabriel L. Somlo wrote:
> Apple's boot.efi checks if the ConsoleControl protocol returns
> EFI_SUCCESS in both GetMode and SetMode.
> 
> Apple uses a kernel extension to make parts of the DataHub protocol
> available to the xnu kernel. The xnu kernel then checks for FSB
> frequency and if it's not found fails with:
> 
> "tsc_init: EFI not supported!"
> https://github.com/opensource-apple/xnu/blob/10.9/osfmk/i386/tsc.c
> 
> Also, some firmware settings are added to AppleSupport and written
> to nvram.
> 
> Lastly, use (already present) HFS+ filesystem driver to locate, load,
> and launch the Apple-provided OS X bootloader.

Why can't the Apple-provided OS X boot loader be simply launched through
a normal Boot#### option?

More comments below (staying at the level that I announced in my
response to your blurb):

> 
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
> ---
>  OvmfPkg/Include/Library/AppleSupportLib.h          |  28 ++++
>  OvmfPkg/Library/AppleSupportLib/AppleSupport.c     | 107 +++++++++++++++
>  .../Library/AppleSupportLib/AppleSupportLib.inf    |  50 +++++++
>  OvmfPkg/Library/AppleSupportLib/Bds.c              | 151 +++++++++++++++++++++
>  OvmfPkg/Library/AppleSupportLib/Bds.h              |  21 +++
>  OvmfPkg/Library/AppleSupportLib/Common.h           |  24 ++++
>  OvmfPkg/Library/AppleSupportLib/Console.c          |  86 ++++++++++++
>  OvmfPkg/Library/AppleSupportLib/Console.h          |  28 ++++
>  OvmfPkg/Library/AppleSupportLib/Datahub.c          | 104 ++++++++++++++
>  OvmfPkg/Library/AppleSupportLib/Datahub.h          |  32 +++++
>  10 files changed, 631 insertions(+)
>  create mode 100644 OvmfPkg/Include/Library/AppleSupportLib.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupport.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Bds.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Common.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Console.h
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.c
>  create mode 100644 OvmfPkg/Library/AppleSupportLib/Datahub.h
> 
> diff --git a/OvmfPkg/Include/Library/AppleSupportLib.h b/OvmfPkg/Include/Library/AppleSupportLib.h
> new file mode 100644
> index 0000000..583d06b
> --- /dev/null
> +++ b/OvmfPkg/Include/Library/AppleSupportLib.h
> @@ -0,0 +1,28 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at

This line (and a lot of other lines) are too long. Please stick with a
line length of 80 if possible, across the series.

> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +#ifndef _APPLESUPPORT_LIB_INCLUDED_
> +#define _APPLESUPPORT_LIB_INCLUDED_
> +
> +EFI_STATUS
> +EFIAPI
> +InitializeAppleSupport (
> +  IN EFI_HANDLE         ImageHandle,
> +  IN EFI_SYSTEM_TABLE   *SystemTable
> +  );
> +
> +EFI_STATUS
> +BdsBootApple ();

Public library interfaces are required to be EFIAPI.

> +
> +#endif

This library class header uses EFI-level types, such as EFI_STATUS,
EFI_HANDLE, and EFI_SYSTEM_TABLE. If I'm right that implies that the
MODULE_TYPE, for any of the library instances (== the INF files) cannot
be BASE. I think you need MODULE_TYPE=DXE_DRIVER.


> diff --git a/OvmfPkg/Library/AppleSupportLib/AppleSupport.c b/OvmfPkg/Library/AppleSupportLib/AppleSupport.c
> new file mode 100644
> index 0000000..92f7d79
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/AppleSupport.c
> @@ -0,0 +1,107 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +#include "Console.h"
> +#include "Datahub.h"
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <FrameworkDxe.h>
> +#include <Guid/DataHubRecords.h>
> +#include <Protocol/DataHub.h>
> +#include <Library/UefiRuntimeServicesTableLib.h>

Please keep the includes sorted, at least by category. Also,
module-specific includes should go to the bottom.

> +
> +
> +EFI_GUID gAppleFirmwareVariableGuid = {
> +    0x4D1EDE05, 0x38C7, 0x4A6A, {0x9C, 0xC6, 0x4B, 0xCC, 0xA8, 0xB3, 0x8C, 0x14 }
> +};

GUID definitions belong under OvmfPkg/Include/Guid, and to
OvmfPkg/OvmfPkg.dec. Please refer to the examples seen in those places
for the pattern to follow.


> +
> +/**
> +  Register Handler for the specified interrupt source.
> +
> +  @param This     Instance pointer for this protocol
> +  @param Source   Hardware source of the interrupt
> +  @param Handler  Callback for interrupt. NULL to unregister

@param should be annotated with [in] / [out] / [in,out].

> +
> +  @retval  EFI_SUCCESS            The firmware has successfully stored the variable and its data as
> +  defined by the Attributes.

Wrapped line should be aligned to the right column.

> +  @retval  EFI_INVALID_PARAMETER  An invalid combination of attribute bits was supplied, or the
> +  DataSize exceeds the maximum allowed.

Ditto.

> +  @retval  EFI_INVALID_PARAMETER  VariableName is an empty Unicode string.
> +  @retval  EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
> +  @retval  EFI_DEVICE_ERROR       The variable could not be saved due to a hardware failure.
> +  @retval  EFI_WRITE_PROTECTED    The variable in question is read-only.
> +  @retval  EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
> +  @retval  EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
> +  set but the AuthInfo does NOT pass the validation check carried
> +  out by the firmware.

Lines too long etc.

> +
> +**/
> +EFI_STATUS
> +InitializeFirmware ()
> +{
> +  EFI_STATUS          Status;
> +
> +  UINT32              BackgroundClear = 0x00000000;
> +  UINT32              FwFeatures      = 0x80000015;
> +  UINT32              FwFeaturesMask  = 0x800003ff;
> +
> +  Status = gRT->SetVariable(L"BackgroundClear",
> +                            &gAppleFirmwareVariableGuid,
> +                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
> +                            sizeof(BackgroundClear), &BackgroundClear);

Please consult the coding style how to align the second and further
lines -- they should be indented one or (preferably) two characters
relative to the word "SetVariable".

Also, Status is ignored, ...

> +
> +  Status = gRT->SetVariable(L"FirmwareFeatures",
> +                            &gAppleFirmwareVariableGuid,
> +                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
> +                            sizeof(FwFeatures), &FwFeatures);
> +
> +  Status = gRT->SetVariable(L"FirmwareFeaturesMask",
> +                            &gAppleFirmwareVariableGuid,
> +                            EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
> +                            sizeof(FwFeaturesMask), &FwFeaturesMask);
> +
> +  return Status;
> +}
> +
> +/**
> +  Register driver.
> +
> +  @param  ImageHandle   of the loaded driver
> +  @param  SystemTable   Pointer to the System Table
> +
> +  @retval EFI_SUCCESS   Driver registered
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeAppleSupport (
> +  IN EFI_HANDLE         ImageHandle,
> +  IN EFI_SYSTEM_TABLE   *SystemTable
> +  )
> +{
> +  EFI_STATUS                         Status;
> +
> +  Status = InitializeConsoleControl(ImageHandle);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = InitializeDatahub(ImageHandle);
> +  ASSERT_EFI_ERROR(Status);
> +
> +  Status = InitializeFirmware();
> +  ASSERT_EFI_ERROR(Status);
> +
> +  return Status;
> +}

If these functions fail:
- do we really want to hang the boot process?
- can we roll them back as appropriate, and just return an error?

> diff --git a/OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf b/OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
> new file mode 100644
> index 0000000..6caf29d
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
> @@ -0,0 +1,50 @@
> +#/* @file

Missing note about the purpose of this library instance.

> +#  Copyright (c) 2014, Reza Jelveh. All rights reserved.

Given that these are new files (which you'll likely modify), I think
your (C) should be here as well.

> +#
> +#  This program and the accompanying materials #  are licensed and made
> +#  available under the terms and conditions of the BSD License which
> +#  accompanies this distribution. The full text of the license may be
> +#  found at http://opensource.org/licenses/bsd-license.php
> +#
> +#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +#
> +#*/
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010005

Please use the most recent version of the INF spec here. (The INF spec
documents the INF_VERSION define.)

> +  BASE_NAME                      = AppleSupportLib
> +  FILE_GUID                      = CEC880AF-68DF-4CDF-BBA5-FF9B202382AB
> +  MODULE_TYPE                    = BASE

Shouldn't be BASE, pls. see above.

> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = AppleSupportLib
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64
> +#
> +
> +[Sources]
> +  AppleSupport.c
> +  Console.c
> +  Datahub.c
> +  Bds.c
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  OvmfPkg/OvmfPkg.dec
> +  IntelFrameworkPkg/IntelFrameworkPkg.dec
> +  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
> +  EdkCompatibilityPkg/EdkCompatibilityPkg.dec
> +
> +[LibraryClasses]
> +  UefiBootServicesTableLib
> +  BaseLib
> +  DebugLib
> +  MemoryAllocationLib
> +  BaseMemoryLib
> +
> +[Protocols]
> +  gEfiConsoleControlProtocolGuid
> +  gEfiDataHubProtocolGuid
> diff --git a/OvmfPkg/Library/AppleSupportLib/Bds.c b/OvmfPkg/Library/AppleSupportLib/Bds.c
> new file mode 100644
> index 0000000..88f7437
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Bds.c
> @@ -0,0 +1,151 @@
> +#include "Common.h"
> +#include "Bds.h"
> +
> +#include <PiDxe.h>
> +
> +#include <Library/DevicePathLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/GenericBdsLib.h>
> +#include <Protocol/SimpleFileSystem.h>
> +
> +#include <Guid/FileInfo.h>
> +
> +EFI_STATUS
> +BdsFileSystemLoadImage (
> +  IN     EFI_HANDLE Handle,
> +  IN     EFI_ALLOCATE_TYPE     Type,
> +  IN OUT EFI_PHYSICAL_ADDRESS* Image,
> +  OUT    UINTN                 *ImageSize
> +  )
> +{
> +  EFI_STATUS                      Status;
> +  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
> +  EFI_FILE_PROTOCOL               *Fs;
> +  EFI_FILE_INFO                   *FileInfo;
> +  EFI_FILE_PROTOCOL               *File;
> +  UINTN                           Size;
> +
> +  /* FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath; */

Dead code, please remove this.

> +
> +  Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // Try to Open the volume and get root directory

The comment format is:

//
// comment
//

> +  Status = FsProtocol->OpenVolume (FsProtocol, &Fs);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  File = NULL;
> +  Status = Fs->Open (Fs, &File, EFI_CORESERVICES, EFI_FILE_MODE_READ, 0);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }

Resource leak, the volume has not been closed. (But, again, I'm not sure
why normal Boot#### options are not suitable for loading whatever is
being loaded here.)

> +
> +  Size = 0;
> +  File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);
> +  FileInfo = AllocatePool (Size);
> +  Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }

More resource leaks. I know you didn't write this code, but can we
please at least *pretend* to not leak resources left and right on error?

> +
> +  // Get the file size
> +  Size = FileInfo->FileSize;
> +  if (ImageSize) {
> +    *ImageSize = Size;
> +  }
> +  FreePool (FileInfo);
> +
> +  Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
> +  // Try to allocate in any pages if failed to allocate memory at the defined location
> +  if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
> +    Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
> +  }
> +  if (!EFI_ERROR (Status)) {
> +    Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));
> +  }
> +
> +  return Status;

Leaks resources even on success. Splendid.

Sorry if I'm sarcastic, but this has nothing to do with UEFI or
firmware; releasing resources is just plain C.

Again, I know you didn't write this, but I shouldn't be the *first*
person actually reading this code and gasping.


> +}
> +
> +
> +/**
> +  Start an EFI Application from a Device Path
> +
> +  @param  ParentImageHandle     Handle of the calling image
> +  @param  DevicePath            Location of the EFI Application
> +
> +  @retval EFI_SUCCESS           All drivers have been connected
> +  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
> +  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store the matching results.
> +
> +**/
> +EFI_STATUS
> +BdsStartEfiApplication (
> +  IN EFI_HANDLE                  Handle,
> +  IN EFI_HANDLE                  ParentImageHandle,
> +  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath
> +  )
> +{
> +  EFI_STATUS                   Status;
> +  EFI_HANDLE                   ImageHandle;
> +  EFI_PHYSICAL_ADDRESS         BinaryBuffer;
> +  UINTN                        BinarySize;
> +
> +  // Find the nearest supported file loader
> +  Status = BdsFileSystemLoadImage (Handle, AllocateAnyPages, &BinaryBuffer, &BinarySize);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG((EFI_D_INFO, "== Bds could not load System image =="));

New code should use DEBUG_INFO, not EFI_D_INFO (same for all other
similar constants).

Additionally, the coding style requires a space between DEBUG and "((".

> +    return Status;
> +  }
> +
> +  // Load the image from the Buffer with Boot Services function
> +  Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  // Before calling the image, enable the Watchdog Timer for  the 5 Minute period
> +  gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
> +  // Start the image
> +  Status = gBS->StartImage (ImageHandle, NULL, NULL);
> +  // Clear the Watchdog Timer after the image returns
> +  gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);

So this appears to come from the boot manager requirements in the UEFI
spec, but I still don't understand why we can't just boot this thing via
plain Boot#### / BootOrder.

> +
> +  return Status;
> +}
> +
> +
> +EFI_STATUS
> +BdsBootApple ()
> +{
> +  UINTN                           Index;
> +  EFI_DEVICE_PATH_PROTOCOL        *TempDevicePath;
> +  EFI_HANDLE                      *SimpleFileSystemHandles;
> +  UINTN                           NumberSimpleFileSystemHandles;
> +
> +  gBS->LocateHandleBuffer (
> +      ByProtocol,
> +      &gEfiSimpleFileSystemProtocolGuid,
> +      NULL,
> +      &NumberSimpleFileSystemHandles,
> +      &SimpleFileSystemHandles
> +      );

Return code not checked.

I'm sorry, I got exhausted by now. There are so many issues even on the
surface that I don't know what to comment on.

I recommend the following approach:

- distill all the information necessary for booting OSX into a design /
requirements document. Make it a TechNotes.txt somewhere, or a detailed
commit message. In particular, painstakingly list where the OSX boot
process diverges from the normal UEFI boot process, please.

- implement the functionality from the bottom up, adhering to the coding
standard at every step. Doing three iterations just to clean up the
surface (without any consideration for the functionality) doesn't look
fun. PatchCheck.py should help with most of the warts.

Again, I don't intend to verify the functionality of this code, but to
some extent I do feel responsible for ascertaining the superficial
quality of this code -- not for myself, but for the next poor bloke that
will have to look at it.

Even though my main goal here is the perfect isolation of this feature
from the default build, in order to keep regressions out, and that
implies I shouldn't dig into the code very deeply, there seem to be so
many issues with the basics that I couldn't, with a clear conscience,
say *only*: "okay, after isolating this, please dump this into a
separate directory, and take onwership of it". I do wish for you to own
this in OVMF, but it needs a lot of code improvement even for that.

Thanks
Laszlo

> +  DEBUG((EFI_D_INFO, "Number Device File System: %d\n", NumberSimpleFileSystemHandles));
> +  for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
> +    //
> +    // Get the device path size of SimpleFileSystem handle
> +    //
> +    TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
> +    BdsStartEfiApplication (
> +      SimpleFileSystemHandles[Index],
> +      gImageHandle,
> +      TempDevicePath
> +      );
> +
> +  }
> +  return EFI_SUCCESS;
> +}
> diff --git a/OvmfPkg/Library/AppleSupportLib/Bds.h b/OvmfPkg/Library/AppleSupportLib/Bds.h
> new file mode 100644
> index 0000000..725d03b
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Bds.h
> @@ -0,0 +1,21 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +
> +#ifndef _APPLESUPPORT_BDS_H_INCLUDED_
> +#define _APPLESUPPORT_BDS_H_INCLUDED_
> +
> +#define EFI_CORESERVICES L"System\\Library\\CoreServices\\boot.efi"
> +
> +#endif
> diff --git a/OvmfPkg/Library/AppleSupportLib/Common.h b/OvmfPkg/Library/AppleSupportLib/Common.h
> new file mode 100644
> index 0000000..725f2af
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Common.h
> @@ -0,0 +1,24 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +
> +#ifndef _APPLESUPPORT_COMMON_H_INCLUDED_
> +#define _APPLESUPPORT_COMMON_H_INCLUDED_
> +
> +#include <Uefi.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +
> +#endif
> diff --git a/OvmfPkg/Library/AppleSupportLib/Console.c b/OvmfPkg/Library/AppleSupportLib/Console.c
> new file mode 100644
> index 0000000..5f23300
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Console.c
> @@ -0,0 +1,86 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +#include "Console.h"
> +
> +EFI_STATUS EFIAPI
> +GetModeImpl(
> +    IN  EFI_CONSOLE_CONTROL_PROTOCOL      *This,
> +    OUT EFI_CONSOLE_CONTROL_SCREEN_MODE   *Mode,
> +    OUT BOOLEAN                           *GopUgaExists,  OPTIONAL
> +    OUT BOOLEAN                           *StdInLocked    OPTIONAL
> +    )
> +{
> +  *Mode = EfiConsoleControlScreenGraphics;
> +
> +  if (GopUgaExists)
> +    *GopUgaExists = TRUE;
> +  if (StdInLocked)
> +    *StdInLocked = FALSE;
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS EFIAPI
> +SetModeImpl(
> +    IN  EFI_CONSOLE_CONTROL_PROTOCOL      *This,
> +    IN  EFI_CONSOLE_CONTROL_SCREEN_MODE   Mode
> +    )
> +{
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS EFIAPI
> +LockStdInImpl(
> +    IN  EFI_CONSOLE_CONTROL_PROTOCOL      *This,
> +    IN CHAR16                             *Password
> +    )
> +{
> +  return EFI_SUCCESS;
> +}
> +
> +
> +
> +EFI_CONSOLE_CONTROL_PROTOCOL gConsoleController =
> +{
> +  GetModeImpl,
> +  SetModeImpl,
> +};
> +
> +/**
> +  Install ConsoleControl protocol, which is needed for Apple's
> +  boot.efi
> +
> +  @param  ImageHandle   of the loaded driver
> +
> +  @retval EFI_SUCCESS       Successfully installed protocol handler
> +  @retval EFI_DEVICE_ERROR  ConsoleProtocol could not be installed
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeConsoleControl (
> +  IN EFI_HANDLE         ImageHandle
> +  )
> +{
> +  EFI_STATUS                         Status;
> +
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +      &ImageHandle,
> +      &gEfiConsoleControlProtocolGuid,
> +      &gConsoleController,
> +      NULL
> +      );
> +
> +  return Status;
> +}
> diff --git a/OvmfPkg/Library/AppleSupportLib/Console.h b/OvmfPkg/Library/AppleSupportLib/Console.h
> new file mode 100644
> index 0000000..dabf902
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Console.h
> @@ -0,0 +1,28 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +#ifndef _APPLESUPPORT_CONSOLE_H_INCLUDED_
> +#define _APPLESUPPORT_CONSOLE_H_INCLUDED_
> +
> +#include "Common.h"
> +
> +#include <Protocol/ConsoleControl/ConsoleControl.h>
> +
> +EFI_STATUS
> +EFIAPI
> +InitializeConsoleControl (
> +  IN EFI_HANDLE         ImageHandle
> +  );
> +
> +#endif
> diff --git a/OvmfPkg/Library/AppleSupportLib/Datahub.c b/OvmfPkg/Library/AppleSupportLib/Datahub.c
> new file mode 100644
> index 0000000..3f1ec56
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Datahub.c
> @@ -0,0 +1,104 @@
> +/** @file
> +*
> +*  Copyright (c) 2011, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +#include "Datahub.h"
> +
> +EFI_GUID gAppleSystemInfoProducerNameGuid = {0x64517CC8, 0x6561, 0x4051, {0xB0, 0x3C, 0x59, 0x64, 0xB6, 0x0F, 0x4C, 0x7A}};
> +EFI_GUID gAppleFsbFrequencyPropertyGuid = {0xD1A04D55, 0x75B9, 0x41A3, {0x90, 0x36, 0x8F, 0x4A, 0x26, 0x1C, 0xBB, 0xA2}};
> +EFI_GUID gAppleDevicePathsSupportedGuid = {0x5BB91CF7, 0xD816, 0x404B, {0x86, 0x72, 0x68, 0xF2, 0x7F, 0x78, 0x31, 0xDC}};
> +
> +typedef struct {
> +  UINT32              DataNameSize;
> +  UINT32              DataSize;
> +} EFI_PROPERTY_SUBCLASS_RECORD;
> +
> +typedef struct {
> +  EFI_SUBCLASS_TYPE1_HEADER   Header;
> +  EFI_PROPERTY_SUBCLASS_RECORD  Record;
> +} EFI_PROPERTY_SUBCLASS_DATA;
> +
> +
> +EFI_STATUS
> +SetEfiPlatformProperty (
> +    IN EFI_DATA_HUB_PROTOCOL *DataHub,
> +    IN CONST EFI_STRING Name,
> +    EFI_GUID EfiPropertyGuid,
> +    VOID *Data,
> +    UINT32 DataSize
> +    )
> +{
> +  EFI_STATUS Status;
> +  UINT32 DataNameSize;
> +  EFI_PROPERTY_SUBCLASS_DATA *DataRecord;
> +
> +  DataNameSize = (UINT32)StrSize(Name);
> +
> +  DataRecord = AllocateZeroPool (sizeof (EFI_PROPERTY_SUBCLASS_DATA) + DataNameSize + DataSize);
> +  ASSERT (DataRecord != NULL);
> +
> +  DataRecord->Header.Version = EFI_DATA_RECORD_HEADER_VERSION;
> +  DataRecord->Header.HeaderSize = sizeof(EFI_SUBCLASS_TYPE1_HEADER);
> +  DataRecord->Header.Instance = 0xFFFF;
> +  DataRecord->Header.SubInstance = 0xFFFF;
> +  DataRecord->Header.RecordType = 0xFFFFFFFF;
> +  DataRecord->Record.DataNameSize = DataNameSize;
> +  DataRecord->Record.DataSize = DataSize;
> +
> +
> +  CopyMem((UINT8 *)DataRecord + sizeof(EFI_PROPERTY_SUBCLASS_DATA), Name, DataNameSize);
> +  CopyMem((UINT8 *)DataRecord + sizeof(EFI_PROPERTY_SUBCLASS_DATA) + DataNameSize, Data, DataSize);
> +
> +  Status = DataHub->LogData(DataHub, &EfiPropertyGuid, &gAppleSystemInfoProducerNameGuid,
> +                             EFI_DATA_RECORD_CLASS_DATA,
> +                             DataRecord,
> +                             sizeof(EFI_PROPERTY_SUBCLASS_DATA) + DataNameSize + DataSize);
> +
> +  if (DataRecord) {
> +    gBS->FreePool(DataRecord);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Initialize the DataHub protocols data for the xnu kernel to
> +  detect an EFI boot.
> +
> +  @param  ImageHandle   of the loaded driver
> +
> +  @retval EFI_SUCCESS Source was updated to support Handler.
> +  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeDatahub (
> +  IN EFI_HANDLE         ImageHandle
> +  )
> +{
> +  EFI_STATUS                         Status;
> +  EFI_DATA_HUB_PROTOCOL              *DataHub;
> +
> +  Status = gBS->LocateProtocol(&gEfiDataHubProtocolGuid, NULL, (VOID **)&DataHub);
> +  ASSERT_EFI_ERROR(Status);
> +
> +  UINT64 FsbFrequency = 667000000;
> +  UINT32 DevicePathsSupported = 1;
> +
> +  SetEfiPlatformProperty(DataHub, L"FSBFrequency", gAppleFsbFrequencyPropertyGuid, &FsbFrequency, sizeof(UINT64));
> +  SetEfiPlatformProperty(DataHub, L"DevicePathsSupported", gAppleDevicePathsSupportedGuid, &DevicePathsSupported, sizeof(UINT32));
> +  ASSERT_EFI_ERROR(Status);
> +
> +  return Status;
> +}
> diff --git a/OvmfPkg/Library/AppleSupportLib/Datahub.h b/OvmfPkg/Library/AppleSupportLib/Datahub.h
> new file mode 100644
> index 0000000..d7f8806
> --- /dev/null
> +++ b/OvmfPkg/Library/AppleSupportLib/Datahub.h
> @@ -0,0 +1,32 @@
> +/** @file
> +*
> +*  Copyright (c) 2014, Reza Jelveh. All rights reserved.
> +*
> +*  This program and the accompanying materials
> +*  are licensed and made available under the terms and conditions of the BSD License
> +*  which accompanies this distribution.  The full text of the license may be found at
> +*  http://opensource.org/licenses/bsd-license.php
> +*
> +*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
> +*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
> +*
> +**/
> +
> +#ifndef _APPLESUPPORT_DATAHUB_H_INCLUDED_
> +#define _APPLESUPPORT_DATAHUB_H_INCLUDED_
> +
> +#include "Common.h"
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <FrameworkDxe.h>
> +#include <Guid/DataHubRecords.h>
> +#include <Protocol/DataHub.h>
> +
> +EFI_STATUS
> +EFIAPI
> +InitializeDatahub (
> +  IN EFI_HANDLE         ImageHandle
> +  );
> +
> +#endif
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware
  2017-03-07  3:14 ` [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware Gabriel L. Somlo
@ 2017-03-07 16:21   ` Laszlo Ersek
  0 siblings, 0 replies; 12+ messages in thread
From: Laszlo Ersek @ 2017-03-07 16:21 UTC (permalink / raw)
  To: Gabriel L. Somlo, edk2-devel; +Cc: jordan.l.justen, reza.jelveh, agraf, kraxel

On 03/07/17 04:14, Gabriel L. Somlo wrote:
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Reza Jelveh <reza.jelveh@tuhh.de>
> Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
> ---
>  OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c           | 10 ++++++++++
>  OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h           |  1 +
>  .../Library/PlatformBootManagerLib/PlatformBootManagerLib.inf  |  1 +
>  OvmfPkg/OvmfPkgIa32.dsc                                        |  8 ++++++++
>  OvmfPkg/OvmfPkgIa32.fdf                                        |  3 +++
>  OvmfPkg/OvmfPkgIa32X64.dsc                                     |  8 ++++++++
>  OvmfPkg/OvmfPkgIa32X64.fdf                                     |  3 +++
>  OvmfPkg/OvmfPkgX64.dsc                                         |  8 ++++++++
>  OvmfPkg/OvmfPkgX64.fdf                                         |  3 +++

We usually split the DSC/FDF changes from module changes. In some cases
it might mean splitting a patch like this into three: first add the
library resolutions (DSC), then modify the code, then include further
driver modules (DSC/FDF).

>  9 files changed, 45 insertions(+)
> 
> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
> index cc35630..9f6be90 100644
> --- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
> +++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
> @@ -381,6 +381,11 @@ Returns:
>    }
>  
>    //
> +  // Initialize AppleSupport library
> +  //
> +  InitializeAppleSupport (gImageHandle, gST);
> +
> +  //
>    // Prevent further changes to LockBoxes or SMRAM.
>    //
>    Handle = NULL;
> @@ -1474,6 +1479,11 @@ Routine Description:
>  
>    RemoveStaleFvFileOptions ();
>    SetBootOrderFromQemu ();
> +
> +  //
> +  // Locate and launch Apple's OS X bootloader
> +  //
> +  BdsBootApple ();
>  }

So, points I mentioned earlier:
- not sure why BdsBootApple() is needed,
- whatever calls we add here, they should depend on the new FeaturePCD.

>  
>  /**
> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h
> index ec58efa..3fa7712 100644
> --- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h
> +++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h
> @@ -49,6 +49,7 @@ Abstract:
>  #include <Library/NvVarsFileLib.h>
>  #include <Library/QemuFwCfgLib.h>
>  #include <Library/QemuBootOrderLib.h>
> +#include <Library/AppleSupportLib.h>
>  
>  #include <Protocol/Decompress.h>
>  #include <Protocol/PciIo.h>
> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> index f9e35c9..66d31a5 100644
> --- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> @@ -40,6 +40,7 @@
>    OvmfPkg/OvmfPkg.dec
>  
>  [LibraryClasses]
> +  AppleSupportLib
>    BaseLib
>    MemoryAllocationLib
>    UefiBootServicesTableLib

Please configure your git setup to include the INF/DSC/FDF/DEC section
names in the diff hunk headers. For that, please refer to:

<https://github.com/tianocore/tianocore.github.io/wiki/Laszlo's-unkempt-git-guide-for-edk2-contributors-and-maintainers#contrib-05>

(specifically the "xfuncname" setting), and to

https://github.com/tianocore/tianocore.github.io/wiki/Laszlo's-unkempt-git-guide-for-edk2-contributors-and-maintainers#contrib-09

> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> index 0bce56b..da83cba 100644
> --- a/OvmfPkg/OvmfPkgIa32.dsc
> +++ b/OvmfPkg/OvmfPkgIa32.dsc
> @@ -176,6 +176,8 @@
>    OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
>    XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf
>  
> +  AppleSupportLib|OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
> +

Any reason to keep an empty line between XenHypercallLib and
AppleSupportLib?

>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
>  
> @@ -696,6 +698,12 @@
>    MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
>    #
> +  # Apple Support
> +  #
> +  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
> +  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
> +

Should depend on the new build flag.

> +  #
>    # Network Support
>    #
>    MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
> diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
> index 09c1658..0e00bd9 100644
> --- a/OvmfPkg/OvmfPkgIa32.fdf
> +++ b/OvmfPkg/OvmfPkgIa32.fdf
> @@ -280,6 +280,9 @@ INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
> +INF  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
> +INF  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
> +

Should depend on the new build flag, plus a leading comment (since we're
adding one to the DSC too) would be welcome.

Stopping here.

Thanks
Laszlo

>  INF  FatPkg/EnhancedFatDxe/Fat.inf
>  
>  !ifndef $(USE_OLD_SHELL)
> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> index 56f7ff9..a38dc9b 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> @@ -181,6 +181,8 @@
>    OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
>    XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf
>  
> +  AppleSupportLib|OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
> +
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
>  
> @@ -705,6 +707,12 @@
>    MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
>    #
> +  # Apple Support
> +  #
> +  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
> +  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
> +
> +  #
>    # Network Support
>    #
>    MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
> diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
> index 5233314..2bd2d77 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.fdf
> +++ b/OvmfPkg/OvmfPkgIa32X64.fdf
> @@ -280,6 +280,9 @@ INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
> +INF  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
> +INF  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
> +
>  INF  FatPkg/EnhancedFatDxe/Fat.inf
>  
>  !ifndef $(USE_OLD_SHELL)
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index d0b0b0e..2bff68d 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -181,6 +181,8 @@
>    OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
>    XenHypercallLib|OvmfPkg/Library/XenHypercallLib/XenHypercallLib.inf
>  
> +  AppleSupportLib|OvmfPkg/Library/AppleSupportLib/AppleSupportLib.inf
> +
>  [LibraryClasses.common]
>    BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
>  
> @@ -703,6 +705,12 @@
>    MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
>    #
> +  # Apple Support
> +  #
> +  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
> +  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
> +
> +  #
>    # Network Support
>    #
>    MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index 3615010..0165a63 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -280,6 +280,9 @@ INF  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
>  INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
>  
> +INF  OvmfPkg/FswHfsPlus/FswHfsPlus.inf
> +INF  IntelFrameworkModulePkg/Universal/DataHubDxe/DataHubDxe.inf
> +
>  INF  FatPkg/EnhancedFatDxe/Fat.inf
>  
>  !ifndef $(USE_OLD_SHELL)
> 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot)
  2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
                   ` (6 preceding siblings ...)
  2017-03-07 15:41 ` [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Laszlo Ersek
@ 2017-03-29 23:22 ` Phil Dennis-Jordan
  2017-03-31 20:01   ` Gabriel L. Somlo
  7 siblings, 1 reply; 12+ messages in thread
From: Phil Dennis-Jordan @ 2017-03-29 23:22 UTC (permalink / raw)
  To: Gabriel L. Somlo; +Cc: edk2-devel, Jordan Justen, Laszlo Ersek, agraf

Hi Gabriel,

First off, thanks for going to the effort of building an HFS+ driver!
Due to travel, I've only just got around to checking these out, sorry.

Before I can try to help out with cleaning the patches up to the
extent they can be upstreamed, I still need to get them working.
boot.efi seems to load, and it finds the kernel, so the HFS+ driver is
definitely working, but it fails during early boot with "Error loading
drivers." This is with existing VM disk images that work with an old
variant of Reza's patchset, so it's possible that the prelinked kernel
image assumes something about the EFI implementation that's not true
for this patchset. I'll try reinstalling the VM from scratch with the
instructions from your website [1] before I try to dig deeper. It
could also be something to do with the hardlink issue I mention later
on, or maybe the lack of support for fragmented files.

A few general comments so far on the HFS patches below:

On Tue, Mar 7, 2017 at 4:14 PM, Gabriel L. Somlo <gsomlo@gmail.com> wrote:
>
>     - patch 3/6: A BSD-licensed implementation of an FSW HFS+ driver.
>                  Based on Apple's HFS+ specification (TN1150), this is
>                  a minimalistic, bare-bones set of FSW methods capable
>                  of locating and loading files from an HFS+ volume.
>                  Lots of functionality (e.g. stat, readdir, hard/sym
>                  links, or accessing files with more than 8 fragments)
>                  is unimplemented at this time. I only implemented the
>                  methods necessary to support loading Apple's boot.efi
>                  and kernel.

The OS X installer disk images produced by Apple's own
"createinstallmedia" tool creates hardlinks to the bootloader and
kernel image, so supporting hardlinks might be a useful feature we can
add, as it would simplify the installer image creation process
somewhat. But I guess the higher priority is getting rid of the FSW
wrapper.

> If not, it might be worth factoring out the common bits and roll the
> the whole thing up into a standalone HFS+ driver, which would be a
> significantly different direction to go.

Based on Laszlo's comments, that seems to be the preferred approach.
(Not entirely unexpectedly.) I'm unfamiliar with the FSW, so I'll try
to find some time to look over that and work out if I can make a
sufficient time investment to help out with distilling this down. Do
you think it'd be easier to start from zero, or to rip out FSW bits
one by one and hard-code them into the HFS+ driver, having the whole
thing working throughout?

Phil


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot)
  2017-03-29 23:22 ` Phil Dennis-Jordan
@ 2017-03-31 20:01   ` Gabriel L. Somlo
  0 siblings, 0 replies; 12+ messages in thread
From: Gabriel L. Somlo @ 2017-03-31 20:01 UTC (permalink / raw)
  To: Phil Dennis-Jordan; +Cc: edk2-devel, Jordan Justen, Laszlo Ersek, agraf

Hi Phil,

On Thu, Mar 30, 2017 at 12:22:16PM +1300, Phil Dennis-Jordan wrote:
> First off, thanks for going to the effort of building an HFS+ driver!
> Due to travel, I've only just got around to checking these out, sorry.
> 
> Before I can try to help out with cleaning the patches up to the
> extent they can be upstreamed, I still need to get them working.
> boot.efi seems to load, and it finds the kernel, so the HFS+ driver is
> definitely working, but it fails during early boot with "Error loading
> drivers." This is with existing VM disk images that work with an old
> variant of Reza's patchset, so it's possible that the prelinked kernel
> image assumes something about the EFI implementation that's not true
> for this patchset. I'll try reinstalling the VM from scratch with the
> instructions from your website [1] before I try to dig deeper. It
> could also be something to do with the hardlink issue I mention later
> on, or maybe the lack of support for fragmented files.
> 
> A few general comments so far on the HFS patches below:
> 
> On Tue, Mar 7, 2017 at 4:14 PM, Gabriel L. Somlo <gsomlo@gmail.com> wrote:
> >
> >     - patch 3/6: A BSD-licensed implementation of an FSW HFS+ driver.
> >                  Based on Apple's HFS+ specification (TN1150), this is
> >                  a minimalistic, bare-bones set of FSW methods capable
> >                  of locating and loading files from an HFS+ volume.
> >                  Lots of functionality (e.g. stat, readdir, hard/sym
> >                  links, or accessing files with more than 8 fragments)
> >                  is unimplemented at this time. I only implemented the
> >                  methods necessary to support loading Apple's boot.efi
> >                  and kernel.
> 
> The OS X installer disk images produced by Apple's own
> "createinstallmedia" tool creates hardlinks to the bootloader and
> kernel image, so supporting hardlinks might be a useful feature we can
> add, as it would simplify the installer image creation process
> somewhat. But I guess the higher priority is getting rid of the FSW
> wrapper.
> 
> > If not, it might be worth factoring out the common bits and roll the
> > the whole thing up into a standalone HFS+ driver, which would be a
> > significantly different direction to go.
> 
> Based on Laszlo's comments, that seems to be the preferred approach.
> (Not entirely unexpectedly.) I'm unfamiliar with the FSW, so I'll try
> to find some time to look over that and work out if I can make a
> sufficient time investment to help out with distilling this down. Do
> you think it'd be easier to start from zero, or to rip out FSW bits
> one by one and hard-code them into the HFS+ driver, having the whole
> thing working throughout?

FTR, my main objectives with that patch set were, in roughly this
order:

	- backup: if I get hit by a bus, i wanted to have a (BSD)
	  license-compliant way to glue it all together and get it
	  working

	- something public to point at from my instructions webpage :)

	- solicit initial feedback re. HFS+ support
		- that's where Laszlo's "Lose the FSW!" came in :)

I decided to stick with FSW initially because I needed to focus on the
HFS+ nitty-gritty, and FSW allowed me to ignore the EDK2 nitty-gritty
during that process. (TBH, on the few occasions I've contributed anything
to EDK2, I always started out by writing in "regular" C coding style,
then translate into EDK2-ese immediately before submitting the patch :)

Last, but not least, I figured the refind project might also be
interested in a BSD-licensed HFS+ driver, so that'd be a free bonus.


If you decide to go for it and do the translation or
re-implementation, that'd be awesome! If it were me, I'd probably
try to delay the part where I eliminate the FSW until I were finished
with understanding and implementing the HFS+ specific bits. I'd delay
dealing with the coding guidelines until I had finished eliminating
the FSW, and had working C code I could understand just by
eyeballing...

But that's just me and my personal strength vs. weakness (mainly the
latter) tradeoff :) Any way that works for you is by definition better!


Last, but not least: I completely cargo-culted Reza's patches, with
only the bare minimum intervention to keep them compiling as I rebased
on top the accumulating upstream edk2 commits. Since they have no
reasonable chance of being applied without HFS+ support, I figured I'd
focus on that first and foremost. That also means that, right now, I have
absolutely no idea if (and how) anything in those patches could be done
more elegantly or efficently :)


Thanks again,
--Gabriel


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2017-03-31 20:02 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-03-07  3:14 [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Gabriel L. Somlo
2017-03-07  3:14 ` [RFC PATCH 1/6] FswHfsPlus: add File System Wrapper (FSW) interface code Gabriel L. Somlo
2017-03-07  3:14 ` [RFC PATCH 2/6] FswHfsPlus: connect FSW code to EDK2, fix compile discrepancies Gabriel L. Somlo
2017-03-07  3:14 ` [RFC PATCH 3/6] FswHfsPlus: implement FSW driver for the HFS+ file system Gabriel L. Somlo
2017-03-07  3:14 ` [RFC PATCH 4/6] EdkCompatibilityPkg: allow ConsoleControl protocol to be used Gabriel L. Somlo
2017-03-07  3:14 ` [RFC PATCH 5/6] OvmfPkg: add Apple boot support Gabriel L. Somlo
2017-03-07 16:14   ` Laszlo Ersek
2017-03-07  3:14 ` [RFC PATCH 6/6] OvmfPkg: enable AppleSupport library for Ovmf firmware Gabriel L. Somlo
2017-03-07 16:21   ` Laszlo Ersek
2017-03-07 15:41 ` [RFC PATCH 0/6] OVMF: HFS+ (and Mac OS X boot) Laszlo Ersek
2017-03-29 23:22 ` Phil Dennis-Jordan
2017-03-31 20:01   ` Gabriel L. Somlo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox