blob: 79aff85b09a58a5fa268fd52e1f977500044c438 [file] [log] [blame]
Tony Lee84d430c2019-06-13 15:26:15 +08001#include "nvme_manager.hpp"
2
Patrick Williams7da98582023-05-10 07:50:46 -05003#include "i2c.h"
4
Tony Lee6c595012019-06-19 10:54:59 +08005#include "smbus.hpp"
6
Tony Lee6c595012019-06-19 10:54:59 +08007#include <nlohmann/json.hpp>
Tony Lee84d430c2019-06-13 15:26:15 +08008#include <phosphor-logging/elog-errors.hpp>
9#include <phosphor-logging/log.hpp>
Tony Lee89659212019-06-21 17:34:14 +080010#include <sdbusplus/message.hpp>
Tony Lee89659212019-06-21 17:34:14 +080011#include <xyz/openbmc_project/Led/Physical/server.hpp>
Tony Lee84d430c2019-06-13 15:26:15 +080012
Patrick Williams7da98582023-05-10 07:50:46 -050013#include <filesystem>
14#include <map>
15#include <sstream>
16#include <string>
Tony Lee84d430c2019-06-13 15:26:15 +080017#define MONITOR_INTERVAL_SECONDS 1
Potin Lai6e149e92022-11-23 10:04:05 +080018#define MAX_SMBUS_ERROR_RETRY 0
Tony Lee6c595012019-06-19 10:54:59 +080019#define NVME_SSD_SLAVE_ADDRESS 0x6a
George Hung92a15ba2020-09-14 17:14:52 +080020#define NVME_SSD_VPD_SLAVE_ADDRESS 0x53
Tony Lee6c595012019-06-19 10:54:59 +080021#define GPIO_BASE_PATH "/sys/class/gpio/gpio"
22#define IS_PRESENT "0"
23#define POWERGD "1"
Tony Lee89659212019-06-21 17:34:14 +080024#define NOWARNING_STRING "ff"
Tony Lee6c595012019-06-19 10:54:59 +080025
26static constexpr auto configFile = "/etc/nvme/nvme_config.json";
27static constexpr auto delay = std::chrono::milliseconds{100};
28using Json = nlohmann::json;
29
30static constexpr const uint8_t COMMAND_CODE_0 = 0;
31static constexpr const uint8_t COMMAND_CODE_8 = 8;
George Hung92a15ba2020-09-14 17:14:52 +080032static constexpr const uint8_t CODE_0_LENGTH = 8;
33static constexpr const uint8_t CODE_8_LENGTH = 23;
Tony Lee6c595012019-06-19 10:54:59 +080034
Tony Lee89659212019-06-21 17:34:14 +080035static constexpr int CapacityFaultMask = 1;
36static constexpr int temperatureFaultMask = 1 << 1;
37static constexpr int DegradesFaultMask = 1 << 2;
38static constexpr int MediaFaultMask = 1 << 3;
39static constexpr int BackupDeviceFaultMask = 1 << 4;
Joseph.Fu097562e2024-04-16 14:40:49 +080040static constexpr int DriveNotReadyMask = 1 << 6;
Tony Lee89659212019-06-21 17:34:14 +080041static constexpr int NOWARNING = 255;
42
Tony Lee6c595012019-06-19 10:54:59 +080043static constexpr int SERIALNUMBER_START_INDEX = 3;
44static constexpr int SERIALNUMBER_END_INDEX = 23;
George Hung92a15ba2020-09-14 17:14:52 +080045static constexpr int MODELNUMBER_START_INDEX = 46;
46static constexpr int MODELNUMBER_END_INDEX = 85;
Tony Lee6c595012019-06-19 10:54:59 +080047
48static constexpr const int TEMPERATURE_SENSOR_FAILURE = 0x81;
49
Brandon Kim8353c5f2022-07-08 17:09:23 -070050static std::map<std::string, std::string> map_vendor = {
Munawar Hussain0a0ba602025-06-17 10:11:25 +000051 {"80 86", "Intel"},
52 {"1e f", "Kioxia"},
53 {"14 4d", "Samsung"},
54 {"1b 96", "Sandisk"}};
George Hung69b96182020-08-20 17:19:56 +080055
Tony Lee6c595012019-06-19 10:54:59 +080056namespace fs = std::filesystem;
57
Tony Lee84d430c2019-06-13 15:26:15 +080058namespace phosphor
59{
60namespace nvme
61{
62
63using namespace std;
64using namespace phosphor::logging;
65
Tony Lee89659212019-06-21 17:34:14 +080066void Nvme::setNvmeInventoryProperties(
Chanh Nguyene528b0a2021-07-14 16:57:57 +070067 NVMeConfig& config, bool present,
68 const phosphor::nvme::Nvme::NVMeData& nvmeData,
Tony Lee89659212019-06-21 17:34:14 +080069 const std::string& inventoryPath)
70{
Chanh Nguyene528b0a2021-07-14 16:57:57 +070071 static std::unordered_map<int, std::string> preSerial;
72 static std::unordered_map<int, std::string> preSmartWarning;
73 static std::unordered_map<int, std::string> preStatusFlags;
Tony Lee89659212019-06-21 17:34:14 +080074
Chanh Nguyene528b0a2021-07-14 16:57:57 +070075 if (preSerial[config.busID].compare(nvmeData.serialNumber) != 0)
76 {
77 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
78 ITEM_IFACE, "Present", present);
79 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
80 ASSET_IFACE, "Manufacturer",
81 nvmeData.vendor);
82 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
83 ASSET_IFACE, "SerialNumber",
84 nvmeData.serialNumber);
85 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
86 ASSET_IFACE, "Model",
87 nvmeData.modelNumber);
88 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
89 NVME_STATUS_IFACE, "DriveLifeUsed",
90 nvmeData.driveLifeUsed);
91 preSerial[config.busID] = nvmeData.serialNumber;
92 }
Tony Lee89659212019-06-21 17:34:14 +080093
Chanh Nguyene528b0a2021-07-14 16:57:57 +070094 if (preStatusFlags[config.busID].compare(nvmeData.statusFlags) != 0)
95 {
96 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
97 NVME_STATUS_IFACE, "StatusFlags",
98 nvmeData.statusFlags);
99 preStatusFlags[config.busID] = nvmeData.statusFlags;
100 }
Tony Lee89659212019-06-21 17:34:14 +0800101
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700102 if (preSmartWarning[config.busID].compare(nvmeData.smartWarnings) != 0)
103 {
104 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
105 NVME_STATUS_IFACE, "SmartWarnings",
106 nvmeData.smartWarnings);
107 auto smartWarning = (!nvmeData.smartWarnings.empty())
Jayanth Othayothe56444a2024-12-19 08:35:11 -0600108 ? std::stoi(nvmeData.smartWarnings, nullptr, 16)
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700109 : NOWARNING;
Tony Lee89659212019-06-21 17:34:14 +0800110
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700111 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
112 NVME_STATUS_IFACE, "CapacityFault",
113 !(smartWarning & CapacityFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800114
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700115 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
116 NVME_STATUS_IFACE, "TemperatureFault",
117 !(smartWarning & temperatureFaultMask));
Tony Lee89659212019-06-21 17:34:14 +0800118
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700119 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
120 NVME_STATUS_IFACE, "DegradesFault",
121 !(smartWarning & DegradesFaultMask));
122
123 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
124 NVME_STATUS_IFACE, "MediaFault",
125 !(smartWarning & MediaFaultMask));
126
127 util::SDBusPlus::setProperty(bus, INVENTORY_BUSNAME, inventoryPath,
128 NVME_STATUS_IFACE, "BackupDeviceFault",
129 !(smartWarning & BackupDeviceFaultMask));
130 preSmartWarning[config.busID] = nvmeData.smartWarnings;
131 }
Tony Lee89659212019-06-21 17:34:14 +0800132}
133
134void Nvme::setFaultLED(const std::string& locateLedGroupPath,
135 const std::string& faultLedGroupPath, bool request)
136{
137 if (locateLedGroupPath.empty() || faultLedGroupPath.empty())
138 {
139 return;
140 }
141
142 // Before toggle LED, check whether is Identify or not.
143 if (!getLEDGroupState(locateLedGroupPath))
144 {
Brandon Kimfdffe5c2021-03-30 15:07:59 -0700145 if (getLEDGroupState(faultLedGroupPath) != request)
146 {
147 util::SDBusPlus::setProperty(bus, LED_GROUP_BUSNAME,
148 faultLedGroupPath, LED_GROUP_IFACE,
149 "Asserted", request);
150 }
Tony Lee89659212019-06-21 17:34:14 +0800151 }
152}
153
154void Nvme::setLocateLED(const std::string& locateLedGroupPath,
155 const std::string& locateLedBusName,
156 const std::string& locateLedPath, bool isPresent)
157{
158 if (locateLedGroupPath.empty() || locateLedBusName.empty() ||
159 locateLedPath.empty())
160 {
161 return;
162 }
163
164 namespace server = sdbusplus::xyz::openbmc_project::Led::server;
165
166 if (!getLEDGroupState(locateLedGroupPath))
167 {
168 if (isPresent)
169 util::SDBusPlus::setProperty(
170 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
171 "State",
172 server::convertForMessage(server::Physical::Action::On));
173 else
174 util::SDBusPlus::setProperty(
175 bus, locateLedBusName, locateLedPath, LED_CONTROLLER_IFACE,
176 "State",
177 server::convertForMessage(server::Physical::Action::Off));
178 }
179}
180
181bool Nvme::getLEDGroupState(const std::string& ledPath)
182{
183 auto asserted = util::SDBusPlus::getProperty<bool>(
184 bus, LED_GROUP_BUSNAME, ledPath, LED_GROUP_IFACE, "Asserted");
185
186 return asserted;
187}
188
189void Nvme::setLEDsStatus(const phosphor::nvme::Nvme::NVMeConfig& config,
190 bool success,
191 const phosphor::nvme::Nvme::NVMeData& nvmeData)
192{
Tony Lee89659212019-06-21 17:34:14 +0800193 if (success)
194 {
195 if (!nvmeData.smartWarnings.empty())
196 {
197 auto request =
198 (strcmp(nvmeData.smartWarnings.c_str(), NOWARNING_STRING) == 0)
199 ? false
200 : true;
201
202 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
203 request);
204 setLocateLED(config.locateLedGroupPath,
205 config.locateLedControllerBusName,
206 config.locateLedControllerPath, !request);
207 }
208 isError[config.index] = false;
209 }
210 else
211 {
212 if (isError[config.index] != true)
213 {
214 // Drive is present but can not get data, turn on fault LED.
215 log<level::ERR>("Drive status is good but can not get data.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700216 entry("OBJ_PATH=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800217 isError[config.index] = true;
218 }
219
220 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath, true);
221 setLocateLED(config.locateLedGroupPath,
222 config.locateLedControllerBusName,
223 config.locateLedControllerPath, false);
224 }
225}
226
Tony Lee6c595012019-06-19 10:54:59 +0800227std::string intToHex(int input)
228{
229 std::stringstream tmp;
230 tmp << std::hex << input;
231
232 return tmp.str();
233}
234
235/** @brief Get NVMe info over smbus */
George Hung5e23bcd2021-07-01 12:16:11 +0800236bool Nvme::getNVMeInfobyBusID(int busID,
237 phosphor::nvme::Nvme::NVMeData& nvmeData)
Tony Lee6c595012019-06-19 10:54:59 +0800238{
239 nvmeData.present = true;
240 nvmeData.vendor = "";
241 nvmeData.serialNumber = "";
George Hung831f2042021-05-19 17:02:29 +0800242 nvmeData.modelNumber = "";
Tony Lee6c595012019-06-19 10:54:59 +0800243 nvmeData.smartWarnings = "";
244 nvmeData.statusFlags = "";
245 nvmeData.driveLifeUsed = "";
Brandon Kimd5838d12021-05-19 12:51:55 -0700246 nvmeData.sensorValue = static_cast<int8_t>(TEMPERATURE_SENSOR_FAILURE);
George Hung831f2042021-05-19 17:02:29 +0800247 nvmeData.wcTemp = 0;
Tony Lee6c595012019-06-19 10:54:59 +0800248
249 phosphor::smbus::Smbus smbus;
250
251 unsigned char rsp_data_command_0[I2C_DATA_MAX] = {0};
252 unsigned char rsp_data_command_8[I2C_DATA_MAX] = {0};
253
254 uint8_t tx_data = COMMAND_CODE_0;
255
256 auto init = smbus.smbusInit(busID);
257
Tony Lee6c595012019-06-19 10:54:59 +0800258 if (init == -1)
259 {
260 if (isErrorSmbus[busID] != true)
261 {
262 log<level::ERR>("smbusInit fail!");
263 isErrorSmbus[busID] = true;
264 }
265
266 nvmeData.present = false;
267
268 return nvmeData.present;
269 }
270
Patrick Williamsf89f1532024-08-16 15:20:48 -0400271 auto res_int = smbus.SendSmbusRWCmdRAW(
272 busID, NVME_SSD_SLAVE_ADDRESS, &tx_data, sizeof(tx_data),
273 rsp_data_command_0, CODE_0_LENGTH);
Tony Lee6c595012019-06-19 10:54:59 +0800274
275 if (res_int < 0)
276 {
277 if (isErrorSmbus[busID] != true)
278 {
279 log<level::ERR>("Send command code 0 fail!");
280 isErrorSmbus[busID] = true;
281 }
282
283 smbus.smbusClose(busID);
284 nvmeData.present = false;
285 return nvmeData.present;
286 }
287
Joseph.Fu097562e2024-04-16 14:40:49 +0800288 if (rsp_data_command_0[1] & DriveNotReadyMask)
289 {
290 if (isErrorSmbus[busID] != true)
291 {
292 log<level::ERR>("Drive not ready!");
293 isErrorSmbus[busID] = true;
294 }
295 smbus.smbusClose(busID);
296 return false;
297 }
298
George Hung92a15ba2020-09-14 17:14:52 +0800299 nvmeData.statusFlags = intToHex(rsp_data_command_0[1]);
300 nvmeData.smartWarnings = intToHex(rsp_data_command_0[2]);
301 nvmeData.driveLifeUsed = intToHex(rsp_data_command_0[4]);
George Hung93455332021-01-06 20:57:04 +0800302 nvmeData.sensorValue = static_cast<int8_t>(rsp_data_command_0[3]);
303 nvmeData.wcTemp = static_cast<int8_t>(rsp_data_command_0[5]);
George Hung92a15ba2020-09-14 17:14:52 +0800304
Tony Lee6c595012019-06-19 10:54:59 +0800305 tx_data = COMMAND_CODE_8;
306
George Hung92a15ba2020-09-14 17:14:52 +0800307 res_int = smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_SLAVE_ADDRESS, &tx_data,
308 sizeof(tx_data), rsp_data_command_8,
309 CODE_8_LENGTH);
Tony Lee6c595012019-06-19 10:54:59 +0800310
311 if (res_int < 0)
312 {
313 if (isErrorSmbus[busID] != true)
314 {
315 log<level::ERR>("Send command code 8 fail!");
316 isErrorSmbus[busID] = true;
317 }
318
319 smbus.smbusClose(busID);
320 nvmeData.present = false;
321 return nvmeData.present;
322 }
323
Patrick Williamsf89f1532024-08-16 15:20:48 -0400324 nvmeData.vendor =
325 intToHex(rsp_data_command_8[1]) + " " + intToHex(rsp_data_command_8[2]);
Tony Lee6c595012019-06-19 10:54:59 +0800326
George Hung69b96182020-08-20 17:19:56 +0800327 for (auto iter = map_vendor.begin(); iter != map_vendor.end(); iter++)
328 {
329 if (iter->first == nvmeData.vendor)
330 {
331 nvmeData.vendor = iter->second;
332 break;
333 }
334 }
335
Tony Lee6c595012019-06-19 10:54:59 +0800336 for (int offset = SERIALNUMBER_START_INDEX; offset < SERIALNUMBER_END_INDEX;
337 offset++)
338 {
George Hung31c3a2f2021-07-29 19:15:14 +0800339 // Only accept digits/letters/punctuation characters.
340 if (rsp_data_command_8[offset] >= '!' &&
341 rsp_data_command_8[offset] <= '~')
George Hung69b96182020-08-20 17:19:56 +0800342 nvmeData.serialNumber +=
343 static_cast<char>(rsp_data_command_8[offset]);
Tony Lee6c595012019-06-19 10:54:59 +0800344 }
345
Munawar Hussain0a0ba602025-06-17 10:11:25 +0000346 if ((nvmeData.vendor == "Samsung") || (nvmeData.vendor == "Kioxia") ||
347 (nvmeData.vendor == "Sandisk"))
George Hung92a15ba2020-09-14 17:14:52 +0800348 {
349 unsigned char rsp_data_vpd[I2C_DATA_MAX] = {0};
350 const int rx_len = (MODELNUMBER_END_INDEX - MODELNUMBER_START_INDEX);
351 tx_data = MODELNUMBER_START_INDEX;
352
353 auto res_int =
354 smbus.SendSmbusRWCmdRAW(busID, NVME_SSD_VPD_SLAVE_ADDRESS, &tx_data,
355 sizeof(tx_data), rsp_data_vpd, rx_len);
356
357 if (res_int < 0)
358 {
359 if (isErrorSmbus[busID] != true)
360 {
361 log<level::ERR>("Send command read VPD fail!");
362 isErrorSmbus[busID] = true;
363 }
364
365 smbus.smbusClose(busID);
366 nvmeData.present = false;
367 return nvmeData.present;
368 }
369
370 for (int i = 0; i < rx_len; i++)
371 {
George Hung31c3a2f2021-07-29 19:15:14 +0800372 // Only accept digits/letters/punctuation characters.
373 if ((rsp_data_vpd[i] >= '!' && rsp_data_vpd[i] <= '~'))
George Hung92a15ba2020-09-14 17:14:52 +0800374 nvmeData.modelNumber += static_cast<char>(rsp_data_vpd[i]);
375 }
376
377 if (nvmeData.modelNumber.substr(0, nvmeData.vendor.size()) == "SAMSUNG")
378 nvmeData.modelNumber.erase(0, nvmeData.vendor.size());
379 }
Tony Lee6c595012019-06-19 10:54:59 +0800380
381 smbus.smbusClose(busID);
382
383 isErrorSmbus[busID] = false;
384
385 return nvmeData.present;
386}
387
Tony Lee84d430c2019-06-13 15:26:15 +0800388void Nvme::run()
389{
Tony Lee89659212019-06-21 17:34:14 +0800390 init();
391
Tony Lee84d430c2019-06-13 15:26:15 +0800392 std::function<void()> callback(std::bind(&Nvme::read, this));
393 try
394 {
Potin Laibcae0872022-10-20 15:29:17 +0800395 u_int64_t interval = monitorIntervalSec * 1000000;
Tony Lee84d430c2019-06-13 15:26:15 +0800396 _timer.restart(std::chrono::microseconds(interval));
397 }
398 catch (const std::exception& e)
399 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700400 log<level::ERR>("Error in polling loop. "), entry("ERROR=%s", e.what());
Tony Lee84d430c2019-06-13 15:26:15 +0800401 }
402}
403
Tony Lee6c595012019-06-19 10:54:59 +0800404/** @brief Parsing NVMe config JSON file */
405Json parseSensorConfig()
Tony Lee84d430c2019-06-13 15:26:15 +0800406{
Tony Lee6c595012019-06-19 10:54:59 +0800407 std::ifstream jsonFile(configFile);
408 if (!jsonFile.is_open())
409 {
410 log<level::ERR>("NVMe config JSON file not found");
411 }
412
413 auto data = Json::parse(jsonFile, nullptr, false);
414 if (data.is_discarded())
415 {
416 log<level::ERR>("NVMe config readings JSON parser failure");
417 }
418
419 return data;
Tony Lee84d430c2019-06-13 15:26:15 +0800420}
421
Tony Lee6c595012019-06-19 10:54:59 +0800422/** @brief Obtain the initial configuration value of NVMe */
423std::vector<phosphor::nvme::Nvme::NVMeConfig> Nvme::getNvmeConfig()
424{
Tony Lee6c595012019-06-19 10:54:59 +0800425 phosphor::nvme::Nvme::NVMeConfig nvmeConfig;
426 std::vector<phosphor::nvme::Nvme::NVMeConfig> nvmeConfigs;
427 int8_t criticalHigh = 0;
428 int8_t criticalLow = 0;
429 int8_t maxValue = 0;
430 int8_t minValue = 0;
431 int8_t warningHigh = 0;
432 int8_t warningLow = 0;
433
434 try
435 {
436 auto data = parseSensorConfig();
437 static const std::vector<Json> empty{};
438 std::vector<Json> readings = data.value("config", empty);
439 std::vector<Json> thresholds = data.value("threshold", empty);
Patrick Williamsf89f1532024-08-16 15:20:48 -0400440 monitorIntervalSec =
441 data.value("monitorIntervalSec", MONITOR_INTERVAL_SECONDS);
442 maxSmbusErrorRetry =
443 data.value("maxSmbusErrorRetry", MAX_SMBUS_ERROR_RETRY);
Potin Laibcae0872022-10-20 15:29:17 +0800444
Tony Lee6c595012019-06-19 10:54:59 +0800445 if (!thresholds.empty())
446 {
447 for (const auto& instance : thresholds)
448 {
449 criticalHigh = instance.value("criticalHigh", 0);
450 criticalLow = instance.value("criticalLow", 0);
451 maxValue = instance.value("maxValue", 0);
452 minValue = instance.value("minValue", 0);
453 warningHigh = instance.value("warningHigh", 0);
454 warningLow = instance.value("warningLow", 0);
455 }
456 }
457 else
458 {
459 log<level::ERR>(
Manojkiran Eda3b2e5a92024-06-17 14:47:39 +0530460 "Invalid NVMe config file, thresholds doesn't exist");
Tony Lee6c595012019-06-19 10:54:59 +0800461 }
462
463 if (!readings.empty())
464 {
465 for (const auto& instance : readings)
466 {
467 uint8_t index = instance.value("NVMeDriveIndex", 0);
468 uint8_t busID = instance.value("NVMeDriveBusID", 0);
Tony Lee89659212019-06-21 17:34:14 +0800469 std::string faultLedGroupPath =
470 instance.value("NVMeDriveFaultLEDGroupPath", "");
471 std::string locateLedGroupPath =
472 instance.value("NVMeDriveLocateLEDGroupPath", "");
George Hung873b5b32020-06-20 14:56:15 +0800473 uint16_t presentPin = instance.value("NVMeDrivePresentPin", 0);
474 uint16_t pwrGoodPin = instance.value("NVMeDrivePwrGoodPin", 0);
Tony Lee89659212019-06-21 17:34:14 +0800475 std::string locateLedControllerBusName =
476 instance.value("NVMeDriveLocateLEDControllerBusName", "");
477 std::string locateLedControllerPath =
478 instance.value("NVMeDriveLocateLEDControllerPath", "");
Tony Lee6c595012019-06-19 10:54:59 +0800479
480 nvmeConfig.index = std::to_string(index);
481 nvmeConfig.busID = busID;
Tony Lee89659212019-06-21 17:34:14 +0800482 nvmeConfig.faultLedGroupPath = faultLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800483 nvmeConfig.presentPin = presentPin;
484 nvmeConfig.pwrGoodPin = pwrGoodPin;
Tony Lee89659212019-06-21 17:34:14 +0800485 nvmeConfig.locateLedControllerBusName =
486 locateLedControllerBusName;
487 nvmeConfig.locateLedControllerPath = locateLedControllerPath;
488 nvmeConfig.locateLedGroupPath = locateLedGroupPath;
Tony Lee6c595012019-06-19 10:54:59 +0800489 nvmeConfig.criticalHigh = criticalHigh;
490 nvmeConfig.criticalLow = criticalLow;
491 nvmeConfig.warningHigh = warningHigh;
492 nvmeConfig.warningLow = warningLow;
493 nvmeConfig.maxValue = maxValue;
494 nvmeConfig.minValue = minValue;
495 nvmeConfigs.push_back(nvmeConfig);
496 }
497 }
498 else
499 {
Manojkiran Eda3b2e5a92024-06-17 14:47:39 +0530500 log<level::ERR>("Invalid NVMe config file, config doesn't exist");
Tony Lee6c595012019-06-19 10:54:59 +0800501 }
502 }
503 catch (const Json::exception& e)
504 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700505 log<level::ERR>("Json Exception caught."), entry("MSG=%s", e.what());
Tony Lee6c595012019-06-19 10:54:59 +0800506 }
507
508 return nvmeConfigs;
509}
510
511std::string Nvme::getGPIOValueOfNvme(const std::string& fullPath)
512{
513 std::string val;
514 std::ifstream ifs;
515 auto retries = 3;
516
517 while (retries != 0)
518 {
519 try
520 {
521 if (!ifs.is_open())
522 ifs.open(fullPath);
523 ifs.clear();
524 ifs.seekg(0);
525 ifs >> val;
526 }
527 catch (const std::exception& e)
528 {
529 --retries;
530 std::this_thread::sleep_for(delay);
531 log<level::ERR>("Can not open gpio path.",
Brandon Kim9b771e22020-10-05 12:02:01 -0700532 entry("MSG=%s", e.what()));
Tony Lee6c595012019-06-19 10:54:59 +0800533 continue;
534 }
535 break;
536 }
537
538 ifs.close();
539 return val;
540}
541
Tony Lee89659212019-06-21 17:34:14 +0800542void Nvme::createNVMeInventory()
543{
Patrick Williams05eedaa2020-05-13 17:58:42 -0500544 using Properties = std::map<std::string, std::variant<std::string, bool>>;
Tony Lee89659212019-06-21 17:34:14 +0800545 using Interfaces = std::map<std::string, Properties>;
546
547 std::string inventoryPath;
548 std::map<sdbusplus::message::object_path, Interfaces> obj;
549
George Hung831f2042021-05-19 17:02:29 +0800550 for (const auto& config : configs)
Tony Lee89659212019-06-21 17:34:14 +0800551 {
552 inventoryPath = "/system/chassis/motherboard/nvme" + config.index;
553
554 obj = {{
555 inventoryPath,
556 {{ITEM_IFACE, {}}, {NVME_STATUS_IFACE, {}}, {ASSET_IFACE, {}}},
557 }};
558 util::SDBusPlus::CallMethod(bus, INVENTORY_BUSNAME, INVENTORY_NAMESPACE,
559 INVENTORY_MANAGER_IFACE, "Notify", obj);
560 }
561}
562
563void Nvme::init()
564{
565 createNVMeInventory();
566}
567
Potin Lai03144732023-02-14 22:20:38 +0800568void Nvme::readNvmeData(NVMeConfig& config, bool isPwrGood)
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700569{
570 std::string inventoryPath = NVME_INVENTORY_PATH + config.index;
571 NVMeData nvmeData;
572
573 // get NVMe information through i2c by busID.
Potin Lai03144732023-02-14 22:20:38 +0800574 bool success;
575
576 // skip reading nvme data when power good is false
577 if (isPwrGood)
578 {
579 success = getNVMeInfobyBusID(config.busID, nvmeData);
580 }
581 else
582 {
583 nvmeData.present = false;
584 nvmeData.sensorValue = static_cast<int8_t>(TEMPERATURE_SENSOR_FAILURE);
585 success = false;
586 // Skip retry below when isPwrGood is false because smbus is going to
587 // fail
588 nvmeSmbusErrCnt[config.busID] = maxSmbusErrorRetry;
589 }
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700590
Potin Lai6e149e92022-11-23 10:04:05 +0800591 if (success)
592 {
593 nvmeSmbusErrCnt[config.busID] = 0;
594 }
595 else
596 {
597 if (nvmeSmbusErrCnt[config.busID] < maxSmbusErrorRetry)
598 {
Potin Lai03144732023-02-14 22:20:38 +0800599 // Return early so that we retry
Potin Lai6e149e92022-11-23 10:04:05 +0800600 nvmeSmbusErrCnt[config.busID]++;
601 log<level::INFO>("getNVMeInfobyBusID failed, retry...",
602 entry("INDEX=%s", config.index.c_str()),
603 entry("ERRCNT=%u", nvmeSmbusErrCnt[config.busID]));
604 return;
605 }
606 }
607
Potin Lai03144732023-02-14 22:20:38 +0800608 // find NvmeSSD object by index
609 auto iter = nvmes.find(config.index);
610
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700611 // can not find. create dbus
612 if (iter == nvmes.end())
613 {
Brandon Kim9b771e22020-10-05 12:02:01 -0700614 log<level::INFO>("SSD plug.", entry("INDEX=%s", config.index.c_str()));
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700615
616 std::string objPath = NVME_OBJ_PATH + config.index;
617 auto nvmeSSD =
618 std::make_shared<phosphor::nvme::NvmeSSD>(bus, objPath.c_str());
619 nvmes.emplace(config.index, nvmeSSD);
620
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700621 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700622 nvmeSSD->setSensorValueToDbus(nvmeData.sensorValue);
George Hung93455332021-01-06 20:57:04 +0800623 if (nvmeData.wcTemp != 0)
624 {
625 config.criticalHigh = nvmeData.wcTemp;
626 config.warningHigh = nvmeData.wcTemp;
627 }
George Hung831f2042021-05-19 17:02:29 +0800628 nvmeSSD->setSensorMaxMin(config.maxValue, config.minValue);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700629 nvmeSSD->setSensorThreshold(config.criticalHigh, config.criticalLow,
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700630 config.warningHigh, config.warningLow);
631
Joseph.Fua7782722024-04-19 12:18:07 +0800632 if (success)
633 nvmeSSD->checkSensorThreshold();
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700634 setLEDsStatus(config, success, nvmeData);
635 }
636 else
637 {
Chanh Nguyene528b0a2021-07-14 16:57:57 +0700638 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700639 iter->second->setSensorValueToDbus(nvmeData.sensorValue);
George Hung831f2042021-05-19 17:02:29 +0800640 if (nvmeData.wcTemp != 0)
641 {
George Hungbe343992021-07-02 09:15:54 +0800642 config.criticalHigh = nvmeData.wcTemp;
643 config.warningHigh = nvmeData.wcTemp;
644
George Hung831f2042021-05-19 17:02:29 +0800645 iter->second->setSensorThreshold(
646 config.criticalHigh, config.criticalLow, config.warningHigh,
647 config.warningLow);
648 }
Joseph.Fua7782722024-04-19 12:18:07 +0800649 if (success)
650 iter->second->checkSensorThreshold();
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700651 setLEDsStatus(config, success, nvmeData);
652 }
653}
654
Tony Lee6c595012019-06-19 10:54:59 +0800655/** @brief Monitor NVMe drives every one second */
Tony Lee84d430c2019-06-13 15:26:15 +0800656void Nvme::read()
657{
Tony Lee6c595012019-06-19 10:54:59 +0800658 std::string devPresentPath;
659 std::string devPwrGoodPath;
Tony Lee89659212019-06-21 17:34:14 +0800660 std::string inventoryPath;
Tony Lee6c595012019-06-19 10:54:59 +0800661
Tony Lee6c595012019-06-19 10:54:59 +0800662 for (auto config : configs)
663 {
664 NVMeData nvmeData;
Tony Lee6c595012019-06-19 10:54:59 +0800665
Tony Lee89659212019-06-21 17:34:14 +0800666 inventoryPath = NVME_INVENTORY_PATH + config.index;
Patrick Williamsf89f1532024-08-16 15:20:48 -0400667 devPresentPath =
668 GPIO_BASE_PATH + std::to_string(config.presentPin) + "/value";
669 devPwrGoodPath =
670 GPIO_BASE_PATH + std::to_string(config.pwrGoodPin) + "/value";
Tony Lee89659212019-06-21 17:34:14 +0800671
Potin Laiaef8fa02022-09-21 19:10:30 +0800672 auto presentPinValStr = (config.presentPin)
673 ? getGPIOValueOfNvme(devPresentPath)
674 : IS_PRESENT;
675 auto pwrGoodPinValStr =
676 (config.pwrGoodPin) ? getGPIOValueOfNvme(devPwrGoodPath) : POWERGD;
Potin Lai76f455e2022-09-22 00:11:26 +0800677 const bool isPwrGood = (pwrGoodPinValStr == POWERGD);
Potin Laiaef8fa02022-09-21 19:10:30 +0800678
679 if (presentPinValStr != IS_PRESENT)
Tony Lee6c595012019-06-19 10:54:59 +0800680 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800681 // Drive not present, remove nvme d-bus path ,
682 // clean all properties in inventory
683 // and turn off fault and locate LED
Vijay Khemkae41b2e42020-04-21 09:23:10 -0700684
Potin Laiaef8fa02022-09-21 19:10:30 +0800685 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
686 false);
687 setLocateLED(config.locateLedGroupPath,
688 config.locateLedControllerBusName,
689 config.locateLedControllerPath, false);
690
691 nvmeData = NVMeData();
692 setNvmeInventoryProperties(config, false, nvmeData, inventoryPath);
693 nvmes.erase(config.index);
694 continue;
695 }
696
Potin Lai76f455e2022-09-22 00:11:26 +0800697 if (!isPwrGood)
Potin Laiaef8fa02022-09-21 19:10:30 +0800698 {
699 // IFDET should be used to provide the final say
700 // in SSD's presence - IFDET showing SSD is present
701 // but the power is off (if the drive is plugged in)
702 // is a valid state.
703
704 setFaultLED(config.locateLedGroupPath, config.faultLedGroupPath,
705 true);
706 setLocateLED(config.locateLedGroupPath,
707 config.locateLedControllerBusName,
708 config.locateLedControllerPath, false);
709
710 nvmeData = NVMeData();
711 setNvmeInventoryProperties(config, true, nvmeData, inventoryPath);
712
713 if (isErrorPower[config.index] != true)
Tony Lee6c595012019-06-19 10:54:59 +0800714 {
Potin Laiaef8fa02022-09-21 19:10:30 +0800715 log<level::ERR>(
716 "Present pin is true but power good pin is false.",
717 entry("INDEX=%s", config.index.c_str()));
718 log<level::ERR>("Erase SSD from map and d-bus.",
719 entry("INDEX=%s", config.index.c_str()));
Tony Lee89659212019-06-21 17:34:14 +0800720
Potin Laiaef8fa02022-09-21 19:10:30 +0800721 isErrorPower[config.index] = true;
Tony Lee6c595012019-06-19 10:54:59 +0800722 }
723 }
Potin Laiaef8fa02022-09-21 19:10:30 +0800724 else
725 {
726 isErrorPower[config.index] = false;
727 }
Tony Lee89659212019-06-21 17:34:14 +0800728
Potin Laiaef8fa02022-09-21 19:10:30 +0800729 // Keep reading to report the invalid temperature
730 // (To make thermal loop know that the sensor reading
731 // is invalid).
Potin Lai03144732023-02-14 22:20:38 +0800732 readNvmeData(config, isPwrGood);
Potin Lai6e149e92022-11-23 10:04:05 +0800733 if (nvmes.find(config.index) != nvmes.end())
734 {
735 nvmes.find(config.index)->second->setSensorAvailability(isPwrGood);
736 }
Tony Lee6c595012019-06-19 10:54:59 +0800737 }
Tony Lee84d430c2019-06-13 15:26:15 +0800738}
739} // namespace nvme
740} // namespace phosphor