| // 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 |