Skip to content

Commit

Permalink
smbios: Encode UUID according to SMBIOS specification
Browse files Browse the repository at this point in the history
Differently from older versions, SMBIOS version 2.6 is explicit about
the encoding of UUID fields:

> Although RFC 4122 recommends network byte order for all fields, the PC
> industry (including the ACPI, UEFI, and Microsoft specifications) has
> consistently used little-endian byte encoding for the first three fields:
> time_low, time_mid, time_hi_and_version. The same encoding, also known as
> wire format, should also be used for the SMBIOS representation of the UUID.
>
> The UUID {00112233-4455-6677-8899-AABBCCDDEEFF} would thus be represented
> as 33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF.

The dmidecode tool implements this and decodes the above "wire format"
when SMBIOS version >= 2.6. We moved from SMBIOS version 2.4 to 2.8 when
we started building the SMBIOS entry point inside QEMU, on commit
c97294e.

Change smbios_build_type_1_table() to encode the UUID as specified.

To make sure we won't change the guest-visible UUID when upgrading to a
newer QEMU version, keep the old behavior on pc-*-2.1 and older.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
  • Loading branch information
ehabkost authored and mstsirkin committed Nov 2, 2014
1 parent 2cad57c commit caad057
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 7 deletions.
4 changes: 3 additions & 1 deletion hw/i386/pc_piix.c
Expand Up @@ -63,6 +63,7 @@ static bool has_acpi_build = true;
static int legacy_acpi_table_size;
static bool smbios_defaults = true;
static bool smbios_legacy_mode;
static bool smbios_uuid_encoded = true;
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
* pages in the host.
Expand Down Expand Up @@ -172,7 +173,7 @@ static void pc_init1(MachineState *machine,
MachineClass *mc = MACHINE_GET_CLASS(machine);
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
mc->name, smbios_legacy_mode);
mc->name, smbios_legacy_mode, smbios_uuid_encoded);
}

/* allocate ram and load rom/bios */
Expand Down Expand Up @@ -304,6 +305,7 @@ static void pc_init_pci(MachineState *machine)

static void pc_compat_2_1(MachineState *machine)
{
smbios_uuid_encoded = false;
}

static void pc_compat_2_0(MachineState *machine)
Expand Down
4 changes: 3 additions & 1 deletion hw/i386/pc_q35.c
Expand Up @@ -52,6 +52,7 @@
static bool has_acpi_build = true;
static bool smbios_defaults = true;
static bool smbios_legacy_mode;
static bool smbios_uuid_encoded = true;
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
* pages in the host.
Expand Down Expand Up @@ -163,7 +164,7 @@ static void pc_q35_init(MachineState *machine)
MachineClass *mc = MACHINE_GET_CLASS(machine);
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
mc->name, smbios_legacy_mode);
mc->name, smbios_legacy_mode, smbios_uuid_encoded);
}

/* allocate ram and load rom/bios */
Expand Down Expand Up @@ -283,6 +284,7 @@ static void pc_q35_init(MachineState *machine)

static void pc_compat_2_1(MachineState *machine)
{
smbios_uuid_encoded = false;
}

static void pc_compat_2_0(MachineState *machine)
Expand Down
27 changes: 24 additions & 3 deletions hw/i386/smbios.c
Expand Up @@ -48,6 +48,7 @@ struct smbios_table {
static uint8_t *smbios_entries;
static size_t smbios_entries_len;
static bool smbios_legacy = true;
static bool smbios_uuid_encoded = true;
/* end: legacy structures & constants for <= 2.0 machines */


Expand Down Expand Up @@ -391,6 +392,11 @@ static void smbios_build_type_1_fields(void)
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
type1.family);
if (qemu_uuid_set) {
/* We don't encode the UUID in the "wire format" here because this
* function is for legacy mode and needs to keep the guest ABI, and
* because we don't know what's the SMBIOS version advertised by the
* BIOS.
*/
smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
qemu_uuid, 16);
}
Expand Down Expand Up @@ -523,6 +529,19 @@ static void smbios_build_type_0_table(void)
SMBIOS_BUILD_TABLE_POST;
}

/* Encode UUID from the big endian encoding described on RFC4122 to the wire
* format specified by SMBIOS version 2.6.
*/
static void smbios_encode_uuid(struct smbios_uuid *uuid, const uint8_t *buf)
{
memcpy(uuid, buf, 16);
if (smbios_uuid_encoded) {
uuid->time_low = bswap32(uuid->time_low);
uuid->time_mid = bswap16(uuid->time_mid);
uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version);
}
}

static void smbios_build_type_1_table(void)
{
SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */
Expand All @@ -532,9 +551,9 @@ static void smbios_build_type_1_table(void)
SMBIOS_TABLE_SET_STR(1, version_str, type1.version);
SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial);
if (qemu_uuid_set) {
memcpy(t->uuid, qemu_uuid, 16);
smbios_encode_uuid(&t->uuid, qemu_uuid);
} else {
memset(t->uuid, 0, 16);
memset(&t->uuid, 0, 16);
}
t->wake_up_type = 0x06; /* power switch */
SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku);
Expand Down Expand Up @@ -746,10 +765,12 @@ void smbios_set_cpuid(uint32_t version, uint32_t features)
}

void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version, bool legacy_mode)
const char *version, bool legacy_mode,
bool uuid_encoded)
{
smbios_have_defaults = true;
smbios_legacy = legacy_mode;
smbios_uuid_encoded = uuid_encoded;

/* drop unwanted version of command-line file blob(s) */
if (smbios_legacy) {
Expand Down
17 changes: 15 additions & 2 deletions include/hw/i386/smbios.h
Expand Up @@ -20,7 +20,8 @@
void smbios_entry_add(QemuOpts *opts);
void smbios_set_cpuid(uint32_t version, uint32_t features);
void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version, bool legacy_mode);
const char *version, bool legacy_mode,
bool uuid_encoded);
uint8_t *smbios_get_table_legacy(size_t *length);
void smbios_get_tables(uint8_t **tables, size_t *tables_len,
uint8_t **anchor, size_t *anchor_len);
Expand Down Expand Up @@ -72,14 +73,26 @@ struct smbios_type_0 {
uint8_t embedded_controller_minor_release;
} QEMU_PACKED;

/* UUID encoding. The time_* fields are little-endian, as specified by SMBIOS
* version 2.6.
*/
struct smbios_uuid {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_and_version;
uint8_t clock_seq_hi_and_reserved;
uint8_t clock_seq_low;
uint8_t node[6];
} QEMU_PACKED;

/* SMBIOS type 1 - System Information */
struct smbios_type_1 {
struct smbios_structure_header header;
uint8_t manufacturer_str;
uint8_t product_name_str;
uint8_t version_str;
uint8_t serial_number_str;
uint8_t uuid[16];
struct smbios_uuid uuid;
uint8_t wake_up_type;
uint8_t sku_number_str;
uint8_t family_str;
Expand Down
3 changes: 3 additions & 0 deletions vl.c
Expand Up @@ -190,6 +190,9 @@ int nb_numa_nodes;
int max_numa_nodeid;
NodeInfo numa_info[MAX_NODES];

/* The bytes in qemu_uuid[] are in the order specified by RFC4122, _not_ in the
* little-endian "wire format" described in the SMBIOS 2.6 specification.
*/
uint8_t qemu_uuid[16];
bool qemu_uuid_set;

Expand Down

0 comments on commit caad057

Please sign in to comment.