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

Merge tag 'cxl-for-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl

Pull cxl updates from Dan Williams:
"More preparation and plumbing work in the CXL subsystem.

From an end user perspective the highlight here is lighting up the CXL
Persistent Memory related commands (label read / write) with the
generic ioctl() front-end in LIBNVDIMM.

Otherwise, the ability to instantiate new persistent and volatile
memory regions is still on track for v5.17.

Summary:

- Fix support for platforms that do not enumerate every ACPI0016 (CXL
Host Bridge) in the CHBS (ACPI Host Bridge Structure).

- Introduce a common pci_find_dvsec_capability() helper, clean up
open coded implementations in various drivers.

- Add 'cxl_test' for regression testing CXL subsystem ABIs.
'cxl_test' is a module built from tools/testing/cxl/ that mocks up
a CXL topology to augment the nascent support for emulation of CXL
devices in QEMU.

- Convert libnvdimm to use the uuid API.

- Complete the definition of CXL namespace labels in libnvdimm.

- Tunnel libnvdimm label operations from nd_ioctl() back to the CXL
mailbox driver. Enable 'ndctl {read,write}-labels' for CXL.

- Continue to sort and refactor functionality into distinct driver
and core-infrastructure buckets. For example, mailbox handling is
now a generic core capability consumed by the PCI and cxl_test
drivers"

* tag 'cxl-for-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (34 commits)
ocxl: Use pci core's DVSEC functionality
cxl/pci: Use pci core's DVSEC functionality
PCI: Add pci_find_dvsec_capability to find designated VSEC
cxl/pci: Split cxl_pci_setup_regs()
cxl/pci: Add @base to cxl_register_map
cxl/pci: Make more use of cxl_register_map
cxl/pci: Remove pci request/release regions
cxl/pci: Fix NULL vs ERR_PTR confusion
cxl/pci: Remove dev_dbg for unknown register blocks
cxl/pci: Convert register block identifiers to an enum
cxl/acpi: Do not fail cxl_acpi_probe() based on a missing CHBS
cxl/pci: Disambiguate cxl_pci further from cxl_mem
Documentation/cxl: Add bus internal docs
cxl/core: Split decoder setup into alloc + add
tools/testing/cxl: Introduce a mock memory device + driver
cxl/mbox: Move command definitions to common location
cxl/bus: Populate the target list at decoder create
tools/testing/cxl: Introduce a mocked-up CXL port hierarchy
cxl/pmem: Add support for multiple nvdimm-bridge objects
cxl/pmem: Translate NVDIMM label commands to CXL label commands
...

