summaryrefslogtreecommitdiff
path: root/firmware/2lib/2load_kernel.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/2lib/2load_kernel.c')
-rw-r--r--firmware/2lib/2load_kernel.c762
1 files changed, 762 insertions, 0 deletions
diff --git a/firmware/2lib/2load_kernel.c b/firmware/2lib/2load_kernel.c
new file mode 100644
index 00000000..65e40536
--- /dev/null
+++ b/firmware/2lib/2load_kernel.c
@@ -0,0 +1,762 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Functions for loading a kernel from disk.
+ * (Firmware portion)
+ */
+
+#include "2api.h"
+#include "2common.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+#include "2packed_key.h"
+#include "2secdata.h"
+#include "2sysincludes.h"
+#include "cgptlib.h"
+#include "cgptlib_internal.h"
+#include "gpt_misc.h"
+#include "vboot_api.h"
+
+enum vb2_load_partition_flags {
+ VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY = (1 << 0),
+ VB2_LOAD_PARTITION_FLAG_MINIOS = (1 << 1),
+};
+
+#define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */
+
+/* Minimum context work buffer size needed for vb2_load_partition() */
+#define VB2_LOAD_PARTITION_WORKBUF_BYTES \
+ (VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES + KBUF_SIZE)
+
+#define LOWEST_TPM_VERSION 0xffffffff
+
+/**
+ * Check if a valid keyblock is required.
+ *
+ * @param ctx Vboot context
+ * @return 1 if valid keyblock required (officially signed kernel);
+ * 0 if valid hash is enough (self-signed kernel).
+ */
+static int need_valid_keyblock(struct vb2_context *ctx)
+{
+ /* Normal and recovery modes always require official OS */
+ if (ctx->boot_mode != VB2_BOOT_MODE_DEVELOPER)
+ return 1;
+
+ /* FWMP can require developer mode to use signed kernels */
+ if (vb2_secdata_fwmp_get_flag(
+ ctx, VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY))
+ return 1;
+
+ /* Developers may require signed kernels */
+ if (vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Return a pointer to the keyblock inside a vblock.
+ *
+ * Must only be called during or after vb2_verify_kernel_vblock().
+ *
+ * @param kbuf Buffer containing vblock
+ * @return The keyblock pointer.
+ */
+static struct vb2_keyblock *get_keyblock(uint8_t *kbuf)
+{
+ return (struct vb2_keyblock *)kbuf;
+}
+
+/**
+ * Return a pointer to the kernel preamble inside a vblock.
+ *
+ * Must only be called during or after vb2_verify_kernel_vblock().
+ *
+ * @param kbuf Buffer containing vblock
+ * @return The kernel preamble pointer.
+ */
+static struct vb2_kernel_preamble *get_preamble(uint8_t *kbuf)
+{
+ return (struct vb2_kernel_preamble *)
+ (kbuf + get_keyblock(kbuf)->keyblock_size);
+}
+
+/**
+ * Return the offset of the kernel body from the start of the vblock.
+ *
+ * Must only be called during or after vb2_verify_kernel_vblock().
+ *
+ * @param kbuf Buffer containing vblock
+ * @return The offset of the kernel body from the vblock start, in bytes.
+ */
+static uint32_t get_body_offset(uint8_t *kbuf)
+{
+ return (get_keyblock(kbuf)->keyblock_size +
+ get_preamble(kbuf)->preamble_size);
+}
+
+/**
+ * Verify developer mode key hash.
+ *
+ * @param ctx Vboot context
+ * @param keyblock Keyblock to verify
+ * @return VB2_SUCCESS, or non-zero error code.
+ */
+static vb2_error_t vb2_verify_kernel_dev_key_hash(
+ struct vb2_context *ctx, struct vb2_keyblock *keyblock)
+{
+ struct vb2_packed_key *key = &keyblock->data_key;
+ uint8_t *buf = ((uint8_t *)key) + key->key_offset;
+ uint32_t buflen = key->key_size;
+ uint8_t digest[VB2_SHA256_DIGEST_SIZE];
+
+ VB2_DEBUG("Checking developer key hash.\n");
+ VB2_TRY(vb2_digest_buffer(buf, buflen, VB2_HASH_SHA256, digest,
+ sizeof(digest)));
+
+ uint8_t *fwmp_dev_key_hash =
+ vb2_secdata_fwmp_get_dev_key_hash(ctx);
+ if (fwmp_dev_key_hash == NULL) {
+ VB2_DEBUG("Couldn't retrieve developer key hash.\n");
+ return VB2_ERROR_KERNEL_KEYBLOCK_DEV_KEY_HASH;
+ }
+
+ if (vb2_safe_memcmp(digest, fwmp_dev_key_hash,
+ VB2_SHA256_DIGEST_SIZE)) {
+ int i;
+
+ VB2_DEBUG("Wrong developer key hash.\n");
+ VB2_DEBUG("Want: ");
+ for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++)
+ VB2_DEBUG_RAW("%02x ", fwmp_dev_key_hash[i]);
+ VB2_DEBUG_RAW("\n");
+ VB2_DEBUG("Got: ");
+ for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++)
+ VB2_DEBUG_RAW("%02x ", digest[i]);
+ VB2_DEBUG_RAW("\n");
+
+ return VB2_ERROR_KERNEL_KEYBLOCK_DEV_KEY_HASH;
+ }
+
+ return VB2_SUCCESS;
+}
+
+/**
+ * Verify a kernel vblock.
+ *
+ * @param ctx Vboot context
+ * @param kbuf Buffer containing the vblock
+ * @param kbuf_size Size of the buffer in bytes
+ * @param lpflags Flags (one or more of vb2_load_partition_flags)
+ * @param wb Work buffer. Must be at least
+ * VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES bytes.
+ * @return VB2_SUCCESS, or non-zero error code.
+ */
+static vb2_error_t vb2_verify_kernel_vblock(
+ struct vb2_context *ctx, uint8_t *kbuf, uint32_t kbuf_size,
+ uint32_t lpflags, struct vb2_workbuf *wb)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+ uint8_t *key_data;
+ uint32_t key_size;
+ struct vb2_public_key kernel_key;
+
+ int need_keyblock_valid = need_valid_keyblock(ctx);
+ int keyblock_valid = 1; /* Assume valid */
+
+ vb2_error_t rv;
+
+ /* Locate key to verify kernel. This will either be a recovery key, or
+ a kernel subkey passed from firmware verification. */
+ key_data = vb2_member_of(sd, sd->kernel_key_offset);
+ key_size = sd->kernel_key_size;
+ VB2_TRY(vb2_unpack_key_buffer(&kernel_key, key_data, key_size));
+
+ if (vb2_hwcrypto_allowed(ctx))
+ kernel_key.allow_hwcrypto = 1;
+
+ /*
+ * Clear any previous keyblock-valid flag (for example, from a previous
+ * kernel where the keyblock was signed but the preamble failed
+ * verification).
+ */
+ sd->flags &= ~VB2_SD_FLAG_KERNEL_SIGNED;
+
+ /* Verify the keyblock. */
+ struct vb2_keyblock *keyblock = get_keyblock(kbuf);
+ rv = vb2_verify_keyblock(keyblock, kbuf_size, &kernel_key, wb);
+ if (rv) {
+ VB2_DEBUG("Verifying keyblock signature failed.\n");
+ keyblock_valid = 0;
+
+ /* Check if we must have an officially signed kernel */
+ if (need_keyblock_valid) {
+ VB2_DEBUG("Self-signed kernels not enabled.\n");
+ return rv;
+ }
+
+ /* Otherwise, allow the kernel if the keyblock hash is valid */
+ rv = vb2_verify_keyblock_hash(keyblock, kbuf_size, wb);
+ if (rv) {
+ VB2_DEBUG("Verifying keyblock hash failed.\n");
+ return rv;
+ }
+ }
+
+ /* Check the keyblock flags against boot flags. */
+ if (!(keyblock->keyblock_flags &
+ ((ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) ?
+ VB2_KEYBLOCK_FLAG_DEVELOPER_1 :
+ VB2_KEYBLOCK_FLAG_DEVELOPER_0))) {
+ VB2_DEBUG("Keyblock developer flag mismatch.\n");
+ keyblock_valid = 0;
+ if (need_keyblock_valid)
+ return VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG;
+ }
+ if (!(keyblock->keyblock_flags &
+ ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE) ?
+ VB2_KEYBLOCK_FLAG_RECOVERY_1 :
+ VB2_KEYBLOCK_FLAG_RECOVERY_0))) {
+ VB2_DEBUG("Keyblock recovery flag mismatch.\n");
+ keyblock_valid = 0;
+ if (need_keyblock_valid)
+ return VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG;
+ }
+ if (!(keyblock->keyblock_flags &
+ ((lpflags & VB2_LOAD_PARTITION_FLAG_MINIOS) ?
+ VB2_KEYBLOCK_FLAG_MINIOS_1 :
+ VB2_KEYBLOCK_FLAG_MINIOS_0))) {
+ VB2_DEBUG("Keyblock miniOS flag mismatch.\n");
+ keyblock_valid = 0;
+ if (need_keyblock_valid)
+ return VB2_ERROR_KERNEL_KEYBLOCK_MINIOS_FLAG;
+ }
+
+ /* Check for rollback of key version except in recovery mode. */
+ uint32_t key_version = keyblock->data_key.key_version;
+ if (ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY) {
+ if (key_version < (sd->kernel_version_secdata >> 16)) {
+ keyblock_valid = 0;
+ if (need_keyblock_valid) {
+ VB2_DEBUG("Key version too old.\n");
+ return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK;
+ }
+ }
+ if (key_version > VB2_MAX_KEY_VERSION) {
+ /*
+ * Key version is stored in 16 bits in the TPM, so key
+ * versions greater than 0xFFFF can't be stored
+ * properly.
+ */
+ VB2_DEBUG("Key version > 0xFFFF.\n");
+ keyblock_valid = 0;
+ if (need_keyblock_valid)
+ return VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE;
+ }
+ }
+
+ /* If in developer mode and using key hash, check it. */
+ if (ctx->boot_mode == VB2_BOOT_MODE_DEVELOPER &&
+ vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_USE_KEY_HASH)) {
+ VB2_TRY(vb2_verify_kernel_dev_key_hash(ctx, keyblock));
+ }
+
+ /*
+ * At this point, we've checked everything. The kernel keyblock is at
+ * least self-consistent, and has either a valid signature or a valid
+ * hash. Track if it had a valid signature (that is, would we have
+ * been willing to boot it even if developer mode was off).
+ */
+ if (keyblock_valid)
+ sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED;
+
+ /* Get key for preamble verification from the keyblock. */
+ struct vb2_public_key data_key;
+ rv = vb2_unpack_key(&data_key, &keyblock->data_key);
+ if (rv) {
+ VB2_DEBUG("Unable to unpack kernel data key\n");
+ return rv;
+ }
+
+ /* Verify the preamble, which follows the keyblock */
+ struct vb2_kernel_preamble *preamble = get_preamble(kbuf);
+ rv = vb2_verify_kernel_preamble(preamble,
+ kbuf_size - keyblock->keyblock_size,
+ &data_key,
+ wb);
+ if (rv) {
+ VB2_DEBUG("Preamble verification failed.\n");
+ return rv;
+ }
+
+ /* Rollback check for miniOS */
+ if (need_keyblock_valid && (lpflags & VB2_LOAD_PARTITION_FLAG_MINIOS)) {
+ if (preamble->kernel_version <
+ (sd->kernel_version_secdata >> 24)) {
+ keyblock_valid = 0;
+ if (need_keyblock_valid) {
+ VB2_DEBUG("miniOS kernel version too old.\n");
+ return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK;
+ }
+ }
+ if (preamble->kernel_version > 0xff) {
+ /*
+ * Key version is stored in the top 8 bits of 16 bits
+ * in the TPM, so key versions greater than 0xFF can't
+ * be stored properly.
+ */
+ VB2_DEBUG("Key version > 0xFF.\n");
+ keyblock_valid = 0;
+ if (need_keyblock_valid)
+ return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE;
+ }
+ }
+
+ /*
+ * Kernel preamble version is the lower 16 bits of the composite
+ * kernel version.
+ */
+ if (preamble->kernel_version > VB2_MAX_PREAMBLE_VERSION)
+ return VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE;
+
+ /* Combine with the key version. */
+ sd->kernel_version = key_version << 16 | preamble->kernel_version;
+
+ /* If not in recovery mode, check for rollback of the kernel version. */
+ if (need_keyblock_valid &&
+ ctx->boot_mode != VB2_BOOT_MODE_MANUAL_RECOVERY &&
+ sd->kernel_version < sd->kernel_version_secdata) {
+ VB2_DEBUG("Kernel version too low.\n");
+ return VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK;
+ }
+
+ VB2_DEBUG("Kernel preamble is good.\n");
+ return VB2_SUCCESS;
+}
+
+/**
+ * Load and verify a partition from the stream.
+ *
+ * @param ctx Vboot context
+ * @param params Load-kernel parameters
+ * @param stream Stream to load kernel from
+ * @param lpflags Flags (one or more of vb2_load_partition_flags)
+ * @return VB2_SUCCESS, or non-zero error code.
+ */
+static vb2_error_t vb2_load_partition(
+ struct vb2_context *ctx, struct vb2_kernel_params *params,
+ VbExStream_t stream, uint32_t lpflags)
+{
+ uint32_t read_ms = 0, start_ts;
+ struct vb2_workbuf wb;
+
+ vb2_workbuf_from_ctx(ctx, &wb);
+
+ /* Allocate kernel header buffer in workbuf */
+ uint8_t *kbuf = vb2_workbuf_alloc(&wb, KBUF_SIZE);
+ if (!kbuf)
+ return VB2_ERROR_LOAD_PARTITION_WORKBUF;
+
+ start_ts = vb2ex_mtime();
+ if (VbExStreamRead(stream, KBUF_SIZE, kbuf)) {
+ VB2_DEBUG("Unable to read start of partition.\n");
+ return VB2_ERROR_LOAD_PARTITION_READ_VBLOCK;
+ }
+ read_ms += vb2ex_mtime() - start_ts;
+
+ if (vb2_verify_kernel_vblock(ctx, kbuf, KBUF_SIZE, lpflags, &wb))
+ return VB2_ERROR_LOAD_PARTITION_VERIFY_VBLOCK;
+
+ if (lpflags & VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY)
+ return VB2_SUCCESS;
+
+ struct vb2_keyblock *keyblock = get_keyblock(kbuf);
+ struct vb2_kernel_preamble *preamble = get_preamble(kbuf);
+
+ /*
+ * Make sure the kernel starts at or before what we already read into
+ * kbuf.
+ *
+ * We could deal with a larger offset by reading and discarding the
+ * data in between the vblock and the kernel data.
+ */
+ uint32_t body_offset = get_body_offset(kbuf);
+ if (body_offset > KBUF_SIZE) {
+ VB2_DEBUG("Kernel body offset is %u > 64KB.\n", body_offset);
+ return VB2_ERROR_LOAD_PARTITION_BODY_OFFSET;
+ }
+
+ uint8_t *kernbuf = params->kernel_buffer;
+ uint32_t kernbuf_size = params->kernel_buffer_size;
+ if (!kernbuf) {
+ /* Get kernel load address and size from the header. */
+ kernbuf = (uint8_t *)((long)preamble->body_load_address);
+ kernbuf_size = preamble->body_signature.data_size;
+ } else if (preamble->body_signature.data_size > kernbuf_size) {
+ VB2_DEBUG("Kernel body doesn't fit in memory.\n");
+ return VB2_ERROR_LOAD_PARTITION_BODY_SIZE;
+ }
+
+ uint32_t body_toread = preamble->body_signature.data_size;
+ uint8_t *body_readptr = kernbuf;
+
+ /*
+ * If we've already read part of the kernel, copy that to the beginning
+ * of the kernel buffer.
+ */
+ uint32_t body_copied = KBUF_SIZE - body_offset;
+ if (body_copied > body_toread)
+ body_copied = body_toread; /* Don't over-copy tiny kernel */
+ memcpy(body_readptr, kbuf + body_offset, body_copied);
+ body_toread -= body_copied;
+ body_readptr += body_copied;
+
+ /* Read the kernel data */
+ start_ts = vb2ex_mtime();
+ if (body_toread && VbExStreamRead(stream, body_toread, body_readptr)) {
+ VB2_DEBUG("Unable to read kernel data.\n");
+ return VB2_ERROR_LOAD_PARTITION_READ_BODY;
+ }
+ read_ms += vb2ex_mtime() - start_ts;
+ if (read_ms == 0) /* Avoid division by 0 in speed calculation */
+ read_ms = 1;
+ VB2_DEBUG("read %u KB in %u ms at %u KB/s.\n",
+ (body_toread + KBUF_SIZE) / 1024, read_ms,
+ (uint32_t)(((body_toread + KBUF_SIZE) * VB2_MSEC_PER_SEC) /
+ (read_ms * 1024)));
+
+ /* Get key for preamble/data verification from the keyblock. */
+ struct vb2_public_key data_key;
+ if (vb2_unpack_key(&data_key, &keyblock->data_key)) {
+ VB2_DEBUG("Unable to unpack kernel data key\n");
+ return VB2_ERROR_LOAD_PARTITION_DATA_KEY;
+ }
+
+ if (vb2_hwcrypto_allowed(ctx))
+ data_key.allow_hwcrypto = 1;
+
+ /* Verify kernel data */
+ if (vb2_verify_data(kernbuf, kernbuf_size, &preamble->body_signature,
+ &data_key, &wb)) {
+ VB2_DEBUG("Kernel data verification failed.\n");
+ return VB2_ERROR_LOAD_PARTITION_VERIFY_BODY;
+ }
+
+ /* If we're still here, the kernel is valid */
+ VB2_DEBUG("Partition is good.\n");
+
+ /* Save kernel data back to parameters */
+ params->bootloader_address = preamble->bootloader_address;
+ params->bootloader_size = preamble->bootloader_size;
+ params->flags = vb2_kernel_get_flags(preamble);
+ if (!params->kernel_buffer) {
+ params->kernel_buffer = kernbuf;
+ params->kernel_buffer_size = kernbuf_size;
+ }
+
+ return VB2_SUCCESS;
+}
+
+static vb2_error_t try_minios_kernel(struct vb2_context *ctx,
+ struct vb2_kernel_params *params,
+ struct vb2_disk_info *disk_info,
+ uint64_t sector) {
+ VbExStream_t stream;
+ uint64_t sectors_left = disk_info->lba_count - sector;
+ const uint32_t lpflags = VB2_LOAD_PARTITION_FLAG_MINIOS;
+ vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND;
+
+ /* Re-open stream at correct offset to pass to vb2_load_partition. */
+ if (VbExStreamOpen(disk_info->handle, sector, sectors_left,
+ &stream)) {
+ VB2_DEBUG("Unable to open disk handle.\n");
+ return rv;
+ }
+
+ rv = vb2_load_partition(ctx, params, stream, lpflags);
+ VB2_DEBUG("vb2_load_partition returned: %d\n", rv);
+
+ VbExStreamClose(stream);
+
+ if (rv)
+ return VB2_ERROR_LK_NO_KERNEL_FOUND;
+ return rv;
+}
+
+static vb2_error_t try_minios_sectors(struct vb2_context *ctx,
+ struct vb2_kernel_params *params,
+ struct vb2_disk_info *disk_info,
+ uint64_t start, uint64_t count)
+{
+ const uint32_t buf_size = count * disk_info->bytes_per_lba;
+ char *buf;
+ VbExStream_t stream;
+ uint64_t isector;
+ vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND;
+
+ buf = malloc(buf_size);
+ if (buf == NULL) {
+ VB2_DEBUG("Unable to allocate disk read buffer.\n");
+ return rv;
+ }
+
+ if (VbExStreamOpen(disk_info->handle, start, count, &stream)) {
+ VB2_DEBUG("Unable to open disk handle.\n");
+ free(buf);
+ return rv;
+ }
+ if (VbExStreamRead(stream, buf_size, buf)) {
+ VB2_DEBUG("Unable to read disk.\n");
+ free(buf);
+ VbExStreamClose(stream);
+ return rv;
+ }
+ VbExStreamClose(stream);
+
+ for (isector = 0; isector < count; isector++) {
+ if (memcmp(buf + isector * disk_info->bytes_per_lba,
+ VB2_KEYBLOCK_MAGIC, VB2_KEYBLOCK_MAGIC_SIZE))
+ continue;
+ VB2_DEBUG("Match on sector %" PRIu64 " / %" PRIu64 "\n",
+ start + isector,
+ disk_info->lba_count - 1);
+ rv = try_minios_kernel(ctx, params, disk_info, start + isector);
+ if (rv == VB2_SUCCESS)
+ break;
+ }
+
+ free(buf);
+ return rv;
+}
+
+static vb2_error_t try_minios_sector_region(struct vb2_context *ctx,
+ struct vb2_kernel_params *params,
+ struct vb2_disk_info *disk_info,
+ int end_region)
+{
+ const uint64_t disk_count_half = (disk_info->lba_count + 1) / 2;
+ const uint64_t check_count_256 = 256 * 1024
+ * 1024 / disk_info->bytes_per_lba; // 256 MB
+ const uint64_t batch_count_1 = 1024
+ * 1024 / disk_info->bytes_per_lba; // 1 MB
+ const uint64_t check_count = VB2_MIN(disk_count_half, check_count_256);
+ const uint64_t batch_count = VB2_MIN(disk_count_half, batch_count_1);
+ uint64_t sector;
+ uint64_t start;
+ uint64_t end;
+ const char *region_name;
+ vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND;
+
+ if (!end_region) {
+ start = 0;
+ end = check_count;
+ region_name = "start";
+ } else {
+ start = disk_info->lba_count - check_count;
+ end = disk_info->lba_count;
+ region_name = "end";
+ }
+
+ VB2_DEBUG("Checking %s of disk for kernels...\n", region_name);
+ for (sector = start; sector < end; sector += batch_count) {
+ rv = try_minios_sectors(ctx, params, disk_info, sector,
+ batch_count);
+ if (rv == VB2_SUCCESS)
+ return rv;
+ }
+
+ return rv;
+}
+
+/*
+ * Search for kernels by sector, rather than by partition. Only sectors near
+ * the start and end of disks are considered, and the kernel must start exactly
+ * at the first byte of the sector.
+ */
+vb2_error_t vb2api_load_minios_kernel(struct vb2_context *ctx,
+ struct vb2_kernel_params *params,
+ struct vb2_disk_info *disk_info,
+ uint32_t minios_flags)
+{
+ vb2_error_t rv;
+ int end_region_first = vb2_nv_get(ctx, VB2_NV_MINIOS_PRIORITY);
+
+ if (minios_flags & VB2_MINIOS_FLAG_NON_ACTIVE)
+ rv = VB2_ERROR_UNKNOWN; /* Ignore active partition */
+ else
+ rv = try_minios_sector_region(ctx, params, disk_info,
+ end_region_first);
+
+ if (rv)
+ rv = try_minios_sector_region(ctx, params, disk_info,
+ !end_region_first);
+
+ if (rv == VB2_SUCCESS)
+ params->disk_handle = disk_info->handle;
+
+ return rv;
+}
+
+vb2_error_t vb2api_load_kernel(struct vb2_context *ctx,
+ struct vb2_kernel_params *params,
+ struct vb2_disk_info *disk_info)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ int found_partitions = 0;
+ uint32_t lowest_version = LOWEST_TPM_VERSION;
+ vb2_error_t rv;
+
+ /* Clear output params */
+ params->partition_number = 0;
+
+ /* Read GPT data */
+ GptData gpt;
+ gpt.sector_bytes = (uint32_t)disk_info->bytes_per_lba;
+ gpt.streaming_drive_sectors = disk_info->streaming_lba_count
+ ?: disk_info->lba_count;
+ gpt.gpt_drive_sectors = disk_info->lba_count;
+ gpt.flags = disk_info->flags & VB2_DISK_FLAG_EXTERNAL_GPT
+ ? GPT_FLAG_EXTERNAL : 0;
+ if (AllocAndReadGptData(disk_info->handle, &gpt)) {
+ VB2_DEBUG("Unable to read GPT data\n");
+ goto gpt_done;
+ }
+
+ /* Initialize GPT library */
+ if (GptInit(&gpt)) {
+ VB2_DEBUG("Error parsing GPT\n");
+ goto gpt_done;
+ }
+
+ /* Loop over candidate kernel partitions */
+ uint64_t part_start, part_size;
+ while (GptNextKernelEntry(&gpt, &part_start, &part_size) ==
+ GPT_SUCCESS) {
+
+ VB2_DEBUG("Found kernel entry at %"
+ PRIu64 " size %" PRIu64 "\n",
+ part_start, part_size);
+
+ /* Found at least one kernel partition. */
+ found_partitions++;
+
+ /* Set up the stream */
+ VbExStream_t stream = NULL;
+ if (VbExStreamOpen(disk_info->handle,
+ part_start, part_size, &stream)) {
+ VB2_DEBUG("Partition error getting stream.\n");
+ VB2_DEBUG("Marking kernel as invalid.\n");
+ GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD);
+ continue;
+ }
+
+ uint32_t lpflags = 0;
+ if (params->partition_number > 0) {
+ /*
+ * If we already have a good kernel, we only needed to
+ * look at the vblock versions to check for rollback.
+ */
+ lpflags |= VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY;
+ }
+
+ rv = vb2_load_partition(ctx, params, stream, lpflags);
+ VbExStreamClose(stream);
+
+ if (rv) {
+ VB2_DEBUG("Marking kernel as invalid (err=%x).\n", rv);
+ GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD);
+ continue;
+ }
+
+ int keyblock_valid = sd->flags & VB2_SD_FLAG_KERNEL_SIGNED;
+ /* Track lowest version from a valid header. */
+ if (keyblock_valid && lowest_version > sd->kernel_version) {
+ lowest_version = sd->kernel_version;
+ }
+ VB2_DEBUG("Keyblock valid: %d\n", keyblock_valid);
+ VB2_DEBUG("Combined version: %u\n", sd->kernel_version);
+
+ /*
+ * If we're only looking at headers, we're done with this
+ * partition.
+ */
+ if (lpflags & VB2_LOAD_PARTITION_FLAG_VBLOCK_ONLY)
+ continue;
+
+ /*
+ * Otherwise, we found a partition we like.
+ *
+ * TODO: GPT partitions start at 1, but cgptlib starts them at
+ * 0. Adjust here, until cgptlib is fixed.
+ */
+ params->partition_number = gpt.current_kernel + 1;
+
+ /*
+ * TODO: GetCurrentKernelUniqueGuid() should take a destination
+ * size, or the dest should be a struct, so we know it's big
+ * enough.
+ */
+ GetCurrentKernelUniqueGuid(&gpt, &params->partition_guid);
+
+ /* Update GPT to note this is the kernel we're trying.
+ * But not when we assume that the boot process may
+ * not complete for valid reasons (eg. early shutdown).
+ */
+ if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT))
+ GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY);
+
+ /*
+ * If we're in recovery mode or we're about to boot a
+ * non-officially-signed kernel, there's no rollback
+ * protection, so we can stop at the first valid kernel.
+ */
+ if (ctx->boot_mode == VB2_BOOT_MODE_MANUAL_RECOVERY ||
+ !keyblock_valid) {
+ VB2_DEBUG("In recovery mode or dev-signed kernel\n");
+ break;
+ }
+
+ /*
+ * Otherwise, we do care about the key index in the TPM. If
+ * the good partition's key version is the same as the tpm,
+ * then the TPM doesn't need updating; we can stop now.
+ * Otherwise, we'll check all the other headers to see if they
+ * contain a newer key.
+ */
+ if (sd->kernel_version == sd->kernel_version_secdata) {
+ VB2_DEBUG("Same kernel version\n");
+ break;
+ }
+ } /* while(GptNextKernelEntry) */
+
+ gpt_done:
+ /* Write and free GPT data */
+ WriteAndFreeGptData(disk_info->handle, &gpt);
+
+ /* Handle finding a good partition */
+ if (params->partition_number > 0) {
+ VB2_DEBUG("Good partition %d\n", params->partition_number);
+ /*
+ * Validity check - only store a new TPM version if we found
+ * one. If lowest_version is still at its initial value, we
+ * didn't find one; for example, we're in developer mode and
+ * just didn't look.
+ */
+ if (lowest_version != LOWEST_TPM_VERSION &&
+ lowest_version > sd->kernel_version_secdata)
+ sd->kernel_version = lowest_version;
+
+ /* Success! */
+ rv = VB2_SUCCESS;
+ params->disk_handle = disk_info->handle;
+ } else if (found_partitions > 0) {
+ rv = VB2_ERROR_LK_INVALID_KERNEL_FOUND;
+ } else {
+ rv = VB2_ERROR_LK_NO_KERNEL_FOUND;
+ }
+
+ return rv;
+}