• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #define LOG_TAG "bootcontrolhal"
18 
19 #include "BootControl.h"
20 
21 #include <android-base/file.h>
22 #include <android-base/unique_fd.h>
23 #include <bootloader_message/bootloader_message.h>
24 #include <cutils/properties.h>
25 #include <libboot_control/libboot_control.h>
26 #include <log/log.h>
27 
28 #include "DevInfo.h"
29 #include "GptUtils.h"
30 
31 namespace android {
32 namespace hardware {
33 namespace boot {
34 namespace V1_2 {
35 namespace implementation {
36 
37 using android::bootable::GetMiscVirtualAbMergeStatus;
38 using android::bootable::InitMiscVirtualAbMessageIfNeeded;
39 using android::bootable::SetMiscVirtualAbMergeStatus;
40 using android::hardware::boot::V1_0::BoolResult;
41 using android::hardware::boot::V1_0::CommandResult;
42 using android::hardware::boot::V1_1::MergeStatus;
43 
44 namespace {
45 
46 // clang-format off
47 
48 #define BOOT_A_PATH     "/dev/block/by-name/boot_a"
49 #define BOOT_B_PATH     "/dev/block/by-name/boot_b"
50 #define DEVINFO_PATH    "/dev/block/by-name/devinfo"
51 
52 // slot flags
53 #define AB_ATTR_PRIORITY_SHIFT      52
54 #define AB_ATTR_PRIORITY_MASK       (3UL << AB_ATTR_PRIORITY_SHIFT)
55 #define AB_ATTR_ACTIVE_SHIFT        54
56 #define AB_ATTR_ACTIVE              (1UL << AB_ATTR_ACTIVE_SHIFT)
57 #define AB_ATTR_RETRY_COUNT_SHIFT   (55)
58 #define AB_ATTR_RETRY_COUNT_MASK    (7UL << AB_ATTR_RETRY_COUNT_SHIFT)
59 #define AB_ATTR_SUCCESSFUL          (1UL << 58)
60 #define AB_ATTR_UNBOOTABLE          (1UL << 59)
61 
62 #define AB_ATTR_MAX_PRIORITY        3UL
63 #define AB_ATTR_MAX_RETRY_COUNT     3UL
64 
65 // clang-format on
66 
getDevPath(uint32_t slot)67 static std::string getDevPath(uint32_t slot) {
68     char real_path[PATH_MAX];
69 
70     const char *path = slot == 0 ? BOOT_A_PATH : BOOT_B_PATH;
71 
72     int ret = readlink(path, real_path, sizeof real_path);
73     if (ret < 0) {
74         ALOGE("readlink failed for boot device %s\n", strerror(errno));
75         return std::string();
76     }
77 
78     std::string dp(real_path);
79     // extract /dev/sda.. part
80     return dp.substr(0, sizeof "/dev/block/sdX" - 1);
81 }
82 
isSlotFlagSet(uint32_t slot,uint64_t flag)83 static bool isSlotFlagSet(uint32_t slot, uint64_t flag) {
84     std::string dev_path = getDevPath(slot);
85     if (dev_path.empty()) {
86         ALOGI("Could not get device path for slot %d\n", slot);
87         return false;
88     }
89 
90     GptUtils gpt(dev_path);
91     if (gpt.Load()) {
92         ALOGI("failed to load gpt data\n");
93         return false;
94     }
95 
96     gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a");
97     if (e == nullptr) {
98         ALOGI("failed to get gpt entry\n");
99         return false;
100     }
101 
102     return !!(e->attr & flag);
103 }
104 
setSlotFlag(uint32_t slot,uint64_t flag)105 static bool setSlotFlag(uint32_t slot, uint64_t flag) {
106     std::string dev_path = getDevPath(slot);
107     if (dev_path.empty()) {
108         ALOGI("Could not get device path for slot %d\n", slot);
109         return false;
110     }
111 
112     GptUtils gpt(dev_path);
113     if (gpt.Load()) {
114         ALOGI("failed to load gpt data\n");
115         return false;
116     }
117 
118     gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a");
119     if (e == nullptr) {
120         ALOGI("failed to get gpt entry\n");
121         return false;
122     }
123 
124     e->attr |= flag;
125     gpt.Sync();
126 
127     return true;
128 }
129 
130 static bool is_devinfo_valid;
131 static bool is_devinfo_initialized;
132 static std::mutex devinfo_lock;
133 static devinfo_t devinfo;
134 
isDevInfoValid()135 static bool isDevInfoValid() {
136     const std::lock_guard<std::mutex> lock(devinfo_lock);
137 
138     if (is_devinfo_initialized) {
139         return is_devinfo_valid;
140     }
141 
142     is_devinfo_initialized = true;
143 
144     android::base::unique_fd fd(open(DEVINFO_PATH, O_RDONLY));
145     android::base::ReadFully(fd, &devinfo, sizeof devinfo);
146 
147     if (devinfo.magic != DEVINFO_MAGIC) {
148         return is_devinfo_valid;
149     }
150 
151     uint32_t version = ((uint32_t)devinfo.ver_major << 16) | devinfo.ver_minor;
152     // only version 3.3+ supports A/B data
153     if (version >= 0x0003'0003) {
154         is_devinfo_valid = true;
155     }
156 
157     return is_devinfo_valid;
158 }
159 
DevInfoSync()160 static bool DevInfoSync() {
161     if (!isDevInfoValid()) {
162         return false;
163     }
164 
165     android::base::unique_fd fd(open(DEVINFO_PATH, O_WRONLY | O_DSYNC));
166     return android::base::WriteFully(fd, &devinfo, sizeof devinfo);
167 }
168 
DevInfoInitSlot(devinfo_ab_slot_data_t & slot_data)169 static void DevInfoInitSlot(devinfo_ab_slot_data_t &slot_data) {
170     slot_data.retry_count = AB_ATTR_MAX_RETRY_COUNT;
171     slot_data.unbootable = 0;
172     slot_data.successful = 0;
173     slot_data.active = 1;
174     slot_data.fastboot_ok = 0;
175 }
176 
177 }  // namespace
178 
179 // Methods from ::android::hardware::boot::V1_0::IBootControl follow.
getNumberSlots()180 Return<uint32_t> BootControl::getNumberSlots() {
181     uint32_t slots = 0;
182 
183     if (access(BOOT_A_PATH, F_OK) == 0)
184         slots++;
185 
186     if (access(BOOT_B_PATH, F_OK) == 0)
187         slots++;
188 
189     return slots;
190 }
191 
getCurrentSlot()192 Return<uint32_t> BootControl::getCurrentSlot() {
193     char suffix[PROPERTY_VALUE_MAX];
194     property_get("ro.boot.slot_suffix", suffix, "_a");
195     return std::string(suffix) == "_b" ? 1 : 0;
196 }
197 
markBootSuccessful(markBootSuccessful_cb _hidl_cb)198 Return<void> BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) {
199     if (getNumberSlots() == 0) {
200         // no slots, just return true otherwise Android keeps trying
201         _hidl_cb({true, ""});
202         return Void();
203     }
204 
205     bool ret;
206     if (isDevInfoValid()) {
207         auto const slot = getCurrentSlot();
208         devinfo.ab_data.slots[slot].successful = 1;
209         ret = DevInfoSync();
210     } else {
211         ret = setSlotFlag(getCurrentSlot(), AB_ATTR_SUCCESSFUL);
212     }
213 
214     !ret ? _hidl_cb({false, "Failed to set successful flag"}) : _hidl_cb({true, ""});
215     return Void();
216 }
217 
setActiveBootSlot(uint32_t slot,setActiveBootSlot_cb _hidl_cb)218 Return<void> BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) {
219     if (slot >= 2) {
220         _hidl_cb({false, "Invalid slot"});
221         return Void();
222     }
223 
224     if (isDevInfoValid()) {
225         auto &active_slot_data = devinfo.ab_data.slots[slot];
226         auto &inactive_slot_data = devinfo.ab_data.slots[!slot];
227 
228         inactive_slot_data.active = 0;
229         DevInfoInitSlot(active_slot_data);
230 
231         if (!DevInfoSync()) {
232             _hidl_cb({false, "Could not update DevInfo data"});
233             return Void();
234         }
235     } else {
236         std::string dev_path = getDevPath(slot);
237         if (dev_path.empty()) {
238             _hidl_cb({false, "Could not get device path for slot"});
239             return Void();
240         }
241 
242         GptUtils gpt(dev_path);
243         if (gpt.Load()) {
244             _hidl_cb({false, "failed to load gpt data"});
245             return Void();
246         }
247 
248         gpt_entry *active_entry = gpt.GetPartitionEntry(slot == 0 ? "boot_a" : "boot_b");
249         gpt_entry *inactive_entry = gpt.GetPartitionEntry(slot == 0 ? "boot_b" : "boot_a");
250         if (active_entry == nullptr || inactive_entry == nullptr) {
251             _hidl_cb({false, "failed to get entries for boot partitions"});
252             return Void();
253         }
254 
255         ALOGV("slot active attributes %lx\n", active_entry->attr);
256         ALOGV("slot inactive attributes %lx\n", inactive_entry->attr);
257 
258         // update attributes for active and inactive
259         inactive_entry->attr &= ~AB_ATTR_ACTIVE;
260         active_entry->attr = AB_ATTR_ACTIVE | (AB_ATTR_MAX_PRIORITY << AB_ATTR_PRIORITY_SHIFT) |
261                              (AB_ATTR_MAX_RETRY_COUNT << AB_ATTR_RETRY_COUNT_SHIFT);
262     }
263 
264     char boot_dev[PROPERTY_VALUE_MAX];
265     property_get("ro.boot.bootdevice", boot_dev, "");
266     if (boot_dev[0] == '\0') {
267         _hidl_cb({false, "invalid ro.boot.bootdevice prop"});
268         return Void();
269     }
270 
271     std::string boot_lun_path =
272             std::string("/sys/devices/platform/") + boot_dev + "/pixel/boot_lun_enabled";
273     int fd = open(boot_lun_path.c_str(), O_RDWR | O_DSYNC);
274     if (fd < 0) {
275         // Try old path for kernels < 5.4
276         // TODO: remove once kernel 4.19 support is deprecated
277         std::string boot_lun_path =
278                 std::string("/sys/devices/platform/") + boot_dev + "/attributes/boot_lun_enabled";
279         fd = open(boot_lun_path.c_str(), O_RDWR | O_DSYNC);
280         if (fd < 0) {
281             _hidl_cb({false, "failed to open ufs attr boot_lun_enabled"});
282             return Void();
283         }
284     }
285 
286     //
287     // bBootLunEn
288     // 0x1  => Boot LU A = enabled, Boot LU B = disable
289     // 0x2  => Boot LU A = disable, Boot LU B = enabled
290     //
291     int ret = android::base::WriteStringToFd(slot == 0 ? "1" : "2", fd);
292     close(fd);
293     if (ret < 0) {
294         _hidl_cb({false, "faied to write boot_lun_enabled attribute"});
295         return Void();
296     }
297 
298     _hidl_cb({true, ""});
299     return Void();
300 }
301 
setSlotAsUnbootable(uint32_t slot,setSlotAsUnbootable_cb _hidl_cb)302 Return<void> BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) {
303     if (slot >= 2) {
304         _hidl_cb({false, "Invalid slot"});
305         return Void();
306     }
307 
308     if (isDevInfoValid()) {
309         auto &slot_data = devinfo.ab_data.slots[slot];
310         slot_data.unbootable = 1;
311         if (!DevInfoSync()) {
312             _hidl_cb({false, "Could not update DevInfo data"});
313             return Void();
314         }
315     } else {
316         std::string dev_path = getDevPath(slot);
317         if (dev_path.empty()) {
318             _hidl_cb({false, "Could not get device path for slot"});
319             return Void();
320         }
321 
322         GptUtils gpt(dev_path);
323         gpt.Load();
324 
325         gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a");
326         e->attr |= AB_ATTR_UNBOOTABLE;
327 
328         gpt.Sync();
329     }
330 
331     _hidl_cb({true, ""});
332     return Void();
333 }
334 
isSlotBootable(uint32_t slot)335 Return<::android::hardware::boot::V1_0::BoolResult> BootControl::isSlotBootable(uint32_t slot) {
336     if (getNumberSlots() == 0)
337         return BoolResult::FALSE;
338     if (slot >= getNumberSlots())
339         return BoolResult::INVALID_SLOT;
340 
341     bool unbootable;
342     if (isDevInfoValid()) {
343         auto &slot_data = devinfo.ab_data.slots[slot];
344         unbootable = !!slot_data.unbootable;
345     } else {
346         unbootable = isSlotFlagSet(slot, AB_ATTR_UNBOOTABLE);
347     }
348 
349     return unbootable ? BoolResult::FALSE : BoolResult::TRUE;
350 }
351 
isSlotMarkedSuccessful(uint32_t slot)352 Return<::android::hardware::boot::V1_0::BoolResult> BootControl::isSlotMarkedSuccessful(
353         uint32_t slot) {
354     if (getNumberSlots() == 0) {
355         // just return true so that we don't we another call trying to mark it as successful
356         // when there is no slots
357         return BoolResult::TRUE;
358     }
359     if (slot >= getNumberSlots())
360         return BoolResult::INVALID_SLOT;
361 
362     bool successful;
363     if (isDevInfoValid()) {
364         auto &slot_data = devinfo.ab_data.slots[slot];
365         successful = !!slot_data.successful;
366     } else {
367         successful = isSlotFlagSet(slot, AB_ATTR_SUCCESSFUL);
368     }
369 
370     return successful ? BoolResult::TRUE : BoolResult::FALSE;
371 }
372 
getSuffix(uint32_t slot,getSuffix_cb _hidl_cb)373 Return<void> BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) {
374     _hidl_cb(slot == 0 ? "_a" : slot == 1 ? "_b" : "");
375     return Void();
376 }
377 
378 // Methods from ::android::hardware::boot::V1_1::IBootControl follow.
Init()379 bool BootControl::Init() {
380     return InitMiscVirtualAbMessageIfNeeded();
381 }
382 
setSnapshotMergeStatus(::android::hardware::boot::V1_1::MergeStatus status)383 Return<bool> BootControl::setSnapshotMergeStatus(
384         ::android::hardware::boot::V1_1::MergeStatus status) {
385     return SetMiscVirtualAbMergeStatus(getCurrentSlot(), status);
386 }
387 
getSnapshotMergeStatus()388 Return<::android::hardware::boot::V1_1::MergeStatus> BootControl::getSnapshotMergeStatus() {
389     MergeStatus status;
390     if (!GetMiscVirtualAbMergeStatus(getCurrentSlot(), &status)) {
391         return MergeStatus::UNKNOWN;
392     }
393     return status;
394 }
395 
396 // Methods from ::android::hardware::boot::V1_2::IBootControl follow.
getActiveBootSlot()397 Return<uint32_t> BootControl::getActiveBootSlot() {
398     if (getNumberSlots() == 0)
399         return 0;
400 
401     if (isDevInfoValid())
402         return devinfo.ab_data.slots[1].active ? 1 : 0;
403     return isSlotFlagSet(1, AB_ATTR_ACTIVE) ? 1 : 0;
404 }
405 
406 // Methods from ::android::hidl::base::V1_0::IBase follow.
407 
HIDL_FETCH_IBootControl(const char *)408 IBootControl *HIDL_FETCH_IBootControl(const char * /* name */) {
409     auto module = new BootControl();
410 
411     module->Init();
412 
413     return module;
414 }
415 
416 }  // namespace implementation
417 }  // namespace V1_2
418 }  // namespace boot
419 }  // namespace hardware
420 }  // namespace android
421