1 #include "tpm2.hpp"
2
3 #include "common/include/utils.hpp"
4
5 #include <phosphor-logging/lg2.hpp>
6
7 #include <cstdio>
8 #include <regex>
9 #include <sstream>
10
11 PHOSPHOR_LOG2_USING;
12
13 static constexpr std::string_view getCapPropertiesCmd =
14 "/usr/bin/tpm2_getcap properties-fixed";
15 static constexpr std::string_view fwVer1Property = "TPM2_PT_FIRMWARE_VERSION_1";
16 static constexpr std::string_view fwVer2Property = "TPM2_PT_FIRMWARE_VERSION_2";
17 static constexpr std::string_view manufacturerProperty = "TPM2_PT_MANUFACTURER";
18
19 static constexpr std::string_view hexPattern = R"(^\s*raw:\s+0x([0-9a-fA-F]+))";
20
21 enum class Tpm2Vendor
22 {
23 IFX,
24 Nuvoton
25 };
26
27 // Reference: https://trustedcomputinggroup.org/resource/vendor-id-registry/
28 static const std::unordered_map<uint32_t, Tpm2Vendor> validManufactureIDs = {
29 {0x49465800, Tpm2Vendor::IFX}, {0x4E544300, Tpm2Vendor::Nuvoton}};
30
getTPMResourceManagerPath(uint8_t tpmIndex)31 static std::string getTPMResourceManagerPath(uint8_t tpmIndex)
32 {
33 return "/dev/tpmrm" + std::to_string(tpmIndex);
34 }
35
getProperty(std::string_view property,uint32_t & value)36 sdbusplus::async::task<bool> TPM2Interface::getProperty(
37 std::string_view property, uint32_t& value)
38 {
39 // Reference: https://tpm2-tools.readthedocs.io/en/latest/man/common/tcti/
40 // The TCTI or "Transmission Interface" is the communication mechanism
41 // with the TPM. TCTIs can be changed for communication with TPMs across
42 // different mediums.
43 auto tcti = "device:" + getTPMResourceManagerPath(tpmIndex);
44 auto cmd = std::string(getCapPropertiesCmd) + " --tcti " + tcti +
45 " | grep -A1 " + std::string(property);
46
47 std::string output;
48 if (!co_await asyncSystem(ctx, cmd, output))
49 {
50 error("Failed to run command: {CMD}", "CMD", cmd);
51 co_return false;
52 }
53
54 const std::regex regexPattern{std::string(hexPattern)};
55 std::smatch match;
56 std::istringstream stream(output);
57 std::string line;
58
59 while (std::getline(stream, line))
60 {
61 if (std::regex_search(line, match, regexPattern) && match.size() >= 2)
62 {
63 try
64 {
65 value = std::stoul(match[1].str(), nullptr, 16);
66 co_return true;
67 }
68 catch (const std::exception& e)
69 {
70 error("Failed to parse hex value for property {PT}: {ERR}",
71 "PT", property, "ERR", e.what());
72 co_return false;
73 }
74 }
75 }
76
77 error("No matching hex value found for property: {PT}", "PT", property);
78 co_return false;
79 }
80
updateFirmware(const uint8_t * image,size_t image_size)81 sdbusplus::async::task<bool> TPM2Interface::updateFirmware(const uint8_t* image,
82 size_t image_size)
83 {
84 (void)image;
85 (void)image_size;
86
87 error("TPM2 firmware update is not supported");
88 co_return false;
89 }
90
getVersion(std::string & version)91 sdbusplus::async::task<bool> TPM2Interface::getVersion(std::string& version)
92 {
93 uint32_t manufacturerId = 0;
94 uint32_t fwVer = 0;
95 std::string tpmVer1;
96 std::string tpmVer2;
97
98 if (!co_await getProperty(manufacturerProperty, manufacturerId))
99 {
100 error("Failed to retrieve TPM manufacturer ID");
101 co_return false;
102 }
103
104 auto it = validManufactureIDs.find(manufacturerId);
105
106 if (it == validManufactureIDs.end())
107 {
108 error("Invalid TPM manufacturer ID: {ID}", "ID", lg2::hex,
109 manufacturerId);
110 co_return false;
111 }
112
113 auto vendor = it->second;
114
115 if (!co_await getProperty(fwVer1Property, fwVer))
116 {
117 error("Failed to retrieve TPM firmware version 1");
118 co_return false;
119 }
120
121 tpmVer1 = std::to_string(fwVer >> 16) + "." +
122 std::to_string(fwVer & 0xFFFF);
123
124 if (vendor == Tpm2Vendor::Nuvoton)
125 {
126 if (!co_await getProperty(fwVer2Property, fwVer))
127 {
128 error("Failed to retrieve TPM firmware version 2");
129 co_return false;
130 }
131
132 tpmVer2 = std::to_string(fwVer >> 16) + "." +
133 std::to_string(fwVer & 0xFFFF);
134 version = tpmVer1 + "." + tpmVer2;
135 }
136 else
137 {
138 version = tpmVer1;
139 }
140
141 co_return true;
142 }
143