| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- /*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- #include "avb_ab_flow.h"
- bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest) {
- /* Ensure magic is correct. */
- if (avb_safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
- avb_error("Magic is incorrect.\n");
- return false;
- }
- avb_memcpy(dest, src, sizeof(AvbABData));
- dest->crc32 = avb_be32toh(dest->crc32);
- /* Ensure we don't attempt to access any fields if the major version
- * is not supported.
- */
- if (dest->version_major > AVB_AB_MAJOR_VERSION) {
- avb_error("No support for given major version.\n");
- return false;
- }
- /* Bail if CRC32 doesn't match. */
- if (dest->crc32 !=
- avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))) {
- avb_error("CRC32 does not match.\n");
- return false;
- }
- return true;
- }
- void avb_ab_data_update_crc_and_byteswap(const AvbABData* src,
- AvbABData* dest) {
- avb_memcpy(dest, src, sizeof(AvbABData));
- dest->crc32 = avb_htobe32(
- avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t)));
- }
- void avb_ab_data_init(AvbABData* data) {
- avb_memset(data, '\0', sizeof(AvbABData));
- avb_memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
- data->version_major = AVB_AB_MAJOR_VERSION;
- data->version_minor = AVB_AB_MINOR_VERSION;
- data->slots[0].priority = AVB_AB_MAX_PRIORITY;
- data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
- data->slots[0].successful_boot = 0;
- data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
- data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
- data->slots[1].successful_boot = 0;
- }
- /* The AvbABData struct is stored 2048 bytes into the 'misc' partition
- * following the 'struct bootloader_message' field. The struct is
- * compatible with the guidelines in bootable/recovery/bootloader.h -
- * e.g. it is stored in the |slot_suffix| field, starts with a
- * NUL-byte, and is 32 bytes long.
- */
- #define AB_METADATA_MISC_PARTITION_OFFSET 2048
- AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data) {
- AvbOps* ops = ab_ops->ops;
- AvbABData serialized;
- AvbIOResult io_ret;
- size_t num_bytes_read;
- io_ret = ops->read_from_partition(ops,
- "misc",
- AB_METADATA_MISC_PARTITION_OFFSET,
- sizeof(AvbABData),
- &serialized,
- &num_bytes_read);
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- return AVB_IO_RESULT_ERROR_OOM;
- } else if (io_ret != AVB_IO_RESULT_OK ||
- num_bytes_read != sizeof(AvbABData)) {
- avb_error("Error reading A/B metadata.\n");
- return AVB_IO_RESULT_ERROR_IO;
- }
- if (!avb_ab_data_verify_and_byteswap(&serialized, data)) {
- avb_error(
- "Error validating A/B metadata from disk. "
- "Resetting and writing new A/B metadata to disk.\n");
- avb_ab_data_init(data);
- return avb_ab_data_write(ab_ops, data);
- }
- return AVB_IO_RESULT_OK;
- }
- AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data) {
- AvbOps* ops = ab_ops->ops;
- AvbABData serialized;
- AvbIOResult io_ret;
- avb_ab_data_update_crc_and_byteswap(data, &serialized);
- io_ret = ops->write_to_partition(ops,
- "misc",
- AB_METADATA_MISC_PARTITION_OFFSET,
- sizeof(AvbABData),
- &serialized);
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- return AVB_IO_RESULT_ERROR_OOM;
- } else if (io_ret != AVB_IO_RESULT_OK) {
- avb_error("Error writing A/B metadata.\n");
- return AVB_IO_RESULT_ERROR_IO;
- }
- return AVB_IO_RESULT_OK;
- }
- static bool slot_is_bootable(AvbABSlotData* slot) {
- return slot->priority > 0 &&
- (slot->successful_boot || (slot->tries_remaining > 0));
- }
- static void slot_set_unbootable(AvbABSlotData* slot) {
- slot->priority = 0;
- slot->tries_remaining = 0;
- slot->successful_boot = 0;
- }
- /* Ensure all unbootable and/or illegal states are marked as the
- * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
- * and successful_boot=0.
- */
- static void slot_normalize(AvbABSlotData* slot) {
- if (slot->priority > 0) {
- if (slot->tries_remaining == 0 && !slot->successful_boot) {
- /* We've exhausted all tries -> unbootable. */
- slot_set_unbootable(slot);
- }
- if (slot->tries_remaining > 0 && slot->successful_boot) {
- /* Illegal state - avb_ab_mark_slot_successful() will clear
- * tries_remaining when setting successful_boot.
- */
- slot_set_unbootable(slot);
- }
- } else {
- slot_set_unbootable(slot);
- }
- }
- static const char* slot_suffixes[2] = {"_a", "_b"};
- /* Helper function to load metadata - returns AVB_IO_RESULT_OK on
- * success, error code otherwise.
- */
- static AvbIOResult load_metadata(AvbABOps* ab_ops,
- AvbABData* ab_data,
- AvbABData* ab_data_orig) {
- AvbIOResult io_ret;
- io_ret = ab_ops->read_ab_metadata(ab_ops, ab_data);
- if (io_ret != AVB_IO_RESULT_OK) {
- avb_error("I/O error while loading A/B metadata.\n");
- return io_ret;
- }
- *ab_data_orig = *ab_data;
- /* Ensure data is normalized, e.g. illegal states will be marked as
- * unbootable and all unbootable states are represented with
- * (priority=0, tries_remaining=0, successful_boot=0).
- */
- slot_normalize(&ab_data->slots[0]);
- slot_normalize(&ab_data->slots[1]);
- return AVB_IO_RESULT_OK;
- }
- /* Writes A/B metadata to disk only if it has changed - returns
- * AVB_IO_RESULT_OK on success, error code otherwise.
- */
- static AvbIOResult save_metadata_if_changed(AvbABOps* ab_ops,
- AvbABData* ab_data,
- AvbABData* ab_data_orig) {
- if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) {
- avb_debug("Writing A/B metadata to disk.\n");
- return ab_ops->write_ab_metadata(ab_ops, ab_data);
- }
- return AVB_IO_RESULT_OK;
- }
- AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops,
- const char* const* requested_partitions,
- AvbSlotVerifyFlags flags,
- AvbHashtreeErrorMode hashtree_error_mode,
- AvbSlotVerifyData** out_data) {
- AvbOps* ops = ab_ops->ops;
- AvbSlotVerifyData* slot_data[2] = {NULL, NULL};
- AvbSlotVerifyData* data = NULL;
- AvbABFlowResult ret;
- AvbABData ab_data, ab_data_orig;
- size_t slot_index_to_boot, n;
- AvbIOResult io_ret;
- bool saw_and_allowed_verification_error = false;
- io_ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
- goto out;
- } else if (io_ret != AVB_IO_RESULT_OK) {
- ret = AVB_AB_FLOW_RESULT_ERROR_IO;
- goto out;
- }
- /* Validate all bootable slots. */
- for (n = 0; n < 2; n++) {
- if (slot_is_bootable(&ab_data.slots[n])) {
- AvbSlotVerifyResult verify_result;
- bool set_slot_unbootable = false;
- verify_result = avb_slot_verify(ops,
- requested_partitions,
- slot_suffixes[n],
- flags,
- hashtree_error_mode,
- &slot_data[n]);
- switch (verify_result) {
- case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
- ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
- goto out;
- case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
- ret = AVB_AB_FLOW_RESULT_ERROR_IO;
- goto out;
- case AVB_SLOT_VERIFY_RESULT_OK:
- break;
- case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
- case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
- /* Even with AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
- * these mean game over.
- */
- set_slot_unbootable = true;
- break;
- /* explicit fallthrough. */
- case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
- case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
- case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
- if (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR) {
- /* Do nothing since we allow this. */
- avb_debugv("Allowing slot ",
- slot_suffixes[n],
- " which verified "
- "with result ",
- avb_slot_verify_result_to_string(verify_result),
- " because "
- "AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR "
- "is set.\n",
- NULL);
- saw_and_allowed_verification_error = true;
- } else {
- set_slot_unbootable = true;
- }
- break;
- case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
- ret = AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT;
- goto out;
- /* Do not add a 'default:' case here because of -Wswitch. */
- }
- if (set_slot_unbootable) {
- avb_errorv("Error verifying slot ",
- slot_suffixes[n],
- " with result ",
- avb_slot_verify_result_to_string(verify_result),
- " - setting unbootable.\n",
- NULL);
- slot_set_unbootable(&ab_data.slots[n]);
- }
- }
- }
- if (slot_is_bootable(&ab_data.slots[0]) &&
- slot_is_bootable(&ab_data.slots[1])) {
- if (ab_data.slots[1].priority > ab_data.slots[0].priority) {
- slot_index_to_boot = 1;
- } else {
- slot_index_to_boot = 0;
- }
- } else if (slot_is_bootable(&ab_data.slots[0])) {
- slot_index_to_boot = 0;
- } else if (slot_is_bootable(&ab_data.slots[1])) {
- slot_index_to_boot = 1;
- } else {
- /* No bootable slots! */
- avb_error("No bootable slots found.\n");
- ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
- goto out;
- }
- /* Update stored rollback index such that the stored rollback index
- * is the largest value supporting all currently bootable slots. Do
- * this for every rollback index location.
- */
- for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
- uint64_t rollback_index_value = 0;
- if (slot_data[0] != NULL && slot_data[1] != NULL) {
- uint64_t a_rollback_index = slot_data[0]->rollback_indexes[n];
- uint64_t b_rollback_index = slot_data[1]->rollback_indexes[n];
- rollback_index_value =
- (a_rollback_index < b_rollback_index ? a_rollback_index
- : b_rollback_index);
- } else if (slot_data[0] != NULL) {
- rollback_index_value = slot_data[0]->rollback_indexes[n];
- } else if (slot_data[1] != NULL) {
- rollback_index_value = slot_data[1]->rollback_indexes[n];
- }
- if (rollback_index_value != 0) {
- uint64_t current_rollback_index_value;
- io_ret = ops->read_rollback_index(ops, n, ¤t_rollback_index_value);
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
- goto out;
- } else if (io_ret != AVB_IO_RESULT_OK) {
- avb_error("Error getting rollback index for slot.\n");
- ret = AVB_AB_FLOW_RESULT_ERROR_IO;
- goto out;
- }
- if (current_rollback_index_value != rollback_index_value) {
- io_ret = ops->write_rollback_index(ops, n, rollback_index_value);
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
- goto out;
- } else if (io_ret != AVB_IO_RESULT_OK) {
- avb_error("Error setting stored rollback index.\n");
- ret = AVB_AB_FLOW_RESULT_ERROR_IO;
- goto out;
- }
- }
- }
- }
- /* Finally, select this slot. */
- avb_assert(slot_data[slot_index_to_boot] != NULL);
- data = slot_data[slot_index_to_boot];
- slot_data[slot_index_to_boot] = NULL;
- if (saw_and_allowed_verification_error) {
- avb_assert(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
- ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
- } else {
- ret = AVB_AB_FLOW_RESULT_OK;
- }
- /* ... and decrement tries remaining, if applicable. */
- if (!ab_data.slots[slot_index_to_boot].successful_boot &&
- ab_data.slots[slot_index_to_boot].tries_remaining > 0) {
- ab_data.slots[slot_index_to_boot].tries_remaining -= 1;
- }
- out:
- io_ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
- if (io_ret != AVB_IO_RESULT_OK) {
- if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
- } else {
- ret = AVB_AB_FLOW_RESULT_ERROR_IO;
- }
- if (data != NULL) {
- avb_slot_verify_data_free(data);
- data = NULL;
- }
- }
- for (n = 0; n < 2; n++) {
- if (slot_data[n] != NULL) {
- avb_slot_verify_data_free(slot_data[n]);
- }
- }
- if (out_data != NULL) {
- *out_data = data;
- } else {
- if (data != NULL) {
- avb_slot_verify_data_free(data);
- }
- }
- return ret;
- }
- AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops,
- unsigned int slot_number) {
- AvbABData ab_data, ab_data_orig;
- unsigned int other_slot_number;
- AvbIOResult ret;
- avb_assert(slot_number < 2);
- ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
- if (ret != AVB_IO_RESULT_OK) {
- goto out;
- }
- /* Make requested slot top priority, unsuccessful, and with max tries. */
- ab_data.slots[slot_number].priority = AVB_AB_MAX_PRIORITY;
- ab_data.slots[slot_number].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
- ab_data.slots[slot_number].successful_boot = 0;
- /* Ensure other slot doesn't have as high a priority. */
- other_slot_number = 1 - slot_number;
- if (ab_data.slots[other_slot_number].priority == AVB_AB_MAX_PRIORITY) {
- ab_data.slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1;
- }
- ret = AVB_IO_RESULT_OK;
- out:
- if (ret == AVB_IO_RESULT_OK) {
- ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
- }
- return ret;
- }
- AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops,
- unsigned int slot_number) {
- AvbABData ab_data, ab_data_orig;
- AvbIOResult ret;
- avb_assert(slot_number < 2);
- ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
- if (ret != AVB_IO_RESULT_OK) {
- goto out;
- }
- slot_set_unbootable(&ab_data.slots[slot_number]);
- ret = AVB_IO_RESULT_OK;
- out:
- if (ret == AVB_IO_RESULT_OK) {
- ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
- }
- return ret;
- }
- AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops,
- unsigned int slot_number) {
- AvbABData ab_data, ab_data_orig;
- AvbIOResult ret;
- avb_assert(slot_number < 2);
- ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
- if (ret != AVB_IO_RESULT_OK) {
- goto out;
- }
- if (!slot_is_bootable(&ab_data.slots[slot_number])) {
- avb_error("Cannot mark unbootable slot as successful.\n");
- ret = AVB_IO_RESULT_OK;
- goto out;
- }
- ab_data.slots[slot_number].tries_remaining = 0;
- ab_data.slots[slot_number].successful_boot = 1;
- ret = AVB_IO_RESULT_OK;
- out:
- if (ret == AVB_IO_RESULT_OK) {
- ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
- }
- return ret;
- }
- const char* avb_ab_flow_result_to_string(AvbABFlowResult result) {
- const char* ret = NULL;
- switch (result) {
- case AVB_AB_FLOW_RESULT_OK:
- ret = "OK";
- break;
- case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR:
- ret = "OK_WITH_VERIFICATION_ERROR";
- break;
- case AVB_AB_FLOW_RESULT_ERROR_OOM:
- ret = "ERROR_OOM";
- break;
- case AVB_AB_FLOW_RESULT_ERROR_IO:
- ret = "ERROR_IO";
- break;
- case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS:
- ret = "ERROR_NO_BOOTABLE_SLOTS";
- break;
- case AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT:
- ret = "ERROR_INVALID_ARGUMENT";
- break;
- /* Do not add a 'default:' case here because of -Wswitch. */
- }
- if (ret == NULL) {
- avb_error("Unknown AvbABFlowResult value.\n");
- ret = "(unknown)";
- }
- return ret;
- }
|