| #include "bios.hpp" |
| |
| #include "utils.hpp" |
| #include "xyz/openbmc_project/Common/error.hpp" |
| |
| #include <time.h> |
| |
| #include <array> |
| #include <boost/crc.hpp> |
| #include <chrono> |
| #include <ctime> |
| #include <filesystem> |
| #include <iostream> |
| #include <memory> |
| #include <numeric> |
| #include <stdexcept> |
| #include <string> |
| #include <variant> |
| #include <vector> |
| |
| namespace fs = std::filesystem; |
| using namespace pldm::responder::bios; |
| using namespace bios_parser; |
| |
| constexpr auto stringTableFile = "stringTable"; |
| constexpr auto attrTableFile = "attributeTable"; |
| constexpr auto attrValTableFile = "attributeValueTable"; |
| |
| namespace pldm |
| { |
| |
| using namespace sdbusplus::xyz::openbmc_project::Common::Error; |
| using EpochTimeUS = uint64_t; |
| using BIOSTableRow = std::vector<uint8_t>; |
| using BIOSJsonName = std::string; |
| |
| constexpr auto dbusProperties = "org.freedesktop.DBus.Properties"; |
| constexpr auto padChksumMax = 7; |
| |
| namespace responder |
| { |
| |
| namespace utils |
| { |
| |
| void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes, |
| uint8_t& hours, uint8_t& day, uint8_t& month, |
| uint16_t& year) |
| { |
| auto t = time_t(timeSec); |
| auto time = localtime(&t); |
| |
| seconds = pldm::utils::decimalToBcd(time->tm_sec); |
| minutes = pldm::utils::decimalToBcd(time->tm_min); |
| hours = pldm::utils::decimalToBcd(time->tm_hour); |
| day = pldm::utils::decimalToBcd(time->tm_mday); |
| month = pldm::utils::decimalToBcd(time->tm_mon + |
| 1); // The number of months in the range |
| // 0 to 11.PLDM expects range 1 to 12 |
| year = pldm::utils::decimalToBcd(time->tm_year + |
| 1900); // The number of years since 1900 |
| } |
| |
| std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours, |
| uint8_t day, uint8_t month, uint16_t year) |
| { |
| struct std::tm stm; |
| |
| stm.tm_year = year - 1900; |
| stm.tm_mon = month - 1; |
| stm.tm_mday = day; |
| stm.tm_hour = hours; |
| stm.tm_min = minutes; |
| stm.tm_sec = seconds; |
| stm.tm_isdst = -1; |
| |
| // It will get the time in seconds since |
| // Epoch, 1970.1.1 00:00:00 +0000,UTC. |
| return timegm(&stm); |
| } |
| |
| size_t getTableTotalsize(size_t sizeWithoutPad) |
| { |
| return sizeWithoutPad + pldm_bios_table_pad_checksum_size(sizeWithoutPad); |
| } |
| |
| void padAndChecksum(Table& table) |
| { |
| auto sizeWithoutPad = table.size(); |
| auto padAndChecksumSize = pldm_bios_table_pad_checksum_size(sizeWithoutPad); |
| table.resize(table.size() + padAndChecksumSize); |
| |
| pldm_bios_table_append_pad_checksum(table.data(), table.size(), |
| sizeWithoutPad); |
| } |
| |
| } // namespace utils |
| |
| namespace bios |
| { |
| |
| Handler::Handler() |
| { |
| try |
| { |
| fs::remove( |
| fs::path(std::string(BIOS_TABLES_DIR) + "/" + stringTableFile)); |
| fs::remove( |
| fs::path(std::string(BIOS_TABLES_DIR) + "/" + attrTableFile)); |
| fs::remove( |
| fs::path(std::string(BIOS_TABLES_DIR) + "/" + attrValTableFile)); |
| } |
| catch (const std::exception& e) |
| { |
| } |
| handlers.emplace(PLDM_SET_DATE_TIME, |
| [this](const pldm_msg* request, size_t payloadLength) { |
| return this->setDateTime(request, payloadLength); |
| }); |
| handlers.emplace(PLDM_GET_DATE_TIME, |
| [this](const pldm_msg* request, size_t payloadLength) { |
| return this->getDateTime(request, payloadLength); |
| }); |
| handlers.emplace(PLDM_GET_BIOS_TABLE, |
| [this](const pldm_msg* request, size_t payloadLength) { |
| return this->getBIOSTable(request, payloadLength); |
| }); |
| handlers.emplace(PLDM_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE, |
| [this](const pldm_msg* request, size_t payloadLength) { |
| return this->getBIOSAttributeCurrentValueByHandle( |
| request, payloadLength); |
| }); |
| handlers.emplace(PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE, |
| [this](const pldm_msg* request, size_t payloadLength) { |
| return this->setBIOSAttributeCurrentValue( |
| request, payloadLength); |
| }); |
| } |
| |
| Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/) |
| { |
| uint8_t seconds = 0; |
| uint8_t minutes = 0; |
| uint8_t hours = 0; |
| uint8_t day = 0; |
| uint8_t month = 0; |
| uint16_t year = 0; |
| |
| constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime"; |
| constexpr auto hostTimePath = "/xyz/openbmc_project/time/host"; |
| Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0); |
| auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| EpochTimeUS timeUsec; |
| |
| try |
| { |
| timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>( |
| hostTimePath, "Elapsed", timeInterface); |
| } |
| catch (const sdbusplus::exception::SdBusError& e) |
| { |
| std::cerr << "Error getting time, PATH=" << hostTimePath |
| << " TIME INTERACE=" << timeInterface << "\n"; |
| |
| return CmdHandler::ccOnlyResponse(request, PLDM_ERROR); |
| } |
| |
| uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>( |
| std::chrono::microseconds(timeUsec)) |
| .count(); |
| |
| pldm::responder::utils::epochToBCDTime(timeSec, seconds, minutes, hours, |
| day, month, year); |
| |
| auto rc = encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, |
| seconds, minutes, hours, day, month, |
| year, responsePtr); |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| |
| return response; |
| } |
| |
| Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength) |
| { |
| uint8_t seconds = 0; |
| uint8_t minutes = 0; |
| uint8_t hours = 0; |
| uint8_t day = 0; |
| uint8_t month = 0; |
| uint16_t year = 0; |
| std::time_t timeSec; |
| |
| constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime"; |
| constexpr auto setTimePath = "/xyz/openbmc_project/time/host"; |
| constexpr auto timeSetPro = "Elapsed"; |
| |
| auto rc = decode_set_date_time_req(request, payloadLength, &seconds, |
| &minutes, &hours, &day, &month, &year); |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day, |
| month, year); |
| uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>( |
| std::chrono::seconds(timeSec)) |
| .count(); |
| std::variant<uint64_t> value{timeUsec}; |
| try |
| { |
| pldm::utils::DBusHandler().setDbusProperty(setTimePath, timeSetPro, |
| setTimeInterface, value); |
| } |
| catch (std::exception& e) |
| { |
| |
| std::cerr << "Error Setting time,PATH=" << setTimePath |
| << "TIME INTERFACE=" << setTimeInterface |
| << "ERROR=" << e.what() << "\n"; |
| |
| return ccOnlyResponse(request, PLDM_ERROR); |
| } |
| |
| return ccOnlyResponse(request, PLDM_SUCCESS); |
| } |
| |
| /** @brief Construct the BIOS string table |
| * |
| * @param[in,out] biosStringTable - the string table |
| * @param[in] request - Request message |
| */ |
| Response getBIOSStringTable(BIOSTable& biosStringTable, const pldm_msg* request) |
| |
| { |
| Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, |
| 0); |
| auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| if (!biosStringTable.isEmpty()) |
| { |
| auto rc = encode_get_bios_table_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, |
| 0, /* next transfer handle */ |
| PLDM_START_AND_END, nullptr, response.size(), |
| responsePtr); // filling up the header here |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| |
| biosStringTable.load(response); |
| return response; |
| } |
| auto biosStrings = bios_parser::getStrings(); |
| std::sort(biosStrings.begin(), biosStrings.end()); |
| // remove all duplicate strings received from bios json |
| biosStrings.erase(std::unique(biosStrings.begin(), biosStrings.end()), |
| biosStrings.end()); |
| |
| size_t sizeWithoutPad = std::accumulate( |
| biosStrings.begin(), biosStrings.end(), 0, |
| [](size_t sum, const std::string& elem) { |
| return sum + |
| pldm_bios_table_string_entry_encode_length(elem.length()); |
| }); |
| |
| Table stringTable; |
| stringTable.reserve( |
| pldm::responder::utils::getTableTotalsize(sizeWithoutPad)); |
| |
| stringTable.resize(sizeWithoutPad); |
| auto tablePtr = stringTable.data(); |
| for (const auto& elem : biosStrings) |
| { |
| auto entry_length = |
| pldm_bios_table_string_entry_encode_length(elem.length()); |
| pldm_bios_table_string_entry_encode(tablePtr, sizeWithoutPad, |
| elem.c_str(), elem.length()); |
| tablePtr += entry_length; |
| sizeWithoutPad -= entry_length; |
| } |
| |
| pldm::responder::utils::padAndChecksum(stringTable); |
| biosStringTable.store(stringTable); |
| response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + |
| stringTable.size(), |
| 0); |
| responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| auto rc = encode_get_bios_table_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */, |
| PLDM_START_AND_END, stringTable.data(), response.size(), responsePtr); |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| |
| return response; |
| } |
| |
| /** @brief Find the string handle from the BIOS string table given the name |
| * |
| * @param[in] name - name of the BIOS string |
| * @param[in] biosStringTable - the string table |
| * @return - uint16_t - handle of the string |
| */ |
| StringHandle findStringHandle(const std::string& name, |
| const BIOSTable& biosStringTable) |
| { |
| Table table; |
| biosStringTable.load(table); |
| auto stringEntry = pldm_bios_table_string_find_by_string( |
| table.data(), table.size(), name.c_str()); |
| if (stringEntry == nullptr) |
| { |
| std::cerr << "Reached end of BIOS string table,did not find the " |
| << "handle for the string, STRING=" << name.c_str() << "\n"; |
| throw InternalFailure(); |
| } |
| |
| return pldm_bios_table_string_entry_decode_handle(stringEntry); |
| } |
| |
| /** @brief Find the string name from the BIOS string table for a string handle |
| * |
| * @param[in] stringHdl - string handle |
| * @param[in] biosStringTable - the string table |
| * |
| * @return - std::string - name of the corresponding BIOS string |
| */ |
| std::string findStringName(StringHandle stringHdl, |
| const BIOSTable& biosStringTable) |
| { |
| Table table; |
| biosStringTable.load(table); |
| auto stringEntry = pldm_bios_table_string_find_by_handle( |
| table.data(), table.size(), stringHdl); |
| if (stringEntry == nullptr) |
| { |
| std::cerr << "Reached end of BIOS string table,did not find " |
| << "string name for handle, STRING_HANDLE=" << stringHdl |
| << "\n"; |
| throw InternalFailure(); |
| } |
| auto strLength = |
| pldm_bios_table_string_entry_decode_string_length(stringEntry); |
| std::vector<char> buffer(strLength + 1); |
| pldm_bios_table_string_entry_decode_string(stringEntry, buffer.data(), |
| buffer.size()); |
| |
| return std::string(buffer.data(), buffer.data() + strLength); |
| } |
| |
| namespace bios_type_enum |
| { |
| |
| using namespace bios_parser::bios_enum; |
| |
| /** @brief Find the indices into the array of the possible values of string |
| * handles for the current values.This is used in attribute value table |
| * |
| * @param[in] possiVals - vector of string handles comprising all the possible |
| * values for an attribute |
| * @param[in] currVals - vector of strings comprising all current values |
| * for an attribute |
| * @param[in] biosStringTable - the string table |
| * |
| * @return - std::vector<uint8_t> - indices into the array of the possible |
| * values of string handles |
| */ |
| std::vector<uint8_t> findStrIndices(PossibleValuesByHandle possiVals, |
| CurrentValues currVals, |
| const BIOSTable& biosStringTable) |
| { |
| std::vector<uint8_t> stringIndices; |
| |
| for (const auto& currVal : currVals) |
| { |
| StringHandle curHdl; |
| try |
| { |
| curHdl = findStringHandle(currVal, biosStringTable); |
| } |
| catch (InternalFailure& e) |
| { |
| std::cerr << "Exception fetching handle for the string, STRING=" |
| << currVal.c_str() << "\n"; |
| continue; |
| } |
| |
| uint8_t i = 0; |
| for (auto possiHdl : possiVals) |
| { |
| if (possiHdl == curHdl) |
| { |
| stringIndices.push_back(i); |
| break; |
| } |
| i++; |
| } |
| } |
| return stringIndices; |
| } |
| |
| /** @brief Find the indices into the array of the possible values of string |
| * handles for the default values. This is used in attribute table |
| * |
| * @param[in] possiVals - vector of strings comprising all the possible values |
| * for an attribute |
| * @param[in] defVals - vector of strings comprising all the default values |
| * for an attribute |
| * @return - std::vector<uint8_t> - indices into the array of the possible |
| * values of string |
| */ |
| std::vector<uint8_t> findDefaultValHandle(const PossibleValues& possiVals, |
| const DefaultValues& defVals) |
| { |
| std::vector<uint8_t> defHdls; |
| for (const auto& defs : defVals) |
| { |
| auto index = std::lower_bound(possiVals.begin(), possiVals.end(), defs); |
| if (index != possiVals.end()) |
| { |
| defHdls.push_back(index - possiVals.begin()); |
| } |
| } |
| |
| return defHdls; |
| } |
| |
| /** @brief Construct the attibute table for BIOS type Enumeration and |
| * Enumeration ReadOnly |
| * @param[in] biosStringTable - the string table |
| * @param[in] biosJsonDir - path where the BIOS json files are present |
| * @param[in,out] attributeTable - the attribute table |
| * |
| */ |
| void constructAttrTable(const BIOSTable& biosStringTable, Table& attributeTable) |
| { |
| const auto& attributeMap = getValues(); |
| StringHandle strHandle; |
| |
| for (const auto& [key, value] : attributeMap) |
| { |
| try |
| { |
| strHandle = findStringHandle(key, biosStringTable); |
| } |
| catch (InternalFailure& e) |
| { |
| std::cerr << "Could not find handle for BIOS string, ATTRIBUTE=" |
| << key.c_str() << "\n"; |
| continue; |
| } |
| bool readOnly = (std::get<0>(value)); |
| PossibleValues possiVals = std::get<1>(value); |
| DefaultValues defVals = std::get<2>(value); |
| // both the possible and default values are stored in sorted manner to |
| // ease in fetching back/comparison |
| std::sort(possiVals.begin(), possiVals.end()); |
| std::sort(defVals.begin(), defVals.end()); |
| |
| std::vector<StringHandle> possiValsByHdl; |
| for (const auto& elem : possiVals) |
| { |
| try |
| { |
| auto hdl = findStringHandle(elem, biosStringTable); |
| possiValsByHdl.push_back(std::move(hdl)); |
| } |
| catch (InternalFailure& e) |
| { |
| std::cerr << "Could not find handle for BIOS string, STRING=" |
| << elem.c_str() << "\n"; |
| continue; |
| } |
| } |
| auto defValsByHdl = findDefaultValHandle(possiVals, defVals); |
| auto entryLength = pldm_bios_table_attr_entry_enum_encode_length( |
| possiValsByHdl.size(), defValsByHdl.size()); |
| |
| auto attrTableSize = attributeTable.size(); |
| attributeTable.resize(attrTableSize + entryLength, 0); |
| struct pldm_bios_table_attr_entry_enum_info info = { |
| strHandle, |
| readOnly, |
| (uint8_t)possiValsByHdl.size(), |
| possiValsByHdl.data(), |
| (uint8_t)defValsByHdl.size(), |
| defValsByHdl.data(), |
| }; |
| pldm_bios_table_attr_entry_enum_encode( |
| attributeTable.data() + attrTableSize, entryLength, &info); |
| } |
| } |
| |
| void constructAttrValueEntry( |
| const struct pldm_bios_attr_table_entry* attrTableEntry, |
| const std::string& attrName, const BIOSTable& biosStringTable, |
| Table& attrValueTable) |
| { |
| CurrentValues currVals; |
| try |
| { |
| currVals = getAttrValue(attrName); |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << "getAttrValue returned error for attribute, NAME=" |
| << attrName.c_str() << " ERROR=" << e.what() << "\n"; |
| return; |
| } |
| uint8_t pv_num = |
| pldm_bios_table_attr_entry_enum_decode_pv_num(attrTableEntry); |
| PossibleValuesByHandle pvHdls(pv_num, 0); |
| pldm_bios_table_attr_entry_enum_decode_pv_hdls(attrTableEntry, |
| pvHdls.data(), pv_num); |
| std::sort(currVals.begin(), currVals.end()); |
| |
| auto currValStrIndices = findStrIndices(pvHdls, currVals, biosStringTable); |
| |
| auto entryLength = pldm_bios_table_attr_value_entry_encode_enum_length( |
| currValStrIndices.size()); |
| auto tableSize = attrValueTable.size(); |
| attrValueTable.resize(tableSize + entryLength); |
| pldm_bios_table_attr_value_entry_encode_enum( |
| attrValueTable.data() + tableSize, entryLength, |
| attrTableEntry->attr_handle, attrTableEntry->attr_type, |
| currValStrIndices.size(), currValStrIndices.data()); |
| } |
| |
| } // end namespace bios_type_enum |
| |
| namespace bios_type_string |
| { |
| |
| using namespace bios_parser::bios_string; |
| |
| /** @brief Construct the attibute table for BIOS type String and |
| * String ReadOnly |
| * @param[in] biosStringTable - the string table |
| * @param[in] biosJsonDir - path where the BIOS json files are present |
| * @param[in,out] attributeTable - the attribute table |
| * |
| */ |
| void constructAttrTable(const BIOSTable& biosStringTable, Table& attributeTable) |
| { |
| const auto& attributeMap = getValues(); |
| StringHandle strHandle; |
| for (const auto& [key, value] : attributeMap) |
| { |
| try |
| { |
| strHandle = findStringHandle(key, biosStringTable); |
| } |
| catch (InternalFailure& e) |
| { |
| std::cerr << "Could not find handle for BIOS string, ATTRIBUTE=" |
| << key.c_str() << "\n"; |
| continue; |
| } |
| |
| const auto& [readOnly, strType, minStrLen, maxStrLen, defaultStrLen, |
| defaultStr] = value; |
| auto entryLength = |
| pldm_bios_table_attr_entry_string_encode_length(defaultStrLen); |
| |
| struct pldm_bios_table_attr_entry_string_info info = { |
| strHandle, readOnly, strType, minStrLen, |
| maxStrLen, defaultStrLen, defaultStr.data(), |
| }; |
| auto attrTableSize = attributeTable.size(); |
| attributeTable.resize(attrTableSize + entryLength, 0); |
| pldm_bios_table_attr_entry_string_encode( |
| attributeTable.data() + attrTableSize, entryLength, &info); |
| } |
| } |
| |
| void constructAttrValueEntry(const pldm_bios_attr_table_entry* attrTableEntry, |
| const std::string& attrName, |
| const BIOSTable& biosStringTable, |
| Table& attrValueTable) |
| { |
| std::ignore = biosStringTable; |
| std::string currStr; |
| uint16_t currStrLen = 0; |
| try |
| { |
| currStr = getAttrValue(attrName); |
| currStrLen = currStr.size(); |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << "getAttrValue returned error for attribute, NAME=" |
| << attrName.c_str() << " ERROR=" << e.what() << "\n"; |
| return; |
| } |
| auto entryLength = |
| pldm_bios_table_attr_value_entry_encode_string_length(currStrLen); |
| auto tableSize = attrValueTable.size(); |
| attrValueTable.resize(tableSize + entryLength); |
| pldm_bios_table_attr_value_entry_encode_string( |
| attrValueTable.data() + tableSize, entryLength, |
| attrTableEntry->attr_handle, attrTableEntry->attr_type, currStrLen, |
| currStr.c_str()); |
| } |
| |
| } // end namespace bios_type_string |
| |
| namespace bios_type_integer |
| { |
| |
| using namespace bios_parser::bios_integer; |
| |
| /** @brief Construct the attibute table for BIOS type Integer and |
| * Integer ReadOnly |
| * @param[in] biosStringTable - the string table |
| * @param[in,out] attributeTable - the attribute table |
| * |
| */ |
| void constructAttrTable(const BIOSTable& biosStringTable, Table& attributeTable) |
| { |
| const auto& attributeMap = getValues(); |
| StringHandle strHandle; |
| for (const auto& [key, value] : attributeMap) |
| { |
| try |
| { |
| strHandle = findStringHandle(key, biosStringTable); |
| } |
| catch (InternalFailure& e) |
| { |
| std::cerr << "Could not find handle for BIOS string, ATTRIBUTE=" |
| << key.c_str() << "\n"; |
| continue; |
| } |
| |
| const auto& [readOnly, lowerBound, upperBound, scalarIncrement, |
| defaultValue] = value; |
| auto entryLength = pldm_bios_table_attr_entry_integer_encode_length(); |
| |
| struct pldm_bios_table_attr_entry_integer_info info = { |
| strHandle, readOnly, lowerBound, |
| upperBound, scalarIncrement, defaultValue, |
| }; |
| auto attrTableSize = attributeTable.size(); |
| attributeTable.resize(attrTableSize + entryLength, 0); |
| pldm_bios_table_attr_entry_integer_encode( |
| attributeTable.data() + attrTableSize, entryLength, &info); |
| } |
| } |
| |
| void constructAttrValueEntry(const pldm_bios_attr_table_entry* attrTableEntry, |
| const std::string& attrName, |
| const BIOSTable& biosStringTable, |
| Table& attrValueTable) |
| { |
| std::ignore = biosStringTable; |
| uint64_t currentValue; |
| try |
| { |
| currentValue = getAttrValue(attrName); |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << "Failed to get attribute value, NAME=" << attrName.c_str() |
| << " ERROR=" << e.what() << "\n"; |
| return; |
| } |
| auto entryLength = pldm_bios_table_attr_value_entry_encode_integer_length(); |
| auto tableSize = attrValueTable.size(); |
| attrValueTable.resize(tableSize + entryLength); |
| pldm_bios_table_attr_value_entry_encode_integer( |
| attrValueTable.data() + tableSize, entryLength, |
| attrTableEntry->attr_handle, attrTableEntry->attr_type, currentValue); |
| } |
| |
| } // namespace bios_type_integer |
| |
| void traverseBIOSAttrTable(const Table& biosAttrTable, |
| AttrTableEntryHandler handler) |
| { |
| std::unique_ptr<pldm_bios_table_iter, decltype(&pldm_bios_table_iter_free)> |
| iter(pldm_bios_table_iter_create(biosAttrTable.data(), |
| biosAttrTable.size(), |
| PLDM_BIOS_ATTR_TABLE), |
| pldm_bios_table_iter_free); |
| while (!pldm_bios_table_iter_is_end(iter.get())) |
| { |
| auto table_entry = pldm_bios_table_iter_attr_entry_value(iter.get()); |
| try |
| { |
| handler(table_entry); |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << "handler fails when traversing BIOSAttrTable, ERROR=" |
| << e.what() << "\n"; |
| } |
| pldm_bios_table_iter_next(iter.get()); |
| } |
| } |
| |
| using typeHandler = std::function<void(const BIOSTable& biosStringTable, |
| Table& attributeTable)>; |
| std::map<BIOSJsonName, typeHandler> attrTypeHandlers{ |
| {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable}, |
| {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable}, |
| {bios_parser::bIOSIntegerJson, bios_type_integer::constructAttrTable}, |
| }; |
| |
| /** @brief Construct the BIOS attribute table |
| * |
| * @param[in,out] biosAttributeTable - the attribute table |
| * @param[in] biosStringTable - the string table |
| * @param[in] biosJsonDir - path where the BIOS json files are present |
| * @param[in] request - Request message |
| */ |
| Response getBIOSAttributeTable(BIOSTable& biosAttributeTable, |
| const BIOSTable& biosStringTable, |
| const char* biosJsonDir, const pldm_msg* request) |
| { |
| Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, |
| 0); |
| auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| uint32_t nxtTransferHandle = 0; |
| uint8_t transferFlag = PLDM_START_AND_END; |
| |
| if (biosAttributeTable.isEmpty()) |
| { // no persisted table, constructing fresh table and response |
| Table attributeTable; |
| fs::path dir(biosJsonDir); |
| |
| for (auto it = attrTypeHandlers.begin(); it != attrTypeHandlers.end(); |
| it++) |
| { |
| fs::path file = dir / it->first; |
| if (fs::exists(file)) |
| { |
| it->second(biosStringTable, attributeTable); |
| } |
| } |
| |
| if (attributeTable.empty()) |
| { // no available json file is found |
| return CmdHandler::ccOnlyResponse(request, |
| PLDM_BIOS_TABLE_UNAVAILABLE); |
| } |
| pldm::responder::utils::padAndChecksum(attributeTable); |
| biosAttributeTable.store(attributeTable); |
| response.resize(sizeof(pldm_msg_hdr) + |
| PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + |
| attributeTable.size()); |
| responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| |
| auto rc = encode_get_bios_table_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, nxtTransferHandle, |
| transferFlag, attributeTable.data(), response.size(), responsePtr); |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| } |
| else |
| { // persisted table present, constructing response |
| auto rc = encode_get_bios_table_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, nxtTransferHandle, |
| transferFlag, nullptr, response.size(), |
| responsePtr); // filling up the header here |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| biosAttributeTable.load(response); |
| } |
| |
| return response; |
| } |
| |
| using AttrValTableEntryConstructHandler = |
| std::function<void(const struct pldm_bios_attr_table_entry* tableEntry, |
| const std::string& attrName, |
| const BIOSTable& biosStringTable, Table& table)>; |
| |
| using AttrType = uint8_t; |
| const std::map<AttrType, AttrValTableEntryConstructHandler> |
| AttrValTableConstructMap{ |
| {PLDM_BIOS_STRING, bios_type_string::constructAttrValueEntry}, |
| {PLDM_BIOS_STRING_READ_ONLY, bios_type_string::constructAttrValueEntry}, |
| {PLDM_BIOS_ENUMERATION, bios_type_enum::constructAttrValueEntry}, |
| {PLDM_BIOS_ENUMERATION_READ_ONLY, |
| bios_type_enum::constructAttrValueEntry}, |
| {PLDM_BIOS_INTEGER, bios_type_integer::constructAttrValueEntry}, |
| {PLDM_BIOS_INTEGER_READ_ONLY, |
| bios_type_integer::constructAttrValueEntry}, |
| }; |
| |
| void constructAttrValueTableEntry( |
| const struct pldm_bios_attr_table_entry* attrEntry, |
| const BIOSTable& biosStringTable, Table& attributeValueTable) |
| { |
| auto attrName = findStringName(attrEntry->string_handle, biosStringTable); |
| if (attrName.empty()) |
| { |
| std::cerr << "invalid string handle, STRING_HANDLE=" |
| << attrEntry->string_handle << "\n"; |
| return; |
| } |
| |
| AttrValTableConstructMap.at(attrEntry->attr_type)( |
| attrEntry, attrName, biosStringTable, attributeValueTable); |
| } |
| |
| /** @brief Construct the BIOS attribute value table |
| * |
| * @param[in,out] biosAttributeValueTable - the attribute value table |
| * @param[in] biosAttributeTable - the attribute table |
| * @param[in] biosStringTable - the string table |
| * @param[in] request - Request message |
| */ |
| Response getBIOSAttributeValueTable(BIOSTable& biosAttributeValueTable, |
| const BIOSTable& biosAttributeTable, |
| const BIOSTable& biosStringTable, |
| const pldm_msg* request) |
| { |
| Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, |
| 0); |
| auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| uint32_t nxtTransferHandle = 0; |
| uint8_t transferFlag = PLDM_START_AND_END; |
| |
| if (!biosAttributeValueTable.isEmpty()) |
| { |
| auto rc = encode_get_bios_table_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, nxtTransferHandle, |
| transferFlag, nullptr, response.size(), |
| responsePtr); // filling up the header here |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| |
| biosAttributeValueTable.load(response); |
| return response; |
| } |
| |
| Table attributeValueTable; |
| Table attributeTable; |
| biosAttributeTable.load(attributeTable); |
| traverseBIOSAttrTable( |
| attributeTable, |
| [&biosStringTable, &attributeValueTable]( |
| const struct pldm_bios_attr_table_entry* tableEntry) { |
| constructAttrValueTableEntry(tableEntry, biosStringTable, |
| attributeValueTable); |
| }); |
| if (attributeValueTable.empty()) |
| { |
| return CmdHandler::ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); |
| } |
| pldm::responder::utils::padAndChecksum(attributeValueTable); |
| biosAttributeValueTable.store(attributeValueTable); |
| |
| response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + |
| attributeValueTable.size()); |
| responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| auto rc = encode_get_bios_table_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, nxtTransferHandle, transferFlag, |
| attributeValueTable.data(), response.size(), responsePtr); |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| |
| return response; |
| } |
| |
| Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength) |
| { |
| fs::create_directory(BIOS_TABLES_DIR); |
| auto response = internal::buildBIOSTables(request, payloadLength, |
| BIOS_JSONS_DIR, BIOS_TABLES_DIR); |
| |
| return response; |
| } |
| |
| Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request, |
| size_t payloadLength) |
| { |
| uint32_t transferHandle; |
| uint8_t transferOpFlag; |
| uint16_t attributeHandle; |
| |
| auto rc = decode_get_bios_attribute_current_value_by_handle_req( |
| request, payloadLength, &transferHandle, &transferOpFlag, |
| &attributeHandle); |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| |
| fs::path tablesPath(BIOS_TABLES_DIR); |
| auto stringTablePath = tablesPath / stringTableFile; |
| BIOSTable biosStringTable(stringTablePath.c_str()); |
| auto attrTablePath = tablesPath / attrTableFile; |
| BIOSTable biosAttributeTable(attrTablePath.c_str()); |
| if (biosAttributeTable.isEmpty() || biosStringTable.isEmpty()) |
| { |
| return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); |
| } |
| |
| auto attrValueTablePath = tablesPath / attrValTableFile; |
| BIOSTable biosAttributeValueTable(attrValueTablePath.c_str()); |
| |
| if (biosAttributeValueTable.isEmpty()) |
| { |
| Table attributeValueTable; |
| Table attributeTable; |
| biosAttributeTable.load(attributeTable); |
| traverseBIOSAttrTable( |
| attributeTable, |
| [&biosStringTable, &attributeValueTable]( |
| const struct pldm_bios_attr_table_entry* tableEntry) { |
| constructAttrValueTableEntry(tableEntry, biosStringTable, |
| attributeValueTable); |
| }); |
| if (attributeValueTable.empty()) |
| { |
| return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); |
| } |
| pldm::responder::utils::padAndChecksum(attributeValueTable); |
| biosAttributeValueTable.store(attributeValueTable); |
| } |
| |
| Response table; |
| biosAttributeValueTable.load(table); |
| |
| auto entry = pldm_bios_table_attr_value_find_by_handle( |
| table.data(), table.size(), attributeHandle); |
| if (entry == nullptr) |
| { |
| return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE); |
| } |
| |
| auto entryLength = pldm_bios_table_attr_value_entry_length(entry); |
| Response response(sizeof(pldm_msg_hdr) + |
| PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES + |
| entryLength, |
| 0); |
| auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| rc = encode_get_bios_current_value_by_handle_resp( |
| request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END, |
| reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr); |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| |
| return response; |
| } |
| |
| Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request, |
| size_t payloadLength) |
| { |
| uint32_t transferHandle; |
| uint8_t transferOpFlag; |
| variable_field attributeField; |
| |
| auto rc = decode_set_bios_attribute_current_value_req( |
| request, payloadLength, &transferHandle, &transferOpFlag, |
| &attributeField); |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| |
| fs::path tablesPath(BIOS_TABLES_DIR); |
| auto stringTablePath = tablesPath / stringTableFile; |
| BIOSStringTable biosStringTable(stringTablePath.c_str()); |
| auto attrTablePath = tablesPath / attrTableFile; |
| BIOSTable biosAttributeTable(attrTablePath.c_str()); |
| auto attrValueTablePath = tablesPath / attrValTableFile; |
| BIOSTable biosAttributeValueTable(attrValueTablePath.c_str()); |
| // TODO: Construct attribute value table if it's empty. (another commit) |
| |
| Response srcTable; |
| biosAttributeValueTable.load(srcTable); |
| |
| // Replace the old attribute with the new attribute, the size of table will |
| // change: |
| // sizeof(newTableBuffer) = srcTableSize + sizeof(newAttribute) - |
| // sizeof(oldAttribute) + pad(4-byte alignment, max = |
| // 3) |
| // For simplicity, we use |
| // sizeof(newTableBuffer) = srcTableSize + sizeof(newAttribute) + 3 |
| size_t destBufferLength = srcTable.size() + attributeField.length + 3; |
| Response destTable(destBufferLength); |
| size_t destTableLen = destTable.size(); |
| |
| rc = pldm_bios_table_attr_value_copy_and_update( |
| srcTable.data(), srcTable.size(), destTable.data(), &destTableLen, |
| attributeField.ptr, attributeField.length); |
| destTable.resize(destTableLen); |
| |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| |
| rc = setAttributeValueOnDbus(&attributeField, biosAttributeTable, |
| biosStringTable); |
| if (rc != PLDM_SUCCESS) |
| { |
| return ccOnlyResponse(request, rc); |
| } |
| |
| biosAttributeValueTable.store(destTable); |
| |
| return ccOnlyResponse(request, PLDM_SUCCESS); |
| } |
| |
| namespace internal |
| { |
| |
| Response buildBIOSTables(const pldm_msg* request, size_t payloadLength, |
| const char* biosJsonDir, const char* biosTablePath) |
| { |
| Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, |
| 0); |
| |
| if (setupConfig(biosJsonDir) != 0) |
| { |
| return CmdHandler::ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); |
| } |
| |
| uint32_t transferHandle{}; |
| uint8_t transferOpFlag{}; |
| uint8_t tableType{}; |
| |
| auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle, |
| &transferOpFlag, &tableType); |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| |
| BIOSTable biosStringTable( |
| (std::string(biosTablePath) + "/" + stringTableFile).c_str()); |
| BIOSTable biosAttributeTable( |
| (std::string(biosTablePath) + "/" + attrTableFile).c_str()); |
| BIOSTable biosAttributeValueTable( |
| (std::string(biosTablePath) + "/" + attrValTableFile).c_str()); |
| switch (tableType) |
| { |
| case PLDM_BIOS_STRING_TABLE: |
| |
| response = getBIOSStringTable(biosStringTable, request); |
| break; |
| case PLDM_BIOS_ATTR_TABLE: |
| |
| if (biosStringTable.isEmpty()) |
| { |
| rc = PLDM_BIOS_TABLE_UNAVAILABLE; |
| } |
| else |
| { |
| response = getBIOSAttributeTable( |
| biosAttributeTable, biosStringTable, biosJsonDir, request); |
| } |
| break; |
| case PLDM_BIOS_ATTR_VAL_TABLE: |
| if (biosAttributeTable.isEmpty() || biosStringTable.isEmpty()) |
| { |
| rc = PLDM_BIOS_TABLE_UNAVAILABLE; |
| } |
| else |
| { |
| response = getBIOSAttributeValueTable(biosAttributeValueTable, |
| biosAttributeTable, |
| biosStringTable, request); |
| } |
| break; |
| default: |
| rc = PLDM_INVALID_BIOS_TABLE_TYPE; |
| break; |
| } |
| |
| if (rc != PLDM_SUCCESS) |
| { |
| return CmdHandler::ccOnlyResponse(request, rc); |
| } |
| |
| return response; |
| } |
| |
| } // namespace internal |
| |
| } // namespace bios |
| } // namespace responder |
| } // namespace pldm |