qemu/hw/mem/cxl_type3.c
<<
>>
Prefs
   1#include "qemu/osdep.h"
   2#include "qemu/units.h"
   3#include "qemu/error-report.h"
   4#include "hw/mem/memory-device.h"
   5#include "hw/mem/pc-dimm.h"
   6#include "hw/pci/pci.h"
   7#include "hw/qdev-properties.h"
   8#include "qapi/error.h"
   9#include "qemu/log.h"
  10#include "qemu/module.h"
  11#include "qemu/pmem.h"
  12#include "qemu/range.h"
  13#include "qemu/rcu.h"
  14#include "sysemu/hostmem.h"
  15#include "hw/cxl/cxl.h"
  16
  17static void build_dvsecs(CXLType3Dev *ct3d)
  18{
  19    CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
  20    uint8_t *dvsec;
  21
  22    dvsec = (uint8_t *)&(CXLDVSECDevice){
  23        .cap = 0x1e,
  24        .ctrl = 0x2,
  25        .status2 = 0x2,
  26        .range1_size_hi = ct3d->hostmem->size >> 32,
  27        .range1_size_lo = (2 << 5) | (2 << 2) | 0x3 |
  28        (ct3d->hostmem->size & 0xF0000000),
  29        .range1_base_hi = 0,
  30        .range1_base_lo = 0,
  31    };
  32    cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE,
  33                               PCIE_CXL_DEVICE_DVSEC_LENGTH,
  34                               PCIE_CXL_DEVICE_DVSEC,
  35                               PCIE_CXL2_DEVICE_DVSEC_REVID, dvsec);
  36
  37    dvsec = (uint8_t *)&(CXLDVSECRegisterLocator){
  38        .rsvd         = 0,
  39        .reg0_base_lo = RBI_COMPONENT_REG | CXL_COMPONENT_REG_BAR_IDX,
  40        .reg0_base_hi = 0,
  41        .reg1_base_lo = RBI_CXL_DEVICE_REG | CXL_DEVICE_REG_BAR_IDX,
  42        .reg1_base_hi = 0,
  43    };
  44    cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE,
  45                               REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
  46                               REG_LOC_DVSEC_REVID, dvsec);
  47    dvsec = (uint8_t *)&(CXLDVSECDeviceGPF){
  48        .phase2_duration = 0x603, /* 3 seconds */
  49        .phase2_power = 0x33, /* 0x33 miliwatts */
  50    };
  51    cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE,
  52                               GPF_DEVICE_DVSEC_LENGTH, GPF_PORT_DVSEC,
  53                               GPF_DEVICE_DVSEC_REVID, dvsec);
  54}
  55
  56static void hdm_decoder_commit(CXLType3Dev *ct3d, int which)
  57{
  58    ComponentRegisters *cregs = &ct3d->cxl_cstate.crb;
  59    uint32_t *cache_mem = cregs->cache_mem_registers;
  60
  61    assert(which == 0);
  62
  63    /* TODO: Sanity checks that the decoder is possible */
  64    ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMIT, 0);
  65    ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, ERR, 0);
  66
  67    ARRAY_FIELD_DP32(cache_mem, CXL_HDM_DECODER0_CTRL, COMMITTED, 1);
  68}
  69
  70static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value,
  71                           unsigned size)
  72{
  73    CXLComponentState *cxl_cstate = opaque;
  74    ComponentRegisters *cregs = &cxl_cstate->crb;
  75    CXLType3Dev *ct3d = container_of(cxl_cstate, CXLType3Dev, cxl_cstate);
  76    uint32_t *cache_mem = cregs->cache_mem_registers;
  77    bool should_commit = false;
  78    int which_hdm = -1;
  79
  80    assert(size == 4);
  81    g_assert(offset < CXL2_COMPONENT_CM_REGION_SIZE);
  82
  83    switch (offset) {
  84    case A_CXL_HDM_DECODER0_CTRL:
  85        should_commit = FIELD_EX32(value, CXL_HDM_DECODER0_CTRL, COMMIT);
  86        which_hdm = 0;
  87        break;
  88    default:
  89        break;
  90    }
  91
  92    stl_le_p((uint8_t *)cache_mem + offset, value);
  93    if (should_commit) {
  94        hdm_decoder_commit(ct3d, which_hdm);
  95    }
  96}
  97
  98static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
  99{
 100    DeviceState *ds = DEVICE(ct3d);
 101    MemoryRegion *mr;
 102    char *name;
 103
 104    if (!ct3d->hostmem) {
 105        error_setg(errp, "memdev property must be set");
 106        return false;
 107    }
 108
 109    mr = host_memory_backend_get_memory(ct3d->hostmem);
 110    if (!mr) {
 111        error_setg(errp, "memdev property must be set");
 112        return false;
 113    }
 114    memory_region_set_nonvolatile(mr, true);
 115    memory_region_set_enabled(mr, true);
 116    host_memory_backend_set_mapped(ct3d->hostmem, true);
 117
 118    if (ds->id) {
 119        name = g_strdup_printf("cxl-type3-dpa-space:%s", ds->id);
 120    } else {
 121        name = g_strdup("cxl-type3-dpa-space");
 122    }
 123    address_space_init(&ct3d->hostmem_as, mr, name);
 124    g_free(name);
 125
 126    ct3d->cxl_dstate.pmem_size = ct3d->hostmem->size;
 127
 128    if (!ct3d->lsa) {
 129        error_setg(errp, "lsa property must be set");
 130        return false;
 131    }
 132
 133    return true;
 134}
 135
 136static void ct3_realize(PCIDevice *pci_dev, Error **errp)
 137{
 138    CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
 139    CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
 140    ComponentRegisters *regs = &cxl_cstate->crb;
 141    MemoryRegion *mr = &regs->component_registers;
 142    uint8_t *pci_conf = pci_dev->config;
 143
 144    if (!cxl_setup_memory(ct3d, errp)) {
 145        return;
 146    }
 147
 148    pci_config_set_prog_interface(pci_conf, 0x10);
 149    pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_CXL);
 150
 151    pcie_endpoint_cap_init(pci_dev, 0x80);
 152    cxl_cstate->dvsec_offset = 0x100;
 153
 154    ct3d->cxl_cstate.pdev = pci_dev;
 155    build_dvsecs(ct3d);
 156
 157    regs->special_ops = g_new0(MemoryRegionOps, 1);
 158    regs->special_ops->write = ct3d_reg_write;
 159
 160    cxl_component_register_block_init(OBJECT(pci_dev), cxl_cstate,
 161                                      TYPE_CXL_TYPE3);
 162
 163    pci_register_bar(
 164        pci_dev, CXL_COMPONENT_REG_BAR_IDX,
 165        PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr);
 166
 167    cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate);
 168    pci_register_bar(pci_dev, CXL_DEVICE_REG_BAR_IDX,
 169                     PCI_BASE_ADDRESS_SPACE_MEMORY |
 170                         PCI_BASE_ADDRESS_MEM_TYPE_64,
 171                     &ct3d->cxl_dstate.device_registers);
 172}
 173
 174static void ct3_exit(PCIDevice *pci_dev)
 175{
 176    CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
 177    CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
 178    ComponentRegisters *regs = &cxl_cstate->crb;
 179
 180    g_free(regs->special_ops);
 181    address_space_destroy(&ct3d->hostmem_as);
 182}
 183
 184/* TODO: Support multiple HDM decoders and DPA skip */
 185static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa)
 186{
 187    uint32_t *cache_mem = ct3d->cxl_cstate.crb.cache_mem_registers;
 188    uint64_t decoder_base, decoder_size, hpa_offset;
 189    uint32_t hdm0_ctrl;
 190    int ig, iw;
 191
 192    decoder_base = (((uint64_t)cache_mem[R_CXL_HDM_DECODER0_BASE_HI] << 32) |
 193                    cache_mem[R_CXL_HDM_DECODER0_BASE_LO]);
 194    if ((uint64_t)host_addr < decoder_base) {
 195        return false;
 196    }
 197
 198    hpa_offset = (uint64_t)host_addr - decoder_base;
 199
 200    decoder_size = ((uint64_t)cache_mem[R_CXL_HDM_DECODER0_SIZE_HI] << 32) |
 201        cache_mem[R_CXL_HDM_DECODER0_SIZE_LO];
 202    if (hpa_offset >= decoder_size) {
 203        return false;
 204    }
 205
 206    hdm0_ctrl = cache_mem[R_CXL_HDM_DECODER0_CTRL];
 207    iw = FIELD_EX32(hdm0_ctrl, CXL_HDM_DECODER0_CTRL, IW);
 208    ig = FIELD_EX32(hdm0_ctrl, CXL_HDM_DECODER0_CTRL, IG);
 209
 210    *dpa = (MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) |
 211        ((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset) >> iw);
 212
 213    return true;
 214}
 215
 216MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
 217                           unsigned size, MemTxAttrs attrs)
 218{
 219    CXLType3Dev *ct3d = CXL_TYPE3(d);
 220    uint64_t dpa_offset;
 221    MemoryRegion *mr;
 222
 223    /* TODO support volatile region */
 224    mr = host_memory_backend_get_memory(ct3d->hostmem);
 225    if (!mr) {
 226        return MEMTX_ERROR;
 227    }
 228
 229    if (!cxl_type3_dpa(ct3d, host_addr, &dpa_offset)) {
 230        return MEMTX_ERROR;
 231    }
 232
 233    if (dpa_offset > int128_get64(mr->size)) {
 234        return MEMTX_ERROR;
 235    }
 236
 237    return address_space_read(&ct3d->hostmem_as, dpa_offset, attrs, data, size);
 238}
 239
 240MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
 241                            unsigned size, MemTxAttrs attrs)
 242{
 243    CXLType3Dev *ct3d = CXL_TYPE3(d);
 244    uint64_t dpa_offset;
 245    MemoryRegion *mr;
 246
 247    mr = host_memory_backend_get_memory(ct3d->hostmem);
 248    if (!mr) {
 249        return MEMTX_OK;
 250    }
 251
 252    if (!cxl_type3_dpa(ct3d, host_addr, &dpa_offset)) {
 253        return MEMTX_OK;
 254    }
 255
 256    if (dpa_offset > int128_get64(mr->size)) {
 257        return MEMTX_OK;
 258    }
 259    return address_space_write(&ct3d->hostmem_as, dpa_offset, attrs,
 260                               &data, size);
 261}
 262
 263static void ct3d_reset(DeviceState *dev)
 264{
 265    CXLType3Dev *ct3d = CXL_TYPE3(dev);
 266    uint32_t *reg_state = ct3d->cxl_cstate.crb.cache_mem_registers;
 267    uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask;
 268
 269    cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
 270    cxl_device_register_init_common(&ct3d->cxl_dstate);
 271}
 272
 273static Property ct3_props[] = {
 274    DEFINE_PROP_LINK("memdev", CXLType3Dev, hostmem, TYPE_MEMORY_BACKEND,
 275                     HostMemoryBackend *),
 276    DEFINE_PROP_LINK("lsa", CXLType3Dev, lsa, TYPE_MEMORY_BACKEND,
 277                     HostMemoryBackend *),
 278    DEFINE_PROP_END_OF_LIST(),
 279};
 280
 281static uint64_t get_lsa_size(CXLType3Dev *ct3d)
 282{
 283    MemoryRegion *mr;
 284
 285    mr = host_memory_backend_get_memory(ct3d->lsa);
 286    return memory_region_size(mr);
 287}
 288
 289static void validate_lsa_access(MemoryRegion *mr, uint64_t size,
 290                                uint64_t offset)
 291{
 292    assert(offset + size <= memory_region_size(mr));
 293    assert(offset + size > offset);
 294}
 295
 296static uint64_t get_lsa(CXLType3Dev *ct3d, void *buf, uint64_t size,
 297                    uint64_t offset)
 298{
 299    MemoryRegion *mr;
 300    void *lsa;
 301
 302    mr = host_memory_backend_get_memory(ct3d->lsa);
 303    validate_lsa_access(mr, size, offset);
 304
 305    lsa = memory_region_get_ram_ptr(mr) + offset;
 306    memcpy(buf, lsa, size);
 307
 308    return size;
 309}
 310
 311static void set_lsa(CXLType3Dev *ct3d, const void *buf, uint64_t size,
 312                    uint64_t offset)
 313{
 314    MemoryRegion *mr;
 315    void *lsa;
 316
 317    mr = host_memory_backend_get_memory(ct3d->lsa);
 318    validate_lsa_access(mr, size, offset);
 319
 320    lsa = memory_region_get_ram_ptr(mr) + offset;
 321    memcpy(lsa, buf, size);
 322    memory_region_set_dirty(mr, offset, size);
 323
 324    /*
 325     * Just like the PMEM, if the guest is not allowed to exit gracefully, label
 326     * updates will get lost.
 327     */
 328}
 329
 330static void ct3_class_init(ObjectClass *oc, void *data)
 331{
 332    DeviceClass *dc = DEVICE_CLASS(oc);
 333    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
 334    CXLType3Class *cvc = CXL_TYPE3_CLASS(oc);
 335
 336    pc->realize = ct3_realize;
 337    pc->exit = ct3_exit;
 338    pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
 339    pc->vendor_id = PCI_VENDOR_ID_INTEL;
 340    pc->device_id = 0xd93; /* LVF for now */
 341    pc->revision = 1;
 342
 343    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 344    dc->desc = "CXL PMEM Device (Type 3)";
 345    dc->reset = ct3d_reset;
 346    device_class_set_props(dc, ct3_props);
 347
 348    cvc->get_lsa_size = get_lsa_size;
 349    cvc->get_lsa = get_lsa;
 350    cvc->set_lsa = set_lsa;
 351}
 352
 353static const TypeInfo ct3d_info = {
 354    .name = TYPE_CXL_TYPE3,
 355    .parent = TYPE_PCI_DEVICE,
 356    .class_size = sizeof(struct CXLType3Class),
 357    .class_init = ct3_class_init,
 358    .instance_size = sizeof(CXLType3Dev),
 359    .interfaces = (InterfaceInfo[]) {
 360        { INTERFACE_CXL_DEVICE },
 361        { INTERFACE_PCIE_DEVICE },
 362        {}
 363    },
 364};
 365
 366static void ct3d_registers(void)
 367{
 368    type_register_static(&ct3d_info);
 369}
 370
 371type_init(ct3d_registers);
 372