blob: 1af0405b127c443c05030e83314483d31d4d3030 [file] [log] [blame]
// Copyright 2019 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 <stdint.h>
#include <stdlib.h>
#include <string>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/notreached.h>
#include <base/time/time.h>
#include <base/timer/elapsed_timer.h>
#include <brillo/daemons/daemon.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include <base/strings/stringprintf.h>
#include <cros_config/cros_config.h>
#include "biod/biod_config.h"
#include "biod/biod_metrics.h"
#include "biod/biod_version.h"
#include "biod/cros_fp_device.h"
#include "biod/updater/cros_fp_updater.h"
#include "biod/updater/update_reason.h"
#include "biod/updater/update_status.h"
#include "biod/updater/update_utils.h"
using UpdateStatus = biod::updater::UpdateStatus;
using UpdateReason = biod::updater::UpdateReason;
using FwUpdaterStatus = biod::BiodMetrics::FwUpdaterStatus;
using FindFirmwareFileStatus = biod::updater::FindFirmwareFileStatus;
namespace {
constexpr char kHelpText[] =
"bio_fw_updater ensures the fingerprint mcu has the latest firmware\n";
void LogFWFileVersion(const biod::CrosFpFirmware& fw) {
biod::CrosFpFirmware::ImageVersion ver = fw.GetVersion();
LOG(INFO) << "FWFile RO Version: '" << ver.ro_version << "'";
LOG(INFO) << "FWFile RW Version: '" << ver.rw_version << "'";
}
void LogFPMCUVersion(const biod::CrosFpDevice::EcVersion& ver) {
LOG(INFO) << "FPMCU RO Version: '" << ver.ro_version << "'";
LOG(INFO) << "FPMCU RW Version: '" << ver.rw_version << "'";
LOG(INFO) << "FPMCU Active Image: "
<< biod::CrosFpDeviceUpdate::EcCurrentImageToString(
ver.current_image);
}
void LogSetupDirectory(const base::FilePath& log_dir_path) {
const auto log_file_path = log_dir_path.Append(base::StringPrintf(
"bio_fw_updater.%s",
brillo::GetTimeAsLogString(base::Time::Now()).c_str()));
brillo::UpdateLogSymlinks(log_dir_path.Append("bio_fw_updater.LATEST"),
log_dir_path.Append("bio_fw_updater.PREVIOUS"),
log_file_path);
logging::LoggingSettings logging_settings;
logging_settings.logging_dest = logging::LOG_TO_FILE;
logging_settings.log_file_path = log_file_path.value().c_str();
logging_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
logging::InitLogging(logging_settings);
logging::SetLogItems(true, // process ID
true, // thread ID
true, // timestamp
false); // tickcount
}
class UpdaterMetrics {
public:
void SetUpdateReason(UpdateReason reason) { update_reason_ = reason; }
void Finished(FwUpdaterStatus status) {
uint64_t overall = runtime_.Elapsed().InMilliseconds();
DLOG(INFO) << "Runtime took " << overall << "ms.";
metrics_.SendFwUpdaterStatus(status, update_reason_,
static_cast<int>(overall));
}
private:
biod::BiodMetrics metrics_;
UpdateReason update_reason_ = UpdateReason::kNone;
base::ElapsedTimer runtime_;
};
} // namespace
int main(int argc, char* argv[]) {
UpdaterMetrics metrics;
DEFINE_string(log_dir, "/var/log/biod", "Directory where logs are written.");
brillo::FlagHelper::Init(argc, argv, kHelpText);
const auto log_dir_path = base::FilePath(FLAGS_log_dir);
if (base::DirectoryExists(log_dir_path)) {
LogSetupDirectory(log_dir_path);
} else {
LOG(ERROR) << "Log directory '" << log_dir_path.value()
<< "' does not exist, using syslog and stderr logging.";
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderrIfTty |
brillo::kLogHeader);
}
biod::LogVersion();
// Check for firmware disable mechanism
if (biod::updater::UpdateDisallowed()) {
LOG(INFO) << "FPMCU update disabled, exiting.";
return EXIT_SUCCESS;
}
// Check if model supports fingerprint
brillo::CrosConfig cros_config;
if (!cros_config.Init()) {
LOG(WARNING) << "Cros config is not supported on this model, continuing "
"with legacy update.";
}
if (biod::FingerprintUnsupported(&cros_config)) {
LOG(INFO) << "Fingerprint is not supported on this model, exiting.";
return EXIT_SUCCESS;
}
// Find a firmware file that matches the firmware file pattern
base::FilePath file;
auto status = biod::updater::FindFirmwareFile(
base::FilePath(biod::updater::kFirmwareDir), &cros_config, &file);
if (status == FindFirmwareFileStatus::kNoDirectory ||
status == FindFirmwareFileStatus::kFileNotFound) {
LOG(INFO) << "No firmware "
<< ((status == FindFirmwareFileStatus::kNoDirectory) ? "directory"
: "file")
<< " on rootfs, exiting.";
return EXIT_SUCCESS;
}
if (status == FindFirmwareFileStatus::kMultipleFiles) {
LOG(ERROR) << "Found more than one firmware file, aborting.";
metrics.Finished(FwUpdaterStatus::kFailureFirmwareFileMultiple);
return EXIT_FAILURE;
}
biod::CrosFpFirmware fw(file);
if (!fw.IsValid()) {
LOG(ERROR) << "Failed to load firmware file '" << fw.GetPath().value()
<< "': " << fw.GetStatusString();
LOG(ERROR) << "We are aborting update.";
}
switch (fw.GetStatus()) {
case biod::CrosFpFirmware::Status::kUninitialized:
NOTREACHED();
return EXIT_FAILURE;
case biod::CrosFpFirmware::Status::kOk:
break;
case biod::CrosFpFirmware::Status::kNotFound:
metrics.Finished(FwUpdaterStatus::kFailureFirmwareFileNotFound);
return EXIT_FAILURE;
case biod::CrosFpFirmware::Status::kOpenError:
metrics.Finished(FwUpdaterStatus::kFailureFirmwareFileOpen);
return EXIT_FAILURE;
case biod::CrosFpFirmware::Status::kBadFmap:
metrics.Finished(FwUpdaterStatus::kFailureFirmwareFileFmap);
return EXIT_FAILURE;
}
LogFWFileVersion(fw);
biod::CrosFpDeviceUpdate ec_device;
biod::CrosFpBootUpdateCtrl boot_ctrl;
auto ecver = ec_device.GetVersion();
if (!ecver) {
LOG(INFO) << "Failed to fetch EC version, aborting.";
metrics.Finished(FwUpdaterStatus::kFailurePreUpdateVersionCheck);
return EXIT_FAILURE;
}
LogFPMCUVersion(*ecver);
auto result = biod::updater::DoUpdate(ec_device, boot_ctrl, fw);
metrics.SetUpdateReason(result.reason);
switch (result.status) {
case UpdateStatus::kUpdateFailedGetVersion:
LOG(INFO) << "Failed to fetch EC version, aborting.";
metrics.Finished(FwUpdaterStatus::kFailureUpdateVersionCheck);
return EXIT_FAILURE;
case UpdateStatus::kUpdateFailedFlashProtect:
LOG(ERROR) << "Failed to fetch flash protect status, aborting.";
metrics.Finished(FwUpdaterStatus::kFailureUpdateFlashProtect);
return EXIT_FAILURE;
case UpdateStatus::kUpdateFailedRO:
LOG(ERROR) << "Failed to update RO image, aborting.";
metrics.Finished(FwUpdaterStatus::kFailureUpdateRO);
return EXIT_FAILURE;
case UpdateStatus::kUpdateFailedRW:
LOG(ERROR) << "Failed to update RW image, aborting.";
metrics.Finished(FwUpdaterStatus::kFailureUpdateRW);
return EXIT_FAILURE;
case UpdateStatus::kUpdateSucceeded:
ecver = ec_device.GetVersion();
if (!ecver) {
LOG(ERROR) << "Failed to fetch final EC version, update failed.";
metrics.Finished(FwUpdaterStatus::kFailurePostUpdateVersionCheck);
return EXIT_FAILURE;
}
LogFPMCUVersion(*ecver);
LOG(INFO) << "The update was successful.";
metrics.Finished(FwUpdaterStatus::kSuccessful);
return EXIT_SUCCESS;
case UpdateStatus::kUpdateNotNecessary:
LOG(INFO) << "Update was not necessary.";
metrics.Finished(FwUpdaterStatus::kUnnecessary);
return EXIT_SUCCESS;
}
NOTREACHED();
return EXIT_SUCCESS;
}