From: Chris Browy <cbrowy@avery-design.com>
To: Chris Browy <cbrowy@avery-design.com>
Cc: ben.widawsky@intel.com, david@redhat.com, qemu-devel@nongnu.org,
vishal.l.verma@intel.com, jgroves@micron.com, armbru@redhat.com,
linux-cxl@vger.kernel.org, f4bug@amsat.org, mst@redhat.com,
Jonathan.Cameron@Huawei.com, imammedo@redhat.com,
dan.j.williams@intel.com, ira.weiny@intel.com
Subject: Re: [RFC PATCH v1 01/01] PCIe DOE for PCIe and CXL 2.0
Date: Tue, 2 Feb 2021 15:43:28 -0500 (EST) [thread overview]
Message-ID: <alpine.LRH.2.23.451.2102021543190.30097@server4> (raw)
In-Reply-To: <alpine.LRH.2.23.451.2102021236150.13496@server4>
[-- Attachment #1: Type: text/plain, Size: 47043 bytes --]
diff --git a/MAINTAINERS b/MAINTAINERS
index 981dc92e25..4fb865e0b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1655,6 +1655,13 @@ F: docs/pci*
F: docs/specs/*pci*
F: default-configs/pci.mak
+PCIE DOE
+M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
+M: Chris Browy <cbrowy@avery-design.com>
+S: Supported
+F: include/hw/pci/pcie_doe.h
+F: hw/pci/pcie_doe.c
+
ACPI/SMBIOS
M: Michael S. Tsirkin <mst@redhat.com>
M: Igor Mammedov <imammedo@redhat.com>
diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c
index e1bcee5bdb..c49d2aa896 100644
--- a/hw/cxl/cxl-component-utils.c
+++ b/hw/cxl/cxl-component-utils.c
@@ -195,3 +195,154 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, uint16_t length,
range_init_nofail(&cxl->dvsecs[type], cxl->dvsec_offset, length);
cxl->dvsec_offset += length;
}
+
+uint32_t cxl_doe_compliance_init(CXLComponentState *cxl_cstate)
+{
+ PCIDevice *pci_dev = cxl_cstate->pdev;
+ uint32_t req;
+ uint32_t byte_cnt = 0;
+
+ DOE_DBG(">> %s\n", __func__);
+
+ req = ((struct cxl_compliance_mode_cap *)pcie_doe_get_req(pci_dev))
+ ->req_code;
+ switch (req) {
+ case CXL_COMP_MODE_CAP:
+ byte_cnt = sizeof(struct cxl_compliance_mode_cap_rsp);
+ cxl_cstate->doe_resp.cap_rsp.header.vendor_id = CXL_VENDOR_ID;
+ cxl_cstate->doe_resp.cap_rsp.header.doe_type = CXL_DOE_COMPLIANCE;
+ cxl_cstate->doe_resp.cap_rsp.header.reserved = 0x0;
+ cxl_cstate->doe_resp.cap_rsp.header.length =
+ dwsizeof(struct cxl_compliance_mode_cap_rsp);
+ cxl_cstate->doe_resp.cap_rsp.rsp_code = 0x0;
+ cxl_cstate->doe_resp.cap_rsp.version = 0x1;
+ cxl_cstate->doe_resp.cap_rsp.length = 0x1c;
+ cxl_cstate->doe_resp.cap_rsp.status = 0x0;
+ cxl_cstate->doe_resp.cap_rsp.available_cap_bitmask = 0x3;
+ cxl_cstate->doe_resp.cap_rsp.enabled_cap_bitmask = 0x3;
+ break;
+ case CXL_COMP_MODE_STATUS:
+ byte_cnt = sizeof(struct cxl_compliance_mode_status_rsp);
+ cxl_cstate->doe_resp.status_rsp.header.vendor_id = CXL_VENDOR_ID;
+ cxl_cstate->doe_resp.status_rsp.header.doe_type = CXL_DOE_COMPLIANCE;
+ cxl_cstate->doe_resp.status_rsp.header.reserved = 0x0;
+ cxl_cstate->doe_resp.status_rsp.header.length =
+ dwsizeof(struct cxl_compliance_mode_status_rsp);
+ cxl_cstate->doe_resp.status_rsp.rsp_code = 0x1;
+ cxl_cstate->doe_resp.status_rsp.version = 0x1;
+ cxl_cstate->doe_resp.status_rsp.length = 0x14;
+ cxl_cstate->doe_resp.status_rsp.cap_bitfield = 0x3;
+ cxl_cstate->doe_resp.status_rsp.cache_size = 0;
+ cxl_cstate->doe_resp.status_rsp.cache_size_units = 0;
+ break;
+ default:
+ break;
+ }
+
+ DOE_DBG(" REQ=%x, RSP BYTE_CNT=%d\n", req, byte_cnt);
+ DOE_DBG("<< %s\n", __func__);
+ return byte_cnt;
+}
+
+void cxl_doe_cdat_init(CXLComponentState *cxl_cstate)
+{
+
+ DOE_DBG(">> %s\n", __func__);
+
+ cxl_cstate->doe_resp.cdat_rsp.header.vendor_id = CXL_VENDOR_ID;
+ cxl_cstate->doe_resp.cdat_rsp.header.doe_type = CXL_DOE_TABLE_ACCESS;
+ cxl_cstate->doe_resp.cdat_rsp.header.reserved = 0x0;
+ cxl_cstate->doe_resp.cdat_rsp.header.length = 0;
+ cxl_cstate->doe_resp.cdat_rsp.rsp_code = 0x0;
+ cxl_cstate->doe_resp.cdat_rsp.table_type = 0x0;
+ cxl_cstate->doe_resp.cdat_rsp.next_entry_handle = 0xffff;
+
+ /* copy the DSMAS entry */
+ cxl_cstate->dsmas.type = CDAT_TYPE_DSMAS;
+ cxl_cstate->dsmas.reserved = 0x0;
+ cxl_cstate->dsmas.length = 0x0;
+ cxl_cstate->dsmas.DSMADhandle = 0x0;
+ cxl_cstate->dsmas.flags = 0x0;
+ cxl_cstate->dsmas.reserved2 = 0x0;
+ cxl_cstate->dsmas.DPA_base = 0x0;
+ cxl_cstate->dsmas.DPA_length = 0x40000;
+
+ /* copy the DSLBIS entry */
+ cxl_cstate->dslbis.type = CDAT_TYPE_DSLBIS;
+ cxl_cstate->dslbis.reserved = 0;
+ cxl_cstate->dslbis.length = 16;
+ cxl_cstate->dslbis.handle = 0;
+ cxl_cstate->dslbis.flags = 0;
+ cxl_cstate->dslbis.data_type = 0;
+ cxl_cstate->dslbis.reserved2 = 0;
+ cxl_cstate->dslbis.entry_base_unit = 0;
+ cxl_cstate->dslbis.entry[0] = 0;
+ cxl_cstate->dslbis.entry[1] = 0;
+ cxl_cstate->dslbis.entry[2] = 0;
+ cxl_cstate->dslbis.reserved3 = 0;
+
+ /* copy the DSMSCIS entry */
+ cxl_cstate->dsmscis.type = CDAT_TYPE_DSMSCIS;
+ cxl_cstate->dsmscis.reserved = 0;
+ cxl_cstate->dsmscis.length = 20;
+ cxl_cstate->dsmscis.DSMASH_handle = 0;
+ cxl_cstate->dsmscis.reserved2[0] = 0;
+ cxl_cstate->dsmscis.reserved2[1] = 0;
+ cxl_cstate->dsmscis.reserved2[2] = 0;
+ cxl_cstate->dsmscis.memory_side_cache_size = 0;
+ cxl_cstate->dsmscis.cache_attributes = 0;
+
+ /* copy the DSIS entry */
+ cxl_cstate->dsis.type = CDAT_TYPE_DSIS;
+ cxl_cstate->dsis.reserved = 0;
+ cxl_cstate->dsis.length = 8;
+ cxl_cstate->dsis.flags = 0;
+ cxl_cstate->dsis.handle = 0;
+ cxl_cstate->dsis.reserved2 = 0;
+ cxl_cstate->dsis.DPA_offset = 0;
+ cxl_cstate->dsis.DPA_length = 0;
+
+ /* copy the DSEMTS entry */
+ cxl_cstate->dsemts.type = CDAT_TYPE_DSEMTS;
+ cxl_cstate->dsemts.reserved = 0;
+ cxl_cstate->dsemts.length = 24;
+ cxl_cstate->dsemts.DSMAS_handle = 0;
+ cxl_cstate->dsemts.EFI_memory_type_attr = 0;
+ cxl_cstate->dsemts.reserved2 = 0;
+
+ /* copy the SSLBE entry */
+ cxl_cstate->sslbe.port_x_id = 0;
+ cxl_cstate->sslbe.port_y_id = 0;
+ cxl_cstate->sslbe.latency_bandwidth = 0;
+ cxl_cstate->sslbe.reserved = 0;
+
+ /* copy the SSLBIS entry */
+ cxl_cstate->sslbis.type = CDAT_TYPE_SSLBIS;
+ cxl_cstate->sslbis.reserved = 0;
+ cxl_cstate->sslbis.length = 0;
+ cxl_cstate->sslbis.data_type = 0;
+ cxl_cstate->sslbis.reserved2[0] = 0;
+ cxl_cstate->sslbis.reserved2[1] = 0;
+ cxl_cstate->sslbis.reserved2[2] = 0;
+ cxl_cstate->sslbis.cdat_sslbe_array[0] = cxl_cstate->sslbe;
+
+ DOE_DBG("<< %s\n", __func__);
+}
+
+/*
+ * Helper to creates a DOE header for a CXL entity. The caller is responsible
+ * for tracking the valid offset.
+ *
+ * This function will build the DOE header on behalf of the caller and then
+ * copy in the remaining bits.
+ */
+void cxl_component_create_doe(CXLComponentState *cxl, uint16_t offset)
+{
+ PCIDevice *pdev = cxl->pdev;
+
+ pcie_cap_doe_init(pdev, offset);
+ pcie_doe_register_protocol(pdev, CXL_VENDOR_ID, CXL_DOE_COMPLIANCE,
+ cxl_doe_compliance_rsp);
+ pcie_doe_register_protocol(pdev, CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS,
+ cxl_doe_cdat_rsp);
+}
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index d091e645aa..2875536826 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -14,6 +14,123 @@
#include "sysemu/hostmem.h"
#include "hw/cxl/cxl.h"
+bool cxl_doe_compliance_rsp(PCIDevice *pci_dev)
+{
+ CXLComponentState *cxl_cstate = &CT3(pci_dev)->cxl_cstate;
+ uint32_t byte_cnt;
+ uint32_t dw_cnt;
+
+ DOE_DBG(">> %s\n", __func__);
+
+ byte_cnt = cxl_doe_compliance_init(cxl_cstate);
+ dw_cnt = byte_cnt / 4;
+ memcpy(pci_dev->exp.doe_state.read_mbox,
+ cxl_cstate->doe_resp.data_byte, byte_cnt);
+
+ pci_dev->exp.doe_state.read_mbox_len += dw_cnt;
+ DOE_DBG(" read_mbox_len=%x\n",
+ pci_dev->exp.doe_state.read_mbox_len);
+
+ DOE_DBG(" RD MBOX[%d]=%x\n", dw_cnt - 1,
+ *(pci_dev->exp.doe_state.read_mbox + dw_cnt - 1));
+
+ DOE_DBG("<< %s\n", __func__);
+
+ return DOE_SUCCESS;
+}
+
+bool cxl_doe_cdat_rsp(PCIDevice *pci_dev)
+{
+ CXLComponentState *cxl_cstate = &CT3(pci_dev)->cxl_cstate;
+ uint32_t cnt;
+ uint32_t dw_cnt;
+
+ DOE_DBG(">> %s\n", __func__);
+
+ cnt = sizeof(struct cxl_cdat_rsp);
+ cxl_doe_cdat_init(cxl_cstate);
+ memcpy(pci_dev->exp.doe_state.read_mbox,
+ cxl_cstate->doe_resp.data_byte, cnt);
+
+ /* copy the DSMAS entry */
+ memcpy(pci_dev->exp.doe_state.read_mbox + cnt / 4,
+ &cxl_cstate->dsmas, sizeof(struct cdat_dsmas));
+ cnt += sizeof(struct cdat_dsmas);
+
+ /* copy the DSLBIS entry */
+ memcpy(pci_dev->exp.doe_state.read_mbox + cnt / 4,
+ &cxl_cstate->dslbis, sizeof(struct cdat_dslbis));
+ cnt += sizeof(struct cdat_dslbis);
+
+ /* copy the DSMSCIS entry */
+ memcpy(pci_dev->exp.doe_state.read_mbox + cnt / 4,
+ &cxl_cstate->dsmscis, sizeof(struct cdat_dsmscis));
+ cnt += sizeof(struct cdat_dsmscis);
+
+ /* copy the DSIS entry */
+ memcpy(pci_dev->exp.doe_state.read_mbox + cnt / 4,
+ &cxl_cstate->dsis, sizeof(struct cdat_dsis));
+ cnt += sizeof(struct cdat_dsis);
+
+ /* copy the DSEMTS entry */
+ memcpy(pci_dev->exp.doe_state.read_mbox + cnt / 4,
+ &cxl_cstate->dsemts, sizeof(struct cdat_dsemts));
+ cnt += sizeof(struct cdat_dsemts);
+
+ /* copy the SSLBE entry */
+
+ /* copy the SSLBIS entry */
+
+ memcpy(pci_dev->exp.doe_state.read_mbox + cnt / 4,
+ &cxl_cstate->sslbis, 4);
+ cnt += 4;
+ dw_cnt = cnt / 4;
+ pci_dev->exp.doe_state.read_mbox_len += dw_cnt;
+ DOE_DBG(" read_mbox_len=%x\n",
+ pci_dev->exp.doe_state.read_mbox_len);
+ memcpy(pci_dev->exp.doe_state.read_mbox + 1, &dw_cnt, 4);
+ DOE_DBG(" RD MBOX[0]=%x", *(pci_dev->exp.doe_state.read_mbox));
+ DOE_DBG(" RD MBOX[%d]=%x\n",
+ dw_cnt - 1, *(pci_dev->exp.doe_state.read_mbox + dw_cnt - 1));
+ for (int i = 0; i < dw_cnt; i++)
+ DOE_DBG("\033[31m RD MBOX[%d]=%x\033[m\n", i,
+ pci_dev->exp.doe_state.read_mbox[i]);
+
+ DOE_DBG("<< %s\n", __func__);
+
+ return DOE_SUCCESS;
+}
+
+static uint32_t ct3d_config_read(PCIDevice *pci_dev,
+ uint32_t addr, int size)
+{
+ DOE_DBG(">> %s addr: %"PRIx32" size: %x\n", __func__, addr, size);
+
+ DOE_DBG("<< %s\n", __func__);
+ return pcie_doe_read_config(pci_dev, addr, size); ;
+}
+
+static void ct3d_config_write(PCIDevice *pci_dev,
+ uint32_t addr, uint32_t val, int size)
+{
+ DOE_DBG(">> %s\n", __func__);
+ DOE_DBG(" addr=%x, val=%x, size=%x\n", addr, val, size);
+
+ pcie_doe_write_config(pci_dev, addr, val, size);
+
+ DOE_DBG("<< %s\n", __func__);
+}
+
+static void build_doe(CXLType3Dev *ct3d)
+{
+ CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
+
+ DOE_DBG(">> %s\n", __func__);
+ cxl_component_create_doe(cxl_cstate, 0x160);
+
+ DOE_DBG("<< %s\n", __func__);
+}
+
static void build_dvsecs(CXLType3Dev *ct3d)
{
CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
@@ -239,6 +356,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64,
&ct3d->cxl_dstate.device_registers);
+ build_doe(ct3d);
}
static uint64_t cxl_md_get_addr(const MemoryDeviceState *md)
@@ -357,6 +475,9 @@ static void ct3_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
+
+ pc->config_write = ct3d_config_write;
+ pc->config_read = ct3d_config_read;
CXLType3Class *cvc = CXL_TYPE3_DEV_CLASS(oc);
pc->realize = ct3_realize;
diff --git a/hw/pci/meson.build b/hw/pci/meson.build
index 5c4bbac817..aa4783d978 100644
--- a/hw/pci/meson.build
+++ b/hw/pci/meson.build
@@ -12,6 +12,7 @@ pci_ss.add(files(
# allow plugging PCIe devices into PCI buses, include them even if
# CONFIG_PCI_EXPRESS=n.
pci_ss.add(files('pcie.c', 'pcie_aer.c'))
+pci_ss.add(files('pcie.c', 'pcie_doe.c'))
softmmu_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c'))
softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 1ecf6f6a55..532219ae17 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -19,6 +19,8 @@
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/mem/memory-device.h"
#include "hw/pci/pci_bridge.h"
@@ -735,7 +737,7 @@ void pcie_cap_slot_write_config(PCIDevice *dev,
hotplug_event_notify(dev);
- /*
+ /*
* 6.7.3.2 Command Completed Events
*
* Software issues a command to a hot-plug capable Downstream Port by
diff --git a/hw/pci/pcie_doe.c b/hw/pci/pcie_doe.c
new file mode 100644
index 0000000000..92e16f328c
--- /dev/null
+++ b/hw/pci/pcie_doe.c
@@ -0,0 +1,360 @@
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qemu/range.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_doe.h"
+
+/*
+ * DOE Default Protocols (Discovery, CMA)
+ */
+/* Discovery Request Object */
+struct doe_discovery {
+ DOEHeader header;
+ uint8_t index;
+ uint8_t reserved[3];
+} __attribute__((__packed__));
+
+/* Discovery Response Object */
+struct doe_discovery_rsp {
+ DOEHeader header;
+ uint16_t vendor_id;
+ uint8_t doe_type;
+ uint8_t next_index;
+} __attribute__((__packed__));
+
+/* Callback for Discovery */
+static bool pcie_doe_discovery_rsp(PCIDevice *dev)
+{
+ struct doe_discovery *req = pcie_doe_get_req(dev);
+ uint8_t index = req->index;
+ DOEProtocol *prot = NULL;
+
+ /* Length mismatch, discard */
+ if (req->header.length < dwsizeof(struct doe_discovery)) {
+ return DOE_DISCARD;
+ }
+
+ if (index < dev->exp.doe_state.protocol_num) {
+ prot = &dev->exp.doe_state.protocols[index];
+ }
+
+ struct doe_discovery_rsp response = {
+ .header = {
+ .vendor_id = PCI_DOE_PCI_SIG_VID,
+ .doe_type = PCI_SIG_DOE_DISCOVERY,
+ .reserved = 0x0,
+ .length = dwsizeof(struct doe_discovery_rsp),
+ },
+ .vendor_id = (prot) ? prot->vendor_id : 0xFFFF,
+ .doe_type = (prot) ? prot->doe_type : 0xFF,
+ .next_index = (index + 1) < dev->exp.doe_state.protocol_num ?
+ (index + 1) : 0,
+ };
+
+ pcie_doe_set_rsp(dev, &response);
+
+ return DOE_SUCCESS;
+}
+
+/* Callback for CMA */
+static bool pcie_doe_cma_rsp(PCIDevice *dev)
+{
+ uint32_t local_val;
+
+ local_val =
+ pci_get_long(dev->config + dev->exp.doe_cap + PCI_EXP_DOE_STATUS);
+ local_val = FIELD_DP32(local_val, PCI_DOE_CAP_STATUS, DOE_ERROR, 0x1);
+ pci_set_long(dev->config + dev->exp.doe_cap + PCI_EXP_DOE_STATUS,
+ local_val);
+ memset(dev->exp.doe_state.read_mbox, 0,
+ PCI_DOE_MAX_DW_SIZE * sizeof(uint32_t));
+
+ dev->exp.doe_state.write_mbox_len = 0;
+
+ return DOE_DISCARD;
+}
+
+/*
+ * DOE Utilities
+ */
+static void pcie_doe_reset_mbox(PCIDevice *dev)
+{
+ DOEState *st = &dev->exp.doe_state;
+ uint16_t offset = dev->exp.doe_cap;
+
+ st->read_mbox_idx = 0;
+
+ st->read_mbox_len = 0;
+ st->write_mbox_len = 0;
+
+ memset(st->read_mbox, 0, PCI_DOE_MAX_DW_SIZE * sizeof(uint32_t));
+ memset(st->write_mbox, 0, PCI_DOE_MAX_DW_SIZE * sizeof(uint32_t));
+
+ pci_set_word(dev->config + offset + PCI_EXP_DOE_WR_DATA_MBOX, 0);
+ pci_set_word(dev->config + offset + PCI_EXP_DOE_RD_DATA_MBOX, 0);
+}
+
+void pcie_cap_doe_init(PCIDevice *dev, uint16_t offset)
+{
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_DOE, PCI_DOE_VER, offset,
+ PCI_DOE_SIZEOF);
+ dev->exp.doe_cap = offset;
+
+ /* Set wmask on all registers */
+ pci_set_long(dev->wmask + offset + PCI_EXP_DOE_CTRL, 0xffffffff);
+ pci_set_long(dev->wmask + offset + PCI_EXP_DOE_WR_DATA_MBOX, 0xffffffff);
+ pci_set_long(dev->wmask + offset + PCI_EXP_DOE_RD_DATA_MBOX, 0xffffffff);
+
+ dev->exp.doe_state.write_mbox =
+ calloc(PCI_DOE_MAX_DW_SIZE, sizeof(uint32_t));
+ if (dev->exp.doe_state.write_mbox == NULL) {
+ DOE_DBG("%s could not allocate DOE write mbox memory\n", __func__);
+ }
+
+ dev->exp.doe_state.read_mbox =
+ calloc(PCI_DOE_MAX_DW_SIZE, sizeof(uint32_t));
+ if (dev->exp.doe_state.read_mbox == NULL) {
+ DOE_DBG("%s could not allocate DOE read mbox memory\n", __func__);
+ }
+
+ dev->exp.doe_state.protocol_num = 0;
+ pcie_doe_register_protocol(dev, PCI_DOE_PCI_SIG_VID,
+ PCI_SIG_DOE_DISCOVERY, pcie_doe_discovery_rsp);
+ pcie_doe_register_protocol(dev, PCI_DOE_PCI_SIG_VID,
+ PCI_SIG_DOE_CMA, pcie_doe_cma_rsp);
+
+ pcie_doe_reset_mbox(dev);
+}
+
+void pcie_doe_register_protocol(PCIDevice *dev, uint16_t vendor_id,
+ uint8_t doe_type, bool (*set_rsp)(PCIDevice *))
+{
+ DOEState *st;
+
+ st = &dev->exp.doe_state;
+
+ /* Protocol num should not exceed 256 */
+ assert(st->protocol_num < PCI_DOE_PROTOCOL_MAX);
+
+ st->protocols[st->protocol_num].vendor_id = vendor_id;
+ st->protocols[st->protocol_num].doe_type = doe_type;
+ st->protocols[st->protocol_num].set_rsp = set_rsp;
+
+ st->protocol_num++;
+}
+
+void pcie_cap_doe_reset(PCIDevice *dev)
+{
+ uint16_t offset;
+
+ offset = dev->exp.doe_cap;
+ if (offset) {
+ pci_set_word(dev->config + offset + PCI_EXP_DOE_CTRL, 0);
+ pcie_doe_reset_mbox(dev);
+ }
+}
+
+uint32_t pcie_doe_build_protocol(DOEProtocol *p)
+{
+ return DATA_OBJ_BUILD_HEADER1(p->vendor_id, p->doe_type);
+}
+
+/* Return the pointer of DOE request in write mailbox buffer */
+void *pcie_doe_get_req(PCIDevice *dev)
+{
+ return dev->exp.doe_state.write_mbox;
+}
+
+/* Copy the response to read mailbox buffer */
+void pcie_doe_set_rsp(PCIDevice *dev, void *rsp)
+{
+ DOEState *st = &dev->exp.doe_state;
+ uint32_t len = ((DOEHeader *)rsp)->length;
+
+ memcpy(st->read_mbox + st->read_mbox_len, rsp, len * sizeof(uint32_t));
+ st->read_mbox_len += len;
+}
+
+static void pcie_doe_write_mbox(DOEState *st, uint32_t val)
+{
+ memcpy(st->write_mbox + st->write_mbox_len, &val, sizeof(uint32_t));
+
+ if (st->write_mbox_len == 1) {
+ DOE_DBG(" Capture DOE request DO length = %d\n", val & 0x0003ffff);
+ }
+
+ st->write_mbox_len += 1;
+}
+
+static bool pcie_doe_check_ready(PCIDevice *p)
+{
+ uint32_t val;
+
+ val = pci_get_long(p->config + p->exp.doe_cap + PCI_EXP_DOE_STATUS);
+ DOE_DBG(" STATUS_REG=%x\n", val);
+
+ val = FIELD_EX32(val, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY);
+ DOE_DBG(" DATA OBJECT READY=%x\n", val);
+
+ return val;
+}
+
+static void pcie_doe_set_ready(PCIDevice *p, bool rdy)
+{
+ uint32_t val;
+
+ val = pci_get_long(p->config + p->exp.doe_cap + PCI_EXP_DOE_STATUS);
+ val = FIELD_DP32(val, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY, rdy);
+ pci_set_long(p->config + p->exp.doe_cap + PCI_EXP_DOE_STATUS, val);
+}
+
+static void pcie_doe_set_error(PCIDevice *p, bool err)
+{
+ uint32_t val;
+
+ val = pci_get_long(p->config + p->exp.doe_cap + PCI_EXP_DOE_STATUS);
+ val = FIELD_DP32(val, PCI_DOE_CAP_STATUS, DOE_ERROR, err);
+ pci_set_long(p->config + p->exp.doe_cap + PCI_EXP_DOE_STATUS, val);
+}
+
+uint32_t pcie_doe_read_config(PCIDevice *pci_dev,
+ uint32_t addr, int size)
+{
+ uint32_t ret_val;
+ uint16_t doe_offset = pci_dev->exp.doe_cap;
+ uint32_t doe_reg = addr - doe_offset;
+ DOEState *st = &pci_dev->exp.doe_state;
+
+ /* Decode address and process DOE protocol if overlaps DOE cap range */
+ if (!range_covers_byte(doe_offset, PCI_DOE_SIZEOF, addr))
+ ret_val = pci_default_read_config(pci_dev, addr, size);
+ else {
+ switch (doe_reg) {
+ case PCI_EXP_DOE_CTRL:
+ /* must return ABORT=0 and GO=0 */
+ ret_val = pci_get_long(pci_dev->config + addr);
+ ret_val &= PCI_EXP_DOE_CTRL_RMASK;
+ DOE_DBG(" CONTROL REG=%x\n", ret_val);
+ break;
+ case PCI_EXP_DOE_RD_DATA_MBOX:
+ /* check that DOE_READY is set */
+ if (!pcie_doe_check_ready(pci_dev)) {
+ /* return 0 if not ready */
+ ret_val = 0;
+ DOE_DBG(" RD MBOX RETURN=%x\n", ret_val);
+ } else {
+ /* deposit next DO DW into read mbox */
+ DOE_DBG(" RD MBOX, DATA OBJECT READY,"
+ " CURRENT DO DW OFFSET=%x\n",
+ st->read_mbox_idx);
+
+ ret_val = st->read_mbox[st->read_mbox_idx];
+ pci_set_long(pci_dev->config + addr, ret_val);
+
+ DOE_DBG(" RD MBOX DW=%x\n", ret_val);
+ DOE_DBG(" RD MBOX DW OFFSET=%d, RD MBOX LENGTH=%d\n",
+ st->read_mbox_idx, st->read_mbox_len);
+ }
+ break;
+ case PCI_EXP_DOE_WR_DATA_MBOX:
+ ret_val = 0;
+ DOE_DBG(" WR MBOX, local_val =%x\n", ret_val);
+ break;
+ default:
+ ret_val = pci_default_read_config(pci_dev, addr, size);
+ DOE_DBG(" ADDR=%x, VAL =%x\n", addr, ret_val);
+ break;
+ }
+ DOE_DBG(" RETURN=%x\n", ret_val);
+ }
+
+ return ret_val;
+}
+
+void pcie_doe_write_config(PCIDevice *pci_dev,
+ uint32_t addr, uint32_t val, int size)
+{
+ uint16_t doe_offset = pci_dev->exp.doe_cap;
+ uint32_t doe_reg = addr - doe_offset;
+ DOEState *st = &pci_dev->exp.doe_state;
+ int p;
+ bool discard;
+
+ DOE_DBG(" addr=%x, val=%x, size=%x\n", addr, val, size);
+
+ /* if accessing DOE cap read or write mailbox */
+ if (!range_covers_byte(doe_offset, PCI_DOE_SIZEOF, addr))
+ pci_default_write_config(pci_dev, addr, val, size);
+ else {
+ switch (doe_reg) {
+ case PCI_EXP_DOE_CTRL:
+ DOE_DBG(" CONTROL=%x\n", val);
+ /* control reg */
+ if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_ABORT)) {
+ /* If ABORT, clear status reg DOE Error and DOE Ready */
+ DOE_DBG(" Setting ABORT\n");
+ pcie_doe_set_ready(pci_dev, 0);
+ pcie_doe_set_error(pci_dev, 0);
+ pcie_doe_reset_mbox(pci_dev);
+ } else if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_GO)) {
+ DOE_DBG(" CONTROL GO=%x\n", val);
+ /*
+ * Check protocol the incoming request in write_mbox and
+ * memcpy the corresponding response to read_mbox.
+ *
+ * "discard" should be set up if the response callback
+ * function could not deal with request (e.g. length
+ * mismatch) or the protocol of request was not found.
+ */
+ p = 0;
+ discard = DOE_DISCARD;
+ while (p < st->protocol_num) {
+ if (st->write_mbox[0] ==
+ pcie_doe_build_protocol(&st->protocols[p])) {
+ /* Found */
+ DOE_DBG(" DOE PROTOCOL=%x\n", st->write_mbox[0]);
+ /*
+ * Spec:
+ * If the number of DW transferred does not match the
+ * indicated Length for a data object, then the
+ * data object must be silently discarded.
+ */
+ if (st->write_mbox_len ==
+ ((DOEHeader *)pcie_doe_get_req(pci_dev))->length)
+ discard = st->protocols[p].set_rsp(pci_dev);
+ break;
+ }
+ p++;
+ }
+
+ /* set DOE Ready */
+ if (!discard) {
+ pcie_doe_set_ready(pci_dev, 1);
+ } else {
+ pcie_doe_reset_mbox(pci_dev);
+ }
+ }
+ break;
+ case PCI_EXP_DOE_RD_DATA_MBOX:
+ /* read mbox */
+ DOE_DBG(" INCR RD MBOX DO DW OFFSET=%d\n", st->read_mbox_idx);
+ st->read_mbox_idx += 1;
+ /* Last DW */
+ if (st->read_mbox_idx >= st->read_mbox_len) {
+ pcie_doe_reset_mbox(pci_dev);
+ pcie_doe_set_ready(pci_dev, 0);
+ }
+ break;
+ case PCI_EXP_DOE_WR_DATA_MBOX:
+ /* write mbox */
+ DOE_DBG(" WR MBOX=%x, DW OFFSET = %d\n", val, st->write_mbox_len);
+ pcie_doe_write_mbox(st, val);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h
index 762feb54da..4078b99c49 100644
--- a/include/hw/cxl/cxl_component.h
+++ b/include/hw/cxl/cxl_component.h
@@ -132,6 +132,23 @@ HDM_DECODER_INIT(0);
_Static_assert((CXL_SNOOP_REGISTERS_OFFSET + CXL_SNOOP_REGISTERS_SIZE) < 0x1000,
"No space for registers");
+/* 14.16.4 - Compliance Mode */
+#define CXL_COMP_MODE_CAP 0x0
+#define CXL_COMP_MODE_STATUS 0x1
+#define CXL_COMP_MODE_HALT 0x2
+#define CXL_COMP_MODE_MULT_WR_STREAM 0x3
+#define CXL_COMP_MODE_PRO_CON 0x4
+#define CXL_COMP_MODE_BOGUS 0x5
+#define CXL_COMP_MODE_INJ_POISON 0x6
+#define CXL_COMP_MODE_INJ_CRC 0x7
+#define CXL_COMP_MODE_INJ_FC 0x8
+#define CXL_COMP_MODE_TOGGLE_CACHE 0x9
+#define CXL_COMP_MODE_INJ_MAC 0xa
+#define CXL_COMP_MODE_INS_UNEXP_MAC 0xb
+#define CXL_COMP_MODE_INJ_VIRAL 0xc
+#define CXL_COMP_MODE_INJ_ALMP 0xd
+#define CXL_COMP_MODE_IGN_ALMP 0xe
+
typedef struct component_registers {
/*
* Main memory region to be registered with QEMU core.
@@ -173,6 +190,103 @@ typedef struct cxl_component {
struct PCIDevice *pdev;
};
};
+
+ /*
+ * SW write write mailbox and GO on last DW
+ * device sets READY of offset DW in DO types to copy
+ * to read mailbox register on subsequent cfg_read
+ * of read mailbox register and then on cfg_write to
+ * denote success read increments offset to next DW
+ */
+ struct cdat_table_header cdat_header;
+
+ union doe_request_u {
+ /* Compliance DOE Data Objects Type=0*/
+ struct cxl_compliance_mode_cap
+ mode_cap;
+ struct cxl_compliance_mode_status
+ mode_status;
+ struct cxl_compliance_mode_halt
+ mode_halt;
+ struct cxl_compliance_mode_multiple_write_streaming
+ multiple_write_streaming;
+ struct cxl_compliance_mode_producer_consumer
+ producer_consumer;
+ struct cxl_compliance_mode_inject_bogus_writes
+ inject_bogus_writes;
+ struct cxl_compliance_mode_inject_poison
+ inject_poison;
+ struct cxl_compliance_mode_inject_crc
+ inject_crc;
+ struct cxl_compliance_mode_inject_flow_control
+ inject_flow_control;
+ struct cxl_compliance_mode_toggle_cache_flush
+ toggle_cache_flush;
+ struct cxl_compliance_mode_inject_mac_delay
+ inject_mac_delay;
+ struct cxl_compliance_mode_insert_unexp_mac
+ insert_unexp_mac;
+ struct cxl_compliance_mode_inject_viral
+ inject_viral;
+ struct cxl_compliance_mode_inject_almp
+ inject_almp;
+ struct cxl_compliance_mode_ignore_almp
+ ignore_almp;
+ struct cxl_compliance_mode_ignore_bit_error
+ ignore_bit_error;
+ /* CDAT DOE Data Objects Type=2*/
+ struct cxl_cdat cdat;
+ char data_byte[128];
+ } doe_request;
+
+ union doe_resp_u {
+ /* Compliance DOE Data Objects Type=0*/
+ struct cxl_compliance_mode_cap_rsp
+ cap_rsp;
+ struct cxl_compliance_mode_status_rsp
+ status_rsp;
+ struct cxl_compliance_mode_halt_rsp
+ halt_rsp;
+ struct cxl_compliance_mode_multiple_write_streaming_rsp
+ multiple_write_streaming_rsp;
+ struct cxl_compliance_mode_producer_consumer_rsp
+ producer_consumer_rsp;
+ struct cxl_compliance_mode_inject_bogus_writes_rsp
+ inject_bogus_writes_rsp;
+ struct cxl_compliance_mode_inject_poison_rsp
+ inject_poison_rsp;
+ struct cxl_compliance_mode_inject_crc_rsp
+ inject_crc_rsp;
+ struct cxl_compliance_mode_inject_flow_control_rsp
+ inject_flow_control_rsp;
+ struct cxl_compliance_mode_toggle_cache_flush_rsp
+ toggle_cache_flush_rsp;
+ struct cxl_compliance_mode_inject_mac_delay_rsp
+ inject_mac_delay_rsp;
+ struct cxl_compliance_mode_insert_unexp_mac_rsp
+ insert_unexp_mac_rsp;
+ struct cxl_compliance_mode_inject_viral
+ inject_viral_rsp;
+ struct cxl_compliance_mode_inject_almp_rsp
+ inject_almp_rsp;
+ struct cxl_compliance_mode_ignore_almp_rsp
+ ignore_almp_rsp;
+ struct cxl_compliance_mode_ignore_bit_error_rsp
+ ignore_bit_error_rsp;
+ /* CDAT DOE Data Objects Type=2*/
+ struct cxl_cdat_rsp cdat_rsp;
+ char data_byte[520 * 4];
+ uint32_t data_dword[520];
+ } doe_resp;
+
+ /* Table entry types */
+ struct cdat_dsmas dsmas;
+ struct cdat_dslbis dslbis;
+ struct cdat_dsmscis dsmscis;
+ struct cdat_dsis dsis;
+ struct cdat_dsemts dsemts;
+ struct cdat_sslbe sslbe;
+ struct cdat_sslbis sslbis;
} CXLComponentState;
void cxl_component_register_block_init(Object *obj,
@@ -184,4 +298,10 @@ void cxl_component_register_init_common(uint32_t *reg_state,
void cxl_component_create_dvsec(CXLComponentState *cxl_cstate, uint16_t length,
uint16_t type, uint8_t rev, uint8_t *body);
+void cxl_component_create_doe(CXLComponentState *cxl_cstate, uint16_t offset);
+
+uint32_t cxl_doe_compliance_init(CXLComponentState *cxl_cstate);
+bool cxl_doe_compliance_rsp(PCIDevice *pci_dev);
+void cxl_doe_cdat_init(CXLComponentState *cxl_cstate);
+bool cxl_doe_cdat_rsp(PCIDevice *pci_dev);
#endif
diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h
index 9ec28c9feb..c7300af8f8 100644
--- a/include/hw/cxl/cxl_pci.h
+++ b/include/hw/cxl/cxl_pci.h
@@ -34,6 +34,15 @@
#define REG_LOC_DVSEC_LENGTH 0x24
#define REG_LOC_DVSEC_REVID 0
+enum {
+ CXL_DOE_COMPLIANCE = 0,
+ CXL_DOE_TABLE_ACCESS = 2,
+ CXL_DOE_MAX_TYPE
+};
+
+#define CXL_DOE_PROTOCOL_COMPLIANCE ((CXL_DOE_COMPLIANCE << 16) | CXL_VENDOR_ID)
+#define CXL_DOE_PROTOCOL_CDAT ((CXL_DOE_TABLE_ACCESS << 16) | CXL_VENDOR_ID)
+
enum {
PCIE_CXL_DEVICE_DVSEC = 0,
NON_CXL_FUNCTION_MAP_DVSEC = 2,
@@ -54,6 +63,425 @@ struct dvsec_header {
_Static_assert(sizeof(struct dvsec_header) == 10,
"dvsec header size incorrect");
+/* CXL 2.0 - 8.1.11 */
+/*
+ * CDAT - Coherence Device Attributes Table
+ * Version 1
+ */
+
+/*
+ * CXL 2.0 devices may implement certain DOE Cap
+ */
+
+/*
+ * DOE CDAT Table Protocol
+ */
+
+/* Data object header */
+struct cdat_table_header {
+ uint32_t length; /* Length of table in bytes, including this header */
+ uint8_t revision; /* ACPI Specification minor version number */
+ uint8_t checksum; /* To make sum of entire table == 0 */
+ char reserved[6];
+ char sequence[4]; /* ASCII table signature */
+} __attribute__((__packed__));
+
+/* Values for subtable type in CDAT structures */
+
+enum cdat_type {
+ CDAT_TYPE_DSMAS = 0,
+ CDAT_TYPE_DSLBIS = 1,
+ CDAT_TYPE_DSMSCIS = 2,
+ CDAT_TYPE_DSIS = 3,
+ CDAT_TYPE_DSEMTS = 4,
+ CDAT_TYPE_SSLBIS = 5
+};
+
+/*
+ * CDAT Structure Subtables
+ */
+
+struct cxl_cdat {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t table_type;
+ uint16_t entry_handle;
+} __attribute__((__packed__));
+
+struct cxl_cdat_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t table_type;
+ uint16_t next_entry_handle;
+ uint32_t *entry; /* CDAT Table Entry content */
+} __attribute__((__packed__));
+
+struct cdat_dsmas {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t DSMADhandle;
+ uint8_t flags;
+ uint16_t reserved2;
+ uint64_t DPA_base;
+ uint64_t DPA_length;
+} __attribute__((__packed__));
+
+struct cdat_dslbis {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t handle;
+ uint8_t flags;
+ uint8_t data_type;
+ uint8_t reserved2;
+ uint64_t entry_base_unit;
+ uint16_t entry[3];
+ uint16_t reserved3;
+} __attribute__((__packed__));
+
+struct cdat_dsmscis {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t DSMASH_handle;
+ char reserved2[3];
+ uint64_t memory_side_cache_size;
+ uint32_t cache_attributes;
+} __attribute__((__packed__));
+
+struct cdat_dsis {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t flags;
+ uint8_t handle;
+ uint16_t reserved2;
+ uint64_t DPA_offset;
+ uint64_t DPA_length;
+} __attribute__((__packed__));
+
+struct cdat_dsemts {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t DSMAS_handle;
+ uint8_t EFI_memory_type_attr;
+ uint16_t reserved2;
+} __attribute__((__packed__));
+
+struct cdat_sslbe {
+ uint16_t port_x_id;
+ uint16_t port_y_id;
+ uint16_t latency_bandwidth;
+ uint16_t reserved;
+} __attribute__((__packed__));
+
+struct cdat_sslbis {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t data_type;
+ char reserved2[3];
+ struct cdat_sslbe cdat_sslbe_array[256];
+} __attribute__((__packed__));
+
+/*
+ * DOE Compliance Mode Protocol
+ * Version 1
+ */
+
+/*
+ * CXL 2.0 devices may implement certain DOE Cap
+ */
+
+struct cxl_compliance_mode_cap {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_cap_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+ uint64_t available_cap_bitmask;
+ uint64_t enabled_cap_bitmask;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_status {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_status_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint32_t cap_bitfield;
+ uint16_t cache_size;
+ uint8_t cache_size_units;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_halt {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_halt_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_multiple_write_streaming {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t protocol;
+ uint8_t virtual_addr;
+ uint8_t self_checking;
+ uint8_t verify_read_semantics;
+ uint8_t num_inc;
+ uint8_t num_sets;
+ uint8_t num_loops;
+ uint8_t reserved2;
+ uint64_t start_addr;
+ uint64_t write_addr;
+ uint64_t writeback_addr;
+ uint64_t byte_mask;
+ uint32_t addr_incr;
+ uint32_t set_offset;
+ uint32_t pattern_p;
+ uint32_t inc_pattern_b;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_multiple_write_streaming_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_producer_consumer {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t protocol;
+ uint8_t num_inc;
+ uint8_t num_sets;
+ uint8_t num_loops;
+ uint8_t write_semantics;
+ char reserved2[3];
+ uint64_t start_addr;
+ uint64_t byte_mask;
+ uint32_t addr_incr;
+ uint32_t set_offset;
+ uint32_t pattern;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_producer_consumer_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_bogus_writes {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t count;
+ uint8_t reserved2;
+ uint32_t pattern;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_bogus_writes_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_poison {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t protocol;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_poison_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_crc {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t num_bits_flip;
+ uint8_t num_flits_inj;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_crc_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_flow_control {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t inj_flow_control;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_flow_control_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_toggle_cache_flush {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t inj_flow_control;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_toggle_cache_flush_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_mac_delay {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t enable;
+ uint8_t mode;
+ uint8_t delay;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_mac_delay_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_insert_unexp_mac {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t opcode;
+ uint8_t mode;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_insert_unexp_mac_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_viral {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t protocol;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_viral_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t length;
+ uint8_t status;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_almp {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t opcode;
+ char reserved2[3];
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_inject_almp_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t reserved[6];
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_ignore_almp {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t opcode;
+ uint8_t reserved2[3];
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_ignore_almp_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t reserved[6];
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_ignore_bit_error {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t version;
+ uint16_t reserved;
+ uint8_t opcode;
+} __attribute__((__packed__));
+
+struct cxl_compliance_mode_ignore_bit_error_rsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t version;
+ uint8_t reserved[6];
+} __attribute__((__packed__));
+
/*
* CXL 2.0 devices must implement certain DVSEC IDs, and can [optionally]
* implement others.
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index 14c58ebdb6..6d199f3093 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -25,6 +25,7 @@
#include "hw/pci/pcie_regs.h"
#include "hw/pci/pcie_aer.h"
#include "hw/hotplug.h"
+#include "hw/pci/pcie_doe.h"
typedef enum {
/* for attention and power indicator */
@@ -81,6 +82,10 @@ struct PCIExpressDevice {
/* ACS */
uint16_t acs_cap;
+
+ /* DOE */
+ uint16_t doe_cap;
+ DOEState doe_state;
};
#define COMPAT_PROP_PCP "power_controller_present"
diff --git a/include/hw/pci/pcie_doe.h b/include/hw/pci/pcie_doe.h
new file mode 100644
index 0000000000..4eef0acea9
--- /dev/null
+++ b/include/hw/pci/pcie_doe.h
@@ -0,0 +1,130 @@
+#ifndef PCIE_DOE_H
+#define PCIE_DOE_H
+
+#include "qemu/range.h"
+#include "qemu/typedefs.h"
+#include "hw/register.h"
+
+/* PCI DOE register defines 7.9.xx */
+/* DOE Capabilities Register */
+#define PCI_EXP_DOE_CAP 0x04
+#define PCI_EXP_DOE_CAP_INTR_SUPP 0x00000001
+#define PCI_EXP_DOE_CAP_INTR(x) ((x) >> 11)
+REG32(PCI_DOE_CAP_REG, 0)
+ FIELD(PCI_DOE_CAP_REG, INTR_SUPP, 0, 1)
+ FIELD(PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM, 1, 11)
+/* DOE Control Register */
+#define PCI_EXP_DOE_CTRL 0x08
+#define PCI_EXP_DOE_CTRL_ABORT 0x00000001
+#define PCI_EXP_DOE_CTRL_INTR_EN 0x00000002
+#define PCI_EXP_DOE_CTRL_GO 0x80000000
+REG32(PCI_DOE_CAP_CONTROL, 0)
+ FIELD(PCI_DOE_CAP_CONTROL, DOE_ABORT, 0, 1)
+ FIELD(PCI_DOE_CAP_CONTROL, DOE_INTR_EN, 1, 1)
+ FIELD(PCI_DOE_CAP_CONTROL, DOE_GO, 31, 1)
+#define PCI_EXP_DOE_CTRL_RMASK \
+ (~(PCI_EXP_DOE_CTRL_ABORT | PCI_EXP_DOE_CTRL_GO))
+/* DOE Status Register */
+#define PCI_EXP_DOE_STATUS 0x0c
+#define PCI_EXP_DOE_STATUS_BUSY 0x00000001
+#define PCI_EXP_DOE_STATUS_INTR 0x00000002
+#define PCI_EXP_DOE_STATUS_ERR 0x00000004
+#define PCI_EXP_DOE_STATUS_DO_RDY 0x80000000
+REG32(PCI_DOE_CAP_STATUS, 0)
+ FIELD(PCI_DOE_CAP_STATUS, DOE_BUSY, 0, 1)
+ FIELD(PCI_DOE_CAP_STATUS, DOE_INTR_STATUS, 1, 1)
+ FIELD(PCI_DOE_CAP_STATUS, DOE_ERROR, 2, 1)
+ FIELD(PCI_DOE_CAP_STATUS, DATA_OBJ_RDY, 31, 1)
+/* DOE Write Data Mailbox Register */
+#define PCI_EXP_DOE_WR_DATA_MBOX 0x10
+/* DOE Read Data Mailbox Register */
+#define PCI_EXP_DOE_RD_DATA_MBOX 0x14
+
+/* Table 7-x2 */
+#define PCI_DOE_PCI_SIG_VID 0x0001
+#define PCI_SIG_DOE_DISCOVERY 0x00
+#define PCI_SIG_DOE_CMA 0x01
+
+#define DATA_OBJ_BUILD_HEADER1(v, p) ((p << 16) | v)
+#define PCI_DOE_PROTOCOL_DISCOVERY \
+ DATA_OBJ_BUILD_HEADER1(PCI_DOE_PCI_SIG_VID, PCI_SIG_DOE_DISCOVERY)
+#define PCI_DOE_PROTOCOL_CMA \
+ DATA_OBJ_BUILD_HEADER1(PCI_DOE_PCI_SIG_VID, PCI_SIG_DOE_CMA)
+
+/* Table 7-x3 */
+#define DOE_DISCOVERY_IDX_MASK 0x000000ff
+
+/* Figure 6-x1 */
+#define DATA_OBJECT_HEADER1_OFFSET 0
+#define DATA_OBJECT_HEADER2_OFFSET 1
+#define DATA_OBJECT_CONTENT_OFFSET 2
+
+#define PCI_DOE_MAX_DW_SIZE (1 << 18)
+#define PCI_DOE_PROTOCOL_MAX 256
+
+#define DOE_SUCCESS 0
+#define DOE_DISCARD 1
+/*
+ * DOE Protocol - Data Object
+ */
+typedef struct DOEHeader DOEHeader;
+typedef struct DOEProtocol DOEProtocol;
+typedef struct DOEState DOEState;
+
+struct DOEHeader {
+ uint16_t vendor_id;
+ uint8_t doe_type;
+ uint8_t reserved;
+ struct {
+ uint32_t length:18;
+ uint32_t reserved2:14;
+ };
+} __attribute__((__packed__));
+
+/* Protocol infos and rsp function callback */
+struct DOEProtocol {
+ uint16_t vendor_id;
+ uint8_t doe_type;
+
+ bool (*set_rsp)(PCIDevice *);
+};
+
+struct DOEState {
+ /* Mailbox buffer for device */
+ uint32_t *write_mbox;
+ uint32_t *read_mbox;
+
+ /* Mailbox position indicator */
+ uint32_t read_mbox_idx;
+ uint32_t read_mbox_len;
+ uint32_t write_mbox_len;
+
+ /* Protocols and its callback response */
+ DOEProtocol protocols[PCI_DOE_PROTOCOL_MAX];
+ uint32_t protocol_num;
+};
+
+void pcie_cap_doe_init(PCIDevice *dev, uint16_t offset);
+void pcie_cap_doe_reset(PCIDevice *dev);
+uint32_t pcie_doe_build_protocol(DOEProtocol *p);
+uint32_t pcie_doe_read_config(PCIDevice *pci_dev, uint32_t addr, int size);
+void pcie_doe_write_config(PCIDevice *pci_dev, uint32_t addr,
+ uint32_t val, int size);
+void pcie_doe_register_protocol(PCIDevice *dev, uint16_t vendor_id,
+ uint8_t doe_type,
+ bool (*set_rsp)(PCIDevice *));
+
+/* Utility functions for set_rsp in DOEProtocol */
+void *pcie_doe_get_req(PCIDevice *dev);
+void pcie_doe_set_rsp(PCIDevice *dev, void *rsp);
+
+#define DOE_DEBUG
+#ifdef DOE_DEBUG
+#define DOE_DBG(...) printf(__VA_ARGS__)
+#else
+#define DOE_DBG(...) {}
+#endif
+
+#define dwsizeof(s) ((sizeof(s) + 4 - 1) / 4)
+
+#endif /* PCIE_DOE_H */
diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h
index 1db86b0ec4..963dc2e170 100644
--- a/include/hw/pci/pcie_regs.h
+++ b/include/hw/pci/pcie_regs.h
@@ -179,4 +179,8 @@ typedef enum PCIExpLinkWidth {
#define PCI_ACS_VER 0x1
#define PCI_ACS_SIZEOF 8
+/* DOE Capability Register Fields */
+#define PCI_DOE_VER 0x1
+#define PCI_DOE_SIZEOF 24
+
#endif /* QEMU_PCIE_REGS_H */
diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h
index e709ae8235..4a7b7a426d 100644
--- a/include/standard-headers/linux/pci_regs.h
+++ b/include/standard-headers/linux/pci_regs.h
@@ -730,7 +730,8 @@
#define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */
#define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */
#define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */
-#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT
+#define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
next prev parent reply other threads:[~2021-02-02 22:32 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-02-02 17:46 [RFC PATCH v1 00/01] PCIe DOE for PCIe and CXL 2.0 Chris Browy
2021-02-02 20:43 ` Chris Browy [this message]
2021-02-03 17:23 ` [RFC PATCH v1 01/01] " Jonathan Cameron
2021-02-03 0:48 ` [RFC PATCH v1 00/01] " Chris Browy
2021-02-04 4:53 [RFC PATCH v1 01/01] " Chris Browy
2021-02-05 16:09 ` Jonathan Cameron
2021-02-05 17:19 ` Ben Widawsky
2021-02-05 18:49 ` Jonathan Cameron
2021-02-05 19:35 ` Chris Browy
2021-02-08 10:55 ` Jonathan Cameron
2021-02-08 17:51 ` Ben Widawsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=alpine.LRH.2.23.451.2102021543190.30097@server4 \
--to=cbrowy@avery-design.com \
--cc=Jonathan.Cameron@Huawei.com \
--cc=armbru@redhat.com \
--cc=ben.widawsky@intel.com \
--cc=dan.j.williams@intel.com \
--cc=david@redhat.com \
--cc=f4bug@amsat.org \
--cc=imammedo@redhat.com \
--cc=ira.weiny@intel.com \
--cc=jgroves@micron.com \
--cc=linux-cxl@vger.kernel.org \
--cc=mst@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).