static void tpm_clear_tests(void) { /* No clear request */ reset_common_data(); TEST_SUCC(vb2_check_tpm_clear(&cc), "no clear request"); TEST_EQ(mock_tpm_clear_called, 0, "tpm not cleared"); /* Successful request */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 1); TEST_SUCC(vb2_check_tpm_clear(&cc), "clear request"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST), 0, "request cleared"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_DONE), 1, "done set"); TEST_EQ(mock_tpm_clear_called, 1, "tpm cleared"); /* Failed request */ reset_common_data(); mock_tpm_clear_retval = VB2_ERROR_EX_TPM_CLEAR_OWNER; vb2_nv_set(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 1); TEST_EQ(vb2_check_tpm_clear(&cc), VB2_ERROR_EX_TPM_CLEAR_OWNER, "clear failure"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST), 0, "request cleared"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_DONE), 0, "done not set"); }
static void load_kernel_vblock_tests(void) { reset_common_data(FOR_PHASE1); TEST_SUCC(vb2api_load_kernel_vblock(&cc), "load vblock good"); reset_common_data(FOR_PHASE1); mock_load_kernel_keyblock_retval = VB2_ERROR_MOCK; TEST_EQ(vb2api_load_kernel_vblock(&cc), VB2_ERROR_MOCK, "load vblock bad keyblock"); reset_common_data(FOR_PHASE1); mock_load_kernel_preamble_retval = VB2_ERROR_MOCK; TEST_EQ(vb2api_load_kernel_vblock(&cc), VB2_ERROR_MOCK, "load vblock bad preamble"); }
static void get_kernel_size_tests(void) { uint32_t offs, size; reset_common_data(FOR_PHASE2); offs = size = 0; TEST_SUCC(vb2api_get_kernel_size(&cc, &offs, &size), "get size good"); TEST_EQ(offs, 0x10000, " offset"); TEST_EQ(size, sizeof(kernel_data), " size"); /* Don't need to pass pointers */ reset_common_data(FOR_PHASE2); TEST_SUCC(vb2api_get_kernel_size(&cc, NULL, NULL), "get size null"); reset_common_data(FOR_PHASE2); sd->workbuf_preamble_size = 0; TEST_EQ(vb2api_get_kernel_size(&cc, &offs, &size), VB2_ERROR_API_GET_KERNEL_SIZE_PREAMBLE, "get size no preamble"); }
static void verify_kernel_data_tests(void) { reset_common_data(FOR_PHASE2); TEST_SUCC(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), "verify data good"); reset_common_data(FOR_PHASE2); sd->workbuf_preamble_size = 0; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_API_VERIFY_KDATA_PREAMBLE, "verify no preamble"); reset_common_data(FOR_PHASE2); TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data) + 1), VB2_ERROR_API_VERIFY_KDATA_SIZE, "verify size"); reset_common_data(FOR_PHASE2); cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_digest_context) + 1; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_API_VERIFY_KDATA_WORKBUF, "verify workbuf"); reset_common_data(FOR_PHASE2); sd->workbuf_data_key_size = 0; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_API_VERIFY_KDATA_KEY, "verify no key"); reset_common_data(FOR_PHASE2); mock_unpack_key_retval = VB2_ERROR_MOCK; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_MOCK, "verify unpack key"); reset_common_data(FOR_PHASE2); kdkey->algorithm = VB2_ALG_COUNT; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_SHA_INIT_ALGORITHM, "verify hash init"); reset_common_data(FOR_PHASE2); cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_digest_context) - 4; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_API_CHECK_HASH_WORKBUF_DIGEST, "verify hash workbuf"); reset_common_data(FOR_PHASE2); kernel_data[3] ^= 0xd0; TEST_EQ(vb2api_verify_kernel_data(&cc, kernel_data, sizeof(kernel_data)), VB2_ERROR_VDATA_VERIFY_DIGEST, "verify hash digest"); kernel_data[3] ^= 0xd0; }
static void recovery_tests(void) { /* No recovery */ reset_common_data(); vb2_check_recovery(&cc); TEST_EQ(sd->recovery_reason, 0, "No recovery reason"); TEST_EQ(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY, 0, "Not manual recovery"); TEST_EQ(cc.flags & VB2_CONTEXT_RECOVERY_MODE, 0, "Not recovery mode"); /* From request */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, 3); vb2_check_recovery(&cc); TEST_EQ(sd->recovery_reason, 3, "Recovery reason from request"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0, "NV cleared"); TEST_EQ(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY, 0, "Not manual recovery"); TEST_NEQ(cc.flags & VB2_CONTEXT_RECOVERY_MODE, 0, "Recovery mode"); /* From request, but already failed */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, 4); sd->recovery_reason = 5; vb2_check_recovery(&cc); TEST_EQ(sd->recovery_reason, 5, "Recovery reason already failed"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0, "NV still cleared"); /* Override */ reset_common_data(); sd->recovery_reason = 6; cc.flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE; vb2_check_recovery(&cc); TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_MANUAL, "Recovery reason forced"); TEST_NEQ(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY, 0, "SD flag set"); }
static void phase3_tests(void) { uint32_t v; reset_common_data(FOR_PHASE3); TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 good"); vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v); TEST_EQ(v, 0x20004, " version"); reset_common_data(FOR_PHASE3); sd->kernel_version = 0x20001; TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 no rollback"); vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v); TEST_EQ(v, 0x20002, " version"); reset_common_data(FOR_PHASE3); sd->flags &= ~VB2_SD_FLAG_KERNEL_SIGNED; TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 unsigned kernel"); vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v); TEST_EQ(v, 0x20002, " version"); reset_common_data(FOR_PHASE3); cc.flags |= VB2_CONTEXT_RECOVERY_MODE; TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 recovery"); vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v); TEST_EQ(v, 0x20002, " version"); reset_common_data(FOR_PHASE3); cc.flags &= ~VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD; TEST_SUCC(vb2api_kernel_phase3(&cc), "phase3 no rollforward"); vb2_secdatak_get(&cc, VB2_SECDATAK_VERSIONS, &v); TEST_EQ(v, 0x20002, " version"); reset_common_data(FOR_PHASE3); sd->status &= ~VB2_SD_STATUS_SECDATAK_INIT; TEST_EQ(vb2api_kernel_phase3(&cc), VB2_ERROR_SECDATAK_SET_UNINITIALIZED, "phase3 set fail"); }
static void verify_preamble_tests(void) { struct vb2_fw_preamble *pre = &mock_vblock.p.pre; int wb_used_before; uint32_t v; /* Test successful call */ reset_common_data(FOR_PREAMBLE); wb_used_before = cc.workbuf_used; TEST_SUCC(vb2_load_fw_preamble(&cc), "preamble good"); TEST_EQ(sd->fw_version, 0x20002, "combined version"); TEST_EQ(sd->workbuf_preamble_offset, (wb_used_before + (VB2_WORKBUF_ALIGN - 1)) & ~(VB2_WORKBUF_ALIGN - 1), "preamble offset"); TEST_EQ(sd->workbuf_preamble_size, pre->preamble_size, "preamble size"); TEST_EQ(cc.workbuf_used, sd->workbuf_preamble_offset + sd->workbuf_preamble_size, "workbuf used"); /* Expected failures */ reset_common_data(FOR_PREAMBLE); sd->workbuf_data_key_size = 0; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_FW_PREAMBLE2_DATA_KEY, "preamble no data key"); reset_common_data(FOR_PREAMBLE); mock_unpack_key_retval = VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM, "preamble unpack data key"); reset_common_data(FOR_PREAMBLE); cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_fw_preamble) + 8; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_FW_PREAMBLE2_WORKBUF_HEADER, "preamble not enough workbuf for header"); reset_common_data(FOR_PREAMBLE); sd->vblock_preamble_offset = sizeof(mock_vblock); TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_EX_READ_RESOURCE_SIZE, "preamble read header"); reset_common_data(FOR_PREAMBLE); cc.workbuf_used = cc.workbuf_size - sizeof(mock_vblock.p) + 8; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_FW_PREAMBLE2_WORKBUF, "preamble not enough workbuf"); reset_common_data(FOR_PREAMBLE); pre->preamble_size = sizeof(mock_vblock); TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_EX_READ_RESOURCE_SIZE, "preamble read full"); reset_common_data(FOR_PREAMBLE); mock_verify_preamble_retval = VB2_ERROR_PREAMBLE_SIG_INVALID; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_PREAMBLE_SIG_INVALID, "preamble verify"); reset_common_data(FOR_PREAMBLE); pre->firmware_version = 0x10000; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_FW_PREAMBLE_VERSION_RANGE, "preamble version range"); reset_common_data(FOR_PREAMBLE); pre->firmware_version = 1; TEST_EQ(vb2_load_fw_preamble(&cc), VB2_ERROR_FW_PREAMBLE_VERSION_ROLLBACK, "preamble version rollback"); reset_common_data(FOR_PREAMBLE); pre->firmware_version = 3; TEST_SUCC(vb2_load_fw_preamble(&cc), "preamble version roll forward"); vb2_secdata_get(&cc, VB2_SECDATA_VERSIONS, &v); TEST_EQ(v, 0x20003, "roll forward"); /* Newer version without result success doesn't roll forward */ reset_common_data(FOR_PREAMBLE); pre->firmware_version = 3; sd->last_fw_result = VB2_FW_RESULT_UNKNOWN; TEST_SUCC(vb2_load_fw_preamble(&cc), "preamble version no roll forward 1"); vb2_secdata_get(&cc, VB2_SECDATA_VERSIONS, &v); TEST_EQ(v, 0x20002, "no roll forward"); /* Newer version with success but for other slot doesn't roll forward */ reset_common_data(FOR_PREAMBLE); pre->firmware_version = 3; sd->last_fw_slot = 1; TEST_SUCC(vb2_load_fw_preamble(&cc), "preamble version no roll forward 2"); vb2_secdata_get(&cc, VB2_SECDATA_VERSIONS, &v); TEST_EQ(v, 0x20002, "no roll forward"); }
static void verify_keyblock_tests(void) { struct vb2_keyblock *kb = &mock_vblock.k.kb; struct vb2_packed_key *k; int wb_used_before; /* Test successful call */ reset_common_data(FOR_KEYBLOCK); wb_used_before = cc.workbuf_used; TEST_SUCC(vb2_load_fw_keyblock(&cc), "keyblock verify"); TEST_EQ(sd->fw_version, 0x20000, "keyblock version"); TEST_EQ(sd->vblock_preamble_offset, sizeof(mock_vblock.k), "preamble offset"); TEST_EQ(sd->workbuf_data_key_offset, (wb_used_before + (VB2_WORKBUF_ALIGN - 1)) & ~(VB2_WORKBUF_ALIGN - 1), "keyblock data key offset"); TEST_EQ(cc.workbuf_used, sd->workbuf_data_key_offset + sd->workbuf_data_key_size, "workbuf used"); /* Make sure data key was properly saved */ k = (struct vb2_packed_key *)(cc.workbuf + sd->workbuf_data_key_offset); TEST_EQ(k->algorithm, 7, "data key algorithm"); TEST_EQ(k->key_version, 2, "data key version"); TEST_EQ(k->key_size, sizeof(mock_vblock.k.data_key_data), "data key size"); TEST_EQ(memcmp(cc.workbuf + sd->workbuf_data_key_offset + k->key_offset, mock_vblock.k.data_key_data, sizeof(mock_vblock.k.data_key_data)), 0, "data key data"); TEST_EQ(cc.workbuf_used, sd->workbuf_data_key_offset + sd->workbuf_data_key_size, "workbuf used after"); /* Test failures */ reset_common_data(FOR_KEYBLOCK); cc.workbuf_used = cc.workbuf_size - sd->gbb_rootkey_size + 8; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_FW_KEYBLOCK_WORKBUF_ROOT_KEY, "keyblock not enough workbuf for root key"); reset_common_data(FOR_KEYBLOCK); sd->gbb_rootkey_size = sizeof(mock_gbb); TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_EX_READ_RESOURCE_SIZE, "keyblock read root key"); reset_common_data(FOR_KEYBLOCK); mock_unpack_key_retval = VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM, "keyblock unpack root key"); reset_common_data(FOR_KEYBLOCK); cc.workbuf_used = cc.workbuf_size - sd->gbb_rootkey_size - 8; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_FW_KEYBLOCK_WORKBUF_HEADER, "keyblock not enough workbuf for header"); reset_common_data(FOR_KEYBLOCK); mock_read_res_fail_on_call = 2; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_EX_READ_RESOURCE_INDEX, "keyblock read keyblock header"); reset_common_data(FOR_KEYBLOCK); cc.workbuf_used = cc.workbuf_size - sd->gbb_rootkey_size - sizeof(struct vb2_keyblock); TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_FW_KEYBLOCK_WORKBUF, "keyblock not enough workbuf for entire keyblock"); reset_common_data(FOR_KEYBLOCK); kb->keyblock_size = sizeof(mock_vblock) + 1; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_EX_READ_RESOURCE_SIZE, "keyblock read keyblock"); reset_common_data(FOR_KEYBLOCK); mock_verify_keyblock_retval = VB2_ERROR_KEYBLOCK_MAGIC; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_KEYBLOCK_MAGIC, "keyblock verify keyblock"); reset_common_data(FOR_KEYBLOCK); kb->data_key.key_version = 0x10000; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE, "keyblock version range"); reset_common_data(FOR_KEYBLOCK); kb->data_key.key_version = 1; TEST_EQ(vb2_load_fw_keyblock(&cc), VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK, "keyblock rollback"); }
static void phase1_tests(void) { struct vb2_packed_key *k; uint32_t old_preamble_offset; /* Test successful call */ reset_common_data(FOR_PHASE1); old_preamble_offset = sd->workbuf_preamble_offset; TEST_SUCC(vb2api_kernel_phase1(&cc), "phase1 good"); TEST_EQ(sd->workbuf_preamble_size, 0, " no more fw preamble"); /* Make sure normal key was loaded */ TEST_EQ(sd->workbuf_kernel_key_offset, old_preamble_offset, " workbuf key offset"); k = (struct vb2_packed_key *) (cc.workbuf + sd->workbuf_kernel_key_offset); TEST_EQ(sd->workbuf_kernel_key_size, k->key_offset + k->key_size, " workbuf key size"); TEST_EQ(cc.workbuf_used, sd->workbuf_kernel_key_offset + sd->workbuf_kernel_key_size, " workbuf used"); TEST_EQ(k->algorithm, 7, " key algorithm"); TEST_EQ(k->key_size, sizeof(fw_kernel_key_data), " key_size"); TEST_EQ(memcmp((uint8_t *)k + k->key_offset, fw_kernel_key_data, k->key_size), 0, " key data"); TEST_EQ(sd->kernel_version_secdatak, 0x20002, " secdatak version"); /* Test successful call in recovery mode */ reset_common_data(FOR_PHASE1); cc.flags |= VB2_CONTEXT_RECOVERY_MODE; /* No preamble loaded in recovery mode */ cc.workbuf_used = old_preamble_offset = sd->workbuf_preamble_offset; sd->workbuf_preamble_offset = sd->workbuf_preamble_size = 0; TEST_SUCC(vb2api_kernel_phase1(&cc), "phase1 rec good"); TEST_EQ(sd->workbuf_preamble_size, 0, "no more fw preamble"); /* Make sure recovery key was loaded */ TEST_EQ(sd->workbuf_kernel_key_offset, old_preamble_offset, " workbuf key offset"); k = (struct vb2_packed_key *) (cc.workbuf + sd->workbuf_kernel_key_offset); TEST_EQ(sd->workbuf_kernel_key_size, k->key_offset + k->key_size, " workbuf key size"); TEST_EQ(cc.workbuf_used, sd->workbuf_kernel_key_offset + sd->workbuf_kernel_key_size, " workbuf used"); TEST_EQ(k->algorithm, 11, " key algorithm"); TEST_EQ(k->key_size, sizeof(mock_gbb.recovery_key_data), " key_size"); TEST_EQ(memcmp((uint8_t *)k + k->key_offset, mock_gbb.recovery_key_data, k->key_size), 0, " key data"); TEST_EQ(sd->kernel_version_secdatak, 0x20002, " secdatak version"); /* Bad secdatak causes failure in normal mode only */ reset_common_data(FOR_PHASE1); cc.secdatak[0] ^= 0x33; TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_SECDATAK_CRC, "phase1 bad secdata"); reset_common_data(FOR_PHASE1); cc.secdatak[0] ^= 0x33; cc.flags |= VB2_CONTEXT_RECOVERY_MODE; TEST_SUCC(vb2api_kernel_phase1(&cc), "phase1 bad secdata rec"); TEST_EQ(sd->kernel_version_secdatak, 0, " secdatak version"); /* Failures while reading recovery key */ reset_common_data(FOR_PHASE1); cc.flags |= VB2_CONTEXT_RECOVERY_MODE; cc.workbuf_used = cc.workbuf_size - sizeof(struct vb2_gbb_header) + 1; TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_GBB_WORKBUF, "phase1 rec workbuf gbb header"); reset_common_data(FOR_PHASE1); cc.flags |= VB2_CONTEXT_RECOVERY_MODE; mock_read_gbb_header_retval = VB2_ERROR_MOCK; TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_MOCK, "phase1 rec gbb read header"); reset_common_data(FOR_PHASE1); cc.flags |= VB2_CONTEXT_RECOVERY_MODE; mock_gbb.h.recovery_key_size = cc.workbuf_size - 1; TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_API_KPHASE1_WORKBUF_REC_KEY, "phase1 rec workbuf key"); reset_common_data(FOR_PHASE1); cc.flags |= VB2_CONTEXT_RECOVERY_MODE; mock_read_res_fail_on_call = 1; TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_MOCK, "phase1 rec gbb read key"); /* Failures while parsing subkey from firmware preamble */ reset_common_data(FOR_PHASE1); sd->workbuf_preamble_size = 0; TEST_EQ(vb2api_kernel_phase1(&cc), VB2_ERROR_API_KPHASE1_PREAMBLE, "phase1 fw preamble"); }
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"); }
static void select_slot_tests(void) { /* Slot A */ reset_common_data(); TEST_SUCC(vb2_select_fw_slot(&cc), "select slot A"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_UNKNOWN, "result unknown"); TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 0, "tried A"); TEST_EQ(sd->fw_slot, 0, "selected A"); TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B"); /* Slot B */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_TRY_NEXT, 1); TEST_SUCC(vb2_select_fw_slot(&cc), "select slot B"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_UNKNOWN, "result unknown"); TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 1, "tried B"); TEST_EQ(sd->fw_slot, 1, "selected B"); TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B"); /* Slot A ran out of tries */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING); TEST_SUCC(vb2_select_fw_slot(&cc), "select slot A out of tries"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "try B next"); TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 1, "tried B"); TEST_EQ(sd->fw_slot, 1, "selected B"); TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B"); /* Slot A ran out of tries, even with nofail active */ reset_common_data(); cc.flags |= VB2_CONTEXT_NOFAIL_BOOT; vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING); TEST_SUCC(vb2_select_fw_slot(&cc), "select slot A out of tries"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "try B next"); TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 1, "tried B"); TEST_EQ(sd->fw_slot, 1, "selected B"); TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B"); /* Slot A used up a try */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3); TEST_SUCC(vb2_select_fw_slot(&cc), "try slot A"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_TRYING, "result trying"); TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 0, "tried A"); TEST_EQ(sd->fw_slot, 0, "selected A"); TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 2, "tries decremented"); /* Slot A failed, but nofail active */ reset_common_data(); cc.flags |= VB2_CONTEXT_NOFAIL_BOOT; vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3); TEST_SUCC(vb2_select_fw_slot(&cc), "try slot A"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT), VB2_FW_RESULT_TRYING, "result trying"); TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_TRIED), 0, "tried A"); TEST_EQ(sd->fw_slot, 0, "selected A"); TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 3, "tries not decremented"); /* Tried/result get copied to the previous fields */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_FW_TRIED, 0); vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_SUCCESS); vb2_select_fw_slot(&cc); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_TRIED), 0, "prev A"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_RESULT), VB2_FW_RESULT_SUCCESS, "prev success"); reset_common_data(); vb2_nv_set(&cc, VB2_NV_FW_TRIED, 1); vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE); vb2_select_fw_slot(&cc); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_TRIED), 1, "prev B"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_PREV_RESULT), VB2_FW_RESULT_FAILURE, "prev failure"); }
static void dev_switch_tests(void) { uint32_t v; /* Normal mode */ reset_common_data(); TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode off"); TEST_EQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd not in dev"); TEST_EQ(cc.flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "ctx not in dev"); TEST_EQ(mock_tpm_clear_called, 0, "no tpm clear"); /* Dev mode */ reset_common_data(); vb2_secdata_set(&cc, VB2_SECDATA_FLAGS, (VB2_SECDATA_FLAG_DEV_MODE | VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER)); TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode on"); TEST_NEQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd in dev"); TEST_NEQ(cc.flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "ctx in dev"); TEST_EQ(mock_tpm_clear_called, 0, "no tpm clear"); /* Any normal mode boot clears dev boot flags */ reset_common_data(); vb2_nv_set(&cc, VB2_NV_DEV_BOOT_USB, 1); vb2_nv_set(&cc, VB2_NV_DEV_BOOT_LEGACY, 1); vb2_nv_set(&cc, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1); vb2_nv_set(&cc, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP, 1); vb2_nv_set(&cc, VB2_NV_FASTBOOT_UNLOCK_IN_FW, 1); TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode off"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_USB), 0, "cleared dev boot usb"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_LEGACY), 0, "cleared dev boot legacy"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_SIGNED_ONLY), 0, "cleared dev boot signed only"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP), 0, "cleared dev boot fastboot full cap"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_FASTBOOT_UNLOCK_IN_FW), 0, "cleared dev boot fastboot unlock in fw"); /* Normal-dev transition clears TPM */ reset_common_data(); vb2_secdata_set(&cc, VB2_SECDATA_FLAGS, VB2_SECDATA_FLAG_DEV_MODE); TEST_SUCC(vb2_check_dev_switch(&cc), "to dev mode"); TEST_EQ(mock_tpm_clear_called, 1, "tpm clear"); vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v); TEST_EQ(v, (VB2_SECDATA_FLAG_DEV_MODE | VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER), "last boot developer now"); /* Dev-normal transition clears TPM too */ reset_common_data(); vb2_secdata_set(&cc, VB2_SECDATA_FLAGS, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER); TEST_SUCC(vb2_check_dev_switch(&cc), "from dev mode"); TEST_EQ(mock_tpm_clear_called, 1, "tpm clear"); vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v); TEST_EQ(v, 0, "last boot not developer now"); /* Disable dev mode */ reset_common_data(); vb2_secdata_set(&cc, VB2_SECDATA_FLAGS, (VB2_SECDATA_FLAG_DEV_MODE | VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER)); vb2_nv_set(&cc, VB2_NV_DISABLE_DEV_REQUEST, 1); TEST_SUCC(vb2_check_dev_switch(&cc), "disable dev request"); TEST_EQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd not in dev"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_DISABLE_DEV_REQUEST), 0, "request cleared"); /* Force enabled by gbb */ reset_common_data(); sd->gbb_flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON; TEST_SUCC(vb2_check_dev_switch(&cc), "dev on via gbb"); TEST_NEQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd in dev"); vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v); TEST_EQ(v, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER, "doesn't set dev on in secdata but does set last boot dev"); TEST_EQ(mock_tpm_clear_called, 1, "tpm clear"); /* Force enabled by ctx flag */ reset_common_data(); cc.flags |= VB2_CONTEXT_FORCE_DEVELOPER_MODE; TEST_SUCC(vb2_check_dev_switch(&cc), "dev on via ctx flag"); TEST_NEQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd in dev"); vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v); TEST_EQ(v, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER, "doesn't set dev on in secdata but does set last boot dev"); TEST_EQ(mock_tpm_clear_called, 1, "tpm clear"); /* Simulate clear owner failure */ reset_common_data(); vb2_secdata_set(&cc, VB2_SECDATA_FLAGS, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER); mock_tpm_clear_retval = VB2_ERROR_EX_TPM_CLEAR_OWNER; TEST_EQ(vb2_check_dev_switch(&cc), VB2_ERROR_EX_TPM_CLEAR_OWNER, "tpm clear fail"); TEST_EQ(mock_tpm_clear_called, 1, "tpm clear"); vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v); TEST_EQ(v, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER, "last boot still developer"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), VB2_RECOVERY_TPM_CLEAR_OWNER, "requests recovery"); TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE), (uint8_t)VB2_ERROR_EX_TPM_CLEAR_OWNER, "recovery subcode"); }