Hothd: Add support for querying authz record.

TESTED=gpaste/5916622332887040

Google-Bug-Id: 377507258
Change-Id: I54bb5a2f2f72e829cd742b786ac6dd1aaf79f3e9
Signed-off-by: Christian Kungler <ckungler@google.com>
diff --git a/ec_util.cpp b/ec_util.cpp
index 05bd01d..5098473 100644
--- a/ec_util.cpp
+++ b/ec_util.cpp
@@ -153,6 +153,20 @@
     return matchPersistentPanicMagic(response_body);
 }
 
+ec_authz_record_get_response EcUtilImpl::getHothAuthRecord() const {
+    ec_authz_record_get_request request = {
+        .index = 0,
+        .reserved = {},
+    };
+
+    std::vector<uint8_t> response = hostCmd->sendCommand(
+        EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_AUTHZ_RECORD,
+        kVersionZero, &request, sizeof(request));
+    std::span<const uint8_t> response_body = getResponseBody(response);
+
+    return stdplus::raw::copyFrom<ec_authz_record_get_response>(response_body);
+}
+
 } // namespace internal
 
 } // namespace hoth
diff --git a/ec_util.hpp b/ec_util.hpp
index 02cf6d5..b4b3061 100644
--- a/ec_util.hpp
+++ b/ec_util.hpp
@@ -45,6 +45,10 @@
     // EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO command.
     bool checkHothPersistentPanicInfo() const override;
 
+    // Get the Hoth auth record.
+    // Only supports record id = 0, which is the only one currently used.
+    ec_authz_record_get_response getHothAuthRecord() const override;
+
   private:
     [[nodiscard]] static std::span<const uint8_t>
     getResponseBody(std::vector<uint8_t> &response);
diff --git a/ec_util_interface.hpp b/ec_util_interface.hpp
index 3662c56..0cd9dd6 100644
--- a/ec_util_interface.hpp
+++ b/ec_util_interface.hpp
@@ -39,6 +39,8 @@
     // Check the presence of Hoth peresistent panic info by issuing a single
     // EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO command.
     virtual bool checkHothPersistentPanicInfo() const = 0;
+
+    virtual ec_authz_record_get_response getHothAuthRecord() const = 0;
 };
 
 } // namespace internal
diff --git a/google3/host_commands.h b/google3/host_commands.h
index 15f1c1e..11f952a 100644
--- a/google3/host_commands.h
+++ b/google3/host_commands.h
@@ -566,4 +566,48 @@
     // followed by the requested bytes.
 } __attribute__((aligned(4)));
 
+#define EC_PRV_CMD_HOTH_GET_AUTHZ_RECORD 0x0018
+
+struct ec_authz_record_get_request {
+  // Authorization record index to get. Currently only index=0 is
+  // supported.
+  uint8_t index;
+  uint8_t reserved[3];
+} __attribute__((packed));
+
+
+#define AUTHORIZATION_RECORD_MAGIC_SIZE 8
+#define AUTHORIZATION_RECORD_SIGNATURE_SIZE (96 * 4)
+#define AUTHORIZATION_RECORD_FAUX_FUSES_SIZE 4
+#define AUTHORIZATION_RECORD_NONCE_SIZE 32
+
+// All multi-byte fields are little endian.
+struct authorization_record {
+  uint8_t magic[AUTHORIZATION_RECORD_MAGIC_SIZE];
+  little_uint32_t signature[AUTHORIZATION_RECORD_SIGNATURE_SIZE / sizeof(uint32_t)];
+  little_uint32_t version;
+  little_uint32_t reserved_0;
+  little_uint32_t size;
+  little_uint32_t key_id;
+  little_uint32_t flags;
+  uint8_t faux_fuses[AUTHORIZATION_RECORD_FAUX_FUSES_SIZE];
+  little_uint64_t capabilities;
+  little_uint32_t dev_id_0;
+  little_uint32_t dev_id_1;
+  little_uint32_t
+      authorization_nonce[AUTHORIZATION_RECORD_NONCE_SIZE / sizeof(uint32_t)];
+} __attribute__((aligned(4)));
+
+struct ec_authz_record_get_response {
+  // Index of authorization record in the response. This value matches the
+  // `index` in the corresponding host command request.
+  uint8_t index;
+
+  // When `valid` is non-zero value, the `record` at `index` in this
+  // response is valid.
+  uint8_t valid;
+  uint8_t reserved[2];
+  struct authorization_record record;
+} __attribute__((aligned(4)));
+
 #endif /* __PRIVATE_CR51_INCLUDE_CR51_HOST_COMMANDS_H */
