Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'for-6.15/features' into fwctl

Add CXL mailbox Features commands enabling. This is also preparation for
CXL fwctl enabling. The same code will also be utilized by the CXL EDAC
enabling. The commands 'Get Supported Features', 'Get Feature', and 'Set
Feature' are enabled for kernel usages.

Required for the CXL fwctl driver.

* branch 'for-6.15/features'
cxl: Setup exclusive CXL features that are reserved for the kernel
cxl/mbox: Add SET_FEATURE mailbox command
cxl/mbox: Add GET_FEATURE mailbox command
cxl/test: Add Get Supported Features mailbox command support
cxl: Add Get Supported Features command for kernel usage
cxl: Enumerate feature commands
cxl: Refactor user ioctl command path from mds to mailbox

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

+772 -97
+11
drivers/cxl/Kconfig
··· 102 102 103 103 If unsure say 'm'. 104 104 105 + config CXL_FEATURES 106 + bool "CXL: Features" 107 + depends on CXL_PCI 108 + help 109 + Enable support for CXL Features. A CXL device that includes a mailbox 110 + supports commands that allows listing, getting, and setting of 111 + optionally defined features such as memory sparing or post package 112 + sparing. Vendors may define custom features for the device. 113 + 114 + If unsure say 'n' 115 + 105 116 config CXL_PORT 106 117 default CXL_BUS 107 118 tristate
+1
drivers/cxl/core/Makefile
··· 16 16 cxl_core-y += cdat.o 17 17 cxl_core-$(CONFIG_TRACING) += trace.o 18 18 cxl_core-$(CONFIG_CXL_REGION) += region.o 19 + cxl_core-$(CONFIG_CXL_FEATURES) += features.o
+15 -2
drivers/cxl/core/core.h
··· 4 4 #ifndef __CXL_CORE_H__ 5 5 #define __CXL_CORE_H__ 6 6 7 + #include <cxl/mailbox.h> 8 + 7 9 extern const struct device_type cxl_nvdimm_bridge_type; 8 10 extern const struct device_type cxl_nvdimm_type; 9 11 extern const struct device_type cxl_pmu_type; ··· 67 65 68 66 struct cxl_send_command; 69 67 struct cxl_mem_query_commands; 70 - int cxl_query_cmd(struct cxl_memdev *cxlmd, 68 + int cxl_query_cmd(struct cxl_mailbox *cxl_mbox, 71 69 struct cxl_mem_query_commands __user *q); 72 - int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s); 70 + int cxl_send_cmd(struct cxl_mailbox *cxl_mbox, struct cxl_send_command __user *s); 73 71 void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr, 74 72 resource_size_t length); 75 73 ··· 116 114 bool cxl_need_node_perf_attrs_update(int nid); 117 115 int cxl_port_get_switch_dport_bandwidth(struct cxl_port *port, 118 116 struct access_coordinate *c); 117 + 118 + #ifdef CONFIG_CXL_FEATURES 119 + size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, 120 + enum cxl_get_feat_selection selection, 121 + void *feat_out, size_t feat_out_size, u16 offset, 122 + u16 *return_code); 123 + int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, 124 + u8 feat_version, const void *feat_data, 125 + size_t feat_data_size, u32 feat_flag, u16 offset, 126 + u16 *return_code); 127 + #endif 119 128 120 129 #endif /* __CXL_CORE_H__ */
+333
drivers/cxl/core/features.c
··· 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 + }
+77 -47
drivers/cxl/core/mbox.c
··· 349 349 return true; 350 350 } 351 351 352 - static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox, 353 - struct cxl_memdev_state *mds, u16 opcode, 352 + static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox_cmd, 353 + struct cxl_mailbox *cxl_mbox, u16 opcode, 354 354 size_t in_size, size_t out_size, u64 in_payload) 355 355 { 356 - struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 357 - *mbox = (struct cxl_mbox_cmd) { 356 + *mbox_cmd = (struct cxl_mbox_cmd) { 358 357 .opcode = opcode, 359 358 .size_in = in_size, 360 359 }; 361 360 362 361 if (in_size) { 363 - mbox->payload_in = vmemdup_user(u64_to_user_ptr(in_payload), 364 - in_size); 365 - if (IS_ERR(mbox->payload_in)) 366 - return PTR_ERR(mbox->payload_in); 362 + mbox_cmd->payload_in = vmemdup_user(u64_to_user_ptr(in_payload), 363 + in_size); 364 + if (IS_ERR(mbox_cmd->payload_in)) 365 + return PTR_ERR(mbox_cmd->payload_in); 367 366 368 - if (!cxl_payload_from_user_allowed(opcode, mbox->payload_in)) { 369 - dev_dbg(mds->cxlds.dev, "%s: input payload not allowed\n", 367 + if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in)) { 368 + dev_dbg(cxl_mbox->host, "%s: input payload not allowed\n", 370 369 cxl_mem_opcode_to_name(opcode)); 371 - kvfree(mbox->payload_in); 370 + kvfree(mbox_cmd->payload_in); 372 371 return -EBUSY; 373 372 } 374 373 } 375 374 376 375 /* Prepare to handle a full payload for variable sized output */ 377 376 if (out_size == CXL_VARIABLE_PAYLOAD) 378 - mbox->size_out = cxl_mbox->payload_size; 377 + mbox_cmd->size_out = cxl_mbox->payload_size; 379 378 else 380 - mbox->size_out = out_size; 379 + mbox_cmd->size_out = out_size; 381 380 382 - if (mbox->size_out) { 383 - mbox->payload_out = kvzalloc(mbox->size_out, GFP_KERNEL); 384 - if (!mbox->payload_out) { 385 - kvfree(mbox->payload_in); 381 + if (mbox_cmd->size_out) { 382 + mbox_cmd->payload_out = kvzalloc(mbox_cmd->size_out, GFP_KERNEL); 383 + if (!mbox_cmd->payload_out) { 384 + kvfree(mbox_cmd->payload_in); 386 385 return -ENOMEM; 387 386 } 388 387 } ··· 396 397 397 398 static int cxl_to_mem_cmd_raw(struct cxl_mem_command *mem_cmd, 398 399 const struct cxl_send_command *send_cmd, 399 - struct cxl_memdev_state *mds) 400 + struct cxl_mailbox *cxl_mbox) 400 401 { 401 - struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 402 - 403 402 if (send_cmd->raw.rsvd) 404 403 return -EINVAL; 405 404 ··· 412 415 if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode)) 413 416 return -EPERM; 414 417 415 - dev_WARN_ONCE(mds->cxlds.dev, true, "raw command path used\n"); 418 + dev_WARN_ONCE(cxl_mbox->host, true, "raw command path used\n"); 416 419 417 420 *mem_cmd = (struct cxl_mem_command) { 418 421 .info = { ··· 428 431 429 432 static int cxl_to_mem_cmd(struct cxl_mem_command *mem_cmd, 430 433 const struct cxl_send_command *send_cmd, 431 - struct cxl_memdev_state *mds) 434 + struct cxl_mailbox *cxl_mbox) 432 435 { 433 436 struct cxl_mem_command *c = &cxl_mem_commands[send_cmd->id]; 434 437 const struct cxl_command_info *info = &c->info; ··· 443 446 return -EINVAL; 444 447 445 448 /* Check that the command is enabled for hardware */ 446 - if (!test_bit(info->id, mds->enabled_cmds)) 449 + if (!test_bit(info->id, cxl_mbox->enabled_cmds)) 447 450 return -ENOTTY; 448 451 449 452 /* Check that the command is not claimed for exclusive kernel use */ 450 - if (test_bit(info->id, mds->exclusive_cmds)) 453 + if (test_bit(info->id, cxl_mbox->exclusive_cmds)) 451 454 return -EBUSY; 452 455 453 456 /* Check the input buffer is the expected size */ ··· 476 479 /** 477 480 * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND. 478 481 * @mbox_cmd: Sanitized and populated &struct cxl_mbox_cmd. 479 - * @mds: The driver data for the operation 482 + * @cxl_mbox: CXL mailbox context 480 483 * @send_cmd: &struct cxl_send_command copied in from userspace. 481 484 * 482 485 * Return: ··· 491 494 * safe to send to the hardware. 492 495 */ 493 496 static int cxl_validate_cmd_from_user(struct cxl_mbox_cmd *mbox_cmd, 494 - struct cxl_memdev_state *mds, 497 + struct cxl_mailbox *cxl_mbox, 495 498 const struct cxl_send_command *send_cmd) 496 499 { 497 - struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 498 500 struct cxl_mem_command mem_cmd; 499 501 int rc; 500 502 ··· 510 514 511 515 /* Sanitize and construct a cxl_mem_command */ 512 516 if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) 513 - rc = cxl_to_mem_cmd_raw(&mem_cmd, send_cmd, mds); 517 + rc = cxl_to_mem_cmd_raw(&mem_cmd, send_cmd, cxl_mbox); 514 518 else 515 - rc = cxl_to_mem_cmd(&mem_cmd, send_cmd, mds); 519 + rc = cxl_to_mem_cmd(&mem_cmd, send_cmd, cxl_mbox); 516 520 517 521 if (rc) 518 522 return rc; 519 523 520 524 /* Sanitize and construct a cxl_mbox_cmd */ 521 - return cxl_mbox_cmd_ctor(mbox_cmd, mds, mem_cmd.opcode, 525 + return cxl_mbox_cmd_ctor(mbox_cmd, cxl_mbox, mem_cmd.opcode, 522 526 mem_cmd.info.size_in, mem_cmd.info.size_out, 523 527 send_cmd->in.payload); 524 528 } 525 529 526 - int cxl_query_cmd(struct cxl_memdev *cxlmd, 530 + int cxl_query_cmd(struct cxl_mailbox *cxl_mbox, 527 531 struct cxl_mem_query_commands __user *q) 528 532 { 529 - struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 530 - struct device *dev = &cxlmd->dev; 533 + struct device *dev = cxl_mbox->host; 531 534 struct cxl_mem_command *cmd; 532 535 u32 n_commands; 533 536 int j = 0; ··· 547 552 cxl_for_each_cmd(cmd) { 548 553 struct cxl_command_info info = cmd->info; 549 554 550 - if (test_bit(info.id, mds->enabled_cmds)) 555 + if (test_bit(info.id, cxl_mbox->enabled_cmds)) 551 556 info.flags |= CXL_MEM_COMMAND_FLAG_ENABLED; 552 - if (test_bit(info.id, mds->exclusive_cmds)) 557 + if (test_bit(info.id, cxl_mbox->exclusive_cmds)) 553 558 info.flags |= CXL_MEM_COMMAND_FLAG_EXCLUSIVE; 554 559 555 560 if (copy_to_user(&q->commands[j++], &info, sizeof(info))) ··· 564 569 565 570 /** 566 571 * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace. 567 - * @mds: The driver data for the operation 572 + * @cxl_mbox: The mailbox context for the operation. 568 573 * @mbox_cmd: The validated mailbox command. 569 574 * @out_payload: Pointer to userspace's output payload. 570 575 * @size_out: (Input) Max payload size to copy out. ··· 585 590 * 586 591 * See cxl_send_cmd(). 587 592 */ 588 - static int handle_mailbox_cmd_from_user(struct cxl_memdev_state *mds, 593 + static int handle_mailbox_cmd_from_user(struct cxl_mailbox *cxl_mbox, 589 594 struct cxl_mbox_cmd *mbox_cmd, 590 595 u64 out_payload, s32 *size_out, 591 596 u32 *retval) 592 597 { 593 - struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 594 - struct device *dev = mds->cxlds.dev; 598 + struct device *dev = cxl_mbox->host; 595 599 int rc; 596 600 597 601 dev_dbg(dev, ··· 627 633 return rc; 628 634 } 629 635 630 - int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s) 636 + int cxl_send_cmd(struct cxl_mailbox *cxl_mbox, struct cxl_send_command __user *s) 631 637 { 632 - struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 633 - struct device *dev = &cxlmd->dev; 638 + struct device *dev = cxl_mbox->host; 634 639 struct cxl_send_command send; 635 640 struct cxl_mbox_cmd mbox_cmd; 636 641 int rc; ··· 639 646 if (copy_from_user(&send, s, sizeof(send))) 640 647 return -EFAULT; 641 648 642 - rc = cxl_validate_cmd_from_user(&mbox_cmd, mds, &send); 649 + rc = cxl_validate_cmd_from_user(&mbox_cmd, cxl_mbox, &send); 643 650 if (rc) 644 651 return rc; 645 652 646 - rc = handle_mailbox_cmd_from_user(mds, &mbox_cmd, send.out.payload, 653 + rc = handle_mailbox_cmd_from_user(cxl_mbox, &mbox_cmd, send.out.payload, 647 654 &send.out.size, &send.retval); 648 655 if (rc) 649 656 return rc; ··· 706 713 return 0; 707 714 } 708 715 716 + static int check_features_opcodes(u16 opcode, int *ro_cmds, int *wr_cmds) 717 + { 718 + switch (opcode) { 719 + case CXL_MBOX_OP_GET_SUPPORTED_FEATURES: 720 + case CXL_MBOX_OP_GET_FEATURE: 721 + (*ro_cmds)++; 722 + return 1; 723 + case CXL_MBOX_OP_SET_FEATURE: 724 + (*wr_cmds)++; 725 + return 1; 726 + default: 727 + return 0; 728 + } 729 + } 730 + 731 + /* 'Get Supported Features' and 'Get Feature' */ 732 + #define MAX_FEATURES_READ_CMDS 2 733 + static void set_features_cap(struct cxl_mailbox *cxl_mbox, 734 + int ro_cmds, int wr_cmds) 735 + { 736 + /* Setting up Features capability while walking the CEL */ 737 + if (ro_cmds == MAX_FEATURES_READ_CMDS) { 738 + if (wr_cmds) 739 + cxl_mbox->feat_cap = CXL_FEATURES_RW; 740 + else 741 + cxl_mbox->feat_cap = CXL_FEATURES_RO; 742 + } 743 + } 744 + 709 745 /** 710 746 * cxl_walk_cel() - Walk through the Command Effects Log. 711 747 * @mds: The driver data for the operation ··· 746 724 */ 747 725 static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel) 748 726 { 727 + struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 749 728 struct cxl_cel_entry *cel_entry; 750 729 const int cel_entries = size / sizeof(*cel_entry); 751 730 struct device *dev = mds->cxlds.dev; 752 - int i; 731 + int i, ro_cmds = 0, wr_cmds = 0; 753 732 754 733 cel_entry = (struct cxl_cel_entry *) cel; 755 734 ··· 760 737 int enabled = 0; 761 738 762 739 if (cmd) { 763 - set_bit(cmd->info.id, mds->enabled_cmds); 740 + set_bit(cmd->info.id, cxl_mbox->enabled_cmds); 764 741 enabled++; 765 742 } 743 + 744 + enabled += check_features_opcodes(opcode, &ro_cmds, 745 + &wr_cmds); 766 746 767 747 if (cxl_is_poison_command(opcode)) { 768 748 cxl_set_poison_cmd_enabled(&mds->poison, opcode); ··· 780 754 dev_dbg(dev, "Opcode 0x%04x %s\n", opcode, 781 755 enabled ? "enabled" : "unsupported by driver"); 782 756 } 757 + 758 + set_features_cap(cxl_mbox, ro_cmds, wr_cmds); 783 759 } 784 760 785 761 static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds) ··· 835 807 */ 836 808 int cxl_enumerate_cmds(struct cxl_memdev_state *mds) 837 809 { 810 + struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 838 811 struct cxl_mbox_get_supported_logs *gsl; 839 812 struct device *dev = mds->cxlds.dev; 840 813 struct cxl_mem_command *cmd; ··· 874 845 /* In case CEL was bogus, enable some default commands. */ 875 846 cxl_for_each_cmd(cmd) 876 847 if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE) 877 - set_bit(cmd->info.id, mds->enabled_cmds); 848 + set_bit(cmd->info.id, cxl_mbox->enabled_cmds); 878 849 879 850 /* Found the required CEL */ 880 851 rc = 0; ··· 1477 1448 mutex_init(&mds->event.log_lock); 1478 1449 mds->cxlds.dev = dev; 1479 1450 mds->cxlds.reg_map.host = dev; 1451 + mds->cxlds.cxl_mbox.host = dev; 1480 1452 mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE; 1481 1453 mds->cxlds.type = CXL_DEVTYPE_CLASSMEM; 1482 1454 mds->ram_perf.qos_class = CXL_QOS_CLASS_INVALID;
+15 -7
drivers/cxl/core/memdev.c
··· 564 564 void set_exclusive_cxl_commands(struct cxl_memdev_state *mds, 565 565 unsigned long *cmds) 566 566 { 567 + struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 568 + 567 569 down_write(&cxl_memdev_rwsem); 568 - bitmap_or(mds->exclusive_cmds, mds->exclusive_cmds, cmds, 569 - CXL_MEM_COMMAND_ID_MAX); 570 + bitmap_or(cxl_mbox->exclusive_cmds, cxl_mbox->exclusive_cmds, 571 + cmds, CXL_MEM_COMMAND_ID_MAX); 570 572 up_write(&cxl_memdev_rwsem); 571 573 } 572 574 EXPORT_SYMBOL_NS_GPL(set_exclusive_cxl_commands, "CXL"); ··· 581 579 void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds, 582 580 unsigned long *cmds) 583 581 { 582 + struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 583 + 584 584 down_write(&cxl_memdev_rwsem); 585 - bitmap_andnot(mds->exclusive_cmds, mds->exclusive_cmds, cmds, 586 - CXL_MEM_COMMAND_ID_MAX); 585 + bitmap_andnot(cxl_mbox->exclusive_cmds, cxl_mbox->exclusive_cmds, 586 + cmds, CXL_MEM_COMMAND_ID_MAX); 587 587 up_write(&cxl_memdev_rwsem); 588 588 } 589 589 EXPORT_SYMBOL_NS_GPL(clear_exclusive_cxl_commands, "CXL"); ··· 660 656 static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd, 661 657 unsigned long arg) 662 658 { 659 + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 660 + struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; 661 + 663 662 switch (cmd) { 664 663 case CXL_MEM_QUERY_COMMANDS: 665 - return cxl_query_cmd(cxlmd, (void __user *)arg); 664 + return cxl_query_cmd(cxl_mbox, (void __user *)arg); 666 665 case CXL_MEM_SEND_COMMAND: 667 - return cxl_send_cmd(cxlmd, (void __user *)arg); 666 + return cxl_send_cmd(cxl_mbox, (void __user *)arg); 668 667 default: 669 668 return -ENOTTY; 670 669 } ··· 1001 994 int devm_cxl_setup_fw_upload(struct device *host, struct cxl_memdev_state *mds) 1002 995 { 1003 996 struct cxl_dev_state *cxlds = &mds->cxlds; 997 + struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox; 1004 998 struct device *dev = &cxlds->cxlmd->dev; 1005 999 struct fw_upload *fwl; 1006 1000 1007 - if (!test_bit(CXL_MEM_COMMAND_ID_GET_FW_INFO, mds->enabled_cmds)) 1001 + if (!test_bit(CXL_MEM_COMMAND_ID_GET_FW_INFO, cxl_mbox->enabled_cmds)) 1008 1002 return 0; 1009 1003 1010 1004 fwl = firmware_upload_register(THIS_MODULE, dev, dev_name(dev),
+7 -40
drivers/cxl/cxlmem.h
··· 106 106 return xa_load(&port->endpoints, (unsigned long)&cxlmd->dev); 107 107 } 108 108 109 - /** 110 - * struct cxl_mbox_cmd - A command to be submitted to hardware. 111 - * @opcode: (input) The command set and command submitted to hardware. 112 - * @payload_in: (input) Pointer to the input payload. 113 - * @payload_out: (output) Pointer to the output payload. Must be allocated by 114 - * the caller. 115 - * @size_in: (input) Number of bytes to load from @payload_in. 116 - * @size_out: (input) Max number of bytes loaded into @payload_out. 117 - * (output) Number of bytes generated by the device. For fixed size 118 - * outputs commands this is always expected to be deterministic. For 119 - * variable sized output commands, it tells the exact number of bytes 120 - * written. 121 - * @min_out: (input) internal command output payload size validation 122 - * @poll_count: (input) Number of timeouts to attempt. 123 - * @poll_interval_ms: (input) Time between mailbox background command polling 124 - * interval timeouts. 125 - * @return_code: (output) Error code returned from hardware. 126 - * 127 - * This is the primary mechanism used to send commands to the hardware. 128 - * All the fields except @payload_* correspond exactly to the fields described in 129 - * Command Register section of the CXL 2.0 8.2.8.4.5. @payload_in and 130 - * @payload_out are written to, and read from the Command Payload Registers 131 - * defined in CXL 2.0 8.2.8.4.8. 132 - */ 133 - struct cxl_mbox_cmd { 134 - u16 opcode; 135 - void *payload_in; 136 - void *payload_out; 137 - size_t size_in; 138 - size_t size_out; 139 - size_t min_out; 140 - int poll_count; 141 - int poll_interval_ms; 142 - u16 return_code; 143 - }; 144 - 145 109 /* 146 110 * Per CXL 3.0 Section 8.2.8.4.5.1 147 111 */ ··· 392 428 * @serial: PCIe Device Serial Number 393 429 * @type: Generic Memory Class device or Vendor Specific Memory device 394 430 * @cxl_mbox: CXL mailbox context 431 + * @cxlfs: CXL features context 395 432 */ 396 433 struct cxl_dev_state { 397 434 struct device *dev; ··· 408 443 u64 serial; 409 444 enum cxl_devtype type; 410 445 struct cxl_mailbox cxl_mbox; 446 + #ifdef CONFIG_CXL_FEATURES 447 + struct cxl_features_state *cxlfs; 448 + #endif 411 449 }; 412 450 413 451 static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox) ··· 429 461 * @lsa_size: Size of Label Storage Area 430 462 * (CXL 2.0 8.2.9.5.1.1 Identify Memory Device) 431 463 * @firmware_version: Firmware version for the memory device. 432 - * @enabled_cmds: Hardware commands found enabled in CEL. 433 - * @exclusive_cmds: Commands that are kernel-internal only 434 464 * @total_bytes: sum of all possible capacities 435 465 * @volatile_only_bytes: hard volatile capacity 436 466 * @persistent_only_bytes: hard persistent capacity ··· 451 485 struct cxl_dev_state cxlds; 452 486 size_t lsa_size; 453 487 char firmware_version[0x10]; 454 - DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX); 455 - DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX); 456 488 u64 total_bytes; 457 489 u64 volatile_only_bytes; 458 490 u64 persistent_only_bytes; ··· 494 530 CXL_MBOX_OP_GET_LOG_CAPS = 0x0402, 495 531 CXL_MBOX_OP_CLEAR_LOG = 0x0403, 496 532 CXL_MBOX_OP_GET_SUP_LOG_SUBLIST = 0x0405, 533 + CXL_MBOX_OP_GET_SUPPORTED_FEATURES = 0x0500, 534 + CXL_MBOX_OP_GET_FEATURE = 0x0501, 535 + CXL_MBOX_OP_SET_FEATURE = 0x0502, 497 536 CXL_MBOX_OP_IDENTIFY = 0x4000, 498 537 CXL_MBOX_OP_GET_PARTITION_INFO = 0x4100, 499 538 CXL_MBOX_OP_SET_PARTITION_INFO = 0x4101,
+4
drivers/cxl/pci.c
··· 997 997 if (rc) 998 998 return rc; 999 999 1000 + rc = devm_cxl_setup_features(cxlds); 1001 + if (rc) 1002 + dev_dbg(&pdev->dev, "No CXL Features discovered\n"); 1003 + 1000 1004 cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds); 1001 1005 if (IS_ERR(cxlmd)) 1002 1006 return PTR_ERR(cxlmd);
+191
include/cxl/features.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* Copyright(c) 2024-2025 Intel Corporation. */ 3 + #ifndef __CXL_FEATURES_H__ 4 + #define __CXL_FEATURES_H__ 5 + 6 + #include <linux/uuid.h> 7 + 8 + /* Feature UUIDs used by the kernel */ 9 + #define CXL_FEAT_PATROL_SCRUB_UUID \ 10 + UUID_INIT(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33, 0x75, 0x77, 0x4e, \ 11 + 0x06, 0xdb, 0x8a) 12 + 13 + #define CXL_FEAT_ECS_UUID \ 14 + UUID_INIT(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba, 0xb9, 0x69, 0x1e, \ 15 + 0x89, 0x33, 0x86) 16 + 17 + #define CXL_FEAT_SPPR_UUID \ 18 + UUID_INIT(0x892ba475, 0xfad8, 0x474e, 0x9d, 0x3e, 0x69, 0x2c, 0x91, \ 19 + 0x75, 0x68, 0xbb) 20 + 21 + #define CXL_FEAT_HPPR_UUID \ 22 + UUID_INIT(0x80ea4521, 0x786f, 0x4127, 0xaf, 0xb1, 0xec, 0x74, 0x59, \ 23 + 0xfb, 0x0e, 0x24) 24 + 25 + #define CXL_FEAT_CACHELINE_SPARING_UUID \ 26 + UUID_INIT(0x96C33386, 0x91dd, 0x44c7, 0x9e, 0xcb, 0xfd, 0xaf, 0x65, \ 27 + 0x03, 0xba, 0xc4) 28 + 29 + #define CXL_FEAT_ROW_SPARING_UUID \ 30 + UUID_INIT(0x450ebf67, 0xb135, 0x4f97, 0xa4, 0x98, 0xc2, 0xd5, 0x7f, \ 31 + 0x27, 0x9b, 0xed) 32 + 33 + #define CXL_FEAT_BANK_SPARING_UUID \ 34 + UUID_INIT(0x78b79636, 0x90ac, 0x4b64, 0xa4, 0xef, 0xfa, 0xac, 0x5d, \ 35 + 0x18, 0xa8, 0x63) 36 + 37 + #define CXL_FEAT_RANK_SPARING_UUID \ 38 + UUID_INIT(0x34dbaff5, 0x0552, 0x4281, 0x8f, 0x76, 0xda, 0x0b, 0x5e, \ 39 + 0x7a, 0x76, 0xa7) 40 + 41 + /* Feature commands capability supported by a device */ 42 + enum cxl_features_capability { 43 + CXL_FEATURES_NONE = 0, 44 + CXL_FEATURES_RO, 45 + CXL_FEATURES_RW, 46 + }; 47 + 48 + /* Get Supported Features (0x500h) CXL r3.2 8.2.9.6.1 */ 49 + struct cxl_mbox_get_sup_feats_in { 50 + __le32 count; 51 + __le16 start_idx; 52 + u8 reserved[2]; 53 + } __packed; 54 + 55 + /* CXL spec r3.2 Table 8-87 command effects */ 56 + #define CXL_CMD_CONFIG_CHANGE_COLD_RESET BIT(0) 57 + #define CXL_CMD_CONFIG_CHANGE_IMMEDIATE BIT(1) 58 + #define CXL_CMD_DATA_CHANGE_IMMEDIATE BIT(2) 59 + #define CXL_CMD_POLICY_CHANGE_IMMEDIATE BIT(3) 60 + #define CXL_CMD_LOG_CHANGE_IMMEDIATE BIT(4) 61 + #define CXL_CMD_SECURITY_STATE_CHANGE BIT(5) 62 + #define CXL_CMD_BACKGROUND BIT(6) 63 + #define CXL_CMD_BGCMD_ABORT_SUPPORTED BIT(7) 64 + #define CXL_CMD_EFFECTS_VALID BIT(9) 65 + #define CXL_CMD_CONFIG_CHANGE_CONV_RESET BIT(10) 66 + #define CXL_CMD_CONFIG_CHANGE_CXL_RESET BIT(11) 67 + 68 + /* 69 + * CXL spec r3.2 Table 8-109 70 + * Get Supported Features Supported Feature Entry 71 + */ 72 + struct cxl_feat_entry { 73 + uuid_t uuid; 74 + __le16 id; 75 + __le16 get_feat_size; 76 + __le16 set_feat_size; 77 + __le32 flags; 78 + u8 get_feat_ver; 79 + u8 set_feat_ver; 80 + __le16 effects; 81 + u8 reserved[18]; 82 + } __packed; 83 + 84 + /* @flags field for 'struct cxl_feat_entry' */ 85 + #define CXL_FEATURE_F_CHANGEABLE BIT(0) 86 + #define CXL_FEATURE_F_PERSIST_FW_UPDATE BIT(4) 87 + #define CXL_FEATURE_F_DEFAULT_SEL BIT(5) 88 + #define CXL_FEATURE_F_SAVED_SEL BIT(6) 89 + 90 + /* 91 + * CXL spec r3.2 Table 8-108 92 + * Get supported Features Output Payload 93 + */ 94 + struct cxl_mbox_get_sup_feats_out { 95 + __struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* no attrs */, 96 + __le16 num_entries; 97 + __le16 supported_feats; 98 + __u8 reserved[4]; 99 + ); 100 + struct cxl_feat_entry ents[] __counted_by_le(num_entries); 101 + } __packed; 102 + 103 + /* 104 + * Get Feature CXL spec r3.2 Spec 8.2.9.6.2 105 + */ 106 + 107 + /* 108 + * Get Feature input payload 109 + * CXL spec r3.2 section 8.2.9.6.2 Table 8-99 110 + */ 111 + struct cxl_mbox_get_feat_in { 112 + uuid_t uuid; 113 + __le16 offset; 114 + __le16 count; 115 + u8 selection; 116 + } __packed; 117 + 118 + /* Selection field for 'struct cxl_mbox_get_feat_in' */ 119 + enum cxl_get_feat_selection { 120 + CXL_GET_FEAT_SEL_CURRENT_VALUE, 121 + CXL_GET_FEAT_SEL_DEFAULT_VALUE, 122 + CXL_GET_FEAT_SEL_SAVED_VALUE, 123 + CXL_GET_FEAT_SEL_MAX 124 + }; 125 + 126 + /* 127 + * Set Feature CXL spec r3.2 8.2.9.6.3 128 + */ 129 + 130 + /* 131 + * Set Feature input payload 132 + * CXL spec r3.2 section 8.2.9.6.3 Table 8-101 133 + */ 134 + struct cxl_mbox_set_feat_in { 135 + __struct_group(cxl_mbox_set_feat_hdr, hdr, /* no attrs */, 136 + uuid_t uuid; 137 + __le32 flags; 138 + __le16 offset; 139 + u8 version; 140 + u8 rsvd[9]; 141 + ); 142 + __u8 feat_data[]; 143 + } __packed; 144 + 145 + /* Set Feature flags field */ 146 + enum cxl_set_feat_flag_data_transfer { 147 + CXL_SET_FEAT_FLAG_FULL_DATA_TRANSFER = 0, 148 + CXL_SET_FEAT_FLAG_INITIATE_DATA_TRANSFER, 149 + CXL_SET_FEAT_FLAG_CONTINUE_DATA_TRANSFER, 150 + CXL_SET_FEAT_FLAG_FINISH_DATA_TRANSFER, 151 + CXL_SET_FEAT_FLAG_ABORT_DATA_TRANSFER, 152 + CXL_SET_FEAT_FLAG_DATA_TRANSFER_MAX 153 + }; 154 + 155 + #define CXL_SET_FEAT_FLAG_DATA_TRANSFER_MASK GENMASK(2, 0) 156 + 157 + #define CXL_SET_FEAT_FLAG_DATA_SAVED_ACROSS_RESET BIT(3) 158 + 159 + /** 160 + * struct cxl_features_state - The Features state for the device 161 + * @cxlds: Pointer to CXL device state 162 + * @entries: CXl feature entry context 163 + * @num_features: total Features supported by the device 164 + * @ent: Flex array of Feature detail entries from the device 165 + */ 166 + struct cxl_features_state { 167 + struct cxl_dev_state *cxlds; 168 + struct cxl_feat_entries { 169 + int num_features; 170 + int num_user_features; 171 + struct cxl_feat_entry ent[] __counted_by(num_features); 172 + } *entries; 173 + }; 174 + 175 + struct cxl_mailbox; 176 + #ifdef CONFIG_CXL_FEATURES 177 + inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds); 178 + int devm_cxl_setup_features(struct cxl_dev_state *cxlds); 179 + #else 180 + static inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds) 181 + { 182 + return NULL; 183 + } 184 + 185 + static inline int devm_cxl_setup_features(struct cxl_dev_state *cxlds) 186 + { 187 + return -EOPNOTSUPP; 188 + } 189 + #endif 190 + 191 + #endif
+43 -1
include/cxl/mailbox.h
··· 3 3 #ifndef __CXL_MBOX_H__ 4 4 #define __CXL_MBOX_H__ 5 5 #include <linux/rcuwait.h> 6 + #include <cxl/features.h> 7 + #include <uapi/linux/cxl_mem.h> 6 8 7 - struct cxl_mbox_cmd; 9 + /** 10 + * struct cxl_mbox_cmd - A command to be submitted to hardware. 11 + * @opcode: (input) The command set and command submitted to hardware. 12 + * @payload_in: (input) Pointer to the input payload. 13 + * @payload_out: (output) Pointer to the output payload. Must be allocated by 14 + * the caller. 15 + * @size_in: (input) Number of bytes to load from @payload_in. 16 + * @size_out: (input) Max number of bytes loaded into @payload_out. 17 + * (output) Number of bytes generated by the device. For fixed size 18 + * outputs commands this is always expected to be deterministic. For 19 + * variable sized output commands, it tells the exact number of bytes 20 + * written. 21 + * @min_out: (input) internal command output payload size validation 22 + * @poll_count: (input) Number of timeouts to attempt. 23 + * @poll_interval_ms: (input) Time between mailbox background command polling 24 + * interval timeouts. 25 + * @return_code: (output) Error code returned from hardware. 26 + * 27 + * This is the primary mechanism used to send commands to the hardware. 28 + * All the fields except @payload_* correspond exactly to the fields described in 29 + * Command Register section of the CXL 2.0 8.2.8.4.5. @payload_in and 30 + * @payload_out are written to, and read from the Command Payload Registers 31 + * defined in CXL 2.0 8.2.8.4.8. 32 + */ 33 + struct cxl_mbox_cmd { 34 + u16 opcode; 35 + void *payload_in; 36 + void *payload_out; 37 + size_t size_in; 38 + size_t size_out; 39 + size_t min_out; 40 + int poll_count; 41 + int poll_interval_ms; 42 + u16 return_code; 43 + }; 8 44 9 45 /** 10 46 * struct cxl_mailbox - context for CXL mailbox operations 11 47 * @host: device that hosts the mailbox 48 + * @enabled_cmds: mailbox commands that are enabled by the driver 49 + * @exclusive_cmds: mailbox commands that are exclusive to the kernel 12 50 * @payload_size: Size of space for payload 13 51 * (CXL 3.1 8.2.8.4.3 Mailbox Capabilities Register) 14 52 * @mbox_mutex: mutex protects device mailbox and firmware 15 53 * @mbox_wait: rcuwait for mailbox 16 54 * @mbox_send: @dev specific transport for transmitting mailbox commands 55 + * @feat_cap: Features capability 17 56 */ 18 57 struct cxl_mailbox { 19 58 struct device *host; 59 + DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX); 60 + DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX); 20 61 size_t payload_size; 21 62 struct mutex mbox_mutex; /* lock to protect mailbox context */ 22 63 struct rcuwait mbox_wait; 23 64 int (*mbox_send)(struct cxl_mailbox *cxl_mbox, struct cxl_mbox_cmd *cmd); 65 + enum cxl_features_capability feat_cap; 24 66 }; 25 67 26 68 int cxl_mailbox_init(struct cxl_mailbox *cxl_mbox, struct device *host);
+1
tools/testing/cxl/Kbuild
··· 63 63 cxl_core-y += $(CXL_CORE_SRC)/cdat.o 64 64 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o 65 65 cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o 66 + cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o 66 67 cxl_core-y += config_check.o 67 68 cxl_core-y += cxl_core_test.o 68 69 cxl_core-y += cxl_core_exports.o
+74
tools/testing/cxl/test/mem.c
··· 45 45 .effect = CXL_CMD_EFFECT_NONE, 46 46 }, 47 47 { 48 + .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_FEATURES), 49 + .effect = CXL_CMD_EFFECT_NONE, 50 + }, 51 + { 48 52 .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY), 49 53 .effect = CXL_CMD_EFFECT_NONE, 50 54 }, ··· 1358 1354 return -EINVAL; 1359 1355 } 1360 1356 1357 + #define CXL_VENDOR_FEATURE_TEST \ 1358 + UUID_INIT(0xffffffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 1359 + 0xff, 0xff, 0xff) 1360 + 1361 + static void fill_feature_vendor_test(struct cxl_feat_entry *feat) 1362 + { 1363 + feat->uuid = CXL_VENDOR_FEATURE_TEST; 1364 + feat->id = 0; 1365 + feat->get_feat_size = cpu_to_le16(0x4); 1366 + feat->set_feat_size = cpu_to_le16(0x4); 1367 + feat->flags = cpu_to_le32(CXL_FEATURE_F_CHANGEABLE | 1368 + CXL_FEATURE_F_DEFAULT_SEL | 1369 + CXL_FEATURE_F_SAVED_SEL); 1370 + feat->get_feat_ver = 1; 1371 + feat->set_feat_ver = 1; 1372 + feat->effects = cpu_to_le16(CXL_CMD_CONFIG_CHANGE_COLD_RESET | 1373 + CXL_CMD_EFFECTS_VALID); 1374 + } 1375 + 1376 + #define MAX_CXL_TEST_FEATS 1 1377 + 1378 + static int mock_get_supported_features(struct cxl_mockmem_data *mdata, 1379 + struct cxl_mbox_cmd *cmd) 1380 + { 1381 + struct cxl_mbox_get_sup_feats_in *in = cmd->payload_in; 1382 + struct cxl_mbox_get_sup_feats_out *out = cmd->payload_out; 1383 + struct cxl_feat_entry *feat; 1384 + u16 start_idx, count; 1385 + 1386 + if (cmd->size_out < sizeof(*out)) { 1387 + cmd->return_code = CXL_MBOX_CMD_RC_PAYLOADLEN; 1388 + return -EINVAL; 1389 + } 1390 + 1391 + /* 1392 + * Current emulation only supports 1 feature 1393 + */ 1394 + start_idx = le16_to_cpu(in->start_idx); 1395 + if (start_idx != 0) { 1396 + cmd->return_code = CXL_MBOX_CMD_RC_INPUT; 1397 + return -EINVAL; 1398 + } 1399 + 1400 + count = le16_to_cpu(in->count); 1401 + if (count < struct_size(out, ents, 0)) { 1402 + cmd->return_code = CXL_MBOX_CMD_RC_PAYLOADLEN; 1403 + return -EINVAL; 1404 + } 1405 + 1406 + out->supported_feats = cpu_to_le16(MAX_CXL_TEST_FEATS); 1407 + cmd->return_code = 0; 1408 + if (count < struct_size(out, ents, MAX_CXL_TEST_FEATS)) { 1409 + out->num_entries = 0; 1410 + return 0; 1411 + } 1412 + 1413 + out->num_entries = cpu_to_le16(MAX_CXL_TEST_FEATS); 1414 + feat = out->ents; 1415 + fill_feature_vendor_test(feat); 1416 + 1417 + return 0; 1418 + } 1419 + 1361 1420 static int cxl_mock_mbox_send(struct cxl_mailbox *cxl_mbox, 1362 1421 struct cxl_mbox_cmd *cmd) 1363 1422 { ··· 1505 1438 break; 1506 1439 case CXL_MBOX_OP_ACTIVATE_FW: 1507 1440 rc = mock_activate_fw(mdata, cmd); 1441 + break; 1442 + case CXL_MBOX_OP_GET_SUPPORTED_FEATURES: 1443 + rc = mock_get_supported_features(mdata, cmd); 1508 1444 break; 1509 1445 default: 1510 1446 break; ··· 1627 1557 rc = cxl_mem_create_range_info(mds); 1628 1558 if (rc) 1629 1559 return rc; 1560 + 1561 + rc = devm_cxl_setup_features(cxlds); 1562 + if (rc) 1563 + dev_dbg(dev, "No CXL Features discovered\n"); 1630 1564 1631 1565 cxl_mock_add_event_logs(&mdata->mes); 1632 1566