218e99
From f602774bca68ddc853f88cf90c62e1af94390731 Mon Sep 17 00:00:00 2001
218e99
From: Markus Armbruster <armbru@redhat.com>
218e99
Date: Wed, 6 Nov 2013 09:09:09 +0100
218e99
Subject: [PATCH 23/29] smbios: Make multiple -smbios type= accumulate sanely
218e99
218e99
RH-Author: Markus Armbruster <armbru@redhat.com>
218e99
Message-id: <1383386488-29789-7-git-send-email-armbru@redhat.com>
218e99
Patchwork-id: 55244
218e99
O-Subject: [PATCH 7.0 qemu-kvm 06/11] smbios: Make multiple -smbios type= accumulate sanely
218e99
Bugzilla: 994490
218e99
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
218e99
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
218e99
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
218e99
218e99
From: Markus Armbruster <armbru@redhat.com>
218e99
218e99
Currently, -smbios type=T,NAME=VAL,... adds one field (T,NAME) with
218e99
value VAL to fw_cfg for each unique NAME.  If NAME occurs multiple
218e99
times, the last one's VAL is used (before the QemuOpts conversion, the
218e99
first one was used).
218e99
218e99
Multiple -smbios can add multiple fields with the same (T, NAME).
218e99
SeaBIOS reads all of them from fw_cfg, but uses only the first field
218e99
(T, NAME).  The others are ignored.
218e99
218e99
"First one wins, subsequent ones get ignored silently" isn't nice.  We
218e99
commonly let the last option win.  Useful, because it lets you
218e99
-readconfig first, then selectively override with command line
218e99
options.
218e99
218e99
Clean up -smbios to work the common way.  Accumulate the settings,
218e99
with later ones overwriting earlier ones.  Put the result into fw_cfg
218e99
(no more useless duplicates).
218e99
218e99
Bonus cleanup: qemu_uuid_parse() no longer sets SMBIOS system uuid by
218e99
side effect.
218e99
218e99
Signed-off-by: Markus Armbruster <armbru@redhat.com>
218e99
Reviewed-by: Eric Blake <eblake@redhat.com>
218e99
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
218e99
(cherry picked from commit fc3b32958a80bca13309e2695de07b43dd788421)
218e99
218e99
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
218e99
---
218e99
 arch_init.c              |    3 -
218e99
 hw/i386/smbios.c         |  152 +++++++++++++++++++++++++++------------------
218e99
 include/hw/i386/smbios.h |    1 -
218e99
 include/sysemu/sysemu.h  |    1 +
218e99
 vl.c                     |    2 +
218e99
 5 files changed, 94 insertions(+), 65 deletions(-)
218e99
218e99
diff --git a/arch_init.c b/arch_init.c
218e99
index d8c4e2e..5301cfd 100644
218e99
--- a/arch_init.c
218e99
+++ b/arch_init.c
218e99
@@ -1056,9 +1056,6 @@ int qemu_uuid_parse(const char *str, uint8_t *uuid)
218e99
     if (ret != 16) {
218e99
         return -1;
218e99
     }
218e99
-#ifdef TARGET_I386
218e99
-    smbios_add_field(1, offsetof(struct smbios_type_1, uuid), uuid, 16);
218e99
-#endif
218e99
     return 0;
218e99
 }
218e99
 
218e99
diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c
218e99
index 4263551..d2dba6c 100644
218e99
--- a/hw/i386/smbios.c
218e99
+++ b/hw/i386/smbios.c
218e99
@@ -47,6 +47,7 @@ struct smbios_table {
218e99
 static uint8_t *smbios_entries;
218e99
 static size_t smbios_entries_len;
218e99
 static int smbios_type4_count = 0;
218e99
+static bool smbios_immutable;
218e99
 
218e99
 static struct {
218e99
     bool seen;
218e99
@@ -54,6 +55,17 @@ static struct {
218e99
     Location loc;
218e99
 } first_opt[2];
218e99
 
218e99
+static struct {
218e99
+    const char *vendor, *version, *date;
218e99
+    bool have_major_minor;
218e99
+    uint8_t major, minor;
218e99
+} type0;
218e99
+
218e99
+static struct {
218e99
+    const char *manufacturer, *product, *version, *serial, *sku, *family;
218e99
+    /* uuid is in qemu_uuid[] */
218e99
+} type1;
218e99
+
218e99
 static QemuOptsList qemu_smbios_opts = {
218e99
     .name = "smbios",
218e99
     .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head),
218e99
@@ -152,13 +164,6 @@ static void smbios_validate_table(void)
218e99
     }
218e99
 }
218e99
 
