• 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_recovery.h"
18 
19 #include <base/bind.h>
20 #include <base/files/file_util.h>
21 #include <base/logging.h>
22 #include <base/strings/string_util.h>
23 #include <brillo/make_unique_ptr.h>
24 #include <brillo/message_loops/message_loop.h>
25 
26 #include "update_engine/common/utils.h"
27 #include "update_engine/utils_android.h"
28 
29 using std::string;
30 
31 #ifndef _UE_SIDELOAD
32 #error "BootControlRecovery should only be used for update_engine_sideload."
33 #endif
34 
35 // When called from update_engine_sideload, we don't attempt to dynamically load
36 // the right boot_control HAL, instead we use the only HAL statically linked in
37 // via the PRODUCT_STATIC_BOOT_CONTROL_HAL make variable and access the module
38 // struct directly.
39 extern const hw_module_t HAL_MODULE_INFO_SYM;
40 
41 namespace chromeos_update_engine {
42 
43 namespace boot_control {
44 
45 // Factory defined in boot_control.h.
CreateBootControl()46 std::unique_ptr<BootControlInterface> CreateBootControl() {
47   std::unique_ptr<BootControlRecovery> boot_control(new BootControlRecovery());
48   if (!boot_control->Init()) {
49     return nullptr;
50   }
51   return std::move(boot_control);
52 }
53 
54 }  // namespace boot_control
55 
Init()56 bool BootControlRecovery::Init() {
57   const hw_module_t* hw_module;
58   int ret;
59 
60   // For update_engine_sideload, we simulate the hw_get_module() by accessing it
61   // from the current process directly.
62   hw_module = &HAL_MODULE_INFO_SYM;
63   ret = 0;
64   if (!hw_module ||
65       strcmp(BOOT_CONTROL_HARDWARE_MODULE_ID, hw_module->id) != 0) {
66     ret = -EINVAL;
67   }
68   if (ret != 0) {
69     LOG(ERROR) << "Error loading boot_control HAL implementation.";
70     return false;
71   }
72 
73   module_ = reinterpret_cast<boot_control_module_t*>(
74       const_cast<hw_module_t*>(hw_module));
75   module_->init(module_);
76 
77   LOG(INFO) << "Loaded boot_control HAL "
78             << "'" << hw_module->name << "' "
79             << "version " << (hw_module->module_api_version >> 8) << "."
80             << (hw_module->module_api_version & 0xff) << " "
81             << "authored by '" << hw_module->author << "'.";
82   return true;
83 }
84 
GetNumSlots() const85 unsigned int BootControlRecovery::GetNumSlots() const {
86   return module_->getNumberSlots(module_);
87 }
88 
GetCurrentSlot() const89 BootControlInterface::Slot BootControlRecovery::GetCurrentSlot() const {
90   return module_->getCurrentSlot(module_);
91 }
92 
GetPartitionDevice(const string & partition_name,Slot slot,string * device) const93 bool BootControlRecovery::GetPartitionDevice(const string& partition_name,
94                                              Slot slot,
95                                              string* device) const {
96   // We can't use fs_mgr to look up |partition_name| because fstab
97   // doesn't list every slot partition (it uses the slotselect option
98   // to mask the suffix).
99   //
100   // We can however assume that there's an entry for the /misc mount
101   // point and use that to get the device file for the misc
102   // partition. This helps us locate the disk that |partition_name|
103   // resides on. From there we'll assume that a by-name scheme is used
104   // so we can just replace the trailing "misc" by the given
105   // |partition_name| and suffix corresponding to |slot|, e.g.
106   //
107   //   /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
108   //   /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
109   //
110   // If needed, it's possible to relax the by-name assumption in the
111   // future by trawling /sys/block looking for the appropriate sibling
112   // of misc and then finding an entry in /dev matching the sysfs
113   // entry.
114 
115   base::FilePath misc_device;
116   if (!utils::DeviceForMountPoint("/misc", &misc_device))
117     return false;
118 
119   if (!utils::IsSymlink(misc_device.value().c_str())) {
120     LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
121                << "is not a symlink.";
122     return false;
123   }
124 
125   const char* suffix = module_->getSuffix(module_, slot);
126   if (suffix == nullptr) {
127     LOG(ERROR) << "boot_control impl returned no suffix for slot "
128                << SlotName(slot);
129     return false;
130   }
131 
132   base::FilePath path = misc_device.DirName().Append(partition_name + suffix);
133   if (!base::PathExists(path)) {
134     LOG(ERROR) << "Device file " << path.value() << " does not exist.";
135     return false;
136   }
137 
138   *device = path.value();
139   return true;
140 }
141 
IsSlotBootable(Slot slot) const142 bool BootControlRecovery::IsSlotBootable(Slot slot) const {
143   int ret = module_->isSlotBootable(module_, slot);
144   if (ret < 0) {
145     LOG(ERROR) << "Unable to determine if slot " << SlotName(slot)
146                << " is bootable: " << strerror(-ret);
147     return false;
148   }
149   return ret == 1;
150 }
151 
MarkSlotUnbootable(Slot slot)152 bool BootControlRecovery::MarkSlotUnbootable(Slot slot) {
153   int ret = module_->setSlotAsUnbootable(module_, slot);
154   if (ret < 0) {
155     LOG(ERROR) << "Unable to mark slot " << SlotName(slot)
156                << " as bootable: " << strerror(-ret);
157     return false;
158   }
159   return ret == 0;
160 }
161 
SetActiveBootSlot(Slot slot)162 bool BootControlRecovery::SetActiveBootSlot(Slot slot) {
163   int ret = module_->setActiveBootSlot(module_, slot);
164   if (ret < 0) {
165     LOG(ERROR) << "Unable to set the active slot to slot " << SlotName(slot)
166                << ": " << strerror(-ret);
167   }
168   return ret == 0;
169 }
170 
MarkBootSuccessfulAsync(base::Callback<void (bool)> callback)171 bool BootControlRecovery::MarkBootSuccessfulAsync(
172     base::Callback<void(bool)> callback) {
173   int ret = module_->markBootSuccessful(module_);
174   if (ret < 0) {
175     LOG(ERROR) << "Unable to mark boot successful: " << strerror(-ret);
176   }
177   return brillo::MessageLoop::current()->PostTask(
178              FROM_HERE, base::Bind(callback, ret == 0)) !=
179          brillo::MessageLoop::kTaskIdNull;
180 }
181 
182 }  // namespace chromeos_update_engine
183