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