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