1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */ 3 #include <linux/fwctl.h> 4 #include <linux/device.h> 5 #include <cxl/mailbox.h> 6 #include <cxl/features.h> 7 #include <uapi/fwctl/cxl.h> 8 #include "cxl.h" 9 #include "core.h" 10 #include "cxlmem.h" 11 12 /** 13 * DOC: cxl features 14 * 15 * CXL Features: 16 * A CXL device that includes a mailbox supports commands that allows 17 * listing, getting, and setting of optionally defined features such 18 * as memory sparing or post package sparing. Vendors may define custom 19 * features for the device. 20 */ 21 22 /* All the features below are exclusive to the kernel */ 23 static const uuid_t cxl_exclusive_feats[] = { 24 CXL_FEAT_PATROL_SCRUB_UUID, 25 CXL_FEAT_ECS_UUID, 26 CXL_FEAT_SPPR_UUID, 27 CXL_FEAT_HPPR_UUID, 28 CXL_FEAT_CACHELINE_SPARING_UUID, 29 CXL_FEAT_ROW_SPARING_UUID, 30 CXL_FEAT_BANK_SPARING_UUID, 31 CXL_FEAT_RANK_SPARING_UUID, 32 }; 33 34 static bool is_cxl_feature_exclusive_by_uuid(const uuid_t *uuid) 35 { 36 for (int i = 0; i < ARRAY_SIZE(cxl_exclusive_feats); i++) { 37 if (uuid_equal(uuid, &cxl_exclusive_feats[i])) 38 return true; 39 } 40 41 return false; 42 } 43 44 static bool is_cxl_feature_exclusive(struct cxl_feat_entry *entry) 45 { 46 return is_cxl_feature_exclusive_by_uuid(&entry->uuid); 47 } 48 49 struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds) 50 { 51 return cxlds->cxlfs; 52 } 53 EXPORT_SYMBOL_NS_GPL(to_cxlfs, "CXL"); 54 55 static int cxl_get_supported_features_count(struct cxl_mailbox *cxl_mbox) 56 { 57 struct cxl_mbox_get_sup_feats_out mbox_out; 58 struct cxl_mbox_get_sup_feats_in mbox_in; 59 struct cxl_mbox_cmd mbox_cmd; 60 int rc; 61 62 memset(&mbox_in, 0, sizeof(mbox_in)); 63 mbox_in.count = cpu_to_le32(sizeof(mbox_out)); 64 memset(&mbox_out, 0, sizeof(mbox_out)); 65 mbox_cmd = (struct cxl_mbox_cmd) { 66 .opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES, 67 .size_in = sizeof(mbox_in), 68 .payload_in = &mbox_in, 69 .size_out = sizeof(mbox_out), 70 .payload_out = &mbox_out, 71 .min_out = sizeof(mbox_out), 72 }; 73 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 74 if (rc < 0) 75 return rc; 76 77 return le16_to_cpu(mbox_out.supported_feats); 78 } 79 80 static struct cxl_feat_entries * 81 get_supported_features(struct cxl_features_state *cxlfs) 82 { 83 int remain_feats, max_size, max_feats, start, rc, hdr_size; 84 struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox; 85 int feat_size = sizeof(struct cxl_feat_entry); 86 struct cxl_mbox_get_sup_feats_in mbox_in; 87 struct cxl_feat_entry *entry; 88 struct cxl_mbox_cmd mbox_cmd; 89 int user_feats = 0; 90 int count; 91 92 count = cxl_get_supported_features_count(cxl_mbox); 93 if (count <= 0) 94 return NULL; 95 96 struct cxl_feat_entries *entries __free(kvfree) = 97 kvmalloc_flex(*entries, ent, count); 98 if (!entries) 99 return NULL; 100 101 struct cxl_mbox_get_sup_feats_out *mbox_out __free(kvfree) = 102 kvmalloc(cxl_mbox->payload_size, GFP_KERNEL); 103 if (!mbox_out) 104 return NULL; 105 106 hdr_size = struct_size(mbox_out, ents, 0); 107 max_size = cxl_mbox->payload_size - hdr_size; 108 /* max feat entries that can fit in mailbox max payload size */ 109 max_feats = max_size / feat_size; 110 entry = entries->ent; 111 112 start = 0; 113 remain_feats = count; 114 do { 115 int retrieved, alloc_size, copy_feats; 116 int num_entries; 117 118 if (remain_feats > max_feats) { 119 alloc_size = struct_size(mbox_out, ents, max_feats); 120 remain_feats = remain_feats - max_feats; 121 copy_feats = max_feats; 122 } else { 123 alloc_size = struct_size(mbox_out, ents, remain_feats); 124 copy_feats = remain_feats; 125 remain_feats = 0; 126 } 127 128 memset(&mbox_in, 0, sizeof(mbox_in)); 129 mbox_in.count = cpu_to_le32(alloc_size); 130 mbox_in.start_idx = cpu_to_le16(start); 131 memset(mbox_out, 0, alloc_size); 132 mbox_cmd = (struct cxl_mbox_cmd) { 133 .opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES, 134 .size_in = sizeof(mbox_in), 135 .payload_in = &mbox_in, 136 .size_out = alloc_size, 137 .payload_out = mbox_out, 138 .min_out = hdr_size, 139 }; 140 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 141 if (rc < 0) 142 return NULL; 143 144 if (mbox_cmd.size_out <= hdr_size) 145 return NULL; 146 147 /* 148 * Make sure retrieved out buffer is multiple of feature 149 * entries. 150 */ 151 retrieved = mbox_cmd.size_out - hdr_size; 152 if (retrieved % feat_size) 153 return NULL; 154 155 num_entries = le16_to_cpu(mbox_out->num_entries); 156 /* 157 * If the reported output entries * defined entry size != 158 * retrieved output bytes, then the output package is incorrect. 159 */ 160 if (num_entries * feat_size != retrieved) 161 return NULL; 162 163 memcpy(entry, mbox_out->ents, retrieved); 164 for (int i = 0; i < num_entries; i++) { 165 if (!is_cxl_feature_exclusive(entry + i)) 166 user_feats++; 167 } 168 entry += num_entries; 169 /* 170 * If the number of output entries is less than expected, add the 171 * remaining entries to the next batch. 172 */ 173 remain_feats += copy_feats - num_entries; 174 start += num_entries; 175 } while (remain_feats); 176 177 entries->num_features = count; 178 entries->num_user_features = user_feats; 179 180 return no_free_ptr(entries); 181 } 182 183 static void free_cxlfs(void *_cxlfs) 184 { 185 struct cxl_features_state *cxlfs = _cxlfs; 186 struct cxl_dev_state *cxlds = cxlfs->cxlds; 187 188 cxlds->cxlfs = NULL; 189 kvfree(cxlfs->entries); 190 kfree(cxlfs); 191 } 192 193 /** 194 * devm_cxl_setup_features() - Allocate and initialize features context 195 * @cxlds: CXL device context 196 * 197 * Return 0 on success or -errno on failure. 198 */ 199 int devm_cxl_setup_features(struct cxl_dev_state *cxlds) 200 { 201 struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox; 202 203 if (cxl_mbox->feat_cap < CXL_FEATURES_RO) 204 return -ENODEV; 205 206 struct cxl_features_state *cxlfs __free(kfree) = 207 kzalloc_obj(*cxlfs); 208 if (!cxlfs) 209 return -ENOMEM; 210 211 cxlfs->cxlds = cxlds; 212 213 cxlfs->entries = get_supported_features(cxlfs); 214 if (!cxlfs->entries) 215 return -ENOMEM; 216 217 cxlds->cxlfs = cxlfs; 218 219 return devm_add_action_or_reset(cxlds->dev, free_cxlfs, no_free_ptr(cxlfs)); 220 } 221 EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_features, "CXL"); 222 223 size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, 224 enum cxl_get_feat_selection selection, 225 void *feat_out, size_t feat_out_size, u16 offset, 226 u16 *return_code) 227 { 228 size_t data_to_rd_size; 229 struct cxl_mbox_get_feat_in pi; 230 struct cxl_mbox_cmd mbox_cmd; 231 size_t data_rcvd_size = 0; 232 int rc; 233 234 if (return_code) 235 *return_code = CXL_MBOX_CMD_RC_INPUT; 236 237 if (!feat_out || !feat_out_size) 238 return 0; 239 240 uuid_copy(&pi.uuid, feat_uuid); 241 pi.selection = selection; 242 243 guard(mutex)(&cxl_mbox->feat_mutex); 244 do { 245 data_to_rd_size = min(feat_out_size - data_rcvd_size, 246 cxl_mbox->payload_size); 247 pi.offset = cpu_to_le16(offset + data_rcvd_size); 248 pi.count = cpu_to_le16(data_to_rd_size); 249 250 mbox_cmd = (struct cxl_mbox_cmd) { 251 .opcode = CXL_MBOX_OP_GET_FEATURE, 252 .size_in = sizeof(pi), 253 .payload_in = &pi, 254 .size_out = data_to_rd_size, 255 .payload_out = feat_out + data_rcvd_size, 256 .min_out = data_to_rd_size, 257 }; 258 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 259 if (rc < 0 || !mbox_cmd.size_out) { 260 if (return_code) 261 *return_code = mbox_cmd.return_code; 262 return 0; 263 } 264 data_rcvd_size += mbox_cmd.size_out; 265 } while (data_rcvd_size < feat_out_size); 266 267 if (return_code) 268 *return_code = CXL_MBOX_CMD_RC_SUCCESS; 269 270 return data_rcvd_size; 271 } 272 273 /* 274 * FEAT_DATA_MIN_PAYLOAD_SIZE - min extra number of bytes should be 275 * available in the mailbox for storing the actual feature data so that 276 * the feature data transfer would work as expected. 277 */ 278 #define FEAT_DATA_MIN_PAYLOAD_SIZE 10 279 int cxl_set_feature(struct cxl_mailbox *cxl_mbox, 280 const uuid_t *feat_uuid, u8 feat_version, 281 const void *feat_data, size_t feat_data_size, 282 u32 feat_flag, u16 offset, u16 *return_code) 283 { 284 size_t data_in_size, data_sent_size = 0; 285 struct cxl_mbox_cmd mbox_cmd; 286 size_t hdr_size; 287 288 if (return_code) 289 *return_code = CXL_MBOX_CMD_RC_INPUT; 290 291 struct cxl_mbox_set_feat_in *pi __free(kfree) = 292 kzalloc(cxl_mbox->payload_size, GFP_KERNEL); 293 if (!pi) 294 return -ENOMEM; 295 296 uuid_copy(&pi->uuid, feat_uuid); 297 pi->version = feat_version; 298 feat_flag &= ~CXL_SET_FEAT_FLAG_DATA_TRANSFER_MASK; 299 feat_flag |= CXL_SET_FEAT_FLAG_DATA_SAVED_ACROSS_RESET; 300 hdr_size = sizeof(pi->hdr); 301 /* 302 * Check minimum mbox payload size is available for 303 * the feature data transfer. 304 */ 305 if (hdr_size + FEAT_DATA_MIN_PAYLOAD_SIZE > cxl_mbox->payload_size) 306 return -ENOMEM; 307 308 if (hdr_size + feat_data_size <= cxl_mbox->payload_size) { 309 pi->flags = cpu_to_le32(feat_flag | 310 CXL_SET_FEAT_FLAG_FULL_DATA_TRANSFER); 311 data_in_size = feat_data_size; 312 } else { 313 pi->flags = cpu_to_le32(feat_flag | 314 CXL_SET_FEAT_FLAG_INITIATE_DATA_TRANSFER); 315 data_in_size = cxl_mbox->payload_size - hdr_size; 316 } 317 318 guard(mutex)(&cxl_mbox->feat_mutex); 319 do { 320 int rc; 321 322 pi->offset = cpu_to_le16(offset + data_sent_size); 323 memcpy(pi->feat_data, feat_data + data_sent_size, data_in_size); 324 mbox_cmd = (struct cxl_mbox_cmd) { 325 .opcode = CXL_MBOX_OP_SET_FEATURE, 326 .size_in = hdr_size + data_in_size, 327 .payload_in = pi, 328 }; 329 rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd); 330 if (rc < 0) { 331 if (return_code) 332 *return_code = mbox_cmd.return_code; 333 return rc; 334 } 335 336 data_sent_size += data_in_size; 337 if (data_sent_size >= feat_data_size) { 338 if (return_code) 339 *return_code = CXL_MBOX_CMD_RC_SUCCESS; 340 return 0; 341 } 342 343 if ((feat_data_size - data_sent_size) <= (cxl_mbox->payload_size - hdr_size)) { 344 data_in_size = feat_data_size - data_sent_size; 345 pi->flags = cpu_to_le32(feat_flag | 346 CXL_SET_FEAT_FLAG_FINISH_DATA_TRANSFER); 347 } else { 348 pi->flags = cpu_to_le32(feat_flag | 349 CXL_SET_FEAT_FLAG_CONTINUE_DATA_TRANSFER); 350 } 351 } while (true); 352 } 353 354 /* FWCTL support */ 355 356 static inline struct cxl_memdev *fwctl_to_memdev(struct fwctl_device *fwctl_dev) 357 { 358 return to_cxl_memdev(fwctl_dev->dev.parent); 359 } 360 361 static int cxlctl_open_uctx(struct fwctl_uctx *uctx) 362 { 363 return 0; 364 } 365 366 static void cxlctl_close_uctx(struct fwctl_uctx *uctx) 367 { 368 } 369 370 struct cxl_feat_entry * 371 cxl_feature_info(struct cxl_features_state *cxlfs, 372 const uuid_t *uuid) 373 { 374 struct cxl_feat_entry *feat; 375 376 if (!cxlfs || !cxlfs->entries) 377 return ERR_PTR(-EOPNOTSUPP); 378 379 for (int i = 0; i < cxlfs->entries->num_features; i++) { 380 feat = &cxlfs->entries->ent[i]; 381 if (uuid_equal(uuid, &feat->uuid)) 382 return feat; 383 } 384 385 return ERR_PTR(-EINVAL); 386 } 387 388 static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, 389 const struct fwctl_rpc_cxl *rpc_in, 390 size_t *out_len) 391 { 392 const struct cxl_mbox_get_sup_feats_in *feat_in; 393 struct cxl_mbox_get_sup_feats_out *feat_out; 394 struct cxl_feat_entry *pos; 395 size_t out_size; 396 int requested; 397 u32 count; 398 u16 start; 399 int i; 400 401 if (rpc_in->op_size != sizeof(*feat_in)) 402 return ERR_PTR(-EINVAL); 403 404 feat_in = &rpc_in->get_sup_feats_in; 405 count = le32_to_cpu(feat_in->count); 406 start = le16_to_cpu(feat_in->start_idx); 407 requested = count / sizeof(*pos); 408 409 /* 410 * Make sure that the total requested number of entries is not greater 411 * than the total number of supported features allowed for userspace. 412 */ 413 if (start >= cxlfs->entries->num_features) 414 return ERR_PTR(-EINVAL); 415 416 requested = min_t(int, requested, cxlfs->entries->num_features - start); 417 418 out_size = sizeof(struct fwctl_rpc_cxl_out) + 419 struct_size(feat_out, ents, requested); 420 421 struct fwctl_rpc_cxl_out *rpc_out __free(kvfree) = 422 kvzalloc(out_size, GFP_KERNEL); 423 if (!rpc_out) 424 return ERR_PTR(-ENOMEM); 425 426 rpc_out->size = struct_size(feat_out, ents, requested); 427 feat_out = &rpc_out->get_sup_feats_out; 428 feat_out->num_entries = cpu_to_le16(requested); 429 430 for (i = start, pos = &feat_out->ents[0]; 431 i < cxlfs->entries->num_features; i++, pos++) { 432 if (i - start == requested) 433 break; 434 435 memcpy(pos, &cxlfs->entries->ent[i], sizeof(*pos)); 436 /* 437 * If the feature is exclusive, set the set_feat_size to 0 to 438 * indicate that the feature is not changeable. 439 */ 440 if (is_cxl_feature_exclusive(pos)) { 441 u32 flags; 442 443 pos->set_feat_size = 0; 444 flags = le32_to_cpu(pos->flags); 445 flags &= ~CXL_FEATURE_F_CHANGEABLE; 446 pos->flags = cpu_to_le32(flags); 447 } 448 } 449 450 feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); 451 rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; 452 *out_len = out_size; 453 454 return no_free_ptr(rpc_out); 455 } 456 457 static void *cxlctl_get_feature(struct cxl_features_state *cxlfs, 458 const struct fwctl_rpc_cxl *rpc_in, 459 size_t *out_len) 460 { 461 struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox; 462 const struct cxl_mbox_get_feat_in *feat_in; 463 u16 offset, count, return_code; 464 size_t out_size = *out_len; 465 466 if (rpc_in->op_size != sizeof(*feat_in)) 467 return ERR_PTR(-EINVAL); 468 469 feat_in = &rpc_in->get_feat_in; 470 offset = le16_to_cpu(feat_in->offset); 471 count = le16_to_cpu(feat_in->count); 472 473 if (!count) 474 return ERR_PTR(-EINVAL); 475 476 if (out_size < offsetof(struct fwctl_rpc_cxl_out, payload) || 477 count > out_size - offsetof(struct fwctl_rpc_cxl_out, payload)) 478 return ERR_PTR(-EINVAL); 479 480 struct fwctl_rpc_cxl_out *rpc_out __free(kvfree) = 481 kvzalloc(out_size, GFP_KERNEL); 482 if (!rpc_out) 483 return ERR_PTR(-ENOMEM); 484 485 out_size = cxl_get_feature(cxl_mbox, &feat_in->uuid, 486 feat_in->selection, rpc_out->payload, 487 count, offset, &return_code); 488 *out_len = sizeof(struct fwctl_rpc_cxl_out); 489 if (!out_size) { 490 rpc_out->size = 0; 491 rpc_out->retval = return_code; 492 return no_free_ptr(rpc_out); 493 } 494 495 rpc_out->size = out_size; 496 rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; 497 *out_len += out_size; 498 499 return no_free_ptr(rpc_out); 500 } 501 502 static void *cxlctl_set_feature(struct cxl_features_state *cxlfs, 503 const struct fwctl_rpc_cxl *rpc_in, 504 size_t *out_len) 505 { 506 struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox; 507 const struct cxl_mbox_set_feat_in *feat_in; 508 size_t out_size, data_size; 509 u16 offset, return_code; 510 u32 flags; 511 int rc; 512 513 if (rpc_in->op_size <= sizeof(feat_in->hdr)) 514 return ERR_PTR(-EINVAL); 515 516 feat_in = &rpc_in->set_feat_in; 517 518 if (is_cxl_feature_exclusive_by_uuid(&feat_in->uuid)) 519 return ERR_PTR(-EPERM); 520 521 offset = le16_to_cpu(feat_in->offset); 522 flags = le32_to_cpu(feat_in->flags); 523 out_size = *out_len; 524 525 if (out_size < offsetof(struct fwctl_rpc_cxl_out, payload)) 526 return ERR_PTR(-EINVAL); 527 528 struct fwctl_rpc_cxl_out *rpc_out __free(kvfree) = 529 kvzalloc(out_size, GFP_KERNEL); 530 if (!rpc_out) 531 return ERR_PTR(-ENOMEM); 532 533 rpc_out->size = 0; 534 535 data_size = rpc_in->op_size - sizeof(feat_in->hdr); 536 rc = cxl_set_feature(cxl_mbox, &feat_in->uuid, 537 feat_in->version, feat_in->feat_data, 538 data_size, flags, offset, &return_code); 539 *out_len = sizeof(*rpc_out); 540 if (rc) { 541 rpc_out->retval = return_code; 542 return no_free_ptr(rpc_out); 543 } 544 545 rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; 546 547 return no_free_ptr(rpc_out); 548 } 549 550 static bool cxlctl_validate_set_features(struct cxl_features_state *cxlfs, 551 const struct fwctl_rpc_cxl *rpc_in, 552 enum fwctl_rpc_scope scope) 553 { 554 u16 effects, imm_mask, reset_mask; 555 struct cxl_feat_entry *feat; 556 u32 flags; 557 558 if (rpc_in->op_size < sizeof(uuid_t)) 559 return false; 560 561 feat = cxl_feature_info(cxlfs, &rpc_in->set_feat_in.uuid); 562 if (IS_ERR(feat)) 563 return false; 564 565 /* Ensure that the attribute is changeable */ 566 flags = le32_to_cpu(feat->flags); 567 if (!(flags & CXL_FEATURE_F_CHANGEABLE)) 568 return false; 569 570 effects = le16_to_cpu(feat->effects); 571 572 /* 573 * Reserved bits are set, rejecting since the effects is not 574 * comprehended by the driver. 575 */ 576 if (effects & CXL_CMD_EFFECTS_RESERVED) { 577 dev_warn_once(cxlfs->cxlds->dev, 578 "Reserved bits set in the Feature effects field!\n"); 579 return false; 580 } 581 582 /* Currently no user background command support */ 583 if (effects & CXL_CMD_BACKGROUND) 584 return false; 585 586 /* Effects cause immediate change, highest security scope is needed */ 587 imm_mask = CXL_CMD_CONFIG_CHANGE_IMMEDIATE | 588 CXL_CMD_DATA_CHANGE_IMMEDIATE | 589 CXL_CMD_POLICY_CHANGE_IMMEDIATE | 590 CXL_CMD_LOG_CHANGE_IMMEDIATE; 591 592 reset_mask = CXL_CMD_CONFIG_CHANGE_COLD_RESET | 593 CXL_CMD_CONFIG_CHANGE_CONV_RESET | 594 CXL_CMD_CONFIG_CHANGE_CXL_RESET; 595 596 /* If no immediate or reset effect set, The hardware has a bug */ 597 if (!(effects & imm_mask) && !(effects & reset_mask)) 598 return false; 599 600 /* 601 * If the Feature setting causes immediate configuration change 602 * then we need the full write permission policy. 603 */ 604 if (effects & imm_mask && scope >= FWCTL_RPC_DEBUG_WRITE_FULL) 605 return true; 606 607 /* 608 * If the Feature setting only causes configuration change 609 * after a reset, then the lesser level of write permission 610 * policy is ok. 611 */ 612 if (!(effects & imm_mask) && scope >= FWCTL_RPC_DEBUG_WRITE) 613 return true; 614 615 return false; 616 } 617 618 static bool cxlctl_validate_hw_command(struct cxl_features_state *cxlfs, 619 const struct fwctl_rpc_cxl *rpc_in, 620 enum fwctl_rpc_scope scope, 621 u16 opcode) 622 { 623 struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox; 624 625 switch (opcode) { 626 case CXL_MBOX_OP_GET_SUPPORTED_FEATURES: 627 case CXL_MBOX_OP_GET_FEATURE: 628 return cxl_mbox->feat_cap >= CXL_FEATURES_RO; 629 case CXL_MBOX_OP_SET_FEATURE: 630 if (cxl_mbox->feat_cap < CXL_FEATURES_RW) 631 return false; 632 return cxlctl_validate_set_features(cxlfs, rpc_in, scope); 633 default: 634 return false; 635 } 636 } 637 638 static void *cxlctl_handle_commands(struct cxl_features_state *cxlfs, 639 const struct fwctl_rpc_cxl *rpc_in, 640 size_t *out_len, u16 opcode) 641 { 642 switch (opcode) { 643 case CXL_MBOX_OP_GET_SUPPORTED_FEATURES: 644 return cxlctl_get_supported_features(cxlfs, rpc_in, out_len); 645 case CXL_MBOX_OP_GET_FEATURE: 646 return cxlctl_get_feature(cxlfs, rpc_in, out_len); 647 case CXL_MBOX_OP_SET_FEATURE: 648 return cxlctl_set_feature(cxlfs, rpc_in, out_len); 649 default: 650 return ERR_PTR(-EOPNOTSUPP); 651 } 652 } 653 654 static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope, 655 void *in, size_t in_len, size_t *out_len) 656 { 657 struct fwctl_device *fwctl_dev = uctx->fwctl; 658 struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev); 659 struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds); 660 const struct fwctl_rpc_cxl *rpc_in = in; 661 u16 opcode; 662 663 if (in_len < sizeof(rpc_in->hdr) || 664 rpc_in->op_size > in_len - sizeof(rpc_in->hdr)) 665 return ERR_PTR(-EINVAL); 666 667 opcode = rpc_in->opcode; 668 669 if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode)) 670 return ERR_PTR(-EINVAL); 671 672 return cxlctl_handle_commands(cxlfs, rpc_in, out_len, opcode); 673 } 674 675 static const struct fwctl_ops cxlctl_ops = { 676 .device_type = FWCTL_DEVICE_TYPE_CXL, 677 .uctx_size = sizeof(struct fwctl_uctx), 678 .open_uctx = cxlctl_open_uctx, 679 .close_uctx = cxlctl_close_uctx, 680 .fw_rpc = cxlctl_fw_rpc, 681 }; 682 683 DEFINE_FREE(free_fwctl_dev, struct fwctl_device *, if (_T) fwctl_put(_T)) 684 685 static void free_memdev_fwctl(void *_fwctl_dev) 686 { 687 struct fwctl_device *fwctl_dev = _fwctl_dev; 688 689 fwctl_unregister(fwctl_dev); 690 fwctl_put(fwctl_dev); 691 } 692 693 int devm_cxl_setup_fwctl(struct device *host, struct cxl_memdev *cxlmd) 694 { 695 struct cxl_dev_state *cxlds = cxlmd->cxlds; 696 struct cxl_features_state *cxlfs; 697 int rc; 698 699 cxlfs = to_cxlfs(cxlds); 700 if (!cxlfs) 701 return -ENODEV; 702 703 /* No need to setup FWCTL if there are no user allowed features found */ 704 if (!cxlfs->entries->num_user_features) 705 return -ENODEV; 706 707 struct fwctl_device *fwctl_dev __free(free_fwctl_dev) = 708 _fwctl_alloc_device(&cxlmd->dev, &cxlctl_ops, sizeof(*fwctl_dev)); 709 if (!fwctl_dev) 710 return -ENOMEM; 711 712 rc = fwctl_register(fwctl_dev); 713 if (rc) 714 return rc; 715 716 return devm_add_action_or_reset(host, free_memdev_fwctl, 717 no_free_ptr(fwctl_dev)); 718 } 719 EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_fwctl, "CXL"); 720 721 MODULE_IMPORT_NS("FWCTL"); 722