boot_control_chromeos.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. //
  2. // Copyright (C) 2015 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. #include "update_engine/boot_control_chromeos.h"
  17. #include <memory>
  18. #include <string>
  19. #include <utility>
  20. #include <base/bind.h>
  21. #include <base/files/file_path.h>
  22. #include <base/files/file_util.h>
  23. #include <base/strings/string_util.h>
  24. #include <rootdev/rootdev.h>
  25. extern "C" {
  26. #include <vboot/vboot_host.h>
  27. }
  28. #include "update_engine/common/boot_control.h"
  29. #include "update_engine/common/subprocess.h"
  30. #include "update_engine/common/utils.h"
  31. using std::string;
  32. namespace {
  33. const char* kChromeOSPartitionNameKernel = "kernel";
  34. const char* kChromeOSPartitionNameRoot = "root";
  35. const char* kAndroidPartitionNameKernel = "boot";
  36. const char* kAndroidPartitionNameRoot = "system";
  37. const char kDlcInstallRootDirectoryEncrypted[] = "/home/chronos/dlc";
  38. const char kPartitionNamePrefixDlc[] = "dlc_";
  39. const char kPartitionNameDlcA[] = "dlc_a";
  40. const char kPartitionNameDlcB[] = "dlc_b";
  41. const char kPartitionNameDlcImage[] = "dlc.img";
  42. // Returns the currently booted rootfs partition. "/dev/sda3", for example.
  43. string GetBootDevice() {
  44. char boot_path[PATH_MAX];
  45. // Resolve the boot device path fully, including dereferencing through
  46. // dm-verity.
  47. int ret = rootdev(boot_path, sizeof(boot_path), true, false);
  48. if (ret < 0) {
  49. LOG(ERROR) << "rootdev failed to find the root device";
  50. return "";
  51. }
  52. LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node";
  53. // This local variable is used to construct the return string and is not
  54. // passed around after use.
  55. return boot_path;
  56. }
  57. // ExecCallback called when the execution of setgoodkernel finishes. Notifies
  58. // the caller of MarkBootSuccessfullAsync() by calling |callback| with the
  59. // result.
  60. void OnMarkBootSuccessfulDone(base::Callback<void(bool)> callback,
  61. int return_code,
  62. const string& output) {
  63. callback.Run(return_code == 0);
  64. }
  65. } // namespace
  66. namespace chromeos_update_engine {
  67. namespace boot_control {
  68. // Factory defined in boot_control.h.
  69. std::unique_ptr<BootControlInterface> CreateBootControl() {
  70. std::unique_ptr<BootControlChromeOS> boot_control_chromeos(
  71. new BootControlChromeOS());
  72. if (!boot_control_chromeos->Init()) {
  73. LOG(ERROR) << "Ignoring BootControlChromeOS failure. We won't run updates.";
  74. }
  75. return std::move(boot_control_chromeos);
  76. }
  77. } // namespace boot_control
  78. bool BootControlChromeOS::Init() {
  79. string boot_device = GetBootDevice();
  80. if (boot_device.empty())
  81. return false;
  82. int partition_num;
  83. if (!utils::SplitPartitionName(boot_device, &boot_disk_name_, &partition_num))
  84. return false;
  85. // All installed Chrome OS devices have two slots. We don't update removable
  86. // devices, so we will pretend we have only one slot in that case.
  87. if (IsRemovableDevice(boot_disk_name_)) {
  88. LOG(INFO)
  89. << "Booted from a removable device, pretending we have only one slot.";
  90. num_slots_ = 1;
  91. } else {
  92. // TODO(deymo): Look at the actual number of slots reported in the GPT.
  93. num_slots_ = 2;
  94. }
  95. // Search through the slots to see which slot has the partition_num we booted
  96. // from. This should map to one of the existing slots, otherwise something is
  97. // very wrong.
  98. current_slot_ = 0;
  99. while (current_slot_ < num_slots_ &&
  100. partition_num !=
  101. GetPartitionNumber(kChromeOSPartitionNameRoot, current_slot_)) {
  102. current_slot_++;
  103. }
  104. if (current_slot_ >= num_slots_) {
  105. LOG(ERROR) << "Couldn't find the slot number corresponding to the "
  106. << "partition " << boot_device << ", number of slots: "
  107. << num_slots_ << ". This device is not updateable.";
  108. num_slots_ = 1;
  109. current_slot_ = BootControlInterface::kInvalidSlot;
  110. return false;
  111. }
  112. LOG(INFO) << "Booted from slot " << current_slot_ << " (slot "
  113. << SlotName(current_slot_) << ") of " << num_slots_
  114. << " slots present on disk " << boot_disk_name_;
  115. return true;
  116. }
  117. unsigned int BootControlChromeOS::GetNumSlots() const {
  118. return num_slots_;
  119. }
  120. BootControlInterface::Slot BootControlChromeOS::GetCurrentSlot() const {
  121. return current_slot_;
  122. }
  123. bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
  124. unsigned int slot,
  125. string* device) const {
  126. // Partition name prefixed with |kPartitionNamePrefixDlc| is a DLC module.
  127. if (base::StartsWith(partition_name,
  128. kPartitionNamePrefixDlc,
  129. base::CompareCase::SENSITIVE)) {
  130. // Extract DLC module ID from partition_name (DLC module ID is the string
  131. // after |kPartitionNamePrefixDlc| in partition_name).
  132. const auto dlc_module_id =
  133. partition_name.substr(strlen(kPartitionNamePrefixDlc));
  134. if (dlc_module_id.empty()) {
  135. LOG(ERROR) << " partition name does not contain DLC module ID:"
  136. << partition_name;
  137. return false;
  138. }
  139. *device = base::FilePath(kDlcInstallRootDirectoryEncrypted)
  140. .Append(dlc_module_id)
  141. .Append(slot == 0 ? kPartitionNameDlcA : kPartitionNameDlcB)
  142. .Append(kPartitionNameDlcImage)
  143. .value();
  144. return true;
  145. }
  146. int partition_num = GetPartitionNumber(partition_name, slot);
  147. if (partition_num < 0)
  148. return false;
  149. string part_device = utils::MakePartitionName(boot_disk_name_, partition_num);
  150. if (part_device.empty())
  151. return false;
  152. *device = part_device;
  153. return true;
  154. }
  155. bool BootControlChromeOS::IsSlotBootable(Slot slot) const {
  156. int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
  157. if (partition_num < 0)
  158. return false;
  159. CgptAddParams params;
  160. memset(&params, '\0', sizeof(params));
  161. params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
  162. params.partition = partition_num;
  163. int retval = CgptGetPartitionDetails(&params);
  164. if (retval != CGPT_OK)
  165. return false;
  166. return params.successful || params.tries > 0;
  167. }
  168. bool BootControlChromeOS::MarkSlotUnbootable(Slot slot) {
  169. LOG(INFO) << "Marking slot " << SlotName(slot) << " unbootable";
  170. if (slot == current_slot_) {
  171. LOG(ERROR) << "Refusing to mark current slot as unbootable.";
  172. return false;
  173. }
  174. int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
  175. if (partition_num < 0)
  176. return false;
  177. CgptAddParams params;
  178. memset(&params, 0, sizeof(params));
  179. params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
  180. params.partition = partition_num;
  181. params.successful = false;
  182. params.set_successful = true;
  183. params.tries = 0;
  184. params.set_tries = true;
  185. int retval = CgptSetAttributes(&params);
  186. if (retval != CGPT_OK) {
  187. LOG(ERROR) << "Marking kernel unbootable failed.";
  188. return false;
  189. }
  190. return true;
  191. }
  192. bool BootControlChromeOS::SetActiveBootSlot(Slot slot) {
  193. LOG(INFO) << "Marking slot " << SlotName(slot) << " active.";
  194. int partition_num = GetPartitionNumber(kChromeOSPartitionNameKernel, slot);
  195. if (partition_num < 0)
  196. return false;
  197. CgptPrioritizeParams prio_params;
  198. memset(&prio_params, 0, sizeof(prio_params));
  199. prio_params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
  200. prio_params.set_partition = partition_num;
  201. prio_params.max_priority = 0;
  202. int retval = CgptPrioritize(&prio_params);
  203. if (retval != CGPT_OK) {
  204. LOG(ERROR) << "Unable to set highest priority for slot " << SlotName(slot)
  205. << " (partition " << partition_num << ").";
  206. return false;
  207. }
  208. CgptAddParams add_params;
  209. memset(&add_params, 0, sizeof(add_params));
  210. add_params.drive_name = const_cast<char*>(boot_disk_name_.c_str());
  211. add_params.partition = partition_num;
  212. add_params.tries = 6;
  213. add_params.set_tries = true;
  214. retval = CgptSetAttributes(&add_params);
  215. if (retval != CGPT_OK) {
  216. LOG(ERROR) << "Unable to set NumTriesLeft to " << add_params.tries
  217. << " for slot " << SlotName(slot) << " (partition "
  218. << partition_num << ").";
  219. return false;
  220. }
  221. return true;
  222. }
  223. bool BootControlChromeOS::MarkBootSuccessfulAsync(
  224. base::Callback<void(bool)> callback) {
  225. return Subprocess::Get().Exec(
  226. {"/usr/sbin/chromeos-setgoodkernel"},
  227. base::Bind(&OnMarkBootSuccessfulDone, callback)) != 0;
  228. }
  229. // static
  230. string BootControlChromeOS::SysfsBlockDevice(const string& device) {
  231. base::FilePath device_path(device);
  232. if (device_path.DirName().value() != "/dev") {
  233. return "";
  234. }
  235. return base::FilePath("/sys/block").Append(device_path.BaseName()).value();
  236. }
  237. // static
  238. bool BootControlChromeOS::IsRemovableDevice(const string& device) {
  239. string sysfs_block = SysfsBlockDevice(device);
  240. string removable;
  241. if (sysfs_block.empty() ||
  242. !base::ReadFileToString(base::FilePath(sysfs_block).Append("removable"),
  243. &removable)) {
  244. return false;
  245. }
  246. base::TrimWhitespaceASCII(removable, base::TRIM_ALL, &removable);
  247. return removable == "1";
  248. }
  249. int BootControlChromeOS::GetPartitionNumber(
  250. const string partition_name, BootControlInterface::Slot slot) const {
  251. if (slot >= num_slots_) {
  252. LOG(ERROR) << "Invalid slot number: " << slot << ", we only have "
  253. << num_slots_ << " slot(s)";
  254. return -1;
  255. }
  256. // In Chrome OS, the partition numbers are hard-coded:
  257. // KERNEL-A=2, ROOT-A=3, KERNEL-B=4, ROOT-B=4, ...
  258. // To help compatibility between different we accept both lowercase and
  259. // uppercase names in the ChromeOS or Brillo standard names.
  260. // See http://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format
  261. string partition_lower = base::ToLowerASCII(partition_name);
  262. int base_part_num = 2 + 2 * slot;
  263. if (partition_lower == kChromeOSPartitionNameKernel ||
  264. partition_lower == kAndroidPartitionNameKernel)
  265. return base_part_num + 0;
  266. if (partition_lower == kChromeOSPartitionNameRoot ||
  267. partition_lower == kAndroidPartitionNameRoot)
  268. return base_part_num + 1;
  269. LOG(ERROR) << "Unknown Chrome OS partition name \"" << partition_name << "\"";
  270. return -1;
  271. }
  272. bool BootControlChromeOS::InitPartitionMetadata(
  273. Slot slot,
  274. const PartitionMetadata& partition_metadata,
  275. bool update_metadata) {
  276. return true;
  277. }
  278. void BootControlChromeOS::Cleanup() {}
  279. } // namespace chromeos_update_engine