diff mbox series

[RFC,v3,cxl-2.0-doe,2/2] CXL DOE support for CDAT and Compliance Mode

Message ID 1615323876-17716-1-git-send-email-cbrowy@avery-design.com
State New
Headers show
Series Version 3 patch series for PCIe DOE for PCIe and CXL 2.0 | expand

Commit Message

Chris Browy March 9, 2021, 9:04 p.m. UTC
---
 hw/cxl/cxl-component-utils.c   |  93 ++++++++++++
 hw/mem/cxl_type3.c             | 184 ++++++++++++++++++++++++
 include/hw/cxl/cxl_cdat.h      | 127 +++++++++++++++++
 include/hw/cxl/cxl_compl.h     | 252 +++++++++++++++++++++++++++++++++
 include/hw/cxl/cxl_component.h |  74 ++++++++++
 include/hw/cxl/cxl_device.h    |   4 +
 include/hw/cxl/cxl_pci.h       |   2 +
 7 files changed, 736 insertions(+)
 create mode 100644 include/hw/cxl/cxl_cdat.h
 create mode 100644 include/hw/cxl/cxl_compl.h

Comments

Jonathan Cameron March 12, 2021, 11:51 a.m. UTC | #1
On Tue, 9 Mar 2021 16:04:36 -0500
Chris Browy <cbrowy@avery-design.com> wrote:

Hi Chris,

As for patch 1, description needed here.  Let's move this series towards
the form needed for a final submission.

2 features, 2 patches...  If nothing else makes each one small enough to be
suitable for review with morning coffee :)


> ---
>  hw/cxl/cxl-component-utils.c   |  93 ++++++++++++
>  hw/mem/cxl_type3.c             | 184 ++++++++++++++++++++++++
>  include/hw/cxl/cxl_cdat.h      | 127 +++++++++++++++++
>  include/hw/cxl/cxl_compl.h     | 252 +++++++++++++++++++++++++++++++++
>  include/hw/cxl/cxl_component.h |  74 ++++++++++
>  include/hw/cxl/cxl_device.h    |   4 +
>  include/hw/cxl/cxl_pci.h       |   2 +
>  7 files changed, 736 insertions(+)
>  create mode 100644 include/hw/cxl/cxl_cdat.h
>  create mode 100644 include/hw/cxl/cxl_compl.h
> 
> diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c
> index 41d36f802a..1a2408dc76 100644
> --- a/hw/cxl/cxl-component-utils.c
> +++ b/hw/cxl/cxl-component-utils.c

...

