86
#define GET_SUPPORTED 0x0
87
#define GET_FEATURE 0x1
88
#define SET_FEATURE 0x2
89
+ MAINTENANCE = 0x06,
90
+ #define PERFORM 0x0
91
IDENTIFY = 0x40,
92
#define MEMORY_DEVICE 0x0
93
CCLS = 0x41,
1113
#define CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE BIT(0)
1114
#define CXL_FEAT_ENTRY_ATTR_FLAG_DEEPEST_RESET_PERSISTENCE_MASK GENMASK(3, 1)
1115
#define CXL_FEAT_ENTRY_ATTR_FLAG_PERSIST_ACROSS_FIRMWARE_UPDATE BIT(4)
1114
-#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SELECTION BIT(5)
1115
-#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_SAVED_SELECTION BIT(6)
1116
+#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL BIT(5)
1117
+#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_SAVED_SEL BIT(6)
1118
1119
/* Supported Feature Entry : set feature effects */
1120
#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_COLD_RESET BIT(0)
1133
enum CXL_SUPPORTED_FEATURES_LIST {
1134
CXL_FEATURE_PATROL_SCRUB = 0,
1135
CXL_FEATURE_ECS,
1136
+ CXL_FEATURE_SPPR,
1137
+ CXL_FEATURE_HPPR,
1138
CXL_FEATURE_MAX
1139
};
1140
1176
};
1177
#define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3)
1178
1179
+/* CXL r3.2 section 8.2.10.7.2.1: sPPR Feature Discovery and Configuration */
1180
+static const QemuUUID soft_ppr_uuid = {
1181
+ .data = UUID(0x892ba475, 0xfad8, 0x474e, 0x9d, 0x3e,
1182
+ 0x69, 0x2c, 0x91, 0x75, 0x68, 0xbb)
1183
+};
1184
+
1185
+typedef struct CXLMemSoftPPRSetFeature {
1186
+ CXLSetFeatureInHeader hdr;
1187
+ CXLMemSoftPPRWriteAttrs feat_data;
1188
+} QEMU_PACKED QEMU_ALIGNED(16) CXLMemSoftPPRSetFeature;
1189
+
1190
+/* CXL r3.2 section 8.2.10.7.2.2: hPPR Feature Discovery and Configuration */
1191
+static const QemuUUID hard_ppr_uuid = {
1192
+ .data = UUID(0x80ea4521, 0x786f, 0x4127, 0xaf, 0xb1,
1193
+ 0xec, 0x74, 0x59, 0xfb, 0x0e, 0x24)
1194
+};
1195
+
1196
+typedef struct CXLMemHardPPRSetFeature {
1197
+ CXLSetFeatureInHeader hdr;
1198
+ CXLMemHardPPRWriteAttrs feat_data;
1199
+} QEMU_PACKED QEMU_ALIGNED(16) CXLMemHardPPRSetFeature;
1200
+
1201
/* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */
1202
static const QemuUUID patrol_scrub_uuid = {
1203
.data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33,
1261
for (entry = 0, index = get_feats_in->start_index;
1262
entry < req_entries; index++) {
1263
switch (index) {
1264
+ case CXL_FEATURE_SPPR:
1265
+ /* Fill supported feature entry for soft-PPR */
1266
+ get_feats_out->feat_entries[entry++] =
1267
+ (struct CXLSupportedFeatureEntry) {
1268
+ .uuid = soft_ppr_uuid,
1269
+ .feat_index = index,
1270
+ .get_feat_size = sizeof(CXLMemSoftPPRReadAttrs),
1271
+ .set_feat_size = sizeof(CXLMemSoftPPRWriteAttrs),
1272
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1273
+ CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1274
+ .get_feat_version = CXL_MEMDEV_SPPR_GET_FEATURE_VERSION,
1275
+ .set_feat_version = CXL_MEMDEV_SPPR_SET_FEATURE_VERSION,
1276
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1277
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
1278
+ };
1279
+ break;
1280
+ case CXL_FEATURE_HPPR:
1281
+ /* Fill supported feature entry for hard-PPR */
1282
+ get_feats_out->feat_entries[entry++] =
1283
+ (struct CXLSupportedFeatureEntry) {
1284
+ .uuid = hard_ppr_uuid,
1285
+ .feat_index = index,
1286
+ .get_feat_size = sizeof(CXLMemHardPPRReadAttrs),
1287
+ .set_feat_size = sizeof(CXLMemHardPPRWriteAttrs),
1288
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1289
+ CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1290
+ .get_feat_version = CXL_MEMDEV_HPPR_GET_FEATURE_VERSION,
1291
+ .set_feat_version = CXL_MEMDEV_HPPR_SET_FEATURE_VERSION,
1292
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1293
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
1294
+ };
1295
+ break;
1296
case CXL_FEATURE_PATROL_SCRUB:
1297
/* Fill supported feature entry for device patrol scrub control */
1298
get_feats_out->feat_entries[entry++] =
1391
memcpy(payload_out,
1392
(uint8_t *)&ct3d->ecs_attrs + get_feature->offset,
1393
bytes_to_copy);
1394
+ } else if (qemu_uuid_is_equal(&get_feature->uuid, &soft_ppr_uuid)) {
1395
+ if (get_feature->offset >= sizeof(CXLMemSoftPPRReadAttrs)) {
1396
+ return CXL_MBOX_INVALID_INPUT;
1397
+ }
1398
+ bytes_to_copy = sizeof(CXLMemSoftPPRReadAttrs) -
1399
+ get_feature->offset;
1400
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1401
+ memcpy(payload_out,
1402
+ (uint8_t *)&ct3d->soft_ppr_attrs + get_feature->offset,
1403
+ bytes_to_copy);
1404
+ } else if (qemu_uuid_is_equal(&get_feature->uuid, &hard_ppr_uuid)) {
1405
+ if (get_feature->offset >= sizeof(CXLMemHardPPRReadAttrs)) {
1406
+ return CXL_MBOX_INVALID_INPUT;
1407
+ }
1408
+ bytes_to_copy = sizeof(CXLMemHardPPRReadAttrs) -
1409
+ get_feature->offset;
1410
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1411
+ memcpy(payload_out,
1412
+ (uint8_t *)&ct3d->hard_ppr_attrs + get_feature->offset,
1413
+ bytes_to_copy);
1414
} else {
1415
return CXL_MBOX_UNSUPPORTED;
1416
}
1522
ct3d->ecs_wr_attrs.fru_attrs[count].ecs_config & 0x1F;
1523
}
1524
}
1525
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &soft_ppr_uuid)) {
1526
+ CXLMemSoftPPRSetFeature *sppr_set_feature = (void *)payload_in;
1527
+ CXLMemSoftPPRWriteAttrs *sppr_write_attrs =
1528
+ &sppr_set_feature->feat_data;
1529
+
1530
+ if (hdr->version != CXL_MEMDEV_SPPR_SET_FEATURE_VERSION) {
1531
+ return CXL_MBOX_UNSUPPORTED;
1532
+ }
1533
+
1534
+ memcpy((uint8_t *)&ct3d->soft_ppr_wr_attrs + hdr->offset,
1535
+ sppr_write_attrs, bytes_to_copy);
1536
+ set_feat_info->data_size += bytes_to_copy;
1537
+
1538
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1539
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1540
+ ct3d->soft_ppr_attrs.op_mode = ct3d->soft_ppr_wr_attrs.op_mode;
1541
+ ct3d->soft_ppr_attrs.sppr_op_mode =
1542
+ ct3d->soft_ppr_wr_attrs.sppr_op_mode;
1543
+ }
1544
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &hard_ppr_uuid)) {
1545
+ CXLMemHardPPRSetFeature *hppr_set_feature = (void *)payload_in;
1546
+ CXLMemHardPPRWriteAttrs *hppr_write_attrs =
1547
+ &hppr_set_feature->feat_data;
1548
+
1549
+ if (hdr->version != CXL_MEMDEV_HPPR_SET_FEATURE_VERSION) {
1550
+ return CXL_MBOX_UNSUPPORTED;
1551
+ }
1552
+
1553
+ memcpy((uint8_t *)&ct3d->hard_ppr_wr_attrs + hdr->offset,
1554
+ hppr_write_attrs, bytes_to_copy);
1555
+ set_feat_info->data_size += bytes_to_copy;
1556
+
1557
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1558
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1559
+ ct3d->hard_ppr_attrs.op_mode = ct3d->hard_ppr_wr_attrs.op_mode;
1560
+ ct3d->hard_ppr_attrs.hppr_op_mode =
1561
+ ct3d->hard_ppr_wr_attrs.hppr_op_mode;
1562
+ }
1563
} else {
1564
return CXL_MBOX_UNSUPPORTED;
1565
}
1572
memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size);
1573
} else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) {
1574
memset(&ct3d->ecs_wr_attrs, 0, set_feat_info->data_size);
1575
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &soft_ppr_uuid)) {
1576
+ memset(&ct3d->soft_ppr_wr_attrs, 0, set_feat_info->data_size);
1577
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &hard_ppr_uuid)) {
1578
+ memset(&ct3d->hard_ppr_wr_attrs, 0, set_feat_info->data_size);
1579
}
1580
set_feat_info->data_transfer_flag = 0;
1581
set_feat_info->data_saved_across_reset = false;
1586
return CXL_MBOX_SUCCESS;
1587
}
1588
1589
+static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1590
+ uint8_t maint_op_class, uint8_t maint_op_sub_class,
1591
+ CXLMaintenance *ent)
1592
+{
1593
+ CXLEventSparing event_rec = {};
1594
+
1595
+ cxl_assign_event_header(&event_rec.hdr,
1596
+ &sparing_uuid,
1597
+ (1 << CXL_EVENT_TYPE_INFO),
1598
+ sizeof(event_rec),
1599
+ cxl_device_get_timestamp(&ct3d->cxl_dstate),
1600
+ 1, maint_op_class, 1, maint_op_sub_class,
1601
+ 0, 0, 0, 0);
1602
+ if (ent) {
1603
+ event_rec.flags = 0;
1604
+ event_rec.result = 0;
1605
+ stw_le_p(&event_rec.res_avail, 2);
1606
+ stw_le_p(&event_rec.validity_flags, ent->validity_flags);
1607
+ event_rec.channel = ent->channel;
1608
+ event_rec.rank = ent->rank;
1609
+ st24_le_p(event_rec.nibble_mask, ent->nibble_mask);
1610
+ event_rec.bank_group = ent->bank_group;
1611
+ event_rec.bank = ent->bank;
1612
+ st24_le_p(event_rec.row, ent->row);
1613
+ stw_le_p(&event_rec.column, ent->column);
1614
+ event_rec.sub_channel = ent->sub_channel;
1615
+ if (ent->validity_flags & CXL_MSER_VALID_COMP_ID) {
1616
+ strncpy((char *)event_rec.component_id, (char *)ent->component_id,
1617
+ sizeof(event_rec.component_id));
1618
+ }
1619
+ } else {
1620
+ return;
1621
+ }
1622
+
1623
+ if (cxl_event_insert(&ct3d->cxl_dstate,
1624
+ CXL_EVENT_TYPE_INFO,
1625
+ (CXLEventRecordRaw *)&event_rec)) {
1626
+ cxl_event_irq_assert(ct3d);
1627
+ }
1628
+}
1629
+
1630
+
1631
+static void cxl_perform_ppr(CXLType3Dev *ct3d, uint64_t dpa)
1632
+{
1633
+ CXLMaintenance *ent, *next;
1634
+
1635
+ QLIST_FOREACH_SAFE(ent, &ct3d->maint_list, node, next) {
1636
+ if (dpa == ent->dpa) {
1637
+ /* Produce a Memory Sparing Event Record */
1638
+ if (ct3d->soft_ppr_attrs.sppr_op_mode &
1639
+ CXL_MEMDEV_SPPR_OP_MODE_MEM_SPARING_EV_REC_EN) {
1640
+ cxl_mbox_create_mem_sparing_event_records(ct3d,
1641
+ CXL_MEMDEV_MAINT_CLASS_SPARING,
1642
+ CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING,
1643
+ ent);
1644
+ }
1645
+ break;
1646
+ }
1647
+ }
1648
+}
1649
+
1650
+/* CXL r3.2 section 8.2.10.7.1 - Perform Maintenance (Opcode 0600h) */
1651
+#define MAINTENANCE_PPR_QUERY_RESOURCES BIT(0)
1652
+
1653
+static CXLRetCode cmd_media_perform_maintenance(const struct cxl_cmd *cmd,
1654
+ uint8_t *payload_in, size_t len_in,
1655
+ uint8_t *payload_out, size_t *len_out,
1656
+ CXLCCI *cci)
1657
+{
1658
+ struct {
1659
+ uint8_t class;
1660
+ uint8_t subclass;
1661
+ union {
1662
+ struct {
1663
+ uint8_t flags;
1664
+ uint64_t dpa;
1665
+ uint8_t nibble_mask[3];
1666
+ } QEMU_PACKED ppr;
1667
+ };
1668
+ } QEMU_PACKED *maint_in = (void *)payload_in;
1669
+ CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
1670
+
1671
+ if (maintenance_running(cci)) {
1672
+ return CXL_MBOX_BUSY;
1673
+ }
1674
+
1675
+ switch (maint_in->class) {
1676
+ case CXL_MEMDEV_MAINT_CLASS_NO_OP:
1677
+ return CXL_MBOX_SUCCESS; /* nop */
1678
+ case CXL_MEMDEV_MAINT_CLASS_PPR:
1679
+ if (maint_in->ppr.flags & MAINTENANCE_PPR_QUERY_RESOURCES) {
1680
+ return CXL_MBOX_SUCCESS;
1681
+ }
1682
+
1683
+ switch (maint_in->subclass) {
1684
+ case CXL_MEMDEV_MAINT_SUBCLASS_SPPR:
1685
+ case CXL_MEMDEV_MAINT_SUBCLASS_HPPR:
1686
+ cxl_perform_ppr(ct3d, ldq_le_p(&maint_in->ppr.dpa));
1687
+ return CXL_MBOX_SUCCESS;
1688
+ default:
1689
+ return CXL_MBOX_INVALID_INPUT;
1690
+ }
1691
+ break;
1692
+ default:
1693
+ return CXL_MBOX_INVALID_INPUT;
1694
+ }
1695
+
1696
+ return CXL_MBOX_SUCCESS;
1697
+}
1698
+
1699
/* CXL r3.1 Section 8.2.9.9.1.1: Identify Memory Device (Opcode 4000h) */
1700
static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd,
1701
uint8_t *payload_in,
3997
CXL_MBOX_IMMEDIATE_POLICY_CHANGE |
3998
CXL_MBOX_IMMEDIATE_LOG_CHANGE |
3999
CXL_MBOX_SECURITY_STATE_CHANGE)},
4000
+ [MAINTENANCE][PERFORM] = { "MAINTENANCE_PERFORM",
4001
+ cmd_media_perform_maintenance, ~0,
4002
+ CXL_MBOX_IMMEDIATE_CONFIG_CHANGE |
4003
+ CXL_MBOX_IMMEDIATE_DATA_CHANGE |
4004
+ CXL_MBOX_IMMEDIATE_LOG_CHANGE |
4005
+ CXL_MBOX_BACKGROUND_OPERATION },
4006
[IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE",
4007
cmd_identify_memory_device, 0, 0 },
4008
[CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO",