blob: b61e13f832e93d8d50f05e5c6e05dc2f0bb9f204 [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
Patrick Williams95655222023-12-05 12:45:02 -0600106std::unique_ptr<sdbusplus::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{
Patrick Williams95655222023-12-05 12:45:02 -0600151 writeTimer = std::make_unique<sdbusplus::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
Alexander Hansenea46f3c2023-09-04 11:27:54 +0200672 std::vector<std::string> nameProperties = {
673 "PRODUCT_PRODUCT_NAME", "BOARD_PRODUCT_NAME", "PRODUCT_PART_NUMBER",
674 "BOARD_PART_NUMBER", "PRODUCT_MANUFACTURER", "BOARD_MANUFACTURER",
675 "PRODUCT_SERIAL_NUMBER", "BOARD_SERIAL_NUMBER"};
676
677 for (const std::string& prop : nameProperties)
678 {
679 auto findProp = fruData->find(prop);
680 if (findProp != fruData->end())
681 {
682 name = std::get<std::string>(findProp->second);
683 break;
684 }
685 }
686
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530687 if (name.empty())
Willy Tude54f482021-01-26 15:59:09 -0800688 {
689 name = "UNKNOWN";
690 }
691 if (name.size() > maxFruSdrNameSize)
692 {
693 name = name.substr(0, maxFruSdrNameSize);
694 }
695 size_t sizeDiff = maxFruSdrNameSize - name.size();
696
697 resp.header.record_id_lsb = 0x0; // calling code is to implement these
698 resp.header.record_id_msb = 0x0;
699 resp.header.sdr_version = ipmiSdrVersion;
700 resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
701 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
702 resp.key.deviceAddress = 0x20;
703 resp.key.fruID = device->first;
704 resp.key.accessLun = 0x80; // logical / physical fru device
705 resp.key.channelNumber = 0x0;
706 resp.body.reserved = 0x0;
707 resp.body.deviceType = 0x10;
708 resp.body.deviceTypeModifier = 0x0;
709
710 uint8_t entityID = 0;
711 uint8_t entityInstance = 0x1;
712
713#ifdef USING_ENTITY_MANAGER_DECORATORS
714 if (entityData)
715 {
716 auto entityIdProperty = entityData->find("EntityId");
717 auto entityInstanceProperty = entityData->find("EntityInstance");
718
719 if (entityIdProperty != entityData->end())
720 {
721 entityID = static_cast<uint8_t>(
722 std::get<uint64_t>(entityIdProperty->second));
723 }
724 if (entityInstanceProperty != entityData->end())
725 {
726 entityInstance = static_cast<uint8_t>(
727 std::get<uint64_t>(entityInstanceProperty->second));
728 }
729 }
730#endif
731
732 resp.body.entityID = entityID;
733 resp.body.entityInstance = entityInstance;
734
735 resp.body.oem = 0x0;
736 resp.body.deviceIDLen = name.size();
737 name.copy(resp.body.deviceID, name.size());
738
739 return IPMI_CC_OK;
740}
741
742static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
743{
744 // Loop through the directory looking for ipmi_sel log files
745 for (const std::filesystem::directory_entry& dirEnt :
746 std::filesystem::directory_iterator(
747 dynamic_sensors::ipmi::sel::selLogDir))
748 {
749 std::string filename = dirEnt.path().filename();
750 if (boost::starts_with(filename,
751 dynamic_sensors::ipmi::sel::selLogFilename))
752 {
753 // If we find an ipmi_sel log file, save the path
754 selLogFiles.emplace_back(dynamic_sensors::ipmi::sel::selLogDir /
755 filename);
756 }
757 }
758 // As the log files rotate, they are appended with a ".#" that is higher for
759 // the older logs. Since we don't expect more than 10 log files, we
760 // can just sort the list to get them in order from newest to oldest
761 std::sort(selLogFiles.begin(), selLogFiles.end());
762
763 return !selLogFiles.empty();
764}
765
766static int countSELEntries()
767{
768 // Get the list of ipmi_sel log files
769 std::vector<std::filesystem::path> selLogFiles;
770 if (!getSELLogFiles(selLogFiles))
771 {
772 return 0;
773 }
774 int numSELEntries = 0;
775 // Loop through each log file and count the number of logs
776 for (const std::filesystem::path& file : selLogFiles)
777 {
778 std::ifstream logStream(file);
779 if (!logStream.is_open())
780 {
781 continue;
782 }
783
784 std::string line;
785 while (std::getline(logStream, line))
786 {
787 numSELEntries++;
788 }
789 }
790 return numSELEntries;
791}
792
793static bool findSELEntry(const int recordID,
794 const std::vector<std::filesystem::path>& selLogFiles,
795 std::string& entry)
796{
797 // Record ID is the first entry field following the timestamp. It is
798 // preceded by a space and followed by a comma
799 std::string search = " " + std::to_string(recordID) + ",";
800
801 // Loop through the ipmi_sel log entries
802 for (const std::filesystem::path& file : selLogFiles)
803 {
804 std::ifstream logStream(file);
805 if (!logStream.is_open())
806 {
807 continue;
808 }
809
810 while (std::getline(logStream, entry))
811 {
812 // Check if the record ID matches
813 if (entry.find(search) != std::string::npos)
814 {
815 return true;
816 }
817 }
818 }
819 return false;
820}
821
822static uint16_t
823 getNextRecordID(const uint16_t recordID,
824 const std::vector<std::filesystem::path>& selLogFiles)
825{
826 uint16_t nextRecordID = recordID + 1;
827 std::string entry;
828 if (findSELEntry(nextRecordID, selLogFiles, entry))
829 {
830 return nextRecordID;
831 }
832 else
833 {
834 return ipmi::sel::lastEntry;
835 }
836}
837
838static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
839{
840 for (unsigned int i = 0; i < hexStr.size(); i += 2)
841 {
842 try
843 {
844 data.push_back(static_cast<uint8_t>(
845 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
846 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500847 catch (const std::invalid_argument& e)
Willy Tude54f482021-01-26 15:59:09 -0800848 {
849 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
850 return -1;
851 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500852 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800853 {
854 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
855 return -1;
856 }
857 }
858 return 0;
859}
860
861ipmi::RspType<uint8_t, // SEL version
862 uint16_t, // SEL entry count
863 uint16_t, // free space
864 uint32_t, // last add timestamp
865 uint32_t, // last erase timestamp
866 uint8_t> // operation support
867 ipmiStorageGetSELInfo()
868{
869 constexpr uint8_t selVersion = ipmi::sel::selVersion;
870 uint16_t entries = countSELEntries();
871 uint32_t addTimeStamp = dynamic_sensors::ipmi::sel::getFileTimestamp(
872 dynamic_sensors::ipmi::sel::selLogDir /
873 dynamic_sensors::ipmi::sel::selLogFilename);
874 uint32_t eraseTimeStamp = dynamic_sensors::ipmi::sel::erase_time::get();
875 constexpr uint8_t operationSupport =
876 dynamic_sensors::ipmi::sel::selOperationSupport;
877 constexpr uint16_t freeSpace =
878 0xffff; // Spec indicates that more than 64kB is free
879
880 return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
881 eraseTimeStamp, operationSupport);
882}
883
884using systemEventType = std::tuple<
885 uint32_t, // Timestamp
886 uint16_t, // Generator ID
887 uint8_t, // EvM Rev
888 uint8_t, // Sensor Type
889 uint8_t, // Sensor Number
890 uint7_t, // Event Type
891 bool, // Event Direction
892 std::array<uint8_t, dynamic_sensors::ipmi::sel::systemEventSize>>; // Event
893 // Data
894using oemTsEventType = std::tuple<
895 uint32_t, // Timestamp
896 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemTsEventSize>>; // Event
897 // Data
898using oemEventType =
899 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemEventSize>; // Event Data
900
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500901ipmi::RspType<uint16_t, // Next Record ID
902 uint16_t, // Record ID
903 uint8_t, // Record Type
Willy Tude54f482021-01-26 15:59:09 -0800904 std::variant<systemEventType, oemTsEventType,
905 oemEventType>> // Record Content
906 ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
907 uint8_t offset, uint8_t size)
908{
909 // Only support getting the entire SEL record. If a partial size or non-zero
910 // offset is requested, return an error
911 if (offset != 0 || size != ipmi::sel::entireRecord)
912 {
913 return ipmi::responseRetBytesUnavailable();
914 }
915
916 // Check the reservation ID if one is provided or required (only if the
917 // offset is non-zero)
918 if (reservationID != 0 || offset != 0)
919 {
920 if (!checkSELReservation(reservationID))
921 {
922 return ipmi::responseInvalidReservationId();
923 }
924 }
925
926 // Get the ipmi_sel log files
927 std::vector<std::filesystem::path> selLogFiles;
928 if (!getSELLogFiles(selLogFiles))
929 {
930 return ipmi::responseSensorInvalid();
931 }
932
933 std::string targetEntry;
934
935 if (targetID == ipmi::sel::firstEntry)
936 {
937 // The first entry will be at the top of the oldest log file
938 std::ifstream logStream(selLogFiles.back());
939 if (!logStream.is_open())
940 {
941 return ipmi::responseUnspecifiedError();
942 }
943
944 if (!std::getline(logStream, targetEntry))
945 {
946 return ipmi::responseUnspecifiedError();
947 }
948 }
949 else if (targetID == ipmi::sel::lastEntry)
950 {
951 // The last entry will be at the bottom of the newest log file
952 std::ifstream logStream(selLogFiles.front());
953 if (!logStream.is_open())
954 {
955 return ipmi::responseUnspecifiedError();
956 }
957
958 std::string line;
959 while (std::getline(logStream, line))
960 {
961 targetEntry = line;
962 }
963 }
964 else
965 {
966 if (!findSELEntry(targetID, selLogFiles, targetEntry))
967 {
968 return ipmi::responseSensorInvalid();
969 }
970 }
971
972 // The format of the ipmi_sel message is "<Timestamp>
973 // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
974 // First get the Timestamp
975 size_t space = targetEntry.find_first_of(" ");
976 if (space == std::string::npos)
977 {
978 return ipmi::responseUnspecifiedError();
979 }
980 std::string entryTimestamp = targetEntry.substr(0, space);
981 // Then get the log contents
982 size_t entryStart = targetEntry.find_first_not_of(" ", space);
983 if (entryStart == std::string::npos)
984 {
985 return ipmi::responseUnspecifiedError();
986 }
987 std::string_view entry(targetEntry);
988 entry.remove_prefix(entryStart);
989 // Use split to separate the entry into its fields
990 std::vector<std::string> targetEntryFields;
991 boost::split(targetEntryFields, entry, boost::is_any_of(","),
992 boost::token_compress_on);
993 if (targetEntryFields.size() < 3)
994 {
995 return ipmi::responseUnspecifiedError();
996 }
997 std::string& recordIDStr = targetEntryFields[0];
998 std::string& recordTypeStr = targetEntryFields[1];
999 std::string& eventDataStr = targetEntryFields[2];
1000
1001 uint16_t recordID;
1002 uint8_t recordType;
1003 try
1004 {
1005 recordID = std::stoul(recordIDStr);
1006 recordType = std::stoul(recordTypeStr, nullptr, 16);
1007 }
1008 catch (const std::invalid_argument&)
1009 {
1010 return ipmi::responseUnspecifiedError();
1011 }
1012 uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
1013 std::vector<uint8_t> eventDataBytes;
1014 if (fromHexStr(eventDataStr, eventDataBytes) < 0)
1015 {
1016 return ipmi::responseUnspecifiedError();
1017 }
1018
1019 if (recordType == dynamic_sensors::ipmi::sel::systemEvent)
1020 {
1021 // Get the timestamp
1022 std::tm timeStruct = {};
1023 std::istringstream entryStream(entryTimestamp);
1024
1025 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1026 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1027 {
Willy Tu7bb412f2023-09-25 11:30:45 -07001028 timeStruct.tm_isdst = -1;
Willy Tude54f482021-01-26 15:59:09 -08001029 timestamp = std::mktime(&timeStruct);
1030 }
1031
1032 // Set the event message revision
1033 uint8_t evmRev = dynamic_sensors::ipmi::sel::eventMsgRev;
1034
1035 uint16_t generatorID = 0;
1036 uint8_t sensorType = 0;
1037 uint16_t sensorAndLun = 0;
1038 uint8_t sensorNum = 0xFF;
1039 uint7_t eventType = 0;
1040 bool eventDir = 0;
1041 // System type events should have six fields
1042 if (targetEntryFields.size() >= 6)
1043 {
1044 std::string& generatorIDStr = targetEntryFields[3];
1045 std::string& sensorPath = targetEntryFields[4];
1046 std::string& eventDirStr = targetEntryFields[5];
1047
1048 // Get the generator ID
1049 try
1050 {
1051 generatorID = std::stoul(generatorIDStr, nullptr, 16);
1052 }
1053 catch (const std::invalid_argument&)
1054 {
1055 std::cerr << "Invalid Generator ID\n";
1056 }
1057
1058 // Get the sensor type, sensor number, and event type for the sensor
1059 sensorType = getSensorTypeFromPath(sensorPath);
1060 sensorAndLun = getSensorNumberFromPath(sensorPath);
1061 sensorNum = static_cast<uint8_t>(sensorAndLun);
Harvey.Wu4376cdf2021-11-16 19:40:55 +08001062 if ((generatorID & 0x0001) == 0)
1063 {
1064 // IPMB Address
1065 generatorID |= sensorAndLun & 0x0300;
1066 }
1067 else
1068 {
1069 // system software
1070 generatorID |= sensorAndLun >> 8;
1071 }
Willy Tude54f482021-01-26 15:59:09 -08001072 eventType = getSensorEventTypeFromPath(sensorPath);
1073
1074 // Get the event direction
1075 try
1076 {
1077 eventDir = std::stoul(eventDirStr) ? 0 : 1;
1078 }
1079 catch (const std::invalid_argument&)
1080 {
1081 std::cerr << "Invalid Event Direction\n";
1082 }
1083 }
1084
1085 // Only keep the eventData bytes that fit in the record
1086 std::array<uint8_t, dynamic_sensors::ipmi::sel::systemEventSize>
1087 eventData{};
1088 std::copy_n(eventDataBytes.begin(),
1089 std::min(eventDataBytes.size(), eventData.size()),
1090 eventData.begin());
1091
1092 return ipmi::responseSuccess(
1093 nextRecordID, recordID, recordType,
1094 systemEventType{timestamp, generatorID, evmRev, sensorType,
1095 sensorNum, eventType, eventDir, eventData});
1096 }
1097
Thang Trane70c59b2023-09-21 13:54:28 +07001098 if (recordType >= dynamic_sensors::ipmi::sel::oemTsEventFirst &&
1099 recordType <= dynamic_sensors::ipmi::sel::oemTsEventLast)
1100 {
1101 // Get the timestamp
1102 std::tm timeStruct = {};
1103 std::istringstream entryStream(entryTimestamp);
1104
1105 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1106 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1107 {
1108 timeStruct.tm_isdst = -1;
1109 timestamp = std::mktime(&timeStruct);
1110 }
1111
1112 // Only keep the bytes that fit in the record
1113 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemTsEventSize>
1114 eventData{};
1115 std::copy_n(eventDataBytes.begin(),
1116 std::min(eventDataBytes.size(), eventData.size()),
1117 eventData.begin());
1118
1119 return ipmi::responseSuccess(nextRecordID, recordID, recordType,
1120 oemTsEventType{timestamp, eventData});
1121 }
1122
1123 if (recordType >= dynamic_sensors::ipmi::sel::oemEventFirst)
1124 {
1125 // Only keep the bytes that fit in the record
1126 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemEventSize>
1127 eventData{};
1128 std::copy_n(eventDataBytes.begin(),
1129 std::min(eventDataBytes.size(), eventData.size()),
1130 eventData.begin());
1131
1132 return ipmi::responseSuccess(nextRecordID, recordID, recordType,
1133 eventData);
1134 }
1135
Willy Tude54f482021-01-26 15:59:09 -08001136 return ipmi::responseUnspecifiedError();
1137}
1138
Willy Tu11d68892022-01-20 10:37:34 -08001139/*
1140Unused arguments
1141 uint16_t recordID, uint8_t recordType, uint32_t timestamp,
1142 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
1143 uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
1144 uint8_t eventData3
1145*/
1146ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(uint16_t, uint8_t, uint32_t,
1147 uint16_t, uint8_t, uint8_t,
1148 uint8_t, uint8_t, uint8_t,
1149 uint8_t, uint8_t)
Willy Tude54f482021-01-26 15:59:09 -08001150{
1151 // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1152 // added
1153 cancelSELReservation();
1154
1155 uint16_t responseID = 0xFFFF;
1156 return ipmi::responseSuccess(responseID);
1157}
1158
Tim Lee11317d72022-07-27 10:13:46 +08001159ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr ctx,
Willy Tude54f482021-01-26 15:59:09 -08001160 uint16_t reservationID,
1161 const std::array<uint8_t, 3>& clr,
1162 uint8_t eraseOperation)
1163{
1164 if (!checkSELReservation(reservationID))
1165 {
1166 return ipmi::responseInvalidReservationId();
1167 }
1168
1169 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1170 if (clr != clrExpected)
1171 {
1172 return ipmi::responseInvalidFieldRequest();
1173 }
1174
1175 // Erasure status cannot be fetched, so always return erasure status as
1176 // `erase completed`.
1177 if (eraseOperation == ipmi::sel::getEraseStatus)
1178 {
1179 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1180 }
1181
1182 // Check that initiate erase is correct
1183 if (eraseOperation != ipmi::sel::initiateErase)
1184 {
1185 return ipmi::responseInvalidFieldRequest();
1186 }
1187
1188 // Per the IPMI spec, need to cancel any reservation when the SEL is
1189 // cleared
1190 cancelSELReservation();
1191
Charles Boyer818bea12021-09-20 16:56:36 -05001192 boost::system::error_code ec;
1193 ctx->bus->yield_method_call<>(ctx->yield, ec, selLoggerServiceName,
1194 "/xyz/openbmc_project/Logging/IPMI",
1195 "xyz.openbmc_project.Logging.IPMI", "Clear");
1196 if (ec)
1197 {
1198 std::cerr << "error in clear SEL: " << ec << std::endl;
1199 return ipmi::responseUnspecifiedError();
1200 }
Willy Tude54f482021-01-26 15:59:09 -08001201
1202 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1203}
1204
Harvey Wu05d17c02021-09-15 08:46:59 +08001205std::vector<uint8_t>
1206 getType8SDRs(ipmi::sensor::EntityInfoMap::const_iterator& entity,
1207 uint16_t recordId)
1208{
1209 std::vector<uint8_t> resp;
1210 get_sdr::SensorDataEntityRecord data{};
1211
1212 /* Header */
1213 get_sdr::header::set_record_id(recordId, &(data.header));
1214 // Based on IPMI Spec v2.0 rev 1.1
1215 data.header.sdr_version = SDR_VERSION;
1216 data.header.record_type = 0x08;
1217 data.header.record_length = sizeof(data.key) + sizeof(data.body);
1218
1219 /* Key */
1220 data.key.containerEntityId = entity->second.containerEntityId;
1221 data.key.containerEntityInstance = entity->second.containerEntityInstance;
1222 get_sdr::key::set_flags(entity->second.isList, entity->second.isLinked,
1223 &(data.key));
1224 data.key.entityId1 = entity->second.containedEntities[0].first;
1225 data.key.entityInstance1 = entity->second.containedEntities[0].second;
1226
1227 /* Body */
1228 data.body.entityId2 = entity->second.containedEntities[1].first;
1229 data.body.entityInstance2 = entity->second.containedEntities[1].second;
1230 data.body.entityId3 = entity->second.containedEntities[2].first;
1231 data.body.entityInstance3 = entity->second.containedEntities[2].second;
1232 data.body.entityId4 = entity->second.containedEntities[3].first;
1233 data.body.entityInstance4 = entity->second.containedEntities[3].second;
1234
1235 resp.insert(resp.end(), (uint8_t*)&data, ((uint8_t*)&data) + sizeof(data));
1236
1237 return resp;
1238}
1239
Willy Tude54f482021-01-26 15:59:09 -08001240std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
1241{
1242 std::vector<uint8_t> resp;
1243 if (index == 0)
1244 {
Willy Tude54f482021-01-26 15:59:09 -08001245 std::string bmcName = "Basbrd Mgmt Ctlr";
Johnathan Manteycd1c4962021-09-22 12:58:08 -07001246 Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName);
Willy Tude54f482021-01-26 15:59:09 -08001247 uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
1248 resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
1249 }
1250 else if (index == 1)
1251 {
Willy Tude54f482021-01-26 15:59:09 -08001252 std::string meName = "Mgmt Engine";
Johnathan Manteycd1c4962021-09-22 12:58:08 -07001253 Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName);
Willy Tude54f482021-01-26 15:59:09 -08001254 uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
1255 resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
1256 }
1257 else
1258 {
1259 throw std::runtime_error("getType12SDRs:: Illegal index " +
1260 std::to_string(index));
1261 }
1262
1263 return resp;
1264}
1265
1266void registerStorageFunctions()
1267{
1268 createTimers();
1269 startMatch();
1270
1271 // <Get FRU Inventory Area Info>
Willy Tud351a722021-08-12 14:33:40 -07001272 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Willy Tude54f482021-01-26 15:59:09 -08001273 ipmi::storage::cmdGetFruInventoryAreaInfo,
1274 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1275 // <READ FRU Data>
1276 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1277 ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
1278 ipmiStorageReadFruData);
1279
1280 // <WRITE FRU Data>
1281 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1282 ipmi::storage::cmdWriteFruData,
1283 ipmi::Privilege::Operator, ipmiStorageWriteFruData);
1284
1285 // <Get SEL Info>
1286 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1287 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1288 ipmiStorageGetSELInfo);
1289
1290 // <Get SEL Entry>
1291 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1292 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1293 ipmiStorageGetSELEntry);
1294
1295 // <Add SEL Entry>
1296 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1297 ipmi::storage::cmdAddSelEntry,
1298 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1299
1300 // <Clear SEL>
1301 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1302 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1303 ipmiStorageClearSEL);
Willy Tude54f482021-01-26 15:59:09 -08001304}
1305} // namespace storage
1306} // namespace ipmi