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