118a5ab91SZhikui Ren /*
218a5ab91SZhikui Ren // Copyright (c) 2020 Intel Corporation
318a5ab91SZhikui Ren //
418a5ab91SZhikui Ren // Licensed under the Apache License, Version 2.0 (the "License");
518a5ab91SZhikui Ren // you may not use this file except in compliance with the License.
618a5ab91SZhikui Ren // You may obtain a copy of the License at
718a5ab91SZhikui Ren //
818a5ab91SZhikui Ren // http://www.apache.org/licenses/LICENSE-2.0
918a5ab91SZhikui Ren //
1018a5ab91SZhikui Ren // Unless required by applicable law or agreed to in writing, software
1118a5ab91SZhikui Ren // distributed under the License is distributed on an "AS IS" BASIS,
1218a5ab91SZhikui Ren // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1318a5ab91SZhikui Ren // See the License for the specific language governing permissions and
1418a5ab91SZhikui Ren // limitations under the License.
1518a5ab91SZhikui Ren */
1618a5ab91SZhikui Ren
1718a5ab91SZhikui Ren #include "cpuinfo.hpp"
18703a1856SJonathan Doman #include "cpuinfo_utils.hpp"
1918a5ab91SZhikui Ren
2018a5ab91SZhikui Ren #include <errno.h>
2118a5ab91SZhikui Ren #include <fcntl.h>
2218a5ab91SZhikui Ren #include <stdio.h>
2318a5ab91SZhikui Ren #include <sys/ioctl.h>
2418a5ab91SZhikui Ren
2518a5ab91SZhikui Ren #include <boost/asio/io_service.hpp>
2618a5ab91SZhikui Ren #include <boost/asio/steady_timer.hpp>
271e1ca736SJason M. Bills #include <boost/container/flat_map.hpp>
2818a5ab91SZhikui Ren
296d3ad586SZhikui Ren #include <iostream>
302285be4fSJonathan Doman #include <list>
3118a5ab91SZhikui Ren #include <optional>
3218a5ab91SZhikui Ren #include <sstream>
3318a5ab91SZhikui Ren #include <string>
3418a5ab91SZhikui Ren
3518a5ab91SZhikui Ren extern "C"
3618a5ab91SZhikui Ren {
3718a5ab91SZhikui Ren #include <i2c/smbus.h>
3818a5ab91SZhikui Ren #include <linux/i2c-dev.h>
3918a5ab91SZhikui Ren }
4018a5ab91SZhikui Ren
414e1cf099SJonathan Doman #if PECI_ENABLED
424e1cf099SJonathan Doman #include "speed_select.hpp"
434e1cf099SJonathan Doman
4418a5ab91SZhikui Ren #include <peci.h>
454e1cf099SJonathan Doman #endif
4618a5ab91SZhikui Ren
4718a5ab91SZhikui Ren #include <phosphor-logging/log.hpp>
4818a5ab91SZhikui Ren #include <sdbusplus/asio/object_server.hpp>
4918a5ab91SZhikui Ren
5018a5ab91SZhikui Ren namespace cpu_info
5118a5ab91SZhikui Ren {
526d3ad586SZhikui Ren static constexpr bool debug = false;
532285be4fSJonathan Doman static constexpr const char* assetInterfaceName =
5418a5ab91SZhikui Ren "xyz.openbmc_project.Inventory.Decorator.Asset";
5518a5ab91SZhikui Ren static constexpr const char* cpuProcessName =
5618a5ab91SZhikui Ren "xyz.openbmc_project.Smbios.MDR_V2";
5718a5ab91SZhikui Ren
586d3ad586SZhikui Ren // constants for reading SSPEC or QDF string from PIROM
59b86e4f19SJason M. Bills // Currently, they are the same for platforms with Ice Lake
606d3ad586SZhikui Ren static constexpr uint8_t defaultI2cBus = 13;
616d3ad586SZhikui Ren static constexpr uint8_t defaultI2cSlaveAddr0 = 0x50;
626d3ad586SZhikui Ren static constexpr uint8_t sspecRegAddr = 0xd;
636d3ad586SZhikui Ren static constexpr uint8_t sspecSize = 6;
6418a5ab91SZhikui Ren
656d3ad586SZhikui Ren using CPUInfoMap = boost::container::flat_map<size_t, std::shared_ptr<CPUInfo>>;
6618a5ab91SZhikui Ren
676d3ad586SZhikui Ren static CPUInfoMap cpuInfoMap = {};
6818a5ab91SZhikui Ren
692285be4fSJonathan Doman /**
702285be4fSJonathan Doman * Simple aggregate to define an external D-Bus property which needs to be set
712285be4fSJonathan Doman * by this application.
722285be4fSJonathan Doman */
732285be4fSJonathan Doman struct CpuProperty
742285be4fSJonathan Doman {
752285be4fSJonathan Doman std::string object;
762285be4fSJonathan Doman std::string interface;
772285be4fSJonathan Doman std::string name;
782285be4fSJonathan Doman std::string value;
792285be4fSJonathan Doman };
802285be4fSJonathan Doman
812285be4fSJonathan Doman /**
822285be4fSJonathan Doman * List of properties we want to set on other D-Bus objects. This list is kept
83*0fe13abaSManojkiran Eda * around so that if any target objects are removed+re-added, then we can set
84*0fe13abaSManojkiran Eda * the values again.
852285be4fSJonathan Doman */
862285be4fSJonathan Doman static std::list<CpuProperty> propertiesToSet;
872285be4fSJonathan Doman
logStream(int cpu)882285be4fSJonathan Doman static std::ostream& logStream(int cpu)
892285be4fSJonathan Doman {
902285be4fSJonathan Doman return std::cerr << "[CPU " << cpu << "] ";
912285be4fSJonathan Doman }
922285be4fSJonathan Doman
932285be4fSJonathan Doman static void
942285be4fSJonathan Doman setCpuProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
952285be4fSJonathan Doman size_t cpu, const std::string& interface,
962285be4fSJonathan Doman const std::string& propName, const std::string& propVal);
972285be4fSJonathan Doman static void
982285be4fSJonathan Doman setDbusProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
992285be4fSJonathan Doman size_t cpu, const CpuProperty& newProp);
1002285be4fSJonathan Doman static void createCpuUpdatedMatch(
1012285be4fSJonathan Doman const std::shared_ptr<sdbusplus::asio::connection>& conn, size_t cpu);
10218a5ab91SZhikui Ren
readSSpec(uint8_t bus,uint8_t slaveAddr,uint8_t regAddr,size_t count)10318a5ab91SZhikui Ren static std::optional<std::string> readSSpec(uint8_t bus, uint8_t slaveAddr,
10418a5ab91SZhikui Ren uint8_t regAddr, size_t count)
10518a5ab91SZhikui Ren {
10618a5ab91SZhikui Ren unsigned long funcs = 0;
10718a5ab91SZhikui Ren std::string devPath = "/dev/i2c-" + std::to_string(bus);
10818a5ab91SZhikui Ren
10918a5ab91SZhikui Ren int fd = ::open(devPath.c_str(), O_RDWR);
11018a5ab91SZhikui Ren if (fd < 0)
11118a5ab91SZhikui Ren {
11218a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
11318a5ab91SZhikui Ren "Error in open!",
11418a5ab91SZhikui Ren phosphor::logging::entry("PATH=%s", devPath.c_str()),
11518a5ab91SZhikui Ren phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
11618a5ab91SZhikui Ren return std::nullopt;
11718a5ab91SZhikui Ren }
11818a5ab91SZhikui Ren
11918a5ab91SZhikui Ren if (::ioctl(fd, I2C_FUNCS, &funcs) < 0)
12018a5ab91SZhikui Ren {
12118a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
12218a5ab91SZhikui Ren "Error in I2C_FUNCS!",
12318a5ab91SZhikui Ren phosphor::logging::entry("PATH=%s", devPath.c_str()),
12418a5ab91SZhikui Ren phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
12518a5ab91SZhikui Ren ::close(fd);
12618a5ab91SZhikui Ren return std::nullopt;
12718a5ab91SZhikui Ren }
12818a5ab91SZhikui Ren
12918a5ab91SZhikui Ren if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
13018a5ab91SZhikui Ren {
13118a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
13218a5ab91SZhikui Ren "i2c bus does not support read!",
13318a5ab91SZhikui Ren phosphor::logging::entry("PATH=%s", devPath.c_str()),
13418a5ab91SZhikui Ren phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
13518a5ab91SZhikui Ren ::close(fd);
13618a5ab91SZhikui Ren return std::nullopt;
13718a5ab91SZhikui Ren }
13818a5ab91SZhikui Ren
13918a5ab91SZhikui Ren if (::ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
14018a5ab91SZhikui Ren {
14118a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
14218a5ab91SZhikui Ren "Error in I2C_SLAVE_FORCE!",
14318a5ab91SZhikui Ren phosphor::logging::entry("PATH=%s", devPath.c_str()),
14418a5ab91SZhikui Ren phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
14518a5ab91SZhikui Ren ::close(fd);
14618a5ab91SZhikui Ren return std::nullopt;
14718a5ab91SZhikui Ren }
14818a5ab91SZhikui Ren
14918a5ab91SZhikui Ren int value = 0;
15018a5ab91SZhikui Ren std::string sspec;
15118a5ab91SZhikui Ren sspec.reserve(count);
15218a5ab91SZhikui Ren
15318a5ab91SZhikui Ren for (size_t i = 0; i < count; i++)
15418a5ab91SZhikui Ren {
1556d3ad586SZhikui Ren value = ::i2c_smbus_read_byte_data(fd, regAddr + i);
15618a5ab91SZhikui Ren if (value < 0)
15718a5ab91SZhikui Ren {
15818a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
15918a5ab91SZhikui Ren "Error in i2c read!",
16018a5ab91SZhikui Ren phosphor::logging::entry("PATH=%s", devPath.c_str()),
16118a5ab91SZhikui Ren phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
16218a5ab91SZhikui Ren ::close(fd);
16318a5ab91SZhikui Ren return std::nullopt;
16418a5ab91SZhikui Ren }
16518a5ab91SZhikui Ren if (!std::isprint(static_cast<unsigned char>(value)))
16618a5ab91SZhikui Ren {
16718a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
16818a5ab91SZhikui Ren "Non printable value in sspec, ignored.");
16918a5ab91SZhikui Ren continue;
17018a5ab91SZhikui Ren }
1716d3ad586SZhikui Ren // sspec always starts with S,
1726d3ad586SZhikui Ren // if not assume it is QDF string which starts at offset 2
1736d3ad586SZhikui Ren if (i == 0 && static_cast<unsigned char>(value) != 'S')
1746d3ad586SZhikui Ren {
1756d3ad586SZhikui Ren i = 1;
1766d3ad586SZhikui Ren continue;
1776d3ad586SZhikui Ren }
17818a5ab91SZhikui Ren sspec.push_back(static_cast<unsigned char>(value));
17918a5ab91SZhikui Ren }
18018a5ab91SZhikui Ren ::close(fd);
1812285be4fSJonathan Doman
1822285be4fSJonathan Doman if (sspec.size() < 4)
1832285be4fSJonathan Doman {
1842285be4fSJonathan Doman return std::nullopt;
1852285be4fSJonathan Doman }
1862285be4fSJonathan Doman
18718a5ab91SZhikui Ren return sspec;
18818a5ab91SZhikui Ren }
18918a5ab91SZhikui Ren
1902285be4fSJonathan Doman /**
1912285be4fSJonathan Doman * Higher level SSpec logic.
1922285be4fSJonathan Doman * This handles retrying the PIROM reads until two subsequent reads are
1932285be4fSJonathan Doman * successful and return matching data. When we have confidence that the data
1942285be4fSJonathan Doman * read is correct, then set the property on D-Bus.
1952285be4fSJonathan Doman */
1962285be4fSJonathan Doman static void
tryReadSSpec(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t cpuIndex)1972285be4fSJonathan Doman tryReadSSpec(const std::shared_ptr<sdbusplus::asio::connection>& conn,
1984e1cf099SJonathan Doman size_t cpuIndex)
1992285be4fSJonathan Doman {
2002285be4fSJonathan Doman static int failedReads = 0;
2012285be4fSJonathan Doman
2024e1cf099SJonathan Doman auto cpuInfoIt = cpuInfoMap.find(cpuIndex);
2034e1cf099SJonathan Doman if (cpuInfoIt == cpuInfoMap.end())
2044e1cf099SJonathan Doman {
2054e1cf099SJonathan Doman return;
2064e1cf099SJonathan Doman }
2074e1cf099SJonathan Doman auto cpuInfo = cpuInfoIt->second;
2084e1cf099SJonathan Doman
2092285be4fSJonathan Doman std::optional<std::string> newSSpec =
2102285be4fSJonathan Doman readSSpec(cpuInfo->i2cBus, cpuInfo->i2cDevice, sspecRegAddr, sspecSize);
2112285be4fSJonathan Doman logStream(cpuInfo->id) << "SSpec read status: "
2122285be4fSJonathan Doman << static_cast<bool>(newSSpec) << "\n";
2132285be4fSJonathan Doman if (newSSpec && newSSpec == cpuInfo->sSpec)
2142285be4fSJonathan Doman {
2152285be4fSJonathan Doman setCpuProperty(conn, cpuInfo->id, assetInterfaceName, "Model",
2162285be4fSJonathan Doman *newSSpec);
2172285be4fSJonathan Doman return;
2182285be4fSJonathan Doman }
2192285be4fSJonathan Doman
2202285be4fSJonathan Doman // If this read failed, back off for a little longer so that hopefully the
2212285be4fSJonathan Doman // transient condition affecting PIROM reads will pass, but give up after
2222285be4fSJonathan Doman // several consecutive failures. But if this read looked OK, try again
2232285be4fSJonathan Doman // sooner to confirm it.
2242285be4fSJonathan Doman int retrySeconds;
2252285be4fSJonathan Doman if (newSSpec)
2262285be4fSJonathan Doman {
2272285be4fSJonathan Doman retrySeconds = 1;
2282285be4fSJonathan Doman failedReads = 0;
2292285be4fSJonathan Doman cpuInfo->sSpec = *newSSpec;
2302285be4fSJonathan Doman }
2312285be4fSJonathan Doman else
2322285be4fSJonathan Doman {
2332285be4fSJonathan Doman retrySeconds = 5;
2342285be4fSJonathan Doman if (++failedReads > 10)
2352285be4fSJonathan Doman {
2362285be4fSJonathan Doman logStream(cpuInfo->id) << "PIROM Read failed too many times\n";
2372285be4fSJonathan Doman return;
2382285be4fSJonathan Doman }
2392285be4fSJonathan Doman }
2402285be4fSJonathan Doman
2412285be4fSJonathan Doman auto sspecTimer = std::make_shared<boost::asio::steady_timer>(
2422285be4fSJonathan Doman conn->get_io_context(), std::chrono::seconds(retrySeconds));
2432285be4fSJonathan Doman sspecTimer->async_wait(
2444e1cf099SJonathan Doman [sspecTimer, conn, cpuIndex](boost::system::error_code ec) {
2452285be4fSJonathan Doman if (ec)
2462285be4fSJonathan Doman {
2472285be4fSJonathan Doman return;
2482285be4fSJonathan Doman }
2494e1cf099SJonathan Doman tryReadSSpec(conn, cpuIndex);
2502285be4fSJonathan Doman });
2512285be4fSJonathan Doman }
2522285be4fSJonathan Doman
2532285be4fSJonathan Doman /**
2542285be4fSJonathan Doman * Add a D-Bus property to the global list, and attempt to set it by calling
2552285be4fSJonathan Doman * `setDbusProperty`.
2562285be4fSJonathan Doman *
2572285be4fSJonathan Doman * @param[in,out] conn D-Bus connection.
2582285be4fSJonathan Doman * @param[in] cpu 1-based CPU index.
2592285be4fSJonathan Doman * @param[in] interface Interface to set.
2602285be4fSJonathan Doman * @param[in] propName Property to set.
2612285be4fSJonathan Doman * @param[in] propVal Value to set.
2622285be4fSJonathan Doman */
2632285be4fSJonathan Doman static void
setCpuProperty(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t cpu,const std::string & interface,const std::string & propName,const std::string & propVal)2642285be4fSJonathan Doman setCpuProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
2652285be4fSJonathan Doman size_t cpu, const std::string& interface,
2662285be4fSJonathan Doman const std::string& propName, const std::string& propVal)
26718a5ab91SZhikui Ren {
2686d3ad586SZhikui Ren // cpuId from configuration is one based as
2696d3ad586SZhikui Ren // dbus object path used by smbios is 0 based
2706d3ad586SZhikui Ren const std::string objectPath = cpuPath + std::to_string(cpu - 1);
2712285be4fSJonathan Doman
2722285be4fSJonathan Doman // Can switch to emplace_back if you define a CpuProperty constructor.
2732285be4fSJonathan Doman propertiesToSet.push_back(
2742285be4fSJonathan Doman CpuProperty{objectPath, interface, propName, propVal});
2752285be4fSJonathan Doman
2762285be4fSJonathan Doman setDbusProperty(conn, cpu, propertiesToSet.back());
2772285be4fSJonathan Doman }
2782285be4fSJonathan Doman
2792285be4fSJonathan Doman /**
2802285be4fSJonathan Doman * Set a D-Bus property which is already contained in the global list, and also
2812285be4fSJonathan Doman * setup a D-Bus match to make sure the target property stays correct.
2822285be4fSJonathan Doman *
2832285be4fSJonathan Doman * @param[in,out] conn D-Bus connection.
2842285be4fSJonathan Doman * @param[in] cpu 1-baesd CPU index.
2852285be4fSJonathan Doman * @param[in] newProp Property to set.
2862285be4fSJonathan Doman */
2872285be4fSJonathan Doman static void
setDbusProperty(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t cpu,const CpuProperty & newProp)2882285be4fSJonathan Doman setDbusProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
2892285be4fSJonathan Doman size_t cpu, const CpuProperty& newProp)
29018a5ab91SZhikui Ren {
2912285be4fSJonathan Doman createCpuUpdatedMatch(conn, cpu);
29218a5ab91SZhikui Ren conn->async_method_call(
29318a5ab91SZhikui Ren [](const boost::system::error_code ec) {
29418a5ab91SZhikui Ren if (ec)
29518a5ab91SZhikui Ren {
29618a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
2972285be4fSJonathan Doman "Cannot set CPU property!");
29818a5ab91SZhikui Ren return;
29918a5ab91SZhikui Ren }
30018a5ab91SZhikui Ren },
3012285be4fSJonathan Doman cpuProcessName, newProp.object.c_str(),
3022285be4fSJonathan Doman "org.freedesktop.DBus.Properties", "Set", newProp.interface,
3032285be4fSJonathan Doman newProp.name, std::variant<std::string>{newProp.value});
30418a5ab91SZhikui Ren }
30518a5ab91SZhikui Ren
3062285be4fSJonathan Doman /**
3072285be4fSJonathan Doman * Set up a D-Bus match (if one does not already exist) to watch for any new
3082285be4fSJonathan Doman * interfaces on the cpu object. When new interfaces are added, re-send all
3092285be4fSJonathan Doman * properties targeting that object/interface.
3102285be4fSJonathan Doman *
3112285be4fSJonathan Doman * @param[in,out] conn D-Bus connection.
3122285be4fSJonathan Doman * @param[in] cpu 1-based CPU index.
3132285be4fSJonathan Doman */
createCpuUpdatedMatch(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t cpu)31418a5ab91SZhikui Ren static void createCpuUpdatedMatch(
3152285be4fSJonathan Doman const std::shared_ptr<sdbusplus::asio::connection>& conn, size_t cpu)
31618a5ab91SZhikui Ren {
3172285be4fSJonathan Doman static boost::container::flat_map<size_t,
3182285be4fSJonathan Doman std::unique_ptr<sdbusplus::bus::match_t>>
3192285be4fSJonathan Doman cpuUpdatedMatch;
3202285be4fSJonathan Doman
3216d3ad586SZhikui Ren if (cpuUpdatedMatch[cpu])
32218a5ab91SZhikui Ren {
32318a5ab91SZhikui Ren return;
32418a5ab91SZhikui Ren }
32518a5ab91SZhikui Ren
3266d3ad586SZhikui Ren const std::string objectPath = cpuPath + std::to_string(cpu - 1);
32718a5ab91SZhikui Ren
3286d3ad586SZhikui Ren cpuUpdatedMatch.insert_or_assign(
3296d3ad586SZhikui Ren cpu,
33077b9c478SPatrick Williams std::make_unique<sdbusplus::bus::match_t>(
33177b9c478SPatrick Williams static_cast<sdbusplus::bus_t&>(*conn),
33218a5ab91SZhikui Ren sdbusplus::bus::match::rules::interfacesAdded() +
33318a5ab91SZhikui Ren sdbusplus::bus::match::rules::argNpath(0, objectPath.c_str()),
33477b9c478SPatrick Williams [conn, cpu](sdbusplus::message_t& msg) {
335a43eec82SJonathan Doman sdbusplus::message::object_path objectName;
33618a5ab91SZhikui Ren boost::container::flat_map<
337c39d3dfcSPatrick Williams std::string, boost::container::flat_map<
3386d3ad586SZhikui Ren std::string, std::variant<std::string, uint64_t>>>
33918a5ab91SZhikui Ren msgData;
34018a5ab91SZhikui Ren
34118a5ab91SZhikui Ren msg.read(objectName, msgData);
34218a5ab91SZhikui Ren
3432285be4fSJonathan Doman // Go through all the property changes, and retry all of them
3442285be4fSJonathan Doman // targeting this object/interface which was just added.
3452285be4fSJonathan Doman for (const CpuProperty& prop : propertiesToSet)
34618a5ab91SZhikui Ren {
347c39d3dfcSPatrick Williams if (prop.object == objectName && msgData.contains(prop.interface))
3482285be4fSJonathan Doman {
3492285be4fSJonathan Doman setDbusProperty(conn, cpu, prop);
3502285be4fSJonathan Doman }
35118a5ab91SZhikui Ren }
3526d3ad586SZhikui Ren }));
35318a5ab91SZhikui Ren }
35418a5ab91SZhikui Ren
3554e1cf099SJonathan Doman #if PECI_ENABLED
getPPIN(boost::asio::io_service & io,const std::shared_ptr<sdbusplus::asio::connection> & conn,const size_t & cpu)3564e1cf099SJonathan Doman static void getPPIN(boost::asio::io_service& io,
3576d3ad586SZhikui Ren const std::shared_ptr<sdbusplus::asio::connection>& conn,
3586d3ad586SZhikui Ren const size_t& cpu)
35918a5ab91SZhikui Ren {
3606d3ad586SZhikui Ren if (cpuInfoMap.find(cpu) == cpuInfoMap.end() || cpuInfoMap[cpu] == nullptr)
36118a5ab91SZhikui Ren {
3626d3ad586SZhikui Ren std::cerr << "No information found for cpu " << cpu << "\n";
3636d3ad586SZhikui Ren return;
3646d3ad586SZhikui Ren }
3656d3ad586SZhikui Ren
3662285be4fSJonathan Doman std::shared_ptr<CPUInfo> cpuInfo = cpuInfoMap[cpu];
3672285be4fSJonathan Doman
3682285be4fSJonathan Doman if (cpuInfo->id != cpu)
3696d3ad586SZhikui Ren {
3702285be4fSJonathan Doman std::cerr << "Incorrect CPU id " << (unsigned)cpuInfo->id << " expect "
3712285be4fSJonathan Doman << cpu << "\n";
3726d3ad586SZhikui Ren return;
3736d3ad586SZhikui Ren }
3746d3ad586SZhikui Ren
3752285be4fSJonathan Doman uint8_t cpuAddr = cpuInfo->peciAddr;
3766d3ad586SZhikui Ren
37718a5ab91SZhikui Ren uint8_t cc = 0;
37818a5ab91SZhikui Ren CPUModel model{};
37918a5ab91SZhikui Ren uint8_t stepping = 0;
38018a5ab91SZhikui Ren
3810a385373SJonathan Doman // Wait for POST to complete to ensure that BIOS has time to enable the
3820a385373SJonathan Doman // PPIN. Before BIOS enables it, we would get a 0x90 CC on PECI.
3830a385373SJonathan Doman if (hostState != HostState::postComplete ||
3840a385373SJonathan Doman peci_GetCPUID(cpuAddr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
3856d3ad586SZhikui Ren {
3866d3ad586SZhikui Ren // Start the PECI check loop
3876d3ad586SZhikui Ren auto waitTimer = std::make_shared<boost::asio::steady_timer>(io);
3886d3ad586SZhikui Ren waitTimer->expires_after(
3890a385373SJonathan Doman std::chrono::seconds(cpu_info::peciCheckInterval));
39018a5ab91SZhikui Ren
3916d3ad586SZhikui Ren waitTimer->async_wait(
3926d3ad586SZhikui Ren [waitTimer, &io, conn, cpu](const boost::system::error_code& ec) {
3936d3ad586SZhikui Ren if (ec)
3946d3ad586SZhikui Ren {
3956d3ad586SZhikui Ren // operation_aborted is expected if timer is canceled
3966d3ad586SZhikui Ren // before completion.
3976d3ad586SZhikui Ren if (ec != boost::asio::error::operation_aborted)
39818a5ab91SZhikui Ren {
39918a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
4006d3ad586SZhikui Ren "info update timer async_wait failed ",
4016d3ad586SZhikui Ren phosphor::logging::entry("EC=0x%x", ec.value()));
4026d3ad586SZhikui Ren }
4036d3ad586SZhikui Ren return;
4046d3ad586SZhikui Ren }
4054e1cf099SJonathan Doman getPPIN(io, conn, cpu);
4066d3ad586SZhikui Ren });
4076d3ad586SZhikui Ren return;
40818a5ab91SZhikui Ren }
40918a5ab91SZhikui Ren
41018a5ab91SZhikui Ren switch (model)
41118a5ab91SZhikui Ren {
412b86e4f19SJason M. Bills case iceLake:
413b86e4f19SJason M. Bills case iceLakeD:
414b86e4f19SJason M. Bills case sapphireRapids:
415b86e4f19SJason M. Bills case emeraldRapids:
416b86e4f19SJason M. Bills case graniteRapids:
417b86e4f19SJason M. Bills case graniteRapidsD:
418b86e4f19SJason M. Bills case sierraForest:
41918a5ab91SZhikui Ren {
4206d3ad586SZhikui Ren // PPIN can be read through PCS 19
42118a5ab91SZhikui Ren static constexpr uint8_t u8Size = 4; // default to a DWORD
42218a5ab91SZhikui Ren static constexpr uint8_t u8PPINPkgIndex = 19;
42318a5ab91SZhikui Ren static constexpr uint16_t u16PPINPkgParamHigh = 2;
42418a5ab91SZhikui Ren static constexpr uint16_t u16PPINPkgParamLow = 1;
42518a5ab91SZhikui Ren uint64_t cpuPPIN = 0;
42618a5ab91SZhikui Ren uint32_t u32PkgValue = 0;
42718a5ab91SZhikui Ren
428c39d3dfcSPatrick Williams int ret = peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex,
429c39d3dfcSPatrick Williams u16PPINPkgParamLow, u8Size,
430c39d3dfcSPatrick Williams (uint8_t*)&u32PkgValue, &cc);
43118a5ab91SZhikui Ren if (0 != ret)
43218a5ab91SZhikui Ren {
43318a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
43418a5ab91SZhikui Ren "peci read package config failed at address",
4356d3ad586SZhikui Ren phosphor::logging::entry("PECIADDR=0x%x",
4366d3ad586SZhikui Ren (unsigned)cpuAddr),
43718a5ab91SZhikui Ren phosphor::logging::entry("CC=0x%x", cc));
43818a5ab91SZhikui Ren u32PkgValue = 0;
43918a5ab91SZhikui Ren }
44018a5ab91SZhikui Ren
44118a5ab91SZhikui Ren cpuPPIN = u32PkgValue;
4426d3ad586SZhikui Ren ret = peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamHigh,
4436d3ad586SZhikui Ren u8Size, (uint8_t*)&u32PkgValue, &cc);
44418a5ab91SZhikui Ren if (0 != ret)
44518a5ab91SZhikui Ren {
44618a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::ERR>(
44718a5ab91SZhikui Ren "peci read package config failed at address",
4486d3ad586SZhikui Ren phosphor::logging::entry("PECIADDR=0x%x",
4496d3ad586SZhikui Ren (unsigned)cpuAddr),
45018a5ab91SZhikui Ren phosphor::logging::entry("CC=0x%x", cc));
45118a5ab91SZhikui Ren cpuPPIN = 0;
45218a5ab91SZhikui Ren u32PkgValue = 0;
45318a5ab91SZhikui Ren }
45418a5ab91SZhikui Ren
45518a5ab91SZhikui Ren cpuPPIN |= static_cast<uint64_t>(u32PkgValue) << 32;
45618a5ab91SZhikui Ren
45718a5ab91SZhikui Ren // set SerialNumber if cpuPPIN is valid
45818a5ab91SZhikui Ren if (0 != cpuPPIN)
45918a5ab91SZhikui Ren {
46018a5ab91SZhikui Ren std::stringstream stream;
46118a5ab91SZhikui Ren stream << std::hex << cpuPPIN;
46218a5ab91SZhikui Ren std::string serialNumber(stream.str());
4634e1cf099SJonathan Doman cpuInfo->publishUUID(*conn, serialNumber);
46418a5ab91SZhikui Ren }
46518a5ab91SZhikui Ren break;
46618a5ab91SZhikui Ren }
46718a5ab91SZhikui Ren default:
46818a5ab91SZhikui Ren phosphor::logging::log<phosphor::logging::level::INFO>(
46918a5ab91SZhikui Ren "in-compatible cpu for cpu asset info");
47018a5ab91SZhikui Ren break;
47118a5ab91SZhikui Ren }
47218a5ab91SZhikui Ren }
4734e1cf099SJonathan Doman #endif
47418a5ab91SZhikui Ren
4756d3ad586SZhikui Ren /**
4766d3ad586SZhikui Ren * Get cpu and pirom address
4776d3ad586SZhikui Ren */
47818a5ab91SZhikui Ren 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)4796d3ad586SZhikui Ren getCpuAddress(boost::asio::io_service& io,
4806d3ad586SZhikui Ren const std::shared_ptr<sdbusplus::asio::connection>& conn,
4816d3ad586SZhikui Ren const std::string& service, const std::string& object,
4826d3ad586SZhikui Ren const std::string& interface)
48318a5ab91SZhikui Ren {
4846d3ad586SZhikui Ren conn->async_method_call(
4856d3ad586SZhikui Ren [&io, conn](boost::system::error_code ec,
4866d3ad586SZhikui Ren const boost::container::flat_map<
4876d3ad586SZhikui Ren std::string,
4886d3ad586SZhikui Ren std::variant<std::string, uint64_t, uint32_t, uint16_t,
4896d3ad586SZhikui Ren std::vector<std::string>>>& properties) {
4906d3ad586SZhikui Ren const uint64_t* value = nullptr;
4914e1cf099SJonathan Doman std::optional<uint8_t> peciAddress;
4926d3ad586SZhikui Ren uint8_t i2cBus = defaultI2cBus;
4934e1cf099SJonathan Doman std::optional<uint8_t> i2cDevice;
4944e1cf099SJonathan Doman std::optional<size_t> cpu;
4956d3ad586SZhikui Ren
49618a5ab91SZhikui Ren if (ec)
49718a5ab91SZhikui Ren {
4986d3ad586SZhikui Ren std::cerr << "DBUS response error " << ec.value() << ": "
4996d3ad586SZhikui Ren << ec.message() << "\n";
50018a5ab91SZhikui Ren return;
50118a5ab91SZhikui Ren }
5026d3ad586SZhikui Ren
5036d3ad586SZhikui Ren for (const auto& property : properties)
5046d3ad586SZhikui Ren {
5056d3ad586SZhikui Ren std::cerr << "property " << property.first << "\n";
5066d3ad586SZhikui Ren if (property.first == "Address")
5076d3ad586SZhikui Ren {
5086d3ad586SZhikui Ren value = std::get_if<uint64_t>(&property.second);
5096d3ad586SZhikui Ren if (value != nullptr)
5106d3ad586SZhikui Ren {
5116d3ad586SZhikui Ren peciAddress = static_cast<uint8_t>(*value);
51218a5ab91SZhikui Ren }
51318a5ab91SZhikui Ren }
5146d3ad586SZhikui Ren if (property.first == "CpuID")
5156d3ad586SZhikui Ren {
5166d3ad586SZhikui Ren value = std::get_if<uint64_t>(&property.second);
5176d3ad586SZhikui Ren if (value != nullptr)
5186d3ad586SZhikui Ren {
5196d3ad586SZhikui Ren cpu = static_cast<size_t>(*value);
5206d3ad586SZhikui Ren }
5216d3ad586SZhikui Ren }
5226d3ad586SZhikui Ren if (property.first == "PiromI2cAddress")
5236d3ad586SZhikui Ren {
5246d3ad586SZhikui Ren value = std::get_if<uint64_t>(&property.second);
5256d3ad586SZhikui Ren if (value != nullptr)
5266d3ad586SZhikui Ren {
5276d3ad586SZhikui Ren i2cDevice = static_cast<uint8_t>(*value);
5286d3ad586SZhikui Ren }
5296d3ad586SZhikui Ren }
5306d3ad586SZhikui Ren if (property.first == "PiromI2cBus")
5316d3ad586SZhikui Ren {
5326d3ad586SZhikui Ren value = std::get_if<uint64_t>(&property.second);
5336d3ad586SZhikui Ren if (value != nullptr)
5346d3ad586SZhikui Ren {
5356d3ad586SZhikui Ren i2cBus = static_cast<uint8_t>(*value);
5366d3ad586SZhikui Ren }
5376d3ad586SZhikui Ren }
5386d3ad586SZhikui Ren }
5396d3ad586SZhikui Ren
5404e1cf099SJonathan Doman if (!cpu || !peciAddress)
5416d3ad586SZhikui Ren {
5424e1cf099SJonathan Doman return;
5436d3ad586SZhikui Ren }
544132d037eSAnkita Vilas Gawade
5454e1cf099SJonathan Doman if (!i2cDevice)
5464e1cf099SJonathan Doman {
5474e1cf099SJonathan Doman i2cDevice = defaultI2cSlaveAddr0 + *cpu - 1;
5484e1cf099SJonathan Doman }
5494e1cf099SJonathan Doman
5504e1cf099SJonathan Doman auto key = cpuInfoMap.find(*cpu);
551132d037eSAnkita Vilas Gawade
552132d037eSAnkita Vilas Gawade if (key != cpuInfoMap.end())
553132d037eSAnkita Vilas Gawade {
554132d037eSAnkita Vilas Gawade cpuInfoMap.erase(key);
555132d037eSAnkita Vilas Gawade }
556132d037eSAnkita Vilas Gawade
5574e1cf099SJonathan Doman cpuInfoMap.emplace(*cpu, std::make_shared<CPUInfo>(*cpu, *peciAddress,
5584e1cf099SJonathan Doman i2cBus, *i2cDevice));
5596d3ad586SZhikui Ren
5604e1cf099SJonathan Doman tryReadSSpec(conn, *cpu);
5614e1cf099SJonathan Doman
5624e1cf099SJonathan Doman #if PECI_ENABLED
5634e1cf099SJonathan Doman getPPIN(io, conn, *cpu);
5644e1cf099SJonathan Doman #endif
5656d3ad586SZhikui Ren },
5666d3ad586SZhikui Ren service, object, "org.freedesktop.DBus.Properties", "GetAll",
5676d3ad586SZhikui Ren interface);
5686d3ad586SZhikui Ren }
5696d3ad586SZhikui Ren
5706d3ad586SZhikui Ren /**
5716d3ad586SZhikui Ren * D-Bus client: to get platform specific configs
5726d3ad586SZhikui Ren */
getCpuConfiguration(boost::asio::io_service & io,const std::shared_ptr<sdbusplus::asio::connection> & conn,sdbusplus::asio::object_server & objServer)5736d3ad586SZhikui Ren static void getCpuConfiguration(
5746d3ad586SZhikui Ren boost::asio::io_service& io,
5756d3ad586SZhikui Ren const std::shared_ptr<sdbusplus::asio::connection>& conn,
5766d3ad586SZhikui Ren sdbusplus::asio::object_server& objServer)
5776d3ad586SZhikui Ren {
5786d3ad586SZhikui Ren // Get the Cpu configuration
5796d3ad586SZhikui Ren // In case it's not available, set a match for it
58077b9c478SPatrick Williams static std::unique_ptr<sdbusplus::bus::match_t> cpuConfigMatch =
58177b9c478SPatrick Williams std::make_unique<sdbusplus::bus::match_t>(
5826d3ad586SZhikui Ren *conn,
5836d3ad586SZhikui Ren "type='signal',interface='org.freedesktop.DBus.Properties',member='"
5846d3ad586SZhikui Ren "PropertiesChanged',arg0='xyz.openbmc_project."
5856d3ad586SZhikui Ren "Configuration.XeonCPU'",
586f2d8bb48SJonathan Doman [&io, conn, &objServer](sdbusplus::message_t& /* msg */) {
5876d3ad586SZhikui Ren std::cerr << "get cpu configuration match\n";
5886d3ad586SZhikui Ren static boost::asio::steady_timer filterTimer(io);
589c39d3dfcSPatrick Williams filterTimer.expires_after(std::chrono::seconds(configCheckInterval));
5906d3ad586SZhikui Ren
5916d3ad586SZhikui Ren filterTimer.async_wait(
592c39d3dfcSPatrick Williams [&io, conn, &objServer](const boost::system::error_code& ec) {
5936d3ad586SZhikui Ren if (ec == boost::asio::error::operation_aborted)
5946d3ad586SZhikui Ren {
5956d3ad586SZhikui Ren return; // we're being canceled
5966d3ad586SZhikui Ren }
5976d3ad586SZhikui Ren else if (ec)
5986d3ad586SZhikui Ren {
5996d3ad586SZhikui Ren std::cerr << "Error: " << ec.message() << "\n";
6006d3ad586SZhikui Ren return;
6016d3ad586SZhikui Ren }
6026d3ad586SZhikui Ren getCpuConfiguration(io, conn, objServer);
6036d3ad586SZhikui Ren });
6046d3ad586SZhikui Ren });
6056d3ad586SZhikui Ren
6066d3ad586SZhikui Ren conn->async_method_call(
6076d3ad586SZhikui Ren [&io, conn](
6086d3ad586SZhikui Ren boost::system::error_code ec,
6096d3ad586SZhikui Ren const std::vector<std::pair<
6106d3ad586SZhikui Ren std::string,
6116d3ad586SZhikui Ren std::vector<std::pair<std::string, std::vector<std::string>>>>>&
6126d3ad586SZhikui Ren subtree) {
6136d3ad586SZhikui Ren if constexpr (debug)
6146d3ad586SZhikui Ren std::cerr << "async_method_call callback\n";
6156d3ad586SZhikui Ren
6166d3ad586SZhikui Ren if (ec)
6176d3ad586SZhikui Ren {
6186d3ad586SZhikui Ren std::cerr << "error with async_method_call\n";
6196d3ad586SZhikui Ren return;
6206d3ad586SZhikui Ren }
6216d3ad586SZhikui Ren if (subtree.empty())
6226d3ad586SZhikui Ren {
6236d3ad586SZhikui Ren // No config data yet, so wait for the match
6246d3ad586SZhikui Ren return;
6256d3ad586SZhikui Ren }
6266d3ad586SZhikui Ren
6276d3ad586SZhikui Ren for (const auto& object : subtree)
6286d3ad586SZhikui Ren {
6296d3ad586SZhikui Ren for (const auto& service : object.second)
6306d3ad586SZhikui Ren {
6316d3ad586SZhikui Ren getCpuAddress(io, conn, service.first, object.first,
6326d3ad586SZhikui Ren "xyz.openbmc_project.Configuration.XeonCPU");
6336d3ad586SZhikui Ren }
6346d3ad586SZhikui Ren }
6356d3ad586SZhikui Ren if constexpr (debug)
6366d3ad586SZhikui Ren std::cerr << "getCpuConfiguration callback complete\n";
6376d3ad586SZhikui Ren
6386d3ad586SZhikui Ren return;
6396d3ad586SZhikui Ren },
6406d3ad586SZhikui Ren "xyz.openbmc_project.ObjectMapper",
6416d3ad586SZhikui Ren "/xyz/openbmc_project/object_mapper",
6426d3ad586SZhikui Ren "xyz.openbmc_project.ObjectMapper", "GetSubTree",
6436d3ad586SZhikui Ren "/xyz/openbmc_project/", 0,
6446d3ad586SZhikui Ren std::array<const char*, 1>{
6456d3ad586SZhikui Ren "xyz.openbmc_project.Configuration.XeonCPU"});
6466d3ad586SZhikui Ren }
64718a5ab91SZhikui Ren
64818a5ab91SZhikui Ren } // namespace cpu_info
64918a5ab91SZhikui Ren
main()650f2d8bb48SJonathan Doman int main()
65118a5ab91SZhikui Ren {
65218a5ab91SZhikui Ren // setup connection to dbus
65316a2ced3SJonathan Doman boost::asio::io_service& io = cpu_info::dbus::getIOContext();
65418a5ab91SZhikui Ren std::shared_ptr<sdbusplus::asio::connection> conn =
65516a2ced3SJonathan Doman cpu_info::dbus::getConnection();
65618a5ab91SZhikui Ren
65718a5ab91SZhikui Ren // CPUInfo Object
6580a385373SJonathan Doman conn->request_name(cpu_info::cpuInfoObject);
65918a5ab91SZhikui Ren sdbusplus::asio::object_server server =
66018a5ab91SZhikui Ren sdbusplus::asio::object_server(conn);
66177b9c478SPatrick Williams sdbusplus::bus_t& bus = static_cast<sdbusplus::bus_t&>(*conn);
66277b9c478SPatrick Williams sdbusplus::server::manager_t objManager(bus,
66377b9c478SPatrick Williams "/xyz/openbmc_project/inventory");
66418a5ab91SZhikui Ren
665703a1856SJonathan Doman cpu_info::hostStateSetup(conn);
666703a1856SJonathan Doman
6674e1cf099SJonathan Doman #if PECI_ENABLED
66849ea830eSJonathan Doman cpu_info::sst::init();
6694e1cf099SJonathan Doman #endif
67094c94bfbSJonathan Doman
6716d3ad586SZhikui Ren // shared_ptr conn is global for the service
6726d3ad586SZhikui Ren // const reference of conn is passed to async calls
6730a385373SJonathan Doman cpu_info::getCpuConfiguration(io, conn, server);
67418a5ab91SZhikui Ren
67518a5ab91SZhikui Ren io.run();
67618a5ab91SZhikui Ren
67718a5ab91SZhikui Ren return 0;
67818a5ab91SZhikui Ren }
679