1 /*
2 * Copyright (C) 2019 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 #include "GptUtils.h"
21
22 #include <android-base/file.h>
23 #include <cutils/properties.h>
24 #include <log/log.h>
25
26 namespace android {
27 namespace hardware {
28 namespace boot {
29 namespace V1_0 {
30 namespace implementation {
31
32 namespace {
33
34 #define BOOT_A_PATH "/dev/block/by-name/boot_a"
35 #define BOOT_B_PATH "/dev/block/by-name/boot_b"
36
37 // slot flags
38 #define AB_ATTR_PRIORITY_SHIFT 52
39 #define AB_ATTR_PRIORITY_MASK (3UL << AB_ATTR_PRIORITY_SHIFT)
40 #define AB_ATTR_ACTIVE_SHIFT 54
41 #define AB_ATTR_ACTIVE (1UL << AB_ATTR_ACTIVE_SHIFT)
42 #define AB_ATTR_RETRY_COUNT_SHIFT (55)
43 #define AB_ATTR_RETRY_COUNT_MASK (7UL << AB_ATTR_RETRY_COUNT_SHIFT)
44 #define AB_ATTR_SUCCESSFUL (1UL << 58)
45 #define AB_ATTR_UNBOOTABLE (1UL << 59)
46
47 #define AB_ATTR_MAX_PRIORITY 3UL
48 #define AB_ATTR_MAX_RETRY_COUNT 3UL
49
getDevPath(uint32_t slot)50 static std::string getDevPath(uint32_t slot) {
51 char real_path[PATH_MAX];
52
53 const char *path = slot == 0 ? BOOT_A_PATH : BOOT_B_PATH;
54
55 int ret = readlink(path, real_path, sizeof real_path);
56 if (ret < 0) {
57 ALOGE("readlink failed for boot device %s\n", strerror(errno));
58 return std::string();
59 }
60
61 std::string dp(real_path);
62 // extract /dev/sda.. part
63 return dp.substr(0, sizeof "/dev/block/sdX" - 1);
64 }
65
isSlotFlagSet(uint32_t slot,uint64_t flag)66 static bool isSlotFlagSet(uint32_t slot, uint64_t flag) {
67 std::string dev_path = getDevPath(slot);
68 if (dev_path.empty()) {
69 ALOGI("Could not get device path for slot %d\n", slot);
70 return false;
71 }
72
73 GptUtils gpt(dev_path);
74 if (gpt.Load()) {
75 ALOGI("failed to load gpt data\n");
76 return false;
77 }
78
79 gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a");
80 if (e == nullptr) {
81 ALOGI("failed to get gpt entry\n");
82 return false;
83 }
84
85 return !!(e->attr & flag);
86 }
87
setSlotFlag(uint32_t slot,uint64_t flag)88 static int setSlotFlag(uint32_t slot, uint64_t flag) {
89 std::string dev_path = getDevPath(slot);
90 if (dev_path.empty()) {
91 ALOGI("Could not get device path for slot %d\n", slot);
92 return -1;
93 }
94
95 GptUtils gpt(dev_path);
96 if (gpt.Load()) {
97 ALOGI("failed to load gpt data\n");
98 return -1;
99 }
100
101 gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a");
102 if (e == nullptr) {
103 ALOGI("failed to get gpt entry\n");
104 return -1;
105 }
106
107 e->attr |= flag;
108 gpt.Sync();
109
110 return 0;
111 }
112
113 }
114
115 // Methods from ::android::hardware::boot::V1_0::IBootControl follow.
getNumberSlots()116 Return<uint32_t> BootControl::getNumberSlots() {
117 uint32_t slots = 0;
118
119 if (access(BOOT_A_PATH, F_OK) == 0)
120 slots++;
121
122 if (access(BOOT_B_PATH, F_OK) == 0)
123 slots++;
124
125 return slots;
126 }
127
getCurrentSlot()128 Return<uint32_t> BootControl::getCurrentSlot() {
129 char suffix[PROPERTY_VALUE_MAX];
130 property_get("ro.boot.slot_suffix", suffix, "_a");
131 return std::string(suffix) == "_b" ? 1 : 0;
132 }
133
markBootSuccessful(markBootSuccessful_cb _hidl_cb)134 Return<void> BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) {
135 if (getNumberSlots() == 0) {
136 // no slots, just return true otherwise Android keeps trying
137 _hidl_cb({true, ""});
138 return Void();
139 }
140 int ret = setSlotFlag(getCurrentSlot(), AB_ATTR_SUCCESSFUL);
141 ret ? _hidl_cb({false, "Failed to set successfull flag"}) : _hidl_cb({true, ""});
142 return Void();
143 }
144
setActiveBootSlot(uint32_t slot,setActiveBootSlot_cb _hidl_cb)145 Return<void> BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) {
146 if (slot >= 2) {
147 _hidl_cb({false, "Invalid slot"});
148 return Void();
149 }
150
151 std::string dev_path = getDevPath(slot);
152 if (dev_path.empty()) {
153 _hidl_cb({false, "Could not get device path for slot"});
154 return Void();
155 }
156
157 GptUtils gpt(dev_path);
158 if (gpt.Load()) {
159 _hidl_cb({false, "failed to load gpt data"});
160 return Void();
161 }
162
163 gpt_entry *active_entry = gpt.GetPartitionEntry(slot == 0 ? "boot_a" : "boot_b");
164 gpt_entry *inactive_entry = gpt.GetPartitionEntry(slot == 0 ? "boot_b" : "boot_a");
165 if (active_entry == nullptr || inactive_entry == nullptr) {
166 _hidl_cb({false, "failed to get entries for boot partitions"});
167 return Void();
168 }
169
170 ALOGV("slot active attributes %lx\n", active_entry->attr);
171 ALOGV("slot inactive attributes %lx\n", inactive_entry->attr);
172
173 char boot_dev[PROPERTY_VALUE_MAX];
174 property_get("ro.boot.bootdevice", boot_dev, "");
175 if (boot_dev[0] == '\0') {
176 _hidl_cb({false, "invalid ro.boot.bootdevice prop"});
177 return Void();
178 }
179
180 std::string boot_lun_path = std::string("/sys/devices/platform/") +
181 boot_dev + "/pixel/boot_lun_enabled";
182 int fd = open(boot_lun_path.c_str(), O_RDWR);
183 if (fd < 0) {
184 // Try old path for kernels < 5.4
185 // TODO: remove once kernel 4.19 support is deprecated
186 std::string boot_lun_path = std::string("/sys/devices/platform/") +
187 boot_dev + "/attributes/boot_lun_enabled";
188 fd = open(boot_lun_path.c_str(), O_RDWR);
189 if (fd < 0) {
190 _hidl_cb({false, "failed to open ufs attr boot_lun_enabled"});
191 return Void();
192 }
193 }
194
195 // update attributes for active and inactive
196 inactive_entry->attr &= ~AB_ATTR_ACTIVE;
197 active_entry->attr = AB_ATTR_ACTIVE | (AB_ATTR_MAX_PRIORITY << AB_ATTR_PRIORITY_SHIFT) |
198 (AB_ATTR_MAX_RETRY_COUNT << AB_ATTR_RETRY_COUNT_SHIFT);
199
200 //
201 // bBootLunEn
202 // 0x1 => Boot LU A = enabled, Boot LU B = disable
203 // 0x2 => Boot LU A = disable, Boot LU B = enabled
204 //
205 int ret = android::base::WriteStringToFd(slot == 0 ? "1" : "2", fd);
206 close(fd);
207 if (ret < 0) {
208 _hidl_cb({false, "faied to write boot_lun_enabled attribute"});
209 return Void();
210 }
211
212 _hidl_cb({true, ""});
213 return Void();
214 }
215
setSlotAsUnbootable(uint32_t slot,setSlotAsUnbootable_cb _hidl_cb)216 Return<void> BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) {
217 if (slot >= 2) {
218 _hidl_cb({false, "Invalid slot"});
219 return Void();
220 }
221
222 std::string dev_path = getDevPath(slot);
223 if (dev_path.empty()) {
224 _hidl_cb({false, "Could not get device path for slot"});
225 return Void();
226 }
227
228 GptUtils gpt(dev_path);
229 gpt.Load();
230
231 gpt_entry *e = gpt.GetPartitionEntry(slot ? "boot_b" : "boot_a");
232 e->attr |= AB_ATTR_UNBOOTABLE;
233
234 gpt.Sync();
235
236 _hidl_cb({true, ""});
237 return Void();
238 }
239
isSlotBootable(uint32_t slot)240 Return<::android::hardware::boot::V1_0::BoolResult> BootControl::isSlotBootable(uint32_t slot) {
241 if (getNumberSlots() == 0)
242 return BoolResult::FALSE;
243 if (slot >= getNumberSlots())
244 return BoolResult::INVALID_SLOT;
245 return isSlotFlagSet(slot, AB_ATTR_UNBOOTABLE) ? BoolResult::FALSE : BoolResult::TRUE;
246 }
247
isSlotMarkedSuccessful(uint32_t slot)248 Return<::android::hardware::boot::V1_0::BoolResult> BootControl::isSlotMarkedSuccessful(uint32_t slot) {
249 if (getNumberSlots() == 0) {
250 // just return true so that we don't we another call trying to mark it as successful
251 // when there is no slots
252 return BoolResult::TRUE;
253 }
254 if (slot >= getNumberSlots())
255 return BoolResult::INVALID_SLOT;
256 return isSlotFlagSet(slot, AB_ATTR_SUCCESSFUL) ? BoolResult::TRUE : BoolResult::FALSE;
257 }
258
getSuffix(uint32_t slot,getSuffix_cb _hidl_cb)259 Return<void> BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) {
260 _hidl_cb(slot == 0 ? "_a" : slot == 1 ? "_b" : "");
261 return Void();
262 }
263
HIDL_FETCH_IBootControl(const char *)264 extern "C" IBootControl* HIDL_FETCH_IBootControl(const char*) {
265 return new BootControl();
266 }
267
268 } // namespace implementation
269 } // namespace V1_0
270 } // namespace boot
271 } // namespace hardware
272 } // namespace android
273