blob: 8ad287968327a09150712892c6285ec8df39a688 [file] [log] [blame]
Potin Lai5d214202023-01-16 18:11:49 +08001#include "config.h"
2
Patrick Venture0b02be92018-08-31 11:55:55 -07003#include <arpa/inet.h>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05304#include <fcntl.h>
Xo Wang87651332017-08-11 10:17:59 -07005#include <limits.h>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05306#include <linux/i2c-dev.h>
7#include <linux/i2c.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07008#include <mapper.h>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05309#include <sys/ioctl.h>
10#include <sys/stat.h>
11#include <sys/types.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070012#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -070013#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070014
Patrick Venture3a5071a2018-09-12 13:27:42 -070015#include <algorithm>
Vernon Mauery0120b682019-03-25 13:08:54 -070016#include <app/channel.hpp>
17#include <app/watchdog.hpp>
18#include <apphandler.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070019#include <array>
Willy Tu886d6842022-06-03 02:50:47 -070020#include <charconv>
Patrick Venture3a5071a2018-09-12 13:27:42 -070021#include <cstddef>
Vernon Mauery0120b682019-03-25 13:08:54 -070022#include <cstdint>
Vernon Mauerybdda8002019-02-26 10:18:51 -080023#include <filesystem>
Patrick Venture3a5071a2018-09-12 13:27:42 -070024#include <fstream>
Vernon Mauery0120b682019-03-25 13:08:54 -070025#include <ipmid/api.hpp>
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +000026#include <ipmid/sessiondef.hpp>
27#include <ipmid/sessionhelper.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -070028#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070029#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070030#include <memory>
Patrick Venture46470a32018-09-07 19:26:25 -070031#include <nlohmann/json.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070032#include <phosphor-logging/elog-errors.hpp>
33#include <phosphor-logging/log.hpp>
Potin Lai5d214202023-01-16 18:11:49 +080034#include <regex>
William A. Kennington III4c008022018-10-12 17:18:14 -070035#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070036#include <string>
Willy Tu886d6842022-06-03 02:50:47 -070037#include <string_view>
Vernon Mauery0120b682019-03-25 13:08:54 -070038#include <sys_info_param.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070039#include <tuple>
40#include <vector>
41#include <xyz/openbmc_project/Common/error.hpp>
Yong Li18d77262018-10-09 01:59:45 +080042#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070043#include <xyz/openbmc_project/Software/Activation/server.hpp>
44#include <xyz/openbmc_project/Software/Version/server.hpp>
45#include <xyz/openbmc_project/State/BMC/server.hpp>
Ratan Guptab8e99552017-07-27 07:07:48 +053046
Patrick Venture0b02be92018-08-31 11:55:55 -070047extern sd_bus* bus;
vishwabmcba0bd5f2015-09-30 16:50:23 +053048
Alexander Amelkinba19c182018-09-04 15:49:36 +030049constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
50constexpr auto bmc_state_property = "CurrentBMCState";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060051
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060052static constexpr auto redundancyIntf =
53 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070054static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060055static constexpr auto activationIntf =
56 "xyz.openbmc_project.Software.Activation";
57static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
58
Chris Austen6caf28b2015-10-13 12:40:40 -050059void register_netfn_app_functions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053060
Ratan Guptab8e99552017-07-27 07:07:48 +053061using namespace phosphor::logging;
62using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060063using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
64using Activation =
65 sdbusplus::xyz::openbmc_project::Software::server::Activation;
Alexander Amelkinba19c182018-09-04 15:49:36 +030066using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070067namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053068
Yong Libd0503a2019-08-22 17:17:17 +080069#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053070typedef struct
71{
72 uint8_t busId;
73 uint8_t slaveAddr;
74 uint8_t slaveAddrMask;
75 std::vector<uint8_t> data;
76 std::vector<uint8_t> dataMask;
77} i2cMasterWRWhitelist;
78
79static std::vector<i2cMasterWRWhitelist>& getWRWhitelist()
80{
81 static std::vector<i2cMasterWRWhitelist> wrWhitelist;
82 return wrWhitelist;
83}
84
85static constexpr const char* i2cMasterWRWhitelistFile =
86 "/usr/share/ipmi-providers/master_write_read_white_list.json";
87
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053088static constexpr const char* filtersStr = "filters";
89static constexpr const char* busIdStr = "busId";
90static constexpr const char* slaveAddrStr = "slaveAddr";
91static constexpr const char* slaveAddrMaskStr = "slaveAddrMask";
92static constexpr const char* cmdStr = "command";
93static constexpr const char* cmdMaskStr = "commandMask";
94static constexpr int base_16 = 16;
Yong Libd0503a2019-08-22 17:17:17 +080095#endif // ENABLE_I2C_WHITELIST_CHECK
Joshi-Mansi7fd91fa2021-06-11 07:18:15 +053096static constexpr uint8_t maxIPMIWriteReadSize = 255;
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +000097static constexpr uint8_t oemCmdStart = 192;
98static constexpr uint8_t oemCmdEnd = 255;
99static constexpr uint8_t invalidParamSelectorStart = 8;
100static constexpr uint8_t invalidParamSelectorEnd = 191;
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +0530101
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600102/**
103 * @brief Returns the Version info from primary s/w object
104 *
105 * Get the Version info from the active s/w object which is having high
106 * "Priority" value(a smaller number is a higher priority) and "Purpose"
107 * is "BMC" from the list of all s/w objects those are implementing
108 * RedundancyPriority interface from the given softwareRoot path.
109 *
110 * @return On success returns the Version info from primary s/w object.
111 *
112 */
Vernon Maueryea1c4012019-05-24 13:26:16 -0700113std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600114{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600115 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -0700116 ipmi::ObjectTree objectTree;
117 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600118 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700119 objectTree =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700120 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
Vernon Mauery86a50822019-03-25 13:11:36 -0700121 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500122 catch (const sdbusplus::exception_t& e)
Vernon Mauery86a50822019-03-25 13:11:36 -0700123 {
124 log<level::ERR>("Failed to fetch redundancy object from dbus",
125 entry("INTERFACE=%s", redundancyIntf),
126 entry("ERRMSG=%s", e.what()));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600127 elog<InternalFailure>();
128 }
129
130 auto objectFound = false;
131 for (auto& softObject : objectTree)
132 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700133 auto service =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700134 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
Vernon Mauery86a50822019-03-25 13:11:36 -0700135 auto objValueTree =
Vernon Maueryea1c4012019-05-24 13:26:16 -0700136 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600137
138 auto minPriority = 0xFF;
139 for (const auto& objIter : objValueTree)
140 {
141 try
142 {
143 auto& intfMap = objIter.second;
144 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
145 auto& versionProps = intfMap.at(versionIntf);
146 auto& activationProps = intfMap.at(activationIntf);
Vernon Maueryf442e112019-04-09 11:44:36 -0700147 auto priority =
148 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700149 auto purpose =
Vernon Maueryf442e112019-04-09 11:44:36 -0700150 std::get<std::string>(versionProps.at("Purpose"));
151 auto activation =
152 std::get<std::string>(activationProps.at("Activation"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700153 auto version =
Vernon Maueryf442e112019-04-09 11:44:36 -0700154 std::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600155 if ((Version::convertVersionPurposeFromString(purpose) ==
156 Version::VersionPurpose::BMC) &&
157 (Activation::convertActivationsFromString(activation) ==
158 Activation::Activations::Active))
159 {
160 if (priority < minPriority)
161 {
162 minPriority = priority;
163 objectFound = true;
164 revision = std::move(version);
165 }
166 }
167 }
168 catch (const std::exception& e)
169 {
170 log<level::ERR>(e.what());
171 }
172 }
173 }
174
175 if (!objectFound)
176 {
177 log<level::ERR>("Could not found an BMC software Object");
178 elog<InternalFailure>();
179 }
180
181 return revision;
182}
183
Alexander Amelkinba19c182018-09-04 15:49:36 +0300184bool getCurrentBmcState()
185{
Patrick Williams5d82f472022-07-22 19:26:53 -0500186 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Alexander Amelkinba19c182018-09-04 15:49:36 +0300187
188 // Get the Inventory object implementing the BMC interface
189 ipmi::DbusObjectInfo bmcObject =
190 ipmi::getDbusObject(bus, bmc_state_interface);
191 auto variant =
192 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
193 bmc_state_interface, bmc_state_property);
194
Vernon Maueryf442e112019-04-09 11:44:36 -0700195 return std::holds_alternative<std::string>(variant) &&
196 BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
197 BMC::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300198}
199
Patrick Venture94930a12019-04-30 10:01:58 -0700200bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
201{
202 try
203 {
204 return getCurrentBmcState();
205 }
206 catch (...)
207 {
208 // Nothing provided the BMC interface, therefore return whatever was
209 // configured as the default.
210 return fallbackAvailability;
211 }
212}
213
Yong Li18d77262018-10-09 01:59:45 +0800214namespace acpi_state
215{
216using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
217
218const static constexpr char* acpiObjPath =
219 "/xyz/openbmc_project/control/host0/acpi_power_state";
220const static constexpr char* acpiInterface =
221 "xyz.openbmc_project.Control.Power.ACPIPowerState";
222const static constexpr char* sysACPIProp = "SysACPIStatus";
223const static constexpr char* devACPIProp = "DevACPIStatus";
224
225enum class PowerStateType : uint8_t
226{
227 sysPowerState = 0x00,
228 devPowerState = 0x01,
229};
230
231// Defined in 20.6 of ipmi doc
232enum class PowerState : uint8_t
233{
234 s0G0D0 = 0x00,
235 s1D1 = 0x01,
236 s2D2 = 0x02,
237 s3D3 = 0x03,
238 s4 = 0x04,
239 s5G2 = 0x05,
240 s4S5 = 0x06,
241 g3 = 0x07,
242 sleep = 0x08,
243 g1Sleep = 0x09,
244 override = 0x0a,
245 legacyOn = 0x20,
246 legacyOff = 0x21,
247 unknown = 0x2a,
248 noChange = 0x7f,
249};
250
251static constexpr uint8_t stateChanged = 0x80;
252
Yong Li18d77262018-10-09 01:59:45 +0800253std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
254 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
255 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
256 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
257 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
258 {ACPIPowerState::ACPI::S4, PowerState::s4},
259 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
260 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
261 {ACPIPowerState::ACPI::G3, PowerState::g3},
262 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
263 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
264 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
265 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
266 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
267 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
268
269bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
270{
271 if (type == acpi_state::PowerStateType::sysPowerState)
272 {
273 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
274 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
275 (state ==
276 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
277 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
278 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
279 {
280 return true;
281 }
282 else
283 {
284 return false;
285 }
286 }
287 else if (type == acpi_state::PowerStateType::devPowerState)
288 {
289 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
290 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
291 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
292 {
293 return true;
294 }
295 else
296 {
297 return false;
298 }
299 }
300 else
301 {
302 return false;
303 }
304 return false;
305}
306} // namespace acpi_state
307
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000308/** @brief implements Set ACPI Power State command
309 * @param sysAcpiState - ACPI system power state to set
310 * @param devAcpiState - ACPI device power state to set
311 *
312 * @return IPMI completion code on success
313 **/
314ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState,
315 uint8_t devAcpiState)
Chris Austen6caf28b2015-10-13 12:40:40 -0500316{
Yong Li18d77262018-10-09 01:59:45 +0800317 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Yong Li18d77262018-10-09 01:59:45 +0800318
Patrick Williams5d82f472022-07-22 19:26:53 -0500319 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Yong Li18d77262018-10-09 01:59:45 +0800320
321 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
322
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000323 if (sysAcpiState & acpi_state::stateChanged)
Yong Li18d77262018-10-09 01:59:45 +0800324 {
325 // set system power state
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000326 s = sysAcpiState & ~acpi_state::stateChanged;
Yong Li18d77262018-10-09 01:59:45 +0800327
328 if (!acpi_state::isValidACPIState(
329 acpi_state::PowerStateType::sysPowerState, s))
330 {
331 log<level::ERR>("set_acpi_power sys invalid input",
332 entry("S=%x", s));
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000333 return ipmi::responseParmOutOfRange();
Yong Li18d77262018-10-09 01:59:45 +0800334 }
335
336 // valid input
337 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
338 {
339 log<level::DEBUG>("No change for system power state");
340 }
341 else
342 {
343 auto found = std::find_if(
344 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
345 [&s](const auto& iter) {
346 return (static_cast<uint8_t>(iter.second) == s);
347 });
348
349 value = found->first;
350
351 try
352 {
353 auto acpiObject =
354 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
355 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
356 acpi_state::acpiInterface,
357 acpi_state::sysACPIProp,
358 convertForMessage(value));
359 }
360 catch (const InternalFailure& e)
361 {
362 log<level::ERR>("Failed in set ACPI system property",
363 entry("EXCEPTION=%s", e.what()));
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000364 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800365 }
366 }
367 }
368 else
369 {
370 log<level::DEBUG>("Do not change system power state");
371 }
372
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000373 if (devAcpiState & acpi_state::stateChanged)
Yong Li18d77262018-10-09 01:59:45 +0800374 {
375 // set device power state
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000376 s = devAcpiState & ~acpi_state::stateChanged;
Yong Li18d77262018-10-09 01:59:45 +0800377 if (!acpi_state::isValidACPIState(
378 acpi_state::PowerStateType::devPowerState, s))
379 {
380 log<level::ERR>("set_acpi_power dev invalid input",
381 entry("S=%x", s));
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000382 return ipmi::responseParmOutOfRange();
Yong Li18d77262018-10-09 01:59:45 +0800383 }
384
385 // valid input
386 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
387 {
388 log<level::DEBUG>("No change for device power state");
389 }
390 else
391 {
392 auto found = std::find_if(
393 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
394 [&s](const auto& iter) {
395 return (static_cast<uint8_t>(iter.second) == s);
396 });
397
398 value = found->first;
399
400 try
401 {
402 auto acpiObject =
403 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
404 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
405 acpi_state::acpiInterface,
406 acpi_state::devACPIProp,
407 convertForMessage(value));
408 }
409 catch (const InternalFailure& e)
410 {
411 log<level::ERR>("Failed in set ACPI device property",
412 entry("EXCEPTION=%s", e.what()));
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000413 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800414 }
415 }
416 }
417 else
418 {
419 log<level::DEBUG>("Do not change device power state");
420 }
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000421 return ipmi::responseSuccess();
Yong Li18d77262018-10-09 01:59:45 +0800422}
423
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000424/**
425 * @brief implements the get ACPI power state command
426 *
427 * @return IPMI completion code plus response data on success.
428 * - ACPI system power state
429 * - ACPI device power state
430 **/
431ipmi::RspType<uint8_t, // acpiSystemPowerState
432 uint8_t // acpiDevicePowerState
433 >
434 ipmiGetAcpiPowerState()
Yong Li18d77262018-10-09 01:59:45 +0800435{
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000436 uint8_t sysAcpiState;
437 uint8_t devAcpiState;
Yong Li18d77262018-10-09 01:59:45 +0800438
Patrick Williams5d82f472022-07-22 19:26:53 -0500439 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Yong Li18d77262018-10-09 01:59:45 +0800440
Yong Li18d77262018-10-09 01:59:45 +0800441 try
442 {
443 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
444
445 auto sysACPIVal = ipmi::getDbusProperty(
446 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
447 acpi_state::sysACPIProp);
448 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700449 std::get<std::string>(sysACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000450 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
Yong Li18d77262018-10-09 01:59:45 +0800451
452 auto devACPIVal = ipmi::getDbusProperty(
453 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
454 acpi_state::devACPIProp);
455 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700456 std::get<std::string>(devACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000457 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
Yong Li18d77262018-10-09 01:59:45 +0800458 }
459 catch (const InternalFailure& e)
460 {
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000461 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800462 }
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000463
464 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
Chris Austen6caf28b2015-10-13 12:40:40 -0500465}
466
Chris Austen7303bdc2016-04-17 11:50:54 -0500467typedef struct
468{
469 char major;
470 char minor;
Chris Austen176c9652016-04-30 16:32:17 -0500471 uint16_t d[2];
Vernon Mauery86a50822019-03-25 13:11:36 -0700472} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500473
Potin Lai5d214202023-01-16 18:11:49 +0800474/* Use regular expression searching matched pattern X.Y, and convert it to */
475/* Major (X) and Minor (Y) version. */
476/* Example: */
477/* version = 2.14.0-dev */
478/* ^ ^ */
479/* | |---------------- Minor */
480/* |------------------ Major */
481/* */
482int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500483{
Potin Lai5d214202023-01-16 18:11:49 +0800484 std::regex fw_regex(FW_VER_REGEX);
485 std::smatch m;
486 Revision r = {0};
487 size_t val;
Chris Austen7303bdc2016-04-17 11:50:54 -0500488
Potin Lai5d214202023-01-16 18:11:49 +0800489 while (std::regex_search(s, m, fw_regex))
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600490 {
Potin Lai5d214202023-01-16 18:11:49 +0800491 // convert major
492 {
493 std::string_view str = m[1].str();
494 auto [ptr, ec]{std::from_chars(str.begin(), str.end(), val)};
495 if (ec != std::errc() || ptr != str.begin() + str.size())
496 { // failed to convert major string
497 continue;
498 }
499 r.major = val & 0x7F;
500 }
501
502 // convert minor
503 {
504 std::string_view str = m[2].str();
505 auto [ptr, ec]{std::from_chars(str.begin(), str.end(), val)};
506 if (ec != std::errc() || ptr != str.begin() + str.size())
507 { // failed to convert minor string
508 continue;
509 }
510 r.minor = val & 0xFF;
511 }
512
513 // all matched
514 rev = r;
515 return 0;
Chris Austen176c9652016-04-30 16:32:17 -0500516 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500517
Potin Lai5d214202023-01-16 18:11:49 +0800518 return -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500519}
520
Vernon Maueryea1c4012019-05-24 13:26:16 -0700521/* @brief: Implement the Get Device ID IPMI command per the IPMI spec
522 * @param[in] ctx - shared_ptr to an IPMI context struct
523 *
524 * @returns IPMI completion code plus response data
525 * - Device ID (manufacturer defined)
526 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
527 * - FW revision major[7 bits] (binary encoded); available[1 bit]
528 * - FW Revision minor (BCD encoded)
529 * - IPMI version (0x02 for IPMI 2.0)
530 * - device support (bitfield of supported options)
531 * - MFG IANA ID (3 bytes)
532 * - product ID (2 bytes)
533 * - AUX info (4 bytes)
534 */
535ipmi::RspType<uint8_t, // Device ID
536 uint8_t, // Device Revision
537 uint8_t, // Firmware Revision Major
538 uint8_t, // Firmware Revision minor
539 uint8_t, // IPMI version
540 uint8_t, // Additional device support
541 uint24_t, // MFG ID
542 uint16_t, // Product ID
543 uint32_t // AUX info
544 >
Willy Tu11d68892022-01-20 10:37:34 -0800545 ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx)
Chris Austen6caf28b2015-10-13 12:40:40 -0500546{
Vernon Mauery86a50822019-03-25 13:11:36 -0700547 static struct
548 {
549 uint8_t id;
550 uint8_t revision;
551 uint8_t fw[2];
552 uint8_t ipmiVer;
553 uint8_t addnDevSupport;
554 uint24_t manufId;
555 uint16_t prodId;
556 uint32_t aux;
557 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800558 static bool dev_id_initialized = false;
Patrick Venture94930a12019-04-30 10:01:58 -0700559 static bool defaultActivationSetting = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800560 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300561 constexpr auto ipmiDevIdStateShift = 7;
562 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Willy Tub78184e2022-10-27 22:57:38 +0000563
564#ifdef GET_DBUS_ACTIVE_SOFTWARE
565 static bool haveBMCVersion = false;
JeffLin27a62ec2021-11-24 15:40:33 +0800566 if (!haveBMCVersion || !dev_id_initialized)
David Cobbleya1adb072017-11-21 15:58:13 -0800567 {
Willy Tub78184e2022-10-27 22:57:38 +0000568 int r = -1;
569 Revision rev = {0, 0, 0, 0};
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600570 try
571 {
Vernon Maueryea1c4012019-05-24 13:26:16 -0700572 auto version = getActiveSoftwareVersionInfo(ctx);
Vernon Mauery86a50822019-03-25 13:11:36 -0700573 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800574 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600575 catch (const std::exception& e)
576 {
577 log<level::ERR>(e.what());
578 }
Nan Liee0cb902016-07-11 15:38:03 +0800579
Patrick Venture0b02be92018-08-31 11:55:55 -0700580 if (r >= 0)
581 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600582 // bit7 identifies if the device is available
583 // 0=normal operation
584 // 1=device firmware, SDR update,
585 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300586 // The availability may change in run time, so mask here
587 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700588 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600589
590 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700591 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
592 std::memcpy(&devId.aux, rev.d, 4);
Brandon Kimc1f5aca2022-03-17 17:54:06 -0700593 haveBMCVersion = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800594 }
JeffLin27a62ec2021-11-24 15:40:33 +0800595 }
Willy Tub78184e2022-10-27 22:57:38 +0000596#endif
JeffLin27a62ec2021-11-24 15:40:33 +0800597 if (!dev_id_initialized)
598 {
David Cobbleya1adb072017-11-21 15:58:13 -0800599 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700600 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500601
Vernon Mauery86a50822019-03-25 13:11:36 -0700602 std::ifstream devIdFile(filename);
603 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800604 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700605 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800606 if (!data.is_discarded())
607 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700608 devId.id = data.value("id", 0);
609 devId.revision = data.value("revision", 0);
610 devId.addnDevSupport = data.value("addn_dev_support", 0);
611 devId.manufId = data.value("manuf_id", 0);
612 devId.prodId = data.value("prod_id", 0);
613 devId.aux = data.value("aux", 0);
David Cobbleya1adb072017-11-21 15:58:13 -0800614
Willy Tubfd3a172022-05-31 13:57:54 -0700615 if (data.contains("firmware_revision"))
616 {
617 const auto& firmwareRevision = data.at("firmware_revision");
618 if (firmwareRevision.contains("major"))
619 {
620 firmwareRevision.at("major").get_to(devId.fw[0]);
621 }
622 if (firmwareRevision.contains("minor"))
623 {
624 firmwareRevision.at("minor").get_to(devId.fw[1]);
625 }
626 }
627
Patrick Venture94930a12019-04-30 10:01:58 -0700628 // Set the availablitity of the BMC.
629 defaultActivationSetting = data.value("availability", true);
630
Patrick Venture0b02be92018-08-31 11:55:55 -0700631 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800632 dev_id_initialized = true;
633 }
634 else
635 {
636 log<level::ERR>("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700637 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800638 }
639 }
640 else
641 {
642 log<level::ERR>("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700643 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500644 }
645 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500646
Alexander Amelkinba19c182018-09-04 15:49:36 +0300647 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700648 devId.fw[0] &= ipmiDevIdFw1Mask;
Patrick Venture94930a12019-04-30 10:01:58 -0700649 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
Alexander Amelkinba19c182018-09-04 15:49:36 +0300650 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700651 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300652 }
653
Vernon Mauery86a50822019-03-25 13:11:36 -0700654 return ipmi::responseSuccess(
655 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
656 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500657}
658
Vernon Maueryb84a5282019-03-25 13:39:03 -0700659auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800660{
Nan Li41fa24a2016-11-10 20:12:37 +0800661 // Byte 2:
662 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500663 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800664 // 57h - Corrupted or inaccesssible data or devices.
665 // 58h - Fatal hardware error.
666 // FFh - reserved.
667 // all other: Device-specific 'internal failure'.
668 // Byte 3:
669 // For byte 2 = 55h, 56h, FFh: 00h
670 // For byte 2 = 58h, all other: Device-specific
671 // For byte 2 = 57h: self-test error bitfield.
672 // Note: returning 57h does not imply that all test were run.
673 // [7] 1b = Cannot access SEL device.
674 // [6] 1b = Cannot access SDR Repository.
675 // [5] 1b = Cannot access BMC FRU device.
676 // [4] 1b = IPMB signal lines do not respond.
677 // [3] 1b = SDR Repository empty.
678 // [2] 1b = Internal Use Area of BMC FRU corrupted.
679 // [1] 1b = controller update 'boot block' firmware corrupted.
680 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700681 constexpr uint8_t notImplemented = 0x56;
682 constexpr uint8_t zero = 0;
683 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800684}
685
Vernon Mauery15541322019-03-25 13:33:03 -0700686static constexpr size_t uuidBinaryLength = 16;
687static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500688{
Vernon Mauery15541322019-03-25 13:33:03 -0700689 using Argument = xyz::openbmc_project::Common::InvalidArgument;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500690 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800691 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
692 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500693 // Ex: 0x2332fc2c40e66298e511f2782395a361
Vernon Mauery15541322019-03-25 13:33:03 -0700694 constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
695 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
696 std::array<uint8_t, uuidBinaryLength> uuid;
697 if (rfc4122.size() == uuidRfc4122Length)
Patrick Venture0b02be92018-08-31 11:55:55 -0700698 {
Vernon Mauery15541322019-03-25 13:33:03 -0700699 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
700 rfc4122.end());
Sergey Solomineb9b8142016-08-23 09:07:28 -0500701 }
Vernon Mauery15541322019-03-25 13:33:03 -0700702 if (rfc4122.size() != uuidHexLength)
vishwa1eaea4f2016-02-26 11:57:40 -0600703 {
Vernon Mauery15541322019-03-25 13:33:03 -0700704 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
705 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
vishwa1eaea4f2016-02-26 11:57:40 -0600706 }
Vernon Mauery15541322019-03-25 13:33:03 -0700707 for (size_t ind = 0; ind < uuidHexLength; ind += 2)
vishwa1eaea4f2016-02-26 11:57:40 -0600708 {
Vernon Mauery15541322019-03-25 13:33:03 -0700709 char v[3];
710 v[0] = rfc4122[ind];
711 v[1] = rfc4122[ind + 1];
712 v[2] = 0;
713 size_t err;
714 long b;
715 try
Emily Shafferedb8bb02018-09-27 14:50:15 -0700716 {
Vernon Mauery15541322019-03-25 13:33:03 -0700717 b = std::stoul(v, &err, 16);
Emily Shafferedb8bb02018-09-27 14:50:15 -0700718 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500719 catch (const std::exception& e)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500720 {
Vernon Mauery15541322019-03-25 13:33:03 -0700721 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
722 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500723 }
Vernon Mauery15541322019-03-25 13:33:03 -0700724 // check that exactly two ascii bytes were converted
725 if (err != 2)
726 {
727 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
728 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
729 }
730 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500731 }
Vernon Mauery15541322019-03-25 13:33:03 -0700732 return uuid;
733}
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500734
Vernon Mauery15541322019-03-25 13:33:03 -0700735auto ipmiAppGetDeviceGuid()
736 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
737{
738 // return a fixed GUID based on /etc/machine-id
739 // This should match the /redfish/v1/Managers/bmc's UUID data
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500740
Vernon Mauery15541322019-03-25 13:33:03 -0700741 // machine specific application ID (for BMC ID)
742 // generated by systemd-id128 -p new as per man page
743 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
744 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500745
Vernon Mauery15541322019-03-25 13:33:03 -0700746 sd_id128_t bmcUuid;
747 // create the UUID from /etc/machine-id via the systemd API
748 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500749
Vernon Mauery15541322019-03-25 13:33:03 -0700750 char bmcUuidCstr[SD_ID128_STRING_MAX];
751 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
752
753 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
754 return ipmi::responseSuccess(uuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500755}
Chris Austen6caf28b2015-10-13 12:40:40 -0500756
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700757auto ipmiAppGetBtCapabilities()
758 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530759{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600760 // Per IPMI 2.0 spec, the input and output buffer size must be the max
761 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700762 constexpr uint8_t nrOutstanding = 0x01;
763 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
764 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
765 constexpr uint8_t transactionTime = 0x0A;
766 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530767
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700768 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
769 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530770}
771
Vernon Maueryb90a5322019-03-25 13:36:55 -0700772auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>>
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600773{
Vernon Maueryb90a5322019-03-25 13:36:55 -0700774 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
775 static constexpr auto uuidProperty = "UUID";
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600776
Vernon Maueryb90a5322019-03-25 13:36:55 -0700777 ipmi::Value propValue;
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600778 try
779 {
780 // Get the Inventory object implementing BMC interface
Vernon Maueryb90a5322019-03-25 13:36:55 -0700781 auto busPtr = getSdBus();
Hieu Huynh45aed692022-10-12 10:58:26 +0000782 auto objectInfo = ipmi::getDbusObject(*busPtr, uuidInterface);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600783
784 // Read UUID property value from bmcObject
785 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Vernon Maueryb90a5322019-03-25 13:36:55 -0700786 propValue =
787 ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first,
788 uuidInterface, uuidProperty);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600789 }
790 catch (const InternalFailure& e)
791 {
792 log<level::ERR>("Failed in reading BMC UUID property",
Vernon Maueryb90a5322019-03-25 13:36:55 -0700793 entry("INTERFACE=%s", uuidInterface),
794 entry("PROPERTY=%s", uuidProperty));
795 return ipmi::responseUnspecifiedError();
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600796 }
Vernon Maueryb90a5322019-03-25 13:36:55 -0700797 std::array<uint8_t, 16> uuid;
798 std::string rfc4122Uuid = std::get<std::string>(propValue);
799 try
800 {
801 // convert to IPMI format
802 uuid = rfc4122ToIpmi(rfc4122Uuid);
803 }
804 catch (const InvalidArgument& e)
805 {
806 log<level::ERR>("Failed in parsing BMC UUID property",
807 entry("INTERFACE=%s", uuidInterface),
808 entry("PROPERTY=%s", uuidProperty),
809 entry("VALUE=%s", rfc4122Uuid.c_str()));
810 return ipmi::responseUnspecifiedError();
811 }
812 return ipmi::responseSuccess(uuid);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600813}
814
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000815/**
816 * @brief set the session state as teardown
817 *
818 * This function is to set the session state to tear down in progress if the
819 * state is active.
820 *
821 * @param[in] busp - Dbus obj
822 * @param[in] service - service name
823 * @param[in] obj - object path
824 *
825 * @return success completion code if it sets the session state to
826 * tearDownInProgress else return the corresponding error completion code.
827 **/
828uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
829 const std::string& service, const std::string& obj)
830{
831 try
832 {
833 uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
834 *busp, service, obj, session::sessionIntf, "State"));
835
836 if (sessionState == static_cast<uint8_t>(session::State::active))
837 {
838 ipmi::setDbusProperty(
839 *busp, service, obj, session::sessionIntf, "State",
840 static_cast<uint8_t>(session::State::tearDownInProgress));
841 return ipmi::ccSuccess;
842 }
843 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500844 catch (const std::exception& e)
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000845 {
846 log<level::ERR>("Failed in getting session state property",
847 entry("service=%s", service.c_str()),
848 entry("object path=%s", obj.c_str()),
849 entry("interface=%s", session::sessionIntf));
850 return ipmi::ccUnspecifiedError;
851 }
852
853 return ipmi::ccInvalidFieldRequest;
854}
855
856ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
857 std::optional<uint8_t> requestSessionHandle)
858{
859 auto busp = getSdBus();
860 uint8_t reqSessionHandle =
861 requestSessionHandle.value_or(session::defaultSessionHandle);
862
863 if (reqSessionId == session::sessionZero &&
864 reqSessionHandle == session::defaultSessionHandle)
865 {
866 return ipmi::response(session::ccInvalidSessionId);
867 }
868
869 if (reqSessionId == session::sessionZero &&
870 reqSessionHandle == session::invalidSessionHandle)
871 {
872 return ipmi::response(session::ccInvalidSessionHandle);
873 }
874
875 if (reqSessionId != session::sessionZero &&
876 reqSessionHandle != session::defaultSessionHandle)
877 {
878 return ipmi::response(ipmi::ccInvalidFieldRequest);
879 }
880
881 try
882 {
883 ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
884 *busp, session::sessionManagerRootPath, session::sessionIntf);
885
886 for (auto& objectTreeItr : objectTree)
887 {
888 const std::string obj = objectTreeItr.first;
889
890 if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
891 {
892 auto& serviceMap = objectTreeItr.second;
893
894 // Session id and session handle are unique for each session.
895 // Session id and handler are retrived from the object path and
896 // object path will be unique for each session. Checking if
897 // multiple objects exist with same object path under multiple
898 // services.
899 if (serviceMap.size() != 1)
900 {
901 return ipmi::responseUnspecifiedError();
902 }
903
904 auto itr = serviceMap.begin();
905 const std::string service = itr->first;
906 return ipmi::response(setSessionState(busp, service, obj));
907 }
908 }
909 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500910 catch (const sdbusplus::exception_t& e)
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000911 {
912 log<level::ERR>("Failed to fetch object from dbus",
913 entry("INTERFACE=%s", session::sessionIntf),
914 entry("ERRMSG=%s", e.what()));
915 return ipmi::responseUnspecifiedError();
916 }
917
918 return ipmi::responseInvalidFieldRequest();
919}
920
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000921uint8_t getTotalSessionCount()
922{
Meera-Kattac1789482021-05-18 09:53:26 +0000923 uint8_t count = 0, ch = 0;
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000924
925 while (ch < ipmi::maxIpmiChannels &&
926 count < session::maxNetworkInstanceSupported)
927 {
Jayaprakash Mutyala80207e62020-07-04 16:34:15 +0000928 ipmi::ChannelInfo chInfo{};
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000929 ipmi::getChannelInfo(ch, chInfo);
930 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
931 ipmi::EChannelMediumType::lan8032)
932 {
933 count++;
934 }
935 ch++;
936 }
937 return count * session::maxSessionCountPerChannel;
938}
939
940/**
941 * @brief get session info request data.
942 *
943 * This function validates the request data and retrive request session id,
944 * session handle.
945 *
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +0530946 * @param[in] ctx - context of current session.
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000947 * @param[in] sessionIndex - request session index
948 * @param[in] payload - input payload
949 * @param[in] reqSessionId - unpacked session Id will be asigned
950 * @param[in] reqSessionHandle - unpacked session handle will be asigned
951 *
952 * @return success completion code if request data is valid
953 * else return the correcponding error completion code.
954 **/
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +0530955uint8_t getSessionInfoRequestData(const ipmi::Context::ptr ctx,
956 const uint8_t sessionIndex,
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000957 ipmi::message::Payload& payload,
958 uint32_t& reqSessionId,
959 uint8_t& reqSessionHandle)
960{
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +0530961 if ((sessionIndex > session::maxSessionCountPerChannel) &&
962 (sessionIndex < session::searchSessionByHandle))
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000963 {
964 return ipmi::ccInvalidFieldRequest;
965 }
966
967 switch (sessionIndex)
968 {
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +0530969 case session::searchCurrentSession:
970
971 ipmi::ChannelInfo chInfo;
972 ipmi::getChannelInfo(ctx->channel, chInfo);
973
974 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) !=
975 ipmi::EChannelMediumType::lan8032)
976 {
977 return ipmi::ccInvalidFieldRequest;
978 }
979
980 if (!payload.fullyUnpacked())
981 {
982 return ipmi::ccReqDataLenInvalid;
983 }
984 // Check if current sessionId is 0, sessionId 0 is reserved.
985 if (ctx->sessionId == session::sessionZero)
986 {
987 return session::ccInvalidSessionId;
988 }
989 reqSessionId = ctx->sessionId;
990 break;
991
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000992 case session::searchSessionByHandle:
993
994 if ((payload.unpack(reqSessionHandle)) ||
995 (!payload.fullyUnpacked()))
996 {
997 return ipmi::ccReqDataLenInvalid;
998 }
999
1000 if ((reqSessionHandle == session::sessionZero) ||
1001 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
1002 session::maxSessionCountPerChannel))
1003 {
1004 return session::ccInvalidSessionHandle;
1005 }
1006 break;
1007
1008 case session::searchSessionById:
1009
1010 if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
1011 {
1012 return ipmi::ccReqDataLenInvalid;
1013 }
1014
1015 if (reqSessionId == session::sessionZero)
1016 {
1017 return session::ccInvalidSessionId;
1018 }
1019 break;
1020
1021 default:
1022 if (!payload.fullyUnpacked())
1023 {
1024 return ipmi::ccReqDataLenInvalid;
1025 }
1026 break;
1027 }
1028 return ipmi::ccSuccess;
1029}
1030
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001031uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service,
1032 const std::string& objPath, uint8_t& sessionState)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001033{
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001034 boost::system::error_code ec = ipmi::getDbusProperty(
1035 ctx, service, objPath, session::sessionIntf, "State", sessionState);
1036 if (ec)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001037 {
1038 log<level::ERR>("Failed to fetch state property ",
1039 entry("SERVICE=%s", service.c_str()),
1040 entry("OBJECTPATH=%s", objPath.c_str()),
1041 entry("INTERFACE=%s", session::sessionIntf),
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001042 entry("ERRMSG=%s", ec.message().c_str()));
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001043 return ipmi::ccUnspecifiedError;
1044 }
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001045 return ipmi::ccSuccess;
1046}
1047
1048static constexpr uint8_t macAddrLen = 6;
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001049/** Alias SessionDetails - contain the optional information about an
1050 * RMCP+ session.
1051 *
1052 * @param userID - uint6_t session user ID (0-63)
1053 * @param reserved - uint2_t reserved
1054 * @param privilege - uint4_t session privilege (0-5)
1055 * @param reserved - uint4_t reserved
1056 * @param channel - uint4_t session channel number
1057 * @param protocol - uint4_t session protocol
1058 * @param remoteIP - uint32_t remote IP address
1059 * @param macAddr - std::array<uint8_t, 6> mac address
1060 * @param port - uint16_t remote port
1061 */
1062using SessionDetails =
1063 std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t,
1064 std::array<uint8_t, macAddrLen>, uint16_t>;
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001065
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001066/** @brief get session details for a given session
1067 *
1068 * @param[in] ctx - ipmi::Context pointer for accessing D-Bus
1069 * @param[in] service - D-Bus service name to fetch details from
1070 * @param[in] objPath - D-Bus object path for session
1071 * @param[out] sessionHandle - return session handle for session
1072 * @param[out] sessionState - return session state for session
1073 * @param[out] details - return a SessionDetails tuple containing other
1074 * session info
1075 * @return - ipmi::Cc success or error code
1076 */
1077ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service,
1078 const std::string& objPath, uint8_t& sessionHandle,
1079 uint8_t& sessionState, SessionDetails& details)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001080{
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001081 ipmi::PropertyMap sessionProps;
1082 boost::system::error_code ec = ipmi::getAllDbusProperties(
1083 ctx, service, objPath, session::sessionIntf, sessionProps);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001084
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001085 if (ec)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001086 {
1087 log<level::ERR>("Failed to fetch state property ",
1088 entry("SERVICE=%s", service.c_str()),
1089 entry("OBJECTPATH=%s", objPath.c_str()),
1090 entry("INTERFACE=%s", session::sessionIntf),
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001091 entry("ERRMSG=%s", ec.message().c_str()));
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001092 return ipmi::ccUnspecifiedError;
1093 }
1094
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001095 sessionState = ipmi::mappedVariant<uint8_t>(
1096 sessionProps, "State", static_cast<uint8_t>(session::State::inactive));
1097 if (sessionState == static_cast<uint8_t>(session::State::active))
1098 {
1099 sessionHandle =
1100 ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0);
1101 std::get<0>(details) =
1102 ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff);
1103 // std::get<1>(details) = 0; // (default constructed to 0)
1104 std::get<2>(details) =
1105 ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0);
1106 // std::get<3>(details) = 0; // (default constructed to 0)
1107 std::get<4>(details) =
1108 ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff);
1109 constexpr uint4_t rmcpPlusProtocol = 1;
1110 std::get<5>(details) = rmcpPlusProtocol;
1111 std::get<6>(details) =
1112 ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0);
1113 // std::get<7>(details) = {{0}}; // default constructed to all 0
1114 std::get<8>(details) =
1115 ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0);
1116 }
1117
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001118 return ipmi::ccSuccess;
1119}
1120
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001121ipmi::RspType<uint8_t, // session handle,
1122 uint8_t, // total session count
1123 uint8_t, // active session count
1124 std::optional<SessionDetails>>
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301125 ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex,
1126 ipmi::message::Payload& payload)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001127{
1128 uint32_t reqSessionId = 0;
1129 uint8_t reqSessionHandle = session::defaultSessionHandle;
1130 // initializing state to 0xff as 0 represents state as inactive.
1131 uint8_t state = 0xFF;
1132
1133 uint8_t completionCode = getSessionInfoRequestData(
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301134 ctx, sessionIndex, payload, reqSessionId, reqSessionHandle);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001135
1136 if (completionCode)
1137 {
1138 return ipmi::response(completionCode);
1139 }
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001140 ipmi::ObjectTree objectTree;
1141 boost::system::error_code ec = ipmi::getAllDbusObjects(
1142 ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree);
1143 if (ec)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001144 {
1145 log<level::ERR>("Failed to fetch object from dbus",
1146 entry("INTERFACE=%s", session::sessionIntf),
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001147 entry("ERRMSG=%s", ec.message().c_str()));
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001148 return ipmi::responseUnspecifiedError();
1149 }
1150
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001151 uint8_t totalSessionCount = getTotalSessionCount();
1152 uint8_t activeSessionCount = 0;
1153 uint8_t sessionHandle = session::defaultSessionHandle;
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001154 uint8_t activeSessionHandle = 0;
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001155 std::optional<SessionDetails> maybeDetails;
1156 uint8_t index = 0;
1157 for (auto& objectTreeItr : objectTree)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001158 {
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001159 uint32_t sessionId = 0;
1160 std::string objectPath = objectTreeItr.first;
1161
1162 if (!parseCloseSessionInputPayload(objectPath, sessionId,
1163 sessionHandle))
1164 {
1165 continue;
1166 }
1167 index++;
1168 auto& serviceMap = objectTreeItr.second;
1169 auto itr = serviceMap.begin();
1170
1171 if (serviceMap.size() != 1)
1172 {
1173 return ipmi::responseUnspecifiedError();
1174 }
1175
1176 std::string service = itr->first;
1177 uint8_t sessionState = 0;
1178 completionCode =
1179 getSessionState(ctx, service, objectPath, sessionState);
1180 if (completionCode)
1181 {
1182 return ipmi::response(completionCode);
1183 }
1184
1185 if (sessionState == static_cast<uint8_t>(session::State::active))
1186 {
1187 activeSessionCount++;
1188 }
1189
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001190 if (index == sessionIndex || reqSessionId == sessionId ||
1191 reqSessionHandle == sessionHandle)
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001192 {
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001193 SessionDetails details{};
1194 completionCode = getSessionDetails(ctx, service, objectPath,
1195 sessionHandle, state, details);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001196
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001197 if (completionCode)
1198 {
1199 return ipmi::response(completionCode);
1200 }
1201 activeSessionHandle = sessionHandle;
1202 maybeDetails = std::move(details);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001203 }
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001204 }
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001205
1206 if (state == static_cast<uint8_t>(session::State::active) ||
1207 state == static_cast<uint8_t>(session::State::tearDownInProgress))
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001208 {
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001209 return ipmi::responseSuccess(activeSessionHandle, totalSessionCount,
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001210 activeSessionCount, maybeDetails);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001211 }
1212
1213 return ipmi::responseInvalidFieldRequest();
1214}
1215
Xo Wangf542e8b2017-08-09 15:34:16 -07001216static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
1217
Xo Wang87651332017-08-11 10:17:59 -07001218static std::string sysInfoReadSystemName()
1219{
1220 // Use the BMC hostname as the "System Name."
1221 char hostname[HOST_NAME_MAX + 1] = {};
1222 if (gethostname(hostname, HOST_NAME_MAX) != 0)
1223 {
1224 perror("System info parameter: system name");
1225 }
1226 return hostname;
1227}
1228
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001229static constexpr uint8_t paramRevision = 0x11;
1230static constexpr size_t configParameterLength = 16;
Xo Wangf542e8b2017-08-09 15:34:16 -07001231
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001232static constexpr size_t smallChunkSize = 14;
1233static constexpr size_t fullChunkSize = 16;
Jia, chunhui449f2162019-09-11 16:51:51 +08001234static constexpr uint8_t progressMask = 0x3;
Xo Wangf542e8b2017-08-09 15:34:16 -07001235
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001236static constexpr uint8_t setComplete = 0x0;
1237static constexpr uint8_t setInProgress = 0x1;
1238static constexpr uint8_t commitWrite = 0x2;
1239static uint8_t transferStatus = setComplete;
1240
Jia, chunhui449f2162019-09-11 16:51:51 +08001241static constexpr uint8_t configDataOverhead = 2;
1242
1243// For EFI based system, 256 bytes is recommended.
1244static constexpr size_t maxBytesPerParameter = 256;
1245
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001246namespace ipmi
1247{
1248constexpr Cc ccParmNotSupported = 0x80;
Jia, chunhui449f2162019-09-11 16:51:51 +08001249constexpr Cc ccSetInProgressActive = 0x81;
1250constexpr Cc ccSystemInfoParameterSetReadOnly = 0x82;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001251
1252static inline auto responseParmNotSupported()
1253{
1254 return response(ccParmNotSupported);
Xo Wangf542e8b2017-08-09 15:34:16 -07001255}
Jia, chunhui449f2162019-09-11 16:51:51 +08001256static inline auto responseSetInProgressActive()
1257{
1258 return response(ccSetInProgressActive);
1259}
1260static inline auto responseSystemInfoParameterSetReadOnly()
1261{
1262 return response(ccSystemInfoParameterSetReadOnly);
1263}
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001264} // namespace ipmi
Xo Wangf542e8b2017-08-09 15:34:16 -07001265
Jia, chunhui449f2162019-09-11 16:51:51 +08001266ipmi::RspType<uint8_t, // Parameter revision
1267 std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
1268 std::optional<std::vector<uint8_t>>> // data2-17
jayaprakash Mutyalac2566a92020-04-23 21:18:35 +00001269 ipmiAppGetSystemInfo(uint7_t reserved, bool getRevision,
1270 uint8_t paramSelector, uint8_t setSelector,
1271 uint8_t BlockSelector)
Xo Wangf542e8b2017-08-09 15:34:16 -07001272{
Snehalatha Va5ae7722020-05-02 18:18:57 +00001273 if (reserved || (paramSelector >= invalidParamSelectorStart &&
1274 paramSelector <= invalidParamSelectorEnd))
jayaprakash Mutyalac2566a92020-04-23 21:18:35 +00001275 {
1276 return ipmi::responseInvalidFieldRequest();
1277 }
Snehalatha Va5ae7722020-05-02 18:18:57 +00001278 if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd))
1279 {
1280 return ipmi::responseParmNotSupported();
1281 }
jayaprakash Mutyalac2566a92020-04-23 21:18:35 +00001282 if (getRevision)
Xo Wangf542e8b2017-08-09 15:34:16 -07001283 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001284 return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
Xo Wangf542e8b2017-08-09 15:34:16 -07001285 }
1286
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001287 if (paramSelector == 0)
Xo Wangf542e8b2017-08-09 15:34:16 -07001288 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001289 return ipmi::responseSuccess(paramRevision, transferStatus,
1290 std::nullopt);
Xo Wangf542e8b2017-08-09 15:34:16 -07001291 }
1292
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001293 if (BlockSelector != 0) // 00h if parameter does not require a block number
Xo Wangf542e8b2017-08-09 15:34:16 -07001294 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001295 return ipmi::responseParmNotSupported();
Xo Wangf542e8b2017-08-09 15:34:16 -07001296 }
1297
1298 if (sysInfoParamStore == nullptr)
1299 {
1300 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001301 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1302 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001303 }
1304
1305 // Parameters other than Set In Progress are assumed to be strings.
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001306 std::tuple<bool, std::string> ret =
1307 sysInfoParamStore->lookup(paramSelector);
1308 bool found = std::get<0>(ret);
Xo Wangf542e8b2017-08-09 15:34:16 -07001309 if (!found)
1310 {
Snehalatha Va5ae7722020-05-02 18:18:57 +00001311 return ipmi::responseSensorInvalid();
Xo Wangf542e8b2017-08-09 15:34:16 -07001312 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001313 std::string& paramString = std::get<1>(ret);
Jia, chunhui449f2162019-09-11 16:51:51 +08001314 std::vector<uint8_t> configData;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001315 size_t count = 0;
1316 if (setSelector == 0)
Jia, chunhui449f2162019-09-11 16:51:51 +08001317 { // First chunk has only 14 bytes.
1318 configData.emplace_back(0); // encoding
1319 configData.emplace_back(paramString.length()); // string length
1320 count = std::min(paramString.length(), smallChunkSize);
1321 configData.resize(count + configDataOverhead);
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001322 std::copy_n(paramString.begin(), count,
Snehalatha Va5ae7722020-05-02 18:18:57 +00001323 configData.begin() + configDataOverhead); // 14 bytes chunk
1324
1325 // Append zero's to remaining bytes
1326 if (configData.size() < configParameterLength)
1327 {
1328 std::fill_n(std::back_inserter(configData),
1329 configParameterLength - configData.size(), 0x00);
1330 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001331 }
1332 else
Xo Wangf542e8b2017-08-09 15:34:16 -07001333 {
Jia, chunhui449f2162019-09-11 16:51:51 +08001334 size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001335 if (offset >= paramString.length())
1336 {
1337 return ipmi::responseParmOutOfRange();
1338 }
Jia, chunhui449f2162019-09-11 16:51:51 +08001339 count = std::min(paramString.length() - offset, fullChunkSize);
1340 configData.resize(count);
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001341 std::copy_n(paramString.begin() + offset, count,
1342 configData.begin()); // 16 bytes chunk
Xo Wangf542e8b2017-08-09 15:34:16 -07001343 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001344 return ipmi::responseSuccess(paramRevision, setSelector, configData);
Xo Wangf542e8b2017-08-09 15:34:16 -07001345}
1346
Jia, chunhui449f2162019-09-11 16:51:51 +08001347ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1,
1348 std::vector<uint8_t> configData)
1349{
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +00001350 if (paramSelector >= invalidParamSelectorStart &&
1351 paramSelector <= invalidParamSelectorEnd)
1352 {
1353 return ipmi::responseInvalidFieldRequest();
1354 }
1355 if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd))
1356 {
1357 return ipmi::responseParmNotSupported();
1358 }
1359
Jia, chunhui449f2162019-09-11 16:51:51 +08001360 if (paramSelector == 0)
1361 {
1362 // attempt to set the 'set in progress' value (in parameter #0)
1363 // when not in the set complete state.
1364 if ((transferStatus != setComplete) && (data1 == setInProgress))
1365 {
1366 return ipmi::responseSetInProgressActive();
1367 }
1368 // only following 2 states are supported
1369 if (data1 > setInProgress)
1370 {
1371 phosphor::logging::log<phosphor::logging::level::ERR>(
1372 "illegal SetInProgress status");
1373 return ipmi::responseInvalidFieldRequest();
1374 }
1375
1376 transferStatus = data1 & progressMask;
1377 return ipmi::responseSuccess();
1378 }
1379
1380 if (configData.size() > configParameterLength)
1381 {
1382 return ipmi::responseInvalidFieldRequest();
1383 }
1384
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +00001385 // Append zero's to remaining bytes
1386 if (configData.size() < configParameterLength)
1387 {
1388 fill_n(back_inserter(configData),
1389 (configParameterLength - configData.size()), 0x00);
1390 }
1391
Jia, chunhui449f2162019-09-11 16:51:51 +08001392 if (!sysInfoParamStore)
1393 {
1394 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1395 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1396 sysInfoReadSystemName);
1397 }
1398
1399 // lookup
1400 std::tuple<bool, std::string> ret =
1401 sysInfoParamStore->lookup(paramSelector);
1402 bool found = std::get<0>(ret);
1403 std::string& paramString = std::get<1>(ret);
1404 if (!found)
1405 {
1406 // parameter does not exist. Init new
1407 paramString = "";
1408 }
1409
1410 uint8_t setSelector = data1;
1411 size_t count = 0;
1412 if (setSelector == 0) // First chunk has only 14 bytes.
1413 {
1414 size_t stringLen = configData.at(1); // string length
1415 // maxBytesPerParamter is 256. It will always be greater than stringLen
1416 // (unit8_t) if maxBytes changes in future, then following line is
1417 // needed.
1418 // stringLen = std::min(stringLen, maxBytesPerParameter);
1419 count = std::min(stringLen, smallChunkSize);
1420 count = std::min(count, configData.size());
1421 paramString.resize(stringLen); // reserve space
1422 std::copy_n(configData.begin() + configDataOverhead, count,
1423 paramString.begin());
1424 }
1425 else
1426 {
1427 size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1428 if (offset >= paramString.length())
1429 {
1430 return ipmi::responseParmOutOfRange();
1431 }
1432 count = std::min(paramString.length() - offset, configData.size());
1433 std::copy_n(configData.begin(), count, paramString.begin() + offset);
1434 }
1435 sysInfoParamStore->update(paramSelector, paramString);
1436 return ipmi::responseSuccess();
1437}
1438
Yong Libd0503a2019-08-22 17:17:17 +08001439#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301440inline std::vector<uint8_t> convertStringToData(const std::string& command)
1441{
1442 std::istringstream iss(command);
1443 std::string token;
1444 std::vector<uint8_t> dataValue;
1445 while (std::getline(iss, token, ' '))
1446 {
1447 dataValue.emplace_back(
1448 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1449 }
1450 return dataValue;
1451}
1452
1453static bool populateI2CMasterWRWhitelist()
1454{
1455 nlohmann::json data = nullptr;
1456 std::ifstream jsonFile(i2cMasterWRWhitelistFile);
1457
1458 if (!jsonFile.good())
1459 {
1460 log<level::WARNING>("i2c white list file not found!",
1461 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1462 return false;
1463 }
1464
1465 try
1466 {
1467 data = nlohmann::json::parse(jsonFile, nullptr, false);
1468 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001469 catch (const nlohmann::json::parse_error& e)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301470 {
1471 log<level::ERR>("Corrupted i2c white list config file",
1472 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile),
1473 entry("MSG: %s", e.what()));
1474 return false;
1475 }
1476
1477 try
1478 {
1479 // Example JSON Structure format
1480 // "filters": [
1481 // {
1482 // "Description": "Allow full read - ignore first byte write value
1483 // for 0x40 to 0x4F",
1484 // "busId": "0x01",
1485 // "slaveAddr": "0x40",
1486 // "slaveAddrMask": "0x0F",
1487 // "command": "0x00",
1488 // "commandMask": "0xFF"
1489 // },
1490 // {
1491 // "Description": "Allow full read - first byte match 0x05 and
1492 // ignore second byte",
1493 // "busId": "0x01",
1494 // "slaveAddr": "0x57",
1495 // "slaveAddrMask": "0x00",
1496 // "command": "0x05 0x00",
1497 // "commandMask": "0x00 0xFF"
1498 // },]
1499
1500 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
1501 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist();
1502 for (const auto& it : filters.items())
1503 {
1504 nlohmann::json filter = it.value();
1505 if (filter.is_null())
1506 {
1507 log<level::ERR>(
1508 "Corrupted I2C master write read whitelist config file",
1509 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile));
1510 return false;
1511 }
1512 const std::vector<uint8_t>& writeData =
1513 convertStringToData(filter[cmdStr].get<std::string>());
1514 const std::vector<uint8_t>& writeDataMask =
1515 convertStringToData(filter[cmdMaskStr].get<std::string>());
1516 if (writeDataMask.size() != writeData.size())
1517 {
1518 log<level::ERR>("I2C master write read whitelist filter "
1519 "mismatch for command & mask size");
1520 return false;
1521 }
1522 whitelist.push_back(
1523 {static_cast<uint8_t>(std::stoul(
1524 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1525 static_cast<uint8_t>(
1526 std::stoul(filter[slaveAddrStr].get<std::string>(),
1527 nullptr, base_16)),
1528 static_cast<uint8_t>(
1529 std::stoul(filter[slaveAddrMaskStr].get<std::string>(),
1530 nullptr, base_16)),
1531 writeData, writeDataMask});
1532 }
1533 if (whitelist.size() != filters.size())
1534 {
1535 log<level::ERR>(
1536 "I2C master write read whitelist filter size mismatch");
1537 return false;
1538 }
1539 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001540 catch (const std::exception& e)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301541 {
1542 log<level::ERR>("I2C master write read whitelist unexpected exception",
1543 entry("ERROR=%s", e.what()));
1544 return false;
1545 }
1546 return true;
1547}
1548
1549static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data,
1550 const std::vector<uint8_t>& dataMask,
1551 const std::vector<uint8_t>& writeData)
1552{
1553 std::vector<uint8_t> processedDataBuf(data.size());
1554 std::vector<uint8_t> processedReqBuf(dataMask.size());
1555 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1556 processedReqBuf.begin(), std::bit_or<uint8_t>());
1557 std::transform(data.begin(), data.end(), dataMask.begin(),
1558 processedDataBuf.begin(), std::bit_or<uint8_t>());
1559
1560 return (processedDataBuf == processedReqBuf);
1561}
1562
1563static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr,
1564 std::vector<uint8_t>& writeData)
1565{
1566 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist();
1567 for (const auto& wlEntry : whiteList)
1568 {
1569 if ((busId == wlEntry.busId) &&
1570 ((slaveAddr | wlEntry.slaveAddrMask) ==
1571 (wlEntry.slaveAddr | wlEntry.slaveAddrMask)))
1572 {
1573 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1574 // Skip as no-match, if requested write data is more than the
1575 // write data mask size
1576 if (writeData.size() > dataMask.size())
1577 {
1578 continue;
1579 }
1580 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData))
1581 {
1582 return true;
1583 }
1584 }
1585 }
1586 return false;
1587}
Yong Libd0503a2019-08-22 17:17:17 +08001588#else
1589static bool populateI2CMasterWRWhitelist()
1590{
1591 log<level::INFO>(
1592 "I2C_WHITELIST_CHECK is disabled, do not populate whitelist");
1593 return true;
1594}
1595#endif // ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301596
1597/** @brief implements master write read IPMI command which can be used for
1598 * low-level I2C/SMBus write, read or write-read access
1599 * @param isPrivateBus -to indicate private bus usage
1600 * @param busId - bus id
1601 * @param channelNum - channel number
1602 * @param reserved - skip 1 bit
1603 * @param slaveAddr - slave address
1604 * @param read count - number of bytes to be read
1605 * @param writeData - data to be written
1606 *
1607 * @returns IPMI completion code plus response data
1608 * - readData - i2c response data
1609 */
1610ipmi::RspType<std::vector<uint8_t>>
Willy Tu11d68892022-01-20 10:37:34 -08001611 ipmiMasterWriteRead([[maybe_unused]] bool isPrivateBus, uint3_t busId,
1612 [[maybe_unused]] uint4_t channelNum, bool reserved,
1613 uint7_t slaveAddr, uint8_t readCount,
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301614 std::vector<uint8_t> writeData)
1615{
Jayaprakash Mutyalac2af98b2021-09-14 09:19:11 +00001616 if (reserved)
1617 {
1618 return ipmi::responseInvalidFieldRequest();
1619 }
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301620 if (readCount > maxIPMIWriteReadSize)
1621 {
1622 log<level::ERR>("Master write read command: Read count exceeds limit");
1623 return ipmi::responseParmOutOfRange();
1624 }
1625 const size_t writeCount = writeData.size();
1626 if (!readCount && !writeCount)
1627 {
1628 log<level::ERR>("Master write read command: Read & write count are 0");
1629 return ipmi::responseInvalidFieldRequest();
1630 }
Yong Libd0503a2019-08-22 17:17:17 +08001631#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301632 if (!isCmdWhitelisted(static_cast<uint8_t>(busId),
1633 static_cast<uint8_t>(slaveAddr), writeData))
1634 {
1635 log<level::ERR>("Master write read request blocked!",
1636 entry("BUS=%d", static_cast<uint8_t>(busId)),
1637 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr)));
1638 return ipmi::responseInvalidFieldRequest();
1639 }
Yong Libd0503a2019-08-22 17:17:17 +08001640#endif // ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301641 std::vector<uint8_t> readBuf(readCount);
1642 std::string i2cBus =
1643 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
1644
Yong Li7dc4ac02019-08-23 17:44:32 +08001645 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr),
1646 writeData, readBuf);
1647 if (ret != ipmi::ccSuccess)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301648 {
Yong Li7dc4ac02019-08-23 17:44:32 +08001649 return ipmi::response(ret);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301650 }
1651 return ipmi::responseSuccess(readBuf);
1652}
1653
Chris Austen6caf28b2015-10-13 12:40:40 -05001654void register_netfn_app_functions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301655{
Vernon Mauery86a50822019-03-25 13:11:36 -07001656 // <Get Device ID>
1657 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1658 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1659 ipmiAppGetDeviceId);
1660
Tom05732372016-09-06 17:21:23 +05301661 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001662 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1663 ipmi::app::cmdGetBtIfaceCapabilities,
1664 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001665
Tom05732372016-09-06 17:21:23 +05301666 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001667 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1668 ipmi::app::cmdResetWatchdogTimer,
1669 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001670
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001671 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
AppaRao Puli5a98ea62019-11-10 21:15:02 +05301672 ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User,
1673 ipmiAppGetSessionInfo);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001674
Tom05732372016-09-06 17:21:23 +05301675 // <Set Watchdog Timer>
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +00001676 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1677 ipmi::app::cmdSetWatchdogTimer,
1678 ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001679
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +00001680 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1681 ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
1682 ipmiAppCloseSession);
1683
William A. Kennington III73f44512018-02-09 15:28:46 -08001684 // <Get Watchdog Timer>
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +00001685 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
AppaRao Puli5a98ea62019-11-10 21:15:02 +05301686 ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User,
1687 ipmiGetWatchdogTimer);
William A. Kennington III73f44512018-02-09 15:28:46 -08001688
Tom05732372016-09-06 17:21:23 +05301689 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001690 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1691 ipmi::app::cmdGetSelfTestResults,
1692 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001693
Tom05732372016-09-06 17:21:23 +05301694 // <Get Device GUID>
Vernon Mauery15541322019-03-25 13:33:03 -07001695 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1696 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1697 ipmiAppGetDeviceGuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001698
Tom05732372016-09-06 17:21:23 +05301699 // <Set ACPI Power State>
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +00001700 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1701 ipmi::app::cmdSetAcpiPowerState,
1702 ipmi::Privilege::Admin, ipmiSetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001703 // <Get ACPI Power State>
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +00001704 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1705 ipmi::app::cmdGetAcpiPowerState,
AppaRao Puli5a98ea62019-11-10 21:15:02 +05301706 ipmi::Privilege::User, ipmiGetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001707
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301708 // Note: For security reason, this command will be registered only when
1709 // there are proper I2C Master write read whitelist
1710 if (populateI2CMasterWRWhitelist())
1711 {
1712 // Note: For security reasons, registering master write read as admin
1713 // privilege command, even though IPMI 2.0 specification allows it as
1714 // operator privilege.
1715 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1716 ipmi::app::cmdMasterWriteRead,
1717 ipmi::Privilege::Admin, ipmiMasterWriteRead);
1718 }
1719
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001720 // <Get System GUID Command>
Vernon Maueryb90a5322019-03-25 13:36:55 -07001721 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1722 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1723 ipmiAppGetSystemGuid);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301724
1725 // <Get Channel Cipher Suites Command>
Ayushi Smriti5c3b72c2019-08-30 13:47:31 +00001726 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1727 ipmi::app::cmdGetChannelCipherSuites,
Vernon Mauery79b4eea2019-11-07 09:51:39 -08001728 ipmi::Privilege::None, getChannelCipherSuites);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001729
Xo Wangf542e8b2017-08-09 15:34:16 -07001730 // <Get System Info Command>
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001731 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1732 ipmi::app::cmdGetSystemInfoParameters,
1733 ipmi::Privilege::User, ipmiAppGetSystemInfo);
Jia, chunhui449f2162019-09-11 16:51:51 +08001734 // <Set System Info Command>
1735 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1736 ipmi::app::cmdSetSystemInfoParameters,
1737 ipmi::Privilege::Admin, ipmiAppSetSystemInfo);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301738 return;
1739}