| /* 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 "2common.h" |
| #include "2misc.h" |
| #include "2nvstorage.h" |
| #include "2rsa.h" |
| #include "2sha.h" |
| #include "2secdata.h" |
| #include "2sysincludes.h" |
| #include "cgptlib.h" |
| #include "cgptlib_internal.h" |
| #include "gpt_misc.h" |
| #include "load_kernel_fw.h" |
| #include "vb2_common.h" |
| #include "vboot_api.h" |
| #include "vboot_kernel.h" |
| #include "vboot_struct.h" |
| |
| #define LOWEST_TPM_VERSION 0xffffffff |
| |
| enum vboot_mode { |
| kBootRecovery = 0, /* Recovery firmware, any dev switch position */ |
| kBootNormal = 1, /* Normal boot - kernel must be verified */ |
| kBootDev = 2 /* Developer boot - self-signed kernel ok */ |
| }; |
| |
| /** |
| * Return the boot mode based on the parameters. |
| * |
| * @param params Load kernel parameters |
| * @return The current boot mode. |
| */ |
| static enum vboot_mode get_kernel_boot_mode(struct vb2_context *ctx) |
| { |
| if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) |
| return kBootRecovery; |
| |
| if (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE) |
| return kBootDev; |
| |
| return kBootNormal; |
| }; |
| |
| /** |
| * Check if the parameters require an officially signed OS. |
| * |
| * @param params Load kernel parameters |
| * @return 1 if official OS required; 0 if self-signed kernels are ok |
| */ |
| static int require_official_os(struct vb2_context *ctx, |
| const LoadKernelParams *params) |
| { |
| /* Normal and recovery modes always require official OS */ |
| if (get_kernel_boot_mode(ctx) != kBootDev) |
| return 1; |
| |
| /* FWMP can require developer mode to use official OS */ |
| if (vb2_secdata_fwmp_get_flag( |
| ctx, VB2_SECDATA_FWMP_DEV_ENABLE_OFFICIAL_ONLY)) |
| return 1; |
| |
| /* Developer can request official OS via nvstorage */ |
| return vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY); |
| } |
| |
| /** |
| * 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 a kernel vblock. |
| * |
| * @param kbuf Buffer containing the vblock |
| * @param kbuf_size Size of the buffer in bytes |
| * @param kernel_subkey Packed kernel subkey to use in validating keyblock |
| * @param params Load kernel parameters |
| * @param min_version Minimum kernel version |
| * @param shpart Destination for verification results |
| * @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, |
| const struct vb2_packed_key *kernel_subkey, |
| const LoadKernelParams *params, uint32_t min_version, |
| VbSharedDataKernelPart *shpart, struct vb2_workbuf *wb) |
| { |
| /* Unpack kernel subkey */ |
| struct vb2_public_key kernel_subkey2; |
| if (VB2_SUCCESS != vb2_unpack_key(&kernel_subkey2, kernel_subkey)) { |
| VB2_DEBUG("Unable to unpack kernel subkey\n"); |
| return VB2_ERROR_VBLOCK_KERNEL_SUBKEY; |
| } |
| |
| /* Verify the keyblock. */ |
| int keyblock_valid = 1; /* Assume valid */ |
| struct vb2_keyblock *keyblock = get_keyblock(kbuf); |
| if (VB2_SUCCESS != vb2_verify_keyblock(keyblock, kbuf_size, |
| &kernel_subkey2, wb)) { |
| VB2_DEBUG("Verifying keyblock signature failed.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_KEYBLOCK_SIG; |
| keyblock_valid = 0; |
| |
| /* Check if we must have an officially signed kernel */ |
| if (require_official_os(ctx, params)) { |
| VB2_DEBUG("Self-signed kernels not enabled.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_SELF_SIGNED; |
| return VB2_ERROR_VBLOCK_SELF_SIGNED; |
| } |
| |
| /* Otherwise, allow the kernel if the keyblock hash is valid */ |
| if (VB2_SUCCESS != |
| vb2_verify_keyblock_hash(keyblock, kbuf_size, wb)) { |
| VB2_DEBUG("Verifying keyblock hash failed.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_KEYBLOCK_HASH; |
| return VB2_ERROR_VBLOCK_KEYBLOCK_HASH; |
| } |
| } |
| |
| /* 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"); |
| shpart->check_result = VBSD_LKP_CHECK_DEV_MISMATCH; |
| keyblock_valid = 0; |
| } |
| 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"); |
| shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH; |
| keyblock_valid = 0; |
| } |
| |
| /* Check for rollback of key version except in recovery mode. */ |
| enum vboot_mode boot_mode = get_kernel_boot_mode(ctx); |
| uint32_t key_version = keyblock->data_key.key_version; |
| if (kBootRecovery != boot_mode) { |
| if (key_version < (min_version >> 16)) { |
| VB2_DEBUG("Key version too old.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK; |
| keyblock_valid = 0; |
| } |
| if (key_version > 0xFFFF) { |
| /* |
| * 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"); |
| shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK; |
| keyblock_valid = 0; |
| } |
| } |
| |
| /* If not in developer mode, keyblock required to be valid. */ |
| if (kBootDev != boot_mode && !keyblock_valid) { |
| VB2_DEBUG("Keyblock is invalid.\n"); |
| return VB2_ERROR_VBLOCK_KEYBLOCK; |
| } |
| |
| /* If in developer mode and using key hash, check it */ |
| if ((kBootDev == boot_mode) && |
| vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_USE_KEY_HASH)) { |
| 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_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_VBLOCK_DEV_KEY_HASH; |
| } |
| |
| if (0 != 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("%02x", fwmp_dev_key_hash[i]); |
| VB2_DEBUG("\nGot: "); |
| for (i = 0; i < VB2_SHA256_DIGEST_SIZE; i++) |
| VB2_DEBUG("%02x", digest[i]); |
| VB2_DEBUG("\n"); |
| |
| return VB2_ERROR_VBLOCK_DEV_KEY_HASH; |
| } |
| } |
| |
| /* Get key for preamble verification from the keyblock. */ |
| struct vb2_public_key data_key; |
| if (VB2_SUCCESS != vb2_unpack_key(&data_key, &keyblock->data_key)) { |
| VB2_DEBUG("Unable to unpack kernel data key\n"); |
| shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE; |
| return VB2_ERROR_UNKNOWN; |
| } |
| |
| /* Verify the preamble, which follows the keyblock */ |
| struct vb2_kernel_preamble *preamble = get_preamble(kbuf); |
| if (VB2_SUCCESS != |
| vb2_verify_kernel_preamble(preamble, |
| kbuf_size - keyblock->keyblock_size, |
| &data_key, |
| wb)) { |
| VB2_DEBUG("Preamble verification failed.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE; |
| return VB2_ERROR_UNKNOWN; |
| } |
| |
| /* |
| * If the keyblock is valid and we're not in recovery mode, check for |
| * rollback of the kernel version. |
| */ |
| uint32_t combined_version = (key_version << 16) | |
| (preamble->kernel_version & 0xFFFF); |
| shpart->combined_version = combined_version; |
| if (keyblock_valid && kBootRecovery != boot_mode) { |
| if (combined_version < min_version) { |
| VB2_DEBUG("Kernel version too low.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_KERNEL_ROLLBACK; |
| /* |
| * If not in developer mode, kernel version |
| * must be valid. |
| */ |
| if (kBootDev != boot_mode) |
| return VB2_ERROR_UNKNOWN; |
| } |
| } |
| |
| VB2_DEBUG("Kernel preamble is good.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID; |
| if (keyblock_valid) |
| shpart->flags |= VBSD_LKP_FLAG_KEYBLOCK_VALID; |
| |
| return VB2_SUCCESS; |
| } |
| |
| enum vb2_load_partition_flags { |
| /* Only check the vblock to */ |
| VB2_LOAD_PARTITION_VBLOCK_ONLY = (1 << 0), |
| }; |
| |
| #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) |
| |
| /** |
| * Load and verify a partition from the stream. |
| * |
| * @param ctx Vboot context |
| * @param stream Stream to load kernel from |
| * @param kernel_subkey Key to use to verify vblock |
| * @param flags Flags (one or more of vb2_load_partition_flags) |
| * @param params Load-kernel parameters |
| * @param min_version Minimum kernel version from TPM |
| * @param shpart Destination for verification results |
| * @param wb Workbuf for data storage |
| * @return VB2_SUCCESS, or non-zero error code. |
| */ |
| static vb2_error_t vb2_load_partition( |
| struct vb2_context *ctx, VbExStream_t stream, |
| const struct vb2_packed_key *kernel_subkey, uint32_t flags, |
| LoadKernelParams *params, uint32_t min_version, |
| VbSharedDataKernelPart *shpart, struct vb2_workbuf *wb) |
| { |
| uint32_t read_ms = 0, start_ts; |
| struct vb2_workbuf wblocal = *wb; |
| |
| /* Allocate kernel header buffer in workbuf */ |
| uint8_t *kbuf = vb2_workbuf_alloc(&wblocal, 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"); |
| shpart->check_result = VBSD_LKP_CHECK_READ_START; |
| return VB2_ERROR_LOAD_PARTITION_READ_VBLOCK; |
| } |
| read_ms += vb2ex_mtime() - start_ts; |
| |
| if (VB2_SUCCESS != |
| vb2_verify_kernel_vblock(ctx, kbuf, KBUF_SIZE, kernel_subkey, |
| params, min_version, shpart, &wblocal)) { |
| return VB2_ERROR_LOAD_PARTITION_VERIFY_VBLOCK; |
| } |
| |
| if (flags & VB2_LOAD_PARTITION_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) { |
| shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET; |
| 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"); |
| shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_MEM; |
| 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"); |
| shpart->check_result = VBSD_LKP_CHECK_READ_DATA; |
| 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_SUCCESS != vb2_unpack_key(&data_key, &keyblock->data_key)) { |
| VB2_DEBUG("Unable to unpack kernel data key\n"); |
| shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE; |
| return VB2_ERROR_LOAD_PARTITION_DATA_KEY; |
| } |
| |
| /* Verify kernel data */ |
| if (VB2_SUCCESS != vb2_verify_data(kernbuf, kernbuf_size, |
| &preamble->body_signature, |
| &data_key, &wblocal)) { |
| VB2_DEBUG("Kernel data verification failed.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA; |
| return VB2_ERROR_LOAD_PARTITION_VERIFY_BODY; |
| } |
| |
| /* If we're still here, the kernel is valid */ |
| VB2_DEBUG("Partition is good.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_KERNEL_GOOD; |
| |
| /* 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; |
| } |
| |
| vb2_error_t LoadKernel(struct vb2_context *ctx, LoadKernelParams *params) |
| { |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); |
| struct vb2_workbuf wb; |
| VbSharedDataKernelCall shcall; |
| int found_partitions = 0; |
| uint32_t lowest_version = LOWEST_TPM_VERSION; |
| vb2_error_t rv; |
| |
| vb2_workbuf_from_ctx(ctx, &wb); |
| |
| /* Clear output params in case we fail */ |
| params->partition_number = 0; |
| params->bootloader_address = 0; |
| params->bootloader_size = 0; |
| params->flags = 0; |
| |
| /* |
| * Set up tracking for this call. This wraps around if called many |
| * times, so we need to initialize the call entry each time. |
| */ |
| memset(&shcall, 0, sizeof(shcall)); |
| shcall.boot_flags = (uint32_t)params->boot_flags; |
| shcall.boot_mode = get_kernel_boot_mode(ctx); |
| shcall.sector_size = (uint32_t)params->bytes_per_lba; |
| shcall.sector_count = params->streaming_lba_count; |
| |
| /* Locate key to verify kernel. This will either be a recovery key, or |
| a kernel subkey passed from firmware verification. */ |
| struct vb2_packed_key *kernel_subkey = |
| vb2_member_of(sd, sd->kernel_key_offset); |
| |
| /* Read GPT data */ |
| GptData gpt; |
| gpt.sector_bytes = (uint32_t)params->bytes_per_lba; |
| gpt.streaming_drive_sectors = params->streaming_lba_count; |
| gpt.gpt_drive_sectors = params->gpt_lba_count; |
| gpt.flags = params->boot_flags & BOOT_FLAG_EXTERNAL_GPT |
| ? GPT_FLAG_EXTERNAL : 0; |
| if (0 != AllocAndReadGptData(params->disk_handle, &gpt)) { |
| VB2_DEBUG("Unable to read GPT data\n"); |
| shcall.check_result = VBSD_LKC_CHECK_GPT_READ_ERROR; |
| goto gpt_done; |
| } |
| |
| /* Initialize GPT library */ |
| if (GPT_SUCCESS != GptInit(&gpt)) { |
| VB2_DEBUG("Error parsing GPT\n"); |
| shcall.check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR; |
| goto gpt_done; |
| } |
| |
| /* Loop over candidate kernel partitions */ |
| uint64_t part_start, part_size; |
| while (GPT_SUCCESS == |
| GptNextKernelEntry(&gpt, &part_start, &part_size)) { |
| |
| VB2_DEBUG("Found kernel entry at %" |
| PRIu64 " size %" PRIu64 "\n", |
| part_start, part_size); |
| |
| /* |
| * Set up tracking for this partition. This wraps around if |
| * called many times, so initialize the partition entry each |
| * time. |
| */ |
| VbSharedDataKernelPart *shpart = |
| shcall.parts + (shcall.kernel_parts_found |
| & (VBSD_MAX_KERNEL_PARTS - 1)); |
| memset(shpart, 0, sizeof(VbSharedDataKernelPart)); |
| shpart->sector_start = part_start; |
| shpart->sector_count = part_size; |
| /* |
| * TODO: GPT partitions start at 1, but cgptlib starts them at |
| * 0. Adjust here, until cgptlib is fixed. |
| */ |
| shpart->gpt_index = (uint8_t)(gpt.current_kernel + 1); |
| shcall.kernel_parts_found++; |
| |
| /* Found at least one kernel partition. */ |
| found_partitions++; |
| |
| /* Set up the stream */ |
| VbExStream_t stream = NULL; |
| if (VbExStreamOpen(params->disk_handle, |
| part_start, part_size, &stream)) { |
| VB2_DEBUG("Partition error getting stream.\n"); |
| shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL; |
| 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_VBLOCK_ONLY; |
| } |
| |
| rv = vb2_load_partition(ctx, |
| stream, |
| kernel_subkey, |
| lpflags, |
| params, |
| sd->kernel_version, |
| shpart, |
| &wb); |
| VbExStreamClose(stream); |
| |
| if (rv != VB2_SUCCESS) { |
| VB2_DEBUG("Marking kernel as invalid.\n"); |
| GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); |
| continue; |
| } |
| |
| int keyblock_valid = (shpart->flags & |
| VBSD_LKP_FLAG_KEYBLOCK_VALID); |
| if (keyblock_valid) { |
| sd->flags |= VB2_SD_FLAG_KERNEL_SIGNED; |
| /* Track lowest version from a valid header. */ |
| if (lowest_version > shpart->combined_version) |
| lowest_version = shpart->combined_version; |
| } |
| VB2_DEBUG("Keyblock valid: %d\n", keyblock_valid); |
| VB2_DEBUG("Combined version: %u\n", shpart->combined_version); |
| |
| /* |
| * If we're only looking at headers, we're done with this |
| * partition. |
| */ |
| if (lpflags & VB2_LOAD_PARTITION_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, ¶ms->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 (kBootRecovery == shcall.boot_mode || !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 (shpart->combined_version == sd->kernel_version) { |
| VB2_DEBUG("Same kernel version\n"); |
| break; |
| } |
| } /* while(GptNextKernelEntry) */ |
| |
| gpt_done: |
| /* Write and free GPT data */ |
| WriteAndFreeGptData(params->disk_handle, &gpt); |
| |
| /* Handle finding a good partition */ |
| if (params->partition_number > 0) { |
| VB2_DEBUG("Good partition %d\n", params->partition_number); |
| shcall.check_result = VBSD_LKC_CHECK_GOOD_PARTITION; |
| /* |
| * 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) |
| sd->kernel_version = lowest_version; |
| |
| /* Success! */ |
| rv = VB2_SUCCESS; |
| } else if (found_partitions > 0) { |
| shcall.check_result = VBSD_LKC_CHECK_INVALID_PARTITIONS; |
| rv = VB2_ERROR_LK_INVALID_KERNEL_FOUND; |
| } else { |
| shcall.check_result = VBSD_LKC_CHECK_NO_PARTITIONS; |
| rv = VB2_ERROR_LK_NO_KERNEL_FOUND; |
| } |
| |
| shcall.return_code = (uint8_t)rv; |
| return rv; |
| } |