Skip to content

Commit

Permalink
smbios: Make multiple -smbios type= accumulate sanely
Browse files Browse the repository at this point in the history
Currently, -smbios type=T,NAME=VAL,... adds one field (T,NAME) with
value VAL to fw_cfg for each unique NAME.  If NAME occurs multiple
times, the last one's VAL is used (before the QemuOpts conversion, the
first one was used).

Multiple -smbios can add multiple fields with the same (T, NAME).
SeaBIOS reads all of them from fw_cfg, but uses only the first field
(T, NAME).  The others are ignored.

"First one wins, subsequent ones get ignored silently" isn't nice.  We
commonly let the last option win.  Useful, because it lets you
-readconfig first, then selectively override with command line
options.

Clean up -smbios to work the common way.  Accumulate the settings,
with later ones overwriting earlier ones.  Put the result into fw_cfg
(no more useless duplicates).

Bonus cleanup: qemu_uuid_parse() no longer sets SMBIOS system uuid by
side effect.

Signed-off-by: Markus Armbruster <[email protected]>
Reviewed-by: Eric Blake <[email protected]>
Signed-off-by: Michael S. Tsirkin <[email protected]>
  • Loading branch information
Markus Armbruster authored and mstsirkin committed Sep 28, 2013
1 parent ec2df8c commit fc3b329
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 65 deletions.
3 changes: 0 additions & 3 deletions arch_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -1113,9 +1113,6 @@ int qemu_uuid_parse(const char *str, uint8_t *uuid)
if (ret != 16) {
return -1;
}
#ifdef TARGET_I386
smbios_add_field(1, offsetof(struct smbios_type_1, uuid), uuid, 16);
#endif
return 0;
}

Expand Down
152 changes: 91 additions & 61 deletions hw/i386/smbios.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,25 @@ struct smbios_table {
static uint8_t *smbios_entries;
static size_t smbios_entries_len;
static int smbios_type4_count = 0;
static bool smbios_immutable;

static struct {
bool seen;
int headertype;
Location loc;
} first_opt[2];

static struct {
const char *vendor, *version, *date;
bool have_major_minor;
uint8_t major, minor;
} type0;

static struct {
const char *manufacturer, *product, *version, *serial, *sku, *family;
/* uuid is in qemu_uuid[] */
} type1;

static QemuOptsList qemu_smbios_opts = {
.name = "smbios",
.head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head),
Expand Down Expand Up @@ -152,13 +164,6 @@ static void smbios_validate_table(void)
}
}

uint8_t *smbios_get_table(size_t *length)
{
smbios_validate_table();
*length = smbios_entries_len;
return smbios_entries;
}