diff --git a/hoth_state.cpp b/hoth_state.cpp
index aead187..b63d77c 100644
--- a/hoth_state.cpp
+++ b/hoth_state.cpp
@@ -60,56 +60,86 @@
 
 void HothState::updateMetrics()
 {
-    statistics = ecUtil->getHothStatistics();
+    try
+    {
+        statistics = ecUtil->getHothStatistics();
 
-    // Emit the signal that property has changed.
-    // Fall back to default values if response does not contain enought words.
-    HothStateObject::resetFlags(
-        statisticsReponseHasEnoughWords(statistics, statistics.hoth_reset_flags)
-            ? (uint32_t)statistics.hoth_reset_flags
-            : 0);
-    HothStateObject::upTime(statisticsReponseHasEnoughWords(
-                                statistics, statistics.time_since_hoth_boot_us)
-                                ? (uint64_t)statistics.time_since_hoth_boot_us
-                                : 0);
-    HothStateObject::roInfoStrikes(
-        statisticsReponseHasEnoughWords(statistics, statistics.ro_info_strikes)
-            ? (uint32_t)statistics.ro_info_strikes
-            : 0xffffffff);
-    HothStateObject::rwInfoStrikes(
-        statisticsReponseHasEnoughWords(statistics, statistics.rw_info_strikes)
-            ? (uint32_t)statistics.rw_info_strikes
-            : 0xffffffff);
-    HothStateObject::payloadUpdateFailureCode(
-        statisticsReponseHasEnoughWords(
-            statistics, statistics.payload_update_failure_reason)
-            ? (uint16_t)statistics.payload_update_failure_reason
-            : 0xffff);
-    HothStateObject::payloadConfirmFailureCode(
-        statisticsReponseHasEnoughWords(
-            statistics,
-            statistics.payload_update_confirmation_cookie_failure_reason)
-            ? (uint32_t)
-                  statistics.payload_update_confirmation_cookie_failure_reason
-            : 0xffffffff);
-    HothStateObject::firmwareUpdateFailureCode(
-        statisticsReponseHasEnoughWords(
-            statistics, statistics.firmware_update_failure_reason)
-            ? (uint16_t)statistics.firmware_update_failure_reason
-            : 0xffff);
-    HothStateObject::firmwareUpdateFailedMinor(
-        statisticsReponseHasEnoughWords(
-            statistics, statistics.failed_firmware_minor_version)
-            ? (uint32_t)statistics.failed_firmware_minor_version
-            : 0xffffffff);
-    HothStateObject::bootloaderUpdateFailureCode(
-        statisticsReponseHasEnoughWords(statistics,
-                                        statistics.bootloader_update_error)
-            ? (uint32_t)statistics.bootloader_update_error
-            : 0xffffffff);
+        // Emit the signal that property has changed.
+        // Fall back to default values if response does not contain enought words.
+        HothStateObject::resetFlags(
+            statisticsReponseHasEnoughWords(statistics, statistics.hoth_reset_flags)
+                ? (uint32_t)statistics.hoth_reset_flags
+                : 0);
+        HothStateObject::upTime(statisticsReponseHasEnoughWords(
+                                    statistics, statistics.time_since_hoth_boot_us)
+                                    ? (uint64_t)statistics.time_since_hoth_boot_us
+                                    : 0);
+        HothStateObject::roInfoStrikes(
+            statisticsReponseHasEnoughWords(statistics, statistics.ro_info_strikes)
+                ? (uint32_t)statistics.ro_info_strikes
+                : 0xffffffff);
+        HothStateObject::rwInfoStrikes(
+            statisticsReponseHasEnoughWords(statistics, statistics.rw_info_strikes)
+                ? (uint32_t)statistics.rw_info_strikes
+                : 0xffffffff);
+        HothStateObject::payloadUpdateFailureCode(
+            statisticsReponseHasEnoughWords(
+                statistics, statistics.payload_update_failure_reason)
+                ? (uint16_t)statistics.payload_update_failure_reason
+                : 0xffff);
+        HothStateObject::payloadConfirmFailureCode(
+            statisticsReponseHasEnoughWords(
+                statistics,
+                statistics.payload_update_confirmation_cookie_failure_reason)
+                ? (uint32_t)
+                    statistics.payload_update_confirmation_cookie_failure_reason
+                : 0xffffffff);
+        HothStateObject::firmwareUpdateFailureCode(
+            statisticsReponseHasEnoughWords(
+                statistics, statistics.firmware_update_failure_reason)
+                ? (uint16_t)statistics.firmware_update_failure_reason
+                : 0xffff);
+        HothStateObject::firmwareUpdateFailedMinor(
+            statisticsReponseHasEnoughWords(
+                statistics, statistics.failed_firmware_minor_version)
+                ? (uint32_t)statistics.failed_firmware_minor_version
+                : 0xffffffff);
+        HothStateObject::bootloaderUpdateFailureCode(
+            statisticsReponseHasEnoughWords(statistics,
+                                            statistics.bootloader_update_error)
+                ? (uint32_t)statistics.bootloader_update_error
+                : 0xffffffff);
+    }
+    catch (const std::exception& e)
+    {
+        stdplus::print(stderr, "Fetching Hoth statistics had an exception: {}\n",
+                       e.what());
+    }
 
-    HothStateObject::hasPersistentPanicInfo(
-        ecUtil->checkHothPersistentPanicInfo());
+    try
+    {
+        HothStateObject::hasPersistentPanicInfo(
+            ecUtil->checkHothPersistentPanicInfo());
+    }
+    catch (const std::exception& e)
+    {
+        stdplus::print(stderr, "Fetching Hoth panic info had an exception: {}\n",
+                       e.what());
+    }
+
+    try
+    {
+        ec_authz_record_get_response auth_record_resp = ecUtil->getHothAuthRecord();
+        HothStateObject::hasValidAuthRecord(auth_record_resp.valid == 1);
+        if (auth_record_resp.valid == 1) {
+            HothStateObject::authRecordCapabilities(auth_record_resp.record.capabilities);
+        }
+    }
+    catch (const std::exception& e)
+    {
+        stdplus::print(stderr, "Fetching Hoth auth record had an exception: {}\n",
+                       e.what());
+    }
 }
 
 void HothState::timerUpdateMetrics()
