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
39.. kernel-doc:: drivers/cxl/core/bus.c39.. kernel-doc:: drivers/cxl/core/bus.c
40 :doc: cxl core40 :doc: cxl core
41 41
42.. kernel-doc:: drivers/cxl/core/bus.c
43 :identifiers:
44
42.. kernel-doc:: drivers/cxl/core/pmem.c45.. kernel-doc:: drivers/cxl/core/pmem.c
43 :doc: cxl pmem46 :doc: cxl pmem
44 47
45.. kernel-doc:: drivers/cxl/core/regs.c48.. kernel-doc:: drivers/cxl/core/regs.c
46 :doc: cxl registers49 :doc: cxl registers
47 50
51.. kernel-doc:: drivers/cxl/core/mbox.c
52 :doc: cxl mbox
53
48External Interfaces54External Interfaces
49===================55===================
50 56
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
107 int pos;107 int pos;
108 u32 val;108 u32 val;
109 109
110 pos = find_dvsec_from_pos(dev, OCXL_DVSEC_FUNC_ID, 0);110 pos = pci_find_dvsec_capability(dev, PCI_VENDOR_ID_IBM,
111 OCXL_DVSEC_FUNC_ID);
111 if (!pos)112 if (!pos)
112 return -ESRCH;113 return -ESRCH;
113 114
drivers/cxl/acpi.c+97−42
@@ -52,6 +52,12 @@ static int cxl_acpi_cfmws_verify(struct device *dev, · 51 unmodified lines
52 return -EINVAL;52 return -EINVAL;
53 }53 }
54 54
55 if (CFMWS_INTERLEAVE_WAYS(cfmws) > CXL_DECODER_MAX_INTERLEAVE) {
56 dev_err(dev, "CFMWS Interleave Ways (%d) too large\n",
57 CFMWS_INTERLEAVE_WAYS(cfmws));
58 return -EINVAL;
59 }
60
55 expected_len = struct_size((cfmws), interleave_targets,61 expected_len = struct_size((cfmws), interleave_targets,
56 CFMWS_INTERLEAVE_WAYS(cfmws));62 CFMWS_INTERLEAVE_WAYS(cfmws));
57 63
@@ -71,11 +77,11 @@ static int cxl_acpi_cfmws_verify(struct device *dev, · 13 unmodified lines
71static void cxl_add_cfmws_decoders(struct device *dev,77static void cxl_add_cfmws_decoders(struct device *dev,
72 struct cxl_port *root_port)78 struct cxl_port *root_port)
73{79{
80 int target_map[CXL_DECODER_MAX_INTERLEAVE];
74 struct acpi_cedt_cfmws *cfmws;81 struct acpi_cedt_cfmws *cfmws;
75 struct cxl_decoder *cxld;82 struct cxl_decoder *cxld;
76 acpi_size len, cur = 0;83 acpi_size len, cur = 0;
77 void *cedt_subtable;84 void *cedt_subtable;
78 unsigned long flags;
79 int rc;85 int rc;
80 86
81 len = acpi_cedt->length - sizeof(*acpi_cedt);87 len = acpi_cedt->length - sizeof(*acpi_cedt);
@@ -83,6 +89,7 @@ static void cxl_add_cfmws_decoders(struct device *dev, · 1 unmodified line
83 89
84 while (cur < len) {90 while (cur < len) {
85 struct acpi_cedt_header *c = cedt_subtable + cur;91 struct acpi_cedt_header *c = cedt_subtable + cur;
92 int i;
86 93
87 if (c->type != ACPI_CEDT_TYPE_CFMWS) {94 if (c->type != ACPI_CEDT_TYPE_CFMWS) {
88 cur += c->length;95 cur += c->length;
@@ -108,24 +115,39 @@ static void cxl_add_cfmws_decoders(struct device *dev, · 19 unmodified lines
108 continue;115 continue;
109 }116 }
110 117
111 flags = cfmws_to_decoder_flags(cfmws->restrictions);118 for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
112 cxld = devm_cxl_add_decoder(dev, root_port,119 target_map[i] = cfmws->interleave_targets[i];
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);
119 120
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) {
121 dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",142 dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
122 cfmws->base_hpa, cfmws->base_hpa +143 cfmws->base_hpa, cfmws->base_hpa +
123 cfmws->window_size - 1);144 cfmws->window_size - 1);
124 } else {145 goto next;
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);
128 }146 }
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);
150next:
129 cur += c->length;151 cur += c->length;
130 }152 }
131}153}
@@ -182,15 +204,7 @@ static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs) · 50 unmodified lines
182 return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;204 return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
183}205}
184 206
185struct cxl_walk_context {207__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
186 struct device *dev;
187 struct pci_bus *root;
188 struct cxl_port *port;
189 int error;
190 int count;
191};
192
193static int match_add_root_ports(struct pci_dev *pdev, void *data)
194{208{
195 struct cxl_walk_context *ctx = data;209 struct cxl_walk_context *ctx = data;
196 struct pci_bus *root_bus = ctx->root;210 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
239 return NULL;253 return NULL;
240}254}
241 255
242static 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)
243{258{
244 struct acpi_device *adev = to_acpi_device(dev);259 struct acpi_device *adev = to_acpi_device(dev);
245 260
@@ -257,11 +272,12 @@ static struct acpi_device *to_cxl_host_bridge(struct device *dev) · 11 unmodified lines
257 */272 */
258static int add_host_bridge_uport(struct device *match, void *arg)273static int add_host_bridge_uport(struct device *match, void *arg)
259{274{
260 struct acpi_device *bridge = to_cxl_host_bridge(match);
261 struct cxl_port *root_port = arg;275 struct cxl_port *root_port = arg;
262 struct device *host = root_port->dev.parent;276 struct device *host = root_port->dev.parent;
277 struct acpi_device *bridge = to_cxl_host_bridge(host, match);
263 struct acpi_pci_root *pci_root;278 struct acpi_pci_root *pci_root;
264 struct cxl_walk_context ctx;279 struct cxl_walk_context ctx;
280 int single_port_map[1], rc;
265 struct cxl_decoder *cxld;281 struct cxl_decoder *cxld;
266 struct cxl_dport *dport;282 struct cxl_dport *dport;
267 struct cxl_port *port;283 struct cxl_port *port;
@@ -272,7 +288,7 @@ static int add_host_bridge_uport(struct device *match, void *arg) · 4 unmodified lines
272 dport = find_dport_by_dev(root_port, match);288 dport = find_dport_by_dev(root_port, match);
273 if (!dport) {289 if (!dport) {
274 dev_dbg(host, "host bridge expected and not found\n");290 dev_dbg(host, "host bridge expected and not found\n");
275 return -ENODEV;291 return 0;
276 }292 }
277 293
278 port = devm_cxl_add_port(host, match, dport->component_reg_phys,294 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
297 return -ENODEV;313 return -ENODEV;
298 if (ctx.error)314 if (ctx.error)
299 return ctx.error;315 return ctx.error;
316 if (ctx.count > 1)
317 return 0;
300 318
301 /* TODO: Scan CHBCR for HDM Decoder resources */319 /* TODO: Scan CHBCR for HDM Decoder resources */
302 320
303 /*321 /*
304 * In the single-port host-bridge case there are no HDM decoders322 * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability
305 * in the CHBCR and a 1:1 passthrough decode is implied.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.
306 */328 */
307 if (ctx.count == 1) {329 cxld = cxl_decoder_alloc(port, 1);
308 cxld = devm_cxl_add_passthrough_decoder(host, port);330 if (IS_ERR(cxld))
309 if (IS_ERR(cxld))331 return PTR_ERR(cxld);
310 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 };
311 340
312 dev_dbg(host, "add: %s\n", dev_name(&cxld->dev));341 device_lock(&port->dev);
313 }342 dport = list_first_entry(&port->dports, typeof(*dport), list);
343 device_unlock(&port->dev);
314 344
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;
316}356}
317 357
318static int add_host_bridge_dport(struct device *match, void *arg)358static 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
323 struct acpi_cedt_chbs *chbs;363 struct acpi_cedt_chbs *chbs;
324 struct cxl_port *root_port = arg;364 struct cxl_port *root_port = arg;
325 struct device *host = root_port->dev.parent;365 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);
327 367
328 if (!bridge)368 if (!bridge)
329 return 0;369 return 0;
@@ -337,9 +377,11 @@ static int add_host_bridge_dport(struct device *match, void *arg) · 7 unmodified lines
337 }377 }
338 378
339 chbs = cxl_acpi_match_chbs(host, uid);379 chbs = cxl_acpi_match_chbs(host, uid);
340 if (IS_ERR(chbs))380 if (IS_ERR(chbs)) {
341 dev_dbg(host, "No CHBS found for Host Bridge: %s\n",381 dev_warn(host, "No CHBS found for Host Bridge: %s\n",
342 dev_name(match));382 dev_name(match));
383 return 0;
384 }
343 385
344 rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));386 rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));
345 if (rc) {387 if (rc) {
@@ -375,6 +417,17 @@ static int add_root_nvdimm_bridge(struct device *match, void *data) · 29 unmodified lines
375 return 1;417 return 1;
376}418}
377 419
420static 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
378static int cxl_acpi_probe(struct platform_device *pdev)431static int cxl_acpi_probe(struct platform_device *pdev)
379{432{
380 int rc;433 int rc;
@@ -388,7 +441,7 @@ static int cxl_acpi_probe(struct platform_device *pdev) · 7 unmodified lines
388 return PTR_ERR(root_port);441 return PTR_ERR(root_port);
389 dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));442 dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
390 443
391 status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt);444 status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt);
392 if (ACPI_FAILURE(status))445 if (ACPI_FAILURE(status))
393 return -ENXIO;446 return -ENXIO;
394 447
@@ -419,9 +472,11 @@ out: · 24 unmodified lines
419 return 0;472 return 0;
420}473}
421 474
475static bool native_acpi0017 = true;
476
422static const struct acpi_device_id cxl_acpi_ids[] = {477static const struct acpi_device_id cxl_acpi_ids[] = {
423 { "ACPI0017", 0 },478 { "ACPI0017", (unsigned long) &native_acpi0017 },
424 { "", 0 },479 { },
425};480};
426MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);481MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
427 482
drivers/cxl/core/Makefile+1−0
@@ -6,3 +6,4 @@ cxl_core-y := bus.o · 5 unmodified lines
6cxl_core-y += pmem.o6cxl_core-y += pmem.o
7cxl_core-y += regs.o7cxl_core-y += regs.o
8cxl_core-y += memdev.o8cxl_core-y += memdev.o
9cxl_core-y += mbox.o
drivers/cxl/core/bus.c+68−51
@@ -453,50 +453,57 @@ err: · 452 unmodified lines
453}453}
454EXPORT_SYMBOL_GPL(cxl_add_dport);454EXPORT_SYMBOL_GPL(cxl_add_dport);
455 455
456static struct cxl_decoder *456static int decoder_populate_targets(struct cxl_decoder *cxld,
457cxl_decoder_alloc(struct cxl_port *port, int nr_targets, resource_size_t base,457 struct cxl_port *port, int *target_map)
458 resource_size_t len, int interleave_ways,
459 int interleave_granularity, enum cxl_decoder_type type,
460 unsigned long flags)
461{458{
462 struct cxl_decoder *cxld;459 int rc = 0, i;
463 struct device *dev;
464 int rc = 0;
465 460
466 if (interleave_ways < 1)461 if (!target_map)
467 return ERR_PTR(-EINVAL);462 return 0;
468 463
469 device_lock(&port->dev);464 device_lock(&port->dev);
470 if (list_empty(&port->dports))465 if (list_empty(&port->dports)) {
471 rc = -EINVAL;466 rc = -EINVAL;
467 goto out_unlock;
468 }
469
470 for (i = 0; i < cxld->nr_targets; i++) {
471 struct cxl_dport *dport = find_dport(port, target_map[i]);
472
473 if (!dport) {
474 rc = -ENXIO;
475 goto out_unlock;
476 }
477 cxld->target[i] = dport;
478 }
479
480out_unlock:
472 device_unlock(&port->dev);481 device_unlock(&port->dev);
473 if (rc)482
474 return ERR_PTR(rc);483 return rc;
484}
485
486struct 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);
475 496
476 cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);497 cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
477 if (!cxld)498 if (!cxld)
478 return ERR_PTR(-ENOMEM);499 return ERR_PTR(-ENOMEM);
500 memcpy(cxld, &cxld_const_init, sizeof(cxld_const_init));
479 501
480 rc = ida_alloc(&port->decoder_ida, GFP_KERNEL);502 rc = ida_alloc(&port->decoder_ida, GFP_KERNEL);
481 if (rc < 0)503 if (rc < 0)
482 goto err;504 goto err;
483 505
484 *cxld = (struct cxl_decoder) {506 cxld->id = rc;
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);
500 dev = &cxld->dev;507 dev = &cxld->dev;
501 device_initialize(dev);508 device_initialize(dev);
502 device_set_pm_not_required(dev);509 device_set_pm_not_required(dev);
@@ -514,41 +521,47 @@ err: · 11 unmodified lines
514 kfree(cxld);521 kfree(cxld);
515 return ERR_PTR(rc);522 return ERR_PTR(rc);
516}523}
524EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
517 525
518struct cxl_decoder *526int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map)
519devm_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)
523{527{
524 struct cxl_decoder *cxld;528 struct cxl_port *port;
525 struct device *dev;529 struct device *dev;
526 int rc;530 int rc;
527 531
528 cxld = cxl_decoder_alloc(port, nr_targets, base, len, interleave_ways,532 if (WARN_ON_ONCE(!cxld))
529 interleave_granularity, type, flags);533 return -EINVAL;
530 if (IS_ERR(cxld))534
531 return cxld;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;
532 545
533 dev = &cxld->dev;546 dev = &cxld->dev;
534 rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);547 rc = dev_set_name(dev, "decoder%d.%d", port->id, cxld->id);
535 if (rc)548 if (rc)
536 goto err;549 return rc;
537 550
538 rc = device_add(dev);551 return device_add(dev);
539 if (rc)552}
540 goto err;553EXPORT_SYMBOL_GPL(cxl_decoder_add);
541 554
542 rc = devm_add_action_or_reset(host, unregister_cxl_dev, dev);555static void cxld_unregister(void *dev)
543 if (rc)556{
544 return ERR_PTR(rc);557 device_unregister(dev);
545 return cxld;558}
546 559
547err:560int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
548 put_device(dev);561{
549 return ERR_PTR(rc);562 return devm_add_action_or_reset(host, cxld_unregister, &cxld->dev);
550}563}
551EXPORT_SYMBOL_GPL(devm_cxl_add_decoder);564EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
552 565
553/**566/**
554 * __cxl_driver_register - register a driver for the cxl bus567 * __cxl_driver_register - register a driver for the cxl bus
@@ -635,6 +648,8 @@ static __init int cxl_core_init(void) · 80 unmodified lines
635{648{
636 int rc;649 int rc;
637 650
651 cxl_mbox_init();
652
638 rc = cxl_memdev_init();653 rc = cxl_memdev_init();
639 if (rc)654 if (rc)
640 return rc;655 return rc;
@@ -646,6 +661,7 @@ static __init int cxl_core_init(void) · 5 unmodified lines
646 661
647err:662err:
648 cxl_memdev_exit();663 cxl_memdev_exit();
664 cxl_mbox_exit();
649 return rc;665 return rc;
650}666}
651 667
@@ -653,6 +669,7 @@ static void cxl_core_exit(void) · 1 unmodified line
653{669{
654 bus_unregister(&cxl_bus_type);670 bus_unregister(&cxl_bus_type);
655 cxl_memdev_exit();671 cxl_memdev_exit();
672 cxl_mbox_exit();
656}673}
657 674
658module_init(cxl_core_init);675module_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
9 9
10extern struct attribute_group cxl_base_attribute_group;10extern struct attribute_group cxl_base_attribute_group;
11 11
12static inline void unregister_cxl_dev(void *dev)12struct cxl_send_command;
13{13struct cxl_mem_query_commands;
14 device_unregister(dev);14int cxl_query_cmd(struct cxl_memdev *cxlmd,
15}15 struct cxl_mem_query_commands __user *q);
16int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s);
16 17
17int cxl_memdev_init(void);18int cxl_memdev_init(void);
18void cxl_memdev_exit(void);19void cxl_memdev_exit(void);
20void cxl_mbox_init(void);
21void cxl_mbox_exit(void);
19 22
20#endif /* __CXL_CORE_H__ */23#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
12static 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 */
44static 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 */
88static 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 */
103static u8 security_command_sets[] = {
104 0x44, /* Sanitize */
105 0x45, /* Persistent Memory Data-at-rest Security */
106 0x46, /* Security Passthrough */
107};
108
109static 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
119static 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 */
154int 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}
187EXPORT_SYMBOL_GPL(cxl_mem_mbox_send_cmd);
188
189static 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 */
231static 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
323int 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 */
382static 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
442out:
443 kvfree(mbox_cmd.payload_in);
444 kvfree(mbox_cmd.payload_out);
445 return rc;
446}
447
448int 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
481static 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 */
517static 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
539static 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
558enum {
559 CEL_UUID,
560 VENDOR_DEBUG_UUID,
561};
562
563/* See CXL 2.0 Table 170. Get Log Input Payload */
564static 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 */
579int 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
625out:
626 kvfree(gsl);
627 return rc;
628}
629EXPORT_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 */
643static 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 */
680int 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}
714EXPORT_SYMBOL_GPL(cxl_mem_identify);
715
716int 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}
753EXPORT_SYMBOL_GPL(cxl_mem_create_range_info);
754
755struct 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}
770EXPORT_SYMBOL_GPL(cxl_mem_create);
771
772static struct dentry *cxl_debugfs;
773
774void __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
784void 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
8#include <cxlmem.h>8#include <cxlmem.h>
9#include "core.h"9#include "core.h"
10 10
11static DECLARE_RWSEM(cxl_memdev_rwsem);
12
11/*13/*
12 * An entire PCI topology full of devices should be enough for any14 * An entire PCI topology full of devices should be enough for any
13 * config15 * config
@@ -132,16 +134,53 @@ static const struct device_type cxl_memdev_type = { · 118 unmodified lines
132 .groups = cxl_memdev_attribute_groups,134 .groups = cxl_memdev_attribute_groups,
133};135};
134 136
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 */
146void 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}
153EXPORT_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 */
160void 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}
167EXPORT_SYMBOL_GPL(clear_exclusive_cxl_commands);
168
169static 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
135static void cxl_memdev_unregister(void *_cxlmd)178static void cxl_memdev_unregister(void *_cxlmd)
136{179{
137 struct cxl_memdev *cxlmd = _cxlmd;180 struct cxl_memdev *cxlmd = _cxlmd;
138 struct device *dev = &cxlmd->dev;181 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);
144 182
183 cxl_memdev_shutdown(dev);
145 cdev_device_del(&cxlmd->cdev, dev);184 cdev_device_del(&cxlmd->cdev, dev);
146 put_device(dev);185 put_device(dev);
147}186}
@@ -149,7 +188,6 @@ static void cxl_memdev_unregister(void *_cxlmd) · 1 unmodified line
149static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,188static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
150 const struct file_operations *fops)189 const struct file_operations *fops)
151{190{
152 struct pci_dev *pdev = cxlm->pdev;
153 struct cxl_memdev *cxlmd;191 struct cxl_memdev *cxlmd;
154 struct device *dev;192 struct device *dev;
155 struct cdev *cdev;193 struct cdev *cdev;
@@ -166,7 +204,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm, · 10 unmodified lines
166 204
167 dev = &cxlmd->dev;205 dev = &cxlmd->dev;
168 device_initialize(dev);206 device_initialize(dev);
169 dev->parent = &pdev->dev;207 dev->parent = cxlm->dev;
170 dev->bus = &cxl_bus_type;208 dev->bus = &cxl_bus_type;
171 dev->devt = MKDEV(cxl_mem_major, cxlmd->id);209 dev->devt = MKDEV(cxl_mem_major, cxlmd->id);
172 dev->type = &cxl_memdev_type;210 dev->type = &cxl_memdev_type;
@@ -181,16 +219,72 @@ err: · 8 unmodified lines
181 return ERR_PTR(rc);219 return ERR_PTR(rc);
182}220}
183 221
222static 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
235static 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
249static 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
260static 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
270static 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
184struct cxl_memdev *279struct cxl_memdev *
185devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm,280devm_cxl_add_memdev(struct cxl_mem *cxlm)
186 const struct cdevm_file_operations *cdevm_fops)
187{281{
188 struct cxl_memdev *cxlmd;282 struct cxl_memdev *cxlmd;
189 struct device *dev;283 struct device *dev;
190 struct cdev *cdev;284 struct cdev *cdev;
191 int rc;285 int rc;
192 286
193 cxlmd = cxl_memdev_alloc(cxlm, &cdevm_fops->fops);287 cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
194 if (IS_ERR(cxlmd))288 if (IS_ERR(cxlmd))
195 return cxlmd;289 return cxlmd;
196 290
@@ -210,7 +304,7 @@ devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm, · 13 unmodified lines
210 if (rc)304 if (rc)
211 goto err;305 goto err;
212 306
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);
214 if (rc)308 if (rc)
215 return ERR_PTR(rc);309 return ERR_PTR(rc);
216 return cxlmd;310 return cxlmd;
@@ -220,7 +314,7 @@ err: · 3 unmodified lines
220 * The cdev was briefly live, shutdown any ioctl operations that314 * The cdev was briefly live, shutdown any ioctl operations that
221 * saw that state.315 * saw that state.
222 */316 */
223 cdevm_fops->shutdown(dev);317 cxl_memdev_shutdown(dev);
224 put_device(dev);318 put_device(dev);
225 return ERR_PTR(rc);319 return ERR_PTR(rc);
226}320}
drivers/cxl/core/pmem.c+37−2
@@ -2,6 +2,7 @@ · 1 unmodified line
2/* Copyright(c) 2020 Intel Corporation. */2/* Copyright(c) 2020 Intel Corporation. */
3#include <linux/device.h>3#include <linux/device.h>
4#include <linux/slab.h>4#include <linux/slab.h>
5#include <linux/idr.h>
5#include <cxlmem.h>6#include <cxlmem.h>
6#include <cxl.h>7#include <cxl.h>
7#include "core.h"8#include "core.h"
@@ -20,10 +21,13 @@ · 12 unmodified lines
20 * operations, for example, namespace label access commands.21 * operations, for example, namespace label access commands.
21 */22 */
22 23
24static DEFINE_IDA(cxl_nvdimm_bridge_ida);
25
23static void cxl_nvdimm_bridge_release(struct device *dev)26static void cxl_nvdimm_bridge_release(struct device *dev)
24{27{
25 struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);28 struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
26 29
30 ida_free(&cxl_nvdimm_bridge_ida, cxl_nvb->id);
27 kfree(cxl_nvb);31 kfree(cxl_nvb);
28}32}
29 33
@@ -47,16 +51,38 @@ struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev) · 17 unmodified lines
47}51}
48EXPORT_SYMBOL_GPL(to_cxl_nvdimm_bridge);52EXPORT_SYMBOL_GPL(to_cxl_nvdimm_bridge);
49 53
54__mock int match_nvdimm_bridge(struct device *dev, const void *data)
55{
56 return dev->type == &cxl_nvdimm_bridge_type;
57}
58
59struct 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}
68EXPORT_SYMBOL_GPL(cxl_find_nvdimm_bridge);
69
50static struct cxl_nvdimm_bridge *70static struct cxl_nvdimm_bridge *
51cxl_nvdimm_bridge_alloc(struct cxl_port *port)71cxl_nvdimm_bridge_alloc(struct cxl_port *port)
52{72{
53 struct cxl_nvdimm_bridge *cxl_nvb;73 struct cxl_nvdimm_bridge *cxl_nvb;
54 struct device *dev;74 struct device *dev;
75 int rc;
55 76
56 cxl_nvb = kzalloc(sizeof(*cxl_nvb), GFP_KERNEL);77 cxl_nvb = kzalloc(sizeof(*cxl_nvb), GFP_KERNEL);
57 if (!cxl_nvb)78 if (!cxl_nvb)
58 return ERR_PTR(-ENOMEM);79 return ERR_PTR(-ENOMEM);
59 80
81 rc = ida_alloc(&cxl_nvdimm_bridge_ida, GFP_KERNEL);
82 if (rc < 0)
83 goto err;
84 cxl_nvb->id = rc;
85
60 dev = &cxl_nvb->dev;86 dev = &cxl_nvb->dev;
61 cxl_nvb->port = port;87 cxl_nvb->port = port;
62 cxl_nvb->state = CXL_NVB_NEW;88 cxl_nvb->state = CXL_NVB_NEW;
@@ -67,6 +93,10 @@ cxl_nvdimm_bridge_alloc(struct cxl_port *port) · 4 unmodified lines
67 dev->type = &cxl_nvdimm_bridge_type;93 dev->type = &cxl_nvdimm_bridge_type;
68 94
69 return cxl_nvb;95 return cxl_nvb;
96
97err:
98 kfree(cxl_nvb);
99 return ERR_PTR(rc);
70}100}
71 101
72static void unregister_nvb(void *_cxl_nvb)102static 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
119 return cxl_nvb;149 return cxl_nvb;
120 150
121 dev = &cxl_nvb->dev;151 dev = &cxl_nvb->dev;
122 rc = dev_set_name(dev, "nvdimm-bridge");152 rc = dev_set_name(dev, "nvdimm-bridge%d", cxl_nvb->id);
123 if (rc)153 if (rc)
124 goto err;154 goto err;
125 155
@@ -192,6 +222,11 @@ static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_memdev *cxlmd) · 66 unmodified lines
192 return cxl_nvd;222 return cxl_nvd;
193}223}
194 224
225static void cxl_nvd_unregister(void *dev)
226{
227 device_unregister(dev);
228}
229
195/**230/**
196 * devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm231 * devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm
197 * @host: same host as @cxlmd232 * @host: same host as @cxlmd
@@ -221,7 +256,7 @@ int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd) · 23 unmodified lines
221 dev_dbg(host, "%s: register %s\n", dev_name(dev->parent),256 dev_dbg(host, "%s: register %s\n", dev_name(dev->parent),
222 dev_name(dev));257 dev_name(dev));
223 258
224 return devm_add_action_or_reset(host, unregister_cxl_dev, dev);259 return devm_add_action_or_reset(host, cxl_nvd_unregister, dev);
225 260
226err:261err:
227 put_device(dev);262 put_device(dev);
drivers/cxl/cxl.h+39−19
@@ -114,7 +114,17 @@ struct cxl_device_reg_map { · 113 unmodified lines
114 struct cxl_reg_map memdev;114 struct cxl_reg_map memdev;
115};115};
116 116
117/**
118 * struct cxl_register_map - DVSEC harvested register block mapping parameters
119 * @base: virtual base of the register-block-BAR + @block_offset
120 * @block_offset: offset to start of register block in @barno
121 * @reg_type: see enum cxl_regloc_type
122 * @barno: PCI BAR number containing the register block
123 * @component_map: cxl_reg_map for component registers
124 * @device_map: cxl_reg_maps for device registers
125 */
117struct cxl_register_map {126struct cxl_register_map {
127 void __iomem *base;
118 u64 block_offset;128 u64 block_offset;
119 u8 reg_type;129 u8 reg_type;
120 u8 barno;130 u8 barno;
@@ -155,6 +165,12 @@ enum cxl_decoder_type { · 34 unmodified lines
155 CXL_DECODER_EXPANDER = 3,165 CXL_DECODER_EXPANDER = 3,
156};166};
157 167
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
158/**174/**
159 * struct cxl_decoder - CXL address range decode configuration175 * struct cxl_decoder - CXL address range decode configuration
160 * @dev: this decoder's device176 * @dev: this decoder's device
@@ -164,6 +180,7 @@ enum cxl_decoder_type { · 3 unmodified lines
164 * @interleave_granularity: data stride per dport180 * @interleave_granularity: data stride per dport
165 * @target_type: accelerator vs expander (type2 vs type3) selector181 * @target_type: accelerator vs expander (type2 vs type3) selector
166 * @flags: memory type capabilities and locking182 * @flags: memory type capabilities and locking
183 * @nr_targets: number of elements in @target
167 * @target: active ordered target list in current decoder configuration184 * @target: active ordered target list in current decoder configuration
168 */185 */
169struct cxl_decoder {186struct cxl_decoder {
@@ -174,6 +191,7 @@ struct cxl_decoder { · 4 unmodified lines
174 int interleave_granularity;191 int interleave_granularity;
175 enum cxl_decoder_type target_type;192 enum cxl_decoder_type target_type;
176 unsigned long flags;193 unsigned long flags;
194 const int nr_targets;
177 struct cxl_dport *target[];195 struct cxl_dport *target[];
178};196};
179 197
@@ -186,6 +204,7 @@ enum cxl_nvdimm_brige_state { · 6 unmodified lines
186};204};
187 205
188struct cxl_nvdimm_bridge {206struct cxl_nvdimm_bridge {
207 int id;
189 struct device dev;208 struct device dev;
190 struct cxl_port *port;209 struct cxl_port *port;
191 struct nvdimm_bus *nvdimm_bus;210 struct nvdimm_bus *nvdimm_bus;
@@ -200,6 +219,14 @@ struct cxl_nvdimm { · 8 unmodified lines
200 struct nvdimm *nvdimm;219 struct nvdimm *nvdimm;
201};220};
202 221
222struct 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
203/**230/**
204 * struct cxl_port - logical collection of upstream port devices and231 * struct cxl_port - logical collection of upstream port devices and
205 * downstream port devices to construct a CXL memory232 * 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
246 273
247struct cxl_decoder *to_cxl_decoder(struct device *dev);274struct cxl_decoder *to_cxl_decoder(struct device *dev);
248bool is_root_decoder(struct device *dev);275bool is_root_decoder(struct device *dev);
249struct cxl_decoder *276struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets);
250devm_cxl_add_decoder(struct device *host, struct cxl_port *port, int nr_targets,277int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map);
251 resource_size_t base, resource_size_t len,278int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld);
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 */
262static inline struct cxl_decoder *
263devm_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}
268 279
269extern struct bus_type cxl_bus_type;280extern struct bus_type cxl_bus_type;
270 281
@@ -298,4 +309,13 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host, · 27 unmodified lines
298struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);309struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
299bool is_cxl_nvdimm(struct device *dev);310bool is_cxl_nvdimm(struct device *dev);
300int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);311int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);
312struct 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
301#endif /* __CXL_H__ */321#endif /* __CXL_H__ */
drivers/cxl/cxlmem.h+179−23
@@ -2,6 +2,7 @@ · 1 unmodified line
2/* Copyright(c) 2020-2021 Intel Corporation. */2/* Copyright(c) 2020-2021 Intel Corporation. */
3#ifndef __CXL_MEM_H__3#ifndef __CXL_MEM_H__
4#define __CXL_MEM_H__4#define __CXL_MEM_H__
5#include <uapi/linux/cxl_mem.h>
5#include <linux/cdev.h>6#include <linux/cdev.h>
6#include "cxl.h"7#include "cxl.h"
7 8
@@ -28,21 +29,6 @@ · 20 unmodified lines
28 (FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \29 (FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \
29 CXLMDEV_RESET_NEEDED_NOT)30 CXLMDEV_RESET_NEEDED_NOT)
30 31
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 */
41struct cdevm_file_operations {
42 struct file_operations fops;
43 void (*shutdown)(struct device *dev);
44};
45
46/**32/**
47 * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device33 * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device
48 * @dev: driver core device object34 * @dev: driver core device object
@@ -62,13 +48,50 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev) · 13 unmodified lines
62 return container_of(dev, struct cxl_memdev, dev);48 return container_of(dev, struct cxl_memdev, dev);
63}49}
64 50
65struct cxl_memdev *51struct cxl_memdev *devm_cxl_add_memdev(struct cxl_mem *cxlm);
66devm_cxl_add_memdev(struct device *host, struct cxl_mem *cxlm,52
67 const struct cdevm_file_operations *cdevm_fops);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 */
73struct 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
68 91
69/**92/**
70 * struct cxl_mem - A CXL memory device93 * 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.
72 * @cxlmd: Logical memory device chardev / interface95 * @cxlmd: Logical memory device chardev / interface
73 * @regs: Parsed register blocks96 * @regs: Parsed register blocks
74 * @payload_size: Size of space for payload97 * @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
78 * @mbox_mutex: Mutex to synchronize mailbox access.101 * @mbox_mutex: Mutex to synchronize mailbox access.
79 * @firmware_version: Firmware version for the memory device.102 * @firmware_version: Firmware version for the memory device.
80 * @enabled_cmds: Hardware commands found enabled in CEL.103 * @enabled_cmds: Hardware commands found enabled in CEL.
81 * @pmem_range: Persistent memory capacity information.104 * @exclusive_cmds: Commands that are kernel-internal only
82 * @ram_range: Volatile memory capacity information.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.
83 */119 */
84struct cxl_mem {120struct cxl_mem {
85 struct pci_dev *pdev;121 struct device *dev;
86 struct cxl_memdev *cxlmd;122 struct cxl_memdev *cxlmd;
87 123
88 struct cxl_regs regs;124 struct cxl_regs regs;
@@ -91,7 +127,8 @@ struct cxl_mem { · 2 unmodified lines
91 size_t lsa_size;127 size_t lsa_size;
92 struct mutex mbox_mutex; /* Protects device mailbox and firmware */128 struct mutex mbox_mutex; /* Protects device mailbox and firmware */
93 char firmware_version[0x10];129 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);
95 132
96 struct range pmem_range;133 struct range pmem_range;
97 struct range ram_range;134 struct range ram_range;
@@ -104,5 +141,124 @@ struct cxl_mem { · 6 unmodified lines
104 u64 active_persistent_bytes;141 u64 active_persistent_bytes;
105 u64 next_volatile_bytes;142 u64 next_volatile_bytes;
106 u64 next_persistent_bytes;143 u64 next_persistent_bytes;
144
145 int (*mbox_send)(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd);
146};
147
148enum 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
107};172};
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
182struct 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
191struct cxl_cel_entry {
192 __le16 opcode;
193 __le16 effect;
194} __packed;
195
196struct 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 */
203struct 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
220struct cxl_mbox_get_lsa {
221 u32 offset;
222 u32 length;
223} __packed;
224
225struct 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 */
248struct 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
256int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
257 size_t in_size, void *out, size_t out_size);
258int cxl_mem_identify(struct cxl_mem *cxlm);
259int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm);
260int cxl_mem_create_range_info(struct cxl_mem *cxlm);
261struct cxl_mem *cxl_mem_create(struct device *dev);
262void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds);
263void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds);
108#endif /* __CXL_MEM_H__ */264#endif /* __CXL_MEM_H__ */
drivers/cxl/pci.c+109−1131
@@ -1,17 +1,12 @@
1// SPDX-License-Identifier: GPL-2.0-only1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2020 Intel Corporation. All rights reserved. */2/* Copyright(c) 2020 Intel Corporation. All rights reserved. */
3#include <uapi/linux/cxl_mem.h>3#include <linux/io-64-nonatomic-lo-hi.h>
4#include <linux/security.h>
5#include <linux/debugfs.h>
6#include <linux/module.h>4#include <linux/module.h>
7#include <linux/sizes.h>5#include <linux/sizes.h>
8#include <linux/mutex.h>6#include <linux/mutex.h>
9#include <linux/list.h>7#include <linux/list.h>
10#include <linux/cdev.h>
11#include <linux/idr.h>
12#include <linux/pci.h>8#include <linux/pci.h>
13#include <linux/io.h>9#include <linux/io.h>
14#include <linux/io-64-nonatomic-lo-hi.h>
15#include "cxlmem.h"10#include "cxlmem.h"
16#include "pci.h"11#include "pci.h"
17#include "cxl.h"12#include "cxl.h"
@@ -21,14 +16,16 @@ · 3 unmodified lines
21 *16 *
22 * This implements the PCI exclusive functionality for a CXL device as it is17 * This implements the PCI exclusive functionality for a CXL device as it is
23 * defined by the Compute Express Link specification. CXL devices may surface18 * 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.
25 *23 *
26 * The driver has several responsibilities, mainly:24 * The driver has several responsibilities, mainly:
27 * - Create the memX device and register on the CXL bus.25 * - Create the memX device and register on the CXL bus.
28 * - Enumerate device's register interface and map them.26 * - Enumerate device's register interface and map them.
29 * - Probe the device attributes to establish sysfs interface.27 * - Registers nvdimm bridge device with cxl_core.
30 * - Provide an IOCTL interface to userspace to communicate with the device for28 * - Registers a CXL mailbox with cxl_core.
31 * things like firmware update.
32 */29 */
33 30
34#define cxl_doorbell_busy(cxlm) \31#define cxl_doorbell_busy(cxlm) \
@@ -38,202 +35,7 @@ · 3 unmodified lines
38/* CXL 2.0 - 8.2.8.4 */35/* CXL 2.0 - 8.2.8.4 */
39#define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)36#define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
40 37
41enum opcode {38static int cxl_pci_mbox_wait_for_doorbell(struct cxl_mem *cxlm)
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 */
96struct 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
106static DECLARE_RWSEM(cxl_memdev_rwsem);
107static struct dentry *cxl_debugfs;
108static bool cxl_raw_allow_all;
109
110enum {
111 CEL_UUID,
112 VENDOR_DEBUG_UUID,
113};
114
115/* See CXL 2.0 Table 170. Get Log Input Payload */
116static 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 */
140struct 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 */
165static 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 */
209static 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 */
224static 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
236static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm)
237{39{
238 const unsigned long start = jiffies;40 const unsigned long start = jiffies;
239 unsigned long end = start;41 unsigned long end = start;
@@ -250,32 +52,22 @@ static int cxl_mem_wait_for_doorbell(struct cxl_mem *cxlm) · 10 unmodified lines
250 cpu_relax();52 cpu_relax();
251 }53 }
252 54
253 dev_dbg(&cxlm->pdev->dev, "Doorbell wait took %dms",55 dev_dbg(cxlm->dev, "Doorbell wait took %dms",
254 jiffies_to_msecs(end) - jiffies_to_msecs(start));56 jiffies_to_msecs(end) - jiffies_to_msecs(start));
255 return 0;57 return 0;
256}58}
257 59
258static bool cxl_is_security_command(u16 opcode)60static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm,
61 struct cxl_mbox_cmd *mbox_cmd)
259{62{
260 int i;63 struct device *dev = cxlm->dev;
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
268static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm,
269 struct mbox_cmd *mbox_cmd)
270{
271 struct device *dev = &cxlm->pdev->dev;
272 64
273 dev_dbg(dev, "Mailbox command (opcode: %#x size: %zub) timed out\n",65 dev_dbg(dev, "Mailbox command (opcode: %#x size: %zub) timed out\n",
274 mbox_cmd->opcode, mbox_cmd->size_in);66 mbox_cmd->opcode, mbox_cmd->size_in);
275}67}
276 68
277/**69/**
278 * __cxl_mem_mbox_send_cmd() - Execute a mailbox command70 * __cxl_pci_mbox_send_cmd() - Execute a mailbox command
279 * @cxlm: The CXL memory device to communicate with.71 * @cxlm: The CXL memory device to communicate with.
280 * @mbox_cmd: Command to send to the memory device.72 * @mbox_cmd: Command to send to the memory device.
281 *73 *
@@ -296,10 +88,11 @@ static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm, · 14 unmodified lines
296 * not need to coordinate with each other. The driver only uses the primary88 * not need to coordinate with each other. The driver only uses the primary
297 * mailbox.89 * mailbox.
298 */90 */
299static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm,91static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
300 struct mbox_cmd *mbox_cmd)92 struct cxl_mbox_cmd *mbox_cmd)
301{93{
302 void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;94 void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
95 struct device *dev = cxlm->dev;
303 u64 cmd_reg, status_reg;96 u64 cmd_reg, status_reg;
304 size_t out_len;97 size_t out_len;
305 int rc;98 int rc;
@@ -325,8 +118,7 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 19 unmodified lines
325 118
326 /* #1 */119 /* #1 */
327 if (cxl_doorbell_busy(cxlm)) {120 if (cxl_doorbell_busy(cxlm)) {
328 dev_err_ratelimited(&cxlm->pdev->dev,121 dev_err_ratelimited(dev, "Mailbox re-busy after acquiring\n");
329 "Mailbox re-busy after acquiring\n");
330 return -EBUSY;122 return -EBUSY;
331 }123 }
332 124
@@ -345,14 +137,14 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 12 unmodified lines
345 writeq(cmd_reg, cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);137 writeq(cmd_reg, cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
346 138
347 /* #4 */139 /* #4 */
348 dev_dbg(&cxlm->pdev->dev, "Sending command\n");140 dev_dbg(dev, "Sending command\n");
349 writel(CXLDEV_MBOX_CTRL_DOORBELL,141 writel(CXLDEV_MBOX_CTRL_DOORBELL,
350 cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);142 cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
351 143
352 /* #5 */144 /* #5 */
353 rc = cxl_mem_wait_for_doorbell(cxlm);145 rc = cxl_pci_mbox_wait_for_doorbell(cxlm);
354 if (rc == -ETIMEDOUT) {146 if (rc == -ETIMEDOUT) {
355 cxl_mem_mbox_timeout(cxlm, mbox_cmd);147 cxl_pci_mbox_timeout(cxlm, mbox_cmd);
356 return rc;148 return rc;
357 }149 }
358 150
@@ -362,7 +154,7 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 3 unmodified lines
362 FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);154 FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
363 155
364 if (mbox_cmd->return_code != 0) {156 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");
366 return 0;158 return 0;
367 }159 }
368 160
@@ -391,15 +183,15 @@ static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, · 22 unmodified lines
391}183}
392 184
393/**185/**
394 * cxl_mem_mbox_get() - Acquire exclusive access to the mailbox.186 * cxl_pci_mbox_get() - Acquire exclusive access to the mailbox.
395 * @cxlm: The memory device to gain access to.187 * @cxlm: The memory device to gain access to.
396 *188 *
397 * Context: Any context. Takes the mbox_mutex.189 * Context: Any context. Takes the mbox_mutex.
398 * Return: 0 if exclusive access was acquired.190 * Return: 0 if exclusive access was acquired.
399 */191 */
400static int cxl_mem_mbox_get(struct cxl_mem *cxlm)192static int cxl_pci_mbox_get(struct cxl_mem *cxlm)
401{193{
402 struct device *dev = &cxlm->pdev->dev;194 struct device *dev = cxlm->dev;
403 u64 md_status;195 u64 md_status;
404 int rc;196 int rc;
405 197
@@ -422,7 +214,7 @@ static int cxl_mem_mbox_get(struct cxl_mem *cxlm) · 16 unmodified lines
422 * Mailbox Interface Ready bit. Therefore, waiting for the doorbell214 * Mailbox Interface Ready bit. Therefore, waiting for the doorbell
423 * to be ready is sufficient.215 * to be ready is sufficient.
424 */216 */
425 rc = cxl_mem_wait_for_doorbell(cxlm);217 rc = cxl_pci_mbox_wait_for_doorbell(cxlm);
426 if (rc) {218 if (rc) {
427 dev_warn(dev, "Mailbox interface not ready\n");219 dev_warn(dev, "Mailbox interface not ready\n");
428 goto out;220 goto out;
@@ -462,457 +254,35 @@ out: · 33 unmodified lines
462}254}
463 255
464/**256/**
465 * cxl_mem_mbox_put() - Release exclusive access to the mailbox.257 * cxl_pci_mbox_put() - Release exclusive access to the mailbox.
466 * @cxlm: The CXL memory device to communicate with.258 * @cxlm: The CXL memory device to communicate with.
467 *259 *
468 * Context: Any context. Expects mbox_mutex to be held.260 * Context: Any context. Expects mbox_mutex to be held.
469 */261 */
470static void cxl_mem_mbox_put(struct cxl_mem *cxlm)262static void cxl_pci_mbox_put(struct cxl_mem *cxlm)
471{263{
472 mutex_unlock(&cxlm->mbox_mutex);264 mutex_unlock(&cxlm->mbox_mutex);
473}265}
474 266
475/**267static int cxl_pci_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
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 */
500static 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
565out:
566 kvfree(mbox_cmd.payload_in);
567 kvfree(mbox_cmd.payload_out);
568 return rc;
569}
570
571static 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 */
612static 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
700static int cxl_query_cmd(struct cxl_memdev *cxlmd,
701 struct cxl_mem_query_commands __user *q)
702{268{
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
734static 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;
741 int rc;269 int rc;
742 270
743 dev_dbg(dev, "Send IOCTL\n");271 rc = cxl_pci_mbox_get(cxlm);
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);
759 if (rc)272 if (rc)
760 return rc;273 return rc;
761 274
762 if (copy_to_user(s, &send, sizeof(send)))275 rc = __cxl_pci_mbox_send_cmd(cxlm, cmd);
763 return -EFAULT;276 cxl_pci_mbox_put(cxlm);
764
765 return 0;
766}
767
768static 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
781static 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);
791 277
792 return rc;278 return rc;
793}279}
794 280
795static int cxl_memdev_open(struct inode *inode, struct file *file)281static int cxl_pci_setup_mailbox(struct cxl_mem *cxlm)
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
806static 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
816static 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
825static 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
837static 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 */
872static 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
912static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
913{282{
914 const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);283 const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
915 284
285 cxlm->mbox_send = cxl_pci_mbox_send;
916 cxlm->payload_size =286 cxlm->payload_size =
917 1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap);287 1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap);
918 288
@@ -925,103 +295,57 @@ static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm) · 6 unmodified lines
925 */295 */
926 cxlm->payload_size = min_t(size_t, cxlm->payload_size, SZ_1M);296 cxlm->payload_size = min_t(size_t, cxlm->payload_size, SZ_1M);
927 if (cxlm->payload_size < 256) {297 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)",
929 cxlm->payload_size);299 cxlm->payload_size);
930 return -ENXIO;300 return -ENXIO;
931 }301 }
932 302
933 dev_dbg(&cxlm->pdev->dev, "Mailbox payload sized %zu",303 dev_dbg(cxlm->dev, "Mailbox payload sized %zu",
934 cxlm->payload_size);304 cxlm->payload_size);
935 305
936 return 0;306 return 0;
937}307}
938 308
939static struct cxl_mem *cxl_mem_create(struct pci_dev *pdev)309static int cxl_map_regblock(struct pci_dev *pdev, struct cxl_register_map *map)
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
964static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
965 u8 bar, u64 offset)
966{310{
967 struct pci_dev *pdev = cxlm->pdev;
968 struct device *dev = &pdev->dev;
969 void __iomem *addr;311 void __iomem *addr;
312 int bar = map->barno;
313 struct device *dev = &pdev->dev;
314 resource_size_t offset = map->block_offset;
970 315
971 /* Basic sanity check that BAR is big enough */316 /* Basic sanity check that BAR is big enough */
972 if (pci_resource_len(pdev, bar) < offset) {317 if (pci_resource_len(pdev, bar) < offset) {
973 dev_err(dev, "BAR%d: %pr: too small (offset: %#llx)\n", bar,318 dev_err(dev, "BAR%d: %pr: too small (offset: %pa)\n", bar,
974 &pdev->resource[bar], (unsigned long long)offset);319 &pdev->resource[bar], &offset);
975 return IOMEM_ERR_PTR(-ENXIO);320 return -ENXIO;
976 }321 }
977 322
978 addr = pci_iomap(pdev, bar, 0);323 addr = pci_iomap(pdev, bar, 0);
979 if (!addr) {324 if (!addr) {
980 dev_err(dev, "failed to map registers\n");325 dev_err(dev, "failed to map registers\n");
981 return addr;326 return -ENOMEM;
982 }327 }
983 328
984 dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %#llx\n",329 dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %pa\n",
985 bar, offset);330 bar, &offset);
986 331
987 return addr;332 map->base = addr + map->block_offset;
333 return 0;
988}334}
989 335
990static void cxl_mem_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)336static void cxl_unmap_regblock(struct pci_dev *pdev,
337 struct cxl_register_map *map)
991{338{
992 pci_iounmap(cxlm->pdev, base);339 pci_iounmap(pdev, map->base - map->block_offset);
340 map->base = NULL;
993}341}
994 342
995static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)343static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
996{344{
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
1018static 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;
1023 struct cxl_component_reg_map *comp_map;345 struct cxl_component_reg_map *comp_map;
1024 struct cxl_device_reg_map *dev_map;346 struct cxl_device_reg_map *dev_map;
347 struct device *dev = &pdev->dev;
348 void __iomem *base = map->base;
1025 349
1026 switch (map->reg_type) {350 switch (map->reg_type) {
1027 case CXL_REGLOC_RBI_COMPONENT:351 case CXL_REGLOC_RBI_COMPONENT:
@@ -1057,8 +381,8 @@ static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base, · 29 unmodified lines
1057 381
1058static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)382static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
1059{383{
1060 struct pci_dev *pdev = cxlm->pdev;384 struct device *dev = cxlm->dev;
1061 struct device *dev = &pdev->dev;385 struct pci_dev *pdev = to_pci_dev(dev);
1062 386
1063 switch (map->reg_type) {387 switch (map->reg_type) {
1064 case CXL_REGLOC_RBI_COMPONENT:388 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
1076 return 0;400 return 0;
1077}401}
1078 402
1079static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,403static void cxl_decode_regblock(u32 reg_lo, u32 reg_hi,
1080 u8 *bar, u64 *offset, u8 *reg_type)404 struct cxl_register_map *map)
1081{405{
1082 *offset = ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);406 map->block_offset =
1083 *bar = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);407 ((u64)reg_hi << 32) | (reg_lo & CXL_REGLOC_ADDR_MASK);
1084 *reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);408 map->barno = FIELD_GET(CXL_REGLOC_BIR_MASK, reg_lo);
409 map->reg_type = FIELD_GET(CXL_REGLOC_RBI_MASK, reg_lo);
1085}410}
1086 411
1087/**412/**
1088 * cxl_mem_setup_regs() - Setup necessary MMIO.413 * cxl_find_regblock() - Locate register blocks by type
1089 * @cxlm: The CXL memory device to communicate with.414 * @pdev: The CXL PCI device to enumerate.
415 * @type: Register Block Indicator id
416 * @map: Enumeration output, clobbered on error
1090 *417 *
1091 * Return: 0 if all necessary registers mapped.418 * Return: 0 if register block enumerated, negative error code otherwise
1092 *419 *
1093 * A memory device is required by spec to implement a certain set of MMIO420 * A CXL DVSEC may point to one or more register blocks, search for them
1094 * regions. The purpose of this function is to enumerate and map those421 * by @type.
1095 * registers.
1096 */422 */
1097static int cxl_mem_setup_regs(struct cxl_mem *cxlm)423static int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
424 struct cxl_register_map *map)
1098{425{
1099 struct pci_dev *pdev = cxlm->pdev;
1100 struct device *dev = &pdev->dev;
1101 u32 regloc_size, regblocks;426 u32 regloc_size, regblocks;
1102 void __iomem *base;427 int regloc, i;
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 }
1112 428
1113 if (pci_request_mem_regions(pdev, pci_name(pdev)))429 regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
1114 return -ENODEV;430 PCI_DVSEC_ID_CXL_REGLOC_DVSEC_ID);
431 if (!regloc)
432 return -ENXIO;
1115 433
1116 /* Get the size of the Register Locator DVSEC */
1117 pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);434 pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, &regloc_size);
1118 regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);435 regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
1119 436
1120 regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;437 regloc += PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET;
1121 regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;438 regblocks = (regloc_size - PCI_DVSEC_ID_CXL_REGLOC_BLOCK1_OFFSET) / 8;
1122 439
1123 for (i = 0, n_maps = 0; i < regblocks; i++, regloc += 8) {440 for (i = 0; i < regblocks; i++, regloc += 8) {
1124 u32 reg_lo, reg_hi;441 u32 reg_lo, reg_hi;
1125 u8 reg_type;
1126 u64 offset;
1127 u8 bar;
1128 442
1129 pci_read_config_dword(pdev, regloc, &reg_lo);443 pci_read_config_dword(pdev, regloc, &reg_lo);
1130 pci_read_config_dword(pdev, regloc + 4, &reg_hi);444 pci_read_config_dword(pdev, regloc + 4, &reg_hi);
1131 445
1132 cxl_decode_register_block(reg_lo, reg_hi, &bar, &offset,446 cxl_decode_regblock(reg_lo, reg_hi, map);
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
1173static 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 */
1213static 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
1238struct 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
1247static 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;
1251 447
1252 ret = kvmalloc(cxlm->payload_size, GFP_KERNEL);448 if (map->reg_type == type)
1253 if (!ret)449 return 0;
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);
1261 }450 }
1262 451
1263 return ret;452 return -ENODEV;
1264}453}
1265 454
1266/**455static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
1267 * cxl_mem_get_partition_info - Get partition info456 struct cxl_register_map *map)
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 */
1281static 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)
1286{457{
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;
1293 int rc;458 int rc;
1294 459
1295 rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO,460 rc = cxl_find_regblock(pdev, type, map);
1296 NULL, 0, &pi, sizeof(pi));
1297 if (rc)461 if (rc)
1298 return rc;462 return rc;
1299 463
1300 *active_volatile_bytes = le64_to_cpu(pi.active_volatile_cap);464 rc = cxl_map_regblock(pdev, map);
1301 *active_persistent_bytes = le64_to_cpu(pi.active_persistent_cap);465 if (rc)
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 */
1323static 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
1369out:
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 */
1383static 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
1437static 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");
1457 return rc;466 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);
1469 467
1470 cxlm->ram_range.start = 0;468 rc = cxl_probe_regs(pdev, map);
1471 cxlm->ram_range.end = cxlm->active_volatile_bytes - 1;469 cxl_unmap_regblock(pdev, map);
1472 470
1473 cxlm->pmem_range.start = cxlm->active_volatile_bytes;471 return rc;
1474 cxlm->pmem_range.end = cxlm->active_volatile_bytes +
1475 cxlm->active_persistent_bytes - 1;
1476
1477 return 0;
1478}472}
1479 473
1480static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)474static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
1481{475{
476 struct cxl_register_map map;
1482 struct cxl_memdev *cxlmd;477 struct cxl_memdev *cxlmd;
1483 struct cxl_mem *cxlm;478 struct cxl_mem *cxlm;
1484 int rc;479 int rc;
1485 480
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
1486 rc = pcim_enable_device(pdev);488 rc = pcim_enable_device(pdev);
1487 if (rc)489 if (rc)
1488 return rc;490 return rc;
1489 491
1490 cxlm = cxl_mem_create(pdev);492 cxlm = cxl_mem_create(&pdev->dev);
1491 if (IS_ERR(cxlm))493 if (IS_ERR(cxlm))
1492 return PTR_ERR(cxlm);494 return PTR_ERR(cxlm);
1493 495
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);
1495 if (rc)501 if (rc)
1496 return rc;502 return rc;
1497 503
1498 rc = cxl_mem_setup_mailbox(cxlm);504 rc = cxl_pci_setup_mailbox(cxlm);
1499 if (rc)505 if (rc)
1500 return rc;506 return rc;
1501 507
@@ -1511,7 +517,7 @@ static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id) · 9 unmodified lines
1511 if (rc)517 if (rc)
1512 return rc;518 return rc;
1513 519
1514 cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlm, &cxl_memdev_fops);520 cxlmd = devm_cxl_add_memdev(cxlm);
1515 if (IS_ERR(cxlmd))521 if (IS_ERR(cxlmd))
1516 return PTR_ERR(cxlmd);522 return PTR_ERR(cxlmd);
1517 523
@@ -1528,43 +534,15 @@ static const struct pci_device_id cxl_mem_pci_tbl[] = { · 10 unmodified lines
1528};534};
1529MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl);535MODULE_DEVICE_TABLE(pci, cxl_mem_pci_tbl);
1530 536
1531static struct pci_driver cxl_mem_driver = {537static struct pci_driver cxl_pci_driver = {
1532 .name = KBUILD_MODNAME,538 .name = KBUILD_MODNAME,
1533 .id_table = cxl_mem_pci_tbl,539 .id_table = cxl_mem_pci_tbl,
1534 .probe = cxl_mem_probe,540 .probe = cxl_pci_probe,
1535 .driver = {541 .driver = {
1536 .probe_type = PROBE_PREFER_ASYNCHRONOUS,542 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
1537 },543 },
1538};544};
1539 545
1540static __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
1561static __exit void cxl_mem_exit(void)
1562{
1563 debugfs_remove_recursive(cxl_debugfs);
1564 pci_unregister_driver(&cxl_mem_driver);
1565}
1566
1567MODULE_LICENSE("GPL v2");546MODULE_LICENSE("GPL v2");
1568module_init(cxl_mem_init);547module_pci_driver(cxl_pci_driver);
1569module_exit(cxl_mem_exit);
1570MODULE_IMPORT_NS(CXL);548MODULE_IMPORT_NS(CXL);
drivers/cxl/pci.h+8−6
@@ -20,13 +20,15 @@ · 19 unmodified lines
20#define CXL_REGLOC_BIR_MASK GENMASK(2, 0)20#define CXL_REGLOC_BIR_MASK GENMASK(2, 0)
21 21
22/* Register Block Identifier (RBI) */22/* Register Block Identifier (RBI) */
23#define CXL_REGLOC_RBI_MASK GENMASK(15, 8)23enum cxl_regloc_type {
24#define CXL_REGLOC_RBI_EMPTY 024 CXL_REGLOC_RBI_EMPTY = 0,
25#define CXL_REGLOC_RBI_COMPONENT 125 CXL_REGLOC_RBI_COMPONENT,
26#define CXL_REGLOC_RBI_VIRT 226 CXL_REGLOC_RBI_VIRT,
27#define CXL_REGLOC_RBI_MEMDEV 327 CXL_REGLOC_RBI_MEMDEV,
28#define CXL_REGLOC_RBI_TYPES CXL_REGLOC_RBI_MEMDEV + 128 CXL_REGLOC_RBI_TYPES
29};
29 30
31#define CXL_REGLOC_RBI_MASK GENMASK(15, 8)
30#define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)32#define CXL_REGLOC_ADDR_MASK GENMASK(31, 16)
31 33
32#endif /* __CXL_PCI_H__ */34#endif /* __CXL_PCI_H__ */
drivers/cxl/pmem.c+142−21
@@ -1,6 +1,7 @@
1// SPDX-License-Identifier: GPL-2.0-only1// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2021 Intel Corporation. All rights reserved. */2/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3#include <linux/libnvdimm.h>3#include <linux/libnvdimm.h>
4#include <asm/unaligned.h>
4#include <linux/device.h>5#include <linux/device.h>
5#include <linux/module.h>6#include <linux/module.h>
6#include <linux/ndctl.h>7#include <linux/ndctl.h>
@@ -16,48 +17,55 @@ · 9 unmodified lines
16 */17 */
17static struct workqueue_struct *cxl_pmem_wq;18static struct workqueue_struct *cxl_pmem_wq;
18 19
19static void unregister_nvdimm(void *nvdimm)20static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
20{
21 nvdimm_delete(nvdimm);
22}
23 21
24static int match_nvdimm_bridge(struct device *dev, const void *data)22static void clear_exclusive(void *cxlm)
25{23{
26 return strcmp(dev_name(dev), "nvdimm-bridge") == 0;24 clear_exclusive_cxl_commands(cxlm, exclusive_cmds);
27}25}
28 26
29static struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(void)27static void unregister_nvdimm(void *nvdimm)
30{28{
31 struct device *dev;29 nvdimm_delete(nvdimm);
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);
37}30}
38 31
39static int cxl_nvdimm_probe(struct device *dev)32static int cxl_nvdimm_probe(struct device *dev)
40{33{
41 struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);34 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;
42 struct cxl_nvdimm_bridge *cxl_nvb;38 struct cxl_nvdimm_bridge *cxl_nvb;
43 unsigned long flags = 0;
44 struct nvdimm *nvdimm;39 struct nvdimm *nvdimm;
45 int rc = -ENXIO;40 int rc;
46 41
47 cxl_nvb = cxl_find_nvdimm_bridge();42 cxl_nvb = cxl_find_nvdimm_bridge(cxl_nvd);
48 if (!cxl_nvb)43 if (!cxl_nvb)
49 return -ENXIO;44 return -ENXIO;
50 45
51 device_lock(&cxl_nvb->dev);46 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)
53 goto out;55 goto out;
54 56
55 set_bit(NDD_LABELING, &flags);57 set_bit(NDD_LABELING, &flags);
56 nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags, 0, 0,58 set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
57 NULL);59 set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
58 if (!nvdimm)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;
59 goto out;65 goto out;
66 }
60 67
68 dev_set_drvdata(dev, nvdimm);
61 rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);69 rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
62out:70out:
63 device_unlock(&cxl_nvb->dev);71 device_unlock(&cxl_nvb->dev);
@@ -72,11 +80,120 @@ static struct cxl_driver cxl_nvdimm_driver = { · 8 unmodified lines
72 .id = CXL_DEVICE_NVDIMM,80 .id = CXL_DEVICE_NVDIMM,
73};81};
74 82
83static 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
98static 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
123static 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
161static 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
75static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,184static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,
76 struct nvdimm *nvdimm, unsigned int cmd, void *buf,185 struct nvdimm *nvdimm, unsigned int cmd, void *buf,
77 unsigned int buf_len, int *cmd_rc)186 unsigned int buf_len, int *cmd_rc)
78{187{
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);
80}197}
81 198
82static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)199static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)
@@ -194,6 +311,10 @@ static __init int cxl_pmem_init(void) · 111 unmodified lines
194{311{
195 int rc;312 int rc;
196 313
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
197 cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0);318 cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0);
198 if (!cxl_pmem_wq)319 if (!cxl_pmem_wq)
199 return -ENXIO;320 return -ENXIO;
drivers/misc/ocxl/config.c+1−12
@@ -33,18 +33,7 @@ · 32 unmodified lines
33 33
34static int find_dvsec(struct pci_dev *dev, int dvsec_id)34static int find_dvsec(struct pci_dev *dev, int dvsec_id)
35{35{
36 int vsec = 0;36 return pci_find_dvsec_capability(dev, PCI_VENDOR_ID_IBM, dvsec_id);
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;
48}37}
49 38
50static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)39static 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
973 u64 sum;973 u64 sum;
974 struct btt_sb *super;974 struct btt_sb *super;
975 struct nd_btt *nd_btt = arena->nd_btt;975 struct nd_btt *nd_btt = arena->nd_btt;
976 const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);976 const uuid_t *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);
977 977
978 ret = btt_map_init(arena);978 ret = btt_map_init(arena);
979 if (ret)979 if (ret)
@@ -988,8 +988,8 @@ static int btt_arena_write_layout(struct arena_info *arena) · 8 unmodified lines
988 return -ENOMEM;988 return -ENOMEM;
989 989
990 strncpy(super->signature, BTT_SIG, BTT_SIG_LEN);990 strncpy(super->signature, BTT_SIG, BTT_SIG_LEN);
991 memcpy(super->uuid, nd_btt->uuid, 16);991 export_uuid(super->uuid, nd_btt->uuid);
992 memcpy(super->parent_uuid, parent_uuid, 16);992 export_uuid(super->parent_uuid, parent_uuid);
993 super->flags = cpu_to_le32(arena->flags);993 super->flags = cpu_to_le32(arena->flags);
994 super->version_major = cpu_to_le16(arena->version_major);994 super->version_major = cpu_to_le16(arena->version_major);
995 super->version_minor = cpu_to_le16(arena->version_minor);995 super->version_minor = cpu_to_le16(arena->version_minor);
@@ -1574,7 +1574,8 @@ static void btt_blk_cleanup(struct btt *btt) · 578 unmodified lines
1574 * Pointer to a new struct btt on success, NULL on failure.1574 * Pointer to a new struct btt on success, NULL on failure.
1575 */1575 */
1576static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,1576static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
1577 u32 lbasize, u8 *uuid, struct nd_region *nd_region)1577 u32 lbasize, uuid_t *uuid,
1578 struct nd_region *nd_region)
1578{1579{
1579 int ret;1580 int ret;
1580 struct btt *btt;1581 struct btt *btt;
@@ -1693,7 +1694,7 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) · 112 unmodified lines
1693 }1694 }
1694 nd_region = to_nd_region(nd_btt->dev.parent);1695 nd_region = to_nd_region(nd_btt->dev.parent);
1695 btt = btt_init(nd_btt, rawsize, nd_btt->lbasize, nd_btt->uuid,1696 btt = btt_init(nd_btt, rawsize, nd_btt->lbasize, nd_btt->uuid,
1696 nd_region);1697 nd_region);
1697 if (!btt)1698 if (!btt)
1698 return -ENOMEM;1699 return -ENOMEM;
1699 nd_btt->btt = btt;1700 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
180EXPORT_SYMBOL(is_nd_btt);180EXPORT_SYMBOL(is_nd_btt);
181 181
182static struct device *__nd_btt_create(struct nd_region *nd_region,182static struct device *__nd_btt_create(struct nd_region *nd_region,
183 unsigned long lbasize, u8 *uuid,183 unsigned long lbasize, uuid_t *uuid,
184 struct nd_namespace_common *ndns)184 struct nd_namespace_common *ndns)
185{185{
186 struct nd_btt *nd_btt;186 struct nd_btt *nd_btt;
187 struct device *dev;187 struct device *dev;
@@ -244,14 +244,16 @@ struct device *nd_btt_create(struct nd_region *nd_region) · 56 unmodified lines
244 */244 */
245bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super)245bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super)
246{246{
247 const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);247 const uuid_t *ns_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);
248 uuid_t parent_uuid;
248 u64 checksum;249 u64 checksum;
249 250
250 if (memcmp(super->signature, BTT_SIG, BTT_SIG_LEN) != 0)251 if (memcmp(super->signature, BTT_SIG, BTT_SIG_LEN) != 0)
251 return false;252 return false;
252 253
253 if (!guid_is_null((guid_t *)&super->parent_uuid))254 import_uuid(&parent_uuid, super->parent_uuid);
254 if (memcmp(super->parent_uuid, parent_uuid, 16) != 0)255 if (!uuid_is_null(&parent_uuid))
256 if (!uuid_equal(&parent_uuid, ns_uuid))
255 return false;257 return false;
256 258
257 checksum = le64_to_cpu(super->checksum);259 checksum = le64_to_cpu(super->checksum);
@@ -319,7 +321,7 @@ static int __nd_btt_probe(struct nd_btt *nd_btt, · 61 unmodified lines
319 return rc;321 return rc;
320 322
321 nd_btt->lbasize = le32_to_cpu(btt_sb->external_lbasize);323 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);
323 if (!nd_btt->uuid)325 if (!nd_btt->uuid)
324 return -ENOMEM;326 return -ENOMEM;
325 327
drivers/nvdimm/core.c+4−36
@@ -207,38 +207,6 @@ struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus) · 206 unmodified lines
207}207}
208EXPORT_SYMBOL_GPL(to_nvdimm_bus_dev);208EXPORT_SYMBOL_GPL(to_nvdimm_bus_dev);
209 209
210static bool is_uuid_sep(char sep)
211{
212 if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0')
213 return true;
214 return false;
215}
216
217static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf,
218 size_t len)
219{
220 const char *str = buf;
221 u8 uuid[16];
222 int i;
223
224 for (i = 0; i < 16; i++) {
225 if (!isxdigit(str[0]) || !isxdigit(str[1])) {
226 dev_dbg(dev, "pos: %d buf[%zd]: %c buf[%zd]: %c\n",
227 i, str - buf, str[0],
228 str + 1 - buf, str[1]);
229 return -EINVAL;
230 }
231
232 uuid[i] = (hex_to_bin(str[0]) << 4) | hex_to_bin(str[1]);
233 str += 2;
234 if (is_uuid_sep(*str))
235 str++;
236 }
237
238 memcpy(uuid_out, uuid, sizeof(uuid));
239 return 0;
240}
241
242/**210/**
243 * nd_uuid_store: common implementation for writing 'uuid' sysfs attributes211 * nd_uuid_store: common implementation for writing 'uuid' sysfs attributes
244 * @dev: container device for the uuid property212 * @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
249 * (driver detached)217 * (driver detached)
250 * LOCKING: expects nd_device_lock() is held on entry218 * LOCKING: expects nd_device_lock() is held on entry
251 */219 */
252int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,220int nd_uuid_store(struct device *dev, uuid_t **uuid_out, const char *buf,
253 size_t len)221 size_t len)
254{222{
255 u8 uuid[16];223 uuid_t uuid;
256 int rc;224 int rc;
257 225
258 if (dev->driver)226 if (dev->driver)
259 return -EBUSY;227 return -EBUSY;
260 228
261 rc = nd_uuid_parse(dev, uuid, buf, len);229 rc = uuid_parse(buf, &uuid);
262 if (rc)230 if (rc)
263 return rc;231 return rc;
264 232
265 kfree(*uuid_out);233 kfree(*uuid_out);
266 *uuid_out = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);234 *uuid_out = kmemdup(&uuid, sizeof(uuid), GFP_KERNEL);
267 if (!(*uuid_out))235 if (!(*uuid_out))
268 return -ENOMEM;236 return -ENOMEM;
269 237
drivers/nvdimm/label.c+102−37
@@ -17,6 +17,14 @@ static guid_t nvdimm_btt2_guid; · 16 unmodified lines
17static guid_t nvdimm_pfn_guid;17static guid_t nvdimm_pfn_guid;
18static guid_t nvdimm_dax_guid;18static guid_t nvdimm_dax_guid;
19 19
20static uuid_t nvdimm_btt_uuid;
21static uuid_t nvdimm_btt2_uuid;
22static uuid_t nvdimm_pfn_uuid;
23static uuid_t nvdimm_dax_uuid;
24
25static uuid_t cxl_region_uuid;
26static uuid_t cxl_namespace_uuid;
27
20static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";28static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";
21 29
22static u32 best_seq(u32 a, u32 b)30static u32 best_seq(u32 a, u32 b)
@@ -321,7 +329,8 @@ static bool preamble_index(struct nvdimm_drvdata *ndd, int idx, · 298 unmodified lines
321 return true;329 return true;
322}330}
323 331
324char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags)332char *nd_label_gen_id(struct nd_label_id *label_id, const uuid_t *uuid,
333 u32 flags)
325{334{
326 if (!label_id || !uuid)335 if (!label_id || !uuid)
327 return NULL;336 return NULL;
@@ -351,7 +360,7 @@ static bool nsl_validate_checksum(struct nvdimm_drvdata *ndd, · 23 unmodified lines
351{360{
352 u64 sum, sum_save;361 u64 sum, sum_save;
353 362
354 if (!namespace_label_has(ndd, checksum))363 if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
355 return true;364 return true;
356 365
357 sum_save = nsl_get_checksum(ndd, nd_label);366 sum_save = nsl_get_checksum(ndd, nd_label);
@@ -366,7 +375,7 @@ static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd, · 8 unmodified lines
366{375{
367 u64 sum;376 u64 sum;
368 377
369 if (!namespace_label_has(ndd, checksum))378 if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum))
370 return;379 return;
371 nsl_set_checksum(ndd, nd_label, 0);380 nsl_set_checksum(ndd, nd_label, 0);
372 sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1);381 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
400 struct nvdimm *nvdimm = to_nvdimm(ndd->dev);409 struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
401 struct nd_namespace_label *nd_label;410 struct nd_namespace_label *nd_label;
402 struct nd_region *nd_region = NULL;411 struct nd_region *nd_region = NULL;
403 u8 label_uuid[NSLABEL_UUID_LEN];
404 struct nd_label_id label_id;412 struct nd_label_id label_id;
405 struct resource *res;413 struct resource *res;
414 uuid_t label_uuid;
406 u32 flags;415 u32 flags;
407 416
408 nd_label = to_label(ndd, slot);417 nd_label = to_label(ndd, slot);
@@ -410,11 +419,11 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) · 1 unmodified line
410 if (!slot_valid(ndd, nd_label, slot))419 if (!slot_valid(ndd, nd_label, slot))
411 continue;420 continue;
412 421
413 memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);422 nsl_get_uuid(ndd, nd_label, &label_uuid);
414 flags = nsl_get_flags(ndd, nd_label);423 flags = nsl_get_flags(ndd, nd_label);
415 if (test_bit(NDD_NOBLK, &nvdimm->flags))424 if (test_bit(NDD_NOBLK, &nvdimm->flags))
416 flags &= ~NSLABEL_FLAG_LOCAL;425 flags &= ~NSLABEL_FLAG_LOCAL;
417 nd_label_gen_id(&label_id, label_uuid, flags);426 nd_label_gen_id(&label_id, &label_uuid, flags);
418 res = nvdimm_allocate_dpa(ndd, &label_id,427 res = nvdimm_allocate_dpa(ndd, &label_id,
419 nsl_get_dpa(ndd, nd_label),428 nsl_get_dpa(ndd, nd_label),
420 nsl_get_rawsize(ndd, nd_label));429 nsl_get_rawsize(ndd, nd_label));
@@ -724,7 +733,7 @@ static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, · 303 unmodified lines
724 - (unsigned long) to_namespace_index(ndd, 0);733 - (unsigned long) to_namespace_index(ndd, 0);
725}734}
726 735
727static enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid)736static enum nvdimm_claim_class guid_to_nvdimm_cclass(guid_t *guid)
728{737{
729 if (guid_equal(guid, &nvdimm_btt_guid))738 if (guid_equal(guid, &nvdimm_btt_guid))
730 return NVDIMM_CCLASS_BTT;739 return NVDIMM_CCLASS_BTT;
@@ -740,6 +749,23 @@ static enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid) · 9 unmodified lines
740 return NVDIMM_CCLASS_UNKNOWN;749 return NVDIMM_CCLASS_UNKNOWN;
741}750}
742 751
752/* CXL labels store UUIDs instead of GUIDs for the same data */
753static 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
743static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class,769static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class,
744 guid_t *target)770 guid_t *target)
745{771{
@@ -761,6 +787,28 @@ static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, · 15 unmodified lines
761 return &guid_null;787 return &guid_null;
762}788}
763 789
790/* CXL labels store UUIDs instead of GUIDs for the same data */
791static 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
764static void reap_victim(struct nd_mapping *nd_mapping,812static void reap_victim(struct nd_mapping *nd_mapping,
765 struct nd_label_ent *victim)813 struct nd_label_ent *victim)
766{814{
@@ -775,18 +823,18 @@ static void reap_victim(struct nd_mapping *nd_mapping, · 8 unmodified lines
775static void nsl_set_type_guid(struct nvdimm_drvdata *ndd,823static void nsl_set_type_guid(struct nvdimm_drvdata *ndd,
776 struct nd_namespace_label *nd_label, guid_t *guid)824 struct nd_namespace_label *nd_label, guid_t *guid)
777{825{
778 if (namespace_label_has(ndd, type_guid))826 if (efi_namespace_label_has(ndd, type_guid))
779 guid_copy(&nd_label->type_guid, guid);827 guid_copy(&nd_label->efi.type_guid, guid);
780}828}
781 829
782bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd,830bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd,
783 struct nd_namespace_label *nd_label, guid_t *guid)831 struct nd_namespace_label *nd_label, guid_t *guid)
784{832{
785 if (!namespace_label_has(ndd, type_guid))833 if (ndd->cxl || !efi_namespace_label_has(ndd, type_guid))
786 return true;834 return true;
787 if (!guid_equal(&nd_label->type_guid, guid)) {835 if (!guid_equal(&nd_label->efi.type_guid, guid)) {
788 dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", guid,836 dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", guid,
789 &nd_label->type_guid);837 &nd_label->efi.type_guid);
790 return false;838 return false;
791 }839 }
792 return true;840 return true;
@@ -796,19 +844,34 @@ static void nsl_set_claim_class(struct nvdimm_drvdata *ndd, · 3 unmodified lines
796 struct nd_namespace_label *nd_label,844 struct nd_namespace_label *nd_label,
797 enum nvdimm_claim_class claim_class)845 enum nvdimm_claim_class claim_class)
798{846{
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))
800 return;857 return;
801 guid_copy(&nd_label->abstraction_guid,858 guid_copy(&nd_label->efi.abstraction_guid,
802 to_abstraction_guid(claim_class,859 to_abstraction_guid(claim_class,
803 &nd_label->abstraction_guid));860 &nd_label->efi.abstraction_guid));
804}861}
805 862
806enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,863enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd,
807 struct nd_namespace_label *nd_label)864 struct nd_namespace_label *nd_label)
808{865{
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))
810 return NVDIMM_CCLASS_NONE;873 return NVDIMM_CCLASS_NONE;
811 return to_nvdimm_cclass(&nd_label->abstraction_guid);874 return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid);
812}875}
813 876
814static int __pmem_label_update(struct nd_region *nd_region,877static 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
851 914
852 nd_label = to_label(ndd, slot);915 nd_label = to_label(ndd, slot);
853 memset(nd_label, 0, sizeof_namespace_label(ndd));916 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);
855 nsl_set_name(ndd, nd_label, nspm->alt_name);918 nsl_set_name(ndd, nd_label, nspm->alt_name);
856 nsl_set_flags(ndd, nd_label, flags);919 nsl_set_flags(ndd, nd_label, flags);
857 nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings);920 nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings);
921 nsl_set_nrange(ndd, nd_label, 1);
858 nsl_set_position(ndd, nd_label, pos);922 nsl_set_position(ndd, nd_label, pos);
859 nsl_set_isetcookie(ndd, nd_label, cookie);923 nsl_set_isetcookie(ndd, nd_label, cookie);
860 nsl_set_rawsize(ndd, nd_label, resource_size(res));924 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
878 list_for_each_entry(label_ent, &nd_mapping->labels, list) {942 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
879 if (!label_ent->label)943 if (!label_ent->label)
880 continue;944 continue;
881 if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags)945 if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) ||
882 || memcmp(nspm->uuid, label_ent->label->uuid,946 nsl_uuid_equal(ndd, label_ent->label, nspm->uuid))
883 NSLABEL_UUID_LEN) == 0)
884 reap_victim(nd_mapping, label_ent);947 reap_victim(nd_mapping, label_ent);
885 }948 }
886 949
@@ -941,7 +1004,7 @@ static void nsl_set_blk_isetcookie(struct nvdimm_drvdata *ndd, · 54 unmodified lines
941 struct nd_namespace_label *nd_label,1004 struct nd_namespace_label *nd_label,
942 u64 isetcookie)1005 u64 isetcookie)
943{1006{
944 if (namespace_label_has(ndd, type_guid)) {1007 if (efi_namespace_label_has(ndd, type_guid)) {
945 nsl_set_isetcookie(ndd, nd_label, isetcookie);1008 nsl_set_isetcookie(ndd, nd_label, isetcookie);
946 return;1009 return;
947 }1010 }
@@ -952,7 +1015,7 @@ bool nsl_validate_blk_isetcookie(struct nvdimm_drvdata *ndd, · 4 unmodified lines
952 struct nd_namespace_label *nd_label,1015 struct nd_namespace_label *nd_label,
953 u64 isetcookie)1016 u64 isetcookie)
954{1017{
955 if (!namespace_label_has(ndd, type_guid))1018 if (!efi_namespace_label_has(ndd, type_guid))
956 return true;1019 return true;
957 1020
958 if (nsl_get_isetcookie(ndd, nd_label) != isetcookie) {1021 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
968 struct nd_namespace_label *nd_label, int nlabel,1031 struct nd_namespace_label *nd_label, int nlabel,
969 bool first)1032 bool first)
970{1033{
971 if (!namespace_label_has(ndd, type_guid)) {1034 if (!efi_namespace_label_has(ndd, type_guid)) {
972 nsl_set_nlabel(ndd, nd_label, 0); /* N/A */1035 nsl_set_nlabel(ndd, nd_label, 0); /* N/A */
973 return;1036 return;
974 }1037 }
@@ -979,7 +1042,7 @@ static void nsl_set_blk_position(struct nvdimm_drvdata *ndd, · 4 unmodified lines
979 struct nd_namespace_label *nd_label,1042 struct nd_namespace_label *nd_label,
980 bool first)1043 bool first)
981{1044{
982 if (!namespace_label_has(ndd, type_guid)) {1045 if (!efi_namespace_label_has(ndd, type_guid)) {
983 nsl_set_position(ndd, nd_label, 0);1046 nsl_set_position(ndd, nd_label, 0);
984 return;1047 return;
985 }1048 }
@@ -1005,7 +1068,6 @@ static int __blk_label_update(struct nd_region *nd_region, · 19 unmodified lines
1005 unsigned long *free, *victim_map = NULL;1068 unsigned long *free, *victim_map = NULL;
1006 struct resource *res, **old_res_list;1069 struct resource *res, **old_res_list;
1007 struct nd_label_id label_id;1070 struct nd_label_id label_id;
1008 u8 uuid[NSLABEL_UUID_LEN];
1009 int min_dpa_idx = 0;1071 int min_dpa_idx = 0;
1010 LIST_HEAD(list);1072 LIST_HEAD(list);
1011 u32 nslot, slot;1073 u32 nslot, slot;
@@ -1043,8 +1105,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 31 unmodified lines
1043 /* mark unused labels for garbage collection */1105 /* mark unused labels for garbage collection */
1044 for_each_clear_bit_le(slot, free, nslot) {1106 for_each_clear_bit_le(slot, free, nslot) {
1045 nd_label = to_label(ndd, slot);1107 nd_label = to_label(ndd, slot);
1046 memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN);1108 if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
1047 if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0)
1048 continue;1109 continue;
1049 res = to_resource(ndd, nd_label);1110 res = to_resource(ndd, nd_label);
1050 if (res && is_old_resource(res, old_res_list,1111 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
1113 1174
1114 nd_label = to_label(ndd, slot);1175 nd_label = to_label(ndd, slot);
1115 memset(nd_label, 0, sizeof_namespace_label(ndd));1176 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);
1117 nsl_set_name(ndd, nd_label, nsblk->alt_name);1178 nsl_set_name(ndd, nd_label, nsblk->alt_name);
1118 nsl_set_flags(ndd, nd_label, NSLABEL_FLAG_LOCAL);1179 nsl_set_flags(ndd, nd_label, NSLABEL_FLAG_LOCAL);
1119 1180
@@ -1161,8 +1222,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 41 unmodified lines
1161 if (!nd_label)1222 if (!nd_label)
1162 continue;1223 continue;
1163 nlabel++;1224 nlabel++;
1164 memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN);1225 if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
1165 if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0)
1166 continue;1226 continue;
1167 nlabel--;1227 nlabel--;
1168 list_move(&label_ent->list, &list);1228 list_move(&label_ent->list, &list);
@@ -1192,8 +1252,7 @@ static int __blk_label_update(struct nd_region *nd_region, · 23 unmodified lines
1192 }1252 }
1193 for_each_clear_bit_le(slot, free, nslot) {1253 for_each_clear_bit_le(slot, free, nslot) {
1194 nd_label = to_label(ndd, slot);1254 nd_label = to_label(ndd, slot);
1195 memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN);1255 if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid))
1196 if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0)
1197 continue;1256 continue;
1198 res = to_resource(ndd, nd_label);1257 res = to_resource(ndd, nd_label);
1199 res->flags &= ~DPA_RESOURCE_ADJUSTED;1258 res->flags &= ~DPA_RESOURCE_ADJUSTED;
@@ -1273,12 +1332,11 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels) · 73 unmodified lines
1273 return max(num_labels, old_num_labels);1332 return max(num_labels, old_num_labels);
1274}1333}
1275 1334
1276static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid)1335static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid)
1277{1336{
1278 struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);1337 struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
1279 struct nd_label_ent *label_ent, *e;1338 struct nd_label_ent *label_ent, *e;
1280 struct nd_namespace_index *nsindex;1339 struct nd_namespace_index *nsindex;
1281 u8 label_uuid[NSLABEL_UUID_LEN];
1282 unsigned long *free;1340 unsigned long *free;
1283 LIST_HEAD(list);1341 LIST_HEAD(list);
1284 u32 nslot, slot;1342 u32 nslot, slot;
@@ -1298,8 +1356,7 @@ static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) · 13 unmodified lines
1298 if (!nd_label)1356 if (!nd_label)
1299 continue;1357 continue;
1300 active++;1358 active++;
1301 memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);1359 if (!nsl_uuid_equal(ndd, nd_label, uuid))
1302 if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0)
1303 continue;1360 continue;
1304 active--;1361 active--;
1305 slot = to_slot(ndd, nd_label);1362 slot = to_slot(ndd, nd_label);
@@ -1395,5 +1452,13 @@ int __init nd_label_init(void) · 89 unmodified lines
1395 WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid));1452 WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid));
1396 WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid));1453 WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid));
1397 1454
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
1398 return 0;1463 return 0;
1399}1464}
drivers/nvdimm/label.h+89−5
@@ -34,6 +34,7 @@ enum { · 33 unmodified lines
34 * struct nd_namespace_index - label set superblock34 * struct nd_namespace_index - label set superblock
35 * @sig: NAMESPACE_INDEX\035 * @sig: NAMESPACE_INDEX\0
36 * @flags: placeholder36 * @flags: placeholder
37 * @labelsize: log2 size (v1 labels 128 bytes v2 labels 256 bytes)
37 * @seq: sequence number for this index38 * @seq: sequence number for this index
38 * @myoff: offset of this index in label area39 * @myoff: offset of this index in label area
39 * @mysize: size of this index struct40 * @mysize: size of this index struct
@@ -43,7 +44,7 @@ enum { · 3 unmodified lines
43 * @major: label area major version44 * @major: label area major version
44 * @minor: label area minor version45 * @minor: label area minor version
45 * @checksum: fletcher64 of all fields46 * @checksum: fletcher64 of all fields
46 * @free[0]: bitmap, nlabel bits47 * @free: bitmap, nlabel bits
47 *48 *
48 * The size of free[] is rounded up so the total struct size is a49 * The size of free[] is rounded up so the total struct size is a
49 * multiple of NSINDEX_ALIGN bytes. Any bits this allocates beyond50 * multiple of NSINDEX_ALIGN bytes. Any bits this allocates beyond
@@ -66,7 +67,39 @@ struct nd_namespace_index { · 16 unmodified lines
66};67};
67 68
68/**69/**
69 * struct nd_namespace_label - namespace superblock70 * 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 */
85struct 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
70 * @uuid: UUID per RFC 4122103 * @uuid: UUID per RFC 4122
71 * @name: optional name (NULL-terminated)104 * @name: optional name (NULL-terminated)
72 * @flags: see NSLABEL_FLAG_*105 * @flags: see NSLABEL_FLAG_*
@@ -77,9 +110,14 @@ struct nd_namespace_index { · 4 unmodified lines
77 * @dpa: DPA of NVM range on this DIMM110 * @dpa: DPA of NVM range on this DIMM
78 * @rawsize: size of namespace111 * @rawsize: size of namespace
79 * @slot: slot of this label in label area112 * @slot: slot of this label in label area
80 * @unused: must be zero113 * @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
81 */119 */
82struct nd_namespace_label {120struct nvdimm_efi_label {
83 u8 uuid[NSLABEL_UUID_LEN];121 u8 uuid[NSLABEL_UUID_LEN];
84 u8 name[NSLABEL_NAME_LEN];122 u8 name[NSLABEL_NAME_LEN];
85 __le32 flags;123 __le32 flags;
@@ -92,7 +130,7 @@ struct nd_namespace_label { · 6 unmodified lines
92 __le32 slot;130 __le32 slot;
93 /*131 /*
94 * Accessing fields past this point should be gated by a132 * Accessing fields past this point should be gated by a
95 * namespace_label_has() check.133 * efi_namespace_label_has() check.
96 */134 */
97 u8 align;135 u8 align;
98 u8 reserved[3];136 u8 reserved[3];
@@ -102,11 +140,57 @@ struct nd_namespace_label { · 3 unmodified lines
102 __le64 checksum;140 __le64 checksum;
103};141};
104 142
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 */
161struct 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
179struct nd_namespace_label {
180 union {
181 struct nvdimm_cxl_label cxl;
182 struct nvdimm_efi_label efi;
183 };
184};
185
105#define NVDIMM_BTT_GUID "8aed63a2-29a2-4c66-8b12-f05d15d3922a"186#define NVDIMM_BTT_GUID "8aed63a2-29a2-4c66-8b12-f05d15d3922a"
106#define NVDIMM_BTT2_GUID "18633bfc-1735-4217-8ac9-17239282d3f8"187#define NVDIMM_BTT2_GUID "18633bfc-1735-4217-8ac9-17239282d3f8"
107#define NVDIMM_PFN_GUID "266400ba-fb9f-4677-bcb0-968f11d0d225"188#define NVDIMM_PFN_GUID "266400ba-fb9f-4677-bcb0-968f11d0d225"
108#define NVDIMM_DAX_GUID "97a86d9c-3cdd-4eda-986f-5068b4f80088"189#define NVDIMM_DAX_GUID "97a86d9c-3cdd-4eda-986f-5068b4f80088"
109 190
191#define CXL_REGION_UUID "529d7c61-da07-47c4-a93f-ecdf2c06f444"
192#define CXL_NAMESPACE_UUID "68bb2c0a-5a77-4937-9f85-3caf41a0f93c"
193
110/**194/**
111 * struct nd_label_id - identifier string for dpa allocation195 * struct nd_label_id - identifier string for dpa allocation
112 * @id: "{blk|pmem}-<namespace uuid>"196 * @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
51 51
52static int is_uuid_busy(struct device *dev, void *data)52static int is_uuid_busy(struct device *dev, void *data)
53{53{
54 u8 *uuid1 = data, *uuid2 = NULL;54 uuid_t *uuid1 = data, *uuid2 = NULL;
55 55
56 if (is_namespace_pmem(dev)) {56 if (is_namespace_pmem(dev)) {
57 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);57 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
71 uuid2 = nd_pfn->uuid;71 uuid2 = nd_pfn->uuid;
72 }72 }
73 73
74 if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0)74 if (uuid2 && uuid_equal(uuid1, uuid2))
75 return -EBUSY;75 return -EBUSY;
76 76
77 return 0;77 return 0;
@@ -89,7 +89,7 @@ static int is_namespace_uuid_busy(struct device *dev, void *data) · 11 unmodified lines
89 * @dev: any device on a nvdimm_bus89 * @dev: any device on a nvdimm_bus
90 * @uuid: uuid to check90 * @uuid: uuid to check
91 */91 */
92bool nd_is_uuid_unique(struct device *dev, u8 *uuid)92bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid)
93{93{
94 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);94 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
95 95
@@ -192,12 +192,10 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, · 96 unmodified lines
192}192}
193EXPORT_SYMBOL(nvdimm_namespace_disk_name);193EXPORT_SYMBOL(nvdimm_namespace_disk_name);
194 194
195const u8 *nd_dev_to_uuid(struct device *dev)195const uuid_t *nd_dev_to_uuid(struct device *dev)
196{196{
197 static const u8 null_uuid[16];
198
199 if (!dev)197 if (!dev)
200 return null_uuid;198 return &uuid_null;
201 199
202 if (is_namespace_pmem(dev)) {200 if (is_namespace_pmem(dev)) {
203 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);201 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
208 206
209 return nsblk->uuid;207 return nsblk->uuid;
210 } else208 } else
211 return null_uuid;209 return &uuid_null;
212}210}
213EXPORT_SYMBOL(nd_dev_to_uuid);211EXPORT_SYMBOL(nd_dev_to_uuid);
214 212
@@ -938,7 +936,8 @@ static void nd_namespace_pmem_set_resource(struct nd_region *nd_region, · 723 unmodified lines
938 res->end = res->start + size - 1;936 res->end = res->start + size - 1;
939}937}
940 938
941static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where)939static bool uuid_not_set(const uuid_t *uuid, struct device *dev,
940 const char *where)
942{941{
943 if (!uuid) {942 if (!uuid) {
944 dev_dbg(dev, "%s: uuid not set\n", where);943 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
957 struct nd_label_id label_id;956 struct nd_label_id label_id;
958 u32 flags = 0, remainder;957 u32 flags = 0, remainder;
959 int rc, i, id = -1;958 int rc, i, id = -1;
960 u8 *uuid = NULL;959 uuid_t *uuid = NULL;
961 960
962 if (dev->driver || ndns->claim)961 if (dev->driver || ndns->claim)
963 return -EBUSY;962 return -EBUSY;
@@ -1050,7 +1049,7 @@ static ssize_t size_store(struct device *dev, · 86 unmodified lines
1050{1049{
1051 struct nd_region *nd_region = to_nd_region(dev->parent);1050 struct nd_region *nd_region = to_nd_region(dev->parent);
1052 unsigned long long val;1051 unsigned long long val;
1053 u8 **uuid = NULL;1052 uuid_t **uuid = NULL;
1054 int rc;1053 int rc;
1055 1054
1056 rc = kstrtoull(buf, 0, &val);1055 rc = kstrtoull(buf, 0, &val);
@@ -1147,7 +1146,7 @@ static ssize_t size_show(struct device *dev, · 90 unmodified lines
1147}1146}
1148static DEVICE_ATTR(size, 0444, size_show, size_store);1147static DEVICE_ATTR(size, 0444, size_show, size_store);
1149 1148
1150static u8 *namespace_to_uuid(struct device *dev)1149static uuid_t *namespace_to_uuid(struct device *dev)
1151{1150{
1152 if (is_namespace_pmem(dev)) {1151 if (is_namespace_pmem(dev)) {
1153 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);1152 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
1161 return ERR_PTR(-ENXIO);1160 return ERR_PTR(-ENXIO);
1162}1161}
1163 1162
1164static ssize_t uuid_show(struct device *dev,1163static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
1165 struct device_attribute *attr, char *buf)1164 char *buf)
1166{1165{
1167 u8 *uuid = namespace_to_uuid(dev);1166 uuid_t *uuid = namespace_to_uuid(dev);
1168 1167
1169 if (IS_ERR(uuid))1168 if (IS_ERR(uuid))
1170 return PTR_ERR(uuid);1169 return PTR_ERR(uuid);
@@ -1181,7 +1180,8 @@ static ssize_t uuid_show(struct device *dev, · 10 unmodified lines
1181 * @old_uuid: reference to the uuid storage location in the namespace object1180 * @old_uuid: reference to the uuid storage location in the namespace object
1182 */1181 */
1183static int namespace_update_uuid(struct nd_region *nd_region,1182static 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)
1185{1185{
1186 u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0;1186 u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0;
1187 struct nd_label_id old_label_id;1187 struct nd_label_id old_label_id;
@@ -1231,10 +1231,12 @@ static int namespace_update_uuid(struct nd_region *nd_region, · 43 unmodified lines
1231 list_for_each_entry(label_ent, &nd_mapping->labels, list) {1231 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
1232 struct nd_namespace_label *nd_label = label_ent->label;1232 struct nd_namespace_label *nd_label = label_ent->label;
1233 struct nd_label_id label_id;1233 struct nd_label_id label_id;
1234 uuid_t uuid;
1234 1235
1235 if (!nd_label)1236 if (!nd_label)
1236 continue;1237 continue;
1237 nd_label_gen_id(&label_id, nd_label->uuid,1238 nsl_get_uuid(ndd, nd_label, &uuid);
1239 nd_label_gen_id(&label_id, &uuid,
1238 nsl_get_flags(ndd, nd_label));1240 nsl_get_flags(ndd, nd_label));
1239 if (strcmp(old_label_id.id, label_id.id) == 0)1241 if (strcmp(old_label_id.id, label_id.id) == 0)
1240 set_bit(ND_LABEL_REAP, &label_ent->flags);1242 set_bit(ND_LABEL_REAP, &label_ent->flags);
@@ -1251,9 +1253,9 @@ static ssize_t uuid_store(struct device *dev, · 10 unmodified lines
1251 struct device_attribute *attr, const char *buf, size_t len)1253 struct device_attribute *attr, const char *buf, size_t len)
1252{1254{
1253 struct nd_region *nd_region = to_nd_region(dev->parent);1255 struct nd_region *nd_region = to_nd_region(dev->parent);
1254 u8 *uuid = NULL;1256 uuid_t *uuid = NULL;
1257 uuid_t **ns_uuid;
1255 ssize_t rc = 0;1258 ssize_t rc = 0;
1256 u8 **ns_uuid;
1257 1259
1258 if (is_namespace_pmem(dev)) {1260 if (is_namespace_pmem(dev)) {
1259 struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);1261 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
1378{1380{
1379 struct nd_region *nd_region = to_nd_region(dev->parent);1381 struct nd_region *nd_region = to_nd_region(dev->parent);
1380 struct nd_label_id label_id;1382 struct nd_label_id label_id;
1383 uuid_t *uuid = NULL;
1381 int count = 0, i;1384 int count = 0, i;
1382 u8 *uuid = NULL;
1383 u32 flags = 0;1385 u32 flags = 0;
1384 1386
1385 nvdimm_bus_lock(dev);1387 nvdimm_bus_lock(dev);
@@ -1831,8 +1833,8 @@ static struct device **create_namespace_io(struct nd_region *nd_region) · 445 unmodified lines
1831 return devs;1833 return devs;
1832}1834}
1833 1835
1834static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid,1836static bool has_uuid_at_pos(struct nd_region *nd_region, const uuid_t *uuid,
1835 u64 cookie, u16 pos)1837 u64 cookie, u16 pos)
1836{1838{
1837 struct nd_namespace_label *found = NULL;1839 struct nd_namespace_label *found = NULL;
1838 int i;1840 int i;
@@ -1846,17 +1848,16 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 7 unmodified lines
1846 1848
1847 list_for_each_entry(label_ent, &nd_mapping->labels, list) {1849 list_for_each_entry(label_ent, &nd_mapping->labels, list) {
1848 struct nd_namespace_label *nd_label = label_ent->label;1850 struct nd_namespace_label *nd_label = label_ent->label;
1849 u16 position, nlabel;1851 u16 position;
1850 1852
1851 if (!nd_label)1853 if (!nd_label)
1852 continue;1854 continue;
1853 position = nsl_get_position(ndd, nd_label);1855 position = nsl_get_position(ndd, nd_label);
1854 nlabel = nsl_get_nlabel(ndd, nd_label);
1855 1856
1856 if (!nsl_validate_isetcookie(ndd, nd_label, cookie))1857 if (!nsl_validate_isetcookie(ndd, nd_label, cookie))
1857 continue;1858 continue;
1858 1859
1859 if (memcmp(nd_label->uuid, uuid, NSLABEL_UUID_LEN) != 0)1860 if (!nsl_uuid_equal(ndd, nd_label, uuid))
1860 continue;1861 continue;
1861 1862
1862 if (!nsl_validate_type_guid(ndd, nd_label,1863 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
1868 return false;1869 return false;
1869 }1870 }
1870 found_uuid = true;1871 found_uuid = true;
1871 if (nlabel != nd_region->ndr_mappings)1872 if (!nsl_validate_nlabel(nd_region, ndd, nd_label))
1872 continue;1873 continue;
1873 if (position != pos)1874 if (position != pos)
1874 continue;1875 continue;
@@ -1881,7 +1882,7 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 6 unmodified lines
1881 return found != NULL;1882 return found != NULL;
1882}1883}
1883 1884
1884static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)1885static int select_pmem_id(struct nd_region *nd_region, const uuid_t *pmem_id)
1885{1886{
1886 int i;1887 int i;
1887 1888
@@ -1900,7 +1901,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) · 12 unmodified lines
1900 nd_label = label_ent->label;1901 nd_label = label_ent->label;
1901 if (!nd_label)1902 if (!nd_label)
1902 continue;1903 continue;
1903 if (memcmp(nd_label->uuid, pmem_id, NSLABEL_UUID_LEN) == 0)1904 if (nsl_uuid_equal(ndd, nd_label, pmem_id))
1904 break;1905 break;
1905 nd_label = NULL;1906 nd_label = NULL;
1906 }1907 }
@@ -1923,7 +1924,8 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) · 16 unmodified lines
1923 /* pass */;1924 /* pass */;
1924 else {1925 else {
1925 dev_dbg(&nd_region->dev, "%s invalid label for %pUb\n",1926 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));
1927 return -EINVAL;1929 return -EINVAL;
1928 }1930 }
1929 1931
@@ -1953,6 +1955,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 23 unmodified lines
1953 resource_size_t size = 0;1955 resource_size_t size = 0;
1954 struct resource *res;1956 struct resource *res;
1955 struct device *dev;1957 struct device *dev;
1958 uuid_t uuid;
1956 int rc = 0;1959 int rc = 0;
1957 u16 i;1960 u16 i;
1958 1961
@@ -1963,12 +1966,12 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 4 unmodified lines
1963 1966
1964 if (!nsl_validate_isetcookie(ndd, nd_label, cookie)) {1967 if (!nsl_validate_isetcookie(ndd, nd_label, cookie)) {
1965 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",1968 dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
1966 nd_label->uuid);1969 nsl_uuid_raw(ndd, nd_label));
1967 if (!nsl_validate_isetcookie(ndd, nd_label, altcookie))1970 if (!nsl_validate_isetcookie(ndd, nd_label, altcookie))
1968 return ERR_PTR(-EAGAIN);1971 return ERR_PTR(-EAGAIN);
1969 1972
1970 dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",1973 dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
1971 nd_label->uuid);1974 nsl_uuid_raw(ndd, nd_label));
1972 }1975 }
1973 1976
1974 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);1977 nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
@@ -1984,9 +1987,12 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 9 unmodified lines
1984 res->flags = IORESOURCE_MEM;1987 res->flags = IORESOURCE_MEM;
1985 1988
1986 for (i = 0; i < nd_region->ndr_mappings; i++) {1989 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))
1988 continue;1994 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))
1990 continue;1996 continue;
1991 break;1997 break;
1992 }1998 }
@@ -2000,7 +2006,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 7 unmodified lines
2000 * find a dimm with two instances of the same uuid.2006 * find a dimm with two instances of the same uuid.
2001 */2007 */
2002 dev_err(&nd_region->dev, "%s missing label for %pUb\n",2008 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));
2004 rc = -EINVAL;2010 rc = -EINVAL;
2005 goto err;2011 goto err;
2006 }2012 }
@@ -2013,7 +2019,8 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 6 unmodified lines
2013 * the dimm being enabled (i.e. nd_label_reserve_dpa()2019 * the dimm being enabled (i.e. nd_label_reserve_dpa()
2014 * succeeded).2020 * succeeded).
2015 */2021 */
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);
2017 if (rc)2024 if (rc)
2018 goto err;2025 goto err;
2019 2026
@@ -2039,8 +2046,8 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, · 19 unmodified lines
2039 WARN_ON(nspm->alt_name || nspm->uuid);2046 WARN_ON(nspm->alt_name || nspm->uuid);
2040 nspm->alt_name = kmemdup(nsl_ref_name(ndd, label0),2047 nspm->alt_name = kmemdup(nsl_ref_name(ndd, label0),
2041 NSLABEL_NAME_LEN, GFP_KERNEL);2048 NSLABEL_NAME_LEN, GFP_KERNEL);
2042 nspm->uuid = kmemdup((void __force *) label0->uuid,2049 nsl_get_uuid(ndd, label0, &uuid);
2043 NSLABEL_UUID_LEN, GFP_KERNEL);2050 nspm->uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL);
2044 nspm->lbasize = nsl_get_lbasize(ndd, label0);2051 nspm->lbasize = nsl_get_lbasize(ndd, label0);
2045 nspm->nsio.common.claim_class =2052 nspm->nsio.common.claim_class =
2046 nsl_get_claim_class(ndd, label0);2053 nsl_get_claim_class(ndd, label0);
@@ -2217,15 +2224,15 @@ static int add_namespace_resource(struct nd_region *nd_region, · 170 unmodified lines
2217 int i;2224 int i;
2218 2225
2219 for (i = 0; i < count; i++) {2226 for (i = 0; i < count; i++) {
2220 u8 *uuid = namespace_to_uuid(devs[i]);2227 uuid_t *uuid = namespace_to_uuid(devs[i]);
2221 struct resource *res;2228 struct resource *res;
2222 2229
2223 if (IS_ERR_OR_NULL(uuid)) {2230 if (IS_ERR(uuid)) {
2224 WARN_ON(1);2231 WARN_ON(1);
2225 continue;2232 continue;
2226 }2233 }
2227 2234
2228 if (memcmp(uuid, nd_label->uuid, NSLABEL_UUID_LEN) != 0)2235 if (!nsl_uuid_equal(ndd, nd_label, uuid))
2229 continue;2236 continue;
2230 if (is_namespace_blk(devs[i])) {2237 if (is_namespace_blk(devs[i])) {
2231 res = nsblk_add_resource(nd_region, ndd,2238 res = nsblk_add_resource(nd_region, ndd,
@@ -2236,8 +2243,8 @@ static int add_namespace_resource(struct nd_region *nd_region, · 4 unmodified lines
2236 nd_dbg_dpa(nd_region, ndd, res, "%d assign\n", count);2243 nd_dbg_dpa(nd_region, ndd, res, "%d assign\n", count);
2237 } else {2244 } else {
2238 dev_err(&nd_region->dev,2245 dev_err(&nd_region->dev,
2239 "error: conflicting extents for uuid: %pUb\n",2246 "error: conflicting extents for uuid: %pUb\n",
2240 nd_label->uuid);2247 uuid);
2241 return -ENXIO;2248 return -ENXIO;
2242 }2249 }
2243 break;2250 break;
@@ -2257,6 +2264,7 @@ static struct device *create_namespace_blk(struct nd_region *nd_region, · 13 unmodified lines
2257 char name[NSLABEL_NAME_LEN];2264 char name[NSLABEL_NAME_LEN];
2258 struct device *dev = NULL;2265 struct device *dev = NULL;
2259 struct resource *res;2266 struct resource *res;
2267 uuid_t uuid;
2260 2268
2261 if (!nsl_validate_type_guid(ndd, nd_label, &nd_set->type_guid))2269 if (!nsl_validate_type_guid(ndd, nd_label, &nd_set->type_guid))
2262 return ERR_PTR(-EAGAIN);2270 return ERR_PTR(-EAGAIN);
@@ -2271,7 +2279,8 @@ static struct device *create_namespace_blk(struct nd_region *nd_region, · 8 unmodified lines
2271 dev->parent = &nd_region->dev;2279 dev->parent = &nd_region->dev;
2272 nsblk->id = -1;2280 nsblk->id = -1;
2273 nsblk->lbasize = nsl_get_lbasize(ndd, nd_label);2281 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);
2275 nsblk->common.claim_class = nsl_get_claim_class(ndd, nd_label);2284 nsblk->common.claim_class = nsl_get_claim_class(ndd, nd_label);
2276 if (!nsblk->uuid)2285 if (!nsblk->uuid)
2277 goto blk_err;2286 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
126void nd_synchronize(void);126void nd_synchronize(void);
127void __nd_device_register(struct device *dev);127void __nd_device_register(struct device *dev);
128struct nd_label_id;128struct nd_label_id;
129char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags);129char *nd_label_gen_id(struct nd_label_id *label_id, const uuid_t *uuid,
130bool nd_is_uuid_unique(struct device *dev, u8 *uuid);130 u32 flags);
131bool nd_is_uuid_unique(struct device *dev, uuid_t *uuid);
131struct nd_region;132struct nd_region;
132struct nvdimm_drvdata;133struct nvdimm_drvdata;
133struct nd_mapping;134struct nd_mapping;
drivers/nvdimm/nd.h+156−29
@@ -30,6 +30,7 @@ struct nvdimm_drvdata { · 29 unmodified lines
30 int nslabel_size;30 int nslabel_size;
31 struct nd_cmd_get_config_size nsarea;31 struct nd_cmd_get_config_size nsarea;
32 void *data;32 void *data;
33 bool cxl;
33 int ns_current, ns_next;34 int ns_current, ns_next;
34 struct resource dpa;35 struct resource dpa;
35 struct kref kref;36 struct kref kref;
@@ -38,13 +39,17 @@ struct nvdimm_drvdata { · 2 unmodified lines
38static inline const u8 *nsl_ref_name(struct nvdimm_drvdata *ndd,39static inline const u8 *nsl_ref_name(struct nvdimm_drvdata *ndd,
39 struct nd_namespace_label *nd_label)40 struct nd_namespace_label *nd_label)
40{41{
41 return nd_label->name;42 if (ndd->cxl)
43 return nd_label->cxl.name;
44 return nd_label->efi.name;
42}45}
43 46
44static inline u8 *nsl_get_name(struct nvdimm_drvdata *ndd,47static inline u8 *nsl_get_name(struct nvdimm_drvdata *ndd,
45 struct nd_namespace_label *nd_label, u8 *name)48 struct nd_namespace_label *nd_label, u8 *name)
46{49{
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);
48}53}
49 54
50static inline u8 *nsl_set_name(struct nvdimm_drvdata *ndd,55static 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
52{57{
53 if (!name)58 if (!name)
54 return NULL;59 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);
56}63}
57 64
58static inline u32 nsl_get_slot(struct nvdimm_drvdata *ndd,65static inline u32 nsl_get_slot(struct nvdimm_drvdata *ndd,
59 struct nd_namespace_label *nd_label)66 struct nd_namespace_label *nd_label)
60{67{
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);
62}71}
63 72
64static inline void nsl_set_slot(struct nvdimm_drvdata *ndd,73static inline void nsl_set_slot(struct nvdimm_drvdata *ndd,
65 struct nd_namespace_label *nd_label, u32 slot)74 struct nd_namespace_label *nd_label, u32 slot)
66{75{
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);
68}80}
69 81
70static inline u64 nsl_get_checksum(struct nvdimm_drvdata *ndd,82static inline u64 nsl_get_checksum(struct nvdimm_drvdata *ndd,
71 struct nd_namespace_label *nd_label)83 struct nd_namespace_label *nd_label)
72{84{
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);
74}88}
75 89
76static inline void nsl_set_checksum(struct nvdimm_drvdata *ndd,90static inline void nsl_set_checksum(struct nvdimm_drvdata *ndd,
77 struct nd_namespace_label *nd_label,91 struct nd_namespace_label *nd_label,
78 u64 checksum)92 u64 checksum)
79{93{
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);
81}98}
82 99
83static inline u32 nsl_get_flags(struct nvdimm_drvdata *ndd,100static inline u32 nsl_get_flags(struct nvdimm_drvdata *ndd,
84 struct nd_namespace_label *nd_label)101 struct nd_namespace_label *nd_label)
85{102{
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);
87}106}
88 107
89static inline void nsl_set_flags(struct nvdimm_drvdata *ndd,108static inline void nsl_set_flags(struct nvdimm_drvdata *ndd,
90 struct nd_namespace_label *nd_label, u32 flags)109 struct nd_namespace_label *nd_label, u32 flags)
91{110{
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);
93}115}
94 116
95static inline u64 nsl_get_dpa(struct nvdimm_drvdata *ndd,117static inline u64 nsl_get_dpa(struct nvdimm_drvdata *ndd,
96 struct nd_namespace_label *nd_label)118 struct nd_namespace_label *nd_label)
97{119{
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);
99}123}
100 124
101static inline void nsl_set_dpa(struct nvdimm_drvdata *ndd,125static inline void nsl_set_dpa(struct nvdimm_drvdata *ndd,
102 struct nd_namespace_label *nd_label, u64 dpa)126 struct nd_namespace_label *nd_label, u64 dpa)
103{127{
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);
105}132}
106 133
107static inline u64 nsl_get_rawsize(struct nvdimm_drvdata *ndd,134static inline u64 nsl_get_rawsize(struct nvdimm_drvdata *ndd,
108 struct nd_namespace_label *nd_label)135 struct nd_namespace_label *nd_label)
109{136{
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);
111}140}
112 141
113static inline void nsl_set_rawsize(struct nvdimm_drvdata *ndd,142static inline void nsl_set_rawsize(struct nvdimm_drvdata *ndd,
114 struct nd_namespace_label *nd_label,143 struct nd_namespace_label *nd_label,
115 u64 rawsize)144 u64 rawsize)
116{145{
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);
118}150}
119 151
120static inline u64 nsl_get_isetcookie(struct nvdimm_drvdata *ndd,152static inline u64 nsl_get_isetcookie(struct nvdimm_drvdata *ndd,
121 struct nd_namespace_label *nd_label)153 struct nd_namespace_label *nd_label)
122{154{
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);
124}160}
125 161
126static inline void nsl_set_isetcookie(struct nvdimm_drvdata *ndd,162static inline void nsl_set_isetcookie(struct nvdimm_drvdata *ndd,
127 struct nd_namespace_label *nd_label,163 struct nd_namespace_label *nd_label,
128 u64 isetcookie)164 u64 isetcookie)
129{165{
130 nd_label->isetcookie = __cpu_to_le64(isetcookie);166 if (!ndd->cxl)
167 nd_label->efi.isetcookie = __cpu_to_le64(isetcookie);
131}168}
132 169
133static inline bool nsl_validate_isetcookie(struct nvdimm_drvdata *ndd,170static inline bool nsl_validate_isetcookie(struct nvdimm_drvdata *ndd,
134 struct nd_namespace_label *nd_label,171 struct nd_namespace_label *nd_label,
135 u64 cookie)172 u64 cookie)
136{173{
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);
138}181}
139 182
140static inline u16 nsl_get_position(struct nvdimm_drvdata *ndd,183static inline u16 nsl_get_position(struct nvdimm_drvdata *ndd,
141 struct nd_namespace_label *nd_label)184 struct nd_namespace_label *nd_label)
142{185{
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);
144}189}
145 190
146static inline void nsl_set_position(struct nvdimm_drvdata *ndd,191static inline void nsl_set_position(struct nvdimm_drvdata *ndd,
147 struct nd_namespace_label *nd_label,192 struct nd_namespace_label *nd_label,
148 u16 position)193 u16 position)
149{194{
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);
151}199}
152 200
153
154static inline u16 nsl_get_nlabel(struct nvdimm_drvdata *ndd,201static inline u16 nsl_get_nlabel(struct nvdimm_drvdata *ndd,
155 struct nd_namespace_label *nd_label)202 struct nd_namespace_label *nd_label)
156{203{
157 return __le16_to_cpu(nd_label->nlabel);204 if (ndd->cxl)
205 return 0;
206 return __le16_to_cpu(nd_label->efi.nlabel);
158}207}
159 208
160static inline void nsl_set_nlabel(struct nvdimm_drvdata *ndd,209static inline void nsl_set_nlabel(struct nvdimm_drvdata *ndd,
161 struct nd_namespace_label *nd_label,210 struct nd_namespace_label *nd_label,
162 u16 nlabel)211 u16 nlabel)
163{212{
164 nd_label->nlabel = __cpu_to_le16(nlabel);213 if (!ndd->cxl)
214 nd_label->efi.nlabel = __cpu_to_le16(nlabel);
215}
216
217static 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
225static 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);
165}231}
166 232
167static inline u64 nsl_get_lbasize(struct nvdimm_drvdata *ndd,233static inline u64 nsl_get_lbasize(struct nvdimm_drvdata *ndd,
168 struct nd_namespace_label *nd_label)234 struct nd_namespace_label *nd_label)
169{235{
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);
171}243}
172 244
173static inline void nsl_set_lbasize(struct nvdimm_drvdata *ndd,245static inline void nsl_set_lbasize(struct nvdimm_drvdata *ndd,
174 struct nd_namespace_label *nd_label,246 struct nd_namespace_label *nd_label,
175 u64 lbasize)247 u64 lbasize)
176{248{
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
255static 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
266static 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
277static 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
290static 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;
178}296}
179 297
180bool nsl_validate_blk_isetcookie(struct nvdimm_drvdata *ndd,298bool 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
233 351
234unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd);352unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd);
235 353
236#define namespace_label_has(ndd, field) \354#define efi_namespace_label_has(ndd, field) \
237 (offsetof(struct nd_namespace_label, field) \355 (!ndd->cxl && offsetof(struct nvdimm_efi_label, field) \
238 < sizeof_namespace_label(ndd))356 < sizeof_namespace_label(ndd))
239 357
240#define nd_dbg_dpa(r, d, res, fmt, arg...) \358#define nd_dbg_dpa(r, d, res, fmt, arg...) \
@@ -310,6 +428,15 @@ struct nd_region { · 69 unmodified lines
310 struct nd_mapping mapping[];428 struct nd_mapping mapping[];
311};429};
312 430
431static 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
313struct nd_blk_region {440struct nd_blk_region {
314 int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev);441 int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev);
315 int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,442 int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
@@ -335,7 +462,7 @@ struct nd_btt { · 19 unmodified lines
335 struct btt *btt;462 struct btt *btt;
336 unsigned long lbasize;463 unsigned long lbasize;
337 u64 size;464 u64 size;
338 u8 *uuid;465 uuid_t *uuid;
339 int id;466 int id;
340 int initial_offset;467 int initial_offset;
341 u16 version_major;468 u16 version_major;
@@ -350,7 +477,7 @@ enum nd_pfn_mode { · 8 unmodified lines
350 477
351struct nd_pfn {478struct nd_pfn {
352 int id;479 int id;
353 u8 *uuid;480 uuid_t *uuid;
354 struct device dev;481 struct device dev;
355 unsigned long align;482 unsigned long align;
356 unsigned long npfns;483 unsigned long npfns;
@@ -378,7 +505,7 @@ void wait_nvdimm_bus_probe_idle(struct device *dev); · 21 unmodified lines
378void nd_device_register(struct device *dev);505void nd_device_register(struct device *dev);
379void nd_device_unregister(struct device *dev, enum nd_async_mode mode);506void nd_device_unregister(struct device *dev, enum nd_async_mode mode);
380void nd_device_notify(struct device *dev, enum nvdimm_event event);507void nd_device_notify(struct device *dev, enum nvdimm_event event);
381int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,508int nd_uuid_store(struct device *dev, uuid_t **uuid_out, const char *buf,
382 size_t len);509 size_t len);
383ssize_t nd_size_select_show(unsigned long current_size,510ssize_t nd_size_select_show(unsigned long current_size,
384 const unsigned long *supported, char *buf);511 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
561 return false;688 return false;
562}689}
563resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk);690resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk);
564const u8 *nd_dev_to_uuid(struct device *dev);691const uuid_t *nd_dev_to_uuid(struct device *dev);
565bool pmem_should_map_pages(struct device *dev);692bool pmem_should_map_pages(struct device *dev);
566#endif /* __ND_H__ */693#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
452 unsigned long align, start_pad;452 unsigned long align, start_pad;
453 struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;453 struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;
454 struct nd_namespace_common *ndns = nd_pfn->ndns;454 struct nd_namespace_common *ndns = nd_pfn->ndns;
455 const u8 *parent_uuid = nd_dev_to_uuid(&ndns->dev);455 const uuid_t *parent_uuid = nd_dev_to_uuid(&ndns->dev);
456 456
457 if (!pfn_sb || !ndns)457 if (!pfn_sb || !ndns)
458 return -ENODEV;458 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
732}732}
733EXPORT_SYMBOL_GPL(pci_find_vsec_capability);733EXPORT_SYMBOL_GPL(pci_find_vsec_capability);
734 734
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 */
744u16 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}
765EXPORT_SYMBOL_GPL(pci_find_dvsec_capability);
766
735/**767/**
736 * pci_find_parent_resource - return resource region of parent bus of given768 * pci_find_parent_resource - return resource region of parent bus of given
737 * region769 * region
include/linux/nd.h+2−2
@@ -88,7 +88,7 @@ struct nd_namespace_pmem { · 87 unmodified lines
88 struct nd_namespace_io nsio;88 struct nd_namespace_io nsio;
89 unsigned long lbasize;89 unsigned long lbasize;
90 char *alt_name;90 char *alt_name;
91 u8 *uuid;91 uuid_t *uuid;
92 int id;92 int id;
93};93};
94 94
@@ -105,7 +105,7 @@ struct nd_namespace_pmem { · 10 unmodified lines
105struct nd_namespace_blk {105struct nd_namespace_blk {
106 struct nd_namespace_common common;106 struct nd_namespace_common common;
107 char *alt_name;107 char *alt_name;
108 u8 *uuid;108 uuid_t *uuid;
109 int id;109 int id;
110 unsigned long lbasize;110 unsigned long lbasize;
111 resource_size_t size;111 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
1132u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 pos, int cap);1132u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 pos, int cap);
1133struct pci_bus *pci_find_next_bus(const struct pci_bus *from);1133struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
1134u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap);1134u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap);
1135u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec);
1135 1136
1136u64 pci_get_dsn(struct pci_dev *dev);1137u64 pci_get_dsn(struct pci_dev *dev);
1137 1138
tools/testing/cxl/Kbuild+38−0
@@ -0,0 +1,38 @@
1# SPDX-License-Identifier: GPL-2.0
2ldflags-y += --wrap=is_acpi_device_node
3ldflags-y += --wrap=acpi_get_table
4ldflags-y += --wrap=acpi_put_table
5ldflags-y += --wrap=acpi_evaluate_integer
6ldflags-y += --wrap=acpi_pci_find_root
7ldflags-y += --wrap=pci_walk_bus
8ldflags-y += --wrap=nvdimm_bus_register
9
10DRIVERS := ../../../drivers
11CXL_SRC := $(DRIVERS)/cxl
12CXL_CORE_SRC := $(DRIVERS)/cxl/core
13ccflags-y := -I$(srctree)/drivers/cxl/
14ccflags-y += -D__mock=__weak
15
16obj-m += cxl_acpi.o
17
18cxl_acpi-y := $(CXL_SRC)/acpi.o
19cxl_acpi-y += mock_acpi.o
20cxl_acpi-y += config_check.o
21
22obj-m += cxl_pmem.o
23
24cxl_pmem-y := $(CXL_SRC)/pmem.o
25cxl_pmem-y += config_check.o
26
27obj-m += cxl_core.o
28
29cxl_core-y := $(CXL_CORE_SRC)/bus.o
30cxl_core-y += $(CXL_CORE_SRC)/pmem.o
31cxl_core-y += $(CXL_CORE_SRC)/regs.o
32cxl_core-y += $(CXL_CORE_SRC)/memdev.o
33cxl_core-y += $(CXL_CORE_SRC)/mbox.o
34cxl_core-y += config_check.o
35
36cxl_core-y += mock_pmem.o
37
38obj-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
4void 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
11struct 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 }
33out:
34 put_cxl_mock_ops(index);
35 return found;
36}
37
38static 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
74static 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
95int 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
7int 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
2ccflags-y := -I$(srctree)/drivers/cxl/
3
4obj-m += cxl_test.o
5obj-m += cxl_mock.o
6obj-m += cxl_mock_mem.o
7
8cxl_test-y := cxl.o
9cxl_mock-y := mock.o
10cxl_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
16static struct platform_device *cxl_acpi;
17static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES];
18static struct platform_device
19 *cxl_root_port[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
20struct platform_device *cxl_mem[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
21
22static struct acpi_device acpi0017_mock;
23static 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
38static 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
50static 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
64static 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
185struct cxl_mock_res {
186 struct list_head list;
187 struct range range;
188};
189
190static LIST_HEAD(mock_res);
191static DEFINE_MUTEX(mock_res_lock);
192static struct gen_pool *cxl_mock_pool;
193
194static 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
208static 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
233static 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
272static 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
282static 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
289static 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
300static int host_bridge_index(struct acpi_device *adev)
301{
302 return adev - host_bridge;
303}
304
305static 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
315static acpi_status
316mock_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
329static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES];
330static 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
345static 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
355static 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
365static 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
375static 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
384static 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
398static 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
414static 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
447err:
448 platform_device_put(pdev);
449 return NULL;
450}
451
452static __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
538err_add:
539 platform_device_put(cxl_acpi);
540err_mem:
541 for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--)
542 platform_device_unregister(cxl_mem[i]);
543err_port:
544 for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
545 platform_device_unregister(cxl_root_port[i]);
546err_bridge:
547 for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
548 platform_device_unregister(cxl_host_bridge[i]);
549err_populate:
550 depopulate_all_mock_resources();
551err_gen_pool_add:
552 gen_pool_destroy(cxl_mock_pool);
553err_gen_pool_create:
554 unregister_cxl_mock_ops(&cxl_mock_ops);
555 return rc;
556}
557
558static __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
574module_init(cxl_test_init);
575module_exit(cxl_test_exit);
576MODULE_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
14static 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
33static 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
46static 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
57static 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
81static 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
123static 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
142static 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
159static 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
190static void label_area_release(void *lsa)
191{
192 vfree(lsa);
193}
194
195static 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
240static const struct platform_device_id cxl_mock_mem_ids[] = {
241 { .name = "cxl_mem", },
242 { },
243};
244MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
245
246static 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
254module_platform_driver(cxl_mock_mem_driver);
255MODULE_LICENSE("GPL v2");
256MODULE_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
12static LIST_HEAD(mock);
13
14void register_cxl_mock_ops(struct cxl_mock_ops *ops)
15{
16 list_add_rcu(&ops->list, &mock);
17}
18EXPORT_SYMBOL_GPL(register_cxl_mock_ops);
19
20static DEFINE_SRCU(cxl_mock_srcu);
21
22void unregister_cxl_mock_ops(struct cxl_mock_ops *ops)
23{
24 list_del_rcu(&ops->list);
25 synchronize_srcu(&cxl_mock_srcu);
26}
27EXPORT_SYMBOL_GPL(unregister_cxl_mock_ops);
28
29struct 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}
34EXPORT_SYMBOL_GPL(get_cxl_mock_ops);
35
36void put_cxl_mock_ops(int index)
37{
38 srcu_read_unlock(&cxl_mock_srcu, index);
39}
40EXPORT_SYMBOL_GPL(put_cxl_mock_ops);
41
42bool __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}
59EXPORT_SYMBOL(__wrap_is_acpi_device_node);
60
61acpi_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}
77EXPORT_SYMBOL(__wrap_acpi_get_table);
78
79void __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}
90EXPORT_SYMBOL(__wrap_acpi_put_table);
91
92acpi_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}
111EXPORT_SYMBOL(__wrap_acpi_evaluate_integer);
112
113struct 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}
128EXPORT_SYMBOL_GPL(__wrap_acpi_pci_find_root);
129
130void __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}
154EXPORT_SYMBOL_GPL(__wrap_pci_walk_bus);
155
156struct 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}
169EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register);
170
171MODULE_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
6struct 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
24void register_cxl_mock_ops(struct cxl_mock_ops *ops);
25void unregister_cxl_mock_ops(struct cxl_mock_ops *ops);
26struct cxl_mock_ops *get_cxl_mock_ops(int *index);
27void put_cxl_mock_ops(int index);