/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "dimm.hpp" #include "mdrv2.hpp" #include #include #include namespace phosphor { namespace smbios { #ifdef DIMM_ONLY_LOCATOR bool onlyDimmLocationCode = true; #else bool onlyDimmLocationCode = false; #endif using DeviceType = sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::DeviceType; using EccType = sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::Ecc; static constexpr uint16_t maxOldDimmSize = 0x7fff; void Dimm::memoryInfoUpdate(uint8_t* smbiosTableStorage, const std::string& motherboard) { storage = smbiosTableStorage; motherboardPath = motherboard; uint8_t* dataIn = storage; dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType); if (dataIn == nullptr) { return; } for (uint8_t index = 0; index < dimmNum; index++) { dataIn = smbiosNextPtr(dataIn); if (dataIn == nullptr) { return; } dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType); if (dataIn == nullptr) { return; } } auto memoryInfo = reinterpret_cast(dataIn); memoryDataWidth(memoryInfo->dataWidth); memoryTotalWidth(memoryInfo->totalWidth); if (memoryInfo->size == maxOldDimmSize) { dimmSizeExt(memoryInfo->extendedSize); } else { dimmSize(memoryInfo->size); } // If the size is 0, no memory device is installed in the socket. const auto isDimmPresent = memoryInfo->size > 0; present(isDimmPresent); functional(isDimmPresent); dimmDeviceLocator(memoryInfo->bankLocator, memoryInfo->deviceLocator, memoryInfo->length, dataIn); dimmType(memoryInfo->memoryType); dimmTypeDetail(memoryInfo->typeDetail); maxMemorySpeedInMhz(memoryInfo->speed); dimmManufacturer(memoryInfo->manufacturer, memoryInfo->length, dataIn); dimmSerialNum(memoryInfo->serialNum, memoryInfo->length, dataIn); dimmPartNum(memoryInfo->partNum, memoryInfo->length, dataIn); memoryAttributes(memoryInfo->attributes); dimmMedia(memoryInfo->memoryTechnology); memoryConfiguredSpeedInMhz(memoryInfo->confClockSpeed); updateEccType(memoryInfo->phyArrayHandle); if (!motherboardPath.empty()) { std::vector> assocs; assocs.emplace_back("chassis", "memories", motherboardPath); association::associations(assocs); } return; } void Dimm::updateEccType(uint16_t exPhyArrayHandle) { uint8_t* dataIn = storage; while (dataIn != nullptr) { dataIn = getSMBIOSTypePtr(dataIn, physicalMemoryArrayType); if (dataIn == nullptr) { phosphor::logging::log( "Failed to get SMBIOS table type-16 data."); return; } auto info = reinterpret_cast(dataIn); if (info->handle == exPhyArrayHandle) { std::map::const_iterator it = dimmEccTypeMap.find(info->memoryErrorCorrection); if (it == dimmEccTypeMap.end()) { ecc(EccType::NoECC); } else { ecc(it->second); } return; } dataIn = smbiosNextPtr(dataIn); } phosphor::logging::log( "Failed find the corresponding SMBIOS table type-16 data for dimm:", phosphor::logging::entry("DIMM:%d", dimmNum)); } EccType Dimm::ecc(EccType value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::ecc( value); } uint16_t Dimm::memoryDataWidth(uint16_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryDataWidth(value); } uint16_t Dimm::memoryTotalWidth(uint16_t value) { return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm:: memoryTotalWidth(value); } static constexpr uint16_t baseNewVersionDimmSize = 0x8000; static constexpr uint16_t dimmSizeUnit = 1024; void Dimm::dimmSize(const uint16_t size) { uint32_t result = size & maxOldDimmSize; if (0 == (size & baseNewVersionDimmSize)) { result = result * dimmSizeUnit; } memorySizeInKB(result); } void Dimm::dimmSizeExt(uint32_t size) { size = size * dimmSizeUnit; memorySizeInKB(size); } size_t Dimm::memorySizeInKB(size_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memorySizeInKB(value); } void Dimm::dimmDeviceLocator(const uint8_t bankLocatorPositionNum, const uint8_t deviceLocatorPositionNum, const uint8_t structLen, uint8_t* dataIn) { std::string deviceLocator = positionToString(deviceLocatorPositionNum, structLen, dataIn); std::string bankLocator = positionToString(bankLocatorPositionNum, structLen, dataIn); std::string result; if (bankLocator.empty() || onlyDimmLocationCode) { result = deviceLocator; } else { result = bankLocator + " " + deviceLocator; } memoryDeviceLocator(result); locationCode(result); const std::string substrCpu = "CPU"; auto cpuPos = deviceLocator.find(substrCpu); if (cpuPos != std::string::npos) { std::string socketString = deviceLocator.substr(cpuPos + substrCpu.length(), 1); try { uint8_t socketNum = static_cast(std::stoi(socketString) + 1); socket(socketNum); } catch (const sdbusplus::exception_t& ex) { phosphor::logging::log( "std::stoi operation failed ", phosphor::logging::entry("ERROR=%s", ex.what())); } } const std::string substrDimm = "DIMM"; auto dimmPos = deviceLocator.find(substrDimm); if (dimmPos != std::string::npos) { std::string slotString = deviceLocator.substr(dimmPos + substrDimm.length() + 1); /* slotString is extracted from substrDimm (DIMM_A) if slotString is * single alphabet like A, B , C.. then assign ASCII value of slotString * to slot */ if ((std::regex_match(slotString, std::regex("^[A-Za-z]+$"))) && (slotString.length() == 1)) { slot(static_cast(toupper(slotString[0]))); } } } std::string Dimm::memoryDeviceLocator(std::string value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryDeviceLocator(value); } void Dimm::dimmType(const uint8_t type) { std::map::const_iterator it = dimmTypeTable.find(type); if (it == dimmTypeTable.end()) { memoryType(DeviceType::Unknown); } else { memoryType(it->second); } } DeviceType Dimm::memoryType(DeviceType value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryType(value); } void Dimm::dimmMedia(const uint8_t type) { std::map::const_iterator it = dimmMemoryTechTypeMap.find(type); if (it == dimmMemoryTechTypeMap.end()) { memoryMedia(MemoryTechType::Unknown); } else { memoryMedia(it->second); } } MemoryTechType Dimm::memoryMedia(MemoryTechType value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryMedia(value); } void Dimm::dimmTypeDetail(uint16_t detail) { std::string result; for (uint8_t index = 0; index < (8 * sizeof(detail)); index++) { if (detail & 0x01) { result += detailTable[index]; } detail >>= 1; } memoryTypeDetail(result); } std::string Dimm::memoryTypeDetail(std::string value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryTypeDetail(value); } uint16_t Dimm::maxMemorySpeedInMhz(uint16_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: maxMemorySpeedInMhz(value); } void Dimm::dimmManufacturer(const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn) { std::string result = positionToString(positionNum, structLen, dataIn); if (result == "NO DIMM") { // No dimm presence so making manufacturer value as "" (instead of // NO DIMM - as there won't be any manufacturer for DIMM which is not // present). result = ""; } manufacturer(result); } std::string Dimm::manufacturer(std::string value) { return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: Asset::manufacturer(value); } bool Dimm::present(bool value) { return sdbusplus::server::xyz::openbmc_project::inventory::Item::present( value); } void Dimm::dimmSerialNum(const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn) { std::string result = positionToString(positionNum, structLen, dataIn); serialNumber(result); } std::string Dimm::serialNumber(std::string value) { return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: Asset::serialNumber(value); } void Dimm::dimmPartNum(const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn) { std::string result = positionToString(positionNum, structLen, dataIn); // Part number could contain spaces at the end. Eg: "abcd123 ". Since its // unnecessary, we should remove them. boost::algorithm::trim_right(result); partNumber(result); } std::string Dimm::partNumber(std::string value) { return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: Asset::partNumber(value); } std::string Dimm::locationCode(std::string value) { return sdbusplus::server::xyz::openbmc_project::inventory::decorator:: LocationCode::locationCode(value); } size_t Dimm::memoryAttributes(size_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryAttributes(value); } uint8_t Dimm::slot(uint8_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm:: MemoryLocation::slot(value); } uint8_t Dimm::socket(uint8_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm:: MemoryLocation::socket(value); } uint16_t Dimm::memoryConfiguredSpeedInMhz(uint16_t value) { return sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm:: memoryConfiguredSpeedInMhz(value); } bool Dimm::functional(bool value) { return sdbusplus::server::xyz::openbmc_project::state::decorator:: OperationalStatus::functional(value); } } // namespace smbios } // namespace phosphor