diff options
author | Hollis Blanchard <hollisb@us.ibm.com> | 2006-11-29 14:16:36 -0600 |
---|---|---|
committer | Hollis Blanchard <hollisb@us.ibm.com> | 2006-11-29 14:16:36 -0600 |
commit | ab26a6a563a0acb589af87a8e063c0e171d75665 (patch) | |
tree | 71a432bde5d016e928ab3ad7860fca01312ec787 /tools/firmware/hvmloader/acpi_utils.c | |
parent | d3be8a6ca1aa9312cc01e780a2fea56ab8ec12b4 (diff) | |
parent | 1c804664cf63f0c2e80d0420e52d5f82c3956685 (diff) | |
download | xen-ab26a6a563a0acb589af87a8e063c0e171d75665.tar.gz xen-ab26a6a563a0acb589af87a8e063c0e171d75665.tar.bz2 xen-ab26a6a563a0acb589af87a8e063c0e171d75665.zip |
Merge with xen-unstable.hg.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
Diffstat (limited to 'tools/firmware/hvmloader/acpi_utils.c')
-rw-r--r-- | tools/firmware/hvmloader/acpi_utils.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/tools/firmware/hvmloader/acpi_utils.c b/tools/firmware/hvmloader/acpi_utils.c new file mode 100644 index 0000000000..5fd9847d4e --- /dev/null +++ b/tools/firmware/hvmloader/acpi_utils.c @@ -0,0 +1,318 @@ +/* + * Commonly used ACPI utility functions. + * Probing for devices and writing SSDT entries into XSDT and RSDT tables. + * + * Yu Ke, ke.yu@intel.com + * Copyright (c) 2005, Intel Corporation. + * Copyright (c) 2006, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "acpi/acpi2_0.h" +#include "acpi_utils.h" +#include "util.h" +#include <xen/hvm/e820.h> + +static int acpi_rsdt_add_entry_pointer(unsigned char *acpi_start, + unsigned char *entry); +static int acpi_xsdt_add_entry_pointer(unsigned char *acpi_start, + unsigned char *entry); +static unsigned char *acpi_xsdt_add_entry(unsigned char *acpi_start, + unsigned char **freemem, + unsigned char *limit, + unsigned char *table, + unsigned int table_size); + +void set_checksum(void *start, int checksum_offset, int len) +{ + unsigned char sum = 0; + unsigned char *ptr; + + ptr = start; + ptr[checksum_offset] = 0; + while ( len-- ) + sum += *ptr++; + + ptr = start; + ptr[checksum_offset] = -sum; +} + + +#include "acpi_ssdt_tpm.h" +static void acpi_tpm_tis_probe(unsigned char *acpi_start, + unsigned char **freemem, + unsigned char *limit) +{ + unsigned char *addr; + ACPI_2_0_TCPA_CLIENT *tcpa; + /* check TPM_DID, TPM_VID, TPM_RID in ioemu/hw/tpm_tis.c */ + uint16_t tis_did_vid_rid[] = {0x0001, 0x0001, 0x0001}; + static const ACPI_2_0_TCPA_CLIENT Tcpa = { + .header = { + .signature = ACPI_2_0_TCPA_SIGNATURE, + .length = sizeof(ACPI_2_0_TCPA_CLIENT), + .revision = ACPI_2_0_TCPA_REVISION, + .oem_id = {'I', 'B', 'M', ' ', ' ', ' '}, + .oem_table_id = ASCII64(' ', ' ', ' ', ' ', ' ', 'x', 'e', 'n'), + .oem_revision = 1, + .creator_id = ASCII32('I', 'B', 'M', ' '), + .creator_revision = 1, + } + }; + + /* probe for TIS interface ... */ + if ( memcmp((char *)(0xFED40000 + 0xF00), + tis_did_vid_rid, + sizeof(tis_did_vid_rid)) != 0 ) + return; + + printf("TIS is available\n"); + addr = acpi_xsdt_add_entry(acpi_start, freemem, limit, + AmlCode_TPM, sizeof(AmlCode_TPM)); + if ( addr == NULL ) + return; + + /* legacy systems need an RSDT entry */ + if ( acpi_rsdt_add_entry_pointer(acpi_start, addr) != 1 ) + return; + + /* add ACPI TCPA table */ + addr = acpi_xsdt_add_entry(acpi_start, freemem, limit, + (unsigned char *)&Tcpa, + sizeof(Tcpa)); + if ( addr == NULL ) + return; + + tcpa = (ACPI_2_0_TCPA_CLIENT *)addr; + tcpa->LASA = e820_malloc( + ACPI_2_0_TCPA_LAML_SIZE, E820_RESERVED, (uint32_t)~0); + if ( tcpa->LASA ) + { + tcpa->LAML = ACPI_2_0_TCPA_LAML_SIZE; + memset((char *)(unsigned long)tcpa->LASA, + 0x0, + tcpa->LAML); + set_checksum(tcpa, + FIELD_OFFSET(struct acpi_header, checksum), + tcpa->header.length); + } + + if ( acpi_rsdt_add_entry_pointer(acpi_start, addr) != 1 ) + return; +} + + +/* + * Call functions that probe for devices and have them register their + * SSDT entries with the XSDT and RSDT tables. + */ +void acpi_update(unsigned char *acpi_start, + unsigned long acpi_size, + unsigned char *limit, + unsigned char **freemem) +{ + acpi_tpm_tis_probe(acpi_start, freemem, limit); +} + + +/* + * Search for the RSDP in memory below the BIOS + */ +struct acpi_20_rsdp *acpi_rsdp_get(unsigned char *acpi_start) +{ + int offset = 0; + int found = 0; + static int displayed = 0; + struct acpi_20_rsdp *rsdp; + + while ( &acpi_start[offset] < (unsigned char *)0xf0000 ) + { + rsdp = (struct acpi_20_rsdp *)&acpi_start[offset]; + if ( rsdp->signature == ACPI_2_0_RSDP_SIGNATURE ) + { + found = 1; + break; + } + offset += 0x10; + } + + if ( !found ) + rsdp = NULL; + + if ( !displayed ) + { + if ( rsdp ) + printf("Found RSDP at %lx\n",(long)rsdp); + else + printf("ERROR: RSDP was not found\n"); + displayed = 1; + } + + return rsdp; +} + + +struct acpi_20_rsdt *acpi_rsdt_get(unsigned char *acpi_start) +{ + struct acpi_20_rsdp *rsdp; + struct acpi_20_rsdt *rsdt; + + rsdp = acpi_rsdp_get(acpi_start); + if (!rsdp) + return NULL; + + rsdt = (struct acpi_20_rsdt *) + (acpi_start + rsdp->rsdt_address - ACPI_PHYSICAL_ADDRESS); + if ( rsdt->header.signature != ACPI_2_0_RSDT_SIGNATURE ) + { + printf("Bad RSDT signature\n"); + return NULL; + } + + return rsdt; +} + +/* + * Add an entry to the RSDT table given the pointer to the entry. + */ +static int acpi_rsdt_add_entry_pointer(unsigned char *acpi_start, + unsigned char *entry) +{ + struct acpi_20_rsdt *rsdt = acpi_rsdt_get(acpi_start); + int found = 0; + int i = 0; + + /* Find an empty slot in the RSDT table. */ + while ( i < ACPI_MAX_NUM_TABLES ) + { + if ( rsdt->entry[i] == 0 ) + { + found = 1; + break; + } + i++; + } + + if ( found ) + { + rsdt->entry[i] = (uint64_t)(unsigned long)entry; + rsdt->header.length = + sizeof(struct acpi_header) + + (i + 1) * sizeof(uint64_t); + set_checksum(rsdt, + FIELD_OFFSET(struct acpi_header, checksum), + rsdt->header.length); + } + + return found; +} + +/* Get the XSDT table. */ +struct acpi_20_xsdt *acpi_xsdt_get(unsigned char *acpi_start) +{ + struct acpi_20_rsdp *rsdp; + struct acpi_20_xsdt *xsdt; + + rsdp = acpi_rsdp_get(acpi_start); + if (!rsdp) + return NULL; + + xsdt = (struct acpi_20_xsdt *) + (acpi_start + rsdp->xsdt_address - ACPI_PHYSICAL_ADDRESS); + if ( xsdt->header.signature != ACPI_2_0_XSDT_SIGNATURE ) + { + printf("Bad XSDT signature\n"); + return NULL; + } + return xsdt; +} + +/* + * Add an entry to the XSDT table given the pointer to the entry. + */ +static int acpi_xsdt_add_entry_pointer(unsigned char *acpi_start, + unsigned char *entry) +{ + struct acpi_20_xsdt *xsdt = acpi_xsdt_get(acpi_start); + int found = 0; + int i = 0; + + /* Find an empty slot in the XSDT table. */ + while ( i < ACPI_MAX_NUM_TABLES ) + { + if ( xsdt->entry[i] == 0 ) + { + found = 1; + break; + } + i++; + } + + if ( found ) + { + xsdt->entry[i] = (uint64_t)(unsigned long)entry; + xsdt->header.length = + sizeof(struct acpi_header) + + (i + 1) * sizeof(uint64_t); + set_checksum(xsdt, + FIELD_OFFSET(struct acpi_header, checksum), + xsdt->header.length); + } + + return found; +} + +/* + add an entry to the xdst table entry pointers + copy the given ssdt data to the current available memory at + freemem, if it does not exceed the limit + */ +static unsigned char *acpi_xsdt_add_entry(unsigned char *acpi_start, + unsigned char **freemem, + unsigned char *limit, + unsigned char *table, + unsigned int table_size) +{ + struct acpi_20_xsdt *xsdt = acpi_xsdt_get(acpi_start); + int found = 0, i = 0; + unsigned char *addr = NULL; + + /* Check for an empty slot in the Xsdt table. */ + while ( i < ACPI_MAX_NUM_TABLES ) + { + if ( xsdt->entry[i] == 0 ) + { + found = 1; + break; + } + i++; + } + + if ( found ) + { + /* memory below hard limit ? */ + if ( (*freemem + table_size) <= limit ) + { + addr = *freemem; + memcpy(addr, table, table_size); + printf("Copied dyn. ACPI entry to %lx\n",(long)addr); + *freemem += ((table_size + 0xf) & ~0xf); + + acpi_xsdt_add_entry_pointer(acpi_start, addr); + } + } + + return addr; +} |