blob: 24bb858ba3beda71444fdfe552986a25e23764a7 [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 Doman94c94bf2020-10-05 23:25:45 -070018#include "speed_select.hpp"
Zhikui Ren18a5ab92020-09-01 21:35:20 -070019
20#include <errno.h>
21#include <fcntl.h>
22#include <stdio.h>
23#include <sys/ioctl.h>
24
25#include <boost/asio/io_service.hpp>
26#include <boost/asio/steady_timer.hpp>
27
28#include <optional>
29#include <sstream>
30#include <string>
31
32extern "C"
33{
34#include <i2c/smbus.h>
35#include <linux/i2c-dev.h>
36}
37
38#include <peci.h>
39
40#include <phosphor-logging/log.hpp>
41#include <sdbusplus/asio/object_server.hpp>
42
43namespace phosphor
44{
45namespace cpu_info
46{
47
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
53struct ProcessorInfo
54{
55 uint64_t ppin;
56 std::string sspec;
57};
58
59using CPUMap =
60 boost::container::flat_map<size_t,
61 std::pair<int, std::shared_ptr<CPUInfo>>>;
62
63static CPUMap cpuMap = {};
64
65static std::unique_ptr<sdbusplus::bus::match_t> cpuUpdatedMatch = nullptr;
66
67static std::optional<std::string> readSSpec(uint8_t bus, uint8_t slaveAddr,
68 uint8_t regAddr, size_t count)
69{
70 unsigned long funcs = 0;
71 std::string devPath = "/dev/i2c-" + std::to_string(bus);
72
73 int fd = ::open(devPath.c_str(), O_RDWR);
74 if (fd < 0)
75 {
76 phosphor::logging::log<phosphor::logging::level::ERR>(
77 "Error in open!",
78 phosphor::logging::entry("PATH=%s", devPath.c_str()),
79 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
80 return std::nullopt;
81 }
82
83 if (::ioctl(fd, I2C_FUNCS, &funcs) < 0)
84 {
85 phosphor::logging::log<phosphor::logging::level::ERR>(
86 "Error in I2C_FUNCS!",
87 phosphor::logging::entry("PATH=%s", devPath.c_str()),
88 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
89 ::close(fd);
90 return std::nullopt;
91 }
92
93 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
94 {
95 phosphor::logging::log<phosphor::logging::level::ERR>(
96 "i2c bus does not support read!",
97 phosphor::logging::entry("PATH=%s", devPath.c_str()),
98 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
99 ::close(fd);
100 return std::nullopt;
101 }
102
103 if (::ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
104 {
105 phosphor::logging::log<phosphor::logging::level::ERR>(
106 "Error in I2C_SLAVE_FORCE!",
107 phosphor::logging::entry("PATH=%s", devPath.c_str()),
108 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
109 ::close(fd);
110 return std::nullopt;
111 }
112
113 int value = 0;
114 std::string sspec;
115 sspec.reserve(count);
116
117 for (size_t i = 0; i < count; i++)
118 {
119 value = ::i2c_smbus_read_byte_data(fd, regAddr++);
120 if (value < 0)
121 {
122 phosphor::logging::log<phosphor::logging::level::ERR>(
123 "Error in i2c read!",
124 phosphor::logging::entry("PATH=%s", devPath.c_str()),
125 phosphor::logging::entry("SLAVEADDR=0x%x", slaveAddr));
126 ::close(fd);
127 return std::nullopt;
128 }
129 if (!std::isprint(static_cast<unsigned char>(value)))
130 {
131 phosphor::logging::log<phosphor::logging::level::ERR>(
132 "Non printable value in sspec, ignored.");
133 continue;
134 }
135 sspec.push_back(static_cast<unsigned char>(value));
136 }
137 ::close(fd);
138 return sspec;
139}
140
141// PECI Client Address Map
142static void getPECIAddrMap(CPUMap& cpuMap)
143{
144 int idx = 0;
145 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
146 {
147 if (peci_Ping(i) == PECI_CC_SUCCESS)
148 {
149 cpuMap.emplace(std::make_pair(i, std::make_pair(idx, nullptr)));
150 idx++;
151 }
152 }
153}
154
155static std::shared_ptr<CPUInfo>
156 createCPUInfo(std::shared_ptr<sdbusplus::asio::connection>& conn,
157 const int& cpu)
158{
159 std::string path = cpuPath + std::to_string(cpu);
160 std::shared_ptr<CPUInfo> cpuInfo = std::make_shared<CPUInfo>(
161 static_cast<sdbusplus::bus::bus&>(*conn), path);
162 return cpuInfo;
163}
164
165static void setAssetProperty(
166 std::shared_ptr<sdbusplus::asio::connection>& conn, const int& cpu,
167 const std::vector<std::pair<std::string, std::string>>& propValues)
168{
169
170 const std::string objectPath = cpuPath + std::to_string(cpu);
171 for (const auto& prop : propValues)
172 {
173 conn->async_method_call(
174 [](const boost::system::error_code ec) {
175 // Use "Set" method to set the property value.
176 if (ec)
177 {
178 phosphor::logging::log<phosphor::logging::level::ERR>(
179 "Cannot get CPU property!");
180 return;
181 }
182 },
183 cpuProcessName, objectPath.c_str(),
184 "org.freedesktop.DBus.Properties", "Set", cpuInterfaceName,
185 prop.first.c_str(), std::variant<std::string>{prop.second});
186 }
187}
188
189static void createCpuUpdatedMatch(
190 std::shared_ptr<sdbusplus::asio::connection>& conn, const int& cpu,
191 const std::vector<std::pair<std::string, std::string>>& propValues)
192{
193 if (cpuUpdatedMatch)
194 {
195 return;
196 }
197
198 const std::string objectPath = cpuPath + std::to_string(cpu);
199
200 cpuUpdatedMatch = std::make_unique<sdbusplus::bus::match::match>(
201 static_cast<sdbusplus::bus::bus&>(*conn),
202 sdbusplus::bus::match::rules::interfacesAdded() +
203 sdbusplus::bus::match::rules::argNpath(0, objectPath.c_str()),
204 [&conn, cpu, propValues](sdbusplus::message::message& msg) {
Jonathan Domana43eec82020-10-06 09:23:09 -0700205 sdbusplus::message::object_path objectName;
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700206 boost::container::flat_map<
207 std::string,
208 boost::container::flat_map<std::string,
209 std::variant<std::string, uint64_t>>>
210 msgData;
211
212 msg.read(objectName, msgData);
213
214 // Check for xyz.openbmc_project.Inventory.Item.Cpu
215 // interface match
216 auto intfFound = msgData.find(cpuInterfaceName);
217 if (msgData.end() != intfFound)
218 {
219 setAssetProperty(conn, cpu, propValues);
220 }
221 });
222}
223
224// constants for reading QDF string from PIROM
225// Currently, they are the same for platforms with icx
226// \todo: move into configuration file to be more robust
227static constexpr uint8_t i2cBus = 13;
228static constexpr uint8_t slaveAddr0 = 0x50;
229static constexpr uint8_t regAddr = 0xf;
230static constexpr uint8_t sspecSize = 4;
231
232static void getProcessorInfo(std::shared_ptr<sdbusplus::asio::connection>& conn,
233 sdbusplus::asio::object_server& objServer,
234 CPUMap& cpuMap)
235{
236
237 for (auto& cpu : cpuMap)
238 {
239 uint8_t cc = 0;
240 CPUModel model{};
241 uint8_t stepping = 0;
242
243 /// \todo in a follwup patch
244 // CPUInfo will be updated as the centrol place for CPU information
245 // std::shared_ptr<CPUInfo> cpuInfo =
246 // createCPUInfo(conn, cpu.second.first);
247 // cpu.second.second = cpuInfo;
248
249 if (peci_GetCPUID(cpu.first, &model, &stepping, &cc) != PECI_CC_SUCCESS)
250 {
251 phosphor::logging::log<phosphor::logging::level::ERR>(
252 "Cannot get CPUID!",
253 phosphor::logging::entry("PECIADDR=0x%x", cpu.first));
254 continue;
255 }
256
257 switch (model)
258 {
259 case icx:
260 {
261 // get processor ID
262 static constexpr uint8_t u8Size = 4; // default to a DWORD
263 static constexpr uint8_t u8PPINPkgIndex = 19;
264 static constexpr uint16_t u16PPINPkgParamHigh = 2;
265 static constexpr uint16_t u16PPINPkgParamLow = 1;
266 uint64_t cpuPPIN = 0;
267 uint32_t u32PkgValue = 0;
268
269 int ret = peci_RdPkgConfig(cpu.first, u8PPINPkgIndex,
270 u16PPINPkgParamLow, u8Size,
271 (uint8_t*)&u32PkgValue, &cc);
272 if (0 != ret)
273 {
274 phosphor::logging::log<phosphor::logging::level::ERR>(
275 "peci read package config failed at address",
276 phosphor::logging::entry("PECIADDR=0x%x", cpu.first),
277 phosphor::logging::entry("CC=0x%x", cc));
278 u32PkgValue = 0;
279 }
280
281 cpuPPIN = u32PkgValue;
282 ret = peci_RdPkgConfig(cpu.first, u8PPINPkgIndex,
283 u16PPINPkgParamHigh, u8Size,
284 (uint8_t*)&u32PkgValue, &cc);
285 if (0 != ret)
286 {
287 phosphor::logging::log<phosphor::logging::level::ERR>(
288 "peci read package config failed at address",
289 phosphor::logging::entry("PECIADDR=0x%x", cpu.first),
290 phosphor::logging::entry("CC=0x%x", cc));
291 cpuPPIN = 0;
292 u32PkgValue = 0;
293 }
294
295 cpuPPIN |= static_cast<uint64_t>(u32PkgValue) << 32;
296
297 std::vector<std::pair<std::string, std::string>> values;
298
299 // set SerialNumber if cpuPPIN is valid
300 if (0 != cpuPPIN)
301 {
302 std::stringstream stream;
303 stream << std::hex << cpuPPIN;
304 std::string serialNumber(stream.str());
305 // cpuInfo->serialNumber(serialNumber);
306 values.emplace_back(
307 std::make_pair("SerialNumber", serialNumber));
308 }
309
310 // assuming the slaveAddress will be incrementing like peci
311 // client address
312 std::optional<std::string> sspec = readSSpec(
313 i2cBus, static_cast<uint8_t>(slaveAddr0 + cpu.second.first),
314 regAddr, sspecSize);
315 // cpuInfo->model(sspec.value_or(""));
316 values.emplace_back(
317 std::make_pair("Model", sspec.value_or("")));
318
319 /// \todo in followup patch
320 // CPUInfo is created by this service
321 // update the below logic, which is needed because smbios
322 // service creates the cpu object
323 createCpuUpdatedMatch(conn, cpu.second.first, values);
324 setAssetProperty(conn, cpu.second.first, values);
325 break;
326 }
327 default:
328 phosphor::logging::log<phosphor::logging::level::INFO>(
329 "in-compatible cpu for cpu asset info");
330 break;
331 }
332 }
333}
334
335static bool isPECIAvailable(void)
336{
337 for (size_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; i++)
338 {
339 if (peci_Ping(i) == PECI_CC_SUCCESS)
340 {
341 return true;
342 }
343 }
344 return false;
345}
346
347static void
348 peciAvailableCheck(boost::asio::steady_timer& peciWaitTimer,
349 boost::asio::io_service& io,
350 std::shared_ptr<sdbusplus::asio::connection>& conn,
351 sdbusplus::asio::object_server& objServer)
352{
353 bool peciAvailable = isPECIAvailable();
354 if (peciAvailable)
355 {
356 // get the PECI client address list
357 getPECIAddrMap(cpuMap);
358 getProcessorInfo(conn, objServer, cpuMap);
359 }
360 if (!peciAvailable || !cpuMap.size())
361 {
362 peciWaitTimer.expires_after(
363 std::chrono::seconds(6 * peciCheckInterval));
364 peciWaitTimer.async_wait([&peciWaitTimer, &io, &conn, &objServer](
365 const boost::system::error_code& ec) {
366 if (ec)
367 {
368 // operation_aborted is expected if timer is canceled
369 // before completion.
370 if (ec != boost::asio::error::operation_aborted)
371 {
372 phosphor::logging::log<phosphor::logging::level::ERR>(
373 "PECI Available Check async_wait failed",
374 phosphor::logging::entry("EC=0x%x", ec.value()));
375 }
376 return;
377 }
378 peciAvailableCheck(peciWaitTimer, io, conn, objServer);
379 });
380 }
381}
382
383} // namespace cpu_info
384} // namespace phosphor
385
386int main(int argc, char* argv[])
387{
388 // setup connection to dbus
389 boost::asio::io_service io;
390 std::shared_ptr<sdbusplus::asio::connection> conn =
391 std::make_shared<sdbusplus::asio::connection>(io);
392
393 // CPUInfo Object
394 conn->request_name(phosphor::cpu_info::cpuInfoObject);
395 sdbusplus::asio::object_server server =
396 sdbusplus::asio::object_server(conn);
397 sdbusplus::bus::bus& bus = static_cast<sdbusplus::bus::bus&>(*conn);
398 sdbusplus::server::manager::manager objManager(
399 bus, "/xyz/openbmc_project/inventory");
400
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700401 cpu_info::sst::init(io, conn);
402
Zhikui Ren18a5ab92020-09-01 21:35:20 -0700403 // Start the PECI check loop
404 boost::asio::steady_timer peciWaitTimer(
405 io, std::chrono::seconds(phosphor::cpu_info::peciCheckInterval));
406 peciWaitTimer.async_wait([&peciWaitTimer, &io, &conn,
407 &server](const boost::system::error_code& ec) {
408 if (ec)
409 {
410 // operation_aborted is expected if timer is canceled
411 // before completion.
412 if (ec != boost::asio::error::operation_aborted)
413 {
414 phosphor::logging::log<phosphor::logging::level::ERR>(
415 "PECI Available Check async_wait failed ",
416 phosphor::logging::entry("EC=0x%x", ec.value()));
417 }
418 return;
419 }
420 phosphor::cpu_info::peciAvailableCheck(peciWaitTimer, io, conn, server);
421 });
422
423 io.run();
424
425 return 0;
426}