summaryrefslogtreecommitdiff
path: root/firmware/2lib/2kernel.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/2lib/2kernel.c')
-rw-r--r--firmware/2lib/2kernel.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/firmware/2lib/2kernel.c b/firmware/2lib/2kernel.c
new file mode 100644
index 00000000..f49b6dea
--- /dev/null
+++ b/firmware/2lib/2kernel.c
@@ -0,0 +1,193 @@
+/* Copyright 2020 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.
+ *
+ * Kernel selection, loading, verification, and booting.
+ */
+
+#include "2common.h"
+#include "2kernel.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+#include "2rsa.h"
+#include "2secdata.h"
+#include "vb2_common.h"
+#include "vboot_kernel.h"
+
+/**
+ * Reset any NVRAM requests.
+ *
+ * @param ctx Vboot context
+ * @return 1 if a reboot is required, 0 otherwise.
+ */
+static int vb2_reset_nv_requests(struct vb2_context *ctx)
+{
+ int need_reboot = 0;
+
+ if (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST)) {
+ VB2_DEBUG("Unset display request (undo display init)\n");
+ vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 0);
+ need_reboot = 1;
+ }
+
+ if (vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST)) {
+ VB2_DEBUG("Unset diagnostic request (undo display init)\n");
+ vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 0);
+ need_reboot = 1;
+ }
+
+ return need_reboot;
+}
+
+vb2_error_t vb2_normal_boot(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ uint32_t max_rollforward = vb2_nv_get(ctx,
+ VB2_NV_KERNEL_MAX_ROLLFORWARD);
+
+ /* Boot from fixed disk only */
+ VB2_DEBUG("Entering\n");
+
+ if (vb2_reset_nv_requests(ctx)) {
+ VB2_DEBUG("Normal mode: reboot to reset NVRAM requests\n");
+ return VBERROR_REBOOT_REQUIRED;
+ }
+
+ vb2_error_t rv = VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED);
+
+ VB2_DEBUG("Checking if TPM kernel version needs advancing\n");
+
+ /*
+ * Special case for when we're trying a slot with new firmware.
+ * Firmware updates also usually change the kernel key, which means
+ * that the new firmware can only boot a new kernel, and the old
+ * firmware in the previous slot can only boot the previous kernel.
+ *
+ * Don't roll-forward the kernel version, because we don't yet know if
+ * the new kernel will successfully boot.
+ */
+ if (vb2_nv_get(ctx, VB2_NV_FW_RESULT) == VB2_FW_RESULT_TRYING) {
+ VB2_DEBUG("Trying new FW; skip kernel version roll-forward.\n");
+ return rv;
+ }
+
+ /*
+ * Limit kernel version rollforward if needed. Can't limit kernel
+ * version to less than the version currently in the TPM. That is,
+ * we're limiting rollforward, not allowing rollback.
+ */
+ if (max_rollforward < sd->kernel_version_secdata)
+ max_rollforward = sd->kernel_version_secdata;
+
+ if (sd->kernel_version > max_rollforward) {
+ VB2_DEBUG("Limiting TPM kernel version roll-forward "
+ "to %#x < %#x\n",
+ max_rollforward, sd->kernel_version);
+
+ sd->kernel_version = max_rollforward;
+ }
+
+ if (sd->kernel_version > sd->kernel_version_secdata) {
+ vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_VERSIONS,
+ sd->kernel_version);
+ }
+
+ return rv;
+}
+
+int vb2api_is_developer_signed(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+ if (!sd->kernel_key_offset || !sd->kernel_key_size) {
+ VB2_REC_OR_DIE(ctx, "Cannot call this before kernel_phase1!\n");
+ return 0;
+ }
+
+ struct vb2_public_key key;
+ if (vb2_unpack_key(&key, vb2_member_of(sd, sd->kernel_key_offset)))
+ return 0;
+
+ /* This is a debugging aid, not a security-relevant feature. There's no
+ reason to hardcode the whole key or waste time computing a hash. Just
+ spot check the starting bytes of the pseudorandom part of the key. */
+ uint32_t devkey_n0inv = ctx->flags & VB2_CONTEXT_RECOVERY_MODE ?
+ 0x18cebcf5 : /* recovery_key.vbpubk @0x24 */
+ 0xe0cd87d9; /* kernel_subkey.vbpubk @0x24 */
+
+ if (key.n0inv == devkey_n0inv)
+ return 1;
+
+ return 0;
+}
+
+vb2_error_t vb2api_kernel_phase1(struct vb2_context *ctx)
+{
+ struct vb2_shared_data *sd = vb2_get_sd(ctx);
+ struct vb2_workbuf wb;
+ struct vb2_packed_key *packed_key;
+ vb2_error_t rv;
+
+ vb2_workbuf_from_ctx(ctx, &wb);
+
+ /*
+ * Init secdata_kernel and secdata_fwmp spaces. No need to init
+ * secdata_firmware, since it was already read during firmware
+ * verification. Ignore errors in recovery mode.
+ */
+ rv = vb2_secdata_kernel_init(ctx);
+ if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) {
+ VB2_DEBUG("TPM: init secdata_kernel returned %#x\n", rv);
+ vb2api_fail(ctx, VB2_RECOVERY_SECDATA_KERNEL_INIT, rv);
+ return rv;
+ }
+ rv = vb2_secdata_fwmp_init(ctx);
+ if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) {
+ VB2_DEBUG("TPM: init secdata_fwmp returned %#x\n", rv);
+ vb2api_fail(ctx, VB2_RECOVERY_SECDATA_FWMP_INIT, rv);
+ return rv;
+ }
+
+ /* Read kernel version from secdata. */
+ sd->kernel_version_secdata =
+ vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_VERSIONS);
+ sd->kernel_version = sd->kernel_version_secdata;
+
+ /* Find the key to use to verify the kernel keyblock */
+ if ((ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) {
+ /* Load recovery key from GBB. */
+ rv = vb2_gbb_read_recovery_key(ctx, &packed_key, NULL, &wb);
+ if (rv) {
+ if (vb2_allow_recovery(ctx))
+ VB2_DIE("GBB read recovery key failed.\n");
+ else
+ /*
+ * If we're headed for the BROKEN screen,
+ * we won't need the recovery key. Just
+ * short-circuit with success.
+ */
+ return VB2_SUCCESS;
+ }
+ } else {
+ /* Kernel subkey from firmware preamble */
+ struct vb2_fw_preamble *pre;
+
+ /* Make sure we have a firmware preamble loaded */
+ if (!sd->preamble_size)
+ return VB2_ERROR_API_KPHASE1_PREAMBLE;
+
+ pre = (struct vb2_fw_preamble *)
+ vb2_member_of(sd, sd->preamble_offset);
+ packed_key = &pre->kernel_subkey;
+ }
+
+ sd->kernel_key_offset = vb2_offset_of(sd, packed_key);
+ sd->kernel_key_size = packed_key->key_offset + packed_key->key_size;
+
+ vb2_set_workbuf_used(ctx, vb2_offset_of(sd, wb.buf));
+
+ if (vb2api_is_developer_signed(ctx))
+ VB2_DEBUG("This is developer-signed firmware.\n");
+
+ return VB2_SUCCESS;
+}