blob: 014355cc68a296f13b07c79700ca1a35ac629b82 [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>
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05308#include <sys/ioctl.h>
9#include <sys/stat.h>
10#include <sys/types.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070011#include <systemd/sd-bus.h>
Xo Wang87651332017-08-11 10:17:59 -070012#include <unistd.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070013
Vernon Mauery0120b682019-03-25 13:08:54 -070014#include <app/channel.hpp>
15#include <app/watchdog.hpp>
16#include <apphandler.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050017#include <ipmid/api.hpp>
18#include <ipmid/sessiondef.hpp>
19#include <ipmid/sessionhelper.hpp>
20#include <ipmid/types.hpp>
21#include <ipmid/utils.hpp>
22#include <nlohmann/json.hpp>
23#include <phosphor-logging/elog-errors.hpp>
George Liu24fffdc2024-07-17 17:40:53 +080024#include <phosphor-logging/lg2.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050025#include <sdbusplus/message/types.hpp>
26#include <sys_info_param.hpp>
27#include <xyz/openbmc_project/Common/error.hpp>
28#include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp>
29#include <xyz/openbmc_project/Software/Activation/server.hpp>
30#include <xyz/openbmc_project/Software/Version/server.hpp>
31#include <xyz/openbmc_project/State/BMC/server.hpp>
32
33#include <algorithm>
Patrick Venture3a5071a2018-09-12 13:27:42 -070034#include <array>
Willy Tu886d6842022-06-03 02:50:47 -070035#include <charconv>
Patrick Venture3a5071a2018-09-12 13:27:42 -070036#include <cstddef>
Vernon Mauery0120b682019-03-25 13:08:54 -070037#include <cstdint>
Vernon Mauerybdda8002019-02-26 10:18:51 -080038#include <filesystem>
Patrick Venture3a5071a2018-09-12 13:27:42 -070039#include <fstream>
40#include <memory>
Potin Lai5d214202023-01-16 18:11:49 +080041#include <regex>
Patrick Venture3a5071a2018-09-12 13:27:42 -070042#include <string>
Willy Tu886d6842022-06-03 02:50:47 -070043#include <string_view>
Patrick Venture3a5071a2018-09-12 13:27:42 -070044#include <tuple>
45#include <vector>
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
Thang Trana809fa52024-08-08 14:46:34 +070049constexpr auto versionPurposeHostEnd = ".Host";
Marri Devender Rao5e007a52018-01-08 06:18:36 -060050
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060051static constexpr auto redundancyIntf =
52 "xyz.openbmc_project.Software.RedundancyPriority";
Patrick Venture0b02be92018-08-31 11:55:55 -070053static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060054static constexpr auto activationIntf =
55 "xyz.openbmc_project.Software.Activation";
56static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
57
George Liu5087b072025-03-11 19:28:17 +080058void registerNetFnAppFunctions() __attribute__((constructor));
vishwabmcba0bd5f2015-09-30 16:50:23 +053059
Ratan Guptab8e99552017-07-27 07:07:48 +053060using namespace phosphor::logging;
Willy Tu523e2d12023-09-05 11:36:48 -070061using namespace sdbusplus::error::xyz::openbmc_project::common;
62using Version = sdbusplus::server::xyz::openbmc_project::software::Version;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060063using Activation =
Willy Tu523e2d12023-09-05 11:36:48 -070064 sdbusplus::server::xyz::openbmc_project::software::Activation;
Alexander Hansenb1f1f112025-11-07 16:36:43 +010065using BMCState = sdbusplus::server::xyz::openbmc_project::state::BMC;
Vernon Mauery185b9f82018-07-20 10:52:36 -070066namespace fs = std::filesystem;
Ratan Guptab8e99552017-07-27 07:07:48 +053067
Yong Libd0503a2019-08-22 17:17:17 +080068#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053069typedef struct
70{
71 uint8_t busId;
Matt Simmering68d9d402023-11-09 14:22:11 -080072 uint8_t targetAddr;
73 uint8_t targetAddrMask;
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053074 std::vector<uint8_t> data;
75 std::vector<uint8_t> dataMask;
Matt Simmering68d9d402023-11-09 14:22:11 -080076} i2cControllerWRAllowlist;
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053077
Matt Simmering68d9d402023-11-09 14:22:11 -080078static std::vector<i2cControllerWRAllowlist>& getWRAllowlist()
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053079{
Matt Simmering68d9d402023-11-09 14:22:11 -080080 static std::vector<i2cControllerWRAllowlist> wrAllowlist;
81 return wrAllowlist;
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053082}
83
Matt Simmering68d9d402023-11-09 14:22:11 -080084static constexpr const char* i2cControllerWRAllowlistFile =
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053085 "/usr/share/ipmi-providers/master_write_read_white_list.json";
86
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053087static constexpr const char* filtersStr = "filters";
88static constexpr const char* busIdStr = "busId";
Matt Simmering68d9d402023-11-09 14:22:11 -080089static constexpr const char* targetAddrStr = "slaveAddr";
90static constexpr const char* targetAddrMaskStr = "slaveAddrMask";
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053091static constexpr const char* cmdStr = "command";
92static constexpr const char* cmdMaskStr = "commandMask";
93static constexpr int base_16 = 16;
Yong Libd0503a2019-08-22 17:17:17 +080094#endif // ENABLE_I2C_WHITELIST_CHECK
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +000095static constexpr uint8_t oemCmdStart = 192;
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +000096static constexpr uint8_t invalidParamSelectorStart = 8;
97static constexpr uint8_t invalidParamSelectorEnd = 191;
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +053098
Nagaraju Goruganti744398d2018-02-20 09:52:00 -060099/**
100 * @brief Returns the Version info from primary s/w object
101 *
102 * Get the Version info from the active s/w object which is having high
103 * "Priority" value(a smaller number is a higher priority) and "Purpose"
104 * is "BMC" from the list of all s/w objects those are implementing
105 * RedundancyPriority interface from the given softwareRoot path.
106 *
107 * @return On success returns the Version info from primary s/w object.
108 *
109 */
Vernon Maueryea1c4012019-05-24 13:26:16 -0700110std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx)
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600111{
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600112 std::string revision{};
Vernon Mauery86a50822019-03-25 13:11:36 -0700113 ipmi::ObjectTree objectTree;
114 try
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600115 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400116 objectTree =
117 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf);
Vernon Mauery86a50822019-03-25 13:11:36 -0700118 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500119 catch (const sdbusplus::exception_t& e)
Vernon Mauery86a50822019-03-25 13:11:36 -0700120 {
George Liu24fffdc2024-07-17 17:40:53 +0800121 lg2::error("Failed to fetch redundancy object from dbus, "
122 "interface: {INTERFACE}, error: {ERROR}",
123 "INTERFACE", redundancyIntf, "ERROR", e);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600124 elog<InternalFailure>();
125 }
126
127 auto objectFound = false;
128 for (auto& softObject : objectTree)
129 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400130 auto service =
131 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first);
132 auto objValueTree =
133 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600134
135 auto minPriority = 0xFF;
136 for (const auto& objIter : objValueTree)
137 {
138 try
139 {
140 auto& intfMap = objIter.second;
141 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
142 auto& versionProps = intfMap.at(versionIntf);
143 auto& activationProps = intfMap.at(activationIntf);
Vernon Maueryf442e112019-04-09 11:44:36 -0700144 auto priority =
145 std::get<uint8_t>(redundancyPriorityProps.at("Priority"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700146 auto purpose =
Vernon Maueryf442e112019-04-09 11:44:36 -0700147 std::get<std::string>(versionProps.at("Purpose"));
148 auto activation =
149 std::get<std::string>(activationProps.at("Activation"));
William A. Kennington III4c008022018-10-12 17:18:14 -0700150 auto version =
Vernon Maueryf442e112019-04-09 11:44:36 -0700151 std::get<std::string>(versionProps.at("Version"));
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600152 if ((Version::convertVersionPurposeFromString(purpose) ==
153 Version::VersionPurpose::BMC) &&
154 (Activation::convertActivationsFromString(activation) ==
155 Activation::Activations::Active))
156 {
157 if (priority < minPriority)
158 {
159 minPriority = priority;
160 objectFound = true;
161 revision = std::move(version);
162 }
163 }
164 }
165 catch (const std::exception& e)
166 {
George Liu24fffdc2024-07-17 17:40:53 +0800167 lg2::error("error message: {ERROR}", "ERROR", e);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600168 }
169 }
170 }
171
172 if (!objectFound)
173 {
George Liu24fffdc2024-07-17 17:40:53 +0800174 lg2::error("Could not found an BMC software Object");
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600175 elog<InternalFailure>();
176 }
177
178 return revision;
179}
180
Alexander Amelkinba19c182018-09-04 15:49:36 +0300181bool getCurrentBmcState()
182{
Patrick Williams5d82f472022-07-22 19:26:53 -0500183 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Alexander Amelkinba19c182018-09-04 15:49:36 +0300184
185 // Get the Inventory object implementing the BMC interface
Patrick Williams1318a5e2024-08-16 15:19:54 -0400186 ipmi::DbusObjectInfo bmcObject =
Alexander Hansenb1f1f112025-11-07 16:36:43 +0100187 ipmi::getDbusObject(bus, BMCState::interface);
188 auto variant = ipmi::getDbusProperty(
189 bus, bmcObject.second, bmcObject.first, BMCState::interface,
190 BMCState::property_names::current_bmc_state);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300191
Vernon Maueryf442e112019-04-09 11:44:36 -0700192 return std::holds_alternative<std::string>(variant) &&
Alexander Hansenb1f1f112025-11-07 16:36:43 +0100193 BMCState::convertBMCStateFromString(
194 std::get<std::string>(variant)) == BMCState::BMCState::Ready;
Alexander Amelkinba19c182018-09-04 15:49:36 +0300195}
196
Patrick Venture94930a12019-04-30 10:01:58 -0700197bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
198{
199 try
200 {
201 return getCurrentBmcState();
202 }
203 catch (...)
204 {
205 // Nothing provided the BMC interface, therefore return whatever was
206 // configured as the default.
207 return fallbackAvailability;
208 }
209}
210
Yong Li18d77262018-10-09 01:59:45 +0800211namespace acpi_state
212{
Willy Tu523e2d12023-09-05 11:36:48 -0700213using namespace sdbusplus::server::xyz::openbmc_project::control::power;
Yong Li18d77262018-10-09 01:59:45 +0800214
Yong Li18d77262018-10-09 01:59:45 +0800215const static constexpr char* acpiInterface =
216 "xyz.openbmc_project.Control.Power.ACPIPowerState";
217const static constexpr char* sysACPIProp = "SysACPIStatus";
218const static constexpr char* devACPIProp = "DevACPIStatus";
219
220enum class PowerStateType : uint8_t
221{
222 sysPowerState = 0x00,
223 devPowerState = 0x01,
224};
225
226// Defined in 20.6 of ipmi doc
227enum class PowerState : uint8_t
228{
229 s0G0D0 = 0x00,
230 s1D1 = 0x01,
231 s2D2 = 0x02,
232 s3D3 = 0x03,
233 s4 = 0x04,
234 s5G2 = 0x05,
235 s4S5 = 0x06,
236 g3 = 0x07,
237 sleep = 0x08,
238 g1Sleep = 0x09,
239 override = 0x0a,
240 legacyOn = 0x20,
241 legacyOff = 0x21,
242 unknown = 0x2a,
243 noChange = 0x7f,
244};
245
246static constexpr uint8_t stateChanged = 0x80;
247
Yong Li18d77262018-10-09 01:59:45 +0800248std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = {
249 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0},
250 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1},
251 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2},
252 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3},
253 {ACPIPowerState::ACPI::S4, PowerState::s4},
254 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2},
255 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5},
256 {ACPIPowerState::ACPI::G3, PowerState::g3},
257 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep},
258 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep},
259 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override},
260 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn},
261 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff},
262 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}};
263
264bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state)
265{
266 if (type == acpi_state::PowerStateType::sysPowerState)
267 {
268 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) ||
269 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) ||
270 (state ==
271 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) ||
272 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
273 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
274 {
275 return true;
276 }
277 else
278 {
279 return false;
280 }
281 }
282 else if (type == acpi_state::PowerStateType::devPowerState)
283 {
284 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) ||
285 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) ||
286 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange)))
287 {
288 return true;
289 }
290 else
291 {
292 return false;
293 }
294 }
295 else
296 {
297 return false;
298 }
299 return false;
300}
301} // namespace acpi_state
302
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000303/** @brief implements Set ACPI Power State command
304 * @param sysAcpiState - ACPI system power state to set
305 * @param devAcpiState - ACPI device power state to set
306 *
307 * @return IPMI completion code on success
308 **/
309ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState,
310 uint8_t devAcpiState)
Chris Austen6caf28b2015-10-13 12:40:40 -0500311{
Yong Li18d77262018-10-09 01:59:45 +0800312 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown);
Yong Li18d77262018-10-09 01:59:45 +0800313
Patrick Williams5d82f472022-07-22 19:26:53 -0500314 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Yong Li18d77262018-10-09 01:59:45 +0800315
316 auto value = acpi_state::ACPIPowerState::ACPI::Unknown;
317
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000318 if (sysAcpiState & acpi_state::stateChanged)
Yong Li18d77262018-10-09 01:59:45 +0800319 {
320 // set system power state
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000321 s = sysAcpiState & ~acpi_state::stateChanged;
Yong Li18d77262018-10-09 01:59:45 +0800322
323 if (!acpi_state::isValidACPIState(
324 acpi_state::PowerStateType::sysPowerState, s))
325 {
George Liu24fffdc2024-07-17 17:40:53 +0800326 lg2::error("set_acpi_power sys invalid input, S: {S}", "S", s);
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000327 return ipmi::responseParmOutOfRange();
Yong Li18d77262018-10-09 01:59:45 +0800328 }
329
330 // valid input
331 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
332 {
George Liu24fffdc2024-07-17 17:40:53 +0800333 lg2::debug("No change for system power state");
Yong Li18d77262018-10-09 01:59:45 +0800334 }
335 else
336 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400337 auto found = std::find_if(
338 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
339 [&s](const auto& iter) {
340 return (static_cast<uint8_t>(iter.second) == s);
341 });
Yong Li18d77262018-10-09 01:59:45 +0800342
343 value = found->first;
344
345 try
346 {
347 auto acpiObject =
348 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
349 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
350 acpi_state::acpiInterface,
351 acpi_state::sysACPIProp,
352 convertForMessage(value));
353 }
354 catch (const InternalFailure& e)
355 {
George Liu24fffdc2024-07-17 17:40:53 +0800356 lg2::error("Failed in set ACPI system property: {ERROR}",
357 "ERROR", e);
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000358 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800359 }
360 }
361 }
362 else
363 {
George Liu24fffdc2024-07-17 17:40:53 +0800364 lg2::debug("Do not change system power state");
Yong Li18d77262018-10-09 01:59:45 +0800365 }
366
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000367 if (devAcpiState & acpi_state::stateChanged)
Yong Li18d77262018-10-09 01:59:45 +0800368 {
369 // set device power state
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000370 s = devAcpiState & ~acpi_state::stateChanged;
Yong Li18d77262018-10-09 01:59:45 +0800371 if (!acpi_state::isValidACPIState(
372 acpi_state::PowerStateType::devPowerState, s))
373 {
George Liu24fffdc2024-07-17 17:40:53 +0800374 lg2::error("set_acpi_power dev invalid input, S: {S}", "S", s);
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000375 return ipmi::responseParmOutOfRange();
Yong Li18d77262018-10-09 01:59:45 +0800376 }
377
378 // valid input
379 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange))
380 {
George Liu24fffdc2024-07-17 17:40:53 +0800381 lg2::debug("No change for device power state");
Yong Li18d77262018-10-09 01:59:45 +0800382 }
383 else
384 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400385 auto found = std::find_if(
386 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(),
387 [&s](const auto& iter) {
388 return (static_cast<uint8_t>(iter.second) == s);
389 });
Yong Li18d77262018-10-09 01:59:45 +0800390
391 value = found->first;
392
393 try
394 {
395 auto acpiObject =
396 ipmi::getDbusObject(bus, acpi_state::acpiInterface);
397 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first,
398 acpi_state::acpiInterface,
399 acpi_state::devACPIProp,
400 convertForMessage(value));
401 }
402 catch (const InternalFailure& e)
403 {
George Liu24fffdc2024-07-17 17:40:53 +0800404 lg2::error("Failed in set ACPI device property: {ERROR}",
405 "ERROR", e);
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000406 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800407 }
408 }
409 }
410 else
411 {
George Liu24fffdc2024-07-17 17:40:53 +0800412 lg2::debug("Do not change device power state");
Yong Li18d77262018-10-09 01:59:45 +0800413 }
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +0000414 return ipmi::responseSuccess();
Yong Li18d77262018-10-09 01:59:45 +0800415}
416
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000417/**
418 * @brief implements the get ACPI power state command
419 *
420 * @return IPMI completion code plus response data on success.
421 * - ACPI system power state
422 * - ACPI device power state
423 **/
424ipmi::RspType<uint8_t, // acpiSystemPowerState
425 uint8_t // acpiDevicePowerState
426 >
427 ipmiGetAcpiPowerState()
Yong Li18d77262018-10-09 01:59:45 +0800428{
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000429 uint8_t sysAcpiState;
430 uint8_t devAcpiState;
Yong Li18d77262018-10-09 01:59:45 +0800431
Patrick Williams5d82f472022-07-22 19:26:53 -0500432 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Yong Li18d77262018-10-09 01:59:45 +0800433
Yong Li18d77262018-10-09 01:59:45 +0800434 try
435 {
436 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface);
437
438 auto sysACPIVal = ipmi::getDbusProperty(
439 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
440 acpi_state::sysACPIProp);
441 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700442 std::get<std::string>(sysACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000443 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI));
Yong Li18d77262018-10-09 01:59:45 +0800444
445 auto devACPIVal = ipmi::getDbusProperty(
446 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface,
447 acpi_state::devACPIProp);
448 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString(
Vernon Maueryf442e112019-04-09 11:44:36 -0700449 std::get<std::string>(devACPIVal));
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000450 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI));
Yong Li18d77262018-10-09 01:59:45 +0800451 }
452 catch (const InternalFailure& e)
453 {
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000454 return ipmi::responseUnspecifiedError();
Yong Li18d77262018-10-09 01:59:45 +0800455 }
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +0000456
457 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
Chris Austen6caf28b2015-10-13 12:40:40 -0500458}
459
Chris Austen7303bdc2016-04-17 11:50:54 -0500460typedef struct
461{
462 char major;
463 char minor;
Potin Laic7c55922023-02-16 10:33:55 +0800464 uint8_t aux[4];
Vernon Mauery86a50822019-03-25 13:11:36 -0700465} Revision;
Chris Austen7303bdc2016-04-17 11:50:54 -0500466
Potin Lai5d214202023-01-16 18:11:49 +0800467/* Use regular expression searching matched pattern X.Y, and convert it to */
468/* Major (X) and Minor (Y) version. */
469/* Example: */
470/* version = 2.14.0-dev */
471/* ^ ^ */
472/* | |---------------- Minor */
473/* |------------------ Major */
474/* */
Potin Laic7c55922023-02-16 10:33:55 +0800475/* Default regex string only tries to match Major and Minor version. */
476/* */
477/* To match more firmware version info, platforms need to define it own */
478/* regex string to match more strings, and assign correct mapping index in */
479/* matches array. */
480/* */
481/* matches[0]: matched index for major ver */
482/* matches[1]: matched index for minor ver */
483/* matches[2]: matched index for aux[0] (set 0 to skip) */
484/* matches[3]: matched index for aux[1] (set 0 to skip) */
485/* matches[4]: matched index for aux[2] (set 0 to skip) */
486/* matches[5]: matched index for aux[3] (set 0 to skip) */
487/* Example: */
488/* regex = "([\d]+).([\d]+).([\d]+)-dev-([\d]+)-g([0-9a-fA-F]{2}) */
489/* ([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})" */
490/* matches = {1,2,5,6,7,8} */
491/* version = 2.14.0-dev-750-g37a7c5ad1-dirty */
492/* ^ ^ ^ ^ ^ ^ ^ ^ */
493/* | | | | | | | | */
494/* | | | | | | | |-- Aux byte 3 (0xAD), index 8 */
495/* | | | | | | |---- Aux byte 2 (0xC5), index 7 */
496/* | | | | | |------ Aux byte 1 (0xA7), index 6 */
497/* | | | | |-------- Aux byte 0 (0x37), index 5 */
498/* | | | |------------- Not used, index 4 */
499/* | | |------------------- Not used, index 3 */
500/* | |---------------------- Minor (14), index 2 */
501/* |------------------------ Major (2), index 1 */
Potin Lai5d214202023-01-16 18:11:49 +0800502int convertVersion(std::string s, Revision& rev)
Chris Austen7303bdc2016-04-17 11:50:54 -0500503{
Potin Laic7c55922023-02-16 10:33:55 +0800504 static const std::vector<size_t> matches = {
505 MAJOR_MATCH_INDEX, MINOR_MATCH_INDEX, AUX_0_MATCH_INDEX,
506 AUX_1_MATCH_INDEX, AUX_2_MATCH_INDEX, AUX_3_MATCH_INDEX};
Potin Lai5d214202023-01-16 18:11:49 +0800507 std::regex fw_regex(FW_VER_REGEX);
508 std::smatch m;
509 Revision r = {0};
510 size_t val;
Chris Austen7303bdc2016-04-17 11:50:54 -0500511
Potin Laidcde04e2023-03-16 18:44:15 +0800512 if (std::regex_search(s, m, fw_regex))
Dinesh Chinari2b7e07d2017-11-08 15:38:50 -0600513 {
Potin Laic7c55922023-02-16 10:33:55 +0800514 if (m.size() < *std::max_element(matches.begin(), matches.end()))
515 { // max index higher than match count
Potin Laidcde04e2023-03-16 18:44:15 +0800516 return -1;
517 }
518
Potin Lai5d214202023-01-16 18:11:49 +0800519 // convert major
520 {
Jayanth Othayotha6c307a2024-12-08 09:07:14 -0600521 std::string str = m[matches[0]].str();
522 const auto& [ptr, ec] =
523 std::from_chars(str.data(), str.data() + str.size(), val);
524 if (ec != std::errc() || ptr != str.data() + str.size())
Potin Lai5d214202023-01-16 18:11:49 +0800525 { // failed to convert major string
Potin Laidcde04e2023-03-16 18:44:15 +0800526 return -1;
Potin Lai5d214202023-01-16 18:11:49 +0800527 }
Potin Lai058e2912023-09-07 10:16:38 +0800528
529 if (val >= 2000)
530 { // For the platforms use year as major version, it would expect to
531 // have major version between 0 - 99. If the major version is
532 // greater than or equal to 2000, it is treated as a year and
533 // converted to 0 - 99.
534 r.major = val % 100;
535 }
536 else
537 {
538 r.major = val & 0x7F;
539 }
Potin Lai5d214202023-01-16 18:11:49 +0800540 }
541
542 // convert minor
543 {
Jayanth Othayotha6c307a2024-12-08 09:07:14 -0600544 std::string str = m[matches[1]].str();
545 const auto& [ptr, ec] =
546 std::from_chars(str.data(), str.data() + str.size(), val);
547 if (ec != std::errc() || ptr != str.data() + str.size())
Potin Lai5d214202023-01-16 18:11:49 +0800548 { // failed to convert minor string
Potin Laidcde04e2023-03-16 18:44:15 +0800549 return -1;
Potin Lai5d214202023-01-16 18:11:49 +0800550 }
551 r.minor = val & 0xFF;
552 }
553
Potin Laic7c55922023-02-16 10:33:55 +0800554 // convert aux bytes
555 {
556 size_t i;
557 for (i = 0; i < 4; i++)
558 {
559 if (matches[i + 2] == 0)
560 {
561 continue;
562 }
563
Jayanth Othayotha6c307a2024-12-08 09:07:14 -0600564 std::string str = m[matches[i + 2]].str();
565 const char* cstr = str.c_str();
Potin Laic7c55922023-02-16 10:33:55 +0800566 auto [ptr,
Jayanth Othayotha6c307a2024-12-08 09:07:14 -0600567 ec] = std::from_chars(cstr, cstr + str.size(), val, 16);
568 if (ec != std::errc() || ptr != cstr + str.size())
Potin Laic7c55922023-02-16 10:33:55 +0800569 { // failed to convert aux byte string
570 break;
571 }
572
573 r.aux[i] = val & 0xFF;
574 }
575
576 if (i != 4)
577 { // something wrong durign converting aux bytes
578 return -1;
579 }
580 }
581
Potin Lai5d214202023-01-16 18:11:49 +0800582 // all matched
583 rev = r;
584 return 0;
Chris Austen176c9652016-04-30 16:32:17 -0500585 }
Chris Austen7303bdc2016-04-17 11:50:54 -0500586
Potin Lai5d214202023-01-16 18:11:49 +0800587 return -1;
Chris Austen7303bdc2016-04-17 11:50:54 -0500588}
589
Vernon Maueryea1c4012019-05-24 13:26:16 -0700590/* @brief: Implement the Get Device ID IPMI command per the IPMI spec
591 * @param[in] ctx - shared_ptr to an IPMI context struct
592 *
593 * @returns IPMI completion code plus response data
594 * - Device ID (manufacturer defined)
595 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit]
596 * - FW revision major[7 bits] (binary encoded); available[1 bit]
597 * - FW Revision minor (BCD encoded)
598 * - IPMI version (0x02 for IPMI 2.0)
599 * - device support (bitfield of supported options)
600 * - MFG IANA ID (3 bytes)
601 * - product ID (2 bytes)
602 * - AUX info (4 bytes)
603 */
604ipmi::RspType<uint8_t, // Device ID
605 uint8_t, // Device Revision
606 uint8_t, // Firmware Revision Major
607 uint8_t, // Firmware Revision minor
608 uint8_t, // IPMI version
609 uint8_t, // Additional device support
610 uint24_t, // MFG ID
611 uint16_t, // Product ID
612 uint32_t // AUX info
613 >
Willy Tu11d68892022-01-20 10:37:34 -0800614 ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx)
Chris Austen6caf28b2015-10-13 12:40:40 -0500615{
Vernon Mauery86a50822019-03-25 13:11:36 -0700616 static struct
617 {
618 uint8_t id;
619 uint8_t revision;
620 uint8_t fw[2];
621 uint8_t ipmiVer;
622 uint8_t addnDevSupport;
623 uint24_t manufId;
624 uint16_t prodId;
625 uint32_t aux;
626 } devId;
David Cobbleya1adb072017-11-21 15:58:13 -0800627 static bool dev_id_initialized = false;
Patrick Venture94930a12019-04-30 10:01:58 -0700628 static bool defaultActivationSetting = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800629 const char* filename = "/usr/share/ipmi-providers/dev_id.json";
Alexander Amelkinba19c182018-09-04 15:49:36 +0300630 constexpr auto ipmiDevIdStateShift = 7;
631 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift);
Willy Tub78184e2022-10-27 22:57:38 +0000632
633#ifdef GET_DBUS_ACTIVE_SOFTWARE
634 static bool haveBMCVersion = false;
JeffLin27a62ec2021-11-24 15:40:33 +0800635 if (!haveBMCVersion || !dev_id_initialized)
David Cobbleya1adb072017-11-21 15:58:13 -0800636 {
Willy Tub78184e2022-10-27 22:57:38 +0000637 int r = -1;
Jayanth Othayothceee8112024-12-08 09:46:37 -0600638 Revision rev = {0, 0, {0, 0, 0, 0}};
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600639 try
640 {
Vernon Maueryea1c4012019-05-24 13:26:16 -0700641 auto version = getActiveSoftwareVersionInfo(ctx);
Vernon Mauery86a50822019-03-25 13:11:36 -0700642 r = convertVersion(version, rev);
David Cobbleya1adb072017-11-21 15:58:13 -0800643 }
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600644 catch (const std::exception& e)
645 {
George Liu24fffdc2024-07-17 17:40:53 +0800646 lg2::error("error message: {ERROR}", "ERROR", e);
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600647 }
Nan Liee0cb902016-07-11 15:38:03 +0800648
Patrick Venture0b02be92018-08-31 11:55:55 -0700649 if (r >= 0)
650 {
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600651 // bit7 identifies if the device is available
652 // 0=normal operation
653 // 1=device firmware, SDR update,
654 // or self-initialization in progress.
Alexander Amelkinba19c182018-09-04 15:49:36 +0300655 // The availability may change in run time, so mask here
656 // and initialize later.
Vernon Mauery86a50822019-03-25 13:11:36 -0700657 devId.fw[0] = rev.major & ipmiDevIdFw1Mask;
Nagaraju Goruganti744398d2018-02-20 09:52:00 -0600658
659 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
Vernon Mauery86a50822019-03-25 13:11:36 -0700660 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
Potin Laic7c55922023-02-16 10:33:55 +0800661 std::memcpy(&devId.aux, rev.aux, sizeof(rev.aux));
Brandon Kimc1f5aca2022-03-17 17:54:06 -0700662 haveBMCVersion = true;
David Cobbleya1adb072017-11-21 15:58:13 -0800663 }
JeffLin27a62ec2021-11-24 15:40:33 +0800664 }
Willy Tub78184e2022-10-27 22:57:38 +0000665#endif
JeffLin27a62ec2021-11-24 15:40:33 +0800666 if (!dev_id_initialized)
667 {
David Cobbleya1adb072017-11-21 15:58:13 -0800668 // IPMI Spec version 2.0
Vernon Mauery86a50822019-03-25 13:11:36 -0700669 devId.ipmiVer = 2;
Adriana Kobylak0e912642016-06-22 16:54:39 -0500670
Vernon Mauery86a50822019-03-25 13:11:36 -0700671 std::ifstream devIdFile(filename);
672 if (devIdFile.is_open())
David Cobbleya1adb072017-11-21 15:58:13 -0800673 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700674 auto data = nlohmann::json::parse(devIdFile, nullptr, false);
David Cobbleya1adb072017-11-21 15:58:13 -0800675 if (!data.is_discarded())
676 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700677 devId.id = data.value("id", 0);
678 devId.revision = data.value("revision", 0);
679 devId.addnDevSupport = data.value("addn_dev_support", 0);
680 devId.manufId = data.value("manuf_id", 0);
681 devId.prodId = data.value("prod_id", 0);
Potin Lai9a686362023-09-05 09:27:53 +0800682#ifdef GET_DBUS_ACTIVE_SOFTWARE
683 if (!(AUX_0_MATCH_INDEX || AUX_1_MATCH_INDEX ||
684 AUX_2_MATCH_INDEX || AUX_3_MATCH_INDEX))
685#endif
686 {
687 devId.aux = data.value("aux", 0);
688 }
David Cobbleya1adb072017-11-21 15:58:13 -0800689
Willy Tubfd3a172022-05-31 13:57:54 -0700690 if (data.contains("firmware_revision"))
691 {
692 const auto& firmwareRevision = data.at("firmware_revision");
693 if (firmwareRevision.contains("major"))
694 {
695 firmwareRevision.at("major").get_to(devId.fw[0]);
696 }
697 if (firmwareRevision.contains("minor"))
698 {
699 firmwareRevision.at("minor").get_to(devId.fw[1]);
700 }
701 }
702
Patrick Venture94930a12019-04-30 10:01:58 -0700703 // Set the availablitity of the BMC.
704 defaultActivationSetting = data.value("availability", true);
705
Patrick Venture0b02be92018-08-31 11:55:55 -0700706 // Don't read the file every time if successful
David Cobbleya1adb072017-11-21 15:58:13 -0800707 dev_id_initialized = true;
708 }
709 else
710 {
George Liu24fffdc2024-07-17 17:40:53 +0800711 lg2::error("Device ID JSON parser failure");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700712 return ipmi::responseUnspecifiedError();
David Cobbleya1adb072017-11-21 15:58:13 -0800713 }
714 }
715 else
716 {
George Liu24fffdc2024-07-17 17:40:53 +0800717 lg2::error("Device ID file not found");
Vernon Maueryf2587fc2019-04-09 14:28:15 -0700718 return ipmi::responseUnspecifiedError();
Chris Austen7303bdc2016-04-17 11:50:54 -0500719 }
720 }
Chris Austen6caf28b2015-10-13 12:40:40 -0500721
Alexander Amelkinba19c182018-09-04 15:49:36 +0300722 // Set availability to the actual current BMC state
Vernon Mauery86a50822019-03-25 13:11:36 -0700723 devId.fw[0] &= ipmiDevIdFw1Mask;
Patrick Venture94930a12019-04-30 10:01:58 -0700724 if (!getCurrentBmcStateWithFallback(defaultActivationSetting))
Alexander Amelkinba19c182018-09-04 15:49:36 +0300725 {
Vernon Mauery86a50822019-03-25 13:11:36 -0700726 devId.fw[0] |= (1 << ipmiDevIdStateShift);
Alexander Amelkinba19c182018-09-04 15:49:36 +0300727 }
728
Vernon Mauery86a50822019-03-25 13:11:36 -0700729 return ipmi::responseSuccess(
730 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer,
731 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux);
Chris Austen6caf28b2015-10-13 12:40:40 -0500732}
733
Vernon Maueryb84a5282019-03-25 13:39:03 -0700734auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t>
Nan Li41fa24a2016-11-10 20:12:37 +0800735{
Nan Li41fa24a2016-11-10 20:12:37 +0800736 // Byte 2:
737 // 55h - No error.
Gunnar Mills8991dd62017-10-25 17:11:29 -0500738 // 56h - Self Test function not implemented in this controller.
Nan Li41fa24a2016-11-10 20:12:37 +0800739 // 57h - Corrupted or inaccesssible data or devices.
740 // 58h - Fatal hardware error.
741 // FFh - reserved.
742 // all other: Device-specific 'internal failure'.
743 // Byte 3:
744 // For byte 2 = 55h, 56h, FFh: 00h
745 // For byte 2 = 58h, all other: Device-specific
746 // For byte 2 = 57h: self-test error bitfield.
747 // Note: returning 57h does not imply that all test were run.
748 // [7] 1b = Cannot access SEL device.
749 // [6] 1b = Cannot access SDR Repository.
750 // [5] 1b = Cannot access BMC FRU device.
751 // [4] 1b = IPMB signal lines do not respond.
752 // [3] 1b = SDR Repository empty.
753 // [2] 1b = Internal Use Area of BMC FRU corrupted.
754 // [1] 1b = controller update 'boot block' firmware corrupted.
755 // [0] 1b = controller operational firmware corrupted.
Vernon Maueryb84a5282019-03-25 13:39:03 -0700756 constexpr uint8_t notImplemented = 0x56;
757 constexpr uint8_t zero = 0;
758 return ipmi::responseSuccess(notImplemented, zero);
Nan Li41fa24a2016-11-10 20:12:37 +0800759}
760
Vernon Mauery15541322019-03-25 13:33:03 -0700761static constexpr size_t uuidBinaryLength = 16;
762static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500763{
Willy Tu523e2d12023-09-05 11:36:48 -0700764 using Argument = xyz::openbmc_project::common::InvalidArgument;
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500765 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
Patrick Ventured2117022018-02-06 08:54:37 -0800766 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
767 // order
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500768 // Ex: 0x2332fc2c40e66298e511f2782395a361
Vernon Mauery15541322019-03-25 13:33:03 -0700769 constexpr size_t uuidHexLength = (2 * uuidBinaryLength);
770 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
771 std::array<uint8_t, uuidBinaryLength> uuid;
772 if (rfc4122.size() == uuidRfc4122Length)
Patrick Venture0b02be92018-08-31 11:55:55 -0700773 {
Vernon Mauery15541322019-03-25 13:33:03 -0700774 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
775 rfc4122.end());
Sergey Solomineb9b8142016-08-23 09:07:28 -0500776 }
Vernon Mauery15541322019-03-25 13:33:03 -0700777 if (rfc4122.size() != uuidHexLength)
vishwa1eaea4f2016-02-26 11:57:40 -0600778 {
Vernon Mauery15541322019-03-25 13:33:03 -0700779 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
780 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
vishwa1eaea4f2016-02-26 11:57:40 -0600781 }
Vernon Mauery15541322019-03-25 13:33:03 -0700782 for (size_t ind = 0; ind < uuidHexLength; ind += 2)
vishwa1eaea4f2016-02-26 11:57:40 -0600783 {
Vernon Mauery15541322019-03-25 13:33:03 -0700784 char v[3];
785 v[0] = rfc4122[ind];
786 v[1] = rfc4122[ind + 1];
787 v[2] = 0;
788 size_t err;
789 long b;
790 try
Emily Shafferedb8bb02018-09-27 14:50:15 -0700791 {
Vernon Mauery15541322019-03-25 13:33:03 -0700792 b = std::stoul(v, &err, 16);
Emily Shafferedb8bb02018-09-27 14:50:15 -0700793 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500794 catch (const std::exception& e)
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500795 {
Vernon Mauery15541322019-03-25 13:33:03 -0700796 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
797 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500798 }
Vernon Mauery15541322019-03-25 13:33:03 -0700799 // check that exactly two ascii bytes were converted
800 if (err != 2)
801 {
802 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
803 Argument::ARGUMENT_VALUE(rfc4122.c_str()));
804 }
805 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500806 }
Vernon Mauery15541322019-03-25 13:33:03 -0700807 return uuid;
808}
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500809
Vernon Mauery15541322019-03-25 13:33:03 -0700810auto ipmiAppGetDeviceGuid()
811 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>>
812{
813 // return a fixed GUID based on /etc/machine-id
814 // This should match the /redfish/v1/Managers/bmc's UUID data
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500815
Vernon Mauery15541322019-03-25 13:33:03 -0700816 // machine specific application ID (for BMC ID)
817 // generated by systemd-id128 -p new as per man page
818 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE(
819 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500820
Vernon Mauery15541322019-03-25 13:33:03 -0700821 sd_id128_t bmcUuid;
822 // create the UUID from /etc/machine-id via the systemd API
823 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500824
Vernon Mauery15541322019-03-25 13:33:03 -0700825 char bmcUuidCstr[SD_ID128_STRING_MAX];
826 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr);
827
828 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid);
829 return ipmi::responseSuccess(uuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -0500830}
Chris Austen6caf28b2015-10-13 12:40:40 -0500831
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700832auto ipmiAppGetBtCapabilities()
833 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t>
vishwabmcba0bd5f2015-09-30 16:50:23 +0530834{
Adriana Kobylak88ad8152016-12-13 10:09:08 -0600835 // Per IPMI 2.0 spec, the input and output buffer size must be the max
836 // buffer size minus one byte to allocate space for the length byte.
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700837 constexpr uint8_t nrOutstanding = 0x01;
838 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1;
839 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1;
840 constexpr uint8_t transactionTime = 0x0A;
841 constexpr uint8_t nrRetries = 0x01;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530842
Vernon Mauerycc99ba42019-03-25 13:40:11 -0700843 return ipmi::responseSuccess(nrOutstanding, inputBufferSize,
844 outputBufferSize, transactionTime, nrRetries);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530845}
846
Vernon Mauery6c44a942023-07-27 11:00:10 -0700847auto ipmiAppGetSystemGuid(ipmi::Context::ptr& ctx)
848 -> ipmi::RspType<std::array<uint8_t, 16>>
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600849{
Prithvi Paid7966892025-08-02 18:40:13 +0530850 static constexpr auto uuidService = "xyz.openbmc_project.Settings";
851 static constexpr auto uuidObject = "/xyz/openbmc_project/Common/UUID";
Vernon Maueryb90a5322019-03-25 13:36:55 -0700852 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID";
853 static constexpr auto uuidProperty = "UUID";
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600854
Prithvi Paid7966892025-08-02 18:40:13 +0530855 // Read UUID property value from settings object
Vernon Mauery6c44a942023-07-27 11:00:10 -0700856 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
857 std::string rfc4122Uuid{};
Prithvi Paid7966892025-08-02 18:40:13 +0530858 boost::system::error_code ec = ipmi::getDbusProperty(
859 ctx, uuidService, uuidObject, uuidInterface, uuidProperty, rfc4122Uuid);
Vernon Mauery6c44a942023-07-27 11:00:10 -0700860 if (ec.value())
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600861 {
George Liu24fffdc2024-07-17 17:40:53 +0800862 lg2::error("Failed to read System UUID property, "
863 "interface: {INTERFACE}, property: {PROPERTY}, "
864 "error: {ERROR}",
865 "INTERFACE", uuidInterface, "PROPERTY", uuidProperty,
866 "ERROR", ec.message());
Vernon Maueryb90a5322019-03-25 13:36:55 -0700867 return ipmi::responseUnspecifiedError();
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600868 }
Vernon Maueryb90a5322019-03-25 13:36:55 -0700869 std::array<uint8_t, 16> uuid;
Vernon Maueryb90a5322019-03-25 13:36:55 -0700870 try
871 {
872 // convert to IPMI format
873 uuid = rfc4122ToIpmi(rfc4122Uuid);
874 }
875 catch (const InvalidArgument& e)
876 {
George Liu24fffdc2024-07-17 17:40:53 +0800877 lg2::error("Failed in parsing BMC UUID property, "
878 "interface: {INTERFACE}, property: {PROPERTY}, "
879 "value: {VALUE}, error: {ERROR}",
880 "INTERFACE", uuidInterface, "PROPERTY", uuidProperty,
881 "VALUE", rfc4122Uuid, "ERROR", e);
Vernon Maueryb90a5322019-03-25 13:36:55 -0700882 return ipmi::responseUnspecifiedError();
883 }
884 return ipmi::responseSuccess(uuid);
Marri Devender Rao5e007a52018-01-08 06:18:36 -0600885}
886
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000887/**
888 * @brief set the session state as teardown
889 *
890 * This function is to set the session state to tear down in progress if the
891 * state is active.
892 *
893 * @param[in] busp - Dbus obj
894 * @param[in] service - service name
895 * @param[in] obj - object path
896 *
897 * @return success completion code if it sets the session state to
898 * tearDownInProgress else return the corresponding error completion code.
899 **/
900uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
901 const std::string& service, const std::string& obj)
902{
903 try
904 {
905 uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
906 *busp, service, obj, session::sessionIntf, "State"));
907
908 if (sessionState == static_cast<uint8_t>(session::State::active))
909 {
910 ipmi::setDbusProperty(
911 *busp, service, obj, session::sessionIntf, "State",
912 static_cast<uint8_t>(session::State::tearDownInProgress));
913 return ipmi::ccSuccess;
914 }
915 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500916 catch (const std::exception& e)
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000917 {
George Liu24fffdc2024-07-17 17:40:53 +0800918 lg2::error("Failed in getting session state property, "
919 "service: {SERVICE}, object path: {OBJECT_PATH}, "
920 "interface: {INTERFACE}, error: {ERROR}",
921 "SERVICE", service, "OBJECT_PATH", obj, "INTERFACE",
922 session::sessionIntf, "ERROR", e);
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000923 return ipmi::ccUnspecifiedError;
924 }
925
926 return ipmi::ccInvalidFieldRequest;
927}
928
929ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId,
930 std::optional<uint8_t> requestSessionHandle)
931{
932 auto busp = getSdBus();
933 uint8_t reqSessionHandle =
934 requestSessionHandle.value_or(session::defaultSessionHandle);
935
936 if (reqSessionId == session::sessionZero &&
937 reqSessionHandle == session::defaultSessionHandle)
938 {
939 return ipmi::response(session::ccInvalidSessionId);
940 }
941
942 if (reqSessionId == session::sessionZero &&
943 reqSessionHandle == session::invalidSessionHandle)
944 {
945 return ipmi::response(session::ccInvalidSessionHandle);
946 }
947
948 if (reqSessionId != session::sessionZero &&
949 reqSessionHandle != session::defaultSessionHandle)
950 {
951 return ipmi::response(ipmi::ccInvalidFieldRequest);
952 }
953
954 try
955 {
956 ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
957 *busp, session::sessionManagerRootPath, session::sessionIntf);
958
959 for (auto& objectTreeItr : objectTree)
960 {
961 const std::string obj = objectTreeItr.first;
962
963 if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle))
964 {
965 auto& serviceMap = objectTreeItr.second;
966
967 // Session id and session handle are unique for each session.
968 // Session id and handler are retrived from the object path and
969 // object path will be unique for each session. Checking if
970 // multiple objects exist with same object path under multiple
971 // services.
972 if (serviceMap.size() != 1)
973 {
974 return ipmi::responseUnspecifiedError();
975 }
976
977 auto itr = serviceMap.begin();
978 const std::string service = itr->first;
979 return ipmi::response(setSessionState(busp, service, obj));
980 }
981 }
982 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500983 catch (const sdbusplus::exception_t& e)
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000984 {
George Liu24fffdc2024-07-17 17:40:53 +0800985 lg2::error("Failed to fetch object from dbus, "
986 "interface: {INTERFACE}, error: {ERROR}",
987 "INTERFACE", session::sessionIntf, "ERROR", e);
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +0000988 return ipmi::responseUnspecifiedError();
989 }
990
991 return ipmi::responseInvalidFieldRequest();
992}
993
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000994uint8_t getTotalSessionCount()
995{
Meera-Kattac1789482021-05-18 09:53:26 +0000996 uint8_t count = 0, ch = 0;
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +0000997
998 while (ch < ipmi::maxIpmiChannels &&
999 count < session::maxNetworkInstanceSupported)
1000 {
Jayaprakash Mutyala80207e62020-07-04 16:34:15 +00001001 ipmi::ChannelInfo chInfo{};
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001002 ipmi::getChannelInfo(ch, chInfo);
1003 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
1004 ipmi::EChannelMediumType::lan8032)
1005 {
1006 count++;
1007 }
1008 ch++;
1009 }
1010 return count * session::maxSessionCountPerChannel;
1011}
1012
1013/**
1014 * @brief get session info request data.
1015 *
1016 * This function validates the request data and retrive request session id,
1017 * session handle.
1018 *
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301019 * @param[in] ctx - context of current session.
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001020 * @param[in] sessionIndex - request session index
1021 * @param[in] payload - input payload
1022 * @param[in] reqSessionId - unpacked session Id will be asigned
1023 * @param[in] reqSessionHandle - unpacked session handle will be asigned
1024 *
1025 * @return success completion code if request data is valid
1026 * else return the correcponding error completion code.
1027 **/
Patrick Williams1318a5e2024-08-16 15:19:54 -04001028uint8_t getSessionInfoRequestData(
1029 const ipmi::Context::ptr ctx, const uint8_t sessionIndex,
1030 ipmi::message::Payload& payload, uint32_t& reqSessionId,
1031 uint8_t& reqSessionHandle)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001032{
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301033 if ((sessionIndex > session::maxSessionCountPerChannel) &&
1034 (sessionIndex < session::searchSessionByHandle))
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001035 {
1036 return ipmi::ccInvalidFieldRequest;
1037 }
1038
1039 switch (sessionIndex)
1040 {
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301041 case session::searchCurrentSession:
1042
1043 ipmi::ChannelInfo chInfo;
1044 ipmi::getChannelInfo(ctx->channel, chInfo);
1045
1046 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) !=
1047 ipmi::EChannelMediumType::lan8032)
1048 {
1049 return ipmi::ccInvalidFieldRequest;
1050 }
1051
1052 if (!payload.fullyUnpacked())
1053 {
1054 return ipmi::ccReqDataLenInvalid;
1055 }
1056 // Check if current sessionId is 0, sessionId 0 is reserved.
1057 if (ctx->sessionId == session::sessionZero)
1058 {
1059 return session::ccInvalidSessionId;
1060 }
1061 reqSessionId = ctx->sessionId;
1062 break;
1063
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001064 case session::searchSessionByHandle:
1065
1066 if ((payload.unpack(reqSessionHandle)) ||
1067 (!payload.fullyUnpacked()))
1068 {
1069 return ipmi::ccReqDataLenInvalid;
1070 }
1071
1072 if ((reqSessionHandle == session::sessionZero) ||
1073 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
1074 session::maxSessionCountPerChannel))
1075 {
1076 return session::ccInvalidSessionHandle;
1077 }
1078 break;
1079
1080 case session::searchSessionById:
1081
1082 if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
1083 {
1084 return ipmi::ccReqDataLenInvalid;
1085 }
1086
1087 if (reqSessionId == session::sessionZero)
1088 {
1089 return session::ccInvalidSessionId;
1090 }
1091 break;
1092
1093 default:
1094 if (!payload.fullyUnpacked())
1095 {
1096 return ipmi::ccReqDataLenInvalid;
1097 }
1098 break;
1099 }
1100 return ipmi::ccSuccess;
1101}
1102
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001103uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service,
1104 const std::string& objPath, uint8_t& sessionState)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001105{
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001106 boost::system::error_code ec = ipmi::getDbusProperty(
1107 ctx, service, objPath, session::sessionIntf, "State", sessionState);
1108 if (ec)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001109 {
George Liu24fffdc2024-07-17 17:40:53 +08001110 lg2::error("Failed to fetch state property, service: {SERVICE}, "
1111 "object path: {OBJECTPATH}, interface: {INTERFACE}, "
1112 "error: {ERROR}",
1113 "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE",
1114 session::sessionIntf, "ERROR", ec.message());
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001115 return ipmi::ccUnspecifiedError;
1116 }
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001117 return ipmi::ccSuccess;
1118}
1119
1120static constexpr uint8_t macAddrLen = 6;
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001121/** Alias SessionDetails - contain the optional information about an
1122 * RMCP+ session.
1123 *
1124 * @param userID - uint6_t session user ID (0-63)
1125 * @param reserved - uint2_t reserved
1126 * @param privilege - uint4_t session privilege (0-5)
1127 * @param reserved - uint4_t reserved
1128 * @param channel - uint4_t session channel number
1129 * @param protocol - uint4_t session protocol
1130 * @param remoteIP - uint32_t remote IP address
1131 * @param macAddr - std::array<uint8_t, 6> mac address
1132 * @param port - uint16_t remote port
1133 */
1134using SessionDetails =
1135 std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t,
1136 std::array<uint8_t, macAddrLen>, uint16_t>;
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001137
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001138/** @brief get session details for a given session
1139 *
1140 * @param[in] ctx - ipmi::Context pointer for accessing D-Bus
1141 * @param[in] service - D-Bus service name to fetch details from
1142 * @param[in] objPath - D-Bus object path for session
1143 * @param[out] sessionHandle - return session handle for session
1144 * @param[out] sessionState - return session state for session
1145 * @param[out] details - return a SessionDetails tuple containing other
1146 * session info
1147 * @return - ipmi::Cc success or error code
1148 */
1149ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service,
1150 const std::string& objPath, uint8_t& sessionHandle,
1151 uint8_t& sessionState, SessionDetails& details)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001152{
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001153 ipmi::PropertyMap sessionProps;
1154 boost::system::error_code ec = ipmi::getAllDbusProperties(
1155 ctx, service, objPath, session::sessionIntf, sessionProps);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001156
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001157 if (ec)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001158 {
George Liu24fffdc2024-07-17 17:40:53 +08001159 lg2::error("Failed to fetch state property, service: {SERVICE}, "
1160 "object path: {OBJECTPATH}, interface: {INTERFACE}, "
1161 "error: {ERROR}",
1162 "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE",
1163 session::sessionIntf, "ERROR", ec.message());
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001164 return ipmi::ccUnspecifiedError;
1165 }
1166
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001167 sessionState = ipmi::mappedVariant<uint8_t>(
1168 sessionProps, "State", static_cast<uint8_t>(session::State::inactive));
1169 if (sessionState == static_cast<uint8_t>(session::State::active))
1170 {
Patrick Williams1318a5e2024-08-16 15:19:54 -04001171 sessionHandle =
1172 ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0);
1173 std::get<0>(details) =
1174 ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001175 // std::get<1>(details) = 0; // (default constructed to 0)
1176 std::get<2>(details) =
1177 ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0);
1178 // std::get<3>(details) = 0; // (default constructed to 0)
Patrick Williams1318a5e2024-08-16 15:19:54 -04001179 std::get<4>(details) =
1180 ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001181 constexpr uint4_t rmcpPlusProtocol = 1;
1182 std::get<5>(details) = rmcpPlusProtocol;
Patrick Williams1318a5e2024-08-16 15:19:54 -04001183 std::get<6>(details) =
1184 ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001185 // std::get<7>(details) = {{0}}; // default constructed to all 0
Patrick Williams1318a5e2024-08-16 15:19:54 -04001186 std::get<8>(details) =
1187 ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001188 }
1189
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001190 return ipmi::ccSuccess;
1191}
1192
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001193ipmi::RspType<uint8_t, // session handle,
1194 uint8_t, // total session count
1195 uint8_t, // active session count
1196 std::optional<SessionDetails>>
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301197 ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex,
1198 ipmi::message::Payload& payload)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001199{
1200 uint32_t reqSessionId = 0;
1201 uint8_t reqSessionHandle = session::defaultSessionHandle;
1202 // initializing state to 0xff as 0 represents state as inactive.
1203 uint8_t state = 0xFF;
1204
1205 uint8_t completionCode = getSessionInfoRequestData(
Rajashekar Gade Reddy4d226402019-11-13 17:13:05 +05301206 ctx, sessionIndex, payload, reqSessionId, reqSessionHandle);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001207
1208 if (completionCode)
1209 {
1210 return ipmi::response(completionCode);
1211 }
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001212 ipmi::ObjectTree objectTree;
1213 boost::system::error_code ec = ipmi::getAllDbusObjects(
1214 ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree);
1215 if (ec)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001216 {
George Liu24fffdc2024-07-17 17:40:53 +08001217 lg2::error("Failed to fetch object from dbus, "
1218 "interface: {INTERFACE}, error: {ERROR}",
1219 "INTERFACE", session::sessionIntf, "ERROR", ec.message());
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001220 return ipmi::responseUnspecifiedError();
1221 }
1222
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001223 uint8_t totalSessionCount = getTotalSessionCount();
1224 uint8_t activeSessionCount = 0;
1225 uint8_t sessionHandle = session::defaultSessionHandle;
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001226 uint8_t activeSessionHandle = 0;
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001227 std::optional<SessionDetails> maybeDetails;
1228 uint8_t index = 0;
1229 for (auto& objectTreeItr : objectTree)
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001230 {
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001231 uint32_t sessionId = 0;
1232 std::string objectPath = objectTreeItr.first;
1233
1234 if (!parseCloseSessionInputPayload(objectPath, sessionId,
1235 sessionHandle))
1236 {
1237 continue;
1238 }
1239 index++;
1240 auto& serviceMap = objectTreeItr.second;
1241 auto itr = serviceMap.begin();
1242
1243 if (serviceMap.size() != 1)
1244 {
1245 return ipmi::responseUnspecifiedError();
1246 }
1247
1248 std::string service = itr->first;
1249 uint8_t sessionState = 0;
Patrick Williams1318a5e2024-08-16 15:19:54 -04001250 completionCode =
1251 getSessionState(ctx, service, objectPath, sessionState);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001252 if (completionCode)
1253 {
1254 return ipmi::response(completionCode);
1255 }
1256
1257 if (sessionState == static_cast<uint8_t>(session::State::active))
1258 {
1259 activeSessionCount++;
1260 }
1261
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001262 if (index == sessionIndex || reqSessionId == sessionId ||
1263 reqSessionHandle == sessionHandle)
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001264 {
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001265 SessionDetails details{};
1266 completionCode = getSessionDetails(ctx, service, objectPath,
1267 sessionHandle, state, details);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001268
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001269 if (completionCode)
1270 {
1271 return ipmi::response(completionCode);
1272 }
1273 activeSessionHandle = sessionHandle;
1274 maybeDetails = std::move(details);
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001275 }
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001276 }
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001277
1278 if (state == static_cast<uint8_t>(session::State::active) ||
1279 state == static_cast<uint8_t>(session::State::tearDownInProgress))
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001280 {
Johnathan Manteyca93bb12022-12-02 13:33:17 -08001281 return ipmi::responseSuccess(activeSessionHandle, totalSessionCount,
Vernon Mauerye7e8b812019-10-28 16:00:34 -07001282 activeSessionCount, maybeDetails);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001283 }
1284
1285 return ipmi::responseInvalidFieldRequest();
1286}
1287
Thang Trana809fa52024-08-08 14:46:34 +07001288std::optional<std::string> getSysFWVersion(ipmi::Context::ptr& ctx)
1289{
1290 /*
1291 * The System Firmware version is detected via following steps:
1292 * - Get all of object paths that include
1293 * "xyz.openbmc_project.Software.Version" interface.
1294 * - Get the Purpose property of above object paths.
1295 * - If the Purpose is Host then get the Version property.
1296 */
1297 ipmi::ObjectTree objectTree;
1298 boost::system::error_code ec =
1299 ipmi::getAllDbusObjects(ctx, softwareRoot, versionIntf, objectTree);
1300 if (ec.value())
1301 {
1302 return std::nullopt;
1303 }
1304
1305 for (const auto& [objPath, serviceMap] : objectTree)
1306 {
1307 for (const auto& [service, intfs] : serviceMap)
1308 {
1309 ipmi::PropertyMap props;
1310 ec = ipmi::getAllDbusProperties(ctx, service, objPath, versionIntf,
1311 props);
1312 if (ec.value())
1313 {
1314 continue;
1315 }
1316
1317 std::string purposeProp = std::string(
1318 ipmi::mappedVariant<std::string>(props, "Purpose", ""));
1319
1320 if (!purposeProp.ends_with(versionPurposeHostEnd))
1321 {
1322 continue;
1323 }
1324
1325 std::string sysFWVersion = std::string(
1326 ipmi::mappedVariant<std::string>(props, "Version", ""));
1327
1328 if (sysFWVersion.empty())
1329 {
1330 return std::nullopt;
1331 }
1332
1333 return sysFWVersion;
1334 }
1335 }
1336
1337 return std::nullopt;
1338}
1339
Xo Wangf542e8b2017-08-09 15:34:16 -07001340static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
1341
Xo Wang87651332017-08-11 10:17:59 -07001342static std::string sysInfoReadSystemName()
1343{
1344 // Use the BMC hostname as the "System Name."
1345 char hostname[HOST_NAME_MAX + 1] = {};
1346 if (gethostname(hostname, HOST_NAME_MAX) != 0)
1347 {
1348 perror("System info parameter: system name");
1349 }
1350 return hostname;
1351}
1352
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001353static constexpr uint8_t paramRevision = 0x11;
1354static constexpr size_t configParameterLength = 16;
Xo Wangf542e8b2017-08-09 15:34:16 -07001355
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001356static constexpr size_t smallChunkSize = 14;
1357static constexpr size_t fullChunkSize = 16;
Jia, chunhui449f2162019-09-11 16:51:51 +08001358static constexpr uint8_t progressMask = 0x3;
krishnar410752832023-11-06 13:58:35 +05301359static constexpr uint8_t maxValidEncodingData = 0x02;
Xo Wangf542e8b2017-08-09 15:34:16 -07001360
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001361static constexpr uint8_t setComplete = 0x0;
1362static constexpr uint8_t setInProgress = 0x1;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001363static uint8_t transferStatus = setComplete;
1364
Jia, chunhui449f2162019-09-11 16:51:51 +08001365static constexpr uint8_t configDataOverhead = 2;
1366
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001367namespace ipmi
1368{
1369constexpr Cc ccParmNotSupported = 0x80;
Jia, chunhui449f2162019-09-11 16:51:51 +08001370constexpr Cc ccSetInProgressActive = 0x81;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001371
1372static inline auto responseParmNotSupported()
1373{
1374 return response(ccParmNotSupported);
Xo Wangf542e8b2017-08-09 15:34:16 -07001375}
Jia, chunhui449f2162019-09-11 16:51:51 +08001376static inline auto responseSetInProgressActive()
1377{
1378 return response(ccSetInProgressActive);
1379}
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001380} // namespace ipmi
Xo Wangf542e8b2017-08-09 15:34:16 -07001381
Jia, chunhui449f2162019-09-11 16:51:51 +08001382ipmi::RspType<uint8_t, // Parameter revision
1383 std::optional<uint8_t>, // data1 / setSelector / ProgressStatus
1384 std::optional<std::vector<uint8_t>>> // data2-17
Thang Trana809fa52024-08-08 14:46:34 +07001385 ipmiAppGetSystemInfo(ipmi::Context::ptr ctx, uint7_t reserved,
1386 bool getRevision, uint8_t paramSelector,
1387 uint8_t setSelector, uint8_t BlockSelector)
Xo Wangf542e8b2017-08-09 15:34:16 -07001388{
Snehalatha Va5ae7722020-05-02 18:18:57 +00001389 if (reserved || (paramSelector >= invalidParamSelectorStart &&
1390 paramSelector <= invalidParamSelectorEnd))
jayaprakash Mutyalac2566a92020-04-23 21:18:35 +00001391 {
1392 return ipmi::responseInvalidFieldRequest();
1393 }
PavanKumarIntel3771f5f2023-11-02 06:26:42 +00001394 if (paramSelector >= oemCmdStart)
Snehalatha Va5ae7722020-05-02 18:18:57 +00001395 {
1396 return ipmi::responseParmNotSupported();
1397 }
jayaprakash Mutyalac2566a92020-04-23 21:18:35 +00001398 if (getRevision)
Xo Wangf542e8b2017-08-09 15:34:16 -07001399 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001400 return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt);
Xo Wangf542e8b2017-08-09 15:34:16 -07001401 }
1402
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001403 if (paramSelector == 0)
Xo Wangf542e8b2017-08-09 15:34:16 -07001404 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001405 return ipmi::responseSuccess(paramRevision, transferStatus,
1406 std::nullopt);
Xo Wangf542e8b2017-08-09 15:34:16 -07001407 }
1408
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001409 if (BlockSelector != 0) // 00h if parameter does not require a block number
Xo Wangf542e8b2017-08-09 15:34:16 -07001410 {
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001411 return ipmi::responseParmNotSupported();
Xo Wangf542e8b2017-08-09 15:34:16 -07001412 }
1413
1414 if (sysInfoParamStore == nullptr)
1415 {
1416 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
Xo Wang87651332017-08-11 10:17:59 -07001417 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1418 sysInfoReadSystemName);
Xo Wangf542e8b2017-08-09 15:34:16 -07001419 }
1420
Thang Trana809fa52024-08-08 14:46:34 +07001421 if (paramSelector == IPMI_SYSINFO_SYSTEM_FW_VERSION)
1422 {
Prithvi Paid20670b2025-08-01 20:11:28 +05301423 // If the system firmware version is not cached, get it from the D-Bus.
1424 const auto& [found, paramString] =
1425 sysInfoParamStore->lookup(IPMI_SYSINFO_SYSTEM_FW_VERSION);
Thang Trana809fa52024-08-08 14:46:34 +07001426
Prithvi Paid20670b2025-08-01 20:11:28 +05301427 if (!found || paramString.empty())
Thang Trana809fa52024-08-08 14:46:34 +07001428 {
Prithvi Paid20670b2025-08-01 20:11:28 +05301429 auto fwVersion = getSysFWVersion(ctx);
1430 if (fwVersion == std::nullopt)
1431 {
1432 return ipmi::responseUnspecifiedError();
1433 }
1434 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_FW_VERSION,
1435 *fwVersion);
Thang Trana809fa52024-08-08 14:46:34 +07001436 }
Thang Trana809fa52024-08-08 14:46:34 +07001437 }
1438
Xo Wangf542e8b2017-08-09 15:34:16 -07001439 // Parameters other than Set In Progress are assumed to be strings.
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001440 std::tuple<bool, std::string> ret =
1441 sysInfoParamStore->lookup(paramSelector);
1442 bool found = std::get<0>(ret);
Xo Wangf542e8b2017-08-09 15:34:16 -07001443 if (!found)
1444 {
Snehalatha Va5ae7722020-05-02 18:18:57 +00001445 return ipmi::responseSensorInvalid();
Xo Wangf542e8b2017-08-09 15:34:16 -07001446 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001447 std::string& paramString = std::get<1>(ret);
Jia, chunhui449f2162019-09-11 16:51:51 +08001448 std::vector<uint8_t> configData;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001449 size_t count = 0;
1450 if (setSelector == 0)
Jia, chunhui449f2162019-09-11 16:51:51 +08001451 { // First chunk has only 14 bytes.
1452 configData.emplace_back(0); // encoding
1453 configData.emplace_back(paramString.length()); // string length
1454 count = std::min(paramString.length(), smallChunkSize);
1455 configData.resize(count + configDataOverhead);
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001456 std::copy_n(paramString.begin(), count,
Snehalatha Va5ae7722020-05-02 18:18:57 +00001457 configData.begin() + configDataOverhead); // 14 bytes chunk
1458
1459 // Append zero's to remaining bytes
1460 if (configData.size() < configParameterLength)
1461 {
1462 std::fill_n(std::back_inserter(configData),
1463 configParameterLength - configData.size(), 0x00);
1464 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001465 }
1466 else
Xo Wangf542e8b2017-08-09 15:34:16 -07001467 {
Jia, chunhui449f2162019-09-11 16:51:51 +08001468 size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001469 if (offset >= paramString.length())
1470 {
1471 return ipmi::responseParmOutOfRange();
1472 }
Jia, chunhui449f2162019-09-11 16:51:51 +08001473 count = std::min(paramString.length() - offset, fullChunkSize);
1474 configData.resize(count);
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001475 std::copy_n(paramString.begin() + offset, count,
1476 configData.begin()); // 16 bytes chunk
Xo Wangf542e8b2017-08-09 15:34:16 -07001477 }
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001478 return ipmi::responseSuccess(paramRevision, setSelector, configData);
Xo Wangf542e8b2017-08-09 15:34:16 -07001479}
1480
Jia, chunhui449f2162019-09-11 16:51:51 +08001481ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1,
1482 std::vector<uint8_t> configData)
1483{
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +00001484 if (paramSelector >= invalidParamSelectorStart &&
1485 paramSelector <= invalidParamSelectorEnd)
1486 {
1487 return ipmi::responseInvalidFieldRequest();
1488 }
PavanKumarIntel3771f5f2023-11-02 06:26:42 +00001489 if (paramSelector >= oemCmdStart)
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +00001490 {
1491 return ipmi::responseParmNotSupported();
1492 }
1493
Jia, chunhui449f2162019-09-11 16:51:51 +08001494 if (paramSelector == 0)
1495 {
1496 // attempt to set the 'set in progress' value (in parameter #0)
1497 // when not in the set complete state.
1498 if ((transferStatus != setComplete) && (data1 == setInProgress))
1499 {
1500 return ipmi::responseSetInProgressActive();
1501 }
1502 // only following 2 states are supported
1503 if (data1 > setInProgress)
1504 {
George Liu24fffdc2024-07-17 17:40:53 +08001505 lg2::error("illegal SetInProgress status");
Jia, chunhui449f2162019-09-11 16:51:51 +08001506 return ipmi::responseInvalidFieldRequest();
1507 }
1508
1509 transferStatus = data1 & progressMask;
1510 return ipmi::responseSuccess();
1511 }
1512
1513 if (configData.size() > configParameterLength)
1514 {
1515 return ipmi::responseInvalidFieldRequest();
1516 }
1517
jayaprakash Mutyala3c5e4132020-04-27 23:00:05 +00001518 // Append zero's to remaining bytes
1519 if (configData.size() < configParameterLength)
1520 {
1521 fill_n(back_inserter(configData),
1522 (configParameterLength - configData.size()), 0x00);
1523 }
1524
Jia, chunhui449f2162019-09-11 16:51:51 +08001525 if (!sysInfoParamStore)
1526 {
1527 sysInfoParamStore = std::make_unique<SysInfoParamStore>();
1528 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME,
1529 sysInfoReadSystemName);
1530 }
1531
1532 // lookup
1533 std::tuple<bool, std::string> ret =
1534 sysInfoParamStore->lookup(paramSelector);
1535 bool found = std::get<0>(ret);
1536 std::string& paramString = std::get<1>(ret);
1537 if (!found)
1538 {
1539 // parameter does not exist. Init new
1540 paramString = "";
1541 }
1542
1543 uint8_t setSelector = data1;
1544 size_t count = 0;
krishnar410752832023-11-06 13:58:35 +05301545 if (setSelector == 0) // First chunk has only 14 bytes.
Jia, chunhui449f2162019-09-11 16:51:51 +08001546 {
krishnar410752832023-11-06 13:58:35 +05301547 uint8_t encoding = configData.at(0);
1548 if (encoding > maxValidEncodingData)
1549 {
1550 return ipmi::responseInvalidFieldRequest();
1551 }
1552
Jia, chunhui449f2162019-09-11 16:51:51 +08001553 size_t stringLen = configData.at(1); // string length
Jia, chunhui449f2162019-09-11 16:51:51 +08001554 count = std::min(stringLen, smallChunkSize);
1555 count = std::min(count, configData.size());
1556 paramString.resize(stringLen); // reserve space
1557 std::copy_n(configData.begin() + configDataOverhead, count,
1558 paramString.begin());
1559 }
1560 else
1561 {
1562 size_t offset = (setSelector * fullChunkSize) - configDataOverhead;
1563 if (offset >= paramString.length())
1564 {
1565 return ipmi::responseParmOutOfRange();
1566 }
1567 count = std::min(paramString.length() - offset, configData.size());
1568 std::copy_n(configData.begin(), count, paramString.begin() + offset);
1569 }
1570 sysInfoParamStore->update(paramSelector, paramString);
1571 return ipmi::responseSuccess();
1572}
1573
Yong Libd0503a2019-08-22 17:17:17 +08001574#ifdef ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301575inline std::vector<uint8_t> convertStringToData(const std::string& command)
1576{
1577 std::istringstream iss(command);
1578 std::string token;
1579 std::vector<uint8_t> dataValue;
1580 while (std::getline(iss, token, ' '))
1581 {
1582 dataValue.emplace_back(
1583 static_cast<uint8_t>(std::stoul(token, nullptr, base_16)));
1584 }
1585 return dataValue;
1586}
1587
Matt Simmering68d9d402023-11-09 14:22:11 -08001588static bool populateI2CControllerWRAllowlist()
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301589{
1590 nlohmann::json data = nullptr;
Matt Simmering68d9d402023-11-09 14:22:11 -08001591 std::ifstream jsonFile(i2cControllerWRAllowlistFile);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301592
1593 if (!jsonFile.good())
1594 {
George Liu24fffdc2024-07-17 17:40:53 +08001595 lg2::warning("i2c allow list file not found! file name: {FILE_NAME}",
1596 "FILE_NAME", i2cControllerWRAllowlistFile);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301597 return false;
1598 }
1599
1600 try
1601 {
1602 data = nlohmann::json::parse(jsonFile, nullptr, false);
1603 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001604 catch (const nlohmann::json::parse_error& e)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301605 {
George Liu24fffdc2024-07-17 17:40:53 +08001606 lg2::error("Corrupted i2c allow list config file, "
1607 "file name: {FILE_NAME}, error: {ERROR}",
1608 "FILE_NAME", i2cControllerWRAllowlistFile, "ERROR", e);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301609 return false;
1610 }
1611
1612 try
1613 {
1614 // Example JSON Structure format
1615 // "filters": [
1616 // {
1617 // "Description": "Allow full read - ignore first byte write value
1618 // for 0x40 to 0x4F",
1619 // "busId": "0x01",
1620 // "slaveAddr": "0x40",
1621 // "slaveAddrMask": "0x0F",
1622 // "command": "0x00",
1623 // "commandMask": "0xFF"
1624 // },
1625 // {
1626 // "Description": "Allow full read - first byte match 0x05 and
1627 // ignore second byte",
1628 // "busId": "0x01",
1629 // "slaveAddr": "0x57",
1630 // "slaveAddrMask": "0x00",
1631 // "command": "0x05 0x00",
1632 // "commandMask": "0x00 0xFF"
1633 // },]
1634
1635 nlohmann::json filters = data[filtersStr].get<nlohmann::json>();
Matt Simmering68d9d402023-11-09 14:22:11 -08001636 std::vector<i2cControllerWRAllowlist>& allowlist = getWRAllowlist();
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301637 for (const auto& it : filters.items())
1638 {
1639 nlohmann::json filter = it.value();
1640 if (filter.is_null())
1641 {
George Liu24fffdc2024-07-17 17:40:53 +08001642 lg2::error(
1643 "Corrupted I2C controller write read allowlist config file, "
1644 "file name: {FILE_NAME}",
1645 "FILE_NAME", i2cControllerWRAllowlistFile);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301646 return false;
1647 }
1648 const std::vector<uint8_t>& writeData =
1649 convertStringToData(filter[cmdStr].get<std::string>());
1650 const std::vector<uint8_t>& writeDataMask =
1651 convertStringToData(filter[cmdMaskStr].get<std::string>());
1652 if (writeDataMask.size() != writeData.size())
1653 {
George Liu24fffdc2024-07-17 17:40:53 +08001654 lg2::error("I2C controller write read allowlist filter "
1655 "mismatch for command & mask size");
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301656 return false;
1657 }
Matt Simmering68d9d402023-11-09 14:22:11 -08001658 allowlist.push_back(
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301659 {static_cast<uint8_t>(std::stoul(
1660 filter[busIdStr].get<std::string>(), nullptr, base_16)),
1661 static_cast<uint8_t>(
Matt Simmering68d9d402023-11-09 14:22:11 -08001662 std::stoul(filter[targetAddrStr].get<std::string>(),
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301663 nullptr, base_16)),
1664 static_cast<uint8_t>(
Matt Simmering68d9d402023-11-09 14:22:11 -08001665 std::stoul(filter[targetAddrMaskStr].get<std::string>(),
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301666 nullptr, base_16)),
1667 writeData, writeDataMask});
1668 }
Matt Simmering68d9d402023-11-09 14:22:11 -08001669 if (allowlist.size() != filters.size())
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301670 {
George Liu24fffdc2024-07-17 17:40:53 +08001671 lg2::error(
Matt Simmering68d9d402023-11-09 14:22:11 -08001672 "I2C controller write read allowlist filter size mismatch");
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301673 return false;
1674 }
1675 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -05001676 catch (const std::exception& e)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301677 {
George Liu24fffdc2024-07-17 17:40:53 +08001678 lg2::error("I2C controller write read allowlist "
1679 "unexpected exception: {ERROR}",
1680 "ERROR", e);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301681 return false;
1682 }
1683 return true;
1684}
1685
Matt Simmering68d9d402023-11-09 14:22:11 -08001686static inline bool isWriteDataAllowlisted(const std::vector<uint8_t>& data,
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301687 const std::vector<uint8_t>& dataMask,
1688 const std::vector<uint8_t>& writeData)
1689{
1690 std::vector<uint8_t> processedDataBuf(data.size());
1691 std::vector<uint8_t> processedReqBuf(dataMask.size());
1692 std::transform(writeData.begin(), writeData.end(), dataMask.begin(),
1693 processedReqBuf.begin(), std::bit_or<uint8_t>());
1694 std::transform(data.begin(), data.end(), dataMask.begin(),
1695 processedDataBuf.begin(), std::bit_or<uint8_t>());
1696
1697 return (processedDataBuf == processedReqBuf);
1698}
1699
Matt Simmering68d9d402023-11-09 14:22:11 -08001700static bool isCmdAllowlisted(uint8_t busId, uint8_t targetAddr,
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301701 std::vector<uint8_t>& writeData)
1702{
Matt Simmering68d9d402023-11-09 14:22:11 -08001703 std::vector<i2cControllerWRAllowlist>& allowList = getWRAllowlist();
1704 for (const auto& wlEntry : allowList)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301705 {
1706 if ((busId == wlEntry.busId) &&
Matt Simmering68d9d402023-11-09 14:22:11 -08001707 ((targetAddr | wlEntry.targetAddrMask) ==
1708 (wlEntry.targetAddr | wlEntry.targetAddrMask)))
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301709 {
1710 const std::vector<uint8_t>& dataMask = wlEntry.dataMask;
1711 // Skip as no-match, if requested write data is more than the
1712 // write data mask size
1713 if (writeData.size() > dataMask.size())
1714 {
1715 continue;
1716 }
Matt Simmering68d9d402023-11-09 14:22:11 -08001717 if (isWriteDataAllowlisted(wlEntry.data, dataMask, writeData))
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301718 {
1719 return true;
1720 }
1721 }
1722 }
1723 return false;
1724}
Yong Libd0503a2019-08-22 17:17:17 +08001725#else
Matt Simmering68d9d402023-11-09 14:22:11 -08001726static bool populateI2CControllerWRAllowlist()
Yong Libd0503a2019-08-22 17:17:17 +08001727{
George Liu24fffdc2024-07-17 17:40:53 +08001728 lg2::info("I2C_WHITELIST_CHECK is disabled, do not populate allowlist");
Yong Libd0503a2019-08-22 17:17:17 +08001729 return true;
1730}
1731#endif // ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301732
Matt Simmering68d9d402023-11-09 14:22:11 -08001733/** @brief implements controller write read IPMI command which can be used for
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301734 * low-level I2C/SMBus write, read or write-read access
1735 * @param isPrivateBus -to indicate private bus usage
1736 * @param busId - bus id
1737 * @param channelNum - channel number
1738 * @param reserved - skip 1 bit
Matt Simmering68d9d402023-11-09 14:22:11 -08001739 * @param targetAddr - target address
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301740 * @param read count - number of bytes to be read
1741 * @param writeData - data to be written
1742 *
1743 * @returns IPMI completion code plus response data
1744 * - readData - i2c response data
1745 */
Patrick Williams1318a5e2024-08-16 15:19:54 -04001746ipmi::RspType<std::vector<uint8_t>> ipmiControllerWriteRead(
1747 [[maybe_unused]] bool isPrivateBus, uint3_t busId,
1748 [[maybe_unused]] uint4_t channelNum, bool reserved, uint7_t targetAddr,
1749 uint8_t readCount, std::vector<uint8_t> writeData)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301750{
Jayaprakash Mutyalac2af98b2021-09-14 09:19:11 +00001751 if (reserved)
1752 {
1753 return ipmi::responseInvalidFieldRequest();
1754 }
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301755 const size_t writeCount = writeData.size();
1756 if (!readCount && !writeCount)
1757 {
George Liu24fffdc2024-07-17 17:40:53 +08001758 lg2::error("Controller write read command: Read & write count are 0");
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301759 return ipmi::responseInvalidFieldRequest();
1760 }
Yong Libd0503a2019-08-22 17:17:17 +08001761#ifdef ENABLE_I2C_WHITELIST_CHECK
Matt Simmering68d9d402023-11-09 14:22:11 -08001762 if (!isCmdAllowlisted(static_cast<uint8_t>(busId),
1763 static_cast<uint8_t>(targetAddr), writeData))
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301764 {
George Liu24fffdc2024-07-17 17:40:53 +08001765 lg2::error("Controller write read request blocked!, "
1766 "bus: {BUS}, addr: {ADDR}",
1767 "BUS", static_cast<uint8_t>(busId), "ADDR", lg2::hex,
1768 static_cast<uint8_t>(targetAddr));
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301769 }
Yong Libd0503a2019-08-22 17:17:17 +08001770#endif // ENABLE_I2C_WHITELIST_CHECK
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301771 std::vector<uint8_t> readBuf(readCount);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001772 std::string i2cBus =
1773 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId));
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301774
Matt Simmering68d9d402023-11-09 14:22:11 -08001775 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(targetAddr),
Yong Li7dc4ac02019-08-23 17:44:32 +08001776 writeData, readBuf);
1777 if (ret != ipmi::ccSuccess)
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301778 {
Yong Li7dc4ac02019-08-23 17:44:32 +08001779 return ipmi::response(ret);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301780 }
1781 return ipmi::responseSuccess(readBuf);
1782}
1783
George Liu5087b072025-03-11 19:28:17 +08001784void registerNetFnAppFunctions()
vishwabmcba0bd5f2015-09-30 16:50:23 +05301785{
Vernon Mauery86a50822019-03-25 13:11:36 -07001786 // <Get Device ID>
1787 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1788 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User,
1789 ipmiAppGetDeviceId);
1790
Tom05732372016-09-06 17:21:23 +05301791 // <Get BT Interface Capabilities>
Vernon Mauerycc99ba42019-03-25 13:40:11 -07001792 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1793 ipmi::app::cmdGetBtIfaceCapabilities,
1794 ipmi::Privilege::User, ipmiAppGetBtCapabilities);
Chris Austen6caf28b2015-10-13 12:40:40 -05001795
Tom05732372016-09-06 17:21:23 +05301796 // <Reset Watchdog Timer>
Vernon Mauery11df4f62019-03-25 14:17:54 -07001797 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1798 ipmi::app::cmdResetWatchdogTimer,
1799 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001800
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001801 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
AppaRao Puli5a98ea62019-11-10 21:15:02 +05301802 ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User,
1803 ipmiAppGetSessionInfo);
Rajashekar Gade Reddyf71444d2019-07-25 15:12:17 +00001804
Tom05732372016-09-06 17:21:23 +05301805 // <Set Watchdog Timer>
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +00001806 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1807 ipmi::app::cmdSetWatchdogTimer,
1808 ipmi::Privilege::Operator, ipmiSetWatchdogTimer);
Chris Austen6caf28b2015-10-13 12:40:40 -05001809
Rajashekar Gade Reddye7023922019-07-10 16:54:55 +00001810 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1811 ipmi::app::cmdCloseSession, ipmi::Privilege::Callback,
1812 ipmiAppCloseSession);
1813
William A. Kennington III73f44512018-02-09 15:28:46 -08001814 // <Get Watchdog Timer>
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +00001815 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
AppaRao Puli5a98ea62019-11-10 21:15:02 +05301816 ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User,
1817 ipmiGetWatchdogTimer);
William A. Kennington III73f44512018-02-09 15:28:46 -08001818
Tom05732372016-09-06 17:21:23 +05301819 // <Get Self Test Results>
Vernon Maueryb84a5282019-03-25 13:39:03 -07001820 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1821 ipmi::app::cmdGetSelfTestResults,
1822 ipmi::Privilege::User, ipmiAppGetSelfTestResults);
Nan Li41fa24a2016-11-10 20:12:37 +08001823
Tom05732372016-09-06 17:21:23 +05301824 // <Get Device GUID>
Vernon Mauery15541322019-03-25 13:33:03 -07001825 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1826 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User,
1827 ipmiAppGetDeviceGuid);
Adriana Kobylakd100ee52015-10-20 17:02:37 -05001828
Tom05732372016-09-06 17:21:23 +05301829 // <Set ACPI Power State>
Deepak Kumar Sahu520c1312019-05-17 18:14:09 +00001830 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1831 ipmi::app::cmdSetAcpiPowerState,
1832 ipmi::Privilege::Admin, ipmiSetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001833 // <Get ACPI Power State>
Deepak Kumar Sahu4e6d2572019-05-07 19:53:37 +00001834 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1835 ipmi::app::cmdGetAcpiPowerState,
AppaRao Puli5a98ea62019-11-10 21:15:02 +05301836 ipmi::Privilege::User, ipmiGetAcpiPowerState);
Yong Li18d77262018-10-09 01:59:45 +08001837
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301838 // Note: For security reason, this command will be registered only when
Matt Simmering68d9d402023-11-09 14:22:11 -08001839 // there are proper I2C Controller write read allowlist
1840 if (populateI2CControllerWRAllowlist())
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301841 {
Matt Simmering68d9d402023-11-09 14:22:11 -08001842 // Note: For security reasons, registering controller write read as
1843 // admin privilege command, even though IPMI 2.0 specification allows it
1844 // as operator privilege.
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301845 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1846 ipmi::app::cmdMasterWriteRead,
Matt Simmering68d9d402023-11-09 14:22:11 -08001847 ipmi::Privilege::Admin, ipmiControllerWriteRead);
Richard Marian Thomaiyar84bf9be2019-04-26 22:59:16 +05301848 }
1849
Marri Devender Rao5e007a52018-01-08 06:18:36 -06001850 // <Get System GUID Command>
Vernon Maueryb90a5322019-03-25 13:36:55 -07001851 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1852 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
1853 ipmiAppGetSystemGuid);
Tom Joseph7cbe2282018-03-21 21:17:33 +05301854
1855 // <Get Channel Cipher Suites Command>
Ayushi Smriti5c3b72c2019-08-30 13:47:31 +00001856 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1857 ipmi::app::cmdGetChannelCipherSuites,
Vernon Mauery79b4eea2019-11-07 09:51:39 -08001858 ipmi::Privilege::None, getChannelCipherSuites);
Vernon Maueryd2a57de2019-03-25 12:45:28 -07001859
Xo Wangf542e8b2017-08-09 15:34:16 -07001860 // <Get System Info Command>
Jia, chunhui98b43ba2019-09-05 15:54:23 +08001861 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1862 ipmi::app::cmdGetSystemInfoParameters,
1863 ipmi::Privilege::User, ipmiAppGetSystemInfo);
Jia, chunhui449f2162019-09-11 16:51:51 +08001864 // <Set System Info Command>
1865 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
1866 ipmi::app::cmdSetSystemInfoParameters,
1867 ipmi::Privilege::Admin, ipmiAppSetSystemInfo);
vishwabmcba0bd5f2015-09-30 16:50:23 +05301868 return;
1869}