| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "trunks/tpm_handle.h" |
| |
| #include <fcntl.h> |
| #include <unistd.h> |
| |
| #include <base/callback.h> |
| #include <base/logging.h> |
| #include <base/posix/eintr_wrapper.h> |
| #include <base/threading/platform_thread.h> |
| #include <base/time/time.h> |
| |
| #include "trunks/trunks_metrics.h" |
| |
| namespace { |
| |
| const char kTpmDevice[] = "/dev/tpm0"; |
| const uint32_t kTpmBufferSize = 4096; |
| const int kInvalidFileDescriptor = -1; |
| |
| // Retry parameters for opening /dev/tpm0. |
| // How long do we wait after the first try? |
| constexpr base::TimeDelta kInitialRetry = base::TimeDelta::FromSecondsD(0.1); |
| // When we retry the next time, how much longer do we wait? |
| constexpr double kRetryMultiplier = 2.0; |
| // How many times to retry? |
| constexpr int kMaxRetry = 5; |
| // Total of 4 wait time between 5 retries. |
| // sum 0.1*2^k for k = 0 to 3 = 1.5s |
| // Note that if this period is not enough, upstart will still respawn trunksd |
| // after it all fall through. |
| |
| } // namespace |
| |
| namespace trunks { |
| |
| TpmHandle::TpmHandle() : fd_(kInvalidFileDescriptor) {} |
| |
| TpmHandle::~TpmHandle() { |
| int result = IGNORE_EINTR(close(fd_)); |
| if (result == -1) { |
| PLOG(ERROR) << "TPM: couldn't close " << kTpmDevice; |
| } |
| LOG(INFO) << "TPM: " << kTpmDevice << " closed successfully"; |
| } |
| |
| bool TpmHandle::Init() { |
| if (fd_ != kInvalidFileDescriptor) { |
| VLOG(1) << "Tpm already initialized."; |
| return true; |
| } |
| base::TimeDelta current_wait = kInitialRetry; |
| for (int i = 0; i < kMaxRetry; i++) { |
| fd_ = HANDLE_EINTR(open(kTpmDevice, O_RDWR)); |
| if (fd_ == kInvalidFileDescriptor) { |
| PLOG(ERROR) << "TPM: Error opening tpm0 file descriptor at " |
| << kTpmDevice; |
| if (i == kMaxRetry - 1) { |
| // If we get here, it doesn't work. |
| return false; |
| } |
| base::PlatformThread::Sleep(current_wait); |
| current_wait = current_wait * kRetryMultiplier; |
| continue; |
| } |
| LOG(INFO) << "TPM: " << kTpmDevice << " opened successfully"; |
| break; |
| } |
| return true; |
| } |
| |
| void TpmHandle::SendCommand(const std::string& command, |
| const ResponseCallback& callback) { |
| callback.Run(SendCommandAndWait(command)); |
| } |
| |
| std::string TpmHandle::SendCommandAndWait(const std::string& command) { |
| std::string response; |
| TPM_RC result = SendCommandInternal(command, &response); |
| if (result != TPM_RC_SUCCESS) { |
| response = CreateErrorResponse(result); |
| // Send the command code and system uptime of the first timeout command |
| if (errno == ETIME) { |
| static bool has_reported = false; |
| if (!has_reported) { |
| TrunksMetrics metrics; |
| if (metrics.ReportTpmHandleTimeoutCommandAndTime(result, command)) |
| has_reported = true; |
| } |
| } |
| } |
| return response; |
| } |
| |
| TPM_RC TpmHandle::SendCommandInternal(const std::string& command, |
| std::string* response) { |
| CHECK_NE(fd_, kInvalidFileDescriptor); |
| int result = HANDLE_EINTR(write(fd_, command.data(), command.length())); |
| if (result < 0 && errno == EREMOTEIO) { |
| // Retry once in case the error is caused by late wakeup from sleep. |
| // Repeated error should lead to failure. |
| LOG(WARNING) << "TPM: Retrying write after Remote I/O error."; |
| result = HANDLE_EINTR(write(fd_, command.data(), command.length())); |
| } |
| if (result < 0) { |
| PLOG(ERROR) << "TPM: Error writing to TPM handle."; |
| return TRUNKS_RC_WRITE_ERROR; |
| } |
| if (static_cast<size_t>(result) != command.length()) { |
| LOG(ERROR) << "TPM: Error writing to TPM handle: " << result << " vs " |
| << command.length(); |
| return TRUNKS_RC_WRITE_ERROR; |
| } |
| char response_buf[kTpmBufferSize]; |
| result = HANDLE_EINTR(read(fd_, response_buf, kTpmBufferSize)); |
| if (result < 0) { |
| PLOG(ERROR) << "TPM: Error reading from TPM handle."; |
| return TRUNKS_RC_READ_ERROR; |
| } |
| response->assign(response_buf, static_cast<size_t>(result)); |
| return TPM_RC_SUCCESS; |
| } |
| |
| } // namespace trunks |