| #include "NVMeFeatureStore.hpp" | |
| #include "nvme_features.pb.h" | |
| #include <endian.h> | |
| #include <boost/asio/async_result.hpp> | |
| #include <boost/asio/post.hpp> | |
| #include <phosphor-logging/lg2.hpp> | |
| #include <cstring> | |
| #include <iomanip> | |
| #include <map> | |
| #include <sstream> | |
| #include <tuple> | |
| #include <utility> | |
| #include <vector> | |
| static constexpr uint32_t nvmeGetFeaturesCapSaveable = 0x1; | |
| static constexpr uint32_t nvmeGetFeaturesCapChangeable = 0x4; | |
| struct FeatureMeta | |
| { | |
| uint8_t fid; ///< NVMe Feature Identifier | |
| std::string protoClass; ///< Fully qualified Protobuf class name | |
| FeatureScope scope; ///< Applicability scope | |
| }; | |
| /** | |
| * @brief Map of P0 features defined in the design document. | |
| */ | |
| static const std::map<std::string, FeatureMeta> supportedFeatures = { | |
| {"Arbitration", | |
| {0x01, "google.gbmc.nvme.base.Arbitration", FeatureScope::Controller}}, | |
| {"PowerManagement", | |
| {0x02, "google.gbmc.nvme.base.PowerManagement", FeatureScope::Controller}}, | |
| {"TemperatureThreshold", | |
| {0x04, "google.gbmc.nvme.base.TemperatureThreshold", | |
| FeatureScope::Controller}}, | |
| {"ErrorRecovery", | |
| {0x05, "google.gbmc.nvme.base.ErrorRecovery", FeatureScope::Namespace}}, | |
| {"VolatileWriteCache", | |
| {0x06, "google.gbmc.nvme.base.VolatileWriteCache", | |
| FeatureScope::Controller}}, | |
| {"NumberOfQueues", | |
| {0x07, "google.gbmc.nvme.base.NumberOfQueues", FeatureScope::Controller}}, | |
| {"InterruptCoalescing", | |
| {0x08, "google.gbmc.nvme.base.InterruptCoalescing", | |
| FeatureScope::Controller}}, | |
| {"InterruptVectorConfiguration", | |
| {0x09, "google.gbmc.nvme.base.InterruptVectorConfiguration", | |
| FeatureScope::Controller}}, | |
| {"WriteAtomicityNormal", | |
| {0x0A, "google.gbmc.nvme.base.WriteAtomicityNormal", | |
| FeatureScope::Controller}}, | |
| {"AsynchronousEventConfiguration", | |
| {0x0B, "google.gbmc.nvme.base.AsynchronousEventConfiguration", | |
| FeatureScope::Controller}}, | |
| }; | |
| template <typename T> | |
| static T parseProto(const std::vector<uint8_t>& data) | |
| { | |
| T msg; | |
| if (!msg.ParseFromArray(data.data(), static_cast<int>(data.size()))) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| return msg; | |
| } | |
| // Helper for async_initiate | |
| static std::tuple<nvme_ex_ptr, uint32_t, std::vector<uint8_t>> | |
| asyncAdminGetFeatures(boost::asio::io_context& io, | |
| const std::shared_ptr<NVMeMiIntf>& nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, | |
| const NVMeMiIntf::GetFeaturesRequest& req, | |
| boost::asio::yield_context yield) | |
| { | |
| return boost::asio::async_initiate<boost::asio::yield_context, | |
| void(nvme_ex_ptr, uint32_t, | |
| std::vector<uint8_t>)>( | |
| [&io](auto&& callback, const std::shared_ptr<NVMeMiIntf>& nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, const NVMeMiIntf::GetFeaturesRequest& req) { | |
| auto sharedCb = std::make_shared<std::decay_t<decltype(callback)>>( | |
| std::forward<decltype(callback)>(callback)); | |
| nvmeIntf->adminGetFeatures( | |
| ctrl, req, | |
| [sharedCb, &io](nvme_ex_ptr ex, uint32_t resp, | |
| std::span<uint8_t> data) mutable { | |
| std::vector<uint8_t> dataCopy(data.begin(), data.end()); | |
| boost::asio::post(io, [sharedCb = std::move(sharedCb), ex, resp, | |
| dataCopy = std::move(dataCopy)]() mutable { | |
| (*sharedCb)(ex, resp, dataCopy); | |
| }); | |
| }); | |
| }, | |
| yield, nvmeIntf, ctrl, req); | |
| } | |
| static std::tuple<nvme_ex_ptr, uint32_t> asyncAdminSetFeatures( | |
| boost::asio::io_context& io, const std::shared_ptr<NVMeMiIntf>& nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, const NVMeMiIntf::SetFeaturesRequest& req, | |
| boost::asio::yield_context yield) | |
| { | |
| return boost::asio::async_initiate<boost::asio::yield_context, | |
| void(nvme_ex_ptr, uint32_t)>( | |
| [&io](auto&& callback, const std::shared_ptr<NVMeMiIntf>& nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, const NVMeMiIntf::SetFeaturesRequest& req) { | |
| auto sharedCb = std::make_shared<std::decay_t<decltype(callback)>>( | |
| std::forward<decltype(callback)>(callback)); | |
| nvmeIntf->adminSetFeatures( | |
| ctrl, req, [sharedCb, &io](nvme_ex_ptr ex, uint32_t resp) mutable { | |
| boost::asio::post(io, [sharedCb = std::move(sharedCb), ex, | |
| resp]() mutable { (*sharedCb)(ex, resp); }); | |
| }); | |
| }, | |
| yield, nvmeIntf, ctrl, req); | |
| } | |
| static std::tuple<nvme_ex_ptr, std::vector<uint8_t>> asyncAdminIdentify( | |
| boost::asio::io_context& io, const std::shared_ptr<NVMeMiIntf>& nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid, uint16_t cntid, | |
| boost::asio::yield_context yield) | |
| { | |
| return boost::asio::async_initiate<boost::asio::yield_context, | |
| void(nvme_ex_ptr, std::vector<uint8_t>)>( | |
| [&io](auto&& callback, const std::shared_ptr<NVMeMiIntf>& nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid, | |
| uint16_t cntid) { | |
| auto sharedCb = std::make_shared<std::decay_t<decltype(callback)>>( | |
| std::forward<decltype(callback)>(callback)); | |
| nvmeIntf->adminIdentify( | |
| ctrl, cns, nsid, cntid, | |
| [sharedCb, &io](nvme_ex_ptr ex, std::span<uint8_t> data) mutable { | |
| std::vector<uint8_t> dataCopy(data.begin(), data.end()); | |
| boost::asio::post(io, [sharedCb = std::move(sharedCb), ex, | |
| dataCopy = std::move(dataCopy)]() mutable { | |
| (*sharedCb)(ex, dataCopy); | |
| }); | |
| }); | |
| }, | |
| yield, nvmeIntf, ctrl, cns, nsid, cntid); | |
| } | |
| FeatureStore::FeatureStore( | |
| boost::asio::io_context& io, sdbusplus::asio::object_server& objServer, | |
| const std::shared_ptr<sdbusplus::asio::connection>& conn, | |
| const std::string& objectPath, std::shared_ptr<NVMeMiIntf> nvmeIntf, | |
| nvme_mi_ctrl_t ctrl, uint32_t nsid, FeatureScope scope) : | |
| io(io), objServer(objServer), conn(conn), objectPath(objectPath), | |
| nvmeIntf(std::move(nvmeIntf)), ctrl(ctrl), nsid(nsid), scope(scope) | |
| {} | |
| void FeatureStore::registerDbusInterface() | |
| { | |
| dbusIntf = objServer.add_interface(objectPath, | |
| "xyz.openbmc_project.NVMe.FeatureStore"); | |
| dbusIntf->register_property("ProtoLibraryVersion", std::string("2.0"), | |
| sdbusplus::asio::PropertyPermission::readOnly); | |
| dbusIntf->register_property("FeatureCollection", featureCollection, | |
| sdbusplus::asio::PropertyPermission::readOnly); | |
| dbusIntf->register_method( | |
| "GetFeature", | |
| [weakSelf = weak_from_this()](boost::asio::yield_context yield, | |
| const std::string& featureName) { | |
| auto self = weakSelf.lock(); | |
| if (!self) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); | |
| } | |
| return self->getFeature(std::move(yield), featureName); | |
| }); | |
| dbusIntf->register_method( | |
| "SetFeature", | |
| [weakSelf = weak_from_this()](boost::asio::yield_context yield, | |
| const std::string& featureName, | |
| const std::vector<uint8_t>& data) { | |
| auto self = weakSelf.lock(); | |
| if (!self) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); | |
| } | |
| self->setFeature(std::move(yield), featureName, data); | |
| }); | |
| dbusIntf->initialize(); | |
| } | |
| void FeatureStore::init(std::function<void(const std::error_code&)>&& cb) | |
| { | |
| (void)boost::asio::spawn( | |
| io, [self{shared_from_this()}, cb{std::move(cb)}]( | |
| const boost::asio::yield_context& yield) mutable { | |
| self->fetchIdentifyData(yield); | |
| self->registerDbusInterface(); | |
| cb({}); | |
| }); | |
| } | |
| void FeatureStore::fetchIdentifyData(const boost::asio::yield_context& yield) | |
| { | |
| auto [ex, data] = asyncAdminIdentify( | |
| io, nvmeIntf, ctrl, NVME_IDENTIFY_CNS_CTRL, NVME_NSID_NONE, 0, yield); | |
| if (ex) | |
| { | |
| lg2::error( | |
| "Identify Controller failed during FeatureStore init: {ERROR}", | |
| "ERROR", ex->what()); | |
| discoverFeatures(yield); | |
| return; | |
| } | |
| if (data.size() < sizeof(nvme_id_ctrl)) | |
| { | |
| lg2::error("Identify Controller returned insufficient data"); | |
| discoverFeatures(yield); | |
| return; | |
| } | |
| nvme_id_ctrl idCtrl = {}; | |
| std::memcpy(&idCtrl, data.data(), sizeof(nvme_id_ctrl)); | |
| vwcSupported = (idCtrl.vwc & NVME_CTRL_VWC_PRESENT) != 0; | |
| oncs = le16toh(idCtrl.oncs); | |
| if (!vwcSupported) | |
| { | |
| lg2::info("Volatile Write Cache (FID 0x06) not supported by hardware"); | |
| } | |
| discoverFeatures(yield); | |
| } | |
| void FeatureStore::discoverFeatures(const boost::asio::yield_context& yield) | |
| { | |
| bool oncsBit4 = (oncs & (1 << 4)) != 0; | |
| featureCollection.clear(); | |
| std::vector<std::pair<std::string, FeatureMeta>> featuresToCheck; | |
| for (const auto& [name, meta] : supportedFeatures) | |
| { | |
| if (meta.scope == scope) | |
| { | |
| if (name == "VolatileWriteCache" && !vwcSupported) | |
| { | |
| continue; | |
| } | |
| featuresToCheck.emplace_back(name, meta); | |
| } | |
| } | |
| for (const auto& [name, meta] : featuresToCheck) | |
| { | |
| std::stringstream ss; | |
| ss << "0x" << std::setfill('0') << std::setw(2) << std::hex | |
| << static_cast<int>(meta.fid); | |
| std::string fidStr = ss.str(); | |
| if (!oncsBit4) | |
| { | |
| featureCollection.emplace_back(name + "_Current", fidStr, | |
| meta.protoClass); | |
| continue; | |
| } | |
| NVMeMiIntf::GetFeaturesRequest args{}; | |
| args.fid = meta.fid; | |
| args.sel = NVME_GET_FEATURES_SEL_SUPPORTED; // 011b | |
| args.nsid = (scope == FeatureScope::Namespace) ? nsid : NVME_NSID_NONE; | |
| args.cdw11 = 0; | |
| args.data_len = 0; | |
| auto [ex, cdw0, data] = asyncAdminGetFeatures(io, nvmeIntf, ctrl, args, | |
| yield); | |
| if (ex) | |
| { | |
| lg2::error("Failed to query capabilities for FID {FID}: {ERROR}", | |
| "FID", meta.fid, "ERROR", ex->what()); | |
| featureCollection.emplace_back(name + "_Current", fidStr, | |
| meta.protoClass); | |
| } | |
| else | |
| { | |
| featureCaps[meta.fid] = cdw0; | |
| featureCollection.emplace_back(name + "_Current", fidStr, | |
| meta.protoClass); | |
| if ((cdw0 & nvmeGetFeaturesCapChangeable) != 0) | |
| { | |
| featureCollection.emplace_back(name + "_Default", fidStr, | |
| meta.protoClass); | |
| } | |
| if ((cdw0 & nvmeGetFeaturesCapSaveable) != 0) | |
| { | |
| featureCollection.emplace_back(name + "_Saved", fidStr, | |
| meta.protoClass); | |
| } | |
| } | |
| } | |
| } | |
| std::vector<uint8_t> FeatureStore::getFeature(boost::asio::yield_context yield, | |
| const std::string& featureName) | |
| { | |
| size_t lastUnderscore = featureName.find_last_of('_'); | |
| if (lastUnderscore == std::string::npos) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| std::string baseName = featureName.substr(0, lastUnderscore); | |
| std::string selectStr = featureName.substr(lastUnderscore + 1); | |
| auto it = supportedFeatures.find(baseName); | |
| if (it == supportedFeatures.end() || it->second.scope != scope) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| uint8_t fid = it->second.fid; | |
| uint8_t sel = 0; | |
| if (selectStr == "Current") | |
| { | |
| sel = 0; | |
| } | |
| else if (selectStr == "Default") | |
| { | |
| sel = 1; | |
| } | |
| else if (selectStr == "Saved") | |
| { | |
| sel = 2; | |
| } | |
| else | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| NVMeMiIntf::GetFeaturesRequest args{}; | |
| args.fid = fid; | |
| args.sel = sel; | |
| args.nsid = (scope == FeatureScope::Namespace) ? nsid : NVME_NSID_NONE; | |
| args.cdw11 = 0; | |
| args.data_len = 0; | |
| auto [ex, cdw0, data] = asyncAdminGetFeatures(io, nvmeIntf, ctrl, args, | |
| std::move(yield)); | |
| if (ex) | |
| { | |
| lg2::error("NVMe Get Features (FID {FID}) failed: {ERROR}", "FID", fid, | |
| "ERROR", ex->what()); | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); | |
| } | |
| std::vector<uint8_t> protoData; | |
| switch (fid) | |
| { | |
| case 0x01: | |
| { | |
| google::gbmc::nvme::base::Arbitration msg; | |
| msg.set_arbitration_burst(cdw0 & 0xFF); | |
| msg.set_low_priority_weight((cdw0 >> 8) & 0xFF); | |
| msg.set_medium_priority_weight((cdw0 >> 16) & 0xFF); | |
| msg.set_high_priority_weight((cdw0 >> 24) & 0xFF); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize Arbitration"); | |
| } | |
| break; | |
| } | |
| case 0x02: | |
| { | |
| google::gbmc::nvme::base::PowerManagement msg; | |
| msg.set_power_state(cdw0 & 0x1F); | |
| msg.set_workload_hint((cdw0 >> 8) & 0x7); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize PowerManagement"); | |
| } | |
| break; | |
| } | |
| case 0x04: | |
| { | |
| google::gbmc::nvme::base::TemperatureThreshold msg; | |
| msg.set_temperature_threshold(cdw0 & 0xFFFF); | |
| msg.set_threshold_temperature_select((cdw0 >> 16) & 0xF); | |
| msg.set_threshold_type_select( | |
| static_cast<google::gbmc::nvme::base:: | |
| TemperatureThreshold_ThresholdType>( | |
| (cdw0 >> 20) & 0x3)); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize TemperatureThreshold"); | |
| } | |
| break; | |
| } | |
| case 0x05: | |
| { | |
| google::gbmc::nvme::base::ErrorRecovery msg; | |
| msg.set_tler(cdw0 & 0xFFFF); | |
| msg.set_dulbe_enable( | |
| static_cast<google::gbmc::nvme::base::ErrorRecovery_State>( | |
| (cdw0 >> 16) & 0x1)); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize ErrorRecovery"); | |
| } | |
| break; | |
| } | |
| case 0x06: | |
| { | |
| google::gbmc::nvme::base::VolatileWriteCache msg; | |
| msg.set_write_cache_enable( | |
| static_cast<google::gbmc::nvme::base::VolatileWriteCache_State>( | |
| cdw0 & 0x1)); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize VolatileWriteCache"); | |
| } | |
| break; | |
| } | |
| case 0x07: | |
| { | |
| google::gbmc::nvme::base::NumberOfQueues msg; | |
| msg.set_number_of_submission_queues(cdw0 & 0xFFFF); | |
| msg.set_number_of_completion_queues((cdw0 >> 16) & 0xFFFF); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize NumberOfQueues"); | |
| } | |
| break; | |
| } | |
| case 0x08: | |
| { | |
| google::gbmc::nvme::base::InterruptCoalescing msg; | |
| msg.set_aggregation_threshold(cdw0 & 0xFF); | |
| msg.set_aggregation_time((cdw0 >> 8) & 0xFF); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize Interrupt Coalescing"); | |
| } | |
| break; | |
| } | |
| case 0x09: | |
| { | |
| google::gbmc::nvme::base::InterruptVectorConfiguration msg; | |
| msg.set_interrupt_vector(cdw0 & 0xFFFF); | |
| msg.set_coalescing_disable( | |
| static_cast<google::gbmc::nvme::base:: | |
| InterruptVectorConfiguration_CoalescingState>( | |
| (cdw0 >> 16) & 0x1)); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize InterruptVectorConfiguration"); | |
| } | |
| break; | |
| } | |
| case 0x0A: | |
| { | |
| google::gbmc::nvme::base::WriteAtomicityNormal msg; | |
| msg.set_disable_normal( | |
| static_cast<google::gbmc::nvme::base:: | |
| WriteAtomicityNormal_NormalWriteAtomicityState>( | |
| cdw0 & 0x1)); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error("Failed to serialize WriteAtomicityNormal"); | |
| } | |
| break; | |
| } | |
| case 0x0B: | |
| { | |
| google::gbmc::nvme::base::AsynchronousEventConfiguration msg; | |
| msg.set_smart_critical_warning_config(cdw0 & 0xFF); | |
| protoData.resize(msg.ByteSizeLong()); | |
| if (!msg.SerializeToArray(protoData.data(), | |
| static_cast<int>(protoData.size()))) | |
| { | |
| lg2::error( | |
| "Failed to serialize AsynchronousEventConfiguration"); | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| return protoData; | |
| } | |
| void FeatureStore::setFeature(boost::asio::yield_context yield, | |
| const std::string& featureName, | |
| const std::vector<uint8_t>& data) | |
| { | |
| size_t lastUnderscore = featureName.find_last_of('_'); | |
| if (lastUnderscore == std::string::npos) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| std::string baseName = featureName.substr(0, lastUnderscore); | |
| std::string selectStr = featureName.substr(lastUnderscore + 1); | |
| auto it = supportedFeatures.find(baseName); | |
| if (it == supportedFeatures.end() || it->second.scope != scope) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| if (selectStr == "Default") | |
| { | |
| // Default endpoint is read-only | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed(); | |
| } | |
| if (selectStr != "Current" && selectStr != "Saved") | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); | |
| } | |
| uint8_t fid = it->second.fid; | |
| bool save = (selectStr == "Saved"); | |
| auto capIt = featureCaps.find(fid); | |
| if (capIt != featureCaps.end()) | |
| { | |
| uint32_t caps = capIt->second; | |
| bool changeable = (caps & nvmeGetFeaturesCapChangeable) != 0; | |
| bool saveable = (caps & nvmeGetFeaturesCapSaveable) != 0; | |
| if (save && !saveable) | |
| { | |
| lg2::error("SetFeature: FID {FID} is not saveable per SEL=011b", | |
| "FID", lg2::hex, fid); | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed(); | |
| } | |
| if (!save && !changeable) | |
| { | |
| lg2::error("SetFeature: FID {FID} is not changeable per SEL=011b", | |
| "FID", lg2::hex, fid); | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed(); | |
| } | |
| } | |
| uint32_t cdw11 = 0; | |
| switch (fid) | |
| { | |
| case 0x01: | |
| { | |
| auto msg = parseProto<google::gbmc::nvme::base::Arbitration>(data); | |
| if (msg.arbitration_burst() > 0xFF || | |
| msg.low_priority_weight() > 0xFF || | |
| msg.medium_priority_weight() > 0xFF || | |
| msg.high_priority_weight() > 0xFF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.arbitration_burst() & 0xFF) | | |
| ((msg.low_priority_weight() & 0xFF) << 8) | | |
| ((msg.medium_priority_weight() & 0xFF) << 16) | | |
| ((msg.high_priority_weight() & 0xFF) << 24); | |
| break; | |
| } | |
| case 0x02: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::PowerManagement>(data); | |
| if (msg.power_state() > 0x1F || msg.workload_hint() > 0x7) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.power_state() & 0x1F) | | |
| ((msg.workload_hint() & 0x7) << 8); | |
| break; | |
| } | |
| case 0x04: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::TemperatureThreshold>( | |
| data); | |
| if (msg.temperature_threshold() > 0xFFFF || | |
| msg.threshold_temperature_select() > 0xF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.temperature_threshold() & 0xFFFF) | | |
| ((msg.threshold_temperature_select() & 0xF) << 16) | | |
| ((static_cast<uint32_t>(msg.threshold_type_select()) & 0x3) | |
| << 20); | |
| break; | |
| } | |
| case 0x05: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::ErrorRecovery>(data); | |
| if (msg.tler() > 0xFFFF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.tler() & 0xFFFF) | | |
| ((static_cast<uint32_t>(msg.dulbe_enable()) & 0x1) << 16); | |
| break; | |
| } | |
| case 0x06: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::VolatileWriteCache>(data); | |
| cdw11 = static_cast<uint32_t>(msg.write_cache_enable()) & 0x1; | |
| break; | |
| } | |
| case 0x07: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::NumberOfQueues>(data); | |
| if (msg.number_of_submission_queues() > 0xFFFF || | |
| msg.number_of_completion_queues() > 0xFFFF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.number_of_submission_queues() & 0xFFFF) | | |
| ((msg.number_of_completion_queues() & 0xFFFF) << 16); | |
| break; | |
| } | |
| case 0x08: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::InterruptCoalescing>(data); | |
| if (msg.aggregation_threshold() > 0xFF || | |
| msg.aggregation_time() > 0xFF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.aggregation_threshold() & 0xFF) | | |
| ((msg.aggregation_time() & 0xFF) << 8); | |
| break; | |
| } | |
| case 0x09: | |
| { | |
| auto msg = parseProto< | |
| google::gbmc::nvme::base::InterruptVectorConfiguration>(data); | |
| if (msg.interrupt_vector() > 0xFFFF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = (msg.interrupt_vector() & 0xFFFF) | | |
| ((static_cast<uint32_t>(msg.coalescing_disable()) & 0x1) | |
| << 16); | |
| break; | |
| } | |
| case 0x0A: | |
| { | |
| auto msg = | |
| parseProto<google::gbmc::nvme::base::WriteAtomicityNormal>( | |
| data); | |
| cdw11 = static_cast<uint32_t>(msg.disable_normal()) & 0x1; | |
| break; | |
| } | |
| case 0x0B: | |
| { | |
| auto msg = parseProto< | |
| google::gbmc::nvme::base::AsynchronousEventConfiguration>(data); | |
| if (msg.smart_critical_warning_config() > 0xFF) | |
| { | |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: | |
| InvalidArgument(); | |
| } | |
| cdw11 = msg.smart_critical_warning_config() & 0xFF; | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| NVMeMiIntf::SetFeaturesRequest args{}; | |
| args.fid = fid; | |
| args.save = save; | |
| args.cdw11 = cdw11; | |
| args.nsid = (scope == FeatureScope::Namespace) ? nsid : NVME_NSID_NONE; | |
| args.data = {}; | |
| auto [ex, resp] = asyncAdminSetFeatures(io, nvmeIntf, ctrl, args, | |
| std::move(yield)); | |
| if (ex) | |
| { | |
| lg2::error("NVMe Set Features (FID {FID}) failed: {ERROR}", "FID", fid, | |
| "ERROR", ex->what()); | |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); | |
| } | |
| } |