> +void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp)
> +{
> +    uint8_t sum = 0;
> +    int i, curr = 0, ent = 0;
> +    CDATStruct cdat_st[1024];
> +    struct cdat_sub_header hdr;
> +    FILE *fp;
> +    uint8_t iasl_hdr[IASL_HEADER_LEN];
> +    size_t rcount;
> +
> +    fp = fopen(cxl_cstate->cdat_filename, "r");
> +
> +    if (fp) {
> +        /* Read iASL header */
> +        rcount= fread(&iasl_hdr, sizeof(iasl_hdr), 1, fp);
> +	if (rcount == 0)
> +            error_setg(errp, "File read failed");
> +	    
> +
> +        for (i = 0; i < IASL_HEADER_LEN; i++) {
> +            sum += iasl_hdr[i];
> +        }
> +        sum = (~sum + 1) & 0xFF;
> +
> +        curr = ftell(fp);
> +
> +        /* Read CDAT structures */
> +        while (fread(&hdr, sizeof(hdr), 1, fp)) {
> +            cdat_len_check(&hdr, errp);
> +
> +            cdat_st[ent].base2 = curr;
> +            cdat_st[ent].length = hdr.length;
> +            ent++;
> +
> +            fseek(fp, curr + hdr.length, SEEK_SET);
> +            curr = ftell(fp);
> +        }
> +        /* Check the last structure */
> +        fseek(fp, 0, SEEK_END);
> +        assert(curr == ftell(fp));
> +    } else {
> +        error_setg(errp, "Please specify the CDAT file by using ',cdat='");

It definitely wants to be optional.  Fine to not provide one.
I'd hope we can generate one that makes 'sense' for whatever memory etc
is configured though and have that as the fallback if one isn't provided.
That will be a lot more convenient if people are working on other aspects
of support and don't happen to care about exactly what is in CDAT on
a given day.

> +    }
> +    cxl_cstate->cdat_file = fp;
> +
> +    cxl_cstate->cdat_ent_len = ent;
> +    cxl_cstate->cdat_ent = g_malloc0(sizeof(CDATStruct) * ent);
> +    memcpy(cxl_cstate->cdat_ent, cdat_st, sizeof(CDATStruct) * ent);
> +
> +    /* Set CDAT header, ent = 0 */
> +    cxl_cstate->cdat_header.revision = CXL_CDAT_REV;
> +    cxl_cstate->cdat_header.sequence = 0;
> +    cxl_cstate->cdat_header.length += curr + sizeof(cxl_cstate->cdat_header);
> +
> +    sum += cxl_cstate->cdat_header.revision +
> +           cxl_cstate->cdat_header.sequence + cxl_cstate->cdat_header.length;
> +
> +    cxl_cstate->cdat_header.checksum = ~sum + 1;
> +}
> diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
> index bf33ddb915..33f571e1a5 100644
> --- a/hw/mem/cxl_type3.c
> +++ b/hw/mem/cxl_type3.c
> @@ -13,6 +13,156 @@
>  #include "qemu/rcu.h"
>  #include "sysemu/hostmem.h"
>  #include "hw/cxl/cxl.h"
> +#include "hw/pci/msi.h"
> +#include "hw/pci/msix.h"
> +
> +uint32_t cxl_doe_compliance_init(DOECap *doe_cap)
> +{
> +    CXLComponentState *cxl_cstate = &CT3(doe_cap->pdev)->cxl_cstate;
> +    uint32_t req;
> +    uint32_t byte_cnt = 0;
> +
> +    DOE_DBG(">> %s\n",  __func__);
> +
> +    req = ((struct cxl_compliance_mode_cap *)pcie_doe_get_req(doe_cap))
> +        ->req_header.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 =
> +            DIV_ROUND_UP(sizeof(struct cxl_compliance_mode_cap_rsp), 4);
> +        cxl_cstate->doe_resp.cap_rsp.rsp_header.rsp_code = 0x0;
> +        cxl_cstate->doe_resp.cap_rsp.rsp_header.version = 0x1;
> +        cxl_cstate->doe_resp.cap_rsp.rsp_header.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 =
> +            DIV_ROUND_UP(sizeof(struct cxl_compliance_mode_status_rsp), 4);
> +        cxl_cstate->doe_resp.status_rsp.rsp_header.rsp_code = 0x1;
> +        cxl_cstate->doe_resp.status_rsp.rsp_header.version = 0x1;
> +        cxl_cstate->doe_resp.status_rsp.rsp_header.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;

Given there are only two types in reality.  Can we get rid of all the others
in the big union?  Right now it looks like those aren't handled, but reality
is they are the same as the ones here.


> +    default:
> +        break;
> +    }
> +
> +    DOE_DBG("  REQ=%x, RSP BYTE_CNT=%d\n", req, byte_cnt);
> +    DOE_DBG("<< %s\n",  __func__);
> +    return byte_cnt;
> +}
> +
> +
> +bool cxl_doe_compliance_rsp(DOECap *doe_cap)
> +{
> +    CXLComponentState *cxl_cstate = &CT3(doe_cap->pdev)->cxl_cstate;
> +    uint32_t byte_cnt;
> +    uint32_t dw_cnt;
> +
> +    DOE_DBG(">> %s\n",  __func__);
> +
> +    byte_cnt = cxl_doe_compliance_init(doe_cap);
> +    dw_cnt = byte_cnt / 4;
> +    memcpy(doe_cap->read_mbox,
> +           cxl_cstate->doe_resp.data_byte, byte_cnt);
> +
> +    doe_cap->read_mbox_len += dw_cnt;
> +
> +    DOE_DBG("  LEN=%x, RD MBOX[%d]=%x\n", dw_cnt - 1,
> +            doe_cap->read_mbox_len,
> +            *(doe_cap->read_mbox + dw_cnt - 1));
> +
> +    DOE_DBG("<< %s\n",  __func__);
> +
> +    return true;
> +}
> +
> +bool cxl_doe_cdat_rsp(DOECap *doe_cap)
> +{
> +    CXLComponentState *cxl_cstate = &CT3(doe_cap->pdev)->cxl_cstate;
> +    uint16_t ent;
> +    uint32_t base = 0;
> +    uint32_t len = 0 ;
> +    size_t rcount;
> +    struct cxl_cdat *req = pcie_doe_get_req(doe_cap);
> +
> +    ent = req->entry_handle;
> +    if (ent == 0) {
> +        len = sizeof(cxl_cstate->cdat_header);
> +    } else if (ent <= cxl_cstate->cdat_ent_len) {
> +        base = cxl_cstate->cdat_ent[ent - 1].base2;
> +        len = cxl_cstate->cdat_ent[ent - 1].length;
> +    }
> +
> +    struct cxl_cdat_rsp rsp = {
> +        .header = {
> +            .vendor_id = CXL_VENDOR_ID,
> +            .doe_type = CXL_DOE_TABLE_ACCESS,
> +            .reserved = 0x0,
> +            .length = (sizeof(struct cxl_cdat_rsp) + len) / 4,
> +        },
> +        .req_code = CXL_DOE_TAB_RSP,
> +        .table_type = CXL_DOE_TAB_TYPE_CDAT,
> +        .entry_handle = (ent < cxl_cstate->cdat_ent_len) ? ent + 1 : CXL_DOE_TAB_ENT_MAX,
> +    };
> +
> +    memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp));
> +
> +    if (ent == 0) {
> +        memcpy(doe_cap->read_mbox + sizeof(rsp) / 4,
> +               &cxl_cstate->cdat_header, len);
> +    } else if (ent <= cxl_cstate->cdat_ent_len) {
> +        fseek(cxl_cstate->cdat_file, base, SEEK_SET);
> +        rcount= fread(doe_cap->read_mbox + sizeof(rsp) / 4, len, 1, cxl_cstate->cdat_file);
> +	if (rcount == 0)
> +	    DOE_DBG("fread returned 0\n");
> +    }
> +
> +    doe_cap->read_mbox_len += rsp.header.length;
> +    DOE_DBG("%s: INCR RD_MBOX OFF=%x\n", __func__, doe_cap->read_mbox_len);
> +
> +    for (int i = 0; i < doe_cap->read_mbox_len; i++) {
> +        DOE_DBG("  RD_MBOX[%d]=%08x\n", i, doe_cap->read_mbox[i]);
> +    }

It's trivial to sanity check this isn't broken by just doing the read, so
I'd drop the debug now you have it working.

> +
> +    return true;
> +}
> +


