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: 407*631388e6SJonathan Doman case icxd: 408*631388e6SJonathan 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 63918a5ab91SZhikui Ren boost::asio::io_service io; 64018a5ab91SZhikui Ren std::shared_ptr<sdbusplus::asio::connection> conn = 64118a5ab91SZhikui Ren std::make_shared<sdbusplus::asio::connection>(io); 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 65394c94bfbSJonathan Doman cpu_info::sst::init(io, conn); 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