/** * Do next chunk of hashing work, if any. */ static void vboot_hash_next_chunk(void) { int size; /* Handle abort */ if (want_abort) { in_progress = 0; vboot_hash_abort(); return; } /* Compute the next chunk of hash */ size = MIN(CHUNK_SIZE, data_size - curr_pos); SHA256_update(&ctx, (const uint8_t *)(CONFIG_FLASH_BASE + data_offset + curr_pos), size); curr_pos += size; if (curr_pos >= data_size) { /* Store the final hash */ hash = SHA256_final(&ctx); CPRINTS("hash done %.*h", SHA256_DIGEST_SIZE, hash); in_progress = 0; /* Handle receiving abort during finalize */ if (want_abort) vboot_hash_abort(); return; } /* If we're still here, more work to do; come back later */ hook_call_deferred(vboot_hash_next_chunk, WORK_INTERVAL_US); }
static int read_and_hash_chunk(int offset, int size) { char *buf; int rv; if (size == 0) return EC_SUCCESS; rv = shared_mem_acquire(size, &buf); if (rv == EC_ERROR_BUSY) { /* Couldn't update hash right now; try again later */ hook_call_deferred(vboot_hash_next_chunk, WORK_INTERVAL_US); return rv; } else if (rv != EC_SUCCESS) { vboot_hash_abort(); return rv; } rv = flash_read(offset, size, buf); if (rv == EC_SUCCESS) SHA256_update(&ctx, (const uint8_t *)buf, size); else vboot_hash_abort(); shared_mem_release(buf); return rv; }
static int host_command_vboot_hash(struct host_cmd_handler_args *args) { const struct ec_params_vboot_hash *p = args->params; struct ec_response_vboot_hash *r = args->response; int rv; switch (p->cmd) { case EC_VBOOT_HASH_GET: fill_response(r); args->response_size = sizeof(*r); return EC_RES_SUCCESS; case EC_VBOOT_HASH_ABORT: vboot_hash_abort(); return EC_RES_SUCCESS; case EC_VBOOT_HASH_START: case EC_VBOOT_HASH_RECALC: rv = host_start_hash(p); if (rv != EC_RES_SUCCESS) return rv; /* Wait for hash to finish if command is RECALC */ if (p->cmd == EC_VBOOT_HASH_RECALC) while (in_progress) usleep(1000); fill_response(r); args->response_size = sizeof(*r); return EC_RES_SUCCESS; default: return EC_RES_INVALID_PARAM; } }
static int command_hash(int argc, char **argv) { uint32_t offset = CONFIG_FW_RW_OFF; uint32_t size = CONFIG_FW_RW_SIZE; char *e; if (argc == 1) { ccprintf("Offset: 0x%08x\n", data_offset); ccprintf("Size: 0x%08x (%d)\n", data_size, data_size); ccprintf("Digest: "); if (want_abort) ccprintf("(aborting)\n"); else if (in_progress) ccprintf("(in progress)\n"); else if (hash) ccprintf("%.*h\n", SHA256_DIGEST_SIZE, hash); else ccprintf("(invalid)\n"); return EC_SUCCESS; } if (argc == 2) { if (!strcasecmp(argv[1], "abort")) { vboot_hash_abort(); return EC_SUCCESS; } else if (!strcasecmp(argv[1], "rw")) { return vboot_hash_start( CONFIG_FW_RW_OFF, system_get_image_used(SYSTEM_IMAGE_RW), NULL, 0); } else if (!strcasecmp(argv[1], "ro")) { return vboot_hash_start( CONFIG_FW_RO_OFF, system_get_image_used(SYSTEM_IMAGE_RO), NULL, 0); } } if (argc >= 3) { offset = strtoi(argv[1], &e, 0); if (*e) return EC_ERROR_PARAM1; size = strtoi(argv[2], &e, 0); if (*e) return EC_ERROR_PARAM2; } if (argc == 4) { int nonce = strtoi(argv[3], &e, 0); if (*e) return EC_ERROR_PARAM3; return vboot_hash_start(offset, size, (const uint8_t *)&nonce, sizeof(nonce)); } else return vboot_hash_start(offset, size, NULL, 0); }
int vboot_hash_invalidate(int offset, int size) { /* Don't invalidate if passed an invalid region */ if (offset < 0 || size <= 0 || offset + size < 0) return 0; /* Don't invalidate if hash is already invalid */ if (!hash) return 0; /* No overlap if passed region is off either end of hashed region */ if (offset + size <= data_offset || offset >= data_offset + data_size) return 0; /* Invalidate the hash */ CPRINTS("hash invalidated 0x%08x 0x%08x", offset, size); vboot_hash_abort(); return 1; }