// SPDX-License-Identifier: GPL-2.0 /* * Utility functions for ACPI * * Copyright 2023 Google LLC */ #define LOG_CATEGORY LOGC_ACPI #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* * OEM_REVISION is 32-bit unsigned number. It should be increased only when * changing software version. Therefore it should not depend on build time. * U-Boot calculates it from U-Boot version and represent it in hexadecimal * notation. As U-Boot version is in form year.month set low 8 bits to 0x01 * to have valid date. So for U-Boot version 2021.04 OEM_REVISION is set to * value 0x20210401. */ #define OEM_REVISION ((((version_num / 1000) % 10) << 28) | \ (((version_num / 100) % 10) << 24) | \ (((version_num / 10) % 10) << 20) | \ ((version_num % 10) << 16) | \ (((version_num_patch / 10) % 10) << 12) | \ ((version_num_patch % 10) << 8) | \ 0x01) void acpi_update_checksum(struct acpi_table_header *header) { header->checksum = 0; header->checksum = table_compute_checksum(header, header->length); } static bool acpi_valid_rsdp(struct acpi_rsdp *rsdp) { if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0) return false; debug("Looking on %p for valid checksum\n", rsdp); if (table_compute_checksum((void *)rsdp, 20) != 0) return false; debug("acpi rsdp checksum 1 passed\n"); if (rsdp->revision > 1 && table_compute_checksum((void *)rsdp, rsdp->length)) return false; debug("acpi rsdp checksum 2 passed\n"); return true; } /** * setup_search() - Set up for searching through the RSDT/XSDT * * Looks for XSDT first and uses those entries if available, else RSDT * * @rsdtp: Returns RSDT if present * @xsdtp: Returns XSDT if present * Return: number of entries in table, -ENOENT if there is no RSDP, -EINVAL if * the RSDP is invalid, -ENOTSYNC if both tables exist and their counts * disagree */ static int setup_search(struct acpi_rsdt **rsdtp, struct acpi_xsdt **xsdtp) { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt = NULL; struct acpi_xsdt *xsdt = NULL; int len, count = 0; rsdp = map_sysmem(gd_acpi_start(), 0); if (!rsdp) return -ENOENT; if (!acpi_valid_rsdp(rsdp)) return -EINVAL; if (rsdp->xsdt_address) { xsdt = nomap_sysmem(rsdp->xsdt_address, 0); len = xsdt->header.length - sizeof(xsdt->header); count = len / sizeof(u64); } if (rsdp->rsdt_address) { int rcount; rsdt = nomap_sysmem(rsdp->rsdt_address, 0); len = rsdt->header.length - sizeof(rsdt->header); rcount = len / sizeof(u32); if (xsdt) { if (rcount != count) return -ENOTSYNC; } else { count = rcount; } } *rsdtp = rsdt; *xsdtp = xsdt; return count; } struct acpi_table_header *acpi_find_table(const char *sig) { struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; int i, count, ret; ret = setup_search(&rsdt, &xsdt); if (ret < 0) { log_warning("acpi: Failed to find tables (err=%d)\n", ret); return NULL; } if (!ret) return NULL; count = ret; if (!strcmp("RSDT", sig)) return &rsdt->header; if (!strcmp("XSDT", sig)) return &xsdt->header; for (i = 0; i < count; i++) { struct acpi_table_header *hdr; if (xsdt) hdr = nomap_sysmem(xsdt->entry[i], 0); else hdr = nomap_sysmem(rsdt->entry[i], 0); if (!memcmp(hdr->signature, sig, ACPI_NAME_LEN)) return hdr; if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) { struct acpi_fadt *fadt = (struct acpi_fadt *)hdr; if (!memcmp(sig, "DSDT", ACPI_NAME_LEN)) { void *dsdt; if (fadt->header.revision >= 3 && fadt->x_dsdt) dsdt = nomap_sysmem(fadt->x_dsdt, 0); else if (fadt->dsdt) dsdt = nomap_sysmem(fadt->dsdt, 0); else dsdt = NULL; return dsdt; } if (!memcmp(sig, "FACS", ACPI_NAME_LEN)) { void *facs; if (fadt->header.revision >= 3 && fadt->x_firmware_ctrl) facs = nomap_sysmem(fadt->x_firmware_ctrl, 0); else if (fadt->firmware_ctrl) facs = nomap_sysmem(fadt->firmware_ctrl, 0); else facs = NULL; return facs; } } } return NULL; } void acpi_fill_header(struct acpi_table_header *header, char *signature) { memcpy(header->signature, signature, 4); memcpy(header->oem_id, OEM_ID, 6); memcpy(header->oem_table_id, OEM_TABLE_ID, 8); header->oem_revision = OEM_REVISION; memcpy(header->creator_id, ASLC_ID, 4); header->creator_revision = ASL_REVISION; } void acpi_align(struct acpi_ctx *ctx) { ctx->current = (void *)ALIGN((ulong)ctx->current, 16); } void acpi_align64(struct acpi_ctx *ctx) { ctx->current = (void *)ALIGN((ulong)ctx->current, 64); } void acpi_inc(struct acpi_ctx *ctx, uint amount) { ctx->current += amount; } void acpi_inc_align(struct acpi_ctx *ctx, uint amount) { ctx->current += amount; acpi_align(ctx); } /** * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length * and checksum. */ int acpi_add_table(struct acpi_ctx *ctx, void *table) { int i, entries_num; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; if (!ctx->rsdt && !ctx->xsdt) { log_err("ACPI: Error: no RSDT / XSDT\n"); return -EINVAL; } /* On legacy x86 platforms the RSDT is mandatory while the XSDT is not. * On other platforms there might be no memory below 4GiB, thus RSDT is NULL. */ if (ctx->rsdt) { rsdt = ctx->rsdt; /* This should always be MAX_ACPI_TABLES */ entries_num = ARRAY_SIZE(rsdt->entry); for (i = 0; i < entries_num; i++) { if (rsdt->entry[i] == 0) break; } if (i >= entries_num) { log_err("ACPI: Error: too many tables\n"); return -E2BIG; } /* Add table to the RSDT */ rsdt->entry[i] = nomap_to_sysmem(table); /* Fix RSDT length or the kernel will assume invalid entries */ rsdt->header.length = sizeof(struct acpi_table_header) + (sizeof(u32) * (i + 1)); /* Re-calculate checksum */ acpi_update_checksum(&rsdt->header); } if (ctx->xsdt) { /* * And now the same thing for the XSDT. We use the same index as for * now we want the XSDT and RSDT to always be in sync in U-Boot */ xsdt = ctx->xsdt; /* This should always be MAX_ACPI_TABLES */ entries_num = ARRAY_SIZE(xsdt->entry); for (i = 0; i < entries_num; i++) { if (xsdt->entry[i] == 0) break; } if (i >= entries_num) { log_err("ACPI: Error: too many tables\n"); return -E2BIG; } /* Add table to the XSDT */ xsdt->entry[i] = nomap_to_sysmem(table); /* Fix XSDT length */ xsdt->header.length = sizeof(struct acpi_table_header) + (sizeof(u64) * (i + 1)); /* Re-calculate checksum */ acpi_update_checksum(&xsdt->header); } return 0; } void *acpi_get_end(void) { const struct acpi_table_header *end; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; int i, count; count = setup_search(&rsdt, &xsdt); if (!count) return NULL; end = xsdt ? &xsdt->header : &rsdt->header; for (i = 0; i < count; i++) { const struct acpi_table_header *hdr; if (xsdt) hdr = nomap_sysmem(xsdt->entry[i], 0); else hdr = nomap_sysmem(rsdt->entry[i], 0); end = max(hdr, end); } return (void *)end + end->length; }