TPM code updater
This commit introduces a TPM code updater that currently supports
reading the firmware version for both Infineon and Nuvoton TPM 2.0.
Support for firmware updates will be introduced in a future patch.
The updater's configuration are managed by the EM [1].
[1] https://gerrit.openbmc.org/c/openbmc/entity-manager/+/82416
Tested on Yosemite5 with the following steps:
1. Display the fw inventory:
```
curl --silent $creds https://$bmc/redfish/v1/UpdateService/FirmwareInventory
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory",
"@odata.type": "#SoftwareInventoryCollection.SoftwareInventoryCollection",
"Members": [
{...},
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Yosemite5_TPM_4945"
},
{...}
],
"Members@odata.count": 4,
"Name": "Software Inventory Collection"
}
```
2. Query TPM version:
```
curl --silent $creds https://$bmc/redfish/v1/UpdateService/FirmwareInventory/Yosemite5_TPM_4945
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Yosemite5_TPM_4945",
"@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
"Description": "Unknown image",
"Id": "Yosemite5_TPM_4945",
"Name": "Software Inventory",
"Status": {
"Health": "Warning",
"HealthRollup": "OK",
"State": "Disabled"
},
"Updateable": false,
"Version": "15.23"
}
```
Change-Id: I42568242356d55fe005ba1f41ddf8aaf9f682fc8
Signed-off-by: Kevin Tung <kevin.tung.openbmc@gmail.com>
diff --git a/tpm/tpm2/tpm2.cpp b/tpm/tpm2/tpm2.cpp
new file mode 100644
index 0000000..bd3ca86
--- /dev/null
+++ b/tpm/tpm2/tpm2.cpp
@@ -0,0 +1,142 @@
+#include "tpm2.hpp"
+
+#include "common/include/utils.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <cstdio>
+#include <regex>
+#include <sstream>
+
+PHOSPHOR_LOG2_USING;
+
+static constexpr std::string_view getCapPropertiesCmd =
+ "/usr/bin/tpm2_getcap properties-fixed";
+static constexpr std::string_view fwVer1Property = "TPM2_PT_FIRMWARE_VERSION_1";
+static constexpr std::string_view fwVer2Property = "TPM2_PT_FIRMWARE_VERSION_2";
+static constexpr std::string_view manufacturerProperty = "TPM2_PT_MANUFACTURER";
+
+static constexpr std::string_view hexPattern = R"(^\s*raw:\s+0x([0-9a-fA-F]+))";
+
+enum class Tpm2Vendor
+{
+ IFX,
+ Nuvoton
+};
+
+// Reference: https://trustedcomputinggroup.org/resource/vendor-id-registry/
+static const std::unordered_map<uint32_t, Tpm2Vendor> validManufactureIDs = {
+ {0x49465800, Tpm2Vendor::IFX}, {0x4E544300, Tpm2Vendor::Nuvoton}};
+
+static std::string getTPMResourceManagerPath(uint8_t tpmIndex)
+{
+ return "/dev/tpmrm" + std::to_string(tpmIndex);
+}
+
+sdbusplus::async::task<bool> TPM2Interface::getProperty(
+ std::string_view property, uint32_t& value)
+{
+ // Reference: https://tpm2-tools.readthedocs.io/en/latest/man/common/tcti/
+ // The TCTI or "Transmission Interface" is the communication mechanism
+ // with the TPM. TCTIs can be changed for communication with TPMs across
+ // different mediums.
+ auto tcti = "device:" + getTPMResourceManagerPath(tpmIndex);
+ auto cmd = std::string(getCapPropertiesCmd) + " --tcti " + tcti +
+ " | grep -A1 " + std::string(property);
+
+ std::string output;
+ if (!co_await asyncSystem(ctx, cmd, output))
+ {
+ error("Failed to run command: {CMD}", "CMD", cmd);
+ co_return false;
+ }
+
+ const std::regex regexPattern{std::string(hexPattern)};
+ std::smatch match;
+ std::istringstream stream(output);
+ std::string line;
+
+ while (std::getline(stream, line))
+ {
+ if (std::regex_search(line, match, regexPattern) && match.size() >= 2)
+ {
+ try
+ {
+ value = std::stoul(match[1].str(), nullptr, 16);
+ co_return true;
+ }
+ catch (const std::exception& e)
+ {
+ error("Failed to parse hex value for property {PT}: {ERR}",
+ "PT", property, "ERR", e.what());
+ co_return false;
+ }
+ }
+ }
+
+ error("No matching hex value found for property: {PT}", "PT", property);
+ co_return false;
+}
+
+sdbusplus::async::task<bool> TPM2Interface::updateFirmware(const uint8_t* image,
+ size_t image_size)
+{
+ (void)image;
+ (void)image_size;
+
+ error("TPM2 firmware update is not supported");
+ co_return false;
+}
+
+sdbusplus::async::task<bool> TPM2Interface::getVersion(std::string& version)
+{
+ uint32_t manufacturerId = 0;
+ uint32_t fwVer = 0;
+ std::string tpmVer1;
+ std::string tpmVer2;
+
+ if (!co_await getProperty(manufacturerProperty, manufacturerId))
+ {
+ error("Failed to retrieve TPM manufacturer ID");
+ co_return false;
+ }
+
+ auto it = validManufactureIDs.find(manufacturerId);
+
+ if (it == validManufactureIDs.end())
+ {
+ error("Invalid TPM manufacturer ID: {ID}", "ID", lg2::hex,
+ manufacturerId);
+ co_return false;
+ }
+
+ auto vendor = it->second;
+
+ if (!co_await getProperty(fwVer1Property, fwVer))
+ {
+ error("Failed to retrieve TPM firmware version 1");
+ co_return false;
+ }
+
+ tpmVer1 = std::to_string(fwVer >> 16) + "." +
+ std::to_string(fwVer & 0xFFFF);
+
+ if (vendor == Tpm2Vendor::Nuvoton)
+ {
+ if (!co_await getProperty(fwVer2Property, fwVer))
+ {
+ error("Failed to retrieve TPM firmware version 2");
+ co_return false;
+ }
+
+ tpmVer2 = std::to_string(fwVer >> 16) + "." +
+ std::to_string(fwVer & 0xFFFF);
+ version = tpmVer1 + "." + tpmVer2;
+ }
+ else
+ {
+ version = tpmVer1;
+ }
+
+ co_return true;
+}