mirror of https://gitlab.com/qemu-project/qemu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
11 KiB
C
327 lines
11 KiB
C
/*
|
|
* CXL ACPI Implementation
|
|
*
|
|
* Copyright(C) 2020 Intel Corporation.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "hw/sysbus.h"
|
|
#include "hw/pci/pci_bridge.h"
|
|
#include "hw/pci/pci_host.h"
|
|
#include "hw/cxl/cxl.h"
|
|
#include "hw/mem/memory-device.h"
|
|
#include "hw/acpi/acpi.h"
|
|
#include "hw/acpi/aml-build.h"
|
|
#include "hw/acpi/bios-linker-loader.h"
|
|
#include "hw/acpi/cxl.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/uuid.h"
|
|
|
|
void build_cxl_dsm_method(Aml *dev)
|
|
{
|
|
Aml *method, *ifctx, *ifctx2;
|
|
|
|
method = aml_method("_DSM", 4, AML_SERIALIZED);
|
|
{
|
|
Aml *function, *uuid;
|
|
|
|
uuid = aml_arg(0);
|
|
function = aml_arg(2);
|
|
/* CXL spec v3.0 9.17.3.1 _DSM Function for Retrieving QTG ID */
|
|
ifctx = aml_if(aml_equal(
|
|
uuid, aml_touuid("F365F9A6-A7DE-4071-A66A-B40C0B4F8E52")));
|
|
|
|
/* Function 0, standard DSM query function */
|
|
ifctx2 = aml_if(aml_equal(function, aml_int(0)));
|
|
{
|
|
uint8_t byte_list[1] = { 0x01 }; /* function 1 only */
|
|
|
|
aml_append(ifctx2,
|
|
aml_return(aml_buffer(sizeof(byte_list), byte_list)));
|
|
}
|
|
aml_append(ifctx, ifctx2);
|
|
|
|
/*
|
|
* Function 1
|
|
* Creating a package with static values. The max supported QTG ID will
|
|
* be 1 and recommended QTG IDs are 0 and then 1.
|
|
* The values here are statically created to simplify emulation. Values
|
|
* from a real BIOS would be determined by the performance of all the
|
|
* present CXL memory and then assigned.
|
|
*/
|
|
ifctx2 = aml_if(aml_equal(function, aml_int(1)));
|
|
{
|
|
Aml *pak, *pak1;
|
|
|
|
/*
|
|
* Return: A package containing two elements - a WORD that returns
|
|
* the maximum throttling group that the platform supports, and a
|
|
* package containing the QTG ID(s) that the platform recommends.
|
|
* Package {
|
|
* Max Supported QTG ID
|
|
* Package {QTG Recommendations}
|
|
* }
|
|
*
|
|
* While the SPEC specified WORD that hints at the value being
|
|
* 16bit, the ACPI dump of BIOS DSDT table showed that the values
|
|
* are integers with no specific size specification. aml_int() will
|
|
* be used for the values.
|
|
*/
|
|
pak1 = aml_package(2);
|
|
/* Set QTG ID of 0 */
|
|
aml_append(pak1, aml_int(0));
|
|
/* Set QTG ID of 1 */
|
|
aml_append(pak1, aml_int(1));
|
|
|
|
pak = aml_package(2);
|
|
/* Set Max QTG 1 */
|
|
aml_append(pak, aml_int(1));
|
|
aml_append(pak, pak1);
|
|
|
|
aml_append(ifctx2, aml_return(pak));
|
|
}
|
|
aml_append(ifctx, ifctx2);
|
|
}
|
|
aml_append(method, ifctx);
|
|
aml_append(dev, method);
|
|
}
|
|
|
|
static void cedt_build_chbs(GArray *table_data, PXBCXLDev *cxl)
|
|
{
|
|
PXBDev *pxb = PXB_DEV(cxl);
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl_host_bridge);
|
|
struct MemoryRegion *mr = sbd->mmio[0].memory;
|
|
|
|
/* Type */
|
|
build_append_int_noprefix(table_data, 0, 1);
|
|
|
|
/* Reserved */
|
|
build_append_int_noprefix(table_data, 0, 1);
|
|
|
|
/* Record Length */
|
|
build_append_int_noprefix(table_data, 32, 2);
|
|
|
|
/* UID - currently equal to bus number */
|
|
build_append_int_noprefix(table_data, pxb->bus_nr, 4);
|
|
|
|
/* Version */
|
|
build_append_int_noprefix(table_data, 1, 4);
|
|
|
|
/* Reserved */
|
|
build_append_int_noprefix(table_data, 0, 4);
|
|
|
|
/* Base - subregion within a container that is in PA space */
|
|
build_append_int_noprefix(table_data, mr->container->addr + mr->addr, 8);
|
|
|
|
/* Length */
|
|
build_append_int_noprefix(table_data, memory_region_size(mr), 8);
|
|
}
|
|
|
|
/*
|
|
* CFMWS entries in CXL 2.0 ECN: CEDT CFMWS & QTG _DSM.
|
|
* Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory
|
|
* interleaving.
|
|
*/
|
|
static void cedt_build_cfmws(GArray *table_data, CXLState *cxls)
|
|
{
|
|
GList *it;
|
|
|
|
for (it = cxls->fixed_windows; it; it = it->next) {
|
|
CXLFixedWindow *fw = it->data;
|
|
int i;
|
|
|
|
/* Type */
|
|
build_append_int_noprefix(table_data, 1, 1);
|
|
|
|
/* Reserved */
|
|
build_append_int_noprefix(table_data, 0, 1);
|
|
|
|
/* Record Length */
|
|
build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2);
|
|
|
|
/* Reserved */
|
|
build_append_int_noprefix(table_data, 0, 4);
|
|
|
|
/* Base HPA */
|
|
build_append_int_noprefix(table_data, fw->mr.addr, 8);
|
|
|
|
/* Window Size */
|
|
build_append_int_noprefix(table_data, fw->size, 8);
|
|
|
|
/* Host Bridge Interleave Ways */
|
|
build_append_int_noprefix(table_data, fw->enc_int_ways, 1);
|
|
|
|
/* Host Bridge Interleave Arithmetic */
|
|
build_append_int_noprefix(table_data, 0, 1);
|
|
|
|
/* Reserved */
|
|
build_append_int_noprefix(table_data, 0, 2);
|
|
|
|
/* Host Bridge Interleave Granularity */
|
|
build_append_int_noprefix(table_data, fw->enc_int_gran, 4);
|
|
|
|
/* Window Restrictions */
|
|
build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */
|
|
|
|
/* QTG ID */
|
|
build_append_int_noprefix(table_data, 0, 2);
|
|
|
|
/* Host Bridge List (list of UIDs - currently bus_nr) */
|
|
for (i = 0; i < fw->num_targets; i++) {
|
|
g_assert(fw->target_hbs[i]);
|
|
build_append_int_noprefix(table_data, PXB_DEV(fw->target_hbs[i])->bus_nr, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
|
|
{
|
|
Aml *cedt = opaque;
|
|
|
|
if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEV)) {
|
|
cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
|
|
BIOSLinker *linker, const char *oem_id,
|
|
const char *oem_table_id, CXLState *cxl_state)
|
|
{
|
|
Aml *cedt;
|
|
AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
|
|
.oem_table_id = oem_table_id };
|
|
|
|
acpi_add_table(table_offsets, table_data);
|
|
acpi_table_begin(&table, table_data);
|
|
cedt = init_aml_allocator();
|
|
|
|
/* reserve space for CEDT header */
|
|
|
|
object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
|
|
cedt_build_cfmws(cedt->buf, cxl_state);
|
|
|
|
/* copy AML table into ACPI tables blob and patch header there */
|
|
g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
|
|
free_aml_allocator();
|
|
|
|
acpi_table_end(linker, &table);
|
|
}
|
|
|
|
static Aml *__build_cxl_osc_method(void)
|
|
{
|
|
Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
|
|
Aml *a_ctrl = aml_local(0);
|
|
Aml *a_cdw1 = aml_name("CDW1");
|
|
|
|
method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
|
|
/* CDW1 is used for the return value so is present whether or not a match occurs */
|
|
aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
|
|
|
|
/*
|
|
* Generate shared section between:
|
|
* CXL 2.0 - 9.14.2.1.4 and
|
|
* PCI Firmware Specification 3.0
|
|
* 4.5.1. _OSC Interface for PCI Host Bridge Devices
|
|
* The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
|
|
* identified by the Universal Unique IDentifier (UUID)
|
|
* 33DB4D5B-1FF7-401C-9657-7441C03DD766
|
|
* The _OSC interface for a CXL Host bridge is
|
|
* identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC
|
|
* A CXL Host bridge is compatible with a PCI host bridge so
|
|
* for the shared section match both.
|
|
*/
|
|
if_uuid = aml_if(
|
|
aml_lor(aml_equal(aml_arg(0),
|
|
aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")),
|
|
aml_equal(aml_arg(0),
|
|
aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))));
|
|
aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
|
|
aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
|
|
|
|
aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl));
|
|
|
|
/*
|
|
*
|
|
* Allows OS control for all 5 features:
|
|
* PCIeHotplug SHPCHotplug PME AER PCIeCapability
|
|
*/
|
|
aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
|
|
|
|
/*
|
|
* Check _OSC revision.
|
|
* PCI Firmware specification 3.3 and CXL 2.0 both use revision 1
|
|
* Unknown Revision is CDW1 - BIT (3)
|
|
*/
|
|
if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
|
|
aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1));
|
|
aml_append(if_uuid, if_arg1_not_1);
|
|
|
|
if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
|
|
|
|
/* Capability bits were masked */
|
|
aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
|
|
aml_append(if_uuid, if_caps_masked);
|
|
|
|
aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP")));
|
|
aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL")));
|
|
|
|
/* Update DWORD3 (the return value) */
|
|
aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3")));
|
|
|
|
/* CXL only section as per CXL 2.0 - 9.14.2.1.4 */
|
|
if_cxl = aml_if(aml_equal(
|
|
aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")));
|
|
/* CXL support field */
|
|
aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
|
|
/* CXL capabilities */
|
|
aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
|
|
aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
|
|
aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
|
|
|
|
/* CXL 2.0 Port/Device Register access */
|
|
aml_append(if_cxl,
|
|
aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
|
|
aml_append(if_uuid, if_cxl);
|
|
|
|
aml_append(if_uuid, aml_return(aml_arg(3)));
|
|
aml_append(method, if_uuid);
|
|
|
|
/*
|
|
* If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
|
|
* ACPI 6.4 - 6.2.11
|
|
* Unrecognised UUID - BIT(2)
|
|
*/
|
|
else_uuid = aml_else();
|
|
|
|
aml_append(else_uuid,
|
|
aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1")));
|
|
aml_append(else_uuid, aml_return(aml_arg(3)));
|
|
aml_append(method, else_uuid);
|
|
|
|
return method;
|
|
}
|
|
|
|
void build_cxl_osc_method(Aml *dev)
|
|
{
|
|
aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
|
|
aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
|
|
aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
|
|
aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
|
|
aml_append(dev, __build_cxl_osc_method());
|
|
}
|