Example #1
0
///-----------------------------------------------------
// Accepts the filename and validates per IPMI FRU spec
//----------------------------------------------------
int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name,
                           sd_bus *bus_type, const bool bmc_fru)
{
    size_t data_len = 0;
    size_t bytes_read = 0;
    int rc = -1;

    // Vector that holds individual IPMI FRU AREAs. Although MULTI and INTERNAL
    // are not used, keeping it here for completeness.
    fru_area_vec_t fru_area_vec;
    std::vector<std::string> defined_fru_area;

    // BMC defines fru areas that should be present in Skeleton
    rc = get_defined_fru_area(bus_type, fruid, defined_fru_area);
    if(rc < 0)
    {
        fprintf(stderr, "ERROR: cannot get defined fru area\n");
        return rc;
    }
    for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
        fru_entry < (sizeof(struct common_header) -2); fru_entry++)
    {
        // Create an object and push onto a vector.
        std::unique_ptr<ipmi_fru> fru_area = std::make_unique<ipmi_fru>
                         (fruid, get_fru_area_type(fru_entry), bus_type, bmc_fru);

        // Physically being present
        bool present = access(fru_file_name, F_OK) == 0;
        fru_area->set_present(present);

        // Only setup dbus path for areas defined in BMC.
        // Otherwise Skeleton will report 'not found' error
        std::string fru_area_name = fru_area->get_name() + std::to_string(fruid);
        auto iter = std::find(defined_fru_area.begin(), defined_fru_area.end(),
                                  fru_area_name);
        if (iter != defined_fru_area.end())
        {
            fru_area->setup_sd_bus_paths();
        }
        fru_area_vec.emplace_back(std::move(fru_area));
    }

    FILE *fru_fp = fopen(fru_file_name,"rb");
    if(fru_fp == NULL)
    {
        fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
        perror("Error:");
        return cleanup_error(fru_fp, fru_area_vec);
    }

    // Get the size of the file to see if it meets minimum requirement
    if(fseek(fru_fp, 0, SEEK_END))
    {
        perror("Error:");
        return cleanup_error(fru_fp, fru_area_vec);
    }

    // Allocate a buffer to hold entire file content
    data_len = ftell(fru_fp);
    uint8_t fru_data[data_len] = {0};

    rewind(fru_fp);
    bytes_read = fread(fru_data, data_len, 1, fru_fp);
    if(bytes_read != 1)
    {
        fprintf(stderr, "Failed reading fru data. Bytes_read=[%d]\n",bytes_read);
        perror("Error:");
        return cleanup_error(fru_fp, fru_area_vec);
    }

    // We are done reading.
    fclose(fru_fp);
    fru_fp = NULL;

    rc = ipmi_validate_common_hdr(fru_data, data_len);
    if(rc < 0)
    {
        return cleanup_error(fru_fp, fru_area_vec);
    }

    // Now that we validated the common header, populate various fru sections if we have them here.
    rc = ipmi_populate_fru_areas(fru_data, data_len, fru_area_vec);
    if(rc < 0)
    {
        fprintf(stderr,"Populating FRU areas failed for:[%d]\n",fruid);
        return cleanup_error(fru_fp, fru_area_vec);
    }
    else
    {
        printf("SUCCESS: Populated FRU areas for:[%s]\n",fru_file_name);
    }

#ifdef __IPMI_DEBUG__
    for(auto& iter : fru_area_vec)
    {
        printf("FRU ID : [%d]\n",(iter)->get_fruid());
        printf("AREA NAME : [%s]\n",(iter)->get_name());
        printf("TYPE : [%d]\n",(iter)->get_type());
        printf("LEN : [%d]\n",(iter)->get_len());
        printf("BUS NAME : [%s]\n", (iter)->get_bus_name());
        printf("OBJ PATH : [%s]\n", (iter)->get_obj_path());
        printf("INTF NAME :[%s]\n", (iter)->get_intf_name());
    }
