| /* |
| * Copyright (c) 2019 MediaTek Inc. |
| * |
| * 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 <lib/bio.h> |
| #include <lib/dl_commands.h> |
| /* VERIFIED BOOT 2 */ |
| #include <libavb/libavb.h> |
| #include <libavb_ab/libavb_ab.h> |
| |
| #include <platform/mmc_rpmb.h> |
| #include <sys/types.h> |
| #include <string.h> |
| |
| static AvbIOResult mt_read_from_partition(AvbOps* ops, |
| const char* partition, |
| int64_t offset, |
| size_t num_bytes, |
| void* buffer, |
| size_t* out_num_read) |
| { |
| bdev_t *bdev; |
| off_t part_size; |
| size_t read_bytes; |
| |
| bdev = bio_open_by_label(partition) ? : bio_open(partition); |
| if (!bdev) { |
| dprintf(CRITICAL, "Partition [%s] is not exist.\n", partition); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| part_size = bdev->total_size; |
| |
| if (offset < 0) { |
| if (-offset > part_size) |
| { |
| bio_close(bdev); |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| } |
| |
| offset += part_size; |
| } |
| |
| if (offset+num_bytes > (uint64_t)part_size) |
| { |
| bio_close(bdev); |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| } |
| |
| read_bytes = bio_read(bdev, buffer, offset, num_bytes); |
| |
| if (out_num_read != NULL) |
| *out_num_read = read_bytes; |
| |
| bio_close(bdev); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult mt_write_to_partition(AvbOps* ops, |
| const char* partition, |
| int64_t offset, |
| size_t num_bytes, |
| const void* buffer) |
| { |
| bdev_t *bdev; |
| off_t part_size; |
| size_t write_bytes; |
| |
| bdev = bio_open_by_label(partition) ? : bio_open(partition); |
| if (!bdev) { |
| dprintf(CRITICAL, "Partition [%s] is not exist.\n", partition); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| part_size = bdev->total_size; |
| |
| if (offset < 0) { |
| if (-offset > part_size) |
| { |
| bio_close(bdev); |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| } |
| |
| offset += part_size; |
| } |
| |
| if (offset+num_bytes > (uint64_t)part_size) |
| { |
| bio_close(bdev); |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| } |
| |
| write_bytes = bio_write(bdev, buffer,offset, num_bytes); |
| |
| if (write_bytes != num_bytes) |
| { |
| bio_close(bdev); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| |
| bio_close(bdev); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| #ifdef AVB_ENABLE_ANTIROLLBACK |
| typedef struct { |
| uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS]; |
| } rollback_indexes_t; |
| #define VERIDX_IN_PRMB 0 |
| /*antirollback index is stored in rpmb region block 1*/ |
| static AvbIOResult mt_read_rollback_index(AvbOps* ops, |
| size_t rollback_index_location, |
| uint64_t* out_rollback_index) |
| { |
| int ret=0; |
| unsigned char blk[256] = {0}; |
| ret = mmc_rpmb_block_read(VERIDX_IN_PRMB,blk); |
| if(ret != 0 ) |
| { |
| *out_rollback_index = 0; |
| return AVB_IO_RESULT_OK; |
| } |
| rollback_indexes_t* indexes = (rollback_indexes_t*)blk; |
| *out_rollback_index=indexes->rollback_indexes[rollback_index_location]; |
| #if 0 |
| dprintf(CRITICAL, "indexes read: %08llx %08llx %08llx %08llx\n", |
| indexes->rollback_indexes[0], indexes->rollback_indexes[1], |
| indexes->rollback_indexes[2], indexes->rollback_indexes[3]); |
| #endif |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult mt_write_rollback_index(AvbOps* ops, |
| size_t rollback_index_location, |
| uint64_t rollback_index) |
| { |
| int ret=0; |
| unsigned char blk[256] = {0}; |
| ret = mmc_rpmb_block_read(VERIDX_IN_PRMB,blk); |
| if(ret != 0 ) |
| { |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| rollback_indexes_t* indexes = (rollback_indexes_t*)blk; |
| indexes->rollback_indexes[rollback_index_location] = rollback_index; |
| #if 0 |
| dprintf(CRITICAL, "indexes write: %08llx %08llx %08llx %08llx\n", |
| indexes->rollback_indexes[0], indexes->rollback_indexes[1], |
| indexes->rollback_indexes[2], indexes->rollback_indexes[3]); |
| #endif |
| ret = mmc_rpmb_block_write(VERIDX_IN_PRMB,(unsigned char*)indexes); |
| if(ret != 0 ) |
| { |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| return AVB_IO_RESULT_OK; |
| } |
| #else |
| static AvbIOResult mt_read_rollback_index(AvbOps* ops, |
| size_t rollback_index_location, |
| uint64_t* out_rollback_index) |
| { |
| *out_rollback_index = 0; |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult mt_write_rollback_index(AvbOps* ops, |
| size_t rollback_index_location, |
| uint64_t rollback_index) |
| { |
| return AVB_IO_RESULT_OK; |
| } |
| #endif |
| |
| #define INDEX_RPMB_UNLOCK 1 |
| #define AVB_DEVICE_UNLOCK 0x5a |
| static AvbIOResult mt_read_is_device_unlocked(AvbOps* ops, bool* out_is_unlocked) |
| { |
| if (out_is_unlocked != NULL) { |
| *out_is_unlocked = false; |
| } |
| |
| #if defined(AVB_ENABLE_ANTIROLLBACK) || defined(AVB_ENABLE_DEVICE_STATE_CHANGE) |
| unsigned char blk[256]={0}; |
| int ret = -1; |
| |
| ret=mmc_rpmb_block_read(INDEX_RPMB_UNLOCK,&blk[0]); |
| if(ret != 0){ |
| dprintf(CRITICAL, "mmc_rpmb_block_read fail %d.\n", ret); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| |
| if(blk[0] == AVB_DEVICE_UNLOCK) |
| *out_is_unlocked = true; |
| #endif |
| |
| dprintf(CRITICAL, "mt_read_is_device_unlocked return: %d.\n", *out_is_unlocked); |
| |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbIOResult mt_get_unique_guid_for_partition(AvbOps* ops, |
| const char* partition, |
| char* guid_buf, |
| size_t guid_buf_size) |
| { |
| bdev_t *bdev; |
| |
| bdev = bio_open_by_label(partition) ? : bio_open(partition); |
| if (!bdev) { |
| dprintf(CRITICAL, "Partition [%s] is not exist.\n", partition); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| strlcpy(guid_buf, bdev->unique_uuid, guid_buf_size); |
| |
| bio_close(bdev); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| #include <libfdt.h> |
| #include <image.h> |
| extern const unsigned char blob[]; |
| static const unsigned char* get_pubkey_in_blob(void) |
| { |
| int sig_node; |
| int noffset; |
| const void *sig_blob=&blob[0]; |
| sig_node = fdt_subnode_offset(sig_blob, 0, FDT_SIG_NODE); |
| for (noffset = fdt_first_subnode(sig_blob, sig_node); |
| noffset >= 0; |
| noffset = fdt_next_subnode(sig_blob, noffset)) |
| { |
| return (const unsigned char*)fdt_getprop(sig_blob, noffset, BLOB_MOD_NODE, NULL); |
| } |
| return NULL; |
| } |
| |
| static AvbIOResult mt_validate_vbmeta_public_key(AvbOps* ops, |
| const uint8_t* public_key_data, |
| size_t public_key_length, |
| const uint8_t* public_key_metadata, |
| size_t public_key_metadata_length, |
| bool* out_is_trusted) |
| { |
| unsigned char* pubkey_in_blob; |
| unsigned char* pubkey_in_footer; |
| AvbRSAPublicKeyHeader* header; |
| |
| header = (AvbRSAPublicKeyHeader*)public_key_data; |
| pubkey_in_blob = (unsigned char*)get_pubkey_in_blob(); |
| pubkey_in_footer = (unsigned char*)(public_key_data+sizeof(AvbRSAPublicKeyHeader)); |
| if(memcmp(pubkey_in_blob,pubkey_in_footer,avb_be32toh(header->key_num_bits)/32)==0) |
| { |
| *out_is_trusted = true; |
| } |
| else |
| { |
| *out_is_trusted = false; |
| } |
| |
| return AVB_IO_RESULT_OK; |
| } |
| |
| static AvbOps avbops; |
| |
| static AvbABOps avbabops = { |
| .ops = &avbops, |
| |
| .read_ab_metadata = avb_ab_data_read, |
| .write_ab_metadata = avb_ab_data_write, |
| }; |
| |
| |
| static AvbOps avbops = { |
| .ab_ops = &avbabops, |
| |
| .read_from_partition = mt_read_from_partition, |
| .write_to_partition = mt_write_to_partition, |
| .validate_vbmeta_public_key = mt_validate_vbmeta_public_key, |
| .read_rollback_index = mt_read_rollback_index, |
| .write_rollback_index = mt_write_rollback_index, |
| .read_is_device_unlocked = mt_read_is_device_unlocked, |
| .get_unique_guid_for_partition = mt_get_unique_guid_for_partition, |
| }; |
| |
| bool is_device_unlocked(void) |
| { |
| AvbIOResult io_ret; |
| bool is_device_unlocked; |
| io_ret = avbops.read_is_device_unlocked(&avbops, &is_device_unlocked); |
| if (io_ret != AVB_IO_RESULT_OK) { |
| //any error treat as locked |
| return false; |
| } |
| return is_device_unlocked; |
| } |
| |
| void* get_partition_data(char* part_name,AvbSlotVerifyData* verifyData) |
| { |
| size_t i=0; |
| |
| for(;i<verifyData->num_loaded_partitions;i++) |
| { |
| if(avb_strcmp(part_name,verifyData->loaded_partitions[i].partition_name)==0) |
| { |
| return verifyData->loaded_partitions[i].data; |
| } |
| } |
| return NULL; |
| } |
| |
| AvbSlotVerifyResult avb_update_rollback_indexes(AvbOps* ops,AvbSlotVerifyData* verifyData) |
| { |
| size_t n = 0; |
| AvbIOResult io_ret; |
| AvbSlotVerifyResult ret = AVB_SLOT_VERIFY_RESULT_OK; |
| /* 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; |
| |
| rollback_index_value = verifyData->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_SLOT_VERIFY_RESULT_ERROR_OOM; |
| goto out; |
| } else if (io_ret != AVB_IO_RESULT_OK) { |
| avb_error("Error getting rollback index for slot.\n"); |
| ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; |
| 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_SLOT_VERIFY_RESULT_ERROR_OOM; |
| goto out; |
| } else if (io_ret != AVB_IO_RESULT_OK) { |
| avb_error("Error setting stored rollback index.\n"); |
| ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; |
| goto out; |
| } |
| } |
| } |
| } |
| out: |
| return ret; |
| } |
| |
| #ifndef AB_OTA_UPDATER |
| AvbSlotVerifyResult android_verified_boot_2_0(AvbSlotVerifyData** verifyData) |
| { |
| AvbSlotVerifyResult verify_result; |
| const char* requested_partitions[] = {BOOT_PART_NAME,NULL}; |
| verify_result = avb_slot_verify(&avbops,requested_partitions,"",is_device_unlocked(),verifyData); |
| dprintf(CRITICAL, "avb boot verification result is %s\n",avb_slot_verify_result_to_string(verify_result)); |
| if(verify_result == AVB_SLOT_VERIFY_RESULT_OK) |
| { |
| verify_result = avb_update_rollback_indexes(&avbops,*verifyData); |
| dprintf(CRITICAL, "avb boot rollback indexes result is %s\n",avb_slot_verify_result_to_string(verify_result)); |
| } |
| return verify_result; |
| } |
| #else |
| //static AvbSlotVerifyData *slot_data; |
| AvbSlotVerifyResult android_verified_boot_2_0(AvbSlotVerifyData** verifyData) |
| { |
| AvbABFlowResult ab_result; |
| const char* requested_partitions[] = {"bootimg", NULL}; |
| /* ab flow */ |
| ab_result = avb_ab_flow(&avbabops, requested_partitions, false, verifyData); |
| dprintf(CRITICAL, "ab_result: %s\n", avb_ab_flow_result_to_string(ab_result)); |
| |
| return ab_result; |
| } |
| #endif |