blob: 8c9b49abb3d701509b8d7058c7aded477105b008 [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07002// Copyright (c) 2017-2019 Intel Corporation
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07003//
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
Patrick Ventureca99ef52019-10-20 14:00:50 -070017#include "storagecommands.hpp"
18
19#include "commandutils.hpp"
20#include "ipmi_to_redfish_hooks.hpp"
21#include "sdrutils.hpp"
Patrick Venturec2a07d42020-05-30 16:35:03 -070022#include "types.hpp"
Patrick Ventureca99ef52019-10-20 14:00:50 -070023
Jason M. Bills1d4d54d2019-04-23 11:26:11 -070024#include <boost/algorithm/string.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070025#include <boost/container/flat_map.hpp>
James Feist2a265d52019-04-08 11:16:27 -070026#include <ipmid/api.hpp>
James Feist25690252019-12-23 12:25:49 -080027#include <ipmid/message.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070028#include <phosphor-logging/log.hpp>
29#include <sdbusplus/message/types.hpp>
30#include <sdbusplus/timer.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070031
32#include <filesystem>
Archana Kakanif23fd542021-09-16 05:05:10 +000033#include <fstream>
James Feistfcd2d3a2020-05-28 10:38:15 -070034#include <iostream>
Jason M. Billsc04e2e72018-11-28 15:15:56 -080035#include <stdexcept>
Archana Kakanif23fd542021-09-16 05:05:10 +000036#include <unordered_set>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070037
Patrick Venture9ce789f2019-10-17 09:09:39 -070038static constexpr bool DEBUG = false;
39
Jason M. Bills1d4d54d2019-04-23 11:26:11 -070040namespace intel_oem::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
Jason M. Bills7944c302019-03-20 15:24:05 -070057{
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{
Jason M. Bills1d4d54d2019-04-23 11:26:11 -070081 return getFileTimestamp(selEraseTimestamp);
Jason M. Bills7944c302019-03-20 15:24:05 -070082}
Jason M. Bills1d4d54d2019-04-23 11:26:11 -070083} // namespace erase_time
84} // namespace intel_oem::ipmi::sel
Jason M. Bills7944c302019-03-20 15:24:05 -070085
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070086namespace ipmi
87{
88
89namespace storage
90{
91
Jason M. Billse2d1aee2018-10-03 15:57:18 -070092constexpr static const size_t maxMessageSize = 64;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070093constexpr static const size_t maxFruSdrNameSize = 16;
James Feiste4f710d2020-05-20 15:50:30 -070094using ObjectType = boost::container::flat_map<
95 std::string, boost::container::flat_map<std::string, DbusVariant>>;
96using ManagedObjectType =
97 boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
98using ManagedEntry = std::pair<sdbusplus::message::object_path, ObjectType>;
Archana Kakanif23fd542021-09-16 05:05:10 +000099using GetObjectType =
100 std::vector<std::pair<std::string, std::vector<std::string>>>;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700101
James Feist3bcba452018-12-20 12:31:03 -0800102constexpr static const char* fruDeviceServiceName =
103 "xyz.openbmc_project.FruDevice";
James Feist25690252019-12-23 12:25:49 -0800104constexpr static const size_t writeTimeoutSeconds = 10;
Anoop S358e7df2020-05-05 16:43:34 +0000105constexpr static const char* chassisTypeRackMount = "23";
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700106
Jason M. Bills4ed6f2c2019-04-02 12:21:25 -0700107// event direction is bit[7] of eventType where 1b = Deassertion event
108constexpr static const uint8_t deassertionEvent = 0x80;
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800109
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700110static std::vector<uint8_t> fruCache;
Johnathan Mantey07574002022-12-13 14:52:54 -0800111static uint16_t cacheBus = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700112static uint8_t cacheAddr = 0XFF;
James Feiste4f710d2020-05-20 15:50:30 -0700113static uint8_t lastDevId = 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700114
Johnathan Mantey07574002022-12-13 14:52:54 -0800115static uint16_t writeBus = 0xFFFF;
James Feist25690252019-12-23 12:25:49 -0800116static uint8_t writeAddr = 0XFF;
117
Patrick Williamsf0feb492023-12-05 12:45:02 -0600118std::unique_ptr<sdbusplus::Timer> writeTimer = nullptr;
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500119static std::vector<sdbusplus::bus::match_t> fruMatches;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700120
James Feist25690252019-12-23 12:25:49 -0800121ManagedObjectType frus;
122
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700123// we unfortunately have to build a map of hashes in case there is a
124// collision to verify our dev-id
Johnathan Mantey07574002022-12-13 14:52:54 -0800125boost::container::flat_map<uint8_t, std::pair<uint16_t, uint8_t>> deviceHashes;
Archana Kakanif23fd542021-09-16 05:05:10 +0000126// Map devId to Object Path
127boost::container::flat_map<uint8_t, std::string> devicePath;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700128
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700129void registerStorageFunctions() __attribute__((constructor));
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700130
131bool writeFru()
132{
Johnathan Mantey07574002022-12-13 14:52:54 -0800133 if (writeBus == 0xFFFF && writeAddr == 0xFF)
James Feist25690252019-12-23 12:25:49 -0800134 {
135 return true;
136 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700137 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500138 sdbusplus::message_t writeFru = dbus->new_method_call(
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700139 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
140 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
James Feist25690252019-12-23 12:25:49 -0800141 writeFru.append(writeBus, writeAddr, fruCache);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700142 try
143 {
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500144 sdbusplus::message_t writeFruResp = dbus->call(writeFru);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700145 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500146 catch (const sdbusplus::exception_t&)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700147 {
148 // todo: log sel?
149 phosphor::logging::log<phosphor::logging::level::ERR>(
150 "error writing fru");
151 return false;
152 }
Johnathan Mantey07574002022-12-13 14:52:54 -0800153 writeBus = 0xFFFF;
James Feist25690252019-12-23 12:25:49 -0800154 writeAddr = 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700155 return true;
156}
157
James Feist25690252019-12-23 12:25:49 -0800158void createTimers()
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700159{
Patrick Williamsf0feb492023-12-05 12:45:02 -0600160 writeTimer = std::make_unique<sdbusplus::Timer>(writeFru);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700161}
162
James Feiste4f710d2020-05-20 15:50:30 -0700163void recalculateHashes()
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700164{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700165 deviceHashes.clear();
Archana Kakanif23fd542021-09-16 05:05:10 +0000166 devicePath.clear();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700167 // hash the object paths to create unique device id's. increment on
168 // collision
169 std::hash<std::string> hasher;
170 for (const auto& fru : frus)
171 {
172 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
173 if (fruIface == fru.second.end())
174 {
175 continue;
176 }
177
178 auto busFind = fruIface->second.find("BUS");
179 auto addrFind = fruIface->second.find("ADDRESS");
180 if (busFind == fruIface->second.end() ||
181 addrFind == fruIface->second.end())
182 {
183 phosphor::logging::log<phosphor::logging::level::INFO>(
184 "fru device missing Bus or Address",
185 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
186 continue;
187 }
188
Johnathan Mantey07574002022-12-13 14:52:54 -0800189 uint16_t fruBus = std::get<uint32_t>(busFind->second);
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700190 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
Anoop S358e7df2020-05-05 16:43:34 +0000191 auto chassisFind = fruIface->second.find("CHASSIS_TYPE");
192 std::string chassisType;
193 if (chassisFind != fruIface->second.end())
194 {
195 chassisType = std::get<std::string>(chassisFind->second);
196 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700197
198 uint8_t fruHash = 0;
Anoop S358e7df2020-05-05 16:43:34 +0000199 if (chassisType.compare(chassisTypeRackMount) != 0)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700200 {
201 fruHash = hasher(fru.first.str);
202 // can't be 0xFF based on spec, and 0 is reserved for baseboard
203 if (fruHash == 0 || fruHash == 0xFF)
204 {
205 fruHash = 1;
206 }
207 }
Johnathan Mantey07574002022-12-13 14:52:54 -0800208 std::pair<uint16_t, uint8_t> newDev(fruBus, fruAddr);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700209
210 bool emplacePassed = false;
211 while (!emplacePassed)
212 {
213 auto resp = deviceHashes.emplace(fruHash, newDev);
Archana Kakanif23fd542021-09-16 05:05:10 +0000214
215 devicePath.emplace(fruHash, fru.first);
216
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700217 emplacePassed = resp.second;
218 if (!emplacePassed)
219 {
220 fruHash++;
221 // can't be 0xFF based on spec, and 0 is reserved for
222 // baseboard
223 if (fruHash == 0XFF)
224 {
225 fruHash = 0x1;
226 }
227 }
228 }
229 }
James Feiste4f710d2020-05-20 15:50:30 -0700230}
231
232void replaceCacheFru(const std::shared_ptr<sdbusplus::asio::connection>& bus,
Archana Kakanif23fd542021-09-16 05:05:10 +0000233 boost::asio::yield_context& yield)
James Feiste4f710d2020-05-20 15:50:30 -0700234{
235 boost::system::error_code ec;
Archana Kakanif23fd542021-09-16 05:05:10 +0000236 // ObjectPaths and Services which implements "xyz.openbmc_project.FruDevice"
237 // interface
238 GetSubTreeType fruServices = bus->yield_method_call<GetSubTreeType>(
239 yield, ec, "xyz.openbmc_project.ObjectMapper",
240 "/xyz/openbmc_project/object_mapper",
241 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
242 std::array<const char*, 1>{"xyz.openbmc_project.FruDevice"});
James Feiste4f710d2020-05-20 15:50:30 -0700243
James Feiste4f710d2020-05-20 15:50:30 -0700244 if (ec)
245 {
246 phosphor::logging::log<phosphor::logging::level::ERR>(
Archana Kakanif23fd542021-09-16 05:05:10 +0000247 "GetSubTree failed for FruDevice Interface ",
James Feiste4f710d2020-05-20 15:50:30 -0700248 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
249
250 return;
251 }
Archana Kakanif23fd542021-09-16 05:05:10 +0000252 // Get List of services which have implemented FruDevice interface
253 std::unordered_set<std::string> services;
254 for (const auto& [path, serviceMap] : fruServices)
255 {
256 for (const auto& [service, interfaces] : serviceMap)
257 {
258 services.insert(service);
259 }
260 }
261
262 // GetAll the objects under services which implement FruDevice interface
263 for (const std::string& service : services)
264 {
265 ec = boost::system::errc::make_error_code(boost::system::errc::success);
266 ManagedObjectType obj = bus->yield_method_call<ManagedObjectType>(
267 yield, ec, service, "/", "org.freedesktop.DBus.ObjectManager",
268 "GetManagedObjects");
269 if (ec)
270 {
271 phosphor::logging::log<phosphor::logging::level::ERR>(
272 "GetMangagedObjects failed",
273 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
274 continue;
275 }
276 // Save the object path which has FruDevice interface
277 for (const auto& [path, serviceMap] : fruServices)
278 {
279 for (const auto& serv : serviceMap)
280 {
281 if (serv.first == service)
282 {
283 auto fru = obj.find(path);
284 if (fru == obj.end())
285 {
286 continue;
287 }
288 frus.emplace(fru->first, fru->second);
289 }
290 }
291 }
292 }
293
James Feiste4f710d2020-05-20 15:50:30 -0700294 recalculateHashes();
295}
296
Vernon Mauerydcff1502022-09-28 11:12:46 -0700297ipmi::Cc getFru(ipmi::Context::ptr& ctx, uint8_t devId)
James Feiste4f710d2020-05-20 15:50:30 -0700298{
299 if (lastDevId == devId && devId != 0xFF)
300 {
301 return ipmi::ccSuccess;
302 }
303
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700304 auto deviceFind = deviceHashes.find(devId);
Archana Kakanif23fd542021-09-16 05:05:10 +0000305 auto devPath = devicePath.find(devId);
306 if (deviceFind == deviceHashes.end() || devPath == devicePath.end())
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700307 {
308 return IPMI_CC_SENSOR_INVALID;
309 }
310
PavanKumarIntel3432a0a2023-07-10 14:37:29 +0000311 if (writeTimer->isRunning())
312 {
313 phosphor::logging::log<phosphor::logging::level::ERR>(
314 "Couldn't get raw fru as fru is updating");
315 return ipmi::ccBusy;
316 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700317 fruCache.clear();
James Feist25690252019-12-23 12:25:49 -0800318
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700319 cacheBus = deviceFind->second.first;
320 cacheAddr = deviceFind->second.second;
James Feist25690252019-12-23 12:25:49 -0800321
James Feiste4f710d2020-05-20 15:50:30 -0700322 boost::system::error_code ec;
Archana Kakanif23fd542021-09-16 05:05:10 +0000323 GetObjectType fruService = ctx->bus->yield_method_call<GetObjectType>(
324 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
325 "/xyz/openbmc_project/object_mapper",
326 "xyz.openbmc_project.ObjectMapper", "GetObject", devPath->second,
327 std::array<const char*, 1>{"xyz.openbmc_project.FruDevice"});
James Feiste4f710d2020-05-20 15:50:30 -0700328
James Feist25690252019-12-23 12:25:49 -0800329 if (ec)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700330 {
James Feist25690252019-12-23 12:25:49 -0800331 phosphor::logging::log<phosphor::logging::level::ERR>(
Archana Kakanif23fd542021-09-16 05:05:10 +0000332 "Couldn't get raw fru because of service",
333 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
334 return ipmi::ccResponseError;
335 }
336
337 bool foundFru = false;
338 for (auto& service : fruService)
339 {
340 fruCache = ctx->bus->yield_method_call<std::vector<uint8_t>>(
341 ctx->yield, ec, service.first, "/xyz/openbmc_project/FruDevice",
342 "xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus,
343 cacheAddr);
344
345 if (!ec)
346 {
347 foundFru = true;
348 break;
349 }
350 }
351
352 if (!foundFru)
353 {
354 phosphor::logging::log<phosphor::logging::level::ERR>(
James Feist25690252019-12-23 12:25:49 -0800355 "Couldn't get raw fru",
356 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
Johnathan Mantey07574002022-12-13 14:52:54 -0800357 cacheBus = 0xFFFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700358 cacheAddr = 0xFF;
James Feist25690252019-12-23 12:25:49 -0800359 return ipmi::ccResponseError;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700360 }
361
362 lastDevId = devId;
James Feist25690252019-12-23 12:25:49 -0800363 return ipmi::ccSuccess;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700364}
365
James Feiste4f710d2020-05-20 15:50:30 -0700366void writeFruIfRunning()
367{
368 if (!writeTimer->isRunning())
369 {
370 return;
371 }
372 writeTimer->stop();
373 writeFru();
374}
375
376void startMatch(void)
377{
378 if (fruMatches.size())
379 {
380 return;
381 }
382
383 fruMatches.reserve(2);
384
385 auto bus = getSdBus();
386 fruMatches.emplace_back(*bus,
387 "type='signal',arg0path='/xyz/openbmc_project/"
388 "FruDevice/',member='InterfacesAdded'",
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500389 [](sdbusplus::message_t& message) {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500390 sdbusplus::message::object_path path;
391 ObjectType object;
392 try
393 {
394 message.read(path, object);
395 }
396 catch (const sdbusplus::exception_t&)
397 {
398 return;
399 }
400 auto findType = object.find("xyz.openbmc_project.FruDevice");
401 if (findType == object.end())
402 {
403 return;
404 }
405 writeFruIfRunning();
406 frus[path] = object;
407 recalculateHashes();
408 lastDevId = 0xFF;
409 });
James Feiste4f710d2020-05-20 15:50:30 -0700410
411 fruMatches.emplace_back(*bus,
412 "type='signal',arg0path='/xyz/openbmc_project/"
413 "FruDevice/',member='InterfacesRemoved'",
Patrick Williamsf944d2e2022-07-22 19:26:52 -0500414 [](sdbusplus::message_t& message) {
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500415 sdbusplus::message::object_path path;
416 std::set<std::string> interfaces;
417 try
418 {
419 message.read(path, interfaces);
420 }
421 catch (const sdbusplus::exception_t&)
422 {
423 return;
424 }
425 auto findType = interfaces.find("xyz.openbmc_project.FruDevice");
426 if (findType == interfaces.end())
427 {
428 return;
429 }
430 writeFruIfRunning();
431 frus.erase(path);
432 recalculateHashes();
433 lastDevId = 0xFF;
434 });
James Feiste4f710d2020-05-20 15:50:30 -0700435
436 // call once to populate
437 boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) {
438 replaceCacheFru(getSdBus(), yield);
439 });
440}
441
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000442/** @brief implements the read FRU data command
443 * @param fruDeviceId - FRU Device ID
444 * @param fruInventoryOffset - FRU Inventory Offset to write
445 * @param countToRead - Count to read
446 *
447 * @returns ipmi completion code plus response data
448 * - countWritten - Count written
449 */
450ipmi::RspType<uint8_t, // Count
451 std::vector<uint8_t> // Requested data
452 >
Vernon Mauerydcff1502022-09-28 11:12:46 -0700453 ipmiStorageReadFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId,
James Feist25690252019-12-23 12:25:49 -0800454 uint16_t fruInventoryOffset, uint8_t countToRead)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700455{
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000456 if (fruDeviceId == 0xFF)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700457 {
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000458 return ipmi::responseInvalidFieldRequest();
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700459 }
460
James Feiste4f710d2020-05-20 15:50:30 -0700461 ipmi::Cc status = getFru(ctx, fruDeviceId);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700462
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000463 if (status != ipmi::ccSuccess)
464 {
465 return ipmi::response(status);
466 }
467
468 size_t fromFruByteLen = 0;
469 if (countToRead + fruInventoryOffset < fruCache.size())
470 {
471 fromFruByteLen = countToRead;
472 }
473 else if (fruCache.size() > fruInventoryOffset)
474 {
475 fromFruByteLen = fruCache.size() - fruInventoryOffset;
476 }
477 else
478 {
srikanta mondal92108382020-02-27 18:53:20 +0000479 return ipmi::responseReqDataLenExceeded();
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000480 }
481
482 std::vector<uint8_t> requestedData;
483
484 requestedData.insert(
485 requestedData.begin(), fruCache.begin() + fruInventoryOffset,
486 fruCache.begin() + fruInventoryOffset + fromFruByteLen);
487
Patrick Venture70b17f92019-10-28 20:01:53 -0700488 return ipmi::responseSuccess(static_cast<uint8_t>(requestedData.size()),
489 requestedData);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700490}
491
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000492/** @brief implements the write FRU data command
493 * @param fruDeviceId - FRU Device ID
494 * @param fruInventoryOffset - FRU Inventory Offset to write
495 * @param dataToWrite - Data to write
496 *
497 * @returns ipmi completion code plus response data
498 * - countWritten - Count written
499 */
500ipmi::RspType<uint8_t>
Vernon Mauerydcff1502022-09-28 11:12:46 -0700501 ipmiStorageWriteFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId,
James Feist25690252019-12-23 12:25:49 -0800502 uint16_t fruInventoryOffset,
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000503 std::vector<uint8_t>& dataToWrite)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700504{
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000505 if (fruDeviceId == 0xFF)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700506 {
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000507 return ipmi::responseInvalidFieldRequest();
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700508 }
509
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000510 size_t writeLen = dataToWrite.size();
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700511
James Feiste4f710d2020-05-20 15:50:30 -0700512 ipmi::Cc status = getFru(ctx, fruDeviceId);
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000513 if (status != ipmi::ccSuccess)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700514 {
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000515 return ipmi::response(status);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700516 }
Vernon Mauerydcff1502022-09-28 11:12:46 -0700517 size_t lastWriteAddr = fruInventoryOffset + writeLen;
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700518 if (fruCache.size() < lastWriteAddr)
519 {
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000520 fruCache.resize(fruInventoryOffset + writeLen);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700521 }
522
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000523 std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
524 fruCache.begin() + fruInventoryOffset);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700525
526 bool atEnd = false;
527
528 if (fruCache.size() >= sizeof(FRUHeader))
529 {
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700530 FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
531
Peter Lundgrenc59391f2019-11-19 14:26:15 -0800532 int areaLength = 0;
Vernon Mauerydcff1502022-09-28 11:12:46 -0700533 size_t lastRecordStart = std::max(
Peter Lundgrenc59391f2019-11-19 14:26:15 -0800534 {header->internalOffset, header->chassisOffset, header->boardOffset,
535 header->productOffset, header->multiRecordOffset});
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700536 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
537
Peter Lundgrenc59391f2019-11-19 14:26:15 -0800538 if (header->multiRecordOffset)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700539 {
Peter Lundgrenc59391f2019-11-19 14:26:15 -0800540 // This FRU has a MultiRecord Area
541 uint8_t endOfList = 0;
542 // Walk the MultiRecord headers until the last record
543 while (!endOfList)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700544 {
Peter Lundgrenc59391f2019-11-19 14:26:15 -0800545 // The MSB in the second byte of the MultiRecord header signals
546 // "End of list"
547 endOfList = fruCache[lastRecordStart + 1] & 0x80;
548 // Third byte in the MultiRecord header is the length
549 areaLength = fruCache[lastRecordStart + 2];
550 // This length is in bytes (not 8 bytes like other headers)
551 areaLength += 5; // The length omits the 5 byte header
552 if (!endOfList)
553 {
554 // Next MultiRecord header
555 lastRecordStart += areaLength;
556 }
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700557 }
558 }
Peter Lundgrenc59391f2019-11-19 14:26:15 -0800559 else
560 {
561 // This FRU does not have a MultiRecord Area
562 // Get the length of the area in multiples of 8 bytes
563 if (lastWriteAddr > (lastRecordStart + 1))
564 {
565 // second byte in record area is the length
566 areaLength = fruCache[lastRecordStart + 1];
567 areaLength *= 8; // it is in multiples of 8 bytes
568 }
569 }
570 if (lastWriteAddr >= (areaLength + lastRecordStart))
571 {
572 atEnd = true;
573 }
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700574 }
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000575 uint8_t countWritten = 0;
James Feist25690252019-12-23 12:25:49 -0800576
577 writeBus = cacheBus;
578 writeAddr = cacheAddr;
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700579 if (atEnd)
580 {
581 // cancel timer, we're at the end so might as well send it
James Feist25690252019-12-23 12:25:49 -0800582 writeTimer->stop();
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700583 if (!writeFru())
584 {
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000585 return ipmi::responseInvalidFieldRequest();
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700586 }
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000587 countWritten = std::min(fruCache.size(), static_cast<size_t>(0xFF));
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700588 }
589 else
590 {
James Feist25690252019-12-23 12:25:49 -0800591 // start a timer, if no further data is sent to check to see if it is
592 // valid
593 writeTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
594 std::chrono::seconds(writeTimeoutSeconds)));
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000595 countWritten = 0;
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700596 }
597
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +0000598 return ipmi::responseSuccess(countWritten);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700599}
600
jayaprakash Mutyalad33acd62019-05-17 19:37:25 +0000601/** @brief implements the get FRU inventory area info command
602 * @param fruDeviceId - FRU Device ID
603 *
604 * @returns IPMI completion code plus response data
605 * - inventorySize - Number of possible allocation units
606 * - accessType - Allocation unit size in bytes.
607 */
608ipmi::RspType<uint16_t, // inventorySize
609 uint8_t> // accessType
Vernon Mauerydcff1502022-09-28 11:12:46 -0700610 ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr& ctx, uint8_t fruDeviceId)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700611{
jayaprakash Mutyalad33acd62019-05-17 19:37:25 +0000612 if (fruDeviceId == 0xFF)
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700613 {
jayaprakash Mutyalad33acd62019-05-17 19:37:25 +0000614 return ipmi::responseInvalidFieldRequest();
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700615 }
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700616
Jayaprakash Mutyala1e2ab062020-08-03 16:57:00 +0000617 ipmi::Cc ret = getFru(ctx, fruDeviceId);
618 if (ret != ipmi::ccSuccess)
619 {
620 return ipmi::response(ret);
621 }
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700622
jayaprakash Mutyalad33acd62019-05-17 19:37:25 +0000623 constexpr uint8_t accessType =
624 static_cast<uint8_t>(GetFRUAreaAccessType::byte);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700625
jayaprakash Mutyalad33acd62019-05-17 19:37:25 +0000626 return ipmi::responseSuccess(fruCache.size(), accessType);
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700627}
628
Vernon Mauerydcff1502022-09-28 11:12:46 -0700629ipmi::Cc getFruSdrCount(ipmi::Context::ptr&, size_t& count)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700630{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700631 count = deviceHashes.size();
Vernon Mauerydcff1502022-09-28 11:12:46 -0700632 return ipmi::ccSuccess;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700633}
634
Vernon Mauerydcff1502022-09-28 11:12:46 -0700635ipmi::Cc getFruSdrs(ipmi::Context::ptr& ctx, size_t index,
636 get_sdr::SensorDataFruRecord& resp)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700637{
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700638 if (deviceHashes.size() < index)
639 {
Vernon Mauerydcff1502022-09-28 11:12:46 -0700640 return ipmi::ccInvalidFieldRequest;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700641 }
642 auto device = deviceHashes.begin() + index;
Johnathan Mantey07574002022-12-13 14:52:54 -0800643 uint16_t& bus = device->second.first;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700644 uint8_t& address = device->second.second;
645
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700646 boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500647 auto fru = std::find_if(frus.begin(), frus.end(),
648 [bus, address, &fruData](ManagedEntry& entry) {
649 auto findFruDevice = entry.second.find("xyz.openbmc_project.FruDevice");
650 if (findFruDevice == entry.second.end())
651 {
652 return false;
653 }
654 fruData = &(findFruDevice->second);
655 auto findBus = findFruDevice->second.find("BUS");
656 auto findAddress = findFruDevice->second.find("ADDRESS");
657 if (findBus == findFruDevice->second.end() ||
658 findAddress == findFruDevice->second.end())
659 {
660 return false;
661 }
662 if (std::get<uint32_t>(findBus->second) != bus)
663 {
664 return false;
665 }
666 if (std::get<uint32_t>(findAddress->second) != address)
667 {
668 return false;
669 }
670 return true;
671 });
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700672 if (fru == frus.end())
673 {
Vernon Mauerydcff1502022-09-28 11:12:46 -0700674 return ipmi::ccResponseError;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700675 }
Patrick Venture9ce789f2019-10-17 09:09:39 -0700676
James Feist25690252019-12-23 12:25:49 -0800677#ifdef USING_ENTITY_MANAGER_DECORATORS
678
Patrick Venture9ce789f2019-10-17 09:09:39 -0700679 boost::container::flat_map<std::string, DbusVariant>* entityData = nullptr;
Patrick Venture9ce789f2019-10-17 09:09:39 -0700680
James Feist25690252019-12-23 12:25:49 -0800681 // todo: this should really use caching, this is a very inefficient lookup
682 boost::system::error_code ec;
683 ManagedObjectType entities = ctx->bus->yield_method_call<ManagedObjectType>(
Nan Zhou9d2894d2022-09-20 22:22:00 +0000684 ctx->yield, ec, "xyz.openbmc_project.EntityManager",
685 "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager",
686 "GetManagedObjects");
James Feist25690252019-12-23 12:25:49 -0800687
688 if (ec)
Patrick Venture9ce789f2019-10-17 09:09:39 -0700689 {
James Feist25690252019-12-23 12:25:49 -0800690 phosphor::logging::log<phosphor::logging::level::ERR>(
691 "GetMangagedObjects for getSensorMap failed",
692 phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
Patrick Venture9ce789f2019-10-17 09:09:39 -0700693
James Feist25690252019-12-23 12:25:49 -0800694 return ipmi::ccResponseError;
695 }
Patrick Venture9ce789f2019-10-17 09:09:39 -0700696
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500697 auto entity =
698 std::find_if(entities.begin(), entities.end(),
699 [bus, address, &entityData](ManagedEntry& entry) {
700 auto findFruDevice = entry.second.find(
701 "xyz.openbmc_project.Inventory.Decorator.FruDevice");
702 if (findFruDevice == entry.second.end())
703 {
704 return false;
705 }
James Feist25690252019-12-23 12:25:49 -0800706
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500707 // Integer fields added via Entity-Manager json are uint64_ts by
708 // default.
709 auto findBus = findFruDevice->second.find("Bus");
710 auto findAddress = findFruDevice->second.find("Address");
James Feist25690252019-12-23 12:25:49 -0800711
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500712 if (findBus == findFruDevice->second.end() ||
713 findAddress == findFruDevice->second.end())
714 {
715 return false;
716 }
717 if ((std::get<uint64_t>(findBus->second) != bus) ||
718 (std::get<uint64_t>(findAddress->second) != address))
719 {
720 return false;
721 }
James Feist25690252019-12-23 12:25:49 -0800722
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500723 // At this point we found the device entry and should return
724 // true.
725 auto findIpmiDevice =
726 entry.second.find("xyz.openbmc_project.Inventory.Decorator.Ipmi");
727 if (findIpmiDevice != entry.second.end())
728 {
729 entityData = &(findIpmiDevice->second);
730 }
James Feist25690252019-12-23 12:25:49 -0800731
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500732 return true;
Patrick Williams87381412023-10-20 11:18:48 -0500733 });
James Feist25690252019-12-23 12:25:49 -0800734
735 if (entity == entities.end())
736 {
737 if constexpr (DEBUG)
738 {
739 std::fprintf(stderr, "Ipmi or FruDevice Decorator interface "
740 "not found for Fru\n");
Patrick Venture9ce789f2019-10-17 09:09:39 -0700741 }
742 }
James Feist25690252019-12-23 12:25:49 -0800743
744#endif
Patrick Venture9ce789f2019-10-17 09:09:39 -0700745
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700746 std::string name;
747 auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
748 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
749 if (findProductName != fruData->end())
750 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700751 name = std::get<std::string>(findProductName->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700752 }
753 else if (findBoardName != fruData->end())
754 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700755 name = std::get<std::string>(findBoardName->second);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700756 }
757 else
758 {
759 name = "UNKNOWN";
760 }
761 if (name.size() > maxFruSdrNameSize)
762 {
763 name = name.substr(0, maxFruSdrNameSize);
764 }
765 size_t sizeDiff = maxFruSdrNameSize - name.size();
766
767 resp.header.record_id_lsb = 0x0; // calling code is to implement these
768 resp.header.record_id_msb = 0x0;
769 resp.header.sdr_version = ipmiSdrVersion;
Patrick Venture73d01352019-10-11 18:32:59 -0700770 resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700771 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
772 resp.key.deviceAddress = 0x20;
773 resp.key.fruID = device->first;
774 resp.key.accessLun = 0x80; // logical / physical fru device
775 resp.key.channelNumber = 0x0;
776 resp.body.reserved = 0x0;
777 resp.body.deviceType = 0x10;
James Feist4f86d1f2019-04-03 10:30:26 -0700778 resp.body.deviceTypeModifier = 0x0;
Patrick Venture9ce789f2019-10-17 09:09:39 -0700779
780 uint8_t entityID = 0;
781 uint8_t entityInstance = 0x1;
782
James Feist25690252019-12-23 12:25:49 -0800783#ifdef USING_ENTITY_MANAGER_DECORATORS
Patrick Venture9ce789f2019-10-17 09:09:39 -0700784 if (entityData)
785 {
786 auto entityIdProperty = entityData->find("EntityId");
787 auto entityInstanceProperty = entityData->find("EntityInstance");
788
789 if (entityIdProperty != entityData->end())
790 {
791 entityID = static_cast<uint8_t>(
792 std::get<uint64_t>(entityIdProperty->second));
793 }
794 if (entityInstanceProperty != entityData->end())
795 {
796 entityInstance = static_cast<uint8_t>(
797 std::get<uint64_t>(entityInstanceProperty->second));
798 }
799 }
James Feist25690252019-12-23 12:25:49 -0800800#endif
Patrick Venture9ce789f2019-10-17 09:09:39 -0700801
802 resp.body.entityID = entityID;
803 resp.body.entityInstance = entityInstance;
804
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700805 resp.body.oem = 0x0;
806 resp.body.deviceIDLen = name.size();
807 name.copy(resp.body.deviceID, name.size());
808
Vernon Mauerydcff1502022-09-28 11:12:46 -0700809 return ipmi::ccSuccess;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700810}
Jason M. Billse2d1aee2018-10-03 15:57:18 -0700811
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700812static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800813{
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700814 // Loop through the directory looking for ipmi_sel log files
815 for (const std::filesystem::directory_entry& dirEnt :
816 std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir))
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800817 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700818 std::string filename = dirEnt.path().filename();
819 if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename))
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800820 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700821 // If we find an ipmi_sel log file, save the path
822 selLogFiles.emplace_back(intel_oem::ipmi::sel::selLogDir /
823 filename);
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800824 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800825 }
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700826 // As the log files rotate, they are appended with a ".#" that is higher for
827 // the older logs. Since we don't expect more than 10 log files, we
828 // can just sort the list to get them in order from newest to oldest
829 std::sort(selLogFiles.begin(), selLogFiles.end());
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800830
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700831 return !selLogFiles.empty();
832}
833
834static int countSELEntries()
835{
836 // Get the list of ipmi_sel log files
837 std::vector<std::filesystem::path> selLogFiles;
838 if (!getSELLogFiles(selLogFiles))
839 {
840 return 0;
841 }
842 int numSELEntries = 0;
843 // Loop through each log file and count the number of logs
844 for (const std::filesystem::path& file : selLogFiles)
845 {
846 std::ifstream logStream(file);
847 if (!logStream.is_open())
848 {
849 continue;
850 }
851
852 std::string line;
853 while (std::getline(logStream, line))
854 {
855 numSELEntries++;
856 }
857 }
858 return numSELEntries;
859}
860
861static bool findSELEntry(const int recordID,
Patrick Ventureff7e15b2019-09-25 16:48:26 -0700862 const std::vector<std::filesystem::path>& selLogFiles,
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700863 std::string& entry)
864{
865 // Record ID is the first entry field following the timestamp. It is
866 // preceded by a space and followed by a comma
867 std::string search = " " + std::to_string(recordID) + ",";
868
869 // Loop through the ipmi_sel log entries
870 for (const std::filesystem::path& file : selLogFiles)
871 {
872 std::ifstream logStream(file);
873 if (!logStream.is_open())
874 {
875 continue;
876 }
877
878 while (std::getline(logStream, entry))
879 {
880 // Check if the record ID matches
881 if (entry.find(search) != std::string::npos)
882 {
883 return true;
884 }
885 }
886 }
887 return false;
888}
889
890static uint16_t
891 getNextRecordID(const uint16_t recordID,
Patrick Ventureff7e15b2019-09-25 16:48:26 -0700892 const std::vector<std::filesystem::path>& selLogFiles)
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700893{
894 uint16_t nextRecordID = recordID + 1;
895 std::string entry;
896 if (findSELEntry(nextRecordID, selLogFiles, entry))
897 {
898 return nextRecordID;
899 }
900 else
901 {
902 return ipmi::sel::lastEntry;
903 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800904}
905
Patrick Ventureff7e15b2019-09-25 16:48:26 -0700906static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800907{
908 for (unsigned int i = 0; i < hexStr.size(); i += 2)
909 {
910 try
911 {
912 data.push_back(static_cast<uint8_t>(
913 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
914 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500915 catch (const std::invalid_argument& e)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800916 {
917 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
918 return -1;
919 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -0500920 catch (const std::out_of_range& e)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800921 {
922 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
923 return -1;
924 }
925 }
926 return 0;
927}
928
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700929ipmi::RspType<uint8_t, // SEL version
930 uint16_t, // SEL entry count
931 uint16_t, // free space
932 uint32_t, // last add timestamp
933 uint32_t, // last erase timestamp
934 uint8_t> // operation support
935 ipmiStorageGetSELInfo()
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800936{
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700937 constexpr uint8_t selVersion = ipmi::sel::selVersion;
938 uint16_t entries = countSELEntries();
939 uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp(
940 intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename);
941 uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get();
942 constexpr uint8_t operationSupport =
943 intel_oem::ipmi::sel::selOperationSupport;
944 constexpr uint16_t freeSpace =
945 0xffff; // Spec indicates that more than 64kB is free
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800946
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700947 return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
948 eraseTimeStamp, operationSupport);
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800949}
950
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700951using systemEventType = std::tuple<
952 uint32_t, // Timestamp
953 uint16_t, // Generator ID
954 uint8_t, // EvM Rev
955 uint8_t, // Sensor Type
956 uint8_t, // Sensor Number
957 uint7_t, // Event Type
958 bool, // Event Direction
959 std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
960using oemTsEventType = std::tuple<
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500961 uint32_t, // Timestamp
962 std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700963using oemEventType =
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500964 std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800965
Patrick Williamsb37abfb2023-05-10 07:50:33 -0500966ipmi::RspType<uint16_t, // Next Record ID
967 uint16_t, // Record ID
968 uint8_t, // Record Type
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700969 std::variant<systemEventType, oemTsEventType,
970 oemEventType>> // Record Content
971 ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
972 uint8_t offset, uint8_t size)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800973{
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700974 // Only support getting the entire SEL record. If a partial size or non-zero
975 // offset is requested, return an error
976 if (offset != 0 || size != ipmi::sel::entireRecord)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800977 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700978 return ipmi::responseRetBytesUnavailable();
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800979 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800980
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700981 // Check the reservation ID if one is provided or required (only if the
982 // offset is non-zero)
983 if (reservationID != 0 || offset != 0)
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800984 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700985 if (!checkSELReservation(reservationID))
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800986 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700987 return ipmi::responseInvalidReservationId();
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800988 }
989 }
990
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700991 // Get the ipmi_sel log files
992 std::vector<std::filesystem::path> selLogFiles;
993 if (!getSELLogFiles(selLogFiles))
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800994 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700995 return ipmi::responseSensorInvalid();
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800996 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800997
Jason M. Bills1d4d54d2019-04-23 11:26:11 -0700998 std::string targetEntry;
Jason M. Billsc04e2e72018-11-28 15:15:56 -0800999
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001000 if (targetID == ipmi::sel::firstEntry)
1001 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001002 // The first entry will be at the top of the oldest log file
1003 std::ifstream logStream(selLogFiles.back());
1004 if (!logStream.is_open())
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001005 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001006 return ipmi::responseUnspecifiedError();
1007 }
1008
1009 if (!std::getline(logStream, targetEntry))
1010 {
1011 return ipmi::responseUnspecifiedError();
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001012 }
1013 }
1014 else if (targetID == ipmi::sel::lastEntry)
1015 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001016 // The last entry will be at the bottom of the newest log file
1017 std::ifstream logStream(selLogFiles.front());
1018 if (!logStream.is_open())
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001019 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001020 return ipmi::responseUnspecifiedError();
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001021 }
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001022
1023 std::string line;
1024 while (std::getline(logStream, line))
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001025 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001026 targetEntry = line;
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001027 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001028 }
1029 else
1030 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001031 if (!findSELEntry(targetID, selLogFiles, targetEntry))
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001032 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001033 return ipmi::responseSensorInvalid();
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001034 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001035 }
1036
Jason M. Bills52aaa7d2019-05-08 15:21:39 -07001037 // The format of the ipmi_sel message is "<Timestamp>
1038 // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
1039 // First get the Timestamp
1040 size_t space = targetEntry.find_first_of(" ");
1041 if (space == std::string::npos)
1042 {
1043 return ipmi::responseUnspecifiedError();
1044 }
1045 std::string entryTimestamp = targetEntry.substr(0, space);
1046 // Then get the log contents
1047 size_t entryStart = targetEntry.find_first_not_of(" ", space);
1048 if (entryStart == std::string::npos)
1049 {
1050 return ipmi::responseUnspecifiedError();
1051 }
1052 std::string_view entry(targetEntry);
1053 entry.remove_prefix(entryStart);
1054 // Use split to separate the entry into its fields
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001055 std::vector<std::string> targetEntryFields;
Jason M. Bills52aaa7d2019-05-08 15:21:39 -07001056 boost::split(targetEntryFields, entry, boost::is_any_of(","),
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001057 boost::token_compress_on);
Jason M. Bills52aaa7d2019-05-08 15:21:39 -07001058 if (targetEntryFields.size() < 3)
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001059 {
1060 return ipmi::responseUnspecifiedError();
1061 }
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001062 std::string& recordIDStr = targetEntryFields[0];
1063 std::string& recordTypeStr = targetEntryFields[1];
1064 std::string& eventDataStr = targetEntryFields[2];
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001065
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001066 uint16_t recordID;
1067 uint8_t recordType;
1068 try
1069 {
1070 recordID = std::stoul(recordIDStr);
1071 recordType = std::stoul(recordTypeStr, nullptr, 16);
1072 }
1073 catch (const std::invalid_argument&)
1074 {
1075 return ipmi::responseUnspecifiedError();
1076 }
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001077 uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001078 std::vector<uint8_t> eventDataBytes;
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001079 if (fromHexStr(eventDataStr, eventDataBytes) < 0)
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001080 {
1081 return ipmi::responseUnspecifiedError();
1082 }
1083
1084 if (recordType == intel_oem::ipmi::sel::systemEvent)
1085 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001086 // Get the timestamp
1087 std::tm timeStruct = {};
Jason M. Bills52aaa7d2019-05-08 15:21:39 -07001088 std::istringstream entryStream(entryTimestamp);
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001089
1090 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1091 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1092 {
1093 timestamp = std::mktime(&timeStruct);
1094 }
1095
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001096 // Set the event message revision
1097 uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev;
1098
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001099 uint16_t generatorID = 0;
1100 uint8_t sensorType = 0;
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001101 uint16_t sensorAndLun = 0;
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001102 uint8_t sensorNum = 0xFF;
1103 uint7_t eventType = 0;
1104 bool eventDir = 0;
1105 // System type events should have six fields
1106 if (targetEntryFields.size() >= 6)
1107 {
1108 std::string& generatorIDStr = targetEntryFields[3];
1109 std::string& sensorPath = targetEntryFields[4];
1110 std::string& eventDirStr = targetEntryFields[5];
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001111
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001112 // Get the generator ID
1113 try
1114 {
1115 generatorID = std::stoul(generatorIDStr, nullptr, 16);
1116 }
1117 catch (const std::invalid_argument&)
1118 {
1119 std::cerr << "Invalid Generator ID\n";
1120 }
1121
1122 // Get the sensor type, sensor number, and event type for the sensor
1123 sensorType = getSensorTypeFromPath(sensorPath);
Johnathan Mantey308c3a82020-07-22 11:50:54 -07001124 sensorAndLun = getSensorNumberFromPath(sensorPath);
1125 sensorNum = static_cast<uint8_t>(sensorAndLun);
1126 generatorID |= sensorAndLun >> 8;
Jason M. Bills1a2fbdd2019-05-10 09:05:37 -07001127 eventType = getSensorEventTypeFromPath(sensorPath);
1128
1129 // Get the event direction
1130 try
1131 {
1132 eventDir = std::stoul(eventDirStr) ? 0 : 1;
1133 }
1134 catch (const std::invalid_argument&)
1135 {
1136 std::cerr << "Invalid Event Direction\n";
1137 }
1138 }
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001139
1140 // Only keep the eventData bytes that fit in the record
1141 std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
1142 std::copy_n(eventDataBytes.begin(),
1143 std::min(eventDataBytes.size(), eventData.size()),
1144 eventData.begin());
1145
1146 return ipmi::responseSuccess(
1147 nextRecordID, recordID, recordType,
1148 systemEventType{timestamp, generatorID, evmRev, sensorType,
1149 sensorNum, eventType, eventDir, eventData});
1150 }
1151 else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst &&
1152 recordType <= intel_oem::ipmi::sel::oemTsEventLast)
1153 {
1154 // Get the timestamp
1155 std::tm timeStruct = {};
Jason M. Bills52aaa7d2019-05-08 15:21:39 -07001156 std::istringstream entryStream(entryTimestamp);
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001157
1158 uint32_t timestamp = ipmi::sel::invalidTimeStamp;
1159 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
1160 {
1161 timestamp = std::mktime(&timeStruct);
1162 }
1163
1164 // Only keep the bytes that fit in the record
1165 std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{};
1166 std::copy_n(eventDataBytes.begin(),
1167 std::min(eventDataBytes.size(), eventData.size()),
1168 eventData.begin());
1169
1170 return ipmi::responseSuccess(nextRecordID, recordID, recordType,
1171 oemTsEventType{timestamp, eventData});
1172 }
Patrick Venturec5136aa2019-10-04 20:39:31 -07001173 else if (recordType >= intel_oem::ipmi::sel::oemEventFirst)
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001174 {
1175 // Only keep the bytes that fit in the record
1176 std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{};
1177 std::copy_n(eventDataBytes.begin(),
1178 std::min(eventDataBytes.size(), eventData.size()),
1179 eventData.begin());
1180
1181 return ipmi::responseSuccess(nextRecordID, recordID, recordType,
1182 eventData);
1183 }
1184
1185 return ipmi::responseUnspecifiedError();
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001186}
1187
Jason M. Bills6dd8f042019-04-11 10:39:02 -07001188ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(
1189 uint16_t recordID, uint8_t recordType, uint32_t timestamp,
1190 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
1191 uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
1192 uint8_t eventData3)
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001193{
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001194 // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1195 // added
1196 cancelSELReservation();
1197
Jason M. Bills6dd8f042019-04-11 10:39:02 -07001198 // Send this request to the Redfish hooks to log it as a Redfish message
1199 // instead. There is no need to add it to the SEL, so just return success.
1200 intel_oem::ipmi::sel::checkRedfishHooks(
1201 recordID, recordType, timestamp, generatorID, evmRev, sensorType,
1202 sensorNum, eventType, eventData1, eventData2, eventData3);
Jason M. Bills99b78ec2019-01-18 10:42:18 -08001203
Jason M. Bills6dd8f042019-04-11 10:39:02 -07001204 uint16_t responseID = 0xFFFF;
1205 return ipmi::responseSuccess(responseID);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001206}
1207
Vernon Mauerydcff1502022-09-28 11:12:46 -07001208ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr&,
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001209 uint16_t reservationID,
1210 const std::array<uint8_t, 3>& clr,
1211 uint8_t eraseOperation)
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001212{
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001213 if (!checkSELReservation(reservationID))
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001214 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001215 return ipmi::responseInvalidReservationId();
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001216 }
1217
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001218 static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
1219 if (clr != clrExpected)
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001220 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001221 return ipmi::responseInvalidFieldRequest();
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001222 }
1223
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001224 // Erasure status cannot be fetched, so always return erasure status as
1225 // `erase completed`.
1226 if (eraseOperation == ipmi::sel::getEraseStatus)
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001227 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001228 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001229 }
1230
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001231 // Check that initiate erase is correct
1232 if (eraseOperation != ipmi::sel::initiateErase)
1233 {
1234 return ipmi::responseInvalidFieldRequest();
1235 }
1236
1237 // Per the IPMI spec, need to cancel any reservation when the SEL is
1238 // cleared
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001239 cancelSELReservation();
1240
Jason M. Bills7944c302019-03-20 15:24:05 -07001241 // Save the erase time
1242 intel_oem::ipmi::sel::erase_time::save();
1243
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001244 // Clear the SEL by deleting the log files
1245 std::vector<std::filesystem::path> selLogFiles;
1246 if (getSELLogFiles(selLogFiles))
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001247 {
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001248 for (const std::filesystem::path& file : selLogFiles)
1249 {
1250 std::error_code ec;
1251 std::filesystem::remove(file, ec);
1252 }
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001253 }
1254
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001255 // Reload rsyslog so it knows to start new log files
Vernon Mauery15419dd2019-05-24 09:40:30 -07001256 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williamsf944d2e2022-07-22 19:26:52 -05001257 sdbusplus::message_t rsyslogReload = dbus->new_method_call(
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001258 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1259 "org.freedesktop.systemd1.Manager", "ReloadUnit");
1260 rsyslogReload.append("rsyslog.service", "replace");
1261 try
1262 {
Patrick Williamsf944d2e2022-07-22 19:26:52 -05001263 sdbusplus::message_t reloadResponse = dbus->call(rsyslogReload);
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001264 }
Patrick Williamsbd51e6a2021-10-06 13:09:44 -05001265 catch (const sdbusplus::exception_t& e)
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001266 {
1267 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1268 }
1269
1270 return ipmi::responseSuccess(ipmi::sel::eraseComplete);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001271}
1272
Jason M. Bills1a474622019-06-14 14:51:33 -07001273ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
1274{
1275 struct timespec selTime = {};
1276
1277 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
1278 {
1279 return ipmi::responseUnspecifiedError();
1280 }
1281
1282 return ipmi::responseSuccess(selTime.tv_sec);
1283}
1284
Vernon Mauerydcff1502022-09-28 11:12:46 -07001285ipmi::RspType<> ipmiStorageSetSELTime([[maybe_unused]] uint32_t selTime)
Jason M. Billscac97a52019-01-30 14:43:46 -08001286{
1287 // Set SEL Time is not supported
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001288 return ipmi::responseInvalidCommand();
Jason M. Billscac97a52019-01-30 14:43:46 -08001289}
1290
James Feist74c50c62019-08-14 14:18:41 -07001291std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
1292{
1293 std::vector<uint8_t> resp;
1294 if (index == 0)
1295 {
James Feist74c50c62019-08-14 14:18:41 -07001296 std::string bmcName = "Basbrd Mgmt Ctlr";
Johnathan Manteyf4d5e052021-09-22 12:58:08 -07001297 Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName);
James Feist74c50c62019-08-14 14:18:41 -07001298 uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
1299 resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
1300 }
1301 else if (index == 1)
1302 {
James Feist74c50c62019-08-14 14:18:41 -07001303 std::string meName = "Mgmt Engine";
Johnathan Manteyf4d5e052021-09-22 12:58:08 -07001304 Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName);
James Feist74c50c62019-08-14 14:18:41 -07001305 uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
1306 resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
1307 }
1308 else
1309 {
1310 throw std::runtime_error("getType12SDRs:: Illegal index " +
1311 std::to_string(index));
1312 }
1313
1314 return resp;
1315}
1316
Yong Lifee5e4c2020-01-17 19:36:29 +08001317std::vector<uint8_t> getNMDiscoverySDR(uint16_t index, uint16_t recordId)
1318{
1319 std::vector<uint8_t> resp;
1320 if (index == 0)
1321 {
1322 NMDiscoveryRecord nm = {};
1323 nm.header.record_id_lsb = recordId;
1324 nm.header.record_id_msb = recordId >> 8;
1325 nm.header.sdr_version = ipmiSdrVersion;
1326 nm.header.record_type = 0xC0;
1327 nm.header.record_length = 0xB;
1328 nm.oemID0 = 0x57;
1329 nm.oemID1 = 0x1;
1330 nm.oemID2 = 0x0;
1331 nm.subType = 0x0D;
1332 nm.version = 0x1;
Matt Simmering80d4d5f2023-02-15 15:18:51 -08001333 nm.targetAddress = 0x2C;
Yong Lifee5e4c2020-01-17 19:36:29 +08001334 nm.channelNumber = 0x60;
1335 nm.healthEventSensor = 0x19;
1336 nm.exceptionEventSensor = 0x18;
1337 nm.operationalCapSensor = 0x1A;
1338 nm.thresholdExceededSensor = 0x1B;
1339
1340 uint8_t* nmPtr = reinterpret_cast<uint8_t*>(&nm);
1341 resp.insert(resp.end(), nmPtr, nmPtr + sizeof(NMDiscoveryRecord));
1342 }
1343 else
1344 {
1345 throw std::runtime_error("getNMDiscoverySDR:: Illegal index " +
1346 std::to_string(index));
1347 }
1348
1349 return resp;
1350}
1351
Jason M. Billse2d1aee2018-10-03 15:57:18 -07001352void registerStorageFunctions()
1353{
James Feist25690252019-12-23 12:25:49 -08001354 createTimers();
James Feiste4f710d2020-05-20 15:50:30 -07001355 startMatch();
1356
Jason M. Billse2d1aee2018-10-03 15:57:18 -07001357 // <Get FRU Inventory Area Info>
jayaprakash Mutyalad33acd62019-05-17 19:37:25 +00001358 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1359 ipmi::storage::cmdGetFruInventoryAreaInfo,
1360 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001361 // <READ FRU Data>
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +00001362 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1363 ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
1364 ipmiStorageReadFruData);
Jason M. Billse2d1aee2018-10-03 15:57:18 -07001365
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001366 // <WRITE FRU Data>
jayaprakash Mutyala5f4194e2019-05-20 16:17:01 +00001367 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1368 ipmi::storage::cmdWriteFruData,
1369 ipmi::Privilege::Operator, ipmiStorageWriteFruData);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001370
1371 // <Get SEL Info>
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001372 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Jason M. Bills542498e2019-06-24 16:57:42 -07001373 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1374 ipmiStorageGetSELInfo);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001375
1376 // <Get SEL Entry>
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001377 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Jason M. Bills542498e2019-06-24 16:57:42 -07001378 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1379 ipmiStorageGetSELEntry);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001380
1381 // <Add SEL Entry>
Jason M. Bills6dd8f042019-04-11 10:39:02 -07001382 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Vernon Mauery98bbf692019-09-16 11:14:59 -07001383 ipmi::storage::cmdAddSelEntry,
Jason M. Bills6dd8f042019-04-11 10:39:02 -07001384 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001385
1386 // <Clear SEL>
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001387 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1388 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
1389 ipmiStorageClearSEL);
Jason M. Billscac97a52019-01-30 14:43:46 -08001390
Jason M. Bills1a474622019-06-14 14:51:33 -07001391 // <Get SEL Time>
1392 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
Jason M. Bills542498e2019-06-24 16:57:42 -07001393 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1394 ipmiStorageGetSELTime);
Jason M. Bills1a474622019-06-14 14:51:33 -07001395
Jason M. Billscac97a52019-01-30 14:43:46 -08001396 // <Set SEL Time>
Jason M. Bills1d4d54d2019-04-23 11:26:11 -07001397 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1398 ipmi::storage::cmdSetSelTime,
1399 ipmi::Privilege::Operator, ipmiStorageSetSELTime);
Jason M. Billse2d1aee2018-10-03 15:57:18 -07001400}
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001401} // namespace storage
Jason M. Billsc04e2e72018-11-28 15:15:56 -08001402} // namespace ipmi