diff --git a/test/ec_util_mock.hpp b/test/ec_util_mock.hpp
index 0f689cf..21d0e63 100644
--- a/test/ec_util_mock.hpp
+++ b/test/ec_util_mock.hpp
@@ -32,6 +32,7 @@
   public:
     MOCK_CONST_METHOD0(getHothStatistics, ec_response_statistics());
     MOCK_CONST_METHOD0(checkHothPersistentPanicInfo, bool());
+    MOCK_CONST_METHOD0(getHothAuthRecord, ec_authz_record_get_response());
 };
 
 } // namespace internal
diff --git a/test/hoth_state_unittest.cpp b/test/hoth_state_unittest.cpp
index 07ade6b..d803d75 100644
--- a/test/hoth_state_unittest.cpp
+++ b/test/hoth_state_unittest.cpp
@@ -100,6 +100,26 @@
     EXPECT_FALSE(hoth_state->hasPersistentPanicInfo());
 }
 
+TEST_F(HothStateTest, ValidAuthRecord)
+{
+    ec_authz_record_get_response response = {};
+    response.valid = 1;
+    response.record.capabilities = 0x0102030405060708;
+    EXPECT_CALL(ecUtil, getHothAuthRecord()).WillOnce(Return(response));
+    EXPECT_NO_THROW(hoth_state->updateMetrics());
+    EXPECT_TRUE(hoth_state->hasValidAuthRecord());
+    EXPECT_EQ(hoth_state->authRecordCapabilities(), 0x0102030405060708);
+}
+
+TEST_F(HothStateTest, InvalidAuthRecord)
+{
+    ec_authz_record_get_response response = {};
+    response.valid = 0;
+    EXPECT_CALL(ecUtil, getHothAuthRecord()).WillOnce(Return(response));
+    EXPECT_NO_THROW(hoth_state->updateMetrics());
+    EXPECT_FALSE(hoth_state->hasValidAuthRecord());
+}
+
 ec_response_statistics getMockStatistics()
 {
     ec_response_statistics ret = {
diff --git a/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml b/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml
index 6da82ae..f3cc258 100644
--- a/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml
+++ b/yaml/xyz/openbmc_project/Control/Hoth/State.interface.yaml
@@ -113,3 +113,21 @@
           - readonly
       description: >
           Whether the Hoth device has a persistent panic record stored.
+
+    - name: HasValidAuthRecord
+      type: boolean
+      default: false
+      flags:
+          - readonly
+      description: >
+          Whether the Hoth device has a valid auth record.
+
+    - name: AuthRecordCapabilities
+      type: uint64
+      default: 0
+      flags:
+          - readonly
+      description: >
+          A bitfield of enabled Auth record capabilities.
+      errors:
+          - self.Error.ExpectedInfoNotFound