blob: f2df360d1ea02db2f4d74bf1ea839016224f5572 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "vtpm_manager"
#include <linux/types.h>
#include <linux/vtpm_proxy.h>
#include <sys/sysmacros.h>
#include <tss2/tss2_rc.h>
#include <future>
#include <gflags/gflags.h>
#include <android-base/endian.h>
#include <android-base/logging.h>
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
#include "guest/commands/vtpm_manager/commands.h"
DEFINE_uint32(tpm_vsock_port, 0, "vsock port to connect to for the TPM");
struct __attribute__((__packed__)) tpm_message_header {
__be16 tag;
__be32 length;
__be32 ordinal;
};
unsigned char locality = 0;
bool ReadResponseLoop(cvd::SharedFD in_fd, cvd::SharedFD out_fd) {
std::vector<char> message;
while (true) {
std::vector<char> response_size_bytes(4, 0);
CHECK(cvd::ReadExact(in_fd, &response_size_bytes) == 4) << "Could not read response size";
// the tpm simulator writes 4 extra bytes at the end of the message.
std::uint32_t response_size = be32toh(*reinterpret_cast<std::uint32_t*>(response_size_bytes.data()));
message.resize(response_size, '\0');
CHECK(cvd::ReadExact(in_fd, &message) == response_size) << "Could not read response message";
auto header = reinterpret_cast<tpm_message_header*>(message.data());
auto host_rc = betoh32(header->ordinal);
LOG(DEBUG) << "TPM response was: \"" << Tss2_RC_Decode(host_rc) << "\" (" << host_rc << ")";
std::vector<char> response_bytes(4, 0);
CHECK(cvd::ReadExact(in_fd, &response_bytes) == 4) << "Could not read parity response";
CHECK(cvd::WriteAll(out_fd, message) == message.size()) << "Could not forward message to vTPM";
}
}
void SendCommand(cvd::SharedFD out_fd, std::vector<char> command) {
// TODO(schuffelen): Implement this logic on the host.
// TPM2 simulator command protocol.
std::vector<char> command_bytes(4, 0);
*reinterpret_cast<std::uint32_t*>(command_bytes.data()) = htobe32(8); // TPM_SEND_COMMAND
CHECK(cvd::WriteAll(out_fd, command_bytes) == 4) << "Could not send TPM_SEND_COMMAND";
CHECK(cvd::WriteAll(out_fd, std::vector<char>{(char)locality}) == 1) << "Could not send locality";
std::vector<char> length_bytes(4, 0);
*reinterpret_cast<std::uint32_t*>(length_bytes.data()) = htobe32(command.size());
CHECK(cvd::WriteAll(out_fd, length_bytes) == 4) << "Could not send command length";
CHECK(cvd::WriteAll(out_fd, command) == command.size()) << "Could not write TPM message";
}
bool SendCommandLoop(cvd::SharedFD in_fd, cvd::SharedFD out_fd) {
std::vector<char> message(8192, '\0');
while (true) {
std::int32_t data_length = 0;
// Read the whole message in one chunk. The kernel returns EIO if the buffer
// is not large enough.
// https://github.com/torvalds/linux/blob/407e9ef72476e64937ebec44cc835e03a25fb408/drivers/char/tpm/tpm_vtpm_proxy.c#L98
while ((data_length = in_fd->Read(message.data(), message.size())) < 0) {
CHECK(in_fd->GetErrno() == EIO) << "Error in reading TPM command from kernel: "
<< in_fd->StrError();
message.resize((message.size() + 1) * 2, '\0');
}
message.resize(data_length, 0);
auto header = reinterpret_cast<tpm_message_header*>(message.data());
LOG(DEBUG) << "Received TPM command " << TpmCommandName(betoh32(header->ordinal));
if (header->ordinal == htobe32(TPM2_CC_SET_LOCALITY)) { // "Driver command"
locality = *reinterpret_cast<unsigned char*>(header + 1);
header->ordinal = htobe32(locality);
header->length = htobe32(sizeof(tpm_message_header));
message.resize(sizeof(tpm_message_header), '\0');
CHECK(cvd::WriteAll(in_fd, message) == message.size()) << "Could not write TPM message";
} else {
SendCommand(out_fd, message);
}
}
return false;
}
int main(int argc, char** argv) {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
::android::base::InitLogging(argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
CHECK(FLAGS_tpm_vsock_port != 0) << "Need a value for -tpm_vsock_port";
auto proxy = cvd::SharedFD::VsockClient(2, FLAGS_tpm_vsock_port, SOCK_STREAM);
CHECK(proxy->IsOpen()) << proxy->StrError();
auto vtpmx = cvd::SharedFD::Open("/dev/vtpmx", O_RDWR | O_CLOEXEC);
CHECK(vtpmx->IsOpen()) << vtpmx->StrError();
vtpm_proxy_new_dev vtpm_creation;
vtpm_creation.flags = VTPM_PROXY_FLAG_TPM2;
CHECK(vtpmx->Ioctl(VTPM_PROXY_IOC_NEW_DEV, &vtpm_creation) == 0) << vtpmx->StrError();
auto device_fd = cvd::SharedFD::Dup(vtpm_creation.fd);
CHECK(device_fd->IsOpen()) << device_fd->StrError();
close(vtpm_creation.fd);
LOG(VERBOSE) << "major was " << vtpm_creation.major << " minor was " << vtpm_creation.minor;
auto proxy_to_device = std::async(std::launch::async, ReadResponseLoop, proxy, device_fd);
auto device_to_proxy = std::async(std::launch::async, SendCommandLoop, device_fd, proxy);
CHECK(proxy_to_device.get())
<< "(" << device_fd->StrError() << ")"
<< "(" << proxy->StrError() << ")";
CHECK(device_to_proxy.get())
<< "(" << device_fd->StrError() << ")"
<< "(" << proxy->StrError() << ")";
}