blob: fbeea7250b6553b0daa19eebae6e311c7761dadd [file] [log] [blame]
/*
* 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>
#include "blxboot_ab.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_node_for_partition(AvbOps *ops,
const char *partition,
char *guid_buf,
size_t guid_buf_size)
{
strlcpy(guid_buf, "/dev/ubiblock0_0", guid_buf_size);
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();
if (!pubkey_in_blob) {
avb_error("Public key is NULL in blob\n");
return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE;
}
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 AvbIOResult mt_get_size_of_partition(AvbOps *ops,
const char *partition,
uint64_t *out_size_num_bytes)
{
bdev_t *bdev;
bdev = bio_open_by_label(partition) ? : bio_open(partition);
*out_size_num_bytes = (uint64_t)bdev->total_size;
return AVB_IO_RESULT_OK;
}
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_size_of_partition = mt_get_size_of_partition,
#ifdef BOOT_DEV_NAND
.get_unique_guid_for_partition = mt_get_unique_node_for_partition,
#else
.get_unique_guid_for_partition = mt_get_unique_guid_for_partition,
#endif
};
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(const char *part_name, AvbSlotVerifyData *verifyData)
{
size_t i=0;
if ((part_name == NULL) || (verifyData == NULL))
return NULL;
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, &current_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;
}
AvbSlotVerifyResult android_verified_boot_2_0(const char *part_name,
AvbSlotVerifyData **verifyData)
{
AvbSlotVerifyResult verify_result;
const char *requested_partitions[] = { part_name, NULL };
const char *ab_suffix = get_suffix() ? : "";
verify_result = avb_slot_verify(&avbops,requested_partitions,ab_suffix,is_device_unlocked(),AVB_HASHTREE_ERROR_MODE_RESTART,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;
}