| /* |
| * Copyright (C) 2016 Freescale Semiconductor, Inc. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <common.h> |
| #include <part.h> |
| #include <stdlib.h> |
| |
| #include <fsl_fastboot.h> |
| #include "../../../drivers/usb/gadget/fastboot_lock_unlock.h" |
| |
| #include <fsl_avb.h> |
| #include "fsl_avbkey.h" |
| #include "utils.h" |
| #include "debug.h" |
| #include "trusty/avb.h" |
| #if !defined(CONFIG_IMX_TRUSTY_OS) |
| #include "fsl_public_key.h" |
| #endif |
| #include "fsl_atx_attributes.h" |
| |
| #define FSL_AVB_DEV "mmc" |
| #define AVB_MAX_BUFFER_LENGTH 2048 |
| |
| static struct blk_desc *fs_dev_desc = NULL; |
| static struct blk_desc *get_mmc_desc(void) { |
| extern int mmc_get_env_dev(void); |
| int dev_no = mmc_get_env_dev(); |
| return blk_get_dev(FSL_AVB_DEV, dev_no); |
| } |
| |
| /* Reads |num_bytes| from offset |offset| from partition with name |
| * |partition| (NUL-terminated UTF-8 string). If |offset| is |
| * negative, its absolute value should be interpreted as the number |
| * of bytes from the end of the partition. |
| * |
| * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if |
| * there is no partition with the given name, |
| * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested |
| * |offset| is outside the partition, and AVB_IO_RESULT_ERROR_IO if |
| * there was an I/O error from the underlying I/O subsystem. If the |
| * operation succeeds as requested AVB_IO_RESULT_OK is returned and |
| * the data is available in |buffer|. |
| * |
| * The only time partial I/O may occur is if reading beyond the end |
| * of the partition. In this case the value returned in |
| * |out_num_read| may be smaller than |num_bytes|. |
| */ |
| AvbIOResult fsl_read_from_partition(AvbOps* ops, const char* partition, |
| int64_t offset, size_t num_bytes, |
| void* buffer, size_t* out_num_read) |
| { |
| struct fastboot_ptentry *pte; |
| unsigned char *bdata; |
| unsigned char *out_buf = (unsigned char *)buffer; |
| unsigned long blksz; |
| unsigned long s, cnt; |
| size_t num_read = 0; |
| lbaint_t part_start, part_end, bs, be; |
| margin_pos_t margin; |
| |
| AvbIOResult ret; |
| |
| DEBUGAVB("[%s]: offset=%ld, num_bytes=%zu\n", partition, (long)offset, num_bytes); |
| |
| assert(buffer != NULL && out_num_read != NULL); |
| |
| if ((fs_dev_desc = get_mmc_desc()) == NULL) { |
| ERR("mmc device not found\n"); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| pte = fastboot_flash_find_ptn(partition); |
| if (!pte) { |
| ERR("no %s partition\n", partition); |
| fastboot_flash_dump_ptn(); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| blksz = fs_dev_desc->blksz; |
| part_start = pte->start; |
| part_end = pte->start + pte->length - 1; |
| VDEBUG("blksz: %ld, part_end: %ld, part_start: %ld:\n", |
| blksz, part_end, part_start); |
| |
| if(get_margin_pos((uint64_t)part_start, (uint64_t)part_end, blksz, |
| &margin, offset, num_bytes, true)) |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| |
| bs = (lbaint_t)margin.blk_start; |
| be = (lbaint_t)margin.blk_end; |
| s = margin.start; |
| |
| // alloc a blksz mem |
| bdata = (unsigned char *)memalign(ALIGN_BYTES, blksz); |
| if (bdata == NULL) |
| return AVB_IO_RESULT_ERROR_OOM; |
| |
| // one block a time |
| while (bs <= be) { |
| memset(bdata, 0, blksz); |
| if (blk_dread(fs_dev_desc, bs, 1, bdata) != 1) { |
| ret = AVB_IO_RESULT_ERROR_IO; |
| goto fail; |
| } |
| cnt = blksz - s; |
| if (num_read + cnt > num_bytes) |
| cnt = num_bytes - num_read; |
| VDEBUG("cur: bs=%ld, start=%ld, cnt=%ld bdata=0x%08x\n", |
| bs, s, cnt, bdata); |
| memcpy(out_buf, bdata + s, cnt); |
| bs++; |
| num_read += cnt; |
| out_buf += cnt; |
| s = 0; |
| } |
| *out_num_read = num_read; |
| ret = AVB_IO_RESULT_OK; |
| #ifdef AVB_VVDEBUG |
| printf("\nnum_read=%zu", num_read); |
| printf("\n----dump---\n"); |
| print_buffer(0, buffer, HEXDUMP_WIDTH, num_read, 0); |
| printf("--- end ---\n"); |
| #endif |
| |
| fail: |
| free(bdata); |
| return ret; |
| } |
| |
| /* multi block read version of read_from_partition */ |
| AvbIOResult fsl_read_from_partition_multi(AvbOps* ops, const char* partition, |
| int64_t offset, size_t num_bytes, |
| void* buffer, size_t* out_num_read) |
| { |
| struct fastboot_ptentry *pte; |
| unsigned char *bdata; |
| unsigned char *out_buf = (unsigned char *)buffer; |
| unsigned char *dst, *dst64 = NULL; |
| unsigned long blksz; |
| unsigned long s, cnt; |
| size_t num_read = 0; |
| lbaint_t part_start, part_end, bs, be, bm, blk_num; |
| margin_pos_t margin; |
| |
| AvbIOResult ret; |
| |
| DEBUGAVB("[%s]: offset=%ld, num_bytes=%zu\n", partition, (long)offset, num_bytes); |
| |
| assert(buffer != NULL && out_num_read != NULL); |
| |
| if ((fs_dev_desc = get_mmc_desc()) == NULL) { |
| ERR("mmc device not found\n"); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| pte = fastboot_flash_find_ptn(partition); |
| if (!pte) { |
| ERR("no %s partition\n", partition); |
| fastboot_flash_dump_ptn(); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| blksz = fs_dev_desc->blksz; |
| part_start = pte->start; |
| part_end = pte->start + pte->length - 1; |
| VDEBUG("blksz: %ld, part_end: %ld, part_start: %ld:\n", |
| blksz, part_end, part_start); |
| |
| if(get_margin_pos((uint64_t)part_start, (uint64_t)part_end, blksz, |
| &margin, offset, num_bytes, true)) |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| |
| bs = (lbaint_t)margin.blk_start; |
| be = (lbaint_t)margin.blk_end; |
| s = margin.start; |
| bm = margin.multi; |
| |
| // alloc a blksz mem |
| bdata = (unsigned char *)memalign(ALIGN_BYTES, blksz); |
| if (bdata == NULL) |
| return AVB_IO_RESULT_ERROR_OOM; |
| |
| // support multi blk read |
| while (bs <= be) { |
| if (!s && bm > 1) { |
| dst = out_buf; |
| dst64 = PTR_ALIGN(out_buf, 64); //for mmc blk read alignment |
| VDEBUG("cur: dst=0x%08x, dst64=0x%08x\n", dst, dst64); |
| if (dst64 != dst) { |
| dst = dst64; |
| bm--; |
| } |
| blk_num = bm; |
| cnt = bm * blksz; |
| bm = 0; //no more multi blk |
| } else { |
| blk_num = 1; |
| cnt = blksz - s; |
| if (num_read + cnt > num_bytes) |
| cnt = num_bytes - num_read; |
| dst = bdata; |
| } |
| VDEBUG("cur: bs=%ld, num=%ld, start=%ld, cnt=%ld dst=0x%08x\n", |
| bs, blk_num, s, cnt, dst); |
| if (blk_dread(fs_dev_desc, bs, blk_num, dst) != blk_num) { |
| ret = AVB_IO_RESULT_ERROR_IO; |
| goto fail; |
| } |
| |
| if (dst == bdata) |
| memcpy(out_buf, bdata + s, cnt); |
| else if (dst == dst64) |
| memcpy(out_buf, dst, cnt); //internal copy |
| |
| s = 0; |
| bs += blk_num; |
| num_read += cnt; |
| out_buf += cnt; |
| #ifdef AVB_VVDEBUG |
| printf("\nnum_read=%ld", cnt); |
| printf("\n----dump---\n"); |
| print_buffer(0, buffer, HEXDUMP_WIDTH, cnt, 0); |
| printf("--- end ---\n"); |
| #endif |
| } |
| *out_num_read = num_read; |
| ret = AVB_IO_RESULT_OK; |
| #ifdef AVB_VVDEBUG |
| printf("\nnum_read=%zu", num_read); |
| printf("\n----dump---\n"); |
| print_buffer(0, buffer, HEXDUMP_WIDTH, num_read, 0); |
| printf("--- end ---\n"); |
| #endif |
| |
| fail: |
| free(bdata); |
| return ret; |
| } |
| |
| /* Writes |num_bytes| from |bffer| at offset |offset| to partition |
| * with name |partition| (NUL-terminated UTF-8 string). If |offset| |
| * is negative, its absolute value should be interpreted as the |
| * number of bytes from the end of the partition. |
| * |
| * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if |
| * there is no partition with the given name, |
| * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested |
| * byterange goes outside the partition, and AVB_IO_RESULT_ERROR_IO |
| * if there was an I/O error from the underlying I/O subsystem. If |
| * the operation succeeds as requested AVB_IO_RESULT_OK is |
| * returned. |
| * |
| * This function never does any partial I/O, it either transfers all |
| * of the requested bytes or returns an error. |
| */ |
| AvbIOResult fsl_write_to_partition(AvbOps* ops, const char* partition, |
| int64_t offset, size_t num_bytes, |
| const void* buffer) |
| { |
| struct fastboot_ptentry *pte; |
| unsigned char *bdata; |
| unsigned char *in_buf = (unsigned char *)buffer; |
| unsigned long blksz; |
| unsigned long s, cnt; |
| size_t num_write = 0; |
| lbaint_t part_start, part_end, bs; |
| margin_pos_t margin; |
| |
| AvbIOResult ret; |
| |
| DEBUGAVB("[%s]: offset=%ld, num_bytes=%zu\n", partition, (long)offset, num_bytes); |
| |
| assert(buffer != NULL); |
| |
| if ((fs_dev_desc = get_mmc_desc()) == NULL) { |
| ERR("mmc device not found\n"); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| pte = fastboot_flash_find_ptn(partition); |
| if (!pte) { |
| ERR("no %s partition\n", partition); |
| fastboot_flash_dump_ptn(); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| |
| blksz = fs_dev_desc->blksz; |
| part_start = pte->start; |
| part_end = pte->start + pte->length - 1; |
| VDEBUG("blksz: %ld, part_end: %ld, part_start: %ld:\n", |
| blksz, part_end, part_start); |
| |
| if(get_margin_pos((uint64_t)part_start, (uint64_t)part_end, blksz, |
| &margin, offset, num_bytes, false)) |
| return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION; |
| |
| bs = (lbaint_t)margin.blk_start; |
| s = margin.start; |
| |
| // alloc a blksz mem |
| bdata = (unsigned char *)memalign(ALIGN_BYTES, blksz); |
| if (bdata == NULL) |
| return AVB_IO_RESULT_ERROR_OOM; |
| |
| while (num_write < num_bytes) { |
| memset(bdata, 0, blksz); |
| cnt = blksz - s; |
| if (num_write + cnt > num_bytes) |
| cnt = num_bytes - num_write; |
| if (!s || cnt != blksz) { //read blk first |
| if (blk_dread(fs_dev_desc, bs, 1, bdata) != 1) { |
| ret = AVB_IO_RESULT_ERROR_IO; |
| goto fail; |
| } |
| } |
| memcpy(bdata + s, in_buf, cnt); //change data |
| VDEBUG("cur: bs=%ld, start=%ld, cnt=%ld bdata=0x%08x\n", |
| bs, s, cnt, bdata); |
| if (blk_dwrite(fs_dev_desc, bs, 1, bdata) != 1) { |
| ret = AVB_IO_RESULT_ERROR_IO; |
| goto fail; |
| } |
| bs++; |
| num_write += cnt; |
| in_buf += cnt; |
| if (s != 0) |
| s = 0; |
| } |
| ret = AVB_IO_RESULT_OK; |
| |
| fail: |
| free(bdata); |
| return ret; |
| } |
| |
| /* Reads A/B metadata from persistent storage. Returned data is |
| * properly byteswapped. Returns AVB_IO_RESULT_OK on success, error |
| * code otherwise. |
| * |
| * If the data read is invalid (e.g. wrong magic or CRC checksum |
| * failure), the metadata shoule be reset using avb_ab_data_init() |
| * and then written to persistent storage. |
| * |
| * Implementations will typically want to use avb_ab_data_read() |
| * here to use the 'misc' partition for persistent storage. |
| */ |
| AvbIOResult fsl_read_ab_metadata(AvbABOps* ab_ops, struct AvbABData* data) |
| { |
| return avb_ab_data_read(ab_ops, data); |
| } |
| |
| /* Writes A/B metadata to persistent storage. This will byteswap and |
| * update the CRC as needed. Returns AVB_IO_RESULT_OK on success, |
| * error code otherwise. |
| * |
| * Implementations will typically want to use avb_ab_data_write() |
| * here to use the 'misc' partition for persistent storage. |
| */ |
| AvbIOResult fsl_write_ab_metadata(AvbABOps* ab_ops, const struct AvbABData* data) |
| { |
| return avb_ab_data_write(ab_ops, data); |
| } |
| |
| /* Gets whether the device is unlocked. The value is returned in |
| * |out_is_unlocked| (true if unlocked, false otherwise). Returns |
| * AVB_IO_RESULT_OK if the state was retrieved, otherwise an error |
| * code. |
| */ |
| AvbIOResult fsl_read_is_device_unlocked(AvbOps* ops, bool* out_is_unlocked) { |
| |
| FbLockState status; |
| |
| assert(out_is_unlocked != NULL); |
| *out_is_unlocked = false; |
| |
| status = fastboot_get_lock_stat(); |
| if (status != FASTBOOT_LOCK_ERROR) { |
| if (status == FASTBOOT_LOCK) |
| *out_is_unlocked = false; |
| else |
| *out_is_unlocked = true; |
| } else |
| return AVB_IO_RESULT_ERROR_IO; |
| |
| DEBUGAVB("is_unlocked=%d\n", *out_is_unlocked); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| /* Gets the unique partition GUID for a partition with name in |
| * |partition| (NUL-terminated UTF-8 string). The GUID is copied as |
| * a string into |guid_buf| of size |guid_buf_size| and will be NUL |
| * terminated. The string must be lower-case and properly |
| * hyphenated. For example: |
| * |
| * 527c1c6d-6361-4593-8842-3c78fcd39219 |
| * |
| * Returns AVB_IO_RESULT_OK on success, otherwise an error code. |
| */ |
| AvbIOResult fsl_get_unique_guid_for_partition(AvbOps* ops, |
| const char* partition, |
| char* guid_buf, |
| size_t guid_buf_size) { |
| assert(guid_buf != NULL); |
| #ifdef CONFIG_PARTITION_UUIDS |
| struct fastboot_ptentry *pte; |
| pte = fastboot_flash_find_ptn(partition); |
| if (!pte) { |
| ERR("no %s partition\n", partition); |
| fastboot_flash_dump_ptn(); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| strncpy(guid_buf, (const char *)pte->uuid, guid_buf_size); |
| guid_buf[guid_buf_size - 1] = '\0'; |
| DEBUGAVB("[%s]: GUID=%s\n", partition, guid_buf); |
| return AVB_IO_RESULT_OK; |
| #else |
| return AVB_IO_RESULT_ERROR_IO; |
| #endif |
| |
| } |
| /* Gets the size of a partition with the name in |partition| |
| * (NUL-terminated UTF-8 string). Returns the value in |
| * |out_size_num_bytes|. |
| * Returns AVB_IO_RESULT_OK on success, otherwise an error code. |
| */ |
| AvbIOResult fsl_get_size_of_partition(AvbOps* ops, |
| const char* partition, |
| uint64_t* out_size_num_bytes) |
| { |
| struct fastboot_ptentry *pte; |
| pte = fastboot_flash_find_ptn(partition); |
| if (!pte) { |
| ERR("no %s partition\n", partition); |
| fastboot_flash_dump_ptn(); |
| return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; |
| } |
| *out_size_num_bytes = (uint64_t)(pte->length * 512); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| #ifdef CONFIG_AVB_ATX |
| /* Reads permanent |attributes| data. There are no restrictions on where this |
| * data is stored. On success, returns AVB_IO_RESULT_OK and populates |
| * |attributes|. |
| */ |
| AvbIOResult fsl_read_permanent_attributes( |
| AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes) { |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| if (!trusty_read_permanent_attributes((uint8_t *)attributes, |
| sizeof(AvbAtxPermanentAttributes))) { |
| return AVB_IO_RESULT_OK; |
| } |
| ERR("No perm-attr fused. Will use hard code one.\n"); |
| #endif /* CONFIG_IMX_TRUSTY_OS */ |
| |
| /* use hard code permanent attributes due to limited fuse and RPMB */ |
| attributes->version = fsl_version; |
| memcpy(attributes->product_root_public_key, fsl_product_root_public_key, |
| sizeof(fsl_product_root_public_key)); |
| memcpy(attributes->product_id, fsl_atx_product_id, |
| sizeof(fsl_atx_product_id)); |
| |
| return AVB_IO_RESULT_OK; |
| } |
| |
| /* Reads a |hash| of permanent attributes. This hash MUST be retrieved from a |
| * permanently read-only location (e.g. fuses) when a device is LOCKED. On |
| * success, returned AVB_IO_RESULT_OK and populates |hash|. |
| */ |
| AvbIOResult fsl_read_permanent_attributes_hash( |
| AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) { |
| #ifdef CONFIG_ARM64 |
| /* calculate sha256(permanent attributes) */ |
| if (permanent_attributes_sha256_hash(hash) != RESULT_OK) { |
| return AVB_IO_RESULT_ERROR_IO; |
| } else { |
| return AVB_IO_RESULT_OK; |
| } |
| #else |
| uint8_t sha256_hash_buf[AVB_SHA256_DIGEST_SIZE]; |
| uint32_t sha256_hash_fuse[ATX_FUSE_BANK_NUM]; |
| |
| /* read first 112 bits of sha256(permanent attributes) from fuse */ |
| if (fsl_fuse_read(sha256_hash_fuse, ATX_FUSE_BANK_NUM, |
| PERMANENT_ATTRIBUTE_HASH_OFFSET)) { |
| printf("ERROR - read permanent attributes hash from " |
| "fuse error\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| /* only take the lower 2 bytes of last bank */ |
| sha256_hash_fuse[ATX_FUSE_BANK_NUM - 1] &= ATX_FUSE_BANK_MASK; |
| |
| /* calculate sha256(permanent attributes) */ |
| if (permanent_attributes_sha256_hash(sha256_hash_buf) != RESULT_OK) { |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| /* check if the sha256(permanent attributes) hash match the calculated one, |
| * if not match, just return all zeros hash. |
| */ |
| if (memcmp(sha256_hash_fuse, sha256_hash_buf, ATX_HASH_LENGTH)) { |
| printf("ERROR - sha256(permanent attributes) does not match\n"); |
| memset(hash, 0, AVB_SHA256_DIGEST_SIZE); |
| } else { |
| memcpy(hash, sha256_hash_buf, AVB_SHA256_DIGEST_SIZE); |
| } |
| |
| return AVB_IO_RESULT_OK; |
| #endif /* CONFIG_ARM64 */ |
| } |
| |
| /* Generates |num_bytes| random bytes and stores them in |output|, |
| * which must point to a buffer large enough to store the bytes. |
| * |
| * Returns AVB_IO_RESULT_OK on success, otherwise an error code. |
| */ |
| AvbIOResult fsl_get_random(AvbAtxOps* atx_ops, |
| size_t num_bytes, |
| uint8_t* output) |
| { |
| uint32_t num = 0; |
| uint32_t i; |
| |
| if (output == NULL) { |
| ERR("Output buffer is NULL!\n"); |
| return AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE; |
| } |
| |
| /* set the seed as device boot time. */ |
| srand((uint32_t)get_timer(0)); |
| for (i = 0; i < num_bytes; i++) { |
| num = rand() % 256; |
| output[i] = (uint8_t)num; |
| } |
| |
| return AVB_IO_RESULT_OK; |
| } |
| /* Provides the key version of a key used during verification. This may be |
| * useful for managing the minimum key version. |
| */ |
| void fsl_set_key_version(AvbAtxOps* atx_ops, |
| size_t rollback_index_location, |
| uint64_t key_version) { |
| kblb_hdr_t hdr; |
| kblb_tag_t *rbk; |
| uint64_t *plain_idx = NULL; |
| struct mmc *mmc_dev; |
| static const uint32_t kTypeMask = 0xF000; |
| |
| DEBUGAVB("[rpmb] write to rollback slot: (%zu, %" PRIu64 ")\n", |
| rollback_index_location, key_version); |
| |
| assert(atx_ops != NULL); |
| |
| if ((mmc_dev = get_mmc()) == NULL) { |
| ERR("err get mmc device\n"); |
| } |
| /* read the kblb header */ |
| if (rpmb_read(mmc_dev, (uint8_t *)&hdr, sizeof(hdr), 0) != 0) { |
| ERR("read RPMB error\n"); |
| } |
| |
| if (memcmp(hdr.magic, AVB_KBLB_MAGIC, AVB_KBLB_MAGIC_LEN) != 0) { |
| ERR("magic not match\n"); |
| } |
| |
| /* rollback index for Android Things key versions */ |
| rbk = &hdr.atx_rbk_tags[rollback_index_location & ~kTypeMask]; |
| |
| plain_idx = malloc(rbk->len); |
| if (plain_idx == NULL) |
| printf("\nError! allocate memory fail!\n"); |
| memset(plain_idx, 0, rbk->len); |
| *plain_idx = key_version; |
| |
| /* write rollback_index keyblob */ |
| if (rpmb_write(mmc_dev, (uint8_t *)plain_idx, rbk->len, rbk->offset) != |
| 0) { |
| ERR("write rollback index error\n"); |
| goto fail; |
| } |
| fail: |
| if (plain_idx != NULL) |
| free(plain_idx); |
| } |
| #endif /* CONFIG_AVB_ATX */ |
| |
| #ifdef AVB_RPMB |
| /* Checks if the given public key used to sign the 'vbmeta' |
| * partition is trusted. Boot loaders typically compare this with |
| * embedded key material generated with 'avbtool |
| * extract_public_key'. |
| * |
| * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set - |
| * true if trusted or false if untrusted. |
| */ |
| AvbIOResult fsl_validate_vbmeta_public_key_rpmb(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) { |
| AvbIOResult ret; |
| assert(ops != NULL && out_is_trusted != NULL); |
| *out_is_trusted = false; |
| |
| #if defined(CONFIG_IMX_TRUSTY_OS) && !defined(CONFIG_AVB_ATX) |
| uint8_t public_key_buf[AVB_MAX_BUFFER_LENGTH]; |
| if (trusty_read_vbmeta_public_key(public_key_buf, |
| public_key_length) != 0) { |
| ERR("Read public key error\n"); |
| /* We're not going to return error code here because it will |
| * abort the following avb verify process even we allow the |
| * verification error. Return AVB_IO_RESULT_OK and keep the |
| * 'out_is_trusted' as false, avb will handle the error |
| * depends on the 'allow_verification_error' flag. |
| */ |
| return AVB_IO_RESULT_OK; |
| } |
| |
| if (memcmp(public_key_buf, public_key_data, public_key_length)) { |
| #else |
| /* match given public key */ |
| if (memcmp(fsl_public_key, public_key_data, public_key_length)) { |
| #endif |
| ERR("public key not match\n"); |
| return AVB_IO_RESULT_OK; |
| } |
| |
| *out_is_trusted = true; |
| ret = AVB_IO_RESULT_OK; |
| |
| return ret; |
| } |
| |
| /* Sets the rollback index corresponding to the slot given by |
| * |rollback_index_slot| to |rollback_index|. Returns |
| * AVB_IO_RESULT_OK if the rollback index was set, otherwise an |
| * error code. |
| * |
| * A device may have a limited amount of rollback index slots (say, |
| * one or four) so may error out if |rollback_index_slot| exceeds |
| * this number. |
| */ |
| AvbIOResult fsl_write_rollback_index_rpmb(AvbOps* ops, size_t rollback_index_slot, |
| uint64_t rollback_index) { |
| AvbIOResult ret; |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| if (trusty_write_rollback_index(rollback_index_slot, rollback_index)) { |
| ERR("write rollback from Trusty error!\n"); |
| #ifndef CONFIG_AVB_ATX |
| /* Read/write rollback index from rpmb will fail if the rpmb |
| * key hasn't been set, return AVB_IO_RESULT_OK in this case. |
| */ |
| if (!rpmbkey_is_set()) |
| ret = AVB_IO_RESULT_OK; |
| else |
| #endif |
| ret = AVB_IO_RESULT_ERROR_IO; |
| } else { |
| ret = AVB_IO_RESULT_OK; |
| } |
| return ret; |
| #else |
| kblb_hdr_t hdr; |
| kblb_tag_t *rbk; |
| uint64_t *plain_idx = NULL; |
| struct mmc *mmc_dev; |
| #ifdef CONFIG_AVB_ATX |
| static const uint32_t kTypeMask = 0xF000; |
| static const unsigned int kTypeShift = 12; |
| #endif |
| |
| DEBUGAVB("[rpmb] write to rollback slot: (%zu, %" PRIu64 ")\n", |
| rollback_index_slot, rollback_index); |
| |
| assert(ops != NULL); |
| /* check if the rollback index location exceed the limit */ |
| #ifdef CONFIG_AVB_ATX |
| if ((rollback_index_slot & ~kTypeMask) >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) |
| #else |
| if (rollback_index_slot >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) |
| #endif /* CONFIG_AVB_ATX */ |
| return AVB_IO_RESULT_ERROR_IO; |
| |
| if ((mmc_dev = get_mmc()) == NULL) { |
| ERR("err get mmc device\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| /* read the kblb header */ |
| if (rpmb_read(mmc_dev, (uint8_t *)&hdr, sizeof(hdr), 0) != 0) { |
| ERR("read RPMB error\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| |
| if (memcmp(hdr.magic, AVB_KBLB_MAGIC, AVB_KBLB_MAGIC_LEN) != 0) { |
| ERR("magic not match\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| /* choose rollback index type */ |
| #ifdef CONFIG_AVB_ATX |
| if ((rollback_index_slot & kTypeMask) >> kTypeShift) { |
| /* rollback index for Android Things key versions */ |
| rbk = &hdr.atx_rbk_tags[rollback_index_slot & ~kTypeMask]; |
| } else { |
| /* rollback index for vbmeta */ |
| rbk = &hdr.rbk_tags[rollback_index_slot & ~kTypeMask]; |
| } |
| #else |
| rbk = &hdr.rbk_tags[rollback_index_slot]; |
| #endif /* CONFIG_AVB_ATX */ |
| plain_idx = malloc(rbk->len); |
| if (plain_idx == NULL) |
| return AVB_IO_RESULT_ERROR_OOM; |
| memset(plain_idx, 0, rbk->len); |
| *plain_idx = rollback_index; |
| |
| /* write rollback_index keyblob */ |
| if (rpmb_write(mmc_dev, (uint8_t *)plain_idx, rbk->len, rbk->offset) != |
| 0) { |
| ERR("write rollback index error\n"); |
| ret = AVB_IO_RESULT_ERROR_IO; |
| goto fail; |
| } |
| ret = AVB_IO_RESULT_OK; |
| fail: |
| if (plain_idx != NULL) |
| free(plain_idx); |
| return ret; |
| #endif /* CONFIG_IMX_TRUSTY_OS */ |
| } |
| |
| /* Gets the rollback index corresponding to the slot given by |
| * |rollback_index_slot|. The value is returned in |
| * |out_rollback_index|. Returns AVB_IO_RESULT_OK if the rollback |
| * index was retrieved, otherwise an error code. |
| * |
| * A device may have a limited amount of rollback index slots (say, |
| * one or four) so may error out if |rollback_index_slot| exceeds |
| * this number. |
| */ |
| AvbIOResult fsl_read_rollback_index_rpmb(AvbOps* ops, size_t rollback_index_slot, |
| uint64_t* out_rollback_index) { |
| AvbIOResult ret; |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| if (trusty_read_rollback_index(rollback_index_slot, out_rollback_index)) { |
| ERR("read rollback from Trusty error!\n"); |
| #ifndef CONFIG_AVB_ATX |
| if (!rpmbkey_is_set()) { |
| *out_rollback_index = 0; |
| ret = AVB_IO_RESULT_OK; |
| } else |
| #endif |
| ret = AVB_IO_RESULT_ERROR_IO; |
| } else { |
| ret = AVB_IO_RESULT_OK; |
| } |
| return ret; |
| #else |
| kblb_hdr_t hdr; |
| kblb_tag_t *rbk; |
| uint64_t *extract_idx = NULL; |
| struct mmc *mmc_dev; |
| #ifdef CONFIG_AVB_ATX |
| static const uint32_t kTypeMask = 0xF000; |
| static const unsigned int kTypeShift = 12; |
| #endif |
| |
| assert(ops != NULL && out_rollback_index != NULL); |
| *out_rollback_index = ~0; |
| |
| DEBUGAVB("[rpmb] read rollback slot: %zu\n", rollback_index_slot); |
| |
| /* check if the rollback index location exceed the limit */ |
| #ifdef CONFIG_AVB_ATX |
| if ((rollback_index_slot & ~kTypeMask) >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) |
| #else |
| if (rollback_index_slot >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) |
| #endif |
| return AVB_IO_RESULT_ERROR_IO; |
| |
| if ((mmc_dev = get_mmc()) == NULL) { |
| ERR("err get mmc device\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| /* read the kblb header */ |
| if (rpmb_read(mmc_dev, (uint8_t *)&hdr, sizeof(hdr), 0) != 0) { |
| ERR("read RPMB error\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| |
| if (memcmp(hdr.magic, AVB_KBLB_MAGIC, AVB_KBLB_MAGIC_LEN) != 0) { |
| ERR("magic not match\n"); |
| return AVB_IO_RESULT_ERROR_IO; |
| } |
| /* choose rollback index type */ |
| #ifdef CONFIG_AVB_ATX |
| if ((rollback_index_slot & kTypeMask) >> kTypeShift) { |
| /* rollback index for Android Things key versions */ |
| rbk = &hdr.atx_rbk_tags[rollback_index_slot & ~kTypeMask]; |
| } else { |
| /* rollback index for vbmeta */ |
| rbk = &hdr.rbk_tags[rollback_index_slot & ~kTypeMask]; |
| } |
| #else |
| rbk = &hdr.rbk_tags[rollback_index_slot]; |
| #endif /* CONFIG_AVB_ATX */ |
| extract_idx = malloc(rbk->len); |
| if (extract_idx == NULL) |
| return AVB_IO_RESULT_ERROR_OOM; |
| |
| /* read rollback_index keyblob */ |
| if (rpmb_read(mmc_dev, (uint8_t *)extract_idx, rbk->len, rbk->offset) != 0) { |
| ERR("read rollback index error\n"); |
| ret = AVB_IO_RESULT_ERROR_IO; |
| goto fail; |
| } |
| |
| #ifdef AVB_VVDEBUG |
| printf("\n----idx dump: ---\n"); |
| print_buffer(0, extract_idx, HEXDUMP_WIDTH, rbk->len, 0); |
| printf("--- end ---\n"); |
| #endif |
| *out_rollback_index = *extract_idx; |
| DEBUGAVB("rollback_index = %" PRIu64 "\n", *out_rollback_index); |
| ret = AVB_IO_RESULT_OK; |
| fail: |
| if (extract_idx != NULL) |
| free(extract_idx); |
| return ret; |
| #endif /* CONFIG_IMX_TRUSTY_OS */ |
| } |
| #else /* AVB_RPMB */ |
| /* |
| * In no security enhanced ARM64, we cannot protect public key. |
| * So that we choose to trust the key from vbmeta image |
| */ |
| AvbIOResult fsl_validate_vbmeta_public_key_rpmb(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) { |
| *out_is_trusted = true; |
| return AVB_IO_RESULT_OK; |
| } |
| |
| /* In no security enhanced ARM64, rollback index has no protection so no use it */ |
| AvbIOResult fsl_write_rollback_index_rpmb(AvbOps* ops, size_t rollback_index_slot, |
| uint64_t rollback_index) { |
| return AVB_IO_RESULT_OK; |
| |
| } |
| AvbIOResult fsl_read_rollback_index_rpmb(AvbOps* ops, size_t rollback_index_slot, |
| uint64_t* out_rollback_index) { |
| *out_rollback_index = 0; |
| return AVB_IO_RESULT_OK; |
| } |
| #endif /* AVB_RPMB */ |