blob: 7439fd3ec4a74e704b898ea3cc45ff3ea799facb [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2017-2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "dbus-sdr/storagecommands.hpp"
18
19#include "dbus-sdr/sdrutils.hpp"
20#include "selutility.hpp"
21
22#include <boost/algorithm/string.hpp>
23#include <boost/container/flat_map.hpp>
24#include <boost/process.hpp>
Willy Tude54f482021-01-26 15:59:09 -080025#include <ipmid/api.hpp>
26#include <ipmid/message.hpp>
27#include <ipmid/types.hpp>
28#include <phosphor-logging/log.hpp>
29#include <sdbusplus/message/types.hpp>
30#include <sdbusplus/timer.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050031
32#include <filesystem>
33#include <fstream>
34#include <functional>
35#include <iostream>
Willy Tude54f482021-01-26 15:59:09 -080036#include <stdexcept>
37#include <string_view>
38
39static constexpr bool DEBUG = false;
40
41namespace dynamic_sensors::ipmi::sel
42{
43static const std::filesystem::path selLogDir = "/var/log";
44static const std::string selLogFilename = "ipmi_sel";
45
46static int getFileTimestamp(const std::filesystem::path& file)
47{
48 struct stat st;
49
50 if (stat(file.c_str(), &st) >= 0)
51 {
52 return st.st_mtime;
53 }
54 return ::ipmi::sel::invalidTimeStamp;
55}
56
57namespace erase_time
58{
59static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time";
60
Willy Tude54f482021-01-26 15:59:09 -080061int get()
62{
63 return getFileTimestamp(selEraseTimestamp);
64}
65} // namespace erase_time
66} // namespace dynamic_sensors::ipmi::sel
67
68namespace ipmi
69{
70
71namespace storage
72{
73
74constexpr static const size_t maxMessageSize = 64;
75constexpr static const size_t maxFruSdrNameSize = 16;
76using ObjectType =
77 boost::container::flat_map<std::string,
78 boost::container::flat_map<std::string, Value>>;
79using ManagedObjectType =
80 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
81using ManagedEntry = std::pair<sdbusplus::message::object_path, ObjectType>;
82
Charles Boyer818bea12021-09-20 16:56:36 -050083constexpr static const char* selLoggerServiceName =
84 "xyz.openbmc_project.Logging.IPMI";
Willy Tude54f482021-01-26 15:59:09 -080085constexpr static const char* fruDeviceServiceName =
86 "xyz.openbmc_project.FruDevice";
87constexpr static const char* entityManagerServiceName =
88 "xyz.openbmc_project.EntityManager";
89constexpr static const size_t writeTimeoutSeconds = 10;
90constexpr static const char* chassisTypeRackMount = "23";
Zev Weissf38f9d12021-05-21 13:30:16 -050091constexpr static const char* chassisTypeMainServer = "17";
Willy Tude54f482021-01-26 15:59:09 -080092
93// event direction is bit[7] of eventType where 1b = Deassertion event
94constexpr static const uint8_t deassertionEvent = 0x80;
95
96static std::vector<uint8_t> fruCache;
krishnar4d90b3f02022-11-11 16:18:32 +053097static constexpr uint16_t invalidBus = 0xFFFF;
98static constexpr uint8_t invalidAddr = 0xFF;
99static uint16_t cacheBus = invalidBus;
100static uint8_t cacheAddr = invalidAddr;
Willy Tude54f482021-01-26 15:59:09 -0800101static uint8_t lastDevId = 0xFF;
102
krishnar4d90b3f02022-11-11 16:18:32 +0530103static uint16_t writeBus = invalidBus;
104static uint8_t writeAddr = invalidAddr;
Willy Tude54f482021-01-26 15:59:09 -0800105
106std::unique_ptr<phosphor::Timer> writeTimer = nullptr;
Patrick Williams5d82f472022-07-22 19:26:53 -0500107static std::vector<sdbusplus::bus::match_t> fruMatches;
Willy Tude54f482021-01-26 15:59:09 -0800108
109ManagedObjectType frus;
110
111// we unfortunately have to build a map of hashes in case there is a
112// collision to verify our dev-id
krishnar4d90b3f02022-11-11 16:18:32 +0530113boost::container::flat_map<uint8_t, std::pair<uint16_t, uint8_t>> deviceHashes;
Willy Tude54f482021-01-26 15:59:09 -0800114void registerStorageFunctions() __attribute__((constructor));
115
Willy Tu48fe64e2022-08-01 23:23:46 +0000116bool writeFru(const std::vector<uint8_t>& fru)
Willy Tude54f482021-01-26 15:59:09 -0800117{
krishnar4d90b3f02022-11-11 16:18:32 +0530118 if (writeBus == invalidBus && writeAddr == invalidAddr)
Willy Tude54f482021-01-26 15:59:09 -0800119 {
120 return true;
121 }
Thang Trand934be92021-12-08 10:13:50 +0700122 lastDevId = 0xFF;
Willy Tude54f482021-01-26 15:59:09 -0800123 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williams5d82f472022-07-22 19:26:53 -0500124 sdbusplus::message_t writeFru = dbus->new_method_call(
Willy Tude54f482021-01-26 15:59:09 -0800125 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
126 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
Willy Tu48fe64e2022-08-01 23:23:46 +0000127 writeFru.append(writeBus, writeAddr, fru);
Willy Tude54f482021-01-26 15:59:09 -0800128 try
129 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500130 sdbusplus::message_t writeFruResp = dbus->call(writeFru);
Willy Tude54f482021-01-26 15:59:09 -0800131 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500132 catch (const sdbusplus::exception_t&)
Willy Tude54f482021-01-26 15:59:09 -0800133 {
134 // todo: log sel?
135 phosphor::logging::log<phosphor::logging::level::ERR>(
136 "error writing fru");
137 return false;
138 }
krishnar4d90b3f02022-11-11 16:18:32 +0530139 writeBus = invalidBus;
140 writeAddr = invalidAddr;
Willy Tude54f482021-01-26 15:59:09 -0800141 return true;
142}
143
William A. Kennington III52535622022-11-28 18:28:22 -0800144void writeFruCache()
Willy Tu48fe64e2022-08-01 23:23:46 +0000145{
William A. Kennington III52535622022-11-28 18:28:22 -0800146 writeFru(fruCache);
Willy Tu48fe64e2022-08-01 23:23:46 +0000147}
148
Willy Tude54f482021-01-26 15:59:09 -0800149void createTimers()
150{
Willy Tu48fe64e2022-08-01 23:23:46 +0000151 writeTimer = std::make_unique<phosphor::Timer>(writeFruCache);
Willy Tude54f482021-01-26 15:59:09 -0800152}
153
154void recalculateHashes()
155{
Willy Tude54f482021-01-26 15:59:09 -0800156 deviceHashes.clear();
157 // hash the object paths to create unique device id's. increment on
158 // collision
159 std::hash<std::string> hasher;
160 for (const auto& fru : frus)
161 {
162 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
163 if (fruIface == fru.second.end())
164 {
165 continue;
166 }
167
168 auto busFind = fruIface->second.find("BUS");
169 auto addrFind = fruIface->second.find("ADDRESS");
170 if (busFind == fruIface->second.end() ||
171 addrFind == fruIface->second.end())
172 {
173 phosphor::logging::log<phosphor::logging::level::INFO>(
174 "fru device missing Bus or Address",
175 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
176 continue;
177 }
178
krishnar4d90b3f02022-11-11 16:18:32 +0530179 uint16_t fruBus = std::get<uint32_t>(busFind->second);
Willy Tude54f482021-01-26 15:59:09 -0800180 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
181 auto chassisFind = fruIface->second.find("CHASSIS_TYPE");
182 std::string chassisType;
183 if (chassisFind != fruIface->second.end())
184 {
185 chassisType = std::get<std::string>(chassisFind->second);
186 }
187
188 uint8_t fruHash = 0;
Zev Weissf38f9d12021-05-21 13:30:16 -0500189 if (chassisType.compare(chassisTypeRackMount) != 0 &&
190 chassisType.compare(chassisTypeMainServer) != 0)
Willy Tude54f482021-01-26 15:59:09 -0800191 {
192 fruHash = hasher(fru.first.str);
193 // can't be 0xFF based on spec, and 0 is reserved for baseboard
194 if (fruHash == 0 || fruHash == 0xFF)
195 {
196 fruHash = 1;
197 }
198 }
krishnar4d90b3f02022-11-11 16:18:32 +0530199 std::pair<uint16_t, uint8_t> newDev(fruBus, fruAddr);
Willy Tude54f482021-01-26 15:59:09 -0800200
201 bool emplacePassed = false;
202 while (!emplacePassed)
203 {
204 auto resp = deviceHashes.emplace(fruHash, newDev);
205 emplacePassed = resp.second;
206 if (!emplacePassed)
207 {
208 fruHash++;
209 // can't be 0xFF based on spec, and 0 is reserved for
210 // baseboard
211 if (fruHash == 0XFF)
212 {
213 fruHash = 0x1;
214 }
215 }
216 }
217 }
218}
219
Willy Tu11d68892022-01-20 10:37:34 -0800220void replaceCacheFru(
221 const std::shared_ptr<sdbusplus::asio::connection>& bus,
222 boost::asio::yield_context& yield,
223 [[maybe_unused]] const std::optional<std::string>& path = std::nullopt)
Willy Tude54f482021-01-26 15:59:09 -0800224{
225 boost::system::error_code ec;
226
227 frus = bus->yield_method_call<ManagedObjectType>(
228 yield, ec, fruDeviceServiceName, "/",
229 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
230 if (ec)
231 {
232 phosphor::logging::log<phosphor::logging::level::ERR>(
233 "GetMangagedObjects for replaceCacheFru failed",
234 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
235
236 return;
237 }
238 recalculateHashes();
239}
240
Willy Tu48fe64e2022-08-01 23:23:46 +0000241std::pair<ipmi::Cc, std::vector<uint8_t>> getFru(ipmi::Context::ptr ctx,
242 uint8_t devId)
Willy Tude54f482021-01-26 15:59:09 -0800243{
244 if (lastDevId == devId && devId != 0xFF)
245 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000246 return {ipmi::ccSuccess, fruCache};
Willy Tude54f482021-01-26 15:59:09 -0800247 }
248
Willy Tude54f482021-01-26 15:59:09 -0800249 auto deviceFind = deviceHashes.find(devId);
250 if (deviceFind == deviceHashes.end())
251 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000252 return {IPMI_CC_SENSOR_INVALID, {}};
Willy Tude54f482021-01-26 15:59:09 -0800253 }
254
Willy Tude54f482021-01-26 15:59:09 -0800255 cacheBus = deviceFind->second.first;
256 cacheAddr = deviceFind->second.second;
257
258 boost::system::error_code ec;
259
Willy Tu48fe64e2022-08-01 23:23:46 +0000260 std::vector<uint8_t> fru =
261 ctx->bus->yield_method_call<std::vector<uint8_t>>(
262 ctx->yield, ec, fruDeviceServiceName,
263 "/xyz/openbmc_project/FruDevice",
264 "xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus,
265 cacheAddr);
Willy Tude54f482021-01-26 15:59:09 -0800266 if (ec)
267 {
268 phosphor::logging::log<phosphor::logging::level::ERR>(
269 "Couldn't get raw fru",
270 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
271
krishnar4d90b3f02022-11-11 16:18:32 +0530272 cacheBus = invalidBus;
273 cacheAddr = invalidAddr;
Willy Tu48fe64e2022-08-01 23:23:46 +0000274 return {ipmi::ccResponseError, {}};
Willy Tude54f482021-01-26 15:59:09 -0800275 }
276
Willy Tu48fe64e2022-08-01 23:23:46 +0000277 fruCache.clear();
Willy Tude54f482021-01-26 15:59:09 -0800278 lastDevId = devId;
Willy Tu48fe64e2022-08-01 23:23:46 +0000279 fruCache = fru;
280
281 return {ipmi::ccSuccess, fru};
Willy Tude54f482021-01-26 15:59:09 -0800282}
283
284void writeFruIfRunning()
285{
286 if (!writeTimer->isRunning())
287 {
288 return;
289 }
290 writeTimer->stop();
Willy Tu48fe64e2022-08-01 23:23:46 +0000291 writeFruCache();
Willy Tude54f482021-01-26 15:59:09 -0800292}
293
294void startMatch(void)
295{
296 if (fruMatches.size())
297 {
298 return;
299 }
300
301 fruMatches.reserve(2);
302
303 auto bus = getSdBus();
304 fruMatches.emplace_back(*bus,
305 "type='signal',arg0path='/xyz/openbmc_project/"
306 "FruDevice/',member='InterfacesAdded'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500307 [](sdbusplus::message_t& message) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500308 sdbusplus::message::object_path path;
309 ObjectType object;
310 try
311 {
312 message.read(path, object);
313 }
314 catch (const sdbusplus::exception_t&)
315 {
316 return;
317 }
318 auto findType = object.find("xyz.openbmc_project.FruDevice");
319 if (findType == object.end())
320 {
321 return;
322 }
323 writeFruIfRunning();
324 frus[path] = object;
325 recalculateHashes();
326 lastDevId = 0xFF;
327 });
Willy Tude54f482021-01-26 15:59:09 -0800328
329 fruMatches.emplace_back(*bus,
330 "type='signal',arg0path='/xyz/openbmc_project/"
331 "FruDevice/',member='InterfacesRemoved'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500332 [](sdbusplus::message_t& message) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500333 sdbusplus::message::object_path path;
334 std::set<std::string> interfaces;
335 try
336 {
337 message.read(path, interfaces);
338 }
339 catch (const sdbusplus::exception_t&)
340 {
341 return;
342 }
343 auto findType = interfaces.find("xyz.openbmc_project.FruDevice");
344 if (findType == interfaces.end())
345 {
346 return;
347 }
348 writeFruIfRunning();
349 frus.erase(path);
350 recalculateHashes();
351 lastDevId = 0xFF;
352 });
Willy Tude54f482021-01-26 15:59:09 -0800353
354 // call once to populate
355 boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) {
356 replaceCacheFru(getSdBus(), yield);
357 });
358}
359
360/** @brief implements the read FRU data command
361 * @param fruDeviceId - FRU Device ID
362 * @param fruInventoryOffset - FRU Inventory Offset to write
363 * @param countToRead - Count to read
364 *
365 * @returns ipmi completion code plus response data
366 * - countWritten - Count written
367 */
368ipmi::RspType<uint8_t, // Count
369 std::vector<uint8_t> // Requested data
370 >
371 ipmiStorageReadFruData(ipmi::Context::ptr ctx, uint8_t fruDeviceId,
372 uint16_t fruInventoryOffset, uint8_t countToRead)
373{
374 if (fruDeviceId == 0xFF)
375 {
376 return ipmi::responseInvalidFieldRequest();
377 }
378
Willy Tu48fe64e2022-08-01 23:23:46 +0000379 auto [status, fru] = getFru(ctx, fruDeviceId);
Willy Tude54f482021-01-26 15:59:09 -0800380 if (status != ipmi::ccSuccess)
381 {
382 return ipmi::response(status);
383 }
384
385 size_t fromFruByteLen = 0;
Willy Tu48fe64e2022-08-01 23:23:46 +0000386 if (countToRead + fruInventoryOffset < fru.size())
Willy Tude54f482021-01-26 15:59:09 -0800387 {
388 fromFruByteLen = countToRead;
389 }
Willy Tu48fe64e2022-08-01 23:23:46 +0000390 else if (fru.size() > fruInventoryOffset)
Willy Tude54f482021-01-26 15:59:09 -0800391 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000392 fromFruByteLen = fru.size() - fruInventoryOffset;
Willy Tude54f482021-01-26 15:59:09 -0800393 }
394 else
395 {
396 return ipmi::responseReqDataLenExceeded();
397 }
398
399 std::vector<uint8_t> requestedData;
400
Willy Tu48fe64e2022-08-01 23:23:46 +0000401 requestedData.insert(requestedData.begin(),
402 fru.begin() + fruInventoryOffset,
403 fru.begin() + fruInventoryOffset + fromFruByteLen);
Willy Tude54f482021-01-26 15:59:09 -0800404
405 return ipmi::responseSuccess(static_cast<uint8_t>(requestedData.size()),
406 requestedData);
407}
408
409/** @brief implements the write FRU data command
410 * @param fruDeviceId - FRU Device ID
411 * @param fruInventoryOffset - FRU Inventory Offset to write
412 * @param dataToWrite - Data to write
413 *
414 * @returns ipmi completion code plus response data
415 * - countWritten - Count written
416 */
417ipmi::RspType<uint8_t>
418 ipmiStorageWriteFruData(ipmi::Context::ptr ctx, uint8_t fruDeviceId,
419 uint16_t fruInventoryOffset,
420 std::vector<uint8_t>& dataToWrite)
421{
422 if (fruDeviceId == 0xFF)
423 {
424 return ipmi::responseInvalidFieldRequest();
425 }
426
427 size_t writeLen = dataToWrite.size();
428
Willy Tu48fe64e2022-08-01 23:23:46 +0000429 auto [status, fru] = getFru(ctx, fruDeviceId);
Willy Tude54f482021-01-26 15:59:09 -0800430 if (status != ipmi::ccSuccess)
431 {
432 return ipmi::response(status);
433 }
434 size_t lastWriteAddr = fruInventoryOffset + writeLen;
Willy Tu48fe64e2022-08-01 23:23:46 +0000435 if (fru.size() < lastWriteAddr)
Willy Tude54f482021-01-26 15:59:09 -0800436 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000437 fru.resize(fruInventoryOffset + writeLen);
Willy Tude54f482021-01-26 15:59:09 -0800438 }
439
440 std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
Willy Tu48fe64e2022-08-01 23:23:46 +0000441 fru.begin() + fruInventoryOffset);
Willy Tude54f482021-01-26 15:59:09 -0800442
443 bool atEnd = false;
444
Willy Tu48fe64e2022-08-01 23:23:46 +0000445 if (fru.size() >= sizeof(FRUHeader))
Willy Tude54f482021-01-26 15:59:09 -0800446 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000447 FRUHeader* header = reinterpret_cast<FRUHeader*>(fru.data());
Willy Tude54f482021-01-26 15:59:09 -0800448
449 size_t areaLength = 0;
450 size_t lastRecordStart = std::max(
451 {header->internalOffset, header->chassisOffset, header->boardOffset,
452 header->productOffset, header->multiRecordOffset});
453 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
454
455 if (header->multiRecordOffset)
456 {
457 // This FRU has a MultiRecord Area
458 uint8_t endOfList = 0;
459 // Walk the MultiRecord headers until the last record
460 while (!endOfList)
461 {
462 // The MSB in the second byte of the MultiRecord header signals
463 // "End of list"
Willy Tu48fe64e2022-08-01 23:23:46 +0000464 endOfList = fru[lastRecordStart + 1] & 0x80;
Willy Tude54f482021-01-26 15:59:09 -0800465 // Third byte in the MultiRecord header is the length
Willy Tu48fe64e2022-08-01 23:23:46 +0000466 areaLength = fru[lastRecordStart + 2];
Willy Tude54f482021-01-26 15:59:09 -0800467 // This length is in bytes (not 8 bytes like other headers)
468 areaLength += 5; // The length omits the 5 byte header
469 if (!endOfList)
470 {
471 // Next MultiRecord header
472 lastRecordStart += areaLength;
473 }
474 }
475 }
476 else
477 {
478 // This FRU does not have a MultiRecord Area
479 // Get the length of the area in multiples of 8 bytes
480 if (lastWriteAddr > (lastRecordStart + 1))
481 {
482 // second byte in record area is the length
Willy Tu48fe64e2022-08-01 23:23:46 +0000483 areaLength = fru[lastRecordStart + 1];
Willy Tude54f482021-01-26 15:59:09 -0800484 areaLength *= 8; // it is in multiples of 8 bytes
485 }
486 }
487 if (lastWriteAddr >= (areaLength + lastRecordStart))
488 {
489 atEnd = true;
490 }
491 }
492 uint8_t countWritten = 0;
493
494 writeBus = cacheBus;
495 writeAddr = cacheAddr;
496 if (atEnd)
497 {
498 // cancel timer, we're at the end so might as well send it
499 writeTimer->stop();
Willy Tu48fe64e2022-08-01 23:23:46 +0000500 if (!writeFru(fru))
Willy Tude54f482021-01-26 15:59:09 -0800501 {
502 return ipmi::responseInvalidFieldRequest();
503 }
Willy Tu48fe64e2022-08-01 23:23:46 +0000504 countWritten = std::min(fru.size(), static_cast<size_t>(0xFF));
Willy Tude54f482021-01-26 15:59:09 -0800505 }
506 else
507 {
Sui Chen548d1a22022-09-14 07:41:17 -0700508 fruCache = fru; // Write-back
Willy Tude54f482021-01-26 15:59:09 -0800509 // start a timer, if no further data is sent to check to see if it is
510 // valid
511 writeTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
512 std::chrono::seconds(writeTimeoutSeconds)));
513 countWritten = 0;
514 }
515
516 return ipmi::responseSuccess(countWritten);
517}
518
519/** @brief implements the get FRU inventory area info command
520 * @param fruDeviceId - FRU Device ID
521 *
522 * @returns IPMI completion code plus response data
523 * - inventorySize - Number of possible allocation units
524 * - accessType - Allocation unit size in bytes.
525 */
526ipmi::RspType<uint16_t, // inventorySize
527 uint8_t> // accessType
528 ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr ctx, uint8_t fruDeviceId)
529{
530 if (fruDeviceId == 0xFF)
531 {
532 return ipmi::responseInvalidFieldRequest();
533 }
534
Willy Tu48fe64e2022-08-01 23:23:46 +0000535 auto [ret, fru] = getFru(ctx, fruDeviceId);
Willy Tude54f482021-01-26 15:59:09 -0800536 if (ret != ipmi::ccSuccess)
537 {
538 return ipmi::response(ret);
539 }
540
541 constexpr uint8_t accessType =
542 static_cast<uint8_t>(GetFRUAreaAccessType::byte);
543
Willy Tu48fe64e2022-08-01 23:23:46 +0000544 return ipmi::responseSuccess(fru.size(), accessType);
Willy Tude54f482021-01-26 15:59:09 -0800545}
546
Willy Tu11d68892022-01-20 10:37:34 -0800547ipmi_ret_t getFruSdrCount(ipmi::Context::ptr, size_t& count)
Willy Tude54f482021-01-26 15:59:09 -0800548{
549 count = deviceHashes.size();
550 return IPMI_CC_OK;
551}
552
Johnathan Mantey23a722c2023-05-12 08:18:54 -0700553ipmi_ret_t getFruSdrs([[maybe_unused]] ipmi::Context::ptr ctx, size_t index,
Willy Tude54f482021-01-26 15:59:09 -0800554 get_sdr::SensorDataFruRecord& resp)
555{
556 if (deviceHashes.size() < index)
557 {
558 return IPMI_CC_INVALID_FIELD_REQUEST;
559 }
560 auto device = deviceHashes.begin() + index;
krishnar4d90b3f02022-11-11 16:18:32 +0530561 uint16_t& bus = device->second.first;
Willy Tude54f482021-01-26 15:59:09 -0800562 uint8_t& address = device->second.second;
563
564 boost::container::flat_map<std::string, Value>* fruData = nullptr;
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500565 auto fru = std::find_if(frus.begin(), frus.end(),
566 [bus, address, &fruData](ManagedEntry& entry) {
567 auto findFruDevice = entry.second.find("xyz.openbmc_project.FruDevice");
568 if (findFruDevice == entry.second.end())
569 {
570 return false;
571 }
572 fruData = &(findFruDevice->second);
573 auto findBus = findFruDevice->second.find("BUS");
574 auto findAddress = findFruDevice->second.find("ADDRESS");
575 if (findBus == findFruDevice->second.end() ||
576 findAddress == findFruDevice->second.end())
577 {
578 return false;
579 }
580 if (std::get<uint32_t>(findBus->second) != bus)
581 {
582 return false;
583 }
584 if (std::get<uint32_t>(findAddress->second) != address)
585 {
586 return false;
587 }
588 return true;
589 });
Willy Tude54f482021-01-26 15:59:09 -0800590 if (fru == frus.end())
591 {
592 return IPMI_CC_RESPONSE_ERROR;
593 }
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530594 std::string name;
Willy Tude54f482021-01-26 15:59:09 -0800595
596#ifdef USING_ENTITY_MANAGER_DECORATORS
597
598 boost::container::flat_map<std::string, Value>* entityData = nullptr;
599
600 // todo: this should really use caching, this is a very inefficient lookup
601 boost::system::error_code ec;
Nan Zhou947da1b2022-09-20 20:40:59 +0000602
Willy Tude54f482021-01-26 15:59:09 -0800603 ManagedObjectType entities = ctx->bus->yield_method_call<ManagedObjectType>(
Nan Zhou947da1b2022-09-20 20:40:59 +0000604 ctx->yield, ec, entityManagerServiceName,
605 "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager",
606 "GetManagedObjects");
Willy Tude54f482021-01-26 15:59:09 -0800607
608 if (ec)
609 {
610 phosphor::logging::log<phosphor::logging::level::ERR>(
611 "GetMangagedObjects for ipmiStorageGetFruInvAreaInfo failed",
612 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
613
614 return ipmi::ccResponseError;
615 }
616
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500617 auto entity =
618 std::find_if(entities.begin(), entities.end(),
619 [bus, address, &entityData, &name](ManagedEntry& entry) {
620 auto findFruDevice = entry.second.find(
621 "xyz.openbmc_project.Inventory.Decorator.I2CDevice");
622 if (findFruDevice == entry.second.end())
623 {
624 return false;
625 }
Willy Tude54f482021-01-26 15:59:09 -0800626
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500627 // Integer fields added via Entity-Manager json are uint64_ts by
628 // default.
629 auto findBus = findFruDevice->second.find("Bus");
630 auto findAddress = findFruDevice->second.find("Address");
Willy Tude54f482021-01-26 15:59:09 -0800631
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500632 if (findBus == findFruDevice->second.end() ||
633 findAddress == findFruDevice->second.end())
634 {
635 return false;
636 }
637 if ((std::get<uint64_t>(findBus->second) != bus) ||
638 (std::get<uint64_t>(findAddress->second) != address))
639 {
640 return false;
641 }
Willy Tude54f482021-01-26 15:59:09 -0800642
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500643 auto fruName = findFruDevice->second.find("Name");
644 if (fruName != findFruDevice->second.end())
645 {
646 name = std::get<std::string>(fruName->second);
647 }
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530648
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500649 // At this point we found the device entry and should return
650 // true.
651 auto findIpmiDevice =
652 entry.second.find("xyz.openbmc_project.Inventory.Decorator.Ipmi");
653 if (findIpmiDevice != entry.second.end())
654 {
655 entityData = &(findIpmiDevice->second);
656 }
Willy Tude54f482021-01-26 15:59:09 -0800657
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500658 return true;
Patrick Williams369824e2023-10-20 11:18:23 -0500659 });
Willy Tude54f482021-01-26 15:59:09 -0800660
661 if (entity == entities.end())
662 {
663 if constexpr (DEBUG)
664 {
665 std::fprintf(stderr, "Ipmi or FruDevice Decorator interface "
666 "not found for Fru\n");
667 }
668 }
669
670#endif
671
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530672 if (name.empty())
Willy Tude54f482021-01-26 15:59:09 -0800673 {
674 name = "UNKNOWN";
675 }
676 if (name.size() > maxFruSdrNameSize)
677 {
678 name = name.substr(0, maxFruSdrNameSize);
679 }
680 size_t sizeDiff = maxFruSdrNameSize - name.size();
681
682 resp.header.record_id_lsb = 0x0; // calling code is to implement these
683 resp.header.record_id_msb = 0x0;
684 resp.header.sdr_version = ipmiSdrVersion;
685 resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
686 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
687 resp.key.deviceAddress = 0x20;
688 resp.key.fruID = device->first;
689 resp.key.accessLun = 0x80; // logical / physical fru device
690 resp.key.channelNumber = 0x0;
691 resp.body.reserved = 0x0;
692 resp.body.deviceType = 0x10;
693 resp.body.deviceTypeModifier = 0x0;
694
695 uint8_t entityID = 0;
696 uint8_t entityInstance = 0x1;
697
698#ifdef USING_ENTITY_MANAGER_DECORATORS
699 if (entityData)
700 {
701 auto entityIdProperty = entityData->find("EntityId");
702 auto entityInstanceProperty = entityData->find("EntityInstance");
703
704 if (entityIdProperty != entityData->end())
705 {
706 entityID = static_cast<uint8_t>(
707 std::get<uint64_t>(entityIdProperty->second));
708 }
709 if (entityInstanceProperty != entityData->end())
710 {
711 entityInstance = static_cast<uint8_t>(
712 std::get<uint64_t>(entityInstanceProperty->second));
713 }
714 }
715#endif
716
717 resp.body.entityID = entityID;
718 resp.body.entityInstance = entityInstance;
719
720 resp.body.oem = 0x0;
721 resp.body.deviceIDLen = name.size();
722 name.copy(resp.body.deviceID, name.size());
723
724 return IPMI_CC_OK;
725}
726
727static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
728{
729 // Loop through the directory looking for ipmi_sel log files
730 for (const std::filesystem::directory_entry& dirEnt :
731 std::filesystem::directory_iterator(
732 dynamic_sensors::ipmi::sel::selLogDir))
733 {
734 std::string filename = dirEnt.path().filename();
735 if (boost::starts_with(filename,
736 dynamic_sensors::ipmi::sel::selLogFilename))
737 {
738 // If we find an ipmi_sel log file, save the path
739 selLogFiles.emplace_back(dynamic_sensors::ipmi::sel::selLogDir /
740 filename);
741 }
742 }
743 // As the log files rotate, they are appended with a ".#" that is higher for
744 // the older logs. Since we don't expect more than 10 log files, we
745 // can just sort the list to get them in order from newest to oldest
746 std::sort(selLogFiles.begin(), selLogFiles.end());
747
748 return !selLogFiles.empty();
749}
750
751static int countSELEntries()
752{
753 // Get the list of ipmi_sel log files
754 std::vector<std::filesystem::path> selLogFiles;
755 if (!getSELLogFiles(selLogFiles))
756 {
757 return 0;
758 }
759 int numSELEntries = 0;
760 // Loop through each log file and count the number of logs
761 for (const std::filesystem::path& file : selLogFiles)
762 {
763 std::ifstream logStream(file);
764 if (!logStream.is_open())
765 {
766 continue;
767 }
768
769 std::string line;
770 while (std::getline(logStream, line))
771 {
772 numSELEntries++;
773 }
774 }
775 return numSELEntries;
776}
777
778static bool findSELEntry(const int recordID,
779 const std::vector<std::filesystem::path>& selLogFiles,
780 std::string& entry)
781{
782 // Record ID is the first entry field following the timestamp. It is
783 // preceded by a space and followed by a comma
784 std::string search = " " + std::to_string(recordID) + ",";
785
786 // Loop through the ipmi_sel log entries
787 for (const std::filesystem::path& file : selLogFiles)
788 {
789 std::ifstream logStream(file);
790 if (!logStream.is_open())
791 {
792 continue;
793 }
794
795 while (std::getline(logStream, entry))
796 {
797 // Check if the record ID matches
798 if (entry.find(search) != std::string::npos)
799 {
800 return true;
801 }
802 }
803 }
804 return false;
805}
806
807static uint16_t
808 getNextRecordID(const uint16_t recordID,
809 const std::vector<std::filesystem::path>& selLogFiles)
810{
811 uint16_t nextRecordID = recordID + 1;
812 std::string entry;
813 if (findSELEntry(nextRecordID, selLogFiles, entry))
814 {
815 return nextRecordID;
816 }
817 else
818 {
819 return ipmi::sel::lastEntry;
820 }
821}
822
823static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
824{
825 for (unsigned int i = 0; i < hexStr.size(); i += 2)
826 {
827 try
828 {
829 data.push_back(static_cast<uint8_t>(
830 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
831 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500832 catch (const std::invalid_argument& e)
Willy Tude54f482021-01-26 15:59:09 -0800833 {
834 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
835 return -1;
836 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500837 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800838 {
839 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
840 return -1;
841 }
842 }
843 return 0;
844}
845
846ipmi::RspType<uint8_t, // SEL version
847 uint16_t, // SEL entry count
848 uint16_t, // free space
849 uint32_t, // last add timestamp
850 uint32_t, // last erase timestamp
851 uint8_t> // operation support
852 ipmiStorageGetSELInfo()
853{
854 constexpr uint8_t selVersion = ipmi::sel::selVersion;
855 uint16_t entries = countSELEntries();
856 uint32_t addTimeStamp = dynamic_sensors::ipmi::sel::getFileTimestamp(
857 dynamic_sensors::ipmi::sel::selLogDir /
858 dynamic_sensors::ipmi::sel::selLogFilename);
859 uint32_t eraseTimeStamp = dynamic_sensors::ipmi::sel::erase_time::get();
860 constexpr uint8_t operationSupport =
861 dynamic_sensors::ipmi::sel::selOperationSupport;
862 constexpr uint16_t freeSpace =
863 0xffff; // Spec indicates that more than 64kB is free
864
865 return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
866 eraseTimeStamp, operationSupport);
867}
868
869using systemEventType = std::tuple<
870 uint32_t, // Timestamp
871 uint16_t, // Generator ID
872 uint8_t, // EvM Rev
873 uint8_t, // Sensor Type
874 uint8_t, // Sensor Number
875 uint7_t, // Event Type
876 bool, // Event Direction
877 std::array<uint8_t, dynamic_sensors::ipmi::sel::systemEventSize>>; // Event
878 // Data
879using oemTsEventType = std::tuple<
880 uint32_t, // Timestamp
881 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemTsEventSize>>; // Event
882 // Data
883using oemEventType =
884 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemEventSize>; // Event Data
885
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500886ipmi::RspType<uint16_t, // Next Record ID
887 uint16_t, // Record ID
888 uint8_t, // Record Type
Willy Tude54f482021-01-26 15:59:09 -0800889 std::variant<systemEventType, oemTsEventType,
890 oemEventType>> // Record Content
891 ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
892 uint8_t offset, uint8_t size)
893{
894 // Only support getting the entire SEL record. If a partial size or non-zero
895 // offset is requested, return an error
896 if (offset != 0 || size != ipmi::sel::entireRecord)
897 {
898 return ipmi::responseRetBytesUnavailable();
899 }
900
901 // Check the reservation ID if one is provided or required (only if the
902 // offset is non-zero)
903 if (reservationID != 0 || offset != 0)
904 {
905 if (!checkSELReservation(reservationID))
906 {
907 return ipmi::responseInvalidReservationId();
908 }
909 }
910
911 // Get the ipmi_sel log files
912 std::vector<std::filesystem::path> selLogFiles;
913 if (!getSELLogFiles(selLogFiles))
914 {
915 return ipmi::responseSensorInvalid();
916 }
917
918 std::string targetEntry;
919
920 if (targetID == ipmi::sel::firstEntry)
921 {
922 // The first entry will be at the top of the oldest log file
923 std::ifstream logStream(selLogFiles.back());
924 if (!logStream.is_open())
925 {
926 return ipmi::responseUnspecifiedError();
927 }
928
929 if (!std::getline(logStream, targetEntry))
930 {
931 return ipmi::responseUnspecifiedError();
932 }
933 }
934 else if (targetID == ipmi::sel::lastEntry)
935 {
936 // The last entry will be at the bottom of the newest log file
937 std::ifstream logStream(selLogFiles.front());
938 if (!logStream.is_open())
939 {
940 return ipmi::responseUnspecifiedError();
941 }
942
943 std::string line;
944 while (std::getline(logStream, line))
945 {
946 targetEntry = line;
947 }
948 }
949 else
950 {
951 if (!findSELEntry(targetID, selLogFiles, targetEntry))
952 {
953 return ipmi::responseSensorInvalid();
954 }
955 }
956
957 // The format of the ipmi_sel message is "<Timestamp>
958 // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
959 // First get the Timestamp
960 size_t space = targetEntry.find_first_of(" ");
961 if (space == std::string::npos)
962 {
963 return ipmi::responseUnspecifiedError();
964 }
965 std::string entryTimestamp = targetEntry.substr(0, space);
966 // Then get the log contents
967 size_t entryStart = targetEntry.find_first_not_of(" ", space);
968 if (entryStart == std::string::npos)
969 {
970 return ipmi::responseUnspecifiedError();
971 }
972 std::string_view entry(targetEntry);
973 entry.remove_prefix(entryStart);
974 // Use split to separate the entry into its fields
975 std::vector<std::string> targetEntryFields;
976 boost::split(targetEntryFields, entry, boost::is_any_of(","),
977 boost::token_compress_on);
978 if (targetEntryFields.size() < 3)
979 {
980 return ipmi::responseUnspecifiedError();
981 }
982 std::string& recordIDStr = targetEntryFields[0];
983 std::string& recordTypeStr = targetEntryFields[1];
984 std::string& eventDataStr = targetEntryFields[2];
985
986 uint16_t recordID;
987 uint8_t recordType;
988 try
989 {
990 recordID = std::stoul(recordIDStr);
991 recordType = std::stoul(recordTypeStr, nullptr, 16);
992 }
993 catch (const std::invalid_argument&)
994 {
995 return ipmi::responseUnspecifiedError();
996 }
997 uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
998 std::vector<uint8_t> eventDataBytes;
999 if (fromHexStr(eventDataStr, eventDataBytes) < 0)
1000 {
1001 return ipmi::responseUnspecifiedError();
1002 }
1003
1004 if (recordType == dynamic_sensors::ipmi::sel::systemEvent)
1005 {
1006 // Get the timestamp
1007 std::tm timeStruct = {};
1008 std::istringstream entryStream(entryTimestamp);
1009
1010 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1011 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1012 {
Willy Tu7bb412f2023-09-25 11:30:45 -07001013 timeStruct.tm_isdst = -1;
Willy Tude54f482021-01-26 15:59:09 -08001014 timestamp = std::mktime(&timeStruct);
1015 }
1016
1017 // Set the event message revision
1018 uint8_t evmRev = dynamic_sensors::ipmi::sel::eventMsgRev;
1019
1020 uint16_t generatorID = 0;
1021 uint8_t sensorType = 0;
1022 uint16_t sensorAndLun = 0;
1023 uint8_t sensorNum = 0xFF;
1024 uint7_t eventType = 0;
1025 bool eventDir = 0;
1026 // System type events should have six fields
1027 if (targetEntryFields.size() >= 6)
1028 {
1029 std::string& generatorIDStr = targetEntryFields[3];
1030 std::string& sensorPath = targetEntryFields[4];
1031 std::string& eventDirStr = targetEntryFields[5];
1032
1033 // Get the generator ID
1034 try
1035 {
1036 generatorID = std::stoul(generatorIDStr, nullptr, 16);
1037 }
1038 catch (const std::invalid_argument&)
1039 {
1040 std::cerr << "Invalid Generator ID\n";
1041 }
1042
1043 // Get the sensor type, sensor number, and event type for the sensor
1044 sensorType = getSensorTypeFromPath(sensorPath);
1045 sensorAndLun = getSensorNumberFromPath(sensorPath);
1046 sensorNum = static_cast<uint8_t>(sensorAndLun);
Harvey.Wu4376cdf2021-11-16 19:40:55 +08001047 if ((generatorID & 0x0001) == 0)
1048 {
1049 // IPMB Address
1050 generatorID |= sensorAndLun & 0x0300;
1051 }
1052 else
1053 {
1054 // system software
1055 generatorID |= sensorAndLun >> 8;
1056 }
Willy Tude54f482021-01-26 15:59:09 -08001057 eventType = getSensorEventTypeFromPath(sensorPath);
1058
1059 // Get the event direction
1060 try
1061 {
1062 eventDir = std::stoul(eventDirStr) ? 0 : 1;
1063 }
1064 catch (const std::invalid_argument&)
1065 {
1066 std::cerr << "Invalid Event Direction\n";
1067 }
1068 }
1069
1070 // Only keep the eventData bytes that fit in the record
1071 std::array<uint8_t, dynamic_sensors::ipmi::sel::systemEventSize>
1072 eventData{};
1073 std::copy_n(eventDataBytes.begin(),
1074 std::min(eventDataBytes.size(), eventData.size()),
1075 eventData.begin());
1076
1077 return ipmi::responseSuccess(
1078 nextRecordID, recordID, recordType,
1079 systemEventType{timestamp, generatorID, evmRev, sensorType,
1080 sensorNum, eventType, eventDir, eventData});
1081 }
1082
Thang Trane70c59b2023-09-21 13:54:28 +07001083 if (recordType >= dynamic_sensors::ipmi::sel::oemTsEventFirst &&
1084 recordType <= dynamic_sensors::ipmi::sel::oemTsEventLast)
1085 {
1086 // Get the timestamp
1087 std::tm timeStruct = {};
1088 std::istringstream entryStream(entryTimestamp);
1089
1090 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1091 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1092 {
1093 timeStruct.tm_isdst = -1;
1094 timestamp = std::mktime(&timeStruct);
1095 }
1096
1097 // Only keep the bytes that fit in the record
1098 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemTsEventSize>
1099 eventData{};
1100 std::copy_n(eventDataBytes.begin(),
1101 std::min(eventDataBytes.size(), eventData.size()),
1102 eventData.begin());
1103
1104 return ipmi::responseSuccess(nextRecordID, recordID, recordType,
1105 oemTsEventType{timestamp, eventData});
1106 }
1107
1108 if (recordType >= dynamic_sensors::ipmi::sel::oemEventFirst)
1109 {
1110 // Only keep the bytes that fit in the record
1111 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemEventSize>
1112 eventData{};
1113 std::copy_n(eventDataBytes.begin(),
1114 std::min(eventDataBytes.size(), eventData.size()),
1115 eventData.begin());
1116
1117 return ipmi::responseSuccess(nextRecordID, recordID, recordType,
1118 eventData);
1119 }
1120
Willy Tude54f482021-01-26 15:59:09 -08001121 return ipmi::responseUnspecifiedError();
1122}
1123
Willy Tu11d68892022-01-20 10:37:34 -08001124/*
1125Unused arguments
1126 uint16_t recordID, uint8_t recordType, uint32_t timestamp,
1127 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
1128 uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
1129 uint8_t eventData3
1130*/
1131ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(uint16_t, uint8_t, uint32_t,
1132 uint16_t, uint8_t, uint8_t,
1133 uint8_t, uint8_t, uint8_t,
1134 uint8_t, uint8_t)
Willy Tude54f482021-01-26 15:59:09 -08001135{
1136 // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1137 // added
1138 cancelSELReservation();
1139
1140 uint16_t responseID = 0xFFFF;
1141 return ipmi::responseSuccess(responseID);
1142}
1143
Tim Lee11317d72022-07-27 10:13:46 +08001144ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr ctx,
Willy Tude54f482021-01-26 15:59:09 -08001145 uint16_t reservationID,
1146 const std::array<uint8_t, 3>& clr,
1147 uint8_t eraseOperation)
1148{
1149 if (!checkSELReservation(reservationID))
1150 {
1151 return ipmi::responseInvalidReservationId();
1152 }
1153
1154 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1155 if (clr != clrExpected)
1156 {
1157 return ipmi::responseInvalidFieldRequest();
1158 }
1159
1160 // Erasure status cannot be fetched, so always return erasure status as
1161 // `erase completed`.
1162 if (eraseOperation == ipmi::sel::getEraseStatus)
1163 {
1164 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1165 }
1166
1167 // Check that initiate erase is correct
1168 if (eraseOperation != ipmi::sel::initiateErase)
1169 {
1170 return ipmi::responseInvalidFieldRequest();
1171 }
1172
1173 // Per the IPMI spec, need to cancel any reservation when the SEL is
1174 // cleared
1175 cancelSELReservation();
1176
Charles Boyer818bea12021-09-20 16:56:36 -05001177 boost::system::error_code ec;
1178 ctx->bus->yield_method_call<>(ctx->yield, ec, selLoggerServiceName,
1179 "/xyz/openbmc_project/Logging/IPMI",
1180 "xyz.openbmc_project.Logging.IPMI", "Clear");
1181 if (ec)
1182 {
1183 std::cerr << "error in clear SEL: " << ec << std::endl;
1184 return ipmi::responseUnspecifiedError();
1185 }
Willy Tude54f482021-01-26 15:59:09 -08001186
1187 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1188}
1189
Harvey Wu05d17c02021-09-15 08:46:59 +08001190std::vector<uint8_t>
1191 getType8SDRs(ipmi::sensor::EntityInfoMap::const_iterator& entity,
1192 uint16_t recordId)
1193{
1194 std::vector<uint8_t> resp;
1195 get_sdr::SensorDataEntityRecord data{};
1196
1197 /* Header */
1198 get_sdr::header::set_record_id(recordId, &(data.header));
1199 // Based on IPMI Spec v2.0 rev 1.1
1200 data.header.sdr_version = SDR_VERSION;
1201 data.header.record_type = 0x08;
1202 data.header.record_length = sizeof(data.key) + sizeof(data.body);
1203
1204 /* Key */
1205 data.key.containerEntityId = entity->second.containerEntityId;
1206 data.key.containerEntityInstance = entity->second.containerEntityInstance;
1207 get_sdr::key::set_flags(entity->second.isList, entity->second.isLinked,
1208 &(data.key));
1209 data.key.entityId1 = entity->second.containedEntities[0].first;
1210 data.key.entityInstance1 = entity->second.containedEntities[0].second;
1211
1212 /* Body */
1213 data.body.entityId2 = entity->second.containedEntities[1].first;
1214 data.body.entityInstance2 = entity->second.containedEntities[1].second;
1215 data.body.entityId3 = entity->second.containedEntities[2].first;
1216 data.body.entityInstance3 = entity->second.containedEntities[2].second;
1217 data.body.entityId4 = entity->second.containedEntities[3].first;
1218 data.body.entityInstance4 = entity->second.containedEntities[3].second;
1219
1220 resp.insert(resp.end(), (uint8_t*)&data, ((uint8_t*)&data) + sizeof(data));
1221
1222 return resp;
1223}
1224
Willy Tude54f482021-01-26 15:59:09 -08001225std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
1226{
1227 std::vector<uint8_t> resp;
1228 if (index == 0)
1229 {
Willy Tude54f482021-01-26 15:59:09 -08001230 std::string bmcName = "Basbrd Mgmt Ctlr";
Johnathan Manteycd1c4962021-09-22 12:58:08 -07001231 Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName);
Willy Tude54f482021-01-26 15:59:09 -08001232 uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
1233 resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
1234 }
1235 else if (index == 1)
1236 {
Willy Tude54f482021-01-26 15:59:09 -08001237 std::string meName = "Mgmt Engine";
Johnathan Manteycd1c4962021-09-22 12:58:08 -07001238 Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName);
Willy Tude54f482021-01-26 15:59:09 -08001239 uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
1240 resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
1241 }
1242 else
1243 {
1244 throw std::runtime_error("getType12SDRs:: Illegal index " +
1245 std::to_string(index));
1246 }
1247
1248 return resp;
1249}
1250
1251void registerStorageFunctions()
1252{
1253 createTimers();
1254 startMatch();
1255
1256 // <Get FRU Inventory Area Info>
Willy Tud351a722021-08-12 14:33:40 -07001257 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Willy Tude54f482021-01-26 15:59:09 -08001258 ipmi::storage::cmdGetFruInventoryAreaInfo,
1259 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1260 // <READ FRU Data>
1261 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1262 ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
1263 ipmiStorageReadFruData);
1264
1265 // <WRITE FRU Data>
1266 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1267 ipmi::storage::cmdWriteFruData,
1268 ipmi::Privilege::Operator, ipmiStorageWriteFruData);
1269
1270 // <Get SEL Info>
1271 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1272 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1273 ipmiStorageGetSELInfo);
1274
1275 // <Get SEL Entry>
1276 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1277 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1278 ipmiStorageGetSELEntry);
1279
1280 // <Add SEL Entry>
1281 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1282 ipmi::storage::cmdAddSelEntry,
1283 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1284
1285 // <Clear SEL>
1286 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1287 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1288 ipmiStorageClearSEL);
Willy Tude54f482021-01-26 15:59:09 -08001289}
1290} // namespace storage
1291} // namespace ipmi