blob: fcd161cafed2b009ced113c0e73482e676e732d1 [file] [log] [blame] [edit]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <rmad/utils/futility_utils_impl.h>
#include <array>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <re2/re2.h>
#include "rmad/utils/cmd_utils_impl.h"
#include "rmad/utils/futility_utils.h"
#include "rmad/utils/hwid_utils_impl.h"
namespace {
// MTD path for checking flash information on arm platform.
constexpr char kMtdPath[] = "/sys/class/mtd/mtd0/device/spi-nor";
constexpr char kFutilityCmd[] = "/usr/bin/futility";
constexpr char kFutilityWriteProtectDisabledStr[] = "WP status: disabled";
constexpr std::array<std::string_view, 5> kSetHwidArgv = {
kFutilityCmd, "gbb", "--set", "--flash", "--hwid"};
// The format specifier of futility flash size is `%#010x`.
constexpr char kFutilityFlashSizeRegexp[] =
R"(Flash size: 0x([[:xdigit:]]{8}))";
constexpr char kFutilityFlashNameRegexp[] = R"(Flash name: (.+)\n)";
constexpr char kFutilityFlashWpsrRangeRegexp[] =
R"(\(start = (\w+), length = (\w+)\))";
} // namespace
namespace rmad {
FutilityUtilsImpl::FutilityUtilsImpl() {
cmd_utils_ = std::make_unique<CmdUtilsImpl>();
hwid_utils_ = std::make_unique<HwidUtilsImpl>();
mtd_path_ = base::FilePath(kMtdPath);
}
FutilityUtilsImpl::FutilityUtilsImpl(std::unique_ptr<CmdUtils> cmd_utils,
std::unique_ptr<HwidUtils> hwid_utils,
base::FilePath mtd_path)
: cmd_utils_(std::move(cmd_utils)),
hwid_utils_(std::move(hwid_utils)),
mtd_path_(mtd_path) {}
std::optional<bool> FutilityUtilsImpl::GetApWriteProtectionStatus() {
std::string futility_output;
// Get WP status output string.
if (!cmd_utils_->GetOutput(
{kFutilityCmd, "flash", "--wp-status", "--ignore-hw"},
&futility_output)) {
return std::nullopt;
}
// Check if WP is disabled.
return (futility_output.find(kFutilityWriteProtectDisabledStr) ==
std::string::npos);
}
bool FutilityUtilsImpl::EnableApSoftwareWriteProtection() {
// Enable AP WP.
if (std::string output;
!cmd_utils_->GetOutput({kFutilityCmd, "flash", "--wp-enable"}, &output)) {
LOG(ERROR) << "Failed to enable AP SWWP";
LOG(ERROR) << output;
return false;
}
return true;
}
bool FutilityUtilsImpl::DisableApSoftwareWriteProtection() {
// Disable AP WP.
if (std::string output; !cmd_utils_->GetOutput(
{kFutilityCmd, "flash", "--wp-disable"}, &output)) {
LOG(ERROR) << "Failed to disable AP SWWP";
LOG(ERROR) << output;
return false;
}
return true;
}
bool FutilityUtilsImpl::SetHwid(const std::string& hwid) {
if (!hwid_utils_->VerifyHwidFormat(hwid, true)) {
LOG(ERROR) << "The given HWID has a invalid format.";
return false;
}
if (!hwid_utils_->VerifyChecksum(hwid)) {
LOG(ERROR) << "The checksum of the given HWID is incorrect.";
return false;
}
std::vector<std::string> argv{kSetHwidArgv.begin(), kSetHwidArgv.end()};
argv.push_back(hwid);
std::string output;
if (!cmd_utils_->GetOutputAndError(argv, &output)) {
LOG(ERROR) << "Failed to set HWID: " << output;
return false;
}
return true;
}
std::optional<uint64_t> FutilityUtilsImpl::GetFlashSize() {
std::string output;
if (!cmd_utils_->GetOutputAndError({kFutilityCmd, "flash", "--flash-size"},
&output)) {
LOG(ERROR) << "Failed to get flash size: " << output;
return std::nullopt;
}
std::string size_string;
re2::StringPiece string_piece(output);
re2::RE2 regexp(kFutilityFlashSizeRegexp);
if (!RE2::PartialMatch(string_piece, regexp, &size_string)) {
LOG(ERROR) << "Failed to parse flash size output.";
LOG(ERROR) << "Flash size output: " << output;
return std::nullopt;
}
uint64_t size;
if (!base::HexStringToUInt64(size_string, &size)) {
LOG(ERROR) << "Failed to convert hexadecimal string to integer.";
LOG(ERROR) << "Hex string: " << output;
return std::nullopt;
}
return size;
}
std::optional<FlashInfo> FutilityUtilsImpl::GetFlashInfo() {
std::string output;
if (!cmd_utils_->GetOutputAndError({kFutilityCmd, "flash", "--flash-info"},
&output)) {
LOG(ERROR) << "Failed to get flash info: " << output;
return std::nullopt;
}
auto flash_name = ParseFlashName(output);
if (!flash_name.has_value()) {
return std::nullopt;
}
auto flash_range = ParseFlashWpsrRange(output);
if (!flash_range.has_value()) {
return std::nullopt;
}
return FlashInfo{.flash_name = flash_name.value(),
.wpsr_start = flash_range.value().first,
.wpsr_length = flash_range.value().second};
}
std::optional<std::string> FutilityUtilsImpl::ParseFlashName(
const std::string& flash_info_string) {
std::string name_string;
if (!RE2::PartialMatch(flash_info_string, kFutilityFlashNameRegexp,
&name_string)) {
LOG(ERROR) << "Failed to parse flash name.";
LOG(ERROR) << "Flash info string: " << flash_info_string;
return std::nullopt;
}
// In the arm platform, we use the linux MTD driver for the spi nor flash. On
// the other hand, the flashrom doesn't support the flashinfo query because
// the linux MTD driver doesn't support it. As a result, we added a debugfs
// under the MTD_PATH for querying the partid and partname
if (name_string == "Opaque flash chip") {
DLOG(INFO) << "Checking flash name via MTD_PATH.";
base::FilePath partname_path = mtd_path_.AppendASCII("partname");
std::string partname;
if (!base::ReadFileToString(partname_path, &partname)) {
LOG(ERROR) << "Failed to read flash chip partname.";
return std::nullopt;
}
return static_cast<std::string>(
base::TrimWhitespaceASCII(partname, base::TRIM_TRAILING));
}
return name_string;
}
std::optional<std::pair<uint64_t, uint64_t>>
FutilityUtilsImpl::ParseFlashWpsrRange(const std::string& flash_info_string) {
std::string start_string, length_string;
if (!RE2::PartialMatch(flash_info_string, kFutilityFlashWpsrRangeRegexp,
&start_string, &length_string)) {
LOG(ERROR) << "Failed to parse flash WPSR range.";
LOG(ERROR) << "Flash info string: " << flash_info_string;
return std::nullopt;
}
uint64_t start, length;
if (!base::HexStringToUInt64(start_string, &start) ||
!base::HexStringToUInt64(length_string, &length)) {
LOG(ERROR) << "Failed to convert hexadecimal strings to integers.";
LOG(ERROR) << "Start string: " << start_string
<< ", Length string: " << length_string;
return std::nullopt;
}
return std::make_pair(start, length);
}
} // namespace rmad