blob: 278ea08bbcf853e980b8befccf18a0c2c61a84ad [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <signal.h>
#include <string>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
// Needs to be included after sys/socket.h
#include <linux/un.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include <base/posix/unix_domain_socket.h>
#include <base/threading/platform_thread.h>
#include <base/time/time.h>
#include <brillo/process/process.h>
#include <linux/vtpm_proxy.h>
#include "tpm2-simulator/constants.h"
#include "tpm2-simulator/tpm_command_utils.h"
#include "tpm2-simulator/tpm_executor_ti50_impl.h"
#include "tpm2-simulator/tpm_vendor_cmd_locality.h"
namespace tpm2_simulator {
namespace {
constexpr base::TimeDelta kPollingInterval = base::Milliseconds(100);
constexpr char kGpioPltRstFile[] = "gpioPltRst";
constexpr char kTpmFifoFile[] = "direct_tpm_fifo";
constexpr char kOne[] = "1";
constexpr char kTi50EmulatorKernel[] = "/usr/bin/ti50-emulator-kernel";
constexpr char const* kTi50EmulatorApps[] = {
"/usr/bin/ti50-emulator-fw_updater", "/usr/bin/ti50-emulator-tpm2",
"/usr/bin/ti50-emulator-sys_mgr", "/usr/bin/ti50-emulator-ec_comm",
"/usr/bin/ti50-emulator-u2f", "/usr/bin/ti50-emulator-pinweaver",
};
constexpr size_t kBufferSize = 1024;
bool ToSockAddr(const base::FilePath& path, struct sockaddr_un* sa) {
// sun_path needs to include trailing '\0' byte.
if (path.value().size() >= sizeof(sa->sun_path)) {
LOG(ERROR) << "Path is too long: " << path.value();
return false;
}
memset(sa, 0, sizeof(*sa));
sa->sun_family = AF_UNIX;
strncpy(sa->sun_path, path.value().c_str(), sizeof(sa->sun_path) - 1);
return true;
}
std::pair<int, base::ScopedFD> ConnectUnixDomainSocket(
const base::FilePath& path) {
struct sockaddr_un sa;
if (!ToSockAddr(path, &sa))
return std::make_pair(EFAULT, base::ScopedFD());
base::ScopedFD fd(
socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0 /* protocol */));
if (!fd.is_valid()) {
int result_errno = errno;
PLOG(ERROR) << "Failed to create unix domain socket";
return std::make_pair(result_errno, base::ScopedFD());
}
if (HANDLE_EINTR(connect(fd.get(),
reinterpret_cast<const struct sockaddr*>(&sa),
sizeof(sa))) == -1) {
int result_errno = errno;
PLOG(ERROR) << "Failed to connect.";
return std::make_pair(result_errno, base::ScopedFD());
}
return std::make_pair(0, std::move(fd));
}
void RemoveSocketFiles() {
base::FileEnumerator emt(base::FilePath("."), true,
base::FileEnumerator::FILES);
for (base::FilePath path = emt.Next(); !path.empty(); path = emt.Next()) {
base::stat_wrapper_t statbuf;
if (base::File::Lstat(path.value().c_str(), &statbuf) != 0) {
PLOG(ERROR) << "Failed to lstat " << path.value();
continue;
}
if (S_ISSOCK(statbuf.st_mode)) {
if (!base::DeleteFile(path)) {
LOG(ERROR) << "Failed to delete " << path.value();
}
}
}
}
} // namespace
TpmExecutorTi50Impl::TpmExecutorTi50Impl() {
vendor_commands_.emplace_back(std::make_unique<TpmVendorCommandLocality>());
}
TpmExecutorTi50Impl::~TpmExecutorTi50Impl() {
process_.Wait();
}
void TpmExecutorTi50Impl::InitializeVTPM() {
RemoveSocketFiles();
for (const auto& vendor_cmd : vendor_commands_) {
if (!vendor_cmd->Init()) {
LOG(ERROR) << "Failed to initialize vendor command.";
}
}
process_.AddArg(kTi50EmulatorKernel);
// Use current dir.
process_.AddArg("--path");
process_.AddArg(".");
for (const char* app : kTi50EmulatorApps) {
process_.AddArg("-a");
process_.AddArg(app);
}
if (!process_.Start()) {
LOG(ERROR) << "Failed to start ti50 emulation.";
return;
}
base::FilePath gpio(kGpioPltRstFile);
base::FilePath tpm_fifo(kTpmFifoFile);
while (!base::PathExists(gpio) || !base::PathExists(tpm_fifo)) {
// TODO(yich): Don't use polling.
base::PlatformThread::Sleep(kPollingInterval);
}
auto pair = ConnectUnixDomainSocket(gpio);
if (pair.first != 0) {
LOG(ERROR) << "Failed to connect to ti50 emulation.";
return;
}
if (!base::UnixDomainSocket::SendMsg(pair.second.get(), kOne, std::size(kOne),
{pair.second.get()})) {
LOG(ERROR) << "Failed to send all data over socket.";
return;
}
LOG(INFO) << "vTPM Initialize.";
}
size_t TpmExecutorTi50Impl::GetCommandSize(const std::string& command) {
uint32_t size;
if (!ExtractCommandSize(command, &size)) {
LOG(ERROR) << "Command too small.";
return command.size();
}
return size;
}
std::string TpmExecutorTi50Impl::RunCommand(const std::string& command) {
for (const auto& vendor_cmd : vendor_commands_) {
if (vendor_cmd->IsVendorCommand(command)) {
return vendor_cmd->RunCommand(command);
}
}
auto pair = ConnectUnixDomainSocket(base::FilePath(kTpmFifoFile));
if (pair.first != 0) {
LOG(ERROR) << "Failed to connect to ti50 TPM FIFO.";
return CreateCommandWithCode(0xdead);
}
if (!base::UnixDomainSocket::SendMsg(pair.second.get(), command.data(),
command.length(), {pair.second.get()})) {
LOG(ERROR) << "Failed to send all data over socket.";
return CreateCommandWithCode(0xdead);
}
char buffer[kBufferSize];
std::string result;
while (true) {
uint32_t size;
if (ExtractCommandSize(result, &size) && result.size() >= size) {
break;
}
std::vector<base::ScopedFD> fds;
ssize_t len = base::UnixDomainSocket::RecvMsg(pair.second.get(), buffer,
sizeof(buffer), &fds);
result += std::string(buffer, len);
if (len < 0) {
break;
}
}
return result;
}
} // namespace tpm2_simulator