• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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