blob: ad819ad4bbf17586f8a3ef01ec8a09747da2741c [file] [log] [blame]
Alex Deymob17327c2015-09-04 10:29:00 -07001//
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
Alex Deymo1b03f9f2015-12-09 00:38:36 -080017#include "update_engine/boot_control_android.h"
Alex Deymob17327c2015-09-04 10:29:00 -070018
Sen Jiangd944faa2018-08-22 18:46:39 -070019#include <memory>
20#include <utility>
21
Alex Deymoaa26f622015-09-16 18:21:27 -070022#include <base/bind.h>
Alex Deymoaa26f622015-09-16 18:21:27 -070023#include <base/logging.h>
Sen Jiangd944faa2018-08-22 18:46:39 -070024#include <bootloader_message/bootloader_message.h>
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070025#include <brillo/message_loops/message_loop.h>
David Andersond63cb3c2018-10-01 14:15:00 -070026#include <fs_mgr.h>
Alex Deymob17327c2015-09-04 10:29:00 -070027
Alex Deymo39910dc2015-11-09 17:04:30 -080028#include "update_engine/common/utils.h"
Yifan Hong537802d2018-08-15 13:15:42 -070029#include "update_engine/dynamic_partition_control_android.h"
Alex Deymob17327c2015-09-04 10:29:00 -070030
31using std::string;
32
Yifan Hong537802d2018-08-15 13:15:42 -070033using android::dm::DmDeviceState;
34using android::fs_mgr::MetadataBuilder;
35using android::fs_mgr::Partition;
36using android::fs_mgr::UpdatePartitionTable;
37using android::hardware::hidl_string;
Connor O'Briencee6ad92016-11-21 13:53:52 -080038using android::hardware::Return;
39using android::hardware::boot::V1_0::BoolResult;
40using android::hardware::boot::V1_0::CommandResult;
41using android::hardware::boot::V1_0::IBootControl;
Yifan Hong537802d2018-08-15 13:15:42 -070042using Slot = chromeos_update_engine::BootControlInterface::Slot;
43using PartitionSizes =
44 chromeos_update_engine::BootControlInterface::PartitionSizes;
Connor O'Briencee6ad92016-11-21 13:53:52 -080045
46namespace {
Yifan Hong537802d2018-08-15 13:15:42 -070047
Connor O'Briencee6ad92016-11-21 13:53:52 -080048auto StoreResultCallback(CommandResult* dest) {
49 return [dest](const CommandResult& result) { *dest = result; };
50}
51} // namespace
Alex Deymo44348e02016-07-29 16:22:26 -070052
Alex Deymob17327c2015-09-04 10:29:00 -070053namespace chromeos_update_engine {
54
55namespace boot_control {
56
57// Factory defined in boot_control.h.
58std::unique_ptr<BootControlInterface> CreateBootControl() {
Yifan Hong537802d2018-08-15 13:15:42 -070059 auto boot_control = std::make_unique<BootControlAndroid>();
David Zeuthen753fadc2015-09-15 16:34:09 -040060 if (!boot_control->Init()) {
61 return nullptr;
62 }
Alex Vakulenkoce8c8ee2016-04-08 08:59:26 -070063 return std::move(boot_control);
Alex Deymob17327c2015-09-04 10:29:00 -070064}
65
66} // namespace boot_control
67
David Zeuthen753fadc2015-09-15 16:34:09 -040068bool BootControlAndroid::Init() {
Chris Phoenixafde8e82017-01-17 23:14:58 -080069 module_ = IBootControl::getService();
Connor O'Briencee6ad92016-11-21 13:53:52 -080070 if (module_ == nullptr) {
Steven Moreland927e00d2017-01-04 12:58:40 -080071 LOG(ERROR) << "Error getting bootctrl HIDL module.";
David Zeuthen753fadc2015-09-15 16:34:09 -040072 return false;
73 }
74
Steven Moreland927e00d2017-01-04 12:58:40 -080075 LOG(INFO) << "Loaded boot control hidl hal.";
David Zeuthen753fadc2015-09-15 16:34:09 -040076
Yifan Hong537802d2018-08-15 13:15:42 -070077 dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
78
David Zeuthen753fadc2015-09-15 16:34:09 -040079 return true;
80}
Alex Deymob17327c2015-09-04 10:29:00 -070081
Yifan Hong537802d2018-08-15 13:15:42 -070082void BootControlAndroid::Cleanup() {
83 dynamic_control_->Cleanup();
84}
85
Alex Deymob17327c2015-09-04 10:29:00 -070086unsigned int BootControlAndroid::GetNumSlots() const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080087 return module_->getNumberSlots();
Alex Deymob17327c2015-09-04 10:29:00 -070088}
89
90BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080091 return module_->getCurrentSlot();
Alex Deymob17327c2015-09-04 10:29:00 -070092}
93
Yifan Hong537802d2018-08-15 13:15:42 -070094bool BootControlAndroid::GetSuffix(Slot slot, string* suffix) const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080095 auto store_suffix_cb = [&suffix](hidl_string cb_suffix) {
Yifan Hong537802d2018-08-15 13:15:42 -070096 *suffix = cb_suffix.c_str();
Connor O'Briencee6ad92016-11-21 13:53:52 -080097 };
98 Return<void> ret = module_->getSuffix(slot, store_suffix_cb);
99
Yifan Hong7b514b42016-12-21 13:02:00 -0800100 if (!ret.isOk()) {
Alex Deymo31d95ac2015-09-17 11:56:18 -0700101 LOG(ERROR) << "boot_control impl returned no suffix for slot "
102 << SlotName(slot);
David Zeuthen753fadc2015-09-15 16:34:09 -0400103 return false;
104 }
Yifan Hong537802d2018-08-15 13:15:42 -0700105 return true;
106}
107
108bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
109 Slot slot,
110 string* device) const {
111 string suffix;
112 if (!GetSuffix(slot, &suffix)) {
113 return false;
114 }
115
116 const string target_partition_name = partition_name + suffix;
117
118 // DeltaPerformer calls InitPartitionMetadata before calling
119 // InstallPlan::LoadPartitionsFromSlots. After InitPartitionMetadata,
120 // the partition must be re-mapped with force_writable == true. Hence,
121 // we only need to check device mapper.
122 if (dynamic_control_->IsDynamicPartitionsEnabled()) {
123 switch (dynamic_control_->GetState(target_partition_name)) {
124 case DmDeviceState::ACTIVE:
125 if (dynamic_control_->GetDmDevicePathByName(target_partition_name,
126 device)) {
127 LOG(INFO) << target_partition_name
128 << " is mapped on device mapper: " << *device;
129 return true;
130 }
131 LOG(ERROR) << target_partition_name
132 << " is mapped but path is unknown.";
133 return false;
134
135 case DmDeviceState::INVALID:
136 // Try static partitions.
137 break;
138
139 case DmDeviceState::SUSPENDED: // fallthrough
140 default:
141 LOG(ERROR) << target_partition_name
142 << " is mapped on device mapper but state is unknown";
143 return false;
144 }
145 }
146
147 string device_dir_str;
148 if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
149 return false;
150 }
David Zeuthen753fadc2015-09-15 16:34:09 -0400151
Sen Jiangd944faa2018-08-22 18:46:39 -0700152 base::FilePath path =
Yifan Hong537802d2018-08-15 13:15:42 -0700153 base::FilePath(device_dir_str).Append(target_partition_name);
154 if (!dynamic_control_->DeviceExists(path.value())) {
David Zeuthen753fadc2015-09-15 16:34:09 -0400155 LOG(ERROR) << "Device file " << path.value() << " does not exist.";
156 return false;
157 }
158
159 *device = path.value();
160 return true;
Alex Deymob17327c2015-09-04 10:29:00 -0700161}
162
163bool BootControlAndroid::IsSlotBootable(Slot slot) const {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800164 Return<BoolResult> ret = module_->isSlotBootable(slot);
Yifan Hong7b514b42016-12-21 13:02:00 -0800165 if (!ret.isOk()) {
Alex Deymo31d95ac2015-09-17 11:56:18 -0700166 LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
Connor O'Briencee6ad92016-11-21 13:53:52 -0800167 << " is bootable: "
Yifan Hong7b514b42016-12-21 13:02:00 -0800168 << ret.description();
David Zeuthen753fadc2015-09-15 16:34:09 -0400169 return false;
170 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800171 if (ret == BoolResult::INVALID_SLOT) {
172 LOG(ERROR) << "Invalid slot: " << SlotName(slot);
173 return false;
174 }
175 return ret == BoolResult::TRUE;
Alex Deymob17327c2015-09-04 10:29:00 -0700176}
177
178bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800179 CommandResult result;
180 auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800181 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800182 LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
183 << SlotName(slot) << ": "
Yifan Hong7b514b42016-12-21 13:02:00 -0800184 << ret.description();
David Zeuthen753fadc2015-09-15 16:34:09 -0400185 return false;
186 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800187 if (!result.success) {
188 LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
189 << " as unbootable: " << result.errMsg.c_str();
190 }
191 return result.success;
Alex Deymob17327c2015-09-04 10:29:00 -0700192}
193
Alex Deymo31d95ac2015-09-17 11:56:18 -0700194bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800195 CommandResult result;
196 auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800197 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800198 LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
Yifan Hong7b514b42016-12-21 13:02:00 -0800199 << ": " << ret.description();
Connor O'Briencee6ad92016-11-21 13:53:52 -0800200 return false;
Alex Deymo29dcbf32016-10-06 13:33:20 -0700201 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800202 if (!result.success) {
203 LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
204 << ": " << result.errMsg.c_str();
205 }
206 return result.success;
Alex Deymo31d95ac2015-09-17 11:56:18 -0700207}
208
Alex Deymoaa26f622015-09-16 18:21:27 -0700209bool BootControlAndroid::MarkBootSuccessfulAsync(
210 base::Callback<void(bool)> callback) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800211 CommandResult result;
212 auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800213 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800214 LOG(ERROR) << "Unable to call MarkBootSuccessful: "
Yifan Hong7b514b42016-12-21 13:02:00 -0800215 << ret.description();
Connor O'Briencee6ad92016-11-21 13:53:52 -0800216 return false;
217 }
218 if (!result.success) {
219 LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
Alex Deymoaa26f622015-09-16 18:21:27 -0700220 }
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700221 return brillo::MessageLoop::current()->PostTask(
Connor O'Briencee6ad92016-11-21 13:53:52 -0800222 FROM_HERE, base::Bind(callback, result.success)) !=
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700223 brillo::MessageLoop::kTaskIdNull;
Alex Deymoaa26f622015-09-16 18:21:27 -0700224}
225
Yifan Hong537802d2018-08-15 13:15:42 -0700226namespace {
227
228// Resize |partition_name|_|slot| to the given |size|.
229bool ResizePartition(MetadataBuilder* builder,
230 const string& target_partition_name,
231 uint64_t size) {
232 Partition* partition = builder->FindPartition(target_partition_name);
233 if (partition == nullptr) {
234 LOG(ERROR) << "Cannot find " << target_partition_name << " in metadata.";
235 return false;
236 }
237
238 uint64_t old_size = partition->size();
239 const string action = "resize " + target_partition_name + " in super (" +
240 std::to_string(old_size) + " -> " +
241 std::to_string(size) + " bytes)";
242 if (!builder->ResizePartition(partition, size)) {
243 LOG(ERROR) << "Cannot " << action << "; see previous log messages.";
244 return false;
245 }
246
247 if (partition->size() != size) {
248 LOG(ERROR) << "Cannot " << action
249 << "; value is misaligned and partition should have been "
250 << partition->size();
251 return false;
252 }
253
254 LOG(INFO) << "Successfully " << action;
255
256 return true;
257}
258
259bool ResizePartitions(DynamicPartitionControlInterface* dynamic_control,
260 const string& super_device,
261 Slot target_slot,
262 const string& target_suffix,
263 const PartitionSizes& logical_sizes,
264 MetadataBuilder* builder) {
265 // Delete all extents to ensure that each partition has enough space to
266 // grow.
267 for (const auto& pair : logical_sizes) {
268 const string target_partition_name = pair.first + target_suffix;
269 if (builder->FindPartition(target_partition_name) == nullptr) {
270 // Use constant GUID because it is unused.
271 LOG(INFO) << "Adding partition " << target_partition_name << " to slot "
272 << BootControlInterface::SlotName(target_slot) << " in "
273 << super_device;
274 if (builder->AddPartition(target_partition_name,
Yifan Hong537802d2018-08-15 13:15:42 -0700275 LP_PARTITION_ATTR_READONLY) == nullptr) {
276 LOG(ERROR) << "Cannot add partition " << target_partition_name;
277 return false;
278 }
279 }
280 if (!ResizePartition(builder, pair.first + target_suffix, 0 /* size */)) {
281 return false;
282 }
283 }
284
285 for (const auto& pair : logical_sizes) {
286 if (!ResizePartition(builder, pair.first + target_suffix, pair.second)) {
287 LOG(ERROR) << "Not enough space?";
288 return false;
289 }
290 }
291
292 if (!dynamic_control->StoreMetadata(super_device, builder, target_slot)) {
293 return false;
294 }
295 return true;
296}
297
298// Assume upgrading from slot A to B. A partition foo is considered dynamic
299// iff one of the following:
300// 1. foo_a exists as a dynamic partition (so it should continue to be a
301// dynamic partition)
302// 2. foo_b does not exist as a static partition (in which case we may be
303// adding a new partition).
304bool IsDynamicPartition(DynamicPartitionControlInterface* dynamic_control,
305 const base::FilePath& device_dir,
306 MetadataBuilder* source_metadata,
307 const string& partition_name,
308 const string& source_suffix,
309 const string& target_suffix) {
310 bool dynamic_source_exist =
311 source_metadata->FindPartition(partition_name + source_suffix) != nullptr;
312 bool static_target_exist = dynamic_control->DeviceExists(
313 device_dir.Append(partition_name + target_suffix).value());
314
315 return dynamic_source_exist || !static_target_exist;
316}
317
318bool FilterPartitionSizes(DynamicPartitionControlInterface* dynamic_control,
319 const base::FilePath& device_dir,
320 const PartitionSizes& partition_sizes,
321 MetadataBuilder* source_metadata,
322 const string& source_suffix,
323 const string& target_suffix,
324 PartitionSizes* logical_sizes) {
325 for (const auto& pair : partition_sizes) {
326 if (!IsDynamicPartition(dynamic_control,
327 device_dir,
328 source_metadata,
329 pair.first,
330 source_suffix,
331 target_suffix)) {
332 // In the future we can check static partition sizes, but skip for now.
333 LOG(INFO) << pair.first << " is static; assume its size is "
334 << pair.second << " bytes.";
335 continue;
336 }
337
338 logical_sizes->insert(pair);
339 }
340 return true;
341}
342
343// Return false if partition sizes are all correct in metadata slot
344// |target_slot|. If so, no need to resize. |logical_sizes| have format like
345// {vendor: size, ...}, and fail if a partition is not found.
346bool NeedResizePartitions(DynamicPartitionControlInterface* dynamic_control,
347 const string& super_device,
348 Slot target_slot,
349 const string& suffix,
350 const PartitionSizes& logical_sizes) {
351 auto target_metadata =
352 dynamic_control->LoadMetadataBuilder(super_device, target_slot);
353 if (target_metadata == nullptr) {
354 LOG(INFO) << "Metadata slot " << BootControlInterface::SlotName(target_slot)
355 << " in " << super_device
356 << " is corrupted; attempt to recover from source slot.";
357 return true;
358 }
359
360 for (const auto& pair : logical_sizes) {
361 Partition* partition = target_metadata->FindPartition(pair.first + suffix);
362 if (partition == nullptr) {
363 LOG(INFO) << "Cannot find " << pair.first << suffix << " at slot "
364 << BootControlInterface::SlotName(target_slot) << " in "
365 << super_device << ". Need to resize.";
366 return true;
367 }
368 if (partition->size() != pair.second) {
369 LOG(INFO) << super_device << ":"
370 << BootControlInterface::SlotName(target_slot) << ":"
371 << pair.first << suffix << ": size == " << partition->size()
372 << " but requested " << pair.second << ". Need to resize.";
373 return true;
374 }
375 LOG(INFO) << super_device << ":"
376 << BootControlInterface::SlotName(target_slot) << ":"
377 << pair.first << suffix << ": size == " << partition->size()
378 << " as requested.";
379 }
380 LOG(INFO) << "No need to resize at metadata slot "
381 << BootControlInterface::SlotName(target_slot) << " in "
382 << super_device;
383 return false;
384}
385} // namespace
386
387bool BootControlAndroid::InitPartitionMetadata(
388 Slot target_slot, const PartitionSizes& partition_sizes) {
389 if (!dynamic_control_->IsDynamicPartitionsEnabled()) {
390 return true;
391 }
392
393 string device_dir_str;
394 if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
395 return false;
396 }
397 base::FilePath device_dir(device_dir_str);
David Andersond63cb3c2018-10-01 14:15:00 -0700398 string super_device =
399 device_dir.Append(fs_mgr_get_super_partition_name()).value();
Yifan Hong537802d2018-08-15 13:15:42 -0700400
401 Slot current_slot = GetCurrentSlot();
402 if (target_slot == current_slot) {
403 LOG(ERROR) << "Cannot call InitPartitionMetadata on current slot.";
404 return false;
405 }
406
407 string current_suffix;
408 if (!GetSuffix(current_slot, &current_suffix)) {
409 return false;
410 }
411
412 string target_suffix;
413 if (!GetSuffix(target_slot, &target_suffix)) {
414 return false;
415 }
416
417 auto builder =
418 dynamic_control_->LoadMetadataBuilder(super_device, current_slot);
419 if (builder == nullptr) {
420 return false;
421 }
422
423 // Read metadata from current slot to determine which partitions are logical
424 // and may be resized. Do not read from target slot because metadata at
425 // target slot may be corrupted.
426 PartitionSizes logical_sizes;
427 if (!FilterPartitionSizes(dynamic_control_.get(),
428 device_dir,
429 partition_sizes,
430 builder.get() /* source metadata */,
431 current_suffix,
432 target_suffix,
433 &logical_sizes)) {
434 return false;
435 }
436
437 // Read metadata from target slot to determine if the sizes are correct. Only
438 // test logical partitions.
439 if (NeedResizePartitions(dynamic_control_.get(),
440 super_device,
441 target_slot,
442 target_suffix,
443 logical_sizes)) {
444 if (!ResizePartitions(dynamic_control_.get(),
445 super_device,
446 target_slot,
447 target_suffix,
448 logical_sizes,
449 builder.get())) {
450 return false;
451 }
452 }
453
454 // Unmap all partitions, and remap partitions if size is non-zero.
455 for (const auto& pair : logical_sizes) {
456 if (!dynamic_control_->UnmapPartitionOnDeviceMapper(
457 pair.first + target_suffix, true /* wait */)) {
458 return false;
459 }
460 if (pair.second == 0) {
461 continue;
462 }
463 string map_path;
464 if (!dynamic_control_->MapPartitionOnDeviceMapper(
465 super_device, pair.first + target_suffix, target_slot, &map_path)) {
466 return false;
467 }
468 }
469 return true;
470}
471
Alex Deymob17327c2015-09-04 10:29:00 -0700472} // namespace chromeos_update_engine