blob: 84801ba199717d27c54cbeb0e6fe6361de09f793 [file] [log] [blame]
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001/*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <ipmid/api.h>
19
20#include <boost/container/flat_map.hpp>
21#include <commandutils.hpp>
Vijay Khemka1b6fae32019-03-25 17:43:01 -070022#include <ipmid/utils.hpp>
Vijay Khemkae7d23d02019-03-08 13:13:40 -080023#include <phosphor-logging/log.hpp>
24#include <sdbusplus/message/types.hpp>
25#include <sdbusplus/timer.hpp>
26#include <sensorutils.hpp>
27#include <storagecommands.hpp>
28
Vijay Khemka63c99be2020-05-27 19:14:35 -070029#include <iostream>
Patrick Williams2405ae92023-05-10 07:50:09 -050030#include <unordered_map>
Vijay Khemka63c99be2020-05-27 19:14:35 -070031
Vijay Khemkae7d23d02019-03-08 13:13:40 -080032namespace ipmi
33{
34
35namespace storage
36{
37void registerStorageFunctions() __attribute__((constructor));
38
39constexpr static const size_t maxMessageSize = 64;
40constexpr static const size_t maxFruSdrNameSize = 16;
41static constexpr int sensorMapUpdatePeriod = 2;
42using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080043
44using ManagedObjectSensor =
45 std::map<sdbusplus::message::object_path,
46 std::map<std::string, std::map<std::string, DbusVariant>>>;
47
48static uint16_t sdrReservationID;
49
50static boost::container::flat_map<std::string, ManagedObjectSensor> SensorCache;
51static SensorSubTree sensorTree;
52
53void registerSensorFunctions() __attribute__((constructor));
54using ManagedObjectType = boost::container::flat_map<
55 sdbusplus::message::object_path,
56 boost::container::flat_map<
57 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
58using ManagedEntry = std::pair<
59 sdbusplus::message::object_path,
60 boost::container::flat_map<
61 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
62
Vijay Khemka63c99be2020-05-27 19:14:35 -070063constexpr static const char* fruDeviceServiceName =
Vijay Khemkae7d23d02019-03-08 13:13:40 -080064 "xyz.openbmc_project.FruDevice";
65constexpr static const size_t cacheTimeoutSeconds = 10;
66
67static std::vector<uint8_t> fruCache;
Potin Lai4de58762023-09-01 15:56:00 +080068static uint16_t cacheBus = 0xFFFF;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080069static uint8_t cacheAddr = 0XFF;
70
Patrick Williamsd79f89e2023-12-05 12:45:03 -060071std::unique_ptr<sdbusplus::Timer> cacheTimer = nullptr;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080072
73// we unfortunately have to build a map of hashes in case there is a
74// collision to verify our dev-id
75boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
76
Patrick Williamscd315e02022-07-22 19:26:52 -050077static sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
Vijay Khemkae7d23d02019-03-08 13:13:40 -080078
Delphine CC Chiu2ca4aa02023-02-01 16:30:18 +080079using InterfaceName = std::string;
80using PropertyName = std::string;
81using ThresholdStr = std::string;
82
83enum class AlarmType
84{
85 low,
86 high
87};
88
89struct Property
90{
91 PropertyName name;
92 ThresholdStr threshold;
93};
94
95const std::vector<InterfaceName> thresholdCheckedOrder{
96 "xyz.openbmc_project.Sensor.Threshold.HardShutdown",
97 "xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
98 "xyz.openbmc_project.Sensor.Threshold.Critical",
99 "xyz.openbmc_project.Sensor.Threshold.Warning"};
100
101const std::unordered_map<std::string, std::map<AlarmType, Property>>
102 alarmProperties{
103 {"xyz.openbmc_project.Sensor.Threshold.HardShutdown",
104 {{AlarmType::low, Property{"HardShutdownAlarmLow", "LNR"}},
105 {AlarmType::high, Property{"HardShutdownAlarmHigh", "UNR"}}}},
106
107 {"xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
108 {{AlarmType::low, Property{"SoftShutdownAlarmLow", "LNR"}},
109 {AlarmType::high, Property{"SoftShutdownAlarmHigh", "UNR"}}}},
110
111 {"xyz.openbmc_project.Sensor.Threshold.Critical",
112 {{AlarmType::low, Property{"CriticalAlarmLow", "LCR"}},
113 {AlarmType::high, Property{"CriticalAlarmHigh", "UCR"}}}},
114
115 {"xyz.openbmc_project.Sensor.Threshold.Warning",
116 {{AlarmType::low, Property{"WarningAlarmLow", "LNC"}},
117 {AlarmType::high, Property{"WarningAlarmHigh", "UNC"}}}},
118 };
119
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800120static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
Vijay Khemka63c99be2020-05-27 19:14:35 -0700121 SensorMap& sensorMap)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800122{
123 static boost::container::flat_map<
124 std::string, std::chrono::time_point<std::chrono::steady_clock>>
125 updateTimeMap;
126
127 auto updateFind = updateTimeMap.find(sensorConnection);
128 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
129 if (updateFind != updateTimeMap.end())
130 {
131 lastUpdate = updateFind->second;
132 }
133
134 auto now = std::chrono::steady_clock::now();
135
136 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
137 .count() > sensorMapUpdatePeriod)
138 {
139 updateTimeMap[sensorConnection] = now;
140
141 auto managedObj = dbus.new_method_call(
Potin Lai8ee95d62022-12-21 11:11:59 +0800142 sensorConnection.c_str(), "/xyz/openbmc_project/sensors",
143 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800144
145 ManagedObjectSensor managedObjects;
146 try
147 {
148 auto reply = dbus.call(managedObj);
149 reply.read(managedObjects);
150 }
Patrick Williams35d12542021-10-06 11:21:13 -0500151 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800152 {
153 phosphor::logging::log<phosphor::logging::level::ERR>(
154 "Error getting managed objects from connection",
155 phosphor::logging::entry("CONNECTION=%s",
156 sensorConnection.c_str()));
157 return false;
158 }
159
160 SensorCache[sensorConnection] = managedObjects;
161 }
162 auto connection = SensorCache.find(sensorConnection);
163 if (connection == SensorCache.end())
164 {
165 return false;
166 }
167 auto path = connection->second.find(sensorPath);
168 if (path == connection->second.end())
169 {
170 return false;
171 }
172 sensorMap = path->second;
173
174 return true;
175}
176
177bool writeFru()
178{
Patrick Williamscd315e02022-07-22 19:26:52 -0500179 sdbusplus::message_t writeFru = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800180 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
181 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
182 writeFru.append(cacheBus, cacheAddr, fruCache);
183 try
184 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500185 sdbusplus::message_t writeFruResp = dbus.call(writeFru);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800186 }
Patrick Williams35d12542021-10-06 11:21:13 -0500187 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800188 {
189 // todo: log sel?
190 phosphor::logging::log<phosphor::logging::level::ERR>(
191 "error writing fru");
192 return false;
193 }
194 return true;
195}
196
197void createTimer()
198{
199 if (cacheTimer == nullptr)
200 {
Patrick Williamsd79f89e2023-12-05 12:45:03 -0600201 cacheTimer = std::make_unique<sdbusplus::Timer>(writeFru);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800202 }
203}
204
205ipmi_ret_t replaceCacheFru(uint8_t devId)
206{
207 static uint8_t lastDevId = 0xFF;
208
209 bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
210 if (lastDevId == devId && timerRunning)
211 {
212 return IPMI_CC_OK; // cache already up to date
213 }
214 // if timer is running, stop it and writeFru manually
215 else if (timerRunning)
216 {
217 cacheTimer->stop();
218 writeFru();
219 }
220
Patrick Williamscd315e02022-07-22 19:26:52 -0500221 sdbusplus::message_t getObjects = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800222 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
223 "GetManagedObjects");
224 ManagedObjectType frus;
225 try
226 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500227 sdbusplus::message_t resp = dbus.call(getObjects);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800228 resp.read(frus);
229 }
Patrick Williams35d12542021-10-06 11:21:13 -0500230 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800231 {
232 phosphor::logging::log<phosphor::logging::level::ERR>(
233 "replaceCacheFru: error getting managed objects");
234 return IPMI_CC_RESPONSE_ERROR;
235 }
236
237 deviceHashes.clear();
238
cchouxb2ae88b2023-09-13 00:35:36 +0800239 uint8_t fruHash = 0;
240 uint8_t mbFruBus = 0, mbFruAddr = 0;
241
242 auto device = getMbFruDevice();
243 if (device)
244 {
245 std::tie(mbFruBus, mbFruAddr) = *device;
246 deviceHashes.emplace(0, std::make_pair(mbFruBus, mbFruAddr));
247 fruHash++;
248 }
249
Vijay Khemka63c99be2020-05-27 19:14:35 -0700250 for (const auto& fru : frus)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800251 {
252 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
253 if (fruIface == fru.second.end())
254 {
255 continue;
256 }
257
258 auto busFind = fruIface->second.find("BUS");
259 auto addrFind = fruIface->second.find("ADDRESS");
260 if (busFind == fruIface->second.end() ||
261 addrFind == fruIface->second.end())
262 {
263 phosphor::logging::log<phosphor::logging::level::INFO>(
264 "fru device missing Bus or Address",
265 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
266 continue;
267 }
268
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500269 uint8_t fruBus = std::get<uint32_t>(busFind->second);
270 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
cchouxb2ae88b2023-09-13 00:35:36 +0800271 if (fruBus != mbFruBus || fruAddr != mbFruAddr)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800272 {
cchouxb2ae88b2023-09-13 00:35:36 +0800273 deviceHashes.emplace(fruHash, std::make_pair(fruBus, fruAddr));
274 fruHash++;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800275 }
276 }
277 auto deviceFind = deviceHashes.find(devId);
278 if (deviceFind == deviceHashes.end())
279 {
280 return IPMI_CC_SENSOR_INVALID;
281 }
282
283 fruCache.clear();
Patrick Williamscd315e02022-07-22 19:26:52 -0500284 sdbusplus::message_t getRawFru = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800285 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
286 "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
287 cacheBus = deviceFind->second.first;
288 cacheAddr = deviceFind->second.second;
289 getRawFru.append(cacheBus, cacheAddr);
290 try
291 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500292 sdbusplus::message_t getRawResp = dbus.call(getRawFru);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800293 getRawResp.read(fruCache);
294 }
Patrick Williams35d12542021-10-06 11:21:13 -0500295 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800296 {
297 lastDevId = 0xFF;
Potin Lai4de58762023-09-01 15:56:00 +0800298 cacheBus = 0xFFFF;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800299 cacheAddr = 0xFF;
300 return IPMI_CC_RESPONSE_ERROR;
301 }
302
303 lastDevId = devId;
304 return IPMI_CC_OK;
305}
306
Patrick Williams010dee02024-08-16 15:19:44 -0400307ipmi_ret_t ipmiStorageReadFRUData(
308 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
309 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800310{
311 if (*dataLen != 4)
312 {
313 *dataLen = 0;
314 return IPMI_CC_REQ_DATA_LEN_INVALID;
315 }
316 *dataLen = 0; // default to 0 in case of an error
317
Vijay Khemka63c99be2020-05-27 19:14:35 -0700318 auto req = static_cast<GetFRUAreaReq*>(request);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800319
320 if (req->countToRead > maxMessageSize - 1)
321 {
322 return IPMI_CC_INVALID_FIELD_REQUEST;
323 }
324 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
325
326 if (status != IPMI_CC_OK)
327 {
328 return status;
329 }
330
331 size_t fromFRUByteLen = 0;
332 if (req->countToRead + req->fruInventoryOffset < fruCache.size())
333 {
334 fromFRUByteLen = req->countToRead;
335 }
336 else if (fruCache.size() > req->fruInventoryOffset)
337 {
338 fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
339 }
340 size_t padByteLen = req->countToRead - fromFRUByteLen;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700341 uint8_t* respPtr = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800342 *respPtr = req->countToRead;
343 std::copy(fruCache.begin() + req->fruInventoryOffset,
344 fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
345 ++respPtr);
346 // if longer than the fru is requested, fill with 0xFF
347 if (padByteLen)
348 {
349 respPtr += fromFRUByteLen;
350 std::fill(respPtr, respPtr + padByteLen, 0xFF);
351 }
352 *dataLen = fromFRUByteLen + 1;
353
354 return IPMI_CC_OK;
355}
356
Patrick Williams010dee02024-08-16 15:19:44 -0400357ipmi_ret_t ipmiStorageWriteFRUData(
358 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
359 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800360{
361 if (*dataLen < 4 ||
362 *dataLen >=
363 0xFF + 3) // count written return is one byte, so limit to one
364 // byte of data after the three request data bytes
365 {
366 *dataLen = 0;
367 return IPMI_CC_REQ_DATA_LEN_INVALID;
368 }
369
Vijay Khemka63c99be2020-05-27 19:14:35 -0700370 auto req = static_cast<WriteFRUDataReq*>(request);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800371 size_t writeLen = *dataLen - 3;
372 *dataLen = 0; // default to 0 in case of an error
373
374 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
375 if (status != IPMI_CC_OK)
376 {
377 return status;
378 }
Willy Tue39f9392022-06-15 13:24:20 -0700379 size_t lastWriteAddr = req->fruInventoryOffset + writeLen;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800380 if (fruCache.size() < lastWriteAddr)
381 {
382 fruCache.resize(req->fruInventoryOffset + writeLen);
383 }
384
385 std::copy(req->data, req->data + writeLen,
386 fruCache.begin() + req->fruInventoryOffset);
387
388 bool atEnd = false;
389
390 if (fruCache.size() >= sizeof(FRUHeader))
391 {
Vijay Khemka63c99be2020-05-27 19:14:35 -0700392 FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800393
Willy Tue39f9392022-06-15 13:24:20 -0700394 size_t lastRecordStart = std::max(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800395 header->internalOffset,
396 std::max(header->chassisOffset,
397 std::max(header->boardOffset, header->productOffset)));
398 // TODO: Handle Multi-Record FRUs?
399
400 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
401
402 // get the length of the area in multiples of 8 bytes
403 if (lastWriteAddr > (lastRecordStart + 1))
404 {
405 // second byte in record area is the length
406 int areaLength(fruCache[lastRecordStart + 1]);
407 areaLength *= 8; // it is in multiples of 8 bytes
408
409 if (lastWriteAddr >= (areaLength + lastRecordStart))
410 {
411 atEnd = true;
412 }
413 }
414 }
Vijay Khemka63c99be2020-05-27 19:14:35 -0700415 uint8_t* respPtr = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800416 if (atEnd)
417 {
418 // cancel timer, we're at the end so might as well send it
419 cacheTimer->stop();
420 if (!writeFru())
421 {
422 return IPMI_CC_INVALID_FIELD_REQUEST;
423 }
424 *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
425 }
426 else
427 {
428 // start a timer, if no further data is sent in cacheTimeoutSeconds
429 // seconds, check to see if it is valid
430 createTimer();
431 cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
432 std::chrono::seconds(cacheTimeoutSeconds)));
433 *respPtr = 0;
434 }
435
436 *dataLen = 1;
437
438 return IPMI_CC_OK;
439}
440
Vijay Khemka63c99be2020-05-27 19:14:35 -0700441ipmi_ret_t getFruSdrCount(size_t& count)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800442{
443 ipmi_ret_t ret = replaceCacheFru(0);
444 if (ret != IPMI_CC_OK)
445 {
446 return ret;
447 }
448 count = deviceHashes.size();
449 return IPMI_CC_OK;
450}
451
Vijay Khemka63c99be2020-05-27 19:14:35 -0700452ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800453{
454 ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
455 if (ret != IPMI_CC_OK)
456 {
457 return ret;
458 }
459 if (deviceHashes.size() < index)
460 {
461 return IPMI_CC_INVALID_FIELD_REQUEST;
462 }
463 auto device = deviceHashes.begin() + index;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700464 uint8_t& bus = device->second.first;
465 uint8_t& address = device->second.second;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800466
467 ManagedObjectType frus;
468
Patrick Williamscd315e02022-07-22 19:26:52 -0500469 sdbusplus::message_t getObjects = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800470 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
471 "GetManagedObjects");
472 try
473 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500474 sdbusplus::message_t resp = dbus.call(getObjects);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800475 resp.read(frus);
476 }
Patrick Williams35d12542021-10-06 11:21:13 -0500477 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800478 {
479 return IPMI_CC_RESPONSE_ERROR;
480 }
Vijay Khemka63c99be2020-05-27 19:14:35 -0700481 boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
Patrick Williams010dee02024-08-16 15:19:44 -0400482 auto fru = std::find_if(
483 frus.begin(), frus.end(),
484 [bus, address, &fruData](ManagedEntry& entry) {
485 auto findFruDevice =
486 entry.second.find("xyz.openbmc_project.FruDevice");
487 if (findFruDevice == entry.second.end())
488 {
489 return false;
490 }
491 fruData = &(findFruDevice->second);
492 auto findBus = findFruDevice->second.find("BUS");
493 auto findAddress = findFruDevice->second.find("ADDRESS");
494 if (findBus == findFruDevice->second.end() ||
495 findAddress == findFruDevice->second.end())
496 {
497 return false;
498 }
499 if (std::get<uint32_t>(findBus->second) != bus)
500 {
501 return false;
502 }
503 if (std::get<uint32_t>(findAddress->second) != address)
504 {
505 return false;
506 }
507 return true;
508 });
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800509 if (fru == frus.end())
510 {
511 return IPMI_CC_RESPONSE_ERROR;
512 }
513 std::string name;
514 auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
515 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
516 if (findProductName != fruData->end())
517 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500518 name = std::get<std::string>(findProductName->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800519 }
520 else if (findBoardName != fruData->end())
521 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500522 name = std::get<std::string>(findBoardName->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800523 }
524 else
525 {
526 name = "UNKNOWN";
527 }
528 if (name.size() > maxFruSdrNameSize)
529 {
530 name = name.substr(0, maxFruSdrNameSize);
531 }
532 size_t sizeDiff = maxFruSdrNameSize - name.size();
533
534 resp.header.record_id_lsb = 0x0; // calling code is to implement these
535 resp.header.record_id_msb = 0x0;
536 resp.header.sdr_version = ipmiSdrVersion;
537 resp.header.record_type = 0x11; // FRU Device Locator
538 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
539 resp.key.deviceAddress = 0x20;
540 resp.key.fruID = device->first;
541 resp.key.accessLun = 0x80; // logical / physical fru device
542 resp.key.channelNumber = 0x0;
543 resp.body.reserved = 0x0;
544 resp.body.deviceType = 0x10;
545 resp.body.entityID = 0x0;
546 resp.body.entityInstance = 0x1;
547 resp.body.oem = 0x0;
548 resp.body.deviceIDLen = name.size();
549 name.copy(resp.body.deviceID, name.size());
550
551 return IPMI_CC_OK;
552}
553
554ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Willy Tue39f9392022-06-15 13:24:20 -0700555 ipmi_request_t, ipmi_response_t response,
556 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800557{
558 printCommand(+netfn, +cmd);
559
560 if (*dataLen)
561 {
562 *dataLen = 0;
563 return IPMI_CC_REQ_DATA_LEN_INVALID;
564 }
565 *dataLen = 0; // default to 0 in case of an error
566 sdrReservationID++;
567 if (sdrReservationID == 0)
568 {
569 sdrReservationID++;
570 }
571 *dataLen = 2;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700572 auto resp = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800573 resp[0] = sdrReservationID & 0xFF;
574 resp[1] = sdrReservationID >> 8;
575
576 return IPMI_CC_OK;
577}
578
579ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
580 ipmi_request_t request, ipmi_response_t response,
Willy Tue39f9392022-06-15 13:24:20 -0700581 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800582{
583 printCommand(+netfn, +cmd);
584
585 if (*dataLen != 6)
586 {
587 *dataLen = 0;
588 return IPMI_CC_REQ_DATA_LEN_INVALID;
589 }
590 auto requestedSize = *dataLen;
591 *dataLen = 0; // default to 0 in case of an error
592
593 constexpr uint16_t lastRecordIndex = 0xFFFF;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700594 auto req = static_cast<GetSDRReq*>(request);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800595
596 // reservation required for partial reads with non zero offset into
597 // record
598 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
599 req->offset)
600 {
601 return IPMI_CC_INVALID_RESERVATION_ID;
602 }
603
Potin Lai5a7a04d2024-01-10 15:25:21 +0800604 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800605 {
606 return IPMI_CC_RESPONSE_ERROR;
607 }
608
609 size_t fruCount = 0;
610 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
611 if (ret != IPMI_CC_OK)
612 {
613 return ret;
614 }
615
616 size_t lastRecord = sensorTree.size() + fruCount - 1;
617 if (req->recordID == lastRecordIndex)
618 {
619 req->recordID = lastRecord;
620 }
621 if (req->recordID > lastRecord)
622 {
623 return IPMI_CC_INVALID_FIELD_REQUEST;
624 }
625
cchoux87134272023-10-06 15:20:23 +0800626 uint16_t nextRecord = lastRecord >= static_cast<size_t>(req->recordID + 1)
Willy Tue39f9392022-06-15 13:24:20 -0700627 ? req->recordID + 1
628 : 0XFFFF;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800629
Vijay Khemka63c99be2020-05-27 19:14:35 -0700630 auto responseClear = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800631 std::fill(responseClear, responseClear + requestedSize, 0);
632
Vijay Khemka63c99be2020-05-27 19:14:35 -0700633 auto resp = static_cast<get_sdr::GetSdrResp*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800634 resp->next_record_id_lsb = nextRecord & 0xFF;
635 resp->next_record_id_msb = nextRecord >> 8;
636
637 if (req->recordID >= sensorTree.size())
638 {
639 size_t fruIndex = req->recordID - sensorTree.size();
640 if (fruIndex >= fruCount)
641 {
642 return IPMI_CC_INVALID_FIELD_REQUEST;
643 }
644 get_sdr::SensorDataFruRecord data;
645 if (req->offset > sizeof(data))
646 {
647 return IPMI_CC_INVALID_FIELD_REQUEST;
648 }
649 ret = ipmi::storage::getFruSdrs(fruIndex, data);
650 if (ret != IPMI_CC_OK)
651 {
652 return ret;
653 }
654 data.header.record_id_msb = req->recordID << 8;
655 data.header.record_id_lsb = req->recordID & 0xFF;
656 if (sizeof(data) < (req->offset + req->bytesToRead))
657 {
658 req->bytesToRead = sizeof(data) - req->offset;
659 }
660 *dataLen = req->bytesToRead + 2; // next record
Vijay Khemka63c99be2020-05-27 19:14:35 -0700661 std::memcpy(&resp->record_data, (char*)&data + req->offset,
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800662 req->bytesToRead);
663 return IPMI_CC_OK;
664 }
665
666 std::string connection;
667 std::string path;
668 uint16_t sensorIndex = req->recordID;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700669 for (const auto& sensor : sensorTree)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800670 {
671 if (sensorIndex-- == 0)
672 {
673 if (!sensor.second.size())
674 {
675 return IPMI_CC_RESPONSE_ERROR;
676 }
677 connection = sensor.second.begin()->first;
678 path = sensor.first;
679 break;
680 }
681 }
682
683 SensorMap sensorMap;
684 if (!getSensorMap(connection, path, sensorMap))
685 {
686 return IPMI_CC_RESPONSE_ERROR;
687 }
688 uint8_t sensornumber = (req->recordID & 0xFF);
Willy Tue39f9392022-06-15 13:24:20 -0700689 get_sdr::SensorDataFullRecord record = {};
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800690
691 record.header.record_id_msb = req->recordID << 8;
692 record.header.record_id_lsb = req->recordID & 0xFF;
693 record.header.sdr_version = ipmiSdrVersion;
694 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
695 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
696 sizeof(get_sdr::SensorDataRecordHeader);
697 record.key.owner_id = 0x20;
698 record.key.owner_lun = 0x0;
699 record.key.sensor_number = sensornumber;
700
701 record.body.entity_id = 0x0;
702 record.body.entity_instance = 0x01;
703 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
704 record.body.sensor_type = getSensorTypeFromPath(path);
705 std::string type = getSensorTypeStringFromPath(path);
706 auto typeCstr = type.c_str();
707 auto findUnits = sensorUnits.find(typeCstr);
708 if (findUnits != sensorUnits.end())
709 {
710 record.body.sensor_units_2_base =
711 static_cast<uint8_t>(findUnits->second);
712 } // else default 0x0 unspecified
713
714 record.body.event_reading_type = getSensorEventTypeFromPath(path);
715
716 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
717 if (sensorObject == sensorMap.end())
718 {
719 return IPMI_CC_RESPONSE_ERROR;
720 }
721
722 auto maxObject = sensorObject->second.find("MaxValue");
723 auto minObject = sensorObject->second.find("MinValue");
724 double max = 128;
725 double min = -127;
726 if (maxObject != sensorObject->second.end())
727 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500728 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800729 }
730
731 if (minObject != sensorObject->second.end())
732 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500733 min = std::visit(VariantToDoubleVisitor(), minObject->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800734 }
735
736 int16_t mValue;
737 int8_t rExp;
738 int16_t bValue;
739 int8_t bExp;
740 bool bSigned;
741
742 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
743 {
744 return IPMI_CC_RESPONSE_ERROR;
745 }
746
747 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
748 record.body.m_lsb = mValue & 0xFF;
749
750 // move the smallest bit of the MSB into place (bit 9)
751 // the MSbs are bits 7:8 in m_msb_and_tolerance
752 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
753
754 // assign the negative
755 if (mValue < 0)
756 {
757 mMsb |= (1 << 7);
758 }
759 record.body.m_msb_and_tolerance = mMsb;
760
761 record.body.b_lsb = bValue & 0xFF;
762
763 // move the smallest bit of the MSB into place
764 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
765 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
766
767 // assign the negative
768 if (bValue < 0)
769 {
770 bMsb |= (1 << 7);
771 }
772 record.body.b_msb_and_accuracy_lsb = bMsb;
773
774 record.body.r_b_exponents = bExp & 0x7;
775 if (bExp < 0)
776 {
777 record.body.r_b_exponents |= 1 << 3;
778 }
779 record.body.r_b_exponents = (rExp & 0x7) << 4;
780 if (rExp < 0)
781 {
782 record.body.r_b_exponents |= 1 << 7;
783 }
784
785 // todo fill out rest of units
786 if (bSigned)
787 {
788 record.body.sensor_units_1 = 1 << 7;
789 }
790
791 // populate sensor name from path
792 std::string name;
793 size_t nameStart = path.rfind("/");
794 if (nameStart != std::string::npos)
795 {
796 name = path.substr(nameStart + 1, std::string::npos - nameStart);
797 }
798
799 std::replace(name.begin(), name.end(), '_', ' ');
800 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
801 {
802 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
803 }
804 record.body.id_string_info = name.size();
805 std::strncpy(record.body.id_string, name.c_str(),
806 sizeof(record.body.id_string));
807
808 if (sizeof(get_sdr::SensorDataFullRecord) <
809 (req->offset + req->bytesToRead))
810 {
811 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
812 }
813
Patrick Williams2405ae92023-05-10 07:50:09 -0500814 *dataLen = 2 +
815 req->bytesToRead; // bytesToRead + MSB and LSB of next record id
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800816
Vijay Khemka63c99be2020-05-27 19:14:35 -0700817 std::memcpy(&resp->record_data, (char*)&record + req->offset,
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800818 req->bytesToRead);
819
820 return IPMI_CC_OK;
821}
822
Vijay Khemka63c99be2020-05-27 19:14:35 -0700823static int getSensorConnectionByName(std::string& name, std::string& connection,
824 std::string& path)
Vijay Khemka427b2762019-12-12 12:49:25 -0800825{
Potin Lai5a7a04d2024-01-10 15:25:21 +0800826 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Vijay Khemka427b2762019-12-12 12:49:25 -0800827 {
828 return -1;
829 }
830
Vijay Khemka63c99be2020-05-27 19:14:35 -0700831 for (const auto& sensor : sensorTree)
Vijay Khemka427b2762019-12-12 12:49:25 -0800832 {
833 path = sensor.first;
834 if (path.find(name) != std::string::npos)
835 {
836 connection = sensor.second.begin()->first;
837 return 0;
838 }
839 }
840 return -1;
841}
842
Delphine CC Chiu2ca4aa02023-02-01 16:30:18 +0800843int getSensorThreshold(std::string& name, std::string& thresholdStr)
844{
845 std::string connection;
846 std::string path;
847 int ret = -1;
848 thresholdStr = "";
849
850 ret = getSensorConnectionByName(name, connection, path);
851 if (ret < 0)
852 {
853 return ret;
854 }
855
856 SensorMap sensorMap;
857 if (!getSensorMap(connection, path, sensorMap))
858 {
859 return ret;
860 }
861
862 // Iterate threshold interfaces with priority order
863 for (auto& interface : thresholdCheckedOrder)
864 {
865 auto interfaceProperty = alarmProperties.find(interface);
866 if (interfaceProperty == alarmProperties.end())
867 {
868 continue;
869 }
870
871 auto propertyValue = interfaceProperty->second;
872
873 // Checks threshold properties value in sensorMap
874 auto thresholdInterfaceSensorMap = sensorMap.find(interface);
875
876 // Ignore if interface not set
877 if (thresholdInterfaceSensorMap == sensorMap.end())
878 {
879 continue;
880 }
881
882 auto& thresholdMap = thresholdInterfaceSensorMap->second;
883
884 auto& propertyAlarmHigh = propertyValue.at(AlarmType::high);
885 auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name);
886 if (alarmHigh != thresholdMap.end())
887 {
888 if (std::get<bool>(alarmHigh->second))
889 {
890 thresholdStr = propertyAlarmHigh.threshold;
891 break;
892 }
893 }
894
895 auto& propertyAlarmLow = propertyValue.at(AlarmType::low);
896 auto alarmLow = thresholdMap.find(propertyAlarmLow.name);
897 if (alarmLow != thresholdMap.end())
898 {
899 if (std::get<bool>(alarmLow->second))
900 {
901 thresholdStr = propertyAlarmLow.threshold;
902 break;
903 }
904 }
905 }
906
907 return 0;
908}
909
Vijay Khemka63c99be2020-05-27 19:14:35 -0700910int getSensorValue(std::string& name, double& val)
Vijay Khemka427b2762019-12-12 12:49:25 -0800911{
912 std::string connection;
913 std::string path;
914 int ret = -1;
915
916 ret = getSensorConnectionByName(name, connection, path);
917 if (ret < 0)
918 {
919 return ret;
920 }
921
922 SensorMap sensorMap;
923 if (!getSensorMap(connection, path, sensorMap))
924 {
925 return ret;
926 }
927 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
928
929 if (sensorObject == sensorMap.end() ||
930 sensorObject->second.find("Value") == sensorObject->second.end())
931 {
932 return ret;
933 }
Vijay Khemka63c99be2020-05-27 19:14:35 -0700934 auto& valueVariant = sensorObject->second["Value"];
Vijay Khemka427b2762019-12-12 12:49:25 -0800935 val = std::visit(VariantToDoubleVisitor(), valueVariant);
936
937 return 0;
938}
939
Vijay Khemka63c99be2020-05-27 19:14:35 -0700940const static boost::container::flat_map<const char*, std::string, CmpStr>
Vijay Khemka58bd5d82019-12-13 11:05:56 -0800941 sensorUnitStr{{{"temperature", "C"},
942 {"voltage", "V"},
943 {"current", "mA"},
944 {"fan_tach", "RPM"},
945 {"fan_pwm", "RPM"},
946 {"power", "W"}}};
947
Vijay Khemka63c99be2020-05-27 19:14:35 -0700948int getSensorUnit(std::string& name, std::string& unit)
Vijay Khemka58bd5d82019-12-13 11:05:56 -0800949{
950 std::string connection;
951 std::string path;
952 int ret = -1;
953
954 ret = getSensorConnectionByName(name, connection, path);
955 if (ret < 0)
956 {
957 return ret;
958 }
959
960 std::string sensorTypeStr = getSensorTypeStringFromPath(path);
961 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
962 if (findSensor != sensorUnitStr.end())
963 {
964 unit = findSensor->second;
965 return 0;
966 }
967 else
968 return -1;
969}
970
Patrick Williams010dee02024-08-16 15:19:44 -0400971ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(
972 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
973 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemka17cf1342020-11-02 14:44:42 -0800974{
975 if (*dataLen != 1)
976 {
977 *dataLen = 0;
978 return IPMI_CC_REQ_DATA_LEN_INVALID;
979 }
980 *dataLen = 0; // default to 0 in case of an error
981
982 uint8_t reqDev = *(static_cast<uint8_t*>(request));
983 if (reqDev == 0xFF)
984 {
985 return IPMI_CC_INVALID_FIELD_REQUEST;
986 }
987 ipmi_ret_t status = replaceCacheFru(reqDev);
988
989 if (status != IPMI_CC_OK)
990 {
991 return status;
992 }
993
994 GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response);
995 respPtr->inventorySizeLSB = fruCache.size() & 0xFF;
996 respPtr->inventorySizeMSB = fruCache.size() >> 8;
997 respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte);
998
999 *dataLen = sizeof(GetFRUAreaResp);
1000 return IPMI_CC_OK;
1001}
1002
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001003void registerStorageFunctions()
1004{
Vijay Khemka17cf1342020-11-02 14:44:42 -08001005 // <Get FRU Inventory Area Info>
1006 ipmiPrintAndRegister(
1007 NETFUN_STORAGE,
1008 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo),
1009 NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR);
1010
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001011 // <READ FRU Data>
1012 ipmiPrintAndRegister(
1013 NETFUN_STORAGE,
1014 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1015 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
1016
1017 // <WRITE FRU Data>
1018 ipmiPrintAndRegister(
1019 NETFUN_STORAGE,
1020 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1021 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1022
1023 // <Reserve SDR Repo>
1024 ipmiPrintAndRegister(
1025 NETFUN_STORAGE,
1026 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1027 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1028
1029 // <Get Sdr>
1030 ipmiPrintAndRegister(
1031 NETFUN_STORAGE,
1032 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1033 ipmiStorageGetSDR, PRIVILEGE_USER);
1034 return;
1035}
1036} // namespace storage
1037} // namespace ipmi