qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 1/7] vnvram: VNVRAM bdrv support


From: Corey Bryant
Subject: [Qemu-devel] [PATCH 1/7] vnvram: VNVRAM bdrv support
Date: Thu, 23 May 2013 13:44:41 -0400

Provides low-level VNVRAM functionality that reads and writes data,
such as an entry's binary blob, to a drive image using the block
driver.

Signed-off-by: Corey Bryant <address@hidden>
---
 Makefile.objs |    2 +
 vnvram.c      |  487 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vnvram.h      |   22 +++
 3 files changed, 511 insertions(+), 0 deletions(-)
 create mode 100644 vnvram.c
 create mode 100644 vnvram.h

diff --git a/Makefile.objs b/Makefile.objs
index 286ce06..4875a94 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -76,6 +76,8 @@ common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
 
 common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
 
+common-obj-y += vnvram.o
+
 ######################################################################
 # qapi
 
diff --git a/vnvram.c b/vnvram.c
new file mode 100644
index 0000000..e467198
--- /dev/null
+++ b/vnvram.c
@@ -0,0 +1,487 @@
+/*
+ * VNVRAM -- stores persistent data in image files
+ *
+ * Copyright (C) 2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <address@hidden>
+ *  Corey Bryant     <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "vnvram.h"
+#include "block/block.h"
+
+/*
+#define VNVRAM_DEBUG
+*/
+
+#ifdef VNVRAM_DEBUG
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+#define VNVRAM_ENTRY_DATA                              \
+    VNVRAMEntryName name; /* name of entry */          \
+    uint64_t blob_offset; /* start of blob on drive */ \
+    uint32_t cur_size;    /* current size of blob */   \
+    uint32_t max_size;    /* max size of blob */
+
+/* The following VNVRAM information is stored in-memory */
+typedef struct VNVRAMEntry {
+    VNVRAM_ENTRY_DATA
+    QLIST_ENTRY(VNVRAMEntry) next;
+} VNVRAMEntry;
+
+struct VNVRAM {
+    char *drv_id;            /* corresponds to -drive id= on command line */
+    BlockDriverState *bds;   /* bds for the VNVRAM drive */
+    uint64_t end_offset;     /* offset on drive where next entry will go */
+    QLIST_HEAD(entries_head, VNVRAMEntry) entries_head; /* in-memory entries */
+    QLIST_ENTRY(VNVRAM) list;
+};
+
+/* There can be multiple VNVRAMS */
+static QLIST_HEAD(, VNVRAM) vnvrams = QLIST_HEAD_INITIALIZER(vnvrams);
+
+#define VNVRAM_VERSION_1        1
+#define VNVRAM_CURRENT_VERSION  VNVRAM_VERSION_1
+#define VNVRAM_MAGIC            0x4E56524D /* NVRM */
+
+/* VNVRAM drive data consists of a header followed by entries and their blobs.
+ * For example:
+ *   | header | entry 1 | entry 1's blob | entry 2 | entry 2's blob | ... |
+ */
+typedef struct VNVRAMDrvHdr {
+    uint16_t version;
+    uint32_t magic;
+    uint32_t num_entries;
+} QEMU_PACKED VNVRAMDrvHdr;
+
+typedef struct VNVRAMDrvEntry {
+    VNVRAM_ENTRY_DATA
+} QEMU_PACKED VNVRAMDrvEntry;
+
+static int vnvram_drv_entry_create(VNVRAM *, VNVRAMEntry *, uint64_t, 
uint32_t);
+static int vnvram_drv_entry_update(VNVRAM *, VNVRAMEntry *, uint64_t, 
uint32_t);
+
+/*
+ * Macros for finding entries and their drive offsets
+ */
+#define VNVRAM_FIRST_ENTRY(vnvram) \
+        QLIST_FIRST(&(vnvram)->entries_head)
+
+#define VNVRAM_NEXT_ENTRY(cur_entry) \
+    QLIST_NEXT(cur_entry, next)
+
+#define VNVRAM_FIRST_ENTRY_OFFSET() \
+    sizeof(VNVRAMDrvHdr)
+
+#define VNVRAM_NEXT_ENTRY_OFFSET(entry) \
+    ((entry)->blob_offset + (entry)->max_size)
+
+#define VNVRAM_NEXT_AVAIL_BLOB_OFFSET(vnvram) \
+    ((vnvram)->end_offset + sizeof(VNVRAMDrvEntry))
+
+#define VNVRAM_ENTRY_OFFSET_FROM_BLOB(blob_offset) \
+    (blob_offset - sizeof(VNVRAMDrvEntry))
+
+#define VNVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset) \
+    (entry_offset + sizeof(VNVRAMDrvEntry))
+
+/************************* VNVRAM drv ********************************/
+/* Low-level VNVRAM functions that work with the drive header and    */
+/* entries.                                                          */
+/*********************************************************************/
+
+/*
+ * Big-endian conversions
+ */
+static void vnvram_drv_hdr_cpu_to_be(VNVRAMDrvHdr *hdr)
+{
+    hdr->version = cpu_to_be16(hdr->version);
+    hdr->magic = cpu_to_be32(hdr->magic);
+    hdr->num_entries = cpu_to_be32(hdr->num_entries);
+}
+
+static void vnvram_drv_hdr_be_to_cpu(VNVRAMDrvHdr *hdr)
+{
+    hdr->version = be16_to_cpu(hdr->version);
+    hdr->magic = be32_to_cpu(hdr->magic);
+    hdr->num_entries = be32_to_cpu(hdr->num_entries);
+}
+
+static void vnvram_drv_entry_cpu_to_be(VNVRAMDrvEntry *drv_entry)
+{
+    drv_entry->blob_offset = cpu_to_be64(drv_entry->blob_offset);
+    drv_entry->cur_size = cpu_to_be32(drv_entry->cur_size);
+    drv_entry->max_size = cpu_to_be32(drv_entry->max_size);
+}
+
+static void vnvram_drv_entry_be_to_cpu(VNVRAMDrvEntry *drv_entry)
+{
+    drv_entry->blob_offset = be64_to_cpu(drv_entry->blob_offset);
+    drv_entry->cur_size = be32_to_cpu(drv_entry->cur_size);
+    drv_entry->max_size = be32_to_cpu(drv_entry->max_size);
+}
+
+/*
+ * Find the VNVRAM that corresponds to the specified drive ID string
+ */
+static VNVRAM *vnvram_drv_find_by_id(const char *drv_id)
+{
+    VNVRAM *vnvram;
+
+    QLIST_FOREACH(vnvram, &vnvrams, list) {
+        if (strcmp(vnvram->drv_id, drv_id) == 0) {
+            return vnvram;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Increase the drive size if it's too small to fit the VNVRAM data
+ */
+static int vnvram_drv_adjust_size(VNVRAM *vnvram)
+{
+    int rc = 0;
+    int64_t needed_size;
+
+    needed_size = 0;
+
+    if (bdrv_getlength(vnvram->bds) < needed_size) {
+        rc = bdrv_truncate(vnvram->bds, needed_size);
+        if (rc != 0) {
+            DPRINTF("%s: VNVRAM drive too small\n", __func__);
+        }
+    }
+
+    return rc;
+}
+
+/*
+ * Write a header to the drive with entry count of zero
+ */
+static int vnvram_drv_hdr_create_empty(VNVRAM *vnvram)
+{
+    VNVRAMDrvHdr hdr;
+
+    hdr.version = VNVRAM_CURRENT_VERSION;
+    hdr.magic = VNVRAM_MAGIC;
+    hdr.num_entries = 0;
+
+    vnvram_drv_hdr_cpu_to_be((&hdr));
+
+    if (bdrv_pwrite(vnvram->bds, 0, (&hdr), sizeof(hdr)) != sizeof(hdr)) {
+        DPRINTF("%s: Write of header to drive failed\n", __func__);
+        return -EIO;
+    }
+
+    vnvram->end_offset = sizeof(VNVRAMDrvHdr);
+
+    return 0;
+}
+
+/*
+ * Read the header from the drive
+ */
+static int vnvram_drv_hdr_read(VNVRAM *vnvram, VNVRAMDrvHdr *hdr)
+{
+    if (bdrv_pread(vnvram->bds, 0, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
+        DPRINTF("%s: Read of header from drive failed\n", __func__);
+        return -EIO;
+    }
+
+    vnvram_drv_hdr_be_to_cpu(hdr);
+
+    return 0;
+}
+
+/*
+ * Write the header to the drive
+ */
+static int vnvram_drv_hdr_write(VNVRAM *vnvram, VNVRAMDrvHdr *hdr)
+{
+    vnvram_drv_hdr_cpu_to_be(hdr);
+
+    if (bdrv_pwrite(vnvram->bds, 0, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
+        DPRINTF("%s: Write of header to drive failed\n", __func__);
+        return -EIO;
+    }
+
+    vnvram_drv_hdr_be_to_cpu(hdr);
+
+    return 0;
+}
+
+/*
+ * Read an entry from the drive (does not include blob)
+ */
+static int vnvram_drv_entry_read(VNVRAM *vnvram, uint64_t entry_offset,
+                                 VNVRAMDrvEntry *drv_entry)
+{
+    if (bdrv_pread(vnvram->bds, entry_offset, drv_entry, sizeof(*drv_entry))
+            != sizeof(*drv_entry)) {
+        DPRINTF("%s: VNVRAM error reading entry from drive\n", __func__);
+        return -EIO;
+    }
+
+    vnvram_drv_entry_be_to_cpu(drv_entry);
+
+    return 0;
+}
+
+/*
+ * Write an entry to the drive (does not include blob)
+ */
+static int vnvram_drv_entry_write(VNVRAM *vnvram, uint64_t entry_offset,
+                                  VNVRAMDrvEntry *drv_entry)
+{
+    vnvram_drv_entry_cpu_to_be(drv_entry);
+
+    if (bdrv_pwrite(vnvram->bds, entry_offset, drv_entry, sizeof(*drv_entry))
+            != sizeof(*drv_entry)) {
+        DPRINTF("%s: VNVRAM error writing entry to drive\n", __func__);
+        return -EIO;
+    }
+
+    vnvram_drv_entry_be_to_cpu(drv_entry);
+
+    return 0;
+}
+
+/*
+ * Read an entry's blob from the drive
+ */
+static int vnvram_drv_entry_read_blob(VNVRAM *vnvram, const VNVRAMEntry *entry,
+                                      char **blob, uint32_t *blob_size)
+{
+    int rc = 0;
+
+    *blob = NULL;
+    *blob_size = 0;
+
+    if (entry->cur_size == 0) {
+        DPRINTF("%s: VNVRAM entry not found\n", __func__);
+        rc = -ENOENT;
+        goto err_exit;
+    }
+
+    *blob = g_malloc(entry->cur_size);
+
+    DPRINTF("%s: VNVRAM read: name=%s, blob_offset=%"PRIu64", 
size=%"PRIu32"\n",
+            __func__, (char *)entry->name, entry->blob_offset, 
entry->cur_size);
+
+    if (bdrv_pread(vnvram->bds, entry->blob_offset, *blob, entry->cur_size)
+            != entry->cur_size) {
+        DPRINTF("%s: VNVRAM error reading blob from drive\n", __func__);
+        rc = -EIO;
+        goto err_exit;
+    }
+
+    *blob_size = entry->cur_size;
+
+    return rc;
+
+err_exit:
+    g_free(*blob);
+    *blob = NULL;
+
+    return rc;
+}
+
+/*
+ * Write an entry's blob to the drive
+ */
+static int vnvram_drv_entry_write_blob(VNVRAM *vnvram, VNVRAMEntry *entry,
+                                       char *blob, uint32_t blob_size)
+{
+    int rc;
+    uint64_t blob_offset;
+
+    if (blob_size == 0 || blob_size > entry->max_size) {
+        DPRINTF("%s: Blob size is not valid for entry\n", __func__);
+        rc = -EMSGSIZE;
+        goto err_exit;
+    }
+
+    rc = vnvram_drv_adjust_size(vnvram);
+    if (rc != 0) {
+        goto err_exit;
+    }
+
+    if (entry->blob_offset == 0) {
+        /* Entry doesn't exist on the drive yet */
+        blob_offset = VNVRAM_NEXT_AVAIL_BLOB_OFFSET(vnvram);
+    } else {
+        blob_offset = entry->blob_offset;
+    }
+
+    DPRINTF("%s: VNVRAM write: name=%s, blob_offset=%"PRIu64", "
+            "size=%"PRIu32"\n", __func__, (char *)entry->name,
+            blob_offset, blob_size);
+
+    if (bdrv_pwrite(vnvram->bds, blob_offset, blob, blob_size) != blob_size) {
+        DPRINTF("%s: VNVRAM error writing blob to drive\n", __func__);
+        rc = -EIO;
+        goto err_exit;
+    }
+
+    if (entry->blob_offset == 0) {
+        /* Entry doesn't exist on the drive yet */
+        rc = vnvram_drv_entry_create(vnvram, entry,
+                                     
VNVRAM_ENTRY_OFFSET_FROM_BLOB(blob_offset),
+                                     blob_size);
+        if (rc != 0) {
+            DPRINTF("%s: Unable to create VNVRAM entry\n", __func__);
+            goto err_exit;
+        }
+    } else {
+        rc = vnvram_drv_entry_update(vnvram, entry,
+                                     
VNVRAM_ENTRY_OFFSET_FROM_BLOB(blob_offset),
+                                     blob_size);
+        if (rc != 0) {
+            DPRINTF("%s: Unable to update VNVRAM entry\n", __func__);
+            goto err_exit;
+        }
+    }
+
+    entry->blob_offset = blob_offset;
+    entry->cur_size = blob_size;
+
+err_exit:
+    return rc;
+}
+
+/*
+ * Create an entry and write it to the drive (does not include blob)
+ */
+static int vnvram_drv_entry_create(VNVRAM *vnvram, VNVRAMEntry *entry,
+                                   uint64_t entry_offset, uint32_t blob_size)
+{
+    int rc;
+    VNVRAMDrvHdr hdr;
+    VNVRAMDrvEntry *drv_entry;
+
+    drv_entry = g_new0(VNVRAMDrvEntry, 1);
+
+    pstrcpy(drv_entry->name, sizeof(drv_entry->name), (char *)entry->name);
+    drv_entry->blob_offset = VNVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset);
+    drv_entry->cur_size = blob_size;
+    drv_entry->max_size = entry->max_size;
+
+    rc = vnvram_drv_entry_write(vnvram, entry_offset, drv_entry);
+    if (rc != 0) {
+        goto err_exit;
+    }
+
+    rc = vnvram_drv_hdr_read(vnvram, (&hdr));
+    if (rc != 0) {
+        goto err_exit;
+    }
+
+    hdr.num_entries++;
+
+    rc = vnvram_drv_hdr_write(vnvram, (&hdr));
+    if (rc != 0) {
+        goto err_exit;
+    }
+
+    vnvram->end_offset = drv_entry->blob_offset + drv_entry->max_size;
+
+err_exit:
+    g_free(drv_entry);
+
+    return rc;
+}
+
+/*
+ * Update an entry on the drive (does not include blob)
+ */
+static int vnvram_drv_entry_update(VNVRAM *vnvram, VNVRAMEntry *entry,
+                                   uint64_t entry_offset, uint32_t blob_size)
+{
+    int rc;
+    VNVRAMDrvEntry *drv_entry;
+
+    drv_entry = g_new0(VNVRAMDrvEntry, 1);
+
+    pstrcpy(drv_entry->name, sizeof(drv_entry->name), (char *)entry->name);
+    drv_entry->blob_offset = VNVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset);
+    drv_entry->cur_size = blob_size;
+    drv_entry->max_size = entry->max_size;
+
+    rc = vnvram_drv_entry_write(vnvram, entry_offset, drv_entry);
+
+    g_free(drv_entry);
+
+    return rc;
+}
+
+/*
+ * Get all entry data from the drive (does not get blob data)
+ */
+static int vnvram_drv_entries_get(VNVRAM *vnvram, VNVRAMDrvHdr *hdr,
+                                  VNVRAMDrvEntry **drv_entries,
+                                  int *num_entries)
+{
+    int i, rc;
+    uint64_t entry_offset;
+
+    *drv_entries = NULL;
+    *num_entries = 0;
+
+    *num_entries = hdr->num_entries;
+    if (*num_entries == 0) {
+        return 0;
+    }
+
+    *drv_entries = g_malloc_n(hdr->num_entries, sizeof(VNVRAMDrvEntry));
+
+    entry_offset = VNVRAM_FIRST_ENTRY_OFFSET();
+
+    for (i = 0; i < hdr->num_entries; i++) {
+        VNVRAMDrvEntry *drv_entry = &(*drv_entries)[i];
+
+        rc = vnvram_drv_entry_read(vnvram, entry_offset, drv_entry);
+        if (rc != 0) {
+            goto err_exit;
+        }
+
+        entry_offset = VNVRAM_NEXT_ENTRY_OFFSET(drv_entry);
+    }
+
+    return 0;
+
+err_exit:
+    g_free(*drv_entries);
+    *drv_entries = NULL;
+    *num_entries = 0;
+
+    return rc;
+}
+
+/*
+ * Check if the VNVRAM drive header is valid
+ */
+static bool vnvram_drv_hdr_is_valid(VNVRAM *vnvram, VNVRAMDrvHdr *hdr)
+{
+    if (hdr->version != VNVRAM_CURRENT_VERSION) {
+        DPRINTF("%s: VNVRAM drive version not valid\n", __func__);
+        return false;
+    }
+
+    if (hdr->magic != VNVRAM_MAGIC) {
+        DPRINTF("%s: VNVRAM drive magic not valid\n", __func__);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vnvram.h b/vnvram.h
new file mode 100644
index 0000000..b6d7cd7
--- /dev/null
+++ b/vnvram.h
@@ -0,0 +1,22 @@
+/*
+ * VNVRAM -- stores persistent data in image files
+ *
+ * Copyright (C) 2013 IBM Corporation
+ *
+ * Authors:
+ *  Stefan Berger    <address@hidden>
+ *  Corey Bryant     <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef _QEMU_VNVRAM_H_
+#define _QEMU_VNVRAM_H_
+
+typedef struct VNVRAM VNVRAM;
+
+#define VNVRAM_ENTRY_NAME_LENGTH 16
+typedef char VNVRAMEntryName[VNVRAM_ENTRY_NAME_LENGTH];
+
+#endif
-- 
1.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]