| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 1 | // Copyright (c) 2013 The Chromium OS Authors. All rights reserved. | 
 | 2 | // Use of this source code is governed by a BSD-style license that can be | 
 | 3 | // found in the LICENSE file. | 
 | 4 |  | 
 | 5 | #include "update_engine/hardware.h" | 
 | 6 |  | 
| J. Richard Barnette | 056b0ab | 2013-10-29 15:24:56 -0700 | [diff] [blame] | 7 | #include <base/file_util.h> | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 8 | #include <base/logging.h> | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 9 | #include <base/strings/string_util.h> | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 10 | #include <rootdev/rootdev.h> | 
| J. Richard Barnette | c7dd853 | 2013-10-29 16:30:46 -0700 | [diff] [blame] | 11 | #include <vboot/crossystem.h> | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 12 |  | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 13 | extern "C" { | 
 | 14 | #include "vboot/vboot_host.h" | 
 | 15 | } | 
 | 16 |  | 
| Chris Masone | f8d037f | 2014-02-19 01:53:00 +0000 | [diff] [blame] | 17 | #include "update_engine/hwid_override.h" | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 18 | #include "update_engine/subprocess.h" | 
 | 19 | #include "update_engine/utils.h" | 
 | 20 |  | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 21 | using std::string; | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 22 | using std::vector; | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 23 |  | 
