From: Zhi Wang <[email protected]> cxl_component_create_dvsec() sets the DVSEC registers once, at realize, and nothing resets them. DVSEC Control Mem_Enable then stays 1 across a system reset. The Linux CXL core takes that as HDM decoders set up by firmware, uses whatever the decoders hold, and fails to probe the device.
Add cxl_component_update_dvsec() so that a device can rewrite its DVSEC registers at reset. Signed-off-by: Zhi Wang <[email protected]> Signed-off-by: Cédric Le Goater <[email protected]> [ mhonap: comment and indentation fixes, commit message update ] Signed-off-by: Manish Honap <[email protected]> --- hw/cxl/cxl-component-utils.c | 33 +++++++++++++++++++++++++++------ include/hw/cxl/cxl_component.h | 3 +++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 5db57f7394..bb4c3f541d 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -490,9 +490,10 @@ void cxl_component_register_init_common(uint32_t *reg_state, * Helper to creates a DVSEC header for a CXL entity. The caller is responsible * for tracking the valid offset. * - * This function will build the DVSEC header on behalf of the caller and then - * copy in the remaining data for the vendor specific bits. - * It will also set up appropriate write masks. + * This function will build the DVSEC header on behalf of the caller and set + * up appropriate write masks. With @body it also copies in the remaining + * data; otherwise the caller fills it in later with + * cxl_component_update_dvsec(). */ void cxl_component_create_dvsec(CXLComponentState *cxl, enum reg_type cxl_dev_type, uint16_t length, @@ -512,9 +513,12 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, pci_set_long(pdev->config + offset + PCIE_DVSEC_HEADER1_OFFSET, (length << 20) | (rev << 16) | CXL_VENDOR_ID); pci_set_word(pdev->config + offset + PCIE_DVSEC_ID_OFFSET, type); - memcpy(pdev->config + offset + sizeof(DVSECHeader), - body + sizeof(DVSECHeader), - length - sizeof(DVSECHeader)); + + if (body) { + memcpy(pdev->config + offset + sizeof(DVSECHeader), + body + sizeof(DVSECHeader), + length - sizeof(DVSECHeader)); + } /* Configure write masks */ switch (type) { @@ -606,6 +610,23 @@ void cxl_component_create_dvsec(CXLComponentState *cxl, cxl->dvsec_offset += length; } +void cxl_component_update_dvsec(CXLComponentState *cxl, uint16_t length, + uint16_t type, uint8_t *body) +{ + PCIDevice *pdev = cxl->pdev; + struct Range *r; + + assert(type < CXL20_MAX_DVSEC); + + r = &cxl->dvsecs[type]; + + assert(range_size(r) == length); + + memcpy(pdev->config + r->lob + sizeof(DVSECHeader), + body + sizeof(DVSECHeader), + length - sizeof(DVSECHeader)); +} + /* CXL r3.1 Section 8.2.4.20.7 CXL HDM Decoder n Control Register */ uint8_t cxl_interleave_ways_enc(int iw, Error **errp) { diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index 7929a39b73..d6fdb6c1ab 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -320,6 +320,9 @@ void cxl_component_create_dvsec(CXLComponentState *cxl_cstate, enum reg_type cxl_dev_type, uint16_t length, uint16_t type, uint8_t rev, uint8_t *body); +void cxl_component_update_dvsec(CXLComponentState *cxl, uint16_t length, + uint16_t type, uint8_t *body); + int cxl_decoder_count_enc(int count); int cxl_decoder_count_dec(int enc_cnt); -- 2.53.0
