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