blob: 79a630e65dce7d716869c23096eb128d3579778a [file] [log] [blame]
// Copyright 2017 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 "modemfwd/modem_flasher.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/stringprintf.h>
#include <chromeos/switches/modemfwd_switches.h>
#include <dbus/modemfwd/dbus-constants.h>
#include "modemfwd/error.h"
#include "modemfwd/firmware_file.h"
#include "modemfwd/logging.h"
#include "modemfwd/modem.h"
#include "modemfwd/notification_manager.h"
namespace modemfwd {
namespace {
class InhibitMode {
public:
explicit InhibitMode(Modem* modem) : modem_(modem) {
if (!modem_->SetInhibited(true))
ELOG(INFO) << "Inhibiting failed";
}
~InhibitMode() {
if (!modem_->SetInhibited(false))
ELOG(INFO) << "Uninhibiting failed";
}
private:
Modem* modem_;
};
} // namespace
ModemFlasher::ModemFlasher(FirmwareDirectory* firmware_directory,
std::unique_ptr<Journal> journal,
NotificationManager* notification_mgr)
: journal_(std::move(journal)),
firmware_directory_(firmware_directory),
notification_mgr_(notification_mgr) {}
void ModemFlasher::ProcessFailedToPrepareFirmwareFile(
const base::Location& code_location,
FlashState* flash_state,
const std::string& firmware_path,
brillo::ErrorPtr* err) {
Error::AddTo(err, code_location, kErrorResultFailedToPrepareFirmwareFile,
base::StringPrintf("Failed to prepare firmware file: %s",
firmware_path.c_str()));
notification_mgr_->NotifyUpdateFirmwareCompletedFailure(err->get());
flash_state->fw_flashed_ = false;
}
base::OnceClosure ModemFlasher::TryFlashForTesting(Modem* modem,
const std::string& variant,
brillo::ErrorPtr* err) {
firmware_directory_->OverrideVariantForTesting(variant);
return TryFlash(modem, err);
}
base::OnceClosure ModemFlasher::TryFlash(Modem* modem, brillo::ErrorPtr* err) {
std::string equipment_id = modem->GetEquipmentId();
FlashState* flash_state = &modem_info_[equipment_id];
if (!flash_state->ShouldFlash()) {
Error::AddTo(
err, FROM_HERE, kErrorResultFlashFailure,
base::StringPrintf("Modem with equipment ID \"%s\" failed to flash too "
"many times; not flashing",
equipment_id.c_str()));
notification_mgr_->NotifyUpdateFirmwareCompletedFailure(err->get());
flash_state->fw_flashed_ = false;
return base::OnceClosure();
}
std::string device_id = modem->GetDeviceId();
std::string current_carrier = modem->GetCarrierId();
// The real carrier ID before it might be replaced by the generic one
std::string real_carrier = current_carrier;
flash_state->OnCarrierSeen(current_carrier);
FirmwareDirectory::Files files = firmware_directory_->FindFirmware(
device_id, current_carrier.empty() ? nullptr : &current_carrier);
// Clear the attach APN if needed for a specific modem/carrier combination.
if (!real_carrier.empty() && !modem->ClearAttachAPN(real_carrier))
ELOG(INFO) << "Clear attach APN failed for current carrier.";
std::vector<FirmwareConfig> flash_cfg;
std::map<std::string, std::unique_ptr<FirmwareFile>> flash_files;
// Check if we need to update the main firmware.
if (flash_state->ShouldFlashMainFirmware() &&
files.main_firmware.has_value()) {
const FirmwareFileInfo& file_info = files.main_firmware.value();
ELOG(INFO) << "Found main firmware blob " << file_info.version
<< ", currently installed main firmware version: "
<< modem->GetMainFirmwareVersion();
if (file_info.version == modem->GetMainFirmwareVersion()) {
// We don't need to check the main firmware again if there's nothing new.
// Pretend that we successfully flashed it.
flash_state->OnFlashedMainFirmware();
} else {
auto firmware_file = std::make_unique<FirmwareFile>();
if (!firmware_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
file_info)) {
ProcessFailedToPrepareFirmwareFile(FROM_HERE, flash_state,
file_info.firmware_path, err);
return base::OnceClosure();
}
// We found different firmware!
// record to flash the main firmware binary.
flash_cfg.push_back(
{kFwMain, firmware_file->path_on_filesystem(), file_info.version});
flash_files[kFwMain] = std::move(firmware_file);
// If there are associated firmwares, we also need to prepare those.
for (const auto& assoc_entry : files.assoc_firmware) {
auto assoc_file = std::make_unique<FirmwareFile>();
if (!assoc_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
assoc_entry.second)) {
ProcessFailedToPrepareFirmwareFile(
FROM_HERE, flash_state, assoc_entry.second.firmware_path, err);
return base::OnceClosure();
}
flash_cfg.push_back({assoc_entry.first,
assoc_file->path_on_filesystem(),
assoc_entry.second.version});
flash_files[assoc_entry.first] = std::move(assoc_file);
}
}
}
// Check if we need to update the OEM firmware.
if (flash_state->ShouldFlashOemFirmware() && files.oem_firmware.has_value()) {
const FirmwareFileInfo& file_info = files.oem_firmware.value();
ELOG(INFO) << "Found OEM firmware blob " << file_info.version
<< ", currently installed OEM firmware version: "
<< modem->GetOemFirmwareVersion();
if (file_info.version == modem->GetOemFirmwareVersion()) {
flash_state->OnFlashedOemFirmware();
} else {
auto firmware_file = std::make_unique<FirmwareFile>();
if (!firmware_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
file_info)) {
ProcessFailedToPrepareFirmwareFile(FROM_HERE, flash_state,
file_info.firmware_path, err);
return base::OnceClosure();
}
flash_cfg.push_back(
{kFwOem, firmware_file->path_on_filesystem(), file_info.version});
flash_files[kFwOem] = std::move(firmware_file);
}
}
// Check if we need to update the carrier firmware.
if (!current_carrier.empty() && files.carrier_firmware.has_value() &&
flash_state->ShouldFlashCarrierFirmware(
firmware_directory_->GetFirmwarePath().Append(
files.carrier_firmware.value().firmware_path))) {
const FirmwareFileInfo& file_info = files.carrier_firmware.value();
ELOG(INFO) << "Found carrier firmware blob " << file_info.version
<< " for carrier " << current_carrier;
// Carrier firmware operates a bit differently. We need to flash if
// the carrier or the version has changed, or if there wasn't any carrier
// firmware to begin with.
std::string carrier_fw_id = modem->GetCarrierFirmwareId();
std::string carrier_fw_version = modem->GetCarrierFirmwareVersion();
bool has_carrier_fw =
!(carrier_fw_id.empty() || carrier_fw_version.empty());
if (has_carrier_fw) {
ELOG(INFO) << "Currently installed carrier firmware version "
<< carrier_fw_version << " for carrier " << carrier_fw_id;
} else {
ELOG(INFO) << "No carrier firmware is currently installed";
}
if (!has_carrier_fw ||
!firmware_directory_->IsUsingSameFirmware(device_id, carrier_fw_id,
current_carrier) ||
carrier_fw_version != file_info.version) {
auto firmware_file = std::make_unique<FirmwareFile>();
if (!firmware_file->PrepareFrom(firmware_directory_->GetFirmwarePath(),
file_info)) {
ProcessFailedToPrepareFirmwareFile(FROM_HERE, flash_state,
file_info.firmware_path, err);
return base::OnceClosure();
}
flash_cfg.push_back(
{kFwCarrier, firmware_file->path_on_filesystem(), file_info.version});
flash_files[kFwCarrier] = std::move(firmware_file);
}
} else {
// Log why we are not flashing the carrier firmware for debug
if (current_carrier.empty()) {
ELOG(INFO) << "No carrier found. Is a SIM card inserted?";
} else if (!files.carrier_firmware.has_value()) {
// Check if we have carrier firmware matching the SIM's carrier. If not,
// there's nothing to flash.
ELOG(INFO) << "No carrier firmware found for carrier " << current_carrier;
} else {
// ShouldFlashCarrierFirmware() was false
ELOG(INFO) << "Already flashed carrier firmware for " << current_carrier;
}
}
// Flash if we have new firmwares
if (flash_cfg.empty()) {
// This message is used by tests to track the end of flashing.
LOG(INFO) << "The modem already has the correct firmware installed";
notification_mgr_->NotifyUpdateFirmwareCompletedSuccess(
flash_state->fw_flashed_);
flash_state->fw_flashed_ = false;
return base::OnceClosure();
}
std::vector<std::string> fw_types;
std::transform(flash_cfg.begin(), flash_cfg.end(),
std::back_inserter(fw_types),
[](const FirmwareConfig& cfg) { return cfg.fw_type; });
InhibitMode _inhibit(modem);
journal_->MarkStartOfFlashingFirmware(fw_types, device_id, current_carrier);
if (!modem->FlashFirmwares(flash_cfg)) {
flash_state->OnFlashFailed();
journal_->MarkEndOfFlashingFirmware(device_id, current_carrier);
Error::AddTo(err, FROM_HERE, kErrorResultFailureReturnedByHelper,
"Helper failed to flash firmware files");
notification_mgr_->NotifyUpdateFirmwareCompletedFailure(err->get());
flash_state->fw_flashed_ = false;
return base::OnceClosure();
}
flash_state->fw_flashed_ = true;
for (const auto& info : flash_cfg) {
std::string fw_type = info.fw_type;
base::FilePath path_for_logging = flash_files[fw_type]->path_for_logging();
if (fw_type == kFwMain)
flash_state->OnFlashedMainFirmware();
else if (fw_type == kFwOem)
flash_state->OnFlashedOemFirmware();
else if (fw_type == kFwCarrier)
flash_state->OnFlashedCarrierFirmware(path_for_logging);
ELOG(INFO) << "Flashed " << fw_type << " firmware (" << path_for_logging
<< ") to the modem";
}
return base::BindOnce(&Journal::MarkEndOfFlashingFirmware,
base::Unretained(journal_.get()), device_id,
current_carrier);
}
} // namespace modemfwd