blob: 5a3a5ff5a9b1408cfd9604ae4e07e949091d04ed [file] [log] [blame]
Zhikui Ren18a5ab92020-09-01 21:35:20 -07001/*
2// Copyright (c) 2020 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "cpuinfo.hpp"
Jonathan Doman703a1852020-11-11 13:04:02 -080018#include "cpuinfo_utils.hpp"
Jonathan Doman94c94bf2020-10-05 23:25:45 -070019#include "speed_select.hpp"
Zhikui Ren18a5ab92020-09-01 21:35:20 -070020
21#include <errno.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <sys/ioctl.h>
25
26#include <boost/asio/io_service.hpp>
27#include <boost/asio/steady_timer.hpp>
28
Zhikui Ren6d3ad582020-09-11 21:25:59 -070029#include <iostream>
Zhikui Ren18a5ab92020-09-01 21:35:20 -070030#include <optional>
31#include <sstream>
32#include <string>
33
34extern "C"
35{
36#include <i2c/smbus.h>
37#include <linux/i2c-dev.h>
38}
39
40#include <peci.h>
41
42#include <phosphor-logging/log.hpp>
43#include <sdbusplus/asio/object_server.hpp>
44
Zhikui Ren18a5ab92020-09-01 21:35:20 -070045namespace cpu_info
46{
Zhikui Ren6d3ad582020-09-11 21:25:59 -070047static constexpr bool debug = false;
Zhikui Ren18a5ab92020-09-01 21:35:20 -070048static constexpr const char* cpuInterfaceName =
49 "xyz.openbmc_project.Inventory.Decorator.Asset";
50static constexpr const char* cpuProcessName =
51 "xyz.openbmc_project.Smbios.MDR_V2";
52
Zhikui Ren6d3ad582020-09-11 21:25:59 -070053// constants for reading SSPEC or QDF string from PIROM
54// Currently, they are the same for platforms with icx
55static constexpr uint8_t defaultI2cBus = 13;
56static constexpr uint8_t defaultI2cSlaveAddr0 = 0x50;
57static constexpr uint8_t sspecRegAddr = 0xd;
58static constexpr uint8_t sspecSize = 6;
Zhikui Ren18a5ab92020-09-01 21:35:20 -070059
Zhikui Ren6d3ad582020-09-11 21:25:59 -070060using CPUInfoMap = boost::container::flat_map<size_t, std::shared_ptr<CPUInfo>>;
Zhikui Ren18a5ab92020-09-01 21:35:20 -070061
Zhikui Ren6d3ad582020-09-11 21:25:59 -070062static CPUInfoMap cpuInfoMap = {};
Zhikui Ren18a5ab92020-09-01 21:35:20 -070063
Zhikui Ren6d3ad582020-09-11 21:25:59 -070064static boost::container::flat_map<size_t,
65 std::unique_ptr<sdbusplus::bus::match_t>>
66 cpuUpdatedMatch = {};
Zhikui Ren18a5ab92020-09-01 21:35:20 -070067
68static std::optional<std::string> readSSpec(uint8_t bus, uint8_t slaveAddr,
69 uint8_t regAddr, size_t count)
70{
71 unsigned long funcs = 0;
72 std::string devPath = "/dev/i2c-" + std::to_string(bus);
73
74 int fd = ::open(devPath.c_str(), O_RDWR);
75 if (fd < 0)
76 {
77 phosphor::logging::log<phosphor::logging::level::ERR>(
78 "Error in open!",
79 phosphor::logging::entry("PATH=%s", devPath.c_str()),
80 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
81 return std::nullopt;
82 }
83
84 if (::ioctl(fd, I2C_FUNCS, &funcs) < 0)
85 {
86 phosphor::logging::log<phosphor::logging::level::ERR>(
87 "Error in I2C_FUNCS!",
88 phosphor::logging::entry("PATH=%s", devPath.c_str()),
89 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
90 ::close(fd);
91 return std::nullopt;
92 }
93
94 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
95 {
96 phosphor::logging::log<phosphor::logging::level::ERR>(
97 "i2c bus does not support read!",
98 phosphor::logging::entry("PATH=%s", devPath.c_str()),
99 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
100 ::close(fd);
101 return std::nullopt;
102 }
103
104 if (::ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
105 {
106 phosphor::logging::log<phosphor::logging::level::ERR>(
107 "Error in I2C_SLAVE_FORCE!",
108 phosphor::logging::entry("PATH=%s", devPath.c_str()),
109 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
110 ::close(fd);
111 return std::nullopt;
112 }
113
114 int value = 0;
115 std::string sspec;
116 sspec.reserve(count);
117
118 for (size_t i = 0; i < count; i++)
119 {
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700120 value = ::i2c_smbus_read_byte_data(fd, regAddr + i);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700121 if (value < 0)
122 {
123 phosphor::logging::log<phosphor::logging::level::ERR>(
124 "Error in i2c read!",
125 phosphor::logging::entry("PATH=%s", devPath.c_str()),
126 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
127 ::close(fd);
128 return std::nullopt;
129 }
130 if (!std::isprint(static_cast<unsigned char>(value)))
131 {
132 phosphor::logging::log<phosphor::logging::level::ERR>(
133 "Non printable value in sspec, ignored.");
134 continue;
135 }
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700136 // sspec always starts with S,
137 // if not assume it is QDF string which starts at offset 2
138 if (i == 0 && static_cast<unsigned char>(value) != 'S')
139 {
140 i = 1;
141 continue;
142 }
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700143 sspec.push_back(static_cast<unsigned char>(value));
144 }
145 ::close(fd);
146 return sspec;
147}
148
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700149static void setAssetProperty(
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700150 const std::shared_ptr<sdbusplus::asio::connection>& conn, const int& cpu,
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700151 const std::vector<std::pair<std::string, std::string>>& propValues)
152{
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700153 // cpuId from configuration is one based as
154 // dbus object path used by smbios is 0 based
155 const std::string objectPath = cpuPath + std::to_string(cpu - 1);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700156 for (const auto& prop : propValues)
157 {
158 conn->async_method_call(
159 [](const boost::system::error_code ec) {
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700160 if (ec)
161 {
162 phosphor::logging::log<phosphor::logging::level::ERR>(
163 "Cannot get CPU property!");
164 return;
165 }
166 },
167 cpuProcessName, objectPath.c_str(),
168 "org.freedesktop.DBus.Properties", "Set", cpuInterfaceName,
169 prop.first.c_str(), std::variant<std::string>{prop.second});
170 }
171}
172
173static void createCpuUpdatedMatch(
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700174 const std::shared_ptr<sdbusplus::asio::connection>& conn, const int cpu,
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700175 const std::vector<std::pair<std::string, std::string>>& propValues)
176{
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700177 if (cpuUpdatedMatch[cpu])
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700178 {
179 return;
180 }
181
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700182 const std::string objectPath = cpuPath + std::to_string(cpu - 1);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700183
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700184 cpuUpdatedMatch.insert_or_assign(
185 cpu,
186 std::make_unique<sdbusplus::bus::match::match>(
187 static_cast<sdbusplus::bus::bus&>(*conn),
188 sdbusplus::bus::match::rules::interfacesAdded() +
189 sdbusplus::bus::match::rules::argNpath(0, objectPath.c_str()),
190 [conn, cpu, propValues](sdbusplus::message::message& msg) {
191 sdbusplus::message::object_path objectName;
192 boost::container::flat_map<
193 std::string,
194 boost::container::flat_map<
195 std::string, std::variant<std::string, uint64_t>>>
196 msgData;
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700197
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700198 msg.read(objectName, msgData);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700199
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700200 // Check for xyz.openbmc_project.Inventory.Item.Cpu
201 // interface match
202 const auto& intfFound = msgData.find(cpuInterfaceName);
203 if (msgData.end() != intfFound)
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700204 {
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700205 setAssetProperty(conn, cpu, propValues);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700206 }
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700207 }));
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700208}
209
210static void
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700211 getProcessorInfo(boost::asio::io_service& io,
212 const std::shared_ptr<sdbusplus::asio::connection>& conn,
213 const size_t& cpu)
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700214{
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700215 if (cpuInfoMap.find(cpu) == cpuInfoMap.end() || cpuInfoMap[cpu] == nullptr)
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700216 {
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700217 std::cerr << "No information found for cpu " << cpu << "\n";
218 return;
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700219 }
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700220
221 if (cpuInfoMap[cpu]->id != cpu)
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700222 {
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700223 std::cerr << "Incorrect CPU id " << (unsigned)cpuInfoMap[cpu]->id
224 << " expect " << cpu << "\n";
225 return;
226 }
227
228 uint8_t cpuAddr = cpuInfoMap[cpu]->peciAddr;
229 uint8_t i2cBus = cpuInfoMap[cpu]->i2cBus;
230 uint8_t i2cDevice = cpuInfoMap[cpu]->i2cDevice;
231
232 uint8_t cc = 0;
233 CPUModel model{};
234 uint8_t stepping = 0;
235
Jonathan Doman0a385372021-03-08 17:04:13 -0800236 // Wait for POST to complete to ensure that BIOS has time to enable the
237 // PPIN. Before BIOS enables it, we would get a 0x90 CC on PECI.
238 if (hostState != HostState::postComplete ||
239 peci_GetCPUID(cpuAddr, &model, &stepping, &cc) != PECI_CC_SUCCESS)
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700240 {
241 // Start the PECI check loop
242 auto waitTimer = std::make_shared<boost::asio::steady_timer>(io);
243 waitTimer->expires_after(
Jonathan Doman0a385372021-03-08 17:04:13 -0800244 std::chrono::seconds(cpu_info::peciCheckInterval));
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700245
246 waitTimer->async_wait(
247 [waitTimer, &io, conn, cpu](const boost::system::error_code& ec) {
248 if (ec)
249 {
250 // operation_aborted is expected if timer is canceled
251 // before completion.
252 if (ec != boost::asio::error::operation_aborted)
253 {
254 phosphor::logging::log<phosphor::logging::level::ERR>(
255 "info update timer async_wait failed ",
256 phosphor::logging::entry("EC=0x%x", ec.value()));
257 }
258 return;
259 }
260 getProcessorInfo(io, conn, cpu);
261 });
262 return;
263 }
264
265 switch (model)
266 {
267 case icx:
268 {
269 // PPIN can be read through PCS 19
270 static constexpr uint8_t u8Size = 4; // default to a DWORD
271 static constexpr uint8_t u8PPINPkgIndex = 19;
272 static constexpr uint16_t u16PPINPkgParamHigh = 2;
273 static constexpr uint16_t u16PPINPkgParamLow = 1;
274 uint64_t cpuPPIN = 0;
275 uint32_t u32PkgValue = 0;
276
277 int ret =
278 peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamLow,
279 u8Size, (uint8_t*)&u32PkgValue, &cc);
280 if (0 != ret)
281 {
282 phosphor::logging::log<phosphor::logging::level::ERR>(
283 "peci read package config failed at address",
284 phosphor::logging::entry("PECIADDR=0x%x",
285 (unsigned)cpuAddr),
286 phosphor::logging::entry("CC=0x%x", cc));
287 u32PkgValue = 0;
288 }
289
290 cpuPPIN = u32PkgValue;
291 ret = peci_RdPkgConfig(cpuAddr, u8PPINPkgIndex, u16PPINPkgParamHigh,
292 u8Size, (uint8_t*)&u32PkgValue, &cc);
293 if (0 != ret)
294 {
295 phosphor::logging::log<phosphor::logging::level::ERR>(
296 "peci read package config failed at address",
297 phosphor::logging::entry("PECIADDR=0x%x",
298 (unsigned)cpuAddr),
299 phosphor::logging::entry("CC=0x%x", cc));
300 cpuPPIN = 0;
301 u32PkgValue = 0;
302 }
303
304 cpuPPIN |= static_cast<uint64_t>(u32PkgValue) << 32;
305
306 std::vector<std::pair<std::string, std::string>> values;
307
308 // set SerialNumber if cpuPPIN is valid
309 if (0 != cpuPPIN)
310 {
311 std::stringstream stream;
312 stream << std::hex << cpuPPIN;
313 std::string serialNumber(stream.str());
314 // cpuInfo->serialNumber(serialNumber);
315 values.emplace_back(
316 std::make_pair("SerialNumber", serialNumber));
317 }
318
319 std::optional<std::string> sspec =
320 readSSpec(i2cBus, i2cDevice, sspecRegAddr, sspecSize);
321
322 // cpuInfo->model(sspec.value_or(""));
323 values.emplace_back(std::make_pair("Model", sspec.value_or("")));
324
325 /// \todo in followup patch
326 // CPUInfo is created by this service
327 // update the below logic, which is needed because smbios
328 // service creates the cpu object
329 createCpuUpdatedMatch(conn, cpu, values);
330 setAssetProperty(conn, cpu, values);
331 break;
332 }
333 default:
334 phosphor::logging::log<phosphor::logging::level::INFO>(
335 "in-compatible cpu for cpu asset info");
336 break;
337 }
338}
339
340/**
341 * Get cpu and pirom address
342 */
343static void
344 getCpuAddress(boost::asio::io_service& io,
345 const std::shared_ptr<sdbusplus::asio::connection>& conn,
346 const std::string& service, const std::string& object,
347 const std::string& interface)
348{
349 conn->async_method_call(
350 [&io, conn](boost::system::error_code ec,
351 const boost::container::flat_map<
352 std::string,
353 std::variant<std::string, uint64_t, uint32_t, uint16_t,
354 std::vector<std::string>>>& properties) {
355 const uint64_t* value = nullptr;
356 uint8_t peciAddress = 0;
357 uint8_t i2cBus = defaultI2cBus;
358 uint8_t i2cDevice;
359 bool i2cDeviceFound = false;
360 size_t cpu = 0;
361
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700362 if (ec)
363 {
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700364 std::cerr << "DBUS response error " << ec.value() << ": "
365 << ec.message() << "\n";
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700366 return;
367 }
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700368
369 for (const auto& property : properties)
370 {
371 std::cerr << "property " << property.first << "\n";
372 if (property.first == "Address")
373 {
374 value = std::get_if<uint64_t>(&property.second);
375 if (value != nullptr)
376 {
377 peciAddress = static_cast<uint8_t>(*value);
378 }
379 }
380 if (property.first == "CpuID")
381 {
382 value = std::get_if<uint64_t>(&property.second);
383 if (value != nullptr)
384 {
385 cpu = static_cast<size_t>(*value);
386 }
387 }
388 if (property.first == "PiromI2cAddress")
389 {
390 value = std::get_if<uint64_t>(&property.second);
391 if (value != nullptr)
392 {
393 i2cDevice = static_cast<uint8_t>(*value);
394 i2cDeviceFound = true;
395 }
396 }
397 if (property.first == "PiromI2cBus")
398 {
399 value = std::get_if<uint64_t>(&property.second);
400 if (value != nullptr)
401 {
402 i2cBus = static_cast<uint8_t>(*value);
403 }
404 }
405 }
406
407 ///\todo replace this with present + power state
408 if (cpu != 0 && peciAddress != 0)
409 {
410 if (!i2cDeviceFound)
411 {
412 i2cDevice = defaultI2cSlaveAddr0 + cpu - 1;
413 }
414 cpuInfoMap.insert_or_assign(
415 cpu, std::make_shared<CPUInfo>(cpu, peciAddress, i2cBus,
416 i2cDevice));
417
418 getProcessorInfo(io, conn, cpu);
419 }
420 },
421 service, object, "org.freedesktop.DBus.Properties", "GetAll",
422 interface);
423}
424
425/**
426 * D-Bus client: to get platform specific configs
427 */
428static void getCpuConfiguration(
429 boost::asio::io_service& io,
430 const std::shared_ptr<sdbusplus::asio::connection>& conn,
431 sdbusplus::asio::object_server& objServer)
432{
433 // Get the Cpu configuration
434 // In case it's not available, set a match for it
435 static std::unique_ptr<sdbusplus::bus::match::match> cpuConfigMatch =
436 std::make_unique<sdbusplus::bus::match::match>(
437 *conn,
438 "type='signal',interface='org.freedesktop.DBus.Properties',member='"
439 "PropertiesChanged',arg0='xyz.openbmc_project."
440 "Configuration.XeonCPU'",
441 [&io, conn, &objServer](sdbusplus::message::message& msg) {
442 std::cerr << "get cpu configuration match\n";
443 static boost::asio::steady_timer filterTimer(io);
444 filterTimer.expires_after(
445 std::chrono::seconds(configCheckInterval));
446
447 filterTimer.async_wait(
448 [&io, conn,
449 &objServer](const boost::system::error_code& ec) {
450 if (ec == boost::asio::error::operation_aborted)
451 {
452 return; // we're being canceled
453 }
454 else if (ec)
455 {
456 std::cerr << "Error: " << ec.message() << "\n";
457 return;
458 }
459 getCpuConfiguration(io, conn, objServer);
460 });
461 });
462
463 conn->async_method_call(
464 [&io, conn](
465 boost::system::error_code ec,
466 const std::vector<std::pair<
467 std::string,
468 std::vector<std::pair<std::string, std::vector<std::string>>>>>&
469 subtree) {
470 if constexpr (debug)
471 std::cerr << "async_method_call callback\n";
472
473 if (ec)
474 {
475 std::cerr << "error with async_method_call\n";
476 return;
477 }
478 if (subtree.empty())
479 {
480 // No config data yet, so wait for the match
481 return;
482 }
483
484 for (const auto& object : subtree)
485 {
486 for (const auto& service : object.second)
487 {
488 getCpuAddress(io, conn, service.first, object.first,
489 "xyz.openbmc_project.Configuration.XeonCPU");
490 }
491 }
492 if constexpr (debug)
493 std::cerr << "getCpuConfiguration callback complete\n";
494
495 return;
496 },
497 "xyz.openbmc_project.ObjectMapper",
498 "/xyz/openbmc_project/object_mapper",
499 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
500 "/xyz/openbmc_project/", 0,
501 std::array<const char*, 1>{
502 "xyz.openbmc_project.Configuration.XeonCPU"});
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700503}
504
505} // namespace cpu_info
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700506
507int main(int argc, char* argv[])
508{
509 // setup connection to dbus
510 boost::asio::io_service io;
511 std::shared_ptr<sdbusplus::asio::connection> conn =
512 std::make_shared<sdbusplus::asio::connection>(io);
513
514 // CPUInfo Object
Jonathan Doman0a385372021-03-08 17:04:13 -0800515 conn->request_name(cpu_info::cpuInfoObject);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700516 sdbusplus::asio::object_server server =
517 sdbusplus::asio::object_server(conn);
518 sdbusplus::bus::bus& bus = static_cast<sdbusplus::bus::bus&>(*conn);
519 sdbusplus::server::manager::manager objManager(
520 bus, "/xyz/openbmc_project/inventory");
521
Jonathan Doman703a1852020-11-11 13:04:02 -0800522 cpu_info::hostStateSetup(conn);
523
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700524 cpu_info::sst::init(io, conn);
525
Zhikui Ren6d3ad582020-09-11 21:25:59 -0700526 // shared_ptr conn is global for the service
527 // const reference of conn is passed to async calls
Jonathan Doman0a385372021-03-08 17:04:13 -0800528 cpu_info::getCpuConfiguration(io, conn, server);
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700529
530 io.run();
531
532 return 0;
533}