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 <regex> 25 26 namespace phosphor 27 { 28 namespace smbios 29 { 30 31 #ifdef DIMM_ONLY_LOCATOR 32 bool onlyDimmLocationCode = true; 33 #else 34 bool onlyDimmLocationCode = false; 35 #endif 36 37 using DeviceType = 38 sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::DeviceType; 39 40 using EccType = 41 sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::Ecc; 42 43 static constexpr uint16_t maxOldDimmSize = 0x7fff; 44 void Dimm::memoryInfoUpdate(uint8_t* smbiosTableStorage, 45 const std::string& motherboard) 46 { 47 storage = smbiosTableStorage; 48 motherboardPath = motherboard; 49 50 uint8_t* dataIn = storage; 51 52 dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType); 53 54 if (dataIn == nullptr) 55 { 56 return; 57 } 58 for (uint8_t index = 0; index < dimmNum; index++) 59 { 60 dataIn = smbiosNextPtr(dataIn); 61 if (dataIn == nullptr) 62 { 63 return; 64 } 65 dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType); 66 if (dataIn == nullptr) 67 { 68 return; 69 } 70 } 71 72 auto memoryInfo = reinterpret_cast<struct MemoryInfo*>(dataIn); 73 74 memoryDataWidth(memoryInfo->dataWidth); 75 memoryTotalWidth(memoryInfo->totalWidth); 76 77 if (memoryInfo->size == maxOldDimmSize) 78 { 79 dimmSizeExt(memoryInfo->extendedSize); 80 } 81 else 82 { 83 dimmSize(memoryInfo->size); 84 } 85 // If the size is 0, no memory device is installed in the socket. 86 const auto isDimmPresent = memoryInfo->size > 0; 87 present(isDimmPresent); 88 functional(isDimmPresent); 89 90 dimmDeviceLocator(memoryInfo->bankLocator, memoryInfo->deviceLocator, 91 memoryInfo->length, dataIn); 92 dimmType(memoryInfo->memoryType); 93 dimmTypeDetail(memoryInfo->typeDetail); 94 maxMemorySpeedInMhz(memoryInfo->speed); 95 dimmManufacturer(memoryInfo->manufacturer, memoryInfo->length, dataIn); 96 dimmSerialNum(memoryInfo->serialNum, memoryInfo->length, dataIn); 97 dimmPartNum(memoryInfo->partNum, memoryInfo->length, dataIn); 98 memoryAttributes(memoryInfo->attributes); 99 dimmMedia(memoryInfo->memoryTechnology); 100 memoryConfiguredSpeedInMhz(memoryInfo->confClockSpeed); 101 102 updateEccType(memoryInfo->phyArrayHandle); 103 104 if (!motherboardPath.empty()) 105 { 106 std::vector<std::tuple<std::string, std::string, std::string>> assocs; 107 assocs.emplace_back("chassis", "memories", motherboardPath); 108 association::associations(assocs); 109 } 110 111 return; 112 } 113 114 void Dimm::updateEccType(uint16_t exPhyArrayHandle) 115 { 116 uint8_t* dataIn = storage; 117 118 while (dataIn != nullptr) 119 { 120 dataIn = getSMBIOSTypePtr(dataIn, physicalMemoryArrayType); 121 if (dataIn == nullptr) 122 { 123 phosphor::logging::log<phosphor::logging::level::ERR>( 124 "Failed to get SMBIOS table type-16 data."); 125 return; 126 } 127 128 auto info = reinterpret_cast<struct PhysicalMemoryArrayInfo*>(dataIn); 129 if (info->handle == exPhyArrayHandle) 130 { 131 std::map<uint8_t, EccType>::const_iterator it = 132 dimmEccTypeMap.find(info->memoryErrorCorrection); 133 if (it == dimmEccTypeMap.end()) 134 { 135 ecc(EccType::NoECC); 136 } 137 else 138 { 139 ecc(it->second); 140 } 141 return; 142 } 143 144 dataIn = smbiosNextPtr(dataIn); 145 } 146 phosphor::logging::log<phosphor::logging::level::ERR>( 147 "Failed find the corresponding SMBIOS table type-16 data for dimm:", 148 phosphor::logging::entry("DIMM:%d", dimmNum)); 149 } 150 151 EccType Dimm::ecc(EccType value) 152 { 153 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::ecc( 154 value); 155 } 156 157 uint16_t Dimm::memoryDataWidth(uint16_t value) 158 { 159 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 160 memoryDataWidth(value); 161 } 162 163 uint16_t Dimm::memoryTotalWidth(uint16_t value) 164 { 165 return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm:: 166 memoryTotalWidth(value); 167 } 168 169 static constexpr uint16_t baseNewVersionDimmSize = 0x8000; 170 static constexpr uint16_t dimmSizeUnit = 1024; 171 void Dimm::dimmSize(const uint16_t size) 172 { 173 uint32_t result = size & maxOldDimmSize; 174 if (0 == (size & baseNewVersionDimmSize)) 175 { 176 result = result * dimmSizeUnit; 177 } 178 memorySizeInKB(result); 179 } 180 181 void Dimm::dimmSizeExt(uint32_t size) 182 { 183 size = size * dimmSizeUnit; 184 memorySizeInKB(size); 185 } 186 187 size_t Dimm::memorySizeInKB(size_t value) 188 { 189 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 190 memorySizeInKB(value); 191 } 192 193 void Dimm::dimmDeviceLocator(const uint8_t bankLocatorPositionNum, 194 const uint8_t deviceLocatorPositionNum, 195 const uint8_t structLen, uint8_t* dataIn) 196 { 197 std::string deviceLocator = 198 positionToString(deviceLocatorPositionNum, structLen, dataIn); 199 std::string bankLocator = 200 positionToString(bankLocatorPositionNum, structLen, dataIn); 201 202 std::string result; 203 if (bankLocator.empty() || onlyDimmLocationCode) 204 { 205 result = deviceLocator; 206 } 207 else 208 { 209 result = bankLocator + " " + deviceLocator; 210 } 211 212 memoryDeviceLocator(result); 213 214 locationCode(result); 215 const std::string substrCpu = "CPU"; 216 auto cpuPos = deviceLocator.find(substrCpu); 217 218 if (cpuPos != std::string::npos) 219 { 220 std::string socketString = 221 deviceLocator.substr(cpuPos + substrCpu.length(), 1); 222 try 223 { 224 uint8_t socketNum = 225 static_cast<uint8_t>(std::stoi(socketString) + 1); 226 socket(socketNum); 227 } 228 catch (const sdbusplus::exception_t& ex) 229 { 230 phosphor::logging::log<phosphor::logging::level::ERR>( 231 "std::stoi operation failed ", 232 phosphor::logging::entry("ERROR=%s", ex.what())); 233 } 234 } 235 236 const std::string substrDimm = "DIMM"; 237 auto dimmPos = deviceLocator.find(substrDimm); 238 239 if (dimmPos != std::string::npos) 240 { 241 std::string slotString = 242 deviceLocator.substr(dimmPos + substrDimm.length() + 1); 243 /* slotString is extracted from substrDimm (DIMM_A) if slotString is 244 * single alphabet like A, B , C.. then assign ASCII value of slotString 245 * to slot */ 246 if ((std::regex_match(slotString, std::regex("^[A-Za-z]+$"))) && 247 (slotString.length() == 1)) 248 { 249 slot(static_cast<uint8_t>(toupper(slotString[0]))); 250 } 251 } 252 } 253 254 std::string Dimm::memoryDeviceLocator(std::string value) 255 { 256 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 257 memoryDeviceLocator(value); 258 } 259 260 void Dimm::dimmType(const uint8_t type) 261 { 262 std::map<uint8_t, DeviceType>::const_iterator it = dimmTypeTable.find(type); 263 if (it == dimmTypeTable.end()) 264 { 265 memoryType(DeviceType::Unknown); 266 } 267 else 268 { 269 memoryType(it->second); 270 } 271 } 272 273 DeviceType Dimm::memoryType(DeviceType value) 274 { 275 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 276 memoryType(value); 277 } 278 279 void Dimm::dimmMedia(const uint8_t type) 280 { 281 std::map<uint8_t, MemoryTechType>::const_iterator it = 282 dimmMemoryTechTypeMap.find(type); 283 if (it == dimmMemoryTechTypeMap.end()) 284 { 285 memoryMedia(MemoryTechType::Unknown); 286 } 287 else 288 { 289 memoryMedia(it->second); 290 } 291 } 292 293 MemoryTechType Dimm::memoryMedia(MemoryTechType value) 294 { 295 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 296 memoryMedia(value); 297 } 298 299 void Dimm::dimmTypeDetail(uint16_t detail) 300 { 301 std::string result; 302 for (uint8_t index = 0; index < (8 * sizeof(detail)); index++) 303 { 304 if (detail & 0x01) 305 { 306 result += detailTable[index]; 307 } 308 detail >>= 1; 309 } 310 memoryTypeDetail(result); 311 } 312 313 std::string Dimm::memoryTypeDetail(std::string value) 314 { 315 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 316 memoryTypeDetail(value); 317 } 318 319 uint16_t Dimm::maxMemorySpeedInMhz(uint16_t value) 320 { 321 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 322 maxMemorySpeedInMhz(value); 323 } 324 325 void Dimm::dimmManufacturer(const uint8_t positionNum, const uint8_t structLen, 326 uint8_t* dataIn) 327 { 328 std::string result = positionToString(positionNum, structLen, dataIn); 329 330 if (result == "NO DIMM") 331 { 332 // No dimm presence so making manufacturer value as "" (instead of 333 // NO DIMM - as there won't be any manufacturer for DIMM which is not 334 // present). 335 result = ""; 336 } 337 manufacturer(result); 338 } 339 340 std::string Dimm::manufacturer(std::string value) 341 { 342 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 343 Asset::manufacturer(value); 344 } 345 346 bool Dimm::present(bool value) 347 { 348 return sdbusplus::server::xyz::openbmc_project::inventory::Item::present( 349 value); 350 } 351 352 void Dimm::dimmSerialNum(const uint8_t positionNum, const uint8_t structLen, 353 uint8_t* dataIn) 354 { 355 std::string result = positionToString(positionNum, structLen, dataIn); 356 357 serialNumber(result); 358 } 359 360 std::string Dimm::serialNumber(std::string value) 361 { 362 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 363 Asset::serialNumber(value); 364 } 365 366 void Dimm::dimmPartNum(const uint8_t positionNum, const uint8_t structLen, 367 uint8_t* dataIn) 368 { 369 std::string result = positionToString(positionNum, structLen, dataIn); 370 371 // Part number could contain spaces at the end. Eg: "abcd123 ". Since its 372 // unnecessary, we should remove them. 373 boost::algorithm::trim_right(result); 374 partNumber(result); 375 } 376 377 std::string Dimm::partNumber(std::string value) 378 { 379 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 380 Asset::partNumber(value); 381 } 382 383 std::string Dimm::locationCode(std::string value) 384 { 385 return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: 386 LocationCode::locationCode(value); 387 } 388 389 size_t Dimm::memoryAttributes(size_t value) 390 { 391 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 392 memoryAttributes(value); 393 } 394 395 uint8_t Dimm::slot(uint8_t value) 396 { 397 return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm:: 398 MemoryLocation::slot(value); 399 } 400 401 uint8_t Dimm::socket(uint8_t value) 402 { 403 return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm:: 404 MemoryLocation::socket(value); 405 } 406 407 uint16_t Dimm::memoryConfiguredSpeedInMhz(uint16_t value) 408 { 409 return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: 410 memoryConfiguredSpeedInMhz(value); 411 } 412 413 bool Dimm::functional(bool value) 414 { 415 return sdbusplus::server::xyz::openbmc_project::state::decorator:: 416 OperationalStatus::functional(value); 417 } 418 419 } // namespace smbios 420 } // namespace phosphor 421