>  
>  static void build_dvsecs(CXLType3Dev *ct3d)
>  {
> @@ -134,6 +284,8 @@ static void ct3_finalize(Object *obj)
>  
>      g_free((void *)regs->special_ops);
>      g_free(ct3d->cxl_dstate.pmem);
> +
> +    fclose(cxl_cstate->cdat_file);

cdat will be small, perhaps we just read it in once and cache it?
That lets us not keep the file handle around or the file open once we have read it.

>  }
>  
>  static void cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
> @@ -203,6 +355,19 @@ static MemoryRegion *cxl_md_get_memory_region(MemoryDeviceState *md,
>      return ct3d->cxl_dstate.pmem;
>  }
>  
> +static DOEProtocol doe_comp_prot[] = {
> +    {PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_cma_rsp},
> +    {CXL_VENDOR_ID, CXL_DOE_COMPLIANCE, cxl_doe_compliance_rsp},
> +    {CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp},

This is kind of useful for exercising software paths, but I'm not sure
why any real hardware would do this.

Generic software is likely to stop at the first DOE that supports what
it wants, so it'll never reach the CDAT only one.

> +    { /* End of array */ },
> +};
> +
> +static DOEProtocol doe_cdat_prot[] = {
> +    {PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_cma_rsp},

Not allowed to do that according to CMA ECN.

> +    {CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp},
> +    { /* End of array */ },
> +};
> +
>  static void ct3_realize(PCIDevice *pci_dev, Error **errp)
>  {
>      CXLType3Dev *ct3d = CT3(pci_dev);
> @@ -210,6 +375,8 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
>      ComponentRegisters *regs = &cxl_cstate->crb;
>      MemoryRegion *mr = &regs->component_registers;
>      uint8_t *pci_conf = pci_dev->config;
> +    unsigned short msix_num = 2;
> +    int i;
>  
>      if (!ct3d->cxl_dstate.pmem) {
>          cxl_setup_memory(ct3d, errp);
> @@ -239,6 +406,19 @@ 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);
> +
> +    /* MSI(-X) Initailization */
> +    msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL);
> +    for (i = 0; i < msix_num; i++) {
> +        msix_vector_use(pci_dev, i);
> +    }
> +    /* msi_init(pci_dev, 0x60, 16, true, false, NULL); */
> +
> +    /* DOE Initailization */
> +    pcie_doe_init(pci_dev, &ct3d->doe_comp, 0x160, doe_comp_prot, true, 0);
> +    pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 1);
> +
> +    cxl_doe_cdat_init(cxl_cstate, errp);
>  }
>  
>  static uint64_t cxl_md_get_addr(const MemoryDeviceState *md)
> @@ -275,6 +455,7 @@ static Property ct3_props[] = {
>                       HostMemoryBackend *),
>      DEFINE_PROP_LINK("lsa", CXLType3Dev, lsa, TYPE_MEMORY_BACKEND,
>                       HostMemoryBackend *),
> +    DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat_filename),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>  
> @@ -357,6 +538,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/include/hw/cxl/cxl_cdat.h b/include/hw/cxl/cxl_cdat.h
> new file mode 100644
> index 0000000000..edd994d410
> --- /dev/null
> +++ b/include/hw/cxl/cxl_cdat.h
> @@ -0,0 +1,127 @@

...

> +
> +/* Read Entry Request, 8.1.11.1 Table 134 */
> +struct cxl_cdat {
> +    DOEHeader header;
> +    uint8_t req_code;
> +    uint8_t table_type;
> +    uint16_t entry_handle;
> +} QEMU_PACKED;
> +
> +/* Read Entry Response, 8.1.11.1 Table 135 */
> +#define cxl_cdat_rsp    cxl_cdat    /* Same as request */

The header is the same as the request, but lots of data that this
is implying doesn't exist.  Also it doesn't have a request code
so at very least just make that field code in the structure.


> +
> +/*
> + * CDAT Table Structure (CDAT Spec)
> + */
> +#define CXL_CDAT_REV 1
> +
> +/* 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 */

No.  CDAT specification revision number. This isn't an ACPI table.

> +    uint8_t checksum;   /* To make sum of entire table == 0 */
> +    uint8_t reserved[6];
> +    uint32_t sequence;  /* ASCII table signature */

Not what the sequence number is at all.


> +} QEMU_PACKED;
> +


...

> +#endif /* CXL_CDAT_H */
> diff --git a/include/hw/cxl/cxl_compl.h b/include/hw/cxl/cxl_compl.h
> new file mode 100644
> index 0000000000..5edeaa5f27
> --- /dev/null
> +++ b/include/hw/cxl/cxl_compl.h
> @@ -0,0 +1,252 @@
> +/*
> + * CXL Compliance Structure
> + *
> + * Copyright (C) 2020 Avery Design Systems, Inc.

Time for an update to include 2021.

> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */

...

> +
> +struct status_rsp {
> +    DOEHeader header;
> +    CompRspHeader rsp_header;
> +    uint8_t status;
> +} QEMU_PACKED;
> +
> +struct len_rsvd_rsp {

Prefix these so they don't look like generic things.
Also for this one, the naming made me wonder if CXL really had defined
a length field then marked it reserved (which would be odd). 
I would go with two versions of the header and name this differently.

struct compliance_rsp_header_no_len {
    uint8_t rsp_code;
    uint8_t version;
} QEMU_PACKED;

struct comp_no_data_resp {
   DOEHeader header;
   struct compliance_rsp_header_no_len resp_header; /* typedefs if you like are fine */
   uint8_t reserved[6];
} QEMU_PACKED;

> +    DOEHeader header;
> +    /* The length field in rsp_header is reserved. */
> +    CompRspHeader rsp_header;
> +    uint8_t reserved[5];
> +} QEMU_PACKED;
> +
...

> +
> +/* 14.16.4.10 */
> +struct cxl_compliance_mode_toggle_cache_flush {
> +    DOEHeader header;
> +    CompReqHeader req_header;
> +    uint8_t inj_flow_control;

?

> +} QEMU_PACKED;
> +
> +#define cxl_compliance_mode_toggle_cache_flush_rsp status_rsp
> +

...

