2004-01-28 17:56:14 +01:00
|
|
|
/*
|
2010-02-16 00:10:19 +01:00
|
|
|
* This file is part of the coreboot project.
|
|
|
|
*
|
2016-01-12 23:55:28 +01:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2019-08-31 05:09:37 +02:00
|
|
|
*
|
|
|
|
* coreboot ACPI Table support
|
2005-01-19 15:06:41 +01:00
|
|
|
*/
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
/*
|
2005-01-19 15:06:41 +01:00
|
|
|
* Each system port implementing ACPI has to provide two functions:
|
2010-04-27 08:56:47 +02:00
|
|
|
*
|
2005-01-19 15:06:41 +01:00
|
|
|
* write_acpi_tables()
|
|
|
|
* acpi_dump_apics()
|
2010-04-27 08:56:47 +02:00
|
|
|
*
|
2009-11-10 23:17:15 +01:00
|
|
|
* See Kontron 986LCD-M port for a good example of an ACPI implementation
|
|
|
|
* in coreboot.
|
2004-10-06 19:33:54 +02:00
|
|
|
*/
|
2004-01-28 17:56:14 +01:00
|
|
|
|
|
|
|
#include <console/console.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <arch/acpi.h>
|
2016-09-06 18:22:34 +02:00
|
|
|
#include <arch/acpi_ivrs.h>
|
2009-02-01 19:35:15 +01:00
|
|
|
#include <arch/acpigen.h>
|
2005-01-19 15:06:41 +01:00
|
|
|
#include <device/pci.h>
|
2009-11-10 23:17:15 +01:00
|
|
|
#include <cbmem.h>
|
2019-06-21 07:06:50 +02:00
|
|
|
#include <commonlib/helpers.h>
|
2012-02-16 18:43:25 +01:00
|
|
|
#include <cpu/x86/lapic_def.h>
|
2012-10-04 04:07:05 +02:00
|
|
|
#include <cpu/cpu.h>
|
2014-10-26 20:42:08 +01:00
|
|
|
#include <cbfs.h>
|
2019-02-15 08:21:33 +01:00
|
|
|
#include <version.h>
|
2019-02-19 13:39:56 +01:00
|
|
|
#include <commonlib/sort.h>
|
2004-10-06 19:33:54 +02:00
|
|
|
|
2019-09-10 15:40:47 +02:00
|
|
|
static acpi_rsdp_t *valid_rsdp(acpi_rsdp_t *rsdp);
|
|
|
|
|
2004-10-06 19:33:54 +02:00
|
|
|
u8 acpi_checksum(u8 *table, u32 length)
|
2004-01-28 17:56:14 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
u8 ret = 0;
|
2004-01-28 17:56:14 +01:00
|
|
|
while (length--) {
|
|
|
|
ret += *table;
|
|
|
|
table++;
|
|
|
|
}
|
2004-01-29 18:31:34 +01:00
|
|
|
return -ret;
|
2004-01-28 17:56:14 +01:00
|
|
|
}
|
|
|
|
|
2009-11-10 23:17:15 +01:00
|
|
|
/**
|
2010-11-19 16:14:42 +01:00
|
|
|
* Add an ACPI table to the RSDT (and XSDT) structure, recalculate length
|
|
|
|
* and checksum.
|
2004-02-03 17:11:35 +01:00
|
|
|
*/
|
2009-07-21 23:38:33 +02:00
|
|
|
void acpi_add_table(acpi_rsdp_t *rsdp, void *table)
|
2004-01-28 17:56:14 +01:00
|
|
|
{
|
2009-11-10 23:17:15 +01:00
|
|
|
int i, entries_num;
|
2009-07-21 23:38:33 +02:00
|
|
|
acpi_rsdt_t *rsdt;
|
|
|
|
acpi_xsdt_t *xsdt = NULL;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* The RSDT is mandatory... */
|
2015-06-18 10:11:19 +02:00
|
|
|
rsdt = (acpi_rsdt_t *)(uintptr_t)rsdp->rsdt_address;
|
2009-11-10 23:17:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* ...while the XSDT is not. */
|
|
|
|
if (rsdp->xsdt_address)
|
2015-06-18 10:11:19 +02:00
|
|
|
xsdt = (acpi_xsdt_t *)((uintptr_t)rsdp->xsdt_address);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* This should always be MAX_ACPI_TABLES. */
|
2009-11-10 23:17:15 +01:00
|
|
|
entries_num = ARRAY_SIZE(rsdt->entry);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2009-11-10 23:17:15 +01:00
|
|
|
for (i = 0; i < entries_num; i++) {
|
2010-11-19 16:14:42 +01:00
|
|
|
if (rsdt->entry[i] == 0)
|
2009-11-10 23:17:15 +01:00
|
|
|
break;
|
2004-01-28 17:56:14 +01:00
|
|
|
}
|
|
|
|
|
2009-11-10 23:17:15 +01:00
|
|
|
if (i >= entries_num) {
|
2010-11-19 16:14:42 +01:00
|
|
|
printk(BIOS_ERR, "ACPI: Error: Could not add ACPI table, "
|
2011-07-01 23:57:07 +02:00
|
|
|
"too many tables.\n");
|
2009-11-10 23:17:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Add table to the RSDT. */
|
2015-06-18 10:11:19 +02:00
|
|
|
rsdt->entry[i] = (uintptr_t)table;
|
2009-11-10 23:17:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fix RSDT length or the kernel will assume invalid entries. */
|
|
|
|
rsdt->header.length = sizeof(acpi_header_t) + (sizeof(u32) * (i + 1));
|
2009-11-10 23:17:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Re-calculate checksum. */
|
2009-11-10 23:17:15 +01:00
|
|
|
rsdt->header.checksum = 0; /* Hope this won't get optimized away */
|
2010-11-19 16:14:42 +01:00
|
|
|
rsdt->header.checksum = acpi_checksum((u8 *)rsdt, rsdt->header.length);
|
2009-11-10 23:17:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/*
|
|
|
|
* And now the same thing for the XSDT. We use the same index as for
|
2009-11-10 23:17:15 +01:00
|
|
|
* now we want the XSDT and RSDT to always be in sync in coreboot.
|
|
|
|
*/
|
|
|
|
if (xsdt) {
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Add table to the XSDT. */
|
2015-06-18 10:11:19 +02:00
|
|
|
xsdt->entry[i] = (u64)(uintptr_t)table;
|
2009-11-10 23:17:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fix XSDT length. */
|
2009-11-10 23:17:15 +01:00
|
|
|
xsdt->header.length = sizeof(acpi_header_t) +
|
2011-07-01 23:57:07 +02:00
|
|
|
(sizeof(u64) * (i + 1));
|
2009-11-10 23:17:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Re-calculate checksum. */
|
|
|
|
xsdt->header.checksum = 0;
|
|
|
|
xsdt->header.checksum = acpi_checksum((u8 *)xsdt,
|
2011-07-01 23:57:07 +02:00
|
|
|
xsdt->header.length);
|
2009-11-10 23:17:15 +01:00
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
printk(BIOS_DEBUG, "ACPI: added table %d/%d, length now %d\n",
|
2011-07-01 23:57:07 +02:00
|
|
|
i + 1, entries_num, rsdt->header.length);
|
2004-01-28 17:56:14 +01:00
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
int acpi_create_mcfg_mmconfig(acpi_mcfg_mmconfig_t *mmconfig, u32 base,
|
2011-07-01 23:57:07 +02:00
|
|
|
u16 seg_nr, u8 start, u8 end)
|
2009-01-20 20:17:51 +01:00
|
|
|
{
|
2014-10-05 11:05:16 +02:00
|
|
|
memset(mmconfig, 0, sizeof(*mmconfig));
|
2007-11-03 13:50:26 +01:00
|
|
|
mmconfig->base_address = base;
|
|
|
|
mmconfig->base_reserved = 0;
|
|
|
|
mmconfig->pci_segment_group_number = seg_nr;
|
|
|
|
mmconfig->start_bus_number = start;
|
|
|
|
mmconfig->end_bus_number = end;
|
2010-11-19 16:14:42 +01:00
|
|
|
|
|
|
|
return sizeof(acpi_mcfg_mmconfig_t);
|
2007-11-03 13:50:26 +01:00
|
|
|
}
|
|
|
|
|
2005-01-19 15:06:41 +01:00
|
|
|
int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic)
|
2004-02-03 17:11:35 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
lapic->type = 0; /* Local APIC structure */
|
|
|
|
lapic->length = sizeof(acpi_madt_lapic_t);
|
|
|
|
lapic->flags = (1 << 0); /* Processor/LAPIC enabled */
|
|
|
|
lapic->processor_id = cpu;
|
|
|
|
lapic->apic_id = apic;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
return lapic->length;
|
2004-02-03 17:11:35 +01:00
|
|
|
}
|
|
|
|
|
2009-04-22 10:18:37 +02:00
|
|
|
unsigned long acpi_create_madt_lapics(unsigned long current)
|
|
|
|
{
|
2014-10-27 13:29:29 +01:00
|
|
|
struct device *cpu;
|
2019-03-06 09:31:02 +01:00
|
|
|
int index, apic_ids[CONFIG_MAX_CPUS] = {0}, num_cpus = 0;
|
2009-04-22 10:18:37 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
for (cpu = all_devices; cpu; cpu = cpu->next) {
|
2009-04-22 10:18:37 +02:00
|
|
|
if ((cpu->path.type != DEVICE_PATH_APIC) ||
|
2013-02-13 00:20:54 +01:00
|
|
|
(cpu->bus->dev->path.type != DEVICE_PATH_CPU_CLUSTER)) {
|
2009-04-22 10:18:37 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-11-19 16:14:42 +01:00
|
|
|
if (!cpu->enabled)
|
2009-04-22 10:18:37 +02:00
|
|
|
continue;
|
2019-02-19 13:39:56 +01:00
|
|
|
if (num_cpus >= ARRAY_SIZE(apic_ids))
|
|
|
|
break;
|
|
|
|
apic_ids[num_cpus++] = cpu->path.apic.apic_id;
|
|
|
|
}
|
2019-03-12 07:07:50 +01:00
|
|
|
if (num_cpus > 1)
|
|
|
|
bubblesort(apic_ids, num_cpus, NUM_ASCENDING);
|
2019-02-19 13:39:56 +01:00
|
|
|
for (index = 0; index < num_cpus; index++) {
|
2010-11-19 16:14:42 +01:00
|
|
|
current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current,
|
2019-02-19 13:39:56 +01:00
|
|
|
index, apic_ids[index]);
|
2009-04-22 10:18:37 +02:00
|
|
|
}
|
2010-11-19 16:14:42 +01:00
|
|
|
|
2009-04-22 10:18:37 +02:00
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr,
|
2011-07-01 23:57:07 +02:00
|
|
|
u32 gsi_base)
|
2004-02-03 17:11:35 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
ioapic->type = 1; /* I/O APIC structure */
|
|
|
|
ioapic->length = sizeof(acpi_madt_ioapic_t);
|
|
|
|
ioapic->reserved = 0x00;
|
|
|
|
ioapic->gsi_base = gsi_base;
|
|
|
|
ioapic->ioapic_id = id;
|
|
|
|
ioapic->ioapic_addr = addr;
|
|
|
|
|
|
|
|
return ioapic->length;
|
2004-02-03 17:11:35 +01:00
|
|
|
}
|
|
|
|
|
2005-01-19 15:06:41 +01:00
|
|
|
int acpi_create_madt_irqoverride(acpi_madt_irqoverride_t *irqoverride,
|
2004-02-03 17:11:35 +01:00
|
|
|
u8 bus, u8 source, u32 gsirq, u16 flags)
|
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
irqoverride->type = 2; /* Interrupt source override */
|
|
|
|
irqoverride->length = sizeof(acpi_madt_irqoverride_t);
|
|
|
|
irqoverride->bus = bus;
|
|
|
|
irqoverride->source = source;
|
|
|
|
irqoverride->gsirq = gsirq;
|
|
|
|
irqoverride->flags = flags;
|
|
|
|
|
|
|
|
return irqoverride->length;
|
2004-02-03 17:11:35 +01:00
|
|
|
}
|
|
|
|
|
2005-01-19 15:06:41 +01:00
|
|
|
int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu,
|
2011-07-01 23:57:07 +02:00
|
|
|
u16 flags, u8 lint)
|
2004-02-03 17:11:35 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
lapic_nmi->type = 4; /* Local APIC NMI structure */
|
|
|
|
lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t);
|
|
|
|
lapic_nmi->flags = flags;
|
|
|
|
lapic_nmi->processor_id = cpu;
|
|
|
|
lapic_nmi->lint = lint;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
return lapic_nmi->length;
|
2004-02-03 17:11:35 +01:00
|
|
|
}
|
|
|
|
|
2005-01-19 15:06:41 +01:00
|
|
|
void acpi_create_madt(acpi_madt_t *madt)
|
2004-02-03 17:11:35 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(madt->header);
|
|
|
|
unsigned long current = (unsigned long)madt + sizeof(acpi_madt_t);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2004-02-03 17:11:35 +01:00
|
|
|
memset((void *)madt, 0, sizeof(acpi_madt_t));
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->signature, "APIC", 4);
|
2004-02-03 17:11:35 +01:00
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
2004-02-03 17:11:35 +01:00
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2004-02-03 17:11:35 +01:00
|
|
|
header->length = sizeof(acpi_madt_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(MADT);
|
2004-02-03 17:11:35 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
madt->lapic_addr = LOCAL_APIC_ADDR;
|
2018-11-24 18:25:50 +01:00
|
|
|
if (CONFIG(ACPI_HAVE_PCAT_8259))
|
|
|
|
madt->flags |= 1;
|
2004-02-03 17:11:35 +01:00
|
|
|
|
2005-11-26 17:56:05 +01:00
|
|
|
current = acpi_fill_madt(current);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)madt;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
header->checksum = acpi_checksum((void *)madt, header->length);
|
2005-11-26 17:56:05 +01:00
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* MCFG is defined in the PCI Firmware Specification 3.0. */
|
2007-11-03 13:50:26 +01:00
|
|
|
void acpi_create_mcfg(acpi_mcfg_t *mcfg)
|
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(mcfg->header);
|
|
|
|
unsigned long current = (unsigned long)mcfg + sizeof(acpi_mcfg_t);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2007-11-03 13:50:26 +01:00
|
|
|
memset((void *)mcfg, 0, sizeof(acpi_mcfg_t));
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->signature, "MCFG", 4);
|
2007-11-03 13:50:26 +01:00
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
2007-11-03 13:50:26 +01:00
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2007-11-03 13:50:26 +01:00
|
|
|
header->length = sizeof(acpi_mcfg_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(MCFG);
|
2007-11-03 13:50:26 +01:00
|
|
|
|
|
|
|
current = acpi_fill_mcfg(current);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)mcfg;
|
|
|
|
header->checksum = acpi_checksum((void *)mcfg, header->length);
|
2007-11-03 13:50:26 +01:00
|
|
|
}
|
|
|
|
|
2015-05-20 19:52:01 +02:00
|
|
|
static void *get_tcpa_log(u32 *size)
|
|
|
|
{
|
|
|
|
const struct cbmem_entry *ce;
|
|
|
|
const u32 tcpa_default_log_len = 0x10000;
|
|
|
|
void *lasa;
|
2018-07-30 01:15:20 +02:00
|
|
|
ce = cbmem_entry_find(CBMEM_ID_TCPA_TCG_LOG);
|
2015-05-20 19:52:01 +02:00
|
|
|
if (ce) {
|
|
|
|
lasa = cbmem_entry_start(ce);
|
|
|
|
*size = cbmem_entry_size(ce);
|
|
|
|
printk(BIOS_DEBUG, "TCPA log found at %p\n", lasa);
|
|
|
|
return lasa;
|
|
|
|
}
|
2018-07-30 01:15:20 +02:00
|
|
|
lasa = cbmem_add(CBMEM_ID_TCPA_TCG_LOG, tcpa_default_log_len);
|
2015-05-20 19:52:01 +02:00
|
|
|
if (!lasa) {
|
|
|
|
printk(BIOS_ERR, "TCPA log creation failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "TCPA log created at %p\n", lasa);
|
2017-03-16 21:41:11 +01:00
|
|
|
memset(lasa, 0, tcpa_default_log_len);
|
2015-05-20 19:52:01 +02:00
|
|
|
|
|
|
|
*size = tcpa_default_log_len;
|
|
|
|
return lasa;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_create_tcpa(acpi_tcpa_t *tcpa)
|
|
|
|
{
|
|
|
|
acpi_header_t *header = &(tcpa->header);
|
|
|
|
u32 tcpa_log_len;
|
|
|
|
void *lasa;
|
|
|
|
|
|
|
|
memset((void *)tcpa, 0, sizeof(acpi_tcpa_t));
|
|
|
|
|
|
|
|
lasa = get_tcpa_log(&tcpa_log_len);
|
2017-03-16 19:24:09 +01:00
|
|
|
if (!lasa)
|
2015-05-20 19:52:01 +02:00
|
|
|
return;
|
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2015-05-20 19:52:01 +02:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "TCPA", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2015-05-20 19:52:01 +02:00
|
|
|
header->length = sizeof(acpi_tcpa_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(TCPA);
|
2015-05-20 19:52:01 +02:00
|
|
|
|
|
|
|
tcpa->platform_class = 0;
|
|
|
|
tcpa->laml = tcpa_log_len;
|
2015-06-18 10:11:19 +02:00
|
|
|
tcpa->lasa = (uintptr_t) lasa;
|
2015-05-20 19:52:01 +02:00
|
|
|
|
|
|
|
/* Calculate checksum. */
|
|
|
|
header->checksum = acpi_checksum((void *)tcpa, header->length);
|
|
|
|
}
|
|
|
|
|
2018-11-22 16:57:50 +01:00
|
|
|
static void *get_tpm2_log(u32 *size)
|
|
|
|
{
|
|
|
|
const struct cbmem_entry *ce;
|
|
|
|
const u32 tpm2_default_log_len = 0x10000;
|
|
|
|
void *lasa;
|
|
|
|
ce = cbmem_entry_find(CBMEM_ID_TPM2_TCG_LOG);
|
|
|
|
if (ce) {
|
|
|
|
lasa = cbmem_entry_start(ce);
|
|
|
|
*size = cbmem_entry_size(ce);
|
|
|
|
printk(BIOS_DEBUG, "TPM2 log found at %p\n", lasa);
|
|
|
|
return lasa;
|
|
|
|
}
|
|
|
|
lasa = cbmem_add(CBMEM_ID_TPM2_TCG_LOG, tpm2_default_log_len);
|
|
|
|
if (!lasa) {
|
|
|
|
printk(BIOS_ERR, "TPM2 log creation failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "TPM2 log created at %p\n", lasa);
|
|
|
|
memset(lasa, 0, tpm2_default_log_len);
|
|
|
|
|
|
|
|
*size = tpm2_default_log_len;
|
|
|
|
return lasa;
|
|
|
|
}
|
|
|
|
|
2018-10-18 15:39:34 +02:00
|
|
|
static void acpi_create_tpm2(acpi_tpm2_t *tpm2)
|
|
|
|
{
|
|
|
|
acpi_header_t *header = &(tpm2->header);
|
2018-11-22 16:57:50 +01:00
|
|
|
u32 tpm2_log_len;
|
|
|
|
void *lasa;
|
2018-10-18 15:39:34 +02:00
|
|
|
|
|
|
|
memset((void *)tpm2, 0, sizeof(acpi_tpm2_t));
|
|
|
|
|
2018-11-22 16:57:50 +01:00
|
|
|
/*
|
|
|
|
* Some payloads like SeaBIOS depend on log area to use TPM2.
|
|
|
|
* Get the memory size and address of TPM2 log area or initialize it.
|
|
|
|
*/
|
|
|
|
lasa = get_tpm2_log(&tpm2_log_len);
|
|
|
|
if (!lasa)
|
|
|
|
tpm2_log_len = 0;
|
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2018-10-18 15:39:34 +02:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "TPM2", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2018-10-18 15:39:34 +02:00
|
|
|
header->length = sizeof(acpi_tpm2_t);
|
|
|
|
header->revision = get_acpi_table_revision(TPM2);
|
|
|
|
|
|
|
|
/* Hard to detect for coreboot. Just set it to 0 */
|
|
|
|
tpm2->platform_class = 0;
|
2019-07-14 15:47:23 +02:00
|
|
|
if (CONFIG(CRB_TPM)) {
|
|
|
|
/* Must be set to 7 for CRB Support */
|
|
|
|
tpm2->control_area = CONFIG_CRB_TPM_BASE_ADDRESS + 0x40;
|
|
|
|
tpm2->start_method = 7;
|
|
|
|
} else {
|
|
|
|
/* Must be set to 0 for FIFO interface support */
|
|
|
|
tpm2->control_area = 0;
|
|
|
|
tpm2->start_method = 6;
|
|
|
|
}
|
2018-10-18 15:39:34 +02:00
|
|
|
memset(tpm2->msp, 0, sizeof(tpm2->msp));
|
|
|
|
|
2018-11-22 16:57:50 +01:00
|
|
|
/* Fill the log area size and start address fields. */
|
|
|
|
tpm2->laml = tpm2_log_len;
|
|
|
|
tpm2->lasa = (uintptr_t) lasa;
|
|
|
|
|
2018-10-18 15:39:34 +02:00
|
|
|
/* Calculate checksum. */
|
|
|
|
header->checksum = acpi_checksum((void *)tpm2, header->length);
|
|
|
|
}
|
|
|
|
|
acpi: Generate object for coreboot table region
Generate an object to describe the coreboot table region in ACPI
with the HID "CORE0000" so it can be used by kernel drivers.
To keep track of the "CORE" HID usage add them to an enum and add
a function to generate the HID in AML: Name (_HID, "CORExxxx")
BUG=chromium:589817
BRANCH=none
TEST=build and boot on chell, dump SSDT to verify contents:
Device (CTBL)
{
Name (_HID, "CORE0000") // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
Method (_STA, 0, NotSerialized) // _STA: Status
{
Return (0x0F)
}
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadOnly,
0x7AB84000, // Address Base
0x00008000, // Address Length
)
})
}
Change-Id: I2c681c1fee02d52b8df2e72f6f6f0b76fa9592fb
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://review.coreboot.org/16056
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
2016-05-26 21:47:05 +02:00
|
|
|
static void acpi_ssdt_write_cbtable(void)
|
|
|
|
{
|
|
|
|
const struct cbmem_entry *cbtable;
|
|
|
|
uintptr_t base;
|
|
|
|
uint32_t size;
|
|
|
|
|
|
|
|
cbtable = cbmem_entry_find(CBMEM_ID_CBTABLE);
|
|
|
|
if (!cbtable)
|
|
|
|
return;
|
|
|
|
base = (uintptr_t)cbmem_entry_start(cbtable);
|
|
|
|
size = cbmem_entry_size(cbtable);
|
|
|
|
|
|
|
|
acpigen_write_device("CTBL");
|
|
|
|
acpigen_write_coreboot_hid(COREBOOT_ACPI_ID_CBTABLE);
|
|
|
|
acpigen_write_name_integer("_UID", 0);
|
2018-03-21 09:48:53 +01:00
|
|
|
acpigen_write_STA(ACPI_STATUS_DEVICE_HIDDEN_ON);
|
acpi: Generate object for coreboot table region
Generate an object to describe the coreboot table region in ACPI
with the HID "CORE0000" so it can be used by kernel drivers.
To keep track of the "CORE" HID usage add them to an enum and add
a function to generate the HID in AML: Name (_HID, "CORExxxx")
BUG=chromium:589817
BRANCH=none
TEST=build and boot on chell, dump SSDT to verify contents:
Device (CTBL)
{
Name (_HID, "CORE0000") // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
Method (_STA, 0, NotSerialized) // _STA: Status
{
Return (0x0F)
}
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadOnly,
0x7AB84000, // Address Base
0x00008000, // Address Length
)
})
}
Change-Id: I2c681c1fee02d52b8df2e72f6f6f0b76fa9592fb
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://review.coreboot.org/16056
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
2016-05-26 21:47:05 +02:00
|
|
|
acpigen_write_name("_CRS");
|
|
|
|
acpigen_write_resourcetemplate_header();
|
|
|
|
acpigen_write_mem32fixed(0, base, size);
|
|
|
|
acpigen_write_resourcetemplate_footer();
|
|
|
|
acpigen_pop_len();
|
|
|
|
}
|
|
|
|
|
2009-10-09 22:13:43 +02:00
|
|
|
void acpi_create_ssdt_generator(acpi_header_t *ssdt, const char *oem_table_id)
|
2009-02-01 19:35:15 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
unsigned long current = (unsigned long)ssdt + sizeof(acpi_header_t);
|
|
|
|
|
2009-02-01 19:35:15 +01:00
|
|
|
memset((void *)ssdt, 0, sizeof(acpi_header_t));
|
2010-11-19 16:14:42 +01:00
|
|
|
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(&ssdt->signature, "SSDT", 4);
|
2018-08-23 02:57:24 +02:00
|
|
|
ssdt->revision = get_acpi_table_revision(SSDT);
|
2009-02-01 19:35:15 +01:00
|
|
|
memcpy(&ssdt->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(&ssdt->oem_table_id, oem_table_id, 8);
|
|
|
|
ssdt->oem_revision = 42;
|
2010-11-19 16:14:42 +01:00
|
|
|
memcpy(&ssdt->asl_compiler_id, ASLC, 4);
|
2019-02-15 08:21:33 +01:00
|
|
|
ssdt->asl_compiler_revision = asl_revision;
|
2009-02-01 19:35:15 +01:00
|
|
|
ssdt->length = sizeof(acpi_header_t);
|
|
|
|
|
2009-03-06 18:24:29 +01:00
|
|
|
acpigen_set_current((char *) current);
|
acpi: Generate object for coreboot table region
Generate an object to describe the coreboot table region in ACPI
with the HID "CORE0000" so it can be used by kernel drivers.
To keep track of the "CORE" HID usage add them to an enum and add
a function to generate the HID in AML: Name (_HID, "CORExxxx")
BUG=chromium:589817
BRANCH=none
TEST=build and boot on chell, dump SSDT to verify contents:
Device (CTBL)
{
Name (_HID, "CORE0000") // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
Method (_STA, 0, NotSerialized) // _STA: Status
{
Return (0x0F)
}
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadOnly,
0x7AB84000, // Address Base
0x00008000, // Address Length
)
})
}
Change-Id: I2c681c1fee02d52b8df2e72f6f6f0b76fa9592fb
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://review.coreboot.org/16056
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
2016-05-26 21:47:05 +02:00
|
|
|
|
|
|
|
/* Write object to declare coreboot tables */
|
|
|
|
acpi_ssdt_write_cbtable();
|
|
|
|
|
2014-08-30 19:28:05 +02:00
|
|
|
{
|
2014-10-27 13:29:29 +01:00
|
|
|
struct device *dev;
|
2014-08-30 19:28:05 +02:00
|
|
|
for (dev = all_devices; dev; dev = dev->next)
|
2017-03-16 19:24:09 +01:00
|
|
|
if (dev->ops && dev->ops->acpi_fill_ssdt_generator)
|
2015-04-12 22:18:55 +02:00
|
|
|
dev->ops->acpi_fill_ssdt_generator(dev);
|
2014-10-05 14:34:17 +02:00
|
|
|
current = (unsigned long) acpigen_get_current();
|
2014-08-30 19:28:05 +02:00
|
|
|
}
|
2009-02-01 19:35:15 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* (Re)calculate length and checksum. */
|
2009-02-01 19:35:15 +01:00
|
|
|
ssdt->length = current - (unsigned long)ssdt;
|
|
|
|
ssdt->checksum = acpi_checksum((void *)ssdt, ssdt->length);
|
|
|
|
}
|
|
|
|
|
2005-11-26 17:56:05 +01:00
|
|
|
int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic)
|
|
|
|
{
|
2009-03-10 19:06:47 +01:00
|
|
|
memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t));
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */
|
|
|
|
lapic->length = sizeof(acpi_srat_lapic_t);
|
|
|
|
lapic->flags = (1 << 0); /* Enabled (the use of this structure). */
|
|
|
|
lapic->proximity_domain_7_0 = node;
|
|
|
|
/* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */
|
|
|
|
lapic->apic_id = apic;
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
return lapic->length;
|
2005-11-26 17:56:05 +01:00
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
int acpi_create_srat_mem(acpi_srat_mem_t *mem, u8 node, u32 basek, u32 sizek,
|
2011-07-01 23:57:07 +02:00
|
|
|
u32 flags)
|
2005-11-26 17:56:05 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
mem->type = 1; /* Memory affinity structure */
|
|
|
|
mem->length = sizeof(acpi_srat_mem_t);
|
|
|
|
mem->base_address_low = (basek << 10);
|
|
|
|
mem->base_address_high = (basek >> (32 - 10));
|
|
|
|
mem->length_low = (sizek << 10);
|
|
|
|
mem->length_high = (sizek >> (32 - 10));
|
|
|
|
mem->proximity_domain = node;
|
2010-04-27 08:56:47 +02:00
|
|
|
mem->flags = flags;
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
return mem->length;
|
2005-11-26 17:56:05 +01:00
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* http://www.microsoft.com/whdc/system/sysinternals/sratdwn.mspx */
|
2014-10-11 23:45:40 +02:00
|
|
|
void acpi_create_srat(acpi_srat_t *srat,
|
|
|
|
unsigned long (*acpi_fill_srat)(unsigned long current))
|
2005-11-26 17:56:05 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(srat->header);
|
|
|
|
unsigned long current = (unsigned long)srat + sizeof(acpi_srat_t);
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
memset((void *)srat, 0, sizeof(acpi_srat_t));
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "SRAT", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2010-11-19 16:14:42 +01:00
|
|
|
header->length = sizeof(acpi_srat_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(SRAT);
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
srat->resv = 1; /* Spec: Reserved to 1 for backwards compatibility. */
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
current = acpi_fill_srat(current);
|
2005-11-26 17:56:05 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)srat;
|
|
|
|
header->checksum = acpi_checksum((void *)srat, header->length);
|
2006-10-04 22:46:15 +02:00
|
|
|
}
|
|
|
|
|
2015-10-26 11:51:25 +01:00
|
|
|
void acpi_create_dmar(acpi_dmar_t *dmar, enum dmar_flags flags,
|
2017-03-16 21:41:11 +01:00
|
|
|
unsigned long (*acpi_fill_dmar)(unsigned long))
|
2012-09-11 15:21:01 +02:00
|
|
|
{
|
|
|
|
acpi_header_t *header = &(dmar->header);
|
|
|
|
unsigned long current = (unsigned long)dmar + sizeof(acpi_dmar_t);
|
|
|
|
|
|
|
|
memset((void *)dmar, 0, sizeof(acpi_dmar_t));
|
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2012-09-11 15:21:01 +02:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "DMAR", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2012-09-11 15:21:01 +02:00
|
|
|
header->length = sizeof(acpi_dmar_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(DMAR);
|
2012-09-11 15:21:01 +02:00
|
|
|
|
2016-03-16 03:53:27 +01:00
|
|
|
dmar->host_address_width = cpu_phys_address_size() - 1;
|
2015-10-26 11:51:25 +01:00
|
|
|
dmar->flags = flags;
|
2012-09-11 15:21:01 +02:00
|
|
|
|
|
|
|
current = acpi_fill_dmar(current);
|
|
|
|
|
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)dmar;
|
|
|
|
header->checksum = acpi_checksum((void *)dmar, header->length);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long acpi_create_dmar_drhd(unsigned long current, u8 flags,
|
2018-03-29 14:59:57 +02:00
|
|
|
u16 segment, u64 bar)
|
2012-09-11 15:21:01 +02:00
|
|
|
{
|
|
|
|
dmar_entry_t *drhd = (dmar_entry_t *)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;
|
|
|
|
|
|
|
|
return drhd->length;
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
unsigned long acpi_create_dmar_rmrr(unsigned long current, u16 segment,
|
|
|
|
u64 bar, u64 limit)
|
|
|
|
{
|
|
|
|
dmar_rmrr_entry_t *rmrr = (dmar_rmrr_entry_t *)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;
|
|
|
|
|
|
|
|
return rmrr->length;
|
|
|
|
}
|
|
|
|
|
2016-07-27 06:56:36 +02:00
|
|
|
unsigned long acpi_create_dmar_atsr(unsigned long current, u8 flags,
|
|
|
|
u16 segment)
|
|
|
|
{
|
|
|
|
dmar_atsr_entry_t *atsr = (dmar_atsr_entry_t *)current;
|
|
|
|
memset(atsr, 0, sizeof(*atsr));
|
|
|
|
atsr->type = DMAR_ATSR;
|
|
|
|
atsr->length = sizeof(*atsr); /* will be fixed up later */
|
|
|
|
atsr->flags = flags;
|
|
|
|
atsr->segment = segment;
|
|
|
|
|
|
|
|
return atsr->length;
|
|
|
|
}
|
|
|
|
|
2019-04-04 20:03:28 +02:00
|
|
|
unsigned long acpi_create_dmar_rhsa(unsigned long current, u64 base_addr,
|
|
|
|
u32 proximity_domain)
|
|
|
|
{
|
|
|
|
dmar_rhsa_entry_t *rhsa = (dmar_rhsa_entry_t *)current;
|
|
|
|
memset(rhsa, 0, sizeof(*rhsa));
|
|
|
|
rhsa->type = DMAR_RHSA;
|
|
|
|
rhsa->length = sizeof(*rhsa);
|
|
|
|
rhsa->base_address = base_addr;
|
|
|
|
rhsa->proximity_domain = proximity_domain;
|
|
|
|
|
|
|
|
return rhsa->length;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long acpi_create_dmar_andd(unsigned long current, u8 device_number,
|
|
|
|
const char *device_name)
|
|
|
|
{
|
|
|
|
dmar_andd_entry_t *andd = (dmar_andd_entry_t *)current;
|
|
|
|
int andd_len = sizeof(dmar_andd_entry_t) + strlen(device_name) + 1;
|
|
|
|
memset(andd, 0, andd_len);
|
|
|
|
andd->type = DMAR_ANDD;
|
|
|
|
andd->length = andd_len;
|
|
|
|
andd->device_number = device_number;
|
|
|
|
memcpy(&andd->device_name, device_name, strlen(device_name));
|
|
|
|
|
|
|
|
return andd->length;
|
|
|
|
}
|
|
|
|
|
2012-09-11 15:21:01 +02:00
|
|
|
void acpi_dmar_drhd_fixup(unsigned long base, unsigned long current)
|
|
|
|
{
|
|
|
|
dmar_entry_t *drhd = (dmar_entry_t *)base;
|
|
|
|
drhd->length = current - base;
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
void acpi_dmar_rmrr_fixup(unsigned long base, unsigned long current)
|
|
|
|
{
|
|
|
|
dmar_rmrr_entry_t *rmrr = (dmar_rmrr_entry_t *)base;
|
|
|
|
rmrr->length = current - base;
|
|
|
|
}
|
|
|
|
|
2016-07-27 06:56:36 +02:00
|
|
|
void acpi_dmar_atsr_fixup(unsigned long base, unsigned long current)
|
|
|
|
{
|
|
|
|
dmar_atsr_entry_t *atsr = (dmar_atsr_entry_t *)base;
|
|
|
|
atsr->length = current - base;
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
static unsigned long acpi_create_dmar_ds(unsigned long current,
|
2015-10-26 12:03:54 +01:00
|
|
|
enum dev_scope_type type, u8 enumeration_id, u8 bus, u8 dev, u8 fn)
|
2012-09-11 15:21:01 +02:00
|
|
|
{
|
2015-10-26 12:03:54 +01:00
|
|
|
/* we don't support longer paths yet */
|
|
|
|
const size_t dev_scope_length = sizeof(dev_scope_t) + 2;
|
|
|
|
|
2012-09-11 15:21:01 +02:00
|
|
|
dev_scope_t *ds = (dev_scope_t *)current;
|
2015-10-26 12:03:54 +01:00
|
|
|
memset(ds, 0, dev_scope_length);
|
|
|
|
ds->type = type;
|
|
|
|
ds->length = dev_scope_length;
|
|
|
|
ds->enumeration = enumeration_id;
|
|
|
|
ds->start_bus = bus;
|
|
|
|
ds->path[0].dev = dev;
|
|
|
|
ds->path[0].fn = fn;
|
2012-09-11 15:21:01 +02:00
|
|
|
|
|
|
|
return ds->length;
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
unsigned long acpi_create_dmar_ds_pci_br(unsigned long current, u8 bus,
|
2016-07-27 07:07:20 +02:00
|
|
|
u8 dev, u8 fn)
|
|
|
|
{
|
2018-03-29 14:59:57 +02:00
|
|
|
return acpi_create_dmar_ds(current,
|
2016-07-27 07:07:20 +02:00
|
|
|
SCOPE_PCI_SUB, 0, bus, dev, fn);
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
unsigned long acpi_create_dmar_ds_pci(unsigned long current, u8 bus,
|
2015-10-26 12:03:54 +01:00
|
|
|
u8 dev, u8 fn)
|
|
|
|
{
|
2018-03-29 14:59:57 +02:00
|
|
|
return acpi_create_dmar_ds(current,
|
2015-10-26 12:03:54 +01:00
|
|
|
SCOPE_PCI_ENDPOINT, 0, bus, dev, fn);
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
unsigned long acpi_create_dmar_ds_ioapic(unsigned long current,
|
2015-10-26 12:03:54 +01:00
|
|
|
u8 enumeration_id, u8 bus, u8 dev, u8 fn)
|
|
|
|
{
|
2018-03-29 14:59:57 +02:00
|
|
|
return acpi_create_dmar_ds(current,
|
2015-10-26 12:03:54 +01:00
|
|
|
SCOPE_IOAPIC, enumeration_id, bus, dev, fn);
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:59:57 +02:00
|
|
|
unsigned long acpi_create_dmar_ds_msi_hpet(unsigned long current,
|
2015-10-26 12:03:54 +01:00
|
|
|
u8 enumeration_id, u8 bus, u8 dev, u8 fn)
|
|
|
|
{
|
2018-03-29 14:59:57 +02:00
|
|
|
return acpi_create_dmar_ds(current,
|
2015-10-26 12:03:54 +01:00
|
|
|
SCOPE_MSI_HPET, enumeration_id, bus, dev, fn);
|
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* http://h21007.www2.hp.com/portal/download/files/unprot/Itanium/slit.pdf */
|
2014-10-11 23:45:40 +02:00
|
|
|
void acpi_create_slit(acpi_slit_t *slit,
|
|
|
|
unsigned long (*acpi_fill_slit)(unsigned long current))
|
2006-10-04 22:46:15 +02:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(slit->header);
|
|
|
|
unsigned long current = (unsigned long)slit + sizeof(acpi_slit_t);
|
2006-10-04 22:46:15 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
memset((void *)slit, 0, sizeof(acpi_slit_t));
|
2006-10-04 22:46:15 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "SLIT", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2006-10-04 22:46:15 +02:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2010-11-19 16:14:42 +01:00
|
|
|
header->length = sizeof(acpi_slit_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(SLIT);
|
2006-10-04 22:46:15 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
current = acpi_fill_slit(current);
|
2006-10-04 22:46:15 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)slit;
|
|
|
|
header->checksum = acpi_checksum((void *)slit, header->length);
|
2004-02-03 17:11:35 +01:00
|
|
|
}
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* http://www.intel.com/hardwaredesign/hpetspec_1.pdf */
|
2005-01-19 15:06:41 +01:00
|
|
|
void acpi_create_hpet(acpi_hpet_t *hpet)
|
2004-01-28 17:56:14 +01:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(hpet->header);
|
|
|
|
acpi_addr_t *addr = &(hpet->addr);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2004-01-29 18:31:34 +01:00
|
|
|
memset((void *)hpet, 0, sizeof(acpi_hpet_t));
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
2009-07-22 03:11:37 +02:00
|
|
|
memcpy(header->signature, "HPET", 4);
|
2004-01-28 17:56:14 +01:00
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
2004-01-28 17:56:14 +01:00
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2004-01-28 17:56:14 +01:00
|
|
|
header->length = sizeof(acpi_hpet_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(HPET);
|
2004-01-28 17:56:14 +01:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out HPET address. */
|
|
|
|
addr->space_id = 0; /* Memory */
|
|
|
|
addr->bit_width = 64;
|
|
|
|
addr->bit_offset = 0;
|
2012-10-05 21:54:38 +02:00
|
|
|
addr->addrl = CONFIG_HPET_ADDRESS & 0xffffffff;
|
|
|
|
addr->addrh = ((unsigned long long)CONFIG_HPET_ADDRESS) >> 32;
|
2004-01-28 17:56:14 +01:00
|
|
|
|
2017-03-16 21:41:11 +01:00
|
|
|
hpet->id = *(unsigned int *)CONFIG_HPET_ADDRESS;
|
2010-11-19 16:14:42 +01:00
|
|
|
hpet->number = 0;
|
2012-10-05 21:54:38 +02:00
|
|
|
hpet->min_tick = CONFIG_HPET_MIN_TICKS;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
header->checksum = acpi_checksum((void *)hpet, sizeof(acpi_hpet_t));
|
2004-01-28 17:56:14 +01:00
|
|
|
}
|
2010-11-19 16:14:42 +01:00
|
|
|
|
2016-03-31 20:04:23 +02:00
|
|
|
void acpi_create_vfct(struct device *device,
|
2019-10-21 11:57:19 +02:00
|
|
|
acpi_vfct_t *vfct,
|
2017-03-16 23:18:22 +01:00
|
|
|
unsigned long (*acpi_fill_vfct)(struct device *device,
|
2019-10-21 11:57:19 +02:00
|
|
|
acpi_vfct_t *vfct_struct, unsigned long current))
|
2016-03-31 20:04:23 +02:00
|
|
|
{
|
|
|
|
acpi_header_t *header = &(vfct->header);
|
2019-10-21 11:57:19 +02:00
|
|
|
unsigned long current = (unsigned long)vfct + sizeof(acpi_vfct_t);
|
2016-03-31 20:04:23 +02:00
|
|
|
|
2019-10-21 11:57:19 +02:00
|
|
|
memset((void *)vfct, 0, sizeof(acpi_vfct_t));
|
2016-03-31 20:04:23 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2016-03-31 20:04:23 +02:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "VFCT", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(VFCT);
|
2016-03-31 20:04:23 +02:00
|
|
|
|
|
|
|
current = acpi_fill_vfct(device, vfct, current);
|
|
|
|
|
2019-06-30 05:29:52 +02:00
|
|
|
/* If no BIOS image, return with header->length == 0. */
|
|
|
|
if (!vfct->VBIOSImageOffset)
|
|
|
|
return;
|
|
|
|
|
2016-03-31 20:04:23 +02:00
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)vfct;
|
|
|
|
header->checksum = acpi_checksum((void *)vfct, header->length);
|
|
|
|
}
|
|
|
|
|
2019-06-14 19:00:04 +02:00
|
|
|
void acpi_create_ipmi(struct device *device,
|
|
|
|
struct acpi_spmi *spmi,
|
|
|
|
const u16 ipmi_revision,
|
|
|
|
const acpi_addr_t *addr,
|
|
|
|
const enum acpi_ipmi_interface_type type,
|
|
|
|
const s8 gpe_interrupt,
|
|
|
|
const u32 apic_interrupt,
|
|
|
|
const u32 uid)
|
|
|
|
{
|
|
|
|
acpi_header_t *header = &(spmi->header);
|
|
|
|
memset((void *)spmi, 0, sizeof(struct acpi_spmi));
|
|
|
|
|
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "SPMI", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
|
|
|
|
|
|
|
header->asl_compiler_revision = asl_revision;
|
|
|
|
header->length = sizeof(struct acpi_spmi);
|
|
|
|
header->revision = get_acpi_table_revision(SPMI);
|
|
|
|
|
|
|
|
spmi->reserved = 1;
|
|
|
|
|
|
|
|
if (device->path.type == DEVICE_PATH_PCI) {
|
|
|
|
spmi->pci_device_flag = ACPI_IPMI_PCI_DEVICE_FLAG;
|
|
|
|
spmi->pci_bus = device->bus->secondary;
|
|
|
|
spmi->pci_device = device->path.pci.devfn >> 3;
|
|
|
|
spmi->pci_function = device->path.pci.devfn & 0x7;
|
|
|
|
} else if (type != IPMI_INTERFACE_SSIF) {
|
|
|
|
memcpy(spmi->uid, &uid, sizeof(spmi->uid));
|
|
|
|
}
|
|
|
|
|
|
|
|
spmi->base_address = *addr;
|
|
|
|
spmi->specification_revision = ipmi_revision;
|
|
|
|
|
|
|
|
spmi->interface_type = type;
|
|
|
|
|
|
|
|
if (gpe_interrupt >= 0 && gpe_interrupt < 32) {
|
|
|
|
spmi->gpe = gpe_interrupt;
|
|
|
|
spmi->interrupt_type |= ACPI_IPMI_INT_TYPE_SCI;
|
|
|
|
}
|
|
|
|
if (apic_interrupt > 0) {
|
|
|
|
spmi->global_system_interrupt = apic_interrupt;
|
|
|
|
spmi->interrupt_type |= ACPI_IPMI_INT_TYPE_APIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate checksum. */
|
|
|
|
header->checksum = acpi_checksum((void *)spmi, header->length);
|
|
|
|
}
|
|
|
|
|
2015-08-12 00:48:32 +02:00
|
|
|
void acpi_create_ivrs(acpi_ivrs_t *ivrs,
|
2017-03-16 23:18:22 +01:00
|
|
|
unsigned long (*acpi_fill_ivrs)(acpi_ivrs_t *ivrs_struct,
|
|
|
|
unsigned long current))
|
2015-08-12 00:48:32 +02:00
|
|
|
{
|
|
|
|
acpi_header_t *header = &(ivrs->header);
|
|
|
|
unsigned long current = (unsigned long)ivrs + sizeof(acpi_ivrs_t);
|
|
|
|
|
|
|
|
memset((void *)ivrs, 0, sizeof(acpi_ivrs_t));
|
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2015-08-12 00:48:32 +02:00
|
|
|
/* Fill out header fields. */
|
|
|
|
memcpy(header->signature, "IVRS", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2015-08-12 00:48:32 +02:00
|
|
|
header->length = sizeof(acpi_ivrs_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(IVRS);
|
2015-08-12 00:48:32 +02:00
|
|
|
|
|
|
|
current = acpi_fill_ivrs(ivrs, current);
|
|
|
|
|
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
header->length = current - (unsigned long)ivrs;
|
|
|
|
header->checksum = acpi_checksum((void *)ivrs, header->length);
|
|
|
|
}
|
|
|
|
|
2018-05-04 17:56:47 +02:00
|
|
|
unsigned long acpi_write_hpet(struct device *device, unsigned long current,
|
2017-03-16 23:18:22 +01:00
|
|
|
acpi_rsdp_t *rsdp)
|
2014-08-30 19:28:05 +02:00
|
|
|
{
|
|
|
|
acpi_hpet_t *hpet;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We explicitly add these tables later on:
|
|
|
|
*/
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * HPET\n");
|
|
|
|
|
|
|
|
hpet = (acpi_hpet_t *) current;
|
|
|
|
current += sizeof(acpi_hpet_t);
|
2019-06-20 13:50:17 +02:00
|
|
|
current = ALIGN_UP(current, 16);
|
2014-08-30 19:28:05 +02:00
|
|
|
acpi_create_hpet(hpet);
|
|
|
|
acpi_add_table(rsdp, hpet);
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2017-11-12 04:33:25 +01:00
|
|
|
void acpi_create_dbg2(acpi_dbg2_header_t *dbg2,
|
|
|
|
int port_type, int port_subtype,
|
|
|
|
acpi_addr_t *address, uint32_t address_size,
|
|
|
|
const char *device_path)
|
|
|
|
{
|
|
|
|
uintptr_t current;
|
|
|
|
acpi_dbg2_device_t *device;
|
|
|
|
uint32_t *dbg2_addr_size;
|
|
|
|
acpi_header_t *header;
|
|
|
|
size_t path_len;
|
|
|
|
const char *path;
|
|
|
|
char *namespace;
|
|
|
|
|
|
|
|
/* Fill out header fields. */
|
|
|
|
current = (uintptr_t)dbg2;
|
|
|
|
memset(dbg2, 0, sizeof(acpi_dbg2_header_t));
|
|
|
|
header = &(dbg2->header);
|
2019-05-29 01:48:14 +02:00
|
|
|
|
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(DBG2);
|
2017-11-12 04:33:25 +01:00
|
|
|
memcpy(header->signature, "DBG2", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2017-11-12 04:33:25 +01:00
|
|
|
|
|
|
|
/* One debug device defined */
|
|
|
|
dbg2->devices_offset = sizeof(acpi_dbg2_header_t);
|
|
|
|
dbg2->devices_count = 1;
|
|
|
|
current += sizeof(acpi_dbg2_header_t);
|
|
|
|
|
|
|
|
/* Device comes after the header */
|
|
|
|
device = (acpi_dbg2_device_t *)current;
|
|
|
|
memset(device, 0, sizeof(acpi_dbg2_device_t));
|
|
|
|
current += sizeof(acpi_dbg2_device_t);
|
|
|
|
|
|
|
|
device->revision = 0;
|
|
|
|
device->address_count = 1;
|
|
|
|
device->port_type = port_type;
|
|
|
|
device->port_subtype = port_subtype;
|
|
|
|
|
|
|
|
/* Base Address comes after device structure */
|
|
|
|
memcpy((void *)current, address, sizeof(acpi_addr_t));
|
|
|
|
device->base_address_offset = current - (uintptr_t)device;
|
|
|
|
current += sizeof(acpi_addr_t);
|
|
|
|
|
|
|
|
/* Address Size comes after address structure */
|
|
|
|
dbg2_addr_size = (uint32_t *)current;
|
|
|
|
device->address_size_offset = current - (uintptr_t)device;
|
|
|
|
*dbg2_addr_size = address_size;
|
|
|
|
current += sizeof(uint32_t);
|
|
|
|
|
|
|
|
/* Namespace string comes last, use '.' if not provided */
|
|
|
|
path = device_path ? : ".";
|
|
|
|
/* Namespace string length includes NULL terminator */
|
|
|
|
path_len = strlen(path) + 1;
|
|
|
|
namespace = (char *)current;
|
|
|
|
device->namespace_string_length = path_len;
|
|
|
|
device->namespace_string_offset = current - (uintptr_t)device;
|
|
|
|
strncpy(namespace, path, path_len);
|
|
|
|
current += path_len;
|
|
|
|
|
|
|
|
/* Update structure lengths and checksum */
|
|
|
|
device->length = current - (uintptr_t)device;
|
|
|
|
header->length = current - (uintptr_t)dbg2;
|
|
|
|
header->checksum = acpi_checksum((uint8_t *)dbg2, header->length);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long acpi_write_dbg2_pci_uart(acpi_rsdp_t *rsdp, unsigned long current,
|
2019-07-30 06:41:41 +02:00
|
|
|
const struct device *dev, uint8_t access_size)
|
2017-11-12 04:33:25 +01:00
|
|
|
{
|
|
|
|
acpi_dbg2_header_t *dbg2 = (acpi_dbg2_header_t *)current;
|
|
|
|
struct resource *res;
|
|
|
|
acpi_addr_t address;
|
|
|
|
|
|
|
|
if (!dev) {
|
|
|
|
printk(BIOS_ERR, "%s: Device not found\n", __func__);
|
|
|
|
return current;
|
|
|
|
}
|
2017-11-21 13:52:53 +01:00
|
|
|
if (!dev->enabled) {
|
|
|
|
printk(BIOS_INFO, "%s: Device not enabled\n", __func__);
|
|
|
|
return current;
|
|
|
|
}
|
2017-11-12 04:33:25 +01:00
|
|
|
res = find_resource(dev, PCI_BASE_ADDRESS_0);
|
|
|
|
if (!res) {
|
|
|
|
printk(BIOS_ERR, "%s: Unable to find resource for %s\n",
|
|
|
|
__func__, dev_path(dev));
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&address, 0, sizeof(address));
|
|
|
|
if (res->flags & IORESOURCE_IO)
|
|
|
|
address.space_id = ACPI_ADDRESS_SPACE_IO;
|
|
|
|
else if (res->flags & IORESOURCE_MEM)
|
|
|
|
address.space_id = ACPI_ADDRESS_SPACE_MEMORY;
|
|
|
|
else {
|
|
|
|
printk(BIOS_ERR, "%s: Unknown address space type\n", __func__);
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
address.addrl = (uint32_t)res->base;
|
|
|
|
address.addrh = (uint32_t)((res->base >> 32) & 0xffffffff);
|
|
|
|
address.access_size = access_size;
|
|
|
|
|
|
|
|
acpi_create_dbg2(dbg2,
|
|
|
|
ACPI_DBG2_PORT_SERIAL,
|
|
|
|
ACPI_DBG2_PORT_SERIAL_16550,
|
|
|
|
&address, res->size,
|
|
|
|
acpi_device_path(dev));
|
|
|
|
|
|
|
|
if (dbg2->header.length) {
|
|
|
|
current += dbg2->header.length;
|
|
|
|
current = acpi_align_current(current);
|
|
|
|
acpi_add_table(rsdp, dbg2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2005-01-19 15:06:41 +01:00
|
|
|
void acpi_create_facs(acpi_facs_t *facs)
|
2004-10-06 19:33:54 +02:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
memset((void *)facs, 0, sizeof(acpi_facs_t));
|
2004-10-06 19:33:54 +02:00
|
|
|
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(facs->signature, "FACS", 4);
|
2004-10-06 19:33:54 +02:00
|
|
|
facs->length = sizeof(acpi_facs_t);
|
|
|
|
facs->hardware_signature = 0;
|
|
|
|
facs->firmware_waking_vector = 0;
|
|
|
|
facs->global_lock = 0;
|
|
|
|
facs->flags = 0;
|
|
|
|
facs->x_firmware_waking_vector_l = 0;
|
|
|
|
facs->x_firmware_waking_vector_h = 0;
|
2018-08-23 02:57:24 +02:00
|
|
|
facs->version = get_acpi_table_revision(FACS);
|
2004-10-06 19:33:54 +02:00
|
|
|
}
|
2005-01-19 15:06:41 +01:00
|
|
|
|
2014-10-26 20:42:08 +01:00
|
|
|
static void acpi_write_rsdt(acpi_rsdt_t *rsdt, char *oem_id, char *oem_table_id)
|
2010-04-27 08:56:47 +02:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(rsdt->header);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->signature, "RSDT", 4);
|
2014-10-26 20:42:08 +01:00
|
|
|
memcpy(header->oem_id, oem_id, 6);
|
|
|
|
memcpy(header->oem_table_id, oem_table_id, 8);
|
2004-01-28 17:56:14 +01:00
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2004-01-28 17:56:14 +01:00
|
|
|
header->length = sizeof(acpi_rsdt_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(RSDT);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Entries are filled in later, we come with an empty set. */
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fix checksum. */
|
2009-07-21 23:38:33 +02:00
|
|
|
header->checksum = acpi_checksum((void *)rsdt, sizeof(acpi_rsdt_t));
|
|
|
|
}
|
|
|
|
|
2014-10-26 20:42:08 +01:00
|
|
|
static void acpi_write_xsdt(acpi_xsdt_t *xsdt, char *oem_id, char *oem_table_id)
|
2010-04-27 08:56:47 +02:00
|
|
|
{
|
2010-11-19 16:14:42 +01:00
|
|
|
acpi_header_t *header = &(xsdt->header);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fill out header fields. */
|
2009-07-22 00:15:43 +02:00
|
|
|
memcpy(header->signature, "XSDT", 4);
|
2014-10-26 20:42:08 +01:00
|
|
|
memcpy(header->oem_id, oem_id, 6);
|
|
|
|
memcpy(header->oem_table_id, oem_table_id, 8);
|
2009-07-21 23:38:33 +02:00
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2009-07-21 23:38:33 +02:00
|
|
|
header->length = sizeof(acpi_xsdt_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(XSDT);
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Entries are filled in later, we come with an empty set. */
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Fix checksum. */
|
2009-07-21 23:38:33 +02:00
|
|
|
header->checksum = acpi_checksum((void *)xsdt, sizeof(acpi_xsdt_t));
|
2004-01-28 17:56:14 +01:00
|
|
|
}
|
|
|
|
|
2014-10-26 20:42:08 +01:00
|
|
|
static void acpi_write_rsdp(acpi_rsdp_t *rsdp, acpi_rsdt_t *rsdt,
|
|
|
|
acpi_xsdt_t *xsdt, char *oem_id)
|
2004-01-28 17:56:14 +01:00
|
|
|
{
|
2009-11-05 19:06:43 +01:00
|
|
|
memset(rsdp, 0, sizeof(acpi_rsdp_t));
|
2010-11-19 16:14:42 +01:00
|
|
|
|
2004-01-28 17:56:14 +01:00
|
|
|
memcpy(rsdp->signature, RSDP_SIG, 8);
|
2014-10-26 20:42:08 +01:00
|
|
|
memcpy(rsdp->oem_id, oem_id, 6);
|
2010-11-19 16:14:42 +01:00
|
|
|
|
|
|
|
rsdp->length = sizeof(acpi_rsdp_t);
|
2015-06-18 10:11:19 +02:00
|
|
|
rsdp->rsdt_address = (uintptr_t)rsdt;
|
2010-11-19 16:14:42 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2.
|
|
|
|
*
|
|
|
|
* Some OSes expect an XSDT to be present for RSD PTR revisions >= 2.
|
|
|
|
* If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR
|
|
|
|
* revision 0).
|
2009-07-21 23:38:33 +02:00
|
|
|
*/
|
|
|
|
if (xsdt == NULL) {
|
2010-11-19 16:14:42 +01:00
|
|
|
rsdp->revision = 0;
|
2009-07-21 23:38:33 +02:00
|
|
|
} else {
|
2015-06-18 10:11:19 +02:00
|
|
|
rsdp->xsdt_address = (u64)(uintptr_t)xsdt;
|
2018-08-23 02:57:24 +02:00
|
|
|
rsdp->revision = get_acpi_table_revision(RSDP);
|
2009-07-21 23:38:33 +02:00
|
|
|
}
|
2010-11-19 16:14:42 +01:00
|
|
|
|
|
|
|
/* Calculate checksums. */
|
|
|
|
rsdp->checksum = acpi_checksum((void *)rsdp, 20);
|
|
|
|
rsdp->ext_checksum = acpi_checksum((void *)rsdp, sizeof(acpi_rsdp_t));
|
2004-01-28 17:56:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-16 23:18:22 +01:00
|
|
|
unsigned long acpi_create_hest_error_source(acpi_hest_t *hest,
|
|
|
|
acpi_hest_esd_t *esd, u16 type, void *data, u16 data_len)
|
2012-04-13 07:57:14 +02:00
|
|
|
{
|
|
|
|
acpi_header_t *header = &(hest->header);
|
|
|
|
acpi_hest_hen_t *hen;
|
|
|
|
void *pos;
|
|
|
|
u16 len;
|
|
|
|
|
|
|
|
pos = esd;
|
|
|
|
memset(pos, 0, sizeof(acpi_hest_esd_t));
|
|
|
|
len = 0;
|
|
|
|
esd->type = type; /* MCE */
|
|
|
|
esd->source_id = hest->error_source_count;
|
|
|
|
esd->flags = 0; /* FIRMWARE_FIRST */
|
|
|
|
esd->enabled = 1;
|
|
|
|
esd->prealloc_erecords = 1;
|
|
|
|
esd->max_section_per_record = 0x1;
|
|
|
|
|
|
|
|
len += sizeof(acpi_hest_esd_t);
|
|
|
|
pos = esd + 1;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 0: /* MCE */
|
|
|
|
break;
|
|
|
|
case 1: /* CMC */
|
|
|
|
hen = (acpi_hest_hen_t *) (pos);
|
|
|
|
memset(pos, 0, sizeof(acpi_hest_hen_t));
|
|
|
|
hen->type = 3; /* SCI? */
|
|
|
|
hen->length = sizeof(acpi_hest_hen_t);
|
2017-03-16 23:18:22 +01:00
|
|
|
hen->conf_we = 0; /* Configuration Write Enable. */
|
2012-04-13 07:57:14 +02:00
|
|
|
hen->poll_interval = 0;
|
|
|
|
hen->vector = 0;
|
|
|
|
hen->sw2poll_threshold_val = 0;
|
|
|
|
hen->sw2poll_threshold_win = 0;
|
|
|
|
hen->error_threshold_val = 0;
|
|
|
|
hen->error_threshold_win = 0;
|
|
|
|
len += sizeof(acpi_hest_hen_t);
|
|
|
|
pos = hen + 1;
|
|
|
|
break;
|
|
|
|
case 2: /* NMI */
|
|
|
|
case 6: /* AER Root Port */
|
|
|
|
case 7: /* AER Endpoint */
|
|
|
|
case 8: /* AER Bridge */
|
|
|
|
case 9: /* Generic Hardware Error Source. */
|
|
|
|
/* TODO: */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printk(BIOS_DEBUG, "Invalid type of Error Source.");
|
|
|
|
break;
|
|
|
|
}
|
2017-03-16 21:41:11 +01:00
|
|
|
hest->error_source_count++;
|
2012-04-13 07:57:14 +02:00
|
|
|
|
|
|
|
memcpy(pos, data, data_len);
|
|
|
|
len += data_len;
|
2019-05-29 01:48:14 +02:00
|
|
|
if (header)
|
|
|
|
header->length += len;
|
2012-04-13 07:57:14 +02:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ACPI 4.0 */
|
2014-11-09 13:36:18 +01:00
|
|
|
void acpi_write_hest(acpi_hest_t *hest,
|
|
|
|
unsigned long (*acpi_fill_hest)(acpi_hest_t *hest))
|
2012-04-13 07:57:14 +02:00
|
|
|
{
|
|
|
|
acpi_header_t *header = &(hest->header);
|
|
|
|
|
|
|
|
memset(hest, 0, sizeof(acpi_hest_t));
|
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2012-04-13 07:57:14 +02:00
|
|
|
memcpy(header->signature, "HEST", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2012-04-13 07:57:14 +02:00
|
|
|
header->length += sizeof(acpi_hest_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(HEST);
|
2012-04-13 07:57:14 +02:00
|
|
|
|
|
|
|
acpi_fill_hest(hest);
|
|
|
|
|
|
|
|
/* Calculate checksums. */
|
|
|
|
header->checksum = acpi_checksum((void *)hest, header->length);
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:45:26 +02:00
|
|
|
/* ACPI 3.0b */
|
|
|
|
void acpi_write_bert(acpi_bert_t *bert, uintptr_t region, size_t length)
|
|
|
|
{
|
|
|
|
acpi_header_t *header = &(bert->header);
|
|
|
|
|
|
|
|
memset(bert, 0, sizeof(acpi_bert_t));
|
|
|
|
|
2019-05-29 01:48:14 +02:00
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2018-09-04 21:45:26 +02:00
|
|
|
memcpy(header->signature, "BERT", 4);
|
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2019-02-25 15:42:07 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2018-09-04 21:45:26 +02:00
|
|
|
header->length += sizeof(acpi_bert_t);
|
|
|
|
header->revision = get_acpi_table_revision(BERT);
|
|
|
|
|
|
|
|
bert->error_region = region;
|
|
|
|
bert->region_length = length;
|
|
|
|
|
|
|
|
/* Calculate checksums. */
|
|
|
|
header->checksum = acpi_checksum((void *)bert, header->length);
|
|
|
|
}
|
|
|
|
|
2019-03-06 01:53:33 +01:00
|
|
|
#if CONFIG(COMMON_FADT)
|
2017-03-16 21:41:11 +01:00
|
|
|
void acpi_create_fadt(acpi_fadt_t *fadt, acpi_facs_t *facs, void *dsdt)
|
2014-10-16 12:48:19 +02:00
|
|
|
{
|
|
|
|
acpi_header_t *header = &(fadt->header);
|
|
|
|
|
|
|
|
memset((void *) fadt, 0, sizeof(acpi_fadt_t));
|
2019-05-29 01:48:14 +02:00
|
|
|
|
|
|
|
if (!header)
|
|
|
|
return;
|
|
|
|
|
2014-10-16 12:48:19 +02:00
|
|
|
memcpy(header->signature, "FACP", 4);
|
|
|
|
header->length = sizeof(acpi_fadt_t);
|
2018-08-23 02:57:24 +02:00
|
|
|
header->revision = get_acpi_table_revision(FADT);
|
2014-10-16 12:48:19 +02:00
|
|
|
memcpy(header->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
memcpy(header->asl_compiler_id, ASLC, 4);
|
2019-02-15 08:21:33 +01:00
|
|
|
header->asl_compiler_revision = asl_revision;
|
2014-10-16 12:48:19 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-03-06 01:53:33 +01:00
|
|
|
if (CONFIG(SYSTEM_TYPE_CONVERTIBLE) ||
|
|
|
|
CONFIG(SYSTEM_TYPE_LAPTOP))
|
2014-10-16 12:48:19 +02:00
|
|
|
fadt->preferred_pm_profile = PM_MOBILE;
|
2019-03-06 01:53:33 +01:00
|
|
|
else if (CONFIG(SYSTEM_TYPE_DETACHABLE) ||
|
|
|
|
CONFIG(SYSTEM_TYPE_TABLET))
|
2019-02-07 20:30:06 +01:00
|
|
|
fadt->preferred_pm_profile = PM_TABLET;
|
2017-03-16 19:24:09 +01:00
|
|
|
else
|
2014-10-16 12:48:19 +02:00
|
|
|
fadt->preferred_pm_profile = PM_DESKTOP;
|
|
|
|
|
2014-10-25 15:18:25 +02:00
|
|
|
acpi_fill_fadt(fadt);
|
2014-10-16 12:48:19 +02:00
|
|
|
|
|
|
|
header->checksum =
|
|
|
|
acpi_checksum((void *) fadt, header->length);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-21 22:45:32 +02:00
|
|
|
unsigned long __weak fw_cfg_acpi_tables(unsigned long start)
|
2014-09-01 22:18:01 +02:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-30 19:28:05 +02:00
|
|
|
unsigned long write_acpi_tables(unsigned long start)
|
|
|
|
{
|
|
|
|
unsigned long current;
|
|
|
|
acpi_rsdp_t *rsdp;
|
|
|
|
acpi_rsdt_t *rsdt;
|
|
|
|
acpi_xsdt_t *xsdt;
|
|
|
|
acpi_fadt_t *fadt;
|
|
|
|
acpi_facs_t *facs;
|
2014-10-26 20:42:08 +01:00
|
|
|
acpi_header_t *slic_file, *slic;
|
2014-08-30 19:28:05 +02:00
|
|
|
acpi_header_t *ssdt;
|
2015-05-31 12:31:59 +02:00
|
|
|
acpi_header_t *dsdt_file, *dsdt;
|
2014-08-30 19:28:05 +02:00
|
|
|
acpi_mcfg_t *mcfg;
|
2015-05-20 19:52:01 +02:00
|
|
|
acpi_tcpa_t *tcpa;
|
2018-10-18 15:39:34 +02:00
|
|
|
acpi_tpm2_t *tpm2;
|
2014-08-30 19:28:05 +02:00
|
|
|
acpi_madt_t *madt;
|
2014-10-27 13:29:29 +01:00
|
|
|
struct device *dev;
|
2014-09-01 22:18:01 +02:00
|
|
|
unsigned long fw;
|
2015-05-31 12:31:59 +02:00
|
|
|
size_t slic_size, dsdt_size;
|
2014-10-26 20:42:08 +01:00
|
|
|
char oem_id[6], oem_table_id[8];
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
current = start;
|
|
|
|
|
|
|
|
/* Align ACPI tables to 16byte */
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
|
2019-09-10 15:40:47 +02:00
|
|
|
/* Special case for qemu */
|
2014-09-01 22:18:01 +02:00
|
|
|
fw = fw_cfg_acpi_tables(current);
|
2019-09-10 15:40:47 +02:00
|
|
|
if (fw) {
|
|
|
|
rsdp = NULL;
|
|
|
|
/* Find RSDP. */
|
|
|
|
for (void *p = (void *)current; p < (void *)fw; p += 16) {
|
|
|
|
if (valid_rsdp((acpi_rsdp_t *)p)) {
|
|
|
|
rsdp = p;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!rsdp)
|
|
|
|
return fw;
|
|
|
|
|
|
|
|
/* Add BOOT0000 for Linux google firmware driver */
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * SSDT\n");
|
|
|
|
ssdt = (acpi_header_t *)fw;
|
|
|
|
current = (unsigned long)ssdt + sizeof(acpi_header_t);
|
|
|
|
|
|
|
|
memset((void *)ssdt, 0, sizeof(acpi_header_t));
|
|
|
|
|
|
|
|
memcpy(&ssdt->signature, "SSDT", 4);
|
|
|
|
ssdt->revision = get_acpi_table_revision(SSDT);
|
|
|
|
memcpy(&ssdt->oem_id, OEM_ID, 6);
|
|
|
|
memcpy(&ssdt->oem_table_id, oem_table_id, 8);
|
|
|
|
ssdt->oem_revision = 42;
|
|
|
|
memcpy(&ssdt->asl_compiler_id, ASLC, 4);
|
|
|
|
ssdt->asl_compiler_revision = asl_revision;
|
|
|
|
ssdt->length = sizeof(acpi_header_t);
|
|
|
|
|
|
|
|
acpigen_set_current((char *) current);
|
|
|
|
|
|
|
|
/* Write object to declare coreboot tables */
|
|
|
|
acpi_ssdt_write_cbtable();
|
|
|
|
|
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
ssdt->length = current - (unsigned long)ssdt;
|
|
|
|
ssdt->checksum = acpi_checksum((void *)ssdt, ssdt->length);
|
|
|
|
|
|
|
|
acpi_create_ssdt_generator(ssdt, ACPI_TABLE_CREATOR);
|
|
|
|
|
|
|
|
acpi_add_table(rsdp, ssdt);
|
|
|
|
|
2014-09-01 22:18:01 +02:00
|
|
|
return fw;
|
2019-09-10 15:40:47 +02:00
|
|
|
}
|
2014-09-01 22:18:01 +02:00
|
|
|
|
2015-06-02 21:40:29 +02:00
|
|
|
dsdt_file = cbfs_boot_map_with_leak(
|
2015-05-31 12:31:59 +02:00
|
|
|
CONFIG_CBFS_PREFIX "/dsdt.aml",
|
|
|
|
CBFS_TYPE_RAW, &dsdt_size);
|
|
|
|
if (!dsdt_file) {
|
|
|
|
printk(BIOS_ERR, "No DSDT file, skipping ACPI tables\n");
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dsdt_file->length > dsdt_size
|
2016-10-03 22:11:07 +02:00
|
|
|
|| dsdt_file->length < sizeof(acpi_header_t)
|
2015-05-31 12:31:59 +02:00
|
|
|
|| memcmp(dsdt_file->signature, "DSDT", 4) != 0) {
|
|
|
|
printk(BIOS_ERR, "Invalid DSDT file, skipping ACPI tables\n");
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2015-05-16 06:39:23 +02:00
|
|
|
slic_file = cbfs_boot_map_with_leak(CONFIG_CBFS_PREFIX "/slic",
|
2014-10-26 20:42:08 +01:00
|
|
|
CBFS_TYPE_RAW, &slic_size);
|
2015-05-31 11:32:09 +02:00
|
|
|
if (slic_file
|
|
|
|
&& (slic_file->length > slic_size
|
2016-10-03 22:11:07 +02:00
|
|
|
|| slic_file->length < sizeof(acpi_header_t)
|
2015-05-31 11:32:09 +02:00
|
|
|
|| memcmp(slic_file->signature, "SLIC", 4) != 0)) {
|
2014-10-26 20:42:08 +01:00
|
|
|
slic_file = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slic_file) {
|
|
|
|
memcpy(oem_id, slic_file->oem_id, 6);
|
|
|
|
memcpy(oem_table_id, slic_file->oem_table_id, 8);
|
|
|
|
} else {
|
|
|
|
memcpy(oem_id, OEM_ID, 6);
|
|
|
|
memcpy(oem_table_id, ACPI_TABLE_CREATOR, 8);
|
|
|
|
}
|
|
|
|
|
2014-08-30 19:28:05 +02:00
|
|
|
printk(BIOS_INFO, "ACPI: Writing ACPI tables at %lx.\n", start);
|
|
|
|
|
|
|
|
/* We need at least an RSDP and an RSDT Table */
|
|
|
|
rsdp = (acpi_rsdp_t *) current;
|
|
|
|
current += sizeof(acpi_rsdp_t);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
rsdt = (acpi_rsdt_t *) current;
|
|
|
|
current += sizeof(acpi_rsdt_t);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
xsdt = (acpi_xsdt_t *) current;
|
|
|
|
current += sizeof(acpi_xsdt_t);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
/* clear all table memory */
|
|
|
|
memset((void *) start, 0, current - start);
|
|
|
|
|
2014-10-26 20:42:08 +01:00
|
|
|
acpi_write_rsdp(rsdp, rsdt, xsdt, oem_id);
|
|
|
|
acpi_write_rsdt(rsdt, oem_id, oem_table_id);
|
|
|
|
acpi_write_xsdt(xsdt, oem_id, oem_table_id);
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * FACS\n");
|
2019-06-20 13:50:17 +02:00
|
|
|
current = ALIGN_UP(current, 64);
|
2014-08-30 19:28:05 +02:00
|
|
|
facs = (acpi_facs_t *) current;
|
|
|
|
current += sizeof(acpi_facs_t);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
acpi_create_facs(facs);
|
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * DSDT\n");
|
|
|
|
dsdt = (acpi_header_t *) current;
|
2015-05-31 12:31:59 +02:00
|
|
|
memcpy(dsdt, dsdt_file, sizeof(acpi_header_t));
|
2014-10-05 11:10:35 +02:00
|
|
|
if (dsdt->length >= sizeof(acpi_header_t)) {
|
|
|
|
current += sizeof(acpi_header_t);
|
|
|
|
|
|
|
|
acpigen_set_current((char *) current);
|
|
|
|
for (dev = all_devices; dev; dev = dev->next)
|
2017-03-16 19:24:09 +01:00
|
|
|
if (dev->ops && dev->ops->acpi_inject_dsdt_generator)
|
2015-04-12 21:49:46 +02:00
|
|
|
dev->ops->acpi_inject_dsdt_generator(dev);
|
2014-10-05 11:10:35 +02:00
|
|
|
current = (unsigned long) acpigen_get_current();
|
|
|
|
memcpy((char *)current,
|
2015-05-31 12:31:59 +02:00
|
|
|
(char *)dsdt_file + sizeof(acpi_header_t),
|
2014-10-05 11:10:35 +02:00
|
|
|
dsdt->length - sizeof(acpi_header_t));
|
|
|
|
current += dsdt->length - sizeof(acpi_header_t);
|
|
|
|
|
|
|
|
/* (Re)calculate length and checksum. */
|
|
|
|
dsdt->length = current - (unsigned long)dsdt;
|
|
|
|
dsdt->checksum = 0;
|
|
|
|
dsdt->checksum = acpi_checksum((void *)dsdt, dsdt->length);
|
|
|
|
}
|
2014-08-30 19:28:05 +02:00
|
|
|
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * FADT\n");
|
|
|
|
fadt = (acpi_fadt_t *) current;
|
|
|
|
current += sizeof(acpi_fadt_t);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
acpi_create_fadt(fadt, facs, dsdt);
|
|
|
|
acpi_add_table(rsdp, fadt);
|
|
|
|
|
2014-10-26 20:42:08 +01:00
|
|
|
if (slic_file) {
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * SLIC\n");
|
|
|
|
slic = (acpi_header_t *)current;
|
|
|
|
memcpy(slic, slic_file, slic_file->length);
|
|
|
|
current += slic_file->length;
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-10-26 20:42:08 +01:00
|
|
|
acpi_add_table(rsdp, slic);
|
|
|
|
}
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * SSDT\n");
|
|
|
|
ssdt = (acpi_header_t *)current;
|
|
|
|
acpi_create_ssdt_generator(ssdt, ACPI_TABLE_CREATOR);
|
2014-10-10 20:40:41 +02:00
|
|
|
if (ssdt->length > sizeof(acpi_header_t)) {
|
|
|
|
current += ssdt->length;
|
|
|
|
acpi_add_table(rsdp, ssdt);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-10-10 20:40:41 +02:00
|
|
|
}
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "ACPI: * MCFG\n");
|
|
|
|
mcfg = (acpi_mcfg_t *) current;
|
|
|
|
acpi_create_mcfg(mcfg);
|
|
|
|
if (mcfg->header.length > sizeof(acpi_mcfg_t)) {
|
|
|
|
current += mcfg->header.length;
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
acpi_add_table(rsdp, mcfg);
|
|
|
|
}
|
|
|
|
|
2019-03-06 01:53:33 +01:00
|
|
|
if (CONFIG(TPM1)) {
|
2018-10-18 15:39:34 +02:00
|
|
|
printk(BIOS_DEBUG, "ACPI: * TCPA\n");
|
|
|
|
tcpa = (acpi_tcpa_t *) current;
|
|
|
|
acpi_create_tcpa(tcpa);
|
|
|
|
if (tcpa->header.length >= sizeof(acpi_tcpa_t)) {
|
|
|
|
current += tcpa->header.length;
|
|
|
|
current = acpi_align_current(current);
|
|
|
|
acpi_add_table(rsdp, tcpa);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-06 01:53:33 +01:00
|
|
|
if (CONFIG(TPM2)) {
|
2018-10-18 15:39:34 +02:00
|
|
|
printk(BIOS_DEBUG, "ACPI: * TPM2\n");
|
|
|
|
tpm2 = (acpi_tpm2_t *) current;
|
|
|
|
acpi_create_tpm2(tpm2);
|
|
|
|
if (tpm2->header.length >= sizeof(acpi_tpm2_t)) {
|
|
|
|
current += tpm2->header.length;
|
|
|
|
current = acpi_align_current(current);
|
|
|
|
acpi_add_table(rsdp, tpm2);
|
|
|
|
}
|
2015-05-20 19:52:01 +02:00
|
|
|
}
|
|
|
|
|
2014-08-30 19:28:05 +02:00
|
|
|
printk(BIOS_DEBUG, "ACPI: * MADT\n");
|
|
|
|
|
|
|
|
madt = (acpi_madt_t *) current;
|
|
|
|
acpi_create_madt(madt);
|
|
|
|
if (madt->header.length > sizeof(acpi_madt_t)) {
|
2017-03-16 21:41:11 +01:00
|
|
|
current += madt->header.length;
|
|
|
|
acpi_add_table(rsdp, madt);
|
2014-08-30 19:28:05 +02:00
|
|
|
}
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
|
|
|
|
printk(BIOS_DEBUG, "current = %lx\n", current);
|
|
|
|
|
|
|
|
for (dev = all_devices; dev; dev = dev->next) {
|
|
|
|
if (dev->ops && dev->ops->write_acpi_tables) {
|
2017-03-16 23:18:22 +01:00
|
|
|
current = dev->ops->write_acpi_tables(dev, current,
|
|
|
|
rsdp);
|
2015-12-11 00:07:38 +01:00
|
|
|
current = acpi_align_current(current);
|
2014-08-30 19:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printk(BIOS_INFO, "ACPI: done.\n");
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2009-04-13 20:07:02 +02:00
|
|
|
static acpi_rsdp_t *valid_rsdp(acpi_rsdp_t *rsdp)
|
|
|
|
{
|
|
|
|
if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0)
|
|
|
|
return NULL;
|
|
|
|
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "Looking on %p for valid checksum\n", rsdp);
|
2009-04-13 20:07:02 +02:00
|
|
|
|
|
|
|
if (acpi_checksum((void *)rsdp, 20) != 0)
|
|
|
|
return NULL;
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "Checksum 1 passed\n");
|
2009-04-13 20:07:02 +02:00
|
|
|
|
|
|
|
if ((rsdp->revision > 1) && (acpi_checksum((void *)rsdp,
|
|
|
|
rsdp->length) != 0))
|
|
|
|
return NULL;
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "Checksum 2 passed all OK\n");
|
2009-04-13 20:07:02 +02:00
|
|
|
|
|
|
|
return rsdp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *acpi_find_wakeup_vector(void)
|
|
|
|
{
|
|
|
|
char *p, *end;
|
|
|
|
acpi_rsdt_t *rsdt;
|
|
|
|
acpi_facs_t *facs;
|
2012-12-15 03:17:55 +01:00
|
|
|
acpi_fadt_t *fadt = NULL;
|
2016-06-17 07:44:40 +02:00
|
|
|
acpi_rsdp_t *rsdp = NULL;
|
2010-11-19 16:14:42 +01:00
|
|
|
void *wake_vec;
|
2009-04-13 20:07:02 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!acpi_is_wakeup())
|
|
|
|
return NULL;
|
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
printk(BIOS_DEBUG, "Trying to find the wakeup vector...\n");
|
2009-04-13 20:07:02 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
/* Find RSDP. */
|
|
|
|
for (p = (char *)0xe0000; p < (char *)0xfffff; p += 16) {
|
2017-03-17 00:01:40 +01:00
|
|
|
rsdp = valid_rsdp((acpi_rsdp_t *)p);
|
|
|
|
if (rsdp)
|
2009-04-13 20:07:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-11-04 12:25:25 +01:00
|
|
|
if (rsdp == NULL) {
|
|
|
|
printk(BIOS_ALERT,
|
|
|
|
"No RSDP found, wake up from S3 not possible.\n");
|
2009-04-13 20:07:02 +02:00
|
|
|
return NULL;
|
2018-11-04 12:25:25 +01:00
|
|
|
}
|
2009-04-13 20:07:02 +02:00
|
|
|
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "RSDP found at %p\n", rsdp);
|
2015-07-31 01:28:13 +02:00
|
|
|
rsdt = (acpi_rsdt_t *)(uintptr_t)rsdp->rsdt_address;
|
2010-04-27 08:56:47 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
end = (char *)rsdt + rsdt->header.length;
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "RSDT found at %p ends at %p\n", rsdt, end);
|
2009-04-13 20:07:02 +02:00
|
|
|
|
2010-11-19 16:14:42 +01:00
|
|
|
for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) {
|
2015-07-31 01:28:13 +02:00
|
|
|
fadt = (acpi_fadt_t *)(uintptr_t)rsdt->entry[i];
|
2009-07-22 00:15:43 +02:00
|
|
|
if (strncmp((char *)fadt, "FACP", 4) == 0)
|
2009-04-13 20:07:02 +02:00
|
|
|
break;
|
|
|
|
fadt = NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-04 12:25:25 +01:00
|
|
|
if (fadt == NULL) {
|
|
|
|
printk(BIOS_ALERT,
|
|
|
|
"No FADT found, wake up from S3 not possible.\n");
|
2009-04-13 20:07:02 +02:00
|
|
|
return NULL;
|
2018-11-04 12:25:25 +01:00
|
|
|
}
|
2009-04-13 20:07:02 +02:00
|
|
|
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "FADT found at %p\n", fadt);
|
2015-07-31 01:28:13 +02:00
|
|
|
facs = (acpi_facs_t *)(uintptr_t)fadt->firmware_ctrl;
|
2009-04-13 20:07:02 +02:00
|
|
|
|
2009-04-22 10:17:38 +02:00
|
|
|
if (facs == NULL) {
|
2018-11-04 12:25:25 +01:00
|
|
|
printk(BIOS_ALERT,
|
|
|
|
"No FACS found, wake up from S3 not possible.\n");
|
2009-04-13 20:07:02 +02:00
|
|
|
return NULL;
|
2009-04-22 10:17:38 +02:00
|
|
|
}
|
2009-04-13 20:07:02 +02:00
|
|
|
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "FACS found at %p\n", facs);
|
2015-07-31 01:28:13 +02:00
|
|
|
wake_vec = (void *)(uintptr_t)facs->firmware_waking_vector;
|
2010-03-22 12:42:32 +01:00
|
|
|
printk(BIOS_DEBUG, "OS waking vector is %p\n", wake_vec);
|
2010-11-19 16:14:42 +01:00
|
|
|
|
2009-04-13 20:07:02 +02:00
|
|
|
return wake_vec;
|
|
|
|
}
|
|
|
|
|
2018-04-21 22:45:32 +02:00
|
|
|
__weak int acpi_get_gpe(int gpe)
|
2016-09-19 21:04:19 +02:00
|
|
|
{
|
|
|
|
return -1; /* implemented by SOC */
|
|
|
|
}
|
2018-08-23 02:57:24 +02:00
|
|
|
|
|
|
|
int get_acpi_table_revision(enum acpi_tables table)
|
|
|
|
{
|
|
|
|
switch (table) {
|
|
|
|
case FADT:
|
|
|
|
return ACPI_FADT_REV_ACPI_3_0;
|
2018-11-11 15:22:30 +01:00
|
|
|
case MADT: /* ACPI 3.0: 2, ACPI 4.0/5.0: 3, ACPI 6.2b/6.3: 5 */
|
2018-08-21 18:50:57 +02:00
|
|
|
return 2;
|
2018-08-23 02:57:24 +02:00
|
|
|
case MCFG:
|
|
|
|
return 1;
|
|
|
|
case TCPA:
|
|
|
|
return 2;
|
2018-10-18 15:39:34 +02:00
|
|
|
case TPM2:
|
|
|
|
return 4;
|
2018-11-11 15:22:30 +01:00
|
|
|
case SSDT: /* ACPI 3.0 upto 6.3: 2 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 2;
|
2018-11-11 15:22:30 +01:00
|
|
|
case SRAT: /* ACPI 2.0: 1, ACPI 3.0: 2, ACPI 4.0 upto 6.3: 3 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 1; /* TODO Should probably be upgraded to 2 */
|
|
|
|
case DMAR:
|
|
|
|
return 1;
|
2018-11-11 15:22:30 +01:00
|
|
|
case SLIT: /* ACPI 2.0 upto 6.3: 1 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 1;
|
2019-06-14 19:00:04 +02:00
|
|
|
case SPMI: /* IMPI 2.0 */
|
|
|
|
return 5;
|
2018-08-23 02:57:24 +02:00
|
|
|
case HPET: /* Currently 1. Table added in ACPI 2.0. */
|
|
|
|
return 1;
|
2018-11-11 15:22:30 +01:00
|
|
|
case VFCT: /* ACPI 2.0/3.0/4.0: 1 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 1;
|
|
|
|
case IVRS:
|
|
|
|
return IVRS_FORMAT_FIXED;
|
|
|
|
case DBG2:
|
|
|
|
return 0;
|
2018-11-11 15:22:30 +01:00
|
|
|
case FACS: /* ACPI 2.0/3.0: 1, ACPI 4.0 upto 6.3: 2 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 1;
|
2018-11-11 15:22:30 +01:00
|
|
|
case RSDT: /* ACPI 1.0 upto 6.3: 1 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 1;
|
2018-11-11 15:22:30 +01:00
|
|
|
case XSDT: /* ACPI 2.0 upto 6.3: 1 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 1;
|
2018-11-11 15:22:30 +01:00
|
|
|
case RSDP: /* ACPI 2.0 upto 6.3: 2 */
|
2018-08-23 02:57:24 +02:00
|
|
|
return 2;
|
|
|
|
case HEST:
|
|
|
|
return 1;
|
|
|
|
case NHLT:
|
|
|
|
return 5;
|
2018-09-04 21:47:15 +02:00
|
|
|
case BERT:
|
|
|
|
return 1;
|
2018-08-23 02:57:24 +02:00
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|