blob: 10f4afe3581421d8b81bb40821c73bde624ae5a7 [file] [log] [blame]
// Copyright 2021 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 "rmad/state_handler/update_device_info_state_handler.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/logging.h>
#include <google/protobuf/repeated_field.h>
#include "rmad/constants.h"
#include "rmad/utils/cbi_utils_impl.h"
#include "rmad/utils/cros_config_utils_impl.h"
#include "rmad/utils/crossystem_utils_impl.h"
#include "rmad/utils/fake_cbi_utils.h"
#include "rmad/utils/fake_cros_config_utils.h"
#include "rmad/utils/fake_crossystem_utils.h"
#include "rmad/utils/fake_regions_utils.h"
#include "rmad/utils/fake_vpd_utils.h"
#include "rmad/utils/regions_utils_impl.h"
#include "rmad/utils/vpd_utils_impl.h"
namespace {
template <typename T>
bool IsRepeatedFieldSame(const T& list1, const T& list2) {
int size = list1.size();
if (size != list2.size()) {
return false;
}
for (int i = 0; i < size; i++) {
if (list1.at(i) != list2.at(i)) {
return false;
}
}
return true;
}
} // namespace
namespace rmad {
namespace fake {
FakeUpdateDeviceInfoStateHandler::FakeUpdateDeviceInfoStateHandler(
scoped_refptr<JsonStore> json_store, const base::FilePath& working_dir_path)
: UpdateDeviceInfoStateHandler(
json_store,
std::make_unique<FakeCbiUtils>(working_dir_path),
std::make_unique<FakeCrosConfigUtils>(),
std::make_unique<FakeCrosSystemUtils>(working_dir_path),
std::make_unique<FakeRegionsUtils>(),
std::make_unique<FakeVpdUtils>(working_dir_path)) {}
} // namespace fake
UpdateDeviceInfoStateHandler::UpdateDeviceInfoStateHandler(
scoped_refptr<JsonStore> json_store)
: BaseStateHandler(json_store) {
cbi_utils_ = std::make_unique<CbiUtilsImpl>();
cros_config_utils_ = std::make_unique<CrosConfigUtilsImpl>();
crossystem_utils_ = std::make_unique<CrosSystemUtilsImpl>();
regions_utils_ = std::make_unique<RegionsUtilsImpl>();
vpd_utils_ = std::make_unique<VpdUtilsImpl>();
}
UpdateDeviceInfoStateHandler::UpdateDeviceInfoStateHandler(
scoped_refptr<JsonStore> json_store,
std::unique_ptr<CbiUtils> cbi_utils,
std::unique_ptr<CrosConfigUtils> cros_config_utils,
std::unique_ptr<CrosSystemUtils> crossystem_utils,
std::unique_ptr<RegionsUtils> regions_utils,
std::unique_ptr<VpdUtils> vpd_utils)
: BaseStateHandler(json_store),
cbi_utils_(std::move(cbi_utils)),
cros_config_utils_(std::move(cros_config_utils)),
crossystem_utils_(std::move(crossystem_utils)),
regions_utils_(std::move(regions_utils)),
vpd_utils_(std::move(vpd_utils)) {}
RmadErrorCode UpdateDeviceInfoStateHandler::InitializeState() {
CHECK(cbi_utils_);
CHECK(cros_config_utils_);
CHECK(crossystem_utils_);
CHECK(regions_utils_);
CHECK(vpd_utils_);
// Make sure HWWP is off before initializing the state.
if (int hwwp_status;
!crossystem_utils_->GetHwwpStatus(&hwwp_status) || hwwp_status != 0) {
return RMAD_ERROR_WP_ENABLED;
}
auto update_dev_info = std::make_unique<UpdateDeviceInfoState>();
std::string serial_number;
std::string region;
uint64_t sku_id;
bool is_custom_label_exist;
std::string custom_label_tag;
std::string dram_part_number;
std::vector<std::string> region_list;
std::vector<int> sku_id_list;
std::vector<std::string> custom_label_tag_list;
// We reserve -1 for unmatched indexes.
int32_t region_index = -1;
int32_t sku_index = -1;
int32_t custom_label_index = -1;
bool mlb_repair;
// We can allow incorrect device info in vpd and cbi before writing.
if (!vpd_utils_->GetSerialNumber(&serial_number)) {
LOG(WARNING) << "Failed to get original serial number from vpd.";
}
if (!vpd_utils_->GetRegion(&region)) {
LOG(WARNING) << "Failed to get original region from vpd.";
}
if (!cbi_utils_->GetSku(&sku_id)) {
LOG(WARNING) << "Failed to get original sku from cbi.";
}
// For backward compatibility, we should use cros_config to get the
// custom-label, which already handles it.
is_custom_label_exist =
cros_config_utils_->GetCurrentCustomLabelTag(&custom_label_tag);
if (!cbi_utils_->GetDramPartNum(&dram_part_number)) {
LOG(WARNING) << "Failed to get original dram part number from cbi.";
}
if (!regions_utils_->GetRegionList(&region_list)) {
LOG(ERROR) << "Failed to get the list of possible regions to initialize "
"the handler.";
return RMAD_ERROR_STATE_HANDLER_INITIALIZATION_FAILED;
}
if (auto it = std::find(region_list.begin(), region_list.end(), region);
it != region_list.end()) {
region_index = std::distance(region_list.begin(), it);
}
if (!cros_config_utils_->GetSkuIdList(&sku_id_list)) {
LOG(ERROR) << "Failed to get the list of possible sku-ids "
"to initialize the handler.";
return RMAD_ERROR_STATE_HANDLER_INITIALIZATION_FAILED;
}
if (auto it = std::find(sku_id_list.begin(), sku_id_list.end(), sku_id);
it != sku_id_list.end()) {
sku_index = std::distance(sku_id_list.begin(), it);
}
if (!cros_config_utils_->GetCustomLabelTagList(&custom_label_tag_list)) {
LOG(ERROR) << "Failed to get the list of possible custom-label-tags "
"to initialize the handler.";
return RMAD_ERROR_STATE_HANDLER_INITIALIZATION_FAILED;
}
if (is_custom_label_exist) {
if (auto it = std::find(custom_label_tag_list.begin(),
custom_label_tag_list.end(), custom_label_tag);
it != custom_label_tag_list.end()) {
custom_label_index = std::distance(custom_label_tag_list.begin(), it);
}
if (custom_label_index == -1) {
LOG(WARNING) << "We found an unmatched custom-label-tag in vpd.";
vpd_utils_->RemoveCustomLabelTag();
}
}
if (!json_store_->GetValue(kMlbRepair, &mlb_repair)) {
LOG(ERROR) << "Failed to get the mainboard repair status "
"to initialize the handler.";
return RMAD_ERROR_STATE_HANDLER_INITIALIZATION_FAILED;
}
update_dev_info->set_original_serial_number(serial_number);
update_dev_info->set_original_region_index(region_index);
update_dev_info->set_original_sku_index(sku_index);
update_dev_info->set_original_whitelabel_index(custom_label_index);
update_dev_info->set_original_dram_part_number(dram_part_number);
for (auto region_option : region_list) {
update_dev_info->add_region_list(region_option);
}
for (auto sku_option : sku_id_list) {
// We get a vector<int> from cros_config, but we set a uint64_t to cbi.
// Therefore, we should cast it to uint64_t here.
update_dev_info->add_sku_list(static_cast<uint64_t>(sku_option));
}
for (auto custom_label_option : custom_label_tag_list) {
update_dev_info->add_whitelabel_list(custom_label_option);
}
update_dev_info->set_mlb_repair(mlb_repair);
state_.set_allocated_update_device_info(update_dev_info.release());
return RMAD_ERROR_OK;
}
BaseStateHandler::GetNextStateCaseReply
UpdateDeviceInfoStateHandler::GetNextStateCase(const RmadState& state) {
if (!state.has_update_device_info()) {
LOG(ERROR) << "RmadState missing |update device info| state.";
return NextStateCaseWrapper(RMAD_ERROR_REQUEST_INVALID);
}
if (!VerifyReadOnly(state.update_device_info())) {
return NextStateCaseWrapper(RMAD_ERROR_REQUEST_ARGS_VIOLATION);
}
if (!WriteDeviceInfo(state.update_device_info())) {
vpd_utils_->ClearRoVpdCache();
vpd_utils_->ClearRwVpdCache();
if (int hwwp_status;
!crossystem_utils_->GetHwwpStatus(&hwwp_status) || hwwp_status != 0) {
return NextStateCaseWrapper(RMAD_ERROR_WP_ENABLED);
}
return NextStateCaseWrapper(RMAD_ERROR_CANNOT_WRITE);
}
state_ = state;
return NextStateCaseWrapper(RmadState::StateCase::kProvisionDevice);
}
bool UpdateDeviceInfoStateHandler::VerifyReadOnly(
const UpdateDeviceInfoState& device_info) {
auto original_device_info = state_.update_device_info();
if (original_device_info.original_serial_number() !=
device_info.original_serial_number()) {
LOG(ERROR) << "The read-only |original serial number| of "
"|update device info| is changed.";
return false;
}
if (original_device_info.original_region_index() !=
device_info.original_region_index()) {
LOG(ERROR) << "The read-only |original region index| of "
"|update device info| is changed.";
return false;
}
if (original_device_info.original_sku_index() !=
device_info.original_sku_index()) {
LOG(ERROR) << "The read-only |original sku index| of "
"|update device info| is changed.";
return false;
}
if (original_device_info.original_whitelabel_index() !=
device_info.original_whitelabel_index()) {
LOG(ERROR) << "The read-only |original custom-label-tag index| of "
"|update device info| is changed.";
return false;
}
if (original_device_info.original_dram_part_number() !=
device_info.original_dram_part_number()) {
LOG(ERROR) << "The read-only |original dram part number| of "
"|update device info| is changed.";
return false;
}
if (!IsRepeatedFieldSame(original_device_info.region_list(),
device_info.region_list())) {
LOG(ERROR) << "The read-only |region list| of |update device info| "
"is changed.";
return false;
}
if (!IsRepeatedFieldSame(original_device_info.sku_list(),
device_info.sku_list())) {
LOG(ERROR) << "The read-only |sku list| of |update device info| "
"is changed.";
return false;
}
if (!IsRepeatedFieldSame(original_device_info.whitelabel_list(),
device_info.whitelabel_list())) {
LOG(ERROR)
<< "The read-only |custom-label-tag list| of |update device info| "
"is changed.";
return false;
}
if (original_device_info.mlb_repair() != device_info.mlb_repair()) {
LOG(ERROR) << "The read-only |mlb repair| of |update device info| "
"is changed.";
return false;
}
if (device_info.region_index() < 0 ||
device_info.region_index() >= device_info.region_list_size()) {
LOG(ERROR) << "It is a wrong |region index| of |region list|.";
return false;
}
if (device_info.sku_index() < 0 ||
device_info.sku_index() >= device_info.sku_list_size()) {
LOG(ERROR) << "It is a wrong |sku index| of |sku list|.";
return false;
}
// We can allow empty custom-label-tag, so we reserved negative index for
// empty string of custom-label-tag.
if (device_info.whitelabel_index() >= device_info.whitelabel_list_size()) {
LOG(ERROR)
<< "It is a wrong |custom-label-tag index| of |custom-label-tag list|.";
return false;
}
return true;
}
bool UpdateDeviceInfoStateHandler::WriteDeviceInfo(
const UpdateDeviceInfoState& device_info) {
if (!vpd_utils_->SetSerialNumber(device_info.serial_number())) {
LOG(ERROR) << "Failed to save |serial number| to vpd cache.";
return false;
}
if (!vpd_utils_->SetRegion(
device_info.region_list(device_info.region_index()))) {
LOG(ERROR) << "Failed to save region to vpd cache.";
return false;
}
if (!cbi_utils_->SetSku(device_info.sku_list(device_info.sku_index()))) {
LOG(ERROR) << "Failed to write sku to cbi.";
return false;
}
// The default custom-label-tag is always an empty string.
std::string custom_label_tag = "";
if (device_info.whitelabel_index() >= 0) {
custom_label_tag =
device_info.whitelabel_list(device_info.whitelabel_index());
}
// We need to set the custom-label-tag when the model has it.
if (!device_info.whitelabel_list().empty() &&
!vpd_utils_->SetCustomLabelTag(custom_label_tag)) {
LOG(ERROR) << "Failed to save custom_label_tag to vpd cache.";
return false;
}
if (!cbi_utils_->SetDramPartNum(device_info.dram_part_number())) {
LOG(ERROR) << "Failed to write dram part number to cbi.";
return false;
}
if (!vpd_utils_->FlushOutRoVpdCache()) {
LOG(ERROR) << "Failed to flush cache to ro vpd.";
return false;
}
return true;
}
} // namespace rmad