|
28 | 28 | #include "sandisk-utils.h" |
29 | 29 | #include "plugins/wdc/wdc-nvme-cmds.h" |
30 | 30 |
|
| 31 | +static __u8 ocp_C2_guid[SNDK_GUID_LENGTH] = { |
| 32 | + 0x6D, 0x79, 0x9A, 0x76, 0xB4, 0xDA, 0xF6, 0xA3, |
| 33 | + 0xE2, 0x4D, 0xB2, 0x8A, 0xAC, 0xF3, 0x1C, 0xD1 |
| 34 | +}; |
| 35 | + |
31 | 36 | static int sndk_do_cap_telemetry_log(struct nvme_dev *dev, const char *file, |
32 | 37 | __u32 bs, int type, int data_area) |
33 | 38 | { |
@@ -494,18 +499,366 @@ static int sndk_drive_resize(int argc, char **argv, |
494 | 499 | return run_wdc_drive_resize(argc, argv, command, plugin); |
495 | 500 | } |
496 | 501 |
|
| 502 | +static void sndk_print_fw_act_history_log_normal(__u8 *data, int num_entries) |
| 503 | +{ |
| 504 | + int i, j; |
| 505 | + char previous_fw[9]; |
| 506 | + char new_fw[9]; |
| 507 | + char commit_action_bin[8]; |
| 508 | + char time_str[100]; |
| 509 | + __u16 oldestEntryIdx = 0, entryIdx = 0; |
| 510 | + uint64_t timestamp; |
| 511 | + const char *null_fw = "--------"; |
| 512 | + |
| 513 | + memset((void *)time_str, '\0', 100); |
| 514 | + |
| 515 | + if (data[0] == SNDK_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { |
| 516 | + printf(" Firmware Activate History Log\n"); |
| 517 | + printf(" Power Cycle Previous New\n"); |
| 518 | + printf(" Entry Timestamp Count Firmware Firmware Slot Action Result\n"); |
| 519 | + printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); |
| 520 | + |
| 521 | + struct sndk_fw_act_history_log_format_c2 *fw_act_history_entry = (struct sndk_fw_act_history_log_format_c2 *)(data); |
| 522 | + |
| 523 | + oldestEntryIdx = SNDK_MAX_NUM_ACT_HIST_ENTRIES; |
| 524 | + if (num_entries == SNDK_MAX_NUM_ACT_HIST_ENTRIES) { |
| 525 | + /* find lowest/oldest entry */ |
| 526 | + for (i = 0; i < num_entries; i++) { |
| 527 | + j = (i+1 == SNDK_MAX_NUM_ACT_HIST_ENTRIES) ? 0 : i+1; |
| 528 | + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > |
| 529 | + le16_to_cpu(fw_act_history_entry->entry[j].fw_act_hist_entries)) { |
| 530 | + oldestEntryIdx = j; |
| 531 | + break; |
| 532 | + } |
| 533 | + } |
| 534 | + } |
| 535 | + if (oldestEntryIdx == SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 536 | + entryIdx = 0; |
| 537 | + else |
| 538 | + entryIdx = oldestEntryIdx; |
| 539 | + |
| 540 | + for (i = 0; i < num_entries; i++) { |
| 541 | + memset((void *)previous_fw, 0, 9); |
| 542 | + memset((void *)new_fw, 0, 9); |
| 543 | + memset((void *)commit_action_bin, 0, 8); |
| 544 | + |
| 545 | + memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), 8); |
| 546 | + if (strlen((char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version)) > 1) |
| 547 | + memcpy(new_fw, (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), 8); |
| 548 | + else |
| 549 | + memcpy(new_fw, null_fw, 8); |
| 550 | + |
| 551 | + printf("%5"PRIu16"", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); |
| 552 | + |
| 553 | + timestamp = (0x0000FFFFFFFFFFFF & |
| 554 | + le64_to_cpu( |
| 555 | + fw_act_history_entry->entry[entryIdx].timestamp)); |
| 556 | + printf(" "); |
| 557 | + printf("%16"PRIu64"", timestamp); |
| 558 | + printf(" "); |
| 559 | + |
| 560 | + printf("%16"PRIu64"", (uint64_t)le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); |
| 561 | + printf(" "); |
| 562 | + printf("%s", (char *)previous_fw); |
| 563 | + printf(" "); |
| 564 | + printf("%s", (char *)new_fw); |
| 565 | + printf(" "); |
| 566 | + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->entry[entryIdx].slot_number); |
| 567 | + printf(" "); |
| 568 | + sndk_get_commit_action_bin( |
| 569 | + fw_act_history_entry->entry[entryIdx].commit_action_type, |
| 570 | + (char *)&commit_action_bin); |
| 571 | + printf(" %s", (char *)commit_action_bin); |
| 572 | + printf(" "); |
| 573 | + if (!le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)) |
| 574 | + printf("pass"); |
| 575 | + else |
| 576 | + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)); |
| 577 | + printf("\n"); |
| 578 | + |
| 579 | + entryIdx++; |
| 580 | + if (entryIdx >= SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 581 | + entryIdx = 0; |
| 582 | + } |
| 583 | + } else |
| 584 | + fprintf(stderr, "ERROR: SNDK: %s: Unknown log page\n", __func__); |
| 585 | +} |
| 586 | + |
| 587 | +static void sndk_print_fw_act_history_log_json(__u8 *data, int num_entries) |
| 588 | +{ |
| 589 | + struct json_object *root = json_create_object(); |
| 590 | + int i, j; |
| 591 | + char previous_fw[9]; |
| 592 | + char new_fw[9]; |
| 593 | + char commit_action_bin[8]; |
| 594 | + char fail_str[32]; |
| 595 | + char time_str[100]; |
| 596 | + char ext_time_str[20]; |
| 597 | + uint64_t timestamp; |
| 598 | + |
| 599 | + memset((void *)previous_fw, 0, 9); |
| 600 | + memset((void *)new_fw, 0, 9); |
| 601 | + memset((void *)commit_action_bin, 0, 8); |
| 602 | + memset((void *)time_str, '\0', 100); |
| 603 | + memset((void *)ext_time_str, 0, 20); |
| 604 | + memset((void *)fail_str, 0, 11); |
| 605 | + char *null_fw = "--------"; |
| 606 | + __u16 oldestEntryIdx = 0, entryIdx = 0; |
| 607 | + |
| 608 | + if (data[0] == SNDK_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { |
| 609 | + struct sndk_fw_act_history_log_format_c2 *fw_act_history_entry = (struct sndk_fw_act_history_log_format_c2 *)(data); |
| 610 | + |
| 611 | + oldestEntryIdx = SNDK_MAX_NUM_ACT_HIST_ENTRIES; |
| 612 | + if (num_entries == SNDK_MAX_NUM_ACT_HIST_ENTRIES) { |
| 613 | + /* find lowest/oldest entry */ |
| 614 | + for (i = 0; i < num_entries; i++) { |
| 615 | + j = (i+1 == SNDK_MAX_NUM_ACT_HIST_ENTRIES) ? 0 : i+1; |
| 616 | + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > |
| 617 | + le16_to_cpu(fw_act_history_entry->entry[j].fw_act_hist_entries)) { |
| 618 | + oldestEntryIdx = j; |
| 619 | + break; |
| 620 | + } |
| 621 | + } |
| 622 | + } |
| 623 | + if (oldestEntryIdx == SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 624 | + entryIdx = 0; |
| 625 | + else |
| 626 | + entryIdx = oldestEntryIdx; |
| 627 | + |
| 628 | + for (i = 0; i < num_entries; i++) { |
| 629 | + memcpy(previous_fw, |
| 630 | + (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), |
| 631 | + 8); |
| 632 | + if (strlen((char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version)) > 1) |
| 633 | + memcpy(new_fw, |
| 634 | + (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), |
| 635 | + 8); |
| 636 | + else |
| 637 | + memcpy(new_fw, null_fw, 8); |
| 638 | + |
| 639 | + json_object_add_value_int(root, "Entry", |
| 640 | + le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); |
| 641 | + |
| 642 | + timestamp = (0x0000FFFFFFFFFFFF & |
| 643 | + le64_to_cpu( |
| 644 | + fw_act_history_entry->entry[entryIdx].timestamp)); |
| 645 | + json_object_add_value_uint64(root, "Timestamp", timestamp); |
| 646 | + |
| 647 | + json_object_add_value_int(root, "Power Cycle Count", |
| 648 | + le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); |
| 649 | + json_object_add_value_string(root, "Previous Firmware", |
| 650 | + previous_fw); |
| 651 | + json_object_add_value_string(root, "New Firmware", |
| 652 | + new_fw); |
| 653 | + json_object_add_value_int(root, "Slot", |
| 654 | + fw_act_history_entry->entry[entryIdx].slot_number); |
| 655 | + |
| 656 | + sndk_get_commit_action_bin( |
| 657 | + fw_act_history_entry->entry[entryIdx].commit_action_type, |
| 658 | + (char *)&commit_action_bin); |
| 659 | + json_object_add_value_string(root, "Action", commit_action_bin); |
| 660 | + |
| 661 | + if (!le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)) { |
| 662 | + json_object_add_value_string(root, "Result", "pass"); |
| 663 | + } else { |
| 664 | + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->entry[entryIdx].result))); |
| 665 | + json_object_add_value_string(root, "Result", fail_str); |
| 666 | + } |
| 667 | + |
| 668 | + json_print_object(root, NULL); |
| 669 | + printf("\n"); |
| 670 | + |
| 671 | + entryIdx++; |
| 672 | + if (entryIdx >= SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 673 | + entryIdx = 0; |
| 674 | + } |
| 675 | + } else |
| 676 | + fprintf(stderr, "ERROR: SNDK: %s: Unknown log page\n", __func__); |
| 677 | + |
| 678 | + json_free_object(root); |
| 679 | +} |
| 680 | + |
| 681 | +static int sndk_print_fw_act_history_log(__u8 *data, int num_entries, int fmt) |
| 682 | +{ |
| 683 | + if (!data) { |
| 684 | + fprintf(stderr, "ERROR: SNDK: Invalid buffer to read fw activate history entries\n"); |
| 685 | + return -1; |
| 686 | + } |
| 687 | + |
| 688 | + switch (fmt) { |
| 689 | + case NORMAL: |
| 690 | + sndk_print_fw_act_history_log_normal(data, num_entries); |
| 691 | + break; |
| 692 | + case JSON: |
| 693 | + sndk_print_fw_act_history_log_json(data, num_entries); |
| 694 | + break; |
| 695 | + } |
| 696 | + return 0; |
| 697 | +} |
| 698 | + |
| 699 | +static int sndk_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev, |
| 700 | + char *format) |
| 701 | +{ |
| 702 | + struct sndk_fw_act_history_log_format_c2 *fw_act_history_log; |
| 703 | + __u32 tot_entries = 0, num_entries = 0; |
| 704 | + nvme_print_flags_t fmt; |
| 705 | + __u8 *data; |
| 706 | + int ret; |
| 707 | + bool c2GuidMatch = false; |
| 708 | + |
| 709 | + if (!sndk_check_device(r, dev)) |
| 710 | + return -1; |
| 711 | + |
| 712 | + ret = validate_output_format(format, &fmt); |
| 713 | + if (ret < 0) { |
| 714 | + fprintf(stderr, "ERROR: SNDK: invalid output format\n"); |
| 715 | + return ret; |
| 716 | + } |
| 717 | + |
| 718 | + data = (__u8 *)malloc(sizeof(__u8) * SNDK_FW_ACT_HISTORY_C2_LOG_BUF_LEN); |
| 719 | + if (!data) { |
| 720 | + fprintf(stderr, "ERROR: SNDK: malloc: %s\n", strerror(errno)); |
| 721 | + return -1; |
| 722 | + } |
| 723 | + |
| 724 | + memset(data, 0, sizeof(__u8) * SNDK_FW_ACT_HISTORY_C2_LOG_BUF_LEN); |
| 725 | + |
| 726 | + ret = nvme_get_log_simple(dev_fd(dev), |
| 727 | + SNDK_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID, |
| 728 | + SNDK_FW_ACT_HISTORY_C2_LOG_BUF_LEN, data); |
| 729 | + |
| 730 | + if (strcmp(format, "json")) |
| 731 | + nvme_show_status(ret); |
| 732 | + |
| 733 | + if (!ret) { |
| 734 | + /* Get the log page data and verify the GUID */ |
| 735 | + fw_act_history_log = (struct sndk_fw_act_history_log_format_c2 *)(data); |
| 736 | + |
| 737 | + c2GuidMatch = !memcmp(ocp_C2_guid, |
| 738 | + fw_act_history_log->log_page_guid, |
| 739 | + SNDK_GUID_LENGTH); |
| 740 | + |
| 741 | + if (c2GuidMatch) { |
| 742 | + /* parse the data */ |
| 743 | + tot_entries = le32_to_cpu(fw_act_history_log->num_entries); |
| 744 | + |
| 745 | + if (tot_entries > 0) { |
| 746 | + num_entries = (tot_entries < SNDK_MAX_NUM_ACT_HIST_ENTRIES) ? |
| 747 | + tot_entries : SNDK_MAX_NUM_ACT_HIST_ENTRIES; |
| 748 | + ret = sndk_print_fw_act_history_log(data, num_entries, |
| 749 | + fmt); |
| 750 | + } else { |
| 751 | + fprintf(stderr, "INFO: SNDK: No entries found.\n"); |
| 752 | + ret = 0; |
| 753 | + } |
| 754 | + } else { |
| 755 | + fprintf(stderr, "ERROR: SNDK: Invalid C2 log page GUID\n"); |
| 756 | + ret = -1; |
| 757 | + } |
| 758 | + } else { |
| 759 | + fprintf(stderr, "ERROR: SNDK: Unable to read FW Activate History Log Page data\n"); |
| 760 | + ret = -1; |
| 761 | + } |
| 762 | + |
| 763 | + free(data); |
| 764 | + return ret; |
| 765 | +} |
| 766 | + |
| 767 | + |
497 | 768 | static int sndk_vs_fw_activate_history(int argc, char **argv, |
498 | 769 | struct command *command, |
499 | 770 | struct plugin *plugin) |
500 | 771 | { |
501 | | - return run_wdc_vs_fw_activate_history(argc, argv, command, plugin); |
| 772 | + const char *desc = "Retrieve FW activate history table."; |
| 773 | + __u64 capabilities = 0; |
| 774 | + struct nvme_dev *dev; |
| 775 | + nvme_root_t r; |
| 776 | + int ret = -1; |
| 777 | + |
| 778 | + struct config { |
| 779 | + char *output_format; |
| 780 | + }; |
| 781 | + |
| 782 | + struct config cfg = { |
| 783 | + .output_format = "normal", |
| 784 | + }; |
| 785 | + |
| 786 | + OPT_ARGS(opts) = { |
| 787 | + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), |
| 788 | + OPT_END() |
| 789 | + }; |
| 790 | + |
| 791 | + ret = parse_and_open(&dev, argc, argv, desc, opts); |
| 792 | + if (ret) |
| 793 | + return ret; |
| 794 | + |
| 795 | + r = nvme_scan(NULL); |
| 796 | + capabilities = sndk_get_drive_capabilities(r, dev); |
| 797 | + |
| 798 | + if (capabilities & SNDK_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2) { |
| 799 | + ret = sndk_get_fw_act_history_C2(r, dev, cfg.output_format); |
| 800 | + |
| 801 | + if (ret) |
| 802 | + fprintf(stderr, "ERROR: SNDK: Failure reading the FW " |
| 803 | + "Activate History, ret = %d\n", ret); |
| 804 | + } |
| 805 | + else |
| 806 | + /* Fall back to the wdc plugin command */ |
| 807 | + ret = run_wdc_vs_fw_activate_history(argc, argv, command, plugin); |
| 808 | + |
| 809 | + nvme_free_tree(r); |
| 810 | + dev_close(dev); |
| 811 | + return ret; |
| 812 | +} |
| 813 | + |
| 814 | +static int sndk_do_clear_fw_activate_history_fid(int fd) |
| 815 | +{ |
| 816 | + int ret = -1; |
| 817 | + __u32 result; |
| 818 | + __u32 value = 1 << 31; /* Bit 31 - Clear Firmware Update History Log */ |
| 819 | + |
| 820 | + ret = nvme_set_features_simple(fd, SNDK_NVME_CLEAR_FW_ACT_HIST_VU_FID, 0, value, |
| 821 | + false, &result); |
| 822 | + |
| 823 | + nvme_show_status(ret); |
| 824 | + return ret; |
502 | 825 | } |
503 | 826 |
|
504 | 827 | static int sndk_clear_fw_activate_history(int argc, char **argv, |
505 | 828 | struct command *command, |
506 | 829 | struct plugin *plugin) |
507 | 830 | { |
508 | | - return run_wdc_clear_fw_activate_history(argc, argv, command, plugin); |
| 831 | + const char *desc = "Clear FW activate history table."; |
| 832 | + __u64 capabilities = 0; |
| 833 | + struct nvme_dev *dev; |
| 834 | + nvme_root_t r; |
| 835 | + int ret; |
| 836 | + |
| 837 | + OPT_ARGS(opts) = { |
| 838 | + OPT_END() |
| 839 | + }; |
| 840 | + |
| 841 | + ret = parse_and_open(&dev, argc, argv, desc, opts); |
| 842 | + if (ret) |
| 843 | + return ret; |
| 844 | + |
| 845 | + r = nvme_scan(NULL); |
| 846 | + capabilities = sndk_get_drive_capabilities(r, dev); |
| 847 | + |
| 848 | + if (capabilities & SNDK_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY) { |
| 849 | + ret = sndk_do_clear_fw_activate_history_fid(dev_fd(dev)); |
| 850 | + |
| 851 | + if (ret) |
| 852 | + fprintf(stderr, "ERROR: SNDK: Failure clearing the FW " |
| 853 | + "Activate History, ret = %d\n", ret); |
| 854 | + } |
| 855 | + else |
| 856 | + /* Fall back to the wdc plugin command */ |
| 857 | + ret = run_wdc_clear_fw_activate_history(argc, argv, command, plugin); |
| 858 | + |
| 859 | + nvme_free_tree(r); |
| 860 | + dev_close(dev); |
| 861 | + return ret; |
509 | 862 | } |
510 | 863 |
|
511 | 864 | static int sndk_vs_telemetry_controller_option(int argc, char **argv, |
|
0 commit comments