|
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 | { |
@@ -571,18 +576,384 @@ static int sndk_drive_resize(int argc, char **argv, |
571 | 576 | return ret; |
572 | 577 | } |
573 | 578 |
|
| 579 | +static void sndk_print_fw_act_history_log_normal(__u8 *data, int num_entries) |
| 580 | +{ |
| 581 | + int i, j; |
| 582 | + char previous_fw[9]; |
| 583 | + char new_fw[9]; |
| 584 | + char commit_action_bin[8]; |
| 585 | + char time_str[100]; |
| 586 | + __u16 oldestEntryIdx = 0, entryIdx = 0; |
| 587 | + uint64_t timestamp; |
| 588 | + int fw_vers_len = 0; |
| 589 | + const char *null_fw = "--------"; |
| 590 | + |
| 591 | + memset((void *)time_str, '\0', 100); |
| 592 | + |
| 593 | + if (data[0] == SNDK_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { |
| 594 | + printf(" Firmware Activate History Log\n"); |
| 595 | + printf(" Power Cycle Previous New\n"); |
| 596 | + printf(" Entry Timestamp Count Firmware Firmware Slot Action Result\n"); |
| 597 | + printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); |
| 598 | + |
| 599 | + struct sndk_fw_act_history_log_format_c2 *fw_act_history_entry = |
| 600 | + (struct sndk_fw_act_history_log_format_c2 *)(data); |
| 601 | + |
| 602 | + oldestEntryIdx = SNDK_MAX_NUM_ACT_HIST_ENTRIES; |
| 603 | + if (num_entries == SNDK_MAX_NUM_ACT_HIST_ENTRIES) { |
| 604 | + /* find lowest/oldest entry */ |
| 605 | + for (i = 0; i < num_entries; i++) { |
| 606 | + j = (i+1 == SNDK_MAX_NUM_ACT_HIST_ENTRIES) ? 0 : i+1; |
| 607 | + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > |
| 608 | + le16_to_cpu(fw_act_history_entry->entry[j].fw_act_hist_entries)) { |
| 609 | + oldestEntryIdx = j; |
| 610 | + break; |
| 611 | + } |
| 612 | + } |
| 613 | + } |
| 614 | + if (oldestEntryIdx == SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 615 | + entryIdx = 0; |
| 616 | + else |
| 617 | + entryIdx = oldestEntryIdx; |
| 618 | + |
| 619 | + for (i = 0; i < num_entries; i++) { |
| 620 | + memset((void *)previous_fw, 0, 9); |
| 621 | + memset((void *)new_fw, 0, 9); |
| 622 | + memset((void *)commit_action_bin, 0, 8); |
| 623 | + |
| 624 | + memcpy(previous_fw, |
| 625 | + (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), |
| 626 | + 8); |
| 627 | + fw_vers_len = strlen((char *) |
| 628 | + &(fw_act_history_entry->entry[entryIdx].current_fw_version)); |
| 629 | + if (fw_vers_len > 1) |
| 630 | + memcpy(new_fw, |
| 631 | + (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), |
| 632 | + 8); |
| 633 | + else |
| 634 | + memcpy(new_fw, null_fw, 8); |
| 635 | + |
| 636 | + printf("%5"PRIu16"", |
| 637 | + (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); |
| 638 | + |
| 639 | + timestamp = (0x0000FFFFFFFFFFFF & |
| 640 | + le64_to_cpu( |
| 641 | + fw_act_history_entry->entry[entryIdx].timestamp)); |
| 642 | + printf(" "); |
| 643 | + printf("%16"PRIu64"", timestamp); |
| 644 | + printf(" "); |
| 645 | + |
| 646 | + printf("%16"PRIu64"", |
| 647 | + (uint64_t)le64_to_cpu(fw_act_history_entry->entry[entryIdx].power_cycle_count)); |
| 648 | + printf(" "); |
| 649 | + printf("%s", (char *)previous_fw); |
| 650 | + printf(" "); |
| 651 | + printf("%s", (char *)new_fw); |
| 652 | + printf(" "); |
| 653 | + printf("%2"PRIu8"", |
| 654 | + (uint8_t)fw_act_history_entry->entry[entryIdx].slot_number); |
| 655 | + printf(" "); |
| 656 | + sndk_get_commit_action_bin( |
| 657 | + fw_act_history_entry->entry[entryIdx].commit_action_type, |
| 658 | + (char *)&commit_action_bin); |
| 659 | + printf(" %s", (char *)commit_action_bin); |
| 660 | + printf(" "); |
| 661 | + if (!le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)) |
| 662 | + printf("pass"); |
| 663 | + else |
| 664 | + printf("fail #%d", |
| 665 | + (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)); |
| 666 | + printf("\n"); |
| 667 | + |
| 668 | + entryIdx++; |
| 669 | + if (entryIdx >= SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 670 | + entryIdx = 0; |
| 671 | + } |
| 672 | + } else |
| 673 | + fprintf(stderr, "ERROR: SNDK: %s: Unknown log page\n", __func__); |
| 674 | +} |
| 675 | + |
| 676 | +static void sndk_print_fw_act_history_log_json(__u8 *data, int num_entries) |
| 677 | +{ |
| 678 | + struct json_object *root = json_create_object(); |
| 679 | + int i, j; |
| 680 | + char previous_fw[9]; |
| 681 | + char new_fw[9]; |
| 682 | + char commit_action_bin[8]; |
| 683 | + char fail_str[32]; |
| 684 | + char time_str[100]; |
| 685 | + char ext_time_str[20]; |
| 686 | + uint64_t timestamp; |
| 687 | + int fw_vers_len = 0; |
| 688 | + |
| 689 | + memset((void *)previous_fw, 0, 9); |
| 690 | + memset((void *)new_fw, 0, 9); |
| 691 | + memset((void *)commit_action_bin, 0, 8); |
| 692 | + memset((void *)time_str, '\0', 100); |
| 693 | + memset((void *)ext_time_str, 0, 20); |
| 694 | + memset((void *)fail_str, 0, 11); |
| 695 | + char *null_fw = "--------"; |
| 696 | + __u16 oldestEntryIdx = 0, entryIdx = 0; |
| 697 | + |
| 698 | + if (data[0] == SNDK_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { |
| 699 | + struct sndk_fw_act_history_log_format_c2 *fw_act_history_entry = |
| 700 | + (struct sndk_fw_act_history_log_format_c2 *)(data); |
| 701 | + |
| 702 | + oldestEntryIdx = SNDK_MAX_NUM_ACT_HIST_ENTRIES; |
| 703 | + if (num_entries == SNDK_MAX_NUM_ACT_HIST_ENTRIES) { |
| 704 | + /* find lowest/oldest entry */ |
| 705 | + for (i = 0; i < num_entries; i++) { |
| 706 | + j = (i+1 == SNDK_MAX_NUM_ACT_HIST_ENTRIES) ? 0 : i+1; |
| 707 | + if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) > |
| 708 | + le16_to_cpu(fw_act_history_entry->entry[j].fw_act_hist_entries)) { |
| 709 | + oldestEntryIdx = j; |
| 710 | + break; |
| 711 | + } |
| 712 | + } |
| 713 | + } |
| 714 | + if (oldestEntryIdx == SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 715 | + entryIdx = 0; |
| 716 | + else |
| 717 | + entryIdx = oldestEntryIdx; |
| 718 | + |
| 719 | + for (i = 0; i < num_entries; i++) { |
| 720 | + memcpy(previous_fw, |
| 721 | + (char *)&(fw_act_history_entry->entry[entryIdx].previous_fw_version), |
| 722 | + 8); |
| 723 | + fw_vers_len = strlen((char *) |
| 724 | + &(fw_act_history_entry->entry[entryIdx].current_fw_version)); |
| 725 | + if (fw_vers_len > 1) |
| 726 | + memcpy(new_fw, |
| 727 | + (char *)&(fw_act_history_entry->entry[entryIdx].current_fw_version), |
| 728 | + 8); |
| 729 | + else |
| 730 | + memcpy(new_fw, null_fw, 8); |
| 731 | + |
| 732 | + json_object_add_value_int(root, "Entry", |
| 733 | + le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); |
| 734 | + |
| 735 | + timestamp = (0x0000FFFFFFFFFFFF & |
| 736 | + le64_to_cpu( |
| 737 | + fw_act_history_entry->entry[entryIdx].timestamp)); |
| 738 | + json_object_add_value_uint64(root, "Timestamp", timestamp); |
| 739 | + |
| 740 | + json_object_add_value_int(root, "Power Cycle Count", |
| 741 | + le64_to_cpu( |
| 742 | + fw_act_history_entry->entry[entryIdx].power_cycle_count)); |
| 743 | + json_object_add_value_string(root, "Previous Firmware", |
| 744 | + previous_fw); |
| 745 | + json_object_add_value_string(root, "New Firmware", |
| 746 | + new_fw); |
| 747 | + json_object_add_value_int(root, "Slot", |
| 748 | + fw_act_history_entry->entry[entryIdx].slot_number); |
| 749 | + |
| 750 | + sndk_get_commit_action_bin( |
| 751 | + fw_act_history_entry->entry[entryIdx].commit_action_type, |
| 752 | + (char *)&commit_action_bin); |
| 753 | + json_object_add_value_string(root, "Action", commit_action_bin); |
| 754 | + |
| 755 | + if (!le16_to_cpu(fw_act_history_entry->entry[entryIdx].result)) { |
| 756 | + json_object_add_value_string(root, "Result", "pass"); |
| 757 | + } else { |
| 758 | + sprintf((char *)fail_str, "fail #%d", |
| 759 | + (int)(le16_to_cpu(fw_act_history_entry->entry[entryIdx].result))); |
| 760 | + json_object_add_value_string(root, "Result", fail_str); |
| 761 | + } |
| 762 | + |
| 763 | + json_print_object(root, NULL); |
| 764 | + printf("\n"); |
| 765 | + |
| 766 | + entryIdx++; |
| 767 | + if (entryIdx >= SNDK_MAX_NUM_ACT_HIST_ENTRIES) |
| 768 | + entryIdx = 0; |
| 769 | + } |
| 770 | + } else |
| 771 | + fprintf(stderr, "ERROR: SNDK: %s: Unknown log page\n", __func__); |
| 772 | + |
| 773 | + json_free_object(root); |
| 774 | +} |
| 775 | + |
| 776 | +static int sndk_print_fw_act_history_log(__u8 *data, int num_entries, int fmt) |
| 777 | +{ |
| 778 | + if (!data) { |
| 779 | + fprintf(stderr, "ERROR: SNDK: Invalid buffer in print_fw act_history_log\n"); |
| 780 | + return -1; |
| 781 | + } |
| 782 | + |
| 783 | + switch (fmt) { |
| 784 | + case NORMAL: |
| 785 | + sndk_print_fw_act_history_log_normal(data, num_entries); |
| 786 | + break; |
| 787 | + case JSON: |
| 788 | + sndk_print_fw_act_history_log_json(data, num_entries); |
| 789 | + break; |
| 790 | + } |
| 791 | + return 0; |
| 792 | +} |
| 793 | + |
| 794 | +static int sndk_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev, |
| 795 | + char *format) |
| 796 | +{ |
| 797 | + struct sndk_fw_act_history_log_format_c2 *fw_act_history_log; |
| 798 | + __u32 tot_entries = 0, num_entries = 0; |
| 799 | + nvme_print_flags_t fmt; |
| 800 | + __u8 *data; |
| 801 | + int ret; |
| 802 | + bool c2GuidMatch = false; |
| 803 | + |
| 804 | + if (!sndk_check_device(r, dev)) |
| 805 | + return -1; |
| 806 | + |
| 807 | + ret = validate_output_format(format, &fmt); |
| 808 | + if (ret < 0) { |
| 809 | + fprintf(stderr, "ERROR: SNDK: invalid output format\n"); |
| 810 | + return ret; |
| 811 | + } |
| 812 | + |
| 813 | + data = (__u8 *)malloc(sizeof(__u8) * SNDK_FW_ACT_HISTORY_C2_LOG_BUF_LEN); |
| 814 | + if (!data) { |
| 815 | + fprintf(stderr, "ERROR: SNDK: malloc: %s\n", strerror(errno)); |
| 816 | + return -1; |
| 817 | + } |
| 818 | + |
| 819 | + memset(data, 0, sizeof(__u8) * SNDK_FW_ACT_HISTORY_C2_LOG_BUF_LEN); |
| 820 | + |
| 821 | + ret = nvme_get_log_simple(dev_fd(dev), |
| 822 | + SNDK_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID, |
| 823 | + SNDK_FW_ACT_HISTORY_C2_LOG_BUF_LEN, data); |
| 824 | + |
| 825 | + if (strcmp(format, "json")) |
| 826 | + nvme_show_status(ret); |
| 827 | + |
| 828 | + if (!ret) { |
| 829 | + /* Get the log page data and verify the GUID */ |
| 830 | + fw_act_history_log = (struct sndk_fw_act_history_log_format_c2 *)(data); |
| 831 | + |
| 832 | + c2GuidMatch = !memcmp(ocp_C2_guid, |
| 833 | + fw_act_history_log->log_page_guid, |
| 834 | + SNDK_GUID_LENGTH); |
| 835 | + |
| 836 | + if (c2GuidMatch) { |
| 837 | + /* parse the data */ |
| 838 | + tot_entries = le32_to_cpu(fw_act_history_log->num_entries); |
| 839 | + |
| 840 | + if (tot_entries > 0) { |
| 841 | + num_entries = (tot_entries < SNDK_MAX_NUM_ACT_HIST_ENTRIES) ? |
| 842 | + tot_entries : SNDK_MAX_NUM_ACT_HIST_ENTRIES; |
| 843 | + ret = sndk_print_fw_act_history_log(data, num_entries, |
| 844 | + fmt); |
| 845 | + } else { |
| 846 | + fprintf(stderr, "INFO: SNDK: No entries found.\n"); |
| 847 | + ret = 0; |
| 848 | + } |
| 849 | + } else { |
| 850 | + fprintf(stderr, "ERROR: SNDK: Invalid C2 log page GUID\n"); |
| 851 | + ret = -1; |
| 852 | + } |
| 853 | + } else { |
| 854 | + fprintf(stderr, "ERROR: SNDK: Unable to read FW Activate History Log Page data\n"); |
| 855 | + ret = -1; |
| 856 | + } |
| 857 | + |
| 858 | + free(data); |
| 859 | + return ret; |
| 860 | +} |
| 861 | + |
| 862 | + |
574 | 863 | static int sndk_vs_fw_activate_history(int argc, char **argv, |
575 | 864 | struct command *command, |
576 | 865 | struct plugin *plugin) |
577 | 866 | { |
578 | | - return run_wdc_vs_fw_activate_history(argc, argv, command, plugin); |
| 867 | + const char *desc = "Retrieve FW activate history table."; |
| 868 | + __u64 capabilities = 0; |
| 869 | + struct nvme_dev *dev; |
| 870 | + nvme_root_t r; |
| 871 | + int ret = -1; |
| 872 | + |
| 873 | + struct config { |
| 874 | + char *output_format; |
| 875 | + }; |
| 876 | + |
| 877 | + struct config cfg = { |
| 878 | + .output_format = "normal", |
| 879 | + }; |
| 880 | + |
| 881 | + OPT_ARGS(opts) = { |
| 882 | + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), |
| 883 | + OPT_END() |
| 884 | + }; |
| 885 | + |
| 886 | + ret = parse_and_open(&dev, argc, argv, desc, opts); |
| 887 | + if (ret) |
| 888 | + return ret; |
| 889 | + |
| 890 | + r = nvme_scan(NULL); |
| 891 | + capabilities = sndk_get_drive_capabilities(r, dev); |
| 892 | + |
| 893 | + if (capabilities & SNDK_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2) { |
| 894 | + ret = sndk_get_fw_act_history_C2(r, dev, cfg.output_format); |
| 895 | + |
| 896 | + if (ret) { |
| 897 | + fprintf(stderr, "ERROR: SNDK: Failure reading the FW "); |
| 898 | + fprintf(stderr, "Activate History, ret = %d\n", ret); |
| 899 | + } |
| 900 | + } else |
| 901 | + /* Fall back to the wdc plugin command */ |
| 902 | + ret = run_wdc_vs_fw_activate_history(argc, argv, command, plugin); |
| 903 | + |
| 904 | + nvme_free_tree(r); |
| 905 | + dev_close(dev); |
| 906 | + return ret; |
| 907 | +} |
| 908 | + |
| 909 | +static int sndk_do_clear_fw_activate_history_fid(int fd) |
| 910 | +{ |
| 911 | + int ret = -1; |
| 912 | + __u32 result; |
| 913 | + __u32 value = 1 << 31; /* Bit 31 - Clear Firmware Update History Log */ |
| 914 | + |
| 915 | + ret = nvme_set_features_simple(fd, SNDK_NVME_CLEAR_FW_ACT_HIST_VU_FID, 0, value, |
| 916 | + false, &result); |
| 917 | + |
| 918 | + nvme_show_status(ret); |
| 919 | + return ret; |
579 | 920 | } |
580 | 921 |
|
581 | 922 | static int sndk_clear_fw_activate_history(int argc, char **argv, |
582 | 923 | struct command *command, |
583 | 924 | struct plugin *plugin) |
584 | 925 | { |
585 | | - return run_wdc_clear_fw_activate_history(argc, argv, command, plugin); |
| 926 | + const char *desc = "Clear FW activate history table."; |
| 927 | + __u64 capabilities = 0; |
| 928 | + struct nvme_dev *dev; |
| 929 | + nvme_root_t r; |
| 930 | + int ret; |
| 931 | + |
| 932 | + OPT_ARGS(opts) = { |
| 933 | + OPT_END() |
| 934 | + }; |
| 935 | + |
| 936 | + ret = parse_and_open(&dev, argc, argv, desc, opts); |
| 937 | + if (ret) |
| 938 | + return ret; |
| 939 | + |
| 940 | + r = nvme_scan(NULL); |
| 941 | + capabilities = sndk_get_drive_capabilities(r, dev); |
| 942 | + |
| 943 | + if (capabilities & SNDK_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY) { |
| 944 | + ret = sndk_do_clear_fw_activate_history_fid(dev_fd(dev)); |
| 945 | + |
| 946 | + if (ret) { |
| 947 | + fprintf(stderr, "ERROR: SNDK: Failure clearing the FW "); |
| 948 | + fprintf(stderr, "Activate History, ret = %d\n", ret); |
| 949 | + } |
| 950 | + } else |
| 951 | + /* Fall back to the wdc plugin command */ |
| 952 | + ret = run_wdc_clear_fw_activate_history(argc, argv, command, plugin); |
| 953 | + |
| 954 | + nvme_free_tree(r); |
| 955 | + dev_close(dev); |
| 956 | + return ret; |
586 | 957 | } |
587 | 958 |
|
588 | 959 | static int sndk_vs_telemetry_controller_option(int argc, char **argv, |
|
0 commit comments