| // Copyright 2013 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "update_engine/cros/hardware_chromeos.h" | |
| #include <optional> | |
| #include <utility> | |
| #include <base/files/file_path.h> | |
| #include <base/files/file_util.h> | |
| #include <base/json/json_file_value_serializer.h> | |
| #include <base/logging.h> | |
| #include <base/strings/string_number_conversions.h> | |
| #include <base/strings/string_util.h> | |
| #include <brillo/blkdev_utils/lvm.h> | |
| #include <brillo/key_value_store.h> | |
| #include <debugd/dbus-constants.h> | |
| #include <libcrossystem/crossystem.h> | |
| #include <vboot/crossystem.h> | |
| #include <vpd/vpd.h> | |
| extern "C" { | |
| #include "vboot/vboot_host.h" | |
| } | |
| #include "update_engine/common/constants.h" | |
| #include "update_engine/common/hardware.h" | |
| #include "update_engine/common/hwid_override.h" | |
| #include "update_engine/common/platform_constants.h" | |
| #include "update_engine/common/subprocess.h" | |
| #include "update_engine/common/system_state.h" | |
| #include "update_engine/common/utils.h" | |
| #include "update_engine/cros/boot_control_chromeos.h" | |
| #include "update_engine/cros/dbus_connection.h" | |
| using std::string; | |
| using std::vector; | |
| namespace { | |
| const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed"; | |
| // The stateful directory used by update_engine to store powerwash-safe files. | |
| // The files stored here must be added to the powerwash script allowlist. | |
| const char kPowerwashSafeDirectory[] = | |
| "/mnt/stateful_partition/unencrypted/preserve"; | |
| // The powerwash_count marker file contains the number of times the device was | |
| // powerwashed. This value is incremented by the clobber-state script when | |
| // a powerwash is performed. | |
| const char kPowerwashCountMarker[] = "powerwash_count"; | |
| // The path of the marker file used to trigger powerwash when post-install | |
| // completes successfully so that the device is powerwashed on next reboot. | |
| constexpr char kPowerwashMarkerPath[] = | |
| "mnt/stateful_partition/factory_install_reset"; | |
| // Expected tag in the powerwash marker file that indicates that | |
| // powerwash is initiated by the update engine. | |
| constexpr char kPowerwashReasonUpdateEngineTag[] = "reason=update_engine"; | |
| // The name of the marker file used to trigger a save of rollback data | |
| // during the next shutdown. | |
| const char kRollbackSaveMarkerFile[] = | |
| "/mnt/stateful_partition/.save_rollback_data"; | |
| // The contents of the powerwash marker file for the non-rollback case. | |
| const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n"; | |
| // The contents of the powerwas marker file for the rollback case. | |
| const char kRollbackPowerwashCommand[] = | |
| "safe fast keepimg rollback reason=update_engine\n"; | |
| #if USE_LVM_STATEFUL_PARTITION | |
| // Powerwash marker when preserving logical volumes. | |
| // Append at the front. | |
| const char kPowerwashPreserveLVs[] = "preserve_lvs"; | |
| #endif // USE_LVM_STATEFUL_PARTITION | |
| // UpdateManager config path. | |
| const char* kConfigFilePath = "/etc/update_manager.conf"; | |
| // UpdateManager config options: | |
| const char* kConfigOptsIsOOBEEnabled = "is_oobe_enabled"; | |
| const char* kActivePingKey = "first_active_omaha_ping_sent"; | |
| // The week when the device was first used. | |
| const char* kActivateDateVpdKey = "ActivateDate"; | |
| // The FSI version the device shipped with. | |
| const char* kFsiVersionVpdKey = "fsi_version"; | |
| // The VPD key for managed device. | |
| const char* kCheckEnrollmentKey = "check_enrollment"; | |
| // Vboot MiniOS booting priority flag. | |
| const char kMiniOsPriorityFlag[] = "minios_priority"; | |
| const char kKernelCmdline[] = "proc/cmdline"; | |
| const char kRunningFromMiniOSLabel[] = "cros_minios"; | |
| constexpr char kLocalStatePath[] = "/home/chronos/Local State"; | |
| constexpr char kEnrollmentRecoveryRequired[] = "EnrollmentRecoveryRequired"; | |
| constexpr char kConsumerSegment[] = "IsConsumerSegment"; | |
| // Firmware slot to try next (A or B). | |
| constexpr char kFWTryNextFlag[] = "fw_try_next"; | |
| // Current main firmware. | |
| constexpr char kMainFWActFlag[] = "mainfw_act"; | |
| // Firmware boot result this boot. | |
| constexpr char kFWResultFlag[] = "fw_result"; | |
| // Number of times to try to boot `kFWTryNextFlag` slot. | |
| constexpr char kFWTryCountFlag[] = "fw_try_count"; | |
| // Firmware partition slots. | |
| constexpr char kFWSlotA[] = "A"; | |
| constexpr char kFWSlotB[] = "B"; | |
| } // namespace | |
| namespace chromeos_update_engine { | |
| namespace hardware { | |
| // Factory defined in hardware.h. | |
| std::unique_ptr<HardwareInterface> CreateHardware() { | |
| std::unique_ptr<HardwareChromeOS> hardware(new HardwareChromeOS()); | |
| hardware->Init(); | |
| return std::move(hardware); | |
| } | |
| } // namespace hardware | |
| HardwareChromeOS::HardwareChromeOS() | |
| : root_("/"), non_volatile_path_(constants::kNonVolatileDirectory) {} | |
| void HardwareChromeOS::Init() { | |
| LoadConfig("" /* root_prefix */, IsNormalBootMode()); | |
| debugd_proxy_.reset( | |
| new org::chromium::debugdProxy(DBusConnection::Get()->GetDBus())); | |
| crossystem_.reset(new crossystem::Crossystem()); | |
| } | |
| bool HardwareChromeOS::IsOfficialBuild() const { | |
| return VbGetSystemPropertyInt("debug_build") == 0; | |
| } | |
| bool HardwareChromeOS::IsNormalBootMode() const { | |
| return VbGetSystemPropertyInt("devsw_boot") == 0; | |
| } | |
| bool HardwareChromeOS::IsRunningFromMiniOs() const { | |
| // Look up the current kernel command line. | |
| string kernel_cmd_line; | |
| if (!base::ReadFileToString(base::FilePath(root_.Append(kKernelCmdline)), | |
| &kernel_cmd_line)) { | |
| LOG(ERROR) << "Can't read kernel commandline options."; | |
| return false; | |
| } | |
| size_t match_start = 0; | |
| while ((match_start = kernel_cmd_line.find( | |
| kRunningFromMiniOSLabel, match_start)) != std::string::npos) { | |
| // Make sure the MiniOS flag is not a part of any other key by checking for | |
| // a space or a quote after it, except if it is at the end of the string. | |
| match_start += sizeof(kRunningFromMiniOSLabel) - 1; | |
| if (match_start < kernel_cmd_line.size() && | |
| kernel_cmd_line[match_start] != ' ' && | |
| kernel_cmd_line[match_start] != '"') { | |
| // Ignore partial matches. | |
| continue; | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool HardwareChromeOS::AreDevFeaturesEnabled() const { | |
| // Even though the debugd tools are also gated on devmode, checking here can | |
| // save us a D-Bus call so it's worth doing explicitly. | |
| if (IsNormalBootMode()) | |
| return false; | |
| int32_t dev_features = debugd::DEV_FEATURES_DISABLED; | |
| brillo::ErrorPtr error; | |
| // Some boards may not include debugd so it's expected that this may fail, | |
| // in which case we treat it as disabled. | |
| if (debugd_proxy_ && debugd_proxy_->QueryDevFeatures(&dev_features, &error) && | |
| !(dev_features & debugd::DEV_FEATURES_DISABLED)) { | |
| LOG(INFO) << "Debugd dev tools enabled."; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool HardwareChromeOS::IsOOBEEnabled() const { | |
| return is_oobe_enabled_; | |
| } | |
| bool HardwareChromeOS::IsOOBEComplete(base::Time* out_time_of_oobe) const { | |
| if (!is_oobe_enabled_) { | |
| LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() was called"; | |
| } | |
| struct stat statbuf; | |
| if (stat(kOOBECompletedMarker, &statbuf) != 0) { | |
| if (errno != ENOENT) { | |
| PLOG(ERROR) << "Error getting information about " << kOOBECompletedMarker; | |
| } | |
| return false; | |
| } | |
| if (out_time_of_oobe != nullptr) | |
| *out_time_of_oobe = base::Time::FromTimeT(statbuf.st_mtime); | |
| return true; | |
| } | |
| static string ReadValueFromCrosSystem(const string& key) { | |
| char value_buffer[VB_MAX_STRING_PROPERTY]; | |
| if (VbGetSystemPropertyString(key.c_str(), value_buffer, | |
| sizeof(value_buffer)) != -1) { | |
| string return_value(value_buffer); | |
| base::TrimWhitespaceASCII(return_value, base::TRIM_ALL, &return_value); | |
| return return_value; | |
| } | |
| LOG(ERROR) << "Unable to read crossystem key " << key; | |
| return ""; | |
| } | |
| string HardwareChromeOS::GetHardwareClass() const { | |
| if (USE_HWID_OVERRIDE) { | |
| return HwidOverride::Read(base::FilePath("/")); | |
| } | |
| return ReadValueFromCrosSystem("hwid"); | |
| } | |
| int HardwareChromeOS::GetMinKernelKeyVersion() const { | |
| return VbGetSystemPropertyInt("tpm_kernver"); | |
| } | |
| int HardwareChromeOS::GetMaxFirmwareKeyRollforward() const { | |
| return VbGetSystemPropertyInt("firmware_max_rollforward"); | |
| } | |
| bool HardwareChromeOS::SetMaxFirmwareKeyRollforward( | |
| int firmware_max_rollforward) { | |
| // Not all devices have this field yet. So first try to read | |
| // it and if there is an error just fail. | |
| if (GetMaxFirmwareKeyRollforward() == -1) | |
| return false; | |
| return VbSetSystemPropertyInt("firmware_max_rollforward", | |
| firmware_max_rollforward) == 0; | |
| } | |
| int HardwareChromeOS::GetMinFirmwareKeyVersion() const { | |
| return VbGetSystemPropertyInt("tpm_fwver"); | |
| } | |
| bool HardwareChromeOS::SetMaxKernelKeyRollforward(int kernel_max_rollforward) { | |
| return VbSetSystemPropertyInt("kernel_max_rollforward", | |
| kernel_max_rollforward) == 0; | |
| } | |
| int HardwareChromeOS::GetPowerwashCount() const { | |
| int powerwash_count; | |
| base::FilePath marker_path = | |
| base::FilePath(kPowerwashSafeDirectory).Append(kPowerwashCountMarker); | |
| string contents; | |
| if (!utils::ReadFile(marker_path.value(), &contents)) | |
| return -1; | |
| base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents); | |
| if (!base::StringToInt(contents, &powerwash_count)) | |
| return -1; | |
| return powerwash_count; | |
| } | |
| std::string HardwareChromeOS::GeneratePowerwashCommand( | |
| bool save_rollback_data) const { | |
| std::string powerwash_command = | |
| save_rollback_data ? kRollbackPowerwashCommand : kPowerwashCommand; | |
| #if USE_LVM_STATEFUL_PARTITION | |
| brillo::LogicalVolumeManager lvm; | |
| if (SystemState::Get()->boot_control()->IsLvmStackEnabled(&lvm)) { | |
| powerwash_command = | |
| base::JoinString({kPowerwashPreserveLVs, powerwash_command}, " "); | |
| } else { | |
| LOG(WARNING) << "LVM stack is not enabled, skipping " | |
| << kPowerwashPreserveLVs << " during powerwash."; | |
| } | |
| #endif // USE_LVM_STATEFUL_PARTITION | |
| return powerwash_command; | |
| } | |
| bool HardwareChromeOS::SchedulePowerwash(bool save_rollback_data) { | |
| if (save_rollback_data) { | |
| if (!utils::WriteFile(kRollbackSaveMarkerFile, nullptr, 0)) { | |
| PLOG(ERROR) << "Error in creating rollback save marker file: " | |
| << kRollbackSaveMarkerFile << ". Rollback will not" | |
| << " preserve any data."; | |
| } else { | |
| LOG(INFO) << "Rollback data save has been scheduled on next shutdown."; | |
| } | |
| } | |
| auto powerwash_command = GeneratePowerwashCommand(save_rollback_data); | |
| const std::string powerwash_marker_full_path = | |
| GetPowerwashMarkerFullPath().value(); | |
| bool result = | |
| utils::WriteFile(powerwash_marker_full_path.c_str(), | |
| powerwash_command.data(), powerwash_command.size()); | |
| if (result) { | |
| LOG(INFO) << "Created " << powerwash_marker_full_path | |
| << " to powerwash on next reboot (" | |
| << "save_rollback_data=" << save_rollback_data << ")"; | |
| } else { | |
| PLOG(ERROR) << "Error in creating powerwash marker file: " | |
| << powerwash_marker_full_path; | |
| } | |
| return result; | |
| } | |
| std::optional<bool> HardwareChromeOS::IsPowerwashScheduledByUpdateEngine() | |
| const { | |
| const std::string powerwash_marker_full_path = | |
| GetPowerwashMarkerFullPath().value(); | |
| if (!utils::FileExists(powerwash_marker_full_path.c_str())) { | |
| return std::nullopt; | |
| } | |
| std::string contents; | |
| if (!utils::ReadFile(powerwash_marker_full_path, &contents)) { | |
| LOG(ERROR) << "Failed to read the powerwash marker file."; | |
| return false; | |
| } | |
| return contents.find(kPowerwashReasonUpdateEngineTag) != std::string::npos; | |
| } | |
| bool HardwareChromeOS::CancelPowerwash() { | |
| const base::FilePath powerwash_marker_full_path = | |
| GetPowerwashMarkerFullPath(); | |
| bool result = base::DeleteFile(powerwash_marker_full_path); | |
| if (result) { | |
| LOG(INFO) << "Successfully deleted the powerwash marker file : " | |
| << powerwash_marker_full_path; | |
| } else { | |
| PLOG(ERROR) << "Could not delete the powerwash marker file : " | |
| << powerwash_marker_full_path; | |
| } | |
| // Delete the rollback save marker file if it existed. | |
| if (!base::DeleteFile(base::FilePath(kRollbackSaveMarkerFile))) { | |
| PLOG(ERROR) << "Could not remove rollback save marker"; | |
| } | |
| return result; | |
| } | |
| bool HardwareChromeOS::GetNonVolatileDirectory(base::FilePath* path) const { | |
| *path = non_volatile_path_; | |
| return true; | |
| } | |
| bool HardwareChromeOS::GetRecoveryKeyVersion(std::string* version) { | |
| // Returned the cached value to read once per boot if read successfully. | |
| if (!recovery_key_version_.empty()) { | |
| *version = recovery_key_version_; | |
| return true; | |
| } | |
| // Clear for safety. | |
| version->clear(); | |
| base::FilePath non_volatile_path; | |
| if (!GetNonVolatileDirectory(&non_volatile_path)) { | |
| LOG(ERROR) << "Failed to get non-volatile path."; | |
| return false; | |
| } | |
| auto recovery_key_version_path = | |
| non_volatile_path.Append(constants::kRecoveryKeyVersionFileName); | |
| // Use temporary version string to return empty string on read failure. | |
| string tmp_version; | |
| if (!base::ReadFileToString(recovery_key_version_path, &tmp_version)) { | |
| LOG(ERROR) << "Failed to read recovery key version file at: " | |
| << recovery_key_version_path.value(); | |
| return false; | |
| } | |
| base::TrimWhitespaceASCII(tmp_version, base::TRIM_ALL, &tmp_version); | |
| // Check that the version is a valid string of integer. | |
| int x; | |
| if (!base::StringToInt(tmp_version, &x)) { | |
| LOG(ERROR) << "Recovery key version file does not hold a valid version: " | |
| << tmp_version; | |
| return false; | |
| } | |
| // Only perfect conversions above return true, so safe to return the string | |
| // itself without using `NumberToString(...)` or alike. | |
| *version = tmp_version; | |
| return true; | |
| } | |
| bool HardwareChromeOS::GetPowerwashSafeDirectory(base::FilePath* path) const { | |
| *path = base::FilePath(kPowerwashSafeDirectory); | |
| return true; | |
| } | |
| int64_t HardwareChromeOS::GetBuildTimestamp() const { | |
| // TODO(senj): implement this in Chrome OS. | |
| return 0; | |
| } | |
| void HardwareChromeOS::LoadConfig(const string& root_prefix, bool normal_mode) { | |
| brillo::KeyValueStore store; | |
| if (normal_mode) { | |
| store.Load(base::FilePath(root_prefix + kConfigFilePath)); | |
| } else { | |
| if (store.Load(base::FilePath(root_prefix + kStatefulPartition + | |
| kConfigFilePath))) { | |
| LOG(INFO) << "UpdateManager Config loaded from stateful partition."; | |
| } else { | |
| store.Load(base::FilePath(root_prefix + kConfigFilePath)); | |
| } | |
| } | |
| if (!store.GetBoolean(kConfigOptsIsOOBEEnabled, &is_oobe_enabled_)) | |
| is_oobe_enabled_ = true; // Default value. | |
| } | |
| bool HardwareChromeOS::GetFirstActiveOmahaPingSent() const { | |
| auto active_ping_str = vpd::Vpd().GetValue(vpd::VpdRw, kActivePingKey); | |
| if (!active_ping_str) { | |
| return false; | |
| } | |
| int active_ping; | |
| if (active_ping_str->empty() || | |
| !base::StringToInt(*active_ping_str, &active_ping)) { | |
| LOG(INFO) << "Failed to parse active_ping value: " << *active_ping_str; | |
| return false; | |
| } | |
| return static_cast<bool>(active_ping); | |
| } | |
| bool HardwareChromeOS::SetFirstActiveOmahaPingSent() { | |
| if (!vpd::Vpd().WriteValue(vpd::VpdRw, kActivePingKey, "1")) { | |
| LOG(ERROR) << "Failed to set vpd key for " << kActivePingKey; | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::string HardwareChromeOS::GetActivateDate() const { | |
| auto activate_date = vpd::Vpd().GetValue(vpd::VpdRw, kActivateDateVpdKey); | |
| if (!activate_date) { | |
| return ""; | |
| } | |
| return *activate_date; | |
| } | |
| std::string HardwareChromeOS::GetFsiVersion() const { | |
| auto fsi_version = vpd::Vpd().GetValue(vpd::VpdRo, kFsiVersionVpdKey); | |
| if (!fsi_version) { | |
| return ""; | |
| } | |
| return *fsi_version; | |
| } | |
| bool HardwareChromeOS::GetCheckEnrollment() const { | |
| auto check_enrollment = vpd::Vpd().GetValue(vpd::VpdRw, kCheckEnrollmentKey); | |
| if (!check_enrollment) { | |
| return false; | |
| } | |
| return *check_enrollment == "1"; | |
| } | |
| std::unique_ptr<base::Value> HardwareChromeOS::ReadLocalState() const { | |
| base::FilePath local_state_file = base::FilePath(kLocalStatePath); | |
| JSONFileValueDeserializer deserializer(local_state_file); | |
| int error_code; | |
| std::string error_msg; | |
| std::unique_ptr<base::Value> root = | |
| deserializer.Deserialize(&error_code, &error_msg); | |
| if (!root) { | |
| if (error_code != 0) { | |
| LOG(ERROR) << "Unable to deserialize Local State with exit code: " | |
| << error_code << " and error: " << error_msg; | |
| } | |
| return nullptr; | |
| } | |
| return root; | |
| } | |
| // Check for given given Local State the value of the enrollment | |
| // recovery mode. Returns true if Recoverymode is set on CrOS. | |
| bool HardwareChromeOS::IsEnrollmentRecoveryModeEnabled( | |
| const base::Value* local_state) const { | |
| if (!local_state) { | |
| return false; | |
| } | |
| auto& local_state_dict = local_state->GetDict(); | |
| auto* path = local_state_dict.FindByDottedPath(kEnrollmentRecoveryRequired); | |
| if (!path || !path->is_bool()) { | |
| LOG(INFO) << "EnrollmentRecoveryRequired path does not exist in" | |
| << "Local State or is incorrectly formatted."; | |
| return false; | |
| } | |
| return path->GetBool(); | |
| } | |
| // Check for given given Local State the value of the consumer | |
| // segment. Returns true if IsConsumerSegement is set on CrOS. | |
| bool HardwareChromeOS::IsConsumerSegmentSet( | |
| const base::Value* local_state) const { | |
| if (!local_state) { | |
| return false; | |
| } | |
| auto& local_state_dict = local_state->GetDict(); | |
| auto* path = local_state_dict.FindByDottedPath(kConsumerSegment); | |
| if (!path) { | |
| LOG(INFO) << "IsConsumerSegment path does not exist in Local State."; | |
| return false; | |
| } | |
| if (!path->is_bool()) { | |
| LOG(INFO) << "IsConsumerSegment is incorrectly formatted in Local State."; | |
| return false; | |
| } | |
| return path->GetBool(); | |
| } | |
| int HardwareChromeOS::GetActiveMiniOsPartition() const { | |
| char value_buffer[VB_MAX_STRING_PROPERTY]; | |
| if (VbGetSystemPropertyString(kMiniOsPriorityFlag, value_buffer, | |
| sizeof(value_buffer)) == -1) { | |
| LOG(WARNING) << "Unable to get the active MiniOS partition from " | |
| << kMiniOsPriorityFlag << ", defaulting to MINIOS-A."; | |
| return 0; | |
| } | |
| return (std::string(value_buffer) == "A") ? 0 : 1; | |
| } | |
| bool HardwareChromeOS::SetActiveMiniOsPartition(int active_partition) { | |
| std::string partition = active_partition == 0 ? "A" : "B"; | |
| return VbSetSystemPropertyString(kMiniOsPriorityFlag, partition.c_str()) == 0; | |
| } | |
| void HardwareChromeOS::SetWarmReset(bool warm_reset) {} | |
| std::string HardwareChromeOS::GetVersionForLogging( | |
| const std::string& partition_name) const { | |
| // TODO(zhangkelvin) Implement per-partition timestamp for Chrome OS. | |
| return ""; | |
| } | |
| ErrorCode HardwareChromeOS::IsPartitionUpdateValid( | |
| const std::string& partition_name, const std::string& new_version) const { | |
| // TODO(zhangkelvin) Implement per-partition timestamp for Chrome OS. | |
| return ErrorCode::kSuccess; | |
| } | |
| bool HardwareChromeOS::IsRootfsVerificationEnabled() const { | |
| std::string kernel_cmd_line; | |
| if (!base::ReadFileToString(base::FilePath(root_.Append(kKernelCmdline)), | |
| &kernel_cmd_line)) { | |
| LOG(ERROR) << "Can't read kernel commandline options."; | |
| return false; | |
| } | |
| return kernel_cmd_line.find("dm_verity.dev_wait=1") != std::string::npos; | |
| } | |
| bool HardwareChromeOS::ResetFWTryNextSlot() { | |
| const std::optional<std::string> main_fw_act = GetMainFWAct(); | |
| const int fw_try_count = 0; | |
| if (!main_fw_act) { | |
| return false; | |
| } | |
| return SetFWTryNextSlot(*main_fw_act) && SetFWResultSuccessful() && | |
| SetFWTryCount(fw_try_count); | |
| } | |
| bool HardwareChromeOS::SetFWTryNextSlot(std::string_view target_slot) { | |
| DCHECK(crossystem_); | |
| if (target_slot != kFWSlotA && target_slot != kFWSlotB) { | |
| LOG(ERROR) << "Invalid target_slot " << target_slot; | |
| return false; | |
| } | |
| if (!crossystem_->VbSetSystemPropertyString(kFWTryNextFlag, | |
| target_slot.data())) { | |
| LOG(ERROR) << "Unable to set " << kFWTryNextFlag << " to " | |
| << target_slot.data(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::optional<std::string> HardwareChromeOS::GetMainFWAct() const { | |
| DCHECK(crossystem_); | |
| const std::optional<std::string> main_fw_act = | |
| crossystem_->VbGetSystemPropertyString(kMainFWActFlag); | |
| if (!main_fw_act) { | |
| LOG(ERROR) << "Unable to get a current FW slot from " << kMainFWActFlag; | |
| return std::nullopt; | |
| } | |
| return *main_fw_act; | |
| } | |
| bool HardwareChromeOS::SetFWResultSuccessful() { | |
| DCHECK(crossystem_); | |
| if (!crossystem_->VbSetSystemPropertyString(kFWResultFlag, "success")) { | |
| LOG(ERROR) << "Unable to set " << kFWResultFlag << " to success"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool HardwareChromeOS::SetFWTryCount(int count) { | |
| DCHECK(crossystem_); | |
| if (!crossystem_->VbSetSystemPropertyInt(kFWTryCountFlag, count)) { | |
| LOG(ERROR) << "Unable to set " << kFWTryCountFlag << " to " << count; | |
| return false; | |
| } | |
| return true; | |
| } | |
| base::FilePath HardwareChromeOS::GetPowerwashMarkerFullPath() const { | |
| return root_.Append(kPowerwashMarkerPath); | |
| } | |
| bool HardwareChromeOS::IsManagedDeviceInOobe() const { | |
| return IsOOBEEnabled() && !IsOOBEComplete(nullptr) && GetCheckEnrollment(); | |
| } | |
| } // namespace chromeos_update_engine |