blob: d3b53db4d033bdf36e3e7adf77649fdae18d3bce [file]
/* Copyright 2025 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Functions to load and verify an Android kernel.
*/
#include "2api.h"
#include "2avb.h"
#include "2common.h"
#include "2load_android_kernel.h"
#include "2misc.h"
#include "2secdata.h"
#include "cgptlib.h"
#include "cgptlib_internal.h"
#include "gpt_misc.h"
#include "vboot_api.h"
#include "vb2_android_bootimg.h"
#define GPT_ENT_NAME_ANDROID_A_SUFFIX "_a"
#define GPT_ENT_NAME_ANDROID_B_SUFFIX "_b"
#define MICRODROID_VENDOR_DIGEST_KEY "com.android.build.microdroid-vendor.root_digest"
#define VERIFIED_BOOT_PROPERTY_NAME "androidboot.verifiedbootstate"
#define SLOT_SUFFIX_BOOT_PROPERTY_NAME "androidboot.slot_suffix"
#define ANDROID_FORCE_NORMAL_BOOT_PROPERTY_NAME "androidboot.force_normal_boot"
static vb2_error_t vb2_map_libavb_errors(AvbSlotVerifyResult avb_error)
{
/* Map AVB error into VB2 */
switch (avb_error) {
case AVB_SLOT_VERIFY_RESULT_OK:
return VB2_SUCCESS;
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
return VB2_ERROR_AVB_OOM;
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
return VB2_ERROR_AVB_ERROR_IO;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
return VB2_ERROR_AVB_ERROR_VERIFICATION;
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
return VB2_ERROR_AVB_ERROR_ROLLBACK_INDEX;
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
return VB2_ERROR_AVB_ERROR_PUBLIC_KEY_REJECTED;
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
return VB2_ERROR_AVB_ERROR_INVALID_METADATA;
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
return VB2_ERROR_AVB_ERROR_UNSUPPORTED_VERSION;
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
return VB2_ERROR_AVB_ERROR_INVALID_ARGUMENT;
default:
return VB2_ERROR_AVB_ERROR_VERIFICATION;
}
}
/*
* Copy bootconfig into separate buffer, it can be overwritten when ramdisks
* are concatenated. Bootconfig buffer will be processed by depthcharge.
*/
static vb2_error_t save_bootconfig(struct vendor_boot_img_hdr_v4 *vendor_hdr,
size_t total_size,
struct vb2_kernel_params *params)
{
uint8_t *bootconfig;
size_t bootconfig_offset;
uint32_t page_size = vendor_hdr->page_size;
if (!vendor_hdr->bootconfig_size)
return VB2_SUCCESS;
bootconfig_offset = VB2_ALIGN_UP(sizeof(struct vendor_boot_img_hdr_v4), page_size) +
VB2_ALIGN_UP(vendor_hdr->vendor_ramdisk_size, page_size) +
VB2_ALIGN_UP(vendor_hdr->dtb_size, page_size) +
VB2_ALIGN_UP(vendor_hdr->vendor_ramdisk_table_size, page_size);
if (bootconfig_offset > total_size ||
total_size - bootconfig_offset < vendor_hdr->bootconfig_size) {
VB2_DEBUG("Broken 'vendor_boot' image\n");
return VB2_ERROR_ANDROID_BROKEN_VENDOR_BOOT;
}
params->bootconfig = malloc(vendor_hdr->bootconfig_size);
if (!params->bootconfig) {
VB2_DEBUG("Cannot malloc %u bytes for bootconfig", vendor_hdr->bootconfig_size);
return VB2_ERROR_ANDROID_MEMORY_ALLOC;
}
bootconfig = (uint8_t *)vendor_hdr + bootconfig_offset;
memcpy(params->bootconfig, bootconfig, vendor_hdr->bootconfig_size);
params->bootconfig_size = vendor_hdr->bootconfig_size;
return VB2_SUCCESS;
}
static bool gki_ramdisk_fragment_needed(struct vendor_ramdisk_table_entry_v4 *fragment,
bool recovery_boot)
{
/* Ignore all other properties except ramdisk type */
switch (fragment->ramdisk_type) {
case VENDOR_RAMDISK_TYPE_PLATFORM:
case VENDOR_RAMDISK_TYPE_DLKM:
return true;
case VENDOR_RAMDISK_TYPE_RECOVERY:
return recovery_boot;
default:
printf("Unknown ramdisk type 0x%x\n", fragment->ramdisk_type);
return false;
}
}
/* Function for finding a loaded partition in AvbSlotVerifyData */
static AvbPartitionData *avb_find_part(AvbSlotVerifyData *verify_data, enum GptPartition name)
{
size_t i;
AvbPartitionData *part;
for (i = 0; i < verify_data->num_loaded_partitions; i++) {
part = &verify_data->loaded_partitions[i];
if (!strcmp(part->partition_name, GptPartitionNames[name]))
return part;
}
return NULL;
}
/*
* This function removes unnecessary ramdisks from ramdisk table, concatenates rest of
* them and returns start and end of new ramdisk.
*/
static vb2_error_t prepare_vendor_ramdisks(struct vendor_boot_img_hdr_v4 *vendor_hdr,
size_t total_size,
bool recovery_boot,
uint8_t **vendor_ramdisk,
uint8_t **vendor_ramdisk_end)
{
uint32_t ramdisk_offset;
uint32_t ramdisk_table_offset;
uint32_t ramdisk_table_size = vendor_hdr->vendor_ramdisk_table_size;
uint32_t ramdisk_table_entry_size = vendor_hdr->vendor_ramdisk_table_entry_size;
uint32_t ramdisk_table_entry_num = vendor_hdr->vendor_ramdisk_table_entry_num;
uint32_t page_size = vendor_hdr->page_size;
uintptr_t fragment_ptr;
/* Calculate address offset of vendor_ramdisk section on vendor_boot partition */
ramdisk_offset = VB2_ALIGN_UP(sizeof(struct vendor_boot_img_hdr_v4), page_size);
ramdisk_table_offset = ramdisk_offset +
VB2_ALIGN_UP(vendor_hdr->vendor_ramdisk_size, page_size) +
VB2_ALIGN_UP(vendor_hdr->dtb_size, page_size);
/* Check if vendor ramdisk table is correct */
if (ramdisk_offset > total_size ||
ramdisk_table_offset > total_size ||
ramdisk_table_entry_size < sizeof(struct vendor_ramdisk_table_entry_v4) ||
total_size - ramdisk_offset < vendor_hdr->vendor_ramdisk_size ||
total_size - ramdisk_table_offset < ramdisk_table_size ||
ramdisk_table_size < (ramdisk_table_entry_num * ramdisk_table_entry_size)) {
VB2_DEBUG("Broken 'vendor_boot' image\n");
return VB2_ERROR_ANDROID_BROKEN_VENDOR_BOOT;
}
*vendor_ramdisk = (uint8_t *)vendor_hdr + ramdisk_offset;
*vendor_ramdisk_end = *vendor_ramdisk;
fragment_ptr = (uintptr_t)vendor_hdr + ramdisk_table_offset;
/* Go through all ramdisk fragments and keep only the required ones */
for (int i = 0; i < ramdisk_table_entry_num;
fragment_ptr += ramdisk_table_entry_size, i++) {
struct vendor_ramdisk_table_entry_v4 *fragment;
uint8_t *fragment_src;
fragment = (struct vendor_ramdisk_table_entry_v4 *)fragment_ptr;
if (!gki_ramdisk_fragment_needed(fragment, recovery_boot))
continue;
uint32_t fragment_size = fragment->ramdisk_size;
uint32_t fragment_offset = fragment->ramdisk_offset;
if (fragment_offset > vendor_hdr->vendor_ramdisk_size ||
vendor_hdr->vendor_ramdisk_size - fragment_offset < fragment_size) {
VB2_DEBUG("Incorrect fragment - offset:%x size:%x, ramdisk_size: %x\n",
fragment_offset, fragment_size,
vendor_hdr->vendor_ramdisk_size);
return VB2_ERROR_ANDROID_BROKEN_VENDOR_BOOT;
}
fragment_src = *vendor_ramdisk + fragment_offset;
if (*vendor_ramdisk_end != fragment_src)
/*
* A fragment was skipped before, we need to move current one
* at the correct place.
*/
memmove(*vendor_ramdisk_end, fragment_src, fragment_size);
/* Update location of the end of vendor ramdisk */
*vendor_ramdisk_end += fragment_size;
}
return VB2_SUCCESS;
}
static const char *find_avb_prop(AvbSlotVerifyData *verify_data, const char *key)
{
AvbVBMetaData *vbmeta;
const char *value;
size_t i;
for (i = 0; i < verify_data->num_vbmeta_images; i++) {
vbmeta = &verify_data->vbmeta_images[i];
value = avb_property_lookup(vbmeta->vbmeta_data, vbmeta->vbmeta_size, key, 0,
NULL);
if (value)
return value;
}
return NULL;
}
static vb2_error_t append_recovery_ramdisk(AvbOps *avb_ops, uint8_t **ramdisk_end)
{
struct boot_img_hdr_v4 *rec_hdr;
size_t recovery_size;
uint32_t kernel_aligned;
uint8_t *rec_ramdisk;
if (vb2_android_get_buffer(avb_ops, GPT_ANDROID_RECOVERY,
(void **)&rec_hdr, &recovery_size)) {
VB2_DEBUG("Cannot get information about preloaded 'recovery' partition\n");
return VB2_ERROR_ANDROID_BROKEN_RECOVERY;
}
if (recovery_size < BOOT_HEADER_SIZE ||
memcmp(rec_hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
VB2_DEBUG("Incorrect magic or size (%zu) of 'recovery' image\n",
recovery_size);
return VB2_ERROR_ANDROID_BROKEN_RECOVERY;
}
if (rec_hdr->kernel_size > recovery_size - BOOT_HEADER_SIZE) {
VB2_DEBUG("Incorrect size (%u) of 'recovery' kernel\n",
rec_hdr->kernel_size);
return VB2_ERROR_ANDROID_BROKEN_RECOVERY;
}
kernel_aligned = VB2_ALIGN_UP(rec_hdr->kernel_size, BOOT_HEADER_SIZE);
if (recovery_size - BOOT_HEADER_SIZE < kernel_aligned ||
recovery_size - BOOT_HEADER_SIZE - kernel_aligned < rec_hdr->ramdisk_size ||
rec_hdr->ramdisk_size == 0) {
VB2_DEBUG("Incorrect 'recovery' header or sizes: ksz=%u, rsz=%u, total=%zu\n",
rec_hdr->kernel_size, rec_hdr->ramdisk_size, recovery_size);
return VB2_ERROR_ANDROID_BROKEN_RECOVERY;
}
rec_ramdisk = (uint8_t *)rec_hdr + BOOT_HEADER_SIZE + kernel_aligned;
memmove(*ramdisk_end, rec_ramdisk, rec_hdr->ramdisk_size);
*ramdisk_end += rec_hdr->ramdisk_size;
return VB2_SUCCESS;
}
static vb2_error_t prepare_pvmfw(AvbSlotVerifyData *verify_data,
struct vb2_kernel_params *params)
{
AvbPartitionData *part;
struct boot_img_hdr_v4 *pvmfw_hdr;
part = avb_find_part(verify_data, GPT_ANDROID_PVMFW);
if (!part) {
VB2_DEBUG("Ignoring lack of pvmfw partition\n");
params->pvmfw_out_size = 0;
return VB2_SUCCESS;
}
pvmfw_hdr = (void *)part->data;
/* If loaded pvmfw is smaller then boot header or the boot header magic is invalid
* or the header kernel size exceeds buffer size, then fail */
if (part->data_size < BOOT_HEADER_SIZE ||
memcmp(pvmfw_hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE) ||
part->data_size - BOOT_HEADER_SIZE < pvmfw_hdr->kernel_size) {
VB2_DEBUG("Incorrect magic or size (%zx) of 'pvmfw' image\n", part->data_size);
return VB2_ERROR_ANDROID_BROKEN_PVMFW;
}
/* Get pvmfw code size */
params->pvmfw_out_size = pvmfw_hdr->kernel_size;
/* pvmfw code starts after the boot header. Discard the boot header, by
* moving the buffer start and trimming its size. */
params->pvmfw_buffer = ((void *)pvmfw_hdr) + BOOT_HEADER_SIZE;
params->pvmfw_buffer_size -= BOOT_HEADER_SIZE;
return VB2_SUCCESS;
}
/*
* This function validates the partitions magic numbers and move them into place requested
* from linux.
*/
static vb2_error_t rearrange_partitions(AvbOps *avb_ops,
struct vb2_kernel_params *params,
bool recovery_boot,
bool dedicated_recovery)
{
struct vendor_boot_img_hdr_v4 *vendor_hdr;
struct boot_img_hdr_v4 *init_hdr;
size_t vendor_boot_size, init_boot_size;
uint8_t *vendor_ramdisk_end = 0;
if (vb2_android_get_buffer(avb_ops, GPT_ANDROID_VENDOR_BOOT, (void **)&vendor_hdr,
&vendor_boot_size) ||
vb2_android_get_buffer(avb_ops, GPT_ANDROID_INIT_BOOT, (void **)&init_hdr,
&init_boot_size)) {
VB2_DEBUG("Cannot get information about preloaded partition\n");
return VB2_ERROR_ANDROID_RAMDISK_ERROR;
}
if (vendor_boot_size < sizeof(*vendor_hdr) ||
memcmp(vendor_hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE)) {
VB2_DEBUG("Incorrect magic or size (%zx) of 'vendor_boot' image\n",
vendor_boot_size);
return VB2_ERROR_ANDROID_BROKEN_VENDOR_BOOT;
}
/* Save bootconfig for depthcharge, it can be overwritten when ramdisk are moved */
VB2_TRY(save_bootconfig(vendor_hdr, vendor_boot_size, params));
/* Remove unused ramdisks */
VB2_TRY(prepare_vendor_ramdisks(vendor_hdr, vendor_boot_size,
recovery_boot && !dedicated_recovery,
&params->ramdisk, &vendor_ramdisk_end));
if (dedicated_recovery)
VB2_TRY(append_recovery_ramdisk(avb_ops, &vendor_ramdisk_end));
params->ramdisk_size = vendor_ramdisk_end - params->ramdisk;
/* Validate init_boot partition */
if (init_boot_size < BOOT_HEADER_SIZE ||
init_boot_size - BOOT_HEADER_SIZE < init_hdr->ramdisk_size ||
init_hdr->kernel_size != 0 ||
memcmp(init_hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
VB2_DEBUG("Incorrect 'init_boot' header, total size: %zx\n",
init_boot_size);
return VB2_ERROR_ANDROID_BROKEN_INIT_BOOT;
}
/* On init_boot there's no kernel, so ramdisk follows the header */
uint8_t *init_boot_ramdisk = (uint8_t *)init_hdr + BOOT_HEADER_SIZE;
size_t init_boot_ramdisk_size = init_hdr->ramdisk_size;
/*
* Move init_boot ramdisk to directly follow the vendor_boot ramdisk.
* This is a requirement from Android system. The cpio/gzip/lz4
* compression formats support this type of concatenation. After
* the kernel decompresses, it extracts concatenated file into
* an initramfs, which results in a file structure that's a generic
* ramdisk (from init_boot) overlaid on the vendor ramdisk (from
* vendor_boot) file structure.
*/
VB2_ASSERT(vendor_ramdisk_end < init_boot_ramdisk);
memmove(vendor_ramdisk_end, init_boot_ramdisk, init_boot_ramdisk_size);
params->ramdisk_size += init_boot_ramdisk_size;
/* Save vendor cmdline for booting */
vendor_hdr->cmdline[sizeof(vendor_hdr->cmdline) - 1] = '\0';
params->real_cmdline_ptr = (char *)vendor_hdr->cmdline;
return VB2_SUCCESS;
}
static vb2_error_t prepare_dtbo(AvbSlotVerifyData *verify_data,
struct vb2_kernel_params *params)
{
AvbPartitionData *part;
part = avb_find_part(verify_data, GPT_ANDROID_DTBO);
if (!part) {
VB2_DEBUG("Continuing without a DTBO partition\n");
params->dtbo = NULL;
params->dtbo_size = 0;
} else {
params->dtbo = part->data;
params->dtbo_size = part->data_size;
}
return VB2_SUCCESS;
}
static void print_avb_slot_verify_data(const AvbSlotVerifyData *data)
{
if (!data) {
VB2_DEBUG("AvbSlotVerifyData is NULL\n");
return;
}
VB2_DEBUG("AvbSlotVerifyData:\n");
VB2_DEBUG(" ab_suffix: %s\n", data->ab_suffix);
for (size_t i = 0; i < data->num_vbmeta_images; i++) {
const AvbVBMetaData *vbmeta = &data->vbmeta_images[i];
VB2_DEBUG(" vbmeta '%s' verify_result: %d\n",
vbmeta->partition_name, vbmeta->verify_result);
}
for (size_t i = 0; i < data->num_loaded_partitions; i++) {
const AvbPartitionData *part = &data->loaded_partitions[i];
VB2_DEBUG(" loaded '%s' data_size: %zu "
"preloaded: %d verify_result: %#x\n",
part->partition_name, part->data_size,
part->preloaded, part->verify_result);
}
VB2_DEBUG(" rollback_index: %"PRIu64"\n", (uint64_t)data->rollback_indexes[0]);
}
static vb2_error_t verify_avb_data(const AvbSlotVerifyData *verify_data,
AvbOps *avb_ops,
const char *expected_slot_suffix,
const char **requested_partitions,
bool need_verification)
{
if (!verify_data) {
VB2_DEBUG("ERROR: verify_data is NULL\n");
return VB2_ERROR_AVB_ERROR_VERIFICATION;
}
/* Verify suffix */
if (!verify_data->ab_suffix ||
strcmp(verify_data->ab_suffix, expected_slot_suffix) != 0) {
VB2_DEBUG("ERROR: suffix mismatch. Expected %s, got %s\n",
expected_slot_suffix,
verify_data->ab_suffix ? verify_data->ab_suffix : "(null)");
return VB2_ERROR_ANDROID_INVALID_SLOT_SUFFIX;
}
/* Verify all requested partitions are loaded and preloaded */
for (size_t i = 0; requested_partitions[i] != NULL; i++) {
const char *part_name = requested_partitions[i];
/*
* TODO(b/408266619): Skip checking of recovery partition for now.
* Recovery will be verified when the whole feature in OS is ready.
*/
if (!strcmp(part_name, GptPartitionNames[GPT_ANDROID_RECOVERY]))
continue;
const AvbPartitionData *part = NULL;
bool found = false;
for (size_t j = 0; j < verify_data->num_loaded_partitions; j++) {
part = &verify_data->loaded_partitions[j];
if (part->partition_name &&
strcmp(part->partition_name, part_name) == 0) {
found = true;
break;
}
}
if (!found) {
VB2_DEBUG("ERROR: partition %s not found in loaded partitions\n",
part_name);
return VB2_ERROR_AVB_ERROR_VERIFICATION;
}
if (!part->preloaded) {
VB2_DEBUG("ERROR: Partition %s is not preloaded\n",
part_name);
return VB2_ERROR_AVB_ERROR_IO;
}
enum GptPartition gpt_part = GPT_ANDROID_PRELOADED_NUM;
for (size_t k = 0; k < GPT_ANDROID_PRELOADED_NUM; k++) {
if (strcmp(part_name, GptPartitionNames[k]) == 0) {
gpt_part = (enum GptPartition)k;
break;
}
}
if (gpt_part == GPT_ANDROID_PRELOADED_NUM) {
VB2_DEBUG("ERROR: Partition %s is not in preloaded list\n",
part_name);
return VB2_ERROR_AVB_ERROR_IO;
}
/* Verify that libavb used our reserved preloaded buffer. */
void *buffer;
size_t size;
AvbIOResult rv = vb2_android_get_buffer(avb_ops, gpt_part, &buffer, &size);
if (rv != AVB_IO_RESULT_OK || buffer != part->data ||
size != part->data_size) {
VB2_DEBUG("ERROR: Partition %s buffer verification failed\n",
part_name);
return VB2_ERROR_AVB_ERROR_IO;
}
if (!need_verification)
continue;
if (part->verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
VB2_DEBUG("ERROR: '%s' verification failed: %d\n",
part_name, part->verify_result);
return VB2_ERROR_AVB_ERROR_VERIFICATION;
}
if (!part->digest || part->digest_size == 0) {
VB2_DEBUG("ERROR: Partition %s has no digest\n",
part_name);
return VB2_ERROR_AVB_ERROR_VERIFICATION;
}
}
if (!need_verification)
goto out;
/* Verify vbmeta images if verification is needed */
for (size_t i = 0; i < verify_data->num_vbmeta_images; i++) {
const AvbVBMetaData *vbmeta = &verify_data->vbmeta_images[i];
if (vbmeta->verify_result != AVB_VBMETA_VERIFY_RESULT_OK) {
VB2_DEBUG("ERROR: VBMeta image %s verification failed: %d\n",
vbmeta->partition_name ? vbmeta->partition_name : "(null)",
vbmeta->verify_result);
return VB2_ERROR_AVB_ERROR_VERIFICATION;
}
}
out:
return VB2_SUCCESS;
}
vb2_error_t vb2_load_android(struct vb2_context *ctx, GptData *gpt, GptEntry *entry,
struct vb2_kernel_params *params, vb2ex_disk_handle_t disk_handle,
uint32_t *kernel_version)
{
enum vb2_android_bootmode bootmode = VB2_ANDROID_NORMAL_BOOT;
AvbSlotVerifyData *verify_data = NULL;
AvbOps *avb_ops = NULL;
AvbSlotVerifyFlags avb_flags;
AvbSlotVerifyResult result;
vb2_error_t rv;
const char *boot_partitions[GPT_ANDROID_PRELOADED_NUM + 1] = {0};
size_t partition_count = 0;
const char *slot_suffix = NULL;
bool need_verification = vb2_need_kernel_verification(ctx);
boot_partitions[partition_count++] = GptPartitionNames[GPT_ANDROID_BOOT];
boot_partitions[partition_count++] = GptPartitionNames[GPT_ANDROID_INIT_BOOT];
boot_partitions[partition_count++] = GptPartitionNames[GPT_ANDROID_VENDOR_BOOT];
rv = vb2ex_handle_android_misc_partition(ctx, disk_handle, gpt, &bootmode);
if (rv != VB2_SUCCESS) {
VB2_DEBUG("Unable to get android bootmode\n");
goto out;
}
bool recovery_boot = bootmode == VB2_ANDROID_RECOVERY_BOOT;
if (recovery_boot)
boot_partitions[partition_count++] = GptPartitionNames[GPT_ANDROID_RECOVERY];
/* Update flags to mark loaded GKI image */
params->flags = VB2_KERNEL_TYPE_BOOTIMG;
const char *vbmeta = GptPartitionNames[GPT_ANDROID_VBMETA];
if (GptEntryHasName(entry, vbmeta, GPT_ENT_NAME_ANDROID_A_SUFFIX))
slot_suffix = GPT_ENT_NAME_ANDROID_A_SUFFIX;
else if (GptEntryHasName(entry, vbmeta, GPT_ENT_NAME_ANDROID_B_SUFFIX))
slot_suffix = GPT_ENT_NAME_ANDROID_B_SUFFIX;
else
return VB2_ERROR_ANDROID_INVALID_SLOT_SUFFIX;
/*
* Check if the pvmfw buffer is zero sized
* (ie. pvmfw loading is not requested)
*/
if (params->pvmfw_buffer_size &&
GptFindEntryByName(gpt, GptPartitionNames[GPT_ANDROID_PVMFW], slot_suffix)) {
boot_partitions[partition_count++] = GptPartitionNames[GPT_ANDROID_PVMFW];
} else {
VB2_DEBUG("Not loading pvmfw: not requested or partition not present.\n");
params->pvmfw_out_size = 0;
}
/* If DTBO partition exists, include it in the list of partitions to be pre-loaded. */
if (GptFindEntryByName(gpt, GptPartitionNames[GPT_ANDROID_DTBO], slot_suffix))
boot_partitions[partition_count++] = GptPartitionNames[GPT_ANDROID_DTBO];
avb_ops = vboot_avb_ops_new(ctx, params, gpt, disk_handle, slot_suffix);
if (!avb_ops)
return VB2_ERROR_ANDROID_MEMORY_ALLOC;
avb_flags = AVB_SLOT_VERIFY_FLAGS_NONE;
if (!need_verification &&
!vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_USE_KEY_HASH))
avb_flags |= AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR;
VB2_ASSERT(boot_partitions[ARRAY_SIZE(boot_partitions) - 1] == NULL);
result = avb_slot_verify(avb_ops, boot_partitions, slot_suffix, avb_flags,
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
&verify_data);
if (result == AVB_SLOT_VERIFY_RESULT_OK) {
struct vb2_shared_data *sd = vb2_get_sd(ctx);
sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED;
}
/* Allow verification errors in dev mode unless DEV_USE_KEY_HASH is set */
if (!need_verification) {
switch (result) {
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
result = AVB_SLOT_VERIFY_RESULT_OK;
break;
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
if (!vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_USE_KEY_HASH))
result = AVB_SLOT_VERIFY_RESULT_OK;
break;
default:
break;
}
}
/* Map AVB return code into VB2 code */
rv = vb2_map_libavb_errors(result);
if (rv != VB2_SUCCESS)
goto out;
print_avb_slot_verify_data(verify_data);
rv = verify_avb_data(verify_data, avb_ops, slot_suffix, boot_partitions,
need_verification);
if (rv != VB2_SUCCESS)
goto out;
/* Fetch kernel version */
if (verify_data->rollback_indexes[0] & 0xFFFFFFFF00000000ULL) {
rv = VB2_ERROR_ANDROID_ROLLBACK_VERSION_RANGE;
goto out;
}
*kernel_version = (uint32_t)verify_data->rollback_indexes[0];
/*
* Before booting we need to rearrange buffers with partition data, which includes:
* - determine source of recovery ramdisk (dedicated 'recovery' or 'vendor_boot' for
* older images)
* - save bootconfig in separate buffer, so depthcharge can modify it
* - remove unused ramdisks depending on boot type (normal/recovery)
* - concatenate ramdisks from vendor_boot, optional recovery, & init_boot partitions
*/
bool dedicated_recovery = recovery_boot &&
avb_find_part(verify_data, GPT_ANDROID_RECOVERY) != NULL;
rv = rearrange_partitions(avb_ops, params, recovery_boot, dedicated_recovery);
if (rv)
goto out;
/*
* Use orange verifiedbootstate if OS wasn't verified (e.g. in developer mode) or
* when booting to recovery with GBB enabled fastboot to unlock all commands of
* fastbootd (normally when we boot to recovery with green flag, fastbootd would be
* locked).
*/
bool orange = !need_verification ||
(recovery_boot &&
vb2api_gbb_get_flags(ctx) & VB2_GBB_FLAG_FORCE_UNLOCK_FASTBOOT);
/*
* Try to find the microdroid vendor partition digest from the host vbmeta
* properties. If missing set to NULL.
*/
const char *microdroid_vendor_digest =
find_avb_prop(verify_data, MICRODROID_VENDOR_DIGEST_KEY);
params->microdroid_vendor_digest =
microdroid_vendor_digest ? strdup(microdroid_vendor_digest) : NULL;
/*
* TODO(b/335901799): Add support for marking verifiedbootstate yellow
*/
int chars = snprintf(params->bootconfig_cmdline_buffer, params->bootconfig_cmdline_size,
"%s %s=%s %s=%s %s=%s", verify_data->cmdline,
VERIFIED_BOOT_PROPERTY_NAME,
orange ? "orange" : "green",
SLOT_SUFFIX_BOOT_PROPERTY_NAME, slot_suffix,
ANDROID_FORCE_NORMAL_BOOT_PROPERTY_NAME, recovery_boot ? "0" : "1"
);
if (chars < 0 || chars >= params->bootconfig_cmdline_size) {
VB2_DEBUG("ERROR: Command line doesn't fit provided buffer: %s\n",
verify_data->cmdline);
rv = VB2_ERROR_ANDROID_CMDLINE_BUF_TOO_SMALL;
goto out;
}
rv = prepare_pvmfw(verify_data, params);
if (rv)
goto out;
rv = prepare_dtbo(verify_data, params);
out:
/* No need for slot data */
if (verify_data != NULL)
avb_slot_verify_data_free(verify_data);
if (avb_ops != NULL)
vboot_avb_ops_free(avb_ops);
return rv;
}