xref: /openbmc/smbios-mdr/src/dimm.cpp (revision 8f789c3ac4fa47e02bc4862c50390dbf043be80c)
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(memoryControllerValue);
239             memoryController(socketValue);
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