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