> +
> +#endif /* CXL_COMPL_H */
> diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h
> index acc0730d96..1294d74d78 100644
> --- a/include/hw/cxl/cxl_component.h
> +++ b/include/hw/cxl/cxl_component.h
> @@ -18,6 +18,7 @@
>  #include "qemu/range.h"
>  #include "qemu/typedefs.h"
>  #include "hw/register.h"
> +#include "qapi/error.h"
>  
>  enum reg_type {
>      CXL2_DEVICE,
> @@ -160,6 +161,11 @@ typedef struct component_registers {
>      MemoryRegionOps *special_ops;
>  } ComponentRegisters;
>  
> +typedef struct cdat_struct {
> +    void *base;
> +    uint32_t base2;

Given base vs base2 isn't obvious naming, add some comments or
give it a more meaningful name.

> +    uint32_t length;
> +} CDATStruct;
>  /*
>   * A CXL component represents all entities in a CXL hierarchy. This includes,
>   * host bridges, root ports, upstream/downstream switch ports, and devices
> @@ -173,6 +179,70 @@ 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
> +     */

Comment doesn't seem to have anything to do with any code around here, so drop it.

> +
> +    union doe_request_u {
> +        /* Compliance DOE Data Objects Type=0*/
Space before */
> +        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_inject_bit_error ignore_bit_error;
> +        char data_byte[128];

We aren't accessing this as a byte anywhere, so just use a pointer
to the whole structure.  That will avoid padding this to a meaningless
length. (closer inspection this one isn't even referenced, but resp below is)

> +    } doe_request;
> +
> +    union doe_resp_u {
> +        /* Compliance DOE Data Objects Type=0*/

Space before */

> +        struct cxl_compliance_mode_cap_rsp cap_rsp;

I wonder if we can make this all shorter by using an abbreviation?

cxl_cmpl_mode_stat_rsp

and similar?

It's nice to match spec names where possible, but sometimes they just
end up as stupidly long/

> +        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_inject_bit_error_rsp ignore_bit_error_rsp;
> +        char data_byte[520 * 4];

As above, I can't see a reason to have this.  If individual structures can be
variable length, put the maximums in them, not here.

> +    } doe_resp;
> +
> +    /* Table entry types */
> +    struct cdat_table_header cdat_header;
> +
> +    CDATStruct *cdat_ent;
> +    int cdat_ent_len;
> +
> +    char *cdat_filename;
> +    FILE *cdat_file;
>  } CXLComponentState;
>  
>  void cxl_component_register_block_init(Object *obj,
> @@ -184,4 +254,8 @@ 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);
>  
> +uint32_t cxl_doe_compliance_init(DOECap *doe_cap);
> +bool cxl_doe_compliance_rsp(DOECap *doe_cap);
> +void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp);
> +bool cxl_doe_cdat_rsp(DOECap *doe_cap);
>  #endif
> diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
> index 057c5b85c6..de006ff463 100644
> --- a/include/hw/cxl/cxl_device.h
> +++ b/include/hw/cxl/cxl_device.h
> @@ -236,6 +236,10 @@ typedef struct cxl_type3_dev {
>      /* State */
>      CXLComponentState cxl_cstate;
>      CXLDeviceState cxl_dstate;
> +
> +    /* DOE */
> +    DOECap doe_comp;
> +    DOECap doe_cdat;
>  } CXLType3Dev;
>  
>  #ifndef TYPE_CXL_TYPE3_DEV
> diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h
> index e8235b10cc..a0df324a1f 100644
> --- a/include/hw/cxl/cxl_pci.h
> +++ b/include/hw/cxl/cxl_pci.h
> @@ -12,6 +12,8 @@
>  
>  #include "hw/pci/pci.h"
>  #include "hw/pci/pcie.h"
> +#include "hw/cxl/cxl_cdat.h"
> +#include "hw/cxl/cxl_compl.h"
>  
>  #define CXL_VENDOR_ID 0x1e98
>
diff mbox series

Patch

diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c
index 41d36f802a..1a2408dc76 100644
--- a/hw/cxl/cxl-component-utils.c
+++ b/hw/cxl/cxl-component-utils.c
@@ -11,6 +11,7 @@ 
 #include "qemu/log.h"
 #include "hw/pci/pci.h"
 #include "hw/cxl/cxl.h"
+#include "qapi/error.h"
 
 static uint64_t cxl_cache_mem_read_reg(void *opaque, hwaddr offset,
                                        unsigned size)
@@ -206,3 +207,95 @@  void cxl_component_create_dvsec(CXLComponentState *cxl, uint16_t length,
     range_init_nofail(&cxl->dvsecs[type], cxl->dvsec_offset, length);
     cxl->dvsec_offset += length;
 }
