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