+3275 -1496
+6
Documentation/driver-api/cxl/memory-devices.rst
··· 39 39 .. kernel-doc:: drivers/cxl/core/bus.c 40 40 :doc: cxl core 41 41 42 + .. kernel-doc:: drivers/cxl/core/bus.c 43 + :identifiers: 44 + 42 45 .. kernel-doc:: drivers/cxl/core/pmem.c 43 46 :doc: cxl pmem 44 47 45 48 .. kernel-doc:: drivers/cxl/core/regs.c 46 49 :doc: cxl registers 50 + 51 + .. kernel-doc:: drivers/cxl/core/mbox.c 52 + :doc: cxl mbox 47 53 48 54 External Interfaces 49 55 ===================
+2 -1
arch/powerpc/platforms/powernv/ocxl.c
··· 107 107 int pos; 108 108 u32 val; 109 109 110 - pos = find_dvsec_from_pos(dev, OCXL_DVSEC_FUNC_ID, 0); 110 + pos = pci_find_dvsec_capability(dev, PCI_VENDOR_ID_IBM, 111 + OCXL_DVSEC_FUNC_ID); 111 112 if (!pos) 112 113 return -ESRCH; 113 114
+97 -42
drivers/cxl/acpi.c
··· 52 52 return -EINVAL; 53 53 } 54 54 55 + if (CFMWS_INTERLEAVE_WAYS(cfmws) > CXL_DECODER_MAX_INTERLEAVE) { 56 + dev_err(dev, "CFMWS Interleave Ways (%d) too large\n", 57 + CFMWS_INTERLEAVE_WAYS(cfmws)); 58 + return -EINVAL; 59 + } 60 + 55 61 expected_len = struct_size((cfmws), interleave_targets, 56 62 CFMWS_INTERLEAVE_WAYS(cfmws)); 57 63 ··· 77 71 static void cxl_add_cfmws_decoders(struct device *dev, 78 72 struct cxl_port *root_port) 79 73 { 74 + int target_map[CXL_DECODER_MAX_INTERLEAVE]; 80 75 struct acpi_cedt_cfmws *cfmws; 81 76 struct cxl_decoder *cxld; 82 77 acpi_size len, cur = 0; 83 78 void *cedt_subtable; 84 - unsigned long flags; 85 79 int rc; 86 80 87 81 len = acpi_cedt->length - sizeof(*acpi_cedt); ··· 89 83 90 84 while (cur < len) { 91 85 struct acpi_cedt_header *c = cedt_subtable + cur; 86 + int i; 92 87 93 88 if (c->type != ACPI_CEDT_TYPE_CFMWS) { 94 89 cur += c->length; ··· 115 108 continue; 116 109 } 117 110 118 - flags = cfmws_to_decoder_flags(cfmws->restrictions); 119 - cxld = devm_cxl_add_decoder(dev, root_port, 120 - CFMWS_INTERLEAVE_WAYS(cfmws), 121 - cfmws->base_hpa, cfmws->window_size, 122 - CFMWS_INTERLEAVE_WAYS(cfmws), 123 - CFMWS_INTERLEAVE_GRANULARITY(cfmws), 124 - CXL_DECODER_EXPANDER, 125 - flags); 111 + for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++) 112 + target_map[i] = cfmws->interleave_targets[i]; 126 113 127 - if (IS_ERR(cxld)) { 114 + cxld = cxl_decoder_alloc(root_port, 115 + CFMWS_INTERLEAVE_WAYS(cfmws)); 116 + if (IS_ERR(cxld)) 117 + goto next; 118 + 119 + cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions); 120 + cxld->target_type = CXL_DECODER_EXPANDER; 121 + cxld->range = (struct range) { 122 + .start = cfmws->base_hpa, 123 + .end = cfmws->base_hpa + cfmws->window_size - 1, 124 + }; 125 + cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws); 126 + cxld->interleave_granularity = 127 + CFMWS_INTERLEAVE_GRANULARITY(cfmws); 128 + 129 + rc = cxl_decoder_add(cxld, target_map); 130 + if (rc) 131 + put_device(&cxld->dev); 132 + else 133 + rc = cxl_decoder_autoremove(dev, cxld); 134 + if (rc) { 128 135 dev_err(dev, "Failed to add decoder for %#llx-%#llx\n", 129 136 cfmws->base_hpa, cfmws->base_hpa + 130 137 cfmws->window_size - 1); 131 - } else { 132 - dev_dbg(dev, "add: %s range %#llx-%#llx\n", 133 - dev_name(&cxld->dev), cfmws->base_hpa, 134 - cfmws->base_hpa + cfmws->window_size - 1); 138 + goto next; 135 139 } 140 + dev_dbg(dev, "add: %s range %#llx-%#llx\n", 141 + dev_name(&cxld->dev), cfmws->base_hpa, 142 + cfmws->base_hpa + cfmws->window_size - 1); 143 + next: 136 144 cur += c->length; 137 145 } 138 146 } ··· 204 182 return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base; 205 183 } 206 184 207 - struct cxl_walk_context { 208 - struct device *dev; 209 - struct pci_bus *root; 210 - struct cxl_port *port; 211 - int error; 212 - int count; 213 - }; 214 - 215 - static int match_add_root_ports(struct pci_dev *pdev, void *data) 185 + __mock int match_add_root_ports(struct pci_dev *pdev, void *data) 216 186 { 217 187 struct cxl_walk_context *ctx = data; 218 188 struct pci_bus *root_bus = ctx->root; ··· 253 239 return NULL; 254 240 } 255 241 256 - static struct acpi_device *to_cxl_host_bridge(struct device *dev) 242 + __mock struct acpi_device *to_cxl_host_bridge(struct device *host, 243 + struct device *dev) 257 244 { 258 245 struct acpi_device *adev = to_acpi_device(dev); 259 246 ··· 272 257 */ 273 258 static int add_host_bridge_uport(struct device *match, void *arg) 274 259 { 275 - struct acpi_device *bridge = to_cxl_host_bridge(match); 276 260 struct cxl_port *root_port = arg; 277 261 struct device *host = root_port->dev.parent; 262 + struct acpi_device *bridge = to_cxl_host_bridge(host, match); 278 263 struct acpi_pci_root *pci_root; 279 264 struct cxl_walk_context ctx; 265 + int single_port_map[1], rc; 280 266 struct cxl_decoder *cxld; 281 267 struct cxl_dport *dport; 282 268 struct cxl_port *port; ··· 288 272 dport = find_dport_by_dev(root_port, match); 289 273 if (!dport) { 290 274 dev_dbg(host, "host bridge expected and not found\n"); 291 - return -ENODEV; 275 + return 0; 292 276 } 293 277 294 278 port = devm_cxl_add_port(host, match, dport->component_reg_phys, ··· 313 297 return -ENODEV; 314 298 if (ctx.error) 315 299 return ctx.error; 300 + if (ctx.count > 1) 301 + return 0; 316 302 317 303 /* TODO: Scan CHBCR for HDM Decoder resources */ 318 304 319 305 /* 320 - * In the single-port host-bridge case there are no HDM decoders 321 - * in the CHBCR and a 1:1 passthrough decode is implied. 306 + * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability 307 + * Structure) single ported host-bridges need not publish a decoder 308 + * capability when a passthrough decode can be assumed, i.e. all 309 + * transactions that the uport sees are claimed and passed to the single 310 + * dport. Disable the range until the first CXL region is enumerated / 311 + * activated. 322 312 */ 323 - if (ctx.count == 1) { 324 - cxld = devm_cxl_add_passthrough_decoder(host, port); 325 - if (IS_ERR(cxld)) 326 - return PTR_ERR(cxld); 313 + cxld = cxl_decoder_alloc(port, 1); 314 + if (IS_ERR(cxld)) 315 + return PTR_ERR(cxld); 327 316 317 + cxld->interleave_ways = 1; 318 + cxld->interleave_granularity = PAGE_SIZE; 319 + cxld->target_type = CXL_DECODER_EXPANDER; 320 + cxld->range = (struct range) { 321 + .start = 0, 322 + .end = -1, 323 + }; 324 + 325 + device_lock(&port->dev); 326 + dport = list_first_entry(&port->dports, typeof(*dport), list); 327 + device_unlock(&port->dev); 328 + 329 + single_port_map[0] = dport->port_id; 330 + 331 + rc = cxl_decoder_add(cxld, single_port_map); 332 + if (rc) 333 + put_device(&cxld->dev); 334 + else 335 + rc = cxl_decoder_autoremove(host, cxld); 336 + 337 + if (rc == 0) 328 338 dev_dbg(host, "add: %s\n", dev_name(&cxld->dev)); 329 - } 330 - 331 - return 0; 339 + return rc; 332 340 } 333 341 334 342 static int add_host_bridge_dport(struct device *match, void *arg) ··· 363 323 struct acpi_cedt_chbs *chbs; 364 324 struct cxl_port *root_port = arg; 365 325 struct device *host = root_port->dev.parent; 366 - struct acpi_device *bridge = to_cxl_host_bridge(match); 326 + struct acpi_device *bridge = to_cxl_host_bridge(host, match); 367 327 368 328 if (!bridge) 369 329 return 0; ··· 377 337 } 378 338 379 339 chbs = cxl_acpi_match_chbs(host, uid); 380 - if (IS_ERR(chbs)) 381 - dev_dbg(host, "No CHBS found for Host Bridge: %s\n", 382 - dev_name(match)); 340 + if (IS_ERR(chbs)) { 341 + dev_warn(host, "No CHBS found for Host Bridge: %s\n", 342 + dev_name(match)); 343 + return 0; 344 + } 383 345 384 346 rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs)); 385 347 if (rc) { ··· 417 375 return 1; 418 376 } 419 377 378 + static u32 cedt_instance(struct platform_device *pdev) 379 + { 380 + const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev); 381 + 382 + if (native_acpi0017 && *native_acpi0017) 383 + return 0; 384 + 385 + /* for cxl_test request a non-canonical instance */ 386 + return U32_MAX; 387 + } 388 + 420 389 static int cxl_acpi_probe(struct platform_device *pdev) 421 390 { 422 391 int rc; ··· 441 388 return PTR_ERR(root_port); 442 389 dev_dbg(host, "add: %s\n", dev_name(&root_port->dev)); 443 390 444 - status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt); 391 + status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt); 445 392 if (ACPI_FAILURE(status)) 446 393 return -ENXIO; 447 394 ··· 472 419 return 0; 473 420 } 474 421 422 + static bool native_acpi0017 = true; 423 + 475 424 static const struct acpi_device_id cxl_acpi_ids[] = { 476 - { "ACPI0017", 0 }, 477 - { "", 0 }, 425 + { "ACPI0017", (unsigned long) &native_acpi0017 }, 426 + { }, 478 427 }; 479 428 MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids); 480 429
+1
drivers/cxl/core/Makefile
··· 6 6 cxl_core-y += pmem.o 7 7 cxl_core-y += regs.o 8 8 cxl_core-y += memdev.o 9 + cxl_core-y += mbox.o
+71 -54
drivers/cxl/core/bus.c
··· 453 453 } 454 454 EXPORT_SYMBOL_GPL(cxl_add_dport); 455 455 456 - static struct cxl_decoder * 457 - cxl_decoder_alloc(struct cxl_port *port, int nr_targets, resource_size_t base, 458 - resource_size_t len, int interleave_ways, 459 - int interleave_granularity, enum cxl_decoder_type type, 460 - unsigned long flags) 456 + static int decoder_populate_targets(struct cxl_decoder *cxld, 457 + struct cxl_port *port, int *target_map) 461 458 { 462 - struct cxl_decoder *cxld; 459 + int rc = 0, i; 460 + 461 + if (!target_map) 462 + return 0; 463 + 464 + device_lock(&port->dev); 465 + if (list_empty(&port->dports)) { 466 + rc = -EINVAL; 467 + goto out_unlock; 468 + } 469 + 470 + for (i = 0; i < cxld->nr_targets; i++) { 471 + struct cxl_dport *dport = find_dport(port, target_map[i]); 472 + 473 + if (!dport) { 474 + rc = -ENXIO; 475 + goto out_unlock; 476 + } 477 + cxld->target[i] = dport; 478 + } 479 + 480 + out_unlock: 481 + device_unlock(&port->dev); 482 + 483 + return rc; 484 + } 485 + 486 + struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets) 487 + { 488 + struct cxl_decoder *cxld, cxld_const_init = { 489 + .nr_targets = nr_targets, 490 + }; 463 491 struct device *dev; 464 492 int rc = 0; 465 493 466 - if (interleave_ways < 1) 494 + if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1) 467 495 return ERR_PTR(-EINVAL); 468 - 469 - device_lock(&port->dev); 470 - if (list_empty(&port->dports)) 471 - rc = -EINVAL; 472 - device_unlock(&port->dev); 473 - if (rc) 474 - return ERR_PTR(rc); 475 496 476 497 cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL); 477 498 if (!cxld) 478 499 return ERR_PTR(-ENOMEM); 500 + memcpy(cxld, &cxld_const_init, sizeof(cxld_const_init)); 479 501 480 502 rc = ida_alloc(&port->decoder_ida, GFP_KERNEL); 481 503 if (rc < 0) 482 504 goto err; 483 505 484 - *cxld = (struct cxl_decoder) { 485 - .id = rc, 486 - .range = { 487 - .start = base, 488 - .end = base + len - 1, 489 - }, 490 - .flags = flags, 491 - .interleave_ways = interleave_ways, 492 - .interleave_granularity = interleave_granularity, 493 - .target_type = type, 494 - }; 495 - 496 - /* handle implied target_list */ 497 - if (interleave_ways == 1) 498 - cxld->target[0] = 499 - list_first_entry(&port->dports, struct cxl_dport, list); 506 + cxld->id = rc; 500 507 dev = &cxld->dev; 501 508 device_initialize(dev); 502 509 device_set_pm_not_required(dev); ··· 521 514 kfree(cxld); 522 515 return ERR_PTR(rc); 523 516 } 517 + EXPORT_SYMBOL_GPL(cxl_decoder_alloc); 524 518 525 - struct cxl_decoder * 526 - devm_cxl_add_decoder(struct device *host, struct cxl_port *port, int nr_targets, 527 - resource_size_t base, resource_size_t len, 528 - int interleave_ways, int interleave_granularity, 529 - enum cxl_decoder_type type, unsigned long flags) 519 + int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map) 530 520 { 531 - struct cxl_decoder *cxld; 521 + struct cxl_port *port; 532 522 struct device *dev; 533 523 int rc; 534 524 535 - cxld = cxl_decoder_alloc(port, nr_targets, base, len, interleave_ways, 536 - interleave_granularity, type, flags); 537 - if (IS_ERR(cxld)) 538 - return cxld; 525 + if (WARN_ON_ONCE(!cxld)) 526 + return -EINVAL; 527 + 528 + if (WARN_ON_ONCE(IS_ERR(cxld))) 529 + return PTR_ERR(cxld); 530 + 531 + if (cxld->interleave_ways < 1) 532 + return -EINVAL; 533 + 534 + port = to_cxl_port(cxld->dev.parent); 535 + rc = decoder_populate_targets(cxld, port, target_map); 536 + if (rc) 537 + return rc; 539 538 540 539 dev = &cxld->dev; 541 540 rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id); 542 541 if (rc) 543 - goto err; 542 + return rc; 544 543 545 - rc = device_add(dev); 546 - if (rc) 547 - goto err; 548 - 549 - rc = devm_add_action_or_reset(host, unregister_cxl_dev, dev); 550 - if (rc) 551 - return ERR_PTR(rc); 552 - return cxld; 553 - 554 - err: 555 - put_device(dev); 556 - return ERR_PTR(rc); 544 + return device_add(dev); 557 545 } 558 - EXPORT_SYMBOL_GPL(devm_cxl_add_decoder); 546 + EXPORT_SYMBOL_GPL(cxl_decoder_add); 547 + 548 + static void cxld_unregister(void *dev) 549 + { 550 + device_unregister(dev); 551 + } 552 + 553 + int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld) 554 + { 555 + return devm_add_action_or_reset(host, cxld_unregister, &cxld->dev); 556 + } 557 + EXPORT_SYMBOL_GPL(cxl_decoder_autoremove); 559 558 560 559 /** 561 560 * __cxl_driver_register - register a driver for the cxl bus ··· 648 635 { 649 636 int rc; 650 637 638 + cxl_mbox_init(); 639 + 651 640 rc = cxl_memdev_init(); 652 641 if (rc) 653 642 return rc; ··· 661 646 662 647 err: 663 648 cxl_memdev_exit(); 649 + cxl_mbox_exit(); 664 650 return rc; 665 651 } 666 652 ··· 669 653 { 670 654 bus_unregister(&cxl_bus_type); 671 655 cxl_memdev_exit(); 656 + cxl_mbox_exit(); 672 657 } 673 658 674 659 module_init(cxl_core_init);
+7 -4
drivers/cxl/core/core.h
··· 9 9 10 10 extern struct attribute_group cxl_base_attribute_group; 11 11 12 - static inline void unregister_cxl_dev(void *dev) 13 - { 14 - device_unregister(dev); 15 - } 12 + struct cxl_send_command; 13 + struct cxl_mem_query_commands; 14 + int cxl_query_cmd(struct cxl_memdev *cxlmd, 15 + struct cxl_mem_query_commands __user *q); 16 + int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s); 16 17 17 18 int cxl_memdev_init(void); 18 19 void cxl_memdev_exit(void); 20 + void cxl_mbox_init(void); 21 + void cxl_mbox_exit(void); 19 22 20 23 #endif /* __CXL_CORE_H__ */
+787
drivers/cxl/core/mbox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ 3 + #include <linux/io-64-nonatomic-lo-hi.h> 4 + #include <linux/security.h> 5 + #include <linux/debugfs.h> 6 + #include <linux/mutex.h> 7 + #include <cxlmem.h> 8 + #include <cxl.h> 9 + 10 + #include "core.h" 11 + 12 + static bool cxl_raw_allow_all; 13 + 14 + /** 15 + * DOC: cxl mbox 16 + * 17 + * Core implementation of the CXL 2.0 Type-3 Memory Device Mailbox. The 18 + * implementation is used by the cxl_pci driver to initialize the device 19 + * and implement the cxl_mem.h IOCTL UAPI. It also implements the 20 + * backend of the cxl_pmem_ctl() transport for LIBNVDIMM. 21 + */ 22 + 23 + #define cxl_for_each_cmd(cmd) \ 24 + for ((cmd) = &cxl_mem_commands[0]; \ 25 + ((cmd) - cxl_mem_commands) < ARRAY_SIZE(cxl_mem_commands); (cmd)++) 26 + 27 + #define CXL_CMD(_id, sin, sout, _flags) \ 28 + [CXL_MEM_COMMAND_ID_##_id] = { \ 29 + .info = { \ 30 + .id = CXL_MEM_COMMAND_ID_##_id, \ 31 + .size_in = sin, \ 32 + .size_out = sout, \ 33 + }, \ 34 + .opcode = CXL_MBOX_OP_##_id, \ 35 + .flags = _flags, \ 36 + } 37 + 38 + /* 39 + * This table defines the supported mailbox commands for the driver. This table 40 + * is made up of a UAPI structure. Non-negative values as parameters in the 41 + * table will be validated against the user's input. For example, if size_in is 42 + * 0, and the user passed in 1, it is an error. 43 + */ 44 + static struct cxl_mem_command cxl_mem_commands[CXL_MEM_COMMAND_ID_MAX] = { 45 + CXL_CMD(IDENTIFY, 0, 0x43, CXL_CMD_FLAG_FORCE_ENABLE), 46 + #ifdef CONFIG_CXL_MEM_RAW_COMMANDS 47 + CXL_CMD(RAW, ~0, ~0, 0), 48 + #endif 49 + CXL_CMD(GET_SUPPORTED_LOGS, 0, ~0, CXL_CMD_FLAG_FORCE_ENABLE), 50 + CXL_CMD(GET_FW_INFO, 0, 0x50, 0), 51 + CXL_CMD(GET_PARTITION_INFO, 0, 0x20, 0), 52 + CXL_CMD(GET_LSA, 0x8, ~0, 0), 53 + CXL_CMD(GET_HEALTH_INFO, 0, 0x12, 0), 54 + CXL_CMD(GET_LOG, 0x18, ~0, CXL_CMD_FLAG_FORCE_ENABLE), 55 + CXL_CMD(SET_PARTITION_INFO, 0x0a, 0, 0), 56 + CXL_CMD(SET_LSA, ~0, 0, 0), 57 + CXL_CMD(GET_ALERT_CONFIG, 0, 0x10, 0), 58 + CXL_CMD(SET_ALERT_CONFIG, 0xc, 0, 0), 59 + CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0), 60 + CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0), 61 + CXL_CMD(GET_POISON, 0x10, ~0, 0), 62 + CXL_CMD(INJECT_POISON, 0x8, 0, 0), 63 + CXL_CMD(CLEAR_POISON, 0x48, 0, 0), 64 + CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0), 65 + CXL_CMD(SCAN_MEDIA, 0x11, 0, 0), 66 + CXL_CMD(GET_SCAN_MEDIA, 0, ~0, 0), 67 + }; 68 + 69 + /* 70 + * Commands that RAW doesn't permit. The rationale for each: 71 + * 72 + * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment / 73 + * coordination of transaction timeout values at the root bridge level. 74 + * 75 + * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live 76 + * and needs to be coordinated with HDM updates. 77 + * 78 + * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the 79 + * driver and any writes from userspace invalidates those contents. 80 + * 81 + * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes 82 + * to the device after it is marked clean, userspace can not make that 83 + * assertion. 84 + * 85 + * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that 86 + * is kept up to date with patrol notifications and error management. 87 + */ 88 + static u16 cxl_disabled_raw_commands[] = { 89 + CXL_MBOX_OP_ACTIVATE_FW, 90 + CXL_MBOX_OP_SET_PARTITION_INFO, 91 + CXL_MBOX_OP_SET_LSA, 92 + CXL_MBOX_OP_SET_SHUTDOWN_STATE, 93 + CXL_MBOX_OP_SCAN_MEDIA, 94 + CXL_MBOX_OP_GET_SCAN_MEDIA, 95 + }; 96 + 97 + /* 98 + * Command sets that RAW doesn't permit. All opcodes in this set are 99 + * disabled because they pass plain text security payloads over the 100 + * user/kernel boundary. This functionality is intended to be wrapped 101 + * behind the keys ABI which allows for encrypted payloads in the UAPI 102 + */ 103 + static u8 security_command_sets[] = { 104 + 0x44, /* Sanitize */ 105 + 0x45, /* Persistent Memory Data-at-rest Security */ 106 + 0x46, /* Security Passthrough */ 107 + }; 108 + 109 + static bool cxl_is_security_command(u16 opcode) 110 + { 111 + int i; 112 + 113 + for (i = 0; i < ARRAY_SIZE(security_command_sets); i++) 114 + if (security_command_sets[i] == (opcode >> 8)) 115 + return true; 116 + return false; 117 + } 118 + 119 + static struct cxl_mem_command *cxl_mem_find_command(u16 opcode) 120 + { 121 + struct cxl_mem_command *c; 122 + 123 + cxl_for_each_cmd(c) 124 + if (c->opcode == opcode) 125 + return c; 126 + 127 + return NULL; 128 + } 129 + 130 + /** 131 + * cxl_mem_mbox_send_cmd() - Send a mailbox command to a memory device. 132 + * @cxlm: The CXL memory device to communicate with. 133 + * @opcode: Opcode for the mailbox command. 134 + * @in: The input payload for the mailbox command. 135 + * @in_size: The length of the input payload 136 + * @out: Caller allocated buffer for the output. 137 + * @out_size: Expected size of output. 138 + * 139 + * Context: Any context. Will acquire and release mbox_mutex. 140 + * Return: 141 + * * %>=0 - Number of bytes returned in @out. 142 + * * %-E2BIG - Payload is too large for hardware. 143 + * * %-EBUSY - Couldn't acquire exclusive mailbox access. 144 + * * %-EFAULT - Hardware error occurred. 145 + * * %-ENXIO - Command completed, but device reported an error. 146 + * * %-EIO - Unexpected output size. 147 + * 148 + * Mailbox commands may execute successfully yet the device itself reported an 149 + * error. While this distinction can be useful for commands from userspace, the 150 + * kernel will only be able to use results when both are successful. 151 + * 152 + * See __cxl_mem_mbox_send_cmd() 153 + */ 154 + int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in, 155 + size_t in_size, void *out, size_t out_size) 156 + { 157 + const struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); 158 + struct cxl_mbox_cmd mbox_cmd = { 159 + .opcode = opcode, 160 + .payload_in = in, 161 + .size_in = in_size, 162 + .size_out = out_size, 163 + .payload_out = out, 164 + }; 165 + int rc; 166 + 167 + if (out_size > cxlm->payload_size) 168 + return -E2BIG; 169 + 170 + rc = cxlm->mbox_send(cxlm, &mbox_cmd); 171 + if (rc) 172 + return rc; 173 + 174 + /* TODO: Map return code to proper kernel style errno */ 175 + if (mbox_cmd.return_code != CXL_MBOX_SUCCESS) 176 + return -ENXIO; 177 + 178 + /* 179 + * Variable sized commands can't be validated and so it's up to the 180 + * caller to do that if they wish. 181 + */ 182 + if (cmd->info.size_out >= 0 && mbox_cmd.size_out != out_size) 183 + return -EIO; 184 + 185 + return 0; 186 + } 187 + EXPORT_SYMBOL_GPL(cxl_mem_mbox_send_cmd); 188 + 189 + static bool cxl_mem_raw_command_allowed(u16 opcode) 190 + { 191 + int i; 192 + 193 + if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS)) 194 + return false; 195 + 196 + if (security_locked_down(LOCKDOWN_PCI_ACCESS)) 197 + return false; 198 + 199 + if (cxl_raw_allow_all) 200 + return true; 201 + 202 + if (cxl_is_security_command(opcode)) 203 + return false; 204 + 205 + for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++) 206 + if (cxl_disabled_raw_commands[i] == opcode) 207 + return false; 208 + 209 + return true; 210 + } 211 + 212 + /** 213 + * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND. 214 + * @cxlm: &struct cxl_mem device whose mailbox will be used. 215 + * @send_cmd: &struct cxl_send_command copied in from userspace. 216 + * @out_cmd: Sanitized and populated &struct cxl_mem_command. 217 + * 218 + * Return: 219 + * * %0 - @out_cmd is ready to send. 220 + * * %-ENOTTY - Invalid command specified. 221 + * * %-EINVAL - Reserved fields or invalid values were used. 222 + * * %-ENOMEM - Input or output buffer wasn't sized properly. 223 + * * %-EPERM - Attempted to use a protected command. 224 + * * %-EBUSY - Kernel has claimed exclusive access to this opcode 225 + * 226 + * The result of this command is a fully validated command in @out_cmd that is 227 + * safe to send to the hardware. 228 + * 229 + * See handle_mailbox_cmd_from_user() 230 + */ 231 + static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm, 232 + const struct cxl_send_command *send_cmd, 233 + struct cxl_mem_command *out_cmd) 234 + { 235 + const struct cxl_command_info *info; 236 + struct cxl_mem_command *c; 237 + 238 + if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX) 239 + return -ENOTTY; 240 + 241 + /* 242 + * The user can never specify an input payload larger than what hardware 243 + * supports, but output can be arbitrarily large (simply write out as 244 + * much data as the hardware provides). 245 + */ 246 + if (send_cmd->in.size > cxlm->payload_size) 247 + return -EINVAL; 248 + 249 + /* 250 + * Checks are bypassed for raw commands but a WARN/taint will occur 251 + * later in the callchain 252 + */ 253 + if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) { 254 + const struct cxl_mem_command temp = { 255 + .info = { 256 + .id = CXL_MEM_COMMAND_ID_RAW, 257 + .flags = 0, 258 + .size_in = send_cmd->in.size, 259 + .size_out = send_cmd->out.size, 260 + }, 261 + .opcode = send_cmd->raw.opcode 262 + }; 263 + 264 + if (send_cmd->raw.rsvd) 265 + return -EINVAL; 266 + 267 + /* 268 + * Unlike supported commands, the output size of RAW commands 269 + * gets passed along without further checking, so it must be 270 + * validated here. 271 + */ 272 + if (send_cmd->out.size > cxlm->payload_size) 273 + return -EINVAL; 274 + 275 + if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode)) 276 + return -EPERM; 277 + 278 + memcpy(out_cmd, &temp, sizeof(temp)); 279 + 280 + return 0; 281 + } 282 + 283 + if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK) 284 + return -EINVAL; 285 + 286 + if (send_cmd->rsvd) 287 + return -EINVAL; 288 + 289 + if (send_cmd->in.rsvd || send_cmd->out.rsvd) 290 + return -EINVAL; 291 + 292 + /* Convert user's command into the internal representation */ 293 + c = &cxl_mem_commands[send_cmd->id]; 294 + info = &c->info; 295 + 296 + /* Check that the command is enabled for hardware */ 297 + if (!test_bit(info->id, cxlm->enabled_cmds)) 298 + return -ENOTTY; 299 + 300 + /* Check that the command is not claimed for exclusive kernel use */ 301 + if (test_bit(info->id, cxlm->exclusive_cmds)) 302 + return -EBUSY; 303 + 304 + /* Check the input buffer is the expected size */ 305 + if (info->size_in >= 0 && info->size_in != send_cmd->in.size) 306 + return -ENOMEM; 307 + 308 + /* Check the output buffer is at least large enough */ 309 + if (info->size_out >= 0 && send_cmd->out.size < info->size_out) 310 + return -ENOMEM; 311 + 312 + memcpy(out_cmd, c, sizeof(*c)); 313 + out_cmd->info.size_in = send_cmd->in.size; 314 + /* 315 + * XXX: out_cmd->info.size_out will be controlled by the driver, and the 316 + * specified number of bytes @send_cmd->out.size will be copied back out 317 + * to userspace. 318 + */ 319 + 320 + return 0; 321 + } 322 + 323 + int cxl_query_cmd(struct cxl_memdev *cxlmd, 324 + struct cxl_mem_query_commands __user *q) 325 + { 326 + struct device *dev = &cxlmd->dev; 327 + struct cxl_mem_command *cmd; 328 + u32 n_commands; 329 + int j = 0; 330 + 331 + dev_dbg(dev, "Query IOCTL\n"); 332 + 333 + if (get_user(n_commands, &q->n_commands)) 334 + return -EFAULT; 335 + 336 + /* returns the total number if 0 elements are requested. */ 337 + if (n_commands == 0) 338 + return put_user(ARRAY_SIZE(cxl_mem_commands), &q->n_commands); 339 + 340 + /* 341 + * otherwise, return max(n_commands, total commands) cxl_command_info 342 + * structures. 343 + */ 344 + cxl_for_each_cmd(cmd) { 345 + const struct cxl_command_info *info = &cmd->info; 346 + 347 + if (copy_to_user(&q->commands[j++], info, sizeof(*info))) 348 + return -EFAULT; 349 + 350 + if (j == n_commands) 351 + break; 352 + } 353 + 354 + return 0; 355 + } 356 + 357 + /** 358 + * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace. 359 + * @cxlm: The CXL memory device to communicate with. 360 + * @cmd: The validated command. 361 + * @in_payload: Pointer to userspace's input payload. 362 + * @out_payload: Pointer to userspace's output payload. 363 + * @size_out: (Input) Max payload size to copy out. 364 + * (Output) Payload size hardware generated. 365 + * @retval: Hardware generated return code from the operation. 366 + * 367 + * Return: 368 + * * %0 - Mailbox transaction succeeded. This implies the mailbox 369 + * protocol completed successfully not that the operation itself 370 + * was successful. 371 + * * %-ENOMEM - Couldn't allocate a bounce buffer. 372 + * * %-EFAULT - Something happened with copy_to/from_user. 373 + * * %-EINTR - Mailbox acquisition interrupted. 374 + * * %-EXXX - Transaction level failures. 375 + * 376 + * Creates the appropriate mailbox command and dispatches it on behalf of a 377 + * userspace request. The input and output payloads are copied between 378 + * userspace. 379 + * 380 + * See cxl_send_cmd(). 381 + */ 382 + static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm, 383 + const struct cxl_mem_command *cmd, 384 + u64 in_payload, u64 out_payload, 385 + s32 *size_out, u32 *retval) 386 + { 387 + struct device *dev = cxlm->dev; 388 + struct cxl_mbox_cmd mbox_cmd = { 389 + .opcode = cmd->opcode, 390 + .size_in = cmd->info.size_in, 391 + .size_out = cmd->info.size_out, 392 + }; 393 + int rc; 394 + 395 + if (cmd->info.size_out) { 396 + mbox_cmd.payload_out = kvzalloc(cmd->info.size_out, GFP_KERNEL); 397 + if (!mbox_cmd.payload_out) 398 + return -ENOMEM; 399 + } 400 + 401 + if (cmd->info.size_in) { 402 + mbox_cmd.payload_in = vmemdup_user(u64_to_user_ptr(in_payload), 403 + cmd->info.size_in); 404 + if (IS_ERR(mbox_cmd.payload_in)) { 405 + kvfree(mbox_cmd.payload_out); 406 + return PTR_ERR(mbox_cmd.payload_in); 407 + } 408 + } 409 + 410 + dev_dbg(dev, 411 + "Submitting %s command for user\n" 412 + "\topcode: %x\n" 413 + "\tsize: %ub\n", 414 + cxl_command_names[cmd->info.id].name, mbox_cmd.opcode, 415 + cmd->info.size_in); 416 + 417 + dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW, 418 + "raw command path used\n"); 419 + 420 + rc = cxlm->mbox_send(cxlm, &mbox_cmd); 421 + if (rc) 422 + goto out; 423 + 424 + /* 425 + * @size_out contains the max size that's allowed to be written back out 426 + * to userspace. While the payload may have written more output than 427 + * this it will have to be ignored. 428 + */ 429 + if (mbox_cmd.size_out) { 430 + dev_WARN_ONCE(dev, mbox_cmd.size_out > *size_out, 431 + "Invalid return size\n"); 432 + if (copy_to_user(u64_to_user_ptr(out_payload), 433 + mbox_cmd.payload_out, mbox_cmd.size_out)) { 434 + rc = -EFAULT; 435 + goto out; 436 + } 437 + } 438 + 439 + *size_out = mbox_cmd.size_out; 440 + *retval = mbox_cmd.return_code; 441 + 442 + out: 443 + kvfree(mbox_cmd.payload_in); 444 + kvfree(mbox_cmd.payload_out); 445 + return rc; 446 + } 447 + 448 + int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s) 449 + { 450 + struct cxl_mem *cxlm = cxlmd->cxlm; 451 + struct device *dev = &cxlmd->dev; 452 + struct cxl_send_command send; 453 + struct cxl_mem_command c; 454 + int rc; 455 + 456 + dev_dbg(dev, "Send IOCTL\n"); 457 + 458 + if (copy_from_user(&send, s, sizeof(send))) 459 + return -EFAULT; 460 + 461 + rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c); 462 + if (rc) 463 + return rc; 464 + 465 + /* Prepare to handle a full payload for variable sized output */ 466 + if (c.info.size_out < 0) 467 + c.info.size_out = cxlm->payload_size; 468 + 469 + rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload, 470 + send.out.payload, &send.out.size, 471 + &send.retval); 472 + if (rc) 473 + return rc; 474 + 475 + if (copy_to_user(s, &send, sizeof(send))) 476 + return -EFAULT; 477 + 478 + return 0; 479 + } 480 + 481 + static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out) 482 + { 483 + u32 remaining = size; 484 + u32 offset = 0; 485 + 486 + while (remaining) { 487 + u32 xfer_size = min_t(u32, remaining, cxlm->payload_size); 488 + struct cxl_mbox_get_log log = { 489 + .uuid = *uuid, 490 + .offset = cpu_to_le32(offset), 491 + .length = cpu_to_le32(xfer_size) 492 + }; 493 + int rc; 494 + 495 + rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LOG, &log, 496 + sizeof(log), out, xfer_size); 497 + if (rc < 0) 498 + return rc; 499 + 500 + out += xfer_size; 501 + remaining -= xfer_size; 502 + offset += xfer_size; 503 + } 504 + 505 + return 0; 506 + } 507 + 508 + /** 509 + * cxl_walk_cel() - Walk through the Command Effects Log. 510 + * @cxlm: Device. 511 + * @size: Length of the Command Effects Log. 512 + * @cel: CEL 513 + * 514 + * Iterate over each entry in the CEL and determine if the driver supports the 515 + * command. If so, the command is enabled for the device and can be used later. 516 + */ 517 + static void cxl_walk_cel(struct cxl_mem *cxlm, size_t size, u8 *cel) 518 + { 519 + struct cxl_cel_entry *cel_entry; 520 + const int cel_entries = size / sizeof(*cel_entry); 521 + int i; 522 + 523 + cel_entry = (struct cxl_cel_entry *) cel; 524 + 525 + for (i = 0; i < cel_entries; i++) { 526 + u16 opcode = le16_to_cpu(cel_entry[i].opcode); 527 + struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); 528 + 529 + if (!cmd) { 530 + dev_dbg(cxlm->dev, 531 + "Opcode 0x%04x unsupported by driver", opcode); 532 + continue; 533 + } 534 + 535 + set_bit(cmd->info.id, cxlm->enabled_cmds); 536 + } 537 + } 538 + 539 + static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_mem *cxlm) 540 + { 541 + struct cxl_mbox_get_supported_logs *ret; 542 + int rc; 543 + 544 + ret = kvmalloc(cxlm->payload_size, GFP_KERNEL); 545 + if (!ret) 546 + return ERR_PTR(-ENOMEM); 547 + 548 + rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL, 549 + 0, ret, cxlm->payload_size); 550 + if (rc < 0) { 551 + kvfree(ret); 552 + return ERR_PTR(rc); 553 + } 554 + 555 + return ret; 556 + } 557 + 558 + enum { 559 + CEL_UUID, 560 + VENDOR_DEBUG_UUID, 561 + }; 562 + 563 + /* See CXL 2.0 Table 170. Get Log Input Payload */ 564 + static const uuid_t log_uuid[] = { 565 + [CEL_UUID] = DEFINE_CXL_CEL_UUID, 566 + [VENDOR_DEBUG_UUID] = DEFINE_CXL_VENDOR_DEBUG_UUID, 567 + }; 568 + 569 + /** 570 + * cxl_mem_enumerate_cmds() - Enumerate commands for a device. 571 + * @cxlm: The device. 572 + * 573 + * Returns 0 if enumerate completed successfully. 574 + * 575 + * CXL devices have optional support for certain commands. This function will 576 + * determine the set of supported commands for the hardware and update the 577 + * enabled_cmds bitmap in the @cxlm. 578 + */ 579 + int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm) 580 + { 581 + struct cxl_mbox_get_supported_logs *gsl; 582 + struct device *dev = cxlm->dev; 583 + struct cxl_mem_command *cmd; 584 + int i, rc; 585 + 586 + gsl = cxl_get_gsl(cxlm); 587 + if (IS_ERR(gsl)) 588 + return PTR_ERR(gsl); 589 + 590 + rc = -ENOENT; 591 + for (i = 0; i < le16_to_cpu(gsl->entries); i++) { 592 + u32 size = le32_to_cpu(gsl->entry[i].size); 593 + uuid_t uuid = gsl->entry[i].uuid; 594 + u8 *log; 595 + 596 + dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size); 597 + 598 + if (!uuid_equal(&uuid, &log_uuid[CEL_UUID])) 599 + continue; 600 + 601 + log = kvmalloc(size, GFP_KERNEL); 602 + if (!log) { 603 + rc = -ENOMEM; 604 + goto out; 605 + } 606 + 607 + rc = cxl_xfer_log(cxlm, &uuid, size, log); 608 + if (rc) { 609 + kvfree(log); 610 + goto out; 611 + } 612 + 613 + cxl_walk_cel(cxlm, size, log); 614 + kvfree(log); 615 + 616 + /* In case CEL was bogus, enable some default commands. */ 617 + cxl_for_each_cmd(cmd) 618 + if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE) 619 + set_bit(cmd->info.id, cxlm->enabled_cmds); 620 + 621 + /* Found the required CEL */ 622 + rc = 0; 623 + } 624 + 625 + out: 626 + kvfree(gsl); 627 + return rc; 628 + } 629 + EXPORT_SYMBOL_GPL(cxl_mem_enumerate_cmds); 630 + 631 + /** 632 + * cxl_mem_get_partition_info - Get partition info 633 + * @cxlm: cxl_mem instance to update partition info 634 + * 635 + * Retrieve the current partition info for the device specified. The active 636 + * values are the current capacity in bytes. If not 0, the 'next' values are 637 + * the pending values, in bytes, which take affect on next cold reset. 638 + * 639 + * Return: 0 if no error: or the result of the mailbox command. 640 + * 641 + * See CXL @8.2.9.5.2.1 Get Partition Info 642 + */ 643 + static int cxl_mem_get_partition_info(struct cxl_mem *cxlm) 644 + { 645 + struct cxl_mbox_get_partition_info { 646 + __le64 active_volatile_cap; 647 + __le64 active_persistent_cap; 648 + __le64 next_volatile_cap; 649 + __le64 next_persistent_cap; 650 + } __packed pi; 651 + int rc; 652 + 653 + rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO, 654 + NULL, 0, &pi, sizeof(pi)); 655 + 656 + if (rc) 657 + return rc; 658 + 659 + cxlm->active_volatile_bytes = 660 + le64_to_cpu(pi.active_volatile_cap) * CXL_CAPACITY_MULTIPLIER; 661 + cxlm->active_persistent_bytes = 662 + le64_to_cpu(pi.active_persistent_cap) * CXL_CAPACITY_MULTIPLIER; 663 + cxlm->next_volatile_bytes = 664 + le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER; 665 + cxlm->next_persistent_bytes = 666 + le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER; 667 + 668 + return 0; 669 + } 670 + 671 + /** 672 + * cxl_mem_identify() - Send the IDENTIFY command to the device. 673 + * @cxlm: The device to identify. 674 + * 675 + * Return: 0 if identify was executed successfully. 676 + * 677 + * This will dispatch the identify command to the device and on success populate 678 + * structures to be exported to sysfs. 679 + */ 680 + int cxl_mem_identify(struct cxl_mem *cxlm) 681 + { 682 + /* See CXL 2.0 Table 175 Identify Memory Device Output Payload */ 683 + struct cxl_mbox_identify id; 684 + int rc; 685 + 686 + rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id, 687 + sizeof(id)); 688 + if (rc < 0) 689 + return rc; 690 + 691 + cxlm->total_bytes = 692 + le64_to_cpu(id.total_capacity) * CXL_CAPACITY_MULTIPLIER; 693 + cxlm->volatile_only_bytes = 694 + le64_to_cpu(id.volatile_capacity) * CXL_CAPACITY_MULTIPLIER; 695 + cxlm->persistent_only_bytes = 696 + le64_to_cpu(id.persistent_capacity) * CXL_CAPACITY_MULTIPLIER; 697 + cxlm->partition_align_bytes = 698 + le64_to_cpu(id.partition_align) * CXL_CAPACITY_MULTIPLIER; 699 + 700 + dev_dbg(cxlm->dev, 701 + "Identify Memory Device\n" 702 + " total_bytes = %#llx\n" 703 + " volatile_only_bytes = %#llx\n" 704 + " persistent_only_bytes = %#llx\n" 705 + " partition_align_bytes = %#llx\n", 706 + cxlm->total_bytes, cxlm->volatile_only_bytes, 707 + cxlm->persistent_only_bytes, cxlm->partition_align_bytes); 708 + 709 + cxlm->lsa_size = le32_to_cpu(id.lsa_size); 710 + memcpy(cxlm->firmware_version, id.fw_revision, sizeof(id.fw_revision)); 711 + 712 + return 0; 713 + } 714 + EXPORT_SYMBOL_GPL(cxl_mem_identify); 715 + 716 + int cxl_mem_create_range_info(struct cxl_mem *cxlm) 717 + { 718 + int rc; 719 + 720 + if (cxlm->partition_align_bytes == 0) { 721 + cxlm->ram_range.start = 0; 722 + cxlm->ram_range.end = cxlm->volatile_only_bytes - 1; 723 + cxlm->pmem_range.start = cxlm->volatile_only_bytes; 724 + cxlm->pmem_range.end = cxlm->volatile_only_bytes + 725 + cxlm->persistent_only_bytes - 1; 726 + return 0; 727 + } 728 + 729 + rc = cxl_mem_get_partition_info(cxlm); 730 + if (rc) { 731 + dev_err(cxlm->dev, "Failed to query partition information\n"); 732 + return rc; 733 + } 734 + 735 + dev_dbg(cxlm->dev, 736 + "Get Partition Info\n" 737 + " active_volatile_bytes = %#llx\n" 738 + " active_persistent_bytes = %#llx\n" 739 + " next_volatile_bytes = %#llx\n" 740 + " next_persistent_bytes = %#llx\n", 741 + cxlm->active_volatile_bytes, cxlm->active_persistent_bytes, 742 + cxlm->next_volatile_bytes, cxlm->next_persistent_bytes); 743 + 744 + cxlm->ram_range.start = 0; 745 + cxlm->ram_range.end = cxlm->active_volatile_bytes - 1; 746 + 747 + cxlm->pmem_range.start = cxlm->active_volatile_bytes; 748 + cxlm->pmem_range.end = 749 + cxlm->active_volatile_bytes + cxlm->active_persistent_bytes - 1; 750 + 751 + return 0; 752 + } 753 + EXPORT_SYMBOL_GPL(cxl_mem_create_range_info); 754 + 755 + struct cxl_mem *cxl_mem_create(struct device *dev) 756 + { 757 + struct cxl_mem *cxlm; 758 + 759 + cxlm = devm_kzalloc(dev, sizeof(*cxlm), GFP_KERNEL); 760 + if (!cxlm) { 761 + dev_err(dev, "No memory available\n"); 762 + return ERR_PTR(-ENOMEM); 763 + } 764 + 765 + mutex_init(&cxlm->mbox_mutex); 766 + cxlm->dev = dev; 767 + 768 + return cxlm; 769 + } 770 + EXPORT_SYMBOL_GPL(cxl_mem_create); 771 + 772 + static struct dentry *cxl_debugfs; 773 + 774 + void __init cxl_mbox_init(void) 775 + { 776 + struct dentry *mbox_debugfs; 777 + 778 + cxl_debugfs = debugfs_create_dir("cxl", NULL); 779 + mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs); 780 + debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs, 781 + &cxl_raw_allow_all); 782 + } 783 + 784 + void cxl_mbox_exit(void) 785 + { 786 + debugfs_remove_recursive(cxl_debugfs); 787 + }
+106 -12
drivers/cxl/core/memdev.c
··· 8 8 #include <cxlmem.h> 9 9 #include "core.h" 10 10 11 + static DECLARE_RWSEM(cxl_memdev_rwsem); 12 + 11 13 /* 12 14 * An entire PCI topology full of devices should be enough for any 13 15 * config ··· 134 132 .groups = cxl_memdev_attribute_groups, 135 133 }; 136 134 135 + /** 136 + * set_exclusive_cxl_commands() - atomically disable user cxl commands 137 + * @cxlm: cxl_mem instance to modify 138 + * @cmds: bitmap of commands to mark exclusive 139 + * 140 + * Grab the cxl_memdev_rwsem in write mode to flush in-flight 141 + * invocations of the ioctl path and then disable future execution of 142 + * commands with the command ids set in @cmds. 143 + */ 144 + void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds) 145 + { 146 + down_write(&cxl_memdev_rwsem); 147 + bitmap_or(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds, 148 + CXL_MEM_COMMAND_ID_MAX); 149 + up_write(&cxl_memdev_rwsem); 150 + } 151 + EXPORT_SYMBOL_GPL(set_exclusive_cxl_commands); 152 + 153 + /** 154 + * clear_exclusive_cxl_commands() - atomically enable user cxl commands 155 + * @cxlm: cxl_mem instance to modify 156 + * @cmds: bitmap of commands to mark available for userspace 157 + */ 158 + void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds) 159 + { 160 + down_write(&cxl_memdev_rwsem); 161 + bitmap_andnot(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds, 162 + CXL_MEM_COMMAND_ID_MAX); 163 + up_write(&cxl_memdev_rwsem); 164 + } 165 + EXPORT_SYMBOL_GPL(clear_exclusive_cxl_commands); 166 + 167 + static void cxl_memdev_shutdown(struct device *dev) 168 + { 169 + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); 170 + 171 + down_write(&cxl_memdev_rwsem); 172 + cxlmd->cxlm = NULL; 173 + up_write(&cxl_memdev_rwsem); 174 + } 175 + 137 176 static void cxl_memdev_unregister(void *_cxlmd) 138 177 { 139 178 struct cxl_memdev *cxlmd = _cxlmd; 140 179 struct device *dev = &cxlmd->dev; 141 - struct cdev *cdev = &cxlmd->cdev; 142 - const struct cdevm_file_operations *cdevm_fops; 143 180 144 - cdevm_fops = container_of(cdev->ops, typeof(*cdevm_fops), fops); 145 - cdevm_fops->shutdown(dev); 146 - 181 + cxl_memdev_shutdown(dev); 147 182 cdev_device_del(&cxlmd->cdev, dev); 148 183 put_device(dev); 149 184 } ··· 188 149 static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm, 189 150 const struct file_operations *fops) 190 151 { 191 - struct pci_dev *pdev = cxlm->pdev; 192 152 struct cxl_memdev *cxlmd; 193 153 struct device *dev; 194 154 struct cdev *cdev; ··· 204 166 205 167 dev = &cxlmd->dev; 206 168 device_initialize(dev); 207 - dev->parent = &pdev->dev; 169 + dev->parent = cxlm->dev; 208 170 dev->bus = &cxl_bus_type; 209 171 dev->devt = MKDEV(cxl_mem_major, cxlmd->id); 210 172 dev->type = &cxl_memdev_type; ··· 219 181 return ERR_PTR(rc); 220 182 } 221 183 184 + static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd, 185 + unsigned long arg) 186 + { 187 + switch (cmd) { 188 + case CXL_MEM_QUERY_COMMANDS: 189 + return cxl_query_cmd(cxlmd, (void __user *)arg); 190 + case CXL_MEM_SEND_COMMAND: 191 + return cxl_send_cmd(cxlmd, (void __user *)arg); 192 + default: 193 + return -ENOTTY; 194 + } 195 + } 196 + 197 + static long cxl_memdev_ioctl(struct file *file, unsigned int cmd, 198 + unsigned long arg) 199 + { 200 + struct cxl_memdev *cxlmd = file->private_data; 201 + int rc = -ENXIO; 202 + 203 + down_read(&cxl_memdev_rwsem); 204 + if (cxlmd->cxlm) 205 + rc = __cxl_memdev_ioctl(cxlmd, cmd, arg); 206 + up_read(&cxl_memdev_rwsem); 207 + 208 + return rc; 209 + } 210 + 211 + static int cxl_memdev_open(struct inode *inode, struct file *file) 212 + { 213 + struct cxl_memdev *cxlmd = 214 + container_of(inode->i_cdev, typeof(*cxlmd), cdev); 215 + 216 + get_device(&cxlmd->dev); 217 + file->private_data = cxlmd; 218 + 219 + return 0; 220 + } 221 + 222 + static int cxl_memdev_release_file(struct inode *inode, struct file *file) 223 + { 224 + struct cxl_memdev *cxlmd = 225 + container_of(inode->i_cdev, typeof(*cxlmd), cdev); 226 + 227 + put_device(&cxlmd->dev); 228 + 229 + return 0; 230 + } 231 + 232 + static const struct file_operations cxl_memdev_fops = { 233 + .owner = THIS_MODULE, 234 + .unlocked_ioctl = cxl_memdev_ioctl, 235 + .open = cxl_memdev_open, 236 + .release = cxl_memdev_release_file, 237 + .compat_ioctl = compat_ptr_ioctl, 238 + .llseek = noop_llseek, 239 + }; 240 + 222 241 struct cxl_memdev * 223 - devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm, 224 - const struct cdevm_file_operations *cdevm_fops) 242 + devm_cxl_add_memdev(struct cxl_mem *cxlm) 225 243 { 226 244 struct cxl_memdev *cxlmd; 227 245 struct device *dev; 228 246 struct cdev *cdev; 229 247 int rc; 230 248 231 - cxlmd = cxl_memdev_alloc(cxlm, &cdevm_fops->fops); 249 + cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops); 232 250 if (IS_ERR(cxlmd)) 233 251 return cxlmd; 234 252 ··· 304 210 if (rc) 305 211 goto err; 306 212 307 - rc = devm_add_action_or_reset(host, cxl_memdev_unregister, cxlmd); 213 + rc = devm_add_action_or_reset(cxlm->dev, cxl_memdev_unregister, cxlmd); 308 214 if (rc) 309 215 return ERR_PTR(rc); 310 216 return cxlmd; ··· 314 220 * The cdev was briefly live, shutdown any ioctl operations that 315 221 * saw that state. 316 222 */ 317 - cdevm_fops->shutdown(dev); 223 + cxl_memdev_shutdown(dev); 318 224 put_device(dev); 319 225 return ERR_PTR(rc); 320 226 }
+37 -2
drivers/cxl/core/pmem.c
··· 2 2 /* Copyright(c) 2020 Intel Corporation. */ 3 3 #include <linux/device.h> 4 4 #include <linux/slab.h> 5 + #include <linux/idr.h> 5 6 #include <cxlmem.h> 6 7 #include <cxl.h> 7 8 #include "core.h" ··· 21 20 * operations, for example, namespace label access commands. 22 21 */ 23 22 23 + static DEFINE_IDA(cxl_nvdimm_bridge_ida); 24 + 24 25 static void cxl_nvdimm_bridge_release(struct device *dev) 25 26 { 26 27 struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev); 27 28 29 + ida_free(&cxl_nvdimm_bridge_ida, cxl_nvb->id); 28 30 kfree(cxl_nvb); 29 31 } 30 32 ··· 51 47 } 52 48 EXPORT_SYMBOL_GPL(to_cxl_nvdimm_bridge); 53 49 50 + __mock int match_nvdimm_bridge(struct device *dev, const void *data) 51 + { 52 + return dev->type == &cxl_nvdimm_bridge_type; 53 + } 54 + 55 + struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd) 56 + { 57 + struct device *dev; 58 + 59 + dev = bus_find_device(&cxl_bus_type, NULL, cxl_nvd, match_nvdimm_bridge); 60 + if (!dev) 61 + return NULL; 62 + return to_cxl_nvdimm_bridge(dev); 63 + } 64 + EXPORT_SYMBOL_GPL(cxl_find_nvdimm_bridge); 65 + 54 66 static struct cxl_nvdimm_bridge * 55 67 cxl_nvdimm_bridge_alloc(struct cxl_port *port) 56 68 { 57 69 struct cxl_nvdimm_bridge *cxl_nvb; 58 70 struct device *dev; 71 + int rc; 59 72 60 73 cxl_nvb = kzalloc(sizeof(*cxl_nvb), GFP_KERNEL); 61 74 if (!cxl_nvb) 62 75 return ERR_PTR(-ENOMEM); 76 + 77 + rc = ida_alloc(&cxl_nvdimm_bridge_ida, GFP_KERNEL); 78 + if (rc < 0) 79 + goto err; 80 + cxl_nvb->id = rc; 63 81 64 82 dev = &cxl_nvb->dev; 65 83 cxl_nvb->port = port; ··· 93 67 dev->type = &cxl_nvdimm_bridge_type; 94 68 95 69 return cxl_nvb; 70 + 71 + err: 72 + kfree(cxl_nvb); 73 + return ERR_PTR(rc); 96 74 } 97 75 98 76 static void unregister_nvb(void *_cxl_nvb) ··· 149 119 return cxl_nvb; 150 120 151 121 dev = &cxl_nvb->dev; 152 - rc = dev_set_name(dev, "nvdimm-bridge"); 122 + rc = dev_set_name(dev, "nvdimm-bridge%d", cxl_nvb->id); 153 123 if (rc) 154 124 goto err; 155 125 ··· 222 192 return cxl_nvd; 223 193 } 224 194 195 + static void cxl_nvd_unregister(void *dev) 196 + { 197 + device_unregister(dev); 198 + } 199 + 225 200 /** 226 201 * devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm 227 202 * @host: same host as @cxlmd ··· 256 221 dev_dbg(host, "%s: register %s\n", dev_name(dev->parent), 257 222 dev_name(dev)); 258 223 259 - return devm_add_action_or_reset(host, unregister_cxl_dev, dev); 224 + return devm_add_action_or_reset(host, cxl_nvd_unregister, dev); 260 225 261 226 err: 262 227 put_device(dev);
+39 -19
drivers/cxl/cxl.h
··· 114 114 struct cxl_reg_map memdev; 115 115 }; 116 116 117 + /** 118 + * struct cxl_register_map - DVSEC harvested register block mapping parameters 119 + * @base: virtual base of the register-block-BAR + @block_offset 120 + * @block_offset: offset to start of register block in @barno 121 + * @reg_type: see enum cxl_regloc_type 122 + * @barno: PCI BAR number containing the register block 123 + * @component_map: cxl_reg_map for component registers 124 + * @device_map: cxl_reg_maps for device registers 125 + */ 117 126 struct cxl_register_map { 127 + void __iomem *base; 118 128 u64 block_offset; 119 129 u8 reg_type; 120 130 u8 barno; ··· 165 155 CXL_DECODER_EXPANDER = 3, 166 156 }; 167 157 158 + /* 159 + * Current specification goes up to 8, double that seems a reasonable 160 + * software max for the foreseeable future 161 + */ 162 + #define CXL_DECODER_MAX_INTERLEAVE 16 163 + 168 164 /** 169 165 * struct cxl_decoder - CXL address range decode configuration 170 166 * @dev: this decoder's device ··· 180 164 * @interleave_granularity: data stride per dport 181 165 * @target_type: accelerator vs expander (type2 vs type3) selector 182 166 * @flags: memory type capabilities and locking 167 + * @nr_targets: number of elements in @target 183 168 * @target: active ordered target list in current decoder configuration 184 169 */ 185 170 struct cxl_decoder { ··· 191 174 int interleave_granularity; 192 175 enum cxl_decoder_type target_type; 193 176 unsigned long flags; 177 + const int nr_targets; 194 178 struct cxl_dport *target[]; 195 179 }; 196 180 ··· 204 186 }; 205 187 206 188 struct cxl_nvdimm_bridge { 189 + int id; 207 190 struct device dev; 208 191 struct cxl_port *port; 209 192 struct nvdimm_bus *nvdimm_bus; ··· 217 198 struct device dev; 218 199 struct cxl_memdev *cxlmd; 219 200 struct nvdimm *nvdimm; 201 + }; 202 + 203 + struct cxl_walk_context { 204 + struct device *dev; 205 + struct pci_bus *root; 206 + struct cxl_port *port; 207 + int error; 208 + int count; 220 209 }; 221 210 222 211 /** ··· 273 246 274 247 struct cxl_decoder *to_cxl_decoder(struct device *dev); 275 248 bool is_root_decoder(struct device *dev); 276 - struct cxl_decoder * 277 - devm_cxl_add_decoder(struct device *host, struct cxl_port *port, int nr_targets, 278 - resource_size_t base, resource_size_t len, 279 - int interleave_ways, int interleave_granularity, 280 - enum cxl_decoder_type type, unsigned long flags); 281 - 282 - /* 283 - * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability Structure) 284 - * single ported host-bridges need not publish a decoder capability when a 285 - * passthrough decode can be assumed, i.e. all transactions that the uport sees 286 - * are claimed and passed to the single dport. Default the range a 0-base 287 - * 0-length until the first CXL region is activated. 288 - */ 289 - static inline struct cxl_decoder * 290 - devm_cxl_add_passthrough_decoder(struct device *host, struct cxl_port *port) 291 - { 292 - return devm_cxl_add_decoder(host, port, 1, 0, 0, 1, PAGE_SIZE, 293 - CXL_DECODER_EXPANDER, 0); 294 - } 249 + struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets); 250 + int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map); 251 + int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld); 295 252 296 253 extern struct bus_type cxl_bus_type; 297 254 ··· 309 298 struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev); 310 299 bool is_cxl_nvdimm(struct device *dev); 311 300 int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd); 301 + struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd); 302 + 303 + /* 304 + * Unit test builds overrides this to __weak, find the 'strong' version 305 + * of these symbols in tools/testing/cxl/. 306 + */ 307 + #ifndef __mock 308 + #define __mock static 309 + #endif 312 310 #endif /* __CXL_H__ */
+179 -23
drivers/cxl/cxlmem.h
··· 2 2 /* Copyright(c) 2020-2021 Intel Corporation. */ 3 3 #ifndef __CXL_MEM_H__ 4 4 #define __CXL_MEM_H__ 5 + #include <uapi/linux/cxl_mem.h> 5 6 #include <linux/cdev.h> 6 7 #include "cxl.h" 7 8 ··· 30 29 CXLMDEV_RESET_NEEDED_NOT) 31 30 32 31 /** 33 - * struct cdevm_file_operations - devm coordinated cdev file operations 34 - * @fops: file operations that are synchronized against @shutdown 35 - * @shutdown: disconnect driver data 36 - * 37 - * @shutdown is invoked in the devres release path to disconnect any 38 - * driver instance data from @dev. It assumes synchronization with any 39 - * fops operation that requires driver data. After @shutdown an 40 - * operation may only reference @device data. 41 - */ 42 - struct cdevm_file_operations { 43 - struct file_operations fops; 44 - void (*shutdown)(struct device *dev); 45 - }; 46 - 47 - /** 48 32 * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device 49 33 * @dev: driver core device object 50 34 * @cdev: char dev core object for ioctl operations ··· 48 62 return container_of(dev, struct cxl_memdev, dev); 49 63 } 50 64 51 - struct cxl_memdev * 52 - devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm, 53 - const struct cdevm_file_operations *cdevm_fops); 65 + struct cxl_memdev *devm_cxl_add_memdev(struct cxl_mem *cxlm); 66 + 67 + /** 68 + * struct cxl_mbox_cmd - A command to be submitted to hardware. 69 + * @opcode: (input) The command set and command submitted to hardware. 70 + * @payload_in: (input) Pointer to the input payload. 71 + * @payload_out: (output) Pointer to the output payload. Must be allocated by 72 + * the caller. 73 + * @size_in: (input) Number of bytes to load from @payload_in. 74 + * @size_out: (input) Max number of bytes loaded into @payload_out. 75 + * (output) Number of bytes generated by the device. For fixed size 76 + * outputs commands this is always expected to be deterministic. For 77 + * variable sized output commands, it tells the exact number of bytes 78 + * written. 79 + * @return_code: (output) Error code returned from hardware. 80 + * 81 + * This is the primary mechanism used to send commands to the hardware. 82 + * All the fields except @payload_* correspond exactly to the fields described in 83 + * Command Register section of the CXL 2.0 8.2.8.4.5. @payload_in and 84 + * @payload_out are written to, and read from the Command Payload Registers 85 + * defined in CXL 2.0 8.2.8.4.8. 86 + */ 87 + struct cxl_mbox_cmd { 88 + u16 opcode; 89 + void *payload_in; 90 + void *payload_out; 91 + size_t size_in; 92 + size_t size_out; 93 + u16 return_code; 94 + #define CXL_MBOX_SUCCESS 0 95 + }; 96 + 97 + /* 98 + * CXL 2.0 - Memory capacity multiplier 99 + * See Section 8.2.9.5 100 + * 101 + * Volatile, Persistent, and Partition capacities are specified to be in 102 + * multiples of 256MB - define a multiplier to convert to/from bytes. 103 + */ 104 + #define CXL_CAPACITY_MULTIPLIER SZ_256M 54 105 55 106 /** 56 107 * struct cxl_mem - A CXL memory device 57 - * @pdev: The PCI device associated with this CXL device. 108 + * @dev: The device associated with this CXL device. 58 109 * @cxlmd: Logical memory device chardev / interface 59 110 * @regs: Parsed register blocks 60 111 * @payload_size: Size of space for payload ··· 101 78 * @mbox_mutex: Mutex to synchronize mailbox access. 102 79 * @firmware_version: Firmware version for the memory device. 103 80 * @enabled_cmds: Hardware commands found enabled in CEL. 104 - * @pmem_range: Persistent memory capacity information. 105 - * @ram_range: Volatile memory capacity information. 81 + * @exclusive_cmds: Commands that are kernel-internal only 82 + * @pmem_range: Active Persistent memory capacity configuration 83 + * @ram_range: Active Volatile memory capacity configuration 84 + * @total_bytes: sum of all possible capacities 85 + * @volatile_only_bytes: hard volatile capacity 86 + * @persistent_only_bytes: hard persistent capacity 87 + * @partition_align_bytes: alignment size for partition-able capacity 88 + * @active_volatile_bytes: sum of hard + soft volatile 89 + * @active_persistent_bytes: sum of hard + soft persistent 90 + * @next_volatile_bytes: volatile capacity change pending device reset 91 + * @next_persistent_bytes: persistent capacity change pending device reset 92 + * @mbox_send: @dev specific transport for transmitting mailbox commands 93 + * 94 + * See section 8.2.9.5.2 Capacity Configuration and Label Storage for 95 + * details on capacity parameters. 106 96 */ 107 97 struct cxl_mem { 108 - struct pci_dev *pdev; 98 + struct device *dev; 109 99 struct cxl_memdev *cxlmd; 110 100 111 101 struct cxl_regs regs; ··· 127 91 size_t lsa_size; 128 92 struct mutex mbox_mutex; /* Protects device mailbox and firmware */ 129 93 char firmware_version[0x10]; 130 - unsigned long *enabled_cmds; 94 + DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX); 95 + DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX); 131 96 132 97 struct range pmem_range; 133 98 struct range ram_range; ··· 141 104 u64 active_persistent_bytes; 142 105 u64 next_volatile_bytes; 143 106 u64 next_persistent_bytes; 107 + 108 + int (*mbox_send)(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd); 144 109 }; 110 + 111 + enum cxl_opcode { 112 + CXL_MBOX_OP_INVALID = 0x0000, 113 + CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID, 114 + CXL_MBOX_OP_GET_FW_INFO = 0x0200, 115 + CXL_MBOX_OP_ACTIVATE_FW = 0x0202, 116 + CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400, 117 + CXL_MBOX_OP_GET_LOG = 0x0401, 118 + CXL_MBOX_OP_IDENTIFY = 0x4000, 119 + CXL_MBOX_OP_GET_PARTITION_INFO = 0x4100, 120 + CXL_MBOX_OP_SET_PARTITION_INFO = 0x4101, 121 + CXL_MBOX_OP_GET_LSA = 0x4102, 122 + CXL_MBOX_OP_SET_LSA = 0x4103, 123 + CXL_MBOX_OP_GET_HEALTH_INFO = 0x4200, 124 + CXL_MBOX_OP_GET_ALERT_CONFIG = 0x4201, 125 + CXL_MBOX_OP_SET_ALERT_CONFIG = 0x4202, 126 + CXL_MBOX_OP_GET_SHUTDOWN_STATE = 0x4203, 127 + CXL_MBOX_OP_SET_SHUTDOWN_STATE = 0x4204, 128 + CXL_MBOX_OP_GET_POISON = 0x4300, 129 + CXL_MBOX_OP_INJECT_POISON = 0x4301, 130 + CXL_MBOX_OP_CLEAR_POISON = 0x4302, 131 + CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS = 0x4303, 132 + CXL_MBOX_OP_SCAN_MEDIA = 0x4304, 133 + CXL_MBOX_OP_GET_SCAN_MEDIA = 0x4305, 134 + CXL_MBOX_OP_MAX = 0x10000 135 + }; 136 + 137 + #define DEFINE_CXL_CEL_UUID \ 138 + UUID_INIT(0xda9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 0x96, 0xb1, 0x62, \ 139 + 0x3b, 0x3f, 0x17) 140 + 141 + #define DEFINE_CXL_VENDOR_DEBUG_UUID \ 142 + UUID_INIT(0xe1819d9, 0x11a9, 0x400c, 0x81, 0x1f, 0xd6, 0x07, 0x19, \ 143 + 0x40, 0x3d, 0x86) 144 + 145 + struct cxl_mbox_get_supported_logs { 146 + __le16 entries; 147 + u8 rsvd[6]; 148 + struct cxl_gsl_entry { 149 + uuid_t uuid; 150 + __le32 size; 151 + } __packed entry[]; 152 + } __packed; 153 + 154 + struct cxl_cel_entry { 155 + __le16 opcode; 156 + __le16 effect; 157 + } __packed; 158 + 159 + struct cxl_mbox_get_log { 160 + uuid_t uuid; 161 + __le32 offset; 162 + __le32 length; 163 + } __packed; 164 + 165 + /* See CXL 2.0 Table 175 Identify Memory Device Output Payload */ 166 + struct cxl_mbox_identify { 167 + char fw_revision[0x10]; 168 + __le64 total_capacity; 169 + __le64 volatile_capacity; 170 + __le64 persistent_capacity; 171 + __le64 partition_align; 172 + __le16 info_event_log_size; 173 + __le16 warning_event_log_size; 174 + __le16 failure_event_log_size; 175 + __le16 fatal_event_log_size; 176 + __le32 lsa_size; 177 + u8 poison_list_max_mer[3]; 178 + __le16 inject_poison_limit; 179 + u8 poison_caps; 180 + u8 qos_telemetry_caps; 181 + } __packed; 182 + 183 + struct cxl_mbox_get_lsa { 184 + u32 offset; 185 + u32 length; 186 + } __packed; 187 + 188 + struct cxl_mbox_set_lsa { 189 + u32 offset; 190 + u32 reserved; 191 + u8 data[]; 192 + } __packed; 193 + 194 + /** 195 + * struct cxl_mem_command - Driver representation of a memory device command 196 + * @info: Command information as it exists for the UAPI 197 + * @opcode: The actual bits used for the mailbox protocol 198 + * @flags: Set of flags effecting driver behavior. 199 + * 200 + * * %CXL_CMD_FLAG_FORCE_ENABLE: In cases of error, commands with this flag 201 + * will be enabled by the driver regardless of what hardware may have 202 + * advertised. 203 + * 204 + * The cxl_mem_command is the driver's internal representation of commands that 205 + * are supported by the driver. Some of these commands may not be supported by 206 + * the hardware. The driver will use @info to validate the fields passed in by 207 + * the user then submit the @opcode to the hardware. 208 + * 209 + * See struct cxl_command_info. 210 + */ 211 + struct cxl_mem_command { 212 + struct cxl_command_info info; 213 + enum cxl_opcode opcode; 214 + u32 flags; 215 + #define CXL_CMD_FLAG_NONE 0 216 + #define CXL_CMD_FLAG_FORCE_ENABLE BIT(0) 217 + }; 218 + 219 + int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in, 220 + size_t in_size, void *out, size_t out_size); 221 + int cxl_mem_identify(struct cxl_mem *cxlm); 222 + int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm); 223 + int cxl_mem_create_range_info(struct cxl_mem *cxlm); 224 + struct cxl_mem *cxl_mem_create(struct device *dev); 225 + void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds); 226 + void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds); 145 227 #endif /* __CXL_MEM_H__ */
+110 -1132
drivers/cxl/pci.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ 3 - #include <uapi/linux/cxl_mem.h> 4 - #include <linux/security.h> 5 - #include <linux/debugfs.h> 3 + #include <linux/io-64-nonatomic-lo-hi.h> 6 4 #include <linux/module.h> 7 5 #include <linux/sizes.h> 8 6 #include <linux/mutex.h> 9 7 #include <linux/list.h> 10 - #include <linux/cdev.h> 11 - #include <linux/idr.h> 12 8 #include <linux/pci.h> 13 9 #include <linux/io.h> 14 - #include <linux/io-64-nonatomic-lo-hi.h> 15 10 #include "cxlmem.h" 16 11 #include "pci.h" 17 12 #include "cxl.h" ··· 16 21 * 17 22 * This implements the PCI exclusive functionality for a CXL device as it is 18 23 * defined by the Compute Express Link specification. CXL devices may surface 19 - * certain functionality even if it isn't CXL enabled. 24 + * certain functionality even if it isn't CXL enabled. While this driver is 25 + * focused around the PCI specific aspects of a CXL device, it binds to the 26 + * specific CXL memory device class code, and therefore the implementation of 27 + * cxl_pci is focused around CXL memory devices. 20 28 * 21 29 * The driver has several responsibilities, mainly: 22 30 * - Create the memX device and register on the CXL bus. 23 31 * - Enumerate device's register interface and map them. 24 - * - Probe the device attributes to establish sysfs interface. 25 - * - Provide an IOCTL interface to userspace to communicate with the device for 26 - * things like firmware update. 32 + * - Registers nvdimm bridge device with cxl_core. 33 + * - Registers a CXL mailbox with cxl_core. 27 34 */ 28 35 29 36 #define cxl_doorbell_busy(cxlm) \ ··· 35 38 /* CXL 2.0 - 8.2.8.4 */ 36 39 #define CXL_MAILBOX_TIMEOUT_MS (2 * HZ) 37 40 38 - enum opcode { 39 - CXL_MBOX_OP_INVALID = 0x0000, 40 - CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID, 41 - CXL_MBOX_OP_GET_FW_INFO = 0x0200, 42 - CXL_MBOX_OP_ACTIVATE_FW = 0x0202, 43 - CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400, 44 - CXL_MBOX_OP_GET_LOG = 0x0401, 45 - CXL_MBOX_OP_IDENTIFY = 0x4000, 46 - CXL_MBOX_OP_GET_PARTITION_INFO = 0x4100, 47 - CXL_MBOX_OP_SET_PARTITION_INFO = 0x4101, 48 - CXL_MBOX_OP_GET_LSA = 0x4102, 49 - CXL_MBOX_OP_SET_LSA = 0x4103, 50 - CXL_MBOX_OP_GET_HEALTH_INFO = 0x4200, 51 - CXL_MBOX_OP_GET_ALERT_CONFIG = 0x4201, 52 - CXL_MBOX_OP_SET_ALERT_CONFIG = 0x4202, 53 - CXL_MBOX_OP_GET_SHUTDOWN_STATE = 0x4203, 54 - CXL_MBOX_OP_SET_SHUTDOWN_STATE = 0x4204, 55 - CXL_MBOX_OP_GET_POISON = 0x4300, 56 - CXL_MBOX_OP_INJECT_POISON = 0x4301, 57 - CXL_MBOX_OP_CLEAR_POISON = 0x4302, 58 - CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS = 0x4303, 59 - CXL_MBOX_OP_SCAN_MEDIA = 0x4304, 60 - CXL_MBOX_OP_GET_SCAN_MEDIA = 0x4305, 61 - CXL_MBOX_OP_MAX = 0x10000 62 - }; 63 - 64 - /* 65 - * CXL 2.0 - Memory capacity multiplier 66 - * See Section 8.2.9.5 67 - * 68 - * Volatile, Persistent, and Partition capacities are specified to be in 69 - * multiples of 256MB - define a multiplier to convert to/from bytes. 70 - */ 71 - #define CXL_CAPACITY_MULTIPLIER SZ_256M 72 - 73 - /** 74 - * struct mbox_cmd - A command to be submitted to hardware. 75 - * @opcode: (input) The command set and command submitted to hardware. 76 - * @payload_in: (input) Pointer to the input payload. 77 - * @payload_out: (output) Pointer to the output payload. Must be allocated by 78 - * the caller. 79 - * @size_in: (input) Number of bytes to load from @payload_in. 80 - * @size_out: (input) Max number of bytes loaded into @payload_out. 81 - * (output) Number of bytes generated by the device. For fixed size 82 - * outputs commands this is always expected to be deterministic. For 83 - * variable sized output commands, it tells the exact number of bytes 84 - * written. 85 - * @return_code: (output) Error code returned from hardware. 86 - * 87 - * This is the primary mechanism used to send commands to the hardware. 88 - * All the fields except @payload_* correspond exactly to the fields described in 89 - * Command Register section of the CXL 2.0 8.2.8.4.5. @payload_in and 90 - * @payload_out are written to, and read from the Command Payload Registers 91 - * defined in CXL 2.0 8.2.8.4.8. 92 - */ 93 - struct mbox_cmd { 94 - u16 opcode; 95 - void *payload_in; 96 - void *payload_out; 97 - size_t size_in; 98 - size_t size_out; 99 - u16 return_code; 100 - #define CXL_MBOX_SUCCESS 0 101 - }; 102 - 103 - static DECLARE_RWSEM(cxl_memdev_rwsem); 104 - static struct dentry *cxl_debugfs; 105 - static bool cxl_raw_allow_all; 106 - 107 - enum { 108 - CEL_UUID, 109 - VENDOR_DEBUG_UUID, 110 - }; 111 - 112 - /* See CXL 2.0 Table 170. Get Log Input Payload */ 113 - static const uuid_t log_uuid[] = { 114 - [CEL_UUID] = UUID_INIT(0xda9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 0x96, 115 - 0xb1, 0x62, 0x3b, 0x3f, 0x17), 116 - [VENDOR_DEBUG_UUID] = UUID_INIT(0xe1819d9, 0x11a9, 0x400c, 0x81, 0x1f, 117 - 0xd6, 0x07, 0x19, 0x40, 0x3d, 0x86), 118 - }; 119 - 120 - /** 121 - * struct cxl_mem_command - Driver representation of a memory device command 122 - * @info: Command information as it exists for the UAPI 123 - * @opcode: The actual bits used for the mailbox protocol 124 - * @flags: Set of flags effecting driver behavior. 125 - * 126 - * * %CXL_CMD_FLAG_FORCE_ENABLE: In cases of error, commands with this flag 127 - * will be enabled by the driver regardless of what hardware may have 128 - * advertised. 129 - * 130 - * The cxl_mem_command is the driver's internal representation of commands that 131 - * are supported by the driver. Some of these commands may not be supported by 132 - * the hardware. The driver will use @info to validate the fields passed in by 133 - * the user then submit the @opcode to the hardware. 134 - * 135 - * See struct cxl_command_info. 136 - */ 137 - struct cxl_mem_command { 138 - struct cxl_command_info info; 139 - enum opcode opcode; 140 - u32 flags; 141 - #define CXL_CMD_FLAG_NONE 0 142 - #define CXL_CMD_FLAG_FORCE_ENABLE BIT(0) 143 - }; 144 - 145 - #define CXL_CMD(_id, sin, sout, _flags) \ 146 - [CXL_MEM_COMMAND_ID_##_id] = { \ 147 - .info = { \ 148 - .id = CXL_MEM_COMMAND_ID_##_id, \ 149 - .size_in = sin, \ 150 - .size_out = sout, \ 151 - }, \ 152 - .opcode = CXL_MBOX_OP_##_id, \ 153 - .flags = _flags, \ 154 - } 155 - 156 - /* 157 - * This table defines the supported mailbox commands for the driver. This table 158 - * is made up of a UAPI structure. Non-negative values as parameters in the 159 - * table will be validated against the user's input. For example, if size_in is 160 - * 0, and the user passed in 1, it is an error. 161 - */ 162 - static struct cxl_mem_command mem_commands[CXL_MEM_COMMAND_ID_MAX] = { 163 - CXL_CMD(IDENTIFY, 0, 0x43, CXL_CMD_FLAG_FORCE_ENABLE), 164 - #ifdef CONFIG_CXL_MEM_RAW_COMMANDS 165 - CXL_CMD(RAW, ~0, ~0, 0), 166 - #endif 167 - CXL_CMD(GET_SUPPORTED_LOGS, 0, ~0, CXL_CMD_FLAG_FORCE_ENABLE), 168 - CXL_CMD(GET_FW_INFO, 0, 0x50, 0), 169 - CXL_CMD(GET_PARTITION_INFO, 0, 0x20, 0), 170 - CXL_CMD(GET_LSA, 0x8, ~0, 0), 171 - CXL_CMD(GET_HEALTH_INFO, 0, 0x12, 0), 172 - CXL_CMD(GET_LOG, 0x18, ~0, CXL_CMD_FLAG_FORCE_ENABLE), 173 - CXL_CMD(SET_PARTITION_INFO, 0x0a, 0, 0), 174 - CXL_CMD(SET_LSA, ~0, 0, 0), 175 - CXL_CMD(GET_ALERT_CONFIG, 0, 0x10, 0), 176 - CXL_CMD(SET_ALERT_CONFIG, 0xc, 0, 0), 177 - CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0), 178 - CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0), 179 - CXL_CMD(GET_POISON, 0x10, ~0, 0), 180 - CXL_CMD(INJECT_POISON, 0x8, 0, 0), 181 - CXL_CMD(CLEAR_POISON, 0x48, 0, 0), 182 - CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0), 183 - CXL_CMD(SCAN_MEDIA, 0x11, 0, 0), 184 - CXL_CMD(GET_SCAN_MEDIA, 0, ~0, 0), 185 - }; 186 - 187 - /* 188 - * Commands that RAW doesn't permit. The rationale for each: 189 - * 190 - * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment / 191 - * coordination of transaction timeout values at the root bridge level. 192 - * 193 - * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live 194 - * and needs to be coordinated with HDM updates. 195 - * 196 - * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the 197 - * driver and any writes from userspace invalidates those contents. 198 - * 199 - * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes 200 - * to the device after it is marked clean, userspace can not make that 201 - * assertion. 202 - * 203 - * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that 204 - * is kept up to date with patrol notifications and error management. 205 - */ 206 - static u16 cxl_disabled_raw_commands[] = { 207 - CXL_MBOX_OP_ACTIVATE_FW, 208 - CXL_MBOX_OP_SET_PARTITION_INFO, 209 - CXL_MBOX_OP_SET_LSA, 210 - CXL_MBOX_OP_SET_SHUTDOWN_STATE, 211 - CXL_MBOX_OP_SCAN_MEDIA, 212 - CXL_MBOX_OP_GET_SCAN_MEDIA, 213 - }; 214 - 215 - /* 216 - * Command sets that RAW doesn't permit. All opcodes in this set are 217 - * disabled because they pass plain text security payloads over the 218 - * user/kernel boundary. This functionality is intended to be wrapped 219 - * behind the keys ABI which allows for encrypted payloads in the UAPI 220 - */ 221 - static u8 security_command_sets[] = { 222 - 0x44, /* Sanitize */ 223 - 0x45, /* Persistent Memory Data-at-rest Security */ 224 - 0x46, /* Security Passthrough */ 225 - }; 226 - 227 - #define cxl_for_each_cmd(cmd) \ 228 - for ((cmd) = &mem_commands[0]; \ 229 - ((cmd) - mem_commands) < ARRAY_SIZE(mem_commands); (cmd)++) 230 - 231 - #define cxl_cmd_count ARRAY_SIZE(mem_commands) 232 - 233 - static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm) 41 + static int cxl_pci_mbox_wait_for_doorbell(struct cxl_mem *cxlm) 234 42 { 235 43 const unsigned long start = jiffies; 236 44 unsigned long end = start; ··· 52 250 cpu_relax(); 53 251 } 54 252 55 - dev_dbg(&cxlm->pdev->dev, "Doorbell wait took %dms", 253 + dev_dbg(cxlm->dev, "Doorbell wait took %dms", 56 254 jiffies_to_msecs(end) - jiffies_to_msecs(start)); 57 255 return 0; 58 256 } 59 257 60 - static bool cxl_is_security_command(u16 opcode) 258 + static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm, 259 + struct cxl_mbox_cmd *mbox_cmd) 61 260 { 62 - int i; 63 - 64 - for (i = 0; i < ARRAY_SIZE(security_command_sets); i++) 65 - if (security_command_sets[i] == (opcode >> 8)) 66 - return true; 67 - return false; 68 - } 69 - 70 - static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm, 71 - struct mbox_cmd *mbox_cmd) 72 - { 73 - struct device *dev = &cxlm->pdev->dev; 261 + struct device *dev = cxlm->dev; 74 262 75 263 dev_dbg(dev, "Mailbox command (opcode: %#x size: %zub) timed out\n", 76 264 mbox_cmd->opcode, mbox_cmd->size_in); 77 265 } 78 266 79 267 /** 80 - * __cxl_mem_mbox_send_cmd() - Execute a mailbox command 268 + * __cxl_pci_mbox_send_cmd() - Execute a mailbox command 81 269 * @cxlm: The CXL memory device to communicate with. 82 270 * @mbox_cmd: Command to send to the memory device. 83 271 * ··· 88 296 * not need to coordinate with each other. The driver only uses the primary 89 297 * mailbox. 90 298 */ 91 - static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, 92 - struct mbox_cmd *mbox_cmd) 299 + static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm, 300 + struct cxl_mbox_cmd *mbox_cmd) 93 301 { 94 302 void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET; 303 + struct device *dev = cxlm->dev; 95 304 u64 cmd_reg, status_reg; 96 305 size_t out_len; 97 306 int rc; ··· 118 325 119 326 /* #1 */ 120 327 if (cxl_doorbell_busy(cxlm)) { 121 - dev_err_ratelimited(&cxlm->pdev->dev, 122 - "Mailbox re-busy after acquiring\n"); 328 + dev_err_ratelimited(dev, "Mailbox re-busy after acquiring\n"); 123 329 return -EBUSY; 124 330 } 125 331 ··· 137 345 writeq(cmd_reg, cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET); 138 346 139 347 /* #4 */ 140 - dev_dbg(&cxlm->pdev->dev, "Sending command\n"); 348 + dev_dbg(dev, "Sending command\n"); 141 349 writel(CXLDEV_MBOX_CTRL_DOORBELL, 142 350 cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET); 143 351 144 352 /* #5 */ 145 - rc = cxl_mem_wait_for_doorbell(cxlm); 353 + rc = cxl_pci_mbox_wait_for_doorbell(cxlm); 146 354 if (rc == -ETIMEDOUT) { 147 - cxl_mem_mbox_timeout(cxlm, mbox_cmd); 355 + cxl_pci_mbox_timeout(cxlm, mbox_cmd); 148 356 return rc; 149 357 } 150 358 ··· 154 362 FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg); 155 363 156 364 if (mbox_cmd->return_code != 0) { 157 - dev_dbg(&cxlm->pdev->dev, "Mailbox operation had an error\n"); 365 + dev_dbg(dev, "Mailbox operation had an error\n"); 158 366 return 0; 159 367 } 160 368 ··· 183 391 } 184 392 185 393 /** 186 - * cxl_mem_mbox_get() - Acquire exclusive access to the mailbox. 394 + * cxl_pci_mbox_get() - Acquire exclusive access to the mailbox. 187 395 * @cxlm: The memory device to gain access to. 188 396 * 189 397 * Context: Any context. Takes the mbox_mutex. 190 398 * Return: 0 if exclusive access was acquired. 191 399 */ 192 - static int cxl_mem_mbox_get(struct cxl_mem *cxlm) 400 + static int cxl_pci_mbox_get(struct cxl_mem *cxlm) 193 401 { 194 - struct device *dev = &cxlm->pdev->dev; 402 + struct device *dev = cxlm->dev; 195 403 u64 md_status; 196 404 int rc; 197 405 ··· 214 422 * Mailbox Interface Ready bit. Therefore, waiting for the doorbell 215 423 * to be ready is sufficient. 216 424 */ 217 - rc = cxl_mem_wait_for_doorbell(cxlm); 425 + rc = cxl_pci_mbox_wait_for_doorbell(cxlm); 218 426 if (rc) { 219 427 dev_warn(dev, "Mailbox interface not ready\n"); 220 428 goto out; ··· 254 462 } 255 463 256 464 /** 257 - * cxl_mem_mbox_put() - Release exclusive access to the mailbox. 465 + * cxl_pci_mbox_put() - Release exclusive access to the mailbox. 258 466 * @cxlm: The CXL memory device to communicate with. 259 467 * 260 468 * Context: Any context. Expects mbox_mutex to be held. 261 469 */ 262 - static void cxl_mem_mbox_put(struct cxl_mem *cxlm) 470 + static void cxl_pci_mbox_put(struct cxl_mem *cxlm) 263 471 { 264 472 mutex_unlock(&cxlm->mbox_mutex); 265 473 } 266 474 267 - /** 268 - * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace. 269 - * @cxlm: The CXL memory device to communicate with. 270 - * @cmd: The validated command. 271 - * @in_payload: Pointer to userspace's input payload. 272 - * @out_payload: Pointer to userspace's output payload. 273 - * @size_out: (Input) Max payload size to copy out. 274 - * (Output) Payload size hardware generated. 275 - * @retval: Hardware generated return code from the operation. 276 - * 277 - * Return: 278 - * * %0 - Mailbox transaction succeeded. This implies the mailbox 279 - * protocol completed successfully not that the operation itself 280 - * was successful. 281 - * * %-ENOMEM - Couldn't allocate a bounce buffer. 282 - * * %-EFAULT - Something happened with copy_to/from_user. 283 - * * %-EINTR - Mailbox acquisition interrupted. 284 - * * %-EXXX - Transaction level failures. 285 - * 286 - * Creates the appropriate mailbox command and dispatches it on behalf of a 287 - * userspace request. The input and output payloads are copied between 288 - * userspace. 289 - * 290 - * See cxl_send_cmd(). 291 - */ 292 - static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm, 293 - const struct cxl_mem_command *cmd, 294 - u64 in_payload, u64 out_payload, 295 - s32 *size_out, u32 *retval) 475 + static int cxl_pci_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 296 476 { 297 - struct device *dev = &cxlm->pdev->dev; 298 - struct mbox_cmd mbox_cmd = { 299 - .opcode = cmd->opcode, 300 - .size_in = cmd->info.size_in, 301 - .size_out = cmd->info.size_out, 302 - }; 303 477 int rc; 304 478 305 - if (cmd->info.size_out) { 306 - mbox_cmd.payload_out = kvzalloc(cmd->info.size_out, GFP_KERNEL); 307 - if (!mbox_cmd.payload_out) 308 - return -ENOMEM; 309 - } 310 - 311 - if (cmd->info.size_in) { 312 - mbox_cmd.payload_in = vmemdup_user(u64_to_user_ptr(in_payload), 313 - cmd->info.size_in); 314 - if (IS_ERR(mbox_cmd.payload_in)) { 315 - kvfree(mbox_cmd.payload_out); 316 - return PTR_ERR(mbox_cmd.payload_in); 317 - } 318 - } 319 - 320 - rc = cxl_mem_mbox_get(cxlm); 321 - if (rc) 322 - goto out; 323 - 324 - dev_dbg(dev, 325 - "Submitting %s command for user\n" 326 - "\topcode: %x\n" 327 - "\tsize: %ub\n", 328 - cxl_command_names[cmd->info.id].name, mbox_cmd.opcode, 329 - cmd->info.size_in); 330 - 331 - dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW, 332 - "raw command path used\n"); 333 - 334 - rc = __cxl_mem_mbox_send_cmd(cxlm, &mbox_cmd); 335 - cxl_mem_mbox_put(cxlm); 336 - if (rc) 337 - goto out; 338 - 339 - /* 340 - * @size_out contains the max size that's allowed to be written back out 341 - * to userspace. While the payload may have written more output than 342 - * this it will have to be ignored. 343 - */ 344 - if (mbox_cmd.size_out) { 345 - dev_WARN_ONCE(dev, mbox_cmd.size_out > *size_out, 346 - "Invalid return size\n"); 347 - if (copy_to_user(u64_to_user_ptr(out_payload), 348 - mbox_cmd.payload_out, mbox_cmd.size_out)) { 349 - rc = -EFAULT; 350 - goto out; 351 - } 352 - } 353 - 354 - *size_out = mbox_cmd.size_out; 355 - *retval = mbox_cmd.return_code; 356 - 357 - out: 358 - kvfree(mbox_cmd.payload_in); 359 - kvfree(mbox_cmd.payload_out); 360 - return rc; 361 - } 362 - 363 - static bool cxl_mem_raw_command_allowed(u16 opcode) 364 - { 365 - int i; 366 - 367 - if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS)) 368 - return false; 369 - 370 - if (security_locked_down(LOCKDOWN_PCI_ACCESS)) 371 - return false; 372 - 373 - if (cxl_raw_allow_all) 374 - return true; 375 - 376 - if (cxl_is_security_command(opcode)) 377 - return false; 378 - 379 - for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++) 380 - if (cxl_disabled_raw_commands[i] == opcode) 381 - return false; 382 - 383 - return true; 384 - } 385 - 386 - /** 387 - * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND. 388 - * @cxlm: &struct cxl_mem device whose mailbox will be used. 389 - * @send_cmd: &struct cxl_send_command copied in from userspace. 390 - * @out_cmd: Sanitized and populated &struct cxl_mem_command. 391 - * 392 - * Return: 393 - * * %0 - @out_cmd is ready to send. 394 - * * %-ENOTTY - Invalid command specified. 395 - * * %-EINVAL - Reserved fields or invalid values were used. 396 - * * %-ENOMEM - Input or output buffer wasn't sized properly. 397 - * * %-EPERM - Attempted to use a protected command. 398 - * 399 - * The result of this command is a fully validated command in @out_cmd that is 400 - * safe to send to the hardware. 401 - * 402 - * See handle_mailbox_cmd_from_user() 403 - */ 404 - static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm, 405 - const struct cxl_send_command *send_cmd, 406 - struct cxl_mem_command *out_cmd) 407 - { 408 - const struct cxl_command_info *info; 409 - struct cxl_mem_command *c; 410 - 411 - if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX) 412 - return -ENOTTY; 413 - 414 - /* 415 - * The user can never specify an input payload larger than what hardware 416 - * supports, but output can be arbitrarily large (simply write out as 417 - * much data as the hardware provides). 418 - */ 419 - if (send_cmd->in.size > cxlm->payload_size) 420 - return -EINVAL; 421 - 422 - /* 423 - * Checks are bypassed for raw commands but a WARN/taint will occur 424 - * later in the callchain 425 - */ 426 - if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) { 427 - const struct cxl_mem_command temp = { 428 - .info = { 429 - .id = CXL_MEM_COMMAND_ID_RAW, 430 - .flags = 0, 431 - .size_in = send_cmd->in.size, 432 - .size_out = send_cmd->out.size, 433 - }, 434 - .opcode = send_cmd->raw.opcode 435 - }; 436 - 437 - if (send_cmd->raw.rsvd) 438 - return -EINVAL; 439 - 440 - /* 441 - * Unlike supported commands, the output size of RAW commands 442 - * gets passed along without further checking, so it must be 443 - * validated here. 444 - */ 445 - if (send_cmd->out.size > cxlm->payload_size) 446 - return -EINVAL; 447 - 448 - if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode)) 449 - return -EPERM; 450 - 451 - memcpy(out_cmd, &temp, sizeof(temp)); 452 - 453 - return 0; 454 - } 455 - 456 - if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK) 457 - return -EINVAL; 458 - 459 - if (send_cmd->rsvd) 460 - return -EINVAL; 461 - 462 - if (send_cmd->in.rsvd || send_cmd->out.rsvd) 463 - return -EINVAL; 464 - 465 - /* Convert user's command into the internal representation */ 466 - c = &mem_commands[send_cmd->id]; 467 - info = &c->info; 468 - 469 - /* Check that the command is enabled for hardware */ 470 - if (!test_bit(info->id, cxlm->enabled_cmds)) 471 - return -ENOTTY; 472 - 473 - /* Check the input buffer is the expected size */ 474 - if (info->size_in >= 0 && info->size_in != send_cmd->in.size) 475 - return -ENOMEM; 476 - 477 - /* Check the output buffer is at least large enough */ 478 - if (info->size_out >= 0 && send_cmd->out.size < info->size_out) 479 - return -ENOMEM; 480 - 481 - memcpy(out_cmd, c, sizeof(*c)); 482 - out_cmd->info.size_in = send_cmd->in.size; 483 - /* 484 - * XXX: out_cmd->info.size_out will be controlled by the driver, and the 485 - * specified number of bytes @send_cmd->out.size will be copied back out 486 - * to userspace. 487 - */ 488 - 489 - return 0; 490 - } 491 - 492 - static int cxl_query_cmd(struct cxl_memdev *cxlmd, 493 - struct cxl_mem_query_commands __user *q) 494 - { 495 - struct device *dev = &cxlmd->dev; 496 - struct cxl_mem_command *cmd; 497 - u32 n_commands; 498 - int j = 0; 499 - 500 - dev_dbg(dev, "Query IOCTL\n"); 501 - 502 - if (get_user(n_commands, &q->n_commands)) 503 - return -EFAULT; 504 - 505 - /* returns the total number if 0 elements are requested. */ 506 - if (n_commands == 0) 507 - return put_user(cxl_cmd_count, &q->n_commands); 508 - 509 - /* 510 - * otherwise, return max(n_commands, total commands) cxl_command_info 511 - * structures. 512 - */ 513 - cxl_for_each_cmd(cmd) { 514 - const struct cxl_command_info *info = &cmd->info; 515 - 516 - if (copy_to_user(&q->commands[j++], info, sizeof(*info))) 517 - return -EFAULT; 518 - 519 - if (j == n_commands) 520 - break; 521 - } 522 - 523 - return 0; 524 - } 525 - 526 - static int cxl_send_cmd(struct cxl_memdev *cxlmd, 527 - struct cxl_send_command __user *s) 528 - { 529 - struct cxl_mem *cxlm = cxlmd->cxlm; 530 - struct device *dev = &cxlmd->dev; 531 - struct cxl_send_command send; 532 - struct cxl_mem_command c; 533 - int rc; 534 - 535 - dev_dbg(dev, "Send IOCTL\n"); 536 - 537 - if (copy_from_user(&send, s, sizeof(send))) 538 - return -EFAULT; 539 - 540 - rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c); 479 + rc = cxl_pci_mbox_get(cxlm); 541 480 if (rc) 542 481 return rc; 543 482 544 - /* Prepare to handle a full payload for variable sized output */ 545 - if (c.info.size_out < 0) 546 - c.info.size_out = cxlm->payload_size; 547 - 548 - rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload, 549 - send.out.payload, &send.out.size, 550 - &send.retval); 551 - if (rc) 552 - return rc; 553 - 554 - if (copy_to_user(s, &send, sizeof(send))) 555 - return -EFAULT; 556 - 557 - return 0; 558 - } 559 - 560 - static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd, 561 - unsigned long arg) 562 - { 563 - switch (cmd) { 564 - case CXL_MEM_QUERY_COMMANDS: 565 - return cxl_query_cmd(cxlmd, (void __user *)arg); 566 - case CXL_MEM_SEND_COMMAND: 567 - return cxl_send_cmd(cxlmd, (void __user *)arg); 568 - default: 569 - return -ENOTTY; 570 - } 571 - } 572 - 573 - static long cxl_memdev_ioctl(struct file *file, unsigned int cmd, 574 - unsigned long arg) 575 - { 576 - struct cxl_memdev *cxlmd = file->private_data; 577 - int rc = -ENXIO; 578 - 579 - down_read(&cxl_memdev_rwsem); 580 - if (cxlmd->cxlm) 581 - rc = __cxl_memdev_ioctl(cxlmd, cmd, arg); 582 - up_read(&cxl_memdev_rwsem); 483 + rc = __cxl_pci_mbox_send_cmd(cxlm, cmd); 484 + cxl_pci_mbox_put(cxlm); 583 485 584 486 return rc; 585 487 } 586 488 587 - static int cxl_memdev_open(struct inode *inode, struct file *file) 588 - { 589 - struct cxl_memdev *cxlmd = 590 - container_of(inode->i_cdev, typeof(*cxlmd), cdev); 591 - 592 - get_device(&cxlmd->dev); 593 - file->private_data = cxlmd; 594 - 595 - return 0; 596 - } 597 - 598 - static int cxl_memdev_release_file(struct inode *inode, struct file *file) 599 - { 600 - struct cxl_memdev *cxlmd = 601 - container_of(inode->i_cdev, typeof(*cxlmd), cdev); 602 - 603 - put_device(&cxlmd->dev); 604 - 605 - return 0; 606 - } 607 - 608 - static void cxl_memdev_shutdown(struct device *dev) 609 - { 610 - struct cxl_memdev *cxlmd = to_cxl_memdev(dev); 611 - 612 - down_write(&cxl_memdev_rwsem); 613 - cxlmd->cxlm = NULL; 614 - up_write(&cxl_memdev_rwsem); 615 - } 616 - 617 - static const struct cdevm_file_operations cxl_memdev_fops = { 618 - .fops = { 619 - .owner = THIS_MODULE, 620 - .unlocked_ioctl = cxl_memdev_ioctl, 621 - .open = cxl_memdev_open, 622 - .release = cxl_memdev_release_file, 623 - .compat_ioctl = compat_ptr_ioctl, 624 - .llseek = noop_llseek, 625 - }, 626 - .shutdown = cxl_memdev_shutdown, 627 - }; 628 - 629 - static inline struct cxl_mem_command *cxl_mem_find_command(u16 opcode) 630 - { 631 - struct cxl_mem_command *c; 632 - 633 - cxl_for_each_cmd(c) 634 - if (c->opcode == opcode) 635 - return c; 636 - 637 - return NULL; 638 - } 639 - 640 - /** 641 - * cxl_mem_mbox_send_cmd() - Send a mailbox command to a memory device. 642 - * @cxlm: The CXL memory device to communicate with. 643 - * @opcode: Opcode for the mailbox command. 644 - * @in: The input payload for the mailbox command. 645 - * @in_size: The length of the input payload 646 - * @out: Caller allocated buffer for the output. 647 - * @out_size: Expected size of output. 648 - * 649 - * Context: Any context. Will acquire and release mbox_mutex. 650 - * Return: 651 - * * %>=0 - Number of bytes returned in @out. 652 - * * %-E2BIG - Payload is too large for hardware. 653 - * * %-EBUSY - Couldn't acquire exclusive mailbox access. 654 - * * %-EFAULT - Hardware error occurred. 655 - * * %-ENXIO - Command completed, but device reported an error. 656 - * * %-EIO - Unexpected output size. 657 - * 658 - * Mailbox commands may execute successfully yet the device itself reported an 659 - * error. While this distinction can be useful for commands from userspace, the 660 - * kernel will only be able to use results when both are successful. 661 - * 662 - * See __cxl_mem_mbox_send_cmd() 663 - */ 664 - static int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, 665 - void *in, size_t in_size, 666 - void *out, size_t out_size) 667 - { 668 - const struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); 669 - struct mbox_cmd mbox_cmd = { 670 - .opcode = opcode, 671 - .payload_in = in, 672 - .size_in = in_size, 673 - .size_out = out_size, 674 - .payload_out = out, 675 - }; 676 - int rc; 677 - 678 - if (out_size > cxlm->payload_size) 679 - return -E2BIG; 680 - 681 - rc = cxl_mem_mbox_get(cxlm); 682 - if (rc) 683 - return rc; 684 - 685 - rc = __cxl_mem_mbox_send_cmd(cxlm, &mbox_cmd); 686 - cxl_mem_mbox_put(cxlm); 687 - if (rc) 688 - return rc; 689 - 690 - /* TODO: Map return code to proper kernel style errno */ 691 - if (mbox_cmd.return_code != CXL_MBOX_SUCCESS) 692 - return -ENXIO; 693 - 694 - /* 695 - * Variable sized commands can't be validated and so it's up to the 696 - * caller to do that if they wish. 697 - */ 698 - if (cmd->info.size_out >= 0 && mbox_cmd.size_out != out_size) 699 - return -EIO; 700 - 701 - return 0; 702 - } 703 - 704 - static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm) 489 + static int cxl_pci_setup_mailbox(struct cxl_mem *cxlm) 705 490 { 706 491 const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET); 707 492 493 + cxlm->mbox_send = cxl_pci_mbox_send; 708 494 cxlm->payload_size = 709 495 1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap); 710 496 ··· 295 925 */ 296 926 cxlm->payload_size = min_t(size_t, cxlm->payload_size, SZ_1M); 297 927 if (cxlm->payload_size < 256) { 298 - dev_err(&cxlm->pdev->dev, "Mailbox is too small (%zub)", 928 + dev_err(cxlm->dev, "Mailbox is too small (%zub)", 299 929 cxlm->payload_size); 300 930 return -ENXIO; 301 931 } 302 932 303 - dev_dbg(&cxlm->pdev->dev, "Mailbox payload sized %zu", 933 + dev_dbg(cxlm->dev, "Mailbox payload sized %zu", 304 934 cxlm->payload_size); 305 935 306 936 return 0; 307 937 } 308 938 309 - static struct cxl_mem *cxl_mem_create(struct pci_dev *pdev) 939 + static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map) 310 940 { 311 - struct device *dev = &pdev->dev; 312 - struct cxl_mem *cxlm; 313 - 314 - cxlm = devm_kzalloc(dev, sizeof(*cxlm), GFP_KERNEL); 315 - if (!cxlm) { 316 - dev_err(dev, "No memory available\n"); 317 - return ERR_PTR(-ENOMEM); 318 - } 319 - 320 - mutex_init(&cxlm->mbox_mutex); 321 - cxlm->pdev = pdev; 322 - cxlm->enabled_cmds = 323 - devm_kmalloc_array(dev, BITS_TO_LONGS(cxl_cmd_count), 324 - sizeof(unsigned long), 325 - GFP_KERNEL | __GFP_ZERO); 326 - if (!cxlm->enabled_cmds) { 327 - dev_err(dev, "No memory available for bitmap\n"); 328 - return ERR_PTR(-ENOMEM); 329 - } 330 - 331 - return cxlm; 332 - } 333 - 334 - static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm, 335 - u8 bar, u64 offset) 336 - { 337 - struct pci_dev *pdev = cxlm->pdev; 338 - struct device *dev = &pdev->dev; 339 941 void __iomem *addr; 942 + int bar = map->barno; 943 + struct device *dev = &pdev->dev; 944 + resource_size_t offset = map->block_offset; 340 945 341 946 /* Basic sanity check that BAR is big enough */ 342 947 if (pci_resource_len(pdev, bar) < offset) { 343 - dev_err(dev, "BAR%d: %pr: too small (offset: %#llx)\n", bar, 344 - &pdev->resource[bar], (unsigned long long)offset); 345 - return IOMEM_ERR_PTR(-ENXIO); 948 + dev_err(dev, "BAR%d: %pr: too small (offset: %pa)\n", bar, 949 + &pdev->resource[bar], &offset); 950 + return -ENXIO; 346 951 } 347 952 348 953 addr = pci_iomap(pdev, bar, 0); 349 954 if (!addr) { 350 955 dev_err(dev, "failed to map registers\n"); 351 - return addr; 956 + return -ENOMEM; 352 957 } 353 958 354 - dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %#llx\n", 355 - bar, offset); 959 + dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %pa\n", 960 + bar, &offset); 356 961 357 - return addr; 358 - } 359 - 360 - static void cxl_mem_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base) 361 - { 362 - pci_iounmap(cxlm->pdev, base); 363 - } 364 - 365 - static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec) 366 - { 367 - int pos; 368 - 369 - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC); 370 - if (!pos) 371 - return 0; 372 - 373 - while (pos) { 374 - u16 vendor, id; 375 - 376 - pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor); 377 - pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id); 378 - if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id) 379 - return pos; 380 - 381 - pos = pci_find_next_ext_capability(pdev, pos, 382 - PCI_EXT_CAP_ID_DVSEC); 383 - } 384 - 962 + map->base = addr + map->block_offset; 385 963 return 0; 386 964 } 387 965 388 - static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base, 389 - struct cxl_register_map *map) 966 + static void cxl_unmap_regblock(struct pci_dev *pdev, 967 + struct cxl_register_map *map) 390 968 { 391 - struct pci_dev *pdev = cxlm->pdev; 392 - struct device *dev = &pdev->dev; 969 + pci_iounmap(pdev, map->base - map->block_offset); 970 + map->base = NULL; 971 + } 972 + 973 + static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map) 974 + { 393 975 struct cxl_component_reg_map *comp_map; 394 976 struct cxl_device_reg_map *dev_map; 977 + struct device *dev = &pdev->dev; 978 + void __iomem *base = map->base; 395 979 396 980 switch (map->reg_type) { 397 981 case CXL_REGLOC_RBI_COMPONENT: ··· 381 1057 382 1058 static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map) 383 1059 { 384 - struct pci_dev *pdev = cxlm->pdev; 385 - struct device *dev = &pdev->dev; 1060 + struct device *dev = cxlm->dev; 1061 + struct pci_dev *pdev = to_pci_dev(dev); 386 1062 387 1063 switch (map->reg_type) { 388 1064 case CXL_REGLOC_RBI_COMPONENT: ··· 400 1076 return 0; 401 1077 } 402 1078 403 - static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi, 404 - u8 *bar, u64 *offset, u8 *reg_type) 1079 + static void cxl_decode_regblock(u32 reg_lo, u32 reg_hi, 1080 + struct cxl_register_map *map) 405 1081 { 406 - *offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK); 407 - *bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo); 408 - *reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo); 1082 + map->block_offset = 1083 + ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK); 1084 + map->barno = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo); 1085 + map->reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo); 409 1086 } 410 1087 411 1088 /** 412 - * cxl_mem_setup_regs() - Setup necessary MMIO. 413 - * @cxlm: The CXL memory device to communicate with. 1089 + * cxl_find_regblock() - Locate register blocks by type 1090 + * @pdev: The CXL PCI device to enumerate. 1091 + * @type: Register Block Indicator id 1092 + * @map: Enumeration output, clobbered on error 414 1093 * 415 - * Return: 0 if all necessary registers mapped. 1094 + * Return: 0 if register block enumerated, negative error code otherwise 416 1095 * 417 - * A memory device is required by spec to implement a certain set of MMIO 418 - * regions. The purpose of this function is to enumerate and map those 419 - * registers. 1096 + * A CXL DVSEC may point to one or more register blocks, search for them 1097 + * by @type. 420 1098 */ 421 - static int cxl_mem_setup_regs(struct cxl_mem *cxlm) 1099 + static int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type, 1100 + struct cxl_register_map *map) 422 1101 { 423 - struct pci_dev *pdev = cxlm->pdev; 424 - struct device *dev = &pdev->dev; 425 1102 u32 regloc_size, regblocks; 426 - void __iomem *base; 427 - int regloc, i, n_maps; 428 - struct cxl_register_map *map, maps[CXL_REGLOC_RBI_TYPES]; 429 - int ret = 0; 1103 + int regloc, i; 430 1104 431 - regloc = cxl_mem_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID); 432 - if (!regloc) { 433 - dev_err(dev, "register location dvsec not found\n"); 1105 + regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL, 1106 + PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID); 1107 + if (!regloc) 434 1108 return -ENXIO; 435 - } 436 1109 437 - if (pci_request_mem_regions(pdev, pci_name(pdev))) 438 - return -ENODEV; 439 - 440 - /* Get the size of the Register Locator DVSEC */ 441 1110 pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size); 442 1111 regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size); 443 1112 444 1113 regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET; 445 1114 regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8; 446 1115 447 - for (i = 0, n_maps = 0; i < regblocks; i++, regloc += 8) { 1116 + for (i = 0; i < regblocks; i++, regloc += 8) { 448 1117 u32 reg_lo, reg_hi; 449 - u8 reg_type; 450 - u64 offset; 451 - u8 bar; 452 1118 453 1119 pci_read_config_dword(pdev, regloc, &reg_lo); 454 1120 pci_read_config_dword(pdev, regloc + 4, &reg_hi); 455 1121 456 - cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset, 457 - &reg_type); 1122 + cxl_decode_regblock(reg_lo, reg_hi, map); 458 1123 459 - dev_dbg(dev, "Found register block in bar %u @ 0x%llx of type %u\n", 460 - bar, offset, reg_type); 461 - 462 - /* Ignore unknown register block types */ 463 - if (reg_type > CXL_REGLOC_RBI_MEMDEV) 464 - continue; 465 - 466 - base = cxl_mem_map_regblock(cxlm, bar, offset); 467 - if (!base) 468 - return -ENOMEM; 469 - 470 - map = &maps[n_maps]; 471 - map->barno = bar; 472 - map->block_offset = offset; 473 - map->reg_type = reg_type; 474 - 475 - ret = cxl_probe_regs(cxlm, base + offset, map); 476 - 477 - /* Always unmap the regblock regardless of probe success */ 478 - cxl_mem_unmap_regblock(cxlm, base); 479 - 480 - if (ret) 481 - return ret; 482 - 483 - n_maps++; 1124 + if (map->reg_type == type) 1125 + return 0; 484 1126 } 485 1127 486 - pci_release_mem_regions(pdev); 487 - 488 - for (i = 0; i < n_maps; i++) { 489 - ret = cxl_map_regs(cxlm, &maps[i]); 490 - if (ret) 491 - break; 492 - } 493 - 494 - return ret; 1128 + return -ENODEV; 495 1129 } 496 1130 497 - static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out) 1131 + static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, 1132 + struct cxl_register_map *map) 498 1133 { 499 - u32 remaining = size; 500 - u32 offset = 0; 501 - 502 - while (remaining) { 503 - u32 xfer_size = min_t(u32, remaining, cxlm->payload_size); 504 - struct cxl_mbox_get_log { 505 - uuid_t uuid; 506 - __le32 offset; 507 - __le32 length; 508 - } __packed log = { 509 - .uuid = *uuid, 510 - .offset = cpu_to_le32(offset), 511 - .length = cpu_to_le32(xfer_size) 512 - }; 513 - int rc; 514 - 515 - rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LOG, &log, 516 - sizeof(log), out, xfer_size); 517 - if (rc < 0) 518 - return rc; 519 - 520 - out += xfer_size; 521 - remaining -= xfer_size; 522 - offset += xfer_size; 523 - } 524 - 525 - return 0; 526 - } 527 - 528 - /** 529 - * cxl_walk_cel() - Walk through the Command Effects Log. 530 - * @cxlm: Device. 531 - * @size: Length of the Command Effects Log. 532 - * @cel: CEL 533 - * 534 - * Iterate over each entry in the CEL and determine if the driver supports the 535 - * command. If so, the command is enabled for the device and can be used later. 536 - */ 537 - static void cxl_walk_cel(struct cxl_mem *cxlm, size_t size, u8 *cel) 538 - { 539 - struct cel_entry { 540 - __le16 opcode; 541 - __le16 effect; 542 - } __packed * cel_entry; 543 - const int cel_entries = size / sizeof(*cel_entry); 544 - int i; 545 - 546 - cel_entry = (struct cel_entry *)cel; 547 - 548 - for (i = 0; i < cel_entries; i++) { 549 - u16 opcode = le16_to_cpu(cel_entry[i].opcode); 550 - struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); 551 - 552 - if (!cmd) { 553 - dev_dbg(&cxlm->pdev->dev, 554 - "Opcode 0x%04x unsupported by driver", opcode); 555 - continue; 556 - } 557 - 558 - set_bit(cmd->info.id, cxlm->enabled_cmds); 559 - } 560 - } 561 - 562 - struct cxl_mbox_get_supported_logs { 563 - __le16 entries; 564 - u8 rsvd[6]; 565 - struct gsl_entry { 566 - uuid_t uuid; 567 - __le32 size; 568 - } __packed entry[]; 569 - } __packed; 570 - 571 - static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_mem *cxlm) 572 - { 573 - struct cxl_mbox_get_supported_logs *ret; 574 1134 int rc; 575 1135 576 - ret = kvmalloc(cxlm->payload_size, GFP_KERNEL); 577 - if (!ret) 578 - return ERR_PTR(-ENOMEM); 579 - 580 - rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL, 581 - 0, ret, cxlm->payload_size); 582 - if (rc < 0) { 583 - kvfree(ret); 584 - return ERR_PTR(rc); 585 - } 586 - 587 - return ret; 588 - } 589 - 590 - /** 591 - * cxl_mem_get_partition_info - Get partition info 592 - * @cxlm: The device to act on 593 - * @active_volatile_bytes: returned active volatile capacity 594 - * @active_persistent_bytes: returned active persistent capacity 595 - * @next_volatile_bytes: return next volatile capacity 596 - * @next_persistent_bytes: return next persistent capacity 597 - * 598 - * Retrieve the current partition info for the device specified. If not 0, the 599 - * 'next' values are pending and take affect on next cold reset. 600 - * 601 - * Return: 0 if no error: or the result of the mailbox command. 602 - * 603 - * See CXL @8.2.9.5.2.1 Get Partition Info 604 - */ 605 - static int cxl_mem_get_partition_info(struct cxl_mem *cxlm, 606 - u64 *active_volatile_bytes, 607 - u64 *active_persistent_bytes, 608 - u64 *next_volatile_bytes, 609 - u64 *next_persistent_bytes) 610 - { 611 - struct cxl_mbox_get_partition_info { 612 - __le64 active_volatile_cap; 613 - __le64 active_persistent_cap; 614 - __le64 next_volatile_cap; 615 - __le64 next_persistent_cap; 616 - } __packed pi; 617 - int rc; 618 - 619 - rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO, 620 - NULL, 0, &pi, sizeof(pi)); 1136 + rc = cxl_find_regblock(pdev, type, map); 621 1137 if (rc) 622 1138 return rc; 623 1139 624 - *active_volatile_bytes = le64_to_cpu(pi.active_volatile_cap); 625 - *active_persistent_bytes = le64_to_cpu(pi.active_persistent_cap); 626 - *next_volatile_bytes = le64_to_cpu(pi.next_volatile_cap); 627 - *next_persistent_bytes = le64_to_cpu(pi.next_volatile_cap); 1140 + rc = cxl_map_regblock(pdev, map); 1141 + if (rc) 1142 + return rc; 628 1143 629 - *active_volatile_bytes *= CXL_CAPACITY_MULTIPLIER; 630 - *active_persistent_bytes *= CXL_CAPACITY_MULTIPLIER; 631 - *next_volatile_bytes *= CXL_CAPACITY_MULTIPLIER; 632 - *next_persistent_bytes *= CXL_CAPACITY_MULTIPLIER; 1144 + rc = cxl_probe_regs(pdev, map); 1145 + cxl_unmap_regblock(pdev, map); 633 1146 634 - return 0; 635 - } 636 - 637 - /** 638 - * cxl_mem_enumerate_cmds() - Enumerate commands for a device. 639 - * @cxlm: The device. 640 - * 641 - * Returns 0 if enumerate completed successfully. 642 - * 643 - * CXL devices have optional support for certain commands. This function will 644 - * determine the set of supported commands for the hardware and update the 645 - * enabled_cmds bitmap in the @cxlm. 646 - */ 647 - static int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm) 648 - { 649 - struct cxl_mbox_get_supported_logs *gsl; 650 - struct device *dev = &cxlm->pdev->dev; 651 - struct cxl_mem_command *cmd; 652 - int i, rc; 653 - 654 - gsl = cxl_get_gsl(cxlm); 655 - if (IS_ERR(gsl)) 656 - return PTR_ERR(gsl); 657 - 658 - rc = -ENOENT; 659 - for (i = 0; i < le16_to_cpu(gsl->entries); i++) { 660 - u32 size = le32_to_cpu(gsl->entry[i].size); 661 - uuid_t uuid = gsl->entry[i].uuid; 662 - u8 *log; 663 - 664 - dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size); 665 - 666 - if (!uuid_equal(&uuid, &log_uuid[CEL_UUID])) 667 - continue; 668 - 669 - log = kvmalloc(size, GFP_KERNEL); 670 - if (!log) { 671 - rc = -ENOMEM; 672 - goto out; 673 - } 674 - 675 - rc = cxl_xfer_log(cxlm, &uuid, size, log); 676 - if (rc) { 677 - kvfree(log); 678 - goto out; 679 - } 680 - 681 - cxl_walk_cel(cxlm, size, log); 682 - kvfree(log); 683 - 684 - /* In case CEL was bogus, enable some default commands. */ 685 - cxl_for_each_cmd(cmd) 686 - if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE) 687 - set_bit(cmd->info.id, cxlm->enabled_cmds); 688 - 689 - /* Found the required CEL */ 690 - rc = 0; 691 - } 692 - 693 - out: 694 - kvfree(gsl); 695 1147 return rc; 696 1148 } 697 1149 698 - /** 699 - * cxl_mem_identify() - Send the IDENTIFY command to the device. 700 - * @cxlm: The device to identify. 701 - * 702 - * Return: 0 if identify was executed successfully. 703 - * 704 - * This will dispatch the identify command to the device and on success populate 705 - * structures to be exported to sysfs. 706 - */ 707 - static int cxl_mem_identify(struct cxl_mem *cxlm) 1150 + static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 708 1151 { 709 - /* See CXL 2.0 Table 175 Identify Memory Device Output Payload */ 710 - struct cxl_mbox_identify { 711 - char fw_revision[0x10]; 712 - __le64 total_capacity; 713 - __le64 volatile_capacity; 714 - __le64 persistent_capacity; 715 - __le64 partition_align; 716 - __le16 info_event_log_size; 717 - __le16 warning_event_log_size; 718 - __le16 failure_event_log_size; 719 - __le16 fatal_event_log_size; 720 - __le32 lsa_size; 721 - u8 poison_list_max_mer[3]; 722 - __le16 inject_poison_limit; 723 - u8 poison_caps; 724 - u8 qos_telemetry_caps; 725 - } __packed id; 726 - int rc; 727 - 728 - rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id, 729 - sizeof(id)); 730 - if (rc < 0) 731 - return rc; 732 - 733 - cxlm->total_bytes = le64_to_cpu(id.total_capacity); 734 - cxlm->total_bytes *= CXL_CAPACITY_MULTIPLIER; 735 - 736 - cxlm->volatile_only_bytes = le64_to_cpu(id.volatile_capacity); 737 - cxlm->volatile_only_bytes *= CXL_CAPACITY_MULTIPLIER; 738 - 739 - cxlm->persistent_only_bytes = le64_to_cpu(id.persistent_capacity); 740 - cxlm->persistent_only_bytes *= CXL_CAPACITY_MULTIPLIER; 741 - 742 - cxlm->partition_align_bytes = le64_to_cpu(id.partition_align); 743 - cxlm->partition_align_bytes *= CXL_CAPACITY_MULTIPLIER; 744 - 745 - dev_dbg(&cxlm->pdev->dev, "Identify Memory Device\n" 746 - " total_bytes = %#llx\n" 747 - " volatile_only_bytes = %#llx\n" 748 - " persistent_only_bytes = %#llx\n" 749 - " partition_align_bytes = %#llx\n", 750 - cxlm->total_bytes, 751 - cxlm->volatile_only_bytes, 752 - cxlm->persistent_only_bytes, 753 - cxlm->partition_align_bytes); 754 - 755 - cxlm->lsa_size = le32_to_cpu(id.lsa_size); 756 - memcpy(cxlm->firmware_version, id.fw_revision, sizeof(id.fw_revision)); 757 - 758 - return 0; 759 - } 760 - 761 - static int cxl_mem_create_range_info(struct cxl_mem *cxlm) 762 - { 763 - int rc; 764 - 765 - if (cxlm->partition_align_bytes == 0) { 766 - cxlm->ram_range.start = 0; 767 - cxlm->ram_range.end = cxlm->volatile_only_bytes - 1; 768 - cxlm->pmem_range.start = cxlm->volatile_only_bytes; 769 - cxlm->pmem_range.end = cxlm->volatile_only_bytes + 770 - cxlm->persistent_only_bytes - 1; 771 - return 0; 772 - } 773 - 774 - rc = cxl_mem_get_partition_info(cxlm, 775 - &cxlm->active_volatile_bytes, 776 - &cxlm->active_persistent_bytes, 777 - &cxlm->next_volatile_bytes, 778 - &cxlm->next_persistent_bytes); 779 - if (rc < 0) { 780 - dev_err(&cxlm->pdev->dev, "Failed to query partition information\n"); 781 - return rc; 782 - } 783 - 784 - dev_dbg(&cxlm->pdev->dev, "Get Partition Info\n" 785 - " active_volatile_bytes = %#llx\n" 786 - " active_persistent_bytes = %#llx\n" 787 - " next_volatile_bytes = %#llx\n" 788 - " next_persistent_bytes = %#llx\n", 789 - cxlm->active_volatile_bytes, 790 - cxlm->active_persistent_bytes, 791 - cxlm->next_volatile_bytes, 792 - cxlm->next_persistent_bytes); 793 - 794 - cxlm->ram_range.start = 0; 795 - cxlm->ram_range.end = cxlm->active_volatile_bytes - 1; 796 - 797 - cxlm->pmem_range.start = cxlm->active_volatile_bytes; 798 - cxlm->pmem_range.end = cxlm->active_volatile_bytes + 799 - cxlm->active_persistent_bytes - 1; 800 - 801 - return 0; 802 - } 803 - 804 - static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id) 805 - { 1152 + struct cxl_register_map map; 806 1153 struct cxl_memdev *cxlmd; 807 1154 struct cxl_mem *cxlm; 808 1155 int rc; 1156 + 1157 + /* 1158 + * Double check the anonymous union trickery in struct cxl_regs 1159 + * FIXME switch to struct_group() 1160 + */ 1161 + BUILD_BUG_ON(offsetof(struct cxl_regs, memdev) != 1162 + offsetof(struct cxl_regs, device_regs.memdev)); 809 1163 810 1164 rc = pcim_enable_device(pdev); 811 1165 if (rc) 812 1166 return rc; 813 1167 814 - cxlm = cxl_mem_create(pdev); 1168 + cxlm = cxl_mem_create(&pdev->dev); 815 1169 if (IS_ERR(cxlm)) 816 1170 return PTR_ERR(cxlm); 817 1171 818 - rc = cxl_mem_setup_regs(cxlm); 1172 + rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map); 819 1173 if (rc) 820 1174 return rc; 821 1175 822 - rc = cxl_mem_setup_mailbox(cxlm); 1176 + rc = cxl_map_regs(cxlm, &map); 1177 + if (rc) 1178 + return rc; 1179 + 1180 + rc = cxl_pci_setup_mailbox(cxlm); 823 1181 if (rc) 824 1182 return rc; 825 1183 ··· 517 1511 if (rc) 518 1512 return rc; 519 1513 520 - cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, &cxl_memdev_fops); 1514 + cxlmd = devm_cxl_add_memdev(cxlm); 521 1515 if (IS_ERR(cxlmd)) 522 1516 return PTR_ERR(cxlmd); 523 1517 ··· 534 1528 }; 535 1529 MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl); 536 1530 537 - static struct pci_driver cxl_mem_driver = { 1531 + static struct pci_driver cxl_pci_driver = { 538 1532 .name = KBUILD_MODNAME, 539 1533 .id_table = cxl_mem_pci_tbl, 540 - .probe = cxl_mem_probe, 1534 + .probe = cxl_pci_probe, 541 1535 .driver = { 542 1536 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 543 1537 }, 544 1538 }; 545 1539 546 - static __init int cxl_mem_init(void) 547 - { 548 - struct dentry *mbox_debugfs; 549 - int rc; 550 - 551 - /* Double check the anonymous union trickery in struct cxl_regs */ 552 - BUILD_BUG_ON(offsetof(struct cxl_regs, memdev) != 553 - offsetof(struct cxl_regs, device_regs.memdev)); 554 - 555 - rc = pci_register_driver(&cxl_mem_driver); 556 - if (rc) 557 - return rc; 558 - 559 - cxl_debugfs = debugfs_create_dir("cxl", NULL); 560 - mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs); 561 - debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs, 562 - &cxl_raw_allow_all); 563 - 564 - return 0; 565 - } 566 - 567 - static __exit void cxl_mem_exit(void) 568 - { 569 - debugfs_remove_recursive(cxl_debugfs); 570 - pci_unregister_driver(&cxl_mem_driver); 571 - } 572 - 573 1540 MODULE_LICENSE("GPL v2"); 574 - module_init(cxl_mem_init); 575 - module_exit(cxl_mem_exit); 1541 + module_pci_driver(cxl_pci_driver); 576 1542 MODULE_IMPORT_NS(CXL);
+8 -6
drivers/cxl/pci.h
··· 20 20 #define CXL_REGLOC_BIR_MASK GENMASK(2, 0) 21 21 22 22 /* Register Block Identifier (RBI) */ 23 - #define CXL_REGLOC_RBI_MASK GENMASK(15, 8) 24 - #define CXL_REGLOC_RBI_EMPTY 0 25 - #define CXL_REGLOC_RBI_COMPONENT 1 26 - #define CXL_REGLOC_RBI_VIRT 2 27 - #define CXL_REGLOC_RBI_MEMDEV 3 28 - #define CXL_REGLOC_RBI_TYPES CXL_REGLOC_RBI_MEMDEV + 1 23 + enum cxl_regloc_type { 24 + CXL_REGLOC_RBI_EMPTY = 0, 25 + CXL_REGLOC_RBI_COMPONENT, 26 + CXL_REGLOC_RBI_VIRT, 27 + CXL_REGLOC_RBI_MEMDEV, 28 + CXL_REGLOC_RBI_TYPES 29 + }; 29 30 31 + #define CXL_REGLOC_RBI_MASK GENMASK(15, 8) 30 32 #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16) 31 33 32 34 #endif /* __CXL_PCI_H__ */
+144 -23
drivers/cxl/pmem.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */ 3 3 #include <linux/libnvdimm.h> 4 + #include <asm/unaligned.h> 4 5 #include <linux/device.h> 5 6 #include <linux/module.h> 6 7 #include <linux/ndctl.h> ··· 17 16 */ 18 17 static struct workqueue_struct *cxl_pmem_wq; 19 18 19 + static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX); 20 + 21 + static void clear_exclusive(void *cxlm) 22 + { 23 + clear_exclusive_cxl_commands(cxlm, exclusive_cmds); 24 + } 25 + 20 26 static void unregister_nvdimm(void *nvdimm) 21 27 { 22 28 nvdimm_delete(nvdimm); 23 29 } 24 30 25 - static int match_nvdimm_bridge(struct device *dev, const void *data) 26 - { 27 - return strcmp(dev_name(dev), "nvdimm-bridge") == 0; 28 - } 29 - 30 - static struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(void) 31 - { 32 - struct device *dev; 33 - 34 - dev = bus_find_device(&cxl_bus_type, NULL, NULL, match_nvdimm_bridge); 35 - if (!dev) 36 - return NULL; 37 - return to_cxl_nvdimm_bridge(dev); 38 - } 39 - 40 31 static int cxl_nvdimm_probe(struct device *dev) 41 32 { 42 33 struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev); 34 + struct cxl_memdev *cxlmd = cxl_nvd->cxlmd; 35 + unsigned long flags = 0, cmd_mask = 0; 36 + struct cxl_mem *cxlm = cxlmd->cxlm; 43 37 struct cxl_nvdimm_bridge *cxl_nvb; 44 - unsigned long flags = 0; 45 38 struct nvdimm *nvdimm; 46 - int rc = -ENXIO; 39 + int rc; 47 40 48 - cxl_nvb = cxl_find_nvdimm_bridge(); 41 + cxl_nvb = cxl_find_nvdimm_bridge(cxl_nvd); 49 42 if (!cxl_nvb) 50 43 return -ENXIO; 51 44 52 45 device_lock(&cxl_nvb->dev); 53 - if (!cxl_nvb->nvdimm_bus) 46 + if (!cxl_nvb->nvdimm_bus) { 47 + rc = -ENXIO; 48 + goto out; 49 + } 50 + 51 + set_exclusive_cxl_commands(cxlm, exclusive_cmds); 52 + rc = devm_add_action_or_reset(dev, clear_exclusive, cxlm); 53 + if (rc) 54 54 goto out; 55 55 56 56 set_bit(NDD_LABELING, &flags); 57 - nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags, 0, 0, 58 - NULL); 59 - if (!nvdimm) 57 + set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask); 58 + set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask); 59 + set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask); 60 + nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags, 61 + cmd_mask, 0, NULL); 62 + if (!nvdimm) { 63 + rc = -ENOMEM; 60 64 goto out; 65 + } 61 66 67 + dev_set_drvdata(dev, nvdimm); 62 68 rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm); 63 69 out: 64 70 device_unlock(&cxl_nvb->dev); ··· 80 72 .id = CXL_DEVICE_NVDIMM, 81 73 }; 82 74 75 + static int cxl_pmem_get_config_size(struct cxl_mem *cxlm, 76 + struct nd_cmd_get_config_size *cmd, 77 + unsigned int buf_len) 78 + { 79 + if (sizeof(*cmd) > buf_len) 80 + return -EINVAL; 81 + 82 + *cmd = (struct nd_cmd_get_config_size) { 83 + .config_size = cxlm->lsa_size, 84 + .max_xfer = cxlm->payload_size, 85 + }; 86 + 87 + return 0; 88 + } 89 + 90 + static int cxl_pmem_get_config_data(struct cxl_mem *cxlm, 91 + struct nd_cmd_get_config_data_hdr *cmd, 92 + unsigned int buf_len) 93 + { 94 + struct cxl_mbox_get_lsa get_lsa; 95 + int rc; 96 + 97 + if (sizeof(*cmd) > buf_len) 98 + return -EINVAL; 99 + if (struct_size(cmd, out_buf, cmd->in_length) > buf_len) 100 + return -EINVAL; 101 + 102 + get_lsa = (struct cxl_mbox_get_lsa) { 103 + .offset = cmd->in_offset, 104 + .length = cmd->in_length, 105 + }; 106 + 107 + rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LSA, &get_lsa, 108 + sizeof(get_lsa), cmd->out_buf, 109 + cmd->in_length); 110 + cmd->status = 0; 111 + 112 + return rc; 113 + } 114 + 115 + static int cxl_pmem_set_config_data(struct cxl_mem *cxlm, 116 + struct nd_cmd_set_config_hdr *cmd, 117 + unsigned int buf_len) 118 + { 119 + struct cxl_mbox_set_lsa *set_lsa; 120 + int rc; 121 + 122 + if (sizeof(*cmd) > buf_len) 123 + return -EINVAL; 124 + 125 + /* 4-byte status follows the input data in the payload */ 126 + if (struct_size(cmd, in_buf, cmd->in_length) + 4 > buf_len) 127 + return -EINVAL; 128 + 129 + set_lsa = 130 + kvzalloc(struct_size(set_lsa, data, cmd->in_length), GFP_KERNEL); 131 + if (!set_lsa) 132 + return -ENOMEM; 133 + 134 + *set_lsa = (struct cxl_mbox_set_lsa) { 135 + .offset = cmd->in_offset, 136 + }; 137 + memcpy(set_lsa->data, cmd->in_buf, cmd->in_length); 138 + 139 + rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_SET_LSA, set_lsa, 140 + struct_size(set_lsa, data, cmd->in_length), 141 + NULL, 0); 142 + 143 + /* 144 + * Set "firmware" status (4-packed bytes at the end of the input 145 + * payload. 146 + */ 147 + put_unaligned(0, (u32 *) &cmd->in_buf[cmd->in_length]); 148 + kvfree(set_lsa); 149 + 150 + return rc; 151 + } 152 + 153 + static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, 154 + void *buf, unsigned int buf_len) 155 + { 156 + struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm); 157 + unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm); 158 + struct cxl_memdev *cxlmd = cxl_nvd->cxlmd; 159 + struct cxl_mem *cxlm = cxlmd->cxlm; 160 + 161 + if (!test_bit(cmd, &cmd_mask)) 162 + return -ENOTTY; 163 + 164 + switch (cmd) { 165 + case ND_CMD_GET_CONFIG_SIZE: 166 + return cxl_pmem_get_config_size(cxlm, buf, buf_len); 167 + case ND_CMD_GET_CONFIG_DATA: 168 + return cxl_pmem_get_config_data(cxlm, buf, buf_len); 169 + case ND_CMD_SET_CONFIG_DATA: 170 + return cxl_pmem_set_config_data(cxlm, buf, buf_len); 171 + default: 172 + return -ENOTTY; 173 + } 174 + } 175 + 83 176 static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc, 84 177 struct nvdimm *nvdimm, unsigned int cmd, void *buf, 85 178 unsigned int buf_len, int *cmd_rc) 86 179 { 87 - return -ENOTTY; 180 + /* 181 + * No firmware response to translate, let the transport error 182 + * code take precedence. 183 + */ 184 + *cmd_rc = 0; 185 + 186 + if (!nvdimm) 187 + return -ENOTTY; 188 + return cxl_pmem_nvdimm_ctl(nvdimm, cmd, buf, buf_len); 88 189 } 89 190 90 191 static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb) ··· 310 193 static __init int cxl_pmem_init(void) 311 194 { 312 195 int rc; 196 + 197 + set_bit(CXL_MEM_COMMAND_ID_SET_PARTITION_INFO, exclusive_cmds); 198 + set_bit(CXL_MEM_COMMAND_ID_SET_SHUTDOWN_STATE, exclusive_cmds); 199 + set_bit(CXL_MEM_COMMAND_ID_SET_LSA, exclusive_cmds); 313 200 314 201 cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0); 315 202 if (!cxl_pmem_wq)
+1 -12
drivers/misc/ocxl/config.c
··· 33 33 34 34 static int find_dvsec(struct pci_dev *dev, int dvsec_id) 35 35 { 36 - int vsec = 0; 37 - u16 vendor, id; 38 - 39 - while ((vsec = pci_find_next_ext_capability(dev, vsec, 40 - OCXL_EXT_CAP_ID_DVSEC))) { 41 - pci_read_config_word(dev, vsec + OCXL_DVSEC_VENDOR_OFFSET, 42 - &vendor); 43 - pci_read_config_word(dev, vsec + OCXL_DVSEC_ID_OFFSET, &id); 44 - if (vendor == PCI_VENDOR_ID_IBM && id == dvsec_id) 45 - return vsec; 46 - } 47 - return 0; 36 + return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_IBM, dvsec_id); 48 37 } 49 38 50 39 static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
+6 -5
drivers/nvdimm/btt.c
··· 973 973 u64 sum; 974 974 struct btt_sb *super; 975 975 struct nd_btt *nd_btt = arena->nd_btt; 976 - const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev); 976 + const uuid_t *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev); 977 977 978 978 ret = btt_map_init(arena); 979 979 if (ret) ··· 988 988 return -ENOMEM; 989 989 990 990 strncpy(super->signature, BTT_SIG, BTT_SIG_LEN); 991 - memcpy(super->uuid, nd_btt->uuid, 16); 992 - memcpy(super->parent_uuid, parent_uuid, 16); 991 + export_uuid(super->uuid, nd_btt->uuid); 992 + export_uuid(super->parent_uuid, parent_uuid); 993 993 super->flags = cpu_to_le32(arena->flags); 994 994 super->version_major = cpu_to_le16(arena->version_major); 995 995 super->version_minor = cpu_to_le16(arena->version_minor); ··· 1574 1574 * Pointer to a new struct btt on success, NULL on failure. 1575 1575 */ 1576 1576 static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize, 1577 - u32 lbasize, u8 *uuid, struct nd_region *nd_region) 1577 + u32 lbasize, uuid_t *uuid, 1578 + struct nd_region *nd_region) 1578 1579 { 1579 1580 int ret; 1580 1581 struct btt *btt; ··· 1694 1693 } 1695 1694 nd_region = to_nd_region(nd_btt->dev.parent); 1696 1695 btt = btt_init(nd_btt, rawsize, nd_btt->lbasize, nd_btt->uuid, 1697 - nd_region); 1696 + nd_region); 1698 1697 if (!btt) 1699 1698 return -ENOMEM; 1700 1699 nd_btt->btt = btt;
+8 -6
drivers/nvdimm/btt_devs.c
··· 180 180 EXPORT_SYMBOL(is_nd_btt); 181 181 182 182 static struct device *__nd_btt_create(struct nd_region *nd_region, 183 - unsigned long lbasize, u8 *uuid, 184 - struct nd_namespace_common *ndns) 183 + unsigned long lbasize, uuid_t *uuid, 184 + struct nd_namespace_common *ndns) 185 185 { 186 186 struct nd_btt *nd_btt; 187 187 struct device *dev; ··· 244 244 */ 245 245 bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super) 246 246 { 247 - const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev); 247 + const uuid_t *ns_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev); 248 + uuid_t parent_uuid; 248 249 u64 checksum; 249 250 250 251 if (memcmp(super->signature, BTT_SIG, BTT_SIG_LEN) != 0) 251 252 return false; 252 253 253 - if (!guid_is_null((guid_t *)&super->parent_uuid)) 254 - if (memcmp(super->parent_uuid, parent_uuid, 16) != 0) 254 + import_uuid(&parent_uuid, super->parent_uuid); 255 + if (!uuid_is_null(&parent_uuid)) 256 + if (!uuid_equal(&parent_uuid, ns_uuid)) 255 257 return false; 256 258 257 259 checksum = le64_to_cpu(super->checksum); ··· 321 319 return rc; 322 320 323 321 nd_btt->lbasize = le32_to_cpu(btt_sb->external_lbasize); 324 - nd_btt->uuid = kmemdup(btt_sb->uuid, 16, GFP_KERNEL); 322 + nd_btt->uuid = kmemdup(&btt_sb->uuid, sizeof(uuid_t), GFP_KERNEL); 325 323 if (!nd_btt->uuid) 326 324 return -ENOMEM; 327 325
+4 -36
drivers/nvdimm/core.c
··· 207 207 } 208 208 EXPORT_SYMBOL_GPL(to_nvdimm_bus_dev); 209 209 210 - static bool is_uuid_sep(char sep) 211 - { 212 - if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0') 213 - return true; 214 - return false; 215 - } 216 - 217 - static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf, 218 - size_t len) 219 - { 220 - const char *str = buf; 221 - u8 uuid[16]; 222 - int i; 223 - 224 - for (i = 0; i < 16; i++) { 225 - if (!isxdigit(str[0]) || !isxdigit(str[1])) { 226 - dev_dbg(dev, "pos: %d buf[%zd]: %c buf[%zd]: %c\n", 227 - i, str - buf, str[0], 228 - str + 1 - buf, str[1]); 229 - return -EINVAL; 230 - } 231 - 232 - uuid[i] = (hex_to_bin(str[0]) << 4) | hex_to_bin(str[1]); 233 - str += 2; 234 - if (is_uuid_sep(*str)) 235 - str++; 236 - } 237 - 238 - memcpy(uuid_out, uuid, sizeof(uuid)); 239 - return 0; 240 - } 241 - 242 210 /** 243 211 * nd_uuid_store: common implementation for writing 'uuid' sysfs attributes 244 212 * @dev: container device for the uuid property ··· 217 249 * (driver detached) 218 250 * LOCKING: expects nd_device_lock() is held on entry 219 251 */ 220 - int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf, 252 + int nd_uuid_store(struct device *dev, uuid_t **uuid_out, const char *buf, 221 253 size_t len) 222 254 { 223 - u8 uuid[16]; 255 + uuid_t uuid; 224 256 int rc; 225 257 226 258 if (dev->driver) 227 259 return -EBUSY; 228 260 229 - rc = nd_uuid_parse(dev, uuid, buf, len); 261 + rc = uuid_parse(buf, &uuid); 230 262 if (rc) 231 263 return rc; 232 264 233 265 kfree(*uuid_out); 234 - *uuid_out = kmemdup(uuid, sizeof(uuid), GFP_KERNEL); 266 + *uuid_out = kmemdup(&uuid, sizeof(uuid), GFP_KERNEL); 235 267 if (!(*uuid_out)) 236 268 return -ENOMEM; 237 269
+102 -37
drivers/nvdimm/label.c
··· 17 17 static guid_t nvdimm_pfn_guid; 18 18 static guid_t nvdimm_dax_guid; 19 19 20 + static uuid_t nvdimm_btt_uuid; 21 + static uuid_t nvdimm_btt2_uuid; 22 + static uuid_t nvdimm_pfn_uuid; 23 + static uuid_t nvdimm_dax_uuid; 24 + 25 + static uuid_t cxl_region_uuid; 26 + static uuid_t cxl_namespace_uuid; 27 + 20 28 static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0"; 21 29 22 30 static u32 best_seq(u32 a, u32 b) ··· 329 321 return true; 330 322 } 331 323 332 - char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags) 324 + char *nd_label_gen_id(struct nd_label_id *label_id, const uuid_t *uuid, 325 + u32 flags) 333 326 { 334 327 if (!label_id || !uuid) 335 328 return NULL; ··· 360 351 { 361 352 u64 sum, sum_save; 362 353 363 - if (!namespace_label_has(ndd, checksum)) 354 + if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum)) 364 355 return true; 365 356 366 357 sum_save = nsl_get_checksum(ndd, nd_label); ··· 375 366 { 376 367 u64 sum; 377 368 378 - if (!namespace_label_has(ndd, checksum)) 369 + if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum)) 379 370 return; 380 371 nsl_set_checksum(ndd, nd_label, 0); 381 372 sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); ··· 409 400 struct nvdimm *nvdimm = to_nvdimm(ndd->dev); 410 401 struct nd_namespace_label *nd_label; 411 402 struct nd_region *nd_region = NULL; 412 - u8 label_uuid[NSLABEL_UUID_LEN]; 413 403 struct nd_label_id label_id; 414 404 struct resource *res; 405 + uuid_t label_uuid; 415 406 u32 flags; 416 407 417 408 nd_label = to_label(ndd, slot); ··· 419 410 if (!slot_valid(ndd, nd_label, slot)) 420 411 continue; 421 412 422 - memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 413 + nsl_get_uuid(ndd, nd_label, &label_uuid); 423 414 flags = nsl_get_flags(ndd, nd_label); 424 415 if (test_bit(NDD_NOBLK, &nvdimm->flags)) 425 416 flags &= ~NSLABEL_FLAG_LOCAL; 426 - nd_label_gen_id(&label_id, label_uuid, flags); 417 + nd_label_gen_id(&label_id, &label_uuid, flags); 427 418 res = nvdimm_allocate_dpa(ndd, &label_id, 428 419 nsl_get_dpa(ndd, nd_label), 429 420 nsl_get_rawsize(ndd, nd_label)); ··· 733 724 - (unsigned long) to_namespace_index(ndd, 0); 734 725 } 735 726 736 - static enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid) 727 + static enum nvdimm_claim_class guid_to_nvdimm_cclass(guid_t *guid) 737 728 { 738 729 if (guid_equal(guid, &nvdimm_btt_guid)) 739 730 return NVDIMM_CCLASS_BTT; ··· 744 735 else if (guid_equal(guid, &nvdimm_dax_guid)) 745 736 return NVDIMM_CCLASS_DAX; 746 737 else if (guid_equal(guid, &guid_null)) 738 + return NVDIMM_CCLASS_NONE; 739 + 740 + return NVDIMM_CCLASS_UNKNOWN; 741 + } 742 + 743 + /* CXL labels store UUIDs instead of GUIDs for the same data */ 744 + static enum nvdimm_claim_class uuid_to_nvdimm_cclass(uuid_t *uuid) 745 + { 746 + if (uuid_equal(uuid, &nvdimm_btt_uuid)) 747 + return NVDIMM_CCLASS_BTT; 748 + else if (uuid_equal(uuid, &nvdimm_btt2_uuid)) 749 + return NVDIMM_CCLASS_BTT2; 750 + else if (uuid_equal(uuid, &nvdimm_pfn_uuid)) 751 + return NVDIMM_CCLASS_PFN; 752 + else if (uuid_equal(uuid, &nvdimm_dax_uuid)) 753 + return NVDIMM_CCLASS_DAX; 754 + else if (uuid_equal(uuid, &uuid_null)) 747 755 return NVDIMM_CCLASS_NONE; 748 756 749 757 return NVDIMM_CCLASS_UNKNOWN; ··· 787 761 return &guid_null; 788 762 } 789 763 764 + /* CXL labels store UUIDs instead of GUIDs for the same data */ 765 + static const uuid_t *to_abstraction_uuid(enum nvdimm_claim_class claim_class, 766 + uuid_t *target) 767 + { 768 + if (claim_class == NVDIMM_CCLASS_BTT) 769 + return &nvdimm_btt_uuid; 770 + else if (claim_class == NVDIMM_CCLASS_BTT2) 771 + return &nvdimm_btt2_uuid; 772 + else if (claim_class == NVDIMM_CCLASS_PFN) 773 + return &nvdimm_pfn_uuid; 774 + else if (claim_class == NVDIMM_CCLASS_DAX) 775 + return &nvdimm_dax_uuid; 776 + else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 777 + /* 778 + * If we're modifying a namespace for which we don't 779 + * know the claim_class, don't touch the existing uuid. 780 + */ 781 + return target; 782 + } else 783 + return &uuid_null; 784 + } 785 + 790 786 static void reap_victim(struct nd_mapping *nd_mapping, 791 787 struct nd_label_ent *victim) 792 788 { ··· 823 775 static void nsl_set_type_guid(struct nvdimm_drvdata *ndd, 824 776 struct nd_namespace_label *nd_label, guid_t *guid) 825 777 { 826 - if (namespace_label_has(ndd, type_guid)) 827 - guid_copy(&nd_label->type_guid, guid); 778 + if (efi_namespace_label_has(ndd, type_guid)) 779 + guid_copy(&nd_label->efi.type_guid, guid); 828 780 } 829 781 830 782 bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd, 831 783 struct nd_namespace_label *nd_label, guid_t *guid) 832 784 { 833 - if (!namespace_label_has(ndd, type_guid)) 785 + if (ndd->cxl || !efi_namespace_label_has(ndd, type_guid)) 834 786 return true; 835 - if (!guid_equal(&nd_label->type_guid, guid)) { 787 + if (!guid_equal(&nd_label->efi.type_guid, guid)) { 836 788 dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", guid, 837 - &nd_label->type_guid); 789 + &nd_label->efi.type_guid); 838 790 return false; 839 791 } 840 792 return true; ··· 844 796 struct nd_namespace_label *nd_label, 845 797 enum nvdimm_claim_class claim_class) 846 798 { 847 - if (!namespace_label_has(ndd, abstraction_guid)) 799 + if (ndd->cxl) { 800 + uuid_t uuid; 801 + 802 + import_uuid(&uuid, nd_label->cxl.abstraction_uuid); 803 + export_uuid(nd_label->cxl.abstraction_uuid, 804 + to_abstraction_uuid(claim_class, &uuid)); 848 805 return; 849 - guid_copy(&nd_label->abstraction_guid, 806 + } 807 + 808 + if (!efi_namespace_label_has(ndd, abstraction_guid)) 809 + return; 810 + guid_copy(&nd_label->efi.abstraction_guid, 850 811 to_abstraction_guid(claim_class, 851 - &nd_label->abstraction_guid)); 812 + &nd_label->efi.abstraction_guid)); 852 813 } 853 814 854 815 enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd, 855 816 struct nd_namespace_label *nd_label) 856 817 { 857 - if (!namespace_label_has(ndd, abstraction_guid)) 818 + if (ndd->cxl) { 819 + uuid_t uuid; 820 + 821 + import_uuid(&uuid, nd_label->cxl.abstraction_uuid); 822 + return uuid_to_nvdimm_cclass(&uuid); 823 + } 824 + if (!efi_namespace_label_has(ndd, abstraction_guid)) 858 825 return NVDIMM_CCLASS_NONE; 859 - return to_nvdimm_cclass(&nd_label->abstraction_guid); 826 + return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid); 860 827 } 861 828 862 829 static int __pmem_label_update(struct nd_region *nd_region, ··· 914 851 915 852 nd_label = to_label(ndd, slot); 916 853 memset(nd_label, 0, sizeof_namespace_label(ndd)); 917 - memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN); 854 + nsl_set_uuid(ndd, nd_label, nspm->uuid); 918 855 nsl_set_name(ndd, nd_label, nspm->alt_name); 919 856 nsl_set_flags(ndd, nd_label, flags); 920 857 nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings); 858 + nsl_set_nrange(ndd, nd_label, 1); 921 859 nsl_set_position(ndd, nd_label, pos); 922 860 nsl_set_isetcookie(ndd, nd_label, cookie); 923 861 nsl_set_rawsize(ndd, nd_label, resource_size(res)); ··· 942 878 list_for_each_entry(label_ent, &nd_mapping->labels, list) { 943 879 if (!label_ent->label) 944 880 continue; 945 - if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) 946 - || memcmp(nspm->uuid, label_ent->label->uuid, 947 - NSLABEL_UUID_LEN) == 0) 881 + if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) || 882 + nsl_uuid_equal(ndd, label_ent->label, nspm->uuid)) 948 883 reap_victim(nd_mapping, label_ent); 949 884 } 950 885 ··· 1004 941 struct nd_namespace_label *nd_label, 1005 942 u64 isetcookie) 1006 943 { 1007 - if (namespace_label_has(ndd, type_guid)) { 944 + if (efi_namespace_label_has(ndd, type_guid)) { 1008 945 nsl_set_isetcookie(ndd, nd_label, isetcookie); 1009 946 return; 1010 947 } ··· 1015 952 struct nd_namespace_label *nd_label, 1016 953 u64 isetcookie) 1017 954 { 1018 - if (!namespace_label_has(ndd, type_guid)) 955 + if (!efi_namespace_label_has(ndd, type_guid)) 1019 956 return true; 1020 957 1021 958 if (nsl_get_isetcookie(ndd, nd_label) != isetcookie) { ··· 1031 968 struct nd_namespace_label *nd_label, int nlabel, 1032 969 bool first) 1033 970 { 1034 - if (!namespace_label_has(ndd, type_guid)) { 971 + if (!efi_namespace_label_has(ndd, type_guid)) { 1035 972 nsl_set_nlabel(ndd, nd_label, 0); /* N/A */ 1036 973 return; 1037 974 } ··· 1042 979 struct nd_namespace_label *nd_label, 1043 980 bool first) 1044 981 { 1045 - if (!namespace_label_has(ndd, type_guid)) { 982 + if (!efi_namespace_label_has(ndd, type_guid)) { 1046 983 nsl_set_position(ndd, nd_label, 0); 1047 984 return; 1048 985 } ··· 1068 1005 unsigned long *free, *victim_map = NULL; 1069 1006 struct resource *res, **old_res_list; 1070 1007 struct nd_label_id label_id; 1071 - u8 uuid[NSLABEL_UUID_LEN]; 1072 1008 int min_dpa_idx = 0; 1073 1009 LIST_HEAD(list); 1074 1010 u32 nslot, slot; ··· 1105 1043 /* mark unused labels for garbage collection */ 1106 1044 for_each_clear_bit_le(slot, free, nslot) { 1107 1045 nd_label = to_label(ndd, slot); 1108 - memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 1109 - if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 1046 + if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid)) 1110 1047 continue; 1111 1048 res = to_resource(ndd, nd_label); 1112 1049 if (res && is_old_resource(res, old_res_list, ··· 1174 1113 1175 1114 nd_label = to_label(ndd, slot); 1176 1115 memset(nd_label, 0, sizeof_namespace_label(ndd)); 1177 - memcpy(nd_label->uuid, nsblk->uuid, NSLABEL_UUID_LEN); 1116 + nsl_set_uuid(ndd, nd_label, nsblk->uuid); 1178 1117 nsl_set_name(ndd, nd_label, nsblk->alt_name); 1179 1118 nsl_set_flags(ndd, nd_label, NSLABEL_FLAG_LOCAL); 1180 1119 ··· 1222 1161 if (!nd_label) 1223 1162 continue; 1224 1163 nlabel++; 1225 - memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 1226 - if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 1164 + if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid)) 1227 1165 continue; 1228 1166 nlabel--; 1229 1167 list_move(&label_ent->list, &list); ··· 1252 1192 } 1253 1193 for_each_clear_bit_le(slot, free, nslot) { 1254 1194 nd_label = to_label(ndd, slot); 1255 - memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 1256 - if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 1195 + if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid)) 1257 1196 continue; 1258 1197 res = to_resource(ndd, nd_label); 1259 1198 res->flags &= ~DPA_RESOURCE_ADJUSTED; ··· 1332 1273 return max(num_labels, old_num_labels); 1333 1274 } 1334 1275 1335 - static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) 1276 + static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid) 1336 1277 { 1337 1278 struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 1338 1279 struct nd_label_ent *label_ent, *e; 1339 1280 struct nd_namespace_index *nsindex; 1340 - u8 label_uuid[NSLABEL_UUID_LEN]; 1341 1281 unsigned long *free; 1342 1282 LIST_HEAD(list); 1343 1283 u32 nslot, slot; ··· 1356 1298 if (!nd_label) 1357 1299 continue; 1358 1300 active++; 1359 - memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 1360 - if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0) 1301 + if (!nsl_uuid_equal(ndd, nd_label, uuid)) 1361 1302 continue; 1362 1303 active--; 1363 1304 slot = to_slot(ndd, nd_label); ··· 1451 1394 WARN_ON(guid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_guid)); 1452 1395 WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid)); 1453 1396 WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid)); 1397 + 1398 + WARN_ON(uuid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_uuid)); 1399 + WARN_ON(uuid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_uuid)); 1400 + WARN_ON(uuid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_uuid)); 1401 + WARN_ON(uuid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_uuid)); 1402 + 1403 + WARN_ON(uuid_parse(CXL_REGION_UUID, &cxl_region_uuid)); 1404 + WARN_ON(uuid_parse(CXL_NAMESPACE_UUID, &cxl_namespace_uuid)); 1454 1405 1455 1406 return 0; 1456 1407 }
+89 -5
drivers/nvdimm/label.h
··· 34 34 * struct nd_namespace_index - label set superblock 35 35 * @sig: NAMESPACE_INDEX\0 36 36 * @flags: placeholder 37 + * @labelsize: log2 size (v1 labels 128 bytes v2 labels 256 bytes) 37 38 * @seq: sequence number for this index 38 39 * @myoff: offset of this index in label area 39 40 * @mysize: size of this index struct ··· 44 43 * @major: label area major version 45 44 * @minor: label area minor version 46 45 * @checksum: fletcher64 of all fields 47 - * @free[0]: bitmap, nlabel bits 46 + * @free: bitmap, nlabel bits 48 47 * 49 48 * The size of free[] is rounded up so the total struct size is a 50 49 * multiple of NSINDEX_ALIGN bytes. Any bits this allocates beyond ··· 67 66 }; 68 67 69 68 /** 70 - * struct nd_namespace_label - namespace superblock 69 + * struct cxl_region_label - CXL 2.0 Table 211 70 + * @type: uuid identifying this label format (region) 71 + * @uuid: uuid for the region this label describes 72 + * @flags: NSLABEL_FLAG_UPDATING (all other flags reserved) 73 + * @nlabel: 1 per interleave-way in the region 74 + * @position: this label's position in the set 75 + * @dpa: start address in device-local capacity for this label 76 + * @rawsize: size of this label's contribution to region 77 + * @hpa: mandatory system physical address to map this region 78 + * @slot: slot id of this label in label area 79 + * @ig: interleave granularity (1 << @ig) * 256 bytes 80 + * @align: alignment in SZ_256M blocks 81 + * @reserved: reserved 82 + * @checksum: fletcher64 sum of this label 83 + */ 84 + struct cxl_region_label { 85 + u8 type[NSLABEL_UUID_LEN]; 86 + u8 uuid[NSLABEL_UUID_LEN]; 87 + __le32 flags; 88 + __le16 nlabel; 89 + __le16 position; 90 + __le64 dpa; 91 + __le64 rawsize; 92 + __le64 hpa; 93 + __le32 slot; 94 + __le32 ig; 95 + __le32 align; 96 + u8 reserved[0xac]; 97 + __le64 checksum; 98 + }; 99 + 100 + /** 101 + * struct nvdimm_efi_label - namespace superblock 71 102 * @uuid: UUID per RFC 4122 72 103 * @name: optional name (NULL-terminated) 73 104 * @flags: see NSLABEL_FLAG_* ··· 110 77 * @dpa: DPA of NVM range on this DIMM 111 78 * @rawsize: size of namespace 112 79 * @slot: slot of this label in label area 113 - * @unused: must be zero 80 + * @align: physical address alignment of the namespace 81 + * @reserved: reserved 82 + * @type_guid: copy of struct acpi_nfit_system_address.range_guid 83 + * @abstraction_guid: personality id (btt, btt2, fsdax, devdax....) 84 + * @reserved2: reserved 85 + * @checksum: fletcher64 sum of this object 114 86 */ 115 - struct nd_namespace_label { 87 + struct nvdimm_efi_label { 116 88 u8 uuid[NSLABEL_UUID_LEN]; 117 89 u8 name[NSLABEL_NAME_LEN]; 118 90 __le32 flags; ··· 130 92 __le32 slot; 131 93 /* 132 94 * Accessing fields past this point should be gated by a 133 - * namespace_label_has() check. 95 + * efi_namespace_label_has() check. 134 96 */ 135 97 u8 align; 136 98 u8 reserved[3]; ··· 140 102 __le64 checksum; 141 103 }; 142 104 105 + /** 106 + * struct nvdimm_cxl_label - CXL 2.0 Table 212 107 + * @type: uuid identifying this label format (namespace) 108 + * @uuid: uuid for the namespace this label describes 109 + * @name: friendly name for the namespace 110 + * @flags: NSLABEL_FLAG_UPDATING (all other flags reserved) 111 + * @nrange: discontiguous namespace support 112 + * @position: this label's position in the set 113 + * @dpa: start address in device-local capacity for this label 114 + * @rawsize: size of this label's contribution to namespace 115 + * @slot: slot id of this label in label area 116 + * @align: alignment in SZ_256M blocks 117 + * @region_uuid: host interleave set identifier 118 + * @abstraction_uuid: personality driver for this namespace 119 + * @lbasize: address geometry for disk-like personalities 120 + * @reserved: reserved 121 + * @checksum: fletcher64 sum of this label 122 + */ 123 + struct nvdimm_cxl_label { 124 + u8 type[NSLABEL_UUID_LEN]; 125 + u8 uuid[NSLABEL_UUID_LEN]; 126 + u8 name[NSLABEL_NAME_LEN]; 127 + __le32 flags; 128 + __le16 nrange; 129 + __le16 position; 130 + __le64 dpa; 131 + __le64 rawsize; 132 + __le32 slot; 133 + __le32 align; 134 + u8 region_uuid[16]; 135 + u8 abstraction_uuid[16]; 136 + __le16 lbasize; 137 + u8 reserved[0x56]; 138 + __le64 checksum; 139 + }; 140 + 141 + struct nd_namespace_label { 142 + union { 143 + struct nvdimm_cxl_label cxl; 144 + struct nvdimm_efi_label efi; 145 + }; 146 + }; 147 + 143 148 #define NVDIMM_BTT_GUID "8aed63a2-29a2-4c66-8b12-f05d15d3922a" 144 149 #define NVDIMM_BTT2_GUID "18633bfc-1735-4217-8ac9-17239282d3f8" 145 150 #define NVDIMM_PFN_GUID "266400ba-fb9f-4677-bcb0-968f11d0d225" 146 151 #define NVDIMM_DAX_GUID "97a86d9c-3cdd-4eda-986f-5068b4f80088" 152 + 153 + #define CXL_REGION_UUID "529d7c61-da07-47c4-a93f-ecdf2c06f444" 154 + #define CXL_NAMESPACE_UUID "68bb2c0a-5a77-4937-9f85-3caf41a0f93c" 147 155 148 156 /** 149 157 * struct nd_label_id - identifier string for dpa allocation
+52 -43
drivers/nvdimm/namespace_devs.c
··· 51 51 52 52 static int is_uuid_busy(struct device *dev, void *data) 53 53 { 54 - u8 *uuid1 = data, *uuid2 = NULL; 54 + uuid_t *uuid1 = data, *uuid2 = NULL; 55 55 56 56 if (is_namespace_pmem(dev)) { 57 57 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ··· 71 71 uuid2 = nd_pfn->uuid; 72 72 } 73 73 74 - if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0) 74 + if (uuid2 && uuid_equal(uuid1, uuid2)) 75 75 return -EBUSY; 76 76 77 77 return 0; ··· 89 89 * @dev: any device on a nvdimm_bus 90 90 * @uuid: uuid to check 91 91 */ 92 - bool nd_is_uuid_unique(struct device *dev, u8 *uuid) 92 + bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid) 93 93 { 94 94 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 95 95 ··· 192 192 } 193 193 EXPORT_SYMBOL(nvdimm_namespace_disk_name); 194 194 195 - const u8 *nd_dev_to_uuid(struct device *dev) 195 + const uuid_t *nd_dev_to_uuid(struct device *dev) 196 196 { 197 - static const u8 null_uuid[16]; 198 - 199 197 if (!dev) 200 - return null_uuid; 198 + return &uuid_null; 201 199 202 200 if (is_namespace_pmem(dev)) { 203 201 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ··· 206 208 207 209 return nsblk->uuid; 208 210 } else 209 - return null_uuid; 211 + return &uuid_null; 210 212 } 211 213 EXPORT_SYMBOL(nd_dev_to_uuid); 212 214 ··· 936 938 res->end = res->start + size - 1; 937 939 } 938 940 939 - static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where) 941 + static bool uuid_not_set(const uuid_t *uuid, struct device *dev, 942 + const char *where) 940 943 { 941 944 if (!uuid) { 942 945 dev_dbg(dev, "%s: uuid not set\n", where); ··· 956 957 struct nd_label_id label_id; 957 958 u32 flags = 0, remainder; 958 959 int rc, i, id = -1; 959 - u8 *uuid = NULL; 960 + uuid_t *uuid = NULL; 960 961 961 962 if (dev->driver || ndns->claim) 962 963 return -EBUSY; ··· 1049 1050 { 1050 1051 struct nd_region *nd_region = to_nd_region(dev->parent); 1051 1052 unsigned long long val; 1052 - u8 **uuid = NULL; 1053 + uuid_t **uuid = NULL; 1053 1054 int rc; 1054 1055 1055 1056 rc = kstrtoull(buf, 0, &val); ··· 1146 1147 } 1147 1148 static DEVICE_ATTR(size, 0444, size_show, size_store); 1148 1149 1149 - static u8 *namespace_to_uuid(struct device *dev) 1150 + static uuid_t *namespace_to_uuid(struct device *dev) 1150 1151 { 1151 1152 if (is_namespace_pmem(dev)) { 1152 1153 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ··· 1160 1161 return ERR_PTR(-ENXIO); 1161 1162 } 1162 1163 1163 - static ssize_t uuid_show(struct device *dev, 1164 - struct device_attribute *attr, char *buf) 1164 + static ssize_t uuid_show(struct device *dev, struct device_attribute *attr, 1165 + char *buf) 1165 1166 { 1166 - u8 *uuid = namespace_to_uuid(dev); 1167 + uuid_t *uuid = namespace_to_uuid(dev); 1167 1168 1168 1169 if (IS_ERR(uuid)) 1169 1170 return PTR_ERR(uuid); ··· 1180 1181 * @old_uuid: reference to the uuid storage location in the namespace object 1181 1182 */ 1182 1183 static int namespace_update_uuid(struct nd_region *nd_region, 1183 - struct device *dev, u8 *new_uuid, u8 **old_uuid) 1184 + struct device *dev, uuid_t *new_uuid, 1185 + uuid_t **old_uuid) 1184 1186 { 1185 1187 u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0; 1186 1188 struct nd_label_id old_label_id; ··· 1231 1231 list_for_each_entry(label_ent, &nd_mapping->labels, list) { 1232 1232 struct nd_namespace_label *nd_label = label_ent->label; 1233 1233 struct nd_label_id label_id; 1234 + uuid_t uuid; 1234 1235 1235 1236 if (!nd_label) 1236 1237 continue; 1237 - nd_label_gen_id(&label_id, nd_label->uuid, 1238 + nsl_get_uuid(ndd, nd_label, &uuid); 1239 + nd_label_gen_id(&label_id, &uuid, 1238 1240 nsl_get_flags(ndd, nd_label)); 1239 1241 if (strcmp(old_label_id.id, label_id.id) == 0) 1240 1242 set_bit(ND_LABEL_REAP, &label_ent->flags); ··· 1253 1251 struct device_attribute *attr, const char *buf, size_t len) 1254 1252 { 1255 1253 struct nd_region *nd_region = to_nd_region(dev->parent); 1256 - u8 *uuid = NULL; 1254 + uuid_t *uuid = NULL; 1255 + uuid_t **ns_uuid; 1257 1256 ssize_t rc = 0; 1258 - u8 **ns_uuid; 1259 1257 1260 1258 if (is_namespace_pmem(dev)) { 1261 1259 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); ··· 1380 1378 { 1381 1379 struct nd_region *nd_region = to_nd_region(dev->parent); 1382 1380 struct nd_label_id label_id; 1381 + uuid_t *uuid = NULL; 1383 1382 int count = 0, i; 1384 - u8 *uuid = NULL; 1385 1383 u32 flags = 0; 1386 1384 1387 1385 nvdimm_bus_lock(dev); ··· 1833 1831 return devs; 1834 1832 } 1835 1833 1836 - static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, 1837 - u64 cookie, u16 pos) 1834 + static bool has_uuid_at_pos(struct nd_region *nd_region, const uuid_t *uuid, 1835 + u64 cookie, u16 pos) 1838 1836 { 1839 1837 struct nd_namespace_label *found = NULL; 1840 1838 int i; ··· 1848 1846 1849 1847 list_for_each_entry(label_ent, &nd_mapping->labels, list) { 1850 1848 struct nd_namespace_label *nd_label = label_ent->label; 1851 - u16 position, nlabel; 1849 + u16 position; 1852 1850 1853 1851 if (!nd_label) 1854 1852 continue; 1855 1853 position = nsl_get_position(ndd, nd_label); 1856 - nlabel = nsl_get_nlabel(ndd, nd_label); 1857 1854 1858 1855 if (!nsl_validate_isetcookie(ndd, nd_label, cookie)) 1859 1856 continue; 1860 1857 1861 - if (memcmp(nd_label->uuid, uuid, NSLABEL_UUID_LEN) != 0) 1858 + if (!nsl_uuid_equal(ndd, nd_label, uuid)) 1862 1859 continue; 1863 1860 1864 1861 if (!nsl_validate_type_guid(ndd, nd_label, ··· 1869 1868 return false; 1870 1869 } 1871 1870 found_uuid = true; 1872 - if (nlabel != nd_region->ndr_mappings) 1871 + if (!nsl_validate_nlabel(nd_region, ndd, nd_label)) 1873 1872 continue; 1874 1873 if (position != pos) 1875 1874 continue; ··· 1882 1881 return found != NULL; 1883 1882 } 1884 1883 1885 - static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) 1884 + static int select_pmem_id(struct nd_region *nd_region, const uuid_t *pmem_id) 1886 1885 { 1887 1886 int i; 1888 1887 ··· 1901 1900 nd_label = label_ent->label; 1902 1901 if (!nd_label) 1903 1902 continue; 1904 - if (memcmp(nd_label->uuid, pmem_id, NSLABEL_UUID_LEN) == 0) 1903 + if (nsl_uuid_equal(ndd, nd_label, pmem_id)) 1905 1904 break; 1906 1905 nd_label = NULL; 1907 1906 } ··· 1924 1923 /* pass */; 1925 1924 else { 1926 1925 dev_dbg(&nd_region->dev, "%s invalid label for %pUb\n", 1927 - dev_name(ndd->dev), nd_label->uuid); 1926 + dev_name(ndd->dev), 1927 + nsl_uuid_raw(ndd, nd_label)); 1928 1928 return -EINVAL; 1929 1929 } 1930 1930 ··· 1955 1953 resource_size_t size = 0; 1956 1954 struct resource *res; 1957 1955 struct device *dev; 1956 + uuid_t uuid; 1958 1957 int rc = 0; 1959 1958 u16 i; 1960 1959 ··· 1966 1963 1967 1964 if (!nsl_validate_isetcookie(ndd, nd_label, cookie)) { 1968 1965 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", 1969 - nd_label->uuid); 1966 + nsl_uuid_raw(ndd, nd_label)); 1970 1967 if (!nsl_validate_isetcookie(ndd, nd_label, altcookie)) 1971 1968 return ERR_PTR(-EAGAIN); 1972 1969 1973 1970 dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n", 1974 - nd_label->uuid); 1971 + nsl_uuid_raw(ndd, nd_label)); 1975 1972 } 1976 1973 1977 1974 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); ··· 1987 1984 res->flags = IORESOURCE_MEM; 1988 1985 1989 1986 for (i = 0; i < nd_region->ndr_mappings; i++) { 1990 - if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) 1987 + uuid_t uuid; 1988 + 1989 + nsl_get_uuid(ndd, nd_label, &uuid); 1990 + if (has_uuid_at_pos(nd_region, &uuid, cookie, i)) 1991 1991 continue; 1992 - if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i)) 1992 + if (has_uuid_at_pos(nd_region, &uuid, altcookie, i)) 1993 1993 continue; 1994 1994 break; 1995 1995 } ··· 2006 2000 * find a dimm with two instances of the same uuid. 2007 2001 */ 2008 2002 dev_err(&nd_region->dev, "%s missing label for %pUb\n", 2009 - nvdimm_name(nvdimm), nd_label->uuid); 2003 + nvdimm_name(nvdimm), nsl_uuid_raw(ndd, nd_label)); 2010 2004 rc = -EINVAL; 2011 2005 goto err; 2012 2006 } ··· 2019 2013 * the dimm being enabled (i.e. nd_label_reserve_dpa() 2020 2014 * succeeded). 2021 2015 */ 2022 - rc = select_pmem_id(nd_region, nd_label->uuid); 2016 + nsl_get_uuid(ndd, nd_label, &uuid); 2017 + rc = select_pmem_id(nd_region, &uuid); 2023 2018 if (rc) 2024 2019 goto err; 2025 2020 ··· 2046 2039 WARN_ON(nspm->alt_name || nspm->uuid); 2047 2040 nspm->alt_name = kmemdup(nsl_ref_name(ndd, label0), 2048 2041 NSLABEL_NAME_LEN, GFP_KERNEL); 2049 - nspm->uuid = kmemdup((void __force *) label0->uuid, 2050 - NSLABEL_UUID_LEN, GFP_KERNEL); 2042 + nsl_get_uuid(ndd, label0, &uuid); 2043 + nspm->uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL); 2051 2044 nspm->lbasize = nsl_get_lbasize(ndd, label0); 2052 2045 nspm->nsio.common.claim_class = 2053 2046 nsl_get_claim_class(ndd, label0); ··· 2224 2217 int i; 2225 2218 2226 2219 for (i = 0; i < count; i++) { 2227 - u8 *uuid = namespace_to_uuid(devs[i]); 2220 + uuid_t *uuid = namespace_to_uuid(devs[i]); 2228 2221 struct resource *res; 2229 2222 2230 - if (IS_ERR_OR_NULL(uuid)) { 2223 + if (IS_ERR(uuid)) { 2231 2224 WARN_ON(1); 2232 2225 continue; 2233 2226 } 2234 2227 2235 - if (memcmp(uuid, nd_label->uuid, NSLABEL_UUID_LEN) != 0) 2228 + if (!nsl_uuid_equal(ndd, nd_label, uuid)) 2236 2229 continue; 2237 2230 if (is_namespace_blk(devs[i])) { 2238 2231 res = nsblk_add_resource(nd_region, ndd, ··· 2243 2236 nd_dbg_dpa(nd_region, ndd, res, "%d assign\n", count); 2244 2237 } else { 2245 2238 dev_err(&nd_region->dev, 2246 - "error: conflicting extents for uuid: %pUb\n", 2247 - nd_label->uuid); 2239 + "error: conflicting extents for uuid: %pUb\n", 2240 + uuid); 2248 2241 return -ENXIO; 2249 2242 } 2250 2243 break; ··· 2264 2257 char name[NSLABEL_NAME_LEN]; 2265 2258 struct device *dev = NULL; 2266 2259 struct resource *res; 2260 + uuid_t uuid; 2267 2261 2268 2262 if (!nsl_validate_type_guid(ndd, nd_label, &nd_set->type_guid)) 2269 2263 return ERR_PTR(-EAGAIN); ··· 2279 2271 dev->parent = &nd_region->dev; 2280 2272 nsblk->id = -1; 2281 2273 nsblk->lbasize = nsl_get_lbasize(ndd, nd_label); 2282 - nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN, GFP_KERNEL); 2274 + nsl_get_uuid(ndd, nd_label, &uuid); 2275 + nsblk->uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL); 2283 2276 nsblk->common.claim_class = nsl_get_claim_class(ndd, nd_label); 2284 2277 if (!nsblk->uuid) 2285 2278 goto blk_err;
+3 -2
drivers/nvdimm/nd-core.h
··· 126 126 void nd_synchronize(void); 127 127 void __nd_device_register(struct device *dev); 128 128 struct nd_label_id; 129 - char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags); 130 - bool nd_is_uuid_unique(struct device *dev, u8 *uuid); 129 + char *nd_label_gen_id(struct nd_label_id *label_id, const uuid_t *uuid, 130 + u32 flags); 131 + bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid); 131 132 struct nd_region; 132 133 struct nvdimm_drvdata; 133 134 struct nd_mapping;
+156 -29
drivers/nvdimm/nd.h
··· 30 30 int nslabel_size; 31 31 struct nd_cmd_get_config_size nsarea; 32 32 void *data; 33 + bool cxl; 33 34 int ns_current, ns_next; 34 35 struct resource dpa; 35 36 struct kref kref; ··· 39 38 static inline const u8 *nsl_ref_name(struct nvdimm_drvdata *ndd, 40 39 struct nd_namespace_label *nd_label) 41 40 { 42 - return nd_label->name; 41 + if (ndd->cxl) 42 + return nd_label->cxl.name; 43 + return nd_label->efi.name; 43 44 } 44 45 45 46 static inline u8 *nsl_get_name(struct nvdimm_drvdata *ndd, 46 47 struct nd_namespace_label *nd_label, u8 *name) 47 48 { 48 - return memcpy(name, nd_label->name, NSLABEL_NAME_LEN); 49 + if (ndd->cxl) 50 + return memcpy(name, nd_label->cxl.name, NSLABEL_NAME_LEN); 51 + return memcpy(name, nd_label->efi.name, NSLABEL_NAME_LEN); 49 52 } 50 53 51 54 static inline u8 *nsl_set_name(struct nvdimm_drvdata *ndd, ··· 57 52 { 58 53 if (!name) 59 54 return NULL; 60 - return memcpy(nd_label->name, name, NSLABEL_NAME_LEN); 55 + if (ndd->cxl) 56 + return memcpy(nd_label->cxl.name, name, NSLABEL_NAME_LEN); 57 + return memcpy(nd_label->efi.name, name, NSLABEL_NAME_LEN); 61 58 } 62 59 63 60 static inline u32 nsl_get_slot(struct nvdimm_drvdata *ndd, 64 61 struct nd_namespace_label *nd_label) 65 62 { 66 - return __le32_to_cpu(nd_label->slot); 63 + if (ndd->cxl) 64 + return __le32_to_cpu(nd_label->cxl.slot); 65 + return __le32_to_cpu(nd_label->efi.slot); 67 66 } 68 67 69 68 static inline void nsl_set_slot(struct nvdimm_drvdata *ndd, 70 69 struct nd_namespace_label *nd_label, u32 slot) 71 70 { 72 - nd_label->slot = __cpu_to_le32(slot); 71 + if (ndd->cxl) 72 + nd_label->cxl.slot = __cpu_to_le32(slot); 73 + else 74 + nd_label->efi.slot = __cpu_to_le32(slot); 73 75 } 74 76 75 77 static inline u64 nsl_get_checksum(struct nvdimm_drvdata *ndd, 76 78 struct nd_namespace_label *nd_label) 77 79 { 78 - return __le64_to_cpu(nd_label->checksum); 80 + if (ndd->cxl) 81 + return __le64_to_cpu(nd_label->cxl.checksum); 82 + return __le64_to_cpu(nd_label->efi.checksum); 79 83 } 80 84 81 85 static inline void nsl_set_checksum(struct nvdimm_drvdata *ndd, 82 86 struct nd_namespace_label *nd_label, 83 87 u64 checksum) 84 88 { 85 - nd_label->checksum = __cpu_to_le64(checksum); 89 + if (ndd->cxl) 90 + nd_label->cxl.checksum = __cpu_to_le64(checksum); 91 + else 92 + nd_label->efi.checksum = __cpu_to_le64(checksum); 86 93 } 87 94 88 95 static inline u32 nsl_get_flags(struct nvdimm_drvdata *ndd, 89 96 struct nd_namespace_label *nd_label) 90 97 { 91 - return __le32_to_cpu(nd_label->flags); 98 + if (ndd->cxl) 99 + return __le32_to_cpu(nd_label->cxl.flags); 100 + return __le32_to_cpu(nd_label->efi.flags); 92 101 } 93 102 94 103 static inline void nsl_set_flags(struct nvdimm_drvdata *ndd, 95 104 struct nd_namespace_label *nd_label, u32 flags) 96 105 { 97 - nd_label->flags = __cpu_to_le32(flags); 106 + if (ndd->cxl) 107 + nd_label->cxl.flags = __cpu_to_le32(flags); 108 + else 109 + nd_label->efi.flags = __cpu_to_le32(flags); 98 110 } 99 111 100 112 static inline u64 nsl_get_dpa(struct nvdimm_drvdata *ndd, 101 113 struct nd_namespace_label *nd_label) 102 114 { 103 - return __le64_to_cpu(nd_label->dpa); 115 + if (ndd->cxl) 116 + return __le64_to_cpu(nd_label->cxl.dpa); 117 + return __le64_to_cpu(nd_label->efi.dpa); 104 118 } 105 119 106 120 static inline void nsl_set_dpa(struct nvdimm_drvdata *ndd, 107 121 struct nd_namespace_label *nd_label, u64 dpa) 108 122 { 109 - nd_label->dpa = __cpu_to_le64(dpa); 123 + if (ndd->cxl) 124 + nd_label->cxl.dpa = __cpu_to_le64(dpa); 125 + else 126 + nd_label->efi.dpa = __cpu_to_le64(dpa); 110 127 } 111 128 112 129 static inline u64 nsl_get_rawsize(struct nvdimm_drvdata *ndd, 113 130 struct nd_namespace_label *nd_label) 114 131 { 115 - return __le64_to_cpu(nd_label->rawsize); 132 + if (ndd->cxl) 133 + return __le64_to_cpu(nd_label->cxl.rawsize); 134 + return __le64_to_cpu(nd_label->efi.rawsize); 116 135 } 117 136 118 137 static inline void nsl_set_rawsize(struct nvdimm_drvdata *ndd, 119 138 struct nd_namespace_label *nd_label, 120 139 u64 rawsize) 121 140 { 122 - nd_label->rawsize = __cpu_to_le64(rawsize); 141 + if (ndd->cxl) 142 + nd_label->cxl.rawsize = __cpu_to_le64(rawsize); 143 + else 144 + nd_label->efi.rawsize = __cpu_to_le64(rawsize); 123 145 } 124 146 125 147 static inline u64 nsl_get_isetcookie(struct nvdimm_drvdata *ndd, 126 148 struct nd_namespace_label *nd_label) 127 149 { 128 - return __le64_to_cpu(nd_label->isetcookie); 150 + /* WARN future refactor attempts that break this assumption */ 151 + if (dev_WARN_ONCE(ndd->dev, ndd->cxl, 152 + "CXL labels do not use the isetcookie concept\n")) 153 + return 0; 154 + return __le64_to_cpu(nd_label->efi.isetcookie); 129 155 } 130 156 131 157 static inline void nsl_set_isetcookie(struct nvdimm_drvdata *ndd, 132 158 struct nd_namespace_label *nd_label, 133 159 u64 isetcookie) 134 160 { 135 - nd_label->isetcookie = __cpu_to_le64(isetcookie); 161 + if (!ndd->cxl) 162 + nd_label->efi.isetcookie = __cpu_to_le64(isetcookie); 136 163 } 137 164 138 165 static inline bool nsl_validate_isetcookie(struct nvdimm_drvdata *ndd, 139 166 struct nd_namespace_label *nd_label, 140 167 u64 cookie) 141 168 { 142 - return cookie == __le64_to_cpu(nd_label->isetcookie); 169 + /* 170 + * Let the EFI and CXL validation comingle, where fields that 171 + * don't matter to CXL always validate. 172 + */ 173 + if (ndd->cxl) 174 + return true; 175 + return cookie == __le64_to_cpu(nd_label->efi.isetcookie); 143 176 } 144 177 145 178 static inline u16 nsl_get_position(struct nvdimm_drvdata *ndd, 146 179 struct nd_namespace_label *nd_label) 147 180 { 148 - return __le16_to_cpu(nd_label->position); 181 + if (ndd->cxl) 182 + return __le16_to_cpu(nd_label->cxl.position); 183 + return __le16_to_cpu(nd_label->efi.position); 149 184 } 150 185 151 186 static inline void nsl_set_position(struct nvdimm_drvdata *ndd, 152 187 struct nd_namespace_label *nd_label, 153 188 u16 position) 154 189 { 155 - nd_label->position = __cpu_to_le16(position); 190 + if (ndd->cxl) 191 + nd_label->cxl.position = __cpu_to_le16(position); 192 + else 193 + nd_label->efi.position = __cpu_to_le16(position); 156 194 } 157 - 158 195 159 196 static inline u16 nsl_get_nlabel(struct nvdimm_drvdata *ndd, 160 197 struct nd_namespace_label *nd_label) 161 198 { 162 - return __le16_to_cpu(nd_label->nlabel); 199 + if (ndd->cxl) 200 + return 0; 201 + return __le16_to_cpu(nd_label->efi.nlabel); 163 202 } 164 203 165 204 static inline void nsl_set_nlabel(struct nvdimm_drvdata *ndd, 166 205 struct nd_namespace_label *nd_label, 167 206 u16 nlabel) 168 207 { 169 - nd_label->nlabel = __cpu_to_le16(nlabel); 208 + if (!ndd->cxl) 209 + nd_label->efi.nlabel = __cpu_to_le16(nlabel); 210 + } 211 + 212 + static inline u16 nsl_get_nrange(struct nvdimm_drvdata *ndd, 213 + struct nd_namespace_label *nd_label) 214 + { 215 + if (ndd->cxl) 216 + return __le16_to_cpu(nd_label->cxl.nrange); 217 + return 1; 218 + } 219 + 220 + static inline void nsl_set_nrange(struct nvdimm_drvdata *ndd, 221 + struct nd_namespace_label *nd_label, 222 + u16 nrange) 223 + { 224 + if (ndd->cxl) 225 + nd_label->cxl.nrange = __cpu_to_le16(nrange); 170 226 } 171 227 172 228 static inline u64 nsl_get_lbasize(struct nvdimm_drvdata *ndd, 173 229 struct nd_namespace_label *nd_label) 174 230 { 175 - return __le64_to_cpu(nd_label->lbasize); 231 + /* 232 + * Yes, for some reason the EFI labels convey a massive 64-bit 233 + * lbasize, that got fixed for CXL. 234 + */ 235 + if (ndd->cxl) 236 + return __le16_to_cpu(nd_label->cxl.lbasize); 237 + return __le64_to_cpu(nd_label->efi.lbasize); 176 238 } 177 239 178 240 static inline void nsl_set_lbasize(struct nvdimm_drvdata *ndd, 179 241 struct nd_namespace_label *nd_label, 180 242 u64 lbasize) 181 243 { 182 - nd_label->lbasize = __cpu_to_le64(lbasize); 244 + if (ndd->cxl) 245 + nd_label->cxl.lbasize = __cpu_to_le16(lbasize); 246 + else 247 + nd_label->efi.lbasize = __cpu_to_le64(lbasize); 248 + } 249 + 250 + static inline const uuid_t *nsl_get_uuid(struct nvdimm_drvdata *ndd, 251 + struct nd_namespace_label *nd_label, 252 + uuid_t *uuid) 253 + { 254 + if (ndd->cxl) 255 + import_uuid(uuid, nd_label->cxl.uuid); 256 + else 257 + import_uuid(uuid, nd_label->efi.uuid); 258 + return uuid; 259 + } 260 + 261 + static inline const uuid_t *nsl_set_uuid(struct nvdimm_drvdata *ndd, 262 + struct nd_namespace_label *nd_label, 263 + const uuid_t *uuid) 264 + { 265 + if (ndd->cxl) 266 + export_uuid(nd_label->cxl.uuid, uuid); 267 + else 268 + export_uuid(nd_label->efi.uuid, uuid); 269 + return uuid; 270 + } 271 + 272 + static inline bool nsl_uuid_equal(struct nvdimm_drvdata *ndd, 273 + struct nd_namespace_label *nd_label, 274 + const uuid_t *uuid) 275 + { 276 + uuid_t tmp; 277 + 278 + if (ndd->cxl) 279 + import_uuid(&tmp, nd_label->cxl.uuid); 280 + else 281 + import_uuid(&tmp, nd_label->efi.uuid); 282 + return uuid_equal(&tmp, uuid); 283 + } 284 + 285 + static inline const u8 *nsl_uuid_raw(struct nvdimm_drvdata *ndd, 286 + struct nd_namespace_label *nd_label) 287 + { 288 + if (ndd->cxl) 289 + return nd_label->cxl.uuid; 290 + return nd_label->efi.uuid; 183 291 } 184 292 185 293 bool nsl_validate_blk_isetcookie(struct nvdimm_drvdata *ndd, ··· 351 233 352 234 unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd); 353 235 354 - #define namespace_label_has(ndd, field) \ 355 - (offsetof(struct nd_namespace_label, field) \ 236 + #define efi_namespace_label_has(ndd, field) \ 237 + (!ndd->cxl && offsetof(struct nvdimm_efi_label, field) \ 356 238 < sizeof_namespace_label(ndd)) 357 239 358 240 #define nd_dbg_dpa(r, d, res, fmt, arg...) \ ··· 428 310 struct nd_mapping mapping[]; 429 311 }; 430 312 313 + static inline bool nsl_validate_nlabel(struct nd_region *nd_region, 314 + struct nvdimm_drvdata *ndd, 315 + struct nd_namespace_label *nd_label) 316 + { 317 + if (ndd->cxl) 318 + return true; 319 + return nsl_get_nlabel(ndd, nd_label) == nd_region->ndr_mappings; 320 + } 321 + 431 322 struct nd_blk_region { 432 323 int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev); 433 324 int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, ··· 462 335 struct btt *btt; 463 336 unsigned long lbasize; 464 337 u64 size; 465 - u8 *uuid; 338 + uuid_t *uuid; 466 339 int id; 467 340 int initial_offset; 468 341 u16 version_major; ··· 477 350 478 351 struct nd_pfn { 479 352 int id; 480 - u8 *uuid; 353 + uuid_t *uuid; 481 354 struct device dev; 482 355 unsigned long align; 483 356 unsigned long npfns; ··· 505 378 void nd_device_register(struct device *dev); 506 379 void nd_device_unregister(struct device *dev, enum nd_async_mode mode); 507 380 void nd_device_notify(struct device *dev, enum nvdimm_event event); 508 - int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf, 381 + int nd_uuid_store(struct device *dev, uuid_t **uuid_out, const char *buf, 509 382 size_t len); 510 383 ssize_t nd_size_select_show(unsigned long current_size, 511 384 const unsigned long *supported, char *buf); ··· 688 561 return false; 689 562 } 690 563 resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk); 691 - const u8 *nd_dev_to_uuid(struct device *dev); 564 + const uuid_t *nd_dev_to_uuid(struct device *dev); 692 565 bool pmem_should_map_pages(struct device *dev); 693 566 #endif /* __ND_H__ */
+1 -1
drivers/nvdimm/pfn_devs.c
··· 452 452 unsigned long align, start_pad; 453 453 struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb; 454 454 struct nd_namespace_common *ndns = nd_pfn->ndns; 455 - const u8 *parent_uuid = nd_dev_to_uuid(&ndns->dev); 455 + const uuid_t *parent_uuid = nd_dev_to_uuid(&ndns->dev); 456 456 457 457 if (!pfn_sb || !ndns) 458 458 return -ENODEV;
+32
drivers/pci/pci.c
··· 733 733 EXPORT_SYMBOL_GPL(pci_find_vsec_capability); 734 734 735 735 /** 736 + * pci_find_dvsec_capability - Find DVSEC for vendor 737 + * @dev: PCI device to query 738 + * @vendor: Vendor ID to match for the DVSEC 739 + * @dvsec: Designated Vendor-specific capability ID 740 + * 741 + * If DVSEC has Vendor ID @vendor and DVSEC ID @dvsec return the capability 742 + * offset in config space; otherwise return 0. 743 + */ 744 + u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec) 745 + { 746 + int pos; 747 + 748 + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DVSEC); 749 + if (!pos) 750 + return 0; 751 + 752 + while (pos) { 753 + u16 v, id; 754 + 755 + pci_read_config_word(dev, pos + PCI_DVSEC_HEADER1, &v); 756 + pci_read_config_word(dev, pos + PCI_DVSEC_HEADER2, &id); 757 + if (vendor == v && dvsec == id) 758 + return pos; 759 + 760 + pos = pci_find_next_ext_capability(dev, pos, PCI_EXT_CAP_ID_DVSEC); 761 + } 762 + 763 + return 0; 764 + } 765 + EXPORT_SYMBOL_GPL(pci_find_dvsec_capability); 766 + 767 + /** 736 768 * pci_find_parent_resource - return resource region of parent bus of given 737 769 * region 738 770 * @dev: PCI device structure contains resources to be searched
+2 -2
include/linux/nd.h
··· 88 88 struct nd_namespace_io nsio; 89 89 unsigned long lbasize; 90 90 char *alt_name; 91 - u8 *uuid; 91 + uuid_t *uuid; 92 92 int id; 93 93 }; 94 94 ··· 105 105 struct nd_namespace_blk { 106 106 struct nd_namespace_common common; 107 107 char *alt_name; 108 - u8 *uuid; 108 + uuid_t *uuid; 109 109 int id; 110 110 unsigned long lbasize; 111 111 resource_size_t size;
+1
include/linux/pci.h
··· 1132 1132 u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 pos, int cap); 1133 1133 struct pci_bus *pci_find_next_bus(const struct pci_bus *from); 1134 1134 u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap); 1135 + u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec); 1135 1136 1136 1137 u64 pci_get_dsn(struct pci_dev *dev); 1137 1138
+38
tools/testing/cxl/Kbuild
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + ldflags-y += --wrap=is_acpi_device_node 3 + ldflags-y += --wrap=acpi_get_table 4 + ldflags-y += --wrap=acpi_put_table 5 + ldflags-y += --wrap=acpi_evaluate_integer 6 + ldflags-y += --wrap=acpi_pci_find_root 7 + ldflags-y += --wrap=pci_walk_bus 8 + ldflags-y += --wrap=nvdimm_bus_register 9 + 10 + DRIVERS := ../../../drivers 11 + CXL_SRC := $(DRIVERS)/cxl 12 + CXL_CORE_SRC := $(DRIVERS)/cxl/core 13 + ccflags-y := -I$(srctree)/drivers/cxl/ 14 + ccflags-y += -D__mock=__weak 15 + 16 + obj-m += cxl_acpi.o 17 + 18 + cxl_acpi-y := $(CXL_SRC)/acpi.o 19 + cxl_acpi-y += mock_acpi.o 20 + cxl_acpi-y += config_check.o 21 + 22 + obj-m += cxl_pmem.o 23 + 24 + cxl_pmem-y := $(CXL_SRC)/pmem.o 25 + cxl_pmem-y += config_check.o 26 + 27 + obj-m += cxl_core.o 28 + 29 + cxl_core-y := $(CXL_CORE_SRC)/bus.o 30 + cxl_core-y += $(CXL_CORE_SRC)/pmem.o 31 + cxl_core-y += $(CXL_CORE_SRC)/regs.o 32 + cxl_core-y += $(CXL_CORE_SRC)/memdev.o 33 + cxl_core-y += $(CXL_CORE_SRC)/mbox.o 34 + cxl_core-y += config_check.o 35 + 36 + cxl_core-y += mock_pmem.o 37 + 38 + obj-m += test/
+13
tools/testing/cxl/config_check.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bug.h> 3 + 4 + void check(void) 5 + { 6 + /* 7 + * These kconfig symbols must be set to "m" for cxl_test to load 8 + * and operate. 9 + */ 10 + BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_BUS)); 11 + BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_ACPI)); 12 + BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_PMEM)); 13 + }
+109
tools/testing/cxl/mock_acpi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2021 Intel Corporation. All rights reserved. */ 3 + 4 + #include <linux/platform_device.h> 5 + #include <linux/device.h> 6 + #include <linux/acpi.h> 7 + #include <linux/pci.h> 8 + #include <cxl.h> 9 + #include "test/mock.h" 10 + 11 + struct acpi_device *to_cxl_host_bridge(struct device *host, struct device *dev) 12 + { 13 + int index; 14 + struct acpi_device *adev, *found = NULL; 15 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 16 + 17 + if (ops && ops->is_mock_bridge(dev)) { 18 + found = ACPI_COMPANION(dev); 19 + goto out; 20 + } 21 + 22 + if (dev->bus == &platform_bus_type) 23 + goto out; 24 + 25 + adev = to_acpi_device(dev); 26 + if (!acpi_pci_find_root(adev->handle)) 27 + goto out; 28 + 29 + if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0) { 30 + found = adev; 31 + dev_dbg(host, "found host bridge %s\n", dev_name(&adev->dev)); 32 + } 33 + out: 34 + put_cxl_mock_ops(index); 35 + return found; 36 + } 37 + 38 + static int match_add_root_port(struct pci_dev *pdev, void *data) 39 + { 40 + struct cxl_walk_context *ctx = data; 41 + struct pci_bus *root_bus = ctx->root; 42 + struct cxl_port *port = ctx->port; 43 + int type = pci_pcie_type(pdev); 44 + struct device *dev = ctx->dev; 45 + u32 lnkcap, port_num; 46 + int rc; 47 + 48 + if (pdev->bus != root_bus) 49 + return 0; 50 + if (!pci_is_pcie(pdev)) 51 + return 0; 52 + if (type != PCI_EXP_TYPE_ROOT_PORT) 53 + return 0; 54 + if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP, 55 + &lnkcap) != PCIBIOS_SUCCESSFUL) 56 + return 0; 57 + 58 + /* TODO walk DVSEC to find component register base */ 59 + port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap); 60 + rc = cxl_add_dport(port, &pdev->dev, port_num, CXL_RESOURCE_NONE); 61 + if (rc) { 62 + dev_err(dev, "failed to add dport: %s (%d)\n", 63 + dev_name(&pdev->dev), rc); 64 + ctx->error = rc; 65 + return rc; 66 + } 67 + ctx->count++; 68 + 69 + dev_dbg(dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev)); 70 + 71 + return 0; 72 + } 73 + 74 + static int mock_add_root_port(struct platform_device *pdev, void *data) 75 + { 76 + struct cxl_walk_context *ctx = data; 77 + struct cxl_port *port = ctx->port; 78 + struct device *dev = ctx->dev; 79 + int rc; 80 + 81 + rc = cxl_add_dport(port, &pdev->dev, pdev->id, CXL_RESOURCE_NONE); 82 + if (rc) { 83 + dev_err(dev, "failed to add dport: %s (%d)\n", 84 + dev_name(&pdev->dev), rc); 85 + ctx->error = rc; 86 + return rc; 87 + } 88 + ctx->count++; 89 + 90 + dev_dbg(dev, "add dport%d: %s\n", pdev->id, dev_name(&pdev->dev)); 91 + 92 + return 0; 93 + } 94 + 95 + int match_add_root_ports(struct pci_dev *dev, void *data) 96 + { 97 + int index, rc; 98 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 99 + struct platform_device *pdev = (struct platform_device *) dev; 100 + 101 + if (ops && ops->is_mock_port(pdev)) 102 + rc = mock_add_root_port(pdev, data); 103 + else 104 + rc = match_add_root_port(dev, data); 105 + 106 + put_cxl_mock_ops(index); 107 + 108 + return rc; 109 + }
+24
tools/testing/cxl/mock_pmem.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2021 Intel Corporation. All rights reserved. */ 3 + #include <cxl.h> 4 + #include "test/mock.h" 5 + #include <core/core.h> 6 + 7 + int match_nvdimm_bridge(struct device *dev, const void *data) 8 + { 9 + int index, rc = 0; 10 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 11 + const struct cxl_nvdimm *cxl_nvd = data; 12 + 13 + if (ops) { 14 + if (dev->type == &cxl_nvdimm_bridge_type && 15 + (ops->is_mock_dev(dev->parent->parent) == 16 + ops->is_mock_dev(cxl_nvd->dev.parent->parent))) 17 + rc = 1; 18 + } else 19 + rc = dev->type == &cxl_nvdimm_bridge_type; 20 + 21 + put_cxl_mock_ops(index); 22 + 23 + return rc; 24 + }
+10
tools/testing/cxl/test/Kbuild
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + ccflags-y := -I$(srctree)/drivers/cxl/ 3 + 4 + obj-m += cxl_test.o 5 + obj-m += cxl_mock.o 6 + obj-m += cxl_mock_mem.o 7 + 8 + cxl_test-y := cxl.o 9 + cxl_mock-y := mock.o 10 + cxl_mock_mem-y := mem.o
+576
tools/testing/cxl/test/cxl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // Copyright(c) 2021 Intel Corporation. All rights reserved. 3 + 4 + #include <linux/platform_device.h> 5 + #include <linux/genalloc.h> 6 + #include <linux/module.h> 7 + #include <linux/mutex.h> 8 + #include <linux/acpi.h> 9 + #include <linux/pci.h> 10 + #include <linux/mm.h> 11 + #include "mock.h" 12 + 13 + #define NR_CXL_HOST_BRIDGES 4 14 + #define NR_CXL_ROOT_PORTS 2 15 + 16 + static struct platform_device *cxl_acpi; 17 + static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES]; 18 + static struct platform_device 19 + *cxl_root_port[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS]; 20 + struct platform_device *cxl_mem[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS]; 21 + 22 + static struct acpi_device acpi0017_mock; 23 + static struct acpi_device host_bridge[NR_CXL_HOST_BRIDGES] = { 24 + [0] = { 25 + .handle = &host_bridge[0], 26 + }, 27 + [1] = { 28 + .handle = &host_bridge[1], 29 + }, 30 + [2] = { 31 + .handle = &host_bridge[2], 32 + }, 33 + [3] = { 34 + .handle = &host_bridge[3], 35 + }, 36 + }; 37 + 38 + static bool is_mock_dev(struct device *dev) 39 + { 40 + int i; 41 + 42 + for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) 43 + if (dev == &cxl_mem[i]->dev) 44 + return true; 45 + if (dev == &cxl_acpi->dev) 46 + return true; 47 + return false; 48 + } 49 + 50 + static bool is_mock_adev(struct acpi_device *adev) 51 + { 52 + int i; 53 + 54 + if (adev == &acpi0017_mock) 55 + return true; 56 + 57 + for (i = 0; i < ARRAY_SIZE(host_bridge); i++) 58 + if (adev == &host_bridge[i]) 59 + return true; 60 + 61 + return false; 62 + } 63 + 64 + static struct { 65 + struct acpi_table_cedt cedt; 66 + struct acpi_cedt_chbs chbs[NR_CXL_HOST_BRIDGES]; 67 + struct { 68 + struct acpi_cedt_cfmws cfmws; 69 + u32 target[1]; 70 + } cfmws0; 71 + struct { 72 + struct acpi_cedt_cfmws cfmws; 73 + u32 target[4]; 74 + } cfmws1; 75 + struct { 76 + struct acpi_cedt_cfmws cfmws; 77 + u32 target[1]; 78 + } cfmws2; 79 + struct { 80 + struct acpi_cedt_cfmws cfmws; 81 + u32 target[4]; 82 + } cfmws3; 83 + } __packed mock_cedt = { 84 + .cedt = { 85 + .header = { 86 + .signature = "CEDT", 87 + .length = sizeof(mock_cedt), 88 + .revision = 1, 89 + }, 90 + }, 91 + .chbs[0] = { 92 + .header = { 93 + .type = ACPI_CEDT_TYPE_CHBS, 94 + .length = sizeof(mock_cedt.chbs[0]), 95 + }, 96 + .uid = 0, 97 + .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20, 98 + }, 99 + .chbs[1] = { 100 + .header = { 101 + .type = ACPI_CEDT_TYPE_CHBS, 102 + .length = sizeof(mock_cedt.chbs[0]), 103 + }, 104 + .uid = 1, 105 + .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20, 106 + }, 107 + .chbs[2] = { 108 + .header = { 109 + .type = ACPI_CEDT_TYPE_CHBS, 110 + .length = sizeof(mock_cedt.chbs[0]), 111 + }, 112 + .uid = 2, 113 + .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20, 114 + }, 115 + .chbs[3] = { 116 + .header = { 117 + .type = ACPI_CEDT_TYPE_CHBS, 118 + .length = sizeof(mock_cedt.chbs[0]), 119 + }, 120 + .uid = 3, 121 + .cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20, 122 + }, 123 + .cfmws0 = { 124 + .cfmws = { 125 + .header = { 126 + .type = ACPI_CEDT_TYPE_CFMWS, 127 + .length = sizeof(mock_cedt.cfmws0), 128 + }, 129 + .interleave_ways = 0, 130 + .granularity = 4, 131 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 132 + ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, 133 + .qtg_id = 0, 134 + .window_size = SZ_256M, 135 + }, 136 + .target = { 0 }, 137 + }, 138 + .cfmws1 = { 139 + .cfmws = { 140 + .header = { 141 + .type = ACPI_CEDT_TYPE_CFMWS, 142 + .length = sizeof(mock_cedt.cfmws1), 143 + }, 144 + .interleave_ways = 2, 145 + .granularity = 4, 146 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 147 + ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, 148 + .qtg_id = 1, 149 + .window_size = SZ_256M * 4, 150 + }, 151 + .target = { 0, 1, 2, 3 }, 152 + }, 153 + .cfmws2 = { 154 + .cfmws = { 155 + .header = { 156 + .type = ACPI_CEDT_TYPE_CFMWS, 157 + .length = sizeof(mock_cedt.cfmws2), 158 + }, 159 + .interleave_ways = 0, 160 + .granularity = 4, 161 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 162 + ACPI_CEDT_CFMWS_RESTRICT_PMEM, 163 + .qtg_id = 2, 164 + .window_size = SZ_256M, 165 + }, 166 + .target = { 0 }, 167 + }, 168 + .cfmws3 = { 169 + .cfmws = { 170 + .header = { 171 + .type = ACPI_CEDT_TYPE_CFMWS, 172 + .length = sizeof(mock_cedt.cfmws3), 173 + }, 174 + .interleave_ways = 2, 175 + .granularity = 4, 176 + .restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | 177 + ACPI_CEDT_CFMWS_RESTRICT_PMEM, 178 + .qtg_id = 3, 179 + .window_size = SZ_256M * 4, 180 + }, 181 + .target = { 0, 1, 2, 3 }, 182 + }, 183 + }; 184 + 185 + struct cxl_mock_res { 186 + struct list_head list; 187 + struct range range; 188 + }; 189 + 190 + static LIST_HEAD(mock_res); 191 + static DEFINE_MUTEX(mock_res_lock); 192 + static struct gen_pool *cxl_mock_pool; 193 + 194 + static void depopulate_all_mock_resources(void) 195 + { 196 + struct cxl_mock_res *res, *_res; 197 + 198 + mutex_lock(&mock_res_lock); 199 + list_for_each_entry_safe(res, _res, &mock_res, list) { 200 + gen_pool_free(cxl_mock_pool, res->range.start, 201 + range_len(&res->range)); 202 + list_del(&res->list); 203 + kfree(res); 204 + } 205 + mutex_unlock(&mock_res_lock); 206 + } 207 + 208 + static struct cxl_mock_res *alloc_mock_res(resource_size_t size) 209 + { 210 + struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); 211 + struct genpool_data_align data = { 212 + .align = SZ_256M, 213 + }; 214 + unsigned long phys; 215 + 216 + INIT_LIST_HEAD(&res->list); 217 + phys = gen_pool_alloc_algo(cxl_mock_pool, size, 218 + gen_pool_first_fit_align, &data); 219 + if (!phys) 220 + return NULL; 221 + 222 + res->range = (struct range) { 223 + .start = phys, 224 + .end = phys + size - 1, 225 + }; 226 + mutex_lock(&mock_res_lock); 227 + list_add(&res->list, &mock_res); 228 + mutex_unlock(&mock_res_lock); 229 + 230 + return res; 231 + } 232 + 233 + static int populate_cedt(void) 234 + { 235 + struct acpi_cedt_cfmws *cfmws[4] = { 236 + [0] = &mock_cedt.cfmws0.cfmws, 237 + [1] = &mock_cedt.cfmws1.cfmws, 238 + [2] = &mock_cedt.cfmws2.cfmws, 239 + [3] = &mock_cedt.cfmws3.cfmws, 240 + }; 241 + struct cxl_mock_res *res; 242 + int i; 243 + 244 + for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) { 245 + struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i]; 246 + resource_size_t size; 247 + 248 + if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL20) 249 + size = ACPI_CEDT_CHBS_LENGTH_CXL20; 250 + else 251 + size = ACPI_CEDT_CHBS_LENGTH_CXL11; 252 + 253 + res = alloc_mock_res(size); 254 + if (!res) 255 + return -ENOMEM; 256 + chbs->base = res->range.start; 257 + chbs->length = size; 258 + } 259 + 260 + for (i = 0; i < ARRAY_SIZE(cfmws); i++) { 261 + struct acpi_cedt_cfmws *window = cfmws[i]; 262 + 263 + res = alloc_mock_res(window->window_size); 264 + if (!res) 265 + return -ENOMEM; 266 + window->base_hpa = res->range.start; 267 + } 268 + 269 + return 0; 270 + } 271 + 272 + static acpi_status mock_acpi_get_table(char *signature, u32 instance, 273 + struct acpi_table_header **out_table) 274 + { 275 + if (instance < U32_MAX || strcmp(signature, ACPI_SIG_CEDT) != 0) 276 + return acpi_get_table(signature, instance, out_table); 277 + 278 + *out_table = (struct acpi_table_header *) &mock_cedt; 279 + return AE_OK; 280 + } 281 + 282 + static void mock_acpi_put_table(struct acpi_table_header *table) 283 + { 284 + if (table == (struct acpi_table_header *) &mock_cedt) 285 + return; 286 + acpi_put_table(table); 287 + } 288 + 289 + static bool is_mock_bridge(struct device *dev) 290 + { 291 + int i; 292 + 293 + for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) 294 + if (dev == &cxl_host_bridge[i]->dev) 295 + return true; 296 + 297 + return false; 298 + } 299 + 300 + static int host_bridge_index(struct acpi_device *adev) 301 + { 302 + return adev - host_bridge; 303 + } 304 + 305 + static struct acpi_device *find_host_bridge(acpi_handle handle) 306 + { 307 + int i; 308 + 309 + for (i = 0; i < ARRAY_SIZE(host_bridge); i++) 310 + if (handle == host_bridge[i].handle) 311 + return &host_bridge[i]; 312 + return NULL; 313 + } 314 + 315 + static acpi_status 316 + mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname, 317 + struct acpi_object_list *arguments, 318 + unsigned long long *data) 319 + { 320 + struct acpi_device *adev = find_host_bridge(handle); 321 + 322 + if (!adev || strcmp(pathname, METHOD_NAME__UID) != 0) 323 + return acpi_evaluate_integer(handle, pathname, arguments, data); 324 + 325 + *data = host_bridge_index(adev); 326 + return AE_OK; 327 + } 328 + 329 + static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES]; 330 + static struct acpi_pci_root mock_pci_root[NR_CXL_HOST_BRIDGES] = { 331 + [0] = { 332 + .bus = &mock_pci_bus[0], 333 + }, 334 + [1] = { 335 + .bus = &mock_pci_bus[1], 336 + }, 337 + [2] = { 338 + .bus = &mock_pci_bus[2], 339 + }, 340 + [3] = { 341 + .bus = &mock_pci_bus[3], 342 + }, 343 + }; 344 + 345 + static struct platform_device *mock_cxl_root_port(struct pci_bus *bus, int index) 346 + { 347 + int i; 348 + 349 + for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++) 350 + if (bus == &mock_pci_bus[i]) 351 + return cxl_root_port[index + i * NR_CXL_ROOT_PORTS]; 352 + return NULL; 353 + } 354 + 355 + static bool is_mock_port(struct platform_device *pdev) 356 + { 357 + int i; 358 + 359 + for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) 360 + if (pdev == cxl_root_port[i]) 361 + return true; 362 + return false; 363 + } 364 + 365 + static bool is_mock_bus(struct pci_bus *bus) 366 + { 367 + int i; 368 + 369 + for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++) 370 + if (bus == &mock_pci_bus[i]) 371 + return true; 372 + return false; 373 + } 374 + 375 + static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle) 376 + { 377 + struct acpi_device *adev = find_host_bridge(handle); 378 + 379 + if (!adev) 380 + return acpi_pci_find_root(handle); 381 + return &mock_pci_root[host_bridge_index(adev)]; 382 + } 383 + 384 + static struct cxl_mock_ops cxl_mock_ops = { 385 + .is_mock_adev = is_mock_adev, 386 + .is_mock_bridge = is_mock_bridge, 387 + .is_mock_bus = is_mock_bus, 388 + .is_mock_port = is_mock_port, 389 + .is_mock_dev = is_mock_dev, 390 + .mock_port = mock_cxl_root_port, 391 + .acpi_get_table = mock_acpi_get_table, 392 + .acpi_put_table = mock_acpi_put_table, 393 + .acpi_evaluate_integer = mock_acpi_evaluate_integer, 394 + .acpi_pci_find_root = mock_acpi_pci_find_root, 395 + .list = LIST_HEAD_INIT(cxl_mock_ops.list), 396 + }; 397 + 398 + static void mock_companion(struct acpi_device *adev, struct device *dev) 399 + { 400 + device_initialize(&adev->dev); 401 + fwnode_init(&adev->fwnode, NULL); 402 + dev->fwnode = &adev->fwnode; 403 + adev->fwnode.dev = dev; 404 + } 405 + 406 + #ifndef SZ_64G 407 + #define SZ_64G (SZ_32G * 2) 408 + #endif 409 + 410 + #ifndef SZ_512G 411 + #define SZ_512G (SZ_64G * 8) 412 + #endif 413 + 414 + static struct platform_device *alloc_memdev(int id) 415 + { 416 + struct resource res[] = { 417 + [0] = { 418 + .flags = IORESOURCE_MEM, 419 + }, 420 + [1] = { 421 + .flags = IORESOURCE_MEM, 422 + .desc = IORES_DESC_PERSISTENT_MEMORY, 423 + }, 424 + }; 425 + struct platform_device *pdev; 426 + int i, rc; 427 + 428 + for (i = 0; i < ARRAY_SIZE(res); i++) { 429 + struct cxl_mock_res *r = alloc_mock_res(SZ_256M); 430 + 431 + if (!r) 432 + return NULL; 433 + res[i].start = r->range.start; 434 + res[i].end = r->range.end; 435 + } 436 + 437 + pdev = platform_device_alloc("cxl_mem", id); 438 + if (!pdev) 439 + return NULL; 440 + 441 + rc = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); 442 + if (rc) 443 + goto err; 444 + 445 + return pdev; 446 + 447 + err: 448 + platform_device_put(pdev); 449 + return NULL; 450 + } 451 + 452 + static __init int cxl_test_init(void) 453 + { 454 + int rc, i; 455 + 456 + register_cxl_mock_ops(&cxl_mock_ops); 457 + 458 + cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE); 459 + if (!cxl_mock_pool) { 460 + rc = -ENOMEM; 461 + goto err_gen_pool_create; 462 + } 463 + 464 + rc = gen_pool_add(cxl_mock_pool, SZ_512G, SZ_64G, NUMA_NO_NODE); 465 + if (rc) 466 + goto err_gen_pool_add; 467 + 468 + rc = populate_cedt(); 469 + if (rc) 470 + goto err_populate; 471 + 472 + for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) { 473 + struct acpi_device *adev = &host_bridge[i]; 474 + struct platform_device *pdev; 475 + 476 + pdev = platform_device_alloc("cxl_host_bridge", i); 477 + if (!pdev) 478 + goto err_bridge; 479 + 480 + mock_companion(adev, &pdev->dev); 481 + rc = platform_device_add(pdev); 482 + if (rc) { 483 + platform_device_put(pdev); 484 + goto err_bridge; 485 + } 486 + cxl_host_bridge[i] = pdev; 487 + } 488 + 489 + for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) { 490 + struct platform_device *bridge = 491 + cxl_host_bridge[i / NR_CXL_ROOT_PORTS]; 492 + struct platform_device *pdev; 493 + 494 + pdev = platform_device_alloc("cxl_root_port", i); 495 + if (!pdev) 496 + goto err_port; 497 + pdev->dev.parent = &bridge->dev; 498 + 499 + rc = platform_device_add(pdev); 500 + if (rc) { 501 + platform_device_put(pdev); 502 + goto err_port; 503 + } 504 + cxl_root_port[i] = pdev; 505 + } 506 + 507 + BUILD_BUG_ON(ARRAY_SIZE(cxl_mem) != ARRAY_SIZE(cxl_root_port)); 508 + for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) { 509 + struct platform_device *port = cxl_root_port[i]; 510 + struct platform_device *pdev; 511 + 512 + pdev = alloc_memdev(i); 513 + if (!pdev) 514 + goto err_mem; 515 + pdev->dev.parent = &port->dev; 516 + 517 + rc = platform_device_add(pdev); 518 + if (rc) { 519 + platform_device_put(pdev); 520 + goto err_mem; 521 + } 522 + cxl_mem[i] = pdev; 523 + } 524 + 525 + cxl_acpi = platform_device_alloc("cxl_acpi", 0); 526 + if (!cxl_acpi) 527 + goto err_mem; 528 + 529 + mock_companion(&acpi0017_mock, &cxl_acpi->dev); 530 + acpi0017_mock.dev.bus = &platform_bus_type; 531 + 532 + rc = platform_device_add(cxl_acpi); 533 + if (rc) 534 + goto err_add; 535 + 536 + return 0; 537 + 538 + err_add: 539 + platform_device_put(cxl_acpi); 540 + err_mem: 541 + for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) 542 + platform_device_unregister(cxl_mem[i]); 543 + err_port: 544 + for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) 545 + platform_device_unregister(cxl_root_port[i]); 546 + err_bridge: 547 + for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) 548 + platform_device_unregister(cxl_host_bridge[i]); 549 + err_populate: 550 + depopulate_all_mock_resources(); 551 + err_gen_pool_add: 552 + gen_pool_destroy(cxl_mock_pool); 553 + err_gen_pool_create: 554 + unregister_cxl_mock_ops(&cxl_mock_ops); 555 + return rc; 556 + } 557 + 558 + static __exit void cxl_test_exit(void) 559 + { 560 + int i; 561 + 562 + platform_device_unregister(cxl_acpi); 563 + for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) 564 + platform_device_unregister(cxl_mem[i]); 565 + for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--) 566 + platform_device_unregister(cxl_root_port[i]); 567 + for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--) 568 + platform_device_unregister(cxl_host_bridge[i]); 569 + depopulate_all_mock_resources(); 570 + gen_pool_destroy(cxl_mock_pool); 571 + unregister_cxl_mock_ops(&cxl_mock_ops); 572 + } 573 + 574 + module_init(cxl_test_init); 575 + module_exit(cxl_test_exit); 576 + MODULE_LICENSE("GPL v2");
+256
tools/testing/cxl/test/mem.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // Copyright(c) 2021 Intel Corporation. All rights reserved. 3 + 4 + #include <linux/platform_device.h> 5 + #include <linux/mod_devicetable.h> 6 + #include <linux/module.h> 7 + #include <linux/sizes.h> 8 + #include <linux/bits.h> 9 + #include <cxlmem.h> 10 + 11 + #define LSA_SIZE SZ_128K 12 + #define EFFECT(x) (1U << x) 13 + 14 + static struct cxl_cel_entry mock_cel[] = { 15 + { 16 + .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS), 17 + .effect = cpu_to_le16(0), 18 + }, 19 + { 20 + .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY), 21 + .effect = cpu_to_le16(0), 22 + }, 23 + { 24 + .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA), 25 + .effect = cpu_to_le16(0), 26 + }, 27 + { 28 + .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA), 29 + .effect = cpu_to_le16(EFFECT(1) | EFFECT(2)), 30 + }, 31 + }; 32 + 33 + static struct { 34 + struct cxl_mbox_get_supported_logs gsl; 35 + struct cxl_gsl_entry entry; 36 + } mock_gsl_payload = { 37 + .gsl = { 38 + .entries = cpu_to_le16(1), 39 + }, 40 + .entry = { 41 + .uuid = DEFINE_CXL_CEL_UUID, 42 + .size = cpu_to_le32(sizeof(mock_cel)), 43 + }, 44 + }; 45 + 46 + static int mock_gsl(struct cxl_mbox_cmd *cmd) 47 + { 48 + if (cmd->size_out < sizeof(mock_gsl_payload)) 49 + return -EINVAL; 50 + 51 + memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload)); 52 + cmd->size_out = sizeof(mock_gsl_payload); 53 + 54 + return 0; 55 + } 56 + 57 + static int mock_get_log(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 58 + { 59 + struct cxl_mbox_get_log *gl = cmd->payload_in; 60 + u32 offset = le32_to_cpu(gl->offset); 61 + u32 length = le32_to_cpu(gl->length); 62 + uuid_t uuid = DEFINE_CXL_CEL_UUID; 63 + void *data = &mock_cel; 64 + 65 + if (cmd->size_in < sizeof(*gl)) 66 + return -EINVAL; 67 + if (length > cxlm->payload_size) 68 + return -EINVAL; 69 + if (offset + length > sizeof(mock_cel)) 70 + return -EINVAL; 71 + if (!uuid_equal(&gl->uuid, &uuid)) 72 + return -EINVAL; 73 + if (length > cmd->size_out) 74 + return -EINVAL; 75 + 76 + memcpy(cmd->payload_out, data + offset, length); 77 + 78 + return 0; 79 + } 80 + 81 + static int mock_id(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 82 + { 83 + struct platform_device *pdev = to_platform_device(cxlm->dev); 84 + struct cxl_mbox_identify id = { 85 + .fw_revision = { "mock fw v1 " }, 86 + .lsa_size = cpu_to_le32(LSA_SIZE), 87 + /* FIXME: Add partition support */ 88 + .partition_align = cpu_to_le64(0), 89 + }; 90 + u64 capacity = 0; 91 + int i; 92 + 93 + if (cmd->size_out < sizeof(id)) 94 + return -EINVAL; 95 + 96 + for (i = 0; i < 2; i++) { 97 + struct resource *res; 98 + 99 + res = platform_get_resource(pdev, IORESOURCE_MEM, i); 100 + if (!res) 101 + break; 102 + 103 + capacity += resource_size(res) / CXL_CAPACITY_MULTIPLIER; 104 + 105 + if (le64_to_cpu(id.partition_align)) 106 + continue; 107 + 108 + if (res->desc == IORES_DESC_PERSISTENT_MEMORY) 109 + id.persistent_capacity = cpu_to_le64( 110 + resource_size(res) / CXL_CAPACITY_MULTIPLIER); 111 + else 112 + id.volatile_capacity = cpu_to_le64( 113 + resource_size(res) / CXL_CAPACITY_MULTIPLIER); 114 + } 115 + 116 + id.total_capacity = cpu_to_le64(capacity); 117 + 118 + memcpy(cmd->payload_out, &id, sizeof(id)); 119 + 120 + return 0; 121 + } 122 + 123 + static int mock_get_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 124 + { 125 + struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in; 126 + void *lsa = dev_get_drvdata(cxlm->dev); 127 + u32 offset, length; 128 + 129 + if (sizeof(*get_lsa) > cmd->size_in) 130 + return -EINVAL; 131 + offset = le32_to_cpu(get_lsa->offset); 132 + length = le32_to_cpu(get_lsa->length); 133 + if (offset + length > LSA_SIZE) 134 + return -EINVAL; 135 + if (length > cmd->size_out) 136 + return -EINVAL; 137 + 138 + memcpy(cmd->payload_out, lsa + offset, length); 139 + return 0; 140 + } 141 + 142 + static int mock_set_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 143 + { 144 + struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in; 145 + void *lsa = dev_get_drvdata(cxlm->dev); 146 + u32 offset, length; 147 + 148 + if (sizeof(*set_lsa) > cmd->size_in) 149 + return -EINVAL; 150 + offset = le32_to_cpu(set_lsa->offset); 151 + length = cmd->size_in - sizeof(*set_lsa); 152 + if (offset + length > LSA_SIZE) 153 + return -EINVAL; 154 + 155 + memcpy(lsa + offset, &set_lsa->data[0], length); 156 + return 0; 157 + } 158 + 159 + static int cxl_mock_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 160 + { 161 + struct device *dev = cxlm->dev; 162 + int rc = -EIO; 163 + 164 + switch (cmd->opcode) { 165 + case CXL_MBOX_OP_GET_SUPPORTED_LOGS: 166 + rc = mock_gsl(cmd); 167 + break; 168 + case CXL_MBOX_OP_GET_LOG: 169 + rc = mock_get_log(cxlm, cmd); 170 + break; 171 + case CXL_MBOX_OP_IDENTIFY: 172 + rc = mock_id(cxlm, cmd); 173 + break; 174 + case CXL_MBOX_OP_GET_LSA: 175 + rc = mock_get_lsa(cxlm, cmd); 176 + break; 177 + case CXL_MBOX_OP_SET_LSA: 178 + rc = mock_set_lsa(cxlm, cmd); 179 + break; 180 + default: 181 + break; 182 + } 183 + 184 + dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode, 185 + cmd->size_in, cmd->size_out, rc); 186 + 187 + return rc; 188 + } 189 + 190 + static void label_area_release(void *lsa) 191 + { 192 + vfree(lsa); 193 + } 194 + 195 + static int cxl_mock_mem_probe(struct platform_device *pdev) 196 + { 197 + struct device *dev = &pdev->dev; 198 + struct cxl_memdev *cxlmd; 199 + struct cxl_mem *cxlm; 200 + void *lsa; 201 + int rc; 202 + 203 + lsa = vmalloc(LSA_SIZE); 204 + if (!lsa) 205 + return -ENOMEM; 206 + rc = devm_add_action_or_reset(dev, label_area_release, lsa); 207 + if (rc) 208 + return rc; 209 + dev_set_drvdata(dev, lsa); 210 + 211 + cxlm = cxl_mem_create(dev); 212 + if (IS_ERR(cxlm)) 213 + return PTR_ERR(cxlm); 214 + 215 + cxlm->mbox_send = cxl_mock_mbox_send; 216 + cxlm->payload_size = SZ_4K; 217 + 218 + rc = cxl_mem_enumerate_cmds(cxlm); 219 + if (rc) 220 + return rc; 221 + 222 + rc = cxl_mem_identify(cxlm); 223 + if (rc) 224 + return rc; 225 + 226 + rc = cxl_mem_create_range_info(cxlm); 227 + if (rc) 228 + return rc; 229 + 230 + cxlmd = devm_cxl_add_memdev(cxlm); 231 + if (IS_ERR(cxlmd)) 232 + return PTR_ERR(cxlmd); 233 + 234 + if (range_len(&cxlm->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM)) 235 + rc = devm_cxl_add_nvdimm(dev, cxlmd); 236 + 237 + return 0; 238 + } 239 + 240 + static const struct platform_device_id cxl_mock_mem_ids[] = { 241 + { .name = "cxl_mem", }, 242 + { }, 243 + }; 244 + MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids); 245 + 246 + static struct platform_driver cxl_mock_mem_driver = { 247 + .probe = cxl_mock_mem_probe, 248 + .id_table = cxl_mock_mem_ids, 249 + .driver = { 250 + .name = KBUILD_MODNAME, 251 + }, 252 + }; 253 + 254 + module_platform_driver(cxl_mock_mem_driver); 255 + MODULE_LICENSE("GPL v2"); 256 + MODULE_IMPORT_NS(CXL);
+171
tools/testing/cxl/test/mock.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + //Copyright(c) 2021 Intel Corporation. All rights reserved. 3 + 4 + #include <linux/libnvdimm.h> 5 + #include <linux/rculist.h> 6 + #include <linux/device.h> 7 + #include <linux/export.h> 8 + #include <linux/acpi.h> 9 + #include <linux/pci.h> 10 + #include "mock.h" 11 + 12 + static LIST_HEAD(mock); 13 + 14 + void register_cxl_mock_ops(struct cxl_mock_ops *ops) 15 + { 16 + list_add_rcu(&ops->list, &mock); 17 + } 18 + EXPORT_SYMBOL_GPL(register_cxl_mock_ops); 19 + 20 + static DEFINE_SRCU(cxl_mock_srcu); 21 + 22 + void unregister_cxl_mock_ops(struct cxl_mock_ops *ops) 23 + { 24 + list_del_rcu(&ops->list); 25 + synchronize_srcu(&cxl_mock_srcu); 26 + } 27 + EXPORT_SYMBOL_GPL(unregister_cxl_mock_ops); 28 + 29 + struct cxl_mock_ops *get_cxl_mock_ops(int *index) 30 + { 31 + *index = srcu_read_lock(&cxl_mock_srcu); 32 + return list_first_or_null_rcu(&mock, struct cxl_mock_ops, list); 33 + } 34 + EXPORT_SYMBOL_GPL(get_cxl_mock_ops); 35 + 36 + void put_cxl_mock_ops(int index) 37 + { 38 + srcu_read_unlock(&cxl_mock_srcu, index); 39 + } 40 + EXPORT_SYMBOL_GPL(put_cxl_mock_ops); 41 + 42 + bool __wrap_is_acpi_device_node(const struct fwnode_handle *fwnode) 43 + { 44 + struct acpi_device *adev = 45 + container_of(fwnode, struct acpi_device, fwnode); 46 + int index; 47 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 48 + bool retval = false; 49 + 50 + if (ops) 51 + retval = ops->is_mock_adev(adev); 52 + 53 + if (!retval) 54 + retval = is_acpi_device_node(fwnode); 55 + 56 + put_cxl_mock_ops(index); 57 + return retval; 58 + } 59 + EXPORT_SYMBOL(__wrap_is_acpi_device_node); 60 + 61 + acpi_status __wrap_acpi_get_table(char *signature, u32 instance, 62 + struct acpi_table_header **out_table) 63 + { 64 + int index; 65 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 66 + acpi_status status; 67 + 68 + if (ops) 69 + status = ops->acpi_get_table(signature, instance, out_table); 70 + else 71 + status = acpi_get_table(signature, instance, out_table); 72 + 73 + put_cxl_mock_ops(index); 74 + 75 + return status; 76 + } 77 + EXPORT_SYMBOL(__wrap_acpi_get_table); 78 + 79 + void __wrap_acpi_put_table(struct acpi_table_header *table) 80 + { 81 + int index; 82 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 83 + 84 + if (ops) 85 + ops->acpi_put_table(table); 86 + else 87 + acpi_put_table(table); 88 + put_cxl_mock_ops(index); 89 + } 90 + EXPORT_SYMBOL(__wrap_acpi_put_table); 91 + 92 + acpi_status __wrap_acpi_evaluate_integer(acpi_handle handle, 93 + acpi_string pathname, 94 + struct acpi_object_list *arguments, 95 + unsigned long long *data) 96 + { 97 + int index; 98 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 99 + acpi_status status; 100 + 101 + if (ops) 102 + status = ops->acpi_evaluate_integer(handle, pathname, arguments, 103 + data); 104 + else 105 + status = acpi_evaluate_integer(handle, pathname, arguments, 106 + data); 107 + put_cxl_mock_ops(index); 108 + 109 + return status; 110 + } 111 + EXPORT_SYMBOL(__wrap_acpi_evaluate_integer); 112 + 113 + struct acpi_pci_root *__wrap_acpi_pci_find_root(acpi_handle handle) 114 + { 115 + int index; 116 + struct acpi_pci_root *root; 117 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 118 + 119 + if (ops) 120 + root = ops->acpi_pci_find_root(handle); 121 + else 122 + root = acpi_pci_find_root(handle); 123 + 124 + put_cxl_mock_ops(index); 125 + 126 + return root; 127 + } 128 + EXPORT_SYMBOL_GPL(__wrap_acpi_pci_find_root); 129 + 130 + void __wrap_pci_walk_bus(struct pci_bus *bus, 131 + int (*cb)(struct pci_dev *, void *), void *userdata) 132 + { 133 + int index; 134 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 135 + 136 + if (ops && ops->is_mock_bus(bus)) { 137 + int rc, i; 138 + 139 + /* 140 + * Simulate 2 root ports per host-bridge and no 141 + * depth recursion. 142 + */ 143 + for (i = 0; i < 2; i++) { 144 + rc = cb((struct pci_dev *) ops->mock_port(bus, i), 145 + userdata); 146 + if (rc) 147 + break; 148 + } 149 + } else 150 + pci_walk_bus(bus, cb, userdata); 151 + 152 + put_cxl_mock_ops(index); 153 + } 154 + EXPORT_SYMBOL_GPL(__wrap_pci_walk_bus); 155 + 156 + struct nvdimm_bus * 157 + __wrap_nvdimm_bus_register(struct device *dev, 158 + struct nvdimm_bus_descriptor *nd_desc) 159 + { 160 + int index; 161 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 162 + 163 + if (ops && ops->is_mock_dev(dev->parent->parent)) 164 + nd_desc->provider_name = "cxl_test"; 165 + put_cxl_mock_ops(index); 166 + 167 + return nvdimm_bus_register(dev, nd_desc); 168 + } 169 + EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register); 170 + 171 + MODULE_LICENSE("GPL v2");
+27
tools/testing/cxl/test/mock.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + #include <linux/list.h> 4 + #include <linux/acpi.h> 5 + 6 + struct cxl_mock_ops { 7 + struct list_head list; 8 + bool (*is_mock_adev)(struct acpi_device *dev); 9 + acpi_status (*acpi_get_table)(char *signature, u32 instance, 10 + struct acpi_table_header **out_table); 11 + void (*acpi_put_table)(struct acpi_table_header *table); 12 + bool (*is_mock_bridge)(struct device *dev); 13 + acpi_status (*acpi_evaluate_integer)(acpi_handle handle, 14 + acpi_string pathname, 15 + struct acpi_object_list *arguments, 16 + unsigned long long *data); 17 + struct acpi_pci_root *(*acpi_pci_find_root)(acpi_handle handle); 18 + struct platform_device *(*mock_port)(struct pci_bus *bus, int index); 19 + bool (*is_mock_bus)(struct pci_bus *bus); 20 + bool (*is_mock_port)(struct platform_device *pdev); 21 + bool (*is_mock_dev)(struct device *dev); 22 + }; 23 + 24 + void register_cxl_mock_ops(struct cxl_mock_ops *ops); 25 + void unregister_cxl_mock_ops(struct cxl_mock_ops *ops); 26 + struct cxl_mock_ops *get_cxl_mock_ops(int *index); 27 + void put_cxl_mock_ops(int index);