aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootloader.c101
-rw-r--r--bootloader.h9
-rw-r--r--firmware.c6
-rw-r--r--firmware.h3
-rw-r--r--recovery.c4
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
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);