diff options
Diffstat (limited to 'drivers/bus/acpi.c')
-rw-r--r-- | drivers/bus/acpi.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/bus/acpi.c b/drivers/bus/acpi.c new file mode 100644 index 0000000000..5936170733 --- /dev/null +++ b/drivers/bus/acpi.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019 Ahmad Fatoum + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <efi.h> +#include <efi/efi-payload.h> +#include <efi/efi-device.h> +#include <acpi.h> + +static struct sig_desc { + const char sig[4]; + const char *desc; +} signatures[] = { + /* ACPI 6.3 Table 5-29, Defined DESCRIPTION_HEADER Signatures */ + { "APIC", "Multiple APIC Description" }, + { "BERT", "Boot Error Record" }, + { "BGRT", "Boot Graphics Resource" }, + { "CPEP", "Corrected Platform Error Polling" }, + { "DSDT", "Differentiated System Description" }, + { "ECDT", "Embedded Controller Boot Resource" }, + { "EINJ", "Error Injection" }, + { "ERST", "Error Record Serialization" }, + { "FACP", "Fixed ACPI Description" }, + { "FACS", "Firmware ACPI Control Structure" }, + { "FPDT", "Firmware Performance Data" }, + { "GTDT", "Generic Timer Description" }, + { "HEST", "Hardware Error Source" }, + { "MSCT", "Maximum System Characteristics" }, + { "MPST", "Memory Power State" }, + { "NFIT", "NVDIMM Firmware Interface" }, + { "OEM\0", "OEM Specific Information" }, + { "PCCT", "Platform Communications Channel" }, + { "PMTT", "Platform Memory Topology" }, + { "PSDT", "Persistent System Description" }, + { "RASF", "ACPI RAS Feature" }, + { "RSDT", "Root System Description" }, + { "SBST", "Smart Battery Specification" }, + { "SDEV", "Secure Devices" }, + { "SLIT", "System Locality Distance Information" }, + { "SRAT", "System Resource Affinity" }, + { "SSDT", "Secondary System Description" }, + { "XSDT", "Extended System Description" }, + + /* ACPI 6.3 Table 5-30, Reserved DESCRIPTION_HEADER Signatures */ + { "BOOT", "Simple BOOT Flag" }, + { "CSRT", "Core System Resource" }, + { "DBG2", "Microsoft Debug Port 2" }, + { "DBGP", "Debug Port" }, + { "DMAR", "DMA Remapping" }, + { "DPPT", "DMA Protection Policy" }, + { "DRTM", "Dynamic Root of Trust for Measurement" }, + { "ETDT", "(Obsolete) Event Timer Description" }, + { "HPET", "IA-PC High Precision Event Timer" }, + { "IBFT", "iSCSI Boot Firmware" }, + { "IORT", "I/O Remapping" }, + { "IVRS", "I/O Virtualization Reporting Structure" }, + { "LPIT", "Low Power Idle" }, + { "MCFG", "PCI Express memory mapped configuration" }, + { "MCHI", "Management Controller Host Interface" }, + { "MSDM", "Microsoft Data Management" }, + { "SDEI", "Software Delegated Exceptions Interface" }, + { "SLIC", "Microsoft Software Licensing Specification" }, + { "SPCR", "Serial Port Console Redirection" }, + { "SPMI", "Server Platform Management Interface" }, + { "STAO", "_STA Override" }, + { "TCPA", "Trusted Computing Platform Alliance Capabilities" }, + { "TPM2", "Trusted Platform Module 2" }, + { "UEFI", "UEFI ACPI Data" }, + { "WAET", "Windows ACPI Emulated Devices" }, + { "WDAT", "Watch Dog Action" }, + { "WDRT", "Watchdog Resource" }, + { "WPBT", "Platform Binary" }, + { "WSMT", "Windows SMM Security Mitigation" }, + { "XENV", "Xen Project" }, + + /* Others */ + { "NHLT", "Non-HD Audio" }, + { "ASF!", "Alert Standard Format" }, + + { /* sentinel */ } +}; + +static struct acpi_sdt *acpi_get_dev_sdt(struct device *dev) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) { + if (!strcmp(dev->resource[i].name, "SDT")) + return (struct acpi_sdt *)dev->resource[i].start; + } + + return NULL; +} + +static void acpi_devinfo(struct device *dev) +{ + struct acpi_sdt *sdt = acpi_get_dev_sdt(dev); + struct sig_desc *sig_desc; + + printf("Signature: %.4s", sdt->signature); + + for (sig_desc = signatures; sig_desc->desc; sig_desc++) { + size_t len = strnlen(sig_desc->sig, 4); + + if (!memcmp(sdt->signature, sig_desc->sig, len)) { + printf(" (%s Table)", sig_desc->desc); + break; + } + } + + printf("\nRevision: %u\n", sdt->revision); + printf("OemId: %.6s\n", sdt->oem_id); + printf("OemTableId: %.8s\n", sdt->oem_table_id); + printf("OemRevision: %u\n", sdt->oem_revision); + printf("CreatorId: 0x%08x\n", sdt->creator_id); + printf("CreatorRevision: %u\n", sdt->creator_revision); +} + +static int acpi_register_device(struct device *dev, struct acpi_sdt *sdt) +{ + device_add_resource(dev, "SDT", (resource_size_t)sdt, sdt->len, + IORESOURCE_MEM | IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY); + + dev_dbg(dev, "registering as ACPI device\n"); + + return register_device(dev); +} + +static struct device *acpi_add_device(struct bus_type *bus, + acpi_sig_t signature) +{ + struct device *dev; + + dev = xzalloc(sizeof(*dev)); + + dev->bus = bus; + dev->parent = bus->dev; + dev->id = DEVICE_ID_DYNAMIC; + dev->info = acpi_devinfo; + + dev_set_name(dev, "acpi-%.4s", signature); + + return dev; +} + +static int acpi_register_devices(struct bus_type *bus) +{ + struct efi_config_table *table = bus->dev->priv; + struct acpi_rsdp *rsdp; + struct acpi_rsdt *root; + size_t entry_count; + const char *sig; + int i; + + rsdp = (struct acpi_rsdp *)table->table; + + if (!rsdp) + return -EFAULT; + + /* ACPI specification v6.3 + * 5.2.5.2 Finding the RSDP on UEFI Enabled Systems + */ + if (memcmp("RSD PTR ", rsdp->signature, sizeof(rsdp->signature))) { + dev_dbg(bus->dev, "unexpected signature at start of config table: '%.8s'\n", + rsdp->signature); + return -ENODEV; + } + + if (rsdp->revision < 0x02) { + sig = "RSDT"; + root = (struct acpi_rsdt *)(unsigned long)rsdp->rsdt_addr; + entry_count = (root->sdt.len - sizeof(struct acpi_rsdt)) / sizeof(u32); + } else { + sig = "XSDT"; + root = (struct acpi_rsdt *)((struct acpi2_rsdp *)rsdp)->xsdt_addr; + entry_count = (root->sdt.len - sizeof(struct acpi_rsdt)) / sizeof(u64); + } + + if (acpi_sigcmp(sig, root->sdt.signature)) { + dev_err(bus->dev, "Expected %s, but found '%.4s'.\n", + sig, root->sdt.signature); + return -EIO; + } + + dev_info(bus->dev, "Found %s (OEM: %.8s) with %zu entries\n", + sig, root->sdt.oem_id, entry_count); + + for (i = 0; i < entry_count; i++) { + struct acpi_sdt *sdt = root->entries[i]; + acpi_register_device(acpi_add_device(bus, sdt->signature), sdt); + } + + return 0; +} + +static int acpi_bus_match(struct device *dev, struct driver *drv) +{ + struct acpi_driver *acpidrv = to_acpi_driver(drv); + struct acpi_sdt *sdt = acpi_get_dev_sdt(dev); + + return acpi_sigcmp(acpidrv->signature, sdt->signature); +} + +struct bus_type acpi_bus = { + .name = "acpi", + .match = acpi_bus_match, +}; + +static int efi_acpi_probe(void) +{ + struct efi_config_table *ect, *table = NULL; + + for_each_efi_config_table(ect) { + /* take ACPI < 2 table only if no ACPI 2.0 is available */ + if (!efi_guidcmp(ect->guid, EFI_ACPI_20_TABLE_GUID)) { + acpi_bus.name = "acpi2"; + table = ect; + } else if (!table && !efi_guidcmp(ect->guid, EFI_ACPI_TABLE_GUID)) { + acpi_bus.name = "acpi1"; + table = ect; + } + } + + bus_register(&acpi_bus); + + if (!table) + return 0; + + acpi_bus.dev->priv = table; + return acpi_register_devices(&acpi_bus); +} +postcore_efi_initcall(efi_acpi_probe); |