diff options
Diffstat (limited to 'arch/x86/lib/acpi_table.c')
-rw-r--r-- | arch/x86/lib/acpi_table.c | 383 |
1 files changed, 368 insertions, 15 deletions
diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index c445aa68703..6d405b09fde 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -6,7 +6,10 @@ * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> */ +#define LOG_CATEGORY LOGC_ACPI + #include <common.h> +#include <bloblist.h> #include <cpu.h> #include <dm.h> #include <log.h> @@ -15,6 +18,7 @@ #include <serial.h> #include <version.h> #include <acpi/acpigen.h> +#include <acpi/acpi_device.h> #include <acpi/acpi_table.h> #include <asm/acpi/global_nvs.h> #include <asm/ioapic.h> @@ -65,14 +69,17 @@ int acpi_create_madt_lapics(u32 current) { struct udevice *dev; int total_length = 0; + int cpu_num = 0; for (uclass_find_first_device(UCLASS_CPU, &dev); dev; uclass_find_next_device(&dev)) { struct cpu_platdata *plat = dev_get_parent_platdata(dev); - int length = acpi_create_madt_lapic( - (struct acpi_madt_lapic *)current, - plat->cpu_id, plat->cpu_id); + int length; + + length = acpi_create_madt_lapic( + (struct acpi_madt_lapic *)current, cpu_num++, + plat->cpu_id); current += length; total_length += length; } @@ -210,6 +217,105 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg) header->checksum = table_compute_checksum((void *)mcfg, header->length); } +/** + * acpi_create_tcpa() - Create a TCPA table + * + * @tcpa: Pointer to place to put table + * + * Trusted Computing Platform Alliance Capabilities Table + * TCPA PC Specific Implementation SpecificationTCPA is defined in the PCI + * Firmware Specification 3.0 + */ +static int acpi_create_tcpa(struct acpi_tcpa *tcpa) +{ + struct acpi_table_header *header = &tcpa->header; + u32 current = (u32)tcpa + sizeof(struct acpi_tcpa); + int size = 0x10000; /* Use this as the default size */ + void *log; + int ret; + + if (!CONFIG_IS_ENABLED(BLOBLIST)) + return -ENXIO; + memset(tcpa, '\0', sizeof(struct acpi_tcpa)); + + /* Fill out header fields */ + acpi_fill_header(header, "TCPA"); + header->length = sizeof(struct acpi_tcpa); + header->revision = 1; + + ret = bloblist_ensure_size_ret(BLOBLISTT_TCPA_LOG, &size, &log); + if (ret) + return log_msg_ret("blob", ret); + + tcpa->platform_class = 0; + tcpa->laml = size; + tcpa->lasa = (ulong)log; + + /* (Re)calculate length and checksum */ + header->length = current - (u32)tcpa; + header->checksum = table_compute_checksum((void *)tcpa, header->length); + + return 0; +} + +static int get_tpm2_log(void **ptrp, int *sizep) +{ + const int tpm2_default_log_len = 0x10000; + int size; + int ret; + + *sizep = 0; + size = tpm2_default_log_len; + ret = bloblist_ensure_size_ret(BLOBLISTT_TPM2_TCG_LOG, &size, ptrp); + if (ret) + return log_msg_ret("blob", ret); + *sizep = size; + + return 0; +} + +static int acpi_create_tpm2(struct acpi_tpm2 *tpm2) +{ + struct acpi_table_header *header = &tpm2->header; + int tpm2_log_len; + void *lasa; + int ret; + + memset((void *)tpm2, 0, sizeof(struct acpi_tpm2)); + + /* + * Some payloads like SeaBIOS depend on log area to use TPM2. + * Get the memory size and address of TPM2 log area or initialize it. + */ + ret = get_tpm2_log(&lasa, &tpm2_log_len); + if (ret) + return ret; + + /* Fill out header fields. */ + acpi_fill_header(header, "TPM2"); + memcpy(header->aslc_id, ASLC_ID, 4); + + header->length = sizeof(struct acpi_tpm2); + header->revision = acpi_get_table_revision(ACPITAB_TPM2); + + /* Hard to detect for coreboot. Just set it to 0 */ + tpm2->platform_class = 0; + + /* Must be set to 0 for FIFO-interface support */ + tpm2->control_area = 0; + tpm2->start_method = 6; + memset(tpm2->msp, 0, sizeof(tpm2->msp)); + + /* Fill the log area size and start address fields. */ + tpm2->laml = tpm2_log_len; + tpm2->lasa = (uintptr_t)lasa; + + /* Calculate checksum. */ + header->checksum = table_compute_checksum((void *)tpm2, header->length); + + return 0; +} + __weak u32 acpi_fill_csrt(u32 current) { return 0; @@ -394,11 +500,13 @@ ulong write_acpi_tables(ulong start_addr) struct acpi_fadt *fadt; struct acpi_table_header *ssdt; struct acpi_mcfg *mcfg; + struct acpi_tcpa *tcpa; struct acpi_madt *madt; struct acpi_csrt *csrt; struct acpi_spcr *spcr; void *start; ulong addr; + int ret; int i; start = map_sysmem(start_addr, 0); @@ -430,17 +538,31 @@ ulong write_acpi_tables(ulong start_addr) dsdt->length - sizeof(struct acpi_table_header)); acpi_inc(ctx, dsdt->length - sizeof(struct acpi_table_header)); + dsdt->length = ctx->current - (void *)dsdt; + acpi_align(ctx); - /* Pack GNVS into the ACPI table area */ - for (i = 0; i < dsdt->length; i++) { - u32 *gnvs = (u32 *)((u32)dsdt + i); - if (*gnvs == ACPI_GNVS_ADDR) { - ulong addr = (ulong)map_to_sysmem(ctx->current); - - debug("Fix up global NVS in DSDT to %#08lx\n", addr); - *gnvs = addr; - break; + if (!IS_ENABLED(CONFIG_ACPI_GNVS_EXTERNAL)) { + /* Pack GNVS into the ACPI table area */ + for (i = 0; i < dsdt->length; i++) { + u32 *gnvs = (u32 *)((u32)dsdt + i); + + if (*gnvs == ACPI_GNVS_ADDR) { + *gnvs = map_to_sysmem(ctx->current); + debug("Fix up global NVS in DSDT to %#08x\n", + *gnvs); + break; + } } + + /* + * Fill in platform-specific global NVS variables. If this fails + * we cannot return the error but this should only happen while + * debugging. + */ + addr = acpi_create_gnvs(ctx->current); + if (IS_ERR_VALUE(addr)) + printf("Error: Gailed to create GNVS\n"); + acpi_inc_align(ctx, sizeof(struct acpi_global_nvs)); } /* @@ -448,12 +570,9 @@ ulong write_acpi_tables(ulong start_addr) * the GNVS address. Set the checksum to zero since it is part of the * region being checksummed. */ - dsdt->length = ctx->current - (void *)dsdt; dsdt->checksum = 0; dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length); - acpi_align(ctx); - /* * Fill in platform-specific global NVS variables. If this fails we * cannot return the error but this should only happen while debugging. @@ -484,12 +603,36 @@ ulong write_acpi_tables(ulong start_addr) acpi_inc_align(ctx, mcfg->header.length); acpi_add_table(ctx, mcfg); + if (IS_ENABLED(CONFIG_TPM_V2)) { + struct acpi_tpm2 *tpm2; + + debug("ACPI: * TPM2\n"); + tpm2 = (struct acpi_tpm2 *)ctx->current; + ret = acpi_create_tpm2(tpm2); + if (!ret) { + acpi_inc_align(ctx, tpm2->header.length); + acpi_add_table(ctx, tpm2); + } else { + log_warning("TPM2 table creation failed\n"); + } + } + debug("ACPI: * MADT\n"); madt = ctx->current; acpi_create_madt(madt); acpi_inc_align(ctx, madt->header.length); acpi_add_table(ctx, madt); + debug("ACPI: * TCPA\n"); + tcpa = (struct acpi_tcpa *)ctx->current; + ret = acpi_create_tcpa(tcpa); + if (ret) { + log_warning("Failed to create TCPA table (err=%d)\n", ret); + } else { + acpi_inc_align(ctx, tcpa->header.length); + acpi_add_table(ctx, tcpa); + } + debug("ACPI: * CSRT\n"); csrt = ctx->current; if (!acpi_create_csrt(csrt)) { @@ -518,3 +661,213 @@ ulong acpi_get_rsdp_addr(void) { return acpi_rsdp_addr; } + +/** + * acpi_write_hpet() - Write out a HPET table + * + * Write out the table for High-Precision Event Timers + * + * @hpet: Place to put HPET table + */ +static int acpi_create_hpet(struct acpi_hpet *hpet) +{ + struct acpi_table_header *header = &hpet->header; + struct acpi_gen_regaddr *addr = &hpet->addr; + + /* + * See IA-PC HPET (High Precision Event Timers) Specification v1.0a + * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf + */ + memset((void *)hpet, '\0', sizeof(struct acpi_hpet)); + + /* Fill out header fields. */ + acpi_fill_header(header, "HPET"); + + header->aslc_revision = ASL_REVISION; + header->length = sizeof(struct acpi_hpet); + header->revision = acpi_get_table_revision(ACPITAB_HPET); + + /* Fill out HPET address */ + addr->space_id = 0; /* Memory */ + addr->bit_width = 64; + addr->bit_offset = 0; + addr->addrl = CONFIG_HPET_ADDRESS & 0xffffffff; + addr->addrh = ((unsigned long long)CONFIG_HPET_ADDRESS) >> 32; + + hpet->id = *(u32 *)CONFIG_HPET_ADDRESS; + hpet->number = 0; + hpet->min_tick = 0; /* HPET_MIN_TICKS */ + + header->checksum = table_compute_checksum(hpet, + sizeof(struct acpi_hpet)); + + return 0; +} + +int acpi_write_hpet(struct acpi_ctx *ctx) +{ + struct acpi_hpet *hpet; + int ret; + + log_debug("ACPI: * HPET\n"); + + hpet = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_hpet)); + acpi_create_hpet(hpet); + ret = acpi_add_table(ctx, hpet); + if (ret) + return log_msg_ret("add", ret); + + return 0; +} + +int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev, + uint access_size) +{ + struct acpi_dbg2_header *dbg2 = ctx->current; + char path[ACPI_PATH_MAX]; + struct acpi_gen_regaddr address; + phys_addr_t addr; + int ret; + + if (!device_active(dev)) { + log_info("Device not enabled\n"); + return -EACCES; + } + /* + * PCI devices don't remember their resource allocation information in + * U-Boot at present. We assume that MMIO is used for the UART and that + * the address space is 32 bytes: ns16550 uses 8 registers of up to + * 32-bits each. This is only for debugging so it is not a big deal. + */ + addr = dm_pci_read_bar32(dev, 0); + printf("UART addr %lx\n", (ulong)addr); + + memset(&address, '\0', sizeof(address)); + address.space_id = ACPI_ADDRESS_SPACE_MEMORY; + address.addrl = (uint32_t)addr; + address.addrh = (uint32_t)((addr >> 32) & 0xffffffff); + address.access_size = access_size; + + ret = acpi_device_path(dev, path, sizeof(path)); + if (ret) + return log_msg_ret("path", ret); + acpi_create_dbg2(dbg2, ACPI_DBG2_SERIAL_PORT, + ACPI_DBG2_16550_COMPATIBLE, &address, 0x1000, path); + + acpi_inc_align(ctx, dbg2->header.length); + acpi_add_table(ctx, dbg2); + + return 0; +} + +void acpi_fadt_common(struct acpi_fadt *fadt, struct acpi_facs *facs, + void *dsdt) +{ + struct acpi_table_header *header = &fadt->header; + + memset((void *)fadt, '\0', sizeof(struct acpi_fadt)); + + acpi_fill_header(header, "FACP"); + header->length = sizeof(struct acpi_fadt); + header->revision = 4; + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, OEM_TABLE_ID, 8); + memcpy(header->aslc_id, ASLC_ID, 4); + header->aslc_revision = 1; + + fadt->firmware_ctrl = (unsigned long)facs; + fadt->dsdt = (unsigned long)dsdt; + + fadt->x_firmware_ctl_l = (unsigned long)facs; + fadt->x_firmware_ctl_h = 0; + fadt->x_dsdt_l = (unsigned long)dsdt; + fadt->x_dsdt_h = 0; + + fadt->preferred_pm_profile = ACPI_PM_MOBILE; + + /* Use ACPI 3.0 revision */ + fadt->header.revision = 4; +} + +void acpi_create_dmar_drhd(struct acpi_ctx *ctx, uint flags, uint segment, + u64 bar) +{ + struct dmar_entry *drhd = ctx->current; + + memset(drhd, '\0', sizeof(*drhd)); + drhd->type = DMAR_DRHD; + drhd->length = sizeof(*drhd); /* will be fixed up later */ + drhd->flags = flags; + drhd->segment = segment; + drhd->bar = bar; + acpi_inc(ctx, drhd->length); +} + +void acpi_create_dmar_rmrr(struct acpi_ctx *ctx, uint segment, u64 bar, + u64 limit) +{ + struct dmar_rmrr_entry *rmrr = ctx->current; + + memset(rmrr, '\0', sizeof(*rmrr)); + rmrr->type = DMAR_RMRR; + rmrr->length = sizeof(*rmrr); /* will be fixed up later */ + rmrr->segment = segment; + rmrr->bar = bar; + rmrr->limit = limit; + acpi_inc(ctx, rmrr->length); +} + +void acpi_dmar_drhd_fixup(struct acpi_ctx *ctx, void *base) +{ + struct dmar_entry *drhd = base; + + drhd->length = ctx->current - base; +} + +void acpi_dmar_rmrr_fixup(struct acpi_ctx *ctx, void *base) +{ + struct dmar_rmrr_entry *rmrr = base; + + rmrr->length = ctx->current - base; +} + +static int acpi_create_dmar_ds(struct acpi_ctx *ctx, enum dev_scope_type type, + uint enumeration_id, pci_dev_t bdf) +{ + /* we don't support longer paths yet */ + const size_t dev_scope_length = sizeof(struct dev_scope) + 2; + struct dev_scope *ds = ctx->current; + + memset(ds, '\0', dev_scope_length); + ds->type = type; + ds->length = dev_scope_length; + ds->enumeration = enumeration_id; + ds->start_bus = PCI_BUS(bdf); + ds->path[0].dev = PCI_DEV(bdf); + ds->path[0].fn = PCI_FUNC(bdf); + + return ds->length; +} + +int acpi_create_dmar_ds_pci_br(struct acpi_ctx *ctx, pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_PCI_SUB, 0, bdf); +} + +int acpi_create_dmar_ds_pci(struct acpi_ctx *ctx, pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_PCI_ENDPOINT, 0, bdf); +} + +int acpi_create_dmar_ds_ioapic(struct acpi_ctx *ctx, uint enumeration_id, + pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_IOAPIC, enumeration_id, bdf); +} + +int acpi_create_dmar_ds_msi_hpet(struct acpi_ctx *ctx, uint enumeration_id, + pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_MSI_HPET, enumeration_id, bdf); +} |