1 //
2 // Copyright (C) 2024 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include <iterator>
17 #include <memory>
18 #include <unordered_set>
19 #include <vector>
20
21 #include <android-base/logging.h>
22 #include <fruit/fruit.h>
23 #include <json/json.h>
24 #include <string.h>
25 #include <sys/socket.h>
26
27 #include "common/libs/fs/shared_buf.h"
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/result.h"
31 #include "common/libs/utils/socket2socket_proxy.h"
32 #include "host/commands/run_cvd/launch/launch.h"
33 #include "host/commands/run_cvd/launch/log_tee_creator.h"
34 #include "host/libs/config/command_source.h"
35 #include "host/libs/config/cuttlefish_config.h"
36
37 namespace cuttlefish {
38 namespace {
39
40 const std::string kControlSocketName = "control_sock";
41 class Ti50Emulator : public vm_manager::VmmDependencyCommand {
42 public:
INJECT(Ti50Emulator (const CuttlefishConfig::InstanceSpecific & instance,LogTeeCreator & log_tee))43 INJECT(Ti50Emulator(const CuttlefishConfig::InstanceSpecific& instance,
44 LogTeeCreator& log_tee))
45 : instance_(instance), log_tee_(log_tee) {}
46
47 // CommandSource
Commands()48 Result<std::vector<MonitorCommand>> Commands() override {
49 if (!Enabled()) {
50 LOG(ERROR) << "ti50 emulator is not enabled";
51 return {};
52 }
53
54 Command command(instance_.ti50_emulator());
55 command.AddParameter("-s");
56 command.AddParameter("--control_socket=",
57 instance_.PerInstancePath(kControlSocketName));
58 command.AddParameter("-p=", instance_.instance_dir());
59
60 std::vector<MonitorCommand> commands;
61 commands.emplace_back(
62 CF_EXPECT(log_tee_.CreateFullLogTee(command, "ti50")));
63 commands.emplace_back(std::move(command));
64 return commands;
65 }
66
67 // SetupFeature
Name() const68 std::string Name() const override { return "Ti50Emulator"; }
Enabled() const69 bool Enabled() const override { return !instance_.ti50_emulator().empty(); }
70
71 // StatusCheckCommandSource
WaitForAvailability() const72 Result<void> WaitForAvailability() const {
73 if (!Enabled()) {
74 return {};
75 }
76
77 // Wait for control socket sending "READY".
78 SharedFD sock = SharedFD::Accept(*ctrl_sock_);
79 const char kExpectedReadyStr[] = "READY";
80 char buf[std::size(kExpectedReadyStr)];
81 CF_EXPECT_NE(sock->Read(buf, sizeof(buf)), 0);
82 CF_EXPECT(!strcmp(buf, "READY"), "Ti50 emulator should return 'READY'");
83
84 CF_EXPECT(ResetGPIO());
85
86 // Initialize TPM socket
87 CF_EXPECT(InitializeTpm());
88
89 return {};
90 }
91
92 private:
Dependencies() const93 std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
94
ResultSetup()95 Result<void> ResultSetup() override {
96 // Socket proxy
97 ctrl_sock_ = SharedFD::SocketLocalServer(
98 instance_.PerInstancePath(kControlSocketName), false, SOCK_STREAM,
99 0777);
100 if (!ctrl_sock_->IsOpen()) {
101 LOG(ERROR) << "Unable to create unix ctrl_sock server: "
102 << ctrl_sock_->StrError();
103 }
104
105 return {};
106 }
107
ResetGPIO() const108 Result<void> ResetGPIO() const {
109 // Write '1' to 'gpioPltRst' to initialize the emulator.
110 std::string gpio_sock = instance_.PerInstancePath("gpioPltRst");
111 CF_EXPECT(WaitForUnixSocket(gpio_sock, 30));
112
113 // Wait for the emulator's internal state to be initialized.
114 // Since the emulator polls the socket at 100 ms intervals before
115 // initializing , 1 second sleep after the socket being ready should be a
116 // sufficiently long.
117 // https://crrev.com/7447dbd20aee11809e89e04bb2fcb2a1476febe1/tpm2-simulator/tpm_executor_ti50_impl.cc#171
118 sleep(1);
119
120 SharedFD cl = SharedFD::SocketLocalClient(gpio_sock, false, SOCK_STREAM);
121 if (!cl->IsOpen()) {
122 return CF_ERR("Failed to connect to gpioPltRst");
123 }
124 CF_EXPECT_EQ(cl->Write("1", 1), 1);
125
126 LOG(INFO) << "ti50 emulator: reset GPIO!";
127 return {};
128 }
129
InitializeTpm() const130 Result<void> InitializeTpm() const {
131 // Connects to direct_tpm_fifo socket, which is a bi-directional Unix domain
132 // socket.
133 std::string fifo_sock = instance_.PerInstancePath("direct_tpm_fifo");
134 CF_EXPECT(WaitForUnixSocket(fifo_sock, 30));
135
136 auto cl = SharedFD::SocketLocalClient(fifo_sock, false, SOCK_STREAM);
137 if (!cl->IsOpen()) {
138 return CF_ERR("Failed to connect to gpioPltRst");
139 }
140
141 const uint32_t kMaxRetryCount = 5;
142 // TPM2_Startup command with SU_CLEAR
143 const uint8_t kTpm2StartupCmd[] = {0x80, 0x01, 0x00, 0x00, 0x00, 0x0c,
144 0x00, 0x00, 0x01, 0x44, 0x00, 0x00};
145 ssize_t cmd_size = sizeof(kTpm2StartupCmd);
146 const uint8_t kExpectedResponse[] = {0x80, 0x01, 0x00, 0x00, 0x00,
147 0x0a, 0x00, 0x00, 0x00, 0x00};
148 ssize_t expected_response_size = sizeof(kExpectedResponse);
149 for (int i = 0; i < kMaxRetryCount; i++) {
150 CF_EXPECT_EQ(WriteAll(cl, (char*)kTpm2StartupCmd, cmd_size), cmd_size,
151 "failed to write TPM2_startup command");
152
153 // Read a response.
154 // First, read a 2-byte tag and 4-byte size.
155 constexpr ssize_t kHeaderSize = 6;
156 uint8_t resp_header[kHeaderSize] = {0};
157 CF_EXPECT_EQ(ReadExact(cl, (char*)resp_header, kHeaderSize), kHeaderSize,
158 "failed to read TPM2_startup response header");
159 uint8_t resp_size[4] = {resp_header[5], resp_header[4], resp_header[3],
160 resp_header[2]};
161 uint32_t* response_size = reinterpret_cast<uint32_t*>(&resp_size);
162
163 // Then, read the response body.
164 uint32_t body_size = *response_size - kHeaderSize;
165 std::vector<char> resp_body(body_size);
166 CF_EXPECT_EQ(ReadExact(cl, &resp_body), body_size,
167 "failed to read TPM2_startup response body");
168
169 // Check if the response is the expected one.
170 if (*response_size != expected_response_size) {
171 LOG(INFO) << "TPM response size mismatch. Try again: " << *response_size
172 << " != " << expected_response_size;
173 sleep(1);
174 continue;
175 }
176
177 bool ok = true;
178 for (int i = 0; i < expected_response_size - kHeaderSize; i++) {
179 ok &= (resp_body.at(i) == kExpectedResponse[kHeaderSize + i]);
180 }
181 if (!ok) {
182 LOG(INFO) << "TPM response body mismatch. Try again.";
183 sleep(1);
184 continue;
185 }
186
187 LOG(INFO) << "TPM initialized successfully for Ti50";
188 return {};
189 }
190
191 return CF_ERR("Failed to initialize Ti50 emulator");
192 }
193
194 const CuttlefishConfig::InstanceSpecific& instance_;
195 LogTeeCreator& log_tee_;
196
197 std::unique_ptr<ProxyServer> socket_proxy_;
198
199 SharedFD ctrl_sock_;
200 SharedFD gpio_sock_;
201 };
202 } // namespace
203
204 fruit::Component<fruit::Required<const CuttlefishConfig, LogTeeCreator,
205 const CuttlefishConfig::InstanceSpecific>>
Ti50EmulatorComponent()206 Ti50EmulatorComponent() {
207 return fruit::createComponent()
208 .addMultibinding<vm_manager::VmmDependencyCommand, Ti50Emulator>()
209 .addMultibinding<CommandSource, Ti50Emulator>()
210 .addMultibinding<SetupFeature, Ti50Emulator>();
211 }
212
213 } // namespace cuttlefish
214