diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces')
3 files changed, 498 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..46e94e339 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-support-for-StandBySpare.patch @@ -0,0 +1,462 @@ +From 461da7ec950704a1f5bcc7f6527ed8ca119cfaf9 Mon Sep 17 00:00:00 2001 +From: Vikram Bodireddy <vikram.bodireddy@intel.com> +Date: Tue, 24 Mar 2020 16:05:32 +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. + +Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com> +--- + redfish-core/lib/update_service.hpp | 274 +++++++++++++++++++++++++++++++----- + 1 file changed, 241 insertions(+), 33 deletions(-) + +diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp +index e9793eb..a913bac 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,119 @@ 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: error_code = " ++ << error_code; ++ BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); ++ } ++ }, ++ 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, + const crow::Request &req) + { +@@ -70,25 +173,27 @@ 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. obj path = " << 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}, + req](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 ++ << "GetSoftwareObject path failed: error_code = " ++ << error_code; + BMCWEB_LOG_DEBUG << "error msg = " + << error_code.message(); + if (asyncResp) +@@ -115,7 +220,7 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, + // is added + fwAvailableTimer = nullptr; + +- activateImage(objPath.str, objInfo[0].first); ++ activateImage(objPath.str, objInfo[0].first, imgTargets); + if (asyncResp) + { + std::shared_ptr<task::TaskData> task = +@@ -196,17 +301,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) +@@ -246,9 +350,10 @@ static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, + } + }); + +- auto callback = [asyncResp, req](sdbusplus::message::message &m) { ++ auto callback = [asyncResp, imgTargets{imgUriTargets}, ++ req](sdbusplus::message::message &m) { + BMCWEB_LOG_DEBUG << "Match fired"; +- softwareInterfaceAdded(asyncResp, m, req); ++ softwareInterfaceAdded(asyncResp, imgTargets, m, req); + }; + + fwUpdateInProgress = true; +@@ -358,9 +463,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 +@@ -394,7 +502,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"}}}, +@@ -406,6 +515,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 + { +@@ -416,6 +528,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"] = { +@@ -475,9 +589,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; + } + +@@ -545,6 +664,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, +@@ -555,7 +766,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/" + +@@ -641,8 +852,7 @@ class SoftwareInventoryCollection : public Node + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/software", static_cast<int32_t>(0), +- std::array<const char *, 1>{ +- "xyz.openbmc_project.Software.Version"}); ++ std::array<const char *, 1>{versionIntf}); + } + }; + +@@ -825,7 +1035,7 @@ class SoftwareInventory : public Node + }, + obj.second[0].first, obj.first, + "org.freedesktop.DBus.Properties", "GetAll", +- "xyz.openbmc_project.Software.Version"); ++ versionIntf); + } + if (!found) + { +@@ -846,9 +1056,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..2e59a7b78 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -0,0 +1,27 @@ +# todo(james) remove nobranch +SRC_URI = "git://github.com/openbmc/bmcweb.git" +SRCREV = "e5aaf047b6b41b0837ef0846cf5356c9a6bcb030" + +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 PFR support +EXTRA_OECMAKE += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '-DBMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE=ON', '', d)}" + +# Enable NBD_PROXY +EXTRA_OECMAKE += " -DBMCWEB_ENABLE_VM_NBDPROXY=ON" + +# Disable MTLS until it passes security review +EXTRA_OECMAKE += " -DBMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION=OFF" + +# Enable Validation unsecure based on IMAGE_FEATURES +EXTRA_OECMAKE += "${@bb.utils.contains('EXTRA_IMAGE_FEATURES', 'validation-unsecure', '-DBMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE=ON', '', d)}" + |