xref: /openbmc/smbios-mdr/src/mdrv2.cpp (revision 1d73dccc89f0bb9d1dce3543e5af6b3e3087d5f4)
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  
getDirectoryInformation(uint8_t dirIndex)34  std::vector<uint8_t> MDRV2::getDirectoryInformation(uint8_t dirIndex)
35  {
36      std::vector<uint8_t> responseDir;
37  
38      std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary);
39      if (!smbiosFile.good())
40      {
41          phosphor::logging::log<phosphor::logging::level::ERR>(
42              "Read data from flash error - Open MDRV2 table file failure");
43          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
44              InvalidParameter();
45      }
46      if (dirIndex > smbiosDir.dirEntries)
47      {
48          responseDir.push_back(0);
49          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
50              InvalidParameter();
51      }
52      responseDir.push_back(mdr2Version);
53      responseDir.push_back(smbiosDir.dirVersion);
54      uint8_t returnedEntries = smbiosDir.dirEntries - dirIndex;
55      responseDir.push_back(returnedEntries);
56      if ((dirIndex + returnedEntries) >= smbiosDir.dirEntries)
57      {
58          responseDir.push_back(0);
59      }
60      else
61      {
62          responseDir.push_back(
63              smbiosDir.dirEntries - dirIndex - returnedEntries);
64      }
65      for (uint8_t index = dirIndex; index < smbiosDir.dirEntries; index++)
66      {
67          for (uint8_t indexId = 0; indexId < sizeof(DataIdStruct); indexId++)
68          {
69              responseDir.push_back(
70                  smbiosDir.dir[index].common.id.dataInfo[indexId]);
71          }
72      }
73  
74      return responseDir;
75  }
76  
smbiosIsAvailForUpdate(uint8_t index)77  bool MDRV2::smbiosIsAvailForUpdate(uint8_t index)
78  {
79      bool ret = false;
80      if (index >= maxDirEntries)
81      {
82          return ret;
83      }
84  
85      switch (smbiosDir.dir[index].stage)
86      {
87          case MDR2SMBIOSStatusEnum::mdr2Updating:
88              ret = false;
89              break;
90  
91          case MDR2SMBIOSStatusEnum::mdr2Init:
92              // This *looks* like there should be a break statement here,
93              // as the effects of the previous statement are a noop given
94              // the following code that this falls through to.
95              // We've elected not to change it, though, since it's been
96              // this way since the old generation, and it would affect
97              // the way the code functions.
98              // If it ain't broke, don't fix it.
99  
100          case MDR2SMBIOSStatusEnum::mdr2Loaded:
101          case MDR2SMBIOSStatusEnum::mdr2Updated:
102              if (smbiosDir.dir[index].lock == MDR2DirLockEnum::mdr2DirLock)
103              {
104                  ret = false;
105              }
106              else
107              {
108                  ret = true;
109              }
110              break;
111  
112          default:
113              break;
114      }
115  
116      return ret;
117  }
118  
getDataOffer()119  std::vector<uint8_t> MDRV2::getDataOffer()
120  {
121      std::vector<uint8_t> offer(sizeof(DataIdStruct));
122      if (smbiosIsAvailForUpdate(0))
123      {
124          std::copy(smbiosDir.dir[0].common.id.dataInfo,
125                    &smbiosDir.dir[0].common.id.dataInfo[16], offer.data());
126      }
127      else
128      {
129          phosphor::logging::log<phosphor::logging::level::ERR>(
130              "smbios is not ready for update");
131          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
132              UpdateInProgress();
133      }
134      return offer;
135  }
136  
smbiosValidFlag(uint8_t index)137  inline uint8_t MDRV2::smbiosValidFlag(uint8_t index)
138  {
139      FlagStatus ret = FlagStatus::flagIsInvalid;
140      MDR2SMBIOSStatusEnum stage = smbiosDir.dir[index].stage;
141      MDR2DirLockEnum lock = smbiosDir.dir[index].lock;
142  
143      switch (stage)
144      {
145          case MDR2SMBIOSStatusEnum::mdr2Loaded:
146          case MDR2SMBIOSStatusEnum::mdr2Updated:
147              if (lock == MDR2DirLockEnum::mdr2DirLock)
148              {
149                  ret = FlagStatus::flagIsLocked; // locked
150              }
151              else
152              {
153                  ret = FlagStatus::flagIsValid; // valid
154              }
155              break;
156  
157          case MDR2SMBIOSStatusEnum::mdr2Updating:
158          case MDR2SMBIOSStatusEnum::mdr2Init:
159              ret = FlagStatus::flagIsInvalid; // invalid
160              break;
161  
162          default:
163              break;
164      }
165  
166      return static_cast<uint8_t>(ret);
167  }
168  
169  // If source variable size is 4 bytes (uint32_t) and destination is Vector type
170  // is 1 byte (uint8_t), then by using this API can copy data byte by byte. For
171  // Example copying data from smbiosDir.dir[idIndex].common.size and
172  // smbiosDir.dir[idIndex].common.timestamp to vector variable responseInfo
173  template <typename T>
appendReversed(std::vector<uint8_t> & vector,const T & value)174  void appendReversed(std::vector<uint8_t>& vector, const T& value)
175  {
176      auto data = reinterpret_cast<const uint8_t*>(&value);
177      std::reverse_copy(data, data + sizeof(value), std::back_inserter(vector));
178  }
179  
getDataInformation(uint8_t idIndex)180  std::vector<uint8_t> MDRV2::getDataInformation(uint8_t idIndex)
181  {
182      std::vector<uint8_t> responseInfo;
183      responseInfo.push_back(mdr2Version);
184  
185      if (idIndex >= maxDirEntries)
186      {
187          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
188              InvalidParameter();
189      }
190  
191      for (uint8_t index = 0; index < sizeof(DataIdStruct); index++)
192      {
193          responseInfo.push_back(
194              smbiosDir.dir[idIndex].common.id.dataInfo[index]);
195      }
196  
197      responseInfo.push_back(smbiosValidFlag(idIndex));
198      appendReversed(responseInfo, smbiosDir.dir[idIndex].common.size);
199      responseInfo.push_back(smbiosDir.dir[idIndex].common.dataVersion);
200      appendReversed(responseInfo, smbiosDir.dir[idIndex].common.timestamp);
201  
202      return responseInfo;
203  }
204  
readDataFromFlash(MDRSMBIOSHeader * mdrHdr,uint8_t * data)205  bool MDRV2::readDataFromFlash(MDRSMBIOSHeader* mdrHdr, uint8_t* data)
206  {
207      if (mdrHdr == nullptr)
208      {
209          phosphor::logging::log<phosphor::logging::level::ERR>(
210              "Read data from flash error - Invalid mdr header");
211          return false;
212      }
213      if (data == nullptr)
214      {
215          phosphor::logging::log<phosphor::logging::level::ERR>(
216              "Read data from flash error - Invalid data point");
217          return false;
218      }
219      std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary);
220      if (!smbiosFile.good())
221      {
222          phosphor::logging::log<phosphor::logging::level::ERR>(
223              "Read data from flash error - Open MDRV2 table file failure");
224          return false;
225      }
226      smbiosFile.clear();
227      smbiosFile.seekg(0, std::ios_base::end);
228      size_t fileLength = smbiosFile.tellg();
229      smbiosFile.seekg(0, std::ios_base::beg);
230      if (fileLength < sizeof(MDRSMBIOSHeader))
231      {
232          phosphor::logging::log<phosphor::logging::level::ERR>(
233              "MDR V2 file size is smaller than mdr header");
234          return false;
235      }
236      smbiosFile.read(reinterpret_cast<char*>(mdrHdr), sizeof(MDRSMBIOSHeader));
237      if (mdrHdr->dataSize > smbiosTableStorageSize)
238      {
239          phosphor::logging::log<phosphor::logging::level::ERR>(
240              "Data size out of limitation");
241          smbiosFile.close();
242          return false;
243      }
244      fileLength -= sizeof(MDRSMBIOSHeader);
245      if (fileLength < mdrHdr->dataSize)
246      {
247          smbiosFile.read(reinterpret_cast<char*>(data), fileLength);
248      }
249      else
250      {
251          smbiosFile.read(reinterpret_cast<char*>(data), mdrHdr->dataSize);
252      }
253      smbiosFile.close();
254      return true;
255  }
256  
sendDirectoryInformation(uint8_t dirVersion,uint8_t dirIndex,uint8_t returnedEntries,uint8_t remainingEntries,std::vector<uint8_t> dirEntry)257  bool MDRV2::sendDirectoryInformation(
258      uint8_t dirVersion, uint8_t dirIndex, uint8_t returnedEntries,
259      uint8_t remainingEntries, std::vector<uint8_t> dirEntry)
260  {
261      bool terminate = false;
262      if ((dirIndex >= maxDirEntries) || (returnedEntries < 1))
263      {
264          phosphor::logging::log<phosphor::logging::level::ERR>(
265              "Send Dir info failed - input parameter invalid");
266          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
267              InvalidParameter();
268      }
269      if ((static_cast<size_t>(returnedEntries) * sizeof(DataIdStruct)) !=
270          dirEntry.size())
271      {
272          phosphor::logging::log<phosphor::logging::level::ERR>(
273              "Directory size invalid");
274          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
275              InvalidParameter();
276      }
277      if (dirVersion == smbiosDir.dirVersion)
278      {
279          terminate = true;
280      }
281      else
282      {
283          if (remainingEntries > 0)
284          {
285              terminate = false;
286          }
287          else
288          {
289              terminate = true;
290              smbiosDir.dirVersion = dirVersion;
291          }
292          uint8_t idIndex = dirIndex;
293          smbiosDir.dirEntries = returnedEntries;
294  
295          uint8_t* pData = dirEntry.data();
296          if (pData == nullptr)
297          {
298              return false;
299          }
300          for (uint8_t index = 0; index < returnedEntries; index++)
301          {
302              auto data = reinterpret_cast<const DataIdStruct*>(pData);
303              std::copy(data->dataInfo, data->dataInfo + sizeof(DataIdStruct),
304                        smbiosDir.dir[idIndex + index].common.id.dataInfo);
305              pData += sizeof(DataIdStruct);
306          }
307      }
308      return terminate;
309  }
310  
sendDataInformation(uint8_t idIndex,uint8_t,uint32_t dataLen,uint32_t dataVer,uint32_t timeStamp)311  bool MDRV2::sendDataInformation(uint8_t idIndex, uint8_t /* flag */,
312                                  uint32_t dataLen, uint32_t dataVer,
313                                  uint32_t timeStamp)
314  {
315      if (idIndex >= maxDirEntries)
316      {
317          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
318              InvalidParameter();
319      }
320      int entryChanged = 0;
321      if (smbiosDir.dir[idIndex].common.dataSetSize != dataLen)
322      {
323          entryChanged++;
324          smbiosDir.dir[idIndex].common.dataSetSize = dataLen;
325      }
326  
327      if (smbiosDir.dir[idIndex].common.dataVersion != dataVer)
328      {
329          entryChanged++;
330          smbiosDir.dir[idIndex].common.dataVersion = dataVer;
331      }
332  
333      if (smbiosDir.dir[idIndex].common.timestamp != timeStamp)
334      {
335          entryChanged++;
336          smbiosDir.dir[idIndex].common.timestamp = timeStamp;
337      }
338      if (entryChanged == 0)
339      {
340          return false;
341      }
342      return true;
343  }
344  
findIdIndex(std::vector<uint8_t> dataInfo)345  int MDRV2::findIdIndex(std::vector<uint8_t> dataInfo)
346  {
347      if (dataInfo.size() != sizeof(DataIdStruct))
348      {
349          phosphor::logging::log<phosphor::logging::level::ERR>(
350              "Length of dataInfo invalid");
351          throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
352              InvalidId();
353      }
354      std::array<uint8_t, 16> arrayDataInfo;
355  
356      std::copy(dataInfo.begin(), dataInfo.end(), arrayDataInfo.begin());
357  
358      for (int index = 0; index < smbiosDir.dirEntries; index++)
359      {
360          size_t info = 0;
361          for (; info < arrayDataInfo.size(); info++)
362          {
363              if (arrayDataInfo[info] !=
364                  smbiosDir.dir[index].common.id.dataInfo[info])
365              {
366                  break;
367              }
368          }
369          if (info == arrayDataInfo.size())
370          {
371              return index;
372          }
373      }
374      throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::InvalidId();
375  }
376  
directoryEntries(uint8_t value)377  uint8_t MDRV2::directoryEntries(uint8_t value)
378  {
379      std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary);
380      if (!smbiosFile.good())
381      {
382          phosphor::logging::log<phosphor::logging::level::ERR>(
383              "Read data from flash error - Open MDRV2 table file failure");
384          value = 0;
385      }
386      else
387      {
388          value = smbiosDir.dirEntries;
389      }
390      return sdbusplus::server::xyz::openbmc_project::smbios::MDRV2::
391          directoryEntries(value);
392  }
393  
systemInfoUpdate()394  void MDRV2::systemInfoUpdate()
395  {
396      // By default, look for System interface on any system/board/* object
397      std::string mapperAncestorPath = smbiosInventoryPath;
398      std::string matchParentPath = smbiosInventoryPath + "/board/";
399      bool requireExactMatch = false;
400  
401      // If customized, look for System on only that custom object
402      if (smbiosInventoryPath != defaultInventoryPath)
403      {
404          std::filesystem::path path(smbiosInventoryPath);
405  
406          // Search under parent to find exact match for self
407          mapperAncestorPath = path.parent_path().string();
408          matchParentPath = mapperAncestorPath;
409          requireExactMatch = true;
410      }
411  
412      std::string motherboardPath;
413      auto method = bus->new_method_call(mapperBusName, mapperPath,
414                                         mapperInterface, "GetSubTreePaths");
415      method.append(mapperAncestorPath);
416      method.append(0);
417  
418      // If customized, also accept Board as anchor, not just System
419      std::vector<std::string> desiredInterfaces{systemInterface};
420      if (requireExactMatch)
421      {
422          desiredInterfaces.emplace_back(boardInterface);
423      }
424      method.append(desiredInterfaces);
425  
426      try
427      {
428          std::vector<std::string> paths;
429          sdbusplus::message_t reply = bus->call(method);
430          reply.read(paths);
431  
432          size_t pathsCount = paths.size();
433          for (size_t i = 0; i < pathsCount; ++i)
434          {
435              if (requireExactMatch && (paths[i] != smbiosInventoryPath))
436              {
437                  continue;
438              }
439  
440              motherboardPath = std::move(paths[i]);
441              break;
442          }
443      }
444      catch (const sdbusplus::exception_t& e)
445      {
446          lg2::error(
447              "Exception while trying to find Inventory anchor object for SMBIOS content {I}: {E}",
448              "I", smbiosInventoryPath, "E", e.what());
449          phosphor::logging::log<phosphor::logging::level::ERR>(
450              "Failed to query system motherboard",
451              phosphor::logging::entry("ERROR=%s", e.what()));
452      }
453  
454      if (motherboardPath.empty())
455      {
456          phosphor::logging::log<phosphor::logging::level::ERR>(
457              "Failed to get system motherboard dbus path. Setting up a "
458              "match rule");
459  
460          if (motherboardConfigMatch)
461          {
462              lg2::info("Motherboard match rule already exists");
463          }
464          else
465          {
466              motherboardConfigMatch = std::make_unique<sdbusplus::bus::match_t>(
467                  *bus,
468                  sdbusplus::bus::match::rules::interfacesAdded() +
469                      sdbusplus::bus::match::rules::argNpath(0, matchParentPath),
470                  [this, requireExactMatch](sdbusplus::message_t& msg) {
471                      sdbusplus::message::object_path objectName;
472                      boost::container::flat_map<
473                          std::string,
474                          boost::container::flat_map<
475                              std::string, std::variant<std::string, uint64_t>>>
476                          msgData;
477                      msg.read(objectName, msgData);
478                      bool gotMatch = false;
479  
480                      if (msgData.contains(systemInterface))
481                      {
482                          lg2::info("Successful match on system interface");
483                          gotMatch = true;
484                      }
485  
486                      // If customized, also accept Board as anchor, not just
487                      // System
488                      if (requireExactMatch && msgData.contains(boardInterface))
489                      {
490                          lg2::info("Successful match on board interface");
491                          gotMatch = true;
492                      }
493  
494                      if (gotMatch)
495                      {
496                          // There is a race condition here: our desired interface
497                          // has just been created, triggering the D-Bus callback,
498                          // but Object Mapper has not been told of it yet. The
499                          // mapper must also add it. Stall for time, so it can.
500                          sleep(2);
501                          systemInfoUpdate();
502                      }
503                  });
504          }
505      }
506      else
507      {
508  #ifdef ASSOC_TRIM_PATH
509          // When enabled, chop off last component of motherboardPath, to trim one
510          // layer, so that associations are built to the underlying chassis
511          // itself, not the system boards in the chassis. This is for
512          // compatibility with traditional systems which only have one
513          // motherboard per chassis.
514          std::filesystem::path foundPath(motherboardPath);
515          motherboardPath = foundPath.parent_path().string();
516  #endif
517  
518          lg2::info("Found Inventory anchor object for SMBIOS content {I}: {M}",
519                    "I", smbiosInventoryPath, "M", motherboardPath);
520      }
521  
522      lg2::info("Using Inventory anchor object for SMBIOS content {I}: {M}", "I",
523                smbiosInventoryPath, "M", motherboardPath);
524  
525      std::optional<size_t> num = getTotalCpuSlot();
526      if (!num)
527      {
528          phosphor::logging::log<phosphor::logging::level::ERR>(
529              "get cpu total slot failed");
530          return;
531      }
532  
533      // In case the new size is smaller than old, trim the vector
534      if (*num < cpus.size())
535      {
536          cpus.resize(*num);
537      }
538  
539      for (unsigned int index = 0; index < *num; index++)
540      {
541          std::string path =
542              smbiosInventoryPath + cpuSuffix + std::to_string(index);
543          if (index + 1 > cpus.size())
544          {
545              cpus.emplace_back(std::make_unique<phosphor::smbios::Cpu>(
546                  *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage,
547                  motherboardPath));
548          }
549          else
550          {
551              cpus[index]->infoUpdate(smbiosDir.dir[smbiosDirIndex].dataStorage,
552                                      motherboardPath);
553          }
554      }
555  
556  #ifdef DIMM_DBUS
557  
558      num = getTotalDimmSlot();
559      if (!num)
560      {
561          phosphor::logging::log<phosphor::logging::level::ERR>(
562              "get dimm total slot failed");
563          return;
564      }
565  
566      // In case the new size is smaller than old, trim the vector
567      if (*num < dimms.size())
568      {
569          dimms.resize(*num);
570      }
571  
572      for (unsigned int index = 0; index < *num; index++)
573      {
574          std::string path =
575              smbiosInventoryPath + dimmSuffix + std::to_string(index);
576          if (index + 1 > dimms.size())
577          {
578              dimms.emplace_back(std::make_unique<phosphor::smbios::Dimm>(
579                  *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage,
580                  motherboardPath));
581          }
582          else
583          {
584              dimms[index]->memoryInfoUpdate(
585                  smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath);
586          }
587      }
588  
589  #endif
590  
591      num = getTotalPcieSlot();
592      if (!num)
593      {
594          phosphor::logging::log<phosphor::logging::level::ERR>(
595              "get pcie total slot failed");
596          return;
597      }
598  
599      // In case the new size is smaller than old, trim the vector
600      if (*num < pcies.size())
601      {
602          pcies.resize(*num);
603      }
604  
605      for (unsigned int index = 0; index < *num; index++)
606      {
607          std::string path =
608              smbiosInventoryPath + pcieSuffix + std::to_string(index);
609          if (index + 1 > pcies.size())
610          {
611              pcies.emplace_back(std::make_unique<phosphor::smbios::Pcie>(
612                  *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage,
613                  motherboardPath));
614          }
615          else
616          {
617              pcies[index]->pcieInfoUpdate(
618                  smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath);
619          }
620      }
621  
622      system.reset();
623      system = std::make_unique<System>(bus, smbiosInventoryPath + systemSuffix,
624                                        smbiosDir.dir[smbiosDirIndex].dataStorage,
625                                        smbiosFilePath);
626  }
627  
getTotalCpuSlot()628  std::optional<size_t> MDRV2::getTotalCpuSlot()
629  {
630      uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
631      size_t num = 0;
632  
633      if (dataIn == nullptr)
634      {
635          phosphor::logging::log<phosphor::logging::level::ERR>(
636              "get cpu total slot failed - no storage data");
637          return std::nullopt;
638      }
639  
640      while (1)
641      {
642          dataIn = getSMBIOSTypePtr(dataIn, processorsType);
643          if (dataIn == nullptr)
644          {
645              break;
646          }
647          num++;
648          dataIn = smbiosNextPtr(dataIn);
649          if (dataIn == nullptr)
650          {
651              break;
652          }
653          if (num >= limitEntryLen)
654          {
655              break;
656          }
657      }
658  
659      return num;
660  }
661  
getTotalDimmSlot()662  std::optional<size_t> MDRV2::getTotalDimmSlot()
663  {
664      uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
665      size_t num = 0;
666  
667      if (dataIn == nullptr)
668      {
669          phosphor::logging::log<phosphor::logging::level::ERR>(
670              "Fail to get dimm total slot - no storage data");
671          return std::nullopt;
672      }
673  
674      while (1)
675      {
676          dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType);
677          if (dataIn == nullptr)
678          {
679              break;
680          }
681          num++;
682          dataIn = smbiosNextPtr(dataIn);
683          if (dataIn == nullptr)
684          {
685              break;
686          }
687          if (num >= limitEntryLen)
688          {
689              break;
690          }
691      }
692  
693      return num;
694  }
695  
getTotalPcieSlot()696  std::optional<size_t> MDRV2::getTotalPcieSlot()
697  {
698      uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
699      size_t num = 0;
700  
701      if (dataIn == nullptr)
702      {
703          phosphor::logging::log<phosphor::logging::level::ERR>(
704              "Fail to get total system slot - no storage data");
705          return std::nullopt;
706      }
707  
708      while (1)
709      {
710          dataIn = getSMBIOSTypePtr(dataIn, systemSlots);
711          if (dataIn == nullptr)
712          {
713              break;
714          }
715  
716          /* System slot type offset. Check if the slot is a PCIE slots. All
717           * PCIE slot type are hardcoded in a table.
718           */
719          if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end())
720          {
721              num++;
722          }
723          dataIn = smbiosNextPtr(dataIn);
724          if (dataIn == nullptr)
725          {
726              break;
727          }
728          if (num >= limitEntryLen)
729          {
730              break;
731          }
732      }
733  
734      return num;
735  }
736  
checkSMBIOSVersion(uint8_t * dataIn)737  bool MDRV2::checkSMBIOSVersion(uint8_t* dataIn)
738  {
739      const std::string anchorString21 = "_SM_";
740      const std::string anchorString30 = "_SM3_";
741      std::string buffer(reinterpret_cast<const char*>(dataIn),
742                         smbiosTableStorageSize);
743  
744      auto it = std::search(std::begin(buffer), std::end(buffer),
745                            std::begin(anchorString21), std::end(anchorString21));
746      bool smbios21Found = it != std::end(buffer);
747      if (!smbios21Found)
748      {
749          phosphor::logging::log<phosphor::logging::level::INFO>(
750              "SMBIOS 2.1 Anchor String not found. Looking for SMBIOS 3.0");
751          it = std::search(std::begin(buffer), std::end(buffer),
752                           std::begin(anchorString30), std::end(anchorString30));
753          if (it == std::end(buffer))
754          {
755              phosphor::logging::log<phosphor::logging::level::ERR>(
756                  "SMBIOS 2.1 and 3.0 Anchor Strings not found");
757              return false;
758          }
759      }
760  
761      auto pos = std::distance(std::begin(buffer), it);
762      size_t length = smbiosTableStorageSize - pos;
763      uint8_t foundMajorVersion;
764      uint8_t foundMinorVersion;
765  
766      if (smbios21Found)
767      {
768          if (length < sizeof(EntryPointStructure21))
769          {
770              phosphor::logging::log<phosphor::logging::level::ERR>(
771                  "Invalid entry point structure for SMBIOS 2.1");
772              return false;
773          }
774  
775          auto epStructure =
776              reinterpret_cast<const EntryPointStructure21*>(&dataIn[pos]);
777          foundMajorVersion = epStructure->smbiosVersion.majorVersion;
778          foundMinorVersion = epStructure->smbiosVersion.minorVersion;
779      }
780      else
781      {
782          if (length < sizeof(EntryPointStructure30))
783          {
784              phosphor::logging::log<phosphor::logging::level::ERR>(
785                  "Invalid entry point structure for SMBIOS 3.0");
786              return false;
787          }
788  
789          auto epStructure =
790              reinterpret_cast<const EntryPointStructure30*>(&dataIn[pos]);
791          foundMajorVersion = epStructure->smbiosVersion.majorVersion;
792          foundMinorVersion = epStructure->smbiosVersion.minorVersion;
793      }
794      lg2::info("SMBIOS VERSION - {MAJOR}.{MINOR}", "MAJOR", foundMajorVersion,
795                "MINOR", foundMinorVersion);
796  
797      auto itr = std::find_if(
798          std::begin(supportedSMBIOSVersions), std::end(supportedSMBIOSVersions),
799          [&](SMBIOSVersion versionItr) {
800              return versionItr.majorVersion == foundMajorVersion &&
801                     versionItr.minorVersion == foundMinorVersion;
802          });
803      if (itr == std::end(supportedSMBIOSVersions))
804      {
805          return false;
806      }
807      return true;
808  }
809  
agentSynchronizeData()810  bool MDRV2::agentSynchronizeData()
811  {
812      struct MDRSMBIOSHeader mdr2SMBIOS;
813      bool status = readDataFromFlash(&mdr2SMBIOS,
814                                      smbiosDir.dir[smbiosDirIndex].dataStorage);
815      if (!status)
816      {
817          phosphor::logging::log<phosphor::logging::level::ERR>(
818              "agent data sync failed - read data from flash failed");
819          return false;
820      }
821  
822      if (!checkSMBIOSVersion(smbiosDir.dir[smbiosDirIndex].dataStorage))
823      {
824          phosphor::logging::log<phosphor::logging::level::ERR>(
825              "Unsupported SMBIOS table version");
826          return false;
827      }
828  
829      systemInfoUpdate();
830      smbiosDir.dir[smbiosDirIndex].common.dataVersion = mdr2SMBIOS.dirVer;
831      smbiosDir.dir[smbiosDirIndex].common.timestamp = mdr2SMBIOS.timestamp;
832      smbiosDir.dir[smbiosDirIndex].common.size = mdr2SMBIOS.dataSize;
833      smbiosDir.dir[smbiosDirIndex].stage = MDR2SMBIOSStatusEnum::mdr2Loaded;
834      smbiosDir.dir[smbiosDirIndex].lock = MDR2DirLockEnum::mdr2DirUnlock;
835  
836      return true;
837  }
838  
839  std::vector<uint32_t>
synchronizeDirectoryCommonData(uint8_t idIndex,uint32_t size)840      MDRV2::synchronizeDirectoryCommonData(uint8_t idIndex, uint32_t size)
841  {
842      std::chrono::microseconds usec(
843          defaultTimeout); // default lock time out is 2s
844      std::vector<uint32_t> result;
845      smbiosDir.dir[idIndex].common.size = size;
846      result.push_back(smbiosDir.dir[idIndex].common.dataSetSize);
847      result.push_back(smbiosDir.dir[idIndex].common.dataVersion);
848      result.push_back(smbiosDir.dir[idIndex].common.timestamp);
849  
850      timer.expires_after(usec);
851      timer.async_wait([this](boost::system::error_code ec) {
852          if (ec)
853          {
854              phosphor::logging::log<phosphor::logging::level::ERR>(
855                  "Timer Error!");
856              return;
857          }
858          agentSynchronizeData();
859      });
860      return result;
861  }
862  
863  std::vector<boost::container::flat_map<std::string, RecordVariant>>
getRecordType(size_t type)864      MDRV2::getRecordType(size_t type)
865  {
866      std::vector<boost::container::flat_map<std::string, RecordVariant>> ret;
867      if (type == memoryDeviceType)
868      {
869          uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
870  
871          if (dataIn == nullptr)
872          {
873              throw std::runtime_error("Data not populated");
874          }
875  
876          do
877          {
878              dataIn =
879                  getSMBIOSTypePtr(dataIn, memoryDeviceType, sizeof(MemoryInfo));
880              if (dataIn == nullptr)
881              {
882                  break;
883              }
884              boost::container::flat_map<std::string, RecordVariant>& record =
885                  ret.emplace_back();
886  
887              auto memoryInfo = reinterpret_cast<MemoryInfo*>(dataIn);
888  
889              record["Type"] = memoryInfo->type;
890              record["Length"] = memoryInfo->length;
891              record["Handle"] = uint16_t(memoryInfo->handle);
892              record["Physical Memory Array Handle"] =
893                  uint16_t(memoryInfo->phyArrayHandle);
894              record["Memory Error Information Handle"] =
895                  uint16_t(memoryInfo->errInfoHandle);
896              record["Total Width"] = uint16_t(memoryInfo->totalWidth);
897              record["Data Width"] = uint16_t(memoryInfo->dataWidth);
898              record["Size"] = uint16_t(memoryInfo->size);
899              record["Form Factor"] = memoryInfo->formFactor;
900              record["Device Set"] = memoryInfo->deviceSet;
901              record["Device Locator"] = positionToString(
902                  memoryInfo->deviceLocator, memoryInfo->length, dataIn);
903              record["Bank Locator"] = positionToString(
904                  memoryInfo->bankLocator, memoryInfo->length, dataIn);
905              record["Memory Type"] = memoryInfo->memoryType;
906              record["Type Detail"] = uint16_t(memoryInfo->typeDetail);
907              record["Speed"] = uint16_t(memoryInfo->speed);
908              record["Manufacturer"] = positionToString(
909                  memoryInfo->manufacturer, memoryInfo->length, dataIn);
910              record["Serial Number"] = positionToString(
911                  memoryInfo->serialNum, memoryInfo->length, dataIn);
912              record["Asset Tag"] = positionToString(memoryInfo->assetTag,
913                                                     memoryInfo->length, dataIn);
914              record["Part Number"] = positionToString(
915                  memoryInfo->partNum, memoryInfo->length, dataIn);
916              record["Attributes"] = uint32_t(memoryInfo->attributes);
917              record["Extended Size"] = uint32_t(memoryInfo->extendedSize);
918              record["Configured Memory Speed"] =
919                  uint32_t(memoryInfo->confClockSpeed);
920              record["Minimum voltage"] = uint16_t(memoryInfo->minimumVoltage);
921              record["Maximum voltage"] = uint16_t(memoryInfo->maximumVoltage);
922              record["Configured voltage"] =
923                  uint16_t(memoryInfo->configuredVoltage);
924              record["Memory Technology"] = memoryInfo->memoryTechnology;
925              record["Memory Operating Mode Capability"] =
926                  uint16_t(memoryInfo->memoryOperatingModeCap);
927              record["Firmware Version"] = memoryInfo->firwareVersion;
928              record["Module Manufacturer ID"] =
929                  uint16_t(memoryInfo->modelManufId);
930              record["Module Product ID"] = uint16_t(memoryInfo->modelProdId);
931              record["Memory Subsystem Controller Manufacturer ID"] =
932                  uint16_t(memoryInfo->memSubConManufId);
933              record["Memory Subsystem Controller Product Id"] =
934                  uint16_t(memoryInfo->memSubConProdId);
935              record["Non-volatile Size"] = uint64_t(memoryInfo->nvSize);
936              record["Volatile Size"] = uint64_t(memoryInfo->volatileSize);
937              record["Cache Size"] = uint64_t(memoryInfo->cacheSize);
938              record["Logical Size"] = uint64_t(memoryInfo->logicalSize);
939          } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr);
940  
941          return ret;
942      }
943  
944      throw std::invalid_argument("Invalid record type");
945      return ret;
946  }
947  
948  } // namespace smbios
949  } // namespace phosphor
950