| Alex Deymo | bccbc38 | 2014-04-03 13:38:55 -0700 | [diff] [blame] | 24 | namespace { | 
 | 25 |  | 
 | 26 | static const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed"; | 
 | 27 |  | 
 | 28 | }  // namespace | 
 | 29 |  | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 30 | namespace chromeos_update_engine { | 
 | 31 |  | 
| Chris Masone | f8d037f | 2014-02-19 01:53:00 +0000 | [diff] [blame] | 32 | Hardware::Hardware() {} | 
 | 33 |  | 
 | 34 | Hardware::~Hardware() {} | 
 | 35 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 36 | string Hardware::BootKernelDevice() const { | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 37 |   return utils::KernelDeviceOfBootDevice(Hardware::BootDevice()); | 
 | 38 | } | 
 | 39 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 40 | string Hardware::BootDevice() const { | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 41 |   char boot_path[PATH_MAX]; | 
 | 42 |   // Resolve the boot device path fully, including dereferencing | 
 | 43 |   // through dm-verity. | 
 | 44 |   int ret = rootdev(boot_path, sizeof(boot_path), true, false); | 
 | 45 |  | 
 | 46 |   if (ret < 0) { | 
 | 47 |     LOG(ERROR) << "rootdev failed to find the root device"; | 
 | 48 |     return ""; | 
 | 49 |   } | 
 | 50 |   LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node"; | 
 | 51 |  | 
 | 52 |   // This local variable is used to construct the return string and is not | 
 | 53 |   // passed around after use. | 
 | 54 |   return boot_path; | 
 | 55 | } | 
 | 56 |  | 
| Alex Deymo | 5708ecd | 2014-04-29 19:44:50 -0700 | [diff] [blame] | 57 | bool Hardware::IsBootDeviceRemovable() const { | 
 | 58 |   return utils::IsRemovableDevice(utils::GetDiskName(BootDevice())); | 
 | 59 | } | 
 | 60 |  | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 61 | bool Hardware::IsKernelBootable(const std::string& kernel_device, | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 62 |                                 bool* bootable) const { | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 63 |   CgptAddParams params; | 
 | 64 |   memset(¶ms, '\0', sizeof(params)); | 
 | 65 |  | 
| Alex Vakulenko | 4f5b144 | 2014-02-21 12:19:44 -0800 | [diff] [blame] | 66 |   std::string disk_name; | 
 | 67 |   int partition_num = 0; | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 68 |  | 
| Alex Vakulenko | 4f5b144 | 2014-02-21 12:19:44 -0800 | [diff] [blame] | 69 |   if (!utils::SplitPartitionName(kernel_device, &disk_name, &partition_num)) | 
 | 70 |     return false; | 
 | 71 |  | 
 | 72 |   params.drive_name = const_cast<char *>(disk_name.c_str()); | 
 | 73 |   params.partition = partition_num; | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 74 |  | 
 | 75 |   int retval = CgptGetPartitionDetails(¶ms); | 
 | 76 |   if (retval != CGPT_OK) | 
 | 77 |     return false; | 
 | 78 |  | 
 | 79 |   *bootable = params.successful || (params.tries > 0); | 
 | 80 |   return true; | 
 | 81 | } | 
 | 82 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 83 | std::vector<std::string> Hardware::GetKernelDevices() const { | 
| Alex Vakulenko | 59e253e | 2014-02-24 10:40:21 -0800 | [diff] [blame] | 84 |   LOG(INFO) << "GetAllKernelDevices"; | 
 | 85 |  | 
 | 86 |   std::string disk_name = utils::GetDiskName(Hardware::BootKernelDevice()); | 
| Alex Deymo | df632d1 | 2014-04-29 20:04:36 -0700 | [diff] [blame] | 87 |   if (disk_name.empty()) { | 
| Alex Vakulenko | 59e253e | 2014-02-24 10:40:21 -0800 | [diff] [blame] | 88 |     LOG(ERROR) << "Failed to get the cuurent kernel boot disk name"; | 
 | 89 |     return std::vector<std::string>(); | 
 | 90 |   } | 
 | 91 |  | 
 | 92 |   std::vector<std::string> devices; | 
| Alex Deymo | df632d1 | 2014-04-29 20:04:36 -0700 | [diff] [blame] | 93 |   for (int partition_num : {2, 4}) {  // for now, only #2, #4 for slot A & B | 
| Alex Vakulenko | 59e253e | 2014-02-24 10:40:21 -0800 | [diff] [blame] | 94 |     std::string device = utils::MakePartitionName(disk_name, partition_num); | 
| Alex Deymo | df632d1 | 2014-04-29 20:04:36 -0700 | [diff] [blame] | 95 |     if (!device.empty()) { | 
| Alex Vakulenko | 59e253e | 2014-02-24 10:40:21 -0800 | [diff] [blame] | 96 |       devices.push_back(std::move(device)); | 
 | 97 |     } else { | 
 | 98 |       LOG(ERROR) << "Cannot make a partition name for disk: " | 
 | 99 |                  << disk_name << ", partition: " << partition_num; | 
 | 100 |     } | 
 | 101 |   } | 
 | 102 |  | 
 | 103 |   return devices; | 
 | 104 | } | 
 | 105 |  | 
 | 106 |  | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 107 | bool Hardware::MarkKernelUnbootable(const std::string& kernel_device) { | 
 | 108 |   LOG(INFO) << "MarkPartitionUnbootable: " << kernel_device; | 
 | 109 |  | 
| Don Garrett | 6646b44 | 2013-11-13 15:29:11 -0800 | [diff] [blame] | 110 |   if (kernel_device == BootKernelDevice()) { | 
 | 111 |     LOG(ERROR) << "Refusing to mark current kernel as unbootable."; | 
 | 112 |     return false; | 
 | 113 |   } | 
 | 114 |  | 
| Alex Vakulenko | 4f5b144 | 2014-02-21 12:19:44 -0800 | [diff] [blame] | 115 |   std::string disk_name; | 
 | 116 |   int partition_num = 0; | 
 | 117 |  | 
 | 118 |   if (!utils::SplitPartitionName(kernel_device, &disk_name, &partition_num)) | 
 | 119 |     return false; | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 120 |  | 
 | 121 |   CgptAddParams params; | 
 | 122 |   memset(¶ms, 0, sizeof(params)); | 
 | 123 |  | 
| Alex Vakulenko | 4f5b144 | 2014-02-21 12:19:44 -0800 | [diff] [blame] | 124 |   params.drive_name = const_cast<char *>(disk_name.c_str()); | 
 | 125 |   params.partition = partition_num; | 
| Don Garrett | 83692e4 | 2013-11-08 10:11:30 -0800 | [diff] [blame] | 126 |  | 
 | 127 |   params.successful = false; | 
 | 128 |   params.set_successful = true; | 
 | 129 |  | 
 | 130 |   params.tries = 0; | 
 | 131 |   params.set_tries = true; | 
 | 132 |  | 
 | 133 |   int retval = CgptSetAttributes(¶ms); | 
 | 134 |   if (retval != CGPT_OK) { | 
 | 135 |     LOG(ERROR) << "Marking kernel unbootable failed."; | 
 | 136 |     return false; | 
 | 137 |   } | 
 | 138 |  | 
 | 139 |   return true; | 
 | 140 | } | 
 | 141 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 142 | bool Hardware::IsOfficialBuild() const { | 
| J. Richard Barnette | c7dd853 | 2013-10-29 16:30:46 -0700 | [diff] [blame] | 143 |   return VbGetSystemPropertyInt("debug_build") == 0; | 
| J. Richard Barnette | 056b0ab | 2013-10-29 15:24:56 -0700 | [diff] [blame] | 144 | } | 
 | 145 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 146 | bool Hardware::IsNormalBootMode() const { | 
| J. Richard Barnette | c7dd853 | 2013-10-29 16:30:46 -0700 | [diff] [blame] | 147 |   bool dev_mode = VbGetSystemPropertyInt("devsw_boot") != 0; | 
| J. Richard Barnette | 056b0ab | 2013-10-29 15:24:56 -0700 | [diff] [blame] | 148 |   LOG_IF(INFO, dev_mode) << "Booted in dev mode."; | 
 | 149 |   return !dev_mode; | 
 | 150 | } | 
 | 151 |  | 
| Alex Deymo | bccbc38 | 2014-04-03 13:38:55 -0700 | [diff] [blame] | 152 | bool Hardware::IsOOBEComplete(base::Time* out_time_of_oobe) const { | 
 | 153 |   struct stat statbuf; | 
 | 154 |   if (stat(kOOBECompletedMarker, &statbuf) != 0) { | 
 | 155 |     if (errno != ENOENT) { | 
 | 156 |       PLOG(ERROR) << "Error getting information about " | 
 | 157 |                   << kOOBECompletedMarker; | 
 | 158 |     } | 
 | 159 |     return false; | 
 | 160 |   } | 
 | 161 |  | 
 | 162 |   if (out_time_of_oobe != nullptr) | 
 | 163 |     *out_time_of_oobe = base::Time::FromTimeT(statbuf.st_mtime); | 
 | 164 |   return true; | 
 | 165 | } | 
 | 166 |  | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 167 | static string ReadValueFromCrosSystem(const string& key) { | 
| J. Richard Barnette | c7dd853 | 2013-10-29 16:30:46 -0700 | [diff] [blame] | 168 |   char value_buffer[VB_MAX_STRING_PROPERTY]; | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 169 |  | 
| J. Richard Barnette | c7dd853 | 2013-10-29 16:30:46 -0700 | [diff] [blame] | 170 |   const char *rv = VbGetSystemPropertyString(key.c_str(), value_buffer, | 
 | 171 |                                              sizeof(value_buffer)); | 
 | 172 |   if (rv != NULL) { | 
 | 173 |     string return_value(value_buffer); | 
| Ben Chan | 736fcb5 | 2014-05-21 18:28:22 -0700 | [diff] [blame] | 174 |     base::TrimWhitespaceASCII(return_value, base::TRIM_ALL, &return_value); | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 175 |     return return_value; | 
 | 176 |   } | 
| J. Richard Barnette | c7dd853 | 2013-10-29 16:30:46 -0700 | [diff] [blame] | 177 |  | 
 | 178 |   LOG(ERROR) << "Unable to read crossystem key " << key; | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 179 |   return ""; | 
 | 180 | } | 
 | 181 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 182 | string Hardware::GetHardwareClass() const { | 
| Chris Masone | f8d037f | 2014-02-19 01:53:00 +0000 | [diff] [blame] | 183 |   if (USE_HWID_OVERRIDE) { | 
 | 184 |     return HwidOverride::Read(base::FilePath("/")); | 
 | 185 |   } | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 186 |   return ReadValueFromCrosSystem("hwid"); | 
 | 187 | } | 
 | 188 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 189 | string Hardware::GetFirmwareVersion() const { | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 190 |   return ReadValueFromCrosSystem("fwid"); | 
 | 191 | } | 
 | 192 |  | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 193 | string Hardware::GetECVersion() const { | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 194 |   string input_line; | 
 | 195 |   int exit_code = 0; | 
| Alex Vakulenko | d0fdfb3 | 2014-02-21 15:26:26 -0800 | [diff] [blame] | 196 |   vector<string> cmd = {"/usr/sbin/mosys", "-k", "ec", "info"}; | 
| J. Richard Barnette | 522d36f | 2013-10-28 17:22:12 -0700 | [diff] [blame] | 197 |  | 
 | 198 |   bool success = Subprocess::SynchronousExec(cmd, &exit_code, &input_line); | 
 | 199 |   if (!success || exit_code) { | 
 | 200 |     LOG(ERROR) << "Unable to read ec info from mosys (" << exit_code << ")"; | 
 | 201 |     return ""; | 
 | 202 |   } | 
 | 203 |  | 
 | 204 |   return utils::ParseECVersion(input_line); | 
 | 205 | } | 
 | 206 |  | 
| Alex Deymo | 4243291 | 2013-07-12 20:21:15 -0700 | [diff] [blame] | 207 | }  // namespace chromeos_update_engine |