blob: 9cc7ef302416b9912115c82e8d3db7fcbc45c3d8 [file] [log] [blame]
/*
// 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 <boost/algorithm/string.hpp>
#include <phosphor-logging/elog-errors.hpp>
namespace phosphor
{
namespace smbios
{
#ifdef DIMM_ONLY_LOCATOR
bool onlyDimmLocationCode = true;
#else
bool onlyDimmLocationCode = false;
#endif
using DeviceType =
sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::DeviceType;
using EccType =
sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::Ecc;
static constexpr uint16_t maxOldDimmSize = 0x7fff;
void Dimm::memoryInfoUpdate(void)
{
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<struct MemoryInfo*>(dataIn);
memoryDataWidth(memoryInfo->dataWidth);
if (memoryInfo->size == maxOldDimmSize)
{
dimmSizeExt(memoryInfo->extendedSize);
}
else
{
dimmSize(memoryInfo->size);
}
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);
memoryConfiguredSpeedInMhz(memoryInfo->confClockSpeed);
updateEccType(memoryInfo->phyArrayHandle);
if (!motherboardPath.empty())
{
std::vector<std::tuple<std::string, std::string, std::string>> 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<phosphor::logging::level::ERR>(
"Failed to get SMBIOS table type-16 data.");
return;
}
auto info = reinterpret_cast<struct PhysicalMemoryArrayInfo*>(dataIn);
if (info->handle == exPhyArrayHandle)
{
std::map<uint8_t, EccType>::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<phosphor::logging::level::ERR>(
"Failed find the corresponding SMBIOS table type-16 data for dimm:",
phosphor::logging::entry("DIMM:%d", dimmNum));
}
EccType Dimm::ecc(EccType value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::ecc(
value);
}
uint16_t Dimm::memoryDataWidth(uint16_t value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
memoryDataWidth(value);
}
static constexpr uint16_t baseNewVersionDimmSize = 0x8000;
static constexpr uint16_t dimmSizeUnit = 1024;
void Dimm::dimmSize(const uint16_t size)
{
size_t result = size & maxOldDimmSize;
if (0 == (size & baseNewVersionDimmSize))
{
result = result * dimmSizeUnit;
}
memorySizeInKB(result);
}
void Dimm::dimmSizeExt(size_t size)
{
size = size * dimmSizeUnit;
memorySizeInKB(size);
}
size_t Dimm::memorySizeInKB(size_t value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::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);
}
std::string Dimm::memoryDeviceLocator(std::string value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
memoryDeviceLocator(value);
}
void Dimm::dimmType(const uint8_t type)
{
std::map<uint8_t, DeviceType>::const_iterator it = dimmTypeTable.find(type);
if (it == dimmTypeTable.end())
{
memoryType(DeviceType::Unknown);
}
else
{
memoryType(it->second);
}
}
DeviceType Dimm::memoryType(DeviceType value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
memoryType(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::xyz::openbmc_project::Inventory::Item::server::Dimm::
memoryTypeDetail(value);
}
uint16_t Dimm::maxMemorySpeedInMhz(uint16_t value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
maxMemorySpeedInMhz(value);
}
void Dimm::dimmManufacturer(const uint8_t positionNum, const uint8_t structLen,
uint8_t* dataIn)
{
std::string result = positionToString(positionNum, structLen, dataIn);
bool val = true;
if (result == "NO DIMM")
{
val = false;
// 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);
present(val);
functional(val);
}
std::string Dimm::manufacturer(std::string value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Decorator::server::
Asset::manufacturer(value);
}
bool Dimm::present(bool value)
{
return sdbusplus::xyz::openbmc_project::Inventory::server::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::xyz::openbmc_project::Inventory::Decorator::server::
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::xyz::openbmc_project::Inventory::Decorator::server::
Asset::partNumber(value);
}
std::string Dimm::locationCode(std::string value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Decorator::server::
LocationCode::locationCode(value);
}
uint8_t Dimm::memoryAttributes(uint8_t value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
memoryAttributes(value);
}
uint16_t Dimm::memoryConfiguredSpeedInMhz(uint16_t value)
{
return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Dimm::
memoryConfiguredSpeedInMhz(value);
}
bool Dimm::functional(bool value)
{
return sdbusplus::xyz::openbmc_project::State::Decorator::server::
OperationalStatus::functional(value);
}
} // namespace smbios
} // namespace phosphor