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