/*
* To avoid unresolvable overlaps in data, don't allow both
* tables and fields for the same smbios type.
Expand All @@ -182,12 +187,10 @@ static void smbios_check_collision(int type, int entry)
}
}

void smbios_add_field(int type, int offset, const void *data, size_t len)
static void smbios_add_field(int type, int offset, const void *data, size_t len)
{
struct smbios_field *field;

smbios_check_collision(type, SMBIOS_FIELD_ENTRY);

if (!smbios_entries) {
smbios_entries_len = sizeof(uint16_t);
smbios_entries = g_malloc0(smbios_entries_len);
Expand All @@ -207,82 +210,81 @@ void smbios_add_field(int type, int offset, const void *data, size_t len)
cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
}

static void smbios_build_type_0_fields(QemuOpts *opts)
static void smbios_build_type_0_fields(void)
{
const char *val;
unsigned char major, minor;

val = qemu_opt_get(opts, "vendor");
if (val) {
if (type0.vendor) {
smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str),
val, strlen(val) + 1);
type0.vendor, strlen(type0.vendor) + 1);
}
val = qemu_opt_get(opts, "version");
if (val) {
if (type0.version) {
smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str),
val, strlen(val) + 1);
type0.version, strlen(type0.version) + 1);
}
val = qemu_opt_get(opts, "date");
if (val) {
if (type0.date) {
smbios_add_field(0, offsetof(struct smbios_type_0,
bios_release_date_str),
val, strlen(val) + 1);
type0.date, strlen(type0.date) + 1);
}
val = qemu_opt_get(opts, "release");
if (val) {
if (sscanf(val, "%hhu.%hhu", &major, &minor) != 2) {
error_report("Invalid release");
exit(1);
}
if (type0.have_major_minor) {
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_major_release),
&major, 1);
&type0.major, 1);
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_minor_release),
&minor, 1);
&type0.minor, 1);
}
}

static void smbios_build_type_1_fields(QemuOpts *opts)
static void smbios_build_type_1_fields(void)
{
const char *val;

val = qemu_opt_get(opts, "manufacturer");
if (val) {
if (type1.manufacturer) {
smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str),
val, strlen(val) + 1);
type1.manufacturer, strlen(type1.manufacturer) + 1);
}
val = qemu_opt_get(opts, "product");
if (val) {
if (type1.product) {
smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str),
val, strlen(val) + 1);
type1.product, strlen(type1.product) + 1);
}
val = qemu_opt_get(opts, "version");
if (val) {
if (type1.version) {
smbios_add_field(1, offsetof(struct smbios_type_1, version_str),
val, strlen(val) + 1);
type1.version, strlen(type1.version) + 1);
}
val = qemu_opt_get(opts, "serial");
if (val) {
if (type1.serial) {
smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str),
val, strlen(val) + 1);
}
val = qemu_opt_get(opts, "uuid");
if (val) {
if (qemu_uuid_parse(val, qemu_uuid) != 0) {
error_report("Invalid UUID");
exit(1);
}
type1.serial, strlen(type1.serial) + 1);
}
val = qemu_opt_get(opts, "sku");
if (val) {
if (type1.sku) {
smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str),
val, strlen(val) + 1);
type1.sku, strlen(type1.sku) + 1);
}
val = qemu_opt_get(opts, "family");
if (val) {
if (type1.family) {
smbios_add_field(1, offsetof(struct smbios_type_1, family_str),
val, strlen(val) + 1);
type1.family, strlen(type1.family) + 1);
}
if (qemu_uuid_set) {
smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
qemu_uuid, 16);
}
}

uint8_t *smbios_get_table(size_t *length)
{
if (!smbios_immutable) {
smbios_build_type_0_fields();
smbios_build_type_1_fields();
smbios_validate_table();
smbios_immutable = true;
}
*length = smbios_entries_len;
return smbios_entries;
}

static void save_opt(const char **dest, QemuOpts *opts, const char *name)
{
const char *val = qemu_opt_get(opts, name);

if (val) {
*dest = val;
}
}

Expand All @@ -291,6 +293,7 @@ void smbios_entry_add(QemuOpts *opts)
Error *local_err = NULL;
const char *val;

assert(!smbios_immutable);
val = qemu_opt_get(opts, "file");
if (val) {
struct smbios_structure_header *header;
Expand Down Expand Up @@ -341,22 +344,49 @@ void smbios_entry_add(QemuOpts *opts)
if (val) {
unsigned long type = strtoul(val, NULL, 0);

smbios_check_collision(type, SMBIOS_FIELD_ENTRY);

switch (type) {
case 0:
qemu_opts_validate(opts, qemu_smbios_type0_opts, &local_err);
if (local_err) {
error_report("%s", error_get_pretty(local_err));
exit(1);
}
smbios_build_type_0_fields(opts);
save_opt(&type0.vendor, opts, "vendor");
save_opt(&type0.version, opts, "version");
save_opt(&type0.date, opts, "date");

val = qemu_opt_get(opts, "release");
if (val) {
if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) {
error_report("Invalid release");
exit(1);
}
type0.have_major_minor = true;
}
return;
case 1:
qemu_opts_validate(opts, qemu_smbios_type1_opts, &local_err);
if (local_err) {
error_report("%s", error_get_pretty(local_err));
exit(1);
}
smbios_build_type_1_fields(opts);
save_opt(&type1.manufacturer, opts, "manufacturer");
save_opt(&type1.product, opts, "product");
save_opt(&type1.version, opts, "version");
save_opt(&type1.serial, opts, "serial");
save_opt(&type1.sku, opts, "sku");
save_opt(&type1.family, opts, "family");

val = qemu_opt_get(opts, "uuid");
if (val) {
if (qemu_uuid_parse(val, qemu_uuid) != 0) {
error_report("Invalid UUID");
exit(1);
}
qemu_uuid_set = true;
}
return;
default:
error_report("Don't know how to build fields for SMBIOS type %ld",
Expand Down
1 change: 0 additions & 1 deletion include/hw/i386/smbios.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include "qemu/option.h"

void smbios_entry_add(QemuOpts *opts);
void smbios_add_field(int type, int offset, const void *data, size_t len);
uint8_t *smbios_get_table(size_t *length);

/*
Expand Down
1 change: 1 addition & 0 deletions include/sysemu/sysemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern const char *bios_name;

extern const char *qemu_name;
extern uint8_t qemu_uuid[];
extern bool qemu_uuid_set;
int qemu_uuid_parse(const char *str, uint8_t *uuid);
#define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"

Expand Down
2 changes: 2 additions & 0 deletions vl.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ uint64_t node_mem[MAX_NODES];
unsigned long *node_cpumask[MAX_NODES];

uint8_t qemu_uuid[16];
bool qemu_uuid_set;

static QEMUBootSetHandler *boot_set_handler;
static void *boot_set_opaque;
Expand Down Expand Up @@ -3588,6 +3589,7 @@ int main(int argc, char **argv, char **envp)
" Wrong format.\n");
exit(1);
}
qemu_uuid_set = true;
break;
case QEMU_OPTION_option_rom:
if (nb_option_roms >= MAX_OPTION_ROMS) {
Expand Down

0 comments on commit fc3b329

Please sign in to comment.