History

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 ...

Linus Torvalds committed 5 years ago

Commit dd729452 parents dab334c c6d7e1336 files · +3269 −1490Browse files

Unified · Split

36 files
  1. Documentation/driver-api/cxl/memory-devices.rst +6 −0
  2. arch/powerpc/platforms/powernv/ocxl.c +2 −1
  3. drivers/cxl/acpi.c +97 −42
  4. drivers/cxl/core/Makefile +1 −0
  5. drivers/cxl/core/bus.c +68 −51
  6. drivers/cxl/core/core.h +7 −4
  7. drivers/cxl/core/mbox.c +787 −0
  8. drivers/cxl/core/memdev.c +106 −12
  9. drivers/cxl/core/pmem.c +37 −2
  10. drivers/cxl/cxl.h +39 −19
  11. drivers/cxl/cxlmem.h +179 −23
  12. drivers/cxl/pci.c +109 −1131
  13. drivers/cxl/pci.h +8 −6
  14. drivers/cxl/pmem.c +142 −21
  15. drivers/misc/ocxl/config.c +1 −12
  16. drivers/nvdimm/btt.c +6 −5
  17. drivers/nvdimm/btt_devs.c +8 −6
  18. drivers/nvdimm/core.c +4 −36
  19. drivers/nvdimm/label.c +102 −37
  20. drivers/nvdimm/label.h +89 −5
  21. drivers/nvdimm/namespace_devs.c +52 −43
  22. drivers/nvdimm/nd-core.h +3 −2
  23. drivers/nvdimm/nd.h +156 −29
  24. drivers/nvdimm/pfn_devs.c +1 −1
  25. drivers/pci/pci.c +32 −0
  26. include/linux/nd.h +2 −2
  27. include/linux/pci.h +1 −0
  28. tools/testing/cxl/Kbuild +38 −0
  29. tools/testing/cxl/config_check.c +13 −0
  30. tools/testing/cxl/mock_acpi.c +109 −0
  31. tools/testing/cxl/mock_pmem.c +24 −0
  32. tools/testing/cxl/test/Kbuild +10 −0
  33. tools/testing/cxl/test/cxl.c +576 −0
  34. tools/testing/cxl/test/mem.c +256 −0
  35. tools/testing/cxl/test/mock.c +171 −0
  36. tools/testing/cxl/test/mock.h +27 −0
