#include <stdint.h>
#include <string.h>
#include <errno.h>
+#include <byteswap.h>
#include <ipxe/settings.h>
#include <ipxe/init.h>
#include <ipxe/uuid.h>
{
uint8_t buf[structure.header.len];
+ const void *raw;
+ union uuid uuid;
/* Read SMBIOS structure */
if ( ( rc = read_smbios_structure ( &structure, buf,
sizeof ( buf ) ) ) != 0 )
return rc;
+ /* A tag length of zero indicates a string */
if ( tag_len == 0 ) {
- /* String */
if ( ( rc = read_smbios_string ( &structure,
buf[tag_offset],
data, len ) ) < 0 ) {
if ( ! setting->type )
setting->type = &setting_type_string;
return rc;
- } else {
- /* Raw data */
- if ( len > tag_len )
- len = tag_len;
- memcpy ( data, &buf[tag_offset], len );
- if ( ! setting->type )
- setting->type = &setting_type_hex;
- return tag_len;
}
+
+ /* Mangle UUIDs if necessary. iPXE treats UUIDs as
+ * being in network byte order (big-endian). SMBIOS
+ * specification version 2.6 states that UUIDs are
+ * stored with little-endian values in the first three
+ * fields; earlier versions did not specify an
+ * endianness. dmidecode assumes that the byte order
+ * is little-endian if and only if the SMBIOS version
+ * is 2.6 or higher; we match this behaviour.
+ */
+ raw = &buf[tag_offset];
+ if ( ( setting->type == &setting_type_uuid ) &&
+ ( tag_len == sizeof ( uuid ) ) &&
+ ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) {
+ DBG ( "SMBIOS detected mangled UUID\n" );
+ memcpy ( &uuid, &buf[tag_offset], sizeof ( uuid ) );
+ __bswap_32s ( &uuid.canonical.a );
+ __bswap_16s ( &uuid.canonical.b );
+ __bswap_16s ( &uuid.canonical.c );
+ raw = &uuid;
+ }
+
+ /* Return data */
+ if ( len > tag_len )
+ len = tag_len;
+ memcpy ( data, raw, len );
+ if ( ! setting->type )
+ setting->type = &setting_type_hex;
+ return tag_len;
}
}