| #include "NVMeController.hpp" |
| |
| #include "AsioHelper.hpp" |
| #include "NVMeCacheImpl.hpp" |
| #include "NVMeError.hpp" |
| #include "NVMePlugin.hpp" |
| #include "NVMeSubsys.hpp" |
| |
| #include <sdbusplus/exception.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| #include <xyz/openbmc_project/Common/File/error.hpp> |
| #include <xyz/openbmc_project/Common/error.hpp> |
| |
| #include <cstdio> |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| |
| // using sdbusplus::xyz::openbmc_project::Inventory::Item::server:: |
| // StorageController; |
| using sdbusplus::xyz::openbmc_project::NVMe::server::NVMeAdmin; |
| using SchedulerClockType = std::chrono::steady_clock; |
| |
| std::shared_ptr<NVMeControllerEnabled> |
| NVMeControllerEnabled::create(NVMeController&& nvmeController) |
| { |
| auto self = std::shared_ptr<NVMeControllerEnabled>( |
| new NVMeControllerEnabled(std::move(nvmeController))); |
| self->init(); |
| return self; |
| } |
| |
| NVMeControllerEnabled::NVMeControllerEnabled(NVMeController&& nvmeController) : |
| NVMeController(nvmeController), |
| NVMeAdmin(*this->NVMeController::conn, this->NVMeController::path.c_str(), |
| {{"FirmwareCommitStatus", {FwCommitStatus::Ready}}, |
| {"FirmwareDownloadStatus", {FwDownloadStatus::Ready}}}), |
| SoftwareExtVersion(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str()), |
| SoftwareVersion(dynamic_cast<sdbusplus::bus_t&>(*conn), path.c_str()) |
| {} |
| |
| void NVMeControllerEnabled::init() |
| { |
| createAssociation(); |
| |
| passthruInterface = |
| objServer.add_interface(path, "xyz.openbmc_project.NVMe.Passthru"); |
| |
| passthruInterface->register_method( |
| "AdminNonDataCmd", |
| [selfWeak{weak_from_this()}]( |
| boost::asio::yield_context yield, uint8_t opcode, uint32_t cdw1, |
| uint32_t cdw2, uint32_t cdw3, uint32_t cdw10, uint32_t cdw11, |
| uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, uint32_t cdw15) { |
| auto self = selfWeak.lock(); |
| if (!self) |
| { |
| checkLibNVMeError(std::make_error_code(std::errc::no_such_device), |
| -1, "AdminNonDataCmd"); |
| return std::tuple<uint32_t, uint32_t, uint32_t>{0, 0, 0}; |
| } |
| |
| if (self->status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| return self->adminNonDataCmdMethod(std::move(yield), opcode, cdw1, cdw2, |
| cdw3, cdw10, cdw11, cdw12, cdw13, |
| cdw14, cdw15); |
| }); |
| passthruInterface->initialize(); |
| |
| securityInterface = objServer.add_interface( |
| path, "xyz.openbmc_project.Inventory.Item.StorageControllerSecurity"); |
| securityInterface->register_method( |
| "SecuritySend", [selfWeak{weak_from_this()}]( |
| boost::asio::yield_context yield, uint8_t proto, |
| uint16_t protoSpecific, std::vector<uint8_t> data) { |
| auto self = selfWeak.lock(); |
| if (!self) |
| { |
| checkLibNVMeError(std::make_error_code(std::errc::no_such_device), |
| -1, "SecuritySend"); |
| return; |
| } |
| |
| if (self->status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| return self->securitySendMethod(std::move(yield), proto, protoSpecific, |
| data); |
| }); |
| securityInterface->register_method( |
| "SecurityReceive", |
| [selfWeak{weak_from_this()}](boost::asio::yield_context yield, |
| uint8_t proto, uint16_t protoSpecific, |
| uint32_t transferLength) { |
| auto self = selfWeak.lock(); |
| if (!self) |
| { |
| checkLibNVMeError(std::make_error_code(std::errc::no_such_device), |
| -1, "SecurityReceive"); |
| return std::vector<uint8_t>{}; |
| } |
| |
| if (self->status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| return self->securityReceiveMethod(std::move(yield), proto, |
| protoSpecific, transferLength); |
| }); |
| |
| // StorageController interface is implemented manually to allow |
| // async methods |
| ctrlInterface = objServer.add_interface( |
| path, "xyz.openbmc_project.Inventory.Item.StorageController"); |
| ctrlInterface->register_method( |
| "AttachVolume", [selfWeak{weak_from_this()}]( |
| boost::asio::yield_context yield, |
| const sdbusplus::message::object_path& volPath) { |
| auto self = selfWeak.lock(); |
| if (!self) |
| { |
| checkLibNVMeError(std::make_error_code(std::errc::no_such_device), |
| -1, "SecurityReceive"); |
| return; |
| } |
| |
| if (self->status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| return self->attachVolume(std::move(yield), volPath); |
| }); |
| ctrlInterface->register_method( |
| "DetachVolume", [selfWeak{weak_from_this()}]( |
| boost::asio::yield_context yield, |
| const sdbusplus::message::object_path& volPath) { |
| auto self = selfWeak.lock(); |
| if (!self) |
| { |
| checkLibNVMeError(std::make_error_code(std::errc::no_such_device), |
| -1, "SecurityReceive"); |
| return; |
| } |
| |
| if (self->status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| return self->detachVolume(std::move(yield), volPath); |
| }); |
| |
| ctrlInterface->initialize(); |
| |
| securityInterface->initialize(); |
| // StorageController::emit_added(); |
| |
| NVMeAdmin::emit_added(); |
| SoftwareExtVersion::emit_added(); |
| SoftwareVersion::emit_added(); |
| } |
| |
| void NVMeControllerEnabled::start( |
| const std::shared_ptr<NVMeControllerPlugin>& nvmePlugin) |
| { |
| this->NVMeController::start(nvmePlugin); |
| |
| if (isPrimary) |
| { |
| // only to test compilation. need to remove. |
| this->metricStore.emplace(this->NVMeController::io, |
| this->NVMeController::conn, |
| this->NVMeController::path); |
| |
| auto scheduler = Scheduler<SchedulerClockType>::getScheduler(path); |
| |
| using IdentifyMetric = |
| IdentifyMetric<NVME_IDENTIFY_CNS_CTRL, SchedulerClockType>; |
| |
| using ErrorInfoMetric = |
| ControllerLogPageMetric<NVME_LOG_LID_ERROR, SchedulerClockType>; |
| using SMARTMetric = |
| ControllerLogPageMetric<NVME_LOG_LID_SMART, SchedulerClockType>; |
| using FwSlotMetric = |
| ControllerLogPageMetric<NVME_LOG_LID_FW_SLOT, SchedulerClockType>; |
| using SelfTestMetric = |
| ControllerLogPageMetric<NVME_LOG_LID_DEVICE_SELF_TEST, |
| SchedulerClockType>; |
| using HostTelemetryMetric = |
| ControllerLogPageMetric<NVME_LOG_LID_TELEMETRY_HOST, |
| SchedulerClockType>; |
| |
| // NOLINTBEGIN(modernize-make-shared) |
| auto identify = std::shared_ptr<IdentifyMetric>(new IdentifyMetric( |
| this->shared_from_this(), scheduler, |
| // Metigate solution before Knuckle FW fix for b/404610159 |
| std::chrono::seconds(60))); |
| |
| auto errorInfo = std::shared_ptr<ErrorInfoMetric>(new ErrorInfoMetric( |
| this->weak_from_this(), scheduler, |
| // TODO: configurable interval |
| std::chrono::duration_cast<SchedulerClockType::duration>( |
| std::chrono::seconds(60)))); |
| |
| auto smart = std::shared_ptr<SMARTMetric>(new SMARTMetric( |
| this->weak_from_this(), scheduler, |
| // TODO: configurable interval |
| std::chrono::duration_cast<SchedulerClockType::duration>( |
| std::chrono::seconds(60)))); |
| |
| auto fwSlot = std::shared_ptr<FwSlotMetric>(new FwSlotMetric( |
| this->weak_from_this(), scheduler, |
| // TODO: configurable interval |
| std::chrono::duration_cast<SchedulerClockType::duration>( |
| std::chrono::seconds(60)))); |
| |
| auto selfTest = std::shared_ptr<SelfTestMetric>(new SelfTestMetric( |
| this->weak_from_this(), scheduler, |
| // TODO: configurable interval |
| std::chrono::duration_cast<SchedulerClockType::duration>( |
| std::chrono::seconds(60)))); |
| |
| auto hostTelemetry = |
| std::shared_ptr<HostTelemetryMetric>(new HostTelemetryMetric( |
| this->weak_from_this(), scheduler, |
| // TODO: configurable interval |
| std::chrono::duration_cast<SchedulerClockType::duration>( |
| std::chrono::seconds(900)))); |
| // NOLINTEND(modernize-make-shared) |
| |
| this->metricStore.emplace( |
| this->NVMeController::io, this->NVMeController::conn, |
| this->NVMeController::path, std::move(identify), |
| std::move(errorInfo), std::move(smart), std::move(fwSlot), |
| std::move(selfTest), std::move(hostTelemetry)); |
| } |
| status = Status::Enabled; |
| } |
| |
| void NVMeControllerEnabled::stop() |
| { |
| status = Status::Disabled; |
| this->metricStore.reset(); |
| } |
| |
| void NVMeController::createAssociation() |
| { |
| assocIntf = objServer.add_interface(path, association::interface); |
| assocIntf->register_property("Associations", makeAssociation()); |
| assocIntf->initialize(); |
| } |
| |
| void NVMeController::updateAssociation() |
| { |
| if (assocIntf) |
| { |
| assocIntf->set_property("Associations", makeAssociation()); |
| } |
| } |
| |
| std::vector<Association> NVMeController::makeAssociation() const |
| { |
| std::vector<Association> associations; |
| std::filesystem::path p(path); |
| |
| auto s = subsys.lock(); |
| if (!s) |
| { |
| std::cerr << "makeAssociation() after shutdown\n"; |
| return associations; |
| } |
| |
| associations.emplace_back("storage", "storage_controller", s->path); |
| |
| for (const auto& cntrl : secondaryControllers) |
| { |
| associations.emplace_back("secondary", "primary", cntrl); |
| } |
| |
| for (uint32_t nsid : s->attachedVolumes(getCntrlId())) |
| { |
| auto p = s->volumePath(nsid); |
| associations.emplace_back("attaching", "attached", p); |
| } |
| |
| return associations; |
| } |
| |
| sdbusplus::message::unix_fd NVMeControllerEnabled::getLogPage(uint8_t lid, |
| uint32_t nsid, |
| uint8_t lsp, |
| uint16_t lsi) |
| { |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| std::array<int, 2> pipe{}; |
| if (::pipe(pipe.data()) < 0) |
| { |
| std::cerr << "GetLogPage fails to open pipe: " << std::strerror(errno) |
| << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::File::Error::Open(); |
| } |
| |
| // standard NVMe Log IDs |
| if (lid < uint8_t{0xC0}) |
| { |
| nvmeIntf->adminGetLogPage( |
| nvmeCtrl, static_cast<nvme_cmd_get_log_lid>(lid), nsid, lsp, lsi, |
| [pipe, &io = this->io](const std::error_code& ec, |
| std::span<uint8_t> data) { |
| if (ec) |
| { |
| std::cerr << "fail to GetLogPage: " << ec.message() << '\n'; |
| close(pipe[1]); |
| close(pipe[0]); |
| return; |
| } |
| |
| // async write to pipe |
| auto asyncPipe = |
| std::make_shared<boost::asio::posix::stream_descriptor>( |
| io, pipe[1]); |
| std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe = |
| asyncPipe; |
| |
| auto asyncBuffer = std::make_shared<std::vector<uint8_t>>( |
| data.begin(), data.end()); |
| boost::asio::async_write( |
| *asyncPipe, boost::asio::buffer(*asyncBuffer), |
| [asyncPipe, asyncBuffer](const boost::system::error_code& error, |
| std::size_t /* transferredSize */) { |
| if (error) |
| { |
| std::cerr |
| << "GetLogPage fails to write fd: " << error.message() |
| << '\n'; |
| } |
| }); |
| |
| // set a timer of 5 second for the client to read the telemetry. |
| auto timer = std::make_shared<boost::asio::steady_timer>(io); |
| timer->expires_after(std::chrono::seconds(5)); |
| timer->async_wait([timer, fd{pipe[0]}, weakPipe]( |
| const boost::system::error_code& /* ec */) { |
| auto pipe = weakPipe.lock(); |
| if (pipe) |
| { |
| pipe->cancel(); |
| } |
| close(fd); |
| }); |
| }); |
| } |
| // vendor Log IDs |
| else if (!plugin.expired()) |
| { |
| auto nvmePlugin = plugin.lock(); |
| auto handler = nvmePlugin->getGetLogPageHandler(); |
| if (handler) |
| { |
| std::function<void(const std::error_code&, std::span<uint8_t>)> cb = |
| [pipe, &io = this->io](std::error_code ec, |
| std::span<uint8_t> data) { |
| if (ec) |
| { |
| std::cerr << "fail to GetLogPage: " << ec.message() << '\n'; |
| close(pipe[1]); |
| close(pipe[0]); |
| return; |
| } |
| |
| // async write to pipe |
| auto asyncPipe = |
| std::make_shared<boost::asio::posix::stream_descriptor>( |
| io, pipe[1]); |
| std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe = |
| asyncPipe; |
| |
| auto asyncBuffer = std::make_shared<std::vector<uint8_t>>( |
| data.begin(), data.end()); |
| boost::asio::async_write( |
| *asyncPipe, boost::asio::buffer(*asyncBuffer), |
| [asyncPipe, |
| asyncBuffer](const boost::system::error_code& error, |
| std::size_t /* transferredSize */) { |
| if (error) |
| { |
| std::cerr << "GetLogPage fails to write fd: " |
| << error.message() << '\n'; |
| } |
| }); |
| |
| // set a timer of 5 second for the client to read the telemetry. |
| auto timer = std::make_shared<boost::asio::steady_timer>(io); |
| timer->expires_after(std::chrono::seconds(5)); |
| timer->async_wait( |
| [timer, fd{pipe[0]}, |
| weakPipe](const boost::system::error_code& /* ec */) { |
| auto pipe = weakPipe.lock(); |
| if (pipe) |
| { |
| pipe->cancel(); |
| } |
| close(fd); |
| }); |
| }; |
| handler(lid, nsid, lsp, lsi, std::move(cb)); |
| } |
| else // No VU LogPage handler |
| { |
| close(pipe[1]); |
| close(pipe[0]); |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| InvalidArgument(); |
| } |
| } |
| else // No VU plugin |
| { |
| close(pipe[0]); |
| close(pipe[1]); |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); |
| } |
| return sdbusplus::message::unix_fd{pipe[0]}; |
| } |
| |
| sdbusplus::message::unix_fd |
| NVMeControllerEnabled::identify(uint8_t cns, uint32_t nsid, uint16_t cntid) |
| { |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| std::array<int, 2> pipe{}; |
| if (::pipe(pipe.data()) < 0) |
| { |
| std::cerr << "Identify fails to open pipe: " << std::strerror(errno) |
| << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::File::Error::Open(); |
| } |
| |
| nvmeIntf->adminIdentify( |
| nvmeCtrl, static_cast<nvme_identify_cns>(cns), nsid, cntid, |
| [pipe, &io = this->io](const nvme_ex_ptr& ex, std::span<uint8_t> data) { |
| if (ex) |
| { |
| std::cerr << "fail to Identify: " << ex << '\n'; |
| close(pipe[1]); |
| close(pipe[0]); |
| return; |
| } |
| |
| // async write to pipe |
| auto asyncPipe = |
| std::make_shared<boost::asio::posix::stream_descriptor>(io, |
| pipe[1]); |
| std::weak_ptr<boost::asio::posix::stream_descriptor> weakPipe = |
| asyncPipe; |
| |
| auto asyncBuffer = std::make_shared<std::vector<uint8_t>>(data.begin(), |
| data.end()); |
| boost::asio::async_write( |
| *asyncPipe, boost::asio::buffer(*asyncBuffer), |
| [asyncPipe, asyncBuffer](const boost::system::error_code& error, |
| std::size_t /* transferredSize */) { |
| if (error) |
| { |
| std::cerr << "Identify fails to write fd: " << error.message() |
| << '\n'; |
| } |
| }); |
| |
| // set a timer of 5 second for the client to read the telemetry. |
| auto timer = std::make_shared<boost::asio::steady_timer>(io); |
| timer->expires_after(std::chrono::seconds(5)); |
| timer->async_wait([timer, fd{pipe[0]}, weakPipe]( |
| const boost::system::error_code& /* ec */) { |
| auto pipe = weakPipe.lock(); |
| if (pipe) |
| { |
| pipe->cancel(); |
| } |
| close(fd); |
| }); |
| }); |
| return sdbusplus::message::unix_fd{pipe[0]}; |
| } |
| |
| NVMeAdmin::FwCommitStatus NVMeControllerEnabled::firmwareCommitStatus( |
| NVMeAdmin::FwCommitStatus status) |
| { |
| auto commitStatus = this->NVMeAdmin::firmwareCommitStatus(); |
| // The function is only allowed to reset the status back to ready |
| if (status != FwCommitStatus::Ready || |
| commitStatus == FwCommitStatus::Ready || |
| commitStatus == FwCommitStatus::InProgress) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed{}; |
| } |
| return this->NVMeAdmin::firmwareCommitStatus(status); |
| } |
| |
| void NVMeControllerEnabled::firmwareCommitAsync(uint8_t commitAction, |
| uint8_t firmwareSlot, bool bpid) |
| { |
| auto commitStatus = this->NVMeAdmin::firmwareCommitStatus(); |
| if (commitStatus != FwCommitStatus::Ready) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed(); |
| } |
| |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| this->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::InProgress); |
| nvmeIntf->adminFwCommit( |
| nvmeCtrl, static_cast<nvme_fw_commit_ca>(commitAction & 0b111), |
| firmwareSlot, bpid, |
| [self{shared_from_this()}](const std::error_code& ec, |
| nvme_status_field status) { |
| if (ec) |
| { |
| self->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::Failed); |
| return; |
| } |
| if (status != NVME_SC_SUCCESS) |
| { |
| self->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::RequireReset); |
| return; |
| } |
| |
| self->NVMeAdmin::firmwareCommitStatus(FwCommitStatus::Success); |
| }); |
| } |
| |
| NVMeAdmin::FwDownloadStatus NVMeControllerEnabled::firmwareDownloadStatus( |
| NVMeAdmin::FwDownloadStatus status) |
| { |
| auto downloadStatus = this->NVMeAdmin::firmwareDownloadStatus(); |
| // The function is only allowed to reset the status back to ready |
| if (status != FwDownloadStatus::Ready || |
| downloadStatus == FwDownloadStatus::Ready || |
| downloadStatus == FwDownloadStatus::InProgress) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed{}; |
| } |
| return this->NVMeAdmin::firmwareDownloadStatus(status); |
| } |
| |
| void NVMeControllerEnabled::firmwareDownloadAsync(std::string pathToImage) |
| { |
| auto downloadStatus = this->NVMeAdmin::firmwareDownloadStatus(); |
| if (downloadStatus != FwDownloadStatus::Ready) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed(); |
| } |
| if (std::filesystem::exists(pathToImage)) |
| { |
| this->NVMeAdmin::firmwareDownloadStatus(FwDownloadStatus::InProgress); |
| nvmeIntf->adminFwDownload( |
| nvmeCtrl, pathToImage, |
| [self{shared_from_this()}](const std::error_code& ec, |
| nvme_status_field status) { |
| if (ec || status != NVME_SC_SUCCESS) |
| { |
| self->NVMeAdmin::firmwareDownloadStatus( |
| FwDownloadStatus::Failed); |
| return; |
| } |
| self->NVMeAdmin::firmwareDownloadStatus(FwDownloadStatus::Success); |
| }); |
| } |
| else |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument(); |
| } |
| } |
| |
| NVMeControllerEnabled::~NVMeControllerEnabled() |
| { |
| objServer.remove_interface(securityInterface); |
| objServer.remove_interface(passthruInterface); |
| SoftwareVersion::emit_removed(); |
| SoftwareExtVersion::emit_removed(); |
| NVMeAdmin::emit_removed(); |
| // StorageController::emit_removed(); |
| objServer.remove_interface(ctrlInterface); |
| } |
| |
| NVMeController::NVMeController( |
| boost::asio::io_context& io, sdbusplus::asio::object_server& objServer, |
| std::shared_ptr<sdbusplus::asio::connection> conn, std::string path, |
| const SensorData& configData, std::shared_ptr<NVMeMiIntf> nvmeIntf, |
| nvme_mi_ctrl_t ctrl, std::weak_ptr<NVMeSubsystem> subsys) : |
| isPrimary(true), io(io), objServer(objServer), conn(std::move(conn)), |
| path(std::move(path)), config(configData), nvmeIntf(std::move(nvmeIntf)), |
| nvmeCtrl(ctrl), subsys(std::move(subsys)) |
| {} |
| |
| NVMeController::~NVMeController() |
| { |
| objServer.remove_interface(assocIntf); |
| } |
| |
| void NVMeController::start( |
| const std::shared_ptr<NVMeControllerPlugin>& nvmePlugin) |
| { |
| plugin = nvmePlugin; |
| } |
| |
| void NVMeController::stop() {} |
| |
| void NVMeController::setSecAssoc( |
| const std::vector<std::shared_ptr<NVMeController>>& secCntrls) |
| { |
| secondaryControllers.clear(); |
| |
| if (secCntrls.empty()) |
| { |
| return; |
| } |
| |
| for (const auto& cntrl : secCntrls) |
| { |
| secondaryControllers.push_back(cntrl->path); |
| } |
| updateAssociation(); |
| } |
| |
| void NVMeControllerEnabled::securitySendMethod(boost::asio::yield_context yield, |
| uint8_t proto, |
| uint16_t protoSpecific, |
| std::span<uint8_t> data) |
| { |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| using callback_t = void(std::tuple<std::error_code, int>); |
| auto [err, nvmeStatus] = |
| boost::asio::async_initiate<boost::asio::yield_context, callback_t>( |
| [intf{nvmeIntf}, ctrl{nvmeCtrl}, proto, protoSpecific, |
| &data](auto&& handler) { |
| auto h = asio_helper::CopyableCallback( |
| std::forward<decltype(handler)>(handler)); |
| |
| intf->adminSecuritySend( |
| ctrl, proto, protoSpecific, data, |
| [h](const std::error_code& err, int nvmeStatus) mutable { |
| h(std::make_tuple(err, nvmeStatus)); |
| }); |
| }, |
| yield); |
| |
| // exception must be thrown outside of the async block |
| checkLibNVMeError(err, nvmeStatus, "SecuritySend"); |
| } |
| |
| std::vector<uint8_t> NVMeControllerEnabled::securityReceiveMethod( |
| boost::asio::yield_context yield, uint8_t proto, uint16_t protoSpecific, |
| uint32_t transferLength) |
| { |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| using callback_t = |
| void(std::tuple<std::error_code, int, std::vector<uint8_t>>); |
| auto [err, nvmeStatus, data] = |
| boost::asio::async_initiate<boost::asio::yield_context, callback_t>( |
| [intf{nvmeIntf}, ctrl{nvmeCtrl}, proto, protoSpecific, |
| transferLength](auto&& handler) { |
| auto h = asio_helper::CopyableCallback( |
| std::forward<decltype(handler)>(handler)); |
| |
| intf->adminSecurityReceive(ctrl, proto, protoSpecific, transferLength, |
| [h](const std::error_code& err, |
| int nvmeStatus, |
| std::span<uint8_t> data) mutable { |
| std::vector<uint8_t> d(data.begin(), data.end()); |
| h(std::make_tuple(err, nvmeStatus, d)); |
| }); |
| }, |
| yield); |
| |
| // exception must be thrown outside of the async block |
| checkLibNVMeError(err, nvmeStatus, "SecurityReceive"); |
| return data; |
| } |
| |
| std::tuple<uint32_t, uint32_t, uint32_t> |
| NVMeControllerEnabled::adminNonDataCmdMethod( |
| boost::asio::yield_context yield, uint8_t opcode, uint32_t cdw1, |
| uint32_t cdw2, uint32_t cdw3, uint32_t cdw10, uint32_t cdw11, |
| uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, uint32_t cdw15) |
| { |
| using callback_t = void(std::tuple<std::error_code, int, uint32_t>); |
| auto [err, nvmeStatus, completionDw0] = |
| boost::asio::async_initiate<boost::asio::yield_context, callback_t>( |
| [intf{nvmeIntf}, ctrl{nvmeCtrl}, opcode, cdw1, cdw2, cdw3, cdw10, |
| cdw11, cdw12, cdw13, cdw14, cdw15](auto&& handler) { |
| auto h = asio_helper::CopyableCallback( |
| std::forward<decltype(handler)>(handler)); |
| |
| intf->adminNonDataCmd(ctrl, opcode, cdw1, cdw2, cdw3, cdw10, cdw11, |
| cdw12, cdw13, cdw14, cdw15, |
| [h](const std::error_code& err, int nvmeStatus, |
| uint32_t completionDw0) mutable { |
| h(std::make_tuple(err, nvmeStatus, completionDw0)); |
| }); |
| }, |
| yield); |
| |
| std::cerr << "nvme_status:" << nvmeStatus << ", dw0:" << completionDw0 |
| << '\n'; |
| if (nvmeStatus < 0) |
| { |
| throw sdbusplus::exception::SdBusError(err.value(), |
| "adminNonDataCmdMethod"); |
| } |
| |
| // Parse MI status Or MI status from nvme_status |
| uint32_t miStatus = 0; |
| uint32_t adminStatus = 0; |
| if (nvme_status_get_type(nvmeStatus) == NVME_STATUS_TYPE_MI) |
| { |
| // there is no Admin status and dw0 if MI layer failed. |
| miStatus = nvme_status_get_value(nvmeStatus); |
| adminStatus = 0; |
| completionDw0 = 0; |
| } |
| else |
| { |
| miStatus = 0; |
| adminStatus = nvme_status_get_value(nvmeStatus); |
| } |
| return {miStatus, adminStatus, completionDw0}; |
| } |
| |
| void NVMeControllerEnabled::attachVolume( |
| boost::asio::yield_context yield, |
| const sdbusplus::message::object_path& volumePath) |
| { |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| uint32_t nsid = 0; |
| if (auto s = subsys.lock()) |
| { |
| auto vol = s->getVolume(volumePath); |
| if (!vol) |
| { |
| throw sdbusplus::exception::SdBusError(ENOENT, "attachVolume"); |
| } |
| nsid = vol->namespaceId(); |
| } |
| else |
| { |
| return; |
| } |
| |
| using callback_t = void(std::tuple<std::error_code, int>); |
| uint16_t ctrlid = getCntrlId(); |
| auto [err, nvmeStatus] = |
| boost::asio::async_initiate<boost::asio::yield_context, callback_t>( |
| [intf{nvmeIntf}, ctrl{nvmeCtrl}, ctrlid, nsid](auto&& handler) { |
| auto h = asio_helper::CopyableCallback( |
| std::forward<decltype(handler)>(handler)); |
| |
| intf->adminAttachDetachNamespace( |
| ctrl, ctrlid, nsid, true, |
| [h](const std::error_code& err, int nvmeStatus) mutable { |
| h(std::make_tuple(err, nvmeStatus)); |
| }); |
| }, yield); |
| |
| // exception must be thrown outside of the async block |
| checkLibNVMeError(err, nvmeStatus, "attachVolume"); |
| |
| if (status == Status::Enabled) |
| { |
| if (auto s = subsys.lock()) |
| { |
| try |
| { |
| s->attachCtrlVolume(getCntrlId(), nsid); |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << e.what() << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| InternalFailure(); |
| } |
| } |
| updateAssociation(); |
| } |
| } |
| |
| void NVMeControllerEnabled::detachVolume( |
| boost::asio::yield_context yield, |
| const sdbusplus::message::object_path& volumePath) |
| { |
| if (status != Status::Enabled) |
| { |
| std::cerr << "Controller has been disabled" << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable(); |
| } |
| |
| uint32_t nsid = 0; |
| if (auto s = subsys.lock()) |
| { |
| auto vol = s->getVolume(volumePath); |
| if (!vol) |
| { |
| throw sdbusplus::exception::SdBusError(ENOENT, "detachVolume"); |
| } |
| nsid = vol->namespaceId(); |
| } |
| else |
| { |
| return; |
| } |
| |
| using callback_t = void(std::tuple<std::error_code, int>); |
| uint16_t ctrlid = getCntrlId(); |
| auto [err, nvmeStatus] = |
| boost::asio::async_initiate<boost::asio::yield_context, callback_t>( |
| [intf{nvmeIntf}, ctrl{nvmeCtrl}, ctrlid, nsid](auto&& handler) { |
| auto h = asio_helper::CopyableCallback( |
| std::forward<decltype(handler)>(handler)); |
| |
| intf->adminAttachDetachNamespace( |
| ctrl, ctrlid, nsid, false, |
| [h](const std::error_code& err, int nvmeStatus) mutable { |
| h(std::make_tuple(err, nvmeStatus)); |
| }); |
| }, yield); |
| |
| // exception must be thrown outside of the async block |
| checkLibNVMeError(err, nvmeStatus, "detachVolume"); |
| |
| if (status == Status::Enabled) |
| { |
| if (auto s = subsys.lock()) |
| { |
| try |
| { |
| s->detachCtrlVolume(getCntrlId(), nsid); |
| } |
| catch (const std::exception& e) |
| { |
| std::cerr << e.what() << '\n'; |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| InternalFailure(); |
| } |
| } |
| updateAssociation(); |
| } |
| } |