blob: f01e23fa7c81ca5cb36fa0080ce4237e7eeb6e8d [file] [log] [blame]
Shashank Mittal024c0332010-02-03 11:44:00 -08001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of Code Aurora Forum, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <debug.h>
30#include <arch/arm.h>
31#include <dev/udc.h>
32#include <string.h>
33#include <kernel/thread.h>
34#include <arch/ops.h>
35
36#include <dev/flash.h>
37#include <lib/ptable.h>
38#include <dev/keys.h>
39
40#include "recovery.h"
41#include "bootimg.h"
42
43static const int MISC_PAGES = 3; // number of pages to save
44static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
45static char buf[4096];
46unsigned boot_into_recovery = 0;
47
48void reboot_device(unsigned);
49
50int get_recovery_message(struct recovery_message *out)
51{
52 struct ptentry *ptn;
53 struct ptable *ptable;
54 unsigned offset = 0;
55 unsigned pagesize = flash_page_size();
56
57 ptable = flash_get_ptable();
58
59 if (ptable == NULL) {
60 dprintf(CRITICAL, "ERROR: Partition table not found\n");
61 return -1;
62 }
63 ptn = ptable_find(ptable, "misc");
64
65 if (ptn == NULL) {
66 dprintf(CRITICAL, "ERROR: No misc partition found\n");
67 return -1;
68 }
69
70 offset += (pagesize * MISC_COMMAND_PAGE);
71 if (flash_read(ptn, offset, buf, pagesize)) {
72 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
73 return -1;
74 }
75 memcpy(out, buf, sizeof(*out));
76 return 0;
77}
78
79int set_recovery_message(const struct recovery_message *in)
80{
81 struct ptentry *ptn;
82 struct ptable *ptable;
83 unsigned offset = 0;
84 unsigned pagesize = flash_page_size();
85 unsigned n = 0;
86
87 ptable = flash_get_ptable();
88
89 if (ptable == NULL) {
90 dprintf(CRITICAL, "ERROR: Partition table not found\n");
91 return -1;
92 }
93 ptn = ptable_find(ptable, "misc");
94
95 if (ptn == NULL) {
96 dprintf(CRITICAL, "ERROR: No misc partition found\n");
97 return -1;
98 }
99
100 n = pagesize * (MISC_COMMAND_PAGE + 1);
101
102 if (flash_read(ptn, offset, SCRATCH_ADDR, n)) {
103 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
104 return -1;
105 }
106
107 offset += (pagesize * MISC_COMMAND_PAGE);
108 offset += SCRATCH_ADDR;
109 memcpy(offset, in, sizeof(*in));
110 if (flash_write(ptn, 0, (void *)SCRATCH_ADDR, n)) {
111 dprintf(CRITICAL, "ERROR: flash write fail!\n");
112 return -1;
113 }
114}
115
116int read_update_header_for_bootloader(struct update_header *header)
117{
118 struct ptentry *ptn;
119 struct ptable *ptable;
120 unsigned offset = 0;
121 unsigned pagesize = flash_page_size();
122
123 ptable = flash_get_ptable();
124 if (ptable == NULL) {
125 dprintf(CRITICAL, "ERROR: Partition table not found\n");
126 return -1;
127 }
128 ptn = ptable_find(ptable, "cache");
129
130 if (ptn == NULL) {
131 dprintf(CRITICAL, "ERROR: No cache partition found\n");
132 return -1;
133 }
134 if (flash_read(ptn, offset, buf, pagesize)) {
135 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
136 return -1;
137 }
138 memcpy(header, buf, sizeof(*header));
139
140 if(strncmp(header->MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE))
141 {
142 return -1;
143 }
144 return 0;
145}
146
147int update_firmware_image (struct update_header *header, char *name)
148{
149 struct ptentry *ptn;
150 struct ptable *ptable;
151 unsigned offset = 0;
152 unsigned pagesize = flash_page_size();
153 unsigned pagemask = pagesize -1;
154 unsigned n = 0;
155
156 ptable = flash_get_ptable();
157 if (ptable == NULL) {
158 dprintf(CRITICAL, "ERROR: Partition table not found\n");
159 return -1;
160 }
161
162 ptn = ptable_find(ptable, "cache");
163 if (ptn == NULL) {
164 dprintf(CRITICAL, "ERROR: No cache partition found\n");
165 return -1;
166 }
167
168 offset += header->image_offset;
169 n = (header->image_length + pagemask) & (~pagemask);
170
171 if (flash_read(ptn, offset, SCRATCH_ADDR, n)) {
172 dprintf(CRITICAL, "ERROR: Cannot read radio image\n");
173 return -1;
174 }
175
176 ptn = ptable_find(ptable, name);
177 if (ptn == NULL) {
178 dprintf(CRITICAL, "ERROR: No %s partition found\n", name);
179 return -1;
180 }
181
182 if (flash_write(ptn, 0, SCRATCH_ADDR, n)) {
183 dprintf(CRITICAL, "ERROR: flash write fail!\n");
184 return -1;
185 }
186
187 dprintf(INFO, "Partition writen successfully!");
188 return 0;
189}
190
191/* Bootloader / Recovery Flow
192 *
193 * On every boot, the bootloader will read the recovery_message
194 * from flash and check the command field. The bootloader should
195 * deal with the command field not having a 0 terminator correctly
196 * (so as to not crash if the block is invalid or corrupt).
197 *
198 * The bootloader will have to publish the partition that contains
199 * the recovery_message to the linux kernel so it can update it.
200 *
201 * if command == "boot-recovery" -> boot recovery.img
202 * else if command == "update-radio" -> update radio image (below)
203 * else -> boot boot.img (normal boot)
204 *
205 * Radio Update Flow
206 * 1. the bootloader will attempt to load and validate the header
207 * 2. if the header is invalid, status="invalid-update", goto #8
208 * 3. display the busy image on-screen
209 * 4. if the update image is invalid, status="invalid-radio-image", goto #8
210 * 5. attempt to update the firmware (depending on the command)
211 * 6. if successful, status="okay", goto #8
212 * 7. if failed, and the old image can still boot, status="failed-update"
213 * 8. write the recovery_message, leaving the recovery field
214 * unchanged, updating status, and setting command to
215 * "boot-recovery"
216 * 9. reboot
217 *
218 * The bootloader will not modify or erase the cache partition.
219 * It is recovery's responsibility to clean up the mess afterwards.
220 */
221
222int recovery_init (void)
223{
224 struct recovery_message msg;
225 struct update_header header;
226 char partition_name[32];
227 unsigned valid_command = 0;
228
229 // get recovery message
230 if(get_recovery_message(&msg))
231 return -1;
232 if (msg.command[0] != 0 && msg.command[0] != 255) {
233 dprintf("Recovery command: %.*s\n", sizeof(msg.command), msg.command);
234 }
235 msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination
236
237 if (!strcmp("boot-recovery",msg.command)) {
238 valid_command = 1;
239 strcpy(msg.command, ""); // to safe against multiple reboot into recovery
240 strcpy(msg.status, "OKAY");
241 set_recovery_message(&msg); // send recovery message
242 boot_into_recovery = 1; // Boot in recovery mode
243 return 0;
244 }
245
246 if (!strcmp("update-radio",msg.command)) {
247 valid_command = 1;
Subbaraman Narayanamurthyeb92bcc2010-07-20 14:32:46 -0700248 strcpy(partition_name, "FOTA");
Shashank Mittal024c0332010-02-03 11:44:00 -0800249 }
250
251 //Todo: Add support for bootloader update too.
252
253 if(!valid_command) {
254 //We need not to do anything
255 return 0; // Boot in normal mode
256 }
257
Shashank Mittal024c0332010-02-03 11:44:00 -0800258 if (read_update_header_for_bootloader(&header)) {
259 strcpy(msg.status, "invalid-update");
260 goto SEND_RECOVERY_MSG;
261 }
262
263 if (update_firmware_image (&header, partition_name)) {
264 strcpy(msg.status, "failed-update");
265 goto SEND_RECOVERY_MSG;
266 }
Shashank Mittal024c0332010-02-03 11:44:00 -0800267 strcpy(msg.status, "OKAY");
268
269SEND_RECOVERY_MSG:
270 strcpy(msg.command, "boot-recovery");
271 set_recovery_message(&msg); // send recovery message
272 boot_into_recovery = 1; // Boot in recovery mode
273 reboot_device(0);
274 return 0;
275}