The length of this post is directly related to the number of hours I’ve wasted working this all out so you don’t have to. You have been warned…

SMBIOS data contains all sorts of useful information about the system, such as manufacturer name, product, serial number and memory configuration. When using Xen PVHVM virtualisation there are default value that are set to tell the domU some information about the host. In Linux the tool to read this data is dmidecode. For example,

root@domu:/# dmidecode
# dmidecode 2.12
SMBIOS 2.4 present.
12 structures occupying 342 bytes.
Table at 0xFC001000.

Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
	Vendor: Xen
	Version: 4.4.1
	Release Date: 06/11/2015
	Address: 0xE8000
	Runtime Size: 96 kB
	ROM Size: 64 kB
	Characteristics:
		PCI is supported
		EDD is supported
		Targeted content distribution is supported
	BIOS Revision: 4.4

Handle 0x0100, DMI type 1, 27 bytes
System Information
	Manufacturer: Xen
	Product Name: HVM domU
	Version: 4.4.1
	Serial Number: 7b84005b-456e-40f1-b896-804a4a91bd6e
	UUID: 7B84005B-456E-40F1-B896-804A4A91BD6E
	Wake-up Type: Power Switch
	SKU Number: Not Specified
	Family: Not Specified
...

It would be nice to be able to update some of this information to pass data from the host to the domU. In this case hopefully to provide information to iPXE for netbooting the host, but could be for other reasons, such as finding out which physical host the domU is running on (though lldpd works really well for that).

But finding out how to set this data has proved hard. There seems to be no good documentation about it on the Internet anywhere. Having spent far too long working out how to get this to actually work, the following explanation might help.

SMBIOS is presented as a set of tables of data. Table type 0 contains BIOS information; type 1, system information; type 2, motherboard data, and many more. These tables are specified in a document, the System Management BIOS (SMBIOS) Reference Specification.

Don’t use QEMU options

When Xen is running in PVHVM mode it emulates the BIOS and hardware using QEMU. The first search result was to find that QEMU has a command-line option, “-smbios”, which can set SMBIOS data. Using this option it is possible to set a file to load, or specific values for type 0 or type 1 data. For example

-smbios type=1,manufacturer=Myself

So the problem then becomes how to pass these arguments into Xen for the domU. A search eventually found the “device_model_args” option for the domU configuration, so the following would go in the config file:

device_model_args = [ '-smbios', 'type=1,manufacturer=Myself' ]

This passes the options to QEMU as intended, as can be seen by running ps | grep qemu on the host, but unfortunately doesn’t have any effect in the guest. It turns out that SMBIOS data to Xen domU guests is provided not by QEMU, but by the domU loader, hvmloader.

Method 1: smbios_firmware

So there are currently at least two ways to set the SMBIOS data with hvmloader. The first is to use the smbios_firmware option in the xl.cfg file. That is documented in the man page, but is rather confusing as it doesn’t explain in detail what that file should exactly contain.

The file contains one or more SMBIOS tables, each preceded by a 32bit length field. The length is necessary because the code that reads this file has to skip over the different tables that may be present and there is no length in the actual data, so without a full parser this would be hard.

But what data to use for the file? It is possible to read the specification and put together some data that way, but the easiest is to take the data from a running system and modify it. Thankfully, dmidecode can output the raw data that can be edited. Let’s look at the type 1 (system information) table:

root@domu:/# dmidecode -t1 -u
# dmidecode 2.12
SMBIOS 2.4 present.

Handle 0x0100, DMI type 1, 27 bytes
        Header and Data:
                01 1B 00 01 01 02 03 04 09 94 42 3A 7B 0E 4D 0D
                80 EE BE 99 D8 10 75 68 06 00 00
        Strings:
                58 58 58 00
                "Xen"
                48 58 58 20 64 6F 6D 55 00
                "HVM domU"
                34 2E 34 2E 31 00
                "4.4.1"
                30 39 39 34 34 32 33 61 2D 37 62 30 65 2D 34 64
                30 64 2D 38 30 65 65 2D 62 65 39 39 64 38 31 30
                37 35 36 38 00
                "0994423a-7b0e-4d0d-80ee-be99d8107568"

The format (see the spec) is a short 4-octet header followed by the data, followed by strings. Each string is numbered from 01, and referred to by number in the data section. In the above the header is ’01 1B 00 01′ (table type 1, length 1B, handle 00 01), then for example the first three values are strings, ’01 02 03′, which refer to the NULL-terminated strings following the data section, namely in this case, “Xen”, “HVM domU” and “4.4.1”.

So we can easily edit the binary values of the strings here. Let’s update the manufacturer from “Xen” to “James”:

# dmidecode 2.12
SMBIOS 2.4 present.

