blob: 57a5e5a96d76bb6f71c3e80f5921bd00428f8de8 [file] [log] [blame]
Steve Foreman4f0d1de2021-09-20 14:06:32 -07001// Copyright 2022 Google LLC
Willy Tua2056e92021-10-10 13:36:16 -07002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Michael Shen8d618532023-10-25 09:14:07 +000014
Vikram Gara4134c742026-01-12 05:29:10 +000015#include "config.h"
16
Patrick Venturef085d912019-03-15 08:50:00 -070017#include "handler.hpp"
18
Patrick Williams444b5ea2023-05-19 13:56:42 -050019#include "bm_config.h"
20
Brandon Kim559cb012024-05-03 09:12:07 +000021#include "bm_instance.hpp"
Nikhil Namjoshi8ec41062022-10-24 21:07:25 +000022#include "bmc_mode_enum.hpp"
Patrick Ventured2037c62019-03-15 10:29:47 -070023#include "errors.hpp"
Patrick Venturec87de552020-05-20 20:25:39 -070024#include "handler_impl.hpp"
Patrick Ventureab650002019-03-16 09:08:47 -070025#include "util.hpp"
Patrick Ventured2037c62019-03-15 10:29:47 -070026
Willy Tu3b1b4272021-03-02 17:58:10 -080027#include <fcntl.h>
Patrick Ventured2037c62019-03-15 10:29:47 -070028#include <ipmid/api.h>
Willy Tu3b1b4272021-03-02 17:58:10 -080029#include <mtd/mtd-abi.h>
30#include <mtd/mtd-user.h>
31#include <sys/ioctl.h>
32#include <unistd.h>
Patrick Ventured2037c62019-03-15 10:29:47 -070033
Patrick Williams444b5ea2023-05-19 13:56:42 -050034#include <nlohmann/json.hpp>
35#include <phosphor-logging/elog-errors.hpp>
36#include <phosphor-logging/log.hpp>
37#include <sdbusplus/bus.hpp>
Michael Shen8d618532023-10-25 09:14:07 +000038#include <stdplus/print.hpp>
Patrick Williams444b5ea2023-05-19 13:56:42 -050039#include <xyz/openbmc_project/Common/error.hpp>
40
Patrick Venturebb90d4f2019-03-15 13:42:06 -070041#include <cinttypes>
Patrick Ventured2037c62019-03-15 10:29:47 -070042#include <cstdio>
43#include <filesystem>
44#include <fstream>
Brandon Kim559cb012024-05-03 09:12:07 +000045#include <iomanip>
Patrick Venture07f85152019-03-15 21:36:56 -070046#include <map>
Vikram Gara4134c742026-01-12 05:29:10 +000047#include <optional>
Patrick Ventured2037c62019-03-15 10:29:47 -070048#include <sstream>
49#include <string>
William A. Kennington III29f35bc2020-11-03 23:30:31 -080050#include <string_view>
Patrick Ventured2037c62019-03-15 10:29:47 -070051#include <tuple>
Steve Foreman4f0d1de2021-09-20 14:06:32 -070052#include <variant>
Nikhil Namjoshi5e70dc82022-09-16 00:36:07 +000053
Patrick Venturef085d912019-03-15 08:50:00 -070054#ifndef NCSI_IF_NAME
55#define NCSI_IF_NAME eth0
56#endif
57
58// To deal with receiving a string without quotes.
59#define QUOTE(name) #name
60#define STR(macro) QUOTE(macro)
61#define NCSI_IF_NAME_STR STR(NCSI_IF_NAME)
62
William A. Kennington III8d3d46a2021-07-13 12:35:35 -070063namespace ipmi
64{
65std::uint8_t getChannelByName(const std::string& chName);
66}
67
Patrick Venturef085d912019-03-15 08:50:00 -070068namespace google
69{
70namespace ipmi
71{
Patrick Venture07f85152019-03-15 21:36:56 -070072using Json = nlohmann::json;
73using namespace phosphor::logging;
74using InternalFailure =
75 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Gaurav Gandhid455bfd2024-01-29 22:32:27 -080076using Value = std::variant<double>;
Patrick Venturef085d912019-03-15 08:50:00 -070077
Hao Zhou15d4d212023-07-11 20:18:04 +000078uint8_t isBmcInBareMetalMode(const std::unique_ptr<FileSystemInterface>& fs)
Nikhil Namjoshi5e70dc82022-09-16 00:36:07 +000079{
80#if BARE_METAL
81 return static_cast<uint8_t>(BmcMode::BM_MODE);
82#else
Brandon Kim3f3ca032023-03-17 18:49:00 +000083 std::error_code ec;
Hao Zhou4baf41c2023-07-06 22:34:04 +000084
Hao Zhou15d4d212023-07-11 20:18:04 +000085 if (fs->exists(bmDriveCleaningDoneAckFlagPath, ec))
Brandon Kim3f3ca032023-03-17 18:49:00 +000086 {
Michael Shen8d618532023-10-25 09:14:07 +000087 stdplus::print(
Hao Zhou4baf41c2023-07-06 22:34:04 +000088 stderr,
Michael Shen8d618532023-10-25 09:14:07 +000089 "{} exists so we acked cleaning done and must be in BM mode\n",
Hao Zhou4baf41c2023-07-06 22:34:04 +000090 bmDriveCleaningDoneAckFlagPath);
Brandon Kim3f3ca032023-03-17 18:49:00 +000091 return static_cast<uint8_t>(BmcMode::BM_MODE);
92 }
93
Hao Zhou15d4d212023-07-11 20:18:04 +000094 if (fs->exists(bmDriveCleaningDoneFlagPath, ec))
Hao Zhou4baf41c2023-07-06 22:34:04 +000095 {
Hao Zhou15d4d212023-07-11 20:18:04 +000096 fs->rename(bmDriveCleaningDoneFlagPath, bmDriveCleaningDoneAckFlagPath,
Hao Zhou4baf41c2023-07-06 22:34:04 +000097 ec);
Michael Shen8d618532023-10-25 09:14:07 +000098 stdplus::print(
Hao Zhou4baf41c2023-07-06 22:34:04 +000099 stderr,
Michael Shen8d618532023-10-25 09:14:07 +0000100 "{} exists so we just finished cleaning and must be in BM mode\n",
Hao Zhou4baf41c2023-07-06 22:34:04 +0000101 bmDriveCleaningDoneFlagPath);
102 return static_cast<uint8_t>(BmcMode::BM_MODE);
103 }
104
Hao Zhou15d4d212023-07-11 20:18:04 +0000105 if (fs->exists(BM_SIGNAL_PATH, ec))
Hao Zhou4baf41c2023-07-06 22:34:04 +0000106 {
Hao Zhou15d4d212023-07-11 20:18:04 +0000107 if (!fs->exists(bmDriveCleaningFlagPath, ec))
Hao Zhou4baf41c2023-07-06 22:34:04 +0000108 {
Hao Zhou15d4d212023-07-11 20:18:04 +0000109 fs->create(bmDriveCleaningFlagPath);
Hao Zhou4baf41c2023-07-06 22:34:04 +0000110 }
111
Michael Shen8d618532023-10-25 09:14:07 +0000112 stdplus::print(
Hao Zhou4baf41c2023-07-06 22:34:04 +0000113 stderr,
Michael Shen8d618532023-10-25 09:14:07 +0000114 "{} exists and no done/ack flag, we must be in BM cleaning mode\n",
Hao Zhou4baf41c2023-07-06 22:34:04 +0000115 BM_SIGNAL_PATH);
116 return static_cast<uint8_t>(BmcMode::BM_CLEANING_MODE);
117 }
118
Michael Shen8d618532023-10-25 09:14:07 +0000119 stdplus::print(
Hao Zhou4baf41c2023-07-06 22:34:04 +0000120 stderr,
121 "Unable to find any BM state files so we must not be in BM mode\n");
Nikhil Namjoshi5e70dc82022-09-16 00:36:07 +0000122 return static_cast<uint8_t>(BmcMode::NON_BM_MODE);
123#endif
124}
125
126uint8_t Handler::getBmcMode()
127{
128 // BM_CLEANING_MODE is not implemented yet
Hao Zhou15d4d212023-07-11 20:18:04 +0000129 return isBmcInBareMetalMode(this->getFs());
Nikhil Namjoshi5e70dc82022-09-16 00:36:07 +0000130}
131
Patrick Williams6dee32b2025-02-01 08:23:56 -0500132std::tuple<std::uint8_t, std::string> Handler::getEthDetails(
133 std::string intf) const
Patrick Venturef085d912019-03-15 08:50:00 -0700134{
William A. Kennington IIIb69209b2021-07-13 13:22:24 -0700135 if (intf.empty())
136 {
137 intf = NCSI_IF_NAME_STR;
138 }
139 return std::make_tuple(::ipmi::getChannelByName(intf), std::move(intf));
Patrick Venturef085d912019-03-15 08:50:00 -0700140}
141
Patrick Ventured2037c62019-03-15 10:29:47 -0700142std::int64_t Handler::getRxPackets(const std::string& name) const
143{
144 std::ostringstream opath;
145 opath << "/sys/class/net/" << name << "/statistics/rx_packets";
146 std::string path = opath.str();
147
148 // Minor sanity & security check (of course, I'm less certain if unicode
149 // comes into play here.
150 //
151 // Basically you can't easily inject ../ or /../ into the path below.
152 if (name.find("/") != std::string::npos)
153 {
Michael Shen8d618532023-10-25 09:14:07 +0000154 stdplus::print(stderr, "Invalid or illegal name: '{}'\n", name);
Michael Shene5a06672022-06-20 05:08:32 +0000155 throw IpmiException(::ipmi::ccInvalidFieldRequest);
Patrick Ventured2037c62019-03-15 10:29:47 -0700156 }
157
158 std::error_code ec;
Hao Zhou15d4d212023-07-11 20:18:04 +0000159 if (!this->getFs()->exists(path, ec))
Patrick Ventured2037c62019-03-15 10:29:47 -0700160 {
Michael Shen8d618532023-10-25 09:14:07 +0000161 stdplus::print(stderr, "Path: '{}' doesn't exist.\n", path);
Michael Shene5a06672022-06-20 05:08:32 +0000162 throw IpmiException(::ipmi::ccInvalidFieldRequest);
Patrick Ventured2037c62019-03-15 10:29:47 -0700163 }
164 // We're uninterested in the state of ec.
165
166 int64_t count = 0;
167 std::ifstream ifs;
168 ifs.exceptions(std::ifstream::failbit);
169 try
170 {
171 ifs.open(path);
172 ifs >> count;
173 }
174 catch (std::ios_base::failure& fail)
175 {
Michael Shene5a06672022-06-20 05:08:32 +0000176 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Ventured2037c62019-03-15 10:29:47 -0700177 }
178
179 return count;
180}
181
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700182VersionTuple Handler::getCpldVersion(unsigned int id) const
183{
184 std::ostringstream opath;
185 opath << "/run/cpld" << id << ".version";
186 // Check for file
187
188 std::error_code ec;
Hao Zhou15d4d212023-07-11 20:18:04 +0000189 if (!this->getFs()->exists(opath.str(), ec))
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700190 {
Michael Shen8d618532023-10-25 09:14:07 +0000191 stdplus::print(stderr, "Path: '{}' doesn't exist.\n", opath.str());
Michael Shene5a06672022-06-20 05:08:32 +0000192 throw IpmiException(::ipmi::ccInvalidFieldRequest);
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700193 }
194 // We're uninterested in the state of ec.
195
196 // If file exists, read.
197 std::ifstream ifs;
198 ifs.exceptions(std::ifstream::failbit);
199 std::string value;
200 try
201 {
202 ifs.open(opath.str());
203 ifs >> value;
204 }
205 catch (std::ios_base::failure& fail)
206 {
Michael Shene5a06672022-06-20 05:08:32 +0000207 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700208 }
209
210 // If value parses as expected, return version.
211 VersionTuple version = std::make_tuple(0, 0, 0, 0);
212
Patrick Williams8c0094e2024-08-16 15:22:37 -0400213 int num_fields =
214 std::sscanf(value.c_str(), "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8,
215 &std::get<0>(version), &std::get<1>(version),
216 &std::get<2>(version), &std::get<3>(version));
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700217 if (num_fields == 0)
218 {
Michael Shen8d618532023-10-25 09:14:07 +0000219 stdplus::print(stderr, "Invalid version.\n");
Michael Shene5a06672022-06-20 05:08:32 +0000220 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Venturebb90d4f2019-03-15 13:42:06 -0700221 }
222
223 return version;
224}
225
Patrick Ventureaa374122019-03-15 15:09:10 -0700226static constexpr auto TIME_DELAY_FILENAME = "/run/psu_timedelay";
227static constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
228static constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
229static constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
230static constexpr auto PSU_HARDRESET_TARGET = "gbmc-psu-hardreset.target";
231
232void Handler::psuResetDelay(std::uint32_t delay) const
233{
Patrick Ventureaa374122019-03-15 15:09:10 -0700234 std::ofstream ofs;
235 ofs.open(TIME_DELAY_FILENAME, std::ofstream::out);
236 if (!ofs.good())
237 {
Michael Shen8d618532023-10-25 09:14:07 +0000238 stdplus::print(stderr, "Unable to open file for output.\n");
Michael Shene5a06672022-06-20 05:08:32 +0000239 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Ventureaa374122019-03-15 15:09:10 -0700240 }
241
242 ofs << "PSU_HARDRESET_DELAY=" << delay << std::endl;
243 if (ofs.fail())
244 {
Michael Shen8d618532023-10-25 09:14:07 +0000245 stdplus::print(stderr, "Write failed\n");
Patrick Ventureaa374122019-03-15 15:09:10 -0700246 ofs.close();
Michael Shene5a06672022-06-20 05:08:32 +0000247 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Ventureaa374122019-03-15 15:09:10 -0700248 }
249
250 // Write succeeded, please continue.
251 ofs.flush();
252 ofs.close();
253
254 auto bus = sdbusplus::bus::new_default();
255 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
256 SYSTEMD_INTERFACE, "StartUnit");
257
258 method.append(PSU_HARDRESET_TARGET);
259 method.append("replace");
260
261 try
262 {
263 bus.call_noreply(method);
264 }
265 catch (const sdbusplus::exception::SdBusError& ex)
266 {
267 log<level::ERR>("Failed to call PSU hard reset");
Michael Shene5a06672022-06-20 05:08:32 +0000268 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Ventureaa374122019-03-15 15:09:10 -0700269 }
270}
271
Shounak Mitraac4a16f2021-02-02 11:11:44 -0800272static constexpr auto RESET_ON_SHUTDOWN_FILENAME = "/run/powercycle_on_s5";
273
274void Handler::psuResetOnShutdown() const
275{
276 std::ofstream ofs;
277 ofs.open(RESET_ON_SHUTDOWN_FILENAME, std::ofstream::out);
278 if (!ofs.good())
279 {
Michael Shen8d618532023-10-25 09:14:07 +0000280 stdplus::print(stderr, "Unable to open file for output.\n");
Michael Shene5a06672022-06-20 05:08:32 +0000281 throw IpmiException(::ipmi::ccUnspecifiedError);
Shounak Mitraac4a16f2021-02-02 11:11:44 -0800282 }
283 ofs.close();
284}
285
Willy Tu3b1b4272021-03-02 17:58:10 -0800286uint32_t Handler::getFlashSize()
287{
288 mtd_info_t info;
289 int fd = open("/dev/mtd0", O_RDONLY);
290 int err = ioctl(fd, MEMGETINFO, &info);
291 close(fd);
292
293 if (err)
294 {
Michael Shene5a06672022-06-20 05:08:32 +0000295 throw IpmiException(::ipmi::ccUnspecifiedError);
Willy Tu3b1b4272021-03-02 17:58:10 -0800296 }
297 return info.size;
298}
299
Patrick Ventureab650002019-03-16 09:08:47 -0700300std::string Handler::getEntityName(std::uint8_t id, std::uint8_t instance)
Patrick Venture07f85152019-03-15 21:36:56 -0700301{
Patrick Ventureab650002019-03-16 09:08:47 -0700302 // Check if we support this Entity ID.
303 auto it = _entityIdToName.find(id);
304 if (it == _entityIdToName.end())
Patrick Venture07f85152019-03-15 21:36:56 -0700305 {
Patrick Ventureab650002019-03-16 09:08:47 -0700306 log<level::ERR>("Unknown Entity ID", entry("ENTITY_ID=%d", id));
Michael Shene5a06672022-06-20 05:08:32 +0000307 throw IpmiException(::ipmi::ccInvalidFieldRequest);
Patrick Venture07f85152019-03-15 21:36:56 -0700308 }
309
Patrick Ventureab650002019-03-16 09:08:47 -0700310 std::string entityName;
311 try
Patrick Venture07f85152019-03-15 21:36:56 -0700312 {
Patrick Ventureab650002019-03-16 09:08:47 -0700313 // Parse the JSON config file.
314 if (!_entityConfigParsed)
315 {
316 _entityConfig = parseConfig(_configFile);
317 _entityConfigParsed = true;
318 }
319
320 // Find the "entity id:entity instance" mapping to entity name.
321 entityName = readNameFromConfig(it->second, instance, _entityConfig);
322 if (entityName.empty())
323 {
Michael Shene5a06672022-06-20 05:08:32 +0000324 throw IpmiException(::ipmi::ccInvalidFieldRequest);
Patrick Ventureab650002019-03-16 09:08:47 -0700325 }
326 }
327 catch (InternalFailure& e)
328 {
Michael Shene5a06672022-06-20 05:08:32 +0000329 throw IpmiException(::ipmi::ccUnspecifiedError);
Patrick Venture07f85152019-03-15 21:36:56 -0700330 }
331
Patrick Ventureab650002019-03-16 09:08:47 -0700332 return entityName;
Patrick Venture07f85152019-03-15 21:36:56 -0700333}
334
William A. Kennington III29f35bc2020-11-03 23:30:31 -0800335std::string Handler::getMachineName()
336{
337 const char* path = "/etc/os-release";
338 std::ifstream ifs(path);
339 if (ifs.fail())
340 {
Michael Shen8d618532023-10-25 09:14:07 +0000341 stdplus::print(stderr, "Failed to open: {}\n", path);
Michael Shene5a06672022-06-20 05:08:32 +0000342 throw IpmiException(::ipmi::ccUnspecifiedError);
William A. Kennington III29f35bc2020-11-03 23:30:31 -0800343 }
344
345 std::string line;
346 while (true)
347 {
348 std::getline(ifs, line);
349 if (ifs.eof())
350 {
Michael Shen8d618532023-10-25 09:14:07 +0000351 stdplus::print(stderr,
352 "Failed to find OPENBMC_TARGET_MACHINE: {}\n", path);
Michael Shene5a06672022-06-20 05:08:32 +0000353 throw IpmiException(::ipmi::ccInvalidCommand);
William A. Kennington III29f35bc2020-11-03 23:30:31 -0800354 }
355 if (ifs.fail())
356 {
Michael Shen8d618532023-10-25 09:14:07 +0000357 stdplus::print(stderr, "Failed to read: {}\n", path);
Michael Shene5a06672022-06-20 05:08:32 +0000358 throw IpmiException(::ipmi::ccUnspecifiedError);
William A. Kennington III29f35bc2020-11-03 23:30:31 -0800359 }
360 std::string_view lineView(line);
361 constexpr std::string_view prefix = "OPENBMC_TARGET_MACHINE=";
362 if (lineView.substr(0, prefix.size()) != prefix)
363 {
364 continue;
365 }
366 lineView.remove_prefix(prefix.size());
367 lineView.remove_prefix(
368 std::min(lineView.find_first_not_of('"'), lineView.size()));
369 lineView.remove_suffix(
370 lineView.size() - 1 -
371 std::min(lineView.find_last_not_of('"'), lineView.size() - 1));
372 return std::string(lineView);
373 }
374}
375
linyuny8cfa4c42021-06-16 13:53:08 -0700376static constexpr auto HOST_TIME_DELAY_FILENAME = "/run/host_poweroff_delay";
377static constexpr auto HOST_POWEROFF_TARGET = "gbmc-host-poweroff.target";
378
379void Handler::hostPowerOffDelay(std::uint32_t delay) const
380{
381 // Set time delay
382 std::ofstream ofs;
383 ofs.open(HOST_TIME_DELAY_FILENAME, std::ofstream::out);
384 if (!ofs.good())
385 {
Michael Shen8d618532023-10-25 09:14:07 +0000386 stdplus::print(stderr, "Unable to open file for output.\n");
Michael Shene5a06672022-06-20 05:08:32 +0000387 throw IpmiException(::ipmi::ccUnspecifiedError);
linyuny8cfa4c42021-06-16 13:53:08 -0700388 }
389
390 ofs << "HOST_POWEROFF_DELAY=" << delay << std::endl;
391 ofs.close();
392 if (ofs.fail())
393 {
Michael Shen8d618532023-10-25 09:14:07 +0000394 stdplus::print(stderr, "Write failed\n");
Michael Shene5a06672022-06-20 05:08:32 +0000395 throw IpmiException(::ipmi::ccUnspecifiedError);
linyuny8cfa4c42021-06-16 13:53:08 -0700396 }
397
398 // Write succeeded, please continue.
399 auto bus = sdbusplus::bus::new_default();
400 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
401 SYSTEMD_INTERFACE, "StartUnit");
402
403 method.append(HOST_POWEROFF_TARGET);
404 method.append("replace");
405
406 try
407 {
408 bus.call_noreply(method);
409 }
410 catch (const sdbusplus::exception::SdBusError& ex)
411 {
412 log<level::ERR>("Failed to call Power Off",
413 entry("WHAT=%s", ex.what()));
Michael Shene5a06672022-06-20 05:08:32 +0000414 throw IpmiException(::ipmi::ccUnspecifiedError);
linyuny8cfa4c42021-06-16 13:53:08 -0700415 }
416}
417
Patrick Ventureab650002019-03-16 09:08:47 -0700418std::string readNameFromConfig(const std::string& type, uint8_t instance,
419 const Json& config)
Patrick Venture07f85152019-03-15 21:36:56 -0700420{
421 static const std::vector<Json> empty{};
422 std::vector<Json> readings = config.value(type, empty);
423 std::string name = "";
Patrick Ventureab650002019-03-16 09:08:47 -0700424
Patrick Venture07f85152019-03-15 21:36:56 -0700425 for (const auto& j : readings)
426 {
427 uint8_t instanceNum = j.value("instance", 0);
428 // Not the instance we're interested in
429 if (instanceNum != instance)
430 {
431 continue;
432 }
433
434 // Found the instance we're interested in
435 name = j.value("name", "");
436
437 break;
438 }
Patrick Ventureab650002019-03-16 09:08:47 -0700439
Patrick Venture07f85152019-03-15 21:36:56 -0700440 return name;
441}
442
Patrick Venture49f23ad2019-03-16 11:59:55 -0700443void Handler::buildI2cPcieMapping()
444{
445 _pcie_i2c_map = buildPcieMap();
446}
447
448size_t Handler::getI2cPcieMappingSize() const
449{
450 return _pcie_i2c_map.size();
451}
452
Patrick Williams6dee32b2025-02-01 08:23:56 -0500453std::tuple<std::uint32_t, std::string> Handler::getI2cEntry(
454 unsigned int entry) const
Patrick Venture49f23ad2019-03-16 11:59:55 -0700455{
456 return _pcie_i2c_map[entry];
457}
458
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700459namespace
460{
461
462static constexpr std::string_view ACCEL_OOB_ROOT = "/com/google/customAccel/";
463static constexpr char ACCEL_OOB_SERVICE[] = "com.google.custom_accel";
464static constexpr char ACCEL_OOB_INTERFACE[] = "com.google.custom_accel.BAR";
465
466// C type for "a{oa{sa{sv}}}" from DBus.ObjectManager::GetManagedObjects()
467using AnyType = std::variant<std::string, uint8_t, uint32_t, uint64_t>;
468using AnyTypeList = std::vector<std::pair<std::string, AnyType>>;
469using NamedArrayOfAnyTypeLists =
470 std::vector<std::pair<std::string, AnyTypeList>>;
471using ArrayOfObjectPathsAndTieredAnyTypeLists = std::vector<
472 std::pair<sdbusplus::message::object_path, NamedArrayOfAnyTypeLists>>;
473
474} // namespace
475
Patrick Williams65371222023-05-19 02:31:40 -0500476sdbusplus::bus_t Handler::getDbus() const
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700477{
478 return sdbusplus::bus::new_default();
479}
480
Hao Zhou15d4d212023-07-11 20:18:04 +0000481const std::unique_ptr<FileSystemInterface>& Handler::getFs() const
482{
483 return this->fsPtr;
484}
485
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700486uint32_t Handler::accelOobDeviceCount() const
487{
488 ArrayOfObjectPathsAndTieredAnyTypeLists data;
489
490 try
491 {
Michael Shen0e928ac2022-06-20 05:21:52 +0000492 auto bus = getDbus();
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700493 auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/",
494 "org.freedesktop.DBus.ObjectManager",
495 "GetManagedObjects");
496 bus.call(method).read(data);
497 }
498 catch (const sdbusplus::exception::SdBusError& ex)
499 {
500 log<level::ERR>(
501 "Failed to call GetManagedObjects on com.google.custom_accel",
502 entry("WHAT=%s", ex.what()));
Michael Shene5a06672022-06-20 05:08:32 +0000503 throw IpmiException(::ipmi::ccUnspecifiedError);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700504 }
505
506 return data.size();
507}
508
509std::string Handler::accelOobDeviceName(size_t index) const
510{
511 ArrayOfObjectPathsAndTieredAnyTypeLists data;
512
513 try
514 {
Michael Shen0e928ac2022-06-20 05:21:52 +0000515 auto bus = getDbus();
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700516 auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/",
517 "org.freedesktop.DBus.ObjectManager",
518 "GetManagedObjects");
519 bus.call(method).read(data);
520 }
521 catch (const sdbusplus::exception::SdBusError& ex)
522 {
523 log<level::ERR>(
524 "Failed to call GetManagedObjects on com.google.custom_accel",
525 entry("WHAT=%s", ex.what()));
Michael Shene5a06672022-06-20 05:08:32 +0000526 throw IpmiException(::ipmi::ccUnspecifiedError);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700527 }
528
529 if (index >= data.size())
530 {
531 log<level::WARNING>(
532 "Requested index is larger than the number of entries.",
533 entry("INDEX=%zu", index), entry("NUM_NAMES=%zu", data.size()));
Michael Shene5a06672022-06-20 05:08:32 +0000534 throw IpmiException(::ipmi::ccParmOutOfRange);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700535 }
536
537 std::string_view name(data[index].first.str);
538 if (!name.starts_with(ACCEL_OOB_ROOT))
539 {
Michael Shene5a06672022-06-20 05:08:32 +0000540 throw IpmiException(::ipmi::ccInvalidCommand);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700541 }
542 name.remove_prefix(ACCEL_OOB_ROOT.length());
543 return std::string(name);
544}
545
546uint64_t Handler::accelOobRead(std::string_view name, uint64_t address,
547 uint8_t num_bytes) const
548{
549 static constexpr char ACCEL_OOB_METHOD[] = "Read";
550
551 std::string object_name(ACCEL_OOB_ROOT);
552 object_name.append(name);
553
Michael Shen0e928ac2022-06-20 05:21:52 +0000554 auto bus = getDbus();
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700555 auto method = bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(),
556 ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD);
557 method.append(address, static_cast<uint64_t>(num_bytes));
558
559 std::vector<uint8_t> bytes;
560
561 try
562 {
563 bus.call(method).read(bytes);
564 }
565 catch (const sdbusplus::exception::SdBusError& ex)
566 {
567 log<level::ERR>("Failed to call Read on com.google.custom_accel",
568 entry("WHAT=%s", ex.what()),
569 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
570 entry("DBUS_OBJECT=%s", object_name.c_str()),
571 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
572 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
573 entry("DBUS_ARG_ADDRESS=%016llx", address),
574 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes));
Michael Shene5a06672022-06-20 05:08:32 +0000575 throw IpmiException(::ipmi::ccUnspecifiedError);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700576 }
577
578 if (bytes.size() < num_bytes)
579 {
580 log<level::ERR>(
581 "Call to Read on com.google.custom_accel didn't return the expected"
582 " number of bytes.",
583 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
584 entry("DBUS_OBJECT=%s", object_name.c_str()),
585 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
586 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
587 entry("DBUS_ARG_ADDRESS=%016llx", address),
588 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
589 entry("DBUS_RETURN_SIZE=%zu", bytes.size()));
Michael Shene5a06672022-06-20 05:08:32 +0000590 throw IpmiException(::ipmi::ccUnspecifiedError);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700591 }
592
593 if (bytes.size() > sizeof(uint64_t))
594 {
595 log<level::ERR>(
596 "Call to Read on com.google.custom_accel returned more than 8B.",
597 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
598 entry("DBUS_OBJECT=%s", object_name.c_str()),
599 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
600 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
601 entry("DBUS_ARG_ADDRESS=%016llx", address),
602 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
603 entry("DBUS_RETURN_SIZE=%zu", bytes.size()));
Michael Shene5a06672022-06-20 05:08:32 +0000604 throw IpmiException(::ipmi::ccReqDataTruncated);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700605 }
606
607 uint64_t data = 0;
608 for (size_t i = 0; i < num_bytes; ++i)
609 {
610 data = (data << 8) | bytes[i];
611 }
612
613 return data;
614}
615
616void Handler::accelOobWrite(std::string_view name, uint64_t address,
617 uint8_t num_bytes, uint64_t data) const
618{
619 static constexpr std::string_view ACCEL_OOB_METHOD = "Write";
620
621 std::string object_name(ACCEL_OOB_ROOT);
622 object_name.append(name);
623
624 if (num_bytes > sizeof(data))
625 {
626 log<level::ERR>(
627 "Call to Write on com.google.custom_accel requested more than 8B.",
628 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
629 entry("DBUS_OBJECT=%s", object_name.c_str()),
630 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
631 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()),
632 entry("DBUS_ARG_ADDRESS=%016llx", address),
633 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
634 entry("DBUS_ARG_DATA=%016llx", data));
Michael Shene5a06672022-06-20 05:08:32 +0000635 throw IpmiException(::ipmi::ccParmOutOfRange);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700636 }
637
638 std::vector<uint8_t> bytes;
639 bytes.reserve(num_bytes);
640 for (size_t i = 0; i < num_bytes; ++i)
641 {
642 bytes.emplace_back(data & 0xff);
643 data >>= 8;
644 }
645
646 try
647 {
Michael Shen0e928ac2022-06-20 05:21:52 +0000648 auto bus = getDbus();
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700649 auto method =
650 bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(),
651 ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD.data());
652 method.append(address, bytes);
653 bus.call_noreply(method);
654 }
655 catch (const sdbusplus::exception::SdBusError& ex)
656 {
657 log<level::ERR>("Failed to call Write on com.google.custom_accel",
658 entry("WHAT=%s", ex.what()),
659 entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
660 entry("DBUS_OBJECT=%s", object_name.c_str()),
661 entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
662 entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()),
663 entry("DBUS_ARG_ADDRESS=%016llx", address),
664 entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
665 entry("DBUS_ARG_DATA=%016llx", data));
Michael Shene5a06672022-06-20 05:08:32 +0000666 throw IpmiException(::ipmi::ccUnspecifiedError);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700667 }
668}
669
Willy Tu6c71b0f2021-10-10 13:34:41 -0700670std::vector<uint8_t> Handler::pcieBifurcation(uint8_t index)
671{
672 return bifurcationHelper.get().getBifurcation(index).value_or(
673 std::vector<uint8_t>{});
674}
675
John Wedig378b59a2024-11-06 16:42:50 -0800676static constexpr auto BARE_METAL_TARGET = "gbmc-bare-metal-active@0.target";
John Wediga92d0e62023-06-29 10:43:47 -0700677
678void Handler::linuxBootDone() const
679{
Hao Zhou15d4d212023-07-11 20:18:04 +0000680 if (isBmcInBareMetalMode(this->fsPtr) !=
681 static_cast<uint8_t>(BmcMode::BM_MODE))
John Wediga92d0e62023-06-29 10:43:47 -0700682 {
683 return;
684 }
685
686 log<level::INFO>("LinuxBootDone: Disabling IPMI");
687
688 // Start the bare metal active systemd target.
689 auto bus = sdbusplus::bus::new_default();
690 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
691 SYSTEMD_INTERFACE, "StartUnit");
692
693 method.append(BARE_METAL_TARGET);
694 method.append("replace");
695
696 try
697 {
698 bus.call_noreply(method);
699 }
700 catch (const sdbusplus::exception::SdBusError& ex)
701 {
702 log<level::ERR>("Failed to start bare metal active systemd target",
703 entry("WHAT=%s", ex.what()));
704 throw IpmiException(::ipmi::ccUnspecifiedError);
705 }
706}
707
Gaurav Gandhic8c64be2024-10-09 10:23:59 -0700708static constexpr char ACCEL_POWER_SERVICE[] = "com.google.AccelPower";
Gaurav Gandhid455bfd2024-01-29 22:32:27 -0800709static constexpr char ACCEL_POWER_PATH_PREFIX[] =
Gaurav Gandhic8c64be2024-10-09 10:23:59 -0700710 "/com/google/accelPower/accel_power_";
711static constexpr char POWER_MODE_IFC[] = "com.google.accelPower.Mode";
Gaurav Gandhid455bfd2024-01-29 22:32:27 -0800712
713void Handler::accelSetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
714 uint8_t settings_id, uint16_t value) const
715{
716 int vrSettingsReq;
717 boost::system::error_code ec;
718 if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end())
719 {
720 log<level::ERR>("Settings ID is not supported",
721 entry("settings_id=%d", settings_id));
722 throw IpmiException(::ipmi::ccParmOutOfRange);
723 }
724
725 vrSettingsReq = static_cast<int>(settings_id | value << 8);
726 std::string object_name(
727 std::format("{}{}", ACCEL_POWER_PATH_PREFIX, chip_id));
728
729 std::variant<int> val = vrSettingsReq;
Patrick Williams8c0094e2024-08-16 15:22:37 -0400730 ctx->bus->yield_method_call(
731 ctx->yield, ec, ACCEL_POWER_SERVICE, object_name.c_str(),
732 "org.freedesktop.DBus.Properties", "Set", POWER_MODE_IFC, "PowerMode",
733 val);
Gaurav Gandhid455bfd2024-01-29 22:32:27 -0800734 if (ec)
735 {
736 log<level::ERR>("Failed to set PowerMode property");
737 throw IpmiException(::ipmi::ccUnspecifiedError);
738 }
739}
740
741static constexpr char EXTERNAL_SENSOR_SERVICE[] =
742 "xyz.openbmc_project.ExternalSensor";
743static constexpr char EXTERNAL_SENSOR_PATH_PREFIX[] =
744 "/xyz/openbmc_project/sensors/power/";
745static constexpr char SENSOR_VALUE_IFC[] = "xyz.openbmc_project.Sensor.Value";
746
747uint16_t Handler::accelGetVrSettings(::ipmi::Context::ptr ctx, uint8_t chip_id,
748 uint8_t settings_id) const
749{
750 Value value;
751 boost::system::error_code ec;
Patrick Williams8c0094e2024-08-16 15:22:37 -0400752 std::string object_name(
753 std::format("{}{}{}", EXTERNAL_SENSOR_PATH_PREFIX,
754 _vrSettingsMap.at(settings_id), chip_id));
Gaurav Gandhid455bfd2024-01-29 22:32:27 -0800755
756 if (_vrSettingsMap.find(settings_id) == _vrSettingsMap.end())
757 {
758 log<level::ERR>("Settings ID is not supported",
759 entry("settings_id=%d", settings_id));
760 throw IpmiException(::ipmi::ccParmOutOfRange);
761 }
762
763 value = ctx->bus->yield_method_call<std::variant<double>>(
764 ctx->yield, ec, EXTERNAL_SENSOR_SERVICE, object_name.c_str(),
765 "org.freedesktop.DBus.Properties", "Get", SENSOR_VALUE_IFC, "Value");
766 if (ec)
767 {
768 log<level::ERR>("accelGetVrSettings: Failed to call GetObject ");
769 throw IpmiException(::ipmi::ccUnspecifiedError);
770 }
771
772 return static_cast<uint16_t>(std::get<double>(value));
773}
Brandon Kim559cb012024-05-03 09:12:07 +0000774
775std::string Handler::getBMInstanceProperty(uint8_t propertyType) const
776{
777 std::string propertyTypeString;
778 if (auto it = bmInstanceTypeStringMap.find(propertyType);
779 it == bmInstanceTypeStringMap.end())
780 {
781 stdplus::print(stderr, "PropertyType: '{}' is invalid.\n",
782 propertyType);
783 throw IpmiException(::ipmi::ccInvalidFieldRequest);
784 }
785 else
786 {
787 propertyTypeString = it->second;
788 }
789 std::string opath = std::format("/run/bm-instance/{}", propertyTypeString);
790 // Check for file
791
792 std::error_code ec;
793 // TODO(brandonkim@google.com): Fix this to use stdplus::ManagedFd
794 if (!this->getFs()->exists(opath, ec))
795 {
796 stdplus::print(stderr, "Path: '{}' doesn't exist.\n", opath);
797 throw IpmiException(::ipmi::ccInvalidFieldRequest);
798 }
799
Brandon Kim559cb012024-05-03 09:12:07 +0000800 std::ifstream ifs;
801 ifs.exceptions(std::ifstream::failbit);
802 std::string property;
803 try
804 {
805 ifs.open(opath);
Brandon Kim56b2d9f2024-05-30 20:54:33 +0000806 std::getline(ifs, property);
Brandon Kim559cb012024-05-03 09:12:07 +0000807 }
808 catch (std::ios_base::failure& fail)
809 {
810 stdplus::print(stderr, "Failed to read: '{}'.\n", opath);
811 throw IpmiException(::ipmi::ccUnspecifiedError);
812 }
Vikram Gara4134c742026-01-12 05:29:10 +0000813
Brandon Kim559cb012024-05-03 09:12:07 +0000814 return property;
815}
816
Vikram Gara4134c742026-01-12 05:29:10 +0000817std::optional<uint16_t> Handler::getCoreCount(const std::string& filePath) const
818{
819 std::error_code ec;
820 if (!this->getFs()->exists(filePath, ec))
821 {
822 log<level::INFO>("CPU config file not found",
823 entry("PATH=%s", filePath.c_str()));
824 return std::nullopt;
825 }
826
827 std::ifstream ifs(filePath);
828 if (!ifs.is_open())
829 {
830 log<level::ERR>("Failed to open CPU config file",
831 entry("PATH=%s", filePath.c_str()));
832 return std::nullopt;
833 }
834
835 try
836 {
837 Json data = Json::parse(ifs);
838 if (data.contains("cpu_core_count") &&
839 data["cpu_core_count"].is_number_integer())
840 {
841 int coreCountInt = data["cpu_core_count"].get<int>();
842 if (coreCountInt < 0 || coreCountInt > UINT16_MAX)
843 {
844 log<level::ERR>("Core count out of range for uint16_t",
845 entry("PATH=%s", filePath.c_str()),
846 entry("VALUE=%d", coreCountInt));
847 return std::nullopt;
848 }
849 return static_cast<uint16_t>(coreCountInt);
850 }
851 else
852 {
853 log<level::ERR>("Invalid format in CPU config file",
854 entry("PATH=%s", filePath.c_str()));
855 return std::nullopt;
856 }
857 }
858 catch (Json::parse_error& e)
859 {
860 log<level::ERR>("Failed to parse CPU config file",
861 entry("PATH=%s", filePath.c_str()),
862 entry("WHAT=%s", e.what()));
863 return std::nullopt;
864 }
865 catch (...)
866 {
867 log<level::ERR>("Unknown error reading CPU config file",
868 entry("PATH=%s", filePath.c_str()));
869 return std::nullopt;
870 }
871}
872
Patrick Venturef085d912019-03-15 08:50:00 -0700873} // namespace ipmi
874} // namespace google