diff options
author | Joel Kitching <kitching@google.com> | 2021-04-28 16:50:21 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-09-24 12:25:24 +0000 |
commit | 13f601fbd4c1b128f333391e4552082594f0ff25 (patch) | |
tree | 4ae29d731f370835f790e8a571cc13d9c5e3025b /firmware | |
parent | 8aded7005e2830f54cf53b329946cbb1f11548f2 (diff) | |
download | vboot-13f601fbd4c1b128f333391e4552082594f0ff25.tar.gz |
vboot: boot from miniOS recovery kernels on diskstabilize-14249.B
Add VbTryLoadMiniOsKernel() to vboot API, which boots from a miniOS
recovery kernel located on internal disk. In this boot path, an attempt
is made to verify and boot this kernel. Recovery proceeds from within
the miniOS kernel by downloading a recovery image over the network. No
USB disk is used in the process.
For more information, see go/nbr-firmware.
BUG=b:188121855, b:186682292
TEST=make clean && make runtests
BRANCH=none
Change-Id: Ic4d1fe5642a2bf71c51c78fd7830ad2b6e9eebeb
Signed-off-by: Yu-Ping Wu <yupingso@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2856364
Reviewed-by: Julius Werner <jwerner@chromium.org>
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 6 | ||||
-rw-r--r-- | firmware/include/vboot_api.h | 15 | ||||
-rw-r--r-- | firmware/lib/include/load_kernel_fw.h | 16 | ||||
-rw-r--r-- | firmware/lib/vboot_api_kernel.c | 30 | ||||
-rw-r--r-- | firmware/lib/vboot_kernel.c | 177 |
5 files changed, 234 insertions, 10 deletions
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index 33cb7623..0dc7c441 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -617,6 +617,12 @@ enum vb2_return_code { /* Escape from NO_BOOT mode is detected */ VB2_ERROR_ESCAPE_NO_BOOT = 0x10080034, + /* + * Keyblock flags don't match current mode in + * vb2_load_kernel_keyblock(). + */ + VB2_ERROR_KERNEL_KEYBLOCK_MINIOS_FLAG = 0x10080035, + /********************************************************************** * API-level errors */ diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h index b7b0ce54..f19ee747 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -89,6 +89,21 @@ vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx, */ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags); +/** + * Attempt loading a miniOS kernel from internal disk. + * + * Scans sectors at the start and end of the disk, and looks for miniOS kernels + * starting at the beginning of the sector. Attempts loading any miniOS + * kernels found. + * + * If successful, sets lkp.disk_handle to the disk for the kernel and returns + * VB2_SUCCESS. + * + * @param ctx Vboot context + * @return VB2_SUCCESS or the most specific VB2_ERROR_LK error. + */ +vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx); + /*****************************************************************************/ /* Disk access (previously in boot_device.h) */ diff --git a/firmware/lib/include/load_kernel_fw.h b/firmware/lib/include/load_kernel_fw.h index 96c8f864..af454703 100644 --- a/firmware/lib/include/load_kernel_fw.h +++ b/firmware/lib/include/load_kernel_fw.h @@ -12,10 +12,11 @@ #include "vboot_api.h" /** - * Attempt to load the kernel from the current device. + * Attempt to load kernel from the specified device. * * @param ctx Vboot context * @param params Params specific to loading the kernel + * @param disk_info Disk from which to read kernel * * Returns VB2_SUCCESS if successful. If unsuccessful, returns an error code. */ @@ -23,4 +24,17 @@ vb2_error_t LoadKernel(struct vb2_context *ctx, VbSelectAndLoadKernelParams *params, VbDiskInfo *disk_info); +/** + * Attempt to load miniOS kernel from the specified device. + * + * @param ctx Vboot context + * @param params Params specific to loading the kernel + * @param disk_info Disk from which to read kernel + * + * Returns VB2_SUCCESS if successful. If unsuccessful, returns an error code. + */ +vb2_error_t LoadMiniOsKernel(struct vb2_context *ctx, + VbSelectAndLoadKernelParams *params, + VbDiskInfo *disk_info); + #endif /* VBOOT_REFERENCE_LOAD_KERNEL_FW_H_ */ diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index b79b26fd..2abd57eb 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -61,13 +61,14 @@ static int is_valid_disk(VbDiskInfo *info, uint32_t disk_flags) ((info->flags & VB_DISK_FLAG_SELECT_MASK) - 1)) == 0; } -test_mockable -vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags) +static vb2_error_t VbTryLoadKernelImpl(struct vb2_context *ctx, + uint32_t disk_flags, int minios) { vb2_error_t rv = VB2_ERROR_LK_NO_DISK_FOUND; VbDiskInfo* disk_info = NULL; uint32_t disk_count = 0; uint32_t i; + vb2_error_t new_rv; /* TODO: Should have been set by VbSelectAndLoadKernel. Remove when this global is no longer needed. */ @@ -91,11 +92,16 @@ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags) disk_info[i].flags); continue; } - kparams_ptr->disk_handle = disk_info[i].handle; - vb2_error_t new_rv = LoadKernel(ctx, kparams_ptr, - &disk_info[i]); - VB2_DEBUG("LoadKernel() = %#x\n", new_rv); + + if (minios) { + new_rv = LoadMiniOsKernel(ctx, kparams_ptr, + &disk_info[i]); + VB2_DEBUG("LoadMiniOsKernel() = %#x\n", new_rv); + } else { + new_rv = LoadKernel(ctx, kparams_ptr, &disk_info[i]); + VB2_DEBUG("LoadKernel() = %#x\n", new_rv); + } /* Stop now if we found a kernel. */ if (VB2_SUCCESS == new_rv) { @@ -133,6 +139,18 @@ vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags) return rv; } +test_mockable +vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags) +{ + return VbTryLoadKernelImpl(ctx, disk_flags, 0); +} + +test_mockable +vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx) +{ + return VbTryLoadKernelImpl(ctx, VB_DISK_FLAG_FIXED, 1); +} + vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx, VbSelectAndLoadKernelParams *kparams) { diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c index d4cca14e..34a8a426 100644 --- a/firmware/lib/vboot_kernel.c +++ b/firmware/lib/vboot_kernel.c @@ -20,6 +20,7 @@ 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 */ @@ -173,15 +174,17 @@ static vb2_error_t vb2_verify_kernel_dev_key_hash( /** * 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, - struct vb2_workbuf *wb) + uint32_t lpflags, struct vb2_workbuf *wb) { struct vb2_shared_data *sd = vb2_get_sd(ctx); @@ -250,6 +253,15 @@ static vb2_error_t vb2_verify_kernel_vblock( 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. */ enum vb2_boot_mode boot_mode = get_boot_mode(ctx); @@ -309,6 +321,29 @@ static vb2_error_t vb2_verify_kernel_vblock( 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. @@ -361,9 +396,8 @@ static vb2_error_t vb2_load_partition( } read_ms += vb2ex_mtime() - start_ts; - if (vb2_verify_kernel_vblock(ctx, kbuf, KBUF_SIZE, &wb)) { + 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; @@ -455,6 +489,143 @@ static vb2_error_t vb2_load_partition( return VB2_SUCCESS; } +static vb2_error_t try_minios_kernel(struct vb2_context *ctx, + VbSelectAndLoadKernelParams *params, + VbDiskInfo *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(params->disk_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, + VbSelectAndLoadKernelParams *params, + VbDiskInfo *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(params->disk_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, + VbSelectAndLoadKernelParams *params, + VbDiskInfo *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 LoadMiniOsKernel(struct vb2_context *ctx, + VbSelectAndLoadKernelParams *params, + VbDiskInfo *disk_info) +{ + vb2_error_t rv; + int end_region_first = vb2_nv_get(ctx, VB2_NV_MINIOS_PRIORITY); + + 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) + return rv; + + rv = vb2ex_tpm_set_mode(VB2_TPM_MODE_DISABLED); + if (rv) + VB2_DEBUG("Failed to disable TPM\n"); + + return rv; +} + vb2_error_t LoadKernel(struct vb2_context *ctx, VbSelectAndLoadKernelParams *params, VbDiskInfo *disk_info) |