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