| // 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/gsc_utils_impl.h> |
| |
| #include <iomanip> |
| #include <memory> |
| #include <optional> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/containers/fixed_flat_map.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/gsc_utils.h" |
| |
| namespace rmad { |
| |
| namespace { |
| |
| constexpr char kGsctoolCmd[] = "gsctool"; |
| // Constants for RSU. |
| const std::vector<std::string> kGetRsuChallengeArgv{kGsctoolCmd, "-a", "-r", |
| "-M"}; |
| const std::vector<std::string> kSendRsuResponseArgv{kGsctoolCmd, "-a", "-r"}; |
| constexpr char kRsuChallengeRegexp[] = R"(CHALLENGE=([[:alnum:]]{80}))"; |
| // Constants for CCD info. |
| const std::vector<std::string> kGetCcdInfoArgv{kGsctoolCmd, "-a", "-I", "-M"}; |
| constexpr char kFactoryModeMatchStr[] = "CCD_FLAG_FACTORY_MODE=Y"; |
| constexpr char kInitialFactoryModeMatchStr[] = "INITIAL_FACTORY_MODE=Y"; |
| // Constants for factory mode. |
| const std::vector<std::string> kEnableFactoryModeArgv{kGsctoolCmd, "-a", "-F", |
| "enable"}; |
| const std::vector<std::string> kDisableFactoryModeArgv{kGsctoolCmd, "-a", "-F", |
| "disable"}; |
| // Constants for board ID. |
| const std::vector<std::string> kGetBoardIdArgv{kGsctoolCmd, "-a", "-i", "-M"}; |
| constexpr char kSetBoardIdCmd[] = "/usr/sbin/gsc_set_board_id"; |
| constexpr char kBoardIdTypeRegexp[] = R"(BID_TYPE=([[:xdigit:]]{8}))"; |
| constexpr char kBoardIdFlagsRegexp[] = R"(BID_FLAGS=([[:xdigit:]]{8}))"; |
| |
| // Constants for reboot. |
| const std::vector<std::string> kRebootArgv{kGsctoolCmd, "-a", "--reboot"}; |
| |
| // Constants for factory config. |
| const std::vector<std::string> kGetFactoryConfigArgv{kGsctoolCmd, "-a", |
| "--factory_config"}; |
| const std::vector<std::string> kSetFactoryConfigArgv{kGsctoolCmd, "-a", |
| "--factory_config"}; |
| constexpr char kFactoryConfigRegexp[] = R"(raw value: ([[:xdigit:]]{16}))"; |
| |
| // Constants for CHASSIS_OPEN. |
| const std::vector<std::string> kGetChassisOpenArgv{kGsctoolCmd, "-a", "-K", |
| "chassis_open"}; |
| constexpr char kChassisOpenRegexp[] = R"(Chassis Open: ((true)|(false)))"; |
| |
| // Constants for addressing mode. |
| constexpr std::array<std::string_view, 3> kAddressingMode = {kGsctoolCmd, "-a", |
| "-C"}; |
| |
| // Constants for wpsr. |
| constexpr std::array<std::string_view, 3> kWpsr = {kGsctoolCmd, "-a", "-E"}; |
| |
| // SPI addressing mode mappings from enum to string. |
| constexpr char kSpiAddressingMode3Byte[] = "3byte"; |
| constexpr char kSpiAddressingMode4Byte[] = "4byte"; |
| constexpr char kSpiAddressingModeNotProvisioned[] = "Not Provisioned"; |
| constexpr char kSpiAddressingModeUnknown[] = "Unknown"; |
| constexpr auto kSpiAddressingModeMap = |
| base::MakeFixedFlatMap<SpiAddressingMode, std::string_view>({ |
| {SpiAddressingMode::kUnknown, kSpiAddressingModeUnknown}, |
| {SpiAddressingMode::k3Byte, kSpiAddressingMode3Byte}, |
| {SpiAddressingMode::k4Byte, kSpiAddressingMode4Byte}, |
| {SpiAddressingMode::kNotProvisioned, kSpiAddressingModeNotProvisioned}, |
| }); |
| |
| // Factory config encoding/decoding functions. |
| // According to b/275356839, factory config is stored in GSC INFO page with 64 |
| // bit length. The lower 5 bits are now allocated to the feature management |
| // flags. |
| std::string Uint64ToHexString(uint64_t value) { |
| std::ostringstream ss; |
| ss << std::hex << std::setfill('0') << std::setw(16) << value; |
| return ss.str(); |
| } |
| |
| std::string EncodeFactoryConfig(bool is_chassis_branded, |
| int hw_compliance_version) { |
| uint64_t factory_config = |
| ((is_chassis_branded & 0x1) << 4) | (hw_compliance_version & 0xF); |
| return Uint64ToHexString(factory_config); |
| } |
| |
| bool DecodeFactoryConfig(const std::string& factory_config_hexstr, |
| bool* is_chassis_branded, |
| int* hw_compliance_version) { |
| uint64_t factory_config; |
| if (!base::HexStringToUInt64(factory_config_hexstr, &factory_config)) { |
| LOG(ERROR) << "Failed to decode hex string " << factory_config_hexstr; |
| return false; |
| } |
| *is_chassis_branded = ((factory_config >> 4) & 0x1); |
| *hw_compliance_version = (factory_config & 0xF); |
| return true; |
| } |
| |
| } // namespace |
| |
| GscUtilsImpl::GscUtilsImpl() : GscUtils() { |
| cmd_utils_ = std::make_unique<CmdUtilsImpl>(); |
| } |
| |
| GscUtilsImpl::GscUtilsImpl(std::unique_ptr<CmdUtils> cmd_utils) |
| : GscUtils(), cmd_utils_(std::move(cmd_utils)) {} |
| |
| bool GscUtilsImpl::GetRsuChallengeCode(std::string* challenge_code) const { |
| // TODO(chenghan): Check with GSC team if we can expose a tpm_managerd API |
| // for this, so we don't need to depend on `gsctool` output |
| // format to do extra string parsing. |
| std::string output; |
| if (!cmd_utils_->GetOutput(kGetRsuChallengeArgv, &output)) { |
| LOG(ERROR) << "Failed to get RSU challenge code"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| re2::StringPiece string_piece(output); |
| re2::RE2 regexp(kRsuChallengeRegexp); |
| if (!RE2::PartialMatch(string_piece, regexp, challenge_code)) { |
| LOG(ERROR) << "Failed to parse RSU challenge code"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| DLOG(INFO) << "Challenge code: " << *challenge_code; |
| return true; |
| } |
| |
| bool GscUtilsImpl::PerformRsu(const std::string& unlock_code) const { |
| std::vector<std::string> argv(kSendRsuResponseArgv); |
| argv.push_back(unlock_code); |
| if (std::string output; !cmd_utils_->GetOutput(argv, &output)) { |
| DLOG(ERROR) << "RSU failed."; |
| DLOG(ERROR) << output; |
| return false; |
| } |
| DLOG(INFO) << "RSU succeeded."; |
| return true; |
| } |
| |
| bool GscUtilsImpl::EnableFactoryMode() const { |
| if (!IsFactoryModeEnabled()) { |
| std::string unused_output; |
| return cmd_utils_->GetOutput(kEnableFactoryModeArgv, &unused_output); |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::DisableFactoryMode() const { |
| if (IsFactoryModeEnabled()) { |
| std::string unused_output; |
| return cmd_utils_->GetOutput(kDisableFactoryModeArgv, &unused_output); |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::IsFactoryModeEnabled() const { |
| std::string output; |
| cmd_utils_->GetOutput(kGetCcdInfoArgv, &output); |
| return output.find(kFactoryModeMatchStr) != std::string::npos; |
| } |
| |
| bool GscUtilsImpl::IsInitialFactoryModeEnabled() const { |
| std::string output; |
| cmd_utils_->GetOutput(kGetCcdInfoArgv, &output); |
| return output.find(kInitialFactoryModeMatchStr) != std::string::npos; |
| } |
| |
| bool GscUtilsImpl::GetBoardIdType(std::string* board_id_type) const { |
| std::string output; |
| if (!cmd_utils_->GetOutput(kGetBoardIdArgv, &output)) { |
| LOG(ERROR) << "Failed to get GSC board ID"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| re2::StringPiece string_piece(output); |
| re2::RE2 regexp(kBoardIdTypeRegexp); |
| if (!RE2::PartialMatch(string_piece, regexp, board_id_type)) { |
| LOG(ERROR) << "Failed to parse GSC board ID type"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::GetBoardIdFlags(std::string* board_id_flags) const { |
| std::string output; |
| if (!cmd_utils_->GetOutput(kGetBoardIdArgv, &output)) { |
| LOG(ERROR) << "Failed to get GSC board ID flags"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| re2::StringPiece string_piece(output); |
| re2::RE2 regexp(kBoardIdFlagsRegexp); |
| if (!RE2::PartialMatch(string_piece, regexp, board_id_flags)) { |
| LOG(ERROR) << "Failed to parse GSC board ID flags"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::SetBoardId(bool is_custom_label) const { |
| std::string output; |
| std::vector<std::string> argv{kSetBoardIdCmd}; |
| if (is_custom_label) { |
| argv.push_back("whitelabel_pvt"); |
| } else { |
| argv.push_back("pvt"); |
| } |
| if (!cmd_utils_->GetOutputAndError(argv, &output)) { |
| LOG(ERROR) << "Failed to set GSC board ID"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::Reboot() const { |
| std::string unused_output; |
| return cmd_utils_->GetOutput(kRebootArgv, &unused_output); |
| } |
| |
| bool GscUtilsImpl::GetFactoryConfig(bool* is_chassis_branded, |
| int* hw_compliance_version) const { |
| std::string output; |
| if (!cmd_utils_->GetOutput(kGetFactoryConfigArgv, &output)) { |
| LOG(ERROR) << "Failed to get factory config"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| re2::StringPiece string_piece(output); |
| re2::RE2 regexp(kFactoryConfigRegexp); |
| std::string factory_config_hexstr; |
| if (!RE2::PartialMatch(string_piece, regexp, &factory_config_hexstr)) { |
| LOG(ERROR) << "Failed to parse factory config"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| if (!DecodeFactoryConfig(factory_config_hexstr, is_chassis_branded, |
| hw_compliance_version)) { |
| LOG(ERROR) << "Failed to parse factory config hex string: " |
| << factory_config_hexstr; |
| return false; |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::SetFactoryConfig(bool is_chassis_branded, |
| int hw_compliance_version) const { |
| std::string factory_config_hexstr = |
| EncodeFactoryConfig(is_chassis_branded, hw_compliance_version); |
| std::string output; |
| std::vector<std::string> argv(kSetFactoryConfigArgv); |
| argv.push_back(factory_config_hexstr); |
| if (!cmd_utils_->GetOutput(argv, &output)) { |
| LOG(ERROR) << "Failed to set factory config"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| return true; |
| } |
| |
| bool GscUtilsImpl::GetChassisOpenStatus(bool* status) { |
| std::string output; |
| if (!cmd_utils_->GetOutput(kGetChassisOpenArgv, &output)) { |
| LOG(ERROR) << "Failed to get CHASSIS_OPEN status"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| |
| re2::StringPiece string_piece(output); |
| re2::RE2 regexp(kChassisOpenRegexp); |
| std::string bool_string; |
| if (!RE2::PartialMatch(string_piece, regexp, &bool_string)) { |
| LOG(ERROR) << "Failed to parse CHASSIS_OPEN status"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| |
| *status = (bool_string == "true"); |
| |
| return true; |
| } |
| |
| SpiAddressingMode GscUtilsImpl::GetAddressingMode() { |
| std::string output; |
| std::vector<std::string> argv{kAddressingMode.begin(), kAddressingMode.end()}; |
| |
| if (!cmd_utils_->GetOutputAndError(argv, &output)) { |
| LOG(ERROR) << "Failed to get addressing mode"; |
| LOG(ERROR) << output; |
| return SpiAddressingMode::kUnknown; |
| } |
| |
| // The output can be "3byte", "4byte", or "not provisioned". |
| if (output == "3byte") { |
| return SpiAddressingMode::k3Byte; |
| } else if (output == "4byte") { |
| return SpiAddressingMode::k4Byte; |
| } else if (output == "not provisioned") { |
| return SpiAddressingMode::kNotProvisioned; |
| } |
| |
| return SpiAddressingMode::kUnknown; |
| } |
| |
| bool GscUtilsImpl::SetAddressingMode(SpiAddressingMode mode) { |
| if (mode != SpiAddressingMode::k3Byte && mode != SpiAddressingMode::k4Byte) { |
| LOG(ERROR) << "Only 3byte and 4byte addressing modes are available."; |
| return false; |
| } |
| |
| std::string output; |
| std::vector<std::string> argv{kAddressingMode.begin(), kAddressingMode.end()}; |
| argv.push_back(std::string(kSpiAddressingModeMap.at(mode))); |
| |
| if (!cmd_utils_->GetOutputAndError(argv, &output)) { |
| LOG(ERROR) << "Failed to set addressing mode"; |
| LOG(ERROR) << output; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| SpiAddressingMode GscUtilsImpl::GetAddressingModeByFlashSize( |
| uint64_t flash_size) { |
| if (flash_size <= 0x1000000) { // 2^24 |
| return SpiAddressingMode::k3Byte; |
| } |
| return SpiAddressingMode::k4Byte; |
| } |
| |
| bool GscUtilsImpl::SetWpsr(std::string_view wpsr) { |
| std::string output; |
| std::vector<std::string> argv{kWpsr.begin(), kWpsr.end()}; |
| argv.emplace_back(wpsr); |
| |
| if (!cmd_utils_->GetOutputAndError(argv, &output)) { |
| LOG(ERROR) << "Failed to set wpsr: " << wpsr; |
| LOG(ERROR) << output; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::optional<bool> GscUtilsImpl::IsApWpsrProvisioned() { |
| std::string output; |
| std::vector<std::string> argv{kWpsr.begin(), kWpsr.end()}; |
| |
| if (!cmd_utils_->GetOutputAndError(argv, &output)) { |
| LOG(ERROR) << "Failed to get wpsr"; |
| LOG(ERROR) << output; |
| return std::nullopt; |
| } |
| |
| return base::TrimWhitespaceASCII(output, base::TRIM_TRAILING) != |
| "not provisioned"; |
| } |
| |
| } // namespace rmad |