int vb2api_fw_phase1(struct vb2_context *ctx) { int rv; /* Initialize the vboot context if it hasn't been yet */ vb2_init_context(ctx); /* Initialize NV context */ vb2_nv_init(ctx); /* Initialize secure data */ rv = vb2_secdata_init(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_SECDATA_INIT, rv); /* Load and parse the GBB header */ rv = vb2_fw_parse_gbb(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_GBB_HEADER, rv); /* Check for dev switch */ rv = vb2_check_dev_switch(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_DEV_SWITCH, rv); /* * Check for recovery. Note that this function returns void, since * any errors result in requesting recovery. */ vb2_check_recovery(ctx); /* Return error if recovery is needed */ if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) { /* Always clear RAM when entering recovery mode */ ctx->flags |= VB2_CONTEXT_CLEAR_RAM; return VB2_ERROR_API_PHASE1_RECOVERY; } return VB2_SUCCESS; }
int vb2api_fw_phase1(struct vb2_context *ctx) { int rv; struct vb2_shared_data *sd; /* Initialize the vboot context if it hasn't been yet */ vb2_init_context(ctx); sd = vb2_get_sd(ctx); /* Initialize NV context */ vb2_nv_init(ctx); /* * Handle caller-requested reboot due to secdata. Do this before we * even look at secdata. If we fail because of a reboot loop we'll be * the first failure so will get to set the recovery reason. */ if (!(ctx->flags & VB2_CONTEXT_SECDATA_WANTS_REBOOT)) { /* No reboot requested */ vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 0); } else if (vb2_nv_get(ctx, VB2_NV_TPM_REQUESTED_REBOOT)) { /* * Reboot requested... again. Fool me once, shame on you. * Fool me twice, shame on me. Fail into recovery to avoid * a reboot loop. */ vb2_fail(ctx, VB2_RECOVERY_RO_TPM_REBOOT, 0); } else { /* Reboot requested for the first time */ vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 1); return VB2_ERROR_API_PHASE1_SECDATA_REBOOT; } /* Initialize secure data */ rv = vb2_secdata_init(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_SECDATA_INIT, rv); /* Load and parse the GBB header */ rv = vb2_fw_parse_gbb(ctx); if (rv) vb2_fail(ctx, VB2_RECOVERY_GBB_HEADER, rv); /* * Check for recovery. Note that this function returns void, since any * errors result in requesting recovery. That's also why we don't * return error from failures in the preceding two steps; those * failures simply cause us to detect recovery mode here. */ vb2_check_recovery(ctx); /* Check for dev switch */ rv = vb2_check_dev_switch(ctx); if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { /* * Error in dev switch processing, and we weren't already * headed for recovery mode. Reboot into recovery mode, since * it's too late to handle those errors this boot, and we need * to take a different path through the dev switch checking * code in that case. */ vb2_fail(ctx, VB2_RECOVERY_DEV_SWITCH, rv); return rv; } /* * Check for possible reasons to ask the firmware to make display * available. sd->recovery_reason may have been set above by * vb2_check_recovery. VB2_SD_FLAG_DEV_MODE_ENABLED may have been set * above by vb2_check_dev_switch. */ if (!(ctx->flags & VB2_CONTEXT_DISPLAY_INIT) && (vb2_nv_get(ctx, VB2_NV_OPROM_NEEDED) || sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED || sd->recovery_reason)) ctx->flags |= VB2_CONTEXT_DISPLAY_INIT; /* Mark display as available for downstream vboot and vboot callers. */ if (ctx->flags & VB2_CONTEXT_DISPLAY_INIT) sd->flags |= VB2_SD_FLAG_DISPLAY_AVAILABLE; /* Return error if recovery is needed */ if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) { /* Always clear RAM when entering recovery mode */ ctx->flags |= VB2_CONTEXT_CLEAR_RAM; return VB2_ERROR_API_PHASE1_RECOVERY; } return VB2_SUCCESS; }
static void init_context_tests(void) { /* Use our own context struct so we can re-init it */ struct vb2_context c = { .workbuf = workbuf, .workbuf_size = sizeof(workbuf), }; reset_common_data(); TEST_SUCC(vb2_init_context(&c), "Init context good"); TEST_EQ(c.workbuf_used, sizeof(struct vb2_shared_data), "Init vbsd"); /* Don't re-init if used is non-zero */ c.workbuf_used = 200; TEST_SUCC(vb2_init_context(&c), "Re-init context good"); TEST_EQ(c.workbuf_used, 200, "Didn't re-init"); /* Handle workbuf errors */ c.workbuf_used = 0; c.workbuf_size = sizeof(struct vb2_shared_data) - 1; TEST_EQ(vb2_init_context(&c), VB2_ERROR_INITCTX_WORKBUF_SMALL, "Init too small"); c.workbuf_size = sizeof(workbuf); /* Handle workbuf unaligned */ c.workbuf++; TEST_EQ(vb2_init_context(&c), VB2_ERROR_INITCTX_WORKBUF_ALIGN, "Init unaligned"); } static void misc_tests(void) { struct vb2_workbuf wb; reset_common_data(); cc.workbuf_used = 16; vb2_workbuf_from_ctx(&cc, &wb); TEST_PTR_EQ(wb.buf, workbuf + 16, "vb_workbuf_from_ctx() buf"); TEST_EQ(wb.size, cc.workbuf_size - 16, "vb_workbuf_from_ctx() size"); } static void gbb_tests(void) { struct vb2_gbb_header gbb = { .signature = {'$', 'G', 'B', 'B'}, .major_version = VB2_GBB_MAJOR_VER, .minor_version = VB2_GBB_MINOR_VER, .header_size = sizeof(struct vb2_gbb_header), .flags = 0x1234, .rootkey_offset = 240, .rootkey_size = 1040, }; struct vb2_gbb_header gbbdest; TEST_EQ(sizeof(struct vb2_gbb_header), EXPECTED_VB2_GBB_HEADER_SIZE, "sizeof(struct vb2_gbb_header)"); reset_common_data(); /* Good contents */ mock_resource_index = VB2_RES_GBB; mock_resource_ptr = &gbb; mock_resource_size = sizeof(gbb); TEST_SUCC(vb2_read_gbb_header(&cc, &gbbdest), "read gbb header good"); TEST_SUCC(memcmp(&gbb, &gbbdest, sizeof(gbb)), "read gbb contents"); mock_resource_index = VB2_RES_GBB + 1; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_EX_READ_RESOURCE_INDEX, "read gbb header missing"); mock_resource_index = VB2_RES_GBB; gbb.signature[0]++; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_MAGIC, "read gbb header bad magic"); gbb.signature[0]--; gbb.major_version = VB2_GBB_MAJOR_VER + 1; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_VERSION, "read gbb header major version"); gbb.major_version = VB2_GBB_MAJOR_VER; gbb.minor_version = VB2_GBB_MINOR_VER + 1; TEST_SUCC(vb2_read_gbb_header(&cc, &gbbdest), "read gbb header minor++"); gbb.minor_version = 1; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_TOO_OLD, "read gbb header 1.1 fails"); gbb.minor_version = 0; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_TOO_OLD, "read gbb header 1.0 fails"); gbb.minor_version = VB2_GBB_MINOR_VER; gbb.header_size--; TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest), VB2_ERROR_GBB_HEADER_SIZE, "read gbb header size"); TEST_EQ(vb2_fw_parse_gbb(&cc), VB2_ERROR_GBB_HEADER_SIZE, "parse gbb failure"); gbb.header_size++; /* Parse GBB */ TEST_SUCC(vb2_fw_parse_gbb(&cc), "parse gbb"); TEST_EQ(sd->gbb_flags, gbb.flags, "gbb flags"); TEST_EQ(sd->gbb_rootkey_offset, gbb.rootkey_offset, "rootkey offset"); TEST_EQ(sd->gbb_rootkey_size, gbb.rootkey_size, "rootkey size"); /* Workbuf failure */ reset_common_data(); cc.workbuf_used = cc.workbuf_size - 4; TEST_EQ(vb2_fw_parse_gbb(&cc), VB2_ERROR_GBB_WORKBUF, "parse gbb no workbuf"); } static void fail_tests(void) { /* Early fail (before even NV init) */ reset_common_data(); sd->status &= ~VB2_SD_STATUS_NV_INIT; vb2_fail(&cc, 1, 2); TEST_NEQ(sd->status & VB2_SD_STATUS_NV_INIT, 0, "vb2_fail inits NV"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 1, "vb2_fail request"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE), 2, "vb2_fail subcode"); /* Repeated fail doesn't overwrite the error code */ vb2_fail(&cc, 3, 4); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 1, "vb2_fail repeat"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE), 2, "vb2_fail repeat2"); /* Fail with other slot good doesn't trigger recovery */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3); vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN); sd->status |= VB2_SD_STATUS_CHOSE_SLOT; sd->fw_slot = 0; sd->last_fw_slot = 1; sd->last_fw_result = VB2_FW_RESULT_UNKNOWN; vb2_fail(&cc, 5, 6); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0, "vb2_failover"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_FAILURE, "vb2_fail this fw"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 0, "vb2_fail use up tries"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "vb2_fail try other slot"); /* Fail with other slot already failing triggers recovery */ reset_common_data(); sd->status |= VB2_SD_STATUS_CHOSE_SLOT; sd->fw_slot = 1; sd->last_fw_slot = 0; sd->last_fw_result = VB2_FW_RESULT_FAILURE; vb2_fail(&cc, 7, 8); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 7, "vb2_fail both slots bad"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_FAILURE, "vb2_fail this fw"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 0, "vb2_fail try other slot"); }