xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 79c37f11)
1 #include "bios.hpp"
2 
3 #include "libpldmresponder/utils.hpp"
4 #include "registration.hpp"
5 #include "xyz/openbmc_project/Common/error.hpp"
6 
7 #include <array>
8 #include <boost/crc.hpp>
9 #include <chrono>
10 #include <ctime>
11 #include <memory>
12 #include <numeric>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <phosphor-logging/log.hpp>
15 #include <stdexcept>
16 #include <string>
17 #include <variant>
18 #include <vector>
19 
20 using namespace pldm::responder::bios;
21 using namespace bios_parser;
22 
23 namespace pldm
24 {
25 
26 using namespace phosphor::logging;
27 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
28 using EpochTimeUS = uint64_t;
29 using BIOSTableRow = std::vector<uint8_t>;
30 using BIOSJsonName = std::string;
31 
32 constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
33 constexpr auto padChksumMax = 7;
34 
35 namespace responder
36 {
37 
38 namespace utils
39 {
40 
41 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
42                     uint8_t& hours, uint8_t& day, uint8_t& month,
43                     uint16_t& year)
44 {
45     auto t = time_t(timeSec);
46     auto time = localtime(&t);
47 
48     seconds = decimalToBcd(time->tm_sec);
49     minutes = decimalToBcd(time->tm_min);
50     hours = decimalToBcd(time->tm_hour);
51     day = decimalToBcd(time->tm_mday);
52     month =
53         decimalToBcd(time->tm_mon + 1); // The number of months in the range
54                                         // 0 to 11.PLDM expects range 1 to 12
55     year = decimalToBcd(time->tm_year + 1900); // The number of years since 1900
56 }
57 
58 size_t getTableTotalsize(size_t sizeWithoutPad)
59 {
60     return sizeWithoutPad + pldm_bios_table_pad_checksum_size(sizeWithoutPad);
61 }
62 
63 void padAndChecksum(Table& table)
64 {
65     auto sizeWithoutPad = table.size();
66     auto padAndChecksumSize = pldm_bios_table_pad_checksum_size(sizeWithoutPad);
67     table.resize(table.size() + padAndChecksumSize);
68 
69     pldm_bios_table_append_pad_checksum(table.data(), table.size(),
70                                         sizeWithoutPad);
71 }
72 
73 } // namespace utils
74 
75 Response getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
76 {
77     uint8_t seconds = 0;
78     uint8_t minutes = 0;
79     uint8_t hours = 0;
80     uint8_t day = 0;
81     uint8_t month = 0;
82     uint16_t year = 0;
83 
84     constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
85     constexpr auto hostTimePath = "/xyz/openbmc_project/time/host";
86     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
87     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
88     std::variant<EpochTimeUS> value;
89 
90     auto bus = sdbusplus::bus::new_default();
91     try
92     {
93         auto service = getService(bus, hostTimePath, timeInterface);
94 
95         auto method = bus.new_method_call(service.c_str(), hostTimePath,
96                                           dbusProperties, "Get");
97         method.append(timeInterface, "Elapsed");
98 
99         auto reply = bus.call(method);
100         reply.read(value);
101     }
102 
103     catch (std::exception& e)
104     {
105         log<level::ERR>("Error getting time", entry("PATH=%s", hostTimePath),
106                         entry("TIME INTERACE=%s", timeInterface));
107 
108         encode_get_date_time_resp(request->hdr.instance_id, PLDM_ERROR, seconds,
109                                   minutes, hours, day, month, year,
110                                   responsePtr);
111         return response;
112     }
113 
114     uint64_t timeUsec = std::get<EpochTimeUS>(value);
115 
116     uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
117                            std::chrono::microseconds(timeUsec))
118                            .count();
119 
120     utils::epochToBCDTime(timeSec, seconds, minutes, hours, day, month, year);
121 
122     encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, seconds,
123                               minutes, hours, day, month, year, responsePtr);
124     return response;
125 }
126 
127 /** @brief Construct the BIOS string table
128  *
129  *  @param[in] BIOSStringTable - the string table
130  *  @param[in] transferHandle - transfer handle to identify part of transfer
131  *  @param[in] transferOpFlag - flag to indicate which part of data being
132  * transferred
133  *  @param[in] instanceID - instance ID to identify the command
134  */
135 Response getBIOSStringTable(BIOSTable& BIOSStringTable,
136                             uint32_t /*transferHandle*/,
137                             uint8_t /*transferOpFlag*/, uint8_t instanceID)
138 
139 {
140     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
141                       0);
142     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
143     if (!BIOSStringTable.isEmpty())
144     {
145         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS,
146                                    0, /* next transfer handle */
147                                    PLDM_START_AND_END, nullptr, response.size(),
148                                    responsePtr); // filling up the header here
149         BIOSStringTable.load(response);
150         return response;
151     }
152     auto biosStrings = bios_parser::getStrings();
153     std::sort(biosStrings.begin(), biosStrings.end());
154     // remove all duplicate strings received from bios json
155     biosStrings.erase(std::unique(biosStrings.begin(), biosStrings.end()),
156                       biosStrings.end());
157 
158     size_t sizeWithoutPad = std::accumulate(
159         biosStrings.begin(), biosStrings.end(), 0,
160         [](size_t sum, const std::string& elem) {
161             return sum +
162                    pldm_bios_table_string_entry_encode_length(elem.length());
163         });
164 
165     Table stringTable;
166     stringTable.reserve(utils::getTableTotalsize(sizeWithoutPad));
167 
168     stringTable.resize(sizeWithoutPad);
169     auto tablePtr = stringTable.data();
170     for (const auto& elem : biosStrings)
171     {
172         auto entry_length =
173             pldm_bios_table_string_entry_encode_length(elem.length());
174         pldm_bios_table_string_entry_encode(tablePtr, sizeWithoutPad,
175                                             elem.c_str(), elem.length());
176         tablePtr += entry_length;
177         sizeWithoutPad -= entry_length;
178     }
179 
180     utils::padAndChecksum(stringTable);
181     BIOSStringTable.store(stringTable);
182     response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
183                         stringTable.size(),
184                     0);
185     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
186     encode_get_bios_table_resp(
187         instanceID, PLDM_SUCCESS, 0 /* nxtTransferHandle */, PLDM_START_AND_END,
188         stringTable.data(), response.size(), responsePtr);
189     return response;
190 }
191 
192 /** @brief Find the string handle from the BIOS string table given the name
193  *
194  *  @param[in] name - name of the BIOS string
195  *  @param[in] BIOSStringTable - the string table
196  *  @return - uint16_t - handle of the string
197  */
198 StringHandle findStringHandle(const std::string& name,
199                               const BIOSTable& BIOSStringTable)
200 {
201     Table table;
202     BIOSStringTable.load(table);
203     auto stringEntry = pldm_bios_table_string_find_by_string(
204         table.data(), table.size(), name.c_str());
205     if (stringEntry == nullptr)
206     {
207         log<level::ERR>("Reached end of BIOS string table,did not find the "
208                         "handle for the string",
209                         entry("STRING=%s", name.c_str()));
210         elog<InternalFailure>();
211     }
212 
213     return pldm_bios_table_string_entry_decode_handle(stringEntry);
214 }
215 
216 /** @brief Find the string name from the BIOS string table for a string handle
217  *
218  *  @param[in] stringHdl - string handle
219  *  @param[in] BIOSStringTable - the string table
220  *
221  *  @return - std::string - name of the corresponding BIOS string
222  */
223 std::string findStringName(StringHandle stringHdl,
224                            const BIOSTable& BIOSStringTable)
225 {
226     Table table;
227     BIOSStringTable.load(table);
228     auto stringEntry = pldm_bios_table_string_find_by_handle(
229         table.data(), table.size(), stringHdl);
230     std::string name;
231     if (stringEntry == nullptr)
232     {
233         log<level::ERR>("Reached end of BIOS string table,did not find "
234                         "string name for handle",
235                         entry("STRING_HANDLE=%d", stringHdl));
236     }
237     auto strLength =
238         pldm_bios_table_string_entry_decode_string_length(stringEntry);
239     name.resize(strLength);
240     pldm_bios_table_string_entry_decode_string(stringEntry, name.data(),
241                                                name.size());
242     return name;
243 }
244 
245 namespace bios_type_enum
246 {
247 
248 using namespace bios_parser::bios_enum;
249 
250 /** @brief Find the indices  into the array of the possible values of string
251  *  handles for the current values.This is used in attribute value table
252  *
253  *  @param[in] possiVals - vector of string handles comprising all the possible
254  *                         values for an attribute
255  *  @param[in] currVals - vector of strings comprising all current values
256  *                        for an attribute
257  *  @param[in] BIOSStringTable - the string table
258  *
259  *  @return - std::vector<uint8_t> - indices into the array of the possible
260  *                                   values of string handles
261  */
262 std::vector<uint8_t> findStrIndices(PossibleValuesByHandle possiVals,
263                                     CurrentValues currVals,
264                                     const BIOSTable& BIOSStringTable)
265 {
266     std::vector<uint8_t> stringIndices;
267 
268     for (const auto& currVal : currVals)
269     {
270         StringHandle curHdl;
271         try
272         {
273             curHdl = findStringHandle(currVal, BIOSStringTable);
274         }
275         catch (InternalFailure& e)
276         {
277             log<level::ERR>("Exception fetching handle for the string",
278                             entry("STRING=%s", currVal.c_str()));
279             continue;
280         }
281 
282         uint8_t i = 0;
283         for (auto possiHdl : possiVals)
284         {
285             if (possiHdl == curHdl)
286             {
287                 stringIndices.push_back(i);
288                 break;
289             }
290             i++;
291         }
292     }
293     return stringIndices;
294 }
295 
296 /** @brief Find the indices into the array of the possible values of string
297  *  handles for the default values. This is used in attribute table
298  *
299  *  @param[in] possiVals - vector of strings comprising all the possible values
300  *                         for an attribute
301  *  @param[in] defVals - vector of strings comprising all the default values
302  *                       for an attribute
303  *  @return - std::vector<uint8_t> - indices into the array of the possible
304  *                                   values of string
305  */
306 std::vector<uint8_t> findDefaultValHandle(const PossibleValues& possiVals,
307                                           const DefaultValues& defVals)
308 {
309     std::vector<uint8_t> defHdls;
310     for (const auto& defs : defVals)
311     {
312         auto index = std::lower_bound(possiVals.begin(), possiVals.end(), defs);
313         if (index != possiVals.end())
314         {
315             defHdls.push_back(index - possiVals.begin());
316         }
317     }
318 
319     return defHdls;
320 }
321 
322 /** @brief Construct the attibute table for BIOS type Enumeration and
323  *         Enumeration ReadOnly
324  *  @param[in] BIOSStringTable - the string table
325  *  @param[in] biosJsonDir - path where the BIOS json files are present
326  *  @param[in,out] attributeTable - the attribute table
327  *
328  */
329 void constructAttrTable(const BIOSTable& BIOSStringTable, Table& attributeTable)
330 {
331     const auto& attributeMap = getValues();
332     StringHandle strHandle;
333 
334     for (const auto& [key, value] : attributeMap)
335     {
336         try
337         {
338             strHandle = findStringHandle(key, BIOSStringTable);
339         }
340         catch (InternalFailure& e)
341         {
342             log<level::ERR>("Could not find handle for BIOS string",
343                             entry("ATTRIBUTE=%s", key.c_str()));
344             continue;
345         }
346         bool readOnly = (std::get<0>(value));
347         PossibleValues possiVals = std::get<1>(value);
348         DefaultValues defVals = std::get<2>(value);
349         // both the possible and default values are stored in sorted manner to
350         // ease in fetching back/comparison
351         std::sort(possiVals.begin(), possiVals.end());
352         std::sort(defVals.begin(), defVals.end());
353 
354         std::vector<StringHandle> possiValsByHdl;
355         for (const auto& elem : possiVals)
356         {
357             try
358             {
359                 auto hdl = findStringHandle(elem, BIOSStringTable);
360                 possiValsByHdl.push_back(std::move(hdl));
361             }
362             catch (InternalFailure& e)
363             {
364                 log<level::ERR>("Could not find handle for BIOS string",
365                                 entry("STRING=%s", elem.c_str()));
366                 continue;
367             }
368         }
369         auto defValsByHdl = findDefaultValHandle(possiVals, defVals);
370         auto entryLength = pldm_bios_table_attr_entry_enum_encode_length(
371             possiValsByHdl.size(), defValsByHdl.size());
372 
373         auto attrTableSize = attributeTable.size();
374         attributeTable.resize(attrTableSize + entryLength, 0);
375         struct pldm_bios_table_attr_entry_enum_info info = {
376             strHandle,
377             readOnly,
378             (uint8_t)possiValsByHdl.size(),
379             possiValsByHdl.data(),
380             (uint8_t)defValsByHdl.size(),
381             defValsByHdl.data(),
382         };
383         pldm_bios_table_attr_entry_enum_encode(
384             attributeTable.data() + attrTableSize, entryLength, &info);
385     }
386 }
387 
388 void constructAttrValueEntry(
389     const struct pldm_bios_attr_table_entry* attrTableEntry,
390     const std::string& attrName, const BIOSTable& BIOSStringTable,
391     Table& attrValueTable)
392 {
393     CurrentValues currVals;
394     try
395     {
396         currVals = getAttrValue(attrName);
397     }
398     catch (const std::exception& e)
399     {
400         log<level::ERR>("getAttrValue returned error for attribute",
401                         entry("NAME=%s", attrName.c_str()),
402                         entry("ERROR=%s", e.what()));
403         return;
404     }
405     uint8_t pv_num =
406         pldm_bios_table_attr_entry_enum_decode_pv_num(attrTableEntry);
407     PossibleValuesByHandle pvHdls(pv_num, 0);
408     pldm_bios_table_attr_entry_enum_decode_pv_hdls(attrTableEntry,
409                                                    pvHdls.data(), pv_num);
410     std::sort(currVals.begin(), currVals.end());
411 
412     auto currValStrIndices = findStrIndices(pvHdls, currVals, BIOSStringTable);
413 
414     auto entryLength = pldm_bios_table_attr_value_entry_encode_enum_length(
415         currValStrIndices.size());
416     auto tableSize = attrValueTable.size();
417     attrValueTable.resize(tableSize + entryLength);
418     pldm_bios_table_attr_value_entry_encode_enum(
419         attrValueTable.data() + tableSize, entryLength,
420         attrTableEntry->attr_handle, attrTableEntry->attr_type,
421         currValStrIndices.size(), currValStrIndices.data());
422 }
423 
424 } // end namespace bios_type_enum
425 
426 namespace bios_type_string
427 {
428 
429 using namespace bios_parser::bios_string;
430 
431 /** @brief Construct the attibute table for BIOS type String and
432  *         String ReadOnly
433  *  @param[in] BIOSStringTable - the string table
434  *  @param[in] biosJsonDir - path where the BIOS json files are present
435  *  @param[in,out] attributeTable - the attribute table
436  *
437  */
438 void constructAttrTable(const BIOSTable& BIOSStringTable, Table& attributeTable)
439 {
440     const auto& attributeMap = getValues();
441     StringHandle strHandle;
442     for (const auto& [key, value] : attributeMap)
443     {
444         try
445         {
446             strHandle = findStringHandle(key, BIOSStringTable);
447         }
448         catch (InternalFailure& e)
449         {
450             log<level::ERR>("Could not find handle for BIOS string",
451                             entry("ATTRIBUTE=%s", key.c_str()));
452             continue;
453         }
454 
455         const auto& [readOnly, strType, minStrLen, maxStrLen, defaultStrLen,
456                      defaultStr] = value;
457         auto entryLength =
458             pldm_bios_table_attr_entry_string_encode_length(defaultStrLen);
459 
460         struct pldm_bios_table_attr_entry_string_info info = {
461             strHandle, readOnly,      strType,           minStrLen,
462             maxStrLen, defaultStrLen, defaultStr.data(),
463         };
464         auto attrTableSize = attributeTable.size();
465         attributeTable.resize(attrTableSize + entryLength, 0);
466         pldm_bios_table_attr_entry_string_encode(
467             attributeTable.data() + attrTableSize, entryLength, &info);
468     }
469 }
470 
471 void constructAttrValueEntry(const pldm_bios_attr_table_entry* attrTableEntry,
472                              const std::string& attrName,
473                              const BIOSTable& BIOSStringTable,
474                              Table& attrValueTable)
475 {
476     std::ignore = BIOSStringTable;
477     std::string currStr;
478     uint16_t currStrLen = 0;
479     try
480     {
481         currStr = getAttrValue(attrName);
482         currStrLen = currStr.size();
483     }
484     catch (const std::exception& e)
485     {
486         log<level::ERR>("getAttrValue returned error for attribute",
487                         entry("NAME=%s", attrName.c_str()),
488                         entry("ERROR=%s", e.what()));
489         return;
490     }
491     auto entryLength =
492         pldm_bios_table_attr_value_entry_encode_string_length(currStrLen);
493     auto tableSize = attrValueTable.size();
494     attrValueTable.resize(tableSize + entryLength);
495     pldm_bios_table_attr_value_entry_encode_string(
496         attrValueTable.data() + tableSize, entryLength,
497         attrTableEntry->attr_handle, attrTableEntry->attr_type, currStrLen,
498         currStr.c_str());
499 }
500 
501 } // end namespace bios_type_string
502 
503 namespace bios_type_integer
504 {
505 
506 using namespace bios_parser::bios_integer;
507 
508 /** @brief Construct the attibute table for BIOS type Integer and
509  *         Integer ReadOnly
510  *  @param[in] BIOSStringTable - the string table
511  *  @param[in,out] attributeTable - the attribute table
512  *
513  */
514 void constructAttrTable(const BIOSTable& BIOSStringTable, Table& attributeTable)
515 {
516     const auto& attributeMap = getValues();
517     StringHandle strHandle;
518     for (const auto& [key, value] : attributeMap)
519     {
520         try
521         {
522             strHandle = findStringHandle(key, BIOSStringTable);
523         }
524         catch (InternalFailure& e)
525         {
526             log<level::ERR>("Could not find handle for BIOS string",
527                             entry("ATTRIBUTE=%s", key.c_str()));
528             continue;
529         }
530 
531         const auto& [readOnly, lowerBound, upperBound, scalarIncrement,
532                      defaultValue] = value;
533         auto entryLength = pldm_bios_table_attr_entry_integer_encode_length();
534 
535         struct pldm_bios_table_attr_entry_integer_info info = {
536             strHandle,  readOnly,        lowerBound,
537             upperBound, scalarIncrement, defaultValue,
538         };
539         auto attrTableSize = attributeTable.size();
540         attributeTable.resize(attrTableSize + entryLength, 0);
541         pldm_bios_table_attr_entry_integer_encode(
542             attributeTable.data() + attrTableSize, entryLength, &info);
543     }
544 }
545 
546 void constructAttrValueEntry(const pldm_bios_attr_table_entry* attrTableEntry,
547                              const std::string& attrName,
548                              const BIOSTable& BIOSStringTable,
549                              Table& attrValueTable)
550 {
551     std::ignore = BIOSStringTable;
552     uint64_t currentValue;
553     try
554     {
555         currentValue = getAttrValue(attrName);
556     }
557     catch (const std::exception& e)
558     {
559         log<level::ERR>("Failed to get attribute value",
560                         entry("NAME=%s", attrName.c_str()),
561                         entry("ERROR=%s", e.what()));
562         return;
563     }
564     auto entryLength = pldm_bios_table_attr_value_entry_encode_integer_length();
565     auto tableSize = attrValueTable.size();
566     attrValueTable.resize(tableSize + entryLength);
567     pldm_bios_table_attr_value_entry_encode_integer(
568         attrValueTable.data() + tableSize, entryLength,
569         attrTableEntry->attr_handle, attrTableEntry->attr_type, currentValue);
570 }
571 
572 } // namespace bios_type_integer
573 
574 void traverseBIOSAttrTable(const Table& biosAttrTable,
575                            AttrTableEntryHandler handler)
576 {
577     std::unique_ptr<pldm_bios_table_iter, decltype(&pldm_bios_table_iter_free)>
578         iter(pldm_bios_table_iter_create(biosAttrTable.data(),
579                                          biosAttrTable.size(),
580                                          PLDM_BIOS_ATTR_TABLE),
581              pldm_bios_table_iter_free);
582     while (!pldm_bios_table_iter_is_end(iter.get()))
583     {
584         auto table_entry = pldm_bios_table_iter_attr_entry_value(iter.get());
585         try
586         {
587             handler(table_entry);
588         }
589         catch (const std::exception& e)
590         {
591             log<level::ERR>("handler fails when traversing BIOSAttrTable",
592                             entry("ERROR=%s", e.what()));
593         }
594         pldm_bios_table_iter_next(iter.get());
595     }
596 }
597 
598 using typeHandler = std::function<void(const BIOSTable& BIOSStringTable,
599                                        Table& attributeTable)>;
600 std::map<BIOSJsonName, typeHandler> attrTypeHandlers{
601     {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable},
602     {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable},
603     {bios_parser::bIOSIntegerJson, bios_type_integer::constructAttrTable},
604 };
605 
606 /** @brief Construct the BIOS attribute table
607  *
608  *  @param[in] BIOSAttributeTable - the attribute table
609  *  @param[in] BIOSStringTable - the string table
610  *  @param[in] transferHandle - transfer handle to identify part of transfer
611  *  @param[in] transferOpFlag - flag to indicate which part of data being
612  * transferred
613  *  @param[in] instanceID - instance ID to identify the command
614  *  @param[in] biosJsonDir - path where the BIOS json files are present
615  */
616 Response getBIOSAttributeTable(BIOSTable& BIOSAttributeTable,
617                                const BIOSTable& BIOSStringTable,
618                                uint32_t /*transferHandle*/,
619                                uint8_t /*transferOpFlag*/, uint8_t instanceID,
620                                const char* biosJsonDir)
621 {
622     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
623                       0);
624     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
625     uint32_t nxtTransferHandle = 0;
626     uint8_t transferFlag = PLDM_START_AND_END;
627 
628     if (BIOSAttributeTable.isEmpty())
629     { // no persisted table, constructing fresh table and response
630         Table attributeTable;
631         fs::path dir(biosJsonDir);
632 
633         for (auto it = attrTypeHandlers.begin(); it != attrTypeHandlers.end();
634              it++)
635         {
636             fs::path file = dir / it->first;
637             if (fs::exists(file))
638             {
639                 it->second(BIOSStringTable, attributeTable);
640             }
641         }
642 
643         if (attributeTable.empty())
644         { // no available json file is found
645             encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
646                                        nxtTransferHandle, transferFlag, nullptr,
647                                        response.size(), responsePtr);
648             return response;
649         }
650         utils::padAndChecksum(attributeTable);
651         BIOSAttributeTable.store(attributeTable);
652         response.resize(sizeof(pldm_msg_hdr) +
653                         PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
654                         attributeTable.size());
655         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
656         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
657                                    transferFlag, attributeTable.data(),
658                                    response.size(), responsePtr);
659     }
660     else
661     { // persisted table present, constructing response
662         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
663                                    transferFlag, nullptr, response.size(),
664                                    responsePtr); // filling up the header here
665         BIOSAttributeTable.load(response);
666     }
667 
668     return response;
669 }
670 
671 using AttrValTableEntryConstructHandler =
672     std::function<void(const struct pldm_bios_attr_table_entry* tableEntry,
673                        const std::string& attrName,
674                        const BIOSTable& BIOSStringTable, Table& table)>;
675 
676 using AttrType = uint8_t;
677 const std::map<AttrType, AttrValTableEntryConstructHandler>
678     AttrValTableConstructMap{
679         {PLDM_BIOS_STRING, bios_type_string::constructAttrValueEntry},
680         {PLDM_BIOS_STRING_READ_ONLY, bios_type_string::constructAttrValueEntry},
681         {PLDM_BIOS_ENUMERATION, bios_type_enum::constructAttrValueEntry},
682         {PLDM_BIOS_ENUMERATION_READ_ONLY,
683          bios_type_enum::constructAttrValueEntry},
684         {PLDM_BIOS_INTEGER, bios_type_integer::constructAttrValueEntry},
685         {PLDM_BIOS_INTEGER_READ_ONLY,
686          bios_type_integer::constructAttrValueEntry},
687     };
688 
689 void constructAttrValueTableEntry(
690     const struct pldm_bios_attr_table_entry* attrEntry,
691     const BIOSTable& BIOSStringTable, Table& attributeValueTable)
692 {
693     auto attrName = findStringName(attrEntry->string_handle, BIOSStringTable);
694     if (attrName.empty())
695     {
696         log<level::ERR>("invalid string handle",
697                         entry("STRING_HANDLE=%d", attrEntry->string_handle));
698         return;
699     }
700 
701     AttrValTableConstructMap.at(attrEntry->attr_type)(
702         attrEntry, attrName, BIOSStringTable, attributeValueTable);
703 }
704 
705 /** @brief Construct the BIOS attribute value table
706  *
707  *  @param[in] BIOSAttributeValueTable - the attribute value table
708  *  @param[in] BIOSAttributeTable - the attribute table
709  *  @param[in] BIOSStringTable - the string table
710  *  @param[in] transferHandle - transfer handle to identify part of transfer
711  *  @param[in] transferOpFlag - flag to indicate which part of data being
712  * transferred
713  *  @param[in] instanceID - instance ID to identify the command
714  */
715 Response getBIOSAttributeValueTable(BIOSTable& BIOSAttributeValueTable,
716                                     const BIOSTable& BIOSAttributeTable,
717                                     const BIOSTable& BIOSStringTable,
718                                     uint32_t& /*transferHandle*/,
719                                     uint8_t& /*transferOpFlag*/,
720                                     uint8_t instanceID)
721 {
722     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
723                       0);
724     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
725     uint32_t nxtTransferHandle = 0;
726     uint8_t transferFlag = PLDM_START_AND_END;
727 
728     if (!BIOSAttributeValueTable.isEmpty())
729     {
730         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
731                                    transferFlag, nullptr, response.size(),
732                                    responsePtr); // filling up the header here
733         BIOSAttributeValueTable.load(response);
734         return response;
735     }
736 
737     Table attributeValueTable;
738     Table attributeTable;
739     BIOSAttributeTable.load(attributeTable);
740     traverseBIOSAttrTable(
741         attributeTable,
742         [&BIOSStringTable, &attributeValueTable](
743             const struct pldm_bios_attr_table_entry* tableEntry) {
744             constructAttrValueTableEntry(tableEntry, BIOSStringTable,
745                                          attributeValueTable);
746         });
747     if (attributeValueTable.empty())
748     {
749         encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
750                                    nxtTransferHandle, transferFlag, nullptr,
751                                    response.size(), responsePtr);
752         return response;
753     }
754     utils::padAndChecksum(attributeValueTable);
755     BIOSAttributeValueTable.store(attributeValueTable);
756 
757     response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
758                     attributeValueTable.size());
759     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
760     encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
761                                transferFlag, attributeValueTable.data(),
762                                response.size(), responsePtr);
763 
764     return response;
765 }
766 
767 Response getBIOSTable(const pldm_msg* request, size_t payloadLength)
768 {
769     fs::create_directory(BIOS_TABLES_DIR);
770     auto response = internal::buildBIOSTables(request, payloadLength,
771                                               BIOS_JSONS_DIR, BIOS_TABLES_DIR);
772 
773     return response;
774 }
775 
776 namespace bios
777 {
778 
779 void registerHandlers()
780 {
781     registerHandler(PLDM_BIOS, PLDM_GET_DATE_TIME, std::move(getDateTime));
782     registerHandler(PLDM_BIOS, PLDM_GET_BIOS_TABLE, std::move(getBIOSTable));
783 }
784 
785 namespace internal
786 {
787 
788 Response buildBIOSTables(const pldm_msg* request, size_t payloadLength,
789                          const char* biosJsonDir, const char* biosTablePath)
790 {
791     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
792                       0);
793     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
794 
795     if (setupConfig(biosJsonDir) != 0)
796     {
797         encode_get_bios_table_resp(
798             request->hdr.instance_id, PLDM_BIOS_TABLE_UNAVAILABLE,
799             0 /* nxtTransferHandle */, PLDM_START_AND_END, nullptr,
800             response.size(), responsePtr);
801         return response;
802     }
803 
804     uint32_t transferHandle{};
805     uint8_t transferOpFlag{};
806     uint8_t tableType{};
807 
808     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
809                                         &transferOpFlag, &tableType);
810     if (rc == PLDM_SUCCESS)
811     {
812         BIOSTable BIOSStringTable(
813             ((std::string(biosTablePath) + "/stringTable")).c_str());
814         BIOSTable BIOSAttributeTable(
815             ((std::string(biosTablePath) + "/attributeTable")).c_str());
816         BIOSTable BIOSAttributeValueTable(
817             ((std::string(biosTablePath) + "/attributeValueTable")).c_str());
818         switch (tableType)
819         {
820             case PLDM_BIOS_STRING_TABLE:
821 
822                 response = getBIOSStringTable(BIOSStringTable, transferHandle,
823                                               transferOpFlag,
824                                               request->hdr.instance_id);
825                 break;
826             case PLDM_BIOS_ATTR_TABLE:
827 
828                 if (BIOSStringTable.isEmpty())
829                 {
830                     rc = PLDM_BIOS_TABLE_UNAVAILABLE;
831                 }
832                 else
833                 {
834                     response = getBIOSAttributeTable(
835                         BIOSAttributeTable, BIOSStringTable, transferHandle,
836                         transferOpFlag, request->hdr.instance_id, biosJsonDir);
837                 }
838                 break;
839             case PLDM_BIOS_ATTR_VAL_TABLE:
840                 if (BIOSAttributeTable.isEmpty() || BIOSStringTable.isEmpty())
841                 {
842                     rc = PLDM_BIOS_TABLE_UNAVAILABLE;
843                 }
844                 else
845                 {
846                     response = getBIOSAttributeValueTable(
847                         BIOSAttributeValueTable, BIOSAttributeTable,
848                         BIOSStringTable, transferHandle, transferOpFlag,
849                         request->hdr.instance_id);
850                 }
851                 break;
852             default:
853                 rc = PLDM_INVALID_BIOS_TABLE_TYPE;
854                 break;
855         }
856     }
857 
858     if (rc != PLDM_SUCCESS)
859     {
860         uint32_t nxtTransferHandle{};
861         uint8_t transferFlag{};
862         size_t respPayloadLength{};
863 
864         encode_get_bios_table_resp(request->hdr.instance_id, rc,
865                                    nxtTransferHandle, transferFlag, nullptr,
866                                    respPayloadLength, responsePtr);
867     }
868 
869     return response;
870 }
871 
872 } // end namespace internal
873 } // namespace bios
874 
875 } // namespace responder
876 } // namespace pldm
877