From c83bab62b3657d97299c8368b6c610e4cbf994b6 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Fri, 13 Dec 2019 12:16:06 +0100 Subject: 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 Reviewed-on: https://review.coreboot.org/c/coreboot/+/37695 Tested-by: build bot (Jenkins) Reviewed-by: Philipp Deppenwiese --- Documentation/acpi/index.md | 2 ++ Documentation/acpi/uid.md | 14 ++++++++++++++ src/arch/x86/acpi_device.c | 22 ++++++++++++++++++++++ src/arch/x86/include/arch/acpi_device.h | 2 ++ src/drivers/crb/tis.c | 2 +- src/drivers/net/r8168.c | 3 ++- src/drivers/pc80/tpm/tis.c | 2 +- src/drivers/wifi/generic.c | 3 ++- src/superio/common/ssdt.c | 3 ++- 9 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 Documentation/acpi/uid.md diff --git a/Documentation/acpi/index.md b/Documentation/acpi/index.md index 8add8db38708..c378722018f7 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 000000000000..0354bc52f0c4 --- /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 5c77953bc2aa..1d791704875d 100644 --- a/src/arch/x86/acpi_device.c +++ b/src/arch/x86/acpi_device.c @@ -18,6 +18,8 @@ #include #include #include +#include + #if CONFIG(GENERIC_GPIO_LIB) #include #endif @@ -98,6 +100,19 @@ const char *acpi_device_hid(const struct device *dev) 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 @@ int acpi_device_status(const struct device *dev) 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 382ef1546bc1..0a702c9329d0 100644 --- a/src/arch/x86/include/arch/acpi_device.h +++ b/src/arch/x86/include/arch/acpi_device.h @@ -62,10 +62,12 @@ struct acpi_dp { 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 f2aba4829797..b1fbad01eb78 100644 --- a/src/drivers/crb/tis.c +++ b/src/drivers/crb/tis.c @@ -117,7 +117,7 @@ static void crb_tpm_fill_ssdt(struct device *dev) 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 bc0132fc1372..1bca8799aad8 100644 --- a/src/drivers/net/r8168.c +++ b/src/drivers/net/r8168.c @@ -312,7 +312,8 @@ static void r8168_net_fill_ssdt(struct device *dev) 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 e9f14854c44b..a35ef83d2c09 100644 --- a/src/drivers/pc80/tpm/tis.c +++ b/src/drivers/pc80/tpm/tis.c @@ -902,7 +902,7 @@ static void lpc_tpm_fill_ssdt(struct device *dev) 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 9f9f4faae731..fe2e39d29d84 100644 --- a/src/drivers/wifi/generic.c +++ b/src/drivers/wifi/generic.c @@ -187,7 +187,8 @@ void generic_wifi_fill_ssdt(struct device *dev, /* 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 41cafdfb0eef..541fa9922c55 100644 --- a/src/superio/common/ssdt.c +++ b/src/superio/common/ssdt.c @@ -196,7 +196,8 @@ void superio_common_fill_ssdt_generator(struct device *dev) /* 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); -- cgit v1.2.3