+
+static void cdat_len_check(struct cdat_sub_header *hdr, Error **errp)
+{
+    assert(hdr->length);
+    assert(hdr->reserved == 0);
+
+    switch (hdr->type) {
+    case CDAT_TYPE_DSMAS:
+        assert(hdr->length == sizeof(struct cdat_dsmas));
+        break;
+    case CDAT_TYPE_DSLBIS:
+        assert(hdr->length == sizeof(struct cdat_dslbis));
+        break;
+    case CDAT_TYPE_DSMSCIS:
+        assert(hdr->length == sizeof(struct cdat_dsmscis));
+        break;
+    case CDAT_TYPE_DSIS:
+        assert(hdr->length == sizeof(struct cdat_dsis));
+        break;
+    case CDAT_TYPE_DSEMTS:
+        assert(hdr->length == sizeof(struct cdat_dsemts));
+        break;
+    case CDAT_TYPE_SSLBIS:
+        assert(hdr->length >= sizeof(struct cdat_sslbis_header));
+        assert((hdr->length - sizeof(struct cdat_sslbis_header)) %
+               sizeof(struct cdat_sslbe) == 0);
+        break;
+    default:
+        error_setg(errp, "Type %d is reserved", hdr->type);
+    }
+}
+
+#define IASL_HEADER_LEN 0x24
+void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp)
+{
+    uint8_t sum = 0;
+    int i, curr = 0, ent = 0;
+    CDATStruct cdat_st[1024];
+    struct cdat_sub_header hdr;
+    FILE *fp;
+    uint8_t iasl_hdr[IASL_HEADER_LEN];
+    size_t rcount;
+
+    fp = fopen(cxl_cstate->cdat_filename, "r");
+
+    if (fp) {
+        /* Read iASL header */
+        rcount= fread(&iasl_hdr, sizeof(iasl_hdr), 1, fp);
+	if (rcount == 0)
+            error_setg(errp, "File read failed");
+	    
+
+        for (i = 0; i < IASL_HEADER_LEN; i++) {
+            sum += iasl_hdr[i];
+        }
+        sum = (~sum + 1) & 0xFF;
+
+        curr = ftell(fp);
+
+        /* Read CDAT structures */
+        while (fread(&hdr, sizeof(hdr), 1, fp)) {
+            cdat_len_check(&hdr, errp);
+
+            cdat_st[ent].base2 = curr;
+            cdat_st[ent].length = hdr.length;
+            ent++;
+
+            fseek(fp, curr + hdr.length, SEEK_SET);
+            curr = ftell(fp);
+        }
+        /* Check the last structure */
+        fseek(fp, 0, SEEK_END);
+        assert(curr == ftell(fp));
+    } else {
+        error_setg(errp, "Please specify the CDAT file by using ',cdat='");
+    }
+    cxl_cstate->cdat_file = fp;
+
+    cxl_cstate->cdat_ent_len = ent;
+    cxl_cstate->cdat_ent = g_malloc0(sizeof(CDATStruct) * ent);
+    memcpy(cxl_cstate->cdat_ent, cdat_st, sizeof(CDATStruct) * ent);
+
+    /* Set CDAT header, ent = 0 */
+    cxl_cstate->cdat_header.revision = CXL_CDAT_REV;
+    cxl_cstate->cdat_header.sequence = 0;
+    cxl_cstate->cdat_header.length += curr + sizeof(cxl_cstate->cdat_header);
+
+    sum += cxl_cstate->cdat_header.revision +
+           cxl_cstate->cdat_header.sequence + cxl_cstate->cdat_header.length;
+
+    cxl_cstate->cdat_header.checksum = ~sum + 1;
+}
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index bf33ddb915..33f571e1a5 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -13,6 +13,156 @@ 
 #include "qemu/rcu.h"
 #include "sysemu/hostmem.h"
 #include "hw/cxl/cxl.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/msix.h"
+
+uint32_t cxl_doe_compliance_init(DOECap *doe_cap)
+{
+    CXLComponentState *cxl_cstate = &CT3(doe_cap->pdev)->cxl_cstate;
+    uint32_t req;
+    uint32_t byte_cnt = 0;
+
+    DOE_DBG(">> %s\n",  __func__);
+
+    req = ((struct cxl_compliance_mode_cap *)pcie_doe_get_req(doe_cap))
+        ->req_header.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 =
+            DIV_ROUND_UP(sizeof(struct cxl_compliance_mode_cap_rsp), 4);
+        cxl_cstate->doe_resp.cap_rsp.rsp_header.rsp_code = 0x0;
+        cxl_cstate->doe_resp.cap_rsp.rsp_header.version = 0x1;
+        cxl_cstate->doe_resp.cap_rsp.rsp_header.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 =
+            DIV_ROUND_UP(sizeof(struct cxl_compliance_mode_status_rsp), 4);
+        cxl_cstate->doe_resp.status_rsp.rsp_header.rsp_code = 0x1;
+        cxl_cstate->doe_resp.status_rsp.rsp_header.version = 0x1;
+        cxl_cstate->doe_resp.status_rsp.rsp_header.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;
+}
+
+
+bool cxl_doe_compliance_rsp(DOECap *doe_cap)
+{
+    CXLComponentState *cxl_cstate = &CT3(doe_cap->pdev)->cxl_cstate;
+    uint32_t byte_cnt;
+    uint32_t dw_cnt;
+
+    DOE_DBG(">> %s\n",  __func__);
+
+    byte_cnt = cxl_doe_compliance_init(doe_cap);
+    dw_cnt = byte_cnt / 4;
+    memcpy(doe_cap->read_mbox,
+           cxl_cstate->doe_resp.data_byte, byte_cnt);
+
+    doe_cap->read_mbox_len += dw_cnt;
+
+    DOE_DBG("  LEN=%x, RD MBOX[%d]=%x\n", dw_cnt - 1,
+            doe_cap->read_mbox_len,
+            *(doe_cap->read_mbox + dw_cnt - 1));
+
+    DOE_DBG("<< %s\n",  __func__);
+
+    return true;
+}
+
+bool cxl_doe_cdat_rsp(DOECap *doe_cap)
+{
+    CXLComponentState *cxl_cstate = &CT3(doe_cap->pdev)->cxl_cstate;
+    uint16_t ent;
+    uint32_t base = 0;
+    uint32_t len = 0 ;
+    size_t rcount;
+    struct cxl_cdat *req = pcie_doe_get_req(doe_cap);
+
+    ent = req->entry_handle;
+    if (ent == 0) {
+        len = sizeof(cxl_cstate->cdat_header);
+    } else if (ent <= cxl_cstate->cdat_ent_len) {
+        base = cxl_cstate->cdat_ent[ent - 1].base2;
+        len = cxl_cstate->cdat_ent[ent - 1].length;
+    }
+
+    struct cxl_cdat_rsp rsp = {
+        .header = {
+            .vendor_id = CXL_VENDOR_ID,
+            .doe_type = CXL_DOE_TABLE_ACCESS,
+            .reserved = 0x0,
+            .length = (sizeof(struct cxl_cdat_rsp) + len) / 4,
+        },
+        .req_code = CXL_DOE_TAB_RSP,
+        .table_type = CXL_DOE_TAB_TYPE_CDAT,
+        .entry_handle = (ent < cxl_cstate->cdat_ent_len) ? ent + 1 : CXL_DOE_TAB_ENT_MAX,
+    };
+
+    memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp));
+
+    if (ent == 0) {
+        memcpy(doe_cap->read_mbox + sizeof(rsp) / 4,
+               &cxl_cstate->cdat_header, len);
+    } else if (ent <= cxl_cstate->cdat_ent_len) {
+        fseek(cxl_cstate->cdat_file, base, SEEK_SET);
+        rcount= fread(doe_cap->read_mbox + sizeof(rsp) / 4, len, 1, cxl_cstate->cdat_file);
+	if (rcount == 0)
+	    DOE_DBG("fread returned 0\n");
+    }
+
+    doe_cap->read_mbox_len += rsp.header.length;
+    DOE_DBG("%s: INCR RD_MBOX OFF=%x\n", __func__, doe_cap->read_mbox_len);
+
+    for (int i = 0; i < doe_cap->read_mbox_len; i++) {
+        DOE_DBG("  RD_MBOX[%d]=%08x\n", i, doe_cap->read_mbox[i]);
+    }
+
+    return true;
+}
+
+static uint32_t ct3d_config_read(PCIDevice *pci_dev, uint32_t addr, int size)
+{
+    CXLType3Dev *ct3d = CT3(pci_dev);
+    uint32_t val;
+
+    if (pcie_doe_read_config(&ct3d->doe_comp, addr, size, &val)) {
+        return val;
+    } else if (pcie_doe_read_config(&ct3d->doe_cdat, addr, size, &val)) {
+        return val;
+    }
+
+    return pci_default_read_config(pci_dev, addr, size);
+}
+
+static void ct3d_config_write(PCIDevice *pci_dev, uint32_t addr, uint32_t val,
+                              int size)
+{
+    CXLType3Dev *ct3d = CT3(pci_dev);
+
+    pcie_doe_write_config(&ct3d->doe_comp, addr, val, size);
+    pcie_doe_write_config(&ct3d->doe_cdat, addr, val, size);
+    pci_default_write_config(pci_dev, addr, val, size);
+}
 
 static void build_dvsecs(CXLType3Dev *ct3d)
 {
@@ -134,6 +284,8 @@  static void ct3_finalize(Object *obj)
 
     g_free((void *)regs->special_ops);
     g_free(ct3d->cxl_dstate.pmem);
+
+    fclose(cxl_cstate->cdat_file);
 }
 
 static void cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
