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