blob: abf898e820c78756cd9233b4fc16e92916852e1a [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>
Alex Deymob17327c2015-09-04 10:29:00 -070026
Alex Deymo39910dc2015-11-09 17:04:30 -080027#include "update_engine/common/utils.h"
Yifan Hong537802d2018-08-15 13:15:42 -070028#include "update_engine/dynamic_partition_control_android.h"
Alex Deymob17327c2015-09-04 10:29:00 -070029
30using std::string;
31
Yifan Hong537802d2018-08-15 13:15:42 -070032using android::dm::DmDeviceState;
33using android::fs_mgr::MetadataBuilder;
34using android::fs_mgr::Partition;
35using android::fs_mgr::UpdatePartitionTable;
36using android::hardware::hidl_string;
Connor O'Briencee6ad92016-11-21 13:53:52 -080037using android::hardware::Return;
38using android::hardware::boot::V1_0::BoolResult;
39using android::hardware::boot::V1_0::CommandResult;
40using android::hardware::boot::V1_0::IBootControl;
Yifan Hong537802d2018-08-15 13:15:42 -070041using Slot = chromeos_update_engine::BootControlInterface::Slot;
42using PartitionSizes =
43 chromeos_update_engine::BootControlInterface::PartitionSizes;
Connor O'Briencee6ad92016-11-21 13:53:52 -080044
45namespace {
Yifan Hong537802d2018-08-15 13:15:42 -070046
47constexpr char kZeroGuid[] = "00000000-0000-0000-0000-000000000000";
48
Connor O'Briencee6ad92016-11-21 13:53:52 -080049auto StoreResultCallback(CommandResult* dest) {
50 return [dest](const CommandResult& result) { *dest = result; };
51}
52} // namespace
Alex Deymo44348e02016-07-29 16:22:26 -070053
Alex Deymob17327c2015-09-04 10:29:00 -070054namespace chromeos_update_engine {
55
56namespace boot_control {
57
58// Factory defined in boot_control.h.
59std::unique_ptr<BootControlInterface> CreateBootControl() {
Yifan Hong537802d2018-08-15 13:15:42 -070060 auto boot_control = std::make_unique<BootControlAndroid>();
David Zeuthen753fadc2015-09-15 16:34:09 -040061 if (!boot_control->Init()) {
62 return nullptr;
63 }
Alex Vakulenkoce8c8ee2016-04-08 08:59:26 -070064 return std::move(boot_control);
Alex Deymob17327c2015-09-04 10:29:00 -070065}
66
67} // namespace boot_control
68
David Zeuthen753fadc2015-09-15 16:34:09 -040069bool BootControlAndroid::Init() {
Chris Phoenixafde8e82017-01-17 23:14:58 -080070 module_ = IBootControl::getService();
Connor O'Briencee6ad92016-11-21 13:53:52 -080071 if (module_ == nullptr) {
Steven Moreland927e00d2017-01-04 12:58:40 -080072 LOG(ERROR) << "Error getting bootctrl HIDL module.";
David Zeuthen753fadc2015-09-15 16:34:09 -040073 return false;
74 }
75
Steven Moreland927e00d2017-01-04 12:58:40 -080076 LOG(INFO) << "Loaded boot control hidl hal.";
David Zeuthen753fadc2015-09-15 16:34:09 -040077
Yifan Hong537802d2018-08-15 13:15:42 -070078 dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
79
David Zeuthen753fadc2015-09-15 16:34:09 -040080 return true;
81}
Alex Deymob17327c2015-09-04 10:29:00 -070082
Yifan Hong537802d2018-08-15 13:15:42 -070083void BootControlAndroid::Cleanup() {
84 dynamic_control_->Cleanup();
85}
86
Alex Deymob17327c2015-09-04 10:29:00 -070087unsigned int BootControlAndroid::GetNumSlots() const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080088 return module_->getNumberSlots();
Alex Deymob17327c2015-09-04 10:29:00 -070089}
90
91BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080092 return module_->getCurrentSlot();
Alex Deymob17327c2015-09-04 10:29:00 -070093}
94
Yifan Hong537802d2018-08-15 13:15:42 -070095bool BootControlAndroid::GetSuffix(Slot slot, string* suffix) const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080096 auto store_suffix_cb = [&suffix](hidl_string cb_suffix) {
Yifan Hong537802d2018-08-15 13:15:42 -070097 *suffix = cb_suffix.c_str();
Connor O'Briencee6ad92016-11-21 13:53:52 -080098 };
99 Return<void> ret = module_->getSuffix(slot, store_suffix_cb);
100
Yifan Hong7b514b42016-12-21 13:02:00 -0800101 if (!ret.isOk()) {
Alex Deymo31d95ac2015-09-17 11:56:18 -0700102 LOG(ERROR) << "boot_control impl returned no suffix for slot "
103 << SlotName(slot);
David Zeuthen753fadc2015-09-15 16:34:09 -0400104 return false;
105 }
Yifan Hong537802d2018-08-15 13:15:42 -0700106 return true;
107}
108
109bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
110 Slot slot,
111 string* device) const {
112 string suffix;
113 if (!GetSuffix(slot, &suffix)) {
114 return false;
115 }
116
117 const string target_partition_name = partition_name + suffix;
118
119 // DeltaPerformer calls InitPartitionMetadata before calling
120 // InstallPlan::LoadPartitionsFromSlots. After InitPartitionMetadata,
121 // the partition must be re-mapped with force_writable == true. Hence,
122 // we only need to check device mapper.
123 if (dynamic_control_->IsDynamicPartitionsEnabled()) {
124 switch (dynamic_control_->GetState(target_partition_name)) {
125 case DmDeviceState::ACTIVE:
126 if (dynamic_control_->GetDmDevicePathByName(target_partition_name,
127 device)) {
128 LOG(INFO) << target_partition_name
129 << " is mapped on device mapper: " << *device;
130 return true;
131 }
132 LOG(ERROR) << target_partition_name
133 << " is mapped but path is unknown.";
134 return false;
135
136 case DmDeviceState::INVALID:
137 // Try static partitions.
138 break;
139
140 case DmDeviceState::SUSPENDED: // fallthrough
141 default:
142 LOG(ERROR) << target_partition_name
143 << " is mapped on device mapper but state is unknown";
144 return false;
145 }
146 }
147
148 string device_dir_str;
149 if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
150 return false;
151 }
David Zeuthen753fadc2015-09-15 16:34:09 -0400152
Sen Jiangd944faa2018-08-22 18:46:39 -0700153 base::FilePath path =
Yifan Hong537802d2018-08-15 13:15:42 -0700154 base::FilePath(device_dir_str).Append(target_partition_name);
155 if (!dynamic_control_->DeviceExists(path.value())) {
David Zeuthen753fadc2015-09-15 16:34:09 -0400156 LOG(ERROR) << "Device file " << path.value() << " does not exist.";
157 return false;
158 }
159
160 *device = path.value();
161 return true;
Alex Deymob17327c2015-09-04 10:29:00 -0700162}
163
164bool BootControlAndroid::IsSlotBootable(Slot slot) const {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800165 Return<BoolResult> ret = module_->isSlotBootable(slot);
Yifan Hong7b514b42016-12-21 13:02:00 -0800166 if (!ret.isOk()) {
Alex Deymo31d95ac2015-09-17 11:56:18 -0700167 LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
Connor O'Briencee6ad92016-11-21 13:53:52 -0800168 << " is bootable: "
Yifan Hong7b514b42016-12-21 13:02:00 -0800169 << ret.description();
David Zeuthen753fadc2015-09-15 16:34:09 -0400170 return false;
171 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800172 if (ret == BoolResult::INVALID_SLOT) {
173 LOG(ERROR) << "Invalid slot: " << SlotName(slot);
174 return false;
175 }
176 return ret == BoolResult::TRUE;
Alex Deymob17327c2015-09-04 10:29:00 -0700177}
178
179bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800180 CommandResult result;
181 auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800182 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800183 LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
184 << SlotName(slot) << ": "
Yifan Hong7b514b42016-12-21 13:02:00 -0800185 << ret.description();
David Zeuthen753fadc2015-09-15 16:34:09 -0400186 return false;
187 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800188 if (!result.success) {
189 LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
190 << " as unbootable: " << result.errMsg.c_str();
191 }
192 return result.success;
Alex Deymob17327c2015-09-04 10:29:00 -0700193}
194
Alex Deymo31d95ac2015-09-17 11:56:18 -0700195bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800196 CommandResult result;
197 auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800198 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800199 LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
Yifan Hong7b514b42016-12-21 13:02:00 -0800200 << ": " << ret.description();
Connor O'Briencee6ad92016-11-21 13:53:52 -0800201 return false;
Alex Deymo29dcbf32016-10-06 13:33:20 -0700202 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800203 if (!result.success) {
204 LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
205 << ": " << result.errMsg.c_str();
206 }
207 return result.success;
Alex Deymo31d95ac2015-09-17 11:56:18 -0700208}
209
Alex Deymoaa26f622015-09-16 18:21:27 -0700210bool BootControlAndroid::MarkBootSuccessfulAsync(
211 base::Callback<void(bool)> callback) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800212 CommandResult result;
213 auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800214 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800215 LOG(ERROR) << "Unable to call MarkBootSuccessful: "
Yifan Hong7b514b42016-12-21 13:02:00 -0800216 << ret.description();
Connor O'Briencee6ad92016-11-21 13:53:52 -0800217 return false;
218 }
219 if (!result.success) {
220 LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
Alex Deymoaa26f622015-09-16 18:21:27 -0700221 }
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700222 return brillo::MessageLoop::current()->PostTask(
Connor O'Briencee6ad92016-11-21 13:53:52 -0800223 FROM_HERE, base::Bind(callback, result.success)) !=
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700224 brillo::MessageLoop::kTaskIdNull;
Alex Deymoaa26f622015-09-16 18:21:27 -0700225}
226
Yifan Hong537802d2018-08-15 13:15:42 -0700227namespace {
228
229// Resize |partition_name|_|slot| to the given |size|.
230bool ResizePartition(MetadataBuilder* builder,
231 const string& target_partition_name,
232 uint64_t size) {
233 Partition* partition = builder->FindPartition(target_partition_name);
234 if (partition == nullptr) {
235 LOG(ERROR) << "Cannot find " << target_partition_name << " in metadata.";
236 return false;
237 }
238
239 uint64_t old_size = partition->size();
240 const string action = "resize " + target_partition_name + " in super (" +
241 std::to_string(old_size) + " -> " +
242 std::to_string(size) + " bytes)";
243 if (!builder->ResizePartition(partition, size)) {
244 LOG(ERROR) << "Cannot " << action << "; see previous log messages.";
245 return false;
246 }
247
248 if (partition->size() != size) {
249 LOG(ERROR) << "Cannot " << action
250 << "; value is misaligned and partition should have been "
251 << partition->size();
252 return false;
253 }
254
255 LOG(INFO) << "Successfully " << action;
256
257 return true;
258}
259
260bool ResizePartitions(DynamicPartitionControlInterface* dynamic_control,
261 const string& super_device,
262 Slot target_slot,
263 const string& target_suffix,
264 const PartitionSizes& logical_sizes,
265 MetadataBuilder* builder) {
266 // Delete all extents to ensure that each partition has enough space to
267 // grow.
268 for (const auto& pair : logical_sizes) {
269 const string target_partition_name = pair.first + target_suffix;
270 if (builder->FindPartition(target_partition_name) == nullptr) {
271 // Use constant GUID because it is unused.
272 LOG(INFO) << "Adding partition " << target_partition_name << " to slot "
273 << BootControlInterface::SlotName(target_slot) << " in "
274 << super_device;
275 if (builder->AddPartition(target_partition_name,
276 kZeroGuid,
277 LP_PARTITION_ATTR_READONLY) == nullptr) {
278 LOG(ERROR) << "Cannot add partition " << target_partition_name;
279 return false;
280 }
281 }
282 if (!ResizePartition(builder, pair.first + target_suffix, 0 /* size */)) {
283 return false;
284 }
285 }
286
287 for (const auto& pair : logical_sizes) {
288 if (!ResizePartition(builder, pair.first + target_suffix, pair.second)) {
289 LOG(ERROR) << "Not enough space?";
290 return false;
291 }
292 }
293
294 if (!dynamic_control->StoreMetadata(super_device, builder, target_slot)) {
295 return false;
296 }
297 return true;
298}
299
300// Assume upgrading from slot A to B. A partition foo is considered dynamic
301// iff one of the following:
302// 1. foo_a exists as a dynamic partition (so it should continue to be a
303// dynamic partition)
304// 2. foo_b does not exist as a static partition (in which case we may be
305// adding a new partition).
306bool IsDynamicPartition(DynamicPartitionControlInterface* dynamic_control,
307 const base::FilePath& device_dir,
308 MetadataBuilder* source_metadata,
309 const string& partition_name,
310 const string& source_suffix,
311 const string& target_suffix) {
312 bool dynamic_source_exist =
313 source_metadata->FindPartition(partition_name + source_suffix) != nullptr;
314 bool static_target_exist = dynamic_control->DeviceExists(
315 device_dir.Append(partition_name + target_suffix).value());
316
317 return dynamic_source_exist || !static_target_exist;
318}
319
320bool FilterPartitionSizes(DynamicPartitionControlInterface* dynamic_control,
321 const base::FilePath& device_dir,
322 const PartitionSizes& partition_sizes,
323 MetadataBuilder* source_metadata,
324 const string& source_suffix,
325 const string& target_suffix,
326 PartitionSizes* logical_sizes) {
327 for (const auto& pair : partition_sizes) {
328 if (!IsDynamicPartition(dynamic_control,
329 device_dir,
330 source_metadata,
331 pair.first,
332 source_suffix,
333 target_suffix)) {
334 // In the future we can check static partition sizes, but skip for now.
335 LOG(INFO) << pair.first << " is static; assume its size is "
336 << pair.second << " bytes.";
337 continue;
338 }
339
340 logical_sizes->insert(pair);
341 }
342 return true;
343}
344
345// Return false if partition sizes are all correct in metadata slot
346// |target_slot|. If so, no need to resize. |logical_sizes| have format like
347// {vendor: size, ...}, and fail if a partition is not found.
348bool NeedResizePartitions(DynamicPartitionControlInterface* dynamic_control,
349 const string& super_device,
350 Slot target_slot,
351 const string& suffix,
352 const PartitionSizes& logical_sizes) {
353 auto target_metadata =
354 dynamic_control->LoadMetadataBuilder(super_device, target_slot);
355 if (target_metadata == nullptr) {
356 LOG(INFO) << "Metadata slot " << BootControlInterface::SlotName(target_slot)
357 << " in " << super_device
358 << " is corrupted; attempt to recover from source slot.";
359 return true;
360 }
361
362 for (const auto& pair : logical_sizes) {
363 Partition* partition = target_metadata->FindPartition(pair.first + suffix);
364 if (partition == nullptr) {
365 LOG(INFO) << "Cannot find " << pair.first << suffix << " at slot "
366 << BootControlInterface::SlotName(target_slot) << " in "
367 << super_device << ". Need to resize.";
368 return true;
369 }
370 if (partition->size() != pair.second) {
371 LOG(INFO) << super_device << ":"
372 << BootControlInterface::SlotName(target_slot) << ":"
373 << pair.first << suffix << ": size == " << partition->size()
374 << " but requested " << pair.second << ". Need to resize.";
375 return true;
376 }
377 LOG(INFO) << super_device << ":"
378 << BootControlInterface::SlotName(target_slot) << ":"
379 << pair.first << suffix << ": size == " << partition->size()
380 << " as requested.";
381 }
382 LOG(INFO) << "No need to resize at metadata slot "
383 << BootControlInterface::SlotName(target_slot) << " in "
384 << super_device;
385 return false;
386}
387} // namespace
388
389bool BootControlAndroid::InitPartitionMetadata(
390 Slot target_slot, const PartitionSizes& partition_sizes) {
391 if (!dynamic_control_->IsDynamicPartitionsEnabled()) {
392 return true;
393 }
394
395 string device_dir_str;
396 if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
397 return false;
398 }
399 base::FilePath device_dir(device_dir_str);
400 string super_device = device_dir.Append(LP_METADATA_PARTITION_NAME).value();
401
402 Slot current_slot = GetCurrentSlot();
403 if (target_slot == current_slot) {
404 LOG(ERROR) << "Cannot call InitPartitionMetadata on current slot.";
405 return false;
406 }
407
408 string current_suffix;
409 if (!GetSuffix(current_slot, &current_suffix)) {
410 return false;
411 }
412
413 string target_suffix;
414 if (!GetSuffix(target_slot, &target_suffix)) {
415 return false;
416 }
417
418 auto builder =
419 dynamic_control_->LoadMetadataBuilder(super_device, current_slot);
420 if (builder == nullptr) {
421 return false;
422 }
423
424 // Read metadata from current slot to determine which partitions are logical
425 // and may be resized. Do not read from target slot because metadata at
426 // target slot may be corrupted.
427 PartitionSizes logical_sizes;
428 if (!FilterPartitionSizes(dynamic_control_.get(),
429 device_dir,
430 partition_sizes,
431 builder.get() /* source metadata */,
432 current_suffix,
433 target_suffix,
434 &logical_sizes)) {
435 return false;
436 }
437
438 // Read metadata from target slot to determine if the sizes are correct. Only
439 // test logical partitions.
440 if (NeedResizePartitions(dynamic_control_.get(),
441 super_device,
442 target_slot,
443 target_suffix,
444 logical_sizes)) {
445 if (!ResizePartitions(dynamic_control_.get(),
446 super_device,
447 target_slot,
448 target_suffix,
449 logical_sizes,
450 builder.get())) {
451 return false;
452 }
453 }
454
455 // Unmap all partitions, and remap partitions if size is non-zero.
456 for (const auto& pair : logical_sizes) {
457 if (!dynamic_control_->UnmapPartitionOnDeviceMapper(
458 pair.first + target_suffix, true /* wait */)) {
459 return false;
460 }
461 if (pair.second == 0) {
462 continue;
463 }
464 string map_path;
465 if (!dynamic_control_->MapPartitionOnDeviceMapper(
466 super_device, pair.first + target_suffix, target_slot, &map_path)) {
467 return false;
468 }
469 }
470 return true;
471}
472
Alex Deymob17327c2015-09-04 10:29:00 -0700473} // namespace chromeos_update_engine