Recovery: Add support in lk to read/write recovery messages.
Add support in bootloader to intract with recovery code.
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index f36eed8..19f3701 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -43,6 +43,7 @@
#include <dev/keys.h>
#include <dev/fbcon.h>
+#include "recovery.h"
#include "bootimg.h"
#include "fastboot.h"
@@ -79,7 +80,6 @@
void reboot_device(unsigned);
void target_battery_charging_enable(unsigned enable, unsigned disconnect);
-static int boot_into_recovery = 0;
static void ptentry_to_tag(unsigned **ptr, struct ptentry *ptn)
{
@@ -440,7 +440,7 @@
}else if(reboot_mode == FASTBOOT_MODE){
goto fastboot;
}
-
+ recovery_init();
boot_linux_from_flash();
dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
"to fastboot mode.\n");
diff --git a/app/aboot/recovery.c b/app/aboot/recovery.c
new file mode 100755
index 0000000..e9b25e5
--- /dev/null
+++ b/app/aboot/recovery.c
@@ -0,0 +1,278 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <debug.h>
+#include <arch/arm.h>
+#include <dev/udc.h>
+#include <string.h>
+#include <kernel/thread.h>
+#include <arch/ops.h>
+
+#include <dev/flash.h>
+#include <lib/ptable.h>
+#include <dev/keys.h>
+
+#include "recovery.h"
+#include "bootimg.h"
+
+static const int MISC_PAGES = 3; // number of pages to save
+static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
+static char buf[4096];
+unsigned boot_into_recovery = 0;
+
+void reboot_device(unsigned);
+
+int get_recovery_message(struct recovery_message *out)
+{
+ struct ptentry *ptn;
+ struct ptable *ptable;
+ unsigned offset = 0;
+ unsigned pagesize = flash_page_size();
+
+ ptable = flash_get_ptable();
+
+ if (ptable == NULL) {
+ dprintf(CRITICAL, "ERROR: Partition table not found\n");
+ return -1;
+ }
+ ptn = ptable_find(ptable, "misc");
+
+ if (ptn == NULL) {
+ dprintf(CRITICAL, "ERROR: No misc partition found\n");
+ return -1;
+ }
+
+ offset += (pagesize * MISC_COMMAND_PAGE);
+ if (flash_read(ptn, offset, buf, pagesize)) {
+ dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
+ return -1;
+ }
+ memcpy(out, buf, sizeof(*out));
+ return 0;
+}
+
+int set_recovery_message(const struct recovery_message *in)
+{
+ struct ptentry *ptn;
+ struct ptable *ptable;
+ unsigned offset = 0;
+ unsigned pagesize = flash_page_size();
+ unsigned n = 0;
+
+ ptable = flash_get_ptable();
+
+ if (ptable == NULL) {
+ dprintf(CRITICAL, "ERROR: Partition table not found\n");
+ return -1;
+ }
+ ptn = ptable_find(ptable, "misc");
+
+ if (ptn == NULL) {
+ dprintf(CRITICAL, "ERROR: No misc partition found\n");
+ return -1;
+ }
+
+ n = pagesize * (MISC_COMMAND_PAGE + 1);
+
+ if (flash_read(ptn, offset, SCRATCH_ADDR, n)) {
+ dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
+ return -1;
+ }
+
+ offset += (pagesize * MISC_COMMAND_PAGE);
+ offset += SCRATCH_ADDR;
+ memcpy(offset, in, sizeof(*in));
+ if (flash_write(ptn, 0, (void *)SCRATCH_ADDR, n)) {
+ dprintf(CRITICAL, "ERROR: flash write fail!\n");
+ return -1;
+ }
+}
+
+int read_update_header_for_bootloader(struct update_header *header)
+{
+ struct ptentry *ptn;
+ struct ptable *ptable;
+ unsigned offset = 0;
+ unsigned pagesize = flash_page_size();
+
+ ptable = flash_get_ptable();
+ if (ptable == NULL) {
+ dprintf(CRITICAL, "ERROR: Partition table not found\n");
+ return -1;
+ }
+ ptn = ptable_find(ptable, "cache");
+
+ if (ptn == NULL) {
+ dprintf(CRITICAL, "ERROR: No cache partition found\n");
+ return -1;
+ }
+ if (flash_read(ptn, offset, buf, pagesize)) {
+ dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
+ return -1;
+ }
+ memcpy(header, buf, sizeof(*header));
+
+ if(strncmp(header->MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE))
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int update_firmware_image (struct update_header *header, char *name)
+{
+ struct ptentry *ptn;
+ struct ptable *ptable;
+ unsigned offset = 0;
+ unsigned pagesize = flash_page_size();
+ unsigned pagemask = pagesize -1;
+ unsigned n = 0;
+
+ ptable = flash_get_ptable();
+ if (ptable == NULL) {
+ dprintf(CRITICAL, "ERROR: Partition table not found\n");
+ return -1;
+ }
+
+ ptn = ptable_find(ptable, "cache");
+ if (ptn == NULL) {
+ dprintf(CRITICAL, "ERROR: No cache partition found\n");
+ return -1;
+ }
+
+ offset += header->image_offset;
+ n = (header->image_length + pagemask) & (~pagemask);
+
+ if (flash_read(ptn, offset, SCRATCH_ADDR, n)) {
+ dprintf(CRITICAL, "ERROR: Cannot read radio image\n");
+ return -1;
+ }
+
+ ptn = ptable_find(ptable, name);
+ if (ptn == NULL) {
+ dprintf(CRITICAL, "ERROR: No %s partition found\n", name);
+ return -1;
+ }
+
+ if (flash_write(ptn, 0, SCRATCH_ADDR, n)) {
+ dprintf(CRITICAL, "ERROR: flash write fail!\n");
+ return -1;
+ }
+
+ dprintf(INFO, "Partition writen successfully!");
+ return 0;
+}
+
+/* Bootloader / Recovery Flow
+ *
+ * On every boot, the bootloader will read the recovery_message
+ * from flash and check the command field. The bootloader should
+ * deal with the command field not having a 0 terminator correctly
+ * (so as to not crash if the block is invalid or corrupt).
+ *
+ * The bootloader will have to publish the partition that contains
+ * the recovery_message to the linux kernel so it can update it.
+ *
+ * if command == "boot-recovery" -> boot recovery.img
+ * else if command == "update-radio" -> update radio image (below)
+ * else -> boot boot.img (normal boot)
+ *
+ * Radio Update Flow
+ * 1. the bootloader will attempt to load and validate the header
+ * 2. if the header is invalid, status="invalid-update", goto #8
+ * 3. display the busy image on-screen
+ * 4. if the update image is invalid, status="invalid-radio-image", goto #8
+ * 5. attempt to update the firmware (depending on the command)
+ * 6. if successful, status="okay", goto #8
+ * 7. if failed, and the old image can still boot, status="failed-update"
+ * 8. write the recovery_message, leaving the recovery field
+ * unchanged, updating status, and setting command to
+ * "boot-recovery"
+ * 9. reboot
+ *
+ * The bootloader will not modify or erase the cache partition.
+ * It is recovery's responsibility to clean up the mess afterwards.
+ */
+
+int recovery_init (void)
+{
+ struct recovery_message msg;
+ struct update_header header;
+ char partition_name[32];
+ unsigned valid_command = 0;
+
+ // get recovery message
+ if(get_recovery_message(&msg))
+ return -1;
+ if (msg.command[0] != 0 && msg.command[0] != 255) {
+ dprintf("Recovery command: %.*s\n", sizeof(msg.command), msg.command);
+ }
+ msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination
+
+ if (!strcmp("boot-recovery",msg.command)) {
+ valid_command = 1;
+ strcpy(msg.command, ""); // to safe against multiple reboot into recovery
+ strcpy(msg.status, "OKAY");
+ set_recovery_message(&msg); // send recovery message
+ boot_into_recovery = 1; // Boot in recovery mode
+ return 0;
+ }
+
+ if (!strcmp("update-radio",msg.command)) {
+ valid_command = 1;
+ strcpy(partition_name, "AMSS");
+ }
+
+ //Todo: Add support for bootloader update too.
+
+ if(!valid_command) {
+ //We need not to do anything
+ return 0; // Boot in normal mode
+ }
+
+// Disabling image update
+# if 0
+ if (read_update_header_for_bootloader(&header)) {
+ strcpy(msg.status, "invalid-update");
+ goto SEND_RECOVERY_MSG;
+ }
+
+ if (update_firmware_image (&header, partition_name)) {
+ strcpy(msg.status, "failed-update");
+ goto SEND_RECOVERY_MSG;
+ }
+# endif
+ strcpy(msg.status, "OKAY");
+
+SEND_RECOVERY_MSG:
+ strcpy(msg.command, "boot-recovery");
+ set_recovery_message(&msg); // send recovery message
+ boot_into_recovery = 1; // Boot in recovery mode
+ reboot_device(0);
+ return 0;
+}
diff --git a/app/aboot/recovery.h b/app/aboot/recovery.h
new file mode 100755
index 0000000..f76358f
--- /dev/null
+++ b/app/aboot/recovery.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _BOOTLOADER_RECOVERY_H
+#define _BOOTLOADER_RECOVERY_H
+
+#define UPDATE_MAGIC "MSM-RADIO-UPDATE"
+#define UPDATE_MAGIC_SIZE 16
+#define UPDATE_VERSION 0x00010000
+
+
+/* Recovery Message */
+struct recovery_message {
+ char command[32];
+ char status[32];
+ char recovery[1024];
+};
+
+
+struct update_header {
+ unsigned char MAGIC[UPDATE_MAGIC_SIZE];
+
+ unsigned version;
+ unsigned size;
+
+ unsigned image_offset;
+ unsigned image_length;
+
+ unsigned bitmap_width;
+ unsigned bitmap_height;
+ unsigned bitmap_bpp;
+
+ unsigned busy_bitmap_offset;
+ unsigned busy_bitmap_length;
+
+ unsigned fail_bitmap_offset;
+ unsigned fail_bitmap_length;
+};
+
+
+
+int get_recovery_message(struct recovery_message *out);
+int set_recovery_message(const struct recovery_message *in);
+
+int read_update_header_for_bootloader(struct update_header *header);
+int update_firmware_image (struct update_header *header, char *name);
+
+int recovery_init (void);
+
+extern unsigned boot_into_recovery;
+
+#endif
diff --git a/app/aboot/rules.mk b/app/aboot/rules.mk
index e133257..c23784e 100644
--- a/app/aboot/rules.mk
+++ b/app/aboot/rules.mk
@@ -2,5 +2,6 @@
OBJS += \
$(LOCAL_DIR)/aboot.o \
- $(LOCAL_DIR)/fastboot.o
+ $(LOCAL_DIR)/fastboot.o \
+ $(LOCAL_DIR)/recovery.o