@@ -203,6 +355,19 @@  static MemoryRegion *cxl_md_get_memory_region(MemoryDeviceState *md,
     return ct3d->cxl_dstate.pmem;
 }
 
+static DOEProtocol doe_comp_prot[] = {
+    {PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_cma_rsp},
+    {CXL_VENDOR_ID, CXL_DOE_COMPLIANCE, cxl_doe_compliance_rsp},
+    {CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp},
+    { /* End of array */ },
+};
+
+static DOEProtocol doe_cdat_prot[] = {
+    {PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_cma_rsp},
+    {CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp},
+    { /* End of array */ },
+};
+
 static void ct3_realize(PCIDevice *pci_dev, Error **errp)
 {
     CXLType3Dev *ct3d = CT3(pci_dev);
@@ -210,6 +375,8 @@  static void ct3_realize(PCIDevice *pci_dev, Error **errp)
     ComponentRegisters *regs = &cxl_cstate->crb;
     MemoryRegion *mr = &regs->component_registers;
     uint8_t *pci_conf = pci_dev->config;
+    unsigned short msix_num = 2;
+    int i;
 
     if (!ct3d->cxl_dstate.pmem) {
         cxl_setup_memory(ct3d, errp);
@@ -239,6 +406,19 @@  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);
+
+    /* MSI(-X) Initailization */
+    msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL);
+    for (i = 0; i < msix_num; i++) {
+        msix_vector_use(pci_dev, i);
+    }
+    /* msi_init(pci_dev, 0x60, 16, true, false, NULL); */
+
+    /* DOE Initailization */
+    pcie_doe_init(pci_dev, &ct3d->doe_comp, 0x160, doe_comp_prot, true, 0);
+    pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 1);
+
+    cxl_doe_cdat_init(cxl_cstate, errp);
 }
 
 static uint64_t cxl_md_get_addr(const MemoryDeviceState *md)
@@ -275,6 +455,7 @@  static Property ct3_props[] = {
                      HostMemoryBackend *),
     DEFINE_PROP_LINK("lsa", CXLType3Dev, lsa, TYPE_MEMORY_BACKEND,
                      HostMemoryBackend *),
