Add SecureBoot monitoring

TESTED=gpaste/5349581642268672

Google-Bug-Id: 438259309

Signed-off-by: Christian Kungler <ckungler@google.com>
Change-Id: I24dee723f201b09b13217ab050f2dbd2c6b7fdc8
diff --git a/ec_util.cpp b/ec_util.cpp
index 416dace..60c38de 100644
--- a/ec_util.cpp
+++ b/ec_util.cpp
@@ -189,6 +189,22 @@
   return stdplus::raw::copyFrom<ec_response_key_rotation_status>(response_body);
 }
 
+secure_boot_enforcement_state EcUtilImpl::getSecureBootEnforcementState()
+    const {
+  const uint16_t kGetSecureBootEnforcementState =
+      EC_CMD_BOARD_SPECIFIC_BASE +
+      EC_PRV_CMD_HOTH_GET_SECURE_BOOT_ENFORCEMENT;
+
+  if (!isHostCommandSupported(kGetSecureBootEnforcementState)) {
+    throw CommandNotSupportedException(kGetSecureBootEnforcementState);
+  }
+
+  std::vector<uint8_t> response_body =
+      sendCommand(kGetSecureBootEnforcementState,
+                  /*request_ptr=*/nullptr, /*request_size=*/0);
+  return stdplus::raw::copyFrom<secure_boot_enforcement_state>(response_body);
+}
+
 }  // namespace internal
 
 }  // namespace hoth
diff --git a/ec_util.hpp b/ec_util.hpp
index 5b8f25d..fb8e9c0 100644
--- a/ec_util.hpp
+++ b/ec_util.hpp
@@ -91,6 +91,8 @@
 
   ec_response_key_rotation_status getHothKeyRotationStatus() const override;
 
+  secure_boot_enforcement_state getSecureBootEnforcementState() const override;
+
  private:
   [[nodiscard]] std::vector<uint8_t> sendCommand(uint16_t command,
                                                  uint8_t* request_ptr,
diff --git a/ec_util_interface.hpp b/ec_util_interface.hpp
index cb12818..b33beb2 100644
--- a/ec_util_interface.hpp
+++ b/ec_util_interface.hpp
@@ -52,6 +52,10 @@
 
     // Get Hoth KeyRotation metrics
     virtual ec_response_key_rotation_status getHothKeyRotationStatus() const = 0;
+
+    // Get Hoth Secure Boot Enforcement status
+    virtual secure_boot_enforcement_state getSecureBootEnforcementState()
+        const = 0;
 };
 
 } // namespace internal
diff --git a/google3/host_commands.h b/google3/host_commands.h
index 4c0cbc2..d327c8b 100644
--- a/google3/host_commands.h
+++ b/google3/host_commands.h
@@ -691,4 +691,16 @@
                                   // of cr51 hash
 } __attribute__((packed, aligned(4)));
 
+
+// The return value for EC_PRV_CMD_HOTH_GET_SECURE_BOOT_ENFORCEMENT. This
+// struct reflects the current state of secure boot enforcement.
+struct secure_boot_enforcement_state {
+  uint8_t enabled;       // 0 = disable(d), 1 = enable(d)
+  uint8_t reserved0[3];  // Reserved. Write zeroes.
+} __attribute__((packed));
+
+// EC_PRV_CMD_HOTH_GET_SECURE_BOOT_ENFORCEMENT queries the current secure boot
+// enforcement state.
+#define EC_PRV_CMD_HOTH_GET_SECURE_BOOT_ENFORCEMENT 0x001D
+
 #endif /* __PRIVATE_CR51_INCLUDE_CR51_HOST_COMMANDS_H */
diff --git a/hoth_state.cpp b/hoth_state.cpp
index ff6b3ef..3cc178f 100644
--- a/hoth_state.cpp
+++ b/hoth_state.cpp
@@ -183,6 +183,20 @@
     stdplus::print(stderr, "Fetching Hoth key rotation had an exception: {}\n",
                    e.what());
   }
+
+  try {
+    secure_boot_enforcement_state secure_boot_state =
+        ecUtil->getSecureBootEnforcementState();
+    HothStateObject::secureBootEnforced(secure_boot_state.enabled != 0);
+  } catch (const internal::CommandNotSupportedException &e) {
+    // Do not log if unsupported
+    HothStateObject::secureBootEnforced(false);
+  } catch (const std::exception &e) {
+    HothStateObject::secureBootEnforced(false);
+    stdplus::print(
+        stderr, "Fetching Hoth secure boot enforcement had an exception: {}\n",
+        e.what());
+  }
 }
 
 void HothState::timerUpdateMetrics() {
diff --git a/test/ec_util_mock.hpp b/test/ec_util_mock.hpp
index a756057..23dc1c5 100644
--- a/test/ec_util_mock.hpp
+++ b/test/ec_util_mock.hpp
@@ -34,6 +34,8 @@
   MOCK_CONST_METHOD0(getHothKeyRotationStatus,
                      ec_response_key_rotation_status());
   MOCK_CONST_METHOD1(isHostCommandSupported, bool(uint16_t));
+  MOCK_CONST_METHOD0(getSecureBootEnforcementState,
+                     secure_boot_enforcement_state());
 };
 
 }  // namespace internal
