xref: /openbmc/smbios-mdr/src/dimm.cpp (revision 9a6ac03d71948d77207fd19258efac270d6b5389)
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 "dimm.hpp"
18  
19  #include "mdrv2.hpp"
20  
21  #include <boost/algorithm/string.hpp>
22  #include <phosphor-logging/elog-errors.hpp>
23  
24  #include <fstream>
25  #include <iostream>
26  #include <regex>
27  
28  namespace phosphor
29  {
30  namespace smbios
31  {
32  
33  #ifdef DIMM_ONLY_LOCATOR
34  bool onlyDimmLocationCode = true;
35  #else
36  bool onlyDimmLocationCode = false;
37  #endif
38  
39  using DeviceType =
40      sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::DeviceType;
41  
42  using EccType =
43      sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::Ecc;
44  
45  static constexpr uint16_t maxOldDimmSize = 0x7fff;
46  
47  static constexpr const char* filename =
48      "/usr/share/smbios-mdr/memoryLocationTable.json";
49  
memoryInfoUpdate(uint8_t * smbiosTableStorage,const std::string & motherboard)50  void Dimm::memoryInfoUpdate(uint8_t* smbiosTableStorage,
51                              const std::string& motherboard)
52  {
53      storage = smbiosTableStorage;
54      motherboardPath = motherboard;
55  
56      uint8_t* dataIn = storage;
57  
58      dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType);
59  
60      if (dataIn == nullptr)
61      {
62          return;
63      }
64      for (uint8_t index = 0; index < dimmNum; index++)
65      {
66          dataIn = smbiosNextPtr(dataIn);
67          if (dataIn == nullptr)
68          {
69              return;
70          }
71          dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType);
72          if (dataIn == nullptr)
73          {
74              return;
75          }
76      }
77  
78      auto memoryInfo = reinterpret_cast<struct MemoryInfo*>(dataIn);
79  
80      memoryDataWidth(memoryInfo->dataWidth);
81      memoryTotalWidth(memoryInfo->totalWidth);
82  
83      if (memoryInfo->size == maxOldDimmSize)
84      {
85          dimmSizeExt(memoryInfo->extendedSize);
86      }
87      else
88      {
89          dimmSize(memoryInfo->size);
90      }
91      // If the size is 0, no memory device is installed in the socket.
92      const auto isDimmPresent = memoryInfo->size > 0;
93      present(isDimmPresent);
94      functional(isDimmPresent);
95  
96      dimmDeviceLocator(memoryInfo->bankLocator, memoryInfo->deviceLocator,
97                        memoryInfo->length, dataIn);
98      dimmType(memoryInfo->memoryType);
99      dimmTypeDetail(memoryInfo->typeDetail);
100      maxMemorySpeedInMhz(memoryInfo->speed);
101      dimmManufacturer(memoryInfo->manufacturer, memoryInfo->length, dataIn);
102      dimmSerialNum(memoryInfo->serialNum, memoryInfo->length, dataIn);
103      dimmPartNum(memoryInfo->partNum, memoryInfo->length, dataIn);
104      memoryAttributes(memoryInfo->attributes);
105      dimmMedia(memoryInfo->memoryTechnology);
106      memoryConfiguredSpeedInMhz(memoryInfo->confClockSpeed);
107  
108      updateEccType(memoryInfo->phyArrayHandle);
109  
110      if (!motherboardPath.empty())
111      {
112          std::vector<std::tuple<std::string, std::string, std::string>> assocs;
113          assocs.emplace_back("chassis", "memories", motherboardPath);
114          association::associations(assocs);
115      }
116  
117      return;
118  }
119  
updateEccType(uint16_t exPhyArrayHandle)120  void Dimm::updateEccType(uint16_t exPhyArrayHandle)
121  {
122      uint8_t* dataIn = storage;
123  
124      while (dataIn != nullptr)
125      {
126          dataIn = getSMBIOSTypePtr(dataIn, physicalMemoryArrayType);
127          if (dataIn == nullptr)
128          {
129              phosphor::logging::log<phosphor::logging::level::ERR>(
130                  "Failed to get SMBIOS table type-16 data.");
131              return;
132          }
133  
134          auto info = reinterpret_cast<struct PhysicalMemoryArrayInfo*>(dataIn);
135          if (info->handle == exPhyArrayHandle)
136          {
137              std::map<uint8_t, EccType>::const_iterator it =
138                  dimmEccTypeMap.find(info->memoryErrorCorrection);
139              if (it == dimmEccTypeMap.end())
140              {
141                  ecc(EccType::NoECC);
142              }
143              else
144              {
145                  ecc(it->second);
146              }
147              return;
148          }
149  
150          dataIn = smbiosNextPtr(dataIn);
151      }
152      phosphor::logging::log<phosphor::logging::level::ERR>(
153          "Failed find the corresponding SMBIOS table type-16 data for dimm:",
154          phosphor::logging::entry("DIMM:%d", dimmNum));
155  }
156  
ecc(EccType value)157  EccType Dimm::ecc(EccType value)
158  {
159      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::ecc(
160          value);
161  }
162  
memoryDataWidth(uint16_t value)163  uint16_t Dimm::memoryDataWidth(uint16_t value)
164  {
165      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
166          memoryDataWidth(value);
167  }
168  
memoryTotalWidth(uint16_t value)169  uint16_t Dimm::memoryTotalWidth(uint16_t value)
170  {
171      return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
172          memoryTotalWidth(value);
173  }
174  
175  static constexpr uint16_t baseNewVersionDimmSize = 0x8000;
176  static constexpr uint16_t dimmSizeUnit = 1024;
dimmSize(const uint16_t size)177  void Dimm::dimmSize(const uint16_t size)
178  {
179      uint32_t result = size & maxOldDimmSize;
180      if (0 == (size & baseNewVersionDimmSize))
181      {
182          result = result * dimmSizeUnit;
183      }
184      memorySizeInKB(result);
185  }
186  
dimmSizeExt(uint32_t size)187  void Dimm::dimmSizeExt(uint32_t size)
188  {
189      size = size * dimmSizeUnit;
190      memorySizeInKB(size);
191  }
192  
memorySizeInKB(size_t value)193  size_t Dimm::memorySizeInKB(size_t value)
194  {
195      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
196          memorySizeInKB(value);
197  }
198  
dimmDeviceLocator(const uint8_t bankLocatorPositionNum,const uint8_t deviceLocatorPositionNum,const uint8_t structLen,uint8_t * dataIn)199  void Dimm::dimmDeviceLocator(const uint8_t bankLocatorPositionNum,
200                               const uint8_t deviceLocatorPositionNum,
201                               const uint8_t structLen, uint8_t* dataIn)
202  {
203      std::string deviceLocator =
204          positionToString(deviceLocatorPositionNum, structLen, dataIn);
205      std::string bankLocator =
206          positionToString(bankLocatorPositionNum, structLen, dataIn);
207  
208      std::string result;
209      if (bankLocator.empty() || onlyDimmLocationCode)
210      {
211          result = deviceLocator;
212      }
213      else
214      {
215          result = bankLocator + " " + deviceLocator;
216      }
217  
218      memoryDeviceLocator(result);
219  
220      locationCode(result);
221      const std::string substrCpu = "CPU";
222      auto cpuPos = deviceLocator.find(substrCpu);
223  
224      auto data = parseConfigFile();
225  
226      if (!data.empty())
227      {
228          auto it = data.find(deviceLocator);
229  
230          if (it != data.end())
231          {
232              uint8_t memoryControllerValue =
233                  it.value()["MemoryController"].get<uint8_t>();
234              uint8_t socketValue = it.value()["Socket"].get<uint8_t>();
235              uint8_t slotValue = it.value()["Slot"].get<uint8_t>();
236              uint8_t channelValue = it.value()["Channel"].get<uint8_t>();
237  
238              socket(socketValue);
239              memoryController(memoryControllerValue);
240              slot(slotValue);
241              channel(channelValue);
242          }
243          else
244          {
245              socket(0);
246              memoryController(0);
247              slot(0);
248              channel(0);
249              phosphor::logging::log<phosphor::logging::level::ERR>(
250                  "Failed find the corresponding table for dimm ",
251                  phosphor::logging::entry("DIMM:%s", deviceLocator.c_str()));
252          }
253      }
254      else
255      {
256          if (cpuPos != std::string::npos)
257          {
258              std::string socketString =
259                  deviceLocator.substr(cpuPos + substrCpu.length(), 1);
260              try
261              {
262                  uint8_t socketNum =
263                      static_cast<uint8_t>(std::stoi(socketString) + 1);
264                  socket(socketNum);
265              }
266              catch (const sdbusplus::exception_t& ex)
267              {
268                  phosphor::logging::log<phosphor::logging::level::ERR>(
269                      "std::stoi operation failed ",
270                      phosphor::logging::entry("ERROR=%s", ex.what()));
271              }
272          }
273      }
274  
275      const std::string substrDimm = "DIMM";
276      auto dimmPos = deviceLocator.find(substrDimm);
277  
278      if (dimmPos != std::string::npos)
279      {
280          std::string slotString =
281              deviceLocator.substr(dimmPos + substrDimm.length() + 1);
282          /* slotString is extracted from substrDimm (DIMM_A) if slotString is
283           * single alphabet like A, B , C.. then assign ASCII value of slotString
284           * to slot */
285          if ((std::regex_match(slotString, std::regex("^[A-Za-z]+$"))) &&
286              (slotString.length() == 1))
287          {
288              slot(static_cast<uint8_t>(toupper(slotString[0])));
289          }
290      }
291  }
292  
memoryDeviceLocator(std::string value)293  std::string Dimm::memoryDeviceLocator(std::string value)
294  {
295      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
296          memoryDeviceLocator(value);
297  }
298  
dimmType(const uint8_t type)299  void Dimm::dimmType(const uint8_t type)
300  {
301      std::map<uint8_t, DeviceType>::const_iterator it = dimmTypeTable.find(type);
302      if (it == dimmTypeTable.end())
303      {
304          memoryType(DeviceType::Unknown);
305      }
306      else
307      {
308          memoryType(it->second);
309      }
310  }
311  
memoryType(DeviceType value)312  DeviceType Dimm::memoryType(DeviceType value)
313  {
314      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
315          memoryType(value);
316  }
317  
dimmMedia(const uint8_t type)318  void Dimm::dimmMedia(const uint8_t type)
319  {
320      std::map<uint8_t, MemoryTechType>::const_iterator it =
321          dimmMemoryTechTypeMap.find(type);
322      if (it == dimmMemoryTechTypeMap.end())
323      {
324          memoryMedia(MemoryTechType::Unknown);
325      }
326      else
327      {
328          memoryMedia(it->second);
329      }
330  }
331  
memoryMedia(MemoryTechType value)332  MemoryTechType Dimm::memoryMedia(MemoryTechType value)
333  {
334      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
335          memoryMedia(value);
336  }
337  
dimmTypeDetail(uint16_t detail)338  void Dimm::dimmTypeDetail(uint16_t detail)
339  {
340      std::string result;
341      for (uint8_t index = 0; index < (8 * sizeof(detail)); index++)
342      {
343          if (detail & 0x01)
344          {
345              result += detailTable[index];
346          }
347          detail >>= 1;
348      }
349      memoryTypeDetail(result);
350  }
351  
memoryTypeDetail(std::string value)352  std::string Dimm::memoryTypeDetail(std::string value)
353  {
354      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
355          memoryTypeDetail(value);
356  }
357  
maxMemorySpeedInMhz(uint16_t value)358  uint16_t Dimm::maxMemorySpeedInMhz(uint16_t value)
359  {
360      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
361          maxMemorySpeedInMhz(value);
362  }
363  
dimmManufacturer(const uint8_t positionNum,const uint8_t structLen,uint8_t * dataIn)364  void Dimm::dimmManufacturer(const uint8_t positionNum, const uint8_t structLen,
365                              uint8_t* dataIn)
366  {
367      std::string result = positionToString(positionNum, structLen, dataIn);
368  
369      if (result == "NO DIMM")
370      {
371          // No dimm presence so making manufacturer value as "" (instead of
372          // NO DIMM - as there won't be any manufacturer for DIMM which is not
373          // present).
374          result = "";
375      }
376      manufacturer(result);
377  }
378  
manufacturer(std::string value)379  std::string Dimm::manufacturer(std::string value)
380  {
381      return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
382          Asset::manufacturer(value);
383  }
384  
present(bool value)385  bool Dimm::present(bool value)
386  {
387      return sdbusplus::server::xyz::openbmc_project::inventory::Item::present(
388          value);
389  }
390  
dimmSerialNum(const uint8_t positionNum,const uint8_t structLen,uint8_t * dataIn)391  void Dimm::dimmSerialNum(const uint8_t positionNum, const uint8_t structLen,
392                           uint8_t* dataIn)
393  {
394      std::string result = positionToString(positionNum, structLen, dataIn);
395  
396      serialNumber(result);
397  }
398  
serialNumber(std::string value)399  std::string Dimm::serialNumber(std::string value)
400  {
401      return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
402          Asset::serialNumber(value);
403  }
404  
dimmPartNum(const uint8_t positionNum,const uint8_t structLen,uint8_t * dataIn)405  void Dimm::dimmPartNum(const uint8_t positionNum, const uint8_t structLen,
406                         uint8_t* dataIn)
407  {
408      std::string result = positionToString(positionNum, structLen, dataIn);
409  
410      // Part number could contain spaces at the end. Eg: "abcd123  ". Since its
411      // unnecessary, we should remove them.
412      boost::algorithm::trim_right(result);
413      partNumber(result);
414  }
415  
partNumber(std::string value)416  std::string Dimm::partNumber(std::string value)
417  {
418      return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
419          Asset::partNumber(value);
420  }
421  
locationCode(std::string value)422  std::string Dimm::locationCode(std::string value)
423  {
424      return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
425          LocationCode::locationCode(value);
426  }
427  
memoryAttributes(size_t value)428  size_t Dimm::memoryAttributes(size_t value)
429  {
430      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
431          memoryAttributes(value);
432  }
433  
slot(uint8_t value)434  uint8_t Dimm::slot(uint8_t value)
435  {
436      return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
437          MemoryLocation::slot(value);
438  }
439  
memoryController(uint8_t value)440  uint8_t Dimm::memoryController(uint8_t value)
441  {
442      return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
443          MemoryLocation::memoryController(value);
444  }
445  
channel(uint8_t value)446  uint8_t Dimm::channel(uint8_t value)
447  {
448      return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
449          MemoryLocation::channel(value);
450  }
451  
socket(uint8_t value)452  uint8_t Dimm::socket(uint8_t value)
453  {
454      return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
455          MemoryLocation::socket(value);
456  }
457  
memoryConfiguredSpeedInMhz(uint16_t value)458  uint16_t Dimm::memoryConfiguredSpeedInMhz(uint16_t value)
459  {
460      return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
461          memoryConfiguredSpeedInMhz(value);
462  }
463  
functional(bool value)464  bool Dimm::functional(bool value)
465  {
466      return sdbusplus::server::xyz::openbmc_project::state::decorator::
467          OperationalStatus::functional(value);
468  }
469  
parseConfigFile()470  Json Dimm::parseConfigFile()
471  {
472      std::ifstream memoryLocationFile(filename);
473  
474      if (!memoryLocationFile.is_open())
475      {
476          phosphor::logging::log<phosphor::logging::level::ERR>(
477              "config JSON file not found, FILENAME ",
478              phosphor::logging::entry("%s", filename));
479          return {};
480      }
481  
482      auto data = Json::parse(memoryLocationFile, nullptr, false);
483      if (data.is_discarded())
484      {
485          phosphor::logging::log<phosphor::logging::level::ERR>(
486              "config readings JSON parser failure");
487          return {};
488      }
489  
490      return data;
491  }
492  
493  } // namespace smbios
494  } // namespace phosphor
495