1 /*
2 * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/
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 #include <log/log.h>
18 #include <cutils/properties.h>
19 #include <zlib.h>
20 #include <hardware/boot_control.h>
21 #include <bootloader_message.h>
22
23 #include <string>
24
25 #define BOOT_SLOT_PROP "ro.boot.slot_suffix"
26
27 struct BootControlPrivate {
28 // The base struct needs to be first in the list.
29 boot_control_module_t base;
30
31 // Whether this struct was initialized with data from the bootloader message
32 // that doesn't change until next reboot.
33 bool initialized;
34
35 // The path to the misc_device as reported in the fstab.
36 const char* misc_device;
37
38 // The number of slots present on the device.
39 unsigned int num_slots;
40
41 // The slot where we are running from.
42 unsigned int current_slot;
43 };
44
45 constexpr unsigned int kMaxNumSlots =
46 sizeof(bootloader_control::slot_info) /
47 sizeof(bootloader_control::slot_info[0]);
48
49 constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
50
51 // Return the little-endian representation of the CRC-32 of the first fields
52 // in |boot_ctrl| up to the crc32_le field.
GetBootloaderControlCRC(const bootloader_control * boot_ctrl)53 static uint32_t GetBootloaderControlCRC(const bootloader_control* boot_ctrl) {
54 return crc32(0, (const uint8_t*)boot_ctrl,
55 offsetof(bootloader_control, crc32_le));
56 }
57
LoadBootloaderControl(const char * misc_device,bootloader_control * boot_ctrl)58 static bool LoadBootloaderControl(const char* misc_device,
59 bootloader_control* boot_ctrl) {
60 std::string str_err;
61 if (read_bootloader_control_from(boot_ctrl, misc_device, &str_err))
62 return true;
63
64 ALOGE("%s", str_err.c_str());
65
66 return false;
67 }
68
SaveBootloaderControl(const char * misc_device,bootloader_control * boot_ctrl)69 static bool SaveBootloaderControl(const char* misc_device,
70 bootloader_control* boot_ctrl) {
71 boot_ctrl->crc32_le = GetBootloaderControlCRC(boot_ctrl);
72
73 std::string str_err;
74 if (write_bootloader_control_to(boot_ctrl, misc_device, &str_err))
75 return true;
76
77 ALOGE("%s", str_err.c_str());
78
79 return false;
80 }
81
82 // Return the index of the slot suffix passed or -1 if not a valid slot suffix.
SlotSuffixToIndex(const char * suffix)83 static int SlotSuffixToIndex(const char* suffix) {
84 for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
85 if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
86 }
87
88 return -1;
89 }
90
IsInitialized(const BootControlPrivate * module)91 static bool IsInitialized(const BootControlPrivate* module) {
92 if (!module->initialized) {
93 ALOGW("Module not initialized");
94 return false;
95 }
96
97 return true;
98 }
99
BootControlInit(boot_control_module_t * module)100 void BootControlInit(boot_control_module_t* module) {
101 struct BootControlPrivate* bootctrl_module =
102 reinterpret_cast<BootControlPrivate*>(module);
103
104 if (bootctrl_module->initialized) return;
105
106 if (!module) {
107 ALOGE("Invalid argument passed to %s", __func__);
108 return;
109 }
110
111 ALOGI("Init %s", module->common.name);
112
113 // Initialize the current_slot from the read-only property. If the property
114 // was not set (from either the command line or the device tree), we can later
115 // initialize it from the bootloader_control struct.
116 char suffix_prop[PROPERTY_VALUE_MAX] = {0};
117 property_get(BOOT_SLOT_PROP, suffix_prop, "");
118 bootctrl_module->current_slot = SlotSuffixToIndex(suffix_prop);
119
120 std::string err;
121 std::string device = get_bootloader_message_blk_device(&err);
122
123 bootloader_control boot_ctrl;
124 if (!LoadBootloaderControl(device.c_str(), &boot_ctrl))
125 ALOGE("Error loading metadata");
126
127 // Note that since there isn't a module unload function this memory is leaked.
128 bootctrl_module->misc_device = strdup(device.c_str());
129 uint32_t computed_crc32 = GetBootloaderControlCRC(&boot_ctrl);
130 if (boot_ctrl.crc32_le != computed_crc32) {
131 ALOGE("Invalid boot control found, expected CRC-32 0x%04X, "
132 "but found 0x%04X. Should re-initializing A/B metadata.",
133 computed_crc32, boot_ctrl.crc32_le);
134 return;
135 }
136
137 std::string metadata_suffix = "_" + std::string(boot_ctrl.slot_suffix);
138 if (SlotSuffixToIndex(metadata_suffix.c_str()) !=
139 bootctrl_module->current_slot) {
140 ALOGE("Kernel slot argument and A/B metadata do not match, "
141 "%s=%s, slot metadata=%s", BOOT_SLOT_PROP, suffix_prop,
142 boot_ctrl.slot_suffix);
143 return;
144 }
145
146 bootctrl_module->initialized = true;
147 bootctrl_module->num_slots = boot_ctrl.nb_slot;
148
149 ALOGI("Current slot: %s(%d), number of slots: %d", boot_ctrl.slot_suffix,
150 bootctrl_module->current_slot, bootctrl_module->num_slots);
151
152 return;
153 }
154
GetNumberSlots(boot_control_module_t * module)155 unsigned int GetNumberSlots(boot_control_module_t* module) {
156 BootControlPrivate* const bootctrl_module =
157 reinterpret_cast<BootControlPrivate*>(module);
158
159 if (!IsInitialized(bootctrl_module)) return -1;
160
161 return bootctrl_module->num_slots;
162 }
163
GetCurrentSlot(boot_control_module_t * module)164 unsigned int GetCurrentSlot(boot_control_module_t* module) {
165 BootControlPrivate* const bootctrl_module =
166 reinterpret_cast<BootControlPrivate*>(module);
167
168 if (!IsInitialized(bootctrl_module)) return -1;
169
170 return bootctrl_module->current_slot;
171 }
172
IsSlotMarkedSuccessful(boot_control_module_t * module,unsigned int slot)173 int IsSlotMarkedSuccessful(boot_control_module_t* module, unsigned int slot) {
174 BootControlPrivate* const bootctrl_module =
175 reinterpret_cast<BootControlPrivate*>(module);
176
177 if (!IsInitialized(bootctrl_module)) return -1;
178
179 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
180 // Invalid slot number.
181 return -1;
182 }
183
184 bootloader_control bootctrl;
185 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
186 return -1;
187
188 return (bootctrl.slot_info[slot].successful_boot &&
189 bootctrl.slot_info[slot].tries_remaining);
190 }
191
MarkBootSuccessful(boot_control_module_t * module)192 int MarkBootSuccessful(boot_control_module_t* module) {
193 BootControlPrivate* const bootctrl_module =
194 reinterpret_cast<BootControlPrivate*>(module);
195
196 if (!IsInitialized(bootctrl_module)) return -1;
197
198 bootloader_control bootctrl;
199 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
200 return -1;
201
202 bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1;
203 // tries_remaining == 0 means that the slot is not bootable anymore, make
204 // sure we mark the current slot as bootable if it succeeds in the last
205 // attempt.
206 bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1;
207 if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
208 return -1;
209
210 ALOGI("Slot %d is marked as successfully booted",
211 bootctrl_module->current_slot);
212
213 return 0;
214 }
215
SetActiveBootSlot(boot_control_module_t * module,unsigned int slot)216 int SetActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
217 BootControlPrivate* const bootctrl_module =
218 reinterpret_cast<BootControlPrivate*>(module);
219
220 if (!IsInitialized(bootctrl_module))
221 return -1;
222
223 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
224 // Invalid slot number.
225 return -1;
226 }
227
228 bootloader_control bootctrl;
229 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
230 return -1;
231
232 // Set every other slot with a lower priority than the new "active" slot.
233 const unsigned int kActivePriority = 15;
234 const unsigned int kActiveTries = 6;
235 for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) {
236 if (i != slot) {
237 if (bootctrl.slot_info[i].priority >= kActivePriority)
238 bootctrl.slot_info[i].priority = kActivePriority - 1;
239 }
240 }
241
242 // Note that setting a slot as active doesn't change the successful bit.
243 // The successful bit will only be changed by setSlotAsUnbootable().
244 bootctrl.slot_info[slot].priority = kActivePriority;
245 bootctrl.slot_info[slot].tries_remaining = kActiveTries;
246
247 // Setting the current slot as active is a way to revert the operation that
248 // set *another* slot as active at the end of an updater. This is commonly
249 // used to cancel the pending update. We should only reset the verity_corrpted
250 // bit when attempting a new slot, otherwise the verity bit on the current
251 // slot would be flip.
252 if (slot != bootctrl_module->current_slot)
253 bootctrl.slot_info[slot].verity_corrupted = 0;
254
255 if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
256 return -1;
257
258 ALOGI("Slot %d is set as active", slot);
259
260 return 0;
261 }
262
SetSlotAsUnbootable(boot_control_module_t * module,unsigned int slot)263 int SetSlotAsUnbootable(boot_control_module_t* module, unsigned int slot) {
264 BootControlPrivate* const bootctrl_module =
265 reinterpret_cast<BootControlPrivate*>(module);
266
267 if (!IsInitialized(bootctrl_module))
268 return -1;
269
270 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
271 // Invalid slot number.
272 return -1;
273 }
274
275 bootloader_control bootctrl;
276 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
277 return -1;
278
279 // The only way to mark a slot as unbootable, regardless of the priority is to
280 // set the tries_remaining to 0.
281 bootctrl.slot_info[slot].successful_boot = 0;
282 bootctrl.slot_info[slot].tries_remaining = 0;
283 if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
284 return -1;
285
286 ALOGI("Slot %d is marked as unbootable", slot);
287
288 return 0;
289 }
290
IsSlotBootable(struct boot_control_module * module,unsigned int slot)291 int IsSlotBootable(struct boot_control_module* module, unsigned int slot) {
292 BootControlPrivate* const bootctrl_module =
293 reinterpret_cast<BootControlPrivate*>(module);
294
295 if (!IsInitialized(bootctrl_module)) return -1;
296
297 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
298 // Invalid slot number.
299 return -1;
300 }
301
302 bootloader_control bootctrl;
303 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
304 return -1;
305
306 return bootctrl.slot_info[slot].tries_remaining;
307 }
308
GetSuffix(boot_control_module_t * module,unsigned int slot)309 const char* GetSuffix(boot_control_module_t* module, unsigned int slot) {
310 BootControlPrivate* const bootctrl_module =
311 reinterpret_cast<BootControlPrivate*>(module);
312
313 if (!IsInitialized(bootctrl_module)) return NULL;
314
315 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) return NULL;
316
317 return kSlotSuffixes[slot];
318 }
319
320 static hw_module_methods_t boot_control_module_methods = {
321 .open = NULL,
322 };
323
324 BootControlPrivate HAL_MODULE_INFO_SYM = {
325 .base = {
326 .common ={
327 .tag = HARDWARE_MODULE_TAG,
328 .module_api_version = 1,
329 .hal_api_version = 0,
330 .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
331 .name = "Yukawa Boot control HAL",
332 .author = "The Android Open Source Project",
333 .methods = &boot_control_module_methods
334 },
335
336 .init = BootControlInit,
337 .getNumberSlots = GetNumberSlots,
338 .getCurrentSlot = GetCurrentSlot,
339 .markBootSuccessful = MarkBootSuccessful,
340 .setActiveBootSlot = SetActiveBootSlot,
341 .setSlotAsUnbootable = SetSlotAsUnbootable,
342 .isSlotBootable = IsSlotBootable,
343 .getSuffix = GetSuffix,
344 .isSlotMarkedSuccessful = IsSlotMarkedSuccessful
345 },
346
347 .initialized = false,
348 .misc_device = nullptr,
349 .num_slots = 0,
350 .current_slot = 0
351 };
352