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