xref: /openbmc/smbios-mdr/src/cpuinfo_main.cpp (revision 49ea830e)
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"
1994c94bfbSJonathan Doman #include "speed_select.hpp"
2018a5ab91SZhikui Ren 
2118a5ab91SZhikui Ren #include <errno.h>
2218a5ab91SZhikui Ren #include <fcntl.h>
2318a5ab91SZhikui Ren #include <stdio.h>
2418a5ab91SZhikui Ren #include <sys/ioctl.h>
2518a5ab91SZhikui Ren 
2618a5ab91SZhikui Ren #include <boost/asio/io_service.hpp>
2718a5ab91SZhikui Ren #include <boost/asio/steady_timer.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 
4118a5ab91SZhikui Ren #include <peci.h>
4218a5ab91SZhikui Ren 
4318a5ab91SZhikui Ren #include <phosphor-logging/log.hpp>
4418a5ab91SZhikui Ren #include <sdbusplus/asio/object_server.hpp>
4518a5ab91SZhikui Ren 
4618a5ab91SZhikui Ren namespace cpu_info
4718a5ab91SZhikui Ren {
486d3ad586SZhikui Ren static constexpr bool debug = false;
492285be4fSJonathan Doman static constexpr const char* assetInterfaceName =
5018a5ab91SZhikui Ren     "xyz.openbmc_project.Inventory.Decorator.Asset";
5118a5ab91SZhikui Ren static constexpr const char* cpuProcessName =
5218a5ab91SZhikui Ren     "xyz.openbmc_project.Smbios.MDR_V2";
5318a5ab91SZhikui Ren 
546d3ad586SZhikui Ren // constants for reading SSPEC or QDF string from PIROM
556d3ad586SZhikui Ren // Currently, they are the same for platforms with icx
566d3ad586SZhikui Ren static constexpr uint8_t defaultI2cBus = 13;
576d3ad586SZhikui Ren static constexpr uint8_t defaultI2cSlaveAddr0 = 0x50;
586d3ad586SZhikui Ren static constexpr uint8_t sspecRegAddr = 0xd;
596d3ad586SZhikui Ren static constexpr uint8_t sspecSize = 6;
6018a5ab91SZhikui Ren 
616d3ad586SZhikui Ren using CPUInfoMap = boost::container::flat_map<size_t, std::shared_ptr<CPUInfo>>;
6218a5ab91SZhikui Ren 
636d3ad586SZhikui Ren static CPUInfoMap cpuInfoMap = {};
6418a5ab91SZhikui Ren 
652285be4fSJonathan Doman /**
662285be4fSJonathan Doman  * Simple aggregate to define an external D-Bus property which needs to be set
672285be4fSJonathan Doman  * by this application.
682285be4fSJonathan Doman  */
692285be4fSJonathan Doman struct CpuProperty
702285be4fSJonathan Doman {
712285be4fSJonathan Doman     std::string object;
722285be4fSJonathan Doman     std::string interface;
732285be4fSJonathan Doman     std::string name;
742285be4fSJonathan Doman     std::string value;
752285be4fSJonathan Doman };
762285be4fSJonathan Doman 
772285be4fSJonathan Doman /**
782285be4fSJonathan Doman  * List of properties we want to set on other D-Bus objects. This list is kept
792285be4fSJonathan Doman  * around so that if any target objects are removed+readded, then we can set the
802285be4fSJonathan Doman  * values again.
812285be4fSJonathan Doman  */
822285be4fSJonathan Doman static std::list<CpuProperty> propertiesToSet;
832285be4fSJonathan Doman 
842285be4fSJonathan Doman static std::ostream& logStream(int cpu)
852285be4fSJonathan Doman {
862285be4fSJonathan Doman     return std::cerr << "[CPU " << cpu << "] ";
872285be4fSJonathan Doman }
882285be4fSJonathan Doman 
892285be4fSJonathan Doman static void
902285be4fSJonathan Doman     setCpuProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
912285be4fSJonathan Doman                    size_t cpu, const std::string& interface,
922285be4fSJonathan Doman                    const std::string& propName, const std::string& propVal);
932285be4fSJonathan Doman static void
942285be4fSJonathan Doman     setDbusProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
952285be4fSJonathan Doman                     size_t cpu, const CpuProperty& newProp);
962285be4fSJonathan Doman static void createCpuUpdatedMatch(
972285be4fSJonathan Doman     const std::shared_ptr<sdbusplus::asio::connection>& conn, size_t cpu);
9818a5ab91SZhikui Ren 
9918a5ab91SZhikui Ren static std::optional<std::string> readSSpec(uint8_t bus, uint8_t slaveAddr,
10018a5ab91SZhikui Ren                                             uint8_t regAddr, size_t count)
10118a5ab91SZhikui Ren {
10218a5ab91SZhikui Ren     unsigned long funcs = 0;
10318a5ab91SZhikui Ren     std::string devPath = "/dev/i2c-" + std::to_string(bus);
10418a5ab91SZhikui Ren 
10518a5ab91SZhikui Ren     int fd = ::open(devPath.c_str(), O_RDWR);
10618a5ab91SZhikui Ren     if (fd < 0)
10718a5ab91SZhikui Ren     {
10818a5ab91SZhikui Ren         phosphor::logging::log<phosphor::logging::level::ERR>(
10918a5ab91SZhikui Ren             "Error in open!",
11018a5ab91SZhikui Ren             phosphor::logging::entry("PATH=%s", devPath.c_str()),
11118a5ab91SZhikui Ren             phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
11218a5ab91SZhikui Ren         return std::nullopt;
11318a5ab91SZhikui Ren     }
11418a5ab91SZhikui Ren 
11518a5ab91SZhikui Ren     if (::ioctl(fd, I2C_FUNCS, &funcs) < 0)
11618a5ab91SZhikui Ren     {
11718a5ab91SZhikui Ren         phosphor::logging::log<phosphor::logging::level::ERR>(
11818a5ab91SZhikui Ren             "Error in I2C_FUNCS!",
11918a5ab91SZhikui Ren             phosphor::logging::entry("PATH=%s", devPath.c_str()),
12018a5ab91SZhikui Ren             phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
12118a5ab91SZhikui Ren         ::close(fd);
12218a5ab91SZhikui Ren         return std::nullopt;
12318a5ab91SZhikui Ren     }
12418a5ab91SZhikui Ren 
12518a5ab91SZhikui Ren     if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
12618a5ab91SZhikui Ren     {
12718a5ab91SZhikui Ren         phosphor::logging::log<phosphor::logging::level::ERR>(
12818a5ab91SZhikui Ren             "i2c bus does not support read!",
12918a5ab91SZhikui Ren             phosphor::logging::entry("PATH=%s", devPath.c_str()),
13018a5ab91SZhikui Ren             phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
13118a5ab91SZhikui Ren         ::close(fd);
13218a5ab91SZhikui Ren         return std::nullopt;
13318a5ab91SZhikui Ren     }
13418a5ab91SZhikui Ren 
13518a5ab91SZhikui Ren     if (::ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
13618a5ab91SZhikui Ren     {
13718a5ab91SZhikui Ren         phosphor::logging::log<phosphor::logging::level::ERR>(
13818a5ab91SZhikui Ren             "Error in I2C_SLAVE_FORCE!",
13918a5ab91SZhikui Ren             phosphor::logging::entry("PATH=%s", devPath.c_str()),
14018a5ab91SZhikui Ren             phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
14118a5ab91SZhikui Ren         ::close(fd);
14218a5ab91SZhikui Ren         return std::nullopt;
14318a5ab91SZhikui Ren     }
14418a5ab91SZhikui Ren 
14518a5ab91SZhikui Ren     int value = 0;
14618a5ab91SZhikui Ren     std::string sspec;
14718a5ab91SZhikui Ren     sspec.reserve(count);
14818a5ab91SZhikui Ren 
14918a5ab91SZhikui Ren     for (size_t i = 0; i < count; i++)
15018a5ab91SZhikui Ren     {
1516d3ad586SZhikui Ren         value = ::i2c_smbus_read_byte_data(fd, regAddr + i);
15218a5ab91SZhikui Ren         if (value < 0)
15318a5ab91SZhikui Ren         {
15418a5ab91SZhikui Ren             phosphor::logging::log<phosphor::logging::level::ERR>(
15518a5ab91SZhikui Ren                 "Error in i2c read!",
15618a5ab91SZhikui Ren                 phosphor::logging::entry("PATH=%s", devPath.c_str()),
15718a5ab91SZhikui Ren                 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
15818a5ab91SZhikui Ren             ::close(fd);
15918a5ab91SZhikui Ren             return std::nullopt;
16018a5ab91SZhikui Ren         }
16118a5ab91SZhikui Ren         if (!std::isprint(static_cast<unsigned char>(value)))
16218a5ab91SZhikui Ren         {
16318a5ab91SZhikui Ren             phosphor::logging::log<phosphor::logging::level::ERR>(
16418a5ab91SZhikui Ren                 "Non printable value in sspec, ignored.");
16518a5ab91SZhikui Ren             continue;
16618a5ab91SZhikui Ren         }
1676d3ad586SZhikui Ren         // sspec always starts with S,
1686d3ad586SZhikui Ren         // if not assume it is QDF string which starts at offset 2
1696d3ad586SZhikui Ren         if (i == 0 && static_cast<unsigned char>(value) != 'S')
1706d3ad586SZhikui Ren         {
1716d3ad586SZhikui Ren             i = 1;
1726d3ad586SZhikui Ren             continue;
1736d3ad586SZhikui Ren         }
17418a5ab91SZhikui Ren         sspec.push_back(static_cast<unsigned char>(value));
17518a5ab91SZhikui Ren     }
17618a5ab91SZhikui Ren     ::close(fd);
1772285be4fSJonathan Doman 
1782285be4fSJonathan Doman     if (sspec.size() < 4)
1792285be4fSJonathan Doman     {
1802285be4fSJonathan Doman         return std::nullopt;
1812285be4fSJonathan Doman     }
1822285be4fSJonathan Doman 
18318a5ab91SZhikui Ren     return sspec;
18418a5ab91SZhikui Ren }
18518a5ab91SZhikui Ren 
1862285be4fSJonathan Doman /**
1872285be4fSJonathan Doman  * Higher level SSpec logic.
1882285be4fSJonathan Doman  * This handles retrying the PIROM reads until two subsequent reads are
1892285be4fSJonathan Doman  * successful and return matching data. When we have confidence that the data
1902285be4fSJonathan Doman  * read is correct, then set the property on D-Bus.
1912285be4fSJonathan Doman  *
1922285be4fSJonathan Doman  * @param[in,out]   conn        D-Bus connection.
1932285be4fSJonathan Doman  * @param[in]       cpuInfo     CPU to read from.
1942285be4fSJonathan Doman  */
1952285be4fSJonathan Doman static void
1962285be4fSJonathan Doman     tryReadSSpec(const std::shared_ptr<sdbusplus::asio::connection>& conn,
1972285be4fSJonathan Doman                  const std::shared_ptr<CPUInfo>& cpuInfo)
1982285be4fSJonathan Doman {
1992285be4fSJonathan Doman     static int failedReads = 0;
2002285be4fSJonathan Doman 
2012285be4fSJonathan Doman     std::optional<std::string> newSSpec =
2022285be4fSJonathan Doman         readSSpec(cpuInfo->i2cBus, cpuInfo->i2cDevice, sspecRegAddr, sspecSize);
2032285be4fSJonathan Doman     logStream(cpuInfo->id) << "SSpec read status: "
2042285be4fSJonathan Doman                            << static_cast<bool>(newSSpec) << "\n";
2052285be4fSJonathan Doman     if (newSSpec && newSSpec == cpuInfo->sSpec)
2062285be4fSJonathan Doman     {
2072285be4fSJonathan Doman         setCpuProperty(conn, cpuInfo->id, assetInterfaceName, "Model",
2082285be4fSJonathan Doman                        *newSSpec);
2092285be4fSJonathan Doman         return;
2102285be4fSJonathan Doman     }
2112285be4fSJonathan Doman 
2122285be4fSJonathan Doman     // If this read failed, back off for a little longer so that hopefully the
2132285be4fSJonathan Doman     // transient condition affecting PIROM reads will pass, but give up after
2142285be4fSJonathan Doman     // several consecutive failures. But if this read looked OK, try again
2152285be4fSJonathan Doman     // sooner to confirm it.
2162285be4fSJonathan Doman     int retrySeconds;
2172285be4fSJonathan Doman     if (newSSpec)
2182285be4fSJonathan Doman     {
2192285be4fSJonathan Doman         retrySeconds = 1;
2202285be4fSJonathan Doman         failedReads = 0;
2212285be4fSJonathan Doman         cpuInfo->sSpec = *newSSpec;
2222285be4fSJonathan Doman     }
2232285be4fSJonathan Doman     else
2242285be4fSJonathan Doman     {
2252285be4fSJonathan Doman         retrySeconds = 5;
2262285be4fSJonathan Doman         if (++failedReads > 10)
2272285be4fSJonathan Doman         {
2282285be4fSJonathan Doman             logStream(cpuInfo->id) << "PIROM Read failed too many times\n";
2292285be4fSJonathan Doman             return;
2302285be4fSJonathan Doman         }
2312285be4fSJonathan Doman     }
2322285be4fSJonathan Doman 
2332285be4fSJonathan Doman     auto sspecTimer = std::make_shared<boost::asio::steady_timer>(
2342285be4fSJonathan Doman         conn->get_io_context(), std::chrono::seconds(retrySeconds));
2352285be4fSJonathan Doman     sspecTimer->async_wait(
2362285be4fSJonathan Doman         [sspecTimer, conn, cpuInfo](boost::system::error_code ec) {
2372285be4fSJonathan Doman             if (ec)
2382285be4fSJonathan Doman             {
2392285be4fSJonathan Doman                 return;
2402285be4fSJonathan Doman             }
2412285be4fSJonathan Doman             tryReadSSpec(conn, cpuInfo);
2422285be4fSJonathan Doman         });
2432285be4fSJonathan Doman }
2442285be4fSJonathan Doman 
2452285be4fSJonathan Doman /**
2462285be4fSJonathan Doman  * Add a D-Bus property to the global list, and attempt to set it by calling
2472285be4fSJonathan Doman  * `setDbusProperty`.
2482285be4fSJonathan Doman  *
2492285be4fSJonathan Doman  * @param[in,out]   conn        D-Bus connection.
2502285be4fSJonathan Doman  * @param[in]       cpu         1-based CPU index.
2512285be4fSJonathan Doman  * @param[in]       interface   Interface to set.
2522285be4fSJonathan Doman  * @param[in]       propName    Property to set.
2532285be4fSJonathan Doman  * @param[in]       propVal     Value to set.
2542285be4fSJonathan Doman  */
2552285be4fSJonathan Doman static void
2562285be4fSJonathan Doman     setCpuProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
2572285be4fSJonathan Doman                    size_t cpu, const std::string& interface,
2582285be4fSJonathan Doman                    const std::string& propName, const std::string& propVal)
25918a5ab91SZhikui Ren {
2606d3ad586SZhikui Ren     // cpuId from configuration is one based as
2616d3ad586SZhikui Ren     // dbus object path used by smbios is 0 based
2626d3ad586SZhikui Ren     const std::string objectPath = cpuPath + std::to_string(cpu - 1);
2632285be4fSJonathan Doman 
2642285be4fSJonathan Doman     // Can switch to emplace_back if you define a CpuProperty constructor.
2652285be4fSJonathan Doman     propertiesToSet.push_back(
2662285be4fSJonathan Doman         CpuProperty{objectPath, interface, propName, propVal});
2672285be4fSJonathan Doman 
2682285be4fSJonathan Doman     setDbusProperty(conn, cpu, propertiesToSet.back());
2692285be4fSJonathan Doman }
2702285be4fSJonathan Doman 
2712285be4fSJonathan Doman /**
2722285be4fSJonathan Doman  * Set a D-Bus property which is already contained in the global list, and also
2732285be4fSJonathan Doman  * setup a D-Bus match to make sure the target property stays correct.
2742285be4fSJonathan Doman  *
2752285be4fSJonathan Doman  * @param[in,out]   conn    D-Bus connection.
2762285be4fSJonathan Doman  * @param[in]       cpu     1-baesd CPU index.
2772285be4fSJonathan Doman  * @param[in]       newProp Property to set.
2782285be4fSJonathan Doman  */
2792285be4fSJonathan Doman static void
2802285be4fSJonathan Doman     setDbusProperty(const std::shared_ptr<sdbusplus::asio::connection>& conn,
2812285be4fSJonathan Doman                     size_t cpu, const CpuProperty& newProp)
28218a5ab91SZhikui Ren {
2832285be4fSJonathan Doman     createCpuUpdatedMatch(conn, cpu);
28418a5ab91SZhikui Ren     conn->async_method_call(
28518a5ab91SZhikui Ren         [](const boost::system::error_code ec) {
28618a5ab91SZhikui Ren             if (ec)
28718a5ab91SZhikui Ren             {
28818a5ab91SZhikui Ren                 phosphor::logging::log<phosphor::logging::level::ERR>(
2892285be4fSJonathan Doman                     "Cannot set CPU property!");
29018a5ab91SZhikui Ren                 return;
29118a5ab91SZhikui Ren             }
29218a5ab91SZhikui Ren         },
2932285be4fSJonathan Doman         cpuProcessName, newProp.object.c_str(),
2942285be4fSJonathan Doman         "org.freedesktop.DBus.Properties", "Set", newProp.interface,
2952285be4fSJonathan Doman         newProp.name, std::variant<std::string>{newProp.value});
29618a5ab91SZhikui Ren }
29718a5ab91SZhikui Ren 
2982285be4fSJonathan Doman /**
2992285be4fSJonathan Doman  * Set up a D-Bus match (if one does not already exist) to watch for any new
3002285be4fSJonathan Doman  * interfaces on the cpu object. When new interfaces are added, re-send all
3012285be4fSJonathan Doman  * properties targeting that object/interface.
3022285be4fSJonathan Doman  *
3032285be4fSJonathan Doman  * @param[in,out]   conn    D-Bus connection.
3042285be4fSJonathan Doman  * @param[in]       cpu     1-based CPU index.
3052285be4fSJonathan Doman  */
30618a5ab91SZhikui Ren static void createCpuUpdatedMatch(
3072285be4fSJonathan Doman     const std::shared_ptr<sdbusplus::asio::connection>& conn, size_t cpu)
30818a5ab91SZhikui Ren {
3092285be4fSJonathan Doman     static boost::container::flat_map<size_t,
3102285be4fSJonathan Doman                                       std::unique_ptr<sdbusplus::bus::match_t>>
3112285be4fSJonathan Doman         cpuUpdatedMatch;
3122285be4fSJonathan Doman 
3136d3ad586SZhikui Ren     if (cpuUpdatedMatch[cpu])
31418a5ab91SZhikui Ren     {
31518a5ab91SZhikui Ren         return;
31618a5ab91SZhikui Ren     }
31718a5ab91SZhikui Ren 
3186d3ad586SZhikui Ren     const std::string objectPath = cpuPath + std::to_string(cpu - 1);
31918a5ab91SZhikui Ren 
3206d3ad586SZhikui Ren     cpuUpdatedMatch.insert_or_assign(
3216d3ad586SZhikui Ren         cpu,
3226d3ad586SZhikui Ren         std::make_unique<sdbusplus::bus::match::match>(
32318a5ab91SZhikui Ren             static_cast<sdbusplus::bus::bus&>(*conn),
32418a5ab91SZhikui Ren             sdbusplus::bus::match::rules::interfacesAdded() +
32518a5ab91SZhikui Ren                 sdbusplus::bus::match::rules::argNpath(0, objectPath.c_str()),
3262285be4fSJonathan Doman             [conn, cpu](sdbusplus::message::message& msg) {
327a43eec82SJonathan Doman                 sdbusplus::message::object_path objectName;
32818a5ab91SZhikui Ren                 boost::container::flat_map<
32918a5ab91SZhikui Ren                     std::string,
3306d3ad586SZhikui Ren                     boost::container::flat_map<
3316d3ad586SZhikui Ren                         std::string, std::variant<std::string, uint64_t>>>
33218a5ab91SZhikui Ren                     msgData;
33318a5ab91SZhikui Ren 
33418a5ab91SZhikui Ren                 msg.read(objectName, msgData);
33518a5ab91SZhikui Ren 
3362285be4fSJonathan Doman                 // Go through all the property changes, and retry all of them
3372285be4fSJonathan Doman                 // targeting this object/interface which was just added.
3382285be4fSJonathan Doman                 for (const CpuProperty& prop : propertiesToSet)
33918a5ab91SZhikui Ren                 {
3402285be4fSJonathan Doman                     if (prop.object == objectName &&
3412285be4fSJonathan Doman                         msgData.contains(prop.interface))
3422285be4fSJonathan Doman                     {
3432285be4fSJonathan Doman                         setDbusProperty(conn, cpu, prop);
3442285be4fSJonathan Doman                     }
34518a5ab91SZhikui Ren                 }
3466d3ad586SZhikui Ren             }));
34718a5ab91SZhikui Ren }
34818a5ab91SZhikui Ren 
3496d3ad586SZhikui Ren static void
3506d3ad586SZhikui Ren     getProcessorInfo(boost::asio::io_service& io,
3516d3ad586SZhikui Ren                      const std::shared_ptr<sdbusplus::asio::connection>& conn,
3526d3ad586SZhikui Ren                      const size_t& cpu)
35318a5ab91SZhikui Ren {
3546d3ad586SZhikui Ren     if (cpuInfoMap.find(cpu) == cpuInfoMap.end() || cpuInfoMap[cpu] == nullptr)
35518a5ab91SZhikui Ren     {
3566d3ad586SZhikui Ren         std::cerr << "No information found for cpu " << cpu << "\n";
3576d3ad586SZhikui Ren         return;
3586d3ad586SZhikui Ren     }
3596d3ad586SZhikui Ren 
3602285be4fSJonathan Doman     std::shared_ptr<CPUInfo> cpuInfo = cpuInfoMap[cpu];
3612285be4fSJonathan Doman 
3622285be4fSJonathan Doman     if (cpuInfo->id != cpu)
3636d3ad586SZhikui Ren     {
3642285be4fSJonathan Doman         std::cerr << "Incorrect CPU id " << (unsigned)cpuInfo->id << " expect "
3652285be4fSJonathan Doman                   << cpu << "\n";
3666d3ad586SZhikui Ren         return;
3676d3ad586SZhikui Ren     }
3686d3ad586SZhikui Ren 
3692285be4fSJonathan Doman     uint8_t cpuAddr = cpuInfo->peciAddr;
3706d3ad586SZhikui Ren 
37118a5ab91SZhikui Ren     uint8_t cc = 0;
37218a5ab91SZhikui Ren     CPUModel model{};
37318a5ab91SZhikui Ren     uint8_t stepping = 0;
37418a5ab91SZhikui Ren 
3750a385373SJonathan Doman     // Wait for POST to complete to ensure that BIOS has time to enable the
3760a385373SJonathan Doman     // PPIN. Before BIOS enables it, we would get a 0x90 CC on PECI.
3770a385373SJonathan Doman     if (hostState != HostState::postComplete ||
3780a385373SJonathan Doman         peci_GetCPUID(cpuAddr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
3796d3ad586SZhikui Ren     {
3806d3ad586SZhikui Ren         // Start the PECI check loop
3816d3ad586SZhikui Ren         auto waitTimer = std::make_shared<boost::asio::steady_timer>(io);
3826d3ad586SZhikui Ren         waitTimer->expires_after(
3830a385373SJonathan Doman             std::chrono::seconds(cpu_info::peciCheckInterval));
38418a5ab91SZhikui Ren 
3856d3ad586SZhikui Ren         waitTimer->async_wait(
3866d3ad586SZhikui Ren             [waitTimer, &io, conn, cpu](const boost::system::error_code& ec) {
3876d3ad586SZhikui Ren                 if (ec)
3886d3ad586SZhikui Ren                 {
3896d3ad586SZhikui Ren                     // operation_aborted is expected if timer is canceled
3906d3ad586SZhikui Ren                     // before completion.
3916d3ad586SZhikui Ren                     if (ec != boost::asio::error::operation_aborted)
39218a5ab91SZhikui Ren                     {
39318a5ab91SZhikui Ren                         phosphor::logging::log<phosphor::logging::level::ERR>(
3946d3ad586SZhikui Ren                             "info update timer async_wait failed ",
3956d3ad586SZhikui Ren                             phosphor::logging::entry("EC=0x%x", ec.value()));
3966d3ad586SZhikui Ren                     }
3976d3ad586SZhikui Ren                     return;
3986d3ad586SZhikui Ren                 }
3996d3ad586SZhikui Ren                 getProcessorInfo(io, conn, cpu);
4006d3ad586SZhikui Ren             });
4016d3ad586SZhikui Ren         return;
40218a5ab91SZhikui Ren     }
40318a5ab91SZhikui Ren 
40418a5ab91SZhikui Ren     switch (model)
40518a5ab91SZhikui Ren     {
40618a5ab91SZhikui Ren         case icx:
407631388e6SJonathan Doman         case icxd:
408631388e6SJonathan Doman         case spr:
40918a5ab91SZhikui Ren         {
4106d3ad586SZhikui Ren             // PPIN can be read through PCS 19
41118a5ab91SZhikui Ren             static constexpr uint8_t u8Size = 4; // default to a DWORD
41218a5ab91SZhikui Ren             static constexpr uint8_t u8PPINPkgIndex = 19;
41318a5ab91SZhikui Ren             static constexpr uint16_t u16PPINPkgParamHigh = 2;
41418a5ab91SZhikui Ren             static constexpr uint16_t u16PPINPkgParamLow = 1;
41518a5ab91SZhikui Ren             uint64_t cpuPPIN = 0;
41618a5ab91SZhikui Ren             uint32_t u32PkgValue = 0;
41718a5ab91SZhikui Ren 
4186d3ad586SZhikui Ren             int ret =
4196d3ad586SZhikui Ren                 peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamLow,
4206d3ad586SZhikui Ren                                  u8Size, (uint8_t*)&u32PkgValue, &cc);
42118a5ab91SZhikui Ren             if (0 != ret)
42218a5ab91SZhikui Ren             {
42318a5ab91SZhikui Ren                 phosphor::logging::log<phosphor::logging::level::ERR>(
42418a5ab91SZhikui Ren                     "peci read package config failed at address",
4256d3ad586SZhikui Ren                     phosphor::logging::entry("PECIADDR=0x%x",
4266d3ad586SZhikui Ren                                              (unsigned)cpuAddr),
42718a5ab91SZhikui Ren                     phosphor::logging::entry("CC=0x%x", cc));
42818a5ab91SZhikui Ren                 u32PkgValue = 0;
42918a5ab91SZhikui Ren             }
43018a5ab91SZhikui Ren 
43118a5ab91SZhikui Ren             cpuPPIN = u32PkgValue;
4326d3ad586SZhikui Ren             ret = peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamHigh,
4336d3ad586SZhikui Ren                                    u8Size, (uint8_t*)&u32PkgValue, &cc);
43418a5ab91SZhikui Ren             if (0 != ret)
43518a5ab91SZhikui Ren             {
43618a5ab91SZhikui Ren                 phosphor::logging::log<phosphor::logging::level::ERR>(
43718a5ab91SZhikui Ren                     "peci read package config failed at address",
4386d3ad586SZhikui Ren                     phosphor::logging::entry("PECIADDR=0x%x",
4396d3ad586SZhikui Ren                                              (unsigned)cpuAddr),
44018a5ab91SZhikui Ren                     phosphor::logging::entry("CC=0x%x", cc));
44118a5ab91SZhikui Ren                 cpuPPIN = 0;
44218a5ab91SZhikui Ren                 u32PkgValue = 0;
44318a5ab91SZhikui Ren             }
44418a5ab91SZhikui Ren 
44518a5ab91SZhikui Ren             cpuPPIN |= static_cast<uint64_t>(u32PkgValue) << 32;
44618a5ab91SZhikui Ren 
44718a5ab91SZhikui Ren             // set SerialNumber if cpuPPIN is valid
44818a5ab91SZhikui Ren             if (0 != cpuPPIN)
44918a5ab91SZhikui Ren             {
45018a5ab91SZhikui Ren                 std::stringstream stream;
45118a5ab91SZhikui Ren                 stream << std::hex << cpuPPIN;
45218a5ab91SZhikui Ren                 std::string serialNumber(stream.str());
4535b285892SJonathan Doman                 cpuInfo->uniqueIdentifier(serialNumber);
4545b285892SJonathan Doman                 // Signal that the iface is added now so that ObjectMapper and
4555b285892SJonathan Doman                 // others can find it.
4565b285892SJonathan Doman                 cpuInfo->emit_added();
45718a5ab91SZhikui Ren             }
45818a5ab91SZhikui Ren 
4592285be4fSJonathan Doman             tryReadSSpec(conn, cpuInfo);
46018a5ab91SZhikui Ren             break;
46118a5ab91SZhikui Ren         }
46218a5ab91SZhikui Ren         default:
46318a5ab91SZhikui Ren             phosphor::logging::log<phosphor::logging::level::INFO>(
46418a5ab91SZhikui Ren                 "in-compatible cpu for cpu asset info");
46518a5ab91SZhikui Ren             break;
46618a5ab91SZhikui Ren     }
46718a5ab91SZhikui Ren }
46818a5ab91SZhikui Ren 
4696d3ad586SZhikui Ren /**
4706d3ad586SZhikui Ren  * Get cpu and pirom address
4716d3ad586SZhikui Ren  */
47218a5ab91SZhikui Ren static void
4736d3ad586SZhikui Ren     getCpuAddress(boost::asio::io_service& io,
4746d3ad586SZhikui Ren                   const std::shared_ptr<sdbusplus::asio::connection>& conn,
4756d3ad586SZhikui Ren                   const std::string& service, const std::string& object,
4766d3ad586SZhikui Ren                   const std::string& interface)
47718a5ab91SZhikui Ren {
4786d3ad586SZhikui Ren     conn->async_method_call(
4796d3ad586SZhikui Ren         [&io, conn](boost::system::error_code ec,
4806d3ad586SZhikui Ren                     const boost::container::flat_map<
4816d3ad586SZhikui Ren                         std::string,
4826d3ad586SZhikui Ren                         std::variant<std::string, uint64_t, uint32_t, uint16_t,
4836d3ad586SZhikui Ren                                      std::vector<std::string>>>& properties) {
4846d3ad586SZhikui Ren             const uint64_t* value = nullptr;
4856d3ad586SZhikui Ren             uint8_t peciAddress = 0;
4866d3ad586SZhikui Ren             uint8_t i2cBus = defaultI2cBus;
4876d3ad586SZhikui Ren             uint8_t i2cDevice;
4886d3ad586SZhikui Ren             bool i2cDeviceFound = false;
4896d3ad586SZhikui Ren             size_t cpu = 0;
4906d3ad586SZhikui Ren 
49118a5ab91SZhikui Ren             if (ec)
49218a5ab91SZhikui Ren             {
4936d3ad586SZhikui Ren                 std::cerr << "DBUS response error " << ec.value() << ": "
4946d3ad586SZhikui Ren                           << ec.message() << "\n";
49518a5ab91SZhikui Ren                 return;
49618a5ab91SZhikui Ren             }
4976d3ad586SZhikui Ren 
4986d3ad586SZhikui Ren             for (const auto& property : properties)
4996d3ad586SZhikui Ren             {
5006d3ad586SZhikui Ren                 std::cerr << "property " << property.first << "\n";
5016d3ad586SZhikui Ren                 if (property.first == "Address")
5026d3ad586SZhikui Ren                 {
5036d3ad586SZhikui Ren                     value = std::get_if<uint64_t>(&property.second);
5046d3ad586SZhikui Ren                     if (value != nullptr)
5056d3ad586SZhikui Ren                     {
5066d3ad586SZhikui Ren                         peciAddress = static_cast<uint8_t>(*value);
50718a5ab91SZhikui Ren                     }
50818a5ab91SZhikui Ren                 }
5096d3ad586SZhikui Ren                 if (property.first == "CpuID")
5106d3ad586SZhikui Ren                 {
5116d3ad586SZhikui Ren                     value = std::get_if<uint64_t>(&property.second);
5126d3ad586SZhikui Ren                     if (value != nullptr)
5136d3ad586SZhikui Ren                     {
5146d3ad586SZhikui Ren                         cpu = static_cast<size_t>(*value);
5156d3ad586SZhikui Ren                     }
5166d3ad586SZhikui Ren                 }
5176d3ad586SZhikui Ren                 if (property.first == "PiromI2cAddress")
5186d3ad586SZhikui Ren                 {
5196d3ad586SZhikui Ren                     value = std::get_if<uint64_t>(&property.second);
5206d3ad586SZhikui Ren                     if (value != nullptr)
5216d3ad586SZhikui Ren                     {
5226d3ad586SZhikui Ren                         i2cDevice = static_cast<uint8_t>(*value);
5236d3ad586SZhikui Ren                         i2cDeviceFound = true;
5246d3ad586SZhikui Ren                     }
5256d3ad586SZhikui Ren                 }
5266d3ad586SZhikui Ren                 if (property.first == "PiromI2cBus")
5276d3ad586SZhikui Ren                 {
5286d3ad586SZhikui Ren                     value = std::get_if<uint64_t>(&property.second);
5296d3ad586SZhikui Ren                     if (value != nullptr)
5306d3ad586SZhikui Ren                     {
5316d3ad586SZhikui Ren                         i2cBus = static_cast<uint8_t>(*value);
5326d3ad586SZhikui Ren                     }
5336d3ad586SZhikui Ren                 }
5346d3ad586SZhikui Ren             }
5356d3ad586SZhikui Ren 
5366d3ad586SZhikui Ren             ///\todo replace this with present + power state
5376d3ad586SZhikui Ren             if (cpu != 0 && peciAddress != 0)
5386d3ad586SZhikui Ren             {
5396d3ad586SZhikui Ren                 if (!i2cDeviceFound)
5406d3ad586SZhikui Ren                 {
5416d3ad586SZhikui Ren                     i2cDevice = defaultI2cSlaveAddr0 + cpu - 1;
5426d3ad586SZhikui Ren                 }
5436d3ad586SZhikui Ren                 cpuInfoMap.insert_or_assign(
5445b285892SJonathan Doman                     cpu, std::make_shared<CPUInfo>(*conn, cpu, peciAddress,
5455b285892SJonathan Doman                                                    i2cBus, i2cDevice));
5466d3ad586SZhikui Ren 
5476d3ad586SZhikui Ren                 getProcessorInfo(io, conn, cpu);
5486d3ad586SZhikui Ren             }
5496d3ad586SZhikui Ren         },
5506d3ad586SZhikui Ren         service, object, "org.freedesktop.DBus.Properties", "GetAll",
5516d3ad586SZhikui Ren         interface);
5526d3ad586SZhikui Ren }
5536d3ad586SZhikui Ren 
5546d3ad586SZhikui Ren /**
5556d3ad586SZhikui Ren  * D-Bus client: to get platform specific configs
5566d3ad586SZhikui Ren  */
5576d3ad586SZhikui Ren static void getCpuConfiguration(
5586d3ad586SZhikui Ren     boost::asio::io_service& io,
5596d3ad586SZhikui Ren     const std::shared_ptr<sdbusplus::asio::connection>& conn,
5606d3ad586SZhikui Ren     sdbusplus::asio::object_server& objServer)
5616d3ad586SZhikui Ren {
5626d3ad586SZhikui Ren     // Get the Cpu configuration
5636d3ad586SZhikui Ren     // In case it's not available, set a match for it
5646d3ad586SZhikui Ren     static std::unique_ptr<sdbusplus::bus::match::match> cpuConfigMatch =
5656d3ad586SZhikui Ren         std::make_unique<sdbusplus::bus::match::match>(
5666d3ad586SZhikui Ren             *conn,
5676d3ad586SZhikui Ren             "type='signal',interface='org.freedesktop.DBus.Properties',member='"
5686d3ad586SZhikui Ren             "PropertiesChanged',arg0='xyz.openbmc_project."
5696d3ad586SZhikui Ren             "Configuration.XeonCPU'",
5706d3ad586SZhikui Ren             [&io, conn, &objServer](sdbusplus::message::message& msg) {
5716d3ad586SZhikui Ren                 std::cerr << "get cpu configuration match\n";
5726d3ad586SZhikui Ren                 static boost::asio::steady_timer filterTimer(io);
5736d3ad586SZhikui Ren                 filterTimer.expires_after(
5746d3ad586SZhikui Ren                     std::chrono::seconds(configCheckInterval));
5756d3ad586SZhikui Ren 
5766d3ad586SZhikui Ren                 filterTimer.async_wait(
5776d3ad586SZhikui Ren                     [&io, conn,
5786d3ad586SZhikui Ren                      &objServer](const boost::system::error_code& ec) {
5796d3ad586SZhikui Ren                         if (ec == boost::asio::error::operation_aborted)
5806d3ad586SZhikui Ren                         {
5816d3ad586SZhikui Ren                             return; // we're being canceled
5826d3ad586SZhikui Ren                         }
5836d3ad586SZhikui Ren                         else if (ec)
5846d3ad586SZhikui Ren                         {
5856d3ad586SZhikui Ren                             std::cerr << "Error: " << ec.message() << "\n";
5866d3ad586SZhikui Ren                             return;
5876d3ad586SZhikui Ren                         }
5886d3ad586SZhikui Ren                         getCpuConfiguration(io, conn, objServer);
5896d3ad586SZhikui Ren                     });
5906d3ad586SZhikui Ren             });
5916d3ad586SZhikui Ren 
5926d3ad586SZhikui Ren     conn->async_method_call(
5936d3ad586SZhikui Ren         [&io, conn](
5946d3ad586SZhikui Ren             boost::system::error_code ec,
5956d3ad586SZhikui Ren             const std::vector<std::pair<
5966d3ad586SZhikui Ren                 std::string,
5976d3ad586SZhikui Ren                 std::vector<std::pair<std::string, std::vector<std::string>>>>>&
5986d3ad586SZhikui Ren                 subtree) {
5996d3ad586SZhikui Ren             if constexpr (debug)
6006d3ad586SZhikui Ren                 std::cerr << "async_method_call callback\n";
6016d3ad586SZhikui Ren 
6026d3ad586SZhikui Ren             if (ec)
6036d3ad586SZhikui Ren             {
6046d3ad586SZhikui Ren                 std::cerr << "error with async_method_call\n";
6056d3ad586SZhikui Ren                 return;
6066d3ad586SZhikui Ren             }
6076d3ad586SZhikui Ren             if (subtree.empty())
6086d3ad586SZhikui Ren             {
6096d3ad586SZhikui Ren                 // No config data yet, so wait for the match
6106d3ad586SZhikui Ren                 return;
6116d3ad586SZhikui Ren             }
6126d3ad586SZhikui Ren 
6136d3ad586SZhikui Ren             for (const auto& object : subtree)
6146d3ad586SZhikui Ren             {
6156d3ad586SZhikui Ren                 for (const auto& service : object.second)
6166d3ad586SZhikui Ren                 {
6176d3ad586SZhikui Ren                     getCpuAddress(io, conn, service.first, object.first,
6186d3ad586SZhikui Ren                                   "xyz.openbmc_project.Configuration.XeonCPU");
6196d3ad586SZhikui Ren                 }
6206d3ad586SZhikui Ren             }
6216d3ad586SZhikui Ren             if constexpr (debug)
6226d3ad586SZhikui Ren                 std::cerr << "getCpuConfiguration callback complete\n";
6236d3ad586SZhikui Ren 
6246d3ad586SZhikui Ren             return;
6256d3ad586SZhikui Ren         },
6266d3ad586SZhikui Ren         "xyz.openbmc_project.ObjectMapper",
6276d3ad586SZhikui Ren         "/xyz/openbmc_project/object_mapper",
6286d3ad586SZhikui Ren         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
6296d3ad586SZhikui Ren         "/xyz/openbmc_project/", 0,
6306d3ad586SZhikui Ren         std::array<const char*, 1>{
6316d3ad586SZhikui Ren             "xyz.openbmc_project.Configuration.XeonCPU"});
6326d3ad586SZhikui Ren }
63318a5ab91SZhikui Ren 
63418a5ab91SZhikui Ren } // namespace cpu_info
63518a5ab91SZhikui Ren 
63618a5ab91SZhikui Ren int main(int argc, char* argv[])
63718a5ab91SZhikui Ren {
63818a5ab91SZhikui Ren     // setup connection to dbus
63916a2ced3SJonathan Doman     boost::asio::io_service& io = cpu_info::dbus::getIOContext();
64018a5ab91SZhikui Ren     std::shared_ptr<sdbusplus::asio::connection> conn =
64116a2ced3SJonathan Doman         cpu_info::dbus::getConnection();
64218a5ab91SZhikui Ren 
64318a5ab91SZhikui Ren     // CPUInfo Object
6440a385373SJonathan Doman     conn->request_name(cpu_info::cpuInfoObject);
64518a5ab91SZhikui Ren     sdbusplus::asio::object_server server =
64618a5ab91SZhikui Ren         sdbusplus::asio::object_server(conn);
64718a5ab91SZhikui Ren     sdbusplus::bus::bus& bus = static_cast<sdbusplus::bus::bus&>(*conn);
64818a5ab91SZhikui Ren     sdbusplus::server::manager::manager objManager(
64918a5ab91SZhikui Ren         bus, "/xyz/openbmc_project/inventory");
65018a5ab91SZhikui Ren 
651703a1856SJonathan Doman     cpu_info::hostStateSetup(conn);
652703a1856SJonathan Doman 
653*49ea830eSJonathan Doman     cpu_info::sst::init();
65494c94bfbSJonathan Doman 
6556d3ad586SZhikui Ren     // shared_ptr conn is global for the service
6566d3ad586SZhikui Ren     // const reference of conn is passed to async calls
6570a385373SJonathan Doman     cpu_info::getCpuConfiguration(io, conn, server);
65818a5ab91SZhikui Ren 
65918a5ab91SZhikui Ren     io.run();
66018a5ab91SZhikui Ren 
66118a5ab91SZhikui Ren     return 0;
66218a5ab91SZhikui Ren }
663