Support to remotely configure UEFI SecureBoot Settings

Redfish added schema for SecureBoot contains UEFI Secure Boot
information and represents properties for managing the UEFI Secure
Boot functionality of a system. This patch adds support to configure
the settings from BMC.

Introduced option 'ENABLE_BIOS_SECUREBOOT` to selectively create
SecureBoot object.

The PDI Changes for SecureBoot:
[1]: https://github.com/openbmc/phosphor-dbus-interfaces/commit/b235159e0acc9943bc5f4e428ba6536f2e3cb621#diff-dbd3a29b95a6a0d436ba19696c3db9852172311f363b6781cc48b49d62ee28fa

Redfish URI enabled with this change
`/redfish/v1/Systems/<system>/SecureBoot`

Tested:
1) Dbus tree view with the change
```
busctl tree xyz.openbmc_project.BIOSConfigManager
`- /xyz
  `- /xyz/openbmc_project
    `- /xyz/openbmc_project/bios_config
      |- /xyz/openbmc_project/bios_config/manager
      |- /xyz/openbmc_project/bios_config/password
      `- /xyz/openbmc_project/bios_config/secure_boot
```
2) Runtime Check at Redfish Level:
On platforms where the ENABLE_BIOS_SECUREBOOT is disabled the
redfish URI at the redfish level is disabled as the dbus path
does not exists.
3) For persistence of BIOS secureboot values the data is written to
separate file `securebootData` under
`/var/lib/bios-settings-manager`. This will avoid any issues for
current platforms.

Change-Id: I51cb42671bb7c62ef51f8d77b17265ab24edbcff
Signed-off-by: Prithvi Pai <ppai@nvidia.com>
diff --git a/README.md b/README.md
index edb02c7..32388c6 100644
--- a/README.md
+++ b/README.md
@@ -194,6 +194,31 @@
 - **PasswordInitialized** Used to indicate whether the BIOS password-related
   details have been received.
 
+## RBC SecureBoot Interface
+
+The SecureBoot interface exposes methods and properties to Get & Set UEFI
+SecureBoot settings via dbus and its documented [here][pdi-secureboot-bios]
+
+### Object Path
+
+```txt
+xyz.openbmc_project.BIOSConfig.SecureBoot
+```
+
+### Properties
+
+- **CurrentBoot** Used to indicate UEFI Secure Boot state during current boot
+  cycle
+- **PendingEnable** An indication of whether the UEFI Secure Boot takes effect
+  on next boot
+- **Mode** The current UEFI Secure Boot Mode
+
+### SecureBoot with Redfish Host Interface as Communication Protocol
+
+For systems that use the **Redfish Host Interface** protocol between BMC & Host,
+UEFI SecureBoot configuration is gathered by BMC via redfish. The settings are
+transformed to native dbus format and properties are set accordingly.
+
 [rbmc-design-document]:
   https://github.com/openbmc/docs/blob/master/designs/remote-bios-configuration.md
 [pldm-bios-json]:
@@ -203,3 +228,5 @@
   https://github.com/openbmc/intel-ipmi-oem/blob/master/src/biosconfigcommands.cpp
 [pdi-manager-bios]:
   https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/xyz/openbmc_project/BIOSConfig/Manager.interface.yaml
