diff options
author | Doug Zongker <dougz@android.com> | 2010-01-20 16:34:10 -0800 |
---|---|---|
committer | Doug Zongker <dougz@android.com> | 2010-01-21 12:50:04 -0800 |
commit | 687bc12ccfbda26ad134b27d09d1d70a2b8705a6 (patch) | |
tree | 83d313c1cb3e9a3a6795faeceb832cfd1f423870 | |
parent | 883b4c8be5f50cde679cdfa589d7ade83c0253b7 (diff) | |
download | bootable_recovery-687bc12ccfbda26ad134b27d09d1d70a2b8705a6.zip bootable_recovery-687bc12ccfbda26ad134b27d09d1d70a2b8705a6.tar.gz bootable_recovery-687bc12ccfbda26ad134b27d09d1d70a2b8705a6.tar.bz2 |
save the recovery log from before HTC firmware updates
When doing a firmware (radio or hboot) update on HTC devices, save the
recovery log in block 1 of the cache partition, before the firmware
image and the UI bitmaps. When we boot back into recovery after the
firmware update to reformat the cache partition, copy that log out of
cache before reformatting it and dump it into the current invocation's
log.
The practical upshot of all this is that we can see the log output
from radio and hboot updates.
Change-Id: Ie0e89566754c88f4bed6a90d8a0aa04047b01a27
-rw-r--r-- | bootloader.c | 101 | ||||
-rw-r--r-- | bootloader.h | 9 | ||||
-rw-r--r-- | firmware.c | 6 | ||||
-rw-r--r-- | firmware.h | 3 | ||||
-rw-r--r-- | recovery.c | 4 |
5 files changed, 117 insertions, 6 deletions
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 @@ -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; @@ -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 @@ -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); |