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