xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 881cde18)
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 <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 } // namespace utils
59 
60 Response getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
61 {
62     uint8_t seconds = 0;
63     uint8_t minutes = 0;
64     uint8_t hours = 0;
65     uint8_t day = 0;
66     uint8_t month = 0;
67     uint16_t year = 0;
68 
69     constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
70     constexpr auto hostTimePath = "/xyz/openbmc_project/time/host";
71     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
72     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
73     std::variant<EpochTimeUS> value;
74 
75     auto bus = sdbusplus::bus::new_default();
76     try
77     {
78         auto service = getService(bus, hostTimePath, timeInterface);
79 
80         auto method = bus.new_method_call(service.c_str(), hostTimePath,
81                                           dbusProperties, "Get");
82         method.append(timeInterface, "Elapsed");
83 
84         auto reply = bus.call(method);
85         reply.read(value);
86     }
87 
88     catch (std::exception& e)
89     {
90         log<level::ERR>("Error getting time", entry("PATH=%s", hostTimePath),
91                         entry("TIME INTERACE=%s", timeInterface));
92 
93         encode_get_date_time_resp(request->hdr.instance_id, PLDM_ERROR, seconds,
94                                   minutes, hours, day, month, year,
95                                   responsePtr);
96         return response;
97     }
98 
99     uint64_t timeUsec = std::get<EpochTimeUS>(value);
100 
101     uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
102                            std::chrono::microseconds(timeUsec))
103                            .count();
104 
105     utils::epochToBCDTime(timeSec, seconds, minutes, hours, day, month, year);
106 
107     encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, seconds,
108                               minutes, hours, day, month, year, responsePtr);
109     return response;
110 }
111 
112 /** @brief Generate the next attribute handle
113  *
114  *  @return - uint16_t - next attribute handle
115  */
116 AttributeHandle nextAttributeHandle()
117 {
118     static AttributeHandle attrHdl = 0;
119     return attrHdl++;
120 }
121 
122 /** @brief Generate the next string handle
123  *  *
124  *  @return - uint16_t - next string handle
125  */
126 StringHandle nextStringHandle()
127 {
128     static StringHandle strHdl = 0;
129     return strHdl++;
130 }
131 
132 /** @brief Construct the BIOS string table
133  *
134  *  @param[in] BIOSStringTable - the string table
135  *  @param[in] transferHandle - transfer handle to identify part of transfer
136  *  @param[in] transferOpFlag - flag to indicate which part of data being
137  * transferred
138  *  @param[in] instanceID - instance ID to identify the command
139  *  @param[in] biosJsonDir - path where the BIOS json files are present
140  */
141 Response getBIOSStringTable(BIOSTable& BIOSStringTable,
142                             uint32_t /*transferHandle*/,
143                             uint8_t /*transferOpFlag*/, uint8_t instanceID,
144                             const char* biosJsonDir)
145 {
146     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
147                       0);
148     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
149 
150     if (BIOSStringTable.isEmpty())
151     { // no persisted table, constructing fresh table and file
152         auto biosStrings = bios_parser::getStrings(biosJsonDir);
153         std::sort(biosStrings.begin(), biosStrings.end());
154         // remove all duplicate strings received from bios json
155         biosStrings.erase(std::unique(biosStrings.begin(), biosStrings.end()),
156                           biosStrings.end());
157         size_t allStringsLen =
158             std::accumulate(biosStrings.begin(), biosStrings.end(), 0,
159                             [](size_t sum, const std::string& elem) {
160                                 return sum + elem.size();
161                             });
162         size_t sizeWithoutPad =
163             allStringsLen +
164             (biosStrings.size() * (sizeof(pldm_bios_string_table_entry) - 1));
165         uint8_t padSize = utils::getNumPadBytes(sizeWithoutPad);
166         uint32_t stringTableSize{};
167         uint32_t checkSum;
168         if (biosStrings.size())
169         {
170             stringTableSize = sizeWithoutPad + padSize + sizeof(checkSum);
171         }
172         Table stringTable(
173             stringTableSize,
174             0); // initializing to 0 so that pad will be automatically added
175         auto tablePtr = reinterpret_cast<uint8_t*>(stringTable.data());
176         for (const auto& elem : biosStrings)
177         {
178             auto stringPtr =
179                 reinterpret_cast<struct pldm_bios_string_table_entry*>(
180                     tablePtr);
181 
182             stringPtr->string_handle = nextStringHandle();
183             stringPtr->string_length = elem.length();
184             memcpy(stringPtr->name, elem.c_str(), elem.length());
185             tablePtr += sizeof(stringPtr->string_handle) +
186                         sizeof(stringPtr->string_length);
187             tablePtr += elem.length();
188         }
189         tablePtr += padSize;
190 
191         if (stringTableSize)
192         {
193             // compute checksum
194             boost::crc_32_type result;
195             result.process_bytes(stringTable.data(), stringTableSize);
196             checkSum = result.checksum();
197             std::copy_n(reinterpret_cast<uint8_t*>(&checkSum), sizeof(checkSum),
198                         stringTable.data() + sizeWithoutPad + padSize);
199             BIOSStringTable.store(stringTable);
200         }
201 
202         response.resize(sizeof(pldm_msg_hdr) +
203                             PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
204                             stringTableSize,
205                         0);
206         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
207         size_t respPayloadLength = response.size();
208         uint32_t nxtTransferHandle = 0;
209         uint8_t transferFlag = PLDM_START_AND_END;
210         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
211                                    transferFlag, stringTable.data(),
212                                    respPayloadLength, responsePtr);
213     }
214     else
215     { // persisted table present, constructing response
216         size_t respPayloadLength = response.size();
217         uint32_t nxtTransferHandle = 0;
218         uint8_t transferFlag = PLDM_START_AND_END;
219         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
220                                    transferFlag, nullptr, respPayloadLength,
221                                    responsePtr); // filling up the header here
222         BIOSStringTable.load(response);
223     }
224 
225     return response;
226 }
227 
228 /** @brief Find the string handle from the BIOS string table given the name
229  *
230  *  @param[in] name - name of the BIOS string
231  *  @param[in] BIOSStringTable - the string table
232  *  @return - uint16_t - handle of the string
233  */
234 StringHandle findStringHandle(const std::string& name,
235                               const BIOSTable& BIOSStringTable)
236 {
237     StringHandle hdl{};
238     Response response;
239     BIOSStringTable.load(response);
240 
241     auto tableData = response.data();
242     size_t tableLen = response.size();
243     auto tableEntry =
244         reinterpret_cast<struct pldm_bios_string_table_entry*>(response.data());
245     while (1)
246     {
247         hdl = tableEntry->string_handle;
248         uint16_t len = tableEntry->string_length;
249         if (name.compare(0, name.length(), tableEntry->name, len) == 0)
250         {
251             break;
252         }
253         tableData += (sizeof(struct pldm_bios_string_table_entry) - 1) + len;
254 
255         if (std::distance(tableData, response.data() + tableLen) <=
256             padChksumMax)
257         {
258             log<level::ERR>("Reached end of BIOS string table,did not find the "
259                             "handle for the string",
260                             entry("STRING=%s", name.c_str()));
261             elog<InternalFailure>();
262             break;
263         }
264 
265         tableEntry =
266             reinterpret_cast<struct pldm_bios_string_table_entry*>(tableData);
267     }
268     return hdl;
269 }
270 
271 /** @brief Find the string name from the BIOS string table for a string handle
272  *
273  *  @param[in] stringHdl - string handle
274  *  @param[in] BIOSStringTable - the string table
275  *
276  *  @return - std::string - name of the corresponding BIOS string
277  */
278 std::string findStringName(StringHandle stringHdl,
279                            const BIOSTable& BIOSStringTable)
280 {
281     std::string name;
282     Response response;
283     BIOSStringTable.load(response);
284 
285     auto tableData = response.data();
286     size_t tableLen = response.size();
287     auto tableEntry =
288         reinterpret_cast<struct pldm_bios_string_table_entry*>(response.data());
289     while (1)
290     {
291         StringHandle currHdl = tableEntry->string_handle;
292         uint16_t len = tableEntry->string_length;
293         if (currHdl == stringHdl)
294         {
295             name.resize(len);
296             memcpy(name.data(), tableEntry->name, len);
297             break;
298         }
299         tableData += (sizeof(struct pldm_bios_string_table_entry) - 1) + len;
300 
301         if (std::distance(tableData, response.data() + tableLen) <=
302             padChksumMax)
303         {
304             log<level::ERR>("Reached end of BIOS string table,did not find "
305                             "string name for handle",
306                             entry("STRING_HANDLE=%d", stringHdl));
307             break;
308         }
309 
310         tableEntry =
311             reinterpret_cast<struct pldm_bios_string_table_entry*>(tableData);
312     }
313     return name;
314 }
315 
316 namespace bios_type_enum
317 {
318 
319 using namespace bios_parser::bios_enum;
320 
321 /** @brief Find the indices  into the array of the possible values of string
322  *  handles for the current values.This is used in attribute value table
323  *
324  *  @param[in] possiVals - vector of string handles comprising all the possible
325  *                         values for an attribute
326  *  @param[in] currVals - vector of strings comprising all current values
327  *                        for an attribute
328  *  @param[in] BIOSStringTable - the string table
329  *
330  *  @return - std::vector<uint8_t> - indices into the array of the possible
331  *                                   values of string handles
332  */
333 std::vector<uint8_t> findStrIndices(PossibleValuesByHandle possiVals,
334                                     CurrentValues currVals,
335                                     const BIOSTable& BIOSStringTable)
336 {
337     std::vector<uint8_t> stringIndices;
338 
339     for (const auto& currVal : currVals)
340     {
341         StringHandle curHdl;
342         try
343         {
344             curHdl = findStringHandle(currVal, BIOSStringTable);
345         }
346         catch (InternalFailure& e)
347         {
348             log<level::ERR>("Exception fetching handle for the string",
349                             entry("STRING=%s", currVal.c_str()));
350             continue;
351         }
352 
353         uint8_t i = 0;
354         for (auto possiHdl : possiVals)
355         {
356             if (possiHdl == curHdl)
357             {
358                 stringIndices.push_back(i);
359                 break;
360             }
361             i++;
362         }
363     }
364     return stringIndices;
365 }
366 
367 /** @brief Find the indices into the array of the possible values of string
368  *  handles for the default values. This is used in attribute table
369  *
370  *  @param[in] possiVals - vector of strings comprising all the possible values
371  *                         for an attribute
372  *  @param[in] defVals - vector of strings comprising all the default values
373  *                       for an attribute
374  *  @return - std::vector<uint8_t> - indices into the array of the possible
375  *                                   values of string
376  */
377 std::vector<uint8_t> findDefaultValHandle(const PossibleValues& possiVals,
378                                           const DefaultValues& defVals)
379 {
380     std::vector<uint8_t> defHdls;
381     for (const auto& defs : defVals)
382     {
383         auto index = std::lower_bound(possiVals.begin(), possiVals.end(), defs);
384         if (index != possiVals.end())
385         {
386             defHdls.push_back(index - possiVals.begin());
387         }
388     }
389 
390     return defHdls;
391 }
392 
393 /** @brief Construct the attibute table for BIOS type Enumeration and
394  *         Enumeration ReadOnly
395  *  @param[in] BIOSStringTable - the string table
396  *  @param[in] biosJsonDir - path where the BIOS json files are present
397  *  @param[in,out] attributeTable - the attribute table
398  *
399  */
400 void constructAttrTable(const BIOSTable& BIOSStringTable,
401                         const char* biosJsonDir, Table& attributeTable)
402 {
403     setupValueLookup(biosJsonDir);
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 /** @brief Construct the attibute value table for BIOS type Enumeration and
478  *  Enumeration ReadOnly
479  *
480  *  @param[in] BIOSAttributeTable - the attribute table
481  *  @param[in] BIOSStringTable - the string table
482  *  @param[in, out] attributeValueTable - the attribute value table
483  *
484  */
485 void constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
486                              const BIOSTable& BIOSStringTable,
487                              Table& attributeValueTable)
488 {
489     Response response;
490     BIOSAttributeTable.load(response);
491 
492     auto tableData = response.data();
493     size_t tableLen = response.size();
494     auto attrPtr =
495         reinterpret_cast<struct pldm_bios_attr_table_entry*>(response.data());
496 
497     while (1)
498     {
499         uint16_t attrHdl = attrPtr->attr_handle;
500         uint8_t attrType = attrPtr->attr_type;
501         uint16_t stringHdl = attrPtr->string_handle;
502         tableData += (sizeof(struct pldm_bios_attr_table_entry) - 1);
503         uint8_t numPossiVals = *tableData;
504         tableData++; // pass number of possible values
505         PossibleValuesByHandle possiValsByHdl(numPossiVals, 0);
506         memcpy(possiValsByHdl.data(), tableData,
507                sizeof(uint16_t) * numPossiVals);
508         tableData += sizeof(uint16_t) * numPossiVals;
509         uint8_t numDefVals = *tableData;
510         tableData++;             // pass number of def vals
511         tableData += numDefVals; // pass all the def val indices
512 
513         auto attrName = findStringName(stringHdl, BIOSStringTable);
514         if (attrName.empty())
515         {
516             if (std::distance(tableData, response.data() + tableLen) <=
517                 padChksumMax)
518             {
519                 log<level::ERR>("Did not find string name for handle",
520                                 entry("STRING_HANDLE=%d", stringHdl));
521                 return;
522             }
523             attrPtr =
524                 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
525             continue;
526         }
527         CurrentValues currVals;
528         try
529         {
530             currVals = getAttrValue(attrName);
531         }
532         catch (const std::exception& e)
533         {
534             log<level::ERR>(
535                 "constructAttrValueTable returned error for attribute",
536                 entry("NAME=%s", attrName.c_str()),
537                 entry("ERROR=%s", e.what()));
538             if (std::distance(tableData, response.data() + tableLen) <=
539                 padChksumMax)
540             {
541                 return;
542             }
543 
544             attrPtr =
545                 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
546             continue;
547         }
548         // sorting since the possible values are stored in sorted way
549         std::sort(currVals.begin(), currVals.end());
550         auto currValStrIndices =
551             findStrIndices(possiValsByHdl, currVals, BIOSStringTable);
552         // number of current values equals to the number of string handles
553         // received not the number of strings received from getAttrValue
554         uint8_t numCurrVals = currValStrIndices.size();
555 
556         BIOSTableRow enumAttrValTable(
557             (sizeof(struct pldm_bios_attr_val_table_entry) - 1) +
558                 sizeof(uint8_t) + numCurrVals * sizeof(uint8_t),
559             0);
560         BIOSTableRow::iterator it = enumAttrValTable.begin();
561         auto attrValPtr =
562             reinterpret_cast<struct pldm_bios_attr_val_table_entry*>(
563                 enumAttrValTable.data());
564         attrValPtr->attr_handle = attrHdl;
565         attrValPtr->attr_type = attrType;
566         std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1));
567         std::copy_n(&numCurrVals, sizeof(numCurrVals), it);
568         std::advance(it, sizeof(numCurrVals));
569         if (numCurrVals)
570         {
571             std::copy(currValStrIndices.begin(), currValStrIndices.end(), it);
572             std::advance(it, currValStrIndices.size());
573         }
574         std::move(enumAttrValTable.begin(), enumAttrValTable.end(),
575                   std::back_inserter(attributeValueTable));
576 
577         if (std::distance(tableData, response.data() + tableLen) <=
578             padChksumMax)
579         {
580             break;
581         }
582 
583         attrPtr =
584             reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
585     }
586 }
587 
588 } // end namespace bios_type_enum
589 
590 namespace bios_type_string
591 {
592 
593 using namespace bios_parser::bios_string;
594 
595 /** @brief Construct the attibute table for BIOS type String and
596  *         String ReadOnly
597  *  @param[in] BIOSStringTable - the string table
598  *  @param[in] biosJsonDir - path where the BIOS json files are present
599  *  @param[in,out] attributeTable - the attribute table
600  *
601  */
602 void constructAttrTable(const BIOSTable& BIOSStringTable,
603                         const char* biosJsonDir, Table& attributeTable)
604 {
605     auto rc = setupValueLookup(biosJsonDir);
606     if (rc == -1)
607     {
608         log<level::ERR>("Failed to parse entries in Json file");
609         return;
610     }
611     const auto& attributeMap = getValues();
612     StringHandle strHandle;
613 
614     for (const auto& [key, value] : attributeMap)
615     {
616         try
617         {
618             strHandle = findStringHandle(key, BIOSStringTable);
619         }
620         catch (InternalFailure& e)
621         {
622             log<level::ERR>("Could not find handle for BIOS string",
623                             entry("ATTRIBUTE=%s", key.c_str()));
624             continue;
625         }
626 
627         const auto& [type, strType, minStrLen, maxStrLen, defaultStrLen,
628                      defaultStr] = value;
629         uint8_t typeOfAttr =
630             type ? PLDM_BIOS_STRING_READ_ONLY : PLDM_BIOS_STRING;
631 
632         BIOSTableRow stringAttrTable(bios_parser::bios_string::attrTableSize +
633                                      defaultStr.size());
634         BIOSTableRow::iterator it = stringAttrTable.begin();
635         auto attrPtr = reinterpret_cast<struct pldm_bios_attr_table_entry*>(
636             stringAttrTable.data());
637         attrPtr->attr_handle = nextAttributeHandle();
638         attrPtr->attr_type = typeOfAttr;
639         attrPtr->string_handle = strHandle;
640 
641         std::advance(it, (sizeof(struct pldm_bios_attr_table_entry) - 1));
642         std::copy_n(&strType, sizeof(uint8_t), it);
643         std::advance(it, sizeof(uint8_t));
644         std::copy_n(reinterpret_cast<const uint8_t*>(&minStrLen),
645                     sizeof(uint16_t), it);
646         std::advance(it, sizeof(uint16_t));
647         std::copy_n(reinterpret_cast<const uint8_t*>(&maxStrLen),
648                     sizeof(uint16_t), it);
649         std::advance(it, sizeof(uint16_t));
650         std::copy_n(reinterpret_cast<const uint8_t*>(&defaultStrLen),
651                     sizeof(uint16_t), it);
652         std::advance(it, sizeof(uint16_t));
653         std::copy_n(defaultStr.data(), defaultStr.size(), it);
654         std::advance(it, defaultStr.size());
655 
656         attributeTable.insert(attributeTable.end(), stringAttrTable.begin(),
657                               stringAttrTable.end());
658     }
659 }
660 
661 /** @brief Construct the attibute value table for BIOS type String and
662  *  String ReadOnly
663  *
664  *  @param[in] BIOSAttributeTable - the attribute table
665  *  @param[in] BIOSStringTable - the string table
666  *  @param[in, out] attributeValueTable - the attribute value table
667  *
668  */
669 void constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
670                              const BIOSTable& BIOSStringTable,
671                              Table& attributeValueTable)
672 {
673     Response response;
674     BIOSAttributeTable.load(response);
675 
676     auto dataPtr = response.data();
677     size_t tableLen = response.size();
678 
679     while (true)
680     {
681         auto attrPtr =
682             reinterpret_cast<struct pldm_bios_attr_table_entry*>(dataPtr);
683         uint16_t attrHdl = attrPtr->attr_handle;
684         uint8_t attrType = attrPtr->attr_type;
685         uint16_t stringHdl = attrPtr->string_handle;
686         dataPtr += (sizeof(struct pldm_bios_attr_table_entry) - 1);
687         // pass number of StringType, MinimumStringLength, MaximumStringLength
688         dataPtr += sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t);
689         auto sizeDefaultStr = *(reinterpret_cast<uint16_t*>(dataPtr));
690         // pass number of DefaultStringLength, DefaultString
691         dataPtr += sizeof(uint16_t) + sizeDefaultStr;
692 
693         auto attrName = findStringName(stringHdl, BIOSStringTable);
694         if (attrName.empty())
695         {
696             if (std::distance(dataPtr, response.data() + tableLen) <=
697                 padChksumMax)
698             {
699                 log<level::ERR>("Did not find string name for handle",
700                                 entry("STRING_HANDLE=%d", stringHdl));
701                 return;
702             }
703             continue;
704         }
705 
706         uint16_t currStrLen = 0;
707         std::string currStr;
708         try
709         {
710             currStr = getAttrValue(attrName);
711             currStrLen = currStr.size();
712         }
713         catch (const std::exception& e)
714         {
715             log<level::ERR>("getAttrValue returned error for attribute",
716                             entry("NAME=%s", attrName.c_str()),
717                             entry("ERROR=%s", e.what()));
718             if (std::distance(dataPtr, response.data() + tableLen) <=
719                 padChksumMax)
720             {
721                 return;
722             }
723             continue;
724         }
725 
726         BIOSTableRow strAttrValTable(
727             bios_parser::bios_string::attrValueTableSize + currStrLen, 0);
728         BIOSTableRow::iterator it = strAttrValTable.begin();
729         auto attrValPtr =
730             reinterpret_cast<struct pldm_bios_attr_val_table_entry*>(
731                 strAttrValTable.data());
732         attrValPtr->attr_handle = attrHdl;
733         attrValPtr->attr_type = attrType;
734         std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1));
735         std::copy_n(reinterpret_cast<uint8_t*>(&currStrLen), sizeof(uint16_t),
736                     it);
737         std::advance(it, sizeof(uint16_t));
738         if (currStrLen)
739         {
740             std::copy_n(currStr.cbegin(), currStrLen, it);
741             std::advance(it, currStrLen);
742         }
743 
744         attributeValueTable.insert(attributeValueTable.end(),
745                                    strAttrValTable.begin(),
746                                    strAttrValTable.end());
747 
748         if (std::distance(dataPtr, response.data() + tableLen) <= padChksumMax)
749         {
750             break;
751         }
752     }
753 }
754 
755 } // end namespace bios_type_string
756 
757 using typeHandler =
758     std::function<void(const BIOSTable& BIOSStringTable,
759                        const char* biosJsonDir, Table& attributeTable)>;
760 std::map<BIOSJsonName, typeHandler> attrTypeHandlers{
761     {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable},
762     {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable}};
763 
764 using valueHandler = std::function<void(const BIOSTable& BIOSAttributeTable,
765 
766                                         const BIOSTable& BIOSStringTable,
767 
768                                         Table& attributeTable)>;
769 
770 std::map<BIOSJsonName, valueHandler> attrValueHandlers{
771     {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrValueTable},
772     {bios_parser::bIOSStrJson, bios_type_string::constructAttrValueTable}};
773 
774 /** @brief Construct the BIOS attribute table
775  *
776  *  @param[in] BIOSAttributeTable - the attribute table
777  *  @param[in] BIOSStringTable - the string table
778  *  @param[in] transferHandle - transfer handle to identify part of transfer
779  *  @param[in] transferOpFlag - flag to indicate which part of data being
780  * transferred
781  *  @param[in] instanceID - instance ID to identify the command
782  *  @param[in] biosJsonDir - path where the BIOS json files are present
783  */
784 Response getBIOSAttributeTable(BIOSTable& BIOSAttributeTable,
785                                const BIOSTable& BIOSStringTable,
786                                uint32_t /*transferHandle*/,
787                                uint8_t /*transferOpFlag*/, uint8_t instanceID,
788                                const char* biosJsonDir)
789 {
790     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
791                       0);
792     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
793     uint32_t nxtTransferHandle = 0;
794     uint8_t transferFlag = PLDM_START_AND_END;
795 
796     if (BIOSAttributeTable.isEmpty())
797     { // no persisted table, constructing fresh table and response
798         Table attributeTable;
799         fs::path dir(biosJsonDir);
800 
801         for (auto it = attrTypeHandlers.begin(); it != attrTypeHandlers.end();
802              it++)
803         {
804             fs::path file = dir / it->first;
805             if (fs::exists(file))
806             {
807                 it->second(BIOSStringTable, biosJsonDir, attributeTable);
808             }
809         }
810 
811         if (attributeTable.empty())
812         { // no available json file is found
813             encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
814                                        nxtTransferHandle, transferFlag, nullptr,
815                                        response.size(), responsePtr);
816             return response;
817         }
818 
819         // calculate pad
820         uint8_t padSize = utils::getNumPadBytes(attributeTable.size());
821         std::vector<uint8_t> pad(padSize, 0);
822         if (padSize)
823         {
824             std::move(pad.begin(), pad.end(),
825                       std::back_inserter(attributeTable));
826         }
827 
828         if (!attributeTable.empty())
829         {
830             // compute checksum
831             boost::crc_32_type result;
832             size_t size = attributeTable.size();
833             result.process_bytes(attributeTable.data(), size);
834             uint32_t checkSum = result.checksum();
835             attributeTable.resize(size + sizeof(checkSum));
836             std::copy_n(reinterpret_cast<uint8_t*>(&checkSum), sizeof(checkSum),
837                         attributeTable.data() + size);
838             BIOSAttributeTable.store(attributeTable);
839         }
840         response.resize(sizeof(pldm_msg_hdr) +
841                         PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
842                         attributeTable.size());
843         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
844         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
845                                    transferFlag, attributeTable.data(),
846                                    response.size(), responsePtr);
847     }
848     else
849     { // persisted table present, constructing response
850         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
851                                    transferFlag, nullptr, response.size(),
852                                    responsePtr); // filling up the header here
853         BIOSAttributeTable.load(response);
854     }
855 
856     return response;
857 }
858 
859 /** @brief Construct the BIOS attribute value table
860  *
861  *  @param[in] BIOSAttributeValueTable - the attribute value table
862  *  @param[in] BIOSAttributeTable - the attribute table
863  *  @param[in] BIOSStringTable - the string table
864  *  @param[in] transferHandle - transfer handle to identify part of transfer
865  *  @param[in] transferOpFlag - flag to indicate which part of data being
866  * transferred
867  *  @param[in] instanceID - instance ID to identify the command
868  */
869 Response getBIOSAttributeValueTable(BIOSTable& BIOSAttributeValueTable,
870                                     const BIOSTable& BIOSAttributeTable,
871                                     const BIOSTable& BIOSStringTable,
872                                     uint32_t& /*transferHandle*/,
873                                     uint8_t& /*transferOpFlag*/,
874                                     uint8_t instanceID, const char* biosJsonDir)
875 {
876     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
877                       0);
878     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
879     uint32_t nxtTransferHandle = 0;
880     uint8_t transferFlag = PLDM_START_AND_END;
881     size_t respPayloadLength{};
882 
883     if (BIOSAttributeValueTable.isEmpty())
884     { // no persisted table, constructing fresh table and data
885         Table attributeValueTable;
886         fs::path dir(biosJsonDir);
887 
888         for (auto it = attrValueHandlers.begin(); it != attrValueHandlers.end();
889              it++)
890         {
891             fs::path file = dir / it->first;
892             if (fs::exists(file))
893             {
894                 it->second(BIOSAttributeTable, BIOSStringTable,
895                            attributeValueTable);
896             }
897         }
898 
899         if (attributeValueTable.empty())
900         { // no available json file is found
901             encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
902                                        nxtTransferHandle, transferFlag, nullptr,
903                                        response.size(), responsePtr);
904             return response;
905         }
906         // calculate pad
907         uint8_t padSize = utils::getNumPadBytes(attributeValueTable.size());
908         std::vector<uint8_t> pad(padSize, 0);
909         if (padSize)
910         {
911             std::move(pad.begin(), pad.end(),
912                       std::back_inserter(attributeValueTable));
913         }
914         if (!attributeValueTable.empty())
915         {
916             // compute checksum
917             boost::crc_32_type result;
918             result.process_bytes(attributeValueTable.data(),
919                                  attributeValueTable.size());
920             uint32_t checkSum = result.checksum();
921             size_t size = attributeValueTable.size();
922             attributeValueTable.resize(size + sizeof(checkSum));
923             std::copy_n(reinterpret_cast<uint8_t*>(&checkSum), sizeof(checkSum),
924                         attributeValueTable.data() + size);
925             BIOSAttributeValueTable.store(attributeValueTable);
926         }
927 
928         response.resize(sizeof(pldm_msg_hdr) +
929                         PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
930                         attributeValueTable.size());
931         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
932         respPayloadLength = response.size();
933         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
934                                    transferFlag, attributeValueTable.data(),
935                                    respPayloadLength, responsePtr);
936     }
937     else
938     { // persisted table present, constructing response
939         respPayloadLength = response.size();
940         encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
941                                    transferFlag, nullptr, respPayloadLength,
942                                    responsePtr); // filling up the header here
943         BIOSAttributeValueTable.load(response);
944     }
945 
946     return response;
947 }
948 
949 Response getBIOSTable(const pldm_msg* request, size_t payloadLength)
950 {
951     fs::create_directory(BIOS_TABLES_DIR);
952     auto response = internal::buildBIOSTables(request, payloadLength,
953                                               BIOS_JSONS_DIR, BIOS_TABLES_DIR);
954 
955     return response;
956 }
957 
958 namespace bios
959 {
960 
961 void registerHandlers()
962 {
963     registerHandler(PLDM_BIOS, PLDM_GET_DATE_TIME, std::move(getDateTime));
964     registerHandler(PLDM_BIOS, PLDM_GET_BIOS_TABLE, std::move(getBIOSTable));
965 }
966 
967 namespace internal
968 {
969 
970 Response buildBIOSTables(const pldm_msg* request, size_t payloadLength,
971                          const char* biosJsonDir, const char* biosTablePath)
972 {
973     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
974                       0);
975     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
976 
977     uint32_t transferHandle{};
978     uint8_t transferOpFlag{};
979     uint8_t tableType{};
980 
981     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
982                                         &transferOpFlag, &tableType);
983     if (rc == PLDM_SUCCESS)
984     {
985         BIOSTable BIOSStringTable(
986             ((std::string(biosTablePath) + "/stringTable")).c_str());
987         BIOSTable BIOSAttributeTable(
988             ((std::string(biosTablePath) + "/attributeTable")).c_str());
989         BIOSTable BIOSAttributeValueTable(
990             ((std::string(biosTablePath) + "/attributeValueTable")).c_str());
991         switch (tableType)
992         {
993             case PLDM_BIOS_STRING_TABLE:
994 
995                 response = getBIOSStringTable(
996                     BIOSStringTable, transferHandle, transferOpFlag,
997                     request->hdr.instance_id, biosJsonDir);
998                 break;
999             case PLDM_BIOS_ATTR_TABLE:
1000 
1001                 if (BIOSStringTable.isEmpty())
1002                 {
1003                     rc = PLDM_BIOS_TABLE_UNAVAILABLE;
1004                 }
1005                 else
1006                 {
1007                     response = getBIOSAttributeTable(
1008                         BIOSAttributeTable, BIOSStringTable, transferHandle,
1009                         transferOpFlag, request->hdr.instance_id, biosJsonDir);
1010                 }
1011                 break;
1012             case PLDM_BIOS_ATTR_VAL_TABLE:
1013                 if (BIOSAttributeTable.isEmpty())
1014                 {
1015                     rc = PLDM_BIOS_TABLE_UNAVAILABLE;
1016                 }
1017                 else
1018                 {
1019                     response = getBIOSAttributeValueTable(
1020                         BIOSAttributeValueTable, BIOSAttributeTable,
1021                         BIOSStringTable, transferHandle, transferOpFlag,
1022                         request->hdr.instance_id, biosJsonDir);
1023                 }
1024                 break;
1025             default:
1026                 rc = PLDM_INVALID_BIOS_TABLE_TYPE;
1027                 break;
1028         }
1029     }
1030 
1031     if (rc != PLDM_SUCCESS)
1032     {
1033         uint32_t nxtTransferHandle{};
1034         uint8_t transferFlag{};
1035         size_t respPayloadLength{};
1036 
1037         encode_get_bios_table_resp(request->hdr.instance_id, rc,
1038                                    nxtTransferHandle, transferFlag, nullptr,
1039                                    respPayloadLength, responsePtr);
1040     }
1041 
1042     return response;
1043 }
1044 
1045 } // end namespace internal
1046 } // namespace bios
1047 
1048 } // namespace responder
1049 } // namespace pldm
1050