xref: /openbmc/smbios-mdr/src/dimm.cpp (revision 6981b7ffb8a81bd1ea90c8deba8466accbe2693b)
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 
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     dimmManufacturer(memoryInfo->manufacturer, memoryInfo->length, dataIn);
103     dimmSerialNum(memoryInfo->serialNum, memoryInfo->length, dataIn);
104     dimmPartNum(memoryInfo->partNum, memoryInfo->length, dataIn);
105     memoryAttributes(memoryInfo->attributes);
106     dimmMedia(memoryInfo->memoryTechnology);
107     memoryConfiguredSpeedInMhz(memoryInfo->confClockSpeed);
108 
109     updateEccType(memoryInfo->phyArrayHandle);
110 
111     if (!motherboardPath.empty())
112     {
113         std::vector<std::tuple<std::string, std::string, std::string>> assocs;
114         assocs.emplace_back("chassis", "memories", motherboardPath);
115         association::associations(assocs);
116     }
117 
118     return;
119 }
120 
121 void Dimm::updateEccType(uint16_t exPhyArrayHandle)
122 {
123     uint8_t* dataIn = storage;
124 
125     while (dataIn != nullptr)
126     {
127         dataIn = getSMBIOSTypePtr(dataIn, physicalMemoryArrayType);
128         if (dataIn == nullptr)
129         {
130             lg2::error("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     lg2::error(
153         "Failed find the corresponding SMBIOS table type-16 data for dimm: {DIMM}",
154         "DIMM", dimmNum);
155 }
156 
157 EccType Dimm::ecc(EccType value)
158 {
159     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::ecc(
160         value);
161 }
162 
163 uint16_t Dimm::memoryDataWidth(uint16_t value)
164 {
165     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
166         memoryDataWidth(value);
167 }
168 
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;
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 
187 void Dimm::dimmSizeExt(uint32_t size)
188 {
189     size = size * dimmSizeUnit;
190     memorySizeInKB(size);
191 }
192 
193 size_t Dimm::memorySizeInKB(size_t value)
194 {
195     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
196         memorySizeInKB(value);
197 }
198 
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             lg2::error("Failed find the corresponding table for dimm {DIMM}",
250                        "DIMM", deviceLocator.c_str());
251         }
252     }
253     else
254     {
255         if (cpuPos != std::string::npos)
256         {
257             std::string socketString =
258                 deviceLocator.substr(cpuPos + substrCpu.length(), 1);
259             try
260             {
261                 uint8_t socketNum =
262                     static_cast<uint8_t>(std::stoi(socketString) + 1);
263                 socket(socketNum);
264             }
265             catch (const sdbusplus::exception_t& ex)
266             {
267                 lg2::error("std::stoi operation failed {ERROR}", "ERROR",
268                            ex.what());
269             }
270         }
271     }
272 
273     const std::string substrDimm = "DIMM";
274     auto dimmPos = deviceLocator.find(substrDimm);
275 
276     if (dimmPos != std::string::npos)
277     {
278         std::string slotString =
279             deviceLocator.substr(dimmPos + substrDimm.length() + 1);
280         /* slotString is extracted from substrDimm (DIMM_A) if slotString is
281          * single alphabet like A, B , C.. then assign ASCII value of slotString
282          * to slot */
283         if ((std::regex_match(slotString, std::regex("^[A-Za-z]+$"))) &&
284             (slotString.length() == 1))
285         {
286             slot(static_cast<uint8_t>(toupper(slotString[0])));
287         }
288     }
289 }
290 
291 std::string Dimm::memoryDeviceLocator(std::string value)
292 {
293     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
294         memoryDeviceLocator(value);
295 }
296 
297 void Dimm::dimmType(const uint8_t type)
298 {
299     std::map<uint8_t, DeviceType>::const_iterator it = dimmTypeTable.find(type);
300     if (it == dimmTypeTable.end())
301     {
302         memoryType(DeviceType::Unknown);
303     }
304     else
305     {
306         memoryType(it->second);
307     }
308 }
309 
310 DeviceType Dimm::memoryType(DeviceType value)
311 {
312     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
313         memoryType(value);
314 }
315 
316 void Dimm::dimmMedia(const uint8_t type)
317 {
318     std::map<uint8_t, MemoryTechType>::const_iterator it =
319         dimmMemoryTechTypeMap.find(type);
320     if (it == dimmMemoryTechTypeMap.end())
321     {
322         memoryMedia(MemoryTechType::Unknown);
323     }
324     else
325     {
326         memoryMedia(it->second);
327     }
328 }
329 
330 MemoryTechType Dimm::memoryMedia(MemoryTechType value)
331 {
332     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
333         memoryMedia(value);
334 }
335 
336 void Dimm::dimmTypeDetail(uint16_t detail)
337 {
338     std::string result;
339     for (uint8_t index = 0; index < (8 * sizeof(detail)); index++)
340     {
341         if (detail & 0x01)
342         {
343             result += detailTable[index];
344         }
345         detail >>= 1;
346     }
347     memoryTypeDetail(result);
348 }
349 
350 std::string Dimm::memoryTypeDetail(std::string value)
351 {
352     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
353         memoryTypeDetail(value);
354 }
355 
356 uint16_t Dimm::maxMemorySpeedInMhz(uint16_t value)
357 {
358     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
359         maxMemorySpeedInMhz(value);
360 }
361 
362 void Dimm::dimmManufacturer(const uint8_t positionNum, const uint8_t structLen,
363                             uint8_t* dataIn)
364 {
365     std::string result = positionToString(positionNum, structLen, dataIn);
366 
367     if (result == "NO DIMM")
368     {
369         // No dimm presence so making manufacturer value as "" (instead of
370         // NO DIMM - as there won't be any manufacturer for DIMM which is not
371         // present).
372         result = "";
373     }
374     manufacturer(result);
375 }
376 
377 std::string Dimm::manufacturer(std::string value)
378 {
379     return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
380         Asset::manufacturer(value);
381 }
382 
383 bool Dimm::present(bool value)
384 {
385     return sdbusplus::server::xyz::openbmc_project::inventory::Item::present(
386         value);
387 }
388 
389 void Dimm::dimmSerialNum(const uint8_t positionNum, const uint8_t structLen,
390                          uint8_t* dataIn)
391 {
392     std::string result = positionToString(positionNum, structLen, dataIn);
393 
394     serialNumber(result);
395 }
396 
397 std::string Dimm::serialNumber(std::string value)
398 {
399     return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
400         Asset::serialNumber(value);
401 }
402 
403 void Dimm::dimmPartNum(const uint8_t positionNum, const uint8_t structLen,
404                        uint8_t* dataIn)
405 {
406     std::string result = positionToString(positionNum, structLen, dataIn);
407 
408     // Part number could contain spaces at the end. Eg: "abcd123  ". Since its
409     // unnecessary, we should remove them.
410     boost::algorithm::trim_right(result);
411     partNumber(result);
412 }
413 
414 std::string Dimm::partNumber(std::string value)
415 {
416     return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
417         Asset::partNumber(value);
418 }
419 
420 std::string Dimm::locationCode(std::string value)
421 {
422     return sdbusplus::server::xyz::openbmc_project::inventory::decorator::
423         LocationCode::locationCode(value);
424 }
425 
426 size_t Dimm::memoryAttributes(size_t value)
427 {
428     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
429         memoryAttributes(value);
430 }
431 
432 uint8_t Dimm::slot(uint8_t value)
433 {
434     return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
435         MemoryLocation::slot(value);
436 }
437 
438 uint8_t Dimm::memoryController(uint8_t value)
439 {
440     return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
441         MemoryLocation::memoryController(value);
442 }
443 
444 uint8_t Dimm::channel(uint8_t value)
445 {
446     return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
447         MemoryLocation::channel(value);
448 }
449 
450 uint8_t Dimm::socket(uint8_t value)
451 {
452     return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
453         MemoryLocation::socket(value);
454 }
455 
456 uint16_t Dimm::memoryConfiguredSpeedInMhz(uint16_t value)
457 {
458     return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::
459         memoryConfiguredSpeedInMhz(value);
460 }
461 
462 bool Dimm::functional(bool value)
463 {
464     return sdbusplus::server::xyz::openbmc_project::state::decorator::
465         OperationalStatus::functional(value);
466 }
467 
468 Json Dimm::parseConfigFile()
469 {
470     std::ifstream memoryLocationFile(filename);
471 
472     if (!memoryLocationFile.is_open())
473     {
474         lg2::error("config JSON file not found, FILENAME {FILENAME}",
475                    "FILENAME", filename);
476         return {};
477     }
478 
479     auto data = Json::parse(memoryLocationFile, nullptr, false);
480     if (data.is_discarded())
481     {
482         lg2::error("config readings JSON parser failure");
483         return {};
484     }
485 
486     return data;
487 }
488 
489 } // namespace smbios
490 } // namespace phosphor
491