diff --git a/test/ec_util_unittest.cpp b/test/ec_util_unittest.cpp
index 575b785..db0d6dd 100644
--- a/test/ec_util_unittest.cpp
+++ b/test/ec_util_unittest.cpp
@@ -218,6 +218,70 @@
   EXPECT_THROW(ecUtil.getHothKeyRotationStatus(), CommandNotSupportedException);
 }
 
+class EcUtilSecureBootEnforcementTest : public EcUtilTest {};
+
+TEST_F(EcUtilSecureBootEnforcementTest, commandNotSupported) {
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  rsp.push_back(0);
+  EXPECT_CALL(hostCmd,
+              sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                              EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED,
+                          _, _, _))
+      .WillOnce(Return(rsp));
+  EXPECT_THROW(ecUtil.getSecureBootEnforcementState(),
+               CommandNotSupportedException);
+}
+
+TEST_F(EcUtilSecureBootEnforcementTest, sendCommandReturnsBadResultFails) {
+  std::vector<uint8_t> rsp_supported(goodResponseStr.begin(),
+                                     goodResponseStr.end());
+  rsp_supported.push_back(1);
+  EXPECT_CALL(hostCmd,
+              sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                              EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED,
+                          _, _, _))
+      .WillOnce(Return(rsp_supported));
+
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  // Change the RspHeader.result to something other than EC_RES_SUCCESS
+  rsp[2] = internal::EC_RES_ERROR;
+
+  EXPECT_CALL(
+      hostCmd,
+      sendCommand(
+          EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_SECURE_BOOT_ENFORCEMENT,
+          ecUtil.kVersionZero, nullptr, 0))
+      .WillOnce(Return(rsp));
+
+  EXPECT_THROW(ecUtil.getSecureBootEnforcementState(), CommandRunException);
+}
+
+TEST_F(EcUtilSecureBootEnforcementTest, sendCommandReturnsGoodResponseSuccess) {
+  std::vector<uint8_t> rsp_supported(goodResponseStr.begin(),
+                                     goodResponseStr.end());
+  rsp_supported.push_back(1);
+  EXPECT_CALL(hostCmd,
+              sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                              EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED,
+                          _, _, _))
+      .WillOnce(Return(rsp_supported));
+
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  secure_boot_enforcement_state expected = {.enabled = 1, .reserved0 = {}};
+  const uint8_t* body_ptr = reinterpret_cast<const uint8_t*>(&expected);
+  rsp.insert(rsp.end(), body_ptr, body_ptr + sizeof(expected));
+
+  EXPECT_CALL(
+      hostCmd,
+      sendCommand(
+          EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_SECURE_BOOT_ENFORCEMENT,
+          ecUtil.kVersionZero, nullptr, 0))
+      .WillOnce(Return(rsp));
+
+  secure_boot_enforcement_state actual = ecUtil.getSecureBootEnforcementState();
+  EXPECT_EQ(actual.enabled, expected.enabled);
+}
+
 }  // namespace
 }  // namespace internal
 }  // namespace hoth
diff --git a/test/hoth_state_unittest.cpp b/test/hoth_state_unittest.cpp
index a0c09a4..efc8589 100644
--- a/test/hoth_state_unittest.cpp
+++ b/test/hoth_state_unittest.cpp
@@ -175,6 +175,35 @@
   EXPECT_EQ(hoth_state->keyRotationValidationHashData(), 0x46464646);
 }
 
+TEST_F(HothStateTest, SecureBootEnforced) {
+  secure_boot_enforcement_state response = {};
+  response.enabled = 1;
+  EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
+      .WillOnce(Return(response));
+  EXPECT_NO_THROW(hoth_state->updateMetrics());
+  EXPECT_TRUE(hoth_state->secureBootEnforced());
+
+  response.enabled = 0;
+  EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
+      .WillOnce(Return(response));
+  EXPECT_NO_THROW(hoth_state->updateMetrics());
+  EXPECT_FALSE(hoth_state->secureBootEnforced());
+}
+
+TEST_F(HothStateTest, SecureBootEnforcedUnsupported) {
+  EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
+      .WillOnce(testing::Throw(internal::CommandNotSupportedException(0)));
+  EXPECT_NO_THROW(hoth_state->updateMetrics());
+  EXPECT_FALSE(hoth_state->secureBootEnforced());
+}
+
+TEST_F(HothStateTest, SecureBootEnforcedException) {
+  EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
+      .WillOnce(testing::Throw(std::runtime_error("test")));
+  EXPECT_NO_THROW(hoth_state->updateMetrics());
+  EXPECT_FALSE(hoth_state->secureBootEnforced());
+}
+
 ec_response_statistics getMockStatistics() {
   ec_response_statistics ret = {
       .valid_words = 22,
diff --git a/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml b/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml
index 1ca65c7..6830c46 100644
--- a/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml
+++ b/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml
@@ -252,3 +252,13 @@
           If validation method is hash, first 32 bits of cr51 hash.
       errors:
           - self.Error.ExpectedInfoNotFound
+
+    - name: SecureBootEnforced
+      type: boolean
+      default: false
+      flags:
+          - readonly
+      description: >
+          Whether secure boot is enforced.
+      errors:
+          - self.Error.ExpectedInfoNotFound