mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
The GPL and LGPL SPDX forms without an explicit -only or -or-later suffix are deprecated and should not be used. Update the documention. Somewhat unhelpfully at the time of writing, this only appears to be indicated by the separation of the two tables at https://spdx.org/licenses/ The recent changes to libacpi are the only examples of deprecated LGPL tags in tree, so fix them all up. For GPL, we have many examples using deprecated tags. For now, just identify them as such and recommend that no new instances get added. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Jan Beulich <jbeulich@suse.com> Acked-by: Stefano Stabellini <sstabellini@kernel.org>
734 lines
25 KiB
C
734 lines
25 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-only */
|
|
/*
|
|
* Copyright (c) 2004, Intel Corporation.
|
|
* Copyright (c) 2006, Keir Fraser, XenSource Inc.
|
|
*/
|
|
|
|
#include LIBACPI_STDUTILS
|
|
#include "acpi2_0.h"
|
|
#include "libacpi.h"
|
|
#include "ssdt_s3.h"
|
|
#include "ssdt_s4.h"
|
|
#include "ssdt_tpm.h"
|
|
#include "ssdt_tpm2.h"
|
|
#include "ssdt_pm.h"
|
|
#include "ssdt_laptop_slate.h"
|
|
#include <xen/hvm/hvm_info_table.h>
|
|
#include <xen/hvm/hvm_xs_strings.h>
|
|
#include <xen/hvm/params.h>
|
|
#include <xen/memory.h>
|
|
|
|
#define ACPI_MAX_SECONDARY_TABLES 16
|
|
|
|
#define align16(sz) (((sz) + 15) & ~15)
|
|
#define fixed_strcpy(d, s) strncpy((d), (s), sizeof(d))
|
|
|
|
extern struct acpi_20_rsdp Rsdp;
|
|
extern struct acpi_20_rsdt Rsdt;
|
|
extern struct acpi_20_xsdt Xsdt;
|
|
extern struct acpi_fadt Fadt;
|
|
extern struct acpi_20_facs Facs;
|
|
extern struct acpi_20_waet Waet;
|
|
|
|
/*
|
|
* Located at ACPI_INFO_PHYSICAL_ADDRESS.
|
|
*
|
|
* This must match the Field("BIOS"....) definition in the DSDT.
|
|
*/
|
|
struct acpi_info {
|
|
uint8_t com1_present:1; /* 0[0] - System has COM1? */
|
|
uint8_t com2_present:1; /* 0[1] - System has COM2? */
|
|
uint8_t lpt1_present:1; /* 0[2] - System has LPT1? */
|
|
uint8_t hpet_present:1; /* 0[3] - System has HPET? */
|
|
uint16_t nr_cpus; /* 2 - Number of CPUs */
|
|
uint32_t pci_min, pci_len; /* 4, 8 - PCI I/O hole boundaries */
|
|
uint32_t madt_csum_addr; /* 12 - Address of MADT checksum */
|
|
uint32_t madt_lapic0_addr; /* 16 - Address of first MADT LAPIC struct */
|
|
uint32_t vm_gid_addr; /* 20 - Address of VM generation id buffer */
|
|
uint64_t pci_hi_min, pci_hi_len; /* 24, 32 - PCI I/O hole boundaries */
|
|
};
|
|
|
|
static void set_checksum(
|
|
void *table, uint32_t checksum_offset, uint32_t length)
|
|
{
|
|
uint8_t *p, sum = 0;
|
|
|
|
p = table;
|
|
p[checksum_offset] = 0;
|
|
|
|
while ( length-- )
|
|
sum = sum + *p++;
|
|
|
|
p = table;
|
|
p[checksum_offset] = -sum;
|
|
}
|
|
|
|
static struct acpi_20_madt *construct_madt(struct acpi_ctxt *ctxt,
|
|
const struct acpi_config *config,
|
|
struct acpi_info *info)
|
|
{
|
|
struct acpi_20_madt *madt;
|
|
struct acpi_20_madt_intsrcovr *intsrcovr;
|
|
struct acpi_20_madt_ioapic *io_apic;
|
|
struct acpi_20_madt_lapic *lapic;
|
|
const struct hvm_info_table *hvminfo = config->hvminfo;
|
|
int i, sz;
|
|
|
|
if ( config->lapic_id == NULL )
|
|
return NULL;
|
|
|
|
sz = sizeof(struct acpi_20_madt);
|
|
sz += sizeof(struct acpi_20_madt_intsrcovr) * 16;
|
|
sz += sizeof(struct acpi_20_madt_ioapic);
|
|
sz += sizeof(struct acpi_20_madt_lapic) * hvminfo->nr_vcpus;
|
|
|
|
madt = ctxt->mem_ops.alloc(ctxt, sz, 16);
|
|
if (!madt) return NULL;
|
|
|
|
memset(madt, 0, sizeof(*madt));
|
|
madt->header.signature = ACPI_2_0_MADT_SIGNATURE;
|
|
madt->header.revision = ACPI_2_0_MADT_REVISION;
|
|
fixed_strcpy(madt->header.oem_id, ACPI_OEM_ID);
|
|
fixed_strcpy(madt->header.oem_table_id, ACPI_OEM_TABLE_ID);
|
|
madt->header.oem_revision = ACPI_OEM_REVISION;
|
|
madt->header.creator_id = ACPI_CREATOR_ID;
|
|
madt->header.creator_revision = ACPI_CREATOR_REVISION;
|
|
madt->lapic_addr = config->lapic_base_address;
|
|
madt->flags = ACPI_PCAT_COMPAT;
|
|
|
|
if ( config->table_flags & ACPI_HAS_IOAPIC )
|
|
{
|
|
intsrcovr = (struct acpi_20_madt_intsrcovr *)(madt + 1);
|
|
for ( i = 0; i < 16; i++ )
|
|
{
|
|
memset(intsrcovr, 0, sizeof(*intsrcovr));
|
|
intsrcovr->type = ACPI_INTERRUPT_SOURCE_OVERRIDE;
|
|
intsrcovr->length = sizeof(*intsrcovr);
|
|
intsrcovr->source = i;
|
|
|
|
if ( i == 0 )
|
|
{
|
|
/* ISA IRQ0 routed to IOAPIC GSI 2. */
|
|
intsrcovr->gsi = 2;
|
|
intsrcovr->flags = 0x0;
|
|
}
|
|
else if ( config->pci_isa_irq_mask & (1U << i) )
|
|
{
|
|
/* PCI: active-low level-triggered. */
|
|
intsrcovr->gsi = i;
|
|
intsrcovr->flags = 0xf;
|
|
}
|
|
else
|
|
{
|
|
/* No need for a INT source override structure. */
|
|
continue;
|
|
}
|
|
|
|
intsrcovr++;
|
|
}
|
|
|
|
io_apic = (struct acpi_20_madt_ioapic *)intsrcovr;
|
|
memset(io_apic, 0, sizeof(*io_apic));
|
|
io_apic->type = ACPI_IO_APIC;
|
|
io_apic->length = sizeof(*io_apic);
|
|
io_apic->ioapic_id = config->ioapic_id;
|
|
io_apic->ioapic_addr = config->ioapic_base_address;
|
|
|
|
lapic = (struct acpi_20_madt_lapic *)(io_apic + 1);
|
|
}
|
|
else
|
|
lapic = (struct acpi_20_madt_lapic *)(madt + 1);
|
|
|
|
info->nr_cpus = hvminfo->nr_vcpus;
|
|
info->madt_lapic0_addr = ctxt->mem_ops.v2p(ctxt, lapic);
|
|
for ( i = 0; i < hvminfo->nr_vcpus; i++ )
|
|
{
|
|
memset(lapic, 0, sizeof(*lapic));
|
|
lapic->type = ACPI_PROCESSOR_LOCAL_APIC;
|
|
lapic->length = sizeof(*lapic);
|
|
/* Processor ID must match processor-object IDs in the DSDT. */
|
|
lapic->acpi_processor_id = i;
|
|
lapic->apic_id = config->lapic_id(i);
|
|
lapic->flags = (test_bit(i, hvminfo->vcpu_online)
|
|
? ACPI_LOCAL_APIC_ENABLED : 0);
|
|
lapic++;
|
|
}
|
|
|
|
madt->header.length = (unsigned char *)lapic - (unsigned char *)madt;
|
|
set_checksum(madt, offsetof(struct acpi_header, checksum),
|
|
madt->header.length);
|
|
info->madt_csum_addr =
|
|
ctxt->mem_ops.v2p(ctxt, &madt->header.checksum);
|
|
|
|
return madt;
|
|
}
|
|
|
|
static struct acpi_20_hpet *construct_hpet(struct acpi_ctxt *ctxt,
|
|
const struct acpi_config *config)
|
|
{
|
|
struct acpi_20_hpet *hpet;
|
|
|
|
hpet = ctxt->mem_ops.alloc(ctxt, sizeof(*hpet), 16);
|
|
if (!hpet) return NULL;
|
|
|
|
memset(hpet, 0, sizeof(*hpet));
|
|
hpet->header.signature = ACPI_2_0_HPET_SIGNATURE;
|
|
hpet->header.revision = ACPI_2_0_HPET_REVISION;
|
|
fixed_strcpy(hpet->header.oem_id, ACPI_OEM_ID);
|
|
fixed_strcpy(hpet->header.oem_table_id, ACPI_OEM_TABLE_ID);
|
|
hpet->header.oem_revision = ACPI_OEM_REVISION;
|
|
hpet->header.creator_id = ACPI_CREATOR_ID;
|
|
hpet->header.creator_revision = ACPI_CREATOR_REVISION;
|
|
hpet->timer_block_id = 0x8086a201;
|
|
hpet->addr.address = ACPI_HPET_ADDRESS;
|
|
|
|
hpet->header.length = sizeof(*hpet);
|
|
set_checksum(hpet, offsetof(struct acpi_header, checksum), sizeof(*hpet));
|
|
return hpet;
|
|
}
|
|
|
|
static struct acpi_20_waet *construct_waet(struct acpi_ctxt *ctxt,
|
|
const struct acpi_config *config)
|
|
{
|
|
struct acpi_20_waet *waet;
|
|
|
|
waet = ctxt->mem_ops.alloc(ctxt, sizeof(*waet), 16);
|
|
if (!waet) return NULL;
|
|
|
|
memcpy(waet, &Waet, sizeof(*waet));
|
|
|
|
waet->header.length = sizeof(*waet);
|
|
set_checksum(waet, offsetof(struct acpi_header, checksum), sizeof(*waet));
|
|
|
|
return waet;
|
|
}
|
|
|
|
static struct acpi_20_srat *construct_srat(struct acpi_ctxt *ctxt,
|
|
const struct acpi_config *config)
|
|
{
|
|
struct acpi_20_srat *srat;
|
|
struct acpi_20_srat_processor *processor;
|
|
struct acpi_20_srat_memory *memory;
|
|
unsigned int size;
|
|
void *p;
|
|
unsigned int i;
|
|
|
|
size = sizeof(*srat) + sizeof(*processor) * config->hvminfo->nr_vcpus +
|
|
sizeof(*memory) * config->numa.nr_vmemranges;
|
|
|
|
p = ctxt->mem_ops.alloc(ctxt, size, 16);
|
|
if ( !p )
|
|
return NULL;
|
|
|
|
srat = memset(p, 0, size);
|
|
srat->header.signature = ACPI_2_0_SRAT_SIGNATURE;
|
|
srat->header.revision = ACPI_2_0_SRAT_REVISION;
|
|
fixed_strcpy(srat->header.oem_id, ACPI_OEM_ID);
|
|
fixed_strcpy(srat->header.oem_table_id, ACPI_OEM_TABLE_ID);
|
|
srat->header.oem_revision = ACPI_OEM_REVISION;
|
|
srat->header.creator_id = ACPI_CREATOR_ID;
|
|
srat->header.creator_revision = ACPI_CREATOR_REVISION;
|
|
srat->table_revision = ACPI_SRAT_TABLE_REVISION;
|
|
|
|
processor = (struct acpi_20_srat_processor *)(srat + 1);
|
|
for ( i = 0; i < config->hvminfo->nr_vcpus; i++ )
|
|
{
|
|
processor->type = ACPI_PROCESSOR_AFFINITY;
|
|
processor->length = sizeof(*processor);
|
|
processor->domain = config->numa.vcpu_to_vnode[i];
|
|
processor->apic_id = config->lapic_id(i);
|
|
processor->flags = ACPI_LOCAL_APIC_AFFIN_ENABLED;
|
|
processor++;
|
|
}
|
|
|
|
memory = (struct acpi_20_srat_memory *)processor;
|
|
for ( i = 0; i < config->numa.nr_vmemranges; i++ )
|
|
{
|
|
memory->type = ACPI_MEMORY_AFFINITY;
|
|
memory->length = sizeof(*memory);
|
|
memory->domain = config->numa.vmemrange[i].nid;
|
|
memory->flags = ACPI_MEM_AFFIN_ENABLED;
|
|
memory->base_address = config->numa.vmemrange[i].start;
|
|
memory->mem_length = config->numa.vmemrange[i].end -
|
|
config->numa.vmemrange[i].start;
|
|
memory++;
|
|
}
|
|
|
|
ASSERT(((unsigned long)memory) - ((unsigned long)p) == size);
|
|
|
|
srat->header.length = size;
|
|
set_checksum(srat, offsetof(struct acpi_header, checksum), size);
|
|
|
|
return srat;
|
|
}
|
|
|
|
static struct acpi_20_slit *construct_slit(struct acpi_ctxt *ctxt,
|
|
const struct acpi_config *config)
|
|
{
|
|
struct acpi_20_slit *slit;
|
|
unsigned int i, num, size;
|
|
|
|
num = config->numa.nr_vnodes * config->numa.nr_vnodes;
|
|
size = sizeof(*slit) + num * sizeof(uint8_t);
|
|
|
|
slit = ctxt->mem_ops.alloc(ctxt, size, 16);
|
|
if ( !slit )
|
|
return NULL;
|
|
|
|
memset(slit, 0, size);
|
|
slit->header.signature = ACPI_2_0_SLIT_SIGNATURE;
|
|
slit->header.revision = ACPI_2_0_SLIT_REVISION;
|
|
fixed_strcpy(slit->header.oem_id, ACPI_OEM_ID);
|
|
fixed_strcpy(slit->header.oem_table_id, ACPI_OEM_TABLE_ID);
|
|
slit->header.oem_revision = ACPI_OEM_REVISION;
|
|
slit->header.creator_id = ACPI_CREATOR_ID;
|
|
slit->header.creator_revision = ACPI_CREATOR_REVISION;
|
|
|
|
for ( i = 0; i < num; i++ )
|
|
slit->entry[i] = config->numa.vdistance[i];
|
|
|
|
slit->localities = config->numa.nr_vnodes;
|
|
|
|
slit->header.length = size;
|
|
set_checksum(slit, offsetof(struct acpi_header, checksum), size);
|
|
|
|
return slit;
|
|
}
|
|
|
|
static int construct_passthrough_tables(struct acpi_ctxt *ctxt,
|
|
unsigned long *table_ptrs,
|
|
int nr_tables,
|
|
struct acpi_config *config)
|
|
{
|
|
unsigned long pt_addr;
|
|
struct acpi_header *header;
|
|
int nr_added;
|
|
int nr_max = (ACPI_MAX_SECONDARY_TABLES - nr_tables - 1);
|
|
uint32_t total = 0;
|
|
uint8_t *buffer;
|
|
|
|
if ( config->pt.addr == 0 )
|
|
return 0;
|
|
|
|
pt_addr = config->pt.addr;
|
|
|
|
for ( nr_added = 0; nr_added < nr_max; nr_added++ )
|
|
{
|
|
if ( (config->pt.length - total) < sizeof(struct acpi_header) )
|
|
break;
|
|
|
|
header = (struct acpi_header*)pt_addr;
|
|
|
|
buffer = ctxt->mem_ops.alloc(ctxt, header->length, 16);
|
|
if ( buffer == NULL )
|
|
break;
|
|
memcpy(buffer, header, header->length);
|
|
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, buffer);
|
|
total += header->length;
|
|
pt_addr += header->length;
|
|
}
|
|
|
|
return nr_added;
|
|
}
|
|
|
|
static int construct_secondary_tables(struct acpi_ctxt *ctxt,
|
|
unsigned long *table_ptrs,
|
|
struct acpi_config *config,
|
|
struct acpi_info *info)
|
|
{
|
|
int nr_tables = 0;
|
|
struct acpi_20_madt *madt;
|
|
struct acpi_20_hpet *hpet;
|
|
struct acpi_20_waet *waet;
|
|
struct acpi_20_tcpa *tcpa;
|
|
struct acpi_20_tpm2 *tpm2;
|
|
unsigned char *ssdt;
|
|
void *lasa;
|
|
|
|
/* MADT. */
|
|
if ( (config->hvminfo->nr_vcpus > 1) || config->hvminfo->apic_mode )
|
|
{
|
|
madt = construct_madt(ctxt, config, info);
|
|
if (!madt) return -1;
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, madt);
|
|
}
|
|
|
|
/* HPET. */
|
|
if ( info->hpet_present )
|
|
{
|
|
hpet = construct_hpet(ctxt, config);
|
|
if (!hpet) return -1;
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, hpet);
|
|
}
|
|
|
|
/* WAET. */
|
|
if ( config->table_flags & ACPI_HAS_WAET )
|
|
{
|
|
waet = construct_waet(ctxt, config);
|
|
if ( !waet )
|
|
return -1;
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, waet);
|
|
}
|
|
|
|
if ( config->table_flags & ACPI_HAS_SSDT_PM )
|
|
{
|
|
ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_pm), 16);
|
|
if (!ssdt) return -1;
|
|
memcpy(ssdt, ssdt_pm, sizeof(ssdt_pm));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt);
|
|
}
|
|
|
|
if ( config->table_flags & ACPI_HAS_SSDT_S3 )
|
|
{
|
|
ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_s3), 16);
|
|
if (!ssdt) return -1;
|
|
memcpy(ssdt, ssdt_s3, sizeof(ssdt_s3));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt);
|
|
}
|
|
|
|
if ( config->table_flags & ACPI_HAS_SSDT_S4 )
|
|
{
|
|
ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_s4), 16);
|
|
if (!ssdt) return -1;
|
|
memcpy(ssdt, ssdt_s4, sizeof(ssdt_s4));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt);
|
|
}
|
|
|
|
if ( config->table_flags & ACPI_HAS_SSDT_LAPTOP_SLATE )
|
|
{
|
|
ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_laptop_slate), 16);
|
|
if (!ssdt) return -1;
|
|
memcpy(ssdt, ssdt_laptop_slate, sizeof(ssdt_laptop_slate));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt);
|
|
}
|
|
/* TPM and its SSDT. */
|
|
if ( config->table_flags & ACPI_HAS_TPM )
|
|
{
|
|
switch ( config->tpm_version )
|
|
{
|
|
case 1:
|
|
if ( config->tis_hdr[0] == 0 || config->tis_hdr[0] == 0xffff ||
|
|
config->tis_hdr[1] == 0 || config->tis_hdr[1] == 0xffff )
|
|
break;
|
|
|
|
ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_tpm), 16);
|
|
if (!ssdt) return -1;
|
|
memcpy(ssdt, ssdt_tpm, sizeof(ssdt_tpm));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt);
|
|
|
|
tcpa = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_tcpa), 16);
|
|
if (!tcpa) return -1;
|
|
memset(tcpa, 0, sizeof(*tcpa));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, tcpa);
|
|
|
|
tcpa->header.signature = ACPI_2_0_TCPA_SIGNATURE;
|
|
tcpa->header.length = sizeof(*tcpa);
|
|
tcpa->header.revision = ACPI_2_0_TCPA_REVISION;
|
|
fixed_strcpy(tcpa->header.oem_id, ACPI_OEM_ID);
|
|
fixed_strcpy(tcpa->header.oem_table_id, ACPI_OEM_TABLE_ID);
|
|
tcpa->header.oem_revision = ACPI_OEM_REVISION;
|
|
tcpa->header.creator_id = ACPI_CREATOR_ID;
|
|
tcpa->header.creator_revision = ACPI_CREATOR_REVISION;
|
|
|
|
lasa = ctxt->mem_ops.alloc(ctxt, ACPI_2_0_TCPA_LAML_SIZE, 16);
|
|
if ( lasa )
|
|
{
|
|
tcpa->lasa = ctxt->mem_ops.v2p(ctxt, lasa);
|
|
tcpa->laml = ACPI_2_0_TCPA_LAML_SIZE;
|
|
memset(lasa, 0, tcpa->laml);
|
|
set_checksum(tcpa,
|
|
offsetof(struct acpi_header, checksum),
|
|
tcpa->header.length);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
/*
|
|
* Check VID stored in bits 37:32 (3rd 16 bit word) of CRB
|
|
* identifier register. See table 16 of TCG PC client platform
|
|
* TPM profile specification for TPM 2.0.
|
|
*/
|
|
if ( config->crb_id[2] == 0 || config->crb_id[2] == 0xffff )
|
|
break;
|
|
|
|
ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_tpm2), 16);
|
|
if (!ssdt) return -1;
|
|
memcpy(ssdt, ssdt_tpm2, sizeof(ssdt_tpm2));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt);
|
|
|
|
tpm2 = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_tpm2), 16);
|
|
if (!tpm2) return -1;
|
|
memset(tpm2, 0, sizeof(*tpm2));
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, tpm2);
|
|
|
|
tpm2->header.signature = ACPI_2_0_TPM2_SIGNATURE;
|
|
tpm2->header.length = sizeof(*tpm2);
|
|
tpm2->header.revision = ACPI_2_0_TPM2_REVISION;
|
|
fixed_strcpy(tpm2->header.oem_id, ACPI_OEM_ID);
|
|
fixed_strcpy(tpm2->header.oem_table_id, ACPI_OEM_TABLE_ID);
|
|
tpm2->header.oem_revision = ACPI_OEM_REVISION;
|
|
tpm2->header.creator_id = ACPI_CREATOR_ID;
|
|
tpm2->header.creator_revision = ACPI_CREATOR_REVISION;
|
|
tpm2->platform_class = TPM2_ACPI_CLASS_CLIENT;
|
|
tpm2->control_area_address = TPM_CRB_CTRL_REQ;
|
|
tpm2->start_method = TPM2_START_METHOD_CRB;
|
|
tpm2->log_area_minimum_length = TPM_LOG_AREA_MINIMUM_SIZE;
|
|
tpm2->log_area_start_address = TPM_LOG_AREA_ADDRESS;
|
|
|
|
set_checksum(tpm2,
|
|
offsetof(struct acpi_header, checksum),
|
|
tpm2->header.length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* SRAT and SLIT */
|
|
if ( config->numa.nr_vnodes > 0 )
|
|
{
|
|
struct acpi_20_srat *srat = construct_srat(ctxt, config);
|
|
struct acpi_20_slit *slit = construct_slit(ctxt, config);
|
|
|
|
if ( srat )
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, srat);
|
|
else
|
|
printf("Failed to build SRAT, skipping...\n");
|
|
if ( slit )
|
|
table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, slit);
|
|
else
|
|
printf("Failed to build SLIT, skipping...\n");
|
|
}
|
|
|
|
/* Load any additional tables passed through. */
|
|
nr_tables += construct_passthrough_tables(ctxt, table_ptrs,
|
|
nr_tables, config);
|
|
|
|
table_ptrs[nr_tables] = 0;
|
|
return nr_tables;
|
|
}
|
|
|
|
/**
|
|
* Allocate and initialize Windows Generation ID
|
|
* If value is not present in the XenStore or if all zeroes
|
|
* the device will be not active
|
|
*
|
|
* Return 0 if memory failure, != 0 if success
|
|
*/
|
|
static int new_vm_gid(struct acpi_ctxt *ctxt,
|
|
struct acpi_config *config,
|
|
struct acpi_info *info)
|
|
{
|
|
uint64_t *buf;
|
|
|
|
info->vm_gid_addr = 0;
|
|
|
|
/* check for 0 ID*/
|
|
if ( !config->vm_gid[0] && !config->vm_gid[1] )
|
|
return 1;
|
|
|
|
/* copy to allocate BIOS memory */
|
|
buf = ctxt->mem_ops.alloc(ctxt, sizeof(config->vm_gid), 8);
|
|
if ( !buf )
|
|
return 0;
|
|
memcpy(buf, config->vm_gid, sizeof(config->vm_gid));
|
|
|
|
/* set the address into ACPI table and also pass it back to the caller */
|
|
info->vm_gid_addr = ctxt->mem_ops.v2p(ctxt, buf);
|
|
config->vm_gid_addr = info->vm_gid_addr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int acpi_build_tables(struct acpi_ctxt *ctxt, struct acpi_config *config)
|
|
{
|
|
struct acpi_info *acpi_info;
|
|
struct acpi_20_rsdp *rsdp;
|
|
struct acpi_20_rsdt *rsdt;
|
|
struct acpi_20_xsdt *xsdt;
|
|
struct acpi_fadt *fadt;
|
|
struct acpi_10_fadt *fadt_10;
|
|
struct acpi_20_facs *facs;
|
|
unsigned char *dsdt;
|
|
unsigned long secondary_tables[ACPI_MAX_SECONDARY_TABLES];
|
|
int nr_secondaries, i;
|
|
unsigned int fadt_size;
|
|
|
|
acpi_info = (struct acpi_info *)config->infop;
|
|
memset(acpi_info, 0, sizeof(*acpi_info));
|
|
acpi_info->com1_present = !!(config->table_flags & ACPI_HAS_COM1);
|
|
acpi_info->com2_present = !!(config->table_flags & ACPI_HAS_COM2);
|
|
acpi_info->lpt1_present = !!(config->table_flags & ACPI_HAS_LPT1);
|
|
acpi_info->hpet_present = !!(config->table_flags & ACPI_HAS_HPET);
|
|
acpi_info->pci_min = config->pci_start;
|
|
acpi_info->pci_len = config->pci_len;
|
|
if ( config->pci_hi_len )
|
|
{
|
|
acpi_info->pci_hi_min = config->pci_hi_start;
|
|
acpi_info->pci_hi_len = config->pci_hi_len;
|
|
}
|
|
|
|
/*
|
|
* Fill in high-memory data structures, starting at @buf.
|
|
*/
|
|
|
|
facs = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_facs), 64);
|
|
if (!facs) goto oom;
|
|
memcpy(facs, &Facs, sizeof(struct acpi_20_facs));
|
|
|
|
/*
|
|
* Alternative DSDTs we get linked against. A cover-all DSDT for up to the
|
|
* implementation-defined maximum number of VCPUs, and an alternative for use
|
|
* when a guest can only have up to 15 VCPUs.
|
|
*
|
|
* The latter is required for Windows 2000, which experiences a BSOD of
|
|
* KMODE_EXCEPTION_NOT_HANDLED if it sees more than 15 processor objects.
|
|
*/
|
|
if ( config->hvminfo->nr_vcpus <= 15 && config->dsdt_15cpu)
|
|
{
|
|
dsdt = ctxt->mem_ops.alloc(ctxt, config->dsdt_15cpu_len, 16);
|
|
if (!dsdt) goto oom;
|
|
memcpy(dsdt, config->dsdt_15cpu, config->dsdt_15cpu_len);
|
|
}
|
|
else
|
|
{
|
|
dsdt = ctxt->mem_ops.alloc(ctxt, config->dsdt_anycpu_len, 16);
|
|
if (!dsdt) goto oom;
|
|
memcpy(dsdt, config->dsdt_anycpu, config->dsdt_anycpu_len);
|
|
}
|
|
|
|
/*
|
|
* N.B. ACPI 1.0 operating systems may not handle FADT with revision 2
|
|
* or above properly, notably Windows 2000, which tries to copy FADT
|
|
* into a 116 bytes buffer thus causing an overflow. The solution is to
|
|
* link the higher revision FADT with the XSDT only and introduce a
|
|
* compatible revision 1 FADT that is linked with the RSDT. Refer to:
|
|
* http://www.acpi.info/presentations/S01USMOBS169_OS%20new.ppt
|
|
*/
|
|
fadt_10 = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_10_fadt), 16);
|
|
if (!fadt_10) goto oom;
|
|
memcpy(fadt_10, &Fadt, sizeof(struct acpi_10_fadt));
|
|
fadt_10->header.length = sizeof(struct acpi_10_fadt);
|
|
fadt_10->header.revision = ACPI_1_0_FADT_REVISION;
|
|
fadt_10->dsdt = ctxt->mem_ops.v2p(ctxt, dsdt);
|
|
fadt_10->firmware_ctrl = ctxt->mem_ops.v2p(ctxt, facs);
|
|
set_checksum(fadt_10,
|
|
offsetof(struct acpi_header, checksum),
|
|
sizeof(struct acpi_10_fadt));
|
|
|
|
switch ( config->acpi_revision )
|
|
{
|
|
case 4:
|
|
/*
|
|
* NB: we can use offsetof because there's no padding between
|
|
* x_gpe1_blk and sleep_control.
|
|
*/
|
|
fadt_size = offsetof(struct acpi_fadt, sleep_control);
|
|
break;
|
|
case 5:
|
|
fadt_size = sizeof(*fadt);
|
|
break;
|
|
default:
|
|
printf("ACPI revision %u not supported\n", config->acpi_revision);
|
|
return -1;
|
|
}
|
|
fadt = ctxt->mem_ops.alloc(ctxt, fadt_size, 16);
|
|
if (!fadt) goto oom;
|
|
if ( !(config->table_flags & ACPI_HAS_PMTIMER) )
|
|
{
|
|
Fadt.pm_tmr_blk = Fadt.pm_tmr_len = 0;
|
|
memset(&Fadt.x_pm_tmr_blk, 0, sizeof(Fadt.x_pm_tmr_blk));
|
|
}
|
|
if ( !(config->table_flags & ACPI_HAS_BUTTONS) )
|
|
Fadt.flags |= (ACPI_PWR_BUTTON | ACPI_SLP_BUTTON);
|
|
memcpy(fadt, &Fadt, fadt_size);
|
|
/*
|
|
* For both ACPI 4 and 5 the revision of the FADT matches the ACPI
|
|
* revision.
|
|
*/
|
|
fadt->header.revision = config->acpi_revision;
|
|
fadt->header.length = fadt_size;
|
|
fadt->dsdt = ctxt->mem_ops.v2p(ctxt, dsdt);
|
|
fadt->x_dsdt = ctxt->mem_ops.v2p(ctxt, dsdt);
|
|
fadt->firmware_ctrl = ctxt->mem_ops.v2p(ctxt, facs);
|
|
fadt->x_firmware_ctrl = ctxt->mem_ops.v2p(ctxt, facs);
|
|
if ( !(config->table_flags & ACPI_HAS_VGA) )
|
|
fadt->iapc_boot_arch |= ACPI_FADT_NO_VGA;
|
|
if ( config->table_flags & ACPI_HAS_8042 )
|
|
fadt->iapc_boot_arch |= ACPI_FADT_8042;
|
|
if ( !(config->table_flags & ACPI_HAS_CMOS_RTC) )
|
|
{
|
|
if ( fadt->header.revision < 5 )
|
|
{
|
|
printf("ACPI_FADT_NO_CMOS_RTC requires FADT revision 5\n");
|
|
return -1;
|
|
}
|
|
fadt->iapc_boot_arch |= ACPI_FADT_NO_CMOS_RTC;
|
|
}
|
|
set_checksum(fadt, offsetof(struct acpi_header, checksum), fadt_size);
|
|
|
|
nr_secondaries = construct_secondary_tables(ctxt, secondary_tables,
|
|
config, acpi_info);
|
|
if ( nr_secondaries < 0 )
|
|
goto oom;
|
|
|
|
xsdt = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_xsdt) +
|
|
sizeof(uint64_t) * nr_secondaries,
|
|
16);
|
|
if (!xsdt) goto oom;
|
|
memcpy(xsdt, &Xsdt, sizeof(struct acpi_header));
|
|
xsdt->entry[0] = ctxt->mem_ops.v2p(ctxt, fadt);
|
|
for ( i = 0; secondary_tables[i]; i++ )
|
|
xsdt->entry[i+1] = secondary_tables[i];
|
|
xsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint64_t);
|
|
set_checksum(xsdt,
|
|
offsetof(struct acpi_header, checksum),
|
|
xsdt->header.length);
|
|
|
|
rsdt = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_rsdt) +
|
|
sizeof(uint32_t) * nr_secondaries,
|
|
16);
|
|
if (!rsdt) goto oom;
|
|
memcpy(rsdt, &Rsdt, sizeof(struct acpi_header));
|
|
rsdt->entry[0] = ctxt->mem_ops.v2p(ctxt, fadt_10);
|
|
for ( i = 0; secondary_tables[i]; i++ )
|
|
rsdt->entry[i+1] = secondary_tables[i];
|
|
rsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint32_t);
|
|
set_checksum(rsdt,
|
|
offsetof(struct acpi_header, checksum),
|
|
rsdt->header.length);
|
|
|
|
/*
|
|
* Fill in low-memory data structures: acpi_info and RSDP.
|
|
*/
|
|
rsdp = (struct acpi_20_rsdp *)config->rsdp;
|
|
|
|
memcpy(rsdp, &Rsdp, sizeof(struct acpi_20_rsdp));
|
|
rsdp->rsdt_address = ctxt->mem_ops.v2p(ctxt, rsdt);
|
|
rsdp->xsdt_address = ctxt->mem_ops.v2p(ctxt, xsdt);
|
|
set_checksum(rsdp,
|
|
offsetof(struct acpi_10_rsdp, checksum),
|
|
sizeof(struct acpi_10_rsdp));
|
|
set_checksum(rsdp,
|
|
offsetof(struct acpi_20_rsdp, extended_checksum),
|
|
sizeof(struct acpi_20_rsdp));
|
|
|
|
if ( !new_vm_gid(ctxt, config, acpi_info) )
|
|
goto oom;
|
|
|
|
return 0;
|
|
|
|
oom:
|
|
printf("unable to build ACPI tables: out of memory\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-file-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|