+[pdi-secureboot-bios]:
+  https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/xyz/openbmc_project/BIOSConfig/SecureBoot.interface.yaml
diff --git a/include/secureboot.hpp b/include/secureboot.hpp
new file mode 100644
index 0000000..da638b8
--- /dev/null
+++ b/include/secureboot.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include <cereal/access.hpp>
+#include <cereal/cereal.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/BIOSConfig/SecureBoot/server.hpp>
+
+#include <filesystem>
+#include <string>
+
+namespace fs = std::filesystem;
+
+namespace bios_config
+{
+static constexpr auto secureBootObjectPath =
+    "/xyz/openbmc_project/bios_config/secure_boot";
+static constexpr auto secureBootPersistFile = "securebootData";
+
+using SecureBootBase =
+    sdbusplus::xyz::openbmc_project::BIOSConfig::server::SecureBoot;
+
+class SecureBoot : public SecureBootBase
+{
+  public:
+    SecureBoot() = delete;
+    ~SecureBoot() = default;
+    SecureBoot(const SecureBoot&) = delete;
+    SecureBoot& operator=(const SecureBoot&) = delete;
+    SecureBoot(SecureBoot&&) = delete;
+    SecureBoot& operator=(SecureBoot&&) = delete;
+
+    /** @brief Constructs SecureBoot object.
+     *
+     *  @param[in] objectServer  - object server
+     *  @param[in] systemBus - bus connection
+     *  @param[in] persistPath - path to the secureboot data file
+     */
+    SecureBoot(sdbusplus::asio::object_server& objectServer,
+               std::shared_ptr<sdbusplus::asio::connection>& systemBus,
+               std::string persistPath);
+
+    /** @brief Indicates the UEFI Secure Boot state during the current boot
+     * cycle
+     *
+     *  @param[in] value - Boot Type during the current cycle
+     *
+     *  @return On success, return the CurrentBootType
+     */
+    CurrentBootType currentBoot(CurrentBootType value) override;
+
+    /** @brief Indicates whether the UEFI Secure Boot takes effect on next boot
+     *
+     *  @param[in] value - new value for the attribute
+     *
+     *  @return On succes, return the new attribute
+     */
+    bool pendingEnable(bool value) override;
+
+    /** @brief Indicates the current UEFI Secure Boot Mode
+     *
+     *  @param[in] value - new value for the attribute
+     *
+     *  @return On success, return the new attribute
+     */
+    ModeType mode(ModeType value) override;
+
+  private:
+    sdbusplus::asio::object_server& objServer;
+    std::shared_ptr<sdbusplus::asio::connection>& systemBus;
+    std::filesystem::path secureBootFile;
+
+    friend class cereal::access;
+
+    /** @brief Save the SecureBoot object to the persistent storage
+     *
+     *  @param[in] archive - archive
+     *  @param[in] version - version
+     */
+    template <class Archive>
+    void save(Archive& archive, const std::uint32_t version) const
+    {
+        // version is not used currently
+        lg2::error("Save is called with version {VER}", "VER", version);
+        archive(sdbusplus::xyz::openbmc_project::BIOSConfig::server::
+                    SecureBoot::currentBoot(),
+                sdbusplus::xyz::openbmc_project::BIOSConfig::server::
+                    SecureBoot::pendingEnable(),
+                sdbusplus::xyz::openbmc_project::BIOSConfig::server::
+                    SecureBoot::mode());
+    }
+
+    /** @brief Load the SecureBoot object from the persistent storage
+     *
+     *  @param[in] archive - archive
+     *  @param[in] version - version
+     */
+    template <class Archive>
+    void load(Archive& archive, const std::uint32_t version)
+    {
+        (void)(version);
+        SecureBoot::CurrentBootType currentBootValue =
+            SecureBoot::CurrentBootType::Unknown;
+        bool enableValue = false;
+        SecureBoot::ModeType modeValue = SecureBoot::ModeType::Unknown;
+
+        archive(currentBootValue, enableValue, modeValue);
+        sdbusplus::xyz::openbmc_project::BIOSConfig::server::SecureBoot::
+            currentBoot(currentBootValue, true);
+        sdbusplus::xyz::openbmc_project::BIOSConfig::server::SecureBoot::
+            pendingEnable(enableValue, true);
+        sdbusplus::xyz::openbmc_project::BIOSConfig::server::SecureBoot::mode(
+            modeValue, true);
+    }
+
+    /** @brief Serialize the SecureBoot object to the persistent storage
+     */
+    void serialize();
+
+    /** @brief Deserialize the SecureBoot object from the persistent storage
+     *
+     *  @return On success, return true
+     *  @return On failure, return false
+     */
+    bool deserialize();
+};
+} // namespace bios_config
diff --git a/meson.build b/meson.build
index 1314efe..5d3df88 100644
--- a/meson.build
+++ b/meson.build
@@ -18,6 +18,12 @@
 # project uses the same compiler, we can safely ignmore these info notes.
 add_project_arguments('-Wno-psabi', language: 'cpp')
 
