|
@@ -825,6 +825,7 @@ typedef struct CXLSupportedFeatureEntry {
|
|
|
|
|
|
enum CXL_SUPPORTED_FEATURES_LIST {
|
|
|
CXL_FEATURE_PATROL_SCRUB = 0,
|
|
|
+ CXL_FEATURE_ECS,
|
|
|
CXL_FEATURE_MAX
|
|
|
};
|
|
|
|
|
@@ -877,6 +878,20 @@ typedef struct CXLMemPatrolScrubSetFeature {
|
|
|
CXLMemPatrolScrubWriteAttrs feat_data;
|
|
|
} QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature;
|
|
|
|
|
|
+/*
|
|
|
+ * CXL r3.1 section 8.2.9.9.11.2:
|
|
|
+ * DDR5 Error Check Scrub (ECS) Control Feature
|
|
|
+ */
|
|
|
+static const QemuUUID ecs_uuid = {
|
|
|
+ .data = UUID(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba,
|
|
|
+ 0xb9, 0x69, 0x1e, 0x89, 0x33, 0x86)
|
|
|
+};
|
|
|
+
|
|
|
+typedef struct CXLMemECSSetFeature {
|
|
|
+ CXLSetFeatureInHeader hdr;
|
|
|
+ CXLMemECSWriteAttrs feat_data[];
|
|
|
+} QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature;
|
|
|
+
|
|
|
/* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
|
|
|
static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
|
|
|
uint8_t *payload_in,
|
|
@@ -930,6 +945,23 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
|
|
|
CXL_FEAT_ENTRY_SFE_CEL_VALID,
|
|
|
};
|
|
|
break;
|
|
|
+ case CXL_FEATURE_ECS:
|
|
|
+ /* Fill supported feature entry for device DDR5 ECS control */
|
|
|
+ get_feats_out->feat_entries[entry++] =
|
|
|
+ (struct CXLSupportedFeatureEntry) {
|
|
|
+ .uuid = ecs_uuid,
|
|
|
+ .feat_index = index,
|
|
|
+ .get_feat_size = CXL_ECS_NUM_MEDIA_FRUS *
|
|
|
+ sizeof(CXLMemECSReadAttrs),
|
|
|
+ .set_feat_size = CXL_ECS_NUM_MEDIA_FRUS *
|
|
|
+ sizeof(CXLMemECSWriteAttrs),
|
|
|
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE,
|
|
|
+ .get_feat_version = CXL_ECS_GET_FEATURE_VERSION,
|
|
|
+ .set_feat_version = CXL_ECS_SET_FEATURE_VERSION,
|
|
|
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
|
|
|
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
|
|
|
+ };
|
|
|
+ break;
|
|
|
default:
|
|
|
__builtin_unreachable();
|
|
|
}
|
|
@@ -989,6 +1021,18 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd,
|
|
|
memcpy(payload_out,
|
|
|
(uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset,
|
|
|
bytes_to_copy);
|
|
|
+ } else if (qemu_uuid_is_equal(&get_feature->uuid, &ecs_uuid)) {
|
|
|
+ if (get_feature->offset >= CXL_ECS_NUM_MEDIA_FRUS *
|
|
|
+ sizeof(CXLMemECSReadAttrs)) {
|
|
|
+ return CXL_MBOX_INVALID_INPUT;
|
|
|
+ }
|
|
|
+ bytes_to_copy = CXL_ECS_NUM_MEDIA_FRUS *
|
|
|
+ sizeof(CXLMemECSReadAttrs) -
|
|
|
+ get_feature->offset;
|
|
|
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
|
|
|
+ memcpy(payload_out,
|
|
|
+ (uint8_t *)&ct3d->ecs_attrs + get_feature->offset,
|
|
|
+ bytes_to_copy);
|
|
|
} else {
|
|
|
return CXL_MBOX_UNSUPPORTED;
|
|
|
}
|
|
@@ -1009,10 +1053,13 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
|
|
|
CXLSetFeatureInHeader *hdr = (void *)payload_in;
|
|
|
CXLMemPatrolScrubWriteAttrs *ps_write_attrs;
|
|
|
CXLMemPatrolScrubSetFeature *ps_set_feature;
|
|
|
+ CXLMemECSWriteAttrs *ecs_write_attrs;
|
|
|
+ CXLMemECSSetFeature *ecs_set_feature;
|
|
|
CXLSetFeatureInfo *set_feat_info;
|
|
|
uint16_t bytes_to_copy = 0;
|
|
|
uint8_t data_transfer_flag;
|
|
|
CXLType3Dev *ct3d;
|
|
|
+ uint16_t count;
|
|
|
|
|
|
|
|
|
if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
|
|
@@ -1062,6 +1109,28 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
|
|
|
ct3d->patrol_scrub_attrs.scrub_flags |=
|
|
|
ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1;
|
|
|
}
|
|
|
+ } else if (qemu_uuid_is_equal(&hdr->uuid,
|
|
|
+ &ecs_uuid)) {
|
|
|
+ if (hdr->version != CXL_ECS_SET_FEATURE_VERSION) {
|
|
|
+ return CXL_MBOX_UNSUPPORTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ ecs_set_feature = (void *)payload_in;
|
|
|
+ ecs_write_attrs = ecs_set_feature->feat_data;
|
|
|
+ memcpy((uint8_t *)ct3d->ecs_wr_attrs + hdr->offset,
|
|
|
+ ecs_write_attrs,
|
|
|
+ bytes_to_copy);
|
|
|
+ set_feat_info->data_size += bytes_to_copy;
|
|
|
+
|
|
|
+ if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
|
|
|
+ data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
|
|
|
+ for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) {
|
|
|
+ ct3d->ecs_attrs[count].ecs_log_cap =
|
|
|
+ ct3d->ecs_wr_attrs[count].ecs_log_cap;
|
|
|
+ ct3d->ecs_attrs[count].ecs_config =
|
|
|
+ ct3d->ecs_wr_attrs[count].ecs_config & 0x1F;
|
|
|
+ }
|
|
|
+ }
|
|
|
} else {
|
|
|
return CXL_MBOX_UNSUPPORTED;
|
|
|
}
|
|
@@ -1072,6 +1141,8 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
|
|
|
memset(&set_feat_info->uuid, 0, sizeof(QemuUUID));
|
|
|
if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) {
|
|
|
memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size);
|
|
|
+ } else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) {
|
|
|
+ memset(ct3d->ecs_wr_attrs, 0, set_feat_info->data_size);
|
|
|
}
|
|
|
set_feat_info->data_transfer_flag = 0;
|
|
|
set_feat_info->data_saved_across_reset = false;
|