blob: b50db1a10bd8a6facdbb3235dd3cd1ad18745755 [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>
25#include <filesystem>
Ed Tanous5d380672022-05-04 15:58:14 -070026#include <fstream>
Willy Tude54f482021-01-26 15:59:09 -080027#include <functional>
28#include <iostream>
29#include <ipmid/api.hpp>
30#include <ipmid/message.hpp>
31#include <ipmid/types.hpp>
32#include <phosphor-logging/log.hpp>
33#include <sdbusplus/message/types.hpp>
34#include <sdbusplus/timer.hpp>
35#include <stdexcept>
36#include <string_view>
37
38static constexpr bool DEBUG = false;
39
40namespace dynamic_sensors::ipmi::sel
41{
42static const std::filesystem::path selLogDir = "/var/log";
43static const std::string selLogFilename = "ipmi_sel";
44
45static int getFileTimestamp(const std::filesystem::path& file)
46{
47 struct stat st;
48
49 if (stat(file.c_str(), &st) >= 0)
50 {
51 return st.st_mtime;
52 }
53 return ::ipmi::sel::invalidTimeStamp;
54}
55
56namespace erase_time
57{
58static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time";
59
60void save()
61{
62 // open the file, creating it if necessary
63 int fd = open(selEraseTimestamp, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
64 if (fd < 0)
65 {
66 std::cerr << "Failed to open file\n";
67 return;
68 }
69
70 // update the file timestamp to the current time
71 if (futimens(fd, NULL) < 0)
72 {
73 std::cerr << "Failed to update timestamp: "
74 << std::string(strerror(errno));
75 }
76 close(fd);
77}
78
79int get()
80{
81 return getFileTimestamp(selEraseTimestamp);
82}
83} // namespace erase_time
84} // namespace dynamic_sensors::ipmi::sel
85
86namespace ipmi
87{
88
89namespace storage
90{
91
92constexpr static const size_t maxMessageSize = 64;
93constexpr static const size_t maxFruSdrNameSize = 16;
94using ObjectType =
95 boost::container::flat_map<std::string,
96 boost::container::flat_map<std::string, Value>>;
97using ManagedObjectType =
98 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
99using ManagedEntry = std::pair<sdbusplus::message::object_path, ObjectType>;
100
Charles Boyer818bea12021-09-20 16:56:36 -0500101constexpr static const char* selLoggerServiceName =
102 "xyz.openbmc_project.Logging.IPMI";
Willy Tude54f482021-01-26 15:59:09 -0800103constexpr static const char* fruDeviceServiceName =
104 "xyz.openbmc_project.FruDevice";
105constexpr static const char* entityManagerServiceName =
106 "xyz.openbmc_project.EntityManager";
107constexpr static const size_t writeTimeoutSeconds = 10;
108constexpr static const char* chassisTypeRackMount = "23";
Zev Weissf38f9d12021-05-21 13:30:16 -0500109constexpr static const char* chassisTypeMainServer = "17";
Willy Tude54f482021-01-26 15:59:09 -0800110
111// event direction is bit[7] of eventType where 1b = Deassertion event
112constexpr static const uint8_t deassertionEvent = 0x80;
113
114static std::vector<uint8_t> fruCache;
krishnar4d90b3f02022-11-11 16:18:32 +0530115static constexpr uint16_t invalidBus = 0xFFFF;
116static constexpr uint8_t invalidAddr = 0xFF;
117static uint16_t cacheBus = invalidBus;
118static uint8_t cacheAddr = invalidAddr;
Willy Tude54f482021-01-26 15:59:09 -0800119static uint8_t lastDevId = 0xFF;
120
krishnar4d90b3f02022-11-11 16:18:32 +0530121static uint16_t writeBus = invalidBus;
122static uint8_t writeAddr = invalidAddr;
Willy Tude54f482021-01-26 15:59:09 -0800123
124std::unique_ptr<phosphor::Timer> writeTimer = nullptr;
Patrick Williams5d82f472022-07-22 19:26:53 -0500125static std::vector<sdbusplus::bus::match_t> fruMatches;
Willy Tude54f482021-01-26 15:59:09 -0800126
127ManagedObjectType frus;
128
129// we unfortunately have to build a map of hashes in case there is a
130// collision to verify our dev-id
krishnar4d90b3f02022-11-11 16:18:32 +0530131boost::container::flat_map<uint8_t, std::pair<uint16_t, uint8_t>> deviceHashes;
Willy Tude54f482021-01-26 15:59:09 -0800132void registerStorageFunctions() __attribute__((constructor));
133
Willy Tu48fe64e2022-08-01 23:23:46 +0000134bool writeFru(const std::vector<uint8_t>& fru)
Willy Tude54f482021-01-26 15:59:09 -0800135{
krishnar4d90b3f02022-11-11 16:18:32 +0530136 if (writeBus == invalidBus && writeAddr == invalidAddr)
Willy Tude54f482021-01-26 15:59:09 -0800137 {
138 return true;
139 }
Thang Trand934be92021-12-08 10:13:50 +0700140 lastDevId = 0xFF;
Willy Tude54f482021-01-26 15:59:09 -0800141 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williams5d82f472022-07-22 19:26:53 -0500142 sdbusplus::message_t writeFru = dbus->new_method_call(
Willy Tude54f482021-01-26 15:59:09 -0800143 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
144 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
Willy Tu48fe64e2022-08-01 23:23:46 +0000145 writeFru.append(writeBus, writeAddr, fru);
Willy Tude54f482021-01-26 15:59:09 -0800146 try
147 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500148 sdbusplus::message_t writeFruResp = dbus->call(writeFru);
Willy Tude54f482021-01-26 15:59:09 -0800149 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500150 catch (const sdbusplus::exception_t&)
Willy Tude54f482021-01-26 15:59:09 -0800151 {
152 // todo: log sel?
153 phosphor::logging::log<phosphor::logging::level::ERR>(
154 "error writing fru");
155 return false;
156 }
krishnar4d90b3f02022-11-11 16:18:32 +0530157 writeBus = invalidBus;
158 writeAddr = invalidAddr;
Willy Tude54f482021-01-26 15:59:09 -0800159 return true;
160}
161
William A. Kennington III52535622022-11-28 18:28:22 -0800162void writeFruCache()
Willy Tu48fe64e2022-08-01 23:23:46 +0000163{
William A. Kennington III52535622022-11-28 18:28:22 -0800164 writeFru(fruCache);
Willy Tu48fe64e2022-08-01 23:23:46 +0000165}
166
Willy Tude54f482021-01-26 15:59:09 -0800167void createTimers()
168{
Willy Tu48fe64e2022-08-01 23:23:46 +0000169 writeTimer = std::make_unique<phosphor::Timer>(writeFruCache);
Willy Tude54f482021-01-26 15:59:09 -0800170}
171
172void recalculateHashes()
173{
174
175 deviceHashes.clear();
176 // hash the object paths to create unique device id's. increment on
177 // collision
178 std::hash<std::string> hasher;
179 for (const auto& fru : frus)
180 {
181 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
182 if (fruIface == fru.second.end())
183 {
184 continue;
185 }
186
187 auto busFind = fruIface->second.find("BUS");
188 auto addrFind = fruIface->second.find("ADDRESS");
189 if (busFind == fruIface->second.end() ||
190 addrFind == fruIface->second.end())
191 {
192 phosphor::logging::log<phosphor::logging::level::INFO>(
193 "fru device missing Bus or Address",
194 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
195 continue;
196 }
197
krishnar4d90b3f02022-11-11 16:18:32 +0530198 uint16_t fruBus = std::get<uint32_t>(busFind->second);
Willy Tude54f482021-01-26 15:59:09 -0800199 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
200 auto chassisFind = fruIface->second.find("CHASSIS_TYPE");
201 std::string chassisType;
202 if (chassisFind != fruIface->second.end())
203 {
204 chassisType = std::get<std::string>(chassisFind->second);
205 }
206
207 uint8_t fruHash = 0;
Zev Weissf38f9d12021-05-21 13:30:16 -0500208 if (chassisType.compare(chassisTypeRackMount) != 0 &&
209 chassisType.compare(chassisTypeMainServer) != 0)
Willy Tude54f482021-01-26 15:59:09 -0800210 {
211 fruHash = hasher(fru.first.str);
212 // can't be 0xFF based on spec, and 0 is reserved for baseboard
213 if (fruHash == 0 || fruHash == 0xFF)
214 {
215 fruHash = 1;
216 }
217 }
krishnar4d90b3f02022-11-11 16:18:32 +0530218 std::pair<uint16_t, uint8_t> newDev(fruBus, fruAddr);
Willy Tude54f482021-01-26 15:59:09 -0800219
220 bool emplacePassed = false;
221 while (!emplacePassed)
222 {
223 auto resp = deviceHashes.emplace(fruHash, newDev);
224 emplacePassed = resp.second;
225 if (!emplacePassed)
226 {
227 fruHash++;
228 // can't be 0xFF based on spec, and 0 is reserved for
229 // baseboard
230 if (fruHash == 0XFF)
231 {
232 fruHash = 0x1;
233 }
234 }
235 }
236 }
237}
238
Willy Tu11d68892022-01-20 10:37:34 -0800239void replaceCacheFru(
240 const std::shared_ptr<sdbusplus::asio::connection>& bus,
241 boost::asio::yield_context& yield,
242 [[maybe_unused]] const std::optional<std::string>& path = std::nullopt)
Willy Tude54f482021-01-26 15:59:09 -0800243{
244 boost::system::error_code ec;
245
246 frus = bus->yield_method_call<ManagedObjectType>(
247 yield, ec, fruDeviceServiceName, "/",
248 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
249 if (ec)
250 {
251 phosphor::logging::log<phosphor::logging::level::ERR>(
252 "GetMangagedObjects for replaceCacheFru failed",
253 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
254
255 return;
256 }
257 recalculateHashes();
258}
259
Willy Tu48fe64e2022-08-01 23:23:46 +0000260std::pair<ipmi::Cc, std::vector<uint8_t>> getFru(ipmi::Context::ptr ctx,
261 uint8_t devId)
Willy Tude54f482021-01-26 15:59:09 -0800262{
263 if (lastDevId == devId && devId != 0xFF)
264 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000265 return {ipmi::ccSuccess, fruCache};
Willy Tude54f482021-01-26 15:59:09 -0800266 }
267
Willy Tude54f482021-01-26 15:59:09 -0800268 auto deviceFind = deviceHashes.find(devId);
269 if (deviceFind == deviceHashes.end())
270 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000271 return {IPMI_CC_SENSOR_INVALID, {}};
Willy Tude54f482021-01-26 15:59:09 -0800272 }
273
Willy Tude54f482021-01-26 15:59:09 -0800274 cacheBus = deviceFind->second.first;
275 cacheAddr = deviceFind->second.second;
276
277 boost::system::error_code ec;
278
Willy Tu48fe64e2022-08-01 23:23:46 +0000279 std::vector<uint8_t> fru =
280 ctx->bus->yield_method_call<std::vector<uint8_t>>(
281 ctx->yield, ec, fruDeviceServiceName,
282 "/xyz/openbmc_project/FruDevice",
283 "xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus,
284 cacheAddr);
Willy Tude54f482021-01-26 15:59:09 -0800285 if (ec)
286 {
287 phosphor::logging::log<phosphor::logging::level::ERR>(
288 "Couldn't get raw fru",
289 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
290
krishnar4d90b3f02022-11-11 16:18:32 +0530291 cacheBus = invalidBus;
292 cacheAddr = invalidAddr;
Willy Tu48fe64e2022-08-01 23:23:46 +0000293 return {ipmi::ccResponseError, {}};
Willy Tude54f482021-01-26 15:59:09 -0800294 }
295
Willy Tu48fe64e2022-08-01 23:23:46 +0000296 fruCache.clear();
Willy Tude54f482021-01-26 15:59:09 -0800297 lastDevId = devId;
Willy Tu48fe64e2022-08-01 23:23:46 +0000298 fruCache = fru;
299
300 return {ipmi::ccSuccess, fru};
Willy Tude54f482021-01-26 15:59:09 -0800301}
302
303void writeFruIfRunning()
304{
305 if (!writeTimer->isRunning())
306 {
307 return;
308 }
309 writeTimer->stop();
Willy Tu48fe64e2022-08-01 23:23:46 +0000310 writeFruCache();
Willy Tude54f482021-01-26 15:59:09 -0800311}
312
313void startMatch(void)
314{
315 if (fruMatches.size())
316 {
317 return;
318 }
319
320 fruMatches.reserve(2);
321
322 auto bus = getSdBus();
323 fruMatches.emplace_back(*bus,
324 "type='signal',arg0path='/xyz/openbmc_project/"
325 "FruDevice/',member='InterfacesAdded'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500326 [](sdbusplus::message_t& message) {
Willy Tude54f482021-01-26 15:59:09 -0800327 sdbusplus::message::object_path path;
328 ObjectType object;
329 try
330 {
331 message.read(path, object);
332 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500333 catch (const sdbusplus::exception_t&)
Willy Tude54f482021-01-26 15:59:09 -0800334 {
335 return;
336 }
337 auto findType = object.find(
338 "xyz.openbmc_project.FruDevice");
339 if (findType == object.end())
340 {
341 return;
342 }
343 writeFruIfRunning();
344 frus[path] = object;
345 recalculateHashes();
346 lastDevId = 0xFF;
347 });
348
349 fruMatches.emplace_back(*bus,
350 "type='signal',arg0path='/xyz/openbmc_project/"
351 "FruDevice/',member='InterfacesRemoved'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500352 [](sdbusplus::message_t& message) {
Willy Tude54f482021-01-26 15:59:09 -0800353 sdbusplus::message::object_path path;
354 std::set<std::string> interfaces;
355 try
356 {
357 message.read(path, interfaces);
358 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500359 catch (const sdbusplus::exception_t&)
Willy Tude54f482021-01-26 15:59:09 -0800360 {
361 return;
362 }
363 auto findType = interfaces.find(
364 "xyz.openbmc_project.FruDevice");
365 if (findType == interfaces.end())
366 {
367 return;
368 }
369 writeFruIfRunning();
370 frus.erase(path);
371 recalculateHashes();
372 lastDevId = 0xFF;
373 });
374
375 // call once to populate
376 boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) {
377 replaceCacheFru(getSdBus(), yield);
378 });
379}
380
381/** @brief implements the read FRU data command
382 * @param fruDeviceId - FRU Device ID
383 * @param fruInventoryOffset - FRU Inventory Offset to write
384 * @param countToRead - Count to read
385 *
386 * @returns ipmi completion code plus response data
387 * - countWritten - Count written
388 */
389ipmi::RspType<uint8_t, // Count
390 std::vector<uint8_t> // Requested data
391 >
392 ipmiStorageReadFruData(ipmi::Context::ptr ctx, uint8_t fruDeviceId,
393 uint16_t fruInventoryOffset, uint8_t countToRead)
394{
395 if (fruDeviceId == 0xFF)
396 {
397 return ipmi::responseInvalidFieldRequest();
398 }
399
Willy Tu48fe64e2022-08-01 23:23:46 +0000400 auto [status, fru] = getFru(ctx, fruDeviceId);
Willy Tude54f482021-01-26 15:59:09 -0800401 if (status != ipmi::ccSuccess)
402 {
403 return ipmi::response(status);
404 }
405
406 size_t fromFruByteLen = 0;
Willy Tu48fe64e2022-08-01 23:23:46 +0000407 if (countToRead + fruInventoryOffset < fru.size())
Willy Tude54f482021-01-26 15:59:09 -0800408 {
409 fromFruByteLen = countToRead;
410 }
Willy Tu48fe64e2022-08-01 23:23:46 +0000411 else if (fru.size() > fruInventoryOffset)
Willy Tude54f482021-01-26 15:59:09 -0800412 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000413 fromFruByteLen = fru.size() - fruInventoryOffset;
Willy Tude54f482021-01-26 15:59:09 -0800414 }
415 else
416 {
417 return ipmi::responseReqDataLenExceeded();
418 }
419
420 std::vector<uint8_t> requestedData;
421
Willy Tu48fe64e2022-08-01 23:23:46 +0000422 requestedData.insert(requestedData.begin(),
423 fru.begin() + fruInventoryOffset,
424 fru.begin() + fruInventoryOffset + fromFruByteLen);
Willy Tude54f482021-01-26 15:59:09 -0800425
426 return ipmi::responseSuccess(static_cast<uint8_t>(requestedData.size()),
427 requestedData);
428}
429
430/** @brief implements the write FRU data command
431 * @param fruDeviceId - FRU Device ID
432 * @param fruInventoryOffset - FRU Inventory Offset to write
433 * @param dataToWrite - Data to write
434 *
435 * @returns ipmi completion code plus response data
436 * - countWritten - Count written
437 */
438ipmi::RspType<uint8_t>
439 ipmiStorageWriteFruData(ipmi::Context::ptr ctx, uint8_t fruDeviceId,
440 uint16_t fruInventoryOffset,
441 std::vector<uint8_t>& dataToWrite)
442{
443 if (fruDeviceId == 0xFF)
444 {
445 return ipmi::responseInvalidFieldRequest();
446 }
447
448 size_t writeLen = dataToWrite.size();
449
Willy Tu48fe64e2022-08-01 23:23:46 +0000450 auto [status, fru] = getFru(ctx, fruDeviceId);
Willy Tude54f482021-01-26 15:59:09 -0800451 if (status != ipmi::ccSuccess)
452 {
453 return ipmi::response(status);
454 }
455 size_t lastWriteAddr = fruInventoryOffset + writeLen;
Willy Tu48fe64e2022-08-01 23:23:46 +0000456 if (fru.size() < lastWriteAddr)
Willy Tude54f482021-01-26 15:59:09 -0800457 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000458 fru.resize(fruInventoryOffset + writeLen);
Willy Tude54f482021-01-26 15:59:09 -0800459 }
460
461 std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
Willy Tu48fe64e2022-08-01 23:23:46 +0000462 fru.begin() + fruInventoryOffset);
Willy Tude54f482021-01-26 15:59:09 -0800463
464 bool atEnd = false;
465
Willy Tu48fe64e2022-08-01 23:23:46 +0000466 if (fru.size() >= sizeof(FRUHeader))
Willy Tude54f482021-01-26 15:59:09 -0800467 {
Willy Tu48fe64e2022-08-01 23:23:46 +0000468 FRUHeader* header = reinterpret_cast<FRUHeader*>(fru.data());
Willy Tude54f482021-01-26 15:59:09 -0800469
470 size_t areaLength = 0;
471 size_t lastRecordStart = std::max(
472 {header->internalOffset, header->chassisOffset, header->boardOffset,
473 header->productOffset, header->multiRecordOffset});
474 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
475
476 if (header->multiRecordOffset)
477 {
478 // This FRU has a MultiRecord Area
479 uint8_t endOfList = 0;
480 // Walk the MultiRecord headers until the last record
481 while (!endOfList)
482 {
483 // The MSB in the second byte of the MultiRecord header signals
484 // "End of list"
Willy Tu48fe64e2022-08-01 23:23:46 +0000485 endOfList = fru[lastRecordStart + 1] & 0x80;
Willy Tude54f482021-01-26 15:59:09 -0800486 // Third byte in the MultiRecord header is the length
Willy Tu48fe64e2022-08-01 23:23:46 +0000487 areaLength = fru[lastRecordStart + 2];
Willy Tude54f482021-01-26 15:59:09 -0800488 // This length is in bytes (not 8 bytes like other headers)
489 areaLength += 5; // The length omits the 5 byte header
490 if (!endOfList)
491 {
492 // Next MultiRecord header
493 lastRecordStart += areaLength;
494 }
495 }
496 }
497 else
498 {
499 // This FRU does not have a MultiRecord Area
500 // Get the length of the area in multiples of 8 bytes
501 if (lastWriteAddr > (lastRecordStart + 1))
502 {
503 // second byte in record area is the length
Willy Tu48fe64e2022-08-01 23:23:46 +0000504 areaLength = fru[lastRecordStart + 1];
Willy Tude54f482021-01-26 15:59:09 -0800505 areaLength *= 8; // it is in multiples of 8 bytes
506 }
507 }
508 if (lastWriteAddr >= (areaLength + lastRecordStart))
509 {
510 atEnd = true;
511 }
512 }
513 uint8_t countWritten = 0;
514
515 writeBus = cacheBus;
516 writeAddr = cacheAddr;
517 if (atEnd)
518 {
519 // cancel timer, we're at the end so might as well send it
520 writeTimer->stop();
Willy Tu48fe64e2022-08-01 23:23:46 +0000521 if (!writeFru(fru))
Willy Tude54f482021-01-26 15:59:09 -0800522 {
523 return ipmi::responseInvalidFieldRequest();
524 }
Willy Tu48fe64e2022-08-01 23:23:46 +0000525 countWritten = std::min(fru.size(), static_cast<size_t>(0xFF));
Willy Tude54f482021-01-26 15:59:09 -0800526 }
527 else
528 {
Sui Chen548d1a22022-09-14 07:41:17 -0700529 fruCache = fru; // Write-back
Willy Tude54f482021-01-26 15:59:09 -0800530 // start a timer, if no further data is sent to check to see if it is
531 // valid
532 writeTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
533 std::chrono::seconds(writeTimeoutSeconds)));
534 countWritten = 0;
535 }
536
537 return ipmi::responseSuccess(countWritten);
538}
539
540/** @brief implements the get FRU inventory area info command
541 * @param fruDeviceId - FRU Device ID
542 *
543 * @returns IPMI completion code plus response data
544 * - inventorySize - Number of possible allocation units
545 * - accessType - Allocation unit size in bytes.
546 */
547ipmi::RspType<uint16_t, // inventorySize
548 uint8_t> // accessType
549 ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr ctx, uint8_t fruDeviceId)
550{
551 if (fruDeviceId == 0xFF)
552 {
553 return ipmi::responseInvalidFieldRequest();
554 }
555
Willy Tu48fe64e2022-08-01 23:23:46 +0000556 auto [ret, fru] = getFru(ctx, fruDeviceId);
Willy Tude54f482021-01-26 15:59:09 -0800557 if (ret != ipmi::ccSuccess)
558 {
559 return ipmi::response(ret);
560 }
561
562 constexpr uint8_t accessType =
563 static_cast<uint8_t>(GetFRUAreaAccessType::byte);
564
Willy Tu48fe64e2022-08-01 23:23:46 +0000565 return ipmi::responseSuccess(fru.size(), accessType);
Willy Tude54f482021-01-26 15:59:09 -0800566}
567
Willy Tu11d68892022-01-20 10:37:34 -0800568ipmi_ret_t getFruSdrCount(ipmi::Context::ptr, size_t& count)
Willy Tude54f482021-01-26 15:59:09 -0800569{
570 count = deviceHashes.size();
571 return IPMI_CC_OK;
572}
573
574ipmi_ret_t getFruSdrs(ipmi::Context::ptr ctx, size_t index,
575 get_sdr::SensorDataFruRecord& resp)
576{
577 if (deviceHashes.size() < index)
578 {
579 return IPMI_CC_INVALID_FIELD_REQUEST;
580 }
581 auto device = deviceHashes.begin() + index;
krishnar4d90b3f02022-11-11 16:18:32 +0530582 uint16_t& bus = device->second.first;
Willy Tude54f482021-01-26 15:59:09 -0800583 uint8_t& address = device->second.second;
584
585 boost::container::flat_map<std::string, Value>* fruData = nullptr;
586 auto fru =
587 std::find_if(frus.begin(), frus.end(),
588 [bus, address, &fruData](ManagedEntry& entry) {
589 auto findFruDevice =
590 entry.second.find("xyz.openbmc_project.FruDevice");
591 if (findFruDevice == entry.second.end())
592 {
593 return false;
594 }
595 fruData = &(findFruDevice->second);
596 auto findBus = findFruDevice->second.find("BUS");
597 auto findAddress =
598 findFruDevice->second.find("ADDRESS");
599 if (findBus == findFruDevice->second.end() ||
600 findAddress == findFruDevice->second.end())
601 {
602 return false;
603 }
604 if (std::get<uint32_t>(findBus->second) != bus)
605 {
606 return false;
607 }
608 if (std::get<uint32_t>(findAddress->second) != address)
609 {
610 return false;
611 }
612 return true;
613 });
614 if (fru == frus.end())
615 {
616 return IPMI_CC_RESPONSE_ERROR;
617 }
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530618 std::string name;
Willy Tude54f482021-01-26 15:59:09 -0800619
620#ifdef USING_ENTITY_MANAGER_DECORATORS
621
622 boost::container::flat_map<std::string, Value>* entityData = nullptr;
623
624 // todo: this should really use caching, this is a very inefficient lookup
625 boost::system::error_code ec;
Nan Zhou947da1b2022-09-20 20:40:59 +0000626
Willy Tude54f482021-01-26 15:59:09 -0800627 ManagedObjectType entities = ctx->bus->yield_method_call<ManagedObjectType>(
Nan Zhou947da1b2022-09-20 20:40:59 +0000628 ctx->yield, ec, entityManagerServiceName,
629 "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager",
630 "GetManagedObjects");
Willy Tude54f482021-01-26 15:59:09 -0800631
632 if (ec)
633 {
634 phosphor::logging::log<phosphor::logging::level::ERR>(
635 "GetMangagedObjects for ipmiStorageGetFruInvAreaInfo failed",
636 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
637
638 return ipmi::ccResponseError;
639 }
640
641 auto entity = std::find_if(
642 entities.begin(), entities.end(),
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530643 [bus, address, &entityData, &name](ManagedEntry& entry) {
Willy Tude54f482021-01-26 15:59:09 -0800644 auto findFruDevice = entry.second.find(
Willy Tud2ee9862021-10-18 20:23:50 -0700645 "xyz.openbmc_project.Inventory.Decorator.I2CDevice");
Willy Tude54f482021-01-26 15:59:09 -0800646 if (findFruDevice == entry.second.end())
647 {
648 return false;
649 }
650
651 // Integer fields added via Entity-Manager json are uint64_ts by
652 // default.
653 auto findBus = findFruDevice->second.find("Bus");
654 auto findAddress = findFruDevice->second.find("Address");
655
656 if (findBus == findFruDevice->second.end() ||
657 findAddress == findFruDevice->second.end())
658 {
659 return false;
660 }
661 if ((std::get<uint64_t>(findBus->second) != bus) ||
662 (std::get<uint64_t>(findAddress->second) != address))
663 {
664 return false;
665 }
666
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530667 auto fruName = findFruDevice->second.find("Name");
668 if (fruName != findFruDevice->second.end())
669 {
670 name = std::get<std::string>(fruName->second);
671 }
672
Willy Tude54f482021-01-26 15:59:09 -0800673 // At this point we found the device entry and should return
674 // true.
675 auto findIpmiDevice = entry.second.find(
676 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
677 if (findIpmiDevice != entry.second.end())
678 {
679 entityData = &(findIpmiDevice->second);
680 }
681
682 return true;
683 });
684
685 if (entity == entities.end())
686 {
687 if constexpr (DEBUG)
688 {
689 std::fprintf(stderr, "Ipmi or FruDevice Decorator interface "
690 "not found for Fru\n");
691 }
692 }
693
694#endif
695
Shakeeb Pashaeacad3c2021-06-28 20:25:17 +0530696 if (name.empty())
Willy Tude54f482021-01-26 15:59:09 -0800697 {
698 name = "UNKNOWN";
699 }
700 if (name.size() > maxFruSdrNameSize)
701 {
702 name = name.substr(0, maxFruSdrNameSize);
703 }
704 size_t sizeDiff = maxFruSdrNameSize - name.size();
705
706 resp.header.record_id_lsb = 0x0; // calling code is to implement these
707 resp.header.record_id_msb = 0x0;
708 resp.header.sdr_version = ipmiSdrVersion;
709 resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
710 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
711 resp.key.deviceAddress = 0x20;
712 resp.key.fruID = device->first;
713 resp.key.accessLun = 0x80; // logical / physical fru device
714 resp.key.channelNumber = 0x0;
715 resp.body.reserved = 0x0;
716 resp.body.deviceType = 0x10;
717 resp.body.deviceTypeModifier = 0x0;
718
719 uint8_t entityID = 0;
720 uint8_t entityInstance = 0x1;
721
722#ifdef USING_ENTITY_MANAGER_DECORATORS
723 if (entityData)
724 {
725 auto entityIdProperty = entityData->find("EntityId");
726 auto entityInstanceProperty = entityData->find("EntityInstance");
727
728 if (entityIdProperty != entityData->end())
729 {
730 entityID = static_cast<uint8_t>(
731 std::get<uint64_t>(entityIdProperty->second));
732 }
733 if (entityInstanceProperty != entityData->end())
734 {
735 entityInstance = static_cast<uint8_t>(
736 std::get<uint64_t>(entityInstanceProperty->second));
737 }
738 }
739#endif
740
741 resp.body.entityID = entityID;
742 resp.body.entityInstance = entityInstance;
743
744 resp.body.oem = 0x0;
745 resp.body.deviceIDLen = name.size();
746 name.copy(resp.body.deviceID, name.size());
747
748 return IPMI_CC_OK;
749}
750
751static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
752{
753 // Loop through the directory looking for ipmi_sel log files
754 for (const std::filesystem::directory_entry& dirEnt :
755 std::filesystem::directory_iterator(
756 dynamic_sensors::ipmi::sel::selLogDir))
757 {
758 std::string filename = dirEnt.path().filename();
759 if (boost::starts_with(filename,
760 dynamic_sensors::ipmi::sel::selLogFilename))
761 {
762 // If we find an ipmi_sel log file, save the path
763 selLogFiles.emplace_back(dynamic_sensors::ipmi::sel::selLogDir /
764 filename);
765 }
766 }
767 // As the log files rotate, they are appended with a ".#" that is higher for
768 // the older logs. Since we don't expect more than 10 log files, we
769 // can just sort the list to get them in order from newest to oldest
770 std::sort(selLogFiles.begin(), selLogFiles.end());
771
772 return !selLogFiles.empty();
773}
774
775static int countSELEntries()
776{
777 // Get the list of ipmi_sel log files
778 std::vector<std::filesystem::path> selLogFiles;
779 if (!getSELLogFiles(selLogFiles))
780 {
781 return 0;
782 }
783 int numSELEntries = 0;
784 // Loop through each log file and count the number of logs
785 for (const std::filesystem::path& file : selLogFiles)
786 {
787 std::ifstream logStream(file);
788 if (!logStream.is_open())
789 {
790 continue;
791 }
792
793 std::string line;
794 while (std::getline(logStream, line))
795 {
796 numSELEntries++;
797 }
798 }
799 return numSELEntries;
800}
801
802static bool findSELEntry(const int recordID,
803 const std::vector<std::filesystem::path>& selLogFiles,
804 std::string& entry)
805{
806 // Record ID is the first entry field following the timestamp. It is
807 // preceded by a space and followed by a comma
808 std::string search = " " + std::to_string(recordID) + ",";
809
810 // Loop through the ipmi_sel log entries
811 for (const std::filesystem::path& file : selLogFiles)
812 {
813 std::ifstream logStream(file);
814 if (!logStream.is_open())
815 {
816 continue;
817 }
818
819 while (std::getline(logStream, entry))
820 {
821 // Check if the record ID matches
822 if (entry.find(search) != std::string::npos)
823 {
824 return true;
825 }
826 }
827 }
828 return false;
829}
830
831static uint16_t
832 getNextRecordID(const uint16_t recordID,
833 const std::vector<std::filesystem::path>& selLogFiles)
834{
835 uint16_t nextRecordID = recordID + 1;
836 std::string entry;
837 if (findSELEntry(nextRecordID, selLogFiles, entry))
838 {
839 return nextRecordID;
840 }
841 else
842 {
843 return ipmi::sel::lastEntry;
844 }
845}
846
847static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
848{
849 for (unsigned int i = 0; i < hexStr.size(); i += 2)
850 {
851 try
852 {
853 data.push_back(static_cast<uint8_t>(
854 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
855 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500856 catch (const std::invalid_argument& e)
Willy Tude54f482021-01-26 15:59:09 -0800857 {
858 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
859 return -1;
860 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500861 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800862 {
863 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
864 return -1;
865 }
866 }
867 return 0;
868}
869
870ipmi::RspType<uint8_t, // SEL version
871 uint16_t, // SEL entry count
872 uint16_t, // free space
873 uint32_t, // last add timestamp
874 uint32_t, // last erase timestamp
875 uint8_t> // operation support
876 ipmiStorageGetSELInfo()
877{
878 constexpr uint8_t selVersion = ipmi::sel::selVersion;
879 uint16_t entries = countSELEntries();
880 uint32_t addTimeStamp = dynamic_sensors::ipmi::sel::getFileTimestamp(
881 dynamic_sensors::ipmi::sel::selLogDir /
882 dynamic_sensors::ipmi::sel::selLogFilename);
883 uint32_t eraseTimeStamp = dynamic_sensors::ipmi::sel::erase_time::get();
884 constexpr uint8_t operationSupport =
885 dynamic_sensors::ipmi::sel::selOperationSupport;
886 constexpr uint16_t freeSpace =
887 0xffff; // Spec indicates that more than 64kB is free
888
889 return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
890 eraseTimeStamp, operationSupport);
891}
892
893using systemEventType = std::tuple<
894 uint32_t, // Timestamp
895 uint16_t, // Generator ID
896 uint8_t, // EvM Rev
897 uint8_t, // Sensor Type
898 uint8_t, // Sensor Number
899 uint7_t, // Event Type
900 bool, // Event Direction
901 std::array<uint8_t, dynamic_sensors::ipmi::sel::systemEventSize>>; // Event
902 // Data
903using oemTsEventType = std::tuple<
904 uint32_t, // Timestamp
905 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemTsEventSize>>; // Event
906 // Data
907using oemEventType =
908 std::array<uint8_t, dynamic_sensors::ipmi::sel::oemEventSize>; // Event Data
909
910ipmi::RspType<uint16_t, // Next Record ID
911 uint16_t, // Record ID
912 uint8_t, // Record Type
913 std::variant<systemEventType, oemTsEventType,
914 oemEventType>> // Record Content
915 ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
916 uint8_t offset, uint8_t size)
917{
918 // Only support getting the entire SEL record. If a partial size or non-zero
919 // offset is requested, return an error
920 if (offset != 0 || size != ipmi::sel::entireRecord)
921 {
922 return ipmi::responseRetBytesUnavailable();
923 }
924
925 // Check the reservation ID if one is provided or required (only if the
926 // offset is non-zero)
927 if (reservationID != 0 || offset != 0)
928 {
929 if (!checkSELReservation(reservationID))
930 {
931 return ipmi::responseInvalidReservationId();
932 }
933 }
934
935 // Get the ipmi_sel log files
936 std::vector<std::filesystem::path> selLogFiles;
937 if (!getSELLogFiles(selLogFiles))
938 {
939 return ipmi::responseSensorInvalid();
940 }
941
942 std::string targetEntry;
943
944 if (targetID == ipmi::sel::firstEntry)
945 {
946 // The first entry will be at the top of the oldest log file
947 std::ifstream logStream(selLogFiles.back());
948 if (!logStream.is_open())
949 {
950 return ipmi::responseUnspecifiedError();
951 }
952
953 if (!std::getline(logStream, targetEntry))
954 {
955 return ipmi::responseUnspecifiedError();
956 }
957 }
958 else if (targetID == ipmi::sel::lastEntry)
959 {
960 // The last entry will be at the bottom of the newest log file
961 std::ifstream logStream(selLogFiles.front());
962 if (!logStream.is_open())
963 {
964 return ipmi::responseUnspecifiedError();
965 }
966
967 std::string line;
968 while (std::getline(logStream, line))
969 {
970 targetEntry = line;
971 }
972 }
973 else
974 {
975 if (!findSELEntry(targetID, selLogFiles, targetEntry))
976 {
977 return ipmi::responseSensorInvalid();
978 }
979 }
980
981 // The format of the ipmi_sel message is "<Timestamp>
982 // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
983 // First get the Timestamp
984 size_t space = targetEntry.find_first_of(" ");
985 if (space == std::string::npos)
986 {
987 return ipmi::responseUnspecifiedError();
988 }
989 std::string entryTimestamp = targetEntry.substr(0, space);
990 // Then get the log contents
991 size_t entryStart = targetEntry.find_first_not_of(" ", space);
992 if (entryStart == std::string::npos)
993 {
994 return ipmi::responseUnspecifiedError();
995 }
996 std::string_view entry(targetEntry);
997 entry.remove_prefix(entryStart);
998 // Use split to separate the entry into its fields
999 std::vector<std::string> targetEntryFields;
1000 boost::split(targetEntryFields, entry, boost::is_any_of(","),
1001 boost::token_compress_on);
1002 if (targetEntryFields.size() < 3)
1003 {
1004 return ipmi::responseUnspecifiedError();
1005 }
1006 std::string& recordIDStr = targetEntryFields[0];
1007 std::string& recordTypeStr = targetEntryFields[1];
1008 std::string& eventDataStr = targetEntryFields[2];
1009
1010 uint16_t recordID;
1011 uint8_t recordType;
1012 try
1013 {
1014 recordID = std::stoul(recordIDStr);
1015 recordType = std::stoul(recordTypeStr, nullptr, 16);
1016 }
1017 catch (const std::invalid_argument&)
1018 {
1019 return ipmi::responseUnspecifiedError();
1020 }
1021 uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
1022 std::vector<uint8_t> eventDataBytes;
1023 if (fromHexStr(eventDataStr, eventDataBytes) < 0)
1024 {
1025 return ipmi::responseUnspecifiedError();
1026 }
1027
1028 if (recordType == dynamic_sensors::ipmi::sel::systemEvent)
1029 {
1030 // Get the timestamp
1031 std::tm timeStruct = {};
1032 std::istringstream entryStream(entryTimestamp);
1033
1034 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1035 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1036 {
1037 timestamp = std::mktime(&timeStruct);
1038 }
1039
1040 // Set the event message revision
1041 uint8_t evmRev = dynamic_sensors::ipmi::sel::eventMsgRev;
1042
1043 uint16_t generatorID = 0;
1044 uint8_t sensorType = 0;
1045 uint16_t sensorAndLun = 0;
1046 uint8_t sensorNum = 0xFF;
1047 uint7_t eventType = 0;
1048 bool eventDir = 0;
1049 // System type events should have six fields
1050 if (targetEntryFields.size() >= 6)
1051 {
1052 std::string& generatorIDStr = targetEntryFields[3];
1053 std::string& sensorPath = targetEntryFields[4];
1054 std::string& eventDirStr = targetEntryFields[5];
1055
1056 // Get the generator ID
1057 try
1058 {
1059 generatorID = std::stoul(generatorIDStr, nullptr, 16);
1060 }
1061 catch (const std::invalid_argument&)
1062 {
1063 std::cerr << "Invalid Generator ID\n";
1064 }
1065
1066 // Get the sensor type, sensor number, and event type for the sensor
1067 sensorType = getSensorTypeFromPath(sensorPath);
1068 sensorAndLun = getSensorNumberFromPath(sensorPath);
1069 sensorNum = static_cast<uint8_t>(sensorAndLun);
Harvey.Wu4376cdf2021-11-16 19:40:55 +08001070 if ((generatorID & 0x0001) == 0)
1071 {
1072 // IPMB Address
1073 generatorID |= sensorAndLun & 0x0300;
1074 }
1075 else
1076 {
1077 // system software
1078 generatorID |= sensorAndLun >> 8;
1079 }
Willy Tude54f482021-01-26 15:59:09 -08001080 eventType = getSensorEventTypeFromPath(sensorPath);
1081
1082 // Get the event direction
1083 try
1084 {
1085 eventDir = std::stoul(eventDirStr) ? 0 : 1;
1086 }
1087 catch (const std::invalid_argument&)
1088 {
1089 std::cerr << "Invalid Event Direction\n";
1090 }
1091 }
1092
1093 // Only keep the eventData bytes that fit in the record
1094 std::array<uint8_t, dynamic_sensors::ipmi::sel::systemEventSize>
1095 eventData{};
1096 std::copy_n(eventDataBytes.begin(),
1097 std::min(eventDataBytes.size(), eventData.size()),
1098 eventData.begin());
1099
1100 return ipmi::responseSuccess(
1101 nextRecordID, recordID, recordType,
1102 systemEventType{timestamp, generatorID, evmRev, sensorType,
1103 sensorNum, eventType, eventDir, eventData});
1104 }
1105
1106 return ipmi::responseUnspecifiedError();
1107}
1108
Willy Tu11d68892022-01-20 10:37:34 -08001109/*
1110Unused arguments
1111 uint16_t recordID, uint8_t recordType, uint32_t timestamp,
1112 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
1113 uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
1114 uint8_t eventData3
1115*/
1116ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(uint16_t, uint8_t, uint32_t,
1117 uint16_t, uint8_t, uint8_t,
1118 uint8_t, uint8_t, uint8_t,
1119 uint8_t, uint8_t)
Willy Tude54f482021-01-26 15:59:09 -08001120{
1121 // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1122 // added
1123 cancelSELReservation();
1124
1125 uint16_t responseID = 0xFFFF;
1126 return ipmi::responseSuccess(responseID);
1127}
1128
Tim Lee11317d72022-07-27 10:13:46 +08001129ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr ctx,
Willy Tude54f482021-01-26 15:59:09 -08001130 uint16_t reservationID,
1131 const std::array<uint8_t, 3>& clr,
1132 uint8_t eraseOperation)
1133{
1134 if (!checkSELReservation(reservationID))
1135 {
1136 return ipmi::responseInvalidReservationId();
1137 }
1138
1139 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1140 if (clr != clrExpected)
1141 {
1142 return ipmi::responseInvalidFieldRequest();
1143 }
1144
1145 // Erasure status cannot be fetched, so always return erasure status as
1146 // `erase completed`.
1147 if (eraseOperation == ipmi::sel::getEraseStatus)
1148 {
1149 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1150 }
1151
1152 // Check that initiate erase is correct
1153 if (eraseOperation != ipmi::sel::initiateErase)
1154 {
1155 return ipmi::responseInvalidFieldRequest();
1156 }
1157
1158 // Per the IPMI spec, need to cancel any reservation when the SEL is
1159 // cleared
1160 cancelSELReservation();
1161
Charles Boyer818bea12021-09-20 16:56:36 -05001162#ifndef FEATURE_SEL_LOGGER_CLEARS_SEL
Willy Tude54f482021-01-26 15:59:09 -08001163 // Save the erase time
1164 dynamic_sensors::ipmi::sel::erase_time::save();
1165
1166 // Clear the SEL by deleting the log files
1167 std::vector<std::filesystem::path> selLogFiles;
1168 if (getSELLogFiles(selLogFiles))
1169 {
1170 for (const std::filesystem::path& file : selLogFiles)
1171 {
1172 std::error_code ec;
1173 std::filesystem::remove(file, ec);
1174 }
1175 }
1176
1177 // Reload rsyslog so it knows to start new log files
Tim Lee11317d72022-07-27 10:13:46 +08001178 boost::system::error_code ec;
Tim Lee2b3507a2022-08-19 17:25:48 +08001179 ctx->bus->yield_method_call<>(ctx->yield, ec, "org.freedesktop.systemd1",
1180 "/org/freedesktop/systemd1",
1181 "org.freedesktop.systemd1.Manager",
1182 "ReloadUnit", "rsyslog.service", "replace");
Tim Lee11317d72022-07-27 10:13:46 +08001183 if (ec)
Willy Tude54f482021-01-26 15:59:09 -08001184 {
Tim Lee11317d72022-07-27 10:13:46 +08001185 std::cerr << "error in reload rsyslog: " << ec << std::endl;
1186 return ipmi::responseUnspecifiedError();
Willy Tude54f482021-01-26 15:59:09 -08001187 }
Charles Boyer818bea12021-09-20 16:56:36 -05001188#else
1189 boost::system::error_code ec;
1190 ctx->bus->yield_method_call<>(ctx->yield, ec, selLoggerServiceName,
1191 "/xyz/openbmc_project/Logging/IPMI",
1192 "xyz.openbmc_project.Logging.IPMI", "Clear");
1193 if (ec)
1194 {
1195 std::cerr << "error in clear SEL: " << ec << std::endl;
1196 return ipmi::responseUnspecifiedError();
1197 }
Willy Tude54f482021-01-26 15:59:09 -08001198
Charles Boyer818bea12021-09-20 16:56:36 -05001199 // Save the erase time
1200 dynamic_sensors::ipmi::sel::erase_time::save();
1201#endif
Willy Tude54f482021-01-26 15:59:09 -08001202 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1203}
1204
1205ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1206{
1207 struct timespec selTime = {};
1208
1209 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1210 {
1211 return ipmi::responseUnspecifiedError();
1212 }
1213
1214 return ipmi::responseSuccess(selTime.tv_sec);
1215}
1216
Willy Tu11d68892022-01-20 10:37:34 -08001217ipmi::RspType<> ipmiStorageSetSELTime(uint32_t)
Willy Tude54f482021-01-26 15:59:09 -08001218{
1219 // Set SEL Time is not supported
1220 return ipmi::responseInvalidCommand();
1221}
1222
Harvey Wu05d17c02021-09-15 08:46:59 +08001223std::vector<uint8_t>
1224 getType8SDRs(ipmi::sensor::EntityInfoMap::const_iterator& entity,
1225 uint16_t recordId)
1226{
1227 std::vector<uint8_t> resp;
1228 get_sdr::SensorDataEntityRecord data{};
1229
1230 /* Header */
1231 get_sdr::header::set_record_id(recordId, &(data.header));
1232 // Based on IPMI Spec v2.0 rev 1.1
1233 data.header.sdr_version = SDR_VERSION;
1234 data.header.record_type = 0x08;
1235 data.header.record_length = sizeof(data.key) + sizeof(data.body);
1236
1237 /* Key */
1238 data.key.containerEntityId = entity->second.containerEntityId;
1239 data.key.containerEntityInstance = entity->second.containerEntityInstance;
1240 get_sdr::key::set_flags(entity->second.isList, entity->second.isLinked,
1241 &(data.key));
1242 data.key.entityId1 = entity->second.containedEntities[0].first;
1243 data.key.entityInstance1 = entity->second.containedEntities[0].second;
1244
1245 /* Body */
1246 data.body.entityId2 = entity->second.containedEntities[1].first;
1247 data.body.entityInstance2 = entity->second.containedEntities[1].second;
1248 data.body.entityId3 = entity->second.containedEntities[2].first;
1249 data.body.entityInstance3 = entity->second.containedEntities[2].second;
1250 data.body.entityId4 = entity->second.containedEntities[3].first;
1251 data.body.entityInstance4 = entity->second.containedEntities[3].second;
1252
1253 resp.insert(resp.end(), (uint8_t*)&data, ((uint8_t*)&data) + sizeof(data));
1254
1255 return resp;
1256}
1257
Willy Tude54f482021-01-26 15:59:09 -08001258std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
1259{
1260 std::vector<uint8_t> resp;
1261 if (index == 0)
1262 {
Willy Tude54f482021-01-26 15:59:09 -08001263 std::string bmcName = "Basbrd Mgmt Ctlr";
Johnathan Manteycd1c4962021-09-22 12:58:08 -07001264 Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName);
Willy Tude54f482021-01-26 15:59:09 -08001265 uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
1266 resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
1267 }
1268 else if (index == 1)
1269 {
Willy Tude54f482021-01-26 15:59:09 -08001270 std::string meName = "Mgmt Engine";
Johnathan Manteycd1c4962021-09-22 12:58:08 -07001271 Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName);
Willy Tude54f482021-01-26 15:59:09 -08001272 uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
1273 resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
1274 }
1275 else
1276 {
1277 throw std::runtime_error("getType12SDRs:: Illegal index " +
1278 std::to_string(index));
1279 }
1280
1281 return resp;
1282}
1283
1284void registerStorageFunctions()
1285{
1286 createTimers();
1287 startMatch();
1288
1289 // <Get FRU Inventory Area Info>
Willy Tud351a722021-08-12 14:33:40 -07001290 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Willy Tude54f482021-01-26 15:59:09 -08001291 ipmi::storage::cmdGetFruInventoryAreaInfo,
1292 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1293 // <READ FRU Data>
1294 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1295 ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
1296 ipmiStorageReadFruData);
1297
1298 // <WRITE FRU Data>
1299 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1300 ipmi::storage::cmdWriteFruData,
1301 ipmi::Privilege::Operator, ipmiStorageWriteFruData);
1302
1303 // <Get SEL Info>
1304 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1305 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1306 ipmiStorageGetSELInfo);
1307
1308 // <Get SEL Entry>
1309 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1310 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1311 ipmiStorageGetSELEntry);
1312
1313 // <Add SEL Entry>
1314 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1315 ipmi::storage::cmdAddSelEntry,
1316 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1317
1318 // <Clear SEL>
1319 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1320 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1321 ipmiStorageClearSEL);
1322
1323 // <Get SEL Time>
1324 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1325 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1326 ipmiStorageGetSELTime);
1327
1328 // <Set SEL Time>
1329 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1330 ipmi::storage::cmdSetSelTime,
1331 ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1332}
1333} // namespace storage
1334} // namespace ipmi