+conf_data = configuration_data()
+if (get_option('enable-bios-secureboot').allowed())
+    add_project_arguments('-DENABLE_BIOS_SECUREBOOT', language: 'cpp')
+endif
+configure_file(output: 'configuration.h', configuration: conf_data)
+
 boost_args = [
     '-DBOOST_ALL_NO_LIB',
     '-DBOOST_ASIO_DISABLE_THREADS',
@@ -65,6 +71,7 @@
     'src/manager.cpp',
     'src/manager_serialize.cpp',
     'src/password.cpp',
+    'src/secureboot.cpp',
 ]
 
 executable(
diff --git a/meson.options b/meson.options
new file mode 100644
index 0000000..b37d923
--- /dev/null
+++ b/meson.options
@@ -0,0 +1,6 @@
+option(
+    'enable-bios-secureboot',
+    type: 'feature',
+    value: 'disabled',
+    description: 'Enable BIOS SecureBoot configuration. The UEFI Secureboot information is obtained via Redfish Host Interface',
+)
diff --git a/src/main.cpp b/src/main.cpp
index df1f9e6..af48808 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -13,10 +13,12 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 */
+#include "configuration.h"
 
 #include "config.hpp"
 #include "manager.hpp"
 #include "password.hpp"
+#include "secureboot.hpp"
 
 #include <boost/asio.hpp>
 #include <phosphor-logging/elog-errors.hpp>
@@ -59,6 +61,18 @@
      */
     bios_config_pwd::Password password(objectServer, systemBus, persistPath);
 
+#ifdef ENABLE_BIOS_SECUREBOOT
+    /**
+     * SecureBoot class is responsible for handling methods and signals under
+     * the following object path and interface.
+     *
+     * Object path : /xyz/openbmc_project/bios_config/secure_boot
+     * Interface : xyz.openbmc_project.BIOSConfig.SecureBoot
+     */
+    bios_config_sec::SecureBoot secureboot(objectServer, systemBus,
+                                           persistPath);
+#endif
+
     io.run();
     return 0;
 }
diff --git a/src/secureboot.cpp b/src/secureboot.cpp
new file mode 100644
index 0000000..36dc875
--- /dev/null
+++ b/src/secureboot.cpp
@@ -0,0 +1,84 @@
+#include "secureboot.hpp"
+
+#include <cereal/archives/binary.hpp>
+
+#include <fstream>
+
+// Register class version with Cereal
+CEREAL_CLASS_VERSION(bios_config::SecureBoot, 0)
+
+namespace bios_config
+{
+
+SecureBoot::SecureBoot(sdbusplus::asio::object_server& objectServer,
+                       std::shared_ptr<sdbusplus::asio::connection>& systemBus,
+                       std::string persistPath) :
+    sdbusplus::xyz::openbmc_project::BIOSConfig::server::SecureBoot(
+        *systemBus, secureBootObjectPath),
+    objServer(objectServer), systemBus(systemBus)
+{
+    fs::path secureBootDir(persistPath);
+    fs::create_directories(secureBootDir);
+    secureBootFile = secureBootDir / secureBootPersistFile;
+    deserialize();
+}
+
+SecureBootBase::CurrentBootType SecureBoot::currentBoot(
+    SecureBootBase::CurrentBootType value)
+{
+    auto ret = SecureBootBase::currentBoot(value);
+    serialize();
+    return ret;
+}
+
+bool SecureBoot::pendingEnable(bool value)
+{
+    auto ret = SecureBootBase::pendingEnable(value);
+    serialize();
+    return ret;
+}
+
+SecureBootBase::ModeType SecureBoot::mode(SecureBootBase::ModeType value)
+{
+    auto ret = SecureBootBase::mode(value);
+    serialize();
+    return ret;
+}
+
+void SecureBoot::serialize()
+{
+    try
+    {
+        std::filesystem::create_directories(secureBootFile.parent_path());
+        std::ofstream os(secureBootFile.c_str(),
+                         std::ios::out | std::ios::binary);
+        cereal::BinaryOutputArchive oarchive(os);
+        oarchive(*this);
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error("Failed to serialize SecureBoot: {ERROR}", "ERROR", e);
+    }
+}
+
+bool SecureBoot::deserialize()
+{
+    try
+    {
+        if (std::filesystem::exists(secureBootFile))
+        {
+            std::ifstream is(secureBootFile.c_str(),
+                             std::ios::in | std::ios::binary);
+            cereal::BinaryInputArchive iarchive(is);
+            iarchive(*this);
+            return true;
+        }
+        return false;
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error("Failed to deserialize SecureBoot: {ERROR}", "ERROR", e);
+        return false;
+    }
+}
+} // namespace bios_config