1 // 2 // Copyright (C) 2013 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #include "update_engine/hardware_chromeos.h" 18 19 #include <utility> 20 21 #include <base/files/file_path.h> 22 #include <base/files/file_util.h> 23 #include <base/logging.h> 24 #include <base/strings/string_number_conversions.h> 25 #include <base/strings/string_util.h> 26 #include <brillo/key_value_store.h> 27 #include <debugd/dbus-constants.h> 28 #include <vboot/crossystem.h> 29 30 extern "C" { 31 #include "vboot/vboot_host.h" 32 } 33 34 #include "update_engine/common/constants.h" 35 #include "update_engine/common/hardware.h" 36 #include "update_engine/common/hwid_override.h" 37 #include "update_engine/common/platform_constants.h" 38 #include "update_engine/common/subprocess.h" 39 #include "update_engine/common/utils.h" 40 #include "update_engine/dbus_connection.h" 41 42 using std::string; 43 using std::vector; 44 45 namespace { 46 47 const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed"; 48 49 // The stateful directory used by update_engine to store powerwash-safe files. 50 // The files stored here must be whitelisted in the powerwash scripts. 51 const char kPowerwashSafeDirectory[] = 52 "/mnt/stateful_partition/unencrypted/preserve"; 53 54 // The powerwash_count marker file contains the number of times the device was 55 // powerwashed. This value is incremented by the clobber-state script when 56 // a powerwash is performed. 57 const char kPowerwashCountMarker[] = "powerwash_count"; 58 59 // The name of the marker file used to trigger powerwash when post-install 60 // completes successfully so that the device is powerwashed on next reboot. 61 const char kPowerwashMarkerFile[] = 62 "/mnt/stateful_partition/factory_install_reset"; 63 64 // The contents of the powerwash marker file for the non-rollback case. 65 const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n"; 66 67 // The contents of the powerwas marker file for the rollback case. 68 const char kRollbackPowerwashCommand[] = 69 "safe fast keepimg rollback reason=update_engine\n"; 70 71 // UpdateManager config path. 72 const char* kConfigFilePath = "/etc/update_manager.conf"; 73 74 // UpdateManager config options: 75 const char* kConfigOptsIsOOBEEnabled = "is_oobe_enabled"; 76 77 const char* kActivePingKey = "first_active_omaha_ping_sent"; 78 79 } // namespace 80 81 namespace chromeos_update_engine { 82 83 namespace hardware { 84 85 // Factory defined in hardware.h. 86 std::unique_ptr<HardwareInterface> CreateHardware() { 87 std::unique_ptr<HardwareChromeOS> hardware(new HardwareChromeOS()); 88 hardware->Init(); 89 return std::move(hardware); 90 } 91 92 } // namespace hardware 93 94 void HardwareChromeOS::Init() { 95 LoadConfig("" /* root_prefix */, IsNormalBootMode()); 96 debugd_proxy_.reset( 97 new org::chromium::debugdProxy(DBusConnection::Get()->GetDBus())); 98 } 99 100 bool HardwareChromeOS::IsOfficialBuild() const { 101 return VbGetSystemPropertyInt("debug_build") == 0; 102 } 103 104 bool HardwareChromeOS::IsNormalBootMode() const { 105 bool dev_mode = VbGetSystemPropertyInt("devsw_boot") != 0; 106 return !dev_mode; 107 } 108 109 bool HardwareChromeOS::AreDevFeaturesEnabled() const { 110 // Even though the debugd tools are also gated on devmode, checking here can 111 // save us a D-Bus call so it's worth doing explicitly. 112 if (IsNormalBootMode()) 113 return false; 114 115 int32_t dev_features = debugd::DEV_FEATURES_DISABLED; 116 brillo::ErrorPtr error; 117 // Some boards may not include debugd so it's expected that this may fail, 118 // in which case we treat it as disabled. 119 if (debugd_proxy_ && debugd_proxy_->QueryDevFeatures(&dev_features, &error) && 120 !(dev_features & debugd::DEV_FEATURES_DISABLED)) { 121 LOG(INFO) << "Debugd dev tools enabled."; 122 return true; 123 } 124 return false; 125 } 126 127 bool HardwareChromeOS::IsOOBEEnabled() const { 128 return is_oobe_enabled_; 129 } 130 131 bool HardwareChromeOS::IsOOBEComplete(base::Time* out_time_of_oobe) const { 132 if (!is_oobe_enabled_) { 133 LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() was called"; 134 } 135 struct stat statbuf; 136 if (stat(kOOBECompletedMarker, &statbuf) != 0) { 137 if (errno != ENOENT) { 138 PLOG(ERROR) << "Error getting information about " << kOOBECompletedMarker; 139 } 140 return false; 141 } 142 143 if (out_time_of_oobe != nullptr) 144 *out_time_of_oobe = base::Time::FromTimeT(statbuf.st_mtime); 145 return true; 146 } 147 148 static string ReadValueFromCrosSystem(const string& key) { 149 char value_buffer[VB_MAX_STRING_PROPERTY]; 150 151 const char* rv = VbGetSystemPropertyString( 152 key.c_str(), value_buffer, sizeof(value_buffer)); 153 if (rv != nullptr) { 154 string return_value(value_buffer); 155 base::TrimWhitespaceASCII(return_value, base::TRIM_ALL, &return_value); 156 return return_value; 157 } 158 159 LOG(ERROR) << "Unable to read crossystem key " << key; 160 return ""; 161 } 162 163 string HardwareChromeOS::GetHardwareClass() const { 164 if (USE_HWID_OVERRIDE) { 165 return HwidOverride::Read(base::FilePath("/")); 166 } 167 return ReadValueFromCrosSystem("hwid"); 168 } 169 170 string HardwareChromeOS::GetFirmwareVersion() const { 171 return ReadValueFromCrosSystem("fwid"); 172 } 173 174 string HardwareChromeOS::GetECVersion() const { 175 string input_line; 176 int exit_code = 0; 177 vector<string> cmd = {"/usr/sbin/mosys", "-k", "ec", "info"}; 178 179 bool success = Subprocess::SynchronousExec(cmd, &exit_code, &input_line); 180 if (!success || exit_code) { 181 LOG(ERROR) << "Unable to read ec info from mosys (" << exit_code << ")"; 182 return ""; 183 } 184 185 return utils::ParseECVersion(input_line); 186 } 187 188 int HardwareChromeOS::GetMinKernelKeyVersion() const { 189 return VbGetSystemPropertyInt("tpm_kernver"); 190 } 191 192 int HardwareChromeOS::GetMaxFirmwareKeyRollforward() const { 193 return VbGetSystemPropertyInt("firmware_max_rollforward"); 194 } 195 196 bool HardwareChromeOS::SetMaxFirmwareKeyRollforward( 197 int firmware_max_rollforward) { 198 // Not all devices have this field yet. So first try to read 199 // it and if there is an error just fail. 200 if (GetMaxFirmwareKeyRollforward() == -1) 201 return false; 202 203 return VbSetSystemPropertyInt("firmware_max_rollforward", 204 firmware_max_rollforward) == 0; 205 } 206 207 int HardwareChromeOS::GetMinFirmwareKeyVersion() const { 208 return VbGetSystemPropertyInt("tpm_fwver"); 209 } 210 211 bool HardwareChromeOS::SetMaxKernelKeyRollforward(int kernel_max_rollforward) { 212 return VbSetSystemPropertyInt("kernel_max_rollforward", 213 kernel_max_rollforward) == 0; 214 } 215 216 int HardwareChromeOS::GetPowerwashCount() const { 217 int powerwash_count; 218 base::FilePath marker_path = 219 base::FilePath(kPowerwashSafeDirectory).Append(kPowerwashCountMarker); 220 string contents; 221 if (!utils::ReadFile(marker_path.value(), &contents)) 222 return -1; 223 base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents); 224 if (!base::StringToInt(contents, &powerwash_count)) 225 return -1; 226 return powerwash_count; 227 } 228 229 bool HardwareChromeOS::SchedulePowerwash(bool is_rollback) { 230 const char* powerwash_command = 231 is_rollback ? kRollbackPowerwashCommand : kPowerwashCommand; 232 bool result = utils::WriteFile( 233 kPowerwashMarkerFile, powerwash_command, strlen(powerwash_command)); 234 if (result) { 235 LOG(INFO) << "Created " << kPowerwashMarkerFile 236 << " to powerwash on next reboot (is_rollback=" << is_rollback 237 << ")"; 238 } else { 239 PLOG(ERROR) << "Error in creating powerwash marker file: " 240 << kPowerwashMarkerFile; 241 } 242 243 return result; 244 } 245 246 bool HardwareChromeOS::CancelPowerwash() { 247 bool result = base::DeleteFile(base::FilePath(kPowerwashMarkerFile), false); 248 249 if (result) { 250 LOG(INFO) << "Successfully deleted the powerwash marker file : " 251 << kPowerwashMarkerFile; 252 } else { 253 PLOG(ERROR) << "Could not delete the powerwash marker file : " 254 << kPowerwashMarkerFile; 255 } 256 257 return result; 258 } 259 260 bool HardwareChromeOS::GetNonVolatileDirectory(base::FilePath* path) const { 261 *path = base::FilePath(constants::kNonVolatileDirectory); 262 return true; 263 } 264 265 bool HardwareChromeOS::GetPowerwashSafeDirectory(base::FilePath* path) const { 266 *path = base::FilePath(kPowerwashSafeDirectory); 267 return true; 268 } 269 270 int64_t HardwareChromeOS::GetBuildTimestamp() const { 271 // TODO(senj): implement this in Chrome OS. 272 return 0; 273 } 274 275 void HardwareChromeOS::LoadConfig(const string& root_prefix, bool normal_mode) { 276 brillo::KeyValueStore store; 277 278 if (normal_mode) { 279 store.Load(base::FilePath(root_prefix + kConfigFilePath)); 280 } else { 281 if (store.Load(base::FilePath(root_prefix + kStatefulPartition + 282 kConfigFilePath))) { 283 LOG(INFO) << "UpdateManager Config loaded from stateful partition."; 284 } else { 285 store.Load(base::FilePath(root_prefix + kConfigFilePath)); 286 } 287 } 288 289 if (!store.GetBoolean(kConfigOptsIsOOBEEnabled, &is_oobe_enabled_)) 290 is_oobe_enabled_ = true; // Default value. 291 } 292 293 bool HardwareChromeOS::GetFirstActiveOmahaPingSent() const { 294 int exit_code = 0; 295 string active_ping_str; 296 vector<string> cmd = {"vpd_get_value", kActivePingKey}; 297 if (!Subprocess::SynchronousExec(cmd, &exit_code, &active_ping_str) || 298 exit_code) { 299 LOG(ERROR) << "Failed to get vpd key for " << kActivePingKey 300 << " with exit code: " << exit_code; 301 return false; 302 } 303 304 base::TrimWhitespaceASCII(active_ping_str, base::TRIM_ALL, &active_ping_str); 305 int active_ping; 306 if (active_ping_str.empty() || 307 !base::StringToInt(active_ping_str, &active_ping)) { 308 LOG(INFO) << "Failed to parse active_ping value: " << active_ping_str; 309 return false; 310 } 311 return static_cast<bool>(active_ping); 312 } 313 314 bool HardwareChromeOS::SetFirstActiveOmahaPingSent() { 315 int exit_code = 0; 316 string output; 317 vector<string> vpd_set_cmd = { 318 "vpd", "-i", "RW_VPD", "-s", string(kActivePingKey) + "=1"}; 319 if (!Subprocess::SynchronousExec(vpd_set_cmd, &exit_code, &output) || 320 exit_code) { 321 LOG(ERROR) << "Failed to set vpd key for " << kActivePingKey 322 << " with exit code: " << exit_code << " with error: " << output; 323 return false; 324 } 325 326 vector<string> vpd_dump_cmd = {"dump_vpd_log", "--force"}; 327 if (!Subprocess::SynchronousExec(vpd_dump_cmd, &exit_code, &output) || 328 exit_code) { 329 LOG(ERROR) << "Failed to cache " << kActivePingKey << " using dump_vpd_log" 330 << " with exit code: " << exit_code << " with error: " << output; 331 return false; 332 } 333 return true; 334 } 335 336 void HardwareChromeOS::SetWarmReset(bool warm_reset) {} 337 338 } // namespace chromeos_update_engine 339