summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch25
-rwxr-xr-xmeta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch696
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Invalid-status-code-from-InsertMedia-REST-methods.patch182
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch74
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch40
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Bmcweb-handle-permission-denied-exception.patch44
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch219
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch547
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-Fix-unmounting-image-in-proxy-mode.patch32
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch850
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch153
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0008-Add-support-to-ResetBios-action.patch62
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch139
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch57
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch135
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch67
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch59
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch75
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch98
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch50
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-bmcweb-crashes-if-socket-directory-not-present.patch44
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-comparison-for-proxy-legacy-mode.patch51
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket9
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch543
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch401
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch477
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch627
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch243
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README19
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch369
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch295
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README10
32 files changed, 6692 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch
new file mode 100644
index 000000000..ec6d70df1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch
@@ -0,0 +1,25 @@
+From 4af788655c5b5a5fae4d85b365a70dc619810fe0 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Thu, 11 Feb 2021 08:35:41 +0000
+Subject: [PATCH] Add ConnectedVia property to virtual media item template
+
+Tested: Verified that ConnectedVia property is returned and set to
+ "NotConnected" for disconnected media.
+
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 188248a..80e7315 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -192,6 +192,7 @@ static nlohmann::json vmItemTemplate(const std::string& name,
+ item["@odata.id"] =
+ "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName;
+ item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
++ item["ConnectedVia"] = "NotConnected";
+ item["Name"] = "Virtual Removable Media";
+ item["Id"] = resName;
+ item["WriteProtected"] = true;
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
new file mode 100755
index 000000000..193461baf
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
@@ -0,0 +1,696 @@
+From 10cb7cb14974725a29b3ead4c543ca5e58234c07 Mon Sep 17 00:00:00 2001
+From: Vikram Bodireddy <vikram.bodireddy@intel.com>
+Date: Wed, 18 Nov 2020 17:14:41 +0530
+Subject: [PATCH] Firmware update configuration changes
+
+This commit will provide user to PATCH the below firmware update
+attributes before uploding the firmware image.
+
+1. This will have PATCH support for 'HttpPushUriTargets' and
+'HttpPushUriTargetsBusy' attributes. These attributes enables
+'HttpPushUri' to distinguish between the firmware update targets.
+
+2. ApplyOptions are used to specify firmware update specific options
+such as ClearConfig which is used while activating the updated
+firmware. This setting is maintained in a local static variable
+when set using PATCH method. Its used in activate image as input
+parameter. This attribute is added as Oem as the default
+UpdateService interface doesn't specify any relevant or appropriate
+attribute for this.
+
+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.
+
+ - Tested setting ClearConfig to true or false using PATCH
+ method.
+
+ - Successfully ran redfish validater with no new errors.
+
+Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com>
+
+%% original patch: 0001-Firmware-update-configuration-changes.patch
+
+Change-Id: I44e1743fd76aa37c7b8affa49a3e05f808187037
+Signed-off-by: Helen Huang <he.huang@intel.com>
+---
+ redfish-core/lib/update_service.hpp | 339 ++++++++++++++++--
+ static/redfish/v1/$metadata/index.xml | 3 +
+ .../JsonSchemas/OemUpdateService/index.json | 69 ++++
+ .../redfish/v1/schema/OemUpdateService_v1.xml | 40 +++
+ 4 files changed, 421 insertions(+), 30 deletions(-)
+ create mode 100644 static/redfish/v1/JsonSchemas/OemUpdateService/index.json
+ create mode 100644 static/redfish/v1/schema/OemUpdateService_v1.xml
+
+diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
+index 6d44171..8eda265 100644
+--- a/redfish-core/lib/update_service.hpp
++++ b/redfish-core/lib/update_service.hpp
+@@ -32,6 +32,17 @@ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
+ 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()
+ {
+@@ -40,27 +51,119 @@ static void cleanUp()
+ fwUpdateErrorMatcher = 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 errorCode) {
+- if (errorCode)
++ [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 = " << errorCode;
+- BMCWEB_LOG_DEBUG << "error msg = " << errorCode.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(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::vector<std::string> imgUriTargets,
+ sdbusplus::message::message& m,
+ const crow::Request& req)
+ {
+@@ -73,22 +176,24 @@ static void softwareInterfaceAdded(const 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)
+ {
+ // Retrieve service and activate
+ crow::connections::systemBus->async_method_call(
+- [objPath, asyncResp,
++ [objPath, asyncResp, imgTargets{imgUriTargets},
+ req](const boost::system::error_code errorCode,
+ const std::vector<std::pair<
+ std::string, std::vector<std::string>>>& objInfo) {
+ if (errorCode)
+ {
+- BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
++ BMCWEB_LOG_DEBUG
++ << "GetSoftwareObject path failed: error_code = "
++ << errorCode;
+ BMCWEB_LOG_DEBUG << "error msg = "
+ << errorCode.message();
+ if (asyncResp)
+@@ -115,7 +220,7 @@ static void softwareInterfaceAdded(const 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 =
+@@ -247,8 +352,7 @@ static void softwareInterfaceAdded(const 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});
+ }
+ }
+ }
+@@ -257,7 +361,8 @@ static void softwareInterfaceAdded(const std::shared_ptr<AsyncResp>& asyncResp,
+ // then no asyncResp updates will occur
+ static void monitorForSoftwareAvailable(
+ const std::shared_ptr<AsyncResp>& asyncResp, const crow::Request& req,
+- const std::string& url, int timeoutTimeSeconds = 10)
++ const std::string& url, const std::vector<std::string>& imgUriTargets,
++ int timeoutTimeSeconds = 10)
+ {
+ // Only allow one FW update at a time
+ if (fwUpdateInProgress != false)
+@@ -297,9 +402,10 @@ static void monitorForSoftwareAvailable(
+ }
+ });
+
+- 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;
+@@ -475,12 +581,15 @@ 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 10 minutes to complete
+ monitorForSoftwareAvailable(
+ asyncResp, req,
+ "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
+- 600);
++ httpUriTargets, 600);
+
+ // TFTP can take up to 10 minutes depending on image size and
+ // connection speed. Return to caller as soon as the TFTP operation
+@@ -514,7 +623,8 @@ class UpdateServiceActionsSimpleUpdate : public Node
+ class UpdateService : public Node
+ {
+ public:
+- UpdateService(App& app) : Node(app, "/redfish/v1/UpdateService/")
++ UpdateService(App& app) :
++ Node(app, "/redfish/v1/UpdateService/"), httpPushUriTargetBusy(false)
+ {
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+@@ -526,6 +636,8 @@ class UpdateService : public Node
+ }
+
+ private:
++ std::vector<std::string> httpPushUriTargets;
++ bool httpPushUriTargetBusy;
+ void doGet(crow::Response& res, const crow::Request&,
+ const std::vector<std::string>&) override
+ {
+@@ -536,6 +648,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"] = {
+@@ -585,6 +699,31 @@ class UpdateService : public Node
+ "/xyz/openbmc_project/software/apply_time",
+ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
++
++ // Get the ApplyOptions value
++ crow::connections::systemBus->async_method_call(
++ [aResp](const boost::system::error_code ec,
++ const std::variant<bool> applyOption) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
++ messages::internalError(aResp->res);
++ return;
++ }
++
++ const bool* b = std::get_if<bool>(&applyOption);
++
++ if (b)
++ {
++ aResp->res.jsonValue["Oem"]["ApplyOptions"]["@odata.type"] =
++ "#OemUpdateService.ApplyOptions";
++ aResp->res.jsonValue["Oem"]["ApplyOptions"]["ClearConfig"] =
++ *b;
++ }
++ },
++ "xyz.openbmc_project.Software.BMC.Updater",
++ "/xyz/openbmc_project/software", "org.freedesktop.DBus.Properties",
++ "Get", "xyz.openbmc_project.Software.ApplyOptions", "ClearConfig");
+ }
+
+ void doPatch(crow::Response& res, const crow::Request& req,
+@@ -595,12 +734,61 @@ 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;
++ std::optional<nlohmann::json> oemProps;
++
++ if (!json_util::readJson(req, res, "HttpPushUriOptions", pushUriOptions,
++ "HttpPushUriTargets", imgTargets,
++ "HttpPushUriTargetsBusy", imgTargetBusy, "Oem",
++ oemProps))
+ {
++ BMCWEB_LOG_DEBUG << "UpdateService doPatch: Invalid request body";
+ return;
+ }
+
++ if (oemProps)
++ {
++ std::optional<nlohmann::json> applyOptions;
++
++ if (!json_util::readJson(*oemProps, res, "ApplyOptions",
++ applyOptions))
++ {
++ return;
++ }
++
++ if (applyOptions)
++ {
++ std::optional<bool> clearConfig;
++ if (!json_util::readJson(*applyOptions, res, "ClearConfig",
++ clearConfig))
++ {
++ return;
++ }
++
++ if (clearConfig)
++ {
++ // Set the requested image apply time value
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "D-Bus responses error: "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ messages::success(asyncResp->res);
++ },
++ "xyz.openbmc_project.Software.BMC.Updater",
++ "/xyz/openbmc_project/software",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Software.ApplyOptions",
++ "ClearConfig", std::variant<bool>{*clearConfig});
++ }
++ }
++ }
++
+ if (pushUriOptions)
+ {
+ std::optional<nlohmann::json> pushUriApplyTime;
+@@ -665,6 +853,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,
+@@ -675,8 +955,8 @@ class UpdateService : public Node
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+
+ // Setup callback for when new software detected
+- monitorForSoftwareAvailable(asyncResp, req,
+- "/redfish/v1/UpdateService");
++ monitorForSoftwareAvailable(asyncResp, req, "/redfish/v1/UpdateService",
++ httpPushUriTargets);
+
+ std::string filepath(
+ "/tmp/images/" +
+@@ -761,7 +1041,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});
+ }
+ };
+
+@@ -943,7 +1223,7 @@ class SoftwareInventory : public Node
+ },
+ obj.second[0].first, obj.first,
+ "org.freedesktop.DBus.Properties", "GetAll",
+- "xyz.openbmc_project.Software.Version");
++ versionIntf);
+ }
+ if (!found)
+ {
+@@ -964,8 +1244,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});
+ }
+ };
+
+diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml
+index 514f3dd..c068d4f 100644
+--- a/static/redfish/v1/$metadata/index.xml
++++ b/static/redfish/v1/$metadata/index.xml
+@@ -2142,6 +2142,9 @@
+ <edmx:Reference Uri="/redfish/v1/schema/OemManager_v1.xml">
+ <edmx:Include Namespace="OemManager"/>
+ </edmx:Reference>
++ <edmx:Reference Uri="/redfish/v1/schema/OemUpdateService_v1.xml">
++ <edmx:Include Namespace="OemUpdateService"/>
++ </edmx:Reference>
+ <edmx:Reference Uri="/redfish/v1/schema/OemCrashdump_v1.xml">
+ <edmx:Include Namespace="OemCrashdump.v1_0_0"/>
+ </edmx:Reference>
+diff --git a/static/redfish/v1/JsonSchemas/OemUpdateService/index.json b/static/redfish/v1/JsonSchemas/OemUpdateService/index.json
+new file mode 100644
+index 0000000..74e39cd
+--- /dev/null
++++ b/static/redfish/v1/JsonSchemas/OemUpdateService/index.json
+@@ -0,0 +1,69 @@
++{
++ "$id": "http://redfish.dmtf.org/schemas/v1/OemUpdateService.json",
++ "$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json",
++ "copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright",
++ "definitions": {
++ "ApplyOptions": {
++ "additionalProperties": false,
++ "description": "An indication by boolean value whether to update firmware configuration along with firmware image update.",
++ "patternProperties": {
++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
++ "description": "This property shall specify a valid odata or Redfish property.",
++ "type": [
++ "array",
++ "boolean",
++ "integer",
++ "number",
++ "null",
++ "object",
++ "string"
++ ]
++ }
++ },
++ "properties": {
++ "ClearConfig": {
++ "description": "This indicates whether to update firmware configuration or not.",
++ "longDescription": "The value of this property is used to indicate the firmware configuration update.",
++ "readonly": false,
++ "type": [
++ "boolean",
++ "null"
++ ]
++ }
++ },
++ "type": "object"
++ },
++ "Oem": {
++ "additionalProperties": true,
++ "description": "OemUpdateService Oem properties.",
++ "patternProperties": {
++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
++ "description": "This property shall specify a valid odata or Redfish property.",
++ "type": [
++ "array",
++ "boolean",
++ "integer",
++ "number",
++ "null",
++ "object",
++ "string"
++ ]
++ }
++ },
++ "properties": {
++ "ApplyOptions": {
++ "anyOf": [
++ {
++ "$ref": "#/definitions/ApplyOptions"
++ },
++ {
++ "type": "null"
++ }
++ ]
++ }
++ },
++ "type": "object"
++ }
++ },
++ "title": "#OemUpdateService"
++}
+diff --git a/static/redfish/v1/schema/OemUpdateService_v1.xml b/static/redfish/v1/schema/OemUpdateService_v1.xml
+new file mode 100644
+index 0000000..cbb7aa4
+--- /dev/null
++++ b/static/redfish/v1/schema/OemUpdateService_v1.xml
+@@ -0,0 +1,40 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
++ <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
++ <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData" />
++ </edmx:Reference>
++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml">
++ <edmx:Include Namespace="Validation.v1_0_0" Alias="Validation"/>
++ <edmx:Include Namespace="RedfishExtensions.v1_0_0" Alias="Redfish"/>
++ </edmx:Reference>
++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/UpdateService_v1.xml">
++ <edmx:Include Namespace="UpdateService"/>
++ <edmx:Include Namespace="UpdateService.v1_4_0"/>
++ </edmx:Reference>
++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml">
++ <edmx:Include Namespace="Resource"/>
++ <edmx:Include Namespace="Resource.v1_0_0"/>
++ </edmx:Reference>
++
++ <edmx:DataServices>
++ <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OemUpdateService">
++ <ComplexType Name="Oem" BaseType="Resource.OemObject">
++ <Annotation Term="OData.AdditionalProperties" Bool="true" />
++ <Annotation Term="OData.Description" String="OemUpdateService Oem properties." />
++ <Annotation Term="OData.AutoExpand"/>
++ <Property Name="ApplyOptions" Type="OemUpdateService.ApplyOptions"/>
++ </ComplexType>
++
++ <ComplexType Name="ApplyOptions" BaseType="Resource.OemObject">
++ <Annotation Term="OData.AdditionalProperties" Bool="false" />
++ <Annotation Term="OData.Description" String="An indication by boolean value whether to update firmware configuration along with firmware image update." />
++ <Property Name="ClearConfig" Type="Edm.Boolean">
++ <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/>
++ <Annotation Term="OData.Description" String="This indicates whether to update firmware configuration or not."/>
++ <Annotation Term="OData.LongDescription" String="The value of this property is used to indicate the firmware configuration update."/>
++ </Property>
++ </ComplexType>
++
++ </Schema>
++ </edmx:DataServices>
++</edmx:Edmx>
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Invalid-status-code-from-InsertMedia-REST-methods.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Invalid-status-code-from-InsertMedia-REST-methods.patch
new file mode 100644
index 000000000..c9a4119a1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Invalid-status-code-from-InsertMedia-REST-methods.patch
@@ -0,0 +1,182 @@
+
+From 1bbabe4ecdeea20da809b9f1d8e194683629517c Mon Sep 17 00:00:00 2001
+From: Alicja Rybak <alicja.rybak@intel.com>
+Date: Tue, 20 Apr 2021 16:32:37 +0200
+Subject: [PATCH] Invalid status code from InsertMedia REST methods GET, PUT,
+ DELETE, PATCH in proxy mode
+
+Add handlers for GET, PUT, DELETE, PATCH method and function that
+checks which mode is used and set suitable status code:
+Not allowed for Legacy and Not found for Proxy.
+
+Change-Id: Ib4c0a3e9a2a8853caa74c59239d9fcfed99c5e8b
+Signed-off-by: Alicja Rybak <alicja.rybak@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 154 +++++++++++++++++++++++++++++
+ 1 file changed, 154 insertions(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 8353ce7..66bf220 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -530,6 +530,160 @@ class VirtualMediaActionInsertMedia : public Node
+ return true;
+ }
+
++ /**
++ * @brief Function checks if insert media request is Legacy or Proxy type
++ * and sets suitable response code for unsupported REST method.
++ *
++ */
++ void CheckProxyMode(crow::Response& res,
++ const crow::Request& req,
++ const std::vector<std::string>& params)
++ {
++ auto aResp = std::make_shared<AsyncResp>(res);
++
++ if (params.size() != 2)
++ {
++ messages::internalError(res);
++ return;
++ }
++
++ // take resource name from URL
++ const std::string& resName = params[1];
++
++ if (params[0] != "bmc")
++ {
++ messages::resourceNotFound(res, "VirtualMedia.Insert", resName);
++
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [this, aResp{std::move(aResp)}, req,
++ resName](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
++ << ec;
++ aResp->res.result(
++ boost::beast::http::status::not_found);
++
++ return;
++ }
++ std::string service = getObjectType.begin()->first;
++ BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
++
++ crow::connections::systemBus->async_method_call(
++ [this, service, resName, req,
++ aResp{aResp}](const boost::system::error_code ec,
++ ManagedObjectType& subtree) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG << "DBUS response error";
++
++ return;
++ }
++
++ for (auto& item : subtree)
++ {
++ std::string thispath = item.first.filename();
++ if (thispath.empty())
++ {
++ continue;
++ }
++
++ if (thispath != resName)
++ {
++ continue;
++ }
++
++ auto mode = item.first.parent_path();
++ auto type = mode.parent_path();
++ if (mode.filename().empty() ||
++ type.filename().empty())
++ {
++ continue;
++ }
++
++ if (type.filename() != "VirtualMedia")
++ {
++ continue;
++ }
++
++ // Check if dbus path is Legacy type
++ if (mode.filename() == "Legacy")
++ {
++ BMCWEB_LOG_DEBUG << "InsertMedia only allowed "
++ "with POST method "
++ "in legacy mode";
++ aResp->res.result(
++ boost::beast::http::status::
++ method_not_allowed);
++
++ return;
++ }
++ // Check if dbus path is Proxy type
++ if (mode.filename() == "Proxy")
++ {
++ // Not possible in proxy mode
++ BMCWEB_LOG_DEBUG << "InsertMedia not "
++ "allowed in proxy mode";
++ aResp->res.result(
++ boost::beast::http::status::not_found);
++
++ return;
++ }
++ }
++
++ BMCWEB_LOG_DEBUG << "Parent item not found";
++ aResp->res.result(
++ boost::beast::http::status::not_found);
++ },
++ service, "/xyz/openbmc_project/VirtualMedia",
++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
++ }
++
++ /**
++ * @brief Function handles GET method request.
++ */
++ void doGet(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ CheckProxyMode(res, req, params);
++ }
++
++ /**
++ * @brief Function handles PATCH method request.
++ */
++ void doPatch(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ CheckProxyMode(res, req, params);
++ }
++
++ /**
++ * @brief Function handles PUT method request.
++ */
++ void doPut(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ CheckProxyMode(res, req, params);
++ }
++
++ /**
++ * @brief Function handles DELETE method request.
++ */
++ void doDelete(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ CheckProxyMode(res, req, params);
++ }
++
+ /**
+ * @brief Function handles POST method request.
+ *
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch
new file mode 100644
index 000000000..02f843bb8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch
@@ -0,0 +1,74 @@
+From 034920eca21bc25899565484928ee72025e21ff8 Mon Sep 17 00:00:00 2001
+From: Wiktor Golgowski <wiktor.golgowski@linux.intel.com>
+Date: Thu, 30 Apr 2020 11:09:35 +0200
+Subject: [PATCH] Use chip id-based UUID for Service Root.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the sysfs-provided chip id is available, it will be used as
+payload to generate Service Root UUID from hardcoded namespace.
+
+Tested:
+Generated UUID is consistent between BMC image reflashes.
+If the sysfs node is not available, code falls back to randomly
+generated UUID.
+
+Signed-off-by: Wiktor Gołgowski <wiktor.golgowski@linux.intel.com>
+---
+ include/persistent_data.hpp | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp
+index 24f7afd..8826b06 100644
+--- a/include/persistent_data.hpp
++++ b/include/persistent_data.hpp
+@@ -25,6 +25,10 @@ class ConfigFile
+ public:
+ // todo(ed) should read this from a fixed location somewhere, not CWD
+ static constexpr const char* filename = "bmcweb_persistent_data.json";
++ static constexpr const char* chipIdSysfsNode = "/sys/devices/platform"
++ "/ahb/ahb:apb/1e6e2000.syscon/1e6e2000.syscon:misc_control/chip_id";
++ static constexpr const char* UuidNs = "{b7b0553a-54cc-4162-982d-"
++ "944847ed76f5}";
+
+ ConfigFile()
+ {
+@@ -144,9 +148,31 @@ class ConfigFile
+
+ if (systemUuid.empty())
+ {
+- systemUuid =
+- boost::uuids::to_string(boost::uuids::random_generator()());
+- needWrite = true;
++ // Try to retrieve chip id-based uuid.
++ std::ifstream chipIdFile(chipIdSysfsNode);
++ if (chipIdFile.is_open())
++ {
++ std::string chipId;
++ std::getline(chipIdFile, chipId);
++ if (!chipId.empty())
++ {
++ boost::uuids::name_generator_sha1 gen(
++ boost::uuids::string_generator()(UuidNs));
++ systemUuid = boost::uuids::to_string(gen(chipId.c_str()));
++ needWrite = true;
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Cannot get chip id-based System UUID.";
++ }
++ }
++ // If the above fails, generate random uuid.
++ if (systemUuid.empty())
++ {
++ systemUuid =
++ boost::uuids::to_string(boost::uuids::random_generator()());
++ needWrite = true;
++ }
+ }
+ if (fileRevision < jsonRevision)
+ {
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch
new file mode 100644
index 000000000..eaba041d5
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch
@@ -0,0 +1,40 @@
+From dab4adbf211b6867f86fcf6080b34a0e41f6f4a1 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Tue, 23 Feb 2021 15:53:16 +0000
+Subject: [PATCH] Set Inserted redfish property for not inserted resources
+
+Tested: Verified that Inserted property is returned and set to
+ "false" for not inserted media.
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 188248a..f477f63 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -95,6 +95,7 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ BMCWEB_LOG_DEBUG << "Value Active not found";
+ return;
+ }
++ aResp->res.jsonValue["Inserted"] = *activeValue;
+
+ const std::string* endpointIdValue =
+ std::get_if<std::string>(&endpointIdProperty->second);
+@@ -106,7 +107,6 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
+ *endpointIdValue;
+ aResp->res.jsonValue["TransferProtocolType"] = "OEM";
+- aResp->res.jsonValue["Inserted"] = *activeValue;
+ if (*activeValue == true)
+ {
+ aResp->res.jsonValue["ConnectedVia"] = "Applet";
+@@ -137,7 +137,6 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ }
+
+ aResp->res.jsonValue["Image"] = *imageUrlValue;
+- aResp->res.jsonValue["Inserted"] = *activeValue;
+ aResp->res.jsonValue["TransferProtocolType"] =
+ getTransferProtocolTypeFromUri(*imageUrlValue);
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Bmcweb-handle-permission-denied-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Bmcweb-handle-permission-denied-exception.patch
new file mode 100644
index 000000000..074718349
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-Bmcweb-handle-permission-denied-exception.patch
@@ -0,0 +1,44 @@
+From 81f2c022b22ef75990f30e5ba5bfd4ba5bd41754 Mon Sep 17 00:00:00 2001
+From: Alicja Rybak <alicja.rybak@intel.com>
+Date: Wed, 14 Apr 2021 16:26:59 +0200
+Subject: [PATCH] Bmcweb handle permission denied exception
+
+Add handling of permission denied exception (EPERM) that
+can be thrown by VirtualMedia service during Mount/Unmount dbus operations.
+
+Tested:
+Verified that after mounting/unmounting HTTPS resource twice in a row in legacy mode,
+VirtualMedia returns EPERM, which bmcweb handles as 403 status code.
+
+Change-Id: Ibc18d5ec822c5072605b1fc4651389982002798b
+Signed-off-by: Alicja Rybak <alicja.rybak@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 365290b..bbdc91b 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -900,6 +900,10 @@ class VirtualMediaActionInsertMedia : public Node
+ {
+ messages::resourceInUse(asyncResp->res);
+ }
++ else if (ec == boost::system::errc::permission_denied)
++ {
++ messages::accessDenied(asyncResp->res, "VirtualMedia.Insert");
++ }
+ else
+ {
+ messages::internalError(asyncResp->res);
+@@ -1092,6 +1096,10 @@ class VirtualMediaActionEjectMedia : public Node
+ {
+ messages::resourceInUse(asyncResp->res);
+ }
++ else if (ec == boost::system::errc::permission_denied)
++ {
++ messages::accessDenied(asyncResp->res, "VirtualMedia.Eject");
++ }
+ else
+ {
+ messages::internalError(asyncResp->res);
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch
new file mode 100644
index 000000000..01c1c858c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0004-bmcweb-handle-device-or-resource-busy-exception.patch
@@ -0,0 +1,219 @@
+From 76480c6a5b1708113f28aecab32a85984371243c Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Fri, 10 Jul 2020 09:54:06 +0000
+Subject: [PATCH] bmcweb handle device or resource busy exception
+
+Use async_method_call_timed() for mount/unmount dbus oprations.
+Long mount/unmount times are supported by VirtualMedia service,
+this works because of settable timeout property, available for each block
+device.
+Default dbus calls will timeout when mount/unmount timeout is long enough.
+
+Get mount/unmount timeout property and use it for mount/unmount calls.
+Add handling of device or resource busy exception (EBUSY) that
+can be thrown by VirtualMedia service during Mount/Unmount dbus operations.
+
+Tested: Verified that after mounting non-existing HTTPS resource
+ in proxy mode, VirtualMedia recovers restoring ready state
+ and returns EBUSY during that transition.
+ Verfied that resources can be mounted/unmounted in both legacy
+ and proxy mode.
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Change-Id: Ica62c34db0cce24c4c6169fc661edfde49e948d0
+---
+ redfish-core/lib/virtual_media.hpp | 144 ++++++++++++++++++++++-------
+ 1 file changed, 110 insertions(+), 34 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 95a8881..188248a 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -24,6 +24,8 @@
+ #include <account_service.hpp>
+ #include <boost/url/url_view.hpp>
+
++#include <chrono>
++
+ namespace redfish
+
+ {
+@@ -160,6 +162,26 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ }
+ }
+
++/**
++ * @brief parses Timeout property and converts to microseconds
++ */
++static std::optional<uint64_t>
++ vmParseTimeoutProperty(const std::variant<int>& timeoutProperty)
++{
++ const int* timeoutValue = std::get_if<int>(&timeoutProperty);
++ if (timeoutValue)
++ {
++ constexpr int timeoutMarginSeconds = 10;
++ return std::chrono::duration_cast<std::chrono::microseconds>(
++ std::chrono::seconds(*timeoutValue + timeoutMarginSeconds))
++ .count();
++ }
++ else
++ {
++ return std::nullopt;
++ }
++}
++
+ /**
+ * @brief Fill template for Virtual Media Item.
+ */
+@@ -856,22 +878,54 @@ class VirtualMediaActionInsertMedia : public Node
+ }
+
+ crow::connections::systemBus->async_method_call(
+- [asyncResp, secretPipe](const boost::system::error_code ec,
+- bool success) {
++ [asyncResp, service, name, imageUrl, rw, unixFd,
++ secretPipe](const boost::system::error_code ec,
++ const std::variant<int> timeoutProperty) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+ messages::internalError(asyncResp->res);
++ return;
+ }
+- else if (!success)
++
++ auto timeout = vmParseTimeoutProperty(timeoutProperty);
++ if (timeout == std::nullopt)
+ {
+- BMCWEB_LOG_ERROR << "Service responded with error";
+- messages::generalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "Timeout property is empty.";
++ messages::internalError(asyncResp->res);
++ return;
+ }
++
++ crow::connections::systemBus->async_method_call_timed(
++ [asyncResp, secretPipe](const boost::system::error_code ec,
++ bool success) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: "
++ << ec;
++ if (ec ==
++ boost::system::errc::device_or_resource_busy)
++ {
++ messages::resourceInUse(asyncResp->res);
++ }
++ else
++ {
++ messages::internalError(asyncResp->res);
++ }
++ }
++ else if (!success)
++ {
++ BMCWEB_LOG_ERROR << "Service responded with error";
++ messages::generalError(asyncResp->res);
++ }
++ },
++ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
++ "xyz.openbmc_project.VirtualMedia.Legacy", "Mount",
++ *timeout, imageUrl, rw, unixFd);
+ },
+ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
+- unixFd);
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout");
+ }
+ };
+
+@@ -1003,38 +1057,60 @@ class VirtualMediaActionEjectMedia : public Node
+ const std::string& service, const std::string& name,
+ bool legacy)
+ {
+-
+- // Legacy mount requires parameter with image
++ std::string objectPath = "/xyz/openbmc_project/VirtualMedia/";
++ std::string ifaceName = "xyz.openbmc_project.VirtualMedia";
+ if (legacy)
+ {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+-
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
++ objectPath += "Legacy/";
++ ifaceName += ".Legacy";
+ }
+- else // proxy
++ else
+ {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+-
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
++ objectPath += "Proxy/";
++ ifaceName += ".Proxy";
+ }
++ objectPath += name;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, service, name, objectPath,
++ ifaceName](const boost::system::error_code ec,
++ const std::variant<int> timeoutProperty) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ auto timeout = vmParseTimeoutProperty(timeoutProperty);
++ if (timeout == std::nullopt)
++ {
++ BMCWEB_LOG_ERROR << "Timeout property is empty.";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ crow::connections::systemBus->async_method_call_timed(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: "
++ << ec;
++ if (ec ==
++ boost::system::errc::device_or_resource_busy)
++ {
++ messages::resourceInUse(asyncResp->res);
++ }
++ else
++ {
++ messages::internalError(asyncResp->res);
++ }
++ return;
++ }
++ },
++ service, objectPath, ifaceName, "Unmount", *timeout);
++ },
++ service, objectPath, "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout");
+ }
+ };
+
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch
new file mode 100644
index 000000000..54f00aa39
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-EventService-https-client-support.patch
@@ -0,0 +1,547 @@
+From d340953bc925ff8535c5a8fac54db24b243ba8ad Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Mon, 19 Oct 2020 13:21:42 +0530
+Subject: [PATCH] EventService: https client support
+
+Add https client support for push style
+eventing. Using this BMC can push the event
+logs/telemetry data to event listener over
+secure http channel.
+
+Tested:
+ - Created subscription with https destination
+ url. Using SubmitTestEvent action set the
+ event and can see event on event listener.
+ - Validator passed.
+
+Change-Id: I44c3918b39baa2eb5fddda9d635f99aa280a422a
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_client.hpp | 370 +++++++++++++-----
+ .../include/event_service_manager.hpp | 2 +-
+ 2 files changed, 267 insertions(+), 105 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index 5c7b13f..d782dee 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -31,12 +31,17 @@ namespace crow
+ {
+
+ static constexpr uint8_t maxRequestQueueSize = 50;
++static constexpr unsigned int httpReadBodyLimit = 1024;
+
+ enum class ConnState
+ {
+ initialized,
++ resolveInProgress,
++ resolveFailed,
++ resolved,
+ connectInProgress,
+ connectFailed,
++ sslHandshakeInProgress,
+ connected,
+ sendInProgress,
+ sendFailed,
+@@ -50,53 +55,124 @@ enum class ConnState
+ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ private:
++ boost::asio::ip::tcp::resolver resolver;
++ boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv12_client};
+ boost::beast::tcp_stream conn;
++ std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream&>> sslConn;
+ boost::asio::steady_timer timer;
+- boost::beast::flat_buffer buffer;
++ boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
++ std::optional<
++ boost::beast::http::response_parser<boost::beast::http::string_body>>
++ parser;
+ boost::beast::http::request<boost::beast::http::string_body> req;
+- boost::beast::http::response<boost::beast::http::string_body> res;
+ boost::asio::ip::tcp::resolver::results_type endpoint;
+- std::vector<std::pair<std::string, std::string>> headers;
++ boost::beast::http::fields fields;
+ std::queue<std::string> requestDataQueue;
+- ConnState state;
+ std::string subId;
+ std::string host;
+ std::string port;
+ std::string uri;
++ bool useSsl;
+ uint32_t retryCount;
+ uint32_t maxRetryAttempts;
+ uint32_t retryIntervalSecs;
+ std::string retryPolicyAction;
+ bool runningTimer;
++ ConnState state;
++
++ void doResolve()
++ {
++ BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":" << port;
++ if (state == ConnState::resolveInProgress)
++ {
++ return;
++ }
++ state = ConnState::resolveInProgress;
++ // TODO: Use async_resolver. boost asio example
++ // code as is crashing with async_resolve().
++ try
++ {
++ endpoint = resolver.resolve(host, port);
++ }
++ catch (const std::exception& e)
++ {
++ BMCWEB_LOG_ERROR << "Failed to resolve hostname: " << host << " - "
++ << e.what();
++ state = ConnState::resolveFailed;
++ checkQueue();
++ return;
++ }
++ state = ConnState::resolved;
++ checkQueue();
++ }
+
+ void doConnect()
+ {
+- if (state == ConnState::connectInProgress)
++ if (useSsl)
++ {
++ sslConn.emplace(conn, ctx);
++ }
++
++ if ((state == ConnState::connectInProgress) ||
++ (state == ConnState::sslHandshakeInProgress))
+ {
+ return;
+ }
+ state = ConnState::connectInProgress;
+
+ BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port;
+- // Set a timeout on the operation
++
++ auto respHandler =
++ [self(shared_from_this())](const boost::beast::error_code ec,
++ const boost::asio::ip::tcp::resolver::
++ results_type::endpoint_type& ep) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Connect " << ep
++ << " failed: " << ec.message();
++ self->state = ConnState::connectFailed;
++ self->checkQueue();
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "Connected to: " << ep;
++ if (self->sslConn)
++ {
++ self->performHandshake();
++ }
++ else
++ {
++ self->state = ConnState::connected;
++ self->checkQueue();
++ }
++ };
++
+ conn.expires_after(std::chrono::seconds(30));
+- conn.async_connect(endpoint, [self(shared_from_this())](
+- const boost::beast::error_code& ec,
+- const boost::asio::ip::tcp::resolver::
+- results_type::endpoint_type& ep) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Connect " << ep
+- << " failed: " << ec.message();
+- self->state = ConnState::connectFailed;
+- self->checkQueue();
+- return;
+- }
+- self->state = ConnState::connected;
+- BMCWEB_LOG_DEBUG << "Connected to: " << ep;
++ conn.async_connect(endpoint, std::move(respHandler));
++ }
++
++ void performHandshake()
++ {
++ if (state == ConnState::sslHandshakeInProgress)
++ {
++ return;
++ }
++ state = ConnState::sslHandshakeInProgress;
++
++ sslConn->async_handshake(
++ boost::asio::ssl::stream_base::client,
++ [self(shared_from_this())](const boost::beast::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "SSL handshake failed: "
++ << ec.message();
++ self->doCloseAndCheckQueue(ConnState::connectFailed);
++ return;
++ }
++ self->state = ConnState::connected;
++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull";
+
+- self->checkQueue();
+- });
++ self->checkQueue();
++ });
+ }
+
+ void sendMessage(const std::string& data)
+@@ -107,100 +183,170 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ state = ConnState::sendInProgress;
+
+- BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port;
++ BMCWEB_LOG_DEBUG << host << ":" << port;
+
+- req.version(static_cast<int>(11)); // HTTP 1.1
+- req.target(uri);
+- req.method(boost::beast::http::verb::post);
+-
+- // Set headers
+- for (const auto& [key, value] : headers)
++ req = {};
++ for (const auto& field : fields)
+ {
+- req.set(key, value);
++ req.set(field.name_string(), field.value());
+ }
+ req.set(boost::beast::http::field::host, host);
++ req.set(boost::beast::http::field::content_type, "text/plain");
++
++ req.version(static_cast<int>(11)); // HTTP 1.1
++ req.target(uri);
++ req.method(boost::beast::http::verb::post);
+ req.keep_alive(true);
+
+ req.body() = data;
+ req.prepare_payload();
+
+- // Set a timeout on the operation
+- conn.expires_after(std::chrono::seconds(30));
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
++ self->doCloseAndCheckQueue(ConnState::sendFailed);
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
+
+- // Send the HTTP request to the remote host
+- boost::beast::http::async_write(
+- conn, req,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "sendMessage() failed: "
+- << ec.message();
+- self->state = ConnState::sendFailed;
+- self->checkQueue();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
+- << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
++ self->recvMessage();
++ };
+
+- self->recvMessage();
+- });
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ boost::beast::http::async_write(*sslConn, req,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_write(conn, req, std::move(respHandler));
++ }
+ }
+
+ void recvMessage()
+ {
+- // Receive the HTTP response
+- boost::beast::http::async_read(
+- conn, buffer, res,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ if (ec && ec != boost::beast::http::error::partial_message)
++ {
++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
++ self->doCloseAndCheckQueue(ConnState::recvFailed);
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
++
++ // TODO: check for return status code and perform
++ // retry if fails(Ex: 40x). Take action depending on
++ // retry policy.
++ BMCWEB_LOG_DEBUG << "recvMessage() data: "
++ << self->parser->get().body();
++
++ // Send is successful, Lets remove data from queue
++ // check for next request data in queue.
++ self->requestDataQueue.pop();
++
++ // Transfer ownership of the response
++ self->parser->release();
++
++ // TODO: Implement the keep-alive connections.
++ // Most of the web servers close connection abruptly
++ // and might be reason due to which its observed that
++ // stream_truncated(Next read) or partial_message
++ // errors. So for now, closing connection and re-open
++ // for all cases.
++ self->doCloseAndCheckQueue(ConnState::closed);
++ };
++
++ parser.emplace(std::piecewise_construct, std::make_tuple());
++ parser->body_limit(httpReadBodyLimit);
++ // Since these are all push style eventing, we are not
++ // bothered about response parsing.
++ parser->skip(true);
++ buffer.consume(buffer.size());
++
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ boost::beast::http::async_read(*sslConn, buffer, *parser,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_read(conn, buffer, *parser,
++ std::move(respHandler));
++ }
++ }
++
++ void doCloseAndCheckQueue(const ConnState setState = ConnState::closed)
++ {
++ if (sslConn)
++ {
++ conn.expires_after(std::chrono::seconds(30));
++ sslConn->async_shutdown([self = shared_from_this(),
++ setState{std::move(setState)}](
++ const boost::system::error_code ec) {
+ if (ec)
+ {
+- BMCWEB_LOG_ERROR << "recvMessage() failed: "
+- << ec.message();
+- self->state = ConnState::recvFailed;
+- self->checkQueue();
+- return;
++ // Many https server closes connection abruptly
++ // i.e witnout close_notify. More details are at
++ // https://github.com/boostorg/beast/issues/824
++ if (ec == boost::asio::ssl::error::stream_truncated)
++ {
++ BMCWEB_LOG_ERROR
++ << "doCloseAndCheckQueue(): Connection "
++ "closed by server. ";
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "doCloseAndCheckQueue() failed: "
++ << ec.message();
++ }
+ }
+- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
+- << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
+-
+- // Discard received data. We are not interested.
+- BMCWEB_LOG_DEBUG << "recvMessage() data: " << self->res;
+-
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- self->requestDataQueue.pop();
+- self->state = ConnState::idle;
++ else
++ {
++ BMCWEB_LOG_DEBUG << "Connection closed gracefully...";
++ }
++ self->conn.cancel();
++ self->state = setState;
+ self->checkQueue();
+ });
+- }
+-
+- void doClose()
+- {
+- boost::beast::error_code ec;
+- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+-
+- state = ConnState::closed;
+- // not_connected happens sometimes so don't bother reporting it.
+- if (ec && ec != boost::beast::errc::not_connected)
++ }
++ else
+ {
+- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message();
+- return;
++ boost::beast::error_code ec;
++ conn.expires_after(std::chrono::seconds(30));
++ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both,
++ ec);
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "doCloseAndCheckQueue() failed: "
++ << ec.message();
++ }
++ else
++ {
++ BMCWEB_LOG_DEBUG << "Connection closed gracefully...";
++ }
++
++ conn.close();
++ state = setState;
++ checkQueue();
+ }
+- BMCWEB_LOG_DEBUG << "Connection closed gracefully";
++ return;
+ }
+
+ void checkQueue(const bool newRecord = false)
+ {
+ if (requestDataQueue.empty())
+ {
+- // TODO: Having issue in keeping connection alive. So lets close if
+- // nothing to be transferred.
+- doClose();
+-
+ BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n";
+ return;
+ }
+@@ -232,6 +378,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+
+ if ((state == ConnState::connectFailed) ||
++ (state == ConnState::resolveFailed) ||
+ (state == ConnState::sendFailed) ||
+ (state == ConnState::recvFailed))
+ {
+@@ -256,14 +403,18 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ << " seconds. RetryCount = " << retryCount;
+ timer.expires_after(std::chrono::seconds(retryIntervalSecs));
+ timer.async_wait(
+- [self = shared_from_this()](const boost::system::error_code&) {
++ [self = shared_from_this()](boost::system::error_code) {
+ self->runningTimer = false;
+ self->connStateCheck();
+ });
+ return;
+ }
+- // reset retry count.
+- retryCount = 0;
++
++ if (state == ConnState::idle)
++ {
++ // State idle means, previous attempt is successful.
++ retryCount = 0;
++ }
+ connStateCheck();
+
+ return;
+@@ -273,15 +424,21 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ switch (state)
+ {
++ case ConnState::initialized:
++ case ConnState::resolveFailed:
++ case ConnState::connectFailed:
++ doResolve();
++ break;
+ case ConnState::connectInProgress:
++ case ConnState::resolveInProgress:
++ case ConnState::sslHandshakeInProgress:
+ case ConnState::sendInProgress:
+ case ConnState::suspended:
+ case ConnState::terminated:
+ // do nothing
+ break;
+- case ConnState::initialized:
+ case ConnState::closed:
+- case ConnState::connectFailed:
++ case ConnState::resolved:
+ case ConnState::sendFailed:
+ case ConnState::recvFailed:
+ {
+@@ -297,22 +454,22 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ sendMessage(data);
+ break;
+ }
++ default:
++ break;
+ }
+ }
+
+ public:
+ explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const std::string& destPort,
+- const std::string& destUri) :
+- conn(ioc),
+- timer(ioc), subId(id), host(destIP), port(destPort), uri(destUri),
+- retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0),
+- retryPolicyAction("TerminateAfterRetries"), runningTimer(false)
+- {
+- boost::asio::ip::tcp::resolver resolver(ioc);
+- endpoint = resolver.resolve(host, port);
+- state = ConnState::initialized;
+- }
++ const std::string& destUri,
++ const bool inUseSsl = true) :
++ resolver(ioc),
++ conn(ioc), timer(ioc), subId(id), host(destIP), port(destPort),
++ uri(destUri), useSsl(inUseSsl), retryCount(0), maxRetryAttempts(5),
++ retryPolicyAction("TerminateAfterRetries"), runningTimer(false),
++ state(ConnState::initialized)
++ {}
+
+ void sendData(const std::string& data)
+ {
+@@ -337,7 +494,12 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ void setHeaders(
+ const std::vector<std::pair<std::string, std::string>>& httpHeaders)
+ {
+- headers = httpHeaders;
++ // Set headers
++ for (const auto& [key, value] : httpHeaders)
++ {
++ // TODO: Validate the header fileds before assign.
++ fields.set(key, value);
++ }
+ }
+
+ void setRetryConfig(const uint32_t retryAttempts,
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 54dafb4..f68ae1d 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -387,7 +387,7 @@ class Subscription
+ {
+ conn = std::make_shared<crow::HttpClient>(
+ crow::connections::systemBus->get_io_context(), id, host, port,
+- path);
++ path, (uriProto == "https" ? true : false));
+ }
+
+ Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-Fix-unmounting-image-in-proxy-mode.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-Fix-unmounting-image-in-proxy-mode.patch
new file mode 100644
index 000000000..acfd9225b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0005-Fix-unmounting-image-in-proxy-mode.patch
@@ -0,0 +1,32 @@
+From 9493bb8e2f5c708e187313c86e6033de5f103c51 Mon Sep 17 00:00:00 2001
+From: Alicja Rybak <alicja.rybak@intel.com>
+Date: Fri, 23 Apr 2021 17:35:52 +0200
+Subject: [PATCH] Fix unmounting image in proxy mode.
+
+Sometimes Slot0 got higher key than Slot1 and erase function for Slot1
+invalidates elements with keys not less than the erased element.
+In that case invalid slot0 will be unmounted.
+Change order of calling close() and erase() functions to
+unmount correct device.
+
+Change-Id: I7a40a4518982f697d3eed635cde6d06978149cf0
+Signed-off-by: Alicja Rybak <alicja.rybak@intel.com>
+---
+ include/nbd_proxy.hpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp
+index 7b90e90..fa7f647 100644
+--- a/include/nbd_proxy.hpp
++++ b/include/nbd_proxy.hpp
+@@ -428,9 +428,9 @@ inline void requestRoutes(App& app)
+ BMCWEB_LOG_DEBUG << "No session to close";
+ return;
+ }
++ session->second->close();
+ // Remove reference to session in global map
+ sessions.erase(session);
+- session->second->close();
+ })
+ .onmessage([](crow::websocket::Connection& conn,
+ const std::string& data, bool) {
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch
new file mode 100644
index 000000000..b2627644b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0006-Define-Redfish-interface-Registries-Bios.patch
@@ -0,0 +1,850 @@
+From c645c011bb3ea2a2aaf52560cb9fcc461d048cae Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Fri, 4 Sep 2020 19:24:25 +0800
+Subject: [PATCH] Define Redfish interface "/Registries/Bios" and enable
+ Attributes property
+
+1. Define Redfish interface "/Registries/Bios" for BIOS Attribute Registry
+ RBC Daemon provide method to get BIOS attribute registry.
+2. Eanble Attributes property for BIOS resource
+3. Define Redfish interface "/Systems/system/Bios/Settings" for BIOS
+settings
+4. RBC daemon is at
+https://gerrit.openbmc-project.xyz/#/c/openbmc/bios-settings-mgr/+/35563/
+5. IPMI command implementation is at
+https://gerrit.openbmc-project.xyz/#/c/openbmc/intel-ipmi-oem/+/30827/
+6. Property design is at
+https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/xyz/openbmc_project/BIOSConfig
+7. Design doc is at
+https://github.com/openbmc/docs/blob/master/designs/remote-bios-configuration.md
+8. There will be 95 test cases for this feature in the validation team.
+
+Tested:
+
+1. Use postman (Redfish tool) could get all the attributes in bios
+resouce, get bios settings, get bios attribute
+registry.
+https://IP_ADDR/redfish/v1/Systems/system/Bios
+{
+ "@Redfish.Settings": {
+ "@odata.type": "#Settings.v1_3_0.Settings",
+ "SettingsObject": {
+ "@odata.id": "/redfish/v1/Systems/system/Bios/Settings"
+ }
+ },
+ "@odata.id": "/redfish/v1/Systems/system/Bios",
+ "@odata.type": "#Bios.v1_1_0.Bios",
+ "Actions": {
+ "#Bios.ChangePassword": {
+ "target": "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword"
+ },
+ "#Bios.ResetBios": {
+ "target": "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"
+ }
+ },
+ "AttributeRegistry": "BiosAttributeRegistry",
+ "Attributes": {
+ "attr0": "current value"
+ },
+ "Description": "BIOS Configuration Service",
+ "Id": "BIOS",
+ "Links": {
+ "ActiveSoftwareImage": {
+ "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/bios_active"
+ },
+ "SoftwareImages": [
+ {
+ "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/bios_active"
+ }
+ ],
+ "SoftwareImages@odata.count": 1
+ },
+ "Name": "BIOS Configuration"
+}
+
+Redfish interface: https://BMCIP/redfish/v1/Registries/BiosAttributeRegistry
+{
+ "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry",
+ "@odata.type": "#MessageRegistryFile.v1_1_0.MessageRegistryFile",
+ "Description": "BiosAttributeRegistry Message Registry File Location",
+ "Id": "BiosAttributeRegistry",
+ "Languages": [
+ "en"
+ ],
+ "Languages@odata.count": 1,
+ "Location": [
+ {
+ "Language": "en",
+ "Uri": "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry"
+ }
+ ],
+ "Location@odata.count": 1,
+ "Name": "BiosAttributeRegistry Message Registry File",
+ "Registry": "BiosAttributeRegistry.1.0.0"
+}
+
+Redfish interface: https://BMCIP/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry
+{
+ "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry",
+ "@odata.type": "#AttributeRegistry.v1_3_2.AttributeRegistry",
+ "Id": "BiosAttributeRegistry",
+ "Language": "en",
+ "Name": "Bios Attribute Registry",
+ "OwningEntity": "OpenBMC",
+ "RegistryEntries": {
+ "Attributes": [
+ {
+ "AttributeName": "attr0",
+ "CurrentValue": "current value",
+ "DefaultValue": "default value",
+ "DisplayName": "display name for attr0",
+ "HelpText": "description for attr0",
+ "MenuPath": "./menu/path/for/attr0",
+ "ReadOnly": false,
+ "Type": "String",
+ "Value": []
+ }
+ ]
+ },
+ "RegistryVersion": "1.0.0"
+}
+
+https://BMC_IPADDR/redfish/v1/Systems/system/Bios/Settings
+{
+ "@odata.id": "/redfish/v1/Systems/system/Bios/Settings",
+ "@odata.type": "#Bios.v1_1_0.Bios",
+ "AttributeRegistry": "BiosAttributeRegistry",
+ "Attributes": {
+ "QuietBoot": "0x0"
+ },
+ "Id": "BiosSettingsV1",
+ "Name": "Bios Settings Version 1"
+}
+
+2. Passed Validator check for bios resource and bios attribute registry
+*** /redfish/v1/Systems/system/Bios
+INFO - Type (#Bios.v1_1_0.Bios), GET SUCCESS (time: 1.57377)
+INFO - PASS
+*** /redfish/v1/Registries/BiosAttributeRegistry
+INFO - Type (#MessageRegistryFile.v1_1_0.MessageRegistryFile), GET SUCCESS (time: 0.075438)
+INFO - PASS
+INFO -
+*** /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry
+INFO - Type (#AttributeRegistry.v1_3_2.AttributeRegistry), GET SUCCESS (time: 0.075751)
+INFO - PASS
+
+@odata.id /redfish/v1/Systems/system/Bios odata Exists PASS
+@odata.type #Settings.v1_3_0.Settings odata Exists PASS
+Links [JSON Object] Bios.v1_1_0.Links Yes complex
+Links.ActiveSoftwareImage Link: /redfish/v1/UpdateService/FirmwareInventory/bios_active link to: SoftwareInventory Yes PASS
+Links.SoftwareImages Array (size: 1) array of: SoftwareInventory Yes ...
+Links.SoftwareImages[0] Link: /redfish/v1/UpdateService/FirmwareInventory/bios_active SoftwareInventory Yes PASS
+Links.Oem - Resource.Oem No Optional
+SoftwareImages@odata.count 1 odata Exists PASS
+AttributeRegistry BiosAttributeRegistry string Yes PASS
+Actions [JSON Object] Bios.v1_0_0.Actions Yes complex
+Actions.#Bios.ResetBios Action - Yes PASS
+Actions.#Bios.ChangePassword Action - Yes PASS
+Attributes [JSON Object] Bios.v1_0_0.Attributes Yes complex
+Attributes.attr0 current value primitive Yes PASS
+Id BIOS string Yes PASS
+Description BIOS Configuration Service string Yes PASS
+Name BIOS Configuration string Yes PASS
+Oem - Resource.Oem No Optional
+@Redfish.Settings [JSON Object] Settings.Settings Yes complex
+@Redfish.Settings.MaintenanceWindowResource - link to: ItemOrCollection No Optional
+@Redfish.Settings.SupportedApplyTimes - string (enum) No Optional
+@Redfish.Settings.Time - date No Optional
+@Redfish.Settings.ETag - string No Optional
+@Redfish.Settings.SettingsObject Link: /redfish/v1/Systems/system/Bios/Settings link to: Item Yes PASS
+@Redfish.Settings.Messages - Message No Optional
+
+@odata.id /redfish/v1/Registries/BiosAttributeRegistry odata Exists PASS
+@odata.type #MessageRegistryFile.v1_1_0.MessageRegistryFile odata Exists PASS
+Languages@odata.count 1 odata Exists PASS
+Location@odata.count 1 odata Exists PASS
+Actions - MessageRegistryFile.v1_1_0.Actions No Optional
+Languages Array (size: 1) string Yes ...
+Languages[0] en string Yes PASS
+Registry BiosAttributeRegistry.1.0.0 string Yes PASS
+Location Array (size: 1) array of: Location Yes ...
+Location[0] [JSON Object] Location Yes complex
+Location[0].Language en string Yes PASS
+Location[0].Uri /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry string Yes PASS
+Location[0].ArchiveUri - string No Optional
+Location[0].PublicationUri - string No Optional
+Location[0].ArchiveFile - string No Optional
+Id BiosAttributeRegistry string Yes PASS
+Description BiosAttributeRegistry Message Registry File Location string Yes PASS
+Name BiosAttributeRegistry Message Registry File string Yes PASS
+Oem - Resource.Oem No Optional
+
+@odata.id /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry odata Exists PASS
+@odata.type #AttributeRegistry.v1_3_2.AttributeRegistry odata Exists PASS
+Actions - AttributeRegistry.v1_1_0.Actions No Optional
+Language en string Yes PASS
+RegistryVersion 1.0.0 string Yes PASS
+OwningEntity OpenBMC string Yes PASS
+SupportedSystems - SupportedSystems No Optional
+RegistryEntries [JSON Object] AttributeRegistry.v1_0_0.RegistryEntries Yes complex
+RegistryEntries.Attributes Array (size: 1) array of: Attributes Yes ...
+RegistryEntries.Attributes[0] [JSON Object] Attributes Yes complex
+RegistryEntries.Attributes[0].Oem - Resource.Oem No Optional
+RegistryEntries.Attributes[0].ResetRequired - boolean No Optional
+RegistryEntries.Attributes[0].UefiDevicePath - string No Optional
+RegistryEntries.Attributes[0].UefiKeywordName - string No Optional
+RegistryEntries.Attributes[0].UefiNamespaceId - string No Optional
+RegistryEntries.Attributes[0].AttributeName attr0 string Yes PASS
+RegistryEntries.Attributes[0].Type String string (enum) Yes PASS
+RegistryEntries.Attributes[0].Value Array (size: 0) array of: AttributeValue Yes ...
+RegistryEntries.Attributes[0].DisplayName display name for attr0 string Yes PASS
+RegistryEntries.Attributes[0].HelpText description for attr0 string Yes PASS
+RegistryEntries.Attributes[0].WarningText - string No Optional
+RegistryEntries.Attributes[0].CurrentValue current value primitive Yes PASS
+RegistryEntries.Attributes[0].DefaultValue default value primitive Yes PASS
+RegistryEntries.Attributes[0].DisplayOrder - number No Optional
+RegistryEntries.Attributes[0].MenuPath ./menu/path/for/attr0 string Yes PASS
+RegistryEntries.Attributes[0].ReadOnly False boolean Yes PASS
+RegistryEntries.Attributes[0].WriteOnly - boolean No Optional
+RegistryEntries.Attributes[0].GrayOut - boolean No Optional
+RegistryEntries.Attributes[0].Hidden - boolean No Optional
+RegistryEntries.Attributes[0].Immutable - boolean No Optional
+RegistryEntries.Attributes[0].IsSystemUniqueProperty - boolean No Optional
+RegistryEntries.Attributes[0].MaxLength - number No Optional
+RegistryEntries.Attributes[0].MinLength - number No Optional
+RegistryEntries.Attributes[0].ScalarIncrement - number No Optional
+RegistryEntries.Attributes[0].UpperBound - number No Optional
+RegistryEntries.Attributes[0].LowerBound - number No Optional
+RegistryEntries.Attributes[0].ValueExpression - string No Optional
+RegistryEntries.Menus - Menus No Optional
+RegistryEntries.Dependencies - Dependencies No Optional
+Id BiosAttributeRegistry string Yes PASS
+Description - string No Optional
+Name Bios Attribute Registry string Yes PASS
+Oem - Resource.Oem No Optional
+
+Change-Id: Iecc61018c350f0b8c89df59b2864b941508b1916
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ redfish-core/include/redfish.hpp | 2 +
+ .../include/registries/bios_registry.hpp | 31 ++
+ redfish-core/lib/bios.hpp | 503 ++++++++++++++++++
+ redfish-core/lib/message_registries.hpp | 9 +-
+ 4 files changed, 544 insertions(+), 1 deletion(-)
+ create mode 100644 redfish-core/include/registries/bios_registry.hpp
+
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index 5d5eb7b..a8e5cf2 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -157,6 +157,8 @@ class RedfishService
+ nodes.emplace_back(std::make_unique<SystemActionsReset>(app));
+ nodes.emplace_back(std::make_unique<SystemResetActionInfo>(app));
+ nodes.emplace_back(std::make_unique<BiosService>(app));
++ nodes.emplace_back(std::make_unique<BiosSettings>(app));
++ nodes.emplace_back(std::make_unique<BiosAttributeRegistry>(app));
+ nodes.emplace_back(std::make_unique<BiosReset>(app));
+ #ifdef BMCWEB_ENABLE_VM_NBDPROXY
+ nodes.emplace_back(std::make_unique<VirtualMedia>(app));
+diff --git a/redfish-core/include/registries/bios_registry.hpp b/redfish-core/include/registries/bios_registry.hpp
+new file mode 100644
+index 0000000..88ef782
+--- /dev/null
++++ b/redfish-core/include/registries/bios_registry.hpp
+@@ -0,0 +1,31 @@
++/*
++// Copyright (c) 2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++#pragma once
++
++namespace redfish::message_registries::bios
++{
++const Header header = {
++ "Copyright 2020 OpenBMC. All rights reserved.",
++ "#MessageRegistry.v1_4_0.MessageRegistry",
++ "BiosAttributeRegistry.1.0.0",
++ "Bios Attribute Registry",
++ "en",
++ "This registry defines the messages for bios attribute registry.",
++ "BiosAttributeRegistry",
++ "1.0.0",
++ "OpenBMC",
++};
++} // namespace redfish::message_registries::bios
+\ No newline at end of file
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index 2c31077..5f8c91b 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -3,8 +3,140 @@
+ #include "node.hpp"
+
+ #include <utils/fw_utils.hpp>
++
+ namespace redfish
+ {
++
++/*baseBIOSTable
++map{attributeName,struct{attributeType,readonlyStatus,displayname,
++ description,menuPath,current,default,
++ array{struct{optionstring,optionvalue}}}}
++*/
++using BiosBaseTableType = std::vector<std::pair<
++ std::string,
++ std::tuple<
++ std::string, bool, std::string, std::string, std::string,
++ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
++ std::vector<
++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>>;
++using BiosBaseTableItemType = std::pair<
++ std::string,
++ std::tuple<
++ std::string, bool, std::string, std::string, std::string,
++ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
++ std::vector<
++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>;
++using OptionsItemType =
++ std::tuple<std::string, std::variant<int64_t, std::string>>;
++
++enum BiosBaseTableIndex
++{
++ biosBaseAttrType = 0,
++ biosBaseReadonlyStatus,
++ biosBaseDisplayName,
++ biosBaseDescription,
++ biosBaseMenuPath,
++ biosBaseCurrValue,
++ biosBaseDefaultValue,
++ biosBaseOptions
++};
++enum OptionsItemIndex
++{
++ optItemType = 0,
++ optItemValue
++};
++/*
++ The Pending attribute name and new value.
++ ex- { {"QuietBoot",Type.Integer, 0x1},
++ { "DdrFreqLimit",Type.String,"2933"}
++ }
++*/
++using PendingAttributesType = std::vector<std::pair<
++ std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>>;
++using PendingAttributesItemType =
++ std::pair<std::string,
++ std::tuple<std::string, std::variant<int64_t, std::string>>>;
++enum PendingAttributesIndex
++{
++ pendingAttrType = 0,
++ pendingAttrValue
++};
++static std::string mapAttrTypeToRedfish(const std::string_view typeDbus)
++{
++ std::string ret;
++ if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager."
++ "AttributeType.Enumeration")
++ {
++ ret = "Enumeration";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.String")
++ {
++ ret = "String";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.Password")
++ {
++ ret = "Password";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.Integer")
++ {
++ ret = "Integer";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.Boolean")
++ {
++ ret = "Boolean";
++ }
++ else
++ {
++ ret = "UNKNOWN";
++ }
++
++ return ret;
++}
++static std::string mapBoundTypeToRedfish(const std::string_view typeDbus)
++{
++ std::string ret;
++ if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.ScalarIncrement")
++ {
++ ret = "ScalarIncrement";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.LowerBound")
++ {
++ ret = "LowerBound";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.UpperBound")
++ {
++ ret = "UpperBound";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.MinStringLength")
++ {
++ ret = "MinStringLength";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.MaxStringLength")
++ {
++ ret = "MaxStringLength";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.OneOf")
++ {
++ ret = "OneOf";
++ }
++ else
++ {
++ ret = "UNKNOWN";
++ }
++
++ return ret;
++}
++
+ /**
+ * BiosService class supports handle get method for bios.
+ */
+@@ -35,6 +167,377 @@ class BiosService : public Node
+ // Get the ActiveSoftwareImage and SoftwareImages
+ fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose,
+ "", true);
++ asyncResp->res.jsonValue["@Redfish.Settings"] = {
++ {"@odata.type", "#Settings.v1_3_0.Settings"},
++ {"SettingsObject",
++ {{"@odata.id", "/redfish/v1/Systems/system/Bios/Settings"}}}};
++ asyncResp->res.jsonValue["AttributeRegistry"] = "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["Attributes"] = {};
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
++ << ec;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ const std::string& service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const std::variant<BiosBaseTableType>& retBiosTable) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "getBiosAttributes DBUS error: "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ const BiosBaseTableType* baseBiosTable =
++ std::get_if<BiosBaseTableType>(&retBiosTable);
++ nlohmann::json& attributesJson =
++ asyncResp->res.jsonValue["Attributes"];
++ if (baseBiosTable == nullptr)
++ {
++ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr ";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ for (const BiosBaseTableItemType& item : *baseBiosTable)
++ {
++ const std::string& key = item.first;
++ const std::string& itemType =
++ std::get<biosBaseAttrType>(item.second);
++ std::string attrType =
++ mapAttrTypeToRedfish(itemType);
++ if (attrType == "String")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<biosBaseCurrValue>(
++ item.second));
++ attributesJson.emplace(key, currValue != nullptr
++ ? *currValue
++ : "");
++ }
++ else if (attrType == "Integer")
++ {
++ const int64_t* currValue = std::get_if<int64_t>(
++ &std::get<biosBaseCurrValue>(item.second));
++ attributesJson.emplace(
++ key, currValue != nullptr ? *currValue : 0);
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ }
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager", "BaseBIOSTable");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
++ }
++};
++
++/**
++ * BiosSettings class supports handle GET/PATCH method for
++ * BIOS configuration pending settings.
++ */
++class BiosSettings : public Node
++{
++ public:
++ BiosSettings(App& app) :
++ Node(app, "/redfish/v1/Systems/system/Bios/Settings")
++ {
++ entityPrivileges = {{boost::beast::http::verb::get, {{"Login"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request&,
++ const std::vector<std::string>&) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/Systems/system/Bios/Settings";
++ asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios";
++ asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1";
++ asyncResp->res.jsonValue["Id"] = "BiosSettingsV1";
++ asyncResp->res.jsonValue["AttributeRegistry"] = "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["Attributes"] = {};
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
++ << ec;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ std::string service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const std::variant<PendingAttributesType>&
++ retPendingAttributes) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "getBiosSettings DBUS error: "
++ << ec;
++ messages::resourceNotFound(asyncResp->res,
++ "Systems/system/Bios",
++ "Settings");
++ return;
++ }
++ const PendingAttributesType* pendingAttributes =
++ std::get_if<PendingAttributesType>(
++ &retPendingAttributes);
++ nlohmann::json& attributesJson =
++ asyncResp->res.jsonValue["Attributes"];
++ if (pendingAttributes == nullptr)
++ {
++ BMCWEB_LOG_ERROR << "pendingAttributes == nullptr ";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ for (const PendingAttributesItemType& item :
++ *pendingAttributes)
++ {
++ const std::string& key = item.first;
++ const std::string& itemType =
++ std::get<pendingAttrType>(item.second);
++ std::string attrType =
++ mapAttrTypeToRedfish(itemType);
++ if (attrType == "String")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<pendingAttrValue>(
++ item.second));
++ attributesJson.emplace(key, currValue != nullptr
++ ? *currValue
++ : "");
++ }
++ else if (attrType == "Integer")
++ {
++ const int64_t* currValue = std::get_if<int64_t>(
++ &std::get<pendingAttrValue>(item.second));
++ attributesJson.emplace(
++ key, currValue != nullptr ? *currValue : 0);
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ }
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager",
++ "PendingAttributes");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
++ }
++};
++/**
++ * BiosAttributeRegistry class supports handle get method for BIOS attribute
++ * registry.
++ */
++class BiosAttributeRegistry : public Node
++{
++ public:
++ BiosAttributeRegistry(App& app) :
++ Node(app, "/redfish/v1/Registries/BiosAttributeRegistry/"
++ "BiosAttributeRegistry")
++ {
++ entityPrivileges = {{boost::beast::http::verb::get, {{"Login"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request&,
++ const std::vector<std::string>&) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/Registries/BiosAttributeRegistry/"
++ "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#AttributeRegistry.v1_3_2.AttributeRegistry";
++ asyncResp->res.jsonValue["Name"] = "Bios Attribute Registry";
++ asyncResp->res.jsonValue["Id"] = "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["RegistryVersion"] = "1.0.0";
++ asyncResp->res.jsonValue["Language"] = "en";
++ asyncResp->res.jsonValue["OwningEntity"] = "OpenBMC";
++ asyncResp->res.jsonValue["RegistryEntries"]["Attributes"] =
++ nlohmann::json::array();
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
++ << ec;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ std::string service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const std::variant<BiosBaseTableType>& retBiosTable) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "getBiosAttributeRegistry DBUS error: "
++ << ec;
++ messages::resourceNotFound(
++ asyncResp->res, "Registries/Bios", "Bios");
++ return;
++ }
++ const BiosBaseTableType* baseBiosTable =
++ std::get_if<BiosBaseTableType>(&retBiosTable);
++ nlohmann::json& attributeArray =
++ asyncResp->res
++ .jsonValue["RegistryEntries"]["Attributes"];
++ nlohmann::json optionsArray = nlohmann::json::array();
++ if (baseBiosTable == nullptr)
++ {
++ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr ";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ for (const BiosBaseTableItemType& item : *baseBiosTable)
++ {
++ const std::string& itemType =
++ std::get<biosBaseAttrType>(item.second);
++ std::string attrType =
++ mapAttrTypeToRedfish(itemType);
++ if (attrType == "UNKNOWN")
++ {
++ BMCWEB_LOG_ERROR << "attrType == UNKNOWN";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ nlohmann::json attributeItem;
++ attributeItem["AttributeName"] = item.first;
++ attributeItem["Type"] = attrType;
++ attributeItem["ReadOnly"] =
++ std::get<biosBaseReadonlyStatus>(item.second);
++ attributeItem["DisplayName"] =
++ std::get<biosBaseDisplayName>(item.second);
++ attributeItem["HelpText"] =
++ std::get<biosBaseDescription>(item.second);
++ attributeItem["MenuPath"] =
++ std::get<biosBaseMenuPath>(item.second);
++
++ if (attrType == "String")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<biosBaseCurrValue>(
++ item.second));
++ const std::string* defValue =
++ std::get_if<std::string>(
++ &std::get<biosBaseDefaultValue>(
++ item.second));
++ attributeItem["CurrentValue"] =
++ currValue != nullptr ? *currValue : "";
++ attributeItem["DefaultValue"] =
++ defValue != nullptr ? *defValue : "";
++ }
++ else if (attrType == "Integer")
++ {
++ const int64_t* currValue = std::get_if<int64_t>(
++ &std::get<biosBaseCurrValue>(item.second));
++ const int64_t* defValue = std::get_if<int64_t>(
++ &std::get<biosBaseDefaultValue>(
++ item.second));
++ attributeItem["CurrentValue"] =
++ currValue != nullptr ? *currValue : 0;
++ attributeItem["DefaultValue"] =
++ defValue != nullptr ? *defValue : 0;
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::vector<OptionsItemType>& optionsVector =
++ std::get<biosBaseOptions>(item.second);
++ for (const OptionsItemType& optItem : optionsVector)
++ {
++ nlohmann::json optItemJson;
++ const std::string& strOptItemType =
++ std::get<optItemType>(optItem);
++ std::string optItemTypeRedfish =
++ mapBoundTypeToRedfish(strOptItemType);
++ if (optItemTypeRedfish == "UNKNOWN")
++ {
++ BMCWEB_LOG_ERROR
++ << "optItemTypeRedfish == UNKNOWN";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ if (optItemTypeRedfish == "OneOf")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<optItemValue>(optItem));
++ optItemJson[optItemTypeRedfish] =
++ currValue != nullptr ? *currValue : "";
++ }
++ else
++ {
++ const int64_t* currValue =
++ std::get_if<int64_t>(
++ &std::get<optItemValue>(optItem));
++ optItemJson[optItemTypeRedfish] =
++ currValue != nullptr ? *currValue : 0;
++ }
++
++ optionsArray.push_back(optItemJson);
++ }
++
++ attributeItem["Value"] = optionsArray;
++ attributeArray.push_back(attributeItem);
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager", "BaseBIOSTable");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
+ }
+ };
+ /**
+diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp
+index 77fc10e..0caf01c 100644
+--- a/redfish-core/lib/message_registries.hpp
++++ b/redfish-core/lib/message_registries.hpp
+@@ -18,6 +18,7 @@
+ #include "node.hpp"
+ #include "registries.hpp"
+ #include "registries/base_message_registry.hpp"
++#include "registries/bios_registry.hpp"
+ #include "registries/openbmc_message_registry.hpp"
+ #include "registries/resource_event_message_registry.hpp"
+ #include "registries/task_event_message_registry.hpp"
+@@ -56,11 +57,12 @@ class MessageRegistryFileCollection : public Node
+ {"@odata.id", "/redfish/v1/Registries"},
+ {"Name", "MessageRegistryFile Collection"},
+ {"Description", "Collection of MessageRegistryFiles"},
+- {"Members@odata.count", 4},
++ {"Members@odata.count", 5},
+ {"Members",
+ {{{"@odata.id", "/redfish/v1/Registries/Base"}},
+ {{"@odata.id", "/redfish/v1/Registries/TaskEvent"}},
+ {{"@odata.id", "/redfish/v1/Registries/ResourceEvent"}},
++ {{"@odata.id", "/redfish/v1/Registries/BiosAttributeRegistry"}},
+ {{"@odata.id", "/redfish/v1/Registries/OpenBMC"}}}}};
+
+ res.end();
+@@ -118,6 +120,11 @@ class MessageRegistryFile : public Node
+ header = &message_registries::resource_event::header;
+ url = message_registries::resource_event::url;
+ }
++ else if (registry == "BiosAttributeRegistry")
++ {
++ header = &message_registries::bios::header;
++ dmtf.clear();
++ }
+ else
+ {
+ messages::resourceNotFound(
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch
new file mode 100644
index 000000000..6f3794478
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0007-BIOS-config-Add-support-for-PATCH-operation.patch
@@ -0,0 +1,153 @@
+From ad2b1c83bd9cb1bb6eb86bebd1867b0172e5a7a8 Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Wed, 23 Dec 2020 16:50:45 +0800
+Subject: [PATCH] BaseBiosTable: Add support for PATCH operation
+
+This commit brings in support for PATCH operation of the
+bios variables that updates the BaseBiosTable.
+
+Tested-By:
+* Passed Redfish validator
+
+* Single Attribute:
+PATCH https://${bmc}/redfish/v1/Systems/system/Bios/Settings -d
+'{"data":[{"AttributeName": <attribute name>, "AttributeType":
+<attribute type>, "AttributeValue": <attribute value>}]}'
+
+* Multiple Attributes:
+PATCH https://${bmc}/redfish/v1/Systems/system/Bios/Settings -d
+'{"data":[{"AttributeName": <attribute name>, "AttributeType":
+<attribute type>, "AttributeValue": <attribute value>},
+{"AttributeName": <attribute name>, "AttributeType":
+<attribute type>, "AttributeValue": <attribute value>}]}'
+
+This makes use of the "Set" of "PendingAttributes" in the
+backend and that updates the BaseBiosTable.
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ redfish-core/lib/bios.hpp | 94 ++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 93 insertions(+), 1 deletion(-)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index 5f8c91b..cf76fe0 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -96,6 +96,29 @@ static std::string mapAttrTypeToRedfish(const std::string_view typeDbus)
+
+ return ret;
+ }
++static std::string mapRedfishToAttrType(const std::string_view type)
++{
++ std::string ret;
++ if (type == "string")
++ {
++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
++ }
++ else if (type == "int")
++ {
++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer";
++ }
++ else if (type == "enum")
++ {
++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType."
++ "Enumeration";
++ }
++ else
++ {
++ ret = "UNKNOWN";
++ }
++
++ return ret;
++}
+ static std::string mapBoundTypeToRedfish(const std::string_view typeDbus)
+ {
+ std::string ret;
+@@ -262,7 +285,9 @@ class BiosSettings : public Node
+ BiosSettings(App& app) :
+ Node(app, "/redfish/v1/Systems/system/Bios/Settings")
+ {
+- entityPrivileges = {{boost::beast::http::verb::get, {{"Login"}}}};
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}};
+ }
+
+ private:
+@@ -359,6 +384,73 @@ class BiosSettings : public Node
+ "/xyz/openbmc_project/bios_config/manager",
+ std::array<const char*, 0>());
+ }
++
++ void doPatch(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>&) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++
++ nlohmann::json inpJson;
++
++ if (!redfish::json_util::readJson(req, asyncResp->res, "data", inpJson))
++ {
++ return;
++ }
++
++ for (auto& attrInfo : inpJson)
++ {
++ std::optional<std::string> attrName;
++ std::optional<std::string> attrType;
++ std::optional<std::string> attrValue;
++ if (!json_util::getValueFromJsonObject(attrInfo, "AttributeName",
++ attrName))
++ {
++ messages::propertyMissing(asyncResp->res, "AttributeName");
++ return;
++ }
++ if (!json_util::getValueFromJsonObject(attrInfo, "AttributeType",
++ attrType))
++ {
++ messages::propertyMissing(asyncResp->res, "AttributeType");
++ return;
++ }
++ if (!json_util::getValueFromJsonObject(attrInfo, "AttributeValue",
++ attrValue))
++ {
++ messages::propertyMissing(asyncResp->res, "AttributeValue");
++ return;
++ }
++ std::string biosAttrType = mapRedfishToAttrType(*attrType);
++
++ if (biosAttrType == "UNKNOWN")
++ {
++ BMCWEB_LOG_ERROR << "Invalid attribute type";
++ messages::propertyValueNotInList(asyncResp->res,
++ "AttributeType", *attrType);
++ return;
++ }
++
++ PendingAttributesType pendingAttributes;
++ pendingAttributes.emplace_back(std::make_pair(
++ *attrName, std::make_tuple(biosAttrType, *attrValue)));
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "doPatch resp_handler got error "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ },
++ "xyz.openbmc_project.BIOSConfigManager",
++ "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
++ std::variant<PendingAttributesType>(pendingAttributes));
++ }
++ }
+ };
+ /**
+ * BiosAttributeRegistry class supports handle get method for BIOS attribute
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0008-Add-support-to-ResetBios-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0008-Add-support-to-ResetBios-action.patch
new file mode 100644
index 000000000..7e4e2e8d8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0008-Add-support-to-ResetBios-action.patch
@@ -0,0 +1,62 @@
+From a78eecb032eefeb84da3ec042700a40f55ae8f10 Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Wed, 23 Dec 2020 22:47:56 +0800
+Subject: [PATCH] Add support to ResetBios action
+
+Tested:
+
+Bios reset flag can be modified throw redfish
+POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios
+
+Change-Id: I5e5fbdd70d4a3ce3b976cc2eb0a7d9a2a3adb124
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+
+---
+ redfish-core/lib/bios.hpp | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index cf76fe0..7b6fc3d 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -643,7 +643,7 @@ class BiosReset : public Node
+ Node(app, "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios/")
+ {
+ entityPrivileges = {
+- {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ }
+
+ private:
+@@ -655,19 +655,23 @@ class BiosReset : public Node
+ const std::vector<std::string>&) override
+ {
+ auto asyncResp = std::make_shared<AsyncResp>(res);
+-
++ std::string resetFlag =
++ "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.FactoryDefaults";
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+- BMCWEB_LOG_ERROR << "Failed to reset bios: " << ec;
++ BMCWEB_LOG_ERROR << "doPost bios reset got error " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
++ BMCWEB_LOG_DEBUG << "bios reset action is done";
+ },
+- "org.open_power.Software.Host.Updater",
+- "/xyz/openbmc_project/software",
+- "xyz.openbmc_project.Common.FactoryReset", "Reset");
++ "xyz.openbmc_project.BIOSConfigManager",
++ "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.BIOSConfig.Manager", "ResetBIOSSettings",
++ std::variant<std::string>(resetFlag));
+ }
+ };
+ } // namespace redfish
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch
new file mode 100644
index 000000000..976292197
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0009-Add-support-to-ChangePassword-action.patch
@@ -0,0 +1,139 @@
+From ede8454491b554c2494a61f42993fa2e39b4d865 Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Wed, 23 Dec 2020 14:41:23 +0800
+Subject: [PATCH] Add support to ChangePassword action
+
+Tested:
+
+Passed Redfish validator.
+Bios change password:
+root@intel-obmc:~# cat /var/lib/bios-settings-manager/seedData
+{
+"UserPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D",
+"AdminPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D",
+"Seed": "123456",
+"HashAlgo": "SHA384"
+}
+POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword
+{
+ "NewPassword": "12345678",
+ "OldPassword": "1234567890",
+ "PasswordName": "Administrator"
+}
+root@intel-obmc:~# cat /var/lib/bios-settings-manager/passwordData
+{
+ "CurrentPassword": "1234567890",
+ "IsAdminPwdChanged": 1,
+ "IsUserPwdChanged": 0,
+ "NewPassword": "2DD65D57EB60B1D92C5F3D2DC84724FCEE7BC02E57AA75E834712266ED94CAC704047B2FF7CEC1C36BED280B36BB5AC6",
+ "UserName": "Administrator"
+}
+
+Change-Id: I90319a68da0b0a7f9c5cd65a8cb8cf52269a5f52
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ redfish-core/include/redfish.hpp | 1 +
+ redfish-core/lib/bios.hpp | 70 ++++++++++++++++++++++++++++++++
+ 2 files changed, 71 insertions(+)
+
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index a8e5cf2..dabf78e 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -160,6 +160,7 @@ class RedfishService
+ nodes.emplace_back(std::make_unique<BiosSettings>(app));
+ nodes.emplace_back(std::make_unique<BiosAttributeRegistry>(app));
+ nodes.emplace_back(std::make_unique<BiosReset>(app));
++ nodes.emplace_back(std::make_unique<BiosChangePassword>(app));
+ #ifdef BMCWEB_ENABLE_VM_NBDPROXY
+ nodes.emplace_back(std::make_unique<VirtualMedia>(app));
+ nodes.emplace_back(std::make_unique<VirtualMediaCollection>(app));
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index 7b6fc3d..61b396b 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -186,6 +186,9 @@ class BiosService : public Node
+ asyncResp->res.jsonValue["Actions"]["#Bios.ResetBios"] = {
+ {"target",
+ "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"}};
++ asyncResp->res.jsonValue["Actions"]["#Bios.ChangePassword"] = {
++ {"target",
++ "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword"}};
+
+ // Get the ActiveSoftwareImage and SoftwareImages
+ fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose,
+@@ -674,4 +677,71 @@ class BiosReset : public Node
+ std::variant<std::string>(resetFlag));
+ }
+ };
++
++/**
++ * BiosChangePassword class supports handle POST method for change bios
++ * password. The class retrieves and sends data directly to D-Bus.
++ */
++class BiosChangePassword : public Node
++{
++ public:
++ BiosChangePassword(App& app) :
++ Node(app,
++ "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword/")
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
++ }
++
++ private:
++ /**
++ * Function handles POST method request.
++ * Analyzes POST body message before sends Reset request data to D-Bus.
++ */
++ void doPost(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>&) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ std::string currentPassword, newPassword, userName;
++ if (!json_util::readJson(req, res, "NewPassword", newPassword,
++ "OldPassword", currentPassword, "PasswordName",
++ userName))
++ {
++ return;
++ }
++ if (currentPassword.empty())
++ {
++ messages::actionParameterUnknown(asyncResp->res, "ChangePassword",
++ "OldPassword");
++ return;
++ }
++ if (newPassword.empty())
++ {
++ messages::actionParameterUnknown(asyncResp->res, "ChangePassword",
++ "NewPassword");
++ return;
++ }
++ if (userName.empty())
++ {
++ messages::actionParameterUnknown(asyncResp->res, "ChangePassword",
++ "PasswordName");
++ return;
++ }
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_CRITICAL << "Failed in doPost(BiosChangePassword) "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ },
++ "xyz.openbmc_project.BIOSConfigPassword",
++ "/xyz/openbmc_project/bios_config/password",
++ "xyz.openbmc_project.BIOSConfig.Password", "ChangePassword",
++ userName, currentPassword, newPassword);
++ }
++};
++
+ } // namespace redfish
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch
new file mode 100644
index 000000000..a9c46f487
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch
@@ -0,0 +1,57 @@
+From a76314cd29f5cbcf19142b7120c5bf83358910fd Mon Sep 17 00:00:00 2001
+From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+Date: Mon, 28 Dec 2020 18:55:57 +0000
+Subject: [PATCH] managers: add attributes for Manager.CommandShell
+
+Issue: ConnectTypesSupported, ServiceEnabled and
+ MaxConcurrentSessions Attributes are missing for
+ Manager.CommandShell, though Requirement mandates it.
+
+Fix: Added missing attributes to Manager.CommandShell
+
+Tested:
+1. Verified redfish validator passed
+2. Get bmc details from Redfish
+Redfish URI: https://<BMC IP>/redfish/v1/Managers/bmc
+Response:
+{
+ "@odata.id": "/redfish/v1/Managers/bmc",
+ "@odata.type": "#Manager.v1_9_0.Manager",
+....
+....
+ "CommandShell": {
+ "ConnectTypesSupported": [
+ "SSH",
+ "IPMI"
+ ],
+ "MaxConcurrentSessions": 4,
+ "ServiceEnabled": true
+ },
+....
+....
+
+Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+---
+ redfish-core/lib/managers.hpp | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
+index 6347caf..c401ca9 100644
+--- a/redfish-core/lib/managers.hpp
++++ b/redfish-core/lib/managers.hpp
+@@ -1767,6 +1767,12 @@ class Manager : public Node
+ res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15;
+ res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
+ "SSH"};
++ // Fill in CommandShell info
++ res.jsonValue["CommandShell"]["ServiceEnabled"] = true;
++ res.jsonValue["CommandShell"]["MaxConcurrentSessions"] = 4;
++ res.jsonValue["CommandShell"]["ConnectTypesSupported"] = {"SSH",
++ "IPMI"};
++
+ #ifdef BMCWEB_ENABLE_KVM
+ // Fill in GraphicalConsole info
+ res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch
new file mode 100644
index 000000000..e52dff3f4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch
@@ -0,0 +1,135 @@
+From b9747ecfce682f15dce0bb6e41e0c894f29419f3 Mon Sep 17 00:00:00 2001
+From: Snehalatha Venkatesh <snehalathax.v@intel.com>
+Date: Thu, 8 Apr 2021 14:42:07 +0000
+Subject: [PATCH] bmcweb: Add PhysicalContext to Thermal resources
+
+Adding PhysicalContext to make redfish data compliance with OCP
+Server Mgmt Interface v0.2.1.pdf and specific to Thermal resources.
+https://github.com/opencomputeproject/OCP-Profiles/blob/master/
+OCPServerHardwareManagement.v0_2_4.json
+
+Tested:
+1. Redfish validator - passed for this new change
+2. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Thermal
+Response:
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/0",
+ "@odata.type": "#Thermal.v1_3_0.Temperature",
+ "LowerThresholdCritical": 0.0,
+ "LowerThresholdNonCritical": 5.0,
+ "MaxReadingRangeTemp": 127.0,
+ "MemberId": "BMC_Temp",
+ "MinReadingRangeTemp": -128.0,
+ "Name": "BMC Temp",
+ "PhysicalContext": "SystemBoard",
+ "ReadingCelsius": 25.75,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 115.0,
+ "UpperThresholdNonCritical": 110.0
+},
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/1",
+ "@odata.type": "#Thermal.v1_3_0.Temperature",
+ "LowerThresholdCritical": 0.0,
+ "LowerThresholdNonCritical": 5.0,
+ "MaxReadingRangeTemp": 255.0,
+ "MemberId": "CPU1_P12V_PVCCIN_VR_Temp",
+ "MinReadingRangeTemp": 0.0,
+ "Name": "CPU1 P12V PVCCIN VR Temp",
+ "PhysicalContext": "CPU",
+ "ReadingCelsius": 41.0,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 115.0,
+ "UpperThresholdNonCritical": 110.0
+},
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/28",
+ "@odata.type": "#Thermal.v1_3_0.Temperature",
+ "LowerThresholdCritical": 0.0,
+ "LowerThresholdNonCritical": 5.0,
+ "MaxReadingRangeTemp": 127.0,
+ "MemberId": "Inlet_BRD_Temp",
+ "MinReadingRangeTemp": -128.0,
+ "Name": "Inlet BRD Temp",
+ "PhysicalContext": "Intake",
+ "ReadingCelsius": 23.187,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 115.0,
+ "UpperThresholdNonCritical": 110.0
+}
+3. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Power
+Response:
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Power#/Voltages/3",
+ "@odata.type": "#Power.v1_0_0.Voltage",
+ "LowerThresholdCritical": 1.648,
+ "LowerThresholdNonCritical": 1.699,
+ "MaxReadingRange": 2.3984009912875566,
+ "MemberId": "P1V8_PCH",
+ "MinReadingRange": 0.0,
+ "Name": "P1V8 PCH",
+ "ReadingVolts": 1.8055,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 1.961,
+ "UpperThresholdNonCritical": 1.904
+}
+4. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Sensors/PSU1_Input_Current
+Response:
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Sensors/PSU1_Input_Current",
+ "@odata.type": "#Sensor.v1_0_0.Sensor",
+ "Id": "PSU1_Input_Current",
+ "Name": "PSU1 Input Current",
+ "Reading": 0.947,
+ "ReadingRangeMax": 12.0,
+ "ReadingRangeMin": 0.0,
+ "ReadingType": "Current",
+ "ReadingUnits": "A",
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ }
+}
+Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com>
+---
+ redfish-core/lib/sensors.hpp | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
+index 9f06d2f..40fcdf8 100644
+--- a/redfish-core/lib/sensors.hpp
++++ b/redfish-core/lib/sensors.hpp
+@@ -964,6 +964,18 @@ inline void objectInterfacesToJson(
+ {
+ unit = "/ReadingCelsius"_json_pointer;
+ sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature";
++ if (sensorName.find("CPU") != std::string::npos)
++ {
++ sensorJson["PhysicalContext"] = "CPU";
++ }
++ else if (sensorName.find("Inlet") != std::string::npos)
++ {
++ sensorJson["PhysicalContext"] = "Intake";
++ }
++ else
++ {
++ sensorJson["PhysicalContext"] = "SystemBoard";
++ }
+ // TODO(ed) Documentation says that path should be type fan_tach,
+ // implementation seems to implement fan
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch
new file mode 100644
index 000000000..3ef4ee2de
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch
@@ -0,0 +1,67 @@
+From 1f572a1991fc8d9b08689aa6e3470080467977a7 Mon Sep 17 00:00:00 2001
+From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+Date: Thu, 15 Apr 2021 10:59:42 +0000
+Subject: [PATCH] Log RedFish event for Invalid login attempt
+
+This commit adds support for logging RedFish event log while user tries
+to attempt login with invalid credentials.
+When user trying to login with invalid credentials on HTTPS interface
+like WebUI and RedFish, event should be logged in RedFish event log.
+This event log is useful for further analysis to debug the root-cause
+for failure.
+
+Tested:
+1. Verified RedFish validator passed
+2. Login with wrong credentials on HTTPS interface.
+3. Verified for RedFish/WebUI events. RedFish event logged successfully.
+GET: https://BMC-IP/redfish/v1/Systems/system/LogServices/
+ EventLog/Entries
+Response:
+"Members": [
+{
+ "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/
+ Entries/1618466128",
+ "@odata.type": "#LogEntry.v1_4_0.LogEntry",
+ "Created": "2021-04-15T05:55:28+00:00",
+ "EntryType": "Event",
+ "Id": "1618466128",
+ "Message": "Invalid username or password attempted on HTTPS.",
+ "MessageArgs": [
+ "HTTPS"
+ ],
+ "MessageId": "OpenBMC.0.1.InvalidLoginAttempted",
+ "Name": "System Event Log Entry",
+ "Severity": "Warning"
+}
+
+Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+---
+ include/pam_authenticate.hpp | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp
+index 12f19c0..01bf301 100644
+--- a/include/pam_authenticate.hpp
++++ b/include/pam_authenticate.hpp
+@@ -1,6 +1,7 @@
+ #pragma once
+
+ #include <security/pam_appl.h>
++#include <systemd/sd-journal.h>
+
+ #include <boost/utility/string_view.hpp>
+
+@@ -75,6 +76,10 @@ inline int pamAuthenticateUser(const std::string_view username,
+ PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
+ if (retval != PAM_SUCCESS)
+ {
++ sd_journal_send("MESSAGE= %s", "Invalid login attempted on HTTPS",
++ "PRIORITY=%i", LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.InvalidLoginAttempted",
++ "REDFISH_MESSAGE_ARGS=%s", "HTTPS", NULL);
+ pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
+ return retval;
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch
new file mode 100644
index 000000000..8b0d90fe0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch
@@ -0,0 +1,59 @@
+From 6c10adb53d3247f65e5d9399290e6b8e7962cdef Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 28 Apr 2021 17:19:50 -0700
+Subject: [PATCH] Add UART routing logic into host console connection flow
+
+Switching UART routing when starting obmc-service introduces garbled
+character printing out on physical host serial output and it's
+inevitable so this commit moves the routing logic into host console
+connection flow in bmcweb to avoid the issue until SOL is actually
+activated.
+
+Tested: The garbled character printing out was not observed during
+BMC booting. SOL worked well.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ include/obmc_console.hpp | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp
+index cdb19901e82d..9c4ae8821074 100644
+--- a/include/obmc_console.hpp
++++ b/include/obmc_console.hpp
+@@ -22,6 +22,9 @@ static boost::container::flat_set<crow::websocket::Connection*> sessions;
+
+ static bool doingWrite = false;
+
++constexpr char const* uartMuxCtrlPath = "/sys/bus/platform/drivers/aspeed-uart-routing/1e78909c.uart_routing/raw";
++constexpr char const* uartMuxCtrlVal = "0x03450003";
++
+ inline void doWrite()
+ {
+ if (doingWrite)
+@@ -110,6 +113,22 @@ inline void connectHandler(const boost::system::error_code& ec)
+ return;
+ }
+
++ FILE* file = fopen(uartMuxCtrlPath, "w");
++ if (file != nullptr)
++ {
++ int rc = fputs(uartMuxCtrlVal, file);
++ fclose(file);
++ if (rc < 0)
++ {
++ BMCWEB_LOG_ERROR << "Couldn't change UART routing: " << rc;
++ for (crow::websocket::Connection* session : sessions)
++ {
++ session->close("Error in connecting to host port");
++ }
++ return;
++ }
++ }
++
+ doWrite();
+ doRead();
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch
new file mode 100644
index 000000000..5ffc259c0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0034-recommended-fixes-by-crypto-review-team.patch
@@ -0,0 +1,75 @@
+From aaaa117817687a05284f8bfff07e2404e0d616b7 Mon Sep 17 00:00:00 2001
+From: Radivoje Jovanovic <radivoje.jovanovic@intel.com>
+Date: Thu, 10 Dec 2020 13:42:20 -0800
+Subject: [PATCH] recommended fixes by crypto review team
+
+some curves/cyphers are forbiden to be used by
+Intel crypto team.
+Only enable approved ones.
+the patch was created by aleksandr.v.tereschenko@intel.com
+
+Signed-off-by: Radivoje Jovanovic <radivoje.jovanovic@intel.com>
+---
+ include/ssl_key_handler.hpp | 39 ++++++++++++++++++++-----------------
+ 1 file changed, 21 insertions(+), 18 deletions(-)
+
+diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp
+index 39e83d7..8de7349 100644
+--- a/include/ssl_key_handler.hpp
++++ b/include/ssl_key_handler.hpp
+@@ -381,31 +381,34 @@ inline std::shared_ptr<boost::asio::ssl::context>
+ mSslContext->use_private_key_file(sslPemFile,
+ boost::asio::ssl::context::pem);
+
+- // Set up EC curves to auto (boost asio doesn't have a method for this)
+- // There is a pull request to add this. Once this is included in an asio
+- // drop, use the right way
+- // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
+- if (SSL_CTX_set_ecdh_auto(mSslContext->native_handle(), 1) != 1)
++ std::string handshakeCurves = "P-384:P-521:X448";
++ if (SSL_CTX_set1_groups_list(mSslContext->native_handle(), handshakeCurves.c_str()) != 1)
+ {
+- BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n";
++ BMCWEB_LOG_ERROR << "Error setting ECDHE group list\n";
+ }
+
+- std::string mozillaModern = "ECDHE-ECDSA-AES256-GCM-SHA384:"
+- "ECDHE-RSA-AES256-GCM-SHA384:"
+- "ECDHE-ECDSA-CHACHA20-POLY1305:"
+- "ECDHE-RSA-CHACHA20-POLY1305:"
+- "ECDHE-ECDSA-AES128-GCM-SHA256:"
+- "ECDHE-RSA-AES128-GCM-SHA256:"
+- "ECDHE-ECDSA-AES256-SHA384:"
+- "ECDHE-RSA-AES256-SHA384:"
+- "ECDHE-ECDSA-AES128-SHA256:"
+- "ECDHE-RSA-AES128-SHA256";
++ std::string tls12Ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384:"
++ "ECDHE-RSA-AES256-GCM-SHA384";
++ std::string tls13Ciphers = "TLS_AES_256_GCM_SHA384";
+
+ if (SSL_CTX_set_cipher_list(mSslContext->native_handle(),
+- mozillaModern.c_str()) != 1)
++ tls12Ciphers.c_str()) != 1)
+ {
+- BMCWEB_LOG_ERROR << "Error setting cipher list\n";
++ BMCWEB_LOG_ERROR << "Error setting TLS 1.2 cipher list\n";
+ }
++
++ if (SSL_CTX_set_ciphersuites(mSslContext->native_handle(),
++ tls13Ciphers.c_str()) != 1)
++ {
++ BMCWEB_LOG_ERROR << "Error setting TLS 1.3 cipher list\n";
++ }
++
++ if ((SSL_CTX_set_options(mSslContext->native_handle(),
++ SSL_OP_CIPHER_SERVER_PREFERENCE) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0)
++ {
++ BMCWEB_LOG_ERROR << "Error setting TLS server preference option\n";
++ }
++
+ return mSslContext;
+ }
+ } // namespace ensuressl
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch
new file mode 100644
index 000000000..b171a8b2c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch
@@ -0,0 +1,98 @@
+From df571ddf0596f73c0318da3a90b9813e6df19dd9 Mon Sep 17 00:00:00 2001
+From: "Arun P. Mohanan" <arun.p.m@linux.intel.com>
+Date: Wed, 27 Jan 2021 18:22:58 +0530
+Subject: [PATCH] Add state sensor messages to the registry
+
+Add messages to registry to indicate state sensor state change.
+
+Tested:
+Build and redfish validator passes.
+Logged these events and confirmed that they appear as expected on
+Redfish.
+GET: https://<BMC IP>/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180
+{
+ "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180",
+ "@odata.type": "#LogEntry.v1_4_0.LogEntry",
+ "Created": "2021-02-05T12:29:40+00:00",
+ "EntryType": "Event",
+ "Id": "1612528180",
+ "Message": "Operational Fault Status of Card_health_1 state sensor changed from Error to Normal.",
+ "MessageArgs": [
+ "Operational Fault Status",
+ "Card_health_1",
+ "Error",
+ "Normal"
+ ],
+ "MessageId": "OpenBMC.0.1.StateSensorNormal",
+ "Name": "System Event Log Entry",
+ "Severity": "OK"
+}
+
+Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com>
+---
+ .../registries/openbmc_message_registry.hpp | 36 +++++++++++++++++--
+ 1 file changed, 34 insertions(+), 2 deletions(-)
+
+diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
+index 5eb9380..dbea97c 100644
+--- a/redfish-core/include/registries/openbmc_message_registry.hpp
++++ b/redfish-core/include/registries/openbmc_message_registry.hpp
+@@ -29,7 +29,7 @@ const Header header = {
+ "0.1.0",
+ "OpenBMC",
+ };
+-constexpr std::array<MessageEntry, 187> registry = {
++constexpr std::array<MessageEntry, 190> registry = {
+ MessageEntry{
+ "ADDDCCorrectable",
+ {
+@@ -2318,6 +2318,39 @@ constexpr std::array<MessageEntry, 187> registry = {
+ {},
+ "None.",
+ }},
++ MessageEntry{
++ "StateSensorNormal",
++ {
++ "Indicates that a state sensor has changed state to normal.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "OK",
++ "OK",
++ 4,
++ {"string", "string", "string", "string"},
++ "None.",
++ }},
++ MessageEntry{
++ "StateSensorWarning",
++ {
++ "Indicates that a state sensor has changed state to warning.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "Warning",
++ "Warning",
++ 4,
++ {"string", "string", "string", "string"},
++ "Check sensor subsystem for errors.",
++ }},
++ MessageEntry{
++ "StateSensorCritical",
++ {
++ "Indicates that a state sensor has changed state to critical.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "Critical",
++ "Critical",
++ 4,
++ {"string", "string", "string", "string"},
++ "Check sensor subsystem for errors.",
++ }},
+ MessageEntry{"SystemInterfaceDisabledProvisioned",
+ {
+ "Indicates that the system interface is in the disabled "
+@@ -2410,6 +2443,5 @@ constexpr std::array<MessageEntry, 187> registry = {
+ {"string"},
+ "None.",
+ }},
+-
+ };
+ } // namespace redfish::message_registries::openbmc
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch
new file mode 100644
index 000000000..3e3f69d1c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch
@@ -0,0 +1,50 @@
+From 95f002dc969d7d6d64dbf2ee0db7dc1c1c6a9173 Mon Sep 17 00:00:00 2001
+From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+Date: Thu, 18 Mar 2021 11:30:28 +0100
+Subject: [PATCH] Revert "Disable nbd proxy from the build"
+
+NBD Proxy has been disabled upstream. Reenable as we use it for Virtual
+Media
+
+This reverts commit efb8062c306474942bc94f15d748b2eb0b58fbb6.
+---
+ meson.build | 2 +-
+ meson_options.txt | 9 +--------
+ 2 files changed, 2 insertions(+), 9 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 66a066b..cef0a49 100644
+--- a/meson.build
++++ b/meson.build
+@@ -81,7 +81,7 @@ feature_map = {
+ 'static-hosting' : '-DBMCWEB_ENABLE_STATIC_HOSTING',
+ 'insecure-tftp-update' : '-DBMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE',
+ 'validate-unsecure-feature' : '-DBMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE',
+-#'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY',
++'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY',
+ 'vm-websocket' : '-DBMCWEB_ENABLE_VM_WEBSOCKET',
+ }
+
+diff --git a/meson_options.txt b/meson_options.txt
+index 9611631..7ee3ebb 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -3,14 +3,7 @@ option('yocto-deps', type: 'feature', value: 'disabled', description : 'Use YOCT
+ option('kvm', type : 'feature',value : 'enabled', description : 'Enable the KVM host video WebSocket. Path is \'/kvm/0\'. Video is from the BMC\'s \'/dev/video\' device.')
+ option ('tests', type : 'feature', value : 'enabled', description : 'Enable Unit tests for bmcweb')
+ option('vm-websocket', type : 'feature', value : 'enabled', description : '''Enable the Virtual Media WebSocket. Path is \'/vm/0/0\'to open the websocket. See https://github.com/openbmc/jsnbd/blob/master/README.''')
+-
+-# if you use this option and are seeing this comment, please comment here:
+-# https://github.com/openbmc/bmcweb/issues/188 and put forward your intentions
+-# for this code. At this point, no daemon has been upstreamed that implements
+-# this interface, so for the moment this appears to be dead code; In leiu of
+-# removing it, it has been disabled to try to give those that use it the
+-# opportunity to upstream their backend implementation
+-#option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.')
++option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.')
+ option('rest', type : 'feature', value : 'enabled', description : '''Enable Phosphor REST (D-Bus) APIs. Paths directly map Phosphor D-Bus object paths, for example, \'/xyz/openbmc_project/logging/entry/enumerate\'. See https://github.com/openbmc/docs/blob/master/rest-api.md.''')
+ option('redfish', type : 'feature',value : 'enabled', description: 'Enable Redfish APIs. Paths are under \'/redfish/v1/\'. See https://github.com/openbmc/bmcweb/blob/master/DEVELOPING.md#redfish.')
+ option('host-serial-socket', type : 'feature', value : 'enabled', description : 'Enable host serial console WebSocket. Path is \'/console0\'. See https://github.com/openbmc/docs/blob/master/console.md.')
+--
+2.26.2
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-bmcweb-crashes-if-socket-directory-not-present.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-bmcweb-crashes-if-socket-directory-not-present.patch
new file mode 100644
index 000000000..bc023839f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-bmcweb-crashes-if-socket-directory-not-present.patch
@@ -0,0 +1,44 @@
+From 48fe2a68d634970795f9ff13903afbedca801088 Mon Sep 17 00:00:00 2001
+From: Nidhin MS <nidhin.ms@intel.com>
+Date: Wed, 14 Apr 2021 11:28:44 +0530
+Subject: [PATCH] Fix: bmcweb crashes if socket directory not present
+
+When trying to mount virtual media image bmcweb tries to create unix
+socket and if the parent directory does not exist
+stream_protocol::acceptor throws error and bmcweb crashes. Fix the same
+
+Tested:
+Removed directory and mounted the vm image. bmcweb crash was not
+observed
+
+Change-Id: I3aea1d8e197c06238f425a97435c01d3c80552a9
+Signed-off-by: Nidhin MS <nidhin.ms@intel.com>
+---
+ include/nbd_proxy.hpp | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp
+index 7b90e90..3b28823 100644
+--- a/include/nbd_proxy.hpp
++++ b/include/nbd_proxy.hpp
+@@ -397,6 +397,17 @@ inline void requestRoutes(App& app)
+ // If the socket file exists (i.e. after bmcweb crash),
+ // we cannot reuse it.
+ std::remove((*socketValue).c_str());
++ std::filesystem::path socketPath(*socketValue);
++ std::error_code fsErr;
++ if (!std::filesystem::exists(socketPath.parent_path(),
++ fsErr))
++ {
++ BMCWEB_LOG_ERROR
++ << "VirtualMedia socket directory not present. "
++ << socketPath.parent_path();
++ conn.close("Unable to create unix socket");
++ return;
++ }
+
+ sessions[&conn] = std::make_shared<NbdProxyServer>(
+ conn, *socketValue, *endpointValue,
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-comparison-for-proxy-legacy-mode.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-comparison-for-proxy-legacy-mode.patch
new file mode 100644
index 000000000..f02e5e1a4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0039-Fix-comparison-for-proxy-legacy-mode.patch
@@ -0,0 +1,51 @@
+From 7cde56820505a4f750cc67caa0dc9300688f3fd9 Mon Sep 17 00:00:00 2001
+From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+Date: Tue, 13 Apr 2021 15:16:43 +0200
+Subject: [PATCH] Fix comparison for proxy/legacy mode
+
+After sdbusplus made available string path wrapper VM code has been
+updated to use it. This makes legacy mode unusable as appropriate
+change introduced a problem with comparison of DBus object path, which
+is now fixed.
+
+Tested:
+After applying this fix, legacy mode has InsertMedia action enabled
+again.
+
+Change-Id: I062994f23b77cb7ab9c4421f296a65a80b9ce896
+Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 365290b..dda73d8 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -274,10 +274,22 @@ static void getVmData(const std::shared_ptr<AsyncResp>& aResp,
+ continue;
+ }
+
++ auto mode = item.first.parent_path();
++ auto type = mode.parent_path();
++ if (mode.filename().empty() || type.filename().empty())
++ {
++ continue;
++ }
++
++ if (type.filename() != "VirtualMedia")
++ {
++ continue;
++ }
++
+ aResp->res.jsonValue = vmItemTemplate(name, resName);
+
+ // Check if dbus path is Legacy type
+- if (thispath.find("VirtualMedia/Legacy") != std::string::npos)
++ if (mode.filename() == "Legacy")
+ {
+ aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
+ ["target"] =
+--
+2.26.2
+
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/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch
new file mode 100644
index 000000000..b46d30149
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch
@@ -0,0 +1,543 @@
+From ae55e89c14ea5abef0895409c956f5f4c38f330f Mon Sep 17 00:00:00 2001
+From: Sunitha Harish <sunithaharish04@gmail.com>
+Date: Fri, 19 Feb 2021 13:38:31 +0530
+Subject: [PATCH 1/2] EventService : Fix retry handling for http-client
+
+When the event send/receive is failed, the bmcweb does not handle
+the failure to tear-down the complete connection and start a fresh
+
+The keep-alive header from the event listener is read to update
+the connection states, so that the connection will be kept alive
+or closed as per the subscriber's specifications
+
+Updated the connection state machine to handle retry logic properly.
+Avoided multiple simultaneous async calls which crashes the bmcweb. So
+added connBusy flag which protects simultaneous async calls.
+
+Used boost http response parser as parser for producing the response
+message. Set the parser skip option to handle the empty response message
+from listening server.
+
+Tested by:
+ - Subscribe for the events at BMC using DMTF event listener
+ - Generate an event and see the same is received at the listener's console
+ - Update the listner to change the keep-alive to true/false and
+ observe the http-client connection states at bmcweb
+
+Change-Id: Ibb45691f139916ba2954da37beda9d4f91c7cef3
+Signed-off-by: Sunitha Harish <sunithaharish04@gmail.com>
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+---
+ http/http_client.hpp | 289 ++++++++++--------
+ .../include/event_service_manager.hpp | 2 +-
+ 2 files changed, 163 insertions(+), 128 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index 992ac2b..d116f6d 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -34,22 +34,28 @@ namespace crow
+ {
+
+ static constexpr uint8_t maxRequestQueueSize = 50;
++static constexpr unsigned int httpReadBodyLimit = 8192;
+
+ enum class ConnState
+ {
+ initialized,
+ resolveInProgress,
+ resolveFailed,
++ resolved,
+ connectInProgress,
+ connectFailed,
+ connected,
+ sendInProgress,
+ sendFailed,
++ recvInProgress,
+ recvFailed,
+ idle,
+- suspended,
++ closeInProgress,
+ closed,
+- terminated
++ suspended,
++ terminated,
++ abortConnection,
++ retry
+ };
+
+ class HttpClient : public std::enable_shared_from_this<HttpClient>
+@@ -58,11 +64,14 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ crow::async_resolve::Resolver resolver;
+ boost::beast::tcp_stream conn;
+ boost::asio::steady_timer timer;
+- boost::beast::flat_buffer buffer;
++ boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
+ boost::beast::http::request<boost::beast::http::string_body> req;
+- boost::beast::http::response<boost::beast::http::string_body> res;
+- std::vector<std::pair<std::string, std::string>> headers;
+- std::queue<std::string> requestDataQueue;
++ std::optional<
++ boost::beast::http::response_parser<boost::beast::http::string_body>>
++ parser;
++ boost::asio::ip::tcp::endpoint endpoint;
++ boost::circular_buffer_space_optimized<std::string> requestDataQueue{};
++ std::vector<boost::asio::ip::tcp::endpoint> endPoints;
+ ConnState state;
+ std::string subId;
+ std::string host;
+@@ -76,12 +85,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+
+ void doResolve()
+ {
+- if (state == ConnState::resolveInProgress)
+- {
+- return;
+- }
+ state = ConnState::resolveInProgress;
+-
+ BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":" << port;
+
+ auto respHandler =
+@@ -89,78 +93,56 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ const boost::beast::error_code ec,
+ const std::vector<boost::asio::ip::tcp::endpoint>&
+ endpointList) {
+- if (ec)
++ if (ec || (endpointList.size() == 0))
+ {
+ BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message();
+ self->state = ConnState::resolveFailed;
+- self->checkQueue();
++ self->handleConnState();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "Resolved";
+- self->doConnect(endpointList);
++ self->endPoints.assign(endpointList.begin(),
++ endpointList.end());
++ self->state = ConnState::resolved;
++ self->handleConnState();
+ };
+ resolver.asyncResolve(host, port, std::move(respHandler));
+ }
+
+- void doConnect(
+- const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
++ void doConnect()
+ {
+- if (state == ConnState::connectInProgress)
+- {
+- return;
+- }
+ state = ConnState::connectInProgress;
+
+ BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port;
+
+ conn.expires_after(std::chrono::seconds(30));
+ conn.async_connect(
+- endpointList, [self(shared_from_this())](
+- const boost::beast::error_code ec,
+- const boost::asio::ip::tcp::endpoint& endpoint) {
++ endPoints, [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const boost::asio::ip::tcp::endpoint& endpoint) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Connect " << endpoint
+ << " failed: " << ec.message();
+ self->state = ConnState::connectFailed;
+- self->checkQueue();
++ self->handleConnState();
+ return;
+ }
+- self->state = ConnState::connected;
+ BMCWEB_LOG_DEBUG << "Connected to: " << endpoint;
+-
+- self->checkQueue();
++ self->state = ConnState::connected;
++ self->handleConnState();
+ });
+ }
+
+ void sendMessage(const std::string& data)
+ {
+- if (state == ConnState::sendInProgress)
+- {
+- return;
+- }
+ state = ConnState::sendInProgress;
+
+ BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port;
+
+- req.version(static_cast<int>(11)); // HTTP 1.1
+- req.target(uri);
+- req.method(boost::beast::http::verb::post);
+-
+- // Set headers
+- for (const auto& [key, value] : headers)
+- {
+- req.set(key, value);
+- }
+- req.set(boost::beast::http::field::host, host);
+- req.keep_alive(true);
+-
+ req.body() = data;
+ req.prepare_payload();
+
+- // Set a timeout on the operation
+- conn.expires_after(std::chrono::seconds(30));
+-
+ // Send the HTTP request to the remote host
+ boost::beast::http::async_write(
+ conn, req,
+@@ -171,7 +153,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ BMCWEB_LOG_ERROR << "sendMessage() failed: "
+ << ec.message();
+ self->state = ConnState::sendFailed;
+- self->checkQueue();
++ self->handleConnState();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
+@@ -184,9 +166,18 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+
+ void recvMessage()
+ {
++ state = ConnState::recvInProgress;
++
++ parser.emplace(std::piecewise_construct, std::make_tuple());
++ parser->body_limit(httpReadBodyLimit);
++ // Since these are all push style eventing, we are not
++ // bothered about response body parsing.
++ // Check only for the response header
++ parser->skip(true);
++
+ // Receive the HTTP response
+ boost::beast::http::async_read(
+- conn, buffer, res,
++ conn, buffer, *parser,
+ [self(shared_from_this())](const boost::beast::error_code& ec,
+ const std::size_t& bytesTransferred) {
+ if (ec)
+@@ -194,30 +185,46 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ BMCWEB_LOG_ERROR << "recvMessage() failed: "
+ << ec.message();
+ self->state = ConnState::recvFailed;
+- self->checkQueue();
++ self->handleConnState();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
+ << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
+-
+- // Discard received data. We are not interested.
+- BMCWEB_LOG_DEBUG << "recvMessage() data: " << self->res;
++ BMCWEB_LOG_DEBUG << "recvMessage() data: "
++ << self->parser->get();
+
+ // Send is successful, Lets remove data from queue
+ // check for next request data in queue.
+- self->requestDataQueue.pop();
++ if (!self->requestDataQueue.empty())
++ {
++ self->requestDataQueue.pop_front();
++ }
+ self->state = ConnState::idle;
+- self->checkQueue();
++
++ // Keep the connection alive if server supports it
++ // Else close the connection
++ BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
++ << self->parser->keep_alive();
++ if (!self->parser->keep_alive())
++ {
++ // Abort the connection since server is not keep-alive
++ // enabled
++ self->state = ConnState::abortConnection;
++ }
++ // Transfer ownership of the response
++ self->parser->release();
++
++ self->handleConnState();
+ });
+ }
+
+ void doClose()
+ {
++ state = ConnState::closeInProgress;
+ boost::beast::error_code ec;
+ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
++ conn.close();
+
+- state = ConnState::closed;
+ // not_connected happens sometimes so don't bother reporting it.
+ if (ec && ec != boost::beast::errc::not_connected)
+ {
+@@ -225,112 +232,139 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "Connection closed gracefully";
+- }
+-
+- void checkQueue(const bool newRecord = false)
+- {
+- if (requestDataQueue.empty())
++ if ((state != ConnState::suspended) && (state != ConnState::terminated))
+ {
+- // TODO: Having issue in keeping connection alive. So lets close if
+- // nothing to be transferred.
+- doClose();
+-
+- BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n";
+- return;
++ state = ConnState::closed;
++ handleConnState();
+ }
++ }
+
++ void waitAndRetry()
++ {
+ if (retryCount >= maxRetryAttempts)
+ {
+- BMCWEB_LOG_ERROR << "Maximum number of retries is reached.";
++ BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
+
+ // Clear queue.
+ while (!requestDataQueue.empty())
+ {
+- requestDataQueue.pop();
++ requestDataQueue.pop_front();
+ }
+
+- BMCWEB_LOG_DEBUG << "Retry policy is set to " << retryPolicyAction;
++ BMCWEB_LOG_DEBUG << "Retry policy: " << retryPolicyAction;
+ if (retryPolicyAction == "TerminateAfterRetries")
+ {
+ // TODO: delete subscription
+ state = ConnState::terminated;
+- return;
+ }
+ if (retryPolicyAction == "SuspendRetries")
+ {
+ state = ConnState::suspended;
+- return;
+ }
+- // keep retrying, reset count and continue.
++ // Reset the retrycount to zero so that client can try connecting
++ // again if needed
+ retryCount = 0;
++ handleConnState();
++ return;
+ }
+
+- if ((state == ConnState::connectFailed) ||
+- (state == ConnState::sendFailed) ||
+- (state == ConnState::recvFailed))
++ if (runningTimer)
+ {
+- if (newRecord)
+- {
+- // We are already running async wait and retry.
+- // Since record is added to queue, it gets the
+- // turn in FIFO.
+- return;
+- }
+-
+- if (runningTimer)
+- {
+- BMCWEB_LOG_DEBUG << "Retry timer is already running.";
+- return;
+- }
+- runningTimer = true;
+-
+- retryCount++;
+-
+- BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs
+- << " seconds. RetryCount = " << retryCount;
+- timer.expires_after(std::chrono::seconds(retryIntervalSecs));
+- timer.async_wait(
+- [self = shared_from_this()](const boost::system::error_code&) {
+- self->runningTimer = false;
+- self->connStateCheck();
+- });
++ BMCWEB_LOG_DEBUG << "Retry timer is already running.";
+ return;
+ }
+- // reset retry count.
+- retryCount = 0;
+- connStateCheck();
++ runningTimer = true;
+
++ retryCount++;
++
++ BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs
++ << " seconds. RetryCount = " << retryCount;
++ timer.expires_after(std::chrono::seconds(retryIntervalSecs));
++ timer.async_wait(
++ [self = shared_from_this()](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message();
++ // Ignore the error and continue the retry loop to attempt
++ // sending the event as per the retry policy
++ }
++ self->runningTimer = false;
++
++ // Lets close connection and start from resolve.
++ self->doClose();
++ });
+ return;
+ }
+
+- void connStateCheck()
++ void handleConnState()
+ {
+ switch (state)
+ {
+ case ConnState::resolveInProgress:
+ case ConnState::connectInProgress:
+ case ConnState::sendInProgress:
+- case ConnState::suspended:
+- case ConnState::terminated:
+- // do nothing
++ case ConnState::recvInProgress:
++ case ConnState::closeInProgress:
++ {
++ BMCWEB_LOG_DEBUG << "Async operation is already in progress";
+ break;
++ }
+ case ConnState::initialized:
+ case ConnState::closed:
++ {
++ if (requestDataQueue.empty())
++ {
++ BMCWEB_LOG_DEBUG << "requestDataQueue is empty";
++ return;
++ }
++ doResolve();
++ break;
++ }
++ case ConnState::resolved:
++ {
++ doConnect();
++ break;
++ }
++ case ConnState::suspended:
++ case ConnState::terminated:
++ {
++ doClose();
++ break;
++ }
++ case ConnState::resolveFailed:
+ case ConnState::connectFailed:
+ case ConnState::sendFailed:
+ case ConnState::recvFailed:
+- case ConnState::resolveFailed:
++ case ConnState::retry:
+ {
+- doResolve();
++ // In case of failures during connect and handshake
++ // the retry policy will be applied
++ waitAndRetry();
+ break;
+ }
+ case ConnState::connected:
+ case ConnState::idle:
+ {
++ // State idle means, previous attempt is successful
++ // State connected means, client connection is established
++ // successfully
++ if (requestDataQueue.empty())
++ {
++ BMCWEB_LOG_DEBUG << "requestDataQueue is empty";
++ return;
++ }
+ std::string data = requestDataQueue.front();
+ sendMessage(data);
+ break;
+ }
++ case ConnState::abortConnection:
++ {
++ // Server did not want to keep alive the session
++ doClose();
++ break;
++ }
++ default:
++ break;
+ }
+ }
+
+@@ -339,37 +373,38 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ const std::string& destIP, const std::string& destPort,
+ const std::string& destUri) :
+ conn(ioc),
+- timer(ioc), subId(id), host(destIP), port(destPort), uri(destUri),
+- retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0),
++ timer(ioc), req(boost::beast::http::verb::post, destUri, 11),
++ state(ConnState::initialized), subId(id), host(destIP), port(destPort),
++ uri(destUri), retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0),
+ retryPolicyAction("TerminateAfterRetries"), runningTimer(false)
+ {
+- state = ConnState::initialized;
++ // Set the request header
++ req.set(boost::beast::http::field::host, host);
++ req.set(boost::beast::http::field::content_type, "application/json");
++ req.keep_alive(true);
++
++ requestDataQueue.set_capacity(maxRequestQueueSize);
+ }
+
+ void sendData(const std::string& data)
+ {
+- if (state == ConnState::suspended)
++ if ((state == ConnState::suspended) || (state == ConnState::terminated))
+ {
+ return;
+ }
+-
+- if (requestDataQueue.size() <= maxRequestQueueSize)
+- {
+- requestDataQueue.push(data);
+- checkQueue(true);
+- }
+- else
+- {
+- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data.";
+- }
+-
++ requestDataQueue.push_back(data);
++ handleConnState();
+ return;
+ }
+
+- void setHeaders(
++ void addHeaders(
+ const std::vector<std::pair<std::string, std::string>>& httpHeaders)
+ {
+- headers = httpHeaders;
++ // Set custom headers
++ for (const auto& [key, value] : httpHeaders)
++ {
++ req.set(key, value);
++ }
+ }
+
+ void setRetryConfig(const uint32_t retryAttempts,
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 148c703..bffa68f 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -412,7 +412,7 @@ class Subscription
+ reqHeaders.emplace_back(std::pair(key, val));
+ }
+ }
+- conn->setHeaders(reqHeaders);
++ conn->addHeaders(reqHeaders);
+ conn->sendData(msg);
+ this->eventSeqNum++;
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch
new file mode 100644
index 000000000..b1f61c6fd
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch
@@ -0,0 +1,401 @@
+From 579fda953ec991b4e7f9d7194b08f6aa103fa0ec Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Mon, 22 Feb 2021 17:07:47 +0000
+Subject: [PATCH 2/2] EventService: https client support
+
+Add https client support for push style
+eventing. Using this BMC can push the event
+logs/telemetry data to event listener over
+secure http channel.
+
+Tested:
+ - Created subscription with https destination
+ url. Using SubmitTestEvent action set the
+ event and can see event on event listener.
+ - Validator passed.
+
+Change-Id: I44c3918b39baa2eb5fddda9d635f99aa280a422a
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+---
+ http/http_client.hpp | 255 ++++++++++++------
+ .../include/event_service_manager.hpp | 2 +-
+ 2 files changed, 175 insertions(+), 82 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index d116f6d..cebc857 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -20,6 +20,7 @@
+ #include <boost/beast/core/flat_buffer.hpp>
+ #include <boost/beast/core/tcp_stream.hpp>
+ #include <boost/beast/http/message.hpp>
++#include <boost/beast/ssl/ssl_stream.hpp>
+ #include <boost/beast/version.hpp>
+ #include <include/async_resolve.hpp>
+
+@@ -44,6 +45,8 @@ enum class ConnState
+ resolved,
+ connectInProgress,
+ connectFailed,
++ handshakeInProgress,
++ handshakeFailed,
+ connected,
+ sendInProgress,
+ sendFailed,
+@@ -62,7 +65,9 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ private:
+ crow::async_resolve::Resolver resolver;
++ boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv12_client};
+ boost::beast::tcp_stream conn;
++ std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream&>> sslConn;
+ boost::asio::steady_timer timer;
+ boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
+ boost::beast::http::request<boost::beast::http::string_body> req;
+@@ -112,23 +117,52 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ void doConnect()
+ {
+ state = ConnState::connectInProgress;
++ sslConn.emplace(conn, ctx);
+
+ BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port;
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const boost::asio::ip::tcp::endpoint& endpoint) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Connect " << endpoint
++ << " failed: " << ec.message();
++ self->state = ConnState::connectFailed;
++ self->handleConnState();
++ return;
++ }
+
++ BMCWEB_LOG_DEBUG << "Connected to: " << endpoint;
++ if (self->sslConn)
++ {
++ self->performHandshake();
++ }
++ else
++ {
++ self->handleConnState();
++ }
++ };
+ conn.expires_after(std::chrono::seconds(30));
+- conn.async_connect(
+- endPoints, [self(shared_from_this())](
+- const boost::beast::error_code ec,
+- const boost::asio::ip::tcp::endpoint& endpoint) {
++ conn.async_connect(endPoints, std::move(respHandler));
++ }
++
++ void performHandshake()
++ {
++ state = ConnState::handshakeInProgress;
++
++ sslConn->async_handshake(
++ boost::asio::ssl::stream_base::client,
++ [self(shared_from_this())](const boost::beast::error_code ec) {
+ if (ec)
+ {
+- BMCWEB_LOG_ERROR << "Connect " << endpoint
+- << " failed: " << ec.message();
+- self->state = ConnState::connectFailed;
++ BMCWEB_LOG_ERROR << "SSL handshake failed: "
++ << ec.message();
++ self->state = ConnState::handshakeFailed;
+ self->handleConnState();
+ return;
+ }
+- BMCWEB_LOG_DEBUG << "Connected to: " << endpoint;
++
++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull";
+ self->state = ConnState::connected;
+ self->handleConnState();
+ });
+@@ -136,106 +170,159 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+
+ void sendMessage(const std::string& data)
+ {
+- state = ConnState::sendInProgress;
+-
+ BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port;
++ state = ConnState::sendInProgress;
+
+ req.body() = data;
+ req.prepare_payload();
+
+- // Send the HTTP request to the remote host
+- boost::beast::http::async_write(
+- conn, req,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "sendMessage() failed: "
+- << ec.message();
+- self->state = ConnState::sendFailed;
+- self->handleConnState();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
+- << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
++ self->state = ConnState::sendFailed;
++ self->handleConnState();
++ return;
++ }
+
+- self->recvMessage();
+- });
+- }
++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
++ self->recvMessage();
++ };
+
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ boost::beast::http::async_write(*sslConn, req,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_write(conn, req, std::move(respHandler));
++ }
++ }
+ void recvMessage()
+ {
+ state = ConnState::recvInProgress;
+
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ if (ec && ec != boost::asio::ssl::error::stream_truncated)
++ {
++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
++
++ self->state = ConnState::recvFailed;
++ self->handleConnState();
++ return;
++ }
++
++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
++ // Send is successful, Lets remove data from queue
++ // check for next request data in queue.
++ if (!self->requestDataQueue.empty())
++ {
++ self->requestDataQueue.pop_front();
++ }
++ self->state = ConnState::idle;
++ // Keep the connection alive if server supports it
++ // Else close the connection
++ BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
++ << self->parser->keep_alive();
++ if (!self->parser->keep_alive())
++ {
++ // Abort the connection since server is not keep-alive enabled
++ self->state = ConnState::abortConnection;
++ }
++ // Transfer ownership of the response
++ self->parser->release();
++
++ self->handleConnState();
++ };
+ parser.emplace(std::piecewise_construct, std::make_tuple());
+ parser->body_limit(httpReadBodyLimit);
+ // Since these are all push style eventing, we are not
+ // bothered about response body parsing.
+ // Check only for the response header
+ parser->skip(true);
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ boost::beast::http::async_read(*sslConn, buffer, *parser,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_read(conn, buffer, *parser,
++ std::move(respHandler));
++ }
++ }
++ void doClose()
++ {
++ state = ConnState::closeInProgress;
+
+- // Receive the HTTP response
+- boost::beast::http::async_read(
+- conn, buffer, *parser,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
++ // Set the timeout on the tcp stream socket for the async operation
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ sslConn->async_shutdown([self = shared_from_this()](
++ const boost::system::error_code ec) {
+ if (ec)
+ {
+- BMCWEB_LOG_ERROR << "recvMessage() failed: "
+- << ec.message();
+- self->state = ConnState::recvFailed;
+- self->handleConnState();
+- return;
++ // Many https server closes connection abruptly
++ // i.e witnout close_notify. More details are at
++ // https://github.com/boostorg/beast/issues/824
++ if (ec == boost::asio::ssl::error::stream_truncated)
++ {
++ BMCWEB_LOG_INFO << "doClose(): Connection "
++ "closed by server. ";
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "doClose() failed: "
++ << ec.message();
++ }
+ }
+- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
+- << bytesTransferred;
+- BMCWEB_LOG_DEBUG << "recvMessage() data: "
+- << self->parser->get();
+-
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- if (!self->requestDataQueue.empty())
++ else
+ {
+- self->requestDataQueue.pop_front();
++ BMCWEB_LOG_DEBUG << "Connection closed gracefully...";
+ }
+- self->state = ConnState::idle;
++ self->conn.close();
+
+- // Keep the connection alive if server supports it
+- // Else close the connection
+- BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
+- << self->parser->keep_alive();
+- if (!self->parser->keep_alive())
++ if ((self->state != ConnState::suspended) &&
++ (self->state != ConnState::terminated))
+ {
+- // Abort the connection since server is not keep-alive
+- // enabled
+- self->state = ConnState::abortConnection;
++ self->state = ConnState::closed;
++ self->handleConnState();
+ }
+- // Transfer ownership of the response
+- self->parser->release();
+-
+- self->handleConnState();
+ });
+- }
+-
+- void doClose()
+- {
+- state = ConnState::closeInProgress;
+- boost::beast::error_code ec;
+- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+- conn.close();
+-
+- // not_connected happens sometimes so don't bother reporting it.
+- if (ec && ec != boost::beast::errc::not_connected)
+- {
+- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message();
+- return;
+ }
+- BMCWEB_LOG_DEBUG << "Connection closed gracefully";
+- if ((state != ConnState::suspended) && (state != ConnState::terminated))
++ else
+ {
+- state = ConnState::closed;
+- handleConnState();
++ boost::beast::error_code ec;
++ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both,
++ ec);
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "doClose() failed: " << ec.message();
++ }
++ else
++ {
++ BMCWEB_LOG_DEBUG << "Connection closed gracefully...";
++ }
++ conn.close();
++
++ if ((state != ConnState::suspended) &&
++ (state != ConnState::terminated))
++ {
++ state = ConnState::closed;
++ handleConnState();
++ }
+ }
+ }
+
+@@ -302,6 +389,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ case ConnState::resolveInProgress:
+ case ConnState::connectInProgress:
++ case ConnState::handshakeInProgress:
+ case ConnState::sendInProgress:
+ case ConnState::recvInProgress:
+ case ConnState::closeInProgress:
+@@ -333,6 +421,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ case ConnState::resolveFailed:
+ case ConnState::connectFailed:
++ case ConnState::handshakeFailed:
+ case ConnState::sendFailed:
+ case ConnState::recvFailed:
+ case ConnState::retry:
+@@ -371,7 +460,8 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ public:
+ explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const std::string& destPort,
+- const std::string& destUri) :
++ const std::string& destUri,
++ const std::string& uriProto) :
+ conn(ioc),
+ timer(ioc), req(boost::beast::http::verb::post, destUri, 11),
+ state(ConnState::initialized), subId(id), host(destIP), port(destPort),
+@@ -384,8 +474,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ req.keep_alive(true);
+
+ requestDataQueue.set_capacity(maxRequestQueueSize);
++ if (uriProto == "https")
++ {
++ sslConn.emplace(conn, ctx);
++ }
+ }
+-
+ void sendData(const std::string& data)
+ {
+ if ((state == ConnState::suspended) || (state == ConnState::terminated))
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index bffa68f..1e6f496 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -387,7 +387,7 @@ class Subscription
+ {
+ conn = std::make_shared<crow::HttpClient>(
+ crow::connections::systemBus->get_io_context(), id, host, port,
+- path);
++ path, uriProto);
+ }
+
+ Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch
new file mode 100644
index 000000000..0d31fbc72
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch
@@ -0,0 +1,477 @@
+From e93a6a02b0fba3371144d474422fadacc3f25fde Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Fri, 12 Mar 2021 18:53:25 +0000
+Subject: [PATCH 4/6] Add Server-Sent-Events support
+
+Server-Sent Events is a standard describing how servers can
+initiate data transmission towards clients once an initial
+client connection has been established. Unlike websockets
+(which are bidirectional), Server-Sent Events are
+unidirectional and commonly used to send message updates or
+continuous data streams to a browser client.
+
+This is base patch for adding Server-Sent events support to
+bmcweb. Redfish eventservice SSE style subscription uses
+this and will be loaded on top of this commit.
+
+Tested:
+ - Tested using follow-up patch on top which adds
+ support for Redfish EventService SSE style subscription
+ and observed events are getting sent periodically.
+
+Change-Id: I36956565cbba30c2007852c9471f477f6d1736e9
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+---
+ http/http_connection.hpp | 14 +-
+ http/http_response.hpp | 7 +-
+ http/routing.hpp | 70 ++++++++++
+ http/server_sent_event.hpp | 279 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 364 insertions(+), 6 deletions(-)
+ create mode 100644 http/server_sent_event.hpp
+
+diff --git a/http/http_connection.hpp b/http/http_connection.hpp
+index 4482f8d..2c8bf40 100644
+--- a/http/http_connection.hpp
++++ b/http/http_connection.hpp
+@@ -326,7 +326,7 @@ class Connection :
+ BMCWEB_LOG_INFO << "Request: "
+ << " " << this << " HTTP/" << req->version() / 10 << "."
+ << req->version() % 10 << ' ' << req->methodString()
+- << " " << req->target() << " " << req->ipAddress;
++ << " " << req->url << " " << req->ipAddress;
+
+ needToCallAfterHandlers = false;
+
+@@ -345,11 +345,15 @@ class Connection :
+ boost::asio::post(self->adaptor.get_executor(),
+ [self] { self->completeRequest(); });
+ };
+- if (req->isUpgrade() &&
+- boost::iequals(
+- req->getHeaderValue(boost::beast::http::field::upgrade),
+- "websocket"))
++
++ if ((req->isUpgrade() &&
++ boost::iequals(req->getHeaderValue(
++ boost::beast::http::field::upgrade),
++ "websocket")) ||
++ (req->url == "/sse"))
+ {
++ BMCWEB_LOG_DEBUG << "Request: " << this
++ << " is getting upgraded";
+ handler->handleUpgrade(*req, res, std::move(adaptor));
+ // delete lambda with self shared_ptr
+ // to enable connection destruction
+diff --git a/http/http_response.hpp b/http/http_response.hpp
+index cd00ec8..ffd6dda 100644
+--- a/http/http_response.hpp
++++ b/http/http_response.hpp
+@@ -13,10 +13,15 @@ namespace crow
+ template <typename Adaptor, typename Handler>
+ class Connection;
+
++template <typename Adaptor>
++class SseConnectionImpl;
++
+ struct Response
+ {
+ template <typename Adaptor, typename Handler>
+ friend class crow::Connection;
++ template <typename Adaptor>
++ friend class crow::SseConnectionImpl;
+ using response_type =
+ boost::beast::http::response<boost::beast::http::string_body>;
+
+@@ -136,8 +141,8 @@ struct Response
+
+ private:
+ bool completed{};
+- std::function<void()> completeRequestHandler;
+ std::function<bool()> isAliveHelper;
++ std::function<void()> completeRequestHandler;
+
+ // In case of a JSON object, set the Content-Type header
+ void jsonMode()
+diff --git a/http/routing.hpp b/http/routing.hpp
+index 65c7b70..0824939 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -6,6 +6,7 @@
+ #include "http_response.hpp"
+ #include "logging.hpp"
+ #include "privileges.hpp"
++#include "server_sent_event.hpp"
+ #include "sessions.hpp"
+ #include "utility.hpp"
+ #include "websocket.hpp"
+@@ -390,6 +391,67 @@ class WebSocketRule : public BaseRule
+ std::function<void(crow::websocket::Connection&)> errorHandler;
+ };
+
++class SseSocketRule : public BaseRule
++{
++ using self_t = SseSocketRule;
++
++ public:
++ SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
++ {}
++
++ void validate() override
++ {}
++
++ void handle(const Request&, Response& res, const RoutingParams&) override
++ {
++ res.result(boost::beast::http::status::not_found);
++ res.end();
++ }
++
++ void handleUpgrade(const Request& req, Response&,
++ boost::asio::ip::tcp::socket&& adaptor) override
++ {
++ std::shared_ptr<crow::SseConnectionImpl<boost::asio::ip::tcp::socket>>
++ myConnection = std::make_shared<
++ crow::SseConnectionImpl<boost::asio::ip::tcp::socket>>(
++ req, std::move(adaptor), openHandler, closeHandler);
++ myConnection->start();
++ }
++#ifdef BMCWEB_ENABLE_SSL
++ void handleUpgrade(const Request& req, Response&,
++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
++ adaptor) override
++ {
++ std::shared_ptr<crow::SseConnectionImpl<
++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
++ myConnection = std::make_shared<crow::SseConnectionImpl<
++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
++ req, std::move(adaptor), openHandler, closeHandler);
++ myConnection->start();
++ }
++#endif
++
++ template <typename Func>
++ self_t& onopen(Func f)
++ {
++ openHandler = f;
++ return *this;
++ }
++
++ template <typename Func>
++ self_t& onclose(Func f)
++ {
++ closeHandler = f;
++ return *this;
++ }
++
++ private:
++ std::function<void(std::shared_ptr<crow::SseConnection>&,
++ const crow::Request&, crow::Response&)>
++ openHandler;
++ std::function<void(std::shared_ptr<crow::SseConnection>&)> closeHandler;
++};
++
+ template <typename T>
+ struct RuleParameterTraits
+ {
+@@ -402,6 +464,14 @@ struct RuleParameterTraits
+ return *p;
+ }
+
++ SseSocketRule& serverSentEvent()
++ {
++ self_t* self = static_cast<self_t*>(this);
++ SseSocketRule* p = new SseSocketRule(self->rule);
++ self->ruleToUpgrade.reset(p);
++ return *p;
++ }
++
+ self_t& name(const std::string_view name) noexcept
+ {
+ self_t* self = static_cast<self_t*>(this);
+diff --git a/http/server_sent_event.hpp b/http/server_sent_event.hpp
+new file mode 100644
+index 0000000..41d18ed
+--- /dev/null
++++ b/http/server_sent_event.hpp
+@@ -0,0 +1,279 @@
++#pragma once
++#include "http_request.hpp"
++
++#include <boost/algorithm/string/predicate.hpp>
++#include <boost/asio/buffer.hpp>
++#include <boost/beast/http/buffer_body.hpp>
++#include <boost/beast/websocket.hpp>
++
++#include <array>
++#include <functional>
++
++#ifdef BMCWEB_ENABLE_SSL
++#include <boost/beast/websocket/ssl.hpp>
++#endif
++
++namespace crow
++{
++
++struct SseConnection : std::enable_shared_from_this<SseConnection>
++{
++ public:
++ SseConnection(const crow::Request& reqIn) : req(reqIn)
++ {}
++ virtual ~SseConnection() = default;
++
++ virtual boost::asio::io_context& getIoContext() = 0;
++ virtual void sendSSEHeader() = 0;
++ virtual void completeRequest() = 0;
++ virtual void close(const std::string_view msg = "quit") = 0;
++ virtual void sendEvent(const std::string_view id,
++ const std::string_view msg) = 0;
++
++ crow::Request req;
++ crow::Response res;
++};
++
++template <typename Adaptor>
++class SseConnectionImpl : public SseConnection
++{
++ public:
++ SseConnectionImpl(
++ const crow::Request& reqIn, Adaptor adaptorIn,
++ std::function<void(std::shared_ptr<SseConnection>&,
++ const crow::Request&, crow::Response&)>
++ openHandler,
++ std::function<void(std::shared_ptr<SseConnection>&)> closeHandler) :
++ SseConnection(reqIn),
++ adaptor(std::move(adaptorIn)), openHandler(std::move(openHandler)),
++ closeHandler(std::move(closeHandler))
++ {
++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE constructor " << this;
++ }
++
++ ~SseConnectionImpl() override
++ {
++ res.completeRequestHandler = nullptr;
++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE destructor " << this;
++ }
++
++ boost::asio::io_context& getIoContext() override
++ {
++ return static_cast<boost::asio::io_context&>(
++ adaptor.get_executor().context());
++ }
++
++ void start()
++ {
++ // Register for completion callback.
++ res.completeRequestHandler = [this, self(shared_from_this())] {
++ boost::asio::post(this->adaptor.get_executor(),
++ [self] { self->completeRequest(); });
++ };
++
++ if (openHandler)
++ {
++ std::shared_ptr<SseConnection> self = this->shared_from_this();
++ openHandler(self, req, res);
++ }
++ }
++
++ void close(const std::string_view msg) override
++ {
++ BMCWEB_LOG_DEBUG << "Closing SSE connection " << this << " - " << msg;
++ boost::beast::get_lowest_layer(adaptor).close();
++
++ // send notification to handler for cleanup
++ if (closeHandler)
++ {
++ std::shared_ptr<SseConnection> self = this->shared_from_this();
++ closeHandler(self);
++ }
++ }
++
++ void sendSSEHeader() override
++ {
++ BMCWEB_LOG_DEBUG << "Starting SSE connection";
++ using BodyType = boost::beast::http::buffer_body;
++ auto response =
++ std::make_shared<boost::beast::http::response<BodyType>>(
++ boost::beast::http::status::ok, 11);
++ auto serializer =
++ std::make_shared<boost::beast::http::response_serializer<BodyType>>(
++ *response);
++
++ response->set(boost::beast::http::field::server, "bmcweb");
++ response->set(boost::beast::http::field::content_type,
++ "text/event-stream");
++ response->body().data = nullptr;
++ response->body().size = 0;
++ response->body().more = true;
++
++ boost::beast::http::async_write_header(
++ adaptor, *serializer,
++ [this, self(shared_from_this()), response, serializer](
++ const boost::beast::error_code& ec, const std::size_t&) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Error sending header" << ec;
++ close("async_write_header failed");
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "SSE header sent - Connection established";
++
++ // SSE stream header sent, So lets setup monitor.
++ // Any read data on this stream will be error in case of SSE.
++ setupRead();
++ });
++ }
++
++ void setupRead()
++ {
++ adaptor.async_read_some(
++ outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
++ [this](const boost::system::error_code& ec, std::size_t bytesRead) {
++ BMCWEB_LOG_DEBUG << "async_read_some: Read " << bytesRead
++ << " bytes";
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Read error: " << ec;
++ }
++ outputBuffer.commit(bytesRead);
++ outputBuffer.consume(bytesRead);
++
++ // After establishing SSE stream, Reading data on this
++ // stream means client is disobeys the SSE protocol.
++ // Read the data to avoid buffer attacks and close connection.
++ close("Close SSE connection");
++ return;
++ });
++ }
++
++ void doWrite()
++ {
++ if (doingWrite)
++ {
++ return;
++ }
++ if (inputBuffer.size() == 0)
++ {
++ BMCWEB_LOG_DEBUG << "inputBuffer is empty... Bailing out";
++ return;
++ }
++ doingWrite = true;
++
++ adaptor.async_write_some(
++ inputBuffer.data(), [this, self(shared_from_this())](
++ boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ doingWrite = false;
++ inputBuffer.consume(bytesTransferred);
++
++ if (ec == boost::asio::error::eof)
++ {
++ BMCWEB_LOG_ERROR << "async_write_some() SSE stream closed";
++ close("SSE stream closed");
++ return;
++ }
++
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "async_write_some() failed: "
++ << ec.message();
++ close("async_write_some failed");
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: "
++ << bytesTransferred;
++
++ doWrite();
++ });
++ }
++
++ void completeRequest() override
++ {
++ BMCWEB_LOG_DEBUG << "SSE completeRequest() handler";
++ if (res.body().empty() && !res.jsonValue.empty())
++ {
++ res.addHeader("Content-Type", "application/json");
++ res.body() = res.jsonValue.dump(
++ 2, ' ', true, nlohmann::json::error_handler_t::replace);
++ }
++
++ res.preparePayload();
++ auto serializer =
++ std::make_shared<boost::beast::http::response_serializer<
++ boost::beast::http::string_body>>(*res.stringResponse);
++
++ boost::beast::http::async_write(
++ adaptor, *serializer,
++ [this, self(shared_from_this()),
++ serializer](const boost::system::error_code& ec,
++ std::size_t bytesTransferred) {
++ BMCWEB_LOG_DEBUG << this << " async_write " << bytesTransferred
++ << " bytes";
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG << this << " from async_write failed";
++ return;
++ }
++ res.clear();
++
++ BMCWEB_LOG_DEBUG << this
++ << " Closing SSE connection - Request invalid";
++ close("Request invalid");
++ });
++
++ // delete lambda with self shared_ptr
++ // to enable connection destruction
++ res.completeRequestHandler = nullptr;
++ }
++
++ void sendEvent(const std::string_view id,
++ const std::string_view msg) override
++ {
++ if (msg.empty())
++ {
++ BMCWEB_LOG_DEBUG << "Empty data, bailing out.";
++ return;
++ }
++
++ std::string rawData;
++ if (!id.empty())
++ {
++ rawData += "id: ";
++ rawData.append(id.begin(), id.end());
++ rawData += "\n";
++ }
++
++ rawData += "data: ";
++ for (char character : msg)
++ {
++ rawData += character;
++ if (character == '\n')
++ {
++ rawData += "data: ";
++ }
++ }
++ rawData += "\n\n";
++
++ boost::asio::buffer_copy(inputBuffer.prepare(rawData.size()),
++ boost::asio::buffer(rawData));
++ inputBuffer.commit(rawData.size());
++
++ doWrite();
++ }
++
++ private:
++ Adaptor adaptor;
++
++ boost::beast::flat_static_buffer<1024U * 8U> outputBuffer;
++ boost::beast::flat_static_buffer<1024U * 64U> inputBuffer;
++ bool doingWrite = false;
++
++ std::function<void(std::shared_ptr<SseConnection>&, const crow::Request&,
++ crow::Response&)>
++ openHandler;
++ std::function<void(std::shared_ptr<SseConnection>&)> closeHandler;
++};
++} // namespace crow
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch
new file mode 100644
index 000000000..02d28833f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch
@@ -0,0 +1,627 @@
+From 698d2615c5bc30ab7f89f11ed5659df8bf248ea0 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Tue, 16 Mar 2021 15:37:24 +0000
+Subject: [PATCH 5/6] Add SSE style subscription support to eventservice
+
+This commit adds the SSE style eventservice subscription
+style event. Using this, end user can subscribe for
+Redfish event logs using GET on SSE usri from
+browser.
+URI: /redfish/v1/EventService/Subscriptions/SSE
+
+Tested:
+ - From Browser did GET on above SSE URI and
+ generated some Redfish event logs(power cycle)
+ and saw redfish event logs streaming on browser.
+ - After SSE registration, Check Subscription collections
+ and GET on individual subscription and saw desired
+ response.
+ - Ran RedfishValidation and its passed.
+
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Change-Id: I7f4b7a34974080739c4ba968ed570489af0474de
+---
+ http/http_connection.hpp | 2 +-
+ include/eventservice_sse.hpp | 75 +++++
+ .../include/event_service_manager.hpp | 77 ++++-
+ redfish-core/include/server_sent_events.hpp | 291 ------------------
+ redfish-core/lib/event_service.hpp | 4 +-
+ src/webserver_main.cpp | 2 +
+ 6 files changed, 149 insertions(+), 302 deletions(-)
+ create mode 100644 include/eventservice_sse.hpp
+ delete mode 100644 redfish-core/include/server_sent_events.hpp
+
+diff --git a/http/http_connection.hpp b/http/http_connection.hpp
+index 2c8bf40..1ab776c 100644
+--- a/http/http_connection.hpp
++++ b/http/http_connection.hpp
+@@ -350,7 +350,7 @@ class Connection :
+ boost::iequals(req->getHeaderValue(
+ boost::beast::http::field::upgrade),
+ "websocket")) ||
+- (req->url == "/sse"))
++ (req->url == "/redfish/v1/EventService/Subscriptions/SSE"))
+ {
+ BMCWEB_LOG_DEBUG << "Request: " << this
+ << " is getting upgraded";
+diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp
+new file mode 100644
+index 0000000..6c98e6e
+--- /dev/null
++++ b/include/eventservice_sse.hpp
+@@ -0,0 +1,75 @@
++#pragma once
++
++#include <app.hpp>
++#include <event_service_manager.hpp>
++
++namespace redfish
++{
++namespace eventservice_sse
++{
++
++static bool createSubscription(std::shared_ptr<crow::SseConnection>& conn,
++ const crow::Request& req, crow::Response& res)
++{
++ if ((EventServiceManager::getInstance().getNumberOfSubscriptions() >=
++ maxNoOfSubscriptions) ||
++ EventServiceManager::getInstance().getNumberOfSSESubscriptions() >=
++ maxNoOfSSESubscriptions)
++ {
++ BMCWEB_LOG_ERROR << "Max SSE subscriptions reached";
++ messages::eventSubscriptionLimitExceeded(res);
++ res.end();
++ return false;
++ }
++ BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size();
++
++ std::shared_ptr<redfish::Subscription> subValue =
++ std::make_shared<redfish::Subscription>(std::move(conn));
++
++ // GET on this URI means, Its SSE subscriptionType.
++ subValue->subscriptionType = redfish::subscriptionTypeSSE;
++
++ // TODO: parse $filter query params and fill config.
++ subValue->protocol = "Redfish";
++ subValue->retryPolicy = "TerminateAfterRetries";
++ subValue->eventFormatType = "Event";
++
++ std::string id =
++ redfish::EventServiceManager::getInstance().addSubscription(subValue,
++ false);
++ if (id.empty())
++ {
++ messages::internalError(res);
++ res.end();
++ return false;
++ }
++
++ return true;
++}
++
++static void deleteSubscription(std::shared_ptr<crow::SseConnection>& conn)
++{
++ redfish::EventServiceManager::getInstance().deleteSubscription(conn);
++}
++
++inline void requestRoutes(App& app)
++{
++ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/SSE")
++ .privileges({"ConfigureComponents", "ConfigureManager"})
++ .serverSentEvent()
++ .onopen([](std::shared_ptr<crow::SseConnection>& conn,
++ const crow::Request& req, crow::Response& res) {
++ BMCWEB_LOG_DEBUG << "Connection " << conn << " opened.";
++ if (createSubscription(conn, req, res))
++ {
++ // All success, lets send SSE haader
++ conn->sendSSEHeader();
++ }
++ })
++ .onclose([](std::shared_ptr<crow::SseConnection>& conn) {
++ BMCWEB_LOG_DEBUG << "Connection " << conn << " closed";
++ deleteSubscription(conn);
++ });
++}
++} // namespace eventservice_sse
++} // namespace redfish
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 5821e2e..f4d57c2 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -23,13 +23,15 @@
+ #include <sys/inotify.h>
+
+ #include <boost/asio/io_context.hpp>
++#include <boost/beast/core/span.hpp>
+ #include <boost/container/flat_map.hpp>
+ #include <error_messages.hpp>
+ #include <http_client.hpp>
+ #include <random.hpp>
+-#include <server_sent_events.hpp>
++#include <server_sent_event.hpp>
+ #include <utils/json_utils.hpp>
+
++#include <algorithm>
+ #include <cstdlib>
+ #include <ctime>
+ #include <fstream>
+@@ -46,9 +48,13 @@ using EventServiceConfig = std::tuple<bool, uint32_t, uint32_t>;
+ static constexpr const char* eventFormatType = "Event";
+ static constexpr const char* metricReportFormatType = "MetricReport";
+
++static constexpr const char* subscriptionTypeSSE = "SSE";
+ static constexpr const char* eventServiceFile =
+ "/var/lib/bmcweb/eventservice_config.json";
+
++static constexpr const uint8_t maxNoOfSubscriptions = 20;
++static constexpr const uint8_t maxNoOfSSESubscriptions = 10;
++
+ #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
+ static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
+ static constexpr const char* redfishEventLogDir = "/var/log";
+@@ -391,11 +397,9 @@ class Subscription
+ path, uriProto);
+ }
+
+- Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+- eventSeqNum(1)
+- {
+- sseConn = std::make_shared<crow::ServerSentEvents>(adaptor);
+- }
++ Subscription(const std::shared_ptr<crow::SseConnection>& adaptor) :
++ sseConn(adaptor), eventSeqNum(1)
++ {}
+
+ ~Subscription() = default;
+
+@@ -420,7 +424,7 @@ class Subscription
+
+ if (sseConn != nullptr)
+ {
+- sseConn->sendData(eventSeqNum, msg);
++ sseConn->sendEvent(std::to_string(eventSeqNum), msg);
+ }
+ }
+
+@@ -510,6 +514,7 @@ class Subscription
+
+ this->sendEvent(
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace));
++ this->eventSeqNum++;
+ }
+ #endif
+
+@@ -565,14 +570,39 @@ class Subscription
+ return eventSeqNum;
+ }
+
++ void setSubscriptionId(const std::string& id)
++ {
++ BMCWEB_LOG_DEBUG << "Subscription ID: " << id;
++ subId = id;
++ }
++
++ std::string getSubscriptionId()
++ {
++ return subId;
++ }
++
++ std::optional<std::string>
++ getSubscriptionId(const std::shared_ptr<crow::SseConnection>& connPtr)
++ {
++ if (sseConn != nullptr && connPtr == sseConn)
++ {
++ BMCWEB_LOG_DEBUG << __FUNCTION__
++ << " conn matched, subId: " << subId;
++ return subId;
++ }
++
++ return std::nullopt;
++ }
++
+ private:
++ std::shared_ptr<crow::SseConnection> sseConn = nullptr;
+ uint64_t eventSeqNum;
+ std::string host;
+ std::string port;
+ std::string path;
+ std::string uriProto;
+ std::shared_ptr<crow::HttpClient> conn = nullptr;
+- std::shared_ptr<crow::ServerSentEvents> sseConn = nullptr;
++ std::string subId;
+ };
+
+ static constexpr const bool defaultEnabledState = true;
+@@ -963,6 +993,8 @@ class EventServiceManager
+ subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
+ subValue->updateRetryPolicy();
+
++ // Set Subscription ID for back trace
++ subValue->setSubscriptionId(id);
+ return id;
+ }
+
+@@ -987,11 +1019,40 @@ class EventServiceManager
+ }
+ }
+
++ void deleteSubscription(const std::shared_ptr<crow::SseConnection>& connPtr)
++ {
++ for (const auto& it : this->subscriptionsMap)
++ {
++ std::shared_ptr<Subscription> entry = it.second;
++ if (entry->subscriptionType == subscriptionTypeSSE)
++ {
++ std::optional<std::string> id =
++ entry->getSubscriptionId(connPtr);
++ if (id)
++ {
++ deleteSubscription(*id);
++ return;
++ }
++ }
++ }
++ }
++
+ size_t getNumberOfSubscriptions()
+ {
+ return subscriptionsMap.size();
+ }
+
++ size_t getNumberOfSSESubscriptions() const
++ {
++ auto count = std::count_if(
++ subscriptionsMap.begin(), subscriptionsMap.end(),
++ [this](const std::pair<std::string, std::shared_ptr<Subscription>>&
++ entry) {
++ return (entry.second->subscriptionType == subscriptionTypeSSE);
++ });
++ return static_cast<size_t>(count);
++ }
++
+ std::vector<std::string> getAllIDs()
+ {
+ std::vector<std::string> idList;
+diff --git a/redfish-core/include/server_sent_events.hpp b/redfish-core/include/server_sent_events.hpp
+deleted file mode 100644
+index 578fa19..0000000
+--- a/redfish-core/include/server_sent_events.hpp
++++ /dev/null
+@@ -1,291 +0,0 @@
+-
+-/*
+-// Copyright (c) 2020 Intel Corporation
+-//
+-// Licensed under the Apache License, Version 2.0 (the "License");
+-// you may not use this file except in compliance with the License.
+-// You may obtain a copy of the License at
+-//
+-// http://www.apache.org/licenses/LICENSE-2.0
+-//
+-// Unless required by applicable law or agreed to in writing, software
+-// distributed under the License is distributed on an "AS IS" BASIS,
+-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-// See the License for the specific language governing permissions and
+-// limitations under the License.
+-*/
+-#pragma once
+-#include "node.hpp"
+-
+-#include <boost/asio/strand.hpp>
+-#include <boost/beast/core/span.hpp>
+-#include <boost/beast/http/buffer_body.hpp>
+-#include <boost/beast/http/message.hpp>
+-#include <boost/beast/version.hpp>
+-
+-#include <cstdlib>
+-#include <functional>
+-#include <iostream>
+-#include <memory>
+-#include <queue>
+-#include <string>
+-
+-namespace crow
+-{
+-
+-static constexpr uint8_t maxReqQueueSize = 50;
+-
+-enum class SseConnState
+-{
+- startInit,
+- initInProgress,
+- initialized,
+- initFailed,
+- sendInProgress,
+- sendFailed,
+- idle,
+- suspended,
+- closed
+-};
+-
+-class ServerSentEvents : public std::enable_shared_from_this<ServerSentEvents>
+-{
+- private:
+- std::shared_ptr<boost::beast::tcp_stream> sseConn;
+- std::queue<std::pair<uint64_t, std::string>> requestDataQueue;
+- std::string outBuffer;
+- SseConnState state;
+- int retryCount;
+- int maxRetryAttempts;
+-
+- void sendEvent(const std::string& id, const std::string& msg)
+- {
+- if (msg.empty())
+- {
+- BMCWEB_LOG_DEBUG << "Empty data, bailing out.";
+- return;
+- }
+-
+- if (state == SseConnState::sendInProgress)
+- {
+- return;
+- }
+- state = SseConnState::sendInProgress;
+-
+- if (!id.empty())
+- {
+- outBuffer += "id: ";
+- outBuffer.append(id.begin(), id.end());
+- outBuffer += "\n";
+- }
+-
+- outBuffer += "data: ";
+- for (char character : msg)
+- {
+- outBuffer += character;
+- if (character == '\n')
+- {
+- outBuffer += "data: ";
+- }
+- }
+- outBuffer += "\n\n";
+-
+- doWrite();
+- }
+-
+- void doWrite()
+- {
+- if (outBuffer.empty())
+- {
+- BMCWEB_LOG_DEBUG << "All data sent successfully.";
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- requestDataQueue.pop();
+- state = SseConnState::idle;
+- checkQueue();
+- return;
+- }
+-
+- sseConn->async_write_some(
+- boost::asio::buffer(outBuffer.data(), outBuffer.size()),
+- [self(shared_from_this())](
+- boost::beast::error_code ec,
+- [[maybe_unused]] const std::size_t& bytesTransferred) {
+- self->outBuffer.erase(0, bytesTransferred);
+-
+- if (ec == boost::asio::error::eof)
+- {
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- self->requestDataQueue.pop();
+- self->state = SseConnState::idle;
+- self->checkQueue();
+- return;
+- }
+-
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "async_write_some() failed: "
+- << ec.message();
+- self->state = SseConnState::sendFailed;
+- self->checkQueue();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: "
+- << bytesTransferred;
+-
+- self->doWrite();
+- });
+- }
+-
+- void startSSE()
+- {
+- if (state == SseConnState::initInProgress)
+- {
+- return;
+- }
+- state = SseConnState::initInProgress;
+-
+- BMCWEB_LOG_DEBUG << "starting SSE connection ";
+- using BodyType = boost::beast::http::buffer_body;
+- auto response =
+- std::make_shared<boost::beast::http::response<BodyType>>(
+- boost::beast::http::status::ok, 11);
+- auto serializer =
+- std::make_shared<boost::beast::http::response_serializer<BodyType>>(
+- *response);
+-
+- // TODO: Add hostname in http header.
+- response->set(boost::beast::http::field::server, "iBMC");
+- response->set(boost::beast::http::field::content_type,
+- "text/event-stream");
+- response->body().data = nullptr;
+- response->body().size = 0;
+- response->body().more = true;
+-
+- boost::beast::http::async_write_header(
+- *sseConn, *serializer,
+- [this, response,
+- serializer](const boost::beast::error_code& ec,
+- [[maybe_unused]] const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Error sending header" << ec;
+- state = SseConnState::initFailed;
+- checkQueue();
+- return;
+- }
+-
+- BMCWEB_LOG_DEBUG << "startSSE Header sent.";
+- state = SseConnState::initialized;
+- checkQueue();
+- });
+- }
+-
+- void checkQueue(const bool newRecord = false)
+- {
+- if (requestDataQueue.empty())
+- {
+- BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n";
+- return;
+- }
+-
+- if (retryCount >= maxRetryAttempts)
+- {
+- BMCWEB_LOG_ERROR << "Maximum number of retries is reached.";
+-
+- // Clear queue.
+- while (!requestDataQueue.empty())
+- {
+- requestDataQueue.pop();
+- }
+-
+- // TODO: Take 'DeliveryRetryPolicy' action.
+- // For now, doing 'SuspendRetries' action.
+- state = SseConnState::suspended;
+- return;
+- }
+-
+- if ((state == SseConnState::initFailed) ||
+- (state == SseConnState::sendFailed))
+- {
+- if (newRecord)
+- {
+- // We are already running async wait and retry.
+- // Since record is added to queue, it gets the
+- // turn in FIFO.
+- return;
+- }
+-
+- retryCount++;
+- // TODO: Perform async wait for retryTimeoutInterval before proceed.
+- }
+- else
+- {
+- // reset retry count.
+- retryCount = 0;
+- }
+-
+- switch (state)
+- {
+- case SseConnState::initInProgress:
+- case SseConnState::sendInProgress:
+- case SseConnState::suspended:
+- case SseConnState::startInit:
+- case SseConnState::closed:
+- // do nothing
+- break;
+- case SseConnState::initFailed:
+- {
+- startSSE();
+- break;
+- }
+- case SseConnState::initialized:
+- case SseConnState::idle:
+- case SseConnState::sendFailed:
+- {
+- std::pair<uint64_t, std::string> reqData =
+- requestDataQueue.front();
+- sendEvent(std::to_string(reqData.first), reqData.second);
+- break;
+- }
+- }
+-
+- return;
+- }
+-
+- public:
+- ServerSentEvents(const ServerSentEvents&) = delete;
+- ServerSentEvents& operator=(const ServerSentEvents&) = delete;
+- ServerSentEvents(ServerSentEvents&&) = delete;
+- ServerSentEvents& operator=(ServerSentEvents&&) = delete;
+-
+- ServerSentEvents(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+- sseConn(adaptor), state(SseConnState::startInit), retryCount(0),
+- maxRetryAttempts(5)
+- {
+- startSSE();
+- }
+-
+- ~ServerSentEvents() = default;
+-
+- void sendData(const uint64_t& id, const std::string& data)
+- {
+- if (state == SseConnState::suspended)
+- {
+- return;
+- }
+-
+- if (requestDataQueue.size() <= maxReqQueueSize)
+- {
+- requestDataQueue.push(std::pair(id, data));
+- checkQueue(true);
+- }
+- else
+- {
+- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data.";
+- }
+- }
+-};
+-
+-} // namespace crow
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index be6f04d..1875ec9 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -34,8 +34,6 @@ static constexpr const std::array<const char*, 1> supportedResourceTypes = {
+ "Task"};
+ #endif
+
+-static constexpr const uint8_t maxNoOfSubscriptions = 20;
+-
+ class EventService : public Node
+ {
+ public:
+@@ -59,6 +57,8 @@ class EventService : public Node
+ {"@odata.type", "#EventService.v1_5_0.EventService"},
+ {"Id", "EventService"},
+ {"Name", "Event Service"},
++ {"ServerSentEventUri",
++ "/redfish/v1/EventService/Subscriptions/SSE"},
+ {"Subscriptions",
+ {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}},
+ {"Actions",
+diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
+index 902c32b..c871faa 100644
+--- a/src/webserver_main.cpp
++++ b/src/webserver_main.cpp
+@@ -5,6 +5,7 @@
+ #include <cors_preflight.hpp>
+ #include <dbus_monitor.hpp>
+ #include <dbus_singleton.hpp>
++#include <eventservice_sse.hpp>
+ #include <hostname_monitor.hpp>
+ #include <ibm/management_console_rest.hpp>
+ #include <image_upload.hpp>
+@@ -83,6 +84,7 @@ int main(int /*argc*/, char** /*argv*/)
+ #ifdef BMCWEB_ENABLE_REDFISH
+ redfish::requestRoutes(app);
+ redfish::RedfishService redfish(app);
++ redfish::eventservice_sse::requestRoutes(app);
+
+ // Create EventServiceManager instance and initialize Config
+ redfish::EventServiceManager::getInstance();
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch
new file mode 100644
index 000000000..4d99b2754
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch
@@ -0,0 +1,243 @@
+From 82185c52ae6e27cc1e086654a3b7d10e63ecc783 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Wed, 17 Mar 2021 01:16:50 +0000
+Subject: [PATCH 6/6] Add EventService SSE filter support
+
+This commit implements the Event Service SSE stream
+filters support. As per redfish specification:
+The SSE streams have these formats:
+ - Metric report SSE stream
+ - Event message SSE stream
+
+To reduce the amount of data, service supports $filter
+query parameter in SSE URI.
+Below properties support as filter criteria:
+ - EventFormatType( Event & MetricReport)
+ - MessageId
+ - RegistryPrefix
+ - MetricReportDefinition
+
+For more details, refer Redfish specification section 13.5.2
+
+Tested:
+ Created SSE stream with different filters and observed
+ desired events on SSE stream client(browser), some examples
+ - To get all Redfish events,
+ URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)
+ - To get Redfish events with RegistryPrefix "OpenBMC"
+ URi: /redfish/v1/EventService/Subscriptions/SSE?$filter=(RegistryPrefix%20eq%20OpenBMC)
+ - To get only DC power of Events,
+ URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)%20and%20(MessageId%20eq%20DCPowerOff)
+
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Change-Id: I55c6f53bb5e57aa1f2d1601f1a16525a33b13bd2
+---
+ include/eventservice_sse.hpp | 94 ++++++++++++++++++-
+ redfish-core/include/error_messages.hpp | 9 ++
+ .../include/event_service_manager.hpp | 5 +
+ redfish-core/lib/event_service.hpp | 4 -
+ redfish-core/src/error_messages.cpp | 26 +++++
+ 5 files changed, 130 insertions(+), 8 deletions(-)
+
+diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp
+index 6c98e6e..ff72c4d 100644
+--- a/include/eventservice_sse.hpp
++++ b/include/eventservice_sse.hpp
+@@ -23,16 +23,102 @@ static bool createSubscription(std::shared_ptr<crow::SseConnection>& conn,
+ }
+ BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size();
+
++ // EventService SSE supports only "$filter" query param.
++ if (req.urlParams.size() > 1)
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++ std::string eventFormatType;
++ std::string queryFilters;
++ if (req.urlParams.size())
++ {
++ boost::urls::url_view::params_type::iterator it =
++ req.urlParams.find("$filter");
++ if (it == req.urlParams.end())
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++ queryFilters = it->value();
++ }
++ else
++ {
++ eventFormatType = "Event";
++ }
++
++ std::vector<std::string> msgIds;
++ std::vector<std::string> regPrefixes;
++ std::vector<std::string> mrdsArray;
++ if (!queryFilters.empty())
++ {
++ // Reading from query params.
++ bool status = readSSEQueryParams(queryFilters, eventFormatType, msgIds,
++ regPrefixes, mrdsArray);
++ if (!status)
++ {
++ messages::invalidObject(res, queryFilters);
++ res.end();
++ return false;
++ }
++
++ // RegsitryPrefix and messageIds are mutuly exclusive as per redfish
++ // specification.
++ if (regPrefixes.size() && msgIds.size())
++ {
++ messages::mutualExclusiveProperties(res, "RegistryPrefix",
++ "MessageId");
++ res.end();
++ return false;
++ }
++
++ if (!eventFormatType.empty())
++ {
++ if (std::find(supportedEvtFormatTypes.begin(),
++ supportedEvtFormatTypes.end(),
++ eventFormatType) == supportedEvtFormatTypes.end())
++ {
++ messages::propertyValueNotInList(res, eventFormatType,
++ "EventFormatType");
++ res.end();
++ return false;
++ }
++ }
++ else
++ {
++ // If nothing specified, using default "Event"
++ eventFormatType = "Event";
++ }
++
++ if (!regPrefixes.empty())
++ {
++ for (const std::string& it : regPrefixes)
++ {
++ if (std::find(supportedRegPrefixes.begin(),
++ supportedRegPrefixes.end(),
++ it) == supportedRegPrefixes.end())
++ {
++ messages::propertyValueNotInList(res, it, "RegistryPrefix");
++ res.end();
++ return false;
++ }
++ }
++ }
++ }
++
+ std::shared_ptr<redfish::Subscription> subValue =
+ std::make_shared<redfish::Subscription>(std::move(conn));
+
+ // GET on this URI means, Its SSE subscriptionType.
+- subValue->subscriptionType = redfish::subscriptionTypeSSE;
+-
+- // TODO: parse $filter query params and fill config.
++ subValue->subscriptionType = subscriptionTypeSSE;
+ subValue->protocol = "Redfish";
+ subValue->retryPolicy = "TerminateAfterRetries";
+- subValue->eventFormatType = "Event";
++ subValue->eventFormatType = eventFormatType;
++ subValue->registryMsgIds = msgIds;
++ subValue->registryPrefixes = regPrefixes;
++ subValue->metricReportDefinitions = mrdsArray;
+
+ std::string id =
+ redfish::EventServiceManager::getInstance().addSubscription(subValue,
+diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp
+index 7dfdc80..922dae9 100644
+--- a/redfish-core/include/error_messages.hpp
++++ b/redfish-core/include/error_messages.hpp
+@@ -959,6 +959,15 @@ nlohmann::json mutualExclusiveProperties(const std::string& arg1,
+ void mutualExclusiveProperties(crow::Response& res, const std::string& arg1,
+ const std::string& arg2);
+
++/**
++ * @brief Formats InvalidQueryFilter message into JSON
++ * Message body: "The requested URL contains the invalid query filters"
++ *
++ * @returns Message InvalidQueryFilter formatted to JSON */
++nlohmann::json invalidQueryFilter();
++
++void invalidQueryFilter(crow::Response& res);
++
+ } // namespace messages
+
+ } // namespace redfish
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index f4d57c2..567fb9c 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -55,6 +55,11 @@ static constexpr const char* eventServiceFile =
+ static constexpr const uint8_t maxNoOfSubscriptions = 20;
+ static constexpr const uint8_t maxNoOfSSESubscriptions = 10;
+
++static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
++ eventFormatType, metricReportFormatType};
++static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
++ "Base", "OpenBMC", "Task"};
++
+ #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
+ static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
+ static constexpr const char* redfishEventLogDir = "/var/log";
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index 1875ec9..4d1ac9f 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -19,10 +19,6 @@
+ namespace redfish
+ {
+
+-static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
+- eventFormatType, metricReportFormatType};
+-static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
+- "Base", "OpenBMC", "Task"};
+ static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
+ "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
+
+diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp
+index 7059a38..1e3ef2f 100644
+--- a/redfish-core/src/error_messages.cpp
++++ b/redfish-core/src/error_messages.cpp
+@@ -2147,6 +2147,32 @@ void mutualExclusiveProperties(crow::Response& res, const std::string& arg1,
+ addMessageToErrorJson(res.jsonValue, mutualExclusiveProperties(arg1, arg2));
+ }
+
++/**
++ * @internal
++ * @brief Formats InvalidQueryFilter into JSON
++ *
++ * See header file for more information
++ * @endinternal
++ */
++nlohmann::json invalidQueryFilter()
++{
++ return nlohmann::json{
++ {"@odata.type", "#Message.v1_0_0.Message"},
++ {"MessageId", "Base.1.5.0.InvalidQueryFilter"},
++ {"Message", "The requested url contains the invalid query filter."},
++ {"MessageArgs", nlohmann::json::array()},
++ {"Severity", "Warning"},
++ {"Resolution",
++ "Ensure the correct query filter is specified in requested url "
++ "and resubmit the request."}};
++}
++
++void invalidQueryFilter(crow::Response& res)
++{
++ res.result(boost::beast::http::status::bad_request);
++ addMessageToErrorJson(res.jsonValue, invalidQueryFilter());
++}
++
+ } // namespace messages
+
+ } // namespace redfish
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README
new file mode 100644
index 000000000..3954e89c1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README
@@ -0,0 +1,19 @@
+Eventservice specific patches: Temporary pulling down
+the upstream patches. These will be remove as soon as
+thee gets merged upstream.
+
+Upstream revision information:
+ - EventService : Fix retry handling for http-client
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/40731/18
+
+ - EventService: https client support
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31735/38
+
+ - Add Server-Sent-Events support
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41258/5
+
+ - Add SSE style subscription support to eventservice
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41319/5
+
+ - Add EventService SSE filter support
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41349/2
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch
new file mode 100644
index 000000000..03feec633
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch
@@ -0,0 +1,369 @@
+From b6ccf463b4cfb8df4a904f06c5f4852029a96c50 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Tue, 15 Dec 2020 12:28:17 +0100
+Subject: [PATCH 3/4] Add support for MetricDefinition scheme
+
+Added MetricDefinition node to Redfish code. Now user is able
+to list all available metrics in OpenBMC that are supported
+by Telemetry service. Metrics are grouped by following
+categories: temperature, power, voltage, current, fan_tach,
+fan_pwm, utilization.
+
+Tested:
+ - MetricDefinitions response is filled with existing sensors,
+ it works with and without Telemetry service
+ - Validated a presence of MetricDefinition members and it
+ attributes
+ - Succesfully passed RedfishServiceValidator.py using
+ witherspoon image on QEMU
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I3086e1302e1ba2e5442d1367939fd5507a0cbc00
+---
+ redfish-core/include/redfish.hpp | 3 +
+ .../include/utils/telemetry_utils.hpp | 2 +
+ redfish-core/lib/metric_definition.hpp | 283 ++++++++++++++++++
+ redfish-core/lib/telemetry_service.hpp | 2 +
+ 4 files changed, 290 insertions(+)
+ create mode 100644 redfish-core/lib/metric_definition.hpp
+
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index aad28ac..dfcb8cd 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -25,6 +25,7 @@
+ #include "../lib/managers.hpp"
+ #include "../lib/memory.hpp"
+ #include "../lib/message_registries.hpp"
++#include "../lib/metric_definition.hpp"
+ #include "../lib/metric_report.hpp"
+ #include "../lib/metric_report_definition.hpp"
+ #include "../lib/network_protocol.hpp"
+@@ -215,6 +216,8 @@ class RedfishService
+ nodes.emplace_back(std::make_unique<HypervisorResetActionInfo>(app));
+
+ nodes.emplace_back(std::make_unique<TelemetryService>(app));
++ nodes.emplace_back(std::make_unique<MetricDefinitionCollection>(app));
++ nodes.emplace_back(std::make_unique<MetricDefinition>(app));
+ nodes.emplace_back(
+ std::make_unique<MetricReportDefinitionCollection>(app));
+ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app));
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 0a3af5f..54b5133 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -10,6 +10,8 @@ namespace telemetry
+
+ constexpr const char* service = "xyz.openbmc_project.Telemetry";
+ constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report";
++constexpr const char* metricDefinitionUri =
++ "/redfish/v1/TelemetryService/MetricDefinitions/";
+ constexpr const char* metricReportDefinitionUri =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
+ constexpr const char* metricReportUri =
+diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp
+new file mode 100644
+index 0000000..f9c7779
+--- /dev/null
++++ b/redfish-core/lib/metric_definition.hpp
+@@ -0,0 +1,283 @@
++#pragma once
++
++#include "node.hpp"
++#include "sensors.hpp"
++#include "utils/telemetry_utils.hpp"
++
++namespace redfish
++{
++
++namespace utils
++{
++
++template <typename F>
++inline void getChassisNames(F&& cb, const std::shared_ptr<AsyncResp>& asyncResp)
++{
++ const std::array<const char*, 2> interfaces = {
++ "xyz.openbmc_project.Inventory.Item.Board",
++ "xyz.openbmc_project.Inventory.Item.Chassis"};
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp,
++ callback = std::move(cb)](const boost::system::error_code ec,
++ std::vector<std::string>& chassis) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_DEBUG << "DBus call error: " << ec.value();
++ return;
++ }
++
++ std::vector<std::string> chassisNames;
++ chassisNames.reserve(chassis.size());
++ for (const auto& path : chassis)
++ {
++ sdbusplus::message::object_path dbusPath = path;
++ std::string name = dbusPath.filename();
++ if (name.empty())
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "Invalid chassis: " << dbusPath.str;
++ return;
++ }
++ chassisNames.emplace_back(std::move(name));
++ }
++
++ callback(chassisNames);
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
++ "/xyz/openbmc_project/inventory", 0, interfaces);
++}
++} // namespace utils
++
++namespace telemetry
++{
++
++class MetricDefinitionCollectionReduce
++{
++ public:
++ MetricDefinitionCollectionReduce(
++ const std::shared_ptr<AsyncResp>& asyncResp) :
++ asyncResp{asyncResp}
++ {}
++
++ ~MetricDefinitionCollectionReduce()
++ {
++ if (asyncResp->res.result() != boost::beast::http::status::ok)
++ {
++ return;
++ }
++
++ nlohmann::json& members = asyncResp->res.jsonValue["Members"];
++ members = nlohmann::json::array();
++
++ for (const std::string& type : dbusTypes)
++ {
++ members.push_back(
++ {{"@odata.id", telemetry::metricDefinitionUri + type}});
++ }
++ asyncResp->res.jsonValue["Members@odata.count"] = members.size();
++ }
++
++ void insert(const boost::container::flat_map<std::string, std::string>& el)
++ {
++ for (const auto& [_, dbusSensor] : el)
++ {
++ sdbusplus::message::object_path path(dbusSensor);
++ sdbusplus::message::object_path parentPath = path.parent_path();
++ std::string type = parentPath.filename();
++ if (type.empty())
++ {
++ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = "
++ << dbusSensor;
++ continue;
++ }
++
++ dbusTypes.insert(std::move(type));
++ }
++ }
++
++ private:
++ const std::shared_ptr<AsyncResp> asyncResp;
++ boost::container::flat_set<std::string> dbusTypes;
++};
++
++class MetricDefinitionReduce
++{
++ public:
++ MetricDefinitionReduce(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& id) :
++ id(id),
++ pattern{'/' + id + '/'}, asyncResp{asyncResp}
++ {}
++ ~MetricDefinitionReduce()
++ {
++ if (asyncResp->res.result() != boost::beast::http::status::ok)
++ {
++ return;
++ }
++ if (redfishSensors.empty())
++ {
++ messages::resourceNotFound(asyncResp->res, "MetricDefinition", id);
++ return;
++ }
++
++ asyncResp->res.jsonValue["MetricProperties"] = redfishSensors;
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = id;
++ asyncResp->res.jsonValue["@odata.id"] =
++ telemetry::metricDefinitionUri + id;
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#MetricDefinition.v1_0_3.MetricDefinition";
++ asyncResp->res.jsonValue["MetricDataType"] = "Decimal";
++ asyncResp->res.jsonValue["MetricType"] = "Numeric";
++ asyncResp->res.jsonValue["IsLinear"] = true;
++ asyncResp->res.jsonValue["Units"] = sensors::toReadingUnits(id);
++ }
++
++ void insert(const boost::container::flat_map<std::string, std::string>& el)
++ {
++ for (const auto& [redfishSensor, dbusSensor] : el)
++ {
++ if (dbusSensor.find(pattern) != std::string::npos)
++ {
++ redfishSensors.push_back(redfishSensor);
++ }
++ }
++ }
++
++ private:
++ const std::string id;
++ const std::string pattern;
++ const std::shared_ptr<AsyncResp> asyncResp;
++ std::vector<std::string> redfishSensors;
++};
++} // namespace telemetry
++
++class MetricDefinitionCollection : public Node
++{
++ public:
++ MetricDefinitionCollection(App& app) :
++ Node(app, "/redfish/v1/TelemetryService/MetricDefinitions/")
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request&,
++ const std::vector<std::string>&) override
++ {
++ res.jsonValue["@odata.type"] = "#MetricDefinitionCollection."
++ "MetricDefinitionCollection";
++ res.jsonValue["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricDefinitions";
++ res.jsonValue["Name"] = "Metric Definition Collection";
++ res.jsonValue["Members"] = nlohmann::json::array();
++ res.jsonValue["Members@odata.count"] = 0;
++
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ utils::getChassisNames(
++ [asyncResp](const std::vector<std::string>& chassisNames) {
++ auto collectionReduce = std::make_shared<
++ telemetry::MetricDefinitionCollectionReduce>(asyncResp);
++ for (const std::string& chassisName : chassisNames)
++ {
++ for (const auto& [sensorNode, _] : sensors::dbus::paths)
++ {
++ BMCWEB_LOG_INFO << "Chassis: " << chassisName
++ << " sensor: " << sensorNode;
++ retrieveUriToDbusMap(
++ chassisName, sensorNode.data(),
++ [asyncResp, collectionReduce](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ if (status != boost::beast::http::status::ok)
++ {
++ BMCWEB_LOG_ERROR
++ << "Failed to retrieve URI to dbus "
++ "sensors map with err "
++ << static_cast<unsigned>(status);
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ collectionReduce->insert(uriToDbus);
++ });
++ }
++ }
++ },
++ asyncResp);
++ }
++};
++
++class MetricDefinition : public Node
++{
++ public:
++ MetricDefinition(App& app) :
++ Node(app, "/redfish/v1/TelemetryService/MetricDefinitions/<str>/",
++ std::string())
++ {
++ entityPrivileges = {
++ {boost::beast::http::verb::get, {{"Login"}}},
++ {boost::beast::http::verb::head, {{"Login"}}},
++ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
++ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
++ }
++
++ private:
++ void doGet(crow::Response& res, const crow::Request&,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ if (params.size() != 1)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::string& id = params[0];
++ utils::getChassisNames(
++ [asyncResp, id](const std::vector<std::string>& chassisNames) {
++ auto definitionGather =
++ std::make_shared<telemetry::MetricDefinitionReduce>(
++ asyncResp, id);
++ for (const std::string& chassisName : chassisNames)
++ {
++ for (const auto& [sensorNode, dbusPaths] :
++ sensors::dbus::paths)
++ {
++ retrieveUriToDbusMap(
++ chassisName, sensorNode.data(),
++ [asyncResp, definitionGather](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ if (status != boost::beast::http::status::ok)
++ {
++ BMCWEB_LOG_ERROR
++ << "Failed to retrieve URI to dbus "
++ "sensors map with err "
++ << static_cast<unsigned>(status);
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ definitionGather->insert(uriToDbus);
++ });
++ }
++ }
++ },
++ asyncResp);
++ }
++};
++
++} // namespace redfish
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+index 61ca891..a8c8b03 100644
+--- a/redfish-core/lib/telemetry_service.hpp
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -32,6 +32,8 @@ class TelemetryService : public Node
+ res.jsonValue["Id"] = "TelemetryService";
+ res.jsonValue["Name"] = "Telemetry Service";
+
++ res.jsonValue["MetricDefinitions"]["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricDefinitions";
+ res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
+ res.jsonValue["MetricReports"]["@odata.id"] =
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch
new file mode 100644
index 000000000..f2ebce6e3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch
@@ -0,0 +1,295 @@
+From efcd128a3d66fce33200fd4211ba5abf13a81375 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Tue, 15 Dec 2020 12:30:31 +0100
+Subject: [PATCH 4/4] Sync Telmetry service with EventService
+
+Synced the latest changes in Telemetry service with Event Service
+code. Now assembling MetricReport is covered in single place in
+code. Updated method of fetching Readings from Telemetry by
+Event Service. Using ReportUpdate signal is no longer
+supported. Now Event Service monitors for PropertiesChanged signal
+from /xyz/openbmc_project/Telemetry/Reports path.
+
+Tested:
+ - Verified that EventListener received MetricReport response from
+ Event Service in insecure http push style eventing mode
+
+Change-Id: I2fc1841a6c9259a8bff30b34bddc0d4aabd41912
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+---
+ .../include/event_service_manager.hpp | 156 ++++++------------
+ redfish-core/lib/metric_report.hpp | 28 ++--
+ 2 files changed, 69 insertions(+), 115 deletions(-)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 148c703..27e41e3 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -14,6 +14,7 @@
+ // limitations under the License.
+ */
+ #pragma once
++#include "metric_report.hpp"
+ #include "node.hpp"
+ #include "registries.hpp"
+ #include "registries/base_message_registry.hpp"
+@@ -512,47 +513,32 @@ class Subscription
+ }
+ #endif
+
+- void filterAndSendReports(const std::string& id2,
+- const std::string& readingsTs,
+- const ReadingsObjType& readings)
++ void filterAndSendReports(
++ const std::string& id,
++ const std::variant<telemetry::TimestampReadings>& var)
+ {
+- std::string metricReportDef =
+- "/redfish/v1/TelemetryService/MetricReportDefinitions/" + id2;
++ std::string mrdUri = telemetry::metricReportDefinitionUri + id;
+
+ // Empty list means no filter. Send everything.
+ if (metricReportDefinitions.size())
+ {
+ if (std::find(metricReportDefinitions.begin(),
+ metricReportDefinitions.end(),
+- metricReportDef) == metricReportDefinitions.end())
++ mrdUri) == metricReportDefinitions.end())
+ {
+ return;
+ }
+ }
+
+- nlohmann::json metricValuesArray = nlohmann::json::array();
+- for (const auto& it : readings)
++ nlohmann::json msg;
++ if (!telemetry::fillReport(msg, id, var))
+ {
+- metricValuesArray.push_back({});
+- nlohmann::json& entry = metricValuesArray.back();
+-
+- auto& [id, property, value, timestamp] = it;
+-
+- entry = {{"MetricId", id},
+- {"MetricProperty", property},
+- {"MetricValue", std::to_string(value)},
+- {"Timestamp", crow::utility::getDateTime(timestamp)}};
++ BMCWEB_LOG_ERROR << "Failed to fill the MetricReport for DBus "
++ "Report with id "
++ << id;
++ return;
+ }
+
+- nlohmann::json msg = {
+- {"@odata.id", "/redfish/v1/TelemetryService/MetricReports/" + id},
+- {"@odata.type", "#MetricReport.v1_3_0.MetricReport"},
+- {"Id", id2},
+- {"Name", id2},
+- {"Timestamp", readingsTs},
+- {"MetricReportDefinition", {{"@odata.id", metricReportDef}}},
+- {"MetricValues", metricValuesArray}};
+-
+ this->sendEvent(
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace));
+ }
+@@ -1348,75 +1334,6 @@ class EventServiceManager
+ }
+
+ #endif
+-
+- void getMetricReading(const std::string& service,
+- const std::string& objPath, const std::string& intf)
+- {
+- std::size_t found = objPath.find_last_of('/');
+- if (found == std::string::npos)
+- {
+- BMCWEB_LOG_DEBUG << "Invalid objPath received";
+- return;
+- }
+-
+- std::string idStr = objPath.substr(found + 1);
+- if (idStr.empty())
+- {
+- BMCWEB_LOG_DEBUG << "Invalid ID in objPath";
+- return;
+- }
+-
+- crow::connections::systemBus->async_method_call(
+- [idStr{std::move(idStr)}](
+- const boost::system::error_code ec,
+- boost::container::flat_map<
+- std::string, std::variant<int32_t, ReadingsObjType>>&
+- resp) {
+- if (ec)
+- {
+- BMCWEB_LOG_DEBUG
+- << "D-Bus call failed to GetAll metric readings.";
+- return;
+- }
+-
+- const int32_t* timestampPtr =
+- std::get_if<int32_t>(&resp["Timestamp"]);
+- if (!timestampPtr)
+- {
+- BMCWEB_LOG_DEBUG << "Failed to Get timestamp.";
+- return;
+- }
+-
+- ReadingsObjType* readingsPtr =
+- std::get_if<ReadingsObjType>(&resp["Readings"]);
+- if (!readingsPtr)
+- {
+- BMCWEB_LOG_DEBUG << "Failed to Get Readings property.";
+- return;
+- }
+-
+- if (!readingsPtr->size())
+- {
+- BMCWEB_LOG_DEBUG << "No metrics report to be transferred";
+- return;
+- }
+-
+- for (const auto& it :
+- EventServiceManager::getInstance().subscriptionsMap)
+- {
+- std::shared_ptr<Subscription> entry = it.second;
+- if (entry->eventFormatType == metricReportFormatType)
+- {
+- entry->filterAndSendReports(
+- idStr, crow::utility::getDateTime(*timestampPtr),
+- *readingsPtr);
+- }
+- }
+- },
+- service, objPath, "org.freedesktop.DBus.Properties", "GetAll",
+- intf);
+- }
+-
+ void unregisterMetricReportSignal()
+ {
+ if (matchTelemetryMonitor)
+@@ -1436,9 +1353,11 @@ class EventServiceManager
+ }
+
+ BMCWEB_LOG_DEBUG << "Metrics report signal - Register";
+- std::string matchStr(
+- "type='signal',member='ReportUpdate', "
+- "interface='xyz.openbmc_project.MonitoringService.Report'");
++ std::string matchStr = "type='signal',member='PropertiesChanged',"
++ "interface='org.freedesktop.DBus.Properties',"
++ "path_namespace=/xyz/openbmc_project/Telemetry/"
++ "Reports/TelemetryService,"
++ "arg0=xyz.openbmc_project.Telemetry.Report";
+
+ matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>(
+ *crow::connections::systemBus, matchStr,
+@@ -1449,10 +1368,43 @@ class EventServiceManager
+ return;
+ }
+
+- std::string service = msg.get_sender();
+- std::string objPath = msg.get_path();
+- std::string intf = msg.get_interface();
+- getMetricReading(service, objPath, intf);
++ sdbusplus::message::object_path path(msg.get_path());
++ std::string id = path.filename();
++ if (id.empty())
++ {
++ BMCWEB_LOG_ERROR << "Failed to get Id from path";
++ return;
++ }
++
++ std::string intf;
++ std::vector<std::pair<
++ std::string, std::variant<telemetry::TimestampReadings>>>
++ props;
++ std::vector<std::string> invalidProps;
++ msg.read(intf, props, invalidProps);
++
++ auto found =
++ std::find_if(props.begin(), props.end(), [](const auto& x) {
++ return x.first == "Readings";
++ });
++ if (found == props.end())
++ {
++ BMCWEB_LOG_ERROR
++ << "Failed to get Readings from Report properties";
++ return;
++ }
++
++ std::variant<telemetry::TimestampReadings>& readings =
++ found->second;
++ for (const auto& it :
++ EventServiceManager::getInstance().subscriptionsMap)
++ {
++ Subscription& entry = *it.second.get();
++ if (entry.eventFormatType == metricReportFormatType)
++ {
++ entry.filterAndSendReports(id, readings);
++ }
++ }
+ });
+ }
+
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+index 9caf4a3..e79a41c 100644
+--- a/redfish-core/lib/metric_report.hpp
++++ b/redfish-core/lib/metric_report.hpp
+@@ -31,16 +31,14 @@ inline nlohmann::json toMetricValues(const Readings& readings)
+ return metricValues;
+ }
+
+-inline void fillReport(const std::shared_ptr<AsyncResp>& asyncResp,
+- const std::string& id,
++inline bool fillReport(nlohmann::json& json, const std::string& id,
+ const std::variant<TimestampReadings>& var)
+ {
+- asyncResp->res.jsonValue["@odata.type"] =
+- "#MetricReport.v1_3_0.MetricReport";
+- asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id;
+- asyncResp->res.jsonValue["Id"] = id;
+- asyncResp->res.jsonValue["Name"] = id;
+- asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] =
++ json["@odata.type"] = "#MetricReport.v1_3_0.MetricReport";
++ json["@odata.id"] = telemetry::metricReportUri + id;
++ json["Id"] = id;
++ json["Name"] = id;
++ json["MetricReportDefinition"]["@odata.id"] =
+ telemetry::metricReportDefinitionUri + id;
+
+ const TimestampReadings* timestampReadings =
+@@ -48,14 +46,14 @@ inline void fillReport(const std::shared_ptr<AsyncResp>& asyncResp,
+ if (!timestampReadings)
+ {
+ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing";
+- messages::internalError(asyncResp->res);
+- return;
++ return false;
+ }
+
+ const auto& [timestamp, readings] = *timestampReadings;
+- asyncResp->res.jsonValue["Timestamp"] =
++ json["Timestamp"] =
+ crow::utility::getDateTime(static_cast<time_t>(timestamp));
+- asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings);
++ json["MetricValues"] = toMetricValues(readings);
++ return true;
+ }
+ } // namespace telemetry
+
+@@ -146,7 +144,11 @@ class MetricReport : public Node
+ return;
+ }
+
+- telemetry::fillReport(asyncResp, id, ret);
++ if (!telemetry::fillReport(asyncResp->res.jsonValue, id,
++ ret))
++ {
++ messages::internalError(asyncResp->res);
++ }
+ },
+ telemetry::service, reportPath,
+ "org.freedesktop.DBus.Properties", "Get",
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
new file mode 100644
index 000000000..46128f7ae
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
@@ -0,0 +1,10 @@
+These patches are mirror of upstream TelemetryService implementation.
+Until change is integrated they will be manually merged here to enable feature in Intel builds.
+
+Current revisions:
+- Add support for MetricDefinition scheme
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/60
+
+- Sync Telmetry service with EventService
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/26
+