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


Reply via email to