Get i2c addresses from configuration files.
Different platforms have different bus topology.
Use the peci address and i2c address specified in
json file to read data from cpu.
Tested:
Update baseboard json file:
{
"Address": "0x30",
"Bus": 0,
"CpuID": 1,
"Name": "CPU 1",
"PresenceGpio": [
{
"Name": "CPU1_PRESENCE",
"Polarity": "Low"
}
],
"PiromI2cBus": 13,
"PiromI2cAddress": "0x50",
"Type": "XeonCPU"
}
Verified that correct bus addresses are used.
Signed-off-by: Zhikui Ren <zhikui.ren@intel.com>
Change-Id: Ib133958af8349b43c2f8f73c32d1aaa0d5bf52eb
diff --git a/src/cpuinfo_main.cpp b/src/cpuinfo_main.cpp
index 24bb858..de21071 100644
--- a/src/cpuinfo_main.cpp
+++ b/src/cpuinfo_main.cpp
@@ -25,6 +25,7 @@
#include <boost/asio/io_service.hpp>
#include <boost/asio/steady_timer.hpp>
+#include <iostream>
#include <optional>
#include <sstream>
#include <string>
@@ -44,25 +45,26 @@
{
namespace cpu_info
{
-
+static constexpr bool debug = false;
static constexpr const char* cpuInterfaceName =
"xyz.openbmc_project.Inventory.Decorator.Asset";
static constexpr const char* cpuProcessName =
"xyz.openbmc_project.Smbios.MDR_V2";
-struct ProcessorInfo
-{
- uint64_t ppin;
- std::string sspec;
-};
+// constants for reading SSPEC or QDF string from PIROM
+// Currently, they are the same for platforms with icx
+static constexpr uint8_t defaultI2cBus = 13;
+static constexpr uint8_t defaultI2cSlaveAddr0 = 0x50;
+static constexpr uint8_t sspecRegAddr = 0xd;
+static constexpr uint8_t sspecSize = 6;
-using CPUMap =
- boost::container::flat_map<size_t,
- std::pair<int, std::shared_ptr<CPUInfo>>>;
+using CPUInfoMap = boost::container::flat_map<size_t, std::shared_ptr<CPUInfo>>;
-static CPUMap cpuMap = {};
+static CPUInfoMap cpuInfoMap = {};
-static std::unique_ptr<sdbusplus::bus::match_t> cpuUpdatedMatch = nullptr;
+static boost::container::flat_map<size_t,
+ std::unique_ptr<sdbusplus::bus::match_t>>
+ cpuUpdatedMatch = {};
static std::optional<std::string> readSSpec(uint8_t bus, uint8_t slaveAddr,
uint8_t regAddr, size_t count)
@@ -116,7 +118,7 @@
for (size_t i = 0; i < count; i++)
{
- value = ::i2c_smbus_read_byte_data(fd, regAddr++);
+ value = ::i2c_smbus_read_byte_data(fd, regAddr + i);
if (value < 0)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
@@ -132,47 +134,30 @@
"Non printable value in sspec, ignored.");
continue;
}
+ // sspec always starts with S,
+ // if not assume it is QDF string which starts at offset 2
+ if (i == 0 && static_cast<unsigned char>(value) != 'S')
+ {
+ i = 1;
+ continue;
+ }
sspec.push_back(static_cast<unsigned char>(value));
}
::close(fd);
return sspec;
}
-// PECI Client Address Map
-static void getPECIAddrMap(CPUMap& cpuMap)
-{
- int idx = 0;
- for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
- {
- if (peci_Ping(i) == PECI_CC_SUCCESS)
- {
- cpuMap.emplace(std::make_pair(i, std::make_pair(idx, nullptr)));
- idx++;
- }
- }
-}
-
-static std::shared_ptr<CPUInfo>
- createCPUInfo(std::shared_ptr<sdbusplus::asio::connection>& conn,
- const int& cpu)
-{
- std::string path = cpuPath + std::to_string(cpu);
- std::shared_ptr<CPUInfo> cpuInfo = std::make_shared<CPUInfo>(
- static_cast<sdbusplus::bus::bus&>(*conn), path);
- return cpuInfo;
-}
-
static void setAssetProperty(
- std::shared_ptr<sdbusplus::asio::connection>& conn, const int& cpu,
+ const std::shared_ptr<sdbusplus::asio::connection>& conn, const int& cpu,
const std::vector<std::pair<std::string, std::string>>& propValues)
{
-
- const std::string objectPath = cpuPath + std::to_string(cpu);
+ // cpuId from configuration is one based as
+ // dbus object path used by smbios is 0 based
+ const std::string objectPath = cpuPath + std::to_string(cpu - 1);
for (const auto& prop : propValues)
{
conn->async_method_call(
[](const boost::system::error_code ec) {
- // Use "Set" method to set the property value.
if (ec)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
@@ -187,197 +172,332 @@
}
static void createCpuUpdatedMatch(
- std::shared_ptr<sdbusplus::asio::connection>& conn, const int& cpu,
+ const std::shared_ptr<sdbusplus::asio::connection>& conn, const int cpu,
const std::vector<std::pair<std::string, std::string>>& propValues)
{
- if (cpuUpdatedMatch)
+ if (cpuUpdatedMatch[cpu])
{
return;
}
- const std::string objectPath = cpuPath + std::to_string(cpu);
+ const std::string objectPath = cpuPath + std::to_string(cpu - 1);
- cpuUpdatedMatch = std::make_unique<sdbusplus::bus::match::match>(
- static_cast<sdbusplus::bus::bus&>(*conn),
- sdbusplus::bus::match::rules::interfacesAdded() +
- sdbusplus::bus::match::rules::argNpath(0, objectPath.c_str()),
- [&conn, cpu, propValues](sdbusplus::message::message& msg) {
- sdbusplus::message::object_path objectName;
- boost::container::flat_map<
- std::string,
- boost::container::flat_map<std::string,
- std::variant<std::string, uint64_t>>>
- msgData;
+ cpuUpdatedMatch.insert_or_assign(
+ cpu,
+ std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*conn),
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::argNpath(0, objectPath.c_str()),
+ [conn, cpu, propValues](sdbusplus::message::message& msg) {
+ sdbusplus::message::object_path objectName;
+ boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<
+ std::string, std::variant<std::string, uint64_t>>>
+ msgData;
- msg.read(objectName, msgData);
+ msg.read(objectName, msgData);
- // Check for xyz.openbmc_project.Inventory.Item.Cpu
- // interface match
- auto intfFound = msgData.find(cpuInterfaceName);
- if (msgData.end() != intfFound)
- {
- setAssetProperty(conn, cpu, propValues);
- }
- });
-}
-
-// constants for reading QDF string from PIROM
-// Currently, they are the same for platforms with icx
-// \todo: move into configuration file to be more robust
-static constexpr uint8_t i2cBus = 13;
-static constexpr uint8_t slaveAddr0 = 0x50;
-static constexpr uint8_t regAddr = 0xf;
-static constexpr uint8_t sspecSize = 4;
-
-static void getProcessorInfo(std::shared_ptr<sdbusplus::asio::connection>& conn,
- sdbusplus::asio::object_server& objServer,
- CPUMap& cpuMap)
-{
-
- for (auto& cpu : cpuMap)
- {
- uint8_t cc = 0;
- CPUModel model{};
- uint8_t stepping = 0;
-
- /// \todo in a follwup patch
- // CPUInfo will be updated as the centrol place for CPU information
- // std::shared_ptr<CPUInfo> cpuInfo =
- // createCPUInfo(conn, cpu.second.first);
- // cpu.second.second = cpuInfo;
-
- if (peci_GetCPUID(cpu.first, &model, &stepping, &cc) != PECI_CC_SUCCESS)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "Cannot get CPUID!",
- phosphor::logging::entry("PECIADDR=0x%x", cpu.first));
- continue;
- }
-
- switch (model)
- {
- case icx:
- {
- // get processor ID
- static constexpr uint8_t u8Size = 4; // default to a DWORD
- static constexpr uint8_t u8PPINPkgIndex = 19;
- static constexpr uint16_t u16PPINPkgParamHigh = 2;
- static constexpr uint16_t u16PPINPkgParamLow = 1;
- uint64_t cpuPPIN = 0;
- uint32_t u32PkgValue = 0;
-
- int ret = peci_RdPkgConfig(cpu.first, u8PPINPkgIndex,
- u16PPINPkgParamLow, u8Size,
- (uint8_t*)&u32PkgValue, &cc);
- if (0 != ret)
+ // Check for xyz.openbmc_project.Inventory.Item.Cpu
+ // interface match
+ const auto& intfFound = msgData.find(cpuInterfaceName);
+ if (msgData.end() != intfFound)
{
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "peci read package config failed at address",
- phosphor::logging::entry("PECIADDR=0x%x", cpu.first),
- phosphor::logging::entry("CC=0x%x", cc));
- u32PkgValue = 0;
+ setAssetProperty(conn, cpu, propValues);
}
-
- cpuPPIN = u32PkgValue;
- ret = peci_RdPkgConfig(cpu.first, u8PPINPkgIndex,
- u16PPINPkgParamHigh, u8Size,
- (uint8_t*)&u32PkgValue, &cc);
- if (0 != ret)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "peci read package config failed at address",
- phosphor::logging::entry("PECIADDR=0x%x", cpu.first),
- phosphor::logging::entry("CC=0x%x", cc));
- cpuPPIN = 0;
- u32PkgValue = 0;
- }
-
- cpuPPIN |= static_cast<uint64_t>(u32PkgValue) << 32;
-
- std::vector<std::pair<std::string, std::string>> values;
-
- // set SerialNumber if cpuPPIN is valid
- if (0 != cpuPPIN)
- {
- std::stringstream stream;
- stream << std::hex << cpuPPIN;
- std::string serialNumber(stream.str());
- // cpuInfo->serialNumber(serialNumber);
- values.emplace_back(
- std::make_pair("SerialNumber", serialNumber));
- }
-
- // assuming the slaveAddress will be incrementing like peci
- // client address
- std::optional<std::string> sspec = readSSpec(
- i2cBus, static_cast<uint8_t>(slaveAddr0 + cpu.second.first),
- regAddr, sspecSize);
- // cpuInfo->model(sspec.value_or(""));
- values.emplace_back(
- std::make_pair("Model", sspec.value_or("")));
-
- /// \todo in followup patch
- // CPUInfo is created by this service
- // update the below logic, which is needed because smbios
- // service creates the cpu object
- createCpuUpdatedMatch(conn, cpu.second.first, values);
- setAssetProperty(conn, cpu.second.first, values);
- break;
- }
- default:
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "in-compatible cpu for cpu asset info");
- break;
- }
- }
-}
-
-static bool isPECIAvailable(void)
-{
- for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
- {
- if (peci_Ping(i) == PECI_CC_SUCCESS)
- {
- return true;
- }
- }
- return false;
+ }));
}
static void
- peciAvailableCheck(boost::asio::steady_timer& peciWaitTimer,
- boost::asio::io_service& io,
- std::shared_ptr<sdbusplus::asio::connection>& conn,
- sdbusplus::asio::object_server& objServer)
+ getProcessorInfo(boost::asio::io_service& io,
+ const std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const size_t& cpu)
{
- bool peciAvailable = isPECIAvailable();
- if (peciAvailable)
+ if (cpuInfoMap.find(cpu) == cpuInfoMap.end() || cpuInfoMap[cpu] == nullptr)
{
- // get the PECI client address list
- getPECIAddrMap(cpuMap);
- getProcessorInfo(conn, objServer, cpuMap);
+ std::cerr << "No information found for cpu " << cpu << "\n";
+ return;
}
- if (!peciAvailable || !cpuMap.size())
+
+ if (cpuInfoMap[cpu]->id != cpu)
{
- peciWaitTimer.expires_after(
- std::chrono::seconds(6 * peciCheckInterval));
- peciWaitTimer.async_wait([&peciWaitTimer, &io, &conn, &objServer](
- const boost::system::error_code& ec) {
+ std::cerr << "Incorrect CPU id " << (unsigned)cpuInfoMap[cpu]->id
+ << " expect " << cpu << "\n";
+ return;
+ }
+
+ uint8_t cpuAddr = cpuInfoMap[cpu]->peciAddr;
+ uint8_t i2cBus = cpuInfoMap[cpu]->i2cBus;
+ uint8_t i2cDevice = cpuInfoMap[cpu]->i2cDevice;
+
+ uint8_t cc = 0;
+ CPUModel model{};
+ uint8_t stepping = 0;
+
+ if (peci_GetCPUID(cpuAddr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
+ {
+ // Start the PECI check loop
+ auto waitTimer = std::make_shared<boost::asio::steady_timer>(io);
+ waitTimer->expires_after(
+ std::chrono::seconds(phosphor::cpu_info::peciCheckInterval));
+
+ waitTimer->async_wait(
+ [waitTimer, &io, conn, cpu](const boost::system::error_code& ec) {
+ if (ec)
+ {
+ // operation_aborted is expected if timer is canceled
+ // before completion.
+ if (ec != boost::asio::error::operation_aborted)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "info update timer async_wait failed ",
+ phosphor::logging::entry("EC=0x%x", ec.value()));
+ }
+ return;
+ }
+ getProcessorInfo(io, conn, cpu);
+ });
+ return;
+ }
+
+ switch (model)
+ {
+ case icx:
+ {
+ // PPIN can be read through PCS 19
+ static constexpr uint8_t u8Size = 4; // default to a DWORD
+ static constexpr uint8_t u8PPINPkgIndex = 19;
+ static constexpr uint16_t u16PPINPkgParamHigh = 2;
+ static constexpr uint16_t u16PPINPkgParamLow = 1;
+ uint64_t cpuPPIN = 0;
+ uint32_t u32PkgValue = 0;
+
+ int ret =
+ peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamLow,
+ u8Size, (uint8_t*)&u32PkgValue, &cc);
+ if (0 != ret)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "peci read package config failed at address",
+ phosphor::logging::entry("PECIADDR=0x%x",
+ (unsigned)cpuAddr),
+ phosphor::logging::entry("CC=0x%x", cc));
+ u32PkgValue = 0;
+ }
+
+ cpuPPIN = u32PkgValue;
+ ret = peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamHigh,
+ u8Size, (uint8_t*)&u32PkgValue, &cc);
+ if (0 != ret)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "peci read package config failed at address",
+ phosphor::logging::entry("PECIADDR=0x%x",
+ (unsigned)cpuAddr),
+ phosphor::logging::entry("CC=0x%x", cc));
+ cpuPPIN = 0;
+ u32PkgValue = 0;
+ }
+
+ cpuPPIN |= static_cast<uint64_t>(u32PkgValue) << 32;
+
+ std::vector<std::pair<std::string, std::string>> values;
+
+ // set SerialNumber if cpuPPIN is valid
+ if (0 != cpuPPIN)
+ {
+ std::stringstream stream;
+ stream << std::hex << cpuPPIN;
+ std::string serialNumber(stream.str());
+ // cpuInfo->serialNumber(serialNumber);
+ values.emplace_back(
+ std::make_pair("SerialNumber", serialNumber));
+ }
+
+ std::optional<std::string> sspec =
+ readSSpec(i2cBus, i2cDevice, sspecRegAddr, sspecSize);
+
+ // cpuInfo->model(sspec.value_or(""));
+ values.emplace_back(std::make_pair("Model", sspec.value_or("")));
+
+ /// \todo in followup patch
+ // CPUInfo is created by this service
+ // update the below logic, which is needed because smbios
+ // service creates the cpu object
+ createCpuUpdatedMatch(conn, cpu, values);
+ setAssetProperty(conn, cpu, values);
+ break;
+ }
+ default:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "in-compatible cpu for cpu asset info");
+ break;
+ }
+}
+
+/**
+ * Get cpu and pirom address
+ */
+static void
+ getCpuAddress(boost::asio::io_service& io,
+ const std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& service, const std::string& object,
+ const std::string& interface)
+{
+ conn->async_method_call(
+ [&io, conn](boost::system::error_code ec,
+ const boost::container::flat_map<
+ std::string,
+ std::variant<std::string, uint64_t, uint32_t, uint16_t,
+ std::vector<std::string>>>& properties) {
+ const uint64_t* value = nullptr;
+ uint8_t peciAddress = 0;
+ uint8_t i2cBus = defaultI2cBus;
+ uint8_t i2cDevice;
+ bool i2cDeviceFound = false;
+ size_t cpu = 0;
+
if (ec)
{
- // operation_aborted is expected if timer is canceled
- // before completion.
- if (ec != boost::asio::error::operation_aborted)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "PECI Available Check async_wait failed",
- phosphor::logging::entry("EC=0x%x", ec.value()));
- }
+ std::cerr << "DBUS response error " << ec.value() << ": "
+ << ec.message() << "\n";
return;
}
- peciAvailableCheck(peciWaitTimer, io, conn, objServer);
- });
- }
+
+ for (const auto& property : properties)
+ {
+ std::cerr << "property " << property.first << "\n";
+ if (property.first == "Address")
+ {
+ value = std::get_if<uint64_t>(&property.second);
+ if (value != nullptr)
+ {
+ peciAddress = static_cast<uint8_t>(*value);
+ }
+ }
+ if (property.first == "CpuID")
+ {
+ value = std::get_if<uint64_t>(&property.second);
+ if (value != nullptr)
+ {
+ cpu = static_cast<size_t>(*value);
+ }
+ }
+ if (property.first == "PiromI2cAddress")
+ {
+ value = std::get_if<uint64_t>(&property.second);
+ if (value != nullptr)
+ {
+ i2cDevice = static_cast<uint8_t>(*value);
+ i2cDeviceFound = true;
+ }
+ }
+ if (property.first == "PiromI2cBus")
+ {
+ value = std::get_if<uint64_t>(&property.second);
+ if (value != nullptr)
+ {
+ i2cBus = static_cast<uint8_t>(*value);
+ }
+ }
+ }
+
+ ///\todo replace this with present + power state
+ if (cpu != 0 && peciAddress != 0)
+ {
+ if (!i2cDeviceFound)
+ {
+ i2cDevice = defaultI2cSlaveAddr0 + cpu - 1;
+ }
+ cpuInfoMap.insert_or_assign(
+ cpu, std::make_shared<CPUInfo>(cpu, peciAddress, i2cBus,
+ i2cDevice));
+
+ getProcessorInfo(io, conn, cpu);
+ }
+ },
+ service, object, "org.freedesktop.DBus.Properties", "GetAll",
+ interface);
+}
+
+/**
+ * D-Bus client: to get platform specific configs
+ */
+static void getCpuConfiguration(
+ boost::asio::io_service& io,
+ const std::shared_ptr<sdbusplus::asio::connection>& conn,
+ sdbusplus::asio::object_server& objServer)
+{
+ // Get the Cpu configuration
+ // In case it's not available, set a match for it
+ static std::unique_ptr<sdbusplus::bus::match::match> cpuConfigMatch =
+ std::make_unique<sdbusplus::bus::match::match>(
+ *conn,
+ "type='signal',interface='org.freedesktop.DBus.Properties',member='"
+ "PropertiesChanged',arg0='xyz.openbmc_project."
+ "Configuration.XeonCPU'",
+ [&io, conn, &objServer](sdbusplus::message::message& msg) {
+ std::cerr << "get cpu configuration match\n";
+ static boost::asio::steady_timer filterTimer(io);
+ filterTimer.expires_after(
+ std::chrono::seconds(configCheckInterval));
+
+ filterTimer.async_wait(
+ [&io, conn,
+ &objServer](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ else if (ec)
+ {
+ std::cerr << "Error: " << ec.message() << "\n";
+ return;
+ }
+ getCpuConfiguration(io, conn, objServer);
+ });
+ });
+
+ conn->async_method_call(
+ [&io, conn](
+ boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string, std::vector<std::string>>>>>&
+ subtree) {
+ if constexpr (debug)
+ std::cerr << "async_method_call callback\n";
+
+ if (ec)
+ {
+ std::cerr << "error with async_method_call\n";
+ return;
+ }
+ if (subtree.empty())
+ {
+ // No config data yet, so wait for the match
+ return;
+ }
+
+ for (const auto& object : subtree)
+ {
+ for (const auto& service : object.second)
+ {
+ getCpuAddress(io, conn, service.first, object.first,
+ "xyz.openbmc_project.Configuration.XeonCPU");
+ }
+ }
+ if constexpr (debug)
+ std::cerr << "getCpuConfiguration callback complete\n";
+
+ return;
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+ "/xyz/openbmc_project/", 0,
+ std::array<const char*, 1>{
+ "xyz.openbmc_project.Configuration.XeonCPU"});
}
} // namespace cpu_info
@@ -400,25 +520,9 @@
cpu_info::sst::init(io, conn);
- // Start the PECI check loop
- boost::asio::steady_timer peciWaitTimer(
- io, std::chrono::seconds(phosphor::cpu_info::peciCheckInterval));
- peciWaitTimer.async_wait([&peciWaitTimer, &io, &conn,
- &server](const boost::system::error_code& ec) {
- if (ec)
- {
- // operation_aborted is expected if timer is canceled
- // before completion.
- if (ec != boost::asio::error::operation_aborted)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "PECI Available Check async_wait failed ",
- phosphor::logging::entry("EC=0x%x", ec.value()));
- }
- return;
- }
- phosphor::cpu_info::peciAvailableCheck(peciWaitTimer, io, conn, server);
- });
+ // shared_ptr conn is global for the service
+ // const reference of conn is passed to async calls
+ phosphor::cpu_info::getCpuConfiguration(io, conn, server);
io.run();