Message ID | 20220913182149.1468366-2-titusr@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Initial PECI bus support | expand |
On Tue, Sep 13, 2022 at 06:21:47PM +0000, Titus Rwantare wrote: > PECI - Platform Environment Control Interface > > This commit adds support for reading basic sensor values from a client > on the PECI bus. > BMCs can use the PECI wire to get thermal information out of an Intel > cpu. Additionally, on hardware, various MSRs are exposed over the > PECI bus. Part of PCI config space is exposed due to Intel posting > various platform configuration in PCI config space. > > Commands implemented: > - Ping > - GetDIB > - GetTemp > - GetPkgConfig (partial) > > Commands not implemented: > - RdIAMSR > - RdPCIConfig > - RdPCIConfigLocal > > Signed-off-by: Titus Rwantare <titusr@google.com> > Reviewed-by: Hao Wu <wuhaotsh@google.com> > Reviewed-by: Patrick Venture <venture@google.com> Reviewed-by: Peter Delevoryas <peter@pjd.dev> > --- > MAINTAINERS | 7 ++ > hw/Kconfig | 1 + > hw/meson.build | 1 + > hw/peci/Kconfig | 2 + > hw/peci/meson.build | 1 + > hw/peci/peci-client.c | 230 +++++++++++++++++++++++++++++++++++++++++ > hw/peci/peci-core.c | 182 ++++++++++++++++++++++++++++++++ > hw/peci/trace-events | 5 + > hw/peci/trace.h | 1 + > include/hw/peci/peci.h | 194 ++++++++++++++++++++++++++++++++++ > meson.build | 1 + > 11 files changed, 625 insertions(+) > create mode 100644 hw/peci/Kconfig > create mode 100644 hw/peci/meson.build > create mode 100644 hw/peci/peci-client.c > create mode 100644 hw/peci/peci-core.c > create mode 100644 hw/peci/trace-events > create mode 100644 hw/peci/trace.h > create mode 100644 include/hw/peci/peci.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 5ce4227ff6..14ab29679d 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -3212,6 +3212,13 @@ F: tests/qtest/adm1272-test.c > F: tests/qtest/max34451-test.c > F: tests/qtest/isl_pmbus_vr-test.c > > +PECI > +M: Titus Rwantare <titusr@google.com> > +S: Maintained > +F: hw/peci/peci-core.c > +F: hw/peci/peci-client.c > +F: include/hw/peci/peci.h > + > Firmware schema specifications > M: Philippe Mathieu-Daudé <f4bug@amsat.org> > R: Daniel P. Berrange <berrange@redhat.com> > diff --git a/hw/Kconfig b/hw/Kconfig > index 38233bbb0f..300ab48127 100644 > --- a/hw/Kconfig > +++ b/hw/Kconfig > @@ -24,6 +24,7 @@ source net/Kconfig > source nubus/Kconfig > source nvme/Kconfig > source nvram/Kconfig > +source peci/Kconfig > source pci-bridge/Kconfig > source pci-host/Kconfig > source pcmcia/Kconfig > diff --git a/hw/meson.build b/hw/meson.build > index c7ac7d3d75..340cc88a52 100644 > --- a/hw/meson.build > +++ b/hw/meson.build > @@ -28,6 +28,7 @@ subdir('pci') > subdir('pci-bridge') > subdir('pci-host') > subdir('pcmcia') > +subdir('peci') > subdir('rdma') > subdir('rtc') > subdir('scsi') > diff --git a/hw/peci/Kconfig b/hw/peci/Kconfig > new file mode 100644 > index 0000000000..fe4f665d21 > --- /dev/null > +++ b/hw/peci/Kconfig > @@ -0,0 +1,2 @@ > +config PECI > + bool > diff --git a/hw/peci/meson.build b/hw/peci/meson.build > new file mode 100644 > index 0000000000..01cfa95abe > --- /dev/null > +++ b/hw/peci/meson.build > @@ -0,0 +1 @@ > +softmmu_ss.add(when: 'CONFIG_PECI', if_true: files('peci-core.c', 'peci-client.c')) > diff --git a/hw/peci/peci-client.c b/hw/peci/peci-client.c > new file mode 100644 > index 0000000000..2aa797b5f6 > --- /dev/null > +++ b/hw/peci/peci-client.c > @@ -0,0 +1,230 @@ > +/* > + * PECI Client device > + * Copyright 2021 Google LLC > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "hw/peci/peci.h" > +#include "hw/qdev-core.h" > +#include "hw/qdev-properties.h" > +#include "migration/vmstate.h" > +#include "qapi/error.h" > +#include "qapi/visitor.h" > +#include "qemu/module.h" > +#include "qemu/log.h" > +#include "qom/object.h" > + > +/* > + * A PECI client represents an Intel socket and the peripherals attached to it > + * that are accessible over the PECI bus. > + */ > + > +#define PECI_CLIENT_DEFAULT_TEMP 30 > + > +static void peci_client_update_temps(PECIClientDevice *client) > +{ > + uint8_t temp_cpu = 0; > + for (size_t i = 0; i < client->cpu_cores; i++) { > + if (temp_cpu < client->core_temp[i]) { > + temp_cpu = client->core_temp[i]; > + } > + } > + client->core_temp_max = -1 * (client->tjmax - temp_cpu); > + > + uint8_t temp_dimm = 0; > + for (size_t i = 0; i < client->dimms; i++) { > + if (temp_dimm < client->dimm_temp[i]) { > + temp_dimm = client->dimm_temp[i]; > + } > + } > + client->dimm_temp_max = temp_dimm; > +} > + > +PECIClientDevice *peci_get_client(PECIBus *bus, uint8_t addr) > +{ > + PECIClientDevice *client; > + BusChild *var; > + > + QTAILQ_FOREACH(var, &bus->qbus.children, sibling) { > + DeviceState *dev = var->child; > + client = PECI_CLIENT(dev); > + > + if (client->address == addr) { > + return client; > + } > + } > + return 0; > +} > + > + > +PECIClientDevice *peci_add_client(PECIBus *bus, > + uint8_t address, > + PECIClientProperties *props) > +{ > + DeviceState *dev = qdev_new("peci-client"); > + PECIClientDevice *client; > + > + /* Only 8 addresses supported as of rev 4.1, 0x30 to 0x37 */ > + if (address < PECI_BASE_ADDR || address > PECI_BASE_ADDR + 7) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to add client at 0x%02x", > + __func__, address); > + return 0; > + } > + > + client = PECI_CLIENT(dev); > + client->address = address; > + > + /* these fields are optional, get set to max if unset or invalid */ > + if (!props->cpu_cores || props->cpu_cores > PECI_NUM_CPUS_MAX) { > + props->cpu_cores = PECI_NUM_CPUS_MAX; > + } > + if (!props->dimms || props->dimms > PECI_NUM_DIMMS_MAX) { > + props->dimms = PECI_NUM_DIMMS_MAX; > + } > + if (!props->cpu_family) { > + props->cpu_family = FAM6_ICELAKE_X; > + } > + if (!props->dimms_per_channel || > + props->dimms_per_channel > PECI_DIMMS_PER_CHANNEL_MAX) { > + props->dimms_per_channel = 2; > + } > + > + client->cpu_id = props->cpu_family; > + client->cpu_cores = props->cpu_cores; > + client->dimms = props->dimms; > + client->dimms_per_channel = props->dimms_per_channel; > + > + /* TODO: find real revisions and TJ max for supported families */ > + client->tjmax = 90; > + client->tcontrol = -5; > + > + switch (props->cpu_family) { > + case FAM6_HASWELL_X: > + client->revision = 0x31; > + break; > + > + case FAM6_BROADWELL_X: > + client->revision = 0x32; > + break; > + > + case FAM6_SKYLAKE_X: > + case FAM6_SKYLAKE_XD: > + client->revision = 0x36; > + break; > + > + case FAM6_ICELAKE_X: > + case FAM6_SAPPHIRE_RAPIDS_X: > + client->revision = 0x40; > + client->ucode = 0x8c0004a0; > + break; > + > + default: > + qemu_log_mask(LOG_UNIMP, "%s: unsupported cpu: 0x%x\n", > + __func__, props->cpu_family); > + client->revision = 0x31; > + break; > + } > + > + qdev_realize_and_unref(&client->qdev, &bus->qbus, &error_abort); > + bus->num_clients += 1; > + return client; > +} > + > +static void peci_client_get(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + /* use millidegrees convention */ > + uint32_t value = *(uint8_t *)opaque * 1000; > + visit_type_uint32(v, name, &value, errp); > +} > + > +static void peci_client_set(Object *obj, Visitor *v, const char *name, > + void *opaque, Error **errp) > +{ > + PECIClientDevice *client = PECI_CLIENT(obj); > + uint8_t *internal = opaque; > + uint32_t value; > + > + if (!visit_type_uint32(v, name, &value, errp)) { > + return; > + } > + > + g_assert(value <= 255000); > + > + *internal = value / 1000; > + peci_client_update_temps(client); > +} > + > +static void peci_client_reset(Object *obj, ResetType type) > +{ > + PECIClientDevice *client = PECI_CLIENT(obj); > + client->core_temp_max = 0; > + client->dimm_temp_max = 0; > +} > + > +static const VMStateDescription vmstate_peci_client = { > + .name = TYPE_PECI_CLIENT, > + .version_id = 0, > + .minimum_version_id = 0, > + .fields = (VMStateField[]) { > + VMSTATE_UINT8(address, PECIClientDevice), > + VMSTATE_UINT8(device_info, PECIClientDevice), > + VMSTATE_UINT8(revision, PECIClientDevice), > + VMSTATE_UINT32(cpu_id, PECIClientDevice), > + VMSTATE_UINT8(cpu_cores, PECIClientDevice), > + VMSTATE_UINT8(tjmax, PECIClientDevice), > + VMSTATE_INT8(tcontrol, PECIClientDevice), > + VMSTATE_UINT8_ARRAY(core_temp, PECIClientDevice, PECI_NUM_CPUS_MAX), > + VMSTATE_UINT8(dimms, PECIClientDevice), > + VMSTATE_UINT8(dimms_per_channel, PECIClientDevice), > + VMSTATE_UINT8_ARRAY(dimm_temp, PECIClientDevice , PECI_NUM_DIMMS_MAX), > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +static void peci_client_realize(DeviceState *dev, Error **errp) > +{ > + PECIClientDevice *client = PECI_CLIENT(dev); > + for (size_t i = 0; i < client->cpu_cores; i++) { > + client->core_temp[i] = PECI_CLIENT_DEFAULT_TEMP; > + object_property_add(OBJECT(client), "cpu_temp[*]", "uint8", > + peci_client_get, > + peci_client_set, NULL, &client->core_temp[i]); > + } > + > + for (size_t i = 0; i < client->dimms; i++) { > + client->dimm_temp[i] = PECI_CLIENT_DEFAULT_TEMP; > + object_property_add(OBJECT(client), "dimm_temp[*]", "uint8", > + peci_client_get, > + peci_client_set, NULL, &client->dimm_temp[i]); > + } > +} > + > +static void peci_client_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + ResettableClass *rc = RESETTABLE_CLASS(klass); > + > + dc->desc = "PECI Client Device"; > + dc->bus_type = TYPE_PECI_BUS; > + dc->realize = peci_client_realize; > + dc->vmsd = &vmstate_peci_client; > + set_bit(DEVICE_CATEGORY_MISC, dc->categories); > + rc->phases.enter = peci_client_reset; > +} > + > +static const TypeInfo peci_client_info = { > + .name = TYPE_PECI_CLIENT, > + .parent = TYPE_DEVICE, > + .instance_size = sizeof(PECIClientDevice), > + .class_init = peci_client_class_init, > +}; > + > +static void peci_client_register_types(void) > +{ > + type_register_static(&peci_client_info); > +} > + > +type_init(peci_client_register_types) > diff --git a/hw/peci/peci-core.c b/hw/peci/peci-core.c > new file mode 100644 > index 0000000000..8210bfa198 > --- /dev/null > +++ b/hw/peci/peci-core.c > @@ -0,0 +1,182 @@ > +/* > + * PECI - Platform Environment Control Interface > + * Copyright 2021 Google LLC > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +/* > + * This PECI implementation aims to simulate a host with peci as viewed by a > + * BMC. This is developed with OpenBMC firmware running in QEMU. > + * > + */ > + > +#include "qemu/osdep.h" > +#include "hw/peci/peci.h" > +#include "hw/qdev-properties.h" > +#include "migration/vmstate.h" > +#include "qemu/module.h" > +#include "qemu/log.h" > +#include "trace.h" > + > +#define PECI_FCS_OK 0 > +#define PECI_FCS_ERR 1 > + > +static void peci_rd_pkg_cfg(PECIClientDevice *client, PECICmd *pcmd) > +{ > + PECIPkgCfg *resp = (PECIPkgCfg *)pcmd->tx; > + uint8_t index = pcmd->rx[1]; > + uint16_t param = pcmd->rx[3] | pcmd->rx[2]; > + > + switch (index) { > + case PECI_MBX_CPU_ID: /* CPU Family ID*/ > + trace_peci_rd_pkg_cfg("CPU ID"); > + switch (param) { > + case PECI_PKG_ID_CPU_ID: > + resp->data = client->cpu_id; > + break; > + > + case PECI_PKG_ID_MICROCODE_REV: > + resp->data = client->ucode; > + break; > + > + default: > + qemu_log_mask(LOG_UNIMP, "%s: CPU ID param %u\n", __func__, param); > + break; > + } > + break; > + > + case PECI_MBX_DTS_MARGIN: > + trace_peci_rd_pkg_cfg("DTS MARGIN"); > + /* > + * Processors return a value of DTS reading in 10.6 format > + * (10 bits signed decimal, 6 bits fractional). > + */ > + resp->data = (-1 * client->core_temp_max) << 6; > + break; > + > + case PECI_MBX_DDR_DIMM_TEMP: > + trace_peci_rd_pkg_cfg("DDR DIMM TEMP"); > + if (param > PECI_NUM_CHANNELS_MAX) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: DDR_DIMM_TEMP unsupported channel index %d", > + __func__, param); > + param = PECI_NUM_CHANNELS_MAX; > + } > + uint8_t channel_index = param * client->dimms_per_channel; > + memcpy(&resp->data, > + &client->dimm_temp[channel_index], > + sizeof(client->dimm_temp[0]) * client->dimms_per_channel); > + break; > + > + case PECI_MBX_TEMP_TARGET: > + trace_peci_rd_pkg_cfg("TEMP TARGET"); > + PECITempTarget target = { > + .tcontrol = client->tcontrol, > + .tjmax = client->tjmax, > + }; > + memcpy(&resp->data, &target, sizeof(target)); > + break; > + > + case PECI_MBX_PPIN: > + trace_peci_rd_pkg_cfg("PPIN"); > + resp->data = 0xdeadbeef; > + break; > + > + default: > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented PkgCfg Index: %u\n", > + __func__, index); > + resp->cc = PECI_DEV_CC_INVALID_REQ; > + return; > + } > + > + resp->cc = PECI_DEV_CC_SUCCESS; > +} > + > +int peci_handle_cmd(PECIBus *bus, PECICmd *pcmd) > +{ > + PECIClientDevice *client = peci_get_client(bus, pcmd->addr); > + > + if (!client) { /* ignore commands if clients aren't found */ > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: no peci client at address: 0x%02x\n", > + __func__, pcmd->addr); > + return PECI_FCS_ERR; > + } > + > + /* clear output buffer before handling command */ > + for (size_t i = 0; i < pcmd->rd_length; i++) { > + pcmd->tx[i] = 0; > + } > + > + switch (pcmd->cmd) { > + case PECI_CMD_PING: > + trace_peci_handle_cmd("PING!", pcmd->addr); > + break; > + > + case PECI_CMD_GET_DIB: /* Device Info Byte */ > + trace_peci_handle_cmd("GetDIB", pcmd->addr); > + PECIDIB dib = { > + .device_info = client->device_info, > + .revision = client->revision, > + }; > + memcpy(pcmd->tx, &dib, sizeof(PECIDIB)); > + break; > + > + case PECI_CMD_GET_TEMP: /* maximum die temp in socket */ > + trace_peci_handle_cmd("GetTemp", pcmd->addr); > + /* > + * The data is returned as a negative value representing the number of > + * degrees centigrade below the maximum processor junction temperature > + */ > + memcpy(pcmd->tx, &client->core_temp_max, sizeof(client->core_temp_max)); > + break; > + > + case PECI_CMD_RD_PKG_CFG: > + trace_peci_handle_cmd("RdPkgConfig", pcmd->addr); > + peci_rd_pkg_cfg(client, pcmd); > + break; > + > + case PECI_CMD_RD_IA_MSR: > + trace_peci_handle_cmd("RdIAMSR", pcmd->addr); > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented RD_IA_MSR\n", __func__); > + break; > + > + case PECI_CMD_RD_PCI_CFG: > + trace_peci_handle_cmd("RdPCIConfig", pcmd->addr); > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented PCI_CFG\n", __func__); > + break; > + > + case PECI_CMD_RD_PCI_CFG_LOCAL: > + trace_peci_handle_cmd("RdPCIConfigLocal", pcmd->addr); > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented PCI_CFG_LOCAL\n", __func__); > + break; > + > + case PECI_CMD_RD_END_PT_CFG: > + qemu_log_mask(LOG_UNIMP, "%s: unimplemented CMD_RD_END_PT_CFG\n", > + __func__); > + break; > + > + default: > + qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown cmd: %x\n", > + __func__, pcmd->cmd); > + break; > + } > + > + return PECI_FCS_OK; > +} > + > +PECIBus *peci_bus_create(DeviceState *parent) > +{ > + return PECI_BUS(qbus_new(TYPE_PECI_BUS, parent, "peci-bus")); > +}; > + > +static const TypeInfo peci_types[] = { > + { > + .name = TYPE_PECI_BUS, > + .parent = TYPE_BUS, > + .instance_size = sizeof(PECIBus), > + }, > +}; > + > +DEFINE_TYPES(peci_types); > diff --git a/hw/peci/trace-events b/hw/peci/trace-events > new file mode 100644 > index 0000000000..f90c998dd9 > --- /dev/null > +++ b/hw/peci/trace-events > @@ -0,0 +1,5 @@ > +# See docs/devel/tracing.rst for syntax documentation. > + > +# peci-core.c > +peci_handle_cmd(const char* s, uint8_t addr) "%s @ 0x%02" PRIx8 > +peci_rd_pkg_cfg(const char* s) "%s" > diff --git a/hw/peci/trace.h b/hw/peci/trace.h > new file mode 100644 > index 0000000000..4283e3bfc5 > --- /dev/null > +++ b/hw/peci/trace.h > @@ -0,0 +1 @@ > +#include "trace/trace-hw_peci.h" > diff --git a/include/hw/peci/peci.h b/include/hw/peci/peci.h > new file mode 100644 > index 0000000000..1a0abe65cd > --- /dev/null > +++ b/include/hw/peci/peci.h > @@ -0,0 +1,194 @@ > +/* > + * PECI - Platform Environment Control Interface > + * > + * Copyright 2021 Google LLC > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#ifndef PECI_H > +#define PECI_H > + > +#include "hw/qdev-core.h" > +#include "qom/object.h" > + > +#define PECI_CMD_PING 0x0 > +#define PECI_CMD_GET_DIB 0xF7 > +#define PECI_CMD_GET_TEMP 0x01 > +#define PECI_CMD_RD_PKG_CFG 0xA1 > +#define PECI_CMD_WR_PKG_CFG 0xA5 > +#define PECI_MBX_CPU_ID 0 /* Package Identifier Read */ > +#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ > +#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ > +#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ > +#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ > +#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Revision */ > +#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ > +#define PECI_MBX_VR_DEBUG 1 /* VR Debug */ > +#define PECI_MBX_PKG_TEMP_READ 2 /* Package Temperature Read */ > +#define PECI_MBX_ENERGY_COUNTER 3 /* Energy counter */ > +#define PECI_MBX_ENERGY_STATUS 4 /* DDR Energy Status */ > +#define PECI_MBX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */ > +#define PECI_MBX_EPI 6 /* Efficient Performance Indication */ > +#define PECI_MBX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */ > +#define PECI_MBX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */ > +#define PECI_MBX_DTS_MARGIN 10 /* DTS thermal margin */ > +#define PECI_MBX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */ > +#define PECI_MBX_CFG_TDP_CONTROL 12 /* TDP Config Control */ > +#define PECI_MBX_CFG_TDP_LEVELS 13 /* TDP Config Levels */ > +#define PECI_MBX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ > +#define PECI_MBX_CFG_ICCMAX 15 /* Configurable ICCMAX */ > +#define PECI_MBX_TEMP_TARGET 16 /* Temperature Target Read */ > +#define PECI_MBX_CURR_CFG_LIMIT 17 /* Current Config Limit */ > +#define PECI_MBX_PPIN 19 /* Processor Inventory Number */ > +#define PECI_MBX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */ > +#define PECI_MBX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */ > +#define PECI_MBX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */ > +#define PECI_MBX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */ > +#define PECI_MBX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */ > +#define PECI_MBX_TDP 28 /* Thermal design power minimum */ > +#define PECI_MBX_TDP_HIGH 29 /* Thermal design power maximum */ > +#define PECI_MBX_TDP_UNITS 30 /* Units for power/energy registers */ > +#define PECI_MBX_RUN_TIME 31 /* Accumulated Run Time */ > +#define PECI_MBX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */ > +#define PECI_MBX_TURBO_RATIO 33 /* Turbo Activation Ratio */ > +#define PECI_MBX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */ > +#define PECI_MBX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */ > +#define PECI_MBX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */ > +#define PECI_MBX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */ > +#define PECI_MBX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */ > +#define PECI_MBX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */ > +#define PECI_MBX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */ > +#define PECI_MBX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */ > +#define PECI_MBX_DDR_THERM_STATUS 46 /* DDR Thermal Status */ > +#define PECI_MBX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */ > +#define PECI_MBX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */ > +#define PECI_MBX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */ > +#define PECI_MBX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */ > +#define PECI_MBX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */ > +#define PECI_MBX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */ > +#define PECI_MBX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */ > +#define PECI_MBX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */ > +#define PECI_MBX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */ > +#define PECI_MBX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */ > +#define PECI_CMD_RD_IA_MSR 0xB1 > +#define PECI_CMD_WR_IA_MSR 0xB5 > +#define PECI_CMD_RD_IA_MSREX 0xD1 > +#define PECI_CMD_RD_PCI_CFG 0x61 > +#define PECI_CMD_WR_PCI_CFG 0x65 > +#define PECI_CMD_RD_PCI_CFG_LOCAL 0xE1 > +#define PECI_CMD_WR_PCI_CFG_LOCAL 0xE5 > +#define PECI_CMD_RD_END_PT_CFG 0xC1 > +#define PECI_CMD_WR_END_PT_CFG 0xC5 > +#define PECI_CMD_CRASHDUMP_GET_FRAME 0x71 > + > +/* Device Specific Completion Code (CC) Definition */ > +#define PECI_DEV_CC_SUCCESS 0x40 > +#define PECI_DEV_CC_NEED_RETRY 0x80 > +#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81 > +#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82 > +#define PECI_DEV_CC_INVALID_REQ 0x90 > +#define PECI_DEV_CC_MCA_ERROR 0x91 > +#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR 0x93 > +#define PECI_DEV_CC_FATAL_MCA_DETECTED 0x94 > +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB 0x98 > +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR 0x9B > +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA 0x9C > + > + > +typedef struct PECIDIB { /* DIB - Device Info Byte(s) */ > + uint8_t device_info; /* bit 2 - num of domains */ > + uint8_t revision; > +} PECIDIB; > + > +typedef struct __attribute__ ((__packed__)) { > + uint8_t cc; /* completion code */ > + uint32_t data; > +} PECIPkgCfg; > + > +typedef struct PECITempTarget { > + uint8_t reserved; > + int8_t tcontrol; > + uint8_t tjmax; > +} PECITempTarget; > + > +#define PECI_BASE_ADDR 0x30 > +#define PECI_BUFFER_SIZE 0x100 > +#define PECI_NUM_CPUS_MAX 56 > +#define PECI_DIMMS_PER_CHANNEL_MAX 3 > +#define PECI_NUM_CHANNELS_MAX 8 > +#define PECI_NUM_DIMMS_MAX (PECI_NUM_CHANNELS_MAX * PECI_DIMMS_PER_CHANNEL_MAX) > + > +typedef struct PECIClientDevice { > + DeviceState qdev; > + uint8_t address; > + uint8_t device_info; > + uint8_t revision; > + > + /* CPU */ > + uint32_t cpu_id; > + uint8_t cpu_cores; > + uint32_t ucode; > + uint8_t tjmax; > + int8_t tcontrol; > + int8_t core_temp_max; /* absolute temp - tjmax */ > + uint8_t core_temp[PECI_NUM_CPUS_MAX]; > + > + /* DIMMS */ > + uint8_t dimms; > + uint8_t dimms_per_channel; > + uint8_t dimm_temp_max; > + uint8_t dimm_temp[PECI_NUM_DIMMS_MAX]; > + > +} PECIClientDevice; > + > +#define TYPE_PECI_CLIENT "peci-client" > +OBJECT_DECLARE_SIMPLE_TYPE(PECIClientDevice, PECI_CLIENT) > + > +typedef struct PECIBus { > + BusState qbus; > + uint8_t num_clients; > +} PECIBus; > + > +#define TYPE_PECI_BUS "peci-bus" > +OBJECT_DECLARE_SIMPLE_TYPE(PECIBus, PECI_BUS) > + > +/* Creates a basic qemu bus onto which PECI clients will be attached */ > +PECIBus *peci_bus_create(DeviceState *parent); > + > +enum { > + FAM6_HASWELL_X = 0x306F0, > + FAM6_BROADWELL_X = 0x406F0, > + FAM6_SKYLAKE_X = 0x50650, > + FAM6_SKYLAKE_XD = 0x50660, > + FAM6_ICELAKE_X = 0x606A0, > + FAM6_SAPPHIRE_RAPIDS_X = 0x806F3, > +}; > + > +typedef struct PECIClientProperties { > + uint32_t cpu_family; > + uint8_t cpu_cores; > + uint8_t dimms; > + uint8_t dimms_per_channel; > +} PECIClientProperties; > + > +/* Creates a PECI client with the specified cpu and dimm count */ > +PECIClientDevice *peci_add_client(PECIBus *bus, > + uint8_t address, > + PECIClientProperties *props); > +PECIClientDevice *peci_get_client(PECIBus *bus, uint8_t addr); > + > +typedef struct PECICmd { > + uint8_t addr; > + uint8_t cmd; > + > + size_t wr_length; > + uint8_t rx[PECI_BUFFER_SIZE]; > + > + size_t rd_length; > + uint8_t tx[PECI_BUFFER_SIZE]; > +} PECICmd; > + > +int peci_handle_cmd(PECIBus *bus, PECICmd *pcmd); > + > +#endif > diff --git a/meson.build b/meson.build > index 20fddbd707..55594702c3 100644 > --- a/meson.build > +++ b/meson.build > @@ -2965,6 +2965,7 @@ if have_system > 'hw/nvram', > 'hw/pci', > 'hw/pci-host', > + 'hw/peci', > 'hw/ppc', > 'hw/rdma', > 'hw/rdma/vmw', > -- > 2.37.3.968.ga6b4b080e4-goog >
diff --git a/MAINTAINERS b/MAINTAINERS index 5ce4227ff6..14ab29679d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3212,6 +3212,13 @@ F: tests/qtest/adm1272-test.c F: tests/qtest/max34451-test.c F: tests/qtest/isl_pmbus_vr-test.c +PECI +M: Titus Rwantare <titusr@google.com> +S: Maintained +F: hw/peci/peci-core.c +F: hw/peci/peci-client.c +F: include/hw/peci/peci.h + Firmware schema specifications M: Philippe Mathieu-Daudé <f4bug@amsat.org> R: Daniel P. Berrange <berrange@redhat.com> diff --git a/hw/Kconfig b/hw/Kconfig index 38233bbb0f..300ab48127 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -24,6 +24,7 @@ source net/Kconfig source nubus/Kconfig source nvme/Kconfig source nvram/Kconfig +source peci/Kconfig source pci-bridge/Kconfig source pci-host/Kconfig source pcmcia/Kconfig diff --git a/hw/meson.build b/hw/meson.build index c7ac7d3d75..340cc88a52 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -28,6 +28,7 @@ subdir('pci') subdir('pci-bridge') subdir('pci-host') subdir('pcmcia') +subdir('peci') subdir('rdma') subdir('rtc') subdir('scsi') diff --git a/hw/peci/Kconfig b/hw/peci/Kconfig new file mode 100644 index 0000000000..fe4f665d21 --- /dev/null +++ b/hw/peci/Kconfig @@ -0,0 +1,2 @@ +config PECI + bool diff --git a/hw/peci/meson.build b/hw/peci/meson.build new file mode 100644 index 0000000000..01cfa95abe --- /dev/null +++ b/hw/peci/meson.build @@ -0,0 +1 @@ +softmmu_ss.add(when: 'CONFIG_PECI', if_true: files('peci-core.c', 'peci-client.c')) diff --git a/hw/peci/peci-client.c b/hw/peci/peci-client.c new file mode 100644 index 0000000000..2aa797b5f6 --- /dev/null +++ b/hw/peci/peci-client.c @@ -0,0 +1,230 @@ +/* + * PECI Client device + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/peci/peci.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "qom/object.h" + +/* + * A PECI client represents an Intel socket and the peripherals attached to it + * that are accessible over the PECI bus. + */ + +#define PECI_CLIENT_DEFAULT_TEMP 30 + +static void peci_client_update_temps(PECIClientDevice *client) +{ + uint8_t temp_cpu = 0; + for (size_t i = 0; i < client->cpu_cores; i++) { + if (temp_cpu < client->core_temp[i]) { + temp_cpu = client->core_temp[i]; + } + } + client->core_temp_max = -1 * (client->tjmax - temp_cpu); + + uint8_t temp_dimm = 0; + for (size_t i = 0; i < client->dimms; i++) { + if (temp_dimm < client->dimm_temp[i]) { + temp_dimm = client->dimm_temp[i]; + } + } + client->dimm_temp_max = temp_dimm; +} + +PECIClientDevice *peci_get_client(PECIBus *bus, uint8_t addr) +{ + PECIClientDevice *client; + BusChild *var; + + QTAILQ_FOREACH(var, &bus->qbus.children, sibling) { + DeviceState *dev = var->child; + client = PECI_CLIENT(dev); + + if (client->address == addr) { + return client; + } + } + return 0; +} + + +PECIClientDevice *peci_add_client(PECIBus *bus, + uint8_t address, + PECIClientProperties *props) +{ + DeviceState *dev = qdev_new("peci-client"); + PECIClientDevice *client; + + /* Only 8 addresses supported as of rev 4.1, 0x30 to 0x37 */ + if (address < PECI_BASE_ADDR || address > PECI_BASE_ADDR + 7) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to add client at 0x%02x", + __func__, address); + return 0; + } + + client = PECI_CLIENT(dev); + client->address = address; + + /* these fields are optional, get set to max if unset or invalid */ + if (!props->cpu_cores || props->cpu_cores > PECI_NUM_CPUS_MAX) { + props->cpu_cores = PECI_NUM_CPUS_MAX; + } + if (!props->dimms || props->dimms > PECI_NUM_DIMMS_MAX) { + props->dimms = PECI_NUM_DIMMS_MAX; + } + if (!props->cpu_family) { + props->cpu_family = FAM6_ICELAKE_X; + } + if (!props->dimms_per_channel || + props->dimms_per_channel > PECI_DIMMS_PER_CHANNEL_MAX) { + props->dimms_per_channel = 2; + } + + client->cpu_id = props->cpu_family; + client->cpu_cores = props->cpu_cores; + client->dimms = props->dimms; + client->dimms_per_channel = props->dimms_per_channel; + + /* TODO: find real revisions and TJ max for supported families */ + client->tjmax = 90; + client->tcontrol = -5; + + switch (props->cpu_family) { + case FAM6_HASWELL_X: + client->revision = 0x31; + break; + + case FAM6_BROADWELL_X: + client->revision = 0x32; + break; + + case FAM6_SKYLAKE_X: + case FAM6_SKYLAKE_XD: + client->revision = 0x36; + break; + + case FAM6_ICELAKE_X: + case FAM6_SAPPHIRE_RAPIDS_X: + client->revision = 0x40; + client->ucode = 0x8c0004a0; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unsupported cpu: 0x%x\n", + __func__, props->cpu_family); + client->revision = 0x31; + break; + } + + qdev_realize_and_unref(&client->qdev, &bus->qbus, &error_abort); + bus->num_clients += 1; + return client; +} + +static void peci_client_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + /* use millidegrees convention */ + uint32_t value = *(uint8_t *)opaque * 1000; + visit_type_uint32(v, name, &value, errp); +} + +static void peci_client_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PECIClientDevice *client = PECI_CLIENT(obj); + uint8_t *internal = opaque; + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + g_assert(value <= 255000); + + *internal = value / 1000; + peci_client_update_temps(client); +} + +static void peci_client_reset(Object *obj, ResetType type) +{ + PECIClientDevice *client = PECI_CLIENT(obj); + client->core_temp_max = 0; + client->dimm_temp_max = 0; +} + +static const VMStateDescription vmstate_peci_client = { + .name = TYPE_PECI_CLIENT, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(address, PECIClientDevice), + VMSTATE_UINT8(device_info, PECIClientDevice), + VMSTATE_UINT8(revision, PECIClientDevice), + VMSTATE_UINT32(cpu_id, PECIClientDevice), + VMSTATE_UINT8(cpu_cores, PECIClientDevice), + VMSTATE_UINT8(tjmax, PECIClientDevice), + VMSTATE_INT8(tcontrol, PECIClientDevice), + VMSTATE_UINT8_ARRAY(core_temp, PECIClientDevice, PECI_NUM_CPUS_MAX), + VMSTATE_UINT8(dimms, PECIClientDevice), + VMSTATE_UINT8(dimms_per_channel, PECIClientDevice), + VMSTATE_UINT8_ARRAY(dimm_temp, PECIClientDevice , PECI_NUM_DIMMS_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void peci_client_realize(DeviceState *dev, Error **errp) +{ + PECIClientDevice *client = PECI_CLIENT(dev); + for (size_t i = 0; i < client->cpu_cores; i++) { + client->core_temp[i] = PECI_CLIENT_DEFAULT_TEMP; + object_property_add(OBJECT(client), "cpu_temp[*]", "uint8", + peci_client_get, + peci_client_set, NULL, &client->core_temp[i]); + } + + for (size_t i = 0; i < client->dimms; i++) { + client->dimm_temp[i] = PECI_CLIENT_DEFAULT_TEMP; + object_property_add(OBJECT(client), "dimm_temp[*]", "uint8", + peci_client_get, + peci_client_set, NULL, &client->dimm_temp[i]); + } +} + +static void peci_client_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "PECI Client Device"; + dc->bus_type = TYPE_PECI_BUS; + dc->realize = peci_client_realize; + dc->vmsd = &vmstate_peci_client; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + rc->phases.enter = peci_client_reset; +} + +static const TypeInfo peci_client_info = { + .name = TYPE_PECI_CLIENT, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PECIClientDevice), + .class_init = peci_client_class_init, +}; + +static void peci_client_register_types(void) +{ + type_register_static(&peci_client_info); +} + +type_init(peci_client_register_types) diff --git a/hw/peci/peci-core.c b/hw/peci/peci-core.c new file mode 100644 index 0000000000..8210bfa198 --- /dev/null +++ b/hw/peci/peci-core.c @@ -0,0 +1,182 @@ +/* + * PECI - Platform Environment Control Interface + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * This PECI implementation aims to simulate a host with peci as viewed by a + * BMC. This is developed with OpenBMC firmware running in QEMU. + * + */ + +#include "qemu/osdep.h" +#include "hw/peci/peci.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "trace.h" + +#define PECI_FCS_OK 0 +#define PECI_FCS_ERR 1 + +static void peci_rd_pkg_cfg(PECIClientDevice *client, PECICmd *pcmd) +{ + PECIPkgCfg *resp = (PECIPkgCfg *)pcmd->tx; + uint8_t index = pcmd->rx[1]; + uint16_t param = pcmd->rx[3] | pcmd->rx[2]; + + switch (index) { + case PECI_MBX_CPU_ID: /* CPU Family ID*/ + trace_peci_rd_pkg_cfg("CPU ID"); + switch (param) { + case PECI_PKG_ID_CPU_ID: + resp->data = client->cpu_id; + break; + + case PECI_PKG_ID_MICROCODE_REV: + resp->data = client->ucode; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: CPU ID param %u\n", __func__, param); + break; + } + break; + + case PECI_MBX_DTS_MARGIN: + trace_peci_rd_pkg_cfg("DTS MARGIN"); + /* + * Processors return a value of DTS reading in 10.6 format + * (10 bits signed decimal, 6 bits fractional). + */ + resp->data = (-1 * client->core_temp_max) << 6; + break; + + case PECI_MBX_DDR_DIMM_TEMP: + trace_peci_rd_pkg_cfg("DDR DIMM TEMP"); + if (param > PECI_NUM_CHANNELS_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DDR_DIMM_TEMP unsupported channel index %d", + __func__, param); + param = PECI_NUM_CHANNELS_MAX; + } + uint8_t channel_index = param * client->dimms_per_channel; + memcpy(&resp->data, + &client->dimm_temp[channel_index], + sizeof(client->dimm_temp[0]) * client->dimms_per_channel); + break; + + case PECI_MBX_TEMP_TARGET: + trace_peci_rd_pkg_cfg("TEMP TARGET"); + PECITempTarget target = { + .tcontrol = client->tcontrol, + .tjmax = client->tjmax, + }; + memcpy(&resp->data, &target, sizeof(target)); + break; + + case PECI_MBX_PPIN: + trace_peci_rd_pkg_cfg("PPIN"); + resp->data = 0xdeadbeef; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented PkgCfg Index: %u\n", + __func__, index); + resp->cc = PECI_DEV_CC_INVALID_REQ; + return; + } + + resp->cc = PECI_DEV_CC_SUCCESS; +} + +int peci_handle_cmd(PECIBus *bus, PECICmd *pcmd) +{ + PECIClientDevice *client = peci_get_client(bus, pcmd->addr); + + if (!client) { /* ignore commands if clients aren't found */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: no peci client at address: 0x%02x\n", + __func__, pcmd->addr); + return PECI_FCS_ERR; + } + + /* clear output buffer before handling command */ + for (size_t i = 0; i < pcmd->rd_length; i++) { + pcmd->tx[i] = 0; + } + + switch (pcmd->cmd) { + case PECI_CMD_PING: + trace_peci_handle_cmd("PING!", pcmd->addr); + break; + + case PECI_CMD_GET_DIB: /* Device Info Byte */ + trace_peci_handle_cmd("GetDIB", pcmd->addr); + PECIDIB dib = { + .device_info = client->device_info, + .revision = client->revision, + }; + memcpy(pcmd->tx, &dib, sizeof(PECIDIB)); + break; + + case PECI_CMD_GET_TEMP: /* maximum die temp in socket */ + trace_peci_handle_cmd("GetTemp", pcmd->addr); + /* + * The data is returned as a negative value representing the number of + * degrees centigrade below the maximum processor junction temperature + */ + memcpy(pcmd->tx, &client->core_temp_max, sizeof(client->core_temp_max)); + break; + + case PECI_CMD_RD_PKG_CFG: + trace_peci_handle_cmd("RdPkgConfig", pcmd->addr); + peci_rd_pkg_cfg(client, pcmd); + break; + + case PECI_CMD_RD_IA_MSR: + trace_peci_handle_cmd("RdIAMSR", pcmd->addr); + qemu_log_mask(LOG_UNIMP, "%s: unimplemented RD_IA_MSR\n", __func__); + break; + + case PECI_CMD_RD_PCI_CFG: + trace_peci_handle_cmd("RdPCIConfig", pcmd->addr); + qemu_log_mask(LOG_UNIMP, "%s: unimplemented PCI_CFG\n", __func__); + break; + + case PECI_CMD_RD_PCI_CFG_LOCAL: + trace_peci_handle_cmd("RdPCIConfigLocal", pcmd->addr); + qemu_log_mask(LOG_UNIMP, "%s: unimplemented PCI_CFG_LOCAL\n", __func__); + break; + + case PECI_CMD_RD_END_PT_CFG: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented CMD_RD_END_PT_CFG\n", + __func__); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown cmd: %x\n", + __func__, pcmd->cmd); + break; + } + + return PECI_FCS_OK; +} + +PECIBus *peci_bus_create(DeviceState *parent) +{ + return PECI_BUS(qbus_new(TYPE_PECI_BUS, parent, "peci-bus")); +}; + +static const TypeInfo peci_types[] = { + { + .name = TYPE_PECI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(PECIBus), + }, +}; + +DEFINE_TYPES(peci_types); diff --git a/hw/peci/trace-events b/hw/peci/trace-events new file mode 100644 index 0000000000..f90c998dd9 --- /dev/null +++ b/hw/peci/trace-events @@ -0,0 +1,5 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# peci-core.c +peci_handle_cmd(const char* s, uint8_t addr) "%s @ 0x%02" PRIx8 +peci_rd_pkg_cfg(const char* s) "%s" diff --git a/hw/peci/trace.h b/hw/peci/trace.h new file mode 100644 index 0000000000..4283e3bfc5 --- /dev/null +++ b/hw/peci/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_peci.h" diff --git a/include/hw/peci/peci.h b/include/hw/peci/peci.h new file mode 100644 index 0000000000..1a0abe65cd --- /dev/null +++ b/include/hw/peci/peci.h @@ -0,0 +1,194 @@ +/* + * PECI - Platform Environment Control Interface + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PECI_H +#define PECI_H + +#include "hw/qdev-core.h" +#include "qom/object.h" + +#define PECI_CMD_PING 0x0 +#define PECI_CMD_GET_DIB 0xF7 +#define PECI_CMD_GET_TEMP 0x01 +#define PECI_CMD_RD_PKG_CFG 0xA1 +#define PECI_CMD_WR_PKG_CFG 0xA5 +#define PECI_MBX_CPU_ID 0 /* Package Identifier Read */ +#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ +#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ +#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ +#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ +#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Revision */ +#define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ +#define PECI_MBX_VR_DEBUG 1 /* VR Debug */ +#define PECI_MBX_PKG_TEMP_READ 2 /* Package Temperature Read */ +#define PECI_MBX_ENERGY_COUNTER 3 /* Energy counter */ +#define PECI_MBX_ENERGY_STATUS 4 /* DDR Energy Status */ +#define PECI_MBX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */ +#define PECI_MBX_EPI 6 /* Efficient Performance Indication */ +#define PECI_MBX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */ +#define PECI_MBX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */ +#define PECI_MBX_DTS_MARGIN 10 /* DTS thermal margin */ +#define PECI_MBX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */ +#define PECI_MBX_CFG_TDP_CONTROL 12 /* TDP Config Control */ +#define PECI_MBX_CFG_TDP_LEVELS 13 /* TDP Config Levels */ +#define PECI_MBX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ +#define PECI_MBX_CFG_ICCMAX 15 /* Configurable ICCMAX */ +#define PECI_MBX_TEMP_TARGET 16 /* Temperature Target Read */ +#define PECI_MBX_CURR_CFG_LIMIT 17 /* Current Config Limit */ +#define PECI_MBX_PPIN 19 /* Processor Inventory Number */ +#define PECI_MBX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */ +#define PECI_MBX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */ +#define PECI_MBX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */ +#define PECI_MBX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */ +#define PECI_MBX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */ +#define PECI_MBX_TDP 28 /* Thermal design power minimum */ +#define PECI_MBX_TDP_HIGH 29 /* Thermal design power maximum */ +#define PECI_MBX_TDP_UNITS 30 /* Units for power/energy registers */ +#define PECI_MBX_RUN_TIME 31 /* Accumulated Run Time */ +#define PECI_MBX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */ +#define PECI_MBX_TURBO_RATIO 33 /* Turbo Activation Ratio */ +#define PECI_MBX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */ +#define PECI_MBX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */ +#define PECI_MBX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */ +#define PECI_MBX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */ +#define PECI_MBX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */ +#define PECI_MBX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */ +#define PECI_MBX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */ +#define PECI_MBX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */ +#define PECI_MBX_DDR_THERM_STATUS 46 /* DDR Thermal Status */ +#define PECI_MBX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */ +#define PECI_MBX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */ +#define PECI_MBX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */ +#define PECI_MBX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */ +#define PECI_MBX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */ +#define PECI_MBX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */ +#define PECI_MBX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */ +#define PECI_MBX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */ +#define PECI_MBX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */ +#define PECI_MBX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */ +#define PECI_CMD_RD_IA_MSR 0xB1 +#define PECI_CMD_WR_IA_MSR 0xB5 +#define PECI_CMD_RD_IA_MSREX 0xD1 +#define PECI_CMD_RD_PCI_CFG 0x61 +#define PECI_CMD_WR_PCI_CFG 0x65 +#define PECI_CMD_RD_PCI_CFG_LOCAL 0xE1 +#define PECI_CMD_WR_PCI_CFG_LOCAL 0xE5 +#define PECI_CMD_RD_END_PT_CFG 0xC1 +#define PECI_CMD_WR_END_PT_CFG 0xC5 +#define PECI_CMD_CRASHDUMP_GET_FRAME 0x71 + +/* Device Specific Completion Code (CC) Definition */ +#define PECI_DEV_CC_SUCCESS 0x40 +#define PECI_DEV_CC_NEED_RETRY 0x80 +#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81 +#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82 +#define PECI_DEV_CC_INVALID_REQ 0x90 +#define PECI_DEV_CC_MCA_ERROR 0x91 +#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR 0x93 +#define PECI_DEV_CC_FATAL_MCA_DETECTED 0x94 +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB 0x98 +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR 0x9B +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA 0x9C + + +typedef struct PECIDIB { /* DIB - Device Info Byte(s) */ + uint8_t device_info; /* bit 2 - num of domains */ + uint8_t revision; +} PECIDIB; + +typedef struct __attribute__ ((__packed__)) { + uint8_t cc; /* completion code */ + uint32_t data; +} PECIPkgCfg; + +typedef struct PECITempTarget { + uint8_t reserved; + int8_t tcontrol; + uint8_t tjmax; +} PECITempTarget; + +#define PECI_BASE_ADDR 0x30 +#define PECI_BUFFER_SIZE 0x100 +#define PECI_NUM_CPUS_MAX 56 +#define PECI_DIMMS_PER_CHANNEL_MAX 3 +#define PECI_NUM_CHANNELS_MAX 8 +#define PECI_NUM_DIMMS_MAX (PECI_NUM_CHANNELS_MAX * PECI_DIMMS_PER_CHANNEL_MAX) + +typedef struct PECIClientDevice { + DeviceState qdev; + uint8_t address; + uint8_t device_info; + uint8_t revision; + + /* CPU */ + uint32_t cpu_id; + uint8_t cpu_cores; + uint32_t ucode; + uint8_t tjmax; + int8_t tcontrol; + int8_t core_temp_max; /* absolute temp - tjmax */ + uint8_t core_temp[PECI_NUM_CPUS_MAX]; + + /* DIMMS */ + uint8_t dimms; + uint8_t dimms_per_channel; + uint8_t dimm_temp_max; + uint8_t dimm_temp[PECI_NUM_DIMMS_MAX]; + +} PECIClientDevice; + +#define TYPE_PECI_CLIENT "peci-client" +OBJECT_DECLARE_SIMPLE_TYPE(PECIClientDevice, PECI_CLIENT) + +typedef struct PECIBus { + BusState qbus; + uint8_t num_clients; +} PECIBus; + +#define TYPE_PECI_BUS "peci-bus" +OBJECT_DECLARE_SIMPLE_TYPE(PECIBus, PECI_BUS) + +/* Creates a basic qemu bus onto which PECI clients will be attached */ +PECIBus *peci_bus_create(DeviceState *parent); + +enum { + FAM6_HASWELL_X = 0x306F0, + FAM6_BROADWELL_X = 0x406F0, + FAM6_SKYLAKE_X = 0x50650, + FAM6_SKYLAKE_XD = 0x50660, + FAM6_ICELAKE_X = 0x606A0, + FAM6_SAPPHIRE_RAPIDS_X = 0x806F3, +}; + +typedef struct PECIClientProperties { + uint32_t cpu_family; + uint8_t cpu_cores; + uint8_t dimms; + uint8_t dimms_per_channel; +} PECIClientProperties; + +/* Creates a PECI client with the specified cpu and dimm count */ +PECIClientDevice *peci_add_client(PECIBus *bus, + uint8_t address, + PECIClientProperties *props); +PECIClientDevice *peci_get_client(PECIBus *bus, uint8_t addr); + +typedef struct PECICmd { + uint8_t addr; + uint8_t cmd; + + size_t wr_length; + uint8_t rx[PECI_BUFFER_SIZE]; + + size_t rd_length; + uint8_t tx[PECI_BUFFER_SIZE]; +} PECICmd; + +int peci_handle_cmd(PECIBus *bus, PECICmd *pcmd); + +#endif diff --git a/meson.build b/meson.build index 20fddbd707..55594702c3 100644 --- a/meson.build +++ b/meson.build @@ -2965,6 +2965,7 @@ if have_system 'hw/nvram', 'hw/pci', 'hw/pci-host', + 'hw/peci', 'hw/ppc', 'hw/rdma', 'hw/rdma/vmw',