From 4c87a1868835d05f1cadae7b8ad6a7c95d9d9c0e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Tue, 14 Mar 2017 15:40:33 -0400 Subject: Initial commit of EFI TBOOT work from internal project. Signed-off-by: Ross Philipson --- tboot/acpi.c | 482 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 tboot/acpi.c (limited to 'tboot/acpi.c') diff --git a/tboot/acpi.c b/tboot/acpi.c new file mode 100644 index 0000000..c2a25a4 --- /dev/null +++ b/tboot/acpi.c @@ -0,0 +1,482 @@ +/* + * acpi.c: ACPI utility fns + * + * Copyright (c) 2010, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ACPI_DEBUG +#define acpi_printk printk +#else +#define acpi_printk(...) /* */ +#endif + +static struct acpi_rsdp *rsdp; +static struct acpi_table_header *g_dmar_table; +static __data bool g_hide_dmar; + +static void dump_gas(const char *reg_name, + const tboot_acpi_generic_address_t *reg) +{ + const char *space_id[] = { "memory", "I/O", "PCI config space", "EC", + "SMBus" }; + + printk(TBOOT_DETA"%s GAS @ %p:\n", reg_name, reg); + if ( reg == NULL ) + return; + + if ( reg->space_id >= ARRAY_SIZE(space_id) ) + printk(TBOOT_DETA"\t space_id: unsupported (%u)\n", reg->space_id); + else + printk(TBOOT_DETA"\t space_id: %s\n", space_id[reg->space_id]); + printk(TBOOT_DETA"\t bit_width: %u\n", reg->bit_width); + printk(TBOOT_DETA"\t bit_offset: %u\n", reg->bit_offset); + printk(TBOOT_DETA"\t access_width: %u\n", reg->access_width); + printk(TBOOT_DETA"\t address: %Lx\n", reg->address); +} + +static inline struct acpi_rsdt *get_rsdt(void) +{ + return (struct acpi_rsdt *)(uint64_t)rsdp->rsdp1.rsdt; +} + +static inline struct acpi_xsdt *get_xsdt(void) +{ + if ( rsdp->rsdp_xsdt >= 0x100000000ULL ) { + printk(TBOOT_ERR"XSDT above 4GB\n"); + return NULL; + } + return (struct acpi_xsdt *)(uintptr_t)rsdp->rsdp_xsdt; +} + +static bool verify_acpi_checksum(uint8_t *start, uint8_t len) +{ + uint8_t sum = 0; + while ( len ) { + sum += *start++; + len--; + } + return (sum == 0); +} + +static bool find_rsdp_in_range(void *start, void *end) +{ +#define RSDP_BOUNDARY 16 /* rsdp ranges on 16-byte boundaries */ +#define RSDP_CHKSUM_LEN 20 /* rsdp check sum length, defined in ACPI 1.0 */ + + for ( ; start < end; start += RSDP_BOUNDARY ) { + rsdp = (struct acpi_rsdp *)start; + + if ( memcmp(rsdp->rsdp1.signature, RSDP_SIG, + sizeof(rsdp->rsdp1.signature)) == 0 ) { + if ( verify_acpi_checksum((uint8_t *)rsdp, RSDP_CHKSUM_LEN) ) { + printk(TBOOT_DETA"RSDP (v%u, %.6s) @ %p\n", rsdp->rsdp1.revision, + rsdp->rsdp1.oemid, rsdp); + return true; + } + else { + printk(TBOOT_ERR"checksum failed.\n"); + return false; + } + } + } + return false; +} + +static bool find_rsdp(void) +{ + uint8_t *ldr_rsdp = NULL; + + if ( rsdp != NULL ) + return true; + + /* We can get the RSDP from EFI */ + ldr_rsdp = efi_get_rsdp(); + if (ldr_rsdp != NULL) { + rsdp = (struct acpi_rsdp *) ldr_rsdp; + return true; + } + + /* 0x00 - 0x400 */ + if ( find_rsdp_in_range(RSDP_SCOPE1_LOW, RSDP_SCOPE1_HIGH) ) + return true; + + /* 0xE0000 - 0x100000 */ + if ( find_rsdp_in_range(RSDP_SCOPE2_LOW, RSDP_SCOPE2_HIGH) ) + return true; + + printk(TBOOT_ERR"can't find RSDP\n"); + rsdp = NULL; + return false; +} + +struct acpi_rsdp +*get_rsdp(void) +{ + if (rsdp != NULL) + return rsdp; + if (true == find_rsdp()) + return rsdp; + + return NULL; +} + +/* this function can find dmar table whether or not it was hidden */ +static struct acpi_table_header *find_table(const char *table_name) +{ + if ( !find_rsdp() ) { + printk(TBOOT_ERR"no rsdp to use\n"); + return NULL; + } + + struct acpi_table_header *table = NULL; + struct acpi_xsdt *xsdt = get_xsdt(); /* it is ok even on 1.0 tables */ + /* because value will be ignored */ + + if ( rsdp->rsdp1.revision >= 2 && xsdt != NULL ) { /* ACPI 2.0+ */ + for ( uint64_t *curr_table = (uint64_t*)xsdt->table_offsets; + curr_table < (uint64_t *)((void *)xsdt + xsdt->hdr.length); + curr_table++ ) { + table = (struct acpi_table_header *)(uintptr_t)*curr_table; + if ( memcmp(table->signature, table_name, + sizeof(table->signature)) == 0 ) + return table; + } + } + else { /* ACPI 1.0 */ + struct acpi_rsdt *rsdt = get_rsdt(); + + if ( rsdt == NULL ) { + printk(TBOOT_ERR"rsdt is invalid.\n"); + return NULL; + } + + for ( uint32_t *curr_table = rsdt->table_offsets; + curr_table < (uint32_t *)((void *)rsdt + rsdt->hdr.length); + curr_table++ ) { + table = (struct acpi_table_header *)(uintptr_t)*curr_table; + if ( memcmp(table->signature, table_name, + sizeof(table->signature)) == 0 ) + return table; + } + } + + printk(TBOOT_ERR"cann't find %s table.\n", table_name); + return NULL; +} + +static struct acpi_dmar *get_vtd_dmar_table(void) +{ + return (struct acpi_dmar *)find_table(DMAR_SIG); +} + +bool save_vtd_dmar_table(void) +{ + /* find DMAR table and save it */ + g_dmar_table = (struct acpi_table_header *)get_vtd_dmar_table(); + + printk(TBOOT_DETA"DMAR table @ %p saved.\n", g_dmar_table); + return true; +} + +bool restore_vtd_dmar_table(void) +{ + struct acpi_table_header *hdr; + + g_hide_dmar = false; + + /* find DMAR table first */ + hdr = (struct acpi_table_header *)get_vtd_dmar_table(); + if ( hdr != NULL ) { + printk(TBOOT_DETA"DMAR table @ %p is still there, skip restore step.\n", hdr); + return true; + } + + /* check saved DMAR table */ + if ( g_dmar_table == NULL ) { + printk(TBOOT_ERR"No DMAR table saved, abort restore step.\n"); + return false; + } + + /* restore DMAR if needed */ + memcpy(g_dmar_table->signature, DMAR_SIG, sizeof(g_dmar_table->signature)); + + /* need to hide DMAR table while resume from S3 */ + g_hide_dmar = true; + printk(TBOOT_DETA"DMAR table @ %p restored.\n", hdr); + return true; +} + +bool remove_vtd_dmar_table(void) +{ + struct acpi_table_header *hdr; + + /* check whether it is needed */ + if ( !g_hide_dmar ) { + printk(TBOOT_DETA"No need to hide DMAR table.\n"); + return true; + } + + /* find DMAR table */ + hdr = (struct acpi_table_header *)get_vtd_dmar_table(); + if ( hdr == NULL ) { + printk(TBOOT_DETA"No DMAR table, skip remove step.\n"); + return true; + } + + /* remove DMAR table */ + hdr->signature[0] = '\0'; + printk(TBOOT_DETA"DMAR table @ %p removed.\n", hdr); + return true; +} + +static struct acpi_madt *get_apic_table(void) +{ + return (struct acpi_madt *)find_table(MADT_SIG); +} + +uint32_t get_madt_apic_base(void) +{ + struct acpi_madt *madt = get_apic_table(); + if ( madt == NULL ) { + printk(TBOOT_ERR"no MADT table found\n"); + return 0; + } + return (uint32_t)madt->local_apic_address; +} + +struct acpi_table_ioapic *get_acpi_ioapic_table(void) +{ + struct acpi_madt *madt = get_apic_table(); + if ( madt == NULL ) { + printk(TBOOT_ERR"no MADT table found\n"); + return NULL; + } + + /* APIC tables begin after MADT */ + union acpi_madt_entry *entry = (union acpi_madt_entry *)(madt + 1); + + while ( (void *)entry < ((void *)madt + madt->hdr.length) ) { + uint8_t length = entry->madt_lapic.length; + + if ( entry->madt_lapic.apic_type == ACPI_MADT_IOAPIC ) { + if ( length != sizeof(entry->madt_ioapic) ) { + printk(TBOOT_ERR"APIC length error.\n"); + return NULL; + } + return (struct acpi_table_ioapic *)entry; + } + entry = (void *)entry + length; + } + printk(TBOOT_ERR"no IOAPIC type.\n"); + return NULL; +} + +struct acpi_mcfg *get_acpi_mcfg_table(void) +{ + return (struct acpi_mcfg *)find_table(MCFG_SIG); +} + +static bool write_to_reg(const tboot_acpi_generic_address_t *reg, + uint32_t val) +{ + if ( reg->address >= 100000000ULL ) { + printk(TBOOT_ERR"GAS address >4GB (0x%Lx)\n", reg->address); + return false; + } + uint64_t address = (uint64_t)reg->address; + + if ( reg->space_id == GAS_SYSTEM_IOSPACE ) { + switch ( reg->bit_width ) { + case 8: + outb(address, (uint8_t)val); + return true; + case 16: + outw(address, (uint16_t)val); + return true; + case 32: + outl(address, val); + return true; + default: + printk(TBOOT_ERR"unsupported GAS bit width: %u\n", reg->bit_width); + return false; + } + } + else if ( reg->space_id == GAS_SYSTEM_MEMORY ) { + switch ( reg->bit_width ) { + case 8: + writeb(address, (uint8_t)val); + return true; + case 16: + writew(address, (uint16_t)val); + return true; + case 32: + writel(address, val); + return true; + default: + printk(TBOOT_ERR"unsupported GAS bit width: %u\n", reg->bit_width); + return false; + } + } + + printk(TBOOT_ERR"unsupported GAS addr space ID: %u\n", reg->space_id); + return false; +} + +static bool read_from_reg(const tboot_acpi_generic_address_t *reg, + uint32_t *val) +{ + if ( reg->address >= 100000000ULL ) { + printk(TBOOT_ERR"GAS address >4GB (0x%Lx)\n", reg->address); + return false; + } + uint64_t address = (uint64_t)reg->address; + + if ( reg->space_id == GAS_SYSTEM_IOSPACE ) { + switch ( reg->bit_width ) { + case 8: + *val = inb(address); + return true; + case 16: + *val = inw(address); + return true; + case 32: + *val = inl(address); + return true; default: + printk(TBOOT_ERR"unsupported GAS bit width: %u\n", reg->bit_width); + return false; + } + } + else if ( reg->space_id == GAS_SYSTEM_MEMORY ) { + switch ( reg->bit_width ) { + case 8: + *val = readb(address); + return true; + case 16: + *val = readw(address); + return true; + case 32: + *val = readl(address); + return true; + default: + printk(TBOOT_ERR"unsupported GAS bit width: %u\n", reg->bit_width); + return false; + } + } + + printk(TBOOT_ERR"unsupported GAS addr space ID: %u\n", reg->space_id); + return false; +} + +static void wait_to_sleep(const tboot_acpi_sleep_info_t *acpi_sinfo) +{ +#define WAKE_STATUS 0x8000 /* the 15th bit */ + while ( true ) { + uint32_t pm1a_value = 0, pm1b_value = 0; + + if ( acpi_sinfo->pm1a_evt_blk.address ) { + if ( read_from_reg(&acpi_sinfo->pm1a_evt_blk, &pm1a_value) && + ( pm1a_value & WAKE_STATUS ) ) + return; + } + + if ( acpi_sinfo->pm1b_evt_blk.address ) { + if ( read_from_reg(&acpi_sinfo->pm1b_evt_blk, &pm1b_value) && + ( pm1b_value & WAKE_STATUS ) ) + return; + } + } +} + +bool machine_sleep(const tboot_acpi_sleep_info_t *acpi_sinfo) +{ + dump_gas("PM1A", &acpi_sinfo->pm1a_cnt_blk); + dump_gas("PM1B", &acpi_sinfo->pm1b_cnt_blk); + + wbinvd(); + + if ( acpi_sinfo->pm1a_cnt_blk.address ) { + if ( !write_to_reg(&acpi_sinfo->pm1a_cnt_blk, acpi_sinfo->pm1a_cnt_val) ) + return false;; + } + + if ( acpi_sinfo->pm1b_cnt_blk.address ) { + if ( !write_to_reg(&acpi_sinfo->pm1b_cnt_blk, acpi_sinfo->pm1b_cnt_val) ) + return false; + } + + /* just to wait, the machine may shutdown before here */ + wait_to_sleep(acpi_sinfo); + return true; +} + +void set_s3_resume_vector(const tboot_acpi_sleep_info_t *acpi_sinfo, + uint64_t resume_vector) +{ + if ( acpi_sinfo->vector_width <= 32 ) + *(uint32_t *)(unsigned long long)(acpi_sinfo->wakeup_vector) = + (uint32_t)resume_vector; + else if ( acpi_sinfo->vector_width <= 64 ) + *(uint64_t *)(unsigned long long)(acpi_sinfo->wakeup_vector) = + resume_vector; + else + printk(TBOOT_WARN"vector_width error.\n"); + + acpi_printk(TBOOT_DETA"wakeup_vector_address = %llx\n", acpi_sinfo->wakeup_vector); + acpi_printk(TBOOT_DETA"wakeup_vector_value = %llxx\n", resume_vector); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3