xref: /openbmc/smbios-mdr/src/mdrv2.cpp (revision e7cf319564add70c5d09bf069db51017e0cb9db7)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "mdrv2.hpp"
18 
19 #include "pcieslot.hpp"
20 
21 #include <sys/mman.h>
22 
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <sdbusplus/exception.hpp>
25 #include <xyz/openbmc_project/Smbios/MDR_V2/error.hpp>
26 
27 #include <fstream>
28 
29 namespace phosphor
30 {
31 namespace smbios
32 {
33 
34 std::vector<uint8_t> MDR_V2::getDirectoryInformation(uint8_t dirIndex)
35 {
36     std::vector<uint8_t> responseDir;
37     if (dirIndex > smbiosDir.dirEntries)
38     {
39         responseDir.push_back(0);
40         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
41             InvalidParameter();
42     }
43     responseDir.push_back(mdr2Version);
44     responseDir.push_back(smbiosDir.dirVersion);
45     uint8_t returnedEntries = smbiosDir.dirEntries - dirIndex;
46     responseDir.push_back(returnedEntries);
47     if ((dirIndex + returnedEntries) >= smbiosDir.dirEntries)
48     {
49         responseDir.push_back(0);
50     }
51     else
52     {
53         responseDir.push_back(smbiosDir.dirEntries - dirIndex -
54                               returnedEntries);
55     }
56     for (uint8_t index = dirIndex; index < smbiosDir.dirEntries; index++)
57     {
58         for (uint8_t indexId = 0; indexId < sizeof(DataIdStruct); indexId++)
59         {
60             responseDir.push_back(
61                 smbiosDir.dir[index].common.id.dataInfo[indexId]);
62         }
63     }
64 
65     return responseDir;
66 }
67 
68 bool MDR_V2::smbiosIsAvailForUpdate(uint8_t index)
69 {
70     bool ret = false;
71     if (index >= maxDirEntries)
72     {
73         return ret;
74     }
75 
76     switch (smbiosDir.dir[index].stage)
77     {
78         case MDR2SMBIOSStatusEnum::mdr2Updating:
79             ret = false;
80             break;
81 
82         case MDR2SMBIOSStatusEnum::mdr2Init:
83             // This *looks* like there should be a break statement here,
84             // as the effects of the previous statement are a noop given
85             // the following code that this falls through to.
86             // We've elected not to change it, though, since it's been
87             // this way since the old generation, and it would affect
88             // the way the code functions.
89             // If it ain't broke, don't fix it.
90 
91         case MDR2SMBIOSStatusEnum::mdr2Loaded:
92         case MDR2SMBIOSStatusEnum::mdr2Updated:
93             if (smbiosDir.dir[index].lock == MDR2DirLockEnum::mdr2DirLock)
94             {
95                 ret = false;
96             }
97             else
98             {
99                 ret = true;
100             }
101             break;
102 
103         default:
104             break;
105     }
106 
107     return ret;
108 }
109 
110 std::vector<uint8_t> MDR_V2::getDataOffer()
111 {
112     std::vector<uint8_t> offer(sizeof(DataIdStruct));
113     if (smbiosIsAvailForUpdate(0))
114     {
115         std::copy(smbiosDir.dir[0].common.id.dataInfo,
116                   &smbiosDir.dir[0].common.id.dataInfo[16], offer.data());
117     }
118     else
119     {
120         phosphor::logging::log<phosphor::logging::level::ERR>(
121             "smbios is not ready for update");
122         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
123             UpdateInProgress();
124     }
125     return offer;
126 }
127 
128 inline uint8_t MDR_V2::smbiosValidFlag(uint8_t index)
129 {
130     FlagStatus ret = FlagStatus::flagIsInvalid;
131     MDR2SMBIOSStatusEnum stage = smbiosDir.dir[index].stage;
132     MDR2DirLockEnum lock = smbiosDir.dir[index].lock;
133 
134     switch (stage)
135     {
136         case MDR2SMBIOSStatusEnum::mdr2Loaded:
137         case MDR2SMBIOSStatusEnum::mdr2Updated:
138             if (lock == MDR2DirLockEnum::mdr2DirLock)
139             {
140                 ret = FlagStatus::flagIsLocked; // locked
141             }
142             else
143             {
144                 ret = FlagStatus::flagIsValid; // valid
145             }
146             break;
147 
148         case MDR2SMBIOSStatusEnum::mdr2Updating:
149         case MDR2SMBIOSStatusEnum::mdr2Init:
150             ret = FlagStatus::flagIsInvalid; // invalid
151             break;
152 
153         default:
154             break;
155     }
156 
157     return static_cast<uint8_t>(ret);
158 }
159 
160 // If source variable size is 4 bytes (uint32_t) and destination is Vector type
161 // is 1 byte (uint8_t), then by using this API can copy data byte by byte. For
162 // Example copying data from smbiosDir.dir[idIndex].common.size and
163 // smbiosDir.dir[idIndex].common.timestamp to vector variable responseInfo
164 template <typename T>
165 void appendReversed(std::vector<uint8_t>& vector, const T& value)
166 {
167     auto data = reinterpret_cast<const uint8_t*>(&value);
168     std::reverse_copy(data, data + sizeof(value), std::back_inserter(vector));
169 }
170 
171 std::vector<uint8_t> MDR_V2::getDataInformation(uint8_t idIndex)
172 {
173     std::vector<uint8_t> responseInfo;
174     responseInfo.push_back(mdr2Version);
175 
176     if (idIndex >= maxDirEntries)
177     {
178         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
179             InvalidParameter();
180     }
181 
182     for (uint8_t index = 0; index < sizeof(DataIdStruct); index++)
183     {
184         responseInfo.push_back(
185             smbiosDir.dir[idIndex].common.id.dataInfo[index]);
186     }
187 
188     responseInfo.push_back(smbiosValidFlag(idIndex));
189     appendReversed(responseInfo, smbiosDir.dir[idIndex].common.size);
190     responseInfo.push_back(smbiosDir.dir[idIndex].common.dataVersion);
191     appendReversed(responseInfo, smbiosDir.dir[idIndex].common.timestamp);
192 
193     return responseInfo;
194 }
195 
196 bool MDR_V2::readDataFromFlash(MDRSMBIOSHeader* mdrHdr, uint8_t* data)
197 {
198     if (mdrHdr == nullptr)
199     {
200         phosphor::logging::log<phosphor::logging::level::ERR>(
201             "Read data from flash error - Invalid mdr header");
202         return false;
203     }
204     if (data == nullptr)
205     {
206         phosphor::logging::log<phosphor::logging::level::ERR>(
207             "Read data from flash error - Invalid data point");
208         return false;
209     }
210     std::ifstream smbiosFile(mdrType2File, std::ios_base::binary);
211     if (!smbiosFile.good())
212     {
213         phosphor::logging::log<phosphor::logging::level::ERR>(
214             "Read data from flash error - Open MDRV2 table file failure");
215         return false;
216     }
217     smbiosFile.clear();
218     smbiosFile.seekg(0, std::ios_base::end);
219     int fileLength = smbiosFile.tellg();
220     smbiosFile.seekg(0, std::ios_base::beg);
221     if (fileLength < sizeof(MDRSMBIOSHeader))
222     {
223         phosphor::logging::log<phosphor::logging::level::ERR>(
224             "MDR V2 file size is smaller than mdr header");
225         return false;
226     }
227     smbiosFile.read(reinterpret_cast<char*>(mdrHdr), sizeof(MDRSMBIOSHeader));
228     if (mdrHdr->dataSize > smbiosTableStorageSize)
229     {
230         phosphor::logging::log<phosphor::logging::level::ERR>(
231             "Data size out of limitation");
232         smbiosFile.close();
233         return false;
234     }
235     fileLength -= sizeof(MDRSMBIOSHeader);
236     if (fileLength < mdrHdr->dataSize)
237     {
238         smbiosFile.read(reinterpret_cast<char*>(data), fileLength);
239     }
240     else
241     {
242         smbiosFile.read(reinterpret_cast<char*>(data), mdrHdr->dataSize);
243     }
244     smbiosFile.close();
245     return true;
246 }
247 
248 bool MDR_V2::sendDirectoryInformation(uint8_t dirVersion, uint8_t dirIndex,
249                                       uint8_t returnedEntries,
250                                       uint8_t remainingEntries,
251                                       std::vector<uint8_t> dirEntry)
252 {
253     bool teminate = false;
254     if ((dirIndex >= maxDirEntries) || (returnedEntries < 1))
255     {
256         phosphor::logging::log<phosphor::logging::level::ERR>(
257             "Send Dir info failed - input parameter invalid");
258         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
259             InvalidParameter();
260     }
261     if ((static_cast<size_t>(returnedEntries) * sizeof(DataIdStruct)) !=
262         dirEntry.size())
263     {
264         phosphor::logging::log<phosphor::logging::level::ERR>(
265             "Directory size invalid");
266         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
267             InvalidParameter();
268     }
269     if (dirVersion == smbiosDir.dirVersion)
270     {
271         teminate = true;
272     }
273     else
274     {
275         if (remainingEntries > 0)
276         {
277             teminate = false;
278         }
279         else
280         {
281             teminate = true;
282             smbiosDir.dirVersion = dirVersion;
283         }
284         uint8_t idIndex = dirIndex;
285         smbiosDir.dirEntries = returnedEntries;
286 
287         uint8_t* pData = dirEntry.data();
288         if (pData == nullptr)
289         {
290             return false;
291         }
292         for (uint8_t index = 0; index < returnedEntries; index++)
293         {
294             auto data = reinterpret_cast<const DataIdStruct*>(pData);
295             std::copy(data->dataInfo, data->dataInfo + sizeof(DataIdStruct),
296                       smbiosDir.dir[idIndex + index].common.id.dataInfo);
297             pData += sizeof(DataIdStruct);
298         }
299     }
300     return teminate;
301 }
302 
303 bool MDR_V2::sendDataInformation(uint8_t idIndex, uint8_t flag,
304                                  uint32_t dataLen, uint32_t dataVer,
305                                  uint32_t timeStamp)
306 {
307     if (idIndex >= maxDirEntries)
308     {
309         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
310             InvalidParameter();
311     }
312     int entryChanged = 0;
313     if (smbiosDir.dir[idIndex].common.dataSetSize != dataLen)
314     {
315         entryChanged++;
316         smbiosDir.dir[idIndex].common.dataSetSize = dataLen;
317     }
318 
319     if (smbiosDir.dir[idIndex].common.dataVersion != dataVer)
320     {
321         entryChanged++;
322         smbiosDir.dir[idIndex].common.dataVersion = dataVer;
323     }
324 
325     if (smbiosDir.dir[idIndex].common.timestamp != timeStamp)
326     {
327         entryChanged++;
328         smbiosDir.dir[idIndex].common.timestamp = timeStamp;
329     }
330     if (entryChanged == 0)
331     {
332         return false;
333     }
334     return true;
335 }
336 
337 int MDR_V2::findIdIndex(std::vector<uint8_t> dataInfo)
338 {
339     if (dataInfo.size() != sizeof(DataIdStruct))
340     {
341         phosphor::logging::log<phosphor::logging::level::ERR>(
342             "Length of dataInfo invalid");
343         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
344             InvalidId();
345     }
346     std::array<uint8_t, 16> arrayDataInfo;
347 
348     std::copy(dataInfo.begin(), dataInfo.end(), arrayDataInfo.begin());
349 
350     for (int index = 0; index < smbiosDir.dirEntries; index++)
351     {
352         int info = 0;
353         for (; info < arrayDataInfo.size(); info++)
354         {
355             if (arrayDataInfo[info] !=
356                 smbiosDir.dir[index].common.id.dataInfo[info])
357             {
358                 break;
359             }
360         }
361         if (info == arrayDataInfo.size())
362         {
363             return index;
364         }
365     }
366     throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::InvalidId();
367 }
368 
369 uint8_t MDR_V2::directoryEntries(uint8_t value)
370 {
371     value = smbiosDir.dirEntries;
372     return sdbusplus::xyz::openbmc_project::Smbios::server::MDR_V2::
373         directoryEntries(value);
374 }
375 
376 void MDR_V2::systemInfoUpdate()
377 {
378     std::string motherboardPath;
379     sdbusplus::message::message method =
380         bus.new_method_call("xyz.openbmc_project.EntityManager",
381                             "/xyz/openbmc_project/EntityManager",
382                             "xyz.openbmc_project.EntityManager", "ReScan");
383     try
384     {
385         sdbusplus::message::message reply = bus.call(method);
386         reply.read(motherboardPath);
387     }
388     catch (const sdbusplus::exception_t& e)
389     {
390         phosphor::logging::log<phosphor::logging::level::ERR>(
391             "Failed to query system motherboard",
392             phosphor::logging::entry("ERROR=%s", e.what()));
393     }
394 
395     cpus.clear();
396     int num = getTotalCpuSlot();
397     if (num == -1)
398     {
399         phosphor::logging::log<phosphor::logging::level::ERR>(
400             "get cpu total slot failed");
401         return;
402     }
403 
404     for (int index = 0; index < num; index++)
405     {
406         std::string path = cpuPath + std::to_string(index);
407         cpus.emplace_back(std::make_unique<phosphor::smbios::Cpu>(
408             bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage,
409             motherboardPath));
410     }
411 
412 #ifdef DIMM_DBUS
413 
414     dimms.clear();
415 
416     num = getTotalDimmSlot();
417     if (num == -1)
418     {
419         phosphor::logging::log<phosphor::logging::level::ERR>(
420             "get dimm total slot failed");
421         return;
422     }
423 
424     for (int index = 0; index < num; index++)
425     {
426         std::string path = dimmPath + std::to_string(index);
427         dimms.emplace_back(std::make_unique<phosphor::smbios::Dimm>(
428             bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage,
429             motherboardPath));
430     }
431 
432 #endif
433 
434     pcies.clear();
435     num = getTotalPcieSlot();
436     if (num == -1)
437     {
438         phosphor::logging::log<phosphor::logging::level::ERR>(
439             "get pcie total slot failed");
440         return;
441     }
442 
443     for (int index = 0; index < num; index++)
444     {
445         std::string path = pciePath + std::to_string(index);
446         pcies.emplace_back(std::make_unique<phosphor::smbios::Pcie>(
447             bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage,
448             motherboardPath));
449     }
450 
451     system.reset();
452     system = std::make_unique<System>(
453         bus, systemPath, smbiosDir.dir[smbiosDirIndex].dataStorage);
454 }
455 
456 int MDR_V2::getTotalCpuSlot()
457 {
458     uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
459     int num = 0;
460 
461     if (dataIn == nullptr)
462     {
463         phosphor::logging::log<phosphor::logging::level::ERR>(
464             "get cpu total slot failed - no storage data");
465         return -1;
466     }
467 
468     while (1)
469     {
470         dataIn = getSMBIOSTypePtr(dataIn, processorsType);
471         if (dataIn == nullptr)
472         {
473             break;
474         }
475         num++;
476         dataIn = smbiosNextPtr(dataIn);
477         if (dataIn == nullptr)
478         {
479             break;
480         }
481         if (num >= limitEntryLen)
482         {
483             break;
484         }
485     }
486 
487     return num;
488 }
489 
490 int MDR_V2::getTotalDimmSlot()
491 {
492     uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
493     uint8_t num = 0;
494 
495     if (dataIn == nullptr)
496     {
497         phosphor::logging::log<phosphor::logging::level::ERR>(
498             "Fail to get dimm total slot - no storage data");
499         return -1;
500     }
501 
502     while (1)
503     {
504         dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType);
505         if (dataIn == nullptr)
506         {
507             break;
508         }
509         num++;
510         dataIn = smbiosNextPtr(dataIn);
511         if (dataIn == nullptr)
512         {
513             break;
514         }
515         if (num >= limitEntryLen)
516         {
517             break;
518         }
519     }
520 
521     return num;
522 }
523 
524 int MDR_V2::getTotalPcieSlot()
525 {
526     uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
527     int num = 0;
528 
529     if (dataIn == nullptr)
530     {
531         phosphor::logging::log<phosphor::logging::level::ERR>(
532             "Fail to get total system slot - no storage data");
533         return -1;
534     }
535 
536     while (1)
537     {
538         dataIn = getSMBIOSTypePtr(dataIn, systemSlots);
539         if (dataIn == nullptr)
540         {
541             break;
542         }
543 
544         /* System slot type offset. Check if the slot is a PCIE slots. All
545          * PCIE slot type are hardcoded in a table.
546          */
547         if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end())
548         {
549             num++;
550         }
551         dataIn = smbiosNextPtr(dataIn);
552         if (dataIn == nullptr)
553         {
554             break;
555         }
556         if (num >= limitEntryLen)
557         {
558             break;
559         }
560     }
561 
562     return num;
563 }
564 
565 bool MDR_V2::agentSynchronizeData()
566 {
567     struct MDRSMBIOSHeader mdr2SMBIOS;
568     bool status = readDataFromFlash(&mdr2SMBIOS,
569                                     smbiosDir.dir[smbiosDirIndex].dataStorage);
570     if (!status)
571     {
572         phosphor::logging::log<phosphor::logging::level::ERR>(
573             "agent data sync failed - read data from flash failed");
574         return false;
575     }
576     else
577     {
578         systemInfoUpdate();
579         smbiosDir.dir[smbiosDirIndex].common.dataVersion = mdr2SMBIOS.dirVer;
580         smbiosDir.dir[smbiosDirIndex].common.timestamp = mdr2SMBIOS.timestamp;
581         smbiosDir.dir[smbiosDirIndex].common.size = mdr2SMBIOS.dataSize;
582         smbiosDir.dir[smbiosDirIndex].stage = MDR2SMBIOSStatusEnum::mdr2Loaded;
583         smbiosDir.dir[smbiosDirIndex].lock = MDR2DirLockEnum::mdr2DirUnlock;
584     }
585     return true;
586 }
587 
588 std::vector<uint32_t> MDR_V2::synchronizeDirectoryCommonData(uint8_t idIndex,
589                                                              uint32_t size)
590 {
591     std::chrono::microseconds usec(
592         defaultTimeout); // default lock time out is 2s
593     std::vector<uint32_t> result;
594     smbiosDir.dir[idIndex].common.size = size;
595     result.push_back(smbiosDir.dir[idIndex].common.dataSetSize);
596     result.push_back(smbiosDir.dir[idIndex].common.dataVersion);
597     result.push_back(smbiosDir.dir[idIndex].common.timestamp);
598 
599     timer.expires_after(usec);
600     timer.async_wait([this](boost::system::error_code ec) {
601         if (ec || this == nullptr)
602         {
603             phosphor::logging::log<phosphor::logging::level::ERR>(
604                 "Timer Error!");
605             return;
606         }
607         agentSynchronizeData();
608     });
609     return result;
610 }
611 
612 std::vector<boost::container::flat_map<std::string, RecordVariant>>
613     MDR_V2::getRecordType(size_t type)
614 {
615 
616     std::vector<boost::container::flat_map<std::string, RecordVariant>> ret;
617     if (type == memoryDeviceType)
618     {
619 
620         uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
621 
622         if (dataIn == nullptr)
623         {
624             throw std::runtime_error("Data not populated");
625         }
626 
627         do
628         {
629             dataIn =
630                 getSMBIOSTypePtr(dataIn, memoryDeviceType, sizeof(MemoryInfo));
631             if (dataIn == nullptr)
632             {
633                 break;
634             }
635             boost::container::flat_map<std::string, RecordVariant>& record =
636                 ret.emplace_back();
637 
638             auto memoryInfo = reinterpret_cast<MemoryInfo*>(dataIn);
639 
640             record["Type"] = memoryInfo->type;
641             record["Length"] = memoryInfo->length;
642             record["Handle"] = uint16_t(memoryInfo->handle);
643             record["Physical Memory Array Handle"] =
644                 uint16_t(memoryInfo->phyArrayHandle);
645             record["Memory Error Information Handle"] =
646                 uint16_t(memoryInfo->errInfoHandle);
647             record["Total Width"] = uint16_t(memoryInfo->totalWidth);
648             record["Data Width"] = uint16_t(memoryInfo->dataWidth);
649             record["Size"] = uint16_t(memoryInfo->size);
650             record["Form Factor"] = memoryInfo->formFactor;
651             record["Device Set"] = memoryInfo->deviceSet;
652             record["Device Locator"] = positionToString(
653                 memoryInfo->deviceLocator, memoryInfo->length, dataIn);
654             record["Bank Locator"] = positionToString(
655                 memoryInfo->bankLocator, memoryInfo->length, dataIn);
656             record["Memory Type"] = memoryInfo->memoryType;
657             record["Type Detail"] = uint16_t(memoryInfo->typeDetail);
658             record["Speed"] = uint16_t(memoryInfo->speed);
659             record["Manufacturer"] = positionToString(
660                 memoryInfo->manufacturer, memoryInfo->length, dataIn);
661             record["Serial Number"] = positionToString(
662                 memoryInfo->serialNum, memoryInfo->length, dataIn);
663             record["Asset Tag"] = positionToString(memoryInfo->assetTag,
664                                                    memoryInfo->length, dataIn);
665             record["Part Number"] = positionToString(
666                 memoryInfo->partNum, memoryInfo->length, dataIn);
667             record["Attributes"] = memoryInfo->attributes;
668             record["Extended Size"] = uint32_t(memoryInfo->extendedSize);
669             record["Configured Memory Speed"] =
670                 uint32_t(memoryInfo->confClockSpeed);
671             record["Minimum voltage"] = uint16_t(memoryInfo->minimumVoltage);
672             record["Maximum voltage"] = uint16_t(memoryInfo->maximumVoltage);
673             record["Configured voltage"] =
674                 uint16_t(memoryInfo->configuredVoltage);
675             record["Memory Technology"] = memoryInfo->memoryTechnology;
676             record["Memory Operating Mode Capabilty"] =
677                 uint16_t(memoryInfo->memoryOperatingModeCap);
678             record["Firmare Version"] = memoryInfo->firwareVersion;
679             record["Module Manufacturer ID"] =
680                 uint16_t(memoryInfo->modelManufId);
681             record["Module Product ID"] = uint16_t(memoryInfo->modelProdId);
682             record["Memory Subsystem Controller Manufacturer ID"] =
683                 uint16_t(memoryInfo->memSubConManufId);
684             record["Memory Subsystem Controller Product Id"] =
685                 uint16_t(memoryInfo->memSubConProdId);
686             record["Non-volatile Size"] = uint64_t(memoryInfo->nvSize);
687             record["Volatile Size"] = uint64_t(memoryInfo->volatileSize);
688             record["Cache Size"] = uint64_t(memoryInfo->cacheSize);
689             record["Logical Size"] = uint64_t(memoryInfo->logicalSize);
690         } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr);
691 
692         return ret;
693     }
694 
695     throw std::invalid_argument("Invalid record type");
696     return ret;
697 }
698 
699 } // namespace smbios
700 } // namespace phosphor
701