Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / seabios / src / fw / romfile_loader.c
diff --git a/qemu/roms/seabios/src/fw/romfile_loader.c b/qemu/roms/seabios/src/fw/romfile_loader.c
new file mode 100644 (file)
index 0000000..f4b17ff
--- /dev/null
@@ -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, 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;
+}