xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 0270040b)
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 /** @brief Construct the attibute value table for BIOS type Enumeration and
489  *  Enumeration ReadOnly
490  *
491  *  @param[in] BIOSAttributeTable - the attribute table
492  *  @param[in] BIOSStringTable - the string table
493  *  @param[in, out] attributeValueTable - the attribute value table
494  *
495  */
496 void constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
497                              const BIOSTable& BIOSStringTable,
498                              Table& attributeValueTable)
499 {
500     Response response;
501     BIOSAttributeTable.load(response);
502 
503     auto tableData = response.data();
504     size_t tableLen = response.size();
505     auto attrPtr =
506         reinterpret_cast<struct pldm_bios_attr_table_entry*>(response.data());
507 
508     while (1)
509     {
510         uint16_t attrHdl = attrPtr->attr_handle;
511         uint8_t attrType = attrPtr->attr_type;
512         uint16_t stringHdl = attrPtr->string_handle;
513         tableData += (sizeof(struct pldm_bios_attr_table_entry) - 1);
514         uint8_t numPossiVals = *tableData;
515         tableData++; // pass number of possible values
516         PossibleValuesByHandle possiValsByHdl(numPossiVals, 0);
517         memcpy(possiValsByHdl.data(), tableData,
518                sizeof(uint16_t) * numPossiVals);
519         tableData += sizeof(uint16_t) * numPossiVals;
520         uint8_t numDefVals = *tableData;
521         tableData++;             // pass number of def vals
522         tableData += numDefVals; // pass all the def val indices
523 
524         auto attrName = findStringName(stringHdl, BIOSStringTable);
525         if (attrName.empty())
526         {
527             if (std::distance(tableData, response.data() + tableLen) <=
528                 padChksumMax)
529             {
530                 log<level::ERR>("Did not find string name for handle",
531                                 entry("STRING_HANDLE=%d", stringHdl));
532                 return;
533             }
534             attrPtr =
535                 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
536             continue;
537         }
538         CurrentValues currVals;
539         try
540         {
541             currVals = getAttrValue(attrName);
542         }
543         catch (const std::exception& e)
544         {
545             log<level::ERR>(
546                 "constructAttrValueTable returned error for attribute",
547                 entry("NAME=%s", attrName.c_str()),
548                 entry("ERROR=%s", e.what()));
549             if (std::distance(tableData, response.data() + tableLen) <=
550                 padChksumMax)
551             {
552                 return;
553             }
554 
555             attrPtr =
556                 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
557             continue;
558         }
559         // sorting since the possible values are stored in sorted way
560         std::sort(currVals.begin(), currVals.end());
561         auto currValStrIndices =
562             findStrIndices(possiValsByHdl, currVals, BIOSStringTable);
563         // number of current values equals to the number of string handles
564         // received not the number of strings received from getAttrValue
565         uint8_t numCurrVals = currValStrIndices.size();
566 
567         BIOSTableRow enumAttrValTable(
568             (sizeof(struct pldm_bios_attr_val_table_entry) - 1) +
569                 sizeof(uint8_t) + numCurrVals * sizeof(uint8_t),
570             0);
571         BIOSTableRow::iterator it = enumAttrValTable.begin();
572         auto attrValPtr =
573             reinterpret_cast<struct pldm_bios_attr_val_table_entry*>(
574                 enumAttrValTable.data());
575         attrValPtr->attr_handle = attrHdl;
576         attrValPtr->attr_type = attrType;
577         std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1));
578         std::copy_n(&numCurrVals, sizeof(numCurrVals), it);
579         std::advance(it, sizeof(numCurrVals));
580         if (numCurrVals)
581         {
582             std::copy(currValStrIndices.begin(), currValStrIndices.end(), it);
583             std::advance(it, currValStrIndices.size());
584         }
585         std::move(enumAttrValTable.begin(), enumAttrValTable.end(),
586                   std::back_inserter(attributeValueTable));
587 
588         if (std::distance(tableData, response.data() + tableLen) <=
589             padChksumMax)
590         {
591             break;
592         }
593 
594         attrPtr =
595             reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
596     }
597 }
598 
599 } // end namespace bios_type_enum
600 
601 namespace bios_type_string
602 {
603 
604 using namespace bios_parser::bios_string;
605 
606 /** @brief Construct the attibute table for BIOS type String and
607  *         String ReadOnly
608  *  @param[in] BIOSStringTable - the string table
609  *  @param[in] biosJsonDir - path where the BIOS json files are present
610  *  @param[in,out] attributeTable - the attribute table
611  *
612  */
613 void constructAttrTable(const BIOSTable& BIOSStringTable,
614                         const char* biosJsonDir, Table& attributeTable)
615 {
616     auto rc = setupValueLookup(biosJsonDir);
617     if (rc == -1)
618     {
619         log<level::ERR>("Failed to parse entries in Json file");
620         return;
621     }
622     const auto& attributeMap = getValues();
623     StringHandle strHandle;
624 
625     for (const auto& [key, value] : attributeMap)
626     {
627         try
628         {
629             strHandle = findStringHandle(key, BIOSStringTable);
630         }
631         catch (InternalFailure& e)
632         {
633             log<level::ERR>("Could not find handle for BIOS string",
634                             entry("ATTRIBUTE=%s", key.c_str()));
635             continue;
636         }
637 
638         const auto& [type, strType, minStrLen, maxStrLen, defaultStrLen,
639                      defaultStr] = value;
640         uint8_t typeOfAttr =
641             type ? PLDM_BIOS_STRING_READ_ONLY : PLDM_BIOS_STRING;
642 
643         BIOSTableRow stringAttrTable(bios_parser::bios_string::attrTableSize +
644                                      defaultStr.size());
645         BIOSTableRow::iterator it = stringAttrTable.begin();
646         auto attrPtr = reinterpret_cast<struct pldm_bios_attr_table_entry*>(
647             stringAttrTable.data());
648         attrPtr->attr_handle = nextAttributeHandle();
649         attrPtr->attr_type = typeOfAttr;
650         attrPtr->string_handle = strHandle;
651 
652         std::advance(it, (sizeof(struct pldm_bios_attr_table_entry) - 1));
653         std::copy_n(&strType, sizeof(uint8_t), it);
654         std::advance(it, sizeof(uint8_t));
655         std::copy_n(reinterpret_cast<const uint8_t*>(&minStrLen),
656                     sizeof(uint16_t), it);
657         std::advance(it, sizeof(uint16_t));
658         std::copy_n(reinterpret_cast<const uint8_t*>(&maxStrLen),
659                     sizeof(uint16_t), it);
660         std::advance(it, sizeof(uint16_t));
661         std::copy_n(reinterpret_cast<const uint8_t*>(&defaultStrLen),
662                     sizeof(uint16_t), it);
663         std::advance(it, sizeof(uint16_t));
664         std::copy_n(defaultStr.data(), defaultStr.size(), it);
665         std::advance(it, defaultStr.size());
666 
667         attributeTable.insert(attributeTable.end(), stringAttrTable.begin(),
668                               stringAttrTable.end());
669     }
670 }
671 
672 /** @brief Construct the attibute value table for BIOS type String and
673  *  String ReadOnly
674  *
675  *  @param[in] BIOSAttributeTable - the attribute table
676  *  @param[in] BIOSStringTable - the string table
677  *  @param[in, out] attributeValueTable - the attribute value table
678  *
679  */
680 void constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
681                              const BIOSTable& BIOSStringTable,
682                              Table& attributeValueTable)
683 {
684     Response response;
685     BIOSAttributeTable.load(response);
686 
687     auto dataPtr = response.data();
688     size_t tableLen = response.size();
689 
690     while (true)
691     {
692         auto attrPtr =
693             reinterpret_cast<struct pldm_bios_attr_table_entry*>(dataPtr);
694         uint16_t attrHdl = attrPtr->attr_handle;
695         uint8_t attrType = attrPtr->attr_type;
696         uint16_t stringHdl = attrPtr->string_handle;
697         dataPtr += (sizeof(struct pldm_bios_attr_table_entry) - 1);
698         // pass number of StringType, MinimumStringLength, MaximumStringLength
699         dataPtr += sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t);
700         auto sizeDefaultStr = *(reinterpret_cast<uint16_t*>(dataPtr));
701         // pass number of DefaultStringLength, DefaultString
702         dataPtr += sizeof(uint16_t) + sizeDefaultStr;
703 
704         auto attrName = findStringName(stringHdl, BIOSStringTable);
705         if (attrName.empty())
706         {
707             if (std::distance(dataPtr, response.data() + tableLen) <=
708                 padChksumMax)
709             {
710                 log<level::ERR>("Did not find string name for handle",
711                                 entry("STRING_HANDLE=%d", stringHdl));
712                 return;
713             }
714             continue;
715         }
716 
717         uint16_t currStrLen = 0;
718         std::string currStr;
719         try
720         {
721             currStr = getAttrValue(attrName);
722             currStrLen = currStr.size();
723         }
724         catch (const std::exception& e)
725         {
726             log<level::ERR>("getAttrValue returned error for attribute",
727                             entry("NAME=%s", attrName.c_str()),
728                             entry("ERROR=%s", e.what()));
729             if (std::distance(dataPtr, response.data() + tableLen) <=
730                 padChksumMax)
731             {
732                 return;
733             }
734             continue;
735         }
736 
737         BIOSTableRow strAttrValTable(
738             bios_parser::bios_string::attrValueTableSize + currStrLen, 0);
739         BIOSTableRow::iterator it = strAttrValTable.begin();
740         auto attrValPtr =
741             reinterpret_cast<struct pldm_bios_attr_val_table_entry*>(
742                 strAttrValTable.data());
743         attrValPtr->attr_handle = attrHdl;
744         attrValPtr->attr_type = attrType;
745         std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1));
746         std::copy_n(reinterpret_cast<uint8_t*>(&currStrLen), sizeof(uint16_t),
747                     it);
748         std::advance(it, sizeof(uint16_t));
749         if (currStrLen)
750         {
751             std::copy_n(currStr.cbegin(), currStrLen, it);
752             std::advance(it, currStrLen);
753         }
754 
755         attributeValueTable.insert(attributeValueTable.end(),
756                                    strAttrValTable.begin(),
757                                    strAttrValTable.end());
758 
759         if (std::distance(dataPtr, response.data() + tableLen) <= padChksumMax)
760         {
761             break;
762         }
763     }
764 }
765 
766 } // end namespace bios_type_string
767 
768 void traverseBIOSAttrTable(const Table& biosAttrTable,
769                            AttrTableEntryHandler handler)
770 {
771     std::unique_ptr<pldm_bios_table_iter, decltype(&pldm_bios_table_iter_free)>
772         iter(pldm_bios_table_iter_create(biosAttrTable.data(),
773                                          biosAttrTable.size(),
774                                          PLDM_BIOS_ATTR_TABLE),
775              pldm_bios_table_iter_free);
776     while (!pldm_bios_table_iter_is_end(iter.get()))
777     {
778         auto table_entry = pldm_bios_table_iter_attr_entry_value(iter.get());
779         try
780         {
781             handler(table_entry);
782         }
783         catch (const std::exception& e)
784         {
785             log<level::ERR>("handler fails when traversing BIOSAttrTable",
786                             entry("ERROR=%s", e.what()));
787         }
788         pldm_bios_table_iter_next(iter.get());
789     }
790 }
791 
792 using typeHandler =
793     std::function<void(const BIOSTable& BIOSStringTable,
794                        const char* biosJsonDir, Table& attributeTable)>;
795 std::map<BIOSJsonName, typeHandler> attrTypeHandlers{
796     {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable},
797     {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable}};
798 
799 using valueHandler = std::function<void(const BIOSTable& BIOSAttributeTable,
800 
801                                         const BIOSTable& BIOSStringTable,
802 
803                                         Table& attributeTable)>;
804 
805 std::map<BIOSJsonName, valueHandler> attrValueHandlers{
806     {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrValueTable},
807     {bios_parser::bIOSStrJson, bios_type_string::constructAttrValueTable}};
808 
809 /** @brief Construct the BIOS attribute table
810  *
811  *  @param[in] BIOSAttributeTable - the attribute table
812  *  @param[in] BIOSStringTable - the string table
813  *  @param[in] transferHandle - transfer handle to identify part of transfer
814  *  @param[in] transferOpFlag - flag to indicate which part of data being
815  * transferred
816  *  @param[in] instanceID - instance ID to identify the command
817  *  @param[in] biosJsonDir - path where the BIOS json files are present
818  */
819 Response getBIOSAttributeTable(BIOSTable& BIOSAttributeTable,
820                                const BIOSTable& BIOSStringTable,
821                                uint32_t /*transferHandle*/,
822                                uint8_t /*transferOpFlag*/, uint8_t instanceID,
823                                const char* biosJsonDir)
824 {
825     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
826                       0);
827     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
828     uint32_t nxtTransferHandle = 0;
829     uint8_t transferFlag = PLDM_START_AND_END;
830 
831     if (BIOSAttributeTable.isEmpty())
832     { // no persisted table, constructing fresh table and response
833         Table attributeTable;
834         fs::path dir(biosJsonDir);
835 
836         for (auto it = attrTypeHandlers.begin(); it != attrTypeHandlers.end();
837              it++)
838         {
839             fs::path file = dir / it->first;
840             if (fs::exists(file))
841             {
842                 it->second(BIOSStringTable, biosJsonDir, attributeTable);
843             }
844         }
845 
846         if (attributeTable.empty())
847         { // no available json file is found
848             encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
849                                        nxtTransferHandle, transferFlag, nullptr,
850                                        response.size(), responsePtr);
851             return response;
852         }
853         utils::padAndChecksum(attributeTable);
854         BIOSAttributeTable.store(attributeTable);
855         response.resize(sizeof(pldm_msg_hdr) +
856                         PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
857                         attributeTable.size());
858         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
859         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
860                                    transferFlag, attributeTable.data(),
861                                    response.size(), responsePtr);
862     }
863     else
864     { // persisted table present, constructing response
865         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
866                                    transferFlag, nullptr, response.size(),
867                                    responsePtr); // filling up the header here
868         BIOSAttributeTable.load(response);
869     }
870 
871     return response;
872 }
873 
874 /** @brief Construct the BIOS attribute value table
875  *
876  *  @param[in] BIOSAttributeValueTable - the attribute value table
877  *  @param[in] BIOSAttributeTable - the attribute table
878  *  @param[in] BIOSStringTable - the string table
879  *  @param[in] transferHandle - transfer handle to identify part of transfer
880  *  @param[in] transferOpFlag - flag to indicate which part of data being
881  * transferred
882  *  @param[in] instanceID - instance ID to identify the command
883  */
884 Response getBIOSAttributeValueTable(BIOSTable& BIOSAttributeValueTable,
885                                     const BIOSTable& BIOSAttributeTable,
886                                     const BIOSTable& BIOSStringTable,
887                                     uint32_t& /*transferHandle*/,
888                                     uint8_t& /*transferOpFlag*/,
889                                     uint8_t instanceID, const char* biosJsonDir)
890 {
891     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
892                       0);
893     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
894     uint32_t nxtTransferHandle = 0;
895     uint8_t transferFlag = PLDM_START_AND_END;
896     size_t respPayloadLength{};
897 
898     if (BIOSAttributeValueTable.isEmpty())
899     { // no persisted table, constructing fresh table and data
900         Table attributeValueTable;
901         fs::path dir(biosJsonDir);
902 
903         for (auto it = attrValueHandlers.begin(); it != attrValueHandlers.end();
904              it++)
905         {
906             fs::path file = dir / it->first;
907             if (fs::exists(file))
908             {
909                 it->second(BIOSAttributeTable, BIOSStringTable,
910                            attributeValueTable);
911             }
912         }
913 
914         if (attributeValueTable.empty())
915         { // no available json file is found
916             encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
917                                        nxtTransferHandle, transferFlag, nullptr,
918                                        response.size(), responsePtr);
919             return response;
920         }
921         // calculate pad
922         uint8_t padSize = utils::getNumPadBytes(attributeValueTable.size());
923         std::vector<uint8_t> pad(padSize, 0);
924         if (padSize)
925         {
926             std::move(pad.begin(), pad.end(),
927                       std::back_inserter(attributeValueTable));
928         }
929         if (!attributeValueTable.empty())
930         {
931             // compute checksum
932             boost::crc_32_type result;
933             result.process_bytes(attributeValueTable.data(),
934                                  attributeValueTable.size());
935             uint32_t checkSum = result.checksum();
936             size_t size = attributeValueTable.size();
937             attributeValueTable.resize(size + sizeof(checkSum));
938             std::copy_n(reinterpret_cast<uint8_t*>(&checkSum), sizeof(checkSum),
939                         attributeValueTable.data() + size);
940             BIOSAttributeValueTable.store(attributeValueTable);
941         }
942 
943         response.resize(sizeof(pldm_msg_hdr) +
944                         PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
945                         attributeValueTable.size());
946         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
947         respPayloadLength = response.size();
948         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
949                                    transferFlag, attributeValueTable.data(),
950                                    respPayloadLength, responsePtr);
951     }
952     else
953     { // persisted table present, constructing response
954         respPayloadLength = response.size();
955         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
956                                    transferFlag, nullptr, respPayloadLength,
957                                    responsePtr); // filling up the header here
958         BIOSAttributeValueTable.load(response);
959     }
960 
961     return response;
962 }
963 
964 Response getBIOSTable(const pldm_msg* request, size_t payloadLength)
965 {
966     fs::create_directory(BIOS_TABLES_DIR);
967     auto response = internal::buildBIOSTables(request, payloadLength,
968                                               BIOS_JSONS_DIR, BIOS_TABLES_DIR);
969 
970     return response;
971 }
972 
973 namespace bios
974 {
975 
976 void registerHandlers()
977 {
978     registerHandler(PLDM_BIOS, PLDM_GET_DATE_TIME, std::move(getDateTime));
979     registerHandler(PLDM_BIOS, PLDM_GET_BIOS_TABLE, std::move(getBIOSTable));
980 }
981 
982 namespace internal
983 {
984 
985 Response buildBIOSTables(const pldm_msg* request, size_t payloadLength,
986                          const char* biosJsonDir, const char* biosTablePath)
987 {
988     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
989                       0);
990     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
991 
992     uint32_t transferHandle{};
993     uint8_t transferOpFlag{};
994     uint8_t tableType{};
995 
996     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
997                                         &transferOpFlag, &tableType);
998     if (rc == PLDM_SUCCESS)
999     {
1000         BIOSTable BIOSStringTable(
1001             ((std::string(biosTablePath) + "/stringTable")).c_str());
1002         BIOSTable BIOSAttributeTable(
1003             ((std::string(biosTablePath) + "/attributeTable")).c_str());
1004         BIOSTable BIOSAttributeValueTable(
1005             ((std::string(biosTablePath) + "/attributeValueTable")).c_str());
1006         switch (tableType)
1007         {
1008             case PLDM_BIOS_STRING_TABLE:
1009 
1010                 response = getBIOSStringTable(
1011                     BIOSStringTable, transferHandle, transferOpFlag,
1012                     request->hdr.instance_id, biosJsonDir);
1013                 break;
1014             case PLDM_BIOS_ATTR_TABLE:
1015 
1016                 if (BIOSStringTable.isEmpty())
1017                 {
1018                     rc = PLDM_BIOS_TABLE_UNAVAILABLE;
1019                 }
1020                 else
1021                 {
1022                     response = getBIOSAttributeTable(
1023                         BIOSAttributeTable, BIOSStringTable, transferHandle,
1024                         transferOpFlag, request->hdr.instance_id, biosJsonDir);
1025                 }
1026                 break;
1027             case PLDM_BIOS_ATTR_VAL_TABLE:
1028                 if (BIOSAttributeTable.isEmpty())
1029                 {
1030                     rc = PLDM_BIOS_TABLE_UNAVAILABLE;
1031                 }
1032                 else
1033                 {
1034                     response = getBIOSAttributeValueTable(
1035                         BIOSAttributeValueTable, BIOSAttributeTable,
1036                         BIOSStringTable, transferHandle, transferOpFlag,
1037                         request->hdr.instance_id, biosJsonDir);
1038                 }
1039                 break;
1040             default:
1041                 rc = PLDM_INVALID_BIOS_TABLE_TYPE;
1042                 break;
1043         }
1044     }
1045 
1046     if (rc != PLDM_SUCCESS)
1047     {
1048         uint32_t nxtTransferHandle{};
1049         uint8_t transferFlag{};
1050         size_t respPayloadLength{};
1051 
1052         encode_get_bios_table_resp(request->hdr.instance_id, rc,
1053                                    nxtTransferHandle, transferFlag, nullptr,
1054                                    respPayloadLength, responsePtr);
1055     }
1056 
1057     return response;
1058 }
1059 
1060 } // end namespace internal
1061 } // namespace bios
1062 
1063 } // namespace responder
1064 } // namespace pldm
1065