1135
CXL_FEATURE_ECS,
1136
CXL_FEATURE_SPPR,
1137
CXL_FEATURE_HPPR,
1138
+ CXL_FEATURE_CACHELINE_SPARING,
1139
+ CXL_FEATURE_ROW_SPARING,
1140
+ CXL_FEATURE_BANK_SPARING,
1141
+ CXL_FEATURE_RANK_SPARING,
1142
CXL_FEATURE_MAX
1143
};
1144
1227
CXLMemECSWriteAttrs feat_data[];
1228
} QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature;
1229
1230
+/*
1231
+ * CXL r3.2 section 8.2.10.7.2.3:
1232
+ * Memory Sparing Features Discovery and Configuration
1233
+ */
1234
+static const QemuUUID cacheline_sparing_uuid = {
1235
+ .data = UUID(0x96C33386, 0x91dd, 0x44c7, 0x9e, 0xcb,
1236
+ 0xfd, 0xaf, 0x65, 0x03, 0xba, 0xc4)
1237
+};
1238
+
1239
+static const QemuUUID row_sparing_uuid = {
1240
+ .data = UUID(0x450ebf67, 0xb135, 0x4f97, 0xa4, 0x98,
1241
+ 0xc2, 0xd5, 0x7f, 0x27, 0x9b, 0xed)
1242
+};
1243
+
1244
+static const QemuUUID bank_sparing_uuid = {
1245
+ .data = UUID(0x78b79636, 0x90ac, 0x4b64, 0xa4, 0xef,
1246
+ 0xfa, 0xac, 0x5d, 0x18, 0xa8, 0x63)
1247
+};
1248
+
1249
+static const QemuUUID rank_sparing_uuid = {
1250
+ .data = UUID(0x34dbaff5, 0x0552, 0x4281, 0x8f, 0x76,
1251
+ 0xda, 0x0b, 0x5e, 0x7a, 0x76, 0xa7)
1252
+};
1253
+
1254
+typedef struct CXLMemSparingSetFeature {
1255
+ CXLSetFeatureInHeader hdr;
1256
+ CXLMemSparingWriteAttrs feat_data;
1257
+} QEMU_PACKED QEMU_ALIGNED(16) CXLMemSparingSetFeature;
1258
+
1259
/* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
1260
static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
1261
uint8_t *payload_in,
1356
CXL_FEAT_ENTRY_SFE_CEL_VALID,
1357
};
1358
break;
1359
+ case CXL_FEATURE_CACHELINE_SPARING:
1360
+ /* Fill supported feature entry for Cacheline Memory Sparing */
1361
+ get_feats_out->feat_entries[entry++] =
1362
+ (struct CXLSupportedFeatureEntry) {
1363
+ .uuid = cacheline_sparing_uuid,
1364
+ .feat_index = index,
1365
+ .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1366
+ .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1367
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1368
+ CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1369
+ .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1370
+ .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1371
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1372
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
1373
+ };
1374
+ break;
1375
+ case CXL_FEATURE_ROW_SPARING:
1376
+ /* Fill supported feature entry for Row Memory Sparing */
1377
+ get_feats_out->feat_entries[entry++] =
1378
+ (struct CXLSupportedFeatureEntry) {
1379
+ .uuid = row_sparing_uuid,
1380
+ .feat_index = index,
1381
+ .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1382
+ .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1383
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1384
+ CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1385
+ .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1386
+ .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1387
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1388
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
1389
+ };
1390
+ break;
1391
+ case CXL_FEATURE_BANK_SPARING:
1392
+ /* Fill supported feature entry for Bank Memory Sparing */
1393
+ get_feats_out->feat_entries[entry++] =
1394
+ (struct CXLSupportedFeatureEntry) {
1395
+ .uuid = bank_sparing_uuid,
1396
+ .feat_index = index,
1397
+ .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1398
+ .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1399
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1400
+ CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1401
+ .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1402
+ .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1403
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1404
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
1405
+ };
1406
+ break;
1407
+ case CXL_FEATURE_RANK_SPARING:
1408
+ /* Fill supported feature entry for Rank Memory Sparing */
1409
+ get_feats_out->feat_entries[entry++] =
1410
+ (struct CXLSupportedFeatureEntry) {
1411
+ .uuid = rank_sparing_uuid,
1412
+ .feat_index = index,
1413
+ .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1414
+ .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1415
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1416
+ CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1417
+ .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1418
+ .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1419
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1420
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
1421
+ };
1422
+ break;
1423
default:
1424
__builtin_unreachable();
1425
}
1508
memcpy(payload_out,
1509
(uint8_t *)&ct3d->hard_ppr_attrs + get_feature->offset,
1510
bytes_to_copy);
1511
+ } else if (qemu_uuid_is_equal(&get_feature->uuid,
1512
+ &cacheline_sparing_uuid)) {
1513
+ if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1514
+ return CXL_MBOX_INVALID_INPUT;
1515
+ }
1516
+ bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1517
+ get_feature->offset;
1518
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1519
+ memcpy(payload_out,
1520
+ (uint8_t *)&ct3d->cacheline_sparing_attrs + get_feature->offset,
1521
+ bytes_to_copy);
1522
+ } else if (qemu_uuid_is_equal(&get_feature->uuid, &row_sparing_uuid)) {
1523
+ if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1524
+ return CXL_MBOX_INVALID_INPUT;
1525
+ }
1526
+ bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1527
+ get_feature->offset;
1528
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1529
+ memcpy(payload_out,
1530
+ (uint8_t *)&ct3d->row_sparing_attrs + get_feature->offset,
1531
+ bytes_to_copy);
1532
+ } else if (qemu_uuid_is_equal(&get_feature->uuid, &bank_sparing_uuid)) {
1533
+ if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1534
+ return CXL_MBOX_INVALID_INPUT;
1535
+ }
1536
+ bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1537
+ get_feature->offset;
1538
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1539
+ memcpy(payload_out,
1540
+ (uint8_t *)&ct3d->bank_sparing_attrs + get_feature->offset,
1541
+ bytes_to_copy);
1542
+ } else if (qemu_uuid_is_equal(&get_feature->uuid, &rank_sparing_uuid)) {
1543
+ if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1544
+ return CXL_MBOX_INVALID_INPUT;
1545
+ }
1546
+ bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1547
+ get_feature->offset;
1548
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1549
+ memcpy(payload_out,
1550
+ (uint8_t *)&ct3d->rank_sparing_attrs + get_feature->offset,
1551
+ bytes_to_copy);
1552
} else {
1553
return CXL_MBOX_UNSUPPORTED;
1554
}
1698
ct3d->hard_ppr_attrs.hppr_op_mode =
1699
ct3d->hard_ppr_wr_attrs.hppr_op_mode;
1700
}
1701
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &cacheline_sparing_uuid)) {
1702
+ CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1703
+ CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1704
+ &mem_sparing_set_feature->feat_data;
1705
+
1706
+ if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1707
+ return CXL_MBOX_UNSUPPORTED;
1708
+ }
1709
+
1710
+ memcpy((uint8_t *)&ct3d->cacheline_sparing_wr_attrs + hdr->offset,
1711
+ mem_sparing_write_attrs, bytes_to_copy);
1712
+ set_feat_info->data_size += bytes_to_copy;
1713
+
1714
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1715
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1716
+ ct3d->cacheline_sparing_attrs.op_mode =
1717
+ ct3d->cacheline_sparing_wr_attrs.op_mode;
1718
+ }
1719
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &row_sparing_uuid)) {
1720
+ CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1721
+ CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1722
+ &mem_sparing_set_feature->feat_data;
1723
+
1724
+ if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1725
+ return CXL_MBOX_UNSUPPORTED;
1726
+ }
1727
+
1728
+ memcpy((uint8_t *)&ct3d->row_sparing_wr_attrs + hdr->offset,
1729
+ mem_sparing_write_attrs, bytes_to_copy);
1730
+ set_feat_info->data_size += bytes_to_copy;
1731
+
1732
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1733
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1734
+ ct3d->row_sparing_attrs.op_mode =
1735
+ ct3d->row_sparing_wr_attrs.op_mode;
1736
+ }
1737
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &bank_sparing_uuid)) {
1738
+ CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1739
+ CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1740
+ &mem_sparing_set_feature->feat_data;
1741
+
1742
+ if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1743
+ return CXL_MBOX_UNSUPPORTED;
1744
+ }
1745
+
1746
+ memcpy((uint8_t *)&ct3d->bank_sparing_wr_attrs + hdr->offset,
1747
+ mem_sparing_write_attrs, bytes_to_copy);
1748
+ set_feat_info->data_size += bytes_to_copy;
1749
+
1750
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1751
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1752
+ ct3d->bank_sparing_attrs.op_mode =
1753
+ ct3d->bank_sparing_wr_attrs.op_mode;
1754
+ }
1755
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &rank_sparing_uuid)) {
1756
+ CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1757
+ CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1758
+ &mem_sparing_set_feature->feat_data;
1759
+
1760
+ if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1761
+ return CXL_MBOX_UNSUPPORTED;
1762
+ }
1763
+
1764
+ memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset,
1765
+ mem_sparing_write_attrs, bytes_to_copy);
1766
+ set_feat_info->data_size += bytes_to_copy;
1767
+
1768
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1769
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1770
+ ct3d->rank_sparing_attrs.op_mode =
1771
+ ct3d->rank_sparing_wr_attrs.op_mode;
1772
+ }
1773
} else {
1774
return CXL_MBOX_UNSUPPORTED;
1775
}
1786
memset(&ct3d->soft_ppr_wr_attrs, 0, set_feat_info->data_size);
1787
} else if (qemu_uuid_is_equal(&hdr->uuid, &hard_ppr_uuid)) {
1788
memset(&ct3d->hard_ppr_wr_attrs, 0, set_feat_info->data_size);
1789
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &cacheline_sparing_uuid)) {
1790
+ memset(&ct3d->cacheline_sparing_wr_attrs, 0,
1791
+ set_feat_info->data_size);
1792
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &row_sparing_uuid)) {
1793
+ memset(&ct3d->row_sparing_wr_attrs, 0, set_feat_info->data_size);
1794
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &bank_sparing_uuid)) {
1795
+ memset(&ct3d->bank_sparing_wr_attrs, 0, set_feat_info->data_size);
1796
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &rank_sparing_uuid)) {
1797
+ memset(&ct3d->rank_sparing_wr_attrs, 0, set_feat_info->data_size);
1798
}
1799
set_feat_info->data_transfer_flag = 0;
1800
set_feat_info->data_saved_across_reset = false;
1805
return CXL_MBOX_SUCCESS;
1806
}
1807
1589
-static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1808
+#define CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES BIT(0)
1809
+#define CXL_MEM_SPARING_FLAGS_HARD_SPARING BIT(1)
1810
+#define CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID BIT(2)
1811
+#define CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID BIT(3)
1812
+
1813
+typedef struct CXLMemSparingMaintInPayload {
1814
+ uint8_t flags;
1815
+ uint8_t channel;
1816
+ uint8_t rank;
1817
+ uint8_t nibble_mask[3];
1818
+ uint8_t bank_group;
1819
+ uint8_t bank;
1820
+ uint8_t row[3];
1821
+ uint16_t column;
1822
+ uint8_t sub_channel;
1823
+} QEMU_PACKED CXLMemSparingMaintInPayload;
1824
+
1825
+static void cxl_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1826
uint8_t maint_op_class, uint8_t maint_op_sub_class,
1591
- CXLMaintenance *ent)
1827
+ CXLMaintenance *ent,
1828
+ CXLMemSparingMaintInPayload *sparing_pi)
1829
{
1830
CXLEventSparing event_rec = {};
1831
1853
strncpy((char *)event_rec.component_id, (char *)ent->component_id,
1854
sizeof(event_rec.component_id));
1855
}
1856
+ } else if (sparing_pi) {
1857
+ event_rec.flags = CXL_MSER_FLAGS_QUERY_RESOURCES;
1858
+ event_rec.result = 0;
1859
+ event_rec.validity_flags = CXL_MSER_VALID_CHANNEL |
1860
+ CXL_MSER_VALID_RANK |
1861
+ CXL_MSER_VALID_NIB_MASK |
1862
+ CXL_MSER_VALID_BANK_GROUP |
1863
+ CXL_MSER_VALID_BANK |
1864
+ CXL_MSER_VALID_ROW |
1865
+ CXL_MSER_VALID_COLUMN;
1866
+ event_rec.res_avail = 1;
1867
+ event_rec.channel = sparing_pi->channel;
1868
+ event_rec.rank = sparing_pi->rank;
1869
+ if (sparing_pi->flags & CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID) {
1870
+ memcpy(event_rec.nibble_mask, sparing_pi->nibble_mask,
1871
+ sizeof(sparing_pi->nibble_mask));
1872
+ }
1873
+ event_rec.bank_group = sparing_pi->bank_group;
1874
+ event_rec.bank = sparing_pi->bank;
1875
+ event_rec.column = sparing_pi->column;
1876
+ memcpy(event_rec.row, sparing_pi->row, sizeof(sparing_pi->row));
1877
+ if (sparing_pi->flags & CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID) {
1878
+ event_rec.sub_channel = sparing_pi->sub_channel;
1879
+ event_rec.validity_flags |= CXL_MSER_VALID_SUB_CHANNEL;
1880
+ }
1881
} else {
1882
return;
1883
}
1889
}
1890
}
1891
1892
+static CXLRetCode cxl_perform_mem_sparing(CXLType3Dev *ct3d, uint8_t sub_class,
1893
+ void *maint_pi)
1894
+{
1895
+ switch (sub_class) {
1896
+ case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING:
1897
+ qemu_log("Cacheline Memory Sparing\n");
1898
+ return CXL_MBOX_SUCCESS;
1899
+ case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING:
1900
+ qemu_log("Row Memory Sparing\n");
1901
+ return CXL_MBOX_SUCCESS;
1902
+ case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING:
1903
+ qemu_log("Bank Memory Sparing\n");
1904
+ return CXL_MBOX_SUCCESS;
1905
+ case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING:
1906
+ qemu_log("Rank Memory Sparing\n");
1907
+ return CXL_MBOX_SUCCESS;
1908
+ default:
1909
+ return CXL_MBOX_INVALID_INPUT;
1910
+ }
1911
+}
1912
1913
static void cxl_perform_ppr(CXLType3Dev *ct3d, uint64_t dpa)
1914
{
1919
/* Produce a Memory Sparing Event Record */
1920
if (ct3d->soft_ppr_attrs.sppr_op_mode &
1921
CXL_MEMDEV_SPPR_OP_MODE_MEM_SPARING_EV_REC_EN) {
1640
- cxl_mbox_create_mem_sparing_event_records(ct3d,
1922
+ cxl_create_mem_sparing_event_records(ct3d,
1923
CXL_MEMDEV_MAINT_CLASS_SPARING,
1924
CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING,
1643
- ent);
1925
+ ent, NULL);
1926
}
1927
break;
1928
}
1946
uint64_t dpa;
1947
uint8_t nibble_mask[3];
1948
} QEMU_PACKED ppr;
1949
+ CXLMemSparingMaintInPayload mem_sparing_pi;
1950
};
1951
} QEMU_PACKED *maint_in = (void *)payload_in;
1952
CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
1972
return CXL_MBOX_INVALID_INPUT;
1973
}
1974
break;
1975
+ case CXL_MEMDEV_MAINT_CLASS_SPARING:
1976
+ if (maint_in->mem_sparing_pi.flags &
1977
+ CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES) {
1978
+ /*
1979
+ * CXL r3.2 sect 8.2.10.7.1.4 - Memory Sparing Maintenance Operation
1980
+ * Produce Memory Sparing Event record to report resources
1981
+ * availability.
1982
+ */
1983
+ cxl_create_mem_sparing_event_records(ct3d, maint_in->class,
1984
+ maint_in->subclass, NULL,
1985
+ &maint_in->mem_sparing_pi);
1986
+
1987
+ return CXL_MBOX_SUCCESS;
1988
+ }
1989
+
1990
+ switch (maint_in->subclass) {
1991
+ case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING:
1992
+ case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING:
1993
+ case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING:
1994
+ case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING:
1995
+ return cxl_perform_mem_sparing(ct3d, maint_in->subclass,
1996
+ &maint_in->mem_sparing_pi);
1997
+ default:
1998
+ return CXL_MBOX_INVALID_INPUT;
1999
+ }
2000
+ break;
2001
default:
2002
return CXL_MBOX_INVALID_INPUT;
2003
}