#endif

    // If the vector is populated with everything, then go ahead and update the
    // inventory.
    if(!(fru_area_vec.empty()))
    {

#ifdef __IPMI_DEBUG__
        printf("\n SIZE of vector is : [%d] \n",fru_area_vec.size());
#endif
        rc = ipmi_update_inventory(fru_area_vec);
        if(rc <0)
        {
            fprintf(stderr, "Error updating inventory\n");
        }
    }

    // we are done with all that we wanted to do. This will do the job of
    // calling any destructors too.
    fru_area_vec.clear();

    return rc;
}
//-------------------------------------------------------------------------
// Validates the CRC and if found good, calls fru areas parser and calls
// Inventory Dbus with the dictionary of Name:Value for updating. 
//-------------------------------------------------------------------------
int ipmi_validate_and_update_inventory(const uint8_t fruid, const uint8_t *fru_data)
{
    // Used for generic checksum calculation
    uint8_t checksum = 0;

    // This can point to any FRU entry.
    uint8_t fru_entry;

    // A generic offset locator for any FRU record.
    uint8_t area_offset = 0;

    // First 2 bytes in the record.
    uint8_t fru_area_hdr[2] = {0};

    // To hold info about individual FRU record areas.
    fru_area_t fru_area;

    // For parsing and updating Inventory.
    fru_area_vec_t fru_area_vec;

    int rc = 0;

    uint8_t common_hdr[sizeof(struct common_header)] = {0};
    memset(common_hdr, 0x0, sizeof(common_hdr));

    // Copy first 8 bytes to verify common header
    memcpy(common_hdr, fru_data, sizeof(common_hdr));

    // Validate for first byte to always have a value of [1]
    if(common_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
    {
        fprintf(stderr, "ERROR: Common Header entry_1:[0x%X] Invalid.\n",common_hdr[0]);
        return -1;
    }
    else
    {
        printf("SUCCESS: Validated [0x%X] in common header\n",common_hdr[0]);
    }

    // Validate the header checskum that is at last byte ( Offset: 7 )
    checksum = calculate_crc(common_hdr, sizeof(common_hdr)-1);
    if(checksum != common_hdr[IPMI_FRU_HDR_CRC_OFFSET])
    {
#ifdef __IPMI__DEBUG__
        fprintf(stderr, "ERROR: Common Header checksum mismatch."
                " Calculated:[0x%X], Embedded:[0x%X]\n", 
                checksum, common_hdr[IPMI_FRU_HDR_CRC_OFFSET]);
#endif    
        return -1;
    }
    else
    {
        printf("SUCCESS: Common Header checksum MATCH:[0x%X]\n",checksum);
    }

    //-------------------------------------------
    // TODO:  Add support for Multi Record later
    //-------------------------------------------

    //  Now start walking the common_hdr array that has offsets into other FRU
    //  record areas and validate those. Starting with second entry since the
    //  first one is always a [0x01]
    for(fru_entry = IPMI_FRU_INTERNAL_OFFSET; fru_entry < (sizeof(struct common_header) -2); fru_entry++)
    {
        // Offset is 'value given in' internal_offset * 8 from the START of
        // common header. So an an example, 01 00 00 00 01 00 00 fe has
        // product area set at the offset 01 * 8 --> 8 bytes from the START of
        // common header. That means, soon after the header checksum.
        area_offset = common_hdr[fru_entry] * IPMI_EIGHT_BYTES;
        
        if(area_offset)
        {
            memset((void *)&fru_area, 0x0, sizeof(fru_area_t));

            // Enumerated FRU area.
            fru_area.type = get_fru_area_type(fru_entry);

            // From start of fru header + record offset, copy 2 bytes.
            fru_area.offset = &((uint8_t *)fru_data)[area_offset];
            memcpy(fru_area_hdr, fru_area.offset, sizeof(fru_area_hdr));

            // A NON zero value means that the vpd packet has the data for that
            // area. err if first element in the record header is _not_ a [0x01].
            if(fru_area_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
            {
                fprintf(stderr, "ERROR: Unexpected :[0x%X] found at Record header\n",
                        fru_area_hdr[0]);

                // This vector by now may have had some entries. Since this is a
                // failure now, clear the state data.
                fru_area_vec.clear();
                return -1;
            }
            else
            {
                printf("SUCCESS: Validated [0x%X] in fru record:[%d] header\n",
                        fru_area_hdr[0],fru_entry);
            }

            // Read Length bytes ( makes a complete record read now )
            fru_area.len = fru_area_hdr[1] * IPMI_EIGHT_BYTES;
#ifdef __IPMI_DEBUG__
            printf("AREA NO[%d], SIZE = [%d]\n",fru_entry, fru_area.len);
#endif
            uint8_t fru_area_data[fru_area.len];
            memset(fru_area_data, 0x0, sizeof(fru_area_data));

            memmove(fru_area_data, fru_area.offset, sizeof(fru_area_data));

            // Calculate checksum (from offset -> (Length-1)).
            // All the bytes except the last byte( which is CRC :) ) will
            // participate in calculating the checksum.
            checksum = calculate_crc(fru_area_data, sizeof(fru_area_data)-1);

            // Verify the embedded checksum in last byte with calculated checksum
            // record_len -1 since length is some N but numbering is 0..N-1
            if(checksum != fru_area_data[fru_area.len-1])
            {
#ifdef __IPMI_DEBUG__
                fprintf(stderr, "ERROR: FRU Header checksum mismatch. "
                        " Calculated:[0x%X], Embedded:[0x%X]\n", 
                        checksum, fru_area_data[fru_area.len - 1]);
#endif
                // This vector by now may have had some entries. Since this is a
                // failure now, clear the state data.
                fru_area_vec.clear();
                return -1;
            }
            else
            {
                printf("SUCCESS: FRU Header checksum MATCH:[0x%X]\n",checksum);
            }

            // Everything is rihgt about this particular FRU record,
            fru_area_vec.push_back(fru_area);

            // Update the internal structure with info about this entry that is
            // needed while handling each areas.
        } // If the packet has data for a particular data record.
    } // End walking all the fru records.
        
    // If we reach here, then we have validated the crc for all the records and
    // time to call FRU area parser to get a Name:Value pair dictionary.
    // This will start iterating all over again on the buffer -BUT- now with the
    // job of taking each areas, getting it parsed and then updating the
    // DBUS.
        
    if(!(fru_area_vec.empty()))
    {
        rc =  ipmi_update_inventory(fruid, fru_data, fru_area_vec);
    }
 
    // We are done with this FRU write packet. 
    fru_area_vec.clear();

    return rc;
}