diff --git a/include/android_avb/avb_ab_flow.h b/include/android_avb/avb_ab_flow.h index 0ba5ccd47c..152901b7c7 100755 --- a/include/android_avb/avb_ab_flow.h +++ b/include/android_avb/avb_ab_flow.h @@ -1,277 +1,278 @@ -/* - * 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. - */ - -/* -#if !defined(AVB_INSIDE_LIBAVB_AB_H) && !defined(AVB_COMPILATION) -#error \ - "Never include this file directly, include libavb_ab/libavb_ab.h instead." -#endif -*/ - -#ifndef AVB_AB_FLOW_H_ -#define AVB_AB_FLOW_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Magic for the A/B struct when serialized. */ -#define AVB_AB_MAGIC "\0AB0" -#define AVB_AB_MAGIC_LEN 4 - -/* Versioning for the on-disk A/B metadata - keep in sync with avbtool. */ -#define AVB_AB_MAJOR_VERSION 1 -#define AVB_AB_MINOR_VERSION 0 - -/* Size of AvbABData struct. */ -#define AVB_AB_DATA_SIZE 32 - -/* Maximum values for slot data */ -#define AVB_AB_MAX_PRIORITY 15 -#define AVB_AB_MAX_TRIES_REMAINING 7 - -/* Struct used for recording per-slot metadata. - * - * When serialized, data is stored in network byte-order. - */ -typedef struct AvbABSlotData { - /* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY, - * both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY - * being the highest. The special value 0 is used to indicate the - * slot is unbootable. - */ - uint8_t priority; - - /* Number of times left attempting to boot this slot ranging from 0 - * to AVB_AB_MAX_TRIES_REMAINING. - */ - uint8_t tries_remaining; - - /* Non-zero if this slot has booted successfully, 0 otherwise. */ - uint8_t successful_boot; - - /* Reserved for future use. */ - uint8_t reserved[1]; -} AVB_ATTR_PACKED AvbABSlotData; - -/* Struct used for recording A/B metadata. - * - * When serialized, data is stored in network byte-order. - */ -typedef struct AvbABData { - /* Magic number used for identification - see AVB_AB_MAGIC. */ - uint8_t magic[AVB_AB_MAGIC_LEN]; - - /* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */ - uint8_t version_major; - uint8_t version_minor; - - /* Padding to ensure |slots| field start eight bytes in. */ - uint8_t reserved1[2]; - - /* Per-slot metadata. */ - AvbABSlotData slots[2]; - - /* Reserved for future use. */ - uint8_t reserved2[12]; - - /* CRC32 of all 28 bytes preceding this field. */ - uint32_t crc32; -} AVB_ATTR_PACKED AvbABData; - -/* Copies |src| to |dest|, byte-swapping fields in the - * process. Returns false if the data is invalid (e.g. wrong magic, - * wrong CRC32 etc.), true otherwise. - */ -bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest); - -/* Copies |src| to |dest|, byte-swapping fields in the process. Also - * updates the |crc32| field in |dest|. - */ -void avb_ab_data_update_crc_and_byteswap(const AvbABData* src, AvbABData* dest); - -/* Initializes |data| such that it has two slots and both slots have - * maximum tries remaining. The CRC is not set. - */ -void avb_ab_data_init(AvbABData* data); - -/* Reads A/B metadata from the 'misc' partition using |ops|. Returned - * data is properly byteswapped. Returns AVB_IO_RESULT_OK on - * success, error code otherwise. - * - * If the data read from disk is invalid (e.g. wrong magic or CRC - * checksum failure), the metadata will be reset using - * avb_ab_data_init() and then written to disk. - */ -AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data); - -/* Writes A/B metadata to the 'misc' partition using |ops|. This will - * byteswap and update the CRC as needed. Returns AVB_IO_RESULT_OK on - * success, error code otherwise. - */ -AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data); - -/* Return codes used in avb_ab_flow(), see that function for - * documentation of each value. - */ -typedef enum { - AVB_AB_FLOW_RESULT_OK, - AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR, - AVB_AB_FLOW_RESULT_ERROR_OOM, - AVB_AB_FLOW_RESULT_ERROR_IO, - AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, - AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT -} AvbABFlowResult; - -/* Get a textual representation of |result|. */ -const char* avb_ab_flow_result_to_string(AvbABFlowResult result); - -/* High-level function to select a slot to boot. The following - * algorithm is used: - * - * 1. A/B metadata is loaded and validated using the - * read_ab_metadata() operation. Typically this means it's read from - * the 'misc' partition and if it's invalid then it's reset using - * avb_ab_data_init() and this reset metadata is returned. - * - * 2. All bootable slots listed in the A/B metadata are verified using - * avb_slot_verify(). If a slot is invalid or if it fails verification - * (and AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is not set, see - * below), it will be marked as unbootable in the A/B metadata and the - * metadata will be saved to disk before returning. - * - * 3. If there are no bootable slots, the value - * AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS is returned. - * - * 4. For each bootable slot, the Stored Rollback Indexes are updated - * such that for each rollback index location, the Stored Rollback - * Index is the largest number smaller than or equal to the Rollback - * Index of each slot. - * - * 5. The bootable slot with the highest priority is selected and - * returned in |out_data|. If this slot is already marked as - * successful, the A/B metadata is not modified. However, if the slot - * is not marked as bootable its |tries_remaining| count is - * decremented and the A/B metadata is saved to disk before returning. - * In either case the value AVB_AB_FLOW_RESULT_OK is returning. - * - * The partitions to load is given in |requested_partitions| as a - * NULL-terminated array of NUL-terminated strings. Typically the - * |requested_partitions| array only contains a single item for the - * boot partition, 'boot'. - * - * If the device is unlocked (and _only_ if it's unlocked), the - * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag should be set - * in the |flags| parameter. This will allow considering slots as - * verified even when avb_slot_verify() returns - * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED, - * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or - * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX for the slot in - * question. - * - * Note that neither androidboot.slot_suffix nor androidboot.slot are - * set in the |cmdline| field in |AvbSlotVerifyData| - you will have - * to pass these yourself. - * - * If a slot was selected and it verified then AVB_AB_FLOW_RESULT_OK - * is returned. - * - * If a slot was selected but it didn't verify then - * AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR is returned. This can - * only happen when the AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR - * flag is set. - * - * If an I/O operation - such as loading/saving metadata or checking - * rollback indexes - fail, the value AVB_AB_FLOW_RESULT_ERROR_IO is - * returned. - * - * If memory allocation fails, AVB_AB_FLOW_RESULT_ERROR_OOM is - * returned. - * - * If invalid arguments are passed, - * AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT is returned. For example - * this can happen if using AVB_HASHTREE_ERROR_MODE_LOGGING without - * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR. - * - * Reasonable behavior for handling AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS - * is to initiate device repair (which is device-dependent). - */ -AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, - const char* const* requested_partitions, - AvbSlotVerifyFlags flags, - AvbHashtreeErrorMode hashtree_error_mode, - AvbSlotVerifyData** out_data); - -/* Marks the slot with the given slot number as active. Returns - * AVB_IO_RESULT_OK on success, error code otherwise. - * - * This function is typically used by the OS updater when completing - * an update. It can also used by the firmware for implementing the - * "set_active" command. - */ -AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops, unsigned int slot_number); - -/* Marks the slot with the given slot number as unbootable. Returns - * AVB_IO_RESULT_OK on success, error code otherwise. - * - * This function is typically used by the OS updater before writing to - * a slot. - */ -AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops, - unsigned int slot_number); - -/* Marks the slot with the given slot number as having booted - * successfully. Returns AVB_IO_RESULT_OK on success, error code - * otherwise. - * - * Calling this on an unbootable slot is an error - AVB_IO_RESULT_OK - * will be returned yet the function will have no side-effects. - * - * This function is typically used by the OS updater after having - * confirmed that the slot works as intended. - */ -AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops, - unsigned int slot_number); - -/* - * Load metadata. - */ -AvbIOResult load_metadata(AvbABOps* ab_ops, - AvbABData* ab_data, - AvbABData* ab_data_orig); - -/* Writes A/B metadata to disk only if it has changed - returns - * AVB_IO_RESULT_OK on success, error code otherwise. - */ -AvbIOResult save_metadata_if_changed(AvbABOps* ab_ops, - AvbABData* ab_data, - AvbABData* ab_data_orig); - -#ifdef __cplusplus -} -#endif - -#endif /* AVB_AB_FLOW_H_ */ +/* + * 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. + */ + +/* +#if !defined(AVB_INSIDE_LIBAVB_AB_H) && !defined(AVB_COMPILATION) +#error \ + "Never include this file directly, include libavb_ab/libavb_ab.h instead." +#endif +*/ + +#ifndef AVB_AB_FLOW_H_ +#define AVB_AB_FLOW_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Magic for the A/B struct when serialized. */ +#define AVB_AB_MAGIC "\0AB0" +#define AVB_AB_MAGIC_LEN 4 + +/* Versioning for the on-disk A/B metadata - keep in sync with avbtool. */ +#define AVB_AB_MAJOR_VERSION 1 +#define AVB_AB_MINOR_VERSION 0 + +/* Size of AvbABData struct. */ +#define AVB_AB_DATA_SIZE 32 + +/* Maximum values for slot data */ +#define AVB_AB_MAX_PRIORITY 15 +#define AVB_AB_MAX_TRIES_REMAINING 7 + +/* Struct used for recording per-slot metadata. + * + * When serialized, data is stored in network byte-order. + */ +typedef struct AvbABSlotData { + /* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY, + * both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY + * being the highest. The special value 0 is used to indicate the + * slot is unbootable. + */ + uint8_t priority; + + /* Number of times left attempting to boot this slot ranging from 0 + * to AVB_AB_MAX_TRIES_REMAINING. + */ + uint8_t tries_remaining; + + /* Non-zero if this slot has booted successfully, 0 otherwise. */ + uint8_t successful_boot; + + /* Reserved for future use. */ + uint8_t reserved[1]; +} AVB_ATTR_PACKED AvbABSlotData; + +/* Struct used for recording A/B metadata. + * + * When serialized, data is stored in network byte-order. + */ +typedef struct AvbABData { + /* Magic number used for identification - see AVB_AB_MAGIC. */ + uint8_t magic[AVB_AB_MAGIC_LEN]; + + /* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */ + uint8_t version_major; + uint8_t version_minor; + + /* Padding to ensure |slots| field start eight bytes in. */ + uint8_t reserved1[2]; + + /* Per-slot metadata. */ + AvbABSlotData slots[2]; + + /* Reserved for future use. */ + uint8_t last_boot; + uint8_t reserved2[11]; + + /* CRC32 of all 28 bytes preceding this field. */ + uint32_t crc32; +} AVB_ATTR_PACKED AvbABData; + +/* Copies |src| to |dest|, byte-swapping fields in the + * process. Returns false if the data is invalid (e.g. wrong magic, + * wrong CRC32 etc.), true otherwise. + */ +bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest); + +/* Copies |src| to |dest|, byte-swapping fields in the process. Also + * updates the |crc32| field in |dest|. + */ +void avb_ab_data_update_crc_and_byteswap(const AvbABData* src, AvbABData* dest); + +/* Initializes |data| such that it has two slots and both slots have + * maximum tries remaining. The CRC is not set. + */ +void avb_ab_data_init(AvbABData* data); + +/* Reads A/B metadata from the 'misc' partition using |ops|. Returned + * data is properly byteswapped. Returns AVB_IO_RESULT_OK on + * success, error code otherwise. + * + * If the data read from disk is invalid (e.g. wrong magic or CRC + * checksum failure), the metadata will be reset using + * avb_ab_data_init() and then written to disk. + */ +AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data); + +/* Writes A/B metadata to the 'misc' partition using |ops|. This will + * byteswap and update the CRC as needed. Returns AVB_IO_RESULT_OK on + * success, error code otherwise. + */ +AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data); + +/* Return codes used in avb_ab_flow(), see that function for + * documentation of each value. + */ +typedef enum { + AVB_AB_FLOW_RESULT_OK, + AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR, + AVB_AB_FLOW_RESULT_ERROR_OOM, + AVB_AB_FLOW_RESULT_ERROR_IO, + AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, + AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT +} AvbABFlowResult; + +/* Get a textual representation of |result|. */ +const char* avb_ab_flow_result_to_string(AvbABFlowResult result); + +/* High-level function to select a slot to boot. The following + * algorithm is used: + * + * 1. A/B metadata is loaded and validated using the + * read_ab_metadata() operation. Typically this means it's read from + * the 'misc' partition and if it's invalid then it's reset using + * avb_ab_data_init() and this reset metadata is returned. + * + * 2. All bootable slots listed in the A/B metadata are verified using + * avb_slot_verify(). If a slot is invalid or if it fails verification + * (and AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is not set, see + * below), it will be marked as unbootable in the A/B metadata and the + * metadata will be saved to disk before returning. + * + * 3. If there are no bootable slots, the value + * AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS is returned. + * + * 4. For each bootable slot, the Stored Rollback Indexes are updated + * such that for each rollback index location, the Stored Rollback + * Index is the largest number smaller than or equal to the Rollback + * Index of each slot. + * + * 5. The bootable slot with the highest priority is selected and + * returned in |out_data|. If this slot is already marked as + * successful, the A/B metadata is not modified. However, if the slot + * is not marked as bootable its |tries_remaining| count is + * decremented and the A/B metadata is saved to disk before returning. + * In either case the value AVB_AB_FLOW_RESULT_OK is returning. + * + * The partitions to load is given in |requested_partitions| as a + * NULL-terminated array of NUL-terminated strings. Typically the + * |requested_partitions| array only contains a single item for the + * boot partition, 'boot'. + * + * If the device is unlocked (and _only_ if it's unlocked), the + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag should be set + * in the |flags| parameter. This will allow considering slots as + * verified even when avb_slot_verify() returns + * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED, + * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or + * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX for the slot in + * question. + * + * Note that neither androidboot.slot_suffix nor androidboot.slot are + * set in the |cmdline| field in |AvbSlotVerifyData| - you will have + * to pass these yourself. + * + * If a slot was selected and it verified then AVB_AB_FLOW_RESULT_OK + * is returned. + * + * If a slot was selected but it didn't verify then + * AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR is returned. This can + * only happen when the AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR + * flag is set. + * + * If an I/O operation - such as loading/saving metadata or checking + * rollback indexes - fail, the value AVB_AB_FLOW_RESULT_ERROR_IO is + * returned. + * + * If memory allocation fails, AVB_AB_FLOW_RESULT_ERROR_OOM is + * returned. + * + * If invalid arguments are passed, + * AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT is returned. For example + * this can happen if using AVB_HASHTREE_ERROR_MODE_LOGGING without + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR. + * + * Reasonable behavior for handling AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS + * is to initiate device repair (which is device-dependent). + */ +AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops, + const char* const* requested_partitions, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data); + +/* Marks the slot with the given slot number as active. Returns + * AVB_IO_RESULT_OK on success, error code otherwise. + * + * This function is typically used by the OS updater when completing + * an update. It can also used by the firmware for implementing the + * "set_active" command. + */ +AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops, unsigned int slot_number); + +/* Marks the slot with the given slot number as unbootable. Returns + * AVB_IO_RESULT_OK on success, error code otherwise. + * + * This function is typically used by the OS updater before writing to + * a slot. + */ +AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops, + unsigned int slot_number); + +/* Marks the slot with the given slot number as having booted + * successfully. Returns AVB_IO_RESULT_OK on success, error code + * otherwise. + * + * Calling this on an unbootable slot is an error - AVB_IO_RESULT_OK + * will be returned yet the function will have no side-effects. + * + * This function is typically used by the OS updater after having + * confirmed that the slot works as intended. + */ +AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops, + unsigned int slot_number); + +/* + * Load metadata. + */ +AvbIOResult load_metadata(AvbABOps* ab_ops, + AvbABData* ab_data, + AvbABData* ab_data_orig); + +/* Writes A/B metadata to disk only if it has changed - returns + * AVB_IO_RESULT_OK on success, error code otherwise. + */ +AvbIOResult save_metadata_if_changed(AvbABOps* ab_ops, + AvbABData* ab_data, + AvbABData* ab_data_orig); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_AB_FLOW_H_ */ diff --git a/include/android_avb/rk_avb_ops_user.h b/include/android_avb/rk_avb_ops_user.h index 0a70e7096b..c9f3cd7927 100644 --- a/include/android_avb/rk_avb_ops_user.h +++ b/include/android_avb/rk_avb_ops_user.h @@ -267,6 +267,14 @@ int rk_auth_unlock(void *buffer, char *out_is_trusted); */ int rk_generate_unlock_challenge(void *buffer, uint32_t *challenge_len); + +/** + * Get last boot slot + * + * @return 0 is slot A; 1 is slot B; -1 is error + */ +int rk_get_lastboot(void); + #ifdef __cplusplus } #endif diff --git a/lib/avb/libavb_ab/avb_ab_flow.c b/lib/avb/libavb_ab/avb_ab_flow.c index 7ed1ab9107..c84131d6c3 100755 --- a/lib/avb/libavb_ab/avb_ab_flow.c +++ b/lib/avb/libavb_ab/avb_ab_flow.c @@ -1,531 +1,532 @@ -/* - * 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 - -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. - */ -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. - */ -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; -} +/* + * 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 + +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->last_boot = 0; + 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. + */ +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. + */ +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; +} diff --git a/lib/avb/rk_avb_user/rk_avb_ops_user.c b/lib/avb/rk_avb_user/rk_avb_ops_user.c index b58a81fe95..3edd428f98 100644 --- a/lib/avb/rk_avb_user/rk_avb_ops_user.c +++ b/lib/avb/rk_avb_user/rk_avb_ops_user.c @@ -117,8 +117,14 @@ int rk_avb_get_current_slot(char *select_slot) } if (rk_avb_ab_slot_select(ops->ab_ops, select_slot) != 0) { - printf("get_current_slot error!\n"); - ret = -1; + printf("###There is no bootable slot, bring up last_boot!###\n"); + if (rk_get_lastboot() == 1) + memcpy(select_slot, "_b", 2); + else if(rk_get_lastboot() == 0) + memcpy(select_slot, "_a", 2); + else + return -1; + ret = 0; } avb_ops_user_free(ops); @@ -697,3 +703,30 @@ int rk_generate_unlock_challenge(void *buffer, uint32_t *challenge_len) else return -1; } + +int rk_get_lastboot(void) +{ + + AvbIOResult io_ret = AVB_IO_RESULT_OK; + AvbABData ab_data; + int lastboot = -1; + AvbOps* ops; + + ops = avb_ops_user_new(); + if (ops == NULL) { + printf("avb_ops_user_new() failed!\n"); + return -1; + } + + io_ret = ops->ab_ops->read_ab_metadata(ops->ab_ops, &ab_data); + if (io_ret != AVB_IO_RESULT_OK) { + avb_error("I/O error while loading A/B metadata.\n"); + goto out; + } + + lastboot = ab_data.last_boot; +out: + avb_ops_user_free(ops); + + return lastboot; +}