acpi: Be more ACPI compliant when generating _UID

* Add function to generate unique _UID using CRC32
* Add function to write the _UID based on a device's ACPI path

ACPI devices that have the same _HID must use different _UID.
Linux doesn't care about _UID if it's not used.
Windows 10 verifies the ACPI code on boot and BSODs if two devices
with the same _HID share the same _UID.

Fixes BSOD seen on Windows 10.

Change-Id: I47cd5396060d325f9ce338afced6af021e7ff2b4
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/37695
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
diff --git a/Documentation/acpi/index.md b/Documentation/acpi/index.md
index 8add8db..c378722 100644
--- a/Documentation/acpi/index.md
+++ b/Documentation/acpi/index.md
@@ -2,6 +2,8 @@
 
 This section contains documentation about coreboot on ACPI.
 
+- [SSDT UID generation](uid.md)
+
 ## GPIO
 
 - [GPIO toggling in ACPI AML](gpio.md)
diff --git a/Documentation/acpi/uid.md b/Documentation/acpi/uid.md
new file mode 100644
index 0000000..0354bc5
--- /dev/null
+++ b/Documentation/acpi/uid.md
@@ -0,0 +1,14 @@
+# ACPI SSDT \_UID generation
+
+According to the ACPI spec:
+
+> The _UID must be unique across all devices with either a common _HID or _CID.
+
+
+When generating SSDTs in coreboot the independent drivers don't know
+which \_UID is already in use for a specific \_HID or \_CID. To generate
+unique \_UIDs the ACPI device's path is hashed and used as ID. As every ACPI
+device has a different path, the hash will be also different for every device.
+
+Windows 10 verifies all devices with the same \_HID or \_CID and makes
+sure that no \_UID is duplicated. If it is it'll BSOD.
diff --git a/src/arch/x86/acpi_device.c b/src/arch/x86/acpi_device.c
index 5c77953..1d79170 100644
--- a/src/arch/x86/acpi_device.c
+++ b/src/arch/x86/acpi_device.c
@@ -18,6 +18,8 @@
 #include <device/device.h>
 #include <device/path.h>
 #include <stdlib.h>
+#include <crc_byte.h>
+
 #if CONFIG(GENERIC_GPIO_LIB)
 #include <gpio.h>
 #endif
@@ -98,6 +100,19 @@
 	return NULL;
 }
 
+/*
+ * Generate unique ID based on the ACPI path.
+ * Collisions on the same _HID are possible but very unlikely.
+ */
+uint32_t acpi_device_uid(struct device *dev)
+{
+	const char *path = acpi_device_path(dev);
+	if (!path)
+		return 0;
+
+	return CRC(path, strlen(path), crc32_byte);
+}
+
 /* Recursive function to find the root device and print a path from there */
 static ssize_t acpi_device_path_fill(const struct device *dev, char *buf,
 				     size_t buf_len, size_t cur)
@@ -192,6 +207,13 @@
 	return ACPI_STATUS_DEVICE_ALL_ON;
 }
 
+
+/* Write the unique _UID based on ACPI device path. */
+void acpi_device_write_uid(struct device *dev)
+{
+	acpigen_write_name_integer("_UID", acpi_device_uid(dev));
+}
+
 /* ACPI 6.1 section 6.4.3.6: Extended Interrupt Descriptor */
 void acpi_device_write_interrupt(const struct acpi_irq *irq)
 {
diff --git a/src/arch/x86/include/arch/acpi_device.h b/src/arch/x86/include/arch/acpi_device.h
index 382ef15..0a702c9 100644
--- a/src/arch/x86/include/arch/acpi_device.h
+++ b/src/arch/x86/include/arch/acpi_device.h
@@ -62,10 +62,12 @@
 struct device;
 const char *acpi_device_name(const struct device *dev);
 const char *acpi_device_hid(const struct device *dev);
+uint32_t acpi_device_uid(struct device *dev);
 const char *acpi_device_path(const struct device *dev);
 const char *acpi_device_scope(const struct device *dev);
 const char *acpi_device_path_join(const struct device *dev, const char *name);
 int acpi_device_status(const struct device *dev);
+void acpi_device_write_uid(struct device *dev);
 
 /*
  * ACPI Descriptor for extended Interrupt()
diff --git a/src/drivers/crb/tis.c b/src/drivers/crb/tis.c
index f2aba48..b1fbad0 100644
--- a/src/drivers/crb/tis.c
+++ b/src/drivers/crb/tis.c
@@ -117,7 +117,7 @@
 	acpigen_write_name_string("_HID", "MSFT0101");
 	acpigen_write_name_string("_CID", "MSFT0101");
 
-	acpigen_write_name_integer("_UID", 1);
+	acpi_device_write_uid(dev);
 
 	acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
 
diff --git a/src/drivers/net/r8168.c b/src/drivers/net/r8168.c
index bc0132f..1bca879 100644
--- a/src/drivers/net/r8168.c
+++ b/src/drivers/net/r8168.c
@@ -312,7 +312,8 @@
 	acpigen_write_scope(path);
 	acpigen_write_device(acpi_device_name(dev));
 	acpigen_write_name_string("_HID", R8168_ACPI_HID);
-	acpigen_write_name_integer("_UID", 0);
+	acpi_device_write_uid(dev);
+
 	if (dev->chip_ops)
 		acpigen_write_name_string("_DDN", dev->chip_ops->name);
 
diff --git a/src/drivers/pc80/tpm/tis.c b/src/drivers/pc80/tpm/tis.c
index e9f1485..a35ef83 100644
--- a/src/drivers/pc80/tpm/tis.c
+++ b/src/drivers/pc80/tpm/tis.c
@@ -902,7 +902,7 @@
 	acpigen_write_name("_CID");
 	acpigen_emit_eisaid("PNP0C31");
 
-	acpigen_write_name_integer("_UID", 1);
+	acpi_device_write_uid(dev);
 
 	u32 did_vid = tpm_read_did_vid(0);
 	if (did_vid > 0 && did_vid < 0xffffffff)
diff --git a/src/drivers/wifi/generic.c b/src/drivers/wifi/generic.c
index 9f9f4fa..fe2e39d 100644
--- a/src/drivers/wifi/generic.c
+++ b/src/drivers/wifi/generic.c
@@ -187,7 +187,8 @@
 	/* Device */
 	acpigen_write_scope(path);
 	acpigen_write_device(acpi_device_name(dev));
-	acpigen_write_name_integer("_UID", 0);
+	acpi_device_write_uid(dev);
+
 	if (dev->chip_ops)
 		acpigen_write_name_string("_DDN", dev->chip_ops->name);
 
diff --git a/src/superio/common/ssdt.c b/src/superio/common/ssdt.c
index 41cafdf..541fa99 100644
--- a/src/superio/common/ssdt.c
+++ b/src/superio/common/ssdt.c
@@ -196,7 +196,8 @@
 	/* Device */
 	acpigen_write_device(name);
 
-	acpigen_write_name_byte("_UID", 0);
+	acpi_device_write_uid(dev);
+
 	acpigen_write_name_byte("LDN", ldn);
 	acpigen_write_name_byte("VLDN", vldn);