blob: 7aa72c3bc4fd1ff88d7f29192c00c08300370644 [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
Connor O'Briencee6ad92016-11-21 13:53:52 -080047auto StoreResultCallback(CommandResult* dest) {
48 return [dest](const CommandResult& result) { *dest = result; };
49}
50} // namespace
Alex Deymo44348e02016-07-29 16:22:26 -070051
Alex Deymob17327c2015-09-04 10:29:00 -070052namespace chromeos_update_engine {
53
54namespace boot_control {
55
56// Factory defined in boot_control.h.
57std::unique_ptr<BootControlInterface> CreateBootControl() {
Yifan Hong537802d2018-08-15 13:15:42 -070058 auto boot_control = std::make_unique<BootControlAndroid>();
David Zeuthen753fadc2015-09-15 16:34:09 -040059 if (!boot_control->Init()) {
60 return nullptr;
61 }
Alex Vakulenkoce8c8ee2016-04-08 08:59:26 -070062 return std::move(boot_control);
Alex Deymob17327c2015-09-04 10:29:00 -070063}
64
65} // namespace boot_control
66
David Zeuthen753fadc2015-09-15 16:34:09 -040067bool BootControlAndroid::Init() {
Chris Phoenixafde8e82017-01-17 23:14:58 -080068 module_ = IBootControl::getService();
Connor O'Briencee6ad92016-11-21 13:53:52 -080069 if (module_ == nullptr) {
Steven Moreland927e00d2017-01-04 12:58:40 -080070 LOG(ERROR) << "Error getting bootctrl HIDL module.";
David Zeuthen753fadc2015-09-15 16:34:09 -040071 return false;
72 }
73
Steven Moreland927e00d2017-01-04 12:58:40 -080074 LOG(INFO) << "Loaded boot control hidl hal.";
David Zeuthen753fadc2015-09-15 16:34:09 -040075
Yifan Hong537802d2018-08-15 13:15:42 -070076 dynamic_control_ = std::make_unique<DynamicPartitionControlAndroid>();
77
David Zeuthen753fadc2015-09-15 16:34:09 -040078 return true;
79}
Alex Deymob17327c2015-09-04 10:29:00 -070080
Yifan Hong537802d2018-08-15 13:15:42 -070081void BootControlAndroid::Cleanup() {
82 dynamic_control_->Cleanup();
83}
84
Alex Deymob17327c2015-09-04 10:29:00 -070085unsigned int BootControlAndroid::GetNumSlots() const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080086 return module_->getNumberSlots();
Alex Deymob17327c2015-09-04 10:29:00 -070087}
88
89BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080090 return module_->getCurrentSlot();
Alex Deymob17327c2015-09-04 10:29:00 -070091}
92
Yifan Hong537802d2018-08-15 13:15:42 -070093bool BootControlAndroid::GetSuffix(Slot slot, string* suffix) const {
Connor O'Briencee6ad92016-11-21 13:53:52 -080094 auto store_suffix_cb = [&suffix](hidl_string cb_suffix) {
Yifan Hong537802d2018-08-15 13:15:42 -070095 *suffix = cb_suffix.c_str();
Connor O'Briencee6ad92016-11-21 13:53:52 -080096 };
97 Return<void> ret = module_->getSuffix(slot, store_suffix_cb);
98
Yifan Hong7b514b42016-12-21 13:02:00 -080099 if (!ret.isOk()) {
Alex Deymo31d95ac2015-09-17 11:56:18 -0700100 LOG(ERROR) << "boot_control impl returned no suffix for slot "
101 << SlotName(slot);
David Zeuthen753fadc2015-09-15 16:34:09 -0400102 return false;
103 }
Yifan Hong537802d2018-08-15 13:15:42 -0700104 return true;
105}
106
107bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
108 Slot slot,
109 string* device) const {
110 string suffix;
111 if (!GetSuffix(slot, &suffix)) {
112 return false;
113 }
114
115 const string target_partition_name = partition_name + suffix;
116
117 // DeltaPerformer calls InitPartitionMetadata before calling
118 // InstallPlan::LoadPartitionsFromSlots. After InitPartitionMetadata,
119 // the partition must be re-mapped with force_writable == true. Hence,
120 // we only need to check device mapper.
121 if (dynamic_control_->IsDynamicPartitionsEnabled()) {
122 switch (dynamic_control_->GetState(target_partition_name)) {
123 case DmDeviceState::ACTIVE:
124 if (dynamic_control_->GetDmDevicePathByName(target_partition_name,
125 device)) {
126 LOG(INFO) << target_partition_name
127 << " is mapped on device mapper: " << *device;
128 return true;
129 }
130 LOG(ERROR) << target_partition_name
131 << " is mapped but path is unknown.";
132 return false;
133
134 case DmDeviceState::INVALID:
135 // Try static partitions.
136 break;
137
138 case DmDeviceState::SUSPENDED: // fallthrough
139 default:
140 LOG(ERROR) << target_partition_name
141 << " is mapped on device mapper but state is unknown";
142 return false;
143 }
144 }
145
146 string device_dir_str;
147 if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
148 return false;
149 }
David Zeuthen753fadc2015-09-15 16:34:09 -0400150
Sen Jiangd944faa2018-08-22 18:46:39 -0700151 base::FilePath path =
Yifan Hong537802d2018-08-15 13:15:42 -0700152 base::FilePath(device_dir_str).Append(target_partition_name);
153 if (!dynamic_control_->DeviceExists(path.value())) {
David Zeuthen753fadc2015-09-15 16:34:09 -0400154 LOG(ERROR) << "Device file " << path.value() << " does not exist.";
155 return false;
156 }
157
158 *device = path.value();
159 return true;
Alex Deymob17327c2015-09-04 10:29:00 -0700160}
161
162bool BootControlAndroid::IsSlotBootable(Slot slot) const {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800163 Return<BoolResult> ret = module_->isSlotBootable(slot);
Yifan Hong7b514b42016-12-21 13:02:00 -0800164 if (!ret.isOk()) {
Alex Deymo31d95ac2015-09-17 11:56:18 -0700165 LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
Connor O'Briencee6ad92016-11-21 13:53:52 -0800166 << " is bootable: "
Yifan Hong7b514b42016-12-21 13:02:00 -0800167 << ret.description();
David Zeuthen753fadc2015-09-15 16:34:09 -0400168 return false;
169 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800170 if (ret == BoolResult::INVALID_SLOT) {
171 LOG(ERROR) << "Invalid slot: " << SlotName(slot);
172 return false;
173 }
174 return ret == BoolResult::TRUE;
Alex Deymob17327c2015-09-04 10:29:00 -0700175}
176
177bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800178 CommandResult result;
179 auto ret = module_->setSlotAsUnbootable(slot, StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800180 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800181 LOG(ERROR) << "Unable to call MarkSlotUnbootable for slot "
182 << SlotName(slot) << ": "
Yifan Hong7b514b42016-12-21 13:02:00 -0800183 << ret.description();
David Zeuthen753fadc2015-09-15 16:34:09 -0400184 return false;
185 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800186 if (!result.success) {
187 LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
188 << " as unbootable: " << result.errMsg.c_str();
189 }
190 return result.success;
Alex Deymob17327c2015-09-04 10:29:00 -0700191}
192
Alex Deymo31d95ac2015-09-17 11:56:18 -0700193bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800194 CommandResult result;
195 auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800196 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800197 LOG(ERROR) << "Unable to call SetActiveBootSlot for slot " << SlotName(slot)
Yifan Hong7b514b42016-12-21 13:02:00 -0800198 << ": " << ret.description();
Connor O'Briencee6ad92016-11-21 13:53:52 -0800199 return false;
Alex Deymo29dcbf32016-10-06 13:33:20 -0700200 }
Connor O'Briencee6ad92016-11-21 13:53:52 -0800201 if (!result.success) {
202 LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
203 << ": " << result.errMsg.c_str();
204 }
205 return result.success;
Alex Deymo31d95ac2015-09-17 11:56:18 -0700206}
207
Alex Deymoaa26f622015-09-16 18:21:27 -0700208bool BootControlAndroid::MarkBootSuccessfulAsync(
209 base::Callback<void(bool)> callback) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800210 CommandResult result;
211 auto ret = module_->markBootSuccessful(StoreResultCallback(&result));
Yifan Hong7b514b42016-12-21 13:02:00 -0800212 if (!ret.isOk()) {
Connor O'Briencee6ad92016-11-21 13:53:52 -0800213 LOG(ERROR) << "Unable to call MarkBootSuccessful: "
Yifan Hong7b514b42016-12-21 13:02:00 -0800214 << ret.description();
Connor O'Briencee6ad92016-11-21 13:53:52 -0800215 return false;
216 }
217 if (!result.success) {
218 LOG(ERROR) << "Unable to mark boot successful: " << result.errMsg.c_str();
Alex Deymoaa26f622015-09-16 18:21:27 -0700219 }
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700220 return brillo::MessageLoop::current()->PostTask(
Connor O'Briencee6ad92016-11-21 13:53:52 -0800221 FROM_HERE, base::Bind(callback, result.success)) !=
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700222 brillo::MessageLoop::kTaskIdNull;
Alex Deymoaa26f622015-09-16 18:21:27 -0700223}
224
Yifan Hong537802d2018-08-15 13:15:42 -0700225namespace {
226
227// Resize |partition_name|_|slot| to the given |size|.
228bool ResizePartition(MetadataBuilder* builder,
229 const string& target_partition_name,
230 uint64_t size) {
231 Partition* partition = builder->FindPartition(target_partition_name);
232 if (partition == nullptr) {
233 LOG(ERROR) << "Cannot find " << target_partition_name << " in metadata.";
234 return false;
235 }
236
237 uint64_t old_size = partition->size();
238 const string action = "resize " + target_partition_name + " in super (" +
239 std::to_string(old_size) + " -> " +
240 std::to_string(size) + " bytes)";
241 if (!builder->ResizePartition(partition, size)) {
242 LOG(ERROR) << "Cannot " << action << "; see previous log messages.";
243 return false;
244 }
245
246 if (partition->size() != size) {
247 LOG(ERROR) << "Cannot " << action
248 << "; value is misaligned and partition should have been "
249 << partition->size();
250 return false;
251 }
252
253 LOG(INFO) << "Successfully " << action;
254
255 return true;
256}
257
258bool ResizePartitions(DynamicPartitionControlInterface* dynamic_control,
259 const string& super_device,
260 Slot target_slot,
261 const string& target_suffix,
262 const PartitionSizes& logical_sizes,
263 MetadataBuilder* builder) {
264 // Delete all extents to ensure that each partition has enough space to
265 // grow.
266 for (const auto& pair : logical_sizes) {
267 const string target_partition_name = pair.first + target_suffix;
268 if (builder->FindPartition(target_partition_name) == nullptr) {
269 // Use constant GUID because it is unused.
270 LOG(INFO) << "Adding partition " << target_partition_name << " to slot "
271 << BootControlInterface::SlotName(target_slot) << " in "
272 << super_device;
273 if (builder->AddPartition(target_partition_name,
Yifan Hong537802d2018-08-15 13:15:42 -0700274 LP_PARTITION_ATTR_READONLY) == nullptr) {
275 LOG(ERROR) << "Cannot add partition " << target_partition_name;
276 return false;
277 }
278 }
279 if (!ResizePartition(builder, pair.first + target_suffix, 0 /* size */)) {
280 return false;
281 }
282 }
283
284 for (const auto& pair : logical_sizes) {
285 if (!ResizePartition(builder, pair.first + target_suffix, pair.second)) {
286 LOG(ERROR) << "Not enough space?";
287 return false;
288 }
289 }
290
291 if (!dynamic_control->StoreMetadata(super_device, builder, target_slot)) {
292 return false;
293 }
294 return true;
295}
296
297// Assume upgrading from slot A to B. A partition foo is considered dynamic
298// iff one of the following:
299// 1. foo_a exists as a dynamic partition (so it should continue to be a
300// dynamic partition)
301// 2. foo_b does not exist as a static partition (in which case we may be
302// adding a new partition).
303bool IsDynamicPartition(DynamicPartitionControlInterface* dynamic_control,
304 const base::FilePath& device_dir,
305 MetadataBuilder* source_metadata,
306 const string& partition_name,
307 const string& source_suffix,
308 const string& target_suffix) {
309 bool dynamic_source_exist =
310 source_metadata->FindPartition(partition_name + source_suffix) != nullptr;
311 bool static_target_exist = dynamic_control->DeviceExists(
312 device_dir.Append(partition_name + target_suffix).value());
313
314 return dynamic_source_exist || !static_target_exist;
315}
316
317bool FilterPartitionSizes(DynamicPartitionControlInterface* dynamic_control,
318 const base::FilePath& device_dir,
319 const PartitionSizes& partition_sizes,
320 MetadataBuilder* source_metadata,
321 const string& source_suffix,
322 const string& target_suffix,
323 PartitionSizes* logical_sizes) {
324 for (const auto& pair : partition_sizes) {
325 if (!IsDynamicPartition(dynamic_control,
326 device_dir,
327 source_metadata,
328 pair.first,
329 source_suffix,
330 target_suffix)) {
331 // In the future we can check static partition sizes, but skip for now.
332 LOG(INFO) << pair.first << " is static; assume its size is "
333 << pair.second << " bytes.";
334 continue;
335 }
336
337 logical_sizes->insert(pair);
338 }
339 return true;
340}
341
342// Return false if partition sizes are all correct in metadata slot
343// |target_slot|. If so, no need to resize. |logical_sizes| have format like
344// {vendor: size, ...}, and fail if a partition is not found.
345bool NeedResizePartitions(DynamicPartitionControlInterface* dynamic_control,
346 const string& super_device,
347 Slot target_slot,
348 const string& suffix,
349 const PartitionSizes& logical_sizes) {
350 auto target_metadata =
351 dynamic_control->LoadMetadataBuilder(super_device, target_slot);
352 if (target_metadata == nullptr) {
353 LOG(INFO) << "Metadata slot " << BootControlInterface::SlotName(target_slot)
354 << " in " << super_device
355 << " is corrupted; attempt to recover from source slot.";
356 return true;
357 }
358
359 for (const auto& pair : logical_sizes) {
360 Partition* partition = target_metadata->FindPartition(pair.first + suffix);
361 if (partition == nullptr) {
362 LOG(INFO) << "Cannot find " << pair.first << suffix << " at slot "
363 << BootControlInterface::SlotName(target_slot) << " in "
364 << super_device << ". Need to resize.";
365 return true;
366 }
367 if (partition->size() != pair.second) {
368 LOG(INFO) << super_device << ":"
369 << BootControlInterface::SlotName(target_slot) << ":"
370 << pair.first << suffix << ": size == " << partition->size()
371 << " but requested " << pair.second << ". Need to resize.";
372 return true;
373 }
374 LOG(INFO) << super_device << ":"
375 << BootControlInterface::SlotName(target_slot) << ":"
376 << pair.first << suffix << ": size == " << partition->size()
377 << " as requested.";
378 }
379 LOG(INFO) << "No need to resize at metadata slot "
380 << BootControlInterface::SlotName(target_slot) << " in "
381 << super_device;
382 return false;
383}
384} // namespace
385
386bool BootControlAndroid::InitPartitionMetadata(
387 Slot target_slot, const PartitionSizes& partition_sizes) {
388 if (!dynamic_control_->IsDynamicPartitionsEnabled()) {
389 return true;
390 }
391
392 string device_dir_str;
393 if (!dynamic_control_->GetDeviceDir(&device_dir_str)) {
394 return false;
395 }
396 base::FilePath device_dir(device_dir_str);
397 string super_device = device_dir.Append(LP_METADATA_PARTITION_NAME).value();
398
399 Slot current_slot = GetCurrentSlot();
400 if (target_slot == current_slot) {
401 LOG(ERROR) << "Cannot call InitPartitionMetadata on current slot.";
402 return false;
403 }
404
405 string current_suffix;
406 if (!GetSuffix(current_slot, &current_suffix)) {
407 return false;
408 }
409
410 string target_suffix;
411 if (!GetSuffix(target_slot, &target_suffix)) {
412 return false;
413 }
414
415 auto builder =
416 dynamic_control_->LoadMetadataBuilder(super_device, current_slot);
417 if (builder == nullptr) {
418 return false;
419 }
420
421 // Read metadata from current slot to determine which partitions are logical
422 // and may be resized. Do not read from target slot because metadata at
423 // target slot may be corrupted.
424 PartitionSizes logical_sizes;
425 if (!FilterPartitionSizes(dynamic_control_.get(),
426 device_dir,
427 partition_sizes,
428 builder.get() /* source metadata */,
429 current_suffix,
430 target_suffix,
431 &logical_sizes)) {
432 return false;
433 }
434
435 // Read metadata from target slot to determine if the sizes are correct. Only
436 // test logical partitions.
437 if (NeedResizePartitions(dynamic_control_.get(),
438 super_device,
439 target_slot,
440 target_suffix,
441 logical_sizes)) {
442 if (!ResizePartitions(dynamic_control_.get(),
443 super_device,
444 target_slot,
445 target_suffix,
446 logical_sizes,
447 builder.get())) {
448 return false;
449 }
450 }
451
452 // Unmap all partitions, and remap partitions if size is non-zero.
453 for (const auto& pair : logical_sizes) {
454 if (!dynamic_control_->UnmapPartitionOnDeviceMapper(
455 pair.first + target_suffix, true /* wait */)) {
456 return false;
457 }
458 if (pair.second == 0) {
459 continue;
460 }
461 string map_path;
462 if (!dynamic_control_->MapPartitionOnDeviceMapper(
463 super_device, pair.first + target_suffix, target_slot, &map_path)) {
464 return false;
465 }
466 }
467 return true;
468}
469
Alex Deymob17327c2015-09-04 10:29:00 -0700470} // namespace chromeos_update_engine