[SeaBIOS] [PATCH v7 2/3] romfile_loader: utility to patch in-memory ROM files

Michael S. Tsirkin mst at redhat.com
Thu Oct 3 15:30:37 CEST 2013


Add ability for a ROM file to point to
it's image in memory. When file is in memory,
add utility that can patch it, storing
pointers to one file within another file.

This is not a lot of code: together with the follow-up patch to load
ACPI tables from ROM, it's about 1K extra.

Signed-off-by: Michael S. Tsirkin <mst at redhat.com>
---
 Makefile                |   2 +-
 src/fw/romfile_loader.h |  72 ++++++++++++++++++++
 src/fw/romfile_loader.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 250 insertions(+), 1 deletion(-)
 create mode 100644 src/fw/romfile_loader.h
 create mode 100644 src/fw/romfile_loader.c

diff --git a/Makefile b/Makefile
index 3984d35..797c00f 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c pmm.c romfile.c optionroms.c \
     hw/usb-hub.c \
     fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \
     fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/mtrr.c fw/xen.c \
-    fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c
+    fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c
 SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c
 DIRS=src src/hw src/fw vgasrc
 
diff --git a/src/fw/romfile_loader.h b/src/fw/romfile_loader.h
new file mode 100644
index 0000000..15eab2a
--- /dev/null
+++ b/src/fw/romfile_loader.h
@@ -0,0 +1,72 @@
+#ifndef __ROMFILE_LOADER_H
+#define __ROMFILE_LOADER_H
+
+#include "types.h" // u8
+#include "util.h" // romfile_s
+
+#define ROMFILE_LOADER_FILESZ 56
+
+/* ROM file linker/loader interface. Linker uses little endian format */
+struct romfile_loader_entry_s {
+    u32 command;
+    union {
+        /*
+         * COMMAND_ALLOCATE - allocate a table from @alloc_file
+         * subject to @alloc_align alignment (must be power of 2)
+         * and @alloc_zone (can be HIGH or FSEG) requirements.
+         *
+         * Must appear exactly once for each file, and before
+         * this file is referenced by any other command.
+         */
+        struct {
+            char alloc_file[ROMFILE_LOADER_FILESZ];
+            u32 alloc_align;
+            u8 alloc_zone;
+        };
+
+        /*
+         * COMMAND_ADD_POINTER - patch the table (originating from
+         * @dest_file) at @pointer_offset, by adding a pointer to the table
+         * originating from @src_file. 1,2,4 or 8 byte unsigned
+         * addition is used depending on @pointer_size.
+         */
+        struct {
+            char pointer_dest_file[ROMFILE_LOADER_FILESZ];
+            char pointer_src_file[ROMFILE_LOADER_FILESZ];
+            u32 pointer_offset;
+            u8 pointer_size;
+        };
+
+        /*
+         * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by
+         * @cksum_start and @cksum_length fields,
+         * and then add the value at @cksum_offset.
+         * Checksum simply sums -X for each byte X in the range
+         * using 8-bit math.
+         */
+        struct {
+            char cksum_file[ROMFILE_LOADER_FILESZ];
+            u32 cksum_offset;
+            u32 cksum_start;
+            u32 cksum_length;
+        };
+
+        /* padding */
+        char pad[124];
+    };
+};
+
+enum {
+    ROMFILE_LOADER_COMMAND_ALLOCATE     = 0x1,
+    ROMFILE_LOADER_COMMAND_ADD_POINTER  = 0x2,
+    ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
+};
+
+enum {
+    ROMFILE_LOADER_ALLOC_ZONE_HIGH = 0x1,
+    ROMFILE_LOADER_ALLOC_ZONE_FSEG = 0x2,
+};
+
+int romfile_loader_execute(const char *name);
+
+#endif
diff --git a/src/fw/romfile_loader.c b/src/fw/romfile_loader.c
new file mode 100644
index 0000000..325a0fa
--- /dev/null
+++ b/src/fw/romfile_loader.c
@@ -0,0 +1,177 @@
+#include "romfile_loader.h"
+#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX
+#include "util.h" // checksum
+#include "string.h" // strcmp
+#include "romfile.h" // struct romfile_s
+#include "malloc.h" // Zone*, _malloc
+#include "output.h" // warn_*
+
+struct romfile_loader_file {
+    struct romfile_s *file;
+    void *data;
+};
+struct romfile_loader_files {
+    int nfiles;
+    struct romfile_loader_file files[];
+};
+
+static struct romfile_loader_file *
+romfile_loader_find(const char *name,
+                    struct romfile_loader_files *files)
+{
+    int i;
+    if (name[ROMFILE_LOADER_FILESZ - 1])
+        return NULL;
+    for (i = 0; i < files->nfiles; ++i)
+        if (!strcmp(files->files[i].file->name, name))
+            return &files->files[i];
+    return NULL;
+}
+
+static void romfile_loader_allocate(struct romfile_loader_entry_s *entry,
+                                    struct romfile_loader_files *files)
+{
+    struct zone_s *zone;
+    struct romfile_loader_file *file = &files->files[files->nfiles];
+    void *data;
+    int ret;
+    unsigned alloc_align = le32_to_cpu(entry->alloc_align);
+
+    if (alloc_align & (alloc_align - 1))
+        goto err;
+
+    switch (entry->alloc_zone) {
+        case ROMFILE_LOADER_ALLOC_ZONE_HIGH:
+            zone = &ZoneHigh;
+            break;
+        case ROMFILE_LOADER_ALLOC_ZONE_FSEG:
+            zone = &ZoneFSeg;
+            break;
+        default:
+            goto err;
+    }
+    if (alloc_align < MALLOC_MIN_ALIGN)
+        alloc_align = MALLOC_MIN_ALIGN;
+    if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1])
+        goto err;
+    file->file = romfile_find(entry->alloc_file);
+    if (!file->file || !file->file->size)
+        return;
+    data = _malloc(zone, MALLOC_DEFAULT_HANDLE, file->file->size, alloc_align);
+    if (!data) {
+        warn_noalloc();
+        return;
+    }
+    ret = file->file->copy(file->file, data, file->file->size);
+    if (ret != file->file->size)
+        goto file_err;
+    file->data = data;
+    files->nfiles++;
+    return;
+
+file_err:
+    free(data);
+err:
+    warn_internalerror();
+}
+
+static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry,
+                                       struct romfile_loader_files *files)
+{
+    struct romfile_loader_file *dest_file;
+    struct romfile_loader_file *src_file;
+    unsigned offset = le32_to_cpu(entry->pointer_offset);
+    u64 pointer = 0;
+
+    dest_file = romfile_loader_find(entry->pointer_dest_file, files);
+    src_file = romfile_loader_find(entry->pointer_src_file, files);
+
+    if (!dest_file || !src_file || !dest_file->data || !src_file->data ||
+        offset + entry->pointer_size < offset ||
+        offset + entry->pointer_size > dest_file->file->size ||
+        entry->pointer_size < 1 || entry->pointer_size > 8 ||
+        entry->pointer_size & (entry->pointer_size - 1))
+        goto err;
+
+    memcpy(&pointer, dest_file->data + offset, entry->pointer_size);
+    pointer = le64_to_cpu(pointer);
+    pointer += (unsigned long)src_file->data;
+    pointer = cpu_to_le64(pointer);
+    memcpy(dest_file->data + offset, &pointer, entry->pointer_size);
+
+    return;
+err:
+    warn_internalerror();
+}
+
+static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry,
+                                        struct romfile_loader_files *files)
+{
+    struct romfile_loader_file *file;
+    unsigned offset = le32_to_cpu(entry->cksum_offset);
+    unsigned start = le32_to_cpu(entry->cksum_start);
+    unsigned len = le32_to_cpu(entry->cksum_length);
+    u8 *data;
+
+    file = romfile_loader_find(entry->cksum_file, files);
+
+    if (!file || !file->data || offset >= file->file->size ||
+        start + len < start || start + len > file->file->size)
+        goto err;
+
+    data = file->data + offset;
+    *data -= checksum(file->data + start, len);
+
+    return;
+err:
+    warn_internalerror();
+}
+
+int romfile_loader_execute(const char *name)
+{
+    struct romfile_loader_entry_s *entry;
+    int size, offset = 0, nfiles;
+    struct romfile_loader_files *files;
+    void *data = romfile_loadfile(name, &size);
+    if (!data)
+        return -1;
+
+    if (size % sizeof(*entry)) {
+        warn_internalerror();
+        goto err;
+    }
+
+    /* (over)estimate the number of files to load. */
+    nfiles = size / sizeof(*entry);
+    files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0]));
+    if (!files) {
+        warn_noalloc();
+        goto err;
+    }
+    files->nfiles = 0;
+
+    for (offset = 0; offset < size; offset += sizeof(*entry)) {
+        entry = data + offset;
+        switch (le32_to_cpu(entry->command)) {
+                case ROMFILE_LOADER_COMMAND_ALLOCATE:
+                        romfile_loader_allocate(entry, files);
+                        break;
+                case ROMFILE_LOADER_COMMAND_ADD_POINTER:
+                        romfile_loader_add_pointer(entry, files);
+                        break;
+                case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM:
+                        romfile_loader_add_checksum(entry, files);
+                default:
+                        /* Skip commands that we don't recognize. */
+                        break;
+        }
+    }
+
+    free(files);
+    free(data);
+    return 0;
+
+err:
+    free(data);
+    return -1;
+}
-- 
MST




More information about the SeaBIOS mailing list