1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */ 3 #include <linux/device.h> 4 #include <cxl/mailbox.h> 5 #include <cxl/features.h> 6 #include "cxl.h" 7 #include "core.h" 8 #include "cxlmem.h" 9 10 /* All the features below are exclusive to the kernel */ 11 static const uuid_t cxl_exclusive_feats[] = { 12 CXL_FEAT_PATROL_SCRUB_UUID, 13 CXL_FEAT_ECS_UUID, 14 CXL_FEAT_SPPR_UUID, 15 CXL_FEAT_HPPR_UUID, 16 CXL_FEAT_CACHELINE_SPARING_UUID, 17 CXL_FEAT_ROW_SPARING_UUID, 18 CXL_FEAT_BANK_SPARING_UUID, 19 CXL_FEAT_RANK_SPARING_UUID, 20 }; 21 22 static bool is_cxl_feature_exclusive(struct cxl_feat_entry *entry) 23 { 24 for (int i = 0; i < ARRAY_SIZE(cxl_exclusive_feats); i++) { 25 if (uuid_equal(&entry->uuid, &cxl_exclusive_feats[i])) 26 return true; 27 } 28 29 return false; 30 } 31 32 inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds) 33 { 34 return cxlds->cxlfs; 35 } 36 EXPORT_SYMBOL_NS_GPL(to_cxlfs, "CXL"); 37 38 static int cxl_get_supported_features_count(struct cxl_mailbox *cxl_mbox) 39 { 40 struct cxl_mbox_get_sup_feats_out mbox_out; 41 struct cxl_mbox_get_sup_feats_in mbox_in; 42 struct cxl_mbox_cmd mbox_cmd; 43 int rc; 44 45 memset(&mbox_in, 0, sizeof(mbox_in)); 46 mbox_in.count = cpu_to_le32(sizeof(mbox_out)); 47 memset(&mbox_out, 0, sizeof(mbox_out)); 48 mbox_cmd = (struct cxl_mbox_cmd) { 49 .opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES, 50 .size_in = sizeof(mbox_in), 51 .payload_in = &mbox_in, 52 .size_out = sizeof(mbox_out), 53 .payload_out = &mbox_out, 54 .min_out = sizeof(mbox_out), 55 }; 56 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 57 if (rc < 0) 58 return rc; 59 60 return le16_to_cpu(mbox_out.supported_feats); 61 } 62 63 static struct cxl_feat_entries * 64 get_supported_features(struct cxl_features_state *cxlfs) 65 { 66 int remain_feats, max_size, max_feats, start, rc, hdr_size; 67 struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox; 68 int feat_size = sizeof(struct cxl_feat_entry); 69 struct cxl_mbox_get_sup_feats_in mbox_in; 70 struct cxl_feat_entry *entry; 71 struct cxl_mbox_cmd mbox_cmd; 72 int user_feats = 0; 73 int count; 74 75 count = cxl_get_supported_features_count(cxl_mbox); 76 if (count <= 0) 77 return NULL; 78 79 struct cxl_feat_entries *entries __free(kvfree) = 80 kvmalloc(struct_size(entries, ent, count), GFP_KERNEL); 81 if (!entries) 82 return NULL; 83 84 struct cxl_mbox_get_sup_feats_out *mbox_out __free(kvfree) = 85 kvmalloc(cxl_mbox->payload_size, GFP_KERNEL); 86 if (!mbox_out) 87 return NULL; 88 89 hdr_size = struct_size(mbox_out, ents, 0); 90 max_size = cxl_mbox->payload_size - hdr_size; 91 /* max feat entries that can fit in mailbox max payload size */ 92 max_feats = max_size / feat_size; 93 entry = entries->ent; 94 95 start = 0; 96 remain_feats = count; 97 do { 98 int retrieved, alloc_size, copy_feats; 99 int num_entries; 100 101 if (remain_feats > max_feats) { 102 alloc_size = struct_size(mbox_out, ents, max_feats); 103 remain_feats = remain_feats - max_feats; 104 copy_feats = max_feats; 105 } else { 106 alloc_size = struct_size(mbox_out, ents, remain_feats); 107 copy_feats = remain_feats; 108 remain_feats = 0; 109 } 110 111 memset(&mbox_in, 0, sizeof(mbox_in)); 112 mbox_in.count = cpu_to_le32(alloc_size); 113 mbox_in.start_idx = cpu_to_le16(start); 114 memset(mbox_out, 0, alloc_size); 115 mbox_cmd = (struct cxl_mbox_cmd) { 116 .opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES, 117 .size_in = sizeof(mbox_in), 118 .payload_in = &mbox_in, 119 .size_out = alloc_size, 120 .payload_out = mbox_out, 121 .min_out = hdr_size, 122 }; 123 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 124 if (rc < 0) 125 return NULL; 126 127 if (mbox_cmd.size_out <= hdr_size) 128 return NULL; 129 130 /* 131 * Make sure retrieved out buffer is multiple of feature 132 * entries. 133 */ 134 retrieved = mbox_cmd.size_out - hdr_size; 135 if (retrieved % feat_size) 136 return NULL; 137 138 num_entries = le16_to_cpu(mbox_out->num_entries); 139 /* 140 * If the reported output entries * defined entry size != 141 * retrieved output bytes, then the output package is incorrect. 142 */ 143 if (num_entries * feat_size != retrieved) 144 return NULL; 145 146 memcpy(entry, mbox_out->ents, retrieved); 147 for (int i = 0; i < num_entries; i++) { 148 if (!is_cxl_feature_exclusive(entry + i)) 149 user_feats++; 150 } 151 entry += num_entries; 152 /* 153 * If the number of output entries is less than expected, add the 154 * remaining entries to the next batch. 155 */ 156 remain_feats += copy_feats - num_entries; 157 start += num_entries; 158 } while (remain_feats); 159 160 entries->num_features = count; 161 entries->num_user_features = user_feats; 162 163 return no_free_ptr(entries); 164 } 165 166 static void free_cxlfs(void *_cxlfs) 167 { 168 struct cxl_features_state *cxlfs = _cxlfs; 169 struct cxl_dev_state *cxlds = cxlfs->cxlds; 170 171 cxlds->cxlfs = NULL; 172 kvfree(cxlfs->entries); 173 kfree(cxlfs); 174 } 175 176 /** 177 * devm_cxl_setup_features() - Allocate and initialize features context 178 * @cxlds: CXL device context 179 * 180 * Return 0 on success or -errno on failure. 181 */ 182 int devm_cxl_setup_features(struct cxl_dev_state *cxlds) 183 { 184 struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox; 185 186 if (cxl_mbox->feat_cap < CXL_FEATURES_RO) 187 return -ENODEV; 188 189 struct cxl_features_state *cxlfs __free(kfree) = 190 kzalloc(sizeof(*cxlfs), GFP_KERNEL); 191 if (!cxlfs) 192 return -ENOMEM; 193 194 cxlfs->cxlds = cxlds; 195 196 cxlfs->entries = get_supported_features(cxlfs); 197 if (!cxlfs->entries) 198 return -ENOMEM; 199 200 cxlds->cxlfs = cxlfs; 201 202 return devm_add_action_or_reset(cxlds->dev, free_cxlfs, no_free_ptr(cxlfs)); 203 } 204 EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_features, "CXL"); 205 206 size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, 207 enum cxl_get_feat_selection selection, 208 void *feat_out, size_t feat_out_size, u16 offset, 209 u16 *return_code) 210 { 211 size_t data_to_rd_size, size_out; 212 struct cxl_mbox_get_feat_in pi; 213 struct cxl_mbox_cmd mbox_cmd; 214 size_t data_rcvd_size = 0; 215 int rc; 216 217 if (return_code) 218 *return_code = CXL_MBOX_CMD_RC_INPUT; 219 220 if (!feat_out || !feat_out_size) 221 return 0; 222 223 size_out = min(feat_out_size, cxl_mbox->payload_size); 224 uuid_copy(&pi.uuid, feat_uuid); 225 pi.selection = selection; 226 do { 227 data_to_rd_size = min(feat_out_size - data_rcvd_size, 228 cxl_mbox->payload_size); 229 pi.offset = cpu_to_le16(offset + data_rcvd_size); 230 pi.count = cpu_to_le16(data_to_rd_size); 231 232 mbox_cmd = (struct cxl_mbox_cmd) { 233 .opcode = CXL_MBOX_OP_GET_FEATURE, 234 .size_in = sizeof(pi), 235 .payload_in = &pi, 236 .size_out = size_out, 237 .payload_out = feat_out + data_rcvd_size, 238 .min_out = data_to_rd_size, 239 }; 240 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 241 if (rc < 0 || !mbox_cmd.size_out) { 242 if (return_code) 243 *return_code = mbox_cmd.return_code; 244 return 0; 245 } 246 data_rcvd_size += mbox_cmd.size_out; 247 } while (data_rcvd_size < feat_out_size); 248 249 if (return_code) 250 *return_code = CXL_MBOX_CMD_RC_SUCCESS; 251 252 return data_rcvd_size; 253 } 254 255 /* 256 * FEAT_DATA_MIN_PAYLOAD_SIZE - min extra number of bytes should be 257 * available in the mailbox for storing the actual feature data so that 258 * the feature data transfer would work as expected. 259 */ 260 #define FEAT_DATA_MIN_PAYLOAD_SIZE 10 261 int cxl_set_feature(struct cxl_mailbox *cxl_mbox, 262 const uuid_t *feat_uuid, u8 feat_version, 263 const void *feat_data, size_t feat_data_size, 264 u32 feat_flag, u16 offset, u16 *return_code) 265 { 266 size_t data_in_size, data_sent_size = 0; 267 struct cxl_mbox_cmd mbox_cmd; 268 size_t hdr_size; 269 270 if (return_code) 271 *return_code = CXL_MBOX_CMD_RC_INPUT; 272 273 struct cxl_mbox_set_feat_in *pi __free(kfree) = 274 kzalloc(cxl_mbox->payload_size, GFP_KERNEL); 275 if (!pi) 276 return -ENOMEM; 277 278 uuid_copy(&pi->uuid, feat_uuid); 279 pi->version = feat_version; 280 feat_flag &= ~CXL_SET_FEAT_FLAG_DATA_TRANSFER_MASK; 281 feat_flag |= CXL_SET_FEAT_FLAG_DATA_SAVED_ACROSS_RESET; 282 hdr_size = sizeof(pi->hdr); 283 /* 284 * Check minimum mbox payload size is available for 285 * the feature data transfer. 286 */ 287 if (hdr_size + FEAT_DATA_MIN_PAYLOAD_SIZE > cxl_mbox->payload_size) 288 return -ENOMEM; 289 290 if (hdr_size + feat_data_size <= cxl_mbox->payload_size) { 291 pi->flags = cpu_to_le32(feat_flag | 292 CXL_SET_FEAT_FLAG_FULL_DATA_TRANSFER); 293 data_in_size = feat_data_size; 294 } else { 295 pi->flags = cpu_to_le32(feat_flag | 296 CXL_SET_FEAT_FLAG_INITIATE_DATA_TRANSFER); 297 data_in_size = cxl_mbox->payload_size - hdr_size; 298 } 299 300 do { 301 int rc; 302 303 pi->offset = cpu_to_le16(offset + data_sent_size); 304 memcpy(pi->feat_data, feat_data + data_sent_size, data_in_size); 305 mbox_cmd = (struct cxl_mbox_cmd) { 306 .opcode = CXL_MBOX_OP_SET_FEATURE, 307 .size_in = hdr_size + data_in_size, 308 .payload_in = pi, 309 }; 310 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 311 if (rc < 0) { 312 if (return_code) 313 *return_code = mbox_cmd.return_code; 314 return rc; 315 } 316 317 data_sent_size += data_in_size; 318 if (data_sent_size >= feat_data_size) { 319 if (return_code) 320 *return_code = CXL_MBOX_CMD_RC_SUCCESS; 321 return 0; 322 } 323 324 if ((feat_data_size - data_sent_size) <= (cxl_mbox->payload_size - hdr_size)) { 325 data_in_size = feat_data_size - data_sent_size; 326 pi->flags = cpu_to_le32(feat_flag | 327 CXL_SET_FEAT_FLAG_FINISH_DATA_TRANSFER); 328 } else { 329 pi->flags = cpu_to_le32(feat_flag | 330 CXL_SET_FEAT_FLAG_CONTINUE_DATA_TRANSFER); 331 } 332 } while (true); 333 } 334