218e99
-uint8_t *smbios_get_table(size_t *length)
218e99
-{
218e99
-    smbios_validate_table();
218e99
-    *length = smbios_entries_len;
218e99
-    return smbios_entries;
218e99
-}
218e99
-
218e99
 /*
218e99
  * To avoid unresolvable overlaps in data, don't allow both
218e99
  * tables and fields for the same smbios type.
218e99
@@ -182,12 +187,10 @@ static void smbios_check_collision(int type, int entry)
218e99
     }
218e99
 }
218e99
 
218e99
-void smbios_add_field(int type, int offset, const void *data, size_t len)
218e99
+static void smbios_add_field(int type, int offset, const void *data, size_t len)
218e99
 {
218e99
     struct smbios_field *field;
218e99
 
218e99
-    smbios_check_collision(type, SMBIOS_FIELD_ENTRY);
218e99
-
218e99
     if (!smbios_entries) {
218e99
         smbios_entries_len = sizeof(uint16_t);
218e99
         smbios_entries = g_malloc0(smbios_entries_len);
218e99
@@ -207,82 +210,81 @@ void smbios_add_field(int type, int offset, const void *data, size_t len)
218e99
             cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
218e99
 }
218e99
 
218e99
-static void smbios_build_type_0_fields(QemuOpts *opts)
218e99
+static void smbios_build_type_0_fields(void)
218e99
 {
218e99
-    const char *val;
218e99
-    unsigned char major, minor;
218e99
-
218e99
-    val = qemu_opt_get(opts, "vendor");
218e99
-    if (val) {
218e99
+    if (type0.vendor) {
218e99
         smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type0.vendor, strlen(type0.vendor) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "version");
218e99
-    if (val) {
218e99
+    if (type0.version) {
218e99
         smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type0.version, strlen(type0.version) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "date");
218e99
-    if (val) {
218e99
+    if (type0.date) {
218e99
         smbios_add_field(0, offsetof(struct smbios_type_0,
218e99
                                      bios_release_date_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type0.date, strlen(type0.date) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "release");
218e99
-    if (val) {
218e99
-        if (sscanf(val, "%hhu.%hhu", &major, &minor) != 2) {
218e99
-            error_report("Invalid release");
218e99
-            exit(1);
218e99
-        }
218e99
+    if (type0.have_major_minor) {
218e99
         smbios_add_field(0, offsetof(struct smbios_type_0,
218e99
                                      system_bios_major_release),
218e99
-                         &major, 1);
218e99
+                         &type0.major, 1);
218e99
         smbios_add_field(0, offsetof(struct smbios_type_0,
218e99
                                      system_bios_minor_release),
218e99
-                         &minor, 1);
218e99
+                         &type0.minor, 1);
218e99
     }
218e99
 }
218e99
 
218e99
-static void smbios_build_type_1_fields(QemuOpts *opts)
218e99
+static void smbios_build_type_1_fields(void)
218e99
 {
218e99
-    const char *val;
218e99
-
218e99
-    val = qemu_opt_get(opts, "manufacturer");
218e99
-    if (val) {
218e99
+    if (type1.manufacturer) {
218e99
         smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type1.manufacturer, strlen(type1.manufacturer) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "product");
218e99
-    if (val) {
218e99
+    if (type1.product) {
218e99
         smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type1.product, strlen(type1.product) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "version");
218e99
-    if (val) {
218e99
+    if (type1.version) {
218e99
         smbios_add_field(1, offsetof(struct smbios_type_1, version_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type1.version, strlen(type1.version) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "serial");
218e99
-    if (val) {
218e99
+    if (type1.serial) {
218e99
         smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str),
218e99
-                         val, strlen(val) + 1);
218e99
-    }
218e99
-    val = qemu_opt_get(opts, "uuid");
218e99
-    if (val) {
218e99
-        if (qemu_uuid_parse(val, qemu_uuid) != 0) {
218e99
-            error_report("Invalid UUID");
218e99
-            exit(1);
218e99
-        }
218e99
+                         type1.serial, strlen(type1.serial) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "sku");
218e99
-    if (val) {
218e99
+    if (type1.sku) {
218e99
         smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type1.sku, strlen(type1.sku) + 1);
218e99
     }
218e99
-    val = qemu_opt_get(opts, "family");
218e99
-    if (val) {
218e99
+    if (type1.family) {
218e99
         smbios_add_field(1, offsetof(struct smbios_type_1, family_str),
218e99
-                         val, strlen(val) + 1);
218e99
+                         type1.family, strlen(type1.family) + 1);
218e99
+    }
218e99
+    if (qemu_uuid_set) {
218e99
+        smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
218e99
+                         qemu_uuid, 16);
218e99
+    }
218e99
+}
218e99
+
218e99
+uint8_t *smbios_get_table(size_t *length)
218e99
+{
218e99
+    if (!smbios_immutable) {
218e99
+        smbios_build_type_0_fields();
218e99
+        smbios_build_type_1_fields();
218e99
+        smbios_validate_table();
218e99
+        smbios_immutable = true;
218e99
+    }
218e99
+    *length = smbios_entries_len;
218e99
+    return smbios_entries;
218e99
+}
218e99
+
218e99
+static void save_opt(const char **dest, QemuOpts *opts, const char *name)
218e99
+{
218e99
+    const char *val = qemu_opt_get(opts, name);
218e99
+
218e99
+    if (val) {
218e99
+        *dest = val;
218e99
     }
218e99
 }
218e99
 
218e99
@@ -291,6 +293,7 @@ void smbios_entry_add(QemuOpts *opts)
218e99
     Error *local_err = NULL;
218e99
     const char *val;
218e99
 
218e99
+    assert(!smbios_immutable);
218e99
     val = qemu_opt_get(opts, "file");
218e99
     if (val) {
218e99
         struct smbios_structure_header *header;
218e99
@@ -341,6 +344,8 @@ void smbios_entry_add(QemuOpts *opts)
218e99
     if (val) {
218e99
         unsigned long type = strtoul(val, NULL, 0);
218e99
 
218e99
+        smbios_check_collision(type, SMBIOS_FIELD_ENTRY);
218e99
+
218e99
         switch (type) {
218e99
         case 0:
218e99
             qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err);
218e99
@@ -348,7 +353,18 @@ void smbios_entry_add(QemuOpts *opts)
218e99
                 error_report("%s", error_get_pretty(local_err));
218e99
                 exit(1);
218e99
             }
218e99
-            smbios_build_type_0_fields(opts);
218e99
+            save_opt(&type0.vendor, opts, "vendor");
218e99
+            save_opt(&type0.version, opts, "version");
218e99
+            save_opt(&type0.date, opts, "date");
218e99
+
218e99
+            val = qemu_opt_get(opts, "release");
218e99
+            if (val) {
218e99
+                if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) {
218e99
+                    error_report("Invalid release");
218e99
+                    exit(1);
218e99
+                }
218e99
+                type0.have_major_minor = true;
218e99
+            }
218e99
             return;
218e99
         case 1:
218e99
             qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err);
218e99
@@ -356,7 +372,21 @@ void smbios_entry_add(QemuOpts *opts)
218e99
                 error_report("%s", error_get_pretty(local_err));
218e99
                 exit(1);
218e99
             }
218e99
-            smbios_build_type_1_fields(opts);
218e99
+            save_opt(&type1.manufacturer, opts, "manufacturer");
218e99
+            save_opt(&type1.product, opts, "product");
218e99
+            save_opt(&type1.version, opts, "version");
218e99
+            save_opt(&type1.serial, opts, "serial");
218e99
+            save_opt(&type1.sku, opts, "sku");
218e99
+            save_opt(&type1.family, opts, "family");
218e99
+
218e99
+            val = qemu_opt_get(opts, "uuid");
218e99
+            if (val) {
218e99
+                if (qemu_uuid_parse(val, qemu_uuid) != 0) {
218e99
+                    error_report("Invalid UUID");
218e99
+                    exit(1);
218e99
+                }
218e99
+                qemu_uuid_set = true;
218e99
+            }
218e99
             return;
218e99
         default:
218e99
             error_report("Don't know how to build fields for SMBIOS type %ld",
218e99
diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h
218e99
index d9f43b7..b08ec71 100644
218e99
--- a/include/hw/i386/smbios.h
218e99
+++ b/include/hw/i386/smbios.h
218e99
@@ -16,7 +16,6 @@
218e99
 #include "qemu/option.h"
218e99
 
218e99
 void smbios_entry_add(QemuOpts *opts);
218e99
-void smbios_add_field(int type, int offset, const void *data, size_t len);
218e99
 uint8_t *smbios_get_table(size_t *length);
218e99
 
218e99
 /*
218e99
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
218e99
index 76c5b64..8fbc818 100644
218e99
--- a/include/sysemu/sysemu.h
218e99
+++ b/include/sysemu/sysemu.h
218e99
@@ -16,6 +16,7 @@ extern const char *bios_name;
218e99
 
218e99
 extern const char *qemu_name;
218e99
 extern uint8_t qemu_uuid[];
218e99
+extern bool qemu_uuid_set;
218e99
 int qemu_uuid_parse(const char *str, uint8_t *uuid);
218e99
 
218e99
 #define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
218e99
diff --git a/vl.c b/vl.c
218e99
index e82eb1c..19cc3b2 100644
218e99
--- a/vl.c
218e99
+++ b/vl.c
218e99
@@ -256,6 +256,7 @@ uint64_t node_mem[MAX_NODES];
218e99
 unsigned long *node_cpumask[MAX_NODES];
218e99
 
218e99
 uint8_t qemu_uuid[16];
218e99
+bool qemu_uuid_set;
218e99
 
218e99
 static QEMUBootSetHandler *boot_set_handler;
218e99
 static void *boot_set_opaque;
218e99
@@ -3573,6 +3574,7 @@ int main(int argc, char **argv, char **envp)
218e99
                             " Wrong format.\n");
218e99
                     exit(1);
218e99
                 }
218e99
+                qemu_uuid_set = true;
218e99
                 break;
218e99
 	    case QEMU_OPTION_option_rom:
218e99
 		if (nb_option_roms >= MAX_OPTION_ROMS) {
218e99
-- 
218e99
1.7.1
218e99