+    DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat_filename),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -357,6 +538,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/include/hw/cxl/cxl_cdat.h b/include/hw/cxl/cxl_cdat.h
new file mode 100644
index 0000000000..edd994d410
--- /dev/null
+++ b/include/hw/cxl/cxl_cdat.h
@@ -0,0 +1,127 @@ 
+/*
+ * CXL CDAT Structure
+ *
+ * Copyright (C) 2020 Avery Design Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef CXL_CDAT_H
+#define CXL_CDAT_H
+
+#include "hw/cxl/cxl_pci.h"
+
+#define CXL_DOE_TABLE_ACCESS      2
+#define CXL_DOE_PROTOCOL_CDAT     ((CXL_DOE_TABLE_ACCESS << 16) | CXL_VENDOR_ID)
+
+/*
+ * DOE CDAT Table Protocol (CXL Spec)
+ */
+#define CXL_DOE_TAB_REQ 0
+#define CXL_DOE_TAB_RSP 0
+#define CXL_DOE_TAB_TYPE_CDAT 0
+#define CXL_DOE_TAB_ENT_MAX 0xFFFF
+
+/* Read Entry Request, 8.1.11.1 Table 134 */
+struct cxl_cdat {
+    DOEHeader header;
+    uint8_t req_code;
+    uint8_t table_type;
+    uint16_t entry_handle;
+} QEMU_PACKED;
+
+/* Read Entry Response, 8.1.11.1 Table 135 */
+#define cxl_cdat_rsp    cxl_cdat    /* Same as request */
+
+/*
+ * CDAT Table Structure (CDAT Spec)
+ */
+#define CXL_CDAT_REV 1
+
+/* 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 */
+    uint8_t reserved[6];
+    uint32_t sequence;  /* ASCII table signature */
+} QEMU_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_TYPE_MAX
+};
+
+struct cdat_sub_header {
+    uint8_t type;
+    uint8_t reserved;
+    uint16_t length;
+};
+
+/* CDAT Structure Subtables */
+struct cdat_dsmas {
+    struct cdat_sub_header header;
+    uint8_t DSMADhandle;
+    uint8_t flags;
+    uint16_t reserved;
+    uint64_t DPA_base;
+    uint64_t DPA_length;
+} QEMU_PACKED;
+
+struct cdat_dslbis {
+    struct cdat_sub_header header;
+    uint8_t handle;
+    uint8_t flags;
+    uint8_t data_type;
+    uint8_t reserved;
+    uint64_t entry_base_unit;
+    uint16_t entry[3];
+    uint16_t reserved2;
+} QEMU_PACKED;
+
+struct cdat_dsmscis {
+    struct cdat_sub_header header;
+    uint8_t DSMAS_handle;
+    uint8_t reserved[3];
+    uint64_t memory_side_cache_size;
+    uint32_t cache_attributes;
+} QEMU_PACKED;
+
+struct cdat_dsis {
+    struct cdat_sub_header header;
+    uint8_t flags;
+    uint8_t handle;
+    uint16_t reserved;
+} QEMU_PACKED;
+
+struct cdat_dsemts {
+    struct cdat_sub_header header;
+    uint8_t DSMAS_handle;
+    uint8_t EFI_memory_type_attr;
+    uint16_t reserved;
+    uint64_t DPA_offset;
+    uint64_t DPA_length;
+} QEMU_PACKED;
+
+struct cdat_sslbe {
+    uint16_t port_x_id;
+    uint16_t port_y_id;
+    uint16_t latency_bandwidth;
+    uint16_t reserved;
+} QEMU_PACKED;
+
+struct cdat_sslbis_header {
+    struct cdat_sub_header header;
+    uint8_t data_type;
+    uint8_t reserved[3];
+    uint64_t entry_base_unit;
+} QEMU_PACKED;
+
+#endif /* CXL_CDAT_H */
diff --git a/include/hw/cxl/cxl_compl.h b/include/hw/cxl/cxl_compl.h
new file mode 100644
index 0000000000..5edeaa5f27
--- /dev/null
+++ b/include/hw/cxl/cxl_compl.h
@@ -0,0 +1,252 @@ 
+/*
+ * CXL Compliance Structure
+ *
+ * Copyright (C) 2020 Avery Design Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef CXL_COMPL_H
+#define CXL_COMPL_H
+
+#include "hw/cxl/cxl_pci.h"
+
+#define CXL_DOE_COMPLIANCE        0
+#define CXL_DOE_PROTOCOL_COMPLIANCE ((CXL_DOE_COMPLIANCE << 16) | CXL_VENDOR_ID)
+
+/* 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 compliance_req_header CompReqHeader;
+typedef struct compliance_rsp_header CompRspHeader;
+
+struct compliance_req_header {
+    uint8_t req_code;
+    uint8_t version;
+    uint16_t reserved;
+} QEMU_PACKED;
+
+struct compliance_rsp_header {
+    uint8_t rsp_code;
+    uint8_t version;
+    uint8_t length;
+} QEMU_PACKED;
+
+struct status_rsp {
+    DOEHeader header;
+    CompRspHeader rsp_header;
+    uint8_t status;
+} QEMU_PACKED;
+
+struct len_rsvd_rsp {
+    DOEHeader header;
+    /* The length field in rsp_header is reserved. */
+    CompRspHeader rsp_header;
+    uint8_t reserved[5];
+} QEMU_PACKED;
+
+/*
+ * CXL Compliance Mode Protocol 14.16.4
+ */
+/* 14.16.4.1 */
+struct cxl_compliance_mode_cap {
+    DOEHeader header;
+    CompReqHeader req_header;
+} QEMU_PACKED;
+
+struct cxl_compliance_mode_cap_rsp {
+    DOEHeader header;
+    CompRspHeader rsp_header;
+    uint8_t status;
+    uint64_t available_cap_bitmask;
+    uint64_t enabled_cap_bitmask;
+} QEMU_PACKED;
+
+/* 14.16.4.2 */
+struct cxl_compliance_mode_status {
+    DOEHeader header;
+    CompReqHeader req_header;
+} QEMU_PACKED;
+
+struct cxl_compliance_mode_status_rsp {
+    DOEHeader header;
+    CompRspHeader rsp_header;
+    uint32_t cap_bitfield;
+    uint16_t cache_size;
+    uint8_t cache_size_units;
+} QEMU_PACKED;
+
+/* 14.16.4.3 */
+struct cxl_compliance_mode_halt {
+    DOEHeader header;
+    CompReqHeader req_header;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_halt_rsp status_rsp
+
+/* 14.16.4.4 */
+struct cxl_compliance_mode_multiple_write_streaming {
+    DOEHeader header;
+    CompReqHeader req_header;
+    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;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_multiple_write_streaming_rsp status_rsp
+
+/* 14.16.4.5 */
+struct cxl_compliance_mode_producer_consumer {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t protocol;
+    uint8_t num_inc;
+    uint8_t num_sets;
+    uint8_t num_loops;
+    uint8_t write_semantics;
+    uint8_t reserved2[3];
+    uint64_t start_addr;
+    uint64_t byte_mask;
+    uint32_t addr_incr;
+    uint32_t set_offset;
+    uint32_t pattern;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_producer_consumer_rsp status_rsp
+
+/* 14.16.4.6 */
+struct cxl_compliance_mode_inject_bogus_writes {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t count;
+    uint8_t reserved;
+    uint32_t pattern;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_bogus_writes_rsp status_rsp
+
+/* 14.16.4.7 */
+struct cxl_compliance_mode_inject_poison {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t protocol;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_poison_rsp status_rsp
+
+/* 14.16.4.8 */
+struct cxl_compliance_mode_inject_crc {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t num_bits_flip;
+    uint8_t num_flits_inj;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_crc_rsp status_rsp
+
+/* 14.16.4.9 */
+struct cxl_compliance_mode_inject_flow_control {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t inj_flow_control;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_flow_control_rsp status_rsp
+
+/* 14.16.4.10 */
+struct cxl_compliance_mode_toggle_cache_flush {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t inj_flow_control;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_toggle_cache_flush_rsp status_rsp
+
+/* 14.16.4.11 */
+struct cxl_compliance_mode_inject_mac_delay {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t enable;
+    uint8_t mode;
+    uint8_t delay;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_mac_delay_rsp status_rsp
+
+/* 14.16.4.12 */
+struct cxl_compliance_mode_insert_unexp_mac {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t opcode;
+    uint8_t mode;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_insert_unexp_mac_rsp status_rsp
+
+/* 14.16.4.13 */
+struct cxl_compliance_mode_inject_viral {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t protocol;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_viral_rsp status_rsp
+
+/* 14.16.4.14 */
+struct cxl_compliance_mode_inject_almp {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t opcode;
+    uint8_t reserved2[3];
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_almp_rsp len_rsvd_rsp
+
+/* 14.16.4.15 */
+struct cxl_compliance_mode_ignore_almp {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t opcode;
+    uint8_t reserved2[3];
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_ignore_almp_rsp len_rsvd_rsp
+
+/* 14.16.4.16 */
+struct cxl_compliance_mode_inject_bit_error {
+    DOEHeader header;
+    CompReqHeader req_header;
+    uint8_t opcode;
+} QEMU_PACKED;
+
+#define cxl_compliance_mode_inject_bit_error_rsp len_rsvd_rsp
+
+#endif /* CXL_COMPL_H */
diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h
index acc0730d96..1294d74d78 100644
--- a/include/hw/cxl/cxl_component.h
+++ b/include/hw/cxl/cxl_component.h
@@ -18,6 +18,7 @@ 
 #include "qemu/range.h"
 #include "qemu/typedefs.h"
 #include "hw/register.h"
+#include "qapi/error.h"
 
 enum reg_type {
     CXL2_DEVICE,
@@ -160,6 +161,11 @@  typedef struct component_registers {
     MemoryRegionOps *special_ops;
 } ComponentRegisters;
 
+typedef struct cdat_struct {
+    void *base;
+    uint32_t base2;
+    uint32_t length;
+} CDATStruct;
 /*
  * A CXL component represents all entities in a CXL hierarchy. This includes,
  * host bridges, root ports, upstream/downstream switch ports, and devices
@@ -173,6 +179,70 @@  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
+     */
+
+    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_inject_bit_error ignore_bit_error;
+        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_inject_bit_error_rsp ignore_bit_error_rsp;
+        char data_byte[520 * 4];
+    } doe_resp;
+
+    /* Table entry types */
+    struct cdat_table_header cdat_header;
+
+    CDATStruct *cdat_ent;
+    int cdat_ent_len;
+
+    char *cdat_filename;
+    FILE *cdat_file;
 } CXLComponentState;
 
 void cxl_component_register_block_init(Object *obj,
@@ -184,4 +254,8 @@  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);
 
+uint32_t cxl_doe_compliance_init(DOECap *doe_cap);
+bool cxl_doe_compliance_rsp(DOECap *doe_cap);
+void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp);
+bool cxl_doe_cdat_rsp(DOECap *doe_cap);
 #endif
diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
index 057c5b85c6..de006ff463 100644
--- a/include/hw/cxl/cxl_device.h
+++ b/include/hw/cxl/cxl_device.h
@@ -236,6 +236,10 @@  typedef struct cxl_type3_dev {
     /* State */
     CXLComponentState cxl_cstate;
     CXLDeviceState cxl_dstate;
+
+    /* DOE */
+    DOECap doe_comp;
+    DOECap doe_cdat;
 } CXLType3Dev;
 
 #ifndef TYPE_CXL_TYPE3_DEV
diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h
index e8235b10cc..a0df324a1f 100644
--- a/include/hw/cxl/cxl_pci.h
+++ b/include/hw/cxl/cxl_pci.h
@@ -12,6 +12,8 @@ 
 
 #include "hw/pci/pci.h"
 #include "hw/pci/pcie.h"
+#include "hw/cxl/cxl_cdat.h"
+#include "hw/cxl/cxl_compl.h"
 
 #define CXL_VENDOR_ID 0x1e98