|
@@ -824,6 +824,7 @@ typedef struct CXLSupportedFeatureEntry {
|
|
|
#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CXL_RESET BIT(11)
|
|
|
|
|
|
enum CXL_SUPPORTED_FEATURES_LIST {
|
|
|
+ CXL_FEATURE_PATROL_SCRUB = 0,
|
|
|
CXL_FEATURE_MAX
|
|
|
};
|
|
|
|
|
@@ -865,6 +866,17 @@ enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER {
|
|
|
};
|
|
|
#define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3)
|
|
|
|
|
|
+/* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */
|
|
|
+static const QemuUUID patrol_scrub_uuid = {
|
|
|
+ .data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33,
|
|
|
+ 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a)
|
|
|
+};
|
|
|
+
|
|
|
+typedef struct CXLMemPatrolScrubSetFeature {
|
|
|
+ CXLSetFeatureInHeader hdr;
|
|
|
+ CXLMemPatrolScrubWriteAttrs feat_data;
|
|
|
+} QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature;
|
|
|
+
|
|
|
/* 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,
|
|
@@ -890,11 +902,7 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
|
|
|
return CXL_MBOX_UNSUPPORTED;
|
|
|
}
|
|
|
if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) ||
|
|
|
- /*
|
|
|
- * Temporary: suppress compiler error due to unsigned
|
|
|
- * comparioson to zero.
|
|
|
- */
|
|
|
- true /*get_feats_in->start_index >= CXL_FEATURE_MAX*/) {
|
|
|
+ get_feats_in->start_index >= CXL_FEATURE_MAX) {
|
|
|
return CXL_MBOX_INVALID_INPUT;
|
|
|
}
|
|
|
|
|
@@ -907,6 +915,21 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
|
|
|
for (entry = 0, index = get_feats_in->start_index;
|
|
|
entry < req_entries; index++) {
|
|
|
switch (index) {
|
|
|
+ case CXL_FEATURE_PATROL_SCRUB:
|
|
|
+ /* Fill supported feature entry for device patrol scrub control */
|
|
|
+ get_feats_out->feat_entries[entry++] =
|
|
|
+ (struct CXLSupportedFeatureEntry) {
|
|
|
+ .uuid = patrol_scrub_uuid,
|
|
|
+ .feat_index = index,
|
|
|
+ .get_feat_size = sizeof(CXLMemPatrolScrubReadAttrs),
|
|
|
+ .set_feat_size = sizeof(CXLMemPatrolScrubWriteAttrs),
|
|
|
+ .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE,
|
|
|
+ .get_feat_version = CXL_MEMDEV_PS_GET_FEATURE_VERSION,
|
|
|
+ .set_feat_version = CXL_MEMDEV_PS_SET_FEATURE_VERSION,
|
|
|
+ .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
|
|
|
+ CXL_FEAT_ENTRY_SFE_CEL_VALID,
|
|
|
+ };
|
|
|
+ break;
|
|
|
default:
|
|
|
__builtin_unreachable();
|
|
|
}
|
|
@@ -956,6 +979,20 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd,
|
|
|
return CXL_MBOX_INVALID_INPUT;
|
|
|
}
|
|
|
|
|
|
+ if (qemu_uuid_is_equal(&get_feature->uuid, &patrol_scrub_uuid)) {
|
|
|
+ if (get_feature->offset >= sizeof(CXLMemPatrolScrubReadAttrs)) {
|
|
|
+ return CXL_MBOX_INVALID_INPUT;
|
|
|
+ }
|
|
|
+ bytes_to_copy = sizeof(CXLMemPatrolScrubReadAttrs) -
|
|
|
+ get_feature->offset;
|
|
|
+ bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
|
|
|
+ memcpy(payload_out,
|
|
|
+ (uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset,
|
|
|
+ bytes_to_copy);
|
|
|
+ } else {
|
|
|
+ return CXL_MBOX_UNSUPPORTED;
|
|
|
+ }
|
|
|
+
|
|
|
*len_out = bytes_to_copy;
|
|
|
|
|
|
return CXL_MBOX_SUCCESS;
|
|
@@ -970,7 +1007,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
|
|
|
CXLCCI *cci)
|
|
|
{
|
|
|
CXLSetFeatureInHeader *hdr = (void *)payload_in;
|
|
|
+ CXLMemPatrolScrubWriteAttrs *ps_write_attrs;
|
|
|
+ CXLMemPatrolScrubSetFeature *ps_set_feature;
|
|
|
CXLSetFeatureInfo *set_feat_info;
|
|
|
+ uint16_t bytes_to_copy = 0;
|
|
|
uint8_t data_transfer_flag;
|
|
|
CXLType3Dev *ct3d;
|
|
|
|
|
@@ -999,11 +1039,40 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
|
|
|
}
|
|
|
set_feat_info->data_transfer_flag = data_transfer_flag;
|
|
|
set_feat_info->data_offset = hdr->offset;
|
|
|
+ bytes_to_copy = len_in - sizeof(CXLSetFeatureInHeader);
|
|
|
+
|
|
|
+ if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) {
|
|
|
+ if (hdr->version != CXL_MEMDEV_PS_SET_FEATURE_VERSION) {
|
|
|
+ return CXL_MBOX_UNSUPPORTED;
|
|
|
+ }
|
|
|
+
|
|
|
+ ps_set_feature = (void *)payload_in;
|
|
|
+ ps_write_attrs = &ps_set_feature->feat_data;
|
|
|
+ memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset,
|
|
|
+ ps_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) {
|
|
|
+ ct3d->patrol_scrub_attrs.scrub_cycle &= ~0xFF;
|
|
|
+ ct3d->patrol_scrub_attrs.scrub_cycle |=
|
|
|
+ ct3d->patrol_scrub_wr_attrs.scrub_cycle_hr & 0xFF;
|
|
|
+ ct3d->patrol_scrub_attrs.scrub_flags &= ~0x1;
|
|
|
+ ct3d->patrol_scrub_attrs.scrub_flags |=
|
|
|
+ ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return CXL_MBOX_UNSUPPORTED;
|
|
|
+ }
|
|
|
|
|
|
if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
|
|
|
data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ||
|
|
|
data_transfer_flag == CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) {
|
|
|
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);
|
|
|
+ }
|
|
|
set_feat_info->data_transfer_flag = 0;
|
|
|
set_feat_info->data_saved_across_reset = false;
|
|
|
set_feat_info->data_offset = 0;
|