Documentation/driver-api/cxl/memory-devices.rst+6−0
@@ -39,12 +39,18 @@ CXL Core · 38 unmodified lines
3939 .. kernel-doc:: drivers/cxl/core/bus.c
4040 :doc: cxl core
4141
42+.. kernel-doc:: drivers/cxl/core/bus.c
43+ :identifiers:
44+
4245 .. kernel-doc:: drivers/cxl/core/pmem.c
4346 :doc: cxl pmem
4447
4548 .. kernel-doc:: drivers/cxl/core/regs.c
4649 :doc: cxl registers
4750
51+.. kernel-doc:: drivers/cxl/core/mbox.c
52+ :doc: cxl mbox
53+
4854 External Interfaces
4955 ===================
5056
arch/powerpc/platforms/powernv/ocxl.c+2−1
@@ -107,7 +107,8 @@ static int get_max_afu_index(struct pci_dev *dev, int *afu_idx) · 106 unmodified lines
107107 int pos;
108108 u32 val;
109109
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);
111112 if (!pos)
112113 return -ESRCH;
113114
drivers/cxl/acpi.c+97−42
@@ -52,6 +52,12 @@ static int cxl_acpi_cfmws_verify(struct device *dev, · 51 unmodified lines
5252 return -EINVAL;
5353 }
5454
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+
5561 expected_len = struct_size((cfmws), interleave_targets,
5662 CFMWS_INTERLEAVE_WAYS(cfmws));
5763
@@ -71,11 +77,11 @@ static int cxl_acpi_cfmws_verify(struct device *dev, · 13 unmodified lines
7177 static void cxl_add_cfmws_decoders(struct device *dev,
7278 struct cxl_port *root_port)
7379 {
80+ int target_map[CXL_DECODER_MAX_INTERLEAVE];
7481 struct acpi_cedt_cfmws *cfmws;
7582 struct cxl_decoder *cxld;
7683 acpi_size len, cur = 0;
7784 void *cedt_subtable;
78− unsigned long flags;
7985 int rc;
8086
8187 len = acpi_cedt->length - sizeof(*acpi_cedt);
@@ -83,6 +89,7 @@ static void cxl_add_cfmws_decoders(struct device *dev, · 1 unmodified line
8389
8490 while (cur < len) {
8591 struct acpi_cedt_header *c = cedt_subtable + cur;
92+ int i;
8693
8794 if (c->type != ACPI_CEDT_TYPE_CFMWS) {
8895 cur += c->length;
@@ -108,24 +115,39 @@ static void cxl_add_cfmws_decoders(struct device *dev, · 19 unmodified lines
108115 continue;
109116 }
110117
111− flags = cfmws_to_decoder_flags(cfmws->restrictions);
112− cxld = devm_cxl_add_decoder(dev, root_port,
113− CFMWS_INTERLEAVE_WAYS(cfmws),
114− cfmws->base_hpa, cfmws->window_size,
115− CFMWS_INTERLEAVE_WAYS(cfmws),
116− CFMWS_INTERLEAVE_GRANULARITY(cfmws),
117− CXL_DECODER_EXPANDER,
118− flags);
118+ for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
119+ target_map[i] = cfmws->interleave_targets[i];
119120
120− if (IS_ERR(cxld)) {
121+ cxld = cxl_decoder_alloc(root_port,
122+ CFMWS_INTERLEAVE_WAYS(cfmws));
123+ if (IS_ERR(cxld))
124+ goto next;
125+
126+ cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
127+ cxld->target_type = CXL_DECODER_EXPANDER;
128+ cxld->range = (struct range) {
129+ .start = cfmws->base_hpa,
130+ .end = cfmws->base_hpa + cfmws->window_size - 1,
131+ };
132+ cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
133+ cxld->interleave_granularity =
134+ CFMWS_INTERLEAVE_GRANULARITY(cfmws);
135+
136+ rc = cxl_decoder_add(cxld, target_map);
137+ if (rc)
138+ put_device(&cxld->dev);
139+ else
140+ rc = cxl_decoder_autoremove(dev, cxld);
141+ if (rc) {
121142 dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
122143 cfmws->base_hpa, cfmws->base_hpa +
123144 cfmws->window_size - 1);
124− } else {
125− dev_dbg(dev, "add: %s range %#llx-%#llx\n",
126− dev_name(&cxld->dev), cfmws->base_hpa,
127− cfmws->base_hpa + cfmws->window_size - 1);
145+ goto next;
128146 }
147+ dev_dbg(dev, "add: %s range %#llx-%#llx\n",
148+ dev_name(&cxld->dev), cfmws->base_hpa,
149+ cfmws->base_hpa + cfmws->window_size - 1);
150+next:
129151 cur += c->length;
130152 }
131153 }
@@ -182,15 +204,7 @@ static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs) · 50 unmodified lines
182204 return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
183205 }
184206
185−struct cxl_walk_context {
186− struct device *dev;
187− struct pci_bus *root;
188− struct cxl_port *port;
189− int error;
190− int count;
191−};
192−
193−static int match_add_root_ports(struct pci_dev *pdev, void *data)
207+__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
194208 {
195209 struct cxl_walk_context *ctx = data;
196210 struct pci_bus *root_bus = ctx->root;
@@ -239,7 +253,8 @@ static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device · 42 unmodified lines
239253 return NULL;
240254 }
241255
242−static struct acpi_device *to_cxl_host_bridge(struct device *dev)
256+__mock struct acpi_device *to_cxl_host_bridge(struct device *host,
257+ struct device *dev)
243258 {
244259 struct acpi_device *adev = to_acpi_device(dev);
245260
@@ -257,11 +272,12 @@ static struct acpi_device *to_cxl_host_bridge(struct device *dev) · 11 unmodified lines
257272 */
258273 static int add_host_bridge_uport(struct device *match, void *arg)
259274 {
260− struct acpi_device *bridge = to_cxl_host_bridge(match);
261275 struct cxl_port *root_port = arg;
262276 struct device *host = root_port->dev.parent;
277+ struct acpi_device *bridge = to_cxl_host_bridge(host, match);
263278 struct acpi_pci_root *pci_root;
264279 struct cxl_walk_context ctx;
280+ int single_port_map[1], rc;
265281 struct cxl_decoder *cxld;
266282 struct cxl_dport *dport;
267283 struct cxl_port *port;
@@ -272,7 +288,7 @@ static int add_host_bridge_uport(struct device *match, void *arg) · 4 unmodified lines
272288 dport = find_dport_by_dev(root_port, match);
273289 if (!dport) {
274290 dev_dbg(host, "host bridge expected and not found\n");
275− return -ENODEV;
291+ return 0;
276292 }
277293
278294 port = devm_cxl_add_port(host, match, dport->component_reg_phys,
@@ -297,22 +313,46 @@ static int add_host_bridge_uport(struct device *match, void *arg) · 18 unmodified lines
297313 return -ENODEV;
298314 if (ctx.error)
299315 return ctx.error;
316+ if (ctx.count > 1)
317+ return 0;
300318
301319 /* TODO: Scan CHBCR for HDM Decoder resources */
302320
303321 /*
304− * In the single-port host-bridge case there are no HDM decoders
305− * in the CHBCR and a 1:1 passthrough decode is implied.
322+ * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability
323+ * Structure) single ported host-bridges need not publish a decoder
324+ * capability when a passthrough decode can be assumed, i.e. all
325+ * transactions that the uport sees are claimed and passed to the single
326+ * dport. Disable the range until the first CXL region is enumerated /
327+ * activated.
306328 */
307− if (ctx.count == 1) {
308− cxld = devm_cxl_add_passthrough_decoder(host, port);
309− if (IS_ERR(cxld))
310− return PTR_ERR(cxld);
329+ cxld = cxl_decoder_alloc(port, 1);
330+ if (IS_ERR(cxld))
331+ return PTR_ERR(cxld);
332+
333+ cxld->interleave_ways = 1;
334+ cxld->interleave_granularity = PAGE_SIZE;
335+ cxld->target_type = CXL_DECODER_EXPANDER;
336+ cxld->range = (struct range) {
337+ .start = 0,
338+ .end = -1,
339+ };
311340
312− dev_dbg(host, "add: %s\n", dev_name(&cxld->dev));
313− }
341+ device_lock(&port->dev);
342+ dport = list_first_entry(&port->dports, typeof(*dport), list);
343+ device_unlock(&port->dev);
314344
315− return 0;
345+ single_port_map[0] = dport->port_id;
346+
347+ rc = cxl_decoder_add(cxld, single_port_map);
348+ if (rc)
349+ put_device(&cxld->dev);
350+ else
351+ rc = cxl_decoder_autoremove(host, cxld);
352+
353+ if (rc == 0)
354+ dev_dbg(host, "add: %s\n", dev_name(&cxld->dev));
355+ return rc;
316356 }
317357
318358 static int add_host_bridge_dport(struct device *match, void *arg)
@@ -323,7 +363,7 @@ static int add_host_bridge_dport(struct device *match, void *arg) · 4 unmodified lines
323363 struct acpi_cedt_chbs *chbs;
324364 struct cxl_port *root_port = arg;
325365 struct device *host = root_port->dev.parent;
326− struct acpi_device *bridge = to_cxl_host_bridge(match);
366+ struct acpi_device *bridge = to_cxl_host_bridge(host, match);
327367
328368 if (!bridge)
329369 return 0;
@@ -337,9 +377,11 @@ static int add_host_bridge_dport(struct device *match, void *arg) · 7 unmodified lines
337377 }
338378
339379 chbs = cxl_acpi_match_chbs(host, uid);
340− if (IS_ERR(chbs))
341− dev_dbg(host, "No CHBS found for Host Bridge: %s\n",
342− dev_name(match));
380+ if (IS_ERR(chbs)) {
381+ dev_warn(host, "No CHBS found for Host Bridge: %s\n",
382+ dev_name(match));
383+ return 0;
384+ }
343385
344386 rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));
345387 if (rc) {
@@ -375,6 +417,17 @@ static int add_root_nvdimm_bridge(struct device *match, void *data) · 29 unmodified lines
375417 return 1;
376418 }
377419
420+static u32 cedt_instance(struct platform_device *pdev)
421+{
422+ const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev);
423+
424+ if (native_acpi0017 && *native_acpi0017)
425+ return 0;
426+
427+ /* for cxl_test request a non-canonical instance */
428+ return U32_MAX;
429+}
430+
378431 static int cxl_acpi_probe(struct platform_device *pdev)
379432 {
380433 int rc;
@@ -388,7 +441,7 @@ static int cxl_acpi_probe(struct platform_device *pdev) · 7 unmodified lines
388441 return PTR_ERR(root_port);
389442 dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
390443
391− status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt);
444+ status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt);
392445 if (ACPI_FAILURE(status))
393446 return -ENXIO;
394447
@@ -419,9 +472,11 @@ out: · 24 unmodified lines
419472 return 0;
420473 }
421474
475+static bool native_acpi0017 = true;
476+
422477 static const struct acpi_device_id cxl_acpi_ids[] = {
423− { "ACPI0017", 0 },
424− { "", 0 },
478+ { "ACPI0017", (unsigned long) &native_acpi0017 },
479+ { },
425480 };
426481 MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
427482
drivers/cxl/core/Makefile+1−0
@@ -6,3 +6,4 @@ cxl_core-y := bus.o · 5 unmodified lines
66 cxl_core-y += pmem.o
77 cxl_core-y += regs.o
88 cxl_core-y += memdev.o
9+cxl_core-y += mbox.o
drivers/cxl/core/bus.c+68−51
@@ -453,50 +453,57 @@ err: · 452 unmodified lines
453453 }
454454 EXPORT_SYMBOL_GPL(cxl_add_dport);
455455
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)
461458 {
462− struct cxl_decoder *cxld;
463− struct device *dev;
464− int rc = 0;
459+ int rc = 0, i;
465460
466− if (interleave_ways < 1)
467− return ERR_PTR(-EINVAL);
461+ if (!target_map)
462+ return 0;
468463
469464 device_lock(&port->dev);
470− if (list_empty(&port->dports))
465+ if (list_empty(&port->dports)) {
471466 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:
472481 device_unlock(&port->dev);
473− if (rc)
474− return ERR_PTR(rc);
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+ };
491+ struct device *dev;
492+ int rc = 0;
493+
494+ if (nr_targets > CXL_DECODER_MAX_INTERLEAVE || nr_targets < 1)
495+ return ERR_PTR(-EINVAL);
475496
476497 cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
477498 if (!cxld)
478499 return ERR_PTR(-ENOMEM);
500+ memcpy(cxld, &cxld_const_init, sizeof(cxld_const_init));
479501
480502 rc = ida_alloc(&port->decoder_ida, GFP_KERNEL);
481503 if (rc < 0)
482504 goto err;
483505
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;
500507 dev = &cxld->dev;
501508 device_initialize(dev);
502509 device_set_pm_not_required(dev);
@@ -514,41 +521,47 @@ err: · 11 unmodified lines
514521 kfree(cxld);
515522 return ERR_PTR(rc);
516523 }
524+EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
517525
518−struct cxl_decoder *
519−devm_cxl_add_decoder(struct device *host, struct cxl_port *port, int nr_targets,
520− resource_size_t base, resource_size_t len,
521− int interleave_ways, int interleave_granularity,
522− enum cxl_decoder_type type, unsigned long flags)
526+int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map)
523527 {
524− struct cxl_decoder *cxld;
528+ struct cxl_port *port;
525529 struct device *dev;
526530 int rc;
527531
528− cxld = cxl_decoder_alloc(port, nr_targets, base, len, interleave_ways,
529− interleave_granularity, type, flags);
530− if (IS_ERR(cxld))
531− return cxld;
532+ if (WARN_ON_ONCE(!cxld))
533+ return -EINVAL;
534+
535+ if (WARN_ON_ONCE(IS_ERR(cxld)))
536+ return PTR_ERR(cxld);
537+
538+ if (cxld->interleave_ways < 1)
539+ return -EINVAL;
540+
541+ port = to_cxl_port(cxld->dev.parent);
542+ rc = decoder_populate_targets(cxld, port, target_map);
543+ if (rc)
544+ return rc;
532545
533546 dev = &cxld->dev;
534547 rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);
535548 if (rc)
536− goto err;
549+ return rc;
537550
538− rc = device_add(dev);
539− if (rc)
540− goto err;
551+ return device_add(dev);
552+}
553+EXPORT_SYMBOL_GPL(cxl_decoder_add);
541554
542− rc = devm_add_action_or_reset(host, unregister_cxl_dev, dev);
543− if (rc)
544− return ERR_PTR(rc);
545− return cxld;
555+static void cxld_unregister(void *dev)
556+{
557+ device_unregister(dev);
558+}
546559
547−err:
548− put_device(dev);
549− return ERR_PTR(rc);
560+int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
561+{
562+ return devm_add_action_or_reset(host, cxld_unregister, &cxld->dev);
550563 }
551−EXPORT_SYMBOL_GPL(devm_cxl_add_decoder);
564+EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
552565
553566 /**
554567 * __cxl_driver_register - register a driver for the cxl bus
@@ -635,6 +648,8 @@ static __init int cxl_core_init(void) · 80 unmodified lines
635648 {
636649 int rc;
637650
651+ cxl_mbox_init();
652+
638653 rc = cxl_memdev_init();
639654 if (rc)
640655 return rc;
@@ -646,6 +661,7 @@ static __init int cxl_core_init(void) · 5 unmodified lines
646661
647662 err:
648663 cxl_memdev_exit();
664+ cxl_mbox_exit();
649665 return rc;
650666 }
651667
@@ -653,6 +669,7 @@ static void cxl_core_exit(void) · 1 unmodified line
653669 {
654670 bus_unregister(&cxl_bus_type);
655671 cxl_memdev_exit();
672+ cxl_mbox_exit();
656673 }
657674
658675 module_init(cxl_core_init);
drivers/cxl/core/core.h+7−4
@@ -9,12 +9,15 @@ extern const struct device_type cxl_nvdimm_type; · 8 unmodified lines
99
1010 extern struct attribute_group cxl_base_attribute_group;
1111
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);
1617
1718 int cxl_memdev_init(void);
1819 void cxl_memdev_exit(void);
20+void cxl_mbox_init(void);
21+void cxl_mbox_exit(void);
1922
2023 #endif /* __CXL_CORE_H__ */
drivers/cxl/core/mbox.c+787−0
@@ -0,0 +1,787 @@
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+}
drivers/cxl/core/memdev.c+106−12
@@ -8,6 +8,8 @@ · 7 unmodified lines
88 #include <cxlmem.h>
99 #include "core.h"
1010
11+static DECLARE_RWSEM(cxl_memdev_rwsem);
12+
1113 /*
1214 * An entire PCI topology full of devices should be enough for any
1315 * config
@@ -132,16 +134,53 @@ static const struct device_type cxl_memdev_type = { · 118 unmodified lines
132134 .groups = cxl_memdev_attribute_groups,
133135 };
134136
137+/**
138+ * set_exclusive_cxl_commands() - atomically disable user cxl commands
139+ * @cxlm: cxl_mem instance to modify
140+ * @cmds: bitmap of commands to mark exclusive
141+ *
142+ * Grab the cxl_memdev_rwsem in write mode to flush in-flight
143+ * invocations of the ioctl path and then disable future execution of
144+ * commands with the command ids set in @cmds.
145+ */
146+void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds)
147+{
148+ down_write(&cxl_memdev_rwsem);
149+ bitmap_or(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds,
150+ CXL_MEM_COMMAND_ID_MAX);
151+ up_write(&cxl_memdev_rwsem);
152+}
153+EXPORT_SYMBOL_GPL(set_exclusive_cxl_commands);
154+
155+/**
156+ * clear_exclusive_cxl_commands() - atomically enable user cxl commands
157+ * @cxlm: cxl_mem instance to modify
158+ * @cmds: bitmap of commands to mark available for userspace
159+ */
160+void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds)
161+{
162+ down_write(&cxl_memdev_rwsem);
163+ bitmap_andnot(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds,
164+ CXL_MEM_COMMAND_ID_MAX);
165+ up_write(&cxl_memdev_rwsem);
166+}
167+EXPORT_SYMBOL_GPL(clear_exclusive_cxl_commands);
168+
169+static void cxl_memdev_shutdown(struct device *dev)
170+{
171+ struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
172+
173+ down_write(&cxl_memdev_rwsem);
174+ cxlmd->cxlm = NULL;
175+ up_write(&cxl_memdev_rwsem);
176+}
177+
135178 static void cxl_memdev_unregister(void *_cxlmd)
136179 {
137180 struct cxl_memdev *cxlmd = _cxlmd;
138181 struct device *dev = &cxlmd->dev;
139− struct cdev *cdev = &cxlmd->cdev;
140− const struct cdevm_file_operations *cdevm_fops;
141−
142− cdevm_fops = container_of(cdev->ops, typeof(*cdevm_fops), fops);
143− cdevm_fops->shutdown(dev);
144182
183+ cxl_memdev_shutdown(dev);
145184 cdev_device_del(&cxlmd->cdev, dev);
146185 put_device(dev);
147186 }
@@ -149,7 +188,6 @@ static void cxl_memdev_unregister(void *_cxlmd) · 1 unmodified line
149188 static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
150189 const struct file_operations *fops)
151190 {
152− struct pci_dev *pdev = cxlm->pdev;
153191 struct cxl_memdev *cxlmd;
154192 struct device *dev;
155193 struct cdev *cdev;
@@ -166,7 +204,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm, · 10 unmodified lines
166204
167205 dev = &cxlmd->dev;
168206 device_initialize(dev);
169− dev->parent = &pdev->dev;
207+ dev->parent = cxlm->dev;
170208 dev->bus = &cxl_bus_type;
171209 dev->devt = MKDEV(cxl_mem_major, cxlmd->id);
172210 dev->type = &cxl_memdev_type;
@@ -181,16 +219,72 @@ err: · 8 unmodified lines
181219 return ERR_PTR(rc);
182220 }
183221
222+static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
223+ unsigned long arg)
224+{
225+ switch (cmd) {
226+ case CXL_MEM_QUERY_COMMANDS:
227+ return cxl_query_cmd(cxlmd, (void __user *)arg);
228+ case CXL_MEM_SEND_COMMAND:
229+ return cxl_send_cmd(cxlmd, (void __user *)arg);
230+ default:
231+ return -ENOTTY;
232+ }
233+}
234+
235+static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
236+ unsigned long arg)
237+{
238+ struct cxl_memdev *cxlmd = file->private_data;
239+ int rc = -ENXIO;
240+
241+ down_read(&cxl_memdev_rwsem);
242+ if (cxlmd->cxlm)
243+ rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
244+ up_read(&cxl_memdev_rwsem);
245+
246+ return rc;
247+}
248+
249+static int cxl_memdev_open(struct inode *inode, struct file *file)
250+{
251+ struct cxl_memdev *cxlmd =
252+ container_of(inode->i_cdev, typeof(*cxlmd), cdev);
253+
254+ get_device(&cxlmd->dev);
255+ file->private_data = cxlmd;
256+
257+ return 0;
258+}
259+
260+static int cxl_memdev_release_file(struct inode *inode, struct file *file)
261+{
262+ struct cxl_memdev *cxlmd =
263+ container_of(inode->i_cdev, typeof(*cxlmd), cdev);
264+
265+ put_device(&cxlmd->dev);
266+
267+ return 0;
268+}
269+
270+static const struct file_operations cxl_memdev_fops = {
271+ .owner = THIS_MODULE,
272+ .unlocked_ioctl = cxl_memdev_ioctl,
273+ .open = cxl_memdev_open,
274+ .release = cxl_memdev_release_file,
275+ .compat_ioctl = compat_ptr_ioctl,
276+ .llseek = noop_llseek,
277+};
278+
184279 struct cxl_memdev *
185−devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm,
186− const struct cdevm_file_operations *cdevm_fops)
280+devm_cxl_add_memdev(struct cxl_mem *cxlm)
187281 {
188282 struct cxl_memdev *cxlmd;
189283 struct device *dev;
190284 struct cdev *cdev;
191285 int rc;
192286
193− cxlmd = cxl_memdev_alloc(cxlm, &cdevm_fops->fops);
287+ cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
194288 if (IS_ERR(cxlmd))
195289 return cxlmd;
196290
@@ -210,7 +304,7 @@ devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm, · 13 unmodified lines
210304 if (rc)
211305 goto err;
212306
213− rc = devm_add_action_or_reset(host, cxl_memdev_unregister, cxlmd);
307+ rc = devm_add_action_or_reset(cxlm->dev, cxl_memdev_unregister, cxlmd);
214308 if (rc)
215309 return ERR_PTR(rc);
216310 return cxlmd;
@@ -220,7 +314,7 @@ err: · 3 unmodified lines
220314 * The cdev was briefly live, shutdown any ioctl operations that
221315 * saw that state.
222316 */
223− cdevm_fops->shutdown(dev);
317+ cxl_memdev_shutdown(dev);
224318 put_device(dev);
225319 return ERR_PTR(rc);
226320 }
drivers/cxl/core/pmem.c+37−2
@@ -2,6 +2,7 @@ · 1 unmodified line
22 /* Copyright(c) 2020 Intel Corporation. */
33 #include <linux/device.h>
44 #include <linux/slab.h>
5+#include <linux/idr.h>
56 #include <cxlmem.h>
67 #include <cxl.h>
78 #include "core.h"
@@ -20,10 +21,13 @@ · 12 unmodified lines
2021 * operations, for example, namespace label access commands.
2122 */
2223
24+static DEFINE_IDA(cxl_nvdimm_bridge_ida);
25+
2326 static void cxl_nvdimm_bridge_release(struct device *dev)
2427 {
2528 struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
2629
30+ ida_free(&cxl_nvdimm_bridge_ida, cxl_nvb->id);
2731 kfree(cxl_nvb);
2832 }
2933
@@ -47,16 +51,38 @@ struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev) · 17 unmodified lines
4751 }
4852 EXPORT_SYMBOL_GPL(to_cxl_nvdimm_bridge);
4953
54+__mock int match_nvdimm_bridge(struct device *dev, const void *data)
55+{
56+ return dev->type == &cxl_nvdimm_bridge_type;
57+}
58+
59+struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd)
60+{
61+ struct device *dev;
62+
63+ dev = bus_find_device(&cxl_bus_type, NULL, cxl_nvd, match_nvdimm_bridge);
64+ if (!dev)
65+ return NULL;
66+ return to_cxl_nvdimm_bridge(dev);
67+}
68+EXPORT_SYMBOL_GPL(cxl_find_nvdimm_bridge);
69+
5070 static struct cxl_nvdimm_bridge *
5171 cxl_nvdimm_bridge_alloc(struct cxl_port *port)
5272 {
5373 struct cxl_nvdimm_bridge *cxl_nvb;
5474 struct device *dev;
75+ int rc;
5576
5677 cxl_nvb = kzalloc(sizeof(*cxl_nvb), GFP_KERNEL);
5778 if (!cxl_nvb)
5879 return ERR_PTR(-ENOMEM);
5980
81+ rc = ida_alloc(&cxl_nvdimm_bridge_ida, GFP_KERNEL);
82+ if (rc < 0)
83+ goto err;
84+ cxl_nvb->id = rc;
85+
6086 dev = &cxl_nvb->dev;
6187 cxl_nvb->port = port;
6288 cxl_nvb->state = CXL_NVB_NEW;
@@ -67,6 +93,10 @@ cxl_nvdimm_bridge_alloc(struct cxl_port *port) · 4 unmodified lines
6793 dev->type = &cxl_nvdimm_bridge_type;
6894
6995 return cxl_nvb;
96+
97+err:
98+ kfree(cxl_nvb);
99+ return ERR_PTR(rc);
70100 }
71101
72102 static void unregister_nvb(void *_cxl_nvb)
@@ -119,7 +149,7 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host, · 46 unmodified lines
119149 return cxl_nvb;
120150
121151 dev = &cxl_nvb->dev;
122− rc = dev_set_name(dev, "nvdimm-bridge");
152+ rc = dev_set_name(dev, "nvdimm-bridge%d", cxl_nvb->id);
123153 if (rc)
124154 goto err;
125155
@@ -192,6 +222,11 @@ static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_memdev *cxlmd) · 66 unmodified lines
192222 return cxl_nvd;
193223 }
194224
225+static void cxl_nvd_unregister(void *dev)
226+{
227+ device_unregister(dev);
228+}
229+
195230 /**
196231 * devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm
197232 * @host: same host as @cxlmd
@@ -221,7 +256,7 @@ int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd) · 23 unmodified lines
221256 dev_dbg(host, "%s: register %s\n", dev_name(dev->parent),
222257 dev_name(dev));
223258
224− return devm_add_action_or_reset(host, unregister_cxl_dev, dev);
259+ return devm_add_action_or_reset(host, cxl_nvd_unregister, dev);
225260
226261 err:
227262 put_device(dev);
drivers/cxl/cxl.h+39−19
@@ -114,7 +114,17 @@ struct cxl_device_reg_map { · 113 unmodified lines
114114 struct cxl_reg_map memdev;
115115 };
116116
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+ */
117126 struct cxl_register_map {
127+ void __iomem *base;
118128 u64 block_offset;
119129 u8 reg_type;
120130 u8 barno;
@@ -155,6 +165,12 @@ enum cxl_decoder_type { · 34 unmodified lines
155165 CXL_DECODER_EXPANDER = 3,
156166 };
157167
168+/*
169+ * Current specification goes up to 8, double that seems a reasonable
170+ * software max for the foreseeable future
171+ */
172+#define CXL_DECODER_MAX_INTERLEAVE 16
173+
158174 /**
159175 * struct cxl_decoder - CXL address range decode configuration
160176 * @dev: this decoder's device
@@ -164,6 +180,7 @@ enum cxl_decoder_type { · 3 unmodified lines
164180 * @interleave_granularity: data stride per dport
165181 * @target_type: accelerator vs expander (type2 vs type3) selector
166182 * @flags: memory type capabilities and locking
183+ * @nr_targets: number of elements in @target
167184 * @target: active ordered target list in current decoder configuration
168185 */
169186 struct cxl_decoder {
@@ -174,6 +191,7 @@ struct cxl_decoder { · 4 unmodified lines
174191 int interleave_granularity;
175192 enum cxl_decoder_type target_type;
176193 unsigned long flags;
194+ const int nr_targets;
177195 struct cxl_dport *target[];
178196 };
179197
@@ -186,6 +204,7 @@ enum cxl_nvdimm_brige_state { · 6 unmodified lines
186204 };
187205
188206 struct cxl_nvdimm_bridge {
207+ int id;
189208 struct device dev;
190209 struct cxl_port *port;
191210 struct nvdimm_bus *nvdimm_bus;
@@ -200,6 +219,14 @@ struct cxl_nvdimm { · 8 unmodified lines
200219 struct nvdimm *nvdimm;
201220 };
202221
222+struct cxl_walk_context {
223+ struct device *dev;
224+ struct pci_bus *root;
225+ struct cxl_port *port;
226+ int error;
227+ int count;
228+};
229+
203230 /**
204231 * struct cxl_port - logical collection of upstream port devices and
205232 * downstream port devices to construct a CXL memory
@@ -246,25 +273,9 @@ int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id, · 40 unmodified lines
246273
247274 struct cxl_decoder *to_cxl_decoder(struct device *dev);
248275 bool is_root_decoder(struct device *dev);
249−struct cxl_decoder *
250−devm_cxl_add_decoder(struct device *host, struct cxl_port *port, int nr_targets,
251− resource_size_t base, resource_size_t len,
252− int interleave_ways, int interleave_granularity,
253− enum cxl_decoder_type type, unsigned long flags);
254−
255−/*
256− * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability Structure)
257− * single ported host-bridges need not publish a decoder capability when a
258− * passthrough decode can be assumed, i.e. all transactions that the uport sees
259− * are claimed and passed to the single dport. Default the range a 0-base
260− * 0-length until the first CXL region is activated.
261− */
262−static inline struct cxl_decoder *
263−devm_cxl_add_passthrough_decoder(struct device *host, struct cxl_port *port)
264−{
265− return devm_cxl_add_decoder(host, port, 1, 0, 0, 1, PAGE_SIZE,
266− CXL_DECODER_EXPANDER, 0);
267−}
276+struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets);
277+int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map);
278+int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld);
268279
269280 extern struct bus_type cxl_bus_type;
270281
@@ -298,4 +309,13 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host, · 27 unmodified lines
298309 struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
299310 bool is_cxl_nvdimm(struct device *dev);
300311 int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);
312+struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd);
313+
314+/*
315+ * Unit test builds overrides this to __weak, find the 'strong' version
316+ * of these symbols in tools/testing/cxl/.
317+ */
318+#ifndef __mock
319+#define __mock static
320+#endif
301321 #endif /* __CXL_H__ */
drivers/cxl/cxlmem.h+179−23
@@ -2,6 +2,7 @@ · 1 unmodified line
22 /* Copyright(c) 2020-2021 Intel Corporation. */
33 #ifndef __CXL_MEM_H__
44 #define __CXL_MEM_H__
5+#include <uapi/linux/cxl_mem.h>
56 #include <linux/cdev.h>
67 #include "cxl.h"
78
@@ -28,21 +29,6 @@ · 20 unmodified lines
2829 (FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \
2930 CXLMDEV_RESET_NEEDED_NOT)
3031
31−/**
32− * struct cdevm_file_operations - devm coordinated cdev file operations
33− * @fops: file operations that are synchronized against @shutdown
34− * @shutdown: disconnect driver data
35− *
36− * @shutdown is invoked in the devres release path to disconnect any
37− * driver instance data from @dev. It assumes synchronization with any
38− * fops operation that requires driver data. After @shutdown an
39− * operation may only reference @device data.
40− */
41−struct cdevm_file_operations {
42− struct file_operations fops;
43− void (*shutdown)(struct device *dev);
44−};
45−
4632 /**
4733 * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device
4834 * @dev: driver core device object
@@ -62,13 +48,50 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev) · 13 unmodified lines
6248 return container_of(dev, struct cxl_memdev, dev);
6349 }
6450
65−struct cxl_memdev *
66−devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm,
67− const struct cdevm_file_operations *cdevm_fops);
51+struct cxl_memdev *devm_cxl_add_memdev(struct cxl_mem *cxlm);
52+
53+/**
54+ * struct cxl_mbox_cmd - A command to be submitted to hardware.
55+ * @opcode: (input) The command set and command submitted to hardware.
56+ * @payload_in: (input) Pointer to the input payload.
57+ * @payload_out: (output) Pointer to the output payload. Must be allocated by
58+ * the caller.
59+ * @size_in: (input) Number of bytes to load from @payload_in.
60+ * @size_out: (input) Max number of bytes loaded into @payload_out.
61+ * (output) Number of bytes generated by the device. For fixed size
62+ * outputs commands this is always expected to be deterministic. For
63+ * variable sized output commands, it tells the exact number of bytes
64+ * written.
65+ * @return_code: (output) Error code returned from hardware.
66+ *
67+ * This is the primary mechanism used to send commands to the hardware.
68+ * All the fields except @payload_* correspond exactly to the fields described in
69+ * Command Register section of the CXL 2.0 8.2.8.4.5. @payload_in and
70+ * @payload_out are written to, and read from the Command Payload Registers
71+ * defined in CXL 2.0 8.2.8.4.8.
72+ */
73+struct cxl_mbox_cmd {
74+ u16 opcode;
75+ void *payload_in;
76+ void *payload_out;
77+ size_t size_in;
78+ size_t size_out;
79+ u16 return_code;
80+#define CXL_MBOX_SUCCESS 0
81+};
82+
83+/*
84+ * CXL 2.0 - Memory capacity multiplier
85+ * See Section 8.2.9.5
86+ *
87+ * Volatile, Persistent, and Partition capacities are specified to be in
88+ * multiples of 256MB - define a multiplier to convert to/from bytes.
89+ */
90+#define CXL_CAPACITY_MULTIPLIER SZ_256M
6891
6992 /**
7093 * struct cxl_mem - A CXL memory device
71− * @pdev: The PCI device associated with this CXL device.
94+ * @dev: The device associated with this CXL device.
7295 * @cxlmd: Logical memory device chardev / interface
7396 * @regs: Parsed register blocks
7497 * @payload_size: Size of space for payload
@@ -78,11 +101,24 @@ devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm, · 3 unmodified lines
78101 * @mbox_mutex: Mutex to synchronize mailbox access.
79102 * @firmware_version: Firmware version for the memory device.
80103 * @enabled_cmds: Hardware commands found enabled in CEL.
81− * @pmem_range: Persistent memory capacity information.
82− * @ram_range: Volatile memory capacity information.
104+ * @exclusive_cmds: Commands that are kernel-internal only
105+ * @pmem_range: Active Persistent memory capacity configuration
106+ * @ram_range: Active Volatile memory capacity configuration
107+ * @total_bytes: sum of all possible capacities
108+ * @volatile_only_bytes: hard volatile capacity
109+ * @persistent_only_bytes: hard persistent capacity
110+ * @partition_align_bytes: alignment size for partition-able capacity
111+ * @active_volatile_bytes: sum of hard + soft volatile
112+ * @active_persistent_bytes: sum of hard + soft persistent
113+ * @next_volatile_bytes: volatile capacity change pending device reset
114+ * @next_persistent_bytes: persistent capacity change pending device reset
115+ * @mbox_send: @dev specific transport for transmitting mailbox commands
116+ *
117+ * See section 8.2.9.5.2 Capacity Configuration and Label Storage for
118+ * details on capacity parameters.
83119 */
84120 struct cxl_mem {
85− struct pci_dev *pdev;
121+ struct device *dev;
86122 struct cxl_memdev *cxlmd;
87123
88124 struct cxl_regs regs;
@@ -91,7 +127,8 @@ struct cxl_mem { · 2 unmodified lines
91127 size_t lsa_size;
92128 struct mutex mbox_mutex; /* Protects device mailbox and firmware */
93129 char firmware_version[0x10];
94− unsigned long *enabled_cmds;
130+ DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX);
131+ DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
95132
96133 struct range pmem_range;
97134 struct range ram_range;
@@ -104,5 +141,124 @@ struct cxl_mem { · 6 unmodified lines
104141 u64 active_persistent_bytes;
105142 u64 next_volatile_bytes;
106143 u64 next_persistent_bytes;
144+
145+ int (*mbox_send)(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd);
146+};
147+
148+enum cxl_opcode {
149+ CXL_MBOX_OP_INVALID = 0x0000,
150+ CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID,
151+ CXL_MBOX_OP_GET_FW_INFO = 0x0200,
152+ CXL_MBOX_OP_ACTIVATE_FW = 0x0202,
153+ CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400,
154+ CXL_MBOX_OP_GET_LOG = 0x0401,
155+ CXL_MBOX_OP_IDENTIFY = 0x4000,
156+ CXL_MBOX_OP_GET_PARTITION_INFO = 0x4100,
157+ CXL_MBOX_OP_SET_PARTITION_INFO = 0x4101,
158+ CXL_MBOX_OP_GET_LSA = 0x4102,
159+ CXL_MBOX_OP_SET_LSA = 0x4103,
160+ CXL_MBOX_OP_GET_HEALTH_INFO = 0x4200,
161+ CXL_MBOX_OP_GET_ALERT_CONFIG = 0x4201,
162+ CXL_MBOX_OP_SET_ALERT_CONFIG = 0x4202,
163+ CXL_MBOX_OP_GET_SHUTDOWN_STATE = 0x4203,
164+ CXL_MBOX_OP_SET_SHUTDOWN_STATE = 0x4204,
165+ CXL_MBOX_OP_GET_POISON = 0x4300,
166+ CXL_MBOX_OP_INJECT_POISON = 0x4301,
167+ CXL_MBOX_OP_CLEAR_POISON = 0x4302,
168+ CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS = 0x4303,
169+ CXL_MBOX_OP_SCAN_MEDIA = 0x4304,
170+ CXL_MBOX_OP_GET_SCAN_MEDIA = 0x4305,
171+ CXL_MBOX_OP_MAX = 0x10000
107172 };
173+
174+#define DEFINE_CXL_CEL_UUID \
175+ UUID_INIT(0xda9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 0x96, 0xb1, 0x62, \
176+ 0x3b, 0x3f, 0x17)
177+
178+#define DEFINE_CXL_VENDOR_DEBUG_UUID \
179+ UUID_INIT(0xe1819d9, 0x11a9, 0x400c, 0x81, 0x1f, 0xd6, 0x07, 0x19, \
180+ 0x40, 0x3d, 0x86)
181+
182+struct cxl_mbox_get_supported_logs {
183+ __le16 entries;
184+ u8 rsvd[6];
185+ struct cxl_gsl_entry {
186+ uuid_t uuid;
187+ __le32 size;
188+ } __packed entry[];
189+} __packed;
190+
191+struct cxl_cel_entry {
192+ __le16 opcode;
193+ __le16 effect;
194+} __packed;
195+
196+struct cxl_mbox_get_log {
197+ uuid_t uuid;
198+ __le32 offset;
199+ __le32 length;
200+} __packed;
201+
202+/* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
203+struct cxl_mbox_identify {
204+ char fw_revision[0x10];
205+ __le64 total_capacity;
206+ __le64 volatile_capacity;
207+ __le64 persistent_capacity;
208+ __le64 partition_align;
209+ __le16 info_event_log_size;
210+ __le16 warning_event_log_size;
211+ __le16 failure_event_log_size;
212+ __le16 fatal_event_log_size;
213+ __le32 lsa_size;
214+ u8 poison_list_max_mer[3];
215+ __le16 inject_poison_limit;
216+ u8 poison_caps;
217+ u8 qos_telemetry_caps;
218+} __packed;
219+
220+struct cxl_mbox_get_lsa {
221+ u32 offset;
222+ u32 length;
223+} __packed;
224+
225+struct cxl_mbox_set_lsa {
226+ u32 offset;
227+ u32 reserved;
228+ u8 data[];
229+} __packed;
230+
231+/**
232+ * struct cxl_mem_command - Driver representation of a memory device command
233+ * @info: Command information as it exists for the UAPI
234+ * @opcode: The actual bits used for the mailbox protocol
235+ * @flags: Set of flags effecting driver behavior.
236+ *
237+ * * %CXL_CMD_FLAG_FORCE_ENABLE: In cases of error, commands with this flag
238+ * will be enabled by the driver regardless of what hardware may have
239+ * advertised.
240+ *
241+ * The cxl_mem_command is the driver's internal representation of commands that
242+ * are supported by the driver. Some of these commands may not be supported by
243+ * the hardware. The driver will use @info to validate the fields passed in by
244+ * the user then submit the @opcode to the hardware.
245+ *
246+ * See struct cxl_command_info.
247+ */
248+struct cxl_mem_command {
249+ struct cxl_command_info info;
250+ enum cxl_opcode opcode;
251+ u32 flags;
252+#define CXL_CMD_FLAG_NONE 0
253+#define CXL_CMD_FLAG_FORCE_ENABLE BIT(0)
254+};
255+
256+int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
257+ size_t in_size, void *out, size_t out_size);
258+int cxl_mem_identify(struct cxl_mem *cxlm);
259+int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm);
260+int cxl_mem_create_range_info(struct cxl_mem *cxlm);
261+struct cxl_mem *cxl_mem_create(struct device *dev);
262+void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds);
263+void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds);
108264 #endif /* __CXL_MEM_H__ */
drivers/cxl/pci.c+109−1131
@@ -1,17 +1,12 @@
11 // SPDX-License-Identifier: GPL-2.0-only
22 /* 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>
64 #include <linux/module.h>
75 #include <linux/sizes.h>
86 #include <linux/mutex.h>
97 #include <linux/list.h>
10−#include <linux/cdev.h>
11−#include <linux/idr.h>
128 #include <linux/pci.h>
139 #include <linux/io.h>
14−#include <linux/io-64-nonatomic-lo-hi.h>
1510 #include "cxlmem.h"
1611 #include "pci.h"
1712 #include "cxl.h"
@@ -21,14 +16,16 @@ · 3 unmodified lines
2116 *
2217 * This implements the PCI exclusive functionality for a CXL device as it is
2318 * defined by the Compute Express Link specification. CXL devices may surface
24− * certain functionality even if it isn't CXL enabled.
19+ * certain functionality even if it isn't CXL enabled. While this driver is
20+ * focused around the PCI specific aspects of a CXL device, it binds to the
21+ * specific CXL memory device class code, and therefore the implementation of
22+ * cxl_pci is focused around CXL memory devices.
2523 *
2624 * The driver has several responsibilities, mainly:
2725 * - Create the memX device and register on the CXL bus.
2826 * - Enumerate device's register interface and map them.
29− * - Probe the device attributes to establish sysfs interface.
30− * - Provide an IOCTL interface to userspace to communicate with the device for
31− * things like firmware update.
27+ * - Registers nvdimm bridge device with cxl_core.
28+ * - Registers a CXL mailbox with cxl_core.
3229 */
3330
3431 #define cxl_doorbell_busy(cxlm) \
@@ -38,202 +35,7 @@ · 3 unmodified lines
3835 /* CXL 2.0 - 8.2.8.4 */
3936 #define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
4037
41−enum opcode {
42− CXL_MBOX_OP_INVALID = 0x0000,
43− CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID,
44− CXL_MBOX_OP_GET_FW_INFO = 0x0200,
45− CXL_MBOX_OP_ACTIVATE_FW = 0x0202,
46− CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400,
47− CXL_MBOX_OP_GET_LOG = 0x0401,
48− CXL_MBOX_OP_IDENTIFY = 0x4000,
49− CXL_MBOX_OP_GET_PARTITION_INFO = 0x4100,
50− CXL_MBOX_OP_SET_PARTITION_INFO = 0x4101,
51− CXL_MBOX_OP_GET_LSA = 0x4102,
52− CXL_MBOX_OP_SET_LSA = 0x4103,
53− CXL_MBOX_OP_GET_HEALTH_INFO = 0x4200,
54− CXL_MBOX_OP_GET_ALERT_CONFIG = 0x4201,
55− CXL_MBOX_OP_SET_ALERT_CONFIG = 0x4202,
56− CXL_MBOX_OP_GET_SHUTDOWN_STATE = 0x4203,
57− CXL_MBOX_OP_SET_SHUTDOWN_STATE = 0x4204,
58− CXL_MBOX_OP_GET_POISON = 0x4300,
59− CXL_MBOX_OP_INJECT_POISON = 0x4301,
60− CXL_MBOX_OP_CLEAR_POISON = 0x4302,
61− CXL_MBOX_OP_GET_SCAN_MEDIA_CAPS = 0x4303,
62− CXL_MBOX_OP_SCAN_MEDIA = 0x4304,
63− CXL_MBOX_OP_GET_SCAN_MEDIA = 0x4305,
64− CXL_MBOX_OP_MAX = 0x10000
65−};
66−
67−/*
68− * CXL 2.0 - Memory capacity multiplier
69− * See Section 8.2.9.5
70− *
71− * Volatile, Persistent, and Partition capacities are specified to be in
72− * multiples of 256MB - define a multiplier to convert to/from bytes.
73− */
74−#define CXL_CAPACITY_MULTIPLIER SZ_256M
75−
76−/**
77− * struct mbox_cmd - A command to be submitted to hardware.
78− * @opcode: (input) The command set and command submitted to hardware.
79− * @payload_in: (input) Pointer to the input payload.
80− * @payload_out: (output) Pointer to the output payload. Must be allocated by
81− * the caller.
82− * @size_in: (input) Number of bytes to load from @payload_in.
83− * @size_out: (input) Max number of bytes loaded into @payload_out.
84− * (output) Number of bytes generated by the device. For fixed size
85− * outputs commands this is always expected to be deterministic. For
86− * variable sized output commands, it tells the exact number of bytes
87− * written.
88− * @return_code: (output) Error code returned from hardware.
89− *
90− * This is the primary mechanism used to send commands to the hardware.
91− * All the fields except @payload_* correspond exactly to the fields described in
92− * Command Register section of the CXL 2.0 8.2.8.4.5. @payload_in and
93− * @payload_out are written to, and read from the Command Payload Registers
94− * defined in CXL 2.0 8.2.8.4.8.
95− */
96−struct mbox_cmd {
97− u16 opcode;
98− void *payload_in;
99− void *payload_out;
100− size_t size_in;
101− size_t size_out;
102− u16 return_code;
103−#define CXL_MBOX_SUCCESS 0
104−};
105−
106−static DECLARE_RWSEM(cxl_memdev_rwsem);
107−static struct dentry *cxl_debugfs;
108−static bool cxl_raw_allow_all;
109−
110−enum {
111− CEL_UUID,
112− VENDOR_DEBUG_UUID,
113−};
114−
115−/* See CXL 2.0 Table 170. Get Log Input Payload */
116−static const uuid_t log_uuid[] = {
117− [CEL_UUID] = UUID_INIT(0xda9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 0x96,
118− 0xb1, 0x62, 0x3b, 0x3f, 0x17),
119− [VENDOR_DEBUG_UUID] = UUID_INIT(0xe1819d9, 0x11a9, 0x400c, 0x81, 0x1f,
120− 0xd6, 0x07, 0x19, 0x40, 0x3d, 0x86),
121−};
122−
123−/**
124− * struct cxl_mem_command - Driver representation of a memory device command
125− * @info: Command information as it exists for the UAPI
126− * @opcode: The actual bits used for the mailbox protocol
127− * @flags: Set of flags effecting driver behavior.
128− *
129− * * %CXL_CMD_FLAG_FORCE_ENABLE: In cases of error, commands with this flag
130− * will be enabled by the driver regardless of what hardware may have
131− * advertised.
132− *
133− * The cxl_mem_command is the driver's internal representation of commands that
134− * are supported by the driver. Some of these commands may not be supported by
135− * the hardware. The driver will use @info to validate the fields passed in by
136− * the user then submit the @opcode to the hardware.
137− *
138− * See struct cxl_command_info.
139− */
140−struct cxl_mem_command {
141− struct cxl_command_info info;
142− enum opcode opcode;
143− u32 flags;
144−#define CXL_CMD_FLAG_NONE 0
145−#define CXL_CMD_FLAG_FORCE_ENABLE BIT(0)
146−};
147−
148−#define CXL_CMD(_id, sin, sout, _flags) \
149− [CXL_MEM_COMMAND_ID_##_id] = { \
150− .info = { \
151− .id = CXL_MEM_COMMAND_ID_##_id, \
152− .size_in = sin, \
153− .size_out = sout, \
154− }, \
155− .opcode = CXL_MBOX_OP_##_id, \
156− .flags = _flags, \
157− }
158−
159−/*
160− * This table defines the supported mailbox commands for the driver. This table
161− * is made up of a UAPI structure. Non-negative values as parameters in the
162− * table will be validated against the user's input. For example, if size_in is
163− * 0, and the user passed in 1, it is an error.
164− */
165−static struct cxl_mem_command mem_commands[CXL_MEM_COMMAND_ID_MAX] = {
166− CXL_CMD(IDENTIFY, 0, 0x43, CXL_CMD_FLAG_FORCE_ENABLE),
167−#ifdef CONFIG_CXL_MEM_RAW_COMMANDS
168− CXL_CMD(RAW, ~0, ~0, 0),
169−#endif
170− CXL_CMD(GET_SUPPORTED_LOGS, 0, ~0, CXL_CMD_FLAG_FORCE_ENABLE),
171− CXL_CMD(GET_FW_INFO, 0, 0x50, 0),
172− CXL_CMD(GET_PARTITION_INFO, 0, 0x20, 0),
173− CXL_CMD(GET_LSA, 0x8, ~0, 0),
174− CXL_CMD(GET_HEALTH_INFO, 0, 0x12, 0),
175− CXL_CMD(GET_LOG, 0x18, ~0, CXL_CMD_FLAG_FORCE_ENABLE),
176− CXL_CMD(SET_PARTITION_INFO, 0x0a, 0, 0),
177− CXL_CMD(SET_LSA, ~0, 0, 0),
178− CXL_CMD(GET_ALERT_CONFIG, 0, 0x10, 0),
179− CXL_CMD(SET_ALERT_CONFIG, 0xc, 0, 0),
180− CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0),
181− CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0),
182− CXL_CMD(GET_POISON, 0x10, ~0, 0),
183− CXL_CMD(INJECT_POISON, 0x8, 0, 0),
184− CXL_CMD(CLEAR_POISON, 0x48, 0, 0),
185− CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0),
186− CXL_CMD(SCAN_MEDIA, 0x11, 0, 0),
187− CXL_CMD(GET_SCAN_MEDIA, 0, ~0, 0),
188−};
189−
190−/*
191− * Commands that RAW doesn't permit. The rationale for each:
192− *
193− * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment /
194− * coordination of transaction timeout values at the root bridge level.
195− *
196− * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live
197− * and needs to be coordinated with HDM updates.
198− *
199− * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the
200− * driver and any writes from userspace invalidates those contents.
201− *
202− * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes
203− * to the device after it is marked clean, userspace can not make that
204− * assertion.
205− *
206− * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that
207− * is kept up to date with patrol notifications and error management.
208− */
209−static u16 cxl_disabled_raw_commands[] = {
210− CXL_MBOX_OP_ACTIVATE_FW,
211− CXL_MBOX_OP_SET_PARTITION_INFO,
212− CXL_MBOX_OP_SET_LSA,
213− CXL_MBOX_OP_SET_SHUTDOWN_STATE,
214− CXL_MBOX_OP_SCAN_MEDIA,
215− CXL_MBOX_OP_GET_SCAN_MEDIA,
216−};
217−
218−/*
219− * Command sets that RAW doesn't permit. All opcodes in this set are
220− * disabled because they pass plain text security payloads over the
221− * user/kernel boundary. This functionality is intended to be wrapped
222− * behind the keys ABI which allows for encrypted payloads in the UAPI
223− */
224−static u8 security_command_sets[] = {
225− 0x44, /* Sanitize */
226− 0x45, /* Persistent Memory Data-at-rest Security */
227− 0x46, /* Security Passthrough */
228−};
229−
230−#define cxl_for_each_cmd(cmd) \
231− for ((cmd) = &mem_commands[0]; \
232− ((cmd) - mem_commands) < ARRAY_SIZE(mem_commands); (cmd)++)
233−
234−#define cxl_cmd_count ARRAY_SIZE(mem_commands)
235−
236−static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
38+static int cxl_pci_mbox_wait_for_doorbell(struct cxl_mem *cxlm)
23739 {
23840 const unsigned long start = jiffies;
23941 unsigned long end = start;
@@ -250,32 +52,22 @@ static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm) · 10 unmodified lines
25052 cpu_relax();
25153 }
25254
253− dev_dbg(&cxlm->pdev->dev, "Doorbell wait took %dms",
55+ dev_dbg(cxlm->dev, "Doorbell wait took %dms",
25456 jiffies_to_msecs(end) - jiffies_to_msecs(start));
25557 return 0;
25658 }
25759
258−static bool cxl_is_security_command(u16 opcode)
60+static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm,
61+ struct cxl_mbox_cmd *mbox_cmd)
25962 {
260− int i;
261−
262− for (i = 0; i < ARRAY_SIZE(security_command_sets); i++)
263− if (security_command_sets[i] == (opcode >> 8))
264− return true;
265− return false;
266−}
267−
268−static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm,
269− struct mbox_cmd *mbox_cmd)
270−{
271− struct device *dev = &cxlm->pdev->dev;
63+ struct device *dev = cxlm->dev;
27264
27365 dev_dbg(dev, "Mailbox command (opcode: %#x size: %zub) timed out\n",
27466 mbox_cmd->opcode, mbox_cmd->size_in);
27567 }
27668
27769 /**
278− * __cxl_mem_mbox_send_cmd() - Execute a mailbox command
70+ * __cxl_pci_mbox_send_cmd() - Execute a mailbox command
27971 * @cxlm: The CXL memory device to communicate with.
28072 * @mbox_cmd: Command to send to the memory device.
28173 *
@@ -296,10 +88,11 @@ static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm, · 14 unmodified lines
29688 * not need to coordinate with each other. The driver only uses the primary
29789 * mailbox.
29890 */
299−static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm,
300− struct mbox_cmd *mbox_cmd)
91+static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
92+ struct cxl_mbox_cmd *mbox_cmd)
30193 {
30294 void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
95+ struct device *dev = cxlm->dev;
30396 u64 cmd_reg, status_reg;
30497 size_t out_len;
30598 int rc;
@@ -325,8 +118,7 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 19 unmodified lines
325118
326119 /* #1 */
327120 if (cxl_doorbell_busy(cxlm)) {
328− dev_err_ratelimited(&cxlm->pdev->dev,
329− "Mailbox re-busy after acquiring\n");
121+ dev_err_ratelimited(dev, "Mailbox re-busy after acquiring\n");
330122 return -EBUSY;
331123 }
332124
@@ -345,14 +137,14 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 12 unmodified lines
345137 writeq(cmd_reg, cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
346138
347139 /* #4 */
348− dev_dbg(&cxlm->pdev->dev, "Sending command\n");
140+ dev_dbg(dev, "Sending command\n");
349141 writel(CXLDEV_MBOX_CTRL_DOORBELL,
350142 cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
351143
352144 /* #5 */
353− rc = cxl_mem_wait_for_doorbell(cxlm);
145+ rc = cxl_pci_mbox_wait_for_doorbell(cxlm);
354146 if (rc == -ETIMEDOUT) {
355− cxl_mem_mbox_timeout(cxlm, mbox_cmd);
147+ cxl_pci_mbox_timeout(cxlm, mbox_cmd);
356148 return rc;
357149 }
358150
@@ -362,7 +154,7 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 3 unmodified lines
362154 FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
363155
364156 if (mbox_cmd->return_code != 0) {
365− dev_dbg(&cxlm->pdev->dev, "Mailbox operation had an error\n");
157+ dev_dbg(dev, "Mailbox operation had an error\n");
366158 return 0;
367159 }
368160
@@ -391,15 +183,15 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 22 unmodified lines
391183 }
392184
393185 /**
394− * cxl_mem_mbox_get() - Acquire exclusive access to the mailbox.
186+ * cxl_pci_mbox_get() - Acquire exclusive access to the mailbox.
395187 * @cxlm: The memory device to gain access to.
396188 *
397189 * Context: Any context. Takes the mbox_mutex.
398190 * Return: 0 if exclusive access was acquired.
399191 */
400−static int cxl_mem_mbox_get(struct cxl_mem *cxlm)
192+static int cxl_pci_mbox_get(struct cxl_mem *cxlm)
401193 {
402− struct device *dev = &cxlm->pdev->dev;
194+ struct device *dev = cxlm->dev;
403195 u64 md_status;
404196 int rc;
405197
@@ -422,7 +214,7 @@ static int cxl_mem_mbox_get(struct cxl_mem *cxlm) · 16 unmodified lines
422214 * Mailbox Interface Ready bit. Therefore, waiting for the doorbell
423215 * to be ready is sufficient.
424216 */
425− rc = cxl_mem_wait_for_doorbell(cxlm);
217+ rc = cxl_pci_mbox_wait_for_doorbell(cxlm);
426218 if (rc) {
427219 dev_warn(dev, "Mailbox interface not ready\n");
428220 goto out;
@@ -462,457 +254,35 @@ out: · 33 unmodified lines
462254 }
463255
464256 /**
465− * cxl_mem_mbox_put() - Release exclusive access to the mailbox.
257+ * cxl_pci_mbox_put() - Release exclusive access to the mailbox.
466258 * @cxlm: The CXL memory device to communicate with.
467259 *
468260 * Context: Any context. Expects mbox_mutex to be held.
469261 */
470−static void cxl_mem_mbox_put(struct cxl_mem *cxlm)
262+static void cxl_pci_mbox_put(struct cxl_mem *cxlm)
471263 {
472264 mutex_unlock(&cxlm->mbox_mutex);
473265 }
474266
475−/**
476− * handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace.
477− * @cxlm: The CXL memory device to communicate with.
478− * @cmd: The validated command.
479− * @in_payload: Pointer to userspace's input payload.
480− * @out_payload: Pointer to userspace's output payload.
481− * @size_out: (Input) Max payload size to copy out.
482− * (Output) Payload size hardware generated.
483− * @retval: Hardware generated return code from the operation.
484− *
485− * Return:
486− * * %0 - Mailbox transaction succeeded. This implies the mailbox
487− * protocol completed successfully not that the operation itself
488− * was successful.
489− * * %-ENOMEM - Couldn't allocate a bounce buffer.
490− * * %-EFAULT - Something happened with copy_to/from_user.
491− * * %-EINTR - Mailbox acquisition interrupted.
492− * * %-EXXX - Transaction level failures.
493− *
494− * Creates the appropriate mailbox command and dispatches it on behalf of a
495− * userspace request. The input and output payloads are copied between
496− * userspace.
497− *
498− * See cxl_send_cmd().
499− */
500−static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm,
501− const struct cxl_mem_command *cmd,
502− u64 in_payload, u64 out_payload,
503− s32 *size_out, u32 *retval)
504−{
505− struct device *dev = &cxlm->pdev->dev;
506− struct mbox_cmd mbox_cmd = {
507− .opcode = cmd->opcode,
508− .size_in = cmd->info.size_in,
509− .size_out = cmd->info.size_out,
510− };
511− int rc;
512−
513− if (cmd->info.size_out) {
514− mbox_cmd.payload_out = kvzalloc(cmd->info.size_out, GFP_KERNEL);
515− if (!mbox_cmd.payload_out)
516− return -ENOMEM;
517− }
518−
519− if (cmd->info.size_in) {
520− mbox_cmd.payload_in = vmemdup_user(u64_to_user_ptr(in_payload),
521− cmd->info.size_in);
522− if (IS_ERR(mbox_cmd.payload_in)) {
523− kvfree(mbox_cmd.payload_out);
524− return PTR_ERR(mbox_cmd.payload_in);
525− }
526− }
527−
528− rc = cxl_mem_mbox_get(cxlm);
529− if (rc)
530− goto out;
531−
532− dev_dbg(dev,
533− "Submitting %s command for user\n"
534− "\topcode: %x\n"
535− "\tsize: %ub\n",
536− cxl_command_names[cmd->info.id].name, mbox_cmd.opcode,
537− cmd->info.size_in);
538−
539− dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW,
540− "raw command path used\n");
541−
542− rc = __cxl_mem_mbox_send_cmd(cxlm, &mbox_cmd);
543− cxl_mem_mbox_put(cxlm);
544− if (rc)
545− goto out;
546−
547− /*
548− * @size_out contains the max size that's allowed to be written back out
549− * to userspace. While the payload may have written more output than
550− * this it will have to be ignored.
551− */
552− if (mbox_cmd.size_out) {
553− dev_WARN_ONCE(dev, mbox_cmd.size_out > *size_out,
554− "Invalid return size\n");
555− if (copy_to_user(u64_to_user_ptr(out_payload),
556− mbox_cmd.payload_out, mbox_cmd.size_out)) {
557− rc = -EFAULT;
558− goto out;
559− }
560− }
561−
562− *size_out = mbox_cmd.size_out;
563− *retval = mbox_cmd.return_code;
564−
565−out:
566− kvfree(mbox_cmd.payload_in);
567− kvfree(mbox_cmd.payload_out);
568− return rc;
569−}
570−
571−static bool cxl_mem_raw_command_allowed(u16 opcode)
572−{
573− int i;
574−
575− if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS))
576− return false;
577−
578− if (security_locked_down(LOCKDOWN_PCI_ACCESS))
579− return false;
580−
581− if (cxl_raw_allow_all)
582− return true;
583−
584− if (cxl_is_security_command(opcode))
585− return false;
586−
587− for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++)
588− if (cxl_disabled_raw_commands[i] == opcode)
589− return false;
590−
591− return true;
592−}
593−
594−/**
595− * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
596− * @cxlm: &struct cxl_mem device whose mailbox will be used.
597− * @send_cmd: &struct cxl_send_command copied in from userspace.
598− * @out_cmd: Sanitized and populated &struct cxl_mem_command.
599− *
600− * Return:
601− * * %0 - @out_cmd is ready to send.
602− * * %-ENOTTY - Invalid command specified.
603− * * %-EINVAL - Reserved fields or invalid values were used.
604− * * %-ENOMEM - Input or output buffer wasn't sized properly.
605− * * %-EPERM - Attempted to use a protected command.
606− *
607− * The result of this command is a fully validated command in @out_cmd that is
608− * safe to send to the hardware.
609− *
610− * See handle_mailbox_cmd_from_user()
611− */
612−static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
613− const struct cxl_send_command *send_cmd,
614− struct cxl_mem_command *out_cmd)
615−{
616− const struct cxl_command_info *info;
617− struct cxl_mem_command *c;
618−
619− if (send_cmd->id == 0 || send_cmd->id >= CXL_MEM_COMMAND_ID_MAX)
620− return -ENOTTY;
621−
622− /*
623− * The user can never specify an input payload larger than what hardware
624− * supports, but output can be arbitrarily large (simply write out as
625− * much data as the hardware provides).
626− */
627− if (send_cmd->in.size > cxlm->payload_size)
628− return -EINVAL;
629−
630− /*
631− * Checks are bypassed for raw commands but a WARN/taint will occur
632− * later in the callchain
633− */
634− if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) {
635− const struct cxl_mem_command temp = {
636− .info = {
637− .id = CXL_MEM_COMMAND_ID_RAW,
638− .flags = 0,
639− .size_in = send_cmd->in.size,
640− .size_out = send_cmd->out.size,
641− },
642− .opcode = send_cmd->raw.opcode
643− };
644−
645− if (send_cmd->raw.rsvd)
646− return -EINVAL;
647−
648− /*
649− * Unlike supported commands, the output size of RAW commands
650− * gets passed along without further checking, so it must be
651− * validated here.
652− */
653− if (send_cmd->out.size > cxlm->payload_size)
654− return -EINVAL;
655−
656− if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
657− return -EPERM;
658−
659− memcpy(out_cmd, &temp, sizeof(temp));
660−
661− return 0;
662− }
663−
664− if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK)
665− return -EINVAL;
666−
667− if (send_cmd->rsvd)
668− return -EINVAL;
669−
670− if (send_cmd->in.rsvd || send_cmd->out.rsvd)
671− return -EINVAL;
672−
673− /* Convert user's command into the internal representation */
674− c = &mem_commands[send_cmd->id];
675− info = &c->info;
676−
677− /* Check that the command is enabled for hardware */
678− if (!test_bit(info->id, cxlm->enabled_cmds))
679− return -ENOTTY;
680−
681− /* Check the input buffer is the expected size */
682− if (info->size_in >= 0 && info->size_in != send_cmd->in.size)
683− return -ENOMEM;
684−
685− /* Check the output buffer is at least large enough */
686− if (info->size_out >= 0 && send_cmd->out.size < info->size_out)
687− return -ENOMEM;
688−
689− memcpy(out_cmd, c, sizeof(*c));
690− out_cmd->info.size_in = send_cmd->in.size;
691− /*
692− * XXX: out_cmd->info.size_out will be controlled by the driver, and the
693− * specified number of bytes @send_cmd->out.size will be copied back out
694− * to userspace.
695− */
696−
697− return 0;
698−}
699−
700−static int cxl_query_cmd(struct cxl_memdev *cxlmd,
701− struct cxl_mem_query_commands __user *q)
267+static int cxl_pci_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
702268 {
703− struct device *dev = &cxlmd->dev;
704− struct cxl_mem_command *cmd;
705− u32 n_commands;
706− int j = 0;
707−
708− dev_dbg(dev, "Query IOCTL\n");
709−
710− if (get_user(n_commands, &q->n_commands))
711− return -EFAULT;
712−
713− /* returns the total number if 0 elements are requested. */
714− if (n_commands == 0)
715− return put_user(cxl_cmd_count, &q->n_commands);
716−
717− /*
718− * otherwise, return max(n_commands, total commands) cxl_command_info
719− * structures.
720− */
721− cxl_for_each_cmd(cmd) {
722− const struct cxl_command_info *info = &cmd->info;
723−
724− if (copy_to_user(&q->commands[j++], info, sizeof(*info)))
725− return -EFAULT;
726−
727− if (j == n_commands)
728− break;
729− }
730−
731− return 0;
732−}
733−
734−static int cxl_send_cmd(struct cxl_memdev *cxlmd,
735− struct cxl_send_command __user *s)
736−{
737− struct cxl_mem *cxlm = cxlmd->cxlm;
738− struct device *dev = &cxlmd->dev;
739− struct cxl_send_command send;
740− struct cxl_mem_command c;
741269 int rc;
742270
743− dev_dbg(dev, "Send IOCTL\n");
744−
745− if (copy_from_user(&send, s, sizeof(send)))
746− return -EFAULT;
747−
748− rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c);
749− if (rc)
750− return rc;
751−
752− /* Prepare to handle a full payload for variable sized output */
753− if (c.info.size_out < 0)
754− c.info.size_out = cxlm->payload_size;
755−
756− rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload,
757− send.out.payload, &send.out.size,
758− &send.retval);
271+ rc = cxl_pci_mbox_get(cxlm);
759272 if (rc)
760273 return rc;
761274
762− if (copy_to_user(s, &send, sizeof(send)))
763− return -EFAULT;
764−
765− return 0;
766−}
767−
768−static long __cxl_memdev_ioctl(struct cxl_memdev *cxlmd, unsigned int cmd,
769− unsigned long arg)
770−{
771− switch (cmd) {
772− case CXL_MEM_QUERY_COMMANDS:
773− return cxl_query_cmd(cxlmd, (void __user *)arg);
774− case CXL_MEM_SEND_COMMAND:
775− return cxl_send_cmd(cxlmd, (void __user *)arg);
776− default:
777− return -ENOTTY;
778− }
779−}
780−
781−static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
782− unsigned long arg)
783−{
784− struct cxl_memdev *cxlmd = file->private_data;
785− int rc = -ENXIO;
786−
787− down_read(&cxl_memdev_rwsem);
788− if (cxlmd->cxlm)
789− rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
790− up_read(&cxl_memdev_rwsem);
275+ rc = __cxl_pci_mbox_send_cmd(cxlm, cmd);
276+ cxl_pci_mbox_put(cxlm);
791277
792278 return rc;
793279 }
794280
795−static int cxl_memdev_open(struct inode *inode, struct file *file)
796−{
797− struct cxl_memdev *cxlmd =
798− container_of(inode->i_cdev, typeof(*cxlmd), cdev);
799−
800− get_device(&cxlmd->dev);
801− file->private_data = cxlmd;
802−
803− return 0;
804−}
805−
806−static int cxl_memdev_release_file(struct inode *inode, struct file *file)
807−{
808− struct cxl_memdev *cxlmd =
809− container_of(inode->i_cdev, typeof(*cxlmd), cdev);
810−
811− put_device(&cxlmd->dev);
812−
813− return 0;
814−}
815−
816−static void cxl_memdev_shutdown(struct device *dev)
817−{
818− struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
819−
820− down_write(&cxl_memdev_rwsem);
821− cxlmd->cxlm = NULL;
822− up_write(&cxl_memdev_rwsem);
823−}
824−
825−static const struct cdevm_file_operations cxl_memdev_fops = {
826− .fops = {
827− .owner = THIS_MODULE,
828− .unlocked_ioctl = cxl_memdev_ioctl,
829− .open = cxl_memdev_open,
830− .release = cxl_memdev_release_file,
831− .compat_ioctl = compat_ptr_ioctl,
832− .llseek = noop_llseek,
833− },
834− .shutdown = cxl_memdev_shutdown,
835−};
836−
837−static inline struct cxl_mem_command *cxl_mem_find_command(u16 opcode)
838−{
839− struct cxl_mem_command *c;
840−
841− cxl_for_each_cmd(c)
842− if (c->opcode == opcode)
843− return c;
844−
845− return NULL;
846−}
847−
848−/**
849− * cxl_mem_mbox_send_cmd() - Send a mailbox command to a memory device.
850− * @cxlm: The CXL memory device to communicate with.
851− * @opcode: Opcode for the mailbox command.
852− * @in: The input payload for the mailbox command.
853− * @in_size: The length of the input payload
854− * @out: Caller allocated buffer for the output.
855− * @out_size: Expected size of output.
856− *
857− * Context: Any context. Will acquire and release mbox_mutex.
858− * Return:
859− * * %>=0 - Number of bytes returned in @out.
860− * * %-E2BIG - Payload is too large for hardware.
861− * * %-EBUSY - Couldn't acquire exclusive mailbox access.
862− * * %-EFAULT - Hardware error occurred.
863− * * %-ENXIO - Command completed, but device reported an error.
864− * * %-EIO - Unexpected output size.
865− *
866− * Mailbox commands may execute successfully yet the device itself reported an
867− * error. While this distinction can be useful for commands from userspace, the
868− * kernel will only be able to use results when both are successful.
869− *
870− * See __cxl_mem_mbox_send_cmd()
871− */
872−static int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode,
873− void *in, size_t in_size,
874− void *out, size_t out_size)
875−{
876− const struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
877− struct mbox_cmd mbox_cmd = {
878− .opcode = opcode,
879− .payload_in = in,
880− .size_in = in_size,
881− .size_out = out_size,
882− .payload_out = out,
883− };
884− int rc;
885−
886− if (out_size > cxlm->payload_size)
887− return -E2BIG;
888−
889− rc = cxl_mem_mbox_get(cxlm);
890− if (rc)
891− return rc;
892−
893− rc = __cxl_mem_mbox_send_cmd(cxlm, &mbox_cmd);
894− cxl_mem_mbox_put(cxlm);
895− if (rc)
896− return rc;
897−
898− /* TODO: Map return code to proper kernel style errno */
899− if (mbox_cmd.return_code != CXL_MBOX_SUCCESS)
900− return -ENXIO;
901−
902− /*
903− * Variable sized commands can't be validated and so it's up to the
904− * caller to do that if they wish.
905− */
906− if (cmd->info.size_out >= 0 && mbox_cmd.size_out != out_size)
907− return -EIO;
908−
909− return 0;
910−}
911−
912−static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
281+static int cxl_pci_setup_mailbox(struct cxl_mem *cxlm)
913282 {
914283 const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
915284
285+ cxlm->mbox_send = cxl_pci_mbox_send;
916286 cxlm->payload_size =
917287 1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap);
918288
@@ -925,103 +295,57 @@ static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm) · 6 unmodified lines
925295 */
926296 cxlm->payload_size = min_t(size_t, cxlm->payload_size, SZ_1M);
927297 if (cxlm->payload_size < 256) {
928− dev_err(&cxlm->pdev->dev, "Mailbox is too small (%zub)",
298+ dev_err(cxlm->dev, "Mailbox is too small (%zub)",
929299 cxlm->payload_size);
930300 return -ENXIO;
931301 }
932302
933− dev_dbg(&cxlm->pdev->dev, "Mailbox payload sized %zu",
303+ dev_dbg(cxlm->dev, "Mailbox payload sized %zu",
934304 cxlm->payload_size);
935305
936306 return 0;
937307 }
938308
939−static struct cxl_mem *cxl_mem_create(struct pci_dev *pdev)
940−{
941− struct device *dev = &pdev->dev;
942− struct cxl_mem *cxlm;
943−
944− cxlm = devm_kzalloc(dev, sizeof(*cxlm), GFP_KERNEL);
945− if (!cxlm) {
946− dev_err(dev, "No memory available\n");
947− return ERR_PTR(-ENOMEM);
948− }
949−
950− mutex_init(&cxlm->mbox_mutex);
951− cxlm->pdev = pdev;
952− cxlm->enabled_cmds =
953− devm_kmalloc_array(dev, BITS_TO_LONGS(cxl_cmd_count),
954− sizeof(unsigned long),
955− GFP_KERNEL | __GFP_ZERO);
956− if (!cxlm->enabled_cmds) {
957− dev_err(dev, "No memory available for bitmap\n");
958− return ERR_PTR(-ENOMEM);
959− }
960−
961− return cxlm;
962−}
963−
964−static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
965− u8 bar, u64 offset)
309+static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
966310 {
967− struct pci_dev *pdev = cxlm->pdev;
968− struct device *dev = &pdev->dev;
969311 void __iomem *addr;
312+ int bar = map->barno;
313+ struct device *dev = &pdev->dev;
314+ resource_size_t offset = map->block_offset;
970315
971316 /* Basic sanity check that BAR is big enough */
972317 if (pci_resource_len(pdev, bar) < offset) {
973− dev_err(dev, "BAR%d: %pr: too small (offset: %#llx)\n", bar,
974− &pdev->resource[bar], (unsigned long long)offset);
975− return IOMEM_ERR_PTR(-ENXIO);
318+ dev_err(dev, "BAR%d: %pr: too small (offset: %pa)\n", bar,
319+ &pdev->resource[bar], &offset);
320+ return -ENXIO;
976321 }
977322
978323 addr = pci_iomap(pdev, bar, 0);
979324 if (!addr) {
980325 dev_err(dev, "failed to map registers\n");
981− return addr;
326+ return -ENOMEM;
982327 }
983328
984− dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %#llx\n",
985− bar, offset);
329+ dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %pa\n",
330+ bar, &offset);
986331
987− return addr;
332+ map->base = addr + map->block_offset;
333+ return 0;
988334 }
989335
990−static void cxl_mem_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
336+static void cxl_unmap_regblock(struct pci_dev *pdev,
337+ struct cxl_register_map *map)
991338 {
992− pci_iounmap(cxlm->pdev, base);
339+ pci_iounmap(pdev, map->base - map->block_offset);
340+ map->base = NULL;
993341 }
994342
995−static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)
343+static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
996344 {
997− int pos;
998−
999− pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DVSEC);
1000− if (!pos)
1001− return 0;
1002−
1003− while (pos) {
1004− u16 vendor, id;
1005−
1006− pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vendor);
1007− pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, &id);
1008− if (vendor == PCI_DVSEC_VENDOR_ID_CXL && dvsec == id)
1009− return pos;
1010−
1011− pos = pci_find_next_ext_capability(pdev, pos,
1012− PCI_EXT_CAP_ID_DVSEC);
1013− }
1014−
1015− return 0;
1016−}
1017−
1018−static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
1019− struct cxl_register_map *map)
1020−{
1021− struct pci_dev *pdev = cxlm->pdev;
1022− struct device *dev = &pdev->dev;
1023345 struct cxl_component_reg_map *comp_map;
1024346 struct cxl_device_reg_map *dev_map;
347+ struct device *dev = &pdev->dev;
348+ void __iomem *base = map->base;
1025349
1026350 switch (map->reg_type) {
1027351 case CXL_REGLOC_RBI_COMPONENT:
@@ -1057,8 +381,8 @@ static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base, · 29 unmodified lines
1057381
1058382 static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
1059383 {
1060− struct pci_dev *pdev = cxlm->pdev;
1061− struct device *dev = &pdev->dev;
384+ struct device *dev = cxlm->dev;
385+ struct pci_dev *pdev = to_pci_dev(dev);
1062386
1063387 switch (map->reg_type) {
1064388 case CXL_REGLOC_RBI_COMPONENT:
@@ -1076,426 +400,108 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map) · 11 unmodified lines
1076400 return 0;
1077401 }
1078402
1079−static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
1080− u8 *bar, u64 *offset, u8 *reg_type)
403+static void cxl_decode_regblock(u32 reg_lo, u32 reg_hi,
404+ struct cxl_register_map *map)
1081405 {
1082− *offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
1083− *bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
1084− *reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
406+ map->block_offset =
407+ ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
408+ map->barno = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
409+ map->reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
1085410 }
1086411
1087412 /**
1088− * cxl_mem_setup_regs() - Setup necessary MMIO.
1089− * @cxlm: The CXL memory device to communicate with.
413+ * cxl_find_regblock() - Locate register blocks by type
414+ * @pdev: The CXL PCI device to enumerate.
415+ * @type: Register Block Indicator id
416+ * @map: Enumeration output, clobbered on error
1090417 *
1091− * Return: 0 if all necessary registers mapped.
418+ * Return: 0 if register block enumerated, negative error code otherwise
1092419 *
1093− * A memory device is required by spec to implement a certain set of MMIO
1094− * regions. The purpose of this function is to enumerate and map those
1095− * registers.
420+ * A CXL DVSEC may point to one or more register blocks, search for them
421+ * by @type.
1096422 */
1097−static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
423+static int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
424+ struct cxl_register_map *map)
1098425 {
1099− struct pci_dev *pdev = cxlm->pdev;
1100− struct device *dev = &pdev->dev;
1101426 u32 regloc_size, regblocks;
1102− void __iomem *base;
1103− int regloc, i, n_maps;
1104− struct cxl_register_map *map, maps[CXL_REGLOC_RBI_TYPES];
1105− int ret = 0;
1106−
1107− regloc = cxl_mem_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
1108− if (!regloc) {
1109− dev_err(dev, "register location dvsec not found\n");
1110− return -ENXIO;
1111− }
427+ int regloc, i;
1112428
1113− if (pci_request_mem_regions(pdev, pci_name(pdev)))
1114− return -ENODEV;
429+ regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
430+ PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
431+ if (!regloc)
432+ return -ENXIO;
1115433
1116− /* Get the size of the Register Locator DVSEC */
1117434 pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
1118435 regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
1119436
1120437 regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;
1121438 regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;
1122439
1123− for (i = 0, n_maps = 0; i < regblocks; i++, regloc += 8) {
440+ for (i = 0; i < regblocks; i++, regloc += 8) {
1124441 u32 reg_lo, reg_hi;
1125− u8 reg_type;
1126− u64 offset;
1127− u8 bar;
1128442
1129443 pci_read_config_dword(pdev, regloc, &reg_lo);
1130444 pci_read_config_dword(pdev, regloc + 4, &reg_hi);
1131445
1132− cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset,
1133− &reg_type);
1134−
1135− dev_dbg(dev, "Found register block in bar %u @ 0x%llx of type %u\n",
1136− bar, offset, reg_type);
1137−
1138− /* Ignore unknown register block types */
1139− if (reg_type > CXL_REGLOC_RBI_MEMDEV)
1140− continue;
1141−
1142− base = cxl_mem_map_regblock(cxlm, bar, offset);
1143− if (!base)
1144− return -ENOMEM;
1145−
1146− map = &maps[n_maps];
1147− map->barno = bar;
1148− map->block_offset = offset;
1149− map->reg_type = reg_type;
1150−
1151− ret = cxl_probe_regs(cxlm, base + offset, map);
1152−
1153− /* Always unmap the regblock regardless of probe success */
1154− cxl_mem_unmap_regblock(cxlm, base);
1155−
1156− if (ret)
1157− return ret;
1158−
1159− n_maps++;
1160− }
1161−
1162− pci_release_mem_regions(pdev);
1163−
1164− for (i = 0; i < n_maps; i++) {
1165− ret = cxl_map_regs(cxlm, &maps[i]);
1166− if (ret)
1167− break;
1168− }
1169−
1170− return ret;
1171−}
1172−
1173−static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
1174−{
1175− u32 remaining = size;
1176− u32 offset = 0;
1177−
1178− while (remaining) {
1179− u32 xfer_size = min_t(u32, remaining, cxlm->payload_size);
1180− struct cxl_mbox_get_log {
1181− uuid_t uuid;
1182− __le32 offset;
1183− __le32 length;
1184− } __packed log = {
1185− .uuid = *uuid,
1186− .offset = cpu_to_le32(offset),
1187− .length = cpu_to_le32(xfer_size)
1188− };
1189− int rc;
1190−
1191− rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LOG, &log,
1192− sizeof(log), out, xfer_size);
1193− if (rc < 0)
1194− return rc;
1195−
1196− out += xfer_size;
1197− remaining -= xfer_size;
1198− offset += xfer_size;
1199− }
1200−
1201− return 0;
1202−}
1203−
1204−/**
1205− * cxl_walk_cel() - Walk through the Command Effects Log.
1206− * @cxlm: Device.
1207− * @size: Length of the Command Effects Log.
1208− * @cel: CEL
1209− *
1210− * Iterate over each entry in the CEL and determine if the driver supports the
1211− * command. If so, the command is enabled for the device and can be used later.
1212− */
1213−static void cxl_walk_cel(struct cxl_mem *cxlm, size_t size, u8 *cel)
1214−{
1215− struct cel_entry {
1216− __le16 opcode;
1217− __le16 effect;
1218− } __packed * cel_entry;
1219− const int cel_entries = size / sizeof(*cel_entry);
1220− int i;
1221−
1222− cel_entry = (struct cel_entry *)cel;
1223−
1224− for (i = 0; i < cel_entries; i++) {
1225− u16 opcode = le16_to_cpu(cel_entry[i].opcode);
1226− struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
1227−
1228− if (!cmd) {
1229− dev_dbg(&cxlm->pdev->dev,
1230− "Opcode 0x%04x unsupported by driver", opcode);
1231− continue;
1232− }
1233−
1234− set_bit(cmd->info.id, cxlm->enabled_cmds);
1235− }
1236−}
1237−
1238−struct cxl_mbox_get_supported_logs {
1239− __le16 entries;
1240− u8 rsvd[6];
1241− struct gsl_entry {
1242− uuid_t uuid;
1243− __le32 size;
1244− } __packed entry[];
1245−} __packed;
1246−
1247−static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_mem *cxlm)
1248−{
1249− struct cxl_mbox_get_supported_logs *ret;
1250− int rc;
446+ cxl_decode_regblock(reg_lo, reg_hi, map);
1251447
1252− ret = kvmalloc(cxlm->payload_size, GFP_KERNEL);
1253− if (!ret)
1254− return ERR_PTR(-ENOMEM);
1255−
1256− rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL,
1257− 0, ret, cxlm->payload_size);
1258− if (rc < 0) {
1259− kvfree(ret);
1260− return ERR_PTR(rc);
448+ if (map->reg_type == type)
449+ return 0;
1261450 }
1262451
1263− return ret;
452+ return -ENODEV;
1264453 }
1265454
1266−/**
1267− * cxl_mem_get_partition_info - Get partition info
1268− * @cxlm: The device to act on
1269− * @active_volatile_bytes: returned active volatile capacity
1270− * @active_persistent_bytes: returned active persistent capacity
1271− * @next_volatile_bytes: return next volatile capacity
1272− * @next_persistent_bytes: return next persistent capacity
1273− *
1274− * Retrieve the current partition info for the device specified. If not 0, the
1275− * 'next' values are pending and take affect on next cold reset.
1276− *
1277− * Return: 0 if no error: or the result of the mailbox command.
1278− *
1279− * See CXL @8.2.9.5.2.1 Get Partition Info
1280− */
1281−static int cxl_mem_get_partition_info(struct cxl_mem *cxlm,
1282− u64 *active_volatile_bytes,
1283− u64 *active_persistent_bytes,
1284− u64 *next_volatile_bytes,
1285− u64 *next_persistent_bytes)
455+static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
456+ struct cxl_register_map *map)
1286457 {
1287− struct cxl_mbox_get_partition_info {
1288− __le64 active_volatile_cap;
1289− __le64 active_persistent_cap;
1290− __le64 next_volatile_cap;
1291− __le64 next_persistent_cap;
1292− } __packed pi;
1293458 int rc;
1294459
1295− rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO,
1296− NULL, 0, &pi, sizeof(pi));
460+ rc = cxl_find_regblock(pdev, type, map);
1297461 if (rc)
1298462 return rc;
1299463
1300− *active_volatile_bytes = le64_to_cpu(pi.active_volatile_cap);
1301− *active_persistent_bytes = le64_to_cpu(pi.active_persistent_cap);
1302− *next_volatile_bytes = le64_to_cpu(pi.next_volatile_cap);
1303− *next_persistent_bytes = le64_to_cpu(pi.next_volatile_cap);
1304−
1305− *active_volatile_bytes *= CXL_CAPACITY_MULTIPLIER;
1306− *active_persistent_bytes *= CXL_CAPACITY_MULTIPLIER;
1307− *next_volatile_bytes *= CXL_CAPACITY_MULTIPLIER;
1308− *next_persistent_bytes *= CXL_CAPACITY_MULTIPLIER;
1309−
1310− return 0;
1311−}
1312−
1313−/**
1314− * cxl_mem_enumerate_cmds() - Enumerate commands for a device.
1315− * @cxlm: The device.
1316− *
1317− * Returns 0 if enumerate completed successfully.
1318− *
1319− * CXL devices have optional support for certain commands. This function will
1320− * determine the set of supported commands for the hardware and update the
1321− * enabled_cmds bitmap in the @cxlm.
1322− */
1323−static int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm)
1324−{
1325− struct cxl_mbox_get_supported_logs *gsl;
1326− struct device *dev = &cxlm->pdev->dev;
1327− struct cxl_mem_command *cmd;
1328− int i, rc;
1329−
1330− gsl = cxl_get_gsl(cxlm);
1331− if (IS_ERR(gsl))
1332− return PTR_ERR(gsl);
1333−
1334− rc = -ENOENT;
1335− for (i = 0; i < le16_to_cpu(gsl->entries); i++) {
1336− u32 size = le32_to_cpu(gsl->entry[i].size);
1337− uuid_t uuid = gsl->entry[i].uuid;
1338− u8 *log;
1339−
1340− dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size);
1341−
1342− if (!uuid_equal(&uuid, &log_uuid[CEL_UUID]))
1343− continue;
1344−
1345− log = kvmalloc(size, GFP_KERNEL);
1346− if (!log) {
1347− rc = -ENOMEM;
1348− goto out;
1349− }
1350−
1351− rc = cxl_xfer_log(cxlm, &uuid, size, log);
1352− if (rc) {
1353− kvfree(log);
1354− goto out;
1355− }
1356−
1357− cxl_walk_cel(cxlm, size, log);
1358− kvfree(log);
1359−
1360− /* In case CEL was bogus, enable some default commands. */
1361− cxl_for_each_cmd(cmd)
1362− if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE)
1363− set_bit(cmd->info.id, cxlm->enabled_cmds);
1364−
1365− /* Found the required CEL */
1366− rc = 0;
1367− }
1368−
1369−out:
1370− kvfree(gsl);
1371− return rc;
1372−}
1373−
1374−/**
1375− * cxl_mem_identify() - Send the IDENTIFY command to the device.
1376− * @cxlm: The device to identify.
1377− *
1378− * Return: 0 if identify was executed successfully.
1379− *
1380− * This will dispatch the identify command to the device and on success populate
1381− * structures to be exported to sysfs.
1382− */
1383−static int cxl_mem_identify(struct cxl_mem *cxlm)
1384−{
1385− /* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
1386− struct cxl_mbox_identify {
1387− char fw_revision[0x10];
1388− __le64 total_capacity;
1389− __le64 volatile_capacity;
1390− __le64 persistent_capacity;
1391− __le64 partition_align;
1392− __le16 info_event_log_size;
1393− __le16 warning_event_log_size;
1394− __le16 failure_event_log_size;
1395− __le16 fatal_event_log_size;
1396− __le32 lsa_size;
1397− u8 poison_list_max_mer[3];
1398− __le16 inject_poison_limit;
1399− u8 poison_caps;
1400− u8 qos_telemetry_caps;
1401− } __packed id;
1402− int rc;
1403−
1404− rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id,
1405− sizeof(id));
1406− if (rc < 0)
1407− return rc;
1408−
1409− cxlm->total_bytes = le64_to_cpu(id.total_capacity);
1410− cxlm->total_bytes *= CXL_CAPACITY_MULTIPLIER;
1411−
1412− cxlm->volatile_only_bytes = le64_to_cpu(id.volatile_capacity);
1413− cxlm->volatile_only_bytes *= CXL_CAPACITY_MULTIPLIER;
1414−
1415− cxlm->persistent_only_bytes = le64_to_cpu(id.persistent_capacity);
1416− cxlm->persistent_only_bytes *= CXL_CAPACITY_MULTIPLIER;
1417−
1418− cxlm->partition_align_bytes = le64_to_cpu(id.partition_align);
1419− cxlm->partition_align_bytes *= CXL_CAPACITY_MULTIPLIER;
1420−
1421− dev_dbg(&cxlm->pdev->dev, "Identify Memory Device\n"
1422− " total_bytes = %#llx\n"
1423− " volatile_only_bytes = %#llx\n"
1424− " persistent_only_bytes = %#llx\n"
1425− " partition_align_bytes = %#llx\n",
1426− cxlm->total_bytes,
1427− cxlm->volatile_only_bytes,
1428− cxlm->persistent_only_bytes,
1429− cxlm->partition_align_bytes);
1430−
1431− cxlm->lsa_size = le32_to_cpu(id.lsa_size);
1432− memcpy(cxlm->firmware_version, id.fw_revision, sizeof(id.fw_revision));
1433−
1434− return 0;
1435−}
1436−
1437−static int cxl_mem_create_range_info(struct cxl_mem *cxlm)
1438−{
1439− int rc;
1440−
1441− if (cxlm->partition_align_bytes == 0) {
1442− cxlm->ram_range.start = 0;
1443− cxlm->ram_range.end = cxlm->volatile_only_bytes - 1;
1444− cxlm->pmem_range.start = cxlm->volatile_only_bytes;
1445− cxlm->pmem_range.end = cxlm->volatile_only_bytes +
1446− cxlm->persistent_only_bytes - 1;
1447− return 0;
1448− }
1449−
1450− rc = cxl_mem_get_partition_info(cxlm,
1451− &cxlm->active_volatile_bytes,
1452− &cxlm->active_persistent_bytes,
1453− &cxlm->next_volatile_bytes,
1454− &cxlm->next_persistent_bytes);
1455− if (rc < 0) {
1456− dev_err(&cxlm->pdev->dev, "Failed to query partition information\n");
464+ rc = cxl_map_regblock(pdev, map);
465+ if (rc)
1457466 return rc;
1458− }
1459−
1460− dev_dbg(&cxlm->pdev->dev, "Get Partition Info\n"
1461− " active_volatile_bytes = %#llx\n"
1462− " active_persistent_bytes = %#llx\n"
1463− " next_volatile_bytes = %#llx\n"
1464− " next_persistent_bytes = %#llx\n",
1465− cxlm->active_volatile_bytes,
1466− cxlm->active_persistent_bytes,
1467− cxlm->next_volatile_bytes,
1468− cxlm->next_persistent_bytes);
1469467
1470− cxlm->ram_range.start = 0;
1471− cxlm->ram_range.end = cxlm->active_volatile_bytes - 1;
468+ rc = cxl_probe_regs(pdev, map);
469+ cxl_unmap_regblock(pdev, map);
1472470
1473− cxlm->pmem_range.start = cxlm->active_volatile_bytes;
1474− cxlm->pmem_range.end = cxlm->active_volatile_bytes +
1475− cxlm->active_persistent_bytes - 1;
1476−
1477− return 0;
471+ return rc;
1478472 }
1479473
1480−static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
474+static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
1481475 {
476+ struct cxl_register_map map;
1482477 struct cxl_memdev *cxlmd;
1483478 struct cxl_mem *cxlm;
1484479 int rc;
1485480
481+ /*
482+ * Double check the anonymous union trickery in struct cxl_regs
483+ * FIXME switch to struct_group()
484+ */
485+ BUILD_BUG_ON(offsetof(struct cxl_regs, memdev) !=
486+ offsetof(struct cxl_regs, device_regs.memdev));
487+
1486488 rc = pcim_enable_device(pdev);
1487489 if (rc)
1488490 return rc;
1489491
1490− cxlm = cxl_mem_create(pdev);
492+ cxlm = cxl_mem_create(&pdev->dev);
1491493 if (IS_ERR(cxlm))
1492494 return PTR_ERR(cxlm);
1493495
1494− rc = cxl_mem_setup_regs(cxlm);
496+ rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
497+ if (rc)
498+ return rc;
499+
500+ rc = cxl_map_regs(cxlm, &map);
1495501 if (rc)
1496502 return rc;
1497503
1498− rc = cxl_mem_setup_mailbox(cxlm);
504+ rc = cxl_pci_setup_mailbox(cxlm);
1499505 if (rc)
1500506 return rc;
1501507
@@ -1511,7 +517,7 @@ static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id) · 9 unmodified lines
1511517 if (rc)
1512518 return rc;
1513519
1514− cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, &cxl_memdev_fops);
520+ cxlmd = devm_cxl_add_memdev(cxlm);
1515521 if (IS_ERR(cxlmd))
1516522 return PTR_ERR(cxlmd);
1517523
@@ -1528,43 +534,15 @@ static const struct pci_device_id cxl_mem_pci_tbl[] = { · 10 unmodified lines
1528534 };
1529535 MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl);
1530536
1531−static struct pci_driver cxl_mem_driver = {
537+static struct pci_driver cxl_pci_driver = {
1532538 .name = KBUILD_MODNAME,
1533539 .id_table = cxl_mem_pci_tbl,
1534− .probe = cxl_mem_probe,
540+ .probe = cxl_pci_probe,
1535541 .driver = {
1536542 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
1537543 },
1538544 };
1539545
1540−static __init int cxl_mem_init(void)
1541−{
1542− struct dentry *mbox_debugfs;
1543− int rc;
1544−
1545− /* Double check the anonymous union trickery in struct cxl_regs */
1546− BUILD_BUG_ON(offsetof(struct cxl_regs, memdev) !=
1547− offsetof(struct cxl_regs, device_regs.memdev));
1548−
1549− rc = pci_register_driver(&cxl_mem_driver);
1550− if (rc)
1551− return rc;
1552−
1553− cxl_debugfs = debugfs_create_dir("cxl", NULL);
1554− mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs);
1555− debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs,
1556− &cxl_raw_allow_all);
1557−
1558− return 0;
1559−}
1560−
1561−static __exit void cxl_mem_exit(void)
1562−{
1563− debugfs_remove_recursive(cxl_debugfs);
1564− pci_unregister_driver(&cxl_mem_driver);
1565−}
1566−
1567546 MODULE_LICENSE("GPL v2");
1568−module_init(cxl_mem_init);
1569−module_exit(cxl_mem_exit);
547+module_pci_driver(cxl_pci_driver);
1570548 MODULE_IMPORT_NS(CXL);
drivers/cxl/pci.h+8−6
@@ -20,13 +20,15 @@ · 19 unmodified lines
2020 #define CXL_REGLOC_BIR_MASK GENMASK(2, 0)
2121
2222 /* 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+};
2930
31+#define CXL_REGLOC_RBI_MASK GENMASK(15, 8)
3032 #define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
3133
3234 #endif /* __CXL_PCI_H__ */
drivers/cxl/pmem.c+142−21
@@ -1,6 +1,7 @@
11 // SPDX-License-Identifier: GPL-2.0-only
22 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
33 #include <linux/libnvdimm.h>
4+#include <asm/unaligned.h>
45 #include <linux/device.h>
56 #include <linux/module.h>
67 #include <linux/ndctl.h>
@@ -16,48 +17,55 @@ · 9 unmodified lines
1617 */
1718 static struct workqueue_struct *cxl_pmem_wq;
1819
19−static void unregister_nvdimm(void *nvdimm)
20−{
21− nvdimm_delete(nvdimm);
22−}
20+static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
2321
24−static int match_nvdimm_bridge(struct device *dev, const void *data)
22+static void clear_exclusive(void *cxlm)
2523 {
26− return strcmp(dev_name(dev), "nvdimm-bridge") == 0;
24+ clear_exclusive_cxl_commands(cxlm, exclusive_cmds);
2725 }
2826
29−static struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(void)
27+static void unregister_nvdimm(void *nvdimm)
3028 {
31− struct device *dev;
32−
33− dev = bus_find_device(&cxl_bus_type, NULL, NULL, match_nvdimm_bridge);
34− if (!dev)
35− return NULL;
36− return to_cxl_nvdimm_bridge(dev);
29+ nvdimm_delete(nvdimm);
3730 }
3831
3932 static int cxl_nvdimm_probe(struct device *dev)
4033 {
4134 struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
35+ struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
36+ unsigned long flags = 0, cmd_mask = 0;
37+ struct cxl_mem *cxlm = cxlmd->cxlm;
4238 struct cxl_nvdimm_bridge *cxl_nvb;
43− unsigned long flags = 0;
4439 struct nvdimm *nvdimm;
45− int rc = -ENXIO;
40+ int rc;
4641
47− cxl_nvb = cxl_find_nvdimm_bridge();
42+ cxl_nvb = cxl_find_nvdimm_bridge(cxl_nvd);
4843 if (!cxl_nvb)
4944 return -ENXIO;
5045
5146 device_lock(&cxl_nvb->dev);
52− if (!cxl_nvb->nvdimm_bus)
47+ if (!cxl_nvb->nvdimm_bus) {
48+ rc = -ENXIO;
49+ goto out;
50+ }
51+
52+ set_exclusive_cxl_commands(cxlm, exclusive_cmds);
53+ rc = devm_add_action_or_reset(dev, clear_exclusive, cxlm);
54+ if (rc)
5355 goto out;
5456
5557 set_bit(NDD_LABELING, &flags);
56− nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags, 0, 0,
57− NULL);
58− if (!nvdimm)
58+ set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
59+ set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
60+ set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
61+ nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags,
62+ cmd_mask, 0, NULL);
63+ if (!nvdimm) {
64+ rc = -ENOMEM;
5965 goto out;
66+ }
6067
68+ dev_set_drvdata(dev, nvdimm);
6169 rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
6270 out:
6371 device_unlock(&cxl_nvb->dev);
@@ -72,11 +80,120 @@ static struct cxl_driver cxl_nvdimm_driver = { · 8 unmodified lines
7280 .id = CXL_DEVICE_NVDIMM,
7381 };
7482
83+static int cxl_pmem_get_config_size(struct cxl_mem *cxlm,
84+ struct nd_cmd_get_config_size *cmd,
85+ unsigned int buf_len)
86+{
87+ if (sizeof(*cmd) > buf_len)
88+ return -EINVAL;
89+
90+ *cmd = (struct nd_cmd_get_config_size) {
91+ .config_size = cxlm->lsa_size,
92+ .max_xfer = cxlm->payload_size,
93+ };
94+
95+ return 0;
96+}
97+
98+static int cxl_pmem_get_config_data(struct cxl_mem *cxlm,
99+ struct nd_cmd_get_config_data_hdr *cmd,
100+ unsigned int buf_len)
101+{
102+ struct cxl_mbox_get_lsa get_lsa;
103+ int rc;
104+
105+ if (sizeof(*cmd) > buf_len)
106+ return -EINVAL;
107+ if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
108+ return -EINVAL;
109+
110+ get_lsa = (struct cxl_mbox_get_lsa) {
111+ .offset = cmd->in_offset,
112+ .length = cmd->in_length,
113+ };
114+
115+ rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LSA, &get_lsa,
116+ sizeof(get_lsa), cmd->out_buf,
117+ cmd->in_length);
118+ cmd->status = 0;
119+
120+ return rc;
121+}
122+
123+static int cxl_pmem_set_config_data(struct cxl_mem *cxlm,
124+ struct nd_cmd_set_config_hdr *cmd,
125+ unsigned int buf_len)
126+{
127+ struct cxl_mbox_set_lsa *set_lsa;
128+ int rc;
129+
130+ if (sizeof(*cmd) > buf_len)
131+ return -EINVAL;
132+
133+ /* 4-byte status follows the input data in the payload */
134+ if (struct_size(cmd, in_buf, cmd->in_length) + 4 > buf_len)
135+ return -EINVAL;
136+
137+ set_lsa =
138+ kvzalloc(struct_size(set_lsa, data, cmd->in_length), GFP_KERNEL);
139+ if (!set_lsa)
140+ return -ENOMEM;
141+
142+ *set_lsa = (struct cxl_mbox_set_lsa) {
143+ .offset = cmd->in_offset,
144+ };
145+ memcpy(set_lsa->data, cmd->in_buf, cmd->in_length);
146+
147+ rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_SET_LSA, set_lsa,
148+ struct_size(set_lsa, data, cmd->in_length),
149+ NULL, 0);
150+
151+ /*
152+ * Set "firmware" status (4-packed bytes at the end of the input
153+ * payload.
154+ */
155+ put_unaligned(0, (u32 *) &cmd->in_buf[cmd->in_length]);
156+ kvfree(set_lsa);
157+
158+ return rc;
159+}
160+
161+static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
162+ void *buf, unsigned int buf_len)
163+{
164+ struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
165+ unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
166+ struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
167+ struct cxl_mem *cxlm = cxlmd->cxlm;
168+
169+ if (!test_bit(cmd, &cmd_mask))
170+ return -ENOTTY;
171+
172+ switch (cmd) {
173+ case ND_CMD_GET_CONFIG_SIZE:
174+ return cxl_pmem_get_config_size(cxlm, buf, buf_len);
175+ case ND_CMD_GET_CONFIG_DATA:
176+ return cxl_pmem_get_config_data(cxlm, buf, buf_len);
177+ case ND_CMD_SET_CONFIG_DATA:
178+ return cxl_pmem_set_config_data(cxlm, buf, buf_len);
179+ default:
180+ return -ENOTTY;
181+ }
182+}
183+
75184 static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,
76185 struct nvdimm *nvdimm, unsigned int cmd, void *buf,
77186 unsigned int buf_len, int *cmd_rc)
78187 {
79− return -ENOTTY;
188+ /*
189+ * No firmware response to translate, let the transport error
190+ * code take precedence.
191+ */
192+ *cmd_rc = 0;
193+
194+ if (!nvdimm)
195+ return -ENOTTY;
196+ return cxl_pmem_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
80197 }
81198
82199 static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)
@@ -194,6 +311,10 @@ static __init int cxl_pmem_init(void) · 111 unmodified lines
194311 {
195312 int rc;
196313
314+ set_bit(CXL_MEM_COMMAND_ID_SET_PARTITION_INFO, exclusive_cmds);
315+ set_bit(CXL_MEM_COMMAND_ID_SET_SHUTDOWN_STATE, exclusive_cmds);
316+ set_bit(CXL_MEM_COMMAND_ID_SET_LSA, exclusive_cmds);
317+
197318 cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0);
198319 if (!cxl_pmem_wq)
199320 return -ENXIO;
drivers/misc/ocxl/config.c+1−12
@@ -33,18 +33,7 @@ · 32 unmodified lines
3333
3434 static int find_dvsec(struct pci_dev *dev, int dvsec_id)
3535 {
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);
4837 }
4938
5039 static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
drivers/nvdimm/btt.c+6−5
@@ -973,7 +973,7 @@ static int btt_arena_write_layout(struct arena_info *arena) · 972 unmodified lines
973973 u64 sum;
974974 struct btt_sb *super;
975975 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);
977977
978978 ret = btt_map_init(arena);
979979 if (ret)
@@ -988,8 +988,8 @@ static int btt_arena_write_layout(struct arena_info *arena) · 8 unmodified lines
988988 return -ENOMEM;
989989
990990 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);
993993 super->flags = cpu_to_le32(arena->flags);
994994 super->version_major = cpu_to_le16(arena->version_major);
995995 super->version_minor = cpu_to_le16(arena->version_minor);
@@ -1574,7 +1574,8 @@ static void btt_blk_cleanup(struct btt *btt) · 578 unmodified lines
15741574 * Pointer to a new struct btt on success, NULL on failure.
15751575 */
15761576 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)
15781579 {
15791580 int ret;
15801581 struct btt *btt;
@@ -1693,7 +1694,7 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) · 112 unmodified lines
16931694 }
16941695 nd_region = to_nd_region(nd_btt->dev.parent);
16951696 btt = btt_init(nd_btt, rawsize, nd_btt->lbasize, nd_btt->uuid,
1696− nd_region);
1697+ nd_region);
16971698 if (!btt)
16981699 return -ENOMEM;
16991700 nd_btt->btt = btt;
drivers/nvdimm/btt_devs.c+8−6
@@ -180,8 +180,8 @@ bool is_nd_btt(struct device *dev) · 179 unmodified lines
180180 EXPORT_SYMBOL(is_nd_btt);
181181
182182 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)
185185 {
186186 struct nd_btt *nd_btt;
187187 struct device *dev;
@@ -244,14 +244,16 @@ struct device *nd_btt_create(struct nd_region *nd_region) · 56 unmodified lines
244244 */
245245 bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super)
246246 {
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;
248249 u64 checksum;
249250
250251 if (memcmp(super->signature, BTT_SIG, BTT_SIG_LEN) != 0)
251252 return false;
252253
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))
255257 return false;
256258
257259 checksum = le64_to_cpu(super->checksum);
@@ -319,7 +321,7 @@ static int __nd_btt_probe(struct nd_btt *nd_btt, · 61 unmodified lines
319321 return rc;
320322
321323 nd_btt->lbasize = le32_to_cpu(btt_sb->external_lbasize);
322− nd_btt->uuid = kmemdup(btt_sb->uuid, 16, GFP_KERNEL);
324+ nd_btt->uuid = kmemdup(&btt_sb->uuid, sizeof(uuid_t), GFP_KERNEL);
323325 if (!nd_btt->uuid)
324326 return -ENOMEM;
325327
drivers/nvdimm/core.c+4−36
@@ -207,38 +207,6 @@ struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus) · 206 unmodified lines
207207 }
208208 EXPORT_SYMBOL_GPL(to_nvdimm_bus_dev);
209209
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−
242210 /**
243211 * nd_uuid_store: common implementation for writing 'uuid' sysfs attributes
244212 * @dev: container device for the uuid property
@@ -249,21 +217,21 @@ static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf, · 4 unmodified lines
249217 * (driver detached)
250218 * LOCKING: expects nd_device_lock() is held on entry
251219 */
252−int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
220+int nd_uuid_store(struct device *dev, uuid_t **uuid_out, const char *buf,
253221 size_t len)
254222 {
255− u8 uuid[16];
223+ uuid_t uuid;
256224 int rc;
257225
258226 if (dev->driver)
259227 return -EBUSY;
260228
261− rc = nd_uuid_parse(dev, uuid, buf, len);
229+ rc = uuid_parse(buf, &uuid);
262230 if (rc)
263231 return rc;
264232
265233 kfree(*uuid_out);
266− *uuid_out = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
234+ *uuid_out = kmemdup(&uuid, sizeof(uuid), GFP_KERNEL);
267235 if (!(*uuid_out))
268236 return -ENOMEM;
269237
drivers/nvdimm/label.c+102−37
@@ -17,6 +17,14 @@ static guid_t nvdimm_btt2_guid; · 16 unmodified lines
1717 static guid_t nvdimm_pfn_guid;
1818 static guid_t nvdimm_dax_guid;
1919
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+
2028 static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";
2129
2230 static u32 best_seq(u32 a, u32 b)
@@ -321,7 +329,8 @@ static bool preamble_index(struct nvdimm_drvdata *ndd, int idx, · 298 unmodified lines
321329 return true;
322330 }
323331
324−char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags)
332+char *nd_label_gen_id(struct nd_label_id *label_id, const uuid_t *uuid,
333+ u32 flags)
325334 {
326335 if (!label_id || !uuid)
327336 return NULL;
@@ -351,7 +360,7 @@ static bool nsl_validate_checksum(struct nvdimm_drvdata *ndd, · 23 unmodified lines
351360 {
352361 u64 sum, sum_save;
353362
354− if (!namespace_label_has(ndd, checksum))
363+ if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
355364 return true;
356365
357366 sum_save = nsl_get_checksum(ndd, nd_label);
@@ -366,7 +375,7 @@ static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd, · 8 unmodified lines
366375 {
367376 u64 sum;
368377
369− if (!namespace_label_has(ndd, checksum))
378+ if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
370379 return;
371380 nsl_set_checksum(ndd, nd_label, 0);
372381 sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1);
@@ -400,9 +409,9 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) · 27 unmodified lines
400409 struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
401410 struct nd_namespace_label *nd_label;
402411 struct nd_region *nd_region = NULL;
403− u8 label_uuid[NSLABEL_UUID_LEN];
404412 struct nd_label_id label_id;
405413 struct resource *res;
414+ uuid_t label_uuid;
406415 u32 flags;
407416
408417 nd_label = to_label(ndd, slot);
@@ -410,11 +419,11 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) · 1 unmodified line
410419 if (!slot_valid(ndd, nd_label, slot))
411420 continue;
412421
413− memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);
422+ nsl_get_uuid(ndd, nd_label, &label_uuid);
414423 flags = nsl_get_flags(ndd, nd_label);
415424 if (test_bit(NDD_NOBLK, &nvdimm->flags))
416425 flags &= ~NSLABEL_FLAG_LOCAL;
417− nd_label_gen_id(&label_id, label_uuid, flags);
426+ nd_label_gen_id(&label_id, &label_uuid, flags);
418427 res = nvdimm_allocate_dpa(ndd, &label_id,
419428 nsl_get_dpa(ndd, nd_label),
420429 nsl_get_rawsize(ndd, nd_label));
@@ -724,7 +733,7 @@ static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, · 303 unmodified lines
724733 - (unsigned long) to_namespace_index(ndd, 0);
725734 }
726735
727−static enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid)
736+static enum nvdimm_claim_class guid_to_nvdimm_cclass(guid_t *guid)
728737 {
729738 if (guid_equal(guid, &nvdimm_btt_guid))
730739 return NVDIMM_CCLASS_BTT;
@@ -740,6 +749,23 @@ static enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid) · 9 unmodified lines
740749 return NVDIMM_CCLASS_UNKNOWN;
741750 }
742751
752+/* CXL labels store UUIDs instead of GUIDs for the same data */
753+static enum nvdimm_claim_class uuid_to_nvdimm_cclass(uuid_t *uuid)
754+{
755+ if (uuid_equal(uuid, &nvdimm_btt_uuid))
756+ return NVDIMM_CCLASS_BTT;
757+ else if (uuid_equal(uuid, &nvdimm_btt2_uuid))
758+ return NVDIMM_CCLASS_BTT2;
759+ else if (uuid_equal(uuid, &nvdimm_pfn_uuid))
760+ return NVDIMM_CCLASS_PFN;
761+ else if (uuid_equal(uuid, &nvdimm_dax_uuid))
762+ return NVDIMM_CCLASS_DAX;
763+ else if (uuid_equal(uuid, &uuid_null))
764+ return NVDIMM_CCLASS_NONE;
765+
766+ return NVDIMM_CCLASS_UNKNOWN;
767+}
768+
743769 static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class,
744770 guid_t *target)
745771 {
@@ -761,6 +787,28 @@ static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, · 15 unmodified lines
761787 return &guid_null;
762788 }
763789
790+/* CXL labels store UUIDs instead of GUIDs for the same data */
791+static const uuid_t *to_abstraction_uuid(enum nvdimm_claim_class claim_class,
792+ uuid_t *target)
793+{
794+ if (claim_class == NVDIMM_CCLASS_BTT)
795+ return &nvdimm_btt_uuid;
796+ else if (claim_class == NVDIMM_CCLASS_BTT2)
797+ return &nvdimm_btt2_uuid;
798+ else if (claim_class == NVDIMM_CCLASS_PFN)
799+ return &nvdimm_pfn_uuid;
800+ else if (claim_class == NVDIMM_CCLASS_DAX)
801+ return &nvdimm_dax_uuid;
802+ else if (claim_class == NVDIMM_CCLASS_UNKNOWN) {
803+ /*
804+ * If we're modifying a namespace for which we don't
805+ * know the claim_class, don't touch the existing uuid.
806+ */
807+ return target;
808+ } else
809+ return &uuid_null;
810+}
811+
764812 static void reap_victim(struct nd_mapping *nd_mapping,
765813 struct nd_label_ent *victim)
766814 {
@@ -775,18 +823,18 @@ static void reap_victim(struct nd_mapping *nd_mapping, · 8 unmodified lines
775823 static void nsl_set_type_guid(struct nvdimm_drvdata *ndd,
776824 struct nd_namespace_label *nd_label, guid_t *guid)
777825 {
778− if (namespace_label_has(ndd, type_guid))
779− guid_copy(&nd_label->type_guid, guid);
826+ if (efi_namespace_label_has(ndd, type_guid))
827+ guid_copy(&nd_label->efi.type_guid, guid);
780828 }
781829
782830 bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd,
783831 struct nd_namespace_label *nd_label, guid_t *guid)
784832 {
785− if (!namespace_label_has(ndd, type_guid))
833+ if (ndd->cxl || !efi_namespace_label_has(ndd, type_guid))
786834 return true;
787− if (!guid_equal(&nd_label->type_guid, guid)) {
835+ if (!guid_equal(&nd_label->efi.type_guid, guid)) {
788836 dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", guid,
789− &nd_label->type_guid);
837+ &nd_label->efi.type_guid);
790838 return false;
791839 }
792840 return true;
@@ -796,19 +844,34 @@ static void nsl_set_claim_class(struct nvdimm_drvdata *ndd, · 3 unmodified lines
796844 struct nd_namespace_label *nd_label,
797845 enum nvdimm_claim_class claim_class)
798846 {
799− if (!namespace_label_has(ndd, abstraction_guid))
847+ if (ndd->cxl) {
848+ uuid_t uuid;
849+
850+ import_uuid(&uuid, nd_label->cxl.abstraction_uuid);
851+ export_uuid(nd_label->cxl.abstraction_uuid,
852+ to_abstraction_uuid(claim_class, &uuid));
853+ return;
854+ }
855+
856+ if (!efi_namespace_label_has(ndd, abstraction_guid))
800857 return;
801− guid_copy(&nd_label->abstraction_guid,
858+ guid_copy(&nd_label->efi.abstraction_guid,
802859 to_abstraction_guid(claim_class,
803− &nd_label->abstraction_guid));
860+ &nd_label->efi.abstraction_guid));
804861 }
805862
806863 enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
807864 struct nd_namespace_label *nd_label)
808865 {
809− if (!namespace_label_has(ndd, abstraction_guid))
866+ if (ndd->cxl) {
867+ uuid_t uuid;
868+
869+ import_uuid(&uuid, nd_label->cxl.abstraction_uuid);
870+ return uuid_to_nvdimm_cclass(&uuid);
871+ }
872+ if (!efi_namespace_label_has(ndd, abstraction_guid))
810873 return NVDIMM_CCLASS_NONE;
811− return to_nvdimm_cclass(&nd_label->abstraction_guid);
874+ return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid);
812875 }
813876
814877 static int __pmem_label_update(struct nd_region *nd_region,
@@ -851,10 +914,11 @@ static int __pmem_label_update(struct nd_region *nd_region, · 36 unmodified lines
851914
852915 nd_label = to_label(ndd, slot);
853916 memset(nd_label, 0, sizeof_namespace_label(ndd));
854− memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN);
917+ nsl_set_uuid(ndd, nd_label, nspm->uuid);
855918 nsl_set_name(ndd, nd_label, nspm->alt_name);
856919 nsl_set_flags(ndd, nd_label, flags);
857920 nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings);
921+ nsl_set_nrange(ndd, nd_label, 1);
858922 nsl_set_position(ndd, nd_label, pos);
859923 nsl_set_isetcookie(ndd, nd_label, cookie);
860924 nsl_set_rawsize(ndd, nd_label, resource_size(res));
@@ -878,9 +942,8 @@ static int __pmem_label_update(struct nd_region *nd_region, · 17 unmodified lines
878942 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
879943 if (!label_ent->label)
880944 continue;
881− if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags)
882− || memcmp(nspm->uuid, label_ent->label->uuid,
883− NSLABEL_UUID_LEN) == 0)
945+ if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) ||
946+ nsl_uuid_equal(ndd, label_ent->label, nspm->uuid))
884947 reap_victim(nd_mapping, label_ent);
885948 }
886949
@@ -941,7 +1004,7 @@ static void nsl_set_blk_isetcookie(struct nvdimm_drvdata *ndd, · 54 unmodified lines
9411004 struct nd_namespace_label *nd_label,
9421005 u64 isetcookie)
9431006 {
944− if (namespace_label_has(ndd, type_guid)) {
1007+ if (efi_namespace_label_has(ndd, type_guid)) {
9451008 nsl_set_isetcookie(ndd, nd_label, isetcookie);
9461009 return;
9471010 }
@@ -952,7 +1015,7 @@ bool nsl_validate_blk_isetcookie(struct nvdimm_drvdata *ndd, · 4 unmodified lines
9521015 struct nd_namespace_label *nd_label,
9531016 u64 isetcookie)
9541017 {
955− if (!namespace_label_has(ndd, type_guid))
1018+ if (!efi_namespace_label_has(ndd, type_guid))
9561019 return true;
9571020
9581021 if (nsl_get_isetcookie(ndd, nd_label) != isetcookie) {
@@ -968,7 +1031,7 @@ static void nsl_set_blk_nlabel(struct nvdimm_drvdata *ndd, · 9 unmodified lines
9681031 struct nd_namespace_label *nd_label, int nlabel,
9691032 bool first)
9701033 {
971− if (!namespace_label_has(ndd, type_guid)) {
1034+ if (!efi_namespace_label_has(ndd, type_guid)) {
9721035 nsl_set_nlabel(ndd, nd_label, 0); /* N/A */
9731036 return;
9741037 }
@@ -979,7 +1042,7 @@ static void nsl_set_blk_position(struct nvdimm_drvdata *ndd, · 4 unmodified lines
9791042 struct nd_namespace_label *nd_label,
9801043 bool first)
9811044 {
982− if (!namespace_label_has(ndd, type_guid)) {
1045+ if (!efi_namespace_label_has(ndd, type_guid)) {
9831046 nsl_set_position(ndd, nd_label, 0);
9841047 return;
9851048 }
@@ -1005,7 +1068,6 @@ static int __blk_label_update(struct nd_region *nd_region, · 19 unmodified lines
10051068 unsigned long *free, *victim_map = NULL;
10061069 struct resource *res, **old_res_list;
10071070 struct nd_label_id label_id;
1008− u8 uuid[NSLABEL_UUID_LEN];
10091071 int min_dpa_idx = 0;
10101072 LIST_HEAD(list);
10111073 u32 nslot, slot;
@@ -1043,8 +1105,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 31 unmodified lines
10431105 /* mark unused labels for garbage collection */
10441106 for_each_clear_bit_le(slot, free, nslot) {
10451107 nd_label = to_label(ndd, slot);
1046− memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN);
1047− if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0)
1108+ if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
10481109 continue;
10491110 res = to_resource(ndd, nd_label);
10501111 if (res && is_old_resource(res, old_res_list,
@@ -1113,7 +1174,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 62 unmodified lines
11131174
11141175 nd_label = to_label(ndd, slot);
11151176 memset(nd_label, 0, sizeof_namespace_label(ndd));
1116− memcpy(nd_label->uuid, nsblk->uuid, NSLABEL_UUID_LEN);
1177+ nsl_set_uuid(ndd, nd_label, nsblk->uuid);
11171178 nsl_set_name(ndd, nd_label, nsblk->alt_name);
11181179 nsl_set_flags(ndd, nd_label, NSLABEL_FLAG_LOCAL);
11191180
@@ -1161,8 +1222,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 41 unmodified lines
11611222 if (!nd_label)
11621223 continue;
11631224 nlabel++;
1164− memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN);
1165− if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0)
1225+ if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
11661226 continue;
11671227 nlabel--;
11681228 list_move(&label_ent->list, &list);
@@ -1192,8 +1252,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 23 unmodified lines
11921252 }
11931253 for_each_clear_bit_le(slot, free, nslot) {
11941254 nd_label = to_label(ndd, slot);
1195− memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN);
1196− if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0)
1255+ if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
11971256 continue;
11981257 res = to_resource(ndd, nd_label);
11991258 res->flags &= ~DPA_RESOURCE_ADJUSTED;
@@ -1273,12 +1332,11 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels) · 73 unmodified lines
12731332 return max(num_labels, old_num_labels);
12741333 }
12751334
1276−static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid)
1335+static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
12771336 {
12781337 struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
12791338 struct nd_label_ent *label_ent, *e;
12801339 struct nd_namespace_index *nsindex;
1281− u8 label_uuid[NSLABEL_UUID_LEN];
12821340 unsigned long *free;
12831341 LIST_HEAD(list);
12841342 u32 nslot, slot;
@@ -1298,8 +1356,7 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) · 13 unmodified lines
12981356 if (!nd_label)
12991357 continue;
13001358 active++;
1301− memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);
1302− if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0)
1359+ if (!nsl_uuid_equal(ndd, nd_label, uuid))
13031360 continue;
13041361 active--;
13051362 slot = to_slot(ndd, nd_label);
@@ -1395,5 +1452,13 @@ int __init nd_label_init(void) · 89 unmodified lines
13951452 WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid));
13961453 WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid));
13971454
1455+ WARN_ON(uuid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_uuid));
1456+ WARN_ON(uuid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_uuid));
1457+ WARN_ON(uuid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_uuid));
1458+ WARN_ON(uuid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_uuid));
1459+
1460+ WARN_ON(uuid_parse(CXL_REGION_UUID, &cxl_region_uuid));
1461+ WARN_ON(uuid_parse(CXL_NAMESPACE_UUID, &cxl_namespace_uuid));
1462+
13981463 return 0;
13991464 }
drivers/nvdimm/label.h+89−5
@@ -34,6 +34,7 @@ enum { · 33 unmodified lines
3434 * struct nd_namespace_index - label set superblock
3535 * @sig: NAMESPACE_INDEX\0
3636 * @flags: placeholder
37+ * @labelsize: log2 size (v1 labels 128 bytes v2 labels 256 bytes)
3738 * @seq: sequence number for this index
3839 * @myoff: offset of this index in label area
3940 * @mysize: size of this index struct
@@ -43,7 +44,7 @@ enum { · 3 unmodified lines
4344 * @major: label area major version
4445 * @minor: label area minor version
4546 * @checksum: fletcher64 of all fields
46− * @free[0]: bitmap, nlabel bits
47+ * @free: bitmap, nlabel bits
4748 *
4849 * The size of free[] is rounded up so the total struct size is a
4950 * multiple of NSINDEX_ALIGN bytes. Any bits this allocates beyond
@@ -66,7 +67,39 @@ struct nd_namespace_index { · 16 unmodified lines
6667 };
6768
6869 /**
69− * struct nd_namespace_label - namespace superblock
70+ * struct cxl_region_label - CXL 2.0 Table 211
71+ * @type: uuid identifying this label format (region)
72+ * @uuid: uuid for the region this label describes
73+ * @flags: NSLABEL_FLAG_UPDATING (all other flags reserved)
74+ * @nlabel: 1 per interleave-way in the region
75+ * @position: this label's position in the set
76+ * @dpa: start address in device-local capacity for this label
77+ * @rawsize: size of this label's contribution to region
78+ * @hpa: mandatory system physical address to map this region
79+ * @slot: slot id of this label in label area
80+ * @ig: interleave granularity (1 << @ig) * 256 bytes
81+ * @align: alignment in SZ_256M blocks
82+ * @reserved: reserved
83+ * @checksum: fletcher64 sum of this label
84+ */
85+struct cxl_region_label {
86+ u8 type[NSLABEL_UUID_LEN];
87+ u8 uuid[NSLABEL_UUID_LEN];
88+ __le32 flags;
89+ __le16 nlabel;
90+ __le16 position;
91+ __le64 dpa;
92+ __le64 rawsize;
93+ __le64 hpa;
94+ __le32 slot;
95+ __le32 ig;
96+ __le32 align;
97+ u8 reserved[0xac];
98+ __le64 checksum;
99+};
100+
101+/**
102+ * struct nvdimm_efi_label - namespace superblock
70103 * @uuid: UUID per RFC 4122
71104 * @name: optional name (NULL-terminated)
72105 * @flags: see NSLABEL_FLAG_*
@@ -77,9 +110,14 @@ struct nd_namespace_index { · 4 unmodified lines
77110 * @dpa: DPA of NVM range on this DIMM
78111 * @rawsize: size of namespace
79112 * @slot: slot of this label in label area
80− * @unused: must be zero
113+ * @align: physical address alignment of the namespace
114+ * @reserved: reserved
115+ * @type_guid: copy of struct acpi_nfit_system_address.range_guid
116+ * @abstraction_guid: personality id (btt, btt2, fsdax, devdax....)
117+ * @reserved2: reserved
118+ * @checksum: fletcher64 sum of this object
81119 */
82−struct nd_namespace_label {
120+struct nvdimm_efi_label {
83121 u8 uuid[NSLABEL_UUID_LEN];
84122 u8 name[NSLABEL_NAME_LEN];
85123 __le32 flags;
@@ -92,7 +130,7 @@ struct nd_namespace_label { · 6 unmodified lines
92130 __le32 slot;
93131 /*
94132 * Accessing fields past this point should be gated by a
95− * namespace_label_has() check.
133+ * efi_namespace_label_has() check.
96134 */
97135 u8 align;
98136 u8 reserved[3];
@@ -102,11 +140,57 @@ struct nd_namespace_label { · 3 unmodified lines
102140 __le64 checksum;
103141 };
104142
143+/**
144+ * struct nvdimm_cxl_label - CXL 2.0 Table 212
145+ * @type: uuid identifying this label format (namespace)
146+ * @uuid: uuid for the namespace this label describes
147+ * @name: friendly name for the namespace
148+ * @flags: NSLABEL_FLAG_UPDATING (all other flags reserved)
149+ * @nrange: discontiguous namespace support
150+ * @position: this label's position in the set
151+ * @dpa: start address in device-local capacity for this label
152+ * @rawsize: size of this label's contribution to namespace
153+ * @slot: slot id of this label in label area
154+ * @align: alignment in SZ_256M blocks
155+ * @region_uuid: host interleave set identifier
156+ * @abstraction_uuid: personality driver for this namespace
157+ * @lbasize: address geometry for disk-like personalities
158+ * @reserved: reserved
159+ * @checksum: fletcher64 sum of this label
160+ */
161+struct nvdimm_cxl_label {
162+ u8 type[NSLABEL_UUID_LEN];
163+ u8 uuid[NSLABEL_UUID_LEN];
164+ u8 name[NSLABEL_NAME_LEN];
165+ __le32 flags;
166+ __le16 nrange;
167+ __le16 position;
168+ __le64 dpa;
169+ __le64 rawsize;
170+ __le32 slot;
171+ __le32 align;
172+ u8 region_uuid[16];
173+ u8 abstraction_uuid[16];
174+ __le16 lbasize;
175+ u8 reserved[0x56];
176+ __le64 checksum;
177+};
178+
179+struct nd_namespace_label {
180+ union {
181+ struct nvdimm_cxl_label cxl;
182+ struct nvdimm_efi_label efi;
183+ };
184+};
185+
105186 #define NVDIMM_BTT_GUID "8aed63a2-29a2-4c66-8b12-f05d15d3922a"
106187 #define NVDIMM_BTT2_GUID "18633bfc-1735-4217-8ac9-17239282d3f8"
107188 #define NVDIMM_PFN_GUID "266400ba-fb9f-4677-bcb0-968f11d0d225"
108189 #define NVDIMM_DAX_GUID "97a86d9c-3cdd-4eda-986f-5068b4f80088"
109190
191+#define CXL_REGION_UUID "529d7c61-da07-47c4-a93f-ecdf2c06f444"
192+#define CXL_NAMESPACE_UUID "68bb2c0a-5a77-4937-9f85-3caf41a0f93c"
193+
110194 /**
111195 * struct nd_label_id - identifier string for dpa allocation
112196 * @id: "{blk|pmem}-<namespace uuid>"
drivers/nvdimm/namespace_devs.c+52−43
@@ -51,7 +51,7 @@ static bool is_namespace_io(const struct device *dev); · 50 unmodified lines
5151
5252 static int is_uuid_busy(struct device *dev, void *data)
5353 {
54− u8 *uuid1 = data, *uuid2 = NULL;
54+ uuid_t *uuid1 = data, *uuid2 = NULL;
5555
5656 if (is_namespace_pmem(dev)) {
5757 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
@@ -71,7 +71,7 @@ static int is_uuid_busy(struct device *dev, void *data) · 13 unmodified lines
7171 uuid2 = nd_pfn->uuid;
7272 }
7373
74− if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0)
74+ if (uuid2 && uuid_equal(uuid1, uuid2))
7575 return -EBUSY;
7676
7777 return 0;
@@ -89,7 +89,7 @@ static int is_namespace_uuid_busy(struct device *dev, void *data) · 11 unmodified lines
8989 * @dev: any device on a nvdimm_bus
9090 * @uuid: uuid to check
9191 */
92−bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
92+bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid)
9393 {
9494 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
9595
@@ -192,12 +192,10 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, · 96 unmodified lines
192192 }
193193 EXPORT_SYMBOL(nvdimm_namespace_disk_name);
194194
195−const u8 *nd_dev_to_uuid(struct device *dev)
195+const uuid_t *nd_dev_to_uuid(struct device *dev)
196196 {
197− static const u8 null_uuid[16];
198−
199197 if (!dev)
200− return null_uuid;
198+ return &uuid_null;
201199
202200 if (is_namespace_pmem(dev)) {
203201 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
@@ -208,7 +206,7 @@ const u8 *nd_dev_to_uuid(struct device *dev) · 4 unmodified lines
208206
209207 return nsblk->uuid;
210208 } else
211− return null_uuid;
209+ return &uuid_null;
212210 }
213211 EXPORT_SYMBOL(nd_dev_to_uuid);
214212
@@ -938,7 +936,8 @@ static void nd_namespace_pmem_set_resource(struct nd_region *nd_region, · 723 unmodified lines
938936 res->end = res->start + size - 1;
939937 }
940938
941−static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where)
939+static bool uuid_not_set(const uuid_t *uuid, struct device *dev,
940+ const char *where)
942941 {
943942 if (!uuid) {
944943 dev_dbg(dev, "%s: uuid not set\n", where);
@@ -957,7 +956,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) · 12 unmodified lines
957956 struct nd_label_id label_id;
958957 u32 flags = 0, remainder;
959958 int rc, i, id = -1;
960− u8 *uuid = NULL;
959+ uuid_t *uuid = NULL;
961960
962961 if (dev->driver || ndns->claim)
963962 return -EBUSY;
@@ -1050,7 +1049,7 @@ static ssize_t size_store(struct device *dev, · 86 unmodified lines
10501049 {
10511050 struct nd_region *nd_region = to_nd_region(dev->parent);
10521051 unsigned long long val;
1053− u8 **uuid = NULL;
1052+ uuid_t **uuid = NULL;
10541053 int rc;
10551054
10561055 rc = kstrtoull(buf, 0, &val);
@@ -1147,7 +1146,7 @@ static ssize_t size_show(struct device *dev, · 90 unmodified lines
11471146 }
11481147 static DEVICE_ATTR(size, 0444, size_show, size_store);
11491148
1150−static u8 *namespace_to_uuid(struct device *dev)
1149+static uuid_t *namespace_to_uuid(struct device *dev)
11511150 {
11521151 if (is_namespace_pmem(dev)) {
11531152 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
@@ -1161,10 +1160,10 @@ static u8 *namespace_to_uuid(struct device *dev) · 7 unmodified lines
11611160 return ERR_PTR(-ENXIO);
11621161 }
11631162
1164−static ssize_t uuid_show(struct device *dev,
1165− struct device_attribute *attr, char *buf)
1163+static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
1164+ char *buf)
11661165 {
1167− u8 *uuid = namespace_to_uuid(dev);
1166+ uuid_t *uuid = namespace_to_uuid(dev);
11681167
11691168 if (IS_ERR(uuid))
11701169 return PTR_ERR(uuid);
@@ -1181,7 +1180,8 @@ static ssize_t uuid_show(struct device *dev, · 10 unmodified lines
11811180 * @old_uuid: reference to the uuid storage location in the namespace object
11821181 */
11831182 static int namespace_update_uuid(struct nd_region *nd_region,
1184− struct device *dev, u8 *new_uuid, u8 **old_uuid)
1183+ struct device *dev, uuid_t *new_uuid,
1184+ uuid_t **old_uuid)
11851185 {
11861186 u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0;
11871187 struct nd_label_id old_label_id;
@@ -1231,10 +1231,12 @@ static int namespace_update_uuid(struct nd_region *nd_region, · 43 unmodified lines
12311231 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
12321232 struct nd_namespace_label *nd_label = label_ent->label;
12331233 struct nd_label_id label_id;
1234+ uuid_t uuid;
12341235
12351236 if (!nd_label)
12361237 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,
12381240 nsl_get_flags(ndd, nd_label));
12391241 if (strcmp(old_label_id.id, label_id.id) == 0)
12401242 set_bit(ND_LABEL_REAP, &label_ent->flags);
@@ -1251,9 +1253,9 @@ static ssize_t uuid_store(struct device *dev, · 10 unmodified lines
12511253 struct device_attribute *attr, const char *buf, size_t len)
12521254 {
12531255 struct nd_region *nd_region = to_nd_region(dev->parent);
1254− u8 *uuid = NULL;
1256+ uuid_t *uuid = NULL;
1257+ uuid_t **ns_uuid;
12551258 ssize_t rc = 0;
1256− u8 **ns_uuid;
12571259
12581260 if (is_namespace_pmem(dev)) {
12591261 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
@@ -1378,8 +1380,8 @@ static ssize_t dpa_extents_show(struct device *dev, · 118 unmodified lines
13781380 {
13791381 struct nd_region *nd_region = to_nd_region(dev->parent);
13801382 struct nd_label_id label_id;
1383+ uuid_t *uuid = NULL;
13811384 int count = 0, i;
1382− u8 *uuid = NULL;
13831385 u32 flags = 0;
13841386
13851387 nvdimm_bus_lock(dev);
@@ -1831,8 +1833,8 @@ static struct device **create_namespace_io(struct nd_region *nd_region) · 445 unmodified lines
18311833 return devs;
18321834 }
18331835
1834−static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid,
1835− u64 cookie, u16 pos)
1836+static bool has_uuid_at_pos(struct nd_region *nd_region, const uuid_t *uuid,
1837+ u64 cookie, u16 pos)
18361838 {
18371839 struct nd_namespace_label *found = NULL;
18381840 int i;
@@ -1846,17 +1848,16 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 7 unmodified lines
18461848
18471849 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
18481850 struct nd_namespace_label *nd_label = label_ent->label;
1849− u16 position, nlabel;
1851+ u16 position;
18501852
18511853 if (!nd_label)
18521854 continue;
18531855 position = nsl_get_position(ndd, nd_label);
1854− nlabel = nsl_get_nlabel(ndd, nd_label);
18551856
18561857 if (!nsl_validate_isetcookie(ndd, nd_label, cookie))
18571858 continue;
18581859
1859− if (memcmp(nd_label->uuid, uuid, NSLABEL_UUID_LEN) != 0)
1860+ if (!nsl_uuid_equal(ndd, nd_label, uuid))
18601861 continue;
18611862
18621863 if (!nsl_validate_type_guid(ndd, nd_label,
@@ -1868,7 +1869,7 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 5 unmodified lines
18681869 return false;
18691870 }
18701871 found_uuid = true;
1871− if (nlabel != nd_region->ndr_mappings)
1872+ if (!nsl_validate_nlabel(nd_region, ndd, nd_label))
18721873 continue;
18731874 if (position != pos)
18741875 continue;
@@ -1881,7 +1882,7 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 6 unmodified lines
18811882 return found != NULL;
18821883 }
18831884
1884−static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
1885+static int select_pmem_id(struct nd_region *nd_region, const uuid_t *pmem_id)
18851886 {
18861887 int i;
18871888
@@ -1900,7 +1901,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) · 12 unmodified lines
19001901 nd_label = label_ent->label;
19011902 if (!nd_label)
19021903 continue;
1903− if (memcmp(nd_label->uuid, pmem_id, NSLABEL_UUID_LEN) == 0)
1904+ if (nsl_uuid_equal(ndd, nd_label, pmem_id))
19041905 break;
19051906 nd_label = NULL;
19061907 }
@@ -1923,7 +1924,8 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) · 16 unmodified lines
19231924 /* pass */;
19241925 else {
19251926 dev_dbg(&nd_region->dev, "%s invalid label for %pUb\n",
1926− dev_name(ndd->dev), nd_label->uuid);
1927+ dev_name(ndd->dev),
1928+ nsl_uuid_raw(ndd, nd_label));
19271929 return -EINVAL;
19281930 }
19291931
@@ -1953,6 +1955,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 23 unmodified lines
19531955 resource_size_t size = 0;
19541956 struct resource *res;
19551957 struct device *dev;
1958+ uuid_t uuid;
19561959 int rc = 0;
19571960 u16 i;
19581961
@@ -1963,12 +1966,12 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 4 unmodified lines
19631966
19641967 if (!nsl_validate_isetcookie(ndd, nd_label, cookie)) {
19651968 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
1966− nd_label->uuid);
1969+ nsl_uuid_raw(ndd, nd_label));
19671970 if (!nsl_validate_isetcookie(ndd, nd_label, altcookie))
19681971 return ERR_PTR(-EAGAIN);
19691972
19701973 dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
1971− nd_label->uuid);
1974+ nsl_uuid_raw(ndd, nd_label));
19721975 }
19731976
19741977 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
@@ -1984,9 +1987,12 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 9 unmodified lines
19841987 res->flags = IORESOURCE_MEM;
19851988
19861989 for (i = 0; i < nd_region->ndr_mappings; i++) {
1987− if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
1990+ uuid_t uuid;
1991+
1992+ nsl_get_uuid(ndd, nd_label, &uuid);
1993+ if (has_uuid_at_pos(nd_region, &uuid, cookie, i))
19881994 continue;
1989− if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
1995+ if (has_uuid_at_pos(nd_region, &uuid, altcookie, i))
19901996 continue;
19911997 break;
19921998 }
@@ -2000,7 +2006,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 7 unmodified lines
20002006 * find a dimm with two instances of the same uuid.
20012007 */
20022008 dev_err(&nd_region->dev, "%s missing label for %pUb\n",
2003− nvdimm_name(nvdimm), nd_label->uuid);
2009+ nvdimm_name(nvdimm), nsl_uuid_raw(ndd, nd_label));
20042010 rc = -EINVAL;
20052011 goto err;
20062012 }
@@ -2013,7 +2019,8 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 6 unmodified lines
20132019 * the dimm being enabled (i.e. nd_label_reserve_dpa()
20142020 * succeeded).
20152021 */
2016− rc = select_pmem_id(nd_region, nd_label->uuid);
2022+ nsl_get_uuid(ndd, nd_label, &uuid);
2023+ rc = select_pmem_id(nd_region, &uuid);
20172024 if (rc)
20182025 goto err;
20192026
@@ -2039,8 +2046,8 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 19 unmodified lines
20392046 WARN_ON(nspm->alt_name || nspm->uuid);
20402047 nspm->alt_name = kmemdup(nsl_ref_name(ndd, label0),
20412048 NSLABEL_NAME_LEN, GFP_KERNEL);
2042− nspm->uuid = kmemdup((void __force *) label0->uuid,
2043− NSLABEL_UUID_LEN, GFP_KERNEL);
2049+ nsl_get_uuid(ndd, label0, &uuid);
2050+ nspm->uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL);
20442051 nspm->lbasize = nsl_get_lbasize(ndd, label0);
20452052 nspm->nsio.common.claim_class =
20462053 nsl_get_claim_class(ndd, label0);
@@ -2217,15 +2224,15 @@ static int add_namespace_resource(struct nd_region *nd_region, · 170 unmodified lines
22172224 int i;
22182225
22192226 for (i = 0; i < count; i++) {
2220− u8 *uuid = namespace_to_uuid(devs[i]);
2227+ uuid_t *uuid = namespace_to_uuid(devs[i]);
22212228 struct resource *res;
22222229
2223− if (IS_ERR_OR_NULL(uuid)) {
2230+ if (IS_ERR(uuid)) {
22242231 WARN_ON(1);
22252232 continue;
22262233 }
22272234
2228− if (memcmp(uuid, nd_label->uuid, NSLABEL_UUID_LEN) != 0)
2235+ if (!nsl_uuid_equal(ndd, nd_label, uuid))
22292236 continue;
22302237 if (is_namespace_blk(devs[i])) {
22312238 res = nsblk_add_resource(nd_region, ndd,
@@ -2236,8 +2243,8 @@ static int add_namespace_resource(struct nd_region *nd_region, · 4 unmodified lines
22362243 nd_dbg_dpa(nd_region, ndd, res, "%d assign\n", count);
22372244 } else {
22382245 dev_err(&nd_region->dev,
2239− "error: conflicting extents for uuid: %pUb\n",
2240− nd_label->uuid);
2246+ "error: conflicting extents for uuid: %pUb\n",
2247+ uuid);
22412248 return -ENXIO;
22422249 }
22432250 break;
@@ -2257,6 +2264,7 @@ static struct device *create_namespace_blk(struct nd_region *nd_region, · 13 unmodified lines
22572264 char name[NSLABEL_NAME_LEN];
22582265 struct device *dev = NULL;
22592266 struct resource *res;
2267+ uuid_t uuid;
22602268
22612269 if (!nsl_validate_type_guid(ndd, nd_label, &nd_set->type_guid))
22622270 return ERR_PTR(-EAGAIN);
@@ -2271,7 +2279,8 @@ static struct device *create_namespace_blk(struct nd_region *nd_region, · 8 unmodified lines
22712279 dev->parent = &nd_region->dev;
22722280 nsblk->id = -1;
22732281 nsblk->lbasize = nsl_get_lbasize(ndd, nd_label);
2274− nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN, GFP_KERNEL);
2282+ nsl_get_uuid(ndd, nd_label, &uuid);
2283+ nsblk->uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL);
22752284 nsblk->common.claim_class = nsl_get_claim_class(ndd, nd_label);
22762285 if (!nsblk->uuid)
22772286 goto blk_err;
drivers/nvdimm/nd-core.h+3−2
@@ -126,8 +126,9 @@ void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus); · 125 unmodified lines
126126 void nd_synchronize(void);
127127 void __nd_device_register(struct device *dev);
128128 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);
131132 struct nd_region;
132133 struct nvdimm_drvdata;
133134 struct nd_mapping;
drivers/nvdimm/nd.h+156−29
@@ -30,6 +30,7 @@ struct nvdimm_drvdata { · 29 unmodified lines
3030 int nslabel_size;
3131 struct nd_cmd_get_config_size nsarea;
3232 void *data;
33+ bool cxl;
3334 int ns_current, ns_next;
3435 struct resource dpa;
3536 struct kref kref;
@@ -38,13 +39,17 @@ struct nvdimm_drvdata { · 2 unmodified lines
3839 static inline const u8 *nsl_ref_name(struct nvdimm_drvdata *ndd,
3940 struct nd_namespace_label *nd_label)
4041 {
41− return nd_label->name;
42+ if (ndd->cxl)
43+ return nd_label->cxl.name;
44+ return nd_label->efi.name;
4245 }
4346
4447 static inline u8 *nsl_get_name(struct nvdimm_drvdata *ndd,
4548 struct nd_namespace_label *nd_label, u8 *name)
4649 {
47− return memcpy(name, nd_label->name, NSLABEL_NAME_LEN);
50+ if (ndd->cxl)
51+ return memcpy(name, nd_label->cxl.name, NSLABEL_NAME_LEN);
52+ return memcpy(name, nd_label->efi.name, NSLABEL_NAME_LEN);
4853 }
4954
5055 static inline u8 *nsl_set_name(struct nvdimm_drvdata *ndd,
@@ -52,129 +57,242 @@ static inline u8 *nsl_set_name(struct nvdimm_drvdata *ndd, · 1 unmodified line
5257 {
5358 if (!name)
5459 return NULL;
55− return memcpy(nd_label->name, name, NSLABEL_NAME_LEN);
60+ if (ndd->cxl)
61+ return memcpy(nd_label->cxl.name, name, NSLABEL_NAME_LEN);
62+ return memcpy(nd_label->efi.name, name, NSLABEL_NAME_LEN);
5663 }
5764
5865 static inline u32 nsl_get_slot(struct nvdimm_drvdata *ndd,
5966 struct nd_namespace_label *nd_label)
6067 {
61− return __le32_to_cpu(nd_label->slot);
68+ if (ndd->cxl)
69+ return __le32_to_cpu(nd_label->cxl.slot);
70+ return __le32_to_cpu(nd_label->efi.slot);
6271 }
6372
6473 static inline void nsl_set_slot(struct nvdimm_drvdata *ndd,
6574 struct nd_namespace_label *nd_label, u32 slot)
6675 {
67− nd_label->slot = __cpu_to_le32(slot);
76+ if (ndd->cxl)
77+ nd_label->cxl.slot = __cpu_to_le32(slot);
78+ else
79+ nd_label->efi.slot = __cpu_to_le32(slot);
6880 }
6981
7082 static inline u64 nsl_get_checksum(struct nvdimm_drvdata *ndd,
7183 struct nd_namespace_label *nd_label)
7284 {
73− return __le64_to_cpu(nd_label->checksum);
85+ if (ndd->cxl)
86+ return __le64_to_cpu(nd_label->cxl.checksum);
87+ return __le64_to_cpu(nd_label->efi.checksum);
7488 }
7589
7690 static inline void nsl_set_checksum(struct nvdimm_drvdata *ndd,
7791 struct nd_namespace_label *nd_label,
7892 u64 checksum)
7993 {
80− nd_label->checksum = __cpu_to_le64(checksum);
94+ if (ndd->cxl)
95+ nd_label->cxl.checksum = __cpu_to_le64(checksum);
96+ else
97+ nd_label->efi.checksum = __cpu_to_le64(checksum);
8198 }
8299
83100 static inline u32 nsl_get_flags(struct nvdimm_drvdata *ndd,
84101 struct nd_namespace_label *nd_label)
85102 {
86− return __le32_to_cpu(nd_label->flags);
103+ if (ndd->cxl)
104+ return __le32_to_cpu(nd_label->cxl.flags);
105+ return __le32_to_cpu(nd_label->efi.flags);
87106 }
88107
89108 static inline void nsl_set_flags(struct nvdimm_drvdata *ndd,
90109 struct nd_namespace_label *nd_label, u32 flags)
91110 {
92− nd_label->flags = __cpu_to_le32(flags);
111+ if (ndd->cxl)
112+ nd_label->cxl.flags = __cpu_to_le32(flags);
113+ else
114+ nd_label->efi.flags = __cpu_to_le32(flags);
93115 }
94116
95117 static inline u64 nsl_get_dpa(struct nvdimm_drvdata *ndd,
96118 struct nd_namespace_label *nd_label)
97119 {
98− return __le64_to_cpu(nd_label->dpa);
120+ if (ndd->cxl)
121+ return __le64_to_cpu(nd_label->cxl.dpa);
122+ return __le64_to_cpu(nd_label->efi.dpa);
99123 }
100124
101125 static inline void nsl_set_dpa(struct nvdimm_drvdata *ndd,
102126 struct nd_namespace_label *nd_label, u64 dpa)
103127 {
104− nd_label->dpa = __cpu_to_le64(dpa);
128+ if (ndd->cxl)
129+ nd_label->cxl.dpa = __cpu_to_le64(dpa);
130+ else
131+ nd_label->efi.dpa = __cpu_to_le64(dpa);
105132 }
106133
107134 static inline u64 nsl_get_rawsize(struct nvdimm_drvdata *ndd,
108135 struct nd_namespace_label *nd_label)
109136 {
110− return __le64_to_cpu(nd_label->rawsize);
137+ if (ndd->cxl)
138+ return __le64_to_cpu(nd_label->cxl.rawsize);
139+ return __le64_to_cpu(nd_label->efi.rawsize);
111140 }
112141
113142 static inline void nsl_set_rawsize(struct nvdimm_drvdata *ndd,
114143 struct nd_namespace_label *nd_label,
115144 u64 rawsize)
116145 {
117− nd_label->rawsize = __cpu_to_le64(rawsize);
146+ if (ndd->cxl)
147+ nd_label->cxl.rawsize = __cpu_to_le64(rawsize);
148+ else
149+ nd_label->efi.rawsize = __cpu_to_le64(rawsize);
118150 }
119151
120152 static inline u64 nsl_get_isetcookie(struct nvdimm_drvdata *ndd,
121153 struct nd_namespace_label *nd_label)
122154 {
123− return __le64_to_cpu(nd_label->isetcookie);
155+ /* WARN future refactor attempts that break this assumption */
156+ if (dev_WARN_ONCE(ndd->dev, ndd->cxl,
157+ "CXL labels do not use the isetcookie concept\n"))
158+ return 0;
159+ return __le64_to_cpu(nd_label->efi.isetcookie);
124160 }
125161
126162 static inline void nsl_set_isetcookie(struct nvdimm_drvdata *ndd,
127163 struct nd_namespace_label *nd_label,
128164 u64 isetcookie)
129165 {
130− nd_label->isetcookie = __cpu_to_le64(isetcookie);
166+ if (!ndd->cxl)
167+ nd_label->efi.isetcookie = __cpu_to_le64(isetcookie);
131168 }
132169
133170 static inline bool nsl_validate_isetcookie(struct nvdimm_drvdata *ndd,
134171 struct nd_namespace_label *nd_label,
135172 u64 cookie)
136173 {
137− return cookie == __le64_to_cpu(nd_label->isetcookie);
174+ /*
175+ * Let the EFI and CXL validation comingle, where fields that
176+ * don't matter to CXL always validate.
177+ */
178+ if (ndd->cxl)
179+ return true;
180+ return cookie == __le64_to_cpu(nd_label->efi.isetcookie);
138181 }
139182
140183 static inline u16 nsl_get_position(struct nvdimm_drvdata *ndd,
141184 struct nd_namespace_label *nd_label)
142185 {
143− return __le16_to_cpu(nd_label->position);
186+ if (ndd->cxl)
187+ return __le16_to_cpu(nd_label->cxl.position);
188+ return __le16_to_cpu(nd_label->efi.position);
144189 }
145190
146191 static inline void nsl_set_position(struct nvdimm_drvdata *ndd,
147192 struct nd_namespace_label *nd_label,
148193 u16 position)
149194 {
150− nd_label->position = __cpu_to_le16(position);
195+ if (ndd->cxl)
196+ nd_label->cxl.position = __cpu_to_le16(position);
197+ else
198+ nd_label->efi.position = __cpu_to_le16(position);
151199 }
152200
153−
154201 static inline u16 nsl_get_nlabel(struct nvdimm_drvdata *ndd,
155202 struct nd_namespace_label *nd_label)
156203 {
157− return __le16_to_cpu(nd_label->nlabel);
204+ if (ndd->cxl)
205+ return 0;
206+ return __le16_to_cpu(nd_label->efi.nlabel);
158207 }
159208
160209 static inline void nsl_set_nlabel(struct nvdimm_drvdata *ndd,
161210 struct nd_namespace_label *nd_label,
162211 u16 nlabel)
163212 {
164− nd_label->nlabel = __cpu_to_le16(nlabel);
213+ if (!ndd->cxl)
214+ nd_label->efi.nlabel = __cpu_to_le16(nlabel);
215+}
216+
217+static inline u16 nsl_get_nrange(struct nvdimm_drvdata *ndd,
218+ struct nd_namespace_label *nd_label)
219+{
220+ if (ndd->cxl)
221+ return __le16_to_cpu(nd_label->cxl.nrange);
222+ return 1;
223+}
224+
225+static inline void nsl_set_nrange(struct nvdimm_drvdata *ndd,
226+ struct nd_namespace_label *nd_label,
227+ u16 nrange)
228+{
229+ if (ndd->cxl)
230+ nd_label->cxl.nrange = __cpu_to_le16(nrange);
165231 }
166232
167233 static inline u64 nsl_get_lbasize(struct nvdimm_drvdata *ndd,
168234 struct nd_namespace_label *nd_label)
169235 {
170− return __le64_to_cpu(nd_label->lbasize);
236+ /*
237+ * Yes, for some reason the EFI labels convey a massive 64-bit
238+ * lbasize, that got fixed for CXL.
239+ */
240+ if (ndd->cxl)
241+ return __le16_to_cpu(nd_label->cxl.lbasize);
242+ return __le64_to_cpu(nd_label->efi.lbasize);
171243 }
172244
173245 static inline void nsl_set_lbasize(struct nvdimm_drvdata *ndd,
174246 struct nd_namespace_label *nd_label,
175247 u64 lbasize)
176248 {
177− nd_label->lbasize = __cpu_to_le64(lbasize);
249+ if (ndd->cxl)
250+ nd_label->cxl.lbasize = __cpu_to_le16(lbasize);
251+ else
252+ nd_label->efi.lbasize = __cpu_to_le64(lbasize);
253+}
254+
255+static inline const uuid_t *nsl_get_uuid(struct nvdimm_drvdata *ndd,
256+ struct nd_namespace_label *nd_label,
257+ uuid_t *uuid)
258+{
259+ if (ndd->cxl)
260+ import_uuid(uuid, nd_label->cxl.uuid);
261+ else
262+ import_uuid(uuid, nd_label->efi.uuid);
263+ return uuid;
264+}
265+
266+static inline const uuid_t *nsl_set_uuid(struct nvdimm_drvdata *ndd,
267+ struct nd_namespace_label *nd_label,
268+ const uuid_t *uuid)
269+{
270+ if (ndd->cxl)
271+ export_uuid(nd_label->cxl.uuid, uuid);
272+ else
273+ export_uuid(nd_label->efi.uuid, uuid);
274+ return uuid;
275+}
276+
277+static inline bool nsl_uuid_equal(struct nvdimm_drvdata *ndd,
278+ struct nd_namespace_label *nd_label,
279+ const uuid_t *uuid)
280+{
281+ uuid_t tmp;
282+
283+ if (ndd->cxl)
284+ import_uuid(&tmp, nd_label->cxl.uuid);
285+ else
286+ import_uuid(&tmp, nd_label->efi.uuid);
287+ return uuid_equal(&tmp, uuid);
288+}
289+
290+static inline const u8 *nsl_uuid_raw(struct nvdimm_drvdata *ndd,
291+ struct nd_namespace_label *nd_label)
292+{
293+ if (ndd->cxl)
294+ return nd_label->cxl.uuid;
295+ return nd_label->efi.uuid;
178296 }
179297
180298 bool nsl_validate_blk_isetcookie(struct nvdimm_drvdata *ndd,
@@ -233,8 +351,8 @@ static inline struct nd_namespace_index *to_next_namespace_index( · 52 unmodified lines
233351
234352 unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd);
235353
236−#define namespace_label_has(ndd, field) \
237− (offsetof(struct nd_namespace_label, field) \
354+#define efi_namespace_label_has(ndd, field) \
355+ (!ndd->cxl && offsetof(struct nvdimm_efi_label, field) \
238356 < sizeof_namespace_label(ndd))
239357
240358 #define nd_dbg_dpa(r, d, res, fmt, arg...) \
@@ -310,6 +428,15 @@ struct nd_region { · 69 unmodified lines
310428 struct nd_mapping mapping[];
311429 };
312430
431+static inline bool nsl_validate_nlabel(struct nd_region *nd_region,
432+ struct nvdimm_drvdata *ndd,
433+ struct nd_namespace_label *nd_label)
434+{
435+ if (ndd->cxl)
436+ return true;
437+ return nsl_get_nlabel(ndd, nd_label) == nd_region->ndr_mappings;
438+}
439+
313440 struct nd_blk_region {
314441 int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev);
315442 int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
@@ -335,7 +462,7 @@ struct nd_btt { · 19 unmodified lines
335462 struct btt *btt;
336463 unsigned long lbasize;
337464 u64 size;
338− u8 *uuid;
465+ uuid_t *uuid;
339466 int id;
340467 int initial_offset;
341468 u16 version_major;
@@ -350,7 +477,7 @@ enum nd_pfn_mode { · 8 unmodified lines
350477
351478 struct nd_pfn {
352479 int id;
353− u8 *uuid;
480+ uuid_t *uuid;
354481 struct device dev;
355482 unsigned long align;
356483 unsigned long npfns;
@@ -378,7 +505,7 @@ void wait_nvdimm_bus_probe_idle(struct device *dev); · 21 unmodified lines
378505 void nd_device_register(struct device *dev);
379506 void nd_device_unregister(struct device *dev, enum nd_async_mode mode);
380507 void nd_device_notify(struct device *dev, enum nvdimm_event event);
381−int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,
508+int nd_uuid_store(struct device *dev, uuid_t **uuid_out, const char *buf,
382509 size_t len);
383510 ssize_t nd_size_select_show(unsigned long current_size,
384511 const unsigned long *supported, char *buf);
@@ -561,6 +688,6 @@ static inline bool is_bad_pmem(struct badblocks *bb, sector_t sector, · 176 unmodified lines
561688 return false;
562689 }
563690 resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk);
564−const u8 *nd_dev_to_uuid(struct device *dev);
691+const uuid_t *nd_dev_to_uuid(struct device *dev);
565692 bool pmem_should_map_pages(struct device *dev);
566693 #endif /* __ND_H__ */
drivers/nvdimm/pfn_devs.c+1−1
@@ -452,7 +452,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig) · 451 unmodified lines
452452 unsigned long align, start_pad;
453453 struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;
454454 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);
456456
457457 if (!pfn_sb || !ndns)
458458 return -ENODEV;
drivers/pci/pci.c+32−0
@@ -732,6 +732,38 @@ u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap) · 731 unmodified lines
732732 }
733733 EXPORT_SYMBOL_GPL(pci_find_vsec_capability);
734734
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+
735767 /**
736768 * pci_find_parent_resource - return resource region of parent bus of given
737769 * region
include/linux/nd.h+2−2
@@ -88,7 +88,7 @@ struct nd_namespace_pmem { · 87 unmodified lines
8888 struct nd_namespace_io nsio;
8989 unsigned long lbasize;
9090 char *alt_name;
91− u8 *uuid;
91+ uuid_t *uuid;
9292 int id;
9393 };
9494
@@ -105,7 +105,7 @@ struct nd_namespace_pmem { · 10 unmodified lines
105105 struct nd_namespace_blk {
106106 struct nd_namespace_common common;
107107 char *alt_name;
108− u8 *uuid;
108+ uuid_t *uuid;
109109 int id;
110110 unsigned long lbasize;
111111 resource_size_t size;
include/linux/pci.h+1−0
@@ -1132,6 +1132,7 @@ u16 pci_find_ext_capability(struct pci_dev *dev, int cap); · 1131 unmodified lines
11321132 u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 pos, int cap);
11331133 struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
11341134 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);
11351136
11361137 u64 pci_get_dsn(struct pci_dev *dev);
11371138
tools/testing/cxl/Kbuild+38−0
@@ -0,0 +1,38 @@
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/
tools/testing/cxl/config_check.c+13−0
@@ -0,0 +1,13 @@
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+}
tools/testing/cxl/mock_acpi.c+109−0
@@ -0,0 +1,109 @@
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+}
tools/testing/cxl/mock_pmem.c+24−0
@@ -0,0 +1,24 @@
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+}
tools/testing/cxl/test/Kbuild+10−0
@@ -0,0 +1,10 @@
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
tools/testing/cxl/test/cxl.c+576−0
@@ -0,0 +1,576 @@
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");
tools/testing/cxl/test/mem.c+256−0
@@ -0,0 +1,256 @@
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);
tools/testing/cxl/test/mock.c+171−0
@@ -0,0 +1,171 @@
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");
tools/testing/cxl/test/mock.h+27−0
@@ -0,0 +1,27 @@
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);