boot_control_android.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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_android.h"
  17. #include <memory>
  18. #include <utility>
  19. #include <vector>
  20. #include <base/bind.h>
  21. #include <base/logging.h>
  22. #include <base/strings/string_util.h>
  23. #include <bootloader_message/bootloader_message.h>
  24. #include <brillo/message_loops/message_loop.h>
  25. #include <fs_mgr.h>
  26. #include <fs_mgr_overlayfs.h>
  27. #include "update_engine/common/utils.h"
  28. #include "update_engine/dynamic_partition_control_android.h"
  29. using std::string;
  30. using android::dm::DmDeviceState;
  31. using android::fs_mgr::Partition;
  32. using android::hardware::hidl_string;
  33. using android::hardware::Return;
  34. using android::hardware::boot::V1_0::BoolResult;
  35. using android::hardware::boot::V1_0::CommandResult;
  36. using android::hardware::boot::V1_0::IBootControl;
  37. using Slot = chromeos_update_engine::BootControlInterface::Slot;
  38. using PartitionMetadata =
  39. chromeos_update_engine::BootControlInterface::PartitionMetadata;
  40. namespace {
  41. auto StoreResultCallback(CommandResult* dest) {
  42. return [dest](const CommandResult& result) { *dest = result; };
  43. }
  44. } // namespace
  45. namespace chromeos_update_engine {
  46. namespace boot_control {
  47. // Factory defined in boot_control.h.
  48. std::unique_ptr<BootControlInterface> CreateBootControl() {
  49. auto boot_control = std::make_unique<BootControlAndroid>();
  50. if (!boot_control->Init()) {
  51. return nullptr;
  52. }
  53. return std::move(boot_control);
  54. }
  55. } // namespace boot_control
  56. bool BootControlAndroid::Init() {
  57. module_ = IBootControl::getService();
  58. if (module_ == nullptr) {
  59. LOG(ERROR) << "Error getting bootctrl HIDL module.";
  60. return false;
  61. }
  62. LOG(INFO) << "Loaded boot control hidl hal.";
  63. dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
  64. return true;
  65. }
  66. void BootControlAndroid::Cleanup() {
  67. dynamic_control_->Cleanup();
  68. }
  69. unsigned int BootControlAndroid::GetNumSlots() const {
  70. return module_->getNumberSlots();
  71. }
  72. BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
  73. return module_->getCurrentSlot();
  74. }
  75. bool BootControlAndroid::GetSuffix(Slot slot, string* suffix) const {
  76. auto store_suffix_cb = [&suffix](hidl_string cb_suffix) {
  77. *suffix = cb_suffix.c_str();
  78. };
  79. Return<void> ret = module_->getSuffix(slot, store_suffix_cb);
  80. if (!ret.isOk()) {
  81. LOG(ERROR) << "boot_control impl returned no suffix for slot "
  82. << SlotName(slot);
  83. return false;
  84. }
  85. return true;
  86. }
  87. bool BootControlAndroid::IsSuperBlockDevice(
  88. const base::FilePath& device_dir,
  89. Slot slot,
  90. const string& partition_name_suffix) const {
  91. string source_device =
  92. device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
  93. auto source_metadata = dynamic_control_->LoadMetadataBuilder(
  94. source_device, slot, BootControlInterface::kInvalidSlot);
  95. return source_metadata->HasBlockDevice(partition_name_suffix);
  96. }
  97. BootControlAndroid::DynamicPartitionDeviceStatus
  98. BootControlAndroid::GetDynamicPartitionDevice(
  99. const base::FilePath& device_dir,
  100. const string& partition_name_suffix,
  101. Slot slot,
  102. string* device) const {
  103. string super_device =
  104. device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
  105. auto builder = dynamic_control_->LoadMetadataBuilder(
  106. super_device, slot, BootControlInterface::kInvalidSlot);
  107. if (builder == nullptr) {
  108. LOG(ERROR) << "No metadata in slot "
  109. << BootControlInterface::SlotName(slot);
  110. return DynamicPartitionDeviceStatus::ERROR;
  111. }
  112. Slot current_slot = GetCurrentSlot();
  113. if (builder->FindPartition(partition_name_suffix) == nullptr) {
  114. LOG(INFO) << partition_name_suffix
  115. << " is not in super partition metadata.";
  116. if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
  117. LOG(ERROR) << "The static partition " << partition_name_suffix
  118. << " is a block device for current metadata ("
  119. << fs_mgr_get_super_partition_name(current_slot) << ", slot "
  120. << BootControlInterface::SlotName(current_slot)
  121. << "). It cannot be used as a logical partition.";
  122. return DynamicPartitionDeviceStatus::ERROR;
  123. }
  124. return DynamicPartitionDeviceStatus::TRY_STATIC;
  125. }
  126. if (slot == current_slot) {
  127. if (dynamic_control_->GetState(partition_name_suffix) !=
  128. DmDeviceState::ACTIVE) {
  129. LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
  130. << "not mapped. Now try to map it.";
  131. } else {
  132. if (dynamic_control_->GetDmDevicePathByName(partition_name_suffix,
  133. device)) {
  134. LOG(INFO) << partition_name_suffix
  135. << " is mapped on device mapper: " << *device;
  136. return DynamicPartitionDeviceStatus::SUCCESS;
  137. }
  138. LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
  139. return DynamicPartitionDeviceStatus::ERROR;
  140. }
  141. }
  142. bool force_writable = slot != current_slot;
  143. if (dynamic_control_->MapPartitionOnDeviceMapper(
  144. super_device, partition_name_suffix, slot, force_writable, device)) {
  145. return DynamicPartitionDeviceStatus::SUCCESS;
  146. }
  147. return DynamicPartitionDeviceStatus::ERROR;
  148. }
  149. bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
  150. Slot slot,
  151. string* device) const {
  152. string suffix;
  153. if (!GetSuffix(slot, &suffix)) {
  154. return false;
  155. }
  156. const string partition_name_suffix = partition_name + suffix;
  157. string device_dir_str;
  158. if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
  159. return false;
  160. }
  161. base::FilePath device_dir(device_dir_str);
  162. // When looking up target partition devices, treat them as static if the
  163. // current payload doesn't encode them as dynamic partitions. This may happen
  164. // when applying a retrofit update on top of a dynamic-partitions-enabled
  165. // build.
  166. if (dynamic_control_->IsDynamicPartitionsEnabled() &&
  167. (slot == GetCurrentSlot() || is_target_dynamic_)) {
  168. switch (GetDynamicPartitionDevice(
  169. device_dir, partition_name_suffix, slot, device)) {
  170. case DynamicPartitionDeviceStatus::SUCCESS:
  171. return true;
  172. case DynamicPartitionDeviceStatus::TRY_STATIC:
  173. break;
  174. case DynamicPartitionDeviceStatus::ERROR: // fallthrough
  175. default:
  176. return false;
  177. }
  178. }
  179. base::FilePath path = device_dir.Append(partition_name_suffix);
  180. if (!dynamic_control_->DeviceExists(path.value())) {
  181. LOG(ERROR) << "Device file " << path.value() << " does not exist.";
  182. return false;
  183. }
  184. *device = path.value();
  185. return true;
  186. }
  187. bool BootControlAndroid::IsSlotBootable(Slot slot) const {
  188. Return<BoolResult> ret = module_->isSlotBootable(slot);
  189. if (!ret.isOk()) {
  190. LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
  191. << " is bootable: " << ret.description();
  192. return false;
  193. }
  194. if (ret == BoolResult::INVALID_SLOT) {
  195. LOG(ERROR) << "Invalid slot: " << SlotName(slot);
  196. return false;
  197. }
  198. return ret == BoolResult::TRUE;
  199. }
  200. bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
  201. CommandResult result;
  202. auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
  203. if (!ret.isOk()) {
  204. LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
  205. << SlotName(slot) << ": " << ret.description();
  206. return false;
  207. }
  208. if (!result.success) {
  209. LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
  210. << " as unbootable: " << result.errMsg.c_str();
  211. }
  212. return result.success;
  213. }
  214. bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
  215. CommandResult result;
  216. auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
  217. if (!ret.isOk()) {
  218. LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
  219. << ": " << ret.description();
  220. return false;
  221. }
  222. if (!result.success) {
  223. LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
  224. << ": " << result.errMsg.c_str();
  225. }
  226. return result.success;
  227. }
  228. bool BootControlAndroid::MarkBootSuccessfulAsync(
  229. base::Callback<void(bool)> callback) {
  230. CommandResult result;
  231. auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
  232. if (!ret.isOk()) {
  233. LOG(ERROR) << "Unable to call MarkBootSuccessful: " << ret.description();
  234. return false;
  235. }
  236. if (!result.success) {
  237. LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
  238. }
  239. return brillo::MessageLoop::current()->PostTask(
  240. FROM_HERE, base::Bind(callback, result.success)) !=
  241. brillo::MessageLoop::kTaskIdNull;
  242. }
  243. namespace {
  244. bool UpdatePartitionMetadata(DynamicPartitionControlInterface* dynamic_control,
  245. Slot source_slot,
  246. Slot target_slot,
  247. const string& target_suffix,
  248. const PartitionMetadata& partition_metadata) {
  249. string device_dir_str;
  250. if (!dynamic_control->GetDeviceDir(&device_dir_str)) {
  251. return false;
  252. }
  253. base::FilePath device_dir(device_dir_str);
  254. auto source_device =
  255. device_dir.Append(fs_mgr_get_super_partition_name(source_slot)).value();
  256. auto builder = dynamic_control->LoadMetadataBuilder(
  257. source_device, source_slot, target_slot);
  258. if (builder == nullptr) {
  259. // TODO(elsk): allow reconstructing metadata from partition_metadata
  260. // in recovery sideload.
  261. LOG(ERROR) << "No metadata at "
  262. << BootControlInterface::SlotName(source_slot);
  263. return false;
  264. }
  265. std::vector<string> groups = builder->ListGroups();
  266. for (const auto& group_name : groups) {
  267. if (base::EndsWith(
  268. group_name, target_suffix, base::CompareCase::SENSITIVE)) {
  269. LOG(INFO) << "Removing group " << group_name;
  270. builder->RemoveGroupAndPartitions(group_name);
  271. }
  272. }
  273. uint64_t total_size = 0;
  274. for (const auto& group : partition_metadata.groups) {
  275. total_size += group.size;
  276. }
  277. string expr;
  278. uint64_t allocatable_space = builder->AllocatableSpace();
  279. if (!dynamic_control->IsDynamicPartitionsRetrofit()) {
  280. allocatable_space /= 2;
  281. expr = "half of ";
  282. }
  283. if (total_size > allocatable_space) {
  284. LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
  285. << " (" << total_size << ") has exceeded " << expr
  286. << " allocatable space for dynamic partitions "
  287. << allocatable_space << ".";
  288. return false;
  289. }
  290. for (const auto& group : partition_metadata.groups) {
  291. auto group_name_suffix = group.name + target_suffix;
  292. if (!builder->AddGroup(group_name_suffix, group.size)) {
  293. LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
  294. << group.size;
  295. return false;
  296. }
  297. LOG(INFO) << "Added group " << group_name_suffix << " with size "
  298. << group.size;
  299. for (const auto& partition : group.partitions) {
  300. auto partition_name_suffix = partition.name + target_suffix;
  301. Partition* p = builder->AddPartition(
  302. partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
  303. if (!p) {
  304. LOG(ERROR) << "Cannot add partition " << partition_name_suffix
  305. << " to group " << group_name_suffix;
  306. return false;
  307. }
  308. if (!builder->ResizePartition(p, partition.size)) {
  309. LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
  310. << " to size " << partition.size << ". Not enough space?";
  311. return false;
  312. }
  313. LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
  314. << group_name_suffix << " with size " << partition.size;
  315. }
  316. }
  317. auto target_device =
  318. device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value();
  319. return dynamic_control->StoreMetadata(
  320. target_device, builder.get(), target_slot);
  321. }
  322. bool UnmapTargetPartitions(DynamicPartitionControlInterface* dynamic_control,
  323. const string& target_suffix,
  324. const PartitionMetadata& partition_metadata) {
  325. for (const auto& group : partition_metadata.groups) {
  326. for (const auto& partition : group.partitions) {
  327. if (!dynamic_control->UnmapPartitionOnDeviceMapper(
  328. partition.name + target_suffix, true /* wait */)) {
  329. return false;
  330. }
  331. }
  332. }
  333. return true;
  334. }
  335. } // namespace
  336. bool BootControlAndroid::InitPartitionMetadata(
  337. Slot target_slot,
  338. const PartitionMetadata& partition_metadata,
  339. bool update_metadata) {
  340. if (fs_mgr_overlayfs_is_setup()) {
  341. // Non DAP devices can use overlayfs as well.
  342. LOG(WARNING)
  343. << "overlayfs overrides are active and can interfere with our "
  344. "resources.\n"
  345. << "run adb enable-verity to deactivate if required and try again.";
  346. }
  347. if (!dynamic_control_->IsDynamicPartitionsEnabled()) {
  348. return true;
  349. }
  350. auto source_slot = GetCurrentSlot();
  351. if (target_slot == source_slot) {
  352. LOG(ERROR) << "Cannot call InitPartitionMetadata on current slot.";
  353. return false;
  354. }
  355. // Although the current build supports dynamic partitions, the given payload
  356. // doesn't use it for target partitions. This could happen when applying a
  357. // retrofit update. Skip updating the partition metadata for the target slot.
  358. is_target_dynamic_ = !partition_metadata.groups.empty();
  359. if (!is_target_dynamic_) {
  360. return true;
  361. }
  362. if (!update_metadata) {
  363. return true;
  364. }
  365. string target_suffix;
  366. if (!GetSuffix(target_slot, &target_suffix)) {
  367. return false;
  368. }
  369. // Unmap all the target dynamic partitions because they would become
  370. // inconsistent with the new metadata.
  371. if (!UnmapTargetPartitions(
  372. dynamic_control_.get(), target_suffix, partition_metadata)) {
  373. return false;
  374. }
  375. return UpdatePartitionMetadata(dynamic_control_.get(),
  376. source_slot,
  377. target_slot,
  378. target_suffix,
  379. partition_metadata);
  380. }
  381. } // namespace chromeos_update_engine