Handle 0x0100, DMI type 1, 27 bytes
        Header and Data:
                01 1B 00 01 01 02 03 04 09 94 42 3A 7B 0E 4D 0D
                80 EE BE 99 D8 10 75 68 06 00 00
        Strings:
                4A 61 6D 65 73 00
                "Xen (this string doesn't matter)"
                48 58 58 20 64 6F 6D 55 00
                "HVM domU"
                34 2E 34 2E 31 00
                "4.4.1"
                30 39 39 34 34 32 33 61 2D 37 62 30 65 2D 34 64
                30 64 2D 38 30 65 65 2D 62 65 39 39 64 38 31 30
                37 35 36 38 00
                "0994423a-7b0e-4d0d-80ee-be99d8107568"

Then we need to convert this to a binary file for the smbios_firmware option. Assuming we sent the output of dmidecode to “smi.txt” and have edited that, the following will do this quite nicely:

$ awk '/^\t\t[^"]/' smi.txt | xxd -r -ps > smbios.bin

However, using this with Xen won’t work quite yet. Remember the length required at the start? It also turns out that the SMBIOS spec calls for a second terminating NULL after the strings that dmidecode doesn’t show us, so let’s add these. Start with the terminating NULL:

# dmidecode 2.12
SMBIOS 2.4 present.

Handle 0x0100, DMI type 1, 27 bytes
        Header and Data:
                01 1B 00 01 01 02 03 04 09 94 42 3A 7B 0E 4D 0D
                80 EE BE 99 D8 10 75 68 06 00 00
        Strings:
                4A 61 6D 65 73 00
                "Xen"
                48 58 58 20 64 6F 6D 55 00
                "HVM domU"
                34 2E 34 2E 31 00
                "4.4.1"
                30 39 39 34 34 32 33 61 2D 37 62 30 65 2D 34 64
                30 64 2D 38 30 65 65 2D 62 65 39 39 64 38 31 30
                37 35 36 38 00
                "0994423a-7b0e-4d0d-80ee-be99d8107568"

                00

(remember to indent the new line with two TABs, not spaces, or add the 00 on the end of the last string). We can then convert this to a binary file again with the above command:

$ awk '/^\t\t[^"]/' smi.txt | xxd -r -ps > smbios.bin
$ ls -l smbios.bin
-rw-r--r-- 1 user user 86 Dec  4 22:24 smbios.bin
$

In this case we ended up with a 86 byte file. This is the length of the actual table. This is 56 in hex, and we’re using a little-endian x86 system, so update the text file once again to add the length to the start (again, two tabs, because awk regex):

# dmidecode 2.12
SMBIOS 2.4 present.
                56 00 00 00

Handle 0x0100, DMI type 1, 27 bytes
        Header and Data:
                01 1B 00 01 01 02 03 04 09 94 42 3A 7B 0E 4D 0D
...

Finally, boot up the domU and run dmidecode, and the manufacturer should now be “James”! Multiple tables can be included in the binary file – each should follow the above format (length then SMBIOS tables data), so the easiest is probably to create each table file separately as above, then concatenate them for the smbios_firmware file.

Method 2: xenstore data

The second way of setting this data is slightly easier, but I have yet to find out a way to set the data in the domU configuration file so the domain has to be created paused, the data set, and then resumed.

Proceed as follows. Start the domain up as usual, but paused:

# xl create -p domu.cfg

Now get the domain id for the new domU:

# xl list domu
Name             ID   Mem VCPUs      State   Time(s)
domu             20  1024     2     -b----     311.3
#

In this case we find that our new domU has domain ID ’20’.

We can now set SMBIOS options in the paused domain:

# xenstore-write "/local/domain/20/bios-strings/system-manufacturer" "James"

and unpause the domain:

# xl unpause domu

When the system has booted up, dmidecode should now show the new system manufacturer as “James” rather than “Xen”. Note that once the domU has booted then changing these bios-strings options will have no effect. They have to be set before hvmloader creates the SMBIOS tables at boot time.

A complete lists of possible options that can be set is found in the source header files. It would just be nice to be able to set these in the domu.cfg file rather than having to set the in the paused domain. But at least this method can easily be scripted, so it’s not too much of a problem.

So, there we go. Two ways to set SMBIOS data for Xen domUs.

Tested with Xen 4.4 on Debian 8.0 (Jessie). With thanks to Terry Burton for letting me know that this might be possible, but not indicating how long it would take…

2 Thoughts on “Setting custom SMBIOS data in Xen DomUs”

  • uoah man! this is the only resource on the web I’ve found explaining exactly how to use the smbios_firmware option!

    successfully used your “method 1” to install Windows 2016 server OEM not recognizing my LENOVO hardware due to the hypervisor!

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *