diff --git a/bootloader.c b/bootloader.c index bc79bee..61b24e9 100644 --- a/bootloader.c +++ b/bootloader.c @@ -155,10 +155,14 @@ struct update_header { unsigned fail_bitmap_length; }; +#define LOG_MAGIC "LOGmagic" +#define LOG_MAGIC_SIZE 8 + int write_update_for_bootloader( const char *update, int update_length, int bitmap_width, int bitmap_height, int bitmap_bpp, - const char *busy_bitmap, const char *fail_bitmap) { + const char *busy_bitmap, const char *fail_bitmap, + const char *log_filename) { if (ensure_root_path_unmounted(CACHE_NAME)) { LOGE("Can't unmount %s\n", CACHE_NAME); return -1; @@ -198,6 +202,21 @@ int write_update_for_bootloader( header.version = UPDATE_VERSION; header.size = header_size; + if (log_filename != NULL) { + // Write 1 byte into the following block, then fill to the end + // in order to reserve that block. We'll use the block to + // send a copy of the log through to the next invocation of + // recovery. We write the log as late as possible in order to + // capture any messages emitted by this function. + mtd_erase_blocks(write, 0); + if (mtd_write_data(write, (char*) &header, 1) != 1) { + LOGE("Can't write log block to %s\n(%s)\n", + CACHE_NAME, strerror(errno)); + mtd_write_close(write); + return -1; + } + } + off_t image_start_pos = mtd_erase_blocks(write, 0); header.image_length = update_length; if ((int) header.image_offset == -1 || @@ -256,6 +275,37 @@ int write_update_for_bootloader( return -1; } + if (log_filename != NULL) { + size_t erase_size; + if (mtd_partition_info(part, NULL, &erase_size, NULL) != 0) { + LOGE("Error reading block size\n(%s)\n", strerror(errno)); + mtd_write_close(write); + return -1; + } + mtd_erase_blocks(write, 0); + + if (erase_size > 0) { + char* log = malloc(erase_size); + FILE* f = fopen(log_filename, "rb"); + // The fseek() may fail if it tries to go before the + // beginning of the log, but that's okay because we want + // to be positioned at the start anyway. + fseek(f, -(erase_size-sizeof(size_t)-LOG_MAGIC_SIZE), SEEK_END); + memcpy(log, LOG_MAGIC, LOG_MAGIC_SIZE); + size_t read = fread(log+sizeof(size_t)+LOG_MAGIC_SIZE, + 1, erase_size-sizeof(size_t)-LOG_MAGIC_SIZE, f); + LOGI("read %d bytes from log\n", (int)read); + *(size_t *)(log + LOG_MAGIC_SIZE) = read; + fclose(f); + if (mtd_write_data(write, log, erase_size) != erase_size) { + LOGE("failed to store log in cache partition\n(%s)\n", + strerror(errno)); + mtd_write_close(write); + } + free(log); + } + } + if (mtd_erase_blocks(write, 0) != image_start_pos) { LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno)); mtd_write_close(write); @@ -269,3 +319,52 @@ int write_update_for_bootloader( return 0; } + +void recover_firmware_update_log() { + printf("recovering log from before firmware update\n"); + + const MtdPartition *part = get_root_mtd_partition(CACHE_NAME); + if (part == NULL) { + LOGE("Can't find %s\n", CACHE_NAME); + return; + } + + MtdReadContext* read = mtd_read_partition(part); + + size_t erase_size; + if (mtd_partition_info(part, NULL, &erase_size, NULL) != 0) { + LOGE("Error reading block size\n(%s)\n", strerror(errno)); + mtd_read_close(read); + return; + } + + char* buffer = malloc(erase_size); + if (mtd_read_data(read, buffer, erase_size) != erase_size) { + LOGE("Error reading header block\n(%s)\n", strerror(errno)); + mtd_read_close(read); + free(buffer); + return; + } + if (mtd_read_data(read, buffer, erase_size) != erase_size) { + LOGE("Error reading log block\n(%s)\n", strerror(errno)); + mtd_read_close(read); + free(buffer); + return; + } + mtd_read_close(read); + + if (memcmp(buffer, LOG_MAGIC, LOG_MAGIC_SIZE) != 0) { + LOGE("No log from before firmware install\n"); + free(buffer); + return; + } + + size_t log_size = *(size_t *)(buffer + LOG_MAGIC_SIZE); + LOGI("header has %d bytes of log\n", (int)log_size); + + printf("\n###\n### START RECOVERED LOG\n###\n\n"); + fwrite(buffer + sizeof(size_t) + LOG_MAGIC_SIZE, 1, log_size, stdout); + printf("\n\n###\n### END RECOVERED LOG\n###\n\n"); + + free(buffer); +} diff --git a/bootloader.h b/bootloader.h index 3d4302f..fec6409 100644 --- a/bootloader.h +++ b/bootloader.h @@ -54,6 +54,13 @@ int set_bootloader_message(const struct bootloader_message *in); int write_update_for_bootloader( const char *update, int update_len, int bitmap_width, int bitmap_height, int bitmap_bpp, - const char *busy_bitmap, const char *error_bitmap); + const char *busy_bitmap, const char *error_bitmap, + const char *log_filename); + +/* Look for a log stored in the cache partition in the block after the + * firmware update header. If we can read such a log, copy it to + * stdout (ie, the current log). + */ +void recover_firmware_update_log(); #endif diff --git a/firmware.c b/firmware.c index e2e4fe6..6739c1e 100644 --- a/firmware.c +++ b/firmware.c @@ -76,7 +76,8 @@ int firmware_update_pending() { * It is recovery's responsibility to clean up the mess afterwards. */ -int maybe_install_firmware_update(const char *send_intent) { +int maybe_install_firmware_update(const char *send_intent, + const char *log_filename) { if (update_data == NULL || update_length == 0) return 0; /* We destroy the cache partition to pass the update image to the @@ -104,7 +105,7 @@ int maybe_install_firmware_update(const char *send_intent) { ui_print("Writing %s image...\n", update_type); if (write_update_for_bootloader( update_data, update_length, - width, height, bpp, busy_image, fail_image)) { + width, height, bpp, busy_image, fail_image, log_filename)) { LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno)); format_root_device("CACHE:"); // Attempt to clean cache up, at least. return -1; @@ -118,6 +119,7 @@ int maybe_install_firmware_update(const char *send_intent) { * wipe the cache and reboot into the system.) */ snprintf(boot.command, sizeof(boot.command), "update-%s", update_type); + strlcat(boot.recovery, "--recover_log\n", sizeof(boot.recovery)); if (set_bootloader_message(&boot)) { format_root_device("CACHE:"); return -1; diff --git a/firmware.h b/firmware.h index aeb8f97..04507bb 100644 --- a/firmware.h +++ b/firmware.h @@ -30,6 +30,7 @@ int firmware_update_pending(); * Returns 0 if no radio image was defined, nonzero on error, * doesn't return at all on success... */ -int maybe_install_firmware_update(const char *send_intent); +int maybe_install_firmware_update(const char *send_intent, + const char *log_filename); #endif diff --git a/recovery.c b/recovery.c index 58c84ef..1a88560 100644 --- a/recovery.c +++ b/recovery.c @@ -46,6 +46,7 @@ static const struct option OPTIONS[] = { { "wipe_cache", no_argument, NULL, 'c' }, // TODO{oam}: implement improved command line passing key, egnot to review. { "set_encrypted_filesystem", required_argument, NULL, 'e' }, + { "recover_log", no_argument, NULL, 'g' }, { NULL, 0, NULL, 0 }, }; @@ -491,6 +492,7 @@ main(int argc, char **argv) { case 'w': wipe_data = wipe_cache = 1; break; case 'c': wipe_cache = 1; break; case 'e': efs_mode = optarg; toggle_efs = 1; break; + case 'g': recover_firmware_update_log(); break; case '?': LOGE("Invalid command argument\n"); continue; @@ -562,7 +564,7 @@ main(int argc, char **argv) { if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait(); // If there is a radio image pending, reboot now to install it. - maybe_install_firmware_update(send_intent); + maybe_install_firmware_update(send_intent, TEMPORARY_LOG_FILE); // Otherwise, get ready to boot the main system... finish_recovery(send_intent);