| @@ -15,13 +15,15 @@ · 14 unmodified lines |
| 15 | #include <linux/seq_buf.h> | 15 | #include <linux/seq_buf.h> |
| 16 | | 16 | |
| 17 | #include <asm/plpar_wrappers.h> | 17 | #include <asm/plpar_wrappers.h> |
| | 18 | #include <asm/papr_pdsm.h> |
| 18 | | 19 | |
| 19 | #define BIND_ANY_ADDR (~0ul) | 20 | #define BIND_ANY_ADDR (~0ul) |
| 20 | | 21 | |
| 21 | #define PAPR_SCM_DIMM_CMD_MASK \ | 22 | #define PAPR_SCM_DIMM_CMD_MASK \ |
| 22 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ | 23 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ |
| 23 | (1ul << ND_CMD_GET_CONFIG_DATA) | \ | 24 | (1ul << ND_CMD_GET_CONFIG_DATA) | \ |
| 24 | (1ul << ND_CMD_SET_CONFIG_DATA)) | 25 | (1ul << ND_CMD_SET_CONFIG_DATA) | \ |
| | 26 | (1ul << ND_CMD_CALL)) |
| 25 | | 27 | |
| 26 | /* DIMM health bitmap bitmap indicators */ | 28 | /* DIMM health bitmap bitmap indicators */ |
| 27 | /* SCM device is unable to persist memory contents */ | 29 | /* SCM device is unable to persist memory contents */ |
| @@ -349,17 +351,195 @@ static int papr_scm_meta_set(struct papr_scm_priv *p, · 321 unmodified lines |
| 349 | return 0; | 351 | return 0; |
| 350 | } | 352 | } |
| 351 | | 353 | |
| | 354 | /* |
| | 355 | * Do a sanity checks on the inputs args to dimm-control function and return |
| | 356 | * '0' if valid. Validation of PDSM payloads happens later in |
| | 357 | * papr_scm_service_pdsm. |
| | 358 | */ |
| | 359 | static int is_cmd_valid(struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| | 360 | unsigned int buf_len) |
| | 361 | { |
| | 362 | unsigned long cmd_mask = PAPR_SCM_DIMM_CMD_MASK; |
| | 363 | struct nd_cmd_pkg *nd_cmd; |
| | 364 | struct papr_scm_priv *p; |
| | 365 | enum papr_pdsm pdsm; |
| | 366 | |
| | 367 | /* Only dimm-specific calls are supported atm */ |
| | 368 | if (!nvdimm) |
| | 369 | return -EINVAL; |
| | 370 | |
| | 371 | /* get the provider data from struct nvdimm */ |
| | 372 | p = nvdimm_provider_data(nvdimm); |
| | 373 | |
| | 374 | if (!test_bit(cmd, &cmd_mask)) { |
| | 375 | dev_dbg(&p->pdev->dev, "Unsupported cmd=%u\n", cmd); |
| | 376 | return -EINVAL; |
| | 377 | } |
| | 378 | |
| | 379 | /* For CMD_CALL verify pdsm request */ |
| | 380 | if (cmd == ND_CMD_CALL) { |
| | 381 | /* Verify the envelope and envelop size */ |
| | 382 | if (!buf || |
| | 383 | buf_len < (sizeof(struct nd_cmd_pkg) + ND_PDSM_HDR_SIZE)) { |
| | 384 | dev_dbg(&p->pdev->dev, "Invalid pkg size=%u\n", |
| | 385 | buf_len); |
| | 386 | return -EINVAL; |
| | 387 | } |
| | 388 | |
| | 389 | /* Verify that the nd_cmd_pkg.nd_family is correct */ |
| | 390 | nd_cmd = (struct nd_cmd_pkg *)buf; |
| | 391 | |
| | 392 | if (nd_cmd->nd_family != NVDIMM_FAMILY_PAPR) { |
| | 393 | dev_dbg(&p->pdev->dev, "Invalid pkg family=0x%llx\n", |
| | 394 | nd_cmd->nd_family); |
| | 395 | return -EINVAL; |
| | 396 | } |
| | 397 | |
| | 398 | pdsm = (enum papr_pdsm)nd_cmd->nd_command; |
| | 399 | |
| | 400 | /* Verify if the pdsm command is valid */ |
| | 401 | if (pdsm <= PAPR_PDSM_MIN || pdsm >= PAPR_PDSM_MAX) { |
| | 402 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid PDSM\n", |
| | 403 | pdsm); |
| | 404 | return -EINVAL; |
| | 405 | } |
| | 406 | |
| | 407 | /* Have enough space to hold returned 'nd_pkg_pdsm' header */ |
| | 408 | if (nd_cmd->nd_size_out < ND_PDSM_HDR_SIZE) { |
| | 409 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid payload\n", |
| | 410 | pdsm); |
| | 411 | return -EINVAL; |
| | 412 | } |
| | 413 | } |
| | 414 | |
| | 415 | /* Let the command be further processed */ |
| | 416 | return 0; |
| | 417 | } |
| | 418 | |
| | 419 | /* |
| | 420 | * 'struct pdsm_cmd_desc' |
| | 421 | * Identifies supported PDSMs' expected length of in/out payloads |
| | 422 | * and pdsm service function. |
| | 423 | * |
| | 424 | * size_in : Size of input payload if any in the PDSM request. |
| | 425 | * size_out : Size of output payload if any in the PDSM request. |
| | 426 | * service : Service function for the PDSM request. Return semantics: |
| | 427 | * rc < 0 : Error servicing PDSM and rc indicates the error. |
| | 428 | * rc >=0 : Serviced successfully and 'rc' indicate number of |
| | 429 | * bytes written to payload. |
| | 430 | */ |
| | 431 | struct pdsm_cmd_desc { |
| | 432 | u32 size_in; |
| | 433 | u32 size_out; |
| | 434 | int (*service)(struct papr_scm_priv *dimm, |
| | 435 | union nd_pdsm_payload *payload); |
| | 436 | }; |
| | 437 | |
| | 438 | /* Holds all supported PDSMs' command descriptors */ |
| | 439 | static const struct pdsm_cmd_desc __pdsm_cmd_descriptors[] = { |
| | 440 | [PAPR_PDSM_MIN] = { |
| | 441 | .size_in = 0, |
| | 442 | .size_out = 0, |
| | 443 | .service = NULL, |
| | 444 | }, |
| | 445 | /* New PDSM command descriptors to be added below */ |
| | 446 | |
| | 447 | /* Empty */ |
| | 448 | [PAPR_PDSM_MAX] = { |
| | 449 | .size_in = 0, |
| | 450 | .size_out = 0, |
| | 451 | .service = NULL, |
| | 452 | }, |
| | 453 | }; |
| | 454 | |
| | 455 | /* Given a valid pdsm cmd return its command descriptor else return NULL */ |
| | 456 | static inline const struct pdsm_cmd_desc *pdsm_cmd_desc(enum papr_pdsm cmd) |
| | 457 | { |
| | 458 | if (cmd >= 0 || cmd < ARRAY_SIZE(__pdsm_cmd_descriptors)) |
| | 459 | return &__pdsm_cmd_descriptors[cmd]; |
| | 460 | |
| | 461 | return NULL; |
| | 462 | } |
| | 463 | |
| | 464 | /* |
| | 465 | * For a given pdsm request call an appropriate service function. |
| | 466 | * Returns errors if any while handling the pdsm command package. |
| | 467 | */ |
| | 468 | static int papr_scm_service_pdsm(struct papr_scm_priv *p, |
| | 469 | struct nd_cmd_pkg *pkg) |
| | 470 | { |
| | 471 | /* Get the PDSM header and PDSM command */ |
| | 472 | struct nd_pkg_pdsm *pdsm_pkg = (struct nd_pkg_pdsm *)pkg->nd_payload; |
| | 473 | enum papr_pdsm pdsm = (enum papr_pdsm)pkg->nd_command; |
| | 474 | const struct pdsm_cmd_desc *pdsc; |
| | 475 | int rc; |
| | 476 | |
| | 477 | /* Fetch corresponding pdsm descriptor for validation and servicing */ |
| | 478 | pdsc = pdsm_cmd_desc(pdsm); |
| | 479 | |
| | 480 | /* Validate pdsm descriptor */ |
| | 481 | /* Ensure that reserved fields are 0 */ |
| | 482 | if (pdsm_pkg->reserved[0] || pdsm_pkg->reserved[1]) { |
| | 483 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid reserved field\n", |
| | 484 | pdsm); |
| | 485 | return -EINVAL; |
| | 486 | } |
| | 487 | |
| | 488 | /* If pdsm expects some input, then ensure that the size_in matches */ |
| | 489 | if (pdsc->size_in && |
| | 490 | pkg->nd_size_in != (pdsc->size_in + ND_PDSM_HDR_SIZE)) { |
| | 491 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_in=%d\n", |
| | 492 | pdsm, pkg->nd_size_in); |
| | 493 | return -EINVAL; |
| | 494 | } |
| | 495 | |
| | 496 | /* If pdsm wants to return data, then ensure that size_out matches */ |
| | 497 | if (pdsc->size_out && |
| | 498 | pkg->nd_size_out != (pdsc->size_out + ND_PDSM_HDR_SIZE)) { |
| | 499 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_out=%d\n", |
| | 500 | pdsm, pkg->nd_size_out); |
| | 501 | return -EINVAL; |
| | 502 | } |
| | 503 | |
| | 504 | /* Service the pdsm */ |
| | 505 | if (pdsc->service) { |
| | 506 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Servicing..\n", pdsm); |
| | 507 | |
| | 508 | rc = pdsc->service(p, &pdsm_pkg->payload); |
| | 509 | |
| | 510 | if (rc < 0) { |
| | 511 | /* error encountered while servicing pdsm */ |
| | 512 | pdsm_pkg->cmd_status = rc; |
| | 513 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE; |
| | 514 | } else { |
| | 515 | /* pdsm serviced and 'rc' bytes written to payload */ |
| | 516 | pdsm_pkg->cmd_status = 0; |
| | 517 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE + rc; |
| | 518 | } |
| | 519 | } else { |
| | 520 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Unsupported PDSM request\n", |
| | 521 | pdsm); |
| | 522 | pdsm_pkg->cmd_status = -ENOENT; |
| | 523 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE; |
| | 524 | } |
| | 525 | |
| | 526 | return pdsm_pkg->cmd_status; |
| | 527 | } |
| | 528 | |
| 352 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, | 529 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, |
| 353 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, | 530 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| 354 | unsigned int buf_len, int *cmd_rc) | 531 | unsigned int buf_len, int *cmd_rc) |
| 355 | { | 532 | { |
| 356 | struct nd_cmd_get_config_size *get_size_hdr; | 533 | struct nd_cmd_get_config_size *get_size_hdr; |
| | 534 | struct nd_cmd_pkg *call_pkg = NULL; |
| 357 | struct papr_scm_priv *p; | 535 | struct papr_scm_priv *p; |
| 358 | int rc; | 536 | int rc; |
| 359 | | 537 | |
| 360 | /* Only dimm-specific calls are supported atm */ | 538 | rc = is_cmd_valid(nvdimm, cmd, buf, buf_len); |
| 361 | if (!nvdimm) | 539 | if (rc) { |
| 362 | return -EINVAL; | 540 | pr_debug("Invalid cmd=0x%x. Err=%d\n", cmd, rc); |
| | 541 | return rc; |
| | 542 | } |
| 363 | | 543 | |
| 364 | /* Use a local variable in case cmd_rc pointer is NULL */ | 544 | /* Use a local variable in case cmd_rc pointer is NULL */ |
| 365 | if (!cmd_rc) | 545 | if (!cmd_rc) |
| @@ -385,6 +565,11 @@ static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, · 19 unmodified lines |
| 385 | *cmd_rc = papr_scm_meta_set(p, buf); | 565 | *cmd_rc = papr_scm_meta_set(p, buf); |
| 386 | break; | 566 | break; |
| 387 | | 567 | |
| | 568 | case ND_CMD_CALL: |
| | 569 | call_pkg = (struct nd_cmd_pkg *)buf; |
| | 570 | *cmd_rc = papr_scm_service_pdsm(p, call_pkg); |
| | 571 | break; |
| | 572 | |
| 388 | default: | 573 | default: |
| 389 | dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); | 574 | dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); |
| 390 | return -EINVAL; | 575 | return -EINVAL; |