blob: de90a81672912cba94ff9d965c016b8162a84067 [file] [log] [blame]
David Zeuthen8b6973b2016-09-20 12:39:49 -04001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include "avb_ab_flow.h"
26#include "avb_util.h"
27
28bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest) {
29 /* Ensure magic is correct. */
30 if (avb_safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
31 avb_error("Magic is incorrect.\n");
32 return false;
33 }
34
35 avb_memcpy(dest, src, sizeof(AvbABData));
36 dest->crc32 = avb_be32toh(dest->crc32);
37
38 /* Ensure we don't attempt to access any fields if the major version
39 * is not supported.
40 */
41 if (dest->version_major > AVB_AB_MAJOR_VERSION) {
42 avb_error("No support for given major version.\n");
43 return false;
44 }
45
46 /* Bail if CRC32 doesn't match. */
47 if (dest->crc32 !=
48 avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))) {
49 avb_error("CRC32 does not match.\n");
50 return false;
51 }
52
53 return true;
54}
55
56void avb_ab_data_update_crc_and_byteswap(const AvbABData* src,
57 AvbABData* dest) {
58 avb_memcpy(dest, src, sizeof(AvbABData));
59 dest->crc32 = avb_htobe32(
60 avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t)));
61}
62
63void avb_ab_data_init(AvbABData* data) {
64 avb_memset(data, '\0', sizeof(AvbABData));
65 avb_memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
66 data->version_major = AVB_AB_MAJOR_VERSION;
67 data->version_minor = AVB_AB_MINOR_VERSION;
68 data->slots[0].priority = AVB_AB_MAX_PRIORITY;
69 data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
70 data->slots[0].successful_boot = 0;
71 data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
72 data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
73 data->slots[1].successful_boot = 0;
74}
75
76/* The AvbABData struct is stored 2048 bytes into the 'misc' partition
77 * following the 'struct bootloader_message' field. The struct is
78 * compatible with the guidelines in bootable/recovery/bootloader.h -
79 * e.g. it is stored in the |slot_suffix| field, starts with a
80 * NUL-byte, and is 32 bytes long.
81 */
82#define AB_METADATA_MISC_PARTITION_OFFSET 2048
83
84bool avb_ab_data_read(AvbOps* ops, AvbABData* data) {
85 AvbABData serialized;
86 AvbIOResult io_result;
87 size_t num_bytes_read;
88
89 io_result =
90 ops->read_from_partition(ops, "misc", AB_METADATA_MISC_PARTITION_OFFSET,
91 sizeof(AvbABData), &serialized, &num_bytes_read);
92 if (io_result != AVB_IO_RESULT_OK || num_bytes_read != sizeof(AvbABData)) {
93 avb_error("Error reading A/B metadata.\n");
94 return false;
95 }
96
97 if (!avb_ab_data_verify_and_byteswap(&serialized, data)) {
98 avb_error(
99 "Error validating A/B metadata from disk. "
100 "Resetting and writing new A/B metadata to disk.\n");
101 avb_ab_data_init(data);
102 return avb_ab_data_write(ops, data);
103 }
104
105 return true;
106}
107
108bool avb_ab_data_write(AvbOps* ops, const AvbABData* data) {
109 AvbABData serialized;
110 AvbIOResult io_result;
111
112 avb_ab_data_update_crc_and_byteswap(data, &serialized);
113 io_result =
114 ops->write_to_partition(ops, "misc", AB_METADATA_MISC_PARTITION_OFFSET,
115 sizeof(AvbABData), &serialized);
116 if (io_result != AVB_IO_RESULT_OK) {
117 avb_error("Error writing A/B metadata.\n");
118 return false;
119 }
120 return true;
121}
122
123static bool slot_is_bootable(AvbABSlotData* slot) {
124 return slot->priority > 0 &&
125 (slot->successful_boot || (slot->tries_remaining > 0));
126}
127
128static void slot_set_unbootable(AvbABSlotData* slot) {
129 slot->priority = 0;
130 slot->tries_remaining = 0;
131 slot->successful_boot = 0;
132}
133
134/* Ensure all unbootable and/or illegal states are marked as the
135 * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
136 * and successful_boot=0.
137 */
138static void slot_normalize(AvbABSlotData* slot) {
139 if (slot->priority > 0) {
140 if (slot->tries_remaining == 0 && !slot->successful_boot) {
141 /* We've exhausted all tries -> unbootable. */
142 slot_set_unbootable(slot);
143 }
144 if (slot->tries_remaining > 0 && slot->successful_boot) {
145 /* Illegal state - avb_ab_mark_slot_successful() will clear
146 * tries_remaining when setting successful_boot.
147 */
148 slot_set_unbootable(slot);
149 }
150 } else {
151 slot_set_unbootable(slot);
152 }
153}
154
155static const char* slot_suffixes[2] = {"_a", "_b"};
156
157/* Helper function to metadata - returns false if an I/O error occurs. */
158static bool load_metadata(AvbOps* ops, AvbABData* ab_data,
159 AvbABData* ab_data_orig) {
160 if (!avb_ab_data_read(ops, ab_data)) {
161 avb_error("I/O error while loading A/B metadata.\n");
162 return false;
163 }
164 *ab_data_orig = *ab_data;
165
166 /* Ensure data is normalized, e.g. illegal states will be marked as
167 * unbootable and all unbootable states are represented with
168 * (priority=0, tries_remaining=0, successful_boot=0).
169 */
170 slot_normalize(&ab_data->slots[0]);
171 slot_normalize(&ab_data->slots[1]);
172 return true;
173}
174
175/* Writes A/B metadata to disk only if it has changed - returns false
176 * if an I/O error occurs.
177 */
178static bool save_metadata_if_changed(AvbOps* ops, AvbABData* ab_data,
179 AvbABData* ab_data_orig) {
180 if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) {
181 avb_debug("Writing A/B metadata to disk.\n");
182 if (!avb_ab_data_write(ops, ab_data)) {
183 avb_error("Error writing A/B metadata to disk.\n");
184 return false;
185 }
186 }
187 return true;
188}
189
190AvbABFlowResult avb_ab_flow(AvbOps* ops, AvbSlotVerifyData** out_data) {
191 AvbSlotVerifyData* slot_data[2] = {NULL, NULL};
192 AvbSlotVerifyData* data = NULL;
193 AvbABFlowResult ret;
194 AvbABData ab_data, ab_data_orig;
195 size_t slot_index_to_boot, n;
196
197 if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
198 ret = AVB_AB_FLOW_RESULT_ERROR_IO;
199 goto out;
200 }
201
202 /* Validate all bootable slots. */
203 for (n = 0; n < 2; n++) {
204 if (slot_is_bootable(&ab_data.slots[n])) {
205 AvbSlotVerifyResult verify_result;
206 verify_result = avb_slot_verify(ops, slot_suffixes[n], &slot_data[n]);
207 if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
208 if (verify_result == AVB_AB_FLOW_RESULT_ERROR_OOM) {
209 ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
210 goto out;
211 }
212 if (verify_result == AVB_AB_FLOW_RESULT_ERROR_IO) {
213 ret = AVB_AB_FLOW_RESULT_ERROR_IO;
214 goto out;
215 }
216 avb_errorv("Error verifying slot ", slot_suffixes[n], " with result ",
217 avb_slot_verify_result_to_string(verify_result),
218 " - setting unbootable.\n", NULL);
219 slot_set_unbootable(&ab_data.slots[n]);
220 }
221 }
222 }
223
224 if (slot_is_bootable(&ab_data.slots[0]) &&
225 slot_is_bootable(&ab_data.slots[1])) {
226 if (ab_data.slots[1].priority > ab_data.slots[0].priority) {
227 slot_index_to_boot = 1;
228 } else {
229 slot_index_to_boot = 0;
230 }
231 } else if (slot_is_bootable(&ab_data.slots[0])) {
232 slot_index_to_boot = 0;
233 } else if (slot_is_bootable(&ab_data.slots[1])) {
234 slot_index_to_boot = 1;
235 } else {
236 /* No bootable slots! */
237 avb_error("No bootable slots found.\n");
238 ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
239 goto out;
240 }
241
242 /* Update stored rollback index such that the stored rollback index
243 * is the largest value supporting all currently bootable slots. Do
244 * this for every rollback index slot.
245 */
246 for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_SLOTS; n++) {
247 uint64_t rollback_index_value = 0;
248
249 if (slot_data[0] != NULL && slot_data[1] != NULL) {
250 uint64_t a_rollback_index = slot_data[0]->rollback_indexes[n];
251 uint64_t b_rollback_index = slot_data[1]->rollback_indexes[n];
252 rollback_index_value =
253 (a_rollback_index < b_rollback_index ? a_rollback_index
254 : b_rollback_index);
255 } else if (slot_data[0] != NULL) {
256 rollback_index_value = slot_data[0]->rollback_indexes[n];
257 } else if (slot_data[1] != NULL) {
258 rollback_index_value = slot_data[1]->rollback_indexes[n];
259 }
260
261 if (rollback_index_value != 0) {
262 uint64_t current_rollback_index_value;
263 if (!ops->read_rollback_index(ops, n, &current_rollback_index_value)) {
264 avb_error("Error getting rollback index for slot.\n");
265 ret = AVB_AB_FLOW_RESULT_ERROR_IO;
266 goto out;
267 }
268 if (current_rollback_index_value != rollback_index_value) {
269 if (!ops->write_rollback_index(ops, n, rollback_index_value)) {
270 avb_error("Error setting stored rollback index.\n");
271 ret = AVB_AB_FLOW_RESULT_ERROR_IO;
272 goto out;
273 }
274 }
275 }
276 }
277
278 /* Finally, select this slot. */
279 avb_assert(slot_data[slot_index_to_boot] != NULL);
280 data = slot_data[slot_index_to_boot];
281 slot_data[slot_index_to_boot] = NULL;
282 ret = AVB_AB_FLOW_RESULT_OK;
283
284 /* ... and decrement tries remaining, if applicable. */
285 if (!ab_data.slots[slot_index_to_boot].successful_boot &&
286 ab_data.slots[slot_index_to_boot].tries_remaining > 0) {
287 ab_data.slots[slot_index_to_boot].tries_remaining -= 1;
288 }
289
290out:
291 if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
292 ret = AVB_AB_FLOW_RESULT_ERROR_IO;
293 if (data != NULL) {
294 avb_slot_verify_data_free(data);
295 data = NULL;
296 }
297 }
298
299 for (n = 0; n < 2; n++) {
300 if (slot_data[n] != NULL) {
301 avb_slot_verify_data_free(slot_data[n]);
302 }
303 }
304
305 if (out_data != NULL) {
306 *out_data = data;
307 } else {
308 if (data != NULL) {
309 avb_slot_verify_data_free(data);
310 }
311 }
312
313 return ret;
314}
315
316bool avb_ab_mark_slot_active(AvbOps* ops, unsigned int slot_number) {
317 AvbABData ab_data, ab_data_orig;
318 bool ret = false;
319 unsigned int other_slot_number;
320
321 avb_assert(slot_number < 2);
322
323 if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
324 goto out;
325 }
326
327 /* Make requested slot top priority, unsuccessful, and with max tries. */
328 ab_data.slots[slot_number].priority = AVB_AB_MAX_PRIORITY;
329 ab_data.slots[slot_number].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
330 ab_data.slots[slot_number].successful_boot = 0;
331
332 /* Ensure other slot doesn't have as high a priority. */
333 other_slot_number = 1 - slot_number;
334 if (ab_data.slots[other_slot_number].priority == AVB_AB_MAX_PRIORITY) {
335 ab_data.slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1;
336 }
337
338 ret = true;
339
340out:
341 if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
342 ret = false;
343 }
344 return ret;
345}
346
347bool avb_ab_mark_slot_unbootable(AvbOps* ops, unsigned int slot_number) {
348 AvbABData ab_data, ab_data_orig;
349 bool ret = false;
350
351 avb_assert(slot_number < 2);
352
353 if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
354 goto out;
355 }
356
357 slot_set_unbootable(&ab_data.slots[slot_number]);
358 ret = true;
359
360out:
361 if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
362 ret = false;
363 }
364 return ret;
365}
366
367bool avb_ab_mark_slot_successful(AvbOps* ops, unsigned int slot_number) {
368 AvbABData ab_data, ab_data_orig;
369 bool ret = false;
370
371 avb_assert(slot_number < 2);
372
373 if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
374 goto out;
375 }
376
377 if (!slot_is_bootable(&ab_data.slots[slot_number])) {
378 avb_error("Cannot mark unbootable slot as successful.\n");
379 goto out;
380 }
381
382 ab_data.slots[slot_number].tries_remaining = 0;
383 ab_data.slots[slot_number].successful_boot = 1;
384
385 ret = true;
386
387out:
388 if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
389 ret = false;
390 }
391 return ret;
392}