示例#1
0
///----------------------------------------------------------------------------------
// Populates various FRU areas
// @prereq : This must be called only after validating common header.
///----------------------------------------------------------------------------------
int ipmi_populate_fru_areas(uint8_t *fru_data, const size_t data_len,
                            fru_area_vec_t & fru_area_vec)
{
    size_t area_offset = 0;
    int rc = -1;

    // Now walk the common header and see if the file size has atleast the last
    // offset mentioned by the common_hdr. If the file size is less than the
    // offset of any if the fru areas mentioned in the common header, then we do
    // not have a complete file.
    for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
            fru_entry < (sizeof(struct common_header) -2); fru_entry++)
    {
        rc = -1;
        // Actual offset in the payload is the offset mentioned in common header
        // multipled by 8. Common header is always the first 8 bytes.
        area_offset = fru_data[fru_entry] * IPMI_EIGHT_BYTES;
        if(area_offset && (data_len < (area_offset + 2)))
        {
            // Our file size is less than what it needs to be. +2 because we are
            // using area len that is at 2 byte off area_offset
            fprintf(stderr, "fru file is incomplete. Size:[%d]\n",data_len);
            return rc;
        }
        else if(area_offset)
        {
            // Read 2 bytes to know the actual size of area.
            uint8_t area_hdr[2] = {0};
            memcpy(area_hdr, &((uint8_t *)fru_data)[area_offset], sizeof(area_hdr));

            // Size of this area will be the 2nd byte in the fru area header.
            size_t  area_len = area_hdr[1] * IPMI_EIGHT_BYTES;
            uint8_t area_data[area_len] = {0};

            printf("fru data size:[%d], area offset:[%d], area_size:[%d]\n",
                    data_len, area_offset, area_len);

            // See if we really have that much buffer. We have area offset amd
            // from there, the actual len.
            if(data_len < (area_len + area_offset))
            {
                fprintf(stderr, "Incomplete Fru file.. Size:[%d]\n",data_len);
                return rc;
            }

            // Save off the data.
            memcpy(area_data, &((uint8_t *)fru_data)[area_offset], area_len);

            // Validate the crc
            rc = verify_fru_data(area_data, area_len);
            if(rc < 0)
            {
                fprintf(stderr, "Error validating fru area. offset:[%d]\n",area_offset);
                return rc;
            }
            else
            {
                printf("Successfully verified area checksum. offset:[%d]\n",area_offset);
            }

            // We already have a vector that is passed to us containing all
            // of the fields populated. Update the data portion now.
            for(auto& iter : fru_area_vec)
            {
                if((iter)->get_type() == get_fru_area_type(fru_entry))
                {
                    (iter)->set_data(area_data, area_len);
                }
            }
        } // If we have fru data present
    } // Walk common_hdr

    // Not all the fields will be populated in a fru data. Mostly all cases will
    // not have more than 2 or 3.
    fru_area_vec.erase(std::remove_if(fru_area_vec.begin(), fru_area_vec.end(),
                       remove_invalid_area), fru_area_vec.end());

    return EXIT_SUCCESS;
}
示例#2
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;
}