diff --git a/vnvram.c b/vnvram.c
index 37b7070..4157482 100644
--- a/vnvram.c
+++ b/vnvram.c
@@ -14,6 +14,7 @@
 #include "vnvram.h"
 #include "block/block.h"
 #include "monitor/monitor.h"
+#include "qemu/thread.h"
 
 /*
 #define VNVRAM_DEBUG
@@ -68,6 +69,30 @@ typedef struct VNVRAMDrvEntry {
     VNVRAM_ENTRY_DATA
 } QEMU_PACKED VNVRAMDrvEntry;
 
+/* Used to pass read/write requests to the bottom-half function */
+typedef struct VNVRAMRWRequest {
+    VNVRAM *vnvram;
+    VNVRAMEntry *entry;
+    bool is_write;
+    char **blob_r;
+    uint32_t *blob_r_size;
+    char *blob_w;
+    uint32_t blob_w_size;
+    int rc;
+
+    QemuMutex completion_mutex;
+    QemuCond completion;
+
+    QSIMPLEQ_ENTRY(VNVRAMRWRequest) list;
+} VNVRAMRWRequest;
+
+/* A mutex protected queue where read/write requests are stored */
+static QemuMutex vnvram_rwrequests_mutex;
+static QSIMPLEQ_HEAD(, VNVRAMRWRequest) vnvram_rwrequests =
+    QSIMPLEQ_HEAD_INITIALIZER(vnvram_rwrequests);
+
+static QEMUBH *vnvram_bh;
+
 static int vnvram_drv_entry_create(VNVRAM *, VNVRAMEntry *, uint64_t, uint32_t);
 static int vnvram_drv_entry_update(VNVRAM *, VNVRAMEntry *, uint64_t, uint32_t);
 static int vnvram_register_entry_internal(VNVRAM *, const VNVRAMEntryName *,
@@ -679,3 +704,120 @@ static VNVRAMEntry *vnvram_find_entry(VNVRAM *vnvram,
 
     return NULL;
 }
+
+/*********************** VNVRAM rwrequest ****************************/
+/* High-level VNVRAM functions that schedule and kick off read/write */
+/* requests.                                                         */
+/*********************************************************************/
+
+/*
+ * VNVRAMRWRequest initialization for read requests
+ */
+static VNVRAMRWRequest *vnvram_rwrequest_init_read(VNVRAM *vnvram,
+                                                   VNVRAMEntry *entry,
+                                                   char **blob,
+                                                   uint32_t *blob_size)
+{
+    VNVRAMRWRequest *rwr;
+
+    rwr = g_new0(VNVRAMRWRequest, 1);
+
+    rwr->is_write = false;
+    rwr->entry = entry;
+    rwr->vnvram = vnvram;
+    rwr->blob_r = blob;
+    rwr->blob_r_size = blob_size;
+
+    return rwr;
+}
+
+/*
+ * VNVRAMRWRequest initialization for write requests
+ */
+static VNVRAMRWRequest *vnvram_rwrequest_init_write(VNVRAM *vnvram,
+                                                    VNVRAMEntry *entry,
+                                                    char *blob,
+                                                    uint32_t blob_size)
+{
+    VNVRAMRWRequest *rwr;
+
+    rwr = g_new0(VNVRAMRWRequest, 1);
+
+    rwr->is_write = true;
+    rwr->entry = entry;
+    rwr->vnvram = vnvram;
+    rwr->blob_w = blob;
+    rwr->blob_w_size = blob_size;
+
+    return rwr;
+}
+
+/*
+ * Execute a read or write of blob data based on an VNVRAMRWRequest
+ */
+static int vnvram_rwrequest_exec(VNVRAMRWRequest *rwr)
+{
+    int rc = 0;
+
+    if (rwr->is_write) {
+        rc = vnvram_drv_entry_write_blob(rwr->vnvram, rwr->entry,
+                                         rwr->blob_w, rwr->blob_w_size);
+    } else {
+        rc = vnvram_drv_entry_read_blob(rwr->vnvram, rwr->entry,
+                                        rwr->blob_r, rwr->blob_r_size);
+    }
+
+    rwr->rc = rc;
+
+    qemu_mutex_lock(&rwr->completion_mutex);
+    qemu_cond_signal(&rwr->completion);
+    qemu_mutex_unlock(&rwr->completion_mutex);
+
+    return rc;
+}
+
+/*
+ * Bottom-half callback that is invoked by QEMU's main thread to
+ * process VNVRAM read/write requests.
+ */
+static void vnvram_rwrequest_callback(void *opaque)
+{
+    VNVRAMRWRequest *rwr, *next;
+
+    qemu_mutex_lock(&vnvram_rwrequests_mutex);
+
+    QSIMPLEQ_FOREACH_SAFE(rwr, &vnvram_rwrequests, list, next) {
+        QSIMPLEQ_REMOVE(&vnvram_rwrequests, rwr, VNVRAMRWRequest, list);
+
+        qemu_mutex_unlock(&vnvram_rwrequests_mutex);
+
+        vnvram_rwrequest_exec(rwr);
+
+        qemu_mutex_lock(&vnvram_rwrequests_mutex);
+    }
+
+    qemu_mutex_unlock(&vnvram_rwrequests_mutex);
+}
+
+/*
+ * Schedules a bottom-half to read or write a blob to the VNVRAM drive.
+ */
+static int vnvram_rwrequest_schedule(VNVRAMRWRequest *rwr)
+{
+    int rc = 0;
+
+    qemu_mutex_lock(&vnvram_rwrequests_mutex);
+    QSIMPLEQ_INSERT_TAIL(&vnvram_rwrequests, rwr, list);
+    qemu_mutex_unlock(&vnvram_rwrequests_mutex);
+
+    qemu_bh_schedule(vnvram_bh);
+
+    /* All reads/writes are synchronous so we wait for completion */
+    qemu_mutex_lock(&rwr->completion_mutex);
+    qemu_cond_wait(&rwr->completion, &rwr->completion_mutex);
+    qemu_mutex_unlock(&rwr->completion_mutex);
+
+    rc = rwr->rc;
+
+    return rc;
+}
