| @@ -15,13 +15,15 @@ · 14 unmodified lines |
| 15 | 15 | #include <linux/seq_buf.h> |
| 16 | 16 | |
| 17 | 17 | #include <asm/plpar_wrappers.h> |
| 18 | +#include <asm/papr_pdsm.h> |
| 18 | 19 | |
| 19 | 20 | #define BIND_ANY_ADDR (~0ul) |
| 20 | 21 | |
| 21 | 22 | #define PAPR_SCM_DIMM_CMD_MASK \ |
| 22 | 23 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ |
| 23 | 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 | 28 | /* DIMM health bitmap bitmap indicators */ |
| 27 | 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 | 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 | 529 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, |
| 353 | 530 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| 354 | 531 | unsigned int buf_len, int *cmd_rc) |
| 355 | 532 | { |
| 356 | 533 | struct nd_cmd_get_config_size *get_size_hdr; |
| 534 | + struct nd_cmd_pkg *call_pkg = NULL; |
| 357 | 535 | struct papr_scm_priv *p; |
| 358 | 536 | int rc; |
| 359 | 537 | |
| 360 | | − /* Only dimm-specific calls are supported atm */ |
| 361 | | − if (!nvdimm) |
| 362 | | − return -EINVAL; |
| 538 | + rc = is_cmd_valid(nvdimm, cmd, buf, buf_len); |
| 539 | + if (rc) { |
| 540 | + pr_debug("Invalid cmd=0x%x. Err=%d\n", cmd, rc); |
| 541 | + return rc; |
| 542 | + } |
| 363 | 543 | |
| 364 | 544 | /* Use a local variable in case cmd_rc pointer is NULL */ |
| 365 | 545 | if (!cmd_rc) |
| @@ -385,6 +565,11 @@ static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, · 19 unmodified lines |
| 385 | 565 | *cmd_rc = papr_scm_meta_set(p, buf); |
| 386 | 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 | 573 | default: |
| 389 | 574 | dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); |
| 390 | 575 | return -EINVAL; |