blob: 10812f897adea5961e0ff01ba5eeda4b08947783 [file] [log] [blame]
Hieu Huynh1463f702021-09-23 03:14:59 +00001/*
2 * Copyright (c) 2018-2021 Ampere Computing LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Thang Tran2a191522022-11-01 18:09:37 +070017#include "oemcommands.hpp"
18
19#include <boost/container/flat_map.hpp>
Hieu Huynh1463f702021-09-23 03:14:59 +000020#include <ipmid/api.hpp>
21#include <ipmid/types.hpp>
22#include <ipmid/utils.hpp>
23#include <phosphor-logging/log.hpp>
Thang Tran2a191522022-11-01 18:09:37 +070024
Hieu Huynh1463f702021-09-23 03:14:59 +000025#include <cstdlib>
26
27using namespace phosphor::logging;
28
Thang Tran2a191522022-11-01 18:09:37 +070029using BasicVariantType =
30 std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
31 double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
32using FruObjectType = boost::container::flat_map<
33 sdbusplus::message::object_path,
34 boost::container::flat_map<
35 std::string,
36 boost::container::flat_map<std::string, BasicVariantType>>>;
37
38constexpr static const char* fruDeviceServiceName =
39 "xyz.openbmc_project.FruDevice";
40
41constexpr static const char* chassisTypeRackMount = "23";
42
Hieu Huynh1463f702021-09-23 03:14:59 +000043static inline auto response(uint8_t cc)
44{
45 return std::make_tuple(cc, std::nullopt);
46}
47
48static inline auto responseFailure()
49{
50 return response(responseFail);
51}
52
Thang Tran2a191522022-11-01 18:09:37 +070053/** @brief get Baseboard FRU's address
54 * @param - busIdx, address of I2C device
55 * @returns - true if successfully, false if fail
56 */
57[[maybe_unused]] static bool getBaseBoardFRUAddr(uint8_t& busIdx, uint8_t& addr)
58{
59 bool retVal = false;
60 sd_bus* bus = NULL;
61 FruObjectType fruObjects;
62
63 /*
64 * Read all managed objects of FRU device
65 */
66 int ret = sd_bus_default_system(&bus);
67 if (ret < 0)
68 {
69 phosphor::logging::log<phosphor::logging::level::ERR>(
70 "Failed to connect to system bus");
71 sd_bus_unref(bus);
72 return false;
73 }
74 sdbusplus::bus::bus dbus(bus);
75 auto mapperCall = dbus.new_method_call(fruDeviceServiceName, "/",
76 "org.freedesktop.DBus.ObjectManager",
77 "GetManagedObjects");
78
79 try
80 {
81 auto mapperReply = dbus.call(mapperCall);
82 mapperReply.read(fruObjects);
83 }
84 catch (sdbusplus::exception_t& e)
85 {
86 log<level::ERR>("Fail to call GetManagedObjects method");
87
88 sd_bus_unref(bus);
89 return false;
90 }
91
92 /*
93 * Scan all FRU objects to find out baseboard FRU device.
Thang Q. Nguyenee2e1df2024-06-24 08:59:42 +070094 * The basedboard FRU device is indicated by chassis type
Thang Tran2a191522022-11-01 18:09:37 +070095 * is Rack Mount - "23"
96 */
97 for (const auto& fruObj : fruObjects)
98 {
99 auto fruDeviceInf = fruObj.second.find("xyz.openbmc_project.FruDevice");
100
101 if (fruDeviceInf != fruObj.second.end())
102 {
103 auto chassisProperty = fruDeviceInf->second.find("CHASSIS_TYPE");
104
105 if (chassisProperty != fruDeviceInf->second.end())
106 {
107 std::string chassisType =
108 std::get<std::string>(chassisProperty->second);
109 auto busProperty = fruDeviceInf->second.find("BUS");
110 auto addrProperty = fruDeviceInf->second.find("ADDRESS");
111
112 if ((0 == chassisType.compare(chassisTypeRackMount)) &&
113 (busProperty != fruDeviceInf->second.end()) &&
114 (addrProperty != fruDeviceInf->second.end()))
115 {
116 busIdx = (uint8_t)std::get<uint32_t>(busProperty->second);
117 addr = (uint8_t)std::get<uint32_t>(addrProperty->second);
118 retVal = true;
119 break;
120 }
121 }
122 }
123 }
124
125 sd_bus_unref(bus);
126 return retVal;
127}
128
129/** @brief get Raw FRU's data
130 * @param - busIdx, address of I2C device.
131 * - fruData: data have been read
132 * @returns - true if successfully, false if fail
133 */
134static bool getRawFruData(uint8_t busIdx, uint8_t addr,
135 std::vector<uint8_t>& fruData)
136{
137 bool retVal = false;
138 sd_bus* bus = NULL;
139 int ret = sd_bus_default_system(&bus);
140
141 if (ret < 0)
142 {
143 phosphor::logging::log<phosphor::logging::level::ERR>(
144 "Failed to connect to system bus",
145 phosphor::logging::entry("ERRNO=0x%X", -ret));
146 }
147 else
148 {
149 sdbusplus::bus::bus dbus(bus);
150 auto MapperCall = dbus.new_method_call(
151 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
152 "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
153
154 MapperCall.append(busIdx, addr);
155
156 try
157 {
158 auto mapperReply = dbus.call(MapperCall);
159 mapperReply.read(fruData);
160 retVal = true;
161 }
162 catch (sdbusplus::exception_t& e)
163 {
164 log<level::ERR>("Fail to read Raw FRU data from system bus\n");
165 }
166 }
167
168 sd_bus_unref(bus);
169
170 return retVal;
171}
172
173/** @brief update MAC address information in FRU data
174 * @param - fruData: FRU data
175 * - macAddress: MAC address information
176 * @returns - true if successfully, false if fail
177 */
178static bool updateMACAddrInFRU(std::vector<uint8_t>& fruData,
179 std::vector<uint8_t> macAddress)
180{
181 bool retVal = false;
182 uint32_t areaOffset = fruData[3] * 8; /* Board area start offset */
183 char macAddressStr[18];
184 uint32_t boardLeng = 0;
185 uint8_t checkSumVal = 0;
186
187 /*
188 * Update MAC address at first custom field of Board Information Area.
189 */
190 if (areaOffset != 0)
191 {
192 /*
193 * The Board Manufacturer type/length byte is stored
194 * at byte 0x06 of Board area.
195 */
196 uint32_t fieldOffset = areaOffset + 6;
197
198 /*
199 * Scan all 5 predefined fields of Board area to jump to
200 * first Custom field.
201 */
202 for (uint32_t i = 0; i < 5; i++)
203 {
204 fieldOffset += (fruData[fieldOffset] & 0x3f) + 1;
205 }
206
207 /*
208 * Update the MAC address information when type/length is not
209 * EndOfField byte and the length of Custom field is 17.
210 */
211 if ((fruData[fieldOffset] != 0xc1) &&
212 ((uint8_t)17 == (fruData[fieldOffset] & (uint8_t)0x3f)))
213 {
214 sprintf(macAddressStr, "%02X:%02X:%02X:%02X:%02X:%02X",
215 macAddress[0], macAddress[1], macAddress[2], macAddress[3],
216 macAddress[4], macAddress[5]);
217
218 /*
219 * Update 17 bytes of MAC address information
220 */
221 fieldOffset++;
222 for (uint32_t i = 0; i < 17; i++)
223 {
224 fruData[fieldOffset + i] = macAddressStr[i];
225 }
226
227 /*
228 * Re-caculate the checksum of Board Information Area.
229 */
230 boardLeng = fruData[areaOffset + 1] * 8;
231 for (uint32_t i = 0; i < boardLeng - 1; i++)
232 {
233 checkSumVal += fruData[areaOffset + i];
234 }
235
236 checkSumVal = ~checkSumVal + 1;
237 fruData[areaOffset + boardLeng - 1] = checkSumVal;
238
239 retVal = true;
240 }
241 else
242 {
243 phosphor::logging::log<phosphor::logging::level::ERR>(
244 "FRU does not include MAC address information");
245 }
246 }
247 else
248 {
249 phosphor::logging::log<phosphor::logging::level::ERR>(
250 "FRU does not include Board Information Area");
251 }
252
253 return retVal;
254}
255
256/** @brief write FRU data to EEPROM
257 * @param - busIdx and address of I2C device.
258 * - fruData: FRU data
259 * @returns - true if successfully, false if fail
260 */
261static bool writeFruData(uint8_t busIdx, uint8_t addr,
262 std::vector<uint8_t>& fruData)
263{
264 bool retVal = false;
265 sd_bus* bus = NULL;
266 int ret = sd_bus_default_system(&bus);
267
268 if (ret < 0)
269 {
270 phosphor::logging::log<phosphor::logging::level::ERR>(
271 "Failed to connect to system bus",
272 phosphor::logging::entry("ERRNO=0x%X", -ret));
273 }
274 else
275 {
276 sdbusplus::bus::bus dbus(bus);
277 auto MapperCall = dbus.new_method_call(
278 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
279 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
280
281 MapperCall.append(busIdx, addr, fruData);
282
283 try
284 {
285 auto mapperReply = dbus.call(MapperCall);
286 retVal = true;
287 }
288 catch (sdbusplus::exception_t& e)
289 {
290 log<level::ERR>("Fail to Write FRU data via system bus\n");
291 }
292 }
293
294 sd_bus_unref(bus);
295
296 return retVal;
297}
298
Hieu Huynh1463f702021-09-23 03:14:59 +0000299/** @brief execute a command and get the output of the command
300 * @param[in] the command
301 * @returns output of the command
302 */
Thang Tran2a191522022-11-01 18:09:37 +0700303std::string exec(const char* cmd)
304{
Hieu Huynh1463f702021-09-23 03:14:59 +0000305 char buffer[128];
306 std::string result = "";
307 /* Pipe stream from a command */
308 FILE* pipe = popen(cmd, "r");
Thang Tran2a191522022-11-01 18:09:37 +0700309 if (!pipe)
310 throw std::runtime_error("popen() failed!");
311 try
312 {
Hieu Huynh1463f702021-09-23 03:14:59 +0000313 /* Reads a line from the specified stream and stores it */
Thang Tran2a191522022-11-01 18:09:37 +0700314 while (fgets(buffer, sizeof buffer, pipe) != NULL)
315 {
Hieu Huynh1463f702021-09-23 03:14:59 +0000316 result += buffer;
317 }
Thang Tran2a191522022-11-01 18:09:37 +0700318 }
319 catch (...)
320 {
Hieu Huynh1463f702021-09-23 03:14:59 +0000321 pclose(pipe);
322 throw;
323 }
324 /* Close a stream that was opened by popen() */
325 pclose(pipe);
326 return result;
327}
328
329/** @brief implements sync RTC time to BMC commands
330 * @param - None
331 * @returns IPMI completion code.
332 */
333ipmi::RspType<> ipmiSyncRTCTimeToBMC()
334{
335 std::string cmd;
336 std::string cmdOutput;
Hieu Huynh90a4fb82022-08-02 07:21:03 +0000337 int ret;
Hieu Huynh1463f702021-09-23 03:14:59 +0000338 try
339 {
340 /* Check the mode of NTP in the system, set the system time in case the
341 * NTP mode is disabled.
342 */
343 cmd = "systemctl status systemd-timesyncd.service | grep inactive";
344 cmdOutput = exec(cmd.c_str());
345 if (cmdOutput.empty())
346 {
347 log<level::INFO>("Can not set system time while the mode is NTP");
348 return responseFailure();
349 }
350 else
351 {
352 /* Sync time from RTC to BMC using hwclock */
Hieu Huynh90a4fb82022-08-02 07:21:03 +0000353 ret = system("hwclock --hctosys");
354 if (ret == -1)
355 {
356 log<level::INFO>("Can not set the system time");
357 return responseFailure();
358 }
Hieu Huynh1463f702021-09-23 03:14:59 +0000359 }
360 }
Thang Tran2a191522022-11-01 18:09:37 +0700361 catch (const std::exception& e)
Hieu Huynh1463f702021-09-23 03:14:59 +0000362 {
363 log<level::ERR>(e.what());
364 return responseFailure();
365 }
366
367 return ipmi::responseSuccess();
368}
369
Thang Tran2a191522022-11-01 18:09:37 +0700370/** @brief implements ipmi oem command edit MAC address
371 * @param - new macAddress
372 * @returns - Fail or Success.
373 */
374ipmi::RspType<uint8_t> ipmiDocmdSetMacAddress(std::vector<uint8_t> macAddress)
375{
376 std::vector<uint8_t> fruData;
377 uint8_t busIdx = 0;
378 uint8_t addrss = 0;
379
380 if (macAddress.size() != 6)
381 {
382 log<level::ERR>("new MAC address is invalid");
383 return responseFailure();
384 }
385
386#if defined(MAC_ADDRESS_FRU_BUS) && defined(MAC_ADDRESS_FRU_ADDR)
387 /* Set BUS and Address of FRU device that includes MAC address */
388 busIdx = MAC_ADDRESS_FRU_BUS;
389 addrss = MAC_ADDRESS_FRU_ADDR;
390#else
391 /* Calculate BUS and Address of FRU device that includes MAC address */
392 if (!getBaseBoardFRUAddr(busIdx, addrss))
393 {
394 log<level::ERR>(
395 "Can not get the bus and address of baseboard FRU device");
396 return responseFailure();
397 }
398#endif
399
400 if (!getRawFruData(busIdx, addrss, fruData))
401 {
402 log<level::ERR>("Can not get raw FRU data");
403 return responseFailure();
404 }
405
406 if (!updateMACAddrInFRU(fruData, macAddress))
407 {
408 log<level::ERR>("Can not update MAC address");
409 return responseFailure();
410 }
411
412 if (!writeFruData(busIdx, addrss, fruData))
413 {
414 log<level::ERR>("Can not Write FRU data");
415 return responseFailure();
416 }
417
418 return ipmi::responseSuccess(macAddress.size());
419}
420
Hieu Huynh1463f702021-09-23 03:14:59 +0000421void registerOEMFunctions() __attribute__((constructor));
422void registerOEMFunctions()
423{
424 ipmi::registerHandler(ipmi::prioOemBase, ipmi::ampere::netFnAmpere,
Thang Tran2a191522022-11-01 18:09:37 +0700425 ipmi::general::cmdSyncRtcTime, ipmi::Privilege::User,
426 ipmiSyncRTCTimeToBMC);
427
428 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::ampere::netFnAmpere,
429 ipmi::general::cmdEditBmcMacAdr,
430 ipmi::Privilege::User, ipmiDocmdSetMacAddress);
Hieu Huynh1463f702021-09-23 03:14:59 +0000431}