• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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