/* * Copyright (c) 2007 Kontron Canada, Inc. All Rights Reserved. * * Base on code from * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #include #include #include #define NO_MORE_INFO_FIELD 0xc1 #define TYPE_CODE 0xc0 /*Language code*/ /* * CONSTANT */ const int ERROR_STATUS = -1; const int OK_STATUS = 0; const char * STAR_LINE_LIMITER = "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"; const char * EQUAL_LINE_LIMITER = "================================================================="; const int SIZE_OF_FILE_TYPE = 3; const unsigned char AMC_MODULE = 0x80; const int PICMG_ID_OFFSET = 3; const unsigned int COMPARE_CANDIDATE = 2; /* In AMC.0 or PICMG 3.0 specification offset start from 0 with 3 bytes of * Mfg.ID, 1 byte of Picmg record Id, and * 1 byte of format version, so the data offset start from 5 */ const int START_DATA_OFFSET = 5; const int LOWER_OEM_TYPE = 0xf0; const int UPPER_OEM_TYPE = 0xfe; const unsigned char DISABLE_PORT = 0x1f; const struct valstr ipmi_ekanalyzer_module_type[] = { { ON_CARRIER_FRU_FILE, "On-Carrier Device" }, { A1_AMC_FRU_FILE, "AMC slot A1" }, { A2_AMC_FRU_FILE, "AMC slot A2" }, { A3_AMC_FRU_FILE, "AMC slot A3" }, { A4_AMC_FRU_FILE, "AMC slot A4" }, { B1_AMC_FRU_FILE, "AMC slot B1" }, { B2_AMC_FRU_FILE, "AMC slot B2" }, { B3_AMC_FRU_FILE, "AMC slot B3" }, { B4_AMC_FRU_FILE, "AMC slot B4" }, { RTM_FRU_FILE, "RTM" }, /*This is OEM specific module*/ { CONFIG_FILE, "Configuration file" }, { SHELF_MANAGER_FRU_FILE, "Shelf Manager" }, { 0xffff , NULL }, }; const struct valstr ipmi_ekanalyzer_IPMBL_addr[] = { { 0x72, "AMC slot A1" }, { 0x74, "AMC slot A2" }, { 0x76, "AMC slot A3" }, { 0x78, "AMC slot A4" }, { 0x7a, "AMC slot B1" }, { 0x7c, "AMC slot B2" }, { 0x7e, "AMC slot B3" }, { 0x80, "AMC slot B4" }, { 0x90, "RTM"}, /*This is OEM specific module*/ { 0xffff , NULL }, }; const struct valstr ipmi_ekanalyzer_link_type[] = { { 0x00, "Reserved" }, { 0x01, "Reserved" }, { 0x02, "AMC.1 PCI Express" }, { 0x03, "AMC.1 PCI Express Advanced Switching" }, { 0x04, "AMC.1 PCI Express Advanced Switching" }, { 0x05, "AMC.2 Ethernet" }, { 0x06, "AMC.4 Serial RapidIO" }, { 0x07, "AMC.3 Storage" }, /*This is OEM specific module*/ { 0xf0, "OEM Type 0"}, { 0xf1, "OEM Type 1"}, { 0xf2, "OEM Type 2"}, { 0xf3, "OEM Type 3"}, { 0xf4, "OEM Type 4"}, { 0xf5, "OEM Type 5"}, { 0xf6, "OEM Type 6"}, { 0xf7, "OEM Type 7"}, { 0xf8, "OEM Type 8"}, { 0xf9, "OEM Type 9"}, { 0xfa, "OEM Type 10"}, { 0xfb, "OEM Type 11"}, { 0xfc, "OEM Type 12"}, { 0xfd, "OEM Type 13"}, { 0xfe, "OEM Type 14"}, { 0xff , "Reserved" }, }; /*Reference: AMC.1 specification*/ const struct valstr ipmi_ekanalyzer_extension_PCIE[] = { { 0x00, "Gen 1 capable - non SSC" }, { 0x01, "Gen 1 capable - SSC" }, { 0x02, "Gen 2 capable - non SSC" }, { 0x03, "Gen 3 capable - SSC" }, { 0x0f, "Reserved"}, }; /*Reference: AMC.2 specification*/ const struct valstr ipmi_ekanalyzer_extension_ETHERNET[] = { { 0x00, "1000BASE-BX (SerDES Gigabit) Ethernet link" }, { 0x01, "10GBASE-BX4 10 Gigabit Ethernet link" }, }; /*Reference: AMC.3 specification*/ const struct valstr ipmi_ekanalyzer_extension_STORAGE[] = { { 0x00, "Fibre Channel (FC)" }, { 0x01, "Serial ATA (SATA)" }, { 0x02, "Serial Attached SCSI (SAS/SATA)" }, }; const struct valstr ipmi_ekanalyzer_asym_PCIE[] = { { 0x00, "exact match"}, { 0x01, "provides a Primary PCI Express Port" }, { 0x02, "provides a Secondary PCI Express Port" }, }; const struct valstr ipmi_ekanalyzer_asym_STORAGE[] = { { 0x00, "FC or SAS interface {exact match}" }, { 0x01, "SATA Server interface" }, { 0x02, "SATA Client interface" }, { 0x03, "Reserved" }, }; const struct valstr ipmi_ekanalyzer_picmg_record_id[] = { { 0x04, "Backplane Point to Point Connectivity Record" }, { 0x10, "Address Table Record" }, { 0x11, "Shelf Power Distribution Record" }, { 0x12, "Shelf Activation and Power Management Record" }, { 0x13, "Shelf Manager IP Connection Record" }, { 0x14, "Board Point to Point Connectivity Record" }, { 0x15, "Radial IPMB-0 Link Mapping Record" }, { 0x16, "Module Current Requirements Record" }, { 0x17, "Carrier Activation and Power Management Record" }, { 0x18, "Carrier Point-to-Point Connectivity Record" }, { 0x19, "AdvancedMC Point-to-Point Connectivity Record" }, { 0x1a, "Carrier Information Table" }, { 0x1b, "Shelf Fan Geography Record" }, { 0x2c, "Carrier Clock Point-to-Point Connectivity Record" }, { 0x2d, "Clock Configuration Record" }, }; extern int verbose; struct ipmi_ek_multi_header { struct fru_multirec_header header; unsigned char * data; struct ipmi_ek_multi_header * prev; struct ipmi_ek_multi_header * next; }; struct ipmi_ek_amc_p2p_connectivity_record{ unsigned char guid_count; struct fru_picmgext_guid * oem_guid; unsigned char rsc_id; unsigned char ch_count; struct fru_picmgext_amc_channel_desc_record * ch_desc; unsigned char link_desc_count; struct fru_picmgext_amc_link_desc_record * link_desc; int * matching_result; /*For link descriptor comparision*/ }; /***************************************************************************** * Function prototype ******************************************************************************/ /**************************************************************************** * command Functions *****************************************************************************/ static int ipmi_ekanalyzer_print( int argc, char * opt, char ** filename, int * file_type ); static tboolean ipmi_ekanalyzer_ekeying_match( int argc, char * opt, char ** filename, int * file_type ); /**************************************************************************** * Linked list Functions *****************************************************************************/ static void ipmi_ek_add_record2list( struct ipmi_ek_multi_header ** record, struct ipmi_ek_multi_header ** list_head, struct ipmi_ek_multi_header ** list_last ); static void ipmi_ek_display_record( struct ipmi_ek_multi_header * record, struct ipmi_ek_multi_header * list_head, struct ipmi_ek_multi_header * list_last ); static void ipmi_ek_remove_record_from_list( struct ipmi_ek_multi_header * record, struct ipmi_ek_multi_header ** list_head, struct ipmi_ek_multi_header ** list_last ); static int ipmi_ekanalyzer_fru_file2structure( char * filename, struct ipmi_ek_multi_header ** list_head, struct ipmi_ek_multi_header ** list_record, struct ipmi_ek_multi_header ** list_last ); /**************************************************************************** * Ekeying match Functions *****************************************************************************/ static int ipmi_ek_matching_process( int * file_type, int index1, int index2, struct ipmi_ek_multi_header ** list_head, struct ipmi_ek_multi_header ** list_last, char * opt, struct ipmi_ek_multi_header * pphysical ); static int ipmi_ek_get_resource_descriptor( int port_count, int index, struct fru_picmgext_carrier_p2p_descriptor * port_desc, struct ipmi_ek_multi_header * record ); static int ipmi_ek_create_amc_p2p_record( struct ipmi_ek_multi_header * record, struct ipmi_ek_amc_p2p_connectivity_record * amc_record ); static int ipmi_ek_compare_link( struct ipmi_ek_multi_header * physic_record, struct ipmi_ek_amc_p2p_connectivity_record record1, struct ipmi_ek_amc_p2p_connectivity_record record2, char * opt, int file_type1, int file_type2 ); static tboolean ipmi_ek_compare_channel_descriptor( struct fru_picmgext_amc_channel_desc_record ch_desc1, struct fru_picmgext_amc_channel_desc_record ch_desc2, struct fru_picmgext_carrier_p2p_descriptor * port_desc, int index_port, unsigned char rsc_id ); static int ipmi_ek_compare_link_descriptor( struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, struct ipmi_ek_amc_p2p_connectivity_record record2, int index2 ); static int ipmi_ek_compare_asym( unsigned char asym[COMPARE_CANDIDATE] ); static int ipmi_ek_compare_number_of_enable_port( struct fru_picmgext_amc_link_desc_record link_desc[COMPARE_CANDIDATE] ); static int ipmi_ek_check_physical_connectivity( struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, struct ipmi_ek_amc_p2p_connectivity_record record2, int index2, struct ipmi_ek_multi_header * record, int filetype1, int filetype2, char * option ); /**************************************************************************** * Display Functions *****************************************************************************/ static int ipmi_ek_display_fru_header( char * filename ); static int ipmi_ek_display_fru_header_detail(char * filename); static int ipmi_ek_display_chassis_info_area(FILE * input_file, long offset); static size_t ipmi_ek_display_board_info_area( FILE * input_file, char * board_type, unsigned int * board_length ); static int ipmi_ek_display_product_info_area(FILE * input_file, long offset); static tboolean ipmi_ek_display_link_descriptor( int file_type, unsigned char rsc_id, char * str, struct fru_picmgext_amc_link_desc_record link_desc ); static void ipmi_ek_display_oem_guid( struct ipmi_ek_amc_p2p_connectivity_record amc_record1 ); static int ipmi_ek_display_carrier_connectivity( struct ipmi_ek_multi_header * record ); static int ipmi_ek_display_power( int argc, char * opt, char ** filename, int * file_type ); static void ipmi_ek_display_current_descriptor( struct fru_picmgext_carrier_activation_record car, struct fru_picmgext_activation_record * cur_desc, char * filename ); static void ipmi_ek_display_backplane_p2p_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_address_table_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_shelf_power_distribution_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_shelf_activation_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_shelf_ip_connection_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_shelf_fan_geography_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_board_p2p_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_radial_ipmb0_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_amc_current_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_amc_activation_record ( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_amc_p2p_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_amc_carrier_info_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_clock_carrier_p2p_record( struct ipmi_ek_multi_header * record ); static void ipmi_ek_display_clock_config_record( struct ipmi_ek_multi_header * record ); /************************************************************************** * * Function name: ipmi_ekanalyzer_usage * * Description : Print the usage (help menu) of ekeying analyzer tool * * Restriction : None * * Input : None * * Output : None * * Global : None * * Return : None * ***************************************************************************/ static void ipmi_ekanalyzer_usage(void) { lprintf(LOG_NOTICE, "Ekeying analyzer tool version 1.00"); lprintf(LOG_NOTICE, "ekanalyzer Commands:"); lprintf(LOG_NOTICE, " print [carrier | power | all] ..."); lprintf(LOG_NOTICE, " frushow "); lprintf(LOG_NOTICE, " summary [match | unmatch | all] ..."); } /************************************************************************** * * Function name: ipmi_ek_get_file_type * * Description: this function takes an argument, then xtract the file type and * convert into module type (on carrier, AMC,...) value. * * * Restriction: None * * Input: argument: strings contain the type and the name of the file * together * * Output: None * * Global: None * * Return: Return value of module type: On carrier FRU file, A1 FRUM file... * if the file type is invalid, it return -1. See structure * ipmi_ekanalyzer_module_type for a list of valid type. ***************************************************************************/ static int ipmi_ek_get_file_type(char *argument) { int filetype = ERROR_STATUS; if (strlen(argument) <= MIN_ARGUMENT) { return filetype; } if (strncmp(argument, "oc=", SIZE_OF_FILE_TYPE) == 0) { filetype = ON_CARRIER_FRU_FILE; } else if (strncmp(argument, "a1=", SIZE_OF_FILE_TYPE) == 0) { filetype = A1_AMC_FRU_FILE; } else if (strncmp(argument, "a2=", SIZE_OF_FILE_TYPE) == 0) { filetype = A2_AMC_FRU_FILE; } else if (strncmp(argument, "a3=", SIZE_OF_FILE_TYPE) == 0) { filetype = A3_AMC_FRU_FILE; } else if (strncmp(argument, "a4=", SIZE_OF_FILE_TYPE) == 0) { filetype = A4_AMC_FRU_FILE; } else if (strncmp(argument, "b1=", SIZE_OF_FILE_TYPE) == 0) { filetype = B1_AMC_FRU_FILE; } else if (strncmp(argument, "b2=", SIZE_OF_FILE_TYPE) == 0) { filetype = B2_AMC_FRU_FILE; } else if (strncmp(argument, "b3=", SIZE_OF_FILE_TYPE) == 0) { filetype = B3_AMC_FRU_FILE; } else if (strncmp(argument, "b4=", SIZE_OF_FILE_TYPE) == 0) { filetype = B4_AMC_FRU_FILE; } else if (strncmp(argument, "rt=", SIZE_OF_FILE_TYPE) == 0) { filetype = RTM_FRU_FILE; } else if (strncmp(argument, "rc=", SIZE_OF_FILE_TYPE) == 0) { filetype = CONFIG_FILE; } else if (strncmp(argument, "sm=", SIZE_OF_FILE_TYPE) == 0) { filetype = SHELF_MANAGER_FRU_FILE; } else { filetype = ERROR_STATUS; } return filetype; } /************************************************************************** * * Function name: ipmi_ekanalyzer_main * * Description: Main program of ekeying analyzer. It calls the appropriate * function according to the command received. * * Restriction: None * * Input: ipmi_intf * intf: ? * int argc : number of argument received * int ** argv: argument strings * * Output: None * * Global: None * * Return: OK_STATUS as succes or ERROR_STATUS as error * ***************************************************************************/ int ipmi_ekanalyzer_main(struct ipmi_intf *intf, int argc, char **argv) { int rc = ERROR_STATUS; int file_type[MAX_FILE_NUMBER]; char *filename[MAX_FILE_NUMBER]; unsigned int argument_offset = 0; unsigned int type_offset = 0; /* list des multi record */ struct ipmi_ek_multi_header *list_head = NULL; struct ipmi_ek_multi_header *list_record = NULL; struct ipmi_ek_multi_header *list_last = NULL; if (argc == 0) { lprintf(LOG_ERR, "Not enough parameters given."); ipmi_ekanalyzer_usage(); return (-1); } else if ((argc - 1) > MAX_FILE_NUMBER) { lprintf(LOG_ERR, "Too too many parameters given."); return (-1); } if (strcmp(argv[argument_offset], "help") == 0) { ipmi_ekanalyzer_usage(); return 0; } else if ((strcmp(argv[argument_offset], "frushow") == 0) && (argc > (MIN_ARGUMENT-1))) { for (type_offset = 0; type_offset < (argc-1); type_offset++ ) { argument_offset++; file_type[type_offset] = ipmi_ek_get_file_type(argv[argument_offset]); if (file_type[type_offset] == ERROR_STATUS || file_type[type_offset] == CONFIG_FILE) { lprintf(LOG_ERR, "Invalid file type!"); lprintf(LOG_ERR, " ekanalyzer frushow ..."); return (-1); } /* because of strlen doesn't count '\0', * we need to add 1 byte for this character * to filename size */ filename[type_offset] = malloc(strlen(argv[argument_offset]) + 1 - SIZE_OF_FILE_TYPE); if (filename[type_offset] == NULL) { lprintf(LOG_ERR, "malloc failure"); return (-1); } strcpy(filename[type_offset], &argv[argument_offset][SIZE_OF_FILE_TYPE]); printf("Start converting file '%s'...\n", filename[type_offset]); /* Display FRU header offset */ rc = ipmi_ek_display_fru_header (filename[type_offset]); if (rc != ERROR_STATUS) { /* Display FRU header info in detail record */ rc = ipmi_ek_display_fru_header_detail(filename[type_offset]); /* Convert from binary data into multi record structure */ rc = ipmi_ekanalyzer_fru_file2structure (filename[type_offset], &list_head, &list_record, &list_last ); ipmi_ek_display_record(list_record, list_head, list_last); /* Remove record of list */ while (list_head != NULL) { ipmi_ek_remove_record_from_list(list_head, &list_head,&list_last ); if (verbose > 1) { printf("record has been removed!\n"); } } } free(filename[type_offset]); filename[type_offset] = NULL; } } else if ((strcmp(argv[argument_offset], "print") == 0) || (strcmp(argv[argument_offset], "summary") == 0)) { /* Display help text for corresponding command * if not enough parameters were given. */ char * option; /* index=1 indicates start position of first file * name in command line */ int index = 1; int filename_size=0; if (argc < MIN_ARGUMENT) { lprintf(LOG_ERR, "Not enough parameters given."); if (strcmp(argv[argument_offset], "print") == 0) { lprintf(LOG_ERR, " ekanalyzer print [carrier/power/all]" " [xx=frufile]"); } else { lprintf(LOG_ERR, " ekanalyzer summary [match/ unmatch/ all]" " [xx=frufile]"); } return ERROR_STATUS; } argument_offset++; if ((strcmp(argv[argument_offset], "carrier") == 0) || (strcmp(argv[argument_offset], "power") == 0) || (strcmp(argv[argument_offset], "all") == 0)) { option = argv[argument_offset]; index ++; argc--; } else if ((strcmp(argv[argument_offset], "match") == 0) || ( strcmp(argv[argument_offset], "unmatch") == 0)) { option = argv[argument_offset]; index ++; argc--; } else if ( strncmp(&argv[argument_offset][2], "=", 1) == 0) { /* since the command line must receive xx=filename, * so the position of "=" sign is 2 */ option = "default"; /* Since there is no option from user, the first argument * becomes first file type */ index = 1; /* index of argument */ } else { option = "invalid"; printf("Invalid option '%s'\n", argv[argument_offset]); argument_offset--; if (strcmp(argv[0], "print") == 0) { lprintf (LOG_ERR, " ekanalyzer print [carrier/power/all]" " [xx=frufile]"); } else { lprintf (LOG_ERR, " ekanalyzer summary [match/ unmatch/ all]" " [xx=frufile]"); } rc = ERROR_STATUS; } if (strcmp(option, "invalid") != 0) { int i=0; for (i = 0; i < (argc-1); i++) { file_type[i] = ipmi_ek_get_file_type (argv[index]); if (file_type[i] == ERROR_STATUS) { /* display the first 2 charactors (file type) of argument */ lprintf(LOG_ERR, "Invalid file type: %c%c\n", argv[index][0], argv[index][1]); ipmi_ekanalyzer_usage(); rc = ERROR_STATUS; break; } /* size is equal to string size minus 3 bytes of file type plus * 1 byte of '\0' since the strlen doesn't count the '\0' */ filename_size = strlen(argv[index]) - SIZE_OF_FILE_TYPE + 1; if (filename_size > 0) { filename[i] = malloc( filename_size ); if (filename[i] != NULL) { strcpy(filename[i], &argv[index][SIZE_OF_FILE_TYPE]); } else { lprintf(LOG_ERR, "ipmitool: malloc failure"); rc = ERROR_STATUS; break; } } rc = OK_STATUS; index++; } if (rc != ERROR_STATUS) { if (verbose > 0) { for (i = 0; i < (argc-1); i++) { printf ("Type: %s, ", val2str(file_type[i], ipmi_ekanalyzer_module_type)); printf("file name: %s\n", filename[i]); } } if (strcmp(argv[0], "print") == 0) { rc = ipmi_ekanalyzer_print((argc-1), option, filename, file_type); } else { rc = ipmi_ekanalyzer_ekeying_match((argc-1), option, filename, file_type); } for (i = 0; i < (argc-1); i++) { if (filename[i] != NULL) { free(filename[i]); filename[i] = NULL; } } } /* End of ERROR_STATUS */ } /* End of comparison of invalid option */ } else { lprintf(LOG_ERR, "Invalid ekanalyzer command: %s", argv[0]); ipmi_ekanalyzer_usage(); rc = ERROR_STATUS; } return rc; } /************************************************************************** * * Function name: ipmi_ekanalyzer_print * * Description: this function will display the topology, power or both * information together according to the option that it received. * * Restriction: None * * Input: int argc: number of the argument received * char* opt: option string that will tell what to display * char** filename: strings that contained filename of FRU data binary file * int* file_type: a pointer that contain file type (on carrier file, * a1 file, b1 file...). See structure * ipmi_ekanalyzer_module_type for a list of valid type * * Output: None * * Global: None * * Return: return 0 as success and -1 as error. * ***************************************************************************/ static int ipmi_ekanalyzer_print(int argc, char *opt, char **filename, int *file_type) { int return_value = OK_STATUS; /* Display carrier topology */ if ((strcmp(opt, "carrier") == 0) || (strcmp(opt, "default") == 0)) { tboolean found_flag = FALSE; int index = 0; int index_name[argc]; int list = 0; /* list of multi record */ struct ipmi_ek_multi_header *list_head[argc]; struct ipmi_ek_multi_header *list_record[argc]; struct ipmi_ek_multi_header *list_last[argc]; for (list=0; list < argc; list++) { list_head[list] = NULL; list_record[list] = NULL; list_last[list] = NULL; } /* reset list count */ list = 0; for (index = 0; index < argc; index++) { if (file_type[index] != ON_CARRIER_FRU_FILE) { continue; } index_name[list] = index; return_value = ipmi_ekanalyzer_fru_file2structure(filename[index], &list_head[list], &list_record[list], &list_last[list]); list++; found_flag = TRUE; } if (!found_flag) { printf("No carrier file has been found\n"); return_value = ERROR_STATUS; } else { int i = 0; for (i = 0; i < argc; i++) { /* this is a flag to advoid displaying * the same data multiple time */ tboolean first_data = TRUE; for (list_record[i] = list_head[i]; list_record[i] != NULL; list_record[i] = list_record[i]->next) { if (list_record[i]->data[PICMG_ID_OFFSET] == FRU_AMC_CARRIER_P2P) { if (first_data) { printf("%s\n", STAR_LINE_LIMITER); printf("From Carrier file: %s\n", filename[index_name[i]]); first_data = FALSE; } return_value = ipmi_ek_display_carrier_connectivity(list_record[i]); } else if (list_record[i]->data[PICMG_ID_OFFSET] == FRU_AMC_CARRIER_INFO) { /*See AMC.0 specification Table3-3 for mor detail*/ #define COUNT_OFFSET 6 if (first_data) { printf("From Carrier file: %s\n", filename[index_name[i]]); first_data = FALSE; } printf(" Number of AMC bays supported by Carrier: %d\n", list_record[i]->data[COUNT_OFFSET]); } } } /*Destroy the list of record*/ for (i = 0; i < argc; i++) { while (list_head[i] != NULL) { ipmi_ek_remove_record_from_list(list_head[i], &list_head[i], &list_last[i]); } /* display deleted result when we * reach the last record */ if ((i == (list-1)) && verbose) { printf("Record list has been removed successfully\n"); } } } } else if (strcmp(opt, "power") == 0) { printf("Print power information\n"); return_value = ipmi_ek_display_power(argc, opt, filename, file_type); } else if (strcmp(opt, "all") == 0) { printf("Print all information\n"); return_value = ipmi_ek_display_power(argc, opt, filename, file_type); } else { lprintf(LOG_ERR, "Invalid option %s", opt); return_value = ERROR_STATUS; } return return_value; } /************************************************************************** * * Function name: ipmi_ek_display_carrier_connectivity * * Description: Display the topology between a Carrier and all AMC modules by * using carrier p2p connectivity record * * Restriction: Ref: AMC.0 Specification: Table 3-13 and Table 3-14 * * Input: struct ipmi_ek_multi_header* record: a pointer to the carrier p2p * connectivity record. * * Output: None * * Global: None * * Return: return 0 on success and -1 if the record doesn't exist. * ***************************************************************************/ static int ipmi_ek_display_carrier_connectivity(struct ipmi_ek_multi_header *record) { int offset = START_DATA_OFFSET; struct fru_picmgext_carrier_p2p_record rsc_desc; struct fru_picmgext_carrier_p2p_descriptor *port_desc; if (record == NULL) { lprintf(LOG_ERR, "P2P connectivity record is invalid\n"); return ERROR_STATUS; } if (verbose > 1) { int k = 0; printf("Binary data of Carrier p2p connectivity"\ " record starting from mfg id\n"); for (k = 0; k < (record->header.len); k++) { printf("%02x ", record->data[k]); } printf("\n"); } while (offset <= (record->header.len - START_DATA_OFFSET)) { rsc_desc.resource_id = record->data[offset++]; rsc_desc.p2p_count = record->data[offset++]; if (verbose > 0) { printf("resource id= %02x port count= %d\n", rsc_desc.resource_id, rsc_desc.p2p_count); } /* check if it is an AMC Module */ if ((rsc_desc.resource_id & AMC_MODULE) == AMC_MODULE) { /* check if it is an RTM module */ if (rsc_desc.resource_id == AMC_MODULE) { printf(" %s topology:\n", val2str(RTM_IPMB_L, ipmi_ekanalyzer_IPMBL_addr)); } else { /* The last four bits of resource ID * represent site number (mask = 0x0f) */ printf(" %s topology:\n", val2str((rsc_desc.resource_id & 0x0f), ipmi_ekanalyzer_module_type)); } } else { printf(" On Carrier Device ID %d topology: \n", (rsc_desc.resource_id & 0x0f)); } while (rsc_desc.p2p_count > 0) { unsigned char data[3]; # ifndef WORDS_BIGENDIAN data[0] = record->data[offset + 0]; data[1] = record->data[offset + 1]; data[2] = record->data[offset + 2]; # else data[0] = record->data[offset + 2]; data[1] = record->data[offset + 1]; data[2] = record->data[offset + 0]; # endif port_desc = (struct fru_picmgext_carrier_p2p_descriptor*)data; offset += sizeof(struct fru_picmgext_carrier_p2p_descriptor); if ((port_desc->remote_resource_id & AMC_MODULE) == AMC_MODULE) { printf("\tPort %d =====> %s, Port %d\n", port_desc->local_port, val2str((port_desc->remote_resource_id & 0x0f), ipmi_ekanalyzer_module_type), port_desc->remote_port); } else { printf("\tPort %d =====> On Carrier Device ID %d, Port %d\n", port_desc->local_port, (port_desc->remote_resource_id & 0x0f), port_desc->remote_port); } rsc_desc.p2p_count--; } } return OK_STATUS; } /************************************************************************** * * Function name: ipmi_ek_display_power * * Description: Display the power management of the Carrier and AMC module by * using current management record. If the display option equal to all, * it will display power and carrier topology together. * * Restriction: Reference: AMC.0 Specification, Table 3-11 * * Input: int argc: number of the argument received * char* opt: option string that will tell what to display * char** filename: strings that contained filename of FRU data binary file * int* file_type: a pointer that contain file type (on carrier file, * a1 file, b1 file...) * * Output: None * * Global: None * * Return: return 0 on success and -1 if the record doesn't exist. * ***************************************************************************/ static int ipmi_ek_display_power( int argc, char * opt, char ** filename, int * file_type ) { int num_file=0; int return_value = ERROR_STATUS; int index = 0; /*list des multi record*/ struct ipmi_ek_multi_header * list_head[argc]; struct ipmi_ek_multi_header * list_record[argc]; struct ipmi_ek_multi_header * list_last[argc]; for ( num_file = 0; num_file < argc; num_file++ ){ list_head[num_file] = NULL; list_record[num_file] = NULL; list_last[num_file] = NULL; } for ( num_file = 0; num_file < argc; num_file++ ){ tboolean is_first_data = TRUE; if ( file_type[num_file] == CONFIG_FILE ){ num_file++; } if ( is_first_data ){ printf("%s\n", STAR_LINE_LIMITER); printf("\nFrom %s file '%s'\n", val2str( file_type[num_file], ipmi_ekanalyzer_module_type), filename[num_file]); is_first_data = FALSE; } return_value = ipmi_ekanalyzer_fru_file2structure( filename[num_file], &list_head[num_file], &list_record[num_file], &list_last[num_file]); if ( list_head[num_file] != NULL ){ for ( list_record[num_file] = list_head[num_file]; list_record[num_file] != NULL; list_record[num_file] = list_record[num_file]->next ){ if ( ( strcmp(opt, "all") == 0 ) && ( file_type[num_file] == ON_CARRIER_FRU_FILE ) ){ if ( list_record[num_file]->data[PICMG_ID_OFFSET] == FRU_AMC_CARRIER_P2P ){ return_value = ipmi_ek_display_carrier_connectivity( list_record[num_file] ); } else if ( list_record[num_file]->data[PICMG_ID_OFFSET] == FRU_AMC_CARRIER_INFO ){ /*Ref: See AMC.0 Specification Table 3-3: Carrier Information * Table about offset value */ printf( " Number of AMC bays supported by Carrier: %d\n", list_record[num_file]->data[START_DATA_OFFSET+1] ); } } /*Ref: AMC.0 Specification: Table 3-11 * Carrier Activation and Current Management Record */ if ( list_record[num_file]->data[PICMG_ID_OFFSET] == FRU_AMC_ACTIVATION ){ int index_data = START_DATA_OFFSET; struct fru_picmgext_carrier_activation_record car; struct fru_picmgext_activation_record * cur_desc; memcpy ( &car, &list_record[num_file]->data[index_data], sizeof (struct fru_picmgext_carrier_activation_record) ); index_data += sizeof (struct fru_picmgext_carrier_activation_record); cur_desc = malloc (car.module_activation_record_count * \ sizeof (struct fru_picmgext_activation_record) ); for(index=0; indexdata[index_data], sizeof (struct fru_picmgext_activation_record) ); index_data += sizeof (struct fru_picmgext_activation_record); } /*Display the current*/ ipmi_ek_display_current_descriptor( car, cur_desc, filename[num_file] ); free(cur_desc); cur_desc = NULL; } /*Ref: AMC.0 specification, Table 3-10: Module Current Requirement*/ else if ( list_record[num_file]->data[PICMG_ID_OFFSET] == FRU_AMC_CURRENT ){ float power_in_watt = 0; float current_in_amp = 0; printf(" %s power required (Current Draw): ", val2str ( file_type[num_file], ipmi_ekanalyzer_module_type) ); current_in_amp = list_record[num_file]->data[START_DATA_OFFSET]*0.1; power_in_watt = current_in_amp * AMC_VOLTAGE; printf("%.2f Watts (%.2f Amps)\n",power_in_watt, current_in_amp); } } return_value = OK_STATUS; /*Destroy the list of record*/ for ( index = 0; index < argc; index++ ){ while ( list_head[index] != NULL ){ ipmi_ek_remove_record_from_list ( list_head[index], &list_head[index],&list_last[index] ); } if ( verbose > 1 ) printf("Record list has been removed successfully\n"); } } } printf("%s\n", STAR_LINE_LIMITER); return return_value; } /************************************************************************** * * Function name: ipmi_ek_display_current_descriptor * * Description: Display the current descriptor under format xx Watts (xx Amps) * * Restriction: None * * Input: struct fru_picmgext_carrier_activation_record car: contain binary data * of carrier activation record * struct fru_picmgext_activation_record * cur_desc: contain current * descriptor * char* filename: strings that contained filename of FRU data binary file * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_current_descriptor( struct fru_picmgext_carrier_activation_record car, struct fru_picmgext_activation_record *cur_desc, char *filename) { int index = 0; float power_in_watt = 0.0; float current_in_amp = 0.0; for (index = 0; index < car.module_activation_record_count; index++) { /* See AMC.0 specification, Table 3-12 for * detail about calculation */ current_in_amp = (float)cur_desc[index].max_module_curr * 0.1; power_in_watt = (float)current_in_amp * AMC_VOLTAGE; printf(" Carrier AMC power available on %s:\n", val2str( cur_desc[index].ibmb_addr, ipmi_ekanalyzer_IPMBL_addr)); printf("\t- Local IPMB Address \t: %02x\n", cur_desc[index].ibmb_addr); printf("\t- Maximum module Current\t: %.2f Watts (%.2f Amps)\n", power_in_watt, current_in_amp); } /* Display total power on Carrier */ current_in_amp = (float)car.max_internal_curr * 0.1; power_in_watt = (float)current_in_amp * AMC_VOLTAGE; printf(" Carrier AMC total power available for all bays from file '%s':", filename); printf(" %.2f Watts (%.2f Amps)\n", power_in_watt, current_in_amp); } /************************************************************************** * * Function name: ipmi_ekanalyzer_ekeying_match * * Description: Check for possible Ekeying match between two FRU files * * Restriction: None * * Input: argc: number of the argument received * opt: string that contains display option received from user. * filename: strings that contained filename of FRU data binary file * file_type: a pointer that contain file type (on carrier file, * a1 file, b1 file...) * * Output: None * * Global: None * * Return: return TRUE on success and FALSE if the record doesn't exist. * ***************************************************************************/ static tboolean ipmi_ekanalyzer_ekeying_match( int argc, char * opt, char ** filename, int * file_type ) { tboolean return_value = FALSE; if ( (strcmp(opt, "carrier") == 0 ) || (strcmp(opt, "power") == 0) ){ lprintf(LOG_ERR, " ekanalyzer summary [match/ unmatch/ all]"\ " [xx=frufile]"); return_value = ERROR_STATUS; } else{ int num_file=0; tboolean amc_file = FALSE; /*used to indicate the present of AMC file*/ tboolean oc_file = FALSE; /*used to indicate the present of Carrier file*/ /*Check for possible ekeying match between files*/ for ( num_file=0; num_file < argc; num_file++ ){ if ( ( file_type[num_file] == ON_CARRIER_FRU_FILE ) || ( file_type[num_file] == CONFIG_FILE ) || ( file_type[num_file] == SHELF_MANAGER_FRU_FILE ) ){ amc_file = FALSE; } else { /*there is an amc file*/ amc_file = TRUE; break; } } if ( amc_file == FALSE ){ printf("\nNo AMC FRU file is provided --->" \ " No possible ekeying match!\n"); return_value = ERROR_STATUS; } else{ /*If no carrier file is provided, return error*/ for ( num_file=0; num_file < argc; num_file++ ){ if ( (file_type[num_file] == ON_CARRIER_FRU_FILE ) || ( file_type[num_file] == CONFIG_FILE ) || ( file_type[num_file] == SHELF_MANAGER_FRU_FILE ) ){ oc_file = TRUE; break; } } if ( !oc_file ){ printf("\nNo Carrier FRU file is provided" \ " ---> No possible ekeying match!\n"); return_value = ERROR_STATUS; } else{ /*list des multi record*/ struct ipmi_ek_multi_header * list_head[argc]; struct ipmi_ek_multi_header * list_record[argc]; struct ipmi_ek_multi_header * list_last[argc]; struct ipmi_ek_multi_header * pcarrier_p2p; int list = 0; int match_pair = 0; /*Create an empty list*/ for ( list=0; listnext ){ if ( pcarrier_p2p->data[PICMG_ID_OFFSET] == FRU_AMC_CARRIER_P2P ){ break; } } break; } } /*Determine the match making pair*/ while ( match_pair < argc ){ for ( num_file = (match_pair+1); num_file0){ printf("Start matching process\n"); } return_value = ipmi_ek_matching_process( file_type, match_pair, num_file, list_head, list_last, opt, pcarrier_p2p); } } } match_pair ++; } for( num_file=0; num_file < argc; num_file++ ){ if (list_head[num_file] != NULL ){ ipmi_ek_remove_record_from_list( list_head[num_file], &list_record[num_file], &list_last[num_file]); } if ( ( num_file == argc-1 ) && verbose ) printf("Record list has been removed successfully\n"); } return_value = OK_STATUS; } } } return return_value; } /************************************************************************** * * Function name: ipmi_ek_matching_process * * Description: This function process the OEM check, Physical Connectivity check, * and Link Descriptor comparison to do Ekeying match * * Restriction: None * * Input: file_type: a pointer that contain file type (on carrier file, * a1 file, b1 file...) * index1: position of the first record in the list of the record * index2: position of the second record in the list of the record * ipmi_ek_multi_header ** list_head: pointer to the header of a * linked list that contain FRU multi record * ipmi_ek_multi_header ** list_last: pointer to the tale of a * linked list that contain FRU multi record * opt: string that contain display option such as "match", "unmatch", or * "all". * pphysical: a pointer that contain a carrier p2p connectivity record * to perform physical check * * Output: None * * Global: None * * Return: return OK_STATUS on success and ERROR_STATUS if the record doesn't * exist. * ***************************************************************************/ static int ipmi_ek_matching_process( int * file_type, int index1, int index2, struct ipmi_ek_multi_header ** list_head, struct ipmi_ek_multi_header ** list_last, char * opt, struct ipmi_ek_multi_header * pphysical ) { int result = ERROR_STATUS; struct ipmi_ek_multi_header * record; int num_amc_record1 = 0;/*Number of AMC records in the first module*/ int num_amc_record2 = 0;/*Number of AMC records in the second module*/ /* Comparison between an On-Carrier and an AMC*/ if ( file_type[index2] == ON_CARRIER_FRU_FILE ){ int index_temp = 0; index_temp = index1; index1 = index2; /*index1 indicate on carrier*/ index2 = index_temp; /*index2 indcate an AMC*/ } /*Calculate record size for Carrier file*/ for ( record=list_head[index1]; record != NULL;record = record->next ){ if ( record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ num_amc_record2++; } } /*Calculate record size for amc file*/ for ( record=list_head[index2]; record != NULL;record = record->next){ if ( record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ num_amc_record1++; } } if ( (num_amc_record1 > 0) && (num_amc_record2 > 0) ){ int index_record1 = 0; int index_record2 = 0; /* Multi records of AMC module */ struct ipmi_ek_amc_p2p_connectivity_record * amc_record1 = NULL; /* Multi records of Carrier or an AMC module */ struct ipmi_ek_amc_p2p_connectivity_record * amc_record2 = NULL; amc_record1 = malloc ( num_amc_record1 * \ sizeof(struct ipmi_ek_amc_p2p_connectivity_record)); amc_record2 = malloc ( num_amc_record2 * \ sizeof(struct ipmi_ek_amc_p2p_connectivity_record)); for (record=list_head[index2]; record != NULL;record = record->next){ if ( record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ result = ipmi_ek_create_amc_p2p_record( record, &amc_record1[index_record1] ); if (result != ERROR_STATUS){ struct ipmi_ek_multi_header * current_record = NULL; for ( current_record=list_head[index1]; current_record != NULL ; current_record = current_record->next ){ if ( current_record->data[PICMG_ID_OFFSET] == FRU_AMC_P2P ){ result = ipmi_ek_create_amc_p2p_record( current_record, &amc_record2[index_record2] ); if ( result != ERROR_STATUS ){ if ( result == OK_STATUS ){ /*Compare Link descriptor*/ result = ipmi_ek_compare_link ( pphysical, amc_record1[index_record1], amc_record2[index_record2], opt, file_type[index1], file_type[index2]); } index_record2++; } } /*end of FRU_AMC_P2P */ } /* end of for loop */ index_record1++; } } } free(amc_record1) ; amc_record1 = NULL; free(amc_record2) ; amc_record2 = NULL; } else{ printf("No amc record is found!\n"); } return result; } /************************************************************************** * * Function name: ipmi_ek_check_physical_connectivity * * Description: This function check for point to point connectivity between * two modules by comparing each enable port in link descriptor * with local and remote ports of port descriptor in * carrier point-to-point connectivity record according to the * corresponding file type ( a1, b1, b2...). * * Restriction: In order to perform physical check connectivity, it needs to * compare between 2 AMC Modules, so the use of index ( 1 and 2 ) * can facilitate the comparison in this case. * * Input: record1: is an AMC p2p record for an AMC module * record2 is an AMC p2p record for an On-Carrier record or an AMC module * char* opt: option string that will tell if a matching result, unmatched * result or all the results will be displayed. * file_type1: indicates type of the first module * file_type2: indicates type of the second module * * Output: None * * Global: None * * Return: return OK_STATUS if both link are matched, otherwise * return ERROR_STATUS * ***************************************************************************/ static int ipmi_ek_check_physical_connectivity( struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, struct ipmi_ek_amc_p2p_connectivity_record record2, int index2, struct ipmi_ek_multi_header * record, int filetype1, int filetype2, char * option ) { int return_status = OK_STATUS; if ( record == NULL ){ printf("NO Carrier p2p connectivity !\n"); return_status = ERROR_STATUS; } else{ #define INVALID_AMC_SITE_NUMBER -1 int index = START_DATA_OFFSET; int amc_site = INVALID_AMC_SITE_NUMBER; struct fru_picmgext_carrier_p2p_record rsc_desc; struct fru_picmgext_carrier_p2p_descriptor * port_desc = NULL; /* Get the physical connectivity record */ while ( index < record->header.len ) { rsc_desc.resource_id = record->data[index++]; rsc_desc.p2p_count = record->data[index++]; /* carrier p2p record starts with on-carrier device */ if ( (rsc_desc.resource_id == record1.rsc_id) || (rsc_desc.resource_id == record2.rsc_id) ){ if (rsc_desc.p2p_count <= 0){ printf("No p2p count\n"); return_status = ERROR_STATUS; } else{ port_desc = malloc ( rsc_desc.p2p_count * sizeof(struct fru_picmgext_carrier_p2p_descriptor) ); index = ipmi_ek_get_resource_descriptor( rsc_desc.p2p_count, index, port_desc, record ); amc_site = INVALID_AMC_SITE_NUMBER; break; } } else{ /* carrier p2p record starts with AMC module */ if (rsc_desc.resource_id == AMC_MODULE){ if (filetype1 != ON_CARRIER_FRU_FILE){ amc_site = filetype1; } else{ amc_site = filetype2; } } else{ amc_site = rsc_desc.resource_id & 0x0f; } if ( amc_site > 0 ){ if ( (amc_site == filetype1) || (amc_site == filetype2) ){ port_desc = malloc ( rsc_desc.p2p_count * sizeof(struct fru_picmgext_carrier_p2p_descriptor) ); index = ipmi_ek_get_resource_descriptor( rsc_desc.p2p_count, index, port_desc, record ); break; } } else{ return_status = ERROR_STATUS; } } /*If the record doesn't contain the same AMC site number in command * line, go to the next record */ index += ( sizeof(struct fru_picmgext_carrier_p2p_descriptor) * rsc_desc.p2p_count ); } if ( (port_desc != NULL) && (return_status != ERROR_STATUS) ){ int j=0; for ( j = 0; j < rsc_desc.p2p_count; j++ ){ /* Compare only enable channel descriptor */ if ( record1.ch_desc[index1].lane0port != DISABLE_PORT ){ /* matching result from channel descriptor comparison */ tboolean match_lane = FALSE; match_lane = ipmi_ek_compare_channel_descriptor ( record1.ch_desc[index1], record2.ch_desc[index2], port_desc, j, rsc_desc.resource_id ); if ( match_lane ){ if ( filetype1 != ON_CARRIER_FRU_FILE ){ if ( ( (filetype1 == (rsc_desc.resource_id & 0x0f)) && (filetype2 ==(port_desc[j].remote_resource_id &0x0f)) ) || ( (filetype2 == (rsc_desc.resource_id & 0x0f)) && (filetype1 ==(port_desc[j].remote_resource_id &0x0f)) ) ){ if ( ! (strcmp(option, "unmatch") == 0) ){ printf("%s port %d ==> %s port %d\n", val2str(filetype2, ipmi_ekanalyzer_module_type), record1.ch_desc[index1].lane0port, val2str(filetype1, ipmi_ekanalyzer_module_type), record2.ch_desc[index2].lane0port); } return_status = OK_STATUS; break; } else{ if (verbose == LOG_DEBUG){ printf("No point 2 point connectivity\n"); } return_status = ERROR_STATUS; } } else{ if ( (record2.rsc_id == (rsc_desc.resource_id) ) && (filetype2 == (port_desc[j].remote_resource_id & 0x0f)) ){ if ( ! (strcmp(option, "unmatch") == 0) ){ printf("%s port %d ==> %s port %d\n", val2str(filetype2, ipmi_ekanalyzer_module_type), record1.ch_desc[index1].lane0port, val2str(filetype1, ipmi_ekanalyzer_module_type), record2.ch_desc[index2].lane0port); } return_status = OK_STATUS; break; } else if ( (filetype2 == (rsc_desc.resource_id & 0x0f) ) && (record2.rsc_id == (port_desc[j].remote_resource_id)) ){ if ( ! (strcmp(option, "unmatch") == 0) ){ printf("%s port %d ==> %s %x port %d\n", val2str(filetype2, ipmi_ekanalyzer_module_type), record1.ch_desc[index1].lane0port, val2str(filetype1, ipmi_ekanalyzer_module_type), record2.rsc_id,record2.ch_desc[index2].lane0port); } return_status = OK_STATUS; break; } else{ if (verbose == LOG_DEBUG){ printf("No point 2 point connectivity\n"); } return_status = ERROR_STATUS; } } } else{ if (verbose == LOG_DEBUG){ printf("No point 2 point connectivity\n"); } return_status = ERROR_STATUS; } } else{ /*If the link is disable, the result is always true*/ return_status = OK_STATUS; } } } else{ if (verbose == LOG_WARN){ printf("Invalid Carrier p2p connectivity record\n"); } return_status = ERROR_STATUS; } if (port_desc != NULL){ free(port_desc); port_desc = NULL; } } return return_status; } /************************************************************************** * * Function name: ipmi_ek_compare_link * * Description: This function compares link grouping id of each * amc p2p connectiviy record * * Restriction: None * * Input: record1: is an AMC p2p record for an AMC module * record2 is an AMC p2p record for an On-Carrier record or an AMC module * char* opt: option string that will tell if a matching result, unmatched * result or all the results will be displayed. * file_type1: indicates type of the first module * file_type2: indicates type of the second module * * Output: None * * Global: None * * Return: return 0 if both link are matched, otherwise return -1 * ***************************************************************************/ static int ipmi_ek_compare_link( struct ipmi_ek_multi_header * physic_record, struct ipmi_ek_amc_p2p_connectivity_record record1, struct ipmi_ek_amc_p2p_connectivity_record record2, char * opt, int file_type1, int file_type2 ) { int result = ERROR_STATUS; int index1 = 0; /*index for AMC module*/ int index2 = 0; /*index for On-carrier type*/ record1.matching_result = malloc ( record1.link_desc_count * sizeof(int) ); record2.matching_result = malloc ( record2.link_desc_count * sizeof(int) ); /*Initialize all the matching_result to false*/ for( index2 = 0; index2 < record2.link_desc_count; index2++ ){ record2.matching_result[index2] = FALSE; } for( index1 = 0; index1 < record1.link_desc_count; index1++ ){ for( index2 = 0; index2 < record2.link_desc_count; index2++ ){ if( record1.link_desc[index1].group_id == 0 ){ if( record2.link_desc[index2].group_id == 0 ){ result = ipmi_ek_compare_link_descriptor( record1, index1, record2, index2 ); if ( result == OK_STATUS ){ /*Calculate the index for Channel descriptor in function of * link designator channel ID */ /*first channel_id in the AMC Link descriptor of record1*/ static int flag_first_link1; int index_ch_desc1; /*index of channel descriptor */ /*first channel_id in the AMC Link descriptor of record2*/ static int flag_first_link2; int index_ch_desc2; /*index of channel descriptor*/ if (index1==0){ /*this indicate the first link is encounter*/ flag_first_link1 = record1.link_desc[index1].channel_id; } index_ch_desc1 = record1.link_desc[index1].channel_id - flag_first_link1; if (index2==0){ flag_first_link2 = record2.link_desc[index2].channel_id; } index_ch_desc2 = record2.link_desc[index2].channel_id - flag_first_link2; /*Check for physical connectivity for each link*/ result = ipmi_ek_check_physical_connectivity ( record1, index_ch_desc1, record2, index_ch_desc2, physic_record, file_type1, file_type2, opt ); if ( result == OK_STATUS ){ /*Display the result if option = match or all*/ if ( (strcmp( opt, "match" ) == 0) || (strcmp( opt, "all" ) == 0) || (strcmp( opt, "default" ) == 0) ){ tboolean isOEMtype = FALSE; printf(" Matching Result\n"); isOEMtype = ipmi_ek_display_link_descriptor( file_type1, record2.rsc_id, "From", record2.link_desc[index2]); if (isOEMtype){ ipmi_ek_display_oem_guid (record2); } isOEMtype = ipmi_ek_display_link_descriptor( file_type2, record1.rsc_id, "To", record1.link_desc[index1] ); if (isOEMtype){ ipmi_ek_display_oem_guid (record1); } printf(" %s\n", STAR_LINE_LIMITER); } record2.matching_result[index2] = TRUE; record1.matching_result[index1] = TRUE; /*quit the fist loop since the match is found*/ index2 = record2.link_desc_count; } } } } else { /*Link Grouping ID is non zero, Compare all link descriptor * that has non-zero link grouping id together */ if (record2.link_desc[index2].group_id != 0 ){ result = ipmi_ek_compare_link_descriptor( record1, index1, record2, index2 ); if ( result == OK_STATUS ){ /*Calculate the index for Channel descriptor in function of * link designator channel ID */ /*first channel_id in the AMC Link descriptor of record1*/ static int flag_first_link1; int index_ch_desc1; /*index of channel descriptor */ /*first channel_id in the AMC Link descriptor of record2*/ static int flag_first_link2; int index_ch_desc2; /*index of channel descriptor*/ if (index1==0){ /*this indicate the first link is encounter*/ flag_first_link1 = record1.link_desc[index1].channel_id; } index_ch_desc1 = record1.link_desc[index1].channel_id - flag_first_link1; if (index2==0){ flag_first_link2 = record2.link_desc[index2].channel_id; } index_ch_desc2 = record2.link_desc[index2].channel_id - flag_first_link2; /*Check for physical connectivity for each link*/ result = ipmi_ek_check_physical_connectivity ( record1, index_ch_desc1, record2, index_ch_desc2, physic_record, file_type1, file_type2, opt ); if ( result == OK_STATUS ){ if ( (strcmp( opt, "match" ) == 0) || (strcmp( opt, "all" ) == 0) || (strcmp( opt, "default" ) == 0) ){ tboolean isOEMtype = FALSE; printf(" Matching Result\n"); isOEMtype = ipmi_ek_display_link_descriptor( file_type1, record2.rsc_id, "From", record2.link_desc[index2] ); if ( isOEMtype ){ ipmi_ek_display_oem_guid (record2); } isOEMtype = ipmi_ek_display_link_descriptor( file_type2, record1.rsc_id, "To", record1.link_desc[index1] ); if (isOEMtype){ ipmi_ek_display_oem_guid (record1); } printf(" %s\n", STAR_LINE_LIMITER); } record2.matching_result[index2] = TRUE; record1.matching_result[index1] = TRUE; /*leave the fist loop since the match is found*/ index2 = record2.link_desc_count; } } } } } } if ( (strcmp(opt, "unmatch") == 0) || (strcmp(opt, "all") == 0) ){ int isOEMtype = FALSE; printf(" Unmatching result\n"); for (index1 = 0; index1 < record1.link_desc_count; index1++){ isOEMtype = ipmi_ek_display_link_descriptor( file_type2, record1.rsc_id, "", record1.link_desc[index1] ); if ( isOEMtype ){ ipmi_ek_display_oem_guid (record1); } printf(" %s\n", STAR_LINE_LIMITER); } for ( index2 = 0; index2 < record2.link_desc_count; index2++){ if ( !record2.matching_result[index2] ){ isOEMtype = ipmi_ek_display_link_descriptor( file_type1, record2.rsc_id, "", record2.link_desc[index2] ); if ( isOEMtype ){ ipmi_ek_display_oem_guid (record2); } printf(" %s\n", STAR_LINE_LIMITER); } } } free(record1.matching_result); record1.matching_result = NULL; free(record2.matching_result); record2.matching_result = NULL; return result; } /************************************************************************** * * Function name: ipmi_ek_compare_channel_descriptor * * Description: This function compares 2 channel descriptors of 2 AMC * point-to-point connectivity records with port descriptor of * carrier point-to-point connectivity record. The comparison is * made between each enable port only. * * Restriction: Reference: AMC.0 specification: * - Table 3-14 for port descriptor * - Table 3-17 for channel descriptor * * Input: ch_desc1: first channel descriptor * ch_desc2: second channel descriptor * port_desc: a pointer that contain a list of port descriptor * index_port: index of the port descriptor * rsc_id: resource id that represents as local resource id in the * resource descriptor table. * * Output: None * * Global: None * * Return: return TRUE if both channel descriptor are matched, * or FALSE otherwise * ***************************************************************************/ static tboolean ipmi_ek_compare_channel_descriptor( struct fru_picmgext_amc_channel_desc_record ch_desc1, struct fru_picmgext_amc_channel_desc_record ch_desc2, struct fru_picmgext_carrier_p2p_descriptor * port_desc, int index_port, unsigned char rsc_id ) { tboolean match_lane = FALSE; /* carrier p2p record start with AMC_MODULE as local port */ if ( (rsc_id & AMC_MODULE) == AMC_MODULE ){ if ( (ch_desc1.lane0port == port_desc[index_port].local_port) && (ch_desc2.lane0port == port_desc[index_port].remote_port) ){ /*check if the port is enable*/ if (ch_desc1.lane1port != DISABLE_PORT){ index_port ++; if ( (ch_desc1.lane1port == port_desc[index_port].local_port) && (ch_desc2.lane1port == port_desc[index_port].remote_port) ){ if (ch_desc1.lane2port != DISABLE_PORT){ index_port++; if ( (ch_desc1.lane2port == port_desc[index_port].local_port) && (ch_desc2.lane2port == port_desc[index_port].remote_port) ){ if (ch_desc1.lane3port != DISABLE_PORT){ index_port++; if ( (ch_desc1.lane3port == port_desc[index_port].local_port) && (ch_desc2.lane3port == port_desc[index_port].remote_port) ){ match_lane = TRUE; } } else{ match_lane = TRUE; } } /* end of if lane2port */ } else{ match_lane = TRUE; } } /* end of if lane1port */ } else{ /*if the port is disable, the compare result is always true*/ match_lane = TRUE; } }/* end of if lane0port */ } /* carrier p2p record start with Carrier as local port */ else{ if ( (ch_desc1.lane0port == port_desc[index_port].remote_port) && (ch_desc2.lane0port == port_desc[index_port].local_port) ){ if (ch_desc1.lane1port != DISABLE_PORT){ index_port ++; if ( (ch_desc1.lane1port == port_desc[index_port].remote_port) && (ch_desc2.lane1port == port_desc[index_port].local_port) ){ if (ch_desc1.lane2port != DISABLE_PORT){ index_port++; if ( (ch_desc1.lane2port == port_desc[index_port].remote_port) && (ch_desc2.lane2port == port_desc[index_port].local_port) ){ if (ch_desc1.lane3port != DISABLE_PORT){ index_port++; if ( (ch_desc1.lane3port == port_desc[index_port].remote_port) && (ch_desc2.lane3port == port_desc[index_port].local_port) ){ match_lane = TRUE; } } else{ match_lane = TRUE; } } /* end of if lane2port */ } else{ match_lane = TRUE; } } /* end of if lane1port */ } else{ match_lane = TRUE; } } /* end of if lane0port */ } return match_lane; } /************************************************************************** * * Function name: ipmi_ek_compare_link_descriptor * * Description: This function compares 2 link descriptors of 2 * amc p2p connectiviy record * * Restriction: None * * Input: record1: AMC p2p connectivity record of the 1rst AMC or Carrier Module * index1: index of AMC link descriptor in 1rst record * record2: AMC p2p connectivity record of the 2nd AMC or Carrier Module * index1: index of AMC link descriptor in 2nd record * * Output: None * * Global: None * * Return: return OK_STATUS if both link are matched, * otherwise return ERROR_STATUS * ***************************************************************************/ static int ipmi_ek_compare_link_descriptor( struct ipmi_ek_amc_p2p_connectivity_record record1, int index1, struct ipmi_ek_amc_p2p_connectivity_record record2, int index2) { int result = ERROR_STATUS; if (record1.link_desc[index1].type != record2.link_desc[index2].type) { return ERROR_STATUS; } /* if it is an OEM type, we compare the OEM GUID */ if ((record1.link_desc[index1].type >= LOWER_OEM_TYPE) && (record1.link_desc[index1].type <= UPPER_OEM_TYPE)) { if ((record1.guid_count == 0) && (record2.guid_count == 0)) { /*there is no GUID for comparison, so the result is always OK*/ result = OK_STATUS; } else { int i = 0; int j = 0; for (i = 0; i < record1.guid_count; i++) { for (j = 0; j < record2.guid_count; j++) { if (memcmp(&record1.oem_guid[i], &record2.oem_guid[j], SIZE_OF_GUID) == 0) { result = OK_STATUS; break; } } } } } else { result = OK_STATUS; } if (result != OK_STATUS) { return result; } if (record1.link_desc[index1].type_ext == record2.link_desc[index2].type_ext) { unsigned char asym[COMPARE_CANDIDATE]; int offset = 0; asym[offset++] = record1.link_desc[index1].asym_match; asym[offset] = record2.link_desc[index2].asym_match; result = ipmi_ek_compare_asym (asym); if (result == OK_STATUS){ struct fru_picmgext_amc_link_desc_record link[COMPARE_CANDIDATE]; int index = 0; link[index++] = record1.link_desc[index1]; link[index] = record2.link_desc[index2]; result = ipmi_ek_compare_number_of_enable_port(link); } else { result = ERROR_STATUS; } } else { result = ERROR_STATUS; } return result; } /************************************************************************** * * Function name: ipmi_ek_compare_asym * * Description: This function compares 2 asymetric match of 2 * amc link descriptors * * Restriction: None * * Input: asym[COMPARE_CANDIDATE]: Contain 2 asymetric match for comparison * * Output: None * * Global: None * * Return: return 0 if both asym. match are matched, otherwise return -1 * ***************************************************************************/ static int ipmi_ek_compare_asym(unsigned char asym[COMPARE_CANDIDATE]) { int return_value = ERROR_STATUS; int first_index = 0; int second_index = 1; if ((asym[first_index] == 0) && (asym[second_index] == 0)) { return_value = OK_STATUS; } else if ((asym[first_index] & asym[second_index]) == 0) { return_value = OK_STATUS; } else { return_value = ERROR_STATUS; } return return_value; } /************************************************************************** * * Function name: ipmi_ek_compare_link_descriptor * * Description: This function compare number of enble port of Link designator * * Restriction: None * * Input: link_designator1: first link designator * link_designator2: second link designator * * Output: None * * Global: None * * Return: return 0 if both link are matched, otherwise return -1 * ***************************************************************************/ static int ipmi_ek_compare_number_of_enable_port( struct fru_picmgext_amc_link_desc_record link_desc[COMPARE_CANDIDATE]) { int amc_port_count = 0; int carrier_port_count = 0; int return_value = ERROR_STATUS; int index = 0; if (link_desc[index].port_flag_0) { /*bit 0 indicates port 0*/ amc_port_count++; } if (link_desc[index].port_flag_1) { /*bit 1 indicates port 1*/ amc_port_count++; } if (link_desc[index].port_flag_2) { /*bit 2 indicates port 2*/ amc_port_count++; } if (link_desc[index++].port_flag_3) { /*bit 3 indicates port 3*/ amc_port_count++; } /* 2nd link designator */ if (link_desc[index].port_flag_0) { /*bit 0 indicates port 0*/ carrier_port_count++; } if (link_desc[index].port_flag_1) { /*bit 1 indicates port 1*/ carrier_port_count++; } if (link_desc[index].port_flag_2) { /*bit 2 indicates port 2*/ carrier_port_count++; } if (link_desc[index].port_flag_3) { /*bit 3 indicates port 3*/ carrier_port_count++; } if (carrier_port_count == amc_port_count) { return_value = OK_STATUS; } else { return_value = ERROR_STATUS; } return return_value; } /************************************************************************** * * Function name: ipmi_ek_display_link_descriptor * * Description: Display the link descriptor of an AMC p2p connectivity record * * Restriction: See AMC.0 or PICMG 3.0 specification for detail about bit masks * * Input: file_type: module type. * rsc_id: resource id * char* str: indicates if it is a source (its value= "From") or a * destination (its value = "To"). ( it is set to "" if it is not * a source nor destination * link_desc: AMC link descriptor * asym: asymetric match * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static tboolean ipmi_ek_display_link_descriptor(int file_type, unsigned char rsc_id, char *str, struct fru_picmgext_amc_link_desc_record link_desc) { tboolean isOEMtype = FALSE; if (file_type == ON_CARRIER_FRU_FILE) { printf(" - %s On-Carrier Device ID %d\n", str, (rsc_id & 0x0f)); } else { printf(" - %s %s\n", str, val2str(file_type, ipmi_ekanalyzer_module_type)); } printf(" - Channel ID %d || ", link_desc.channel_id); printf("%s", link_desc.port_flag_0 ? "Lane 0: enable" : ""); printf("%s", link_desc.port_flag_1 ? ", Lane 1: enable" : ""); printf("%s", link_desc.port_flag_2 ? ", Lane 2: enable" : ""); printf("%s", link_desc.port_flag_3 ? ", Lane 3: enable" : ""); printf("\n"); printf(" - Link Type: %s \n", val2str(link_desc.type, ipmi_ekanalyzer_link_type)); switch (link_desc.type) { case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE: case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1: case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2: printf(" - Link Type extension: %s\n", val2str(link_desc.type_ext, ipmi_ekanalyzer_extension_PCIE)); printf(" - Link Group ID: %d || ", link_desc.group_id); printf("Link Asym. Match: %d - %s\n", link_desc.asym_match, val2str(link_desc.asym_match, ipmi_ekanalyzer_asym_PCIE)); break; case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET: printf(" - Link Type extension: %s\n", val2str(link_desc.type_ext, ipmi_ekanalyzer_extension_ETHERNET)); printf(" - Link Group ID: %d || ", link_desc.group_id); printf("Link Asym. Match: %d - %s\n", link_desc.asym_match, val2str(link_desc.asym_match, ipmi_ekanalyzer_asym_PCIE)); break; case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE: printf(" - Link Type extension: %s\n", val2str(link_desc.type_ext, ipmi_ekanalyzer_extension_STORAGE)); printf(" - Link Group ID: %d || ", link_desc.group_id); printf("Link Asym. Match: %d - %s\n", link_desc.asym_match, val2str(link_desc.asym_match, ipmi_ekanalyzer_asym_STORAGE)); break; default: printf(" - Link Type extension: %i\n", link_desc.type_ext); printf(" - Link Group ID: %d || ", link_desc.group_id); printf("Link Asym. Match: %i\n", link_desc.asym_match); break; } /* return as OEM type if link type indicates OEM */ if ((link_desc.type >= LOWER_OEM_TYPE) && (link_desc.type <= UPPER_OEM_TYPE)) { isOEMtype = TRUE; } return isOEMtype; } /************************************************************************** * * Function name: ipmi_ek_display_oem_guid * * Description: Display the oem guid of an AMC p2p connectivity record * * Restriction: None * * Input: amc_record: AMC p2p connectivity record * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_oem_guid(struct ipmi_ek_amc_p2p_connectivity_record amc_record) { int index_oem = 0; int index = 0; if (amc_record.guid_count == 0) { printf("\tThere is no OEM GUID for this module\n"); } for (index_oem = 0; index_oem < amc_record.guid_count; index_oem++) { printf(" - GUID: "); for (index = 0; index < SIZE_OF_GUID; index++) { printf("%02x", amc_record.oem_guid[index_oem].guid[index]); /* For a better look: putting a "-" after displaying * four bytes of GUID */ if (!(index % 4)){ printf("-"); } } printf("\n"); } } /************************************************************************** * * Function name: ipmi_ek_create_amc_p2p_record * * Description: this function create an AMC point 2 point connectivity record * that contain link descriptor, channel descriptor, oem guid * * Restriction: Reference: AMC.0 Specification Table 3-16 * * Input: record: a pointer to FRU multi record * * Output: amc_record: a pointer to the created AMC p2p record * * Global: None * * Return: Return OK_STATUS on success, or ERROR_STATUS if no record has been * created. * ***************************************************************************/ static int ipmi_ek_create_amc_p2p_record(struct ipmi_ek_multi_header *record, struct ipmi_ek_amc_p2p_connectivity_record *amc_record) { int index_data = START_DATA_OFFSET; int return_status = OK_STATUS; amc_record->guid_count = record->data[index_data++]; if (amc_record->guid_count > 0) { int index_oem = 0; amc_record->oem_guid = malloc(amc_record->guid_count * \ sizeof(struct fru_picmgext_guid)); if (amc_record->oem_guid == NULL) { return ERROR_STATUS; } for (index_oem = 0; index_oem < amc_record->guid_count; index_oem++) { memcpy(&amc_record->oem_guid[index_oem].guid, &record->data[index_data], SIZE_OF_GUID); index_data += (int)SIZE_OF_GUID; } amc_record->rsc_id = record->data[index_data++]; amc_record->ch_count = record->data[index_data++]; /* Calculate link descriptor count */ amc_record->link_desc_count = ((record->header.len) - 8 - (SIZE_OF_GUID*amc_record->guid_count) - (FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE * amc_record->ch_count)) / 5 ; } else { amc_record->rsc_id = record->data[index_data++]; amc_record->ch_count = record->data[index_data++]; /* Calculate link descriptor count see spec AMC.0 for detail */ amc_record->link_desc_count = ((record->header.len) - 8 - (FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE * amc_record->ch_count)) / 5; } if (amc_record->ch_count > 0) { int ch_index = 0; amc_record->ch_desc = malloc((amc_record->ch_count) * \ sizeof(struct fru_picmgext_amc_channel_desc_record)); if (amc_record->ch_desc == NULL) { return ERROR_STATUS; } for (ch_index = 0; ch_index < amc_record->ch_count; ch_index++) { unsigned int data; struct fru_picmgext_amc_channel_desc_record *src, *dst; data = record->data[index_data] | (record->data[index_data + 1] << 8) | (record->data[index_data + 2] << 16); src = (struct fru_picmgext_amc_channel_desc_record *)&data; dst = (struct fru_picmgext_amc_channel_desc_record *) &amc_record->ch_desc[ch_index]; dst->lane0port = src->lane0port; dst->lane1port = src->lane1port; dst->lane2port = src->lane2port; dst->lane3port = src->lane3port; index_data += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE; } } if (amc_record->link_desc_count > 0) { int i=0; amc_record->link_desc = malloc(amc_record->link_desc_count * \ sizeof(struct fru_picmgext_amc_link_desc_record)); if (amc_record->link_desc == NULL) { return ERROR_STATUS; } for (i = 0; i< amc_record->link_desc_count; i++) { unsigned int data[2]; struct fru_picmgext_amc_link_desc_record *src, *dst; data[0] = record->data[index_data] | (record->data[index_data + 1] << 8) | (record->data[index_data + 2] << 16) | (record->data[index_data + 3] << 24); data[1] = record->data[index_data + 4]; src = (struct fru_picmgext_amc_link_desc_record*)&data; dst = (struct fru_picmgext_amc_link_desc_record*) &amc_record->link_desc[i]; dst->channel_id = src->channel_id; dst->port_flag_0 = src->port_flag_0; dst->port_flag_1 = src->port_flag_1; dst->port_flag_2 = src->port_flag_2; dst->port_flag_3 = src->port_flag_3; dst->type = src->type; dst->type_ext = src->type_ext; dst->group_id = src->group_id; dst->asym_match = src->asym_match; index_data += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE; } } else { return_status = ERROR_STATUS; } return return_status; } /************************************************************************** * * Function name: ipmi_ek_get_resource_descriptor * * Description: this function create the resource descriptor of Carrier p2p * connectivity record. * * Restriction: None * * Input: port_count: number of port count * index: index to the position of data start offset * record: a pointer to FRU multi record * * Output: port_desc: a pointer to the created resource descriptor * * Global: None * * Return: Return index that indicates the current position of data in record. * ***************************************************************************/ static int ipmi_ek_get_resource_descriptor(int port_count, int index, struct fru_picmgext_carrier_p2p_descriptor *port_desc, struct ipmi_ek_multi_header *record) { int num_port = 0; while (num_port < port_count) { memcpy(&port_desc[num_port], &record->data[index], sizeof (struct fru_picmgext_carrier_p2p_descriptor)); index += sizeof (struct fru_picmgext_carrier_p2p_descriptor); num_port++; } return index; } /************************************************************************** * * Function name: ipmi_ek_display_fru_header * * Description: this function display FRU header offset from a FRU binary file * * Restriction: Reference: IPMI Platform Management FRU Information Storage * Definition V1.0, Section 8 * * Input: filename: name of FRU binary file * * Output: None * * Global: None * * Return: Return OK_STATUS on sucess, ERROR_STATUS on error * ***************************************************************************/ static int ipmi_ek_display_fru_header(char *filename) { FILE *input_file; struct fru_header header; int ret = 0; input_file = fopen(filename, "r"); if (input_file == NULL) { lprintf(LOG_ERR, "File '%s' not found.", filename); return (ERROR_STATUS); } ret = fread(&header, sizeof (struct fru_header), 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Failed to read FRU header!"); fclose(input_file); return (ERROR_STATUS); } printf("%s\n", EQUAL_LINE_LIMITER); printf("FRU Header Info\n"); printf("%s\n", EQUAL_LINE_LIMITER); printf("Format Version :0x%02x %s\n", (header.version & 0x0f), ((header.version & 0x0f) == 1) ? "" : "{unsupported}"); printf("Internal Use Offset :0x%02x\n", header.offset.internal); printf("Chassis Info Offset :0x%02x\n", header.offset.chassis); printf("Board Info Offset :0x%02x\n", header.offset.board); printf("Product Info Offset :0x%02x\n", header.offset.product); printf("MultiRecord Offset :0x%02x\n", header.offset.multi); printf("Common header Checksum :0x%02x\n", header.checksum); fclose(input_file); return OK_STATUS; } /************************************************************************** * * Function name: ipmi_ek_display_fru_header_detail * * Description: this function display detail FRU header information * from a FRU binary file. * * Restriction: Reference: IPMI Platform Management FRU Information Storage * Definition V1.0, Section 8 * * Input: filename: name of FRU binary file * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static int ipmi_ek_display_fru_header_detail(char *filename) { # define FACTOR_OFFSET 8 # define SIZE_MFG_DATE 3 FILE *input_file; size_t file_offset = 0; struct fru_header header; time_t tval; int ret = 0; unsigned char data = 0; unsigned char lan_code = 0; unsigned char mfg_date[SIZE_MFG_DATE]; unsigned int board_length = 0; input_file = fopen(filename, "r"); if (input_file == NULL) { lprintf(LOG_ERR, "File '%s' not found.", filename); return (-1); } /* The offset in each fru is in multiple of 8 bytes * See IPMI Platform Management FRU Information Storage Definition * for detail */ ret = fread(&header, sizeof(struct fru_header), 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Failed to read FRU header!"); fclose(input_file); return (-1); } /*** Display FRU Internal Use Info ***/ if (header.offset.internal != 0) { unsigned char format_version; unsigned long len = 0; printf("%s\n", EQUAL_LINE_LIMITER); printf("FRU Internal Use Info\n"); printf("%s\n", EQUAL_LINE_LIMITER); ret = fread(&format_version, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid format version!"); fclose(input_file); return (-1); } printf("Format Version: %d\n", (format_version & 0x0f)); if (header.offset.chassis > 0) { len = (header.offset.chassis * FACTOR_OFFSET) - (header.offset.internal * FACTOR_OFFSET); } else { len = (header.offset.board * FACTOR_OFFSET) - (header.offset.internal * FACTOR_OFFSET); } printf("Length: %ld\n", len); printf("Data dump:\n"); while ((len > 0) && (!feof(input_file))) { unsigned char data; ret = fread(&data, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid data!"); fclose(input_file); return (-1); } printf("0x%02x ", data); len--; } printf("\n"); } /*** Chassis Info Area ***/ if (header.offset.chassis != 0) { long offset = 0; offset = header.offset.chassis * FACTOR_OFFSET; ret = ipmi_ek_display_chassis_info_area(input_file, offset); } /*** Display FRU Board Info Area ***/ while (1) { if (header.offset.board == 0) { break; } ret = fseek(input_file, (header.offset.board * FACTOR_OFFSET), SEEK_SET); if (feof(input_file)) { break; } file_offset = ftell(input_file); printf("%s\n", EQUAL_LINE_LIMITER); printf("FRU Board Info Area\n"); printf("%s\n", EQUAL_LINE_LIMITER); ret = fread(&data, 1, 1, input_file); /* Format version */ if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid FRU Format Version!"); fclose(input_file); return (-1); } printf("Format Version: %d\n", (data & 0x0f)); if (feof(input_file)) { break; } ret = fread(&data, 1, 1, input_file); /* Board Area Length */ if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Board Area Length!"); fclose(input_file); return (-1); } board_length = (data * FACTOR_OFFSET); printf("Area Length: %d\n", board_length); /* Decrease the length of board area by 1 byte of format version * and 1 byte for area length itself. the rest of this length will * be used to check for additional custom mfg. byte */ board_length -= 2; if (feof(input_file)) { break; } ret = fread(&lan_code, 1, 1, input_file); /* Language Code */ if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Language Code in input"); fclose(input_file); return (-1); } printf("Language Code: %d\n", lan_code); board_length--; /* Board Mfg Date */ if (feof(input_file)) { break; } ret = fread(mfg_date, SIZE_MFG_DATE, 1, input_file); if (ret != 1) { lprintf(LOG_ERR, "Invalid Board Data."); fclose(input_file); return (-1); } tval = ((mfg_date[2] << 16) + (mfg_date[1] << 8) + (mfg_date[0])); tval = tval * 60; tval = tval + secs_from_1970_1996; printf("Board Mfg Date: %ld, %s", tval, asctime(localtime(&tval))); board_length -= SIZE_MFG_DATE; /* Board Mfg */ file_offset = ipmi_ek_display_board_info_area( input_file, "Board Manufacture Data", &board_length); ret = fseek(input_file, file_offset, SEEK_SET); /* Board Product */ file_offset = ipmi_ek_display_board_info_area( input_file, "Board Product Name", &board_length); ret = fseek(input_file, file_offset, SEEK_SET); /* Board Serial */ file_offset = ipmi_ek_display_board_info_area( input_file, "Board Serial Number", &board_length); ret = fseek(input_file, file_offset, SEEK_SET); /* Board Part */ file_offset = ipmi_ek_display_board_info_area( input_file, "Board Part Number", &board_length); ret = fseek(input_file, file_offset, SEEK_SET); /* FRU file ID */ file_offset = ipmi_ek_display_board_info_area( input_file, "FRU File ID", &board_length); ret = fseek(input_file, file_offset, SEEK_SET); /* Additional Custom Mfg. */ file_offset = ipmi_ek_display_board_info_area( input_file, "Custom", &board_length); break; } /* Product Info Area */ if (header.offset.product && (!feof(input_file))) { long offset = 0; offset = header.offset.product * FACTOR_OFFSET; ret = ipmi_ek_display_product_info_area(input_file, offset); } fclose(input_file); return 0; } /************************************************************************** * * Function name: ipmi_ek_display_chassis_info_area * * Description: this function displays detail format of product info area record * into humain readable text format * * Restriction: Reference: IPMI Platform Management FRU Information Storage * Definition V1.0, Section 10 * * Input: input_file: pointer to file stream * offset: start offset of chassis info area * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static int ipmi_ek_display_chassis_info_area(FILE *input_file, long offset) { size_t file_offset; int ret = 0; unsigned char data = 0; unsigned char ch_len = 0; unsigned char ch_type = 0; unsigned int len; if (input_file == NULL) { lprintf(LOG_ERR, "No file stream to read."); return (-1); } printf("%s\n", EQUAL_LINE_LIMITER); printf("Chassis Info Area\n"); printf("%s\n", EQUAL_LINE_LIMITER); ret = fseek(input_file, offset, SEEK_SET); if (feof(input_file)) { lprintf(LOG_ERR, "Invalid Chassis Info Area!"); return (-1); } ret = fread(&data, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Version Number!"); return (-1); } printf("Format Version Number: %d\n", (data & 0x0f)); ret = fread(&ch_len, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid length!"); return (-1); } /* len is in factor of 8 bytes */ len = ch_len * 8; printf("Area Length: %d\n", len); len -= 2; if (feof(input_file)) { return (-1); } /* Chassis Type*/ ret = fread(&ch_type, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Chassis Type!"); return (-1); } printf("Chassis Type: %d\n", ch_type); len--; /* Chassis Part Number*/ file_offset = ipmi_ek_display_board_info_area(input_file, "Chassis Part Number", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Chassis Serial */ file_offset = ipmi_ek_display_board_info_area(input_file, "Chassis Serial Number", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Custom product info area */ file_offset = ipmi_ek_display_board_info_area(input_file, "Custom", &len); return 0; } /************************************************************************** * * Function name: ipmi_ek_display_board_info_area * * Description: this function displays board info area depending on board type * that pass in argument. Board type can be: * Manufacturer, Serial, Product or Part... * * Restriction: IPMI Platform Management FRU Information Storage * Definition V1.0, Section 11 * * Input: input_file: pointer to file stream * board_type: a string that contain board type * board_length: length of info area * * Output: None * * Global: None * * Return: the current position of input_file * ***************************************************************************/ static size_t ipmi_ek_display_board_info_area(FILE *input_file, char *board_type, unsigned int *board_length) { size_t file_offset; int ret = 0; unsigned char len = 0; unsigned int size_board = 0; if (input_file == NULL || board_type == NULL || board_length == NULL) { return (size_t)(-1); } file_offset = ftell(input_file); /* Board length*/ ret = fread(&len, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Length!"); goto out; } (*board_length)--; /* Bit 5:0 of Board Mfg type represent legnth */ size_board = (len & 0x3f); if (size_board == 0) { printf("%s: None\n", board_type); goto out; } if (strncmp(board_type, "Custom", 6 ) != 0) { unsigned char *data; unsigned int i = 0; data = malloc(size_board); if (data == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return (size_t)(-1); } ret = fread(data, size_board, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid board type size!"); free(data); data = NULL; goto out; } printf("%s type: 0x%02x\n", board_type, len); printf("%s: ", board_type); for (i = 0; i < size_board; i++) { if ((len & TYPE_CODE) == TYPE_CODE) { printf("%c", data[i]); } else { /* other than language code (binary, BCD, * ASCII 6 bit...) is not supported */ printf("%02x", data[i]); } } printf("\n"); free(data); data = NULL; (*board_length) -= size_board; goto out; } while (!feof(input_file)) { if (len == NO_MORE_INFO_FIELD) { unsigned char padding; unsigned char checksum = 0; /* take the rest of data in the area minus 1 byte of * checksum */ printf("Additional Custom Mfg. length: 0x%02x\n", len); padding = (*board_length) - 1; if ((padding > 0) && (!feof(input_file))) { printf("Unused space: %d (bytes)\n", padding); fseek(input_file, padding, SEEK_CUR); } ret = fread(&checksum, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Checksum!"); goto out; } printf("Checksum: 0x%02x\n", checksum); goto out; } printf("Additional Custom Mfg. length: 0x%02x\n", len); if ((size_board > 0) && (size_board < (*board_length))) { unsigned char * additional_data = NULL; unsigned int i = 0; additional_data = malloc(size_board); if (additional_data == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return (size_t)(-1); } ret = fread(additional_data, size_board, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Additional Data!"); if (additional_data != NULL) { free(additional_data); additional_data = NULL; } goto out; } printf("Additional Custom Mfg. Data: %02x", additional_data[0]); for (i = 1; i < size_board; i++) { printf("-%02x", additional_data[i]); } printf("\n"); free(additional_data); additional_data = NULL; (*board_length) -= size_board; } else { printf("No Additional Custom Mfg. %d\n", *board_length); goto out; } } out: file_offset = ftell(input_file); return file_offset; } /************************************************************************** * * Function name: ipmi_ek_display_product_info_area * * Description: this function displays detail format of product info area record * into humain readable text format * * Restriction: Reference: IPMI Platform Management FRU Information Storage * Definition V1.0, Section 12 * * Input: input_file: pointer to file stream * offset: start offset of product info area * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static int ipmi_ek_display_product_info_area(FILE *input_file, long offset) { size_t file_offset; int ret = 0; unsigned char ch_len = 0; unsigned char data = 0; unsigned int len = 0; if (input_file == NULL) { lprintf(LOG_ERR, "No file stream to read."); return (-1); } file_offset = ftell(input_file); printf("%s\n", EQUAL_LINE_LIMITER); printf("Product Info Area\n"); printf("%s\n", EQUAL_LINE_LIMITER); ret = fseek(input_file, offset, SEEK_SET); if (feof(input_file)) { lprintf(LOG_ERR, "Invalid Product Info Area!"); return (-1); } ret = fread(&data, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Data!"); return (-1); } printf("Format Version Number: %d\n", (data & 0x0f)); if (feof(input_file)) { return (-1); } /* Have to read this into a char or else * it ends up byte swapped on big endian machines */ ret = fread(&ch_len, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Length!"); return (-1); } /* length is in factor of 8 bytes */ len = ch_len * 8; printf("Area Length: %d\n", len); len -= 2; /* -1 byte of format version and -1 byte itself */ ret = fread(&data, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Length!"); return (-1); } printf("Language Code: %d\n", data); len--; /* Product Mfg */ file_offset = ipmi_ek_display_board_info_area(input_file, "Product Manufacture Data", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Product Name */ file_offset = ipmi_ek_display_board_info_area(input_file, "Product Name", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Product Part */ file_offset = ipmi_ek_display_board_info_area(input_file, "Product Part/Model Number", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Product Version */ file_offset = ipmi_ek_display_board_info_area(input_file, "Product Version", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Product Serial */ file_offset = ipmi_ek_display_board_info_area(input_file, "Product Serial Number", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Product Asset Tag */ file_offset = ipmi_ek_display_board_info_area(input_file, "Asset Tag", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* FRU file ID */ file_offset = ipmi_ek_display_board_info_area(input_file, "FRU File ID", &len); ret = fseek(input_file, file_offset, SEEK_SET); /* Custom product info area */ file_offset = ipmi_ek_display_board_info_area(input_file, "Custom", &len); return 0; } /************************************************************************** * * Function name: ipmi_ek_display_record * * Description: this function displays FRU multi record information. * * Restriction: None * * Input: record: a pointer to current record * list_head: a pointer to header of the list * list_last: a pointer to tale of the list * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_record(struct ipmi_ek_multi_header *record, struct ipmi_ek_multi_header *list_head, struct ipmi_ek_multi_header *list_last) { if (list_head == NULL) { printf("***empty list***\n"); return; } printf("%s\n", EQUAL_LINE_LIMITER); printf("FRU Multi Info area\n"); printf("%s\n", EQUAL_LINE_LIMITER); for (record = list_head; record != NULL; record = record->next) { printf("Record Type ID: 0x%02x\n", record->header.type); printf("Record Format version: 0x%02x\n", record->header.format); if (record->header.len <= PICMG_ID_OFFSET) { continue; } /* In picmg3.0 specification, picmg record * id lower than 4 or greater than 0x2d * isn't supported */ #define PICMG_ID_LOWER_LIMIT 0x04 #define PICMG_ID_UPPER_LIMIT 0x2d unsigned char picmg_id; picmg_id = record->data[PICMG_ID_OFFSET]; printf("Manufacturer ID: %02x%02x%02x h\n", record->data[2], record->data[1], record->data[0]); if ((picmg_id < PICMG_ID_LOWER_LIMIT) || (picmg_id > PICMG_ID_UPPER_LIMIT)) { printf("Picmg record ID: Unsupported {0x%02x}\n", picmg_id); } else { printf("Picmg record ID: %s {0x%02x}\n", val2str(picmg_id, ipmi_ekanalyzer_picmg_record_id), picmg_id); } switch (picmg_id) { case FRU_PICMG_BACKPLANE_P2P: /*0x04*/ ipmi_ek_display_backplane_p2p_record (record); break; case FRU_PICMG_ADDRESS_TABLE: /*0x10*/ ipmi_ek_display_address_table_record (record); break; case FRU_PICMG_SHELF_POWER_DIST: /*0x11*/ ipmi_ek_display_shelf_power_distribution_record (record); break; case FRU_PICMG_SHELF_ACTIVATION: /*/0x12*/ ipmi_ek_display_shelf_activation_record (record); break; case FRU_PICMG_SHMC_IP_CONN: /*0x13*/ ipmi_ek_display_shelf_ip_connection_record (record); break; case FRU_PICMG_BOARD_P2P: /*0x14*/ ipmi_ek_display_board_p2p_record (record); break; case FRU_RADIAL_IPMB0_LINK_MAPPING: /*0x15*/ ipmi_ek_display_radial_ipmb0_record (record); break; case FRU_AMC_CURRENT: /*0x16*/ ipmi_ek_display_amc_current_record (record); break; case FRU_AMC_ACTIVATION: /*0x17*/ ipmi_ek_display_amc_activation_record (record); break; case FRU_AMC_CARRIER_P2P: /*0x18*/ ipmi_ek_display_carrier_connectivity (record); break; case FRU_AMC_P2P: /*0x19*/ ipmi_ek_display_amc_p2p_record (record); break; case FRU_AMC_CARRIER_INFO: /*0x1a*/ ipmi_ek_display_amc_carrier_info_record (record); break; case FRU_PICMG_CLK_CARRIER_P2P: /*0x2c*/ ipmi_ek_display_clock_carrier_p2p_record (record); break; case FRU_PICMG_CLK_CONFIG: /*0x2d*/ ipmi_ek_display_clock_config_record (record); break; default: if (verbose > 0) { int i; printf("%02x %02x %02x %02x %02x ", record->header.type, record->header.format, record->header.len, record->header.record_checksum, record->header.header_checksum); for (i = 0; i < record->header.len; i++) { printf("%02x ", record->data[i]); } printf("\n"); } break; } printf("%s\n", STAR_LINE_LIMITER); } } /************************************************************************** * * Function name: ipmi_ek_display_backplane_p2p_record * * Description: this function displays backplane p2p record. * * Restriction: Reference: PICMG 3.0 Specification Table 3-40 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_backplane_p2p_record(struct ipmi_ek_multi_header *record) { uint8_t index; int offset = START_DATA_OFFSET; struct fru_picmgext_slot_desc *slot_d = (struct fru_picmgext_slot_desc*)&record->data[offset]; offset += sizeof(struct fru_picmgext_slot_desc); while (offset <= record->header.len) { printf(" Channel Type: "); switch (slot_d->chan_type) { case 0x00: case 0x07: printf("PICMG 2.9\n"); break; case 0x08: printf("Single Port Fabric IF\n"); break; case 0x09: printf("Double Port Fabric IF\n"); break; case 0x0a: printf("Full Channel Fabric IF\n"); break; case 0x0b: printf("Base IF\n"); break; case 0x0c: printf("Update Channel IF\n"); break; default: printf("Unknown IF\n"); break; } printf(" Slot Address: %02x\n", slot_d->slot_addr); printf(" Channel Count: %i\n", slot_d->chn_count); for (index = 0; index < (slot_d->chn_count); index++) { struct fru_picmgext_chn_desc *d = (struct fru_picmgext_chn_desc *)&record->data[offset]; if (verbose) { printf("\t" "Chn: %02x --> " "Chn: %02x in " "Slot: %02x\n", d->local_chn, d->remote_chn, d->remote_slot); } offset += sizeof(struct fru_picmgext_chn_desc); } slot_d = (struct fru_picmgext_slot_desc*)&record->data[offset]; offset += sizeof(struct fru_picmgext_slot_desc); } } /************************************************************************** * * Function name: ipmi_ek_display_address_table_record * * Description: this function displays address table record. * * Restriction: Reference: PICMG 3.0 Specification Table 3-6 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_address_table_record(struct ipmi_ek_multi_header *record) { #define SIZE_SHELF_ADDRESS_BYTE 20 unsigned char entries = 0; unsigned char i; int offset = START_DATA_OFFSET; printf(" Type/Len: 0x%02x\n", record->data[offset++]); printf(" Shelf Addr: "); for (i = 0; i < SIZE_SHELF_ADDRESS_BYTE; i++) { printf("0x%02x ", record->data[offset++]); } printf("\n"); entries = record->data[offset++]; printf(" Addr Table Entries count: 0x%02x\n", entries); for (i = 0; i < entries; i++) { printf("\tHWAddr: 0x%02x - SiteNum: 0x%02x - SiteType: 0x%02x \n", record->data[offset+0], record->data[offset+1], record->data[offset+2]); offset += 3; } } /************************************************************************** * * Function name: ipmi_ek_display_shelf_power_distribution_record * * Description: this function displays shelf power distribution record. * * Restriction: Reference: PICMG 3.0 Specification Table 3-70 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_shelf_power_distribution_record( struct ipmi_ek_multi_header *record) { int offset = START_DATA_OFFSET; unsigned char i; unsigned char j; unsigned char feeds = 0; feeds = record->data[offset++]; printf(" Number of Power Feeds: 0x%02x\n", feeds); for (i = 0; i < feeds; i++) { unsigned char entries; unsigned long max_ext = 0; unsigned long max_int = 0; max_ext = record->data[offset+0] | (record->data[offset+1] << 8); printf(" Max External Available Current: %ld Amps\n", (max_ext * 10)); offset += 2; max_int = record->data[offset+0] | (record->data[offset+1] << 8); printf(" Max Internal Current:\t %ld Amps\n", (max_int * 10)); offset += 2; printf(" Min Expected Operating Voltage: %d Volts\n", (record->data[offset++] / 2)); entries = record->data[offset++]; printf(" Feed to FRU count: 0x%02x\n", entries); for (j = 0; j < entries; j++) { printf("\tHW: 0x%02x", record->data[offset++]); printf("\tFRU ID: 0x%02x\n", record->data[offset++]); } } } /************************************************************************** * * Function name: ipmi_ek_display_shelf_activation_record * * Description: this function displays shelf activation record. * * Restriction: Reference: PICMG 3.0 Specification Table 3-73 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_shelf_activation_record(struct ipmi_ek_multi_header *record) { unsigned char count = 0; int offset = START_DATA_OFFSET; printf(" Allowance for FRU Act Readiness: 0x%02x\n", record->data[offset++]); count = record->data[offset++]; printf(" FRU activation and Power Desc Cnt: 0x%02x\n", count); while (count > 0) { printf(" FRU activation and Power descriptor:\n"); printf("\tHardware Address:\t\t0x%02x\n", record->data[offset++]); printf("\tFRU Device ID:\t\t\t0x%02x\n", record->data[offset++]); printf("\tMax FRU Power Capability:\t0x%04x Watts\n", (record->data[offset+0] | (record->data[offset+1]<<8))); offset += 2; printf("\tConfiguration parameter:\t0x%02x\n", record->data[offset++]); count --; } } /************************************************************************** * * Function name: ipmi_ek_display_shelf_ip_connection_record * * Description: this function displays shelf ip connection record. * * Restriction: Fix me: Don't test yet * Reference: PICMG 3.0 Specification Table 3-31 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_shelf_ip_connection_record(struct ipmi_ek_multi_header *record) { int ioffset = START_DATA_OFFSET; if (ioffset > record->header.len) { printf(" Shelf Manager IP Address: %d.%d.%d.%d\n", record->data[ioffset+0], record->data[ioffset+1], record->data[ioffset+2], record->data[ioffset+3]); ioffset += 4; } if (ioffset > record->header.len) { printf(" Default Gateway Address: %d.%d.%d.%d\n", record->data[ioffset+0], record->data[ioffset+1], record->data[ioffset+2], record->data[ioffset+3]); ioffset += 4; } if (ioffset > record->header.len) { printf(" Subnet Mask: %d.%d.%d.%d\n", record->data[ioffset+0], record->data[ioffset+1], record->data[ioffset+2], record->data[ioffset+3]); ioffset += 4; } } /************************************************************************** * * Function name: ipmi_ek_display_shelf_fan_geography_record * * Description: this function displays shelf fan geography record. * * Restriction: Fix me: Don't test yet * Reference: PICMG 3.0 Specification Table 3-75 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_shelf_fan_geography_record(struct ipmi_ek_multi_header *record) { int ioffset = START_DATA_OFFSET; unsigned char fan_count = 0; fan_count = record->data[ioffset]; ioffset++; printf(" Fan-to-FRU Entry Count: 0x%02x\n", fan_count); while ((fan_count > 0) && (ioffset <= record->header.len)) { printf(" Fan-to-FRU Mapping Entry: {%2x%2x%2x%2x}\n", record->data[ioffset], record->data[ioffset+1], record->data[ioffset+2], record->data[ioffset+3]); printf(" Hardware Address: 0x%02x\n", record->data[ioffset++]); printf(" FRU device ID: 0x%02x\n", record->data[ioffset++]); printf(" Site Number: 0x%02x\n", record->data[ioffset++]); printf(" Site Type: 0x%02x\n", record->data[ioffset++]); fan_count --; } } /************************************************************************** * * Function name: ipmi_ek_display_board_p2p_record * * Description: this function displays board pont-to-point record. * * Restriction: Reference: PICMG 3.0 Specification Table 3-44 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_board_p2p_record(struct ipmi_ek_multi_header *record) { unsigned char guid_count; int offset = START_DATA_OFFSET; int i = 0; guid_count = record->data[offset++]; printf(" GUID count: %2d\n", guid_count); for (i = 0 ; i < guid_count; i++) { int j; printf("\tGUID: "); for (j = 0; j < sizeof(struct fru_picmgext_guid); j++) { printf("%02x", record->data[offset+j]); } printf("\n"); offset += sizeof(struct fru_picmgext_guid); } for (offset = offset; offset < record->header.len; offset += sizeof(struct fru_picmgext_link_desc)) { /* to solve little endian/big endian problem */ unsigned long data; struct fru_picmgext_link_desc * d; data = (record->data[offset+0]) | (record->data[offset+1] << 8)\ | (record->data[offset+2] << 16)\ | (record->data[offset+3] << 24); d = (struct fru_picmgext_link_desc *)&data; printf(" Link Descriptor\n"); printf("\tLink Grouping ID:\t0x%02x\n", d->grouping); printf("\tLink Type Extension:\t0x%02x - ", d->ext); if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE) { switch (d->ext) { case 0: printf("10/100/1000BASE-T Link (four-pair)\n"); break; case 1: printf("ShMC Cross-connect (two-pair)\n"); break; default: printf("Unknwon\n"); break; } } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET) { switch (d->ext) { case 0: printf("Fixed 1000Base-BX\n"); break; case 1: printf("Fixed 10GBASE-BX4 [XAUI]\n"); break; case 2: printf("FC-PI\n"); break; default: printf("Unknwon\n"); break; } } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND) { printf("Unknwon\n"); } else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR) { printf("Unknwon\n"); } else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE) { printf("Unknwon\n"); } else { printf("Unknwon\n"); } printf("\tLink Type:\t\t0x%02x - ", d->type); if (d->type == 0 || d->type == 0xff) { printf("Reserved\n"); } else if (d->type >= 0x06 && d->type <= 0xef) { printf("Reserved\n"); } else if (d->type >= LOWER_OEM_TYPE && d->type <= UPPER_OEM_TYPE) { printf("OEM GUID Definition\n"); } else { switch (d->type){ case FRU_PICMGEXT_LINK_TYPE_BASE: printf("PICMG 3.0 Base Interface 10/100/1000\n"); break; case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET: printf("PICMG 3.1 Ethernet Fabric Interface\n"); break; case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND: printf("PICMG 3.2 Infiniband Fabric Interface\n"); break; case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR: printf("PICMG 3.3 Star Fabric Interface\n"); break; case FRU_PICMGEXT_LINK_TYPE_PCIE: printf("PICMG 3.4 PCI Express Fabric Interface\n"); break; default: printf("Invalid\n"); break; } } printf("\tLink Designator: \n"); printf("\t Port 0 Flag: %s\n", (d->desig_port & 0x01) ? "enable" : "disable"); printf("\t Port 1 Flag: %s\n", (d->desig_port & 0x02) ? "enable" : "disable"); printf("\t Port 2 Flag: %s\n", (d->desig_port & 0x04) ? "enable" : "disable"); printf("\t Port 3 Flag: %s\n", (d->desig_port & 0x08) ? "enable" : "disable"); printf("\t Interface: 0x%02x - ", d->desig_if); switch (d->desig_if) { case FRU_PICMGEXT_DESIGN_IF_BASE: printf("Base Interface\n"); break; case FRU_PICMGEXT_DESIGN_IF_FABRIC: printf("Fabric Interface\n"); break; case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL: printf("Update Channel\n"); break; case FRU_PICMGEXT_DESIGN_IF_RESERVED: printf("Reserved\n"); break; default: printf("Invalid"); break; } printf("\t Channel Number: 0x%02x\n", d->desig_channel); } } /************************************************************************** * * Function name: ipmi_ek_display_radial_ipmb0_record * * Description: this function displays radial IPMB-0 record. * * Restriction: Fix me: Don't test yet * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_radial_ipmb0_record(struct ipmi_ek_multi_header *record) { #define SIZE_OF_CONNECTOR_DEFINER 3; /*bytes*/ int offset = START_DATA_OFFSET; /* Ref: PICMG 3.0 Specification Revision 2.0, Table 3-59 */ printf(" IPMB-0 Connector Definer: "); #ifndef WORDS_BIGENDIAN printf("%02x %02x %02x h\n", record->data[offset], record->data[offset+1], record->data[offset+2]); #else printf("%02x %02x %02x h\n", record->data[offset+2], record->data[offset+1], record->data[offset]); #endif /* 3 bytes of connector definer was used */ offset += SIZE_OF_CONNECTOR_DEFINER; printf(" IPMB-0 Connector version ID: "); #ifndef WORDS_BIGENDIAN printf("%02x %02x h\n", record->data[offset], record->data[offset+1]); #else printf("%02x %02x h\n", record->data[offset+1], record->data[offset]); #endif offset += 2; printf(" IPMB-0 Hub Descriptor Count: 0x%02x", record->data[offset++]); if (record->data[offset] < 1) { return; } for (offset = offset; offset < record->header.len;) { unsigned char entry_count = 0; printf(" IPMB-0 Hub Descriptor\n"); printf("\tHardware Address: 0x%02x\n", record->data[offset++]); printf("\tHub Info {0x%02x}: ", record->data[offset]); /* Bit mask specified in Table 3-59 * of PICMG 3.0 Specification */ if ((record->data[offset] & 0x01) == 0x01) { printf("IPMB-A only\n"); } else if ((record->data[offset] & 0x02) == 0x02) { printf("IPMB-B only\n"); } else if ((record->data[offset] & 0x03) == 0x03) { printf("IPMB-A and IPMB-B\n"); } else { printf("Reserved.\n"); } offset ++; entry_count = record->data[offset++]; printf("\tAddress Entry count: 0x%02x", entry_count); while (entry_count > 0) { printf("\t Hardware Address: 0x%02x\n", record->data[offset++]); printf("\t IPMB-0 Link Entry: 0x%02x\n", record->data[offset++]); entry_count --; } } } /************************************************************************** * * Function name: ipmi_ek_display_amc_current_record * * Description: this function displays AMC current record. * * Restriction: None * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_amc_current_record(struct ipmi_ek_multi_header *record) { unsigned char current; current = record->data[START_DATA_OFFSET]; printf(" Current draw: %.1f A @ 12V => %.2f Watt\n", (float)current / 10.0, ((float)current / 10.0) * 12.0); printf("\n"); } /************************************************************************** * * Function name: ipmi_ek_display_amc_activation_record * * Description: this function displays carrier activation and current management * record. * * Restriction: Reference: AMC.0 Specification Table 3-11 and Table 3-12 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_amc_activation_record(struct ipmi_ek_multi_header *record) { uint16_t max_current; int offset = START_DATA_OFFSET; max_current = record->data[offset]; max_current |= record->data[++offset] << 8; printf(" Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n", (float) max_current / 10, (float) max_current / 10 * 12); printf(" Module Activation Readiness: %i sec.\n", record->data[++offset]); printf(" Descriptor Count: %i\n", record->data[++offset]); for (++offset; (offset < record->header.len); offset += 3) { struct fru_picmgext_activation_record *a = (struct fru_picmgext_activation_record *)&record->data[offset]; printf("\tIPMB-Address:\t\t0x%x\n", a->ibmb_addr); printf("\tMax. Module Current:\t%.2f A\n", (float)a->max_module_curr / 10); printf("\n"); } } /************************************************************************** * * Function name: ipmi_ek_display_amc_p2p_record * * Description: this function display amc p2p connectivity record in humain * readable text format * * Restriction: Reference: AMC.0 Specification Table 3-16 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_amc_p2p_record(struct ipmi_ek_multi_header *record) { int index_data = START_DATA_OFFSET; int oem_count = 0; int ch_count = 0; int index=0; oem_count = record->data[index_data++]; printf("OEM GUID count: %02x\n", oem_count); if (oem_count > 0) { while (oem_count > 0) { printf("OEM GUID: "); for (index = 1; index <= SIZE_OF_GUID; index++) { printf("%02x", record->data[index_data++]); /* For a better look, display a "-" character * after each 5 bytes of OEM GUID */ if (!(index % 5)) { printf("-"); } } printf("\n"); oem_count--; } } if ((record->data[index_data] & AMC_MODULE) == AMC_MODULE) { printf("AMC module connection\n"); } else { printf("On-Carrier Device %02x h\n", (record->data[index_data] & 0x0f)); } index_data ++; ch_count = record->data[index_data++]; printf("AMC Channel Descriptor count: %02x h\n", ch_count); if (ch_count > 0) { for (index = 0; index < ch_count; index++) { unsigned int data; struct fru_picmgext_amc_channel_desc_record *ch_desc; printf(" AMC Channel Descriptor {%02x%02x%02x}\n", record->data[index_data+2], record->data[index_data+1], record->data[index_data]); data = record->data[index_data] | (record->data[index_data + 1] << 8) | (record->data[index_data + 2] << 16); ch_desc = (struct fru_picmgext_amc_channel_desc_record *)&data; printf(" Lane 0 Port: %d\n", ch_desc->lane0port); printf(" Lane 1 Port: %d\n", ch_desc->lane1port); printf(" Lane 2 Port: %d\n", ch_desc->lane2port); printf(" Lane 3 Port: %d\n\n", ch_desc->lane3port); index_data += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE; } } while (index_data < record->header.len) { /* Warning: This code doesn't work with gcc version * between 4.0 and 4.3 */ unsigned int data[2]; struct fru_picmgext_amc_link_desc_record *link_desc; data[0] = record->data[index_data] | (record->data[index_data + 1] << 8) | (record->data[index_data + 2] << 16) | (record->data[index_data + 3] << 24); data[1] = record->data[index_data + 4]; link_desc = (struct fru_picmgext_amc_link_desc_record *)&data[0]; printf(" AMC Link Descriptor:\n"); printf("\t- Link Type: %s \n", val2str(link_desc->type, ipmi_ekanalyzer_link_type)); switch (link_desc->type) { case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE: case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1: case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2: printf("\t- Link Type extension: %s\n", val2str(link_desc->type_ext, ipmi_ekanalyzer_extension_PCIE)); printf("\t- Link Group ID: %d\n ", link_desc->group_id); printf("\t- Link Asym. Match: %d - %s\n", link_desc->asym_match, val2str(link_desc->asym_match, ipmi_ekanalyzer_asym_PCIE)); break; case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET: printf("\t- Link Type extension: %s\n", val2str (link_desc->type_ext, ipmi_ekanalyzer_extension_ETHERNET)); printf("\t- Link Group ID: %d \n", link_desc->group_id); printf("\t- Link Asym. Match: %d - %s\n", link_desc->asym_match, val2str(link_desc->asym_match, ipmi_ekanalyzer_asym_PCIE)); break; case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE: printf("\t- Link Type extension: %s\n", val2str (link_desc->type_ext, ipmi_ekanalyzer_extension_STORAGE)); printf("\t- Link Group ID: %d \n", link_desc->group_id); printf("\t- Link Asym. Match: %d - %s\n", link_desc->asym_match, val2str(link_desc->asym_match, ipmi_ekanalyzer_asym_STORAGE)); break; default: printf("\t- Link Type extension: %i (Unknown)\n", link_desc->type_ext); printf("\t- Link Group ID: %d \n", link_desc->group_id); printf("\t- Link Asym. Match: %i\n", link_desc->asym_match); break; } printf("\t- AMC Link Designator:\n"); printf("\t Channel ID: %i\n", link_desc->channel_id); printf("\t\t Lane 0: %s\n", (link_desc->port_flag_0) ? "enable" : "disable"); printf("\t\t Lane 1: %s\n", (link_desc->port_flag_1) ? "enable" : "disable"); printf("\t\t Lane 2: %s\n", (link_desc->port_flag_2) ? "enable" : "disable"); printf("\t\t Lane 3: %s\n", (link_desc->port_flag_3) ? "enable" : "disable"); index_data += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE; } } /************************************************************************** * * Function name: ipmi_ek_display_amc_carrier_info_record * * Description: this function displays Carrier information table. * * Restriction: Reference: AMC.0 Specification Table 3-3 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: START_DATA_OFFSET * * Return: None * ***************************************************************************/ static void ipmi_ek_display_amc_carrier_info_record(struct ipmi_ek_multi_header *record) { unsigned char extVersion; unsigned char siteCount; int offset = START_DATA_OFFSET; extVersion = record->data[offset++]; siteCount = record->data[offset++]; printf(" AMC.0 extension version: R%d.%d\n", (extVersion >> 0) & 0x0F, (extVersion >> 4) & 0x0F); printf(" Carrier Sie Number Count: %d\n", siteCount); while (siteCount > 0) { printf("\tSite ID (%d): %s \n", record->data[offset], val2str(record->data[offset], ipmi_ekanalyzer_module_type)); offset++; siteCount--; } printf("\n"); } /************************************************************************** * * Function name: ipmi_ek_display_clock_carrier_p2p_record * * Description: this function displays Carrier clock point-to-pont * connectivity record. * * Restriction: the following code is copy from ipmi_fru.c with modification in * reference to AMC.0 Specification Table 3-29 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_display_clock_carrier_p2p_record(struct ipmi_ek_multi_header *record) { unsigned char desc_count; int i; int j; int offset = START_DATA_OFFSET; desc_count = record->data[offset++]; for(i = 0; i < desc_count; i++) { unsigned char resource_id; unsigned char channel_count; resource_id = record->data[offset++]; channel_count = record->data[offset++]; printf(" Clock Resource ID: 0x%02x\n", resource_id); printf(" Type: "); if ((resource_id & 0xC0) >> 6 == 0) { printf("On-Carrier-Device\n"); } else if ((resource_id & 0xC0) >> 6 == 1) { printf("AMC slot\n"); } else if ((resource_id & 0xC0) >> 6 == 2) { printf("Backplane\n"); } else{ printf("reserved\n"); } printf(" Channel Count: 0x%02x\n", channel_count); for (j = 0; j < channel_count; j++) { unsigned char loc_channel; unsigned char rem_channel; unsigned char rem_resource; loc_channel = record->data[offset++]; rem_channel = record->data[offset++]; rem_resource = record->data[offset++]; printf("\tCLK-ID: 0x%02x ---> ", loc_channel); printf(" remote CLKID: 0x%02x ", rem_channel); if ((rem_resource & 0xC0) >> 6 == 0) { printf("[ Carrier-Dev"); } else if ((rem_resource & 0xC0) >> 6 == 1) { printf("[ AMC slot "); } else if ((rem_resource & 0xC0) >> 6 == 2) { printf("[ Backplane "); } else { printf("reserved "); } printf(" 0x%02x ]\n", rem_resource & 0xF); } } printf("\n"); } /************************************************************************** * * Function name: ipmi_ek_display_clock_config_record * * Description: this function displays clock configuration record. * * Restriction: the following codes are copy from ipmi_fru.c with modification * in reference to AMC.0 Specification Table 3-35 and Table 3-36 * * Input: record: a pointer to current record to be displayed * * Output: None * * Global: START_DATA_OFFSET * * Return: None * ***************************************************************************/ void ipmi_ek_display_clock_config_record(struct ipmi_ek_multi_header *record) { unsigned char resource_id; unsigned char descr_count; int i; int offset = START_DATA_OFFSET; resource_id = record->data[offset++]; descr_count = record->data[offset++]; printf(" Clock Resource ID: 0x%02x\n", resource_id); printf(" Clock Configuration Descriptor Count: 0x%02x\n", descr_count); for (i = 0; i < descr_count; i++) { int j = 0; unsigned char channel_id; unsigned char control; unsigned char indirect_cnt; unsigned char direct_cnt; channel_id = record->data[offset++]; control = record->data[offset++]; printf("\tCLK-ID: 0x%02x - ", channel_id); printf("CTRL 0x%02x [ %12s ]\n", control, ((control & 0x1) == 0) ? "Carrier IPMC" : "Application"); indirect_cnt = record->data[offset++]; direct_cnt = record->data[offset++]; printf("\t Count: Indirect 0x%02x / Direct 0x%02x\n", indirect_cnt, direct_cnt); /* indirect desc */ for (j = 0; j < indirect_cnt; j++) { unsigned char feature; unsigned char dep_chn_id; feature = record->data[offset++]; dep_chn_id = record->data[offset++]; printf("\t\tFeature: 0x%02x [%8s] - ", feature, (feature & 0x1) == 1 ? "Source" : "Receiver"); printf(" Dep. CLK-ID: 0x%02x\n", dep_chn_id); } /* direct desc */ for (j = 0; j < direct_cnt; j++) { unsigned char feature; unsigned char family; unsigned char accuracy; unsigned long freq; unsigned long min_freq; unsigned long max_freq; feature = record->data[offset++]; family = record->data[offset++]; accuracy = record->data[offset++]; freq = (record->data[offset+0] << 0) | (record->data[offset+1] << 8) | (record->data[offset+2] << 16) | (record->data[offset+3] << 24); offset += 4; min_freq = (record->data[offset+0] << 0) | (record->data[offset+1] << 8) | (record->data[offset+2] << 16) | (record->data[offset+3] << 24); offset += 4; max_freq = (record->data[offset+0] << 0) | (record->data[offset+1] << 8) | (record->data[offset+2] << 16) | (record->data[offset+3] << 24); offset += 4; printf("\t- Feature: 0x%02x - PLL: %x / Asym: %s\n", feature, (feature > 1) & 1, (feature & 1) ? "Source" : "Receiver"); printf("\tFamily: 0x%02x - AccLVL: 0x%02x\n", family, accuracy); printf("\tFRQ: %-9ld - min: %-9ld - max: %-9ld\n", freq, min_freq, max_freq); } printf("\n"); } } /************************************************************************** * * Function name: ipmi_ekanalyzer_fru_file2structure * * Description: this function convert a FRU binary file into a linked list of * FRU multi record * * Restriction: None * * Input/Ouput: filename1: name of the file that contain FRU binary data * record: a pointer to current record * list_head: a pointer to header of the list * list_last: a pointer to tale of the list * * Global: None * * Return: return -1 as Error status, and 0 as Ok status * ***************************************************************************/ static int ipmi_ekanalyzer_fru_file2structure(char *filename, struct ipmi_ek_multi_header **list_head, struct ipmi_ek_multi_header **list_record, struct ipmi_ek_multi_header **list_last) { FILE *input_file; unsigned char data; unsigned char last_record = 0; unsigned int multi_offset = 0; int record_count = 0; int ret = 0; input_file = fopen(filename, "r"); if (input_file == NULL) { lprintf(LOG_ERR, "File: '%s' is not found", filename); return ERROR_STATUS; } fseek(input_file, START_DATA_OFFSET, SEEK_SET); data = 0; ret = fread(&data, 1, 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Offset!"); fclose(input_file); return ERROR_STATUS; } if (data == 0) { lprintf(LOG_ERR, "There is no multi record in the file '%s'", filename); fclose(input_file); return ERROR_STATUS; } /* the offset value is in multiple of 8 bytes. */ multi_offset = data * 8; lprintf(LOG_DEBUG, "start multi offset = 0x%02x", multi_offset); fseek(input_file, multi_offset, SEEK_SET); while (!feof(input_file)) { *list_record = malloc(sizeof(struct ipmi_ek_multi_header)); if (*list_record == NULL) { lprintf(LOG_ERR, "ipmitool: malloc failure"); return ERROR_STATUS; } ret = fread(&(*list_record)->header, START_DATA_OFFSET, 1, input_file); if ((ret != 1) || ferror(input_file)) { free(*list_record); *list_record = NULL; fclose(input_file); lprintf(LOG_ERR, "Invalid Header!"); return ERROR_STATUS; } if ((*list_record)->header.len == 0) { record_count++; continue; } (*list_record)->data = malloc((*list_record)->header.len); if ((*list_record)->data == NULL) { lprintf(LOG_ERR, "Failed to allocation memory size %d\n", (*list_record)->header.len); record_count++; continue; } ret = fread((*list_record)->data, ((*list_record)->header.len), 1, input_file); if ((ret != 1) || ferror(input_file)) { lprintf(LOG_ERR, "Invalid Record Data!"); fclose(input_file); return ERROR_STATUS; } if (verbose > 0) printf("Record %d has length = %02x\n", record_count, (*list_record)->header.len); if (verbose > 1) { int i; printf("Type: %02x", (*list_record)->header.type); for (i = 0; i < ((*list_record)->header.len); i++) { if (!(i % 8)) { printf("\n0x%02x: ", i); } printf("%02x ", (*list_record)->data[i]); } printf("\n\n"); } ipmi_ek_add_record2list(list_record, list_head, list_last); /* mask the 8th bits to see if it is the last record */ last_record = ((*list_record)->header.format) & 0x80; if (last_record) { break; } record_count++; } fclose(input_file); return OK_STATUS; } /************************************************************************** * * Function name: ipmi_ek_add_record2list * * Description: this function adds a sigle FRU multi record to a linked list of * FRU multi record. * * Restriction: None * * Input/Output: record: a pointer to current record * list_head: a pointer to header of the list * list_last: a pointer to tale of the list * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_add_record2list(struct ipmi_ek_multi_header **record, struct ipmi_ek_multi_header **list_head, struct ipmi_ek_multi_header **list_last) { if (*list_head == NULL) { *list_head = *record; (*record)->prev = NULL; if (verbose > 2) { printf("Adding first record to list\n"); } } else { (*list_last)->next = *record; (*record)->prev = *list_last; if (verbose > 2) { printf("Add 1 record to list\n"); } } *list_last = *record; (*record)->next = NULL; } /************************************************************************** * * Function name: ipmi_ek_remove_record_from_list * * Description: this function removes a sigle FRU multi record from a linked * list of FRU multi record. * * Restriction: None * * Input/Output: record: a pointer to record to be deleted * list_head: a pointer to header of the list * list_last: a pointer to tale of the list * * Global: None * * Return: None * ***************************************************************************/ static void ipmi_ek_remove_record_from_list(struct ipmi_ek_multi_header *record, struct ipmi_ek_multi_header **list_head, struct ipmi_ek_multi_header **list_last) { if (record->prev == NULL) { *list_head = record->next; } else { record->prev->next = record->next; } if (record->next == NULL) { (*list_last) = record->prev; } else { record->next->prev = record->prev; } free(record); record = NULL; }