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 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 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 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(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 293 std::string Dimm::memoryDeviceLocator(std::string value) 294 { 295 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 296 memoryDeviceLocator(value); 297 } 298 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 312 DeviceType Dimm::memoryType(DeviceType value) 313 { 314 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 315 memoryType(value); 316 } 317 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 332 MemoryTechType Dimm::memoryMedia(MemoryTechType value) 333 { 334 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 335 memoryMedia(value); 336 } 337 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 352 std::string Dimm::memoryTypeDetail(std::string value) 353 { 354 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 355 memoryTypeDetail(value); 356 } 357 358 uint16_t Dimm::maxMemorySpeedInMhz(uint16_t value) 359 { 360 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 361 maxMemorySpeedInMhz(value); 362 } 363 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 379 std::string Dimm::manufacturer(std::string value) 380 { 381 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 382 Asset::manufacturer(value); 383 } 384 385 bool Dimm::present(bool value) 386 { 387 return sdbusplus::server::xyz::openbmc_project::inventory::Item::present( 388 value); 389 } 390 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 399 std::string Dimm::serialNumber(std::string value) 400 { 401 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 402 Asset::serialNumber(value); 403 } 404 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 416 std::string Dimm::partNumber(std::string value) 417 { 418 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 419 Asset::partNumber(value); 420 } 421 422 std::string Dimm::locationCode(std::string value) 423 { 424 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 425 LocationCode::locationCode(value); 426 } 427 428 size_t Dimm::memoryAttributes(size_t value) 429 { 430 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 431 memoryAttributes(value); 432 } 433 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 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 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 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 458 uint16_t Dimm::memoryConfiguredSpeedInMhz(uint16_t value) 459 { 460 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 461 memoryConfiguredSpeedInMhz(value); 462 } 463 464 bool Dimm::functional(bool value) 465 { 466 return sdbusplus::server::xyz::openbmc_project::state::decorator:: 467 OperationalStatus::functional(value); 468 } 469 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