1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2019 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu // http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu
17d7be555eSGeorge Liu #include "NVMeBasicContext.hpp"
18d7be555eSGeorge Liu #include "NVMeContext.hpp"
19d7be555eSGeorge Liu #include "NVMeSensor.hpp"
20d7be555eSGeorge Liu #include "Thresholds.hpp"
21d7be555eSGeorge Liu #include "Utils.hpp"
22d7be555eSGeorge Liu #include "VariantVisitors.hpp"
23d7be555eSGeorge Liu
24d7be555eSGeorge Liu #include <boost/asio/error.hpp>
25d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
26d7be555eSGeorge Liu #include <boost/asio/post.hpp>
27d7be555eSGeorge Liu #include <boost/asio/steady_timer.hpp>
28*7201be43SGeorge Liu #include <phosphor-logging/lg2.hpp>
29d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
30d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
31d7be555eSGeorge Liu #include <sdbusplus/bus.hpp>
32d7be555eSGeorge Liu #include <sdbusplus/bus/match.hpp>
33d7be555eSGeorge Liu #include <sdbusplus/message.hpp>
34d7be555eSGeorge Liu #include <sdbusplus/message/native_types.hpp>
35d7be555eSGeorge Liu
36d7be555eSGeorge Liu #include <algorithm>
37d7be555eSGeorge Liu #include <array>
38d7be555eSGeorge Liu #include <chrono>
39d7be555eSGeorge Liu #include <cstddef>
40d7be555eSGeorge Liu #include <cstdint>
41d7be555eSGeorge Liu #include <filesystem>
42d7be555eSGeorge Liu #include <functional>
43d7be555eSGeorge Liu #include <memory>
44d7be555eSGeorge Liu #include <optional>
45d7be555eSGeorge Liu #include <stdexcept>
46d7be555eSGeorge Liu #include <string>
47d7be555eSGeorge Liu #include <utility>
48d7be555eSGeorge Liu #include <variant>
49d7be555eSGeorge Liu #include <vector>
50d7be555eSGeorge Liu
51d7be555eSGeorge Liu static constexpr uint8_t nvmeMiDefaultSlaveAddr = 0x6A;
52d7be555eSGeorge Liu
53d7be555eSGeorge Liu static NVMEMap nvmeDeviceMap;
54d7be555eSGeorge Liu
getNVMEMap()55d7be555eSGeorge Liu NVMEMap& getNVMEMap()
56d7be555eSGeorge Liu {
57d7be555eSGeorge Liu return nvmeDeviceMap;
58d7be555eSGeorge Liu }
59d7be555eSGeorge Liu
extractBusNumber(const std::string & path,const SensorBaseConfigMap & properties)60d7be555eSGeorge Liu static std::optional<int> extractBusNumber(
61d7be555eSGeorge Liu const std::string& path, const SensorBaseConfigMap& properties)
62d7be555eSGeorge Liu {
63d7be555eSGeorge Liu auto findBus = properties.find("Bus");
64d7be555eSGeorge Liu if (findBus == properties.end())
65d7be555eSGeorge Liu {
66*7201be43SGeorge Liu lg2::error("could not determine bus number for '{PATH}'", "PATH", path);
67d7be555eSGeorge Liu return std::nullopt;
68d7be555eSGeorge Liu }
69d7be555eSGeorge Liu
70d7be555eSGeorge Liu return std::visit(VariantToIntVisitor(), findBus->second);
71d7be555eSGeorge Liu }
72d7be555eSGeorge Liu
extractSlaveAddr(const std::string & path,const SensorBaseConfigMap & properties)73d7be555eSGeorge Liu static uint8_t extractSlaveAddr(const std::string& path,
74d7be555eSGeorge Liu const SensorBaseConfigMap& properties)
75d7be555eSGeorge Liu {
76d7be555eSGeorge Liu auto findSlaveAddr = properties.find("Address");
77d7be555eSGeorge Liu if (findSlaveAddr == properties.end())
78d7be555eSGeorge Liu {
79*7201be43SGeorge Liu lg2::error(
80*7201be43SGeorge Liu "could not determine slave address for '{PATH} 'using default as "
81*7201be43SGeorge Liu "specified in nvme-mi",
82*7201be43SGeorge Liu "PATH", path);
83d7be555eSGeorge Liu return nvmeMiDefaultSlaveAddr;
84d7be555eSGeorge Liu }
85d7be555eSGeorge Liu
86d7be555eSGeorge Liu return std::visit(VariantToUnsignedIntVisitor(), findSlaveAddr->second);
87d7be555eSGeorge Liu }
88d7be555eSGeorge Liu
extractSensorName(const std::string & path,const SensorBaseConfigMap & properties)89d7be555eSGeorge Liu static std::optional<std::string> extractSensorName(
90d7be555eSGeorge Liu const std::string& path, const SensorBaseConfigMap& properties)
91d7be555eSGeorge Liu {
92d7be555eSGeorge Liu auto findSensorName = properties.find("Name");
93d7be555eSGeorge Liu if (findSensorName == properties.end())
94d7be555eSGeorge Liu {
95*7201be43SGeorge Liu lg2::error("could not determine configuration name for '{PATH}'",
96*7201be43SGeorge Liu "PATH", path);
97d7be555eSGeorge Liu return std::nullopt;
98d7be555eSGeorge Liu }
99d7be555eSGeorge Liu
100d7be555eSGeorge Liu return std::get<std::string>(findSensorName->second);
101d7be555eSGeorge Liu }
102d7be555eSGeorge Liu
deriveRootBusPath(int busNumber)103d7be555eSGeorge Liu static std::filesystem::path deriveRootBusPath(int busNumber)
104d7be555eSGeorge Liu {
105d7be555eSGeorge Liu return "/sys/bus/i2c/devices/i2c-" + std::to_string(busNumber) +
106d7be555eSGeorge Liu "/mux_device";
107d7be555eSGeorge Liu }
108d7be555eSGeorge Liu
deriveRootBus(std::optional<int> busNumber)109d7be555eSGeorge Liu static std::optional<int> deriveRootBus(std::optional<int> busNumber)
110d7be555eSGeorge Liu {
111d7be555eSGeorge Liu if (!busNumber)
112d7be555eSGeorge Liu {
113d7be555eSGeorge Liu return std::nullopt;
114d7be555eSGeorge Liu }
115d7be555eSGeorge Liu
116d7be555eSGeorge Liu std::filesystem::path muxPath = deriveRootBusPath(*busNumber);
117d7be555eSGeorge Liu
118d7be555eSGeorge Liu if (!std::filesystem::is_symlink(muxPath))
119d7be555eSGeorge Liu {
120d7be555eSGeorge Liu return busNumber;
121d7be555eSGeorge Liu }
122d7be555eSGeorge Liu
123d7be555eSGeorge Liu std::string rootName = std::filesystem::read_symlink(muxPath).filename();
124d7be555eSGeorge Liu size_t dash = rootName.find('-');
125d7be555eSGeorge Liu if (dash == std::string::npos)
126d7be555eSGeorge Liu {
127*7201be43SGeorge Liu lg2::error("Error finding root bus for '{NAME}'", "NAME", rootName);
128d7be555eSGeorge Liu return std::nullopt;
129d7be555eSGeorge Liu }
130d7be555eSGeorge Liu
131d7be555eSGeorge Liu return std::stoi(rootName.substr(0, dash));
132d7be555eSGeorge Liu }
133d7be555eSGeorge Liu
provideRootBusContext(boost::asio::io_context & io,NVMEMap & map,int rootBus)134d7be555eSGeorge Liu static std::shared_ptr<NVMeContext> provideRootBusContext(
135d7be555eSGeorge Liu boost::asio::io_context& io, NVMEMap& map, int rootBus)
136d7be555eSGeorge Liu {
137d7be555eSGeorge Liu auto findRoot = map.find(rootBus);
138d7be555eSGeorge Liu if (findRoot != map.end())
139d7be555eSGeorge Liu {
140d7be555eSGeorge Liu return findRoot->second;
141d7be555eSGeorge Liu }
142d7be555eSGeorge Liu
143d7be555eSGeorge Liu std::shared_ptr<NVMeContext> context =
144d7be555eSGeorge Liu std::make_shared<NVMeBasicContext>(io, rootBus);
145d7be555eSGeorge Liu map[rootBus] = context;
146d7be555eSGeorge Liu
147d7be555eSGeorge Liu return context;
148d7be555eSGeorge Liu }
149d7be555eSGeorge Liu
handleSensorConfigurations(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,const ManagedObjectType & sensorConfigurations)150d7be555eSGeorge Liu static void handleSensorConfigurations(
151d7be555eSGeorge Liu boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
152d7be555eSGeorge Liu std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
153d7be555eSGeorge Liu const ManagedObjectType& sensorConfigurations)
154d7be555eSGeorge Liu {
155d7be555eSGeorge Liu // todo: it'd be better to only update the ones we care about
156d7be555eSGeorge Liu for (const auto& [_, nvmeContextPtr] : nvmeDeviceMap)
157d7be555eSGeorge Liu {
158d7be555eSGeorge Liu if (nvmeContextPtr)
159d7be555eSGeorge Liu {
160d7be555eSGeorge Liu nvmeContextPtr->close();
161d7be555eSGeorge Liu }
162d7be555eSGeorge Liu }
163d7be555eSGeorge Liu nvmeDeviceMap.clear();
164d7be555eSGeorge Liu
165d7be555eSGeorge Liu // iterate through all found configurations
166d7be555eSGeorge Liu for (const auto& [interfacePath, sensorData] : sensorConfigurations)
167d7be555eSGeorge Liu {
168d7be555eSGeorge Liu // find base configuration
169d7be555eSGeorge Liu auto sensorBase =
170d7be555eSGeorge Liu sensorData.find(configInterfaceName(NVMeSensor::sensorType));
171d7be555eSGeorge Liu if (sensorBase == sensorData.end())
172d7be555eSGeorge Liu {
173d7be555eSGeorge Liu continue;
174d7be555eSGeorge Liu }
175d7be555eSGeorge Liu
176d7be555eSGeorge Liu const SensorBaseConfigMap& sensorConfig = sensorBase->second;
177d7be555eSGeorge Liu std::optional<int> busNumber =
178d7be555eSGeorge Liu extractBusNumber(interfacePath, sensorConfig);
179d7be555eSGeorge Liu std::optional<std::string> sensorName =
180d7be555eSGeorge Liu extractSensorName(interfacePath, sensorConfig);
181d7be555eSGeorge Liu uint8_t slaveAddr = extractSlaveAddr(interfacePath, sensorConfig);
182d7be555eSGeorge Liu std::optional<int> rootBus = deriveRootBus(busNumber);
183d7be555eSGeorge Liu
184d7be555eSGeorge Liu if (!(busNumber && sensorName && rootBus))
185d7be555eSGeorge Liu {
186d7be555eSGeorge Liu continue;
187d7be555eSGeorge Liu }
188d7be555eSGeorge Liu
189d7be555eSGeorge Liu std::vector<thresholds::Threshold> sensorThresholds;
190d7be555eSGeorge Liu if (!parseThresholdsFromConfig(sensorData, sensorThresholds))
191d7be555eSGeorge Liu {
192*7201be43SGeorge Liu lg2::error("error populating thresholds for '{NAME}'", "NAME",
193*7201be43SGeorge Liu *sensorName);
194d7be555eSGeorge Liu }
195d7be555eSGeorge Liu
196d7be555eSGeorge Liu try
197d7be555eSGeorge Liu {
198d7be555eSGeorge Liu // May throw for an invalid rootBus
199d7be555eSGeorge Liu std::shared_ptr<NVMeContext> context =
200d7be555eSGeorge Liu provideRootBusContext(io, nvmeDeviceMap, *rootBus);
201d7be555eSGeorge Liu
202d7be555eSGeorge Liu // Construct the sensor after grabbing the context so we don't
203d7be555eSGeorge Liu // glitch D-Bus May throw for an invalid busNumber
204d7be555eSGeorge Liu std::shared_ptr<NVMeSensor> sensorPtr =
205d7be555eSGeorge Liu std::make_shared<NVMeSensor>(
206d7be555eSGeorge Liu objectServer, io, dbusConnection, *sensorName,
207d7be555eSGeorge Liu std::move(sensorThresholds), interfacePath, *busNumber,
208d7be555eSGeorge Liu slaveAddr);
209d7be555eSGeorge Liu
210d7be555eSGeorge Liu context->addSensor(sensorPtr);
211d7be555eSGeorge Liu }
212d7be555eSGeorge Liu catch (const std::invalid_argument& ex)
213d7be555eSGeorge Liu {
214*7201be43SGeorge Liu lg2::error("Failed to add sensor for '{PATH}': '{ERROR}'", "PATH",
215*7201be43SGeorge Liu interfacePath.str, "ERROR", ex);
216d7be555eSGeorge Liu }
217d7be555eSGeorge Liu }
218d7be555eSGeorge Liu for (const auto& [_, context] : nvmeDeviceMap)
219d7be555eSGeorge Liu {
220d7be555eSGeorge Liu context->pollNVMeDevices();
221d7be555eSGeorge Liu }
222d7be555eSGeorge Liu }
223d7be555eSGeorge Liu
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)224d7be555eSGeorge Liu void createSensors(boost::asio::io_context& io,
225d7be555eSGeorge Liu sdbusplus::asio::object_server& objectServer,
226d7be555eSGeorge Liu std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
227d7be555eSGeorge Liu {
228d7be555eSGeorge Liu auto getter = std::make_shared<GetSensorConfiguration>(
229d7be555eSGeorge Liu dbusConnection, [&io, &objectServer, &dbusConnection](
230d7be555eSGeorge Liu const ManagedObjectType& sensorConfigurations) {
231d7be555eSGeorge Liu handleSensorConfigurations(io, objectServer, dbusConnection,
232d7be555eSGeorge Liu sensorConfigurations);
233d7be555eSGeorge Liu });
234d7be555eSGeorge Liu getter->getConfiguration(std::vector<std::string>{NVMeSensor::sensorType});
235d7be555eSGeorge Liu }
236d7be555eSGeorge Liu
interfaceRemoved(sdbusplus::message_t & message,NVMEMap & contexts)237d7be555eSGeorge Liu static void interfaceRemoved(sdbusplus::message_t& message, NVMEMap& contexts)
238d7be555eSGeorge Liu {
239d7be555eSGeorge Liu if (message.is_method_error())
240d7be555eSGeorge Liu {
241*7201be43SGeorge Liu lg2::error("interfacesRemoved callback method error");
242d7be555eSGeorge Liu return;
243d7be555eSGeorge Liu }
244d7be555eSGeorge Liu
245d7be555eSGeorge Liu sdbusplus::message::object_path path;
246d7be555eSGeorge Liu std::vector<std::string> interfaces;
247d7be555eSGeorge Liu
248d7be555eSGeorge Liu message.read(path, interfaces);
249d7be555eSGeorge Liu
250d7be555eSGeorge Liu for (auto& [_, context] : contexts)
251d7be555eSGeorge Liu {
252d7be555eSGeorge Liu std::optional<std::shared_ptr<NVMeSensor>> sensor =
253d7be555eSGeorge Liu context->getSensorAtPath(path);
254d7be555eSGeorge Liu if (!sensor)
255d7be555eSGeorge Liu {
256d7be555eSGeorge Liu continue;
257d7be555eSGeorge Liu }
258d7be555eSGeorge Liu
259d7be555eSGeorge Liu auto interface = std::find(interfaces.begin(), interfaces.end(),
260d7be555eSGeorge Liu (*sensor)->configInterface);
261d7be555eSGeorge Liu if (interface == interfaces.end())
262d7be555eSGeorge Liu {
263d7be555eSGeorge Liu continue;
264d7be555eSGeorge Liu }
265d7be555eSGeorge Liu
266d7be555eSGeorge Liu context->removeSensor(sensor.value());
267d7be555eSGeorge Liu }
268d7be555eSGeorge Liu }
269d7be555eSGeorge Liu
main()270d7be555eSGeorge Liu int main()
271d7be555eSGeorge Liu {
272d7be555eSGeorge Liu boost::asio::io_context io;
273d7be555eSGeorge Liu auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
274d7be555eSGeorge Liu systemBus->request_name("xyz.openbmc_project.NVMeSensor");
275d7be555eSGeorge Liu sdbusplus::asio::object_server objectServer(systemBus, true);
276d7be555eSGeorge Liu objectServer.add_manager("/xyz/openbmc_project/sensors");
277d7be555eSGeorge Liu
278d7be555eSGeorge Liu boost::asio::post(io,
279d7be555eSGeorge Liu [&]() { createSensors(io, objectServer, systemBus); });
280d7be555eSGeorge Liu
281d7be555eSGeorge Liu boost::asio::steady_timer filterTimer(io);
282d7be555eSGeorge Liu std::function<void(sdbusplus::message_t&)> eventHandler =
283d7be555eSGeorge Liu [&filterTimer, &io, &objectServer, &systemBus](sdbusplus::message_t&) {
284d7be555eSGeorge Liu // this implicitly cancels the timer
285d7be555eSGeorge Liu filterTimer.expires_after(std::chrono::seconds(1));
286d7be555eSGeorge Liu
287d7be555eSGeorge Liu filterTimer.async_wait([&](const boost::system::error_code& ec) {
288d7be555eSGeorge Liu if (ec == boost::asio::error::operation_aborted)
289d7be555eSGeorge Liu {
290d7be555eSGeorge Liu return; // we're being canceled
291d7be555eSGeorge Liu }
292d7be555eSGeorge Liu
293d7be555eSGeorge Liu if (ec)
294d7be555eSGeorge Liu {
295*7201be43SGeorge Liu lg2::error("Error: '{ERROR_MESSAGE}'", "ERROR_MESSAGE",
296*7201be43SGeorge Liu ec.message());
297d7be555eSGeorge Liu return;
298d7be555eSGeorge Liu }
299d7be555eSGeorge Liu
300d7be555eSGeorge Liu createSensors(io, objectServer, systemBus);
301d7be555eSGeorge Liu });
302d7be555eSGeorge Liu };
303d7be555eSGeorge Liu
304d7be555eSGeorge Liu std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
305d7be555eSGeorge Liu setupPropertiesChangedMatches(
306d7be555eSGeorge Liu *systemBus, std::to_array<const char*>({NVMeSensor::sensorType}),
307d7be555eSGeorge Liu eventHandler);
308d7be555eSGeorge Liu
309d7be555eSGeorge Liu // Watch for entity-manager to remove configuration interfaces
310d7be555eSGeorge Liu // so the corresponding sensors can be removed.
311d7be555eSGeorge Liu auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
312d7be555eSGeorge Liu static_cast<sdbusplus::bus_t&>(*systemBus),
313d7be555eSGeorge Liu "type='signal',member='InterfacesRemoved',arg0path='" +
314d7be555eSGeorge Liu std::string(inventoryPath) + "/'",
315d7be555eSGeorge Liu [](sdbusplus::message_t& msg) {
316d7be555eSGeorge Liu interfaceRemoved(msg, nvmeDeviceMap);
317d7be555eSGeorge Liu });
318d7be555eSGeorge Liu
319d7be555eSGeorge Liu setupManufacturingModeMatch(*systemBus);
320d7be555eSGeorge Liu io.run();
321d7be555eSGeorge Liu }
322