diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces')
3 files changed, 519 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-support-for-StandBySpare.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-support-for-StandBySpare.patch new file mode 100644 index 000000000..9e7cdf768 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-support-for-StandBySpare.patch @@ -0,0 +1,484 @@ +From ba9d7f8443716887bc101e300b06c570f7da8159 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@linux.intel.com> +Date: Thu, 5 Dec 2019 15:13:52 +0530 +Subject: [PATCH] Firmware update support for StandBySpare + +Firmware update support for StandBySpare. This will +have support for adding 'HttpPushUriTargets' and +'HttpPushUriTargetsBusy' attributes. These attributes enables +'HttpPushUri' to distinguish between the firmware update targets. + +Tested: + - GET on "/redfish/v1/UpdateService", got below response +......... + "HttpPushUriTargets": [], + "HttpPushUriTargetsBusy": false +........ + + - PATCH on "/redfish/v1/UpdateService" and works fine. +{ + "HttpPushUriTargets": ["bmc_recovery"], + "HttpPushUriTargetsBusy": true +} + + - Did Firmware update and verified end to end functionality + for both bmc active and backup images. + + - Successfully ran redfish validater with no new errors. + +Change-Id: I59f317ac001ebf56bbf30e7f43dbec5d69fa249a +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@linux.intel.com> +--- + redfish-core/lib/update_service.hpp | 285 ++++++++++++++++++++++++++++++------ + 1 file changed, 241 insertions(+), 44 deletions(-) + +diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp +index 57dcc07..c189d5a 100644 +--- a/redfish-core/lib/update_service.hpp ++++ b/redfish-core/lib/update_service.hpp +@@ -30,6 +30,17 @@ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; + static bool fwUpdateInProgress = false; + // Timer for software available + static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer; ++static constexpr const char *versionIntf = ++ "xyz.openbmc_project.Software.Version"; ++static constexpr const char *activationIntf = ++ "xyz.openbmc_project.Software.Activation"; ++static constexpr const char *reqActivationPropName = "RequestedActivation"; ++static constexpr const char *reqActivationsActive = ++ "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"; ++static constexpr const char *reqActivationsStandBySpare = ++ "xyz.openbmc_project.Software.Activation.RequestedActivations.StandbySpare"; ++static constexpr const char *activationsStandBySpare = ++ "xyz.openbmc_project.Software.Activation.Activations.StandbySpare"; + + static void cleanUp() + { +@@ -37,27 +48,118 @@ static void cleanUp() + fwUpdateMatcher = nullptr; + } + static void activateImage(const std::string &objPath, +- const std::string &service) ++ const std::string &service, ++ const std::vector<std::string> &imgUriTargets) + { + BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service; ++ ++ // If targets is empty, it will apply to the active. ++ if (imgUriTargets.size() == 0) ++ { ++ crow::connections::systemBus->async_method_call( ++ [](const boost::system::error_code error_code) { ++ if (error_code) ++ { ++ BMCWEB_LOG_DEBUG << "RequestedActivation failed: ec = " ++ << error_code; ++ } ++ }, ++ service, objPath, "org.freedesktop.DBus.Properties", "Set", ++ activationIntf, reqActivationPropName, ++ std::variant<std::string>(reqActivationsActive)); ++ return; ++ } ++ ++ // TODO: Now we support only one target becuase software-manager ++ // code support one activation per object. It will be enhanced ++ // to multiple targets for single image in future. For now, ++ // consider first target alone. + crow::connections::systemBus->async_method_call( +- [](const boost::system::error_code error_code) { +- if (error_code) ++ [objPath, service, imgTarget{imgUriTargets[0]}]( ++ const boost::system::error_code ec, ++ const crow::openbmc_mapper::GetSubTreeType &subtree) { ++ if (ec || !subtree.size()) + { +- BMCWEB_LOG_DEBUG << "error_code = " << error_code; +- BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); ++ return; ++ } ++ ++ for (const auto &[invObjPath, invDict] : subtree) ++ { ++ std::size_t idPos = invObjPath.rfind("/"); ++ if ((idPos == std::string::npos) || ++ ((idPos + 1) >= invObjPath.size())) ++ { ++ BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!"; ++ return; ++ } ++ std::string swId = invObjPath.substr(idPos + 1); ++ ++ if (swId != imgTarget) ++ { ++ continue; ++ } ++ ++ if (invDict.size() < 1) ++ { ++ continue; ++ } ++ BMCWEB_LOG_DEBUG << "Image target matched with object " ++ << invObjPath; ++ crow::connections::systemBus->async_method_call( ++ [objPath, ++ service](const boost::system::error_code error_code, ++ const std::variant<std::string> value) { ++ if (error_code) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Error in querying activation value"; ++ // not all fwtypes are updateable, ++ // this is ok ++ return; ++ } ++ std::string activationValue = ++ std::get<std::string>(value); ++ BMCWEB_LOG_DEBUG << "Activation Value: " ++ << activationValue; ++ std::string reqActivation = reqActivationsActive; ++ if (activationValue == activationsStandBySpare) ++ { ++ reqActivation = reqActivationsStandBySpare; ++ } ++ BMCWEB_LOG_DEBUG ++ << "Setting RequestedActivation value as " ++ << reqActivation << " for " << service << " " ++ << objPath; ++ crow::connections::systemBus->async_method_call( ++ [](const boost::system::error_code error_code) { ++ if (error_code) ++ { ++ BMCWEB_LOG_DEBUG ++ << "RequestedActivation failed: ec = " ++ << error_code; ++ } ++ return; ++ }, ++ service, objPath, "org.freedesktop.DBus.Properties", ++ "Set", activationIntf, reqActivationPropName, ++ std::variant<std::string>(reqActivation)); ++ }, ++ invDict[0].first, ++ "/xyz/openbmc_project/software/" + imgTarget, ++ "org.freedesktop.DBus.Properties", "Get", activationIntf, ++ "Activation"); + } + }, +- service, objPath, "org.freedesktop.DBus.Properties", "Set", +- "xyz.openbmc_project.Software.Activation", "RequestedActivation", +- std::variant<std::string>( +- "xyz.openbmc_project.Software.Activation.RequestedActivations." +- "Active")); ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", ++ static_cast<int32_t>(0), std::array<const char *, 1>{versionIntf}); + } + + // Note that asyncResp can be either a valid pointer or nullptr. If nullptr + // then no asyncResp updates will occur + static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, ++ const std::vector<std::string> imgUriTargets, + sdbusplus::message::message &m) + { + std::vector<std::pair< +@@ -69,27 +171,24 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, + + m.read(objPath, interfacesProperties); + +- BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; ++ BMCWEB_LOG_DEBUG << "Software Interface Added. objPath = " << objPath.str; + for (auto &interface : interfacesProperties) + { +- BMCWEB_LOG_DEBUG << "interface = " << interface.first; +- +- if (interface.first == "xyz.openbmc_project.Software.Activation") ++ if (interface.first == activationIntf) + { + // Found our interface, disable callbacks + fwUpdateMatcher = nullptr; +- + // Retrieve service and activate + crow::connections::systemBus->async_method_call( +- [objPath, asyncResp]( ++ [objPath, asyncResp, imgTargets{imgUriTargets}]( + const boost::system::error_code error_code, + const std::vector<std::pair< + std::string, std::vector<std::string>>> &objInfo) { + if (error_code) + { +- BMCWEB_LOG_DEBUG << "error_code = " << error_code; +- BMCWEB_LOG_DEBUG << "error msg = " +- << error_code.message(); ++ BMCWEB_LOG_DEBUG ++ << "GetSoftwareObject path failed: ec = " ++ << error_code; + if (asyncResp) + { + messages::internalError(asyncResp->res); +@@ -113,8 +212,7 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, + // xyz.openbmc_project.Software.Activation interface + // is added + fwAvailableTimer = nullptr; +- +- activateImage(objPath.str, objInfo[0].first); ++ activateImage(objPath.str, objInfo[0].first, imgTargets); + if (asyncResp) + { + redfish::messages::success(asyncResp->res); +@@ -124,17 +222,16 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str, +- std::array<const char *, 1>{ +- "xyz.openbmc_project.Software.Activation"}); ++ std::array<const char *, 1>{activationIntf}); + } + } + } + + // Note that asyncResp can be either a valid pointer or nullptr. If nullptr + // then no asyncResp updates will occur +-static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, +- const crow::Request &req, +- int timeoutTimeSeconds = 5) ++static void monitorForSoftwareAvailable( ++ std::shared_ptr<AsyncResp> asyncResp, const crow::Request &req, ++ const std::vector<std::string> &imgUriTargets, int timeoutTimeSeconds = 5) + { + // Only allow one FW update at a time + if (fwUpdateInProgress != false) +@@ -145,7 +242,6 @@ static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, + } + return; + } +- + fwAvailableTimer = + std::make_unique<boost::asio::steady_timer>(*req.ioService); + +@@ -174,10 +270,10 @@ static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, + } + }); + +- auto callback = [asyncResp](sdbusplus::message::message &m) { +- BMCWEB_LOG_DEBUG << "Match fired"; +- softwareInterfaceAdded(asyncResp, m); +- }; ++ auto callback = ++ [asyncResp, imgTargets{imgUriTargets}](sdbusplus::message::message &m) { ++ softwareInterfaceAdded(asyncResp, imgTargets, m); ++ }; + + fwUpdateInProgress = true; + +@@ -286,9 +382,12 @@ class UpdateServiceActionsSimpleUpdate : public Node + std::string fwFile = imageURI.substr(separator + 1); + BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile; + ++ // We will pass empty targets and its handled in activation. ++ std::vector<std::string> httpUriTargets; ++ + // Setup callback for when new software detected + // Give TFTP 2 minutes to complete +- monitorForSoftwareAvailable(nullptr, req, 120); ++ monitorForSoftwareAvailable(nullptr, req, httpUriTargets, 120); + + // TFTP can take up to 2 minutes depending on image size and + // connection speed. Return to caller as soon as the TFTP operation +@@ -322,7 +421,8 @@ class UpdateServiceActionsSimpleUpdate : public Node + class UpdateService : public Node + { + public: +- UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") ++ UpdateService(CrowApp &app) : ++ Node(app, "/redfish/v1/UpdateService/"), httpPushUriTargetBusy(false) + { + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, +@@ -334,6 +434,9 @@ class UpdateService : public Node + } + + private: ++ std::vector<std::string> httpPushUriTargets; ++ bool httpPushUriTargetBusy; ++ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { +@@ -346,6 +449,8 @@ class UpdateService : public Node + res.jsonValue["Description"] = "Service for Software Update"; + res.jsonValue["Name"] = "Update Service"; + res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService"; ++ res.jsonValue["HttpPushUriTargets"] = httpPushUriTargets; ++ res.jsonValue["HttpPushUriTargetsBusy"] = httpPushUriTargetBusy; + // UpdateService cannot be disabled + res.jsonValue["ServiceEnabled"] = true; + res.jsonValue["FirmwareInventory"] = { +@@ -405,9 +510,14 @@ class UpdateService : public Node + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + std::optional<nlohmann::json> pushUriOptions; +- if (!json_util::readJson(req, res, "HttpPushUriOptions", +- pushUriOptions)) ++ std::optional<std::vector<std::string>> imgTargets; ++ std::optional<bool> imgTargetBusy; ++ ++ if (!json_util::readJson(req, res, "HttpPushUriOptions", pushUriOptions, ++ "HttpPushUriTargets", imgTargets, ++ "HttpPushUriTargetsBusy", imgTargetBusy)) + { ++ BMCWEB_LOG_DEBUG << "UpdateService doPatch: Invalid request body"; + return; + } + +@@ -464,7 +574,6 @@ class UpdateService : public Node + messages::internalError(asyncResp->res); + return; + } +- messages::success(asyncResp->res); + }, + "xyz.openbmc_project.Settings", + "/xyz/openbmc_project/software/apply_time", +@@ -475,6 +584,98 @@ class UpdateService : public Node + } + } + } ++ ++ if (imgTargetBusy) ++ { ++ if ((httpPushUriTargetBusy) && (*imgTargetBusy)) ++ { ++ BMCWEB_LOG_DEBUG ++ << "Other client has reserved the HttpPushUriTargets " ++ "property for firmware updates."; ++ messages::resourceInUse(asyncResp->res); ++ return; ++ } ++ ++ if (imgTargets) ++ { ++ if (!(*imgTargetBusy)) ++ { ++ BMCWEB_LOG_DEBUG ++ << "UpdateService doPatch: httpPushUriTargetBusy " ++ "should be " ++ "true before setting httpPushUriTargets"; ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargetsBusy"); ++ return; ++ } ++ if ((*imgTargets).size() != 0) ++ { ++ // TODO: Now we support max one target becuase ++ // software-manager code support one activation per object. ++ // It will be enhanced to multiple targets for single image ++ // in future. For now, consider first target alone. ++ if ((*imgTargets).size() != 1) ++ { ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargets"); ++ return; ++ } ++ crow::connections::systemBus->async_method_call( ++ [this, asyncResp, uriTargets{*imgTargets}, ++ targetBusy{*imgTargetBusy}]( ++ const boost::system::error_code ec, ++ const std::vector<std::string> swInvPaths) { ++ if (ec) ++ { ++ return; ++ } ++ ++ bool swInvObjFound = false; ++ for (const std::string &path : swInvPaths) ++ { ++ std::size_t idPos = path.rfind("/"); ++ if ((idPos == std::string::npos) || ++ ((idPos + 1) >= path.size())) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_DEBUG ++ << "Can't parse firmware ID!!"; ++ return; ++ } ++ std::string swId = path.substr(idPos + 1); ++ ++ if (swId == uriTargets[0]) ++ { ++ swInvObjFound = true; ++ break; ++ } ++ } ++ if (!swInvObjFound) ++ { ++ messages::invalidObject(asyncResp->res, ++ "HttpPushUriTargets"); ++ return; ++ } ++ this->httpPushUriTargetBusy = targetBusy; ++ this->httpPushUriTargets = uriTargets; ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", ++ "/", static_cast<int32_t>(0), ++ std::array<const char *, 1>{versionIntf}); ++ } ++ else ++ { ++ httpPushUriTargetBusy = *imgTargetBusy; ++ httpPushUriTargets = *imgTargets; ++ } ++ } ++ else ++ { ++ httpPushUriTargetBusy = *imgTargetBusy; ++ } ++ } + } + + void doPost(crow::Response &res, const crow::Request &req, +@@ -485,7 +686,7 @@ class UpdateService : public Node + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + // Setup callback for when new software detected +- monitorForSoftwareAvailable(asyncResp, req); ++ monitorForSoftwareAvailable(asyncResp, req, httpPushUriTargets); + + std::string filepath( + "/tmp/images/" + +@@ -569,9 +770,7 @@ class SoftwareInventoryCollection : public Node + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", +- static_cast<int32_t>(0), +- std::array<const char *, 1>{ +- "xyz.openbmc_project.Software.Version"}); ++ static_cast<int32_t>(0), std::array<const char *, 1>{versionIntf}); + } + }; + +@@ -756,7 +955,7 @@ class SoftwareInventory : public Node + }, + obj.second[0].first, obj.first, + "org.freedesktop.DBus.Properties", "GetAll", +- "xyz.openbmc_project.Software.Version"); ++ versionIntf); + } + if (!found) + { +@@ -778,9 +977,7 @@ class SoftwareInventory : public Node + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", +- static_cast<int32_t>(0), +- std::array<const char *, 1>{ +- "xyz.openbmc_project.Software.Version"}); ++ static_cast<int32_t>(0), std::array<const char *, 1>{versionIntf}); + } + }; + +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket new file mode 100644 index 000000000..8782e4dd3 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket @@ -0,0 +1,9 @@ +[Unit] +Description=BMC Webserver socket + +[Socket] +ListenStream=443 +ReusePort=true + +[Install] +WantedBy=sockets.target diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend new file mode 100644 index 000000000..00eb38799 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -0,0 +1,26 @@ +SRC_URI = "git://github.com/openbmc/bmcweb.git" +SRCREV = "97d57aaa0b95a110c71016d190f95f853051126a" + +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +# add a user called bmcweb for the server to assume +# bmcweb is part of group shadow for non-root pam authentication +USERADD_PARAM_${PN} = "-r -s /usr/sbin/nologin -d /home/bmcweb -m -G shadow bmcweb" + +GROUPADD_PARAM_${PN} = "web; redfish " + +SRC_URI += "file://0001-Firmware-update-support-for-StandBySpare.patch \ + " + +# Enable CPU Log and Raw PECI support +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_CPU_LOG=ON" +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_RAW_PECI=ON" + +# Enable Redfish BMC Journal support +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_REDFISH_BMC_JOURNAL=ON" + +# Enable PFR support +EXTRA_OECMAKE += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '-DBMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE=ON', '', d)}" + +#Disable the Dbus interface +EXTRA_OECMAKE += "-DBMCWEB_ENABLE_DBUS_REST=OFF" |