[settings] Support formatting UUIDs as little-endian GUIDs

The RFC4122 specification defines UUIDs as being in network byte
order, but an unfortunately significant amount of (mostly Microsoft)
software treats them as having the first three fields in little-endian
byte order.

In an ideal world, any server-side software that compares UUIDs for
equality would perform an endian-insensitive comparison (analogous to
comparing strings for equality using a case-insensitive comparison),
and would therefore not care about byte order differences.

Define a setting type name ":guid" to allow a UUID setting to be
formatted in little-endian order, to simplify interoperability with
server-side software that expects such a formatting.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/core/settings.c b/src/core/settings.c
index 430cdc8..fcdf98d 100644
--- a/src/core/settings.c
+++ b/src/core/settings.c
@@ -2199,7 +2199,7 @@
 };
 
 /**
- * Format UUID setting value
+ * Format UUID/GUID setting value
  *
  * @v type		Setting type
  * @v raw		Raw setting value
@@ -2208,17 +2208,24 @@
  * @v len		Length of buffer
  * @ret len		Length of formatted value, or negative error
  */
-static int format_uuid_setting ( const struct setting_type *type __unused,
+static int format_uuid_setting ( const struct setting_type *type,
 				 const void *raw, size_t raw_len, char *buf,
 				 size_t len ) {
-	const union uuid *uuid = raw;
+	union uuid uuid;
 
 	/* Range check */
-	if ( raw_len != sizeof ( *uuid ) )
+	if ( raw_len != sizeof ( uuid ) )
 		return -ERANGE;
 
+	/* Copy value */
+	memcpy ( &uuid, raw, sizeof ( uuid ) );
+
+	/* Mangle GUID byte ordering */
+	if ( type == &setting_type_guid )
+		uuid_mangle ( &uuid );
+
 	/* Format value */
-	return snprintf ( buf, len, "%s", uuid_ntoa ( uuid ) );
+	return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) );
 }
 
 /** UUID setting type */
@@ -2227,6 +2234,12 @@
 	.format = format_uuid_setting,
 };
 
+/** GUID setting type */
+const struct setting_type setting_type_guid __setting_type = {
+	.name = "guid",
+	.format = format_uuid_setting,
+};
+
 /**
  * Format PCI bus:dev.fn setting value
  *
diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h
index f463e66..e042b97 100644
--- a/src/include/ipxe/settings.h
+++ b/src/include/ipxe/settings.h
@@ -426,6 +426,7 @@
 extern const struct setting_type setting_type_hexraw __setting_type;
 extern const struct setting_type setting_type_base64 __setting_type;
 extern const struct setting_type setting_type_uuid __setting_type;
+extern const struct setting_type setting_type_guid __setting_type;
 extern const struct setting_type setting_type_busdevfn __setting_type;
 extern const struct setting_type setting_type_dnssl __setting_type;
 
diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c
index 2d571f2..ec31b43 100644
--- a/src/interface/smbios/smbios_settings.c
+++ b/src/interface/smbios/smbios_settings.c
@@ -140,7 +140,8 @@
 		 * is 2.6 or higher; we match this behaviour.
 		 */
 		raw = &buf[tag_offset];
-		if ( ( setting->type == &setting_type_uuid ) &&
+		if ( ( ( setting->type == &setting_type_uuid ) ||
+		       ( setting->type == &setting_type_guid ) ) &&
 		     ( tag_len == sizeof ( uuid ) ) &&
 		     ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) {
 			DBG ( "SMBIOS detected mangled UUID\n" );
diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c
index 828901b..5da7eb0 100644
--- a/src/tests/settings_test.c
+++ b/src/tests/settings_test.c
@@ -250,6 +250,12 @@
 	.type = &setting_type_uuid,
 };
 
+/** Test GUID setting type */
+static struct setting test_guid_setting = {
+	.name = "test_guid",
+	.type = &setting_type_guid,
+};
+
 /** Test PCI bus:dev.fn setting type */
 static struct setting test_busdevfn_setting = {
 	.name = "test_busdevfn",
@@ -419,6 +425,10 @@
 		    RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8,
 			  0x7a, 0x7c, 0xfe, 0x4f, 0xca, 0x4a, 0x57 ),
 		    "1a6a749d-0eda-461a-a87a-7cfe4fca4a57" );
+	fetchf_ok ( &test_settings, &test_guid_setting,
+		    RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8,
+			  0x7a, 0x7c, 0xfe, 0x4f, 0xca, 0x4a, 0x57 ),
+		    "9d746a1a-da0e-1a46-a87a-7cfe4fca4a57" );
 
 	/* "busdevfn" setting type (no store capability) */
 	fetchf_ok ( &test_settings, &test_busdevfn_setting,