From: Manish Honap <[email protected]> QEMU has no firmware that commits the HDM decoders of a CXL route at boot, so a guest finds no committed region.
Let a cold-plugged device register one range with its pxb-cxl, which then commits HDM decoder 0 for it at every reset. A host bridge with a registered range keeps its decoder instead of passing accesses through. If the device resets first, pxb-cxl retries the direct mapping after the commit. The CXL Type-2 device registers a range later in this series. AI-used-for: code (prototype) Signed-off-by: Manish Honap <[email protected]> --- hw/cxl/cxl-component-utils.c | 47 +++++++++++++++++++++++++ hw/pci-bridge/pci_expander_bridge_cxl.c | 22 ++++++++++-- include/hw/cxl/cxl_component.h | 13 +++++++ include/hw/pci/pci_bridge.h | 5 +++ 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index f9bb1684fa..c61966b202 100644 --- a/hw/cxl/cxl-component-utils.c +++ b/hw/cxl/cxl-component-utils.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/pci/pci.h" #include "hw/cxl/cxl.h" @@ -500,6 +501,52 @@ void cxl_component_register_init_common(uint32_t *reg_state, CXL_HDM_DECODER_COUNT); } +/* One range per host bridge; registering the same range again succeeds */ +bool cxl_firmware_hdm_set(CXLFirmwareHDM *fw, const char *owner, + uint64_t base, uint64_t size, uint8_t target, + Error **errp) +{ + if (!size || !QEMU_IS_ALIGNED(base, 256 * MiB) || + !QEMU_IS_ALIGNED(size, 256 * MiB)) { + error_setg(errp, "firmware HDM range must be 256 MiB aligned"); + return false; + } + + if (fw->valid) { + if (fw->base == base && fw->size == size && fw->target == target) { + return true; + } + error_setg(errp, "%s supports one firmware HDM range", owner); + return false; + } + + *fw = (CXLFirmwareHDM) { + .valid = true, + .base = base, + .size = size, + .target = target, + }; + return true; +} + +/* + * The caller sets HDM Decoder Enable where it applies. It reads as 0 on host + * bridges (CXL r4.0 Section 8.2.4.20.2). + */ +void cxl_firmware_hdm_program(const CXLFirmwareHDM *fw, uint32_t *cache_mem) +{ + uint32_t ctrl = 0; + + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO, fw->base & 0xf0000000); + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI, fw->base >> 32); + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO, fw->size & 0xf0000000); + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI, fw->size >> 32); + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_TARGET_LIST_LO, fw->target); + ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMIT, 1); + ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED, 1); + stl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL, ctrl); +} + /* * Helper to creates a DVSEC header for a CXL entity. The caller is responsible * for tracking the valid offset. diff --git a/hw/pci-bridge/pci_expander_bridge_cxl.c b/hw/pci-bridge/pci_expander_bridge_cxl.c index ceb8928f8f..01fd479a9e 100644 --- a/hw/pci-bridge/pci_expander_bridge_cxl.c +++ b/hw/pci-bridge/pci_expander_bridge_cxl.c @@ -111,9 +111,18 @@ static const TypeInfo cxl_host_info = { .class_init = pxb_cxl_host_class_init, }; +bool pxb_cxl_register_firmware_hdm(PXBCXLDev *pxb, uint64_t base, + uint64_t size, uint8_t target, + Error **errp) +{ + return cxl_firmware_hdm_set(&pxb->firmware_hdm, "pxb-cxl", base, size, + target, errp); +} + static void pxb_cxl_dev_reset(DeviceState *dev) { - CXLHost *cxl = PXB_CXL_DEV(dev)->cxl_host_bridge; + PXBCXLDev *pxb = PXB_CXL_DEV(dev); + CXLHost *cxl = pxb->cxl_host_bridge; CXLComponentState *cxl_cstate = &cxl->cxl_cstate; PCIHostState *hb = PCI_HOST_BRIDGE(cxl); uint32_t *reg_state = cxl_cstate->crb.cache_mem_registers; @@ -121,11 +130,12 @@ static void pxb_cxl_dev_reset(DeviceState *dev) int dsp_count = 0; cxl_component_register_init_common(reg_state, write_msk, CXL2_RC, false); + cxl->passthrough = false; /* * The CXL specification allows for host bridges with no HDM decoders * if they only have a single root port. */ - if (!PXB_CXL_DEV(dev)->hdm_for_passthrough) { + if (!pxb->hdm_for_passthrough && !pxb->firmware_hdm.valid) { dsp_count = pcie_count_ds_ports(hb->bus); } /* Initial reset will have 0 dsp so wait until > 0 */ @@ -136,6 +146,14 @@ static void pxb_cxl_dev_reset(DeviceState *dev) } else { ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 8); + if (pxb->firmware_hdm.valid) { + cxl_firmware_hdm_program(&pxb->firmware_hdm, reg_state); + /* + * An endpoint that committed before this reset could not map + * its memory directly, as this decoder was not committed yet. + */ + cfmws_update_non_interleaved(true); + } } } diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index b38e81d129..eb71f1c80a 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -328,6 +328,19 @@ void cxl_component_create_dvsec(CXLComponentState *cxl_cstate, void cxl_component_update_dvsec(CXLComponentState *cxl, uint16_t length, uint16_t type, uint8_t *body); +/* The range that firmware routes through HDM decoder 0 to one port */ +typedef struct CXLFirmwareHDM { + bool valid; + uint64_t base; + uint64_t size; + uint8_t target; +} CXLFirmwareHDM; + +bool cxl_firmware_hdm_set(CXLFirmwareHDM *fw, const char *owner, + uint64_t base, uint64_t size, uint8_t target, + Error **errp); +void cxl_firmware_hdm_program(const CXLFirmwareHDM *fw, uint32_t *cache_mem); + int cxl_decoder_count_enc(int count); int cxl_decoder_count_dec(int enc_cnt); diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index f114f0a651..d0227a4c38 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -117,11 +117,16 @@ typedef struct PXBCXLDev { bool hdm_for_passthrough; CXLHost *cxl_host_bridge; /* Pointer to a CXLHost */ + CXLFirmwareHDM firmware_hdm; } PXBCXLDev; #define TYPE_PXB_CXL_DEV "pxb-cxl" OBJECT_DECLARE_SIMPLE_TYPE(PXBCXLDev, PXB_CXL_DEV) +bool pxb_cxl_register_firmware_hdm(PXBCXLDev *pxb, uint64_t base, + uint64_t size, uint8_t target, + Error **errp); + int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, uint16_t svid, uint16_t ssid, Error **errp); -- 2.53.0
